Browse Source

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
Stefan Eissing 7 months ago
parent
commit
bb4032a152
7 changed files with 258 additions and 229 deletions
  1. 107 181
      lib/cf-h1-proxy.c
  2. 7 46
      lib/cf-h2-proxy.c
  3. 2 1
      lib/dynhds.c
  4. 24 0
      lib/http1.c
  5. 2 0
      lib/http1.h
  6. 107 1
      lib/http_proxy.c
  7. 9 0
      lib/http_proxy.h

+ 107 - 181
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;

+ 7 - 46
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);

+ 2 - 1
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

+ 24 - 0
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 */

+ 2 - 0
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 */

+ 107 - 1
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;

+ 9 - 0
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)