]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
Curl_http(), decomplexify
authorStefan Eissing <stefan@eissing.org>
Mon, 1 Sep 2025 13:52:30 +0000 (15:52 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Tue, 2 Sep 2025 05:54:46 +0000 (07:54 +0200)
Split out adding of individual request headers into a switch. Check
the connection http version only on fresh connections, use separate
methods.

Add TE: header directly without allocation. Add bit for indicating
Connection: header has been added and custom headers should not do
that again.

Closes #18444

25 files changed:
lib/http.c
lib/http.h
lib/http2.c
lib/http_aws_sigv4.c
lib/http_proxy.c
lib/url.c
lib/urldata.h
lib/ws.c
tests/data/test1122
tests/data/test1123
tests/data/test1124
tests/data/test1125
tests/data/test1170
tests/data/test1171
tests/data/test1277
tests/data/test1546
tests/data/test1704
tests/data/test1800
tests/data/test2300
tests/data/test2301
tests/data/test2302
tests/data/test2303
tests/data/test2304
tests/data/test387
tests/data/test418

index 3f8c69e49232b9d68398ed0b0cf8ce5ff4dc2cf0..0ce3b25bc3b15b62b3ee71f494ed35eb0e694d32 100644 (file)
@@ -105,24 +105,16 @@ static void http_exp100_got100(struct Curl_easy *data);
 static CURLcode http_firstwrite(struct Curl_easy *data);
 static CURLcode http_header(struct Curl_easy *data,
                             const char *hd, size_t hdlen);
-static CURLcode http_host(struct Curl_easy *data, struct connectdata *conn);
 static CURLcode http_range(struct Curl_easy *data,
                            Curl_HttpReq httpreq);
-static CURLcode http_req_complete(struct Curl_easy *data,
-                                  struct dynbuf *r, int httpversion,
-                                  Curl_HttpReq httpreq);
-static CURLcode http_req_set_reader(struct Curl_easy *data,
-                                    Curl_HttpReq httpreq, int httpversion,
-                                    const char **tep);
+static CURLcode http_req_set_TE(struct Curl_easy *data,
+                                struct dynbuf *req,
+                                int httpversion);
 static CURLcode http_size(struct Curl_easy *data);
 static CURLcode http_statusline(struct Curl_easy *data,
                                 struct connectdata *conn);
-static CURLcode http_target(struct Curl_easy *data, struct connectdata *conn,
-                            struct dynbuf *req);
+static CURLcode http_target(struct Curl_easy *data, struct dynbuf *req);
 static CURLcode http_useragent(struct Curl_easy *data);
-#ifdef HAVE_LIBZ
-static CURLcode http_transferencode(struct Curl_easy *data);
-#endif
 
 
 /*
@@ -1709,10 +1701,8 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data,
                  we will force length zero then */
               curlx_str_casecompare(&name, "Content-Length"))
         ;
-      else if(data->state.aptr.te &&
-              /* when asking for Transfer-Encoding, do not pass on a custom
-                 Connection: */
-              curlx_str_casecompare(&name, "Connection"))
+      else if(curlx_str_casecompare(&name, "Connection"))
+        /* Normal Connection: header generation takes care of this */
         ;
       else if((httpversion >= 20) &&
               curlx_str_casecompare(&name, "Transfer-Encoding"))
@@ -1817,14 +1807,14 @@ CURLcode Curl_add_timecondition(struct Curl_easy *data,
 }
 #endif
 
-void Curl_http_method(struct Curl_easy *data, struct connectdata *conn,
+void Curl_http_method(struct Curl_easy *data,
                       const char **method, Curl_HttpReq *reqp)
 {
   Curl_HttpReq httpreq = (Curl_HttpReq)data->state.httpreq;
   const char *request;
-  if(conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS))
+  if(data->conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS))
     httpreq = HTTPREQ_GET;
-  else if((conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_FTP)) &&
+  else if((data->conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_FTP)) &&
      data->state.upload)
     httpreq = HTTPREQ_PUT;
 
@@ -1875,10 +1865,12 @@ static CURLcode http_useragent(struct Curl_easy *data)
 }
 
 
-static CURLcode http_host(struct Curl_easy *data, struct connectdata *conn)
+static CURLcode http_set_aptr_host(struct Curl_easy *data)
 {
-  const char *ptr;
+  struct connectdata *conn = data->conn;
   struct dynamically_allocated_data *aptr = &data->state.aptr;
+  const char *ptr;
+
   if(!data->state.this_is_a_follow) {
     /* Free to avoid leaking memory on multiple requests */
     free(data->state.first_host);
@@ -1966,7 +1958,6 @@ static CURLcode http_host(struct Curl_easy *data, struct connectdata *conn)
  * Append the request-target to the HTTP request
  */
 static CURLcode http_target(struct Curl_easy *data,
-                            struct connectdata *conn,
                             struct dynbuf *r)
 {
   CURLcode result = CURLE_OK;
@@ -1979,7 +1970,7 @@ static CURLcode http_target(struct Curl_easy *data,
   }
 
 #ifndef CURL_DISABLE_PROXY
-  if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
+  if(data->conn->bits.httpproxy && !data->conn->bits.tunnel_proxy) {
     /* Using a proxy but does not tunnel through it */
 
     /* The path sent to the proxy is in fact the entire URL. But if the remote
@@ -1993,8 +1984,8 @@ static CURLcode http_target(struct Curl_easy *data,
     if(!h)
       return CURLE_OUT_OF_MEMORY;
 
-    if(conn->host.dispname != conn->host.name) {
-      uc = curl_url_set(h, CURLUPART_HOST, conn->host.name, 0);
+    if(data->conn->host.dispname != data->conn->host.name) {
+      uc = curl_url_set(h, CURLUPART_HOST, data->conn->host.name, 0);
       if(uc) {
         curl_url_cleanup(h);
         return CURLE_OUT_OF_MEMORY;
@@ -2060,8 +2051,6 @@ static CURLcode http_target(struct Curl_easy *data,
   }
 
   else
-#else
-    (void)conn; /* not used in disabled-proxy builds */
 #endif
   {
     result = curlx_dyn_add(r, path);
@@ -2247,21 +2236,13 @@ static CURLcode http_resume(struct Curl_easy *data, Curl_HttpReq httpreq)
   return CURLE_OK;
 }
 
-static CURLcode http_req_set_reader(struct Curl_easy *data,
-                                    Curl_HttpReq httpreq, int httpversion,
-                                    const char **tep)
+static CURLcode http_req_set_TE(struct Curl_easy *data,
+                                struct dynbuf *req,
+                                int httpversion)
 {
   CURLcode result = CURLE_OK;
   const char *ptr;
 
-  result = set_reader(data, httpreq);
-  if(result)
-    return result;
-
-  result = http_resume(data, httpreq);
-  if(result)
-    return result;
-
   ptr = Curl_checkheaders(data, STRCONST("Transfer-Encoding"));
   if(ptr) {
     /* Some kind of TE is requested, check if 'chunked' is chosen */
@@ -2295,7 +2276,7 @@ static CURLcode http_req_set_reader(struct Curl_easy *data,
     }
 
     if(data->req.upload_chunky)
-      *tep = "Transfer-Encoding: chunked\r\n";
+      result = curlx_dyn_add(req, "Transfer-Encoding: chunked\r\n");
   }
   return result;
 }
@@ -2335,9 +2316,10 @@ static CURLcode addexpect(struct Curl_easy *data, struct dynbuf *r,
   return CURLE_OK;
 }
 
-static CURLcode http_req_complete(struct Curl_easy *data,
-                                  struct dynbuf *r, int httpversion,
-                                  Curl_HttpReq httpreq)
+static CURLcode http_add_content_hds(struct Curl_easy *data,
+                                     struct dynbuf *r,
+                                     int httpversion,
+                                     Curl_HttpReq httpreq)
 {
   CURLcode result = CURLE_OK;
   curl_off_t req_clen;
@@ -2405,26 +2387,17 @@ static CURLcode http_req_complete(struct Curl_easy *data,
     break;
   }
 
-  /* end of headers */
-  result = curlx_dyn_addn(r, STRCONST("\r\n"));
-  if(!result) {
-    Curl_pgrsSetUploadSize(data, req_clen);
-    if(announced_exp100)
-      result = http_exp100_add_reader(data);
-  }
+  Curl_pgrsSetUploadSize(data, req_clen);
+  if(announced_exp100)
+    result = http_exp100_add_reader(data);
 
 out:
-  if(!result) {
-    /* setup variables for the upcoming transfer */
-    Curl_xfer_setup_sendrecv(data, FIRSTSOCKET, -1);
-  }
   return result;
 }
 
 #ifndef CURL_DISABLE_COOKIES
 
 static CURLcode http_cookies(struct Curl_easy *data,
-                             struct connectdata *conn,
                              struct dynbuf *r)
 {
   CURLcode result = CURLE_OK;
@@ -2441,9 +2414,9 @@ static CURLcode http_cookies(struct Curl_easy *data,
 
     if(data->cookies && data->state.cookie_engine) {
       const char *host = data->state.aptr.cookiehost ?
-        data->state.aptr.cookiehost : conn->host.name;
+        data->state.aptr.cookiehost : data->conn->host.name;
       Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
-      rc = Curl_cookie_getlist(data, conn, host, &list);
+      rc = Curl_cookie_getlist(data, data->conn, host, &list);
       Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
     }
     if(!rc) {
@@ -2494,7 +2467,7 @@ static CURLcode http_cookies(struct Curl_easy *data,
   return result;
 }
 #else
-#define http_cookies(a,b,c) CURLE_OK
+#define http_cookies(a,b) CURLE_OK
 #endif
 
 static CURLcode http_range(struct Curl_easy *data,
@@ -2616,63 +2589,13 @@ static CURLcode http_firstwrite(struct Curl_easy *data)
   return CURLE_OK;
 }
 
-#ifdef HAVE_LIBZ
-static CURLcode http_transferencode(struct Curl_easy *data)
-{
-  if(!Curl_checkheaders(data, STRCONST("TE")) &&
-     data->set.http_transfer_encoding) {
-    /* When we are to insert a TE: header in the request, we must also insert
-       TE in a Connection: header, so we need to merge the custom provided
-       Connection: header and prevent the original to get sent. Note that if
-       the user has inserted his/her own TE: header we do not do this magic
-       but then assume that the user will handle it all! */
-    char *cptr = Curl_checkheaders(data, STRCONST("Connection"));
-#define TE_HEADER "TE: gzip\r\n"
-
-    Curl_safefree(data->state.aptr.te);
-
-    if(cptr) {
-      cptr = Curl_copy_header_value(cptr);
-      if(!cptr)
-        return CURLE_OUT_OF_MEMORY;
-    }
-
-    /* Create the (updated) Connection: header */
-    data->state.aptr.te = aprintf("Connection: %s%sTE\r\n" TE_HEADER,
-                                cptr ? cptr : "", (cptr && *cptr) ? ", ":"");
-
-    free(cptr);
-    if(!data->state.aptr.te)
-      return CURLE_OUT_OF_MEMORY;
-  }
-  return CURLE_OK;
-}
-#endif
-
-/*
- * Curl_http() gets called from the generic multi_do() function when an HTTP
- * request is to be performed. This creates and sends a properly constructed
- * HTTP request.
- */
-CURLcode Curl_http(struct Curl_easy *data, bool *done)
+static CURLcode http_check_new_conn(struct Curl_easy *data)
 {
   struct connectdata *conn = data->conn;
-  CURLcode result = CURLE_OK;
-  Curl_HttpReq httpreq;
-  const char *te = ""; /* transfer-encoding */
-  const char *request;
-  const char *httpstring;
-  struct dynbuf req;
-  char *altused = NULL;
-  const char *p_accept;      /* Accept: string */
-  unsigned char httpversion;
-  const char *alpn;
   const char *info_version = NULL;
+  const char *alpn;
+  CURLcode result;
 
-  /* Always consider the DO phase done after this function call, even if there
-     may be parts of the request that are not yet sent, since we can deal with
-     the rest of the request in the PERFORM phase. */
-  *done = TRUE;
   alpn = Curl_conn_get_alpn_negotiated(data, conn);
   if(alpn && !strcmp("h3", alpn)) {
     DEBUGASSERT(Curl_conn_http_version(data, conn) == 30);
@@ -2684,7 +2607,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
        conn->bits.proxy && !conn->bits.tunnel_proxy) {
       result = Curl_http2_switch(data);
       if(result)
-        goto fail;
+        return result;
     }
     else
 #endif
@@ -2697,7 +2620,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
       DEBUGF(infof(data, "HTTP/2 over clean TCP"));
       result = Curl_http2_switch(data);
       if(result)
-        goto fail;
+        return result;
       info_version = "HTTP/2";
       /* There is no ALPN here, but the connection is now definitely h2 */
       conn->httpversion_seen = 20;
@@ -2706,213 +2629,321 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
       info_version = "HTTP/1.x";
   }
 
-  if(info_version && !data->conn->bits.reuse)
+  if(info_version)
     infof(data, "using %s", info_version);
+  return CURLE_OK;
+}
 
-  /* Add collecting of headers written to client. For a new connection,
-   * we might have done that already, but reuse
-   * or multiplex needs it here as well. */
-  result = Curl_headers_init(data);
-  if(result)
-    goto fail;
+static CURLcode http_add_connection_hd(struct Curl_easy *data,
+                                       struct dynbuf *req)
+{
+  char *custom = Curl_checkheaders(data, STRCONST("Connection"));
+  char *custom_val = custom ? Curl_copy_header_value(custom) : NULL;
+  const char *sep = (custom_val && *custom_val) ? ", " : "Connection: ";
+  CURLcode result = CURLE_OK;
+  size_t rlen = curlx_dyn_len(req);
 
-  result = http_host(data, conn);
-  if(result)
-    goto fail;
+  if(custom && !custom_val)
+    return CURLE_OUT_OF_MEMORY;
 
-  result = http_useragent(data);
-  if(result)
-    goto fail;
+  if(custom_val && *custom_val)
+    result = curlx_dyn_addf(req, "Connection: %s", custom_val);
+  if(!result && data->state.http_hd_te) {
+    result = curlx_dyn_addf(req, "%s%s", sep, "TE");
+    sep = ", ";
+  }
+  if(!result && data->state.http_hd_upgrade) {
+    result = curlx_dyn_addf(req, "%s%s", sep, "Upgrade");
+    sep = ", ";
+  }
+  if(!result && data->state.http_hd_h2_settings) {
+    result = curlx_dyn_addf(req, "%s%s", sep, "HTTP2-Settings");
+  }
+  if(rlen < curlx_dyn_len(req))
+    result = curlx_dyn_addn(req, STRCONST("\r\n"));
 
-  Curl_http_method(data, conn, &request, &httpreq);
+  free(custom_val);
+  return result;
+}
 
-  /* setup the authentication headers */
-  {
-    char *pq = NULL;
-    if(data->state.up.query) {
-      pq = aprintf("%s?%s", data->state.up.path, data->state.up.query);
-      if(!pq)
-        return CURLE_OUT_OF_MEMORY;
-    }
-    result = Curl_http_output_auth(data, conn, request, httpreq,
-                                   (pq ? pq : data->state.up.path), FALSE);
-    free(pq);
-    if(result)
-      goto fail;
-  }
+/* Header identifier in order we send them by default */
+typedef enum {
+  H1_HD_REQUEST,
+  H1_HD_HOST,
+#ifndef CURL_DISABLE_PROXY
+  H1_HD_PROXY_AUTH,
+#endif
+  H1_HD_USER_AUTH,
+  H1_HD_RANGE,
+  H1_HD_USER_AGENT,
+  H1_HD_ACCEPT,
+  H1_HD_TE,
+  H1_HD_ACCEPT_ENCODING,
+  H1_HD_REFERER,
+#ifndef CURL_DISABLE_PROXY
+  H1_HD_PROXY_CONNECTION,
+#endif
+  H1_HD_TRANSFER_ENCODING,
+#ifndef CURL_DISABLE_ALTSVC
+  H1_HD_ALT_USED,
+#endif
+  H1_HD_UPGRADE,
+  H1_HD_COOKIES,
+  H1_HD_CONDITIONALS,
+  H1_HD_CUSTOM,
+  H1_HD_CONTENT,
+  H1_HD_CONNECTION,
+  H1_HD_LAST  /* the last, empty header line */
+} http_hd_t;
+
+static CURLcode http_add_hd(struct Curl_easy *data,
+                            struct dynbuf *req,
+                            http_hd_t id,
+                            unsigned char httpversion,
+                            const char *method,
+                            Curl_HttpReq httpreq)
+{
+  CURLcode result = CURLE_OK;
+  switch(id) {
+  case H1_HD_REQUEST:
+    /* add the main request stuff */
+    /* GET/HEAD/POST/PUT */
+    result = curlx_dyn_addf(req, "%s ", method);
+    if(!result)
+      result = http_target(data, req);
+    if(!result)
+      result = curlx_dyn_addf(req, " HTTP/%s\r\n",
+                              get_http_string(httpversion));
+    break;
 
-  Curl_safefree(data->state.aptr.ref);
-  if(data->state.referer && !Curl_checkheaders(data, STRCONST("Referer"))) {
-    data->state.aptr.ref = aprintf("Referer: %s\r\n", data->state.referer);
-    if(!data->state.aptr.ref)
-      return CURLE_OUT_OF_MEMORY;
-  }
+  case H1_HD_HOST:
+    if(data->state.aptr.host)
+      result = curlx_dyn_add(req, data->state.aptr.host);
+    break;
 
-  if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) &&
-     data->set.str[STRING_ENCODING]) {
-    free(data->state.aptr.accept_encoding);
-    data->state.aptr.accept_encoding =
-      aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]);
-    if(!data->state.aptr.accept_encoding)
-      return CURLE_OUT_OF_MEMORY;
-  }
-  else
-    Curl_safefree(data->state.aptr.accept_encoding);
+#ifndef CURL_DISABLE_PROXY
+  case H1_HD_PROXY_AUTH:
+    if(data->state.aptr.proxyuserpwd)
+      result = curlx_dyn_add(req, data->state.aptr.proxyuserpwd);
+    break;
+#endif
+
+  case H1_HD_USER_AUTH:
+    if(data->state.aptr.userpwd)
+      result = curlx_dyn_add(req, data->state.aptr.userpwd);
+    break;
 
+  case H1_HD_RANGE:
+    if(data->state.use_range && data->state.aptr.rangeline)
+      result = curlx_dyn_add(req, data->state.aptr.rangeline);
+    break;
+
+  case H1_HD_USER_AGENT:
+    if(data->set.str[STRING_USERAGENT] && /* User-Agent: */
+       *data->set.str[STRING_USERAGENT] &&
+       data->state.aptr.uagent)
+      result = curlx_dyn_add(req, data->state.aptr.uagent);
+    break;
+
+  case H1_HD_ACCEPT:
+    if(!Curl_checkheaders(data, STRCONST("Accept")))
+      result = curlx_dyn_add(req, "Accept: */*\r\n");
+    break;
+
+  case H1_HD_TE:
 #ifdef HAVE_LIBZ
-  /* we only consider transfer-encoding magic if libz support is built-in */
-  result = http_transferencode(data);
-  if(result)
-    goto fail;
+    if(!Curl_checkheaders(data, STRCONST("TE")) &&
+       data->set.http_transfer_encoding) {
+      data->state.http_hd_te = TRUE;
+      result = curlx_dyn_add(req, "TE: gzip\r\n");
+    }
 #endif
+    break;
 
-  httpversion = http_request_version(data);
-  httpstring = get_http_string(httpversion);
+  case H1_HD_ACCEPT_ENCODING:
+    Curl_safefree(data->state.aptr.accept_encoding);
+    if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) &&
+       data->set.str[STRING_ENCODING])
+      result = curlx_dyn_addf(req, "Accept-Encoding: %s\r\n",
+                              data->set.str[STRING_ENCODING]);
+    break;
 
-  result = http_req_set_reader(data, httpreq, httpversion, &te);
-  if(result)
-    goto fail;
+  case H1_HD_REFERER:
+    Curl_safefree(data->state.aptr.ref);
+    if(data->state.referer && !Curl_checkheaders(data, STRCONST("Referer")))
+      result = curlx_dyn_addf(req, "Referer: %s\r\n", data->state.referer);
+    break;
+
+#ifndef CURL_DISABLE_PROXY
+  case H1_HD_PROXY_CONNECTION:
+    if(data->conn->bits.httpproxy &&
+       !data->conn->bits.tunnel_proxy &&
+       !Curl_checkheaders(data, STRCONST("Proxy-Connection")) &&
+       !Curl_checkProxyheaders(data, data->conn, STRCONST("Proxy-Connection")))
+      result = curlx_dyn_add(req, "Proxy-Connection: Keep-Alive\r\n");
+    break;
+#endif
 
-  p_accept = Curl_checkheaders(data,
-                               STRCONST("Accept")) ? NULL : "Accept: */*\r\n";
+  case H1_HD_TRANSFER_ENCODING:
+    result = http_req_set_TE(data, req, httpversion);
+    break;
 
-  result = http_range(data, httpreq);
-  if(result)
-    goto fail;
+#ifndef CURL_DISABLE_ALTSVC
+  case H1_HD_ALT_USED:
+    if(data->conn->bits.altused &&
+       !Curl_checkheaders(data, STRCONST("Alt-Used")))
+      result = curlx_dyn_addf(req, "Alt-Used: %s:%d\r\n",
+                              data->conn->conn_to_host.name,
+                              data->conn->conn_to_port);
+    break;
+#endif
+
+  case H1_HD_UPGRADE:
+    if(!Curl_conn_is_ssl(data->conn, FIRSTSOCKET) && (httpversion < 20) &&
+       (data->state.http_neg.wanted & CURL_HTTP_V2x) &&
+       data->state.http_neg.h2_upgrade) {
+      /* append HTTP2 upgrade magic stuff to the HTTP request if it is not done
+         over SSL */
+      result = Curl_http2_request_upgrade(req, data);
+    }
+#ifndef CURL_DISABLE_WEBSOCKETS
+    if(!result && data->conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS))
+      result = Curl_ws_request(data, req);
+#endif
+    break;
 
+  case H1_HD_COOKIES:
+    result = http_cookies(data, req);
+    break;
+
+  case H1_HD_CONDITIONALS:
+    result = Curl_add_timecondition(data, req);
+    break;
+
+  case H1_HD_CUSTOM:
+    result = Curl_add_custom_headers(data, FALSE, httpversion, req);
+    break;
+
+  case H1_HD_CONTENT:
+    result = http_add_content_hds(data, req, httpversion, httpreq);
+    break;
+
+  case H1_HD_CONNECTION: {
+    result = http_add_connection_hd(data, req);
+    break;
+  }
+
+  case H1_HD_LAST:
+    result = curlx_dyn_addn(req, STRCONST("\r\n"));
+    break;
+  }
+  return result;
+}
+
+/*
+ * Curl_http() gets called from the generic multi_do() function when an HTTP
+ * request is to be performed. This creates and sends a properly constructed
+ * HTTP request.
+ */
+CURLcode Curl_http(struct Curl_easy *data, bool *done)
+{
+  CURLcode result = CURLE_OK;
+  Curl_HttpReq httpreq;
+  const char *method;
+  struct dynbuf req;
+  unsigned char httpversion;
+  size_t hd_id;
+
+  /* Always consider the DO phase done after this function call, even if there
+     may be parts of the request that are not yet sent, since we can deal with
+     the rest of the request in the PERFORM phase. */
+  *done = TRUE;
   /* initialize a dynamic send-buffer */
   curlx_dyn_init(&req, DYN_HTTP_REQUEST);
-
   /* make sure the header buffer is reset - if there are leftovers from a
      previous transfer */
   curlx_dyn_reset(&data->state.headerb);
 
-  /* add the main request stuff */
-  /* GET/HEAD/POST/PUT */
-  result = curlx_dyn_addf(&req, "%s ", request);
-  if(!result)
-    result = http_target(data, conn, &req);
-  if(result) {
-    curlx_dyn_free(&req);
-    goto fail;
+  if(!data->conn->bits.reuse) {
+    result = http_check_new_conn(data);
+    if(result)
+      goto out;
   }
 
-#ifndef CURL_DISABLE_ALTSVC
-  if(conn->bits.altused && !Curl_checkheaders(data, STRCONST("Alt-Used"))) {
-    altused = aprintf("Alt-Used: %s:%d\r\n",
-                      conn->conn_to_host.name, conn->conn_to_port);
-    if(!altused) {
-      curlx_dyn_free(&req);
-      return CURLE_OUT_OF_MEMORY;
-    }
-  }
-#endif
-  result =
-    curlx_dyn_addf(&req,
-                   " HTTP/%s\r\n" /* HTTP version */
-                   "%s" /* host */
-                   "%s" /* proxyuserpwd */
-                   "%s" /* userpwd */
-                   "%s" /* range */
-                   "%s" /* user agent */
-                   "%s" /* accept */
-                   "%s" /* TE: */
-                   "%s" /* accept-encoding */
-                   "%s" /* referer */
-                   "%s" /* Proxy-Connection */
-                   "%s" /* transfer-encoding */
-                   "%s",/* Alt-Used */
-
-                   httpstring,
-                   (data->state.aptr.host ? data->state.aptr.host : ""),
-#ifndef CURL_DISABLE_PROXY
-                   data->state.aptr.proxyuserpwd ?
-                   data->state.aptr.proxyuserpwd : "",
-#else
-                   "",
-#endif
-                   data->state.aptr.userpwd ? data->state.aptr.userpwd : "",
-                   (data->state.use_range && data->state.aptr.rangeline) ?
-                   data->state.aptr.rangeline : "",
-                   (data->set.str[STRING_USERAGENT] &&
-                    *data->set.str[STRING_USERAGENT] &&
-                    data->state.aptr.uagent) ?
-                   data->state.aptr.uagent : "",
-                   p_accept ? p_accept : "",
-                   data->state.aptr.te ? data->state.aptr.te : "",
-                   (data->set.str[STRING_ENCODING] &&
-                    *data->set.str[STRING_ENCODING] &&
-                    data->state.aptr.accept_encoding) ?
-                   data->state.aptr.accept_encoding : "",
-                   (data->state.referer && data->state.aptr.ref) ?
-                   data->state.aptr.ref : "" /* Referer: <data> */,
-#ifndef CURL_DISABLE_PROXY
-                   (conn->bits.httpproxy &&
-                    !conn->bits.tunnel_proxy &&
-                    !Curl_checkheaders(data, STRCONST("Proxy-Connection")) &&
-                    !Curl_checkProxyheaders(data, conn,
-                                            STRCONST("Proxy-Connection"))) ?
-                   "Proxy-Connection: Keep-Alive\r\n":"",
-#else
-                   "",
-#endif
-                   te,
-                   altused ? altused : ""
-      );
+  /* Add collecting of headers written to client. For a new connection,
+   * we might have done that already, but reuse
+   * or multiplex needs it here as well. */
+  result = Curl_headers_init(data);
+  if(result)
+    goto out;
 
-  /* clear userpwd and proxyuserpwd to avoid reusing old credentials
-   * from reused connections */
-  Curl_safefree(data->state.aptr.userpwd);
-#ifndef CURL_DISABLE_PROXY
-  Curl_safefree(data->state.aptr.proxyuserpwd);
-#endif
-  free(altused);
+  data->state.http_hd_te = FALSE;
+  data->state.http_hd_upgrade = FALSE;
+  data->state.http_hd_h2_settings = FALSE;
 
-  if(result) {
-    curlx_dyn_free(&req);
-    goto fail;
-  }
+  /* what kind of request do we need to send? */
+  Curl_http_method(data, &method, &httpreq);
 
-  if(!Curl_conn_is_ssl(conn, FIRSTSOCKET) && (httpversion < 20) &&
-     (data->state.http_neg.wanted & CURL_HTTP_V2x) &&
-     data->state.http_neg.h2_upgrade) {
-    /* append HTTP2 upgrade magic stuff to the HTTP request if it is not done
-       over SSL */
-    result = Curl_http2_request_upgrade(&req, data);
-    if(result) {
-      curlx_dyn_free(&req);
-      return result;
+  /* select host to send */
+  result = http_set_aptr_host(data);
+  if(!result) {
+    /* setup the authentication headers, how that method and host are known */
+    char *pq = NULL;
+    if(data->state.up.query) {
+      pq = aprintf("%s?%s", data->state.up.path, data->state.up.query);
+      if(!pq)
+        return CURLE_OUT_OF_MEMORY;
     }
+    result = Curl_http_output_auth(data, data->conn, method, httpreq,
+                                   (pq ? pq : data->state.up.path), FALSE);
+    free(pq);
   }
+  if(result)
+    goto out;
 
-  result = http_cookies(data, conn, &req);
-#ifndef CURL_DISABLE_WEBSOCKETS
-  if(!result && conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS))
-    result = Curl_ws_request(data, &req);
-#endif
+  result = http_useragent(data);
+  if(result)
+    goto out;
+
+  /* Setup input reader, resume information and ranges */
+  result = set_reader(data, httpreq);
   if(!result)
-    result = Curl_add_timecondition(data, &req);
+    result = http_resume(data, httpreq);
   if(!result)
-    result = Curl_add_custom_headers(data, FALSE, httpversion, &req);
+    result = http_range(data, httpreq);
+  if(result)
+    goto out;
 
-  if(!result) {
-    /* req_send takes ownership of the 'req' memory on success */
-    result = http_req_complete(data, &req, httpversion, httpreq);
-    if(!result)
-      result = Curl_req_send(data, &req, httpversion);
+  httpversion = http_request_version(data);
+  /* Add request line and all headers to `req` */
+  for(hd_id = 0; hd_id <= H1_HD_LAST; ++hd_id) {
+    result = http_add_hd(data, &req, (http_hd_t)hd_id,
+                         httpversion, method, httpreq);
+    if(result)
+      goto out;
   }
-  curlx_dyn_free(&req);
-  if(result)
-    goto fail;
+
+  /* setup variables for the upcoming transfer and send */
+  Curl_xfer_setup_sendrecv(data, FIRSTSOCKET, -1);
+  result = Curl_req_send(data, &req, httpversion);
 
   if((httpversion >= 20) && data->req.upload_chunky)
     /* upload_chunky was set above to set up the request in a chunky fashion,
        but is disabled here again to avoid that the chunked encoded version is
        actually used when sending the request body over h2 */
     data->req.upload_chunky = FALSE;
-fail:
+
+out:
   if(CURLE_TOO_LARGE == result)
     failf(data, "HTTP request too large");
+
+  /* clear userpwd and proxyuserpwd to avoid reusing old credentials
+   * from reused connections */
+  Curl_safefree(data->state.aptr.userpwd);
+#ifndef CURL_DISABLE_PROXY
+  Curl_safefree(data->state.aptr.proxyuserpwd);
+#endif
+  curlx_dyn_free(&req);
   return result;
 }
 
index 50279aedfaad7b02221855783d2c9c061e24b84b..67ef17f5b953e0fea7d062989e9e737da3fec833 100644 (file)
@@ -106,7 +106,7 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, bool is_connect,
 CURLcode Curl_dynhds_add_custom(struct Curl_easy *data, bool is_connect,
                                 struct dynhds *hds);
 
-void Curl_http_method(struct Curl_easy *data, struct connectdata *conn,
+void Curl_http_method(struct Curl_easy *data,
                       const char **method, Curl_HttpReq *);
 
 /* protocol-specific functions set up to be called by the main engine */
index 7c95c2ff7e4be7c42015a1e2e7204f26922101a9..e2cdc9181f5f22ce59a6decb8387542e474c51c3 100644 (file)
@@ -1820,8 +1820,9 @@ CURLcode Curl_http2_request_upgrade(struct dynbuf *req,
     return result;
   }
 
+  data->state.http_hd_upgrade = TRUE;
+  data->state.http_hd_h2_settings = TRUE;
   result = curlx_dyn_addf(req,
-                          "Connection: Upgrade, HTTP2-Settings\r\n"
                           "Upgrade: %s\r\n"
                           "HTTP2-Settings: %s\r\n",
                           NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64);
index 850b7ae112510b7442b278bfcead318082be3bd3..c62db499ca19642e377c7ab560f220de417d7338 100644 (file)
@@ -775,7 +775,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data)
     }
   }
 
-  Curl_http_method(data, conn, &method, &httpreq);
+  Curl_http_method(data, &method, &httpreq);
 
   payload_hash =
     parse_content_sha_hdr(data, curlx_str(&provider1),
index df51484eb9770f9a0240ce79261c14087e6ec7db..2d742856ce81acc111b9ff8f404d107239d6e07c 100644 (file)
@@ -162,11 +162,6 @@ static CURLcode dynhds_add_custom(struct Curl_easy *data,
                  we will force length zero then */
               hd_name_eq(name, namelen, STRCONST("Content-Length:")))
         ;
-      else if(data->state.aptr.te &&
-              /* when asking for Transfer-Encoding, do not pass on a custom
-                 Connection: */
-              hd_name_eq(name, namelen, STRCONST("Connection:")))
-        ;
       else if((httpversion >= 20) &&
               hd_name_eq(name, namelen, STRCONST("Transfer-Encoding:")))
         /* HTTP/2 and HTTP/3 do not support chunked requests */
index 20161cac44a3b8e1641da4b73a591bdbe7e6de25..b52267cb67399d3bc280c98f1f6545b176b78486 100644 (file)
--- a/lib/url.c
+++ b/lib/url.c
@@ -320,7 +320,6 @@ CURLcode Curl_close(struct Curl_easy **datap)
   Curl_safefree(data->state.aptr.uagent);
   Curl_safefree(data->state.aptr.userpwd);
   Curl_safefree(data->state.aptr.accept_encoding);
-  Curl_safefree(data->state.aptr.te);
   Curl_safefree(data->state.aptr.rangeline);
   Curl_safefree(data->state.aptr.ref);
   Curl_safefree(data->state.aptr.host);
index a35512cf7a87625254c9448145f6676c1695b105..cf181af641fd0c391396315c0e4b7c5563748db5 100644 (file)
@@ -1122,7 +1122,6 @@ struct UrlState {
 #ifndef CURL_DISABLE_RTSP
     char *rtsp_transport;
 #endif
-    char *te; /* TE: request header */
 
     /* transfer credentials */
     char *user;
@@ -1178,6 +1177,11 @@ struct UrlState {
                     internal use and the user does not have ownership of the
                     handle. */
   BIT(http_ignorecustom); /* ignore custom method from now */
+#ifndef CURL_DISABLE_HTTP
+  BIT(http_hd_te); /* Added HTTP header TE: */
+  BIT(http_hd_upgrade); /* Added HTTP header Upgrade: */
+  BIT(http_hd_h2_settings); /* Added HTTP header H2Settings: */
+#endif
 };
 
 /*
index b6434b0b8060478cdf42f891fc7131af47ddf309..6070cd6d0d851d21f1f90fbca31819a8d60dafc9 100644 (file)
--- a/lib/ws.c
+++ b/lib/ws.c
@@ -1023,11 +1023,6 @@ CURLcode Curl_ws_request(struct Curl_easy *data, struct dynbuf *req)
          MUST include the "websocket" keyword. */
       "Upgrade", "websocket"
     },
-    {
-      /* The request MUST contain a |Connection| header field whose value
-         MUST include the "Upgrade" token. */
-      "Connection", "Upgrade",
-    },
     {
       /* The request MUST include a header field with the name
          |Sec-WebSocket-Version|. The value of this header field MUST be
@@ -1043,7 +1038,7 @@ CURLcode Curl_ws_request(struct Curl_easy *data, struct dynbuf *req)
       "Sec-WebSocket-Key", NULL,
     }
   };
-  heads[3].val = &keyval[0];
+  heads[2].val = &keyval[0];
 
   /* 16 bytes random */
   result = Curl_rand(data, (unsigned char *)rand, sizeof(rand));
@@ -1065,6 +1060,7 @@ CURLcode Curl_ws_request(struct Curl_easy *data, struct dynbuf *req)
                               heads[i].val);
     }
   }
+  data->state.http_hd_upgrade = TRUE;
   k->upgr101 = UPGR101_WS;
   return result;
 }
index 073c59acbf3bffd35511fb11f6484ddc70de0945..d0ce3ffaf043691cc42d65589d94f7c476965b67 100644 (file)
@@ -66,8 +66,8 @@ GET /%TESTNUMBER HTTP/1.1
 Host: %HOSTIP:%HTTPPORT\r
 User-Agent: curl/%VERSION\r
 Accept: */*\r
-Connection: TE\r
 TE: gzip\r
+Connection: TE\r
 \r
 </protocol>
 </verify>
index 08320aa599ee2cd5be06e73fbb04d0c6236fef51..a01aae69beb06117d743403f70a0ecddb27d0a7b 100644 (file)
@@ -176,8 +176,8 @@ GET /%TESTNUMBER HTTP/1.1
 Host: %HOSTIP:%HTTPPORT\r
 User-Agent: curl/%VERSION\r
 Accept: */*\r
-Connection: TE\r
 TE: gzip\r
+Connection: TE\r
 \r
 </protocol>
 </verify>
index ee969cda0a3cbbbba0c30c63fd479a61141db549..fdc172398ff89f4404aec0fbbb55ef6929dfffa5 100644 (file)
@@ -67,8 +67,8 @@ GET /%TESTNUMBER HTTP/1.1
 Host: %HOSTIP:%HTTPPORT\r
 User-Agent: curl/%VERSION\r
 Accept: */*\r
-Connection: TE\r
 TE: gzip\r
+Connection: TE\r
 \r
 </protocol>
 </verify>
index 248432f3ce7e37c76ffbd41de21d09443ca9a4d4..3b4d58b2e6058f4298ff214d94d6691acf429386 100644 (file)
@@ -66,8 +66,8 @@ GET /%TESTNUMBER HTTP/1.1
 Host: %HOSTIP:%HTTPPORT\r
 User-Agent: curl/%VERSION\r
 Accept: */*\r
-Connection: close, TE\r
 TE: gzip\r
+Connection: close, TE\r
 \r
 </protocol>
 </verify>
index f3ade56b7e6436f32d6c6a795645571caf7b597b..d6271714bd1161ece0a6e68d9041c09b84c2ae99 100644 (file)
@@ -66,8 +66,8 @@ GET /%TESTNUMBER HTTP/1.1
 Host: %HOSTIP:%HTTPPORT\r
 User-Agent: curl/%VERSION\r
 Accept: */*\r
-Connection: TE\r
 TE: gzip\r
+Connection: TE\r
 \r
 </protocol>
 </verify>
index 14d660176b5f08423872ec1773a4987c89b772a8..486aadb94b5d67d3ff3be63d2fedb296d86cfa1f 100644 (file)
@@ -66,8 +66,8 @@ GET /%TESTNUMBER HTTP/1.1
 Host: %HOSTIP:%HTTPPORT\r
 User-Agent: curl/%VERSION\r
 Accept: */*\r
-Connection: TE\r
 TE: gzip\r
+Connection: TE\r
 \r
 </protocol>
 </verify>
index 7b163a47e7341d07dc7924f7f3c7e989ab7ba51d..52a0f675a2e87ad03c890b573d472baa9c2345e8 100644 (file)
@@ -183,9 +183,9 @@ GET /%TESTNUMBER HTTP/1.1
 Host: %HOSTIP:%HTTPPORT\r
 User-Agent: curl/%VERSION\r
 Accept: */*\r
-Connection: TE\r
 TE: gzip\r
 Accept-Encoding: xxx\r
+Connection: TE\r
 \r
 </protocol>
 </verify>
index 0ec7093f712d9269e7ac9051642f16b75b2b81e5..b83b7ee95fa1a82e1b22e60eba9524fcc5acaa97 100644 (file)
@@ -52,8 +52,8 @@ GET /%TESTNUMBER HTTP/1.1
 Host: %HOSTIP:%HTTPPORT\r
 User-Agent: curl/%VERSION\r
 Accept: */*\r
-Connection: TE\r
 TE: gzip\r
+Connection: TE\r
 \r
 </protocol>
 <errorcode>
index 0401f796b417ead8aa8a93ad47a8bceecb81639d..324551a068d72bd418c0af742343a64d681595ff 100644 (file)
@@ -52,9 +52,9 @@ GET /%TESTNUMBER HTTP/1.1
 Host: %HOSTIP:%HTTPPORT\r
 User-Agent: curl/%VERSION\r
 Accept: */*\r
-Connection: Upgrade, HTTP2-Settings\r
 Upgrade: h2c\r
 HTTP2-Settings: AAMAAABkAAQAAQAAAAIAAAAA\r
+Connection: Upgrade, HTTP2-Settings\r
 \r
 </protocol>
 
index b1fd8021a6d982f98e71e8e81c4bf9f7942603f4..222437e8e1790d1db9b4080746abdfd57a041e37 100644 (file)
@@ -47,9 +47,9 @@ GET /%TESTNUMBER HTTP/1.1
 Host: %HOSTIP:%HTTPPORT\r
 User-Agent: curl/%VERSION\r
 Accept: */*\r
-Connection: Upgrade, HTTP2-Settings\r
 Upgrade: %H2CVER\r
 HTTP2-Settings: AAMAAABkAAQAAQAAAAIAAAAA\r
+Connection: Upgrade, HTTP2-Settings\r
 \r
 </protocol>
 </verify>
index 18a38e04d05e372a0f8dfca46c9878b5f589ad0f..8b2877468609f5d12e1485f8f3c96cd0cf42b083 100644 (file)
@@ -53,9 +53,9 @@ Host: %HOSTIP:%HTTPPORT
 User-Agent: curl/%VERSION\r
 Accept: */*\r
 Upgrade: websocket\r
-Connection: Upgrade\r
 Sec-WebSocket-Version: 13\r
 Sec-WebSocket-Key: NDMyMTUzMjE2MzIxNzMyMQ==\r
+Connection: Upgrade\r
 \r
 </protocol>
 <errorcode>
index e3cab99f15f7e356cde867c759d71550294fa8d0..1b968c7672850e76b815d333377d55eaca43c8f3 100644 (file)
@@ -58,9 +58,9 @@ Host: %HOSTIP:%HTTPPORT
 User-Agent: webbie-sox/3\r
 Accept: */*\r
 Upgrade: websocket\r
-Connection: Upgrade\r
 Sec-WebSocket-Version: 13\r
 Sec-WebSocket-Key: NDMyMTUzMjE2MzIxNzMyMQ==\r
+Connection: Upgrade\r
 \r
 %hex[%8a%00]hex%
 </protocol>
index b1d11fcf6e88a91690f2676ac8cf818c2449565b..8e2d34877f5f6cf8716bcd2562431629c437d77e 100644 (file)
@@ -59,9 +59,9 @@ Host: %HOSTIP:%HTTPPORT
 User-Agent: webbie-sox/3\r
 Accept: */*\r
 Upgrade: websocket\r
-Connection: Upgrade\r
 Sec-WebSocket-Version: 13\r
 Sec-WebSocket-Key: NDMyMTUzMjE2MzIxNzMyMQ==\r
+Connection: Upgrade\r
 \r
 %hex[%8a%808321]hex%
 </protocol>
index d90d23dc9ed5c4146f16439dafc5f9464db963e7..8ad45cbecaf46ef3ad86c75a49c147d30bd0b4cf 100644 (file)
@@ -49,9 +49,9 @@ Host: %HOSTIP:%HTTPPORT
 User-Agent: webbie-sox/3\r
 Accept: */*\r
 Upgrade: websocket\r
-Connection: Upgrade\r
 Sec-WebSocket-Version: 13\r
 Sec-WebSocket-Key: NDMyMTUzMjE2MzIxNzMyMQ==\r
+Connection: Upgrade\r
 \r
 </protocol>
 # 22 == CURLE_HTTP_RETURNED_ERROR
index 21fac7a0f03e4cd3a1403d41f15c5c8d122aef9c..8ed75d96a9cfada33797aeeb8780ecf6f85afbca 100644 (file)
@@ -58,9 +58,9 @@ Host: %HOSTIP:%HTTPPORT
 User-Agent: websocket/%TESTNUMBER
 Accept: */*
 Upgrade: websocket
-Connection: Upgrade
 Sec-WebSocket-Version: 13
 Sec-WebSocket-Key: NDMyMTUzMjE2MzIxNzMyMQ==
+Connection: Upgrade
 
 </protocol>
 
index d0e5c95ed4b0c3e950a18e22d1cfd9de0459be7b..a6d2ab03fcab15d0773ac903f28b8640a0cc171c 100644 (file)
@@ -43,8 +43,8 @@ GET /%TESTNUMBER HTTP/1.1
 Host: %HOSTIP:%HTTPPORT\r
 User-Agent: curl/%VERSION\r
 Accept: */*\r
-Connection: TE\r
 TE: gzip\r
+Connection: TE\r
 \r
 </protocol>
 
index 6031b867b889895300813792c415cbd8214a3c27..fa502f1b562731cabd17636137ec5e8326036250 100644 (file)
@@ -51,8 +51,8 @@ GET /%TESTNUMBER HTTP/1.1
 Host: %HOSTIP:%HTTPPORT
 User-Agent: curl/%VERSION
 Accept: */*
-Connection: TE
 TE: gzip
+Connection: TE
 
 </protocol>