2
0

ossl-nghttp3.c 25 KB

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