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