1/*
2 * QPACK Interop -- decode from intermediate format and output QIF
3 *
4 * https://github.com/quicwg/base-drafts/wiki/QPACK-Offline-Interop
5 */
6
7#include <assert.h>
8
9#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__)
10#include <sys/endian.h>
11#define bswap_16 bswap16
12#define bswap_32 bswap32
13#define bswap_64 bswap64
14#elif defined(__APPLE__)
15#include <libkern/OSByteOrder.h>
16#define bswap_16 OSSwapInt16
17#define bswap_32 OSSwapInt32
18#define bswap_64 OSSwapInt64
19#elif defined(WIN32)
20#define bswap_16 _byteswap_ushort
21#define bswap_32 _byteswap_ulong
22#define bswap_64 _byteswap_uint64
23#else
24#include <byteswap.h>
25#endif
26
27#include <errno.h>
28#include <inttypes.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#ifdef WIN32
33#include <getopt.h>
34#else
35#include <unistd.h>
36#endif
37#include <sys/queue.h>
38#include <sys/types.h>
39#include <sys/stat.h>
40#include <fcntl.h>
41
42#include "lsqpack.h"
43#include "lsxpack_header.h"
44#include "xxhash.h"
45#ifndef DEBUG
46#include "lsqpack-test.h"
47#endif
48
49#ifndef NDEBUG
50struct static_table_entry
51{
52    const char       *name;
53    const char       *val;
54    unsigned          name_len;
55    unsigned          val_len;
56};
57
58/* [draft-ietf-quic-qpack-03] Appendix A */
59static const struct static_table_entry static_table[] =
60{
61    {":authority", "", 10, 0,},
62    {":path", "/", 5, 1,},
63    {"age", "0", 3, 1,},
64    {"content-disposition", "", 19, 0,},
65    {"content-length", "0", 14, 1,},
66    {"cookie", "", 6, 0,},
67    {"date", "", 4, 0,},
68    {"etag", "", 4, 0,},
69    {"if-modified-since", "", 17, 0,},
70    {"if-none-match", "", 13, 0,},
71    {"last-modified", "", 13, 0,},
72    {"link", "", 4, 0,},
73    {"location", "", 8, 0,},
74    {"referer", "", 7, 0,},
75    {"set-cookie", "", 10, 0,},
76    {":method", "CONNECT", 7, 7,},
77    {":method", "DELETE", 7, 6,},
78    {":method", "GET", 7, 3,},
79    {":method", "HEAD", 7, 4,},
80    {":method", "OPTIONS", 7, 7,},
81    {":method", "POST", 7, 4,},
82    {":method", "PUT", 7, 3,},
83    {":scheme", "http", 7, 4,},
84    {":scheme", "https", 7, 5,},
85    {":status", "103", 7, 3,},
86    {":status", "200", 7, 3,},
87    {":status", "304", 7, 3,},
88    {":status", "404", 7, 3,},
89    {":status", "503", 7, 3,},
90    {"accept", "*/*", 6, 3,},
91    {"accept", "application/dns-message", 6, 23,},
92    {"accept-encoding", "gzip, deflate, br", 15, 17,},
93    {"accept-ranges", "bytes", 13, 5,},
94    {"access-control-allow-headers", "cache-control", 28, 13,},
95    {"access-control-allow-headers", "content-type", 28, 12,},
96    {"access-control-allow-origin", "*", 27, 1,},
97    {"cache-control", "max-age=0", 13, 9,},
98    {"cache-control", "max-age=2592000", 13, 15,},
99    {"cache-control", "max-age=604800", 13, 14,},
100    {"cache-control", "no-cache", 13, 8,},
101    {"cache-control", "no-store", 13, 8,},
102    {"cache-control", "public, max-age=31536000", 13, 24,},
103    {"content-encoding", "br", 16, 2,},
104    {"content-encoding", "gzip", 16, 4,},
105    {"content-type", "application/dns-message", 12, 23,},
106    {"content-type", "application/javascript", 12, 22,},
107    {"content-type", "application/json", 12, 16,},
108    {"content-type", "application/x-www-form-urlencoded", 12, 33,},
109    {"content-type", "image/gif", 12, 9,},
110    {"content-type", "image/jpeg", 12, 10,},
111    {"content-type", "image/png", 12, 9,},
112    {"content-type", "text/css", 12, 8,},
113    {"content-type", "text/html; charset=utf-8", 12, 24,},
114    {"content-type", "text/plain", 12, 10,},
115    {"content-type", "text/plain;charset=utf-8", 12, 24,},
116    {"range", "bytes=0-", 5, 8,},
117    {"strict-transport-security", "max-age=31536000", 25, 16,},
118    {"strict-transport-security", "max-age=31536000; includesubdomains",
119                                                                25, 35,},
120    {"strict-transport-security",
121                "max-age=31536000; includesubdomains; preload", 25, 44,},
122    {"vary", "accept-encoding", 4, 15,},
123    {"vary", "origin", 4, 6,},
124    {"x-content-type-options", "nosniff", 22, 7,},
125    {"x-xss-protection", "1; mode=block", 16, 13,},
126    {":status", "100", 7, 3,},
127    {":status", "204", 7, 3,},
128    {":status", "206", 7, 3,},
129    {":status", "302", 7, 3,},
130    {":status", "400", 7, 3,},
131    {":status", "403", 7, 3,},
132    {":status", "421", 7, 3,},
133    {":status", "425", 7, 3,},
134    {":status", "500", 7, 3,},
135    {"accept-language", "", 15, 0,},
136    {"access-control-allow-credentials", "FALSE", 32, 5,},
137    {"access-control-allow-credentials", "TRUE", 32, 4,},
138    {"access-control-allow-headers", "*", 28, 1,},
139    {"access-control-allow-methods", "get", 28, 3,},
140    {"access-control-allow-methods", "get, post, options", 28, 18,},
141    {"access-control-allow-methods", "options", 28, 7,},
142    {"access-control-expose-headers", "content-length", 29, 14,},
143    {"access-control-request-headers", "content-type", 30, 12,},
144    {"access-control-request-method", "get", 29, 3,},
145    {"access-control-request-method", "post", 29, 4,},
146    {"alt-svc", "clear", 7, 5,},
147    {"authorization", "", 13, 0,},
148    {"content-security-policy",
149            "script-src 'none'; object-src 'none'; base-uri 'none'", 23, 53,},
150    {"early-data", "1", 10, 1,},
151    {"expect-ct", "", 9, 0,},
152    {"forwarded", "", 9, 0,},
153    {"if-range", "", 8, 0,},
154    {"origin", "", 6, 0,},
155    {"purpose", "prefetch", 7, 8,},
156    {"server", "", 6, 0,},
157    {"timing-allow-origin", "*", 19, 1,},
158    {"upgrade-insecure-requests", "1", 25, 1,},
159    {"user-agent", "", 10, 0,},
160    {"x-forwarded-for", "", 15, 0,},
161    {"x-frame-options", "deny", 15, 4,},
162    {"x-frame-options", "sameorigin", 15, 10,},
163};
164#endif
165
166static size_t s_max_read_size = SIZE_MAX;
167
168static int s_verbose;
169static enum lsqpack_dec_opts s_dec_opts = LSQPACK_DEC_OPT_HASH_NAME
170                                        | LSQPACK_DEC_OPT_HASH_NAMEVAL;
171
172static int s_check_unset_qpack_idx = 1;
173
174static FILE *s_out;
175
176#define MIN(a, b) ((a) < (b) ? (a) : (b))
177
178static void
179usage (const char *name)
180{
181    fprintf(stderr,
182"Usage: %s [options] [-i input] [-o output]\n"
183"\n"
184"Options:\n"
185"   -i FILE     Input file.  If not specified or set to `-', the input is\n"
186"                 read from stdin.\n"
187"   -o FILE     Output file.  If not spepcified or set to `-', the output\n"
188"                 is written to stdout.\n"
189"   -r FILE     Recipe file.  Without a recipe, buffers are processed in\n"
190"                 order.\n"
191"   -s NUMBER   Maximum number of risked streams.  Defaults to %u.\n"
192"   -t NUMBER   Dynamic table size.  Defaults to %u.\n"
193"   -m NUMBER   Maximum read size.  Defaults to %zu.\n"
194"   -H [0|1]    Use HTTP/1.x mode and test each header (defaults to `off').\n"
195"   -v          Verbose: print headers and table state to stderr.\n"
196"   -S          Don't swap encoder stream and header blocks.\n"
197"   -Q          Don't check static table when LSXPACK_QPACK_IDX is not set.\n"
198"\n"
199"   -h          Print this help screen and exit\n"
200    , name, LSQPACK_DEF_MAX_RISKED_STREAMS, LSQPACK_DEF_DYN_TABLE_SIZE, SIZE_MAX);
201}
202
203
204struct buf
205{
206    TAILQ_ENTRY(buf)        next_buf;
207    struct lsqpack_dec     *dec;
208    uint64_t                stream_id;     /* Zero means encoder stream */
209    size_t                  size;
210    size_t                  off;
211    size_t                  file_off;
212
213    /* A single header name/value pair is stored in xhdr_buf.  When the
214     * header is done, the whole buffer can be used again for the next
215     * header.
216     */
217    struct lsxpack_header   xhdr;
218    unsigned                xhdr_off;       /* Used in xhdr_buf */
219    unsigned                xhdr_nalloc;    /* Number of bytes allocated */ /* TODO: use it */
220
221    /* Output is written to out_buf out header at a time and the printed all
222     * at once.  This logic is not very important and we use a reasonably
223     * large fixed-size buffer.
224     */
225    unsigned                out_off;
226    char                    out_buf[0x1000];
227
228    unsigned char           buf[0];
229};
230
231
232TAILQ_HEAD(, buf) bufs = TAILQ_HEAD_INITIALIZER(bufs);
233
234static void
235hblock_unblocked (void *buf_p)
236{
237    struct buf *buf = buf_p;
238    TAILQ_INSERT_HEAD(&bufs, buf, next_buf);
239}
240
241
242static struct lsxpack_header *
243prepare_decode (void *hblock_ctx, struct lsxpack_header *xhdr, size_t space)
244{
245    struct buf *const buf = hblock_ctx;
246    char *new;
247
248    if (space > LSXPACK_MAX_STRLEN)
249        return NULL;
250
251    if (xhdr)
252    {
253        assert(xhdr == &buf->xhdr);
254        assert(space > xhdr->val_len);
255        new = realloc(xhdr->buf, space);
256        if (!new)
257            return NULL;
258        xhdr->buf = new;
259        xhdr->val_len = space;
260        return xhdr;
261    }
262    else
263    {
264        xhdr = &buf->xhdr;
265        new = malloc(space);
266        if (!new)
267            return NULL;
268        lsxpack_header_prepare_decode(xhdr, new, 0, space);
269        return xhdr;
270    }
271}
272
273
274static int
275process_header (void *hblock_ctx, struct lsxpack_header *xhdr)
276{
277    struct buf *const buf = hblock_ctx;
278    const char *p;
279    const uint32_t seed = 39378473;
280    uint32_t hash, name_hash;
281    int nw;
282
283    if (s_dec_opts & LSQPACK_DEC_OPT_HTTP1X)
284    {
285        p = lsxpack_header_get_name(xhdr) + xhdr->name_len;
286        assert(0 == memcmp(p, ": ", 2));
287        p += 2 + xhdr->val_len;
288        assert(0 == memcmp(p, "\r\n", 2));
289        assert(xhdr->dec_overhead == 4);
290    }
291    else
292        assert(xhdr->dec_overhead == 0);
293
294    if (s_dec_opts & LSQPACK_DEC_OPT_HASH_NAME)
295    {
296        assert(xhdr->flags & LSXPACK_NAME_HASH);
297        hash = XXH32(lsxpack_header_get_name(xhdr), xhdr->name_len, seed);
298        assert(hash == xhdr->name_hash);
299    }
300
301    if (s_dec_opts & LSQPACK_DEC_OPT_HASH_NAME)
302        assert(xhdr->flags & LSXPACK_NAME_HASH);
303
304    if (xhdr->flags & LSXPACK_NAME_HASH)
305    {
306        name_hash = XXH32(lsxpack_header_get_name(xhdr), xhdr->name_len, seed);
307        assert(hash == xhdr->name_hash);
308    }
309#ifndef NDEBUG
310    else if (!(xhdr->flags & LSXPACK_QPACK_IDX))
311        /* Calculate for upcoming "not in static table check" */
312        name_hash = XXH32(lsxpack_header_get_name(xhdr), xhdr->name_len, seed);
313#endif
314
315    if (s_dec_opts & LSQPACK_DEC_OPT_HASH_NAMEVAL)
316    {
317        /* This is not required by the API, but internally, if the library
318         * calculates nameval hash, it should also set the name hash.
319         */
320        assert(xhdr->flags & LSXPACK_NAME_HASH);
321        assert(xhdr->flags & LSXPACK_NAMEVAL_HASH);
322    }
323
324    if (xhdr->flags & LSXPACK_NAMEVAL_HASH)
325    {
326        hash = XXH32(lsxpack_header_get_name(xhdr), xhdr->name_len, seed);
327        hash = XXH32(lsxpack_header_get_value(xhdr), xhdr->val_len, hash);
328        assert(hash == xhdr->nameval_hash);
329    }
330
331#ifndef NDEBUG
332    if (xhdr->flags & LSXPACK_QPACK_IDX)
333    {
334        assert(xhdr->qpack_index <
335                            sizeof(static_table) / sizeof(static_table[0]));
336        assert(static_table[xhdr->qpack_index].name_len == xhdr->name_len);
337        assert(0 == memcmp(lsxpack_header_get_name(xhdr),
338                        static_table[xhdr->qpack_index].name, xhdr->name_len));
339    }
340    else if (s_check_unset_qpack_idx)
341    {
342        /* The decoder does best effort: if the encoder did not use the
343         * static table, QPACK index is not set.  However, since we are
344         * testing our decoder, we assume that the encoder always uses
345         * the static table when it can.
346         */
347        int idx = lsqpack_find_in_static_headers(name_hash,
348                            lsxpack_header_get_name(xhdr), xhdr->name_len);
349        assert(idx < 0);
350    }
351#endif
352
353    nw = snprintf(buf->out_buf + buf->out_off,
354            sizeof(buf->out_buf) - buf->out_off,
355            "%.*s\t%.*s\n",
356            (int) xhdr->name_len, lsxpack_header_get_name(xhdr),
357            (int) xhdr->val_len, lsxpack_header_get_value(xhdr));
358    free(buf->xhdr.buf);
359    memset(&buf->xhdr, 0, sizeof(buf->xhdr));
360    if (nw > 0 && (size_t) nw <= sizeof(buf->out_buf) - buf->out_off)
361    {
362        buf->out_off += (unsigned) nw;
363        return 0;
364    }
365    else
366    {
367        fprintf(stderr, "header list too long\n");
368        return -1;
369    }
370}
371
372
373static const struct lsqpack_dec_hset_if hset_if = {
374    .dhi_unblocked      = hblock_unblocked,
375    .dhi_prepare_decode = prepare_decode,
376    .dhi_process_header = process_header,
377};
378
379
380static void
381header_block_done (const struct buf *buf)
382{
383    fprintf(s_out, "# stream %"PRIu64"\n", buf->stream_id);
384    fprintf(s_out, "# (stream ID above is used for sorting)\n");
385    fprintf(s_out, "%.*s\n", (int) buf->out_off, buf->out_buf);
386}
387
388
389int
390main (int argc, char **argv)
391{
392    FILE *in = stdin;
393    FILE *recipe = NULL;
394    int opt;
395    unsigned dyn_table_size     = LSQPACK_DEF_DYN_TABLE_SIZE,
396             max_risked_streams = LSQPACK_DEF_MAX_RISKED_STREAMS;
397    struct lsqpack_dec decoder;
398    const struct lsqpack_dec_err *err;
399    const unsigned char *p;
400    ssize_t nr;
401    int r;
402    uint64_t stream_id;
403    uint32_t size;
404    size_t off;         /* For debugging */
405    size_t file_off;
406    struct buf *buf;
407    unsigned lineno;
408    char *line, *end;
409    enum lsqpack_read_header_status rhs;
410    struct lsqpack_header_list *hlist;
411    int do_swap = 1;
412    char command[0x100];
413    char line_buf[0x100];
414
415    while (-1 != (opt = getopt(argc, argv, "i:o:r:s:t:m:hvH:SQ")))
416    {
417        switch (opt)
418        {
419        case 'i':
420            if (0 != strcmp(optarg, "-"))
421            {
422                in = fopen(optarg, "rb");
423                if (!in)
424                {
425                    fprintf(stderr, "cannot open `%s' for reading: %s\n",
426                                                optarg, strerror(errno));
427                    exit(EXIT_FAILURE);
428                }
429            }
430            break;
431        case 'o':
432            if (0 != strcmp(optarg, "-"))
433            {
434                s_out = fopen(optarg, "w");
435                if (!s_out)
436                {
437                    fprintf(stderr, "cannot open `%s' for writing: %s\n",
438                                                optarg, strerror(errno));
439                    exit(EXIT_FAILURE);
440                }
441            }
442            break;
443        case 'r':
444            if (0 == strcmp(optarg, "-"))
445                recipe = stdin;
446            else
447            {
448                recipe = fopen(optarg, "r");
449                if (!recipe)
450                {
451                    fprintf(stderr, "cannot open `%s' for reading: %s\n",
452                                                optarg, strerror(errno));
453                    exit(EXIT_FAILURE);
454                }
455            }
456            break;
457        case 's':
458            max_risked_streams = atoi(optarg);
459            break;
460        case 't':
461            dyn_table_size = atoi(optarg);
462            break;
463        case 'm':
464            s_max_read_size = atoi(optarg);
465            break;
466        case 'h':
467            usage(argv[0]);
468            exit(EXIT_SUCCESS);
469        case 'v':
470            ++s_verbose;
471            break;
472        case 'S':
473            do_swap = 0;
474            break;
475        case 'H':
476            if (atoi(optarg))
477                s_dec_opts |= LSQPACK_DEC_OPT_HTTP1X;
478            else
479                s_dec_opts &= ~LSQPACK_DEC_OPT_HTTP1X;
480            break;
481        case 'Q':
482            s_check_unset_qpack_idx = 0;
483            break;
484        default:
485            exit(EXIT_FAILURE);
486        }
487    }
488
489    if (!s_out)
490        s_out = stdout;
491
492    lsqpack_dec_init(&decoder, s_verbose ? stderr : NULL, dyn_table_size,
493                        max_risked_streams, &hset_if, s_dec_opts);
494
495    off = 0;
496    while (1)
497    {
498        file_off = off;
499        nr = fread(&stream_id, 1, sizeof(stream_id), in);
500        if (nr == 0)
501            break;
502        if (nr != sizeof(stream_id))
503        {
504            fprintf(stderr, "could not read %zu bytes (stream id) at "
505                "offset %zu: %s\n", sizeof(stream_id), off, strerror(errno));
506            goto read_err;
507        }
508        off += nr;
509        nr = fread(&size, 1, sizeof(size), in);
510        if (nr != sizeof(size))
511        {
512            fprintf(stderr, "could not read %zu bytes (size) at "
513                "offset %zu: %s\n", sizeof(size), off, strerror(errno));
514            goto read_err;
515        }
516        off += nr;
517#if __BYTE_ORDER == __LITTLE_ENDIAN
518        stream_id = bswap_64(stream_id);
519        size = bswap_32(size);
520#endif
521        if (stream_id == 0 && size == 0)
522            continue;
523        buf = malloc(sizeof(*buf) + size);
524        if (!buf)
525        {
526            perror("malloc");
527            exit(EXIT_FAILURE);
528        }
529        memset(buf, 0, sizeof(*buf));
530        nr = fread(buf->buf, 1, size, in);
531        if (nr != (ssize_t) size)
532        {
533            fprintf(stderr, "could not read %"PRIu32" bytes (buffer) at "
534                "offset %zu: %s\n", size, off, strerror(errno));
535            goto read_err;
536        }
537        off += nr;
538        buf->dec = &decoder;
539        buf->stream_id = stream_id;
540        buf->size = size;
541        buf->file_off = file_off;
542        if (buf->size == 0)
543            exit(EXIT_FAILURE);
544        TAILQ_INSERT_TAIL(&bufs, buf, next_buf);
545    }
546    (void) fclose(in);
547    in = NULL;
548
549    if (recipe)
550    {
551        lineno = 0;
552        while (line = fgets(line_buf, sizeof(line_buf), recipe), line != NULL)
553        {
554            ++lineno;
555            end = strchr(line, '\n');
556            if (!end)
557            {
558                fprintf(stderr, "no newline on line %u\n", lineno);
559                exit(EXIT_FAILURE);
560            }
561            *end = '\0';
562
563            if (*line == '#')
564                continue;
565
566            if (3 == sscanf(line, " %[s] %"PRIu64" %"PRIu32" ", command, &stream_id, &size))
567            {
568                TAILQ_FOREACH(buf, &bufs, next_buf)
569                    if (stream_id == buf->stream_id)
570                        break;
571                if (!buf)
572                {
573                    fprintf(stderr, "stream %"PRIu64" not found (recipe line %u)\n",
574                        stream_id, lineno);
575                    exit(EXIT_FAILURE);
576                }
577                p = buf->buf;
578                rhs = lsqpack_dec_header_in(&decoder, buf, stream_id,
579                            buf->size, &p,
580                            buf->size /* FIXME: this should be `size' */,
581                            NULL, NULL);
582                switch (rhs)
583                {
584                case LQRHS_DONE:
585                    assert(p == buf->buf + buf->size);
586                    header_block_done(buf);
587                    if (s_verbose)
588                        fprintf(stderr, "compression ratio: %.3f\n",
589                            lsqpack_dec_ratio(&decoder));
590                    TAILQ_REMOVE(&bufs, buf, next_buf);
591                    free(buf);
592                    break;
593                case LQRHS_BLOCKED:
594                    buf->off += (unsigned) (p - buf->buf);
595                    TAILQ_REMOVE(&bufs, buf, next_buf);
596                    break;
597                case LQRHS_NEED:
598                    buf->off += (unsigned) (p - buf->buf);
599                    break;
600                default:
601                    assert(rhs == LQRHS_ERROR);
602                    fprintf(stderr, "recipe line %u: stream %"PRIu64": "
603                        "header_in error\n", lineno, stream_id);
604                    exit(EXIT_FAILURE);
605                }
606            }
607            else if (2 == sscanf(line, " %[z] %u ", command, &size))
608            {
609                s_max_read_size = size;
610            }
611            else
612            {
613                perror("sscanf");
614                exit(EXIT_FAILURE);
615            }
616        }
617        fclose(recipe);
618    }
619    else if (do_swap && max_risked_streams && dyn_table_size
620                    && TAILQ_FIRST(&bufs) && TAILQ_FIRST(&bufs)->stream_id)
621    {
622        /* Swap header blocks and encoder stream bufs to exercise blocked
623         * header blocks logic.
624         */
625        struct buf *saved_hblock = NULL, *next;
626        for (buf = TAILQ_FIRST(&bufs); buf; buf = next)
627        {
628            next = TAILQ_NEXT(buf, next_buf);
629            if (buf->stream_id)
630                continue;
631            if (saved_hblock)
632                TAILQ_INSERT_BEFORE(buf, saved_hblock, next_buf);
633            saved_hblock = buf;
634            TAILQ_REMOVE(&bufs, buf, next_buf);
635        }
636        TAILQ_INSERT_TAIL(&bufs, saved_hblock, next_buf);
637    }
638
639    while (buf = TAILQ_FIRST(&bufs), buf != NULL)
640    {
641        TAILQ_REMOVE(&bufs, buf, next_buf);
642        if (buf->stream_id == 0)
643        {
644            r = lsqpack_dec_enc_in(&decoder, buf->buf, buf->size - buf->off);
645            if (r != 0)
646            {
647                err = lsqpack_dec_get_err_info(buf->dec);
648                fprintf(stderr, "encoder_in error; off %"PRIu64", line %d\n",
649                                                            err->off, err->line);
650                exit(EXIT_FAILURE);
651            }
652            if (s_verbose)
653                lsqpack_dec_print_table(&decoder, stderr);
654            free(buf);
655        }
656        else
657        {
658        dec_header:
659            p = buf->buf + buf->off;
660            if (buf->off == 0)
661                rhs = lsqpack_dec_header_in(&decoder, buf, buf->stream_id,
662                                buf->size, &p, MIN(s_max_read_size, buf->size),
663                                NULL, NULL);
664            else
665                rhs = lsqpack_dec_header_read(buf->dec, buf, &p,
666                                MIN(s_max_read_size, (buf->size - buf->off)),
667                                NULL, NULL);
668            switch (rhs)
669            {
670            case LQRHS_DONE:
671                assert(p == buf->buf + buf->size);
672                header_block_done(buf);
673                if (s_verbose)
674                    fprintf(stderr, "compression ratio: %.3f\n",
675                        lsqpack_dec_ratio(&decoder));
676                free(buf);
677                break;
678            case LQRHS_BLOCKED:
679                buf->off = (unsigned) (p - buf->buf);
680                break;
681            case LQRHS_NEED:
682                buf->off = (unsigned) (p - buf->buf);
683                goto dec_header;
684            default:
685                assert(rhs == LQRHS_ERROR);
686                fprintf(stderr, "stream %"PRIu64": header block error "
687                    "starting at off %zu\n", buf->stream_id, buf->off);
688                err = lsqpack_dec_get_err_info(&decoder);
689                fprintf(stderr, "encoder_in error; off %"PRIu64", line %d\n",
690                                                            err->off, err->line);
691                exit(EXIT_FAILURE);
692            }
693        }
694    }
695
696    if (!TAILQ_EMPTY(&bufs))
697    {
698        fprintf(stderr, "some streams reamain\n");
699        exit(EXIT_FAILURE);
700    }
701    /* TODO: check if decoder has any stream references.  That would be
702     * an error.
703     */
704
705    if (s_verbose)
706        lsqpack_dec_print_table(&decoder, stderr);
707
708    lsqpack_dec_cleanup(&decoder);
709
710    assert(TAILQ_EMPTY(&bufs));
711
712    if (s_out)
713        (void) fclose(s_out);
714
715    exit(EXIT_SUCCESS);
716
717  read_err:
718    if (nr < 0)
719        perror("read");
720    else if (nr == 0)
721        fprintf(stderr, "unexpected EOF\n");
722    else
723        fprintf(stderr, "not enough bytes read (%zu)\n", (size_t) nr);
724    exit(EXIT_FAILURE);
725}
726