From bb4032a152ba3c8dbce93ad7a6e9268f23996746 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Wed, 6 Sep 2023 14:43:22 +0200 Subject: [PATCH] http: h1/h2 proxy unification - use shared code for setting up the CONNECT request when tunneling, used in HTTP/1.x and HTTP/2 proxying - eliminate use of Curl_buffer_send() and other manipulations of `data->req` or `data->state.ulbuf` Closes #11808 --- lib/cf-h1-proxy.c | 288 +++++++++++++++++----------------------------- lib/cf-h2-proxy.c | 53 ++------- lib/dynhds.c | 3 +- lib/http1.c | 24 ++++ lib/http1.h | 2 + lib/http_proxy.c | 108 ++++++++++++++++- lib/http_proxy.h | 9 ++ 7 files changed, 258 insertions(+), 229 deletions(-) diff --git a/lib/cf-h1-proxy.c b/lib/cf-h1-proxy.c index 23cf85c459..674802114c 100644 --- a/lib/cf-h1-proxy.c +++ b/lib/cf-h1-proxy.c @@ -34,6 +34,7 @@ #include "dynbuf.h" #include "sendf.h" #include "http.h" +#include "http1.h" #include "http_proxy.h" #include "url.h" #include "select.h" @@ -64,13 +65,10 @@ typedef enum { /* struct for HTTP CONNECT tunneling */ struct h1_tunnel_state { - int sockindex; - const char *hostname; - int remote_port; struct HTTP CONNECT; struct dynbuf rcvbuf; - struct dynbuf req; - size_t nsend; + struct dynbuf request_data; + size_t nsent; size_t headerlines; enum keeponval { KEEPON_DONE, @@ -94,46 +92,31 @@ static bool tunnel_is_failed(struct h1_tunnel_state *ts) return ts && (ts->tunnel_state == H1_TUNNEL_FAILED); } -static CURLcode tunnel_reinit(struct h1_tunnel_state *ts, - struct connectdata *conn, - struct Curl_easy *data) +static CURLcode tunnel_reinit(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h1_tunnel_state *ts) { (void)data; + (void)cf; DEBUGASSERT(ts); Curl_dyn_reset(&ts->rcvbuf); - Curl_dyn_reset(&ts->req); + Curl_dyn_reset(&ts->request_data); ts->tunnel_state = H1_TUNNEL_INIT; ts->keepon = KEEPON_CONNECT; ts->cl = 0; ts->close_connection = FALSE; - - if(conn->bits.conn_to_host) - ts->hostname = conn->conn_to_host.name; - else if(ts->sockindex == SECONDARYSOCKET) - ts->hostname = conn->secondaryhostname; - else - ts->hostname = conn->host.name; - - if(ts->sockindex == SECONDARYSOCKET) - ts->remote_port = conn->secondary_port; - else if(conn->bits.conn_to_port) - ts->remote_port = conn->conn_to_port; - else - ts->remote_port = conn->remote_port; - return CURLE_OK; } -static CURLcode tunnel_init(struct h1_tunnel_state **pts, +static CURLcode tunnel_init(struct Curl_cfilter *cf, struct Curl_easy *data, - struct connectdata *conn, - int sockindex) + struct h1_tunnel_state **pts) { struct h1_tunnel_state *ts; CURLcode result; - if(conn->handler->flags & PROTOPT_NOTCPPROXY) { - failf(data, "%s cannot be done over CONNECT", conn->handler->scheme); + if(cf->conn->handler->flags & PROTOPT_NOTCPPROXY) { + failf(data, "%s cannot be done over CONNECT", cf->conn->handler->scheme); return CURLE_UNSUPPORTED_PROTOCOL; } @@ -146,15 +129,14 @@ static CURLcode tunnel_init(struct h1_tunnel_state **pts, if(!ts) return CURLE_OUT_OF_MEMORY; - ts->sockindex = sockindex; infof(data, "allocate connect buffer"); Curl_dyn_init(&ts->rcvbuf, DYN_PROXY_CONNECT_HEADERS); - Curl_dyn_init(&ts->req, DYN_HTTP_REQUEST); + Curl_dyn_init(&ts->request_data, DYN_HTTP_REQUEST); *pts = ts; - connkeep(conn, "HTTP proxy CONNECT"); - return tunnel_reinit(ts, conn, data); + connkeep(cf->conn, "HTTP proxy CONNECT"); + return tunnel_reinit(cf, data, ts); } static void h1_tunnel_go_state(struct Curl_cfilter *cf, @@ -176,7 +158,7 @@ static void h1_tunnel_go_state(struct Curl_cfilter *cf, switch(new_state) { case H1_TUNNEL_INIT: CURL_TRC_CF(data, cf, "new tunnel state 'init'"); - tunnel_reinit(ts, cf->conn, data); + tunnel_reinit(cf, data, ts); break; case H1_TUNNEL_CONNECT: @@ -207,7 +189,7 @@ static void h1_tunnel_go_state(struct Curl_cfilter *cf, CURL_TRC_CF(data, cf, "new tunnel state 'failed'"); ts->tunnel_state = new_state; Curl_dyn_reset(&ts->rcvbuf); - Curl_dyn_reset(&ts->req); + Curl_dyn_reset(&ts->request_data); /* restore the protocol pointer */ data->info.httpcode = 0; /* clear it as it might've been used for the proxy */ @@ -229,171 +211,80 @@ static void tunnel_free(struct Curl_cfilter *cf, if(ts) { h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); Curl_dyn_free(&ts->rcvbuf); - Curl_dyn_free(&ts->req); + Curl_dyn_free(&ts->request_data); free(ts); cf->ctx = NULL; } } -static CURLcode CONNECT_host(struct Curl_easy *data, - struct connectdata *conn, - const char *hostname, - int remote_port, - char **connecthostp, - char **hostp) -{ - char *hostheader; /* for CONNECT */ - char *host = NULL; /* Host: */ - bool ipv6_ip = conn->bits.ipv6_ip; - - /* the hostname may be different */ - if(hostname != conn->host.name) - ipv6_ip = (strchr(hostname, ':') != NULL); - hostheader = /* host:port with IPv6 support */ - aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"", - remote_port); - if(!hostheader) - return CURLE_OUT_OF_MEMORY; - - if(!Curl_checkProxyheaders(data, conn, STRCONST("Host"))) { - host = aprintf("Host: %s\r\n", hostheader); - if(!host) { - free(hostheader); - return CURLE_OUT_OF_MEMORY; - } - } - *connecthostp = hostheader; - *hostp = host; - return CURLE_OK; -} - #ifndef USE_HYPER static CURLcode start_CONNECT(struct Curl_cfilter *cf, struct Curl_easy *data, struct h1_tunnel_state *ts) { - struct connectdata *conn = cf->conn; - char *hostheader = NULL; - char *host = NULL; - const char *httpv; + struct httpreq *req = NULL; + int http_minor; CURLcode result; - infof(data, "Establish HTTP proxy tunnel to %s:%d", - ts->hostname, ts->remote_port); - /* This only happens if we've looped here due to authentication reasons, and we don't really use the newly cloned URL here then. Just free() it. */ Curl_safefree(data->req.newurl); - result = CONNECT_host(data, conn, - ts->hostname, ts->remote_port, - &hostheader, &host); - if(result) - goto out; - - /* Setup the proxy-authorization header, if any */ - result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET, - hostheader, TRUE); - if(result) - goto out; - - httpv = (conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? "1.0" : "1.1"; - - result = - Curl_dyn_addf(&ts->req, - "CONNECT %s HTTP/%s\r\n" - "%s" /* Host: */ - "%s", /* Proxy-Authorization */ - hostheader, - httpv, - host?host:"", - data->state.aptr.proxyuserpwd? - data->state.aptr.proxyuserpwd:""); + result = Curl_http_proxy_create_CONNECT(&req, cf, data, 1); if(result) goto out; - if(!Curl_checkProxyheaders(data, conn, STRCONST("User-Agent")) - && data->set.str[STRING_USERAGENT]) - result = Curl_dyn_addf(&ts->req, "User-Agent: %s\r\n", - data->set.str[STRING_USERAGENT]); - if(result) - goto out; - - if(!Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection"))) - result = Curl_dyn_addn(&ts->req, - STRCONST("Proxy-Connection: Keep-Alive\r\n")); - if(result) - goto out; - - result = Curl_add_custom_headers(data, TRUE, &ts->req); - if(result) - goto out; + infof(data, "Establish HTTP proxy tunnel to %s", req->authority); - /* CRLF terminate the request */ - result = Curl_dyn_addn(&ts->req, STRCONST("\r\n")); - if(result) - goto out; - - /* Send the connect request to the proxy */ - result = Curl_buffer_send(&ts->req, data, &ts->CONNECT, - &data->info.request_size, 0, - ts->sockindex); + Curl_dyn_reset(&ts->request_data); + ts->nsent = 0; ts->headerlines = 0; + http_minor = (cf->conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? 0 : 1; + + result = Curl_h1_req_write_head(req, http_minor, &ts->request_data); out: if(result) failf(data, "Failed sending CONNECT to proxy"); - free(host); - free(hostheader); + if(req) + Curl_http_req_free(req); return result; } -static CURLcode send_CONNECT(struct Curl_easy *data, - struct connectdata *conn, +static CURLcode send_CONNECT(struct Curl_cfilter *cf, + struct Curl_easy *data, struct h1_tunnel_state *ts, bool *done) { - struct SingleRequest *k = &data->req; - struct HTTP *http = &ts->CONNECT; + char *buf = Curl_dyn_ptr(&ts->request_data); + size_t request_len = Curl_dyn_len(&ts->request_data); + size_t blen = request_len; CURLcode result = CURLE_OK; + ssize_t nwritten; - if(http->sending != HTTPSEND_REQUEST) - goto out; + if(blen <= ts->nsent) + goto out; /* we are done */ - if(!ts->nsend) { - size_t fillcount; - k->upload_fromhere = data->state.ulbuf; - result = Curl_fillreadbuffer(data, data->set.upload_buffer_size, - &fillcount); - if(result) - goto out; - ts->nsend = fillcount; - } - if(ts->nsend) { - ssize_t bytes_written; - /* write to socket (send away data) */ - result = Curl_write(data, - conn->writesockfd, /* socket to send to */ - k->upload_fromhere, /* buffer pointer */ - ts->nsend, /* buffer size */ - &bytes_written); /* actually sent */ - if(result) - goto out; - /* send to debug callback! */ - Curl_debug(data, CURLINFO_HEADER_OUT, - k->upload_fromhere, bytes_written); + blen -= ts->nsent; + buf += ts->nsent; - ts->nsend -= bytes_written; - k->upload_fromhere += bytes_written; + nwritten = cf->next->cft->do_send(cf->next, data, buf, blen, &result); + if(nwritten < 0) { + if(result == CURLE_AGAIN) { + result = CURLE_OK; + } + goto out; } - if(!ts->nsend) - http->sending = HTTPSEND_NADA; + + DEBUGASSERT(blen >= (size_t)nwritten); + ts->nsent += (size_t)nwritten; + Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)nwritten); out: if(result) failf(data, "Failed sending CONNECT to proxy"); - *done = (http->sending != HTTPSEND_REQUEST); + *done = (!result && (ts->nsent >= request_len)); return result; } @@ -491,7 +382,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, error = SELECT_OK; *done = FALSE; - if(!Curl_conn_data_pending(data, ts->sockindex)) + if(!Curl_conn_data_pending(data, cf->sockindex)) return CURLE_OK; while(ts->keepon) { @@ -669,6 +560,41 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, } #else /* USE_HYPER */ + +static CURLcode CONNECT_host(struct Curl_cfilter *cf, + struct Curl_easy *data, + char **pauthority, + char **phost_header) +{ + const char *hostname; + int port; + bool ipv6_ip; + CURLcode result; + char *authority; /* for CONNECT, the destination host + port */ + char *host_header = NULL; /* Host: authority */ + + result = Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip); + if(result) + return result; + + authority = aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"", + port); + if(!authority) + return CURLE_OUT_OF_MEMORY; + + /* If user is not overriding the Host header later */ + if(!Curl_checkProxyheaders(data, cf->conn, STRCONST("Host"))) { + host_header = aprintf("Host: %s\r\n", authority); + if(!host_header) { + free(authority); + return CURLE_OUT_OF_MEMORY; + } + } + *pauthority = authority; + *phost_header = host_header; + return CURLE_OK; +} + /* The Hyper version of CONNECT */ static CURLcode start_CONNECT(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -685,9 +611,10 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf, hyper_task *task = NULL; /* for the handshake */ hyper_clientconn *client = NULL; hyper_task *sendtask = NULL; /* for the send */ - char *hostheader = NULL; /* for CONNECT */ - char *host = NULL; /* Host: */ + char *authority = NULL; /* for CONNECT */ + char *host_header = NULL; /* Host: */ CURLcode result = CURLE_OUT_OF_MEMORY; + (void)ts; io = hyper_io_new(); if(!io) { @@ -765,27 +692,25 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf, goto error; } - infof(data, "Establish HTTP proxy tunnel to %s:%d", - ts->hostname, ts->remote_port); - /* This only happens if we've looped here due to authentication reasons, and we don't really use the newly cloned URL here then. Just free() it. */ Curl_safefree(data->req.newurl); - result = CONNECT_host(data, conn, ts->hostname, ts->remote_port, - &hostheader, &host); + result = CONNECT_host(cf, data, &authority, &host_header); if(result) goto error; - if(hyper_request_set_uri(req, (uint8_t *)hostheader, - strlen(hostheader))) { + infof(data, "Establish HTTP proxy tunnel to %s", authority); + + if(hyper_request_set_uri(req, (uint8_t *)authority, + strlen(authority))) { failf(data, "error setting path"); result = CURLE_OUT_OF_MEMORY; goto error; } if(data->set.verbose) { - char *se = aprintf("CONNECT %s HTTP/1.1\r\n", hostheader); + char *se = aprintf("CONNECT %s HTTP/1.1\r\n", authority); if(!se) { result = CURLE_OUT_OF_MEMORY; goto error; @@ -795,10 +720,10 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf, } /* Setup the proxy-authorization header, if any */ result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET, - hostheader, TRUE); + authority, TRUE); if(result) goto error; - Curl_safefree(hostheader); + Curl_safefree(authority); /* default is 1.1 */ if((conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) && @@ -815,11 +740,11 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf, result = CURLE_OUT_OF_MEMORY; goto error; } - if(host) { - result = Curl_hyper_header(data, headers, host); + if(host_header) { + result = Curl_hyper_header(data, headers, host_header); if(result) goto error; - Curl_safefree(host); + Curl_safefree(host_header); } if(data->state.aptr.proxyuserpwd) { @@ -873,8 +798,8 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf, client = NULL; error: - free(host); - free(hostheader); + free(host_header); + free(authority); if(io) hyper_io_free(io); if(options) @@ -889,12 +814,13 @@ error: return result; } -static CURLcode send_CONNECT(struct Curl_easy *data, - struct connectdata *conn, +static CURLcode send_CONNECT(struct Curl_cfilter *cf, + struct Curl_easy *data, struct h1_tunnel_state *ts, bool *done) { struct hyptransfer *h = &data->hyp; + struct connectdata *conn = cf->conn; hyper_task *task = NULL; hyper_error *hypererr = NULL; CURLcode result = CURLE_OK; @@ -994,7 +920,7 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf, case H1_TUNNEL_CONNECT: /* see that the request is completely sent */ CURL_TRC_CF(data, cf, "CONNECT send"); - result = send_CONNECT(data, cf->conn, ts, &done); + result = send_CONNECT(cf, data, ts, &done); if(result || !done) goto out; h1_tunnel_go_state(cf, ts, H1_TUNNEL_RECEIVE, data); @@ -1089,7 +1015,7 @@ static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf, *done = FALSE; if(!ts) { - result = tunnel_init(&ts, data, cf->conn, cf->sockindex); + result = tunnel_init(cf, data, &ts); if(result) return result; cf->ctx = ts; diff --git a/lib/cf-h2-proxy.c b/lib/cf-h2-proxy.c index 77e12991b9..9cb804d047 100644 --- a/lib/cf-h2-proxy.c +++ b/lib/cf-h2-proxy.c @@ -84,7 +84,8 @@ static CURLcode tunnel_stream_init(struct Curl_cfilter *cf, { const char *hostname; int port; - bool ipv6_ip = cf->conn->bits.ipv6_ip; + bool ipv6_ip; + CURLcode result; ts->state = H2_TUNNEL_INIT; ts->stream_id = -1; @@ -92,22 +93,9 @@ static CURLcode tunnel_stream_init(struct Curl_cfilter *cf, BUFQ_OPT_SOFT_LIMIT); Curl_bufq_init(&ts->sendbuf, PROXY_H2_CHUNK_SIZE, H2_TUNNEL_SEND_CHUNKS); - if(cf->conn->bits.conn_to_host) - hostname = cf->conn->conn_to_host.name; - else if(cf->sockindex == SECONDARYSOCKET) - hostname = cf->conn->secondaryhostname; - else - hostname = cf->conn->host.name; - - if(cf->sockindex == SECONDARYSOCKET) - port = cf->conn->secondary_port; - else if(cf->conn->bits.conn_to_port) - port = cf->conn->conn_to_port; - else - port = cf->conn->remote_port; - - if(hostname != cf->conn->host.name) - ipv6_ip = (strchr(hostname, ':') != NULL); + result = Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip); + if(result) + return result; ts->authority = /* host:port with IPv6 support */ aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"", port); @@ -984,38 +972,11 @@ static CURLcode submit_CONNECT(struct Curl_cfilter *cf, CURLcode result; struct httpreq *req = NULL; - infof(data, "Establish HTTP/2 proxy tunnel to %s", ts->authority); - - result = Curl_http_req_make(&req, "CONNECT", sizeof("CONNECT")-1, - NULL, 0, ts->authority, strlen(ts->authority), - NULL, 0); + result = Curl_http_proxy_create_CONNECT(&req, cf, data, 2); if(result) goto out; - /* Setup the proxy-authorization header, if any */ - result = Curl_http_output_auth(data, cf->conn, req->method, HTTPREQ_GET, - req->authority, TRUE); - if(result) - goto out; - - if(data->state.aptr.proxyuserpwd) { - result = Curl_dynhds_h1_cadd_line(&req->headers, - data->state.aptr.proxyuserpwd); - if(result) - goto out; - } - - if(!Curl_checkProxyheaders(data, cf->conn, STRCONST("User-Agent")) - && data->set.str[STRING_USERAGENT]) { - result = Curl_dynhds_cadd(&req->headers, "User-Agent", - data->set.str[STRING_USERAGENT]); - if(result) - goto out; - } - - result = Curl_dynhds_add_custom(data, TRUE, &req->headers); - if(result) - goto out; + infof(data, "Establish HTTP/2 proxy tunnel to %s", req->authority); result = proxy_h2_submit(&ts->stream_id, cf, data, ctx->h2, req, NULL, ts, tunnel_send_callback, cf); diff --git a/lib/dynhds.c b/lib/dynhds.c index 007dfc588c..979b3e825b 100644 --- a/lib/dynhds.c +++ b/lib/dynhds.c @@ -344,6 +344,8 @@ size_t Curl_dynhds_cremove(struct dynhds *dynhds, const char *name) return Curl_dynhds_remove(dynhds, name, strlen(name)); } +#endif + CURLcode Curl_dynhds_h1_dprint(struct dynhds *dynhds, struct dynbuf *dbuf) { CURLcode result = CURLE_OK; @@ -363,4 +365,3 @@ CURLcode Curl_dynhds_h1_dprint(struct dynhds *dynhds, struct dynbuf *dbuf) return result; } -#endif diff --git a/lib/http1.c b/lib/http1.c index 1ca7d41e8a..182234ca97 100644 --- a/lib/http1.c +++ b/lib/http1.c @@ -318,5 +318,29 @@ out: return nread; } +CURLcode Curl_h1_req_write_head(struct httpreq *req, int http_minor, + struct dynbuf *dbuf) +{ + CURLcode result; + + result = Curl_dyn_addf(dbuf, "%s %s%s%s%s HTTP/1.%d\r\n", + req->method, + req->scheme? req->scheme : "", + req->scheme? "://" : "", + req->authority? req->authority : "", + req->path? req->path : "", + http_minor); + if(result) + goto out; + + result = Curl_dynhds_h1_dprint(&req->headers, dbuf); + if(result) + goto out; + + result = Curl_dyn_addn(dbuf, STRCONST("\r\n")); + +out: + return result; +} #endif /* !CURL_DISABLE_HTTP */ diff --git a/lib/http1.h b/lib/http1.h index b1eaa969d9..2de302f1f6 100644 --- a/lib/http1.h +++ b/lib/http1.h @@ -56,6 +56,8 @@ ssize_t Curl_h1_req_parse_read(struct h1_req_parser *parser, CURLcode Curl_h1_req_dprint(const struct httpreq *req, struct dynbuf *dbuf); +CURLcode Curl_h1_req_write_head(struct httpreq *req, int http_minor, + struct dynbuf *dbuf); #endif /* !CURL_DISABLE_HTTP */ #endif /* HEADER_CURL_HTTP1_H */ diff --git a/lib/http_proxy.c b/lib/http_proxy.c index 60bbfbe580..a1d6da9548 100644 --- a/lib/http_proxy.c +++ b/lib/http_proxy.c @@ -52,6 +52,113 @@ #include "memdebug.h" +CURLcode Curl_http_proxy_get_destination(struct Curl_cfilter *cf, + const char **phostname, + int *pport, bool *pipv6_ip) +{ + DEBUGASSERT(cf); + DEBUGASSERT(cf->conn); + + if(cf->conn->bits.conn_to_host) + *phostname = cf->conn->conn_to_host.name; + else if(cf->sockindex == SECONDARYSOCKET) + *phostname = cf->conn->secondaryhostname; + else + *phostname = cf->conn->host.name; + + if(cf->sockindex == SECONDARYSOCKET) + *pport = cf->conn->secondary_port; + else if(cf->conn->bits.conn_to_port) + *pport = cf->conn->conn_to_port; + else + *pport = cf->conn->remote_port; + + if(*phostname != cf->conn->host.name) + *pipv6_ip = (strchr(*phostname, ':') != NULL); + else + *pipv6_ip = cf->conn->bits.ipv6_ip; + + return CURLE_OK; +} + +CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, + struct Curl_cfilter *cf, + struct Curl_easy *data, + int http_version_major) +{ + const char *hostname = NULL; + char *authority = NULL; + int port; + bool ipv6_ip; + CURLcode result; + struct httpreq *req = NULL; + + result = Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip); + if(result) + goto out; + + authority = aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, + ipv6_ip?"]":"", port); + if(!authority) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + result = Curl_http_req_make(&req, "CONNECT", sizeof("CONNECT")-1, + NULL, 0, authority, strlen(authority), + NULL, 0); + if(result) + goto out; + + /* Setup the proxy-authorization header, if any */ + result = Curl_http_output_auth(data, cf->conn, req->method, HTTPREQ_GET, + req->authority, TRUE); + if(result) + goto out; + + /* If user is not overriding Host: header, we add for HTTP/1.x */ + if(http_version_major == 1 && + !Curl_checkProxyheaders(data, cf->conn, STRCONST("Host"))) { + result = Curl_dynhds_cadd(&req->headers, "Host", authority); + if(result) + goto out; + } + + if(data->state.aptr.proxyuserpwd) { + result = Curl_dynhds_h1_cadd_line(&req->headers, + data->state.aptr.proxyuserpwd); + if(result) + goto out; + } + + if(!Curl_checkProxyheaders(data, cf->conn, STRCONST("User-Agent")) + && data->set.str[STRING_USERAGENT]) { + result = Curl_dynhds_cadd(&req->headers, "User-Agent", + data->set.str[STRING_USERAGENT]); + if(result) + goto out; + } + + if(http_version_major == 1 && + !Curl_checkProxyheaders(data, cf->conn, STRCONST("Proxy-Connection"))) { + result = Curl_dynhds_cadd(&req->headers, "Proxy-Connection", "Keep-Alive"); + if(result) + goto out; + } + + result = Curl_dynhds_add_custom(data, TRUE, &req->headers); + +out: + if(result && req) { + Curl_http_req_free(req); + req = NULL; + } + free(authority); + *preq = req; + return result; +} + + struct cf_proxy_ctx { /* the protocol specific sub-filter we install during connect */ struct Curl_cfilter *cf_protocol; @@ -105,7 +212,6 @@ connect_sub: break; #endif default: - CURL_TRC_CF(data, cf, "installing subfilter for default HTTP/1.1"); infof(data, "CONNECT tunnel: unsupported ALPN(%d) negotiated", alpn); result = CURLE_COULDNT_CONNECT; goto out; diff --git a/lib/http_proxy.h b/lib/http_proxy.h index a1a03720bd..2b5f7ae706 100644 --- a/lib/http_proxy.h +++ b/lib/http_proxy.h @@ -30,6 +30,15 @@ #include "urldata.h" +CURLcode Curl_http_proxy_get_destination(struct Curl_cfilter *cf, + const char **phostname, + int *pport, bool *pipv6_ip); + +CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, + struct Curl_cfilter *cf, + struct Curl_easy *data, + int http_version_major); + /* Default proxy timeout in milliseconds */ #define PROXY_TIMEOUT (3600*1000) -- 2.47.3