lsquic_hcso_writer.c revision 06b2a236
1/* Copyright (c) 2017 - 2021 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                        unsigned max_header_list_size,
123                        unsigned dyn_table_size, unsigned max_risked_streams,
124                        int is_server)
125{
126    unsigned char *p;
127    unsigned bits;
128    int was_empty;
129#ifdef NDEBUG
130#   define 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#   define 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 (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(max_header_list_size);
160        vint_write(p, max_header_list_size, bits, 1 << bits);
161        p += 1 << bits;
162    }
163
164    if (dyn_table_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(dyn_table_size);
171        vint_write(p, dyn_table_size, bits, 1 << bits);
172        p += 1 << bits;
173    }
174
175    if (max_risked_streams != 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(max_risked_streams);
182        vint_write(p, max_risked_streams, 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    case HQFT_PRIORITY_UPDATE_PUSH:return "PRIORITY_UPDATE (push)";
218    case HQFT_PRIORITY_UPDATE_STREAM:return "PRIORITY_UPDATE (stream)";
219    default:                return "<unknown>";
220    }
221}
222
223
224static int
225hcso_write_number_frame (struct hcso_writer *writer,
226                                    enum hq_frame_type type, uint64_t value)
227{
228    unsigned char *p;
229    unsigned bits;
230    int was_empty;
231    unsigned char buf[1 /* Frame type */ + /* Frame size */ 1 + 8 /* Value */ ];
232
233    p = buf;
234    *p++ = type;
235
236    bits = vint_val2bits(value);
237    *p++ = 1 << bits;
238
239    vint_write(p, value, bits, 1 << bits);
240    p += 1 << bits;
241
242    was_empty = lsquic_frab_list_empty(&writer->how_fral);
243
244    if (0 != lsquic_frab_list_write(&writer->how_fral, buf, p - buf))
245    {
246        LSQ_INFO("cannot write %s frame to frab list", hqft2str(type));
247        return -1;
248    }
249
250    if (was_empty)
251        lsquic_stream_wantwrite(writer->how_stream, 1);
252
253    LSQ_DEBUG("generated %u-byte %s frame", (unsigned) (p - buf),
254                                                            hqft2str(type));
255    return 0;
256}
257
258
259int
260lsquic_hcso_write_goaway (struct hcso_writer *writer,
261                                            lsquic_stream_id_t stream_id)
262{
263    return hcso_write_number_frame(writer, HQFT_GOAWAY, stream_id);
264}
265
266
267int
268lsquic_hcso_write_max_push_id (struct hcso_writer *writer, uint64_t max_push_id)
269{
270    return hcso_write_number_frame(writer, HQFT_MAX_PUSH_ID, max_push_id);
271}
272
273
274int
275lsquic_hcso_write_cancel_push (struct hcso_writer *writer, uint64_t push_id)
276{
277    return hcso_write_number_frame(writer, HQFT_CANCEL_PUSH, push_id);
278}
279
280
281int
282lsquic_hcso_write_priority_update (struct hcso_writer *writer,
283                enum hq_frame_type type, uint64_t stream_or_push_id,
284                const struct lsquic_ext_http_prio *ehp)
285{
286    unsigned char *p, *len;
287    unsigned bits;
288    int was_empty;
289    unsigned char buf[8 /* Frame type */ + /* Frame size */ 1 + 8 /* Value */
290                    + 5 /* PFV: "u=.,i" or "u=." */];
291
292    p = buf;
293    bits = vint_val2bits(type);
294    vint_write(p, type, bits, 1 << bits);
295    p += 1 << bits;
296
297    bits = vint_val2bits(stream_or_push_id);
298    len = p;
299    ++p;
300
301    vint_write(p, stream_or_push_id, bits, 1 << bits);
302    p += 1 << bits;
303    if (!(ehp->urgency == LSQUIC_DEF_HTTP_URGENCY
304                        && ehp->incremental == LSQUIC_DEF_HTTP_INCREMENTAL))
305    {
306        *p++ = 'u';
307        *p++ = '=';
308        *p++ = '0' + ehp->urgency;
309        if (ehp->incremental)
310        {
311            *p++ = ',';
312            *p++ = 'i';
313        }
314    }
315
316    *len = p - len - 1;
317
318    was_empty = lsquic_frab_list_empty(&writer->how_fral);
319
320    if (0 != lsquic_frab_list_write(&writer->how_fral, buf, p - buf))
321    {
322        LSQ_INFO("cannot write %s frame to frab list", hqft2str(type));
323        return -1;
324    }
325
326    if (was_empty)
327        lsquic_stream_wantwrite(writer->how_stream, 1);
328
329    LSQ_DEBUG("generated %u-byte %s frame", (unsigned) (p - buf),
330                                                            hqft2str(type));
331    return 0;
332}
333
334
335#ifndef NDEBUG
336#define MIN(a, b) ((a) < (b) ? (a) : (b))
337static size_t
338one_byte_limit_read (void *ctx, void *buf, size_t bufsz)
339{
340    return lsquic_frab_list_read(ctx, buf, MIN(bufsz, 1));
341}
342
343
344static size_t
345one_byte_limit_size (void *ctx)
346{
347    size_t size;
348
349    size = lsquic_frab_list_size(ctx);
350    return MIN(size, 1);
351}
352#endif
353
354static void
355hcso_on_write (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx)
356{
357    struct hcso_writer *const writer = (void *) ctx;
358    struct lsquic_reader reader = {
359        .lsqr_read  = lsquic_frab_list_read,
360        .lsqr_size  = lsquic_frab_list_size,
361        .lsqr_ctx   = &writer->how_fral
362    };
363    ssize_t nw;
364    struct lsquic_conn *lconn;
365
366#ifndef NDEBUG
367    if (stream->tosend_off < 8 && (writer->how_flags & HOW_CHOP_STREAM))
368    {
369        reader.lsqr_read = one_byte_limit_read;
370        reader.lsqr_size = one_byte_limit_size;
371    }
372#endif
373
374    nw = lsquic_stream_writef(stream, &reader);
375    if (nw >= 0)
376    {
377        LSQ_DEBUG("wrote %zd bytes to stream", nw);
378        (void) lsquic_stream_flush(stream);
379        if (lsquic_frab_list_empty(&writer->how_fral))
380            lsquic_stream_wantwrite(stream, 0);
381    }
382    else
383    {
384        lconn = lsquic_stream_conn(stream);
385        lconn->cn_if->ci_internal_error(lconn, "cannot write to stream: %s",
386                                                            strerror(errno));
387        lsquic_stream_wantwrite(stream, 0);
388    }
389}
390
391
392static void
393hcso_on_close (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx)
394{
395    struct hcso_writer *writer = (void *) ctx;
396    LSQ_DEBUG("close HTTP Control Stream Writer");
397    lsquic_frab_list_cleanup(&writer->how_fral);
398    writer->how_stream = NULL;
399}
400
401
402static void
403hcso_on_read (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx)
404{
405    assert(0);
406}
407
408
409static const struct lsquic_stream_if hcso_if =
410{
411    .on_new_stream  = hcso_on_new,
412    .on_read        = hcso_on_read,
413    .on_write       = hcso_on_write,
414    .on_close       = hcso_on_close,
415};
416
417const struct lsquic_stream_if *const lsquic_hcso_writer_if = &hcso_if;
418