From 368d444cc6d46d18747197d30dc483e93d53d8b7 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 12 Aug 2019 11:10:56 +0200 Subject: ngtcp2: initial h3 request work Closes #4217 --- lib/http.h | 8 ++-- lib/vquic/ngtcp2.c | 132 ++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 116 insertions(+), 24 deletions(-) diff --git a/lib/http.h b/lib/http.h index c27725d2a..945aceb56 100644 --- a/lib/http.h +++ b/lib/http.h @@ -174,14 +174,16 @@ struct HTTP { size_t pauselen; /* the number of bytes left in data */ bool closed; /* TRUE on HTTP2 stream close */ bool close_handled; /* TRUE if stream closure is handled by libcurl */ - char *mem; /* points to a buffer in memory to store received data */ - size_t len; /* size of the buffer 'mem' points to */ - size_t memlen; /* size of data copied to mem */ 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(USE_NGHTTP3) + char *mem; /* points to a buffer in memory to store received data */ + size_t len; /* size of the buffer 'mem' points to */ + size_t memlen; /* size of data copied to mem */ +#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 */ diff --git a/lib/vquic/ngtcp2.c b/lib/vquic/ngtcp2.c index c7a079817..fe8fe8ced 100644 --- a/lib/vquic/ngtcp2.c +++ b/lib/vquic/ngtcp2.c @@ -42,6 +42,7 @@ #include "curl_memory.h" #include "memdebug.h" +/* #define DEBUG_NGTCP2 1 */ #define DEBUG_HTTP3 #ifdef DEBUG_HTTP3 #define H3BUGF(x) x @@ -67,6 +68,7 @@ static ngtcp2_tstamp timestamp(void) return ct.tv_sec * NGTCP2_SECONDS + ct.tv_usec * NGTCP2_MICROSECONDS; } +#ifdef DEBUG_NGTCP2 static void quic_printf(void *user_data, const char *fmt, ...) { va_list ap; @@ -76,6 +78,7 @@ static void quic_printf(void *user_data, const char *fmt, ...) va_end(ap); fprintf(stderr, "\n"); } +#endif static int setup_initial_crypto_context(struct connectdata *conn) { @@ -169,7 +172,11 @@ static int setup_initial_crypto_context(struct connectdata *conn) static void quic_settings(ngtcp2_settings *s) { ngtcp2_settings_default(s); +#ifdef DEBUG_NGTCP2 s->log_printf = quic_printf; +#else + s->log_printf = NULL; +#endif s->initial_ts = timestamp(); s->max_stream_data_bidi_local = QUIC_MAX_STREAMS; s->max_stream_data_bidi_remote = QUIC_MAX_STREAMS; @@ -1137,7 +1144,8 @@ CURLcode Curl_quic_connect(struct connectdata *conn, */ int Curl_quic_ver(char *p, size_t len) { - return msnprintf(p, len, " ngtcp2/blabla nghttp3/bloblo"); + return msnprintf(p, len, " ngtcp2/%s nghttp3/%s", + NGTCP2_VERSION, NGHTTP3_VERSION); } static int ng_getsock(struct connectdata *conn, curl_socket_t *socks) @@ -1215,16 +1223,27 @@ static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id, } static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream_id, - const uint8_t *data, size_t datalen, + const uint8_t *buf, size_t buflen, void *user_data, void *stream_user_data) { + size_t ncopy; + struct Curl_easy *data = stream_user_data; + struct HTTP *stream = data->req.protop; (void)conn; (void)stream_id; - (void)data; - (void)datalen; (void)user_data; - (void)stream_user_data; - fprintf(stderr, "cb_h3_recv_data CALLED\n"); + fprintf(stderr, "cb_h3_recv_data CALLED with %d bytes\n", + buflen); + + /* TODO: this needs to be handled properly */ + DEBUGASSERT(buflen <= stream->len); + + ncopy = CURLMIN(stream->len, buflen); + memcpy(stream->mem, buf, ncopy); + stream->len -= ncopy; + stream->memlen += ncopy; + stream->mem += ncopy; + return 0; } @@ -1241,14 +1260,48 @@ static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream_id, return 0; } -static int cb_h3_begin_headers(nghttp3_conn *conn, int64_t stream_id, - void *user_data, void *stream_user_data) +/* Decode HTTP status code. Returns -1 if no valid status code was + decoded. (duplicate from http2.c) */ +static int decode_status_code(const uint8_t *value, size_t len) +{ + int i; + int res; + + if(len != 3) { + return -1; + } + + res = 0; + + for(i = 0; i < 3; ++i) { + char c = value[i]; + + if(c < '0' || c > '9') { + return -1; + } + + res *= 10; + res += c - '0'; + } + + return res; +} + +static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id, + void *user_data, void *stream_user_data) { + struct Curl_easy *data = stream_user_data; + struct HTTP *stream = data->req.protop; (void)conn; (void)stream_id; (void)user_data; - (void)stream_user_data; - fprintf(stderr, "cb_h3_begin_headers CALLED\n"); + + if(stream->memlen >= 2) { + memcpy(stream->mem, "\r\n", 2); + stream->len -= 2; + stream->memlen += 2; + stream->mem += 2; + } return 0; } @@ -1257,15 +1310,33 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id, nghttp3_rcbuf *value, uint8_t flags, void *user_data, void *stream_user_data) { + nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name); + nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value); + struct Curl_easy *data = stream_user_data; + struct HTTP *stream = data->req.protop; + size_t ncopy; (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"); + + if(h3name.len == sizeof(":status") - 1 && + !memcmp(":status", h3name.base, h3name.len)) { + int status = decode_status_code(h3val.base, h3val.len); + DEBUGASSERT(status != -1); + msnprintf(stream->mem, stream->len, "HTTP/3 %03d \r\n", status); + } + else { + /* store as a HTTP1-style header */ + msnprintf(stream->mem, stream->len, "%.*s: %.*s\n", + h3name.len, h3name.base, h3val.len, h3val.base); + } + + ncopy = strlen(stream->mem); + stream->len -= ncopy; + stream->memlen += ncopy; + stream->mem += ncopy; return 0; } @@ -1288,9 +1359,9 @@ static nghttp3_conn_callbacks ngh3_callbacks = { cb_h3_stream_close, cb_h3_recv_data, cb_h3_deferred_consume, - cb_h3_begin_headers, + NULL, /* begin_headers */ cb_h3_recv_header, - NULL, /* end_headers */ + cb_h3_end_headers, NULL, /* begin_trailers */ cb_h3_recv_header, NULL, /* end_trailers */ @@ -1373,8 +1444,15 @@ static ssize_t ngh3_stream_recv(struct connectdata *conn, CURLcode *curlcode) { curl_socket_t sockfd = conn->sock[sockindex]; - (void)buf; - (void)buffersize; + struct HTTP *stream = conn->data->req.protop; + + fprintf(stderr, "ngh3_stream_recv CALLED (easy %p)\n", conn->data); + + /* remember where to store incoming data for this stream and how big the + buffer is */ + stream->mem = buf; + stream->len = buffersize; + stream->memlen = 0; if(process_ingress(conn, sockfd)) { infof(conn->data, "ngh3_stream_recv returns on ingress\n"); @@ -1386,8 +1464,16 @@ static ssize_t ngh3_stream_recv(struct connectdata *conn, return -1; } - *curlcode = CURLE_AGAIN; + if(stream->memlen) { + /* data arrived */ + *curlcode = CURLE_OK; + infof(conn->data, "ngh3_stream_recv returns %zd bytes\n", + stream->memlen); + return stream->memlen; + } + infof(conn->data, "ngh3_stream_recv returns 0 bytes and EAGAIN\n"); + *curlcode = CURLE_AGAIN; return -1; } @@ -1398,8 +1484,6 @@ static ssize_t ngh3_stream_recv(struct connectdata *conn, 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; @@ -1582,6 +1666,12 @@ static CURLcode http_request(struct connectdata *conn, const void *mem, } } + stream->header_recvbuf = Curl_add_buffer_init(); + if(!stream->header_recvbuf) { + result = CURLE_OUT_OF_MEMORY; + goto fail; + } + switch(data->set.httpreq) { case HTTPREQ_POST: case HTTPREQ_POST_FORM: -- cgit v1.2.1