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