diff options
Diffstat (limited to 'libs/libcurl/src/vquic/curl_quiche.c')
| -rw-r--r-- | libs/libcurl/src/vquic/curl_quiche.c | 316 |
1 files changed, 186 insertions, 130 deletions
diff --git a/libs/libcurl/src/vquic/curl_quiche.c b/libs/libcurl/src/vquic/curl_quiche.c index e79137ebbd..0a2fab2213 100644 --- a/libs/libcurl/src/vquic/curl_quiche.c +++ b/libs/libcurl/src/vquic/curl_quiche.c @@ -29,6 +29,7 @@ #include <openssl/err.h>
#include <openssl/ssl.h>
#include "bufq.h"
+#include "hash.h"
#include "urldata.h"
#include "cfilters.h"
#include "cf-socket.h"
@@ -88,7 +89,7 @@ void Curl_quiche_ver(char *p, size_t len) struct cf_quiche_ctx {
struct cf_quic_ctx q;
struct ssl_peer peer;
- struct quic_tls_ctx tls;
+ struct curl_tls_ctx tls;
quiche_conn *qconn;
quiche_config *cfg;
quiche_h3_conn *h3c;
@@ -98,8 +99,8 @@ struct cf_quiche_ctx { struct curltime handshake_at; /* time connect handshake finished */
struct curltime reconnect_at; /* time the next attempt should start */
struct bufc_pool stream_bufcp; /* chunk pool for streams */
+ struct Curl_hash streams; /* hash `data->id` to `stream_ctx` */
curl_off_t data_recvd;
- uint64_t max_idle_ms; /* max idle time for QUIC conn */
BIT(goaway); /* got GOAWAY from server */
BIT(x509_store_setup); /* if x509 store has been set up */
};
@@ -123,55 +124,71 @@ static void cf_quiche_ctx_clear(struct cf_quiche_ctx *ctx) quiche_conn_free(ctx->qconn);
if(ctx->cfg)
quiche_config_free(ctx->cfg);
- /* quiche just freed ctx->tls.ssl */
- ctx->tls.ssl = NULL;
+ /* quiche just freed it */
+ ctx->tls.ossl.ssl = NULL;
Curl_vquic_tls_cleanup(&ctx->tls);
Curl_ssl_peer_cleanup(&ctx->peer);
vquic_ctx_free(&ctx->q);
Curl_bufcp_free(&ctx->stream_bufcp);
+ Curl_hash_clean(&ctx->streams);
+ Curl_hash_destroy(&ctx->streams);
memset(ctx, 0, sizeof(*ctx));
}
}
+static CURLcode cf_flush_egress(struct Curl_cfilter *cf,
+ struct Curl_easy *data);
+
/**
* All about the H3 internals of a stream
*/
struct stream_ctx {
- int64_t id; /* HTTP/3 protocol stream identifier */
+ curl_uint64_t id; /* HTTP/3 protocol stream identifier */
struct bufq recvbuf; /* h3 response */
struct h1_req_parser h1; /* h1 request parsing */
- uint64_t error3; /* HTTP/3 stream error code */
+ curl_uint64_t error3; /* HTTP/3 stream error code */
curl_off_t upload_left; /* number of request bytes left to upload */
- bool closed; /* TRUE on stream close */
- bool reset; /* TRUE on stream reset */
- bool send_closed; /* stream is locally closed */
- bool resp_hds_complete; /* complete, final response has been received */
- bool resp_got_header; /* TRUE when h3 stream has recvd some HEADER */
+ BIT(opened); /* TRUE after stream has been opened */
+ BIT(closed); /* TRUE on stream close */
+ BIT(reset); /* TRUE on stream reset */
+ BIT(send_closed); /* stream is locally closed */
+ BIT(resp_hds_complete); /* final response has been received */
+ BIT(resp_got_header); /* TRUE when h3 stream has recvd some HEADER */
BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */
};
-#define H3_STREAM_CTX(d) ((struct stream_ctx *)(((d) && (d)->req.p.http)? \
- ((struct HTTP *)(d)->req.p.http)->h3_ctx \
- : NULL))
-#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)
+#define H3_STREAM_CTX(ctx,data) ((struct stream_ctx *)(\
+ data? Curl_hash_offt_get(&(ctx)->streams, (data)->id) : NULL))
+
+static void h3_stream_ctx_free(struct stream_ctx *stream)
+{
+ Curl_bufq_free(&stream->recvbuf);
+ Curl_h1_req_parse_free(&stream->h1);
+ free(stream);
+}
+
+static void h3_stream_hash_free(void *stream)
+{
+ DEBUGASSERT(stream);
+ h3_stream_ctx_free((struct stream_ctx *)stream);
+}
static void check_resumes(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
+ struct cf_quiche_ctx *ctx = cf->ctx;
struct Curl_easy *sdata;
struct stream_ctx *stream;
DEBUGASSERT(data->multi);
for(sdata = data->multi->easyp; sdata; sdata = sdata->next) {
if(sdata->conn == data->conn) {
- stream = H3_STREAM_CTX(sdata);
+ stream = H3_STREAM_CTX(ctx, sdata);
if(stream && stream->quic_flow_blocked) {
stream->quic_flow_blocked = FALSE;
Curl_expire(data, 0, EXPIRE_RUN_NOW);
- CURL_TRC_CF(data, cf, "[%"PRId64"] unblock", stream->id);
+ CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] unblock", stream->id);
}
}
}
@@ -181,7 +198,7 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf, struct Curl_easy *data)
{
struct cf_quiche_ctx *ctx = cf->ctx;
- struct stream_ctx *stream = H3_STREAM_CTX(data);
+ struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
if(stream)
return CURLE_OK;
@@ -190,22 +207,28 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf, if(!stream)
return CURLE_OUT_OF_MEMORY;
- H3_STREAM_LCTX(data) = stream;
stream->id = -1;
Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp,
H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT);
Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
+
+ if(!Curl_hash_offt_set(&ctx->streams, data->id, stream)) {
+ h3_stream_ctx_free(stream);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
return CURLE_OK;
}
static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
{
struct cf_quiche_ctx *ctx = cf->ctx;
- struct stream_ctx *stream = H3_STREAM_CTX(data);
+ struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+ CURLcode result;
(void)cf;
if(stream) {
- CURL_TRC_CF(data, cf, "[%"PRId64"] easy handle is done", stream->id);
+ CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] easy handle is done", stream->id);
if(ctx->qconn && !stream->closed) {
quiche_conn_stream_shutdown(ctx->qconn, stream->id,
QUICHE_SHUTDOWN_READ, CURL_H3_NO_ERROR);
@@ -215,18 +238,19 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) stream->send_closed = TRUE;
}
stream->closed = TRUE;
+ result = cf_flush_egress(cf, data);
+ if(result)
+ CURL_TRC_CF(data, cf, "data_done, flush egress -> %d", result);
}
- Curl_bufq_free(&stream->recvbuf);
- Curl_h1_req_parse_free(&stream->h1);
- free(stream);
- H3_STREAM_LCTX(data) = NULL;
+ Curl_hash_offt_remove(&ctx->streams, data->id);
}
}
static void drain_stream(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
- struct stream_ctx *stream = H3_STREAM_CTX(data);
+ struct cf_quiche_ctx *ctx = cf->ctx;
+ struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
unsigned char bits;
(void)cf;
@@ -241,25 +265,50 @@ static void drain_stream(struct Curl_cfilter *cf, static struct Curl_easy *get_stream_easy(struct Curl_cfilter *cf,
struct Curl_easy *data,
- int64_t stream3_id)
+ curl_uint64_t stream_id,
+ struct stream_ctx **pstream)
{
+ struct cf_quiche_ctx *ctx = cf->ctx;
struct Curl_easy *sdata;
+ struct stream_ctx *stream;
(void)cf;
- if(H3_STREAM_ID(data) == stream3_id) {
+ stream = H3_STREAM_CTX(ctx, data);
+ if(stream && stream->id == stream_id) {
+ *pstream = stream;
return data;
}
else {
DEBUGASSERT(data->multi);
for(sdata = data->multi->easyp; sdata; sdata = sdata->next) {
- if((sdata->conn == data->conn) && H3_STREAM_ID(sdata) == stream3_id) {
+ if(sdata->conn != data->conn)
+ continue;
+ stream = H3_STREAM_CTX(ctx, sdata);
+ if(stream && stream->id == stream_id) {
+ *pstream = stream;
return sdata;
}
}
}
+ *pstream = NULL;
return NULL;
}
+static void cf_quiche_expire_conn_closed(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct Curl_easy *sdata;
+
+ DEBUGASSERT(data->multi);
+ CURL_TRC_CF(data, cf, "conn closed, expire all transfers");
+ for(sdata = data->multi->easyp; sdata; sdata = sdata->next) {
+ if(sdata == data || sdata->conn != data->conn)
+ continue;
+ CURL_TRC_CF(sdata, cf, "conn closed, expire transfer");
+ Curl_expire(sdata, 0, EXPIRE_RUN_NOW);
+ }
+}
+
/*
* write_resp_raw() copies response data in raw format to the `data`'s
* receive buffer. If not enough space is available, it appends to the
@@ -269,7 +318,8 @@ static CURLcode write_resp_raw(struct Curl_cfilter *cf, struct Curl_easy *data,
const void *mem, size_t memlen)
{
- struct stream_ctx *stream = H3_STREAM_CTX(data);
+ struct cf_quiche_ctx *ctx = cf->ctx;
+ struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
CURLcode result = CURLE_OK;
ssize_t nwritten;
@@ -299,14 +349,15 @@ static int cb_each_header(uint8_t *name, size_t name_len, void *argp)
{
struct cb_ctx *x = argp;
- struct stream_ctx *stream = H3_STREAM_CTX(x->data);
+ struct cf_quiche_ctx *ctx = x->cf->ctx;
+ struct stream_ctx *stream = H3_STREAM_CTX(ctx, x->data);
CURLcode result;
if(!stream)
return CURLE_OK;
if((name_len == 7) && !strncmp(HTTP_PSEUDO_STATUS, (char *)name, 7)) {
- CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] status: %.*s",
+ CURL_TRC_CF(x->data, x->cf, "[%" CURL_PRIu64 "] status: %.*s",
stream->id, (int)value_len, value);
result = write_resp_raw(x->cf, x->data, "HTTP/3 ", sizeof("HTTP/3 ") - 1);
if(!result)
@@ -315,7 +366,7 @@ static int cb_each_header(uint8_t *name, size_t name_len, result = write_resp_raw(x->cf, x->data, " \r\n", 3);
}
else {
- CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] header: %.*s: %.*s",
+ CURL_TRC_CF(x->data, x->cf, "[%" CURL_PRIu64 "] header: %.*s: %.*s",
stream->id, (int)name_len, name,
(int)value_len, value);
result = write_resp_raw(x->cf, x->data, name, name_len);
@@ -327,7 +378,7 @@ static int cb_each_header(uint8_t *name, size_t name_len, result = write_resp_raw(x->cf, x->data, "\r\n", 2);
}
if(result) {
- CURL_TRC_CF(x->data, x->cf, "[%"PRId64"] on header error %d",
+ CURL_TRC_CF(x->data, x->cf, "[%"CURL_PRIu64"] on header error %d",
stream->id, result);
}
return result;
@@ -339,7 +390,7 @@ static ssize_t stream_resp_read(void *reader_ctx, {
struct cb_ctx *x = reader_ctx;
struct cf_quiche_ctx *ctx = x->cf->ctx;
- struct stream_ctx *stream = H3_STREAM_CTX(x->data);
+ struct stream_ctx *stream = H3_STREAM_CTX(ctx, x->data);
ssize_t nread;
if(!stream) {
@@ -362,7 +413,8 @@ static ssize_t stream_resp_read(void *reader_ctx, static CURLcode cf_recv_body(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
- struct stream_ctx *stream = H3_STREAM_CTX(data);
+ struct cf_quiche_ctx *ctx = cf->ctx;
+ struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
ssize_t nwritten;
struct cb_ctx cb_ctx;
CURLcode result = CURLE_OK;
@@ -383,9 +435,9 @@ static CURLcode cf_recv_body(struct Curl_cfilter *cf, stream_resp_read, &cb_ctx, &result);
if(nwritten < 0 && result != CURLE_AGAIN) {
- CURL_TRC_CF(data, cf, "[%"PRId64"] recv_body error %zd",
+ CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] recv_body error %zd",
stream->id, nwritten);
- failf(data, "Error %d in HTTP/3 response body for stream[%"PRId64"]",
+ failf(data, "Error %d in HTTP/3 response body for stream[%"CURL_PRIu64"]",
result, stream->id);
stream->closed = TRUE;
stream->reset = TRUE;
@@ -420,17 +472,15 @@ static const char *cf_ev_name(quiche_h3_event *ev) static CURLcode h3_process_event(struct Curl_cfilter *cf,
struct Curl_easy *data,
- int64_t stream3_id,
+ struct stream_ctx *stream,
quiche_h3_event *ev)
{
- struct stream_ctx *stream = H3_STREAM_CTX(data);
struct cb_ctx cb_ctx;
CURLcode result = CURLE_OK;
int rc;
if(!stream)
return CURLE_OK;
- DEBUGASSERT(stream3_id == stream->id);
switch(quiche_h3_event_type(ev)) {
case QUICHE_H3_EVENT_HEADERS:
stream->resp_got_header = TRUE;
@@ -438,11 +488,11 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf, cb_ctx.data = data;
rc = quiche_h3_event_for_each_header(ev, cb_each_header, &cb_ctx);
if(rc) {
- failf(data, "Error %d in HTTP/3 response header for stream[%"PRId64"]",
- rc, stream3_id);
+ failf(data, "Error %d in HTTP/3 response header for stream[%"
+ CURL_PRIu64"]", rc, stream->id);
return CURLE_RECV_ERROR;
}
- CURL_TRC_CF(data, cf, "[%"PRId64"] <- [HEADERS]", stream3_id);
+ CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] <- [HEADERS]", stream->id);
break;
case QUICHE_H3_EVENT_DATA:
@@ -452,7 +502,7 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf, break;
case QUICHE_H3_EVENT_RESET:
- CURL_TRC_CF(data, cf, "[%"PRId64"] RESET", stream3_id);
+ CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] RESET", stream->id);
stream->closed = TRUE;
stream->reset = TRUE;
stream->send_closed = TRUE;
@@ -460,7 +510,7 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf, break;
case QUICHE_H3_EVENT_FINISHED:
- CURL_TRC_CF(data, cf, "[%"PRId64"] CLOSED", stream3_id);
+ CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] CLOSED", stream->id);
if(!stream->resp_hds_complete) {
result = write_resp_raw(cf, data, "\r\n", 2);
if(result)
@@ -472,12 +522,12 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf, break;
case QUICHE_H3_EVENT_GOAWAY:
- CURL_TRC_CF(data, cf, "[%"PRId64"] <- [GOAWAY]", stream3_id);
+ CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] <- [GOAWAY]", stream->id);
break;
default:
- CURL_TRC_CF(data, cf, "[%"PRId64"] recv, unhandled event %d",
- stream3_id, quiche_h3_event_type(ev));
+ CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] recv, unhandled event %d",
+ stream->id, quiche_h3_event_type(ev));
break;
}
return result;
@@ -487,36 +537,33 @@ static CURLcode cf_poll_events(struct Curl_cfilter *cf, struct Curl_easy *data)
{
struct cf_quiche_ctx *ctx = cf->ctx;
- struct stream_ctx *stream = H3_STREAM_CTX(data);
+ struct stream_ctx *stream = NULL;
struct Curl_easy *sdata;
quiche_h3_event *ev;
CURLcode result;
/* Take in the events and distribute them to the transfers. */
while(ctx->h3c) {
- int64_t stream3_id = quiche_h3_conn_poll(ctx->h3c, ctx->qconn, &ev);
+ curl_int64_t stream3_id = quiche_h3_conn_poll(ctx->h3c, ctx->qconn, &ev);
if(stream3_id == QUICHE_H3_ERR_DONE) {
break;
}
else if(stream3_id < 0) {
- CURL_TRC_CF(data, cf, "[%"PRId64"] error poll: %"PRId64,
- stream? stream->id : -1, stream3_id);
+ CURL_TRC_CF(data, cf, "error poll: %"CURL_PRId64, stream3_id);
return CURLE_HTTP3;
}
- sdata = get_stream_easy(cf, data, stream3_id);
- if(!sdata) {
- CURL_TRC_CF(data, cf, "[%"PRId64"] discard event %s for "
- "unknown [%"PRId64"]",
- stream? stream->id : -1, cf_ev_name(ev), stream3_id);
+ sdata = get_stream_easy(cf, data, stream3_id, &stream);
+ if(!sdata || !stream) {
+ CURL_TRC_CF(data, cf, "discard event %s for unknown [%"CURL_PRId64"]",
+ cf_ev_name(ev), stream3_id);
}
else {
- result = h3_process_event(cf, sdata, stream3_id, ev);
+ result = h3_process_event(cf, sdata, stream, ev);
drain_stream(cf, sdata);
if(result) {
- CURL_TRC_CF(data, cf, "[%"PRId64"] error processing event %s "
- "for [%"PRId64"] -> %d",
- stream? stream->id : -1, cf_ev_name(ev),
+ CURL_TRC_CF(data, cf, "error processing event %s "
+ "for [%"CURL_PRIu64"] -> %d", cf_ev_name(ev),
stream3_id, result);
if(data == sdata) {
/* Only report this error to the caller if it is about the
@@ -560,11 +607,19 @@ static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen, &recv_info);
if(nread < 0) {
if(QUICHE_ERR_DONE == nread) {
+ if(quiche_conn_is_draining(ctx->qconn)) {
+ CURL_TRC_CF(r->data, r->cf, "ingress, connection is draining");
+ return CURLE_RECV_ERROR;
+ }
+ if(quiche_conn_is_closed(ctx->qconn)) {
+ CURL_TRC_CF(r->data, r->cf, "ingress, connection is closed");
+ return CURLE_RECV_ERROR;
+ }
CURL_TRC_CF(r->data, r->cf, "ingress, quiche is DONE");
return CURLE_OK;
}
else if(QUICHE_ERR_TLS_FAIL == nread) {
- long verify_ok = SSL_get_verify_result(ctx->tls.ssl);
+ long verify_ok = SSL_get_verify_result(ctx->tls.ossl.ssl);
if(verify_ok != X509_V_OK) {
failf(r->data, "SSL certificate problem: %s",
X509_verify_cert_error_string(verify_ok));
@@ -651,8 +706,8 @@ static CURLcode cf_flush_egress(struct Curl_cfilter *cf, struct cf_quiche_ctx *ctx = cf->ctx;
ssize_t nread;
CURLcode result;
- int64_t expiry_ns;
- int64_t timeout_ns;
+ curl_int64_t expiry_ns;
+ curl_int64_t timeout_ns;
struct read_ctx readx;
size_t pkt_count, gsolen;
@@ -660,7 +715,13 @@ static CURLcode cf_flush_egress(struct Curl_cfilter *cf, if(!expiry_ns) {
quiche_conn_on_timeout(ctx->qconn);
if(quiche_conn_is_closed(ctx->qconn)) {
- failf(data, "quiche_conn_on_timeout closed the connection");
+ if(quiche_conn_is_timed_out(ctx->qconn))
+ failf(data, "connection closed by idle timeout");
+ else
+ failf(data, "connection closed by server");
+ /* Connection timed out, expire all transfers belonging to it
+ * as will not get any more POLL events here. */
+ cf_quiche_expire_conn_closed(cf, data);
return CURLE_SEND_ERROR;
}
}
@@ -725,25 +786,26 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf, struct Curl_easy *data,
CURLcode *err)
{
- struct stream_ctx *stream = H3_STREAM_CTX(data);
+ struct cf_quiche_ctx *ctx = cf->ctx;
+ struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
ssize_t nread = -1;
DEBUGASSERT(stream);
if(stream->reset) {
failf(data,
- "HTTP/3 stream %" PRId64 " reset by server", stream->id);
+ "HTTP/3 stream %" CURL_PRIu64 " reset by server", stream->id);
*err = data->req.bytecount? CURLE_PARTIAL_FILE : CURLE_HTTP3;
- CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv, was reset -> %d",
+ CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] cf_recv, was reset -> %d",
stream->id, *err);
}
else if(!stream->resp_got_header) {
failf(data,
- "HTTP/3 stream %" PRId64 " was closed cleanly, but before getting"
- " all response header fields, treated as error",
+ "HTTP/3 stream %" CURL_PRIu64 " was closed cleanly, but before "
+ "getting all response header fields, treated as error",
stream->id);
/* *err = CURLE_PARTIAL_FILE; */
*err = CURLE_HTTP3;
- CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv, closed incomplete"
+ CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] cf_recv, closed incomplete"
" -> %d", stream->id, *err);
}
else {
@@ -757,7 +819,7 @@ static ssize_t cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data, char *buf, size_t len, CURLcode *err)
{
struct cf_quiche_ctx *ctx = cf->ctx;
- struct stream_ctx *stream = H3_STREAM_CTX(data);
+ struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
ssize_t nread = -1;
CURLcode result;
@@ -771,7 +833,7 @@ static ssize_t cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(!Curl_bufq_is_empty(&stream->recvbuf)) {
nread = Curl_bufq_read(&stream->recvbuf,
(unsigned char *)buf, len, err);
- CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) "
+ CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] read recvbuf(len=%zu) "
"-> %zd, %d", stream->id, len, nread, *err);
if(nread < 0)
goto out;
@@ -788,7 +850,7 @@ static ssize_t cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(nread < 0 && !Curl_bufq_is_empty(&stream->recvbuf)) {
nread = Curl_bufq_read(&stream->recvbuf,
(unsigned char *)buf, len, err);
- CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) "
+ CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] read recvbuf(len=%zu) "
"-> %zd, %d", stream->id, len, nread, *err);
if(nread < 0)
goto out;
@@ -822,7 +884,7 @@ out: }
if(nread > 0)
ctx->data_recvd += nread;
- CURL_TRC_CF(data, cf, "[%"PRId64"] cf_recv(total=%"
+ CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] cf_recv(total=%"
CURL_FORMAT_CURL_OFF_T ") -> %zd, %d",
stream->id, ctx->data_recvd, nread, *err);
return nread;
@@ -838,9 +900,9 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf, CURLcode *err)
{
struct cf_quiche_ctx *ctx = cf->ctx;
- struct stream_ctx *stream = H3_STREAM_CTX(data);
+ struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
size_t nheader, i;
- int64_t stream3_id;
+ curl_int64_t stream3_id;
struct dynhds h2_headers;
quiche_h3_header *nva = NULL;
ssize_t nwritten;
@@ -850,7 +912,7 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf, if(*err) {
return -1;
}
- stream = H3_STREAM_CTX(data);
+ stream = H3_STREAM_CTX(ctx, data);
DEBUGASSERT(stream);
}
@@ -915,14 +977,14 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf, if(QUICHE_H3_ERR_STREAM_BLOCKED == stream3_id) {
/* quiche seems to report this error if the connection window is
* exhausted. Which happens frequently and intermittent. */
- CURL_TRC_CF(data, cf, "[%"PRId64"] blocked", stream->id);
+ CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] blocked", stream->id);
stream->quic_flow_blocked = TRUE;
*err = CURLE_AGAIN;
nwritten = -1;
goto out;
}
else {
- CURL_TRC_CF(data, cf, "send_request(%s) -> %" PRId64,
+ CURL_TRC_CF(data, cf, "send_request(%s) -> %" CURL_PRIu64,
data->state.url, stream3_id);
}
*err = CURLE_SEND_ERROR;
@@ -930,17 +992,18 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf, goto out;
}
- DEBUGASSERT(stream->id == -1);
+ DEBUGASSERT(!stream->opened);
*err = CURLE_OK;
stream->id = stream3_id;
+ stream->opened = TRUE;
stream->closed = FALSE;
stream->reset = FALSE;
if(Curl_trc_is_verbose(data)) {
- infof(data, "[HTTP/3] [%" PRId64 "] OPENED stream for %s",
+ infof(data, "[HTTP/3] [%" CURL_PRIu64 "] OPENED stream for %s",
stream->id, data->state.url);
for(i = 0; i < nheader; ++i) {
- infof(data, "[HTTP/3] [%" PRId64 "] [%.*s: %.*s]", stream->id,
+ infof(data, "[HTTP/3] [%" CURL_PRIu64 "] [%.*s: %.*s]", stream->id,
(int)nva[i].name_len, nva[i].name,
(int)nva[i].value_len, nva[i].value);
}
@@ -956,7 +1019,7 @@ static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, const void *buf, size_t len, CURLcode *err)
{
struct cf_quiche_ctx *ctx = cf->ctx;
- struct stream_ctx *stream = H3_STREAM_CTX(data);
+ struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
CURLcode result;
ssize_t nwritten;
@@ -968,11 +1031,11 @@ static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, goto out;
}
- if(!stream || stream->id < 0) {
+ if(!stream || !stream->opened) {
nwritten = h3_open_stream(cf, data, buf, len, err);
if(nwritten < 0)
goto out;
- stream = H3_STREAM_CTX(data);
+ stream = H3_STREAM_CTX(ctx, data);
}
else if(stream->closed) {
if(stream->resp_hds_complete) {
@@ -984,13 +1047,13 @@ static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, * sending the 30x response.
* This is sort of a race: had the transfer loop called recv first,
* it would see the response and stop/discard sending on its own- */
- CURL_TRC_CF(data, cf, "[%" PRId64 "] discarding data"
+ CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] discarding data"
"on closed stream with response", stream->id);
*err = CURLE_OK;
nwritten = (ssize_t)len;
goto out;
}
- CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) "
+ CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send_body(len=%zu) "
"-> stream closed", stream->id, len);
*err = CURLE_HTTP3;
nwritten = -1;
@@ -1005,7 +1068,7 @@ static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, /* TODO: we seem to be blocked on flow control and should HOLD
* sending. But when do we open again? */
if(!quiche_conn_stream_writable(ctx->qconn, stream->id, len)) {
- CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) "
+ CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send_body(len=%zu) "
"-> window exhausted", stream->id, len);
stream->quic_flow_blocked = TRUE;
}
@@ -1014,21 +1077,21 @@ static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, goto out;
}
else if(nwritten == QUICHE_H3_TRANSPORT_ERR_INVALID_STREAM_STATE) {
- CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) "
+ CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send_body(len=%zu) "
"-> invalid stream state", stream->id, len);
*err = CURLE_HTTP3;
nwritten = -1;
goto out;
}
else if(nwritten == QUICHE_H3_TRANSPORT_ERR_FINAL_SIZE) {
- CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) "
+ CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send_body(len=%zu) "
"-> exceeds size", stream->id, len);
*err = CURLE_SEND_ERROR;
nwritten = -1;
goto out;
}
else if(nwritten < 0) {
- CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) "
+ CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send_body(len=%zu) "
"-> quiche err %zd", stream->id, len, nwritten);
*err = CURLE_SEND_ERROR;
nwritten = -1;
@@ -1043,7 +1106,7 @@ static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, if(stream->upload_left == 0)
stream->send_closed = TRUE;
- CURL_TRC_CF(data, cf, "[%" PRId64 "] send body(len=%zu, "
+ CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] send body(len=%zu, "
"left=%" CURL_FORMAT_CURL_OFF_T ") -> %zd",
stream->id, len, stream->upload_left, nwritten);
*err = CURLE_OK;
@@ -1056,7 +1119,7 @@ out: *err = result;
nwritten = -1;
}
- CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %zd, %d",
+ CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] cf_send(len=%zu) -> %zd, %d",
stream? stream->id : -1, len, nwritten, *err);
return nwritten;
}
@@ -1065,10 +1128,10 @@ static bool stream_is_writeable(struct Curl_cfilter *cf, struct Curl_easy *data)
{
struct cf_quiche_ctx *ctx = cf->ctx;
- struct stream_ctx *stream = H3_STREAM_CTX(data);
+ struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
- return stream && (quiche_conn_stream_writable(ctx->qconn,
- (uint64_t)stream->id, 1) > 0);
+ return stream && (quiche_conn_stream_writable(
+ ctx->qconn, (curl_uint64_t)stream->id, 1) > 0);
}
static void cf_quiche_adjust_pollset(struct Curl_cfilter *cf,
@@ -1083,12 +1146,12 @@ static void cf_quiche_adjust_pollset(struct Curl_cfilter *cf, Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send);
if(want_recv || want_send) {
- struct stream_ctx *stream = H3_STREAM_CTX(data);
+ struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
bool c_exhaust, s_exhaust;
c_exhaust = FALSE; /* Have not found any call in quiche that tells
us if the connection itself is blocked */
- s_exhaust = want_send && stream && stream->id >= 0 &&
+ s_exhaust = want_send && stream && stream->opened &&
(stream->quic_flow_blocked || !stream_is_writeable(cf, data));
want_recv = (want_recv || c_exhaust || s_exhaust);
want_send = (!s_exhaust && want_send) ||
@@ -1105,7 +1168,8 @@ static void cf_quiche_adjust_pollset(struct Curl_cfilter *cf, static bool cf_quiche_data_pending(struct Curl_cfilter *cf,
const struct Curl_easy *data)
{
- const struct stream_ctx *stream = H3_STREAM_CTX(data);
+ struct cf_quiche_ctx *ctx = cf->ctx;
+ const struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
(void)cf;
return stream && !Curl_bufq_is_empty(&stream->recvbuf);
}
@@ -1127,6 +1191,7 @@ static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf, struct Curl_easy *data,
int event, int arg1, void *arg2)
{
+ struct cf_quiche_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
(void)arg1;
@@ -1144,7 +1209,7 @@ static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf, h3_data_done(cf, data);
break;
case CF_CTRL_DATA_DONE_SEND: {
- struct stream_ctx *stream = H3_STREAM_CTX(data);
+ struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
if(stream && !stream->send_closed) {
unsigned char body[1];
ssize_t sent;
@@ -1153,13 +1218,13 @@ static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf, stream->upload_left = 0;
body[0] = 'X';
sent = cf_quiche_send(cf, data, body, 0, &result);
- CURL_TRC_CF(data, cf, "[%"PRId64"] DONE_SEND -> %zd, %d",
+ CURL_TRC_CF(data, cf, "[%"CURL_PRIu64"] DONE_SEND -> %zd, %d",
stream->id, sent, result);
}
break;
}
case CF_CTRL_DATA_IDLE: {
- struct stream_ctx *stream = H3_STREAM_CTX(data);
+ struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
if(stream && !stream->closed) {
result = cf_flush_egress(cf, data);
if(result)
@@ -1191,16 +1256,16 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, debug_log_init = 1;
}
#endif
- ctx->max_idle_ms = CURL_QUIC_MAX_IDLE_MS;
Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE,
H3_STREAM_POOL_SPARES);
+ Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free);
ctx->data_recvd = 0;
result = vquic_ctx_init(&ctx->q);
if(result)
return result;
- result = Curl_ssl_peer_init(&ctx->peer, cf);
+ result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC);
if(result)
return result;
@@ -1210,7 +1275,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, return CURLE_FAILED_INIT;
}
quiche_config_enable_pacing(ctx->cfg, false);
- quiche_config_set_max_idle_timeout(ctx->cfg, ctx->max_idle_ms * 1000);
+ quiche_config_set_max_idle_timeout(ctx->cfg, CURL_QUIC_MAX_IDLE_MS);
quiche_config_set_initial_max_data(ctx->cfg, (1 * 1024 * 1024)
/* (QUIC_MAX_STREAMS/2) * H3_STREAM_WINDOW_SIZE */);
quiche_config_set_initial_max_streams_bidi(ctx->cfg, QUIC_MAX_STREAMS);
@@ -1235,7 +1300,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer,
QUICHE_H3_APPLICATION_PROTOCOL,
sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1,
- NULL, cf);
+ NULL, NULL, cf);
if(result)
return result;
@@ -1255,7 +1320,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, (struct sockaddr *)&ctx->q.local_addr,
ctx->q.local_addrlen,
&sockaddr->sa_addr, sockaddr->addrlen,
- ctx->cfg, ctx->tls.ssl, false);
+ ctx->cfg, ctx->tls.ossl.ssl, false);
if(!ctx->qconn) {
failf(data, "can't create quiche connection");
return CURLE_OUT_OF_MEMORY;
@@ -1433,12 +1498,14 @@ static CURLcode cf_quiche_query(struct Curl_cfilter *cf, switch(query) {
case CF_QUERY_MAX_CONCURRENT: {
- uint64_t max_streams = CONN_INUSE(cf->conn);
+ curl_uint64_t max_streams = CONN_INUSE(cf->conn);
if(!ctx->goaway) {
max_streams += quiche_conn_peer_streams_left_bidi(ctx->qconn);
}
*pres1 = (max_streams > INT_MAX)? INT_MAX : (int)max_streams;
- CURL_TRC_CF(data, cf, "query: MAX_CONCURRENT -> %d", *pres1);
+ CURL_TRC_CF(data, cf, "query conn[%" CURL_FORMAT_CURL_OFF_T "]: "
+ "MAX_CONCURRENT -> %d (%zu in use)",
+ cf->conn->connection_id, *pres1, CONN_INUSE(cf->conn));
return CURLE_OK;
}
case CF_QUERY_CONNECT_REPLY_MS:
@@ -1480,23 +1547,12 @@ static bool cf_quiche_conn_is_alive(struct Curl_cfilter *cf, if(!ctx->qconn)
return FALSE;
- /* Both sides of the QUIC connection announce they max idle times in
- * the transport parameters. Look at the minimum of both and if
- * we exceed this, regard the connection as dead. The other side
- * may have completely purged it and will no longer respond
- * to any packets from us. */
- {
- quiche_transport_params qpeerparams;
- timediff_t idletime;
- uint64_t idle_ms = ctx->max_idle_ms;
-
- if(quiche_conn_peer_transport_params(ctx->qconn, &qpeerparams) &&
- qpeerparams.peer_max_idle_timeout &&
- qpeerparams.peer_max_idle_timeout < idle_ms)
- idle_ms = qpeerparams.peer_max_idle_timeout;
- idletime = Curl_timediff(Curl_now(), cf->conn->lastused);
- if(idletime > 0 && (uint64_t)idletime > idle_ms)
- return FALSE;
+ if(quiche_conn_is_closed(ctx->qconn)) {
+ if(quiche_conn_is_timed_out(ctx->qconn))
+ CURL_TRC_CF(data, cf, "connection was closed due to idle timeout");
+ else
+ CURL_TRC_CF(data, cf, "connection is closed");
+ return FALSE;
}
if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending))
|
