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