/* * nghttp3 * * Copyright (c) 2019 nghttp3 contributors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "nghttp3_qpack_test.h" #include #include #include "nghttp3_qpack.h" #include "nghttp3_macro.h" #include "nghttp3_test_helper.h" static void check_decode_header(nghttp3_qpack_decoder *dec, nghttp3_buf *pbuf, nghttp3_buf *rbuf, nghttp3_buf *ebuf, int64_t stream_id, const nghttp3_nv *nva, size_t nvlen, const nghttp3_mem *mem) { nghttp3_ssize nread; nghttp3_qpack_stream_context sctx; nghttp3_qpack_nv qnv; const nghttp3_nv *nv; uint8_t flags; size_t i = 0; nread = nghttp3_qpack_decoder_read_encoder(dec, ebuf->pos, nghttp3_buf_len(ebuf)); CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(ebuf) == nread); nghttp3_qpack_stream_context_init(&sctx, stream_id, mem); nread = nghttp3_qpack_decoder_read_request( dec, &sctx, &qnv, &flags, pbuf->pos, nghttp3_buf_len(pbuf), 0); CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(pbuf) == nread); for (; nghttp3_buf_len(rbuf);) { nread = nghttp3_qpack_decoder_read_request( dec, &sctx, &qnv, &flags, rbuf->pos, nghttp3_buf_len(rbuf), 1); CU_ASSERT(nread > 0); if (nread < 0) { break; } rbuf->pos += nread; if (flags & NGHTTP3_QPACK_DECODE_FLAG_FINAL) { break; } if (flags & NGHTTP3_QPACK_DECODE_FLAG_EMIT) { nv = &nva[i++]; CU_ASSERT(nv->namelen == qnv.name->len); CU_ASSERT(0 == memcmp(nv->name, qnv.name->base, nv->namelen)); CU_ASSERT(nv->valuelen == qnv.value->len); CU_ASSERT(0 == memcmp(nv->value, qnv.value->base, nv->valuelen)); nghttp3_rcbuf_decref(qnv.name); nghttp3_rcbuf_decref(qnv.value); } } CU_ASSERT(i == nvlen); nghttp3_qpack_stream_context_free(&sctx); nghttp3_buf_reset(pbuf); nghttp3_buf_reset(rbuf); nghttp3_buf_reset(ebuf); } static void decode_header_block(nghttp3_qpack_decoder *dec, nghttp3_buf *pbuf, nghttp3_buf *rbuf, int64_t stream_id, const nghttp3_mem *mem) { nghttp3_ssize nread; nghttp3_qpack_stream_context sctx; nghttp3_qpack_nv qnv; uint8_t flags; nghttp3_qpack_stream_context_init(&sctx, stream_id, mem); nread = nghttp3_qpack_decoder_read_request( dec, &sctx, &qnv, &flags, pbuf->pos, nghttp3_buf_len(pbuf), 0); CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(pbuf) == nread); for (;;) { nread = nghttp3_qpack_decoder_read_request( dec, &sctx, &qnv, &flags, rbuf->pos, nghttp3_buf_len(rbuf), 1); CU_ASSERT(nread >= 0); if (nread < 0) { break; } if (flags & NGHTTP3_QPACK_DECODE_FLAG_FINAL) { CU_ASSERT(nread == 0); CU_ASSERT(!(flags & NGHTTP3_QPACK_DECODE_FLAG_EMIT)); CU_ASSERT(0 == nghttp3_buf_len(rbuf)); CU_ASSERT(nghttp3_buf_len(&dec->dbuf) > 0); break; } CU_ASSERT(nread > 0); CU_ASSERT(flags & NGHTTP3_QPACK_DECODE_FLAG_EMIT); nghttp3_rcbuf_decref(qnv.name); nghttp3_rcbuf_decref(qnv.value); rbuf->pos += nread; } nghttp3_qpack_stream_context_free(&sctx); } void test_nghttp3_qpack_encoder_encode(void) { const nghttp3_mem *mem = nghttp3_mem_default(); nghttp3_qpack_encoder enc; nghttp3_qpack_decoder dec; nghttp3_nv nva[] = { MAKE_NV(":path", "/rsrc.php/v3/yn/r/rIPZ9Qkrdd9.png"), MAKE_NV(":authority", "static.xx.fbcdn.net"), MAKE_NV(":scheme", "https"), MAKE_NV(":method", "GET"), MAKE_NV("accept-encoding", "gzip, deflate, br"), MAKE_NV("accept-language", "en-US,en;q=0.9"), MAKE_NV( "user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36(KHTML, " "like Gecko) Chrome/63.0.3239.70 Safari/537.36"), MAKE_NV("accept", "image/webp,image/apng,image/*,*/*;q=0.8"), MAKE_NV("referer", "https://static.xx.fbcdn.net/rsrc.php/v3/yT/l/0,cross/" "dzXGESIlGQQ.css"), }; int rv; nghttp3_buf pbuf, rbuf, ebuf; nghttp3_qpack_stream *stream; nghttp3_qpack_header_block_ref *ref; nghttp3_buf_init(&pbuf); nghttp3_buf_init(&rbuf); nghttp3_buf_init(&ebuf); rv = nghttp3_qpack_encoder_init(&enc, 4096, mem); CU_ASSERT(0 == rv); nghttp3_qpack_encoder_set_max_blocked_streams(&enc, 1); nghttp3_qpack_encoder_set_max_dtable_capacity(&enc, 4096); rv = nghttp3_qpack_decoder_init(&dec, 4096, 1, mem); CU_ASSERT(0 == rv); rv = nghttp3_qpack_encoder_encode(&enc, &pbuf, &rbuf, &ebuf, 0, nva, nghttp3_arraylen(nva)); CU_ASSERT(0 == rv); stream = nghttp3_qpack_encoder_find_stream(&enc, 0); CU_ASSERT(NULL != stream); CU_ASSERT(nghttp3_qpack_encoder_stream_is_blocked(&enc, stream)); CU_ASSERT(1 == nghttp3_qpack_encoder_get_num_blocked_streams(&enc)); ref = *(nghttp3_qpack_header_block_ref **)nghttp3_ringbuf_get(&stream->refs, 0); CU_ASSERT(5 == ref->max_cnt); CU_ASSERT(1 == ref->min_cnt); CU_ASSERT(5 == nghttp3_qpack_stream_get_max_cnt(stream)); CU_ASSERT(1 == nghttp3_qpack_encoder_get_min_cnt(&enc)); CU_ASSERT(2 == nghttp3_buf_len(&pbuf)); check_decode_header(&dec, &pbuf, &rbuf, &ebuf, 0, nva, nghttp3_arraylen(nva), mem); rv = nghttp3_qpack_encoder_encode(&enc, &pbuf, &rbuf, &ebuf, 4, nva, nghttp3_arraylen(nva)); CU_ASSERT(0 == rv); stream = nghttp3_qpack_encoder_find_stream(&enc, 4); CU_ASSERT(NULL == stream); check_decode_header(&dec, &pbuf, &rbuf, &ebuf, 4, nva, nghttp3_arraylen(nva), mem); nghttp3_qpack_encoder_ack_header(&enc, 0); CU_ASSERT(5 == enc.krcnt); rv = nghttp3_qpack_encoder_encode(&enc, &pbuf, &rbuf, &ebuf, 8, nva, nghttp3_arraylen(nva)); CU_ASSERT(0 == rv); CU_ASSERT(0 == nghttp3_qpack_encoder_get_num_blocked_streams(&enc)); check_decode_header(&dec, &pbuf, &rbuf, &ebuf, 8, nva, nghttp3_arraylen(nva), mem); nghttp3_qpack_decoder_free(&dec); nghttp3_qpack_encoder_free(&enc); nghttp3_buf_free(&ebuf, mem); nghttp3_buf_free(&rbuf, mem); nghttp3_buf_free(&pbuf, mem); } void test_nghttp3_qpack_encoder_still_blocked(void) { const nghttp3_mem *mem = nghttp3_mem_default(); nghttp3_qpack_encoder enc; nghttp3_nv nva1[] = { MAKE_NV(":status", "103"), MAKE_NV("link", "foo"), }; nghttp3_nv nva2[] = { MAKE_NV(":status", "200"), MAKE_NV("content-type", "text/foo"), }; int rv; nghttp3_buf pbuf, rbuf, ebuf; nghttp3_qpack_stream *stream; nghttp3_qpack_header_block_ref *ref; nghttp3_buf_init(&pbuf); nghttp3_buf_init(&rbuf); nghttp3_buf_init(&ebuf); rv = nghttp3_qpack_encoder_init(&enc, 4096, mem); CU_ASSERT(0 == rv); nghttp3_qpack_encoder_set_max_blocked_streams(&enc, 1); nghttp3_qpack_encoder_set_max_dtable_capacity(&enc, 4096); rv = nghttp3_qpack_encoder_encode(&enc, &pbuf, &rbuf, &ebuf, 0, nva1, nghttp3_arraylen(nva1)); CU_ASSERT(0 == rv); CU_ASSERT(1 == nghttp3_qpack_encoder_get_num_blocked_streams(&enc)); rv = nghttp3_qpack_encoder_encode(&enc, &pbuf, &rbuf, &ebuf, 0, nva2, nghttp3_arraylen(nva2)); CU_ASSERT(0 == rv); CU_ASSERT(1 == nghttp3_qpack_encoder_get_num_blocked_streams(&enc)); stream = nghttp3_qpack_encoder_find_stream(&enc, 0); ref = *(nghttp3_qpack_header_block_ref **)nghttp3_ringbuf_get(&stream->refs, 0); CU_ASSERT(nghttp3_ringbuf_len(&stream->refs) > 1); CU_ASSERT(ref->max_cnt != nghttp3_qpack_stream_get_max_cnt(stream)); ref = *(nghttp3_qpack_header_block_ref **)nghttp3_ringbuf_get(&stream->refs, 1); CU_ASSERT(ref->max_cnt == nghttp3_qpack_stream_get_max_cnt(stream)); nghttp3_qpack_encoder_ack_header(&enc, 0); CU_ASSERT(1 == nghttp3_qpack_encoder_get_num_blocked_streams(&enc)); stream = nghttp3_qpack_encoder_find_stream(&enc, 0); ref = *(nghttp3_qpack_header_block_ref **)nghttp3_ringbuf_get(&stream->refs, 0); CU_ASSERT(1 == nghttp3_ringbuf_len(&stream->refs)); CU_ASSERT(ref->max_cnt == nghttp3_qpack_stream_get_max_cnt(stream)); nghttp3_qpack_encoder_ack_header(&enc, 0); CU_ASSERT(0 == nghttp3_qpack_encoder_get_num_blocked_streams(&enc)); CU_ASSERT(NULL == nghttp3_qpack_encoder_find_stream(&enc, 0)); nghttp3_qpack_encoder_free(&enc); nghttp3_buf_free(&ebuf, mem); nghttp3_buf_free(&rbuf, mem); nghttp3_buf_free(&pbuf, mem); } void test_nghttp3_qpack_encoder_set_dtable_cap(void) { const nghttp3_mem *mem = nghttp3_mem_default(); nghttp3_qpack_encoder enc; nghttp3_qpack_decoder dec; nghttp3_buf pbuf, rbuf, ebuf; const nghttp3_nv nva1[] = { MAKE_NV(":path", "/"), MAKE_NV("date", "bar1"), }; const nghttp3_nv nva2[] = { MAKE_NV(":path", "/"), MAKE_NV("vary", "bar2"), }; int rv; nghttp3_ssize nread; nghttp3_buf_init(&pbuf); nghttp3_buf_init(&rbuf); nghttp3_buf_init(&ebuf); rv = nghttp3_qpack_encoder_init(&enc, 4096, mem); CU_ASSERT(0 == rv); nghttp3_qpack_encoder_set_max_blocked_streams(&enc, 3); nghttp3_qpack_encoder_set_max_dtable_capacity(&enc, 4096); rv = nghttp3_qpack_decoder_init(&dec, 4096, 3, mem); CU_ASSERT(0 == rv); rv = nghttp3_qpack_encoder_encode(&enc, &pbuf, &rbuf, &ebuf, 0, nva1, nghttp3_arraylen(nva1)); CU_ASSERT(0 == rv); CU_ASSERT(1 == enc.ctx.next_absidx); CU_ASSERT(strlen("date") + strlen("bar1") + NGHTTP3_QPACK_ENTRY_OVERHEAD == enc.ctx.dtable_size); nread = nghttp3_qpack_decoder_read_encoder(&dec, ebuf.pos, nghttp3_buf_len(&ebuf)); CU_ASSERT(nread == (nghttp3_ssize)nghttp3_buf_len(&ebuf)); CU_ASSERT(1 == dec.ctx.next_absidx); CU_ASSERT(strlen("date") + strlen("bar1") + NGHTTP3_QPACK_ENTRY_OVERHEAD == dec.ctx.dtable_size); CU_ASSERT(4096 == dec.ctx.max_dtable_capacity); decode_header_block(&dec, &pbuf, &rbuf, 0, mem); nghttp3_buf_reset(&pbuf); nghttp3_buf_reset(&rbuf); nghttp3_buf_reset(&ebuf); rv = nghttp3_qpack_encoder_encode(&enc, &pbuf, &rbuf, &ebuf, 4, nva2, nghttp3_arraylen(nva2)); CU_ASSERT(0 == rv); CU_ASSERT(strlen("date") + strlen("bar1") + NGHTTP3_QPACK_ENTRY_OVERHEAD + strlen("vary") + strlen("bar2") + NGHTTP3_QPACK_ENTRY_OVERHEAD == enc.ctx.dtable_size); CU_ASSERT(2 == enc.ctx.next_absidx); CU_ASSERT(2 == nghttp3_qpack_encoder_get_num_blocked_streams(&enc)); nread = nghttp3_qpack_decoder_read_encoder(&dec, ebuf.pos, nghttp3_buf_len(&ebuf)); CU_ASSERT(nread == (nghttp3_ssize)nghttp3_buf_len(&ebuf)); CU_ASSERT(2 == dec.ctx.next_absidx); CU_ASSERT(strlen("date") + strlen("bar1") + NGHTTP3_QPACK_ENTRY_OVERHEAD + strlen("vary") + strlen("bar2") + NGHTTP3_QPACK_ENTRY_OVERHEAD == dec.ctx.dtable_size); CU_ASSERT(4096 == dec.ctx.max_dtable_capacity); decode_header_block(&dec, &pbuf, &rbuf, 4, mem); nghttp3_buf_reset(&pbuf); nghttp3_buf_reset(&rbuf); nghttp3_buf_reset(&ebuf); nghttp3_qpack_encoder_set_max_dtable_capacity(&enc, 0); CU_ASSERT(0 == enc.ctx.max_dtable_capacity); CU_ASSERT(2 == nghttp3_qpack_encoder_get_num_blocked_streams(&enc)); /* Cannot index more headers because we set max_dtable_capacity to 0. */ rv = nghttp3_qpack_encoder_encode(&enc, &pbuf, &rbuf, &ebuf, 8, nva2, nghttp3_arraylen(nva2)); CU_ASSERT(0 == rv); CU_ASSERT(0 == enc.ctx.max_dtable_capacity); CU_ASSERT(2 == enc.ctx.next_absidx); CU_ASSERT(2 == nghttp3_qpack_encoder_get_num_blocked_streams(&enc)); nread = nghttp3_qpack_decoder_read_encoder(&dec, ebuf.pos, nghttp3_buf_len(&ebuf)); CU_ASSERT(nread == (nghttp3_ssize)nghttp3_buf_len(&ebuf)); CU_ASSERT(2 == dec.ctx.next_absidx); CU_ASSERT(strlen("date") + strlen("bar1") + NGHTTP3_QPACK_ENTRY_OVERHEAD + strlen("vary") + strlen("bar2") + NGHTTP3_QPACK_ENTRY_OVERHEAD == dec.ctx.dtable_size); CU_ASSERT(4096 == dec.ctx.max_dtable_capacity); decode_header_block(&dec, &pbuf, &rbuf, 8, mem); nghttp3_buf_reset(&pbuf); nghttp3_buf_reset(&rbuf); nghttp3_buf_reset(&ebuf); /* Acking stream 0 will evict first entry */ nghttp3_qpack_encoder_ack_header(&enc, 0); rv = nghttp3_qpack_encoder_encode(&enc, &pbuf, &rbuf, &ebuf, 12, nva2, nghttp3_arraylen(nva2)); CU_ASSERT(0 == rv); CU_ASSERT(strlen("vary") + strlen("bar2") + NGHTTP3_QPACK_ENTRY_OVERHEAD == enc.ctx.dtable_size); CU_ASSERT(0 == enc.ctx.max_dtable_capacity); CU_ASSERT(1 == nghttp3_qpack_encoder_get_num_blocked_streams(&enc)); nread = nghttp3_qpack_decoder_read_encoder(&dec, ebuf.pos, nghttp3_buf_len(&ebuf)); CU_ASSERT(nread == (nghttp3_ssize)nghttp3_buf_len(&ebuf)); /* decoder still has 2 entries because encoder does not emit Set Dynamic Table Capacity. */ CU_ASSERT(2 == dec.ctx.next_absidx); CU_ASSERT(strlen("date") + strlen("bar1") + NGHTTP3_QPACK_ENTRY_OVERHEAD + strlen("vary") + strlen("bar2") + NGHTTP3_QPACK_ENTRY_OVERHEAD == dec.ctx.dtable_size); CU_ASSERT(4096 == dec.ctx.max_dtable_capacity); decode_header_block(&dec, &pbuf, &rbuf, 12, mem); nghttp3_buf_reset(&pbuf); nghttp3_buf_reset(&rbuf); nghttp3_buf_reset(&ebuf); /* Acking stream 4 will evict another entry */ nghttp3_qpack_encoder_ack_header(&enc, 4); rv = nghttp3_qpack_encoder_encode(&enc, &pbuf, &rbuf, &ebuf, 16, nva2, nghttp3_arraylen(nva2)); CU_ASSERT(0 == rv); CU_ASSERT(0 == enc.ctx.dtable_size); CU_ASSERT(0 == enc.ctx.max_dtable_capacity); CU_ASSERT(0 == enc.last_max_dtable_update); CU_ASSERT(SIZE_MAX == enc.min_dtable_update); CU_ASSERT(0 == nghttp3_qpack_encoder_get_num_blocked_streams(&enc)); nread = nghttp3_qpack_decoder_read_encoder(&dec, ebuf.pos, nghttp3_buf_len(&ebuf)); CU_ASSERT(nread == (nghttp3_ssize)nghttp3_buf_len(&ebuf)); CU_ASSERT(2 == dec.ctx.next_absidx); CU_ASSERT(0 == dec.ctx.dtable_size); CU_ASSERT(0 == dec.ctx.max_dtable_capacity); decode_header_block(&dec, &pbuf, &rbuf, 16, mem); nghttp3_buf_reset(&pbuf); nghttp3_buf_reset(&rbuf); nghttp3_buf_reset(&ebuf); nghttp3_qpack_decoder_free(&dec); nghttp3_qpack_encoder_free(&enc); /* Check that minimum size is emitted */ nghttp3_qpack_encoder_init(&enc, 4096, mem); nghttp3_qpack_encoder_set_max_blocked_streams(&enc, 1); nghttp3_qpack_encoder_set_max_dtable_capacity(&enc, 4096); nghttp3_qpack_decoder_init(&dec, 4096, 1, mem); rv = nghttp3_qpack_encoder_encode(&enc, &pbuf, &rbuf, &ebuf, 0, nva1, nghttp3_arraylen(nva1)); CU_ASSERT(0 == rv); CU_ASSERT(1 == enc.ctx.next_absidx); CU_ASSERT(strlen("date") + strlen("bar1") + NGHTTP3_QPACK_ENTRY_OVERHEAD == enc.ctx.dtable_size); nread = nghttp3_qpack_decoder_read_encoder(&dec, ebuf.pos, nghttp3_buf_len(&ebuf)); CU_ASSERT(nread == (nghttp3_ssize)nghttp3_buf_len(&ebuf)); CU_ASSERT(1 == dec.ctx.next_absidx); CU_ASSERT(strlen("date") + strlen("bar1") + NGHTTP3_QPACK_ENTRY_OVERHEAD == dec.ctx.dtable_size); CU_ASSERT(4096 == dec.ctx.max_dtable_capacity); decode_header_block(&dec, &pbuf, &rbuf, 0, mem); nghttp3_buf_reset(&pbuf); nghttp3_buf_reset(&rbuf); nghttp3_buf_reset(&ebuf); nghttp3_qpack_encoder_set_max_dtable_capacity(&enc, 0); nghttp3_qpack_encoder_set_max_dtable_capacity(&enc, 1024); CU_ASSERT(0 == enc.min_dtable_update); CU_ASSERT(1024 == enc.last_max_dtable_update); nghttp3_qpack_encoder_ack_header(&enc, 0); rv = nghttp3_qpack_encoder_encode(&enc, &pbuf, &rbuf, &ebuf, 4, nva1, nghttp3_arraylen(nva1)); CU_ASSERT(0 == rv); CU_ASSERT(2 == enc.ctx.next_absidx); CU_ASSERT(strlen("date") + strlen("bar1") + NGHTTP3_QPACK_ENTRY_OVERHEAD == enc.ctx.dtable_size); CU_ASSERT(SIZE_MAX == enc.min_dtable_update); CU_ASSERT(1024 == enc.last_max_dtable_update); CU_ASSERT(1024 == enc.ctx.max_dtable_capacity); nread = nghttp3_qpack_decoder_read_encoder(&dec, ebuf.pos, nghttp3_buf_len(&ebuf)); CU_ASSERT(nread == (nghttp3_ssize)nghttp3_buf_len(&ebuf)); CU_ASSERT(2 == dec.ctx.next_absidx); CU_ASSERT(strlen("date") + strlen("bar1") + NGHTTP3_QPACK_ENTRY_OVERHEAD == dec.ctx.dtable_size); CU_ASSERT(1024 == dec.ctx.max_dtable_capacity); decode_header_block(&dec, &pbuf, &rbuf, 4, mem); nghttp3_buf_reset(&pbuf); nghttp3_buf_reset(&rbuf); nghttp3_buf_reset(&ebuf); nghttp3_qpack_decoder_free(&dec); nghttp3_qpack_encoder_free(&enc); nghttp3_buf_free(&ebuf, mem); nghttp3_buf_free(&rbuf, mem); nghttp3_buf_free(&pbuf, mem); } void test_nghttp3_qpack_decoder_feedback(void) { const nghttp3_mem *mem = nghttp3_mem_default(); nghttp3_qpack_encoder enc; nghttp3_qpack_decoder dec; nghttp3_buf pbuf1, rbuf1, pbuf2, rbuf2, pbuf3, rbuf3, ebuf, dbuf; const nghttp3_nv nva1[] = { MAKE_NV(":path", "/"), MAKE_NV("date", "bar1"), }; const nghttp3_nv nva2[] = { MAKE_NV(":path", "/"), MAKE_NV("vary", "bar2"), }; const nghttp3_nv nva3[] = { MAKE_NV(":path", "/"), MAKE_NV("link", "bar3"), }; int rv; nghttp3_ssize nread; nghttp3_buf_init(&pbuf1); nghttp3_buf_init(&rbuf1); nghttp3_buf_init(&pbuf2); nghttp3_buf_init(&rbuf2); nghttp3_buf_init(&pbuf3); nghttp3_buf_init(&rbuf3); nghttp3_buf_init(&ebuf); nghttp3_buf_init(&dbuf); nghttp3_buf_reserve(&dbuf, 4096, mem); rv = nghttp3_qpack_encoder_init(&enc, 4096, mem); CU_ASSERT(0 == rv); nghttp3_qpack_encoder_set_max_blocked_streams(&enc, 2); nghttp3_qpack_encoder_set_max_dtable_capacity(&enc, 4096); rv = nghttp3_qpack_decoder_init(&dec, 4096, 2, mem); CU_ASSERT(0 == rv); rv = nghttp3_qpack_encoder_encode(&enc, &pbuf1, &rbuf1, &ebuf, 0, nva1, nghttp3_arraylen(nva1)); CU_ASSERT(0 == rv); CU_ASSERT(1 == nghttp3_qpack_encoder_get_num_blocked_streams(&enc)); rv = nghttp3_qpack_encoder_encode(&enc, &pbuf2, &rbuf2, &ebuf, 4, nva2, nghttp3_arraylen(nva2)); CU_ASSERT(0 == rv); CU_ASSERT(2 == nghttp3_qpack_encoder_get_num_blocked_streams(&enc)); nread = nghttp3_qpack_decoder_read_encoder(&dec, ebuf.pos, nghttp3_buf_len(&ebuf)); CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&ebuf) == nread); /* Process stream 4 first */ decode_header_block(&dec, &pbuf2, &rbuf2, 4, mem); CU_ASSERT(2 == dec.written_icnt); nghttp3_qpack_decoder_write_decoder(&dec, &dbuf); nread = nghttp3_qpack_encoder_read_decoder(&enc, dbuf.pos, nghttp3_buf_len(&dbuf)); CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&dbuf) == nread); /* This will unblock all streams because higher insert count is acknowledged. */ CU_ASSERT(0 == nghttp3_qpack_encoder_get_num_blocked_streams(&enc)); CU_ASSERT(1 == nghttp3_map_size(&enc.streams)); CU_ASSERT(1 == nghttp3_pq_size(&enc.min_cnts)); CU_ASSERT(0 == nghttp3_ksl_len(&enc.blocked_streams)); CU_ASSERT(2 == enc.krcnt); /* Process stream 0 */ decode_header_block(&dec, &pbuf1, &rbuf1, 0, mem); nghttp3_buf_reset(&dbuf); nghttp3_qpack_decoder_write_decoder(&dec, &dbuf); nread = nghttp3_qpack_encoder_read_decoder(&enc, dbuf.pos, nghttp3_buf_len(&dbuf)); CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&dbuf) == nread); CU_ASSERT(0 == nghttp3_map_size(&enc.streams)); CU_ASSERT(0 == nghttp3_pq_size(&enc.min_cnts)); CU_ASSERT(2 == enc.krcnt); /* Encode another header, and then read encoder stream only. Write decoder stream. */ nghttp3_buf_reset(&ebuf); rv = nghttp3_qpack_encoder_encode(&enc, &pbuf3, &rbuf3, &ebuf, 8, nva3, nghttp3_arraylen(nva3)); CU_ASSERT(0 == rv); CU_ASSERT(1 == nghttp3_qpack_encoder_get_num_blocked_streams(&enc)); nread = nghttp3_qpack_decoder_read_encoder(&dec, ebuf.pos, nghttp3_buf_len(&ebuf)); CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&ebuf) == nread); nghttp3_buf_reset(&dbuf); nghttp3_qpack_decoder_write_decoder(&dec, &dbuf); CU_ASSERT(nghttp3_buf_len(&dbuf) > 0); CU_ASSERT(3 == dec.written_icnt); nread = nghttp3_qpack_encoder_read_decoder(&enc, dbuf.pos, nghttp3_buf_len(&dbuf)); CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&dbuf) == nread); CU_ASSERT(0 == nghttp3_qpack_encoder_get_num_blocked_streams(&enc)); CU_ASSERT(1 == nghttp3_map_size(&enc.streams)); CU_ASSERT(3 == enc.krcnt); /* Cancel stream 8 */ rv = nghttp3_qpack_decoder_cancel_stream(&dec, 8); CU_ASSERT(0 == rv); nghttp3_buf_reset(&dbuf); nghttp3_qpack_decoder_write_decoder(&dec, &dbuf); CU_ASSERT(nghttp3_buf_len(&dbuf) > 0); nread = nghttp3_qpack_encoder_read_decoder(&enc, dbuf.pos, nghttp3_buf_len(&dbuf)); CU_ASSERT((nghttp3_ssize)nghttp3_buf_len(&dbuf) == nread); CU_ASSERT(0 == nghttp3_qpack_encoder_get_num_blocked_streams(&enc)); CU_ASSERT(0 == nghttp3_ksl_len(&enc.blocked_streams)); CU_ASSERT(0 == nghttp3_pq_size(&enc.min_cnts)); CU_ASSERT(0 == nghttp3_map_size(&enc.streams)); nghttp3_qpack_decoder_free(&dec); nghttp3_qpack_encoder_free(&enc); nghttp3_buf_free(&dbuf, mem); nghttp3_buf_free(&ebuf, mem); nghttp3_buf_free(&rbuf3, mem); nghttp3_buf_free(&pbuf3, mem); nghttp3_buf_free(&rbuf2, mem); nghttp3_buf_free(&pbuf2, mem); nghttp3_buf_free(&rbuf1, mem); nghttp3_buf_free(&pbuf1, mem); } void test_nghttp3_qpack_decoder_stream_overflow(void) { const nghttp3_mem *mem = nghttp3_mem_default(); nghttp3_qpack_decoder dec; size_t i; int rv; nghttp3_qpack_decoder_init(&dec, 4096, 0, mem); for (i = 0;; ++i) { rv = nghttp3_qpack_decoder_cancel_stream(&dec, (int64_t)i); if (rv == NGHTTP3_ERR_QPACK_FATAL) { break; } } nghttp3_qpack_decoder_free(&dec); } void test_nghttp3_qpack_huffman(void) { size_t i, j; uint8_t raw[100], ebuf[4096], dbuf[4096]; uint8_t *end; nghttp3_qpack_huffman_decode_context ctx; nghttp3_ssize nwrite; srand(1000000007); for (i = 0; i < 100000; ++i) { for (j = 0; j < sizeof(raw); ++j) { raw[j] = (uint8_t)round(((double)rand() / RAND_MAX * 255)); } end = nghttp3_qpack_huffman_encode(ebuf, raw, sizeof(raw)); nghttp3_qpack_huffman_decode_context_init(&ctx); nwrite = nghttp3_qpack_huffman_decode(&ctx, dbuf, ebuf, (size_t)(end - ebuf), 1); if (nwrite <= 0) { CU_ASSERT(nwrite > 0); continue; } CU_ASSERT((sizeof(raw) == (size_t)nwrite)); CU_ASSERT(0 == memcmp(raw, dbuf, sizeof(raw))); } } void test_nghttp3_qpack_huffman_decode_failure_state(void) { nghttp3_qpack_huffman_decode_context ctx; const uint8_t data[] = {0xff, 0xff, 0xff, 0xff}; uint8_t buf[4096]; nghttp3_ssize nwrite; nghttp3_qpack_huffman_decode_context_init(&ctx); nwrite = nghttp3_qpack_huffman_decode(&ctx, buf, data, sizeof(data) - 1, 0); CU_ASSERT(0 == nwrite); CU_ASSERT(!nghttp3_qpack_huffman_decode_failure_state(&ctx)); nwrite = nghttp3_qpack_huffman_decode(&ctx, buf, data, 1, 0); CU_ASSERT(0 == nwrite); CU_ASSERT(nghttp3_qpack_huffman_decode_failure_state(&ctx)); } void test_nghttp3_qpack_decoder_reconstruct_ricnt(void) { const nghttp3_mem *mem = nghttp3_mem_default(); nghttp3_qpack_decoder dec; uint64_t ricnt; int rv; rv = nghttp3_qpack_decoder_init(&dec, 100, 1, mem); CU_ASSERT(0 == rv); dec.ctx.next_absidx = 10; rv = nghttp3_qpack_decoder_reconstruct_ricnt(&dec, &ricnt, 3); CU_ASSERT(0 == rv); CU_ASSERT(8 == ricnt); nghttp3_qpack_decoder_free(&dec); }