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