Browse Source

http: move headers collecting to writer

- add a client writer that does "push" response
  headers written to the client if the headers api
  is enabled
- remove special handling in sendf.c
- needs to be installed very early on connection
  setup to catch CONNECT response headers

Closes #12880
Stefan Eissing 2 months ago
parent
commit
2abfa3833b
6 changed files with 90 additions and 19 deletions
  1. 58 3
      lib/headers.c
  2. 7 0
      lib/headers.h
  3. 8 0
      lib/http.c
  4. 11 16
      lib/sendf.c
  5. 3 0
      lib/sendf.h
  6. 3 0
      lib/url.c

+ 58 - 3
lib/headers.c

@@ -27,6 +27,7 @@
 #include "urldata.h"
 #include "strdup.h"
 #include "strcase.h"
+#include "sendf.h"
 #include "headers.h"
 
 /* The last 3 #include files should be in this order */
@@ -337,14 +338,68 @@ CURLcode Curl_headers_push(struct Curl_easy *data, const char *header,
 }
 
 /*
- * Curl_headers_init(). Init the headers subsystem.
+ * Curl_headers_reset(). Reset the headers subsystem.
  */
-static void headers_init(struct Curl_easy *data)
+static void headers_reset(struct Curl_easy *data)
 {
   Curl_llist_init(&data->state.httphdrs, NULL);
   data->state.prevhead = NULL;
 }
 
+struct hds_cw_collect_ctx {
+  struct Curl_cwriter super;
+};
+
+static CURLcode hds_cw_collect_write(struct Curl_easy *data,
+                                     struct Curl_cwriter *writer, int type,
+                                     const char *buf, size_t blen)
+{
+  if((type & CLIENTWRITE_HEADER) && !(type & CLIENTWRITE_STATUS)) {
+    unsigned char htype = (unsigned char)
+      (type & CLIENTWRITE_CONNECT ? CURLH_CONNECT :
+       (type & CLIENTWRITE_1XX ? CURLH_1XX :
+        (type & CLIENTWRITE_TRAILER ? CURLH_TRAILER :
+         CURLH_HEADER)));
+    CURLcode result = Curl_headers_push(data, buf, htype);
+    if(result)
+      return result;
+  }
+  return Curl_cwriter_write(data, writer->next, type, buf, blen);
+}
+
+static const struct Curl_cwtype hds_cw_collect = {
+  "hds-collect",
+  NULL,
+  Curl_cwriter_def_init,
+  hds_cw_collect_write,
+  Curl_cwriter_def_close,
+  sizeof(struct hds_cw_collect_ctx)
+};
+
+CURLcode Curl_headers_init(struct Curl_easy *data)
+{
+  struct Curl_cwriter *writer;
+  CURLcode result;
+
+  if(data->conn && (data->conn->handler->protocol & PROTO_FAMILY_HTTP)) {
+    /* avoid installing it twice */
+    if(Curl_cwriter_get_by_name(data, hds_cw_collect.name))
+      return CURLE_OK;
+
+    result = Curl_cwriter_create(&writer, data, &hds_cw_collect,
+                                 CURL_CW_PROTOCOL);
+    if(result)
+      return result;
+
+    result = Curl_cwriter_add(data, writer);
+    if(result) {
+      Curl_cwriter_free(data, writer);
+      return result;
+    }
+  }
+  return CURLE_OK;
+}
+
 /*
  * Curl_headers_cleanup(). Free all stored headers and associated memory.
  */
@@ -358,7 +413,7 @@ CURLcode Curl_headers_cleanup(struct Curl_easy *data)
     n = e->next;
     free(hs);
   }
-  headers_init(data);
+  headers_reset(data);
   return CURLE_OK;
 }
 

+ 7 - 0
lib/headers.h

@@ -36,6 +36,12 @@ struct Curl_header_store {
   char buffer[1]; /* this is the raw header blob */
 };
 
+/*
+ * Initialize header collecting for a transfer.
+ * Will add a client writer that catches CLIENTWRITE_HEADER writes.
+ */
+CURLcode Curl_headers_init(struct Curl_easy *data);
+
 /*
  * Curl_headers_push() gets passed a full header to store.
  */
@@ -48,6 +54,7 @@ CURLcode Curl_headers_push(struct Curl_easy *data, const char *header,
 CURLcode Curl_headers_cleanup(struct Curl_easy *data);
 
 #else
+#define Curl_headers_init(x) CURLE_OK
 #define Curl_headers_push(x,y,z) CURLE_OK
 #define Curl_headers_cleanup(x) Curl_nop_stmt
 #endif

+ 8 - 0
lib/http.c

@@ -73,6 +73,7 @@
 #include "hostip.h"
 #include "dynhds.h"
 #include "http.h"
+#include "headers.h"
 #include "select.h"
 #include "parsedate.h" /* for the week day and month names */
 #include "strtoofft.h"
@@ -3131,6 +3132,13 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
     break;
   }
 
+  /* 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;
+
   http = data->req.p.http;
   DEBUGASSERT(http);
 

+ 11 - 16
lib/sendf.c

@@ -49,7 +49,6 @@
 #include "select.h"
 #include "strdup.h"
 #include "http2.h"
-#include "headers.h"
 #include "progress.h"
 #include "ws.h"
 
@@ -256,21 +255,6 @@ static CURLcode chop_write(struct Curl_easy *data,
     len -= chunklen;
   }
 
-#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HEADERS_API)
-  /* HTTP header, but not status-line */
-  if((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
-     (type & CLIENTWRITE_HEADER) && !(type & CLIENTWRITE_STATUS) ) {
-    unsigned char htype = (unsigned char)
-      (type & CLIENTWRITE_CONNECT ? CURLH_CONNECT :
-       (type & CLIENTWRITE_1XX ? CURLH_1XX :
-        (type & CLIENTWRITE_TRAILER ? CURLH_TRAILER :
-         CURLH_HEADER)));
-    CURLcode result = Curl_headers_push(data, optr, htype);
-    if(result)
-      return result;
-  }
-#endif
-
   if(writeheader) {
     size_t wrote;
 
@@ -674,6 +658,17 @@ CURLcode Curl_cwriter_add(struct Curl_easy *data,
   return CURLE_OK;
 }
 
+struct Curl_cwriter *Curl_cwriter_get_by_name(struct Curl_easy *data,
+                                              const char *name)
+{
+  struct Curl_cwriter *writer;
+  for(writer = data->req.writer_stack; writer; writer = writer->next) {
+    if(!strcmp(name, writer->cwt->name))
+      return writer;
+  }
+  return NULL;
+}
+
 void Curl_cwriter_remove_by_name(struct Curl_easy *data,
                                  const char *name)
 {

+ 3 - 0
lib/sendf.h

@@ -151,6 +151,9 @@ CURLcode Curl_cwriter_add(struct Curl_easy *data,
 void Curl_cwriter_remove_by_name(struct Curl_easy *data,
                                  const char *name);
 
+struct Curl_cwriter *Curl_cwriter_get_by_name(struct Curl_easy *data,
+                                              const char *name);
+
 /**
  * Convenience method for calling `writer->do_write()` that
  * checks for NULL writer.

+ 3 - 0
lib/url.c

@@ -3848,6 +3848,9 @@ CURLcode Curl_setup_conn(struct Curl_easy *data,
   if(!conn->bits.reuse)
     result = Curl_conn_setup(data, conn, FIRSTSOCKET, conn->dns_entry,
                              CURL_CF_SSL_DEFAULT);
+  if(!result)
+    result = Curl_headers_init(data);
+
   /* not sure we need this flag to be passed around any more */
   *protocol_done = FALSE;
   return result;