2 * Copyright 2023-2024 The OpenSSL Project Authors. All Rights Reserved.
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
9 #include "ossl-nghttp3.h"
10 #include <openssl/err.h>
13 #define ARRAY_LEN(x) (sizeof(x)/sizeof((x)[0]))
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
,
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 */
30 uint8_t buf
[BUF_SIZE
];
31 size_t buf_cur
, buf_total
;
34 DEFINE_LHASH_OF_EX(OSSL_DEMO_H3_STREAM
);
36 static void h3_stream_free(OSSL_DEMO_H3_STREAM
*s
)
45 static unsigned long h3_stream_hash(const OSSL_DEMO_H3_STREAM
*s
)
47 return (unsigned long)s
->id
;
50 static int h3_stream_eq(const OSSL_DEMO_H3_STREAM
*a
, const OSSL_DEMO_H3_STREAM
*b
)
52 if (a
->id
< b
->id
) return -1;
53 if (a
->id
> b
->id
) return 1;
57 void *OSSL_DEMO_H3_STREAM_get_user_data(const OSSL_DEMO_H3_STREAM
*s
)
62 struct ossl_demo_h3_conn_st
{
63 /* QUIC connection SSL object */
65 /* BIO wrapping QCSO */
67 /* HTTP/3 connection object */
69 /* map of stream IDs to OSSL_DEMO_H3_STREAMs */
70 LHASH_OF(OSSL_DEMO_H3_STREAM
) *streams
;
71 /* opaque user data pointer */
75 size_t consumed_app_data
;
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
;
85 void OSSL_DEMO_H3_CONN_free(OSSL_DEMO_H3_CONN
*conn
)
90 lh_OSSL_DEMO_H3_STREAM_doall(conn
->streams
, h3_stream_free
);
92 nghttp3_conn_del(conn
->h3conn
);
93 BIO_free_all(conn
->qconn_bio
);
94 lh_OSSL_DEMO_H3_STREAM_free(conn
->streams
);
98 static OSSL_DEMO_H3_STREAM
*h3_conn_create_stream(OSSL_DEMO_H3_CONN
*conn
, int type
)
100 OSSL_DEMO_H3_STREAM
*s
;
101 uint64_t flags
= SSL_STREAM_FLAG_ADVANCE
;
103 if ((s
= OPENSSL_zalloc(sizeof(OSSL_DEMO_H3_STREAM
))) == NULL
)
106 if (type
!= OSSL_DEMO_H3_STREAM_TYPE_REQ
)
107 flags
|= SSL_STREAM_FLAG_UNI
;
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");
115 s
->id
= SSL_get_stream_id(s
->s
);
116 lh_OSSL_DEMO_H3_STREAM_insert(conn
->streams
, s
);
124 static OSSL_DEMO_H3_STREAM
*h3_conn_accept_stream(OSSL_DEMO_H3_CONN
*conn
, SSL
*qstream
)
126 OSSL_DEMO_H3_STREAM
*s
;
128 if ((s
= OPENSSL_zalloc(sizeof(OSSL_DEMO_H3_STREAM
))) == NULL
)
131 s
->id
= SSL_get_stream_id(qstream
);
133 lh_OSSL_DEMO_H3_STREAM_insert(conn
->streams
, s
);
137 static void h3_conn_remove_stream(OSSL_DEMO_H3_CONN
*conn
, OSSL_DEMO_H3_STREAM
*s
)
142 lh_OSSL_DEMO_H3_STREAM_delete(conn
->streams
, s
);
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
)
150 OSSL_DEMO_H3_CONN
*conn
= conn_user_data
;
152 conn
->consumed_app_data
+= datalen
;
153 if (conn
->recv_data_cb
== NULL
)
156 return conn
->recv_data_cb(h3conn
, stream_id
, data
, datalen
,
157 conn_user_data
, stream_user_data
);
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
)
165 OSSL_DEMO_H3_CONN
*conn
= conn_user_data
;
166 OSSL_DEMO_H3_STREAM
*stream
= stream_user_data
;
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
);
172 h3_conn_remove_stream(conn
, stream
);
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
)
181 OSSL_DEMO_H3_CONN
*conn
= conn_user_data
;
182 OSSL_DEMO_H3_STREAM
*stream
= stream_user_data
;
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
);
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
)
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};
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
);
206 if (stream
->s
!= NULL
) {
207 args
.quic_error_code
= app_error_code
;
209 if (!SSL_stream_reset(stream
->s
, &args
, sizeof(args
)))
216 static int h3_conn_deferred_consume(nghttp3_conn
*h3conn
, int64_t stream_id
,
218 void *conn_user_data
, void *stream_user_data
)
221 OSSL_DEMO_H3_CONN
*conn
= conn_user_data
;
223 if (conn
->deferred_consume_cb
!= NULL
)
224 ret
= conn
->deferred_consume_cb(h3conn
, stream_id
, consumed
,
225 conn_user_data
, stream_user_data
);
227 conn
->consumed_app_data
+= consumed
;
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
,
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'};
245 if (qconn_bio
== NULL
) {
246 ERR_raise_data(ERR_LIB_USER
, ERR_R_PASSED_NULL_PARAMETER
,
247 "QUIC connection BIO must be provided");
251 if ((conn
= OPENSSL_zalloc(sizeof(OSSL_DEMO_H3_CONN
))) == NULL
)
254 conn
->qconn_bio
= qconn_bio
;
255 conn
->user_data
= user_data
;
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");
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
)
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.
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");
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.
285 BIO_set_nbio(conn
->qconn_bio
, 1);
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)).
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");
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).
308 = h3_conn_create_stream(conn
, OSSL_DEMO_H3_STREAM_TYPE_CTRL_SEND
)) == NULL
)
312 = h3_conn_create_stream(conn
, OSSL_DEMO_H3_STREAM_TYPE_QPACK_ENC_SEND
)) == NULL
)
316 = h3_conn_create_stream(conn
, OSSL_DEMO_H3_STREAM_TYPE_QPACK_DEC_SEND
)) == NULL
)
319 if (settings
== NULL
) {
320 nghttp3_settings_default(&dsettings
);
321 settings
= &dsettings
;
324 if (callbacks
!= NULL
)
325 intl_callbacks
= *callbacks
;
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.
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
;
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
;
343 /* Create the HTTP/3 client state. */
344 ec
= nghttp3_conn_client_new(&conn
->h3conn
, &intl_callbacks
, settings
,
347 ERR_raise_data(ERR_LIB_USER
, ERR_R_INTERNAL_ERROR
,
348 "cannot create nghttp3 connection: %s (%d)",
349 nghttp3_strerror(ec
), ec
);
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.
361 ec
= nghttp3_conn_bind_control_stream(conn
->h3conn
, s_ctl_send
->id
);
363 ERR_raise_data(ERR_LIB_USER
, ERR_R_INTERNAL_ERROR
,
364 "cannot bind nghttp3 control stream: %s (%d)",
365 nghttp3_strerror(ec
), ec
);
369 ec
= nghttp3_conn_bind_qpack_streams(conn
->h3conn
,
373 ERR_raise_data(ERR_LIB_USER
, ERR_R_INTERNAL_ERROR
,
374 "cannot bind nghttp3 QPACK streams: %s (%d)",
375 nghttp3_strerror(ec
), ec
);
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
);
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
,
396 BIO
*qconn_bio
= NULL
;
398 OSSL_DEMO_H3_CONN
*conn
= NULL
;
399 const char *bare_hostname
;
401 /* QUIC connection setup */
402 if ((qconn_bio
= BIO_new_ssl_connect(ctx
)) == NULL
)
405 /* Pass the 'hostname:port' string into the ssl_connect BIO. */
406 if (BIO_set_conn_hostname(qconn_bio
, addr
) == 0)
410 * Get the 'bare' hostname out of the ssl_connect BIO. This is the hostname
413 bare_hostname
= BIO_get_conn_hostname(qconn_bio
);
414 if (bare_hostname
== NULL
)
417 if (BIO_get_ssl(qconn_bio
, &qconn
) == 0)
420 /* Set the hostname we will validate the X.509 certificate against. */
421 if (SSL_set1_host(qconn
, bare_hostname
) <= 0)
425 if (!SSL_set_tlsext_host_name(qconn
, bare_hostname
))
428 conn
= OSSL_DEMO_H3_CONN_new_for_conn(qconn_bio
, callbacks
,
429 settings
, user_data
);
436 BIO_free_all(qconn_bio
);
440 int OSSL_DEMO_H3_CONN_connect(OSSL_DEMO_H3_CONN
*conn
)
442 return SSL_connect(OSSL_DEMO_H3_CONN_get0_connection(conn
));
445 void *OSSL_DEMO_H3_CONN_get_user_data(const OSSL_DEMO_H3_CONN
*conn
)
447 return conn
->user_data
;
450 SSL
*OSSL_DEMO_H3_CONN_get0_connection(const OSSL_DEMO_H3_CONN
*conn
)
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_
)
459 OSSL_DEMO_H3_CONN
*conn
= conn_
;
460 size_t num_bytes
, consumed
;
465 * Handling of a previous stream in the iteration over all streams
466 * failed, so just do nothing.
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
475 * If we already got a FIN for this stream, there is nothing more to
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.
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
);
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,
497 ERR_raise_data(ERR_LIB_USER
, ERR_R_INTERNAL_ERROR
,
498 "cannot pass FIN to nghttp3: %s (%d)",
499 nghttp3_strerror(ec
), ec
);
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
))
510 ec
= nghttp3_conn_close_stream(conn
->h3conn
, s
->id
, aec
);
512 ERR_raise_data(ERR_LIB_USER
, ERR_R_INTERNAL_ERROR
,
513 "cannot mark stream as reset: %s (%d)",
514 nghttp3_strerror(ec
), ec
);
518 s
->done_recv_fin
= 1;
526 s
->buf_total
= num_bytes
;
529 if (s
->buf_cur
== s
->buf_total
)
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.
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);
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
);
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.
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;
565 int OSSL_DEMO_H3_CONN_handle_events(OSSL_DEMO_H3_CONN
*conn
)
568 size_t i
, num_vecs
, written
, total_written
, total_len
;
571 nghttp3_vec vecs
[8] = {0};
572 OSSL_DEMO_H3_STREAM key
, *s
;
579 * We handle events by doing three things:
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
586 /* 1. Check for new incoming streams */
588 if ((snew
= SSL_accept_stream(conn
->qconn
, SSL_ACCEPT_STREAM_NO_BLOCK
)) == NULL
)
592 * Each new incoming stream gets wrapped into an OSSL_DEMO_H3_STREAM object and
593 * added into our stream ID map.
595 if (h3_conn_accept_stream(conn
, snew
) == NULL
) {
601 /* 2. Pump outgoing data from HTTP/3 engine to QUIC. */
604 * Get a number of send vectors from the HTTP/3 engine.
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.
610 ec
= nghttp3_conn_writev_stream(conn
->h3conn
, &stream_id
, &fin
,
611 vecs
, ARRAY_LEN(vecs
));
618 * we let SSL_write_ex2(3) to conclude the stream for us (send FIN)
619 * after all data are written.
621 flags
= (fin
== 0) ? 0 : SSL_WRITE_FLAG_CONCLUDE
;
623 /* For each of the vectors returned, pass it to OpenSSL QUIC. */
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
);
632 total_len
= nghttp3_vec_len(vecs
, num_vecs
);
634 for (i
= 0; i
< num_vecs
; ++i
) {
635 if (vecs
[i
].len
== 0)
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
) {
644 * We have filled our send buffer so tell nghttp3 to stop
645 * generating more data; we have to do this explicitly.
648 nghttp3_conn_block_stream(conn
->h3conn
, stream_id
);
650 ERR_raise_data(ERR_LIB_USER
, ERR_R_INTERNAL_ERROR
,
651 "writing HTTP/3 data to network failed");
656 * Tell nghttp3 it can resume generating more data in case we
657 * previously called block_stream.
659 nghttp3_conn_unblock_stream(conn
->h3conn
, stream_id
);
662 total_written
+= written
;
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.
669 ec
= nghttp3_conn_add_write_offset(conn
->h3conn
, stream_id
, written
);
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.
679 ec
= nghttp3_conn_add_ack_offset(conn
->h3conn
, stream_id
, written
);
685 if (fin
&& total_written
== total_len
) {
687 if (total_len
== 0) {
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).
693 ec
= nghttp3_conn_add_write_offset(conn
->h3conn
, stream_id
, 0);
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
);
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
,
715 OSSL_DEMO_H3_STREAM
*s_req
= NULL
;
718 ERR_raise_data(ERR_LIB_USER
, ERR_R_PASSED_NULL_PARAMETER
,
719 "connection must be specified");
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
)
727 s_req
->user_data
= user_data
;
729 ec
= nghttp3_conn_submit_request(conn
->h3conn
, s_req
->id
, nva
, nvlen
,
732 ERR_raise_data(ERR_LIB_USER
, ERR_R_INTERNAL_ERROR
,
733 "cannot submit HTTP/3 request: %s (%d)",
734 nghttp3_strerror(ec
), ec
);
741 h3_conn_remove_stream(conn
, s_req
);