diff options
author | dartraiden <wowemuh@gmail.com> | 2023-09-16 22:53:10 +0300 |
---|---|---|
committer | dartraiden <wowemuh@gmail.com> | 2023-09-16 22:53:10 +0300 |
commit | 47346b568cae68439c3d39f06f8c4ab14911475d (patch) | |
tree | 617c91959e8c606a315a1aaaf13a38f5b7333e9a /libs/libcurl/src/vquic | |
parent | cb1787afbb67184321f206f13f836b63cd06740a (diff) |
libcurl: update to 8.3.0
Diffstat (limited to 'libs/libcurl/src/vquic')
-rw-r--r-- | libs/libcurl/src/vquic/curl_msh3.c | 64 | ||||
-rw-r--r-- | libs/libcurl/src/vquic/curl_ngtcp2.c | 310 | ||||
-rw-r--r-- | libs/libcurl/src/vquic/curl_quiche.c | 382 | ||||
-rw-r--r-- | libs/libcurl/src/vquic/vquic.c | 88 | ||||
-rw-r--r-- | libs/libcurl/src/vquic/vquic_int.h | 3 |
5 files changed, 501 insertions, 346 deletions
diff --git a/libs/libcurl/src/vquic/curl_msh3.c b/libs/libcurl/src/vquic/curl_msh3.c index 9e145bfe16..5d31e234d8 100644 --- a/libs/libcurl/src/vquic/curl_msh3.c +++ b/libs/libcurl/src/vquic/curl_msh3.c @@ -30,7 +30,7 @@ #include "timeval.h"
#include "multiif.h"
#include "sendf.h"
-#include "curl_log.h"
+#include "curl_trc.h"
#include "cfilters.h"
#include "cf-socket.h"
#include "connect.h"
@@ -173,7 +173,7 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf, msh3_lock_initialize(&stream->recv_lock);
Curl_bufq_init2(&stream->recvbuf, H3_STREAM_CHUNK_SIZE,
H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT);
- DEBUGF(LOG_CF(data, cf, "data setup"));
+ CURL_TRC_CF(data, cf, "data setup");
return CURLE_OK;
}
@@ -183,7 +183,7 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) (void)cf;
if(stream) {
- DEBUGF(LOG_CF(data, cf, "easy handle is done"));
+ CURL_TRC_CF(data, cf, "easy handle is done");
Curl_bufq_free(&stream->recvbuf);
free(stream);
H3_STREAM_LCTX(data) = NULL;
@@ -235,7 +235,7 @@ static void MSH3_CALL msh3_conn_connected(MSH3_CONNECTION *Connection, struct Curl_easy *data = CF_DATA_CURRENT(cf);
(void)Connection;
- DEBUGF(LOG_CF(data, cf, "[MSH3] connected"));
+ CURL_TRC_CF(data, cf, "[MSH3] connected");
ctx->handshake_succeeded = true;
ctx->connected = true;
ctx->handshake_complete = true;
@@ -249,7 +249,7 @@ static void MSH3_CALL msh3_conn_shutdown_complete(MSH3_CONNECTION *Connection, struct Curl_easy *data = CF_DATA_CURRENT(cf);
(void)Connection;
- DEBUGF(LOG_CF(data, cf, "[MSH3] shutdown complete"));
+ CURL_TRC_CF(data, cf, "[MSH3] shutdown complete");
ctx->connected = false;
ctx->handshake_complete = true;
}
@@ -474,18 +474,18 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf, if(stream->reset) {
failf(data, "HTTP/3 stream reset by server");
*err = CURLE_PARTIAL_FILE;
- DEBUGF(LOG_CF(data, cf, "cf_recv, was reset -> %d", *err));
+ CURL_TRC_CF(data, cf, "cf_recv, was reset -> %d", *err);
goto out;
}
else if(stream->error3) {
failf(data, "HTTP/3 stream was not closed cleanly: (error %zd)",
(ssize_t)stream->error3);
*err = CURLE_HTTP3;
- DEBUGF(LOG_CF(data, cf, "cf_recv, closed uncleanly -> %d", *err));
+ CURL_TRC_CF(data, cf, "cf_recv, closed uncleanly -> %d", *err);
goto out;
}
else {
- DEBUGF(LOG_CF(data, cf, "cf_recv, closed ok -> %d", *err));
+ CURL_TRC_CF(data, cf, "cf_recv, closed ok -> %d", *err);
}
*err = CURLE_OK;
nread = 0;
@@ -523,7 +523,7 @@ static ssize_t cf_msh3_recv(struct Curl_cfilter *cf, struct Curl_easy *data, return -1;
}
CF_DATA_SAVE(save, cf, data);
- DEBUGF(LOG_CF(data, cf, "req: recv with %zu byte buffer", len));
+ CURL_TRC_CF(data, cf, "req: recv with %zu byte buffer", len);
msh3_lock_acquire(&stream->recv_lock);
@@ -538,8 +538,8 @@ static ssize_t cf_msh3_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);
- DEBUGF(LOG_CF(data, cf, "read recvbuf(len=%zu) -> %zd, %d",
- len, nread, *err));
+ CURL_TRC_CF(data, cf, "read recvbuf(len=%zu) -> %zd, %d",
+ len, nread, *err);
if(nread < 0)
goto out;
if(stream->closed)
@@ -550,7 +550,7 @@ static ssize_t cf_msh3_recv(struct Curl_cfilter *cf, struct Curl_easy *data, goto out;
}
else {
- DEBUGF(LOG_CF(data, cf, "req: nothing here, call again"));
+ CURL_TRC_CF(data, cf, "req: nothing here, call again");
*err = CURLE_AGAIN;
}
@@ -581,7 +581,7 @@ static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data, /* Sizes must match for cast below to work" */
DEBUGASSERT(stream);
- DEBUGF(LOG_CF(data, cf, "req: send %zu bytes", len));
+ CURL_TRC_CF(data, cf, "req: send %zu bytes", len);
if(!stream->req) {
/* The first send on the request contains the headers and possibly some
@@ -630,7 +630,7 @@ static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data, break;
}
- DEBUGF(LOG_CF(data, cf, "req: send %zu headers", nheader));
+ CURL_TRC_CF(data, cf, "req: send %zu headers", nheader);
stream->req = MsH3RequestOpen(ctx->qconn, &msh3_request_if, data,
nva, nheader,
eos ? MSH3_REQUEST_FLAG_FIN :
@@ -646,7 +646,7 @@ static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data, }
else {
/* request is open */
- DEBUGF(LOG_CF(data, cf, "req: send %zu body bytes", len));
+ CURL_TRC_CF(data, cf, "req: send %zu body bytes", len);
if(len > 0xFFFFFFFF) {
len = 0xFFFFFFFF;
}
@@ -694,7 +694,7 @@ static int cf_msh3_get_select_socks(struct Curl_cfilter *cf, drain_stream(cf, data);
}
}
- DEBUGF(LOG_CF(data, cf, "select_sock -> %d", bitmap));
+ CURL_TRC_CF(data, cf, "select_sock -> %d", bitmap);
CF_DATA_RESTORE(cf, save);
return bitmap;
}
@@ -711,8 +711,8 @@ static bool cf_msh3_data_pending(struct Curl_cfilter *cf, (void)cf;
if(stream && stream->req) {
msh3_lock_acquire(&stream->recv_lock);
- DEBUGF(LOG_CF((struct Curl_easy *)data, cf, "data pending = %zu",
- Curl_bufq_len(&stream->recvbuf)));
+ CURL_TRC_CF((struct Curl_easy *)data, cf, "data pending = %zu",
+ Curl_bufq_len(&stream->recvbuf));
pending = !Curl_bufq_is_empty(&stream->recvbuf);
msh3_lock_release(&stream->recv_lock);
if(pending)
@@ -774,7 +774,7 @@ static CURLcode cf_msh3_data_event(struct Curl_cfilter *cf, h3_data_done(cf, data);
break;
case CF_CTRL_DATA_DONE_SEND:
- DEBUGF(LOG_CF(data, cf, "req: send done"));
+ CURL_TRC_CF(data, cf, "req: send done");
if(stream) {
stream->upload_done = TRUE;
if(stream->req) {
@@ -787,7 +787,7 @@ static CURLcode cf_msh3_data_event(struct Curl_cfilter *cf, }
break;
case CF_CTRL_CONN_INFO_UPDATE:
- DEBUGF(LOG_CF(data, cf, "req: update info"));
+ CURL_TRC_CF(data, cf, "req: update info");
cf_msh3_active(cf, data);
break;
default:
@@ -813,17 +813,17 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, /* TODO: need a way to provide trust anchors to MSH3 */
#ifdef DEBUGBUILD
/* we need this for our test cases to run */
- DEBUGF(LOG_CF(data, cf, "non-standard CA not supported, "
- "switching off verifypeer in DEBUG mode"));
+ CURL_TRC_CF(data, cf, "non-standard CA not supported, "
+ "switching off verifypeer in DEBUG mode");
verify = 0;
#else
- DEBUGF(LOG_CF(data, cf, "non-standard CA not supported, "
- "attempting with built-in verification"));
+ CURL_TRC_CF(data, cf, "non-standard CA not supported, "
+ "attempting with built-in verification");
#endif
}
- DEBUGF(LOG_CF(data, cf, "connecting to %s:%d (verify=%d)",
- cf->conn->host.name, (int)cf->conn->remote_port, verify));
+ CURL_TRC_CF(data, cf, "connecting to %s:%d (verify=%d)",
+ cf->conn->host.name, (int)cf->conn->remote_port, verify);
ctx->api = MsH3ApiOpen();
if(!ctx->api) {
@@ -888,7 +888,7 @@ static CURLcode cf_msh3_connect(struct Curl_cfilter *cf, if(ctx->handshake_complete) {
ctx->handshake_at = Curl_now();
if(ctx->handshake_succeeded) {
- DEBUGF(LOG_CF(data, cf, "handshake succeeded"));
+ CURL_TRC_CF(data, cf, "handshake succeeded");
cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
cf->conn->httpversion = 30;
cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
@@ -918,7 +918,7 @@ static void cf_msh3_close(struct Curl_cfilter *cf, struct Curl_easy *data) CF_DATA_SAVE(save, cf, data);
if(ctx) {
- DEBUGF(LOG_CF(data, cf, "destroying"));
+ CURL_TRC_CF(data, cf, "destroying");
if(ctx->qconn) {
MsH3ConnectionClose(ctx->qconn);
ctx->qconn = NULL;
@@ -935,13 +935,13 @@ static void cf_msh3_close(struct Curl_cfilter *cf, struct Curl_easy *data) */
ctx->active = FALSE;
if(ctx->sock[SP_LOCAL] == cf->conn->sock[cf->sockindex]) {
- DEBUGF(LOG_CF(data, cf, "cf_msh3_close(%d) active",
- (int)ctx->sock[SP_LOCAL]));
+ CURL_TRC_CF(data, cf, "cf_msh3_close(%d) active",
+ (int)ctx->sock[SP_LOCAL]);
cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
}
else {
- DEBUGF(LOG_CF(data, cf, "cf_socket_close(%d) no longer at "
- "conn->sock[], discarding", (int)ctx->sock[SP_LOCAL]));
+ CURL_TRC_CF(data, cf, "cf_socket_close(%d) no longer at "
+ "conn->sock[], discarding", (int)ctx->sock[SP_LOCAL]);
ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
}
if(cf->sockindex == FIRSTSOCKET)
diff --git a/libs/libcurl/src/vquic/curl_ngtcp2.c b/libs/libcurl/src/vquic/curl_ngtcp2.c index 50ca212a96..7fd7085304 100644 --- a/libs/libcurl/src/vquic/curl_ngtcp2.c +++ b/libs/libcurl/src/vquic/curl_ngtcp2.c @@ -58,6 +58,7 @@ #include "dynbuf.h"
#include "http1.h"
#include "select.h"
+#include "inet_pton.h"
#include "vquic.h"
#include "vquic_int.h"
#include "vtls/keylog.h"
@@ -162,6 +163,9 @@ struct cf_ngtcp2_ctx { size_t max_stream_window; /* max flow window for one stream */
int qlogfd;
BIT(got_first_byte); /* if first byte was received */
+#ifdef USE_OPENSSL
+ BIT(x509_store_setup); /* if x509 store has been set up */
+#endif
};
/* How to access `call_data` from a cf_ngtcp2 filter */
@@ -176,6 +180,7 @@ struct h3_stream_ctx { int64_t id; /* HTTP/3 protocol identifier */
struct bufq sendbuf; /* h3 request body */
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 */
@@ -223,9 +228,9 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf, Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp,
H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT);
stream->recv_buf_nonflow = 0;
+ Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
H3_STREAM_LCTX(data) = stream;
- DEBUGF(LOG_CF(data, cf, "data setup"));
return CURLE_OK;
}
@@ -235,10 +240,10 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) (void)cf;
if(stream) {
- DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] easy handle is done",
- stream->id));
+ CURL_TRC_CF(data, cf, "[%"PRId64"] easy handle is done", stream->id);
Curl_bufq_free(&stream->sendbuf);
Curl_bufq_free(&stream->recvbuf);
+ Curl_h1_req_parse_free(&stream->h1);
free(stream);
H3_STREAM_LCTX(data) = NULL;
}
@@ -392,6 +397,7 @@ static int init_ngh3_conn(struct Curl_cfilter *cf); static CURLcode quic_ssl_ctx(SSL_CTX **pssl_ctx,
struct Curl_cfilter *cf, struct Curl_easy *data)
{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
struct connectdata *conn = cf->conn;
CURLcode result = CURLE_FAILED_INIT;
SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method());
@@ -440,10 +446,6 @@ static CURLcode quic_ssl_ctx(SSL_CTX **pssl_ctx, SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
}
- result = Curl_ssl_setup_x509_store(cf, data, ssl_ctx);
- if(result)
- goto out;
-
/* OpenSSL always tries to verify the peer, this only says whether it should
* fail to connect if the verification fails, or if it should continue
* anyway. In the latter case the result of the verification is checked with
@@ -453,6 +455,15 @@ static CURLcode quic_ssl_ctx(SSL_CTX **pssl_ctx, /* give application a chance to interfere with SSL set up. */
if(data->set.ssl.fsslctx) {
+ /* When a user callback is installed to modify the SSL_CTX,
+ * we need to do the full initialization before calling it.
+ * See: #11800 */
+ if(!ctx->x509_store_setup) {
+ result = Curl_ssl_setup_x509_store(cf, data, ssl_ctx);
+ if(result)
+ goto out;
+ ctx->x509_store_setup = TRUE;
+ }
Curl_set_in_callback(data, true);
result = (*data->set.ssl.fsslctx)(data, ssl_ctx,
data->set.ssl.fsslctxp);
@@ -501,8 +512,8 @@ static CURLcode quic_init_ssl(struct Curl_cfilter *cf, struct cf_ngtcp2_ctx *ctx = cf->ctx;
const uint8_t *alpn = NULL;
size_t alpnlen = 0;
+ unsigned char checkip[16];
- (void)data;
DEBUGASSERT(!ctx->ssl);
ctx->ssl = SSL_new(ctx->sslctx);
@@ -516,7 +527,19 @@ static CURLcode quic_init_ssl(struct Curl_cfilter *cf, SSL_set_alpn_protos(ctx->ssl, alpn, (int)alpnlen);
/* set SNI */
- SSL_set_tlsext_host_name(ctx->ssl, cf->conn->host.name);
+ if((0 == Curl_inet_pton(AF_INET, cf->conn->host.name, checkip))
+#ifdef ENABLE_IPV6
+ && (0 == Curl_inet_pton(AF_INET6, cf->conn->host.name, checkip))
+#endif
+ ) {
+ char *snihost = Curl_ssl_snihost(data, cf->conn->host.name, NULL);
+ if(!snihost || !SSL_set_tlsext_host_name(ctx->ssl, snihost)) {
+ failf(data, "Failed set SNI");
+ SSL_free(ctx->ssl);
+ ctx->ssl = NULL;
+ return CURLE_QUIC_CONNECT_ERROR;
+ }
+ }
return CURLE_OK;
}
#elif defined(USE_GNUTLS)
@@ -544,15 +567,15 @@ static CURLcode quic_init_ssl(struct Curl_cfilter *cf, gnutls_session_set_ptr(ctx->gtls->session, &ctx->conn_ref);
if(ngtcp2_crypto_gnutls_configure_client_session(ctx->gtls->session) != 0) {
- DEBUGF(LOG_CF(data, cf,
- "ngtcp2_crypto_gnutls_configure_client_session failed\n"));
+ CURL_TRC_CF(data, cf,
+ "ngtcp2_crypto_gnutls_configure_client_session failed\n");
return CURLE_QUIC_CONNECT_ERROR;
}
rc = gnutls_priority_set_direct(ctx->gtls->session, QUIC_PRIORITY, NULL);
if(rc < 0) {
- DEBUGF(LOG_CF(data, cf, "gnutls_priority_set_direct failed: %s\n",
- gnutls_strerror(rc)));
+ CURL_TRC_CF(data, cf, "gnutls_priority_set_direct failed: %s\n",
+ gnutls_strerror(rc));
return CURLE_QUIC_CONNECT_ERROR;
}
@@ -596,7 +619,7 @@ static CURLcode quic_ssl_ctx(WOLFSSL_CTX **pssl_ctx, if(wolfSSL_CTX_set_cipher_list(ssl_ctx, QUIC_CIPHERS) != 1) {
char error_buffer[256];
ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer));
- failf(data, "SSL_CTX_set_ciphersuites: %s", error_buffer);
+ failf(data, "wolfSSL_CTX_set_cipher_list: %s", error_buffer);
goto out;
}
@@ -728,8 +751,8 @@ static void report_consumed_data(struct Curl_cfilter *cf, }
}
if(consumed > 0) {
- DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] consumed %zu DATA bytes",
- stream->id, consumed));
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] ACK %zu bytes of DATA",
+ stream->id, consumed);
ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream->id,
consumed);
ngtcp2_conn_extend_max_offset(ctx->qconn, consumed);
@@ -751,8 +774,8 @@ static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags, nconsumed =
nghttp3_conn_read_stream(ctx->h3conn, stream_id, buf, buflen, fin);
- DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read_stream(len=%zu) -> %zd",
- stream_id, buflen, nconsumed));
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] read_stream(len=%zu) -> %zd",
+ stream_id, buflen, nconsumed);
if(nconsumed < 0) {
ngtcp2_ccerr_set_application_error(
&ctx->last_error,
@@ -810,8 +833,8 @@ static int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags, rv = nghttp3_conn_close_stream(ctx->h3conn, stream3_id,
app_error_code);
- DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] quic close(err=%"
- PRIu64 ") -> %d", stream3_id, app_error_code, rv));
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] quic close(err=%"
+ PRIu64 ") -> %d", stream3_id, app_error_code, rv);
if(rv) {
ngtcp2_ccerr_set_application_error(
&ctx->last_error, nghttp3_err_infer_quic_app_error_code(rv), NULL, 0);
@@ -835,7 +858,7 @@ static int cb_stream_reset(ngtcp2_conn *tconn, int64_t stream_id, (void)data;
rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id);
- DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] reset -> %d", stream_id, rv));
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv);
if(rv) {
return NGTCP2_ERR_CALLBACK_FAILURE;
}
@@ -1064,8 +1087,6 @@ 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])); */
CF_DATA_RESTORE(cf, save);
return rv;
}
@@ -1095,21 +1116,23 @@ static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id, struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
(void)conn;
(void)stream_id;
- (void)app_error_code;
- (void)cf;
/* we might be called by nghttp3 after we already cleaned up */
if(!stream)
return 0;
- DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] h3 close(err=%" PRId64 ")",
- stream_id, app_error_code));
stream->closed = TRUE;
stream->error3 = app_error_code;
- if(app_error_code == NGHTTP3_H3_INTERNAL_ERROR) {
+ if(stream->error3 != NGHTTP3_H3_NO_ERROR) {
stream->reset = TRUE;
stream->send_closed = TRUE;
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] RESET: error %" PRId64,
+ stream->id, stream->error3);
+ }
+ else {
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] CLOSED", stream->id);
}
+ data->req.keepon &= ~KEEP_SEND_HOLD;
h3_drain_stream(cf, data);
return 0;
}
@@ -1133,9 +1156,6 @@ static CURLcode write_resp_raw(struct Curl_cfilter *cf, return CURLE_RECV_ERROR;
}
nwritten = Curl_bufq_write(&stream->recvbuf, mem, memlen, &result);
- /* DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] add recvbuf(len=%zu) "
- "-> %zd, %d", stream->id, memlen, nwritten, result));
- */
if(nwritten < 0) {
return result;
}
@@ -1158,14 +1178,24 @@ static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id, {
struct Curl_cfilter *cf = user_data;
struct Curl_easy *data = stream_user_data;
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
CURLcode result;
(void)conn;
(void)stream3_id;
+ if(!stream)
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+
result = write_resp_raw(cf, data, buf, buflen, TRUE);
+ if(result) {
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, ERROR receiving %d",
+ stream->id, buflen, result);
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+ }
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu", stream->id, buflen);
h3_drain_stream(cf, data);
- return result? -1 : 0;
+ return 0;
}
static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream3_id,
@@ -1204,8 +1234,8 @@ static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id, return -1;
}
- DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] end_headers(status_code=%d",
- stream_id, stream->status_code));
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] end_headers, status=%d",
+ stream_id, stream->status_code);
if(stream->status_code / 100 != 1) {
stream->resp_hds_complete = TRUE;
}
@@ -1244,8 +1274,7 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id, return -1;
ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n",
stream->status_code);
- DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] status: %s",
- stream_id, line));
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] status: %s", stream_id, line);
result = write_resp_raw(cf, data, line, ncopy, FALSE);
if(result) {
return -1;
@@ -1253,9 +1282,9 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id, }
else {
/* store as an HTTP1-style header */
- DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] header: %.*s: %.*s",
- stream_id, (int)h3name.len, h3name.base,
- (int)h3val.len, h3val.base));
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] header: %.*s: %.*s",
+ stream_id, (int)h3name.len, h3name.base,
+ (int)h3val.len, h3val.base);
result = write_resp_raw(cf, data, h3name.base, h3name.len, FALSE);
if(result) {
return -1;
@@ -1307,7 +1336,7 @@ static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t stream_id, rv = ngtcp2_conn_shutdown_stream_write(ctx->qconn, 0, stream_id,
app_error_code);
- DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] reset -> %d", stream_id, rv));
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv);
if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) {
return NGTCP2_ERR_CALLBACK_FAILURE;
}
@@ -1404,35 +1433,17 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf, if(stream->reset) {
failf(data,
"HTTP/3 stream %" PRId64 " reset by server", stream->id);
- *err = CURLE_PARTIAL_FILE;
- DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, was reset -> %d",
- stream->id, *err));
+ *err = stream->resp_hds_complete? CURLE_PARTIAL_FILE : CURLE_HTTP3;
goto out;
}
- else if(stream->error3 != NGHTTP3_H3_NO_ERROR) {
- failf(data,
- "HTTP/3 stream %" PRId64 " was not closed cleanly: "
- "(err %"PRId64")", stream->id, stream->error3);
- *err = CURLE_HTTP3;
- DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed uncleanly"
- " -> %d", stream->id, *err));
- goto out;
- }
-
- if(!stream->resp_hds_complete) {
+ else if(!stream->resp_hds_complete) {
failf(data,
"HTTP/3 stream %" PRId64 " was closed cleanly, but before getting"
" all response header fields, treated as error",
stream->id);
*err = CURLE_HTTP3;
- DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed incomplete"
- " -> %d", stream->id, *err));
goto out;
}
- else {
- DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed ok"
- " -> %d", stream->id, *err));
- }
*err = CURLE_OK;
nread = 0;
@@ -1469,10 +1480,11 @@ static ssize_t cf_ngtcp2_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);
- DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read recvbuf(len=%zu) "
- "-> %zd, %d", stream->id, len, nread, *err));
- if(nread < 0)
+ if(nread < 0) {
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) "
+ "-> %zd, %d", stream->id, len, nread, *err);
goto out;
+ }
report_consumed_data(cf, data, nread);
}
@@ -1486,10 +1498,11 @@ static ssize_t cf_ngtcp2_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);
- DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read recvbuf(len=%zu) "
- "-> %zd, %d", stream->id, len, nread, *err));
- if(nread < 0)
+ if(nread < 0) {
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) "
+ "-> %zd, %d", stream->id, len, nread, *err);
goto out;
+ }
report_consumed_data(cf, data, nread);
}
@@ -1517,8 +1530,8 @@ out: nread = -1;
}
}
- DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv(len=%zu) -> %zd, %d",
- stream? stream->id : -1, len, nread, *err));
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv(len=%zu) -> %zd, %d",
+ stream? stream->id : -1, len, nread, *err);
CF_DATA_RESTORE(cf, save);
return nread;
}
@@ -1555,8 +1568,7 @@ static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id, (data->req.keepon & KEEP_SEND)) {
data->req.keepon &= ~KEEP_SEND_HOLD;
h3_drain_stream(cf, data);
- DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] unpausing acks",
- stream_id));
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] unpausing acks", stream_id);
}
}
return 0;
@@ -1613,18 +1625,18 @@ cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id, }
else if(!nwritten) {
/* Not EOF, and nothing to give, we signal WOULDBLOCK. */
- DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read req body -> AGAIN",
- stream->id));
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> AGAIN",
+ stream->id);
return NGHTTP3_ERR_WOULDBLOCK;
}
- DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read req body -> "
- "%d vecs%s with %zu (buffered=%zu, left=%"
- CURL_FORMAT_CURL_OFF_T ")",
- stream->id, (int)nvecs,
- *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"",
- nwritten, Curl_bufq_len(&stream->sendbuf),
- stream->upload_left));
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> "
+ "%d vecs%s with %zu (buffered=%zu, left=%"
+ CURL_FORMAT_CURL_OFF_T ")",
+ stream->id, (int)nvecs,
+ *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"",
+ nwritten, Curl_bufq_len(&stream->sendbuf),
+ stream->upload_left);
return (nghttp3_ssize)nvecs;
}
@@ -1639,7 +1651,6 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, {
struct cf_ngtcp2_ctx *ctx = cf->ctx;
struct h3_stream_ctx *stream = NULL;
- struct h1_req_parser h1;
struct dynhds h2_headers;
size_t nheader;
nghttp3_nv *nva = NULL;
@@ -1649,7 +1660,6 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, nghttp3_data_reader reader;
nghttp3_data_reader *preader = NULL;
- Curl_h1_req_parse_init(&h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST);
*err = h3_data_setup(cf, data);
@@ -1658,24 +1668,22 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, 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;
- }
-
- nwritten = Curl_h1_req_parse_read(&h1, buf, len, NULL, 0, err);
+ nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err);
if(nwritten < 0)
goto out;
- DEBUGASSERT(h1.done);
- DEBUGASSERT(h1.req);
+ if(!stream->h1.done) {
+ /* need more data */
+ goto out;
+ }
+ DEBUGASSERT(stream->h1.req);
- *err = Curl_http_req_to_h2(&h2_headers, h1.req, data);
+ *err = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data);
if(*err) {
nwritten = -1;
goto out;
}
+ /* no longer needed */
+ Curl_h1_req_parse_free(&stream->h1);
nheader = Curl_dynhds_count(&h2_headers);
nva = malloc(sizeof(nghttp3_nv) * nheader);
@@ -1694,6 +1702,13 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, nva[i].flags = NGHTTP3_NV_FLAG_NONE;
}
+ 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;
+ }
+
switch(data->state.httpreq) {
case HTTPREQ_POST:
case HTTPREQ_POST_FORM:
@@ -1723,12 +1738,12 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, if(rc) {
switch(rc) {
case NGHTTP3_ERR_CONN_CLOSING:
- DEBUGF(LOG_CF(data, cf, "h3sid[%"PRId64"] failed to send, "
- "connection is closing", stream->id));
+ CURL_TRC_CF(data, cf, "h3sid[%"PRId64"] failed to send, "
+ "connection is closing", stream->id);
break;
default:
- DEBUGF(LOG_CF(data, cf, "h3sid[%"PRId64"] failed to send -> %d (%s)",
- stream->id, rc, ngtcp2_strerror(rc)));
+ CURL_TRC_CF(data, cf, "h3sid[%"PRId64"] failed to send -> %d (%s)",
+ stream->id, rc, ngtcp2_strerror(rc));
break;
}
*err = CURLE_SEND_ERROR;
@@ -1736,13 +1751,18 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, goto out;
}
- infof(data, "Using HTTP/3 Stream ID: %" PRId64, stream->id);
- DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] opened for %s",
- stream->id, data->state.url));
+ if(Curl_trc_is_verbose(data)) {
+ infof(data, "[HTTP/3] [%" PRId64 "] OPENED stream for %s",
+ stream->id, data->state.url);
+ for(i = 0; i < nheader; ++i) {
+ infof(data, "[HTTP/3] [%" PRId64 "] [%.*s: %.*s]", stream->id,
+ (int)nva[i].namelen, nva[i].name,
+ (int)nva[i].valuelen, nva[i].value);
+ }
+ }
out:
free(nva);
- Curl_h1_req_parse_free(&h1);
Curl_dynhds_free(&h2_headers);
return nwritten;
}
@@ -1773,12 +1793,13 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, if(!stream || stream->id < 0) {
sent = h3_stream_open(cf, data, buf, len, err);
if(sent < 0) {
- DEBUGF(LOG_CF(data, cf, "failed to open stream -> %d", *err));
+ CURL_TRC_CF(data, cf, "failed to open stream -> %d", *err);
goto out;
}
+ stream = H3_STREAM_CTX(data);
}
else if(stream->upload_blocked_len) {
- /* the data in `buf` has alread been submitted or added to the
+ /* 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) {
@@ -1793,15 +1814,27 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, 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
+ * response. This is valid if it is not interested in the request
+ * body. This happens on 30x or 40x responses.
+ * We silently discard the data sent, since this is not a transport
+ * error situation. */
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] discarding data"
+ "on closed stream with response", stream->id);
+ *err = CURLE_OK;
+ sent = (ssize_t)len;
+ goto out;
+ }
*err = CURLE_HTTP3;
sent = -1;
goto out;
}
else {
sent = Curl_bufq_write(&stream->sendbuf, buf, len, err);
- DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_send, add to "
- "sendbuf(len=%zu) -> %zd, %d",
- stream->id, len, sent, *err));
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send, add to "
+ "sendbuf(len=%zu) -> %zd, %d",
+ stream->id, len, sent, *err);
if(sent < 0) {
goto out;
}
@@ -1821,9 +1854,9 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, * "written" into our various internal connection buffers.
* We put the stream upload on HOLD, until this gets ACKed. */
stream->upload_blocked_len = sent;
- DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_send(len=%zu), "
- "%zu bytes in flight -> EGAIN", stream->id, len,
- stream->sendbuf_len_in_flight));
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu), "
+ "%zu bytes in flight -> EGAIN", stream->id, len,
+ stream->sendbuf_len_in_flight);
*err = CURLE_AGAIN;
sent = -1;
data->req.keepon |= KEEP_SEND_HOLD;
@@ -1835,8 +1868,8 @@ out: *err = result;
sent = -1;
}
- DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_send(len=%zu) -> %zd, %d",
- stream? stream->id : -1, len, sent, *err));
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %zd, %d",
+ stream? stream->id : -1, len, sent, *err);
CF_DATA_RESTORE(cf, save);
return sent;
}
@@ -1909,12 +1942,12 @@ static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen, ctx->q.local_addrlen);
ngtcp2_addr_init(&path.remote, (struct sockaddr *)remote_addr,
remote_addrlen);
- pi.ecn = (uint32_t)ecn;
+ pi.ecn = (uint8_t)ecn;
rv = ngtcp2_conn_read_pkt(ctx->qconn, &path, &pi, pkt, pktlen, pktx->ts);
if(rv) {
- DEBUGF(LOG_CF(pktx->data, pktx->cf, "ingress, read_pkt -> %s",
- ngtcp2_strerror(rv)));
+ CURL_TRC_CF(pktx->data, pktx->cf, "ingress, read_pkt -> %s",
+ ngtcp2_strerror(rv));
if(!ctx->last_error.error_code) {
if(rv == NGTCP2_ERR_CRYPTO) {
ngtcp2_ccerr_set_tls_alert(&ctx->last_error,
@@ -1954,6 +1987,15 @@ static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, pktx->ts = timestamp();
}
+#ifdef USE_OPENSSL
+ if(!ctx->x509_store_setup) {
+ result = Curl_ssl_setup_x509_store(cf, data, ctx->sslctx);
+ if(result)
+ return result;
+ ctx->x509_store_setup = TRUE;
+ }
+#endif
+
for(i = 0; i < pkts_max; i += pkts_chunk) {
pktx->pkt_count = 0;
result = vquic_recv_packets(cf, data, &ctx->q, pkts_chunk,
@@ -2129,8 +2171,6 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf, /* add the next packet to send, if any, to our buffer */
nread = Curl_bufq_sipn(&ctx->q.sendbuf, max_payload_size,
read_pkt_to_send, pktx, &curlcode);
- /* DEBUGF(LOG_CF(data, cf, "sip packet(maxlen=%zu) -> %zd, %d",
- max_payload_size, nread, curlcode)); */
if(nread < 0) {
if(curlcode != CURLE_AGAIN)
return curlcode;
@@ -2246,9 +2286,16 @@ static CURLcode cf_ngtcp2_data_event(struct Curl_cfilter *cf, }
break;
}
- case CF_CTRL_DATA_IDLE:
- result = check_and_set_expiry(cf, data, NULL);
+ case CF_CTRL_DATA_IDLE: {
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+ CURL_TRC_CF(data, cf, "data idle");
+ if(stream && !stream->closed) {
+ result = check_and_set_expiry(cf, data, NULL);
+ if(result)
+ CURL_TRC_CF(data, cf, "data idle, check_and_set_expiry -> %d", result);
+ }
break;
+ }
default:
break;
}
@@ -2305,7 +2352,7 @@ static void cf_ngtcp2_close(struct Curl_cfilter *cf, struct Curl_easy *data) ngtcp2_tstamp ts;
ngtcp2_ssize rc;
- DEBUGF(LOG_CF(data, cf, "close"));
+ CURL_TRC_CF(data, cf, "close");
ts = timestamp();
rc = ngtcp2_conn_write_connection_close(ctx->qconn, NULL, /* path */
NULL, /* pkt_info */
@@ -2329,7 +2376,7 @@ static void cf_ngtcp2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) struct cf_call_data save;
CF_DATA_SAVE(save, cf, data);
- DEBUGF(LOG_CF(data, cf, "destroy"));
+ CURL_TRC_CF(data, cf, "destroy");
if(ctx) {
cf_ngtcp2_ctx_clear(ctx);
free(ctx);
@@ -2350,7 +2397,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, int rc;
int rv;
CURLcode result;
- const struct Curl_sockaddr_ex *sockaddr;
+ const struct Curl_sockaddr_ex *sockaddr = NULL;
int qfd;
ctx->version = NGTCP2_PROTO_VER_MAX;
@@ -2396,6 +2443,8 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd,
&sockaddr, NULL, NULL, NULL, NULL);
+ if(!sockaddr)
+ return CURLE_QUIC_CONNECT_ERROR;
ctx->q.local_addrlen = sizeof(ctx->q.local_addr);
rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr,
&ctx->q.local_addrlen);
@@ -2460,7 +2509,7 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf, if(ctx->reconnect_at.tv_sec && Curl_timediff(now, ctx->reconnect_at) < 0) {
/* Not time yet to attempt the next connect */
- DEBUGF(LOG_CF(data, cf, "waiting for reconnect time"));
+ CURL_TRC_CF(data, cf, "waiting for reconnect time");
goto out;
}
@@ -2484,11 +2533,11 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf, if(ngtcp2_conn_get_handshake_completed(ctx->qconn)) {
ctx->handshake_at = now;
- DEBUGF(LOG_CF(data, cf, "handshake complete after %dms",
- (int)Curl_timediff(now, ctx->started_at)));
+ CURL_TRC_CF(data, cf, "handshake complete after %dms",
+ (int)Curl_timediff(now, ctx->started_at));
result = qng_verify_peer(cf, data);
if(!result) {
- DEBUGF(LOG_CF(data, cf, "peer verified"));
+ CURL_TRC_CF(data, cf, "peer verified");
cf->connected = TRUE;
cf->conn->alpn = CURL_HTTP_VERSION_3;
*done = TRUE;
@@ -2510,8 +2559,8 @@ out: */
int reconn_delay_ms = 200;
- DEBUGF(LOG_CF(data, cf, "connect, remote closed, reconnect after %dms",
- reconn_delay_ms));
+ CURL_TRC_CF(data, cf, "connect, remote closed, reconnect after %dms",
+ reconn_delay_ms);
Curl_conn_cf_close(cf->next, data);
cf_ngtcp2_ctx_clear(ctx);
result = Curl_conn_cf_connect(cf->next, data, FALSE, done);
@@ -2526,8 +2575,8 @@ out: #ifndef CURL_DISABLE_VERBOSE_STRINGS
if(result) {
- const char *r_ip;
- int r_port;
+ const char *r_ip = NULL;
+ int r_port = 0;
Curl_cf_socket_peek(cf->next, data, NULL, NULL,
&r_ip, &r_port, NULL, NULL);
@@ -2538,7 +2587,8 @@ out: if(!result && ctx->qconn) {
result = check_and_set_expiry(cf, data, &pktx);
}
- DEBUGF(LOG_CF(data, cf, "connect -> %d, done=%d", result, *done));
+ if(result || *done)
+ CURL_TRC_CF(data, cf, "connect -> %d, done=%d", result, *done);
CF_DATA_RESTORE(cf, save);
return result;
}
@@ -2562,7 +2612,7 @@ static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf, INT_MAX : (int)rp->initial_max_streams_bidi;
else /* not arrived yet? */
*pres1 = Curl_multi_max_concurrent_streams(data->multi);
- DEBUGF(LOG_CF(data, cf, "query max_conncurrent -> %d", *pres1));
+ CURL_TRC_CF(data, cf, "query max_conncurrent -> %d", *pres1);
CF_DATA_RESTORE(cf, save);
return CURLE_OK;
}
diff --git a/libs/libcurl/src/vquic/curl_quiche.c b/libs/libcurl/src/vquic/curl_quiche.c index f6e995c6c5..cec899f161 100644 --- a/libs/libcurl/src/vquic/curl_quiche.c +++ b/libs/libcurl/src/vquic/curl_quiche.c @@ -45,8 +45,10 @@ #include "vquic_int.h"
#include "curl_quiche.h"
#include "transfer.h"
+#include "inet_pton.h"
#include "vtls/openssl.h"
#include "vtls/keylog.h"
+#include "vtls/vtls.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -56,7 +58,7 @@ /* #define DEBUG_QUICHE */
#define QUIC_MAX_STREAMS (100)
-#define QUIC_IDLE_TIMEOUT (5 * 1000) /* milliseconds */
+#define QUIC_IDLE_TIMEOUT (60 * 1000) /* milliseconds */
#define H3_STREAM_WINDOW_SIZE (128 * 1024)
#define H3_STREAM_CHUNK_SIZE (16 * 1024)
@@ -88,54 +90,6 @@ static void keylog_callback(const SSL *ssl, const char *line) Curl_tls_keylog_write_line(line);
}
-static SSL_CTX *quic_ssl_ctx(struct Curl_easy *data)
-{
- SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method());
-
- SSL_CTX_set_alpn_protos(ssl_ctx,
- (const uint8_t *)QUICHE_H3_APPLICATION_PROTOCOL,
- sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1);
-
- SSL_CTX_set_default_verify_paths(ssl_ctx);
-
- /* Open the file if a TLS or QUIC backend has not done this before. */
- Curl_tls_keylog_open();
- if(Curl_tls_keylog_enabled()) {
- SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
- }
-
- {
- struct connectdata *conn = data->conn;
- if(conn->ssl_config.verifypeer) {
- const char * const ssl_cafile = conn->ssl_config.CAfile;
- const char * const ssl_capath = conn->ssl_config.CApath;
- if(ssl_cafile || ssl_capath) {
- SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
- /* tell OpenSSL where to find CA certificates that are used to verify
- the server's certificate. */
- if(!SSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) {
- /* Fail if we insist on successfully verifying the server. */
- failf(data, "error setting certificate verify locations:"
- " CAfile: %s CApath: %s",
- ssl_cafile ? ssl_cafile : "none",
- ssl_capath ? ssl_capath : "none");
- return NULL;
- }
- infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
- infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
- }
-#ifdef CURL_CA_FALLBACK
- else {
- /* verifying the peer without any CA certificates won't work so
- use openssl's built-in default as fallback */
- SSL_CTX_set_default_verify_paths(ssl_ctx);
- }
-#endif
- }
- }
- return ssl_ctx;
-}
-
struct cf_quiche_ctx {
struct cf_quic_ctx q;
quiche_conn *qconn;
@@ -154,6 +108,7 @@ struct cf_quiche_ctx { size_t sends_on_hold; /* # of streams with SEND_HOLD set */
BIT(goaway); /* got GOAWAY from server */
BIT(got_first_byte); /* if first byte was received */
+ BIT(x509_store_setup); /* if x509 store has been set up */
};
#ifdef DEBUG_QUICHE
@@ -181,12 +136,96 @@ static void cf_quiche_ctx_clear(struct cf_quiche_ctx *ctx) }
}
+static CURLcode quic_x509_store_setup(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_quiche_ctx *ctx = cf->ctx;
+
+ if(!ctx->x509_store_setup) {
+ if(cf->conn->ssl_config.verifypeer) {
+ const char * const ssl_cafile = cf->conn->ssl_config.CAfile;
+ const char * const ssl_capath = cf->conn->ssl_config.CApath;
+ if(ssl_cafile || ssl_capath) {
+ SSL_CTX_set_verify(ctx->sslctx, SSL_VERIFY_PEER, NULL);
+ /* tell OpenSSL where to find CA certificates that are used to verify
+ the server's certificate. */
+ if(!SSL_CTX_load_verify_locations(
+ ctx->sslctx, ssl_cafile, ssl_capath)) {
+ /* Fail if we insist on successfully verifying the server. */
+ failf(data, "error setting certificate verify locations:"
+ " CAfile: %s CApath: %s",
+ ssl_cafile ? ssl_cafile : "none",
+ ssl_capath ? ssl_capath : "none");
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
+ infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
+ }
+#ifdef CURL_CA_FALLBACK
+ else {
+ /* verifying the peer without any CA certificates won't work so
+ use openssl's built-in default as fallback */
+ SSL_CTX_set_default_verify_paths(ssl_ctx);
+ }
+#endif
+ }
+ ctx->x509_store_setup = TRUE;
+ }
+ return CURLE_OK;
+}
+
+static CURLcode quic_ssl_setup(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct cf_quiche_ctx *ctx = cf->ctx;
+ unsigned char checkip[16];
+
+ DEBUGASSERT(!ctx->sslctx);
+ ctx->sslctx = SSL_CTX_new(TLS_method());
+ if(!ctx->sslctx)
+ return CURLE_OUT_OF_MEMORY;
+
+ SSL_CTX_set_alpn_protos(ctx->sslctx,
+ (const uint8_t *)QUICHE_H3_APPLICATION_PROTOCOL,
+ sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1);
+
+ SSL_CTX_set_default_verify_paths(ctx->sslctx);
+
+ /* Open the file if a TLS or QUIC backend has not done this before. */
+ Curl_tls_keylog_open();
+ if(Curl_tls_keylog_enabled()) {
+ SSL_CTX_set_keylog_callback(ctx->sslctx, keylog_callback);
+ }
+
+ ctx->ssl = SSL_new(ctx->sslctx);
+ if(!ctx->ssl)
+ return CURLE_QUIC_CONNECT_ERROR;
+
+ SSL_set_app_data(ctx->ssl, cf);
+
+ if((0 == Curl_inet_pton(AF_INET, cf->conn->host.name, checkip))
+#ifdef ENABLE_IPV6
+ && (0 == Curl_inet_pton(AF_INET6, cf->conn->host.name, checkip))
+#endif
+ ) {
+ char *snihost = Curl_ssl_snihost(data, cf->conn->host.name, NULL);
+ if(!snihost || !SSL_set_tlsext_host_name(ctx->ssl, snihost)) {
+ failf(data, "Failed set SNI");
+ SSL_free(ctx->ssl);
+ ctx->ssl = NULL;
+ return CURLE_QUIC_CONNECT_ERROR;
+ }
+ }
+
+ return CURLE_OK;
+}
+
/**
* All about the H3 internals of a stream
*/
struct stream_ctx {
int64_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_off_t upload_left; /* number of request bytes left to upload */
bool closed; /* TRUE on stream close */
@@ -217,11 +256,10 @@ static void stream_send_suspend(struct Curl_cfilter *cf, data->req.keepon |= KEEP_SEND_HOLD;
++ctx->sends_on_hold;
if(H3_STREAM_ID(data) >= 0)
- DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] suspend sending",
- H3_STREAM_ID(data)));
+ CURL_TRC_CF(data, cf, "[%"PRId64"] suspend sending",
+ H3_STREAM_ID(data));
else
- DEBUGF(LOG_CF(data, cf, "[%s] suspend sending",
- data->state.url));
+ CURL_TRC_CF(data, cf, "[%s] suspend sending", data->state.url);
}
}
@@ -234,11 +272,10 @@ static void stream_send_resume(struct Curl_cfilter *cf, data->req.keepon &= ~KEEP_SEND_HOLD;
--ctx->sends_on_hold;
if(H3_STREAM_ID(data) >= 0)
- DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] resume sending",
- H3_STREAM_ID(data)));
+ CURL_TRC_CF(data, cf, "[%"PRId64"] resume sending",
+ H3_STREAM_ID(data));
else
- DEBUGF(LOG_CF(data, cf, "[%s] resume sending",
- data->state.url));
+ CURL_TRC_CF(data, cf, "[%s] resume sending", data->state.url);
Curl_expire(data, 0, EXPIRE_RUN_NOW);
}
}
@@ -277,7 +314,7 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf, stream->id = -1;
Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp,
H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT);
- DEBUGF(LOG_CF(data, cf, "data setup"));
+ Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
return CURLE_OK;
}
@@ -288,13 +325,13 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) (void)cf;
if(stream) {
- DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] easy handle is done",
- stream->id));
+ CURL_TRC_CF(data, cf, "[%"PRId64"] easy handle is done", stream->id);
if(stream_send_is_suspended(data)) {
data->req.keepon &= ~KEEP_SEND_HOLD;
--ctx->sends_on_hold;
}
Curl_bufq_free(&stream->recvbuf);
+ Curl_h1_req_parse_free(&stream->h1);
free(stream);
H3_STREAM_LCTX(data) = NULL;
}
@@ -379,8 +416,12 @@ static int cb_each_header(uint8_t *name, size_t name_len, struct stream_ctx *stream = H3_STREAM_CTX(x->data);
CURLcode result;
- (void)stream;
+ 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",
+ stream->id, (int)value_len, value);
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);
@@ -388,6 +429,9 @@ 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",
+ stream->id, (int)name_len, name,
+ (int)value_len, value);
result = write_resp_raw(x->cf, x->data, name, name_len);
if(!result)
result = write_resp_raw(x->cf, x->data, ": ", 2);
@@ -397,10 +441,8 @@ 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) {
- DEBUGF(LOG_CF(x->data, x->cf,
- "[h3sid=%"PRId64"][HEADERS][%.*s: %.*s] error %d",
- stream? stream->id : -1, (int)name_len, name,
- (int)value_len, value, result));
+ CURL_TRC_CF(x->data, x->cf, "[%"PRId64"] on header error %d",
+ stream->id, result);
}
return result;
}
@@ -455,8 +497,8 @@ static CURLcode cf_recv_body(struct Curl_cfilter *cf, stream_resp_read, &cb_ctx, &result);
if(nwritten < 0 && result != CURLE_AGAIN) {
- DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] recv_body error %zd",
- stream->id, nwritten));
+ CURL_TRC_CF(data, cf, "[%"PRId64"] recv_body error %zd",
+ stream->id, nwritten);
failf(data, "Error %d in HTTP/3 response body for stream[%"PRId64"]",
result, stream->id);
stream->closed = TRUE;
@@ -514,7 +556,7 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf, rc, stream3_id);
return CURLE_RECV_ERROR;
}
- DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][HEADERS]", stream3_id));
+ CURL_TRC_CF(data, cf, "[%"PRId64"] <- [HEADERS]", stream3_id);
break;
case QUICHE_H3_EVENT_DATA:
@@ -524,7 +566,7 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf, break;
case QUICHE_H3_EVENT_RESET:
- DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][RESET]", stream3_id));
+ CURL_TRC_CF(data, cf, "[%"PRId64"] RESET", stream3_id);
stream->closed = TRUE;
stream->reset = TRUE;
stream->send_closed = TRUE;
@@ -532,7 +574,7 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf, break;
case QUICHE_H3_EVENT_FINISHED:
- DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][FINISHED]", stream3_id));
+ CURL_TRC_CF(data, cf, "[%"PRId64"] CLOSED", stream3_id);
if(!stream->resp_hds_complete) {
result = write_resp_raw(cf, data, "\r\n", 2);
if(result)
@@ -541,15 +583,16 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf, }
stream->closed = TRUE;
streamclose(cf->conn, "End of stream");
+ data->req.keepon &= ~KEEP_SEND_HOLD;
break;
case QUICHE_H3_EVENT_GOAWAY:
- DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][GOAWAY]", stream3_id));
+ CURL_TRC_CF(data, cf, "[%"PRId64"] <- [GOAWAY]", stream3_id);
break;
default:
- DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] recv, unhandled event %d",
- stream3_id, quiche_h3_event_type(ev)));
+ CURL_TRC_CF(data, cf, "[%"PRId64"] recv, unhandled event %d",
+ stream3_id, quiche_h3_event_type(ev));
break;
}
return result;
@@ -571,26 +614,25 @@ static CURLcode cf_poll_events(struct Curl_cfilter *cf, break;
}
else if(stream3_id < 0) {
- DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] error poll: %"PRId64,
- stream? stream->id : -1, stream3_id));
+ CURL_TRC_CF(data, cf, "[%"PRId64"] error poll: %"PRId64,
+ stream? stream->id : -1, stream3_id);
return CURLE_HTTP3;
}
sdata = get_stream_easy(cf, data, stream3_id);
if(!sdata) {
- DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] discard event %s for "
- "unknown [h3sid=%"PRId64"]",
- stream? stream->id : -1, cf_ev_name(ev),
- stream3_id));
+ CURL_TRC_CF(data, cf, "[%"PRId64"] discard event %s for "
+ "unknown [%"PRId64"]",
+ stream? stream->id : -1, cf_ev_name(ev), stream3_id);
}
else {
result = h3_process_event(cf, sdata, stream3_id, ev);
drain_stream(cf, sdata);
if(result) {
- DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] error processing event %s "
- "for [h3sid=%"PRId64"] -> %d",
- stream? stream->id : -1, cf_ev_name(ev),
- stream3_id, result));
+ CURL_TRC_CF(data, cf, "[%"PRId64"] error processing event %s "
+ "for [%"PRId64"] -> %d",
+ stream? stream->id : -1, cf_ev_name(ev),
+ stream3_id, result);
if(data == sdata) {
/* Only report this error to the caller if it is about the
* transfer we were called with. Otherwise we fail a transfer
@@ -633,7 +675,7 @@ static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen, &recv_info);
if(nread < 0) {
if(QUICHE_ERR_DONE == nread) {
- DEBUGF(LOG_CF(r->data, r->cf, "ingress, quiche is DONE"));
+ CURL_TRC_CF(r->data, r->cf, "ingress, quiche is DONE");
return CURLE_OK;
}
else if(QUICHE_ERR_TLS_FAIL == nread) {
@@ -650,8 +692,8 @@ static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen, }
}
else if((size_t)nread < pktlen) {
- DEBUGF(LOG_CF(r->data, r->cf, "ingress, quiche only read %zd/%zu bytes",
- nread, pktlen));
+ CURL_TRC_CF(r->data, r->cf, "ingress, quiche only read %zd/%zu bytes",
+ nread, pktlen);
}
return CURLE_OK;
@@ -665,6 +707,10 @@ static CURLcode cf_process_ingress(struct Curl_cfilter *cf, CURLcode result;
DEBUGASSERT(ctx->qconn);
+ result = quic_x509_store_setup(cf, data);
+ if(result)
+ return result;
+
rctx.cf = cf;
rctx.data = data;
rctx.pkts = 0;
@@ -720,10 +766,20 @@ 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;
struct read_ctx readx;
size_t pkt_count, gsolen;
+ expiry_ns = quiche_conn_timeout_as_nanos(ctx->qconn);
+ 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");
+ return CURLE_SEND_ERROR;
+ }
+ }
+
result = vquic_flush(cf, data, &ctx->q);
if(result) {
if(result == CURLE_AGAIN) {
@@ -742,9 +798,6 @@ static CURLcode cf_flush_egress(struct Curl_cfilter *cf, /* add the next packet to send, if any, to our buffer */
nread = Curl_bufq_sipn(&ctx->q.sendbuf, 0,
read_pkt_to_send, &readx, &result);
- /* DEBUGF(LOG_CF(data, cf, "sip packet(maxlen=%zu) -> %zd, %d",
- (size_t)0, nread, result)); */
-
if(nread < 0) {
if(result != CURLE_AGAIN)
return result;
@@ -795,8 +848,8 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf, failf(data,
"HTTP/3 stream %" PRId64 " reset by server", stream->id);
*err = stream->resp_got_header? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR;
- DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, was reset -> %d",
- stream->id, *err));
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv, was reset -> %d",
+ stream->id, *err);
}
else if(!stream->resp_got_header) {
failf(data,
@@ -805,14 +858,12 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf, stream->id);
/* *err = CURLE_PARTIAL_FILE; */
*err = CURLE_RECV_ERROR;
- DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed incomplete"
- " -> %d", stream->id, *err));
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv, closed incomplete"
+ " -> %d", stream->id, *err);
}
else {
*err = CURLE_OK;
nread = 0;
- DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed ok"
- " -> %d", stream->id, *err));
}
return nread;
}
@@ -833,14 +884,14 @@ 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);
- DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read recvbuf(len=%zu) "
- "-> %zd, %d", stream->id, len, nread, *err));
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) "
+ "-> %zd, %d", stream->id, len, nread, *err);
if(nread < 0)
goto out;
}
if(cf_process_ingress(cf, data)) {
- DEBUGF(LOG_CF(data, cf, "cf_recv, error on ingress"));
+ CURL_TRC_CF(data, cf, "cf_recv, error on ingress");
*err = CURLE_RECV_ERROR;
nread = -1;
goto out;
@@ -850,8 +901,8 @@ 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);
- DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read recvbuf(len=%zu) "
- "-> %zd, %d", stream->id, len, nread, *err));
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) "
+ "-> %zd, %d", stream->id, len, nread, *err);
if(nread < 0)
goto out;
}
@@ -878,16 +929,15 @@ static ssize_t cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data, out:
result = cf_flush_egress(cf, data);
if(result) {
- DEBUGF(LOG_CF(data, cf, "cf_recv, flush egress failed"));
+ CURL_TRC_CF(data, cf, "cf_recv, flush egress failed");
*err = result;
nread = -1;
}
if(nread > 0)
ctx->data_recvd += nread;
- DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] cf_recv(total=%"
- CURL_FORMAT_CURL_OFF_T ") -> %zd, %d",
- stream ? stream->id : (int64_t)0,
- ctx->data_recvd, nread, *err));
+ CURL_TRC_CF(data, cf, "[%"PRId64"] cf_recv(total=%"
+ CURL_FORMAT_CURL_OFF_T ") -> %zd, %d",
+ stream->id, ctx->data_recvd, nread, *err);
return nread;
}
@@ -904,7 +954,6 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf, struct stream_ctx *stream = H3_STREAM_CTX(data);
size_t nheader, i;
int64_t stream3_id;
- struct h1_req_parser h1;
struct dynhds h2_headers;
quiche_h3_header *nva = NULL;
ssize_t nwritten;
@@ -918,21 +967,25 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf, DEBUGASSERT(stream);
}
- Curl_h1_req_parse_init(&h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST);
DEBUGASSERT(stream);
- nwritten = Curl_h1_req_parse_read(&h1, buf, len, NULL, 0, err);
+ nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err);
if(nwritten < 0)
goto out;
- DEBUGASSERT(h1.done);
- DEBUGASSERT(h1.req);
+ if(!stream->h1.done) {
+ /* need more data */
+ goto out;
+ }
+ DEBUGASSERT(stream->h1.req);
- *err = Curl_http_req_to_h2(&h2_headers, h1.req, data);
+ *err = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data);
if(*err) {
nwritten = -1;
goto out;
}
+ /* no longer needed */
+ Curl_h1_req_parse_free(&stream->h1);
nheader = Curl_dynhds_count(&h2_headers);
nva = malloc(sizeof(quiche_h3_header) * nheader);
@@ -975,16 +1028,16 @@ 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. */
- DEBUGF(LOG_CF(data, cf, "send_request(%s) rejected with BLOCKED",
- data->state.url));
+ CURL_TRC_CF(data, cf, "send_request(%s) rejected with BLOCKED",
+ data->state.url);
stream_send_suspend(cf, data);
*err = CURLE_AGAIN;
nwritten = -1;
goto out;
}
else {
- DEBUGF(LOG_CF(data, cf, "send_request(%s) -> %" PRId64,
- data->state.url, stream3_id));
+ CURL_TRC_CF(data, cf, "send_request(%s) -> %" PRId64,
+ data->state.url, stream3_id);
}
*err = CURLE_SEND_ERROR;
nwritten = -1;
@@ -997,13 +1050,18 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf, stream->closed = FALSE;
stream->reset = FALSE;
- infof(data, "Using HTTP/3 Stream ID: %" PRId64, stream3_id);
- DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] opened for %s",
- stream3_id, data->state.url));
+ if(Curl_trc_is_verbose(data)) {
+ infof(data, "[HTTP/3] [%" PRId64 "] OPENED stream for %s",
+ stream->id, data->state.url);
+ for(i = 0; i < nheader; ++i) {
+ infof(data, "[HTTP/3] [%" PRId64 "] [%.*s: %.*s]", stream->id,
+ (int)nva[i].name_len, nva[i].name,
+ (int)nva[i].value_len, nva[i].value);
+ }
+ }
out:
free(nva);
- Curl_h1_req_parse_free(&h1);
Curl_dynhds_free(&h2_headers);
return nwritten;
}
@@ -1037,24 +1095,40 @@ 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)) {
- DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] send_body(len=%zu) "
- "-> window exhausted", stream->id, len));
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) "
+ "-> window exhausted", stream->id, len);
stream_send_suspend(cf, data);
}
*err = CURLE_AGAIN;
nwritten = -1;
goto out;
}
+ else if(nwritten == QUICHE_H3_TRANSPORT_ERR_INVALID_STREAM_STATE &&
+ stream->closed && stream->resp_hds_complete) {
+ /* sending request body on a stream that has been closed by the
+ * server. If the server has send us a final response, we should
+ * silently discard the send data.
+ * This happens for example on redirects where the server, instead
+ * of reading the full request body just closed the stream after
+ * 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"
+ "on closed stream with response", stream->id);
+ *err = CURLE_OK;
+ nwritten = (ssize_t)len;
+ goto out;
+ }
else if(nwritten == QUICHE_H3_TRANSPORT_ERR_FINAL_SIZE) {
- DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] send_body(len=%zu) "
- "-> exceeds size", stream->id, len));
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) "
+ "-> exceeds size", stream->id, len);
*err = CURLE_SEND_ERROR;
nwritten = -1;
goto out;
}
else if(nwritten < 0) {
- DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] send_body(len=%zu) "
- "-> quiche err %zd", stream->id, len, nwritten));
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) "
+ "-> quiche err %zd", stream->id, len, nwritten);
*err = CURLE_SEND_ERROR;
nwritten = -1;
goto out;
@@ -1068,9 +1142,9 @@ static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, if(stream->upload_left == 0)
stream->send_closed = TRUE;
- DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] send body(len=%zu, "
- "left=%" CURL_FORMAT_CURL_OFF_T ") -> %zd",
- stream->id, len, stream->upload_left, nwritten));
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] send body(len=%zu, "
+ "left=%" CURL_FORMAT_CURL_OFF_T ") -> %zd",
+ stream->id, len, stream->upload_left, nwritten);
*err = CURLE_OK;
}
}
@@ -1081,8 +1155,8 @@ out: *err = result;
nwritten = -1;
}
- DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_send(len=%zu) -> %zd, %d",
- stream? stream->id : -1, len, nwritten, *err));
+ CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %zd, %d",
+ stream? stream->id : -1, len, nwritten, *err);
return nwritten;
}
@@ -1171,16 +1245,20 @@ 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);
- DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] DONE_SEND -> %zd, %d",
- stream->id, sent, result));
+ CURL_TRC_CF(data, cf, "[%"PRId64"] DONE_SEND -> %zd, %d",
+ stream->id, sent, result);
}
break;
}
- case CF_CTRL_DATA_IDLE:
- result = cf_flush_egress(cf, data);
- if(result)
- DEBUGF(LOG_CF(data, cf, "data idle, flush egress -> %d", result));
+ case CF_CTRL_DATA_IDLE: {
+ struct stream_ctx *stream = H3_STREAM_CTX(data);
+ if(stream && !stream->closed) {
+ result = cf_flush_egress(cf, data);
+ if(result)
+ CURL_TRC_CF(data, cf, "data idle, flush egress -> %d", result);
+ }
break;
+ }
default:
break;
}
@@ -1210,7 +1288,7 @@ static CURLcode cf_verify_peer(struct Curl_cfilter *cf, goto out;
}
else
- DEBUGF(LOG_CF(data, cf, "Skipped certificate verification"));
+ CURL_TRC_CF(data, cf, "Skipped certificate verification");
ctx->h3config = quiche_h3_config_new();
if(!ctx->h3config) {
@@ -1298,15 +1376,9 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, DEBUGASSERT(!ctx->ssl);
DEBUGASSERT(!ctx->sslctx);
- ctx->sslctx = quic_ssl_ctx(data);
- if(!ctx->sslctx)
- return CURLE_QUIC_CONNECT_ERROR;
- ctx->ssl = SSL_new(ctx->sslctx);
- if(!ctx->ssl)
- return CURLE_QUIC_CONNECT_ERROR;
-
- SSL_set_app_data(ctx->ssl, cf);
- SSL_set_tlsext_host_name(ctx->ssl, cf->conn->host.name);
+ result = quic_ssl_setup(cf, data);
+ if(result)
+ return result;
result = Curl_rand(data, ctx->scid, sizeof(ctx->scid));
if(result)
@@ -1357,8 +1429,8 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, offset += 1 + alpn_len;
}
- DEBUGF(LOG_CF(data, cf, "Sent QUIC client Initial, ALPN: %s",
- alpn_protocols + 1));
+ CURL_TRC_CF(data, cf, "Sent QUIC client Initial, ALPN: %s",
+ alpn_protocols + 1);
}
return CURLE_OK;
@@ -1389,7 +1461,7 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, if(ctx->reconnect_at.tv_sec && Curl_timediff(now, ctx->reconnect_at) < 0) {
/* Not time yet to attempt the next connect */
- DEBUGF(LOG_CF(data, cf, "waiting for reconnect time"));
+ CURL_TRC_CF(data, cf, "waiting for reconnect time");
goto out;
}
@@ -1412,12 +1484,12 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, goto out;
if(quiche_conn_is_established(ctx->qconn)) {
- DEBUGF(LOG_CF(data, cf, "handshake complete after %dms",
- (int)Curl_timediff(now, ctx->started_at)));
+ CURL_TRC_CF(data, cf, "handshake complete after %dms",
+ (int)Curl_timediff(now, ctx->started_at));
ctx->handshake_at = now;
result = cf_verify_peer(cf, data);
if(!result) {
- DEBUGF(LOG_CF(data, cf, "peer verified"));
+ CURL_TRC_CF(data, cf, "peer verified");
cf->connected = TRUE;
cf->conn->alpn = CURL_HTTP_VERSION_3;
*done = TRUE;
@@ -1436,8 +1508,8 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, */
int reconn_delay_ms = 200;
- DEBUGF(LOG_CF(data, cf, "connect, remote closed, reconnect after %dms",
- reconn_delay_ms));
+ CURL_TRC_CF(data, cf, "connect, remote closed, reconnect after %dms",
+ reconn_delay_ms);
Curl_conn_cf_close(cf->next, data);
cf_quiche_ctx_clear(ctx);
result = Curl_conn_cf_connect(cf->next, data, FALSE, done);
@@ -1503,7 +1575,7 @@ static CURLcode cf_quiche_query(struct Curl_cfilter *cf, max_streams += quiche_conn_peer_streams_left_bidi(ctx->qconn);
}
*pres1 = (max_streams > INT_MAX)? INT_MAX : (int)max_streams;
- DEBUGF(LOG_CF(data, cf, "query: MAX_CONCURRENT -> %d", *pres1));
+ CURL_TRC_CF(data, cf, "query: MAX_CONCURRENT -> %d", *pres1);
return CURLE_OK;
}
case CF_QUERY_CONNECT_REPLY_MS:
diff --git a/libs/libcurl/src/vquic/vquic.c b/libs/libcurl/src/vquic/vquic.c index 2cf802bd76..e49b6567ad 100644 --- a/libs/libcurl/src/vquic/vquic.c +++ b/libs/libcurl/src/vquic/vquic.c @@ -43,12 +43,14 @@ #include "bufq.h"
#include "dynbuf.h"
#include "cfilters.h"
-#include "curl_log.h"
+#include "curl_trc.h"
#include "curl_msh3.h"
#include "curl_ngtcp2.h"
#include "curl_quiche.h"
+#include "rand.h"
#include "vquic.h"
#include "vquic_int.h"
+#include "strerror.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -88,6 +90,16 @@ CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx) #else
qctx->no_gso = TRUE;
#endif
+#ifdef DEBUGBUILD
+ {
+ char *p = getenv("CURL_DBG_QUIC_WBLOCK");
+ if(p) {
+ long l = strtol(p, NULL, 10);
+ if(l >= 0 && l <= 100)
+ qctx->wblock_percent = (int)l;
+ }
+ }
+#endif
return CURLE_OK;
}
@@ -230,6 +242,17 @@ static CURLcode vquic_send_packets(struct Curl_cfilter *cf, const uint8_t *pkt, size_t pktlen,
size_t gsolen, size_t *psent)
{
+#ifdef DEBUGBUILD
+ /* simulate network blocking/partial writes */
+ if(qctx->wblock_percent > 0) {
+ unsigned char c;
+ Curl_rand(data, &c, 1);
+ if(c >= ((100-qctx->wblock_percent)*256/100)) {
+ CURL_TRC_CF(data, cf, "vquic_flush() simulate EWOULDBLOCK");
+ return CURLE_AGAIN;
+ }
+ }
+#endif
if(qctx->no_gso && pktlen > gsolen) {
return send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent);
}
@@ -253,11 +276,9 @@ CURLcode vquic_flush(struct Curl_cfilter *cf, struct Curl_easy *data, blen = qctx->split_len;
}
- DEBUGF(LOG_CF(data, cf, "vquic_send(len=%zu, gso=%zu)",
- blen, gsolen));
result = vquic_send_packets(cf, data, qctx, buf, blen, gsolen, &sent);
- DEBUGF(LOG_CF(data, cf, "vquic_send(len=%zu, gso=%zu) -> %d, sent=%zu",
- blen, gsolen, result, sent));
+ CURL_TRC_CF(data, cf, "vquic_send(len=%zu, gso=%zu) -> %d, sent=%zu",
+ blen, gsolen, result, sent);
if(result) {
if(result == CURLE_AGAIN) {
Curl_bufq_skip(&qctx->sendbuf, sent);
@@ -288,9 +309,9 @@ CURLcode vquic_send_tail_split(struct Curl_cfilter *cf, struct Curl_easy *data, qctx->split_len = Curl_bufq_len(&qctx->sendbuf) - tail_len;
qctx->split_gsolen = gsolen;
qctx->gsolen = tail_gsolen;
- DEBUGF(LOG_CF(data, cf, "vquic_send_tail_split: [%zu gso=%zu][%zu gso=%zu]",
- qctx->split_len, qctx->split_gsolen,
- tail_len, qctx->gsolen));
+ CURL_TRC_CF(data, cf, "vquic_send_tail_split: [%zu gso=%zu][%zu gso=%zu]",
+ qctx->split_len, qctx->split_gsolen,
+ tail_len, qctx->gsolen);
return vquic_flush(cf, data, qctx);
}
@@ -308,6 +329,7 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, struct sockaddr_storage remote_addr[MMSG_NUM];
size_t total_nread, pkts;
int mcount, i, n;
+ char errstr[STRERROR_LEN];
CURLcode result = CURLE_OK;
DEBUGASSERT(max_pkts > 0);
@@ -330,12 +352,12 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, ;
if(mcount == -1) {
if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
- DEBUGF(LOG_CF(data, cf, "ingress, recvmmsg -> EAGAIN"));
+ CURL_TRC_CF(data, cf, "ingress, recvmmsg -> EAGAIN");
goto out;
}
if(!cf->connected && SOCKERRNO == ECONNREFUSED) {
- const char *r_ip;
- int r_port;
+ const char *r_ip = NULL;
+ int r_port = 0;
Curl_cf_socket_peek(cf->next, data, NULL, NULL,
&r_ip, &r_port, NULL, NULL);
failf(data, "QUIC: connection to %s port %u refused",
@@ -343,13 +365,14 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, result = CURLE_COULDNT_CONNECT;
goto out;
}
- failf(data, "QUIC: recvmsg() unexpectedly returned %d (errno=%d)",
- mcount, SOCKERRNO);
+ Curl_strerror(SOCKERRNO, errstr, sizeof(errstr));
+ failf(data, "QUIC: recvmsg() unexpectedly returned %d (errno=%d; %s)",
+ mcount, SOCKERRNO, errstr);
result = CURLE_RECV_ERROR;
goto out;
}
- DEBUGF(LOG_CF(data, cf, "recvmmsg() -> %d packets", mcount));
+ CURL_TRC_CF(data, cf, "recvmmsg() -> %d packets", mcount);
pkts += mcount;
for(i = 0; i < mcount; ++i) {
total_nread += mmsg[i].msg_len;
@@ -362,8 +385,9 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, }
out:
- DEBUGF(LOG_CF(data, cf, "recvd %zu packets with %zu bytes -> %d",
- pkts, total_nread, result));
+ if(total_nread || result)
+ CURL_TRC_CF(data, cf, "recvd %zu packets with %zu bytes -> %d",
+ pkts, total_nread, result);
return result;
}
@@ -380,6 +404,7 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, struct sockaddr_storage remote_addr;
size_t total_nread, pkts;
ssize_t nread;
+ char errstr[STRERROR_LEN];
CURLcode result = CURLE_OK;
msg_iov.iov_base = buf;
@@ -401,8 +426,8 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, goto out;
}
if(!cf->connected && SOCKERRNO == ECONNREFUSED) {
- const char *r_ip;
- int r_port;
+ const char *r_ip = NULL;
+ int r_port = 0;
Curl_cf_socket_peek(cf->next, data, NULL, NULL,
&r_ip, &r_port, NULL, NULL);
failf(data, "QUIC: connection to %s port %u refused",
@@ -410,8 +435,9 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, result = CURLE_COULDNT_CONNECT;
goto out;
}
- failf(data, "QUIC: recvmsg() unexpectedly returned %zd (errno=%d)",
- nread, SOCKERRNO);
+ Curl_strerror(SOCKERRNO, errstr, sizeof(errstr));
+ failf(data, "QUIC: recvmsg() unexpectedly returned %zd (errno=%d; %s)",
+ nread, SOCKERRNO, errstr);
result = CURLE_RECV_ERROR;
goto out;
}
@@ -425,8 +451,9 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, }
out:
- DEBUGF(LOG_CF(data, cf, "recvd %zu packets with %zu bytes -> %d",
- pkts, total_nread, result));
+ if(total_nread || result)
+ CURL_TRC_CF(data, cf, "recvd %zu packets with %zu bytes -> %d",
+ pkts, total_nread, result);
return result;
}
@@ -443,6 +470,7 @@ static CURLcode recvfrom_packets(struct Curl_cfilter *cf, socklen_t remote_addrlen = sizeof(remote_addr);
size_t total_nread, pkts;
ssize_t nread;
+ char errstr[STRERROR_LEN];
CURLcode result = CURLE_OK;
DEBUGASSERT(max_pkts > 0);
@@ -454,12 +482,12 @@ static CURLcode recvfrom_packets(struct Curl_cfilter *cf, ;
if(nread == -1) {
if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
- DEBUGF(LOG_CF(data, cf, "ingress, recvfrom -> EAGAIN"));
+ CURL_TRC_CF(data, cf, "ingress, recvfrom -> EAGAIN");
goto out;
}
if(!cf->connected && SOCKERRNO == ECONNREFUSED) {
- const char *r_ip;
- int r_port;
+ const char *r_ip = NULL;
+ int r_port = 0;
Curl_cf_socket_peek(cf->next, data, NULL, NULL,
&r_ip, &r_port, NULL, NULL);
failf(data, "QUIC: connection to %s port %u refused",
@@ -467,8 +495,9 @@ static CURLcode recvfrom_packets(struct Curl_cfilter *cf, result = CURLE_COULDNT_CONNECT;
goto out;
}
- failf(data, "QUIC: recvfrom() unexpectedly returned %zd (errno=%d)",
- nread, SOCKERRNO);
+ Curl_strerror(SOCKERRNO, errstr, sizeof(errstr));
+ failf(data, "QUIC: recvfrom() unexpectedly returned %zd (errno=%d; %s)",
+ nread, SOCKERRNO, errstr);
result = CURLE_RECV_ERROR;
goto out;
}
@@ -482,8 +511,9 @@ static CURLcode recvfrom_packets(struct Curl_cfilter *cf, }
out:
- DEBUGF(LOG_CF(data, cf, "recvd %zu packets with %zu bytes -> %d",
- pkts, total_nread, result));
+ if(total_nread || result)
+ CURL_TRC_CF(data, cf, "recvd %zu packets with %zu bytes -> %d",
+ pkts, total_nread, result);
return result;
}
#endif /* !HAVE_SENDMMSG && !HAVE_SENDMSG */
diff --git a/libs/libcurl/src/vquic/vquic_int.h b/libs/libcurl/src/vquic/vquic_int.h index 6c137fc494..3fe06fb9c9 100644 --- a/libs/libcurl/src/vquic/vquic_int.h +++ b/libs/libcurl/src/vquic/vquic_int.h @@ -41,6 +41,9 @@ struct cf_quic_ctx { size_t gsolen; /* length of individual packets in send buf */
size_t split_len; /* if != 0, buffer length after which GSO differs */
size_t split_gsolen; /* length of individual packets after split_len */
+#ifdef DEBUGBUILD
+ int wblock_percent; /* percent of writes doing EAGAIN */
+#endif
bool no_gso; /* do not use gso on sending */
};
|