Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/openssl/demos/http3/ossl-nghttp3.c
109446 views
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
750