Coverage Report

Created: 2026-08-13 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/h2o/lib/http3/server.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2018 Fastly, Kazuho Oku
3
 *
4
 * Permission is hereby granted, free of charge, to any person obtaining a copy
5
 * of this software and associated documentation files (the "Software"), to
6
 * deal in the Software without restriction, including without limitation the
7
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8
 * sell copies of the Software, and to permit persons to whom the Software is
9
 * furnished to do so, subject to the following conditions:
10
 *
11
 * The above copyright notice and this permission notice shall be included in
12
 * all copies or substantial portions of the Software.
13
 *
14
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20
 * IN THE SOFTWARE.
21
 */
22
#include <sys/socket.h>
23
#include "khash.h"
24
#include "h2o/absprio.h"
25
#include "h2o/http3_common.h"
26
#include "h2o/http3_server.h"
27
#include "h2o/http3_internal.h"
28
#include "./../probes_.h"
29
30
/**
31
 * the scheduler
32
 */
33
struct st_h2o_http3_req_scheduler_t {
34
    struct {
35
        struct {
36
            h2o_linklist_t high;
37
            h2o_linklist_t low;
38
        } urgencies[H2O_ABSPRIO_NUM_URGENCY_LEVELS];
39
        size_t smallest_urgency;
40
    } active;
41
    h2o_linklist_t conn_blocked;
42
};
43
44
/**
45
 *
46
 */
47
struct st_h2o_http3_req_scheduler_node_t {
48
    h2o_linklist_t link;
49
    h2o_absprio_t priority;
50
    uint64_t call_cnt;
51
};
52
53
/**
54
 * callback used to compare precedence of the entries within the same urgency level (e.g., by comparing stream IDs)
55
 */
56
typedef int (*h2o_http3_req_scheduler_compare_cb)(struct st_h2o_http3_req_scheduler_t *sched,
57
                                                  const struct st_h2o_http3_req_scheduler_node_t *x,
58
                                                  const struct st_h2o_http3_req_scheduler_node_t *y);
59
60
/**
61
 * Once the size of the request body being received exceeds thit limit, streaming mode will be used (if possible), and the
62
 * concurrency of such requests would be limited to one per connection. This is set to 1 to avoid blocking requests that send
63
 * small payloads without a FIN as well as to have parity with http2.
64
 */
65
0
#define H2O_HTTP3_REQUEST_BODY_MIN_BYTES_TO_BLOCK 1
66
67
enum h2o_http3_server_stream_state {
68
    /**
69
     * receiving headers
70
     */
71
    H2O_HTTP3_SERVER_STREAM_STATE_RECV_HEADERS,
72
    /**
73
     * receiving request body (runs concurrently)
74
     */
75
    H2O_HTTP3_SERVER_STREAM_STATE_RECV_BODY_BEFORE_BLOCK,
76
    /**
77
     * blocked, waiting to be unblocked one by one (either in streaming mode or in non-streaming mode)
78
     */
79
    H2O_HTTP3_SERVER_STREAM_STATE_RECV_BODY_BLOCKED,
80
    /**
81
     * in non-streaming mode, receiving body
82
     */
83
    H2O_HTTP3_SERVER_STREAM_STATE_RECV_BODY_UNBLOCKED,
84
    /**
85
     * in non-streaming mode, waiting for the request to be processed
86
     */
87
    H2O_HTTP3_SERVER_STREAM_STATE_REQ_PENDING,
88
    /**
89
     * request has been processed, waiting for the response headers
90
     */
91
    H2O_HTTP3_SERVER_STREAM_STATE_SEND_HEADERS,
92
    /**
93
     * sending body (the generator MAY have closed, but the transmission to the client is still ongoing)
94
     */
95
    H2O_HTTP3_SERVER_STREAM_STATE_SEND_BODY,
96
    /**
97
     * all data has been sent and ACKed, waiting for the transport stream to close (req might be disposed when entering this state)
98
     */
99
    H2O_HTTP3_SERVER_STREAM_STATE_CLOSE_WAIT
100
};
101
102
struct st_h2o_http3_server_stream_t;
103
KHASH_MAP_INIT_INT64(stream, struct st_h2o_http3_server_stream_t *)
104
105
struct st_h2o_http3_server_conn_t {
106
    h2o_conn_t super;
107
    h2o_http3_conn_t h3;
108
    ptls_handshake_properties_t handshake_properties;
109
    /**
110
     * link-list of pending requests using st_h2o_http3_server_stream_t::link
111
     */
112
    struct {
113
        /**
114
         * holds streams in RECV_BODY_BLOCKED state. They are promoted one by one to the POST_BLOCK State.
115
         */
116
        h2o_linklist_t recv_body_blocked;
117
        /**
118
         * holds streams that are in request streaming mode.
119
         */
120
        h2o_linklist_t req_streaming;
121
        /**
122
         * holds streams in REQ_PENDING state or RECV_BODY_POST_BLOCK state (that is using streaming; i.e., write_req.cb != NULL).
123
         */
124
        h2o_linklist_t pending;
125
        /**
126
         * holds streams in RECV_HEADERS state whose header blocks are blocked by QPACK dynamic table references.
127
         */
128
        h2o_linklist_t qpack_blocked;
129
    } delayed_streams;
130
    /**
131
     * number of streams currently on `delayed_streams.qpack_blocked`; checked against the decoder's max_blocked.
132
     */
133
    uint64_t num_qpack_blocked;
134
    /**
135
     * responses blocked by SETTINGS frame yet to arrive (e.g., CONNECT-UDP requests waiting for SETTINGS to see if
136
     * datagram-flow-id can be sent). There is no separate state for streams linked here, because these streams are techincally
137
     * indifferent from those that are currently queued by the filters after `h2o_send` is called.
138
     */
139
    h2o_linklist_t streams_resp_settings_blocked;
140
    /**
141
     * next application-level timeout
142
     */
143
    h2o_timer_t timeout;
144
    /**
145
     * counter (the order MUST match that of h2o_http3_server_stream_state; it is accessed by index via the use of counters[])
146
     */
147
    union {
148
        struct {
149
            uint32_t recv_headers;
150
            uint32_t recv_body_before_block;
151
            uint32_t recv_body_blocked;
152
            uint32_t recv_body_unblocked;
153
            uint32_t req_pending;
154
            uint32_t send_headers;
155
            uint32_t send_body;
156
            uint32_t close_wait;
157
        };
158
        uint32_t counters[1];
159
    } num_streams;
160
    /**
161
     * Number of streams that is request streaming. The state can be in either one of SEND_HEADERS, SEND_BODY, CLOSE_WAIT.
162
     */
163
    uint32_t num_streams_req_streaming;
164
    /**
165
     * number of streams in tunneling mode
166
     */
167
    uint32_t num_streams_tunnelling;
168
    /**
169
     * aggregate of request stream statistics
170
     */
171
    struct {
172
        /**
173
         * number of request streams handled on this connection
174
         */
175
        uint64_t num_requests;
176
        struct {
177
            uint64_t stream_bytes;
178
            uint64_t headers_frame_bytes;
179
            uint64_t body_bytes;
180
            h2o_qpack_section_stats_t qpack;
181
        } req, resp;
182
    } stats;
183
    /**
184
     * scheduler
185
     */
186
    struct {
187
        /**
188
         * States for request streams.
189
         */
190
        struct st_h2o_http3_req_scheduler_t reqs;
191
        /**
192
         * States for unidirectional streams. Each element is a bit vector where slot for each stream is defined as: 1 << stream_id.
193
         */
194
        struct {
195
            uint16_t active;
196
            uint16_t conn_blocked;
197
        } uni;
198
    } scheduler;
199
    /**
200
     * stream map used for datagram flows
201
     * TODO: Get rid of this structure once we drop support for masque draft-03; RFC 9297 uses quater stream ID instead of
202
     * dynamically mapping streams with flow IDs.
203
     */
204
    khash_t(stream) * datagram_flows;
205
    /**
206
     * the earliest moment (in terms of max_data.sent) when the next resumption token can be sent
207
     */
208
    uint64_t skip_jumpstart_token_until;
209
    /**
210
     * timeout entry used for graceful shutdown
211
     */
212
    h2o_timer_t _graceful_shutdown_timeout;
213
};
214
215
/**
216
 * sendvec, with additional field that contains the starting offset of the content
217
 */
218
struct st_h2o_http3_server_sendvec_t {
219
    h2o_sendvec_t vec;
220
    /**
221
     * Starting offset of the content carried by the vector, or UINT64_MAX if it is not carrying body
222
     */
223
    uint64_t entity_offset;
224
};
225
226
struct st_h2o_http3_server_stream_t {
227
    quicly_stream_t *quic;
228
    struct {
229
        h2o_buffer_t *buf;
230
        quicly_error_t (*handle_input)(struct st_h2o_http3_server_stream_t *stream, const uint8_t **src, const uint8_t *src_end,
231
                                       int in_generator, const char **err_desc);
232
        uint64_t bytes_left_in_data_frame;
233
    } recvbuf;
234
    struct {
235
        H2O_VECTOR(struct st_h2o_http3_server_sendvec_t) vecs;
236
        size_t off_within_first_vec;
237
        size_t min_index_to_addref;
238
        uint64_t final_size, final_body_size;
239
        uint8_t data_frame_header_buf[9];
240
    } sendbuf;
241
    enum h2o_http3_server_stream_state state;
242
    /**
243
     * Non-zero if the stream is in RECV_HEADERS state waiting for the QPACK decoder's insert count to reach this value.
244
     */
245
    uint64_t qpack_blocked_ref;
246
    h2o_linklist_t link;
247
    h2o_linklist_t link_resp_settings_blocked;
248
    h2o_ostream_t ostr_final;
249
    struct st_h2o_http3_req_scheduler_node_t scheduler;
250
    /**
251
     * if read is blocked
252
     */
253
    uint8_t read_blocked : 1;
254
    /**
255
     * if h2o_proceed_response has been invoked, or if the invocation has been requested
256
     */
257
    uint8_t proceed_requested : 1;
258
    /**
259
     * this flag is set by on_send_emit, triggers the invocation h2o_proceed_response in scheduler_do_send, used by do_send to
260
     * take different actions based on if it has been called while scheduler_do_send is running.
261
     */
262
    uint8_t proceed_while_sending : 1;
263
    /**
264
     * if a PRIORITY_UPDATE frame has been received
265
     */
266
    uint8_t received_priority_update : 1;
267
    /**
268
     * used in CLOSE_WAIT state to determine if h2o_dispose_request has been called
269
     */
270
    uint8_t req_disposed : 1;
271
    /**
272
     * indicates if the request is in streaming mode
273
     */
274
    uint8_t req_streaming : 1;
275
    /**
276
     * indicates if the request has ever been QPACK-blocked.
277
     */
278
    uint8_t qpack_blocked_ever : 1;
279
    /**
280
     * if request streaming EOS has been delivered to the handler by calling write_req with is_end_stream set
281
     */
282
    uint8_t req_streaming_eos_delivered : 1;
283
    /**
284
     * buffer to hold the request body (or a chunk of, if in streaming mode), or CONNECT payload
285
     */
286
    h2o_buffer_t *req_body;
287
    /**
288
     * flow ID used by masque over H3_DATAGRAMS
289
     */
290
    uint64_t datagram_flow_id;
291
    /**
292
     * per-stream statistics
293
     */
294
    struct {
295
        struct {
296
            uint64_t headers_frame_bytes;
297
            h2o_qpack_section_stats_t qpack;
298
        } req, resp;
299
    } stats;
300
    /**
301
     * the request. Placed at the end, as it holds the pool.
302
     */
303
    h2o_req_t req;
304
};
305
306
static int foreach_request(h2o_conn_t *_conn, int (*cb)(h2o_req_t *req, void *cbdata), void *cbdata);
307
static void initiate_graceful_shutdown(h2o_conn_t *_conn);
308
static void close_idle_connection(h2o_conn_t *_conn);
309
static void on_stream_destroy(quicly_stream_t *qs, quicly_error_t err);
310
static void record_stream_stats(struct st_h2o_http3_server_stream_t *stream);
311
static quicly_error_t handle_input_post_trailers(struct st_h2o_http3_server_stream_t *stream, const uint8_t **src,
312
                                                 const uint8_t *src_end, int in_generator, const char **err_desc);
313
static quicly_error_t handle_input_expect_data(struct st_h2o_http3_server_stream_t *stream, const uint8_t **src,
314
                                               const uint8_t *src_end, int in_generator, const char **err_desc);
315
316
static const h2o_sendvec_callbacks_t self_allocated_vec_callbacks = {h2o_sendvec_read_raw, NULL},
317
                                     immutable_vec_callbacks = {h2o_sendvec_read_raw, NULL};
318
319
static int sendvec_size_is_for_recycle(size_t size)
320
1.47k
{
321
1.47k
    if (h2o_socket_ssl_buffer_allocator.conf->memsize / 2 <= size && size <= h2o_socket_ssl_buffer_allocator.conf->memsize)
322
0
        return 1;
323
1.47k
    return 0;
324
1.47k
}
325
326
static void dispose_sendvec(struct st_h2o_http3_server_sendvec_t *vec)
327
4.51k
{
328
4.51k
    if (vec->vec.callbacks == &self_allocated_vec_callbacks) {
329
736
        if (sendvec_size_is_for_recycle(vec->vec.len)) {
330
0
            h2o_mem_free_recycle(&h2o_socket_ssl_buffer_allocator, vec->vec.raw);
331
736
        } else {
332
736
            free(vec->vec.raw);
333
736
        }
334
736
    }
335
4.51k
}
336
337
static void req_scheduler_init(struct st_h2o_http3_req_scheduler_t *sched)
338
6.28k
{
339
6.28k
    size_t i;
340
341
56.6k
    for (i = 0; i < H2O_ABSPRIO_NUM_URGENCY_LEVELS; ++i) {
342
50.3k
        h2o_linklist_init_anchor(&sched->active.urgencies[i].high);
343
50.3k
        h2o_linklist_init_anchor(&sched->active.urgencies[i].low);
344
50.3k
    }
345
6.28k
    sched->active.smallest_urgency = i;
346
6.28k
    h2o_linklist_init_anchor(&sched->conn_blocked);
347
6.28k
}
348
349
static void req_scheduler_activate(struct st_h2o_http3_req_scheduler_t *sched, struct st_h2o_http3_req_scheduler_node_t *node,
350
                                   h2o_http3_req_scheduler_compare_cb comp)
351
1.59k
{
352
    /* unlink if necessary */
353
1.59k
    if (h2o_linklist_is_linked(&node->link))
354
4
        h2o_linklist_unlink(&node->link);
355
356
1.59k
    if (!node->priority.incremental || node->call_cnt == 0) {
357
        /* non-incremental streams and the first emission of incremental streams go in strict order */
358
1.59k
        h2o_linklist_t *anchor = &sched->active.urgencies[node->priority.urgency].high, *pos;
359
1.59k
        for (pos = anchor->prev; pos != anchor; pos = pos->prev) {
360
0
            struct st_h2o_http3_req_scheduler_node_t *node_at_pos =
361
0
                H2O_STRUCT_FROM_MEMBER(struct st_h2o_http3_req_scheduler_node_t, link, pos);
362
0
            if (comp(sched, node_at_pos, node) < 0)
363
0
                break;
364
0
        }
365
1.59k
        h2o_linklist_insert(pos->next, &node->link);
366
1.59k
    } else {
367
        /* once sent, incremental streams go into a lower list */
368
4
        h2o_linklist_insert(&sched->active.urgencies[node->priority.urgency].low, &node->link);
369
4
    }
370
371
    /* book keeping */
372
1.59k
    if (node->priority.urgency < sched->active.smallest_urgency)
373
1.59k
        sched->active.smallest_urgency = node->priority.urgency;
374
1.59k
}
375
376
static void req_scheduler_update_smallest_urgency_post_removal(struct st_h2o_http3_req_scheduler_t *sched, size_t changed)
377
8.53k
{
378
8.53k
    if (sched->active.smallest_urgency < changed)
379
0
        return;
380
381
    /* search from the location that *might* have changed */
382
8.53k
    sched->active.smallest_urgency = changed;
383
42.5k
    while (h2o_linklist_is_empty(&sched->active.urgencies[sched->active.smallest_urgency].high) &&
384
42.5k
           h2o_linklist_is_empty(&sched->active.urgencies[sched->active.smallest_urgency].low)) {
385
42.5k
        ++sched->active.smallest_urgency;
386
42.5k
        if (sched->active.smallest_urgency >= H2O_ABSPRIO_NUM_URGENCY_LEVELS)
387
8.53k
            break;
388
42.5k
    }
389
8.53k
}
390
391
static void req_scheduler_deactivate(struct st_h2o_http3_req_scheduler_t *sched, struct st_h2o_http3_req_scheduler_node_t *node)
392
8.53k
{
393
8.53k
    if (h2o_linklist_is_linked(&node->link))
394
1.59k
        h2o_linklist_unlink(&node->link);
395
396
8.53k
    req_scheduler_update_smallest_urgency_post_removal(sched, node->priority.urgency);
397
8.53k
}
398
399
static void req_scheduler_setup_for_next(struct st_h2o_http3_req_scheduler_t *sched, struct st_h2o_http3_req_scheduler_node_t *node,
400
                                         h2o_http3_req_scheduler_compare_cb comp)
401
368
{
402
368
    assert(h2o_linklist_is_linked(&node->link));
403
404
    /* reschedule to achieve round-robin behavior */
405
368
    if (node->priority.incremental)
406
4
        req_scheduler_activate(sched, node, comp);
407
368
}
408
409
static void req_scheduler_conn_blocked(struct st_h2o_http3_req_scheduler_t *sched, struct st_h2o_http3_req_scheduler_node_t *node)
410
0
{
411
0
    if (h2o_linklist_is_linked(&node->link))
412
0
        h2o_linklist_unlink(&node->link);
413
414
0
    h2o_linklist_insert(&sched->conn_blocked, &node->link);
415
416
0
    req_scheduler_update_smallest_urgency_post_removal(sched, node->priority.urgency);
417
0
}
418
419
static void req_scheduler_unblock_conn_blocked(struct st_h2o_http3_req_scheduler_t *sched, h2o_http3_req_scheduler_compare_cb comp)
420
0
{
421
0
    while (!h2o_linklist_is_empty(&sched->conn_blocked)) {
422
0
        struct st_h2o_http3_req_scheduler_node_t *node =
423
0
            H2O_STRUCT_FROM_MEMBER(struct st_h2o_http3_req_scheduler_node_t, link, sched->conn_blocked.next);
424
0
        req_scheduler_activate(sched, node, comp);
425
0
    }
426
0
}
427
428
static int req_scheduler_compare_stream_id(struct st_h2o_http3_req_scheduler_t *sched,
429
                                           const struct st_h2o_http3_req_scheduler_node_t *x,
430
                                           const struct st_h2o_http3_req_scheduler_node_t *y)
431
0
{
432
0
    struct st_h2o_http3_server_stream_t *sx = H2O_STRUCT_FROM_MEMBER(struct st_h2o_http3_server_stream_t, scheduler, x),
433
0
                                        *sy = H2O_STRUCT_FROM_MEMBER(struct st_h2o_http3_server_stream_t, scheduler, y);
434
0
    if (sx->quic->stream_id < sy->quic->stream_id) {
435
0
        return -1;
436
0
    } else if (sx->quic->stream_id > sy->quic->stream_id) {
437
0
        return 1;
438
0
    } else {
439
0
        return 0;
440
0
    }
441
0
}
442
443
static struct st_h2o_http3_server_conn_t *get_conn(struct st_h2o_http3_server_stream_t *stream)
444
92.5k
{
445
92.5k
    return (void *)stream->req.conn;
446
92.5k
}
447
448
static uint32_t *get_state_counter(struct st_h2o_http3_server_conn_t *conn, enum h2o_http3_server_stream_state state)
449
26.3k
{
450
26.3k
    return conn->num_streams.counters + (size_t)state;
451
26.3k
}
452
453
static void handle_priority_change(struct st_h2o_http3_server_stream_t *stream, const char *value, size_t len, h2o_absprio_t base)
454
0
{
455
0
    int reactivate = 0;
456
457
0
    if (h2o_linklist_is_linked(&stream->scheduler.link)) {
458
0
        req_scheduler_deactivate(&get_conn(stream)->scheduler.reqs, &stream->scheduler);
459
0
        reactivate = 1;
460
0
    }
461
462
    /* update priority, using provided value as the base */
463
0
    stream->scheduler.priority = base;
464
0
    h2o_absprio_parse_priority(value, len, &stream->scheduler.priority);
465
466
0
    if (reactivate)
467
0
        req_scheduler_activate(&get_conn(stream)->scheduler.reqs, &stream->scheduler, req_scheduler_compare_stream_id);
468
0
}
469
470
static void tunnel_on_udp_read(h2o_req_t *_req, h2o_iovec_t *datagrams, size_t num_datagrams)
471
0
{
472
0
    struct st_h2o_http3_server_stream_t *stream = H2O_STRUCT_FROM_MEMBER(struct st_h2o_http3_server_stream_t, req, _req);
473
0
    h2o_http3_send_h3_datagrams(&get_conn(stream)->h3, stream->datagram_flow_id, datagrams, num_datagrams);
474
0
}
475
476
static void request_run_delayed(struct st_h2o_http3_server_conn_t *conn)
477
783
{
478
783
    if (!h2o_timer_is_linked(&conn->timeout))
479
783
        h2o_timer_link(conn->super.ctx->loop, 0, &conn->timeout);
480
783
}
481
482
static void check_run_blocked(struct st_h2o_http3_server_conn_t *conn)
483
0
{
484
0
    if (conn->num_streams.recv_body_unblocked + conn->num_streams_req_streaming <
485
0
            conn->super.ctx->globalconf->http3.max_concurrent_streaming_requests_per_connection &&
486
0
        !h2o_linklist_is_empty(&conn->delayed_streams.recv_body_blocked))
487
0
        request_run_delayed(conn);
488
0
}
489
490
static void pre_dispose_request(struct st_h2o_http3_server_stream_t *stream)
491
6.28k
{
492
6.28k
    struct st_h2o_http3_server_conn_t *conn = get_conn(stream);
493
6.28k
    size_t i;
494
495
    /* release vectors */
496
8.42k
    for (i = 0; i != stream->sendbuf.vecs.size; ++i)
497
2.13k
        dispose_sendvec(stream->sendbuf.vecs.entries + i);
498
499
    /* dispose request body buffer */
500
6.28k
    if (stream->req_body != NULL)
501
434
        h2o_buffer_dispose(&stream->req_body);
502
503
    /* clean up request streaming */
504
6.28k
    if (stream->req_streaming && !stream->req.is_tunnel_req) {
505
0
        assert(conn->num_streams_req_streaming != 0);
506
0
        stream->req_streaming = 0;
507
0
        --conn->num_streams_req_streaming;
508
0
        check_run_blocked(conn);
509
0
    }
510
511
    /* remove stream from datagram flow list */
512
6.28k
    if (stream->datagram_flow_id != UINT64_MAX) {
513
671
        khiter_t iter = kh_get(stream, conn->datagram_flows, stream->datagram_flow_id);
514
        /* it's possible the tunnel wasn't established yet */
515
671
        if (iter != kh_end(conn->datagram_flows))
516
0
            kh_del(stream, conn->datagram_flows, iter);
517
671
    }
518
519
6.28k
    if (stream->req.is_tunnel_req)
520
233
        --get_conn(stream)->num_streams_tunnelling;
521
6.28k
}
522
523
static void set_state(struct st_h2o_http3_server_stream_t *stream, enum h2o_http3_server_stream_state state, int in_generator)
524
6.86k
{
525
6.86k
    struct st_h2o_http3_server_conn_t *conn = get_conn(stream);
526
6.86k
    enum h2o_http3_server_stream_state old_state = stream->state;
527
528
6.86k
    H2O_PROBE_CONN(H3S_STREAM_SET_STATE, &conn->super, stream->quic->stream_id, (unsigned)state);
529
530
6.86k
    assert(stream->qpack_blocked_ref == 0 && "QPACK-blocked streams must be unblocked or cancelled before changing stream state");
531
532
6.86k
    --*get_state_counter(conn, old_state);
533
6.86k
    stream->state = state;
534
6.86k
    ++*get_state_counter(conn, stream->state);
535
536
6.86k
    switch (state) {
537
0
    case H2O_HTTP3_SERVER_STREAM_STATE_RECV_BODY_BLOCKED:
538
0
        assert(conn->delayed_streams.recv_body_blocked.prev == &stream->link || !"stream is not registered to the recv_body list?");
539
0
        break;
540
1.87k
    case H2O_HTTP3_SERVER_STREAM_STATE_CLOSE_WAIT: {
541
1.87k
        if (h2o_linklist_is_linked(&stream->link))
542
0
            h2o_linklist_unlink(&stream->link);
543
1.87k
        pre_dispose_request(stream);
544
1.87k
        if (!in_generator) {
545
1.87k
            record_stream_stats(stream);
546
1.87k
            h2o_dispose_request(&stream->req);
547
1.87k
            stream->req_disposed = 1;
548
1.87k
        }
549
1.87k
        static const quicly_stream_callbacks_t close_wait_callbacks = {on_stream_destroy,
550
1.87k
                                                                       quicly_stream_noop_on_send_shift,
551
1.87k
                                                                       quicly_stream_noop_on_send_emit,
552
1.87k
                                                                       quicly_stream_noop_on_send_stop,
553
1.87k
                                                                       quicly_stream_noop_on_receive,
554
1.87k
                                                                       quicly_stream_noop_on_receive_reset};
555
1.87k
        stream->quic->callbacks = &close_wait_callbacks;
556
1.87k
    } break;
557
4.99k
    default:
558
4.99k
        break;
559
6.86k
    }
560
6.86k
}
561
562
static void cancel_qpack_decoder(struct st_h2o_http3_server_stream_t *stream)
563
1.87k
{
564
1.87k
    struct st_h2o_http3_server_conn_t *conn = get_conn(stream);
565
1.87k
    if (stream->qpack_blocked_ref != 0) {
566
0
        assert(conn->num_qpack_blocked > 0);
567
0
        --conn->num_qpack_blocked;
568
0
        stream->qpack_blocked_ref = 0;
569
0
    }
570
1.87k
    h2o_http3_qpack_cancel_stream(&conn->h3, stream->quic->stream_id);
571
1.87k
}
572
573
/**
574
 * Shutdowns a stream. Note that a request stream should not be shut down until receiving some QUIC frame that refers to that
575
 * stream, but we might might have created stream state due to receiving a PRIORITY_UPDATE frame prior to that (see
576
 * handle_priority_update_frame).
577
 */
578
static void shutdown_stream(struct st_h2o_http3_server_stream_t *stream, quicly_error_t stop_sending_code,
579
                            quicly_error_t reset_code, int in_generator, int reset_only_if_open)
580
996
{
581
996
    assert(stream->state < H2O_HTTP3_SERVER_STREAM_STATE_CLOSE_WAIT);
582
996
    if (quicly_stream_has_receive_side(0, stream->quic->stream_id)) {
583
        /* send STOP_SENDING unless RESET_STREAM was received; we send STOP_SENDING even if all data up to EOS have been received,
584
         * as it is allowed and might be beneficial in case ACKs are lost */
585
996
        if (!(quicly_recvstate_transfer_complete(&stream->quic->recvstate) && stream->quic->recvstate.eos == UINT64_MAX))
586
996
            quicly_request_stop(stream->quic, stop_sending_code);
587
996
        cancel_qpack_decoder(stream);
588
996
        if (h2o_linklist_is_linked(&stream->link))
589
0
            h2o_linklist_unlink(&stream->link);
590
996
    }
591
996
    if (reset_only_if_open && quicly_stream_has_send_side(0, stream->quic->stream_id) &&
592
0
        !quicly_sendstate_is_open(&stream->quic->sendstate)) {
593
0
        if (stream->state < H2O_HTTP3_SERVER_STREAM_STATE_SEND_BODY)
594
0
            set_state(stream, H2O_HTTP3_SERVER_STREAM_STATE_SEND_BODY, in_generator);
595
996
    } else {
596
996
        if (quicly_stream_has_send_side(0, stream->quic->stream_id) &&
597
996
            !quicly_sendstate_transfer_complete(&stream->quic->sendstate))
598
996
            quicly_reset_stream(stream->quic, reset_code);
599
996
        set_state(stream, H2O_HTTP3_SERVER_STREAM_STATE_CLOSE_WAIT, in_generator);
600
996
    }
601
996
}
602
603
static socklen_t get_sockname(h2o_conn_t *_conn, struct sockaddr *sa)
604
0
{
605
0
    struct st_h2o_http3_server_conn_t *conn = (void *)_conn;
606
0
    struct sockaddr *src = quicly_get_sockname(conn->h3.super.quic);
607
0
    socklen_t len = src->sa_family == AF_UNSPEC ? sizeof(struct sockaddr) : quicly_get_socklen(src);
608
0
    memcpy(sa, src, len);
609
0
    return len;
610
0
}
611
612
static socklen_t get_peername(h2o_conn_t *_conn, struct sockaddr *sa)
613
409
{
614
409
    struct st_h2o_http3_server_conn_t *conn = (void *)_conn;
615
409
    struct sockaddr *src = quicly_get_peername(conn->h3.super.quic);
616
409
    socklen_t len = quicly_get_socklen(src);
617
409
    memcpy(sa, src, len);
618
409
    return len;
619
409
}
620
621
static ptls_t *get_ptls(h2o_conn_t *_conn)
622
409
{
623
409
    struct st_h2o_http3_server_conn_t *conn = (void *)_conn;
624
409
    return quicly_get_tls(conn->h3.super.quic);
625
409
}
626
627
static const char *get_ssl_server_name(h2o_conn_t *conn)
628
0
{
629
0
    ptls_t *ptls = get_ptls(conn);
630
0
    return ptls_get_server_name(ptls);
631
0
}
632
633
static ptls_log_conn_state_t *log_state(h2o_conn_t *conn)
634
0
{
635
0
    ptls_t *ptls = get_ptls(conn);
636
0
    return ptls_get_log_state(ptls);
637
0
}
638
639
static uint64_t get_req_id(h2o_req_t *req)
640
0
{
641
0
    struct st_h2o_http3_server_stream_t *stream = H2O_STRUCT_FROM_MEMBER(struct st_h2o_http3_server_stream_t, req, req);
642
0
    return stream->quic->stream_id;
643
0
}
644
645
static uint32_t num_reqs_inflight(h2o_conn_t *_conn)
646
0
{
647
0
    struct st_h2o_http3_server_conn_t *conn = (void *)_conn;
648
0
    return quicly_num_streams_by_group(conn->h3.super.quic, 0, 0);
649
0
}
650
651
static quicly_tracer_t *get_tracer(h2o_conn_t *_conn)
652
0
{
653
0
    struct st_h2o_http3_server_conn_t *conn = (void *)_conn;
654
0
    return quicly_get_tracer(conn->h3.super.quic);
655
0
}
656
657
static h2o_iovec_t log_extensible_priorities(h2o_req_t *_req)
658
0
{
659
0
    struct st_h2o_http3_server_stream_t *stream = H2O_STRUCT_FROM_MEMBER(struct st_h2o_http3_server_stream_t, req, _req);
660
0
    char *buf = h2o_mem_alloc_pool(&stream->req.pool, char, sizeof("u=" H2O_UINT8_LONGEST_STR ",i=?1"));
661
0
    int len =
662
0
        sprintf(buf, "u=%" PRIu8 "%s", stream->scheduler.priority.urgency, stream->scheduler.priority.incremental ? ",i=?1" : "");
663
0
    return h2o_iovec_init(buf, len);
664
0
}
665
666
#define DEFINE_NUMERIC_LOGGER(name, fmt, value)                                                                                    \
667
    static h2o_iovec_t log_##name(h2o_req_t *_req)                                                                                 \
668
0
    {                                                                                                                              \
669
0
        struct st_h2o_http3_server_stream_t *stream = H2O_STRUCT_FROM_MEMBER(struct st_h2o_http3_server_stream_t, req, _req);      \
670
0
        char *buf = h2o_mem_alloc_pool(&stream->req.pool, char, sizeof(H2O_INT64_LONGEST_STR));                                    \
671
0
        return h2o_iovec_init(buf, sprintf(buf, fmt, value));                                                                      \
672
0
    }
673
674
0
DEFINE_NUMERIC_LOGGER(request_header_bytes, "%" PRIu64, stream->stats.req.headers_frame_bytes)
675
0
DEFINE_NUMERIC_LOGGER(request_header_text_bytes, "%zu", stream->stats.req.qpack.text_bytes)
676
0
DEFINE_NUMERIC_LOGGER(request_header_count, "%zu", stream->stats.req.qpack.count)
677
0
DEFINE_NUMERIC_LOGGER(response_header_text_bytes, "%zu", stream->stats.resp.qpack.text_bytes)
678
0
DEFINE_NUMERIC_LOGGER(response_header_count, "%zu", stream->stats.resp.qpack.count)
679
680
static h2o_iovec_t log_cc_name(h2o_req_t *req)
681
0
{
682
0
    struct st_h2o_http3_server_conn_t *conn = (struct st_h2o_http3_server_conn_t *)req->conn;
683
0
    quicly_stats_t stats;
684
685
0
    if (quicly_get_stats(conn->h3.super.quic, &stats) == 0)
686
0
        return h2o_iovec_init(stats.cc.type->name, strlen(stats.cc.type->name));
687
0
    return h2o_iovec_init(NULL, 0);
688
0
}
689
690
static h2o_iovec_t log_delivery_rate(h2o_req_t *req)
691
0
{
692
0
    struct st_h2o_http3_server_conn_t *conn = (struct st_h2o_http3_server_conn_t *)req->conn;
693
0
    quicly_rate_t rate;
694
695
0
    if (quicly_get_delivery_rate(conn->h3.super.quic, &rate) == 0 && rate.latest != 0) {
696
0
        char *buf = h2o_mem_alloc_pool(&req->pool, char, sizeof(H2O_UINT64_LONGEST_STR));
697
0
        size_t len = sprintf(buf, "%" PRIu64, rate.latest);
698
0
        return h2o_iovec_init(buf, len);
699
0
    }
700
701
0
    return h2o_iovec_init(NULL, 0);
702
0
}
703
704
static h2o_iovec_t log_tls_protocol_version(h2o_req_t *_req)
705
0
{
706
0
    return h2o_iovec_init(H2O_STRLIT("TLSv1.3"));
707
0
}
708
709
static h2o_iovec_t log_session_reused(h2o_req_t *req)
710
0
{
711
0
    struct st_h2o_http3_server_conn_t *conn = (struct st_h2o_http3_server_conn_t *)req->conn;
712
0
    ptls_t *tls = quicly_get_tls(conn->h3.super.quic);
713
0
    return ptls_is_psk_handshake(tls) ? h2o_iovec_init(H2O_STRLIT("1")) : h2o_iovec_init(H2O_STRLIT("0"));
714
0
}
715
716
static h2o_iovec_t log_cipher(h2o_req_t *req)
717
0
{
718
0
    struct st_h2o_http3_server_conn_t *conn = (struct st_h2o_http3_server_conn_t *)req->conn;
719
0
    ptls_t *tls = quicly_get_tls(conn->h3.super.quic);
720
0
    ptls_cipher_suite_t *cipher = ptls_get_cipher(tls);
721
0
    return cipher != NULL ? h2o_iovec_init(cipher->name, strlen(cipher->name)) : h2o_iovec_init(NULL, 0);
722
0
}
723
724
static h2o_iovec_t log_cipher_bits(h2o_req_t *req)
725
0
{
726
0
    struct st_h2o_http3_server_conn_t *conn = (struct st_h2o_http3_server_conn_t *)req->conn;
727
0
    ptls_t *tls = quicly_get_tls(conn->h3.super.quic);
728
0
    ptls_cipher_suite_t *cipher = ptls_get_cipher(tls);
729
0
    if (cipher == NULL)
730
0
        return h2o_iovec_init(NULL, 0);
731
732
0
    char *buf = h2o_mem_alloc_pool(&req->pool, char, sizeof(H2O_UINT16_LONGEST_STR));
733
0
    return h2o_iovec_init(buf, sprintf(buf, "%" PRIu16, (uint16_t)(cipher->aead->key_size * 8)));
734
0
}
735
736
static h2o_iovec_t log_session_id(h2o_req_t *_req)
737
0
{
738
    /* FIXME */
739
0
    return h2o_iovec_init(NULL, 0);
740
0
}
741
742
static h2o_iovec_t log_negotiated_protocol(h2o_req_t *req)
743
0
{
744
0
    struct st_h2o_http3_server_conn_t *conn = (struct st_h2o_http3_server_conn_t *)req->conn;
745
0
    ptls_t *tls = quicly_get_tls(conn->h3.super.quic);
746
0
    const char *proto = ptls_get_negotiated_protocol(tls);
747
0
    return proto != NULL ? h2o_iovec_init(proto, strlen(proto)) : h2o_iovec_init(NULL, 0);
748
0
}
749
750
static h2o_iovec_t log_ech_config_id(h2o_req_t *req)
751
0
{
752
0
    struct st_h2o_http3_server_conn_t *conn = (struct st_h2o_http3_server_conn_t *)req->conn;
753
0
    ptls_t *tls = quicly_get_tls(conn->h3.super.quic);
754
0
    uint8_t config_id;
755
756
0
    if (ptls_is_ech_handshake(tls, &config_id, NULL, NULL)) {
757
0
        char *s = h2o_mem_alloc_pool(&req->pool, char, sizeof(H2O_UINT8_LONGEST_STR));
758
0
        size_t len = sprintf(s, "%" PRIu8, config_id);
759
0
        return h2o_iovec_init(s, len);
760
0
    } else {
761
0
        return h2o_iovec_init(NULL, 0);
762
0
    }
763
0
}
764
765
static h2o_iovec_t log_ech_kem(h2o_req_t *req)
766
0
{
767
0
    struct st_h2o_http3_server_conn_t *conn = (struct st_h2o_http3_server_conn_t *)req->conn;
768
0
    ptls_t *tls = quicly_get_tls(conn->h3.super.quic);
769
0
    ptls_hpke_kem_t *kem;
770
771
0
    if (ptls_is_ech_handshake(tls, NULL, &kem, NULL)) {
772
0
        return h2o_iovec_init(kem->keyex->name, strlen(kem->keyex->name));
773
0
    } else {
774
0
        return h2o_iovec_init(NULL, 0);
775
0
    }
776
0
}
777
778
static h2o_iovec_t log_ech_cipher(h2o_req_t *req)
779
0
{
780
0
    struct st_h2o_http3_server_conn_t *conn = (struct st_h2o_http3_server_conn_t *)req->conn;
781
0
    ptls_t *tls = quicly_get_tls(conn->h3.super.quic);
782
0
    ptls_hpke_cipher_suite_t *cipher;
783
784
0
    if (ptls_is_ech_handshake(tls, NULL, NULL, &cipher)) {
785
0
        return h2o_iovec_init(cipher->name, strlen(cipher->name));
786
0
    } else {
787
0
        return h2o_iovec_init(NULL, 0);
788
0
    }
789
0
}
790
791
static h2o_iovec_t log_ech_cipher_bits(h2o_req_t *req)
792
0
{
793
0
    struct st_h2o_http3_server_conn_t *conn = (struct st_h2o_http3_server_conn_t *)req->conn;
794
0
    ptls_t *tls = quicly_get_tls(conn->h3.super.quic);
795
0
    ptls_hpke_cipher_suite_t *cipher;
796
797
0
    if (ptls_is_ech_handshake(tls, NULL, NULL, &cipher)) {
798
0
        uint16_t bits = (uint16_t)(cipher->aead->key_size * 8);
799
0
        char *s = h2o_mem_alloc_pool(&req->pool, char, sizeof(H2O_UINT16_LONGEST_STR));
800
0
        size_t len = sprintf(s, "%" PRIu16, bits);
801
0
        return h2o_iovec_init(s, len);
802
0
    } else {
803
0
        return h2o_iovec_init(NULL, 0);
804
0
    }
805
0
}
806
807
0
DEFINE_NUMERIC_LOGGER(stream_id, "%" PRIu64, stream->quic->stream_id)
808
809
static h2o_iovec_t log_qpack_blocked(h2o_req_t *_req)
810
0
{
811
0
    struct st_h2o_http3_server_stream_t *stream = H2O_STRUCT_FROM_MEMBER(struct st_h2o_http3_server_stream_t, req, _req);
812
0
    return stream->qpack_blocked_ever ? h2o_iovec_init(H2O_STRLIT("1")) : h2o_iovec_init(H2O_STRLIT("0"));
813
0
}
814
815
static h2o_iovec_t log_quic_stats(h2o_req_t *req)
816
0
{
817
0
#define PUSH_FIELD(field, name)                                                                                                    \
818
0
    do {                                                                                                                           \
819
0
        len += snprintf(buf + len, bufsize - len, name "=%" PRIu64 ",", (uint64_t)stats.field);                                    \
820
0
        if (len + 1 > bufsize) {                                                                                                   \
821
0
            bufsize = bufsize * 3 / 2;                                                                                             \
822
0
            goto Redo;                                                                                                             \
823
0
        }                                                                                                                          \
824
0
    } while (0);
825
826
0
    struct st_h2o_http3_server_conn_t *conn = (struct st_h2o_http3_server_conn_t *)req->conn;
827
0
    quicly_stats_t stats;
828
829
0
    if (quicly_get_stats(conn->h3.super.quic, &stats) != 0)
830
0
        return h2o_iovec_init(H2O_STRLIT("-"));
831
832
0
    char *buf;
833
0
    size_t len;
834
0
    static __thread size_t bufsize = 100; /* this value grows by 1.5x to find adequete value, and is remembered for future
835
                                           * invocations */
836
0
Redo:
837
0
    buf = h2o_mem_alloc_pool(&req->pool, char, bufsize);
838
0
    len = 0;
839
840
0
    QUICLY_STATS_FOREACH(PUSH_FIELD);
841
842
0
    return h2o_iovec_init(buf, len - 1);
843
844
0
#undef PUSH_FIELD
845
0
}
846
847
0
DEFINE_NUMERIC_LOGGER(quic_version, "%" PRIu32, quicly_get_protocol_version(stream->quic->conn))
848
849
static uint64_t get_request_stream_size(struct st_h2o_http3_server_stream_t *stream)
850
6.28k
{
851
6.28k
    if (!quicly_recvstate_transfer_complete(&stream->quic->recvstate))
852
0
        return stream->quic->recvstate.received.ranges[0].end;
853
854
    /* On reset, recvstate has no final size and clears received ranges. In that case, data_off is the best available contiguous
855
     * byte count. */
856
6.28k
    if (stream->quic->recvstate.eos == UINT64_MAX)
857
0
        return stream->quic->recvstate.data_off;
858
859
6.28k
    return stream->quic->recvstate.eos;
860
6.28k
}
861
862
0
DEFINE_NUMERIC_LOGGER(request_stream_bytes, "%" PRIu64, get_request_stream_size(stream))
863
0
DEFINE_NUMERIC_LOGGER(response_stream_bytes, "%" PRIu64, stream->quic->sendstate.size_inflight)
864
865
#undef DEFINE_NUMERIC_LOGGER
866
867
static void record_stream_stats(struct st_h2o_http3_server_stream_t *stream)
868
6.28k
{
869
6.28k
    struct st_h2o_http3_server_conn_t *conn = get_conn(stream);
870
6.28k
    uint64_t request_stream_bytes = get_request_stream_size(stream);
871
872
6.28k
    ++conn->stats.num_requests;
873
6.28k
    conn->stats.req.stream_bytes += request_stream_bytes;
874
6.28k
    conn->stats.req.headers_frame_bytes += stream->stats.req.headers_frame_bytes;
875
6.28k
    conn->stats.req.body_bytes += stream->req.req_body_bytes_received;
876
6.28k
    conn->stats.req.qpack.count += stream->stats.req.qpack.count;
877
6.28k
    conn->stats.req.qpack.text_bytes += stream->stats.req.qpack.text_bytes;
878
6.28k
    conn->stats.resp.stream_bytes += stream->quic->sendstate.size_inflight;
879
6.28k
    conn->stats.resp.headers_frame_bytes += stream->stats.resp.headers_frame_bytes;
880
6.28k
    conn->stats.resp.body_bytes += stream->req.bytes_sent;
881
6.28k
    conn->stats.resp.qpack.count += stream->stats.resp.qpack.count;
882
6.28k
    conn->stats.resp.qpack.text_bytes += stream->stats.resp.qpack.text_bytes;
883
884
6.28k
    H2O_PROBE_CONN(H3S_STREAM_STATS, &conn->super, stream->quic->stream_id, request_stream_bytes,
885
6.28k
                   stream->stats.req.headers_frame_bytes, stream->req.req_body_bytes_received, stream->stats.req.qpack.count,
886
6.28k
                   stream->stats.req.qpack.text_bytes, stream->quic->sendstate.size_inflight,
887
6.28k
                   stream->stats.resp.headers_frame_bytes, stream->req.bytes_sent, stream->stats.resp.qpack.count,
888
6.28k
                   stream->stats.resp.qpack.text_bytes);
889
6.28k
    H2O_LOG_CONN(h3s_stream_stats, &conn->super, {
890
6.28k
        PTLS_LOG_ELEMENT_UNSIGNED(stream_id, stream->quic->stream_id);
891
6.28k
        PTLS_LOG_ELEMENT_UNSIGNED(request_stream_bytes, request_stream_bytes);
892
6.28k
        PTLS_LOG_ELEMENT_UNSIGNED(request_header_bytes, stream->stats.req.headers_frame_bytes);
893
6.28k
        PTLS_LOG_ELEMENT_UNSIGNED(request_body_bytes, stream->req.req_body_bytes_received);
894
6.28k
        PTLS_LOG_ELEMENT_UNSIGNED(request_header_count, stream->stats.req.qpack.count);
895
6.28k
        PTLS_LOG_ELEMENT_UNSIGNED(request_header_text_bytes, stream->stats.req.qpack.text_bytes);
896
6.28k
        PTLS_LOG_ELEMENT_UNSIGNED(response_stream_bytes, stream->quic->sendstate.size_inflight);
897
6.28k
        PTLS_LOG_ELEMENT_UNSIGNED(response_header_bytes, stream->stats.resp.headers_frame_bytes);
898
6.28k
        PTLS_LOG_ELEMENT_UNSIGNED(response_body_bytes, stream->req.bytes_sent);
899
6.28k
        PTLS_LOG_ELEMENT_UNSIGNED(response_header_count, stream->stats.resp.qpack.count);
900
6.28k
        PTLS_LOG_ELEMENT_UNSIGNED(response_header_text_bytes, stream->stats.resp.qpack.text_bytes);
901
6.28k
    });
902
6.28k
}
903
904
void on_stream_destroy(quicly_stream_t *qs, quicly_error_t err)
905
6.28k
{
906
6.28k
    struct st_h2o_http3_server_stream_t *stream = qs->data;
907
6.28k
    struct st_h2o_http3_server_conn_t *conn = get_conn(stream);
908
909
    /* Unless the stream is closed as part of connection teardown, it is already in CLOSE_WAIT when destroyed; state cleanup has
910
     * already been performed. In contrast, when the QUIC connection is closed, streams can be destroyed in any state. In that case,
911
     * consistency within the connection itself does not always need to be preserved, as the connection is being discarded; however,
912
     * state external to the connection still needs to be maintained. */
913
6.28k
    --*get_state_counter(conn, stream->state);
914
915
6.28k
    req_scheduler_deactivate(&conn->scheduler.reqs, &stream->scheduler);
916
917
6.28k
    if (h2o_linklist_is_linked(&stream->link))
918
0
        h2o_linklist_unlink(&stream->link);
919
6.28k
    if (stream->qpack_blocked_ref != 0) {
920
0
        assert(conn->num_qpack_blocked > 0);
921
0
        --conn->num_qpack_blocked;
922
0
    }
923
6.28k
    if (h2o_linklist_is_linked(&stream->link_resp_settings_blocked))
924
0
        h2o_linklist_unlink(&stream->link_resp_settings_blocked);
925
6.28k
    if (stream->state != H2O_HTTP3_SERVER_STREAM_STATE_CLOSE_WAIT)
926
4.41k
        pre_dispose_request(stream);
927
6.28k
    if (!stream->req_disposed) {
928
4.41k
        record_stream_stats(stream);
929
4.41k
        h2o_dispose_request(&stream->req);
930
4.41k
    }
931
    /* in case the stream is destroyed before the buffer is fully consumed */
932
6.28k
    h2o_buffer_dispose(&stream->recvbuf.buf);
933
934
6.28k
    free(stream);
935
936
6.28k
    uint32_t num_req_streams_incl_self = quicly_num_streams_by_group(conn->h3.super.quic, 0, 0);
937
6.28k
    assert(num_req_streams_incl_self > 0 &&
938
6.28k
           "during the invocation of the destroy callback, stream count should include the number of the stream being destroyed");
939
6.28k
    if (num_req_streams_incl_self == 1)
940
6.28k
        h2o_conn_set_state(&conn->super, H2O_CONN_STATE_IDLE);
941
6.28k
}
942
943
/**
944
 * Converts vectors owned by the generator to ones owned by the HTTP/3 implementation, as the former becomes inaccessible once we
945
 * call `do_proceed`.
946
 */
947
static int retain_sendvecs(struct st_h2o_http3_server_stream_t *stream)
948
368
{
949
1.47k
    for (; stream->sendbuf.min_index_to_addref != stream->sendbuf.vecs.size; ++stream->sendbuf.min_index_to_addref) {
950
1.10k
        struct st_h2o_http3_server_sendvec_t *vec = stream->sendbuf.vecs.entries + stream->sendbuf.min_index_to_addref;
951
1.10k
        assert(vec->vec.callbacks->read_ == h2o_sendvec_read_raw);
952
1.10k
        if (!(vec->vec.callbacks == &self_allocated_vec_callbacks || vec->vec.callbacks == &immutable_vec_callbacks)) {
953
736
            size_t off_within_vec = stream->sendbuf.min_index_to_addref == 0 ? stream->sendbuf.off_within_first_vec : 0,
954
736
                   newlen = vec->vec.len - off_within_vec;
955
736
            void *newbuf = sendvec_size_is_for_recycle(newlen) ? h2o_mem_alloc_recycle(&h2o_socket_ssl_buffer_allocator)
956
736
                                                               : h2o_mem_alloc(newlen);
957
736
            memcpy(newbuf, vec->vec.raw + off_within_vec, newlen);
958
736
            vec->vec = (h2o_sendvec_t){&self_allocated_vec_callbacks, newlen, {newbuf}};
959
736
            if (stream->sendbuf.min_index_to_addref == 0)
960
0
                stream->sendbuf.off_within_first_vec = 0;
961
736
        }
962
1.10k
    }
963
964
368
    return 1;
965
368
}
966
967
static void collect_quic_performance_metrics(struct st_h2o_http3_server_stream_t *stream)
968
877
{
969
877
    struct st_h2o_http3_server_conn_t *conn = get_conn(stream);
970
971
    /* If the request is the first request, log method, content-length, and ttlb so that the performance of HTTP/3 can be evaluated.
972
     * Note, to compare the performance of connections using different CC parameters, only the numbers from connections that served
973
     * requests capable of fulfilling the CWND regardless of CC behavior (i.e, pre-built local objects) can be used. To extract such
974
     * connections, properties other than method and content-length might be needed. */
975
877
    if (stream->quic->stream_id == 0) {
976
877
#define EMIT_STATS_FIELD(fld, lit) PTLS_LOG__DO_ELEMENT_UNSIGNED(lit, stats.fld);
977
877
        H2O_LOG_CONN(h3s_stream0_ttlb, &conn->super, {
978
877
            PTLS_LOG_ELEMENT_UNSAFESTR(method, stream->req.method.base, stream->req.method.len);
979
877
            PTLS_LOG_ELEMENT_UNSIGNED(content_length, stream->req.res.content_length);
980
877
            struct timeval now = h2o_gettimeofday(conn->super.ctx->loop);
981
877
            int64_t ttlb = (h2o_timeval_subtract(&stream->req.timestamps.request_begin_at, &now) + 500) / 1000;
982
877
            if (ttlb < 0)
983
877
                ttlb = 0;
984
877
            PTLS_LOG_ELEMENT_SIGNED(ttlb, ttlb);
985
877
            quicly_stats_t stats;
986
877
            if (quicly_get_stats(conn->h3.super.quic, &stats) == 0) {
987
877
                QUICLY_STATS_FOREACH(EMIT_STATS_FIELD); /* if this is too heavyweight, we can hexdump `stats` instead */
988
877
            }
989
877
        });
990
877
#undef EMIT_STATS_FIELD
991
877
    }
992
877
}
993
994
static void on_send_shift(quicly_stream_t *qs, size_t delta)
995
877
{
996
877
    struct st_h2o_http3_server_stream_t *stream = qs->data;
997
877
    size_t i;
998
999
877
    assert(H2O_HTTP3_SERVER_STREAM_STATE_RECV_BODY_BEFORE_BLOCK <= stream->state &&
1000
877
           stream->state <= H2O_HTTP3_SERVER_STREAM_STATE_SEND_BODY);
1001
877
    assert(delta != 0);
1002
877
    assert(stream->sendbuf.vecs.size != 0);
1003
1004
877
    size_t bytes_avail_in_first_vec = stream->sendbuf.vecs.entries[0].vec.len - stream->sendbuf.off_within_first_vec;
1005
877
    if (delta < bytes_avail_in_first_vec) {
1006
0
        stream->sendbuf.off_within_first_vec += delta;
1007
0
        return;
1008
0
    }
1009
877
    delta -= bytes_avail_in_first_vec;
1010
877
    stream->sendbuf.off_within_first_vec = 0;
1011
877
    dispose_sendvec(&stream->sendbuf.vecs.entries[0]);
1012
1013
2.37k
    for (i = 1; delta != 0; ++i) {
1014
1.50k
        assert(i < stream->sendbuf.vecs.size);
1015
1.50k
        if (delta < stream->sendbuf.vecs.entries[i].vec.len) {
1016
0
            stream->sendbuf.off_within_first_vec = delta;
1017
0
            break;
1018
0
        }
1019
1.50k
        delta -= stream->sendbuf.vecs.entries[i].vec.len;
1020
1.50k
        dispose_sendvec(&stream->sendbuf.vecs.entries[i]);
1021
1.50k
    }
1022
877
    memmove(stream->sendbuf.vecs.entries, stream->sendbuf.vecs.entries + i,
1023
877
            (stream->sendbuf.vecs.size - i) * sizeof(stream->sendbuf.vecs.entries[0]));
1024
877
    stream->sendbuf.vecs.size -= i;
1025
877
    if (stream->sendbuf.min_index_to_addref <= i) {
1026
877
        stream->sendbuf.min_index_to_addref = 0;
1027
877
    } else {
1028
0
        stream->sendbuf.min_index_to_addref -= i;
1029
0
    }
1030
1031
877
    if (stream->sendbuf.vecs.size == 0) {
1032
877
        if (quicly_sendstate_is_open(&stream->quic->sendstate)) {
1033
0
            assert((H2O_HTTP3_SERVER_STREAM_STATE_RECV_BODY_BEFORE_BLOCK <= stream->state &&
1034
0
                    stream->state <= H2O_HTTP3_SERVER_STREAM_STATE_SEND_HEADERS) ||
1035
0
                   stream->proceed_requested);
1036
877
        } else {
1037
877
            collect_quic_performance_metrics(stream);
1038
877
            if (quicly_stream_has_receive_side(0, stream->quic->stream_id)) {
1039
877
                quicly_request_stop(stream->quic, H2O_HTTP3_ERROR_EARLY_RESPONSE);
1040
877
                cancel_qpack_decoder(stream);
1041
877
            }
1042
877
            set_state(stream, H2O_HTTP3_SERVER_STREAM_STATE_CLOSE_WAIT, 0);
1043
877
        }
1044
877
    }
1045
877
}
1046
1047
static void on_send_emit(quicly_stream_t *qs, size_t off, void *_dst, size_t *len, int *wrote_all)
1048
1.24k
{
1049
1.24k
    struct st_h2o_http3_server_stream_t *stream = qs->data;
1050
1051
1.24k
    assert(H2O_HTTP3_SERVER_STREAM_STATE_RECV_BODY_BEFORE_BLOCK <= stream->state &&
1052
1.24k
           stream->state <= H2O_HTTP3_SERVER_STREAM_STATE_SEND_BODY);
1053
1054
1.24k
    uint8_t *dst = _dst, *dst_end = dst + *len;
1055
1.24k
    size_t vec_index = 0;
1056
1057
    /* find the start position identified by vec_index and off */
1058
1.24k
    off += stream->sendbuf.off_within_first_vec;
1059
1.24k
    while (off != 0) {
1060
0
        assert(vec_index < stream->sendbuf.vecs.size);
1061
0
        if (off < stream->sendbuf.vecs.entries[vec_index].vec.len)
1062
0
            break;
1063
0
        off -= stream->sendbuf.vecs.entries[vec_index].vec.len;
1064
0
        ++vec_index;
1065
0
    }
1066
1.24k
    assert(vec_index < stream->sendbuf.vecs.size);
1067
1068
    /* write */
1069
1.24k
    *wrote_all = 0;
1070
3.48k
    do {
1071
3.48k
        struct st_h2o_http3_server_sendvec_t *this_vec = stream->sendbuf.vecs.entries + vec_index;
1072
3.48k
        size_t sz = this_vec->vec.len - off;
1073
3.48k
        if (dst_end - dst < sz)
1074
0
            sz = dst_end - dst;
1075
        /* convert vector into raw form, the first time it's being sent (TODO use ssl_buffer_recyle) */
1076
3.48k
        if (this_vec->vec.callbacks->read_ != h2o_sendvec_read_raw) {
1077
0
            size_t newlen = this_vec->vec.len;
1078
0
            void *newbuf = sendvec_size_is_for_recycle(newlen) ? h2o_mem_alloc_recycle(&h2o_socket_ssl_buffer_allocator)
1079
0
                                                               : h2o_mem_alloc(newlen);
1080
0
            if (!this_vec->vec.callbacks->read_(&this_vec->vec, newbuf, newlen)) {
1081
0
                free(newbuf);
1082
0
                goto Error;
1083
0
            }
1084
0
            this_vec->vec = (h2o_sendvec_t){&self_allocated_vec_callbacks, newlen, {newbuf}};
1085
0
        }
1086
        /* copy payload */
1087
3.48k
        memcpy(dst, this_vec->vec.raw + off, sz);
1088
        /* adjust offsets */
1089
3.48k
        if (this_vec->entity_offset != UINT64_MAX && stream->req.bytes_sent < this_vec->entity_offset + off + sz)
1090
1.11k
            stream->req.bytes_sent = this_vec->entity_offset + off + sz;
1091
3.48k
        dst += sz;
1092
3.48k
        off += sz;
1093
        /* when reaching the end of the current vector, update vec_index, wrote_all */
1094
3.48k
        if (off == this_vec->vec.len) {
1095
3.48k
            off = 0;
1096
3.48k
            ++vec_index;
1097
3.48k
            if (vec_index == stream->sendbuf.vecs.size) {
1098
1.24k
                *wrote_all = 1;
1099
1.24k
                break;
1100
1.24k
            }
1101
3.48k
        }
1102
3.48k
    } while (dst != dst_end);
1103
1104
1.24k
    *len = dst - (uint8_t *)_dst;
1105
1106
    /* retain the payload of response body before calling `h2o_proceed_request`, as the generator might discard the buffer */
1107
1.24k
    if (stream->state == H2O_HTTP3_SERVER_STREAM_STATE_SEND_BODY && *wrote_all &&
1108
1.24k
        quicly_sendstate_is_open(&stream->quic->sendstate) && !stream->proceed_requested) {
1109
368
        if (!retain_sendvecs(stream))
1110
0
            goto Error;
1111
368
        stream->proceed_requested = 1;
1112
368
        stream->proceed_while_sending = 1;
1113
368
    }
1114
1115
1.24k
    return;
1116
1.24k
Error:
1117
0
    *len = 0;
1118
0
    *wrote_all = 1;
1119
0
    shutdown_stream(stream, H2O_HTTP3_ERROR_EARLY_RESPONSE, H2O_HTTP3_ERROR_INTERNAL, 0, 0);
1120
0
}
1121
1122
static void on_send_stop(quicly_stream_t *qs, quicly_error_t err)
1123
0
{
1124
0
    struct st_h2o_http3_server_stream_t *stream = qs->data;
1125
1126
0
    shutdown_stream(stream, H2O_HTTP3_ERROR_REQUEST_CANCELLED, err, 0, 0);
1127
0
}
1128
1129
static void handle_buffered_input(struct st_h2o_http3_server_stream_t *stream, int in_generator)
1130
6.28k
{
1131
6.28k
    struct st_h2o_http3_server_conn_t *conn = get_conn(stream);
1132
1133
6.28k
    if (stream->state >= H2O_HTTP3_SERVER_STREAM_STATE_CLOSE_WAIT)
1134
0
        return;
1135
6.28k
    if (stream->qpack_blocked_ref != 0)
1136
0
        return;
1137
1138
6.28k
    { /* Process contiguous bytes in the receive buffer until one of the following conditions are reached:
1139
       * a) connection- or stream-level error (i.e., state advanced to CLOSE_WAIT) is detected - in which case we exit,
1140
       * b) incomplete frame is detected - wait for more (if the stream is open) or raise a connection error, or
1141
       * c) all bytes are processed or read_blocked / qpack_blocked_ref is set synchronously - exit the loop. */
1142
6.28k
        size_t bytes_available = quicly_recvstate_bytes_available(&stream->quic->recvstate);
1143
6.28k
        assert(bytes_available <= stream->recvbuf.buf->size);
1144
6.28k
        if (bytes_available != 0) {
1145
6.28k
            const uint8_t *src = (const uint8_t *)stream->recvbuf.buf->bytes, *src_end = src + bytes_available;
1146
24.5k
            do {
1147
24.5k
                quicly_error_t err;
1148
24.5k
                const char *err_desc = NULL;
1149
24.5k
                if ((err = stream->recvbuf.handle_input(stream, &src, src_end, in_generator, &err_desc)) != 0) {
1150
4.04k
                    if (err == H2O_HTTP3_ERROR_INCOMPLETE) {
1151
184
                        if (!quicly_recvstate_transfer_complete(&stream->quic->recvstate))
1152
0
                            break;
1153
184
                        err = H2O_HTTP3_ERROR_GENERAL_PROTOCOL;
1154
184
                        err_desc = "incomplete frame";
1155
184
                    }
1156
4.04k
                    h2o_quic_close_connection(&conn->h3.super, err, err_desc);
1157
4.04k
                    return;
1158
20.4k
                } else if (stream->state >= H2O_HTTP3_SERVER_STREAM_STATE_CLOSE_WAIT) {
1159
374
                    return;
1160
374
                }
1161
24.5k
            } while (src != src_end && !stream->read_blocked && stream->qpack_blocked_ref == 0 &&
1162
18.2k
                     !quicly_stop_requested(stream->quic));
1163
            /* Processed zero or more bytes without noticing an error; shift the bytes that have been processed as frames. */
1164
1.86k
            size_t bytes_consumed = src - (const uint8_t *)stream->recvbuf.buf->bytes;
1165
1.86k
            h2o_buffer_consume(&stream->recvbuf.buf, bytes_consumed);
1166
1.86k
            quicly_stream_sync_recvbuf(stream->quic, bytes_consumed);
1167
1.86k
            if (stream->read_blocked || stream->qpack_blocked_ref != 0)
1168
233
                return;
1169
1.86k
        }
1170
6.28k
    }
1171
1172
1.63k
    if (quicly_recvstate_transfer_complete(&stream->quic->recvstate)) {
1173
1.63k
        if (stream->recvbuf.buf->size == 0 && (stream->recvbuf.handle_input == handle_input_expect_data ||
1174
1.19k
                                               stream->recvbuf.handle_input == handle_input_post_trailers)) {
1175
            /* have complete request, advance the state and process the request */
1176
1.19k
            if (stream->req.content_length != SIZE_MAX && stream->req.content_length != stream->req.req_body_bytes_received) {
1177
                /* the request terminated abruptly; reset the stream as we do for HTTP/2 */
1178
186
                shutdown_stream(stream, H2O_HTTP3_ERROR_NONE /* ignored */,
1179
186
                                stream->req.req_body_bytes_received < stream->req.content_length
1180
186
                                    ? H2O_HTTP3_ERROR_REQUEST_INCOMPLETE
1181
186
                                    : H2O_HTTP3_ERROR_GENERAL_PROTOCOL,
1182
186
                                in_generator, 0);
1183
1.01k
            } else {
1184
1.01k
                if (stream->req.write_req.cb != NULL) {
1185
0
                    if (!h2o_linklist_is_linked(&stream->link))
1186
0
                        h2o_linklist_insert(&conn->delayed_streams.req_streaming, &stream->link);
1187
0
                    request_run_delayed(conn);
1188
1.01k
                } else if (!stream->req.process_called && stream->state < H2O_HTTP3_SERVER_STREAM_STATE_SEND_HEADERS) {
1189
                    /* process the request, if we haven't called h2o_process_request nor send an error response */
1190
783
                    switch (stream->state) {
1191
0
                    case H2O_HTTP3_SERVER_STREAM_STATE_RECV_HEADERS:
1192
783
                    case H2O_HTTP3_SERVER_STREAM_STATE_RECV_BODY_BEFORE_BLOCK:
1193
783
                    case H2O_HTTP3_SERVER_STREAM_STATE_RECV_BODY_UNBLOCKED:
1194
783
                        break;
1195
0
                    default:
1196
0
                        assert(!"unexpected state");
1197
0
                        break;
1198
783
                    }
1199
783
                    set_state(stream, H2O_HTTP3_SERVER_STREAM_STATE_REQ_PENDING, in_generator);
1200
783
                    h2o_linklist_insert(&conn->delayed_streams.pending, &stream->link);
1201
783
                    request_run_delayed(conn);
1202
783
                }
1203
1.01k
            }
1204
1.19k
        } else {
1205
            /* request stream closed with an incomplete request, send error */
1206
436
            shutdown_stream(stream, H2O_HTTP3_ERROR_NONE /* ignored */, H2O_HTTP3_ERROR_REQUEST_INCOMPLETE, in_generator, 0);
1207
436
        }
1208
1.63k
    } else {
1209
0
        if (stream->state == H2O_HTTP3_SERVER_STREAM_STATE_RECV_BODY_BEFORE_BLOCK && stream->req_body != NULL &&
1210
0
            stream->req_body->size >= H2O_HTTP3_REQUEST_BODY_MIN_BYTES_TO_BLOCK) {
1211
            /* switch to blocked state if the request body is becoming large (this limits the concurrency to the backend) */
1212
0
            stream->read_blocked = 1;
1213
0
            h2o_linklist_insert(&conn->delayed_streams.recv_body_blocked, &stream->link);
1214
0
            set_state(stream, H2O_HTTP3_SERVER_STREAM_STATE_RECV_BODY_BLOCKED, in_generator);
1215
0
            check_run_blocked(conn);
1216
0
        } else if (stream->req.write_req.cb != NULL && stream->req_body->size != 0) {
1217
            /* in streaming mode, let the run_delayed invoke write_req */
1218
0
            if (!h2o_linklist_is_linked(&stream->link))
1219
0
                h2o_linklist_insert(&conn->delayed_streams.req_streaming, &stream->link);
1220
0
            request_run_delayed(conn);
1221
0
        }
1222
0
    }
1223
1.63k
}
1224
1225
static void on_receive(quicly_stream_t *qs, size_t off, const void *input, size_t len)
1226
6.28k
{
1227
6.28k
    struct st_h2o_http3_server_stream_t *stream = qs->data;
1228
1229
    /* save received data (FIXME avoid copying if possible; see hqclient.c) */
1230
6.28k
    h2o_http3_update_recvbuf(&stream->recvbuf.buf, off, input, len);
1231
1232
6.28k
    if (stream->read_blocked || quicly_stop_requested(stream->quic))
1233
0
        return;
1234
1235
    /* handle input (FIXME propage err_desc) */
1236
6.28k
    handle_buffered_input(stream, 0);
1237
6.28k
}
1238
1239
static void on_receive_reset(quicly_stream_t *qs, quicly_error_t err)
1240
0
{
1241
0
    struct st_h2o_http3_server_stream_t *stream = qs->data;
1242
1243
0
    shutdown_stream(stream, H2O_HTTP3_ERROR_NONE /* ignored */,
1244
0
                    stream->state == H2O_HTTP3_SERVER_STREAM_STATE_RECV_HEADERS ? H2O_HTTP3_ERROR_REQUEST_REJECTED
1245
0
                                                                                : H2O_HTTP3_ERROR_REQUEST_CANCELLED,
1246
0
                    0, 1);
1247
0
}
1248
1249
static void close_request_streaming(struct st_h2o_http3_server_stream_t *stream)
1250
0
{
1251
0
    stream->req.write_req.cb = NULL;
1252
0
    stream->req.write_req.ctx = NULL;
1253
0
    stream->req.forward_datagram.write_ = NULL;
1254
0
    stream->req.proceed_req = NULL;
1255
0
    stream->req_streaming = 0;
1256
0
    if (!stream->req.is_tunnel_req)
1257
0
        --get_conn(stream)->num_streams_req_streaming;
1258
0
    check_run_blocked(get_conn(stream));
1259
0
}
1260
1261
static void proceed_request_streaming(h2o_req_t *_req, const char *errstr)
1262
0
{
1263
0
    struct st_h2o_http3_server_stream_t *stream = H2O_STRUCT_FROM_MEMBER(struct st_h2o_http3_server_stream_t, req, _req);
1264
0
    struct st_h2o_http3_server_conn_t *conn = get_conn(stream);
1265
1266
0
    assert(stream->req_body != NULL);
1267
0
    assert(errstr != NULL || !h2o_linklist_is_linked(&stream->link));
1268
0
    assert(conn->num_streams_req_streaming != 0 || stream->req.is_tunnel_req);
1269
1270
0
    int reset_received = quicly_recvstate_transfer_complete(&stream->quic->recvstate) && stream->quic->recvstate.eos == UINT64_MAX;
1271
0
    if (errstr != NULL || reset_received) {
1272
0
        close_request_streaming(stream);
1273
0
        shutdown_stream(stream, H2O_HTTP3_ERROR_INTERNAL, H2O_HTTP3_ERROR_INTERNAL, 1, 1);
1274
0
        return;
1275
0
    }
1276
1277
    /* remove the bytes from the request body buffer */
1278
0
    assert(stream->req.entity.len == stream->req_body->size);
1279
0
    h2o_buffer_consume(&stream->req_body, stream->req_body->size);
1280
0
    stream->req.entity = h2o_iovec_init(NULL, 0);
1281
1282
    /* unblock read until the next invocation of write_req, or after the final invocation */
1283
0
    stream->read_blocked = 0;
1284
1285
0
    if (stream->req_streaming_eos_delivered) {
1286
0
        close_request_streaming(stream);
1287
0
        return;
1288
0
    }
1289
1290
    /* handle input in the receive buffer */
1291
0
    handle_buffered_input(stream, 1);
1292
0
}
1293
1294
static void run_delayed(h2o_timer_t *timer)
1295
783
{
1296
783
    struct st_h2o_http3_server_conn_t *conn = H2O_STRUCT_FROM_MEMBER(struct st_h2o_http3_server_conn_t, timeout, timer);
1297
783
    int made_progress;
1298
1299
1.56k
    do {
1300
1.56k
        made_progress = 0;
1301
1302
        /* promote blocked stream to unblocked state, if possible */
1303
1.56k
        if (conn->num_streams.recv_body_unblocked + conn->num_streams_req_streaming <
1304
1.56k
                conn->super.ctx->globalconf->http3.max_concurrent_streaming_requests_per_connection &&
1305
1.56k
            !h2o_linklist_is_empty(&conn->delayed_streams.recv_body_blocked)) {
1306
0
            struct st_h2o_http3_server_stream_t *stream =
1307
0
                H2O_STRUCT_FROM_MEMBER(struct st_h2o_http3_server_stream_t, link, conn->delayed_streams.recv_body_blocked.next);
1308
0
            assert(stream->state == H2O_HTTP3_SERVER_STREAM_STATE_RECV_BODY_BLOCKED);
1309
0
            assert(stream->read_blocked);
1310
0
            h2o_linklist_unlink(&stream->link);
1311
0
            made_progress = 1;
1312
0
            quicly_stream_set_receive_window(stream->quic, conn->super.ctx->globalconf->http3.active_stream_window_size);
1313
0
            if (h2o_req_can_stream_request(&stream->req)) {
1314
                /* use streaming mode */
1315
0
                stream->req_streaming = 1;
1316
0
                ++conn->num_streams_req_streaming;
1317
0
                stream->req.proceed_req = proceed_request_streaming;
1318
0
                set_state(stream, H2O_HTTP3_SERVER_STREAM_STATE_SEND_HEADERS, 0);
1319
0
                h2o_process_request(&stream->req);
1320
0
            } else {
1321
                /* unblock, read the bytes in receive buffer */
1322
0
                stream->read_blocked = 0;
1323
0
                set_state(stream, H2O_HTTP3_SERVER_STREAM_STATE_RECV_BODY_UNBLOCKED, 0);
1324
0
                handle_buffered_input(stream, 0);
1325
0
                if (quicly_get_state(conn->h3.super.quic) >= QUICLY_STATE_CLOSING)
1326
0
                    return;
1327
0
            }
1328
0
        }
1329
1330
        /* process streams using request streaming, that have new data to submit */
1331
1.56k
        while (!h2o_linklist_is_empty(&conn->delayed_streams.req_streaming)) {
1332
0
            struct st_h2o_http3_server_stream_t *stream =
1333
0
                H2O_STRUCT_FROM_MEMBER(struct st_h2o_http3_server_stream_t, link, conn->delayed_streams.req_streaming.next);
1334
0
            int is_end_stream = quicly_recvstate_transfer_complete(&stream->quic->recvstate);
1335
0
            assert(stream->req.process_called);
1336
0
            assert(stream->req.write_req.cb != NULL);
1337
0
            assert(stream->req_body != NULL);
1338
0
            assert(stream->req_body->size != 0 || is_end_stream);
1339
0
            assert(!stream->read_blocked);
1340
0
            h2o_linklist_unlink(&stream->link);
1341
0
            stream->read_blocked = 1;
1342
0
            if (is_end_stream)
1343
0
                stream->req_streaming_eos_delivered = 1;
1344
0
            made_progress = 1;
1345
0
            assert(stream->req.entity.len == stream->req_body->size &&
1346
0
                   (stream->req.entity.len == 0 || stream->req.entity.base == stream->req_body->bytes));
1347
0
            if (stream->req.write_req.cb(stream->req.write_req.ctx, is_end_stream) != 0)
1348
0
                shutdown_stream(stream, H2O_HTTP3_ERROR_INTERNAL, H2O_HTTP3_ERROR_INTERNAL, 0, 1);
1349
0
        }
1350
1351
        /* process the requests (not in streaming mode); TODO cap concurrency? */
1352
2.34k
        while (!h2o_linklist_is_empty(&conn->delayed_streams.pending)) {
1353
783
            struct st_h2o_http3_server_stream_t *stream =
1354
783
                H2O_STRUCT_FROM_MEMBER(struct st_h2o_http3_server_stream_t, link, conn->delayed_streams.pending.next);
1355
783
            assert(stream->state == H2O_HTTP3_SERVER_STREAM_STATE_REQ_PENDING);
1356
783
            assert(!stream->req.process_called);
1357
783
            assert(!stream->read_blocked);
1358
783
            h2o_linklist_unlink(&stream->link);
1359
783
            made_progress = 1;
1360
783
            set_state(stream, H2O_HTTP3_SERVER_STREAM_STATE_SEND_HEADERS, 0);
1361
783
            h2o_process_request(&stream->req);
1362
783
        }
1363
1364
1.56k
    } while (made_progress);
1365
783
}
1366
1367
quicly_error_t handle_input_post_trailers(struct st_h2o_http3_server_stream_t *stream, const uint8_t **src, const uint8_t *src_end,
1368
                                          int in_generator, const char **err_desc)
1369
377
{
1370
377
    h2o_http3_read_frame_t frame;
1371
377
    quicly_error_t ret;
1372
1373
    /* read and ignore unknown frames */
1374
377
    if ((ret = h2o_http3_read_frame(&frame, 0, H2O_HTTP3_STREAM_TYPE_REQUEST, get_conn(stream)->h3.max_frame_payload_size, src,
1375
377
                                    src_end, err_desc)) != 0)
1376
15
        return ret;
1377
362
    switch (frame.type) {
1378
1
    case H2O_HTTP3_FRAME_TYPE_HEADERS:
1379
5
    case H2O_HTTP3_FRAME_TYPE_DATA:
1380
5
        return H2O_HTTP3_ERROR_FRAME_UNEXPECTED;
1381
357
    default:
1382
357
        break;
1383
362
    }
1384
1385
357
    return 0;
1386
362
}
1387
1388
static quicly_error_t handle_input_expect_data_payload(struct st_h2o_http3_server_stream_t *stream, const uint8_t **src,
1389
                                                       const uint8_t *src_end, int in_generator, const char **err_desc)
1390
1.72k
{
1391
1.72k
    size_t bytes_avail = src_end - *src;
1392
1393
    /* append data to body buffer */
1394
1.72k
    if (bytes_avail > stream->recvbuf.bytes_left_in_data_frame)
1395
1.55k
        bytes_avail = stream->recvbuf.bytes_left_in_data_frame;
1396
1.72k
    if (stream->req_body == NULL)
1397
201
        h2o_buffer_init(&stream->req_body, &h2o_socket_buffer_prototype);
1398
1.72k
    if (!h2o_buffer_try_append(&stream->req_body, *src, bytes_avail))
1399
0
        return H2O_HTTP3_ERROR_INTERNAL;
1400
1.72k
    stream->req.entity = h2o_iovec_init(stream->req_body->bytes, stream->req_body->size);
1401
1.72k
    stream->req.req_body_bytes_received += bytes_avail;
1402
1.72k
    stream->recvbuf.bytes_left_in_data_frame -= bytes_avail;
1403
1.72k
    *src += bytes_avail;
1404
1405
1.72k
    if (stream->recvbuf.bytes_left_in_data_frame == 0)
1406
1.57k
        stream->recvbuf.handle_input = handle_input_expect_data;
1407
1408
1.72k
    return 0;
1409
1.72k
}
1410
1411
quicly_error_t handle_input_expect_data(struct st_h2o_http3_server_stream_t *stream, const uint8_t **src, const uint8_t *src_end,
1412
                                        int in_generator, const char **err_desc)
1413
15.3k
{
1414
15.3k
    h2o_http3_read_frame_t frame;
1415
15.3k
    quicly_error_t ret;
1416
1417
    /* read frame */
1418
15.3k
    if ((ret = h2o_http3_read_frame(&frame, 0, H2O_HTTP3_STREAM_TYPE_REQUEST, get_conn(stream)->h3.max_frame_payload_size, src,
1419
15.3k
                                    src_end, err_desc)) != 0)
1420
64
        return ret;
1421
15.2k
    switch (frame.type) {
1422
79
    case H2O_HTTP3_FRAME_TYPE_HEADERS:
1423
        /* when in tunnel mode, trailers forbidden */
1424
79
        if (stream->req.is_tunnel_req) {
1425
0
            *err_desc = "unexpected frame type";
1426
0
            return H2O_HTTP3_ERROR_FRAME_UNEXPECTED;
1427
0
        }
1428
        /* trailers, ignore but disallow succeeding DATA or HEADERS frame */
1429
79
        stream->recvbuf.handle_input = handle_input_post_trailers;
1430
79
        return 0;
1431
14.9k
    case H2O_HTTP3_FRAME_TYPE_DATA:
1432
14.9k
        if (stream->req.content_length != SIZE_MAX &&
1433
364
            stream->req.content_length - stream->req.req_body_bytes_received < frame.length) {
1434
            /* The only viable option here is to reset the stream, as we might have already started streaming the request body
1435
             * upstream. This behavior is consistent with what we do in HTTP/2. */
1436
62
            shutdown_stream(stream, H2O_HTTP3_ERROR_EARLY_RESPONSE, H2O_HTTP3_ERROR_GENERAL_PROTOCOL, in_generator, 0);
1437
62
            return 0;
1438
62
        }
1439
14.8k
        break;
1440
14.8k
    default:
1441
270
        return 0;
1442
15.2k
    }
1443
1444
    /* got a DATA frame */
1445
14.8k
    if (frame.length != 0) {
1446
1.82k
        if (h2o_timeval_is_null(&stream->req.timestamps.request_body_begin_at))
1447
298
            stream->req.timestamps.request_body_begin_at = h2o_gettimeofday(get_conn(stream)->super.ctx->loop);
1448
1.82k
        stream->recvbuf.handle_input = handle_input_expect_data_payload;
1449
1.82k
        stream->recvbuf.bytes_left_in_data_frame = frame.length;
1450
1.82k
    }
1451
1452
14.8k
    return 0;
1453
15.2k
}
1454
1455
static int handle_input_expect_headers_send_http_error(struct st_h2o_http3_server_stream_t *stream,
1456
                                                       void (*sendfn)(h2o_req_t *, const char *, const char *, int),
1457
                                                       const char *reason, const char *body, const char **err_desc)
1458
577
{
1459
577
    if (!quicly_recvstate_transfer_complete(&stream->quic->recvstate)) {
1460
0
        quicly_request_stop(stream->quic, H2O_HTTP3_ERROR_EARLY_RESPONSE);
1461
0
        cancel_qpack_decoder(stream);
1462
0
    }
1463
1464
577
    set_state(stream, H2O_HTTP3_SERVER_STREAM_STATE_SEND_HEADERS, 0);
1465
577
    sendfn(&stream->req, reason, body, 0);
1466
577
    *err_desc = NULL;
1467
1468
577
    return 0;
1469
577
}
1470
1471
static int handle_input_expect_headers_process_request_immediately(struct st_h2o_http3_server_stream_t *stream,
1472
                                                                   const char **err_desc)
1473
233
{
1474
233
    h2o_buffer_init(&stream->req_body, &h2o_socket_buffer_prototype);
1475
233
    stream->req.entity = h2o_iovec_init("", 0);
1476
233
    stream->read_blocked = 1;
1477
233
    stream->req.proceed_req = proceed_request_streaming;
1478
233
    set_state(stream, H2O_HTTP3_SERVER_STREAM_STATE_SEND_HEADERS, 0);
1479
233
    quicly_stream_set_receive_window(stream->quic, get_conn(stream)->super.ctx->globalconf->http3.active_stream_window_size);
1480
233
    h2o_process_request(&stream->req);
1481
1482
233
    return 0;
1483
233
}
1484
1485
static int handle_input_expect_headers_process_connect(struct st_h2o_http3_server_stream_t *stream, uint64_t datagram_flow_id,
1486
                                                       const char **err_desc)
1487
432
{
1488
432
    if (stream->req.content_length != SIZE_MAX)
1489
199
        return handle_input_expect_headers_send_http_error(stream, h2o_send_error_400, "Invalid Request",
1490
199
                                                           "CONNECT request cannot have request body", err_desc);
1491
1492
233
    stream->req.is_tunnel_req = 1;
1493
233
    stream->datagram_flow_id = datagram_flow_id;
1494
233
    ++get_conn(stream)->num_streams_tunnelling;
1495
1496
233
    return handle_input_expect_headers_process_request_immediately(stream, err_desc);
1497
432
}
1498
1499
static quicly_error_t handle_input_expect_headers(struct st_h2o_http3_server_stream_t *stream, const uint8_t **src,
1500
                                                  const uint8_t *src_end, int in_generator, const char **err_desc)
1501
7.10k
{
1502
7.10k
    assert(!in_generator); /* this function is processing headers (before generators get assigned), not trailers */
1503
1504
7.10k
    struct st_h2o_http3_server_conn_t *conn = get_conn(stream);
1505
7.10k
    h2o_http3_read_frame_t frame;
1506
7.10k
    int header_exists_map = 0;
1507
7.10k
    h2o_iovec_t expect = h2o_iovec_init(NULL, 0);
1508
7.10k
    h2o_iovec_t datagram_flow_id_field = {};
1509
7.10k
    uint64_t datagram_flow_id = UINT64_MAX;
1510
7.10k
    uint8_t header_ack[H2O_HPACK_ENCODE_INT_MAX_LENGTH];
1511
7.10k
    size_t header_ack_len;
1512
7.10k
    quicly_error_t ret;
1513
1514
7.10k
    if (h2o_timeval_is_null(&stream->req.timestamps.request_begin_at))
1515
6.28k
        stream->req.timestamps.request_begin_at = h2o_gettimeofday(conn->super.ctx->loop);
1516
1517
    /* read the HEADERS frame (or a frame that precedes that) */
1518
7.10k
    const uint8_t *frame_start = *src;
1519
7.10k
    if ((ret = h2o_http3_read_frame(&frame, 0, H2O_HTTP3_STREAM_TYPE_REQUEST, get_conn(stream)->h3.max_frame_payload_size, src,
1520
7.10k
                                    src_end, err_desc)) != 0) {
1521
296
        if (*err_desc == h2o_http3_err_frame_too_large && frame.type == H2O_HTTP3_FRAME_TYPE_HEADERS) {
1522
52
            shutdown_stream(stream, H2O_HTTP3_ERROR_REQUEST_REJECTED, H2O_HTTP3_ERROR_REQUEST_REJECTED, 0, 0);
1523
52
            return 0;
1524
244
        } else {
1525
244
            return ret;
1526
244
        }
1527
296
    }
1528
6.80k
    if (frame.type != H2O_HTTP3_FRAME_TYPE_HEADERS) {
1529
1.01k
        switch (frame.type) {
1530
7
        case H2O_HTTP3_FRAME_TYPE_DATA:
1531
7
            return H2O_HTTP3_ERROR_FRAME_UNEXPECTED;
1532
1.00k
        default:
1533
1.00k
            break;
1534
1.01k
        }
1535
1.00k
        return 0;
1536
1.01k
    }
1537
1538
    /* parse the headers */
1539
5.79k
    stream->stats.req.headers_frame_bytes += frame.length;
1540
5.79k
    if ((ret = h2o_qpack_parse_request(&stream->req.pool, get_conn(stream)->h3.qpack.dec, stream->quic->stream_id,
1541
5.79k
                                       &stream->req.input.method, &stream->req.input.scheme, &stream->req.input.authority,
1542
5.79k
                                       &stream->req.input.path, &stream->req.upgrade, &stream->req.headers, &header_exists_map,
1543
5.79k
                                       &stream->req.content_length, &expect, NULL /* TODO cache-digests */, &datagram_flow_id_field,
1544
5.79k
                                       conn->num_qpack_blocked, &stream->qpack_blocked_ref, &stream->stats.req.qpack, header_ack,
1545
5.79k
                                       &header_ack_len, frame.payload, frame.length, err_desc)) != 0 &&
1546
4.02k
        ret != H2O_HTTP2_ERROR_INVALID_HEADER_CHAR) {
1547
3.71k
        return ret;
1548
3.71k
    }
1549
1550
    /* if the decoding of the frame is blocked by QPACK, return, preserving the HEADERS frame in the receive buffer */
1551
2.08k
    if (stream->qpack_blocked_ref != 0) {
1552
0
        stream->qpack_blocked_ever = 1;
1553
0
        ++conn->num_qpack_blocked;
1554
0
        h2o_linklist_insert(&conn->delayed_streams.qpack_blocked, &stream->link);
1555
0
        *src = frame_start;
1556
0
        return 0;
1557
0
    }
1558
1559
    /* now that the header section have been read and decoded, update the receiver callback and emit an ACK */
1560
2.08k
    stream->recvbuf.handle_input = handle_input_expect_data;
1561
2.08k
    if (header_ack_len != 0)
1562
0
        h2o_http3_send_qpack_header_ack(&conn->h3, header_ack, header_ack_len);
1563
1564
2.08k
    h2o_probe_log_request(&stream->req, stream->quic->stream_id);
1565
1566
2.08k
    if (stream->req.input.scheme == NULL)
1567
786
        stream->req.input.scheme = &H2O_URL_SCHEME_HTTPS;
1568
1569
2.08k
    int is_connect, must_exist_map, may_exist_map;
1570
2.08k
    const int can_receive_datagrams =
1571
2.08k
        quicly_get_context(get_conn(stream)->h3.super.quic)->transport_params.max_datagram_frame_size != 0;
1572
2.08k
    if (h2o_memis(stream->req.input.method.base, stream->req.input.method.len, H2O_STRLIT("CONNECT"))) {
1573
575
        is_connect = 1;
1574
575
        must_exist_map = H2O_HPACK_PARSE_HEADERS_METHOD_EXISTS | H2O_HPACK_PARSE_HEADERS_AUTHORITY_EXISTS;
1575
575
        may_exist_map = 0;
1576
        /* extended connect looks like an ordinary request plus an upgrade token (:protocol) */
1577
575
        if ((header_exists_map & H2O_HPACK_PARSE_HEADERS_PROTOCOL_EXISTS) != 0) {
1578
1
            must_exist_map |= H2O_HPACK_PARSE_HEADERS_SCHEME_EXISTS | H2O_HPACK_PARSE_HEADERS_PATH_EXISTS |
1579
1
                              H2O_HPACK_PARSE_HEADERS_PROTOCOL_EXISTS;
1580
1
            if (can_receive_datagrams)
1581
1
                datagram_flow_id = stream->quic->stream_id / 4;
1582
1
        }
1583
1.50k
    } else if (h2o_memis(stream->req.input.method.base, stream->req.input.method.len, H2O_STRLIT("CONNECT-UDP"))) {
1584
        /* Handling of masque draft-03. Method is CONNECT-UDP and :protocol is not used, so we set `:protocol` to "connect-udp" to
1585
         * make it look like an upgrade. The method is preserved and can be used to distinguish between RFC 9298 version which uses
1586
         * "CONNECT". The draft requires "masque" in `:scheme` but we need to support clients that put "https" there instead. */
1587
14
        if (!((header_exists_map & H2O_HPACK_PARSE_HEADERS_PROTOCOL_EXISTS) == 0 &&
1588
13
              h2o_memis(stream->req.input.path.base, stream->req.input.path.len, H2O_STRLIT("/")))) {
1589
11
            shutdown_stream(stream, H2O_HTTP3_ERROR_GENERAL_PROTOCOL, H2O_HTTP3_ERROR_GENERAL_PROTOCOL, 0, 0);
1590
11
            return 0;
1591
11
        }
1592
3
        if (datagram_flow_id_field.base != NULL) {
1593
0
            if (!can_receive_datagrams) {
1594
0
                *err_desc = "unexpected h3 datagram";
1595
0
                return H2O_HTTP3_ERROR_GENERAL_PROTOCOL;
1596
0
            }
1597
0
            datagram_flow_id = 0;
1598
0
            for (const char *p = datagram_flow_id_field.base; p != datagram_flow_id_field.base + datagram_flow_id_field.len; ++p) {
1599
0
                if (!('0' <= *p && *p <= '9'))
1600
0
                    break;
1601
0
                datagram_flow_id = datagram_flow_id * 10 + *p - '0';
1602
0
            }
1603
0
        }
1604
3
        assert(stream->req.upgrade.base == NULL); /* otherwise PROTOCOL_EXISTS will be set */
1605
3
        is_connect = 1;
1606
3
        must_exist_map = H2O_HPACK_PARSE_HEADERS_METHOD_EXISTS | H2O_HPACK_PARSE_HEADERS_AUTHORITY_EXISTS |
1607
3
                         H2O_HPACK_PARSE_HEADERS_SCHEME_EXISTS | H2O_HPACK_PARSE_HEADERS_PATH_EXISTS;
1608
3
        may_exist_map = 0;
1609
1.49k
    } else {
1610
        /* normal request */
1611
1.49k
        is_connect = 0;
1612
1.49k
        must_exist_map =
1613
1.49k
            H2O_HPACK_PARSE_HEADERS_METHOD_EXISTS | H2O_HPACK_PARSE_HEADERS_SCHEME_EXISTS | H2O_HPACK_PARSE_HEADERS_PATH_EXISTS;
1614
1.49k
        may_exist_map = H2O_HPACK_PARSE_HEADERS_AUTHORITY_EXISTS;
1615
1.49k
    }
1616
1617
    /* check that all MUST pseudo headers exist, and that there are no other pseudo headers than MUST or MAY */
1618
2.07k
    if (!((header_exists_map & must_exist_map) == must_exist_map && (header_exists_map & ~(must_exist_map | may_exist_map)) == 0)) {
1619
249
        shutdown_stream(stream, H2O_HTTP3_ERROR_GENERAL_PROTOCOL, H2O_HTTP3_ERROR_GENERAL_PROTOCOL, 0, 0);
1620
249
        return 0;
1621
249
    }
1622
1623
    /* send a 400 error when observing an invalid header character */
1624
1.82k
    if (ret == H2O_HTTP2_ERROR_INVALID_HEADER_CHAR)
1625
217
        return handle_input_expect_headers_send_http_error(stream, h2o_send_error_400, "Invalid Request", *err_desc, err_desc);
1626
1627
    /* validate semantic requirement */
1628
1.60k
    if (!h2o_req_validate_pseudo_headers(&stream->req)) {
1629
2
        *err_desc = "invalid pseudo headers";
1630
2
        return H2O_HTTP3_ERROR_GENERAL_PROTOCOL;
1631
2
    }
1632
1633
    /* check if content-length is within the permitted bounds */
1634
1.60k
    if (stream->req.content_length != SIZE_MAX && stream->req.content_length > conn->super.ctx->globalconf->max_request_entity_size)
1635
150
        return handle_input_expect_headers_send_http_error(stream, h2o_send_error_413, "Request Entity Too Large",
1636
150
                                                           "request entity is too large", err_desc);
1637
1638
    /* set priority */
1639
1.60k
    assert(!h2o_linklist_is_linked(&stream->scheduler.link));
1640
1.45k
    if (!stream->received_priority_update) {
1641
1.45k
        ssize_t index;
1642
1.45k
        if ((index = h2o_find_header(&stream->req.headers, H2O_TOKEN_PRIORITY, -1)) != -1) {
1643
278
            h2o_iovec_t *value = &stream->req.headers.entries[index].value;
1644
278
            h2o_absprio_parse_priority(value->base, value->len, &stream->scheduler.priority);
1645
1.17k
        } else if (is_connect) {
1646
234
            stream->scheduler.priority.incremental = 1;
1647
234
        }
1648
1.45k
    }
1649
1650
    /* special handling of CONNECT method */
1651
1.45k
    if (is_connect)
1652
432
        return handle_input_expect_headers_process_connect(stream, datagram_flow_id, err_desc);
1653
1654
    /* change state */
1655
1.02k
    set_state(stream, H2O_HTTP3_SERVER_STREAM_STATE_RECV_BODY_BEFORE_BLOCK, 0);
1656
1657
    /* handle expect: 100-continue */
1658
1.02k
    if (expect.base != NULL) {
1659
11
        if (!h2o_lcstris(expect.base, expect.len, H2O_STRLIT("100-continue"))) {
1660
11
            return handle_input_expect_headers_send_http_error(stream, h2o_send_error_417, "Expectation Failed",
1661
11
                                                               "unknown expectation", err_desc);
1662
11
        }
1663
0
        if (h2o_req_should_forward_expect(&stream->req)) {
1664
0
            h2o_add_header(&stream->req.pool, &stream->req.headers, H2O_TOKEN_EXPECT, NULL, expect.base, expect.len);
1665
0
            stream->req_streaming = 1;
1666
0
            ++conn->num_streams_req_streaming;
1667
0
            return handle_input_expect_headers_process_request_immediately(stream, err_desc);
1668
0
        } else {
1669
0
            stream->req.res.status = 100;
1670
0
            h2o_send_informational(&stream->req);
1671
0
        }
1672
0
    }
1673
1674
1.01k
    return 0;
1675
1.02k
}
1676
1677
static void write_response(struct st_h2o_http3_server_stream_t *stream, h2o_iovec_t datagram_flow_id)
1678
1.59k
{
1679
1.59k
    size_t serialized_header_len = 0;
1680
1.59k
    h2o_iovec_t frame = h2o_qpack_flatten_response(
1681
1.59k
        get_conn(stream)->h3.qpack.enc, &stream->req.pool, stream->quic->stream_id, NULL, stream->req.res.status,
1682
1.59k
        stream->req.res.headers.entries, stream->req.res.headers.size, &get_conn(stream)->super.ctx->globalconf->server_name,
1683
1.59k
        stream->req.res.content_length, datagram_flow_id, &stream->stats.resp.qpack, &serialized_header_len);
1684
1.59k
    stream->req.header_bytes_sent += serialized_header_len;
1685
1.59k
    stream->stats.resp.headers_frame_bytes += serialized_header_len;
1686
1687
1.59k
    h2o_vector_reserve(&stream->req.pool, &stream->sendbuf.vecs, stream->sendbuf.vecs.size + 1);
1688
1.59k
    struct st_h2o_http3_server_sendvec_t *vec = stream->sendbuf.vecs.entries + stream->sendbuf.vecs.size++;
1689
1.59k
    vec->vec = (h2o_sendvec_t){&immutable_vec_callbacks, frame.len, {frame.base}};
1690
1.59k
    vec->entity_offset = UINT64_MAX;
1691
1.59k
    stream->sendbuf.final_size += frame.len;
1692
1.59k
}
1693
1694
static size_t flatten_data_frame_header(struct st_h2o_http3_server_stream_t *stream, struct st_h2o_http3_server_sendvec_t *dst,
1695
                                        size_t payload_size)
1696
1.46k
{
1697
1.46k
    size_t header_size = 0;
1698
1699
    /* build header */
1700
1.46k
    stream->sendbuf.data_frame_header_buf[header_size++] = H2O_HTTP3_FRAME_TYPE_DATA;
1701
1.46k
    header_size =
1702
1.46k
        quicly_encodev(stream->sendbuf.data_frame_header_buf + header_size, payload_size) - stream->sendbuf.data_frame_header_buf;
1703
1704
    /* initilaize the vector */
1705
1.46k
    h2o_sendvec_init_raw(&dst->vec, stream->sendbuf.data_frame_header_buf, header_size);
1706
1.46k
    dst->entity_offset = UINT64_MAX;
1707
1708
1.46k
    return header_size;
1709
1.46k
}
1710
1711
static void shutdown_by_generator(struct st_h2o_http3_server_stream_t *stream)
1712
1.59k
{
1713
1.59k
    quicly_sendstate_shutdown(&stream->quic->sendstate, stream->sendbuf.final_size);
1714
1.59k
    if (stream->sendbuf.vecs.size == 0) {
1715
0
        if (quicly_stream_has_receive_side(0, stream->quic->stream_id)) {
1716
0
            quicly_request_stop(stream->quic, H2O_HTTP3_ERROR_EARLY_RESPONSE);
1717
0
            cancel_qpack_decoder(stream);
1718
0
        }
1719
0
        set_state(stream, H2O_HTTP3_SERVER_STREAM_STATE_CLOSE_WAIT, 1);
1720
0
    }
1721
1.59k
}
1722
1723
/**
1724
 * returns boolean indicating if the response is ready to be sent, building the value of datagram-flow-id header field
1725
 */
1726
static int finalize_do_send_setup_udp_tunnel(struct st_h2o_http3_server_stream_t *stream, h2o_send_state_t send_state,
1727
                                             h2o_iovec_t *datagram_flow_id)
1728
1.59k
{
1729
1.59k
    *datagram_flow_id = h2o_iovec_init(NULL, 0);
1730
1731
    /* TODO Convert H3_DATAGRAMs to capsules either here or inside the proxy handler. At the moment, the connect handler provides
1732
     * `h2o_req_t::forward_datagram` callbacks but the proxy handler does not. As support for H3_DATAGRAMs are advertised at the
1733
     * connection level, we need to support forwarding datagrams also when the proxy handler in use.
1734
     * Until then, connect-udp requests on H3 are refused to be tunneled by the proxy handler, see `h2o__proxy_process_request`.
1735
     * Also, as an abundance of caution, we drop the datagrams associated to requests that do not provide the forwarding hooks, by
1736
     * not registering such streams to `datagram_flows`. */
1737
1.59k
    if (!((200 <= stream->req.res.status && stream->req.res.status <= 299) && stream->req.forward_datagram.write_ != NULL) ||
1738
1.59k
        send_state != H2O_SEND_STATE_IN_PROGRESS) {
1739
1.59k
        stream->datagram_flow_id = UINT64_MAX;
1740
1.59k
        return 1;
1741
1.59k
    }
1742
1743
    /* Register the flow id to the connection so that datagram frames being received from the client would be dispatched to
1744
     * `req->forward_datagram.write_`. */
1745
0
    if (stream->datagram_flow_id != UINT64_MAX) {
1746
0
        struct st_h2o_http3_server_conn_t *conn = get_conn(stream);
1747
0
        int r;
1748
0
        khiter_t iter = kh_put(stream, conn->datagram_flows, stream->datagram_flow_id, &r);
1749
0
        assert(iter != kh_end(conn->datagram_flows));
1750
0
        kh_val(conn->datagram_flows, iter) = stream;
1751
0
    }
1752
1753
    /* If the client sent a `datagram-flow-id` request header field and:
1754
     *  a) if the peer is willing to accept datagrams as well, use the same flow ID for sending datagrams from us,
1755
     *  b) if the peer did not send H3_DATAGRAM Settings, use the stream, or
1756
     *  c) if H3 SETTINGS hasn't been received yet, wait for it, then call `do_send` again. We might drop some packets from origin
1757
     *     that arrive before H3 SETTINGS from the client, in the rare occasion of packet carrying H3 SETTINGS getting lost while
1758
     *     those carrying CONNECT-UDP request and the UDP datagram to be forwarded to the origin arrive. */
1759
0
    if (stream->datagram_flow_id != UINT64_MAX) {
1760
0
        struct st_h2o_http3_server_conn_t *conn = get_conn(stream);
1761
0
        if (!h2o_http3_has_received_settings(&conn->h3)) {
1762
0
            h2o_linklist_insert(&conn->streams_resp_settings_blocked, &stream->link_resp_settings_blocked);
1763
0
            return 0;
1764
0
        }
1765
0
        if (conn->h3.peer_settings.h3_datagram) {
1766
            /* register the route that would be used by the CONNECT handler for forwarding datagrams */
1767
0
            stream->req.forward_datagram.read_ = tunnel_on_udp_read;
1768
            /* if the request type is draft-03, build and return the value of datagram-flow-id header field */
1769
0
            if (stream->req.input.method.len == sizeof("CONNECT-UDP") - 1) {
1770
0
                datagram_flow_id->base = h2o_mem_alloc_pool(&stream->req.pool, char, sizeof(H2O_UINT64_LONGEST_STR));
1771
0
                datagram_flow_id->len = sprintf(datagram_flow_id->base, "%" PRIu64, stream->datagram_flow_id);
1772
0
            }
1773
0
        }
1774
0
    }
1775
1776
0
    return 1;
1777
0
}
1778
1779
static void finalize_do_send(struct st_h2o_http3_server_stream_t *stream)
1780
1.96k
{
1781
1.96k
    quicly_stream_sync_sendbuf(stream->quic, 1);
1782
1.96k
    if (!stream->proceed_while_sending)
1783
1.59k
        h2o_quic_schedule_timer(&get_conn(stream)->h3.super);
1784
1.96k
}
1785
1786
static void do_send(h2o_ostream_t *_ostr, h2o_req_t *_req, h2o_sendvec_t *bufs, size_t bufcnt, h2o_send_state_t send_state)
1787
1.96k
{
1788
1.96k
    struct st_h2o_http3_server_stream_t *stream = H2O_STRUCT_FROM_MEMBER(struct st_h2o_http3_server_stream_t, ostr_final, _ostr);
1789
1.96k
    int empty_payload_allowed =
1790
1.96k
        stream->state == H2O_HTTP3_SERVER_STREAM_STATE_SEND_HEADERS || send_state != H2O_SEND_STATE_IN_PROGRESS;
1791
1792
1.96k
    assert(&stream->req == _req);
1793
1794
1.96k
    stream->proceed_requested = 0;
1795
1796
1.96k
    switch (stream->state) {
1797
1.59k
    case H2O_HTTP3_SERVER_STREAM_STATE_SEND_HEADERS: {
1798
1.59k
        h2o_iovec_t datagram_flow_id;
1799
1.59k
        ssize_t priority_header_index;
1800
1.59k
        if (stream->req.send_server_timing != 0)
1801
0
            h2o_add_server_timing_header(&stream->req, 0 /* TODO add support for trailers; it's going to be a little complex as we
1802
0
                                                          * need to build trailers the moment they are emitted onto wire */);
1803
1.59k
        if (!finalize_do_send_setup_udp_tunnel(stream, send_state, &datagram_flow_id))
1804
0
            return;
1805
1.59k
        stream->req.timestamps.response_start_at = h2o_gettimeofday(get_conn(stream)->super.ctx->loop);
1806
1.59k
        write_response(stream, datagram_flow_id);
1807
1.59k
        h2o_probe_log_response(&stream->req, stream->quic->stream_id);
1808
1.59k
        set_state(stream, H2O_HTTP3_SERVER_STREAM_STATE_SEND_BODY, 1);
1809
1.59k
        if ((priority_header_index = h2o_find_header(&stream->req.res.headers, H2O_TOKEN_PRIORITY, -1)) != -1) {
1810
0
            const h2o_header_t *header = &stream->req.res.headers.entries[priority_header_index];
1811
0
            handle_priority_change(
1812
0
                stream, header->value.base, header->value.len,
1813
0
                stream->scheduler.priority /* omission of a parameter is disinterest to change (RFC 9218 Section 8) */);
1814
0
        }
1815
1.59k
        break;
1816
1.59k
    }
1817
368
    case H2O_HTTP3_SERVER_STREAM_STATE_SEND_BODY:
1818
368
        assert(quicly_sendstate_is_open(&stream->quic->sendstate));
1819
368
        break;
1820
368
    case H2O_HTTP3_SERVER_STREAM_STATE_CLOSE_WAIT:
1821
        /* This protocol handler transitions to CLOSE_WAIT when the request side is being reset by the origin. But our client-side
1822
         * implementations are capable of handling uni-directional close, therefore `do_send` might be invoked. The handler swallows
1823
         * the input, and relies on eventual destruction of `h2o_req_t` to discard the generator. */
1824
0
        return;
1825
0
    default:
1826
0
        h2o_fatal("logic flaw");
1827
0
        break;
1828
1.96k
    }
1829
1830
    /* If vectors carrying response body are being provided, copy them, incrementing the reference count if possible (for future
1831
     * retransmissions), as well as prepending a DATA frame header */
1832
1.96k
    h2o_vector_reserve(&stream->req.pool, &stream->sendbuf.vecs, stream->sendbuf.vecs.size + 1 + bufcnt);
1833
1.96k
    size_t dst_slot = stream->sendbuf.vecs.size + 1 /* reserve slot for DATA frame header */, payload_size = 0;
1834
3.42k
    for (size_t i = 0; i != bufcnt; ++i) {
1835
1.46k
        if (bufs[i].len == 0)
1836
0
            continue;
1837
        /* copy one body vector */
1838
1.46k
        payload_size += bufs[i].len;
1839
1.46k
        stream->sendbuf.vecs.entries[dst_slot++] = (struct st_h2o_http3_server_sendvec_t){
1840
1.46k
            .vec = bufs[i],
1841
1.46k
            .entity_offset = stream->sendbuf.final_body_size,
1842
1.46k
        };
1843
1.46k
    }
1844
1.96k
    if (payload_size != 0) {
1845
        /* build DATA frame header */
1846
1.46k
        size_t header_size =
1847
1.46k
            flatten_data_frame_header(stream, stream->sendbuf.vecs.entries + stream->sendbuf.vecs.size, payload_size);
1848
        /* update properties */
1849
1.46k
        stream->sendbuf.vecs.size = dst_slot;
1850
1.46k
        stream->sendbuf.final_body_size += payload_size;
1851
1.46k
        stream->sendbuf.final_size += header_size + payload_size;
1852
1.46k
    } else {
1853
500
        assert(empty_payload_allowed || !"h2o_data must only be called when there is progress");
1854
500
    }
1855
1856
1.96k
    switch (send_state) {
1857
368
    case H2O_SEND_STATE_IN_PROGRESS:
1858
368
        break;
1859
1.59k
    case H2O_SEND_STATE_FINAL:
1860
1.59k
    case H2O_SEND_STATE_ERROR:
1861
        /* TODO consider how to forward error, pending resolution of https://github.com/quicwg/base-drafts/issues/3300 */
1862
1.59k
        shutdown_by_generator(stream);
1863
1.59k
        break;
1864
1.96k
    }
1865
1866
1.96k
    finalize_do_send(stream);
1867
1.96k
}
1868
1869
static void do_send_informational(h2o_ostream_t *_ostr, h2o_req_t *_req)
1870
0
{
1871
0
    struct st_h2o_http3_server_stream_t *stream = H2O_STRUCT_FROM_MEMBER(struct st_h2o_http3_server_stream_t, ostr_final, _ostr);
1872
0
    assert(&stream->req == _req);
1873
1874
0
    write_response(stream, h2o_iovec_init(NULL, 0));
1875
1876
0
    finalize_do_send(stream);
1877
0
}
1878
1879
static quicly_error_t handle_priority_update_frame(struct st_h2o_http3_server_conn_t *conn,
1880
                                                   const h2o_http3_priority_update_frame_t *frame)
1881
0
{
1882
0
    if (frame->element_is_push)
1883
0
        return H2O_HTTP3_ERROR_GENERAL_PROTOCOL;
1884
1885
    /* obtain the stream being referred to (creating one if necessary), or return if the stream has been closed already */
1886
0
    quicly_stream_t *qs;
1887
0
    if (quicly_get_or_open_stream(conn->h3.super.quic, frame->element, &qs) != 0)
1888
0
        return H2O_HTTP3_ERROR_ID;
1889
0
    if (qs == NULL)
1890
0
        return 0;
1891
1892
    /* apply the changes */
1893
0
    struct st_h2o_http3_server_stream_t *stream = qs->data;
1894
0
    assert(stream != NULL);
1895
0
    stream->received_priority_update = 1;
1896
1897
0
    handle_priority_change(stream, frame->value.base, frame->value.len,
1898
0
                           h2o_absprio_default /* the frame communicates a complete set of parameters; RFC 9218 Section 7 */);
1899
1900
0
    return 0;
1901
0
}
1902
1903
static void handle_control_stream_frame(h2o_http3_conn_t *_conn, uint64_t type, const uint8_t *payload, size_t len)
1904
0
{
1905
0
    struct st_h2o_http3_server_conn_t *conn = H2O_STRUCT_FROM_MEMBER(struct st_h2o_http3_server_conn_t, h3, _conn);
1906
0
    quicly_error_t err;
1907
0
    const char *err_desc = NULL;
1908
1909
0
    if (!h2o_http3_has_received_settings(&conn->h3)) {
1910
0
        if (type != H2O_HTTP3_FRAME_TYPE_SETTINGS) {
1911
0
            err = H2O_HTTP3_ERROR_MISSING_SETTINGS;
1912
0
            goto Fail;
1913
0
        }
1914
0
        if ((err = h2o_http3_handle_settings_frame(&conn->h3, payload, len, &err_desc)) != 0)
1915
0
            goto Fail;
1916
0
        assert(h2o_http3_has_received_settings(&conn->h3));
1917
0
        while (!h2o_linklist_is_empty(&conn->streams_resp_settings_blocked)) {
1918
0
            struct st_h2o_http3_server_stream_t *stream = H2O_STRUCT_FROM_MEMBER(
1919
0
                struct st_h2o_http3_server_stream_t, link_resp_settings_blocked, conn->streams_resp_settings_blocked.next);
1920
0
            h2o_linklist_unlink(&stream->link_resp_settings_blocked);
1921
0
            do_send(&stream->ostr_final, &stream->req, NULL, 0, H2O_SEND_STATE_IN_PROGRESS);
1922
0
        }
1923
0
    } else {
1924
0
        switch (type) {
1925
0
        case H2O_HTTP3_FRAME_TYPE_SETTINGS:
1926
0
            err = H2O_HTTP3_ERROR_FRAME_UNEXPECTED;
1927
0
            err_desc = "unexpected SETTINGS frame";
1928
0
            goto Fail;
1929
0
        case H2O_HTTP3_FRAME_TYPE_PRIORITY_UPDATE_REQUEST:
1930
0
        case H2O_HTTP3_FRAME_TYPE_PRIORITY_UPDATE_PUSH: {
1931
0
            h2o_http3_priority_update_frame_t frame;
1932
0
            if ((err = h2o_http3_decode_priority_update_frame(&frame, type == H2O_HTTP3_FRAME_TYPE_PRIORITY_UPDATE_PUSH, payload,
1933
0
                                                              len, &err_desc)) != 0)
1934
0
                goto Fail;
1935
0
            if ((err = handle_priority_update_frame(conn, &frame)) != 0) {
1936
0
                err_desc = "invalid PRIORITY_UPDATE frame";
1937
0
                goto Fail;
1938
0
            }
1939
0
        } break;
1940
0
        default:
1941
0
            break;
1942
0
        }
1943
0
    }
1944
1945
0
    return;
1946
0
Fail:
1947
0
    h2o_quic_close_connection(&conn->h3.super, err, err_desc);
1948
0
}
1949
1950
static void qpack_unblock_streams(h2o_http3_conn_t *_conn, uint64_t insert_count)
1951
0
{
1952
0
    struct st_h2o_http3_server_conn_t *conn = H2O_STRUCT_FROM_MEMBER(struct st_h2o_http3_server_conn_t, h3, _conn);
1953
1954
0
    h2o_linklist_t *node = conn->delayed_streams.qpack_blocked.next;
1955
0
    while (node != &conn->delayed_streams.qpack_blocked) {
1956
0
        struct st_h2o_http3_server_stream_t *stream = H2O_STRUCT_FROM_MEMBER(struct st_h2o_http3_server_stream_t, link, node);
1957
0
        node = node->next;
1958
0
        if (stream->qpack_blocked_ref > insert_count)
1959
0
            continue;
1960
0
        h2o_linklist_unlink(&stream->link);
1961
0
        stream->qpack_blocked_ref = 0;
1962
0
        assert(conn->num_qpack_blocked > 0);
1963
0
        --conn->num_qpack_blocked;
1964
0
        handle_buffered_input(stream, 0);
1965
0
    }
1966
0
}
1967
1968
static quicly_error_t stream_open_cb(quicly_stream_open_t *self, quicly_stream_t *qs)
1969
25.1k
{
1970
25.1k
    static const quicly_stream_callbacks_t callbacks = {on_stream_destroy, on_send_shift, on_send_emit,
1971
25.1k
                                                        on_send_stop,      on_receive,    on_receive_reset};
1972
1973
    /* handling of unidirectional streams is not server-specific */
1974
25.1k
    if (quicly_stream_is_unidirectional(qs->stream_id)) {
1975
18.8k
        h2o_http3_on_create_unidirectional_stream(qs);
1976
18.8k
        return 0;
1977
18.8k
    }
1978
1979
25.1k
    assert(quicly_stream_is_client_initiated(qs->stream_id));
1980
1981
6.28k
    struct st_h2o_http3_server_conn_t *conn =
1982
6.28k
        H2O_STRUCT_FROM_MEMBER(struct st_h2o_http3_server_conn_t, h3, *quicly_get_data(qs->conn));
1983
1984
    /* create new stream and start handling the request */
1985
6.28k
    struct st_h2o_http3_server_stream_t *stream = h2o_mem_alloc(sizeof(*stream));
1986
6.28k
    stream->quic = qs;
1987
6.28k
    h2o_buffer_init(&stream->recvbuf.buf, &h2o_socket_buffer_prototype);
1988
6.28k
    stream->recvbuf.handle_input = handle_input_expect_headers;
1989
6.28k
    memset(&stream->sendbuf, 0, sizeof(stream->sendbuf));
1990
6.28k
    stream->state = H2O_HTTP3_SERVER_STREAM_STATE_RECV_HEADERS;
1991
6.28k
    stream->link = (h2o_linklist_t){NULL};
1992
6.28k
    stream->link_resp_settings_blocked = (h2o_linklist_t){NULL};
1993
6.28k
    stream->ostr_final = (h2o_ostream_t){
1994
6.28k
        NULL, do_send, NULL,
1995
6.28k
        conn->super.ctx->globalconf->send_informational_mode == H2O_SEND_INFORMATIONAL_MODE_NONE ? NULL : do_send_informational};
1996
6.28k
    stream->scheduler.link = (h2o_linklist_t){NULL};
1997
6.28k
    stream->scheduler.priority = h2o_absprio_default;
1998
6.28k
    stream->scheduler.call_cnt = 0;
1999
2000
6.28k
    stream->read_blocked = 0;
2001
6.28k
    stream->proceed_requested = 0;
2002
6.28k
    stream->proceed_while_sending = 0;
2003
6.28k
    stream->received_priority_update = 0;
2004
6.28k
    stream->req_disposed = 0;
2005
6.28k
    stream->req_streaming = 0;
2006
6.28k
    stream->qpack_blocked_ref = 0;
2007
6.28k
    stream->qpack_blocked_ever = 0;
2008
6.28k
    stream->req_streaming_eos_delivered = 0;
2009
6.28k
    stream->req_body = NULL;
2010
6.28k
    memset(&stream->stats, 0, sizeof(stream->stats));
2011
2012
6.28k
    h2o_init_request(&stream->req, &conn->super, NULL);
2013
6.28k
    stream->req.version = 0x0300;
2014
6.28k
    stream->req._ostr_top = &stream->ostr_final;
2015
2016
6.28k
    stream->quic->data = stream;
2017
6.28k
    stream->quic->callbacks = &callbacks;
2018
2019
6.28k
    ++*get_state_counter(get_conn(stream), stream->state);
2020
6.28k
    h2o_conn_set_state(&get_conn(stream)->super, H2O_CONN_STATE_ACTIVE);
2021
2022
6.28k
    return 0;
2023
6.28k
}
2024
2025
static quicly_stream_open_t on_stream_open = {stream_open_cb};
2026
2027
static void unblock_conn_blocked_streams(struct st_h2o_http3_server_conn_t *conn)
2028
0
{
2029
0
    conn->scheduler.uni.active |= conn->scheduler.uni.conn_blocked;
2030
0
    conn->scheduler.uni.conn_blocked = 0;
2031
0
    req_scheduler_unblock_conn_blocked(&conn->scheduler.reqs, req_scheduler_compare_stream_id);
2032
0
}
2033
2034
static int scheduler_can_send(quicly_stream_scheduler_t *sched, quicly_conn_t *qc, int conn_is_saturated)
2035
0
{
2036
0
    struct st_h2o_http3_server_conn_t *conn = H2O_STRUCT_FROM_MEMBER(struct st_h2o_http3_server_conn_t, h3, *quicly_get_data(qc));
2037
2038
0
    if (!conn_is_saturated) {
2039
        /* not saturated, activate streams marked as being conn-blocked */
2040
0
        unblock_conn_blocked_streams(conn);
2041
0
    } else {
2042
        /* TODO lazily move the active request and unidirectional streams to conn_blocked.  Not doing so results in at most one
2043
         * spurious call to quicly_send. */
2044
0
    }
2045
2046
0
    if (conn->scheduler.uni.active != 0)
2047
0
        return 1;
2048
0
    if (conn->scheduler.reqs.active.smallest_urgency < H2O_ABSPRIO_NUM_URGENCY_LEVELS)
2049
0
        return 1;
2050
2051
0
    return 0;
2052
0
}
2053
2054
static quicly_error_t scheduler_do_send(quicly_stream_scheduler_t *sched, quicly_conn_t *qc, quicly_send_context_t *s)
2055
17.6k
{
2056
17.6k
#define HAS_DATA_TO_SEND()                                                                                                         \
2057
26.1k
    (conn->scheduler.uni.active != 0 || conn->scheduler.reqs.active.smallest_urgency < H2O_ABSPRIO_NUM_URGENCY_LEVELS)
2058
2059
17.6k
    struct st_h2o_http3_server_conn_t *conn = H2O_STRUCT_FROM_MEMBER(struct st_h2o_http3_server_conn_t, h3, *quicly_get_data(qc));
2060
17.6k
    int had_data_to_send = HAS_DATA_TO_SEND();
2061
17.6k
    quicly_error_t ret = 0;
2062
2063
39.1k
    while (quicly_can_send_data(conn->h3.super.quic, s)) {
2064
        /* The strategy is:
2065
         *
2066
         * 1. dequeue the first active stream
2067
         * 2. link the stream to the conn_blocked list, if nothing can be sent for the stream due to the connection being capped
2068
         * 3. otherwise, send
2069
         * 4. enqueue to the appropriate place
2070
         */
2071
39.1k
        if (conn->scheduler.uni.active != 0) {
2072
19.8k
            static const ptrdiff_t stream_offsets[] = {
2073
19.8k
                offsetof(struct st_h2o_http3_server_conn_t, h3._control_streams.egress.control),
2074
19.8k
                offsetof(struct st_h2o_http3_server_conn_t, h3._control_streams.egress.qpack_encoder),
2075
19.8k
                offsetof(struct st_h2o_http3_server_conn_t, h3._control_streams.egress.qpack_decoder)};
2076
            /* 1. obtain pointer to the offending stream */
2077
19.8k
            struct st_h2o_http3_egress_unistream_t *stream = NULL;
2078
19.8k
            size_t i;
2079
40.7k
            for (i = 0; i != sizeof(stream_offsets) / sizeof(stream_offsets[0]); ++i) {
2080
40.7k
                stream = *(void **)((char *)conn + stream_offsets[i]);
2081
40.7k
                if ((conn->scheduler.uni.active & (1 << stream->quic->stream_id)) != 0)
2082
19.8k
                    break;
2083
40.7k
            }
2084
19.8k
            assert(i != sizeof(stream_offsets) / sizeof(stream_offsets[0]) && "we should have found one stream");
2085
            /* 2. move to the conn_blocked list if necessary */
2086
19.8k
            if (quicly_is_blocked(conn->h3.super.quic) && !quicly_stream_can_send(stream->quic, 0)) {
2087
0
                conn->scheduler.uni.active &= ~(1 << stream->quic->stream_id);
2088
0
                conn->scheduler.uni.conn_blocked |= 1 << stream->quic->stream_id;
2089
0
                continue;
2090
0
            }
2091
            /* 3. send */
2092
19.8k
            if ((ret = quicly_send_stream(stream->quic, s)) != 0)
2093
0
                goto Exit;
2094
            /* 4. update scheduler state */
2095
19.8k
            conn->scheduler.uni.active &= ~(1 << stream->quic->stream_id);
2096
19.8k
            if (quicly_stream_can_send(stream->quic, 1)) {
2097
0
                uint16_t *slot = &conn->scheduler.uni.active;
2098
0
                if (quicly_is_blocked(conn->h3.super.quic) && !quicly_stream_can_send(stream->quic, 0))
2099
0
                    slot = &conn->scheduler.uni.conn_blocked;
2100
0
                *slot |= 1 << stream->quic->stream_id;
2101
0
            }
2102
19.8k
        } else if (conn->scheduler.reqs.active.smallest_urgency < H2O_ABSPRIO_NUM_URGENCY_LEVELS) {
2103
            /* 1. obtain pointer to the offending stream */
2104
1.61k
            h2o_linklist_t *anchor = &conn->scheduler.reqs.active.urgencies[conn->scheduler.reqs.active.smallest_urgency].high;
2105
1.61k
            if (h2o_linklist_is_empty(anchor)) {
2106
4
                anchor = &conn->scheduler.reqs.active.urgencies[conn->scheduler.reqs.active.smallest_urgency].low;
2107
4
                assert(!h2o_linklist_is_empty(anchor));
2108
4
            }
2109
1.61k
            struct st_h2o_http3_server_stream_t *stream =
2110
1.61k
                H2O_STRUCT_FROM_MEMBER(struct st_h2o_http3_server_stream_t, scheduler.link, anchor->next);
2111
            /* 1. link to the conn_blocked list if necessary */
2112
1.61k
            if (quicly_is_blocked(conn->h3.super.quic) && !quicly_stream_can_send(stream->quic, 0)) {
2113
0
                req_scheduler_conn_blocked(&conn->scheduler.reqs, &stream->scheduler);
2114
0
                continue;
2115
0
            }
2116
            /* 3. send */
2117
1.61k
            if ((ret = quicly_send_stream(stream->quic, s)) != 0)
2118
0
                goto Exit;
2119
1.61k
            ++stream->scheduler.call_cnt;
2120
1.61k
            if (stream->quic->sendstate.size_inflight == stream->quic->sendstate.final_size &&
2121
1.24k
                h2o_timeval_is_null(&stream->req.timestamps.response_end_at)) {
2122
1.24k
                stream->req.timestamps.response_end_at = h2o_gettimeofday(stream->req.conn->ctx->loop);
2123
1.24k
                if (h2o_timeval_is_null(&stream->req.timestamps.response_start_at)) {
2124
0
                    stream->req.timestamps.response_start_at = stream->req.timestamps.response_end_at;
2125
0
                }
2126
1.24k
            }
2127
            /* 4. invoke h2o_proceed_request synchronously, so that we could obtain additional data for the current (i.e. highest)
2128
             *    stream. */
2129
1.61k
            if (stream->proceed_while_sending) {
2130
368
                assert(stream->proceed_requested);
2131
368
                h2o_proceed_response(&stream->req);
2132
368
                stream->proceed_while_sending = 0;
2133
368
            }
2134
            /* 5. prepare for next */
2135
1.61k
            if (quicly_stream_can_send(stream->quic, 1)) {
2136
368
                if (quicly_is_blocked(conn->h3.super.quic) && !quicly_stream_can_send(stream->quic, 0)) {
2137
                    /* capped by connection-level flow control, move the stream to conn-blocked */
2138
0
                    req_scheduler_conn_blocked(&conn->scheduler.reqs, &stream->scheduler);
2139
368
                } else {
2140
                    /* schedule for next emission */
2141
368
                    req_scheduler_setup_for_next(&conn->scheduler.reqs, &stream->scheduler, req_scheduler_compare_stream_id);
2142
368
                }
2143
1.24k
            } else {
2144
                /* nothing to send at this moment */
2145
1.24k
                req_scheduler_deactivate(&conn->scheduler.reqs, &stream->scheduler);
2146
1.24k
            }
2147
17.6k
        } else {
2148
17.6k
            break;
2149
17.6k
        }
2150
39.1k
    }
2151
2152
17.6k
Exit:
2153
    /* Send a resumption token if we've sent all available data and there is still room to send something, but not too frequently.
2154
     * We send a token every 200KB at most; the threshold has been chosen so that the additional overhead would be ~0.1% assuming a
2155
     * token size of 200 bytes (in reality, one NEW_TOKEN frame will uses 56 bytes). */
2156
17.6k
    if (ret == 0 && had_data_to_send && !HAS_DATA_TO_SEND()) {
2157
8.53k
        uint64_t max_data_sent;
2158
8.53k
        quicly_get_max_data(conn->h3.super.quic, NULL, &max_data_sent, NULL, NULL);
2159
8.53k
        if (max_data_sent >= conn->skip_jumpstart_token_until) {
2160
6.28k
            quicly_send_resumption_token(conn->h3.super.quic);
2161
6.28k
            conn->skip_jumpstart_token_until = max_data_sent + 200000;
2162
6.28k
        }
2163
8.53k
    }
2164
2165
17.6k
    return ret;
2166
2167
17.6k
#undef HAS_DATA_TO_SEND
2168
17.6k
}
2169
2170
static void scheduler_update_state(struct st_quicly_stream_scheduler_t *sched, quicly_stream_t *qs)
2171
23.6k
{
2172
23.6k
    struct st_h2o_http3_server_conn_t *conn =
2173
23.6k
        H2O_STRUCT_FROM_MEMBER(struct st_h2o_http3_server_conn_t, h3, *quicly_get_data(qs->conn));
2174
23.6k
    enum { DEACTIVATE, ACTIVATE, CONN_BLOCKED } new_state;
2175
2176
23.6k
    if (quicly_stream_can_send(qs, 1)) {
2177
22.7k
        if (quicly_is_blocked(conn->h3.super.quic) && !quicly_stream_can_send(qs, 0)) {
2178
0
            new_state = CONN_BLOCKED;
2179
22.7k
        } else {
2180
22.7k
            new_state = ACTIVATE;
2181
22.7k
        }
2182
22.7k
    } else {
2183
996
        new_state = DEACTIVATE;
2184
996
    }
2185
2186
23.6k
    if (quicly_stream_is_unidirectional(qs->stream_id)) {
2187
20.7k
        assert(qs->stream_id < sizeof(uint16_t) * 8);
2188
20.7k
        uint16_t mask = (uint16_t)1 << qs->stream_id;
2189
20.7k
        switch (new_state) {
2190
0
        case DEACTIVATE:
2191
0
            conn->scheduler.uni.active &= ~mask;
2192
0
            conn->scheduler.uni.conn_blocked &= ~mask;
2193
0
            break;
2194
20.7k
        case ACTIVATE:
2195
20.7k
            conn->scheduler.uni.active |= mask;
2196
20.7k
            conn->scheduler.uni.conn_blocked &= ~mask;
2197
20.7k
            break;
2198
0
        case CONN_BLOCKED:
2199
0
            conn->scheduler.uni.active &= ~mask;
2200
0
            conn->scheduler.uni.conn_blocked |= mask;
2201
0
            break;
2202
20.7k
        }
2203
20.7k
    } else {
2204
2.95k
        struct st_h2o_http3_server_stream_t *stream = qs->data;
2205
2.95k
        if (stream->proceed_while_sending)
2206
368
            return;
2207
2.58k
        switch (new_state) {
2208
996
        case DEACTIVATE:
2209
996
            req_scheduler_deactivate(&conn->scheduler.reqs, &stream->scheduler);
2210
996
            break;
2211
1.59k
        case ACTIVATE:
2212
1.59k
            req_scheduler_activate(&conn->scheduler.reqs, &stream->scheduler, req_scheduler_compare_stream_id);
2213
1.59k
            break;
2214
0
        case CONN_BLOCKED:
2215
0
            req_scheduler_conn_blocked(&conn->scheduler.reqs, &stream->scheduler);
2216
0
            break;
2217
2.58k
        }
2218
2.58k
    }
2219
23.6k
}
2220
2221
static quicly_stream_scheduler_t scheduler = {scheduler_can_send, scheduler_do_send, scheduler_update_state};
2222
2223
static void datagram_frame_receive_cb(quicly_receive_datagram_frame_t *self, quicly_conn_t *quic, ptls_iovec_t datagram)
2224
0
{
2225
0
    struct st_h2o_http3_server_conn_t *conn = H2O_STRUCT_FROM_MEMBER(struct st_h2o_http3_server_conn_t, h3, *quicly_get_data(quic));
2226
0
    uint64_t flow_id;
2227
0
    h2o_iovec_t payload;
2228
2229
    /* decode */
2230
0
    if ((flow_id = h2o_http3_decode_h3_datagram(&payload, datagram.base, datagram.len)) == UINT64_MAX) {
2231
0
        h2o_quic_close_connection(&conn->h3.super, H2O_HTTP3_ERROR_GENERAL_PROTOCOL, "invalid DATAGRAM frame");
2232
0
        return;
2233
0
    }
2234
2235
    /* find stream */
2236
0
    khiter_t iter = kh_get(stream, conn->datagram_flows, flow_id);
2237
0
    if (iter == kh_end(conn->datagram_flows))
2238
0
        return;
2239
0
    struct st_h2o_http3_server_stream_t *stream = kh_val(conn->datagram_flows, iter);
2240
0
    assert(stream->req.forward_datagram.write_ != NULL);
2241
2242
    /* drop this datagram if req.forward_datagram.write_ has been reset */
2243
0
    if (stream->req.forward_datagram.write_ == NULL)
2244
0
        return;
2245
2246
    /* forward */
2247
0
    stream->req.forward_datagram.write_(&stream->req, &payload, 1);
2248
0
}
2249
2250
static quicly_receive_datagram_frame_t on_receive_datagram_frame = {datagram_frame_receive_cb};
2251
2252
static void on_h3_destroy(h2o_quic_conn_t *h3_)
2253
6.28k
{
2254
6.28k
    h2o_http3_conn_t *h3 = (h2o_http3_conn_t *)h3_;
2255
6.28k
    struct st_h2o_http3_server_conn_t *conn = H2O_STRUCT_FROM_MEMBER(struct st_h2o_http3_server_conn_t, h3, h3);
2256
6.28k
    quicly_stats_t stats;
2257
2258
    /* some attributes are available only via h2olog, as DTrace probes have an upper limit on the number of arguments */
2259
6.28k
    H2O_PROBE_CONN(H3S_DESTROY, &conn->super, conn->stats.num_requests, conn->stats.req.stream_bytes,
2260
6.28k
                   conn->stats.req.headers_frame_bytes, conn->stats.req.body_bytes, conn->stats.req.qpack.count,
2261
6.28k
                   conn->stats.req.qpack.text_bytes, conn->stats.resp.stream_bytes, conn->stats.resp.headers_frame_bytes,
2262
6.28k
                   conn->stats.resp.body_bytes, conn->stats.resp.qpack.count, conn->stats.resp.qpack.text_bytes);
2263
6.28k
    H2O_LOG_CONN(h3s_destroy, &conn->super, {
2264
6.28k
        PTLS_LOG_ELEMENT_UNSIGNED(num_requests, conn->stats.num_requests);
2265
6.28k
        PTLS_LOG_ELEMENT_UNSIGNED(request_stream_bytes, conn->stats.req.stream_bytes);
2266
6.28k
        PTLS_LOG_ELEMENT_UNSIGNED(request_header_bytes, conn->stats.req.headers_frame_bytes);
2267
6.28k
        PTLS_LOG_ELEMENT_UNSIGNED(request_body_bytes, conn->stats.req.body_bytes);
2268
6.28k
        PTLS_LOG_ELEMENT_UNSIGNED(request_header_count, conn->stats.req.qpack.count);
2269
6.28k
        PTLS_LOG_ELEMENT_UNSIGNED(request_header_text_bytes, conn->stats.req.qpack.text_bytes);
2270
6.28k
        PTLS_LOG_ELEMENT_UNSIGNED(response_stream_bytes, conn->stats.resp.stream_bytes);
2271
6.28k
        PTLS_LOG_ELEMENT_UNSIGNED(response_header_bytes, conn->stats.resp.headers_frame_bytes);
2272
6.28k
        PTLS_LOG_ELEMENT_UNSIGNED(response_body_bytes, conn->stats.resp.body_bytes);
2273
6.28k
        PTLS_LOG_ELEMENT_UNSIGNED(response_header_count, conn->stats.resp.qpack.count);
2274
6.28k
        PTLS_LOG_ELEMENT_UNSIGNED(response_header_text_bytes, conn->stats.resp.qpack.text_bytes);
2275
6.28k
        PTLS_LOG_ELEMENT_UNSIGNED(control_stream_bytes_received, h3->stats.bytes_received.control_stream);
2276
6.28k
        PTLS_LOG_ELEMENT_UNSIGNED(qpack_encoder_bytes_received, h3->stats.bytes_received.qpack_encoder);
2277
6.28k
        PTLS_LOG_ELEMENT_UNSIGNED(qpack_decoder_bytes_received, h3->stats.bytes_received.qpack_decoder);
2278
6.28k
        PTLS_LOG_ELEMENT_UNSIGNED(control_stream_bytes_sent, h3->stats.bytes_sent.control_stream);
2279
6.28k
        PTLS_LOG_ELEMENT_UNSIGNED(qpack_encoder_bytes_sent, h3->stats.bytes_sent.qpack_encoder);
2280
6.28k
        PTLS_LOG_ELEMENT_UNSIGNED(qpack_decoder_bytes_sent, h3->stats.bytes_sent.qpack_decoder);
2281
6.28k
    });
2282
2283
6.28k
    if (quicly_get_stats(h3_->quic, &stats) == 0) {
2284
597k
#define ACC(fld, _unused) conn->super.ctx->quic_stats.quicly.fld += stats.fld;
2285
597k
        QUICLY_STATS_FOREACH_COUNTERS(ACC);
2286
6.28k
#undef ACC
2287
6.28k
        if (conn->super.ctx->quic_stats.num_sentmap_packets_largest < stats.num_sentmap_packets_largest)
2288
0
            conn->super.ctx->quic_stats.num_sentmap_packets_largest = stats.num_sentmap_packets_largest;
2289
6.28k
    }
2290
2291
    /* unlink and dispose */
2292
6.28k
    if (h2o_timer_is_linked(&conn->timeout))
2293
0
        h2o_timer_unlink(&conn->timeout);
2294
6.28k
    if (h2o_timer_is_linked(&conn->_graceful_shutdown_timeout))
2295
0
        h2o_timer_unlink(&conn->_graceful_shutdown_timeout);
2296
6.28k
    h2o_http3_dispose_conn(&conn->h3);
2297
6.28k
    kh_destroy(stream, conn->datagram_flows);
2298
2299
    /* check consistency post-disposal */
2300
6.28k
    assert(conn->num_streams.recv_headers == 0);
2301
6.28k
    assert(conn->num_streams.req_pending == 0);
2302
6.28k
    assert(conn->num_streams.send_headers == 0);
2303
6.28k
    assert(conn->num_streams.send_body == 0);
2304
6.28k
    assert(conn->num_streams.close_wait == 0);
2305
6.28k
    assert(conn->num_streams_req_streaming == 0);
2306
6.28k
    assert(conn->num_streams_tunnelling == 0);
2307
6.28k
    assert(h2o_linklist_is_empty(&conn->delayed_streams.recv_body_blocked));
2308
6.28k
    assert(h2o_linklist_is_empty(&conn->delayed_streams.req_streaming));
2309
6.28k
    assert(h2o_linklist_is_empty(&conn->delayed_streams.pending));
2310
6.28k
    assert(h2o_linklist_is_empty(&conn->delayed_streams.qpack_blocked));
2311
6.28k
    assert(conn->num_qpack_blocked == 0);
2312
6.28k
    assert(h2o_linklist_is_empty(&conn->streams_resp_settings_blocked));
2313
6.28k
    assert(conn->scheduler.reqs.active.smallest_urgency >= H2O_ABSPRIO_NUM_URGENCY_LEVELS);
2314
6.28k
    assert(h2o_linklist_is_empty(&conn->scheduler.reqs.conn_blocked));
2315
2316
    /* free memory */
2317
6.28k
    h2o_destroy_connection(&conn->super);
2318
6.28k
}
2319
2320
void h2o_http3_server_init_context(h2o_context_t *h2o, h2o_quic_ctx_t *ctx, h2o_loop_t *loop, h2o_socket_t *sock,
2321
                                   h2o_socket_t *sock_alt_family, quicly_context_t *quic, quicly_cid_plaintext_t *next_cid,
2322
                                   h2o_quic_accept_cb acceptor, h2o_quic_notify_connection_update_cb notify_conn_update,
2323
                                   uint8_t use_gso)
2324
0
{
2325
0
    return h2o_quic_init_context(ctx, loop, sock, sock_alt_family, quic, next_cid, acceptor, notify_conn_update, use_gso,
2326
0
                                 &h2o->quic_stats);
2327
0
}
2328
2329
h2o_http3_conn_t *h2o_http3_server_accept(h2o_http3_server_ctx_t *ctx, quicly_address_t *destaddr, quicly_address_t *srcaddr,
2330
                                          quicly_decoded_packet_t *packet, quicly_address_token_plaintext_t *address_token,
2331
                                          const h2o_http3_conn_callbacks_t *h3_callbacks)
2332
6.28k
{
2333
6.28k
    static const h2o_conn_callbacks_t conn_callbacks = {
2334
6.28k
        .get_sockname = get_sockname,
2335
6.28k
        .get_peername = get_peername,
2336
6.28k
        .get_ptls = get_ptls,
2337
6.28k
        .get_ssl_server_name = get_ssl_server_name,
2338
6.28k
        .log_state = log_state,
2339
6.28k
        .get_req_id = get_req_id,
2340
6.28k
        .close_idle_connection = close_idle_connection,
2341
6.28k
        .foreach_request = foreach_request,
2342
6.28k
        .request_shutdown = initiate_graceful_shutdown,
2343
6.28k
        .num_reqs_inflight = num_reqs_inflight,
2344
6.28k
        .get_tracer = get_tracer,
2345
6.28k
        .log_ = {{
2346
6.28k
            .extensible_priorities = log_extensible_priorities,
2347
6.28k
            .request_header_bytes = log_request_header_bytes,
2348
6.28k
            .request_header_text_bytes = log_request_header_text_bytes,
2349
6.28k
            .request_header_count = log_request_header_count,
2350
6.28k
            .response_header_text_bytes = log_response_header_text_bytes,
2351
6.28k
            .response_header_count = log_response_header_count,
2352
6.28k
            .transport =
2353
6.28k
                {
2354
6.28k
                    .cc_name = log_cc_name,
2355
6.28k
                    .delivery_rate = log_delivery_rate,
2356
6.28k
                },
2357
6.28k
            .ssl =
2358
6.28k
                {
2359
6.28k
                    .protocol_version = log_tls_protocol_version,
2360
6.28k
                    .session_reused = log_session_reused,
2361
6.28k
                    .cipher = log_cipher,
2362
6.28k
                    .cipher_bits = log_cipher_bits,
2363
6.28k
                    .session_id = log_session_id,
2364
6.28k
                    .negotiated_protocol = log_negotiated_protocol,
2365
6.28k
                    .ech_config_id = log_ech_config_id,
2366
6.28k
                    .ech_kem = log_ech_kem,
2367
6.28k
                    .ech_cipher = log_ech_cipher,
2368
6.28k
                    .ech_cipher_bits = log_ech_cipher_bits,
2369
6.28k
                },
2370
6.28k
            .http3 =
2371
6.28k
                {
2372
6.28k
                    .stream_id = log_stream_id,
2373
6.28k
                    .quic_stats = log_quic_stats,
2374
6.28k
                    .quic_version = log_quic_version,
2375
6.28k
                    .qpack_blocked = log_qpack_blocked,
2376
6.28k
                    .request_stream_bytes = log_request_stream_bytes,
2377
6.28k
                    .response_stream_bytes = log_response_stream_bytes,
2378
6.28k
                },
2379
6.28k
        }},
2380
6.28k
    };
2381
2382
    /* setup the structure */
2383
6.28k
    struct st_h2o_http3_server_conn_t *conn = (void *)h2o_create_connection(
2384
6.28k
        sizeof(*conn), ctx->accept_ctx->ctx, ctx->accept_ctx->hosts, h2o_gettimeofday(ctx->accept_ctx->ctx->loop), &conn_callbacks);
2385
6.28k
    memset((char *)conn + sizeof(conn->super), 0, sizeof(*conn) - sizeof(conn->super));
2386
2387
6.28k
    h2o_http3_init_conn(&conn->h3, &ctx->super, h3_callbacks, &ctx->qpack, H2O_MAX_REQLEN);
2388
6.28k
    conn->handshake_properties = (ptls_handshake_properties_t){{{{NULL}}}};
2389
6.28k
    h2o_linklist_init_anchor(&conn->delayed_streams.recv_body_blocked);
2390
6.28k
    h2o_linklist_init_anchor(&conn->delayed_streams.req_streaming);
2391
6.28k
    h2o_linklist_init_anchor(&conn->delayed_streams.pending);
2392
6.28k
    h2o_linklist_init_anchor(&conn->delayed_streams.qpack_blocked);
2393
6.28k
    h2o_linklist_init_anchor(&conn->streams_resp_settings_blocked);
2394
6.28k
    h2o_timer_init(&conn->timeout, run_delayed);
2395
6.28k
    memset(&conn->num_streams, 0, sizeof(conn->num_streams));
2396
6.28k
    conn->num_streams_req_streaming = 0;
2397
6.28k
    conn->num_streams_tunnelling = 0;
2398
6.28k
    req_scheduler_init(&conn->scheduler.reqs);
2399
6.28k
    conn->scheduler.uni.active = 0;
2400
6.28k
    conn->scheduler.uni.conn_blocked = 0;
2401
6.28k
    conn->datagram_flows = kh_init(stream);
2402
6.28k
    conn->skip_jumpstart_token_until =
2403
6.28k
        quicly_cc_calc_initial_cwnd(ctx->super.quic->initcwnd_packets, ctx->super.quic->transport_params.max_udp_payload_size) *
2404
6.28k
        4; /* sending jumpstart token is meaningless until CWND has grown 2x of IW, which translates to 4x data being sent */
2405
2406
6.28k
    assert(ctx->super.next_cid != NULL && "to set next_cid, h2o_quic_set_context_identifier must be called");
2407
2408
    /* accept connection */
2409
6.28k
    ptls_log_conn_state_t log_state_override;
2410
6.28k
    ptls_log_init_conn_state(&log_state_override, ctx->super.quic->tls->random_bytes, conn->super.id, &srcaddr->sa);
2411
6.28k
    ptls_log_conn_state_override = &log_state_override;
2412
6.28k
    quicly_conn_t *qconn;
2413
6.28k
    quicly_error_t accept_ret = quicly_accept(
2414
6.28k
        &qconn, ctx->super.quic, &destaddr->sa, &srcaddr->sa, packet, address_token, ctx->super.next_cid,
2415
6.28k
        &conn->handshake_properties,
2416
6.28k
        &conn->h3 /* back pointer is set up here so that callbacks being called while parsing ClientHello can refer to `conn` */);
2417
6.28k
    ptls_log_conn_state_override = NULL;
2418
6.28k
    if (accept_ret != 0) {
2419
0
        h2o_http3_conn_t *ret = NULL;
2420
0
        if (accept_ret == QUICLY_ERROR_DECRYPTION_FAILED)
2421
0
            ret = (h2o_http3_conn_t *)&h2o_quic_accept_conn_decryption_failed;
2422
0
        h2o_http3_dispose_conn(&conn->h3);
2423
0
        kh_destroy(stream, conn->datagram_flows);
2424
0
        h2o_destroy_connection(&conn->super);
2425
0
        return ret;
2426
0
    }
2427
6.28k
    if (ctx->super.quic_stats != NULL) {
2428
0
        ++ctx->super.quic_stats->packet_processed;
2429
0
    }
2430
6.28k
    ++ctx->super.next_cid->master_id; /* FIXME check overlap */
2431
6.28k
    h2o_http3_setup(&conn->h3, qconn);
2432
2433
6.28k
    H2O_PROBE_CONN(H3S_ACCEPT, &conn->super, &conn->super, conn->h3.super.quic, h2o_conn_get_uuid(&conn->super));
2434
6.28k
    H2O_LOG_CONN(h3s_accept, &conn->super, {
2435
6.28k
        PTLS_LOG_ELEMENT_PTR(conn, &conn->super);
2436
6.28k
        PTLS_LOG_ELEMENT_PTR(quic, conn->h3.super.quic);
2437
6.28k
        PTLS_LOG_ELEMENT_SAFESTR(conn_uuid, h2o_conn_get_uuid(&conn->super));
2438
6.28k
    });
2439
2440
6.28k
    if (!h2o_quic_send(&conn->h3.super)) {
2441
        /* When `h2o_quic_send` fails, it destroys the connection object. */
2442
0
        return &h2o_http3_accept_conn_closed;
2443
0
    }
2444
2445
6.28k
    return &conn->h3;
2446
6.28k
}
2447
2448
void h2o_http3_server_amend_quicly_context(h2o_globalconf_t *conf, quicly_context_t *quic)
2449
1
{
2450
1
    quic->transport_params.max_data =
2451
1
        conf->http3.active_stream_window_size; /* set to a size that does not block the unblocked request stream */
2452
1
    quic->transport_params.max_streams_uni = 10;
2453
1
    quic->transport_params.max_stream_data.bidi_remote = h2o_http3_calc_min_flow_control_size(H2O_MAX_REQLEN);
2454
1
    quic->transport_params.max_stream_data.uni = h2o_http3_calc_min_flow_control_size(H2O_MAX_REQLEN);
2455
1
    quic->transport_params.max_idle_timeout = conf->http3.idle_timeout;
2456
1
    quic->transport_params.min_ack_delay_usec = conf->http3.allow_delayed_ack ? 0 : UINT64_MAX;
2457
1
    quic->ack_frequency = conf->http3.ack_frequency;
2458
1
    quic->transport_params.max_datagram_frame_size = 1500; /* accept DATAGRAM frames; let the sender determine MTU, instead of being
2459
                                                            * potentially too restrictive */
2460
1
    quic->stream_open = &on_stream_open;
2461
1
    quic->stream_scheduler = &scheduler;
2462
1
    quic->receive_datagram_frame = &on_receive_datagram_frame;
2463
2464
4
    for (size_t i = 0; quic->tls->cipher_suites[i] != NULL; ++i)
2465
3
        assert(quic->tls->cipher_suites[i]->aead->ctr_cipher != NULL &&
2466
1
               "for header protection, QUIC ciphers MUST provide CTR mode");
2467
1
}
2468
2469
h2o_conn_t *h2o_http3_get_connection(quicly_conn_t *quic)
2470
0
{
2471
0
    struct st_h2o_http3_server_conn_t *conn = H2O_STRUCT_FROM_MEMBER(struct st_h2o_http3_server_conn_t, h3, *quicly_get_data(quic));
2472
2473
    /* this assertion is most likely to fire if the provided QUIC connection does not represent a server-side HTTP connection */
2474
0
    assert(conn->h3.super.quic == NULL || conn->h3.super.quic == quic);
2475
2476
0
    return &conn->super;
2477
0
}
2478
2479
static void graceful_shutdown_close_straggler(h2o_timer_t *entry)
2480
0
{
2481
0
    struct st_h2o_http3_server_conn_t *conn =
2482
0
        H2O_STRUCT_FROM_MEMBER(struct st_h2o_http3_server_conn_t, _graceful_shutdown_timeout, entry);
2483
2484
    /* We've sent two GOAWAY frames, close the remaining connections */
2485
0
    h2o_quic_close_connection(&conn->h3.super, 0, "shutting down");
2486
2487
0
    conn->_graceful_shutdown_timeout.cb = NULL;
2488
0
}
2489
2490
static void graceful_shutdown_resend_goaway(h2o_timer_t *entry)
2491
0
{
2492
0
    struct st_h2o_http3_server_conn_t *conn =
2493
0
        H2O_STRUCT_FROM_MEMBER(struct st_h2o_http3_server_conn_t, _graceful_shutdown_timeout, entry);
2494
2495
    /* HTTP/3 draft section 5.2.8 --
2496
     * "After allowing time for any in-flight requests or pushes to arrive, the endpoint can send another GOAWAY frame
2497
     * indicating which requests or pushes it might accept before the end of the connection.
2498
     * This ensures that a connection can be cleanly shut down without losing requests. */
2499
2500
0
    if (conn->h3.state < H2O_HTTP3_CONN_STATE_HALF_CLOSED && quicly_get_state(conn->h3.super.quic) == QUICLY_STATE_CONNECTED) {
2501
0
        quicly_stream_id_t next_stream_id = quicly_get_remote_next_stream_id(conn->h3.super.quic, 0 /* == bidi */);
2502
        /* Section 5.2-1: "This identifier MAY be zero if no requests or pushes were processed."" */
2503
0
        quicly_stream_id_t max_stream_id = next_stream_id < 4 ? 0 /* we haven't received any stream yet */ : next_stream_id - 4;
2504
0
        h2o_http3_send_goaway_frame(&conn->h3, max_stream_id);
2505
0
        conn->h3.state = H2O_HTTP3_CONN_STATE_HALF_CLOSED;
2506
        /* After waiting a second, we still have an active connection. If configured, wait one
2507
         * final timeout before closing the connection */
2508
0
        if (conn->super.ctx->globalconf->http3.graceful_shutdown_timeout > 0) {
2509
0
            conn->_graceful_shutdown_timeout.cb = graceful_shutdown_close_straggler;
2510
0
            h2o_timer_link(conn->super.ctx->loop, conn->super.ctx->globalconf->http3.graceful_shutdown_timeout,
2511
0
                           &conn->_graceful_shutdown_timeout);
2512
0
        } else {
2513
0
            conn->_graceful_shutdown_timeout.cb = NULL;
2514
0
        }
2515
0
    }
2516
0
}
2517
2518
static void close_idle_connection(h2o_conn_t *_conn)
2519
0
{
2520
0
    initiate_graceful_shutdown(_conn);
2521
0
}
2522
2523
static void initiate_graceful_shutdown(h2o_conn_t *_conn)
2524
0
{
2525
0
    h2o_conn_set_state(_conn, H2O_CONN_STATE_SHUTDOWN);
2526
2527
0
    struct st_h2o_http3_server_conn_t *conn = (void *)_conn;
2528
0
    assert(conn->_graceful_shutdown_timeout.cb == NULL);
2529
0
    conn->_graceful_shutdown_timeout.cb = graceful_shutdown_resend_goaway;
2530
2531
0
    h2o_http3_send_shutdown_goaway_frame(&conn->h3);
2532
2533
0
    h2o_timer_link(conn->super.ctx->loop, 1000, &conn->_graceful_shutdown_timeout);
2534
0
}
2535
2536
struct foreach_request_ctx {
2537
    int (*cb)(h2o_req_t *req, void *cbdata);
2538
    void *cbdata;
2539
};
2540
2541
static int64_t foreach_request_per_conn(void *_ctx, quicly_stream_t *qs)
2542
0
{
2543
0
    struct foreach_request_ctx *ctx = _ctx;
2544
2545
    /* skip if the stream is not a request stream (TODO handle push?) */
2546
0
    if (!(quicly_stream_is_client_initiated(qs->stream_id) && !quicly_stream_is_unidirectional(qs->stream_id)))
2547
0
        return 0;
2548
2549
0
    struct st_h2o_http3_server_stream_t *stream = qs->data;
2550
0
    assert(stream->quic == qs);
2551
2552
0
    if (stream->state == H2O_HTTP3_SERVER_STREAM_STATE_CLOSE_WAIT)
2553
0
        return 0;
2554
0
    return ctx->cb(&stream->req, ctx->cbdata);
2555
0
}
2556
2557
static int foreach_request(h2o_conn_t *_conn, int (*cb)(h2o_req_t *req, void *cbdata), void *cbdata)
2558
0
{
2559
0
    struct foreach_request_ctx foreach_ctx = {.cb = cb, .cbdata = cbdata};
2560
2561
0
    struct st_h2o_http3_server_conn_t *conn = (void *)_conn;
2562
0
    quicly_foreach_stream(conn->h3.super.quic, &foreach_ctx, foreach_request_per_conn);
2563
0
    return 0;
2564
0
}
2565
2566
const h2o_http3_conn_callbacks_t H2O_HTTP3_CONN_CALLBACKS = {
2567
    {on_h3_destroy},
2568
    handle_control_stream_frame,
2569
    qpack_unblock_streams,
2570
};