Home | History | Annotate | Line # | Download | only in http3
ossl-nghttp3.c revision 1.1
      1 /*
      2  * Copyright 2023-2024 The OpenSSL Project Authors. All Rights Reserved.
      3  *
      4  * Licensed under the Apache License 2.0 (the "License").  You may not use
      5  * this file except in compliance with the License.  You can obtain a copy
      6  * in the file LICENSE in the source distribution or at
      7  * https://www.openssl.org/source/license.html
      8  */
      9 #include "ossl-nghttp3.h"
     10 #include <openssl/err.h>
     11 #include <assert.h>
     12 
     13 #define ARRAY_LEN(x) (sizeof(x)/sizeof((x)[0]))
     14 
     15 enum {
     16     OSSL_DEMO_H3_STREAM_TYPE_CTRL_SEND,
     17     OSSL_DEMO_H3_STREAM_TYPE_QPACK_ENC_SEND,
     18     OSSL_DEMO_H3_STREAM_TYPE_QPACK_DEC_SEND,
     19     OSSL_DEMO_H3_STREAM_TYPE_REQ,
     20 };
     21 
     22 #define BUF_SIZE    4096
     23 
     24 struct ossl_demo_h3_stream_st {
     25     uint64_t            id;             /* QUIC stream ID */
     26     SSL                 *s;             /* QUIC stream SSL object */
     27     int                 done_recv_fin;  /* Received FIN */
     28     void                *user_data;
     29 
     30     uint8_t             buf[BUF_SIZE];
     31     size_t              buf_cur, buf_total;
     32 };
     33 
     34 DEFINE_LHASH_OF_EX(OSSL_DEMO_H3_STREAM);
     35 
     36 static void h3_stream_free(OSSL_DEMO_H3_STREAM *s)
     37 {
     38     if (s == NULL)
     39         return;
     40 
     41     SSL_free(s->s);
     42     OPENSSL_free(s);
     43 }
     44 
     45 static unsigned long h3_stream_hash(const OSSL_DEMO_H3_STREAM *s)
     46 {
     47     return (unsigned long)s->id;
     48 }
     49 
     50 static int h3_stream_eq(const OSSL_DEMO_H3_STREAM *a, const OSSL_DEMO_H3_STREAM *b)
     51 {
     52     if (a->id < b->id) return -1;
     53     if (a->id > b->id) return 1;
     54     return 0;
     55 }
     56 
     57 void *OSSL_DEMO_H3_STREAM_get_user_data(const OSSL_DEMO_H3_STREAM *s)
     58 {
     59     return s->user_data;
     60 }
     61 
     62 struct ossl_demo_h3_conn_st {
     63     /* QUIC connection SSL object */
     64     SSL                             *qconn;
     65     /* BIO wrapping QCSO */
     66     BIO                             *qconn_bio;
     67     /* HTTP/3 connection object */
     68     nghttp3_conn                    *h3conn;
     69     /* map of stream IDs to OSSL_DEMO_H3_STREAMs */
     70     LHASH_OF(OSSL_DEMO_H3_STREAM)   *streams;
     71     /* opaque user data pointer */
     72     void                            *user_data;
     73 
     74     int                             pump_res;
     75     size_t                          consumed_app_data;
     76 
     77     /* Forwarding callbacks */
     78     nghttp3_recv_data               recv_data_cb;
     79     nghttp3_stream_close            stream_close_cb;
     80     nghttp3_stop_sending            stop_sending_cb;
     81     nghttp3_reset_stream            reset_stream_cb;
     82     nghttp3_deferred_consume        deferred_consume_cb;
     83 };
     84 
     85 void OSSL_DEMO_H3_CONN_free(OSSL_DEMO_H3_CONN *conn)
     86 {
     87     if (conn == NULL)
     88         return;
     89 
     90     lh_OSSL_DEMO_H3_STREAM_doall(conn->streams, h3_stream_free);
     91 
     92     nghttp3_conn_del(conn->h3conn);
     93     BIO_free_all(conn->qconn_bio);
     94     lh_OSSL_DEMO_H3_STREAM_free(conn->streams);
     95     OPENSSL_free(conn);
     96 }
     97 
     98 static OSSL_DEMO_H3_STREAM *h3_conn_create_stream(OSSL_DEMO_H3_CONN *conn, int type)
     99 {
    100     OSSL_DEMO_H3_STREAM *s;
    101     uint64_t flags = SSL_STREAM_FLAG_ADVANCE;
    102 
    103     if ((s = OPENSSL_zalloc(sizeof(OSSL_DEMO_H3_STREAM))) == NULL)
    104         return NULL;
    105 
    106     if (type != OSSL_DEMO_H3_STREAM_TYPE_REQ)
    107         flags |= SSL_STREAM_FLAG_UNI;
    108 
    109     if ((s->s = SSL_new_stream(conn->qconn, flags)) == NULL) {
    110         ERR_raise_data(ERR_LIB_USER, ERR_R_INTERNAL_ERROR,
    111                        "could not create QUIC stream object");
    112         goto err;
    113     }
    114 
    115     s->id   = SSL_get_stream_id(s->s);
    116     lh_OSSL_DEMO_H3_STREAM_insert(conn->streams, s);
    117     return s;
    118 
    119 err:
    120     OPENSSL_free(s);
    121     return NULL;
    122 }
    123 
    124 static OSSL_DEMO_H3_STREAM *h3_conn_accept_stream(OSSL_DEMO_H3_CONN *conn, SSL *qstream)
    125 {
    126     OSSL_DEMO_H3_STREAM *s;
    127 
    128     if ((s = OPENSSL_zalloc(sizeof(OSSL_DEMO_H3_STREAM))) == NULL)
    129         return NULL;
    130 
    131     s->id   = SSL_get_stream_id(qstream);
    132     s->s    = qstream;
    133     lh_OSSL_DEMO_H3_STREAM_insert(conn->streams, s);
    134     return s;
    135 }
    136 
    137 static void h3_conn_remove_stream(OSSL_DEMO_H3_CONN *conn, OSSL_DEMO_H3_STREAM *s)
    138 {
    139     if (s == NULL)
    140         return;
    141 
    142     lh_OSSL_DEMO_H3_STREAM_delete(conn->streams, s);
    143     h3_stream_free(s);
    144 }
    145 
    146 static int h3_conn_recv_data(nghttp3_conn *h3conn, int64_t stream_id,
    147                              const uint8_t *data, size_t datalen,
    148                              void *conn_user_data, void *stream_user_data)
    149 {
    150     OSSL_DEMO_H3_CONN *conn = conn_user_data;
    151 
    152     conn->consumed_app_data += datalen;
    153     if (conn->recv_data_cb == NULL)
    154         return 0;
    155 
    156     return conn->recv_data_cb(h3conn, stream_id, data, datalen,
    157                               conn_user_data, stream_user_data);
    158 }
    159 
    160 static int h3_conn_stream_close(nghttp3_conn *h3conn, int64_t stream_id,
    161                                 uint64_t app_error_code,
    162                                 void *conn_user_data, void *stream_user_data)
    163 {
    164     int ret = 0;
    165     OSSL_DEMO_H3_CONN *conn = conn_user_data;
    166     OSSL_DEMO_H3_STREAM *stream = stream_user_data;
    167 
    168     if (conn->stream_close_cb != NULL)
    169         ret = conn->stream_close_cb(h3conn, stream_id, app_error_code,
    170                                     conn_user_data, stream_user_data);
    171 
    172     h3_conn_remove_stream(conn, stream);
    173     return ret;
    174 }
    175 
    176 static int h3_conn_stop_sending(nghttp3_conn *h3conn, int64_t stream_id,
    177                                 uint64_t app_error_code,
    178                                 void *conn_user_data, void *stream_user_data)
    179 {
    180     int ret = 0;
    181     OSSL_DEMO_H3_CONN *conn = conn_user_data;
    182     OSSL_DEMO_H3_STREAM *stream = stream_user_data;
    183 
    184     if (conn->stop_sending_cb != NULL)
    185         ret = conn->stop_sending_cb(h3conn, stream_id, app_error_code,
    186                                     conn_user_data, stream_user_data);
    187 
    188     SSL_free(stream->s);
    189     stream->s = NULL;
    190     return ret;
    191 }
    192 
    193 static int h3_conn_reset_stream(nghttp3_conn *h3conn, int64_t stream_id,
    194                                 uint64_t app_error_code,
    195                                 void *conn_user_data, void *stream_user_data)
    196 {
    197     int ret = 0;
    198     OSSL_DEMO_H3_CONN *conn = conn_user_data;
    199     OSSL_DEMO_H3_STREAM *stream = stream_user_data;
    200     SSL_STREAM_RESET_ARGS args = {0};
    201 
    202     if (conn->reset_stream_cb != NULL)
    203         ret = conn->reset_stream_cb(h3conn, stream_id, app_error_code,
    204                                    conn_user_data, stream_user_data);
    205 
    206     if (stream->s != NULL) {
    207         args.quic_error_code = app_error_code;
    208 
    209         if (!SSL_stream_reset(stream->s, &args, sizeof(args)))
    210             return 1;
    211     }
    212 
    213     return ret;
    214 }
    215 
    216 static int h3_conn_deferred_consume(nghttp3_conn *h3conn, int64_t stream_id,
    217                                     size_t consumed,
    218                                     void *conn_user_data, void *stream_user_data)
    219 {
    220     int ret = 0;
    221     OSSL_DEMO_H3_CONN *conn = conn_user_data;
    222 
    223     if (conn->deferred_consume_cb != NULL)
    224         ret = conn->deferred_consume_cb(h3conn, stream_id, consumed,
    225                                         conn_user_data, stream_user_data);
    226 
    227     conn->consumed_app_data += consumed;
    228     return ret;
    229 }
    230 
    231 OSSL_DEMO_H3_CONN *OSSL_DEMO_H3_CONN_new_for_conn(BIO *qconn_bio,
    232                                                   const nghttp3_callbacks *callbacks,
    233                                                   const nghttp3_settings *settings,
    234                                                   void *user_data)
    235 {
    236     int ec;
    237     OSSL_DEMO_H3_CONN *conn;
    238     OSSL_DEMO_H3_STREAM *s_ctl_send = NULL;
    239     OSSL_DEMO_H3_STREAM *s_qpenc_send = NULL;
    240     OSSL_DEMO_H3_STREAM *s_qpdec_send = NULL;
    241     nghttp3_settings dsettings = {0};
    242     nghttp3_callbacks intl_callbacks = {0};
    243     static const unsigned char alpn[] = {2, 'h', '3'};
    244 
    245     if (qconn_bio == NULL) {
    246         ERR_raise_data(ERR_LIB_USER, ERR_R_PASSED_NULL_PARAMETER,
    247                        "QUIC connection BIO must be provided");
    248         return NULL;
    249     }
    250 
    251     if ((conn = OPENSSL_zalloc(sizeof(OSSL_DEMO_H3_CONN))) == NULL)
    252         return NULL;
    253 
    254     conn->qconn_bio = qconn_bio;
    255     conn->user_data = user_data;
    256 
    257     if (BIO_get_ssl(qconn_bio, &conn->qconn) == 0) {
    258         ERR_raise_data(ERR_LIB_USER, ERR_R_PASSED_INVALID_ARGUMENT,
    259                        "BIO must be an SSL BIO");
    260         goto err;
    261     }
    262 
    263     /* Create the map of stream IDs to OSSL_DEMO_H3_STREAM structures. */
    264     if ((conn->streams = lh_OSSL_DEMO_H3_STREAM_new(h3_stream_hash, h3_stream_eq)) == NULL)
    265         goto err;
    266 
    267     /*
    268      * If the application has not started connecting yet, helpfully
    269      * auto-configure ALPN. If the application wants to initiate the connection
    270      * itself, it must take care of this itself.
    271      */
    272     if (SSL_in_before(conn->qconn))
    273         if (SSL_set_alpn_protos(conn->qconn, alpn, sizeof(alpn))) {
    274             /* SSL_set_alpn_protos returns 1 on failure */
    275             ERR_raise_data(ERR_LIB_USER, ERR_R_INTERNAL_ERROR,
    276                            "failed to configure ALPN");
    277             goto err;
    278         }
    279 
    280     /*
    281      * We use the QUIC stack in non-blocking mode so that we can react to
    282      * incoming data on different streams, and e.g. incoming streams initiated
    283      * by a server, as and when events occur.
    284      */
    285     BIO_set_nbio(conn->qconn_bio, 1);
    286 
    287     /*
    288      * Disable default stream mode and create all streams explicitly. Each QUIC
    289      * stream will be represented by its own QUIC stream SSL object (QSSO). This
    290      * also automatically enables us to accept incoming streams (see
    291      * SSL_set_incoming_stream_policy(3)).
    292      */
    293     if (!SSL_set_default_stream_mode(conn->qconn, SSL_DEFAULT_STREAM_MODE_NONE)) {
    294         ERR_raise_data(ERR_LIB_USER, ERR_R_INTERNAL_ERROR,
    295                        "failed to configure default stream mode");
    296         goto err;
    297     }
    298 
    299     /*
    300      * HTTP/3 requires a couple of unidirectional management streams: a control
    301      * stream and some QPACK state management streams for each side of a
    302      * connection. These are the instances on our side (with us sending); the
    303      * server will also create its own equivalent unidirectional streams on its
    304      * side, which we handle subsequently as they come in (see SSL_accept_stream
    305      * in the event handling code below).
    306      */
    307     if ((s_ctl_send
    308             = h3_conn_create_stream(conn, OSSL_DEMO_H3_STREAM_TYPE_CTRL_SEND)) == NULL)
    309         goto err;
    310 
    311     if ((s_qpenc_send
    312             = h3_conn_create_stream(conn, OSSL_DEMO_H3_STREAM_TYPE_QPACK_ENC_SEND)) == NULL)
    313         goto err;
    314 
    315     if ((s_qpdec_send
    316             = h3_conn_create_stream(conn, OSSL_DEMO_H3_STREAM_TYPE_QPACK_DEC_SEND)) == NULL)
    317         goto err;
    318 
    319     if (settings == NULL) {
    320         nghttp3_settings_default(&dsettings);
    321         settings = &dsettings;
    322     }
    323 
    324     if (callbacks != NULL)
    325         intl_callbacks = *callbacks;
    326 
    327     /*
    328      * We need to do some of our own processing when many of these events occur,
    329      * so we note the original callback functions and forward appropriately.
    330      */
    331     conn->recv_data_cb          = intl_callbacks.recv_data;
    332     conn->stream_close_cb       = intl_callbacks.stream_close;
    333     conn->stop_sending_cb       = intl_callbacks.stop_sending;
    334     conn->reset_stream_cb       = intl_callbacks.reset_stream;
    335     conn->deferred_consume_cb   = intl_callbacks.deferred_consume;
    336 
    337     intl_callbacks.recv_data        = h3_conn_recv_data;
    338     intl_callbacks.stream_close     = h3_conn_stream_close;
    339     intl_callbacks.stop_sending     = h3_conn_stop_sending;
    340     intl_callbacks.reset_stream     = h3_conn_reset_stream;
    341     intl_callbacks.deferred_consume = h3_conn_deferred_consume;
    342 
    343     /* Create the HTTP/3 client state. */
    344     ec = nghttp3_conn_client_new(&conn->h3conn, &intl_callbacks, settings,
    345                                  NULL, conn);
    346     if (ec < 0) {
    347         ERR_raise_data(ERR_LIB_USER, ERR_R_INTERNAL_ERROR,
    348                        "cannot create nghttp3 connection: %s (%d)",
    349                        nghttp3_strerror(ec), ec);
    350         goto err;
    351     }
    352 
    353     /*
    354      * Tell the HTTP/3 stack which stream IDs are used for our outgoing control
    355      * and QPACK streams. Note that we don't have to tell the HTTP/3 stack what
    356      * IDs are used for incoming streams as this is inferred automatically from
    357      * the stream type byte which starts every incoming unidirectional stream,
    358      * so it will autodetect the correct stream IDs for the incoming control and
    359      * QPACK streams initiated by the server.
    360      */
    361     ec = nghttp3_conn_bind_control_stream(conn->h3conn, s_ctl_send->id);
    362     if (ec < 0) {
    363         ERR_raise_data(ERR_LIB_USER, ERR_R_INTERNAL_ERROR,
    364                        "cannot bind nghttp3 control stream: %s (%d)",
    365                        nghttp3_strerror(ec), ec);
    366         goto err;
    367     }
    368 
    369     ec = nghttp3_conn_bind_qpack_streams(conn->h3conn,
    370                                          s_qpenc_send->id,
    371                                          s_qpdec_send->id);
    372     if (ec < 0) {
    373         ERR_raise_data(ERR_LIB_USER, ERR_R_INTERNAL_ERROR,
    374                        "cannot bind nghttp3 QPACK streams: %s (%d)",
    375                        nghttp3_strerror(ec), ec);
    376         goto err;
    377     }
    378 
    379     return conn;
    380 
    381 err:
    382     nghttp3_conn_del(conn->h3conn);
    383     h3_stream_free(s_ctl_send);
    384     h3_stream_free(s_qpenc_send);
    385     h3_stream_free(s_qpdec_send);
    386     lh_OSSL_DEMO_H3_STREAM_free(conn->streams);
    387     OPENSSL_free(conn);
    388     return NULL;
    389 }
    390 
    391 OSSL_DEMO_H3_CONN *OSSL_DEMO_H3_CONN_new_for_addr(SSL_CTX *ctx, const char *addr,
    392                                                   const nghttp3_callbacks *callbacks,
    393                                                   const nghttp3_settings *settings,
    394                                                   void *user_data)
    395 {
    396     BIO *qconn_bio = NULL;
    397     SSL *qconn = NULL;
    398     OSSL_DEMO_H3_CONN *conn = NULL;
    399     const char *bare_hostname;
    400 
    401     /* QUIC connection setup */
    402     if ((qconn_bio = BIO_new_ssl_connect(ctx)) == NULL)
    403         goto err;
    404 
    405     /* Pass the 'hostname:port' string into the ssl_connect BIO. */
    406     if (BIO_set_conn_hostname(qconn_bio, addr) == 0)
    407         goto err;
    408 
    409     /*
    410      * Get the 'bare' hostname out of the ssl_connect BIO. This is the hostname
    411      * without the port.
    412      */
    413     bare_hostname = BIO_get_conn_hostname(qconn_bio);
    414     if (bare_hostname == NULL)
    415         goto err;
    416 
    417     if (BIO_get_ssl(qconn_bio, &qconn) == 0)
    418         goto err;
    419 
    420     /* Set the hostname we will validate the X.509 certificate against. */
    421     if (SSL_set1_host(qconn, bare_hostname) <= 0)
    422         goto err;
    423 
    424     /* Configure SNI */
    425     if (!SSL_set_tlsext_host_name(qconn, bare_hostname))
    426         goto err;
    427 
    428     conn = OSSL_DEMO_H3_CONN_new_for_conn(qconn_bio, callbacks,
    429                                           settings, user_data);
    430     if (conn == NULL)
    431         goto err;
    432 
    433     return conn;
    434 
    435 err:
    436     BIO_free_all(qconn_bio);
    437     return NULL;
    438 }
    439 
    440 int OSSL_DEMO_H3_CONN_connect(OSSL_DEMO_H3_CONN *conn)
    441 {
    442     return SSL_connect(OSSL_DEMO_H3_CONN_get0_connection(conn));
    443 }
    444 
    445 void *OSSL_DEMO_H3_CONN_get_user_data(const OSSL_DEMO_H3_CONN *conn)
    446 {
    447     return conn->user_data;
    448 }
    449 
    450 SSL *OSSL_DEMO_H3_CONN_get0_connection(const OSSL_DEMO_H3_CONN *conn)
    451 {
    452     return conn->qconn;
    453 }
    454 
    455 /* Pumps received data to the HTTP/3 stack for a single stream. */
    456 static void h3_conn_pump_stream(OSSL_DEMO_H3_STREAM *s, void *conn_)
    457 {
    458     int ec;
    459     OSSL_DEMO_H3_CONN *conn = conn_;
    460     size_t num_bytes, consumed;
    461     uint64_t aec;
    462 
    463     if (!conn->pump_res)
    464         /*
    465          * Handling of a previous stream in the iteration over all streams
    466          * failed, so just do nothing.
    467          */
    468         return;
    469 
    470     for (;;) {
    471         if (s->s == NULL /* If we already did STOP_SENDING, ignore this stream. */
    472             /* If this is a write-only stream, there is no read data to check. */
    473             || SSL_get_stream_read_state(s->s) == SSL_STREAM_STATE_WRONG_DIR
    474             /*
    475              * If we already got a FIN for this stream, there is nothing more to
    476              * do for it.
    477              */
    478             || s->done_recv_fin)
    479             break;
    480 
    481         /*
    482          * Pump data from OpenSSL QUIC to the HTTP/3 stack by calling SSL_peek
    483          * to get received data and passing it to nghttp3 using
    484          * nghttp3_conn_read_stream. Note that this function is confusingly
    485          * named and inputs data to the HTTP/3 stack.
    486          */
    487         if (s->buf_cur == s->buf_total) {
    488             /* Need more data. */
    489             ec = SSL_read_ex(s->s, s->buf, sizeof(s->buf), &num_bytes);
    490             if (ec <= 0) {
    491                 num_bytes = 0;
    492                 if (SSL_get_error(s->s, ec) == SSL_ERROR_ZERO_RETURN) {
    493                     /* Stream concluded normally. Pass FIN to HTTP/3 stack. */
    494                     ec = nghttp3_conn_read_stream(conn->h3conn, s->id, NULL, 0,
    495                                                   /*fin=*/1);
    496                     if (ec < 0) {
    497                         ERR_raise_data(ERR_LIB_USER, ERR_R_INTERNAL_ERROR,
    498                                        "cannot pass FIN to nghttp3: %s (%d)",
    499                                        nghttp3_strerror(ec), ec);
    500                         goto err;
    501                     }
    502 
    503                     s->done_recv_fin = 1;
    504                 } else if (SSL_get_stream_read_state(s->s)
    505                             == SSL_STREAM_STATE_RESET_REMOTE) {
    506                     /* Stream was reset by peer. */
    507                     if (!SSL_get_stream_read_error_code(s->s, &aec))
    508                         goto err;
    509 
    510                     ec = nghttp3_conn_close_stream(conn->h3conn, s->id, aec);
    511                     if (ec < 0) {
    512                         ERR_raise_data(ERR_LIB_USER, ERR_R_INTERNAL_ERROR,
    513                                        "cannot mark stream as reset: %s (%d)",
    514                                        nghttp3_strerror(ec), ec);
    515                         goto err;
    516                     }
    517 
    518                     s->done_recv_fin = 1;
    519                 } else {
    520                     /* Other error. */
    521                     goto err;
    522                 }
    523             }
    524 
    525             s->buf_cur      = 0;
    526             s->buf_total    = num_bytes;
    527         }
    528 
    529         if (s->buf_cur == s->buf_total)
    530             break;
    531 
    532         /*
    533          * This function is confusingly named as it is is named from nghttp3's
    534          * 'perspective'; it is used to pass data *into* the HTTP/3 stack which
    535          * has been received from the network.
    536          */
    537         assert(conn->consumed_app_data == 0);
    538         ec = nghttp3_conn_read_stream(conn->h3conn, s->id, s->buf + s->buf_cur,
    539                                       s->buf_total - s->buf_cur, /*fin=*/0);
    540         if (ec < 0) {
    541             ERR_raise_data(ERR_LIB_USER, ERR_R_INTERNAL_ERROR,
    542                            "nghttp3 failed to process incoming data: %s (%d)",
    543                            nghttp3_strerror(ec), ec);
    544             goto err;
    545         }
    546 
    547         /*
    548          * read_stream reports the data it consumes from us in two different
    549          * ways; the non-application data is returned as a number of bytes 'ec'
    550          * above, but the number of bytes of application data has to be recorded
    551          * by our callback. We sum the two to determine the total number of
    552          * bytes which nghttp3 consumed.
    553          */
    554         consumed = ec + conn->consumed_app_data;
    555         assert(consumed <= s->buf_total - s->buf_cur);
    556         s->buf_cur += consumed;
    557         conn->consumed_app_data = 0;
    558     }
    559 
    560     return;
    561 err:
    562     conn->pump_res = 0;
    563 }
    564 
    565 int OSSL_DEMO_H3_CONN_handle_events(OSSL_DEMO_H3_CONN *conn)
    566 {
    567     int ec, fin;
    568     size_t i, num_vecs, written, total_written, total_len;
    569     int64_t stream_id;
    570     uint64_t flags;
    571     nghttp3_vec vecs[8] = {0};
    572     OSSL_DEMO_H3_STREAM key, *s;
    573     SSL *snew;
    574 
    575     if (conn == NULL)
    576         return 0;
    577 
    578     /*
    579      * We handle events by doing three things:
    580      *
    581      * 1. Handle new incoming streams
    582      * 2. Pump outgoing data from the HTTP/3 stack to the QUIC engine
    583      * 3. Pump incoming data from the QUIC engine to the HTTP/3 stack
    584      */
    585 
    586     /* 1. Check for new incoming streams */
    587     for (;;) {
    588         if ((snew = SSL_accept_stream(conn->qconn, SSL_ACCEPT_STREAM_NO_BLOCK)) == NULL)
    589             break;
    590 
    591         /*
    592          * Each new incoming stream gets wrapped into an OSSL_DEMO_H3_STREAM object and
    593          * added into our stream ID map.
    594          */
    595         if (h3_conn_accept_stream(conn, snew) == NULL) {
    596             SSL_free(snew);
    597             return 0;
    598         }
    599     }
    600 
    601     /* 2. Pump outgoing data from HTTP/3 engine to QUIC. */
    602     for (;;) {
    603         /*
    604          * Get a number of send vectors from the HTTP/3 engine.
    605          *
    606          * Note that this function is confusingly named as it is named from
    607          * nghttp3's 'perspective': this outputs pointers to data which nghttp3
    608          * wants to *write* to the network.
    609          */
    610         ec = nghttp3_conn_writev_stream(conn->h3conn, &stream_id, &fin,
    611                                         vecs, ARRAY_LEN(vecs));
    612         if (ec < 0)
    613             return 0;
    614         if (ec == 0)
    615             break;
    616 
    617         /*
    618 	 * we let SSL_write_ex2(3) to conclude the stream for us (send FIN)
    619 	 * after all data are written.
    620          */
    621         flags = (fin == 0) ? 0 : SSL_WRITE_FLAG_CONCLUDE;
    622 
    623         /* For each of the vectors returned, pass it to OpenSSL QUIC. */
    624         key.id = stream_id;
    625         if ((s = lh_OSSL_DEMO_H3_STREAM_retrieve(conn->streams, &key)) == NULL) {
    626             ERR_raise_data(ERR_LIB_USER, ERR_R_INTERNAL_ERROR,
    627                            "no stream for ID %zd", stream_id);
    628             return 0;
    629         }
    630 
    631         num_vecs = ec;
    632         total_len = nghttp3_vec_len(vecs, num_vecs);
    633         total_written = 0;
    634         for (i = 0; i < num_vecs; ++i) {
    635             if (vecs[i].len == 0)
    636                 continue;
    637 
    638             if (s->s == NULL) {
    639                 /* Already did STOP_SENDING and threw away stream, ignore */
    640                 written = vecs[i].len;
    641             } else if (!SSL_write_ex2(s->s, vecs[i].base, vecs[i].len, flags, &written)) {
    642                 if (SSL_get_error(s->s, 0) == SSL_ERROR_WANT_WRITE) {
    643                     /*
    644                      * We have filled our send buffer so tell nghttp3 to stop
    645                      * generating more data; we have to do this explicitly.
    646                      */
    647                     written = 0;
    648                     nghttp3_conn_block_stream(conn->h3conn, stream_id);
    649                 } else {
    650                     ERR_raise_data(ERR_LIB_USER, ERR_R_INTERNAL_ERROR,
    651                                    "writing HTTP/3 data to network failed");
    652                     return 0;
    653                 }
    654             } else {
    655                 /*
    656                  * Tell nghttp3 it can resume generating more data in case we
    657                  * previously called block_stream.
    658                  */
    659                 nghttp3_conn_unblock_stream(conn->h3conn, stream_id);
    660             }
    661 
    662             total_written += written;
    663             if (written > 0) {
    664                 /*
    665                  * Tell nghttp3 we have consumed the data it output when we
    666                  * called writev_stream, otherwise subsequent calls to
    667                  * writev_stream will output the same data.
    668                  */
    669                 ec = nghttp3_conn_add_write_offset(conn->h3conn, stream_id, written);
    670                 if (ec < 0)
    671                     return 0;
    672 
    673                 /*
    674                  * Tell nghttp3 it can free the buffered data because we will
    675                  * not need it again. In our case we can always do this right
    676                  * away because we copy the data into our QUIC send buffers
    677                  * rather than simply storing a reference to it.
    678                  */
    679                 ec = nghttp3_conn_add_ack_offset(conn->h3conn, stream_id, written);
    680                 if (ec < 0)
    681                     return 0;
    682             }
    683         }
    684 
    685         if (fin && total_written == total_len) {
    686 
    687             if (total_len == 0) {
    688                 /*
    689                  * As a special case, if nghttp3 requested to write a
    690                  * zero-length stream with a FIN, we have to tell it we did this
    691                  * by calling add_write_offset(0).
    692                  */
    693                 ec = nghttp3_conn_add_write_offset(conn->h3conn, stream_id, 0);
    694                 if (ec < 0)
    695                     return 0;
    696             }
    697         }
    698     }
    699 
    700     /* 3. Pump incoming data from QUIC to HTTP/3 engine. */
    701     conn->pump_res = 1; /* cleared in below call if an error occurs */
    702     lh_OSSL_DEMO_H3_STREAM_doall_arg(conn->streams, h3_conn_pump_stream, conn);
    703     if (!conn->pump_res)
    704         return 0;
    705 
    706     return 1;
    707 }
    708 
    709 int OSSL_DEMO_H3_CONN_submit_request(OSSL_DEMO_H3_CONN *conn,
    710                                      const nghttp3_nv *nva, size_t nvlen,
    711                                      const nghttp3_data_reader *dr,
    712                                      void *user_data)
    713 {
    714     int ec;
    715     OSSL_DEMO_H3_STREAM *s_req = NULL;
    716 
    717     if (conn == NULL) {
    718         ERR_raise_data(ERR_LIB_USER, ERR_R_PASSED_NULL_PARAMETER,
    719                        "connection must be specified");
    720         return 0;
    721     }
    722 
    723     /* Each HTTP/3 request is represented by a stream. */
    724     if ((s_req = h3_conn_create_stream(conn, OSSL_DEMO_H3_STREAM_TYPE_REQ)) == NULL)
    725         goto err;
    726 
    727     s_req->user_data = user_data;
    728 
    729     ec = nghttp3_conn_submit_request(conn->h3conn, s_req->id, nva, nvlen,
    730                                      dr, s_req);
    731     if (ec < 0) {
    732         ERR_raise_data(ERR_LIB_USER, ERR_R_INTERNAL_ERROR,
    733                        "cannot submit HTTP/3 request: %s (%d)",
    734                        nghttp3_strerror(ec), ec);
    735         goto err;
    736     }
    737 
    738     return 1;
    739 
    740 err:
    741     h3_conn_remove_stream(conn, s_req);
    742     return 0;
    743 }
    744