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