]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
lib: add Curl_xfer_write_resp_hd
authorStefan Eissing <stefan@eissing.org>
Thu, 21 Mar 2024 11:15:59 +0000 (12:15 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Thu, 11 Apr 2024 07:29:21 +0000 (09:29 +0200)
Add method in protocol handlers to allow writing of a single,
0-terminated header line. Avoids parsing and copying these lines.

Closes #13165

30 files changed:
lib/c-hyper.c
lib/curl_rtmp.c
lib/dict.c
lib/file.c
lib/ftp.c
lib/gopher.c
lib/http.c
lib/http.h
lib/http2.c
lib/imap.c
lib/ldap.c
lib/mqtt.c
lib/openldap.c
lib/pop3.c
lib/pop3.h
lib/request.c
lib/rtsp.c
lib/rtsp.h
lib/smb.c
lib/smtp.c
lib/telnet.c
lib/tftp.c
lib/transfer.c
lib/transfer.h
lib/urldata.h
lib/vquic/curl_ngtcp2.c
lib/vssh/libssh.c
lib/vssh/libssh2.c
lib/vssh/wolfssh.c
lib/ws.c

index 88674ee0a1ef526755c6752b67842a7ad61d60bb..0593d97065862819c23329b3b4776f560a4c2604 100644 (file)
@@ -171,7 +171,7 @@ static int hyper_each_header(void *userdata,
   len = Curl_dyn_len(&data->state.headerb);
   headp = Curl_dyn_ptr(&data->state.headerb);
 
-  result = Curl_http_header(data, data->conn, headp, len);
+  result = Curl_http_header(data, headp, len);
   if(result) {
     data->state.hresult = result;
     return HYPER_ITER_BREAK;
index b2f2adad8b192a6d08ddaa4b17ce2957e2344dc5..c1fd981b786980023187923385499221702a85ad 100644 (file)
@@ -80,6 +80,7 @@ const struct Curl_handler Curl_handler_rtmp = {
   ZERO_NULL,                            /* perform_getsock */
   rtmp_disconnect,                      /* disconnect */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_RTMP,                            /* defport */
@@ -103,6 +104,7 @@ const struct Curl_handler Curl_handler_rtmpt = {
   ZERO_NULL,                            /* perform_getsock */
   rtmp_disconnect,                      /* disconnect */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_RTMPT,                           /* defport */
@@ -126,6 +128,7 @@ const struct Curl_handler Curl_handler_rtmpe = {
   ZERO_NULL,                            /* perform_getsock */
   rtmp_disconnect,                      /* disconnect */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_RTMP,                            /* defport */
@@ -149,6 +152,7 @@ const struct Curl_handler Curl_handler_rtmpte = {
   ZERO_NULL,                            /* perform_getsock */
   rtmp_disconnect,                      /* disconnect */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_RTMPT,                           /* defport */
@@ -172,6 +176,7 @@ const struct Curl_handler Curl_handler_rtmps = {
   ZERO_NULL,                            /* perform_getsock */
   rtmp_disconnect,                      /* disconnect */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_RTMPS,                           /* defport */
@@ -195,6 +200,7 @@ const struct Curl_handler Curl_handler_rtmpts = {
   ZERO_NULL,                            /* perform_getsock */
   rtmp_disconnect,                      /* disconnect */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_RTMPS,                           /* defport */
index f37767882e7c61e6c404cb2e17dd6b4b2bed86fd..5404671c646d18c45b25a82ec523354e384ed076 100644 (file)
@@ -90,6 +90,7 @@ const struct Curl_handler Curl_handler_dict = {
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* disconnect */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_DICT,                            /* defport */
index bee9e92ecaa50e77b2f64dba91b75d1af2e5a74f..c436aaaad47df9619db72c35d73cb0349f2c4d7e 100644 (file)
@@ -115,6 +115,7 @@ const struct Curl_handler Curl_handler_file = {
   ZERO_NULL,                            /* perform_getsock */
   file_disconnect,                      /* disconnect */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   0,                                    /* defport */
index 5bbdeb054040d8542eaa408696bb1a036c9ed383..760b54ff3bc69a558c171490870ed193a08a8da0 100644 (file)
--- a/lib/ftp.c
+++ b/lib/ftp.c
@@ -177,6 +177,7 @@ const struct Curl_handler Curl_handler_ftp = {
   ZERO_NULL,                       /* perform_getsock */
   ftp_disconnect,                  /* disconnect */
   ZERO_NULL,                       /* write_resp */
+  ZERO_NULL,                       /* write_resp_hd */
   ZERO_NULL,                       /* connection_check */
   ZERO_NULL,                       /* attach connection */
   PORT_FTP,                        /* defport */
@@ -208,6 +209,7 @@ const struct Curl_handler Curl_handler_ftps = {
   ZERO_NULL,                       /* perform_getsock */
   ftp_disconnect,                  /* disconnect */
   ZERO_NULL,                       /* write_resp */
+  ZERO_NULL,                       /* write_resp_hd */
   ZERO_NULL,                       /* connection_check */
   ZERO_NULL,                       /* attach connection */
   PORT_FTPS,                       /* defport */
index e1a1ba64886288e26fa46f0b8f405530797b742e..7ba070a769b3444d2dcb89f59d267842aa43248c 100644 (file)
@@ -76,6 +76,7 @@ const struct Curl_handler Curl_handler_gopher = {
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* disconnect */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_GOPHER,                          /* defport */
@@ -100,6 +101,7 @@ const struct Curl_handler Curl_handler_gophers = {
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* disconnect */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_GOPHER,                          /* defport */
index 7cb8b63f8c17e98520c67f4d3537ac8b28d582d0..dea166c3dddd409f84384999849d795463519c24 100644 (file)
  * Forward declarations.
  */
 
-static bool http_should_fail(struct Curl_easy *data);
+static bool http_should_fail(struct Curl_easy *data, int httpcode);
 static bool http_exp100_is_waiting(struct Curl_easy *data);
 static CURLcode http_exp100_add_reader(struct Curl_easy *data);
 static void http_exp100_send_anyway(struct Curl_easy *data);
@@ -123,6 +123,7 @@ const struct Curl_handler Curl_handler_http = {
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* disconnect */
   Curl_http_write_resp,                 /* write_resp */
+  Curl_http_write_resp_hd,              /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_HTTP,                            /* defport */
@@ -151,6 +152,7 @@ const struct Curl_handler Curl_handler_https = {
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* disconnect */
   Curl_http_write_resp,                 /* write_resp */
+  Curl_http_write_resp_hd,              /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_HTTPS,                           /* defport */
@@ -243,8 +245,6 @@ char *Curl_copy_header_value(const char *header)
   while(*start && ISSPACE(*start))
     start++;
 
-  /* data is in the host encoding so
-     use '\r' and '\n' instead of 0x0d and 0x0a */
   end = strchr(start, '\r');
   if(!end)
     end = strchr(start, '\n');
@@ -565,7 +565,7 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data)
       data->state.authhost.done = TRUE;
     }
   }
-  if(http_should_fail(data)) {
+  if(http_should_fail(data, data->req.httpcode)) {
     failf(data, "The requested URL returned error: %d",
           data->req.httpcode);
     result = CURLE_HTTP_RETURNED_ERROR;
@@ -1006,21 +1006,18 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
 }
 
 /**
- * http_should_fail() determines whether an HTTP response has gotten us
+ * http_should_fail() determines whether an HTTP response code has gotten us
  * into an error state or not.
  *
  * @retval FALSE communications should continue
  *
  * @retval TRUE communications should not continue
  */
-static bool http_should_fail(struct Curl_easy *data)
+static bool http_should_fail(struct Curl_easy *data, int httpcode)
 {
-  int httpcode;
   DEBUGASSERT(data);
   DEBUGASSERT(data->conn);
 
-  httpcode = data->req.httpcode;
-
   /*
   ** If we haven't been asked to fail on error,
   ** don't fail.
@@ -2836,9 +2833,10 @@ checkprotoprefix(struct Curl_easy *data, struct connectdata *conn,
 /*
  * Curl_http_header() parses a single response header.
  */
-CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn,
-                          char *hd, size_t hdlen)
+CURLcode Curl_http_header(struct Curl_easy *data,
+                          const char *hd, size_t hdlen)
 {
+  struct connectdata *conn = data->conn;
   CURLcode result;
   struct SingleRequest *k = &data->req;
   const char *v;
@@ -2848,7 +2846,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn,
   case 'A':
 #ifndef CURL_DISABLE_ALTSVC
     v = (data->asi &&
-         ((conn->handler->flags & PROTOPT_SSL) ||
+         ((data->conn->handler->flags & PROTOPT_SSL) ||
 #ifdef CURLDEBUG
           /* allow debug builds to circumvent the HTTPS restriction */
           getenv("CURL_ALTSVC_HTTP")
@@ -3306,12 +3304,11 @@ CURLcode Curl_http_size(struct Curl_easy *data)
   return CURLE_OK;
 }
 
-static CURLcode verify_header(struct Curl_easy *data)
+static CURLcode verify_header(struct Curl_easy *data,
+                              const char *hd, size_t hdlen)
 {
   struct SingleRequest *k = &data->req;
-  const char *header = Curl_dyn_ptr(&data->state.headerb);
-  size_t hlen = Curl_dyn_len(&data->state.headerb);
-  char *ptr = memchr(header, 0x00, hlen);
+  char *ptr = memchr(hd, 0x00, hdlen);
   if(ptr) {
     /* this is bad, bail out */
     failf(data, "Nul byte in header");
@@ -3320,11 +3317,11 @@ static CURLcode verify_header(struct Curl_easy *data)
   if(k->headerline < 2)
     /* the first "header" is the status-line and it has no colon */
     return CURLE_OK;
-  if(((header[0] == ' ') || (header[0] == '\t')) && k->headerline > 2)
+  if(((hd[0] == ' ') || (hd[0] == '\t')) && k->headerline > 2)
     /* line folding, can't happen on line 2 */
     ;
   else {
-    ptr = memchr(header, ':', hlen);
+    ptr = memchr(hd, ':', hdlen);
     if(!ptr) {
       /* this is bad, bail out */
       failf(data, "Header without colon");
@@ -3369,7 +3366,6 @@ static CURLcode http_on_response(struct Curl_easy *data,
   struct connectdata *conn = data->conn;
   CURLcode result = CURLE_OK;
   struct SingleRequest *k = &data->req;
-  bool switch_to_h2 = FALSE;
 
   (void)buf; /* not used without HTTP2 enabled */
   *pconsumed = 0;
@@ -3388,96 +3384,92 @@ static CURLcode http_on_response(struct Curl_easy *data,
     return CURLE_UNSUPPORTED_PROTOCOL;
   }
   else if(k->httpcode < 200) {
-    /* "A user agent MAY ignore unexpected 1xx status responses." */
+    /* "A user agent MAY ignore unexpected 1xx status responses."
+     * By default, we expect to get more responses after this one. */
+    k->header = TRUE;
+    k->headerline = 0; /* restart the header line counter */
+
     switch(k->httpcode) {
     case 100:
       /*
        * We have made an HTTP PUT or POST and this is 1.1-lingo
        * that tells us that the server is OK with this and ready
        * to receive the data.
-       * However, we'll get more headers now so we must get
-       * back into the header-parsing state!
        */
-      k->header = TRUE;
-      k->headerline = 0; /* restart the header line counter */
-
-      /* if we did wait for this do enable write now! */
       Curl_http_exp100_got100(data);
       break;
     case 101:
-      if(conn->httpversion == 11) {
-        /* Switching Protocols only allowed from HTTP/1.1 */
-        if(k->upgr101 == UPGR101_H2) {
-          /* Switching to HTTP/2 */
-          infof(data, "Received 101, Switching to HTTP/2");
-          k->upgr101 = UPGR101_RECEIVED;
-
-          /* we'll get more headers (HTTP/2 response) */
-          k->header = TRUE;
-          k->headerline = 0; /* restart the header line counter */
-          switch_to_h2 = TRUE;
-        }
-#ifdef USE_WEBSOCKETS
-        else if(k->upgr101 == UPGR101_WS) {
-          /* verify the response */
-          result = Curl_ws_accept(data, buf, blen);
-          if(result)
-            return result;
-          k->header = FALSE; /* no more header to parse! */
-          *pconsumed += blen; /* ws accept handled the data */
-          blen = 0;
-          if(data->set.connect_only)
-            k->keepon &= ~KEEP_RECV; /* read no more content */
-        }
-#endif
-        else {
-          /* Not switching to another protocol */
-          k->header = FALSE; /* no more header to parse! */
-        }
-      }
-      else {
+      /* Switching Protocols only allowed from HTTP/1.1 */
+      if(conn->httpversion != 11) {
         /* invalid for other HTTP versions */
         failf(data, "unexpected 101 response code");
         return CURLE_WEIRD_SERVER_REPLY;
       }
+      if(k->upgr101 == UPGR101_H2) {
+        /* Switching to HTTP/2, where we will get more responses */
+        infof(data, "Received 101, Switching to HTTP/2");
+        k->upgr101 = UPGR101_RECEIVED;
+        /* We expect more response from HTTP/2 later */
+        k->header = TRUE;
+        k->headerline = 0; /* restart the header line counter */
+        /* Any remaining `buf` bytes are already HTTP/2 and passed to
+         * be processed. */
+        result = Curl_http2_upgrade(data, conn, FIRSTSOCKET, buf, blen);
+        if(result)
+          return result;
+        *pconsumed += blen;
+      }
+#ifdef USE_WEBSOCKETS
+      else if(k->upgr101 == UPGR101_WS) {
+        /* verify the response. Any passed `buf` bytes are already in
+         * WebSockets format and taken in by the protocol handler. */
+        result = Curl_ws_accept(data, buf, blen);
+        if(result)
+          return result;
+        *pconsumed += blen; /* ws accept handled the data */
+        k->header = FALSE; /* we will not get more responses */
+        if(data->set.connect_only)
+          k->keepon &= ~KEEP_RECV; /* read no more content */
+      }
+#endif
+      else {
+        /* We silently accept this as the final response.
+         * TODO: this looks, uhm, wrong. What are we switching to if we
+         * did not ask for an Upgrade? Maybe the application provided an
+         * `Upgrade: xxx` header? */
+        k->header = FALSE;
+      }
       break;
     default:
-      /* the status code 1xx indicates a provisional response, so
-         we'll get another set of headers */
-      k->header = TRUE;
-      k->headerline = 0; /* restart the header line counter */
+      /* The server may send us other 1xx responses, like informative
+       * 103. This have no influence on request processing and we expect
+       * to receive a final response eventually. */
       break;
     }
+    return result;
   }
-  else {
-    /* k->httpcode >= 200, final response */
-    k->header = FALSE;
 
-    if(k->upgr101 == UPGR101_H2) {
-      /* A requested upgrade was denied, poke the multi handle to possibly
-         allow a pending pipewait to continue */
-      Curl_multi_connchanged(data->multi);
-    }
+  /* k->httpcode >= 200, final response */
+  k->header = FALSE;
 
-    if((k->size == -1) && !k->chunk && !conn->bits.close &&
-       (conn->httpversion == 11) &&
-       !(conn->handler->protocol & CURLPROTO_RTSP) &&
-       data->state.httpreq != HTTPREQ_HEAD) {
-      /* On HTTP 1.1, when connection is not to get closed, but no
-         Content-Length nor Transfer-Encoding chunked have been
-         received, according to RFC2616 section 4.4 point 5, we
-         assume that the server will close the connection to
-         signal the end of the document. */
-      infof(data, "no chunk, no close, no size. Assume close to "
-            "signal end");
-      streamclose(conn, "HTTP: No end-of-message indicator");
-    }
+  if(k->upgr101 == UPGR101_H2) {
+    /* A requested upgrade was denied, poke the multi handle to possibly
+       allow a pending pipewait to continue */
+    Curl_multi_connchanged(data->multi);
   }
 
-  if(!k->header) {
-    result = Curl_http_size(data);
-    if(result)
-      return result;
+  if((k->size == -1) && !k->chunk && !conn->bits.close &&
+     (conn->httpversion == 11) &&
+     !(conn->handler->protocol & CURLPROTO_RTSP) &&
+     data->state.httpreq != HTTPREQ_HEAD) {
+    /* On HTTP 1.1, when connection is not to get closed, but no
+       Content-Length nor Transfer-Encoding chunked have been
+       received, according to RFC2616 section 4.4 point 5, we
+       assume that the server will close the connection to
+       signal the end of the document. */
+    infof(data, "no chunk, no close, no size. Assume close to "
+          "signal end");
+    streamclose(conn, "HTTP: No end-of-message indicator");
   }
 
   /* At this point we have some idea about the fate of the connection.
@@ -3511,31 +3503,25 @@ static CURLcode http_on_response(struct Curl_easy *data,
   }
 #endif
 
-  /*
-   * When all the headers have been parsed, see if we should give
-   * up and return an error.
-   */
-  if(http_should_fail(data)) {
-    failf(data, "The requested URL returned error: %d",
-          k->httpcode);
-    return CURLE_HTTP_RETURNED_ERROR;
-  }
-
 #ifdef USE_WEBSOCKETS
-  /* All non-101 HTTP status codes are bad when wanting to upgrade to
-     websockets */
+  /* All >=200 HTTP status codes are errors when wanting websockets */
   if(data->req.upgr101 == UPGR101_WS) {
     failf(data, "Refused WebSockets upgrade: %d", k->httpcode);
     return CURLE_HTTP_RETURNED_ERROR;
   }
 #endif
 
+  /* Check if this response means the transfer errored. */
+  if(http_should_fail(data, data->req.httpcode)) {
+    failf(data, "The requested URL returned error: %d",
+          k->httpcode);
+    return CURLE_HTTP_RETURNED_ERROR;
+  }
 
   /* Curl_http_auth_act() checks what authentication methods
    * that are available and decides which one (if any) to
    * use. It will set 'newurl' if an auth method was picked. */
   result = Curl_http_auth_act(data);
-
   if(result)
     return result;
 
@@ -3606,65 +3592,244 @@ static CURLcode http_on_response(struct Curl_easy *data,
       infof(data, "Keep sending data to get tossed away");
       k->keepon |= KEEP_SEND;
     }
+
   }
 
-  if(!k->header) {
-    /*
-     * really end-of-headers.
-     *
-     * If we requested a "no body", this is a good time to get
-     * out and return home.
+  /* This is the last response that we will got for the current request.
+   * Check on the body size and determine if the response is complete.
+   */
+  result = Curl_http_size(data);
+  if(result)
+    return result;
+
+  /* If we requested a "no body", this is a good time to get
+   * out and return home.
+   */
+  if(data->req.no_body)
+    k->download_done = TRUE;
+
+  /* If max download size is *zero* (nothing) we already have
+     nothing and can safely return ok now!  But for HTTP/2, we'd
+     like to call http2_handle_stream_close to properly close a
+     stream.  In order to do this, we keep reading until we
+     close the stream. */
+  if(0 == k->maxdownload
+     && !Curl_conn_is_http2(data, conn, FIRSTSOCKET)
+     && !Curl_conn_is_http3(data, conn, FIRSTSOCKET))
+    k->download_done = TRUE;
+
+  /* final response without error, prepare to receive the body */
+  return Curl_http_firstwrite(data);
+}
+
+static CURLcode http_rw_hd(struct Curl_easy *data,
+                           const char *hd, size_t hdlen,
+                           const char *buf_remain, size_t blen,
+                           size_t *pconsumed)
+{
+  CURLcode result = CURLE_OK;
+  struct SingleRequest *k = &data->req;
+  int writetype;
+
+  *pconsumed = 0;
+  if((0x0a == *hd) || (0x0d == *hd)) {
+    /* Empty header line means end of headers! */
+    size_t consumed;
+
+    /* now, only output this if the header AND body are requested:
      */
-    if(data->req.no_body)
-      k->download_done = TRUE;
-
-    /* If max download size is *zero* (nothing) we already have
-       nothing and can safely return ok now!  But for HTTP/2, we'd
-       like to call http2_handle_stream_close to properly close a
-       stream.  In order to do this, we keep reading until we
-       close the stream. */
-    if(0 == k->maxdownload
-       && !Curl_conn_is_http2(data, conn, FIRSTSOCKET)
-       && !Curl_conn_is_http3(data, conn, FIRSTSOCKET))
-      k->download_done = TRUE;
-  }
-
-  if(switch_to_h2) {
-    /* Having handled the headers, we can do the HTTP/2 switch.
-     * Any remaining `buf` bytes are already HTTP/2 and passed to
-     * be processed. */
-    result = Curl_http2_upgrade(data, conn, FIRSTSOCKET, buf, blen);
+    Curl_debug(data, CURLINFO_HEADER_IN, (char *)hd, hdlen);
+
+    writetype = CLIENTWRITE_HEADER |
+      ((k->httpcode/100 == 1) ? CLIENTWRITE_1XX : 0);
+
+    result = Curl_client_write(data, writetype, hd, hdlen);
+    if(result)
+      return result;
+
+    result = Curl_bump_headersize(data, hdlen, FALSE);
+    if(result)
+      return result;
+
+    data->req.deductheadercount =
+      (100 <= k->httpcode && 199 >= k->httpcode)?data->req.headerbytecount:0;
+
+    /* analyze the response to find out what to do. */
+    /* Caveat: we clear anything in the header brigade, because a
+     * response might switch HTTP version which may call use recursively.
+     * Not nice, but that is currently the way of things. */
+    Curl_dyn_reset(&data->state.headerb);
+    result = http_on_response(data, buf_remain, blen, &consumed);
     if(result)
       return result;
-    *pconsumed += blen;
+    *pconsumed += consumed;
+    return CURLE_OK;
   }
 
+  /*
+   * Checks for special headers coming up.
+   */
+
+  writetype = CLIENTWRITE_HEADER;
+  if(!k->headerline++) {
+    /* This is the first header, it MUST be the error code line
+       or else we consider this to be the body right away! */
+    bool fine_statusline = FALSE;
+
+    k->httpversion = 0; /* Don't know yet */
+    if(data->conn->handler->protocol & PROTO_FAMILY_HTTP) {
+      /*
+       * https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2
+       *
+       * The response code is always a three-digit number in HTTP as the spec
+       * says. We allow any three-digit number here, but we cannot make
+       * guarantees on future behaviors since it isn't within the protocol.
+       */
+      const char *p = hd;
+
+      while(*p && ISBLANK(*p))
+        p++;
+      if(!strncmp(p, "HTTP/", 5)) {
+        p += 5;
+        switch(*p) {
+        case '1':
+          p++;
+          if((p[0] == '.') && (p[1] == '0' || p[1] == '1')) {
+            if(ISBLANK(p[2])) {
+              k->httpversion = 10 + (p[1] - '0');
+              p += 3;
+              if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) {
+                k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 +
+                  (p[2] - '0');
+                p += 3;
+                if(ISSPACE(*p))
+                  fine_statusline = TRUE;
+              }
+            }
+          }
+          if(!fine_statusline) {
+            failf(data, "Unsupported HTTP/1 subversion in response");
+            return CURLE_UNSUPPORTED_PROTOCOL;
+          }
+          break;
+        case '2':
+        case '3':
+          if(!ISBLANK(p[1]))
+            break;
+          k->httpversion = (*p - '0') * 10;
+          p += 2;
+          if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) {
+            k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 +
+              (p[2] - '0');
+            p += 3;
+            if(!ISSPACE(*p))
+              break;
+            fine_statusline = TRUE;
+          }
+          break;
+        default: /* unsupported */
+          failf(data, "Unsupported HTTP version in response");
+          return CURLE_UNSUPPORTED_PROTOCOL;
+        }
+      }
+
+      if(!fine_statusline) {
+        /* If user has set option HTTP200ALIASES,
+           compare header line against list of aliases
+        */
+        statusline check = checkhttpprefix(data, hd, hdlen);
+        if(check == STATUS_DONE) {
+          fine_statusline = TRUE;
+          k->httpcode = 200;
+          k->httpversion = 10;
+        }
+      }
+    }
+    else if(data->conn->handler->protocol & CURLPROTO_RTSP) {
+      const char *p = hd;
+      while(*p && ISBLANK(*p))
+        p++;
+      if(!strncmp(p, "RTSP/", 5)) {
+        p += 5;
+        if(ISDIGIT(*p)) {
+          p++;
+          if((p[0] == '.') && ISDIGIT(p[1])) {
+            if(ISBLANK(p[2])) {
+              p += 3;
+              if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) {
+                k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 +
+                  (p[2] - '0');
+                p += 3;
+                if(ISSPACE(*p)) {
+                  fine_statusline = TRUE;
+                  k->httpversion = 11; /* RTSP acts like HTTP 1.1 */
+                }
+              }
+            }
+          }
+        }
+        if(!fine_statusline)
+          return CURLE_WEIRD_SERVER_REPLY;
+      }
+    }
+
+    if(fine_statusline) {
+      result = Curl_http_statusline(data, data->conn);
+      if(result)
+        return result;
+      writetype |= CLIENTWRITE_STATUS;
+    }
+    else {
+      k->header = FALSE;   /* this is not a header line */
+      return CURLE_WEIRD_SERVER_REPLY;
+    }
+  }
+
+  result = verify_header(data, hd, hdlen);
+  if(result)
+    return result;
+
+  result = Curl_http_header(data, hd, hdlen);
+  if(result)
+    return result;
+
+  /*
+   * Taken in one (more) header. Write it to the client.
+   */
+  Curl_debug(data, CURLINFO_HEADER_IN, (char *)hd, hdlen);
+
+  if(k->httpcode/100 == 1)
+    writetype |= CLIENTWRITE_1XX;
+  result = Curl_client_write(data, writetype, hd, hdlen);
+  if(result)
+    return result;
+
+  result = Curl_bump_headersize(data, hdlen, FALSE);
+  if(result)
+    return result;
+
   return CURLE_OK;
 }
+
 /*
  * Read any HTTP header lines from the server and pass them to the client app.
  */
-static CURLcode http_rw_headers(struct Curl_easy *data,
-                                const char *buf, size_t blen,
-                                size_t *pconsumed)
+static CURLcode http_parse_headers(struct Curl_easy *data,
+                                   const char *buf, size_t blen,
+                                   size_t *pconsumed)
 {
   struct connectdata *conn = data->conn;
   CURLcode result = CURLE_OK;
   struct SingleRequest *k = &data->req;
-  char *hd;
-  size_t hdlen;
   char *end_ptr;
   bool leftover_body = FALSE;
 
   /* header line within buffer loop */
   *pconsumed = 0;
-  do {
-    size_t line_length;
-    int writetype;
-
-    /* data is in network encoding so use 0x0a instead of '\n' */
-    end_ptr = memchr(buf, 0x0a, blen);
+  while(blen && k->header) {
+    size_t consumed;
 
+    end_ptr = memchr(buf, '\n', blen);
     if(!end_ptr) {
       /* Not a complete header line within buffer, append the data to
          the end of the headerbuff. */
@@ -3700,14 +3865,13 @@ static CURLcode http_rw_headers(struct Curl_easy *data,
     }
 
     /* decrease the size of the remaining (supposed) header line */
-    line_length = (end_ptr - buf) + 1;
-    result = Curl_dyn_addn(&data->state.headerb, buf, line_length);
+    consumed = (end_ptr - buf) + 1;
+    result = Curl_dyn_addn(&data->state.headerb, buf, consumed);
     if(result)
       return result;
-
-    blen -= line_length;
-    buf += line_length;
-    *pconsumed += line_length;
+    blen -= consumed;
+    buf += consumed;
+    *pconsumed += consumed;
 
     /****
      * We now have a FULL header line in 'headerb'.
@@ -3735,195 +3899,21 @@ static CURLcode http_rw_headers(struct Curl_easy *data,
       }
     }
 
-    /* headers are in network encoding so use 0x0a and 0x0d instead of '\n'
-       and '\r' */
-    hd = Curl_dyn_ptr(&data->state.headerb);
-    hdlen = Curl_dyn_len(&data->state.headerb);
-    if((0x0a == *hd) || (0x0d == *hd)) {
-      /* Empty header line means end of headers! */
-      size_t consumed;
-
-      /* now, only output this if the header AND body are requested:
-       */
-      Curl_debug(data, CURLINFO_HEADER_IN, hd, hdlen);
-
-      writetype = CLIENTWRITE_HEADER |
-        ((k->httpcode/100 == 1) ? CLIENTWRITE_1XX : 0);
-
-      result = Curl_client_write(data, writetype, hd, hdlen);
-      if(result)
-        return result;
-
-      result = Curl_bump_headersize(data, hdlen, FALSE);
-      if(result)
-        return result;
-      /* We are done with this line. We reset because response
-       * processing might switch to HTTP/2 and that might call us
-       * directly again. */
-      Curl_dyn_reset(&data->state.headerb);
-
-      data->req.deductheadercount =
-        (100 <= k->httpcode && 199 >= k->httpcode)?data->req.headerbytecount:0;
-
-      /* analyze the response to find out what to do */
-      result = http_on_response(data, buf, blen, &consumed);
-      if(result)
-        return result;
-      *pconsumed += consumed;
+    result = http_rw_hd(data, Curl_dyn_ptr(&data->state.headerb),
+                        Curl_dyn_len(&data->state.headerb),
+                        buf, blen, &consumed);
+    /* We are done with this line. We reset because response
+     * processing might switch to HTTP/2 and that might call us
+     * directly again. */
+    Curl_dyn_reset(&data->state.headerb);
+    if(consumed) {
       blen -= consumed;
       buf += consumed;
-
-      if(!k->header || !blen)
-        goto out; /* exit header line loop */
-
-      continue;
-    }
-
-    /*
-     * Checks for special headers coming up.
-     */
-
-    writetype = CLIENTWRITE_HEADER;
-    if(!k->headerline++) {
-      /* This is the first header, it MUST be the error code line
-         or else we consider this to be the body right away! */
-      bool fine_statusline = FALSE;
-
-      k->httpversion = 0; /* Don't know yet */
-      if(conn->handler->protocol & PROTO_FAMILY_HTTP) {
-        /*
-         * https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2
-         *
-         * The response code is always a three-digit number in HTTP as the spec
-         * says. We allow any three-digit number here, but we cannot make
-         * guarantees on future behaviors since it isn't within the protocol.
-         */
-        char *p = hd;
-
-        while(*p && ISBLANK(*p))
-          p++;
-        if(!strncmp(p, "HTTP/", 5)) {
-          p += 5;
-          switch(*p) {
-          case '1':
-            p++;
-            if((p[0] == '.') && (p[1] == '0' || p[1] == '1')) {
-              if(ISBLANK(p[2])) {
-                k->httpversion = 10 + (p[1] - '0');
-                p += 3;
-                if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) {
-                  k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 +
-                    (p[2] - '0');
-                  p += 3;
-                  if(ISSPACE(*p))
-                    fine_statusline = TRUE;
-                }
-              }
-            }
-            if(!fine_statusline) {
-              failf(data, "Unsupported HTTP/1 subversion in response");
-              return CURLE_UNSUPPORTED_PROTOCOL;
-            }
-            break;
-          case '2':
-          case '3':
-            if(!ISBLANK(p[1]))
-              break;
-            k->httpversion = (*p - '0') * 10;
-            p += 2;
-            if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) {
-              k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 +
-                (p[2] - '0');
-              p += 3;
-              if(!ISSPACE(*p))
-                break;
-              fine_statusline = TRUE;
-            }
-            break;
-          default: /* unsupported */
-            failf(data, "Unsupported HTTP version in response");
-            return CURLE_UNSUPPORTED_PROTOCOL;
-          }
-        }
-
-        if(!fine_statusline) {
-          /* If user has set option HTTP200ALIASES,
-             compare header line against list of aliases
-          */
-          statusline check = checkhttpprefix(data, hd, hdlen);
-          if(check == STATUS_DONE) {
-            fine_statusline = TRUE;
-            k->httpcode = 200;
-            k->httpversion = 10;
-          }
-        }
-      }
-      else if(conn->handler->protocol & CURLPROTO_RTSP) {
-        char *p = hd;
-        while(*p && ISBLANK(*p))
-          p++;
-        if(!strncmp(p, "RTSP/", 5)) {
-          p += 5;
-          if(ISDIGIT(*p)) {
-            p++;
-            if((p[0] == '.') && ISDIGIT(p[1])) {
-              if(ISBLANK(p[2])) {
-                p += 3;
-                if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) {
-                  k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 +
-                    (p[2] - '0');
-                  p += 3;
-                  if(ISSPACE(*p)) {
-                    fine_statusline = TRUE;
-                    k->httpversion = 11; /* RTSP acts like HTTP 1.1 */
-                  }
-                }
-              }
-            }
-          }
-          if(!fine_statusline)
-            return CURLE_WEIRD_SERVER_REPLY;
-        }
-      }
-
-      if(fine_statusline) {
-        result = Curl_http_statusline(data, conn);
-        if(result)
-          return result;
-        writetype |= CLIENTWRITE_STATUS;
-      }
-      else {
-        k->header = FALSE;   /* this is not a header line */
-        break;
-      }
+      *pconsumed += consumed;
     }
-
-    result = verify_header(data);
-    if(result)
-      return result;
-
-    result = Curl_http_header(data, conn, hd, hdlen);
-    if(result)
-      return result;
-
-    /*
-     * Taken in one (more) header. Write it to the client.
-     */
-    Curl_debug(data, CURLINFO_HEADER_IN, hd, hdlen);
-
-    if(k->httpcode/100 == 1)
-      writetype |= CLIENTWRITE_1XX;
-    result = Curl_client_write(data, writetype, hd, hdlen);
-    if(result)
-      return result;
-
-    result = Curl_bump_headersize(data, hdlen, FALSE);
     if(result)
       return result;
-
-    Curl_dyn_reset(&data->state.headerb);
   }
-  while(blen);
 
   /* We might have reached the end of the header part here, but
      there might be a non-header part left in the end of the read
@@ -3935,6 +3925,22 @@ out:
   return CURLE_OK;
 }
 
+CURLcode Curl_http_write_resp_hd(struct Curl_easy *data,
+                                 const char *hd, size_t hdlen,
+                                 bool is_eos)
+{
+  CURLcode result;
+  size_t consumed;
+  char tmp = 0;
+
+  result = http_rw_hd(data, hd, hdlen, &tmp, 0, &consumed);
+  if(!result && is_eos) {
+    result = Curl_client_write(data, (CLIENTWRITE_BODY|CLIENTWRITE_EOS),
+                               &tmp, 0);
+  }
+  return result;
+}
+
 /*
  * HTTP protocol `write_resp` implementation. Will parse headers
  * when not done yet and otherwise return without consuming data.
@@ -3950,11 +3956,8 @@ CURLcode Curl_http_write_resp_hds(struct Curl_easy *data,
   else {
     CURLcode result;
 
-    result = http_rw_headers(data, buf, blen, pconsumed);
+    result = http_parse_headers(data, buf, blen, pconsumed);
     if(!result && !data->req.header) {
-      /* we have successfully finished parsing the HEADERs */
-      result = Curl_http_firstwrite(data);
-
       if(!data->req.no_body && Curl_dyn_len(&data->state.headerb)) {
         /* leftover from parsing something that turned out not
          * to be a header, only happens if we allow for
index 047709f20fc5885c97e9cf0a4a2b0f20e46d3af4..47fae63c782b214c17fd8389d455887563bc24d9 100644 (file)
@@ -102,8 +102,8 @@ CURLcode Curl_http_target(struct Curl_easy *data, struct connectdata *conn,
                           struct dynbuf *req);
 CURLcode Curl_http_statusline(struct Curl_easy *data,
                               struct connectdata *conn);
-CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn,
-                          char *headp, size_t hdlen);
+CURLcode Curl_http_header(struct Curl_easy *data,
+                          const char *hd, size_t hdlen);
 CURLcode Curl_transferencode(struct Curl_easy *data);
 CURLcode Curl_http_req_set_reader(struct Curl_easy *data,
                                   Curl_HttpReq httpreq,
@@ -134,6 +134,9 @@ int Curl_http_getsock_do(struct Curl_easy *data, struct connectdata *conn,
 CURLcode Curl_http_write_resp(struct Curl_easy *data,
                               const char *buf, size_t blen,
                               bool is_eos);
+CURLcode Curl_http_write_resp_hd(struct Curl_easy *data,
+                                 const char *hd, size_t hdlen,
+                                 bool is_eos);
 
 /* These functions are in http.c */
 CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
index fb097c51bbfa9b9055e690ae4620ae61f9abc783..ca224fd6670c2c20d2210a077702f96ac888ad56 100644 (file)
@@ -127,6 +127,7 @@ struct cf_h2_ctx {
   struct bufq inbufq;           /* network input */
   struct bufq outbufq;          /* network output */
   struct bufc_pool stream_bufcp; /* spares for stream buffers */
+  struct dynbuf scratch;        /* scratch buffer for temp use */
 
   size_t drain_total; /* sum of all stream's UrlState drain */
   uint32_t max_concurrent_streams;
@@ -153,6 +154,7 @@ static void cf_h2_ctx_clear(struct cf_h2_ctx *ctx)
   Curl_bufq_free(&ctx->inbufq);
   Curl_bufq_free(&ctx->outbufq);
   Curl_bufcp_free(&ctx->stream_bufcp);
+  Curl_dyn_free(&ctx->scratch);
   memset(ctx, 0, sizeof(*ctx));
   ctx->call_data = save;
 }
@@ -408,6 +410,7 @@ static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf,
   Curl_bufcp_init(&ctx->stream_bufcp, H2_CHUNK_SIZE, H2_STREAM_POOL_SPARES);
   Curl_bufq_initp(&ctx->inbufq, &ctx->stream_bufcp, H2_NW_RECV_CHUNKS, 0);
   Curl_bufq_initp(&ctx->outbufq, &ctx->stream_bufcp, H2_NW_SEND_CHUNKS, 0);
+  Curl_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER);
   ctx->last_stream_id = 2147483647;
 
   rc = nghttp2_session_callbacks_new(&cbs);
@@ -945,14 +948,6 @@ fail:
   return rv;
 }
 
-static CURLcode recvbuf_write_hds(struct Curl_cfilter *cf,
-                                  struct Curl_easy *data,
-                                  const char *buf, size_t blen)
-{
-  (void)cf;
-  return Curl_xfer_write_resp(data, (char *)buf, blen, FALSE);
-}
-
 static CURLcode on_stream_frame(struct Curl_cfilter *cf,
                                 struct Curl_easy *data,
                                 const nghttp2_frame *frame)
@@ -1008,7 +1003,7 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf,
       stream->status_code = -1;
     }
 
-    result = recvbuf_write_hds(cf, data, STRCONST("\r\n"));
+    result = Curl_xfer_write_resp_hd(data, STRCONST("\r\n"), stream->closed);
     if(result)
       return result;
 
@@ -1359,6 +1354,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
                      void *userp)
 {
   struct Curl_cfilter *cf = userp;
+  struct cf_h2_ctx *ctx = cf->ctx;
   struct h2_stream_ctx *stream;
   struct Curl_easy *data_s;
   int32_t stream_id = frame->hd.stream_id;
@@ -1468,14 +1464,15 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
     result = Curl_headers_push(data_s, buffer, CURLH_PSEUDO);
     if(result)
       return NGHTTP2_ERR_CALLBACK_FAILURE;
-    result = recvbuf_write_hds(cf, data_s, STRCONST("HTTP/2 "));
-    if(result)
-      return NGHTTP2_ERR_CALLBACK_FAILURE;
-    result = recvbuf_write_hds(cf, data_s, (const char *)value, valuelen);
-    if(result)
-      return NGHTTP2_ERR_CALLBACK_FAILURE;
-    /* the space character after the status code is mandatory */
-    result = recvbuf_write_hds(cf, data_s, STRCONST(" \r\n"));
+    Curl_dyn_reset(&ctx->scratch);
+    result = Curl_dyn_addn(&ctx->scratch, STRCONST("HTTP/2 "));
+    if(!result)
+      result = Curl_dyn_addn(&ctx->scratch, value, valuelen);
+    if(!result)
+      result = Curl_dyn_addn(&ctx->scratch, STRCONST(" \r\n"));
+    if(!result)
+      result = Curl_xfer_write_resp_hd(data_s, Curl_dyn_ptr(&ctx->scratch),
+                                       Curl_dyn_len(&ctx->scratch), FALSE);
     if(result)
       return NGHTTP2_ERR_CALLBACK_FAILURE;
     /* if we receive data for another handle, wake that up */
@@ -1490,16 +1487,17 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
   /* nghttp2 guarantees that namelen > 0, and :status was already
      received, and this is not pseudo-header field . */
   /* convert to an HTTP1-style header */
-  result = recvbuf_write_hds(cf, data_s, (const char *)name, namelen);
-  if(result)
-    return NGHTTP2_ERR_CALLBACK_FAILURE;
-  result = recvbuf_write_hds(cf, data_s, STRCONST(": "));
-  if(result)
-    return NGHTTP2_ERR_CALLBACK_FAILURE;
-  result = recvbuf_write_hds(cf, data_s, (const char *)value, valuelen);
-  if(result)
-    return NGHTTP2_ERR_CALLBACK_FAILURE;
-  result = recvbuf_write_hds(cf, data_s, STRCONST("\r\n"));
+  Curl_dyn_reset(&ctx->scratch);
+  result = Curl_dyn_addn(&ctx->scratch, (const char *)name, namelen);
+  if(!result)
+    result = Curl_dyn_addn(&ctx->scratch, STRCONST(": "));
+  if(!result)
+    result = Curl_dyn_addn(&ctx->scratch, (const char *)value, valuelen);
+  if(!result)
+    result = Curl_dyn_addn(&ctx->scratch, STRCONST("\r\n"));
+  if(!result)
+    result = Curl_xfer_write_resp_hd(data_s, Curl_dyn_ptr(&ctx->scratch),
+                                     Curl_dyn_len(&ctx->scratch), FALSE);
   if(result)
     return NGHTTP2_ERR_CALLBACK_FAILURE;
   /* if we receive data for another handle, wake that up */
index 0e013e740ae922344bc5a19fa4ce85bea74afcc8..679bfae977f443d1238399efb9e4586d407ff0d9 100644 (file)
@@ -131,6 +131,7 @@ const struct Curl_handler Curl_handler_imap = {
   ZERO_NULL,                        /* perform_getsock */
   imap_disconnect,                  /* disconnect */
   ZERO_NULL,                        /* write_resp */
+  ZERO_NULL,                        /* write_resp_hd */
   ZERO_NULL,                        /* connection_check */
   ZERO_NULL,                        /* attach connection */
   PORT_IMAP,                        /* defport */
@@ -160,6 +161,7 @@ const struct Curl_handler Curl_handler_imaps = {
   ZERO_NULL,                        /* perform_getsock */
   imap_disconnect,                  /* disconnect */
   ZERO_NULL,                        /* write_resp */
+  ZERO_NULL,                        /* write_resp_hd */
   ZERO_NULL,                        /* connection_check */
   ZERO_NULL,                        /* attach connection */
   PORT_IMAPS,                       /* defport */
index 394fd32d4a14351373c33fca84ac6d058e5c800b..e2546aa59ecacc38c4315e1b4f2ca115689b6548 100644 (file)
@@ -178,6 +178,7 @@ const struct Curl_handler Curl_handler_ldap = {
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* disconnect */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_LDAP,                            /* defport */
@@ -206,6 +207,7 @@ const struct Curl_handler Curl_handler_ldaps = {
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* disconnect */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_LDAPS,                           /* defport */
index 9290da031ba3fda5b8b203fc22d2ff1ae76ba103..35458648daee175426f3e96622ed3e1cc55490e9 100644 (file)
@@ -89,6 +89,7 @@ const struct Curl_handler Curl_handler_mqtt = {
   ZERO_NULL,                          /* perform_getsock */
   ZERO_NULL,                          /* disconnect */
   ZERO_NULL,                          /* write_resp */
+  ZERO_NULL,                          /* write_resp_hd */
   ZERO_NULL,                          /* connection_check */
   ZERO_NULL,                          /* attach connection */
   PORT_MQTT,                          /* defport */
index 85a37b8186041a31c26027455b7909a095cb843c..a5fac50577d33fb780f692e74381bfb25a2aa194 100644 (file)
@@ -131,6 +131,7 @@ const struct Curl_handler Curl_handler_ldap = {
   ZERO_NULL,                            /* perform_getsock */
   oldap_disconnect,                     /* disconnect */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_LDAP,                            /* defport */
@@ -159,6 +160,7 @@ const struct Curl_handler Curl_handler_ldaps = {
   ZERO_NULL,                            /* perform_getsock */
   oldap_disconnect,                     /* disconnect */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_LDAPS,                           /* defport */
index 993b2e1c7f526fd3f990d9c08a76447e8cc17680..85c12cbf2cd3d33335dc124b724ff82fae98cf68 100644 (file)
@@ -126,6 +126,7 @@ const struct Curl_handler Curl_handler_pop3 = {
   ZERO_NULL,                        /* perform_getsock */
   pop3_disconnect,                  /* disconnect */
   ZERO_NULL,                        /* write_resp */
+  ZERO_NULL,                        /* write_resp_hd */
   ZERO_NULL,                        /* connection_check */
   ZERO_NULL,                        /* attach connection */
   PORT_POP3,                        /* defport */
@@ -155,6 +156,7 @@ const struct Curl_handler Curl_handler_pop3s = {
   ZERO_NULL,                        /* perform_getsock */
   pop3_disconnect,                  /* disconnect */
   ZERO_NULL,                        /* write_resp */
+  ZERO_NULL,                        /* write_resp_hd */
   ZERO_NULL,                        /* connection_check */
   ZERO_NULL,                        /* attach connection */
   PORT_POP3S,                       /* defport */
@@ -1450,7 +1452,7 @@ static CURLcode pop3_parse_custom_request(struct Curl_easy *data)
  * This function scans the body after the end-of-body and writes everything
  * until the end is found.
  */
-CURLcode Curl_pop3_write(struct Curl_easy *data, char *str, size_t nread)
+CURLcode Curl_pop3_write(struct Curl_easy *data, const char *str, size_t nread)
 {
   /* This code could be made into a special function in the handler struct */
   CURLcode result = CURLE_OK;
index 83f0f831e6c1fa4979b05204c5074a2efada9f4c..e8e98db04b6237ed79c9cb06d0baefd57dacef4a 100644 (file)
@@ -92,6 +92,7 @@ extern const struct Curl_handler Curl_handler_pop3s;
 
 /* This function scans the body after the end-of-body and writes everything
  * until the end is found */
-CURLcode Curl_pop3_write(struct Curl_easy *data, char *str, size_t nread);
+CURLcode Curl_pop3_write(struct Curl_easy *data,
+                         const char *str, size_t nread);
 
 #endif /* HEADER_CURL_POP3_H */
index 40515a9907548475a72fd2a819219bee62904c04..d1605d32a165fe67b6a869f04196e1389f19af6b 100644 (file)
@@ -266,7 +266,7 @@ static CURLcode req_set_upload_done(struct Curl_easy *data)
   else if(data->req.writebytecount)
     infof(data, "upload completely sent off: %" CURL_FORMAT_CURL_OFF_T
           " bytes", data->req.writebytecount);
-  else
+  else if(!data->req.download_done)
     infof(data, Curl_creader_total_length(data)?
                 "We are completely uploaded and fine" :
                 "Request completely sent off");
index 7251c062b1b074dc563467f8abd0048745b37304..ab7fda4c06368b471d4b25b842bc0888230591e4 100644 (file)
@@ -93,7 +93,7 @@ static int rtsp_getsock_do(struct Curl_easy *data, struct connectdata *conn,
 static
 CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, size_t len);
 static
-CURLcode rtsp_parse_transport(struct Curl_easy *data, char *transport);
+CURLcode rtsp_parse_transport(struct Curl_easy *data, const char *transport);
 
 
 /*
@@ -114,6 +114,7 @@ const struct Curl_handler Curl_handler_rtsp = {
   ZERO_NULL,                            /* perform_getsock */
   rtsp_disconnect,                      /* disconnect */
   rtsp_rtp_write_resp,                  /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   rtsp_conncheck,                       /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_RTSP,                            /* defport */
@@ -913,12 +914,12 @@ CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, size_t len)
   return CURLE_OK;
 }
 
-CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, char *header)
+CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, const char *header)
 {
   if(checkprefix("CSeq:", header)) {
     long CSeq = 0;
     char *endp;
-    char *p = &header[5];
+    const char *p = &header[5];
     while(ISBLANK(*p))
       p++;
     CSeq = strtol(p, &endp, 10);
@@ -933,8 +934,7 @@ CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, char *header)
     }
   }
   else if(checkprefix("Session:", header)) {
-    char *start;
-    char *end;
+    const char *start, *end;
     size_t idlen;
 
     /* Find the first non-space letter */
@@ -989,14 +989,13 @@ CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, char *header)
 }
 
 static
-CURLcode rtsp_parse_transport(struct Curl_easy *data, char *transport)
+CURLcode rtsp_parse_transport(struct Curl_easy *data, const char *transport)
 {
   /* If we receive multiple Transport response-headers, the linterleaved
      channels of each response header is recorded and used together for
      subsequent data validity checks.*/
   /* e.g.: ' RTP/AVP/TCP;unicast;interleaved=5-6' */
-  char *start;
-  char *end;
+  const char *start, *end;
   start = transport;
   while(start && *start) {
     while(*start && ISBLANK(*start) )
@@ -1005,7 +1004,7 @@ CURLcode rtsp_parse_transport(struct Curl_easy *data, char *transport)
     if(checkprefix("interleaved=", start)) {
       long chan1, chan2, chan;
       char *endp;
-      char *p = start + 12;
+      const char *p = start + 12;
       chan1 = strtol(p, &endp, 10);
       if(p != endp && chan1 >= 0 && chan1 <= 255) {
         unsigned char *rtp_channel_mask = data->state.rtp_channel_mask;
index 237b80f809fa4045b1dd4fb0952335dad88d0bc1..b1ffa5c7ea651c9d2d0b60cd26a8d9f29677a096 100644 (file)
@@ -31,7 +31,7 @@
 
 extern const struct Curl_handler Curl_handler_rtsp;
 
-CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, char *header);
+CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, const char *header);
 
 #else
 /* disabled */
index 77d34e31c4839534d27a7fbfb5b1575d5a3b4516..2ce6dbf7fc11013e26f99fd5604488f406b10534 100644 (file)
--- a/lib/smb.c
+++ b/lib/smb.c
@@ -273,6 +273,7 @@ const struct Curl_handler Curl_handler_smb = {
   ZERO_NULL,                            /* perform_getsock */
   smb_disconnect,                       /* disconnect */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_SMB,                             /* defport */
@@ -300,6 +301,7 @@ const struct Curl_handler Curl_handler_smbs = {
   ZERO_NULL,                            /* perform_getsock */
   smb_disconnect,                       /* disconnect */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_SMBS,                            /* defport */
index 20763c0c823b45adcd926613779c8cb41fe170d5..dc2f1c91804eb9c641b11959b3e3d83042dadc4b 100644 (file)
@@ -132,6 +132,7 @@ const struct Curl_handler Curl_handler_smtp = {
   ZERO_NULL,                        /* perform_getsock */
   smtp_disconnect,                  /* disconnect */
   ZERO_NULL,                        /* write_resp */
+  ZERO_NULL,                        /* write_resp_hd */
   ZERO_NULL,                        /* connection_check */
   ZERO_NULL,                        /* attach connection */
   PORT_SMTP,                        /* defport */
@@ -161,6 +162,7 @@ const struct Curl_handler Curl_handler_smtps = {
   ZERO_NULL,                        /* perform_getsock */
   smtp_disconnect,                  /* disconnect */
   ZERO_NULL,                        /* write_resp */
+  ZERO_NULL,                        /* write_resp_hd */
   ZERO_NULL,                        /* connection_check */
   ZERO_NULL,                        /* attach connection */
   PORT_SMTPS,                       /* defport */
index 56ee0855f0f875d815d02c374bbc5a02825679c7..b129ff5202102eb45708742784df3c46ed9cca14 100644 (file)
@@ -187,6 +187,7 @@ const struct Curl_handler Curl_handler_telnet = {
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* disconnect */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_TELNET,                          /* defport */
index ff2b7b7457b2e2c37eef0f3f1fb80afab379d98f..4667ae1e42d6dfacaf3308c7a8f819e8b630c75a 100644 (file)
@@ -182,6 +182,7 @@ const struct Curl_handler Curl_handler_tftp = {
   ZERO_NULL,                            /* perform_getsock */
   tftp_disconnect,                      /* disconnect */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_TFTP,                            /* defport */
index ac4710ef5d3ca41f95de34120ec7503422c2233e..6e2067cd6a271d41e76eb5bb1c69fb40578667ba 100644 (file)
@@ -1156,7 +1156,7 @@ void Curl_xfer_setup(
 }
 
 CURLcode Curl_xfer_write_resp(struct Curl_easy *data,
-                              char *buf, size_t blen,
+                              const char *buf, size_t blen,
                               bool is_eos)
 {
   CURLcode result = CURLE_OK;
@@ -1195,6 +1195,18 @@ CURLcode Curl_xfer_write_resp(struct Curl_easy *data,
   return result;
 }
 
+CURLcode Curl_xfer_write_resp_hd(struct Curl_easy *data,
+                                 const char *hd0, size_t hdlen, bool is_eos)
+{
+  if(data->conn->handler->write_resp_hd) {
+    /* protocol handlers offering this function take full responsibility
+     * for writing all received download data to the client. */
+    return data->conn->handler->write_resp_hd(data, hd0, hdlen, is_eos);
+  }
+  /* No special handling by protocol handler, write as response bytes */
+  return Curl_xfer_write_resp(data, hd0, hdlen, is_eos);
+}
+
 CURLcode Curl_xfer_write_done(struct Curl_easy *data, bool premature)
 {
   (void)premature;
index e65b2b147215000e6acef92c1e05de40ac087fc1..ad0f3a20cc95b15685083cbb9cbac280acbe7bfc 100644 (file)
@@ -62,12 +62,20 @@ bool Curl_meets_timecondition(struct Curl_easy *data, time_t timeofdoc);
  * @param blen     the amount of bytes in `buf`
  * @param is_eos   TRUE iff the connection indicates this to be the last
  *                 bytes of the response
- * @param done     on returnm, TRUE iff the response is complete
  */
 CURLcode Curl_xfer_write_resp(struct Curl_easy *data,
-                              char *buf, size_t blen,
+                              const char *buf, size_t blen,
                               bool is_eos);
 
+/**
+ * Write a single "header" line from a server response.
+ * @param hd0      the 0-terminated, single header line
+ * @param hdlen    the length of the header line
+ * @param is_eos   TRUE iff this is the end of the response
+ */
+CURLcode Curl_xfer_write_resp_hd(struct Curl_easy *data,
+                                 const char *hd0, size_t hdlen, bool is_eos);
+
 /* This sets up a forthcoming transfer */
 void Curl_xfer_setup(struct Curl_easy *data,
                      int sockindex,     /* socket index to read from or -1 */
index 90f4d1bb574cc2119bae3504a1e092199f0f81a3..308c51c923796bf0f3521b907828bc65440a0454 100644 (file)
@@ -701,12 +701,18 @@ struct Curl_handler {
   CURLcode (*disconnect)(struct Curl_easy *, struct connectdata *,
                          bool dead_connection);
 
-  /* If used, this function gets called from transfer.c:readwrite_data() to
+  /* If used, this function gets called from transfer.c to
      allow the protocol to do extra handling in writing response to
      the client. */
   CURLcode (*write_resp)(struct Curl_easy *data, const char *buf, size_t blen,
                          bool is_eos);
 
+  /* If used, this function gets called from transfer.c to
+     allow the protocol to do extra handling in writing a single response
+     header line to the client. */
+  CURLcode (*write_resp_hd)(struct Curl_easy *data,
+                            const char *hd, size_t hdlen, bool is_eos);
+
   /* This function can perform various checks on the connection. See
      CONNCHECK_* for more information about the checks that can be performed,
      and CONNRESULT_* for the results that can be returned. */
index eea8e1261e75ce1a0092874eea65e429dda25c32..74006b47277cfa36cf828c898ed3f89c2afe522d 100644 (file)
@@ -130,6 +130,7 @@ struct cf_ngtcp2_ctx {
   struct curltime handshake_at;      /* time connect handshake finished */
   struct curltime reconnect_at;      /* time the next attempt should start */
   struct bufc_pool stream_bufcp;     /* chunk pool for streams */
+  struct dynbuf scratch;             /* temp buffer for header construction */
   size_t max_stream_window;          /* max flow window for one stream */
   uint64_t max_idle_ms;              /* max idle time for QUIC connection */
   int qlogfd;
@@ -765,12 +766,6 @@ static int cb_h3_stream_close(nghttp3_conn *conn, int64_t sid,
   return 0;
 }
 
-static CURLcode write_resp_hds(struct Curl_easy *data,
-                               const char *buf, size_t blen)
-{
-  return Curl_xfer_write_resp(data, (char *)buf, blen, FALSE);
-}
-
 static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id,
                            const uint8_t *buf, size_t blen,
                            void *user_data, void *stream_user_data)
@@ -835,7 +830,7 @@ static int cb_h3_end_headers(nghttp3_conn *conn, int64_t sid,
   if(!stream)
     return 0;
   /* add a CRLF only if we've received some headers */
-  result = write_resp_hds(data, "\r\n", 2);
+  result = Curl_xfer_write_resp_hd(data, STRCONST("\r\n"), stream->closed);
   if(result) {
     return -1;
   }
@@ -855,6 +850,7 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t sid,
                              void *user_data, void *stream_user_data)
 {
   struct Curl_cfilter *cf = user_data;
+  struct cf_ngtcp2_ctx *ctx = cf->ctx;
   curl_int64_t stream_id = (curl_int64_t)sid;
   nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name);
   nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value);
@@ -872,17 +868,23 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t sid,
     return 0;
 
   if(token == NGHTTP3_QPACK_TOKEN__STATUS) {
-    char line[14]; /* status line is always 13 characters long */
-    size_t ncopy;
 
     result = Curl_http_decode_status(&stream->status_code,
                                      (const char *)h3val.base, h3val.len);
     if(result)
       return -1;
-    ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n",
-                      stream->status_code);
-    CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] status: %s", stream_id, line);
-    result = write_resp_hds(data, line, ncopy);
+    Curl_dyn_reset(&ctx->scratch);
+    result = Curl_dyn_addn(&ctx->scratch, STRCONST("HTTP/3 "));
+    if(!result)
+      result = Curl_dyn_addn(&ctx->scratch,
+                             (const char *)h3val.base, h3val.len);
+    if(!result)
+      result = Curl_dyn_addn(&ctx->scratch, STRCONST(" \r\n"));
+    if(!result)
+      result = Curl_xfer_write_resp_hd(data, Curl_dyn_ptr(&ctx->scratch),
+                                       Curl_dyn_len(&ctx->scratch), FALSE);
+    CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] status: %s",
+                stream_id, Curl_dyn_ptr(&ctx->scratch));
     if(result) {
       return -1;
     }
@@ -892,19 +894,19 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t sid,
     CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] header: %.*s: %.*s",
                 stream_id, (int)h3name.len, h3name.base,
                 (int)h3val.len, h3val.base);
-    result = write_resp_hds(data, (const char *)h3name.base, h3name.len);
-    if(result) {
-      return -1;
-    }
-    result = write_resp_hds(data, ": ", 2);
-    if(result) {
-      return -1;
-    }
-    result = write_resp_hds(data, (const char *)h3val.base, h3val.len);
-    if(result) {
-      return -1;
-    }
-    result = write_resp_hds(data, "\r\n", 2);
+    Curl_dyn_reset(&ctx->scratch);
+    result = Curl_dyn_addn(&ctx->scratch,
+                           (const char *)h3name.base, h3name.len);
+    if(!result)
+      result = Curl_dyn_addn(&ctx->scratch, STRCONST(": "));
+    if(!result)
+      result = Curl_dyn_addn(&ctx->scratch,
+                             (const char *)h3val.base, h3val.len);
+    if(!result)
+      result = Curl_dyn_addn(&ctx->scratch, STRCONST("\r\n"));
+    if(!result)
+      result = Curl_xfer_write_resp_hd(data, Curl_dyn_ptr(&ctx->scratch),
+                                       Curl_dyn_len(&ctx->scratch), FALSE);
     if(result) {
       return -1;
     }
@@ -1857,6 +1859,7 @@ static void cf_ngtcp2_ctx_clear(struct cf_ngtcp2_ctx *ctx)
   if(ctx->qconn)
     ngtcp2_conn_del(ctx->qconn);
   Curl_bufcp_free(&ctx->stream_bufcp);
+  Curl_dyn_free(&ctx->scratch);
   Curl_ssl_peer_cleanup(&ctx->peer);
 
   memset(ctx, 0, sizeof(*ctx));
@@ -1994,6 +1997,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
   ctx->max_idle_ms = CURL_QUIC_MAX_IDLE_MS;
   Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE,
                   H3_STREAM_POOL_SPARES);
+  Curl_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER);
 
   result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC);
   if(result)
index da7ce6ad9890e1b14a1f4bce418e12f09a4f9b9a..d6ba987f1c7c37a26f9db39bfa7ebab5aa5ad5e0 100644 (file)
@@ -162,6 +162,7 @@ const struct Curl_handler Curl_handler_scp = {
   myssh_getsock,                /* perform_getsock */
   scp_disconnect,               /* disconnect */
   ZERO_NULL,                    /* write_resp */
+  ZERO_NULL,                    /* write_resp_hd */
   ZERO_NULL,                    /* connection_check */
   ZERO_NULL,                    /* attach connection */
   PORT_SSH,                     /* defport */
@@ -189,6 +190,7 @@ const struct Curl_handler Curl_handler_sftp = {
   myssh_getsock,                        /* perform_getsock */
   sftp_disconnect,                      /* disconnect */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_SSH,                             /* defport */
index 7d8d5f46571e9f23ab351d9b5fd0bd8232ee4e27..6c5704b6a4eca07f2a461205cd6870f7282d2825 100644 (file)
@@ -139,6 +139,7 @@ const struct Curl_handler Curl_handler_scp = {
   ssh_getsock,                          /* perform_getsock */
   scp_disconnect,                       /* disconnect */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ssh_attach,                           /* attach */
   PORT_SSH,                             /* defport */
@@ -168,6 +169,7 @@ const struct Curl_handler Curl_handler_sftp = {
   ssh_getsock,                          /* perform_getsock */
   sftp_disconnect,                      /* disconnect */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ssh_attach,                           /* attach */
   PORT_SSH,                             /* defport */
index 11275910a1f92e61ea475cace44a45881dfa7d76..6a5aed88f74fe0bcbded0d32afe3258522cedf2c 100644 (file)
@@ -94,6 +94,7 @@ const struct Curl_handler Curl_handler_scp = {
   wssh_getsock,                         /* perform_getsock */
   wscp_disconnect,                      /* disconnect */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_SSH,                             /* defport */
@@ -123,6 +124,7 @@ const struct Curl_handler Curl_handler_sftp = {
   wssh_getsock,                         /* perform_getsock */
   wsftp_disconnect,                     /* disconnect */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_SSH,                             /* defport */
index 907f64bac3f5c8050ed0e7a5f3ffdce14fc6957c..d6a83be90f6425de8e0f2f80ddd726a032a2f6dd 100644 (file)
--- a/lib/ws.c
+++ b/lib/ws.c
@@ -1199,6 +1199,7 @@ const struct Curl_handler Curl_handler_ws = {
   ZERO_NULL,                            /* perform_getsock */
   ws_disconnect,                        /* disconnect */
   Curl_http_write_resp,                 /* write_resp */
+  Curl_http_write_resp_hd,              /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_HTTP,                            /* defport */
@@ -1224,6 +1225,7 @@ const struct Curl_handler Curl_handler_wss = {
   ZERO_NULL,                            /* perform_getsock */
   ws_disconnect,                        /* disconnect */
   Curl_http_write_resp,                 /* write_resp */
+  Curl_http_write_resp_hd,              /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_HTTPS,                           /* defport */