Skip to content

Commit

Permalink
Keep separate http implementation contexts for http/2 and http/3
Browse files Browse the repository at this point in the history
- a single context can lead to confusion when a transfer either
  switches http version on the fly (upgrade) or in involved in
  eyeballing where more than one version is active
  • Loading branch information
icing committed Apr 3, 2023
1 parent 770bf3f commit 83d334f
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 40 deletions.
3 changes: 2 additions & 1 deletion lib/http.h
Expand Up @@ -217,7 +217,8 @@ struct HTTP {
#endif

#ifndef CURL_DISABLE_HTTP
void *impl_ctx; /* local data for HTTP implementations */
void *h2_ctx; /* HTTP/2 implementation context */
void *h3_ctx; /* HTTP/3 implementation context */
struct dynbuf send_buffer; /* used if the request couldn't be sent in one
chunk, points to an allocated send_buffer
struct */
Expand Down
52 changes: 30 additions & 22 deletions lib/http2.c
Expand Up @@ -208,9 +208,9 @@ struct stream_ctx {
};

#define H2_STREAM_CTX(d) ((struct stream_ctx *)(((d) && (d)->req.p.http)? \
((struct HTTP *)(d)->req.p.http)->impl_ctx \
((struct HTTP *)(d)->req.p.http)->h2_ctx \
: NULL))
#define H2_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->impl_ctx
#define H2_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->h2_ctx
#define H2_STREAM_ID(d) (H2_STREAM_CTX(d)? \
H2_STREAM_CTX(d)->id : -2)

Expand All @@ -221,6 +221,10 @@ static CURLcode http2_data_setup(struct Curl_cfilter *cf,
struct stream_ctx *stream = H2_STREAM_CTX(data);

(void)cf;
if(!data || !data->req.p.http) {
failf(data, "initialization failure, transfer not http initialized");
return CURLE_FAILED_INIT;
}
if(stream)
return CURLE_OK;

Expand Down Expand Up @@ -415,12 +419,6 @@ static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf,
}
ctx->max_concurrent_streams = DEFAULT_MAX_CONCURRENT_STREAMS;

result = http2_data_setup(cf, data);
if(result)
goto out;
stream = H2_STREAM_CTX(data);
DEBUGASSERT(stream);

if(via_h1_upgrade) {
/* HTTP/1.1 Upgrade issued. H2 Settings have already been submitted
* in the H1 request and we upgrade from there. This stream
Expand All @@ -430,6 +428,11 @@ static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf,

binlen = populate_binsettings(binsettings, data);

result = http2_data_setup(cf, data);
if(result)
goto out;
stream = H2_STREAM_CTX(data);
DEBUGASSERT(stream);
stream->id = 1;
/* queue SETTINGS frame (again) */
rc = nghttp2_session_upgrade2(ctx->h2, binsettings, binlen,
Expand All @@ -454,9 +457,6 @@ static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf,
nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN];
int ivlen;

/* H2 Settings need to be submitted. Stream is not open yet. */
DEBUGASSERT(stream->id == -1);

ivlen = populate_settings(iv, data);
rc = nghttp2_submit_settings(ctx->h2, NGHTTP2_FLAG_NONE,
iv, ivlen);
Expand Down Expand Up @@ -1730,12 +1730,12 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf,

/* Receive data from the "lower" filters, e.g. network until
* it is time to stop or we have enough data for this stream */
while(keep_reading &&
!ctx->conn_closed && /* not closed the connection */
!stream->closed && /* nor the stream */
Curl_bufq_is_empty(&ctx->inbufq) && /* and we consumed our input */
!Curl_bufq_is_full(&stream->recvbuf) && /* enough? */
Curl_bufq_len(&stream->recvbuf) < data->set.buffer_size) {
while(keep_reading && !ctx->conn_closed && /* not closed the connection */
Curl_bufq_is_empty(&ctx->inbufq) && /* and we consumed our input */
(!stream || /* stream not started */
(!stream->closed && /* or ongoing */
!Curl_bufq_is_full(&stream->recvbuf) && /* enough? */
Curl_bufq_len(&stream->recvbuf) < data->set.buffer_size))) {

nread = Curl_bufq_slurp(&ctx->inbufq, nw_in_reader, cf, &result);
DEBUGF(LOG_CF(data, cf, "read %zd bytes nw data -> %zd, %d",
Expand Down Expand Up @@ -1858,7 +1858,7 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data,

CF_DATA_SAVE(save, cf, data);

if(stream->id != -1) {
if(stream && stream->id != -1) {
DEBUGF(LOG_CF(data, cf, "[h2sid=%d] cf_send: win %u/%u",
stream->id,
nghttp2_session_get_remote_window_size(ctx->h2),
Expand Down Expand Up @@ -1934,16 +1934,26 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
goto out;
}

DEBUGF(LOG_CF(data, cf, "cf_send, submit %s", data->state.url));
*err = http2_data_setup(cf, data);
if(*err) {
nwritten = -1;
goto out;
}
stream = H2_STREAM_CTX(data);

if(!stream->req_hds_len) {
/* first invocation carries the HTTP/1.1 formatted request headers.
* we remember that in case we EAGAIN this call, because the next
* invocation may have added request body data into the buffer. */
stream->req_hds_len = len;
DEBUGF(LOG_CF(data, cf, "cf_send, first submit (len=%zu, hds_len=%zu)",
len, stream->req_hds_len));
}

/* Stream has not been opened yet. `buf` is expected to contain
* `stream->req_hds_len` bytes of request headers. */
DEBUGF(LOG_CF(data, cf, "cf_send, submit %s (len=%zu, hds_len=%zu)",
data->state.url, len, stream->req_hds_len));
DEBUGASSERT(stream->req_hds_len <= len);
result = Curl_pseudo_headers(data, buf, stream->req_hds_len,
NULL, &hreq);
Expand Down Expand Up @@ -2204,10 +2214,8 @@ static CURLcode cf_h2_cntrl(struct Curl_cfilter *cf,

CF_DATA_SAVE(save, cf, data);
switch(event) {
case CF_CTRL_DATA_SETUP: {
result = http2_data_setup(cf, data);
case CF_CTRL_DATA_SETUP:
break;
}
case CF_CTRL_DATA_PAUSE:
result = http2_data_pause(cf, data, (arg1 != 0));
break;
Expand Down
4 changes: 2 additions & 2 deletions lib/vquic/curl_msh3.c
Expand Up @@ -148,9 +148,9 @@ struct stream_ctx {
};

#define H3_STREAM_CTX(d) ((struct stream_ctx *)(((d) && (d)->req.p.http)? \
((struct HTTP *)(d)->req.p.http)->impl_ctx \
((struct HTTP *)(d)->req.p.http)->h3_ctx \
: NULL))
#define H3_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->impl_ctx
#define H3_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->h3_ctx
#define H3_STREAM_ID(d) (H3_STREAM_CTX(d)? \
H3_STREAM_CTX(d)->id : -2)

Expand Down
30 changes: 17 additions & 13 deletions lib/vquic/curl_ngtcp2.c
Expand Up @@ -186,9 +186,9 @@ struct stream_ctx {
};

#define H3_STREAM_CTX(d) ((struct stream_ctx *)(((d) && (d)->req.p.http)? \
((struct HTTP *)(d)->req.p.http)->impl_ctx \
((struct HTTP *)(d)->req.p.http)->h3_ctx \
: NULL))
#define H3_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->impl_ctx
#define H3_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->h3_ctx
#define H3_STREAM_ID(d) (H3_STREAM_CTX(d)? \
H3_STREAM_CTX(d)->id : -2)

Expand All @@ -198,6 +198,11 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf,
struct cf_ngtcp2_ctx *ctx = cf->ctx;
struct stream_ctx *stream = H3_STREAM_CTX(data);

if(!data || !data->req.p.http) {
failf(data, "initialization failure, transfer not http initialized");
return CURLE_FAILED_INIT;
}

if(stream)
return CURLE_OK;

Expand Down Expand Up @@ -1542,7 +1547,7 @@ static CURLcode h3_stream_open(struct Curl_cfilter *cf,
size_t len)
{
struct cf_ngtcp2_ctx *ctx = cf->ctx;
struct stream_ctx *stream = H3_STREAM_CTX(data);
struct stream_ctx *stream = NULL;
size_t nheader;
CURLcode result = CURLE_OK;
nghttp3_nv *nva = NULL;
Expand All @@ -1552,6 +1557,11 @@ static CURLcode h3_stream_open(struct Curl_cfilter *cf,
nghttp3_data_reader reader;
nghttp3_data_reader *preader = NULL;

result = h3_data_setup(cf, data);
if(result)
goto out;
stream = H3_STREAM_CTX(data);

rc = ngtcp2_conn_open_bidi_stream(ctx->qconn, &stream->id, NULL);
if(rc) {
failf(data, "can get bidi streams");
Expand Down Expand Up @@ -1604,7 +1614,7 @@ static CURLcode h3_stream_open(struct Curl_cfilter *cf,
stream->id, data->state.url));

out:
if(!result && rc) {
if(stream && !result && rc) {
switch(rc) {
case NGHTTP3_ERR_CONN_CLOSING:
DEBUGF(LOG_CF(data, cf, "h3sid[%"PRId64"] failed to send, "
Expand Down Expand Up @@ -1637,13 +1647,13 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
DEBUGASSERT(ctx->h3conn);
*err = CURLE_OK;

if(stream->closed) {
if(stream && stream->closed) {
*err = CURLE_HTTP3;
sent = -1;
goto out;
}

if(stream->id < 0) {
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));
Expand Down Expand Up @@ -2095,10 +2105,8 @@ static CURLcode cf_ngtcp2_data_event(struct Curl_cfilter *cf,
(void)arg1;
(void)arg2;
switch(event) {
case CF_CTRL_DATA_SETUP: {
result = h3_data_setup(cf, data);
case CF_CTRL_DATA_SETUP:
break;
}
case CF_CTRL_DATA_DONE: {
h3_data_done(cf, data);
break;
Expand Down Expand Up @@ -2260,10 +2268,6 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
if(result)
return result;

result = h3_data_setup(cf, data);
if(result)
return result;

Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd,
&sockaddr, NULL, NULL, NULL, NULL);
ctx->q.local_addrlen = sizeof(ctx->q.local_addr);
Expand Down
4 changes: 2 additions & 2 deletions lib/vquic/curl_quiche.c
Expand Up @@ -197,9 +197,9 @@ struct stream_ctx {
};

#define H3_STREAM_CTX(d) ((struct stream_ctx *)(((d) && (d)->req.p.http)? \
((struct HTTP *)(d)->req.p.http)->impl_ctx \
((struct HTTP *)(d)->req.p.http)->h3_ctx \
: NULL))
#define H3_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->impl_ctx
#define H3_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->h3_ctx
#define H3_STREAM_ID(d) (H3_STREAM_CTX(d)? \
H3_STREAM_CTX(d)->id : -2)

Expand Down

0 comments on commit 83d334f

Please sign in to comment.