Repository navigation
Pass request = true to write_body so HTTP/1.0 request bodies of unknown length raise instead of being close-delimited. - #260
Open
samuel-williams-shopify wants to merge 2 commits into
Open
samuel-williams-shopify wants to merge 2 commits into
samuel-williams-shopify wants to merge 2 commits into
Conversation
…1.0 request bodies of unknown length raise instead of being close-delimited.
This branch has not been deployed
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.
Async::HTTP::Protocol::HTTP1::Clientnow passesrequest = truetoProtocol::HTTP1::Connection#write_body, so an HTTP/1.0 request body of unknown length raises instead of being written without framing where the server expects the next request. Companion to socketry/protocol-http1#65.Release dependency: this depends on the
requestargument added in socketry/protocol-http1#65 and bumps theprotocol-http1constraint to~> 0.42. Against the releasedprotocol-http1(0.41.1) the extra positional argument raisesArgumentError, so this must not merge until that change is released.Problem
A request body cannot be delimited by closing the connection. Per RFC 1945 §7.2.2 and RFC 9112 §6.3, a request with neither
content-lengthnortransfer-encodinghas a zero-length body, so the server reads zero body bytes and either discards the rest, or, if the connection is kept alive, parses the body bytes as the start of the next request.HTTP/1.0 has no chunked encoding, so when the HTTP/1 client is asked to send an HTTP/1.0 request with a body of unknown length (for example a
Protocol::HTTP::Body::Writable),write_bodyfalls through to the close-delimited path that is only valid for responses. Reproduced againstmainwith anAsync::HTTPHTTP/1.0 client and server: aPOST /withconnection: keep-aliveand a writable body containingGET /smuggled HTTP/1.1\r\nhost: localhost\r\n\r\nresulted in the server invoking the application for/with an empty body and then again for/smuggled.Solution
write_bodyin protocol-http1 gains a trailingrequestargument, mirroringhead, so the framing decision is made from information the caller already has. Both call sites inProtocol::HTTP1::Client#call(the streaming child task and the synchronous path) now passtrue. The server call sites are unchanged, since response bodies may legitimately be close-delimited under HTTP/1.0.With this change the client raises instead of sending the body. Because the body is written in a child task which closes the connection on failure, the error currently surfaces to the caller of
client.postasProtocol::HTTP1::ProtocolError: Cannot read response in state: closed!rather than the underlyingCannot write HTTP/1.0 request body of unknown length!. The new test asserts what is observable: the request raises, the body is closed, and the server application is never invoked. Propagating the original error from the child task is a separate follow-up.Tests
test/async/http/protocol/http10.rbadds a client/server round-trip where an HTTP/1.0POSTwith an unknown-length body raises and the server never sees a second request. Onmainthis test fails on both assertions: the request succeeds and the application is invoked for/and/smuggled.BUNDLE_GEMFILEwithprotocol-http1pointed at the Add arequestargument towrite_bodyso HTTP/1.0 request bodies of unknown length raise instead of being close-delimited. protocol-http1#65 worktree.