blob: 194d57f2f48a56300b3e2a4e331190656151817d [file] [log] [blame]
Frédéric Lécailleccac11f2021-03-03 16:09:02 +01001/*
2 * HTTP/3 protocol processing
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation, version 2.1
7 * exclusively.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19#include <haproxy/buf.h>
Amaury Denoyelle99043552021-08-24 15:36:02 +020020#include <haproxy/connection.h>
Frédéric Lécailleccac11f2021-03-03 16:09:02 +010021#include <haproxy/dynbuf.h>
22#include <haproxy/h3.h>
Amaury Denoyelleb49fa1a2021-08-24 15:30:12 +020023#include <haproxy/http.h>
24#include <haproxy/htx.h>
Frédéric Lécailleccac11f2021-03-03 16:09:02 +010025#include <haproxy/istbuf.h>
26#include <haproxy/mux_quic.h>
27#include <haproxy/pool.h>
28#include <haproxy/qpack-dec.h>
Amaury Denoyelle99043552021-08-24 15:36:02 +020029#include <haproxy/stream.h>
Frédéric Lécailleccac11f2021-03-03 16:09:02 +010030#include <haproxy/tools.h>
31#include <haproxy/xprt_quic.h>
32
33#define DEBUG_H3
34
35#if defined(DEBUG_H3)
36#define h3_debug_printf fprintf
37#define h3_debug_hexdump debug_hexdump
38#else
39#define h3_debug_printf(...) do { } while (0)
40#define h3_debug_hexdump(...) do { } while (0)
41#endif
42
43#define H3_CF_SETTINGS_SENT 0x00000001
44
45/* Default settings */
Amaury Denoyelle33949392021-08-24 15:16:58 +020046static uint64_t h3_settings_qpack_max_table_capacity = 0;
47static uint64_t h3_settings_qpack_blocked_streams = 4096;
48static uint64_t h3_settings_max_field_section_size = QUIC_VARINT_8_BYTE_MAX; /* Unlimited */
Frédéric Lécailleccac11f2021-03-03 16:09:02 +010049
50struct h3 {
51 struct qcc *qcc;
52 enum h3_err err;
53 uint32_t flags;
54 /* Locally initiated uni-streams */
55 struct h3_uqs lqpack_enc;
56 struct h3_uqs lqpack_dec;
57 struct h3_uqs lctrl;
58 /* Remotely initiated uni-streams */
59 struct h3_uqs rqpack_enc;
60 struct h3_uqs rqpack_dec;
61 struct h3_uqs rctrl;
62 /* Settings */
63 uint64_t qpack_max_table_capacity;
64 uint64_t qpack_blocked_streams;
65 uint64_t max_field_section_size;
66 struct buffer_wait buf_wait; /* wait list for buffer allocations */
67};
68
69DECLARE_STATIC_POOL(pool_head_h3, "h3", sizeof(struct h3));
70
71/* Simple function to duplicate a buffer */
72static inline struct buffer h3_b_dup(struct buffer *b)
73{
74 return b_make(b->area, b->size, b->head, b->data);
75}
76
77static int qcs_buf_available(void *target)
78{
79 struct h3_uqs *h3_uqs = target;
80 struct qcs *qcs = h3_uqs->qcs;
81
82 if ((qcs->flags & OUQS_SF_TXBUF_MALLOC) && b_alloc(&qcs->tx.buf)) {
83 qcs->flags &= ~OUQS_SF_TXBUF_MALLOC;
84 tasklet_wakeup(h3_uqs->wait_event.tasklet);
85 return 1;
86 }
87
88 return 0;
89}
90
91static struct buffer *h3_uqs_get_buf(struct h3_uqs *h3_uqs)
92{
93 struct buffer *buf = NULL;
94 struct h3 *h3 = h3_uqs->qcs->qcc->ctx;
95
96 if (likely(!LIST_INLIST(&h3->buf_wait.list)) &&
97 unlikely((buf = b_alloc(&h3_uqs->qcs->tx.buf)) == NULL)) {
98 h3->buf_wait.target = h3_uqs;
99 h3->buf_wait.wakeup_cb = qcs_buf_available;
100 LIST_APPEND(&ti->buffer_wq, &h3->buf_wait.list);
101 }
102
103 return buf;
104}
105
106/* Decode a h3 frame header made of two QUIC varints from <b> buffer.
107 * Returns the number of bytes consumed if there was enough data in <b>, 0 if not.
108 * Note that this function update <b> buffer to reflect the number of bytes consumed
109 * to decode the h3 frame header.
110 */
111static inline size_t h3_decode_frm_header(uint64_t *ftype, uint64_t *flen,
112 struct buffer *b)
113{
114 size_t hlen;
115
116 hlen = 0;
117 if (!b_quic_dec_int(ftype, b, &hlen) || !b_quic_dec_int(flen, b, &hlen))
118 return 0;
119
120 return hlen;
121}
122
123/* Decode <qcs> remotely initiated bidi-stream */
124static int h3_decode_qcs(struct qcs *qcs, void *ctx)
125{
126 struct buffer *rxbuf = &qcs->rx.buf;
127 struct h3 *h3 = ctx;
Amaury Denoyelleb49fa1a2021-08-24 15:30:12 +0200128 struct htx *htx;
129 struct htx_sl *sl;
Amaury Denoyelle99043552021-08-24 15:36:02 +0200130 struct conn_stream *cs;
Amaury Denoyellefd7cdc32021-08-24 15:13:20 +0200131 struct http_hdr list[global.tune.max_http_hdr];
Amaury Denoyelleb49fa1a2021-08-24 15:30:12 +0200132 unsigned int flags = HTX_SL_F_NONE;
Amaury Denoyellefd7cdc32021-08-24 15:13:20 +0200133 int hdr_idx;
Frédéric Lécailleccac11f2021-03-03 16:09:02 +0100134
135 h3_debug_printf(stderr, "%s: STREAM ID: %llu\n", __func__, qcs->by_id.key);
136 if (!b_data(rxbuf))
137 return 0;
138
139 while (b_data(rxbuf)) {
140 size_t hlen;
141 uint64_t ftype, flen;
142 struct buffer b;
143
144 /* Work on a copy of <rxbuf> */
145 b = h3_b_dup(rxbuf);
146 hlen = h3_decode_frm_header(&ftype, &flen, &b);
147 if (!hlen)
148 break;
149
150 h3_debug_printf(stderr, "%s: ftype: %llu, flen: %llu\n", __func__,
151 (unsigned long long)ftype, (unsigned long long)flen);
152 if (flen > b_data(&b))
153 break;
154
155 b_del(rxbuf, hlen);
156 switch (ftype) {
157 case H3_FT_DATA:
158 break;
159 case H3_FT_HEADERS:
160 {
161 const unsigned char *buf = (const unsigned char *)b_head(rxbuf);
162 size_t len = b_data(rxbuf);
163 struct buffer *tmp = get_trash_chunk();
Amaury Denoyelleb49fa1a2021-08-24 15:30:12 +0200164 struct ist meth = IST_NULL, path = IST_NULL;
165 struct ist scheme = IST_NULL, authority = IST_NULL;
Frédéric Lécailleccac11f2021-03-03 16:09:02 +0100166
Amaury Denoyellefd7cdc32021-08-24 15:13:20 +0200167 if (qpack_decode_fs(buf, len, tmp, list) < 0) {
Frédéric Lécailleccac11f2021-03-03 16:09:02 +0100168 h3->err = QPACK_DECOMPRESSION_FAILED;
169 return -1;
170 }
Amaury Denoyelleb49fa1a2021-08-24 15:30:12 +0200171
172 struct buffer htx_buf = BUF_NULL;
173 b_alloc(&htx_buf);
174 htx = htx_from_buf(&htx_buf);
175
176 /* first treat pseudo-header to build the start line */
177 hdr_idx = 0;
178 while (1) {
179 if (isteq(list[hdr_idx].n, ist("")))
180 break;
181
182 if (istmatch(list[hdr_idx].n, ist(":"))) {
183 /* pseudo-header */
184 if (isteq(list[hdr_idx].n, ist(":method")))
185 meth = list[hdr_idx].v;
186 else if (isteq(list[hdr_idx].n, ist(":path")))
187 path = list[hdr_idx].v;
188 else if (isteq(list[hdr_idx].n, ist(":scheme")))
189 scheme = list[hdr_idx].v;
190 else if (isteq(list[hdr_idx].n, ist(":authority")))
191 authority = list[hdr_idx].v;
192 }
193
194 ++hdr_idx;
195 }
196
197 flags |= HTX_SL_F_VER_11;
198
199 sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, meth, path, ist("HTTP/3.0"));
200 sl->flags |= HTX_SL_F_BODYLESS;
201 sl->info.req.meth = find_http_meth(meth.ptr, meth.len);
202 BUG_ON(sl->info.req.meth == HTTP_METH_OTHER);
203
204 if (isttest(authority))
205 htx_add_header(htx, ist("host"), authority);
206
207 /* now treat standard headers */
208 hdr_idx = 0;
209 while (1) {
210 if (isteq(list[hdr_idx].n, ist("")))
211 break;
212
213 if (!istmatch(list[hdr_idx].n, ist(":")))
214 htx_add_header(htx, list[hdr_idx].n, list[hdr_idx].v);
215
216 ++hdr_idx;
217 }
218
219 htx_add_endof(htx, HTX_BLK_EOH);
220 htx_to_buf(htx, &htx_buf);
Amaury Denoyelle99043552021-08-24 15:36:02 +0200221
222 cs = cs_new(qcs->qcc->conn, qcs->qcc->conn->target);
223 cs->ctx = qcs;
224 stream_create_from_cs(cs, &htx_buf);
225
226 /* buffer is transfered to conn_stream and set to NULL
227 * except on stream creation error.
228 */
Amaury Denoyelleb49fa1a2021-08-24 15:30:12 +0200229 b_free(&htx_buf);
230
Frédéric Lécailleccac11f2021-03-03 16:09:02 +0100231 break;
232 }
233 case H3_FT_PUSH_PROMISE:
234 /* Not supported */
235 break;
236 default:
237 /* Error */
238 h3->err = H3_FRAME_UNEXPECTED;
239 return -1;
240 }
241 b_del(rxbuf, flen);
242 }
243
244 return 1;
245}
246
247/* Parse a SETTINGS frame which must not be truncated with <flen> as length from
248 * <rxbuf> buffer. This function does not update this buffer.
249 * Returns 0 if something wrong happened, 1 if not.
250 */
251static int h3_parse_settings_frm(struct h3 *h3, const struct buffer *rxbuf, size_t flen)
252{
253 uint64_t id, value;
254 const unsigned char *buf, *end;
255
256 buf = (const unsigned char *)b_head(rxbuf);
257 end = buf + flen;
258
259 while (buf <= end) {
260 if (!quic_dec_int(&id, &buf, end) || !quic_dec_int(&value, &buf, end))
261 return 0;
262
263 h3_debug_printf(stderr, "%s id: %llu value: %llu\n",
264 __func__, (unsigned long long)id, (unsigned long long)value);
265 switch (id) {
266 case H3_SETTINGS_QPACK_MAX_TABLE_CAPACITY:
267 h3->qpack_max_table_capacity = value;
268 break;
269 case H3_SETTINGS_MAX_FIELD_SECTION_SIZE:
270 h3->max_field_section_size = value;
271 break;
272 case H3_SETTINGS_QPACK_BLOCKED_STREAMS:
273 h3->qpack_blocked_streams = value;
274 break;
275 case H3_SETTINGS_RESERVED_2 ... H3_SETTINGS_RESERVED_5:
276 h3->err = H3_SETTINGS_ERROR;
277 return 0;
278 default:
279 /* MUST be ignored */
280 break;
281 }
282 }
283
284 return 1;
285}
286
287/* Decode <qcs> remotely initiated uni-stream. We stop parsing a frame as soon as
288 * there is not enough received data.
289 * Returns 0 if something wrong happened, 1 if not.
290 */
291static int h3_control_recv(struct h3_uqs *h3_uqs, void *ctx)
292{
293 struct buffer *rxbuf = &h3_uqs->qcs->rx.buf;
294 struct h3 *h3 = ctx;
295
296 h3_debug_printf(stderr, "%s STREAM ID: %llu\n", __func__, h3_uqs->qcs->by_id.key);
297 if (!b_data(rxbuf))
298 return 1;
299
300 while (b_data(rxbuf)) {
301 size_t hlen;
302 uint64_t ftype, flen;
303 struct buffer b;
304
305 /* Work on a copy of <rxbuf> */
306 b = h3_b_dup(rxbuf);
307 hlen = h3_decode_frm_header(&ftype, &flen, &b);
308 if (!hlen)
309 break;
310
311 h3_debug_printf(stderr, "%s: ftype: %llu, flen: %llu\n", __func__,
312 (unsigned long long)ftype, (unsigned long long)flen);
313 if (flen > b_data(&b))
314 break;
315
316 b_del(rxbuf, hlen);
317 /* From here, a frame must not be truncated */
318 switch (ftype) {
319 case H3_FT_CANCEL_PUSH:
320 break;
321 case H3_FT_SETTINGS:
322 if (!h3_parse_settings_frm(h3, rxbuf, flen))
323 return 0;
324 break;
325 case H3_FT_GOAWAY:
326 break;
327 case H3_FT_MAX_PUSH_ID:
328 break;
329 default:
330 /* Error */
331 h3->err = H3_FRAME_UNEXPECTED;
332 return 0;
333 }
334 b_del(rxbuf, flen);
335 }
336
337 if (b_data(rxbuf))
338 h3->qcc->conn->mux->ruqs_subscribe(h3_uqs->qcs, SUB_RETRY_RECV, &h3->rctrl.wait_event);
339
340 return 1;
341}
342
343int h3_txbuf_cpy(struct h3_uqs *h3_uqs, unsigned char *buf, size_t len)
344{
345 struct buffer *res = &h3_uqs->qcs->tx.buf;
346 struct qcc *qcc = h3_uqs->qcs->qcc;
347 int ret;
348
349 ret = 0;
350 if (!h3_uqs_get_buf(h3_uqs)) {
351 qcc->flags |= OUQS_SF_TXBUF_MALLOC;
352 goto out;
353 }
354
355 ret = b_istput(res, ist2((char *)buf, len));
356 if (unlikely(!ret))
357 qcc->flags |= OUQS_SF_TXBUF_FULL;
358
359 out:
360 return ret;
361}
362
363/* Function used to emit stream data from <h3_uqs> control uni-stream */
364static int h3_control_send(struct h3_uqs *h3_uqs, void *ctx)
365{
366 int ret;
367 struct h3 *h3 = ctx;
368 unsigned char data[(2 + 3) * 2 * QUIC_VARINT_MAX_SIZE]; /* enough for 3 settings */
369 unsigned char *pos, *end;
370
371 ret = 0;
372 pos = data;
373 end = pos + sizeof data;
374 if (!(h3->flags & H3_CF_SETTINGS_SENT)) {
375 struct qcs *qcs = h3_uqs->qcs;
376 struct buffer *txbuf = &qcs->tx.buf;
377 size_t frm_len;
378
379 frm_len = quic_int_getsize(H3_SETTINGS_QPACK_MAX_TABLE_CAPACITY) +
380 quic_int_getsize(h3_settings_qpack_max_table_capacity) +
381 quic_int_getsize(H3_SETTINGS_QPACK_BLOCKED_STREAMS) +
382 quic_int_getsize(h3_settings_qpack_blocked_streams);
383 if (h3_settings_max_field_section_size) {
384 frm_len += quic_int_getsize(H3_SETTINGS_MAX_FIELD_SECTION_SIZE) +
385 quic_int_getsize(h3_settings_max_field_section_size);
386 }
387
388 quic_enc_int(&pos, end, H3_UNI_STRM_TP_CONTROL_STREAM);
389 /* Build a SETTINGS frame */
390 quic_enc_int(&pos, end, H3_FT_SETTINGS);
391 quic_enc_int(&pos, end, frm_len);
392 quic_enc_int(&pos, end, H3_SETTINGS_QPACK_MAX_TABLE_CAPACITY);
393 quic_enc_int(&pos, end, h3_settings_qpack_max_table_capacity);
394 quic_enc_int(&pos, end, H3_SETTINGS_QPACK_BLOCKED_STREAMS);
395 quic_enc_int(&pos, end, h3_settings_qpack_blocked_streams);
396 if (h3_settings_max_field_section_size) {
397 quic_enc_int(&pos, end, H3_SETTINGS_MAX_FIELD_SECTION_SIZE);
398 quic_enc_int(&pos, end, h3_settings_max_field_section_size);
399 }
400 ret = h3_txbuf_cpy(h3_uqs, data, pos - data);
401 if (ret < 0) {
402 qc_error(qcs->qcc, H3_INTERNAL_ERROR);
403 return ret;
404 }
405
406 if (ret > 0) {
407 h3->flags |= H3_CF_SETTINGS_SENT;
408 luqs_snd_buf(h3_uqs->qcs, txbuf, b_data(&qcs->tx.buf), 0);
409 }
410 if (b_data(&qcs->tx.buf))
411 qcs->qcc->conn->mux->luqs_subscribe(qcs, SUB_RETRY_SEND, &h3->lctrl.wait_event);
412 }
413
414 return ret;
415}
416
Amaury Denoyellef52151d2021-08-24 16:11:18 +0200417/* Return next empty buffer of mux.
418 * TODO to optimize memory consumption, a non-full buffer should be used before
419 * allocating a new one.
420 * TODO put this in mux ??
421 */
422static struct buffer *get_mux_next_tx_buf(struct qcs *qcs)
423{
424 struct buffer *buf = br_tail(qcs->tx.mbuf);
425
426 if (b_data(buf))
427 buf = br_tail_add(qcs->tx.mbuf);
428
429 if (!b_size(buf))
430 qc_get_buf(qcs->qcc, buf);
431
432 if (!buf)
433 ABORT_NOW();
434
435 return buf;
Amaury Denoyelle26dfd902021-08-24 16:33:53 +0200436}
437
438size_t h3_snd_buf(struct conn_stream *cs, struct buffer *buf, size_t count, int flags)
439{
440 size_t total = 0;
441 struct qcs *qcs = cs->ctx;
442 struct htx *htx;
443 enum htx_blk_type btype;
444 struct htx_blk *blk;
445 uint32_t bsize;
446 int32_t idx;
447 int ret;
448
449 htx = htx_from_buf(buf);
450
451 while (count && !htx_is_empty(htx)) {
452 idx = htx_get_head(htx);
453 blk = htx_get_blk(htx, idx);
454 btype = htx_get_blk_type(blk);
455 bsize = htx_get_blksz(blk);
456
457 /* Not implemented : QUIC on backend side */
458 BUG_ON(btype == HTX_BLK_REQ_SL);
459
460 switch (btype) {
461 case HTX_BLK_RES_SL:
462 /* TODO HEADERS h3 frame */
463
464 case HTX_BLK_DATA:
465 /* TODO DATA h3 frame */
466
467 case HTX_BLK_TLR:
468 case HTX_BLK_EOT:
469 /* TODO trailers */
470
471 default:
472 htx_remove_blk(htx, blk);
473 total += bsize;
474 count -= bsize;
475 break;
476 }
477 }
478
479 // TODO should I call the mux directly here ?
480 qc_snd_buf(cs, buf, total, flags);
Amaury Denoyellef52151d2021-08-24 16:11:18 +0200481
Amaury Denoyelle26dfd902021-08-24 16:33:53 +0200482 out:
483 return total;
Amaury Denoyellef52151d2021-08-24 16:11:18 +0200484}
485
Frédéric Lécailleccac11f2021-03-03 16:09:02 +0100486/* Finalize the initialization of remotely initiated uni-stream <qcs>.
487 * Return 1 if succeeded, 0 if not. In this latter case, set the ->err h3 error
488 * to inform the QUIC mux layer of the encountered error.
489 */
490static int h3_attach_ruqs(struct qcs *qcs, void *ctx)
491{
492 uint64_t strm_type;
493 struct h3 *h3 = ctx;
494 struct buffer *rxbuf = &qcs->rx.buf;
495
496 /* First octets: the uni-stream type */
497 if (!b_quic_dec_int(&strm_type, rxbuf, NULL) || strm_type > H3_UNI_STRM_TP_MAX)
498 return 0;
499
500 /* Note that for all the uni-streams below, this is an error to receive two times the
501 * same type of uni-stream (even for Push stream which is not supported at this time.
502 */
503 switch (strm_type) {
504 case H3_UNI_STRM_TP_CONTROL_STREAM:
505 if (h3->rctrl.qcs) {
506 h3->err = H3_STREAM_CREATION_ERROR;
507 return 0;
508 }
509
510 h3->rctrl.qcs = qcs;
511 h3->rctrl.cb = h3_control_recv;
512 h3->qcc->conn->mux->ruqs_subscribe(qcs, SUB_RETRY_RECV, &h3->rctrl.wait_event);
513 break;
514 case H3_UNI_STRM_TP_PUSH_STREAM:
515 /* NOT SUPPORTED */
516 break;
517 case H3_UNI_STRM_TP_QPACK_ENCODER:
518 if (h3->rqpack_enc.qcs) {
519 h3->err = H3_STREAM_CREATION_ERROR;
520 return 0;
521 }
522
523 h3->rqpack_enc.qcs = qcs;
524 h3->rqpack_enc.cb = qpack_decode_enc;
525 h3->qcc->conn->mux->ruqs_subscribe(qcs, SUB_RETRY_RECV, &h3->rqpack_enc.wait_event);
526 break;
527 case H3_UNI_STRM_TP_QPACK_DECODER:
528 if (h3->rqpack_dec.qcs) {
529 h3->err = H3_STREAM_CREATION_ERROR;
530 return 0;
531 }
532
533 h3->rqpack_dec.qcs = qcs;
534 h3->rqpack_dec.cb = qpack_decode_dec;
535 h3->qcc->conn->mux->ruqs_subscribe(qcs, SUB_RETRY_RECV, &h3->rqpack_dec.wait_event);
536 break;
537 default:
538 /* Error */
539 h3->err = H3_STREAM_CREATION_ERROR;
540 return 0;
541 }
542
543 return 1;
544}
545
546static int h3_finalize(void *ctx)
547{
548 struct h3 *h3 = ctx;
549
550 h3->lctrl.qcs = luqs_new(h3->qcc);
551 if (!h3->lctrl.qcs)
552 return 0;
553
554 /* Wakeup ->lctrl uni-stream */
Frédéric Lécaillee16f0bd2021-08-23 09:50:29 +0200555 h3_control_send(&h3->lctrl, h3);
Frédéric Lécailleccac11f2021-03-03 16:09:02 +0100556
557 return 1;
558}
559
560/* Tasklet dedicated to h3 incoming uni-streams */
561static struct task *h3_uqs_task(struct task *t, void *ctx, unsigned int state)
562{
563 struct h3_uqs *h3_uqs = ctx;
564 struct h3 *h3 = h3_uqs->qcs->qcc->ctx;
565
566 h3_uqs->cb(h3_uqs, h3);
567 return NULL;
568}
569
570#if 0
571/* Initialiaze <h3_uqs> uni-stream with <t> as tasklet */
572static int h3_uqs_init(struct h3_uqs *h3_uqs,
573 struct task *(*t)(struct task *, void *, unsigned int))
574{
575 h3_uqs->qcs = NULL;
576 h3_uqs->cb = NULL;
577 h3_uqs->wait_event.tasklet = tasklet_new();
578 if (!h3_uqs->wait_event.tasklet)
579 return 0;
580
581 h3_uqs->wait_event.tasklet->process = t;
582 h3_uqs->wait_event.tasklet->context = h3_uqs;
583 return 1;
584}
585#endif
586
587/* Release all the tasklet attached to <h3_uqs> uni-stream */
588static inline void h3_uqs_tasklet_release(struct h3_uqs *h3_uqs)
589{
590 struct tasklet *t = h3_uqs->wait_event.tasklet;
591
592 if (t)
593 tasklet_free(t);
594}
595
596/* Release all the tasklet attached to <h3> uni-streams */
597static void h3_uqs_tasklets_release(struct h3 *h3)
598{
599 h3_uqs_tasklet_release(&h3->rqpack_enc);
600 h3_uqs_tasklet_release(&h3->rqpack_dec);
601 h3_uqs_tasklet_release(&h3->rctrl);
602}
603
604/* Tasklet dedicated to h3 outgoing uni-streams */
605__maybe_unused
606static struct task *h3_uqs_send_task(struct task *t, void *ctx, unsigned int state)
607{
608 struct h3_uqs *h3_uqs = ctx;
609 struct h3 *h3 = h3_uqs->qcs->qcc->ctx;
610
611 h3_uqs->cb(h3_uqs, h3);
612 return NULL;
613}
614
615/* Initialiaze <h3_uqs> uni-stream with <t> as tasklet */
616static int h3_uqs_init(struct h3_uqs *h3_uqs, struct h3 *h3,
617 int (*cb)(struct h3_uqs *h3_uqs, void *ctx),
618 struct task *(*t)(struct task *, void *, unsigned int))
619{
620 h3_uqs->qcs = NULL;
621 h3_uqs->cb = cb;
622 h3_uqs->wait_event.tasklet = tasklet_new();
623 if (!h3_uqs->wait_event.tasklet)
624 return 0;
625
626 h3_uqs->wait_event.tasklet->process = t;
627 h3_uqs->wait_event.tasklet->context = h3_uqs;
628 return 1;
629
630 err:
631 tasklet_free(h3_uqs->wait_event.tasklet);
632 return 0;
633}
634
635static inline void h3_uqs_release(struct h3_uqs *h3_uqs)
636{
637 if (h3_uqs->qcs)
638 qcs_release(h3_uqs->qcs);
639}
640
641static inline void h3_uqs_release_all(struct h3 *h3)
642{
643 h3_uqs_tasklet_release(&h3->lctrl);
644 h3_uqs_release(&h3->lctrl);
645 h3_uqs_tasklet_release(&h3->lqpack_enc);
646 h3_uqs_release(&h3->lqpack_enc);
647 h3_uqs_tasklet_release(&h3->lqpack_dec);
648 h3_uqs_release(&h3->lqpack_dec);
649}
650
651/* Initialize the HTTP/3 context for <qcc> mux.
652 * Return 1 if succeeded, 0 if not.
653 */
654static int h3_init(struct qcc *qcc)
655{
656 struct h3 *h3;
657
658 h3 = pool_alloc(pool_head_h3);
659 if (!h3)
660 goto fail_no_h3;
661
662 h3->qcc = qcc;
663 h3->err = H3_NO_ERROR;
664 h3->flags = 0;
665
666 if (!h3_uqs_init(&h3->rqpack_enc, h3, NULL, h3_uqs_task) ||
667 !h3_uqs_init(&h3->rqpack_dec, h3, NULL, h3_uqs_task) ||
668 !h3_uqs_init(&h3->rctrl, h3, h3_control_recv, h3_uqs_task))
669 goto fail_no_h3_ruqs;
670
671 if (!h3_uqs_init(&h3->lctrl, h3, h3_control_send, h3_uqs_task) ||
672 !h3_uqs_init(&h3->lqpack_enc, h3, NULL, h3_uqs_task) ||
673 !h3_uqs_init(&h3->lqpack_dec, h3, NULL, h3_uqs_task))
674 goto fail_no_h3_luqs;
675
676 qcc->ctx = h3;
677 LIST_INIT(&h3->buf_wait.list);
678
679 return 1;
680
681 fail_no_h3_ruqs:
682 h3_uqs_release_all(h3);
683 fail_no_h3_luqs:
684 h3_uqs_tasklets_release(h3);
685 pool_free(pool_head_h3, h3);
686 fail_no_h3:
687 return 0;
688}
689
690/* HTTP/3 application layer operations */
691const struct qcc_app_ops h3_ops = {
692 .init = h3_init,
693 .attach_ruqs = h3_attach_ruqs,
694 .decode_qcs = h3_decode_qcs,
695 .finalize = h3_finalize,
696};