lsquic_qdec_hdl.c revision a74702c6
1/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc.  See LICENSE. */
2/*
3 * lsquic_qdec_hdl.c -- QPACK decoder streams handler
4 */
5
6#include <assert.h>
7#include <errno.h>
8#include <inttypes.h>
9#include <stdlib.h>
10#include <string.h>
11#include <sys/queue.h>
12
13#include "lsquic.h"
14#include "lsquic_types.h"
15#include "lsxpack_header.h"
16#include "lsquic_int_types.h"
17#include "lsquic_sfcw.h"
18#include "lsquic_varint.h"
19#include "lsquic_hq.h"
20#include "lsquic_hash.h"
21#include "lsquic_stream.h"
22#include "lsquic_frab_list.h"
23#include "lsqpack.h"
24#include "lsquic_http1x_if.h"
25#include "lsquic_qdec_hdl.h"
26#include "lsquic_mm.h"
27#include "lsquic_engine_public.h"
28#include "lsquic_headers.h"
29#include "lsquic_conn.h"
30#include "lsquic_conn_flow.h"
31#include "lsquic_rtt.h"
32#include "lsquic_conn_public.h"
33#include "lsquic_hq.h"
34#include "lsquic_parse.h"
35#include "lsquic_qpack_exp.h"
36#include "lsquic_util.h"
37
38#define LSQUIC_LOGGER_MODULE LSQLM_QDEC_HDL
39#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(qdh->qdh_conn)
40#include "lsquic_logger.h"
41
42static const struct lsqpack_dec_hset_if dhi_if;
43
44
45struct header_ctx
46{
47    void                    *hset;
48    struct qpack_dec_hdl    *qdh;
49    enum ppc_flags           ppc_flags;
50    struct lsquic_ext_http_prio ehp;
51};
52
53
54/* We need to allocate struct uncompressed_headers anyway when header set
55 * is complete and we give it to the stream using lsquic_stream_uh_in().
56 * To save a malloc, we reuse context after we're done with it.
57 */
58union hblock_ctx
59{
60    struct header_ctx ctx;
61    unsigned char     space_for_uh[sizeof(struct uncompressed_headers)];
62};
63
64
65static int
66qdh_write_decoder (struct qpack_dec_hdl *qdh, const unsigned char *buf,
67                                                                size_t sz)
68{
69    ssize_t nw;
70
71    if (!(qdh->qdh_dec_sm_out && lsquic_frab_list_empty(&qdh->qdh_fral)))
72    {
73  write_to_frab:
74        if (0 == lsquic_frab_list_write(&qdh->qdh_fral,
75                                                (unsigned char *) buf, sz))
76        {
77            LSQ_DEBUG("wrote %zu bytes to frab list", sz);
78            lsquic_stream_wantwrite(qdh->qdh_dec_sm_out, 1);
79            return 0;
80        }
81        else
82        {
83            LSQ_INFO("error writing to frab list");
84            return -1;
85        }
86    }
87
88    nw = lsquic_stream_write(qdh->qdh_dec_sm_out, buf, sz);
89    if (nw < 0)
90    {
91        LSQ_INFO("error writing to outgoing QPACK decoder stream: %s",
92                                                        strerror(errno));
93        return -1;
94    }
95    LSQ_DEBUG("wrote %zd bytes to outgoing QPACK decoder stream", nw);
96
97    if ((size_t) nw == sz)
98        return 0;
99
100    buf = buf + nw;
101    sz -= (size_t) nw;
102    goto write_to_frab;
103}
104
105
106static int
107qdh_write_type (struct qpack_dec_hdl *qdh)
108{
109    int s;
110
111#ifndef NDEBUG
112    const char *env = getenv("LSQUIC_RND_VARINT_LEN");
113    if (env && atoi(env))
114    {
115        s = rand() & 3;
116        LSQ_DEBUG("writing %d-byte stream type", 1 << s);
117    }
118    else
119#endif
120        s = 0;
121
122    switch (s)
123    {
124    case 0:
125        return qdh_write_decoder(qdh,
126                                (unsigned char []) { HQUST_QPACK_DEC }, 1);
127    case 1:
128        return qdh_write_decoder(qdh,
129                            (unsigned char []) { 0x40, HQUST_QPACK_DEC }, 2);
130    case 2:
131        return qdh_write_decoder(qdh,
132                (unsigned char []) { 0x80, 0x00, 0x00, HQUST_QPACK_DEC }, 4);
133    default:
134        return qdh_write_decoder(qdh,
135                (unsigned char []) { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
136                                                        HQUST_QPACK_DEC }, 8);
137    }
138}
139
140
141static void
142qdh_begin_out (struct qpack_dec_hdl *qdh)
143{
144    if (0 != qdh_write_type(qdh))
145    {
146        LSQ_WARN("%s: could not write to decoder", __func__);
147        qdh->qdh_conn->cn_if->ci_internal_error(qdh->qdh_conn,
148                                        "cannot write to decoder stream");
149    }
150}
151
152
153int
154lsquic_qdh_init (struct qpack_dec_hdl *qdh, struct lsquic_conn *conn,
155                    int is_server, const struct lsquic_engine_public *enpub,
156                    unsigned dyn_table_size, unsigned max_risked_streams)
157{
158    enum lsqpack_dec_opts dec_opts;
159
160    dec_opts = 0;
161    if (enpub->enp_hsi_if->hsi_flags & LSQUIC_HSI_HTTP1X)
162        dec_opts |= LSQPACK_DEC_OPT_HTTP1X;
163    if (enpub->enp_hsi_if->hsi_flags & LSQUIC_HSI_HASH_NAME)
164        dec_opts |= LSQPACK_DEC_OPT_HASH_NAME;
165    if (enpub->enp_hsi_if->hsi_flags & LSQUIC_HSI_HASH_NAMEVAL)
166        dec_opts |= LSQPACK_DEC_OPT_HASH_NAMEVAL;
167
168    if (conn->cn_flags & LSCONN_SERVER)
169        qdh->qdh_flags |= QDH_SERVER;
170    if (enpub->enp_settings.es_qpack_experiment)
171    {
172        qdh->qdh_exp_rec = lsquic_qpack_exp_new();
173        if (qdh->qdh_exp_rec)
174        {
175            if (conn->cn_flags & LSCONN_SERVER)
176                qdh->qdh_exp_rec->qer_flags |= QER_SERVER;
177            qdh->qdh_exp_rec->qer_used_max_size = dyn_table_size;
178            qdh->qdh_exp_rec->qer_used_max_blocked = max_risked_streams;
179        }
180    }
181    if (!qdh->qdh_exp_rec && LSQ_LOG_ENABLED_EXT(LSQ_LOG_NOTICE, LSQLM_CONN))
182        qdh->qdh_flags |= QDH_SAVE_UA;
183
184    qdh->qdh_conn = conn;
185    lsquic_frab_list_init(&qdh->qdh_fral, 0x400, NULL, NULL, NULL);
186    lsqpack_dec_init(&qdh->qdh_decoder, (void *) conn, dyn_table_size,
187                        max_risked_streams, &dhi_if, dec_opts);
188    qdh->qdh_flags |= QDH_INITIALIZED;
189    qdh->qdh_enpub = enpub;
190    if (qdh->qdh_enpub->enp_hsi_if == lsquic_http1x_if)
191    {
192        qdh->qdh_h1x_ctor_ctx = (struct http1x_ctor_ctx) {
193            .conn           = conn,
194            .max_headers_sz = MAX_HTTP1X_HEADERS_SIZE,
195            .is_server      = is_server,
196        };
197        qdh->qdh_hsi_ctx = &qdh->qdh_h1x_ctor_ctx;
198    }
199    else
200        qdh->qdh_hsi_ctx = qdh->qdh_enpub->enp_hsi_ctx;
201    if (qdh->qdh_dec_sm_out)
202        qdh_begin_out(qdh);
203    if (qdh->qdh_enc_sm_in)
204        lsquic_stream_wantread(qdh->qdh_enc_sm_in, 1);
205    LSQ_DEBUG("initialized");
206    return 0;
207}
208
209
210static void
211qdh_log_and_clean_exp_rec (struct qpack_dec_hdl *qdh)
212{
213    char buf[0x400];
214
215    qdh->qdh_exp_rec->qer_comp_ratio = lsqpack_dec_ratio(&qdh->qdh_decoder);
216    /* Naughty: poking inside the decoder, it's not exposed.  (Should it be?) */
217    qdh->qdh_exp_rec->qer_peer_max_size = qdh->qdh_decoder.qpd_cur_max_capacity;
218    (void) lsquic_qpack_exp_to_xml(qdh->qdh_exp_rec, buf, sizeof(buf));
219    LSQ_NOTICE("%s", buf);
220    lsquic_qpack_exp_destroy(qdh->qdh_exp_rec);
221    qdh->qdh_exp_rec = NULL;
222}
223
224
225void
226lsquic_qdh_cleanup (struct qpack_dec_hdl *qdh)
227{
228    if (qdh->qdh_flags & QDH_INITIALIZED)
229    {
230        LSQ_DEBUG("cleanup");
231        if (qdh->qdh_exp_rec)
232            qdh_log_and_clean_exp_rec(qdh);
233        if (qdh->qdh_ua)
234        {
235            free(qdh->qdh_ua);
236            qdh->qdh_ua = NULL;
237        }
238        lsqpack_dec_cleanup(&qdh->qdh_decoder);
239        lsquic_frab_list_cleanup(&qdh->qdh_fral);
240        qdh->qdh_flags &= ~QDH_INITIALIZED;
241    }
242}
243
244static lsquic_stream_ctx_t *
245qdh_out_on_new (void *stream_if_ctx, struct lsquic_stream *stream)
246{
247    struct qpack_dec_hdl *const qdh = stream_if_ctx;
248    qdh->qdh_dec_sm_out = stream;
249    if (qdh->qdh_flags & QDH_INITIALIZED)
250        qdh_begin_out(qdh);
251    LSQ_DEBUG("initialized outgoing decoder stream");
252    return (void *) qdh;
253}
254
255
256static void
257qdh_out_on_write (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx)
258{
259    struct qpack_dec_hdl *const qdh = (void *) ctx;
260    struct lsquic_reader reader;
261    ssize_t nw;
262    unsigned char buf[LSQPACK_LONGEST_ICI];
263
264    if (lsqpack_dec_ici_pending(&qdh->qdh_decoder))
265    {
266        nw = lsqpack_dec_write_ici(&qdh->qdh_decoder, buf, sizeof(buf));
267        if (nw > 0)
268        {
269            if (0 == qdh_write_decoder(qdh, buf, nw))
270                LSQ_DEBUG("wrote %zd-byte TSS instruction", nw);
271            else
272                goto err;
273        }
274        else if (nw < 0)
275        {
276            LSQ_WARN("could not generate TSS instruction");
277            goto err;
278        }
279    }
280
281    if (lsquic_frab_list_empty(&qdh->qdh_fral))
282    {
283        LSQ_DEBUG("%s: nothing to write", __func__);
284        lsquic_stream_wantwrite(stream, 0);
285        return;
286    }
287
288    reader = (struct lsquic_reader) {
289        .lsqr_read  = lsquic_frab_list_read,
290        .lsqr_size  = lsquic_frab_list_size,
291        .lsqr_ctx   = &qdh->qdh_fral,
292    };
293
294    nw = lsquic_stream_writef(stream, &reader);
295    if (nw >= 0)
296    {
297        LSQ_DEBUG("wrote %zd bytes to stream", nw);
298        (void) lsquic_stream_flush(stream);
299        if (lsquic_frab_list_empty(&qdh->qdh_fral))
300        {
301            lsquic_stream_wantwrite(stream, 0);
302            if (qdh->qdh_on_dec_sent_func)
303            {
304                LSQ_DEBUG("buffered data written: call callback");
305                qdh->qdh_on_dec_sent_func(qdh->qdh_on_dec_sent_ctx);
306                qdh->qdh_on_dec_sent_func = NULL;
307                qdh->qdh_on_dec_sent_ctx = NULL;
308            }
309        }
310    }
311    else
312    {
313        LSQ_WARN("cannot write to stream: %s", strerror(errno));
314  err:
315        lsquic_stream_wantwrite(stream, 0);
316        qdh->qdh_conn->cn_if->ci_internal_error(qdh->qdh_conn,
317                                        "cannot write to stream");
318    }
319}
320
321
322static void
323qdh_out_on_close (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx)
324{
325    struct qpack_dec_hdl *const qdh = (void *) ctx;
326    qdh->qdh_dec_sm_out = NULL;
327    LSQ_DEBUG("closed outgoing decoder stream");
328}
329
330
331static void
332qdh_out_on_read (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx)
333{
334    assert(0);
335}
336
337
338static const struct lsquic_stream_if qdh_dec_sm_out_if =
339{
340    .on_new_stream  = qdh_out_on_new,
341    .on_read        = qdh_out_on_read,
342    .on_write       = qdh_out_on_write,
343    .on_close       = qdh_out_on_close,
344};
345const struct lsquic_stream_if *const lsquic_qdh_dec_sm_out_if =
346                                                    &qdh_dec_sm_out_if;
347
348
349static lsquic_stream_ctx_t *
350qdh_in_on_new (void *stream_if_ctx, struct lsquic_stream *stream)
351{
352    struct qpack_dec_hdl *const qdh = stream_if_ctx;
353    qdh->qdh_enc_sm_in = stream;
354    if (qdh->qdh_flags & QDH_INITIALIZED)
355        lsquic_stream_wantread(qdh->qdh_enc_sm_in, 1);
356    LSQ_DEBUG("initialized incoming encoder stream");
357    return (void *) qdh;
358}
359
360
361static size_t
362qdh_read_encoder_stream (void *ctx, const unsigned char *buf, size_t sz,
363                                                                    int fin)
364{
365    struct qpack_dec_hdl *const qdh = (void *) ctx;
366    const struct lsqpack_dec_err *qerr;
367    int s;
368
369    if (fin)
370    {
371        LSQ_INFO("encoder stream is closed");
372        qdh->qdh_conn->cn_if->ci_abort_error(qdh->qdh_conn, 1,
373            HEC_CLOSED_CRITICAL_STREAM, "Peer closed QPACK encoder stream");
374        goto end;
375    }
376
377    s = lsqpack_dec_enc_in(&qdh->qdh_decoder, buf, sz);
378    if (s != 0)
379    {
380        LSQ_INFO("error reading encoder stream");
381        qerr = lsqpack_dec_get_err_info(&qdh->qdh_decoder);
382        qdh->qdh_conn->cn_if->ci_abort_error(qdh->qdh_conn, 1,
383            HEC_QPACK_ENCODER_STREAM_ERROR, "Error interpreting QPACK encoder "
384            "stream; offset %"PRIu64", line %d", qerr->off, qerr->line);
385        goto end;
386    }
387    if (qdh->qdh_dec_sm_out
388                    && lsqpack_dec_ici_pending(&qdh->qdh_decoder))
389        lsquic_stream_wantwrite(qdh->qdh_dec_sm_out, 1);
390
391    LSQ_DEBUG("successfully fed %zu bytes to QPACK decoder", sz);
392
393  end:
394    return sz;
395}
396
397
398static void
399qdh_in_on_read (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx)
400{
401    struct qpack_dec_hdl *const qdh = (void *) ctx;
402    ssize_t nread;
403
404    nread = lsquic_stream_readf(stream, qdh_read_encoder_stream, qdh);
405    if (nread <= 0)
406    {
407        if (nread < 0)
408        {
409            LSQ_WARN("cannot read from encoder stream: %s", strerror(errno));
410            qdh->qdh_conn->cn_if->ci_internal_error(qdh->qdh_conn,
411                                        "cannot read from encoder stream");
412        }
413        else
414        {
415            LSQ_INFO("encoder stream closed by peer: abort connection");
416            qdh->qdh_conn->cn_if->ci_abort_error(qdh->qdh_conn, 1,
417                HEC_CLOSED_CRITICAL_STREAM, "encoder stream closed");
418        }
419        lsquic_stream_wantread(stream, 0);
420    }
421}
422
423
424static void
425qdh_in_on_close (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx)
426{
427    struct qpack_dec_hdl *const qdh = (void *) ctx;
428    LSQ_DEBUG("closed incoming encoder stream");
429    qdh->qdh_enc_sm_in = NULL;
430}
431
432
433static void
434qdh_in_on_write (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx)
435{
436    assert(0);
437}
438
439
440static const struct lsquic_stream_if qdh_enc_sm_in_if =
441{
442    .on_new_stream  = qdh_in_on_new,
443    .on_read        = qdh_in_on_read,
444    .on_write       = qdh_in_on_write,
445    .on_close       = qdh_in_on_close,
446};
447const struct lsquic_stream_if *const lsquic_qdh_enc_sm_in_if =
448                                                    &qdh_enc_sm_in_if;
449
450
451static void
452qdh_hblock_unblocked (void *stream_p)
453{
454    struct lsquic_stream *const stream = stream_p;
455    lsquic_stream_qdec_unblocked(stream);
456}
457
458
459struct cont_len
460{
461    unsigned long long      value;
462    int                     has;    /* 1: set, 0: not set, -1: invalid */
463};
464
465
466static void
467process_content_length (const struct qpack_dec_hdl *qdh /* for logging */,
468            struct cont_len *cl, const char *val /* not NUL-terminated */,
469                                                                unsigned len)
470{
471    char *endcl, cont_len_buf[30];
472
473    if (0 == cl->has)
474    {
475        if (len >= sizeof(cont_len_buf))
476        {
477            LSQ_DEBUG("content-length has invalid value `%.*s'",
478                                                            (int) len, val);
479            cl->has = -1;
480            return;
481        }
482        memcpy(cont_len_buf, val, len);
483        cont_len_buf[len] = '\0';
484        cl->value = strtoull(cont_len_buf, &endcl, 10);
485        if (*endcl == '\0' && !(ULLONG_MAX == cl->value && ERANGE == errno))
486        {
487            cl->has = 1;
488            LSQ_DEBUG("content length is %llu", cl->value);
489        }
490        else
491        {
492            cl->has = -1;
493            LSQ_DEBUG("content-length has invalid value `%.*s'",
494                (int) len, val);
495        }
496    }
497    else if (cl->has > 0)
498    {
499        LSQ_DEBUG("header set has two content-length: ambiguous, "
500            "turn off checking");
501        cl->has = -1;
502    }
503}
504
505
506static int
507is_content_length (const struct lsxpack_header *xhdr)
508{
509    return ((xhdr->flags & LSXPACK_QPACK_IDX)
510                        && xhdr->qpack_index == LSQPACK_TNV_CONTENT_LENGTH_0)
511        || (xhdr->name_len == 14 && 0 == memcmp(lsxpack_header_get_name(xhdr),
512                                                        "content-length", 13))
513        ;
514}
515
516
517static int
518is_priority (const struct lsxpack_header *xhdr)
519{
520    return xhdr->name_len == 8
521        && 0 == memcmp(lsxpack_header_get_name(xhdr), "priority", 8);
522}
523
524
525static struct lsxpack_header *
526qdh_prepare_decode (void *stream_p, struct lsxpack_header *xhdr, size_t space)
527{
528    struct lsquic_stream *const stream = stream_p;
529    union hblock_ctx *const u = stream->sm_hblock_ctx;
530    struct qpack_dec_hdl *const qdh = u->ctx.qdh;
531
532    return qdh->qdh_enpub->enp_hsi_if->hsi_prepare_decode(
533                                                u->ctx.hset, xhdr, space);
534}
535
536
537static void
538qdh_maybe_set_user_agent (struct qpack_dec_hdl *qdh,
539                                const struct lsxpack_header *xhdr, char **ua)
540{
541    /* Flipped: we are the *decoder* */
542    const char *const name = qdh->qdh_flags & QDH_SERVER ?
543                                    "user-agent" : "server";
544    const size_t len = qdh->qdh_flags & QDH_SERVER ? 10 : 6;
545
546    if (len == xhdr->name_len
547                && 0 == memcmp(name, lsxpack_header_get_name(xhdr), len))
548        *ua = strndup(lsxpack_header_get_value(xhdr), xhdr->val_len);
549}
550
551
552/* Intercept header errors so that upper-layer errors do not get
553 * misinterpreted as QPACK errors.
554 */
555static int
556qdh_hsi_process_wrapper (struct qpack_dec_hdl *qdh, void *hset,
557                                                struct lsxpack_header *xhdr)
558{
559    int retval;
560
561    retval = qdh->qdh_enpub->enp_hsi_if->hsi_process_header(hset, xhdr);
562    if (0 != retval)
563        qdh->qdh_conn->cn_if->ci_abort_error(qdh->qdh_conn, 1,
564            HEC_MESSAGE_ERROR,
565            "error processing headers");
566
567    return retval;
568}
569
570
571static int
572qdh_process_header (void *stream_p, struct lsxpack_header *xhdr)
573{
574    struct lsquic_stream *const stream = stream_p;
575    union hblock_ctx *const u = stream->sm_hblock_ctx;
576    struct qpack_dec_hdl *const qdh = u->ctx.qdh;
577    struct cont_len cl;
578
579    if (is_content_length(xhdr))
580    {
581        cl.has = 0;
582        process_content_length(qdh, &cl, lsxpack_header_get_value(xhdr),
583                                                            xhdr->val_len);
584        if (cl.has > 0)
585            (void) lsquic_stream_verify_len(stream, cl.value);
586    }
587    else if ((stream->sm_bflags & (SMBF_HTTP_PRIO|SMBF_HPRIO_SET))
588                                                            == SMBF_HTTP_PRIO
589            && is_priority(xhdr))
590    {
591        u->ctx.ppc_flags &= ~(PPC_INC_NAME|PPC_URG_NAME);
592        (void) lsquic_http_parse_pfv(lsxpack_header_get_value(xhdr),
593                        xhdr->val_len, &u->ctx.ppc_flags, &u->ctx.ehp,
594                        (char *) stream->conn_pub->mm->acki,
595                        sizeof(*stream->conn_pub->mm->acki));
596    }
597    else if (qdh->qdh_exp_rec && !qdh->qdh_exp_rec->qer_user_agent)
598        qdh_maybe_set_user_agent(qdh, xhdr, &qdh->qdh_exp_rec->qer_user_agent);
599    else if ((qdh->qdh_flags & QDH_SAVE_UA) && !qdh->qdh_ua)
600        qdh_maybe_set_user_agent(qdh, xhdr, &qdh->qdh_ua);
601
602    return qdh_hsi_process_wrapper(qdh, u->ctx.hset, xhdr);
603}
604
605
606static const struct lsqpack_dec_hset_if dhi_if =
607{
608    .dhi_unblocked      = qdh_hblock_unblocked,
609    .dhi_prepare_decode = qdh_prepare_decode,
610    .dhi_process_header = qdh_process_header,
611};
612
613
614static void
615qdh_maybe_destroy_hblock_ctx (struct qpack_dec_hdl *qdh,
616                                                struct lsquic_stream *stream)
617{
618    if (stream->sm_hblock_ctx)
619    {
620        LSQ_DEBUG("destroy hblock_ctx of stream %"PRIu64, stream->id);
621        qdh->qdh_enpub->enp_hsi_if->hsi_discard_header_set(
622                                            stream->sm_hblock_ctx->ctx.hset);
623        free(stream->sm_hblock_ctx);
624        stream->sm_hblock_ctx = NULL;
625    }
626}
627
628
629static enum lsqpack_read_header_status
630qdh_header_read_results (struct qpack_dec_hdl *qdh,
631        struct lsquic_stream *stream, enum lsqpack_read_header_status rhs,
632        const unsigned char *dec_buf, size_t dec_buf_sz)
633{
634    const struct lsqpack_dec_err *qerr;
635    struct uncompressed_headers *uh;
636    void *hset;
637
638    if (rhs == LQRHS_DONE)
639    {
640        if (1)    //!lsquic_stream_header_is_trailer(stream))
641        {
642            if (stream->sm_hblock_ctx->ctx.ppc_flags
643                                                & (PPC_INC_SET|PPC_URG_SET))
644            {
645                assert(stream->sm_bflags & SMBF_HTTP_PRIO);
646                LSQ_DEBUG("Apply Priority from headers to stream %"PRIu64,
647                                                                stream->id);
648                (void) lsquic_stream_set_http_prio(stream,
649                                            &stream->sm_hblock_ctx->ctx.ehp);
650            }
651            hset = stream->sm_hblock_ctx->ctx.hset;
652            uh = (void *) stream->sm_hblock_ctx;
653            stream->sm_hblock_ctx = NULL;
654            memset(uh, 0, sizeof(*uh));
655            uh->uh_stream_id = stream->id;
656            uh->uh_oth_stream_id = 0;
657            uh->uh_weight = 0;
658            uh->uh_exclusive = -1;
659            if (qdh->qdh_enpub->enp_hsi_if == lsquic_http1x_if)
660                uh->uh_flags    |= UH_H1H;
661            if (0 != qdh_hsi_process_wrapper(qdh, hset, NULL))
662            {
663                LSQ_DEBUG("finishing hset failed");
664                free(uh);
665                qdh->qdh_enpub->enp_hsi_if->hsi_discard_header_set(hset);
666                return LQRHS_ERROR;
667            }
668            uh->uh_hset = hset;
669            if (0 == lsquic_stream_uh_in(stream, uh))
670                LSQ_DEBUG("gave hset to stream %"PRIu64, stream->id);
671            else
672            {
673                LSQ_DEBUG("could not give hset to stream %"PRIu64, stream->id);
674                free(uh);
675                qdh->qdh_enpub->enp_hsi_if->hsi_discard_header_set(hset);
676                return LQRHS_ERROR;
677            }
678        }
679        else
680        {
681            LSQ_DEBUG("discard trailer header set");
682            qdh_maybe_destroy_hblock_ctx(qdh, stream);
683        }
684        if (qdh->qdh_dec_sm_out)
685        {
686            if (dec_buf_sz
687                && 0 != qdh_write_decoder(qdh, dec_buf, dec_buf_sz))
688            {
689                return LQRHS_ERROR;
690            }
691            if (dec_buf_sz || lsqpack_dec_ici_pending(&qdh->qdh_decoder))
692                lsquic_stream_wantwrite(qdh->qdh_dec_sm_out, 1);
693        }
694    }
695    else if (rhs == LQRHS_ERROR)
696    {
697        qdh_maybe_destroy_hblock_ctx(qdh, stream);
698        qerr = lsqpack_dec_get_err_info(&qdh->qdh_decoder);
699        qdh->qdh_conn->cn_if->ci_abort_error(qdh->qdh_conn, 1,
700            HEC_QPACK_DECOMPRESSION_FAILED, "QPACK decompression error; "
701            "stream %"PRIu64", offset %"PRIu64", line %d", qerr->stream_id,
702            qerr->off, qerr->line);
703    }
704
705    return rhs;
706}
707
708
709enum lsqpack_read_header_status
710lsquic_qdh_header_in_begin (struct qpack_dec_hdl *qdh,
711                        struct lsquic_stream *stream, uint64_t header_size,
712                        const unsigned char **buf, size_t bufsz)
713{
714    enum lsqpack_read_header_status rhs;
715    void *hset;
716    int is_pp;
717    size_t dec_buf_sz;
718    union hblock_ctx *u;
719    unsigned char dec_buf[LSQPACK_LONGEST_HEADER_ACK];
720
721    assert(!(stream->stream_flags & STREAM_U_READ_DONE));
722
723    if (!(qdh->qdh_flags & QDH_INITIALIZED))
724    {
725        LSQ_WARN("not initialized: cannot process header block");
726        return LQRHS_ERROR;
727    }
728
729    u = malloc(sizeof(*u));
730    if (!u)
731    {
732        LSQ_INFO("cannot allocate hblock_ctx");
733        return LQRHS_ERROR;
734    }
735
736    is_pp = lsquic_stream_header_is_pp(stream);
737    hset = qdh->qdh_enpub->enp_hsi_if->hsi_create_header_set(
738                                          qdh->qdh_hsi_ctx, stream, is_pp);
739    if (!hset)
740    {
741        free(u);
742        LSQ_DEBUG("hsi_create_header_set failure");
743        return LQRHS_ERROR;
744    }
745
746    u->ctx.hset   = hset;
747    u->ctx.qdh    = qdh;
748    u->ctx.ppc_flags = 0;
749    u->ctx.ehp       = (struct lsquic_ext_http_prio) {
750                            .urgency     = LSQUIC_DEF_HTTP_URGENCY,
751                            .incremental = LSQUIC_DEF_HTTP_INCREMENTAL,
752    };
753    stream->sm_hblock_ctx = u;
754
755    if (qdh->qdh_exp_rec)
756    {
757        const lsquic_time_t now = lsquic_time_now();
758        if (0 == qdh->qdh_exp_rec->qer_hblock_count)
759            qdh->qdh_exp_rec->qer_first_req = now;
760        qdh->qdh_exp_rec->qer_last_req = now;
761        ++qdh->qdh_exp_rec->qer_hblock_count;
762        qdh->qdh_exp_rec->qer_hblock_size += bufsz;
763    }
764
765    dec_buf_sz = sizeof(dec_buf);
766    rhs = lsqpack_dec_header_in(&qdh->qdh_decoder, stream, stream->id,
767                    header_size, buf, bufsz, dec_buf, &dec_buf_sz);
768    if (qdh->qdh_exp_rec)
769        qdh->qdh_exp_rec->qer_peer_max_blocked += rhs == LQRHS_BLOCKED;
770    return qdh_header_read_results(qdh, stream, rhs, dec_buf, dec_buf_sz);
771}
772
773
774enum lsqpack_read_header_status
775lsquic_qdh_header_in_continue (struct qpack_dec_hdl *qdh,
776        struct lsquic_stream *stream, const unsigned char **buf, size_t bufsz)
777{
778    enum lsqpack_read_header_status rhs;
779    size_t dec_buf_sz;
780    unsigned char dec_buf[LSQPACK_LONGEST_HEADER_ACK];
781
782    assert(!(stream->stream_flags & STREAM_U_READ_DONE));
783
784    if (qdh->qdh_flags & QDH_INITIALIZED)
785    {
786        if (qdh->qdh_exp_rec)
787            qdh->qdh_exp_rec->qer_hblock_size += bufsz;
788        dec_buf_sz = sizeof(dec_buf);
789        rhs = lsqpack_dec_header_read(&qdh->qdh_decoder, stream,
790                                    buf, bufsz, dec_buf, &dec_buf_sz);
791        if (qdh->qdh_exp_rec)
792            qdh->qdh_exp_rec->qer_peer_max_blocked += rhs == LQRHS_BLOCKED;
793        return qdh_header_read_results(qdh, stream, rhs, dec_buf, dec_buf_sz);
794    }
795    else
796    {
797        LSQ_WARN("not initialized: cannot process header block");
798        return LQRHS_ERROR;
799    }
800}
801
802
803static void
804lsquic_qdh_unref_stream (struct qpack_dec_hdl *qdh,
805                                                struct lsquic_stream *stream)
806{
807    if (0 == lsqpack_dec_unref_stream(&qdh->qdh_decoder, stream))
808        LSQ_DEBUG("unreffed stream %"PRIu64, stream->id);
809    else
810        LSQ_WARN("cannot unref stream %"PRIu64, stream->id);
811}
812
813
814void
815lsquic_qdh_cancel_stream (struct qpack_dec_hdl *qdh,
816                                                struct lsquic_stream *stream)
817{
818    ssize_t nw;
819    unsigned char buf[LSQPACK_LONGEST_CANCEL];
820
821    qdh_maybe_destroy_hblock_ctx(qdh, stream);
822
823    if (!qdh->qdh_dec_sm_out)
824        return;
825
826    nw = lsqpack_dec_cancel_stream(&qdh->qdh_decoder, stream, buf, sizeof(buf));
827    if (nw > 0)
828    {
829        if (0 == qdh_write_decoder(qdh, buf, nw))
830            LSQ_DEBUG("cancelled stream %"PRIu64" and wrote %zd-byte Cancel "
831                "Stream instruction to the decoder stream", stream->id, nw);
832    }
833    else if (nw == 0)
834        LSQ_WARN("cannot cancel stream %"PRIu64" -- not found", stream->id);
835    else
836    {
837        LSQ_WARN("cannot cancel stream %"PRIu64" -- not enough buffer space "
838            "to encode Cancel Stream instructin", stream->id);
839        lsquic_qdh_unref_stream(qdh, stream);
840    }
841}
842
843
844void
845lsquic_qdh_cancel_stream_id (struct qpack_dec_hdl *qdh,
846                                                lsquic_stream_id_t stream_id)
847{
848    ssize_t nw;
849    unsigned char buf[LSQPACK_LONGEST_CANCEL];
850
851    if (!qdh->qdh_dec_sm_out)
852        return;
853
854    nw = lsqpack_dec_cancel_stream_id(&qdh->qdh_decoder, stream_id, buf,
855                                                                sizeof(buf));
856    if (nw > 0)
857    {
858        if (0 == qdh_write_decoder(qdh, buf, nw))
859            LSQ_DEBUG("wrote %zd-byte Cancel Stream instruction for "
860                "stream %"PRIu64" to the decoder stream", nw, stream_id);
861    }
862    else if (nw == 0)
863        LSQ_DEBUG("not generating Cancel Stream instruction for "
864            "stream %"PRIu64, stream_id);
865    else
866        LSQ_WARN("cannot generate Cancel Stream instruction for "
867            "stream %"PRIu64" -- not enough buffer space", stream_id);
868}
869
870
871int
872lsquic_qdh_arm_if_unsent (struct qpack_dec_hdl *qdh, void (*func)(void *),
873                                                                    void *ctx)
874{
875    size_t bytes;
876
877    /* Use size of a single frab list buffer as an arbitrary threshold */
878    bytes = lsquic_frab_list_size(&qdh->qdh_fral);
879    if (bytes <= qdh->qdh_fral.fl_buf_size)
880        return 0;
881    else
882    {
883        LSQ_DEBUG("have %zu bytes of unsent QPACK decoder stream data: set "
884            "up callback", bytes);
885        qdh->qdh_on_dec_sent_func = func;
886        qdh->qdh_on_dec_sent_ctx  = ctx;
887        return 1;
888    }
889}
890
891
892const char *
893lsquic_qdh_get_ua (const struct qpack_dec_hdl *qdh)
894{
895    if (qdh->qdh_ua)
896        return qdh->qdh_ua;
897    else if (qdh->qdh_exp_rec && qdh->qdh_exp_rec->qer_user_agent)
898        return qdh->qdh_exp_rec->qer_user_agent;
899    else
900        return NULL;
901}
902