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