test_frame_writer.c revision a74702c6
1/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc.  See LICENSE. */
2#include <assert.h>
3#include <errno.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
7#ifndef WIN32
8#include <sys/time.h>
9#endif
10#include <sys/queue.h>
11
12#include "lsquic.h"
13#include "lshpack.h"
14#include "lsquic_mm.h"
15#include "lsquic_int_types.h"
16#include "lsquic_hash.h"
17#include "lsquic_conn.h"
18#include "lsquic_frame_common.h"
19#include "lsquic_frame_writer.h"
20#if LSQUIC_CONN_STATS
21#include "lsquic_int_types.h"
22#include "lsquic_conn.h"
23#endif
24
25#if LSQUIC_CONN_STATS
26static struct conn_stats s_conn_stats;
27#endif
28
29
30static struct {
31    size_t          sz;
32    size_t          max;
33    unsigned char   buf[0x1000];
34} output;
35
36
37#define reset_output(max_) do {         \
38    output.sz = 0;                      \
39    if (max_)                           \
40        output.max = max_;              \
41    else                                \
42        output.max = sizeof(output.buf);\
43} while (0)
44
45
46static ssize_t
47output_write (struct lsquic_stream *stream, struct lsquic_reader *reader)
48{
49    size_t sz;
50
51    sz = reader->lsqr_size(reader->lsqr_ctx);
52    if (output.max - output.sz < sz)
53    {
54        errno = ENOBUFS;
55        return -1;
56    }
57
58    sz = reader->lsqr_read(reader->lsqr_ctx, output.buf + output.sz, sz);
59    output.sz += sz;
60
61    return sz;
62}
63
64
65#define IOV(v) { .iov_base = (v), .iov_len = sizeof(v) - 1, }
66#define XHDR(name_, value_) .buf = name_ value_, .name_offset = 0, .name_len = sizeof(name_) - 1, .val_offset = sizeof(name_) - 1, .val_len = sizeof(value_) - 1,
67
68
69static void
70test_max_frame_size (void)
71{
72    struct lshpack_enc henc;
73    struct lsquic_mm mm;
74    struct lsquic_frame_writer *fw;
75    unsigned max_size;
76
77    lshpack_enc_init(&henc);
78    lsquic_mm_init(&mm);
79
80    for (max_size = 1; max_size < 6 /* one settings frame */; ++max_size)
81    {
82        fw = lsquic_frame_writer_new(&mm, NULL, max_size, &henc,
83                                     output_write,
84#if LSQUIC_CONN_STATS
85                                     &s_conn_stats,
86#endif
87                                     0);
88        assert(!fw);
89    }
90
91    fw = lsquic_frame_writer_new(&mm, NULL, max_size, &henc, output_write,
92#if LSQUIC_CONN_STATS
93                                     &s_conn_stats,
94#endif
95                                0);
96    assert(fw);
97
98    lsquic_frame_writer_destroy(fw);
99    lshpack_enc_cleanup(&henc);
100    lsquic_mm_cleanup(&mm);
101}
102
103
104static void
105test_one_header (void)
106{
107    struct lshpack_enc henc;
108    struct lsquic_frame_writer *fw;
109    int s;
110    struct lsquic_mm mm;
111
112    lshpack_enc_init(&henc);
113    lsquic_mm_init(&mm);
114    fw = lsquic_frame_writer_new(&mm, NULL, 0x200, &henc, output_write,
115#if LSQUIC_CONN_STATS
116                                     &s_conn_stats,
117#endif
118                                0);
119    reset_output(0);
120
121    struct lsxpack_header header_arr[] =
122    {
123        { XHDR(":status", "302") },
124    };
125
126    struct lsquic_http_headers headers = {
127        .count = 1,
128        .headers = header_arr,
129    };
130
131    s = lsquic_frame_writer_write_headers(fw, 12345, &headers, 0, 100);
132    assert(0 == s);
133
134    struct http_frame_header fh;
135    struct http_prio_frame prio_frame;
136
137    assert(4 + sizeof(struct http_frame_header) + sizeof(struct http_prio_frame) == output.sz);
138
139    memcpy(&fh, output.buf, sizeof(fh));
140    assert(4 + sizeof(struct http_prio_frame) == hfh_get_length(&fh));
141    assert(HTTP_FRAME_HEADERS == fh.hfh_type);
142    assert((HFHF_END_HEADERS|HFHF_PRIORITY) == fh.hfh_flags);
143    assert(fh.hfh_stream_id[0] == 0);
144    assert(fh.hfh_stream_id[1] == 0);
145    assert(fh.hfh_stream_id[2] == 0x30);
146    assert(fh.hfh_stream_id[3] == 0x39);
147
148    memcpy(&prio_frame, output.buf + sizeof(struct http_frame_header),
149                                            sizeof(struct http_prio_frame));
150
151    assert(prio_frame.hpf_stream_id[0] == 0);
152    assert(prio_frame.hpf_stream_id[1] == 0);
153    assert(prio_frame.hpf_stream_id[2] == 0);
154    assert(prio_frame.hpf_stream_id[3] == 0);
155    assert(prio_frame.hpf_weight       == 100 - 1);
156
157    assert(0 == memcmp(output.buf + sizeof(struct http_frame_header) +
158                    sizeof(struct http_prio_frame), "\x48\x82\x64\x02", 4));
159
160    lsquic_frame_writer_destroy(fw);
161    lshpack_enc_cleanup(&henc);
162    lsquic_mm_cleanup(&mm);
163}
164
165struct header_buf
166{
167    unsigned    off;
168    char        buf[UINT16_MAX];
169};
170
171
172int
173header_set_ptr (struct lsxpack_header *hdr, struct header_buf *header_buf,
174                const char *name, size_t name_len,
175                const char *val, size_t val_len)
176{
177    if (header_buf->off + name_len + val_len <= sizeof(header_buf->buf))
178    {
179        memcpy(header_buf->buf + header_buf->off, name, name_len);
180        memcpy(header_buf->buf + header_buf->off + name_len, val, val_len);
181        lsxpack_header_set_offset2(hdr, header_buf->buf + header_buf->off,
182                                            0, name_len, name_len, val_len);
183        header_buf->off += name_len + val_len;
184        return 0;
185    }
186    else
187        return -1;
188}
189
190
191static void
192test_oversize_header (void)
193{
194    struct lshpack_enc henc;
195    struct lsquic_frame_writer *fw;
196    int s;
197    struct lsquic_mm mm;
198    const size_t big_len = LSXPACK_MAX_STRLEN - 20;
199    char *value;
200    struct header_buf hbuf;
201
202    lshpack_enc_init(&henc);
203    lsquic_mm_init(&mm);
204    fw = lsquic_frame_writer_new(&mm, NULL, 0x200, &henc, output_write,
205#if LSQUIC_CONN_STATS
206                                     &s_conn_stats,
207#endif
208                                0);
209    lsquic_frame_writer_max_header_list_size(fw, 1 << 16);
210    reset_output(0);
211
212    value = malloc(big_len);
213    memset(value, 'A', big_len);
214
215    struct lsxpack_header header_arr[3] =
216    {
217        { XHDR(":status", "302") },
218    };
219    hbuf.off = 0;
220    header_set_ptr(&header_arr[1], &hbuf, "some-header", 10, value, big_len);
221    header_set_ptr(&header_arr[2], &hbuf, "another-header", 10, value, big_len);
222
223    struct lsquic_http_headers headers = {
224        .count = sizeof(header_arr) / sizeof(header_arr[0]),
225        .headers = header_arr,
226    };
227
228    s = lsquic_frame_writer_write_headers(fw, 12345, &headers, 0, 100);
229    assert(-1 == s);
230
231    lsquic_frame_writer_destroy(fw);
232    lshpack_enc_cleanup(&henc);
233    lsquic_mm_cleanup(&mm);
234    free(value);
235}
236
237
238static void
239test_continuations (void)
240{
241    struct lsquic_frame_writer *fw;
242    struct lshpack_enc henc;
243    int s;
244    struct lsquic_mm mm;
245
246    lshpack_enc_init(&henc);
247    lsquic_mm_init(&mm);
248    fw = lsquic_frame_writer_new(&mm, NULL, 6, &henc, output_write,
249#if LSQUIC_CONN_STATS
250                                 &s_conn_stats,
251#endif
252                                0);
253    reset_output(0);
254
255/*
256perl tools/henc.pl :status 302 x-some-header some-value | hexdump -C
25700000000  48 82 64 02 40 8a f2 b2  0f 49 56 9c a3 90 b6 7f  |H.d.@....IV.....|
25800000010  87 41 e9 2a dd c7 45 a5                           |.A.*..E.|
259*/
260
261    struct lsxpack_header header_arr[] =
262    {
263        { XHDR(":status", "302") },
264        { XHDR("x-some-header", "some-value") },
265    };
266
267    struct lsquic_http_headers headers = {
268        .count = 2,
269        .headers = header_arr,
270    };
271
272    s = lsquic_frame_writer_write_headers(fw, 12345, &headers, 0, 100);
273    assert(0 == s);
274
275    /* Expected payload is 5 bytes of http_prio_frame and 24 bytes of
276     * compressed headers, split into 4 15-byte chunks (9-byte header
277	 * 6-byte payload) and 1 14-byte chunk (9-byte header and 5-byte
278     * payload).
279     */
280    unsigned char expected_buf[] = {
281        /* Length: */       0x00, 0x00, 0x06,               /* 1 */
282        /* Type: */         HTTP_FRAME_HEADERS,
283        /* Flags: */        HFHF_PRIORITY,
284        /* Stream Id: */    0x00, 0x00, 0x30, 0x39,
285        /* Payload (priority info): */
286                            0x00, 0x00, 0x00, 0x00, 100 - 1,
287        /* Payload (headers): */
288                            0x48,
289        /* Length: */       0x00, 0x00, 0x06,               /* 2 */
290        /* Type: */         HTTP_FRAME_CONTINUATION,
291        /* Flags: */        0x00,
292        /* Stream Id: */    0x00, 0x00, 0x30, 0x39,
293        /* Payload (headers): */
294                            0x82, 0x64, 0x02, 0x40, 0x8A, 0xF2,
295        /* Length: */       0x00, 0x00, 0x06,               /* 3 */
296        /* Type: */         HTTP_FRAME_CONTINUATION,
297        /* Flags: */        0x00,
298        /* Stream Id: */    0x00, 0x00, 0x30, 0x39,
299        /* Payload (headers): */
300                            0xb2, 0x0f, 0x49, 0x56, 0x9c, 0xa3,
301        /* Length: */       0x00, 0x00, 0x06,               /* 4 */
302        /* Type: */         HTTP_FRAME_CONTINUATION,
303        /* Flags: */        0x00,
304        /* Stream Id: */    0x00, 0x00, 0x30, 0x39,
305        /* Payload (headers): */
306                            0x90, 0xb6, 0x7f, 0x87, 0x41, 0xe9,
307        /* Length: */       0x00, 0x00, 0x05,               /* 5 */
308        /* Type: */         HTTP_FRAME_CONTINUATION,
309        /* Flags: */        HFHF_END_HEADERS,
310        /* Stream Id: */    0x00, 0x00, 0x30, 0x39,
311        /* Payload (headers): */
312                            0x2a, 0xdd, 0xc7, 0x45, 0xa5,
313    };
314
315    assert(sizeof(expected_buf) == output.sz);
316
317    assert(0 == memcmp(output.buf +  0, expected_buf +  0, 15));
318    assert(0 == memcmp(output.buf + 15, expected_buf + 15, 15));
319    assert(0 == memcmp(output.buf + 30, expected_buf + 30, 15));
320    assert(0 == memcmp(output.buf + 45, expected_buf + 45, 15));
321    assert(0 == memcmp(output.buf + 60, expected_buf + 60, 14));
322
323    lsquic_frame_writer_destroy(fw);
324    lshpack_enc_cleanup(&henc);
325    lsquic_mm_cleanup(&mm);
326}
327
328
329static void
330test_settings_short (void)
331{
332    struct lsquic_frame_writer *fw;
333    int s;
334    struct lsquic_mm mm;
335
336    lsquic_mm_init(&mm);
337    fw = lsquic_frame_writer_new(&mm, NULL, 7, NULL, output_write,
338#if LSQUIC_CONN_STATS
339                                     &s_conn_stats,
340#endif
341                                0);
342
343    {
344        reset_output(0);
345        struct lsquic_http2_setting settings[] = { { 1, 2, }, { 3, 4, } };
346        s = lsquic_frame_writer_write_settings(fw, settings, 2);
347        assert(0 == s);
348        const unsigned char exp_buf[] = {
349            /* Length: */       0x00, 0x00, 0x06,
350            /* Type: */         HTTP_FRAME_SETTINGS,
351            /* Flags: */        0x00,
352            /* Stream Id: */    0x00, 0x00, 0x00, 0x00,
353            /* Payload: */      0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
354            /* Length: */       0x00, 0x00, 0x06,
355            /* Type: */         HTTP_FRAME_SETTINGS,
356            /* Flags: */        0x00,
357            /* Stream Id: */    0x00, 0x00, 0x00, 0x00,
358            /* Payload: */      0x00, 0x03, 0x00, 0x00, 0x00, 0x04,
359        };
360        assert(output.sz == sizeof(exp_buf));
361        assert(0 == memcmp(output.buf, exp_buf, sizeof(exp_buf)));
362    }
363
364    {
365        reset_output(0);
366        struct lsquic_http2_setting settings[] = { { 1, 2, }, { 3, 4, } };
367        s = lsquic_frame_writer_write_settings(fw, settings, 0);
368        assert(-1 == s);
369        assert(EINVAL == errno);
370    }
371
372    lsquic_frame_writer_destroy(fw);
373    lsquic_mm_cleanup(&mm);
374}
375
376
377static void
378test_settings_normal (void)
379{
380    struct lsquic_frame_writer *fw;
381    int s;
382    struct lsquic_mm mm;
383
384    lsquic_mm_init(&mm);
385    fw = lsquic_frame_writer_new(&mm, NULL, 0, NULL, output_write,
386#if LSQUIC_CONN_STATS
387                                     &s_conn_stats,
388#endif
389                                    0);
390
391    {
392        reset_output(0);
393        struct lsquic_http2_setting settings[] = { { 1, 2, }, { 3, 4, } };
394        s = lsquic_frame_writer_write_settings(fw, settings, 2);
395        assert(0 == s);
396        const unsigned char exp_buf[] = {
397            /* Length: */       0x00, 0x00, 0x0C,
398            /* Type: */         HTTP_FRAME_SETTINGS,
399            /* Flags: */        0x00,
400            /* Stream Id: */    0x00, 0x00, 0x00, 0x00,
401            /* Payload: */      0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
402            /* Payload: */      0x00, 0x03, 0x00, 0x00, 0x00, 0x04,
403        };
404        assert(output.sz == sizeof(exp_buf));
405        assert(0 == memcmp(output.buf, exp_buf, sizeof(exp_buf)));
406    }
407
408    {
409        reset_output(0);
410        struct lsquic_http2_setting settings[] = { { 1, 2, }, { 3, 4, } };
411        s = lsquic_frame_writer_write_settings(fw, settings, 0);
412        assert(-1 == s);
413        assert(EINVAL == errno);
414    }
415
416    lsquic_frame_writer_destroy(fw);
417    lsquic_mm_cleanup(&mm);
418}
419
420
421static struct lsquic_conn my_conn = LSCONN_INITIALIZER_CIDLEN(my_conn, 8);
422
423
424#if !defined(NDEBUG) && __GNUC__
425__attribute__((weak))
426#endif
427lsquic_conn_t *
428lsquic_stream_conn (const lsquic_stream_t *stream)
429{
430    return &my_conn;
431}
432
433
434static void
435test_priority (void)
436{
437    struct lsquic_frame_writer *fw;
438    int s;
439    struct lsquic_mm mm;
440
441    lsquic_mm_init(&mm);
442    fw = lsquic_frame_writer_new(&mm, NULL, 6, NULL, output_write,
443#if LSQUIC_CONN_STATS
444                                 &s_conn_stats,
445#endif
446                                0);
447
448    s = lsquic_frame_writer_write_priority(fw, 3, 0, 1UL << 31, 256);
449    assert(s < 0);  /* Invalid dependency stream ID */
450
451    s = lsquic_frame_writer_write_priority(fw, 3, 0, 1, 0);
452    assert(s < 0);  /* Invalid priority stream ID */
453
454    s = lsquic_frame_writer_write_priority(fw, 3, 0, 1, 257);
455    assert(s < 0);  /* Invalid priority stream ID */
456
457    {
458        reset_output(0);
459        s = lsquic_frame_writer_write_priority(fw, 3, 0, 1, 256);
460        assert(0 == s);
461        const unsigned char exp_buf[] = {
462            /* Length: */       0x00, 0x00, 5,
463            /* Type: */         HTTP_FRAME_PRIORITY,
464            /* Flags: */        0x00,
465            /* Stream Id: */    0x00, 0x00, 0x00, 0x03,
466            /* Dep stream Id: */0x00, 0x00, 0x00, 0x01,
467            /* Weight: */       0xFF,
468        };
469        assert(output.sz == sizeof(exp_buf));
470        assert(0 == memcmp(output.buf, exp_buf, sizeof(exp_buf)));
471    }
472
473    {
474        reset_output(0);
475        s = lsquic_frame_writer_write_priority(fw, 20, 1, 100, 256);
476        assert(0 == s);
477        const unsigned char exp_buf[] = {
478            /* Length: */       0x00, 0x00, 5,
479            /* Type: */         HTTP_FRAME_PRIORITY,
480            /* Flags: */        0x00,
481            /* Stream Id: */    0x00, 0x00, 0x00, 0x14,
482            /* Dep stream Id: */0x80, 0x00, 0x00, 0x64,
483            /* Weight: */       0xFF,
484        };
485        assert(output.sz == sizeof(exp_buf));
486        assert(0 == memcmp(output.buf, exp_buf, sizeof(exp_buf)));
487    }
488
489    lsquic_frame_writer_destroy(fw);
490    lsquic_mm_cleanup(&mm);
491}
492
493
494
495static void
496test_errors (void)
497{
498    struct lsquic_frame_writer *fw;
499    struct lsquic_mm mm;
500    struct lshpack_enc henc;
501    int s;
502
503    lshpack_enc_init(&henc);
504    lsquic_mm_init(&mm);
505    fw = lsquic_frame_writer_new(&mm, NULL, 0x200, &henc, output_write,
506#if LSQUIC_CONN_STATS
507                                     &s_conn_stats,
508#endif
509                                1);
510    reset_output(0);
511
512    {
513        struct lsxpack_header header_arr[] =
514        {
515            { XHDR(":status", "200") },
516            { XHDR("Content-type", "text/html") },
517        };
518        struct lsquic_http_headers headers = {
519            .count = 2,
520            .headers = header_arr,
521        };
522        s = lsquic_frame_writer_write_headers(fw, 12345, &headers, 0, 80);
523        assert(-1 == s);
524        assert(EINVAL == errno);
525    }
526
527    {
528        struct lsxpack_header header_arr[] =
529        {
530            { XHDR(":status", "200") },
531            { XHDR("content-type", "text/html") },
532        };
533        struct lsquic_http_headers headers = {
534            .count = 2,
535            .headers = header_arr,
536        };
537        lsquic_frame_writer_max_header_list_size(fw, 40);
538        s = lsquic_frame_writer_write_headers(fw, 12345, &headers, 0, 80);
539        /* Server ignores SETTINGS_MAX_HEADER_LIST_SIZE setting */
540        assert(s == 0);
541    }
542
543    lsquic_frame_writer_destroy(fw);
544    lshpack_enc_cleanup(&henc);
545    lsquic_mm_cleanup(&mm);
546}
547
548
549static void
550test_push_promise (void)
551{
552    struct lshpack_enc henc;
553    struct lsquic_frame_writer *fw;
554    int s;
555    struct lsquic_mm mm;
556
557    lshpack_enc_init(&henc);
558    lsquic_mm_init(&mm);
559    fw = lsquic_frame_writer_new(&mm, NULL, 0x200, &henc, output_write,
560#if LSQUIC_CONN_STATS
561                                     &s_conn_stats,
562#endif
563                                1);
564    reset_output(0);
565
566/*
567perl tools/hpack.pl :method GET :path /index.html :authority www.example.com :scheme https x-some-header some-value| hexdump -C
56800000000  82 85 41 8c f1 e3 c2 e5  f2 3a 6b a0 ab 90 f4 ff  |..A......:k.....|
56900000010  87 40 8a f2 b2 0f 49 56  9c a3 90 b6 7f 87 41 e9  |.@....IV......A.|
57000000020  2a dd c7 45 a5                                    |*..E.|
571*/
572
573	const unsigned char exp_headers[] = {
574        0x82, 0x85, 0x41, 0x8c, 0xf1, 0xe3, 0xc2, 0xe5, 0xf2, 0x3a,
575        0x6b, 0xa0, 0xab, 0x90, 0xf4, 0xff, 0x87, 0x40, 0x8a, 0xf2,
576        0xb2, 0x0f, 0x49, 0x56, 0x9c, 0xa3, 0x90, 0xb6, 0x7f, 0x87,
577        0x41, 0xe9, 0x2a, 0xdd, 0xc7, 0x45, 0xa5,
578	};
579
580    struct lsxpack_header header_arr[] =
581    {
582        { XHDR(":method", "GET") },
583        { XHDR(":path", "/index.html") },
584        { XHDR(":authority", "www.example.com") },
585        { XHDR(":scheme", "https") },
586        { XHDR("x-some-header", "some-value") },
587    };
588
589    struct lsquic_http_headers headers = {
590        .count = 5,
591        .headers = header_arr,
592    };
593
594    s = lsquic_frame_writer_write_promise(fw, 12345, 0xEEEE, &headers);
595    assert(0 == s);
596
597    struct http_frame_header fh;
598    struct http_push_promise_frame push_frame;
599
600    assert(sizeof(exp_headers) + sizeof(struct http_frame_header) +
601                    sizeof(struct http_push_promise_frame) == output.sz);
602
603    memcpy(&fh, output.buf, sizeof(fh));
604    assert(sizeof(exp_headers) + sizeof(struct http_push_promise_frame) == hfh_get_length(&fh));
605    assert(HTTP_FRAME_PUSH_PROMISE == fh.hfh_type);
606    assert(HFHF_END_HEADERS == fh.hfh_flags);
607    assert(fh.hfh_stream_id[0] == 0);
608    assert(fh.hfh_stream_id[1] == 0);
609    assert(fh.hfh_stream_id[2] == 0x30);
610    assert(fh.hfh_stream_id[3] == 0x39);
611
612    memcpy(&push_frame, output.buf + sizeof(struct http_frame_header),
613                                    sizeof(struct http_push_promise_frame));
614
615    assert(push_frame.hppf_promised_id[0] == 0);
616    assert(push_frame.hppf_promised_id[1] == 0);
617    assert(push_frame.hppf_promised_id[2] == 0xEE);
618    assert(push_frame.hppf_promised_id[3] == 0xEE);
619
620    assert(0 == memcmp(output.buf + sizeof(struct http_frame_header) +
621            sizeof(struct http_push_promise_frame), exp_headers,
622                                                sizeof(exp_headers)));
623
624    lsquic_frame_writer_destroy(fw);
625    lshpack_enc_cleanup(&henc);
626    lsquic_mm_cleanup(&mm);
627}
628
629
630
631int
632main (void)
633{
634    test_one_header();
635    test_oversize_header();
636    test_continuations();
637    test_settings_normal();
638    test_settings_short();
639    test_priority();
640    test_push_promise();
641    test_errors();
642    test_max_frame_size();
643    return 0;
644}
645