Skip to content

InvalidOperationException when decompressed data exceeds request body size limit #61723

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
1 task done
mus65 opened this issue Apr 28, 2025 · 0 comments · Fixed by #61812
Closed
1 task done

InvalidOperationException when decompressed data exceeds request body size limit #61723

mus65 opened this issue Apr 28, 2025 · 0 comments · Fixed by #61812
Labels
area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions
Milestone

Comments

@mus65
Copy link

mus65 commented Apr 28, 2025

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

When using the request decompression middleware and the compressed body is below the request body size limit, but the decompressed body exceeds it, the request fails with an InvalidOperationException and returns HTTP Status 500 instead of 413 (Content Too Large).

Without compression or if the compressed body already exceeds the limit, this internally causes a BadHttpRequestException with StatusCode 413 which can properly be handled in a middleware. I would expect the same behaviour here instead of an InvalidOperationException which cannot be handled properly.

The SizeLimitedStream that throws the exception was introduced as part of #40279 .

Expected Behavior

Same behaviour as without compression, meaning a BadHttpRequestException with StatusCode 413.

Steps To Reproduce

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRequestDecompression();

builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.Limits.MaxRequestBodySize = 30;
});

var app = builder.Build();
app.UseRequestDecompression();
app.MapPost("/", ([FromBody]string foo) => foo);
app.Run();

Then run:

echo '"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"' | gzip | curl -v -i --data-binary @- -H "Content-Encoding: gzip" -H "Content-Type: application/json" http://localhost:5146/

which results in:

* Host localhost:5146 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
*   Trying [::1]:5146...
* Connected to localhost (::1) port 5146
* using HTTP/1.x
> POST / HTTP/1.1
> Host: localhost:5146
> User-Agent: curl/8.11.0
> Accept: */*
> Content-Encoding: gzip
> Content-Type: application/json
> Content-Length: 26
>
* upload completely sent off: 26 bytes
< HTTP/1.1 500 Internal Server Error
HTTP/1.1 500 Internal Server Error
< Content-Type: text/plain; charset=utf-8
Content-Type: text/plain; charset=utf-8
< Date: Mon, 28 Apr 2025 13:16:25 GMT
Date: Mon, 28 Apr 2025 13:16:25 GMT
< Server: Kestrel
Server: Kestrel
< Transfer-Encoding: chunked
Transfer-Encoding: chunked
<

System.InvalidOperationException: The maximum number of bytes have been read.
   at SizeLimitedStream.ReadAsync(Memory`1 buffer, CancellationToken cancellationToken)
   at System.Text.Json.Serialization.ReadBufferState.ReadFromStreamAsync(Stream utf8Json, CancellationToken cancellationToken, Boolean fillBuffer)
   at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.DeserializeAsync(Stream utf8Json, CancellationToken cancellationToken)
   at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.DeserializeAsObjectAsync(Stream utf8Json, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Http.HttpRequestJsonExtensions.ReadFromJsonAsync(HttpRequest request, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Http.HttpRequestJsonExtensions.ReadFromJsonAsync(HttpRequest request, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Http.RequestDelegateFactory.<HandleRequestBodyAndCompileRequestDelegateForJson>g__TryReadBodyAsync|102_0(HttpContext httpContext, Type bodyType, String parameterTypeName, String parameterName, Boolean allowEmptyRequestBody, Boolean throwOnBadRequest, JsonTypeInfo jsonTypeInfo)
   at Microsoft.AspNetCore.Http.RequestDelegateFactory.<>c__DisplayClass102_2.<<HandleRequestBodyAndCompileRequestDelegateForJson>b__2>d.MoveNext()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.RequestDecompression.RequestDecompressionMiddleware.InvokeCore(HttpContext context, Stream decompressionStream)
   at Microsoft.AspNetCore.RequestDecompression.RequestDecompressionMiddleware.InvokeCore(HttpContext context, Stream decompressionStream)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)

HEADERS
=======
Accept: */*
Host: localhost:5146
User-Agent: curl/8.11.0
Content-Type: application/json
Content-Length: 26
* Connection #0 to host localhost left intact

Exceptions (if any)

System.InvalidOperationException: The maximum number of bytes have been read.
   at SizeLimitedStream.ReadAsync(Memory`1 buffer, CancellationToken cancellationToken)
   at System.Text.Json.Serialization.ReadBufferState.ReadFromStreamAsync(Stream utf8Json, CancellationToken cancellationToken, Boolean fillBuffer)
   at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.DeserializeAsync(Stream utf8Json, CancellationToken cancellationToken)
   at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.DeserializeAsObjectAsync(Stream utf8Json, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Http.HttpRequestJsonExtensions.ReadFromJsonAsync(HttpRequest request, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Http.HttpRequestJsonExtensions.ReadFromJsonAsync(HttpRequest request, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Http.RequestDelegateFactory.<HandleRequestBodyAndCompileRequestDelegateForJson>g__TryReadBodyAsync|102_0(HttpContext httpContext, Type bodyType, String parameterTypeName, String parameterName, Boolean allowEmptyRequestBody, Boolean throwOnBadRequest, JsonTypeInfo jsonTypeInfo)
   at Microsoft.AspNetCore.Http.RequestDelegateFactory.<>c__DisplayClass102_2.<<HandleRequestBodyAndCompileRequestDelegateForJson>b__2>d.MoveNext()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.RequestDecompression.RequestDecompressionMiddleware.InvokeCore(HttpContext context, Stream decompressionStream)
   at Microsoft.AspNetCore.RequestDecompression.RequestDecompressionMiddleware.InvokeCore(HttpContext context, Stream decompressionStream)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)

.NET Version

10.0.100-preview.3.25201.16

Anything else?

Reproducible with .NET 9.0.4 and .NET 10 Preview 3 .

@ghost ghost added the area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions label Apr 28, 2025
@BrennanConroy BrennanConroy added this to the 10.0-preview5 milestone May 12, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions
Projects
None yet
2 participants