lsquic_frame_writer.c revision a74702c6
1/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc.  See LICENSE. */
2/*
3 * lsquic_frame_writer.c -- write frames to HEADERS stream.
4 *
5 * The frame is first written to list of frame_buf's (frabs) and then
6 * out to the stream.  This is done because frame's size is written out
7 * to the stream and we may not have enough room in the stream to fit
8 * the whole frame.
9 */
10
11#ifndef WIN32
12#include <arpa/inet.h>
13#endif
14#include <assert.h>
15#include <errno.h>
16#include <inttypes.h>
17#include <stdlib.h>
18#include <string.h>
19#include <sys/queue.h>
20
21#include "lshpack.h"
22#include "lsquic_mm.h"
23#include "lsquic.h"
24#include "lsquic_int_types.h"
25#include "lsquic_hash.h"
26#include "lsquic_conn.h"
27
28#include "lsquic_frame_writer.h"
29#include "lsquic_frame_common.h"
30#include "lsquic_frab_list.h"
31#include "lsquic_ev_log.h"
32
33#include "fiu-local.h"
34
35#define LSQUIC_LOGGER_MODULE LSQLM_FRAME_WRITER
36#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(\
37                                        lsquic_stream_conn(fw->fw_stream))
38#include "lsquic_logger.h"
39
40/* Size of the buffer passed to lshpack_enc_encode() -- this limits the size
41 * of a single compressed header field.
42 */
43#define MAX_COMP_HEADER_FIELD_SIZE (64 * 1024)
44
45
46struct lsquic_frame_writer
47{
48    struct lsquic_stream       *fw_stream;
49    fw_writef_f                 fw_writef;
50    struct lsquic_mm           *fw_mm;
51    struct lshpack_enc         *fw_henc;
52#if LSQUIC_CONN_STATS
53    struct conn_stats          *fw_conn_stats;
54#endif
55    struct frab_list            fw_fral;
56    unsigned                    fw_max_frame_sz;
57    uint32_t                    fw_max_header_list_sz;  /* 0 means unlimited */
58    enum {
59        FW_SERVER   = (1 << 0),
60    }                           fw_flags;
61};
62
63
64/* RFC 7540, Section 4.2 */
65#define MIN_MAX_FRAME_SIZE  (1 << 14)
66#define MAX_MAX_FRAME_SIZE ((1 << 24) - 1)
67
68#define MAX(a, b) ((a) > (b) ? (a) : (b))
69#define SETTINGS_FRAME_SZ 6
70#define ABS_MIN_FRAME_SIZE MAX(SETTINGS_FRAME_SZ, \
71                                            sizeof(struct http_prio_frame))
72
73static void *
74fw_alloc (void *ctx, size_t size)
75{
76    return lsquic_mm_get_4k(ctx);
77}
78
79
80struct lsquic_frame_writer *
81lsquic_frame_writer_new (struct lsquic_mm *mm, struct lsquic_stream *stream,
82     unsigned max_frame_sz, struct lshpack_enc *henc, fw_writef_f writef,
83#if LSQUIC_CONN_STATS
84     struct conn_stats *conn_stats,
85#endif
86     int is_server)
87{
88    struct lsquic_frame_writer *fw;
89
90    /* When frame writer is instantiated, limit the maximum size to
91     * MIN_MAX_FRAME_SIZE.  The reference implementation has this value
92     * hardcoded and QUIC does not provide a mechanism to advertise a
93     * different value.
94     */
95    if (0 == max_frame_sz)
96        max_frame_sz = MIN_MAX_FRAME_SIZE;
97    else
98        LSQ_LOG1(LSQ_LOG_WARN, "max frame size specified to be %u bytes "
99            "-- this better be test code!", max_frame_sz);
100
101    if (!is_server && max_frame_sz < ABS_MIN_FRAME_SIZE)
102    {
103        LSQ_LOG1(LSQ_LOG_ERROR, "max frame size must be at least %zd bytes, "
104            "which is the size of priority information that client always "
105            "writes", ABS_MIN_FRAME_SIZE);
106        return NULL;
107    }
108
109    fw = calloc(1, sizeof(*fw));
110    if (!fw)
111        return NULL;
112
113    fw->fw_mm           = mm;
114    fw->fw_henc         = henc;
115    fw->fw_stream       = stream;
116    fw->fw_writef       = writef;
117    fw->fw_max_frame_sz = max_frame_sz;
118    fw->fw_max_header_list_sz = 0;
119    if (is_server)
120        fw->fw_flags    = FW_SERVER;
121    else
122        fw->fw_flags    = 0;
123#if LSQUIC_CONN_STATS
124    fw->fw_conn_stats   = conn_stats;
125#endif
126    lsquic_frab_list_init(&fw->fw_fral, 0x1000, fw_alloc,
127        (void (*)(void *, void *)) lsquic_mm_put_4k, mm);
128    return fw;
129}
130
131
132void
133lsquic_frame_writer_destroy (struct lsquic_frame_writer *fw)
134{
135    lsquic_frab_list_cleanup(&fw->fw_fral);
136    free(fw);
137}
138
139
140int
141lsquic_frame_writer_have_leftovers (const struct lsquic_frame_writer *fw)
142{
143    return !lsquic_frab_list_empty(&fw->fw_fral);
144}
145
146
147int
148lsquic_frame_writer_flush (struct lsquic_frame_writer *fw)
149{
150    struct lsquic_reader reader = {
151        .lsqr_read  = lsquic_frab_list_read,
152        .lsqr_size  = lsquic_frab_list_size,
153        .lsqr_ctx   = &fw->fw_fral,
154    };
155    ssize_t nw;
156
157    nw = fw->fw_writef(fw->fw_stream, &reader);
158
159    if (nw >= 0)
160        return 0;
161    else
162        return -1;
163}
164
165
166struct header_framer_ctx
167{
168    struct lsquic_frame_writer
169               *hfc_fw;
170    struct {
171        struct frame_buf   *frab;
172        unsigned short      off;
173    }           hfc_header_ptr;     /* Points to byte *after* current frame header */
174    unsigned    hfc_max_frame_sz;   /* Maximum frame size.  We always fill it. */
175    unsigned    hfc_cur_sz;         /* Number of bytes in the current frame. */
176    unsigned    hfc_n_frames;       /* Number of frames written. */
177    lsquic_stream_id_t
178                hfc_stream_id;      /* Stream ID */
179    enum http_frame_header_flags
180                hfc_first_flags;
181    enum http_frame_type
182                hfc_frame_type;
183};
184
185
186static void
187hfc_init (struct header_framer_ctx *hfc, struct lsquic_frame_writer *fw,
188      unsigned max_frame_sz, enum http_frame_type frame_type,
189      lsquic_stream_id_t stream_id, enum http_frame_header_flags first_flags)
190{
191    memset(hfc, 0, sizeof(*hfc));
192    hfc->hfc_fw           = fw;
193    hfc->hfc_frame_type   = frame_type;
194    hfc->hfc_stream_id    = stream_id;
195    hfc->hfc_first_flags  = first_flags;
196    hfc->hfc_max_frame_sz = max_frame_sz;
197    hfc->hfc_cur_sz       = max_frame_sz;
198}
199
200
201static void
202hfc_save_ptr (struct header_framer_ctx *hfc)
203{
204    hfc->hfc_header_ptr.frab = TAILQ_LAST(&hfc->hfc_fw->fw_fral.fl_frabs, frame_buf_head);
205    hfc->hfc_header_ptr.off = hfc->hfc_header_ptr.frab->frab_size;
206}
207
208
209static void
210hfc_terminate_frame (struct header_framer_ctx *hfc,
211                     enum http_frame_header_flags flags)
212{
213    union {
214        struct http_frame_header fh;
215        unsigned char            buf[ sizeof(struct http_frame_header) ];
216    } u;
217    uint32_t stream_id;
218    struct frame_buf *frab;
219
220    /* Construct the frame */
221    u.fh.hfh_length[0] = hfc->hfc_cur_sz >> 16;
222    u.fh.hfh_length[1] = hfc->hfc_cur_sz >> 8;
223    u.fh.hfh_length[2] = hfc->hfc_cur_sz;
224    u.fh.hfh_flags     = flags;
225    if (1 == hfc->hfc_n_frames)
226    {
227        u.fh.hfh_type  = hfc->hfc_frame_type;
228        u.fh.hfh_flags |= hfc->hfc_first_flags;
229    }
230    else
231        u.fh.hfh_type  = HTTP_FRAME_CONTINUATION;
232    stream_id = htonl(hfc->hfc_stream_id);
233    memcpy(u.fh.hfh_stream_id, &stream_id, sizeof(stream_id));
234
235    if (hfc->hfc_header_ptr.off >= sizeof(u.fh))
236    {   /* Write in a single chunk */
237        assert(0 == memcmp("123456789", hfc->hfc_header_ptr.frab->frab_buf +
238                    hfc->hfc_header_ptr.off - sizeof(u.buf), sizeof(u.buf)));
239        memcpy(hfc->hfc_header_ptr.frab->frab_buf + hfc->hfc_header_ptr.off -
240                    sizeof(u.buf), u.buf, sizeof(u.buf));
241    }
242    else
243    {   /* Write across frab boundary */
244        memcpy(hfc->hfc_header_ptr.frab->frab_buf,
245            u.buf + sizeof(u.buf) - hfc->hfc_header_ptr.off,
246            hfc->hfc_header_ptr.off);
247        frab = TAILQ_PREV(hfc->hfc_header_ptr.frab, frame_buf_head, frab_next);
248        memcpy(frab->frab_buf + frab->frab_size - sizeof(u.buf) +
249            hfc->hfc_header_ptr.off, u.buf,
250            sizeof(u.buf) - hfc->hfc_header_ptr.off);
251    }
252}
253
254
255static int
256hfc_write (struct header_framer_ctx *hfc, const void *buf, size_t sz)
257{
258    const unsigned char *p = buf;
259    unsigned avail;
260    int s;
261
262    while (sz > 0)
263    {
264        if (hfc->hfc_max_frame_sz == hfc->hfc_cur_sz)
265        {
266            if (hfc->hfc_n_frames > 0)
267                hfc_terminate_frame(hfc, 0);
268            s = lsquic_frab_list_write(&hfc->hfc_fw->fw_fral, "123456789",
269                                        sizeof(struct http_frame_header));
270            if (s < 0)
271                return s;
272            ++hfc->hfc_n_frames;
273            hfc_save_ptr(hfc);
274            hfc->hfc_cur_sz = 0;
275        }
276
277        avail = hfc->hfc_max_frame_sz - hfc->hfc_cur_sz;
278        if (sz < avail)
279            avail = sz;
280        if (avail)
281        {
282            s = lsquic_frab_list_write(&hfc->hfc_fw->fw_fral, p, avail);
283            if (s < 0)
284                return s;
285            hfc->hfc_cur_sz += avail;
286            sz -= avail;
287            p += avail;
288        }
289    }
290
291    return 0;
292}
293
294
295static unsigned
296count_uppercase (const unsigned char *buf, size_t sz)
297{
298    static const unsigned char uppercase[0x100] = {
299        ['A'] = 1, ['B'] = 1, ['C'] = 1, ['D'] = 1, ['E'] = 1, ['F'] = 1,
300        ['G'] = 1, ['H'] = 1, ['I'] = 1, ['J'] = 1, ['K'] = 1, ['L'] = 1,
301        ['M'] = 1, ['N'] = 1, ['O'] = 1, ['P'] = 1, ['Q'] = 1, ['R'] = 1,
302        ['S'] = 1, ['T'] = 1, ['U'] = 1, ['V'] = 1, ['W'] = 1, ['X'] = 1,
303        ['Y'] = 1, ['Z'] = 1,
304    };
305    unsigned n_uppercase, i;
306    n_uppercase = 0;
307    for (i = 0; i < sz; ++i)
308        n_uppercase += uppercase[ buf[i] ];
309    return n_uppercase;
310}
311
312
313static uint32_t
314calc_headers_size (const struct lsquic_http_headers *headers)
315{
316    int i;
317    uint32_t size = 0;
318    for (i = 0; i < headers->count; ++i)
319        if (headers->headers[i].buf)
320            size += 32 + headers->headers[i].name_len +
321                         headers->headers[i].val_len;
322    return size;
323}
324
325
326static int
327have_oversize_strings (const struct lsquic_http_headers *headers)
328{
329#if LSXPACK_MAX_STRLEN > LSHPACK_MAX_STRLEN
330    int i, have;
331    for (i = 0, have = 0; i < headers->count; ++i)
332    {
333        if (headers->headers[i].buf)
334        {
335            have |= headers->headers[i].name_len > LSHPACK_MAX_STRLEN;
336            have |= headers->headers[i].val_len > LSHPACK_MAX_STRLEN;
337        }
338    }
339    return have;
340#else
341    return 0;
342#endif
343}
344
345
346static int
347check_headers_size (const struct lsquic_frame_writer *fw,
348                    const struct lsquic_http_headers *headers)
349{
350    uint32_t headers_sz;
351    headers_sz = calc_headers_size(headers);
352
353    if (headers_sz <= fw->fw_max_header_list_sz)
354        return 0;
355    else if (fw->fw_flags & FW_SERVER)
356    {
357        LSQ_INFO("Sending headers larger (%u bytes) than max allowed (%u)",
358            headers_sz, fw->fw_max_header_list_sz);
359        return 0;
360    }
361    else
362    {
363        LSQ_INFO("Headers size %u is larger than max allowed (%u)",
364            headers_sz, fw->fw_max_header_list_sz);
365        errno = EMSGSIZE;
366        return -1;
367    }
368}
369
370
371static int
372check_headers_case (const struct lsquic_frame_writer *fw,
373                    const struct lsquic_http_headers *headers)
374{
375    unsigned n_uppercase;
376    int i;
377    n_uppercase = 0;
378    for (i = 0; i < headers->count; ++i)
379        if (headers->headers[i].buf)
380            n_uppercase += count_uppercase((unsigned char *)
381                                lsxpack_header_get_name(&headers->headers[i]),
382                                headers->headers[i].name_len);
383    if (n_uppercase)
384    {
385        LSQ_INFO("Uppercase letters in header names");
386        errno = EINVAL;
387        return -1;
388    }
389    return 0;
390}
391
392
393static int
394write_headers (struct lsquic_frame_writer *fw,
395               const struct lsquic_http_headers *headers,
396               struct header_framer_ctx *hfc, unsigned char *buf,
397               const unsigned buf_sz)
398{
399    unsigned char *end;
400    int i, s;
401    for (i = 0; i < headers->count; ++i)
402    {
403        if (headers->headers[i].buf == NULL)
404            continue;
405        end = lshpack_enc_encode(fw->fw_henc, buf, buf + buf_sz,
406                                                    &headers->headers[i]);
407        if (end > buf)
408        {
409            s = hfc_write(hfc, buf, end - buf);
410            if (s < 0)
411                return s;
412#if LSQUIC_CONN_STATS
413            fw->fw_conn_stats->out.headers_uncomp +=
414                headers->headers[i].name_len
415                    + headers->headers[i].val_len;
416            fw->fw_conn_stats->out.headers_comp += end - buf;
417#endif
418        }
419        else
420        {
421            /* Ignore errors, matching HTTP2 behavior in our server code */
422        }
423    }
424
425    return 0;
426}
427
428
429int
430lsquic_frame_writer_write_headers (struct lsquic_frame_writer *fw,
431                                   lsquic_stream_id_t stream_id,
432                                   const struct lsquic_http_headers *headers,
433                                   int eos, unsigned weight)
434{
435    struct header_framer_ctx hfc;
436    int s;
437    struct http_prio_frame prio_frame;
438    enum http_frame_header_flags flags;
439    unsigned char *buf;
440
441    /* Internal function: weight must be valid here */
442    assert(weight >= 1 && weight <= 256);
443
444    if (fw->fw_max_header_list_sz && 0 != check_headers_size(fw, headers))
445        return -1;
446
447    if (0 != check_headers_case(fw, headers))
448        return -1;
449
450    if (have_oversize_strings(headers))
451        return -1;
452
453    if (eos)
454        flags = HFHF_END_STREAM;
455    else
456        flags = 0;
457
458    if (!(fw->fw_flags & FW_SERVER))
459        flags |= HFHF_PRIORITY;
460
461    hfc_init(&hfc, fw, fw->fw_max_frame_sz, HTTP_FRAME_HEADERS, stream_id,
462                                                                        flags);
463
464    if (!(fw->fw_flags & FW_SERVER))
465    {
466        memset(&prio_frame.hpf_stream_id, 0, sizeof(prio_frame.hpf_stream_id));
467        prio_frame.hpf_weight = weight - 1;
468        s = hfc_write(&hfc, &prio_frame, sizeof(struct http_prio_frame));
469        if (s < 0)
470            return s;
471    }
472
473    buf = malloc(MAX_COMP_HEADER_FIELD_SIZE);
474    if (!buf)
475        return -1;
476    s = write_headers(fw, headers, &hfc, buf, MAX_COMP_HEADER_FIELD_SIZE);
477    free(buf);
478    if (0 == s)
479    {
480        EV_LOG_GENERATED_HTTP_HEADERS(LSQUIC_LOG_CONN_ID, stream_id,
481                            fw->fw_flags & FW_SERVER, &prio_frame, headers);
482        hfc_terminate_frame(&hfc, HFHF_END_HEADERS);
483        return lsquic_frame_writer_flush(fw);
484    }
485    else
486        return s;
487}
488
489
490int
491lsquic_frame_writer_write_promise (struct lsquic_frame_writer *fw,
492    lsquic_stream_id_t stream_id64, lsquic_stream_id_t promised_stream_id64,
493    const struct lsquic_http_headers *headers)
494{
495    uint32_t stream_id = stream_id64;
496    uint32_t promised_stream_id = promised_stream_id64;
497    struct header_framer_ctx hfc;
498    struct http_push_promise_frame push_frame;
499    unsigned char *buf;
500    int s;
501
502    fiu_return_on("frame_writer/writer_promise", -1);
503
504    if (fw->fw_max_header_list_sz && 0 != check_headers_size(fw, headers))
505        return -1;
506
507    if (0 != check_headers_case(fw, headers))
508        return -1;
509
510    if (have_oversize_strings(headers))
511        return -1;
512
513    hfc_init(&hfc, fw, fw->fw_max_frame_sz, HTTP_FRAME_PUSH_PROMISE,
514                                                            stream_id, 0);
515
516    promised_stream_id = htonl(promised_stream_id);
517    memcpy(push_frame.hppf_promised_id, &promised_stream_id, 4);
518    s = hfc_write(&hfc, &push_frame, sizeof(struct http_push_promise_frame));
519    if (s < 0)
520        return s;
521
522    buf = malloc(MAX_COMP_HEADER_FIELD_SIZE);
523    if (!buf)
524        return -1;
525
526    s = write_headers(fw, headers, &hfc, buf, MAX_COMP_HEADER_FIELD_SIZE);
527    if (s != 0)
528    {
529        free(buf);
530        return -1;
531    }
532
533    free(buf);
534
535    EV_LOG_GENERATED_HTTP_PUSH_PROMISE(LSQUIC_LOG_CONN_ID, stream_id,
536                                        htonl(promised_stream_id), headers);
537    hfc_terminate_frame(&hfc, HFHF_END_HEADERS);
538    return lsquic_frame_writer_flush(fw);
539}
540
541
542void
543lsquic_frame_writer_max_header_list_size (struct lsquic_frame_writer *fw,
544                                          uint32_t max_size)
545{
546    LSQ_DEBUG("set max_header_list_sz to %u", max_size);
547    fw->fw_max_header_list_sz = max_size;
548}
549
550
551static int
552write_settings (struct lsquic_frame_writer *fw,
553    const struct lsquic_http2_setting *settings, unsigned n_settings)
554{
555    struct http_frame_header fh;
556    unsigned payload_length;
557    uint32_t val;
558    uint16_t id;
559    int s;
560
561    payload_length = n_settings * 6;
562
563    memset(&fh, 0, sizeof(fh));
564    fh.hfh_type  = HTTP_FRAME_SETTINGS;
565    fh.hfh_length[0] = payload_length >> 16;
566    fh.hfh_length[1] = payload_length >> 8;
567    fh.hfh_length[2] = payload_length;
568
569    s = lsquic_frab_list_write(&fw->fw_fral, &fh, sizeof(fh));
570    if (s != 0)
571        return s;
572
573    do
574    {
575        id  = htons(settings->id);
576        val = htonl(settings->value);
577        if (0 != (s = lsquic_frab_list_write(&fw->fw_fral, &id, sizeof(id))) ||
578            0 != (s = lsquic_frab_list_write(&fw->fw_fral, &val, sizeof(val))))
579            return s;
580        EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "wrote HTTP SETTINGS frame: "
581            "%s=%"PRIu32, lsquic_http_setting_id2str(settings->id),
582            settings->value);
583        ++settings;
584    }
585    while (--n_settings);
586
587    return 0;
588}
589
590int
591lsquic_frame_writer_write_settings (struct lsquic_frame_writer *fw,
592    const struct lsquic_http2_setting *settings, unsigned n_settings)
593{
594    unsigned settings_per_frame;
595    unsigned n;
596
597    if (0 == n_settings)
598    {
599        errno = EINVAL;
600        return -1;
601    }
602
603    settings_per_frame = fw->fw_max_frame_sz / SETTINGS_FRAME_SZ;
604    n = 0;
605
606    do {
607        if (settings_per_frame > n_settings - n)
608            settings_per_frame = n_settings - n;
609        if (0 != write_settings(fw, &settings[n], settings_per_frame))
610            return -1;
611        n += settings_per_frame;
612    } while (n < n_settings);
613
614    return lsquic_frame_writer_flush(fw);
615}
616
617
618int
619lsquic_frame_writer_write_priority (struct lsquic_frame_writer *fw,
620        lsquic_stream_id_t stream_id64, int exclusive,
621        lsquic_stream_id_t stream_dep_id64, unsigned weight)
622{
623    uint32_t stream_id = stream_id64;
624    uint32_t stream_dep_id = stream_dep_id64;
625    unsigned char buf[ sizeof(struct http_frame_header) +
626                                        sizeof(struct http_prio_frame) ];
627    struct http_frame_header *fh         = (void *) &buf[0];
628    struct http_prio_frame   *prio_frame = (void *) &buf[sizeof(*fh)];
629    int s;
630
631    if (stream_dep_id & (1UL << 31))
632    {
633        LSQ_WARN("stream ID too high (%u): cannot write PRIORITY frame",
634            stream_dep_id);
635        return -1;
636    }
637
638    if (weight < 1 || weight > 256)
639        return -1;
640
641    memset(fh, 0, sizeof(*fh));
642    fh->hfh_type      = HTTP_FRAME_PRIORITY;
643    fh->hfh_length[2] = sizeof(struct http_prio_frame);
644    stream_id = htonl(stream_id);
645    memcpy(fh->hfh_stream_id, &stream_id, 4);
646
647    stream_dep_id |= !!exclusive << 31;
648    stream_id = htonl(stream_dep_id);
649    memcpy(prio_frame->hpf_stream_id, &stream_id, 4);
650    prio_frame->hpf_weight = weight - 1;
651
652    s = lsquic_frab_list_write(&fw->fw_fral, buf, sizeof(buf));
653    if (s != 0)
654        return s;
655
656    EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "wrote HTTP PRIORITY frame: "
657        "stream %"PRIu32"; weight: %u; exclusive: %d",
658        htonl(stream_id), weight, !!exclusive);
659
660    return lsquic_frame_writer_flush(fw);
661}
662
663
664size_t
665lsquic_frame_writer_mem_used (const struct lsquic_frame_writer *fw)
666{
667    size_t size;
668
669    size = sizeof(*fw)
670         + lsquic_frab_list_mem_used(&fw->fw_fral)
671         - sizeof(fw->fw_fral);
672
673    return size;
674}
675