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