113498266Sopenharmony_ci/*************************************************************************** 213498266Sopenharmony_ci * _ _ ____ _ 313498266Sopenharmony_ci * Project ___| | | | _ \| | 413498266Sopenharmony_ci * / __| | | | |_) | | 513498266Sopenharmony_ci * | (__| |_| | _ <| |___ 613498266Sopenharmony_ci * \___|\___/|_| \_\_____| 713498266Sopenharmony_ci * 813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 913498266Sopenharmony_ci * 1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which 1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms 1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html. 1313498266Sopenharmony_ci * 1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell 1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is 1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file. 1713498266Sopenharmony_ci * 1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 1913498266Sopenharmony_ci * KIND, either express or implied. 2013498266Sopenharmony_ci * 2113498266Sopenharmony_ci * SPDX-License-Identifier: curl 2213498266Sopenharmony_ci * 2313498266Sopenharmony_ci ***************************************************************************/ 2413498266Sopenharmony_ci 2513498266Sopenharmony_ci#include "curl_setup.h" 2613498266Sopenharmony_ci 2713498266Sopenharmony_ci#if defined(USE_NGTCP2) && defined(USE_NGHTTP3) 2813498266Sopenharmony_ci#include <ngtcp2/ngtcp2.h> 2913498266Sopenharmony_ci#include <nghttp3/nghttp3.h> 3013498266Sopenharmony_ci 3113498266Sopenharmony_ci#ifdef USE_OPENSSL 3213498266Sopenharmony_ci#include <openssl/err.h> 3313498266Sopenharmony_ci#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) 3413498266Sopenharmony_ci#include <ngtcp2/ngtcp2_crypto_boringssl.h> 3513498266Sopenharmony_ci#else 3613498266Sopenharmony_ci#include <ngtcp2/ngtcp2_crypto_quictls.h> 3713498266Sopenharmony_ci#endif 3813498266Sopenharmony_ci#include "vtls/openssl.h" 3913498266Sopenharmony_ci#elif defined(USE_GNUTLS) 4013498266Sopenharmony_ci#include <ngtcp2/ngtcp2_crypto_gnutls.h> 4113498266Sopenharmony_ci#include "vtls/gtls.h" 4213498266Sopenharmony_ci#elif defined(USE_WOLFSSL) 4313498266Sopenharmony_ci#include <ngtcp2/ngtcp2_crypto_wolfssl.h> 4413498266Sopenharmony_ci#endif 4513498266Sopenharmony_ci 4613498266Sopenharmony_ci#include "urldata.h" 4713498266Sopenharmony_ci#include "sendf.h" 4813498266Sopenharmony_ci#include "strdup.h" 4913498266Sopenharmony_ci#include "rand.h" 5013498266Sopenharmony_ci#include "multiif.h" 5113498266Sopenharmony_ci#include "strcase.h" 5213498266Sopenharmony_ci#include "cfilters.h" 5313498266Sopenharmony_ci#include "cf-socket.h" 5413498266Sopenharmony_ci#include "connect.h" 5513498266Sopenharmony_ci#include "progress.h" 5613498266Sopenharmony_ci#include "strerror.h" 5713498266Sopenharmony_ci#include "dynbuf.h" 5813498266Sopenharmony_ci#include "http1.h" 5913498266Sopenharmony_ci#include "select.h" 6013498266Sopenharmony_ci#include "inet_pton.h" 6113498266Sopenharmony_ci#include "vquic.h" 6213498266Sopenharmony_ci#include "vquic_int.h" 6313498266Sopenharmony_ci#include "vquic-tls.h" 6413498266Sopenharmony_ci#include "vtls/keylog.h" 6513498266Sopenharmony_ci#include "vtls/vtls.h" 6613498266Sopenharmony_ci#include "curl_ngtcp2.h" 6713498266Sopenharmony_ci 6813498266Sopenharmony_ci#include "warnless.h" 6913498266Sopenharmony_ci 7013498266Sopenharmony_ci/* The last 3 #include files should be in this order */ 7113498266Sopenharmony_ci#include "curl_printf.h" 7213498266Sopenharmony_ci#include "curl_memory.h" 7313498266Sopenharmony_ci#include "memdebug.h" 7413498266Sopenharmony_ci 7513498266Sopenharmony_ci 7613498266Sopenharmony_ci#define QUIC_MAX_STREAMS (256*1024) 7713498266Sopenharmony_ci#define QUIC_MAX_DATA (1*1024*1024) 7813498266Sopenharmony_ci#define QUIC_HANDSHAKE_TIMEOUT (10*NGTCP2_SECONDS) 7913498266Sopenharmony_ci 8013498266Sopenharmony_ci/* A stream window is the maximum amount we need to buffer for 8113498266Sopenharmony_ci * each active transfer. We use HTTP/3 flow control and only ACK 8213498266Sopenharmony_ci * when we take things out of the buffer. 8313498266Sopenharmony_ci * Chunk size is large enough to take a full DATA frame */ 8413498266Sopenharmony_ci#define H3_STREAM_WINDOW_SIZE (128 * 1024) 8513498266Sopenharmony_ci#define H3_STREAM_CHUNK_SIZE (16 * 1024) 8613498266Sopenharmony_ci/* The pool keeps spares around and half of a full stream windows 8713498266Sopenharmony_ci * seems good. More does not seem to improve performance. 8813498266Sopenharmony_ci * The benefit of the pool is that stream buffer to not keep 8913498266Sopenharmony_ci * spares. So memory consumption goes down when streams run empty, 9013498266Sopenharmony_ci * have a large upload done, etc. */ 9113498266Sopenharmony_ci#define H3_STREAM_POOL_SPARES \ 9213498266Sopenharmony_ci (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE ) / 2 9313498266Sopenharmony_ci/* Receive and Send max number of chunks just follows from the 9413498266Sopenharmony_ci * chunk size and window size */ 9513498266Sopenharmony_ci#define H3_STREAM_RECV_CHUNKS \ 9613498266Sopenharmony_ci (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) 9713498266Sopenharmony_ci#define H3_STREAM_SEND_CHUNKS \ 9813498266Sopenharmony_ci (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) 9913498266Sopenharmony_ci 10013498266Sopenharmony_ci 10113498266Sopenharmony_ci/* 10213498266Sopenharmony_ci * Store ngtcp2 version info in this buffer. 10313498266Sopenharmony_ci */ 10413498266Sopenharmony_civoid Curl_ngtcp2_ver(char *p, size_t len) 10513498266Sopenharmony_ci{ 10613498266Sopenharmony_ci const ngtcp2_info *ng2 = ngtcp2_version(0); 10713498266Sopenharmony_ci const nghttp3_info *ht3 = nghttp3_version(0); 10813498266Sopenharmony_ci (void)msnprintf(p, len, "ngtcp2/%s nghttp3/%s", 10913498266Sopenharmony_ci ng2->version_str, ht3->version_str); 11013498266Sopenharmony_ci} 11113498266Sopenharmony_ci 11213498266Sopenharmony_cistruct cf_ngtcp2_ctx { 11313498266Sopenharmony_ci struct cf_quic_ctx q; 11413498266Sopenharmony_ci struct ssl_peer peer; 11513498266Sopenharmony_ci struct quic_tls_ctx tls; 11613498266Sopenharmony_ci ngtcp2_path connected_path; 11713498266Sopenharmony_ci ngtcp2_conn *qconn; 11813498266Sopenharmony_ci ngtcp2_cid dcid; 11913498266Sopenharmony_ci ngtcp2_cid scid; 12013498266Sopenharmony_ci uint32_t version; 12113498266Sopenharmony_ci ngtcp2_settings settings; 12213498266Sopenharmony_ci ngtcp2_transport_params transport_params; 12313498266Sopenharmony_ci ngtcp2_ccerr last_error; 12413498266Sopenharmony_ci ngtcp2_crypto_conn_ref conn_ref; 12513498266Sopenharmony_ci struct cf_call_data call_data; 12613498266Sopenharmony_ci nghttp3_conn *h3conn; 12713498266Sopenharmony_ci nghttp3_settings h3settings; 12813498266Sopenharmony_ci struct curltime started_at; /* time the current attempt started */ 12913498266Sopenharmony_ci struct curltime handshake_at; /* time connect handshake finished */ 13013498266Sopenharmony_ci struct curltime reconnect_at; /* time the next attempt should start */ 13113498266Sopenharmony_ci struct bufc_pool stream_bufcp; /* chunk pool for streams */ 13213498266Sopenharmony_ci size_t max_stream_window; /* max flow window for one stream */ 13313498266Sopenharmony_ci uint64_t max_idle_ms; /* max idle time for QUIC connection */ 13413498266Sopenharmony_ci int qlogfd; 13513498266Sopenharmony_ci}; 13613498266Sopenharmony_ci 13713498266Sopenharmony_ci/* How to access `call_data` from a cf_ngtcp2 filter */ 13813498266Sopenharmony_ci#undef CF_CTX_CALL_DATA 13913498266Sopenharmony_ci#define CF_CTX_CALL_DATA(cf) \ 14013498266Sopenharmony_ci ((struct cf_ngtcp2_ctx *)(cf)->ctx)->call_data 14113498266Sopenharmony_ci 14213498266Sopenharmony_ci/** 14313498266Sopenharmony_ci * All about the H3 internals of a stream 14413498266Sopenharmony_ci */ 14513498266Sopenharmony_cistruct h3_stream_ctx { 14613498266Sopenharmony_ci int64_t id; /* HTTP/3 protocol identifier */ 14713498266Sopenharmony_ci struct bufq sendbuf; /* h3 request body */ 14813498266Sopenharmony_ci struct bufq recvbuf; /* h3 response body */ 14913498266Sopenharmony_ci struct h1_req_parser h1; /* h1 request parsing */ 15013498266Sopenharmony_ci size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */ 15113498266Sopenharmony_ci size_t upload_blocked_len; /* the amount written last and EGAINed */ 15213498266Sopenharmony_ci size_t recv_buf_nonflow; /* buffered bytes, not counting for flow control */ 15313498266Sopenharmony_ci uint64_t error3; /* HTTP/3 stream error code */ 15413498266Sopenharmony_ci curl_off_t upload_left; /* number of request bytes left to upload */ 15513498266Sopenharmony_ci int status_code; /* HTTP status code */ 15613498266Sopenharmony_ci bool resp_hds_complete; /* we have a complete, final response */ 15713498266Sopenharmony_ci bool closed; /* TRUE on stream close */ 15813498266Sopenharmony_ci bool reset; /* TRUE on stream reset */ 15913498266Sopenharmony_ci bool send_closed; /* stream is local closed */ 16013498266Sopenharmony_ci BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */ 16113498266Sopenharmony_ci}; 16213498266Sopenharmony_ci 16313498266Sopenharmony_ci#define H3_STREAM_CTX(d) ((struct h3_stream_ctx *)(((d) && (d)->req.p.http)? \ 16413498266Sopenharmony_ci ((struct HTTP *)(d)->req.p.http)->h3_ctx \ 16513498266Sopenharmony_ci : NULL)) 16613498266Sopenharmony_ci#define H3_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->h3_ctx 16713498266Sopenharmony_ci#define H3_STREAM_ID(d) (H3_STREAM_CTX(d)? \ 16813498266Sopenharmony_ci H3_STREAM_CTX(d)->id : -2) 16913498266Sopenharmony_ci 17013498266Sopenharmony_cistatic CURLcode h3_data_setup(struct Curl_cfilter *cf, 17113498266Sopenharmony_ci struct Curl_easy *data) 17213498266Sopenharmony_ci{ 17313498266Sopenharmony_ci struct cf_ngtcp2_ctx *ctx = cf->ctx; 17413498266Sopenharmony_ci struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 17513498266Sopenharmony_ci 17613498266Sopenharmony_ci if(!data || !data->req.p.http) { 17713498266Sopenharmony_ci failf(data, "initialization failure, transfer not http initialized"); 17813498266Sopenharmony_ci return CURLE_FAILED_INIT; 17913498266Sopenharmony_ci } 18013498266Sopenharmony_ci 18113498266Sopenharmony_ci if(stream) 18213498266Sopenharmony_ci return CURLE_OK; 18313498266Sopenharmony_ci 18413498266Sopenharmony_ci stream = calloc(1, sizeof(*stream)); 18513498266Sopenharmony_ci if(!stream) 18613498266Sopenharmony_ci return CURLE_OUT_OF_MEMORY; 18713498266Sopenharmony_ci 18813498266Sopenharmony_ci stream->id = -1; 18913498266Sopenharmony_ci /* on send, we control how much we put into the buffer */ 19013498266Sopenharmony_ci Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp, 19113498266Sopenharmony_ci H3_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE); 19213498266Sopenharmony_ci stream->sendbuf_len_in_flight = 0; 19313498266Sopenharmony_ci /* on recv, we need a flexible buffer limit since we also write 19413498266Sopenharmony_ci * headers to it that are not counted against the nghttp3 flow limits. */ 19513498266Sopenharmony_ci Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp, 19613498266Sopenharmony_ci H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); 19713498266Sopenharmony_ci stream->recv_buf_nonflow = 0; 19813498266Sopenharmony_ci Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); 19913498266Sopenharmony_ci 20013498266Sopenharmony_ci H3_STREAM_LCTX(data) = stream; 20113498266Sopenharmony_ci return CURLE_OK; 20213498266Sopenharmony_ci} 20313498266Sopenharmony_ci 20413498266Sopenharmony_cistatic void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) 20513498266Sopenharmony_ci{ 20613498266Sopenharmony_ci struct cf_ngtcp2_ctx *ctx = cf->ctx; 20713498266Sopenharmony_ci struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 20813498266Sopenharmony_ci 20913498266Sopenharmony_ci (void)cf; 21013498266Sopenharmony_ci if(stream) { 21113498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%"PRId64"] easy handle is done", stream->id); 21213498266Sopenharmony_ci if(ctx->h3conn && !stream->closed) { 21313498266Sopenharmony_ci nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream->id); 21413498266Sopenharmony_ci nghttp3_conn_close_stream(ctx->h3conn, stream->id, 21513498266Sopenharmony_ci NGHTTP3_H3_REQUEST_CANCELLED); 21613498266Sopenharmony_ci nghttp3_conn_set_stream_user_data(ctx->h3conn, stream->id, NULL); 21713498266Sopenharmony_ci ngtcp2_conn_set_stream_user_data(ctx->qconn, stream->id, NULL); 21813498266Sopenharmony_ci stream->closed = TRUE; 21913498266Sopenharmony_ci } 22013498266Sopenharmony_ci 22113498266Sopenharmony_ci Curl_bufq_free(&stream->sendbuf); 22213498266Sopenharmony_ci Curl_bufq_free(&stream->recvbuf); 22313498266Sopenharmony_ci Curl_h1_req_parse_free(&stream->h1); 22413498266Sopenharmony_ci free(stream); 22513498266Sopenharmony_ci H3_STREAM_LCTX(data) = NULL; 22613498266Sopenharmony_ci } 22713498266Sopenharmony_ci} 22813498266Sopenharmony_ci 22913498266Sopenharmony_cistatic struct Curl_easy *get_stream_easy(struct Curl_cfilter *cf, 23013498266Sopenharmony_ci struct Curl_easy *data, 23113498266Sopenharmony_ci int64_t stream_id) 23213498266Sopenharmony_ci{ 23313498266Sopenharmony_ci struct Curl_easy *sdata; 23413498266Sopenharmony_ci 23513498266Sopenharmony_ci (void)cf; 23613498266Sopenharmony_ci if(H3_STREAM_ID(data) == stream_id) { 23713498266Sopenharmony_ci return data; 23813498266Sopenharmony_ci } 23913498266Sopenharmony_ci else { 24013498266Sopenharmony_ci DEBUGASSERT(data->multi); 24113498266Sopenharmony_ci for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { 24213498266Sopenharmony_ci if((sdata->conn == data->conn) && H3_STREAM_ID(sdata) == stream_id) { 24313498266Sopenharmony_ci return sdata; 24413498266Sopenharmony_ci } 24513498266Sopenharmony_ci } 24613498266Sopenharmony_ci } 24713498266Sopenharmony_ci return NULL; 24813498266Sopenharmony_ci} 24913498266Sopenharmony_ci 25013498266Sopenharmony_cistatic void h3_drain_stream(struct Curl_cfilter *cf, 25113498266Sopenharmony_ci struct Curl_easy *data) 25213498266Sopenharmony_ci{ 25313498266Sopenharmony_ci struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 25413498266Sopenharmony_ci unsigned char bits; 25513498266Sopenharmony_ci 25613498266Sopenharmony_ci (void)cf; 25713498266Sopenharmony_ci bits = CURL_CSELECT_IN; 25813498266Sopenharmony_ci if(stream && stream->upload_left && !stream->send_closed) 25913498266Sopenharmony_ci bits |= CURL_CSELECT_OUT; 26013498266Sopenharmony_ci if(data->state.select_bits != bits) { 26113498266Sopenharmony_ci data->state.select_bits = bits; 26213498266Sopenharmony_ci Curl_expire(data, 0, EXPIRE_RUN_NOW); 26313498266Sopenharmony_ci } 26413498266Sopenharmony_ci} 26513498266Sopenharmony_ci 26613498266Sopenharmony_ci/* ngtcp2 default congestion controller does not perform pacing. Limit 26713498266Sopenharmony_ci the maximum packet burst to MAX_PKT_BURST packets. */ 26813498266Sopenharmony_ci#define MAX_PKT_BURST 10 26913498266Sopenharmony_ci 27013498266Sopenharmony_cistruct pkt_io_ctx { 27113498266Sopenharmony_ci struct Curl_cfilter *cf; 27213498266Sopenharmony_ci struct Curl_easy *data; 27313498266Sopenharmony_ci ngtcp2_tstamp ts; 27413498266Sopenharmony_ci size_t pkt_count; 27513498266Sopenharmony_ci ngtcp2_path_storage ps; 27613498266Sopenharmony_ci}; 27713498266Sopenharmony_ci 27813498266Sopenharmony_cistatic void pktx_update_time(struct pkt_io_ctx *pktx, 27913498266Sopenharmony_ci struct Curl_cfilter *cf) 28013498266Sopenharmony_ci{ 28113498266Sopenharmony_ci struct cf_ngtcp2_ctx *ctx = cf->ctx; 28213498266Sopenharmony_ci 28313498266Sopenharmony_ci vquic_ctx_update_time(&ctx->q); 28413498266Sopenharmony_ci pktx->ts = ctx->q.last_op.tv_sec * NGTCP2_SECONDS + 28513498266Sopenharmony_ci ctx->q.last_op.tv_usec * NGTCP2_MICROSECONDS; 28613498266Sopenharmony_ci} 28713498266Sopenharmony_ci 28813498266Sopenharmony_cistatic void pktx_init(struct pkt_io_ctx *pktx, 28913498266Sopenharmony_ci struct Curl_cfilter *cf, 29013498266Sopenharmony_ci struct Curl_easy *data) 29113498266Sopenharmony_ci{ 29213498266Sopenharmony_ci pktx->cf = cf; 29313498266Sopenharmony_ci pktx->data = data; 29413498266Sopenharmony_ci pktx->pkt_count = 0; 29513498266Sopenharmony_ci ngtcp2_path_storage_zero(&pktx->ps); 29613498266Sopenharmony_ci pktx_update_time(pktx, cf); 29713498266Sopenharmony_ci} 29813498266Sopenharmony_ci 29913498266Sopenharmony_cistatic CURLcode cf_progress_ingress(struct Curl_cfilter *cf, 30013498266Sopenharmony_ci struct Curl_easy *data, 30113498266Sopenharmony_ci struct pkt_io_ctx *pktx); 30213498266Sopenharmony_cistatic CURLcode cf_progress_egress(struct Curl_cfilter *cf, 30313498266Sopenharmony_ci struct Curl_easy *data, 30413498266Sopenharmony_ci struct pkt_io_ctx *pktx); 30513498266Sopenharmony_cistatic int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id, 30613498266Sopenharmony_ci uint64_t datalen, void *user_data, 30713498266Sopenharmony_ci void *stream_user_data); 30813498266Sopenharmony_ci 30913498266Sopenharmony_cistatic ngtcp2_conn *get_conn(ngtcp2_crypto_conn_ref *conn_ref) 31013498266Sopenharmony_ci{ 31113498266Sopenharmony_ci struct Curl_cfilter *cf = conn_ref->user_data; 31213498266Sopenharmony_ci struct cf_ngtcp2_ctx *ctx = cf->ctx; 31313498266Sopenharmony_ci return ctx->qconn; 31413498266Sopenharmony_ci} 31513498266Sopenharmony_ci 31613498266Sopenharmony_ci#ifdef DEBUG_NGTCP2 31713498266Sopenharmony_cistatic void quic_printf(void *user_data, const char *fmt, ...) 31813498266Sopenharmony_ci{ 31913498266Sopenharmony_ci struct Curl_cfilter *cf = user_data; 32013498266Sopenharmony_ci struct cf_ngtcp2_ctx *ctx = cf->ctx; 32113498266Sopenharmony_ci 32213498266Sopenharmony_ci (void)ctx; /* TODO: need an easy handle to infof() message */ 32313498266Sopenharmony_ci va_list ap; 32413498266Sopenharmony_ci va_start(ap, fmt); 32513498266Sopenharmony_ci vfprintf(stderr, fmt, ap); 32613498266Sopenharmony_ci va_end(ap); 32713498266Sopenharmony_ci fprintf(stderr, "\n"); 32813498266Sopenharmony_ci} 32913498266Sopenharmony_ci#endif 33013498266Sopenharmony_ci 33113498266Sopenharmony_cistatic void qlog_callback(void *user_data, uint32_t flags, 33213498266Sopenharmony_ci const void *data, size_t datalen) 33313498266Sopenharmony_ci{ 33413498266Sopenharmony_ci struct Curl_cfilter *cf = user_data; 33513498266Sopenharmony_ci struct cf_ngtcp2_ctx *ctx = cf->ctx; 33613498266Sopenharmony_ci (void)flags; 33713498266Sopenharmony_ci if(ctx->qlogfd != -1) { 33813498266Sopenharmony_ci ssize_t rc = write(ctx->qlogfd, data, datalen); 33913498266Sopenharmony_ci if(rc == -1) { 34013498266Sopenharmony_ci /* on write error, stop further write attempts */ 34113498266Sopenharmony_ci close(ctx->qlogfd); 34213498266Sopenharmony_ci ctx->qlogfd = -1; 34313498266Sopenharmony_ci } 34413498266Sopenharmony_ci } 34513498266Sopenharmony_ci 34613498266Sopenharmony_ci} 34713498266Sopenharmony_ci 34813498266Sopenharmony_cistatic void quic_settings(struct cf_ngtcp2_ctx *ctx, 34913498266Sopenharmony_ci struct Curl_easy *data, 35013498266Sopenharmony_ci struct pkt_io_ctx *pktx) 35113498266Sopenharmony_ci{ 35213498266Sopenharmony_ci ngtcp2_settings *s = &ctx->settings; 35313498266Sopenharmony_ci ngtcp2_transport_params *t = &ctx->transport_params; 35413498266Sopenharmony_ci 35513498266Sopenharmony_ci ngtcp2_settings_default(s); 35613498266Sopenharmony_ci ngtcp2_transport_params_default(t); 35713498266Sopenharmony_ci#ifdef DEBUG_NGTCP2 35813498266Sopenharmony_ci s->log_printf = quic_printf; 35913498266Sopenharmony_ci#else 36013498266Sopenharmony_ci s->log_printf = NULL; 36113498266Sopenharmony_ci#endif 36213498266Sopenharmony_ci 36313498266Sopenharmony_ci (void)data; 36413498266Sopenharmony_ci s->initial_ts = pktx->ts; 36513498266Sopenharmony_ci s->handshake_timeout = QUIC_HANDSHAKE_TIMEOUT; 36613498266Sopenharmony_ci s->max_window = 100 * ctx->max_stream_window; 36713498266Sopenharmony_ci s->max_stream_window = ctx->max_stream_window; 36813498266Sopenharmony_ci 36913498266Sopenharmony_ci t->initial_max_data = 10 * ctx->max_stream_window; 37013498266Sopenharmony_ci t->initial_max_stream_data_bidi_local = ctx->max_stream_window; 37113498266Sopenharmony_ci t->initial_max_stream_data_bidi_remote = ctx->max_stream_window; 37213498266Sopenharmony_ci t->initial_max_stream_data_uni = ctx->max_stream_window; 37313498266Sopenharmony_ci t->initial_max_streams_bidi = QUIC_MAX_STREAMS; 37413498266Sopenharmony_ci t->initial_max_streams_uni = QUIC_MAX_STREAMS; 37513498266Sopenharmony_ci t->max_idle_timeout = (ctx->max_idle_ms * NGTCP2_MILLISECONDS); 37613498266Sopenharmony_ci if(ctx->qlogfd != -1) { 37713498266Sopenharmony_ci s->qlog_write = qlog_callback; 37813498266Sopenharmony_ci } 37913498266Sopenharmony_ci} 38013498266Sopenharmony_ci 38113498266Sopenharmony_cistatic int init_ngh3_conn(struct Curl_cfilter *cf); 38213498266Sopenharmony_ci 38313498266Sopenharmony_cistatic int cb_handshake_completed(ngtcp2_conn *tconn, void *user_data) 38413498266Sopenharmony_ci{ 38513498266Sopenharmony_ci (void)user_data; 38613498266Sopenharmony_ci (void)tconn; 38713498266Sopenharmony_ci return 0; 38813498266Sopenharmony_ci} 38913498266Sopenharmony_ci 39013498266Sopenharmony_cistatic void report_consumed_data(struct Curl_cfilter *cf, 39113498266Sopenharmony_ci struct Curl_easy *data, 39213498266Sopenharmony_ci size_t consumed) 39313498266Sopenharmony_ci{ 39413498266Sopenharmony_ci struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 39513498266Sopenharmony_ci struct cf_ngtcp2_ctx *ctx = cf->ctx; 39613498266Sopenharmony_ci 39713498266Sopenharmony_ci if(!stream) 39813498266Sopenharmony_ci return; 39913498266Sopenharmony_ci /* the HTTP/1.1 response headers are written to the buffer, but 40013498266Sopenharmony_ci * consuming those does not count against flow control. */ 40113498266Sopenharmony_ci if(stream->recv_buf_nonflow) { 40213498266Sopenharmony_ci if(consumed >= stream->recv_buf_nonflow) { 40313498266Sopenharmony_ci consumed -= stream->recv_buf_nonflow; 40413498266Sopenharmony_ci stream->recv_buf_nonflow = 0; 40513498266Sopenharmony_ci } 40613498266Sopenharmony_ci else { 40713498266Sopenharmony_ci stream->recv_buf_nonflow -= consumed; 40813498266Sopenharmony_ci consumed = 0; 40913498266Sopenharmony_ci } 41013498266Sopenharmony_ci } 41113498266Sopenharmony_ci if(consumed > 0) { 41213498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] ACK %zu bytes of DATA", 41313498266Sopenharmony_ci stream->id, consumed); 41413498266Sopenharmony_ci ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream->id, 41513498266Sopenharmony_ci consumed); 41613498266Sopenharmony_ci ngtcp2_conn_extend_max_offset(ctx->qconn, consumed); 41713498266Sopenharmony_ci } 41813498266Sopenharmony_ci} 41913498266Sopenharmony_ci 42013498266Sopenharmony_cistatic int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags, 42113498266Sopenharmony_ci int64_t stream_id, uint64_t offset, 42213498266Sopenharmony_ci const uint8_t *buf, size_t buflen, 42313498266Sopenharmony_ci void *user_data, void *stream_user_data) 42413498266Sopenharmony_ci{ 42513498266Sopenharmony_ci struct Curl_cfilter *cf = user_data; 42613498266Sopenharmony_ci struct cf_ngtcp2_ctx *ctx = cf->ctx; 42713498266Sopenharmony_ci nghttp3_ssize nconsumed; 42813498266Sopenharmony_ci int fin = (flags & NGTCP2_STREAM_DATA_FLAG_FIN) ? 1 : 0; 42913498266Sopenharmony_ci struct Curl_easy *data = stream_user_data; 43013498266Sopenharmony_ci (void)offset; 43113498266Sopenharmony_ci (void)data; 43213498266Sopenharmony_ci 43313498266Sopenharmony_ci nconsumed = 43413498266Sopenharmony_ci nghttp3_conn_read_stream(ctx->h3conn, stream_id, buf, buflen, fin); 43513498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] read_stream(len=%zu) -> %zd", 43613498266Sopenharmony_ci stream_id, buflen, nconsumed); 43713498266Sopenharmony_ci if(nconsumed < 0) { 43813498266Sopenharmony_ci if(!data) { 43913498266Sopenharmony_ci struct Curl_easy *cdata = CF_DATA_CURRENT(cf); 44013498266Sopenharmony_ci CURL_TRC_CF(cdata, cf, "[%" PRId64 "] nghttp3 error on stream not " 44113498266Sopenharmony_ci "used by us, ignored", stream_id); 44213498266Sopenharmony_ci return 0; 44313498266Sopenharmony_ci } 44413498266Sopenharmony_ci ngtcp2_ccerr_set_application_error( 44513498266Sopenharmony_ci &ctx->last_error, 44613498266Sopenharmony_ci nghttp3_err_infer_quic_app_error_code((int)nconsumed), NULL, 0); 44713498266Sopenharmony_ci return NGTCP2_ERR_CALLBACK_FAILURE; 44813498266Sopenharmony_ci } 44913498266Sopenharmony_ci 45013498266Sopenharmony_ci /* number of bytes inside buflen which consists of framing overhead 45113498266Sopenharmony_ci * including QPACK HEADERS. In other words, it does not consume payload of 45213498266Sopenharmony_ci * DATA frame. */ 45313498266Sopenharmony_ci ngtcp2_conn_extend_max_stream_offset(tconn, stream_id, nconsumed); 45413498266Sopenharmony_ci ngtcp2_conn_extend_max_offset(tconn, nconsumed); 45513498266Sopenharmony_ci 45613498266Sopenharmony_ci return 0; 45713498266Sopenharmony_ci} 45813498266Sopenharmony_ci 45913498266Sopenharmony_cistatic int 46013498266Sopenharmony_cicb_acked_stream_data_offset(ngtcp2_conn *tconn, int64_t stream_id, 46113498266Sopenharmony_ci uint64_t offset, uint64_t datalen, void *user_data, 46213498266Sopenharmony_ci void *stream_user_data) 46313498266Sopenharmony_ci{ 46413498266Sopenharmony_ci struct Curl_cfilter *cf = user_data; 46513498266Sopenharmony_ci struct cf_ngtcp2_ctx *ctx = cf->ctx; 46613498266Sopenharmony_ci int rv; 46713498266Sopenharmony_ci (void)stream_id; 46813498266Sopenharmony_ci (void)tconn; 46913498266Sopenharmony_ci (void)offset; 47013498266Sopenharmony_ci (void)datalen; 47113498266Sopenharmony_ci (void)stream_user_data; 47213498266Sopenharmony_ci 47313498266Sopenharmony_ci rv = nghttp3_conn_add_ack_offset(ctx->h3conn, stream_id, datalen); 47413498266Sopenharmony_ci if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { 47513498266Sopenharmony_ci return NGTCP2_ERR_CALLBACK_FAILURE; 47613498266Sopenharmony_ci } 47713498266Sopenharmony_ci 47813498266Sopenharmony_ci return 0; 47913498266Sopenharmony_ci} 48013498266Sopenharmony_ci 48113498266Sopenharmony_cistatic int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags, 48213498266Sopenharmony_ci int64_t stream3_id, uint64_t app_error_code, 48313498266Sopenharmony_ci void *user_data, void *stream_user_data) 48413498266Sopenharmony_ci{ 48513498266Sopenharmony_ci struct Curl_cfilter *cf = user_data; 48613498266Sopenharmony_ci struct Curl_easy *data = stream_user_data; 48713498266Sopenharmony_ci struct cf_ngtcp2_ctx *ctx = cf->ctx; 48813498266Sopenharmony_ci int rv; 48913498266Sopenharmony_ci 49013498266Sopenharmony_ci (void)tconn; 49113498266Sopenharmony_ci (void)data; 49213498266Sopenharmony_ci /* stream is closed... */ 49313498266Sopenharmony_ci 49413498266Sopenharmony_ci if(!(flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET)) { 49513498266Sopenharmony_ci app_error_code = NGHTTP3_H3_NO_ERROR; 49613498266Sopenharmony_ci } 49713498266Sopenharmony_ci 49813498266Sopenharmony_ci rv = nghttp3_conn_close_stream(ctx->h3conn, stream3_id, 49913498266Sopenharmony_ci app_error_code); 50013498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] quic close(err=%" 50113498266Sopenharmony_ci PRIu64 ") -> %d", stream3_id, app_error_code, rv); 50213498266Sopenharmony_ci if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { 50313498266Sopenharmony_ci ngtcp2_ccerr_set_application_error( 50413498266Sopenharmony_ci &ctx->last_error, nghttp3_err_infer_quic_app_error_code(rv), NULL, 0); 50513498266Sopenharmony_ci return NGTCP2_ERR_CALLBACK_FAILURE; 50613498266Sopenharmony_ci } 50713498266Sopenharmony_ci 50813498266Sopenharmony_ci return 0; 50913498266Sopenharmony_ci} 51013498266Sopenharmony_ci 51113498266Sopenharmony_cistatic int cb_stream_reset(ngtcp2_conn *tconn, int64_t stream_id, 51213498266Sopenharmony_ci uint64_t final_size, uint64_t app_error_code, 51313498266Sopenharmony_ci void *user_data, void *stream_user_data) 51413498266Sopenharmony_ci{ 51513498266Sopenharmony_ci struct Curl_cfilter *cf = user_data; 51613498266Sopenharmony_ci struct cf_ngtcp2_ctx *ctx = cf->ctx; 51713498266Sopenharmony_ci struct Curl_easy *data = stream_user_data; 51813498266Sopenharmony_ci int rv; 51913498266Sopenharmony_ci (void)tconn; 52013498266Sopenharmony_ci (void)final_size; 52113498266Sopenharmony_ci (void)app_error_code; 52213498266Sopenharmony_ci (void)data; 52313498266Sopenharmony_ci 52413498266Sopenharmony_ci rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id); 52513498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv); 52613498266Sopenharmony_ci if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { 52713498266Sopenharmony_ci return NGTCP2_ERR_CALLBACK_FAILURE; 52813498266Sopenharmony_ci } 52913498266Sopenharmony_ci 53013498266Sopenharmony_ci return 0; 53113498266Sopenharmony_ci} 53213498266Sopenharmony_ci 53313498266Sopenharmony_cistatic int cb_stream_stop_sending(ngtcp2_conn *tconn, int64_t stream_id, 53413498266Sopenharmony_ci uint64_t app_error_code, void *user_data, 53513498266Sopenharmony_ci void *stream_user_data) 53613498266Sopenharmony_ci{ 53713498266Sopenharmony_ci struct Curl_cfilter *cf = user_data; 53813498266Sopenharmony_ci struct cf_ngtcp2_ctx *ctx = cf->ctx; 53913498266Sopenharmony_ci int rv; 54013498266Sopenharmony_ci (void)tconn; 54113498266Sopenharmony_ci (void)app_error_code; 54213498266Sopenharmony_ci (void)stream_user_data; 54313498266Sopenharmony_ci 54413498266Sopenharmony_ci rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id); 54513498266Sopenharmony_ci if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { 54613498266Sopenharmony_ci return NGTCP2_ERR_CALLBACK_FAILURE; 54713498266Sopenharmony_ci } 54813498266Sopenharmony_ci 54913498266Sopenharmony_ci return 0; 55013498266Sopenharmony_ci} 55113498266Sopenharmony_ci 55213498266Sopenharmony_cistatic int cb_extend_max_local_streams_bidi(ngtcp2_conn *tconn, 55313498266Sopenharmony_ci uint64_t max_streams, 55413498266Sopenharmony_ci void *user_data) 55513498266Sopenharmony_ci{ 55613498266Sopenharmony_ci (void)tconn; 55713498266Sopenharmony_ci (void)max_streams; 55813498266Sopenharmony_ci (void)user_data; 55913498266Sopenharmony_ci 56013498266Sopenharmony_ci return 0; 56113498266Sopenharmony_ci} 56213498266Sopenharmony_ci 56313498266Sopenharmony_cistatic int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t stream_id, 56413498266Sopenharmony_ci uint64_t max_data, void *user_data, 56513498266Sopenharmony_ci void *stream_user_data) 56613498266Sopenharmony_ci{ 56713498266Sopenharmony_ci struct Curl_cfilter *cf = user_data; 56813498266Sopenharmony_ci struct cf_ngtcp2_ctx *ctx = cf->ctx; 56913498266Sopenharmony_ci struct Curl_easy *data = CF_DATA_CURRENT(cf); 57013498266Sopenharmony_ci struct Curl_easy *s_data; 57113498266Sopenharmony_ci struct h3_stream_ctx *stream; 57213498266Sopenharmony_ci int rv; 57313498266Sopenharmony_ci (void)tconn; 57413498266Sopenharmony_ci (void)max_data; 57513498266Sopenharmony_ci (void)stream_user_data; 57613498266Sopenharmony_ci 57713498266Sopenharmony_ci rv = nghttp3_conn_unblock_stream(ctx->h3conn, stream_id); 57813498266Sopenharmony_ci if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { 57913498266Sopenharmony_ci return NGTCP2_ERR_CALLBACK_FAILURE; 58013498266Sopenharmony_ci } 58113498266Sopenharmony_ci s_data = get_stream_easy(cf, data, stream_id); 58213498266Sopenharmony_ci stream = H3_STREAM_CTX(s_data); 58313498266Sopenharmony_ci if(stream && stream->quic_flow_blocked) { 58413498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] unblock quic flow", stream_id); 58513498266Sopenharmony_ci stream->quic_flow_blocked = FALSE; 58613498266Sopenharmony_ci h3_drain_stream(cf, data); 58713498266Sopenharmony_ci } 58813498266Sopenharmony_ci return 0; 58913498266Sopenharmony_ci} 59013498266Sopenharmony_ci 59113498266Sopenharmony_cistatic void cb_rand(uint8_t *dest, size_t destlen, 59213498266Sopenharmony_ci const ngtcp2_rand_ctx *rand_ctx) 59313498266Sopenharmony_ci{ 59413498266Sopenharmony_ci CURLcode result; 59513498266Sopenharmony_ci (void)rand_ctx; 59613498266Sopenharmony_ci 59713498266Sopenharmony_ci result = Curl_rand(NULL, dest, destlen); 59813498266Sopenharmony_ci if(result) { 59913498266Sopenharmony_ci /* cb_rand is only used for non-cryptographic context. If Curl_rand 60013498266Sopenharmony_ci failed, just fill 0 and call it *random*. */ 60113498266Sopenharmony_ci memset(dest, 0, destlen); 60213498266Sopenharmony_ci } 60313498266Sopenharmony_ci} 60413498266Sopenharmony_ci 60513498266Sopenharmony_cistatic int cb_get_new_connection_id(ngtcp2_conn *tconn, ngtcp2_cid *cid, 60613498266Sopenharmony_ci uint8_t *token, size_t cidlen, 60713498266Sopenharmony_ci void *user_data) 60813498266Sopenharmony_ci{ 60913498266Sopenharmony_ci CURLcode result; 61013498266Sopenharmony_ci (void)tconn; 61113498266Sopenharmony_ci (void)user_data; 61213498266Sopenharmony_ci 61313498266Sopenharmony_ci result = Curl_rand(NULL, cid->data, cidlen); 61413498266Sopenharmony_ci if(result) 61513498266Sopenharmony_ci return NGTCP2_ERR_CALLBACK_FAILURE; 61613498266Sopenharmony_ci cid->datalen = cidlen; 61713498266Sopenharmony_ci 61813498266Sopenharmony_ci result = Curl_rand(NULL, token, NGTCP2_STATELESS_RESET_TOKENLEN); 61913498266Sopenharmony_ci if(result) 62013498266Sopenharmony_ci return NGTCP2_ERR_CALLBACK_FAILURE; 62113498266Sopenharmony_ci 62213498266Sopenharmony_ci return 0; 62313498266Sopenharmony_ci} 62413498266Sopenharmony_ci 62513498266Sopenharmony_cistatic int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_encryption_level level, 62613498266Sopenharmony_ci void *user_data) 62713498266Sopenharmony_ci{ 62813498266Sopenharmony_ci struct Curl_cfilter *cf = user_data; 62913498266Sopenharmony_ci (void)tconn; 63013498266Sopenharmony_ci 63113498266Sopenharmony_ci if(level != NGTCP2_ENCRYPTION_LEVEL_1RTT) { 63213498266Sopenharmony_ci return 0; 63313498266Sopenharmony_ci } 63413498266Sopenharmony_ci 63513498266Sopenharmony_ci if(init_ngh3_conn(cf) != CURLE_OK) { 63613498266Sopenharmony_ci return NGTCP2_ERR_CALLBACK_FAILURE; 63713498266Sopenharmony_ci } 63813498266Sopenharmony_ci 63913498266Sopenharmony_ci return 0; 64013498266Sopenharmony_ci} 64113498266Sopenharmony_ci 64213498266Sopenharmony_cistatic ngtcp2_callbacks ng_callbacks = { 64313498266Sopenharmony_ci ngtcp2_crypto_client_initial_cb, 64413498266Sopenharmony_ci NULL, /* recv_client_initial */ 64513498266Sopenharmony_ci ngtcp2_crypto_recv_crypto_data_cb, 64613498266Sopenharmony_ci cb_handshake_completed, 64713498266Sopenharmony_ci NULL, /* recv_version_negotiation */ 64813498266Sopenharmony_ci ngtcp2_crypto_encrypt_cb, 64913498266Sopenharmony_ci ngtcp2_crypto_decrypt_cb, 65013498266Sopenharmony_ci ngtcp2_crypto_hp_mask_cb, 65113498266Sopenharmony_ci cb_recv_stream_data, 65213498266Sopenharmony_ci cb_acked_stream_data_offset, 65313498266Sopenharmony_ci NULL, /* stream_open */ 65413498266Sopenharmony_ci cb_stream_close, 65513498266Sopenharmony_ci NULL, /* recv_stateless_reset */ 65613498266Sopenharmony_ci ngtcp2_crypto_recv_retry_cb, 65713498266Sopenharmony_ci cb_extend_max_local_streams_bidi, 65813498266Sopenharmony_ci NULL, /* extend_max_local_streams_uni */ 65913498266Sopenharmony_ci cb_rand, 66013498266Sopenharmony_ci cb_get_new_connection_id, 66113498266Sopenharmony_ci NULL, /* remove_connection_id */ 66213498266Sopenharmony_ci ngtcp2_crypto_update_key_cb, /* update_key */ 66313498266Sopenharmony_ci NULL, /* path_validation */ 66413498266Sopenharmony_ci NULL, /* select_preferred_addr */ 66513498266Sopenharmony_ci cb_stream_reset, 66613498266Sopenharmony_ci NULL, /* extend_max_remote_streams_bidi */ 66713498266Sopenharmony_ci NULL, /* extend_max_remote_streams_uni */ 66813498266Sopenharmony_ci cb_extend_max_stream_data, 66913498266Sopenharmony_ci NULL, /* dcid_status */ 67013498266Sopenharmony_ci NULL, /* handshake_confirmed */ 67113498266Sopenharmony_ci NULL, /* recv_new_token */ 67213498266Sopenharmony_ci ngtcp2_crypto_delete_crypto_aead_ctx_cb, 67313498266Sopenharmony_ci ngtcp2_crypto_delete_crypto_cipher_ctx_cb, 67413498266Sopenharmony_ci NULL, /* recv_datagram */ 67513498266Sopenharmony_ci NULL, /* ack_datagram */ 67613498266Sopenharmony_ci NULL, /* lost_datagram */ 67713498266Sopenharmony_ci ngtcp2_crypto_get_path_challenge_data_cb, 67813498266Sopenharmony_ci cb_stream_stop_sending, 67913498266Sopenharmony_ci NULL, /* version_negotiation */ 68013498266Sopenharmony_ci cb_recv_rx_key, 68113498266Sopenharmony_ci NULL, /* recv_tx_key */ 68213498266Sopenharmony_ci NULL, /* early_data_rejected */ 68313498266Sopenharmony_ci}; 68413498266Sopenharmony_ci 68513498266Sopenharmony_ci/** 68613498266Sopenharmony_ci * Connection maintenance like timeouts on packet ACKs etc. are done by us, not 68713498266Sopenharmony_ci * the OS like for TCP. POLL events on the socket therefore are not 68813498266Sopenharmony_ci * sufficient. 68913498266Sopenharmony_ci * ngtcp2 tells us when it wants to be invoked again. We handle that via 69013498266Sopenharmony_ci * the `Curl_expire()` mechanisms. 69113498266Sopenharmony_ci */ 69213498266Sopenharmony_cistatic CURLcode check_and_set_expiry(struct Curl_cfilter *cf, 69313498266Sopenharmony_ci struct Curl_easy *data, 69413498266Sopenharmony_ci struct pkt_io_ctx *pktx) 69513498266Sopenharmony_ci{ 69613498266Sopenharmony_ci struct cf_ngtcp2_ctx *ctx = cf->ctx; 69713498266Sopenharmony_ci struct pkt_io_ctx local_pktx; 69813498266Sopenharmony_ci ngtcp2_tstamp expiry; 69913498266Sopenharmony_ci 70013498266Sopenharmony_ci if(!pktx) { 70113498266Sopenharmony_ci pktx_init(&local_pktx, cf, data); 70213498266Sopenharmony_ci pktx = &local_pktx; 70313498266Sopenharmony_ci } 70413498266Sopenharmony_ci else { 70513498266Sopenharmony_ci pktx_update_time(pktx, cf); 70613498266Sopenharmony_ci } 70713498266Sopenharmony_ci 70813498266Sopenharmony_ci expiry = ngtcp2_conn_get_expiry(ctx->qconn); 70913498266Sopenharmony_ci if(expiry != UINT64_MAX) { 71013498266Sopenharmony_ci if(expiry <= pktx->ts) { 71113498266Sopenharmony_ci CURLcode result; 71213498266Sopenharmony_ci int rv = ngtcp2_conn_handle_expiry(ctx->qconn, pktx->ts); 71313498266Sopenharmony_ci if(rv) { 71413498266Sopenharmony_ci failf(data, "ngtcp2_conn_handle_expiry returned error: %s", 71513498266Sopenharmony_ci ngtcp2_strerror(rv)); 71613498266Sopenharmony_ci ngtcp2_ccerr_set_liberr(&ctx->last_error, rv, NULL, 0); 71713498266Sopenharmony_ci return CURLE_SEND_ERROR; 71813498266Sopenharmony_ci } 71913498266Sopenharmony_ci result = cf_progress_ingress(cf, data, pktx); 72013498266Sopenharmony_ci if(result) 72113498266Sopenharmony_ci return result; 72213498266Sopenharmony_ci result = cf_progress_egress(cf, data, pktx); 72313498266Sopenharmony_ci if(result) 72413498266Sopenharmony_ci return result; 72513498266Sopenharmony_ci /* ask again, things might have changed */ 72613498266Sopenharmony_ci expiry = ngtcp2_conn_get_expiry(ctx->qconn); 72713498266Sopenharmony_ci } 72813498266Sopenharmony_ci 72913498266Sopenharmony_ci if(expiry > pktx->ts) { 73013498266Sopenharmony_ci ngtcp2_duration timeout = expiry - pktx->ts; 73113498266Sopenharmony_ci if(timeout % NGTCP2_MILLISECONDS) { 73213498266Sopenharmony_ci timeout += NGTCP2_MILLISECONDS; 73313498266Sopenharmony_ci } 73413498266Sopenharmony_ci Curl_expire(data, timeout / NGTCP2_MILLISECONDS, EXPIRE_QUIC); 73513498266Sopenharmony_ci } 73613498266Sopenharmony_ci } 73713498266Sopenharmony_ci return CURLE_OK; 73813498266Sopenharmony_ci} 73913498266Sopenharmony_ci 74013498266Sopenharmony_cistatic void cf_ngtcp2_adjust_pollset(struct Curl_cfilter *cf, 74113498266Sopenharmony_ci struct Curl_easy *data, 74213498266Sopenharmony_ci struct easy_pollset *ps) 74313498266Sopenharmony_ci{ 74413498266Sopenharmony_ci struct cf_ngtcp2_ctx *ctx = cf->ctx; 74513498266Sopenharmony_ci bool want_recv, want_send; 74613498266Sopenharmony_ci 74713498266Sopenharmony_ci if(!ctx->qconn) 74813498266Sopenharmony_ci return; 74913498266Sopenharmony_ci 75013498266Sopenharmony_ci Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send); 75113498266Sopenharmony_ci if(want_recv || want_send) { 75213498266Sopenharmony_ci struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 75313498266Sopenharmony_ci struct cf_call_data save; 75413498266Sopenharmony_ci bool c_exhaust, s_exhaust; 75513498266Sopenharmony_ci 75613498266Sopenharmony_ci CF_DATA_SAVE(save, cf, data); 75713498266Sopenharmony_ci c_exhaust = want_send && (!ngtcp2_conn_get_cwnd_left(ctx->qconn) || 75813498266Sopenharmony_ci !ngtcp2_conn_get_max_data_left(ctx->qconn)); 75913498266Sopenharmony_ci s_exhaust = want_send && stream && stream->id >= 0 && 76013498266Sopenharmony_ci stream->quic_flow_blocked; 76113498266Sopenharmony_ci want_recv = (want_recv || c_exhaust || s_exhaust); 76213498266Sopenharmony_ci want_send = (!s_exhaust && want_send) || 76313498266Sopenharmony_ci !Curl_bufq_is_empty(&ctx->q.sendbuf); 76413498266Sopenharmony_ci 76513498266Sopenharmony_ci Curl_pollset_set(data, ps, ctx->q.sockfd, want_recv, want_send); 76613498266Sopenharmony_ci CF_DATA_RESTORE(cf, save); 76713498266Sopenharmony_ci } 76813498266Sopenharmony_ci} 76913498266Sopenharmony_ci 77013498266Sopenharmony_cistatic int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id, 77113498266Sopenharmony_ci uint64_t app_error_code, void *user_data, 77213498266Sopenharmony_ci void *stream_user_data) 77313498266Sopenharmony_ci{ 77413498266Sopenharmony_ci struct Curl_cfilter *cf = user_data; 77513498266Sopenharmony_ci struct Curl_easy *data = stream_user_data; 77613498266Sopenharmony_ci struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 77713498266Sopenharmony_ci (void)conn; 77813498266Sopenharmony_ci (void)stream_id; 77913498266Sopenharmony_ci 78013498266Sopenharmony_ci /* we might be called by nghttp3 after we already cleaned up */ 78113498266Sopenharmony_ci if(!stream) 78213498266Sopenharmony_ci return 0; 78313498266Sopenharmony_ci 78413498266Sopenharmony_ci stream->closed = TRUE; 78513498266Sopenharmony_ci stream->error3 = app_error_code; 78613498266Sopenharmony_ci if(stream->error3 != NGHTTP3_H3_NO_ERROR) { 78713498266Sopenharmony_ci stream->reset = TRUE; 78813498266Sopenharmony_ci stream->send_closed = TRUE; 78913498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] RESET: error %" PRId64, 79013498266Sopenharmony_ci stream->id, stream->error3); 79113498266Sopenharmony_ci } 79213498266Sopenharmony_ci else { 79313498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] CLOSED", stream->id); 79413498266Sopenharmony_ci } 79513498266Sopenharmony_ci h3_drain_stream(cf, data); 79613498266Sopenharmony_ci return 0; 79713498266Sopenharmony_ci} 79813498266Sopenharmony_ci 79913498266Sopenharmony_ci/* 80013498266Sopenharmony_ci * write_resp_raw() copies response data in raw format to the `data`'s 80113498266Sopenharmony_ci * receive buffer. If not enough space is available, it appends to the 80213498266Sopenharmony_ci * `data`'s overflow buffer. 80313498266Sopenharmony_ci */ 80413498266Sopenharmony_cistatic CURLcode write_resp_raw(struct Curl_cfilter *cf, 80513498266Sopenharmony_ci struct Curl_easy *data, 80613498266Sopenharmony_ci const void *mem, size_t memlen, 80713498266Sopenharmony_ci bool flow) 80813498266Sopenharmony_ci{ 80913498266Sopenharmony_ci struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 81013498266Sopenharmony_ci CURLcode result = CURLE_OK; 81113498266Sopenharmony_ci ssize_t nwritten; 81213498266Sopenharmony_ci 81313498266Sopenharmony_ci (void)cf; 81413498266Sopenharmony_ci if(!stream) { 81513498266Sopenharmony_ci return CURLE_RECV_ERROR; 81613498266Sopenharmony_ci } 81713498266Sopenharmony_ci nwritten = Curl_bufq_write(&stream->recvbuf, mem, memlen, &result); 81813498266Sopenharmony_ci if(nwritten < 0) { 81913498266Sopenharmony_ci return result; 82013498266Sopenharmony_ci } 82113498266Sopenharmony_ci 82213498266Sopenharmony_ci if(!flow) 82313498266Sopenharmony_ci stream->recv_buf_nonflow += (size_t)nwritten; 82413498266Sopenharmony_ci 82513498266Sopenharmony_ci if((size_t)nwritten < memlen) { 82613498266Sopenharmony_ci /* This MUST not happen. Our recbuf is dimensioned to hold the 82713498266Sopenharmony_ci * full max_stream_window and then some for this very reason. */ 82813498266Sopenharmony_ci DEBUGASSERT(0); 82913498266Sopenharmony_ci return CURLE_RECV_ERROR; 83013498266Sopenharmony_ci } 83113498266Sopenharmony_ci return result; 83213498266Sopenharmony_ci} 83313498266Sopenharmony_ci 83413498266Sopenharmony_cistatic int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id, 83513498266Sopenharmony_ci const uint8_t *buf, size_t buflen, 83613498266Sopenharmony_ci void *user_data, void *stream_user_data) 83713498266Sopenharmony_ci{ 83813498266Sopenharmony_ci struct Curl_cfilter *cf = user_data; 83913498266Sopenharmony_ci struct Curl_easy *data = stream_user_data; 84013498266Sopenharmony_ci struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 84113498266Sopenharmony_ci CURLcode result; 84213498266Sopenharmony_ci 84313498266Sopenharmony_ci (void)conn; 84413498266Sopenharmony_ci (void)stream3_id; 84513498266Sopenharmony_ci 84613498266Sopenharmony_ci if(!stream) 84713498266Sopenharmony_ci return NGHTTP3_ERR_CALLBACK_FAILURE; 84813498266Sopenharmony_ci 84913498266Sopenharmony_ci result = write_resp_raw(cf, data, buf, buflen, TRUE); 85013498266Sopenharmony_ci if(result) { 85113498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, ERROR receiving %d", 85213498266Sopenharmony_ci stream->id, buflen, result); 85313498266Sopenharmony_ci return NGHTTP3_ERR_CALLBACK_FAILURE; 85413498266Sopenharmony_ci } 85513498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu", stream->id, buflen); 85613498266Sopenharmony_ci h3_drain_stream(cf, data); 85713498266Sopenharmony_ci return 0; 85813498266Sopenharmony_ci} 85913498266Sopenharmony_ci 86013498266Sopenharmony_cistatic int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream3_id, 86113498266Sopenharmony_ci size_t consumed, void *user_data, 86213498266Sopenharmony_ci void *stream_user_data) 86313498266Sopenharmony_ci{ 86413498266Sopenharmony_ci struct Curl_cfilter *cf = user_data; 86513498266Sopenharmony_ci struct cf_ngtcp2_ctx *ctx = cf->ctx; 86613498266Sopenharmony_ci (void)conn; 86713498266Sopenharmony_ci (void)stream_user_data; 86813498266Sopenharmony_ci 86913498266Sopenharmony_ci /* nghttp3 has consumed bytes on the QUIC stream and we need to 87013498266Sopenharmony_ci * tell the QUIC connection to increase its flow control */ 87113498266Sopenharmony_ci ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream3_id, consumed); 87213498266Sopenharmony_ci ngtcp2_conn_extend_max_offset(ctx->qconn, consumed); 87313498266Sopenharmony_ci return 0; 87413498266Sopenharmony_ci} 87513498266Sopenharmony_ci 87613498266Sopenharmony_cistatic int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id, 87713498266Sopenharmony_ci int fin, void *user_data, void *stream_user_data) 87813498266Sopenharmony_ci{ 87913498266Sopenharmony_ci struct Curl_cfilter *cf = user_data; 88013498266Sopenharmony_ci struct Curl_easy *data = stream_user_data; 88113498266Sopenharmony_ci struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 88213498266Sopenharmony_ci CURLcode result = CURLE_OK; 88313498266Sopenharmony_ci (void)conn; 88413498266Sopenharmony_ci (void)stream_id; 88513498266Sopenharmony_ci (void)fin; 88613498266Sopenharmony_ci (void)cf; 88713498266Sopenharmony_ci 88813498266Sopenharmony_ci if(!stream) 88913498266Sopenharmony_ci return 0; 89013498266Sopenharmony_ci /* add a CRLF only if we've received some headers */ 89113498266Sopenharmony_ci result = write_resp_raw(cf, data, "\r\n", 2, FALSE); 89213498266Sopenharmony_ci if(result) { 89313498266Sopenharmony_ci return -1; 89413498266Sopenharmony_ci } 89513498266Sopenharmony_ci 89613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] end_headers, status=%d", 89713498266Sopenharmony_ci stream_id, stream->status_code); 89813498266Sopenharmony_ci if(stream->status_code / 100 != 1) { 89913498266Sopenharmony_ci stream->resp_hds_complete = TRUE; 90013498266Sopenharmony_ci } 90113498266Sopenharmony_ci h3_drain_stream(cf, data); 90213498266Sopenharmony_ci return 0; 90313498266Sopenharmony_ci} 90413498266Sopenharmony_ci 90513498266Sopenharmony_cistatic int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id, 90613498266Sopenharmony_ci int32_t token, nghttp3_rcbuf *name, 90713498266Sopenharmony_ci nghttp3_rcbuf *value, uint8_t flags, 90813498266Sopenharmony_ci void *user_data, void *stream_user_data) 90913498266Sopenharmony_ci{ 91013498266Sopenharmony_ci struct Curl_cfilter *cf = user_data; 91113498266Sopenharmony_ci nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name); 91213498266Sopenharmony_ci nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value); 91313498266Sopenharmony_ci struct Curl_easy *data = stream_user_data; 91413498266Sopenharmony_ci struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 91513498266Sopenharmony_ci CURLcode result = CURLE_OK; 91613498266Sopenharmony_ci (void)conn; 91713498266Sopenharmony_ci (void)stream_id; 91813498266Sopenharmony_ci (void)token; 91913498266Sopenharmony_ci (void)flags; 92013498266Sopenharmony_ci (void)cf; 92113498266Sopenharmony_ci 92213498266Sopenharmony_ci /* we might have cleaned up this transfer already */ 92313498266Sopenharmony_ci if(!stream) 92413498266Sopenharmony_ci return 0; 92513498266Sopenharmony_ci 92613498266Sopenharmony_ci if(token == NGHTTP3_QPACK_TOKEN__STATUS) { 92713498266Sopenharmony_ci char line[14]; /* status line is always 13 characters long */ 92813498266Sopenharmony_ci size_t ncopy; 92913498266Sopenharmony_ci 93013498266Sopenharmony_ci result = Curl_http_decode_status(&stream->status_code, 93113498266Sopenharmony_ci (const char *)h3val.base, h3val.len); 93213498266Sopenharmony_ci if(result) 93313498266Sopenharmony_ci return -1; 93413498266Sopenharmony_ci ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n", 93513498266Sopenharmony_ci stream->status_code); 93613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] status: %s", stream_id, line); 93713498266Sopenharmony_ci result = write_resp_raw(cf, data, line, ncopy, FALSE); 93813498266Sopenharmony_ci if(result) { 93913498266Sopenharmony_ci return -1; 94013498266Sopenharmony_ci } 94113498266Sopenharmony_ci } 94213498266Sopenharmony_ci else { 94313498266Sopenharmony_ci /* store as an HTTP1-style header */ 94413498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] header: %.*s: %.*s", 94513498266Sopenharmony_ci stream_id, (int)h3name.len, h3name.base, 94613498266Sopenharmony_ci (int)h3val.len, h3val.base); 94713498266Sopenharmony_ci result = write_resp_raw(cf, data, h3name.base, h3name.len, FALSE); 94813498266Sopenharmony_ci if(result) { 94913498266Sopenharmony_ci return -1; 95013498266Sopenharmony_ci } 95113498266Sopenharmony_ci result = write_resp_raw(cf, data, ": ", 2, FALSE); 95213498266Sopenharmony_ci if(result) { 95313498266Sopenharmony_ci return -1; 95413498266Sopenharmony_ci } 95513498266Sopenharmony_ci result = write_resp_raw(cf, data, h3val.base, h3val.len, FALSE); 95613498266Sopenharmony_ci if(result) { 95713498266Sopenharmony_ci return -1; 95813498266Sopenharmony_ci } 95913498266Sopenharmony_ci result = write_resp_raw(cf, data, "\r\n", 2, FALSE); 96013498266Sopenharmony_ci if(result) { 96113498266Sopenharmony_ci return -1; 96213498266Sopenharmony_ci } 96313498266Sopenharmony_ci } 96413498266Sopenharmony_ci return 0; 96513498266Sopenharmony_ci} 96613498266Sopenharmony_ci 96713498266Sopenharmony_cistatic int cb_h3_stop_sending(nghttp3_conn *conn, int64_t stream_id, 96813498266Sopenharmony_ci uint64_t app_error_code, void *user_data, 96913498266Sopenharmony_ci void *stream_user_data) 97013498266Sopenharmony_ci{ 97113498266Sopenharmony_ci struct Curl_cfilter *cf = user_data; 97213498266Sopenharmony_ci struct cf_ngtcp2_ctx *ctx = cf->ctx; 97313498266Sopenharmony_ci int rv; 97413498266Sopenharmony_ci (void)conn; 97513498266Sopenharmony_ci (void)stream_user_data; 97613498266Sopenharmony_ci 97713498266Sopenharmony_ci rv = ngtcp2_conn_shutdown_stream_read(ctx->qconn, 0, stream_id, 97813498266Sopenharmony_ci app_error_code); 97913498266Sopenharmony_ci if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { 98013498266Sopenharmony_ci return NGTCP2_ERR_CALLBACK_FAILURE; 98113498266Sopenharmony_ci } 98213498266Sopenharmony_ci 98313498266Sopenharmony_ci return 0; 98413498266Sopenharmony_ci} 98513498266Sopenharmony_ci 98613498266Sopenharmony_cistatic int cb_h3_reset_stream(nghttp3_conn *conn, int64_t stream_id, 98713498266Sopenharmony_ci uint64_t app_error_code, void *user_data, 98813498266Sopenharmony_ci void *stream_user_data) { 98913498266Sopenharmony_ci struct Curl_cfilter *cf = user_data; 99013498266Sopenharmony_ci struct cf_ngtcp2_ctx *ctx = cf->ctx; 99113498266Sopenharmony_ci struct Curl_easy *data = stream_user_data; 99213498266Sopenharmony_ci int rv; 99313498266Sopenharmony_ci (void)conn; 99413498266Sopenharmony_ci (void)data; 99513498266Sopenharmony_ci 99613498266Sopenharmony_ci rv = ngtcp2_conn_shutdown_stream_write(ctx->qconn, 0, stream_id, 99713498266Sopenharmony_ci app_error_code); 99813498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv); 99913498266Sopenharmony_ci if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { 100013498266Sopenharmony_ci return NGTCP2_ERR_CALLBACK_FAILURE; 100113498266Sopenharmony_ci } 100213498266Sopenharmony_ci 100313498266Sopenharmony_ci return 0; 100413498266Sopenharmony_ci} 100513498266Sopenharmony_ci 100613498266Sopenharmony_cistatic nghttp3_callbacks ngh3_callbacks = { 100713498266Sopenharmony_ci cb_h3_acked_req_body, /* acked_stream_data */ 100813498266Sopenharmony_ci cb_h3_stream_close, 100913498266Sopenharmony_ci cb_h3_recv_data, 101013498266Sopenharmony_ci cb_h3_deferred_consume, 101113498266Sopenharmony_ci NULL, /* begin_headers */ 101213498266Sopenharmony_ci cb_h3_recv_header, 101313498266Sopenharmony_ci cb_h3_end_headers, 101413498266Sopenharmony_ci NULL, /* begin_trailers */ 101513498266Sopenharmony_ci cb_h3_recv_header, 101613498266Sopenharmony_ci NULL, /* end_trailers */ 101713498266Sopenharmony_ci cb_h3_stop_sending, 101813498266Sopenharmony_ci NULL, /* end_stream */ 101913498266Sopenharmony_ci cb_h3_reset_stream, 102013498266Sopenharmony_ci NULL, /* shutdown */ 102113498266Sopenharmony_ci NULL /* recv_settings */ 102213498266Sopenharmony_ci}; 102313498266Sopenharmony_ci 102413498266Sopenharmony_cistatic int init_ngh3_conn(struct Curl_cfilter *cf) 102513498266Sopenharmony_ci{ 102613498266Sopenharmony_ci struct cf_ngtcp2_ctx *ctx = cf->ctx; 102713498266Sopenharmony_ci CURLcode result; 102813498266Sopenharmony_ci int rc; 102913498266Sopenharmony_ci int64_t ctrl_stream_id, qpack_enc_stream_id, qpack_dec_stream_id; 103013498266Sopenharmony_ci 103113498266Sopenharmony_ci if(ngtcp2_conn_get_streams_uni_left(ctx->qconn) < 3) { 103213498266Sopenharmony_ci return CURLE_QUIC_CONNECT_ERROR; 103313498266Sopenharmony_ci } 103413498266Sopenharmony_ci 103513498266Sopenharmony_ci nghttp3_settings_default(&ctx->h3settings); 103613498266Sopenharmony_ci 103713498266Sopenharmony_ci rc = nghttp3_conn_client_new(&ctx->h3conn, 103813498266Sopenharmony_ci &ngh3_callbacks, 103913498266Sopenharmony_ci &ctx->h3settings, 104013498266Sopenharmony_ci nghttp3_mem_default(), 104113498266Sopenharmony_ci cf); 104213498266Sopenharmony_ci if(rc) { 104313498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 104413498266Sopenharmony_ci goto fail; 104513498266Sopenharmony_ci } 104613498266Sopenharmony_ci 104713498266Sopenharmony_ci rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &ctrl_stream_id, NULL); 104813498266Sopenharmony_ci if(rc) { 104913498266Sopenharmony_ci result = CURLE_QUIC_CONNECT_ERROR; 105013498266Sopenharmony_ci goto fail; 105113498266Sopenharmony_ci } 105213498266Sopenharmony_ci 105313498266Sopenharmony_ci rc = nghttp3_conn_bind_control_stream(ctx->h3conn, ctrl_stream_id); 105413498266Sopenharmony_ci if(rc) { 105513498266Sopenharmony_ci result = CURLE_QUIC_CONNECT_ERROR; 105613498266Sopenharmony_ci goto fail; 105713498266Sopenharmony_ci } 105813498266Sopenharmony_ci 105913498266Sopenharmony_ci rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_enc_stream_id, NULL); 106013498266Sopenharmony_ci if(rc) { 106113498266Sopenharmony_ci result = CURLE_QUIC_CONNECT_ERROR; 106213498266Sopenharmony_ci goto fail; 106313498266Sopenharmony_ci } 106413498266Sopenharmony_ci 106513498266Sopenharmony_ci rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_dec_stream_id, NULL); 106613498266Sopenharmony_ci if(rc) { 106713498266Sopenharmony_ci result = CURLE_QUIC_CONNECT_ERROR; 106813498266Sopenharmony_ci goto fail; 106913498266Sopenharmony_ci } 107013498266Sopenharmony_ci 107113498266Sopenharmony_ci rc = nghttp3_conn_bind_qpack_streams(ctx->h3conn, qpack_enc_stream_id, 107213498266Sopenharmony_ci qpack_dec_stream_id); 107313498266Sopenharmony_ci if(rc) { 107413498266Sopenharmony_ci result = CURLE_QUIC_CONNECT_ERROR; 107513498266Sopenharmony_ci goto fail; 107613498266Sopenharmony_ci } 107713498266Sopenharmony_ci 107813498266Sopenharmony_ci return CURLE_OK; 107913498266Sopenharmony_cifail: 108013498266Sopenharmony_ci 108113498266Sopenharmony_ci return result; 108213498266Sopenharmony_ci} 108313498266Sopenharmony_ci 108413498266Sopenharmony_cistatic ssize_t recv_closed_stream(struct Curl_cfilter *cf, 108513498266Sopenharmony_ci struct Curl_easy *data, 108613498266Sopenharmony_ci struct h3_stream_ctx *stream, 108713498266Sopenharmony_ci CURLcode *err) 108813498266Sopenharmony_ci{ 108913498266Sopenharmony_ci ssize_t nread = -1; 109013498266Sopenharmony_ci 109113498266Sopenharmony_ci (void)cf; 109213498266Sopenharmony_ci if(stream->reset) { 109313498266Sopenharmony_ci failf(data, 109413498266Sopenharmony_ci "HTTP/3 stream %" PRId64 " reset by server", stream->id); 109513498266Sopenharmony_ci *err = stream->resp_hds_complete? CURLE_PARTIAL_FILE : CURLE_HTTP3; 109613498266Sopenharmony_ci goto out; 109713498266Sopenharmony_ci } 109813498266Sopenharmony_ci else if(!stream->resp_hds_complete) { 109913498266Sopenharmony_ci failf(data, 110013498266Sopenharmony_ci "HTTP/3 stream %" PRId64 " was closed cleanly, but before getting" 110113498266Sopenharmony_ci " all response header fields, treated as error", 110213498266Sopenharmony_ci stream->id); 110313498266Sopenharmony_ci *err = CURLE_HTTP3; 110413498266Sopenharmony_ci goto out; 110513498266Sopenharmony_ci } 110613498266Sopenharmony_ci *err = CURLE_OK; 110713498266Sopenharmony_ci nread = 0; 110813498266Sopenharmony_ci 110913498266Sopenharmony_ciout: 111013498266Sopenharmony_ci return nread; 111113498266Sopenharmony_ci} 111213498266Sopenharmony_ci 111313498266Sopenharmony_ci/* incoming data frames on the h3 stream */ 111413498266Sopenharmony_cistatic ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, 111513498266Sopenharmony_ci char *buf, size_t len, CURLcode *err) 111613498266Sopenharmony_ci{ 111713498266Sopenharmony_ci struct cf_ngtcp2_ctx *ctx = cf->ctx; 111813498266Sopenharmony_ci struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 111913498266Sopenharmony_ci ssize_t nread = -1; 112013498266Sopenharmony_ci struct cf_call_data save; 112113498266Sopenharmony_ci struct pkt_io_ctx pktx; 112213498266Sopenharmony_ci 112313498266Sopenharmony_ci (void)ctx; 112413498266Sopenharmony_ci 112513498266Sopenharmony_ci CF_DATA_SAVE(save, cf, data); 112613498266Sopenharmony_ci DEBUGASSERT(cf->connected); 112713498266Sopenharmony_ci DEBUGASSERT(ctx); 112813498266Sopenharmony_ci DEBUGASSERT(ctx->qconn); 112913498266Sopenharmony_ci DEBUGASSERT(ctx->h3conn); 113013498266Sopenharmony_ci *err = CURLE_OK; 113113498266Sopenharmony_ci 113213498266Sopenharmony_ci pktx_init(&pktx, cf, data); 113313498266Sopenharmony_ci 113413498266Sopenharmony_ci if(!stream) { 113513498266Sopenharmony_ci *err = CURLE_RECV_ERROR; 113613498266Sopenharmony_ci goto out; 113713498266Sopenharmony_ci } 113813498266Sopenharmony_ci 113913498266Sopenharmony_ci if(!Curl_bufq_is_empty(&stream->recvbuf)) { 114013498266Sopenharmony_ci nread = Curl_bufq_read(&stream->recvbuf, 114113498266Sopenharmony_ci (unsigned char *)buf, len, err); 114213498266Sopenharmony_ci if(nread < 0) { 114313498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) " 114413498266Sopenharmony_ci "-> %zd, %d", stream->id, len, nread, *err); 114513498266Sopenharmony_ci goto out; 114613498266Sopenharmony_ci } 114713498266Sopenharmony_ci report_consumed_data(cf, data, nread); 114813498266Sopenharmony_ci } 114913498266Sopenharmony_ci 115013498266Sopenharmony_ci if(cf_progress_ingress(cf, data, &pktx)) { 115113498266Sopenharmony_ci *err = CURLE_RECV_ERROR; 115213498266Sopenharmony_ci nread = -1; 115313498266Sopenharmony_ci goto out; 115413498266Sopenharmony_ci } 115513498266Sopenharmony_ci 115613498266Sopenharmony_ci /* recvbuf had nothing before, maybe after progressing ingress? */ 115713498266Sopenharmony_ci if(nread < 0 && !Curl_bufq_is_empty(&stream->recvbuf)) { 115813498266Sopenharmony_ci nread = Curl_bufq_read(&stream->recvbuf, 115913498266Sopenharmony_ci (unsigned char *)buf, len, err); 116013498266Sopenharmony_ci if(nread < 0) { 116113498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) " 116213498266Sopenharmony_ci "-> %zd, %d", stream->id, len, nread, *err); 116313498266Sopenharmony_ci goto out; 116413498266Sopenharmony_ci } 116513498266Sopenharmony_ci report_consumed_data(cf, data, nread); 116613498266Sopenharmony_ci } 116713498266Sopenharmony_ci 116813498266Sopenharmony_ci if(nread > 0) { 116913498266Sopenharmony_ci h3_drain_stream(cf, data); 117013498266Sopenharmony_ci } 117113498266Sopenharmony_ci else { 117213498266Sopenharmony_ci if(stream->closed) { 117313498266Sopenharmony_ci nread = recv_closed_stream(cf, data, stream, err); 117413498266Sopenharmony_ci goto out; 117513498266Sopenharmony_ci } 117613498266Sopenharmony_ci *err = CURLE_AGAIN; 117713498266Sopenharmony_ci nread = -1; 117813498266Sopenharmony_ci } 117913498266Sopenharmony_ci 118013498266Sopenharmony_ciout: 118113498266Sopenharmony_ci if(cf_progress_egress(cf, data, &pktx)) { 118213498266Sopenharmony_ci *err = CURLE_SEND_ERROR; 118313498266Sopenharmony_ci nread = -1; 118413498266Sopenharmony_ci } 118513498266Sopenharmony_ci else { 118613498266Sopenharmony_ci CURLcode result2 = check_and_set_expiry(cf, data, &pktx); 118713498266Sopenharmony_ci if(result2) { 118813498266Sopenharmony_ci *err = result2; 118913498266Sopenharmony_ci nread = -1; 119013498266Sopenharmony_ci } 119113498266Sopenharmony_ci } 119213498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv(len=%zu) -> %zd, %d", 119313498266Sopenharmony_ci stream? stream->id : -1, len, nread, *err); 119413498266Sopenharmony_ci CF_DATA_RESTORE(cf, save); 119513498266Sopenharmony_ci return nread; 119613498266Sopenharmony_ci} 119713498266Sopenharmony_ci 119813498266Sopenharmony_cistatic int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id, 119913498266Sopenharmony_ci uint64_t datalen, void *user_data, 120013498266Sopenharmony_ci void *stream_user_data) 120113498266Sopenharmony_ci{ 120213498266Sopenharmony_ci struct Curl_cfilter *cf = user_data; 120313498266Sopenharmony_ci struct Curl_easy *data = stream_user_data; 120413498266Sopenharmony_ci struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 120513498266Sopenharmony_ci size_t skiplen; 120613498266Sopenharmony_ci 120713498266Sopenharmony_ci (void)cf; 120813498266Sopenharmony_ci if(!stream) 120913498266Sopenharmony_ci return 0; 121013498266Sopenharmony_ci /* The server acknowledged `datalen` of bytes from our request body. 121113498266Sopenharmony_ci * This is a delta. We have kept this data in `sendbuf` for 121213498266Sopenharmony_ci * re-transmissions and can free it now. */ 121313498266Sopenharmony_ci if(datalen >= (uint64_t)stream->sendbuf_len_in_flight) 121413498266Sopenharmony_ci skiplen = stream->sendbuf_len_in_flight; 121513498266Sopenharmony_ci else 121613498266Sopenharmony_ci skiplen = (size_t)datalen; 121713498266Sopenharmony_ci Curl_bufq_skip(&stream->sendbuf, skiplen); 121813498266Sopenharmony_ci stream->sendbuf_len_in_flight -= skiplen; 121913498266Sopenharmony_ci 122013498266Sopenharmony_ci /* Everything ACKed, we resume upload processing */ 122113498266Sopenharmony_ci if(!stream->sendbuf_len_in_flight) { 122213498266Sopenharmony_ci int rv = nghttp3_conn_resume_stream(conn, stream_id); 122313498266Sopenharmony_ci if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { 122413498266Sopenharmony_ci return NGTCP2_ERR_CALLBACK_FAILURE; 122513498266Sopenharmony_ci } 122613498266Sopenharmony_ci } 122713498266Sopenharmony_ci return 0; 122813498266Sopenharmony_ci} 122913498266Sopenharmony_ci 123013498266Sopenharmony_cistatic nghttp3_ssize 123113498266Sopenharmony_cicb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id, 123213498266Sopenharmony_ci nghttp3_vec *vec, size_t veccnt, 123313498266Sopenharmony_ci uint32_t *pflags, void *user_data, 123413498266Sopenharmony_ci void *stream_user_data) 123513498266Sopenharmony_ci{ 123613498266Sopenharmony_ci struct Curl_cfilter *cf = user_data; 123713498266Sopenharmony_ci struct Curl_easy *data = stream_user_data; 123813498266Sopenharmony_ci struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 123913498266Sopenharmony_ci ssize_t nwritten = 0; 124013498266Sopenharmony_ci size_t nvecs = 0; 124113498266Sopenharmony_ci (void)cf; 124213498266Sopenharmony_ci (void)conn; 124313498266Sopenharmony_ci (void)stream_id; 124413498266Sopenharmony_ci (void)user_data; 124513498266Sopenharmony_ci (void)veccnt; 124613498266Sopenharmony_ci 124713498266Sopenharmony_ci if(!stream) 124813498266Sopenharmony_ci return NGHTTP3_ERR_CALLBACK_FAILURE; 124913498266Sopenharmony_ci /* nghttp3 keeps references to the sendbuf data until it is ACKed 125013498266Sopenharmony_ci * by the server (see `cb_h3_acked_req_body()` for updates). 125113498266Sopenharmony_ci * `sendbuf_len_in_flight` is the amount of bytes in `sendbuf` 125213498266Sopenharmony_ci * that we have already passed to nghttp3, but which have not been 125313498266Sopenharmony_ci * ACKed yet. 125413498266Sopenharmony_ci * Any amount beyond `sendbuf_len_in_flight` we need still to pass 125513498266Sopenharmony_ci * to nghttp3. Do that now, if we can. */ 125613498266Sopenharmony_ci if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) { 125713498266Sopenharmony_ci nvecs = 0; 125813498266Sopenharmony_ci while(nvecs < veccnt && 125913498266Sopenharmony_ci Curl_bufq_peek_at(&stream->sendbuf, 126013498266Sopenharmony_ci stream->sendbuf_len_in_flight, 126113498266Sopenharmony_ci (const unsigned char **)&vec[nvecs].base, 126213498266Sopenharmony_ci &vec[nvecs].len)) { 126313498266Sopenharmony_ci stream->sendbuf_len_in_flight += vec[nvecs].len; 126413498266Sopenharmony_ci nwritten += vec[nvecs].len; 126513498266Sopenharmony_ci ++nvecs; 126613498266Sopenharmony_ci } 126713498266Sopenharmony_ci DEBUGASSERT(nvecs > 0); /* we SHOULD have been be able to peek */ 126813498266Sopenharmony_ci } 126913498266Sopenharmony_ci 127013498266Sopenharmony_ci if(nwritten > 0 && stream->upload_left != -1) 127113498266Sopenharmony_ci stream->upload_left -= nwritten; 127213498266Sopenharmony_ci 127313498266Sopenharmony_ci /* When we stopped sending and everything in `sendbuf` is "in flight", 127413498266Sopenharmony_ci * we are at the end of the request body. */ 127513498266Sopenharmony_ci if(stream->upload_left == 0) { 127613498266Sopenharmony_ci *pflags = NGHTTP3_DATA_FLAG_EOF; 127713498266Sopenharmony_ci stream->send_closed = TRUE; 127813498266Sopenharmony_ci } 127913498266Sopenharmony_ci else if(!nwritten) { 128013498266Sopenharmony_ci /* Not EOF, and nothing to give, we signal WOULDBLOCK. */ 128113498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> AGAIN", 128213498266Sopenharmony_ci stream->id); 128313498266Sopenharmony_ci return NGHTTP3_ERR_WOULDBLOCK; 128413498266Sopenharmony_ci } 128513498266Sopenharmony_ci 128613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> " 128713498266Sopenharmony_ci "%d vecs%s with %zu (buffered=%zu, left=%" 128813498266Sopenharmony_ci CURL_FORMAT_CURL_OFF_T ")", 128913498266Sopenharmony_ci stream->id, (int)nvecs, 129013498266Sopenharmony_ci *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"", 129113498266Sopenharmony_ci nwritten, Curl_bufq_len(&stream->sendbuf), 129213498266Sopenharmony_ci stream->upload_left); 129313498266Sopenharmony_ci return (nghttp3_ssize)nvecs; 129413498266Sopenharmony_ci} 129513498266Sopenharmony_ci 129613498266Sopenharmony_ci/* Index where :authority header field will appear in request header 129713498266Sopenharmony_ci field list. */ 129813498266Sopenharmony_ci#define AUTHORITY_DST_IDX 3 129913498266Sopenharmony_ci 130013498266Sopenharmony_cistatic ssize_t h3_stream_open(struct Curl_cfilter *cf, 130113498266Sopenharmony_ci struct Curl_easy *data, 130213498266Sopenharmony_ci const void *buf, size_t len, 130313498266Sopenharmony_ci CURLcode *err) 130413498266Sopenharmony_ci{ 130513498266Sopenharmony_ci struct cf_ngtcp2_ctx *ctx = cf->ctx; 130613498266Sopenharmony_ci struct h3_stream_ctx *stream = NULL; 130713498266Sopenharmony_ci struct dynhds h2_headers; 130813498266Sopenharmony_ci size_t nheader; 130913498266Sopenharmony_ci nghttp3_nv *nva = NULL; 131013498266Sopenharmony_ci int rc = 0; 131113498266Sopenharmony_ci unsigned int i; 131213498266Sopenharmony_ci ssize_t nwritten = -1; 131313498266Sopenharmony_ci nghttp3_data_reader reader; 131413498266Sopenharmony_ci nghttp3_data_reader *preader = NULL; 131513498266Sopenharmony_ci 131613498266Sopenharmony_ci Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); 131713498266Sopenharmony_ci 131813498266Sopenharmony_ci *err = h3_data_setup(cf, data); 131913498266Sopenharmony_ci if(*err) 132013498266Sopenharmony_ci goto out; 132113498266Sopenharmony_ci stream = H3_STREAM_CTX(data); 132213498266Sopenharmony_ci DEBUGASSERT(stream); 132313498266Sopenharmony_ci if(!stream) { 132413498266Sopenharmony_ci *err = CURLE_FAILED_INIT; 132513498266Sopenharmony_ci goto out; 132613498266Sopenharmony_ci } 132713498266Sopenharmony_ci 132813498266Sopenharmony_ci nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err); 132913498266Sopenharmony_ci if(nwritten < 0) 133013498266Sopenharmony_ci goto out; 133113498266Sopenharmony_ci if(!stream->h1.done) { 133213498266Sopenharmony_ci /* need more data */ 133313498266Sopenharmony_ci goto out; 133413498266Sopenharmony_ci } 133513498266Sopenharmony_ci DEBUGASSERT(stream->h1.req); 133613498266Sopenharmony_ci 133713498266Sopenharmony_ci *err = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data); 133813498266Sopenharmony_ci if(*err) { 133913498266Sopenharmony_ci nwritten = -1; 134013498266Sopenharmony_ci goto out; 134113498266Sopenharmony_ci } 134213498266Sopenharmony_ci /* no longer needed */ 134313498266Sopenharmony_ci Curl_h1_req_parse_free(&stream->h1); 134413498266Sopenharmony_ci 134513498266Sopenharmony_ci nheader = Curl_dynhds_count(&h2_headers); 134613498266Sopenharmony_ci nva = malloc(sizeof(nghttp3_nv) * nheader); 134713498266Sopenharmony_ci if(!nva) { 134813498266Sopenharmony_ci *err = CURLE_OUT_OF_MEMORY; 134913498266Sopenharmony_ci nwritten = -1; 135013498266Sopenharmony_ci goto out; 135113498266Sopenharmony_ci } 135213498266Sopenharmony_ci 135313498266Sopenharmony_ci for(i = 0; i < nheader; ++i) { 135413498266Sopenharmony_ci struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i); 135513498266Sopenharmony_ci nva[i].name = (unsigned char *)e->name; 135613498266Sopenharmony_ci nva[i].namelen = e->namelen; 135713498266Sopenharmony_ci nva[i].value = (unsigned char *)e->value; 135813498266Sopenharmony_ci nva[i].valuelen = e->valuelen; 135913498266Sopenharmony_ci nva[i].flags = NGHTTP3_NV_FLAG_NONE; 136013498266Sopenharmony_ci } 136113498266Sopenharmony_ci 136213498266Sopenharmony_ci rc = ngtcp2_conn_open_bidi_stream(ctx->qconn, &stream->id, data); 136313498266Sopenharmony_ci if(rc) { 136413498266Sopenharmony_ci failf(data, "can get bidi streams"); 136513498266Sopenharmony_ci *err = CURLE_SEND_ERROR; 136613498266Sopenharmony_ci goto out; 136713498266Sopenharmony_ci } 136813498266Sopenharmony_ci 136913498266Sopenharmony_ci switch(data->state.httpreq) { 137013498266Sopenharmony_ci case HTTPREQ_POST: 137113498266Sopenharmony_ci case HTTPREQ_POST_FORM: 137213498266Sopenharmony_ci case HTTPREQ_POST_MIME: 137313498266Sopenharmony_ci case HTTPREQ_PUT: 137413498266Sopenharmony_ci /* known request body size or -1 */ 137513498266Sopenharmony_ci if(data->state.infilesize != -1) 137613498266Sopenharmony_ci stream->upload_left = data->state.infilesize; 137713498266Sopenharmony_ci else 137813498266Sopenharmony_ci /* data sending without specifying the data amount up front */ 137913498266Sopenharmony_ci stream->upload_left = -1; /* unknown */ 138013498266Sopenharmony_ci break; 138113498266Sopenharmony_ci default: 138213498266Sopenharmony_ci /* there is not request body */ 138313498266Sopenharmony_ci stream->upload_left = 0; /* no request body */ 138413498266Sopenharmony_ci break; 138513498266Sopenharmony_ci } 138613498266Sopenharmony_ci 138713498266Sopenharmony_ci stream->send_closed = (stream->upload_left == 0); 138813498266Sopenharmony_ci if(!stream->send_closed) { 138913498266Sopenharmony_ci reader.read_data = cb_h3_read_req_body; 139013498266Sopenharmony_ci preader = &reader; 139113498266Sopenharmony_ci } 139213498266Sopenharmony_ci 139313498266Sopenharmony_ci rc = nghttp3_conn_submit_request(ctx->h3conn, stream->id, 139413498266Sopenharmony_ci nva, nheader, preader, data); 139513498266Sopenharmony_ci if(rc) { 139613498266Sopenharmony_ci switch(rc) { 139713498266Sopenharmony_ci case NGHTTP3_ERR_CONN_CLOSING: 139813498266Sopenharmony_ci CURL_TRC_CF(data, cf, "h3sid[%"PRId64"] failed to send, " 139913498266Sopenharmony_ci "connection is closing", stream->id); 140013498266Sopenharmony_ci break; 140113498266Sopenharmony_ci default: 140213498266Sopenharmony_ci CURL_TRC_CF(data, cf, "h3sid[%"PRId64"] failed to send -> %d (%s)", 140313498266Sopenharmony_ci stream->id, rc, ngtcp2_strerror(rc)); 140413498266Sopenharmony_ci break; 140513498266Sopenharmony_ci } 140613498266Sopenharmony_ci *err = CURLE_SEND_ERROR; 140713498266Sopenharmony_ci nwritten = -1; 140813498266Sopenharmony_ci goto out; 140913498266Sopenharmony_ci } 141013498266Sopenharmony_ci 141113498266Sopenharmony_ci if(Curl_trc_is_verbose(data)) { 141213498266Sopenharmony_ci infof(data, "[HTTP/3] [%" PRId64 "] OPENED stream for %s", 141313498266Sopenharmony_ci stream->id, data->state.url); 141413498266Sopenharmony_ci for(i = 0; i < nheader; ++i) { 141513498266Sopenharmony_ci infof(data, "[HTTP/3] [%" PRId64 "] [%.*s: %.*s]", stream->id, 141613498266Sopenharmony_ci (int)nva[i].namelen, nva[i].name, 141713498266Sopenharmony_ci (int)nva[i].valuelen, nva[i].value); 141813498266Sopenharmony_ci } 141913498266Sopenharmony_ci } 142013498266Sopenharmony_ci 142113498266Sopenharmony_ciout: 142213498266Sopenharmony_ci free(nva); 142313498266Sopenharmony_ci Curl_dynhds_free(&h2_headers); 142413498266Sopenharmony_ci return nwritten; 142513498266Sopenharmony_ci} 142613498266Sopenharmony_ci 142713498266Sopenharmony_cistatic ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, 142813498266Sopenharmony_ci const void *buf, size_t len, CURLcode *err) 142913498266Sopenharmony_ci{ 143013498266Sopenharmony_ci struct cf_ngtcp2_ctx *ctx = cf->ctx; 143113498266Sopenharmony_ci struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 143213498266Sopenharmony_ci ssize_t sent = 0; 143313498266Sopenharmony_ci struct cf_call_data save; 143413498266Sopenharmony_ci struct pkt_io_ctx pktx; 143513498266Sopenharmony_ci CURLcode result; 143613498266Sopenharmony_ci 143713498266Sopenharmony_ci CF_DATA_SAVE(save, cf, data); 143813498266Sopenharmony_ci DEBUGASSERT(cf->connected); 143913498266Sopenharmony_ci DEBUGASSERT(ctx->qconn); 144013498266Sopenharmony_ci DEBUGASSERT(ctx->h3conn); 144113498266Sopenharmony_ci pktx_init(&pktx, cf, data); 144213498266Sopenharmony_ci *err = CURLE_OK; 144313498266Sopenharmony_ci 144413498266Sopenharmony_ci result = cf_progress_ingress(cf, data, &pktx); 144513498266Sopenharmony_ci if(result) { 144613498266Sopenharmony_ci *err = result; 144713498266Sopenharmony_ci sent = -1; 144813498266Sopenharmony_ci } 144913498266Sopenharmony_ci 145013498266Sopenharmony_ci if(!stream || stream->id < 0) { 145113498266Sopenharmony_ci sent = h3_stream_open(cf, data, buf, len, err); 145213498266Sopenharmony_ci if(sent < 0) { 145313498266Sopenharmony_ci CURL_TRC_CF(data, cf, "failed to open stream -> %d", *err); 145413498266Sopenharmony_ci goto out; 145513498266Sopenharmony_ci } 145613498266Sopenharmony_ci stream = H3_STREAM_CTX(data); 145713498266Sopenharmony_ci } 145813498266Sopenharmony_ci else if(stream->upload_blocked_len) { 145913498266Sopenharmony_ci /* the data in `buf` has already been submitted or added to the 146013498266Sopenharmony_ci * buffers, but have been EAGAINed on the last invocation. */ 146113498266Sopenharmony_ci DEBUGASSERT(len >= stream->upload_blocked_len); 146213498266Sopenharmony_ci if(len < stream->upload_blocked_len) { 146313498266Sopenharmony_ci /* Did we get called again with a smaller `len`? This should not 146413498266Sopenharmony_ci * happen. We are not prepared to handle that. */ 146513498266Sopenharmony_ci failf(data, "HTTP/3 send again with decreased length"); 146613498266Sopenharmony_ci *err = CURLE_HTTP3; 146713498266Sopenharmony_ci sent = -1; 146813498266Sopenharmony_ci goto out; 146913498266Sopenharmony_ci } 147013498266Sopenharmony_ci sent = (ssize_t)stream->upload_blocked_len; 147113498266Sopenharmony_ci stream->upload_blocked_len = 0; 147213498266Sopenharmony_ci } 147313498266Sopenharmony_ci else if(stream->closed) { 147413498266Sopenharmony_ci if(stream->resp_hds_complete) { 147513498266Sopenharmony_ci /* Server decided to close the stream after having sent us a final 147613498266Sopenharmony_ci * response. This is valid if it is not interested in the request 147713498266Sopenharmony_ci * body. This happens on 30x or 40x responses. 147813498266Sopenharmony_ci * We silently discard the data sent, since this is not a transport 147913498266Sopenharmony_ci * error situation. */ 148013498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] discarding data" 148113498266Sopenharmony_ci "on closed stream with response", stream->id); 148213498266Sopenharmony_ci *err = CURLE_OK; 148313498266Sopenharmony_ci sent = (ssize_t)len; 148413498266Sopenharmony_ci goto out; 148513498266Sopenharmony_ci } 148613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " 148713498266Sopenharmony_ci "-> stream closed", stream->id, len); 148813498266Sopenharmony_ci *err = CURLE_HTTP3; 148913498266Sopenharmony_ci sent = -1; 149013498266Sopenharmony_ci goto out; 149113498266Sopenharmony_ci } 149213498266Sopenharmony_ci else { 149313498266Sopenharmony_ci sent = Curl_bufq_write(&stream->sendbuf, buf, len, err); 149413498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send, add to " 149513498266Sopenharmony_ci "sendbuf(len=%zu) -> %zd, %d", 149613498266Sopenharmony_ci stream->id, len, sent, *err); 149713498266Sopenharmony_ci if(sent < 0) { 149813498266Sopenharmony_ci goto out; 149913498266Sopenharmony_ci } 150013498266Sopenharmony_ci 150113498266Sopenharmony_ci (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id); 150213498266Sopenharmony_ci } 150313498266Sopenharmony_ci 150413498266Sopenharmony_ci result = cf_progress_egress(cf, data, &pktx); 150513498266Sopenharmony_ci if(result) { 150613498266Sopenharmony_ci *err = result; 150713498266Sopenharmony_ci sent = -1; 150813498266Sopenharmony_ci } 150913498266Sopenharmony_ci 151013498266Sopenharmony_ci if(stream && sent > 0 && stream->sendbuf_len_in_flight) { 151113498266Sopenharmony_ci /* We have unacknowledged DATA and cannot report success to our 151213498266Sopenharmony_ci * caller. Instead we EAGAIN and remember how much we have already 151313498266Sopenharmony_ci * "written" into our various internal connection buffers. */ 151413498266Sopenharmony_ci stream->upload_blocked_len = sent; 151513498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu), " 151613498266Sopenharmony_ci "%zu bytes in flight -> EGAIN", stream->id, len, 151713498266Sopenharmony_ci stream->sendbuf_len_in_flight); 151813498266Sopenharmony_ci *err = CURLE_AGAIN; 151913498266Sopenharmony_ci sent = -1; 152013498266Sopenharmony_ci } 152113498266Sopenharmony_ci 152213498266Sopenharmony_ciout: 152313498266Sopenharmony_ci result = check_and_set_expiry(cf, data, &pktx); 152413498266Sopenharmony_ci if(result) { 152513498266Sopenharmony_ci *err = result; 152613498266Sopenharmony_ci sent = -1; 152713498266Sopenharmony_ci } 152813498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %zd, %d", 152913498266Sopenharmony_ci stream? stream->id : -1, len, sent, *err); 153013498266Sopenharmony_ci CF_DATA_RESTORE(cf, save); 153113498266Sopenharmony_ci return sent; 153213498266Sopenharmony_ci} 153313498266Sopenharmony_ci 153413498266Sopenharmony_cistatic CURLcode qng_verify_peer(struct Curl_cfilter *cf, 153513498266Sopenharmony_ci struct Curl_easy *data) 153613498266Sopenharmony_ci{ 153713498266Sopenharmony_ci struct cf_ngtcp2_ctx *ctx = cf->ctx; 153813498266Sopenharmony_ci 153913498266Sopenharmony_ci cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ 154013498266Sopenharmony_ci cf->conn->httpversion = 30; 154113498266Sopenharmony_ci cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX; 154213498266Sopenharmony_ci 154313498266Sopenharmony_ci return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer); 154413498266Sopenharmony_ci} 154513498266Sopenharmony_ci 154613498266Sopenharmony_cistatic CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen, 154713498266Sopenharmony_ci struct sockaddr_storage *remote_addr, 154813498266Sopenharmony_ci socklen_t remote_addrlen, int ecn, 154913498266Sopenharmony_ci void *userp) 155013498266Sopenharmony_ci{ 155113498266Sopenharmony_ci struct pkt_io_ctx *pktx = userp; 155213498266Sopenharmony_ci struct cf_ngtcp2_ctx *ctx = pktx->cf->ctx; 155313498266Sopenharmony_ci ngtcp2_pkt_info pi; 155413498266Sopenharmony_ci ngtcp2_path path; 155513498266Sopenharmony_ci int rv; 155613498266Sopenharmony_ci 155713498266Sopenharmony_ci ++pktx->pkt_count; 155813498266Sopenharmony_ci ngtcp2_addr_init(&path.local, (struct sockaddr *)&ctx->q.local_addr, 155913498266Sopenharmony_ci ctx->q.local_addrlen); 156013498266Sopenharmony_ci ngtcp2_addr_init(&path.remote, (struct sockaddr *)remote_addr, 156113498266Sopenharmony_ci remote_addrlen); 156213498266Sopenharmony_ci pi.ecn = (uint8_t)ecn; 156313498266Sopenharmony_ci 156413498266Sopenharmony_ci rv = ngtcp2_conn_read_pkt(ctx->qconn, &path, &pi, pkt, pktlen, pktx->ts); 156513498266Sopenharmony_ci if(rv) { 156613498266Sopenharmony_ci CURL_TRC_CF(pktx->data, pktx->cf, "ingress, read_pkt -> %s (%d)", 156713498266Sopenharmony_ci ngtcp2_strerror(rv), rv); 156813498266Sopenharmony_ci if(!ctx->last_error.error_code) { 156913498266Sopenharmony_ci if(rv == NGTCP2_ERR_CRYPTO) { 157013498266Sopenharmony_ci ngtcp2_ccerr_set_tls_alert(&ctx->last_error, 157113498266Sopenharmony_ci ngtcp2_conn_get_tls_alert(ctx->qconn), 157213498266Sopenharmony_ci NULL, 0); 157313498266Sopenharmony_ci } 157413498266Sopenharmony_ci else { 157513498266Sopenharmony_ci ngtcp2_ccerr_set_liberr(&ctx->last_error, rv, NULL, 0); 157613498266Sopenharmony_ci } 157713498266Sopenharmony_ci } 157813498266Sopenharmony_ci 157913498266Sopenharmony_ci if(rv == NGTCP2_ERR_CRYPTO) 158013498266Sopenharmony_ci /* this is a "TLS problem", but a failed certificate verification 158113498266Sopenharmony_ci is a common reason for this */ 158213498266Sopenharmony_ci return CURLE_PEER_FAILED_VERIFICATION; 158313498266Sopenharmony_ci return CURLE_RECV_ERROR; 158413498266Sopenharmony_ci } 158513498266Sopenharmony_ci 158613498266Sopenharmony_ci return CURLE_OK; 158713498266Sopenharmony_ci} 158813498266Sopenharmony_ci 158913498266Sopenharmony_cistatic CURLcode cf_progress_ingress(struct Curl_cfilter *cf, 159013498266Sopenharmony_ci struct Curl_easy *data, 159113498266Sopenharmony_ci struct pkt_io_ctx *pktx) 159213498266Sopenharmony_ci{ 159313498266Sopenharmony_ci struct cf_ngtcp2_ctx *ctx = cf->ctx; 159413498266Sopenharmony_ci struct pkt_io_ctx local_pktx; 159513498266Sopenharmony_ci size_t pkts_chunk = 128, i; 159613498266Sopenharmony_ci size_t pkts_max = 10 * pkts_chunk; 159713498266Sopenharmony_ci CURLcode result = CURLE_OK; 159813498266Sopenharmony_ci 159913498266Sopenharmony_ci if(!pktx) { 160013498266Sopenharmony_ci pktx_init(&local_pktx, cf, data); 160113498266Sopenharmony_ci pktx = &local_pktx; 160213498266Sopenharmony_ci } 160313498266Sopenharmony_ci else { 160413498266Sopenharmony_ci pktx_update_time(pktx, cf); 160513498266Sopenharmony_ci } 160613498266Sopenharmony_ci 160713498266Sopenharmony_ci result = Curl_vquic_tls_before_recv(&ctx->tls, cf, data); 160813498266Sopenharmony_ci if(result) 160913498266Sopenharmony_ci return result; 161013498266Sopenharmony_ci 161113498266Sopenharmony_ci for(i = 0; i < pkts_max; i += pkts_chunk) { 161213498266Sopenharmony_ci pktx->pkt_count = 0; 161313498266Sopenharmony_ci result = vquic_recv_packets(cf, data, &ctx->q, pkts_chunk, 161413498266Sopenharmony_ci recv_pkt, pktx); 161513498266Sopenharmony_ci if(result) /* error */ 161613498266Sopenharmony_ci break; 161713498266Sopenharmony_ci if(pktx->pkt_count < pkts_chunk) /* got less than we could */ 161813498266Sopenharmony_ci break; 161913498266Sopenharmony_ci /* give egress a chance before we receive more */ 162013498266Sopenharmony_ci result = cf_progress_egress(cf, data, pktx); 162113498266Sopenharmony_ci if(result) /* error */ 162213498266Sopenharmony_ci break; 162313498266Sopenharmony_ci } 162413498266Sopenharmony_ci return result; 162513498266Sopenharmony_ci} 162613498266Sopenharmony_ci 162713498266Sopenharmony_ci/** 162813498266Sopenharmony_ci * Read a network packet to send from ngtcp2 into `buf`. 162913498266Sopenharmony_ci * Return number of bytes written or -1 with *err set. 163013498266Sopenharmony_ci */ 163113498266Sopenharmony_cistatic ssize_t read_pkt_to_send(void *userp, 163213498266Sopenharmony_ci unsigned char *buf, size_t buflen, 163313498266Sopenharmony_ci CURLcode *err) 163413498266Sopenharmony_ci{ 163513498266Sopenharmony_ci struct pkt_io_ctx *x = userp; 163613498266Sopenharmony_ci struct cf_ngtcp2_ctx *ctx = x->cf->ctx; 163713498266Sopenharmony_ci nghttp3_vec vec[16]; 163813498266Sopenharmony_ci nghttp3_ssize veccnt; 163913498266Sopenharmony_ci ngtcp2_ssize ndatalen; 164013498266Sopenharmony_ci uint32_t flags; 164113498266Sopenharmony_ci int64_t stream_id; 164213498266Sopenharmony_ci int fin; 164313498266Sopenharmony_ci ssize_t nwritten, n; 164413498266Sopenharmony_ci veccnt = 0; 164513498266Sopenharmony_ci stream_id = -1; 164613498266Sopenharmony_ci fin = 0; 164713498266Sopenharmony_ci 164813498266Sopenharmony_ci /* ngtcp2 may want to put several frames from different streams into 164913498266Sopenharmony_ci * this packet. `NGTCP2_WRITE_STREAM_FLAG_MORE` tells it to do so. 165013498266Sopenharmony_ci * When `NGTCP2_ERR_WRITE_MORE` is returned, we *need* to make 165113498266Sopenharmony_ci * another iteration. 165213498266Sopenharmony_ci * When ngtcp2 is happy (because it has no other frame that would fit 165313498266Sopenharmony_ci * or it has nothing more to send), it returns the total length 165413498266Sopenharmony_ci * of the assembled packet. This may be 0 if there was nothing to send. */ 165513498266Sopenharmony_ci nwritten = 0; 165613498266Sopenharmony_ci *err = CURLE_OK; 165713498266Sopenharmony_ci for(;;) { 165813498266Sopenharmony_ci 165913498266Sopenharmony_ci if(ctx->h3conn && ngtcp2_conn_get_max_data_left(ctx->qconn)) { 166013498266Sopenharmony_ci veccnt = nghttp3_conn_writev_stream(ctx->h3conn, &stream_id, &fin, vec, 166113498266Sopenharmony_ci sizeof(vec) / sizeof(vec[0])); 166213498266Sopenharmony_ci if(veccnt < 0) { 166313498266Sopenharmony_ci failf(x->data, "nghttp3_conn_writev_stream returned error: %s", 166413498266Sopenharmony_ci nghttp3_strerror((int)veccnt)); 166513498266Sopenharmony_ci ngtcp2_ccerr_set_application_error( 166613498266Sopenharmony_ci &ctx->last_error, 166713498266Sopenharmony_ci nghttp3_err_infer_quic_app_error_code((int)veccnt), NULL, 0); 166813498266Sopenharmony_ci *err = CURLE_SEND_ERROR; 166913498266Sopenharmony_ci return -1; 167013498266Sopenharmony_ci } 167113498266Sopenharmony_ci } 167213498266Sopenharmony_ci 167313498266Sopenharmony_ci flags = NGTCP2_WRITE_STREAM_FLAG_MORE | 167413498266Sopenharmony_ci (fin ? NGTCP2_WRITE_STREAM_FLAG_FIN : 0); 167513498266Sopenharmony_ci n = ngtcp2_conn_writev_stream(ctx->qconn, &x->ps.path, 167613498266Sopenharmony_ci NULL, buf, buflen, 167713498266Sopenharmony_ci &ndatalen, flags, stream_id, 167813498266Sopenharmony_ci (const ngtcp2_vec *)vec, veccnt, x->ts); 167913498266Sopenharmony_ci if(n == 0) { 168013498266Sopenharmony_ci /* nothing to send */ 168113498266Sopenharmony_ci *err = CURLE_AGAIN; 168213498266Sopenharmony_ci nwritten = -1; 168313498266Sopenharmony_ci goto out; 168413498266Sopenharmony_ci } 168513498266Sopenharmony_ci else if(n < 0) { 168613498266Sopenharmony_ci switch(n) { 168713498266Sopenharmony_ci case NGTCP2_ERR_STREAM_DATA_BLOCKED: { 168813498266Sopenharmony_ci struct h3_stream_ctx *stream = H3_STREAM_CTX(x->data); 168913498266Sopenharmony_ci DEBUGASSERT(ndatalen == -1); 169013498266Sopenharmony_ci nghttp3_conn_block_stream(ctx->h3conn, stream_id); 169113498266Sopenharmony_ci CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] block quic flow", 169213498266Sopenharmony_ci stream_id); 169313498266Sopenharmony_ci DEBUGASSERT(stream); 169413498266Sopenharmony_ci if(stream) 169513498266Sopenharmony_ci stream->quic_flow_blocked = TRUE; 169613498266Sopenharmony_ci n = 0; 169713498266Sopenharmony_ci break; 169813498266Sopenharmony_ci } 169913498266Sopenharmony_ci case NGTCP2_ERR_STREAM_SHUT_WR: 170013498266Sopenharmony_ci DEBUGASSERT(ndatalen == -1); 170113498266Sopenharmony_ci nghttp3_conn_shutdown_stream_write(ctx->h3conn, stream_id); 170213498266Sopenharmony_ci n = 0; 170313498266Sopenharmony_ci break; 170413498266Sopenharmony_ci case NGTCP2_ERR_WRITE_MORE: 170513498266Sopenharmony_ci /* ngtcp2 wants to send more. update the flow of the stream whose data 170613498266Sopenharmony_ci * is in the buffer and continue */ 170713498266Sopenharmony_ci DEBUGASSERT(ndatalen >= 0); 170813498266Sopenharmony_ci n = 0; 170913498266Sopenharmony_ci break; 171013498266Sopenharmony_ci default: 171113498266Sopenharmony_ci DEBUGASSERT(ndatalen == -1); 171213498266Sopenharmony_ci failf(x->data, "ngtcp2_conn_writev_stream returned error: %s", 171313498266Sopenharmony_ci ngtcp2_strerror((int)n)); 171413498266Sopenharmony_ci ngtcp2_ccerr_set_liberr(&ctx->last_error, (int)n, NULL, 0); 171513498266Sopenharmony_ci *err = CURLE_SEND_ERROR; 171613498266Sopenharmony_ci nwritten = -1; 171713498266Sopenharmony_ci goto out; 171813498266Sopenharmony_ci } 171913498266Sopenharmony_ci } 172013498266Sopenharmony_ci 172113498266Sopenharmony_ci if(ndatalen >= 0) { 172213498266Sopenharmony_ci /* we add the amount of data bytes to the flow windows */ 172313498266Sopenharmony_ci int rv = nghttp3_conn_add_write_offset(ctx->h3conn, stream_id, ndatalen); 172413498266Sopenharmony_ci if(rv) { 172513498266Sopenharmony_ci failf(x->data, "nghttp3_conn_add_write_offset returned error: %s\n", 172613498266Sopenharmony_ci nghttp3_strerror(rv)); 172713498266Sopenharmony_ci return CURLE_SEND_ERROR; 172813498266Sopenharmony_ci } 172913498266Sopenharmony_ci } 173013498266Sopenharmony_ci 173113498266Sopenharmony_ci if(n > 0) { 173213498266Sopenharmony_ci /* packet assembled, leave */ 173313498266Sopenharmony_ci nwritten = n; 173413498266Sopenharmony_ci goto out; 173513498266Sopenharmony_ci } 173613498266Sopenharmony_ci } 173713498266Sopenharmony_ciout: 173813498266Sopenharmony_ci return nwritten; 173913498266Sopenharmony_ci} 174013498266Sopenharmony_ci 174113498266Sopenharmony_cistatic CURLcode cf_progress_egress(struct Curl_cfilter *cf, 174213498266Sopenharmony_ci struct Curl_easy *data, 174313498266Sopenharmony_ci struct pkt_io_ctx *pktx) 174413498266Sopenharmony_ci{ 174513498266Sopenharmony_ci struct cf_ngtcp2_ctx *ctx = cf->ctx; 174613498266Sopenharmony_ci ssize_t nread; 174713498266Sopenharmony_ci size_t max_payload_size, path_max_payload_size, max_pktcnt; 174813498266Sopenharmony_ci size_t pktcnt = 0; 174913498266Sopenharmony_ci size_t gsolen = 0; /* this disables gso until we have a clue */ 175013498266Sopenharmony_ci CURLcode curlcode; 175113498266Sopenharmony_ci struct pkt_io_ctx local_pktx; 175213498266Sopenharmony_ci 175313498266Sopenharmony_ci if(!pktx) { 175413498266Sopenharmony_ci pktx_init(&local_pktx, cf, data); 175513498266Sopenharmony_ci pktx = &local_pktx; 175613498266Sopenharmony_ci } 175713498266Sopenharmony_ci else { 175813498266Sopenharmony_ci pktx_update_time(pktx, cf); 175913498266Sopenharmony_ci ngtcp2_path_storage_zero(&pktx->ps); 176013498266Sopenharmony_ci } 176113498266Sopenharmony_ci 176213498266Sopenharmony_ci curlcode = vquic_flush(cf, data, &ctx->q); 176313498266Sopenharmony_ci if(curlcode) { 176413498266Sopenharmony_ci if(curlcode == CURLE_AGAIN) { 176513498266Sopenharmony_ci Curl_expire(data, 1, EXPIRE_QUIC); 176613498266Sopenharmony_ci return CURLE_OK; 176713498266Sopenharmony_ci } 176813498266Sopenharmony_ci return curlcode; 176913498266Sopenharmony_ci } 177013498266Sopenharmony_ci 177113498266Sopenharmony_ci /* In UDP, there is a maximum theoretical packet paload length and 177213498266Sopenharmony_ci * a minimum payload length that is "guarantueed" to work. 177313498266Sopenharmony_ci * To detect if this minimum payload can be increased, ngtcp2 sends 177413498266Sopenharmony_ci * now and then a packet payload larger than the minimum. It that 177513498266Sopenharmony_ci * is ACKed by the peer, both parties know that it works and 177613498266Sopenharmony_ci * the subsequent packets can use a larger one. 177713498266Sopenharmony_ci * This is called PMTUD (Path Maximum Transmission Unit Discovery). 177813498266Sopenharmony_ci * Since a PMTUD might be rejected right on send, we do not want it 177913498266Sopenharmony_ci * be followed by other packets of lesser size. Because those would 178013498266Sopenharmony_ci * also fail then. So, if we detect a PMTUD while buffering, we flush. 178113498266Sopenharmony_ci */ 178213498266Sopenharmony_ci max_payload_size = ngtcp2_conn_get_max_tx_udp_payload_size(ctx->qconn); 178313498266Sopenharmony_ci path_max_payload_size = 178413498266Sopenharmony_ci ngtcp2_conn_get_path_max_tx_udp_payload_size(ctx->qconn); 178513498266Sopenharmony_ci /* maximum number of packets buffered before we flush to the socket */ 178613498266Sopenharmony_ci max_pktcnt = CURLMIN(MAX_PKT_BURST, 178713498266Sopenharmony_ci ctx->q.sendbuf.chunk_size / max_payload_size); 178813498266Sopenharmony_ci 178913498266Sopenharmony_ci for(;;) { 179013498266Sopenharmony_ci /* add the next packet to send, if any, to our buffer */ 179113498266Sopenharmony_ci nread = Curl_bufq_sipn(&ctx->q.sendbuf, max_payload_size, 179213498266Sopenharmony_ci read_pkt_to_send, pktx, &curlcode); 179313498266Sopenharmony_ci if(nread < 0) { 179413498266Sopenharmony_ci if(curlcode != CURLE_AGAIN) 179513498266Sopenharmony_ci return curlcode; 179613498266Sopenharmony_ci /* Nothing more to add, flush and leave */ 179713498266Sopenharmony_ci curlcode = vquic_send(cf, data, &ctx->q, gsolen); 179813498266Sopenharmony_ci if(curlcode) { 179913498266Sopenharmony_ci if(curlcode == CURLE_AGAIN) { 180013498266Sopenharmony_ci Curl_expire(data, 1, EXPIRE_QUIC); 180113498266Sopenharmony_ci return CURLE_OK; 180213498266Sopenharmony_ci } 180313498266Sopenharmony_ci return curlcode; 180413498266Sopenharmony_ci } 180513498266Sopenharmony_ci goto out; 180613498266Sopenharmony_ci } 180713498266Sopenharmony_ci 180813498266Sopenharmony_ci DEBUGASSERT(nread > 0); 180913498266Sopenharmony_ci if(pktcnt == 0) { 181013498266Sopenharmony_ci /* first packet in buffer. This is either of a known, "good" 181113498266Sopenharmony_ci * payload size or it is a PMTUD. We'll see. */ 181213498266Sopenharmony_ci gsolen = (size_t)nread; 181313498266Sopenharmony_ci } 181413498266Sopenharmony_ci else if((size_t)nread > gsolen || 181513498266Sopenharmony_ci (gsolen > path_max_payload_size && (size_t)nread != gsolen)) { 181613498266Sopenharmony_ci /* The just added packet is a PMTUD *or* the one(s) before the 181713498266Sopenharmony_ci * just added were PMTUD and the last one is smaller. 181813498266Sopenharmony_ci * Flush the buffer before the last add. */ 181913498266Sopenharmony_ci curlcode = vquic_send_tail_split(cf, data, &ctx->q, 182013498266Sopenharmony_ci gsolen, nread, nread); 182113498266Sopenharmony_ci if(curlcode) { 182213498266Sopenharmony_ci if(curlcode == CURLE_AGAIN) { 182313498266Sopenharmony_ci Curl_expire(data, 1, EXPIRE_QUIC); 182413498266Sopenharmony_ci return CURLE_OK; 182513498266Sopenharmony_ci } 182613498266Sopenharmony_ci return curlcode; 182713498266Sopenharmony_ci } 182813498266Sopenharmony_ci pktcnt = 0; 182913498266Sopenharmony_ci continue; 183013498266Sopenharmony_ci } 183113498266Sopenharmony_ci 183213498266Sopenharmony_ci if(++pktcnt >= max_pktcnt || (size_t)nread < gsolen) { 183313498266Sopenharmony_ci /* Reached MAX_PKT_BURST *or* 183413498266Sopenharmony_ci * the capacity of our buffer *or* 183513498266Sopenharmony_ci * last add was shorter than the previous ones, flush */ 183613498266Sopenharmony_ci curlcode = vquic_send(cf, data, &ctx->q, gsolen); 183713498266Sopenharmony_ci if(curlcode) { 183813498266Sopenharmony_ci if(curlcode == CURLE_AGAIN) { 183913498266Sopenharmony_ci Curl_expire(data, 1, EXPIRE_QUIC); 184013498266Sopenharmony_ci return CURLE_OK; 184113498266Sopenharmony_ci } 184213498266Sopenharmony_ci return curlcode; 184313498266Sopenharmony_ci } 184413498266Sopenharmony_ci /* pktbuf has been completely sent */ 184513498266Sopenharmony_ci pktcnt = 0; 184613498266Sopenharmony_ci } 184713498266Sopenharmony_ci } 184813498266Sopenharmony_ci 184913498266Sopenharmony_ciout: 185013498266Sopenharmony_ci return CURLE_OK; 185113498266Sopenharmony_ci} 185213498266Sopenharmony_ci 185313498266Sopenharmony_ci/* 185413498266Sopenharmony_ci * Called from transfer.c:data_pending to know if we should keep looping 185513498266Sopenharmony_ci * to receive more data from the connection. 185613498266Sopenharmony_ci */ 185713498266Sopenharmony_cistatic bool cf_ngtcp2_data_pending(struct Curl_cfilter *cf, 185813498266Sopenharmony_ci const struct Curl_easy *data) 185913498266Sopenharmony_ci{ 186013498266Sopenharmony_ci const struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 186113498266Sopenharmony_ci (void)cf; 186213498266Sopenharmony_ci return stream && !Curl_bufq_is_empty(&stream->recvbuf); 186313498266Sopenharmony_ci} 186413498266Sopenharmony_ci 186513498266Sopenharmony_cistatic CURLcode h3_data_pause(struct Curl_cfilter *cf, 186613498266Sopenharmony_ci struct Curl_easy *data, 186713498266Sopenharmony_ci bool pause) 186813498266Sopenharmony_ci{ 186913498266Sopenharmony_ci /* TODO: there seems right now no API in ngtcp2 to shrink/enlarge 187013498266Sopenharmony_ci * the streams windows. As we do in HTTP/2. */ 187113498266Sopenharmony_ci if(!pause) { 187213498266Sopenharmony_ci h3_drain_stream(cf, data); 187313498266Sopenharmony_ci Curl_expire(data, 0, EXPIRE_RUN_NOW); 187413498266Sopenharmony_ci } 187513498266Sopenharmony_ci return CURLE_OK; 187613498266Sopenharmony_ci} 187713498266Sopenharmony_ci 187813498266Sopenharmony_cistatic CURLcode cf_ngtcp2_data_event(struct Curl_cfilter *cf, 187913498266Sopenharmony_ci struct Curl_easy *data, 188013498266Sopenharmony_ci int event, int arg1, void *arg2) 188113498266Sopenharmony_ci{ 188213498266Sopenharmony_ci struct cf_ngtcp2_ctx *ctx = cf->ctx; 188313498266Sopenharmony_ci CURLcode result = CURLE_OK; 188413498266Sopenharmony_ci struct cf_call_data save; 188513498266Sopenharmony_ci 188613498266Sopenharmony_ci CF_DATA_SAVE(save, cf, data); 188713498266Sopenharmony_ci (void)arg1; 188813498266Sopenharmony_ci (void)arg2; 188913498266Sopenharmony_ci switch(event) { 189013498266Sopenharmony_ci case CF_CTRL_DATA_SETUP: 189113498266Sopenharmony_ci break; 189213498266Sopenharmony_ci case CF_CTRL_DATA_PAUSE: 189313498266Sopenharmony_ci result = h3_data_pause(cf, data, (arg1 != 0)); 189413498266Sopenharmony_ci break; 189513498266Sopenharmony_ci case CF_CTRL_DATA_DETACH: 189613498266Sopenharmony_ci h3_data_done(cf, data); 189713498266Sopenharmony_ci break; 189813498266Sopenharmony_ci case CF_CTRL_DATA_DONE: 189913498266Sopenharmony_ci h3_data_done(cf, data); 190013498266Sopenharmony_ci break; 190113498266Sopenharmony_ci case CF_CTRL_DATA_DONE_SEND: { 190213498266Sopenharmony_ci struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 190313498266Sopenharmony_ci if(stream && !stream->send_closed) { 190413498266Sopenharmony_ci stream->send_closed = TRUE; 190513498266Sopenharmony_ci stream->upload_left = Curl_bufq_len(&stream->sendbuf); 190613498266Sopenharmony_ci (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id); 190713498266Sopenharmony_ci } 190813498266Sopenharmony_ci break; 190913498266Sopenharmony_ci } 191013498266Sopenharmony_ci case CF_CTRL_DATA_IDLE: { 191113498266Sopenharmony_ci struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 191213498266Sopenharmony_ci CURL_TRC_CF(data, cf, "data idle"); 191313498266Sopenharmony_ci if(stream && !stream->closed) { 191413498266Sopenharmony_ci result = check_and_set_expiry(cf, data, NULL); 191513498266Sopenharmony_ci if(result) 191613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "data idle, check_and_set_expiry -> %d", result); 191713498266Sopenharmony_ci } 191813498266Sopenharmony_ci break; 191913498266Sopenharmony_ci } 192013498266Sopenharmony_ci default: 192113498266Sopenharmony_ci break; 192213498266Sopenharmony_ci } 192313498266Sopenharmony_ci CF_DATA_RESTORE(cf, save); 192413498266Sopenharmony_ci return result; 192513498266Sopenharmony_ci} 192613498266Sopenharmony_ci 192713498266Sopenharmony_cistatic void cf_ngtcp2_ctx_clear(struct cf_ngtcp2_ctx *ctx) 192813498266Sopenharmony_ci{ 192913498266Sopenharmony_ci struct cf_call_data save = ctx->call_data; 193013498266Sopenharmony_ci 193113498266Sopenharmony_ci if(ctx->qlogfd != -1) { 193213498266Sopenharmony_ci close(ctx->qlogfd); 193313498266Sopenharmony_ci } 193413498266Sopenharmony_ci Curl_vquic_tls_cleanup(&ctx->tls); 193513498266Sopenharmony_ci vquic_ctx_free(&ctx->q); 193613498266Sopenharmony_ci if(ctx->h3conn) 193713498266Sopenharmony_ci nghttp3_conn_del(ctx->h3conn); 193813498266Sopenharmony_ci if(ctx->qconn) 193913498266Sopenharmony_ci ngtcp2_conn_del(ctx->qconn); 194013498266Sopenharmony_ci Curl_bufcp_free(&ctx->stream_bufcp); 194113498266Sopenharmony_ci Curl_ssl_peer_cleanup(&ctx->peer); 194213498266Sopenharmony_ci 194313498266Sopenharmony_ci memset(ctx, 0, sizeof(*ctx)); 194413498266Sopenharmony_ci ctx->qlogfd = -1; 194513498266Sopenharmony_ci ctx->call_data = save; 194613498266Sopenharmony_ci} 194713498266Sopenharmony_ci 194813498266Sopenharmony_cistatic void cf_ngtcp2_close(struct Curl_cfilter *cf, struct Curl_easy *data) 194913498266Sopenharmony_ci{ 195013498266Sopenharmony_ci struct cf_ngtcp2_ctx *ctx = cf->ctx; 195113498266Sopenharmony_ci struct cf_call_data save; 195213498266Sopenharmony_ci 195313498266Sopenharmony_ci CF_DATA_SAVE(save, cf, data); 195413498266Sopenharmony_ci if(ctx && ctx->qconn) { 195513498266Sopenharmony_ci char buffer[NGTCP2_MAX_UDP_PAYLOAD_SIZE]; 195613498266Sopenharmony_ci struct pkt_io_ctx pktx; 195713498266Sopenharmony_ci ngtcp2_ssize rc; 195813498266Sopenharmony_ci 195913498266Sopenharmony_ci CURL_TRC_CF(data, cf, "close"); 196013498266Sopenharmony_ci pktx_init(&pktx, cf, data); 196113498266Sopenharmony_ci rc = ngtcp2_conn_write_connection_close(ctx->qconn, NULL, /* path */ 196213498266Sopenharmony_ci NULL, /* pkt_info */ 196313498266Sopenharmony_ci (uint8_t *)buffer, sizeof(buffer), 196413498266Sopenharmony_ci &ctx->last_error, pktx.ts); 196513498266Sopenharmony_ci if(rc > 0) { 196613498266Sopenharmony_ci while((send(ctx->q.sockfd, buffer, (SEND_TYPE_ARG3)rc, 0) == -1) && 196713498266Sopenharmony_ci SOCKERRNO == EINTR); 196813498266Sopenharmony_ci } 196913498266Sopenharmony_ci 197013498266Sopenharmony_ci cf_ngtcp2_ctx_clear(ctx); 197113498266Sopenharmony_ci } 197213498266Sopenharmony_ci 197313498266Sopenharmony_ci cf->connected = FALSE; 197413498266Sopenharmony_ci CF_DATA_RESTORE(cf, save); 197513498266Sopenharmony_ci} 197613498266Sopenharmony_ci 197713498266Sopenharmony_cistatic void cf_ngtcp2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) 197813498266Sopenharmony_ci{ 197913498266Sopenharmony_ci struct cf_ngtcp2_ctx *ctx = cf->ctx; 198013498266Sopenharmony_ci struct cf_call_data save; 198113498266Sopenharmony_ci 198213498266Sopenharmony_ci CF_DATA_SAVE(save, cf, data); 198313498266Sopenharmony_ci CURL_TRC_CF(data, cf, "destroy"); 198413498266Sopenharmony_ci if(ctx) { 198513498266Sopenharmony_ci cf_ngtcp2_ctx_clear(ctx); 198613498266Sopenharmony_ci free(ctx); 198713498266Sopenharmony_ci } 198813498266Sopenharmony_ci cf->ctx = NULL; 198913498266Sopenharmony_ci /* No CF_DATA_RESTORE(cf, save) possible */ 199013498266Sopenharmony_ci (void)save; 199113498266Sopenharmony_ci} 199213498266Sopenharmony_ci 199313498266Sopenharmony_cistatic CURLcode tls_ctx_setup(struct quic_tls_ctx *ctx, 199413498266Sopenharmony_ci struct Curl_cfilter *cf, 199513498266Sopenharmony_ci struct Curl_easy *data) 199613498266Sopenharmony_ci{ 199713498266Sopenharmony_ci (void)cf; 199813498266Sopenharmony_ci#ifdef USE_OPENSSL 199913498266Sopenharmony_ci#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) 200013498266Sopenharmony_ci if(ngtcp2_crypto_boringssl_configure_client_context(ctx->ssl_ctx) != 0) { 200113498266Sopenharmony_ci failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed"); 200213498266Sopenharmony_ci return CURLE_FAILED_INIT; 200313498266Sopenharmony_ci } 200413498266Sopenharmony_ci#else 200513498266Sopenharmony_ci if(ngtcp2_crypto_quictls_configure_client_context(ctx->ssl_ctx) != 0) { 200613498266Sopenharmony_ci failf(data, "ngtcp2_crypto_quictls_configure_client_context failed"); 200713498266Sopenharmony_ci return CURLE_FAILED_INIT; 200813498266Sopenharmony_ci } 200913498266Sopenharmony_ci#endif /* !OPENSSL_IS_BORINGSSL && !OPENSSL_IS_AWSLC */ 201013498266Sopenharmony_ci#elif defined(USE_GNUTLS) 201113498266Sopenharmony_ci if(ngtcp2_crypto_gnutls_configure_client_session(ctx->gtls->session) != 0) { 201213498266Sopenharmony_ci failf(data, "ngtcp2_crypto_gnutls_configure_client_session failed"); 201313498266Sopenharmony_ci return CURLE_FAILED_INIT; 201413498266Sopenharmony_ci } 201513498266Sopenharmony_ci#elif defined(USE_WOLFSSL) 201613498266Sopenharmony_ci if(ngtcp2_crypto_wolfssl_configure_client_context(ctx->ssl_ctx) != 0) { 201713498266Sopenharmony_ci failf(data, "ngtcp2_crypto_wolfssl_configure_client_context failed"); 201813498266Sopenharmony_ci return CURLE_FAILED_INIT; 201913498266Sopenharmony_ci } 202013498266Sopenharmony_ci#endif 202113498266Sopenharmony_ci return CURLE_OK; 202213498266Sopenharmony_ci} 202313498266Sopenharmony_ci 202413498266Sopenharmony_ci/* 202513498266Sopenharmony_ci * Might be called twice for happy eyeballs. 202613498266Sopenharmony_ci */ 202713498266Sopenharmony_cistatic CURLcode cf_connect_start(struct Curl_cfilter *cf, 202813498266Sopenharmony_ci struct Curl_easy *data, 202913498266Sopenharmony_ci struct pkt_io_ctx *pktx) 203013498266Sopenharmony_ci{ 203113498266Sopenharmony_ci struct cf_ngtcp2_ctx *ctx = cf->ctx; 203213498266Sopenharmony_ci int rc; 203313498266Sopenharmony_ci int rv; 203413498266Sopenharmony_ci CURLcode result; 203513498266Sopenharmony_ci const struct Curl_sockaddr_ex *sockaddr = NULL; 203613498266Sopenharmony_ci int qfd; 203713498266Sopenharmony_ci 203813498266Sopenharmony_ci ctx->version = NGTCP2_PROTO_VER_MAX; 203913498266Sopenharmony_ci ctx->max_stream_window = H3_STREAM_WINDOW_SIZE; 204013498266Sopenharmony_ci ctx->max_idle_ms = CURL_QUIC_MAX_IDLE_MS; 204113498266Sopenharmony_ci Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, 204213498266Sopenharmony_ci H3_STREAM_POOL_SPARES); 204313498266Sopenharmony_ci 204413498266Sopenharmony_ci result = Curl_ssl_peer_init(&ctx->peer, cf); 204513498266Sopenharmony_ci if(result) 204613498266Sopenharmony_ci return result; 204713498266Sopenharmony_ci 204813498266Sopenharmony_ci#define H3_ALPN "\x2h3\x5h3-29" 204913498266Sopenharmony_ci result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer, 205013498266Sopenharmony_ci H3_ALPN, sizeof(H3_ALPN) - 1, 205113498266Sopenharmony_ci tls_ctx_setup, &ctx->conn_ref); 205213498266Sopenharmony_ci if(result) 205313498266Sopenharmony_ci return result; 205413498266Sopenharmony_ci 205513498266Sopenharmony_ci ctx->dcid.datalen = NGTCP2_MAX_CIDLEN; 205613498266Sopenharmony_ci result = Curl_rand(data, ctx->dcid.data, NGTCP2_MAX_CIDLEN); 205713498266Sopenharmony_ci if(result) 205813498266Sopenharmony_ci return result; 205913498266Sopenharmony_ci 206013498266Sopenharmony_ci ctx->scid.datalen = NGTCP2_MAX_CIDLEN; 206113498266Sopenharmony_ci result = Curl_rand(data, ctx->scid.data, NGTCP2_MAX_CIDLEN); 206213498266Sopenharmony_ci if(result) 206313498266Sopenharmony_ci return result; 206413498266Sopenharmony_ci 206513498266Sopenharmony_ci (void)Curl_qlogdir(data, ctx->scid.data, NGTCP2_MAX_CIDLEN, &qfd); 206613498266Sopenharmony_ci ctx->qlogfd = qfd; /* -1 if failure above */ 206713498266Sopenharmony_ci quic_settings(ctx, data, pktx); 206813498266Sopenharmony_ci 206913498266Sopenharmony_ci result = vquic_ctx_init(&ctx->q); 207013498266Sopenharmony_ci if(result) 207113498266Sopenharmony_ci return result; 207213498266Sopenharmony_ci 207313498266Sopenharmony_ci Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, 207413498266Sopenharmony_ci &sockaddr, NULL, NULL, NULL, NULL); 207513498266Sopenharmony_ci if(!sockaddr) 207613498266Sopenharmony_ci return CURLE_QUIC_CONNECT_ERROR; 207713498266Sopenharmony_ci ctx->q.local_addrlen = sizeof(ctx->q.local_addr); 207813498266Sopenharmony_ci rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr, 207913498266Sopenharmony_ci &ctx->q.local_addrlen); 208013498266Sopenharmony_ci if(rv == -1) 208113498266Sopenharmony_ci return CURLE_QUIC_CONNECT_ERROR; 208213498266Sopenharmony_ci 208313498266Sopenharmony_ci ngtcp2_addr_init(&ctx->connected_path.local, 208413498266Sopenharmony_ci (struct sockaddr *)&ctx->q.local_addr, 208513498266Sopenharmony_ci ctx->q.local_addrlen); 208613498266Sopenharmony_ci ngtcp2_addr_init(&ctx->connected_path.remote, 208713498266Sopenharmony_ci &sockaddr->sa_addr, sockaddr->addrlen); 208813498266Sopenharmony_ci 208913498266Sopenharmony_ci rc = ngtcp2_conn_client_new(&ctx->qconn, &ctx->dcid, &ctx->scid, 209013498266Sopenharmony_ci &ctx->connected_path, 209113498266Sopenharmony_ci NGTCP2_PROTO_VER_V1, &ng_callbacks, 209213498266Sopenharmony_ci &ctx->settings, &ctx->transport_params, 209313498266Sopenharmony_ci NULL, cf); 209413498266Sopenharmony_ci if(rc) 209513498266Sopenharmony_ci return CURLE_QUIC_CONNECT_ERROR; 209613498266Sopenharmony_ci 209713498266Sopenharmony_ci#ifdef USE_GNUTLS 209813498266Sopenharmony_ci ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.gtls->session); 209913498266Sopenharmony_ci#else 210013498266Sopenharmony_ci ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.ssl); 210113498266Sopenharmony_ci#endif 210213498266Sopenharmony_ci 210313498266Sopenharmony_ci ngtcp2_ccerr_default(&ctx->last_error); 210413498266Sopenharmony_ci 210513498266Sopenharmony_ci ctx->conn_ref.get_conn = get_conn; 210613498266Sopenharmony_ci ctx->conn_ref.user_data = cf; 210713498266Sopenharmony_ci 210813498266Sopenharmony_ci return CURLE_OK; 210913498266Sopenharmony_ci} 211013498266Sopenharmony_ci 211113498266Sopenharmony_cistatic CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf, 211213498266Sopenharmony_ci struct Curl_easy *data, 211313498266Sopenharmony_ci bool blocking, bool *done) 211413498266Sopenharmony_ci{ 211513498266Sopenharmony_ci struct cf_ngtcp2_ctx *ctx = cf->ctx; 211613498266Sopenharmony_ci CURLcode result = CURLE_OK; 211713498266Sopenharmony_ci struct cf_call_data save; 211813498266Sopenharmony_ci struct curltime now; 211913498266Sopenharmony_ci struct pkt_io_ctx pktx; 212013498266Sopenharmony_ci 212113498266Sopenharmony_ci if(cf->connected) { 212213498266Sopenharmony_ci *done = TRUE; 212313498266Sopenharmony_ci return CURLE_OK; 212413498266Sopenharmony_ci } 212513498266Sopenharmony_ci 212613498266Sopenharmony_ci /* Connect the UDP filter first */ 212713498266Sopenharmony_ci if(!cf->next->connected) { 212813498266Sopenharmony_ci result = Curl_conn_cf_connect(cf->next, data, blocking, done); 212913498266Sopenharmony_ci if(result || !*done) 213013498266Sopenharmony_ci return result; 213113498266Sopenharmony_ci } 213213498266Sopenharmony_ci 213313498266Sopenharmony_ci *done = FALSE; 213413498266Sopenharmony_ci now = Curl_now(); 213513498266Sopenharmony_ci pktx_init(&pktx, cf, data); 213613498266Sopenharmony_ci 213713498266Sopenharmony_ci CF_DATA_SAVE(save, cf, data); 213813498266Sopenharmony_ci 213913498266Sopenharmony_ci if(ctx->reconnect_at.tv_sec && Curl_timediff(now, ctx->reconnect_at) < 0) { 214013498266Sopenharmony_ci /* Not time yet to attempt the next connect */ 214113498266Sopenharmony_ci CURL_TRC_CF(data, cf, "waiting for reconnect time"); 214213498266Sopenharmony_ci goto out; 214313498266Sopenharmony_ci } 214413498266Sopenharmony_ci 214513498266Sopenharmony_ci if(!ctx->qconn) { 214613498266Sopenharmony_ci ctx->started_at = now; 214713498266Sopenharmony_ci result = cf_connect_start(cf, data, &pktx); 214813498266Sopenharmony_ci if(result) 214913498266Sopenharmony_ci goto out; 215013498266Sopenharmony_ci result = cf_progress_egress(cf, data, &pktx); 215113498266Sopenharmony_ci /* we do not expect to be able to recv anything yet */ 215213498266Sopenharmony_ci goto out; 215313498266Sopenharmony_ci } 215413498266Sopenharmony_ci 215513498266Sopenharmony_ci result = cf_progress_ingress(cf, data, &pktx); 215613498266Sopenharmony_ci if(result) 215713498266Sopenharmony_ci goto out; 215813498266Sopenharmony_ci 215913498266Sopenharmony_ci result = cf_progress_egress(cf, data, &pktx); 216013498266Sopenharmony_ci if(result) 216113498266Sopenharmony_ci goto out; 216213498266Sopenharmony_ci 216313498266Sopenharmony_ci if(ngtcp2_conn_get_handshake_completed(ctx->qconn)) { 216413498266Sopenharmony_ci ctx->handshake_at = now; 216513498266Sopenharmony_ci CURL_TRC_CF(data, cf, "handshake complete after %dms", 216613498266Sopenharmony_ci (int)Curl_timediff(now, ctx->started_at)); 216713498266Sopenharmony_ci result = qng_verify_peer(cf, data); 216813498266Sopenharmony_ci if(!result) { 216913498266Sopenharmony_ci CURL_TRC_CF(data, cf, "peer verified"); 217013498266Sopenharmony_ci cf->connected = TRUE; 217113498266Sopenharmony_ci cf->conn->alpn = CURL_HTTP_VERSION_3; 217213498266Sopenharmony_ci *done = TRUE; 217313498266Sopenharmony_ci connkeep(cf->conn, "HTTP/3 default"); 217413498266Sopenharmony_ci } 217513498266Sopenharmony_ci } 217613498266Sopenharmony_ci 217713498266Sopenharmony_ciout: 217813498266Sopenharmony_ci if(result == CURLE_RECV_ERROR && ctx->qconn && 217913498266Sopenharmony_ci ngtcp2_conn_in_draining_period(ctx->qconn)) { 218013498266Sopenharmony_ci /* When a QUIC server instance is shutting down, it may send us a 218113498266Sopenharmony_ci * CONNECTION_CLOSE right away. Our connection then enters the DRAINING 218213498266Sopenharmony_ci * state. The CONNECT may work in the near future again. Indicate 218313498266Sopenharmony_ci * that as a "weird" reply. */ 218413498266Sopenharmony_ci result = CURLE_WEIRD_SERVER_REPLY; 218513498266Sopenharmony_ci } 218613498266Sopenharmony_ci 218713498266Sopenharmony_ci#ifndef CURL_DISABLE_VERBOSE_STRINGS 218813498266Sopenharmony_ci if(result) { 218913498266Sopenharmony_ci const char *r_ip = NULL; 219013498266Sopenharmony_ci int r_port = 0; 219113498266Sopenharmony_ci 219213498266Sopenharmony_ci Curl_cf_socket_peek(cf->next, data, NULL, NULL, 219313498266Sopenharmony_ci &r_ip, &r_port, NULL, NULL); 219413498266Sopenharmony_ci infof(data, "QUIC connect to %s port %u failed: %s", 219513498266Sopenharmony_ci r_ip, r_port, curl_easy_strerror(result)); 219613498266Sopenharmony_ci } 219713498266Sopenharmony_ci#endif 219813498266Sopenharmony_ci if(!result && ctx->qconn) { 219913498266Sopenharmony_ci result = check_and_set_expiry(cf, data, &pktx); 220013498266Sopenharmony_ci } 220113498266Sopenharmony_ci if(result || *done) 220213498266Sopenharmony_ci CURL_TRC_CF(data, cf, "connect -> %d, done=%d", result, *done); 220313498266Sopenharmony_ci CF_DATA_RESTORE(cf, save); 220413498266Sopenharmony_ci return result; 220513498266Sopenharmony_ci} 220613498266Sopenharmony_ci 220713498266Sopenharmony_cistatic CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf, 220813498266Sopenharmony_ci struct Curl_easy *data, 220913498266Sopenharmony_ci int query, int *pres1, void *pres2) 221013498266Sopenharmony_ci{ 221113498266Sopenharmony_ci struct cf_ngtcp2_ctx *ctx = cf->ctx; 221213498266Sopenharmony_ci struct cf_call_data save; 221313498266Sopenharmony_ci 221413498266Sopenharmony_ci switch(query) { 221513498266Sopenharmony_ci case CF_QUERY_MAX_CONCURRENT: { 221613498266Sopenharmony_ci const ngtcp2_transport_params *rp; 221713498266Sopenharmony_ci DEBUGASSERT(pres1); 221813498266Sopenharmony_ci 221913498266Sopenharmony_ci CF_DATA_SAVE(save, cf, data); 222013498266Sopenharmony_ci rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn); 222113498266Sopenharmony_ci if(rp) 222213498266Sopenharmony_ci *pres1 = (rp->initial_max_streams_bidi > INT_MAX)? 222313498266Sopenharmony_ci INT_MAX : (int)rp->initial_max_streams_bidi; 222413498266Sopenharmony_ci else /* not arrived yet? */ 222513498266Sopenharmony_ci *pres1 = Curl_multi_max_concurrent_streams(data->multi); 222613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "query max_conncurrent -> %d", *pres1); 222713498266Sopenharmony_ci CF_DATA_RESTORE(cf, save); 222813498266Sopenharmony_ci return CURLE_OK; 222913498266Sopenharmony_ci } 223013498266Sopenharmony_ci case CF_QUERY_CONNECT_REPLY_MS: 223113498266Sopenharmony_ci if(ctx->q.got_first_byte) { 223213498266Sopenharmony_ci timediff_t ms = Curl_timediff(ctx->q.first_byte_at, ctx->started_at); 223313498266Sopenharmony_ci *pres1 = (ms < INT_MAX)? (int)ms : INT_MAX; 223413498266Sopenharmony_ci } 223513498266Sopenharmony_ci else 223613498266Sopenharmony_ci *pres1 = -1; 223713498266Sopenharmony_ci return CURLE_OK; 223813498266Sopenharmony_ci case CF_QUERY_TIMER_CONNECT: { 223913498266Sopenharmony_ci struct curltime *when = pres2; 224013498266Sopenharmony_ci if(ctx->q.got_first_byte) 224113498266Sopenharmony_ci *when = ctx->q.first_byte_at; 224213498266Sopenharmony_ci return CURLE_OK; 224313498266Sopenharmony_ci } 224413498266Sopenharmony_ci case CF_QUERY_TIMER_APPCONNECT: { 224513498266Sopenharmony_ci struct curltime *when = pres2; 224613498266Sopenharmony_ci if(cf->connected) 224713498266Sopenharmony_ci *when = ctx->handshake_at; 224813498266Sopenharmony_ci return CURLE_OK; 224913498266Sopenharmony_ci } 225013498266Sopenharmony_ci default: 225113498266Sopenharmony_ci break; 225213498266Sopenharmony_ci } 225313498266Sopenharmony_ci return cf->next? 225413498266Sopenharmony_ci cf->next->cft->query(cf->next, data, query, pres1, pres2) : 225513498266Sopenharmony_ci CURLE_UNKNOWN_OPTION; 225613498266Sopenharmony_ci} 225713498266Sopenharmony_ci 225813498266Sopenharmony_cistatic bool cf_ngtcp2_conn_is_alive(struct Curl_cfilter *cf, 225913498266Sopenharmony_ci struct Curl_easy *data, 226013498266Sopenharmony_ci bool *input_pending) 226113498266Sopenharmony_ci{ 226213498266Sopenharmony_ci struct cf_ngtcp2_ctx *ctx = cf->ctx; 226313498266Sopenharmony_ci bool alive = FALSE; 226413498266Sopenharmony_ci const ngtcp2_transport_params *rp; 226513498266Sopenharmony_ci struct cf_call_data save; 226613498266Sopenharmony_ci 226713498266Sopenharmony_ci CF_DATA_SAVE(save, cf, data); 226813498266Sopenharmony_ci *input_pending = FALSE; 226913498266Sopenharmony_ci if(!ctx->qconn) 227013498266Sopenharmony_ci goto out; 227113498266Sopenharmony_ci 227213498266Sopenharmony_ci /* Both sides of the QUIC connection announce they max idle times in 227313498266Sopenharmony_ci * the transport parameters. Look at the minimum of both and if 227413498266Sopenharmony_ci * we exceed this, regard the connection as dead. The other side 227513498266Sopenharmony_ci * may have completely purged it and will no longer respond 227613498266Sopenharmony_ci * to any packets from us. */ 227713498266Sopenharmony_ci rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn); 227813498266Sopenharmony_ci if(rp) { 227913498266Sopenharmony_ci timediff_t idletime; 228013498266Sopenharmony_ci uint64_t idle_ms = ctx->max_idle_ms; 228113498266Sopenharmony_ci 228213498266Sopenharmony_ci if(rp->max_idle_timeout && 228313498266Sopenharmony_ci (rp->max_idle_timeout / NGTCP2_MILLISECONDS) < idle_ms) 228413498266Sopenharmony_ci idle_ms = (rp->max_idle_timeout / NGTCP2_MILLISECONDS); 228513498266Sopenharmony_ci idletime = Curl_timediff(Curl_now(), ctx->q.last_io); 228613498266Sopenharmony_ci if(idletime > 0 && (uint64_t)idletime > idle_ms) 228713498266Sopenharmony_ci goto out; 228813498266Sopenharmony_ci } 228913498266Sopenharmony_ci 229013498266Sopenharmony_ci if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending)) 229113498266Sopenharmony_ci goto out; 229213498266Sopenharmony_ci 229313498266Sopenharmony_ci alive = TRUE; 229413498266Sopenharmony_ci if(*input_pending) { 229513498266Sopenharmony_ci CURLcode result; 229613498266Sopenharmony_ci /* This happens before we've sent off a request and the connection is 229713498266Sopenharmony_ci not in use by any other transfer, there shouldn't be any data here, 229813498266Sopenharmony_ci only "protocol frames" */ 229913498266Sopenharmony_ci *input_pending = FALSE; 230013498266Sopenharmony_ci result = cf_progress_ingress(cf, data, NULL); 230113498266Sopenharmony_ci CURL_TRC_CF(data, cf, "is_alive, progress ingress -> %d", result); 230213498266Sopenharmony_ci alive = result? FALSE : TRUE; 230313498266Sopenharmony_ci } 230413498266Sopenharmony_ci 230513498266Sopenharmony_ciout: 230613498266Sopenharmony_ci CF_DATA_RESTORE(cf, save); 230713498266Sopenharmony_ci return alive; 230813498266Sopenharmony_ci} 230913498266Sopenharmony_ci 231013498266Sopenharmony_cistruct Curl_cftype Curl_cft_http3 = { 231113498266Sopenharmony_ci "HTTP/3", 231213498266Sopenharmony_ci CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX, 231313498266Sopenharmony_ci 0, 231413498266Sopenharmony_ci cf_ngtcp2_destroy, 231513498266Sopenharmony_ci cf_ngtcp2_connect, 231613498266Sopenharmony_ci cf_ngtcp2_close, 231713498266Sopenharmony_ci Curl_cf_def_get_host, 231813498266Sopenharmony_ci cf_ngtcp2_adjust_pollset, 231913498266Sopenharmony_ci cf_ngtcp2_data_pending, 232013498266Sopenharmony_ci cf_ngtcp2_send, 232113498266Sopenharmony_ci cf_ngtcp2_recv, 232213498266Sopenharmony_ci cf_ngtcp2_data_event, 232313498266Sopenharmony_ci cf_ngtcp2_conn_is_alive, 232413498266Sopenharmony_ci Curl_cf_def_conn_keep_alive, 232513498266Sopenharmony_ci cf_ngtcp2_query, 232613498266Sopenharmony_ci}; 232713498266Sopenharmony_ci 232813498266Sopenharmony_ciCURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf, 232913498266Sopenharmony_ci struct Curl_easy *data, 233013498266Sopenharmony_ci struct connectdata *conn, 233113498266Sopenharmony_ci const struct Curl_addrinfo *ai) 233213498266Sopenharmony_ci{ 233313498266Sopenharmony_ci struct cf_ngtcp2_ctx *ctx = NULL; 233413498266Sopenharmony_ci struct Curl_cfilter *cf = NULL, *udp_cf = NULL; 233513498266Sopenharmony_ci CURLcode result; 233613498266Sopenharmony_ci 233713498266Sopenharmony_ci (void)data; 233813498266Sopenharmony_ci ctx = calloc(1, sizeof(*ctx)); 233913498266Sopenharmony_ci if(!ctx) { 234013498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 234113498266Sopenharmony_ci goto out; 234213498266Sopenharmony_ci } 234313498266Sopenharmony_ci ctx->qlogfd = -1; 234413498266Sopenharmony_ci cf_ngtcp2_ctx_clear(ctx); 234513498266Sopenharmony_ci 234613498266Sopenharmony_ci result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); 234713498266Sopenharmony_ci if(result) 234813498266Sopenharmony_ci goto out; 234913498266Sopenharmony_ci 235013498266Sopenharmony_ci result = Curl_cf_udp_create(&udp_cf, data, conn, ai, TRNSPRT_QUIC); 235113498266Sopenharmony_ci if(result) 235213498266Sopenharmony_ci goto out; 235313498266Sopenharmony_ci 235413498266Sopenharmony_ci cf->conn = conn; 235513498266Sopenharmony_ci udp_cf->conn = cf->conn; 235613498266Sopenharmony_ci udp_cf->sockindex = cf->sockindex; 235713498266Sopenharmony_ci cf->next = udp_cf; 235813498266Sopenharmony_ci 235913498266Sopenharmony_ciout: 236013498266Sopenharmony_ci *pcf = (!result)? cf : NULL; 236113498266Sopenharmony_ci if(result) { 236213498266Sopenharmony_ci if(udp_cf) 236313498266Sopenharmony_ci Curl_conn_cf_discard_sub(cf, udp_cf, data, TRUE); 236413498266Sopenharmony_ci Curl_safefree(cf); 236513498266Sopenharmony_ci Curl_safefree(ctx); 236613498266Sopenharmony_ci } 236713498266Sopenharmony_ci return result; 236813498266Sopenharmony_ci} 236913498266Sopenharmony_ci 237013498266Sopenharmony_cibool Curl_conn_is_ngtcp2(const struct Curl_easy *data, 237113498266Sopenharmony_ci const struct connectdata *conn, 237213498266Sopenharmony_ci int sockindex) 237313498266Sopenharmony_ci{ 237413498266Sopenharmony_ci struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL; 237513498266Sopenharmony_ci 237613498266Sopenharmony_ci (void)data; 237713498266Sopenharmony_ci for(; cf; cf = cf->next) { 237813498266Sopenharmony_ci if(cf->cft == &Curl_cft_http3) 237913498266Sopenharmony_ci return TRUE; 238013498266Sopenharmony_ci if(cf->cft->flags & CF_TYPE_IP_CONNECT) 238113498266Sopenharmony_ci return FALSE; 238213498266Sopenharmony_ci } 238313498266Sopenharmony_ci return FALSE; 238413498266Sopenharmony_ci} 238513498266Sopenharmony_ci 238613498266Sopenharmony_ci#endif 2387