test_frame_writer.c revision 9a690580
1/* Copyright (c) 2017 - 2020 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 = value_, .name_ptr = name_, .val_len = sizeof(value_) - 1, .name_len = sizeof(name_) - 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
165
166static void
167test_oversize_header (void)
168{
169    struct lshpack_enc henc;
170    struct lsquic_frame_writer *fw;
171    int s;
172    struct lsquic_mm mm;
173    const size_t big_len = LSXPACK_MAX_STRLEN - 20;
174    char *value;
175
176    lshpack_enc_init(&henc);
177    lsquic_mm_init(&mm);
178    fw = lsquic_frame_writer_new(&mm, NULL, 0x200, &henc, output_write,
179#if LSQUIC_CONN_STATS
180                                     &s_conn_stats,
181#endif
182                                0);
183    lsquic_frame_writer_max_header_list_size(fw, 1 << 16);
184    reset_output(0);
185
186    value = malloc(big_len);
187    memset(value, 'A', big_len);
188
189    struct lsxpack_header header_arr[3] =
190    {
191        { XHDR(":status", "302") },
192    };
193    lsxpack_header_set_ptr(&header_arr[1], "some-header", 10, value, big_len);
194    lsxpack_header_set_ptr(&header_arr[2], "another-header", 10, value, big_len);
195
196    struct lsquic_http_headers headers = {
197        .count = sizeof(header_arr) / sizeof(header_arr[0]),
198        .headers = header_arr,
199    };
200
201    s = lsquic_frame_writer_write_headers(fw, 12345, &headers, 0, 100);
202    assert(-1 == s);
203
204    lsquic_frame_writer_destroy(fw);
205    lshpack_enc_cleanup(&henc);
206    lsquic_mm_cleanup(&mm);
207    free(value);
208}
209
210
211static void
212test_continuations (void)
213{
214    struct lsquic_frame_writer *fw;
215    struct lshpack_enc henc;
216    int s;
217    struct lsquic_mm mm;
218
219    lshpack_enc_init(&henc);
220    lsquic_mm_init(&mm);
221    fw = lsquic_frame_writer_new(&mm, NULL, 6, &henc, output_write,
222#if LSQUIC_CONN_STATS
223                                 &s_conn_stats,
224#endif
225                                0);
226    reset_output(0);
227
228/*
229perl tools/henc.pl :status 302 x-some-header some-value | hexdump -C
23000000000  48 82 64 02 40 8a f2 b2  0f 49 56 9c a3 90 b6 7f  |H.d.@....IV.....|
23100000010  87 41 e9 2a dd c7 45 a5                           |.A.*..E.|
232*/
233
234    struct lsxpack_header header_arr[] =
235    {
236        { XHDR(":status", "302") },
237        { XHDR("x-some-header", "some-value") },
238    };
239
240    struct lsquic_http_headers headers = {
241        .count = 2,
242        .headers = header_arr,
243    };
244
245    s = lsquic_frame_writer_write_headers(fw, 12345, &headers, 0, 100);
246    assert(0 == s);
247
248    /* Expected payload is 5 bytes of http_prio_frame and 24 bytes of
249     * compressed headers, split into 4 15-byte chunks (9-byte header
250	 * 6-byte payload) and 1 14-byte chunk (9-byte header and 5-byte
251     * payload).
252     */
253    unsigned char expected_buf[] = {
254        /* Length: */       0x00, 0x00, 0x06,               /* 1 */
255        /* Type: */         HTTP_FRAME_HEADERS,
256        /* Flags: */        HFHF_PRIORITY,
257        /* Stream Id: */    0x00, 0x00, 0x30, 0x39,
258        /* Payload (priority info): */
259                            0x00, 0x00, 0x00, 0x00, 100 - 1,
260        /* Payload (headers): */
261                            0x48,
262        /* Length: */       0x00, 0x00, 0x06,               /* 2 */
263        /* Type: */         HTTP_FRAME_CONTINUATION,
264        /* Flags: */        0x00,
265        /* Stream Id: */    0x00, 0x00, 0x30, 0x39,
266        /* Payload (headers): */
267                            0x82, 0x64, 0x02, 0x40, 0x8A, 0xF2,
268        /* Length: */       0x00, 0x00, 0x06,               /* 3 */
269        /* Type: */         HTTP_FRAME_CONTINUATION,
270        /* Flags: */        0x00,
271        /* Stream Id: */    0x00, 0x00, 0x30, 0x39,
272        /* Payload (headers): */
273                            0xb2, 0x0f, 0x49, 0x56, 0x9c, 0xa3,
274        /* Length: */       0x00, 0x00, 0x06,               /* 4 */
275        /* Type: */         HTTP_FRAME_CONTINUATION,
276        /* Flags: */        0x00,
277        /* Stream Id: */    0x00, 0x00, 0x30, 0x39,
278        /* Payload (headers): */
279                            0x90, 0xb6, 0x7f, 0x87, 0x41, 0xe9,
280        /* Length: */       0x00, 0x00, 0x05,               /* 5 */
281        /* Type: */         HTTP_FRAME_CONTINUATION,
282        /* Flags: */        HFHF_END_HEADERS,
283        /* Stream Id: */    0x00, 0x00, 0x30, 0x39,
284        /* Payload (headers): */
285                            0x2a, 0xdd, 0xc7, 0x45, 0xa5,
286    };
287
288    assert(sizeof(expected_buf) == output.sz);
289
290    assert(0 == memcmp(output.buf +  0, expected_buf +  0, 15));
291    assert(0 == memcmp(output.buf + 15, expected_buf + 15, 15));
292    assert(0 == memcmp(output.buf + 30, expected_buf + 30, 15));
293    assert(0 == memcmp(output.buf + 45, expected_buf + 45, 15));
294    assert(0 == memcmp(output.buf + 60, expected_buf + 60, 14));
295
296    lsquic_frame_writer_destroy(fw);
297    lshpack_enc_cleanup(&henc);
298    lsquic_mm_cleanup(&mm);
299}
300
301
302static void
303test_settings_short (void)
304{
305    struct lsquic_frame_writer *fw;
306    int s;
307    struct lsquic_mm mm;
308
309    lsquic_mm_init(&mm);
310    fw = lsquic_frame_writer_new(&mm, NULL, 7, NULL, output_write,
311#if LSQUIC_CONN_STATS
312                                     &s_conn_stats,
313#endif
314                                0);
315
316    {
317        reset_output(0);
318        struct lsquic_http2_setting settings[] = { { 1, 2, }, { 3, 4, } };
319        s = lsquic_frame_writer_write_settings(fw, settings, 2);
320        assert(0 == s);
321        const unsigned char exp_buf[] = {
322            /* Length: */       0x00, 0x00, 0x06,
323            /* Type: */         HTTP_FRAME_SETTINGS,
324            /* Flags: */        0x00,
325            /* Stream Id: */    0x00, 0x00, 0x00, 0x00,
326            /* Payload: */      0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
327            /* Length: */       0x00, 0x00, 0x06,
328            /* Type: */         HTTP_FRAME_SETTINGS,
329            /* Flags: */        0x00,
330            /* Stream Id: */    0x00, 0x00, 0x00, 0x00,
331            /* Payload: */      0x00, 0x03, 0x00, 0x00, 0x00, 0x04,
332        };
333        assert(output.sz == sizeof(exp_buf));
334        assert(0 == memcmp(output.buf, exp_buf, sizeof(exp_buf)));
335    }
336
337    {
338        reset_output(0);
339        struct lsquic_http2_setting settings[] = { { 1, 2, }, { 3, 4, } };
340        s = lsquic_frame_writer_write_settings(fw, settings, 0);
341        assert(-1 == s);
342        assert(EINVAL == errno);
343    }
344
345    lsquic_frame_writer_destroy(fw);
346    lsquic_mm_cleanup(&mm);
347}
348
349
350static void
351test_settings_normal (void)
352{
353    struct lsquic_frame_writer *fw;
354    int s;
355    struct lsquic_mm mm;
356
357    lsquic_mm_init(&mm);
358    fw = lsquic_frame_writer_new(&mm, NULL, 0, NULL, output_write,
359#if LSQUIC_CONN_STATS
360                                     &s_conn_stats,
361#endif
362                                    0);
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, 2);
368        assert(0 == s);
369        const unsigned char exp_buf[] = {
370            /* Length: */       0x00, 0x00, 0x0C,
371            /* Type: */         HTTP_FRAME_SETTINGS,
372            /* Flags: */        0x00,
373            /* Stream Id: */    0x00, 0x00, 0x00, 0x00,
374            /* Payload: */      0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
375            /* Payload: */      0x00, 0x03, 0x00, 0x00, 0x00, 0x04,
376        };
377        assert(output.sz == sizeof(exp_buf));
378        assert(0 == memcmp(output.buf, exp_buf, sizeof(exp_buf)));
379    }
380
381    {
382        reset_output(0);
383        struct lsquic_http2_setting settings[] = { { 1, 2, }, { 3, 4, } };
384        s = lsquic_frame_writer_write_settings(fw, settings, 0);
385        assert(-1 == s);
386        assert(EINVAL == errno);
387    }
388
389    lsquic_frame_writer_destroy(fw);
390    lsquic_mm_cleanup(&mm);
391}
392
393
394static struct lsquic_conn my_conn = LSCONN_INITIALIZER_CIDLEN(my_conn, 8);
395
396
397#if !defined(NDEBUG) && __GNUC__
398__attribute__((weak))
399#endif
400lsquic_conn_t *
401lsquic_stream_conn (const lsquic_stream_t *stream)
402{
403    return &my_conn;
404}
405
406
407static void
408test_priority (void)
409{
410    struct lsquic_frame_writer *fw;
411    int s;
412    struct lsquic_mm mm;
413
414    lsquic_mm_init(&mm);
415    fw = lsquic_frame_writer_new(&mm, NULL, 6, NULL, output_write,
416#if LSQUIC_CONN_STATS
417                                 &s_conn_stats,
418#endif
419                                0);
420
421    s = lsquic_frame_writer_write_priority(fw, 3, 0, 1UL << 31, 256);
422    assert(s < 0);  /* Invalid dependency stream ID */
423
424    s = lsquic_frame_writer_write_priority(fw, 3, 0, 1, 0);
425    assert(s < 0);  /* Invalid priority stream ID */
426
427    s = lsquic_frame_writer_write_priority(fw, 3, 0, 1, 257);
428    assert(s < 0);  /* Invalid priority stream ID */
429
430    {
431        reset_output(0);
432        s = lsquic_frame_writer_write_priority(fw, 3, 0, 1, 256);
433        assert(0 == s);
434        const unsigned char exp_buf[] = {
435            /* Length: */       0x00, 0x00, 5,
436            /* Type: */         HTTP_FRAME_PRIORITY,
437            /* Flags: */        0x00,
438            /* Stream Id: */    0x00, 0x00, 0x00, 0x03,
439            /* Dep stream Id: */0x00, 0x00, 0x00, 0x01,
440            /* Weight: */       0xFF,
441        };
442        assert(output.sz == sizeof(exp_buf));
443        assert(0 == memcmp(output.buf, exp_buf, sizeof(exp_buf)));
444    }
445
446    {
447        reset_output(0);
448        s = lsquic_frame_writer_write_priority(fw, 20, 1, 100, 256);
449        assert(0 == s);
450        const unsigned char exp_buf[] = {
451            /* Length: */       0x00, 0x00, 5,
452            /* Type: */         HTTP_FRAME_PRIORITY,
453            /* Flags: */        0x00,
454            /* Stream Id: */    0x00, 0x00, 0x00, 0x14,
455            /* Dep stream Id: */0x80, 0x00, 0x00, 0x64,
456            /* Weight: */       0xFF,
457        };
458        assert(output.sz == sizeof(exp_buf));
459        assert(0 == memcmp(output.buf, exp_buf, sizeof(exp_buf)));
460    }
461
462    lsquic_frame_writer_destroy(fw);
463    lsquic_mm_cleanup(&mm);
464}
465
466
467
468static void
469test_errors (void)
470{
471    struct lsquic_frame_writer *fw;
472    struct lsquic_mm mm;
473    struct lshpack_enc henc;
474    int s;
475
476    lshpack_enc_init(&henc);
477    lsquic_mm_init(&mm);
478    fw = lsquic_frame_writer_new(&mm, NULL, 0x200, &henc, output_write,
479#if LSQUIC_CONN_STATS
480                                     &s_conn_stats,
481#endif
482                                1);
483    reset_output(0);
484
485    {
486        struct lsxpack_header header_arr[] =
487        {
488            { XHDR(":status", "200") },
489            { XHDR("Content-type", "text/html") },
490        };
491        struct lsquic_http_headers headers = {
492            .count = 2,
493            .headers = header_arr,
494        };
495        s = lsquic_frame_writer_write_headers(fw, 12345, &headers, 0, 80);
496        assert(-1 == s);
497        assert(EINVAL == errno);
498    }
499
500    {
501        struct lsxpack_header header_arr[] =
502        {
503            { XHDR(":status", "200") },
504            { XHDR("content-type", "text/html") },
505        };
506        struct lsquic_http_headers headers = {
507            .count = 2,
508            .headers = header_arr,
509        };
510        lsquic_frame_writer_max_header_list_size(fw, 40);
511        s = lsquic_frame_writer_write_headers(fw, 12345, &headers, 0, 80);
512        /* Server ignores SETTINGS_MAX_HEADER_LIST_SIZE setting */
513        assert(s == 0);
514    }
515
516    lsquic_frame_writer_destroy(fw);
517    lshpack_enc_cleanup(&henc);
518    lsquic_mm_cleanup(&mm);
519}
520
521
522static void
523test_push_promise (void)
524{
525    struct lshpack_enc henc;
526    struct lsquic_frame_writer *fw;
527    int s;
528    struct lsquic_mm mm;
529
530    lshpack_enc_init(&henc);
531    lsquic_mm_init(&mm);
532    fw = lsquic_frame_writer_new(&mm, NULL, 0x200, &henc, output_write,
533#if LSQUIC_CONN_STATS
534                                     &s_conn_stats,
535#endif
536                                1);
537    reset_output(0);
538
539/*
540perl tools/hpack.pl :method GET :path /index.html :authority www.example.com :scheme https x-some-header some-value| hexdump -C
54100000000  82 85 41 8c f1 e3 c2 e5  f2 3a 6b a0 ab 90 f4 ff  |..A......:k.....|
54200000010  87 40 8a f2 b2 0f 49 56  9c a3 90 b6 7f 87 41 e9  |.@....IV......A.|
54300000020  2a dd c7 45 a5                                    |*..E.|
544*/
545
546	const unsigned char exp_headers[] = {
547        0x82, 0x85, 0x41, 0x8c, 0xf1, 0xe3, 0xc2, 0xe5, 0xf2, 0x3a,
548        0x6b, 0xa0, 0xab, 0x90, 0xf4, 0xff, 0x87, 0x40, 0x8a, 0xf2,
549        0xb2, 0x0f, 0x49, 0x56, 0x9c, 0xa3, 0x90, 0xb6, 0x7f, 0x87,
550        0x41, 0xe9, 0x2a, 0xdd, 0xc7, 0x45, 0xa5,
551	};
552
553    struct lsxpack_header header_arr[] =
554    {
555        { XHDR(":method", "GET") },
556        { XHDR(":path", "/index.html") },
557        { XHDR(":authority", "www.example.com") },
558        { XHDR(":scheme", "https") },
559        { XHDR("x-some-header", "some-value") },
560    };
561
562    struct lsquic_http_headers headers = {
563        .count = 5,
564        .headers = header_arr,
565    };
566
567    s = lsquic_frame_writer_write_promise(fw, 12345, 0xEEEE, &headers);
568    assert(0 == s);
569
570    struct http_frame_header fh;
571    struct http_push_promise_frame push_frame;
572
573    assert(sizeof(exp_headers) + sizeof(struct http_frame_header) +
574                    sizeof(struct http_push_promise_frame) == output.sz);
575
576    memcpy(&fh, output.buf, sizeof(fh));
577    assert(sizeof(exp_headers) + sizeof(struct http_push_promise_frame) == hfh_get_length(&fh));
578    assert(HTTP_FRAME_PUSH_PROMISE == fh.hfh_type);
579    assert(HFHF_END_HEADERS == fh.hfh_flags);
580    assert(fh.hfh_stream_id[0] == 0);
581    assert(fh.hfh_stream_id[1] == 0);
582    assert(fh.hfh_stream_id[2] == 0x30);
583    assert(fh.hfh_stream_id[3] == 0x39);
584
585    memcpy(&push_frame, output.buf + sizeof(struct http_frame_header),
586                                    sizeof(struct http_push_promise_frame));
587
588    assert(push_frame.hppf_promised_id[0] == 0);
589    assert(push_frame.hppf_promised_id[1] == 0);
590    assert(push_frame.hppf_promised_id[2] == 0xEE);
591    assert(push_frame.hppf_promised_id[3] == 0xEE);
592
593    assert(0 == memcmp(output.buf + sizeof(struct http_frame_header) +
594            sizeof(struct http_push_promise_frame), exp_headers,
595                                                sizeof(exp_headers)));
596
597    lsquic_frame_writer_destroy(fw);
598    lshpack_enc_cleanup(&henc);
599    lsquic_mm_cleanup(&mm);
600}
601
602
603
604int
605main (void)
606{
607    test_one_header();
608    test_oversize_header();
609    test_continuations();
610    test_settings_normal();
611    test_settings_short();
612    test_priority();
613    test_push_promise();
614    test_errors();
615    test_max_frame_size();
616    return 0;
617}
618