diff options
| author | dartraiden <wowemuh@gmail.com> | 2024-07-25 00:50:30 +0300 |
|---|---|---|
| committer | dartraiden <wowemuh@gmail.com> | 2024-07-25 02:38:23 +0300 |
| commit | 67a42fc97c64c83e02f6f0d68e5a4a22c71138d3 (patch) | |
| tree | 21eb2d53a9cd7e645a58662dee11588f56057eee /libs/libcurl/src/vquic | |
| parent | 0a365886f2d06750a707037d894e1492988eb53c (diff) | |
libcurl: update to 8.9.0
Diffstat (limited to 'libs/libcurl/src/vquic')
| -rw-r--r-- | libs/libcurl/src/vquic/curl_msh3.c | 11 | ||||
| -rw-r--r-- | libs/libcurl/src/vquic/curl_ngtcp2.c | 194 | ||||
| -rw-r--r-- | libs/libcurl/src/vquic/curl_osslq.c | 153 | ||||
| -rw-r--r-- | libs/libcurl/src/vquic/curl_quiche.c | 77 | ||||
| -rw-r--r-- | libs/libcurl/src/vquic/vquic-tls.c | 77 | ||||
| -rw-r--r-- | libs/libcurl/src/vquic/vquic-tls.h | 5 | ||||
| -rw-r--r-- | libs/libcurl/src/vquic/vquic.c | 102 |
7 files changed, 420 insertions, 199 deletions
diff --git a/libs/libcurl/src/vquic/curl_msh3.c b/libs/libcurl/src/vquic/curl_msh3.c index 5827d99c24..a780acc31f 100644 --- a/libs/libcurl/src/vquic/curl_msh3.c +++ b/libs/libcurl/src/vquic/curl_msh3.c @@ -293,7 +293,7 @@ static const MSH3_REQUEST_IF msh3_request_if = { msh3_data_sent
};
-/* Decode HTTP status code. Returns -1 if no valid status code was
+/* Decode HTTP status code. Returns -1 if no valid status code was
decoded. (duplicate from http2.c) */
static int decode_status_code(const char *value, size_t len)
{
@@ -689,7 +689,7 @@ static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data, }
/* TODO - msh3/msquic will hold onto this memory until the send complete
- event. How do we make sure curl doesn't free it until then? */
+ event. How do we make sure curl does not free it until then? */
*err = CURLE_OK;
nwritten = len;
}
@@ -840,7 +840,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, ctx->api = MsH3ApiOpen();
if(!ctx->api) {
- failf(data, "can't create msh3 api");
+ failf(data, "cannot create msh3 api");
return CURLE_FAILED_INIT;
}
@@ -851,7 +851,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, &addr,
!verify);
if(!ctx->qconn) {
- failf(data, "can't create msh3 connection");
+ failf(data, "cannot create msh3 connection");
if(ctx->api) {
MsH3ApiClose(ctx->api);
ctx->api = NULL;
@@ -883,7 +883,7 @@ static CURLcode cf_msh3_connect(struct Curl_cfilter *cf, CF_DATA_SAVE(save, cf, data);
if(ctx->sock[SP_LOCAL] == CURL_SOCKET_BAD) {
- if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, &ctx->sock[0]) < 0) {
+ if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, &ctx->sock[0], FALSE) < 0) {
ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD;
return CURLE_COULDNT_CONNECT;
@@ -1038,6 +1038,7 @@ struct Curl_cftype Curl_cft_http3 = { cf_msh3_destroy,
cf_msh3_connect,
cf_msh3_close,
+ Curl_cf_def_shutdown,
Curl_cf_def_get_host,
cf_msh3_adjust_pollset,
cf_msh3_data_pending,
diff --git a/libs/libcurl/src/vquic/curl_ngtcp2.c b/libs/libcurl/src/vquic/curl_ngtcp2.c index 9bc4b3a5a5..790e3c6ff4 100644 --- a/libs/libcurl/src/vquic/curl_ngtcp2.c +++ b/libs/libcurl/src/vquic/curl_ngtcp2.c @@ -88,7 +88,7 @@ /* The pool keeps spares around and half of a full stream windows
* seems good. More does not seem to improve performance.
* The benefit of the pool is that stream buffer to not keep
- * spares. So memory consumption goes down when streams run empty,
+ * spares. Memory consumption goes down when streams run empty,
* have a large upload done, etc. */
#define H3_STREAM_POOL_SPARES \
(H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE ) / 2
@@ -138,7 +138,7 @@ struct cf_ngtcp2_ctx { uint64_t used_bidi_streams; /* bidi streams we have opened */
uint64_t max_bidi_streams; /* max bidi streams we can open */
int qlogfd;
- BIT(conn_closed); /* connection is closed */
+ BIT(shutdown_started); /* queued shutdown packets */
};
/* How to access `call_data` from a cf_ngtcp2 filter */
@@ -162,7 +162,6 @@ struct h3_stream_ctx { struct bufq sendbuf; /* h3 request body */
struct h1_req_parser h1; /* h1 request parsing */
size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */
- size_t upload_blocked_len; /* the amount written last and EGAINed */
curl_uint64_t error3; /* HTTP/3 stream error code */
curl_off_t upload_left; /* number of request bytes left to upload */
int status_code; /* HTTP status code */
@@ -198,7 +197,7 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf, struct cf_ngtcp2_ctx *ctx = cf->ctx;
struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
- if(!data || !data->req.p.http) {
+ if(!data) {
failf(data, "initialization failure, transfer not http initialized");
return CURLE_FAILED_INIT;
}
@@ -326,8 +325,8 @@ static void pktx_update_time(struct pkt_io_ctx *pktx, struct cf_ngtcp2_ctx *ctx = cf->ctx;
vquic_ctx_update_time(&ctx->q);
- pktx->ts = ctx->q.last_op.tv_sec * NGTCP2_SECONDS +
- ctx->q.last_op.tv_usec * NGTCP2_MICROSECONDS;
+ pktx->ts = (ngtcp2_tstamp)ctx->q.last_op.tv_sec * NGTCP2_SECONDS +
+ (ngtcp2_tstamp)ctx->q.last_op.tv_usec * NGTCP2_MICROSECONDS;
}
static void pktx_init(struct pkt_io_ctx *pktx,
@@ -417,7 +416,7 @@ static void quic_settings(struct cf_ngtcp2_ctx *ctx, }
}
-static int init_ngh3_conn(struct Curl_cfilter *cf);
+static CURLcode init_ngh3_conn(struct Curl_cfilter *cf);
static int cb_handshake_completed(ngtcp2_conn *tconn, void *user_data)
{
@@ -506,8 +505,8 @@ static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags, /* number of bytes inside buflen which consists of framing overhead
* including QPACK HEADERS. In other words, it does not consume payload of
* DATA frame. */
- ngtcp2_conn_extend_max_stream_offset(tconn, stream_id, nconsumed);
- ngtcp2_conn_extend_max_offset(tconn, nconsumed);
+ ngtcp2_conn_extend_max_stream_offset(tconn, stream_id, (uint64_t)nconsumed);
+ ngtcp2_conn_extend_max_offset(tconn, (uint64_t)nconsumed);
return 0;
}
@@ -663,7 +662,7 @@ static void cb_rand(uint8_t *dest, size_t destlen, result = Curl_rand(NULL, dest, destlen);
if(result) {
- /* cb_rand is only used for non-cryptographic context. If Curl_rand
+ /* cb_rand is only used for non-cryptographic context. If Curl_rand
failed, just fill 0 and call it *random*. */
memset(dest, 0, destlen);
}
@@ -798,7 +797,8 @@ static CURLcode check_and_set_expiry(struct Curl_cfilter *cf, if(timeout % NGTCP2_MILLISECONDS) {
timeout += NGTCP2_MILLISECONDS;
}
- Curl_expire(data, timeout / NGTCP2_MILLISECONDS, EXPIRE_QUIC);
+ Curl_expire(data, (timediff_t)(timeout / NGTCP2_MILLISECONDS),
+ EXPIRE_QUIC);
}
}
return CURLE_OK;
@@ -815,6 +815,9 @@ static void cf_ngtcp2_adjust_pollset(struct Curl_cfilter *cf, return;
Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send);
+ if(!want_send && !Curl_bufq_is_empty(&ctx->q.sendbuf))
+ want_send = TRUE;
+
if(want_recv || want_send) {
struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
struct cf_call_data save;
@@ -954,7 +957,7 @@ static int cb_h3_end_headers(nghttp3_conn *conn, int64_t sid, if(!stream)
return 0;
- /* add a CRLF only if we've received some headers */
+ /* add a CRLF only if we have received some headers */
h3_xfer_write_resp_hd(cf, data, stream, STRCONST("\r\n"), stream->closed);
CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] end_headers, status=%d",
@@ -1091,7 +1094,7 @@ static nghttp3_callbacks ngh3_callbacks = { NULL /* recv_settings */
};
-static int init_ngh3_conn(struct Curl_cfilter *cf)
+static CURLcode init_ngh3_conn(struct Curl_cfilter *cf)
{
struct cf_ngtcp2_ctx *ctx = cf->ctx;
CURLcode result;
@@ -1202,7 +1205,7 @@ static ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, pktx_init(&pktx, cf, data);
- if(!stream || ctx->conn_closed) {
+ if(!stream || ctx->shutdown_started) {
*err = CURLE_RECV_ERROR;
goto out;
}
@@ -1268,8 +1271,8 @@ static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id, Curl_bufq_skip(&stream->sendbuf, skiplen);
stream->sendbuf_len_in_flight -= skiplen;
- /* Everything ACKed, we resume upload processing */
- if(!stream->sendbuf_len_in_flight) {
+ /* Resume upload processing if we have more data to send */
+ if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) {
int rv = nghttp3_conn_resume_stream(conn, stream_id);
if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
return NGTCP2_ERR_CALLBACK_FAILURE;
@@ -1504,7 +1507,7 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, }
if(!stream || stream->id < 0) {
- if(ctx->conn_closed) {
+ if(ctx->shutdown_started) {
CURL_TRC_CF(data, cf, "cannot open stream on closed connection");
*err = CURLE_SEND_ERROR;
sent = -1;
@@ -1524,21 +1527,6 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, sent = -1;
goto out;
}
- else if(stream->upload_blocked_len) {
- /* the data in `buf` has already been submitted or added to the
- * buffers, but have been EAGAINed on the last invocation. */
- DEBUGASSERT(len >= stream->upload_blocked_len);
- if(len < stream->upload_blocked_len) {
- /* Did we get called again with a smaller `len`? This should not
- * happen. We are not prepared to handle that. */
- failf(data, "HTTP/3 send again with decreased length");
- *err = CURLE_HTTP3;
- sent = -1;
- goto out;
- }
- sent = (ssize_t)stream->upload_blocked_len;
- stream->upload_blocked_len = 0;
- }
else if(stream->closed) {
if(stream->resp_hds_complete) {
/* Server decided to close the stream after having sent us a final
@@ -1558,7 +1546,7 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, sent = -1;
goto out;
}
- else if(ctx->conn_closed) {
+ else if(ctx->shutdown_started) {
CURL_TRC_CF(data, cf, "cannot send on closed connection");
*err = CURLE_SEND_ERROR;
sent = -1;
@@ -1582,18 +1570,6 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, sent = -1;
}
- if(stream && sent > 0 && stream->sendbuf_len_in_flight) {
- /* We have unacknowledged DATA and cannot report success to our
- * caller. Instead we EAGAIN and remember how much we have already
- * "written" into our various internal connection buffers. */
- stream->upload_blocked_len = sent;
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_send(len=%zu), "
- "%zu bytes in flight -> EGAIN", stream->id, len,
- stream->sendbuf_len_in_flight);
- *err = CURLE_AGAIN;
- sent = -1;
- }
-
out:
result = check_and_set_expiry(cf, data, &pktx);
if(result) {
@@ -1631,7 +1607,7 @@ static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen, ++pktx->pkt_count;
ngtcp2_addr_init(&path.local, (struct sockaddr *)&ctx->q.local_addr,
- ctx->q.local_addrlen);
+ (socklen_t)ctx->q.local_addrlen);
ngtcp2_addr_init(&path.remote, (struct sockaddr *)remote_addr,
remote_addrlen);
pi.ecn = (uint8_t)ecn;
@@ -1867,7 +1843,7 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf, DEBUGASSERT(nread > 0);
if(pktcnt == 0) {
/* first packet in buffer. This is either of a known, "good"
- * payload size or it is a PMTUD. We'll see. */
+ * payload size or it is a PMTUD. We will see. */
gsolen = (size_t)nread;
}
else if((size_t)nread > gsolen ||
@@ -1961,7 +1937,8 @@ static CURLcode cf_ngtcp2_data_event(struct Curl_cfilter *cf, struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
if(stream && !stream->send_closed) {
stream->send_closed = TRUE;
- stream->upload_left = Curl_bufq_len(&stream->sendbuf);
+ stream->upload_left = Curl_bufq_len(&stream->sendbuf) -
+ stream->sendbuf_len_in_flight;
(void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id);
}
break;
@@ -2007,29 +1984,97 @@ static void cf_ngtcp2_ctx_clear(struct cf_ngtcp2_ctx *ctx) ctx->call_data = save;
}
-static void cf_ngtcp2_conn_close(struct Curl_cfilter *cf,
- struct Curl_easy *data)
+static CURLcode cf_ngtcp2_shutdown(struct Curl_cfilter *cf,
+ struct Curl_easy *data, bool *done)
{
struct cf_ngtcp2_ctx *ctx = cf->ctx;
- if(ctx && ctx->qconn && !ctx->conn_closed) {
+ struct cf_call_data save;
+ struct pkt_io_ctx pktx;
+ CURLcode result = CURLE_OK;
+
+ if(cf->shutdown || !ctx->qconn) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+
+ CF_DATA_SAVE(save, cf, data);
+ *done = FALSE;
+ pktx_init(&pktx, cf, data);
+
+ if(!ctx->shutdown_started) {
char buffer[NGTCP2_MAX_UDP_PAYLOAD_SIZE];
- struct pkt_io_ctx pktx;
- ngtcp2_ssize rc;
-
- ctx->conn_closed = TRUE;
- pktx_init(&pktx, cf, data);
- rc = ngtcp2_conn_write_connection_close(ctx->qconn, NULL, /* path */
- NULL, /* pkt_info */
- (uint8_t *)buffer, sizeof(buffer),
- &ctx->last_error, pktx.ts);
- CURL_TRC_CF(data, cf, "closing connection(err_type=%d, err_code=%"
+ ngtcp2_ssize nwritten;
+
+ if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) {
+ CURL_TRC_CF(data, cf, "shutdown, flushing sendbuf");
+ result = cf_progress_egress(cf, data, &pktx);
+ if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) {
+ CURL_TRC_CF(data, cf, "sending shutdown packets blocked");
+ result = CURLE_OK;
+ goto out;
+ }
+ else if(result) {
+ CURL_TRC_CF(data, cf, "shutdown, error %d flushing sendbuf", result);
+ *done = TRUE;
+ goto out;
+ }
+ }
+
+ ctx->shutdown_started = TRUE;
+ nwritten = ngtcp2_conn_write_connection_close(
+ ctx->qconn, NULL, /* path */
+ NULL, /* pkt_info */
+ (uint8_t *)buffer, sizeof(buffer),
+ &ctx->last_error, pktx.ts);
+ CURL_TRC_CF(data, cf, "start shutdown(err_type=%d, err_code=%"
CURL_PRIu64 ") -> %d", ctx->last_error.type,
- (curl_uint64_t)ctx->last_error.error_code, (int)rc);
- if(rc > 0) {
- while((send(ctx->q.sockfd, buffer, (SEND_TYPE_ARG3)rc, 0) == -1) &&
- SOCKERRNO == EINTR);
+ (curl_uint64_t)ctx->last_error.error_code, (int)nwritten);
+ if(nwritten > 0) {
+ Curl_bufq_write(&ctx->q.sendbuf, (const unsigned char *)buffer,
+ (size_t)nwritten, &result);
+ if(result) {
+ CURL_TRC_CF(data, cf, "error %d adding shutdown packets to sendbuf, "
+ "aborting shutdown", result);
+ goto out;
+ }
+ ctx->q.no_gso = TRUE;
+ ctx->q.gsolen = (size_t)nwritten;
+ ctx->q.split_len = 0;
}
}
+
+ if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) {
+ CURL_TRC_CF(data, cf, "shutdown, flushing egress");
+ result = vquic_flush(cf, data, &ctx->q);
+ if(result == CURLE_AGAIN) {
+ CURL_TRC_CF(data, cf, "sending shutdown packets blocked");
+ result = CURLE_OK;
+ goto out;
+ }
+ else if(result) {
+ CURL_TRC_CF(data, cf, "shutdown, error %d flushing sendbuf", result);
+ *done = TRUE;
+ goto out;
+ }
+ }
+
+ if(Curl_bufq_is_empty(&ctx->q.sendbuf)) {
+ /* Sent everything off. ngtcp2 seems to have no support for graceful
+ * shutdowns. So, we are done. */
+ CURL_TRC_CF(data, cf, "shutdown completely sent off, done");
+ *done = TRUE;
+ result = CURLE_OK;
+ }
+out:
+ CF_DATA_RESTORE(cf, save);
+ return result;
+}
+
+static void cf_ngtcp2_conn_close(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ bool done;
+ cf_ngtcp2_shutdown(cf, data, &done);
}
static void cf_ngtcp2_close(struct Curl_cfilter *cf, struct Curl_easy *data)
@@ -2105,7 +2150,7 @@ static CURLcode tls_ctx_setup(struct Curl_cfilter *cf, return CURLE_FAILED_INIT;
}
#endif /* !OPENSSL_IS_BORINGSSL && !OPENSSL_IS_AWSLC */
- /* Enable the session cache because it's a prerequisite for the
+ /* Enable the session cache because it is a prerequisite for the
* "new session" callback. Use the "external storage" mode to prevent
* OpenSSL from creating an internal session cache.
*/
@@ -2120,7 +2165,7 @@ static CURLcode tls_ctx_setup(struct Curl_cfilter *cf, return CURLE_FAILED_INIT;
}
#elif defined(USE_WOLFSSL)
- if(ngtcp2_crypto_wolfssl_configure_client_context(ctx->ssl_ctx) != 0) {
+ if(ngtcp2_crypto_wolfssl_configure_client_context(ctx->wssl.ctx) != 0) {
failf(data, "ngtcp2_crypto_wolfssl_configure_client_context failed");
return CURLE_FAILED_INIT;
}
@@ -2196,7 +2241,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, (struct sockaddr *)&ctx->q.local_addr,
ctx->q.local_addrlen);
ngtcp2_addr_init(&ctx->connected_path.remote,
- &sockaddr->sa_addr, sockaddr->addrlen);
+ &sockaddr->sa_addr, (socklen_t)sockaddr->addrlen);
rc = ngtcp2_conn_client_new(&ctx->qconn, &ctx->dcid, &ctx->scid,
&ctx->connected_path,
@@ -2211,7 +2256,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, #elif defined(USE_GNUTLS)
ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.gtls.session);
#else
- ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.ssl);
+ ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.wssl.handle);
#endif
ngtcp2_ccerr_default(&ctx->last_error);
@@ -2331,7 +2376,7 @@ static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf, * by callback. QUIC counts the number over the lifetime of the
* connection, ever increasing.
* We count the *open* transfers plus the budget for new ones. */
- if(!ctx->qconn || ctx->conn_closed) {
+ if(!ctx->qconn || ctx->shutdown_started) {
*pres1 = 0;
}
else if(ctx->max_bidi_streams) {
@@ -2343,7 +2388,7 @@ static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf, *pres1 = (max_streams > INT_MAX)? INT_MAX : (int)max_streams;
}
else /* transport params not arrived yet? take our default. */
- *pres1 = Curl_multi_max_concurrent_streams(data->multi);
+ *pres1 = (int)Curl_multi_max_concurrent_streams(data->multi);
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));
@@ -2389,7 +2434,7 @@ static bool cf_ngtcp2_conn_is_alive(struct Curl_cfilter *cf, CF_DATA_SAVE(save, cf, data);
*input_pending = FALSE;
- if(!ctx->qconn || ctx->conn_closed)
+ if(!ctx->qconn || ctx->shutdown_started)
goto out;
/* Both sides of the QUIC connection announce they max idle times in
@@ -2416,8 +2461,8 @@ static bool cf_ngtcp2_conn_is_alive(struct Curl_cfilter *cf, alive = TRUE;
if(*input_pending) {
CURLcode result;
- /* This happens before we've sent off a request and the connection is
- not in use by any other transfer, there shouldn't be any data here,
+ /* This happens before we have sent off a request and the connection is
+ not in use by any other transfer, there should not be any data here,
only "protocol frames" */
*input_pending = FALSE;
result = cf_progress_ingress(cf, data, NULL);
@@ -2437,6 +2482,7 @@ struct Curl_cftype Curl_cft_http3 = { cf_ngtcp2_destroy,
cf_ngtcp2_connect,
cf_ngtcp2_close,
+ cf_ngtcp2_shutdown,
Curl_cf_def_get_host,
cf_ngtcp2_adjust_pollset,
cf_ngtcp2_data_pending,
diff --git a/libs/libcurl/src/vquic/curl_osslq.c b/libs/libcurl/src/vquic/curl_osslq.c index 3ffdfecf82..59c1f5053e 100644 --- a/libs/libcurl/src/vquic/curl_osslq.c +++ b/libs/libcurl/src/vquic/curl_osslq.c @@ -71,7 +71,7 @@ /* The pool keeps spares around and half of a full stream window
* seems good. More does not seem to improve performance.
* The benefit of the pool is that stream buffer to not keep
- * spares. So memory consumption goes down when streams run empty,
+ * spares. Memory consumption goes down when streams run empty,
* have a large upload done, etc. */
#define H3_STREAM_POOL_SPARES \
(H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE ) / 2
@@ -232,7 +232,7 @@ static CURLcode cf_osslq_stream_open(struct cf_osslq_stream *s, if(!s->ssl) {
return CURLE_FAILED_INIT;
}
- s->id = SSL_get_stream_id(s->ssl);
+ s->id = (curl_int64_t)SSL_get_stream_id(s->ssl);
SSL_set_app_data(s->ssl, user_data);
return CURLE_OK;
}
@@ -294,10 +294,10 @@ struct cf_osslq_ctx { size_t max_stream_window; /* max flow window for one stream */
uint64_t max_idle_ms; /* max idle time for QUIC connection */
BIT(got_first_byte); /* if first byte was received */
-#ifdef USE_OPENSSL
BIT(x509_store_setup); /* if x509 store has been set up */
BIT(protocol_shutdown); /* QUIC connection is shut down */
-#endif
+ BIT(need_recv); /* QUIC connection needs to receive */
+ BIT(need_send); /* QUIC connection needs to send */
};
static void cf_osslq_ctx_clear(struct cf_osslq_ctx *ctx)
@@ -316,6 +316,77 @@ static void cf_osslq_ctx_clear(struct cf_osslq_ctx *ctx) ctx->call_data = save;
}
+static CURLcode cf_osslq_shutdown(struct Curl_cfilter *cf,
+ struct Curl_easy *data, bool *done)
+{
+ struct cf_osslq_ctx *ctx = cf->ctx;
+ struct cf_call_data save;
+ CURLcode result = CURLE_OK;
+ int rc;
+
+ CF_DATA_SAVE(save, cf, data);
+
+ if(cf->shutdown || ctx->protocol_shutdown) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+
+ CF_DATA_SAVE(save, cf, data);
+ *done = FALSE;
+ ctx->need_send = FALSE;
+ ctx->need_recv = FALSE;
+
+ rc = SSL_shutdown_ex(ctx->tls.ossl.ssl,
+ SSL_SHUTDOWN_FLAG_NO_BLOCK, NULL, 0);
+ if(rc == 0) { /* ongoing */
+ CURL_TRC_CF(data, cf, "shutdown ongoing");
+ ctx->need_recv = TRUE;
+ goto out;
+ }
+ else if(rc == 1) { /* done */
+ CURL_TRC_CF(data, cf, "shutdown finished");
+ *done = TRUE;
+ goto out;
+ }
+ else {
+ long sslerr;
+ char err_buffer[256];
+ int err = SSL_get_error(ctx->tls.ossl.ssl, rc);
+
+ switch(err) {
+ case SSL_ERROR_NONE:
+ case SSL_ERROR_ZERO_RETURN:
+ CURL_TRC_CF(data, cf, "shutdown not received, but closed");
+ *done = TRUE;
+ goto out;
+ case SSL_ERROR_WANT_READ:
+ /* SSL has send its notify and now wants to read the reply
+ * from the server. We are not really interested in that. */
+ CURL_TRC_CF(data, cf, "shutdown sent, want receive");
+ ctx->need_recv = TRUE;
+ goto out;
+ case SSL_ERROR_WANT_WRITE:
+ CURL_TRC_CF(data, cf, "shutdown send blocked");
+ ctx->need_send = TRUE;
+ goto out;
+ default:
+ /* We give up on this. */
+ sslerr = ERR_get_error();
+ CURL_TRC_CF(data, cf, "shutdown, ignore recv error: '%s', errno %d",
+ (sslerr ?
+ osslq_strerror(sslerr, err_buffer, sizeof(err_buffer)) :
+ osslq_SSL_ERROR_to_str(err)),
+ SOCKERRNO);
+ *done = TRUE;
+ result = CURLE_OK;
+ goto out;
+ }
+ }
+out:
+ CF_DATA_RESTORE(cf, save);
+ return result;
+}
+
static void cf_osslq_close(struct Curl_cfilter *cf, struct Curl_easy *data)
{
struct cf_osslq_ctx *ctx = cf->ctx;
@@ -323,8 +394,13 @@ static void cf_osslq_close(struct Curl_cfilter *cf, struct Curl_easy *data) CF_DATA_SAVE(save, cf, data);
if(ctx && ctx->tls.ossl.ssl) {
- /* TODO: send connection close */
CURL_TRC_CF(data, cf, "cf_osslq_close()");
+ if(!cf->shutdown && !ctx->protocol_shutdown) {
+ /* last best effort, which OpenSSL calls a "rapid" shutdown. */
+ SSL_shutdown_ex(ctx->tls.ossl.ssl,
+ (SSL_SHUTDOWN_FLAG_NO_BLOCK | SSL_SHUTDOWN_FLAG_RAPID),
+ NULL, 0);
+ }
cf_osslq_ctx_clear(ctx);
}
@@ -355,12 +431,12 @@ static CURLcode cf_osslq_h3conn_add_stream(struct cf_osslq_h3conn *h3, struct Curl_easy *data)
{
struct cf_osslq_ctx *ctx = cf->ctx;
- int64_t stream_id = SSL_get_stream_id(stream_ssl);
+ curl_int64_t stream_id = (curl_int64_t)SSL_get_stream_id(stream_ssl);
if(h3->remote_ctrl_n >= ARRAYSIZE(h3->remote_ctrl)) {
/* rejected, we are full */
CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] rejecting remote stream",
- (curl_int64_t)stream_id);
+ stream_id);
SSL_free(stream_ssl);
return CURLE_FAILED_INIT;
}
@@ -371,12 +447,12 @@ static CURLcode cf_osslq_h3conn_add_stream(struct cf_osslq_h3conn *h3, nstream->ssl = stream_ssl;
Curl_bufq_initp(&nstream->recvbuf, &ctx->stream_bufcp, 1, BUFQ_OPT_NONE);
CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] accepted remote uni stream",
- (curl_int64_t)stream_id);
+ stream_id);
break;
}
default:
CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] reject remote non-uni-read"
- " stream", (curl_int64_t)stream_id);
+ " stream", stream_id);
SSL_free(stream_ssl);
return CURLE_FAILED_INIT;
}
@@ -440,7 +516,7 @@ static CURLcode cf_osslq_ssl_err(struct Curl_cfilter *cf, /* detail is already set to the SSL error above */
- /* If we e.g. use SSLv2 request-method and the server doesn't like us
+ /* If we e.g. use SSLv2 request-method and the server does not like us
* (RST connection, etc.), OpenSSL gives no explanation whatsoever and
* the SO_ERROR is also lost.
*/
@@ -484,7 +560,6 @@ struct h3_stream_ctx { struct bufq recvbuf; /* h3 response body */
struct h1_req_parser h1; /* h1 request parsing */
size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */
- size_t upload_blocked_len; /* the amount written last and EGAINed */
size_t recv_buf_nonflow; /* buffered bytes, not counting for flow control */
curl_uint64_t error3; /* HTTP/3 stream error code */
curl_off_t upload_left; /* number of request bytes left to upload */
@@ -521,7 +596,7 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf, struct cf_osslq_ctx *ctx = cf->ctx;
struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
- if(!data || !data->req.p.http) {
+ if(!data) {
failf(data, "initialization failure, transfer not http initialized");
return CURLE_FAILED_INIT;
}
@@ -829,7 +904,7 @@ static int cb_h3_end_headers(nghttp3_conn *conn, int64_t sid, if(!stream)
return 0;
- /* add a CRLF only if we've received some headers */
+ /* add a CRLF only if we have received some headers */
result = write_resp_raw(cf, data, "\r\n", 2, FALSE);
if(result) {
return -1;
@@ -977,8 +1052,8 @@ static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id, Curl_bufq_skip(&stream->sendbuf, skiplen);
stream->sendbuf_len_in_flight -= skiplen;
- /* Everything ACKed, we resume upload processing */
- if(!stream->sendbuf_len_in_flight) {
+ /* Resume upload processing if we have more data to send */
+ if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) {
int rv = nghttp3_conn_resume_stream(conn, stream_id);
if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
return NGHTTP3_ERR_CALLBACK_FAILURE;
@@ -1442,19 +1517,12 @@ static CURLcode h3_send_streams(struct Curl_cfilter *cf, for(i = 0; (i < n) && !blocked; ++i) {
/* Without stream->s.ssl, we closed that already, so
* pretend the write did succeed. */
-#ifdef SSL_WRITE_FLAG_CONCLUDE
- /* Since OpenSSL v3.3.x, on last chunk set EOS if needed */
uint64_t flags = (eos && ((i + 1) == n))? SSL_WRITE_FLAG_CONCLUDE : 0;
written = vec[i].len;
ok = !s->ssl || SSL_write_ex2(s->ssl, vec[i].base, vec[i].len, flags,
&written);
if(ok && flags & SSL_WRITE_FLAG_CONCLUDE)
eos_written = TRUE;
-#else
- written = vec[i].len;
- ok = !s->ssl || SSL_write_ex(s->ssl, vec[i].base, vec[i].len,
- &written);
-#endif
if(ok) {
/* As OpenSSL buffers the data, we count this as acknowledged
* from nghttp3's point of view */
@@ -1766,7 +1834,7 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, *err = cf_osslq_stream_open(&stream->s, ctx->tls.ossl.ssl, 0,
&ctx->stream_bufcp, data);
if(*err) {
- failf(data, "can't get bidi streams");
+ failf(data, "cannot get bidi streams");
*err = CURLE_SEND_ERROR;
goto out;
}
@@ -1867,21 +1935,6 @@ static ssize_t cf_osslq_send(struct Curl_cfilter *cf, struct Curl_easy *data, }
stream = H3_STREAM_CTX(ctx, data);
}
- else if(stream->upload_blocked_len) {
- /* the data in `buf` has already been submitted or added to the
- * buffers, but have been EAGAINed on the last invocation. */
- DEBUGASSERT(len >= stream->upload_blocked_len);
- if(len < stream->upload_blocked_len) {
- /* Did we get called again with a smaller `len`? This should not
- * happen. We are not prepared to handle that. */
- failf(data, "HTTP/3 send again with decreased length");
- *err = CURLE_HTTP3;
- nwritten = -1;
- goto out;
- }
- nwritten = (ssize_t)stream->upload_blocked_len;
- stream->upload_blocked_len = 0;
- }
else if(stream->closed) {
if(stream->resp_hds_complete) {
/* Server decided to close the stream after having sent us a final
@@ -1919,18 +1972,6 @@ static ssize_t cf_osslq_send(struct Curl_cfilter *cf, struct Curl_easy *data, nwritten = -1;
}
- if(stream && nwritten > 0 && stream->sendbuf_len_in_flight) {
- /* We have unacknowledged DATA and cannot report success to our
- * caller. Instead we EAGAIN and remember how much we have already
- * "written" into our various internal connection buffers. */
- stream->upload_blocked_len = nwritten;
- CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_send(len=%zu), "
- "%zu bytes in flight -> EGAIN", stream->s.id, len,
- stream->sendbuf_len_in_flight);
- *err = CURLE_AGAIN;
- nwritten = -1;
- }
-
out:
result = check_and_set_expiry(cf, data);
CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] cf_send(len=%zu) -> %zd, %d",
@@ -2090,7 +2131,8 @@ static CURLcode cf_osslq_data_event(struct Curl_cfilter *cf, struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
if(stream && !stream->send_closed) {
stream->send_closed = TRUE;
- stream->upload_left = Curl_bufq_len(&stream->sendbuf);
+ stream->upload_left = Curl_bufq_len(&stream->sendbuf) -
+ stream->sendbuf_len_in_flight;
(void)nghttp3_conn_resume_stream(ctx->h3.conn, stream->s.id);
}
break;
@@ -2149,8 +2191,8 @@ static bool cf_osslq_conn_is_alive(struct Curl_cfilter *cf, alive = TRUE;
if(*input_pending) {
CURLcode result;
- /* This happens before we've sent off a request and the connection is
- not in use by any other transfer, there shouldn't be any data here,
+ /* This happens before we have sent off a request and the connection is
+ not in use by any other transfer, there should not be any data here,
only "protocol frames" */
*input_pending = FALSE;
result = cf_progress_ingress(cf, data);
@@ -2189,6 +2231,10 @@ static void cf_osslq_adjust_pollset(struct Curl_cfilter *cf, SSL_net_read_desired(ctx->tls.ossl.ssl),
SSL_net_write_desired(ctx->tls.ossl.ssl));
}
+ else if(ctx->need_recv || ctx->need_send) {
+ Curl_pollset_set(data, ps, ctx->q.sockfd,
+ ctx->need_recv, ctx->need_send);
+ }
}
}
@@ -2252,6 +2298,7 @@ struct Curl_cftype Curl_cft_http3 = { cf_osslq_destroy,
cf_osslq_connect,
cf_osslq_close,
+ cf_osslq_shutdown,
Curl_cf_def_get_host,
cf_osslq_adjust_pollset,
cf_osslq_data_pending,
diff --git a/libs/libcurl/src/vquic/curl_quiche.c b/libs/libcurl/src/vquic/curl_quiche.c index 0a2fab2213..66e3592bb2 100644 --- a/libs/libcurl/src/vquic/curl_quiche.c +++ b/libs/libcurl/src/vquic/curl_quiche.c @@ -64,11 +64,10 @@ #define H3_STREAM_WINDOW_SIZE (128 * 1024)
#define H3_STREAM_CHUNK_SIZE (16 * 1024)
-/* The pool keeps spares around and half of a full stream windows
- * seems good. More does not seem to improve performance.
- * The benefit of the pool is that stream buffer to not keep
- * spares. So memory consumption goes down when streams run empty,
- * have a large upload done, etc. */
+/* The pool keeps spares around and half of a full stream windows seems good.
+ * More does not seem to improve performance. The benefit of the pool is that
+ * stream buffer to not keep spares. Memory consumption goes down when streams
+ * run empty, have a large upload done, etc. */
#define H3_STREAM_POOL_SPARES \
(H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE ) / 2
/* Receive and Send max number of chunks just follows from the
@@ -103,6 +102,7 @@ struct cf_quiche_ctx { curl_off_t data_recvd;
BIT(goaway); /* got GOAWAY from server */
BIT(x509_store_setup); /* if x509 store has been set up */
+ BIT(shutdown_started); /* queued shutdown packets */
};
#ifdef DEBUG_QUICHE
@@ -1120,7 +1120,7 @@ out: nwritten = -1;
}
CURL_TRC_CF(data, cf, "[%" CURL_PRIu64 "] cf_send(len=%zu) -> %zd, %d",
- stream? stream->id : -1, len, nwritten, *err);
+ stream? stream->id : (uint64_t)~0, len, nwritten, *err);
return nwritten;
}
@@ -1271,7 +1271,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, ctx->cfg = quiche_config_new(QUICHE_PROTOCOL_VERSION);
if(!ctx->cfg) {
- failf(data, "can't create quiche config");
+ failf(data, "cannot create quiche config");
return CURLE_FAILED_INIT;
}
quiche_config_enable_pacing(ctx->cfg, false);
@@ -1322,7 +1322,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, &sockaddr->sa_addr, sockaddr->addrlen,
ctx->cfg, ctx->tls.ossl.ssl, false);
if(!ctx->qconn) {
- failf(data, "can't create quiche connection");
+ failf(data, "cannot create quiche connection");
return CURLE_OUT_OF_MEMORY;
}
@@ -1464,18 +1464,60 @@ out: return result;
}
+static CURLcode cf_quiche_shutdown(struct Curl_cfilter *cf,
+ struct Curl_easy *data, bool *done)
+{
+ struct cf_quiche_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_OK;
+
+ if(cf->shutdown || !ctx || !ctx->qconn) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+
+ *done = FALSE;
+ if(!ctx->shutdown_started) {
+ int err;
+
+ ctx->shutdown_started = TRUE;
+ vquic_ctx_update_time(&ctx->q);
+ err = quiche_conn_close(ctx->qconn, TRUE, 0, NULL, 0);
+ if(err) {
+ CURL_TRC_CF(data, cf, "error %d adding shutdown packet, "
+ "aborting shutdown", err);
+ result = CURLE_SEND_ERROR;
+ goto out;
+ }
+ }
+
+ if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) {
+ CURL_TRC_CF(data, cf, "shutdown, flushing sendbuf");
+ result = cf_flush_egress(cf, data);
+ if(result)
+ goto out;
+ }
+
+ if(Curl_bufq_is_empty(&ctx->q.sendbuf)) {
+ /* sent everything, quiche does not seem to support a graceful
+ * shutdown waiting for a reply, so ware done. */
+ CURL_TRC_CF(data, cf, "shutdown completely sent off, done");
+ *done = TRUE;
+ }
+ else {
+ CURL_TRC_CF(data, cf, "shutdown sending blocked");
+ }
+
+out:
+ return result;
+}
+
static void cf_quiche_close(struct Curl_cfilter *cf, struct Curl_easy *data)
{
struct cf_quiche_ctx *ctx = cf->ctx;
if(ctx) {
- if(ctx->qconn) {
- vquic_ctx_update_time(&ctx->q);
- (void)quiche_conn_close(ctx->qconn, TRUE, 0, NULL, 0);
- /* flushing the egress is not a failsafe way to deliver all the
- outstanding packets, but we also don't want to get stuck here... */
- (void)cf_flush_egress(cf, data);
- }
+ bool done;
+ (void)cf_quiche_shutdown(cf, data, &done);
cf_quiche_ctx_clear(ctx);
}
}
@@ -1559,8 +1601,8 @@ static bool cf_quiche_conn_is_alive(struct Curl_cfilter *cf, return FALSE;
if(*input_pending) {
- /* This happens before we've sent off a request and the connection is
- not in use by any other transfer, there shouldn't be any data here,
+ /* This happens before we have sent off a request and the connection is
+ not in use by any other transfer, there should not be any data here,
only "protocol frames" */
*input_pending = FALSE;
if(cf_process_ingress(cf, data))
@@ -1580,6 +1622,7 @@ struct Curl_cftype Curl_cft_http3 = { cf_quiche_destroy,
cf_quiche_connect,
cf_quiche_close,
+ cf_quiche_shutdown,
Curl_cf_def_get_host,
cf_quiche_adjust_pollset,
cf_quiche_data_pending,
diff --git a/libs/libcurl/src/vquic/vquic-tls.c b/libs/libcurl/src/vquic/vquic-tls.c index df8a4f5c19..d3ddce2c53 100644 --- a/libs/libcurl/src/vquic/vquic-tls.c +++ b/libs/libcurl/src/vquic/vquic-tls.c @@ -76,7 +76,7 @@ static void keylog_callback(const WOLFSSL *ssl, const char *line) }
#endif
-static CURLcode curl_wssl_init_ctx(struct curl_tls_ctx *ctx,
+static CURLcode Curl_wssl_init_ctx(struct curl_tls_ctx *ctx,
struct Curl_cfilter *cf,
struct Curl_easy *data,
Curl_vquic_tls_ctx_setup *cb_setup,
@@ -91,8 +91,8 @@ static CURLcode curl_wssl_init_ctx(struct curl_tls_ctx *ctx, goto out;
}
- ctx->ssl_ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method());
- if(!ctx->ssl_ctx) {
+ ctx->wssl.ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method());
+ if(!ctx->wssl.ctx) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
@@ -103,9 +103,9 @@ static CURLcode curl_wssl_init_ctx(struct curl_tls_ctx *ctx, goto out;
}
- wolfSSL_CTX_set_default_verify_paths(ctx->ssl_ctx);
+ wolfSSL_CTX_set_default_verify_paths(ctx->wssl.ctx);
- if(wolfSSL_CTX_set_cipher_list(ctx->ssl_ctx, conn_config->cipher_list13 ?
+ if(wolfSSL_CTX_set_cipher_list(ctx->wssl.ctx, conn_config->cipher_list13 ?
conn_config->cipher_list13 :
QUIC_CIPHERS) != 1) {
char error_buffer[256];
@@ -115,7 +115,7 @@ static CURLcode curl_wssl_init_ctx(struct curl_tls_ctx *ctx, goto out;
}
- if(wolfSSL_CTX_set1_groups_list(ctx->ssl_ctx, conn_config->curves ?
+ if(wolfSSL_CTX_set1_groups_list(ctx->wssl.ctx, conn_config->curves ?
conn_config->curves :
(char *)QUIC_GROUPS) != 1) {
failf(data, "wolfSSL failed to set curves");
@@ -127,7 +127,7 @@ static CURLcode curl_wssl_init_ctx(struct curl_tls_ctx *ctx, Curl_tls_keylog_open();
if(Curl_tls_keylog_enabled()) {
#if defined(HAVE_SECRET_CALLBACK)
- wolfSSL_CTX_set_keylog_callback(ctx->ssl_ctx, keylog_callback);
+ wolfSSL_CTX_set_keylog_callback(ctx->wssl.ctx, keylog_callback);
#else
failf(data, "wolfSSL was built without keylog callback");
result = CURLE_NOT_BUILT_IN;
@@ -139,12 +139,12 @@ static CURLcode curl_wssl_init_ctx(struct curl_tls_ctx *ctx, const char * const ssl_cafile = conn_config->CAfile;
const char * const ssl_capath = conn_config->CApath;
- wolfSSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_PEER, NULL);
+ wolfSSL_CTX_set_verify(ctx->wssl.ctx, SSL_VERIFY_PEER, NULL);
if(ssl_cafile || ssl_capath) {
/* tell wolfSSL where to find CA certificates that are used to verify
the server's certificate. */
int rc =
- wolfSSL_CTX_load_verify_locations_ex(ctx->ssl_ctx, ssl_cafile,
+ wolfSSL_CTX_load_verify_locations_ex(ctx->wssl.ctx, ssl_cafile,
ssl_capath,
WOLFSSL_LOAD_FLAG_IGNORE_ERR);
if(SSL_SUCCESS != rc) {
@@ -161,20 +161,20 @@ static CURLcode curl_wssl_init_ctx(struct curl_tls_ctx *ctx, }
#ifdef CURL_CA_FALLBACK
else {
- /* verifying the peer without any CA certificates won't work so
+ /* verifying the peer without any CA certificates will not work so
use wolfssl's built-in default as fallback */
- wolfSSL_CTX_set_default_verify_paths(ctx->ssl_ctx);
+ wolfSSL_CTX_set_default_verify_paths(ctx->wssl.ctx);
}
#endif
}
else {
- wolfSSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_NONE, NULL);
+ wolfSSL_CTX_set_verify(ctx->wssl.ctx, SSL_VERIFY_NONE, NULL);
}
/* give application a chance to interfere with SSL set up. */
if(data->set.ssl.fsslctx) {
Curl_set_in_callback(data, true);
- result = (*data->set.ssl.fsslctx)(data, ctx->ssl_ctx,
+ result = (*data->set.ssl.fsslctx)(data, ctx->wssl.ctx,
data->set.ssl.fsslctxp);
Curl_set_in_callback(data, false);
if(result) {
@@ -185,36 +185,36 @@ static CURLcode curl_wssl_init_ctx(struct curl_tls_ctx *ctx, result = CURLE_OK;
out:
- if(result && ctx->ssl_ctx) {
- SSL_CTX_free(ctx->ssl_ctx);
- ctx->ssl_ctx = NULL;
+ if(result && ctx->wssl.ctx) {
+ SSL_CTX_free(ctx->wssl.ctx);
+ ctx->wssl.ctx = NULL;
}
return result;
}
/** SSL callbacks ***/
-static CURLcode curl_wssl_init_ssl(struct curl_tls_ctx *ctx,
+static CURLcode Curl_wssl_init_ssl(struct curl_tls_ctx *ctx,
struct Curl_easy *data,
struct ssl_peer *peer,
const char *alpn, size_t alpn_len,
void *user_data)
{
(void)data;
- DEBUGASSERT(!ctx->ssl);
- DEBUGASSERT(ctx->ssl_ctx);
- ctx->ssl = wolfSSL_new(ctx->ssl_ctx);
+ DEBUGASSERT(!ctx->wssl.handle);
+ DEBUGASSERT(ctx->wssl.ctx);
+ ctx->wssl.handle = wolfSSL_new(ctx->wssl.ctx);
- wolfSSL_set_app_data(ctx->ssl, user_data);
- wolfSSL_set_connect_state(ctx->ssl);
- wolfSSL_set_quic_use_legacy_codepoint(ctx->ssl, 0);
+ wolfSSL_set_app_data(ctx->wssl.handle, user_data);
+ wolfSSL_set_connect_state(ctx->wssl.handle);
+ wolfSSL_set_quic_use_legacy_codepoint(ctx->wssl.handle, 0);
if(alpn)
- wolfSSL_set_alpn_protos(ctx->ssl, (const unsigned char *)alpn,
- (int)alpn_len);
+ wolfSSL_set_alpn_protos(ctx->wssl.handle, (const unsigned char *)alpn,
+ (unsigned int)alpn_len);
if(peer->sni) {
- wolfSSL_UseSNI(ctx->ssl, WOLFSSL_SNI_HOST_NAME,
+ wolfSSL_UseSNI(ctx->wssl.handle, WOLFSSL_SNI_HOST_NAME,
peer->sni, (unsigned short)strlen(peer->sni));
}
@@ -243,11 +243,11 @@ CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx, (const unsigned char *)alpn, alpn_len,
cb_setup, cb_user_data, ssl_user_data);
#elif defined(USE_WOLFSSL)
- result = curl_wssl_init_ctx(ctx, cf, data, cb_setup, cb_user_data);
+ result = Curl_wssl_init_ctx(ctx, cf, data, cb_setup, cb_user_data);
if(result)
return result;
- return curl_wssl_init_ssl(ctx, data, peer, alpn, alpn_len, ssl_user_data);
+ return Curl_wssl_init_ssl(ctx, data, peer, alpn, alpn_len, ssl_user_data);
#else
#error "no TLS lib in used, should not happen"
return CURLE_FAILED_INIT;
@@ -262,15 +262,14 @@ void Curl_vquic_tls_cleanup(struct curl_tls_ctx *ctx) if(ctx->ossl.ssl_ctx)
SSL_CTX_free(ctx->ossl.ssl_ctx);
#elif defined(USE_GNUTLS)
- if(ctx->gtls.cred)
- gnutls_certificate_free_credentials(ctx->gtls.cred);
if(ctx->gtls.session)
gnutls_deinit(ctx->gtls.session);
+ Curl_gtls_shared_creds_free(&ctx->gtls.shared_creds);
#elif defined(USE_WOLFSSL)
- if(ctx->ssl)
- wolfSSL_free(ctx->ssl);
- if(ctx->ssl_ctx)
- wolfSSL_CTX_free(ctx->ssl_ctx);
+ if(ctx->wssl.handle)
+ wolfSSL_free(ctx->wssl.handle);
+ if(ctx->wssl.ctx)
+ wolfSSL_CTX_free(ctx->wssl.ctx);
#endif
memset(ctx, 0, sizeof(*ctx));
}
@@ -286,8 +285,14 @@ CURLcode Curl_vquic_tls_before_recv(struct curl_tls_ctx *ctx, return result;
ctx->ossl.x509_store_setup = TRUE;
}
+#elif defined(USE_WOLFSSL)
+ if(!ctx->wssl.x509_store_setup) {
+ CURLcode result = Curl_wssl_setup_x509_store(cf, data, &ctx->wssl);
+ if(result)
+ return result;
+ }
#elif defined(USE_GNUTLS)
- if(!ctx->gtls.trust_setup) {
+ if(!ctx->gtls.shared_creds->trust_setup) {
CURLcode result = Curl_gtls_client_trust_setup(cf, data, &ctx->gtls);
if(result)
return result;
@@ -325,7 +330,7 @@ CURLcode Curl_vquic_tls_verify_peer(struct curl_tls_ctx *ctx, (void)data;
if(conn_config->verifyhost) {
if(peer->sni) {
- WOLFSSL_X509* cert = wolfSSL_get_peer_certificate(ctx->ssl);
+ WOLFSSL_X509* cert = wolfSSL_get_peer_certificate(ctx->wssl.handle);
if(wolfSSL_X509_check_host(cert, peer->sni, strlen(peer->sni), 0, NULL)
== WOLFSSL_FAILURE) {
result = CURLE_PEER_FAILED_VERIFICATION;
diff --git a/libs/libcurl/src/vquic/vquic-tls.h b/libs/libcurl/src/vquic/vquic-tls.h index e7d33d4b46..74db0e0ada 100644 --- a/libs/libcurl/src/vquic/vquic-tls.h +++ b/libs/libcurl/src/vquic/vquic-tls.h @@ -31,14 +31,15 @@ #if defined(USE_HTTP3) && \
(defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_WOLFSSL))
+#include "vtls/wolfssl.h"
+
struct curl_tls_ctx {
#ifdef USE_OPENSSL
struct ossl_ctx ossl;
#elif defined(USE_GNUTLS)
struct gtls_ctx gtls;
#elif defined(USE_WOLFSSL)
- WOLFSSL_CTX *ssl_ctx;
- WOLFSSL *ssl;
+ struct wolfssl_ctx wssl;
#endif
};
diff --git a/libs/libcurl/src/vquic/vquic.c b/libs/libcurl/src/vquic/vquic.c index feff3395cc..13d9929ee2 100644 --- a/libs/libcurl/src/vquic/vquic.c +++ b/libs/libcurl/src/vquic/vquic.c @@ -36,6 +36,9 @@ #include "curl_setup.h"
+#ifdef HAVE_NETINET_UDP_H
+#include <netinet/udp.h>
+#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
@@ -329,6 +332,36 @@ CURLcode vquic_send_tail_split(struct Curl_cfilter *cf, struct Curl_easy *data, return vquic_flush(cf, data, qctx);
}
+#if defined(HAVE_SENDMMSG) || defined(HAVE_SENDMSG)
+static size_t msghdr_get_udp_gro(struct msghdr *msg)
+{
+ int gso_size = 0;
+#if defined(__linux__) && defined(UDP_GRO)
+ struct cmsghdr *cmsg;
+
+ /* Workaround musl CMSG_NXTHDR issue */
+#ifndef __GLIBC__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wsign-compare"
+#pragma clang diagnostic ignored "-Wcast-align"
+#endif
+ for(cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+#ifndef __GLIBC__
+#pragma clang diagnostic pop
+#endif
+ if(cmsg->cmsg_level == SOL_UDP && cmsg->cmsg_type == UDP_GRO) {
+ memcpy(&gso_size, CMSG_DATA(cmsg), sizeof(gso_size));
+
+ break;
+ }
+ }
+#endif
+ (void)msg;
+
+ return (size_t)gso_size;
+}
+#endif
+
#ifdef HAVE_SENDMMSG
static CURLcode recvmmsg_packets(struct Curl_cfilter *cf,
struct Curl_easy *data,
@@ -339,12 +372,16 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, #define MMSG_NUM 64
struct iovec msg_iov[MMSG_NUM];
struct mmsghdr mmsg[MMSG_NUM];
+ uint8_t msg_ctrl[MMSG_NUM * CMSG_SPACE(sizeof(uint16_t))];
uint8_t bufs[MMSG_NUM][2*1024];
struct sockaddr_storage remote_addr[MMSG_NUM];
size_t total_nread, pkts;
int mcount, i, n;
char errstr[STRERROR_LEN];
CURLcode result = CURLE_OK;
+ size_t gso_size;
+ size_t pktlen;
+ size_t offset, to;
DEBUGASSERT(max_pkts > 0);
pkts = 0;
@@ -359,6 +396,8 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, mmsg[i].msg_hdr.msg_iovlen = 1;
mmsg[i].msg_hdr.msg_name = &remote_addr[i];
mmsg[i].msg_hdr.msg_namelen = sizeof(remote_addr[i]);
+ mmsg[i].msg_hdr.msg_control = &msg_ctrl[i];
+ mmsg[i].msg_hdr.msg_controllen = CMSG_SPACE(sizeof(uint16_t));
}
while((mcount = recvmmsg(qctx->sockfd, mmsg, n, 0, NULL)) == -1 &&
@@ -385,14 +424,30 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, }
CURL_TRC_CF(data, cf, "recvmmsg() -> %d packets", mcount);
- pkts += mcount;
for(i = 0; i < mcount; ++i) {
total_nread += mmsg[i].msg_len;
- result = recv_cb(bufs[i], mmsg[i].msg_len,
- mmsg[i].msg_hdr.msg_name, mmsg[i].msg_hdr.msg_namelen,
- 0, userp);
- if(result)
- goto out;
+
+ gso_size = msghdr_get_udp_gro(&mmsg[i].msg_hdr);
+ if(gso_size == 0) {
+ gso_size = mmsg[i].msg_len;
+ }
+
+ for(offset = 0; offset < mmsg[i].msg_len; offset = to) {
+ ++pkts;
+
+ to = offset + gso_size;
+ if(to > mmsg[i].msg_len) {
+ pktlen = mmsg[i].msg_len - offset;
+ }
+ else {
+ pktlen = gso_size;
+ }
+
+ result = recv_cb(bufs[i] + offset, pktlen, mmsg[i].msg_hdr.msg_name,
+ mmsg[i].msg_hdr.msg_namelen, 0, userp);
+ if(result)
+ goto out;
+ }
}
}
@@ -418,6 +473,10 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, ssize_t nread;
char errstr[STRERROR_LEN];
CURLcode result = CURLE_OK;
+ uint8_t msg_ctrl[CMSG_SPACE(sizeof(uint16_t))];
+ size_t gso_size;
+ size_t pktlen;
+ size_t offset, to;
msg_iov.iov_base = buf;
msg_iov.iov_len = (int)sizeof(buf);
@@ -425,11 +484,13 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, memset(&msg, 0, sizeof(msg));
msg.msg_iov = &msg_iov;
msg.msg_iovlen = 1;
+ msg.msg_control = msg_ctrl;
DEBUGASSERT(max_pkts > 0);
for(pkts = 0, total_nread = 0; pkts < max_pkts;) {
msg.msg_name = &remote_addr;
msg.msg_namelen = sizeof(remote_addr);
+ msg.msg_controllen = sizeof(msg_ctrl);
while((nread = recvmsg(qctx->sockfd, &msg, 0)) == -1 &&
SOCKERRNO == EINTR)
;
@@ -452,12 +513,29 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, goto out;
}
- ++pkts;
total_nread += (size_t)nread;
- result = recv_cb(buf, (size_t)nread, msg.msg_name, msg.msg_namelen,
- 0, userp);
- if(result)
- goto out;
+
+ gso_size = msghdr_get_udp_gro(&msg);
+ if(gso_size == 0) {
+ gso_size = (size_t)nread;
+ }
+
+ for(offset = 0; offset < (size_t)nread; offset = to) {
+ ++pkts;
+
+ to = offset + gso_size;
+ if(to > (size_t)nread) {
+ pktlen = (size_t)nread - offset;
+ }
+ else {
+ pktlen = gso_size;
+ }
+
+ result =
+ recv_cb(buf + offset, pktlen, msg.msg_name, msg.msg_namelen, 0, userp);
+ if(result)
+ goto out;
+ }
}
out:
@@ -655,7 +733,7 @@ CURLcode Curl_conn_may_http3(struct Curl_easy *data, return CURLE_URL_MALFORMAT;
}
if(conn->bits.httpproxy && conn->bits.tunnel_proxy) {
- failf(data, "HTTP/3 is not supported over a HTTP proxy");
+ failf(data, "HTTP/3 is not supported over an HTTP proxy");
return CURLE_URL_MALFORMAT;
}
#endif
|
