Returning HttpResult(dto) results in Chunked Transfer Encoding

Hi,

When our service returns an HttpResult object with a DTO content, it always results in Chunked Transfer Encoding.
This doesn’t happen when the DTO is returned directly by the service.

As a workaround, we return the DTO and set the status through Response.StatusCode.
It works but makes the service harder to unit test. Another way we workaround it is through the use of a response filter.

Service sample to reproduce (reproduced on v4 and v5 on both .NET Framework and .NET Core).

[Route("/hello/{Name}")]
public class Hello : IReturn<HelloResponse>
{
    public string Name { get; set; }
}

public class HelloResponse
{
    public string Result { get; set; }
}

public class MyServices : Service
{
    public object Post(Hello request)
    {
        // Transfer-Encoding: Chunked
        return new HttpResult(new HelloResponse { Result = $"Hello, {request.Name}!" }, HttpStatusCode.Created);

        // no streaming
        // new HelloResponse {  Result = $"Hello, {request.Name}!"  };
        // Response.Status = (int) HttpStatusCode.Created;
    }
}

Anyway, I was wondering if the behaviour is by design or if this is a bug.
Somehow, our IIS server becomes unresponsive under load with Chunked Transfer-Encoding

The behavior of Chunked Transfer-Encoding is ultimately determined by the underlying web server (i.e. it’s never explicitly set in ServiceStack) and when it’s not used it means the response was buffered in which case the Content-Length was able to be determined and written to the HTTP Headers before writing the buffered response to the Response Stream.

ServiceStack serializes DTOs directly to the response stream by default which results in .NET Core and HttpListener Self Hosts both returning chunked encoding responses since the length isn’t known when the response is serialized to the response stream (and .NET Core / HttpListener isn’t doing their own buffering behind the scenes).

In contrast IIS buffers responses by default and is able to determine and set the Content-Length so it doesn’t need to send chunked responses.

The differences with the DTO in the HttpResult is that it writes the response using the IStreamWriterAsync API which has an explicit flush after writing to the response stream, this appears to prevent ASP.NET from buffering the entire response before starting to write to the response stream.

This should not affect clients who should be able to handle both response types transparently but if you really need to you can enable buffering so the response is buffered in ServiceStack who’s then able to determine and set the Content-Length itself:

PreRequestFilters.Add((req,res) => res.UseBufferedStream = true);

You can also use the [AddHeader] Request Filter Attribute, e.g:

[AddHeader(HttpStatusCode.Created)]
public object Post(Hello request) => new Hello { Result = $$"Hello, {request.Name}!" };
1 Like

We indeed used a response filter attribute in the end.

I just tried

PreRequestFilters.Add((req,res) => res.UseBufferedStream = true);

This works perfectly. Thanks again @mythz

1 Like