summaryrefslogtreecommitdiff
path: root/lib/vquic
diff options
context:
space:
mode:
authorStefan Eissing <stefan@eissing.org>2023-04-14 11:38:14 +0200
committerDaniel Stenberg <daniel@haxx.se>2023-04-17 17:27:49 +0200
commitfc2f1e547a4a4b4bec5fd3c8bfde5136706488a1 (patch)
tree3d3194251c317144d529b35bb928567a84a9b57b /lib/vquic
parentfb1d62ff0736961032a489a0a8bff4b79b13eccb (diff)
downloadcurl-fc2f1e547a4a4b4bec5fd3c8bfde5136706488a1.tar.gz
http2: support HTTP/2 to forward proxies, non-tunneling
- with `--proxy-http2` allow h2 ALPN negotiation to forward proxies - applies to http: requests against a https: proxy only, as https: requests will auto-tunnel - adding a HTTP/1 request parser in http1.c - removed h2h3.c - using new request parser in nghttp2 and all h3 backends - adding test 2603 for request parser - adding h2 proxy test cases to test_10_* scorecard.py: request scoring accidentally always run curl with '-v'. Removed that, expect double numbers. labeller: added http1.* and h2-proxy sources to detection Closes #10967
Diffstat (limited to 'lib/vquic')
-rw-r--r--lib/vquic/curl_msh3.c66
-rw-r--r--lib/vquic/curl_ngtcp2.c95
-rw-r--r--lib/vquic/curl_quiche.c84
3 files changed, 151 insertions, 94 deletions
diff --git a/lib/vquic/curl_msh3.c b/lib/vquic/curl_msh3.c
index 1c35291d8..1e1a15a8f 100644
--- a/lib/vquic/curl_msh3.c
+++ b/lib/vquic/curl_msh3.c
@@ -35,7 +35,7 @@
#include "cf-socket.h"
#include "connect.h"
#include "progress.h"
-#include "h2h3.h"
+#include "http1.h"
#include "curl_msh3.h"
#include "socketpair.h"
#include "vquic/vquic.h"
@@ -321,7 +321,7 @@ static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
msh3_lock_acquire(&stream->recv_lock);
if((hd->NameLength == 7) &&
- !strncmp(H2H3_PSEUDO_STATUS, (char *)hd->Name, 7)) {
+ !strncmp(HTTP_PSEUDO_STATUS, (char *)hd->Name, 7)) {
char line[14]; /* status line is always 13 characters long */
size_t ncopy;
@@ -548,36 +548,75 @@ static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data,
{
struct cf_msh3_ctx *ctx = cf->ctx;
struct stream_ctx *stream = H3_STREAM_CTX(data);
- struct h2h3req *hreq;
- size_t hdrlen = 0;
+ struct h1_req_parser h1;
+ struct dynhds h2_headers;
+ MSH3_HEADER *nva = NULL;
+ size_t nheader, i;
ssize_t nwritten = -1;
struct cf_call_data save;
+ bool eos;
CF_DATA_SAVE(save, cf, data);
+ Curl_h1_req_parse_init(&h1, (4*1024));
+ Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST);
+
/* Sizes must match for cast below to work" */
DEBUGASSERT(stream);
- DEBUGASSERT(sizeof(MSH3_HEADER) == sizeof(struct h2h3pseudo));
DEBUGF(LOG_CF(data, cf, "req: send %zu bytes", len));
if(!stream->req) {
/* The first send on the request contains the headers and possibly some
data. Parse out the headers and create the request, then if there is
any data left over go ahead and send it too. */
+ nwritten = Curl_h1_req_parse_read(&h1, buf, len, NULL, 0, err);
+ if(nwritten < 0)
+ goto out;
+ DEBUGASSERT(h1.done);
+ DEBUGASSERT(h1.req);
- *err = Curl_pseudo_headers(data, buf, len, &hdrlen, &hreq);
+ *err = Curl_http_req_to_h2(&h2_headers, h1.req, data);
if(*err) {
- failf(data, "Curl_pseudo_headers failed");
- *err = CURLE_SEND_ERROR;
+ nwritten = -1;
+ goto out;
+ }
+
+ nheader = Curl_dynhds_count(&h2_headers);
+ nva = malloc(sizeof(MSH3_HEADER) * nheader);
+ if(!nva) {
+ *err = CURLE_OUT_OF_MEMORY;
+ nwritten = -1;
goto out;
}
- DEBUGF(LOG_CF(data, cf, "req: send %zu headers", hreq->entries));
+ for(i = 0; i < nheader; ++i) {
+ struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i);
+ nva[i].Name = e->name;
+ nva[i].NameLength = e->namelen;
+ nva[i].Value = e->value;
+ nva[i].ValueLength = e->valuelen;
+ }
+
+ switch(data->state.httpreq) {
+ case HTTPREQ_POST:
+ case HTTPREQ_POST_FORM:
+ case HTTPREQ_POST_MIME:
+ case HTTPREQ_PUT:
+ /* known request body size or -1 */
+ eos = FALSE;
+ break;
+ default:
+ /* there is not request body */
+ eos = TRUE;
+ stream->upload_done = TRUE;
+ break;
+ }
+
+ DEBUGF(LOG_CF(data, cf, "req: send %zu headers", nheader));
stream->req = MsH3RequestOpen(ctx->qconn, &msh3_request_if, data,
- (MSH3_HEADER*)hreq->header, hreq->entries,
- hdrlen == len ? MSH3_REQUEST_FLAG_FIN :
+ nva, nheader,
+ eos ? MSH3_REQUEST_FLAG_FIN :
MSH3_REQUEST_FLAG_NONE);
- Curl_pseudo_free(hreq);
if(!stream->req) {
failf(data, "request open failed");
*err = CURLE_SEND_ERROR;
@@ -608,6 +647,9 @@ static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data,
out:
set_quic_expire(cf, data);
+ free(nva);
+ Curl_h1_req_parse_free(&h1);
+ Curl_dynhds_free(&h2_headers);
CF_DATA_RESTORE(cf, save);
return nwritten;
}
diff --git a/lib/vquic/curl_ngtcp2.c b/lib/vquic/curl_ngtcp2.c
index 47b66efa5..9c0c223b4 100644
--- a/lib/vquic/curl_ngtcp2.c
+++ b/lib/vquic/curl_ngtcp2.c
@@ -56,10 +56,10 @@
#include "progress.h"
#include "strerror.h"
#include "dynbuf.h"
+#include "http1.h"
#include "select.h"
#include "vquic.h"
#include "vquic_int.h"
-#include "h2h3.h"
#include "vtls/keylog.h"
#include "vtls/vtls.h"
#include "curl_ngtcp2.h"
@@ -989,8 +989,8 @@ static int cf_ngtcp2_get_select_socks(struct Curl_cfilter *cf,
stream && nghttp3_conn_is_stream_writable(ctx->h3conn, stream->id))
rv |= GETSOCK_WRITESOCK(0);
- DEBUGF(LOG_CF(data, cf, "get_select_socks -> %x (sock=%d)",
- rv, (int)socks[0]));
+ /* DEBUGF(LOG_CF(data, cf, "get_select_socks -> %x (sock=%d)",
+ rv, (int)socks[0])); */
CF_DATA_RESTORE(cf, save);
return rv;
}
@@ -1540,49 +1540,65 @@ cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id,
field list. */
#define AUTHORITY_DST_IDX 3
-static CURLcode h3_stream_open(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const void *mem,
- size_t len)
+static ssize_t h3_stream_open(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const void *buf, size_t len,
+ CURLcode *err)
{
struct cf_ngtcp2_ctx *ctx = cf->ctx;
struct stream_ctx *stream = NULL;
+ struct h1_req_parser h1;
+ struct dynhds h2_headers;
size_t nheader;
- CURLcode result = CURLE_OK;
nghttp3_nv *nva = NULL;
int rc = 0;
unsigned int i;
- struct h2h3req *hreq = NULL;
+ ssize_t nwritten = -1;
nghttp3_data_reader reader;
nghttp3_data_reader *preader = NULL;
- result = h3_data_setup(cf, data);
- if(result)
+ Curl_h1_req_parse_init(&h1, (4*1024));
+ Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST);
+
+ *err = h3_data_setup(cf, data);
+ if(*err)
goto out;
stream = H3_STREAM_CTX(data);
+ DEBUGASSERT(stream);
rc = ngtcp2_conn_open_bidi_stream(ctx->qconn, &stream->id, NULL);
if(rc) {
failf(data, "can get bidi streams");
+ *err = CURLE_SEND_ERROR;
goto out;
}
- result = Curl_pseudo_headers(data, mem, len, NULL, &hreq);
- if(result)
+ nwritten = Curl_h1_req_parse_read(&h1, buf, len, NULL, 0, err);
+ if(nwritten < 0)
goto out;
- nheader = hreq->entries;
+ DEBUGASSERT(h1.done);
+ DEBUGASSERT(h1.req);
+ *err = Curl_http_req_to_h2(&h2_headers, h1.req, data);
+ if(*err) {
+ nwritten = -1;
+ goto out;
+ }
+
+ nheader = Curl_dynhds_count(&h2_headers);
nva = malloc(sizeof(nghttp3_nv) * nheader);
if(!nva) {
- result = CURLE_OUT_OF_MEMORY;
+ *err = CURLE_OUT_OF_MEMORY;
+ nwritten = -1;
goto out;
}
- for(i = 0; i < nheader; i++) {
- nva[i].name = (unsigned char *)hreq->header[i].name;
- nva[i].namelen = hreq->header[i].namelen;
- nva[i].value = (unsigned char *)hreq->header[i].value;
- nva[i].valuelen = hreq->header[i].valuelen;
+ for(i = 0; i < nheader; ++i) {
+ struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i);
+ nva[i].name = (unsigned char *)e->name;
+ nva[i].namelen = e->namelen;
+ nva[i].value = (unsigned char *)e->value;
+ nva[i].valuelen = e->valuelen;
nva[i].flags = NGHTTP3_NV_FLAG_NONE;
}
@@ -1604,32 +1620,32 @@ static CURLcode h3_stream_open(struct Curl_cfilter *cf,
rc = nghttp3_conn_submit_request(ctx->h3conn, stream->id,
nva, nheader, preader, data);
- if(rc)
- goto out;
-
- infof(data, "Using HTTP/3 Stream ID: %" PRId64 " (easy handle %p)",
- stream->id, (void *)data);
- DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] opened for %s",
- stream->id, data->state.url));
-
-out:
- if(stream && !result && rc) {
+ if(rc) {
switch(rc) {
case NGHTTP3_ERR_CONN_CLOSING:
DEBUGF(LOG_CF(data, cf, "h3sid[%"PRId64"] failed to send, "
"connection is closing", stream->id));
- result = CURLE_RECV_ERROR;
break;
default:
DEBUGF(LOG_CF(data, cf, "h3sid[%"PRId64"] failed to send -> %d (%s)",
stream->id, rc, ngtcp2_strerror(rc)));
- result = CURLE_SEND_ERROR;
break;
}
+ *err = CURLE_SEND_ERROR;
+ nwritten = -1;
+ goto out;
}
+
+ infof(data, "Using HTTP/3 Stream ID: %" PRId64 " (easy handle %p)",
+ stream->id, (void *)data);
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] opened for %s",
+ stream->id, data->state.url));
+
+out:
free(nva);
- Curl_pseudo_free(hreq);
- return result;
+ Curl_h1_req_parse_free(&h1);
+ Curl_dynhds_free(&h2_headers);
+ return nwritten;
}
static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
@@ -1653,16 +1669,11 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
}
if(!stream || stream->id < 0) {
- CURLcode result = h3_stream_open(cf, data, buf, len);
- if(result) {
- DEBUGF(LOG_CF(data, cf, "failed to open stream -> %d", result));
- sent = -1;
+ sent = h3_stream_open(cf, data, buf, len, err);
+ if(sent < 0) {
+ DEBUGF(LOG_CF(data, cf, "failed to open stream -> %d", *err));
goto out;
}
- /* Assume that mem of length len only includes HTTP/1.1 style
- header fields. In other words, it does not contain request
- body. */
- sent = len;
}
else {
sent = Curl_bufq_write(&stream->sendbuf, buf, len, err);
diff --git a/lib/vquic/curl_quiche.c b/lib/vquic/curl_quiche.c
index 31a7174bf..eee2fe248 100644
--- a/lib/vquic/curl_quiche.c
+++ b/lib/vquic/curl_quiche.c
@@ -40,11 +40,11 @@
#include "connect.h"
#include "progress.h"
#include "strerror.h"
+#include "http1.h"
#include "vquic.h"
#include "vquic_int.h"
#include "curl_quiche.h"
#include "transfer.h"
-#include "h2h3.h"
#include "vtls/openssl.h"
#include "vtls/keylog.h"
@@ -187,7 +187,6 @@ static void cf_quiche_ctx_clear(struct cf_quiche_ctx *ctx)
struct stream_ctx {
int64_t id; /* HTTP/3 protocol stream identifier */
struct bufq recvbuf; /* h3 response */
- size_t req_hds_len; /* how many bytes in the first send are headers */
uint64_t error3; /* HTTP/3 stream error code */
bool closed; /* TRUE on stream close */
bool reset; /* TRUE on stream reset */
@@ -373,7 +372,7 @@ static int cb_each_header(uint8_t *name, size_t name_len,
CURLcode result;
(void)stream;
- if((name_len == 7) && !strncmp(H2H3_PSEUDO_STATUS, (char *)name, 7)) {
+ if((name_len == 7) && !strncmp(HTTP_PSEUDO_STATUS, (char *)name, 7)) {
result = write_resp_raw(x->cf, x->data, "HTTP/3 ", sizeof("HTTP/3 ") - 1);
if(!result)
result = write_resp_raw(x->cf, x->data, value, value_len);
@@ -876,52 +875,58 @@ out:
static ssize_t h3_open_stream(struct Curl_cfilter *cf,
struct Curl_easy *data,
- const void *mem, size_t len,
+ const void *buf, size_t len,
CURLcode *err)
{
struct cf_quiche_ctx *ctx = cf->ctx;
struct stream_ctx *stream = H3_STREAM_CTX(data);
- size_t nheader;
+ size_t nheader, i;
int64_t stream3_id;
+ struct h1_req_parser h1;
+ struct dynhds h2_headers;
quiche_h3_header *nva = NULL;
- struct h2h3req *hreq = NULL;
+ ssize_t nwritten;
if(!stream) {
*err = h3_data_setup(cf, data);
- if(*err)
- goto fail;
+ if(*err) {
+ nwritten = -1;
+ goto out;
+ }
stream = H3_STREAM_CTX(data);
DEBUGASSERT(stream);
}
- if(!stream->req_hds_len) {
- stream->req_hds_len = len; /* fist call */
- }
- else {
- /* subsequent attempt, we should get at least as many bytes as
- * in the first call as headers are either completely sent or not
- * at all. */
- DEBUGASSERT(stream->req_hds_len <= len);
- }
+ Curl_h1_req_parse_init(&h1, (4*1024));
+ Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST);
- *err = Curl_pseudo_headers(data, mem, stream->req_hds_len, NULL, &hreq);
- if(*err)
- goto fail;
- nheader = hreq->entries;
+ DEBUGASSERT(stream);
+ nwritten = Curl_h1_req_parse_read(&h1, buf, len, NULL, 0, err);
+ if(nwritten < 0)
+ goto out;
+ DEBUGASSERT(h1.done);
+ DEBUGASSERT(h1.req);
+
+ *err = Curl_http_req_to_h2(&h2_headers, h1.req, data);
+ if(*err) {
+ nwritten = -1;
+ goto out;
+ }
+ nheader = Curl_dynhds_count(&h2_headers);
nva = malloc(sizeof(quiche_h3_header) * nheader);
if(!nva) {
*err = CURLE_OUT_OF_MEMORY;
- goto fail;
+ nwritten = -1;
+ goto out;
}
- else {
- unsigned int i;
- for(i = 0; i < nheader; i++) {
- nva[i].name = (unsigned char *)hreq->header[i].name;
- nva[i].name_len = hreq->header[i].namelen;
- nva[i].value = (unsigned char *)hreq->header[i].value;
- nva[i].value_len = hreq->header[i].valuelen;
- }
+
+ for(i = 0; i < nheader; ++i) {
+ struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i);
+ nva[i].name = (unsigned char *)e->name;
+ nva[i].name_len = e->namelen;
+ nva[i].value = (unsigned char *)e->value;
+ nva[i].value_len = e->valuelen;
}
switch(data->state.httpreq) {
@@ -950,17 +955,20 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf,
data->state.url));
stream_send_suspend(cf, data);
*err = CURLE_AGAIN;
- goto fail;
+ nwritten = -1;
+ goto out;
}
else {
DEBUGF(LOG_CF(data, cf, "send_request(%s) -> %" PRId64,
data->state.url, stream3_id));
}
*err = CURLE_SEND_ERROR;
- goto fail;
+ nwritten = -1;
+ goto out;
}
DEBUGASSERT(stream->id == -1);
+ *err = CURLE_OK;
stream->id = stream3_id;
stream->closed = FALSE;
stream->reset = FALSE;
@@ -970,15 +978,11 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf,
DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] opened for %s",
stream3_id, data->state.url));
- Curl_pseudo_free(hreq);
- free(nva);
- *err = CURLE_OK;
- return stream->req_hds_len;
-
-fail:
+out:
free(nva);
- Curl_pseudo_free(hreq);
- return -1;
+ Curl_h1_req_parse_free(&h1);
+ Curl_dynhds_free(&h2_headers);
+ return nwritten;
}
static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data,