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