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_OPENSSL_QUIC) && defined(USE_NGHTTP3)
2813498266Sopenharmony_ci
2913498266Sopenharmony_ci#include <openssl/ssl.h>
3013498266Sopenharmony_ci#include <openssl/bio.h>
3113498266Sopenharmony_ci#include <openssl/err.h>
3213498266Sopenharmony_ci#include <nghttp3/nghttp3.h>
3313498266Sopenharmony_ci
3413498266Sopenharmony_ci#include "urldata.h"
3513498266Sopenharmony_ci#include "sendf.h"
3613498266Sopenharmony_ci#include "strdup.h"
3713498266Sopenharmony_ci#include "rand.h"
3813498266Sopenharmony_ci#include "multiif.h"
3913498266Sopenharmony_ci#include "strcase.h"
4013498266Sopenharmony_ci#include "cfilters.h"
4113498266Sopenharmony_ci#include "cf-socket.h"
4213498266Sopenharmony_ci#include "connect.h"
4313498266Sopenharmony_ci#include "progress.h"
4413498266Sopenharmony_ci#include "strerror.h"
4513498266Sopenharmony_ci#include "dynbuf.h"
4613498266Sopenharmony_ci#include "http1.h"
4713498266Sopenharmony_ci#include "select.h"
4813498266Sopenharmony_ci#include "inet_pton.h"
4913498266Sopenharmony_ci#include "vquic.h"
5013498266Sopenharmony_ci#include "vquic_int.h"
5113498266Sopenharmony_ci#include "vquic-tls.h"
5213498266Sopenharmony_ci#include "vtls/keylog.h"
5313498266Sopenharmony_ci#include "vtls/vtls.h"
5413498266Sopenharmony_ci#include "vtls/openssl.h"
5513498266Sopenharmony_ci#include "curl_osslq.h"
5613498266Sopenharmony_ci
5713498266Sopenharmony_ci#include "warnless.h"
5813498266Sopenharmony_ci
5913498266Sopenharmony_ci/* The last 3 #include files should be in this order */
6013498266Sopenharmony_ci#include "curl_printf.h"
6113498266Sopenharmony_ci#include "curl_memory.h"
6213498266Sopenharmony_ci#include "memdebug.h"
6313498266Sopenharmony_ci
6413498266Sopenharmony_ci/* A stream window is the maximum amount we need to buffer for
6513498266Sopenharmony_ci * each active transfer. We use HTTP/3 flow control and only ACK
6613498266Sopenharmony_ci * when we take things out of the buffer.
6713498266Sopenharmony_ci * Chunk size is large enough to take a full DATA frame */
6813498266Sopenharmony_ci#define H3_STREAM_WINDOW_SIZE (128 * 1024)
6913498266Sopenharmony_ci#define H3_STREAM_CHUNK_SIZE   (16 * 1024)
7013498266Sopenharmony_ci/* The pool keeps spares around and half of a full stream windows
7113498266Sopenharmony_ci * seems good. More does not seem to improve performance.
7213498266Sopenharmony_ci * The benefit of the pool is that stream buffer to not keep
7313498266Sopenharmony_ci * spares. So memory consumption goes down when streams run empty,
7413498266Sopenharmony_ci * have a large upload done, etc. */
7513498266Sopenharmony_ci#define H3_STREAM_POOL_SPARES \
7613498266Sopenharmony_ci          (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE ) / 2
7713498266Sopenharmony_ci/* Receive and Send max number of chunks just follows from the
7813498266Sopenharmony_ci * chunk size and window size */
7913498266Sopenharmony_ci#define H3_STREAM_RECV_CHUNKS \
8013498266Sopenharmony_ci          (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE)
8113498266Sopenharmony_ci#define H3_STREAM_SEND_CHUNKS \
8213498266Sopenharmony_ci          (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE)
8313498266Sopenharmony_ci
8413498266Sopenharmony_ci#ifndef ARRAYSIZE
8513498266Sopenharmony_ci#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
8613498266Sopenharmony_ci#endif
8713498266Sopenharmony_ci
8813498266Sopenharmony_ci#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
8913498266Sopenharmony_citypedef uint32_t sslerr_t;
9013498266Sopenharmony_ci#else
9113498266Sopenharmony_citypedef unsigned long sslerr_t;
9213498266Sopenharmony_ci#endif
9313498266Sopenharmony_ci
9413498266Sopenharmony_ci
9513498266Sopenharmony_ci/* How to access `call_data` from a cf_osslq filter */
9613498266Sopenharmony_ci#undef CF_CTX_CALL_DATA
9713498266Sopenharmony_ci#define CF_CTX_CALL_DATA(cf)  \
9813498266Sopenharmony_ci  ((struct cf_osslq_ctx *)(cf)->ctx)->call_data
9913498266Sopenharmony_ci
10013498266Sopenharmony_cistatic CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
10113498266Sopenharmony_ci                                    struct Curl_easy *data);
10213498266Sopenharmony_ci
10313498266Sopenharmony_cistatic const char *SSL_ERROR_to_str(int err)
10413498266Sopenharmony_ci{
10513498266Sopenharmony_ci  switch(err) {
10613498266Sopenharmony_ci  case SSL_ERROR_NONE:
10713498266Sopenharmony_ci    return "SSL_ERROR_NONE";
10813498266Sopenharmony_ci  case SSL_ERROR_SSL:
10913498266Sopenharmony_ci    return "SSL_ERROR_SSL";
11013498266Sopenharmony_ci  case SSL_ERROR_WANT_READ:
11113498266Sopenharmony_ci    return "SSL_ERROR_WANT_READ";
11213498266Sopenharmony_ci  case SSL_ERROR_WANT_WRITE:
11313498266Sopenharmony_ci    return "SSL_ERROR_WANT_WRITE";
11413498266Sopenharmony_ci  case SSL_ERROR_WANT_X509_LOOKUP:
11513498266Sopenharmony_ci    return "SSL_ERROR_WANT_X509_LOOKUP";
11613498266Sopenharmony_ci  case SSL_ERROR_SYSCALL:
11713498266Sopenharmony_ci    return "SSL_ERROR_SYSCALL";
11813498266Sopenharmony_ci  case SSL_ERROR_ZERO_RETURN:
11913498266Sopenharmony_ci    return "SSL_ERROR_ZERO_RETURN";
12013498266Sopenharmony_ci  case SSL_ERROR_WANT_CONNECT:
12113498266Sopenharmony_ci    return "SSL_ERROR_WANT_CONNECT";
12213498266Sopenharmony_ci  case SSL_ERROR_WANT_ACCEPT:
12313498266Sopenharmony_ci    return "SSL_ERROR_WANT_ACCEPT";
12413498266Sopenharmony_ci#if defined(SSL_ERROR_WANT_ASYNC)
12513498266Sopenharmony_ci  case SSL_ERROR_WANT_ASYNC:
12613498266Sopenharmony_ci    return "SSL_ERROR_WANT_ASYNC";
12713498266Sopenharmony_ci#endif
12813498266Sopenharmony_ci#if defined(SSL_ERROR_WANT_ASYNC_JOB)
12913498266Sopenharmony_ci  case SSL_ERROR_WANT_ASYNC_JOB:
13013498266Sopenharmony_ci    return "SSL_ERROR_WANT_ASYNC_JOB";
13113498266Sopenharmony_ci#endif
13213498266Sopenharmony_ci#if defined(SSL_ERROR_WANT_EARLY)
13313498266Sopenharmony_ci  case SSL_ERROR_WANT_EARLY:
13413498266Sopenharmony_ci    return "SSL_ERROR_WANT_EARLY";
13513498266Sopenharmony_ci#endif
13613498266Sopenharmony_ci  default:
13713498266Sopenharmony_ci    return "SSL_ERROR unknown";
13813498266Sopenharmony_ci  }
13913498266Sopenharmony_ci}
14013498266Sopenharmony_ci
14113498266Sopenharmony_ci/* Return error string for last OpenSSL error */
14213498266Sopenharmony_cistatic char *ossl_strerror(unsigned long error, char *buf, size_t size)
14313498266Sopenharmony_ci{
14413498266Sopenharmony_ci  DEBUGASSERT(size);
14513498266Sopenharmony_ci  *buf = '\0';
14613498266Sopenharmony_ci
14713498266Sopenharmony_ci#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
14813498266Sopenharmony_ci  ERR_error_string_n((uint32_t)error, buf, size);
14913498266Sopenharmony_ci#else
15013498266Sopenharmony_ci  ERR_error_string_n(error, buf, size);
15113498266Sopenharmony_ci#endif
15213498266Sopenharmony_ci
15313498266Sopenharmony_ci  if(!*buf) {
15413498266Sopenharmony_ci    const char *msg = error ? "Unknown error" : "No error";
15513498266Sopenharmony_ci    if(strlen(msg) < size)
15613498266Sopenharmony_ci      strcpy(buf, msg);
15713498266Sopenharmony_ci  }
15813498266Sopenharmony_ci
15913498266Sopenharmony_ci  return buf;
16013498266Sopenharmony_ci}
16113498266Sopenharmony_ci
16213498266Sopenharmony_cistatic CURLcode make_bio_addr(BIO_ADDR **pbio_addr,
16313498266Sopenharmony_ci                              const struct Curl_sockaddr_ex *addr)
16413498266Sopenharmony_ci{
16513498266Sopenharmony_ci  BIO_ADDR *ba;
16613498266Sopenharmony_ci  CURLcode result = CURLE_FAILED_INIT;
16713498266Sopenharmony_ci
16813498266Sopenharmony_ci  ba = BIO_ADDR_new();
16913498266Sopenharmony_ci  if(!ba) {
17013498266Sopenharmony_ci    result = CURLE_OUT_OF_MEMORY;
17113498266Sopenharmony_ci    goto out;
17213498266Sopenharmony_ci  }
17313498266Sopenharmony_ci
17413498266Sopenharmony_ci  switch(addr->family) {
17513498266Sopenharmony_ci  case AF_INET: {
17613498266Sopenharmony_ci    struct sockaddr_in * const sin =
17713498266Sopenharmony_ci      (struct sockaddr_in * const)(void *)&addr->sa_addr;
17813498266Sopenharmony_ci    if(!BIO_ADDR_rawmake(ba, AF_INET, &sin->sin_addr,
17913498266Sopenharmony_ci                         sizeof(sin->sin_addr), sin->sin_port)) {
18013498266Sopenharmony_ci      goto out;
18113498266Sopenharmony_ci    }
18213498266Sopenharmony_ci    result = CURLE_OK;
18313498266Sopenharmony_ci    break;
18413498266Sopenharmony_ci  }
18513498266Sopenharmony_ci#ifdef ENABLE_IPV6
18613498266Sopenharmony_ci  case AF_INET6: {
18713498266Sopenharmony_ci    struct sockaddr_in6 * const sin =
18813498266Sopenharmony_ci      (struct sockaddr_in6 * const)(void *)&addr->sa_addr;
18913498266Sopenharmony_ci    if(!BIO_ADDR_rawmake(ba, AF_INET6, &sin->sin6_addr,
19013498266Sopenharmony_ci                         sizeof(sin->sin6_addr), sin->sin6_port)) {
19113498266Sopenharmony_ci    }
19213498266Sopenharmony_ci    result = CURLE_OK;
19313498266Sopenharmony_ci    break;
19413498266Sopenharmony_ci  }
19513498266Sopenharmony_ci#endif /* ENABLE_IPV6 */
19613498266Sopenharmony_ci  default:
19713498266Sopenharmony_ci    /* sunsupported */
19813498266Sopenharmony_ci    DEBUGASSERT(0);
19913498266Sopenharmony_ci    break;
20013498266Sopenharmony_ci  }
20113498266Sopenharmony_ci
20213498266Sopenharmony_ciout:
20313498266Sopenharmony_ci  if(result && ba) {
20413498266Sopenharmony_ci    BIO_ADDR_free(ba);
20513498266Sopenharmony_ci    ba = NULL;
20613498266Sopenharmony_ci  }
20713498266Sopenharmony_ci  *pbio_addr = ba;
20813498266Sopenharmony_ci  return result;
20913498266Sopenharmony_ci}
21013498266Sopenharmony_ci
21113498266Sopenharmony_ci/* QUIC stream (not necessarily H3) */
21213498266Sopenharmony_cistruct cf_osslq_stream {
21313498266Sopenharmony_ci  int64_t id;
21413498266Sopenharmony_ci  SSL *ssl;
21513498266Sopenharmony_ci  struct bufq recvbuf; /* QUIC war data recv buffer */
21613498266Sopenharmony_ci  BIT(recvd_eos);
21713498266Sopenharmony_ci  BIT(closed);
21813498266Sopenharmony_ci  BIT(reset);
21913498266Sopenharmony_ci  BIT(send_blocked);
22013498266Sopenharmony_ci};
22113498266Sopenharmony_ci
22213498266Sopenharmony_cistatic CURLcode cf_osslq_stream_open(struct cf_osslq_stream *s,
22313498266Sopenharmony_ci                                     SSL *conn,
22413498266Sopenharmony_ci                                     uint64_t flags,
22513498266Sopenharmony_ci                                     struct bufc_pool *bufcp,
22613498266Sopenharmony_ci                                     void *user_data)
22713498266Sopenharmony_ci{
22813498266Sopenharmony_ci  DEBUGASSERT(!s->ssl);
22913498266Sopenharmony_ci  Curl_bufq_initp(&s->recvbuf, bufcp, 1, BUFQ_OPT_NONE);
23013498266Sopenharmony_ci  s->ssl = SSL_new_stream(conn, flags);
23113498266Sopenharmony_ci  if(!s->ssl) {
23213498266Sopenharmony_ci    return CURLE_FAILED_INIT;
23313498266Sopenharmony_ci  }
23413498266Sopenharmony_ci  s->id = SSL_get_stream_id(s->ssl);
23513498266Sopenharmony_ci  SSL_set_app_data(s->ssl, user_data);
23613498266Sopenharmony_ci  return CURLE_OK;
23713498266Sopenharmony_ci}
23813498266Sopenharmony_ci
23913498266Sopenharmony_cistatic void cf_osslq_stream_cleanup(struct cf_osslq_stream *s)
24013498266Sopenharmony_ci{
24113498266Sopenharmony_ci  if(s->ssl) {
24213498266Sopenharmony_ci    SSL_set_app_data(s->ssl, NULL);
24313498266Sopenharmony_ci    SSL_free(s->ssl);
24413498266Sopenharmony_ci  }
24513498266Sopenharmony_ci  Curl_bufq_free(&s->recvbuf);
24613498266Sopenharmony_ci  memset(s, 0, sizeof(*s));
24713498266Sopenharmony_ci}
24813498266Sopenharmony_ci
24913498266Sopenharmony_cistatic void cf_osslq_stream_close(struct cf_osslq_stream *s)
25013498266Sopenharmony_ci{
25113498266Sopenharmony_ci  if(s->ssl) {
25213498266Sopenharmony_ci    SSL_free(s->ssl);
25313498266Sopenharmony_ci    s->ssl = NULL;
25413498266Sopenharmony_ci  }
25513498266Sopenharmony_ci}
25613498266Sopenharmony_ci
25713498266Sopenharmony_cistruct cf_osslq_h3conn {
25813498266Sopenharmony_ci  nghttp3_conn *conn;
25913498266Sopenharmony_ci  nghttp3_settings settings;
26013498266Sopenharmony_ci  struct cf_osslq_stream s_ctrl;
26113498266Sopenharmony_ci  struct cf_osslq_stream s_qpack_enc;
26213498266Sopenharmony_ci  struct cf_osslq_stream s_qpack_dec;
26313498266Sopenharmony_ci  struct cf_osslq_stream remote_ctrl[3]; /* uni streams opened by the peer */
26413498266Sopenharmony_ci  size_t remote_ctrl_n; /* number of peer streams opened */
26513498266Sopenharmony_ci};
26613498266Sopenharmony_ci
26713498266Sopenharmony_cistatic void cf_osslq_h3conn_cleanup(struct cf_osslq_h3conn *h3)
26813498266Sopenharmony_ci{
26913498266Sopenharmony_ci  size_t i;
27013498266Sopenharmony_ci
27113498266Sopenharmony_ci  if(h3->conn)
27213498266Sopenharmony_ci    nghttp3_conn_del(h3->conn);
27313498266Sopenharmony_ci  cf_osslq_stream_cleanup(&h3->s_ctrl);
27413498266Sopenharmony_ci  cf_osslq_stream_cleanup(&h3->s_qpack_enc);
27513498266Sopenharmony_ci  cf_osslq_stream_cleanup(&h3->s_qpack_dec);
27613498266Sopenharmony_ci  for(i = 0; i < h3->remote_ctrl_n; ++i) {
27713498266Sopenharmony_ci    cf_osslq_stream_cleanup(&h3->remote_ctrl[i]);
27813498266Sopenharmony_ci  }
27913498266Sopenharmony_ci}
28013498266Sopenharmony_ci
28113498266Sopenharmony_cistruct cf_osslq_ctx {
28213498266Sopenharmony_ci  struct cf_quic_ctx q;
28313498266Sopenharmony_ci  struct ssl_peer peer;
28413498266Sopenharmony_ci  struct quic_tls_ctx tls;
28513498266Sopenharmony_ci  struct cf_call_data call_data;
28613498266Sopenharmony_ci  struct cf_osslq_h3conn h3;
28713498266Sopenharmony_ci  struct curltime started_at;        /* time the current attempt started */
28813498266Sopenharmony_ci  struct curltime handshake_at;      /* time connect handshake finished */
28913498266Sopenharmony_ci  struct curltime first_byte_at;     /* when first byte was recvd */
29013498266Sopenharmony_ci  struct curltime reconnect_at;      /* time the next attempt should start */
29113498266Sopenharmony_ci  struct bufc_pool stream_bufcp;     /* chunk pool for streams */
29213498266Sopenharmony_ci  size_t max_stream_window;          /* max flow window for one stream */
29313498266Sopenharmony_ci  uint64_t max_idle_ms;              /* max idle time for QUIC connection */
29413498266Sopenharmony_ci  BIT(got_first_byte);               /* if first byte was received */
29513498266Sopenharmony_ci#ifdef USE_OPENSSL
29613498266Sopenharmony_ci  BIT(x509_store_setup);             /* if x509 store has been set up */
29713498266Sopenharmony_ci  BIT(protocol_shutdown);            /* QUIC connection is shut down */
29813498266Sopenharmony_ci#endif
29913498266Sopenharmony_ci};
30013498266Sopenharmony_ci
30113498266Sopenharmony_cistatic void cf_osslq_ctx_clear(struct cf_osslq_ctx *ctx)
30213498266Sopenharmony_ci{
30313498266Sopenharmony_ci  struct cf_call_data save = ctx->call_data;
30413498266Sopenharmony_ci
30513498266Sopenharmony_ci  cf_osslq_h3conn_cleanup(&ctx->h3);
30613498266Sopenharmony_ci  Curl_vquic_tls_cleanup(&ctx->tls);
30713498266Sopenharmony_ci  vquic_ctx_free(&ctx->q);
30813498266Sopenharmony_ci  Curl_bufcp_free(&ctx->stream_bufcp);
30913498266Sopenharmony_ci  Curl_ssl_peer_cleanup(&ctx->peer);
31013498266Sopenharmony_ci
31113498266Sopenharmony_ci  memset(ctx, 0, sizeof(*ctx));
31213498266Sopenharmony_ci  ctx->call_data = save;
31313498266Sopenharmony_ci}
31413498266Sopenharmony_ci
31513498266Sopenharmony_cistatic void cf_osslq_close(struct Curl_cfilter *cf, struct Curl_easy *data)
31613498266Sopenharmony_ci{
31713498266Sopenharmony_ci  struct cf_osslq_ctx *ctx = cf->ctx;
31813498266Sopenharmony_ci  struct cf_call_data save;
31913498266Sopenharmony_ci
32013498266Sopenharmony_ci  CF_DATA_SAVE(save, cf, data);
32113498266Sopenharmony_ci  if(ctx && ctx->tls.ssl) {
32213498266Sopenharmony_ci    /* TODO: send connection close */
32313498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "cf_osslq_close()");
32413498266Sopenharmony_ci    cf_osslq_ctx_clear(ctx);
32513498266Sopenharmony_ci  }
32613498266Sopenharmony_ci
32713498266Sopenharmony_ci  cf->connected = FALSE;
32813498266Sopenharmony_ci  CF_DATA_RESTORE(cf, save);
32913498266Sopenharmony_ci}
33013498266Sopenharmony_ci
33113498266Sopenharmony_cistatic void cf_osslq_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
33213498266Sopenharmony_ci{
33313498266Sopenharmony_ci  struct cf_osslq_ctx *ctx = cf->ctx;
33413498266Sopenharmony_ci  struct cf_call_data save;
33513498266Sopenharmony_ci
33613498266Sopenharmony_ci  CF_DATA_SAVE(save, cf, data);
33713498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "destroy");
33813498266Sopenharmony_ci  if(ctx) {
33913498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "cf_osslq_destroy()");
34013498266Sopenharmony_ci    cf_osslq_ctx_clear(ctx);
34113498266Sopenharmony_ci    free(ctx);
34213498266Sopenharmony_ci  }
34313498266Sopenharmony_ci  cf->ctx = NULL;
34413498266Sopenharmony_ci  /* No CF_DATA_RESTORE(cf, save) possible */
34513498266Sopenharmony_ci  (void)save;
34613498266Sopenharmony_ci}
34713498266Sopenharmony_ci
34813498266Sopenharmony_cistatic CURLcode cf_osslq_h3conn_add_stream(struct cf_osslq_h3conn *h3,
34913498266Sopenharmony_ci                                           SSL *stream_ssl,
35013498266Sopenharmony_ci                                           struct Curl_cfilter *cf,
35113498266Sopenharmony_ci                                           struct Curl_easy *data)
35213498266Sopenharmony_ci{
35313498266Sopenharmony_ci  struct cf_osslq_ctx *ctx = cf->ctx;
35413498266Sopenharmony_ci  int64_t stream_id = SSL_get_stream_id(stream_ssl);
35513498266Sopenharmony_ci
35613498266Sopenharmony_ci  if(h3->remote_ctrl_n >= ARRAYSIZE(h3->remote_ctrl)) {
35713498266Sopenharmony_ci    /* rejected, we are full */
35813498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "[%" PRId64 "] rejecting additional remote stream",
35913498266Sopenharmony_ci                stream_id);
36013498266Sopenharmony_ci    SSL_free(stream_ssl);
36113498266Sopenharmony_ci    return CURLE_FAILED_INIT;
36213498266Sopenharmony_ci  }
36313498266Sopenharmony_ci  switch(SSL_get_stream_type(stream_ssl)) {
36413498266Sopenharmony_ci    case SSL_STREAM_TYPE_READ: {
36513498266Sopenharmony_ci      struct cf_osslq_stream *nstream = &h3->remote_ctrl[h3->remote_ctrl_n++];
36613498266Sopenharmony_ci      nstream->id = stream_id;
36713498266Sopenharmony_ci      nstream->ssl = stream_ssl;
36813498266Sopenharmony_ci      Curl_bufq_initp(&nstream->recvbuf, &ctx->stream_bufcp, 1, BUFQ_OPT_NONE);
36913498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "[%" PRId64 "] accepted new remote uni stream",
37013498266Sopenharmony_ci                  stream_id);
37113498266Sopenharmony_ci      break;
37213498266Sopenharmony_ci    }
37313498266Sopenharmony_ci    default:
37413498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "[%" PRId64 "] rejecting remote non-uni-read"
37513498266Sopenharmony_ci                  " stream", stream_id);
37613498266Sopenharmony_ci      SSL_free(stream_ssl);
37713498266Sopenharmony_ci      return CURLE_FAILED_INIT;
37813498266Sopenharmony_ci  }
37913498266Sopenharmony_ci  return CURLE_OK;
38013498266Sopenharmony_ci
38113498266Sopenharmony_ci}
38213498266Sopenharmony_ci
38313498266Sopenharmony_cistatic CURLcode cf_osslq_ssl_err(struct Curl_cfilter *cf,
38413498266Sopenharmony_ci                              struct Curl_easy *data,
38513498266Sopenharmony_ci                              int detail, CURLcode def_result)
38613498266Sopenharmony_ci{
38713498266Sopenharmony_ci  struct cf_osslq_ctx *ctx = cf->ctx;
38813498266Sopenharmony_ci  CURLcode result = def_result;
38913498266Sopenharmony_ci  sslerr_t errdetail;
39013498266Sopenharmony_ci  char ebuf[256] = "unknown";
39113498266Sopenharmony_ci  const char *err_descr = ebuf;
39213498266Sopenharmony_ci  long lerr;
39313498266Sopenharmony_ci  int lib;
39413498266Sopenharmony_ci  int reason;
39513498266Sopenharmony_ci  struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
39613498266Sopenharmony_ci
39713498266Sopenharmony_ci  errdetail = ERR_get_error();
39813498266Sopenharmony_ci  lib = ERR_GET_LIB(errdetail);
39913498266Sopenharmony_ci  reason = ERR_GET_REASON(errdetail);
40013498266Sopenharmony_ci
40113498266Sopenharmony_ci  if((lib == ERR_LIB_SSL) &&
40213498266Sopenharmony_ci     ((reason == SSL_R_CERTIFICATE_VERIFY_FAILED) ||
40313498266Sopenharmony_ci      (reason == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED))) {
40413498266Sopenharmony_ci    result = CURLE_PEER_FAILED_VERIFICATION;
40513498266Sopenharmony_ci
40613498266Sopenharmony_ci    lerr = SSL_get_verify_result(ctx->tls.ssl);
40713498266Sopenharmony_ci    if(lerr != X509_V_OK) {
40813498266Sopenharmony_ci      ssl_config->certverifyresult = lerr;
40913498266Sopenharmony_ci      msnprintf(ebuf, sizeof(ebuf),
41013498266Sopenharmony_ci                "SSL certificate problem: %s",
41113498266Sopenharmony_ci                X509_verify_cert_error_string(lerr));
41213498266Sopenharmony_ci    }
41313498266Sopenharmony_ci    else
41413498266Sopenharmony_ci      err_descr = "SSL certificate verification failed";
41513498266Sopenharmony_ci  }
41613498266Sopenharmony_ci#if defined(SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED)
41713498266Sopenharmony_ci  /* SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED is only available on
41813498266Sopenharmony_ci     OpenSSL version above v1.1.1, not LibreSSL, BoringSSL, or AWS-LC */
41913498266Sopenharmony_ci  else if((lib == ERR_LIB_SSL) &&
42013498266Sopenharmony_ci          (reason == SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED)) {
42113498266Sopenharmony_ci    /* If client certificate is required, communicate the
42213498266Sopenharmony_ci       error to client */
42313498266Sopenharmony_ci    result = CURLE_SSL_CLIENTCERT;
42413498266Sopenharmony_ci    ossl_strerror(errdetail, ebuf, sizeof(ebuf));
42513498266Sopenharmony_ci  }
42613498266Sopenharmony_ci#endif
42713498266Sopenharmony_ci  else if((lib == ERR_LIB_SSL) && (reason == SSL_R_PROTOCOL_IS_SHUTDOWN)) {
42813498266Sopenharmony_ci    ctx->protocol_shutdown = TRUE;
42913498266Sopenharmony_ci    err_descr = "QUIC connectin has been shut down";
43013498266Sopenharmony_ci    result = def_result;
43113498266Sopenharmony_ci  }
43213498266Sopenharmony_ci  else {
43313498266Sopenharmony_ci    result = def_result;
43413498266Sopenharmony_ci    ossl_strerror(errdetail, ebuf, sizeof(ebuf));
43513498266Sopenharmony_ci  }
43613498266Sopenharmony_ci
43713498266Sopenharmony_ci  /* detail is already set to the SSL error above */
43813498266Sopenharmony_ci
43913498266Sopenharmony_ci  /* If we e.g. use SSLv2 request-method and the server doesn't like us
44013498266Sopenharmony_ci   * (RST connection, etc.), OpenSSL gives no explanation whatsoever and
44113498266Sopenharmony_ci   * the SO_ERROR is also lost.
44213498266Sopenharmony_ci   */
44313498266Sopenharmony_ci  if(CURLE_SSL_CONNECT_ERROR == result && errdetail == 0) {
44413498266Sopenharmony_ci    char extramsg[80]="";
44513498266Sopenharmony_ci    int sockerr = SOCKERRNO;
44613498266Sopenharmony_ci    const char *r_ip = NULL;
44713498266Sopenharmony_ci    int r_port = 0;
44813498266Sopenharmony_ci
44913498266Sopenharmony_ci    Curl_cf_socket_peek(cf->next, data, NULL, NULL,
45013498266Sopenharmony_ci                        &r_ip, &r_port, NULL, NULL);
45113498266Sopenharmony_ci    if(sockerr && detail == SSL_ERROR_SYSCALL)
45213498266Sopenharmony_ci      Curl_strerror(sockerr, extramsg, sizeof(extramsg));
45313498266Sopenharmony_ci    failf(data, "QUIC connect: %s in connection to %s:%d (%s)",
45413498266Sopenharmony_ci          extramsg[0] ? extramsg : SSL_ERROR_to_str(detail),
45513498266Sopenharmony_ci          ctx->peer.dispname, r_port, r_ip);
45613498266Sopenharmony_ci  }
45713498266Sopenharmony_ci  else {
45813498266Sopenharmony_ci    /* Could be a CERT problem */
45913498266Sopenharmony_ci    failf(data, "%s", err_descr);
46013498266Sopenharmony_ci  }
46113498266Sopenharmony_ci  return result;
46213498266Sopenharmony_ci}
46313498266Sopenharmony_ci
46413498266Sopenharmony_cistatic CURLcode cf_osslq_verify_peer(struct Curl_cfilter *cf,
46513498266Sopenharmony_ci                                  struct Curl_easy *data)
46613498266Sopenharmony_ci{
46713498266Sopenharmony_ci  struct cf_osslq_ctx *ctx = cf->ctx;
46813498266Sopenharmony_ci
46913498266Sopenharmony_ci  cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
47013498266Sopenharmony_ci  cf->conn->httpversion = 30;
47113498266Sopenharmony_ci  cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
47213498266Sopenharmony_ci
47313498266Sopenharmony_ci  return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer);
47413498266Sopenharmony_ci}
47513498266Sopenharmony_ci
47613498266Sopenharmony_ci/**
47713498266Sopenharmony_ci * All about the H3 internals of a stream
47813498266Sopenharmony_ci */
47913498266Sopenharmony_cistruct h3_stream_ctx {
48013498266Sopenharmony_ci  struct cf_osslq_stream s;
48113498266Sopenharmony_ci  struct bufq sendbuf;   /* h3 request body */
48213498266Sopenharmony_ci  struct bufq recvbuf;   /* h3 response body */
48313498266Sopenharmony_ci  struct h1_req_parser h1; /* h1 request parsing */
48413498266Sopenharmony_ci  size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */
48513498266Sopenharmony_ci  size_t upload_blocked_len; /* the amount written last and EGAINed */
48613498266Sopenharmony_ci  size_t recv_buf_nonflow; /* buffered bytes, not counting for flow control */
48713498266Sopenharmony_ci  uint64_t error3; /* HTTP/3 stream error code */
48813498266Sopenharmony_ci  curl_off_t upload_left; /* number of request bytes left to upload */
48913498266Sopenharmony_ci  curl_off_t download_recvd; /* number of response DATA bytes received */
49013498266Sopenharmony_ci  int status_code; /* HTTP status code */
49113498266Sopenharmony_ci  bool resp_hds_complete; /* we have a complete, final response */
49213498266Sopenharmony_ci  bool closed; /* TRUE on stream close */
49313498266Sopenharmony_ci  bool reset;  /* TRUE on stream reset */
49413498266Sopenharmony_ci  bool send_closed; /* stream is local closed */
49513498266Sopenharmony_ci  BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */
49613498266Sopenharmony_ci};
49713498266Sopenharmony_ci
49813498266Sopenharmony_ci#define H3_STREAM_CTX(d)  ((struct h3_stream_ctx *)(((d) && (d)->req.p.http)? \
49913498266Sopenharmony_ci                           ((struct HTTP *)(d)->req.p.http)->h3_ctx \
50013498266Sopenharmony_ci                             : NULL))
50113498266Sopenharmony_ci#define H3_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->h3_ctx
50213498266Sopenharmony_ci#define H3_STREAM_ID(d)   (H3_STREAM_CTX(d)? \
50313498266Sopenharmony_ci                           H3_STREAM_CTX(d)->s.id : -2)
50413498266Sopenharmony_ci
50513498266Sopenharmony_cistatic CURLcode h3_data_setup(struct Curl_cfilter *cf,
50613498266Sopenharmony_ci                              struct Curl_easy *data)
50713498266Sopenharmony_ci{
50813498266Sopenharmony_ci  struct cf_osslq_ctx *ctx = cf->ctx;
50913498266Sopenharmony_ci  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
51013498266Sopenharmony_ci
51113498266Sopenharmony_ci  if(!data || !data->req.p.http) {
51213498266Sopenharmony_ci    failf(data, "initialization failure, transfer not http initialized");
51313498266Sopenharmony_ci    return CURLE_FAILED_INIT;
51413498266Sopenharmony_ci  }
51513498266Sopenharmony_ci
51613498266Sopenharmony_ci  if(stream)
51713498266Sopenharmony_ci    return CURLE_OK;
51813498266Sopenharmony_ci
51913498266Sopenharmony_ci  stream = calloc(1, sizeof(*stream));
52013498266Sopenharmony_ci  if(!stream)
52113498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
52213498266Sopenharmony_ci
52313498266Sopenharmony_ci  stream->s.id = -1;
52413498266Sopenharmony_ci  /* on send, we control how much we put into the buffer */
52513498266Sopenharmony_ci  Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp,
52613498266Sopenharmony_ci                  H3_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE);
52713498266Sopenharmony_ci  stream->sendbuf_len_in_flight = 0;
52813498266Sopenharmony_ci  /* on recv, we need a flexible buffer limit since we also write
52913498266Sopenharmony_ci   * headers to it that are not counted against the nghttp3 flow limits. */
53013498266Sopenharmony_ci  Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp,
53113498266Sopenharmony_ci                  H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT);
53213498266Sopenharmony_ci  stream->recv_buf_nonflow = 0;
53313498266Sopenharmony_ci  Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
53413498266Sopenharmony_ci
53513498266Sopenharmony_ci  H3_STREAM_LCTX(data) = stream;
53613498266Sopenharmony_ci  return CURLE_OK;
53713498266Sopenharmony_ci}
53813498266Sopenharmony_ci
53913498266Sopenharmony_cistatic void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
54013498266Sopenharmony_ci{
54113498266Sopenharmony_ci  struct cf_osslq_ctx *ctx = cf->ctx;
54213498266Sopenharmony_ci  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
54313498266Sopenharmony_ci
54413498266Sopenharmony_ci  (void)cf;
54513498266Sopenharmony_ci  if(stream) {
54613498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "[%"PRId64"] easy handle is done", stream->s.id);
54713498266Sopenharmony_ci    if(ctx->h3.conn && !stream->closed) {
54813498266Sopenharmony_ci      nghttp3_conn_shutdown_stream_read(ctx->h3.conn, stream->s.id);
54913498266Sopenharmony_ci      nghttp3_conn_close_stream(ctx->h3.conn, stream->s.id,
55013498266Sopenharmony_ci                                NGHTTP3_H3_REQUEST_CANCELLED);
55113498266Sopenharmony_ci      nghttp3_conn_set_stream_user_data(ctx->h3.conn, stream->s.id, NULL);
55213498266Sopenharmony_ci      stream->closed = TRUE;
55313498266Sopenharmony_ci    }
55413498266Sopenharmony_ci
55513498266Sopenharmony_ci    cf_osslq_stream_cleanup(&stream->s);
55613498266Sopenharmony_ci    Curl_bufq_free(&stream->sendbuf);
55713498266Sopenharmony_ci    Curl_bufq_free(&stream->recvbuf);
55813498266Sopenharmony_ci    Curl_h1_req_parse_free(&stream->h1);
55913498266Sopenharmony_ci    free(stream);
56013498266Sopenharmony_ci    H3_STREAM_LCTX(data) = NULL;
56113498266Sopenharmony_ci  }
56213498266Sopenharmony_ci}
56313498266Sopenharmony_ci
56413498266Sopenharmony_cistatic struct cf_osslq_stream *cf_osslq_get_qstream(struct Curl_cfilter *cf,
56513498266Sopenharmony_ci                                                    struct Curl_easy *data,
56613498266Sopenharmony_ci                                                    int64_t stream_id)
56713498266Sopenharmony_ci{
56813498266Sopenharmony_ci  struct cf_osslq_ctx *ctx = cf->ctx;
56913498266Sopenharmony_ci  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
57013498266Sopenharmony_ci  struct Curl_easy *sdata;
57113498266Sopenharmony_ci
57213498266Sopenharmony_ci  if(stream && stream->s.id == stream_id) {
57313498266Sopenharmony_ci    return &stream->s;
57413498266Sopenharmony_ci  }
57513498266Sopenharmony_ci  else if(ctx->h3.s_ctrl.id == stream_id) {
57613498266Sopenharmony_ci    return &ctx->h3.s_ctrl;
57713498266Sopenharmony_ci  }
57813498266Sopenharmony_ci  else if(ctx->h3.s_qpack_enc.id == stream_id) {
57913498266Sopenharmony_ci    return &ctx->h3.s_qpack_enc;
58013498266Sopenharmony_ci  }
58113498266Sopenharmony_ci  else if(ctx->h3.s_qpack_dec.id == stream_id) {
58213498266Sopenharmony_ci    return &ctx->h3.s_qpack_dec;
58313498266Sopenharmony_ci  }
58413498266Sopenharmony_ci  else {
58513498266Sopenharmony_ci    DEBUGASSERT(data->multi);
58613498266Sopenharmony_ci    for(sdata = data->multi->easyp; sdata; sdata = sdata->next) {
58713498266Sopenharmony_ci      if((sdata->conn == data->conn) && H3_STREAM_ID(sdata) == stream_id) {
58813498266Sopenharmony_ci        stream = H3_STREAM_CTX(sdata);
58913498266Sopenharmony_ci        return stream? &stream->s : NULL;
59013498266Sopenharmony_ci      }
59113498266Sopenharmony_ci    }
59213498266Sopenharmony_ci  }
59313498266Sopenharmony_ci  return NULL;
59413498266Sopenharmony_ci}
59513498266Sopenharmony_ci
59613498266Sopenharmony_cistatic void h3_drain_stream(struct Curl_cfilter *cf,
59713498266Sopenharmony_ci                            struct Curl_easy *data)
59813498266Sopenharmony_ci{
59913498266Sopenharmony_ci  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
60013498266Sopenharmony_ci  unsigned char bits;
60113498266Sopenharmony_ci
60213498266Sopenharmony_ci  (void)cf;
60313498266Sopenharmony_ci  bits = CURL_CSELECT_IN;
60413498266Sopenharmony_ci  if(stream && stream->upload_left && !stream->send_closed)
60513498266Sopenharmony_ci    bits |= CURL_CSELECT_OUT;
60613498266Sopenharmony_ci  if(data->state.select_bits != bits) {
60713498266Sopenharmony_ci    data->state.select_bits = bits;
60813498266Sopenharmony_ci    Curl_expire(data, 0, EXPIRE_RUN_NOW);
60913498266Sopenharmony_ci  }
61013498266Sopenharmony_ci}
61113498266Sopenharmony_ci
61213498266Sopenharmony_cistatic CURLcode h3_data_pause(struct Curl_cfilter *cf,
61313498266Sopenharmony_ci                              struct Curl_easy *data,
61413498266Sopenharmony_ci                              bool pause)
61513498266Sopenharmony_ci{
61613498266Sopenharmony_ci  if(!pause) {
61713498266Sopenharmony_ci    /* unpaused. make it run again right away */
61813498266Sopenharmony_ci    h3_drain_stream(cf, data);
61913498266Sopenharmony_ci    Curl_expire(data, 0, EXPIRE_RUN_NOW);
62013498266Sopenharmony_ci  }
62113498266Sopenharmony_ci  return CURLE_OK;
62213498266Sopenharmony_ci}
62313498266Sopenharmony_ci
62413498266Sopenharmony_cistatic int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id,
62513498266Sopenharmony_ci                              uint64_t app_error_code, void *user_data,
62613498266Sopenharmony_ci                              void *stream_user_data)
62713498266Sopenharmony_ci{
62813498266Sopenharmony_ci  struct Curl_cfilter *cf = user_data;
62913498266Sopenharmony_ci  struct Curl_easy *data = stream_user_data;
63013498266Sopenharmony_ci  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
63113498266Sopenharmony_ci  (void)conn;
63213498266Sopenharmony_ci  (void)stream_id;
63313498266Sopenharmony_ci
63413498266Sopenharmony_ci  /* we might be called by nghttp3 after we already cleaned up */
63513498266Sopenharmony_ci  if(!stream)
63613498266Sopenharmony_ci    return 0;
63713498266Sopenharmony_ci
63813498266Sopenharmony_ci  stream->closed = TRUE;
63913498266Sopenharmony_ci  stream->error3 = app_error_code;
64013498266Sopenharmony_ci  if(stream->error3 != NGHTTP3_H3_NO_ERROR) {
64113498266Sopenharmony_ci    stream->reset = TRUE;
64213498266Sopenharmony_ci    stream->send_closed = TRUE;
64313498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "[%" PRId64 "] RESET: error %" PRId64,
64413498266Sopenharmony_ci                stream->s.id, stream->error3);
64513498266Sopenharmony_ci  }
64613498266Sopenharmony_ci  else {
64713498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "[%" PRId64 "] CLOSED", stream->s.id);
64813498266Sopenharmony_ci  }
64913498266Sopenharmony_ci  h3_drain_stream(cf, data);
65013498266Sopenharmony_ci  return 0;
65113498266Sopenharmony_ci}
65213498266Sopenharmony_ci
65313498266Sopenharmony_ci/*
65413498266Sopenharmony_ci * write_resp_raw() copies response data in raw format to the `data`'s
65513498266Sopenharmony_ci  * receive buffer. If not enough space is available, it appends to the
65613498266Sopenharmony_ci * `data`'s overflow buffer.
65713498266Sopenharmony_ci */
65813498266Sopenharmony_cistatic CURLcode write_resp_raw(struct Curl_cfilter *cf,
65913498266Sopenharmony_ci                               struct Curl_easy *data,
66013498266Sopenharmony_ci                               const void *mem, size_t memlen,
66113498266Sopenharmony_ci                               bool flow)
66213498266Sopenharmony_ci{
66313498266Sopenharmony_ci  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
66413498266Sopenharmony_ci  CURLcode result = CURLE_OK;
66513498266Sopenharmony_ci  ssize_t nwritten;
66613498266Sopenharmony_ci
66713498266Sopenharmony_ci  (void)cf;
66813498266Sopenharmony_ci  if(!stream) {
66913498266Sopenharmony_ci    return CURLE_RECV_ERROR;
67013498266Sopenharmony_ci  }
67113498266Sopenharmony_ci  nwritten = Curl_bufq_write(&stream->recvbuf, mem, memlen, &result);
67213498266Sopenharmony_ci  if(nwritten < 0) {
67313498266Sopenharmony_ci    return result;
67413498266Sopenharmony_ci  }
67513498266Sopenharmony_ci
67613498266Sopenharmony_ci  if(!flow)
67713498266Sopenharmony_ci    stream->recv_buf_nonflow += (size_t)nwritten;
67813498266Sopenharmony_ci
67913498266Sopenharmony_ci  if((size_t)nwritten < memlen) {
68013498266Sopenharmony_ci    /* This MUST not happen. Our recbuf is dimensioned to hold the
68113498266Sopenharmony_ci     * full max_stream_window and then some for this very reason. */
68213498266Sopenharmony_ci    DEBUGASSERT(0);
68313498266Sopenharmony_ci    return CURLE_RECV_ERROR;
68413498266Sopenharmony_ci  }
68513498266Sopenharmony_ci  return result;
68613498266Sopenharmony_ci}
68713498266Sopenharmony_ci
68813498266Sopenharmony_cistatic int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id,
68913498266Sopenharmony_ci                           const uint8_t *buf, size_t buflen,
69013498266Sopenharmony_ci                           void *user_data, void *stream_user_data)
69113498266Sopenharmony_ci{
69213498266Sopenharmony_ci  struct Curl_cfilter *cf = user_data;
69313498266Sopenharmony_ci  struct Curl_easy *data = stream_user_data;
69413498266Sopenharmony_ci  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
69513498266Sopenharmony_ci  CURLcode result;
69613498266Sopenharmony_ci
69713498266Sopenharmony_ci  (void)conn;
69813498266Sopenharmony_ci  (void)stream3_id;
69913498266Sopenharmony_ci
70013498266Sopenharmony_ci  if(!stream)
70113498266Sopenharmony_ci    return NGHTTP3_ERR_CALLBACK_FAILURE;
70213498266Sopenharmony_ci
70313498266Sopenharmony_ci  result = write_resp_raw(cf, data, buf, buflen, TRUE);
70413498266Sopenharmony_ci  if(result) {
70513498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, ERROR receiving %d",
70613498266Sopenharmony_ci                stream->s.id, buflen, result);
70713498266Sopenharmony_ci    return NGHTTP3_ERR_CALLBACK_FAILURE;
70813498266Sopenharmony_ci  }
70913498266Sopenharmony_ci  stream->download_recvd += (curl_off_t)buflen;
71013498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, total=%zd",
71113498266Sopenharmony_ci              stream->s.id, buflen, stream->download_recvd);
71213498266Sopenharmony_ci  h3_drain_stream(cf, data);
71313498266Sopenharmony_ci  return 0;
71413498266Sopenharmony_ci}
71513498266Sopenharmony_ci
71613498266Sopenharmony_cistatic int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream_id,
71713498266Sopenharmony_ci                                  size_t consumed, void *user_data,
71813498266Sopenharmony_ci                                  void *stream_user_data)
71913498266Sopenharmony_ci{
72013498266Sopenharmony_ci  struct Curl_cfilter *cf = user_data;
72113498266Sopenharmony_ci  struct Curl_easy *data = stream_user_data;
72213498266Sopenharmony_ci  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
72313498266Sopenharmony_ci
72413498266Sopenharmony_ci  (void)conn;
72513498266Sopenharmony_ci  (void)stream_id;
72613498266Sopenharmony_ci  if(stream)
72713498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "[%" PRId64 "] deferred consume %zu bytes",
72813498266Sopenharmony_ci                stream->s.id, consumed);
72913498266Sopenharmony_ci  return 0;
73013498266Sopenharmony_ci}
73113498266Sopenharmony_ci
73213498266Sopenharmony_cistatic int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id,
73313498266Sopenharmony_ci                             int32_t token, nghttp3_rcbuf *name,
73413498266Sopenharmony_ci                             nghttp3_rcbuf *value, uint8_t flags,
73513498266Sopenharmony_ci                             void *user_data, void *stream_user_data)
73613498266Sopenharmony_ci{
73713498266Sopenharmony_ci  struct Curl_cfilter *cf = user_data;
73813498266Sopenharmony_ci  nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name);
73913498266Sopenharmony_ci  nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value);
74013498266Sopenharmony_ci  struct Curl_easy *data = stream_user_data;
74113498266Sopenharmony_ci  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
74213498266Sopenharmony_ci  CURLcode result = CURLE_OK;
74313498266Sopenharmony_ci  (void)conn;
74413498266Sopenharmony_ci  (void)stream_id;
74513498266Sopenharmony_ci  (void)token;
74613498266Sopenharmony_ci  (void)flags;
74713498266Sopenharmony_ci  (void)cf;
74813498266Sopenharmony_ci
74913498266Sopenharmony_ci  /* we might have cleaned up this transfer already */
75013498266Sopenharmony_ci  if(!stream)
75113498266Sopenharmony_ci    return 0;
75213498266Sopenharmony_ci
75313498266Sopenharmony_ci  if(token == NGHTTP3_QPACK_TOKEN__STATUS) {
75413498266Sopenharmony_ci    char line[14]; /* status line is always 13 characters long */
75513498266Sopenharmony_ci    size_t ncopy;
75613498266Sopenharmony_ci
75713498266Sopenharmony_ci    result = Curl_http_decode_status(&stream->status_code,
75813498266Sopenharmony_ci                                     (const char *)h3val.base, h3val.len);
75913498266Sopenharmony_ci    if(result)
76013498266Sopenharmony_ci      return -1;
76113498266Sopenharmony_ci    ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n",
76213498266Sopenharmony_ci                      stream->status_code);
76313498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "[%" PRId64 "] status: %s", stream_id, line);
76413498266Sopenharmony_ci    result = write_resp_raw(cf, data, line, ncopy, FALSE);
76513498266Sopenharmony_ci    if(result) {
76613498266Sopenharmony_ci      return -1;
76713498266Sopenharmony_ci    }
76813498266Sopenharmony_ci  }
76913498266Sopenharmony_ci  else {
77013498266Sopenharmony_ci    /* store as an HTTP1-style header */
77113498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "[%" PRId64 "] header: %.*s: %.*s",
77213498266Sopenharmony_ci                stream_id, (int)h3name.len, h3name.base,
77313498266Sopenharmony_ci                (int)h3val.len, h3val.base);
77413498266Sopenharmony_ci    result = write_resp_raw(cf, data, h3name.base, h3name.len, FALSE);
77513498266Sopenharmony_ci    if(result) {
77613498266Sopenharmony_ci      return -1;
77713498266Sopenharmony_ci    }
77813498266Sopenharmony_ci    result = write_resp_raw(cf, data, ": ", 2, FALSE);
77913498266Sopenharmony_ci    if(result) {
78013498266Sopenharmony_ci      return -1;
78113498266Sopenharmony_ci    }
78213498266Sopenharmony_ci    result = write_resp_raw(cf, data, h3val.base, h3val.len, FALSE);
78313498266Sopenharmony_ci    if(result) {
78413498266Sopenharmony_ci      return -1;
78513498266Sopenharmony_ci    }
78613498266Sopenharmony_ci    result = write_resp_raw(cf, data, "\r\n", 2, FALSE);
78713498266Sopenharmony_ci    if(result) {
78813498266Sopenharmony_ci      return -1;
78913498266Sopenharmony_ci    }
79013498266Sopenharmony_ci  }
79113498266Sopenharmony_ci  return 0;
79213498266Sopenharmony_ci}
79313498266Sopenharmony_ci
79413498266Sopenharmony_cistatic int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id,
79513498266Sopenharmony_ci                             int fin, void *user_data, void *stream_user_data)
79613498266Sopenharmony_ci{
79713498266Sopenharmony_ci  struct Curl_cfilter *cf = user_data;
79813498266Sopenharmony_ci  struct Curl_easy *data = stream_user_data;
79913498266Sopenharmony_ci  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
80013498266Sopenharmony_ci  CURLcode result = CURLE_OK;
80113498266Sopenharmony_ci  (void)conn;
80213498266Sopenharmony_ci  (void)stream_id;
80313498266Sopenharmony_ci  (void)fin;
80413498266Sopenharmony_ci  (void)cf;
80513498266Sopenharmony_ci
80613498266Sopenharmony_ci  if(!stream)
80713498266Sopenharmony_ci    return 0;
80813498266Sopenharmony_ci  /* add a CRLF only if we've received some headers */
80913498266Sopenharmony_ci  result = write_resp_raw(cf, data, "\r\n", 2, FALSE);
81013498266Sopenharmony_ci  if(result) {
81113498266Sopenharmony_ci    return -1;
81213498266Sopenharmony_ci  }
81313498266Sopenharmony_ci
81413498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "[%" PRId64 "] end_headers, status=%d",
81513498266Sopenharmony_ci              stream_id, stream->status_code);
81613498266Sopenharmony_ci  if(stream->status_code / 100 != 1) {
81713498266Sopenharmony_ci    stream->resp_hds_complete = TRUE;
81813498266Sopenharmony_ci  }
81913498266Sopenharmony_ci  h3_drain_stream(cf, data);
82013498266Sopenharmony_ci  return 0;
82113498266Sopenharmony_ci}
82213498266Sopenharmony_ci
82313498266Sopenharmony_cistatic int cb_h3_stop_sending(nghttp3_conn *conn, int64_t stream_id,
82413498266Sopenharmony_ci                              uint64_t app_error_code, void *user_data,
82513498266Sopenharmony_ci                              void *stream_user_data)
82613498266Sopenharmony_ci{
82713498266Sopenharmony_ci  struct Curl_cfilter *cf = user_data;
82813498266Sopenharmony_ci  struct Curl_easy *data = stream_user_data;
82913498266Sopenharmony_ci  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
83013498266Sopenharmony_ci  (void)conn;
83113498266Sopenharmony_ci  (void)app_error_code;
83213498266Sopenharmony_ci
83313498266Sopenharmony_ci  if(!stream || !stream->s.ssl)
83413498266Sopenharmony_ci    return 0;
83513498266Sopenharmony_ci
83613498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "[%" PRId64 "] stop_sending", stream_id);
83713498266Sopenharmony_ci  cf_osslq_stream_close(&stream->s);
83813498266Sopenharmony_ci  return 0;
83913498266Sopenharmony_ci}
84013498266Sopenharmony_ci
84113498266Sopenharmony_cistatic int cb_h3_reset_stream(nghttp3_conn *conn, int64_t stream_id,
84213498266Sopenharmony_ci                              uint64_t app_error_code, void *user_data,
84313498266Sopenharmony_ci                              void *stream_user_data) {
84413498266Sopenharmony_ci  struct Curl_cfilter *cf = user_data;
84513498266Sopenharmony_ci  struct Curl_easy *data = stream_user_data;
84613498266Sopenharmony_ci  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
84713498266Sopenharmony_ci  int rv;
84813498266Sopenharmony_ci  (void)conn;
84913498266Sopenharmony_ci
85013498266Sopenharmony_ci  if(stream && stream->s.ssl) {
85113498266Sopenharmony_ci    SSL_STREAM_RESET_ARGS args = {0};
85213498266Sopenharmony_ci    args.quic_error_code = app_error_code;
85313498266Sopenharmony_ci    rv = !SSL_stream_reset(stream->s.ssl, &args, sizeof(args));
85413498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv);
85513498266Sopenharmony_ci    if(!rv) {
85613498266Sopenharmony_ci      return NGHTTP3_ERR_CALLBACK_FAILURE;
85713498266Sopenharmony_ci    }
85813498266Sopenharmony_ci  }
85913498266Sopenharmony_ci  return 0;
86013498266Sopenharmony_ci}
86113498266Sopenharmony_ci
86213498266Sopenharmony_cistatic nghttp3_ssize
86313498266Sopenharmony_cicb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id,
86413498266Sopenharmony_ci                    nghttp3_vec *vec, size_t veccnt,
86513498266Sopenharmony_ci                    uint32_t *pflags, void *user_data,
86613498266Sopenharmony_ci                    void *stream_user_data)
86713498266Sopenharmony_ci{
86813498266Sopenharmony_ci  struct Curl_cfilter *cf = user_data;
86913498266Sopenharmony_ci  struct Curl_easy *data = stream_user_data;
87013498266Sopenharmony_ci  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
87113498266Sopenharmony_ci  ssize_t nwritten = 0;
87213498266Sopenharmony_ci  size_t nvecs = 0;
87313498266Sopenharmony_ci  (void)cf;
87413498266Sopenharmony_ci  (void)conn;
87513498266Sopenharmony_ci  (void)stream_id;
87613498266Sopenharmony_ci  (void)user_data;
87713498266Sopenharmony_ci  (void)veccnt;
87813498266Sopenharmony_ci
87913498266Sopenharmony_ci  if(!stream)
88013498266Sopenharmony_ci    return NGHTTP3_ERR_CALLBACK_FAILURE;
88113498266Sopenharmony_ci  /* nghttp3 keeps references to the sendbuf data until it is ACKed
88213498266Sopenharmony_ci   * by the server (see `cb_h3_acked_req_body()` for updates).
88313498266Sopenharmony_ci   * `sendbuf_len_in_flight` is the amount of bytes in `sendbuf`
88413498266Sopenharmony_ci   * that we have already passed to nghttp3, but which have not been
88513498266Sopenharmony_ci   * ACKed yet.
88613498266Sopenharmony_ci   * Any amount beyond `sendbuf_len_in_flight` we need still to pass
88713498266Sopenharmony_ci   * to nghttp3. Do that now, if we can. */
88813498266Sopenharmony_ci  if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) {
88913498266Sopenharmony_ci    nvecs = 0;
89013498266Sopenharmony_ci    while(nvecs < veccnt &&
89113498266Sopenharmony_ci          Curl_bufq_peek_at(&stream->sendbuf,
89213498266Sopenharmony_ci                            stream->sendbuf_len_in_flight,
89313498266Sopenharmony_ci                            (const unsigned char **)&vec[nvecs].base,
89413498266Sopenharmony_ci                            &vec[nvecs].len)) {
89513498266Sopenharmony_ci      stream->sendbuf_len_in_flight += vec[nvecs].len;
89613498266Sopenharmony_ci      nwritten += vec[nvecs].len;
89713498266Sopenharmony_ci      ++nvecs;
89813498266Sopenharmony_ci    }
89913498266Sopenharmony_ci    DEBUGASSERT(nvecs > 0); /* we SHOULD have been be able to peek */
90013498266Sopenharmony_ci  }
90113498266Sopenharmony_ci
90213498266Sopenharmony_ci  if(nwritten > 0 && stream->upload_left != -1)
90313498266Sopenharmony_ci    stream->upload_left -= nwritten;
90413498266Sopenharmony_ci
90513498266Sopenharmony_ci  /* When we stopped sending and everything in `sendbuf` is "in flight",
90613498266Sopenharmony_ci   * we are at the end of the request body. */
90713498266Sopenharmony_ci  if(stream->upload_left == 0) {
90813498266Sopenharmony_ci    *pflags = NGHTTP3_DATA_FLAG_EOF;
90913498266Sopenharmony_ci    stream->send_closed = TRUE;
91013498266Sopenharmony_ci  }
91113498266Sopenharmony_ci  else if(!nwritten) {
91213498266Sopenharmony_ci    /* Not EOF, and nothing to give, we signal WOULDBLOCK. */
91313498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> AGAIN",
91413498266Sopenharmony_ci                stream->s.id);
91513498266Sopenharmony_ci    return NGHTTP3_ERR_WOULDBLOCK;
91613498266Sopenharmony_ci  }
91713498266Sopenharmony_ci
91813498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> "
91913498266Sopenharmony_ci              "%d vecs%s with %zu (buffered=%zu, left=%"
92013498266Sopenharmony_ci              CURL_FORMAT_CURL_OFF_T ")",
92113498266Sopenharmony_ci              stream->s.id, (int)nvecs,
92213498266Sopenharmony_ci              *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"",
92313498266Sopenharmony_ci              nwritten, Curl_bufq_len(&stream->sendbuf),
92413498266Sopenharmony_ci              stream->upload_left);
92513498266Sopenharmony_ci  return (nghttp3_ssize)nvecs;
92613498266Sopenharmony_ci}
92713498266Sopenharmony_ci
92813498266Sopenharmony_cistatic int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id,
92913498266Sopenharmony_ci                                   uint64_t datalen, void *user_data,
93013498266Sopenharmony_ci                                   void *stream_user_data)
93113498266Sopenharmony_ci{
93213498266Sopenharmony_ci  struct Curl_cfilter *cf = user_data;
93313498266Sopenharmony_ci  struct Curl_easy *data = stream_user_data;
93413498266Sopenharmony_ci  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
93513498266Sopenharmony_ci  size_t skiplen;
93613498266Sopenharmony_ci
93713498266Sopenharmony_ci  (void)cf;
93813498266Sopenharmony_ci  if(!stream)
93913498266Sopenharmony_ci    return 0;
94013498266Sopenharmony_ci  /* The server acknowledged `datalen` of bytes from our request body.
94113498266Sopenharmony_ci   * This is a delta. We have kept this data in `sendbuf` for
94213498266Sopenharmony_ci   * re-transmissions and can free it now. */
94313498266Sopenharmony_ci  if(datalen >= (uint64_t)stream->sendbuf_len_in_flight)
94413498266Sopenharmony_ci    skiplen = stream->sendbuf_len_in_flight;
94513498266Sopenharmony_ci  else
94613498266Sopenharmony_ci    skiplen = (size_t)datalen;
94713498266Sopenharmony_ci  Curl_bufq_skip(&stream->sendbuf, skiplen);
94813498266Sopenharmony_ci  stream->sendbuf_len_in_flight -= skiplen;
94913498266Sopenharmony_ci
95013498266Sopenharmony_ci  /* Everything ACKed, we resume upload processing */
95113498266Sopenharmony_ci  if(!stream->sendbuf_len_in_flight) {
95213498266Sopenharmony_ci    int rv = nghttp3_conn_resume_stream(conn, stream_id);
95313498266Sopenharmony_ci    if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
95413498266Sopenharmony_ci      return NGHTTP3_ERR_CALLBACK_FAILURE;
95513498266Sopenharmony_ci    }
95613498266Sopenharmony_ci  }
95713498266Sopenharmony_ci  return 0;
95813498266Sopenharmony_ci}
95913498266Sopenharmony_ci
96013498266Sopenharmony_cistatic nghttp3_callbacks ngh3_callbacks = {
96113498266Sopenharmony_ci  cb_h3_acked_stream_data,
96213498266Sopenharmony_ci  cb_h3_stream_close,
96313498266Sopenharmony_ci  cb_h3_recv_data,
96413498266Sopenharmony_ci  cb_h3_deferred_consume,
96513498266Sopenharmony_ci  NULL, /* begin_headers */
96613498266Sopenharmony_ci  cb_h3_recv_header,
96713498266Sopenharmony_ci  cb_h3_end_headers,
96813498266Sopenharmony_ci  NULL, /* begin_trailers */
96913498266Sopenharmony_ci  cb_h3_recv_header,
97013498266Sopenharmony_ci  NULL, /* end_trailers */
97113498266Sopenharmony_ci  cb_h3_stop_sending,
97213498266Sopenharmony_ci  NULL, /* end_stream */
97313498266Sopenharmony_ci  cb_h3_reset_stream,
97413498266Sopenharmony_ci  NULL, /* shutdown */
97513498266Sopenharmony_ci  NULL /* recv_settings */
97613498266Sopenharmony_ci};
97713498266Sopenharmony_ci
97813498266Sopenharmony_cistatic CURLcode cf_osslq_h3conn_init(struct cf_osslq_ctx *ctx, SSL *conn,
97913498266Sopenharmony_ci                                  void *user_data)
98013498266Sopenharmony_ci{
98113498266Sopenharmony_ci  struct cf_osslq_h3conn *h3 = &ctx->h3;
98213498266Sopenharmony_ci  CURLcode result;
98313498266Sopenharmony_ci  int rc;
98413498266Sopenharmony_ci
98513498266Sopenharmony_ci  nghttp3_settings_default(&h3->settings);
98613498266Sopenharmony_ci  rc = nghttp3_conn_client_new(&h3->conn,
98713498266Sopenharmony_ci                               &ngh3_callbacks,
98813498266Sopenharmony_ci                               &h3->settings,
98913498266Sopenharmony_ci                               nghttp3_mem_default(),
99013498266Sopenharmony_ci                               user_data);
99113498266Sopenharmony_ci  if(rc) {
99213498266Sopenharmony_ci    result = CURLE_OUT_OF_MEMORY;
99313498266Sopenharmony_ci    goto out;
99413498266Sopenharmony_ci  }
99513498266Sopenharmony_ci
99613498266Sopenharmony_ci  result = cf_osslq_stream_open(&h3->s_ctrl, conn,
99713498266Sopenharmony_ci                                SSL_STREAM_FLAG_ADVANCE|SSL_STREAM_FLAG_UNI,
99813498266Sopenharmony_ci                                &ctx->stream_bufcp, NULL);
99913498266Sopenharmony_ci  if(result) {
100013498266Sopenharmony_ci    result = CURLE_QUIC_CONNECT_ERROR;
100113498266Sopenharmony_ci    goto out;
100213498266Sopenharmony_ci  }
100313498266Sopenharmony_ci  result = cf_osslq_stream_open(&h3->s_qpack_enc, conn,
100413498266Sopenharmony_ci                                SSL_STREAM_FLAG_ADVANCE|SSL_STREAM_FLAG_UNI,
100513498266Sopenharmony_ci                                &ctx->stream_bufcp, NULL);
100613498266Sopenharmony_ci  if(result) {
100713498266Sopenharmony_ci    result = CURLE_QUIC_CONNECT_ERROR;
100813498266Sopenharmony_ci    goto out;
100913498266Sopenharmony_ci  }
101013498266Sopenharmony_ci  result = cf_osslq_stream_open(&h3->s_qpack_dec, conn,
101113498266Sopenharmony_ci                                SSL_STREAM_FLAG_ADVANCE|SSL_STREAM_FLAG_UNI,
101213498266Sopenharmony_ci                                &ctx->stream_bufcp, NULL);
101313498266Sopenharmony_ci  if(result) {
101413498266Sopenharmony_ci    result = CURLE_QUIC_CONNECT_ERROR;
101513498266Sopenharmony_ci    goto out;
101613498266Sopenharmony_ci  }
101713498266Sopenharmony_ci
101813498266Sopenharmony_ci  rc = nghttp3_conn_bind_control_stream(h3->conn, h3->s_ctrl.id);
101913498266Sopenharmony_ci  if(rc) {
102013498266Sopenharmony_ci    result = CURLE_QUIC_CONNECT_ERROR;
102113498266Sopenharmony_ci    goto out;
102213498266Sopenharmony_ci  }
102313498266Sopenharmony_ci  rc = nghttp3_conn_bind_qpack_streams(h3->conn, h3->s_qpack_enc.id,
102413498266Sopenharmony_ci                                       h3->s_qpack_dec.id);
102513498266Sopenharmony_ci  if(rc) {
102613498266Sopenharmony_ci    result = CURLE_QUIC_CONNECT_ERROR;
102713498266Sopenharmony_ci    goto out;
102813498266Sopenharmony_ci  }
102913498266Sopenharmony_ci
103013498266Sopenharmony_ci  result = CURLE_OK;
103113498266Sopenharmony_ciout:
103213498266Sopenharmony_ci  return result;
103313498266Sopenharmony_ci}
103413498266Sopenharmony_ci
103513498266Sopenharmony_cistatic CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf,
103613498266Sopenharmony_ci                                   struct Curl_easy *data)
103713498266Sopenharmony_ci{
103813498266Sopenharmony_ci  struct cf_osslq_ctx *ctx = cf->ctx;
103913498266Sopenharmony_ci  CURLcode result;
104013498266Sopenharmony_ci  int rv;
104113498266Sopenharmony_ci  const struct Curl_sockaddr_ex *peer_addr = NULL;
104213498266Sopenharmony_ci  int peer_port;
104313498266Sopenharmony_ci  BIO *bio = NULL;
104413498266Sopenharmony_ci  BIO_ADDR *baddr = NULL;
104513498266Sopenharmony_ci
104613498266Sopenharmony_ci  Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE,
104713498266Sopenharmony_ci                  H3_STREAM_POOL_SPARES);
104813498266Sopenharmony_ci  result = Curl_ssl_peer_init(&ctx->peer, cf);
104913498266Sopenharmony_ci  if(result)
105013498266Sopenharmony_ci    goto out;
105113498266Sopenharmony_ci
105213498266Sopenharmony_ci#define H3_ALPN "\x2h3"
105313498266Sopenharmony_ci  result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer,
105413498266Sopenharmony_ci                               H3_ALPN, sizeof(H3_ALPN) - 1,
105513498266Sopenharmony_ci                               NULL, NULL);
105613498266Sopenharmony_ci  if(result)
105713498266Sopenharmony_ci    goto out;
105813498266Sopenharmony_ci
105913498266Sopenharmony_ci  result = vquic_ctx_init(&ctx->q);
106013498266Sopenharmony_ci  if(result)
106113498266Sopenharmony_ci    goto out;
106213498266Sopenharmony_ci
106313498266Sopenharmony_ci  result = CURLE_QUIC_CONNECT_ERROR;
106413498266Sopenharmony_ci  Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd,
106513498266Sopenharmony_ci                      &peer_addr, NULL, &peer_port, NULL, NULL);
106613498266Sopenharmony_ci  if(!peer_addr)
106713498266Sopenharmony_ci    goto out;
106813498266Sopenharmony_ci
106913498266Sopenharmony_ci  ctx->q.local_addrlen = sizeof(ctx->q.local_addr);
107013498266Sopenharmony_ci  rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr,
107113498266Sopenharmony_ci                   &ctx->q.local_addrlen);
107213498266Sopenharmony_ci  if(rv == -1)
107313498266Sopenharmony_ci    goto out;
107413498266Sopenharmony_ci
107513498266Sopenharmony_ci  result = make_bio_addr(&baddr, peer_addr);
107613498266Sopenharmony_ci  if(result) {
107713498266Sopenharmony_ci    failf(data, "error creating BIO_ADDR from sockaddr");
107813498266Sopenharmony_ci    goto out;
107913498266Sopenharmony_ci  }
108013498266Sopenharmony_ci
108113498266Sopenharmony_ci  bio = BIO_new_dgram(ctx->q.sockfd, BIO_NOCLOSE);
108213498266Sopenharmony_ci  if(!bio) {
108313498266Sopenharmony_ci    result = CURLE_OUT_OF_MEMORY;
108413498266Sopenharmony_ci    goto out;
108513498266Sopenharmony_ci  }
108613498266Sopenharmony_ci
108713498266Sopenharmony_ci  if(!SSL_set1_initial_peer_addr(ctx->tls.ssl, baddr)) {
108813498266Sopenharmony_ci    failf(data, "failed to set the initial peer address");
108913498266Sopenharmony_ci    result = CURLE_FAILED_INIT;
109013498266Sopenharmony_ci    goto out;
109113498266Sopenharmony_ci  }
109213498266Sopenharmony_ci  if(!SSL_set_blocking_mode(ctx->tls.ssl, 0)) {
109313498266Sopenharmony_ci    failf(data, "failed to turn off blocking mode");
109413498266Sopenharmony_ci    result = CURLE_FAILED_INIT;
109513498266Sopenharmony_ci    goto out;
109613498266Sopenharmony_ci  }
109713498266Sopenharmony_ci
109813498266Sopenharmony_ci  SSL_set_bio(ctx->tls.ssl, bio, bio);
109913498266Sopenharmony_ci  bio = NULL;
110013498266Sopenharmony_ci  SSL_set_connect_state(ctx->tls.ssl);
110113498266Sopenharmony_ci  SSL_set_incoming_stream_policy(ctx->tls.ssl,
110213498266Sopenharmony_ci                                 SSL_INCOMING_STREAM_POLICY_ACCEPT, 0);
110313498266Sopenharmony_ci  /* setup the H3 things on top of the QUIC connection */
110413498266Sopenharmony_ci  result = cf_osslq_h3conn_init(ctx, ctx->tls.ssl, cf);
110513498266Sopenharmony_ci
110613498266Sopenharmony_ciout:
110713498266Sopenharmony_ci  if(bio)
110813498266Sopenharmony_ci    BIO_free(bio);
110913498266Sopenharmony_ci  if(baddr)
111013498266Sopenharmony_ci    BIO_ADDR_free(baddr);
111113498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "QUIC tls init -> %d", result);
111213498266Sopenharmony_ci  return result;
111313498266Sopenharmony_ci}
111413498266Sopenharmony_ci
111513498266Sopenharmony_cistruct h3_quic_recv_ctx {
111613498266Sopenharmony_ci  struct Curl_cfilter *cf;
111713498266Sopenharmony_ci  struct Curl_easy *data;
111813498266Sopenharmony_ci  struct cf_osslq_stream *s;
111913498266Sopenharmony_ci};
112013498266Sopenharmony_ci
112113498266Sopenharmony_cistatic ssize_t h3_quic_recv(void *reader_ctx,
112213498266Sopenharmony_ci                            unsigned char *buf, size_t len,
112313498266Sopenharmony_ci                            CURLcode *err)
112413498266Sopenharmony_ci{
112513498266Sopenharmony_ci  struct h3_quic_recv_ctx *x = reader_ctx;
112613498266Sopenharmony_ci  size_t nread;
112713498266Sopenharmony_ci  int rv;
112813498266Sopenharmony_ci
112913498266Sopenharmony_ci  *err = CURLE_OK;
113013498266Sopenharmony_ci  rv = SSL_read_ex(x->s->ssl, buf, len, &nread);
113113498266Sopenharmony_ci  if(rv <= 0) {
113213498266Sopenharmony_ci    int detail = SSL_get_error(x->s->ssl, rv);
113313498266Sopenharmony_ci    if(detail == SSL_ERROR_WANT_READ || detail == SSL_ERROR_WANT_WRITE) {
113413498266Sopenharmony_ci      *err = CURLE_AGAIN;
113513498266Sopenharmony_ci      return -1;
113613498266Sopenharmony_ci    }
113713498266Sopenharmony_ci    else if(detail == SSL_ERROR_ZERO_RETURN) {
113813498266Sopenharmony_ci      CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] h3_quic_recv -> EOS",
113913498266Sopenharmony_ci                  x->s->id);
114013498266Sopenharmony_ci      x->s->recvd_eos = TRUE;
114113498266Sopenharmony_ci      return 0;
114213498266Sopenharmony_ci    }
114313498266Sopenharmony_ci    else if(SSL_get_stream_read_state(x->s->ssl) ==
114413498266Sopenharmony_ci            SSL_STREAM_STATE_RESET_REMOTE) {
114513498266Sopenharmony_ci      uint64_t app_error_code = NGHTTP3_H3_NO_ERROR;
114613498266Sopenharmony_ci      SSL_get_stream_read_error_code(x->s->ssl, &app_error_code);
114713498266Sopenharmony_ci      CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] h3_quic_recv -> RESET, "
114813498266Sopenharmony_ci                  "rv=%d, app_err=%" PRIu64,
114913498266Sopenharmony_ci                   x->s->id, rv, app_error_code);
115013498266Sopenharmony_ci      if(app_error_code != NGHTTP3_H3_NO_ERROR) {
115113498266Sopenharmony_ci        x->s->reset = TRUE;
115213498266Sopenharmony_ci      }
115313498266Sopenharmony_ci      x->s->recvd_eos = TRUE;
115413498266Sopenharmony_ci      return 0;
115513498266Sopenharmony_ci    }
115613498266Sopenharmony_ci    else {
115713498266Sopenharmony_ci      *err = cf_osslq_ssl_err(x->cf, x->data, detail, CURLE_RECV_ERROR);
115813498266Sopenharmony_ci      return -1;
115913498266Sopenharmony_ci    }
116013498266Sopenharmony_ci  }
116113498266Sopenharmony_ci  else {
116213498266Sopenharmony_ci    /* CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] h3_quic_recv -> %zu bytes",
116313498266Sopenharmony_ci                x->s->id, nread); */
116413498266Sopenharmony_ci  }
116513498266Sopenharmony_ci  return (ssize_t)nread;
116613498266Sopenharmony_ci}
116713498266Sopenharmony_ci
116813498266Sopenharmony_cistatic CURLcode cf_osslq_stream_recv(struct cf_osslq_stream *s,
116913498266Sopenharmony_ci                                     struct Curl_cfilter *cf,
117013498266Sopenharmony_ci                                     struct Curl_easy *data)
117113498266Sopenharmony_ci{
117213498266Sopenharmony_ci  struct cf_osslq_ctx *ctx = cf->ctx;
117313498266Sopenharmony_ci  CURLcode result = CURLE_OK;
117413498266Sopenharmony_ci  ssize_t nread;
117513498266Sopenharmony_ci  struct h3_quic_recv_ctx x;
117613498266Sopenharmony_ci  int rv, eagain = FALSE;
117713498266Sopenharmony_ci  size_t total_recv_len = 0;
117813498266Sopenharmony_ci
117913498266Sopenharmony_ci  DEBUGASSERT(s);
118013498266Sopenharmony_ci  if(s->closed)
118113498266Sopenharmony_ci    return CURLE_OK;
118213498266Sopenharmony_ci
118313498266Sopenharmony_ci  x.cf = cf;
118413498266Sopenharmony_ci  x.data = data;
118513498266Sopenharmony_ci  x.s = s;
118613498266Sopenharmony_ci  while(s->ssl && !s->closed && !eagain &&
118713498266Sopenharmony_ci        (total_recv_len < H3_STREAM_CHUNK_SIZE)) {
118813498266Sopenharmony_ci    if(Curl_bufq_is_empty(&s->recvbuf) && !s->recvd_eos) {
118913498266Sopenharmony_ci      while(!eagain && !s->recvd_eos && !Curl_bufq_is_full(&s->recvbuf)) {
119013498266Sopenharmony_ci        nread = Curl_bufq_sipn(&s->recvbuf, 0, h3_quic_recv, &x, &result);
119113498266Sopenharmony_ci        if(nread < 0) {
119213498266Sopenharmony_ci          if(result != CURLE_AGAIN)
119313498266Sopenharmony_ci            goto out;
119413498266Sopenharmony_ci          result = CURLE_OK;
119513498266Sopenharmony_ci          eagain = TRUE;
119613498266Sopenharmony_ci        }
119713498266Sopenharmony_ci      }
119813498266Sopenharmony_ci    }
119913498266Sopenharmony_ci
120013498266Sopenharmony_ci    /* Forward what we have to nghttp3 */
120113498266Sopenharmony_ci    if(!Curl_bufq_is_empty(&s->recvbuf)) {
120213498266Sopenharmony_ci      const unsigned char *buf;
120313498266Sopenharmony_ci      size_t blen;
120413498266Sopenharmony_ci
120513498266Sopenharmony_ci      while(Curl_bufq_peek(&s->recvbuf, &buf, &blen)) {
120613498266Sopenharmony_ci        nread = nghttp3_conn_read_stream(ctx->h3.conn, s->id,
120713498266Sopenharmony_ci                                         buf, blen, 0);
120813498266Sopenharmony_ci        CURL_TRC_CF(data, cf, "[%" PRId64 "] forward %zu bytes "
120913498266Sopenharmony_ci                    "to nghttp3 -> %zd", s->id, blen, nread);
121013498266Sopenharmony_ci        if(nread < 0) {
121113498266Sopenharmony_ci          failf(data, "nghttp3_conn_read_stream(len=%zu) error: %s",
121213498266Sopenharmony_ci                blen, nghttp3_strerror((int)nread));
121313498266Sopenharmony_ci          result = CURLE_RECV_ERROR;
121413498266Sopenharmony_ci          goto out;
121513498266Sopenharmony_ci        }
121613498266Sopenharmony_ci        /* success, `nread` is the flow for QUIC to count as "consumed",
121713498266Sopenharmony_ci         * not sure how that will work with OpenSSL. Anyways, without error,
121813498266Sopenharmony_ci         * all data that we passed is not owned by nghttp3. */
121913498266Sopenharmony_ci        Curl_bufq_skip(&s->recvbuf, blen);
122013498266Sopenharmony_ci        total_recv_len += blen;
122113498266Sopenharmony_ci      }
122213498266Sopenharmony_ci    }
122313498266Sopenharmony_ci
122413498266Sopenharmony_ci    /* When we forwarded everything, handle RESET/EOS */
122513498266Sopenharmony_ci    if(Curl_bufq_is_empty(&s->recvbuf) && !s->closed) {
122613498266Sopenharmony_ci      result = CURLE_OK;
122713498266Sopenharmony_ci      if(s->reset) {
122813498266Sopenharmony_ci        uint64_t app_error;
122913498266Sopenharmony_ci        if(!SSL_get_stream_read_error_code(s->ssl, &app_error)) {
123013498266Sopenharmony_ci          failf(data, "SSL_get_stream_read_error_code returned error");
123113498266Sopenharmony_ci          result = CURLE_RECV_ERROR;
123213498266Sopenharmony_ci          goto out;
123313498266Sopenharmony_ci        }
123413498266Sopenharmony_ci        rv = nghttp3_conn_close_stream(ctx->h3.conn, s->id, app_error);
123513498266Sopenharmony_ci        s->closed = TRUE;
123613498266Sopenharmony_ci        if(rv < 0 && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
123713498266Sopenharmony_ci          failf(data, "nghttp3_conn_close_stream returned error: %s",
123813498266Sopenharmony_ci                nghttp3_strerror(rv));
123913498266Sopenharmony_ci          result = CURLE_RECV_ERROR;
124013498266Sopenharmony_ci          goto out;
124113498266Sopenharmony_ci        }
124213498266Sopenharmony_ci      }
124313498266Sopenharmony_ci      else if(s->recvd_eos) {
124413498266Sopenharmony_ci        rv = nghttp3_conn_close_stream(ctx->h3.conn, s->id,
124513498266Sopenharmony_ci                                       NGHTTP3_H3_NO_ERROR);
124613498266Sopenharmony_ci        s->closed = TRUE;
124713498266Sopenharmony_ci        CURL_TRC_CF(data, cf, "[%" PRId64 "] close nghttp3 stream -> %d",
124813498266Sopenharmony_ci                    s->id, rv);
124913498266Sopenharmony_ci        if(rv < 0 && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
125013498266Sopenharmony_ci          failf(data, "nghttp3_conn_close_stream returned error: %s",
125113498266Sopenharmony_ci                nghttp3_strerror(rv));
125213498266Sopenharmony_ci          result = CURLE_RECV_ERROR;
125313498266Sopenharmony_ci          goto out;
125413498266Sopenharmony_ci        }
125513498266Sopenharmony_ci      }
125613498266Sopenharmony_ci    }
125713498266Sopenharmony_ci  }
125813498266Sopenharmony_ciout:
125913498266Sopenharmony_ci  if(result)
126013498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_osslq_stream_recv -> %d",
126113498266Sopenharmony_ci                s->id, result);
126213498266Sopenharmony_ci  return result;
126313498266Sopenharmony_ci}
126413498266Sopenharmony_ci
126513498266Sopenharmony_cistatic CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
126613498266Sopenharmony_ci                                    struct Curl_easy *data)
126713498266Sopenharmony_ci{
126813498266Sopenharmony_ci  struct cf_osslq_ctx *ctx = cf->ctx;
126913498266Sopenharmony_ci  CURLcode result = CURLE_OK;
127013498266Sopenharmony_ci
127113498266Sopenharmony_ci  if(!ctx->tls.ssl)
127213498266Sopenharmony_ci    goto out;
127313498266Sopenharmony_ci
127413498266Sopenharmony_ci  ERR_clear_error();
127513498266Sopenharmony_ci
127613498266Sopenharmony_ci  /* 1. Check for new incoming streams */
127713498266Sopenharmony_ci  while(1) {
127813498266Sopenharmony_ci    SSL *snew = SSL_accept_stream(ctx->tls.ssl, SSL_ACCEPT_STREAM_NO_BLOCK);
127913498266Sopenharmony_ci    if(!snew)
128013498266Sopenharmony_ci      break;
128113498266Sopenharmony_ci
128213498266Sopenharmony_ci    (void)cf_osslq_h3conn_add_stream(&ctx->h3, snew, cf, data);
128313498266Sopenharmony_ci  }
128413498266Sopenharmony_ci
128513498266Sopenharmony_ci  if(!SSL_handle_events(ctx->tls.ssl)) {
128613498266Sopenharmony_ci    int detail = SSL_get_error(ctx->tls.ssl, 0);
128713498266Sopenharmony_ci    result = cf_osslq_ssl_err(cf, data, detail, CURLE_RECV_ERROR);
128813498266Sopenharmony_ci  }
128913498266Sopenharmony_ci
129013498266Sopenharmony_ci  if(ctx->h3.conn) {
129113498266Sopenharmony_ci    size_t i;
129213498266Sopenharmony_ci    for(i = 0; i < ctx->h3.remote_ctrl_n; ++i) {
129313498266Sopenharmony_ci      result = cf_osslq_stream_recv(&ctx->h3.remote_ctrl[i], cf, data);
129413498266Sopenharmony_ci      if(result)
129513498266Sopenharmony_ci        goto out;
129613498266Sopenharmony_ci    }
129713498266Sopenharmony_ci  }
129813498266Sopenharmony_ci
129913498266Sopenharmony_ci  if(ctx->h3.conn) {
130013498266Sopenharmony_ci    struct Curl_easy *sdata;
130113498266Sopenharmony_ci    struct h3_stream_ctx *stream;
130213498266Sopenharmony_ci    /* PULL all open streams */
130313498266Sopenharmony_ci    DEBUGASSERT(data->multi);
130413498266Sopenharmony_ci    for(sdata = data->multi->easyp; sdata; sdata = sdata->next) {
130513498266Sopenharmony_ci      if(sdata->conn == data->conn && CURL_WANT_RECV(sdata)) {
130613498266Sopenharmony_ci        stream = H3_STREAM_CTX(sdata);
130713498266Sopenharmony_ci        if(stream && !stream->closed &&
130813498266Sopenharmony_ci           !Curl_bufq_is_full(&stream->recvbuf)) {
130913498266Sopenharmony_ci          result = cf_osslq_stream_recv(&stream->s, cf, sdata);
131013498266Sopenharmony_ci          if(result)
131113498266Sopenharmony_ci            goto out;
131213498266Sopenharmony_ci        }
131313498266Sopenharmony_ci      }
131413498266Sopenharmony_ci    }
131513498266Sopenharmony_ci  }
131613498266Sopenharmony_ci
131713498266Sopenharmony_ciout:
131813498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "progress_ingress -> %d", result);
131913498266Sopenharmony_ci  return result;
132013498266Sopenharmony_ci}
132113498266Sopenharmony_ci
132213498266Sopenharmony_ci/* Iterate over all streams and check if blocked can be unblocked */
132313498266Sopenharmony_cistatic CURLcode cf_osslq_check_and_unblock(struct Curl_cfilter *cf,
132413498266Sopenharmony_ci                                           struct Curl_easy *data)
132513498266Sopenharmony_ci{
132613498266Sopenharmony_ci  struct cf_osslq_ctx *ctx = cf->ctx;
132713498266Sopenharmony_ci  struct Curl_easy *sdata;
132813498266Sopenharmony_ci  struct h3_stream_ctx *stream;
132913498266Sopenharmony_ci
133013498266Sopenharmony_ci  if(ctx->h3.conn) {
133113498266Sopenharmony_ci    for(sdata = data->multi->easyp; sdata; sdata = sdata->next) {
133213498266Sopenharmony_ci      if(sdata->conn == data->conn) {
133313498266Sopenharmony_ci        stream = H3_STREAM_CTX(sdata);
133413498266Sopenharmony_ci        if(stream && stream->s.ssl && stream->s.send_blocked &&
133513498266Sopenharmony_ci           !SSL_want_write(stream->s.ssl)) {
133613498266Sopenharmony_ci          nghttp3_conn_unblock_stream(ctx->h3.conn, stream->s.id);
133713498266Sopenharmony_ci          stream->s.send_blocked = FALSE;
133813498266Sopenharmony_ci          h3_drain_stream(cf, sdata);
133913498266Sopenharmony_ci          CURL_TRC_CF(sdata, cf, "unblocked");
134013498266Sopenharmony_ci        }
134113498266Sopenharmony_ci      }
134213498266Sopenharmony_ci    }
134313498266Sopenharmony_ci  }
134413498266Sopenharmony_ci  return CURLE_OK;
134513498266Sopenharmony_ci}
134613498266Sopenharmony_ci
134713498266Sopenharmony_cistatic CURLcode h3_send_streams(struct Curl_cfilter *cf,
134813498266Sopenharmony_ci                                struct Curl_easy *data)
134913498266Sopenharmony_ci{
135013498266Sopenharmony_ci  struct cf_osslq_ctx *ctx = cf->ctx;
135113498266Sopenharmony_ci  CURLcode result = CURLE_OK;
135213498266Sopenharmony_ci
135313498266Sopenharmony_ci  if(!ctx->tls.ssl || !ctx->h3.conn)
135413498266Sopenharmony_ci    goto out;
135513498266Sopenharmony_ci
135613498266Sopenharmony_ci  for(;;) {
135713498266Sopenharmony_ci    struct cf_osslq_stream *s = NULL;
135813498266Sopenharmony_ci    nghttp3_vec vec[16];
135913498266Sopenharmony_ci    nghttp3_ssize n, i;
136013498266Sopenharmony_ci    int64_t stream_id;
136113498266Sopenharmony_ci    size_t written;
136213498266Sopenharmony_ci    int eos, ok, rv;
136313498266Sopenharmony_ci    size_t total_len, acked_len = 0;
136413498266Sopenharmony_ci    bool blocked = FALSE;
136513498266Sopenharmony_ci
136613498266Sopenharmony_ci    n = nghttp3_conn_writev_stream(ctx->h3.conn, &stream_id, &eos,
136713498266Sopenharmony_ci                                   vec, ARRAYSIZE(vec));
136813498266Sopenharmony_ci    if(n < 0) {
136913498266Sopenharmony_ci      failf(data, "nghttp3_conn_writev_stream returned error: %s",
137013498266Sopenharmony_ci            nghttp3_strerror((int)n));
137113498266Sopenharmony_ci      result = CURLE_SEND_ERROR;
137213498266Sopenharmony_ci      goto out;
137313498266Sopenharmony_ci    }
137413498266Sopenharmony_ci    if(stream_id < 0) {
137513498266Sopenharmony_ci      result = CURLE_OK;
137613498266Sopenharmony_ci      goto out;
137713498266Sopenharmony_ci    }
137813498266Sopenharmony_ci
137913498266Sopenharmony_ci    /* Get the stream for this data */
138013498266Sopenharmony_ci    s = cf_osslq_get_qstream(cf, data, stream_id);
138113498266Sopenharmony_ci    if(!s) {
138213498266Sopenharmony_ci      failf(data, "nghttp3_conn_writev_stream gave unknown stream %" PRId64,
138313498266Sopenharmony_ci            stream_id);
138413498266Sopenharmony_ci      result = CURLE_SEND_ERROR;
138513498266Sopenharmony_ci      goto out;
138613498266Sopenharmony_ci    }
138713498266Sopenharmony_ci    /* Now write the data to the stream's SSL*, it may not all fit! */
138813498266Sopenharmony_ci    DEBUGASSERT(s->id == stream_id);
138913498266Sopenharmony_ci    for(i = 0, total_len = 0; i < n; ++i) {
139013498266Sopenharmony_ci      total_len += vec[i].len;
139113498266Sopenharmony_ci    }
139213498266Sopenharmony_ci    for(i = 0; (i < n) && !blocked; ++i) {
139313498266Sopenharmony_ci      /* Without stream->s.ssl, we closed that already, so
139413498266Sopenharmony_ci       * pretend the write did succeed. */
139513498266Sopenharmony_ci      written = vec[i].len;
139613498266Sopenharmony_ci      ok = !s->ssl || SSL_write_ex(s->ssl, vec[i].base, vec[i].len,
139713498266Sopenharmony_ci                                   &written);
139813498266Sopenharmony_ci      if(ok) {
139913498266Sopenharmony_ci        /* As OpenSSL buffers the data, we count this as acknowledged
140013498266Sopenharmony_ci         * from nghttp3's point of view */
140113498266Sopenharmony_ci        CURL_TRC_CF(data, cf, "[%"PRId64"] send %zu bytes to QUIC ok",
140213498266Sopenharmony_ci              s->id, vec[i].len);
140313498266Sopenharmony_ci        acked_len += vec[i].len;
140413498266Sopenharmony_ci      }
140513498266Sopenharmony_ci      else {
140613498266Sopenharmony_ci        int detail = SSL_get_error(s->ssl, 0);
140713498266Sopenharmony_ci        switch(detail) {
140813498266Sopenharmony_ci        case SSL_ERROR_WANT_WRITE:
140913498266Sopenharmony_ci        case SSL_ERROR_WANT_READ:
141013498266Sopenharmony_ci          /* QUIC blocked us from writing more */
141113498266Sopenharmony_ci          CURL_TRC_CF(data, cf, "[%"PRId64"] send %zu bytes to QUIC blocked",
141213498266Sopenharmony_ci                s->id, vec[i].len);
141313498266Sopenharmony_ci          written = 0;
141413498266Sopenharmony_ci          nghttp3_conn_block_stream(ctx->h3.conn, s->id);
141513498266Sopenharmony_ci          s->send_blocked = blocked = TRUE;
141613498266Sopenharmony_ci          break;
141713498266Sopenharmony_ci        default:
141813498266Sopenharmony_ci          failf(data, "[%"PRId64"] send %zu bytes to QUIC, SSL error %d",
141913498266Sopenharmony_ci                s->id, vec[i].len, detail);
142013498266Sopenharmony_ci          result = cf_osslq_ssl_err(cf, data, detail, CURLE_SEND_ERROR);
142113498266Sopenharmony_ci          goto out;
142213498266Sopenharmony_ci        }
142313498266Sopenharmony_ci      }
142413498266Sopenharmony_ci    }
142513498266Sopenharmony_ci
142613498266Sopenharmony_ci    if(acked_len > 0 || (eos && !s->send_blocked)) {
142713498266Sopenharmony_ci      /* Since QUIC buffers the data written internally, we can tell
142813498266Sopenharmony_ci       * nghttp3 that it can move forward on it */
142913498266Sopenharmony_ci      rv = nghttp3_conn_add_write_offset(ctx->h3.conn, s->id, acked_len);
143013498266Sopenharmony_ci      if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
143113498266Sopenharmony_ci        failf(data, "nghttp3_conn_add_write_offset returned error: %s\n",
143213498266Sopenharmony_ci              nghttp3_strerror(rv));
143313498266Sopenharmony_ci        result = CURLE_SEND_ERROR;
143413498266Sopenharmony_ci        goto out;
143513498266Sopenharmony_ci      }
143613498266Sopenharmony_ci      rv = nghttp3_conn_add_ack_offset(ctx->h3.conn, s->id, acked_len);
143713498266Sopenharmony_ci      if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
143813498266Sopenharmony_ci        failf(data, "nghttp3_conn_add_ack_offset returned error: %s\n",
143913498266Sopenharmony_ci              nghttp3_strerror(rv));
144013498266Sopenharmony_ci        result = CURLE_SEND_ERROR;
144113498266Sopenharmony_ci        goto out;
144213498266Sopenharmony_ci      }
144313498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "[%" PRId64 "] forwarded %zu/%zu h3 bytes "
144413498266Sopenharmony_ci                  "to QUIC, eos=%d", s->id, acked_len, total_len, eos);
144513498266Sopenharmony_ci    }
144613498266Sopenharmony_ci
144713498266Sopenharmony_ci    if(eos && !s->send_blocked) {
144813498266Sopenharmony_ci      /* wrote everything and H3 indicates end of stream */
144913498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "[%" PRId64 "] closing QUIC stream", s->id);
145013498266Sopenharmony_ci      SSL_stream_conclude(s->ssl, 0);
145113498266Sopenharmony_ci    }
145213498266Sopenharmony_ci  }
145313498266Sopenharmony_ci
145413498266Sopenharmony_ciout:
145513498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "h3_send_streams -> %d", result);
145613498266Sopenharmony_ci  return result;
145713498266Sopenharmony_ci}
145813498266Sopenharmony_ci
145913498266Sopenharmony_cistatic CURLcode cf_progress_egress(struct Curl_cfilter *cf,
146013498266Sopenharmony_ci                                   struct Curl_easy *data)
146113498266Sopenharmony_ci{
146213498266Sopenharmony_ci  struct cf_osslq_ctx *ctx = cf->ctx;
146313498266Sopenharmony_ci  CURLcode result = CURLE_OK;
146413498266Sopenharmony_ci
146513498266Sopenharmony_ci  if(!ctx->tls.ssl)
146613498266Sopenharmony_ci    goto out;
146713498266Sopenharmony_ci
146813498266Sopenharmony_ci  ERR_clear_error();
146913498266Sopenharmony_ci  result = h3_send_streams(cf, data);
147013498266Sopenharmony_ci  if(result)
147113498266Sopenharmony_ci    goto out;
147213498266Sopenharmony_ci
147313498266Sopenharmony_ci  if(!SSL_handle_events(ctx->tls.ssl)) {
147413498266Sopenharmony_ci    int detail = SSL_get_error(ctx->tls.ssl, 0);
147513498266Sopenharmony_ci    result = cf_osslq_ssl_err(cf, data, detail, CURLE_SEND_ERROR);
147613498266Sopenharmony_ci  }
147713498266Sopenharmony_ci
147813498266Sopenharmony_ci  result = cf_osslq_check_and_unblock(cf, data);
147913498266Sopenharmony_ci
148013498266Sopenharmony_ciout:
148113498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "progress_egress -> %d", result);
148213498266Sopenharmony_ci  return result;
148313498266Sopenharmony_ci}
148413498266Sopenharmony_ci
148513498266Sopenharmony_cistatic CURLcode check_and_set_expiry(struct Curl_cfilter *cf,
148613498266Sopenharmony_ci                                     struct Curl_easy *data)
148713498266Sopenharmony_ci{
148813498266Sopenharmony_ci  struct cf_osslq_ctx *ctx = cf->ctx;
148913498266Sopenharmony_ci  CURLcode result = CURLE_OK;
149013498266Sopenharmony_ci  struct timeval tv;
149113498266Sopenharmony_ci  timediff_t timeoutms;
149213498266Sopenharmony_ci  int is_infinite = TRUE;
149313498266Sopenharmony_ci
149413498266Sopenharmony_ci  if(ctx->tls.ssl &&
149513498266Sopenharmony_ci    SSL_get_event_timeout(ctx->tls.ssl, &tv, &is_infinite) &&
149613498266Sopenharmony_ci    !is_infinite) {
149713498266Sopenharmony_ci    timeoutms = curlx_tvtoms(&tv);
149813498266Sopenharmony_ci    /* QUIC want to be called again latest at the returned timeout */
149913498266Sopenharmony_ci    if(timeoutms <= 0) {
150013498266Sopenharmony_ci      result = cf_progress_ingress(cf, data);
150113498266Sopenharmony_ci      if(result)
150213498266Sopenharmony_ci        goto out;
150313498266Sopenharmony_ci      result = cf_progress_egress(cf, data);
150413498266Sopenharmony_ci      if(result)
150513498266Sopenharmony_ci        goto out;
150613498266Sopenharmony_ci      if(SSL_get_event_timeout(ctx->tls.ssl, &tv, &is_infinite)) {
150713498266Sopenharmony_ci        timeoutms = curlx_tvtoms(&tv);
150813498266Sopenharmony_ci      }
150913498266Sopenharmony_ci    }
151013498266Sopenharmony_ci    if(!is_infinite) {
151113498266Sopenharmony_ci      Curl_expire(data, timeoutms, EXPIRE_QUIC);
151213498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "QUIC expiry in %ldms", (long)timeoutms);
151313498266Sopenharmony_ci    }
151413498266Sopenharmony_ci  }
151513498266Sopenharmony_ciout:
151613498266Sopenharmony_ci  return result;
151713498266Sopenharmony_ci}
151813498266Sopenharmony_ci
151913498266Sopenharmony_cistatic CURLcode cf_osslq_connect(struct Curl_cfilter *cf,
152013498266Sopenharmony_ci                                 struct Curl_easy *data,
152113498266Sopenharmony_ci                                 bool blocking, bool *done)
152213498266Sopenharmony_ci{
152313498266Sopenharmony_ci  struct cf_osslq_ctx *ctx = cf->ctx;
152413498266Sopenharmony_ci  CURLcode result = CURLE_OK;
152513498266Sopenharmony_ci  struct cf_call_data save;
152613498266Sopenharmony_ci  struct curltime now;
152713498266Sopenharmony_ci  int err;
152813498266Sopenharmony_ci
152913498266Sopenharmony_ci  if(cf->connected) {
153013498266Sopenharmony_ci    *done = TRUE;
153113498266Sopenharmony_ci    return CURLE_OK;
153213498266Sopenharmony_ci  }
153313498266Sopenharmony_ci
153413498266Sopenharmony_ci  /* Connect the UDP filter first */
153513498266Sopenharmony_ci  if(!cf->next->connected) {
153613498266Sopenharmony_ci    result = Curl_conn_cf_connect(cf->next, data, blocking, done);
153713498266Sopenharmony_ci    if(result || !*done)
153813498266Sopenharmony_ci      return result;
153913498266Sopenharmony_ci  }
154013498266Sopenharmony_ci
154113498266Sopenharmony_ci  *done = FALSE;
154213498266Sopenharmony_ci  now = Curl_now();
154313498266Sopenharmony_ci  CF_DATA_SAVE(save, cf, data);
154413498266Sopenharmony_ci
154513498266Sopenharmony_ci  if(ctx->reconnect_at.tv_sec && Curl_timediff(now, ctx->reconnect_at) < 0) {
154613498266Sopenharmony_ci    /* Not time yet to attempt the next connect */
154713498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "waiting for reconnect time");
154813498266Sopenharmony_ci    goto out;
154913498266Sopenharmony_ci  }
155013498266Sopenharmony_ci
155113498266Sopenharmony_ci  if(!ctx->tls.ssl) {
155213498266Sopenharmony_ci    ctx->started_at = now;
155313498266Sopenharmony_ci    result = cf_osslq_ctx_start(cf, data);
155413498266Sopenharmony_ci    if(result)
155513498266Sopenharmony_ci      goto out;
155613498266Sopenharmony_ci  }
155713498266Sopenharmony_ci
155813498266Sopenharmony_ci  if(!ctx->got_first_byte) {
155913498266Sopenharmony_ci    int readable = SOCKET_READABLE(ctx->q.sockfd, 0);
156013498266Sopenharmony_ci    if(readable > 0 && (readable & CURL_CSELECT_IN)) {
156113498266Sopenharmony_ci      ctx->got_first_byte = TRUE;
156213498266Sopenharmony_ci      ctx->first_byte_at = Curl_now();
156313498266Sopenharmony_ci    }
156413498266Sopenharmony_ci  }
156513498266Sopenharmony_ci
156613498266Sopenharmony_ci  ERR_clear_error();
156713498266Sopenharmony_ci  err = SSL_do_handshake(ctx->tls.ssl);
156813498266Sopenharmony_ci
156913498266Sopenharmony_ci  if(err == 1) {
157013498266Sopenharmony_ci    /* connected */
157113498266Sopenharmony_ci    ctx->handshake_at = now;
157213498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "handshake complete after %dms",
157313498266Sopenharmony_ci               (int)Curl_timediff(now, ctx->started_at));
157413498266Sopenharmony_ci    result = cf_osslq_verify_peer(cf, data);
157513498266Sopenharmony_ci    if(!result) {
157613498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "peer verified");
157713498266Sopenharmony_ci      cf->connected = TRUE;
157813498266Sopenharmony_ci      cf->conn->alpn = CURL_HTTP_VERSION_3;
157913498266Sopenharmony_ci      *done = TRUE;
158013498266Sopenharmony_ci      connkeep(cf->conn, "HTTP/3 default");
158113498266Sopenharmony_ci    }
158213498266Sopenharmony_ci  }
158313498266Sopenharmony_ci  else {
158413498266Sopenharmony_ci    int detail = SSL_get_error(ctx->tls.ssl, err);
158513498266Sopenharmony_ci    switch(detail) {
158613498266Sopenharmony_ci    case SSL_ERROR_WANT_READ:
158713498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_RECV");
158813498266Sopenharmony_ci      result = Curl_vquic_tls_before_recv(&ctx->tls, cf, data);
158913498266Sopenharmony_ci      goto out;
159013498266Sopenharmony_ci    case SSL_ERROR_WANT_WRITE:
159113498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_SEND");
159213498266Sopenharmony_ci      result = CURLE_OK;
159313498266Sopenharmony_ci      goto out;
159413498266Sopenharmony_ci#ifdef SSL_ERROR_WANT_ASYNC
159513498266Sopenharmony_ci    case SSL_ERROR_WANT_ASYNC:
159613498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_ASYNC");
159713498266Sopenharmony_ci      result = CURLE_OK;
159813498266Sopenharmony_ci      goto out;
159913498266Sopenharmony_ci#endif
160013498266Sopenharmony_ci#ifdef SSL_ERROR_WANT_RETRY_VERIFY
160113498266Sopenharmony_ci    case SSL_ERROR_WANT_RETRY_VERIFY:
160213498266Sopenharmony_ci      result = CURLE_OK;
160313498266Sopenharmony_ci      goto out;
160413498266Sopenharmony_ci#endif
160513498266Sopenharmony_ci    default:
160613498266Sopenharmony_ci      result = cf_osslq_ssl_err(cf, data, detail, CURLE_COULDNT_CONNECT);
160713498266Sopenharmony_ci      goto out;
160813498266Sopenharmony_ci    }
160913498266Sopenharmony_ci  }
161013498266Sopenharmony_ci
161113498266Sopenharmony_ciout:
161213498266Sopenharmony_ci  if(result == CURLE_RECV_ERROR && ctx->tls.ssl && ctx->protocol_shutdown) {
161313498266Sopenharmony_ci    /* When a QUIC server instance is shutting down, it may send us a
161413498266Sopenharmony_ci     * CONNECTION_CLOSE right away. Our connection then enters the DRAINING
161513498266Sopenharmony_ci     * state. The CONNECT may work in the near future again. Indicate
161613498266Sopenharmony_ci     * that as a "weird" reply. */
161713498266Sopenharmony_ci    result = CURLE_WEIRD_SERVER_REPLY;
161813498266Sopenharmony_ci  }
161913498266Sopenharmony_ci
162013498266Sopenharmony_ci#ifndef CURL_DISABLE_VERBOSE_STRINGS
162113498266Sopenharmony_ci  if(result) {
162213498266Sopenharmony_ci    const char *r_ip = NULL;
162313498266Sopenharmony_ci    int r_port = 0;
162413498266Sopenharmony_ci
162513498266Sopenharmony_ci    Curl_cf_socket_peek(cf->next, data, NULL, NULL,
162613498266Sopenharmony_ci                        &r_ip, &r_port, NULL, NULL);
162713498266Sopenharmony_ci    infof(data, "QUIC connect to %s port %u failed: %s",
162813498266Sopenharmony_ci          r_ip, r_port, curl_easy_strerror(result));
162913498266Sopenharmony_ci  }
163013498266Sopenharmony_ci#endif
163113498266Sopenharmony_ci  if(!result)
163213498266Sopenharmony_ci    result = check_and_set_expiry(cf, data);
163313498266Sopenharmony_ci  if(result || *done)
163413498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "connect -> %d, done=%d", result, *done);
163513498266Sopenharmony_ci  CF_DATA_RESTORE(cf, save);
163613498266Sopenharmony_ci  return result;
163713498266Sopenharmony_ci}
163813498266Sopenharmony_ci
163913498266Sopenharmony_cistatic ssize_t h3_stream_open(struct Curl_cfilter *cf,
164013498266Sopenharmony_ci                              struct Curl_easy *data,
164113498266Sopenharmony_ci                              const void *buf, size_t len,
164213498266Sopenharmony_ci                              CURLcode *err)
164313498266Sopenharmony_ci{
164413498266Sopenharmony_ci  struct cf_osslq_ctx *ctx = cf->ctx;
164513498266Sopenharmony_ci  struct h3_stream_ctx *stream = NULL;
164613498266Sopenharmony_ci  struct dynhds h2_headers;
164713498266Sopenharmony_ci  size_t nheader;
164813498266Sopenharmony_ci  nghttp3_nv *nva = NULL;
164913498266Sopenharmony_ci  int rc = 0;
165013498266Sopenharmony_ci  unsigned int i;
165113498266Sopenharmony_ci  ssize_t nwritten = -1;
165213498266Sopenharmony_ci  nghttp3_data_reader reader;
165313498266Sopenharmony_ci  nghttp3_data_reader *preader = NULL;
165413498266Sopenharmony_ci
165513498266Sopenharmony_ci  Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST);
165613498266Sopenharmony_ci
165713498266Sopenharmony_ci  *err = h3_data_setup(cf, data);
165813498266Sopenharmony_ci  if(*err)
165913498266Sopenharmony_ci    goto out;
166013498266Sopenharmony_ci  stream = H3_STREAM_CTX(data);
166113498266Sopenharmony_ci  DEBUGASSERT(stream);
166213498266Sopenharmony_ci  if(!stream) {
166313498266Sopenharmony_ci    *err = CURLE_FAILED_INIT;
166413498266Sopenharmony_ci    goto out;
166513498266Sopenharmony_ci  }
166613498266Sopenharmony_ci
166713498266Sopenharmony_ci  nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err);
166813498266Sopenharmony_ci  if(nwritten < 0)
166913498266Sopenharmony_ci    goto out;
167013498266Sopenharmony_ci  if(!stream->h1.done) {
167113498266Sopenharmony_ci    /* need more data */
167213498266Sopenharmony_ci    goto out;
167313498266Sopenharmony_ci  }
167413498266Sopenharmony_ci  DEBUGASSERT(stream->h1.req);
167513498266Sopenharmony_ci
167613498266Sopenharmony_ci  *err = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data);
167713498266Sopenharmony_ci  if(*err) {
167813498266Sopenharmony_ci    nwritten = -1;
167913498266Sopenharmony_ci    goto out;
168013498266Sopenharmony_ci  }
168113498266Sopenharmony_ci  /* no longer needed */
168213498266Sopenharmony_ci  Curl_h1_req_parse_free(&stream->h1);
168313498266Sopenharmony_ci
168413498266Sopenharmony_ci  nheader = Curl_dynhds_count(&h2_headers);
168513498266Sopenharmony_ci  nva = malloc(sizeof(nghttp3_nv) * nheader);
168613498266Sopenharmony_ci  if(!nva) {
168713498266Sopenharmony_ci    *err = CURLE_OUT_OF_MEMORY;
168813498266Sopenharmony_ci    nwritten = -1;
168913498266Sopenharmony_ci    goto out;
169013498266Sopenharmony_ci  }
169113498266Sopenharmony_ci
169213498266Sopenharmony_ci  for(i = 0; i < nheader; ++i) {
169313498266Sopenharmony_ci    struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i);
169413498266Sopenharmony_ci    nva[i].name = (unsigned char *)e->name;
169513498266Sopenharmony_ci    nva[i].namelen = e->namelen;
169613498266Sopenharmony_ci    nva[i].value = (unsigned char *)e->value;
169713498266Sopenharmony_ci    nva[i].valuelen = e->valuelen;
169813498266Sopenharmony_ci    nva[i].flags = NGHTTP3_NV_FLAG_NONE;
169913498266Sopenharmony_ci  }
170013498266Sopenharmony_ci
170113498266Sopenharmony_ci  DEBUGASSERT(stream->s.id == -1);
170213498266Sopenharmony_ci  *err = cf_osslq_stream_open(&stream->s, ctx->tls.ssl, 0,
170313498266Sopenharmony_ci                              &ctx->stream_bufcp, data);
170413498266Sopenharmony_ci  if(*err) {
170513498266Sopenharmony_ci    failf(data, "can't get bidi streams");
170613498266Sopenharmony_ci    *err = CURLE_SEND_ERROR;
170713498266Sopenharmony_ci    goto out;
170813498266Sopenharmony_ci  }
170913498266Sopenharmony_ci
171013498266Sopenharmony_ci  switch(data->state.httpreq) {
171113498266Sopenharmony_ci  case HTTPREQ_POST:
171213498266Sopenharmony_ci  case HTTPREQ_POST_FORM:
171313498266Sopenharmony_ci  case HTTPREQ_POST_MIME:
171413498266Sopenharmony_ci  case HTTPREQ_PUT:
171513498266Sopenharmony_ci    /* known request body size or -1 */
171613498266Sopenharmony_ci    if(data->state.infilesize != -1)
171713498266Sopenharmony_ci      stream->upload_left = data->state.infilesize;
171813498266Sopenharmony_ci    else
171913498266Sopenharmony_ci      /* data sending without specifying the data amount up front */
172013498266Sopenharmony_ci      stream->upload_left = -1; /* unknown */
172113498266Sopenharmony_ci    break;
172213498266Sopenharmony_ci  default:
172313498266Sopenharmony_ci    /* there is not request body */
172413498266Sopenharmony_ci    stream->upload_left = 0; /* no request body */
172513498266Sopenharmony_ci    break;
172613498266Sopenharmony_ci  }
172713498266Sopenharmony_ci
172813498266Sopenharmony_ci  stream->send_closed = (stream->upload_left == 0);
172913498266Sopenharmony_ci  if(!stream->send_closed) {
173013498266Sopenharmony_ci    reader.read_data = cb_h3_read_req_body;
173113498266Sopenharmony_ci    preader = &reader;
173213498266Sopenharmony_ci  }
173313498266Sopenharmony_ci
173413498266Sopenharmony_ci  rc = nghttp3_conn_submit_request(ctx->h3.conn, stream->s.id,
173513498266Sopenharmony_ci                                   nva, nheader, preader, data);
173613498266Sopenharmony_ci  if(rc) {
173713498266Sopenharmony_ci    switch(rc) {
173813498266Sopenharmony_ci    case NGHTTP3_ERR_CONN_CLOSING:
173913498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "h3sid[%"PRId64"] failed to send, "
174013498266Sopenharmony_ci                  "connection is closing", stream->s.id);
174113498266Sopenharmony_ci      break;
174213498266Sopenharmony_ci    default:
174313498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "h3sid[%"PRId64"] failed to send -> %d (%s)",
174413498266Sopenharmony_ci                  stream->s.id, rc, nghttp3_strerror(rc));
174513498266Sopenharmony_ci      break;
174613498266Sopenharmony_ci    }
174713498266Sopenharmony_ci    *err = CURLE_SEND_ERROR;
174813498266Sopenharmony_ci    nwritten = -1;
174913498266Sopenharmony_ci    goto out;
175013498266Sopenharmony_ci  }
175113498266Sopenharmony_ci
175213498266Sopenharmony_ci  if(Curl_trc_is_verbose(data)) {
175313498266Sopenharmony_ci    infof(data, "[HTTP/3] [%" PRId64 "] OPENED stream for %s",
175413498266Sopenharmony_ci          stream->s.id, data->state.url);
175513498266Sopenharmony_ci    for(i = 0; i < nheader; ++i) {
175613498266Sopenharmony_ci      infof(data, "[HTTP/3] [%" PRId64 "] [%.*s: %.*s]", stream->s.id,
175713498266Sopenharmony_ci            (int)nva[i].namelen, nva[i].name,
175813498266Sopenharmony_ci            (int)nva[i].valuelen, nva[i].value);
175913498266Sopenharmony_ci    }
176013498266Sopenharmony_ci  }
176113498266Sopenharmony_ci
176213498266Sopenharmony_ciout:
176313498266Sopenharmony_ci  free(nva);
176413498266Sopenharmony_ci  Curl_dynhds_free(&h2_headers);
176513498266Sopenharmony_ci  return nwritten;
176613498266Sopenharmony_ci}
176713498266Sopenharmony_ci
176813498266Sopenharmony_cistatic ssize_t cf_osslq_send(struct Curl_cfilter *cf, struct Curl_easy *data,
176913498266Sopenharmony_ci                             const void *buf, size_t len, CURLcode *err)
177013498266Sopenharmony_ci{
177113498266Sopenharmony_ci  struct cf_osslq_ctx *ctx = cf->ctx;
177213498266Sopenharmony_ci  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
177313498266Sopenharmony_ci  struct cf_call_data save;
177413498266Sopenharmony_ci  ssize_t nwritten;
177513498266Sopenharmony_ci  CURLcode result;
177613498266Sopenharmony_ci
177713498266Sopenharmony_ci  CF_DATA_SAVE(save, cf, data);
177813498266Sopenharmony_ci  DEBUGASSERT(cf->connected);
177913498266Sopenharmony_ci  DEBUGASSERT(ctx->tls.ssl);
178013498266Sopenharmony_ci  DEBUGASSERT(ctx->h3.conn);
178113498266Sopenharmony_ci  *err = CURLE_OK;
178213498266Sopenharmony_ci
178313498266Sopenharmony_ci  result = cf_progress_ingress(cf, data);
178413498266Sopenharmony_ci  if(result) {
178513498266Sopenharmony_ci    *err = result;
178613498266Sopenharmony_ci    nwritten = -1;
178713498266Sopenharmony_ci    goto out;
178813498266Sopenharmony_ci  }
178913498266Sopenharmony_ci
179013498266Sopenharmony_ci  result = cf_progress_egress(cf, data);
179113498266Sopenharmony_ci  if(result) {
179213498266Sopenharmony_ci    *err = result;
179313498266Sopenharmony_ci    nwritten = -1;
179413498266Sopenharmony_ci    goto out;
179513498266Sopenharmony_ci  }
179613498266Sopenharmony_ci
179713498266Sopenharmony_ci  if(!stream || stream->s.id < 0) {
179813498266Sopenharmony_ci    nwritten = h3_stream_open(cf, data, buf, len, err);
179913498266Sopenharmony_ci    if(nwritten < 0) {
180013498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "failed to open stream -> %d", *err);
180113498266Sopenharmony_ci      goto out;
180213498266Sopenharmony_ci    }
180313498266Sopenharmony_ci    stream = H3_STREAM_CTX(data);
180413498266Sopenharmony_ci  }
180513498266Sopenharmony_ci  else if(stream->upload_blocked_len) {
180613498266Sopenharmony_ci    /* the data in `buf` has already been submitted or added to the
180713498266Sopenharmony_ci     * buffers, but have been EAGAINed on the last invocation. */
180813498266Sopenharmony_ci    DEBUGASSERT(len >= stream->upload_blocked_len);
180913498266Sopenharmony_ci    if(len < stream->upload_blocked_len) {
181013498266Sopenharmony_ci      /* Did we get called again with a smaller `len`? This should not
181113498266Sopenharmony_ci       * happen. We are not prepared to handle that. */
181213498266Sopenharmony_ci      failf(data, "HTTP/3 send again with decreased length");
181313498266Sopenharmony_ci      *err = CURLE_HTTP3;
181413498266Sopenharmony_ci      nwritten = -1;
181513498266Sopenharmony_ci      goto out;
181613498266Sopenharmony_ci    }
181713498266Sopenharmony_ci    nwritten = (ssize_t)stream->upload_blocked_len;
181813498266Sopenharmony_ci    stream->upload_blocked_len = 0;
181913498266Sopenharmony_ci  }
182013498266Sopenharmony_ci  else if(stream->closed) {
182113498266Sopenharmony_ci    if(stream->resp_hds_complete) {
182213498266Sopenharmony_ci      /* Server decided to close the stream after having sent us a final
182313498266Sopenharmony_ci       * response. This is valid if it is not interested in the request
182413498266Sopenharmony_ci       * body. This happens on 30x or 40x responses.
182513498266Sopenharmony_ci       * We silently discard the data sent, since this is not a transport
182613498266Sopenharmony_ci       * error situation. */
182713498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "[%" PRId64 "] discarding data"
182813498266Sopenharmony_ci                  "on closed stream with response", stream->s.id);
182913498266Sopenharmony_ci      *err = CURLE_OK;
183013498266Sopenharmony_ci      nwritten = (ssize_t)len;
183113498266Sopenharmony_ci      goto out;
183213498266Sopenharmony_ci    }
183313498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) "
183413498266Sopenharmony_ci                "-> stream closed", stream->s.id, len);
183513498266Sopenharmony_ci    *err = CURLE_HTTP3;
183613498266Sopenharmony_ci    nwritten = -1;
183713498266Sopenharmony_ci    goto out;
183813498266Sopenharmony_ci  }
183913498266Sopenharmony_ci  else {
184013498266Sopenharmony_ci    nwritten = Curl_bufq_write(&stream->sendbuf, buf, len, err);
184113498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send, add to "
184213498266Sopenharmony_ci                "sendbuf(len=%zu) -> %zd, %d",
184313498266Sopenharmony_ci                stream->s.id, len, nwritten, *err);
184413498266Sopenharmony_ci    if(nwritten < 0) {
184513498266Sopenharmony_ci      goto out;
184613498266Sopenharmony_ci    }
184713498266Sopenharmony_ci
184813498266Sopenharmony_ci    (void)nghttp3_conn_resume_stream(ctx->h3.conn, stream->s.id);
184913498266Sopenharmony_ci  }
185013498266Sopenharmony_ci
185113498266Sopenharmony_ci  result = cf_progress_egress(cf, data);
185213498266Sopenharmony_ci  if(result) {
185313498266Sopenharmony_ci    *err = result;
185413498266Sopenharmony_ci    nwritten = -1;
185513498266Sopenharmony_ci  }
185613498266Sopenharmony_ci
185713498266Sopenharmony_ci  if(stream && nwritten > 0 && stream->sendbuf_len_in_flight) {
185813498266Sopenharmony_ci    /* We have unacknowledged DATA and cannot report success to our
185913498266Sopenharmony_ci     * caller. Instead we EAGAIN and remember how much we have already
186013498266Sopenharmony_ci     * "written" into our various internal connection buffers. */
186113498266Sopenharmony_ci    stream->upload_blocked_len = nwritten;
186213498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu), "
186313498266Sopenharmony_ci                "%zu bytes in flight -> EGAIN", stream->s.id, len,
186413498266Sopenharmony_ci                stream->sendbuf_len_in_flight);
186513498266Sopenharmony_ci    *err = CURLE_AGAIN;
186613498266Sopenharmony_ci    nwritten = -1;
186713498266Sopenharmony_ci  }
186813498266Sopenharmony_ci
186913498266Sopenharmony_ciout:
187013498266Sopenharmony_ci  result = check_and_set_expiry(cf, data);
187113498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %zd, %d",
187213498266Sopenharmony_ci              stream? stream->s.id : -1, len, nwritten, *err);
187313498266Sopenharmony_ci  CF_DATA_RESTORE(cf, save);
187413498266Sopenharmony_ci  return nwritten;
187513498266Sopenharmony_ci}
187613498266Sopenharmony_ci
187713498266Sopenharmony_cistatic ssize_t recv_closed_stream(struct Curl_cfilter *cf,
187813498266Sopenharmony_ci                                  struct Curl_easy *data,
187913498266Sopenharmony_ci                                  struct h3_stream_ctx *stream,
188013498266Sopenharmony_ci                                  CURLcode *err)
188113498266Sopenharmony_ci{
188213498266Sopenharmony_ci  ssize_t nread = -1;
188313498266Sopenharmony_ci
188413498266Sopenharmony_ci  (void)cf;
188513498266Sopenharmony_ci  if(stream->reset) {
188613498266Sopenharmony_ci    failf(data,
188713498266Sopenharmony_ci          "HTTP/3 stream %" PRId64 " reset by server", stream->s.id);
188813498266Sopenharmony_ci    *err = stream->resp_hds_complete? CURLE_PARTIAL_FILE : CURLE_HTTP3;
188913498266Sopenharmony_ci    goto out;
189013498266Sopenharmony_ci  }
189113498266Sopenharmony_ci  else if(!stream->resp_hds_complete) {
189213498266Sopenharmony_ci    failf(data,
189313498266Sopenharmony_ci          "HTTP/3 stream %" PRId64 " was closed cleanly, but before getting"
189413498266Sopenharmony_ci          " all response header fields, treated as error",
189513498266Sopenharmony_ci          stream->s.id);
189613498266Sopenharmony_ci    *err = CURLE_HTTP3;
189713498266Sopenharmony_ci    goto out;
189813498266Sopenharmony_ci  }
189913498266Sopenharmony_ci  *err = CURLE_OK;
190013498266Sopenharmony_ci  nread = 0;
190113498266Sopenharmony_ci
190213498266Sopenharmony_ciout:
190313498266Sopenharmony_ci  return nread;
190413498266Sopenharmony_ci}
190513498266Sopenharmony_ci
190613498266Sopenharmony_cistatic ssize_t cf_osslq_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
190713498266Sopenharmony_ci                             char *buf, size_t len, CURLcode *err)
190813498266Sopenharmony_ci{
190913498266Sopenharmony_ci  struct cf_osslq_ctx *ctx = cf->ctx;
191013498266Sopenharmony_ci  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
191113498266Sopenharmony_ci  ssize_t nread = -1;
191213498266Sopenharmony_ci  struct cf_call_data save;
191313498266Sopenharmony_ci  CURLcode result;
191413498266Sopenharmony_ci
191513498266Sopenharmony_ci  (void)ctx;
191613498266Sopenharmony_ci  CF_DATA_SAVE(save, cf, data);
191713498266Sopenharmony_ci  DEBUGASSERT(cf->connected);
191813498266Sopenharmony_ci  DEBUGASSERT(ctx);
191913498266Sopenharmony_ci  DEBUGASSERT(ctx->tls.ssl);
192013498266Sopenharmony_ci  DEBUGASSERT(ctx->h3.conn);
192113498266Sopenharmony_ci  *err = CURLE_OK;
192213498266Sopenharmony_ci
192313498266Sopenharmony_ci  if(!stream) {
192413498266Sopenharmony_ci    *err = CURLE_RECV_ERROR;
192513498266Sopenharmony_ci    goto out;
192613498266Sopenharmony_ci  }
192713498266Sopenharmony_ci
192813498266Sopenharmony_ci  if(!Curl_bufq_is_empty(&stream->recvbuf)) {
192913498266Sopenharmony_ci    nread = Curl_bufq_read(&stream->recvbuf,
193013498266Sopenharmony_ci                           (unsigned char *)buf, len, err);
193113498266Sopenharmony_ci    if(nread < 0) {
193213498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) "
193313498266Sopenharmony_ci                  "-> %zd, %d", stream->s.id, len, nread, *err);
193413498266Sopenharmony_ci      goto out;
193513498266Sopenharmony_ci    }
193613498266Sopenharmony_ci  }
193713498266Sopenharmony_ci
193813498266Sopenharmony_ci  result = cf_progress_ingress(cf, data);
193913498266Sopenharmony_ci  if(result) {
194013498266Sopenharmony_ci    *err = result;
194113498266Sopenharmony_ci    nread = -1;
194213498266Sopenharmony_ci    goto out;
194313498266Sopenharmony_ci  }
194413498266Sopenharmony_ci
194513498266Sopenharmony_ci  /* recvbuf had nothing before, maybe after progressing ingress? */
194613498266Sopenharmony_ci  if(nread < 0 && !Curl_bufq_is_empty(&stream->recvbuf)) {
194713498266Sopenharmony_ci    nread = Curl_bufq_read(&stream->recvbuf,
194813498266Sopenharmony_ci                           (unsigned char *)buf, len, err);
194913498266Sopenharmony_ci    if(nread < 0) {
195013498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) "
195113498266Sopenharmony_ci                  "-> %zd, %d", stream->s.id, len, nread, *err);
195213498266Sopenharmony_ci      goto out;
195313498266Sopenharmony_ci    }
195413498266Sopenharmony_ci  }
195513498266Sopenharmony_ci
195613498266Sopenharmony_ci  if(nread > 0) {
195713498266Sopenharmony_ci    h3_drain_stream(cf, data);
195813498266Sopenharmony_ci  }
195913498266Sopenharmony_ci  else {
196013498266Sopenharmony_ci    if(stream->closed) {
196113498266Sopenharmony_ci      nread = recv_closed_stream(cf, data, stream, err);
196213498266Sopenharmony_ci      goto out;
196313498266Sopenharmony_ci    }
196413498266Sopenharmony_ci    *err = CURLE_AGAIN;
196513498266Sopenharmony_ci    nread = -1;
196613498266Sopenharmony_ci  }
196713498266Sopenharmony_ci
196813498266Sopenharmony_ciout:
196913498266Sopenharmony_ci  if(cf_progress_egress(cf, data)) {
197013498266Sopenharmony_ci    *err = CURLE_SEND_ERROR;
197113498266Sopenharmony_ci    nread = -1;
197213498266Sopenharmony_ci  }
197313498266Sopenharmony_ci  else {
197413498266Sopenharmony_ci    CURLcode result2 = check_and_set_expiry(cf, data);
197513498266Sopenharmony_ci    if(result2) {
197613498266Sopenharmony_ci      *err = result2;
197713498266Sopenharmony_ci      nread = -1;
197813498266Sopenharmony_ci    }
197913498266Sopenharmony_ci  }
198013498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv(len=%zu) -> %zd, %d",
198113498266Sopenharmony_ci              stream? stream->s.id : -1, len, nread, *err);
198213498266Sopenharmony_ci  CF_DATA_RESTORE(cf, save);
198313498266Sopenharmony_ci  return nread;
198413498266Sopenharmony_ci}
198513498266Sopenharmony_ci
198613498266Sopenharmony_ci/*
198713498266Sopenharmony_ci * Called from transfer.c:data_pending to know if we should keep looping
198813498266Sopenharmony_ci * to receive more data from the connection.
198913498266Sopenharmony_ci */
199013498266Sopenharmony_cistatic bool cf_osslq_data_pending(struct Curl_cfilter *cf,
199113498266Sopenharmony_ci                                  const struct Curl_easy *data)
199213498266Sopenharmony_ci{
199313498266Sopenharmony_ci  const struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
199413498266Sopenharmony_ci  (void)cf;
199513498266Sopenharmony_ci  return stream && !Curl_bufq_is_empty(&stream->recvbuf);
199613498266Sopenharmony_ci}
199713498266Sopenharmony_ci
199813498266Sopenharmony_cistatic CURLcode cf_osslq_data_event(struct Curl_cfilter *cf,
199913498266Sopenharmony_ci                                    struct Curl_easy *data,
200013498266Sopenharmony_ci                                    int event, int arg1, void *arg2)
200113498266Sopenharmony_ci{
200213498266Sopenharmony_ci  struct cf_osslq_ctx *ctx = cf->ctx;
200313498266Sopenharmony_ci  CURLcode result = CURLE_OK;
200413498266Sopenharmony_ci  struct cf_call_data save;
200513498266Sopenharmony_ci
200613498266Sopenharmony_ci  CF_DATA_SAVE(save, cf, data);
200713498266Sopenharmony_ci  (void)arg1;
200813498266Sopenharmony_ci  (void)arg2;
200913498266Sopenharmony_ci  switch(event) {
201013498266Sopenharmony_ci  case CF_CTRL_DATA_SETUP:
201113498266Sopenharmony_ci    break;
201213498266Sopenharmony_ci  case CF_CTRL_DATA_PAUSE:
201313498266Sopenharmony_ci    result = h3_data_pause(cf, data, (arg1 != 0));
201413498266Sopenharmony_ci    break;
201513498266Sopenharmony_ci  case CF_CTRL_DATA_DETACH:
201613498266Sopenharmony_ci    h3_data_done(cf, data);
201713498266Sopenharmony_ci    break;
201813498266Sopenharmony_ci  case CF_CTRL_DATA_DONE:
201913498266Sopenharmony_ci    h3_data_done(cf, data);
202013498266Sopenharmony_ci    break;
202113498266Sopenharmony_ci  case CF_CTRL_DATA_DONE_SEND: {
202213498266Sopenharmony_ci    struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
202313498266Sopenharmony_ci    if(stream && !stream->send_closed) {
202413498266Sopenharmony_ci      stream->send_closed = TRUE;
202513498266Sopenharmony_ci      stream->upload_left = Curl_bufq_len(&stream->sendbuf);
202613498266Sopenharmony_ci      (void)nghttp3_conn_resume_stream(ctx->h3.conn, stream->s.id);
202713498266Sopenharmony_ci    }
202813498266Sopenharmony_ci    break;
202913498266Sopenharmony_ci  }
203013498266Sopenharmony_ci  case CF_CTRL_DATA_IDLE: {
203113498266Sopenharmony_ci    struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
203213498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "data idle");
203313498266Sopenharmony_ci    if(stream && !stream->closed) {
203413498266Sopenharmony_ci      result = check_and_set_expiry(cf, data);
203513498266Sopenharmony_ci    }
203613498266Sopenharmony_ci    break;
203713498266Sopenharmony_ci  }
203813498266Sopenharmony_ci  default:
203913498266Sopenharmony_ci    break;
204013498266Sopenharmony_ci  }
204113498266Sopenharmony_ci  CF_DATA_RESTORE(cf, save);
204213498266Sopenharmony_ci  return result;
204313498266Sopenharmony_ci}
204413498266Sopenharmony_ci
204513498266Sopenharmony_cistatic bool cf_osslq_conn_is_alive(struct Curl_cfilter *cf,
204613498266Sopenharmony_ci                                   struct Curl_easy *data,
204713498266Sopenharmony_ci                                   bool *input_pending)
204813498266Sopenharmony_ci{
204913498266Sopenharmony_ci  struct cf_osslq_ctx *ctx = cf->ctx;
205013498266Sopenharmony_ci  bool alive = FALSE;
205113498266Sopenharmony_ci  struct cf_call_data save;
205213498266Sopenharmony_ci
205313498266Sopenharmony_ci  CF_DATA_SAVE(save, cf, data);
205413498266Sopenharmony_ci  *input_pending = FALSE;
205513498266Sopenharmony_ci  if(!ctx->tls.ssl)
205613498266Sopenharmony_ci    goto out;
205713498266Sopenharmony_ci
205813498266Sopenharmony_ci  /* TODO: how to check negotiated connection idle time? */
205913498266Sopenharmony_ci
206013498266Sopenharmony_ci  if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending))
206113498266Sopenharmony_ci    goto out;
206213498266Sopenharmony_ci
206313498266Sopenharmony_ci  alive = TRUE;
206413498266Sopenharmony_ci  if(*input_pending) {
206513498266Sopenharmony_ci    CURLcode result;
206613498266Sopenharmony_ci    /* This happens before we've sent off a request and the connection is
206713498266Sopenharmony_ci       not in use by any other transfer, there shouldn't be any data here,
206813498266Sopenharmony_ci       only "protocol frames" */
206913498266Sopenharmony_ci    *input_pending = FALSE;
207013498266Sopenharmony_ci    result = cf_progress_ingress(cf, data);
207113498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "is_alive, progress ingress -> %d", result);
207213498266Sopenharmony_ci    alive = result? FALSE : TRUE;
207313498266Sopenharmony_ci  }
207413498266Sopenharmony_ci
207513498266Sopenharmony_ciout:
207613498266Sopenharmony_ci  CF_DATA_RESTORE(cf, save);
207713498266Sopenharmony_ci  return alive;
207813498266Sopenharmony_ci}
207913498266Sopenharmony_ci
208013498266Sopenharmony_cistatic void cf_osslq_adjust_pollset(struct Curl_cfilter *cf,
208113498266Sopenharmony_ci                                    struct Curl_easy *data,
208213498266Sopenharmony_ci                                    struct easy_pollset *ps)
208313498266Sopenharmony_ci{
208413498266Sopenharmony_ci  struct cf_osslq_ctx *ctx = cf->ctx;
208513498266Sopenharmony_ci
208613498266Sopenharmony_ci  if(!ctx->tls.ssl) {
208713498266Sopenharmony_ci    /* NOP */
208813498266Sopenharmony_ci  }
208913498266Sopenharmony_ci  else if(!cf->connected) {
209013498266Sopenharmony_ci    /* during handshake, transfer has not started yet. we always
209113498266Sopenharmony_ci     * add our socket for polling if SSL wants to send/recv */
209213498266Sopenharmony_ci    Curl_pollset_set(data, ps, ctx->q.sockfd,
209313498266Sopenharmony_ci                     SSL_net_read_desired(ctx->tls.ssl),
209413498266Sopenharmony_ci                     SSL_net_write_desired(ctx->tls.ssl));
209513498266Sopenharmony_ci  }
209613498266Sopenharmony_ci  else {
209713498266Sopenharmony_ci    /* once connected, we only modify the socket if it is present.
209813498266Sopenharmony_ci     * this avoids adding it for paused transfers. */
209913498266Sopenharmony_ci    bool want_recv, want_send;
210013498266Sopenharmony_ci    Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send);
210113498266Sopenharmony_ci    if(want_recv || want_send) {
210213498266Sopenharmony_ci      Curl_pollset_set(data, ps, ctx->q.sockfd,
210313498266Sopenharmony_ci                       SSL_net_read_desired(ctx->tls.ssl),
210413498266Sopenharmony_ci                       SSL_net_write_desired(ctx->tls.ssl));
210513498266Sopenharmony_ci    }
210613498266Sopenharmony_ci  }
210713498266Sopenharmony_ci}
210813498266Sopenharmony_ci
210913498266Sopenharmony_cistatic CURLcode cf_osslq_query(struct Curl_cfilter *cf,
211013498266Sopenharmony_ci                               struct Curl_easy *data,
211113498266Sopenharmony_ci                               int query, int *pres1, void *pres2)
211213498266Sopenharmony_ci{
211313498266Sopenharmony_ci  struct cf_osslq_ctx *ctx = cf->ctx;
211413498266Sopenharmony_ci  struct cf_call_data save;
211513498266Sopenharmony_ci
211613498266Sopenharmony_ci  switch(query) {
211713498266Sopenharmony_ci  case CF_QUERY_MAX_CONCURRENT: {
211813498266Sopenharmony_ci    /* TODO: how to get this? */
211913498266Sopenharmony_ci    CF_DATA_SAVE(save, cf, data);
212013498266Sopenharmony_ci    *pres1 = 100;
212113498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "query max_conncurrent -> %d", *pres1);
212213498266Sopenharmony_ci    CF_DATA_RESTORE(cf, save);
212313498266Sopenharmony_ci    return CURLE_OK;
212413498266Sopenharmony_ci  }
212513498266Sopenharmony_ci  case CF_QUERY_CONNECT_REPLY_MS:
212613498266Sopenharmony_ci    if(ctx->got_first_byte) {
212713498266Sopenharmony_ci      timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at);
212813498266Sopenharmony_ci      *pres1 = (ms < INT_MAX)? (int)ms : INT_MAX;
212913498266Sopenharmony_ci    }
213013498266Sopenharmony_ci    else
213113498266Sopenharmony_ci      *pres1 = -1;
213213498266Sopenharmony_ci    return CURLE_OK;
213313498266Sopenharmony_ci  case CF_QUERY_TIMER_CONNECT: {
213413498266Sopenharmony_ci    struct curltime *when = pres2;
213513498266Sopenharmony_ci    if(ctx->got_first_byte)
213613498266Sopenharmony_ci      *when = ctx->first_byte_at;
213713498266Sopenharmony_ci    return CURLE_OK;
213813498266Sopenharmony_ci  }
213913498266Sopenharmony_ci  case CF_QUERY_TIMER_APPCONNECT: {
214013498266Sopenharmony_ci    struct curltime *when = pres2;
214113498266Sopenharmony_ci    if(cf->connected)
214213498266Sopenharmony_ci      *when = ctx->handshake_at;
214313498266Sopenharmony_ci    return CURLE_OK;
214413498266Sopenharmony_ci  }
214513498266Sopenharmony_ci  default:
214613498266Sopenharmony_ci    break;
214713498266Sopenharmony_ci  }
214813498266Sopenharmony_ci  return cf->next?
214913498266Sopenharmony_ci    cf->next->cft->query(cf->next, data, query, pres1, pres2) :
215013498266Sopenharmony_ci    CURLE_UNKNOWN_OPTION;
215113498266Sopenharmony_ci}
215213498266Sopenharmony_ci
215313498266Sopenharmony_cistruct Curl_cftype Curl_cft_http3 = {
215413498266Sopenharmony_ci  "HTTP/3",
215513498266Sopenharmony_ci  CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX,
215613498266Sopenharmony_ci  0,
215713498266Sopenharmony_ci  cf_osslq_destroy,
215813498266Sopenharmony_ci  cf_osslq_connect,
215913498266Sopenharmony_ci  cf_osslq_close,
216013498266Sopenharmony_ci  Curl_cf_def_get_host,
216113498266Sopenharmony_ci  cf_osslq_adjust_pollset,
216213498266Sopenharmony_ci  cf_osslq_data_pending,
216313498266Sopenharmony_ci  cf_osslq_send,
216413498266Sopenharmony_ci  cf_osslq_recv,
216513498266Sopenharmony_ci  cf_osslq_data_event,
216613498266Sopenharmony_ci  cf_osslq_conn_is_alive,
216713498266Sopenharmony_ci  Curl_cf_def_conn_keep_alive,
216813498266Sopenharmony_ci  cf_osslq_query,
216913498266Sopenharmony_ci};
217013498266Sopenharmony_ci
217113498266Sopenharmony_ciCURLcode Curl_cf_osslq_create(struct Curl_cfilter **pcf,
217213498266Sopenharmony_ci                              struct Curl_easy *data,
217313498266Sopenharmony_ci                              struct connectdata *conn,
217413498266Sopenharmony_ci                              const struct Curl_addrinfo *ai)
217513498266Sopenharmony_ci{
217613498266Sopenharmony_ci  struct cf_osslq_ctx *ctx = NULL;
217713498266Sopenharmony_ci  struct Curl_cfilter *cf = NULL, *udp_cf = NULL;
217813498266Sopenharmony_ci  CURLcode result;
217913498266Sopenharmony_ci
218013498266Sopenharmony_ci  (void)data;
218113498266Sopenharmony_ci  ctx = calloc(1, sizeof(*ctx));
218213498266Sopenharmony_ci  if(!ctx) {
218313498266Sopenharmony_ci    result = CURLE_OUT_OF_MEMORY;
218413498266Sopenharmony_ci    goto out;
218513498266Sopenharmony_ci  }
218613498266Sopenharmony_ci  cf_osslq_ctx_clear(ctx);
218713498266Sopenharmony_ci
218813498266Sopenharmony_ci  result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
218913498266Sopenharmony_ci  if(result)
219013498266Sopenharmony_ci    goto out;
219113498266Sopenharmony_ci
219213498266Sopenharmony_ci  result = Curl_cf_udp_create(&udp_cf, data, conn, ai, TRNSPRT_QUIC);
219313498266Sopenharmony_ci  if(result)
219413498266Sopenharmony_ci    goto out;
219513498266Sopenharmony_ci
219613498266Sopenharmony_ci  cf->conn = conn;
219713498266Sopenharmony_ci  udp_cf->conn = cf->conn;
219813498266Sopenharmony_ci  udp_cf->sockindex = cf->sockindex;
219913498266Sopenharmony_ci  cf->next = udp_cf;
220013498266Sopenharmony_ci
220113498266Sopenharmony_ciout:
220213498266Sopenharmony_ci  *pcf = (!result)? cf : NULL;
220313498266Sopenharmony_ci  if(result) {
220413498266Sopenharmony_ci    if(udp_cf)
220513498266Sopenharmony_ci      Curl_conn_cf_discard_sub(cf, udp_cf, data, TRUE);
220613498266Sopenharmony_ci    Curl_safefree(cf);
220713498266Sopenharmony_ci    Curl_safefree(ctx);
220813498266Sopenharmony_ci  }
220913498266Sopenharmony_ci  return result;
221013498266Sopenharmony_ci}
221113498266Sopenharmony_ci
221213498266Sopenharmony_cibool Curl_conn_is_osslq(const struct Curl_easy *data,
221313498266Sopenharmony_ci                        const struct connectdata *conn,
221413498266Sopenharmony_ci                        int sockindex)
221513498266Sopenharmony_ci{
221613498266Sopenharmony_ci  struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL;
221713498266Sopenharmony_ci
221813498266Sopenharmony_ci  (void)data;
221913498266Sopenharmony_ci  for(; cf; cf = cf->next) {
222013498266Sopenharmony_ci    if(cf->cft == &Curl_cft_http3)
222113498266Sopenharmony_ci      return TRUE;
222213498266Sopenharmony_ci    if(cf->cft->flags & CF_TYPE_IP_CONNECT)
222313498266Sopenharmony_ci      return FALSE;
222413498266Sopenharmony_ci  }
222513498266Sopenharmony_ci  return FALSE;
222613498266Sopenharmony_ci}
222713498266Sopenharmony_ci
222813498266Sopenharmony_ci/*
222913498266Sopenharmony_ci * Store ngtcp2 version info in this buffer.
223013498266Sopenharmony_ci */
223113498266Sopenharmony_civoid Curl_osslq_ver(char *p, size_t len)
223213498266Sopenharmony_ci{
223313498266Sopenharmony_ci  const nghttp3_info *ht3 = nghttp3_version(0);
223413498266Sopenharmony_ci  (void)msnprintf(p, len, "nghttp3/%s", ht3->version_str);
223513498266Sopenharmony_ci}
223613498266Sopenharmony_ci
223713498266Sopenharmony_ci#endif /* USE_OPENSSL_QUIC && USE_NGHTTP3 */
2238