I consider myself pretty good in C#, but I am facing trouble in understanding the following piece of code:
using (var memoryStream = new MemoryStream())
{
var responseStream = httpContext.Response.Body;
httpContext.Response.Body = memoryStream;
await this.next(httpContext);
using (var compressedStream = new GZipStream(responseStream, CompressionLevel.Optimal))
{
httpContext.Response.Headers.Add("Content-Encoding", new [] { "gzip" });
memoryStream.Seek(0, SeekOrigin.Begin);
await memoryStream.CopyToAsync(compressedStream);
}
}
This code is extracted from an ASP.Net Core middleware that compresses the HTTP response, and "surprisingly", it works... or so it seems (I tested it with Fiddler).
Let me put my understanding first:
httpContext.Response.Body in responseStream.httpContext.Response.Body reference with the newly initialised memoryStream.httpContext.Response.Body data with responseStream, while httpContext.Response.Body new data is empty.this.next() is awaitable, our code execution will "stop" until all middlewares return.GZipStream, adds a response header, and "seeks" to the beginning of memoryStream.memoryStream to compressedStream, which writes it to responseStream.So, what is the relation between memoryStream, compressedStream, and responseStream? We created compressedStream to write to responseStream and then eventually to httpContext.Response.Body, but the reference from responseStream to httpContext.Response.Body isn't there anymore?
FWIW the OOB ResponseCompressionMiddleware looks a bit different nowadays.
But in the sample you pasted, i'll annotate to illustrate why memoryStream is NOT actually empty by the time it gets copied to compressedStream.
using (var memoryStream = new MemoryStream()) // Create a buffer so we can capture response content written by future middleware/controller actions
{
var responseStream = httpContext.Response.Body; // save a reference to the ACTUAL STREAM THAT WRITES TO THE HTTP RESPONSE WHEN WRITTEN TO.
httpContext.Response.Body = memoryStream; // replace HttpContext.Response.Body with our buffer (memoryStream).
await this.next(httpContext); // <== somewhere in here is where HttpContext.Response.Body gets written to, and since Body now points to memoryStream, really memoryStream gets written to.
using (var compressedStream = new GZipStream(responseStream, CompressionLevel.Optimal)) // Here is where we wrap the ACTUAL HTTP RESPONSE STREAM with our ready-to-write-to compressedStream.
{
httpContext.Response.Headers.Add("Content-Encoding", new [] { "gzip" });
memoryStream.Seek(0, SeekOrigin.Begin); // Previously, we set HttpContext.Response.Body to a memoryStream buffer, then SOMEONE in the middleware chain or controller action wrote to that Body, we can seek to the beginning of what they wrote, and copy that content to the compressedStream.
await memoryStream.CopyToAsync(compressedStream);
}
}
Hope that helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With