Skip to content

[HTTP/3] New case of "The server returned an invalid or unrecognized response" #57647

@CarnaViire

Description

@CarnaViire

Started happening after updating to 6.0.100-rc.1.21411.28
First noticed in stress tests here #57356 (comment)
But it is not specific to stress, neither to status code, so it is not the same as #55988

I am able to (rarely) reproduce it by serially calling GET requests (harder to repro with Kestrel's logging enabled)

HttpClient error looks like this

System.Net.Http.HttpRequestException : The server returned an invalid or unrecognized response.
      Stack Trace:
        C:\Users\knatalia\dev\git\runtime\src\libraries\System.Net.Http\src\System\Net\Http\SocketsHttpHandler\Http3RequestStream.cs(318,0): at System.Net.Http.Http3RequestStream.ReadResponseAsync(CancellationToken cancellationToken)
        C:\Users\knatalia\dev\git\runtime\src\libraries\System.Net.Http\src\System\Net\Http\SocketsHttpHandler\Http3RequestStream.cs(196,0): at System.Net.Http.Http3RequestStream.SendAsync(CancellationToken cancellationToken)
        C:\Users\knatalia\dev\git\runtime\src\libraries\System.Net.Http\src\System\Net\Http\SocketsHttpHandler\Http3RequestStream.cs(297,0): at System.Net.Http.Http3RequestStream.SendAsync(CancellationToken cancellationToken)
        C:\Users\knatalia\dev\git\runtime\src\libraries\System.Net.Http\src\System\Net\Http\SocketsHttpHandler\Http3Connection.cs(210,0): at System.Net.Http.Http3Connection.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)

and related Kestrel log is

dbug: Microsoft.AspNetCore.Server.Kestrel[25]
      Connection id "0HMB29L9PVOIV", Request id "0HMB29L9PVOIV:00000058": started reading request body.
dbug: Microsoft.AspNetCore.Server.Kestrel[26]
      Connection id "0HMB29L9PVOIV", Request id "0HMB29L9PVOIV:00000058": done reading request body.
info: Microsoft.AspNetCore.Server.Kestrel[32]
      Connection id "0HMB29L9PVOIV", Request id "0HMB29L9PVOIV:00000058": the application completed without reading the entire request body.
dbug: Microsoft.AspNetCore.Server.Kestrel.Transport.Quic[13]
      Stream id "0HMB29L9PVOIV:00000058" read side aborted by application with error code 256 because: "The application completed without reading the entire request body.".
dbug: Microsoft.AspNetCore.Server.Kestrel.Transport.Quic[10]
      Stream id "0HMB29L9PVOIV:00000058" shutting down writes because: "The QUIC transport's send loop completed gracefully.".
dbug: Microsoft.AspNetCore.Server.Kestrel.Transport.Quic[2]
      Stream id "0HMB29L9PVOIV:0000005C" type Bidirectional accepted.
dbug: Microsoft.AspNetCore.Server.Kestrel.Transport.Quic[10]
      Stream id "0HMB29L9PVOIV:0000005C" shutting down writes because: "The QUIC transport's send loop completed gracefully.".

I think the problem might be tied to "the application completed without reading the entire request body".
Kestrel did not call my callback for that request. All the times the error happened without Kestrel's logging enabled, I've also seen that my callback was not called but HttpClient received some response which it couldn't read.

For the record, Kestrel's callback is set up like this

var statuses = Enum.GetValues<HttpStatusCode>()
    .Where(s => s >= HttpStatusCode.OK) // exclude informational
    .Cast<int>().Distinct(); // remove dupes like Ambiguous(300) and MultipleChoices(300) to avoid ambiguous mapping
foreach (var status in statuses)
{    
    app.MapGet("/Expect" + status, () => {
        Console.WriteLine("GET request to /Expect" + status + ", will return " + status + " " + (HttpStatusCode)status);
        //return new StatusCodeResult(status);
        return Results.StatusCode(status);
    });
}

and HttpClient's requests are

            using HttpClient client = CreateHttpClient();

            var statuses = Enum.GetValues<HttpStatusCode>()
                .Where(s => s >= HttpStatusCode.OK) // exclude informational
                .Cast<int>().Distinct(); // remove dupes like Ambiguous(300) and MultipleChoices(300) to avoid ambiguous mapping
            foreach (var status in statuses)
            {    
                Console.WriteLine("Request expecting " + status + " " + (HttpStatusCode)status);
                using HttpRequestMessage request = new()
                {
                    Method = HttpMethod.Get,
                    RequestUri = new Uri("https://localhost:5001/Expect" + status),
                    Version = HttpVersion30,
                    VersionPolicy = HttpVersionPolicy.RequestVersionExact
                };
                using HttpResponseMessage response = await client.SendAsync(request).WaitAsync(TimeSpan.FromSeconds(10));
                
                if(status != (int)response.StatusCode)
                {
                    fail = true;
                    Console.WriteLine("    FAILURE -- received " + (int)response.StatusCode + " " + response.StatusCode);
                }
                else
                {
                    Console.WriteLine("    SUCCESS");
                }
            }

cc @JamesNK

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions