quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

curl_msh3.c (30856B)


      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
      9  *
     10  * This software is licensed as described in the file COPYING, which
     11  * you should have received as part of this distribution. The terms
     12  * are also available at https://curl.se/docs/copyright.html.
     13  *
     14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
     15  * copies of the Software, and permit persons to whom the Software is
     16  * furnished to do so, under the terms of the COPYING file.
     17  *
     18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
     19  * KIND, either express or implied.
     20  *
     21  * SPDX-License-Identifier: curl
     22  *
     23  ***************************************************************************/
     24 
     25 #include "../curl_setup.h"
     26 
     27 #ifdef USE_MSH3
     28 
     29 #include "../urldata.h"
     30 #include "../hash.h"
     31 #include "../uint-hash.h"
     32 #include "../curlx/timeval.h"
     33 #include "../multiif.h"
     34 #include "../sendf.h"
     35 #include "../curl_trc.h"
     36 #include "../cfilters.h"
     37 #include "../cf-socket.h"
     38 #include "../connect.h"
     39 #include "../progress.h"
     40 #include "../http1.h"
     41 #include "curl_msh3.h"
     42 #include "../socketpair.h"
     43 #include "../vtls/vtls.h"
     44 #include "vquic.h"
     45 #include "vquic_int.h"
     46 
     47 /* The last 3 #include files should be in this order */
     48 #include "../curl_printf.h"
     49 #include "../curl_memory.h"
     50 #include "../memdebug.h"
     51 
     52 #ifdef CURL_DISABLE_SOCKETPAIR
     53 #error "MSH3 cannot be build with CURL_DISABLE_SOCKETPAIR set"
     54 #endif
     55 
     56 #define H3_STREAM_WINDOW_SIZE (128 * 1024)
     57 #define H3_STREAM_CHUNK_SIZE   (16 * 1024)
     58 #define H3_STREAM_RECV_CHUNKS \
     59           (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE)
     60 
     61 #ifdef _WIN32
     62 #define msh3_lock CRITICAL_SECTION
     63 #define msh3_lock_initialize(lock) InitializeCriticalSection(lock)
     64 #define msh3_lock_uninitialize(lock) DeleteCriticalSection(lock)
     65 #define msh3_lock_acquire(lock) EnterCriticalSection(lock)
     66 #define msh3_lock_release(lock) LeaveCriticalSection(lock)
     67 #else /* !_WIN32 */
     68 #include <pthread.h>
     69 #define msh3_lock pthread_mutex_t
     70 #define msh3_lock_initialize(lock) do { \
     71   pthread_mutexattr_t attr; \
     72   pthread_mutexattr_init(&attr); \
     73   pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \
     74   pthread_mutex_init(lock, &attr); \
     75   pthread_mutexattr_destroy(&attr); \
     76 } while(0)
     77 #define msh3_lock_uninitialize(lock) pthread_mutex_destroy(lock)
     78 #define msh3_lock_acquire(lock) pthread_mutex_lock(lock)
     79 #define msh3_lock_release(lock) pthread_mutex_unlock(lock)
     80 #endif /* _WIN32 */
     81 
     82 
     83 static void MSH3_CALL msh3_conn_connected(MSH3_CONNECTION *Connection,
     84                                           void *IfContext);
     85 static void MSH3_CALL msh3_conn_shutdown_complete(MSH3_CONNECTION *Connection,
     86                                                   void *IfContext);
     87 static void MSH3_CALL msh3_conn_new_request(MSH3_CONNECTION *Connection,
     88                                             void *IfContext,
     89                                             MSH3_REQUEST *Request);
     90 static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
     91                                            void *IfContext,
     92                                            const MSH3_HEADER *Header);
     93 static bool MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
     94                                          void *IfContext, uint32_t *Length,
     95                                          const uint8_t *Data);
     96 static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
     97                                     bool Aborted, uint64_t AbortError);
     98 static void MSH3_CALL msh3_shutdown_complete(MSH3_REQUEST *Request,
     99                                              void *IfContext);
    100 static void MSH3_CALL msh3_data_sent(MSH3_REQUEST *Request,
    101                                      void *IfContext, void *SendContext);
    102 
    103 
    104 void Curl_msh3_ver(char *p, size_t len)
    105 {
    106   uint32_t v[4];
    107   MsH3Version(v);
    108   (void)msnprintf(p, len, "msh3/%d.%d.%d.%d", v[0], v[1], v[2], v[3]);
    109 }
    110 
    111 #define SP_LOCAL   0
    112 #define SP_REMOTE  1
    113 
    114 struct cf_msh3_ctx {
    115   MSH3_API *api;
    116   MSH3_CONNECTION *qconn;
    117   struct Curl_sockaddr_ex addr;
    118   curl_socket_t sock[2]; /* fake socket pair until we get support in msh3 */
    119   char l_ip[MAX_IPADR_LEN];          /* local IP as string */
    120   int l_port;                        /* local port number */
    121   struct cf_call_data call_data;
    122   struct curltime connect_started;   /* time the current attempt started */
    123   struct curltime handshake_at;      /* time connect handshake finished */
    124   struct uint_hash streams;          /* hash `data->mid` to `stream_ctx` */
    125   /* Flags written by msh3/msquic thread */
    126   BIT(handshake_complete);
    127   BIT(handshake_succeeded);
    128   BIT(connected);
    129   BIT(initialized);
    130   /* Flags written by curl thread */
    131   BIT(verbose);
    132   BIT(active);
    133 };
    134 
    135 static void h3_stream_hash_free(unsigned int id, void *stream);
    136 
    137 static CURLcode cf_msh3_ctx_init(struct cf_msh3_ctx *ctx,
    138                                  const struct Curl_addrinfo *ai)
    139 {
    140   CURLcode result;
    141 
    142   DEBUGASSERT(!ctx->initialized);
    143   Curl_uint_hash_init(&ctx->streams, 63, h3_stream_hash_free);
    144 
    145   result = Curl_sock_assign_addr(&ctx->addr, ai, TRNSPRT_QUIC);
    146   if(result)
    147     return result;
    148 
    149   ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
    150   ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD;
    151   ctx->initialized = TRUE;
    152 
    153   return result;
    154 }
    155 
    156 static void cf_msh3_ctx_free(struct cf_msh3_ctx *ctx)
    157 {
    158   if(ctx && ctx->initialized) {
    159     Curl_uint_hash_destroy(&ctx->streams);
    160   }
    161   free(ctx);
    162 }
    163 
    164 static struct cf_msh3_ctx *h3_get_msh3_ctx(struct Curl_easy *data);
    165 
    166 /* How to access `call_data` from a cf_msh3 filter */
    167 #undef CF_CTX_CALL_DATA
    168 #define CF_CTX_CALL_DATA(cf)  \
    169   ((struct cf_msh3_ctx *)(cf)->ctx)->call_data
    170 
    171 /**
    172  * All about the H3 internals of a stream
    173  */
    174 struct h3_stream_ctx {
    175   struct MSH3_REQUEST *req;
    176   struct bufq recvbuf;   /* h3 response */
    177 #ifdef _WIN32
    178   CRITICAL_SECTION recv_lock;
    179 #else /* !_WIN32 */
    180   pthread_mutex_t recv_lock;
    181 #endif /* _WIN32 */
    182   uint64_t error3; /* HTTP/3 stream error code */
    183   int status_code; /* HTTP status code */
    184   CURLcode recv_error;
    185   BIT(closed);
    186   BIT(reset);
    187   BIT(upload_done);
    188   BIT(firstheader);  /* FALSE until headers arrive */
    189   BIT(recv_header_complete);
    190 };
    191 
    192 static void h3_stream_ctx_free(struct h3_stream_ctx *stream)
    193 {
    194   Curl_bufq_free(&stream->recvbuf);
    195   free(stream);
    196 }
    197 
    198 static void h3_stream_hash_free(unsigned int id, void *stream)
    199 {
    200   (void)id;
    201   DEBUGASSERT(stream);
    202   h3_stream_ctx_free((struct h3_stream_ctx *)stream);
    203 }
    204 
    205 static CURLcode h3_data_setup(struct Curl_cfilter *cf,
    206                               struct Curl_easy *data)
    207 {
    208   struct cf_msh3_ctx *ctx = cf->ctx;
    209   struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
    210 
    211   if(stream)
    212     return CURLE_OK;
    213 
    214   stream = calloc(1, sizeof(*stream));
    215   if(!stream)
    216     return CURLE_OUT_OF_MEMORY;
    217 
    218   stream->req = ZERO_NULL;
    219   msh3_lock_initialize(&stream->recv_lock);
    220   Curl_bufq_init2(&stream->recvbuf, H3_STREAM_CHUNK_SIZE,
    221                   H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT);
    222   CURL_TRC_CF(data, cf, "data setup");
    223 
    224   if(!Curl_uint_hash_set(&ctx->streams, data->mid, stream)) {
    225     h3_stream_ctx_free(stream);
    226     return CURLE_OUT_OF_MEMORY;
    227   }
    228 
    229   return CURLE_OK;
    230 }
    231 
    232 static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
    233 {
    234   struct cf_msh3_ctx *ctx = cf->ctx;
    235   struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
    236 
    237   (void)cf;
    238   if(stream) {
    239     CURL_TRC_CF(data, cf, "easy handle is done");
    240     Curl_uint_hash_remove(&ctx->streams, data->mid);
    241   }
    242 }
    243 
    244 static void drain_stream_from_other_thread(struct Curl_easy *data,
    245                                            struct h3_stream_ctx *stream)
    246 {
    247   (void)data;
    248   (void)stream;
    249   /* cannot expire from other thread.
    250      here is the disconnect between msh3 and curl */
    251 }
    252 
    253 static void h3_drain_stream(struct Curl_cfilter *cf,
    254                             struct Curl_easy *data)
    255 {
    256   (void)cf;
    257   Curl_multi_mark_dirty(data);
    258 }
    259 
    260 static const MSH3_CONNECTION_IF msh3_conn_if = {
    261   msh3_conn_connected,
    262   msh3_conn_shutdown_complete,
    263   msh3_conn_new_request
    264 };
    265 
    266 static void MSH3_CALL msh3_conn_connected(MSH3_CONNECTION *Connection,
    267                                           void *IfContext)
    268 {
    269   struct Curl_cfilter *cf = IfContext;
    270   struct cf_msh3_ctx *ctx = cf->ctx;
    271   struct Curl_easy *data = CF_DATA_CURRENT(cf);
    272   (void)Connection;
    273 
    274   CURL_TRC_CF(data, cf, "[MSH3] connected");
    275   ctx->handshake_succeeded = TRUE;
    276   ctx->connected = TRUE;
    277   ctx->handshake_complete = TRUE;
    278 }
    279 
    280 static void MSH3_CALL msh3_conn_shutdown_complete(MSH3_CONNECTION *Connection,
    281                                                   void *IfContext)
    282 {
    283   struct Curl_cfilter *cf = IfContext;
    284   struct cf_msh3_ctx *ctx = cf->ctx;
    285   struct Curl_easy *data = CF_DATA_CURRENT(cf);
    286 
    287   (void)Connection;
    288   CURL_TRC_CF(data, cf, "[MSH3] shutdown complete");
    289   ctx->connected = FALSE;
    290   ctx->handshake_complete = TRUE;
    291 }
    292 
    293 static void MSH3_CALL msh3_conn_new_request(MSH3_CONNECTION *Connection,
    294                                             void *IfContext,
    295                                             MSH3_REQUEST *Request)
    296 {
    297   (void)Connection;
    298   (void)IfContext;
    299   (void)Request;
    300 }
    301 
    302 static const MSH3_REQUEST_IF msh3_request_if = {
    303   msh3_header_received,
    304   msh3_data_received,
    305   msh3_complete,
    306   msh3_shutdown_complete,
    307   msh3_data_sent
    308 };
    309 
    310 /* Decode HTTP status code. Returns -1 if no valid status code was
    311    decoded. (duplicate from http2.c) */
    312 static int decode_status_code(const char *value, size_t len)
    313 {
    314   int i;
    315   int res;
    316 
    317   if(len != 3) {
    318     return -1;
    319   }
    320 
    321   res = 0;
    322 
    323   for(i = 0; i < 3; ++i) {
    324     char c = value[i];
    325 
    326     if(c < '0' || c > '9') {
    327       return -1;
    328     }
    329 
    330     res *= 10;
    331     res += c - '0';
    332   }
    333 
    334   return res;
    335 }
    336 
    337 /*
    338  * write_resp_raw() copies response data in raw format to the `data`'s
    339   * receive buffer. If not enough space is available, it appends to the
    340  * `data`'s overflow buffer.
    341  */
    342 static CURLcode write_resp_raw(struct Curl_easy *data,
    343                                const void *mem, size_t memlen)
    344 {
    345   struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data);
    346   struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
    347   CURLcode result = CURLE_OK;
    348   size_t nwritten;
    349 
    350   if(!stream)
    351     return CURLE_RECV_ERROR;
    352 
    353   result = Curl_bufq_write(&stream->recvbuf, mem, memlen, &nwritten);
    354   if(result)
    355     return result;
    356 
    357   if(nwritten < memlen) {
    358     /* This MUST not happen. Our recbuf is dimensioned to hold the
    359      * full max_stream_window and then some for this very reason. */
    360     DEBUGASSERT(0);
    361     return CURLE_RECV_ERROR;
    362   }
    363   return result;
    364 }
    365 
    366 static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
    367                                            void *userp,
    368                                            const MSH3_HEADER *hd)
    369 {
    370   struct Curl_easy *data = userp;
    371   struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data);
    372   struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
    373   CURLcode result;
    374   (void)Request;
    375 
    376   DEBUGF(infof(data, "[MSH3] header received, stream=%d", !!stream));
    377   if(!stream || stream->recv_header_complete) {
    378     return;
    379   }
    380 
    381   msh3_lock_acquire(&stream->recv_lock);
    382 
    383   if((hd->NameLength == 7) &&
    384      !strncmp(HTTP_PSEUDO_STATUS, (const char *)hd->Name, 7)) {
    385     char line[14]; /* status line is always 13 characters long */
    386     size_t ncopy;
    387 
    388     DEBUGASSERT(!stream->firstheader);
    389     stream->status_code = decode_status_code(hd->Value, hd->ValueLength);
    390     DEBUGASSERT(stream->status_code != -1);
    391     ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n",
    392                       stream->status_code);
    393     result = write_resp_raw(data, line, ncopy);
    394     if(result)
    395       stream->recv_error = result;
    396     stream->firstheader = TRUE;
    397   }
    398   else {
    399     /* store as an HTTP1-style header */
    400     DEBUGASSERT(stream->firstheader);
    401     result = write_resp_raw(data, hd->Name, hd->NameLength);
    402     if(!result)
    403       result = write_resp_raw(data, ": ", 2);
    404     if(!result)
    405       result = write_resp_raw(data, hd->Value, hd->ValueLength);
    406     if(!result)
    407       result = write_resp_raw(data, "\r\n", 2);
    408     if(result) {
    409       stream->recv_error = result;
    410     }
    411   }
    412 
    413   drain_stream_from_other_thread(data, stream);
    414   msh3_lock_release(&stream->recv_lock);
    415 }
    416 
    417 static bool MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
    418                                          void *IfContext, uint32_t *buflen,
    419                                          const uint8_t *buf)
    420 {
    421   struct Curl_easy *data = IfContext;
    422   struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data);
    423   struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
    424   CURLcode result;
    425   bool rv = FALSE;
    426 
    427   /* We would like to limit the amount of data we are buffer here. There seems
    428    * to be no mechanism in msh3 to adjust flow control and it is undocumented
    429    * what happens if we return FALSE here or less length (buflen is an inout
    430    * parameter).
    431    */
    432   (void)Request;
    433   if(!stream)
    434     return FALSE;
    435 
    436   msh3_lock_acquire(&stream->recv_lock);
    437 
    438   if(!stream->recv_header_complete) {
    439     result = write_resp_raw(data, "\r\n", 2);
    440     if(result) {
    441       stream->recv_error = result;
    442       goto out;
    443     }
    444     stream->recv_header_complete = TRUE;
    445   }
    446 
    447   result = write_resp_raw(data, buf, *buflen);
    448   if(result) {
    449     stream->recv_error = result;
    450   }
    451   rv = TRUE;
    452 
    453 out:
    454   msh3_lock_release(&stream->recv_lock);
    455   return rv;
    456 }
    457 
    458 static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
    459                                     bool aborted, uint64_t error)
    460 {
    461   struct Curl_easy *data = IfContext;
    462   struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data);
    463   struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
    464 
    465   (void)Request;
    466   if(!stream)
    467     return;
    468   msh3_lock_acquire(&stream->recv_lock);
    469   stream->closed = TRUE;
    470   stream->recv_header_complete = TRUE;
    471   if(error)
    472     stream->error3 = error;
    473   if(aborted)
    474     stream->reset = TRUE;
    475   msh3_lock_release(&stream->recv_lock);
    476 }
    477 
    478 static void MSH3_CALL msh3_shutdown_complete(MSH3_REQUEST *Request,
    479                                              void *IfContext)
    480 {
    481   struct Curl_easy *data = IfContext;
    482   struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data);
    483   struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
    484 
    485   if(!stream)
    486     return;
    487   (void)Request;
    488   (void)stream;
    489 }
    490 
    491 static void MSH3_CALL msh3_data_sent(MSH3_REQUEST *Request,
    492                                      void *IfContext, void *SendContext)
    493 {
    494   struct Curl_easy *data = IfContext;
    495   struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data);
    496   struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
    497   if(!stream)
    498     return;
    499   (void)Request;
    500   (void)stream;
    501   (void)SendContext;
    502 }
    503 
    504 static CURLcode recv_closed_stream(struct Curl_cfilter *cf,
    505                                    struct Curl_easy *data,
    506                                    size_t *pnread)
    507 {
    508   struct cf_msh3_ctx *ctx = cf->ctx;
    509   struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
    510 
    511   (void)cf;
    512   *pnread = 0;
    513   if(!stream)
    514     return CURLE_RECV_ERROR;
    515 
    516   if(stream->reset) {
    517     failf(data, "HTTP/3 stream reset by server");
    518     CURL_TRC_CF(data, cf, "cf_recv, was reset");
    519     return CURLE_PARTIAL_FILE;
    520   }
    521   else if(stream->error3) {
    522     failf(data, "HTTP/3 stream was not closed cleanly: (error %zd)",
    523           (ssize_t)stream->error3);
    524     CURL_TRC_CF(data, cf, "cf_recv, closed uncleanly");
    525     return CURLE_HTTP3;
    526   }
    527   else {
    528     CURL_TRC_CF(data, cf, "cf_recv, closed ok");
    529   }
    530   return CURLE_OK;
    531 }
    532 
    533 static void set_quic_expire(struct Curl_cfilter *cf, struct Curl_easy *data)
    534 {
    535   struct cf_msh3_ctx *ctx = cf->ctx;
    536   struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
    537 
    538   /* we have no indication from msh3 when it would be a good time
    539    * to juggle the connection again. So, we compromise by calling
    540    * us again every some milliseconds. */
    541   (void)cf;
    542   if(stream && stream->req && !stream->closed) {
    543     Curl_expire(data, 10, EXPIRE_QUIC);
    544   }
    545   else {
    546     Curl_expire(data, 50, EXPIRE_QUIC);
    547   }
    548 }
    549 
    550 static CURLcode cf_msh3_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
    551                              char *buf, size_t len, size_t *pnread)
    552 {
    553   struct cf_msh3_ctx *ctx = cf->ctx;
    554   struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
    555   struct cf_call_data save;
    556   CURLcode result = CURLE_OK;
    557 
    558   *pnread = 0;
    559   CURL_TRC_CF(data, cf, "cf_recv(len=%zu), stream=%d", len, !!stream);
    560   if(!stream)
    561     return CURLE_RECV_ERROR;
    562   CF_DATA_SAVE(save, cf, data);
    563 
    564   msh3_lock_acquire(&stream->recv_lock);
    565 
    566   if(stream->recv_error) {
    567     failf(data, "request aborted");
    568     result = stream->recv_error;
    569     goto out;
    570   }
    571 
    572   if(!Curl_bufq_is_empty(&stream->recvbuf)) {
    573     result = Curl_bufq_cread(&stream->recvbuf, buf, len, pnread);
    574     CURL_TRC_CF(data, cf, "read recvbuf(len=%zu) -> %d, %zu",
    575                 len, result, *pnread);
    576     if(result)
    577       goto out;
    578     if(stream->closed)
    579       h3_drain_stream(cf, data);
    580   }
    581   else if(stream->closed) {
    582     result = recv_closed_stream(cf, data, pnread);
    583     goto out;
    584   }
    585   else {
    586     CURL_TRC_CF(data, cf, "req: nothing here, call again");
    587     result = CURLE_AGAIN;
    588   }
    589 
    590 out:
    591   msh3_lock_release(&stream->recv_lock);
    592   set_quic_expire(cf, data);
    593   CF_DATA_RESTORE(cf, save);
    594   return result;
    595 }
    596 
    597 static CURLcode cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data,
    598                              const void *buf, size_t len, bool eos,
    599                              size_t *pnwritten)
    600 {
    601   struct cf_msh3_ctx *ctx = cf->ctx;
    602   struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
    603   struct h1_req_parser h1;
    604   struct dynhds h2_headers;
    605   MSH3_HEADER *nva = NULL;
    606   size_t nheader, i;
    607   ssize_t nwritten = -1;
    608   struct cf_call_data save;
    609   CURLcode result = CURLE_OK;
    610 
    611   *pnwritten = 0;
    612   CF_DATA_SAVE(save, cf, data);
    613 
    614   Curl_h1_req_parse_init(&h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
    615   Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST);
    616 
    617   /* Sizes must match for cast below to work" */
    618   DEBUGASSERT(stream);
    619   CURL_TRC_CF(data, cf, "req: send %zu bytes", len);
    620 
    621   if(!stream->req) {
    622     /* The first send on the request contains the headers and possibly some
    623        data. Parse out the headers and create the request, then if there is
    624        any data left over go ahead and send it too. */
    625     nwritten = Curl_h1_req_parse_read(&h1, buf, len, NULL, 0, &result);
    626     if(nwritten < 0)
    627       goto out;
    628     DEBUGASSERT(h1.done);
    629     DEBUGASSERT(h1.req);
    630     *pnwritten = (size_t)nwritten;
    631 
    632     result = Curl_http_req_to_h2(&h2_headers, h1.req, data);
    633     if(result)
    634       goto out;
    635 
    636     nheader = Curl_dynhds_count(&h2_headers);
    637     nva = malloc(sizeof(MSH3_HEADER) * nheader);
    638     if(!nva) {
    639       result = CURLE_OUT_OF_MEMORY;
    640       goto out;
    641     }
    642 
    643     for(i = 0; i < nheader; ++i) {
    644       struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i);
    645       nva[i].Name = e->name;
    646       nva[i].NameLength = e->namelen;
    647       nva[i].Value = e->value;
    648       nva[i].ValueLength = e->valuelen;
    649     }
    650 
    651     CURL_TRC_CF(data, cf, "req: send %zu headers", nheader);
    652     stream->req = MsH3RequestOpen(ctx->qconn, &msh3_request_if, data,
    653                                   nva, nheader,
    654                                   eos ? MSH3_REQUEST_FLAG_FIN :
    655                                   MSH3_REQUEST_FLAG_NONE);
    656     if(!stream->req) {
    657       failf(data, "request open failed");
    658       result = CURLE_SEND_ERROR;
    659     }
    660     result = CURLE_OK;
    661     *pnwritten = len;
    662     goto out;
    663   }
    664   else {
    665     /* request is open */
    666     CURL_TRC_CF(data, cf, "req: send %zu body bytes", len);
    667     if(len > 0xFFFFFFFF) {
    668       len = 0xFFFFFFFF;
    669     }
    670 
    671     if(!MsH3RequestSend(stream->req, MSH3_REQUEST_FLAG_NONE, buf,
    672                         (uint32_t)len, stream)) {
    673       result = CURLE_SEND_ERROR;
    674       goto out;
    675     }
    676 
    677     /* msh3/msquic will hold onto this memory until the send complete event.
    678        How do we make sure curl does not free it until then? */
    679     result = CURLE_OK;
    680     *pnwritten = len;
    681   }
    682 
    683 out:
    684   set_quic_expire(cf, data);
    685   free(nva);
    686   Curl_h1_req_parse_free(&h1);
    687   Curl_dynhds_free(&h2_headers);
    688   CF_DATA_RESTORE(cf, save);
    689   return result;
    690 }
    691 
    692 static void cf_msh3_adjust_pollset(struct Curl_cfilter *cf,
    693                                    struct Curl_easy *data,
    694                                    struct easy_pollset *ps)
    695 {
    696   struct cf_msh3_ctx *ctx = cf->ctx;
    697   struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
    698   struct cf_call_data save;
    699 
    700   CF_DATA_SAVE(save, cf, data);
    701   if(stream && ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD) {
    702     if(stream->recv_error) {
    703       Curl_pollset_add_in(data, ps, ctx->sock[SP_LOCAL]);
    704       h3_drain_stream(cf, data);
    705     }
    706     else if(stream->req) {
    707       Curl_pollset_add_out(data, ps, ctx->sock[SP_LOCAL]);
    708       h3_drain_stream(cf, data);
    709     }
    710   }
    711 }
    712 
    713 static bool cf_msh3_data_pending(struct Curl_cfilter *cf,
    714                                  const struct Curl_easy *data)
    715 {
    716   struct cf_msh3_ctx *ctx = cf->ctx;
    717   struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
    718   struct cf_call_data save;
    719   bool pending = FALSE;
    720 
    721   CF_DATA_SAVE(save, cf, data);
    722 
    723   (void)cf;
    724   if(stream && stream->req) {
    725     msh3_lock_acquire(&stream->recv_lock);
    726     CURL_TRC_CF((struct Curl_easy *)CURL_UNCONST(data), cf,
    727                 "data pending = %zu",
    728                 Curl_bufq_len(&stream->recvbuf));
    729     pending = !Curl_bufq_is_empty(&stream->recvbuf);
    730     msh3_lock_release(&stream->recv_lock);
    731     if(pending)
    732       h3_drain_stream(cf, (struct Curl_easy *)CURL_UNCONST(data));
    733   }
    734 
    735   CF_DATA_RESTORE(cf, save);
    736   return pending;
    737 }
    738 
    739 static CURLcode h3_data_pause(struct Curl_cfilter *cf,
    740                               struct Curl_easy *data,
    741                               bool pause)
    742 {
    743   if(!pause) {
    744     h3_drain_stream(cf, data);
    745     Curl_expire(data, 0, EXPIRE_RUN_NOW);
    746   }
    747   return CURLE_OK;
    748 }
    749 
    750 static CURLcode cf_msh3_data_event(struct Curl_cfilter *cf,
    751                                    struct Curl_easy *data,
    752                                    int event, int arg1, void *arg2)
    753 {
    754   struct cf_msh3_ctx *ctx = cf->ctx;
    755   struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
    756   struct cf_call_data save;
    757   CURLcode result = CURLE_OK;
    758 
    759   CF_DATA_SAVE(save, cf, data);
    760 
    761   (void)arg1;
    762   (void)arg2;
    763   switch(event) {
    764   case CF_CTRL_DATA_SETUP:
    765     result = h3_data_setup(cf, data);
    766     break;
    767   case CF_CTRL_DATA_PAUSE:
    768     result = h3_data_pause(cf, data, (arg1 != 0));
    769     break;
    770   case CF_CTRL_DATA_DONE:
    771     h3_data_done(cf, data);
    772     break;
    773   case CF_CTRL_DATA_DONE_SEND:
    774     CURL_TRC_CF(data, cf, "req: send done");
    775     if(stream) {
    776       stream->upload_done = TRUE;
    777       if(stream->req) {
    778         char buf[1];
    779         if(!MsH3RequestSend(stream->req, MSH3_REQUEST_FLAG_FIN,
    780                             buf, 0, data)) {
    781           result = CURLE_SEND_ERROR;
    782         }
    783       }
    784     }
    785     break;
    786   default:
    787     break;
    788   }
    789 
    790   CF_DATA_RESTORE(cf, save);
    791   return result;
    792 }
    793 
    794 static CURLcode cf_connect_start(struct Curl_cfilter *cf,
    795                                  struct Curl_easy *data)
    796 {
    797   struct cf_msh3_ctx *ctx = cf->ctx;
    798   struct ssl_primary_config *conn_config;
    799   MSH3_ADDR addr = {0};
    800   CURLcode result;
    801   bool verify;
    802 
    803   DEBUGASSERT(ctx->initialized);
    804   conn_config = Curl_ssl_cf_get_primary_config(cf);
    805   if(!conn_config)
    806     return CURLE_FAILED_INIT;
    807   verify = !!conn_config->verifypeer;
    808 
    809   memcpy(&addr, &ctx->addr.curl_sa_addr, ctx->addr.addrlen);
    810   MSH3_SET_PORT(&addr, (uint16_t)cf->conn->remote_port);
    811 
    812   if(verify && (conn_config->CAfile || conn_config->CApath)) {
    813     /* Note there's currently no way to provide trust anchors to MSH3 and
    814        that causes tests to fail. */
    815     CURL_TRC_CF(data, cf, "non-standard CA not supported, "
    816                 "attempting with built-in verification");
    817   }
    818 
    819   CURL_TRC_CF(data, cf, "connecting to %s:%d (verify=%d)",
    820               cf->conn->host.name, (int)cf->conn->remote_port, verify);
    821 
    822   ctx->api = MsH3ApiOpen();
    823   if(!ctx->api) {
    824     failf(data, "cannot create msh3 api");
    825     return CURLE_FAILED_INIT;
    826   }
    827 
    828   ctx->qconn = MsH3ConnectionOpen(ctx->api,
    829                                   &msh3_conn_if,
    830                                   cf,
    831                                   cf->conn->host.name,
    832                                   &addr,
    833                                   !verify);
    834   if(!ctx->qconn) {
    835     failf(data, "cannot create msh3 connection");
    836     if(ctx->api) {
    837       MsH3ApiClose(ctx->api);
    838       ctx->api = NULL;
    839     }
    840     return CURLE_FAILED_INIT;
    841   }
    842 
    843   result = h3_data_setup(cf, data);
    844   if(result)
    845     return result;
    846 
    847   return CURLE_OK;
    848 }
    849 
    850 static CURLcode cf_msh3_connect(struct Curl_cfilter *cf,
    851                                 struct Curl_easy *data,
    852                                 bool *done)
    853 {
    854   struct cf_msh3_ctx *ctx = cf->ctx;
    855   struct cf_call_data save;
    856   CURLcode result = CURLE_OK;
    857 
    858   if(cf->connected) {
    859     *done = TRUE;
    860     return CURLE_OK;
    861   }
    862 
    863   CF_DATA_SAVE(save, cf, data);
    864 
    865   if(ctx->sock[SP_LOCAL] == CURL_SOCKET_BAD) {
    866     if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, &ctx->sock[0], FALSE) < 0) {
    867       ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
    868       ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD;
    869       return CURLE_COULDNT_CONNECT;
    870     }
    871   }
    872 
    873   *done = FALSE;
    874   if(!ctx->qconn) {
    875     ctx->connect_started = curlx_now();
    876     result = cf_connect_start(cf, data);
    877     if(result)
    878       goto out;
    879   }
    880 
    881   if(ctx->handshake_complete) {
    882     ctx->handshake_at = curlx_now();
    883     if(ctx->handshake_succeeded) {
    884       CURL_TRC_CF(data, cf, "handshake succeeded");
    885       cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
    886       cf->connected = TRUE;
    887       cf->conn->alpn = CURL_HTTP_VERSION_3;
    888       *done = TRUE;
    889       connkeep(cf->conn, "HTTP/3 default");
    890       Curl_pgrsTime(data, TIMER_APPCONNECT);
    891     }
    892     else {
    893       failf(data, "failed to connect, handshake failed");
    894       result = CURLE_COULDNT_CONNECT;
    895     }
    896   }
    897 
    898 out:
    899   CF_DATA_RESTORE(cf, save);
    900   return result;
    901 }
    902 
    903 static void cf_msh3_close(struct Curl_cfilter *cf, struct Curl_easy *data)
    904 {
    905   struct cf_msh3_ctx *ctx = cf->ctx;
    906   struct cf_call_data save;
    907 
    908   (void)data;
    909   CF_DATA_SAVE(save, cf, data);
    910 
    911   if(ctx) {
    912     CURL_TRC_CF(data, cf, "destroying");
    913     if(ctx->qconn) {
    914       MsH3ConnectionClose(ctx->qconn);
    915       ctx->qconn = NULL;
    916     }
    917     if(ctx->api) {
    918       MsH3ApiClose(ctx->api);
    919       ctx->api = NULL;
    920     }
    921 
    922     if(ctx->active) {
    923       /* We share our socket at cf->conn->sock[cf->sockindex] when active.
    924        * If it is no longer there, someone has stolen (and hopefully
    925        * closed it) and we just forget about it.
    926        */
    927       ctx->active = FALSE;
    928       if(ctx->sock[SP_LOCAL] == cf->conn->sock[cf->sockindex]) {
    929         CURL_TRC_CF(data, cf, "cf_msh3_close(%d) active",
    930                     (int)ctx->sock[SP_LOCAL]);
    931         cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
    932       }
    933       else {
    934         CURL_TRC_CF(data, cf, "cf_socket_close(%d) no longer at "
    935                     "conn->sock[], discarding", (int)ctx->sock[SP_LOCAL]);
    936         ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
    937       }
    938     }
    939     if(ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD) {
    940       sclose(ctx->sock[SP_LOCAL]);
    941     }
    942     if(ctx->sock[SP_REMOTE] != CURL_SOCKET_BAD) {
    943       sclose(ctx->sock[SP_REMOTE]);
    944     }
    945     ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
    946     ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD;
    947   }
    948   CF_DATA_RESTORE(cf, save);
    949 }
    950 
    951 static void cf_msh3_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
    952 {
    953   struct cf_call_data save;
    954 
    955   CF_DATA_SAVE(save, cf, data);
    956   cf_msh3_close(cf, data);
    957   if(cf->ctx) {
    958     cf_msh3_ctx_free(cf->ctx);
    959     cf->ctx = NULL;
    960   }
    961   /* no CF_DATA_RESTORE(cf, save); its gone */
    962 }
    963 
    964 static CURLcode cf_msh3_query(struct Curl_cfilter *cf,
    965                               struct Curl_easy *data,
    966                               int query, int *pres1, void *pres2)
    967 {
    968   struct cf_msh3_ctx *ctx = cf->ctx;
    969 
    970   switch(query) {
    971   case CF_QUERY_MAX_CONCURRENT: {
    972     /* We do not have access to this so far, fake it */
    973     (void)ctx;
    974     *pres1 = 100;
    975     return CURLE_OK;
    976   }
    977   case CF_QUERY_TIMER_CONNECT: {
    978     struct curltime *when = pres2;
    979     /* we do not know when the first byte arrived */
    980     if(cf->connected)
    981       *when = ctx->handshake_at;
    982     return CURLE_OK;
    983   }
    984   case CF_QUERY_TIMER_APPCONNECT: {
    985     struct curltime *when = pres2;
    986     if(cf->connected)
    987       *when = ctx->handshake_at;
    988     return CURLE_OK;
    989   }
    990   case CF_QUERY_HTTP_VERSION:
    991     *pres1 = 30;
    992     return CURLE_OK;
    993   default:
    994     break;
    995   }
    996   return cf->next ?
    997     cf->next->cft->query(cf->next, data, query, pres1, pres2) :
    998     CURLE_UNKNOWN_OPTION;
    999 }
   1000 
   1001 static bool cf_msh3_conn_is_alive(struct Curl_cfilter *cf,
   1002                                   struct Curl_easy *data,
   1003                                   bool *input_pending)
   1004 {
   1005   struct cf_msh3_ctx *ctx = cf->ctx;
   1006 
   1007   (void)data;
   1008   *input_pending = FALSE;
   1009   return ctx && ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD && ctx->qconn &&
   1010          ctx->connected;
   1011 }
   1012 
   1013 struct Curl_cftype Curl_cft_http3 = {
   1014   "HTTP/3",
   1015   CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX | CF_TYPE_HTTP,
   1016   0,
   1017   cf_msh3_destroy,
   1018   cf_msh3_connect,
   1019   cf_msh3_close,
   1020   Curl_cf_def_shutdown,
   1021   cf_msh3_adjust_pollset,
   1022   cf_msh3_data_pending,
   1023   cf_msh3_send,
   1024   cf_msh3_recv,
   1025   cf_msh3_data_event,
   1026   cf_msh3_conn_is_alive,
   1027   Curl_cf_def_conn_keep_alive,
   1028   cf_msh3_query,
   1029 };
   1030 
   1031 static struct cf_msh3_ctx *h3_get_msh3_ctx(struct Curl_easy *data)
   1032 {
   1033   if(data && data->conn) {
   1034     struct Curl_cfilter *cf = data->conn->cfilter[FIRSTSOCKET];
   1035     while(cf) {
   1036       if(cf->cft == &Curl_cft_http3)
   1037         return cf->ctx;
   1038       cf = cf->next;
   1039     }
   1040   }
   1041   DEBUGF(infof(data, "no filter context found"));
   1042   return NULL;
   1043 }
   1044 
   1045 CURLcode Curl_cf_msh3_create(struct Curl_cfilter **pcf,
   1046                              struct Curl_easy *data,
   1047                              struct connectdata *conn,
   1048                              const struct Curl_addrinfo *ai)
   1049 {
   1050   struct cf_msh3_ctx *ctx = NULL;
   1051   struct Curl_cfilter *cf = NULL;
   1052   CURLcode result;
   1053 
   1054   (void)data;
   1055   (void)conn;
   1056   (void)ai; /* msh3 resolves itself? */
   1057   ctx = calloc(1, sizeof(*ctx));
   1058   if(!ctx) {
   1059     result = CURLE_OUT_OF_MEMORY;
   1060     goto out;
   1061   }
   1062 
   1063   result = cf_msh3_ctx_init(ctx, ai);
   1064   if(result)
   1065     goto out;
   1066 
   1067   result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
   1068 
   1069 out:
   1070   *pcf = (!result) ? cf : NULL;
   1071   if(result) {
   1072     Curl_safefree(cf);
   1073     cf_msh3_ctx_free(ctx);
   1074   }
   1075 
   1076   return result;
   1077 }
   1078 
   1079 bool Curl_conn_is_msh3(const struct Curl_easy *data,
   1080                        const struct connectdata *conn,
   1081                        int sockindex)
   1082 {
   1083   struct Curl_cfilter *cf = conn ? conn->cfilter[sockindex] : NULL;
   1084 
   1085   (void)data;
   1086   for(; cf; cf = cf->next) {
   1087     if(cf->cft == &Curl_cft_http3)
   1088       return TRUE;
   1089     if(cf->cft->flags & CF_TYPE_IP_CONNECT)
   1090       return FALSE;
   1091   }
   1092   return FALSE;
   1093 }
   1094 
   1095 #endif /* USE_MSH3 */