Fix #2458: send body when no 100 Continue arrives over TLS#2460
Merged
Conversation
The auto-added `Expect: 100-continue` (for bodies >= 1024 bytes) decided whether to withhold the request body based on raw socket readability via select_read(). Over TLS, post-handshake records such as TLS 1.3 session tickets make the socket readable without any HTTP response being available, so the client withheld the body and then blocked reading a response that never came, failing with `Failed to read connection`. Decide based on whether a status line can actually be read within the 100-continue timeout instead: temporarily shorten the read timeout, try to read the status line, and if none arrives, send the body and proceed as usual (matching curl). This keeps the `100 Continue` and early final-response paths working while no longer being fooled by TLS records. Add a regression test using a raw OpenSSL server that never sends `100 Continue`.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Client::Put/Postwith a body of at leastCPPHTTPLIB_EXPECT_100_THRESHOLD(1024) bytes auto-addsExpect: 100-continue. Over TLS, this could fail withFailed to read connection(#2458): the server received the request but the client never sent the body.Root cause
The 100-continue wait decided whether to withhold the body based on raw socket readability (
select_readon the underlying socket). Over TLS, post-handshake records such as TLS 1.3 session tickets make the socket readable without any HTTP response being available. The client mistook that for an incoming response, withheld the body, and then blocked reading a response that never came (the server was waiting for the body), eventually timing out asRead.This matches the report: it only happened with large bodies (Expect triggered), only over HTTPS, and disappeared with
CPPHTTPLIB_EXPECT_100_THRESHOLD 0.curlsucceeds against the same server because it sends the body once no100 Continuearrives.Fix
Base the decision on whether a status line can actually be read within the 100-continue timeout, not on raw socket readability:
The
100 Continuepath and the early final-response path (e.g.417/414) keep working, and the client is no longer fooled by TLS records.Test
Adds
Expect100ContinueTest.TLSServerOmits100Continue: a raw OpenSSL server (TLS 1.3) that never sends100 Continue. It reproduces the hang on the previous code (fails withFailed to read connection) and passes with the fix.test_splitbuild verified.🤖 Generated with Claude Code