Skip to content

SocketsHttpHandler may read request Content multiple times in parallel #53914

@davidni

Description

@davidni

Description

Under very specific conditions, SocketsHttpHandler.SendAsync when using HTTP/2 attempts to copy the HttpRequestMessage.Content twice.

In Island Gateway (AKA what eventually became YARP), we have code similar to the following, and this invariant that we do a best-effort check is being violated:

https://github.com/microsoft/reverse-proxy/blob/961608d01adc9ab07d4cd3f5adf7d4d3dfff3f0f/src/ReverseProxy/Proxy/StreamCopyHttpContent.cs#L118-L125

The following criteria seem to be necessary to trigger this bug, though it may be broader than this:

  • Request specifies Expect: 100-continue
  • Destination does not produce a 100 response within the first 1 second
  • HttpConnectionPool.SendWithRetryAsync decides to retry (just a hypothesis)

It seems like the SocketsHttpHandler machinery may be deciding it is safe to retry because the destination service provably did not start processing the first attempt, but it seems to make the wrong assumption that it is okay to read the HttpRequestMessage.Content again on the next attempt. This is not a valid assumption, and leads to issues in applications such as YARP where the Content is a wrapper around Kestrel's incoming request body stream, which cannot be read twice.

Configuration

  • .NET Core 3.1.15
  • Windows Server Datacenter 2019

Other information

Filing this as a result of an offline discussion with @halter73 and @davidfowl. Thanks folks for helping investigate.

@karelz FYI.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions