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