1/*
2 * Test QPACK.
3 */
4
5#include <assert.h>
6#include <ctype.h>
7#include <stdint.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11
12#include "lsqpack.h"
13#include "lsqpack-test.h"
14#include "lsxpack_header.h"
15
16#define ENC_BUF_SZ 200
17#define HEADER_BUF_SZ 200
18#define PREFIX_BUF_SZ 10
19
20static const struct qpack_header_block_test
21{
22    /* Identification: */
23    int             qhbt_lineno;
24
25    /* Input: */
26    unsigned        qhbt_table_size;
27    unsigned        qhbt_max_risked_streams;
28    unsigned        qhbt_n_headers;
29    struct {
30        const char *name, *value;
31        enum lsqpack_enc_flags flags;
32    }               qhbt_headers[10];
33
34    /* Output: */
35    size_t          qhbt_enc_sz;
36    unsigned char   qhbt_enc_buf[ENC_BUF_SZ];
37
38    size_t          qhbt_prefix_sz;
39    unsigned char   qhbt_prefix_buf[PREFIX_BUF_SZ];
40
41    size_t          qhbt_header_sz;
42    unsigned char   qhbt_header_buf[HEADER_BUF_SZ];
43} header_block_tests[] =
44{
45
46    {
47        .qhbt_lineno        = __LINE__,
48        .qhbt_table_size    = 0x1000,
49        .qhbt_max_risked_streams = 100,
50        .qhbt_n_headers     = 1,
51        .qhbt_headers       = {
52            { ":method", "GET", 0, },
53        },
54        .qhbt_enc_sz        = 0,
55        .qhbt_prefix_sz     = 2,
56        .qhbt_prefix_buf    = "\x00\x00",
57        .qhbt_header_sz     = 1,
58        .qhbt_header_buf    = {
59            0x80 | 0x40 | 17,
60        },
61    },
62
63    {
64        .qhbt_lineno        = __LINE__,
65        .qhbt_table_size    = 0x1000,
66        .qhbt_max_risked_streams = 100,
67        .qhbt_n_headers     = 1,
68        .qhbt_headers       = {
69            { ":method", "method", LQEF_NEVER_INDEX, },
70        },
71        .qhbt_enc_sz        = 0,
72        .qhbt_prefix_sz     = 2,
73        .qhbt_prefix_buf    = "\x00\x00",
74        .qhbt_header_sz     = 8,
75        .qhbt_header_buf    = {
76            0x40 | 0x20 | 0x10 | 0x0F, 0x00,
77            0x80 /* Huffman */ | 5 /* length */,
78            0xa4, 0xa9, 0x9c, 0xf2, 0x7f,
79        },
80    },
81
82    {
83        .qhbt_lineno        = __LINE__,
84        .qhbt_table_size    = 0x1000,
85        .qhbt_max_risked_streams = 0,
86        .qhbt_n_headers     = 1,
87        .qhbt_headers       = {
88            { ":method", "method", 0, },
89        },
90        .qhbt_enc_sz        = 7,
91        .qhbt_enc_buf       = {
92            0x80 | 0x40 /* static */ | 15 /* name index */,
93            0x80 /* Huffman */ | 5 /* length */,
94            0xa4, 0xa9, 0x9c, 0xf2, 0x7f,
95        },
96        .qhbt_prefix_sz     = 2,
97        .qhbt_prefix_buf    = "\x00\x00",
98        .qhbt_header_sz     = 8,
99        .qhbt_header_buf    = {
100            0x40 | 0x10 /* Static */ | 15, 0x00,
101            0x80 /* Huffman */ | 5 /* length */,
102            0xa4, 0xa9, 0x9c, 0xf2, 0x7f,
103        },
104    },
105
106    {
107        .qhbt_lineno        = __LINE__,
108        .qhbt_table_size    = 0x1000,
109        .qhbt_max_risked_streams = 0x1000,
110        .qhbt_n_headers     = 1,
111        .qhbt_headers       = {
112            { ":method", "method", 0, },
113        },
114        .qhbt_enc_sz        = 7,
115        .qhbt_enc_buf       = {
116            0x80 | 0x40 /* static */ | 15 /* name index */,
117            0x80 /* Huffman */ | 5 /* length */,
118            0xa4, 0xa9, 0x9c, 0xf2, 0x7f,
119        },
120        .qhbt_prefix_sz     = 2,
121        .qhbt_prefix_buf    = { 0x02, 0x80, },
122        .qhbt_header_sz     = 1,
123        .qhbt_header_buf    = {
124            0x10 | 0 /* Relative dynamic ID */,
125        },
126    },
127
128    {
129        .qhbt_lineno        = __LINE__,
130        .qhbt_table_size    = 0x1000,
131        .qhbt_max_risked_streams = 0x1000,
132        .qhbt_n_headers     = 1,
133        .qhbt_headers       = {
134            { "dude", "where is my car?", LQEF_NEVER_INDEX, },
135        },
136        .qhbt_enc_sz        = 0,
137        .qhbt_prefix_sz     = 2,
138        .qhbt_prefix_buf    = "\x00\x00",
139        .qhbt_header_sz     = 17,
140        .qhbt_header_buf    = {
141            0x20 | 0x10 /* No index */ | 0x08 | 3,
142            0x92, 0xd9, 0x0b,
143            0x80 | 0xC,
144            0xf1, 0x39, 0x6c, 0x2a, 0x86, 0x42, 0x94, 0xfa,
145            0x50, 0x83, 0xb3, 0xfc,
146        },
147    },
148
149    {
150        .qhbt_lineno        = __LINE__,
151        .qhbt_table_size    = 0x1000,
152        .qhbt_max_risked_streams = 0,
153        .qhbt_n_headers     = 3,
154        .qhbt_headers       = {
155            { "dude", "where is my car?", 0, },
156            { ":method", "GET", 0, },
157            /* The "aaa..." string triggers header_out_grow_buf() */
158            { "aaaaaaaaaaaaaaaaaaaaaaaaaa",
159                "aaaaaaaaaaaaaaaaaaaaaaaaaa", 0, },
160        },
161        .qhbt_enc_sz        = 53,
162        .qhbt_enc_buf       = {
163            0x40 | 0x20 | 3,
164            0x92, 0xd9, 0x0b,
165            0x80 | 0xC,
166            0xf1, 0x39, 0x6c, 0x2a, 0x86, 0x42, 0x94, 0xfa,
167            0x50, 0x83, 0xb3, 0xfc,
168            0x40 /* Without name reference */ | 0x20 /* Huffman */ | 17 /* Length */,
169            0x18, 0xc6, 0x31, 0x8c, 0x63, 0x18, 0xc6, 0x31, 0x8c,
170            0x63, 0x18, 0xc6, 0x31, 0x8c, 0x63, 0x18, 0xff,
171            0x80 /* Huffman */ | 17 /* length */,
172            0x18, 0xc6, 0x31, 0x8c, 0x63, 0x18, 0xc6, 0x31, 0x8c,
173            0x63, 0x18, 0xc6, 0x31, 0x8c, 0x63, 0x18, 0xff,
174        },
175        .qhbt_prefix_sz     = 2,
176        .qhbt_prefix_buf    = "\x00\x00",
177        .qhbt_header_sz     = 55,
178        .qhbt_header_buf    = {
179            0x20 | 0x08 | 3,
180            0x92, 0xd9, 0x0b,
181            0x80 | 0xC,
182            0xf1, 0x39, 0x6c, 0x2a, 0x86, 0x42, 0x94, 0xfa,
183            0x50, 0x83, 0xb3, 0xfc,
184            0x80 | 0x40 | 17,
185            0x20 /* Without name reference */ | 0x08 /* Huffman */ | 7 , 0x0A /* Length */,
186            0x18, 0xc6, 0x31, 0x8c, 0x63, 0x18, 0xc6, 0x31, 0x8c,
187            0x63, 0x18, 0xc6, 0x31, 0x8c, 0x63, 0x18, 0xff,
188            0x80 /* Huffman */ | 17 /* length */,
189            0x18, 0xc6, 0x31, 0x8c, 0x63, 0x18, 0xc6, 0x31, 0x8c,
190            0x63, 0x18, 0xc6, 0x31, 0x8c, 0x63, 0x18, 0xff,
191        },
192    },
193
194    {
195        .qhbt_lineno        = __LINE__,
196        .qhbt_table_size    = 0x1000,
197        .qhbt_max_risked_streams = 1,
198        .qhbt_n_headers     = 1,
199        .qhbt_headers       = {
200            { "dude", "where is my car?", 0, },
201        },
202        .qhbt_enc_sz        = 17,
203        .qhbt_enc_buf       = {
204            0x40 | 0x20 | 3,
205            0x92, 0xd9, 0x0b,
206            0x80 | 0xC,
207            0xf1, 0x39, 0x6c, 0x2a, 0x86, 0x42, 0x94, 0xfa,
208            0x50, 0x83, 0xb3, 0xfc,
209        },
210        .qhbt_prefix_sz     = 2,
211        .qhbt_prefix_buf    = { 0x02, 0x80, },
212        .qhbt_header_sz     = 1,
213        .qhbt_header_buf    = {
214            0x10 | 0 /* Relative dynamic ID */,
215        },
216    },
217
218};
219
220
221struct header_buf
222{
223    unsigned    off;
224    char        buf[UINT16_MAX];
225};
226
227
228int
229header_set_ptr (struct lsxpack_header *hdr, struct header_buf *header_buf,
230                const char *name, size_t name_len,
231                const char *val, size_t val_len)
232{
233    if (header_buf->off + name_len + val_len <= sizeof(header_buf->buf))
234    {
235        memcpy(header_buf->buf + header_buf->off, name, name_len);
236        memcpy(header_buf->buf + header_buf->off + name_len, val, val_len);
237        lsxpack_header_set_offset2(hdr, header_buf->buf + header_buf->off,
238                                            0, name_len, name_len, val_len);
239        header_buf->off += name_len + val_len;
240        return 0;
241    }
242    else
243        return -1;
244}
245
246static void
247run_header_test (const struct qpack_header_block_test *test)
248{
249    unsigned char header_buf[HEADER_BUF_SZ], enc_buf[ENC_BUF_SZ],
250        prefix_buf[PREFIX_BUF_SZ];
251    unsigned header_off, enc_off;
252    size_t header_sz, enc_sz, dec_sz;
253    struct lsqpack_enc enc;
254    unsigned i;
255    size_t nw;
256    int s;
257    enum lsqpack_enc_status enc_st;
258    float ratio;
259    unsigned char dec_buf[LSQPACK_LONGEST_SDTC];
260    struct lsxpack_header xhdr;
261    struct header_buf hbuf;
262
263    dec_sz = sizeof(dec_buf);
264    s = lsqpack_enc_init(&enc, stderr, test->qhbt_table_size,
265                test->qhbt_table_size, test->qhbt_max_risked_streams,
266                LSQPACK_ENC_OPT_IX_AGGR, dec_buf, &dec_sz);
267    assert(s == 0);
268
269    s = lsqpack_enc_start_header(&enc, 0, 0);
270    assert(s == 0);
271
272    header_off = 0, enc_off = 0;
273    for (i = 0; i < test->qhbt_n_headers; ++i)
274    {
275        /* Increase buffers one by one to exercise error conditions */
276        enc_sz = 0;
277        header_sz = 0;
278        while (1)
279        {
280            hbuf.off = 0;
281            s = header_set_ptr(&xhdr, &hbuf,
282                    test->qhbt_headers[i].name,
283                    strlen(test->qhbt_headers[i].name),
284                    test->qhbt_headers[i].value,
285                    strlen(test->qhbt_headers[i].value));
286            assert(s == 0);
287            enc_st = lsqpack_enc_encode(&enc,
288                    enc_buf + enc_off, &enc_sz,
289                    header_buf + header_off, &header_sz,
290                    &xhdr,
291                    test->qhbt_headers[i].flags);
292            switch (enc_st)
293            {
294            case LQES_NOBUF_ENC:
295                if (enc_sz < sizeof(enc_buf) - enc_off)
296                    ++enc_sz;
297                else
298                    assert(0);
299                break;
300            case LQES_NOBUF_HEAD:
301                if (header_sz < sizeof(header_buf) - header_off)
302                    ++header_sz;
303                else
304                    assert(0);
305                break;
306            default:
307                assert(enc_st == LQES_OK);
308                goto end_encode_one_header;
309            }
310        }
311  end_encode_one_header:
312        enc_off += enc_sz;
313        header_off += header_sz;
314    }
315
316    nw = lsqpack_enc_end_header(&enc, prefix_buf, sizeof(prefix_buf), NULL);
317    assert(nw == test->qhbt_prefix_sz);
318    assert(0 == memcmp(test->qhbt_prefix_buf, prefix_buf, nw));
319    assert(enc_off == test->qhbt_enc_sz);
320    assert(0 == memcmp(test->qhbt_enc_buf, enc_buf, enc_off));
321    assert(header_off == test->qhbt_header_sz);
322    assert(0 == memcmp(test->qhbt_header_buf, header_buf, header_off));
323    assert(lsqpack_enc_ratio(&enc) > 0.0);
324
325    lsqpack_enc_cleanup(&enc);
326}
327
328
329struct dhte /* decoded header test ext */
330{
331    struct lsxpack_header       xhdr;
332    size_t                      buf_off;
333    char                        buf[0x1000];
334};
335
336
337static void
338dht_unblocked (void *hblock_ctx_p)
339{
340    assert(0);  /* Not expecting this to be called */
341}
342
343
344static struct lsxpack_header *
345dht_prepare_decode (void *hblock_ctx_p, struct lsxpack_header *xhdr, size_t space)
346{
347    struct dhte *const dhte = hblock_ctx_p;
348    size_t avail;
349
350    if (space > LSXPACK_MAX_STRLEN)
351        return NULL;
352
353    if (xhdr)
354    {
355        assert(xhdr == &dhte->xhdr);
356        assert(xhdr->val_len < space);
357        avail = sizeof(dhte->buf) - xhdr->name_offset - 1 /* NUL */;
358        if (avail < space)
359            return NULL;
360        xhdr->val_len = space;
361    }
362    else
363    {
364        avail = sizeof(dhte->buf) - dhte->buf_off - 1 /* NUL */;
365        if (avail < space)
366            return NULL;
367        lsxpack_header_prepare_decode(&dhte->xhdr, dhte->buf, dhte->buf_off,
368                                                                        space);
369    }
370
371    return &dhte->xhdr;
372}
373
374
375static int
376dht_process_header (void *hblock_ctx_p, struct lsxpack_header *xhdr)
377{
378    struct dhte *const dhte = hblock_ctx_p;
379
380    assert(xhdr == &dhte->xhdr);
381    dhte->buf_off += lsxpack_header_get_dec_size(xhdr);
382
383    return 0;
384}
385
386
387static void
388run_decoded_headers_test_ext (const struct qpack_header_block_test *test,
389                const enum lsqpack_dec_opts opts, const size_t name_offset)
390{
391    struct lsqpack_dec dec;
392    char exp_hbuf[0x1000];  /* Large enough to hold all headers */
393    struct dhte dhte;
394    size_t sz;
395    unsigned i;
396    int s;
397    enum lsqpack_read_header_status rhs;
398    const unsigned char *buf;
399    unsigned char dec_buf[LSQPACK_LONGEST_HEADER_ACK];
400    size_t dec_sz;
401    const struct lsqpack_dec_hset_if dht_if = {
402        .dhi_unblocked      = dht_unblocked,
403        .dhi_prepare_decode = dht_prepare_decode,
404        .dhi_process_header = dht_process_header,
405    };
406
407    /* Construct expected headers buffer: */
408    sz = 0;
409    exp_hbuf[0] = '\0';
410    for (i = 0; i < test->qhbt_n_headers; ++i)
411    {
412        strcpy(exp_hbuf + sz, test->qhbt_headers[i].name);
413        sz += strlen(test->qhbt_headers[i].name);
414        if (opts & LSQPACK_DEC_OPT_HTTP1X)
415        {
416            strcpy(exp_hbuf + sz, ": ");
417            sz += 2;
418        }
419        strcpy(exp_hbuf + sz, test->qhbt_headers[i].value);
420        sz += strlen(test->qhbt_headers[i].value);
421        if (opts & LSQPACK_DEC_OPT_HTTP1X)
422        {
423            strcpy(exp_hbuf + sz, "\r\n");
424            sz += 2;
425        }
426    }
427    assert(sz < sizeof(exp_hbuf));  /* Self-check */
428
429    /* Decode headers into dhte buffer */
430    dhte.buf_off = name_offset;
431    lsqpack_dec_init(&dec, NULL, test->qhbt_table_size,
432                                test->qhbt_max_risked_streams, &dht_if, opts);
433    if (test->qhbt_enc_sz)
434    {
435        s = lsqpack_dec_enc_in(&dec, test->qhbt_enc_buf, test->qhbt_enc_sz);
436        assert(s == 0);
437    }
438    assert(test->qhbt_header_sz);   /* Self-check */
439    buf = test->qhbt_prefix_buf;
440    dec_sz = sizeof(dec_buf);
441    rhs = lsqpack_dec_header_in(&dec, &dhte, 0,
442                test->qhbt_prefix_sz + test->qhbt_header_sz,
443                &buf, test->qhbt_prefix_sz,
444                dec_buf, &dec_sz);
445    assert(rhs == LQRHS_NEED);
446    assert(buf == test->qhbt_prefix_buf + test->qhbt_prefix_sz);
447    buf = test->qhbt_header_buf;
448    dec_sz = sizeof(dec_buf);
449    rhs = lsqpack_dec_header_read(&dec, &dhte,
450                &buf, test->qhbt_header_sz,
451                dec_buf, &dec_sz);
452    assert(rhs == LQRHS_DONE);
453    assert(buf == test->qhbt_header_buf + test->qhbt_header_sz);
454    lsqpack_dec_cleanup(&dec);
455
456    /* Now compare the buffers */
457    dhte.buf[dhte.buf_off] = '\0';
458    s = strcmp(dhte.buf + name_offset, exp_hbuf);
459    assert(s == 0);
460}
461
462
463static void
464run_decoded_headers_test (const struct qpack_header_block_test *test)
465{
466    const enum lsqpack_dec_opts opts[] = { 0, LSQPACK_DEC_OPT_HTTP1X, };
467    const size_t offs[] = { 0, 1, 20, };
468    unsigned i, j;
469
470    for (i = 0; i < sizeof(opts) / sizeof(opts[0]); ++i)
471        for (j = 0; j < sizeof(offs) / sizeof(offs[0]); ++j)
472            run_decoded_headers_test_ext(test, opts[i], offs[j]);
473}
474
475
476static void
477run_header_cancellation_test(const struct qpack_header_block_test *test) {
478    unsigned char header_buf[HEADER_BUF_SZ];
479    size_t header_sz, enc_sz;
480    struct lsqpack_enc enc;
481    int s;
482    enum lsqpack_enc_status enc_st;
483    struct lsxpack_header xhdr;
484    struct header_buf hbuf;
485
486    s = lsqpack_enc_init(&enc, stderr, 0, 0, test->qhbt_max_risked_streams,
487                         LSQPACK_ENC_OPT_IX_AGGR, NULL, NULL);
488    assert(s == 0);
489
490    s = lsqpack_enc_start_header(&enc, 0, 0);
491    assert(s == 0);
492
493    header_sz = HEADER_BUF_SZ;
494    enc_sz = 0;
495
496    hbuf.off = 0;
497    header_set_ptr(&xhdr, &hbuf,
498            test->qhbt_headers[0].name,
499            strlen(test->qhbt_headers[0].name),
500            test->qhbt_headers[0].value,
501            strlen(test->qhbt_headers[0].value));
502    enc_st = lsqpack_enc_encode(&enc,
503                    NULL, &enc_sz,
504                    header_buf, &header_sz,
505                    &xhdr,
506                    0);
507    assert(enc_st == LQES_OK);
508
509    s = lsqpack_enc_cancel_header(&enc);
510    assert(s == 0);
511
512    /* Check that we can start again after cancelling. */
513    s = lsqpack_enc_start_header(&enc, 0, 0);
514    assert(s == 0);
515
516    lsqpack_enc_cleanup(&enc);
517}
518
519
520static void
521test_enc_init (void)
522{
523    struct lsqpack_enc enc;
524    size_t dec_sz;
525    int s;
526    unsigned i;
527    const unsigned char *p;
528    uint64_t val;
529    struct lsqpack_dec_int_state state;
530    unsigned char dec_buf[LSQPACK_LONGEST_SDTC];
531
532    const struct {
533        unsigned    peer_max_size;  /* Value provided by peer */
534        unsigned    our_max_size;   /* Value to use */
535        int         expected_tsu;   /* Expecting TSU instruction? */
536    } tests[] = {
537        {   0x1000,     0x1000,     1,  },
538        {   0x1000,     1,          1,  },
539        {    0x100,     0x100,      1,  },
540        {   0x1000,     0,          0,  },
541    };
542
543    for (i = 0; i < sizeof(tests) / sizeof(tests[0]); ++i)
544    {
545        dec_sz = sizeof(dec_buf);
546        s = lsqpack_enc_init(&enc, stderr, tests[i].peer_max_size,
547                        tests[i].our_max_size, 0, 0, dec_buf, &dec_sz);
548        assert(s == 0);
549        if (tests[i].expected_tsu)
550        {
551            assert(dec_sz > 0);
552            assert((dec_buf[0] & 0xE0) == 0x20);
553            p = dec_buf;
554            state.resume = 0;
555            s = lsqpack_dec_int(&p, p + dec_sz, 5, &val, &state);
556            assert(s == 0);
557            assert(val == tests[i].our_max_size);
558        }
559        else
560            assert(dec_sz == 0);
561        lsqpack_enc_cleanup(&enc);
562    }
563}
564
565
566/* Test that push promise header does not use the dynamic table, nor does
567 * it update history.
568 */
569static void
570test_push_promise (void)
571{
572    struct lsqpack_enc enc;
573    ssize_t nw;
574    enum lsqpack_enc_status enc_st;
575    int s;
576    unsigned i;
577    const unsigned char *p;
578    uint64_t val;
579    struct lsqpack_dec_int_state state;
580    unsigned char dec_buf[LSQPACK_LONGEST_SDTC];
581    unsigned char header_buf[HEADER_BUF_SZ], enc_buf[ENC_BUF_SZ],
582        prefix_buf[PREFIX_BUF_SZ];
583    size_t header_sz, enc_sz, dec_sz;
584    enum lsqpack_enc_header_flags hflags;
585    struct lsxpack_header xhdr;
586    struct header_buf hbuf;
587
588    dec_sz = sizeof(dec_buf);
589    s = lsqpack_enc_init(&enc, stderr, 0x1000, 0x1000, 100, 0, dec_buf, &dec_sz);
590    assert(0 == s);
591
592    (void) dec_sz;  /* We don't care for this test */
593
594    s = lsqpack_enc_start_header(&enc, 0, 0);
595    assert(0 == s);
596    enc_sz = sizeof(enc_buf);
597    header_sz = sizeof(header_buf);
598    hbuf.off = 0;
599    header_set_ptr(&xhdr, &hbuf, ":method", 7, "dude!", 5);
600    enc_st = lsqpack_enc_encode(&enc,
601            enc_buf, &enc_sz, header_buf, &header_sz,
602            &xhdr, 0);
603    assert(LQES_OK == enc_st);
604    enc_sz = sizeof(enc_buf);
605    header_sz = sizeof(header_buf);
606    header_set_ptr(&xhdr, &hbuf, ":method", 7, "dude!", 5);
607    enc_st = lsqpack_enc_encode(&enc,
608            enc_buf, &enc_sz, header_buf, &header_sz,
609            &xhdr, 0);
610    assert(LQES_OK == enc_st);
611    nw = lsqpack_enc_end_header(&enc, prefix_buf, sizeof(prefix_buf), &hflags);
612    assert(2 == nw);
613    assert(!(prefix_buf[0] == 0 && prefix_buf[1] == 0)); /* Dynamic table used */
614    assert(hflags & LSQECH_REF_NEW_ENTRIES);
615    assert(hflags & LSQECH_REF_AT_RISK);
616
617    s = lsqpack_enc_start_header(&enc, 0, 0);
618    assert(0 == s);
619    enc_sz = sizeof(enc_buf);
620    header_sz = sizeof(header_buf);
621    header_set_ptr(&xhdr, &hbuf, ":method", 7, "dude!", 5);
622    enc_st = lsqpack_enc_encode(&enc,
623            enc_buf, &enc_sz, header_buf, &header_sz,
624            &xhdr, LQEF_NO_HIST_UPD|LQEF_NO_DYN);
625    assert(LQES_OK == enc_st);
626    enc_sz = sizeof(enc_buf);
627    header_sz = sizeof(header_buf);
628    header_set_ptr(&xhdr, &hbuf, ":method", 7, "where is my car?", 16);
629    enc_st = lsqpack_enc_encode(&enc,
630            enc_buf, &enc_sz, header_buf, &header_sz,
631            &xhdr, LQEF_NO_HIST_UPD|LQEF_NO_DYN);
632    nw = lsqpack_enc_end_header(&enc, prefix_buf, sizeof(prefix_buf), &hflags);
633    assert(2 == nw);
634    assert(prefix_buf[0] == 0 && prefix_buf[1] == 0); /* Dynamic table not used */
635    assert(!(hflags & LSQECH_REF_NEW_ENTRIES));
636
637    /* Last check that history was not updated: */
638    s = lsqpack_enc_start_header(&enc, 4, 0);
639    assert(0 == s);
640    enc_sz = sizeof(enc_buf);
641    header_sz = sizeof(header_buf);
642    header_set_ptr(&xhdr, &hbuf, ":method", 7, "where is my car?", 16);
643    enc_st = lsqpack_enc_encode(&enc,
644            enc_buf, &enc_sz, header_buf, &header_sz,
645            &xhdr, 0);
646    assert(enc_sz == 0);
647    assert(LQES_OK == enc_st);
648    nw = lsqpack_enc_end_header(&enc, prefix_buf, sizeof(prefix_buf), &hflags);
649    assert(2 == nw);
650    assert(prefix_buf[0] == 0 && prefix_buf[1] == 0); /* Dynamic table not used */
651    assert(!(hflags & LSQECH_REF_AT_RISK));
652
653    lsqpack_enc_cleanup(&enc);
654}
655
656
657struct hblock_ctx
658{
659    unsigned                n_headers;
660    int                     finished;
661    struct lsxpack_header   xhdr;
662    char                    buf[0x10000];
663};
664
665
666static void
667unblocked (void *hblock_ctx_p)
668{
669    assert(0);  /* Not expecting this to be called */
670}
671
672
673static struct lsxpack_header *
674prepare_decode (void *hblock_ctx_p, struct lsxpack_header *xhdr, size_t space)
675{
676    struct hblock_ctx *const hctx = hblock_ctx_p;
677
678    if (space > LSXPACK_MAX_STRLEN)
679        return NULL;
680
681    if (xhdr)
682        return NULL;
683
684    lsxpack_header_prepare_decode(&hctx->xhdr, hctx->buf, 0, sizeof(hctx->buf));
685    return &hctx->xhdr;
686}
687
688
689static int
690process_header (void *hblock_ctx_p, struct lsxpack_header *xhdr)
691{
692    struct hblock_ctx *const hctx = hblock_ctx_p;
693
694    if (xhdr)
695        ++hctx->n_headers;
696    else
697        hctx->finished = 1;
698    return 0;
699}
700
701
702static const struct lsqpack_dec_hset_if hset_if = {
703    .dhi_unblocked      = unblocked,
704    .dhi_prepare_decode = prepare_decode,
705    .dhi_process_header = process_header,
706};
707
708
709static void
710test_discard_header (int err)
711{
712    struct lsqpack_dec dec;
713    enum lsqpack_read_header_status rhs;
714    const unsigned char *buf;
715    unsigned char header_block[] = "\x00\x00\xC0\x80";
716    struct hblock_ctx hctx = { .n_headers = 0, };
717
718    lsqpack_dec_init(&dec, NULL, 0, 0, &hset_if, LSQPACK_DEC_OPT_HTTP1X);
719
720    buf = header_block;
721    rhs = lsqpack_dec_header_in(&dec, &hctx, 0, 10,
722                                    &buf, 3 + !!err, NULL, NULL);
723    if (err)
724    {
725        assert(hctx.n_headers == 1);
726        assert(hctx.finished == 0);
727        assert(LQRHS_ERROR == rhs);
728    }
729    else
730    {
731        assert(hctx.n_headers == 1);
732        assert(hctx.finished == 0);
733        assert(3 == buf - header_block);
734        assert(LQRHS_NEED == rhs);
735        lsqpack_dec_cleanup(&dec);
736    }
737}
738
739
740static void
741test_static_bounds_header_block (void)
742{
743    struct lsqpack_dec dec;
744    enum lsqpack_read_header_status rhs;
745    const unsigned char *buf;
746    /* Static table index 1000 */
747    unsigned char header_block[] = "\x00\x00\xFF\xA9\x07";
748    struct hblock_ctx hctx = { .n_headers = 0, };
749
750    lsqpack_dec_init(&dec, stderr, 0, 0, &hset_if, LSQPACK_DEC_OPT_HTTP1X);
751    buf = header_block;
752    rhs = lsqpack_dec_header_in(&dec, &hctx, 0, 10,
753                                    &buf, 5, NULL, NULL);
754    assert(hctx.n_headers == 0);
755    assert(LQRHS_ERROR == rhs);
756    lsqpack_dec_cleanup(&dec);
757}
758
759
760static void
761test_static_bounds_enc_stream (void)
762{
763    struct lsqpack_dec dec;
764    int r;
765    /* Static table index 1000 */
766    unsigned char enc_stream[] = "\xFF\xA9\x07\x04" "dude";
767
768    lsqpack_dec_init(&dec, NULL, 0, 0, &hset_if, LSQPACK_DEC_OPT_HTTP1X);
769    r = lsqpack_dec_enc_in(&dec, enc_stream, 8);
770    assert(r == -1);
771    lsqpack_dec_cleanup(&dec);
772}
773
774
775static void
776test_wonr_name_too_large_huffman (void)
777{
778    struct lsqpack_dec dec;
779    int r;
780    /* Partial Insert Without Name Reference with Huffman-encoded name
781     * string that is larger than 4 x capacity (0x4001)
782     */
783    unsigned char enc_stream[] = "\x7F\xE2\x7F";
784
785    lsqpack_dec_init(&dec, stderr, 0x1000, 0, &hset_if, LSQPACK_DEC_OPT_HTTP1X);
786    r = lsqpack_dec_enc_in(&dec, enc_stream, 3);
787    assert(r == -1);
788    lsqpack_dec_cleanup(&dec);
789}
790
791
792static void
793test_wonr_name_too_large_plain (void)
794{
795    struct lsqpack_dec dec;
796    int r;
797    /* Partial Insert Without Name Reference with plain-encoded name
798     * string that is larger than capacity (0x1001)
799     */
800    unsigned char enc_stream[] = "\x5F\xE2\x1F";
801
802    lsqpack_dec_init(&dec, stderr, 0, 0, &hset_if, LSQPACK_DEC_OPT_HTTP1X);
803    r = lsqpack_dec_enc_in(&dec, enc_stream, 3);
804    assert(r == -1);
805    lsqpack_dec_cleanup(&dec);
806}
807
808
809static void
810test_wonr_value_too_large_huffman (void)
811{
812    struct lsqpack_dec dec;
813    int r;
814    /* Partial Insert Without Name Reference with Huffman-encoded value
815     * string that, together with name, is larger than 4 x capacity (0x3FFF)
816     */
817    unsigned char enc_stream[] = "\x42OK\xFF\x80\x7F";
818
819    lsqpack_dec_init(&dec, stderr, 0, 0, &hset_if, LSQPACK_DEC_OPT_HTTP1X);
820    r = lsqpack_dec_enc_in(&dec, enc_stream, 6);
821    assert(r == -1);
822    lsqpack_dec_cleanup(&dec);
823}
824
825
826static void
827test_wonr_value_too_large_plain (void)
828{
829    struct lsqpack_dec dec;
830    int r;
831    /* Partial Insert Without Name Reference with plain-encoded value
832     * string that, together with name, is larger than capacity (0xFFF)
833     */
834    unsigned char enc_stream[] = "\x42OK\x7F\x80\x1F";
835
836    lsqpack_dec_init(&dec, stderr, 0x1000, 0, &hset_if, LSQPACK_DEC_OPT_HTTP1X);
837    r = lsqpack_dec_enc_in(&dec, enc_stream, 6);
838    assert(r == -1);
839    lsqpack_dec_cleanup(&dec);
840}
841
842
843static void
844test_winr_value_too_large_huffman (void)
845{
846    struct lsqpack_dec dec;
847    int r;
848    /* Partial Insert With Name Reference with Huffman-encoded value
849     * string that, together with name, is larger than 4 x capacity (0x3FE4)
850     */
851    /* Refer to static entry 79: access-control-refer-headers (length 28) */
852    unsigned char enc_stream[] = "\xFF\x10\xFF\xE5\x7E";
853
854    lsqpack_dec_init(&dec, stderr, 0x1000, 0, &hset_if, LSQPACK_DEC_OPT_HTTP1X);
855    r = lsqpack_dec_enc_in(&dec, enc_stream, 5);
856    assert(r == -1);
857    lsqpack_dec_cleanup(&dec);
858}
859
860
861static void
862test_winr_value_too_large_plain (void)
863{
864    struct lsqpack_dec dec;
865    int r;
866    /* Partial Insert With Name Reference with plain-encoded value
867     * string that, together with name, is larger than capacity (0xFE4)
868     */
869    /* Refer to static entry 79: access-control-refer-headers (length 28) */
870    unsigned char enc_stream[] = "\xFF\x10\x7F\xE5\x1E";
871
872    lsqpack_dec_init(&dec, stderr, 0x1000, 0, &hset_if, LSQPACK_DEC_OPT_HTTP1X);
873    r = lsqpack_dec_enc_in(&dec, enc_stream, 5);
874    assert(r == -1);
875    lsqpack_dec_cleanup(&dec);
876}
877
878
879/* This is an odd case, but if the first call should provide no input at all,
880 * the decoder should return LQRHS_NEED
881 */
882static void
883test_dec_header_zero_in (void)
884{
885    struct lsqpack_dec dec;
886    struct lsqpack_header_list *hlist;
887    enum lsqpack_read_header_status rhs;
888    const unsigned char *buf = (unsigned char *) "";
889    struct hblock_ctx hctx = { .n_headers = 0, };
890
891    lsqpack_dec_init(&dec, stderr, 0x1000, 0, &hset_if, LSQPACK_DEC_OPT_HTTP1X);
892
893    rhs = lsqpack_dec_header_in(&dec,
894                &hctx /* hblock */,
895                2 /* Stream ID */,
896                100 /* How long the thing is */,
897                &buf,
898                0 /* How many bytes are available */,
899                NULL, 0);
900    assert(LQRHS_NEED == rhs);
901
902    lsqpack_dec_cleanup(&dec);
903}
904
905
906/* Header that's too should should return LQRHS_ERROR */
907static void
908test_dec_header_too_short (size_t header_size)
909{
910    struct lsqpack_dec dec;
911    struct lsqpack_header_list *hlist;
912    enum lsqpack_read_header_status rhs;
913    const unsigned char *buf = (unsigned char *) "";
914    struct hblock_ctx hctx = { .n_headers = 0, };
915
916    lsqpack_dec_init(&dec, stderr, 0x1000, 0, &hset_if, LSQPACK_DEC_OPT_HTTP1X);
917
918    rhs = lsqpack_dec_header_in(&dec,
919                &hctx /* hblock */,
920                2 /* Stream ID */,
921                header_size,
922                &buf,
923                0 /* How many bytes are available */,
924                NULL, 0);
925    assert(LQRHS_ERROR == rhs);
926
927    lsqpack_dec_cleanup(&dec);
928}
929
930
931static void
932test_enc_risked_streams_test (const char *test)
933{
934    struct lsqpack_enc enc;
935    int s;
936    size_t sz;
937    ssize_t ssz;
938    long arg;
939    enum lsqpack_enc_status es;
940    unsigned n_seqnos = 0;
941    struct {
942        uint64_t stream_id;
943        unsigned seqno;
944    } seqnos[10], *seq_el;
945    unsigned char buf[0x100];
946    unsigned char *end_cmd;
947    int expect_failure;
948    struct lsxpack_header xhdr;
949    struct header_buf hbuf;
950
951    const struct {
952        const char *name;
953        unsigned    name_len;
954        const char *value;
955        unsigned    value_len;
956    } headers[] = {
957        {   "some", 4, "header", 6, },
958        {   "another", 7, "header", 6, },
959        {   "and yet another", 15, "header, duh", 11, },
960    };
961
962    fprintf(stderr, "BEGIN TEST %s\n", test);
963    lsqpack_enc_preinit(&enc, stderr);
964    hbuf.off = 0;
965
966    while (1)
967    {
968        expect_failure = isupper(*test);
969        switch (*test++)
970        {
971        case 'i':
972            arg = strtol(test, (char**)&test, 10);
973            sz = sizeof(buf);
974            s = lsqpack_enc_init(&enc, stderr, 0x1000, 0x1000,
975                                        (unsigned) arg, 0, buf, &sz);
976            assert(s == 0);
977            break;
978        case 'r':
979            arg = strtol(test, (char**)&test, 10);
980            assert((unsigned) arg == enc.qpe_cur_streams_at_risk);
981            break;
982        case 's':   /* Start header */
983            arg = strtol(test, (char**)&test, 10);
984            for (seq_el = seqnos; seq_el < seqnos + n_seqnos; ++seq_el)
985                if (seq_el->stream_id == arg)
986                    break;
987            assert(seq_el < seqnos + sizeof(seqnos) / sizeof(seqnos[0]));
988            if (seq_el == seqnos + n_seqnos)
989            {
990                ++n_seqnos;
991                seq_el->stream_id = arg;
992                seq_el->seqno = 0;
993            }
994            s = lsqpack_enc_start_header(&enc, arg, seq_el->seqno++);
995            assert(s == 0);
996            break;
997        case 'c':   /* En*C*ode */
998            arg = strtol(test, (char**)&test, 10);
999            sz = sizeof(buf);
1000            /* We ignore the output */
1001            header_set_ptr(&xhdr, &hbuf,
1002                        headers[arg].name, headers[arg].name_len,
1003                        headers[arg].value, headers[arg].value_len);
1004            es = lsqpack_enc_encode(&enc, buf, &sz, buf, &sz, &xhdr, 0);
1005            assert(LQES_OK == es);
1006            break;
1007        case 'e':   /* End header */
1008            ssz = lsqpack_enc_end_header(&enc, buf, sizeof(buf), NULL);
1009            assert(ssz > 0);
1010            break;
1011        case 'a':   /* ACK header */
1012        case 'A':
1013            arg = strtol(test, (char**)&test, 10);
1014            buf[0] = 0x80;
1015            end_cmd = lsqpack_enc_int(buf, buf + sizeof(buf), arg, 7);
1016            s = lsqpack_enc_decoder_in(&enc, buf, end_cmd - buf);
1017            if (expect_failure)
1018                assert(s < 0);
1019            else
1020                assert(s == 0);
1021            break;
1022        case 'L':   /* Cancel header */
1023        case 'l':
1024            arg = strtol(test, (char**)&test, 10);
1025            buf[0] = 0x40;
1026            end_cmd = lsqpack_enc_int(buf, buf + sizeof(buf), arg, 6);
1027            s = lsqpack_enc_decoder_in(&enc, buf, end_cmd - buf);
1028            if (expect_failure)
1029                assert(s < 0);
1030            else
1031                assert(s == 0);
1032            break;
1033        case 'N':   /* Insert Count Increment */
1034        case 'n':
1035            arg = strtol(test, (char**)&test, 10);
1036            buf[0] = 0x00;
1037            end_cmd = lsqpack_enc_int(buf, buf + sizeof(buf), arg, 6);
1038            s = lsqpack_enc_decoder_in(&enc, buf, end_cmd - buf);
1039            if (expect_failure)
1040                assert(s < 0);
1041            else
1042                assert(s == 0);
1043            break;
1044            break;
1045        case '\0':
1046            goto end;
1047        default:
1048            assert("unknown action");
1049            goto end;
1050        }
1051    }
1052
1053  end:
1054    lsqpack_enc_cleanup(&enc);
1055}
1056
1057
1058static void
1059test_enc_risked_streams (void)
1060{
1061    const char **test;
1062    const char *tests[] =
1063    {
1064        "i0r0s1c0er0",
1065        "i1r0s1c0er0s2c0er1A1r1a2r0",
1066        "i1r0s1c0er0s2c0er1s2c0c1er1a2r0a2r0",
1067
1068        "i1r0s1c0c1er0"
1069        "s2c0er1"
1070        "s2c0c1er1"
1071        "a2r1"      /* Ack first seqno, # of risked streams still 1 */
1072        "a2r0",
1073
1074        "i1r0s1c0c1er0"
1075        "s2c0er1"
1076        "s2c0c1er1"
1077        "l2r0"      /* Cancel */
1078        ,
1079
1080        "i1r0s1c0c1er0"
1081        "s2c0er1"
1082        "s2c0c1er1"
1083        "N3"
1084        "n1r1"
1085        "n1r0"
1086        ,
1087
1088        NULL,
1089    };
1090
1091    for (test = tests; *test; ++test)
1092        test_enc_risked_streams_test(*test);
1093}
1094
1095
1096int
1097main (void)
1098{
1099    unsigned i;
1100
1101    for (i = 0; i < sizeof(header_block_tests)
1102                                / sizeof(header_block_tests[0]); ++i)
1103    {
1104        run_header_test(&header_block_tests[i]);
1105        run_decoded_headers_test(&header_block_tests[i]);
1106    }
1107
1108    run_header_cancellation_test(&header_block_tests[0]);
1109    test_enc_init();
1110    test_push_promise();
1111    test_discard_header(0);
1112    test_discard_header(1);
1113    test_static_bounds_header_block();
1114    test_static_bounds_enc_stream();
1115    test_wonr_name_too_large_huffman();
1116    test_wonr_name_too_large_plain();
1117    test_wonr_value_too_large_huffman();
1118    test_wonr_value_too_large_plain();
1119    test_winr_value_too_large_huffman();
1120    test_winr_value_too_large_plain();
1121    test_dec_header_zero_in();
1122    test_dec_header_too_short(0);
1123    test_dec_header_too_short(1);
1124    test_enc_risked_streams();
1125
1126    return 0;
1127}
1128