lsquic_hcso_writer.c revision 7d09751d
1/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc.  See LICENSE. */
2/*
3 * lsquic_hcso_writer.c - write to outgoing HTTP Control Stream
4 */
5
6#include <assert.h>
7#include <errno.h>
8#include <stdlib.h>
9#include <string.h>
10#include <sys/queue.h>
11
12#include "lsquic.h"
13#include "lsquic_types.h"
14#include "lsquic_int_types.h"
15#include "lsquic_sfcw.h"
16#include "lsquic_varint.h"
17#include "lsquic_hq.h"
18#include "lsquic_hash.h"
19#include "lsquic_stream.h"
20#include "lsquic_frab_list.h"
21#include "lsquic_varint.h"
22#include "lsquic_byteswap.h"
23#include "lsquic_hcso_writer.h"
24#include "lsquic_conn.h"
25
26#define LSQUIC_LOGGER_MODULE LSQLM_HCSO_WRITER
27#define LSQUIC_LOG_CONN_ID \
28                    lsquic_conn_log_cid(lsquic_stream_conn(writer->how_stream))
29#include "lsquic_logger.h"
30
31
32static int
33hcso_write_type (struct hcso_writer *writer)
34{
35    int s;
36
37#ifndef NDEBUG
38    if (writer->how_flags & HOW_RAND_VARINT)
39    {
40        s = rand() & 3;
41        LSQ_DEBUG("writing %d-byte stream type", 1 << s);
42    }
43    else
44#endif
45        s = 0;
46
47    switch (s)
48    {
49    case 0:
50        return lsquic_frab_list_write(&writer->how_fral,
51                                (unsigned char []) { HQUST_CONTROL }, 1);
52    case 1:
53        return lsquic_frab_list_write(&writer->how_fral,
54                            (unsigned char []) { 0x40, HQUST_CONTROL }, 2);
55    case 2:
56        return lsquic_frab_list_write(&writer->how_fral,
57                (unsigned char []) { 0x80, 0x00, 0x00, HQUST_CONTROL }, 4);
58    default:
59        return lsquic_frab_list_write(&writer->how_fral,
60                (unsigned char []) { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
61                                                        HQUST_CONTROL }, 8);
62    }
63}
64
65
66
67static lsquic_stream_ctx_t *
68hcso_on_new (void *stream_if_ctx, struct lsquic_stream *stream)
69{
70    struct hcso_writer *writer = stream_if_ctx;
71    struct lsquic_conn *lconn;
72
73    writer->how_stream = stream;
74    lsquic_frab_list_init(&writer->how_fral, 0x100, NULL, NULL, NULL);
75#ifndef NDEBUG
76    const char *env = getenv("LSQUIC_RND_VARINT_LEN");
77    if (env && atoi(env))
78    {
79        writer->how_flags |= HOW_RAND_VARINT;
80        LSQ_INFO("will randomize varints");
81        if (0 == (rand() & 3))
82        {
83            writer->how_flags |= HOW_CHOP_STREAM;
84            LSQ_INFO("will chop beginning of stream into tiny STREAM frames");
85        }
86    }
87#endif
88    if (0 != hcso_write_type(writer))
89    {
90        LSQ_INFO("cannot write to frab list");
91        lconn = lsquic_stream_conn(stream);
92        lconn->cn_if->ci_internal_error(lconn, "cannot write to frab list");
93    }
94    LSQ_DEBUG("create HTTP Control Stream Writer");
95    lsquic_stream_wantwrite(stream, 1);
96    return stream_if_ctx;
97}
98
99
100static unsigned
101hcso_setting_type2bits (struct hcso_writer *writer, unsigned setting)
102{
103    unsigned bits = vint_val2bits(setting);
104
105#ifndef NDEBUG
106    unsigned max_bits;
107    if (writer->how_flags & HOW_RAND_VARINT)
108    {
109        max_bits = rand() & 3;
110        if (max_bits > bits)
111            bits = max_bits;
112        LSQ_DEBUG("writing out HTTP/3 setting %u as %d-byte varint",
113                                                        setting, 1 << bits);
114    }
115#endif
116
117    return bits;
118}
119
120
121int
122lsquic_hcso_write_settings (struct hcso_writer *writer,
123                        const struct lsquic_engine_settings *settings,
124                        int is_server)
125{
126    unsigned char *p;
127    unsigned bits;
128    int was_empty;
129#ifdef NDEBUG
130    const unsigned frame_size_len = 1;
131#else
132    /* Need to use two bytes for frame length, as randomization may require
133     * more than 63 bytes.
134     */
135    const unsigned frame_size_len = 2;
136#endif
137    unsigned char buf[1 /* Frame type */ + /* Frame size */ frame_size_len
138        /* There are maximum three settings that need to be written out and
139         * each value can be encoded in maximum 8 bytes:
140         */
141        + 3 * (
142#ifdef NDEBUG
143            1   /* Each setting needs 1-byte varint number, */
144#else
145            8   /* but it can be up to 8 bytes when randomized */
146#endif
147              + 8) ];
148
149    p = buf;
150    *p++ = HQFT_SETTINGS;
151    p += frame_size_len;
152
153    if (settings->es_max_header_list_size != HQ_DF_MAX_HEADER_LIST_SIZE)
154    {
155        /* Write out SETTINGS_MAX_HEADER_LIST_SIZE */
156        bits = hcso_setting_type2bits(writer, HQSID_MAX_HEADER_LIST_SIZE);
157        vint_write(p, HQSID_MAX_HEADER_LIST_SIZE, bits, 1 << bits);
158        p += 1 << bits;
159        bits = vint_val2bits(settings->es_max_header_list_size);
160        vint_write(p, settings->es_max_header_list_size, bits, 1 << bits);
161        p += 1 << bits;
162    }
163
164    if (settings->es_qpack_dec_max_size != HQ_DF_QPACK_MAX_TABLE_CAPACITY)
165    {
166        /* Write out SETTINGS_QPACK_MAX_TABLE_CAPACITY */
167        bits = hcso_setting_type2bits(writer, HQSID_QPACK_MAX_TABLE_CAPACITY);
168        vint_write(p, HQSID_QPACK_MAX_TABLE_CAPACITY, bits, 1 << bits);
169        p += 1 << bits;
170        bits = vint_val2bits(settings->es_qpack_dec_max_size);
171        vint_write(p, settings->es_qpack_dec_max_size, bits, 1 << bits);
172        p += 1 << bits;
173    }
174
175    if (settings->es_qpack_dec_max_blocked != HQ_DF_QPACK_BLOCKED_STREAMS)
176    {
177        /* Write out SETTINGS_QPACK_BLOCKED_STREAMS */
178        bits = hcso_setting_type2bits(writer, HQSID_QPACK_BLOCKED_STREAMS);
179        vint_write(p, HQSID_QPACK_BLOCKED_STREAMS, bits, 1 << bits);
180        p += 1 << bits;
181        bits = vint_val2bits(settings->es_qpack_dec_max_blocked);
182        vint_write(p, settings->es_qpack_dec_max_blocked, bits, 1 << bits);
183        p += 1 << bits;
184    }
185
186#ifdef NDEBUG
187    buf[1] = p - buf - 2;
188#else
189    vint_write(buf + 1, p - buf - 3, 1, 2);
190#endif
191
192    was_empty = lsquic_frab_list_empty(&writer->how_fral);
193
194    if (0 != lsquic_frab_list_write(&writer->how_fral, buf, p - buf))
195    {
196        LSQ_INFO("cannot write SETTINGS frame to frab list");
197        return -1;
198    }
199
200    if (was_empty)
201        lsquic_stream_wantwrite(writer->how_stream, 1);
202
203    LSQ_DEBUG("generated %u-byte SETTINGS frame", (unsigned) (p - buf));
204    return 0;
205}
206
207
208static const char *
209hqft2str (enum hq_frame_type type)
210{
211    switch (type)
212    {
213    case HQFT_PUSH_PROMISE: return "PUSH_PROMISE";
214    case HQFT_MAX_PUSH_ID:  return "MAX_PUSH_ID";
215    case HQFT_CANCEL_PUSH:  return "CANCEL_PUSH";
216    case HQFT_GOAWAY:       return "GOAWAY";
217    default:                return "<unknown>";
218    }
219}
220
221
222int
223hcso_write_number_frame (struct hcso_writer *writer,
224                                    enum hq_frame_type type, uint64_t value)
225{
226    unsigned char *p;
227    unsigned bits;
228    int was_empty;
229    unsigned char buf[1 /* Frame type */ + /* Frame size */ 1 + 8 /* Value */ ];
230
231    p = buf;
232    *p++ = type;
233
234    bits = vint_val2bits(value);
235    *p++ = 1 << bits;
236
237    vint_write(p, value, bits, 1 << bits);
238    p += 1 << bits;
239
240    was_empty = lsquic_frab_list_empty(&writer->how_fral);
241
242    if (0 != lsquic_frab_list_write(&writer->how_fral, buf, p - buf))
243    {
244        LSQ_INFO("cannot write %s frame to frab list", hqft2str(type));
245        return -1;
246    }
247
248    if (was_empty)
249        lsquic_stream_wantwrite(writer->how_stream, 1);
250
251    LSQ_DEBUG("generated %u-byte %s frame", (unsigned) (p - buf),
252                                                            hqft2str(type));
253    return 0;
254}
255
256
257int
258lsquic_hcso_write_goaway (struct hcso_writer *writer,
259                                            lsquic_stream_id_t stream_id)
260{
261    return hcso_write_number_frame(writer, HQFT_GOAWAY, stream_id);
262}
263
264
265int
266lsquic_hcso_write_max_push_id (struct hcso_writer *writer, uint64_t max_push_id)
267{
268    return hcso_write_number_frame(writer, HQFT_MAX_PUSH_ID, max_push_id);
269}
270
271
272int
273lsquic_hcso_write_cancel_push (struct hcso_writer *writer, uint64_t push_id)
274{
275    return hcso_write_number_frame(writer, HQFT_CANCEL_PUSH, push_id);
276}
277
278
279#ifndef NDEBUG
280#define MIN(a, b) ((a) < (b) ? (a) : (b))
281static size_t
282one_byte_limit_read (void *ctx, void *buf, size_t bufsz)
283{
284    return lsquic_frab_list_read(ctx, buf, MIN(bufsz, 1));
285}
286
287
288static size_t
289one_byte_limit_size (void *ctx)
290{
291    size_t size;
292
293    size = lsquic_frab_list_size(ctx);
294    return MIN(size, 1);
295}
296#endif
297
298static void
299hcso_on_write (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx)
300{
301    struct hcso_writer *const writer = (void *) ctx;
302    struct lsquic_reader reader = {
303        .lsqr_read  = lsquic_frab_list_read,
304        .lsqr_size  = lsquic_frab_list_size,
305        .lsqr_ctx   = &writer->how_fral
306    };
307    ssize_t nw;
308    struct lsquic_conn *lconn;
309
310#ifndef NDEBUG
311    if (stream->tosend_off < 8 && (writer->how_flags & HOW_CHOP_STREAM))
312    {
313        reader.lsqr_read = one_byte_limit_read;
314        reader.lsqr_size = one_byte_limit_size;
315    }
316#endif
317
318    nw = lsquic_stream_writef(stream, &reader);
319    if (nw >= 0)
320    {
321        LSQ_DEBUG("wrote %zd bytes to stream", nw);
322        (void) lsquic_stream_flush(stream);
323        if (lsquic_frab_list_empty(&writer->how_fral))
324            lsquic_stream_wantwrite(stream, 0);
325    }
326    else
327    {
328        lconn = lsquic_stream_conn(stream);
329        lconn->cn_if->ci_internal_error(lconn, "cannot write to stream: %s",
330                                                            strerror(errno));
331        lsquic_stream_wantwrite(stream, 0);
332    }
333}
334
335
336static void
337hcso_on_close (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx)
338{
339    struct hcso_writer *writer = (void *) ctx;
340    LSQ_DEBUG("close HTTP Control Stream Writer");
341    lsquic_frab_list_cleanup(&writer->how_fral);
342    writer->how_stream = NULL;
343}
344
345
346static void
347hcso_on_read (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx)
348{
349    assert(0);
350}
351
352
353static const struct lsquic_stream_if hcso_if =
354{
355    .on_new_stream  = hcso_on_new,
356    .on_read        = hcso_on_read,
357    .on_write       = hcso_on_write,
358    .on_close       = hcso_on_close,
359};
360
361const struct lsquic_stream_if *const lsquic_hcso_writer_if = &hcso_if;
362