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