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