lib: transfer origin and proxy handling

Add `data->state.origin` as the origin the transfer is sending the
current request to/gets the response from. Use it for request specific
properties like authentication, hsts and cookie handling, etc.

Unless talking to a forwarding HTTP proxy (e.g. not tunneling),
`data->state.origin` and `conn->origin` are the same.

With a forwarding HTTP proxy in play, `conn->origin` is set to
`conn->http_proxy.peer` and `conn->bits.origin_is_proxy` (a new bit) is
set.

Remove the connection bits, now replaced with:

* `conn->bits.socksproxy` -> `conn->socks_proy.peer`
* `conn->bits.httpproxy` -> `conn->http_proy.peer`
* `conn->bits.proxy` -> `(conn->socks_proy.peer || conn->http_proy.peer`)
* `conn->bits.tunnel_proxy` -> (`conn->http_proy.peer && !conn->bits.origin_is_proxy`)
* `(conn->bits.httpproxy && !conn->bits.tunnel_proxy)` -> `conn->bits.origin_is_proxy`

Rename `noproxy.[ch]` to `proxy.[ch]`. Move the connection proxy setup
code from `url.c` to `proxy.c`.

Remove `data->info.conn_remote_port` as no one uses it.

Add test_40_02b for a SOCKS connection to a forwarding HTTPS proxy.

Update internal documentation about peers and creds.

Closes #21967
This commit is contained in:
Stefan Eissing 2026-06-12 12:02:08 +02:00 committed by Daniel Stenberg
parent c951368579
commit 73daec6620
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
30 changed files with 1083 additions and 1014 deletions

View file

@ -1403,7 +1403,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
struct Curl_cfilter *cf = userp;
struct cf_h2_ctx *ctx = cf->ctx;
struct h2_stream_ctx *stream;
struct Curl_easy *data_s;
struct Curl_easy *data;
int32_t stream_id = frame->hd.stream_id;
CURLcode result;
(void)flags;
@ -1411,15 +1411,15 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
/* get the stream from the hash based on Stream ID */
data_s = nghttp2_session_get_stream_user_data(session, stream_id);
if(!GOOD_EASY_HANDLE(data_s))
data = nghttp2_session_get_stream_user_data(session, stream_id);
if(!GOOD_EASY_HANDLE(data))
/* Receiving a Stream ID not in the hash should not happen, this is an
internal error more than anything else! */
return NGHTTP2_ERR_CALLBACK_FAILURE;
stream = H2_STREAM_CTX(ctx, data_s);
stream = H2_STREAM_CTX(ctx, data);
if(!stream) {
failf(data_s, "Internal NULL stream");
failf(data, "Internal NULL stream");
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
@ -1432,14 +1432,14 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
!strncmp(HTTP_PSEUDO_AUTHORITY, (const char *)name, namelen)) {
/* pseudo headers are lower case */
int rc = 0;
char *check = curl_maprintf("%s:%d", cf->conn->origin->hostname,
cf->conn->origin->port);
char *check = curl_maprintf("%s:%d", data->state.origin->hostname,
data->state.origin->port);
if(!check)
/* no memory */
return NGHTTP2_ERR_CALLBACK_FAILURE;
if(!curl_strequal(check, (const char *)value) &&
((cf->conn->origin->port != cf->conn->given->defport) ||
!curl_strequal(cf->conn->origin->hostname, (const char *)value))) {
((data->state.origin->port != cf->conn->given->defport) ||
!curl_strequal(data->state.origin->hostname, (const char *)value))) {
/* This is push is not for the same authority that was asked for in
* the URL. RFC 7540 section 8.2 says: "A client MUST treat a
* PUSH_PROMISE for which the server is not authoritative as a stream
@ -1467,7 +1467,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
char **headp;
if(stream->push_headers_alloc > 1000) {
/* this is beyond crazy many headers, bail out */
failf(data_s, "Too many PUSH_PROMISE headers");
failf(data, "Too many PUSH_PROMISE headers");
free_push_headers(stream);
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
@ -1491,13 +1491,13 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
if(stream->bodystarted) {
/* This is a trailer */
CURL_TRC_CF(data_s, cf, "[%d] trailer: %.*s: %.*s",
CURL_TRC_CF(data, cf, "[%d] trailer: %.*s: %.*s",
stream->id, (int)namelen, name, (int)valuelen, value);
result = Curl_dynhds_add(&stream->resp_trailers,
(const char *)name, namelen,
(const char *)value, valuelen);
if(result) {
cf_h2_header_error(cf, data_s, stream, result);
cf_h2_header_error(cf, data, stream, result);
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
@ -1512,14 +1512,14 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
result = Curl_http_decode_status(&stream->status_code,
(const char *)value, valuelen);
if(result) {
cf_h2_header_error(cf, data_s, stream, result);
cf_h2_header_error(cf, data, stream, result);
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
hlen = curl_msnprintf(buffer, sizeof(buffer), HTTP_PSEUDO_STATUS ":%d\r",
stream->status_code);
result = Curl_headers_push(data_s, buffer, hlen, CURLH_PSEUDO);
result = Curl_headers_push(data, buffer, hlen, CURLH_PSEUDO);
if(result) {
cf_h2_header_error(cf, data_s, stream, result);
cf_h2_header_error(cf, data, stream, result);
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
curlx_dyn_reset(&ctx->scratch);
@ -1529,17 +1529,17 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
if(!result)
result = curlx_dyn_addn(&ctx->scratch, STRCONST(" \r\n"));
if(!result)
h2_xfer_write_resp_hd(cf, data_s, stream, curlx_dyn_ptr(&ctx->scratch),
h2_xfer_write_resp_hd(cf, data, stream, curlx_dyn_ptr(&ctx->scratch),
curlx_dyn_len(&ctx->scratch), FALSE);
if(result) {
cf_h2_header_error(cf, data_s, stream, result);
cf_h2_header_error(cf, data, stream, result);
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
/* if we receive data for another handle, wake that up */
if(CF_DATA_CURRENT(cf) != data_s)
Curl_multi_mark_dirty(data_s);
if(CF_DATA_CURRENT(cf) != data)
Curl_multi_mark_dirty(data);
CURL_TRC_CF(data_s, cf, "[%d] status: HTTP/2 %03d",
CURL_TRC_CF(data, cf, "[%d] status: HTTP/2 %03d",
stream->id, stream->status_code);
return 0;
}
@ -1556,17 +1556,17 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
if(!result)
result = curlx_dyn_addn(&ctx->scratch, STRCONST("\r\n"));
if(!result)
h2_xfer_write_resp_hd(cf, data_s, stream, curlx_dyn_ptr(&ctx->scratch),
h2_xfer_write_resp_hd(cf, data, stream, curlx_dyn_ptr(&ctx->scratch),
curlx_dyn_len(&ctx->scratch), FALSE);
if(result) {
cf_h2_header_error(cf, data_s, stream, result);
cf_h2_header_error(cf, data, stream, result);
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
/* if we receive data for another handle, wake that up */
if(CF_DATA_CURRENT(cf) != data_s)
Curl_multi_mark_dirty(data_s);
if(CF_DATA_CURRENT(cf) != data)
Curl_multi_mark_dirty(data);
CURL_TRC_CF(data_s, cf, "[%d] header: %.*s: %.*s",
CURL_TRC_CF(data, cf, "[%d] header: %.*s: %.*s",
stream->id, (int)namelen, name, (int)valuelen, value);
return 0; /* 0 is successful */
@ -2840,9 +2840,7 @@ bool Curl_http2_may_switch(struct Curl_easy *data)
(data->state.http_neg.wanted & CURL_HTTP_V2x) &&
data->state.http_neg.h2_prior_knowledge) {
#ifndef CURL_DISABLE_PROXY
if(data->conn->bits.httpproxy && !data->conn->bits.tunnel_proxy) {
/* We do not support HTTP/2 proxies yet. Also it is debatable
whether or not this setting should apply to HTTP/2 proxies. */
if(data->conn->bits.origin_is_proxy) {
infof(data, "Ignoring HTTP/2 prior knowledge due to proxy");
return FALSE;
}