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#ifdef USE_QUICHE 2813498266Sopenharmony_ci#include <quiche.h> 2913498266Sopenharmony_ci#include <openssl/err.h> 3013498266Sopenharmony_ci#include <openssl/ssl.h> 3113498266Sopenharmony_ci#include "bufq.h" 3213498266Sopenharmony_ci#include "urldata.h" 3313498266Sopenharmony_ci#include "cfilters.h" 3413498266Sopenharmony_ci#include "cf-socket.h" 3513498266Sopenharmony_ci#include "sendf.h" 3613498266Sopenharmony_ci#include "strdup.h" 3713498266Sopenharmony_ci#include "rand.h" 3813498266Sopenharmony_ci#include "strcase.h" 3913498266Sopenharmony_ci#include "multiif.h" 4013498266Sopenharmony_ci#include "connect.h" 4113498266Sopenharmony_ci#include "progress.h" 4213498266Sopenharmony_ci#include "strerror.h" 4313498266Sopenharmony_ci#include "http1.h" 4413498266Sopenharmony_ci#include "vquic.h" 4513498266Sopenharmony_ci#include "vquic_int.h" 4613498266Sopenharmony_ci#include "vquic-tls.h" 4713498266Sopenharmony_ci#include "curl_quiche.h" 4813498266Sopenharmony_ci#include "transfer.h" 4913498266Sopenharmony_ci#include "inet_pton.h" 5013498266Sopenharmony_ci#include "vtls/openssl.h" 5113498266Sopenharmony_ci#include "vtls/keylog.h" 5213498266Sopenharmony_ci#include "vtls/vtls.h" 5313498266Sopenharmony_ci 5413498266Sopenharmony_ci/* The last 3 #include files should be in this order */ 5513498266Sopenharmony_ci#include "curl_printf.h" 5613498266Sopenharmony_ci#include "curl_memory.h" 5713498266Sopenharmony_ci#include "memdebug.h" 5813498266Sopenharmony_ci 5913498266Sopenharmony_ci/* HTTP/3 error values defined in RFC 9114, ch. 8.1 */ 6013498266Sopenharmony_ci#define CURL_H3_NO_ERROR (0x0100) 6113498266Sopenharmony_ci 6213498266Sopenharmony_ci#define QUIC_MAX_STREAMS (100) 6313498266Sopenharmony_ci 6413498266Sopenharmony_ci#define H3_STREAM_WINDOW_SIZE (128 * 1024) 6513498266Sopenharmony_ci#define H3_STREAM_CHUNK_SIZE (16 * 1024) 6613498266Sopenharmony_ci/* The pool keeps spares around and half of a full stream windows 6713498266Sopenharmony_ci * seems good. More does not seem to improve performance. 6813498266Sopenharmony_ci * The benefit of the pool is that stream buffer to not keep 6913498266Sopenharmony_ci * spares. So memory consumption goes down when streams run empty, 7013498266Sopenharmony_ci * have a large upload done, etc. */ 7113498266Sopenharmony_ci#define H3_STREAM_POOL_SPARES \ 7213498266Sopenharmony_ci (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE ) / 2 7313498266Sopenharmony_ci/* Receive and Send max number of chunks just follows from the 7413498266Sopenharmony_ci * chunk size and window size */ 7513498266Sopenharmony_ci#define H3_STREAM_RECV_CHUNKS \ 7613498266Sopenharmony_ci (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) 7713498266Sopenharmony_ci#define H3_STREAM_SEND_CHUNKS \ 7813498266Sopenharmony_ci (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) 7913498266Sopenharmony_ci 8013498266Sopenharmony_ci/* 8113498266Sopenharmony_ci * Store quiche version info in this buffer. 8213498266Sopenharmony_ci */ 8313498266Sopenharmony_civoid Curl_quiche_ver(char *p, size_t len) 8413498266Sopenharmony_ci{ 8513498266Sopenharmony_ci (void)msnprintf(p, len, "quiche/%s", quiche_version()); 8613498266Sopenharmony_ci} 8713498266Sopenharmony_ci 8813498266Sopenharmony_cistruct cf_quiche_ctx { 8913498266Sopenharmony_ci struct cf_quic_ctx q; 9013498266Sopenharmony_ci struct ssl_peer peer; 9113498266Sopenharmony_ci struct quic_tls_ctx tls; 9213498266Sopenharmony_ci quiche_conn *qconn; 9313498266Sopenharmony_ci quiche_config *cfg; 9413498266Sopenharmony_ci quiche_h3_conn *h3c; 9513498266Sopenharmony_ci quiche_h3_config *h3config; 9613498266Sopenharmony_ci uint8_t scid[QUICHE_MAX_CONN_ID_LEN]; 9713498266Sopenharmony_ci struct curltime started_at; /* time the current attempt started */ 9813498266Sopenharmony_ci struct curltime handshake_at; /* time connect handshake finished */ 9913498266Sopenharmony_ci struct curltime reconnect_at; /* time the next attempt should start */ 10013498266Sopenharmony_ci struct bufc_pool stream_bufcp; /* chunk pool for streams */ 10113498266Sopenharmony_ci curl_off_t data_recvd; 10213498266Sopenharmony_ci uint64_t max_idle_ms; /* max idle time for QUIC conn */ 10313498266Sopenharmony_ci BIT(goaway); /* got GOAWAY from server */ 10413498266Sopenharmony_ci BIT(x509_store_setup); /* if x509 store has been set up */ 10513498266Sopenharmony_ci}; 10613498266Sopenharmony_ci 10713498266Sopenharmony_ci#ifdef DEBUG_QUICHE 10813498266Sopenharmony_cistatic void quiche_debug_log(const char *line, void *argp) 10913498266Sopenharmony_ci{ 11013498266Sopenharmony_ci (void)argp; 11113498266Sopenharmony_ci fprintf(stderr, "%s\n", line); 11213498266Sopenharmony_ci} 11313498266Sopenharmony_ci#endif 11413498266Sopenharmony_ci 11513498266Sopenharmony_cistatic void cf_quiche_ctx_clear(struct cf_quiche_ctx *ctx) 11613498266Sopenharmony_ci{ 11713498266Sopenharmony_ci if(ctx) { 11813498266Sopenharmony_ci if(ctx->h3c) 11913498266Sopenharmony_ci quiche_h3_conn_free(ctx->h3c); 12013498266Sopenharmony_ci if(ctx->h3config) 12113498266Sopenharmony_ci quiche_h3_config_free(ctx->h3config); 12213498266Sopenharmony_ci if(ctx->qconn) 12313498266Sopenharmony_ci quiche_conn_free(ctx->qconn); 12413498266Sopenharmony_ci if(ctx->cfg) 12513498266Sopenharmony_ci quiche_config_free(ctx->cfg); 12613498266Sopenharmony_ci /* quiche just freed ctx->tls.ssl */ 12713498266Sopenharmony_ci ctx->tls.ssl = NULL; 12813498266Sopenharmony_ci Curl_vquic_tls_cleanup(&ctx->tls); 12913498266Sopenharmony_ci Curl_ssl_peer_cleanup(&ctx->peer); 13013498266Sopenharmony_ci vquic_ctx_free(&ctx->q); 13113498266Sopenharmony_ci Curl_bufcp_free(&ctx->stream_bufcp); 13213498266Sopenharmony_ci 13313498266Sopenharmony_ci memset(ctx, 0, sizeof(*ctx)); 13413498266Sopenharmony_ci } 13513498266Sopenharmony_ci} 13613498266Sopenharmony_ci 13713498266Sopenharmony_ci/** 13813498266Sopenharmony_ci * All about the H3 internals of a stream 13913498266Sopenharmony_ci */ 14013498266Sopenharmony_cistruct stream_ctx { 14113498266Sopenharmony_ci int64_t id; /* HTTP/3 protocol stream identifier */ 14213498266Sopenharmony_ci struct bufq recvbuf; /* h3 response */ 14313498266Sopenharmony_ci struct h1_req_parser h1; /* h1 request parsing */ 14413498266Sopenharmony_ci uint64_t error3; /* HTTP/3 stream error code */ 14513498266Sopenharmony_ci curl_off_t upload_left; /* number of request bytes left to upload */ 14613498266Sopenharmony_ci bool closed; /* TRUE on stream close */ 14713498266Sopenharmony_ci bool reset; /* TRUE on stream reset */ 14813498266Sopenharmony_ci bool send_closed; /* stream is locally closed */ 14913498266Sopenharmony_ci bool resp_hds_complete; /* complete, final response has been received */ 15013498266Sopenharmony_ci bool resp_got_header; /* TRUE when h3 stream has recvd some HEADER */ 15113498266Sopenharmony_ci BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */ 15213498266Sopenharmony_ci}; 15313498266Sopenharmony_ci 15413498266Sopenharmony_ci#define H3_STREAM_CTX(d) ((struct stream_ctx *)(((d) && (d)->req.p.http)? \ 15513498266Sopenharmony_ci ((struct HTTP *)(d)->req.p.http)->h3_ctx \ 15613498266Sopenharmony_ci : NULL)) 15713498266Sopenharmony_ci#define H3_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->h3_ctx 15813498266Sopenharmony_ci#define H3_STREAM_ID(d) (H3_STREAM_CTX(d)? \ 15913498266Sopenharmony_ci H3_STREAM_CTX(d)->id : -2) 16013498266Sopenharmony_ci 16113498266Sopenharmony_cistatic void check_resumes(struct Curl_cfilter *cf, 16213498266Sopenharmony_ci struct Curl_easy *data) 16313498266Sopenharmony_ci{ 16413498266Sopenharmony_ci struct Curl_easy *sdata; 16513498266Sopenharmony_ci struct stream_ctx *stream; 16613498266Sopenharmony_ci 16713498266Sopenharmony_ci DEBUGASSERT(data->multi); 16813498266Sopenharmony_ci for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { 16913498266Sopenharmony_ci if(sdata->conn == data->conn) { 17013498266Sopenharmony_ci stream = H3_STREAM_CTX(sdata); 17113498266Sopenharmony_ci if(stream && stream->quic_flow_blocked) { 17213498266Sopenharmony_ci stream->quic_flow_blocked = FALSE; 17313498266Sopenharmony_ci Curl_expire(data, 0, EXPIRE_RUN_NOW); 17413498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%"PRId64"] unblock", stream->id); 17513498266Sopenharmony_ci } 17613498266Sopenharmony_ci } 17713498266Sopenharmony_ci } 17813498266Sopenharmony_ci} 17913498266Sopenharmony_ci 18013498266Sopenharmony_cistatic CURLcode h3_data_setup(struct Curl_cfilter *cf, 18113498266Sopenharmony_ci struct Curl_easy *data) 18213498266Sopenharmony_ci{ 18313498266Sopenharmony_ci struct cf_quiche_ctx *ctx = cf->ctx; 18413498266Sopenharmony_ci struct stream_ctx *stream = H3_STREAM_CTX(data); 18513498266Sopenharmony_ci 18613498266Sopenharmony_ci if(stream) 18713498266Sopenharmony_ci return CURLE_OK; 18813498266Sopenharmony_ci 18913498266Sopenharmony_ci stream = calloc(1, sizeof(*stream)); 19013498266Sopenharmony_ci if(!stream) 19113498266Sopenharmony_ci return CURLE_OUT_OF_MEMORY; 19213498266Sopenharmony_ci 19313498266Sopenharmony_ci H3_STREAM_LCTX(data) = stream; 19413498266Sopenharmony_ci stream->id = -1; 19513498266Sopenharmony_ci Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp, 19613498266Sopenharmony_ci H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); 19713498266Sopenharmony_ci Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); 19813498266Sopenharmony_ci return CURLE_OK; 19913498266Sopenharmony_ci} 20013498266Sopenharmony_ci 20113498266Sopenharmony_cistatic void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) 20213498266Sopenharmony_ci{ 20313498266Sopenharmony_ci struct cf_quiche_ctx *ctx = cf->ctx; 20413498266Sopenharmony_ci struct stream_ctx *stream = H3_STREAM_CTX(data); 20513498266Sopenharmony_ci 20613498266Sopenharmony_ci (void)cf; 20713498266Sopenharmony_ci if(stream) { 20813498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%"PRId64"] easy handle is done", stream->id); 20913498266Sopenharmony_ci if(ctx->qconn && !stream->closed) { 21013498266Sopenharmony_ci quiche_conn_stream_shutdown(ctx->qconn, stream->id, 21113498266Sopenharmony_ci QUICHE_SHUTDOWN_READ, CURL_H3_NO_ERROR); 21213498266Sopenharmony_ci if(!stream->send_closed) { 21313498266Sopenharmony_ci quiche_conn_stream_shutdown(ctx->qconn, stream->id, 21413498266Sopenharmony_ci QUICHE_SHUTDOWN_WRITE, CURL_H3_NO_ERROR); 21513498266Sopenharmony_ci stream->send_closed = TRUE; 21613498266Sopenharmony_ci } 21713498266Sopenharmony_ci stream->closed = TRUE; 21813498266Sopenharmony_ci } 21913498266Sopenharmony_ci Curl_bufq_free(&stream->recvbuf); 22013498266Sopenharmony_ci Curl_h1_req_parse_free(&stream->h1); 22113498266Sopenharmony_ci free(stream); 22213498266Sopenharmony_ci H3_STREAM_LCTX(data) = NULL; 22313498266Sopenharmony_ci } 22413498266Sopenharmony_ci} 22513498266Sopenharmony_ci 22613498266Sopenharmony_cistatic void drain_stream(struct Curl_cfilter *cf, 22713498266Sopenharmony_ci struct Curl_easy *data) 22813498266Sopenharmony_ci{ 22913498266Sopenharmony_ci struct stream_ctx *stream = H3_STREAM_CTX(data); 23013498266Sopenharmony_ci unsigned char bits; 23113498266Sopenharmony_ci 23213498266Sopenharmony_ci (void)cf; 23313498266Sopenharmony_ci bits = CURL_CSELECT_IN; 23413498266Sopenharmony_ci if(stream && !stream->send_closed && stream->upload_left) 23513498266Sopenharmony_ci bits |= CURL_CSELECT_OUT; 23613498266Sopenharmony_ci if(data->state.select_bits != bits) { 23713498266Sopenharmony_ci data->state.select_bits = bits; 23813498266Sopenharmony_ci Curl_expire(data, 0, EXPIRE_RUN_NOW); 23913498266Sopenharmony_ci } 24013498266Sopenharmony_ci} 24113498266Sopenharmony_ci 24213498266Sopenharmony_cistatic struct Curl_easy *get_stream_easy(struct Curl_cfilter *cf, 24313498266Sopenharmony_ci struct Curl_easy *data, 24413498266Sopenharmony_ci int64_t stream3_id) 24513498266Sopenharmony_ci{ 24613498266Sopenharmony_ci struct Curl_easy *sdata; 24713498266Sopenharmony_ci 24813498266Sopenharmony_ci (void)cf; 24913498266Sopenharmony_ci if(H3_STREAM_ID(data) == stream3_id) { 25013498266Sopenharmony_ci return data; 25113498266Sopenharmony_ci } 25213498266Sopenharmony_ci else { 25313498266Sopenharmony_ci DEBUGASSERT(data->multi); 25413498266Sopenharmony_ci for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { 25513498266Sopenharmony_ci if((sdata->conn == data->conn) && H3_STREAM_ID(sdata) == stream3_id) { 25613498266Sopenharmony_ci return sdata; 25713498266Sopenharmony_ci } 25813498266Sopenharmony_ci } 25913498266Sopenharmony_ci } 26013498266Sopenharmony_ci return NULL; 26113498266Sopenharmony_ci} 26213498266Sopenharmony_ci 26313498266Sopenharmony_ci/* 26413498266Sopenharmony_ci * write_resp_raw() copies response data in raw format to the `data`'s 26513498266Sopenharmony_ci * receive buffer. If not enough space is available, it appends to the 26613498266Sopenharmony_ci * `data`'s overflow buffer. 26713498266Sopenharmony_ci */ 26813498266Sopenharmony_cistatic CURLcode write_resp_raw(struct Curl_cfilter *cf, 26913498266Sopenharmony_ci struct Curl_easy *data, 27013498266Sopenharmony_ci const void *mem, size_t memlen) 27113498266Sopenharmony_ci{ 27213498266Sopenharmony_ci struct stream_ctx *stream = H3_STREAM_CTX(data); 27313498266Sopenharmony_ci CURLcode result = CURLE_OK; 27413498266Sopenharmony_ci ssize_t nwritten; 27513498266Sopenharmony_ci 27613498266Sopenharmony_ci (void)cf; 27713498266Sopenharmony_ci if(!stream) 27813498266Sopenharmony_ci return CURLE_RECV_ERROR; 27913498266Sopenharmony_ci nwritten = Curl_bufq_write(&stream->recvbuf, mem, memlen, &result); 28013498266Sopenharmony_ci if(nwritten < 0) 28113498266Sopenharmony_ci return result; 28213498266Sopenharmony_ci 28313498266Sopenharmony_ci if((size_t)nwritten < memlen) { 28413498266Sopenharmony_ci /* This MUST not happen. Our recbuf is dimensioned to hold the 28513498266Sopenharmony_ci * full max_stream_window and then some for this very reason. */ 28613498266Sopenharmony_ci DEBUGASSERT(0); 28713498266Sopenharmony_ci return CURLE_RECV_ERROR; 28813498266Sopenharmony_ci } 28913498266Sopenharmony_ci return result; 29013498266Sopenharmony_ci} 29113498266Sopenharmony_ci 29213498266Sopenharmony_cistruct cb_ctx { 29313498266Sopenharmony_ci struct Curl_cfilter *cf; 29413498266Sopenharmony_ci struct Curl_easy *data; 29513498266Sopenharmony_ci}; 29613498266Sopenharmony_ci 29713498266Sopenharmony_cistatic int cb_each_header(uint8_t *name, size_t name_len, 29813498266Sopenharmony_ci uint8_t *value, size_t value_len, 29913498266Sopenharmony_ci void *argp) 30013498266Sopenharmony_ci{ 30113498266Sopenharmony_ci struct cb_ctx *x = argp; 30213498266Sopenharmony_ci struct stream_ctx *stream = H3_STREAM_CTX(x->data); 30313498266Sopenharmony_ci CURLcode result; 30413498266Sopenharmony_ci 30513498266Sopenharmony_ci if(!stream) 30613498266Sopenharmony_ci return CURLE_OK; 30713498266Sopenharmony_ci 30813498266Sopenharmony_ci if((name_len == 7) && !strncmp(HTTP_PSEUDO_STATUS, (char *)name, 7)) { 30913498266Sopenharmony_ci CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] status: %.*s", 31013498266Sopenharmony_ci stream->id, (int)value_len, value); 31113498266Sopenharmony_ci result = write_resp_raw(x->cf, x->data, "HTTP/3 ", sizeof("HTTP/3 ") - 1); 31213498266Sopenharmony_ci if(!result) 31313498266Sopenharmony_ci result = write_resp_raw(x->cf, x->data, value, value_len); 31413498266Sopenharmony_ci if(!result) 31513498266Sopenharmony_ci result = write_resp_raw(x->cf, x->data, " \r\n", 3); 31613498266Sopenharmony_ci } 31713498266Sopenharmony_ci else { 31813498266Sopenharmony_ci CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] header: %.*s: %.*s", 31913498266Sopenharmony_ci stream->id, (int)name_len, name, 32013498266Sopenharmony_ci (int)value_len, value); 32113498266Sopenharmony_ci result = write_resp_raw(x->cf, x->data, name, name_len); 32213498266Sopenharmony_ci if(!result) 32313498266Sopenharmony_ci result = write_resp_raw(x->cf, x->data, ": ", 2); 32413498266Sopenharmony_ci if(!result) 32513498266Sopenharmony_ci result = write_resp_raw(x->cf, x->data, value, value_len); 32613498266Sopenharmony_ci if(!result) 32713498266Sopenharmony_ci result = write_resp_raw(x->cf, x->data, "\r\n", 2); 32813498266Sopenharmony_ci } 32913498266Sopenharmony_ci if(result) { 33013498266Sopenharmony_ci CURL_TRC_CF(x->data, x->cf, "[%"PRId64"] on header error %d", 33113498266Sopenharmony_ci stream->id, result); 33213498266Sopenharmony_ci } 33313498266Sopenharmony_ci return result; 33413498266Sopenharmony_ci} 33513498266Sopenharmony_ci 33613498266Sopenharmony_cistatic ssize_t stream_resp_read(void *reader_ctx, 33713498266Sopenharmony_ci unsigned char *buf, size_t len, 33813498266Sopenharmony_ci CURLcode *err) 33913498266Sopenharmony_ci{ 34013498266Sopenharmony_ci struct cb_ctx *x = reader_ctx; 34113498266Sopenharmony_ci struct cf_quiche_ctx *ctx = x->cf->ctx; 34213498266Sopenharmony_ci struct stream_ctx *stream = H3_STREAM_CTX(x->data); 34313498266Sopenharmony_ci ssize_t nread; 34413498266Sopenharmony_ci 34513498266Sopenharmony_ci if(!stream) { 34613498266Sopenharmony_ci *err = CURLE_RECV_ERROR; 34713498266Sopenharmony_ci return -1; 34813498266Sopenharmony_ci } 34913498266Sopenharmony_ci 35013498266Sopenharmony_ci nread = quiche_h3_recv_body(ctx->h3c, ctx->qconn, stream->id, 35113498266Sopenharmony_ci buf, len); 35213498266Sopenharmony_ci if(nread >= 0) { 35313498266Sopenharmony_ci *err = CURLE_OK; 35413498266Sopenharmony_ci return nread; 35513498266Sopenharmony_ci } 35613498266Sopenharmony_ci else { 35713498266Sopenharmony_ci *err = CURLE_AGAIN; 35813498266Sopenharmony_ci return -1; 35913498266Sopenharmony_ci } 36013498266Sopenharmony_ci} 36113498266Sopenharmony_ci 36213498266Sopenharmony_cistatic CURLcode cf_recv_body(struct Curl_cfilter *cf, 36313498266Sopenharmony_ci struct Curl_easy *data) 36413498266Sopenharmony_ci{ 36513498266Sopenharmony_ci struct stream_ctx *stream = H3_STREAM_CTX(data); 36613498266Sopenharmony_ci ssize_t nwritten; 36713498266Sopenharmony_ci struct cb_ctx cb_ctx; 36813498266Sopenharmony_ci CURLcode result = CURLE_OK; 36913498266Sopenharmony_ci 37013498266Sopenharmony_ci if(!stream) 37113498266Sopenharmony_ci return CURLE_RECV_ERROR; 37213498266Sopenharmony_ci 37313498266Sopenharmony_ci if(!stream->resp_hds_complete) { 37413498266Sopenharmony_ci result = write_resp_raw(cf, data, "\r\n", 2); 37513498266Sopenharmony_ci if(result) 37613498266Sopenharmony_ci return result; 37713498266Sopenharmony_ci stream->resp_hds_complete = TRUE; 37813498266Sopenharmony_ci } 37913498266Sopenharmony_ci 38013498266Sopenharmony_ci cb_ctx.cf = cf; 38113498266Sopenharmony_ci cb_ctx.data = data; 38213498266Sopenharmony_ci nwritten = Curl_bufq_slurp(&stream->recvbuf, 38313498266Sopenharmony_ci stream_resp_read, &cb_ctx, &result); 38413498266Sopenharmony_ci 38513498266Sopenharmony_ci if(nwritten < 0 && result != CURLE_AGAIN) { 38613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%"PRId64"] recv_body error %zd", 38713498266Sopenharmony_ci stream->id, nwritten); 38813498266Sopenharmony_ci failf(data, "Error %d in HTTP/3 response body for stream[%"PRId64"]", 38913498266Sopenharmony_ci result, stream->id); 39013498266Sopenharmony_ci stream->closed = TRUE; 39113498266Sopenharmony_ci stream->reset = TRUE; 39213498266Sopenharmony_ci stream->send_closed = TRUE; 39313498266Sopenharmony_ci streamclose(cf->conn, "Reset of stream"); 39413498266Sopenharmony_ci return result; 39513498266Sopenharmony_ci } 39613498266Sopenharmony_ci return CURLE_OK; 39713498266Sopenharmony_ci} 39813498266Sopenharmony_ci 39913498266Sopenharmony_ci#ifdef DEBUGBUILD 40013498266Sopenharmony_cistatic const char *cf_ev_name(quiche_h3_event *ev) 40113498266Sopenharmony_ci{ 40213498266Sopenharmony_ci switch(quiche_h3_event_type(ev)) { 40313498266Sopenharmony_ci case QUICHE_H3_EVENT_HEADERS: 40413498266Sopenharmony_ci return "HEADERS"; 40513498266Sopenharmony_ci case QUICHE_H3_EVENT_DATA: 40613498266Sopenharmony_ci return "DATA"; 40713498266Sopenharmony_ci case QUICHE_H3_EVENT_RESET: 40813498266Sopenharmony_ci return "RESET"; 40913498266Sopenharmony_ci case QUICHE_H3_EVENT_FINISHED: 41013498266Sopenharmony_ci return "FINISHED"; 41113498266Sopenharmony_ci case QUICHE_H3_EVENT_GOAWAY: 41213498266Sopenharmony_ci return "GOAWAY"; 41313498266Sopenharmony_ci default: 41413498266Sopenharmony_ci return "Unknown"; 41513498266Sopenharmony_ci } 41613498266Sopenharmony_ci} 41713498266Sopenharmony_ci#else 41813498266Sopenharmony_ci#define cf_ev_name(x) "" 41913498266Sopenharmony_ci#endif 42013498266Sopenharmony_ci 42113498266Sopenharmony_cistatic CURLcode h3_process_event(struct Curl_cfilter *cf, 42213498266Sopenharmony_ci struct Curl_easy *data, 42313498266Sopenharmony_ci int64_t stream3_id, 42413498266Sopenharmony_ci quiche_h3_event *ev) 42513498266Sopenharmony_ci{ 42613498266Sopenharmony_ci struct stream_ctx *stream = H3_STREAM_CTX(data); 42713498266Sopenharmony_ci struct cb_ctx cb_ctx; 42813498266Sopenharmony_ci CURLcode result = CURLE_OK; 42913498266Sopenharmony_ci int rc; 43013498266Sopenharmony_ci 43113498266Sopenharmony_ci if(!stream) 43213498266Sopenharmony_ci return CURLE_OK; 43313498266Sopenharmony_ci DEBUGASSERT(stream3_id == stream->id); 43413498266Sopenharmony_ci switch(quiche_h3_event_type(ev)) { 43513498266Sopenharmony_ci case QUICHE_H3_EVENT_HEADERS: 43613498266Sopenharmony_ci stream->resp_got_header = TRUE; 43713498266Sopenharmony_ci cb_ctx.cf = cf; 43813498266Sopenharmony_ci cb_ctx.data = data; 43913498266Sopenharmony_ci rc = quiche_h3_event_for_each_header(ev, cb_each_header, &cb_ctx); 44013498266Sopenharmony_ci if(rc) { 44113498266Sopenharmony_ci failf(data, "Error %d in HTTP/3 response header for stream[%"PRId64"]", 44213498266Sopenharmony_ci rc, stream3_id); 44313498266Sopenharmony_ci return CURLE_RECV_ERROR; 44413498266Sopenharmony_ci } 44513498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%"PRId64"] <- [HEADERS]", stream3_id); 44613498266Sopenharmony_ci break; 44713498266Sopenharmony_ci 44813498266Sopenharmony_ci case QUICHE_H3_EVENT_DATA: 44913498266Sopenharmony_ci if(!stream->closed) { 45013498266Sopenharmony_ci result = cf_recv_body(cf, data); 45113498266Sopenharmony_ci } 45213498266Sopenharmony_ci break; 45313498266Sopenharmony_ci 45413498266Sopenharmony_ci case QUICHE_H3_EVENT_RESET: 45513498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%"PRId64"] RESET", stream3_id); 45613498266Sopenharmony_ci stream->closed = TRUE; 45713498266Sopenharmony_ci stream->reset = TRUE; 45813498266Sopenharmony_ci stream->send_closed = TRUE; 45913498266Sopenharmony_ci streamclose(cf->conn, "Reset of stream"); 46013498266Sopenharmony_ci break; 46113498266Sopenharmony_ci 46213498266Sopenharmony_ci case QUICHE_H3_EVENT_FINISHED: 46313498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%"PRId64"] CLOSED", stream3_id); 46413498266Sopenharmony_ci if(!stream->resp_hds_complete) { 46513498266Sopenharmony_ci result = write_resp_raw(cf, data, "\r\n", 2); 46613498266Sopenharmony_ci if(result) 46713498266Sopenharmony_ci return result; 46813498266Sopenharmony_ci stream->resp_hds_complete = TRUE; 46913498266Sopenharmony_ci } 47013498266Sopenharmony_ci stream->closed = TRUE; 47113498266Sopenharmony_ci streamclose(cf->conn, "End of stream"); 47213498266Sopenharmony_ci break; 47313498266Sopenharmony_ci 47413498266Sopenharmony_ci case QUICHE_H3_EVENT_GOAWAY: 47513498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%"PRId64"] <- [GOAWAY]", stream3_id); 47613498266Sopenharmony_ci break; 47713498266Sopenharmony_ci 47813498266Sopenharmony_ci default: 47913498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%"PRId64"] recv, unhandled event %d", 48013498266Sopenharmony_ci stream3_id, quiche_h3_event_type(ev)); 48113498266Sopenharmony_ci break; 48213498266Sopenharmony_ci } 48313498266Sopenharmony_ci return result; 48413498266Sopenharmony_ci} 48513498266Sopenharmony_ci 48613498266Sopenharmony_cistatic CURLcode cf_poll_events(struct Curl_cfilter *cf, 48713498266Sopenharmony_ci struct Curl_easy *data) 48813498266Sopenharmony_ci{ 48913498266Sopenharmony_ci struct cf_quiche_ctx *ctx = cf->ctx; 49013498266Sopenharmony_ci struct stream_ctx *stream = H3_STREAM_CTX(data); 49113498266Sopenharmony_ci struct Curl_easy *sdata; 49213498266Sopenharmony_ci quiche_h3_event *ev; 49313498266Sopenharmony_ci CURLcode result; 49413498266Sopenharmony_ci 49513498266Sopenharmony_ci /* Take in the events and distribute them to the transfers. */ 49613498266Sopenharmony_ci while(ctx->h3c) { 49713498266Sopenharmony_ci int64_t stream3_id = quiche_h3_conn_poll(ctx->h3c, ctx->qconn, &ev); 49813498266Sopenharmony_ci if(stream3_id == QUICHE_H3_ERR_DONE) { 49913498266Sopenharmony_ci break; 50013498266Sopenharmony_ci } 50113498266Sopenharmony_ci else if(stream3_id < 0) { 50213498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%"PRId64"] error poll: %"PRId64, 50313498266Sopenharmony_ci stream? stream->id : -1, stream3_id); 50413498266Sopenharmony_ci return CURLE_HTTP3; 50513498266Sopenharmony_ci } 50613498266Sopenharmony_ci 50713498266Sopenharmony_ci sdata = get_stream_easy(cf, data, stream3_id); 50813498266Sopenharmony_ci if(!sdata) { 50913498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%"PRId64"] discard event %s for " 51013498266Sopenharmony_ci "unknown [%"PRId64"]", 51113498266Sopenharmony_ci stream? stream->id : -1, cf_ev_name(ev), stream3_id); 51213498266Sopenharmony_ci } 51313498266Sopenharmony_ci else { 51413498266Sopenharmony_ci result = h3_process_event(cf, sdata, stream3_id, ev); 51513498266Sopenharmony_ci drain_stream(cf, sdata); 51613498266Sopenharmony_ci if(result) { 51713498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%"PRId64"] error processing event %s " 51813498266Sopenharmony_ci "for [%"PRId64"] -> %d", 51913498266Sopenharmony_ci stream? stream->id : -1, cf_ev_name(ev), 52013498266Sopenharmony_ci stream3_id, result); 52113498266Sopenharmony_ci if(data == sdata) { 52213498266Sopenharmony_ci /* Only report this error to the caller if it is about the 52313498266Sopenharmony_ci * transfer we were called with. Otherwise we fail a transfer 52413498266Sopenharmony_ci * due to a problem in another one. */ 52513498266Sopenharmony_ci quiche_h3_event_free(ev); 52613498266Sopenharmony_ci return result; 52713498266Sopenharmony_ci } 52813498266Sopenharmony_ci } 52913498266Sopenharmony_ci quiche_h3_event_free(ev); 53013498266Sopenharmony_ci } 53113498266Sopenharmony_ci } 53213498266Sopenharmony_ci return CURLE_OK; 53313498266Sopenharmony_ci} 53413498266Sopenharmony_ci 53513498266Sopenharmony_cistruct recv_ctx { 53613498266Sopenharmony_ci struct Curl_cfilter *cf; 53713498266Sopenharmony_ci struct Curl_easy *data; 53813498266Sopenharmony_ci int pkts; 53913498266Sopenharmony_ci}; 54013498266Sopenharmony_ci 54113498266Sopenharmony_cistatic CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen, 54213498266Sopenharmony_ci struct sockaddr_storage *remote_addr, 54313498266Sopenharmony_ci socklen_t remote_addrlen, int ecn, 54413498266Sopenharmony_ci void *userp) 54513498266Sopenharmony_ci{ 54613498266Sopenharmony_ci struct recv_ctx *r = userp; 54713498266Sopenharmony_ci struct cf_quiche_ctx *ctx = r->cf->ctx; 54813498266Sopenharmony_ci quiche_recv_info recv_info; 54913498266Sopenharmony_ci ssize_t nread; 55013498266Sopenharmony_ci 55113498266Sopenharmony_ci (void)ecn; 55213498266Sopenharmony_ci ++r->pkts; 55313498266Sopenharmony_ci 55413498266Sopenharmony_ci recv_info.to = (struct sockaddr *)&ctx->q.local_addr; 55513498266Sopenharmony_ci recv_info.to_len = ctx->q.local_addrlen; 55613498266Sopenharmony_ci recv_info.from = (struct sockaddr *)remote_addr; 55713498266Sopenharmony_ci recv_info.from_len = remote_addrlen; 55813498266Sopenharmony_ci 55913498266Sopenharmony_ci nread = quiche_conn_recv(ctx->qconn, (unsigned char *)pkt, pktlen, 56013498266Sopenharmony_ci &recv_info); 56113498266Sopenharmony_ci if(nread < 0) { 56213498266Sopenharmony_ci if(QUICHE_ERR_DONE == nread) { 56313498266Sopenharmony_ci CURL_TRC_CF(r->data, r->cf, "ingress, quiche is DONE"); 56413498266Sopenharmony_ci return CURLE_OK; 56513498266Sopenharmony_ci } 56613498266Sopenharmony_ci else if(QUICHE_ERR_TLS_FAIL == nread) { 56713498266Sopenharmony_ci long verify_ok = SSL_get_verify_result(ctx->tls.ssl); 56813498266Sopenharmony_ci if(verify_ok != X509_V_OK) { 56913498266Sopenharmony_ci failf(r->data, "SSL certificate problem: %s", 57013498266Sopenharmony_ci X509_verify_cert_error_string(verify_ok)); 57113498266Sopenharmony_ci return CURLE_PEER_FAILED_VERIFICATION; 57213498266Sopenharmony_ci } 57313498266Sopenharmony_ci } 57413498266Sopenharmony_ci else { 57513498266Sopenharmony_ci failf(r->data, "quiche_conn_recv() == %zd", nread); 57613498266Sopenharmony_ci return CURLE_RECV_ERROR; 57713498266Sopenharmony_ci } 57813498266Sopenharmony_ci } 57913498266Sopenharmony_ci else if((size_t)nread < pktlen) { 58013498266Sopenharmony_ci CURL_TRC_CF(r->data, r->cf, "ingress, quiche only read %zd/%zu bytes", 58113498266Sopenharmony_ci nread, pktlen); 58213498266Sopenharmony_ci } 58313498266Sopenharmony_ci 58413498266Sopenharmony_ci return CURLE_OK; 58513498266Sopenharmony_ci} 58613498266Sopenharmony_ci 58713498266Sopenharmony_cistatic CURLcode cf_process_ingress(struct Curl_cfilter *cf, 58813498266Sopenharmony_ci struct Curl_easy *data) 58913498266Sopenharmony_ci{ 59013498266Sopenharmony_ci struct cf_quiche_ctx *ctx = cf->ctx; 59113498266Sopenharmony_ci struct recv_ctx rctx; 59213498266Sopenharmony_ci CURLcode result; 59313498266Sopenharmony_ci 59413498266Sopenharmony_ci DEBUGASSERT(ctx->qconn); 59513498266Sopenharmony_ci result = Curl_vquic_tls_before_recv(&ctx->tls, cf, data); 59613498266Sopenharmony_ci if(result) 59713498266Sopenharmony_ci return result; 59813498266Sopenharmony_ci 59913498266Sopenharmony_ci rctx.cf = cf; 60013498266Sopenharmony_ci rctx.data = data; 60113498266Sopenharmony_ci rctx.pkts = 0; 60213498266Sopenharmony_ci 60313498266Sopenharmony_ci result = vquic_recv_packets(cf, data, &ctx->q, 1000, recv_pkt, &rctx); 60413498266Sopenharmony_ci if(result) 60513498266Sopenharmony_ci return result; 60613498266Sopenharmony_ci 60713498266Sopenharmony_ci if(rctx.pkts > 0) { 60813498266Sopenharmony_ci /* quiche digested ingress packets. It might have opened flow control 60913498266Sopenharmony_ci * windows again. */ 61013498266Sopenharmony_ci check_resumes(cf, data); 61113498266Sopenharmony_ci } 61213498266Sopenharmony_ci return cf_poll_events(cf, data); 61313498266Sopenharmony_ci} 61413498266Sopenharmony_ci 61513498266Sopenharmony_cistruct read_ctx { 61613498266Sopenharmony_ci struct Curl_cfilter *cf; 61713498266Sopenharmony_ci struct Curl_easy *data; 61813498266Sopenharmony_ci quiche_send_info send_info; 61913498266Sopenharmony_ci}; 62013498266Sopenharmony_ci 62113498266Sopenharmony_cistatic ssize_t read_pkt_to_send(void *userp, 62213498266Sopenharmony_ci unsigned char *buf, size_t buflen, 62313498266Sopenharmony_ci CURLcode *err) 62413498266Sopenharmony_ci{ 62513498266Sopenharmony_ci struct read_ctx *x = userp; 62613498266Sopenharmony_ci struct cf_quiche_ctx *ctx = x->cf->ctx; 62713498266Sopenharmony_ci ssize_t nwritten; 62813498266Sopenharmony_ci 62913498266Sopenharmony_ci nwritten = quiche_conn_send(ctx->qconn, buf, buflen, &x->send_info); 63013498266Sopenharmony_ci if(nwritten == QUICHE_ERR_DONE) { 63113498266Sopenharmony_ci *err = CURLE_AGAIN; 63213498266Sopenharmony_ci return -1; 63313498266Sopenharmony_ci } 63413498266Sopenharmony_ci 63513498266Sopenharmony_ci if(nwritten < 0) { 63613498266Sopenharmony_ci failf(x->data, "quiche_conn_send returned %zd", nwritten); 63713498266Sopenharmony_ci *err = CURLE_SEND_ERROR; 63813498266Sopenharmony_ci return -1; 63913498266Sopenharmony_ci } 64013498266Sopenharmony_ci *err = CURLE_OK; 64113498266Sopenharmony_ci return nwritten; 64213498266Sopenharmony_ci} 64313498266Sopenharmony_ci 64413498266Sopenharmony_ci/* 64513498266Sopenharmony_ci * flush_egress drains the buffers and sends off data. 64613498266Sopenharmony_ci * Calls failf() on errors. 64713498266Sopenharmony_ci */ 64813498266Sopenharmony_cistatic CURLcode cf_flush_egress(struct Curl_cfilter *cf, 64913498266Sopenharmony_ci struct Curl_easy *data) 65013498266Sopenharmony_ci{ 65113498266Sopenharmony_ci struct cf_quiche_ctx *ctx = cf->ctx; 65213498266Sopenharmony_ci ssize_t nread; 65313498266Sopenharmony_ci CURLcode result; 65413498266Sopenharmony_ci int64_t expiry_ns; 65513498266Sopenharmony_ci int64_t timeout_ns; 65613498266Sopenharmony_ci struct read_ctx readx; 65713498266Sopenharmony_ci size_t pkt_count, gsolen; 65813498266Sopenharmony_ci 65913498266Sopenharmony_ci expiry_ns = quiche_conn_timeout_as_nanos(ctx->qconn); 66013498266Sopenharmony_ci if(!expiry_ns) { 66113498266Sopenharmony_ci quiche_conn_on_timeout(ctx->qconn); 66213498266Sopenharmony_ci if(quiche_conn_is_closed(ctx->qconn)) { 66313498266Sopenharmony_ci failf(data, "quiche_conn_on_timeout closed the connection"); 66413498266Sopenharmony_ci return CURLE_SEND_ERROR; 66513498266Sopenharmony_ci } 66613498266Sopenharmony_ci } 66713498266Sopenharmony_ci 66813498266Sopenharmony_ci result = vquic_flush(cf, data, &ctx->q); 66913498266Sopenharmony_ci if(result) { 67013498266Sopenharmony_ci if(result == CURLE_AGAIN) { 67113498266Sopenharmony_ci Curl_expire(data, 1, EXPIRE_QUIC); 67213498266Sopenharmony_ci return CURLE_OK; 67313498266Sopenharmony_ci } 67413498266Sopenharmony_ci return result; 67513498266Sopenharmony_ci } 67613498266Sopenharmony_ci 67713498266Sopenharmony_ci readx.cf = cf; 67813498266Sopenharmony_ci readx.data = data; 67913498266Sopenharmony_ci memset(&readx.send_info, 0, sizeof(readx.send_info)); 68013498266Sopenharmony_ci pkt_count = 0; 68113498266Sopenharmony_ci gsolen = quiche_conn_max_send_udp_payload_size(ctx->qconn); 68213498266Sopenharmony_ci for(;;) { 68313498266Sopenharmony_ci /* add the next packet to send, if any, to our buffer */ 68413498266Sopenharmony_ci nread = Curl_bufq_sipn(&ctx->q.sendbuf, 0, 68513498266Sopenharmony_ci read_pkt_to_send, &readx, &result); 68613498266Sopenharmony_ci if(nread < 0) { 68713498266Sopenharmony_ci if(result != CURLE_AGAIN) 68813498266Sopenharmony_ci return result; 68913498266Sopenharmony_ci /* Nothing more to add, flush and leave */ 69013498266Sopenharmony_ci result = vquic_send(cf, data, &ctx->q, gsolen); 69113498266Sopenharmony_ci if(result) { 69213498266Sopenharmony_ci if(result == CURLE_AGAIN) { 69313498266Sopenharmony_ci Curl_expire(data, 1, EXPIRE_QUIC); 69413498266Sopenharmony_ci return CURLE_OK; 69513498266Sopenharmony_ci } 69613498266Sopenharmony_ci return result; 69713498266Sopenharmony_ci } 69813498266Sopenharmony_ci goto out; 69913498266Sopenharmony_ci } 70013498266Sopenharmony_ci 70113498266Sopenharmony_ci ++pkt_count; 70213498266Sopenharmony_ci if((size_t)nread < gsolen || pkt_count >= MAX_PKT_BURST) { 70313498266Sopenharmony_ci result = vquic_send(cf, data, &ctx->q, gsolen); 70413498266Sopenharmony_ci if(result) { 70513498266Sopenharmony_ci if(result == CURLE_AGAIN) { 70613498266Sopenharmony_ci Curl_expire(data, 1, EXPIRE_QUIC); 70713498266Sopenharmony_ci return CURLE_OK; 70813498266Sopenharmony_ci } 70913498266Sopenharmony_ci goto out; 71013498266Sopenharmony_ci } 71113498266Sopenharmony_ci pkt_count = 0; 71213498266Sopenharmony_ci } 71313498266Sopenharmony_ci } 71413498266Sopenharmony_ci 71513498266Sopenharmony_ciout: 71613498266Sopenharmony_ci timeout_ns = quiche_conn_timeout_as_nanos(ctx->qconn); 71713498266Sopenharmony_ci if(timeout_ns % 1000000) 71813498266Sopenharmony_ci timeout_ns += 1000000; 71913498266Sopenharmony_ci /* expire resolution is milliseconds */ 72013498266Sopenharmony_ci Curl_expire(data, (timeout_ns / 1000000), EXPIRE_QUIC); 72113498266Sopenharmony_ci return result; 72213498266Sopenharmony_ci} 72313498266Sopenharmony_ci 72413498266Sopenharmony_cistatic ssize_t recv_closed_stream(struct Curl_cfilter *cf, 72513498266Sopenharmony_ci struct Curl_easy *data, 72613498266Sopenharmony_ci CURLcode *err) 72713498266Sopenharmony_ci{ 72813498266Sopenharmony_ci struct stream_ctx *stream = H3_STREAM_CTX(data); 72913498266Sopenharmony_ci ssize_t nread = -1; 73013498266Sopenharmony_ci 73113498266Sopenharmony_ci DEBUGASSERT(stream); 73213498266Sopenharmony_ci if(stream->reset) { 73313498266Sopenharmony_ci failf(data, 73413498266Sopenharmony_ci "HTTP/3 stream %" PRId64 " reset by server", stream->id); 73513498266Sopenharmony_ci *err = stream->resp_got_header? CURLE_PARTIAL_FILE : CURLE_HTTP3; 73613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv, was reset -> %d", 73713498266Sopenharmony_ci stream->id, *err); 73813498266Sopenharmony_ci } 73913498266Sopenharmony_ci else if(!stream->resp_got_header) { 74013498266Sopenharmony_ci failf(data, 74113498266Sopenharmony_ci "HTTP/3 stream %" PRId64 " was closed cleanly, but before getting" 74213498266Sopenharmony_ci " all response header fields, treated as error", 74313498266Sopenharmony_ci stream->id); 74413498266Sopenharmony_ci /* *err = CURLE_PARTIAL_FILE; */ 74513498266Sopenharmony_ci *err = CURLE_HTTP3; 74613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv, closed incomplete" 74713498266Sopenharmony_ci " -> %d", stream->id, *err); 74813498266Sopenharmony_ci } 74913498266Sopenharmony_ci else { 75013498266Sopenharmony_ci *err = CURLE_OK; 75113498266Sopenharmony_ci nread = 0; 75213498266Sopenharmony_ci } 75313498266Sopenharmony_ci return nread; 75413498266Sopenharmony_ci} 75513498266Sopenharmony_ci 75613498266Sopenharmony_cistatic ssize_t cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data, 75713498266Sopenharmony_ci char *buf, size_t len, CURLcode *err) 75813498266Sopenharmony_ci{ 75913498266Sopenharmony_ci struct cf_quiche_ctx *ctx = cf->ctx; 76013498266Sopenharmony_ci struct stream_ctx *stream = H3_STREAM_CTX(data); 76113498266Sopenharmony_ci ssize_t nread = -1; 76213498266Sopenharmony_ci CURLcode result; 76313498266Sopenharmony_ci 76413498266Sopenharmony_ci vquic_ctx_update_time(&ctx->q); 76513498266Sopenharmony_ci 76613498266Sopenharmony_ci if(!stream) { 76713498266Sopenharmony_ci *err = CURLE_RECV_ERROR; 76813498266Sopenharmony_ci return -1; 76913498266Sopenharmony_ci } 77013498266Sopenharmony_ci 77113498266Sopenharmony_ci if(!Curl_bufq_is_empty(&stream->recvbuf)) { 77213498266Sopenharmony_ci nread = Curl_bufq_read(&stream->recvbuf, 77313498266Sopenharmony_ci (unsigned char *)buf, len, err); 77413498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) " 77513498266Sopenharmony_ci "-> %zd, %d", stream->id, len, nread, *err); 77613498266Sopenharmony_ci if(nread < 0) 77713498266Sopenharmony_ci goto out; 77813498266Sopenharmony_ci } 77913498266Sopenharmony_ci 78013498266Sopenharmony_ci if(cf_process_ingress(cf, data)) { 78113498266Sopenharmony_ci CURL_TRC_CF(data, cf, "cf_recv, error on ingress"); 78213498266Sopenharmony_ci *err = CURLE_RECV_ERROR; 78313498266Sopenharmony_ci nread = -1; 78413498266Sopenharmony_ci goto out; 78513498266Sopenharmony_ci } 78613498266Sopenharmony_ci 78713498266Sopenharmony_ci /* recvbuf had nothing before, maybe after progressing ingress? */ 78813498266Sopenharmony_ci if(nread < 0 && !Curl_bufq_is_empty(&stream->recvbuf)) { 78913498266Sopenharmony_ci nread = Curl_bufq_read(&stream->recvbuf, 79013498266Sopenharmony_ci (unsigned char *)buf, len, err); 79113498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) " 79213498266Sopenharmony_ci "-> %zd, %d", stream->id, len, nread, *err); 79313498266Sopenharmony_ci if(nread < 0) 79413498266Sopenharmony_ci goto out; 79513498266Sopenharmony_ci } 79613498266Sopenharmony_ci 79713498266Sopenharmony_ci if(nread > 0) { 79813498266Sopenharmony_ci if(stream->closed) 79913498266Sopenharmony_ci drain_stream(cf, data); 80013498266Sopenharmony_ci } 80113498266Sopenharmony_ci else { 80213498266Sopenharmony_ci if(stream->closed) { 80313498266Sopenharmony_ci nread = recv_closed_stream(cf, data, err); 80413498266Sopenharmony_ci goto out; 80513498266Sopenharmony_ci } 80613498266Sopenharmony_ci else if(quiche_conn_is_draining(ctx->qconn)) { 80713498266Sopenharmony_ci failf(data, "QUIC connection is draining"); 80813498266Sopenharmony_ci *err = CURLE_HTTP3; 80913498266Sopenharmony_ci nread = -1; 81013498266Sopenharmony_ci goto out; 81113498266Sopenharmony_ci } 81213498266Sopenharmony_ci *err = CURLE_AGAIN; 81313498266Sopenharmony_ci nread = -1; 81413498266Sopenharmony_ci } 81513498266Sopenharmony_ci 81613498266Sopenharmony_ciout: 81713498266Sopenharmony_ci result = cf_flush_egress(cf, data); 81813498266Sopenharmony_ci if(result) { 81913498266Sopenharmony_ci CURL_TRC_CF(data, cf, "cf_recv, flush egress failed"); 82013498266Sopenharmony_ci *err = result; 82113498266Sopenharmony_ci nread = -1; 82213498266Sopenharmony_ci } 82313498266Sopenharmony_ci if(nread > 0) 82413498266Sopenharmony_ci ctx->data_recvd += nread; 82513498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%"PRId64"] cf_recv(total=%" 82613498266Sopenharmony_ci CURL_FORMAT_CURL_OFF_T ") -> %zd, %d", 82713498266Sopenharmony_ci stream->id, ctx->data_recvd, nread, *err); 82813498266Sopenharmony_ci return nread; 82913498266Sopenharmony_ci} 83013498266Sopenharmony_ci 83113498266Sopenharmony_ci/* Index where :authority header field will appear in request header 83213498266Sopenharmony_ci field list. */ 83313498266Sopenharmony_ci#define AUTHORITY_DST_IDX 3 83413498266Sopenharmony_ci 83513498266Sopenharmony_cistatic ssize_t h3_open_stream(struct Curl_cfilter *cf, 83613498266Sopenharmony_ci struct Curl_easy *data, 83713498266Sopenharmony_ci const void *buf, size_t len, 83813498266Sopenharmony_ci CURLcode *err) 83913498266Sopenharmony_ci{ 84013498266Sopenharmony_ci struct cf_quiche_ctx *ctx = cf->ctx; 84113498266Sopenharmony_ci struct stream_ctx *stream = H3_STREAM_CTX(data); 84213498266Sopenharmony_ci size_t nheader, i; 84313498266Sopenharmony_ci int64_t stream3_id; 84413498266Sopenharmony_ci struct dynhds h2_headers; 84513498266Sopenharmony_ci quiche_h3_header *nva = NULL; 84613498266Sopenharmony_ci ssize_t nwritten; 84713498266Sopenharmony_ci 84813498266Sopenharmony_ci if(!stream) { 84913498266Sopenharmony_ci *err = h3_data_setup(cf, data); 85013498266Sopenharmony_ci if(*err) { 85113498266Sopenharmony_ci return -1; 85213498266Sopenharmony_ci } 85313498266Sopenharmony_ci stream = H3_STREAM_CTX(data); 85413498266Sopenharmony_ci DEBUGASSERT(stream); 85513498266Sopenharmony_ci } 85613498266Sopenharmony_ci 85713498266Sopenharmony_ci Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); 85813498266Sopenharmony_ci 85913498266Sopenharmony_ci DEBUGASSERT(stream); 86013498266Sopenharmony_ci nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err); 86113498266Sopenharmony_ci if(nwritten < 0) 86213498266Sopenharmony_ci goto out; 86313498266Sopenharmony_ci if(!stream->h1.done) { 86413498266Sopenharmony_ci /* need more data */ 86513498266Sopenharmony_ci goto out; 86613498266Sopenharmony_ci } 86713498266Sopenharmony_ci DEBUGASSERT(stream->h1.req); 86813498266Sopenharmony_ci 86913498266Sopenharmony_ci *err = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data); 87013498266Sopenharmony_ci if(*err) { 87113498266Sopenharmony_ci nwritten = -1; 87213498266Sopenharmony_ci goto out; 87313498266Sopenharmony_ci } 87413498266Sopenharmony_ci /* no longer needed */ 87513498266Sopenharmony_ci Curl_h1_req_parse_free(&stream->h1); 87613498266Sopenharmony_ci 87713498266Sopenharmony_ci nheader = Curl_dynhds_count(&h2_headers); 87813498266Sopenharmony_ci nva = malloc(sizeof(quiche_h3_header) * nheader); 87913498266Sopenharmony_ci if(!nva) { 88013498266Sopenharmony_ci *err = CURLE_OUT_OF_MEMORY; 88113498266Sopenharmony_ci nwritten = -1; 88213498266Sopenharmony_ci goto out; 88313498266Sopenharmony_ci } 88413498266Sopenharmony_ci 88513498266Sopenharmony_ci for(i = 0; i < nheader; ++i) { 88613498266Sopenharmony_ci struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i); 88713498266Sopenharmony_ci nva[i].name = (unsigned char *)e->name; 88813498266Sopenharmony_ci nva[i].name_len = e->namelen; 88913498266Sopenharmony_ci nva[i].value = (unsigned char *)e->value; 89013498266Sopenharmony_ci nva[i].value_len = e->valuelen; 89113498266Sopenharmony_ci } 89213498266Sopenharmony_ci 89313498266Sopenharmony_ci switch(data->state.httpreq) { 89413498266Sopenharmony_ci case HTTPREQ_POST: 89513498266Sopenharmony_ci case HTTPREQ_POST_FORM: 89613498266Sopenharmony_ci case HTTPREQ_POST_MIME: 89713498266Sopenharmony_ci case HTTPREQ_PUT: 89813498266Sopenharmony_ci if(data->state.infilesize != -1) 89913498266Sopenharmony_ci stream->upload_left = data->state.infilesize; 90013498266Sopenharmony_ci else 90113498266Sopenharmony_ci /* data sending without specifying the data amount up front */ 90213498266Sopenharmony_ci stream->upload_left = -1; /* unknown */ 90313498266Sopenharmony_ci break; 90413498266Sopenharmony_ci default: 90513498266Sopenharmony_ci stream->upload_left = 0; /* no request body */ 90613498266Sopenharmony_ci break; 90713498266Sopenharmony_ci } 90813498266Sopenharmony_ci 90913498266Sopenharmony_ci if(stream->upload_left == 0) 91013498266Sopenharmony_ci stream->send_closed = TRUE; 91113498266Sopenharmony_ci 91213498266Sopenharmony_ci stream3_id = quiche_h3_send_request(ctx->h3c, ctx->qconn, nva, nheader, 91313498266Sopenharmony_ci stream->send_closed); 91413498266Sopenharmony_ci if(stream3_id < 0) { 91513498266Sopenharmony_ci if(QUICHE_H3_ERR_STREAM_BLOCKED == stream3_id) { 91613498266Sopenharmony_ci /* quiche seems to report this error if the connection window is 91713498266Sopenharmony_ci * exhausted. Which happens frequently and intermittent. */ 91813498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%"PRId64"] blocked", stream->id); 91913498266Sopenharmony_ci stream->quic_flow_blocked = TRUE; 92013498266Sopenharmony_ci *err = CURLE_AGAIN; 92113498266Sopenharmony_ci nwritten = -1; 92213498266Sopenharmony_ci goto out; 92313498266Sopenharmony_ci } 92413498266Sopenharmony_ci else { 92513498266Sopenharmony_ci CURL_TRC_CF(data, cf, "send_request(%s) -> %" PRId64, 92613498266Sopenharmony_ci data->state.url, stream3_id); 92713498266Sopenharmony_ci } 92813498266Sopenharmony_ci *err = CURLE_SEND_ERROR; 92913498266Sopenharmony_ci nwritten = -1; 93013498266Sopenharmony_ci goto out; 93113498266Sopenharmony_ci } 93213498266Sopenharmony_ci 93313498266Sopenharmony_ci DEBUGASSERT(stream->id == -1); 93413498266Sopenharmony_ci *err = CURLE_OK; 93513498266Sopenharmony_ci stream->id = stream3_id; 93613498266Sopenharmony_ci stream->closed = FALSE; 93713498266Sopenharmony_ci stream->reset = FALSE; 93813498266Sopenharmony_ci 93913498266Sopenharmony_ci if(Curl_trc_is_verbose(data)) { 94013498266Sopenharmony_ci infof(data, "[HTTP/3] [%" PRId64 "] OPENED stream for %s", 94113498266Sopenharmony_ci stream->id, data->state.url); 94213498266Sopenharmony_ci for(i = 0; i < nheader; ++i) { 94313498266Sopenharmony_ci infof(data, "[HTTP/3] [%" PRId64 "] [%.*s: %.*s]", stream->id, 94413498266Sopenharmony_ci (int)nva[i].name_len, nva[i].name, 94513498266Sopenharmony_ci (int)nva[i].value_len, nva[i].value); 94613498266Sopenharmony_ci } 94713498266Sopenharmony_ci } 94813498266Sopenharmony_ci 94913498266Sopenharmony_ciout: 95013498266Sopenharmony_ci free(nva); 95113498266Sopenharmony_ci Curl_dynhds_free(&h2_headers); 95213498266Sopenharmony_ci return nwritten; 95313498266Sopenharmony_ci} 95413498266Sopenharmony_ci 95513498266Sopenharmony_cistatic ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, 95613498266Sopenharmony_ci const void *buf, size_t len, CURLcode *err) 95713498266Sopenharmony_ci{ 95813498266Sopenharmony_ci struct cf_quiche_ctx *ctx = cf->ctx; 95913498266Sopenharmony_ci struct stream_ctx *stream = H3_STREAM_CTX(data); 96013498266Sopenharmony_ci CURLcode result; 96113498266Sopenharmony_ci ssize_t nwritten; 96213498266Sopenharmony_ci 96313498266Sopenharmony_ci vquic_ctx_update_time(&ctx->q); 96413498266Sopenharmony_ci 96513498266Sopenharmony_ci *err = cf_process_ingress(cf, data); 96613498266Sopenharmony_ci if(*err) { 96713498266Sopenharmony_ci nwritten = -1; 96813498266Sopenharmony_ci goto out; 96913498266Sopenharmony_ci } 97013498266Sopenharmony_ci 97113498266Sopenharmony_ci if(!stream || stream->id < 0) { 97213498266Sopenharmony_ci nwritten = h3_open_stream(cf, data, buf, len, err); 97313498266Sopenharmony_ci if(nwritten < 0) 97413498266Sopenharmony_ci goto out; 97513498266Sopenharmony_ci stream = H3_STREAM_CTX(data); 97613498266Sopenharmony_ci } 97713498266Sopenharmony_ci else if(stream->closed) { 97813498266Sopenharmony_ci if(stream->resp_hds_complete) { 97913498266Sopenharmony_ci /* sending request body on a stream that has been closed by the 98013498266Sopenharmony_ci * server. If the server has send us a final response, we should 98113498266Sopenharmony_ci * silently discard the send data. 98213498266Sopenharmony_ci * This happens for example on redirects where the server, instead 98313498266Sopenharmony_ci * of reading the full request body just closed the stream after 98413498266Sopenharmony_ci * sending the 30x response. 98513498266Sopenharmony_ci * This is sort of a race: had the transfer loop called recv first, 98613498266Sopenharmony_ci * it would see the response and stop/discard sending on its own- */ 98713498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] discarding data" 98813498266Sopenharmony_ci "on closed stream with response", stream->id); 98913498266Sopenharmony_ci *err = CURLE_OK; 99013498266Sopenharmony_ci nwritten = (ssize_t)len; 99113498266Sopenharmony_ci goto out; 99213498266Sopenharmony_ci } 99313498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " 99413498266Sopenharmony_ci "-> stream closed", stream->id, len); 99513498266Sopenharmony_ci *err = CURLE_HTTP3; 99613498266Sopenharmony_ci nwritten = -1; 99713498266Sopenharmony_ci goto out; 99813498266Sopenharmony_ci } 99913498266Sopenharmony_ci else { 100013498266Sopenharmony_ci bool eof = (stream->upload_left >= 0 && 100113498266Sopenharmony_ci (curl_off_t)len >= stream->upload_left); 100213498266Sopenharmony_ci nwritten = quiche_h3_send_body(ctx->h3c, ctx->qconn, stream->id, 100313498266Sopenharmony_ci (uint8_t *)buf, len, eof); 100413498266Sopenharmony_ci if(nwritten == QUICHE_H3_ERR_DONE || (nwritten == 0 && len > 0)) { 100513498266Sopenharmony_ci /* TODO: we seem to be blocked on flow control and should HOLD 100613498266Sopenharmony_ci * sending. But when do we open again? */ 100713498266Sopenharmony_ci if(!quiche_conn_stream_writable(ctx->qconn, stream->id, len)) { 100813498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " 100913498266Sopenharmony_ci "-> window exhausted", stream->id, len); 101013498266Sopenharmony_ci stream->quic_flow_blocked = TRUE; 101113498266Sopenharmony_ci } 101213498266Sopenharmony_ci *err = CURLE_AGAIN; 101313498266Sopenharmony_ci nwritten = -1; 101413498266Sopenharmony_ci goto out; 101513498266Sopenharmony_ci } 101613498266Sopenharmony_ci else if(nwritten == QUICHE_H3_TRANSPORT_ERR_INVALID_STREAM_STATE) { 101713498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " 101813498266Sopenharmony_ci "-> invalid stream state", stream->id, len); 101913498266Sopenharmony_ci *err = CURLE_HTTP3; 102013498266Sopenharmony_ci nwritten = -1; 102113498266Sopenharmony_ci goto out; 102213498266Sopenharmony_ci } 102313498266Sopenharmony_ci else if(nwritten == QUICHE_H3_TRANSPORT_ERR_FINAL_SIZE) { 102413498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " 102513498266Sopenharmony_ci "-> exceeds size", stream->id, len); 102613498266Sopenharmony_ci *err = CURLE_SEND_ERROR; 102713498266Sopenharmony_ci nwritten = -1; 102813498266Sopenharmony_ci goto out; 102913498266Sopenharmony_ci } 103013498266Sopenharmony_ci else if(nwritten < 0) { 103113498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " 103213498266Sopenharmony_ci "-> quiche err %zd", stream->id, len, nwritten); 103313498266Sopenharmony_ci *err = CURLE_SEND_ERROR; 103413498266Sopenharmony_ci nwritten = -1; 103513498266Sopenharmony_ci goto out; 103613498266Sopenharmony_ci } 103713498266Sopenharmony_ci else { 103813498266Sopenharmony_ci /* quiche accepted all or at least a part of the buf */ 103913498266Sopenharmony_ci if(stream->upload_left > 0) { 104013498266Sopenharmony_ci stream->upload_left = (nwritten < stream->upload_left)? 104113498266Sopenharmony_ci (stream->upload_left - nwritten) : 0; 104213498266Sopenharmony_ci } 104313498266Sopenharmony_ci if(stream->upload_left == 0) 104413498266Sopenharmony_ci stream->send_closed = TRUE; 104513498266Sopenharmony_ci 104613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] send body(len=%zu, " 104713498266Sopenharmony_ci "left=%" CURL_FORMAT_CURL_OFF_T ") -> %zd", 104813498266Sopenharmony_ci stream->id, len, stream->upload_left, nwritten); 104913498266Sopenharmony_ci *err = CURLE_OK; 105013498266Sopenharmony_ci } 105113498266Sopenharmony_ci } 105213498266Sopenharmony_ci 105313498266Sopenharmony_ciout: 105413498266Sopenharmony_ci result = cf_flush_egress(cf, data); 105513498266Sopenharmony_ci if(result) { 105613498266Sopenharmony_ci *err = result; 105713498266Sopenharmony_ci nwritten = -1; 105813498266Sopenharmony_ci } 105913498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %zd, %d", 106013498266Sopenharmony_ci stream? stream->id : -1, len, nwritten, *err); 106113498266Sopenharmony_ci return nwritten; 106213498266Sopenharmony_ci} 106313498266Sopenharmony_ci 106413498266Sopenharmony_cistatic bool stream_is_writeable(struct Curl_cfilter *cf, 106513498266Sopenharmony_ci struct Curl_easy *data) 106613498266Sopenharmony_ci{ 106713498266Sopenharmony_ci struct cf_quiche_ctx *ctx = cf->ctx; 106813498266Sopenharmony_ci struct stream_ctx *stream = H3_STREAM_CTX(data); 106913498266Sopenharmony_ci 107013498266Sopenharmony_ci return stream && (quiche_conn_stream_writable(ctx->qconn, 107113498266Sopenharmony_ci (uint64_t)stream->id, 1) > 0); 107213498266Sopenharmony_ci} 107313498266Sopenharmony_ci 107413498266Sopenharmony_cistatic void cf_quiche_adjust_pollset(struct Curl_cfilter *cf, 107513498266Sopenharmony_ci struct Curl_easy *data, 107613498266Sopenharmony_ci struct easy_pollset *ps) 107713498266Sopenharmony_ci{ 107813498266Sopenharmony_ci struct cf_quiche_ctx *ctx = cf->ctx; 107913498266Sopenharmony_ci bool want_recv, want_send; 108013498266Sopenharmony_ci 108113498266Sopenharmony_ci if(!ctx->qconn) 108213498266Sopenharmony_ci return; 108313498266Sopenharmony_ci 108413498266Sopenharmony_ci Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send); 108513498266Sopenharmony_ci if(want_recv || want_send) { 108613498266Sopenharmony_ci struct stream_ctx *stream = H3_STREAM_CTX(data); 108713498266Sopenharmony_ci bool c_exhaust, s_exhaust; 108813498266Sopenharmony_ci 108913498266Sopenharmony_ci c_exhaust = FALSE; /* Have not found any call in quiche that tells 109013498266Sopenharmony_ci us if the connection itself is blocked */ 109113498266Sopenharmony_ci s_exhaust = want_send && stream && stream->id >= 0 && 109213498266Sopenharmony_ci (stream->quic_flow_blocked || !stream_is_writeable(cf, data)); 109313498266Sopenharmony_ci want_recv = (want_recv || c_exhaust || s_exhaust); 109413498266Sopenharmony_ci want_send = (!s_exhaust && want_send) || 109513498266Sopenharmony_ci !Curl_bufq_is_empty(&ctx->q.sendbuf); 109613498266Sopenharmony_ci 109713498266Sopenharmony_ci Curl_pollset_set(data, ps, ctx->q.sockfd, want_recv, want_send); 109813498266Sopenharmony_ci } 109913498266Sopenharmony_ci} 110013498266Sopenharmony_ci 110113498266Sopenharmony_ci/* 110213498266Sopenharmony_ci * Called from transfer.c:data_pending to know if we should keep looping 110313498266Sopenharmony_ci * to receive more data from the connection. 110413498266Sopenharmony_ci */ 110513498266Sopenharmony_cistatic bool cf_quiche_data_pending(struct Curl_cfilter *cf, 110613498266Sopenharmony_ci const struct Curl_easy *data) 110713498266Sopenharmony_ci{ 110813498266Sopenharmony_ci const struct stream_ctx *stream = H3_STREAM_CTX(data); 110913498266Sopenharmony_ci (void)cf; 111013498266Sopenharmony_ci return stream && !Curl_bufq_is_empty(&stream->recvbuf); 111113498266Sopenharmony_ci} 111213498266Sopenharmony_ci 111313498266Sopenharmony_cistatic CURLcode h3_data_pause(struct Curl_cfilter *cf, 111413498266Sopenharmony_ci struct Curl_easy *data, 111513498266Sopenharmony_ci bool pause) 111613498266Sopenharmony_ci{ 111713498266Sopenharmony_ci /* TODO: there seems right now no API in quiche to shrink/enlarge 111813498266Sopenharmony_ci * the streams windows. As we do in HTTP/2. */ 111913498266Sopenharmony_ci if(!pause) { 112013498266Sopenharmony_ci drain_stream(cf, data); 112113498266Sopenharmony_ci Curl_expire(data, 0, EXPIRE_RUN_NOW); 112213498266Sopenharmony_ci } 112313498266Sopenharmony_ci return CURLE_OK; 112413498266Sopenharmony_ci} 112513498266Sopenharmony_ci 112613498266Sopenharmony_cistatic CURLcode cf_quiche_data_event(struct Curl_cfilter *cf, 112713498266Sopenharmony_ci struct Curl_easy *data, 112813498266Sopenharmony_ci int event, int arg1, void *arg2) 112913498266Sopenharmony_ci{ 113013498266Sopenharmony_ci CURLcode result = CURLE_OK; 113113498266Sopenharmony_ci 113213498266Sopenharmony_ci (void)arg1; 113313498266Sopenharmony_ci (void)arg2; 113413498266Sopenharmony_ci switch(event) { 113513498266Sopenharmony_ci case CF_CTRL_DATA_SETUP: 113613498266Sopenharmony_ci break; 113713498266Sopenharmony_ci case CF_CTRL_DATA_PAUSE: 113813498266Sopenharmony_ci result = h3_data_pause(cf, data, (arg1 != 0)); 113913498266Sopenharmony_ci break; 114013498266Sopenharmony_ci case CF_CTRL_DATA_DETACH: 114113498266Sopenharmony_ci h3_data_done(cf, data); 114213498266Sopenharmony_ci break; 114313498266Sopenharmony_ci case CF_CTRL_DATA_DONE: 114413498266Sopenharmony_ci h3_data_done(cf, data); 114513498266Sopenharmony_ci break; 114613498266Sopenharmony_ci case CF_CTRL_DATA_DONE_SEND: { 114713498266Sopenharmony_ci struct stream_ctx *stream = H3_STREAM_CTX(data); 114813498266Sopenharmony_ci if(stream && !stream->send_closed) { 114913498266Sopenharmony_ci unsigned char body[1]; 115013498266Sopenharmony_ci ssize_t sent; 115113498266Sopenharmony_ci 115213498266Sopenharmony_ci stream->send_closed = TRUE; 115313498266Sopenharmony_ci stream->upload_left = 0; 115413498266Sopenharmony_ci body[0] = 'X'; 115513498266Sopenharmony_ci sent = cf_quiche_send(cf, data, body, 0, &result); 115613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%"PRId64"] DONE_SEND -> %zd, %d", 115713498266Sopenharmony_ci stream->id, sent, result); 115813498266Sopenharmony_ci } 115913498266Sopenharmony_ci break; 116013498266Sopenharmony_ci } 116113498266Sopenharmony_ci case CF_CTRL_DATA_IDLE: { 116213498266Sopenharmony_ci struct stream_ctx *stream = H3_STREAM_CTX(data); 116313498266Sopenharmony_ci if(stream && !stream->closed) { 116413498266Sopenharmony_ci result = cf_flush_egress(cf, data); 116513498266Sopenharmony_ci if(result) 116613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "data idle, flush egress -> %d", result); 116713498266Sopenharmony_ci } 116813498266Sopenharmony_ci break; 116913498266Sopenharmony_ci } 117013498266Sopenharmony_ci default: 117113498266Sopenharmony_ci break; 117213498266Sopenharmony_ci } 117313498266Sopenharmony_ci return result; 117413498266Sopenharmony_ci} 117513498266Sopenharmony_ci 117613498266Sopenharmony_cistatic CURLcode cf_connect_start(struct Curl_cfilter *cf, 117713498266Sopenharmony_ci struct Curl_easy *data) 117813498266Sopenharmony_ci{ 117913498266Sopenharmony_ci struct cf_quiche_ctx *ctx = cf->ctx; 118013498266Sopenharmony_ci int rv; 118113498266Sopenharmony_ci CURLcode result; 118213498266Sopenharmony_ci const struct Curl_sockaddr_ex *sockaddr; 118313498266Sopenharmony_ci 118413498266Sopenharmony_ci DEBUGASSERT(ctx->q.sockfd != CURL_SOCKET_BAD); 118513498266Sopenharmony_ci 118613498266Sopenharmony_ci#ifdef DEBUG_QUICHE 118713498266Sopenharmony_ci /* initialize debug log callback only once */ 118813498266Sopenharmony_ci static int debug_log_init = 0; 118913498266Sopenharmony_ci if(!debug_log_init) { 119013498266Sopenharmony_ci quiche_enable_debug_logging(quiche_debug_log, NULL); 119113498266Sopenharmony_ci debug_log_init = 1; 119213498266Sopenharmony_ci } 119313498266Sopenharmony_ci#endif 119413498266Sopenharmony_ci ctx->max_idle_ms = CURL_QUIC_MAX_IDLE_MS; 119513498266Sopenharmony_ci Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, 119613498266Sopenharmony_ci H3_STREAM_POOL_SPARES); 119713498266Sopenharmony_ci ctx->data_recvd = 0; 119813498266Sopenharmony_ci 119913498266Sopenharmony_ci result = vquic_ctx_init(&ctx->q); 120013498266Sopenharmony_ci if(result) 120113498266Sopenharmony_ci return result; 120213498266Sopenharmony_ci 120313498266Sopenharmony_ci result = Curl_ssl_peer_init(&ctx->peer, cf); 120413498266Sopenharmony_ci if(result) 120513498266Sopenharmony_ci return result; 120613498266Sopenharmony_ci 120713498266Sopenharmony_ci ctx->cfg = quiche_config_new(QUICHE_PROTOCOL_VERSION); 120813498266Sopenharmony_ci if(!ctx->cfg) { 120913498266Sopenharmony_ci failf(data, "can't create quiche config"); 121013498266Sopenharmony_ci return CURLE_FAILED_INIT; 121113498266Sopenharmony_ci } 121213498266Sopenharmony_ci quiche_config_enable_pacing(ctx->cfg, false); 121313498266Sopenharmony_ci quiche_config_set_max_idle_timeout(ctx->cfg, ctx->max_idle_ms * 1000); 121413498266Sopenharmony_ci quiche_config_set_initial_max_data(ctx->cfg, (1 * 1024 * 1024) 121513498266Sopenharmony_ci /* (QUIC_MAX_STREAMS/2) * H3_STREAM_WINDOW_SIZE */); 121613498266Sopenharmony_ci quiche_config_set_initial_max_streams_bidi(ctx->cfg, QUIC_MAX_STREAMS); 121713498266Sopenharmony_ci quiche_config_set_initial_max_streams_uni(ctx->cfg, QUIC_MAX_STREAMS); 121813498266Sopenharmony_ci quiche_config_set_initial_max_stream_data_bidi_local(ctx->cfg, 121913498266Sopenharmony_ci H3_STREAM_WINDOW_SIZE); 122013498266Sopenharmony_ci quiche_config_set_initial_max_stream_data_bidi_remote(ctx->cfg, 122113498266Sopenharmony_ci H3_STREAM_WINDOW_SIZE); 122213498266Sopenharmony_ci quiche_config_set_initial_max_stream_data_uni(ctx->cfg, 122313498266Sopenharmony_ci H3_STREAM_WINDOW_SIZE); 122413498266Sopenharmony_ci quiche_config_set_disable_active_migration(ctx->cfg, TRUE); 122513498266Sopenharmony_ci 122613498266Sopenharmony_ci quiche_config_set_max_connection_window(ctx->cfg, 122713498266Sopenharmony_ci 10 * QUIC_MAX_STREAMS * H3_STREAM_WINDOW_SIZE); 122813498266Sopenharmony_ci quiche_config_set_max_stream_window(ctx->cfg, 10 * H3_STREAM_WINDOW_SIZE); 122913498266Sopenharmony_ci quiche_config_set_application_protos(ctx->cfg, 123013498266Sopenharmony_ci (uint8_t *) 123113498266Sopenharmony_ci QUICHE_H3_APPLICATION_PROTOCOL, 123213498266Sopenharmony_ci sizeof(QUICHE_H3_APPLICATION_PROTOCOL) 123313498266Sopenharmony_ci - 1); 123413498266Sopenharmony_ci 123513498266Sopenharmony_ci result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer, 123613498266Sopenharmony_ci QUICHE_H3_APPLICATION_PROTOCOL, 123713498266Sopenharmony_ci sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1, 123813498266Sopenharmony_ci NULL, cf); 123913498266Sopenharmony_ci if(result) 124013498266Sopenharmony_ci return result; 124113498266Sopenharmony_ci 124213498266Sopenharmony_ci result = Curl_rand(data, ctx->scid, sizeof(ctx->scid)); 124313498266Sopenharmony_ci if(result) 124413498266Sopenharmony_ci return result; 124513498266Sopenharmony_ci 124613498266Sopenharmony_ci Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, 124713498266Sopenharmony_ci &sockaddr, NULL, NULL, NULL, NULL); 124813498266Sopenharmony_ci ctx->q.local_addrlen = sizeof(ctx->q.local_addr); 124913498266Sopenharmony_ci rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr, 125013498266Sopenharmony_ci &ctx->q.local_addrlen); 125113498266Sopenharmony_ci if(rv == -1) 125213498266Sopenharmony_ci return CURLE_QUIC_CONNECT_ERROR; 125313498266Sopenharmony_ci 125413498266Sopenharmony_ci ctx->qconn = quiche_conn_new_with_tls((const uint8_t *)ctx->scid, 125513498266Sopenharmony_ci sizeof(ctx->scid), NULL, 0, 125613498266Sopenharmony_ci (struct sockaddr *)&ctx->q.local_addr, 125713498266Sopenharmony_ci ctx->q.local_addrlen, 125813498266Sopenharmony_ci &sockaddr->sa_addr, sockaddr->addrlen, 125913498266Sopenharmony_ci ctx->cfg, ctx->tls.ssl, false); 126013498266Sopenharmony_ci if(!ctx->qconn) { 126113498266Sopenharmony_ci failf(data, "can't create quiche connection"); 126213498266Sopenharmony_ci return CURLE_OUT_OF_MEMORY; 126313498266Sopenharmony_ci } 126413498266Sopenharmony_ci 126513498266Sopenharmony_ci /* Known to not work on Windows */ 126613498266Sopenharmony_ci#if !defined(_WIN32) && defined(HAVE_QUICHE_CONN_SET_QLOG_FD) 126713498266Sopenharmony_ci { 126813498266Sopenharmony_ci int qfd; 126913498266Sopenharmony_ci (void)Curl_qlogdir(data, ctx->scid, sizeof(ctx->scid), &qfd); 127013498266Sopenharmony_ci if(qfd != -1) 127113498266Sopenharmony_ci quiche_conn_set_qlog_fd(ctx->qconn, qfd, 127213498266Sopenharmony_ci "qlog title", "curl qlog"); 127313498266Sopenharmony_ci } 127413498266Sopenharmony_ci#endif 127513498266Sopenharmony_ci 127613498266Sopenharmony_ci result = cf_flush_egress(cf, data); 127713498266Sopenharmony_ci if(result) 127813498266Sopenharmony_ci return result; 127913498266Sopenharmony_ci 128013498266Sopenharmony_ci { 128113498266Sopenharmony_ci unsigned char alpn_protocols[] = QUICHE_H3_APPLICATION_PROTOCOL; 128213498266Sopenharmony_ci unsigned alpn_len, offset = 0; 128313498266Sopenharmony_ci 128413498266Sopenharmony_ci /* Replace each ALPN length prefix by a comma. */ 128513498266Sopenharmony_ci while(offset < sizeof(alpn_protocols) - 1) { 128613498266Sopenharmony_ci alpn_len = alpn_protocols[offset]; 128713498266Sopenharmony_ci alpn_protocols[offset] = ','; 128813498266Sopenharmony_ci offset += 1 + alpn_len; 128913498266Sopenharmony_ci } 129013498266Sopenharmony_ci 129113498266Sopenharmony_ci CURL_TRC_CF(data, cf, "Sent QUIC client Initial, ALPN: %s", 129213498266Sopenharmony_ci alpn_protocols + 1); 129313498266Sopenharmony_ci } 129413498266Sopenharmony_ci 129513498266Sopenharmony_ci return CURLE_OK; 129613498266Sopenharmony_ci} 129713498266Sopenharmony_ci 129813498266Sopenharmony_cistatic CURLcode cf_quiche_verify_peer(struct Curl_cfilter *cf, 129913498266Sopenharmony_ci struct Curl_easy *data) 130013498266Sopenharmony_ci{ 130113498266Sopenharmony_ci struct cf_quiche_ctx *ctx = cf->ctx; 130213498266Sopenharmony_ci 130313498266Sopenharmony_ci cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ 130413498266Sopenharmony_ci cf->conn->httpversion = 30; 130513498266Sopenharmony_ci cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX; 130613498266Sopenharmony_ci 130713498266Sopenharmony_ci return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer); 130813498266Sopenharmony_ci} 130913498266Sopenharmony_ci 131013498266Sopenharmony_cistatic CURLcode cf_quiche_connect(struct Curl_cfilter *cf, 131113498266Sopenharmony_ci struct Curl_easy *data, 131213498266Sopenharmony_ci bool blocking, bool *done) 131313498266Sopenharmony_ci{ 131413498266Sopenharmony_ci struct cf_quiche_ctx *ctx = cf->ctx; 131513498266Sopenharmony_ci CURLcode result = CURLE_OK; 131613498266Sopenharmony_ci 131713498266Sopenharmony_ci if(cf->connected) { 131813498266Sopenharmony_ci *done = TRUE; 131913498266Sopenharmony_ci return CURLE_OK; 132013498266Sopenharmony_ci } 132113498266Sopenharmony_ci 132213498266Sopenharmony_ci /* Connect the UDP filter first */ 132313498266Sopenharmony_ci if(!cf->next->connected) { 132413498266Sopenharmony_ci result = Curl_conn_cf_connect(cf->next, data, blocking, done); 132513498266Sopenharmony_ci if(result || !*done) 132613498266Sopenharmony_ci return result; 132713498266Sopenharmony_ci } 132813498266Sopenharmony_ci 132913498266Sopenharmony_ci *done = FALSE; 133013498266Sopenharmony_ci vquic_ctx_update_time(&ctx->q); 133113498266Sopenharmony_ci 133213498266Sopenharmony_ci if(ctx->reconnect_at.tv_sec && 133313498266Sopenharmony_ci Curl_timediff(ctx->q.last_op, ctx->reconnect_at) < 0) { 133413498266Sopenharmony_ci /* Not time yet to attempt the next connect */ 133513498266Sopenharmony_ci CURL_TRC_CF(data, cf, "waiting for reconnect time"); 133613498266Sopenharmony_ci goto out; 133713498266Sopenharmony_ci } 133813498266Sopenharmony_ci 133913498266Sopenharmony_ci if(!ctx->qconn) { 134013498266Sopenharmony_ci result = cf_connect_start(cf, data); 134113498266Sopenharmony_ci if(result) 134213498266Sopenharmony_ci goto out; 134313498266Sopenharmony_ci ctx->started_at = ctx->q.last_op; 134413498266Sopenharmony_ci result = cf_flush_egress(cf, data); 134513498266Sopenharmony_ci /* we do not expect to be able to recv anything yet */ 134613498266Sopenharmony_ci goto out; 134713498266Sopenharmony_ci } 134813498266Sopenharmony_ci 134913498266Sopenharmony_ci result = cf_process_ingress(cf, data); 135013498266Sopenharmony_ci if(result) 135113498266Sopenharmony_ci goto out; 135213498266Sopenharmony_ci 135313498266Sopenharmony_ci result = cf_flush_egress(cf, data); 135413498266Sopenharmony_ci if(result) 135513498266Sopenharmony_ci goto out; 135613498266Sopenharmony_ci 135713498266Sopenharmony_ci if(quiche_conn_is_established(ctx->qconn)) { 135813498266Sopenharmony_ci ctx->handshake_at = ctx->q.last_op; 135913498266Sopenharmony_ci CURL_TRC_CF(data, cf, "handshake complete after %dms", 136013498266Sopenharmony_ci (int)Curl_timediff(ctx->handshake_at, ctx->started_at)); 136113498266Sopenharmony_ci result = cf_quiche_verify_peer(cf, data); 136213498266Sopenharmony_ci if(!result) { 136313498266Sopenharmony_ci CURL_TRC_CF(data, cf, "peer verified"); 136413498266Sopenharmony_ci ctx->h3config = quiche_h3_config_new(); 136513498266Sopenharmony_ci if(!ctx->h3config) { 136613498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 136713498266Sopenharmony_ci goto out; 136813498266Sopenharmony_ci } 136913498266Sopenharmony_ci 137013498266Sopenharmony_ci /* Create a new HTTP/3 connection on the QUIC connection. */ 137113498266Sopenharmony_ci ctx->h3c = quiche_h3_conn_new_with_transport(ctx->qconn, ctx->h3config); 137213498266Sopenharmony_ci if(!ctx->h3c) { 137313498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 137413498266Sopenharmony_ci goto out; 137513498266Sopenharmony_ci } 137613498266Sopenharmony_ci cf->connected = TRUE; 137713498266Sopenharmony_ci cf->conn->alpn = CURL_HTTP_VERSION_3; 137813498266Sopenharmony_ci *done = TRUE; 137913498266Sopenharmony_ci connkeep(cf->conn, "HTTP/3 default"); 138013498266Sopenharmony_ci } 138113498266Sopenharmony_ci } 138213498266Sopenharmony_ci else if(quiche_conn_is_draining(ctx->qconn)) { 138313498266Sopenharmony_ci /* When a QUIC server instance is shutting down, it may send us a 138413498266Sopenharmony_ci * CONNECTION_CLOSE right away. Our connection then enters the DRAINING 138513498266Sopenharmony_ci * state. The CONNECT may work in the near future again. Indicate 138613498266Sopenharmony_ci * that as a "weird" reply. */ 138713498266Sopenharmony_ci result = CURLE_WEIRD_SERVER_REPLY; 138813498266Sopenharmony_ci } 138913498266Sopenharmony_ci 139013498266Sopenharmony_ciout: 139113498266Sopenharmony_ci#ifndef CURL_DISABLE_VERBOSE_STRINGS 139213498266Sopenharmony_ci if(result && result != CURLE_AGAIN) { 139313498266Sopenharmony_ci const char *r_ip; 139413498266Sopenharmony_ci int r_port; 139513498266Sopenharmony_ci 139613498266Sopenharmony_ci Curl_cf_socket_peek(cf->next, data, NULL, NULL, 139713498266Sopenharmony_ci &r_ip, &r_port, NULL, NULL); 139813498266Sopenharmony_ci infof(data, "connect to %s port %u failed: %s", 139913498266Sopenharmony_ci r_ip, r_port, curl_easy_strerror(result)); 140013498266Sopenharmony_ci } 140113498266Sopenharmony_ci#endif 140213498266Sopenharmony_ci return result; 140313498266Sopenharmony_ci} 140413498266Sopenharmony_ci 140513498266Sopenharmony_cistatic void cf_quiche_close(struct Curl_cfilter *cf, struct Curl_easy *data) 140613498266Sopenharmony_ci{ 140713498266Sopenharmony_ci struct cf_quiche_ctx *ctx = cf->ctx; 140813498266Sopenharmony_ci 140913498266Sopenharmony_ci if(ctx) { 141013498266Sopenharmony_ci if(ctx->qconn) { 141113498266Sopenharmony_ci vquic_ctx_update_time(&ctx->q); 141213498266Sopenharmony_ci (void)quiche_conn_close(ctx->qconn, TRUE, 0, NULL, 0); 141313498266Sopenharmony_ci /* flushing the egress is not a failsafe way to deliver all the 141413498266Sopenharmony_ci outstanding packets, but we also don't want to get stuck here... */ 141513498266Sopenharmony_ci (void)cf_flush_egress(cf, data); 141613498266Sopenharmony_ci } 141713498266Sopenharmony_ci cf_quiche_ctx_clear(ctx); 141813498266Sopenharmony_ci } 141913498266Sopenharmony_ci} 142013498266Sopenharmony_ci 142113498266Sopenharmony_cistatic void cf_quiche_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) 142213498266Sopenharmony_ci{ 142313498266Sopenharmony_ci struct cf_quiche_ctx *ctx = cf->ctx; 142413498266Sopenharmony_ci 142513498266Sopenharmony_ci (void)data; 142613498266Sopenharmony_ci cf_quiche_ctx_clear(ctx); 142713498266Sopenharmony_ci free(ctx); 142813498266Sopenharmony_ci cf->ctx = NULL; 142913498266Sopenharmony_ci} 143013498266Sopenharmony_ci 143113498266Sopenharmony_cistatic CURLcode cf_quiche_query(struct Curl_cfilter *cf, 143213498266Sopenharmony_ci struct Curl_easy *data, 143313498266Sopenharmony_ci int query, int *pres1, void *pres2) 143413498266Sopenharmony_ci{ 143513498266Sopenharmony_ci struct cf_quiche_ctx *ctx = cf->ctx; 143613498266Sopenharmony_ci 143713498266Sopenharmony_ci switch(query) { 143813498266Sopenharmony_ci case CF_QUERY_MAX_CONCURRENT: { 143913498266Sopenharmony_ci uint64_t max_streams = CONN_INUSE(cf->conn); 144013498266Sopenharmony_ci if(!ctx->goaway) { 144113498266Sopenharmony_ci max_streams += quiche_conn_peer_streams_left_bidi(ctx->qconn); 144213498266Sopenharmony_ci } 144313498266Sopenharmony_ci *pres1 = (max_streams > INT_MAX)? INT_MAX : (int)max_streams; 144413498266Sopenharmony_ci CURL_TRC_CF(data, cf, "query: MAX_CONCURRENT -> %d", *pres1); 144513498266Sopenharmony_ci return CURLE_OK; 144613498266Sopenharmony_ci } 144713498266Sopenharmony_ci case CF_QUERY_CONNECT_REPLY_MS: 144813498266Sopenharmony_ci if(ctx->q.got_first_byte) { 144913498266Sopenharmony_ci timediff_t ms = Curl_timediff(ctx->q.first_byte_at, ctx->started_at); 145013498266Sopenharmony_ci *pres1 = (ms < INT_MAX)? (int)ms : INT_MAX; 145113498266Sopenharmony_ci } 145213498266Sopenharmony_ci else 145313498266Sopenharmony_ci *pres1 = -1; 145413498266Sopenharmony_ci return CURLE_OK; 145513498266Sopenharmony_ci case CF_QUERY_TIMER_CONNECT: { 145613498266Sopenharmony_ci struct curltime *when = pres2; 145713498266Sopenharmony_ci if(ctx->q.got_first_byte) 145813498266Sopenharmony_ci *when = ctx->q.first_byte_at; 145913498266Sopenharmony_ci return CURLE_OK; 146013498266Sopenharmony_ci } 146113498266Sopenharmony_ci case CF_QUERY_TIMER_APPCONNECT: { 146213498266Sopenharmony_ci struct curltime *when = pres2; 146313498266Sopenharmony_ci if(cf->connected) 146413498266Sopenharmony_ci *when = ctx->handshake_at; 146513498266Sopenharmony_ci return CURLE_OK; 146613498266Sopenharmony_ci } 146713498266Sopenharmony_ci default: 146813498266Sopenharmony_ci break; 146913498266Sopenharmony_ci } 147013498266Sopenharmony_ci return cf->next? 147113498266Sopenharmony_ci cf->next->cft->query(cf->next, data, query, pres1, pres2) : 147213498266Sopenharmony_ci CURLE_UNKNOWN_OPTION; 147313498266Sopenharmony_ci} 147413498266Sopenharmony_ci 147513498266Sopenharmony_cistatic bool cf_quiche_conn_is_alive(struct Curl_cfilter *cf, 147613498266Sopenharmony_ci struct Curl_easy *data, 147713498266Sopenharmony_ci bool *input_pending) 147813498266Sopenharmony_ci{ 147913498266Sopenharmony_ci struct cf_quiche_ctx *ctx = cf->ctx; 148013498266Sopenharmony_ci bool alive = TRUE; 148113498266Sopenharmony_ci 148213498266Sopenharmony_ci *input_pending = FALSE; 148313498266Sopenharmony_ci if(!ctx->qconn) 148413498266Sopenharmony_ci return FALSE; 148513498266Sopenharmony_ci 148613498266Sopenharmony_ci /* Both sides of the QUIC connection announce they max idle times in 148713498266Sopenharmony_ci * the transport parameters. Look at the minimum of both and if 148813498266Sopenharmony_ci * we exceed this, regard the connection as dead. The other side 148913498266Sopenharmony_ci * may have completely purged it and will no longer respond 149013498266Sopenharmony_ci * to any packets from us. */ 149113498266Sopenharmony_ci { 149213498266Sopenharmony_ci quiche_transport_params qpeerparams; 149313498266Sopenharmony_ci timediff_t idletime; 149413498266Sopenharmony_ci uint64_t idle_ms = ctx->max_idle_ms; 149513498266Sopenharmony_ci 149613498266Sopenharmony_ci if(quiche_conn_peer_transport_params(ctx->qconn, &qpeerparams) && 149713498266Sopenharmony_ci qpeerparams.peer_max_idle_timeout && 149813498266Sopenharmony_ci qpeerparams.peer_max_idle_timeout < idle_ms) 149913498266Sopenharmony_ci idle_ms = qpeerparams.peer_max_idle_timeout; 150013498266Sopenharmony_ci idletime = Curl_timediff(Curl_now(), cf->conn->lastused); 150113498266Sopenharmony_ci if(idletime > 0 && (uint64_t)idletime > idle_ms) 150213498266Sopenharmony_ci return FALSE; 150313498266Sopenharmony_ci } 150413498266Sopenharmony_ci 150513498266Sopenharmony_ci if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending)) 150613498266Sopenharmony_ci return FALSE; 150713498266Sopenharmony_ci 150813498266Sopenharmony_ci if(*input_pending) { 150913498266Sopenharmony_ci /* This happens before we've sent off a request and the connection is 151013498266Sopenharmony_ci not in use by any other transfer, there shouldn't be any data here, 151113498266Sopenharmony_ci only "protocol frames" */ 151213498266Sopenharmony_ci *input_pending = FALSE; 151313498266Sopenharmony_ci if(cf_process_ingress(cf, data)) 151413498266Sopenharmony_ci alive = FALSE; 151513498266Sopenharmony_ci else { 151613498266Sopenharmony_ci alive = TRUE; 151713498266Sopenharmony_ci } 151813498266Sopenharmony_ci } 151913498266Sopenharmony_ci 152013498266Sopenharmony_ci return alive; 152113498266Sopenharmony_ci} 152213498266Sopenharmony_ci 152313498266Sopenharmony_cistruct Curl_cftype Curl_cft_http3 = { 152413498266Sopenharmony_ci "HTTP/3", 152513498266Sopenharmony_ci CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX, 152613498266Sopenharmony_ci 0, 152713498266Sopenharmony_ci cf_quiche_destroy, 152813498266Sopenharmony_ci cf_quiche_connect, 152913498266Sopenharmony_ci cf_quiche_close, 153013498266Sopenharmony_ci Curl_cf_def_get_host, 153113498266Sopenharmony_ci cf_quiche_adjust_pollset, 153213498266Sopenharmony_ci cf_quiche_data_pending, 153313498266Sopenharmony_ci cf_quiche_send, 153413498266Sopenharmony_ci cf_quiche_recv, 153513498266Sopenharmony_ci cf_quiche_data_event, 153613498266Sopenharmony_ci cf_quiche_conn_is_alive, 153713498266Sopenharmony_ci Curl_cf_def_conn_keep_alive, 153813498266Sopenharmony_ci cf_quiche_query, 153913498266Sopenharmony_ci}; 154013498266Sopenharmony_ci 154113498266Sopenharmony_ciCURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf, 154213498266Sopenharmony_ci struct Curl_easy *data, 154313498266Sopenharmony_ci struct connectdata *conn, 154413498266Sopenharmony_ci const struct Curl_addrinfo *ai) 154513498266Sopenharmony_ci{ 154613498266Sopenharmony_ci struct cf_quiche_ctx *ctx = NULL; 154713498266Sopenharmony_ci struct Curl_cfilter *cf = NULL, *udp_cf = NULL; 154813498266Sopenharmony_ci CURLcode result; 154913498266Sopenharmony_ci 155013498266Sopenharmony_ci (void)data; 155113498266Sopenharmony_ci (void)conn; 155213498266Sopenharmony_ci ctx = calloc(1, sizeof(*ctx)); 155313498266Sopenharmony_ci if(!ctx) { 155413498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 155513498266Sopenharmony_ci goto out; 155613498266Sopenharmony_ci } 155713498266Sopenharmony_ci 155813498266Sopenharmony_ci result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); 155913498266Sopenharmony_ci if(result) 156013498266Sopenharmony_ci goto out; 156113498266Sopenharmony_ci 156213498266Sopenharmony_ci result = Curl_cf_udp_create(&udp_cf, data, conn, ai, TRNSPRT_QUIC); 156313498266Sopenharmony_ci if(result) 156413498266Sopenharmony_ci goto out; 156513498266Sopenharmony_ci 156613498266Sopenharmony_ci udp_cf->conn = cf->conn; 156713498266Sopenharmony_ci udp_cf->sockindex = cf->sockindex; 156813498266Sopenharmony_ci cf->next = udp_cf; 156913498266Sopenharmony_ci 157013498266Sopenharmony_ciout: 157113498266Sopenharmony_ci *pcf = (!result)? cf : NULL; 157213498266Sopenharmony_ci if(result) { 157313498266Sopenharmony_ci if(udp_cf) 157413498266Sopenharmony_ci Curl_conn_cf_discard_sub(cf, udp_cf, data, TRUE); 157513498266Sopenharmony_ci Curl_safefree(cf); 157613498266Sopenharmony_ci Curl_safefree(ctx); 157713498266Sopenharmony_ci } 157813498266Sopenharmony_ci 157913498266Sopenharmony_ci return result; 158013498266Sopenharmony_ci} 158113498266Sopenharmony_ci 158213498266Sopenharmony_cibool Curl_conn_is_quiche(const struct Curl_easy *data, 158313498266Sopenharmony_ci const struct connectdata *conn, 158413498266Sopenharmony_ci int sockindex) 158513498266Sopenharmony_ci{ 158613498266Sopenharmony_ci struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL; 158713498266Sopenharmony_ci 158813498266Sopenharmony_ci (void)data; 158913498266Sopenharmony_ci for(; cf; cf = cf->next) { 159013498266Sopenharmony_ci if(cf->cft == &Curl_cft_http3) 159113498266Sopenharmony_ci return TRUE; 159213498266Sopenharmony_ci if(cf->cft->flags & CF_TYPE_IP_CONNECT) 159313498266Sopenharmony_ci return FALSE; 159413498266Sopenharmony_ci } 159513498266Sopenharmony_ci return FALSE; 159613498266Sopenharmony_ci} 159713498266Sopenharmony_ci 159813498266Sopenharmony_ci#endif 1599