1a74702c6SGeorge Wang/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc.  See LICENSE. */
25392f7a3SLiteSpeed Tech/*
35392f7a3SLiteSpeed Tech * lsquic_hcso_writer.c - write to outgoing HTTP Control Stream
45392f7a3SLiteSpeed Tech */
55392f7a3SLiteSpeed Tech
65392f7a3SLiteSpeed Tech#include <assert.h>
75392f7a3SLiteSpeed Tech#include <errno.h>
85392f7a3SLiteSpeed Tech#include <stdlib.h>
95392f7a3SLiteSpeed Tech#include <string.h>
105392f7a3SLiteSpeed Tech#include <sys/queue.h>
115392f7a3SLiteSpeed Tech
125392f7a3SLiteSpeed Tech#include "lsquic.h"
135392f7a3SLiteSpeed Tech#include "lsquic_types.h"
145392f7a3SLiteSpeed Tech#include "lsquic_int_types.h"
155392f7a3SLiteSpeed Tech#include "lsquic_sfcw.h"
165392f7a3SLiteSpeed Tech#include "lsquic_varint.h"
175392f7a3SLiteSpeed Tech#include "lsquic_hq.h"
185392f7a3SLiteSpeed Tech#include "lsquic_hash.h"
195392f7a3SLiteSpeed Tech#include "lsquic_stream.h"
205392f7a3SLiteSpeed Tech#include "lsquic_frab_list.h"
215392f7a3SLiteSpeed Tech#include "lsquic_byteswap.h"
225392f7a3SLiteSpeed Tech#include "lsquic_hcso_writer.h"
237d09751dSDmitri Tikhonov#include "lsquic_conn.h"
245392f7a3SLiteSpeed Tech
255392f7a3SLiteSpeed Tech#define LSQUIC_LOGGER_MODULE LSQLM_HCSO_WRITER
265392f7a3SLiteSpeed Tech#define LSQUIC_LOG_CONN_ID \
275392f7a3SLiteSpeed Tech                    lsquic_conn_log_cid(lsquic_stream_conn(writer->how_stream))
285392f7a3SLiteSpeed Tech#include "lsquic_logger.h"
295392f7a3SLiteSpeed Tech
305392f7a3SLiteSpeed Tech
315392f7a3SLiteSpeed Techstatic int
325392f7a3SLiteSpeed Techhcso_write_type (struct hcso_writer *writer)
335392f7a3SLiteSpeed Tech{
345392f7a3SLiteSpeed Tech    int s;
355392f7a3SLiteSpeed Tech
365392f7a3SLiteSpeed Tech#ifndef NDEBUG
375392f7a3SLiteSpeed Tech    if (writer->how_flags & HOW_RAND_VARINT)
385392f7a3SLiteSpeed Tech    {
395392f7a3SLiteSpeed Tech        s = rand() & 3;
405392f7a3SLiteSpeed Tech        LSQ_DEBUG("writing %d-byte stream type", 1 << s);
415392f7a3SLiteSpeed Tech    }
425392f7a3SLiteSpeed Tech    else
435392f7a3SLiteSpeed Tech#endif
445392f7a3SLiteSpeed Tech        s = 0;
455392f7a3SLiteSpeed Tech
465392f7a3SLiteSpeed Tech    switch (s)
475392f7a3SLiteSpeed Tech    {
485392f7a3SLiteSpeed Tech    case 0:
495392f7a3SLiteSpeed Tech        return lsquic_frab_list_write(&writer->how_fral,
505392f7a3SLiteSpeed Tech                                (unsigned char []) { HQUST_CONTROL }, 1);
515392f7a3SLiteSpeed Tech    case 1:
525392f7a3SLiteSpeed Tech        return lsquic_frab_list_write(&writer->how_fral,
535392f7a3SLiteSpeed Tech                            (unsigned char []) { 0x40, HQUST_CONTROL }, 2);
545392f7a3SLiteSpeed Tech    case 2:
555392f7a3SLiteSpeed Tech        return lsquic_frab_list_write(&writer->how_fral,
565392f7a3SLiteSpeed Tech                (unsigned char []) { 0x80, 0x00, 0x00, HQUST_CONTROL }, 4);
575392f7a3SLiteSpeed Tech    default:
585392f7a3SLiteSpeed Tech        return lsquic_frab_list_write(&writer->how_fral,
595392f7a3SLiteSpeed Tech                (unsigned char []) { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
605392f7a3SLiteSpeed Tech                                                        HQUST_CONTROL }, 8);
615392f7a3SLiteSpeed Tech    }
625392f7a3SLiteSpeed Tech}
635392f7a3SLiteSpeed Tech
645392f7a3SLiteSpeed Tech
655392f7a3SLiteSpeed Tech
665392f7a3SLiteSpeed Techstatic lsquic_stream_ctx_t *
675392f7a3SLiteSpeed Techhcso_on_new (void *stream_if_ctx, struct lsquic_stream *stream)
685392f7a3SLiteSpeed Tech{
695392f7a3SLiteSpeed Tech    struct hcso_writer *writer = stream_if_ctx;
707d09751dSDmitri Tikhonov    struct lsquic_conn *lconn;
717d09751dSDmitri Tikhonov
725392f7a3SLiteSpeed Tech    writer->how_stream = stream;
735392f7a3SLiteSpeed Tech    lsquic_frab_list_init(&writer->how_fral, 0x100, NULL, NULL, NULL);
745392f7a3SLiteSpeed Tech#ifndef NDEBUG
755392f7a3SLiteSpeed Tech    const char *env = getenv("LSQUIC_RND_VARINT_LEN");
765392f7a3SLiteSpeed Tech    if (env && atoi(env))
775392f7a3SLiteSpeed Tech    {
785392f7a3SLiteSpeed Tech        writer->how_flags |= HOW_RAND_VARINT;
795392f7a3SLiteSpeed Tech        LSQ_INFO("will randomize varints");
805392f7a3SLiteSpeed Tech        if (0 == (rand() & 3))
815392f7a3SLiteSpeed Tech        {
825392f7a3SLiteSpeed Tech            writer->how_flags |= HOW_CHOP_STREAM;
835392f7a3SLiteSpeed Tech            LSQ_INFO("will chop beginning of stream into tiny STREAM frames");
845392f7a3SLiteSpeed Tech        }
855392f7a3SLiteSpeed Tech    }
865392f7a3SLiteSpeed Tech#endif
875392f7a3SLiteSpeed Tech    if (0 != hcso_write_type(writer))
885392f7a3SLiteSpeed Tech    {
895392f7a3SLiteSpeed Tech        LSQ_INFO("cannot write to frab list");
907d09751dSDmitri Tikhonov        lconn = lsquic_stream_conn(stream);
917d09751dSDmitri Tikhonov        lconn->cn_if->ci_internal_error(lconn, "cannot write to frab list");
925392f7a3SLiteSpeed Tech    }
935392f7a3SLiteSpeed Tech    LSQ_DEBUG("create HTTP Control Stream Writer");
945392f7a3SLiteSpeed Tech    lsquic_stream_wantwrite(stream, 1);
955392f7a3SLiteSpeed Tech    return stream_if_ctx;
965392f7a3SLiteSpeed Tech}
975392f7a3SLiteSpeed Tech
985392f7a3SLiteSpeed Tech
995392f7a3SLiteSpeed Techstatic unsigned
1005392f7a3SLiteSpeed Techhcso_setting_type2bits (struct hcso_writer *writer, unsigned setting)
1015392f7a3SLiteSpeed Tech{
1025392f7a3SLiteSpeed Tech    unsigned bits = vint_val2bits(setting);
1035392f7a3SLiteSpeed Tech
1045392f7a3SLiteSpeed Tech#ifndef NDEBUG
1055392f7a3SLiteSpeed Tech    unsigned max_bits;
1065392f7a3SLiteSpeed Tech    if (writer->how_flags & HOW_RAND_VARINT)
1075392f7a3SLiteSpeed Tech    {
1085392f7a3SLiteSpeed Tech        max_bits = rand() & 3;
1095392f7a3SLiteSpeed Tech        if (max_bits > bits)
1105392f7a3SLiteSpeed Tech            bits = max_bits;
1115392f7a3SLiteSpeed Tech        LSQ_DEBUG("writing out HTTP/3 setting %u as %d-byte varint",
1125392f7a3SLiteSpeed Tech                                                        setting, 1 << bits);
1135392f7a3SLiteSpeed Tech    }
1145392f7a3SLiteSpeed Tech#endif
1155392f7a3SLiteSpeed Tech
1165392f7a3SLiteSpeed Tech    return bits;
1175392f7a3SLiteSpeed Tech}
1185392f7a3SLiteSpeed Tech
1195392f7a3SLiteSpeed Tech
1205392f7a3SLiteSpeed Techint
1215392f7a3SLiteSpeed Techlsquic_hcso_write_settings (struct hcso_writer *writer,
122758aff32SDmitri Tikhonov                        unsigned max_header_list_size,
123758aff32SDmitri Tikhonov                        unsigned dyn_table_size, unsigned max_risked_streams,
1245392f7a3SLiteSpeed Tech                        int is_server)
1255392f7a3SLiteSpeed Tech{
1265392f7a3SLiteSpeed Tech    unsigned char *p;
1275392f7a3SLiteSpeed Tech    unsigned bits;
1285392f7a3SLiteSpeed Tech    int was_empty;
1295392f7a3SLiteSpeed Tech#ifdef NDEBUG
130fb3e20e0SDmitri Tikhonov#   define frame_size_len 1
1315392f7a3SLiteSpeed Tech#else
1325392f7a3SLiteSpeed Tech    /* Need to use two bytes for frame length, as randomization may require
1335392f7a3SLiteSpeed Tech     * more than 63 bytes.
1345392f7a3SLiteSpeed Tech     */
135fb3e20e0SDmitri Tikhonov#   define frame_size_len 2
1365392f7a3SLiteSpeed Tech#endif
1375392f7a3SLiteSpeed Tech    unsigned char buf[1 /* Frame type */ + /* Frame size */ frame_size_len
13892f6e17bSDmitri Tikhonov        /* There are maximum three settings that need to be written out and
1395392f7a3SLiteSpeed Tech         * each value can be encoded in maximum 8 bytes:
1405392f7a3SLiteSpeed Tech         */
14192f6e17bSDmitri Tikhonov        + 3 * (
1425392f7a3SLiteSpeed Tech#ifdef NDEBUG
1435392f7a3SLiteSpeed Tech            1   /* Each setting needs 1-byte varint number, */
1445392f7a3SLiteSpeed Tech#else
1455392f7a3SLiteSpeed Tech            8   /* but it can be up to 8 bytes when randomized */
1465392f7a3SLiteSpeed Tech#endif
1475392f7a3SLiteSpeed Tech              + 8) ];
1485392f7a3SLiteSpeed Tech
1495392f7a3SLiteSpeed Tech    p = buf;
1505392f7a3SLiteSpeed Tech    *p++ = HQFT_SETTINGS;
1515392f7a3SLiteSpeed Tech    p += frame_size_len;
1525392f7a3SLiteSpeed Tech
153758aff32SDmitri Tikhonov    if (max_header_list_size != HQ_DF_MAX_HEADER_LIST_SIZE)
1545392f7a3SLiteSpeed Tech    {
1555392f7a3SLiteSpeed Tech        /* Write out SETTINGS_MAX_HEADER_LIST_SIZE */
1565392f7a3SLiteSpeed Tech        bits = hcso_setting_type2bits(writer, HQSID_MAX_HEADER_LIST_SIZE);
1575392f7a3SLiteSpeed Tech        vint_write(p, HQSID_MAX_HEADER_LIST_SIZE, bits, 1 << bits);
1585392f7a3SLiteSpeed Tech        p += 1 << bits;
159758aff32SDmitri Tikhonov        bits = vint_val2bits(max_header_list_size);
160758aff32SDmitri Tikhonov        vint_write(p, max_header_list_size, bits, 1 << bits);
1615392f7a3SLiteSpeed Tech        p += 1 << bits;
1625392f7a3SLiteSpeed Tech    }
1635392f7a3SLiteSpeed Tech
164758aff32SDmitri Tikhonov    if (dyn_table_size != HQ_DF_QPACK_MAX_TABLE_CAPACITY)
1655392f7a3SLiteSpeed Tech    {
1665392f7a3SLiteSpeed Tech        /* Write out SETTINGS_QPACK_MAX_TABLE_CAPACITY */
1675392f7a3SLiteSpeed Tech        bits = hcso_setting_type2bits(writer, HQSID_QPACK_MAX_TABLE_CAPACITY);
1685392f7a3SLiteSpeed Tech        vint_write(p, HQSID_QPACK_MAX_TABLE_CAPACITY, bits, 1 << bits);
1695392f7a3SLiteSpeed Tech        p += 1 << bits;
170758aff32SDmitri Tikhonov        bits = vint_val2bits(dyn_table_size);
171758aff32SDmitri Tikhonov        vint_write(p, dyn_table_size, bits, 1 << bits);
1725392f7a3SLiteSpeed Tech        p += 1 << bits;
1735392f7a3SLiteSpeed Tech    }
1745392f7a3SLiteSpeed Tech
175758aff32SDmitri Tikhonov    if (max_risked_streams != HQ_DF_QPACK_BLOCKED_STREAMS)
1765392f7a3SLiteSpeed Tech    {
1775392f7a3SLiteSpeed Tech        /* Write out SETTINGS_QPACK_BLOCKED_STREAMS */
1785392f7a3SLiteSpeed Tech        bits = hcso_setting_type2bits(writer, HQSID_QPACK_BLOCKED_STREAMS);
1795392f7a3SLiteSpeed Tech        vint_write(p, HQSID_QPACK_BLOCKED_STREAMS, bits, 1 << bits);
1805392f7a3SLiteSpeed Tech        p += 1 << bits;
181758aff32SDmitri Tikhonov        bits = vint_val2bits(max_risked_streams);
182758aff32SDmitri Tikhonov        vint_write(p, max_risked_streams, bits, 1 << bits);
1835392f7a3SLiteSpeed Tech        p += 1 << bits;
1845392f7a3SLiteSpeed Tech    }
1855392f7a3SLiteSpeed Tech
1865392f7a3SLiteSpeed Tech#ifdef NDEBUG
1875392f7a3SLiteSpeed Tech    buf[1] = p - buf - 2;
1885392f7a3SLiteSpeed Tech#else
1895392f7a3SLiteSpeed Tech    vint_write(buf + 1, p - buf - 3, 1, 2);
1905392f7a3SLiteSpeed Tech#endif
1915392f7a3SLiteSpeed Tech
1925392f7a3SLiteSpeed Tech    was_empty = lsquic_frab_list_empty(&writer->how_fral);
1935392f7a3SLiteSpeed Tech
1945392f7a3SLiteSpeed Tech    if (0 != lsquic_frab_list_write(&writer->how_fral, buf, p - buf))
1955392f7a3SLiteSpeed Tech    {
1965392f7a3SLiteSpeed Tech        LSQ_INFO("cannot write SETTINGS frame to frab list");
1975392f7a3SLiteSpeed Tech        return -1;
1985392f7a3SLiteSpeed Tech    }
1995392f7a3SLiteSpeed Tech
2005392f7a3SLiteSpeed Tech    if (was_empty)
2015392f7a3SLiteSpeed Tech        lsquic_stream_wantwrite(writer->how_stream, 1);
2025392f7a3SLiteSpeed Tech
2035392f7a3SLiteSpeed Tech    LSQ_DEBUG("generated %u-byte SETTINGS frame", (unsigned) (p - buf));
2045392f7a3SLiteSpeed Tech    return 0;
2055392f7a3SLiteSpeed Tech}
2065392f7a3SLiteSpeed Tech
2075392f7a3SLiteSpeed Tech
2085392f7a3SLiteSpeed Techstatic const char *
2095392f7a3SLiteSpeed Techhqft2str (enum hq_frame_type type)
2105392f7a3SLiteSpeed Tech{
2115392f7a3SLiteSpeed Tech    switch (type)
2125392f7a3SLiteSpeed Tech    {
2135392f7a3SLiteSpeed Tech    case HQFT_PUSH_PROMISE: return "PUSH_PROMISE";
2145392f7a3SLiteSpeed Tech    case HQFT_MAX_PUSH_ID:  return "MAX_PUSH_ID";
2155392f7a3SLiteSpeed Tech    case HQFT_CANCEL_PUSH:  return "CANCEL_PUSH";
2165392f7a3SLiteSpeed Tech    case HQFT_GOAWAY:       return "GOAWAY";
217fbc6cc04SDmitri Tikhonov    case HQFT_PRIORITY_UPDATE_PUSH:return "PRIORITY_UPDATE (push)";
218fbc6cc04SDmitri Tikhonov    case HQFT_PRIORITY_UPDATE_STREAM:return "PRIORITY_UPDATE (stream)";
2195392f7a3SLiteSpeed Tech    default:                return "<unknown>";
2205392f7a3SLiteSpeed Tech    }
2215392f7a3SLiteSpeed Tech}
2225392f7a3SLiteSpeed Tech
2235392f7a3SLiteSpeed Tech
224a5fa05f9SDmitri Tikhonovstatic int
2255392f7a3SLiteSpeed Techhcso_write_number_frame (struct hcso_writer *writer,
2265392f7a3SLiteSpeed Tech                                    enum hq_frame_type type, uint64_t value)
2275392f7a3SLiteSpeed Tech{
2285392f7a3SLiteSpeed Tech    unsigned char *p;
2295392f7a3SLiteSpeed Tech    unsigned bits;
2305392f7a3SLiteSpeed Tech    int was_empty;
2315392f7a3SLiteSpeed Tech    unsigned char buf[1 /* Frame type */ + /* Frame size */ 1 + 8 /* Value */ ];
2325392f7a3SLiteSpeed Tech
2335392f7a3SLiteSpeed Tech    p = buf;
2345392f7a3SLiteSpeed Tech    *p++ = type;
2355392f7a3SLiteSpeed Tech
2365392f7a3SLiteSpeed Tech    bits = vint_val2bits(value);
2375392f7a3SLiteSpeed Tech    *p++ = 1 << bits;
2385392f7a3SLiteSpeed Tech
2395392f7a3SLiteSpeed Tech    vint_write(p, value, bits, 1 << bits);
2405392f7a3SLiteSpeed Tech    p += 1 << bits;
2415392f7a3SLiteSpeed Tech
2425392f7a3SLiteSpeed Tech    was_empty = lsquic_frab_list_empty(&writer->how_fral);
2435392f7a3SLiteSpeed Tech
2445392f7a3SLiteSpeed Tech    if (0 != lsquic_frab_list_write(&writer->how_fral, buf, p - buf))
2455392f7a3SLiteSpeed Tech    {
2465392f7a3SLiteSpeed Tech        LSQ_INFO("cannot write %s frame to frab list", hqft2str(type));
2475392f7a3SLiteSpeed Tech        return -1;
2485392f7a3SLiteSpeed Tech    }
2495392f7a3SLiteSpeed Tech
2505392f7a3SLiteSpeed Tech    if (was_empty)
2515392f7a3SLiteSpeed Tech        lsquic_stream_wantwrite(writer->how_stream, 1);
2525392f7a3SLiteSpeed Tech
2535392f7a3SLiteSpeed Tech    LSQ_DEBUG("generated %u-byte %s frame", (unsigned) (p - buf),
2545392f7a3SLiteSpeed Tech                                                            hqft2str(type));
2555392f7a3SLiteSpeed Tech    return 0;
2565392f7a3SLiteSpeed Tech}
2575392f7a3SLiteSpeed Tech
2585392f7a3SLiteSpeed Tech
2595392f7a3SLiteSpeed Techint
2605392f7a3SLiteSpeed Techlsquic_hcso_write_goaway (struct hcso_writer *writer,
2615392f7a3SLiteSpeed Tech                                            lsquic_stream_id_t stream_id)
2625392f7a3SLiteSpeed Tech{
2635392f7a3SLiteSpeed Tech    return hcso_write_number_frame(writer, HQFT_GOAWAY, stream_id);
2645392f7a3SLiteSpeed Tech}
2655392f7a3SLiteSpeed Tech
2665392f7a3SLiteSpeed Tech
2675392f7a3SLiteSpeed Techint
2685392f7a3SLiteSpeed Techlsquic_hcso_write_max_push_id (struct hcso_writer *writer, uint64_t max_push_id)
2695392f7a3SLiteSpeed Tech{
2705392f7a3SLiteSpeed Tech    return hcso_write_number_frame(writer, HQFT_MAX_PUSH_ID, max_push_id);
2715392f7a3SLiteSpeed Tech}
2725392f7a3SLiteSpeed Tech
2735392f7a3SLiteSpeed Tech
2745392f7a3SLiteSpeed Techint
2755392f7a3SLiteSpeed Techlsquic_hcso_write_cancel_push (struct hcso_writer *writer, uint64_t push_id)
2765392f7a3SLiteSpeed Tech{
2775392f7a3SLiteSpeed Tech    return hcso_write_number_frame(writer, HQFT_CANCEL_PUSH, push_id);
2785392f7a3SLiteSpeed Tech}
2795392f7a3SLiteSpeed Tech
2805392f7a3SLiteSpeed Tech
281fbc6cc04SDmitri Tikhonovint
282fbc6cc04SDmitri Tikhonovlsquic_hcso_write_priority_update (struct hcso_writer *writer,
283fbc6cc04SDmitri Tikhonov                enum hq_frame_type type, uint64_t stream_or_push_id,
284fbc6cc04SDmitri Tikhonov                const struct lsquic_ext_http_prio *ehp)
285fbc6cc04SDmitri Tikhonov{
286fbc6cc04SDmitri Tikhonov    unsigned char *p, *len;
287fbc6cc04SDmitri Tikhonov    unsigned bits;
288fbc6cc04SDmitri Tikhonov    int was_empty;
289fbc6cc04SDmitri Tikhonov    unsigned char buf[8 /* Frame type */ + /* Frame size */ 1 + 8 /* Value */
290fbc6cc04SDmitri Tikhonov                    + 5 /* PFV: "u=.,i" or "u=." */];
291fbc6cc04SDmitri Tikhonov
292fbc6cc04SDmitri Tikhonov    p = buf;
293fbc6cc04SDmitri Tikhonov    bits = vint_val2bits(type);
294fbc6cc04SDmitri Tikhonov    vint_write(p, type, bits, 1 << bits);
295fbc6cc04SDmitri Tikhonov    p += 1 << bits;
296fbc6cc04SDmitri Tikhonov
297fbc6cc04SDmitri Tikhonov    bits = vint_val2bits(stream_or_push_id);
298fbc6cc04SDmitri Tikhonov    len = p;
299fbc6cc04SDmitri Tikhonov    ++p;
300fbc6cc04SDmitri Tikhonov
301fbc6cc04SDmitri Tikhonov    vint_write(p, stream_or_push_id, bits, 1 << bits);
302fbc6cc04SDmitri Tikhonov    p += 1 << bits;
303fbc6cc04SDmitri Tikhonov    if (!(ehp->urgency == LSQUIC_DEF_HTTP_URGENCY
304fbc6cc04SDmitri Tikhonov                        && ehp->incremental == LSQUIC_DEF_HTTP_INCREMENTAL))
305fbc6cc04SDmitri Tikhonov    {
306fbc6cc04SDmitri Tikhonov        *p++ = 'u';
307fbc6cc04SDmitri Tikhonov        *p++ = '=';
308fbc6cc04SDmitri Tikhonov        *p++ = '0' + ehp->urgency;
309fbc6cc04SDmitri Tikhonov        if (ehp->incremental)
310fbc6cc04SDmitri Tikhonov        {
311fbc6cc04SDmitri Tikhonov            *p++ = ',';
312fbc6cc04SDmitri Tikhonov            *p++ = 'i';
313fbc6cc04SDmitri Tikhonov        }
314fbc6cc04SDmitri Tikhonov    }
315fbc6cc04SDmitri Tikhonov
316fbc6cc04SDmitri Tikhonov    *len = p - len - 1;
317fbc6cc04SDmitri Tikhonov
318fbc6cc04SDmitri Tikhonov    was_empty = lsquic_frab_list_empty(&writer->how_fral);
319fbc6cc04SDmitri Tikhonov
320fbc6cc04SDmitri Tikhonov    if (0 != lsquic_frab_list_write(&writer->how_fral, buf, p - buf))
321fbc6cc04SDmitri Tikhonov    {
322fbc6cc04SDmitri Tikhonov        LSQ_INFO("cannot write %s frame to frab list", hqft2str(type));
323fbc6cc04SDmitri Tikhonov        return -1;
324fbc6cc04SDmitri Tikhonov    }
325fbc6cc04SDmitri Tikhonov
326fbc6cc04SDmitri Tikhonov    if (was_empty)
327fbc6cc04SDmitri Tikhonov        lsquic_stream_wantwrite(writer->how_stream, 1);
328fbc6cc04SDmitri Tikhonov
329fbc6cc04SDmitri Tikhonov    LSQ_DEBUG("generated %u-byte %s frame", (unsigned) (p - buf),
330fbc6cc04SDmitri Tikhonov                                                            hqft2str(type));
331fbc6cc04SDmitri Tikhonov    return 0;
332fbc6cc04SDmitri Tikhonov}
333fbc6cc04SDmitri Tikhonov
334fbc6cc04SDmitri Tikhonov
3355392f7a3SLiteSpeed Tech#ifndef NDEBUG
3365392f7a3SLiteSpeed Tech#define MIN(a, b) ((a) < (b) ? (a) : (b))
3375392f7a3SLiteSpeed Techstatic size_t
3385392f7a3SLiteSpeed Techone_byte_limit_read (void *ctx, void *buf, size_t bufsz)
3395392f7a3SLiteSpeed Tech{
3405392f7a3SLiteSpeed Tech    return lsquic_frab_list_read(ctx, buf, MIN(bufsz, 1));
3415392f7a3SLiteSpeed Tech}
3425392f7a3SLiteSpeed Tech
3435392f7a3SLiteSpeed Tech
3445392f7a3SLiteSpeed Techstatic size_t
3455392f7a3SLiteSpeed Techone_byte_limit_size (void *ctx)
3465392f7a3SLiteSpeed Tech{
3475392f7a3SLiteSpeed Tech    size_t size;
3485392f7a3SLiteSpeed Tech
3495392f7a3SLiteSpeed Tech    size = lsquic_frab_list_size(ctx);
3505392f7a3SLiteSpeed Tech    return MIN(size, 1);
3515392f7a3SLiteSpeed Tech}
3525392f7a3SLiteSpeed Tech#endif
3535392f7a3SLiteSpeed Tech
3545392f7a3SLiteSpeed Techstatic void
3555392f7a3SLiteSpeed Techhcso_on_write (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx)
3565392f7a3SLiteSpeed Tech{
3575392f7a3SLiteSpeed Tech    struct hcso_writer *const writer = (void *) ctx;
3585392f7a3SLiteSpeed Tech    struct lsquic_reader reader = {
3595392f7a3SLiteSpeed Tech        .lsqr_read  = lsquic_frab_list_read,
3605392f7a3SLiteSpeed Tech        .lsqr_size  = lsquic_frab_list_size,
3615392f7a3SLiteSpeed Tech        .lsqr_ctx   = &writer->how_fral
3625392f7a3SLiteSpeed Tech    };
3635392f7a3SLiteSpeed Tech    ssize_t nw;
3647d09751dSDmitri Tikhonov    struct lsquic_conn *lconn;
3655392f7a3SLiteSpeed Tech
3665392f7a3SLiteSpeed Tech#ifndef NDEBUG
3675392f7a3SLiteSpeed Tech    if (stream->tosend_off < 8 && (writer->how_flags & HOW_CHOP_STREAM))
3685392f7a3SLiteSpeed Tech    {
3695392f7a3SLiteSpeed Tech        reader.lsqr_read = one_byte_limit_read;
3705392f7a3SLiteSpeed Tech        reader.lsqr_size = one_byte_limit_size;
3715392f7a3SLiteSpeed Tech    }
3725392f7a3SLiteSpeed Tech#endif
3735392f7a3SLiteSpeed Tech
3745392f7a3SLiteSpeed Tech    nw = lsquic_stream_writef(stream, &reader);
3755392f7a3SLiteSpeed Tech    if (nw >= 0)
3765392f7a3SLiteSpeed Tech    {
3775392f7a3SLiteSpeed Tech        LSQ_DEBUG("wrote %zd bytes to stream", nw);
3785392f7a3SLiteSpeed Tech        (void) lsquic_stream_flush(stream);
3795392f7a3SLiteSpeed Tech        if (lsquic_frab_list_empty(&writer->how_fral))
3805392f7a3SLiteSpeed Tech            lsquic_stream_wantwrite(stream, 0);
3815392f7a3SLiteSpeed Tech    }
3825392f7a3SLiteSpeed Tech    else
3835392f7a3SLiteSpeed Tech    {
3847d09751dSDmitri Tikhonov        lconn = lsquic_stream_conn(stream);
3857d09751dSDmitri Tikhonov        lconn->cn_if->ci_internal_error(lconn, "cannot write to stream: %s",
3867d09751dSDmitri Tikhonov                                                            strerror(errno));
3875392f7a3SLiteSpeed Tech        lsquic_stream_wantwrite(stream, 0);
3885392f7a3SLiteSpeed Tech    }
3895392f7a3SLiteSpeed Tech}
3905392f7a3SLiteSpeed Tech
3915392f7a3SLiteSpeed Tech
3925392f7a3SLiteSpeed Techstatic void
3935392f7a3SLiteSpeed Techhcso_on_close (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx)
3945392f7a3SLiteSpeed Tech{
3955392f7a3SLiteSpeed Tech    struct hcso_writer *writer = (void *) ctx;
3965392f7a3SLiteSpeed Tech    LSQ_DEBUG("close HTTP Control Stream Writer");
3975392f7a3SLiteSpeed Tech    lsquic_frab_list_cleanup(&writer->how_fral);
3985392f7a3SLiteSpeed Tech    writer->how_stream = NULL;
3995392f7a3SLiteSpeed Tech}
4005392f7a3SLiteSpeed Tech
4015392f7a3SLiteSpeed Tech
4025392f7a3SLiteSpeed Techstatic void
4035392f7a3SLiteSpeed Techhcso_on_read (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx)
4045392f7a3SLiteSpeed Tech{
4055392f7a3SLiteSpeed Tech    assert(0);
4065392f7a3SLiteSpeed Tech}
4075392f7a3SLiteSpeed Tech
4085392f7a3SLiteSpeed Tech
4095392f7a3SLiteSpeed Techstatic const struct lsquic_stream_if hcso_if =
4105392f7a3SLiteSpeed Tech{
4115392f7a3SLiteSpeed Tech    .on_new_stream  = hcso_on_new,
4125392f7a3SLiteSpeed Tech    .on_read        = hcso_on_read,
4135392f7a3SLiteSpeed Tech    .on_write       = hcso_on_write,
4145392f7a3SLiteSpeed Tech    .on_close       = hcso_on_close,
4155392f7a3SLiteSpeed Tech};
4165392f7a3SLiteSpeed Tech
4175392f7a3SLiteSpeed Techconst struct lsquic_stream_if *const lsquic_hcso_writer_if = &hcso_if;
418