lsquic_hcso_writer.c revision 4947ba95
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 three settings that need to be written out and
135         * each value can be encoded in maximum 8 bytes:
136         */
137        + 3 * (
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 (settings->es_max_header_list_size != HQ_DF_MAX_HEADER_LIST_SIZE)
150    {
151        /* Write out SETTINGS_MAX_HEADER_LIST_SIZE */
152        bits = hcso_setting_type2bits(writer, HQSID_MAX_HEADER_LIST_SIZE);
153        vint_write(p, HQSID_MAX_HEADER_LIST_SIZE, bits, 1 << bits);
154        p += 1 << bits;
155        bits = vint_val2bits(settings->es_max_header_list_size);
156        vint_write(p, settings->es_max_header_list_size, bits, 1 << bits);
157        p += 1 << bits;
158    }
159
160    if (settings->es_qpack_dec_max_size != HQ_DF_QPACK_MAX_TABLE_CAPACITY)
161    {
162        /* Write out SETTINGS_QPACK_MAX_TABLE_CAPACITY */
163        bits = hcso_setting_type2bits(writer, HQSID_QPACK_MAX_TABLE_CAPACITY);
164        vint_write(p, HQSID_QPACK_MAX_TABLE_CAPACITY, bits, 1 << bits);
165        p += 1 << bits;
166        bits = vint_val2bits(settings->es_qpack_dec_max_size);
167        vint_write(p, settings->es_qpack_dec_max_size, bits, 1 << bits);
168        p += 1 << bits;
169    }
170
171    if (settings->es_qpack_dec_max_blocked != HQ_DF_QPACK_BLOCKED_STREAMS)
172    {
173        /* Write out SETTINGS_QPACK_BLOCKED_STREAMS */
174        bits = hcso_setting_type2bits(writer, HQSID_QPACK_BLOCKED_STREAMS);
175        vint_write(p, HQSID_QPACK_BLOCKED_STREAMS, bits, 1 << bits);
176        p += 1 << bits;
177        bits = vint_val2bits(settings->es_qpack_dec_max_blocked);
178        vint_write(p, settings->es_qpack_dec_max_blocked, bits, 1 << bits);
179        p += 1 << bits;
180    }
181
182#ifdef NDEBUG
183    buf[1] = p - buf - 2;
184#else
185    vint_write(buf + 1, p - buf - 3, 1, 2);
186#endif
187
188    was_empty = lsquic_frab_list_empty(&writer->how_fral);
189
190    if (0 != lsquic_frab_list_write(&writer->how_fral, buf, p - buf))
191    {
192        LSQ_INFO("cannot write SETTINGS frame to frab list");
193        return -1;
194    }
195
196    if (was_empty)
197        lsquic_stream_wantwrite(writer->how_stream, 1);
198
199    LSQ_DEBUG("generated %u-byte SETTINGS frame", (unsigned) (p - buf));
200    return 0;
201}
202
203
204static const char *
205hqft2str (enum hq_frame_type type)
206{
207    switch (type)
208    {
209    case HQFT_PUSH_PROMISE: return "PUSH_PROMISE";
210    case HQFT_MAX_PUSH_ID:  return "MAX_PUSH_ID";
211    case HQFT_CANCEL_PUSH:  return "CANCEL_PUSH";
212    case HQFT_GOAWAY:       return "GOAWAY";
213    default:                return "<unknown>";
214    }
215}
216
217
218int
219hcso_write_number_frame (struct hcso_writer *writer,
220                                    enum hq_frame_type type, uint64_t value)
221{
222    unsigned char *p;
223    unsigned bits;
224    int was_empty;
225    unsigned char buf[1 /* Frame type */ + /* Frame size */ 1 + 8 /* Value */ ];
226
227    p = buf;
228    *p++ = type;
229
230    bits = vint_val2bits(value);
231    *p++ = 1 << bits;
232
233    vint_write(p, value, bits, 1 << bits);
234    p += 1 << bits;
235
236    was_empty = lsquic_frab_list_empty(&writer->how_fral);
237
238    if (0 != lsquic_frab_list_write(&writer->how_fral, buf, p - buf))
239    {
240        LSQ_INFO("cannot write %s frame to frab list", hqft2str(type));
241        return -1;
242    }
243
244    if (was_empty)
245        lsquic_stream_wantwrite(writer->how_stream, 1);
246
247    LSQ_DEBUG("generated %u-byte %s frame", (unsigned) (p - buf),
248                                                            hqft2str(type));
249    return 0;
250}
251
252
253int
254lsquic_hcso_write_goaway (struct hcso_writer *writer,
255                                            lsquic_stream_id_t stream_id)
256{
257    return hcso_write_number_frame(writer, HQFT_GOAWAY, stream_id);
258}
259
260
261int
262lsquic_hcso_write_max_push_id (struct hcso_writer *writer, uint64_t max_push_id)
263{
264    return hcso_write_number_frame(writer, HQFT_MAX_PUSH_ID, max_push_id);
265}
266
267
268int
269lsquic_hcso_write_cancel_push (struct hcso_writer *writer, uint64_t push_id)
270{
271    return hcso_write_number_frame(writer, HQFT_CANCEL_PUSH, push_id);
272}
273
274
275#ifndef NDEBUG
276#define MIN(a, b) ((a) < (b) ? (a) : (b))
277static size_t
278one_byte_limit_read (void *ctx, void *buf, size_t bufsz)
279{
280    return lsquic_frab_list_read(ctx, buf, MIN(bufsz, 1));
281}
282
283
284static size_t
285one_byte_limit_size (void *ctx)
286{
287    size_t size;
288
289    size = lsquic_frab_list_size(ctx);
290    return MIN(size, 1);
291}
292#endif
293
294static void
295hcso_on_write (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx)
296{
297    struct hcso_writer *const writer = (void *) ctx;
298    struct lsquic_reader reader = {
299        .lsqr_read  = lsquic_frab_list_read,
300        .lsqr_size  = lsquic_frab_list_size,
301        .lsqr_ctx   = &writer->how_fral
302    };
303    ssize_t nw;
304
305#ifndef NDEBUG
306    if (stream->tosend_off < 8 && (writer->how_flags & HOW_CHOP_STREAM))
307    {
308        reader.lsqr_read = one_byte_limit_read;
309        reader.lsqr_size = one_byte_limit_size;
310    }
311#endif
312
313    nw = lsquic_stream_writef(stream, &reader);
314    if (nw >= 0)
315    {
316        LSQ_DEBUG("wrote %zd bytes to stream", nw);
317        (void) lsquic_stream_flush(stream);
318        if (lsquic_frab_list_empty(&writer->how_fral))
319            lsquic_stream_wantwrite(stream, 0);
320    }
321    else
322    {
323        /* TODO: abort connection */
324        LSQ_WARN("cannot write to stream: %s", strerror(errno));
325        lsquic_stream_wantwrite(stream, 0);
326    }
327}
328
329
330static void
331hcso_on_close (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx)
332{
333    struct hcso_writer *writer = (void *) ctx;
334    LSQ_DEBUG("close HTTP Control Stream Writer");
335    lsquic_frab_list_cleanup(&writer->how_fral);
336    writer->how_stream = NULL;
337}
338
339
340static void
341hcso_on_read (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx)
342{
343    assert(0);
344}
345
346
347static const struct lsquic_stream_if hcso_if =
348{
349    .on_new_stream  = hcso_on_new,
350    .on_read        = hcso_on_read,
351    .on_write       = hcso_on_write,
352    .on_close       = hcso_on_close,
353};
354
355const struct lsquic_stream_if *const lsquic_hcso_writer_if = &hcso_if;
356