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