aspnet / HttpAbstractions

[Archived] HTTP abstractions such as HttpRequest, HttpResponse, and HttpContext, as well as common web utilities. Project moved to https://github.com/aspnet/AspNetCore

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

204 result produces HttpContext response body stream flagged as writable.

mkArtakMSFT opened this issue · comments

commented

From @mfolker-sage on July 30, 2018 9:20

Bug

The HttpContext in middleware has response body stream flagged as writeable when a 204 response is returned. A 204 should not be writable has the status code denotes no content. When you try to write to the response body stream an error is thrown as expected.

Steps to reproduce:

  1. Add a middleware class with the following code and an endpoint that returns a 204 status.
 public async Task Invoke(HttpContext context)
 {
     _logger.LogInformation("Invoking vanilla middleware middleware.");

     await _next(context);

      _logger.LogInformation ($"Status code {context.Response.StatusCode}");

      if (context.Response.Body.CanWrite)
      {
           //Write something to the body.
      }
 }
  1. Write something to the body, for example,

     public async Task Invoke(HttpContext context)
     {
         var bodyStream = context.Response.Body;
    
         using (var responseBodyStream = new MemoryStream())
         {
             using (var reader = new StreamReader(responseBodyStream))
             {
                 context.Response.Body = responseBodyStream;
    
                 await _next(context);
    
                 responseBodyStream.Seek(0, SeekOrigin.Begin);
                 var responseBodyText = reader.ReadToEnd();
    
                 var messageObjToLog = new { responseBody = responseBodyText, statusCode = context.Response.StatusCode };
    
                 _logger.LogInformation(JsonConvert.SerializeObject(messageObjToLog));
    
                 if (context.Response.CanWrite)
                 {
                     responseBodyStream.Seek(0, SeekOrigin.Begin);
                     await responseBodyStream.CopyToAsync(bodyStream);   
                 }
             }
         }
     }
    
  2. When you return a 204 status code you will notice context.Response.CanWrite == true and therefore the lines copies back to it throws an error.

Description of the problem:

In instances of 204 status codes the context.Response.CanWrite should be false.

Microsoft.AspNetCore.All version 2.0.3

Copied from original issue: aspnet/Mvc#8171

commented

Thank you for filing this issue. In order for us to investigate this issue, please provide a minimalistic repro project that illustrates the problem.

This is international. The response body stream is created at the beginning the request so it's available to any component that's ready to write. There is no processing of response headers or status code until the first write/flush, at which time it determines the status code does not allow a body and fails.

  1. Never blindly write to the body after calling next like your first example. I know it was only for demonstration purposes, but don't do it. You can corrupt the response/connection by either adding misformatted data or by exceeding a specified content-length.
  2. In your second example why was there any data in the MemoryStream if the application generated a 204? Shouldn't CopyToAsync have no-op'd?

Thanks for your response, it was very helpful. The reason it behaves that way makes more sense now.

In response to your points, we are only writing what was in the stream back to the stream we actually return so that we can log it. We understand the risks of changing its contents, but that is not what we are doing. We have changed our code to look for a 204 status code before allowing the copying back to take place, but we had hoped to use to "CanWrite" flag.

Question: Would it not make sense to flag that stream as non-writable once a status code that precludes having a response body has been set?

It's possible, but an added burden to implementers. You would be expected to do the same thing on your MemoryStream that you provided as the temporary response body.

Right, it's extremely hostile to require that implementations honor this.

This issue was moved to dotnet/aspnetcore#3572