Skip to content

HTTP proxy CONNECT: chunked-decoder state not reset between auth retries, curl hangs waiting for a body that never arrives #23069

Description

@bupt-Yy-young

Environment: curl 8.22.0 on Linux (also reproduced on current master). HTTP proxy with multi-round
proxy auth (Digest/NTLM/Negotiate) over CONNECT, connection kept alive.

What happens

When a proxy answers the first CONNECT with 407 + Transfer-Encoding: chunked, curl ignores that
body with its chunk decoder. On the auth retry (still the same connection), the per-tunnel state
ts->chunked_encoding and the chunk decoder ts->ch are not reset (tunnel_reinit() only
resets rcvbuf/cl/keepon). If the retry then gets another body-less 407 (e.g. Content-Length: 0),
curl enters its "ignore response body" loop believing the body is chunked, and waits forever for
chunked bytes that never come
-- it never finishes processing the 407, never performs the next auth
round, and the transfer hangs (until the proxy or a timeout kills the connection).

Repro

Fake proxy (Digest, persistent connection):

  1. CONNECT (no auth) -> 407 + Transfer-Encoding: chunked, chunked body HELLO
  2. CONNECT (with auth) -> 407 + Proxy-Authenticate: Digest ... nonce="n2" ... stale=true + Content-Length: 0 (no body)
  3. CONNECT (with auth) -> 200 Connection established

Client: curl --proxy-digest -U user:pass -x http://127.0.0.1:8080/ https://example.invalid/

Observed: after response 2, curl never sends the third CONNECT -- it hangs in "ignore chunked
response body" (the proxy never sees CONNECT 3). Control run where response 1 is Content-Length: 0
instead of chunked: all three CONNECTs proceed normally and the tunnel is established.

Why

  • lib/cf-h1-proxy.c, tunnel_reinit(): resets rcvbuf, request_data, keepon, cl,
    close_connection, maybe_folded, leading_unfold, nsent, headerlines -- but not
    ts->chunked_encoding, and it does not re-init ts->ch (the chunk decoder stays at the
    CHUNK_DONE state after the previous body was ignored).
  • single_header() / the KEEPON_IGNORE loop then treat the next response's (empty) body as
    chunked: Curl_httpchunk_read() on a completed decoder consumes nothing, so is_done is only
    reached when data arrives -- which never happens for a body-less 407.

Expected vs actual

Expected: each CONNECT response is processed with a fresh per-response body state.
Actual: a stale chunked_encoding from a previous 407 stalls the auth retry (or, when bytes do
arrive, swallows the first byte of the next response and corrupts its parse). Connection/protocol
state corruption class, low-to-medium severity.

Fix direction: clear ts->chunked_encoding and reset (re-init) ts->ch in tunnel_reinit() (and/or
before starting to read each new CONNECT response).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions