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