http: reply with "100 Continue" when a client sends "Expect: 100-continue" #36468

pull rudeus112266 wants to merge 1 commits into bitcoin:master from rudeus112266:http-100-continue changing 4 files +201 −0
  1. rudeus112266 commented at 8:38 AM on October 8, 2026: none

    Fixes #36461.

    Problem

    HTTPServer (added in #35182) never inspects the Expect request header, so it never sends the RFC 9110 §10.1.1 "100 Continue" interim response. curl/libcurl sends Expect: 100-continue for any POST body over 1 MiB and waits up to 1 second for either a 100 Continue or a final status before sending the body. Since the server never answers, every such request (large submitblock, sendrawtransaction, submitpackage, PSBTs, JSON-RPC batches) stalls by about a second, even though it eventually succeeds. The previous libevent-based server handled this for free.

    Fix

    HTTPRequest::MaybeSendContinue() is called from HTTPRemoteClient::ReadRequest()'s state machine at the one point where all headers are known but the body hasn't been read yet. If the request is HTTP/1.1 and carries Expect: 100-continue, it queues an interim response via a new HTTPRemoteClient::SendContinue().

    SendContinue() can't reuse Send(): Send() marks the request's final reply (m_req_busy = false) and sets m_keep_alive from the computed final value, which would incorrectly mark this half-read request complete. SendContinue() also forces m_keep_alive = true unconditionally — without that, an optimistic send that drains this tiny interim response down to an empty buffer would hit the disconnect-on-empty-buffer-if-!keep_alive branch in MaybeSendBytesFromBuffer() while m_keep_alive is still at its default of false (normally only set correctly by the first real Send()), disconnecting the client before it ever sends the request body. Send() still overwrites m_keep_alive with the correct value once the real response goes out.

    Testing

    Added http_continue_tests, which drives a real HTTPServer over mock sockets (same harness as the existing http_server_socket_tests):

    • Sends only the headers of an HTTP/1.1 request with Expect: 100-continue and a Content-Length body that hasn't arrived yet, and asserts the client receives 100 Continue immediately, before the request handler is ever invoked. The body is then sent separately and the final response is confirmed to still arrive.
    • Confirms an HTTP/1.1 request without Expect never gets a 100 Continue and the existing http_server_socket_tests case (which asserts the exact bytes of a full response) still passes unchanged, i.e. no regression for the common path.
    • Confirms an HTTP/1.0 request that happens to send Expect: 100-continue is simply ignored, since the 100-continue mechanism doesn't apply to HTTP/1.0.

    All 794 cases in test_bitcoin pass locally after this change.

    Manual repro

    Ran the exact repro from #36461 against a regtest bitcoind built from this branch:

    $ curl -v -u "$(cat ~/.bitcoin/regtest/.cookie)" --data-binary [@big](/bitcoin-bitcoin/contributor/big/).json http://127.0.0.1:18443/ -o /dev/null -w '%{time_total}\n'
    > Expect: 100-continue
    < HTTP/1.1 100 Continue
    < HTTP/1.1 200 OK
    0.076262
    

    100 Continue now arrives immediately instead of the ~1s "Done waiting for 100-continue" stall described in the issue, in line with the v31.0 timing reported there (0.052923s, vs. 1.029281s on v32.0rc3).

  2. http: reply with "100 Continue" when a client sends "Expect: 100-continue"
    The HTTPServer added in #35182 never inspects the "Expect" request header,
    so it never sends the RFC 9110 §10.1.1 "100 Continue" interim response.
    curl/libcurl sends "Expect: 100-continue" for any POST body over 1 MiB and
    then waits up to 1 second for either a "100 Continue" or a final status
    before sending the body. Since the server never answers, every such
    request (large submitblock/sendrawtransaction/submitpackage/PSBTs/batches)
    stalls by about a second before completing, even though it eventually
    succeeds.
    
    Add HTTPRequest::MaybeSendContinue(), called from the state machine in
    HTTPRemoteClient::ReadRequest() at the single point where all request
    headers are known but the body has not been read yet. If the request is
    HTTP/1.1 and carries "Expect: 100-continue", it queues an interim
    response via the new HTTPRemoteClient::SendContinue().
    
    SendContinue() cannot reuse Send(): that method marks the request's final
    reply (m_req_busy = false) and sets m_keep_alive from the computed final
    value. Since the request being handled here is still only half read, using
    Send() would incorrectly complete it. SendContinue() also forces
    m_keep_alive = true unconditionally: without it, an optimistic send that
    drains this tiny interim response down to an empty buffer would hit the
    disconnect-on-empty-buffer-if-!keep_alive logic in
    MaybeSendBytesFromBuffer() while m_keep_alive is still at its default of
    false (it is normally only set correctly by the first real Send()),
    disconnecting the client before it ever gets to send the request body.
    Send() still overwrites m_keep_alive with the correct value once the real
    response goes out.
    76b474932c
  3. DrahtBot added the label RPC/REST/ZMQ on Oct 8, 2026
  4. DrahtBot commented at 8:38 AM on October 8, 2026: contributor

    <!--e57a25ab6845829454e8d69fc972939a-->

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    <!--006a51241073e994b41acfe9ec718e94-->

    External sites

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline and AI policy for information on the review process. A summary of reviews will appear here.

    <!--5faf32d7da4f0f540f40219e4f7537a3-->

  5. sedited commented at 8:40 AM on October 8, 2026: contributor

    To preserve the time of reviewers, this project requires authors to understand the code they are submitting. Given that the description is entirely LLM generated, I don't think that is the case, so I am closing this again.

  6. sedited closed this on Oct 8, 2026


github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-10-14 17:51 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me