Coverage Report

Created: 2025-07-18 06:42

/src/h2o/lib/common/http3client.c
Line
Count
Source (jump to first uncovered line)
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 <assert.h>
23
#include <errno.h>
24
#include <stdlib.h>
25
#include <sys/types.h>
26
#include "quicly.h"
27
#include "h2o.h"
28
#include "h2o/hostinfo.h"
29
#include "h2o/httpclient.h"
30
#include "h2o/http2_common.h"
31
#include "h2o/http3_common.h"
32
#include "h2o/http3_internal.h"
33
#include "../probes_.h"
34
35
/**
36
 * internal error code used for signalling EOS
37
 */
38
0
#define ERROR_EOS H2O_HTTP3_ERROR_USER1
39
/**
40
 * Maxmium amount of unsent bytes to be buffered when acting as a tunnel.
41
 */
42
#define TUNNEL_MAX_UNSENT 16384
43
44
struct st_h2o_http3client_req_t {
45
    /**
46
     * superclass
47
     */
48
    h2o_httpclient_t super;
49
    /**
50
     * pointer to the connection
51
     */
52
    struct st_h2o_httpclient__h3_conn_t *conn;
53
    /**
54
     * is NULL until connection is established
55
     */
56
    quicly_stream_t *quic;
57
    /**
58
     * currently only used for pending_requests
59
     */
60
    h2o_linklist_t link;
61
    /**
62
     *
63
     */
64
    uint64_t bytes_left_in_data_frame;
65
    /**
66
     *
67
     */
68
    h2o_buffer_t *sendbuf;
69
    /**
70
     *
71
     */
72
    struct {
73
        /**
74
         * HTTP-level buffer that contains (part of) response body received. Is the variable registered as `h2o_httpclient::buf`.
75
         */
76
        h2o_buffer_t *body;
77
        /**
78
         * QUIC stream-level buffer that contains bytes that have not yet been processed at the HTTP/3 framing decoding level. This
79
         * buffer may have gaps. The beginning offset of `partial_frame` is equal to `recvstate.data_off`.
80
         */
81
        h2o_buffer_t *stream;
82
        /**
83
         * Retains the amount of stream-level data that was available in the previous call. This value is used to see if processing
84
         * of new stream data is necessary.
85
         */
86
        size_t prev_bytes_available;
87
    } recvbuf;
88
    /**
89
     * called when new contigious data becomes available
90
     */
91
    quicly_error_t (*handle_input)(struct st_h2o_http3client_req_t *req, const uint8_t **src, const uint8_t *src_end,
92
                                   quicly_error_t err, const char **err_desc);
93
    /**
94
     * `proceed_req` callback. The callback is invoked when all bytes in the send buffer is emitted for the first time.
95
     * `bytes_inflight` contains the number of bytes being transmitted, or SIZE_MAX if nothing is inflight.
96
     */
97
    struct {
98
        h2o_httpclient_proceed_req_cb cb;
99
        size_t bytes_inflight;
100
    } proceed_req;
101
    /**
102
     *
103
     */
104
    enum {
105
        H2O_HTTP3CLIENT_RESPONSE_STATE_HEAD,
106
        H2O_HTTP3CLIENT_RESPONSE_STATE_BODY,
107
        H2O_HTTP3CLIENT_RESPONSE_STATE_CLOSED
108
    } response_state;
109
    /**
110
     * callback used for forwarding CONNECT-UDP using H3_DATAGRAMS
111
     */
112
    h2o_httpclient_forward_datagram_cb on_read_datagrams;
113
    /**
114
     * flags
115
     */
116
    unsigned offered_datagram_flow_id : 1;
117
};
118
119
static quicly_error_t handle_input_expect_data_frame(struct st_h2o_http3client_req_t *req, const uint8_t **src,
120
                                                     const uint8_t *src_end, quicly_error_t err, const char **err_desc);
121
static void start_request(struct st_h2o_http3client_req_t *req);
122
static int do_write_req(h2o_httpclient_t *_client, h2o_iovec_t chunk, int is_end_stream);
123
124
static size_t emit_data(struct st_h2o_http3client_req_t *req, h2o_iovec_t payload)
125
0
{
126
0
    size_t nbytes;
127
128
0
    { /* emit header */
129
0
        uint8_t buf[9], *p = buf;
130
0
        *p++ = H2O_HTTP3_FRAME_TYPE_DATA;
131
0
        p = quicly_encodev(p, payload.len);
132
0
        nbytes = p - buf;
133
0
        h2o_buffer_append(&req->sendbuf, buf, nbytes);
134
0
    }
135
136
    /* emit payload */
137
0
    h2o_buffer_append(&req->sendbuf, payload.base, payload.len);
138
0
    nbytes += payload.len;
139
140
0
    return nbytes;
141
0
}
142
143
static void destroy_request(struct st_h2o_http3client_req_t *req)
144
0
{
145
0
    assert(req->quic == NULL);
146
147
0
    h2o_buffer_dispose(&req->sendbuf);
148
0
    h2o_buffer_dispose(&req->recvbuf.body);
149
0
    h2o_buffer_dispose(&req->recvbuf.stream);
150
0
    if (h2o_timer_is_linked(&req->super._timeout))
151
0
        h2o_timer_unlink(&req->super._timeout);
152
0
    if (h2o_linklist_is_linked(&req->link))
153
0
        h2o_linklist_unlink(&req->link);
154
0
    free(req);
155
0
}
156
157
static void detach_stream(struct st_h2o_http3client_req_t *req)
158
0
{
159
0
    req->quic->callbacks = &quicly_stream_noop_callbacks;
160
0
    req->quic->data = NULL;
161
0
    req->quic = NULL;
162
0
}
163
164
static void close_stream(struct st_h2o_http3client_req_t *req, quicly_error_t err)
165
0
{
166
    /* TODO are we expected to send two error codes? */
167
0
    if (!quicly_sendstate_transfer_complete(&req->quic->sendstate))
168
0
        quicly_reset_stream(req->quic, err);
169
0
    if (!quicly_recvstate_transfer_complete(&req->quic->recvstate))
170
0
        quicly_request_stop(req->quic, err);
171
0
    detach_stream(req);
172
0
}
173
174
static void write_datagrams(h2o_httpclient_t *_client, h2o_iovec_t *datagrams, size_t num_datagrams)
175
0
{
176
0
    struct st_h2o_http3client_req_t *req = H2O_STRUCT_FROM_MEMBER(struct st_h2o_http3client_req_t, super, _client);
177
0
    h2o_http3_send_h3_datagrams(&req->conn->super, req->quic->stream_id, datagrams, num_datagrams);
178
0
}
179
180
static struct st_h2o_httpclient__h3_conn_t *find_connection(h2o_httpclient_connection_pool_t *pool, h2o_url_t *origin)
181
0
{
182
0
    int should_check_target = h2o_socketpool_is_global(pool->socketpool);
183
184
    /* FIXME:
185
     * - check connection state(e.g., max_concurrent_streams, if received GOAWAY)
186
     * - use hashmap
187
     */
188
0
    for (h2o_linklist_t *l = pool->http3.conns.next; l != &pool->http3.conns; l = l->next) {
189
0
        struct st_h2o_httpclient__h3_conn_t *conn = H2O_STRUCT_FROM_MEMBER(struct st_h2o_httpclient__h3_conn_t, link, l);
190
0
        if (should_check_target && !(conn->server.origin_url.scheme == origin->scheme &&
191
0
                                     h2o_memis(conn->server.origin_url.authority.base, conn->server.origin_url.authority.len,
192
0
                                               origin->authority.base, origin->authority.len)))
193
0
            continue;
194
0
        return conn;
195
0
    }
196
197
0
    return NULL;
198
0
}
199
200
static void start_pending_requests(struct st_h2o_httpclient__h3_conn_t *conn)
201
0
{
202
0
    while (!h2o_linklist_is_empty(&conn->pending_requests)) {
203
0
        struct st_h2o_http3client_req_t *req =
204
0
            H2O_STRUCT_FROM_MEMBER(struct st_h2o_http3client_req_t, link, conn->pending_requests.next);
205
0
        h2o_linklist_unlink(&req->link);
206
0
        start_request(req);
207
0
    }
208
0
}
209
210
static void call_proceed_req(struct st_h2o_http3client_req_t *req, const char *errstr)
211
0
{
212
0
    req->proceed_req.bytes_inflight = SIZE_MAX;
213
0
    req->proceed_req.cb(&req->super, errstr);
214
0
}
215
216
static void destroy_connection(struct st_h2o_httpclient__h3_conn_t *conn, const char *errstr)
217
0
{
218
0
    assert(errstr != NULL);
219
0
    if (h2o_linklist_is_linked(&conn->link))
220
0
        h2o_linklist_unlink(&conn->link);
221
0
    while (!h2o_linklist_is_empty(&conn->pending_requests)) {
222
0
        struct st_h2o_http3client_req_t *req =
223
0
            H2O_STRUCT_FROM_MEMBER(struct st_h2o_http3client_req_t, link, conn->pending_requests.next);
224
0
        h2o_linklist_unlink(&req->link);
225
0
        req->super._cb.on_connect(&req->super, errstr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
226
0
        destroy_request(req);
227
0
    }
228
0
    assert(h2o_linklist_is_empty(&conn->pending_requests));
229
0
    if (conn->getaddr_req != NULL)
230
0
        h2o_hostinfo_getaddr_cancel(conn->getaddr_req);
231
0
    h2o_timer_unlink(&conn->timeout);
232
0
    free(conn->server.origin_url.authority.base);
233
0
    free(conn->server.origin_url.host.base);
234
0
    free(conn->handshake_properties.client.session_ticket.base);
235
0
    h2o_http3_dispose_conn(&conn->super);
236
0
    free(conn);
237
0
}
238
239
static void destroy_connection_on_transport_close(h2o_quic_conn_t *_conn)
240
0
{
241
0
    struct st_h2o_httpclient__h3_conn_t *conn = (void *)_conn;
242
243
    /* When a connection gets closed while request is inflight, the most probable cause is some error in the transport (or at the
244
     * application protocol layer). But as we do not know the exact cause, we use a generic error here. */
245
0
    destroy_connection(conn, h2o_httpclient_error_io);
246
0
}
247
248
static void on_connect_timeout(h2o_timer_t *timeout)
249
0
{
250
0
    struct st_h2o_httpclient__h3_conn_t *conn = H2O_STRUCT_FROM_MEMBER(struct st_h2o_httpclient__h3_conn_t, timeout, timeout);
251
0
    destroy_connection(conn, h2o_httpclient_error_connect_timeout);
252
0
}
253
254
static void start_connect(struct st_h2o_httpclient__h3_conn_t *conn, struct sockaddr *sa)
255
0
{
256
0
    quicly_conn_t *qconn;
257
0
    ptls_iovec_t address_token = ptls_iovec_init(NULL, 0);
258
0
    quicly_transport_parameters_t resumed_tp;
259
0
    quicly_error_t ret;
260
261
0
    assert(conn->super.super.quic == NULL);
262
0
    assert(conn->getaddr_req == NULL);
263
0
    assert(h2o_timer_is_linked(&conn->timeout));
264
0
    assert(conn->timeout.cb == on_connect_timeout);
265
266
    /* create QUIC connection context and attach */
267
0
    if (conn->ctx->http3->load_session != NULL) {
268
0
        if (!conn->ctx->http3->load_session(conn->ctx, sa, conn->server.origin_url.host.base, &address_token,
269
0
                                            &conn->handshake_properties.client.session_ticket, &resumed_tp))
270
0
            goto Fail;
271
0
    }
272
0
    assert(conn->ctx->http3->h3.next_cid != NULL && "to identify connections, next_cid must be set");
273
0
    if ((ret = quicly_connect(&qconn, &conn->ctx->http3->quic, conn->server.origin_url.host.base, sa, NULL,
274
0
                              conn->ctx->http3->h3.next_cid, address_token, &conn->handshake_properties,
275
0
                              conn->handshake_properties.client.session_ticket.base != NULL ? &resumed_tp : NULL, NULL)) != 0) {
276
0
        conn->super.super.quic = NULL; /* just in case */
277
0
        goto Fail;
278
0
    }
279
0
    ++conn->ctx->http3->h3.next_cid->master_id; /* FIXME check overlap */
280
0
    if ((ret = h2o_http3_setup(&conn->super, qconn)) != 0)
281
0
        goto Fail;
282
283
0
    if (quicly_connection_is_ready(conn->super.super.quic))
284
0
        start_pending_requests(conn);
285
286
0
    h2o_quic_send(&conn->super.super);
287
288
0
    free(address_token.base);
289
0
    return;
290
0
Fail:
291
0
    free(address_token.base);
292
0
    destroy_connection(conn, h2o_httpclient_error_internal);
293
0
}
294
295
static void on_getaddr(h2o_hostinfo_getaddr_req_t *getaddr_req, const char *errstr, struct addrinfo *res, void *_conn)
296
0
{
297
0
    struct st_h2o_httpclient__h3_conn_t *conn = _conn;
298
299
0
    assert(getaddr_req == conn->getaddr_req);
300
0
    conn->getaddr_req = NULL;
301
302
0
    if (errstr != NULL) {
303
0
        destroy_connection(conn, errstr);
304
0
        return;
305
0
    }
306
307
0
    struct addrinfo *selected = h2o_hostinfo_select_one(res);
308
0
    start_connect(conn, selected->ai_addr);
309
0
}
310
311
static void handle_control_stream_frame(h2o_http3_conn_t *_conn, uint64_t type, const uint8_t *payload, size_t len)
312
0
{
313
0
    struct st_h2o_httpclient__h3_conn_t *conn = (void *)_conn;
314
0
    quicly_error_t err;
315
0
    const char *err_desc = NULL;
316
317
0
    if (!h2o_http3_has_received_settings(&conn->super)) {
318
0
        if (type != H2O_HTTP3_FRAME_TYPE_SETTINGS) {
319
0
            err = H2O_HTTP3_ERROR_MISSING_SETTINGS;
320
0
            goto Fail;
321
0
        }
322
0
        if ((err = h2o_http3_handle_settings_frame(&conn->super, payload, len, &err_desc)) != 0)
323
0
            goto Fail;
324
0
        assert(h2o_http3_has_received_settings(&conn->super));
325
        /* issue requests (unless it has been done already due to 0-RTT key being available) */
326
0
        start_pending_requests(conn);
327
0
    } else {
328
0
        switch (type) {
329
0
        case H2O_HTTP3_FRAME_TYPE_SETTINGS:
330
0
            err = H2O_HTTP3_ERROR_FRAME_UNEXPECTED;
331
0
            err_desc = "unexpected SETTINGS frame";
332
0
            goto Fail;
333
0
        case H2O_HTTP3_FRAME_TYPE_GOAWAY: {
334
0
            h2o_http3_goaway_frame_t frame;
335
0
            if ((err = h2o_http3_decode_goaway_frame(&frame, payload, len, &err_desc)) != 0)
336
0
                goto Fail;
337
            /* FIXME: stop issuing new requests */
338
0
            break;
339
0
        }
340
0
        default:
341
0
            break;
342
0
        }
343
0
    }
344
345
0
    return;
346
0
Fail:
347
0
    h2o_quic_close_connection(&conn->super.super, err, err_desc);
348
0
}
349
350
struct st_h2o_httpclient__h3_conn_t *create_connection(h2o_httpclient_ctx_t *ctx, h2o_httpclient_connection_pool_t *pool,
351
                                                       h2o_url_t *origin)
352
0
{
353
    /* FIXME When using a non-global socket pool, let the socket pool load balance H3 connections among the list of targets being
354
     * available. But until then, we use the first entry. */
355
0
    if (!h2o_socketpool_is_global(pool->socketpool))
356
0
        origin = &pool->socketpool->targets.entries[0]->url;
357
358
0
    static const h2o_http3_conn_callbacks_t callbacks = {{destroy_connection_on_transport_close}, handle_control_stream_frame};
359
0
    static const h2o_http3_qpack_context_t qpack_ctx = {0 /* TODO */};
360
361
0
    struct st_h2o_httpclient__h3_conn_t *conn = h2o_mem_alloc(sizeof(*conn));
362
363
0
    h2o_http3_init_conn(&conn->super, &ctx->http3->h3, &callbacks, &qpack_ctx, ctx->http3->max_frame_payload_size);
364
0
    memset((char *)conn + sizeof(conn->super), 0, sizeof(*conn) - sizeof(conn->super));
365
0
    conn->ctx = ctx;
366
0
    h2o_url_copy(NULL, &conn->server.origin_url, origin);
367
0
    sprintf(conn->server.named_serv, "%" PRIu16, h2o_url_get_port(origin));
368
0
    conn->handshake_properties.client.negotiated_protocols.list = h2o_http3_alpn;
369
0
    conn->handshake_properties.client.negotiated_protocols.count = sizeof(h2o_http3_alpn) / sizeof(h2o_http3_alpn[0]);
370
0
    h2o_linklist_insert(&pool->http3.conns, &conn->link);
371
0
    h2o_linklist_init_anchor(&conn->pending_requests);
372
373
0
    conn->getaddr_req = h2o_hostinfo_getaddr(conn->ctx->getaddr_receiver, conn->server.origin_url.host,
374
0
                                             h2o_iovec_init(conn->server.named_serv, strlen(conn->server.named_serv)),
375
0
                                             ctx->http3->h3.sock.addr.ss_family, SOCK_DGRAM, IPPROTO_UDP,
376
0
                                             AI_ADDRCONFIG | AI_NUMERICSERV, on_getaddr, conn);
377
0
    h2o_timer_link(conn->ctx->loop, conn->ctx->connect_timeout, &conn->timeout);
378
0
    conn->timeout.cb = on_connect_timeout;
379
380
0
    return conn;
381
0
}
382
383
static void notify_response_error(struct st_h2o_http3client_req_t *req, const char *errstr)
384
0
{
385
0
    assert(errstr != NULL);
386
387
0
    switch (req->response_state) {
388
0
    case H2O_HTTP3CLIENT_RESPONSE_STATE_HEAD:
389
0
        req->super._cb.on_head(&req->super, errstr, NULL);
390
0
        break;
391
0
    case H2O_HTTP3CLIENT_RESPONSE_STATE_BODY:
392
0
        req->super._cb.on_body(&req->super, errstr, NULL, 0);
393
0
        break;
394
0
    default:
395
0
        break;
396
0
    }
397
0
    req->response_state = H2O_HTTP3CLIENT_RESPONSE_STATE_CLOSED;
398
0
}
399
400
static int call_on_body(struct st_h2o_http3client_req_t *req, const char *errstr)
401
0
{
402
0
    assert(req->response_state == H2O_HTTP3CLIENT_RESPONSE_STATE_BODY);
403
404
0
    int ret = req->super._cb.on_body(&req->super, errstr, NULL, 0);
405
0
    if (errstr != NULL)
406
0
        req->response_state = H2O_HTTP3CLIENT_RESPONSE_STATE_CLOSED;
407
408
0
    return ret;
409
0
}
410
411
static quicly_error_t handle_input_data_payload(struct st_h2o_http3client_req_t *req, const uint8_t **src, const uint8_t *src_end,
412
                                                quicly_error_t err, const char **err_desc)
413
0
{
414
    /* save data, update states */
415
0
    if (req->bytes_left_in_data_frame != 0) {
416
0
        size_t payload_bytes = req->bytes_left_in_data_frame;
417
0
        if (src_end - *src < payload_bytes)
418
0
            payload_bytes = src_end - *src;
419
0
        h2o_buffer_append(&req->recvbuf.body, *src, payload_bytes);
420
0
        *src += payload_bytes;
421
0
        req->bytes_left_in_data_frame -= payload_bytes;
422
0
    }
423
0
    if (req->bytes_left_in_data_frame == 0)
424
0
        req->handle_input = handle_input_expect_data_frame;
425
426
    /* call the handler */
427
0
    const char *errstr = NULL;
428
0
    if (*src == src_end && err != 0) {
429
        /* FIXME also check content-length? see what other protocol handlers do */
430
0
        errstr = err == ERROR_EOS && req->bytes_left_in_data_frame == 0 ? h2o_httpclient_error_is_eos : h2o_httpclient_error_io;
431
0
    }
432
0
    if (call_on_body(req, errstr) != 0)
433
0
        return H2O_HTTP3_ERROR_INTERNAL;
434
435
0
    return 0;
436
0
}
437
438
quicly_error_t handle_input_expect_data_frame(struct st_h2o_http3client_req_t *req, const uint8_t **src, const uint8_t *src_end,
439
                                              quicly_error_t err, const char **err_desc)
440
0
{
441
0
    assert(req->bytes_left_in_data_frame == 0);
442
0
    if (*src == src_end) {
443
        /* return early if no input, no state change */
444
0
        if (err == 0)
445
0
            return 0;
446
        /* either EOS or an unexpected close; delegate the task to the payload processing function */
447
0
    } else {
448
        /* otherwise, read the frame */
449
0
        h2o_http3_read_frame_t frame;
450
0
        quicly_error_t ret;
451
0
        if ((ret = h2o_http3_read_frame(&frame, 1, H2O_HTTP3_STREAM_TYPE_REQUEST, req->conn->super.max_frame_payload_size, src,
452
0
                                        src_end, err_desc)) != 0) {
453
            /* incomplete */
454
0
            if (ret == H2O_HTTP3_ERROR_INCOMPLETE && err == 0)
455
0
                return ret;
456
0
            call_on_body(req, h2o_httpclient_error_malformed_frame);
457
0
            return ret;
458
0
        }
459
0
        switch (frame.type) {
460
0
        case H2O_HTTP3_FRAME_TYPE_DATA:
461
0
            break;
462
0
        case H2O_HTTP3_FRAME_TYPE_HEADERS:
463
0
            if (req->super.upgrade_to != NULL)
464
0
                return H2O_HTTP3_ERROR_FRAME_UNEXPECTED;
465
            /* flow continues */
466
0
        default:
467
            /* FIXME handle push_promise, trailers */
468
0
            return 0;
469
0
        }
470
0
        req->bytes_left_in_data_frame = frame.length;
471
0
    }
472
473
    /* unexpected close of DATA frame is handled by handle_input_data_payload. We rely on the function to detect if the DATA frame
474
     * is closed right after the frame header */
475
0
    req->handle_input = handle_input_data_payload;
476
0
    return handle_input_data_payload(req, src, src_end, err, err_desc);
477
0
}
478
479
static quicly_error_t handle_input_expect_headers(struct st_h2o_http3client_req_t *req, const uint8_t **src, const uint8_t *src_end,
480
                                                  quicly_error_t err, const char **err_desc)
481
0
{
482
0
    h2o_http3_read_frame_t frame;
483
0
    int status;
484
0
    h2o_headers_t headers = {NULL};
485
0
    h2o_iovec_t datagram_flow_id = {};
486
0
    uint8_t header_ack[H2O_HPACK_ENCODE_INT_MAX_LENGTH];
487
0
    size_t header_ack_len;
488
0
    int frame_is_eos;
489
0
    quicly_error_t ret;
490
491
    /* read HEADERS frame */
492
0
    if ((ret = h2o_http3_read_frame(&frame, 1, H2O_HTTP3_STREAM_TYPE_REQUEST, req->conn->super.max_frame_payload_size, src, src_end,
493
0
                                    err_desc)) != 0) {
494
0
        if (ret == H2O_HTTP3_ERROR_INCOMPLETE) {
495
0
            if (err != 0) {
496
0
                notify_response_error(req, h2o_httpclient_error_io);
497
0
                return 0;
498
0
            }
499
0
            return ret;
500
0
        }
501
0
        notify_response_error(req, "response header too large");
502
0
        return H2O_HTTP3_ERROR_EXCESSIVE_LOAD; /* FIXME correct code? */
503
0
    }
504
0
    frame_is_eos = *src == src_end && err != 0;
505
0
    if (frame.type != H2O_HTTP3_FRAME_TYPE_HEADERS) {
506
0
        switch (frame.type) {
507
0
        case H2O_HTTP3_FRAME_TYPE_DATA:
508
0
            *err_desc = "received DATA frame before HEADERS";
509
0
            return H2O_HTTP3_ERROR_FRAME_UNEXPECTED;
510
0
        default:
511
0
            return 0;
512
0
        }
513
0
    }
514
0
    if ((ret = h2o_qpack_parse_response(req->super.pool, req->conn->super.qpack.dec, req->quic->stream_id, &status, &headers,
515
0
                                        &datagram_flow_id, header_ack, &header_ack_len, frame.payload, frame.length, err_desc)) !=
516
0
        0) {
517
0
        if (ret == H2O_HTTP2_ERROR_INCOMPLETE) {
518
            /* the request is blocked by the QPACK stream */
519
0
            req->handle_input = NULL; /* FIXME */
520
0
            return 0;
521
0
        }
522
0
        if (*err_desc == NULL)
523
0
            *err_desc = "qpack error";
524
0
        notify_response_error(req, *err_desc);
525
0
        return H2O_HTTP3_ERROR_GENERAL_PROTOCOL; /* FIXME */
526
0
    }
527
0
    if (header_ack_len != 0)
528
0
        h2o_http3_send_qpack_header_ack(&req->conn->super, header_ack, header_ack_len);
529
530
0
    if (datagram_flow_id.base != NULL) {
531
0
        if (!req->offered_datagram_flow_id) {
532
0
            *err_desc = "no offered datagram-flow-id";
533
0
            return H2O_HTTP3_ERROR_GENERAL_PROTOCOL;
534
0
        }
535
        /* TODO validate the returned value */
536
0
    }
537
538
    /* handle 1xx */
539
0
    if (100 <= status && status <= 199) {
540
0
        if (status == 101) {
541
0
            *err_desc = "unexpected 101";
542
0
            notify_response_error(req, *err_desc);
543
0
            return H2O_HTTP3_ERROR_GENERAL_PROTOCOL;
544
0
        }
545
0
        if (frame_is_eos) {
546
0
            notify_response_error(req, h2o_httpclient_error_io);
547
0
            return 0;
548
0
        }
549
0
        if (req->super.informational_cb != NULL &&
550
0
            req->super.informational_cb(&req->super, 0x300, status, h2o_iovec_init(NULL, 0), headers.entries, headers.size) != 0) {
551
0
            return H2O_HTTP3_ERROR_INTERNAL;
552
0
        }
553
0
        return 0;
554
0
    }
555
556
    /* handle final response, creating tunnel object if necessary */
557
0
    h2o_httpclient_on_head_t on_head = {.version = 0x300,
558
0
                                        .msg = h2o_iovec_init(NULL, 0),
559
0
                                        .status = status,
560
0
                                        .headers = headers.entries,
561
0
                                        .num_headers = headers.size};
562
0
    if (h2o_httpclient__tunnel_is_ready(&req->super, status, on_head.version)) {
563
0
        on_head.forward_datagram.write_ = write_datagrams;
564
0
        on_head.forward_datagram.read_ = &req->on_read_datagrams;
565
0
    }
566
0
    req->super._cb.on_body = req->super._cb.on_head(&req->super, frame_is_eos ? h2o_httpclient_error_is_eos : NULL, &on_head);
567
0
    req->response_state = H2O_HTTP3CLIENT_RESPONSE_STATE_BODY;
568
0
    if (req->super._cb.on_body == NULL)
569
0
        return frame_is_eos ? 0 : H2O_HTTP3_ERROR_INTERNAL;
570
571
    /* handle body */
572
0
    req->handle_input = handle_input_expect_data_frame;
573
0
    return 0;
574
0
}
575
576
static void on_stream_destroy(quicly_stream_t *qs, quicly_error_t err)
577
0
{
578
0
    struct st_h2o_http3client_req_t *req;
579
580
0
    if ((req = qs->data) == NULL)
581
0
        return;
582
0
    notify_response_error(req, h2o_httpclient_error_io);
583
0
    detach_stream(req);
584
0
    destroy_request(req);
585
0
}
586
587
static void on_send_shift(quicly_stream_t *qs, size_t delta)
588
0
{
589
0
    struct st_h2o_http3client_req_t *req = qs->data;
590
591
0
    assert(req != NULL);
592
0
    h2o_buffer_consume(&req->sendbuf, delta);
593
0
}
594
595
static void on_send_emit(quicly_stream_t *qs, size_t off, void *dst, size_t *len, int *wrote_all)
596
0
{
597
0
    struct st_h2o_http3client_req_t *req = qs->data;
598
599
0
    if (*len >= req->sendbuf->size - off) {
600
0
        *len = req->sendbuf->size - off;
601
0
        *wrote_all = 1;
602
0
    } else {
603
0
        *wrote_all = 0;
604
0
    }
605
0
    memcpy(dst, req->sendbuf->bytes + off, *len);
606
607
0
    if (*wrote_all && req->proceed_req.bytes_inflight != SIZE_MAX)
608
0
        call_proceed_req(req, NULL);
609
0
}
610
611
static void on_send_stop(quicly_stream_t *qs, quicly_error_t err)
612
0
{
613
0
    struct st_h2o_http3client_req_t *req;
614
615
0
    if ((req = qs->data) == NULL)
616
0
        return;
617
618
0
    if (!quicly_sendstate_transfer_complete(&req->quic->sendstate))
619
0
        quicly_reset_stream(req->quic, err);
620
621
0
    if (req->proceed_req.bytes_inflight != SIZE_MAX)
622
0
        call_proceed_req(req, h2o_httpclient_error_io /* TODO better error code? */);
623
624
0
    if (!quicly_recvstate_transfer_complete(&req->quic->recvstate)) {
625
0
        quicly_request_stop(req->quic, H2O_HTTP3_ERROR_REQUEST_CANCELLED);
626
0
        notify_response_error(req, h2o_httpclient_error_io);
627
0
    }
628
0
    detach_stream(req);
629
0
    destroy_request(req);
630
0
}
631
632
static quicly_error_t on_receive_process_bytes(struct st_h2o_http3client_req_t *req, const uint8_t **src, const uint8_t *src_end,
633
                                               const char **err_desc)
634
0
{
635
0
    int is_eos = quicly_recvstate_transfer_complete(&req->quic->recvstate);
636
0
    assert(is_eos || *src != src_end);
637
0
    quicly_error_t ret;
638
639
0
    do {
640
0
        if ((ret = req->handle_input(req, src, src_end, is_eos ? ERROR_EOS : 0, err_desc)) != 0) {
641
0
            if (ret == H2O_HTTP3_ERROR_INCOMPLETE)
642
0
                ret = is_eos ? H2O_HTTP3_ERROR_FRAME : 0;
643
0
            break;
644
0
        }
645
0
    } while (*src != src_end);
646
647
0
    return ret;
648
0
}
649
650
static void on_receive(quicly_stream_t *qs, size_t off, const void *input, size_t len)
651
0
{
652
0
    struct st_h2o_http3client_req_t *req = qs->data;
653
0
    size_t bytes_consumed;
654
0
    quicly_error_t err = 0;
655
0
    const char *err_desc = NULL;
656
657
    /* process the input, update stream-level receive buffer */
658
0
    if (req->recvbuf.stream->size == 0 && off == 0) {
659
660
        /* fast path; process the input directly, save the remaining bytes */
661
0
        const uint8_t *src = input;
662
0
        err = on_receive_process_bytes(req, &src, src + len, &err_desc);
663
0
        bytes_consumed = src - (const uint8_t *)input;
664
0
        if (bytes_consumed != len)
665
0
            h2o_buffer_append(&req->recvbuf.stream, src, len - bytes_consumed);
666
0
    } else {
667
        /* slow path; copy data to partial_frame */
668
0
        size_t size_required = off + len;
669
0
        if (req->recvbuf.stream->size < size_required) {
670
0
            h2o_buffer_reserve(&req->recvbuf.stream, size_required - req->recvbuf.stream->size);
671
0
            req->recvbuf.stream->size = size_required;
672
0
        }
673
0
        memcpy(req->recvbuf.stream->bytes + off, input, len);
674
675
        /* just return if no new data is available */
676
0
        size_t bytes_available = quicly_recvstate_bytes_available(&req->quic->recvstate);
677
0
        if (req->recvbuf.prev_bytes_available == bytes_available)
678
0
            return;
679
680
        /* process the bytes that have not been processed, update stream-level buffer */
681
0
        const uint8_t *src = (const uint8_t *)req->recvbuf.stream->bytes;
682
0
        err = on_receive_process_bytes(req, &src, (const uint8_t *)req->recvbuf.stream->bytes + bytes_available, &err_desc);
683
0
        bytes_consumed = src - (const uint8_t *)req->recvbuf.stream->bytes;
684
0
        h2o_buffer_consume(&req->recvbuf.stream, bytes_consumed);
685
0
    }
686
687
    /* update QUIC stream-level state */
688
0
    if (bytes_consumed != 0)
689
0
        quicly_stream_sync_recvbuf(req->quic, bytes_consumed);
690
0
    req->recvbuf.prev_bytes_available = quicly_recvstate_bytes_available(&req->quic->recvstate);
691
692
    /* cleanup */
693
0
    if (quicly_recvstate_transfer_complete(&req->quic->recvstate)) {
694
        /* destroy the request if send-side is already closed, otherwise wait until the send-side gets closed */
695
0
        if (quicly_sendstate_transfer_complete(&req->quic->sendstate)) {
696
0
            detach_stream(req);
697
0
            destroy_request(req);
698
0
        }
699
0
    } else if (err != 0) {
700
0
        notify_response_error(req, h2o_httpclient_error_io);
701
0
        int send_is_open = quicly_sendstate_is_open(&req->quic->sendstate);
702
0
        close_stream(req, err);
703
        /* immediately dispose of the request if possible, or wait for the send-side to close */
704
0
        if (!send_is_open) {
705
0
            destroy_request(req);
706
0
        } else if (req->proceed_req.bytes_inflight != SIZE_MAX) {
707
0
            call_proceed_req(req, h2o_httpclient_error_io);
708
0
            destroy_request(req);
709
0
        } else {
710
            /* wait for write_req to be called */
711
0
        }
712
0
    }
713
0
}
714
715
static void on_receive_reset(quicly_stream_t *qs, quicly_error_t err)
716
0
{
717
0
    struct st_h2o_http3client_req_t *req = qs->data;
718
719
0
    notify_response_error(req, h2o_httpclient_error_io);
720
0
    close_stream(req, H2O_HTTP3_ERROR_REQUEST_CANCELLED);
721
0
    destroy_request(req);
722
0
}
723
724
void start_request(struct st_h2o_http3client_req_t *req)
725
0
{
726
0
    h2o_iovec_t method;
727
0
    h2o_url_t url;
728
0
    const h2o_header_t *headers;
729
0
    size_t num_headers;
730
0
    h2o_iovec_t body;
731
0
    h2o_httpclient_properties_t props = {NULL};
732
0
    char datagram_flow_id_buf[sizeof(H2O_UINT64_LONGEST_STR)];
733
0
    quicly_error_t ret;
734
735
0
    assert(req->quic == NULL);
736
0
    assert(!h2o_linklist_is_linked(&req->link));
737
738
0
    if ((req->super._cb.on_head = req->super._cb.on_connect(&req->super, NULL, &method, &url, &headers, &num_headers, &body,
739
0
                                                            &req->proceed_req.cb, &props, &req->conn->server.origin_url)) == NULL) {
740
0
        destroy_request(req);
741
0
        return;
742
0
    }
743
744
0
    if ((ret = quicly_open_stream(req->conn->super.super.quic, &req->quic, 0)) != 0) {
745
0
        notify_response_error(req, "failed to open stream");
746
0
        destroy_request(req);
747
0
        return;
748
0
    }
749
0
    req->quic->data = req;
750
751
    /* send request (TODO optimize) */
752
0
    h2o_iovec_t protocol = {};
753
0
    h2o_iovec_t datagram_flow_id = {};
754
0
    if (req->super.upgrade_to == h2o_httpclient_upgrade_to_connect &&
755
0
        h2o_memis(method.base, method.len, H2O_STRLIT("CONNECT-UDP")) && req->conn->super.peer_settings.h3_datagram) {
756
0
        datagram_flow_id.len = sprintf(datagram_flow_id_buf, "%" PRIu64, req->quic->stream_id);
757
0
        datagram_flow_id.base = datagram_flow_id_buf;
758
0
        req->offered_datagram_flow_id = 1;
759
0
    } else if (req->super.upgrade_to != NULL && req->super.upgrade_to != h2o_httpclient_upgrade_to_connect) {
760
0
        protocol = h2o_iovec_init(req->super.upgrade_to, strlen(req->super.upgrade_to));
761
0
    }
762
0
    h2o_iovec_t headers_frame =
763
0
        h2o_qpack_flatten_request(req->conn->super.qpack.enc, req->super.pool, req->quic->stream_id, NULL, method, url.scheme,
764
0
                                  url.authority, url.path, protocol, headers, num_headers, datagram_flow_id);
765
0
    h2o_buffer_append(&req->sendbuf, headers_frame.base, headers_frame.len);
766
0
    if (body.len != 0)
767
0
        emit_data(req, body);
768
0
    if (req->proceed_req.cb != NULL) {
769
0
        req->super.write_req = do_write_req;
770
0
        req->proceed_req.bytes_inflight = body.len;
771
0
    }
772
0
    if (req->proceed_req.cb == NULL && req->super.upgrade_to == NULL)
773
0
        quicly_sendstate_shutdown(&req->quic->sendstate, req->sendbuf->size);
774
0
    quicly_stream_sync_sendbuf(req->quic, 1);
775
776
0
    req->handle_input = handle_input_expect_headers;
777
0
}
778
779
static void cancel_request(h2o_httpclient_t *_client)
780
0
{
781
0
    struct st_h2o_http3client_req_t *req = (void *)_client;
782
0
    if (req->quic != NULL)
783
0
        close_stream(req, H2O_HTTP3_ERROR_REQUEST_CANCELLED);
784
0
    destroy_request(req);
785
0
}
786
787
static void do_get_conn_properties(h2o_httpclient_t *_client, h2o_httpclient_conn_properties_t *properties)
788
0
{
789
0
    struct st_h2o_http3client_req_t *req = (void *)_client;
790
0
    ptls_t *tls;
791
0
    ptls_cipher_suite_t *cipher;
792
793
0
    if (req->quic != NULL && (tls = quicly_get_tls(req->quic->conn), (cipher = ptls_get_cipher(tls)) != NULL)) {
794
0
        properties->ssl.protocol_version = "TLSv1.3";
795
0
        properties->ssl.session_reused = ptls_is_psk_handshake(tls);
796
0
        properties->ssl.cipher = cipher->name;
797
0
        properties->ssl.cipher_bits = (int)cipher->aead->key_size;
798
0
    } else {
799
0
        properties->ssl.protocol_version = NULL;
800
0
        properties->ssl.session_reused = -1;
801
0
        properties->ssl.cipher = NULL;
802
0
        properties->ssl.cipher_bits = 0;
803
0
    }
804
0
    properties->sock = NULL;
805
0
}
806
807
static void do_update_window(h2o_httpclient_t *_client)
808
0
{
809
    /* TODO Stop receiving data for the stream when `buf` grows to certain extent. Then, resume when this function is being called.
810
     */
811
0
}
812
813
int do_write_req(h2o_httpclient_t *_client, h2o_iovec_t chunk, int is_end_stream)
814
0
{
815
0
    struct st_h2o_http3client_req_t *req = (void *)_client;
816
817
0
    assert(req->proceed_req.bytes_inflight == SIZE_MAX);
818
819
    /* Notify error to the application, if the stream has already been closed (due to e.g., a stream error) or if the send-side has
820
     * been closed (due to STOP_SENDING). Also, destroy the request if the receive side has already been closed. */
821
0
    if (req->quic == NULL || !quicly_sendstate_is_open(&req->quic->sendstate)) {
822
0
        if (req->quic != NULL && quicly_recvstate_transfer_complete(&req->quic->recvstate))
823
0
            close_stream(req, H2O_HTTP3_ERROR_REQUEST_CANCELLED);
824
0
        if (req->quic == NULL)
825
0
            destroy_request(req);
826
0
        return 1;
827
0
    }
828
829
0
    emit_data(req, chunk);
830
831
    /* shutdown if we've written all request body */
832
0
    if (is_end_stream) {
833
0
        assert(quicly_sendstate_is_open(&req->quic->sendstate));
834
0
        quicly_sendstate_shutdown(&req->quic->sendstate, req->quic->sendstate.acked.ranges[0].end + req->sendbuf->size);
835
0
    } else {
836
0
        assert(chunk.len != 0);
837
0
    }
838
839
0
    req->proceed_req.bytes_inflight = chunk.len;
840
0
    quicly_stream_sync_sendbuf(req->quic, 1);
841
0
    h2o_quic_schedule_timer(&req->conn->super.super);
842
0
    return 0;
843
0
}
844
845
void h2o_httpclient__connect_h3(h2o_httpclient_t **_client, h2o_mem_pool_t *pool, void *data, h2o_httpclient_ctx_t *ctx,
846
                                h2o_httpclient_connection_pool_t *connpool, h2o_url_t *target, const char *upgrade_to,
847
                                h2o_httpclient_connect_cb cb)
848
0
{
849
0
    struct st_h2o_httpclient__h3_conn_t *conn;
850
0
    struct st_h2o_http3client_req_t *req;
851
852
0
    if ((conn = find_connection(connpool, target)) == NULL)
853
0
        conn = create_connection(ctx, connpool, target);
854
855
0
    req = h2o_mem_alloc(sizeof(*req));
856
0
    *req = (struct st_h2o_http3client_req_t){
857
0
        .super = {pool,
858
0
                  ctx,
859
0
                  connpool,
860
0
                  &req->recvbuf.body,
861
0
                  data,
862
0
                  NULL,
863
0
                  {h2o_gettimeofday(ctx->loop)},
864
0
                  upgrade_to,
865
0
                  {0},
866
0
                  {0},
867
0
                  cancel_request,
868
0
                  do_get_conn_properties,
869
0
                  do_update_window},
870
0
        .conn = conn,
871
0
        .proceed_req = {.cb = NULL, .bytes_inflight = SIZE_MAX},
872
0
    };
873
0
    req->super._cb.on_connect = cb;
874
0
    h2o_buffer_init(&req->sendbuf, &h2o_socket_buffer_prototype);
875
0
    h2o_buffer_init(&req->recvbuf.body, &h2o_socket_buffer_prototype);
876
0
    h2o_buffer_init(&req->recvbuf.stream, &h2o_socket_buffer_prototype);
877
878
0
    if (_client != NULL)
879
0
        *_client = &req->super;
880
881
0
    if (h2o_http3_has_received_settings(&conn->super)) {
882
0
        start_request(req);
883
0
        h2o_quic_schedule_timer(&conn->super.super);
884
0
    } else {
885
0
        h2o_linklist_insert(&conn->pending_requests, &req->link);
886
0
    }
887
0
}
888
889
void h2o_httpclient_http3_notify_connection_update(h2o_quic_ctx_t *ctx, h2o_quic_conn_t *_conn)
890
0
{
891
0
    struct st_h2o_httpclient__h3_conn_t *conn = (void *)_conn;
892
893
0
    if (h2o_timer_is_linked(&conn->timeout) && conn->timeout.cb == on_connect_timeout) {
894
        /* TODO check connection state? */
895
0
        h2o_timer_unlink(&conn->timeout);
896
0
    }
897
0
}
898
899
static quicly_error_t stream_open_cb(quicly_stream_open_t *self, quicly_stream_t *qs)
900
0
{
901
0
    if (quicly_stream_is_unidirectional(qs->stream_id)) {
902
0
        h2o_http3_on_create_unidirectional_stream(qs);
903
0
    } else {
904
0
        static const quicly_stream_callbacks_t callbacks = {on_stream_destroy, on_send_shift, on_send_emit,
905
0
                                                            on_send_stop,      on_receive,    on_receive_reset};
906
0
        assert(quicly_stream_is_client_initiated(qs->stream_id));
907
0
        qs->callbacks = &callbacks;
908
0
    }
909
0
    return 0;
910
0
}
911
912
quicly_stream_open_t h2o_httpclient_http3_on_stream_open = {stream_open_cb};
913
914
static void on_receive_datagram_frame(quicly_receive_datagram_frame_t *self, quicly_conn_t *qc, ptls_iovec_t datagram)
915
0
{
916
0
    struct st_h2o_httpclient__h3_conn_t *conn =
917
0
        H2O_STRUCT_FROM_MEMBER(struct st_h2o_httpclient__h3_conn_t, super, *quicly_get_data(qc));
918
0
    uint64_t flow_id;
919
0
    h2o_iovec_t payload;
920
0
    quicly_stream_t *qs;
921
922
    /* decode, validate, get stream */
923
0
    if ((flow_id = h2o_http3_decode_h3_datagram(&payload, datagram.base, datagram.len)) == UINT64_MAX ||
924
0
        !(quicly_stream_is_client_initiated(flow_id) && !quicly_stream_is_unidirectional(flow_id))) {
925
0
        h2o_quic_close_connection(&conn->super.super, H2O_HTTP3_ERROR_GENERAL_PROTOCOL, "invalid DATAGRAM frame");
926
0
        return;
927
0
    }
928
0
    if ((qs = quicly_get_stream(conn->super.super.quic, flow_id)) == NULL)
929
0
        return;
930
931
0
    struct st_h2o_http3client_req_t *req = qs->data;
932
0
    if (req->on_read_datagrams != NULL)
933
0
        req->on_read_datagrams(&req->super, &payload, 1);
934
0
}
935
936
quicly_receive_datagram_frame_t h2o_httpclient_http3_on_receive_datagram_frame = {on_receive_datagram_frame};