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:
- The code starts with taking a reference to
httpContext.Response.Body
inresponseStream
. - Then it replaces
httpContext.Response.Body
reference with the newly initialisedmemoryStream
. - If my understanding of how C# references work, I say we still have a reference to the original
httpContext.Response.Body
data withresponseStream
, whilehttpContext.Response.Body
new data is empty. - Next, we are calling the next middleware in the pipeline.
- Because
this.next()
is awaitable, our code execution will "stop" until all middlewares return. - When our code execution "resumes", it will initialise a
GZipStream
, adds a response header, and "seeks" to the beginning ofmemoryStream
. - Finally, it copies the content or
memoryStream
tocompressedStream
, which writes it toresponseStream
.
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?