gnunet-svn
[Top][All Lists]
Advanced

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

[GNUnet-SVN] [gnurl] 105/220: nghttp3: initial h3 template code added


From: gnunet
Subject: [GNUnet-SVN] [gnurl] 105/220: nghttp3: initial h3 template code added
Date: Thu, 12 Sep 2019 17:27:45 +0200

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

ng0 pushed a commit to branch master
in repository gnurl.

commit 102ebe0459dedf074c6c851d1537baccb090ced2
Author: Daniel Stenberg <address@hidden>
AuthorDate: Sun Aug 11 23:50:11 2019 +0200

    nghttp3: initial h3 template code added
---
 lib/vquic/ngtcp2.c | 498 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 lib/vquic/ngtcp2.h |   4 +
 2 files changed, 501 insertions(+), 1 deletion(-)

diff --git a/lib/vquic/ngtcp2.c b/lib/vquic/ngtcp2.c
index 3669c714b..71ace34ad 100644
--- a/lib/vquic/ngtcp2.c
+++ b/lib/vquic/ngtcp2.c
@@ -24,6 +24,7 @@
 
 #ifdef USE_NGTCP2
 #include <ngtcp2/ngtcp2.h>
+#include <nghttp3/nghttp3.h>
 #include <openssl/err.h>
 #include "urldata.h"
 #include "sendf.h"
@@ -32,12 +33,22 @@
 #include "ngtcp2.h"
 #include "ngtcp2-crypto.h"
 #include "multiif.h"
+#include "strcase.h"
+#include "connect.h"
+#include "strerror.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
 #include "memdebug.h"
 
+#define DEBUG_HTTP3
+#ifdef DEBUG_HTTP3
+#define H3BUGF(x) x
+#else
+#define H3BUGF(x) do { } WHILE_FALSE
+#endif
+
 #define QUIC_MAX_STREAMS (256*1024)
 #define QUIC_MAX_DATA (1*1024*1024)
 #define QUIC_IDLE_TIMEOUT 60000 /* milliseconds */
@@ -1058,6 +1069,490 @@ int Curl_quic_ver(char *p, size_t len)
   return msnprintf(p, len, " ngtcp2/blabla nghttp3/bloblo");
 }
 
+static int ng_getsock(struct connectdata *conn, curl_socket_t *socks)
+{
+  struct SingleRequest *k = &conn->data->req;
+  int bitmap = GETSOCK_BLANK;
+
+  socks[0] = conn->sock[FIRSTSOCKET];
+
+  /* in a HTTP/2 connection we can basically always get a frame so we should
+     always be ready for one */
+  bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
+
+  /* we're still uploading or the HTTP/2 layer wants to send data */
+  if((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND)
+    bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
+
+  return bitmap;
+}
+
+static int ng_perform_getsock(const struct connectdata *conn,
+                              curl_socket_t *socks)
+{
+  return ng_getsock((struct connectdata *)conn, socks);
+}
+
+static CURLcode ng_disconnect(struct connectdata *conn,
+                                  bool dead_connection)
+{
+  (void)conn;
+  (void)dead_connection;
+  return CURLE_OK;
+}
+
+static unsigned int ng_conncheck(struct connectdata *conn,
+                                 unsigned int checks_to_perform)
+{
+  (void)conn;
+  (void)checks_to_perform;
+  return CONNRESULT_NONE;
+}
+
+static const struct Curl_handler Curl_handler_h3_quiche = {
+  "HTTPS",                              /* scheme */
+  ZERO_NULL,                            /* setup_connection */
+  Curl_http,                            /* do_it */
+  Curl_http_done,                       /* done */
+  ZERO_NULL,                            /* do_more */
+  ZERO_NULL,                            /* connect_it */
+  ZERO_NULL,                            /* connecting */
+  ZERO_NULL,                            /* doing */
+  ng_getsock,                           /* proto_getsock */
+  ng_getsock,                           /* doing_getsock */
+  ZERO_NULL,                            /* domore_getsock */
+  ng_perform_getsock,                   /* perform_getsock */
+  ng_disconnect,                        /* disconnect */
+  ZERO_NULL,                            /* readwrite */
+  ng_conncheck,                         /* connection_check */
+  PORT_HTTP,                            /* defport */
+  CURLPROTO_HTTPS,                      /* protocol */
+  PROTOPT_SSL | PROTOPT_STREAM          /* flags */
+};
+
+static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id,
+                              uint64_t app_error_code, void *user_data,
+                              void *stream_user_data)
+{
+  (void)conn;
+  (void)stream_id;
+  (void)app_error_code;
+  (void)user_data;
+  (void)stream_user_data;
+  fprintf(stderr, "cb_h3_stream_close CALLED\n");
+  return 0;
+}
+
+static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream_id,
+                           const uint8_t *data, size_t datalen,
+                           void *user_data, void *stream_user_data)
+{
+  (void)conn;
+  (void)stream_id;
+  (void)data;
+  (void)datalen;
+  (void)user_data;
+  (void)stream_user_data;
+  fprintf(stderr, "cb_h3_recv_data CALLED\n");
+  return 0;
+}
+
+static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream_id,
+                                  size_t consumed, void *user_data,
+                                  void *stream_user_data)
+{
+  (void)conn;
+  (void)stream_id;
+  (void)consumed;
+  (void)user_data;
+  (void)stream_user_data;
+  fprintf(stderr, "cb_h3_deferred_consume CALLED\n");
+  return 0;
+}
+
+static int cb_h3_begin_headers(nghttp3_conn *conn, int64_t stream_id,
+                               void *user_data, void *stream_user_data)
+{
+  (void)conn;
+  (void)stream_id;
+  (void)user_data;
+  (void)stream_user_data;
+  fprintf(stderr, "cb_h3_begin_headers CALLED\n");
+  return 0;
+}
+
+static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id,
+                             int32_t token, nghttp3_rcbuf *name,
+                             nghttp3_rcbuf *value, uint8_t flags,
+                             void *user_data, void *stream_user_data)
+{
+  (void)conn;
+  (void)stream_id;
+  (void)token;
+  (void)name;
+  (void)value;
+  (void)flags;
+  (void)user_data;
+  (void)stream_user_data;
+  fprintf(stderr, "cb_h3_recv_header CALLED\n");
+  return 0;
+}
+
+static int cb_h3_send_stop_sending(nghttp3_conn *conn, int64_t stream_id,
+                                   uint64_t app_error_code,
+                                   void *user_data,
+                                   void *stream_user_data)
+{
+  (void)conn;
+  (void)stream_id;
+  (void)app_error_code;
+  (void)user_data;
+  (void)stream_user_data;
+  fprintf(stderr, "cb_h3_send_stop_sending CALLED\n");
+  return 0;
+}
+
+static nghttp3_conn_callbacks ngh3_callbacks = {
+  NULL, /* acked_stream_data */
+  cb_h3_stream_close,
+  cb_h3_recv_data,
+  cb_h3_deferred_consume,
+  cb_h3_begin_headers,
+  cb_h3_recv_header,
+  NULL, /* end_headers */
+  NULL, /* begin_trailers */
+  cb_h3_recv_header,
+  NULL, /* end_trailers */
+  NULL, /* http_begin_push_promise */
+  NULL, /* http_recv_push_promise */
+  NULL, /* http_end_push_promise */
+  NULL, /* http_cancel_push */
+  cb_h3_send_stop_sending,
+  NULL, /* push_stream */
+};
+
+static Curl_recv ngh3_stream_recv;
+static Curl_send ngh3_stream_send;
+
+static ssize_t ngh3_stream_recv(struct connectdata *conn,
+                                int sockindex,
+                                char *buf,
+                                size_t buffersize,
+                                CURLcode *curlcode)
+{
+  (void)conn;
+  (void)sockindex;
+  (void)buf;
+  (void)buffersize;
+  (void)curlcode;
+  return 0;
+}
+
+/* Index where :authority header field will appear in request header
+   field list. */
+#define AUTHORITY_DST_IDX 3
+
+static CURLcode http_request(struct connectdata *conn, const void *mem,
+                             size_t len)
+{
+  /*
+   */
+  struct HTTP *stream = conn->data->req.protop;
+  size_t nheader;
+  size_t i;
+  size_t authority_idx;
+  char *hdbuf = (char *)mem;
+  char *end, *line_end;
+  struct quicsocket *qs = &conn->quic;
+  CURLcode result = CURLE_OK;
+  struct Curl_easy *data = conn->data;
+  nghttp3_nv *nva = NULL;
+  int64_t stream3_id;
+  int rc;
+
+  rc = ngtcp2_conn_open_bidi_stream(qs->conn, &stream3_id, NULL);
+  if(rc) {
+    failf(conn->data, "can get bidi streams");
+    result = CURLE_SEND_ERROR;
+    goto fail;
+  }
+
+  stream->stream3_id = stream3_id;
+  stream->h3req = TRUE; /* senf off! */
+
+  /* Calculate number of headers contained in [mem, mem + len). Assumes a
+     correctly generated HTTP header field block. */
+  nheader = 0;
+  for(i = 1; i < len; ++i) {
+    if(hdbuf[i] == '\n' && hdbuf[i - 1] == '\r') {
+      ++nheader;
+      ++i;
+    }
+  }
+  if(nheader < 2)
+    goto fail;
+
+  /* We counted additional 2 \r\n in the first and last line. We need 3
+     new headers: :method, :path and :scheme. Therefore we need one
+     more space. */
+  nheader += 1;
+  nva = malloc(sizeof(nghttp3_nv) * nheader);
+  if(!nva) {
+    result = CURLE_OUT_OF_MEMORY;
+    goto fail;
+  }
+
+  /* Extract :method, :path from request line
+     We do line endings with CRLF so checking for CR is enough */
+  line_end = memchr(hdbuf, '\r', len);
+  if(!line_end) {
+    result = CURLE_BAD_FUNCTION_ARGUMENT; /* internal error */
+    goto fail;
+  }
+
+  /* Method does not contain spaces */
+  end = memchr(hdbuf, ' ', line_end - hdbuf);
+  if(!end || end == hdbuf)
+    goto fail;
+  nva[0].name = (unsigned char *)":method";
+  nva[0].namelen = strlen((char *)nva[0].name);
+  nva[0].value = (unsigned char *)hdbuf;
+  nva[0].valuelen = (size_t)(end - hdbuf);
+
+  hdbuf = end + 1;
+
+  /* Path may contain spaces so scan backwards */
+  end = NULL;
+  for(i = (size_t)(line_end - hdbuf); i; --i) {
+    if(hdbuf[i - 1] == ' ') {
+      end = &hdbuf[i - 1];
+      break;
+    }
+  }
+  if(!end || end == hdbuf)
+    goto fail;
+  nva[1].name = (unsigned char *)":path";
+  nva[1].namelen = strlen((char *)nva[1].name);
+  nva[1].value = (unsigned char *)hdbuf;
+  nva[1].valuelen = (size_t)(end - hdbuf);
+
+  nva[2].name = (unsigned char *)":scheme";
+  nva[2].namelen = strlen((char *)nva[2].name);
+  if(conn->handler->flags & PROTOPT_SSL)
+    nva[2].value = (unsigned char *)"https";
+  else
+    nva[2].value = (unsigned char *)"http";
+  nva[2].valuelen = strlen((char *)nva[2].value);
+
+
+  authority_idx = 0;
+  i = 3;
+  while(i < nheader) {
+    size_t hlen;
+
+    hdbuf = line_end + 2;
+
+    /* check for next CR, but only within the piece of data left in the given
+       buffer */
+    line_end = memchr(hdbuf, '\r', len - (hdbuf - (char *)mem));
+    if(!line_end || (line_end == hdbuf))
+      goto fail;
+
+    /* header continuation lines are not supported */
+    if(*hdbuf == ' ' || *hdbuf == '\t')
+      goto fail;
+
+    for(end = hdbuf; end < line_end && *end != ':'; ++end)
+      ;
+    if(end == hdbuf || end == line_end)
+      goto fail;
+    hlen = end - hdbuf;
+
+    if(hlen == 4 && strncasecompare("host", hdbuf, 4)) {
+      authority_idx = i;
+      nva[i].name = (unsigned char *)":authority";
+      nva[i].namelen = strlen((char *)nva[i].name);
+    }
+    else {
+      nva[i].name = (unsigned char *)hdbuf;
+      nva[i].namelen = (size_t)(end - hdbuf);
+    }
+    hdbuf = end + 1;
+    while(*hdbuf == ' ' || *hdbuf == '\t')
+      ++hdbuf;
+    end = line_end;
+
+#if 0 /* This should probably go in more or less like this */
+    switch(inspect_header((const char *)nva[i].name, nva[i].namelen, hdbuf,
+                          end - hdbuf)) {
+    case HEADERINST_IGNORE:
+      /* skip header fields prohibited by HTTP/2 specification. */
+      --nheader;
+      continue;
+    case HEADERINST_TE_TRAILERS:
+      nva[i].value = (uint8_t*)"trailers";
+      nva[i].value_len = sizeof("trailers") - 1;
+      break;
+    default:
+      nva[i].value = (unsigned char *)hdbuf;
+      nva[i].value_len = (size_t)(end - hdbuf);
+    }
+#endif
+    nva[i].value = (unsigned char *)hdbuf;
+    nva[i].valuelen = (size_t)(end - hdbuf);
+
+    ++i;
+  }
+
+  /* :authority must come before non-pseudo header fields */
+  if(authority_idx != 0 && authority_idx != AUTHORITY_DST_IDX) {
+    nghttp3_nv authority = nva[authority_idx];
+    for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) {
+      nva[i] = nva[i - 1];
+    }
+    nva[i] = authority;
+  }
+
+  /* Warn stream may be rejected if cumulative length of headers is too
+     large. */
+#define MAX_ACC 60000  /* <64KB to account for some overhead */
+  {
+    size_t acc = 0;
+
+    for(i = 0; i < nheader; ++i) {
+      acc += nva[i].namelen + nva[i].valuelen;
+
+      H3BUGF(infof(data, "h3 [%.*s: %.*s]\n",
+                   nva[i].namelen, nva[i].name,
+                   nva[i].valuelen, nva[i].value));
+    }
+
+    if(acc > MAX_ACC) {
+      infof(data, "http_request: Warning: The cumulative length of all "
+            "headers exceeds %zu bytes and that could cause the "
+            "stream to be rejected.\n", MAX_ACC);
+    }
+  }
+
+  switch(data->set.httpreq) {
+  case HTTPREQ_POST:
+  case HTTPREQ_POST_FORM:
+  case HTTPREQ_POST_MIME:
+  case HTTPREQ_PUT:
+    if(data->state.infilesize != -1)
+      stream->upload_left = data->state.infilesize;
+    else
+      /* data sending without specifying the data amount up front */
+      stream->upload_left = -1; /* unknown, but not zero */
+
+#if 0
+    stream3_id = quiche_h3_send_request(qs->h3c, qs->conn, nva, nheader,
+                                        stream->upload_left ? FALSE: TRUE);
+    if((stream3_id >= 0) && data->set.postfields) {
+      ssize_t sent = quiche_h3_send_body(qs->h3c, qs->conn, stream3_id,
+                                         (uint8_t *)data->set.postfields,
+                                         stream->upload_left, TRUE);
+      if(sent <= 0) {
+        failf(data, "quiche_h3_send_body failed!");
+        result = CURLE_SEND_ERROR;
+      }
+      stream->upload_left = 0; /* nothing left to send */
+    }
+#endif
+    break;
+  default:
+    rc = nghttp3_conn_submit_request(qs->h3conn, stream->stream3_id,
+                                     nva, nheader,
+                                     NULL, /* no body! */
+                                     conn->data);
+    if(rc) {
+      result = CURLE_SEND_ERROR;
+      goto fail;
+    }
+    break;
+  }
+
+  Curl_safefree(nva);
+
+  infof(data, "Using HTTP/3 Stream ID: %x (easy handle %p)\n",
+        stream3_id, (void *)data);
+
+  return CURLE_OK;
+
+fail:
+  free(nva);
+  return result;
+}
+static ssize_t ngh3_stream_send(struct connectdata *conn,
+                                int sockindex,
+                                const void *mem,
+                                size_t len,
+                                CURLcode *curlcode)
+{
+  ssize_t sent;
+  struct quicsocket *qs = &conn->quic;
+  curl_socket_t sockfd = conn->sock[sockindex];
+  struct HTTP *stream = conn->data->req.protop;
+
+  if(!stream->h3req) {
+    CURLcode result = http_request(conn, mem, len);
+    if(result) {
+      *curlcode = CURLE_SEND_ERROR;
+      return -1;
+    }
+    sent = len;
+  }
+  else {
+    (void)qs;
+    /* TODO */
+  }
+
+  if(flush_egress(conn, sockfd)) {
+    *curlcode = CURLE_SEND_ERROR;
+    return -1;
+  }
+
+  *curlcode = CURLE_OK;
+  return sent;
+}
+
+static CURLcode ng_has_connected(struct connectdata *conn,
+                                   int sockindex)
+{
+  CURLcode result;
+  struct quicsocket *qs = &conn->quic;
+  int rc;
+
+  conn->recv[sockindex] = ngh3_stream_recv;
+  conn->send[sockindex] = ngh3_stream_send;
+  conn->handler = &Curl_handler_h3_quiche;
+  conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
+  conn->httpversion = 30;
+  conn->bundle->multiuse = BUNDLE_MULTIPLEX;
+
+  if(ngtcp2_conn_get_max_local_streams_uni(qs->conn) < 3) {
+    failf(conn->data, "too few available QUIC streams");
+    return CURLE_SEND_ERROR;
+  }
+
+  nghttp3_conn_settings_default(&qs->h3settings);
+
+  rc = nghttp3_conn_client_new(&qs->h3conn,
+                               &ngh3_callbacks,
+                               &qs->h3settings,
+                               nghttp3_mem_default(),
+                               conn->data);
+  if(rc) {
+    result = CURLE_OUT_OF_MEMORY;
+    goto fail;
+  }
+
+  return CURLE_OK;
+  fail:
+
+  return result;
+}
+
 CURLcode Curl_quic_is_connected(struct connectdata *conn, int sockindex,
                                 bool *done)
 {
@@ -1075,10 +1570,11 @@ CURLcode Curl_quic_is_connected(struct connectdata 
*conn, int sockindex,
 
   if(ngtcp2_conn_get_handshake_completed(qs->conn)) {
     *done = TRUE;
+    result = ng_has_connected(conn, sockindex);
     DEBUGF(infof(conn->data, "ngtcp2 established connection!\n"));
   }
 
-  return CURLE_OK;
+  return result;
 }
 
 static CURLcode process_ingress(struct connectdata *conn, int sockfd)
diff --git a/lib/vquic/ngtcp2.h b/lib/vquic/ngtcp2.h
index 73008015f..da84793d1 100644
--- a/lib/vquic/ngtcp2.h
+++ b/lib/vquic/ngtcp2.h
@@ -27,6 +27,7 @@
 #ifdef USE_NGTCP2
 
 #include <ngtcp2/ngtcp2.h>
+#include <nghttp3/nghttp3.h>
 #include <openssl/ssl.h>
 #include "ngtcp2-crypto.h"
 
@@ -56,6 +57,9 @@ struct quicsocket {
   ngtcp2_crypto_level rx_crypto_level;
   struct sockaddr_storage local_addr;
   socklen_t local_addrlen;
+
+  nghttp3_conn *h3conn;
+  nghttp3_conn_settings h3settings;
 };
 
 #include "urldata.h"

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



reply via email to

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