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