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
This commit is contained in:
Stefan Eissing 2023-09-06 14:43:22 +02:00 committed by Daniel Stenberg
parent 9c7165e96a
commit bb4032a152
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
7 changed files with 258 additions and 229 deletions

View File

@ -34,6 +34,7 @@
#include "dynbuf.h" #include "dynbuf.h"
#include "sendf.h" #include "sendf.h"
#include "http.h" #include "http.h"
#include "http1.h"
#include "http_proxy.h" #include "http_proxy.h"
#include "url.h" #include "url.h"
#include "select.h" #include "select.h"
@ -64,13 +65,10 @@ typedef enum {
/* struct for HTTP CONNECT tunneling */ /* struct for HTTP CONNECT tunneling */
struct h1_tunnel_state { struct h1_tunnel_state {
int sockindex;
const char *hostname;
int remote_port;
struct HTTP CONNECT; struct HTTP CONNECT;
struct dynbuf rcvbuf; struct dynbuf rcvbuf;
struct dynbuf req; struct dynbuf request_data;
size_t nsend; size_t nsent;
size_t headerlines; size_t headerlines;
enum keeponval { enum keeponval {
KEEPON_DONE, KEEPON_DONE,
@ -94,46 +92,31 @@ static bool tunnel_is_failed(struct h1_tunnel_state *ts)
return ts && (ts->tunnel_state == H1_TUNNEL_FAILED); return ts && (ts->tunnel_state == H1_TUNNEL_FAILED);
} }
static CURLcode tunnel_reinit(struct h1_tunnel_state *ts, static CURLcode tunnel_reinit(struct Curl_cfilter *cf,
struct connectdata *conn, struct Curl_easy *data,
struct Curl_easy *data) struct h1_tunnel_state *ts)
{ {
(void)data; (void)data;
(void)cf;
DEBUGASSERT(ts); DEBUGASSERT(ts);
Curl_dyn_reset(&ts->rcvbuf); Curl_dyn_reset(&ts->rcvbuf);
Curl_dyn_reset(&ts->req); Curl_dyn_reset(&ts->request_data);
ts->tunnel_state = H1_TUNNEL_INIT; ts->tunnel_state = H1_TUNNEL_INIT;
ts->keepon = KEEPON_CONNECT; ts->keepon = KEEPON_CONNECT;
ts->cl = 0; ts->cl = 0;
ts->close_connection = FALSE; 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; 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 Curl_easy *data,
struct connectdata *conn, struct h1_tunnel_state **pts)
int sockindex)
{ {
struct h1_tunnel_state *ts; struct h1_tunnel_state *ts;
CURLcode result; CURLcode result;
if(conn->handler->flags & PROTOPT_NOTCPPROXY) { if(cf->conn->handler->flags & PROTOPT_NOTCPPROXY) {
failf(data, "%s cannot be done over CONNECT", conn->handler->scheme); failf(data, "%s cannot be done over CONNECT", cf->conn->handler->scheme);
return CURLE_UNSUPPORTED_PROTOCOL; return CURLE_UNSUPPORTED_PROTOCOL;
} }
@ -146,15 +129,14 @@ static CURLcode tunnel_init(struct h1_tunnel_state **pts,
if(!ts) if(!ts)
return CURLE_OUT_OF_MEMORY; return CURLE_OUT_OF_MEMORY;
ts->sockindex = sockindex;
infof(data, "allocate connect buffer"); infof(data, "allocate connect buffer");
Curl_dyn_init(&ts->rcvbuf, DYN_PROXY_CONNECT_HEADERS); 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; *pts = ts;
connkeep(conn, "HTTP proxy CONNECT"); connkeep(cf->conn, "HTTP proxy CONNECT");
return tunnel_reinit(ts, conn, data); return tunnel_reinit(cf, data, ts);
} }
static void h1_tunnel_go_state(struct Curl_cfilter *cf, 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) { switch(new_state) {
case H1_TUNNEL_INIT: case H1_TUNNEL_INIT:
CURL_TRC_CF(data, cf, "new tunnel state 'init'"); CURL_TRC_CF(data, cf, "new tunnel state 'init'");
tunnel_reinit(ts, cf->conn, data); tunnel_reinit(cf, data, ts);
break; break;
case H1_TUNNEL_CONNECT: 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'"); CURL_TRC_CF(data, cf, "new tunnel state 'failed'");
ts->tunnel_state = new_state; ts->tunnel_state = new_state;
Curl_dyn_reset(&ts->rcvbuf); Curl_dyn_reset(&ts->rcvbuf);
Curl_dyn_reset(&ts->req); Curl_dyn_reset(&ts->request_data);
/* restore the protocol pointer */ /* restore the protocol pointer */
data->info.httpcode = 0; /* clear it as it might've been used for the data->info.httpcode = 0; /* clear it as it might've been used for the
proxy */ proxy */
@ -229,171 +211,80 @@ static void tunnel_free(struct Curl_cfilter *cf,
if(ts) { if(ts) {
h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
Curl_dyn_free(&ts->rcvbuf); Curl_dyn_free(&ts->rcvbuf);
Curl_dyn_free(&ts->req); Curl_dyn_free(&ts->request_data);
free(ts); free(ts);
cf->ctx = NULL; 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 #ifndef USE_HYPER
static CURLcode start_CONNECT(struct Curl_cfilter *cf, static CURLcode start_CONNECT(struct Curl_cfilter *cf,
struct Curl_easy *data, struct Curl_easy *data,
struct h1_tunnel_state *ts) struct h1_tunnel_state *ts)
{ {
struct connectdata *conn = cf->conn; struct httpreq *req = NULL;
char *hostheader = NULL; int http_minor;
char *host = NULL;
const char *httpv;
CURLcode result; 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 /* This only happens if we've looped here due to authentication
reasons, and we don't really use the newly cloned URL here reasons, and we don't really use the newly cloned URL here
then. Just free() it. */ then. Just free() it. */
Curl_safefree(data->req.newurl); Curl_safefree(data->req.newurl);
result = CONNECT_host(data, conn, result = Curl_http_proxy_create_CONNECT(&req, cf, data, 1);
ts->hostname, ts->remote_port,
&hostheader, &host);
if(result) if(result)
goto out; goto out;
/* Setup the proxy-authorization header, if any */ infof(data, "Establish HTTP proxy tunnel to %s", req->authority);
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"; Curl_dyn_reset(&ts->request_data);
ts->nsent = 0;
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:"");
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;
/* 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);
ts->headerlines = 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: out:
if(result) if(result)
failf(data, "Failed sending CONNECT to proxy"); failf(data, "Failed sending CONNECT to proxy");
free(host); if(req)
free(hostheader); Curl_http_req_free(req);
return result; return result;
} }
static CURLcode send_CONNECT(struct Curl_easy *data, static CURLcode send_CONNECT(struct Curl_cfilter *cf,
struct connectdata *conn, struct Curl_easy *data,
struct h1_tunnel_state *ts, struct h1_tunnel_state *ts,
bool *done) bool *done)
{ {
struct SingleRequest *k = &data->req; char *buf = Curl_dyn_ptr(&ts->request_data);
struct HTTP *http = &ts->CONNECT; size_t request_len = Curl_dyn_len(&ts->request_data);
size_t blen = request_len;
CURLcode result = CURLE_OK; CURLcode result = CURLE_OK;
ssize_t nwritten;
if(http->sending != HTTPSEND_REQUEST) if(blen <= ts->nsent)
goto out; goto out; /* we are done */
if(!ts->nsend) { blen -= ts->nsent;
size_t fillcount; buf += ts->nsent;
k->upload_fromhere = data->state.ulbuf;
result = Curl_fillreadbuffer(data, data->set.upload_buffer_size, nwritten = cf->next->cft->do_send(cf->next, data, buf, blen, &result);
&fillcount); if(nwritten < 0) {
if(result) if(result == CURLE_AGAIN) {
goto out; result = CURLE_OK;
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; goto out;
/* send to debug callback! */
Curl_debug(data, CURLINFO_HEADER_OUT,
k->upload_fromhere, bytes_written);
ts->nsend -= bytes_written;
k->upload_fromhere += bytes_written;
} }
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: out:
if(result) if(result)
failf(data, "Failed sending CONNECT to proxy"); failf(data, "Failed sending CONNECT to proxy");
*done = (http->sending != HTTPSEND_REQUEST); *done = (!result && (ts->nsent >= request_len));
return result; return result;
} }
@ -491,7 +382,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
error = SELECT_OK; error = SELECT_OK;
*done = FALSE; *done = FALSE;
if(!Curl_conn_data_pending(data, ts->sockindex)) if(!Curl_conn_data_pending(data, cf->sockindex))
return CURLE_OK; return CURLE_OK;
while(ts->keepon) { while(ts->keepon) {
@ -669,6 +560,41 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
} }
#else /* USE_HYPER */ #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 */ /* The Hyper version of CONNECT */
static CURLcode start_CONNECT(struct Curl_cfilter *cf, static CURLcode start_CONNECT(struct Curl_cfilter *cf,
struct Curl_easy *data, struct Curl_easy *data,
@ -685,9 +611,10 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf,
hyper_task *task = NULL; /* for the handshake */ hyper_task *task = NULL; /* for the handshake */
hyper_clientconn *client = NULL; hyper_clientconn *client = NULL;
hyper_task *sendtask = NULL; /* for the send */ hyper_task *sendtask = NULL; /* for the send */
char *hostheader = NULL; /* for CONNECT */ char *authority = NULL; /* for CONNECT */
char *host = NULL; /* Host: */ char *host_header = NULL; /* Host: */
CURLcode result = CURLE_OUT_OF_MEMORY; CURLcode result = CURLE_OUT_OF_MEMORY;
(void)ts;
io = hyper_io_new(); io = hyper_io_new();
if(!io) { if(!io) {
@ -765,27 +692,25 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf,
goto error; 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 /* This only happens if we've looped here due to authentication
reasons, and we don't really use the newly cloned URL here reasons, and we don't really use the newly cloned URL here
then. Just free() it. */ then. Just free() it. */
Curl_safefree(data->req.newurl); Curl_safefree(data->req.newurl);
result = CONNECT_host(data, conn, ts->hostname, ts->remote_port, result = CONNECT_host(cf, data, &authority, &host_header);
&hostheader, &host);
if(result) if(result)
goto error; goto error;
if(hyper_request_set_uri(req, (uint8_t *)hostheader, infof(data, "Establish HTTP proxy tunnel to %s", authority);
strlen(hostheader))) {
if(hyper_request_set_uri(req, (uint8_t *)authority,
strlen(authority))) {
failf(data, "error setting path"); failf(data, "error setting path");
result = CURLE_OUT_OF_MEMORY; result = CURLE_OUT_OF_MEMORY;
goto error; goto error;
} }
if(data->set.verbose) { 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) { if(!se) {
result = CURLE_OUT_OF_MEMORY; result = CURLE_OUT_OF_MEMORY;
goto error; goto error;
@ -795,10 +720,10 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf,
} }
/* Setup the proxy-authorization header, if any */ /* Setup the proxy-authorization header, if any */
result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET, result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET,
hostheader, TRUE); authority, TRUE);
if(result) if(result)
goto error; goto error;
Curl_safefree(hostheader); Curl_safefree(authority);
/* default is 1.1 */ /* default is 1.1 */
if((conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) && 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; result = CURLE_OUT_OF_MEMORY;
goto error; goto error;
} }
if(host) { if(host_header) {
result = Curl_hyper_header(data, headers, host); result = Curl_hyper_header(data, headers, host_header);
if(result) if(result)
goto error; goto error;
Curl_safefree(host); Curl_safefree(host_header);
} }
if(data->state.aptr.proxyuserpwd) { if(data->state.aptr.proxyuserpwd) {
@ -873,8 +798,8 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf,
client = NULL; client = NULL;
error: error:
free(host); free(host_header);
free(hostheader); free(authority);
if(io) if(io)
hyper_io_free(io); hyper_io_free(io);
if(options) if(options)
@ -889,12 +814,13 @@ error:
return result; return result;
} }
static CURLcode send_CONNECT(struct Curl_easy *data, static CURLcode send_CONNECT(struct Curl_cfilter *cf,
struct connectdata *conn, struct Curl_easy *data,
struct h1_tunnel_state *ts, struct h1_tunnel_state *ts,
bool *done) bool *done)
{ {
struct hyptransfer *h = &data->hyp; struct hyptransfer *h = &data->hyp;
struct connectdata *conn = cf->conn;
hyper_task *task = NULL; hyper_task *task = NULL;
hyper_error *hypererr = NULL; hyper_error *hypererr = NULL;
CURLcode result = CURLE_OK; CURLcode result = CURLE_OK;
@ -994,7 +920,7 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf,
case H1_TUNNEL_CONNECT: case H1_TUNNEL_CONNECT:
/* see that the request is completely sent */ /* see that the request is completely sent */
CURL_TRC_CF(data, cf, "CONNECT send"); 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) if(result || !done)
goto out; goto out;
h1_tunnel_go_state(cf, ts, H1_TUNNEL_RECEIVE, data); 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; *done = FALSE;
if(!ts) { if(!ts) {
result = tunnel_init(&ts, data, cf->conn, cf->sockindex); result = tunnel_init(cf, data, &ts);
if(result) if(result)
return result; return result;
cf->ctx = ts; cf->ctx = ts;

View File

@ -84,7 +84,8 @@ static CURLcode tunnel_stream_init(struct Curl_cfilter *cf,
{ {
const char *hostname; const char *hostname;
int port; int port;
bool ipv6_ip = cf->conn->bits.ipv6_ip; bool ipv6_ip;
CURLcode result;
ts->state = H2_TUNNEL_INIT; ts->state = H2_TUNNEL_INIT;
ts->stream_id = -1; ts->stream_id = -1;
@ -92,22 +93,9 @@ static CURLcode tunnel_stream_init(struct Curl_cfilter *cf,
BUFQ_OPT_SOFT_LIMIT); BUFQ_OPT_SOFT_LIMIT);
Curl_bufq_init(&ts->sendbuf, PROXY_H2_CHUNK_SIZE, H2_TUNNEL_SEND_CHUNKS); Curl_bufq_init(&ts->sendbuf, PROXY_H2_CHUNK_SIZE, H2_TUNNEL_SEND_CHUNKS);
if(cf->conn->bits.conn_to_host) result = Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip);
hostname = cf->conn->conn_to_host.name; if(result)
else if(cf->sockindex == SECONDARYSOCKET) return result;
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);
ts->authority = /* host:port with IPv6 support */ ts->authority = /* host:port with IPv6 support */
aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"", port); 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; CURLcode result;
struct httpreq *req = NULL; struct httpreq *req = NULL;
infof(data, "Establish HTTP/2 proxy tunnel to %s", ts->authority); result = Curl_http_proxy_create_CONNECT(&req, cf, data, 2);
result = Curl_http_req_make(&req, "CONNECT", sizeof("CONNECT")-1,
NULL, 0, ts->authority, strlen(ts->authority),
NULL, 0);
if(result) if(result)
goto out; goto out;
/* Setup the proxy-authorization header, if any */ infof(data, "Establish HTTP/2 proxy tunnel to %s", req->authority);
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;
result = proxy_h2_submit(&ts->stream_id, cf, data, ctx->h2, req, result = proxy_h2_submit(&ts->stream_id, cf, data, ctx->h2, req,
NULL, ts, tunnel_send_callback, cf); NULL, ts, tunnel_send_callback, cf);

View File

@ -344,6 +344,8 @@ size_t Curl_dynhds_cremove(struct dynhds *dynhds, const char *name)
return Curl_dynhds_remove(dynhds, name, strlen(name)); return Curl_dynhds_remove(dynhds, name, strlen(name));
} }
#endif
CURLcode Curl_dynhds_h1_dprint(struct dynhds *dynhds, struct dynbuf *dbuf) CURLcode Curl_dynhds_h1_dprint(struct dynhds *dynhds, struct dynbuf *dbuf)
{ {
CURLcode result = CURLE_OK; CURLcode result = CURLE_OK;
@ -363,4 +365,3 @@ CURLcode Curl_dynhds_h1_dprint(struct dynhds *dynhds, struct dynbuf *dbuf)
return result; return result;
} }
#endif

View File

@ -318,5 +318,29 @@ out:
return nread; 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 */ #endif /* !CURL_DISABLE_HTTP */

View File

@ -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, CURLcode Curl_h1_req_dprint(const struct httpreq *req,
struct dynbuf *dbuf); struct dynbuf *dbuf);
CURLcode Curl_h1_req_write_head(struct httpreq *req, int http_minor,
struct dynbuf *dbuf);
#endif /* !CURL_DISABLE_HTTP */ #endif /* !CURL_DISABLE_HTTP */
#endif /* HEADER_CURL_HTTP1_H */ #endif /* HEADER_CURL_HTTP1_H */

View File

@ -52,6 +52,113 @@
#include "memdebug.h" #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 { struct cf_proxy_ctx {
/* the protocol specific sub-filter we install during connect */ /* the protocol specific sub-filter we install during connect */
struct Curl_cfilter *cf_protocol; struct Curl_cfilter *cf_protocol;
@ -105,7 +212,6 @@ connect_sub:
break; break;
#endif #endif
default: default:
CURL_TRC_CF(data, cf, "installing subfilter for default HTTP/1.1");
infof(data, "CONNECT tunnel: unsupported ALPN(%d) negotiated", alpn); infof(data, "CONNECT tunnel: unsupported ALPN(%d) negotiated", alpn);
result = CURLE_COULDNT_CONNECT; result = CURLE_COULDNT_CONNECT;
goto out; goto out;

View File

@ -30,6 +30,15 @@
#include "urldata.h" #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 */ /* Default proxy timeout in milliseconds */
#define PROXY_TIMEOUT (3600*1000) #define PROXY_TIMEOUT (3600*1000)