Skip to content

ManagedHandler: Protocol corruption if 100-continue is expected but not received #23914

Description

@Tratcher

ManagedHandler has the same 100-continue bug as CurlHandler: https://github.com/dotnet/corefx/issues/24435

100-continue is an inherent race condition. The client is waiting for a 100 response or a final response, but only for a given timeout. When the server sends the final response without a 100, it has no way of knowing if the client's timer has expired and it has already started sending data. This would be an ambiguous situation as the server wouldn't reliably know if the next bytes it received were the body it was promised or the headers for the next request. To prevent this ambiguity the client must always send the data it promised, or a chunked terminator, or close the connection. For small bodies it's worth sending it to avoid the cost of creating a new connection, but for large bodies is far more economical close the connection.

ManagedHandler (and CurlHandler) does not send the the request body if it receives a final response rather than a 100 continue. This causes protocol corruption on the next request and it is rejected by the server (e.g. 400 Bad Request).

Final status codes are common in authentication scenarios like when the user's request is rejected due to missing or insufficient permissions. The client can send a new request with the required credentials, and many clients do this automatically. See AuthenticationHandler.

Workaround: Apps must disable ExpectContinue on every request with a body.

The fix for ManagedHandler looks small, handling for ReceivedOtherStatusCode needs to be added to the switch here:
https://github.com/dotnet/corefx/blob/08802ff9c877f7f75a82d6704d84d321e99eb5c8/src/System.Net.Http/src/System/Net/Http/Managed/HttpConnection.cs#L457-L462
Sending the data is the minimal fix, but consider sending only the chunked terminator or closing the connection for non-trivial request bodies. This can also avoid problems with un-buffered HttpContent objects that can't be sent twice.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions