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