lsquic_qdec_hdl.c revision 7d09751d
17d09751dSDmitri Tikhonov/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc.  See LICENSE. */
25392f7a3SLiteSpeed Tech/*
35392f7a3SLiteSpeed Tech * lsquic_qdec_hdl.c -- QPACK decoder streams handler
45392f7a3SLiteSpeed Tech */
55392f7a3SLiteSpeed Tech
65392f7a3SLiteSpeed Tech#include <assert.h>
75392f7a3SLiteSpeed Tech#include <errno.h>
85392f7a3SLiteSpeed Tech#include <inttypes.h>
95392f7a3SLiteSpeed Tech#include <stdlib.h>
105392f7a3SLiteSpeed Tech#include <string.h>
115392f7a3SLiteSpeed Tech#include <sys/queue.h>
125392f7a3SLiteSpeed Tech
135392f7a3SLiteSpeed Tech#include "lsquic.h"
145392f7a3SLiteSpeed Tech#include "lsquic_types.h"
155392f7a3SLiteSpeed Tech#include "lsquic_int_types.h"
165392f7a3SLiteSpeed Tech#include "lsquic_sfcw.h"
175392f7a3SLiteSpeed Tech#include "lsquic_varint.h"
185392f7a3SLiteSpeed Tech#include "lsquic_hq.h"
195392f7a3SLiteSpeed Tech#include "lsquic_hash.h"
205392f7a3SLiteSpeed Tech#include "lsquic_stream.h"
215392f7a3SLiteSpeed Tech#include "lsquic_frab_list.h"
225392f7a3SLiteSpeed Tech#include "lsqpack.h"
235392f7a3SLiteSpeed Tech#include "lsquic_http1x_if.h"
245392f7a3SLiteSpeed Tech#include "lsquic_qdec_hdl.h"
255392f7a3SLiteSpeed Tech#include "lsquic_mm.h"
265392f7a3SLiteSpeed Tech#include "lsquic_engine_public.h"
275392f7a3SLiteSpeed Tech#include "lsquic_headers.h"
285392f7a3SLiteSpeed Tech#include "lsquic_http1x_if.h"
295392f7a3SLiteSpeed Tech#include "lsquic_conn.h"
305392f7a3SLiteSpeed Tech
315392f7a3SLiteSpeed Tech#define LSQUIC_LOGGER_MODULE LSQLM_QDEC_HDL
325392f7a3SLiteSpeed Tech#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(qdh->qdh_conn)
335392f7a3SLiteSpeed Tech#include "lsquic_logger.h"
345392f7a3SLiteSpeed Tech
355392f7a3SLiteSpeed Techstatic void
365392f7a3SLiteSpeed Techqdh_hblock_unblocked (void *);
375392f7a3SLiteSpeed Tech
385392f7a3SLiteSpeed Tech
395392f7a3SLiteSpeed Techstatic int
405392f7a3SLiteSpeed Techqdh_write_decoder (struct qpack_dec_hdl *qdh, const unsigned char *buf,
415392f7a3SLiteSpeed Tech                                                                size_t sz)
425392f7a3SLiteSpeed Tech{
435392f7a3SLiteSpeed Tech    ssize_t nw;
445392f7a3SLiteSpeed Tech
455392f7a3SLiteSpeed Tech    if (!(qdh->qdh_dec_sm_out && lsquic_frab_list_empty(&qdh->qdh_fral)))
465392f7a3SLiteSpeed Tech    {
475392f7a3SLiteSpeed Tech  write_to_frab:
485392f7a3SLiteSpeed Tech        if (0 == lsquic_frab_list_write(&qdh->qdh_fral,
495392f7a3SLiteSpeed Tech                                                (unsigned char *) buf, sz))
505392f7a3SLiteSpeed Tech        {
515392f7a3SLiteSpeed Tech            LSQ_DEBUG("wrote %zu bytes to frab list", sz);
525392f7a3SLiteSpeed Tech            lsquic_stream_wantwrite(qdh->qdh_dec_sm_out, 1);
535392f7a3SLiteSpeed Tech            return 0;
545392f7a3SLiteSpeed Tech        }
555392f7a3SLiteSpeed Tech        else
565392f7a3SLiteSpeed Tech        {
575392f7a3SLiteSpeed Tech            LSQ_INFO("error writing to frab list");
585392f7a3SLiteSpeed Tech            return -1;
595392f7a3SLiteSpeed Tech        }
605392f7a3SLiteSpeed Tech    }
615392f7a3SLiteSpeed Tech
625392f7a3SLiteSpeed Tech    nw = lsquic_stream_write(qdh->qdh_dec_sm_out, buf, sz);
635392f7a3SLiteSpeed Tech    if (nw < 0)
645392f7a3SLiteSpeed Tech    {
655392f7a3SLiteSpeed Tech        LSQ_INFO("error writing to outgoing QPACK decoder stream: %s",
665392f7a3SLiteSpeed Tech                                                        strerror(errno));
675392f7a3SLiteSpeed Tech        return -1;
685392f7a3SLiteSpeed Tech    }
695392f7a3SLiteSpeed Tech    LSQ_DEBUG("wrote %zd bytes to outgoing QPACK decoder stream", nw);
705392f7a3SLiteSpeed Tech
715392f7a3SLiteSpeed Tech    if ((size_t) nw == sz)
725392f7a3SLiteSpeed Tech        return 0;
735392f7a3SLiteSpeed Tech
745392f7a3SLiteSpeed Tech    buf = buf + nw;
755392f7a3SLiteSpeed Tech    sz -= (size_t) nw;
765392f7a3SLiteSpeed Tech    goto write_to_frab;
775392f7a3SLiteSpeed Tech}
785392f7a3SLiteSpeed Tech
795392f7a3SLiteSpeed Tech
805392f7a3SLiteSpeed Techstatic int
815392f7a3SLiteSpeed Techqdh_write_type (struct qpack_dec_hdl *qdh)
825392f7a3SLiteSpeed Tech{
835392f7a3SLiteSpeed Tech    int s;
845392f7a3SLiteSpeed Tech
855392f7a3SLiteSpeed Tech#ifndef NDEBUG
865392f7a3SLiteSpeed Tech    const char *env = getenv("LSQUIC_RND_VARINT_LEN");
875392f7a3SLiteSpeed Tech    if (env && atoi(env))
885392f7a3SLiteSpeed Tech    {
895392f7a3SLiteSpeed Tech        s = rand() & 3;
905392f7a3SLiteSpeed Tech        LSQ_DEBUG("writing %d-byte stream type", 1 << s);
915392f7a3SLiteSpeed Tech    }
925392f7a3SLiteSpeed Tech    else
935392f7a3SLiteSpeed Tech#endif
945392f7a3SLiteSpeed Tech        s = 0;
955392f7a3SLiteSpeed Tech
965392f7a3SLiteSpeed Tech    switch (s)
975392f7a3SLiteSpeed Tech    {
985392f7a3SLiteSpeed Tech    case 0:
995392f7a3SLiteSpeed Tech        return qdh_write_decoder(qdh,
1005392f7a3SLiteSpeed Tech                                (unsigned char []) { HQUST_QPACK_DEC }, 1);
1015392f7a3SLiteSpeed Tech    case 1:
1025392f7a3SLiteSpeed Tech        return qdh_write_decoder(qdh,
1035392f7a3SLiteSpeed Tech                            (unsigned char []) { 0x40, HQUST_QPACK_DEC }, 2);
1045392f7a3SLiteSpeed Tech    case 2:
1055392f7a3SLiteSpeed Tech        return qdh_write_decoder(qdh,
1065392f7a3SLiteSpeed Tech                (unsigned char []) { 0x80, 0x00, 0x00, HQUST_QPACK_DEC }, 4);
1075392f7a3SLiteSpeed Tech    default:
1085392f7a3SLiteSpeed Tech        return qdh_write_decoder(qdh,
1095392f7a3SLiteSpeed Tech                (unsigned char []) { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1105392f7a3SLiteSpeed Tech                                                        HQUST_QPACK_DEC }, 8);
1115392f7a3SLiteSpeed Tech    }
1125392f7a3SLiteSpeed Tech}
1135392f7a3SLiteSpeed Tech
1145392f7a3SLiteSpeed Tech
1155392f7a3SLiteSpeed Techstatic void
1165392f7a3SLiteSpeed Techqdh_begin_out (struct qpack_dec_hdl *qdh)
1175392f7a3SLiteSpeed Tech{
1185392f7a3SLiteSpeed Tech    if (0 != qdh_write_type(qdh))
1195392f7a3SLiteSpeed Tech    {
1205392f7a3SLiteSpeed Tech        LSQ_WARN("%s: could not write to decoder", __func__);
1215392f7a3SLiteSpeed Tech        qdh->qdh_conn->cn_if->ci_internal_error(qdh->qdh_conn,
1225392f7a3SLiteSpeed Tech                                        "cannot write to decoder stream");
1235392f7a3SLiteSpeed Tech    }
1245392f7a3SLiteSpeed Tech}
1255392f7a3SLiteSpeed Tech
1265392f7a3SLiteSpeed Tech
1275392f7a3SLiteSpeed Techint
1285392f7a3SLiteSpeed Techlsquic_qdh_init (struct qpack_dec_hdl *qdh, struct lsquic_conn *conn,
1295392f7a3SLiteSpeed Tech                    int is_server, const struct lsquic_engine_public *enpub,
1305392f7a3SLiteSpeed Tech                    unsigned dyn_table_size, unsigned max_risked_streams)
1315392f7a3SLiteSpeed Tech{
1325392f7a3SLiteSpeed Tech    qdh->qdh_conn = conn;
1335392f7a3SLiteSpeed Tech    lsquic_frab_list_init(&qdh->qdh_fral, 0x400, NULL, NULL, NULL);
1345392f7a3SLiteSpeed Tech    lsqpack_dec_init(&qdh->qdh_decoder, (void *) conn, dyn_table_size,
1355392f7a3SLiteSpeed Tech                        max_risked_streams, qdh_hblock_unblocked);
1365392f7a3SLiteSpeed Tech    qdh->qdh_flags |= QDH_INITIALIZED;
1375392f7a3SLiteSpeed Tech    qdh->qdh_enpub = enpub;
1385392f7a3SLiteSpeed Tech    if (qdh->qdh_enpub->enp_hsi_if == lsquic_http1x_if)
1395392f7a3SLiteSpeed Tech    {
1405392f7a3SLiteSpeed Tech        qdh->qdh_h1x_ctor_ctx = (struct http1x_ctor_ctx) {
1415392f7a3SLiteSpeed Tech            .conn           = conn,
1425392f7a3SLiteSpeed Tech            .max_headers_sz = 0x10000,  /* XXX */
1435392f7a3SLiteSpeed Tech            .is_server      = is_server,
1445392f7a3SLiteSpeed Tech        };
1455392f7a3SLiteSpeed Tech        qdh->qdh_hsi_ctx = &qdh->qdh_h1x_ctor_ctx;
1465392f7a3SLiteSpeed Tech    }
1475392f7a3SLiteSpeed Tech    else
1485392f7a3SLiteSpeed Tech        qdh->qdh_hsi_ctx = qdh->qdh_enpub->enp_hsi_ctx;
1495392f7a3SLiteSpeed Tech    if (qdh->qdh_dec_sm_out)
1505392f7a3SLiteSpeed Tech        qdh_begin_out(qdh);
1515392f7a3SLiteSpeed Tech    if (qdh->qdh_enc_sm_in)
1525392f7a3SLiteSpeed Tech        lsquic_stream_wantread(qdh->qdh_enc_sm_in, 1);
1535392f7a3SLiteSpeed Tech    LSQ_DEBUG("initialized");
1545392f7a3SLiteSpeed Tech    return 0;
1555392f7a3SLiteSpeed Tech}
1565392f7a3SLiteSpeed Tech
1575392f7a3SLiteSpeed Tech
1585392f7a3SLiteSpeed Techvoid
1595392f7a3SLiteSpeed Techlsquic_qdh_cleanup (struct qpack_dec_hdl *qdh)
1605392f7a3SLiteSpeed Tech{
1615392f7a3SLiteSpeed Tech    if (qdh->qdh_flags & QDH_INITIALIZED)
1625392f7a3SLiteSpeed Tech    {
1635392f7a3SLiteSpeed Tech        LSQ_DEBUG("cleanup");
1645392f7a3SLiteSpeed Tech        lsqpack_dec_cleanup(&qdh->qdh_decoder);
1655392f7a3SLiteSpeed Tech        lsquic_frab_list_cleanup(&qdh->qdh_fral);
1665392f7a3SLiteSpeed Tech        qdh->qdh_flags &= ~QDH_INITIALIZED;
1675392f7a3SLiteSpeed Tech    }
1685392f7a3SLiteSpeed Tech}
1695392f7a3SLiteSpeed Tech
1705392f7a3SLiteSpeed Techstatic lsquic_stream_ctx_t *
1715392f7a3SLiteSpeed Techqdh_out_on_new (void *stream_if_ctx, struct lsquic_stream *stream)
1725392f7a3SLiteSpeed Tech{
1735392f7a3SLiteSpeed Tech    struct qpack_dec_hdl *const qdh = stream_if_ctx;
1745392f7a3SLiteSpeed Tech    qdh->qdh_dec_sm_out = stream;
1755392f7a3SLiteSpeed Tech    if (qdh->qdh_flags & QDH_INITIALIZED)
1765392f7a3SLiteSpeed Tech        qdh_begin_out(qdh);
1775392f7a3SLiteSpeed Tech    LSQ_DEBUG("initialized outgoing decoder stream");
1785392f7a3SLiteSpeed Tech    return (void *) qdh;
1795392f7a3SLiteSpeed Tech}
1805392f7a3SLiteSpeed Tech
1815392f7a3SLiteSpeed Tech
1825392f7a3SLiteSpeed Techstatic void
1835392f7a3SLiteSpeed Techqdh_out_on_write (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx)
1845392f7a3SLiteSpeed Tech{
1855392f7a3SLiteSpeed Tech    struct qpack_dec_hdl *const qdh = (void *) ctx;
1865392f7a3SLiteSpeed Tech    struct lsquic_reader reader;
1875392f7a3SLiteSpeed Tech    ssize_t nw;
1885392f7a3SLiteSpeed Tech    unsigned char buf[LSQPACK_LONGEST_ICI];
1895392f7a3SLiteSpeed Tech
1905392f7a3SLiteSpeed Tech    if (lsqpack_dec_ici_pending(&qdh->qdh_decoder))
1915392f7a3SLiteSpeed Tech    {
1925392f7a3SLiteSpeed Tech        nw = lsqpack_dec_write_ici(&qdh->qdh_decoder, buf, sizeof(buf));
1935392f7a3SLiteSpeed Tech        if (nw > 0)
1945392f7a3SLiteSpeed Tech        {
1955392f7a3SLiteSpeed Tech            if (0 == qdh_write_decoder(qdh, buf, nw))
1965392f7a3SLiteSpeed Tech                LSQ_DEBUG("wrote %zd-byte TSS instruction", nw);
1975392f7a3SLiteSpeed Tech            else
1985392f7a3SLiteSpeed Tech                goto err;
1995392f7a3SLiteSpeed Tech        }
2005392f7a3SLiteSpeed Tech        else if (nw < 0)
2015392f7a3SLiteSpeed Tech        {
2025392f7a3SLiteSpeed Tech            LSQ_WARN("could not generate TSS instruction");
2035392f7a3SLiteSpeed Tech            goto err;
2045392f7a3SLiteSpeed Tech        }
2055392f7a3SLiteSpeed Tech    }
2065392f7a3SLiteSpeed Tech
2075392f7a3SLiteSpeed Tech    if (lsquic_frab_list_empty(&qdh->qdh_fral))
2085392f7a3SLiteSpeed Tech    {
2095392f7a3SLiteSpeed Tech        LSQ_DEBUG("%s: nothing to write", __func__);
2105392f7a3SLiteSpeed Tech        lsquic_stream_wantwrite(stream, 0);
2115392f7a3SLiteSpeed Tech        return;
2125392f7a3SLiteSpeed Tech    }
2135392f7a3SLiteSpeed Tech
2145392f7a3SLiteSpeed Tech    reader = (struct lsquic_reader) {
2155392f7a3SLiteSpeed Tech        .lsqr_read  = lsquic_frab_list_read,
2165392f7a3SLiteSpeed Tech        .lsqr_size  = lsquic_frab_list_size,
2175392f7a3SLiteSpeed Tech        .lsqr_ctx   = &qdh->qdh_fral,
2185392f7a3SLiteSpeed Tech    };
2195392f7a3SLiteSpeed Tech
2205392f7a3SLiteSpeed Tech    nw = lsquic_stream_writef(stream, &reader);
2215392f7a3SLiteSpeed Tech    if (nw >= 0)
2225392f7a3SLiteSpeed Tech    {
2235392f7a3SLiteSpeed Tech        LSQ_DEBUG("wrote %zd bytes to stream", nw);
2245392f7a3SLiteSpeed Tech        (void) lsquic_stream_flush(stream);
2255392f7a3SLiteSpeed Tech        if (lsquic_frab_list_empty(&qdh->qdh_fral))
226747be414SDmitri Tikhonov        {
2275392f7a3SLiteSpeed Tech            lsquic_stream_wantwrite(stream, 0);
228747be414SDmitri Tikhonov            if (qdh->qdh_on_dec_sent_func)
229747be414SDmitri Tikhonov            {
230747be414SDmitri Tikhonov                LSQ_DEBUG("buffered data written: call callback");
231747be414SDmitri Tikhonov                qdh->qdh_on_dec_sent_func(qdh->qdh_on_dec_sent_ctx);
232747be414SDmitri Tikhonov                qdh->qdh_on_dec_sent_func = NULL;
233747be414SDmitri Tikhonov                qdh->qdh_on_dec_sent_ctx = NULL;
234747be414SDmitri Tikhonov            }
235747be414SDmitri Tikhonov        }
2365392f7a3SLiteSpeed Tech    }
2375392f7a3SLiteSpeed Tech    else
2385392f7a3SLiteSpeed Tech    {
2395392f7a3SLiteSpeed Tech        LSQ_WARN("cannot write to stream: %s", strerror(errno));
2405392f7a3SLiteSpeed Tech  err:
2415392f7a3SLiteSpeed Tech        lsquic_stream_wantwrite(stream, 0);
2425392f7a3SLiteSpeed Tech        qdh->qdh_conn->cn_if->ci_internal_error(qdh->qdh_conn,
2435392f7a3SLiteSpeed Tech                                        "cannot write to stream");
2445392f7a3SLiteSpeed Tech    }
2455392f7a3SLiteSpeed Tech}
2465392f7a3SLiteSpeed Tech
2475392f7a3SLiteSpeed Tech
2485392f7a3SLiteSpeed Techstatic void
2495392f7a3SLiteSpeed Techqdh_out_on_close (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx)
2505392f7a3SLiteSpeed Tech{
2515392f7a3SLiteSpeed Tech    struct qpack_dec_hdl *const qdh = (void *) ctx;
2525392f7a3SLiteSpeed Tech    qdh->qdh_dec_sm_out = NULL;
2535392f7a3SLiteSpeed Tech    LSQ_DEBUG("closed outgoing decoder stream");
2545392f7a3SLiteSpeed Tech}
2555392f7a3SLiteSpeed Tech
2565392f7a3SLiteSpeed Tech
2575392f7a3SLiteSpeed Techstatic void
2585392f7a3SLiteSpeed Techqdh_out_on_read (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx)
2595392f7a3SLiteSpeed Tech{
2605392f7a3SLiteSpeed Tech    assert(0);
2615392f7a3SLiteSpeed Tech}
2625392f7a3SLiteSpeed Tech
2635392f7a3SLiteSpeed Tech
2645392f7a3SLiteSpeed Techstatic const struct lsquic_stream_if qdh_dec_sm_out_if =
2655392f7a3SLiteSpeed Tech{
2665392f7a3SLiteSpeed Tech    .on_new_stream  = qdh_out_on_new,
2675392f7a3SLiteSpeed Tech    .on_read        = qdh_out_on_read,
2685392f7a3SLiteSpeed Tech    .on_write       = qdh_out_on_write,
2695392f7a3SLiteSpeed Tech    .on_close       = qdh_out_on_close,
2705392f7a3SLiteSpeed Tech};
2715392f7a3SLiteSpeed Techconst struct lsquic_stream_if *const lsquic_qdh_dec_sm_out_if =
2725392f7a3SLiteSpeed Tech                                                    &qdh_dec_sm_out_if;
2735392f7a3SLiteSpeed Tech
2745392f7a3SLiteSpeed Tech
2755392f7a3SLiteSpeed Techstatic lsquic_stream_ctx_t *
2765392f7a3SLiteSpeed Techqdh_in_on_new (void *stream_if_ctx, struct lsquic_stream *stream)
2775392f7a3SLiteSpeed Tech{
2785392f7a3SLiteSpeed Tech    struct qpack_dec_hdl *const qdh = stream_if_ctx;
2795392f7a3SLiteSpeed Tech    qdh->qdh_enc_sm_in = stream;
2805392f7a3SLiteSpeed Tech    if (qdh->qdh_flags & QDH_INITIALIZED)
2815392f7a3SLiteSpeed Tech        lsquic_stream_wantread(qdh->qdh_enc_sm_in, 1);
2825392f7a3SLiteSpeed Tech    LSQ_DEBUG("initialized incoming encoder stream");
2835392f7a3SLiteSpeed Tech    return (void *) qdh;
2845392f7a3SLiteSpeed Tech}
2855392f7a3SLiteSpeed Tech
2865392f7a3SLiteSpeed Tech
2875392f7a3SLiteSpeed Techstatic size_t
2885392f7a3SLiteSpeed Techqdh_read_encoder_stream (void *ctx, const unsigned char *buf, size_t sz,
2895392f7a3SLiteSpeed Tech                                                                    int fin)
2905392f7a3SLiteSpeed Tech{
2915392f7a3SLiteSpeed Tech    struct qpack_dec_hdl *const qdh = (void *) ctx;
2925392f7a3SLiteSpeed Tech    const struct lsqpack_dec_err *qerr;
2935392f7a3SLiteSpeed Tech    int s;
2945392f7a3SLiteSpeed Tech
2955392f7a3SLiteSpeed Tech    if (fin)
2965392f7a3SLiteSpeed Tech    {
2975392f7a3SLiteSpeed Tech        LSQ_INFO("encoder stream is closed");
2985392f7a3SLiteSpeed Tech        qdh->qdh_conn->cn_if->ci_abort_error(qdh->qdh_conn, 1,
2995392f7a3SLiteSpeed Tech            HEC_CLOSED_CRITICAL_STREAM, "Peer closed QPACK encoder stream");
3005392f7a3SLiteSpeed Tech        goto end;
3015392f7a3SLiteSpeed Tech    }
3025392f7a3SLiteSpeed Tech
3035392f7a3SLiteSpeed Tech    s = lsqpack_dec_enc_in(&qdh->qdh_decoder, buf, sz);
3045392f7a3SLiteSpeed Tech    if (s != 0)
3055392f7a3SLiteSpeed Tech    {
306747be414SDmitri Tikhonov        LSQ_INFO("error reading encoder stream");
3075392f7a3SLiteSpeed Tech        qerr = lsqpack_dec_get_err_info(&qdh->qdh_decoder);
3085392f7a3SLiteSpeed Tech        qdh->qdh_conn->cn_if->ci_abort_error(qdh->qdh_conn, 1,
3095392f7a3SLiteSpeed Tech            HEC_QPACK_DECODER_STREAM_ERROR, "Error interpreting QPACK encoder "
3105392f7a3SLiteSpeed Tech            "stream; offset %"PRIu64", line %d", qerr->off, qerr->line);
3115392f7a3SLiteSpeed Tech        goto end;
3125392f7a3SLiteSpeed Tech    }
3135392f7a3SLiteSpeed Tech    if (qdh->qdh_dec_sm_out
3145392f7a3SLiteSpeed Tech                    && lsqpack_dec_ici_pending(&qdh->qdh_decoder))
3155392f7a3SLiteSpeed Tech        lsquic_stream_wantwrite(qdh->qdh_dec_sm_out, 1);
3165392f7a3SLiteSpeed Tech
3175392f7a3SLiteSpeed Tech    LSQ_DEBUG("successfully fed %zu bytes to QPACK decoder", sz);
3185392f7a3SLiteSpeed Tech
3195392f7a3SLiteSpeed Tech  end:
3205392f7a3SLiteSpeed Tech    return sz;
3215392f7a3SLiteSpeed Tech}
3225392f7a3SLiteSpeed Tech
3235392f7a3SLiteSpeed Tech
3245392f7a3SLiteSpeed Techstatic void
3255392f7a3SLiteSpeed Techqdh_in_on_read (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx)
3265392f7a3SLiteSpeed Tech{
3275392f7a3SLiteSpeed Tech    struct qpack_dec_hdl *const qdh = (void *) ctx;
3285392f7a3SLiteSpeed Tech    ssize_t nread;
3295392f7a3SLiteSpeed Tech
3305392f7a3SLiteSpeed Tech    nread = lsquic_stream_readf(stream, qdh_read_encoder_stream, qdh);
3315392f7a3SLiteSpeed Tech    if (nread <= 0)
3325392f7a3SLiteSpeed Tech    {
3335392f7a3SLiteSpeed Tech        if (nread < 0)
3345392f7a3SLiteSpeed Tech        {
3355392f7a3SLiteSpeed Tech            LSQ_WARN("cannot read from encoder stream: %s", strerror(errno));
3365392f7a3SLiteSpeed Tech            qdh->qdh_conn->cn_if->ci_internal_error(qdh->qdh_conn,
3375392f7a3SLiteSpeed Tech                                        "cannot read from encoder stream");
3385392f7a3SLiteSpeed Tech        }
3395392f7a3SLiteSpeed Tech        else
3405392f7a3SLiteSpeed Tech        {
3415392f7a3SLiteSpeed Tech            LSQ_INFO("encoder stream closed by peer: abort connection");
3425392f7a3SLiteSpeed Tech            qdh->qdh_conn->cn_if->ci_abort_error(qdh->qdh_conn, 1,
3435392f7a3SLiteSpeed Tech                HEC_CLOSED_CRITICAL_STREAM, "encoder stream closed");
3445392f7a3SLiteSpeed Tech        }
3455392f7a3SLiteSpeed Tech        lsquic_stream_wantread(stream, 0);
3465392f7a3SLiteSpeed Tech    }
3475392f7a3SLiteSpeed Tech}
3485392f7a3SLiteSpeed Tech
3495392f7a3SLiteSpeed Tech
3505392f7a3SLiteSpeed Techstatic void
3515392f7a3SLiteSpeed Techqdh_in_on_close (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx)
3525392f7a3SLiteSpeed Tech{
3535392f7a3SLiteSpeed Tech    struct qpack_dec_hdl *const qdh = (void *) ctx;
3545392f7a3SLiteSpeed Tech    LSQ_DEBUG("closed incoming encoder stream");
3555392f7a3SLiteSpeed Tech    qdh->qdh_enc_sm_in = NULL;
3565392f7a3SLiteSpeed Tech}
3575392f7a3SLiteSpeed Tech
3585392f7a3SLiteSpeed Tech
3595392f7a3SLiteSpeed Techstatic void
3605392f7a3SLiteSpeed Techqdh_in_on_write (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx)
3615392f7a3SLiteSpeed Tech{
3625392f7a3SLiteSpeed Tech    assert(0);
3635392f7a3SLiteSpeed Tech}
3645392f7a3SLiteSpeed Tech
3655392f7a3SLiteSpeed Tech
3665392f7a3SLiteSpeed Techstatic const struct lsquic_stream_if qdh_enc_sm_in_if =
3675392f7a3SLiteSpeed Tech{
3685392f7a3SLiteSpeed Tech    .on_new_stream  = qdh_in_on_new,
3695392f7a3SLiteSpeed Tech    .on_read        = qdh_in_on_read,
3705392f7a3SLiteSpeed Tech    .on_write       = qdh_in_on_write,
3715392f7a3SLiteSpeed Tech    .on_close       = qdh_in_on_close,
3725392f7a3SLiteSpeed Tech};
3735392f7a3SLiteSpeed Techconst struct lsquic_stream_if *const lsquic_qdh_enc_sm_in_if =
3745392f7a3SLiteSpeed Tech                                                    &qdh_enc_sm_in_if;
3755392f7a3SLiteSpeed Tech
3765392f7a3SLiteSpeed Tech
3775392f7a3SLiteSpeed Techstatic void
3785392f7a3SLiteSpeed Techqdh_hblock_unblocked (void *stream_p)
3795392f7a3SLiteSpeed Tech{
3805392f7a3SLiteSpeed Tech    struct lsquic_stream *const stream = stream_p;
3815392f7a3SLiteSpeed Tech    struct qpack_dec_hdl *const qdh = lsquic_stream_get_qdh(stream);
3825392f7a3SLiteSpeed Tech
3835392f7a3SLiteSpeed Tech    LSQ_DEBUG("header block for stream %"PRIu64" unblocked", stream->id);
3845392f7a3SLiteSpeed Tech    lsquic_stream_qdec_unblocked(stream);
3855392f7a3SLiteSpeed Tech}
3865392f7a3SLiteSpeed Tech
3875392f7a3SLiteSpeed Tech
388747be414SDmitri Tikhonovstruct cont_len
389747be414SDmitri Tikhonov{
390747be414SDmitri Tikhonov    unsigned long long      value;
391747be414SDmitri Tikhonov    int                     has;    /* 1: set, 0: not set, -1: invalid */
392747be414SDmitri Tikhonov};
393747be414SDmitri Tikhonov
394747be414SDmitri Tikhonov
395747be414SDmitri Tikhonovstatic void
396747be414SDmitri Tikhonovprocess_content_length (const struct qpack_dec_hdl *qdh /* for logging */,
397747be414SDmitri Tikhonov            struct cont_len *cl, const char *val /* not NUL-terminated */,
398747be414SDmitri Tikhonov                                                                unsigned len)
399747be414SDmitri Tikhonov{
400747be414SDmitri Tikhonov    char *endcl, cont_len_buf[30];
401747be414SDmitri Tikhonov
402747be414SDmitri Tikhonov    if (0 == cl->has)
403747be414SDmitri Tikhonov    {
404747be414SDmitri Tikhonov        if (len >= sizeof(cont_len_buf))
405747be414SDmitri Tikhonov        {
406747be414SDmitri Tikhonov            LSQ_DEBUG("content-length has invalid value `%.*s'",
407747be414SDmitri Tikhonov                                                            (int) len, val);
408747be414SDmitri Tikhonov            cl->has = -1;
409747be414SDmitri Tikhonov            return;
410747be414SDmitri Tikhonov        }
411747be414SDmitri Tikhonov        memcpy(cont_len_buf, val, len);
412747be414SDmitri Tikhonov        cont_len_buf[len] = '\0';
413747be414SDmitri Tikhonov        cl->value = strtoull(cont_len_buf, &endcl, 10);
414747be414SDmitri Tikhonov        if (*endcl == '\0' && !(ULLONG_MAX == cl->value && ERANGE == errno))
415747be414SDmitri Tikhonov        {
416747be414SDmitri Tikhonov            cl->has = 1;
417747be414SDmitri Tikhonov            LSQ_DEBUG("content length is %llu", cl->value);
418747be414SDmitri Tikhonov        }
419747be414SDmitri Tikhonov        else
420747be414SDmitri Tikhonov        {
421747be414SDmitri Tikhonov            cl->has = -1;
422747be414SDmitri Tikhonov            LSQ_DEBUG("content-length has invalid value `%.*s'",
423747be414SDmitri Tikhonov                (int) len, val);
424747be414SDmitri Tikhonov        }
425747be414SDmitri Tikhonov    }
426747be414SDmitri Tikhonov    else if (cl->has > 0)
427747be414SDmitri Tikhonov    {
428747be414SDmitri Tikhonov        LSQ_DEBUG("header set has two content-length: ambiguous, "
429747be414SDmitri Tikhonov            "turn off checking");
430747be414SDmitri Tikhonov        cl->has = -1;
431747be414SDmitri Tikhonov    }
432747be414SDmitri Tikhonov}
433747be414SDmitri Tikhonov
434747be414SDmitri Tikhonov
435747be414SDmitri Tikhonovstatic int
436747be414SDmitri Tikhonovis_content_length (const struct lsqpack_header *header)
437747be414SDmitri Tikhonov{
438747be414SDmitri Tikhonov    return ((header->qh_flags & QH_ID_SET) && header->qh_static_id == 4)
439747be414SDmitri Tikhonov        || (header->qh_name_len == 14 && header->qh_name[0] == 'c'
440747be414SDmitri Tikhonov                    && 0 == memcmp(header->qh_name + 1, "ontent-length", 13))
441747be414SDmitri Tikhonov        ;
442747be414SDmitri Tikhonov}
443747be414SDmitri Tikhonov
444747be414SDmitri Tikhonov
4455392f7a3SLiteSpeed Techstatic int
4465392f7a3SLiteSpeed Techqdh_supply_hset_to_stream (struct qpack_dec_hdl *qdh,
4475392f7a3SLiteSpeed Tech            struct lsquic_stream *stream, struct lsqpack_header_list *qlist)
4485392f7a3SLiteSpeed Tech{
4495392f7a3SLiteSpeed Tech    const struct lsquic_hset_if *const hset_if = qdh->qdh_enpub->enp_hsi_if;
4507d09751dSDmitri Tikhonov    const unsigned hpack_static_table_size = 61;
4515392f7a3SLiteSpeed Tech    struct uncompressed_headers *uh = NULL;
4525392f7a3SLiteSpeed Tech    const struct lsqpack_header *header;
4535392f7a3SLiteSpeed Tech    enum lsquic_header_status st;
45402b6086dSDmitri Tikhonov    int push_promise;
4555392f7a3SLiteSpeed Tech    unsigned i;
4565392f7a3SLiteSpeed Tech    void *hset;
457747be414SDmitri Tikhonov    struct cont_len cl;
4585392f7a3SLiteSpeed Tech
45902b6086dSDmitri Tikhonov    push_promise = lsquic_stream_header_is_pp(stream);
46002b6086dSDmitri Tikhonov    hset = hset_if->hsi_create_header_set(qdh->qdh_hsi_ctx, push_promise);
4615392f7a3SLiteSpeed Tech    if (!hset)
4625392f7a3SLiteSpeed Tech    {
4635392f7a3SLiteSpeed Tech        LSQ_INFO("call to hsi_create_header_set failed");
4645392f7a3SLiteSpeed Tech        return -1;
4655392f7a3SLiteSpeed Tech    }
4665392f7a3SLiteSpeed Tech
4675392f7a3SLiteSpeed Tech    LSQ_DEBUG("got header set for stream %"PRIu64, stream->id);
4685392f7a3SLiteSpeed Tech
469747be414SDmitri Tikhonov    cl.has = 0;
4705392f7a3SLiteSpeed Tech    for (i = 0; i < qlist->qhl_count; ++i)
4715392f7a3SLiteSpeed Tech    {
4725392f7a3SLiteSpeed Tech        header = qlist->qhl_headers[i];
4735392f7a3SLiteSpeed Tech        LSQ_DEBUG("%.*s: %.*s", header->qh_name_len, header->qh_name,
4745392f7a3SLiteSpeed Tech                                        header->qh_value_len, header->qh_value);
4755392f7a3SLiteSpeed Tech        st = hset_if->hsi_process_header(hset,
4767d09751dSDmitri Tikhonov                    header->qh_flags & QH_ID_SET ?
4777d09751dSDmitri Tikhonov                        hpack_static_table_size + 1 + header->qh_static_id : 0,
4785392f7a3SLiteSpeed Tech                    header->qh_name, header->qh_name_len,
4795392f7a3SLiteSpeed Tech                    header->qh_value, header->qh_value_len);
4805392f7a3SLiteSpeed Tech        if (st != LSQUIC_HDR_OK)
481a137764bSDmitri Tikhonov        {
482a137764bSDmitri Tikhonov            LSQ_INFO("header process returned non-OK code %u", (unsigned) st);
4835392f7a3SLiteSpeed Tech            goto err;
484a137764bSDmitri Tikhonov        }
485747be414SDmitri Tikhonov        if (is_content_length(header))
486747be414SDmitri Tikhonov            process_content_length(qdh, &cl, header->qh_value,
487747be414SDmitri Tikhonov                                                        header->qh_value_len);
4885392f7a3SLiteSpeed Tech    }
4895392f7a3SLiteSpeed Tech
4905392f7a3SLiteSpeed Tech    lsqpack_dec_destroy_header_list(qlist);
491a0e1aeeeSDmitri Tikhonov    qlist = NULL;
4925392f7a3SLiteSpeed Tech    st = hset_if->hsi_process_header(hset, 0, 0, 0, 0, 0);
4935392f7a3SLiteSpeed Tech    if (st != LSQUIC_HDR_OK)
4945392f7a3SLiteSpeed Tech        goto err;
4955392f7a3SLiteSpeed Tech
4965392f7a3SLiteSpeed Tech    uh = calloc(1, sizeof(*uh));
4975392f7a3SLiteSpeed Tech    if (!uh)
4985392f7a3SLiteSpeed Tech        goto err;
4995392f7a3SLiteSpeed Tech    uh->uh_stream_id = stream->id;
5005392f7a3SLiteSpeed Tech    uh->uh_oth_stream_id = 0;
5015392f7a3SLiteSpeed Tech    uh->uh_weight = 0;
5025392f7a3SLiteSpeed Tech    uh->uh_exclusive = -1;
5035392f7a3SLiteSpeed Tech    if (hset_if == lsquic_http1x_if)
5045392f7a3SLiteSpeed Tech        uh->uh_flags    |= UH_H1H;
5055392f7a3SLiteSpeed Tech    uh->uh_hset = hset;
5065392f7a3SLiteSpeed Tech    if (0 != lsquic_stream_uh_in(stream, uh))
5075392f7a3SLiteSpeed Tech        goto err;
5085392f7a3SLiteSpeed Tech    LSQ_DEBUG("converted qlist to hset and gave it to stream %"PRIu64,
5095392f7a3SLiteSpeed Tech                                                                stream->id);
510747be414SDmitri Tikhonov    if (cl.has > 0)
511747be414SDmitri Tikhonov        (void) lsquic_stream_verify_len(stream, cl.value);
5125392f7a3SLiteSpeed Tech    return 0;
5135392f7a3SLiteSpeed Tech
5145392f7a3SLiteSpeed Tech  err:
515a0e1aeeeSDmitri Tikhonov    if (qlist)
516a0e1aeeeSDmitri Tikhonov        lsqpack_dec_destroy_header_list(qlist);
5175392f7a3SLiteSpeed Tech    hset_if->hsi_discard_header_set(hset);
5185392f7a3SLiteSpeed Tech    free(uh);
5195392f7a3SLiteSpeed Tech    return -1;
5205392f7a3SLiteSpeed Tech}
5215392f7a3SLiteSpeed Tech
5225392f7a3SLiteSpeed Tech
52302b6086dSDmitri Tikhonovstatic int
52402b6086dSDmitri Tikhonovqdh_process_qlist (struct qpack_dec_hdl *qdh,
52502b6086dSDmitri Tikhonov            struct lsquic_stream *stream, struct lsqpack_header_list *qlist)
52602b6086dSDmitri Tikhonov{
52702b6086dSDmitri Tikhonov    if (!lsquic_stream_header_is_trailer(stream))
52802b6086dSDmitri Tikhonov        return qdh_supply_hset_to_stream(qdh, stream, qlist);
52902b6086dSDmitri Tikhonov    else
53002b6086dSDmitri Tikhonov    {
53102b6086dSDmitri Tikhonov        LSQ_DEBUG("discard trailer header set");
53202b6086dSDmitri Tikhonov        lsqpack_dec_destroy_header_list(qlist);
53302b6086dSDmitri Tikhonov        return 0;
53402b6086dSDmitri Tikhonov    }
53502b6086dSDmitri Tikhonov}
53602b6086dSDmitri Tikhonov
53702b6086dSDmitri Tikhonov
5385392f7a3SLiteSpeed Techstatic enum lsqpack_read_header_status
5395392f7a3SLiteSpeed Techqdh_header_read_results (struct qpack_dec_hdl *qdh,
5405392f7a3SLiteSpeed Tech        struct lsquic_stream *stream, enum lsqpack_read_header_status rhs,
5415392f7a3SLiteSpeed Tech        struct lsqpack_header_list *qlist, const unsigned char *dec_buf,
5425392f7a3SLiteSpeed Tech        size_t dec_buf_sz)
5435392f7a3SLiteSpeed Tech{
5445392f7a3SLiteSpeed Tech    const struct lsqpack_dec_err *qerr;
5455392f7a3SLiteSpeed Tech
5465392f7a3SLiteSpeed Tech    if (rhs == LQRHS_DONE)
5475392f7a3SLiteSpeed Tech    {
5485392f7a3SLiteSpeed Tech        if (qlist)
5495392f7a3SLiteSpeed Tech        {
55002b6086dSDmitri Tikhonov            if (0 != qdh_process_qlist(qdh, stream, qlist))
5515392f7a3SLiteSpeed Tech                return LQRHS_ERROR;
5525392f7a3SLiteSpeed Tech            if (qdh->qdh_dec_sm_out)
5535392f7a3SLiteSpeed Tech            {
5545392f7a3SLiteSpeed Tech                if (dec_buf_sz
5555392f7a3SLiteSpeed Tech                    && 0 != qdh_write_decoder(qdh, dec_buf, dec_buf_sz))
5565392f7a3SLiteSpeed Tech                {
5575392f7a3SLiteSpeed Tech                    return LQRHS_ERROR;
5585392f7a3SLiteSpeed Tech                }
5595392f7a3SLiteSpeed Tech                if (dec_buf_sz || lsqpack_dec_ici_pending(&qdh->qdh_decoder))
5605392f7a3SLiteSpeed Tech                    lsquic_stream_wantwrite(qdh->qdh_dec_sm_out, 1);
5615392f7a3SLiteSpeed Tech            }
5625392f7a3SLiteSpeed Tech        }
5635392f7a3SLiteSpeed Tech        else
5645392f7a3SLiteSpeed Tech        {
5655392f7a3SLiteSpeed Tech            assert(0);  /* XXX TODO What do we do here? */
5665392f7a3SLiteSpeed Tech            return LQRHS_ERROR;
5675392f7a3SLiteSpeed Tech        }
5685392f7a3SLiteSpeed Tech    }
5695392f7a3SLiteSpeed Tech    else if (rhs == LQRHS_ERROR)
5705392f7a3SLiteSpeed Tech    {
5715392f7a3SLiteSpeed Tech        qerr = lsqpack_dec_get_err_info(&qdh->qdh_decoder);
5725392f7a3SLiteSpeed Tech        qdh->qdh_conn->cn_if->ci_abort_error(qdh->qdh_conn, 1,
5735392f7a3SLiteSpeed Tech            HEC_QPACK_DECOMPRESSION_FAILED, "QPACK decompression error; "
5745392f7a3SLiteSpeed Tech            "stream %"PRIu64", offset %"PRIu64", line %d", qerr->stream_id,
5755392f7a3SLiteSpeed Tech            qerr->off, qerr->line);
5765392f7a3SLiteSpeed Tech    }
5775392f7a3SLiteSpeed Tech
5785392f7a3SLiteSpeed Tech    return rhs;
5795392f7a3SLiteSpeed Tech}
5805392f7a3SLiteSpeed Tech
5815392f7a3SLiteSpeed Tech
5825392f7a3SLiteSpeed Techenum lsqpack_read_header_status
5835392f7a3SLiteSpeed Techlsquic_qdh_header_in_begin (struct qpack_dec_hdl *qdh,
5845392f7a3SLiteSpeed Tech                        struct lsquic_stream *stream, uint64_t header_size,
5855392f7a3SLiteSpeed Tech                        const unsigned char **buf, size_t bufsz)
5865392f7a3SLiteSpeed Tech{
5875392f7a3SLiteSpeed Tech    enum lsqpack_read_header_status rhs;
5885392f7a3SLiteSpeed Tech    struct lsqpack_header_list *qlist;
5895392f7a3SLiteSpeed Tech    size_t dec_buf_sz;
5905392f7a3SLiteSpeed Tech    unsigned char dec_buf[LSQPACK_LONGEST_HEADER_ACK];
5915392f7a3SLiteSpeed Tech
5925392f7a3SLiteSpeed Tech    if (qdh->qdh_flags & QDH_INITIALIZED)
5935392f7a3SLiteSpeed Tech    {
5945392f7a3SLiteSpeed Tech        dec_buf_sz = sizeof(dec_buf);
5955392f7a3SLiteSpeed Tech        rhs = lsqpack_dec_header_in(&qdh->qdh_decoder, stream, stream->id,
5965392f7a3SLiteSpeed Tech                        header_size, buf, bufsz, &qlist, dec_buf, &dec_buf_sz);
5975392f7a3SLiteSpeed Tech        return qdh_header_read_results(qdh, stream, rhs, qlist, dec_buf,
5985392f7a3SLiteSpeed Tech                                                                dec_buf_sz);
5995392f7a3SLiteSpeed Tech    }
6005392f7a3SLiteSpeed Tech    else
6015392f7a3SLiteSpeed Tech    {
6025392f7a3SLiteSpeed Tech        LSQ_WARN("not initialized: cannot process header block");
6035392f7a3SLiteSpeed Tech        return LQRHS_ERROR;
6045392f7a3SLiteSpeed Tech    }
6055392f7a3SLiteSpeed Tech
6065392f7a3SLiteSpeed Tech}
6075392f7a3SLiteSpeed Tech
6085392f7a3SLiteSpeed Tech
6095392f7a3SLiteSpeed Techenum lsqpack_read_header_status
6105392f7a3SLiteSpeed Techlsquic_qdh_header_in_continue (struct qpack_dec_hdl *qdh,
6115392f7a3SLiteSpeed Tech        struct lsquic_stream *stream, const unsigned char **buf, size_t bufsz)
6125392f7a3SLiteSpeed Tech{
6135392f7a3SLiteSpeed Tech    enum lsqpack_read_header_status rhs;
6145392f7a3SLiteSpeed Tech    struct lsqpack_header_list *qlist;
6155392f7a3SLiteSpeed Tech    size_t dec_buf_sz;
6165392f7a3SLiteSpeed Tech    unsigned char dec_buf[LSQPACK_LONGEST_HEADER_ACK];
6175392f7a3SLiteSpeed Tech
6185392f7a3SLiteSpeed Tech    if (qdh->qdh_flags & QDH_INITIALIZED)
6195392f7a3SLiteSpeed Tech    {
6205392f7a3SLiteSpeed Tech        dec_buf_sz = sizeof(dec_buf);
6215392f7a3SLiteSpeed Tech        rhs = lsqpack_dec_header_read(&qdh->qdh_decoder, stream,
6225392f7a3SLiteSpeed Tech                                    buf, bufsz, &qlist, dec_buf, &dec_buf_sz);
6235392f7a3SLiteSpeed Tech        return qdh_header_read_results(qdh, stream, rhs, qlist, dec_buf,
6245392f7a3SLiteSpeed Tech                                                                dec_buf_sz);
6255392f7a3SLiteSpeed Tech    }
6265392f7a3SLiteSpeed Tech    else
6275392f7a3SLiteSpeed Tech    {
6285392f7a3SLiteSpeed Tech        LSQ_WARN("not initialized: cannot process header block");
6295392f7a3SLiteSpeed Tech        return LQRHS_ERROR;
6305392f7a3SLiteSpeed Tech    }
6315392f7a3SLiteSpeed Tech}
6325392f7a3SLiteSpeed Tech
6335392f7a3SLiteSpeed Tech
6345392f7a3SLiteSpeed Techvoid
6355392f7a3SLiteSpeed Techlsquic_qdh_unref_stream (struct qpack_dec_hdl *qdh,
6365392f7a3SLiteSpeed Tech                                                struct lsquic_stream *stream)
6375392f7a3SLiteSpeed Tech{
6385392f7a3SLiteSpeed Tech    if (0 == lsqpack_dec_unref_stream(&qdh->qdh_decoder, stream))
6395392f7a3SLiteSpeed Tech        LSQ_DEBUG("unreffed stream %"PRIu64, stream->id);
6405392f7a3SLiteSpeed Tech    else
6415392f7a3SLiteSpeed Tech        LSQ_WARN("cannot unref stream %"PRIu64, stream->id);
6425392f7a3SLiteSpeed Tech}
6435392f7a3SLiteSpeed Tech
6445392f7a3SLiteSpeed Tech
6455392f7a3SLiteSpeed Techvoid
6465392f7a3SLiteSpeed Techlsquic_qdh_cancel_stream (struct qpack_dec_hdl *qdh,
6475392f7a3SLiteSpeed Tech                                                struct lsquic_stream *stream)
6485392f7a3SLiteSpeed Tech{
6495392f7a3SLiteSpeed Tech    ssize_t nw;
6505392f7a3SLiteSpeed Tech    unsigned char buf[LSQPACK_LONGEST_CANCEL];
6515392f7a3SLiteSpeed Tech
6525392f7a3SLiteSpeed Tech    nw = lsqpack_dec_cancel_stream(&qdh->qdh_decoder, stream, buf, sizeof(buf));
6535392f7a3SLiteSpeed Tech    if (nw > 0)
6545392f7a3SLiteSpeed Tech    {
6555392f7a3SLiteSpeed Tech        if (0 == qdh_write_decoder(qdh, buf, nw))
6565392f7a3SLiteSpeed Tech            LSQ_DEBUG("cancelled stream %"PRIu64" and wrote %zd-byte Cancel "
6575392f7a3SLiteSpeed Tech                "Stream instruction to the decoder stream", stream->id, nw);
6585392f7a3SLiteSpeed Tech    }
6595392f7a3SLiteSpeed Tech    else if (nw == 0)
6605392f7a3SLiteSpeed Tech        LSQ_WARN("cannot cancel stream %"PRIu64" -- not found", stream->id);
6615392f7a3SLiteSpeed Tech    else
6625392f7a3SLiteSpeed Tech    {
6635392f7a3SLiteSpeed Tech        LSQ_WARN("cannot cancel stream %"PRIu64" -- not enough buffer space "
6645392f7a3SLiteSpeed Tech            "to encode Cancel Stream instructin", stream->id);
6655392f7a3SLiteSpeed Tech        lsquic_qdh_unref_stream(qdh, stream);
6665392f7a3SLiteSpeed Tech    }
6675392f7a3SLiteSpeed Tech}
668747be414SDmitri Tikhonov
669747be414SDmitri Tikhonov
670747be414SDmitri Tikhonovint
671747be414SDmitri Tikhonovlsquic_qdh_arm_if_unsent (struct qpack_dec_hdl *qdh, void (*func)(void *),
672747be414SDmitri Tikhonov                                                                    void *ctx)
673747be414SDmitri Tikhonov{
674747be414SDmitri Tikhonov    size_t bytes;
675747be414SDmitri Tikhonov
676747be414SDmitri Tikhonov    /* Use size of a single frab list buffer as an arbitrary threshold */
677747be414SDmitri Tikhonov    bytes = lsquic_frab_list_size(&qdh->qdh_fral);
678747be414SDmitri Tikhonov    if (bytes <= qdh->qdh_fral.fl_buf_size)
679747be414SDmitri Tikhonov        return 0;
680747be414SDmitri Tikhonov    else
681747be414SDmitri Tikhonov    {
682747be414SDmitri Tikhonov        LSQ_DEBUG("have %zu bytes of unsent QPACK decoder stream data: set "
683747be414SDmitri Tikhonov            "up callback", bytes);
684747be414SDmitri Tikhonov        qdh->qdh_on_dec_sent_func = func;
685747be414SDmitri Tikhonov        qdh->qdh_on_dec_sent_ctx  = ctx;
686747be414SDmitri Tikhonov        return 1;
687747be414SDmitri Tikhonov    }
688747be414SDmitri Tikhonov}
689