When handling request responses above a certain size, ASP.NET Core middleware attempts to serialise the response to disk before streaming it to the client.
This can cause errors and the inability to render certain pages under some circumstances if the TEMP folder is not writable by the IIS / ASP.NET Core process.
Steps To Reproduce
Open CCM dashboard
Navigate to the Administration -> Maintenance section
or
Open CCM dashboard
Run a large deployment (exact required # of clients to trigger is unknown)
Navigate to a deployment step result page
NOTE: This may not reproduce on a fresh instance of CCM, as it relies on there being a certain amount of data in the database that is returned during queries from these pages.
Output Log
2021-08-18 15:38:13,918 137 [ERROR] - An unhandled exception has occurred while executing the request.
System.UnauthorizedAccessException: Access to the path 'C:\ProgramData\chocolatey\choco-cache\ASPNETCORE_7bbc18be-f552-4a2b-8d85-325afcd2cdd5.tmp' is denied.
at System.IO.FileStream.ValidateFileHandle(SafeFileHandle fileHandle)
at System.IO.FileStream.CreateFileOpenHandle(FileMode mode, FileShare share, FileOptions options)
at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options)
at Microsoft.AspNetCore.WebUtilities.FileBufferingWriteStream.EnsureFileStream()
at Microsoft.AspNetCore.WebUtilities.FileBufferingWriteStream.Write(Byte[] buffer, Int32 offset, Int32 count)
at Microsoft.AspNetCore.WebUtilities.HttpResponseStreamWriter.Write(Char[] values, Int32 index, Int32 count)
at Newtonsoft.Json.Utilities.JavaScriptUtils.WriteEscapedJavaScriptString(TextWriter writer, String s, Char delimiter, Boolean appendDelimiters, Boolean[] charEscapeFlags, StringEscapeHandling stringEscapeHandling, IArrayPool`1 bufferPool, Char[]& writeBuffer)
at Newtonsoft.Json.JsonTextWriter.WriteEscapedString(String value, Boolean quote)
at Newtonsoft.Json.JsonWriter.WriteValue(JsonWriter writer, PrimitiveTypeCode typeCode, Object value)
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeList(JsonWriter writer, IEnumerable values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriter jsonWriter, Object value, Type objectType)
at Newtonsoft.Json.JsonSerializer.SerializeInternal(JsonWriter jsonWriter, Object value, Type objectType)
at Microsoft.AspNetCore.Mvc.Formatters.NewtonsoftJsonOutputFormatter.WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
at Microsoft.AspNetCore.Mvc.Formatters.NewtonsoftJsonOutputFormatter.WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
at Microsoft.AspNetCore.Mvc.Formatters.NewtonsoftJsonOutputFormatter.WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|29_0[TFilter,TFilterAsync](ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Localization.RequestLocalizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.<Invoke>g__Awaited|6_0(ExceptionHandlerMiddleware middleware, HttpContext context, Task task)
Workarounds
It is possible to edit the secutiry permissions to allow the Application Pool identity that is running the CCM Web site, and provide write permissions to the folder that is identified in the above output log.
Proposed Solution
It looks like we're overriding the TEMP environment variable and pointing it to the choco-cache location. This behaviour was inherited from Chocolatey itself when we pulled in Chocolatey.Common, and seems to be undesirable for the purposes of ASP.NET Core middleware.
Description
When handling request responses above a certain size, ASP.NET Core middleware attempts to serialise the response to disk before streaming it to the client.
This can cause errors and the inability to render certain pages under some circumstances if the TEMP folder is not writable by the IIS / ASP.NET Core process.
Steps To Reproduce
or
NOTE: This may not reproduce on a fresh instance of CCM, as it relies on there being a certain amount of data in the database that is returned during queries from these pages.
Output Log
Workarounds
It is possible to edit the secutiry permissions to allow the Application Pool identity that is running the CCM Web site, and provide write permissions to the folder that is identified in the above output log.
Proposed Solution
It looks like we're overriding the TEMP environment variable and pointing it to the choco-cache location. This behaviour was inherited from Chocolatey itself when we pulled in Chocolatey.Common, and seems to be undesirable for the purposes of ASP.NET Core middleware.
Related Issues and Tickets
Zendesk Ticket #1
Zendesk Ticket #2
Done Checklist
┆Issue is synchronized with this Gitlab issue by Unito ┆Milestone: 0.6.3