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