gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] [gnurl] 63/220: quiche: first working HTTP/3 request


From: gnunet
Subject: [GNUnet-SVN] [gnurl] 63/220: quiche: first working HTTP/3 request
Date: Thu, 12 Sep 2019 17:27:03 +0200

This is an automated email from the git hooks/post-receive script.

ng0 pushed a commit to branch master
in repository gnurl.

commit dc35631ef7fe314c227ba0b8529a4e0f5f383f79
Author: Daniel Stenberg <address@hidden>
AuthorDate: Mon Aug 5 15:17:31 2019 +0200

    quiche: first working HTTP/3 request
    
     - enable debug log
     - fix use of quiche API
     - use download buffer
     - separate header/body
    
    Closes #4193
---
 lib/http.c         |   7 ++--
 lib/http.h         |  11 +++---
 lib/transfer.c     |   2 +-
 lib/vquic/quiche.c | 100 +++++++++++++++++++++++++++++++++++++++++------------
 4 files changed, 89 insertions(+), 31 deletions(-)

diff --git a/lib/http.c b/lib/http.c
index 83180fb29..9d8cd5570 100644
--- a/lib/http.c
+++ b/lib/http.c
@@ -3677,6 +3677,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy 
*data,
          * guarantees on future behaviors since it isn't within the protocol.
          */
         char separator;
+        char twoorthree[2];
         nc = sscanf(HEADER1,
                     " HTTP/%1d.%1d%c%3d",
                     &httpversion_major,
@@ -3684,8 +3685,8 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy 
*data,
                     &separator,
                     &k->httpcode);
 
-        if(nc == 1 && httpversion_major == 2 &&
-           1 == sscanf(HEADER1, " HTTP/2 %d", &k->httpcode)) {
+        if(nc == 1 && httpversion_major >= 2 &&
+           2 == sscanf(HEADER1, " HTTP/%1[23] %d", twoorthree, &k->httpcode)) {
           conn->httpversion = 0;
           nc = 4;
           separator = ' ';
@@ -3723,7 +3724,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy 
*data,
           }
         }
         else {
-          failf(data, "Unsupported HTTP version in response\n");
+          failf(data, "Unsupported HTTP version in response");
           return CURLE_UNSUPPORTED_PROTOCOL;
         }
       }
diff --git a/lib/http.h b/lib/http.h
index ea1310c39..f6f5d4fd5 100644
--- a/lib/http.h
+++ b/lib/http.h
@@ -178,18 +178,21 @@ struct HTTP {
   size_t len;    /* size of the buffer 'mem' points to */
   size_t memlen; /* size of data copied to mem */
 
-  const uint8_t *upload_mem; /* points to a buffer to read from */
-  size_t upload_len; /* size of the buffer 'upload_mem' points to */
-  curl_off_t upload_left; /* number of bytes left to upload */
-
   char **push_headers;       /* allocated array */
   size_t push_headers_used;  /* number of entries filled in */
   size_t push_headers_alloc; /* number of entries allocated */
 #endif
+#if defined(USE_NGHTTP2) || defined(ENABLE_QUIC)
+  /* fields used by both HTTP/2 and HTTP/3 */
+  const uint8_t *upload_mem; /* points to a buffer to read from */
+  size_t upload_len; /* size of the buffer 'upload_mem' points to */
+  curl_off_t upload_left; /* number of bytes left to upload */
+#endif
 
 #ifdef ENABLE_QUIC
   /*********** for HTTP/3 we store stream-local data here *************/
   int64_t stream3_id; /* stream we are interested in */
+  bool firstbody;  /* FALSE until body arrives */
 #endif
 };
 
diff --git a/lib/transfer.c b/lib/transfer.c
index 7f2e6d1fb..ab662fbc0 100644
--- a/lib/transfer.c
+++ b/lib/transfer.c
@@ -497,7 +497,7 @@ static int data_pending(const struct connectdata *conn)
        TRUE. The thing is if we read everything, then http2_recv won't
        be called and we cannot signal the HTTP/2 stream has closed. As
        a workaround, we return nonzero here to call http2_recv. */
-    ((conn->handler->protocol&PROTO_FAMILY_HTTP) && conn->httpversion == 20);
+    ((conn->handler->protocol&PROTO_FAMILY_HTTP) && conn->httpversion >= 20);
 #else
     Curl_ssl_data_pending(conn, FIRSTSOCKET);
 #endif
diff --git a/lib/vquic/quiche.c b/lib/vquic/quiche.c
index 2ccee1142..a70fb2237 100644
--- a/lib/vquic/quiche.c
+++ b/lib/vquic/quiche.c
@@ -38,7 +38,7 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-#define DEBUG_HTTP3
+/* #define DEBUG_HTTP3 */
 #ifdef DEBUG_HTTP3
 #define H3BUGF(x) x
 #else
@@ -198,10 +198,12 @@ static CURLcode process_ingress(struct connectdata *conn, 
int sockfd)
 {
   ssize_t recvd;
   struct quicsocket *qs = &conn->quic;
-  static uint8_t buf[65535];
+  struct Curl_easy *data = conn->data;
+  uint8_t *buf = (uint8_t *)data->state.buffer;
+  size_t bufsize = data->set.buffer_size;
 
   do {
-    recvd = recv(sockfd, buf, sizeof(buf), 0);
+    recvd = recv(sockfd, buf, bufsize, 0);
     if((recvd < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
       break;
 
@@ -245,13 +247,33 @@ static CURLcode flush_egress(struct connectdata *conn, 
int sockfd)
   return CURLE_OK;
 }
 
+struct h3h1header {
+  char *dest;
+  size_t destlen; /* left to use */
+  size_t nlen; /* used */
+};
+
 static int cb_each_header(uint8_t *name, size_t name_len,
                           uint8_t *value, size_t value_len,
                           void *argp)
 {
-  (void)argp;
-  fprintf(stderr, "got HTTP header: %.*s=%.*s\n",
-          (int) name_len, name, (int) value_len, value);
+  struct h3h1header *headers = (struct h3h1header *)argp;
+  size_t olen = 0;
+
+  if((name_len == 7) && !strncmp(":status", (char *)name, 7)) {
+    msnprintf(headers->dest,
+              headers->destlen, "HTTP/3 %.*s\n",
+              (int) value_len, value);
+  }
+  else {
+    msnprintf(headers->dest,
+              headers->destlen, "%.*s: %.*s\n",
+              (int)name_len, name, (int) value_len, value);
+  }
+  olen = strlen(headers->dest);
+  headers->destlen -= olen;
+  headers->nlen += olen;
+  headers->dest += olen;
   return 0;
 }
 
@@ -261,60 +283,80 @@ static ssize_t h3_stream_recv(struct connectdata *conn,
                               size_t buffersize,
                               CURLcode *curlcode)
 {
-  bool fin;
-  ssize_t recvd;
+  ssize_t recvd = -1;
+  ssize_t rcode;
   struct quicsocket *qs = &conn->quic;
   curl_socket_t sockfd = conn->sock[sockindex];
   quiche_h3_event *ev;
   int rc;
+  struct h3h1header headers;
+  struct HTTP *stream = conn->data->req.protop;
+  headers.dest = buf;
+  headers.destlen = buffersize;
+  headers.nlen = 0;
 
   if(process_ingress(conn, sockfd)) {
+    infof(conn->data, "h3_stream_recv returns on ingress\n");
     *curlcode = CURLE_RECV_ERROR;
     return -1;
   }
 
-  recvd = quiche_conn_stream_recv(qs->conn, 0, (uint8_t *) buf, buffersize,
-                                  &fin);
-  if(recvd == QUICHE_ERR_DONE) {
-    *curlcode = CURLE_AGAIN;
-    return -1;
-  }
-
-  infof(conn->data, "%zd bytes of H3 to deal with\n", recvd);
-
-  while(1) {
+  while(recvd < 0) {
     int64_t s = quiche_h3_conn_poll(qs->h3c, qs->conn, &ev);
     if(s < 0)
       /* nothing more to do */
       break;
 
+    infof(conn->data, "quiche_h3_conn_poll got something: %zd\n", s);
+
     switch(quiche_h3_event_type(ev)) {
     case QUICHE_H3_EVENT_HEADERS:
-      rc = quiche_h3_event_for_each_header(ev, cb_each_header, NULL);
+      infof(conn->data, "quiche says HEADERS\n");
+      rc = quiche_h3_event_for_each_header(ev, cb_each_header, &headers);
       if(rc) {
         fprintf(stderr, "failed to process headers");
         /* what do we do about this? */
       }
+      recvd = headers.nlen;
       break;
     case QUICHE_H3_EVENT_DATA:
-      recvd = quiche_h3_recv_body(qs->h3c, qs->conn, s, (unsigned char *)buf,
+      infof(conn->data, "quiche says DATA\n");
+      if(!stream->firstbody) {
+        /* add a header-body separator CRLF */
+        buf[0] = '\r';
+        buf[1] = '\n';
+        buf += 2;
+        buffersize = 2;
+        stream->firstbody = TRUE;
+        recvd = 2; /* two bytes already */
+      }
+      else
+        recvd = 0;
+      rcode = quiche_h3_recv_body(qs->h3c, qs->conn, s, (unsigned char *)buf,
                                   buffersize);
-      if(recvd <= 0) {
+      if(rcode <= 0) {
+        recvd = -1;
         break;
       }
+      recvd += rcode;
       break;
 
     case QUICHE_H3_EVENT_FINISHED:
+      infof(conn->data, "quiche says FINISHED\n");
       if(quiche_conn_close(qs->conn, true, 0, NULL, 0) < 0) {
         fprintf(stderr, "failed to close connection\n");
       }
       break;
+    default:
+      infof(conn->data, "quiche says UNKNOWN\n");
+      break;
     }
 
     quiche_h3_event_free(ev);
   }
 
-  *curlcode = CURLE_OK;
+  *curlcode = (-1 == recvd)? CURLE_AGAIN : CURLE_OK;
+  infof(conn->data, "h3_stream_recv returns %zd\n", recvd);
   return recvd;
 }
 
@@ -334,7 +376,7 @@ static ssize_t h3_stream_send(struct connectdata *conn,
       *curlcode = CURLE_SEND_ERROR;
       return -1;
     }
-    return len;
+    sent = len;
   }
   else {
     sent = quiche_conn_stream_send(qs->conn, 0, mem, len, true);
@@ -362,6 +404,14 @@ int Curl_quic_ver(char *p, size_t len)
   return msnprintf(p, len, " quiche");
 }
 
+#ifdef DEBUG_HTTP3
+static void debug_log(const char *line, void *argp)
+{
+  (void)argp;
+  fprintf(stderr, "%s\n", line);
+}
+#endif
+
 /* Index where :authority header field will appear in request header
    field list. */
 #define AUTHORITY_DST_IDX 3
@@ -381,6 +431,10 @@ static CURLcode http_request(struct connectdata *conn, 
const void *mem,
   quiche_h3_header *nva = NULL;
   struct quicsocket *qs = &conn->quic;
 
+#ifdef DEBUG_HTTP3
+  quiche_enable_debug_logging(debug_log, NULL);
+#endif
+
   qs->config = quiche_h3_config_new(0, 1024, 0, 0);
   /* TODO: handle failure */
 

-- 
To stop receiving notification emails like this one, please contact
address@hidden.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]