### ----------------------------------------------------
### -- HTTP::HPACK
### -- Licenses: Artistic-2.0
### -- Authors: Jonathan Worthington
### -- File: t/examples.rakutest
### ----------------------------------------------------
### -- Chunk 1 of 2
use Test;
use HTTP::HPACK :internal;

# These tests are drawn from the examples section of the HPACK specification,
# located at https://tools.ietf.org/html/rfc7541#appendix-C

sub header($name, $value, $indexing = HTTP::HPACK::Indexing::Indexed) {
    HTTP::HPACK::Header.new(:$name, :$value, :$indexing)
}

# C.1.1.  Example 1: Encoding 10 Using a 5-Bit Prefix
{
    is-deeply encode-int(10, 5), Buf.new(0b00001010), 'encode 10 with 5-bit prefix';
    my int $offset = 0;
    is decode-int(Buf.new(0b11101010), 5, $offset), 10, 'decode 10 with 5-bit prefix';
    is $offset, 1, 'Consumed 1 byte';
}

# C.1.2.  Example 2: Encoding 1337 Using a 5-Bit Prefix
{
    is-deeply encode-int(1337, 5), Buf.new(0b00011111, 0b10011010, 0b00001010),
        'encode 1337 with 5-bit prefix';
    my int $offset = 0;
    is decode-int(Buf.new(0b11111111, 0b10011010, 0b00001010), 5, $offset), 1337,
        'decode 1337 with 5-bit prefix';
    is $offset, 3, 'Consumed 3 bytes';
}

# C.1.3.  Example 3: Encoding 42 Starting at an Octet Boundary
{
    is-deeply encode-int(42, 8), Buf.new(0b00101010), 'encode 42 with 8-bit prefix';
    my int $offset = 0;
    is decode-int(Buf.new(0b00101010), 8, $offset), 42, 'decode 42 with 8-bit prefix';
    is $offset, 1, 'Consumed 1 byte';
}

# C.2.1.  Literal Header Field with Indexing
is-deeply HTTP::HPACK::Decoder.new.decode-headers(
    Buf.new(0x40, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65,
            0x79, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x68, 0x65,
            0x61, 0x64, 0x65, 0x72)),
    [ header('custom-key', 'custom-header') ],
    'decode custom-key: custom-header';
is-deeply HTTP::HPACK::Encoder.new.encode-headers(
    [ header('custom-key', 'custom-header') ]),
    Buf.new(0x40, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65,
            0x79, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x68, 0x65,
            0x61, 0x64, 0x65, 0x72),
    'encode custom-key: custom-header (indexed)';

# C.2.2.  Literal Header Field without Indexing
is-deeply HTTP::HPACK::Decoder.new.decode-headers(
    Buf.new(0x04, 0x0c, 0x2f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2f, 0x70,
            0x61, 0x74, 0x68)),
    [ header(':path', '/sample/path', HTTP::HPACK::Indexing::NotIndexed) ],
    'decode :path: /sample/path';
is-deeply HTTP::HPACK::Encoder.new.encode-headers(
    [ header(':path', '/sample/path', HTTP::HPACK::Indexing::NotIndexed) ]),
    Buf.new(0x04, 0x0c, 0x2f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2f, 0x70,
            0x61, 0x74, 0x68),
    'encode :path: /sample/path (not indexed)';

# C.2.3.  Literal Header Field Never Indexed
is-deeply HTTP::HPACK::Decoder.new.decode-headers(
    Buf.new(0x10, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x06,
            0x73, 0x65, 0x63, 0x72, 0x65, 0x74 )),
    [ header('password', 'secret', HTTP::HPACK::Indexing::NeverIndexed) ],
    'decode password: secret';
is-deeply HTTP::HPACK::Encoder.new.encode-headers(
    [ header('password', 'secret', HTTP::HPACK::Indexing::NeverIndexed) ]),
    Buf.new(0x10, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x06,
            0x73, 0x65, 0x63, 0x72, 0x65, 0x74 ),
    'encode password: secret (never indexed)';

# C.2.4.  Indexed Header Field
is-deeply HTTP::HPACK::Decoder.new.decode-headers(Buf.new(0x82)),
    [ header(':method', 'GET') ],
    'decode :method: GET';
is-deeply HTTP::HPACK::Encoder.new.encode-headers([ header(':method', 'GET') ]),
    Buf.new(0x82),
    'encode :method: GET (indexed)';

# C.3.  Request Examples without Huffman Coding
{
    my $decoder = HTTP::HPACK::Decoder.new;
    is-deeply $decoder.decode-headers(
        Buf.new(0x82, 0x86, 0x84, 0x41, 0x0f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x78,
                0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d)),
        [ header(':method', 'GET'), header(':scheme', 'http'),
          header(':path', '/'), header(':authority', 'www.example.com') ],
        'decoded first request header example';
    is $decoder.dynamic-table-size, 57, 'correct dynamic table size';
    is-deeply $decoder.decode-headers(
        Buf.new(0x82, 0x86, 0x84, 0xbe, 0x58, 0x08, 0x6e, 0x6f, 0x2d, 0x63, 0x61,
                0x63, 0x68, 0x65)),
        [ header(':method', 'GET'), header(':scheme', 'http'),
          header(':path', '/'), header(':authority', 'www.example.com'),
          header('cache-control', 'no-cache') ],
        'decoded second request header example';
    is $decoder.dynamic-table-size, 110, 'correct dynamic table size';
    is-deeply $decoder.decode-headers(
        Buf.new(0x82, 0x87, 0x85, 0xbf, 0x40, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f,
                0x6d, 0x2d, 0x6b, 0x65, 0x79, 0x0c, 0x63, 0x75, 0x73, 0x74, 0x6f,
                0x6d, 0x2d, 0x76, 0x61, 0x6c, 0x75, 0x65)),
        [ header(':method', 'GET'), header(':scheme', 'https'),
          header(':path', '/index.html'), header(':authority', 'www.example.com'),
          header('custom-key', 'custom-value') ],
        'decoded third request header example';
    is $decoder.dynamic-table-size, 164, 'correct dynamic table size';

    my $encoder = HTTP::HPACK::Encoder.new;
    is-deeply $encoder.encode-headers(
        [ header(':method', 'GET'), header(':scheme', 'http'),
          header(':path', '/'), header(':authority', 'www.example.com') ]),
        Buf.new(0x82, 0x86, 0x84, 0x41, 0x0f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x78,
                0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d),
        'encoded first request header example';
    is-deeply $encoder.encode-headers(
        [ header(':method', 'GET'), header(':scheme', 'http'),
          header(':path', '/'), header(':authority', 'www.example.com'),
          header('cache-control', 'no-cache') ]),
        Buf.new(0x82, 0x86, 0x84, 0xbe, 0x58, 0x08, 0x6e, 0x6f, 0x2d, 0x63, 0x61,
                0x63, 0x68, 0x65),
        'encoded second request header example';
    is-deeply $encoder.encode-headers(
        [ header(':method', 'GET'), header(':scheme', 'https'),
          header(':path', '/index.html'), header(':authority', 'www.example.com'),
          header('custom-key', 'custom-value') ]),
        Buf.new(0x82, 0x87, 0x85, 0xbf, 0x40, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f,
                0x6d, 0x2d, 0x6b, 0x65, 0x79, 0x0c, 0x63, 0x75, 0x73, 0x74, 0x6f,
                0x6d, 0x2d, 0x76, 0x61, 0x6c, 0x75, 0x65),
        'encoded third request header example';
}

# C.4.  Request Examples with Huffman Coding
{
    my $decoder = HTTP::HPACK::Decoder.new;
    is-deeply $decoder.decode-headers(
        Buf.new(0x82, 0x86, 0x84, 0x41, 0x8c, 0xf1, 0xe3, 0xc2, 0xe5, 0xf2, 0x3a,
                0x6b, 0xa0, 0xab, 0x90, 0xf4, 0xff)),
        [ header(':method', 'GET'), header(':scheme', 'http'),
          header(':path', '/'), header(':authority', 'www.example.com') ],
        'decoded first request header example (huffman coded)';
    is $decoder.dynamic-table-size, 57, 'correct dynamic table size';
    is-deeply $decoder.decode-headers(
        Buf.new(0x82, 0x86, 0x84, 0xbe, 0x58, 0x86, 0xa8, 0xeb, 0x10, 0x64, 0x9c,
                0xbf)),
        [ header(':method', 'GET'), header(':scheme', 'http'),
          header(':path', '/'), header(':authority', 'www.example.com'),
          header('cache-control', 'no-cache') ],
        'decoded second request header example (huffman coded)';
    is $decoder.dynamic-table-size, 110, 'correct dynamic table size';
    is-deeply $decoder.decode-headers(
        Buf.new(0x82, 0x87, 0x85, 0xbf, 0x40, 0x88, 0x25, 0xa8, 0x49, 0xe9, 0x5b,
                0xa9, 0x7d, 0x7f, 0x89, 0x25, 0xa8, 0x49, 0xe9, 0x5b, 0xb8, 0xe8,
                0xb4, 0xbf)),
        [ header(':method', 'GET'), header(':scheme', 'https'),
          header(':path', '/index.html'), header(':authority', 'www.example.com'),
          header('custom-key', 'custom-value') ],
        'decoded third request header example (huffman coded)';
    is $decoder.dynamic-table-size, 164, 'correct dynamic table size';

    my $encoder = HTTP::HPACK::Encoder.new(:huffman);
    is-deeply $encoder.encode-headers(
        [ header(':method', 'GET'), header(':scheme', 'http'),
          header(':path', '/'), header(':authority', 'www.example.com') ]),
        Buf.new(0x82, 0x86, 0x84, 0x41, 0x8c, 0xf1, 0xe3, 0xc2, 0xe5, 0xf2, 0x3a,
                0x6b, 0xa0, 0xab, 0x90, 0xf4, 0xff),
        'encoded first request header example (huffman coded)';
    is-deeply $encoder.encode-headers(
        [ header(':method', 'GET'), header(':scheme', 'http'),
          header(':path', '/'), header(':authority', 'www.example.com'),
          header('cache-control', 'no-cache') ]),
        Buf.new(0x82, 0x86, 0x84, 0xbe, 0x58, 0x86, 0xa8, 0xeb, 0x10, 0x64, 0x9c,
                0xbf),
        'encoded second request header example (huffman coded)';
    is-deeply $encoder.encode-headers(
        [ header(':method', 'GET'), header(':scheme', 'https'),
          header(':path', '/index.html'), header(':authority', 'www.example.com'),
          header('custom-key', 'custom-value') ]),
        Buf.new(0x82, 0x87, 0x85, 0xbf, 0x40, 0x88, 0x25, 0xa8, 0x49, 0xe9, 0x5b,
                0xa9, 0x7d, 0x7f, 0x89, 0x25, 0xa8, 0x49, 0xe9, 0x5b, 0xb8, 0xe8,
                0xb4, 0xbf),
        'encoded third request header example (huffman coded)';
}

# C.5.  Response Examples without Huffman Coding
{
    my $decoder = HTTP::HPACK::Decoder.new(dynamic-table-limit => 256);
    is-deeply $decoder.decode-headers(
        Buf.new(0x48, 0x03, 0x33, 0x30, 0x32, 0x58, 0x07, 0x70, 0x72, 0x69,
                0x76, 0x61, 0x74, 0x65, 0x61, 0x1d, 0x4d, 0x6f, 0x6e, 0x2c,
                0x20, 0x32, 0x31, 0x20, 0x4f, 0x63, 0x74, 0x20, 0x32, 0x30,
                0x31, 0x33, 0x20, 0x32, 0x30, 0x3a, 0x31, 0x33, 0x3a, 0x32,
                0x31, 0x20, 0x47, 0x4d, 0x54, 0x6e, 0x17, 0x68, 0x74, 0x74,
                0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x65,
                0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d)),
        [ header(':status', '302'), header('cache-control', 'private'),
          header('date', 'Mon, 21 Oct 2013 20:13:21 GMT'),
          header('location', 'https://www.example.com') ],
        'decoded first response header example';
    is $decoder.dynamic-table-size, 222, 'correct dynamic table size';
    is-deeply $decoder.decode-headers(
        Buf.new(0x48, 0x03, 0x33, 0x30, 0x37, 0xc1, 0xc0, 0xbf)),
        [ header(':status', '307'), header('cache-control', 'private'),
          header('date', 'Mon, 21 Oct 2013 20:13:21 GMT'),
          header('location', 'https://www.example.com') ],
        'decoded second response header example';
    is $decoder.dynamic-table-size, 222, 'correct dynamic table size';
    is-deeply $decoder.decode-headers(
        Buf.new(0x88, 0xc1, 0x61, 0x1d, 0x4d, 0x6f, 0x6e, 0x2c, 0x20, 0x32,
                0x31, 0x20, 0x4f, 0x63, 0x74, 0x20, 0x32, 0x30, 0x31, 0x33,
                0x20, 0x32, 0x30, 0x3a, 0x31, 0x33, 0x3a, 0x32, 0x32, 0x20,
                0x47, 0x4d, 0x54, 0xc0, 0x5a, 0x04, 0x67, 0x7a, 0x69, 0x70,
                0x77, 0x38, 0x66, 0x6f, 0x6f, 0x3d, 0x41, 0x53, 0x44, 0x4a,
                0x4b, 0x48, 0x51, 0x4b, 0x42, 0x5a, 0x58, 0x4f, 0x51, 0x57,
                0x45, 0x4f, 0x50, 0x49, 0x55, 0x41, 0x58, 0x51, 0x57, 0x45,
                0x4f, 0x49, 0x55, 0x3b, 0x20, 0x6d, 0x61, 0x78, 0x2d, 0x61,
                0x67, 0x65, 0x3d, 0x33, 0x36, 0x30, 0x30, 0x3b, 0x20, 0x76,
                0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31)),
        [ header(':status', '200'), header('cache-control', 'private'),
          header('date', 'Mon, 21 Oct 2013 20:13:22 GMT'),
          header('location', 'https://www.example.com'),
          header('content-encoding', 'gzip'),
          header('set-cookie', 'foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1') ],
        'decoded third response header example';
    is $decoder.dynamic-table-size, 215, 'correct dynamic table size';

    my $encoder = HTTP::HPACK::Encoder.new(dynamic-table-limit => 256);
    is-deeply $encoder.encode-headers(
        [ header(':status', '302'), header('cache-control', 'private'),
          header('date', 'Mon, 21 Oct 2013 20:13:21 GMT'),
          header('location', 'https://www.example.com') ]),
        Buf.new(0x48, 0x03, 0x33, 0x30, 0x32, 0x58, 0x07, 0x70, 0x72, 0x69,
                0x76, 0x61, 0x74, 0x65, 0x61, 0x1d, 0x4d, 0x6f, 0x6e, 0x2c,
                0x20, 0x32, 0x31, 0x20, 0x4f, 0x63, 0x74, 0x20, 0x32, 0x30,
                0x31, 0x33, 0x20, 0x32, 0x30, 0x3a, 0x31, 0x33, 0x3a, 0x32,
                0x31, 0x20, 0x47, 0x4d, 0x54, 0x6e, 0x17, 0x68, 0x74, 0x74,
                0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x65,
                0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d),
        'encoded first response header example';
    is-deeply $encoder.encode-headers(
        [ header(':status', '307'), header('cache-control', 'private'),
          header('date', 'Mon, 21 Oct 2013 20:13:21 GMT'),
          header('location', 'https://www.example.com') ]),
        Buf.new(0x48, 0x03, 0x33, 0x30, 0x37, 0xc1, 0xc0, 0xbf),
        'encoded second response header example';
    is-deeply $encoder.encode-headers(
        [ header(':status', '200'), header('cache-control', 'private'),
          header('date', 'Mon, 21 Oct 2013 20:13:22 GMT'),
          header('location', 'https://www.example.com'),
          header('content-encoding', 'gzip'),
          header('set-cookie', 'foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1') ]),
        Buf.new(0x88, 0xc1, 0x61, 0x1d, 0x4d, 0x6f, 0x6e, 0x2c, 0x20, 0x32,
                0x31, 0x20, 0x4f, 0x63, 0x74, 0x20, 0x32, 0x30, 0x31, 0x33,
                0x20, 0x32, 0x30, 0x3a, 0x31, 0x33, 0x3a, 0x32, 0x32, 0x20,
                0x47, 0x4d, 0x54, 0xc0, 0x5a, 0x04, 0x67, 0x7a, 0x69, 0x70,
                0x77, 0x38, 0x66, 0x6f, 0x6f, 0x3d, 0x41, 0x53, 0x44, 0x4a,
                0x4b, 0x48, 0x51, 0x4b, 0x42, 0x5a, 0x58, 0x4f, 0x51, 0x57,
                0x45, 0x4f, 0x50, 0x49, 0x55, 0x41, 0x58, 0x51, 0x57, 0x45,
                0x4f, 0x49, 0x55, 0x3b, 0x20, 0x6d, 0x61, 0x78, 0x2d, 0x61,
                0x67, 0x65, 0x3d, 0x33, 0x36, 0x30, 0x30, 0x3b, 0x20, 0x76,
                0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31),
        'encoded third response header example';
}

# C.6.  Response Examples with Huffman Coding
{
    my $decoder = HTTP::HPACK::Decoder.new(dynamic-table-limit => 256);
    is-deeply $decoder.decode-headers(
        Buf.new(0x48, 0x82, 0x64, 0x02, 0x58, 0x85, 0xae, 0xc3, 0x77, 0x1a,
                0x4b, 0x61, 0x96, 0xd0, 0x7a, 0xbe, 0x94, 0x10, 0x54, 0xd4,
                0x44, 0xa8, 0x20, 0x05, 0x95, 0x04, 0x0b, 0x81, 0x66, 0xe0,
                0x82, 0xa6, 0x2d, 0x1b, 0xff, 0x6e, 0x91, 0x9d, 0x29, 0xad,
                0x17, 0x18, 0x63, 0xc7, 0x8f, 0x0b, 0x97, 0xc8, 0xe9, 0xae,
                0x82, 0xae, 0x43, 0xd3)),
        [ header(':status', '302'), header('cache-control', 'private'),
          header('date', 'Mon, 21 Oct 2013 20:13:21 GMT'),
          header('location', 'https://www.example.com') ],
        'decoded first response header example (huffman coded)';
    is $decoder.dynamic-table-size, 222, 'correct dynamic table size';
    is-deeply $decoder.decode-headers(
        Buf.new(0x48, 0x83, 0x64, 0x0e, 0xff, 0xc1, 0xc0, 0xbf)),
        [ header(':status', '307'), header('cache-control', 'private'),
          header('date', 'Mon, 21 Oct 2013 20:13:21 GMT'),
          header('location', 'https://www.example.com') ],
        'decoded second response header example (huffman coded)';
    is $decoder.dynamic-table-size, 222, 'correct dynamic table size';
    is-deeply $decoder.decode-headers(
        Buf.new(0x88, 0xc1, 0x61, 0x96, 0xd0, 0x7a, 0xbe, 0x94, 0x10, 0x54,
                0xd4, 0x44, 0xa8, 0x20, 0x05, 0x95, 0x04, 0x0b, 0x81, 0x66,
                0xe0, 0x84, 0xa6, 0x2d, 0x1b, 0xff, 0xc0, 0x5a, 0x83, 0x9b,
                0xd9, 0xab, 0x77, 0xad, 0x94, 0xe7, 0x82, 0x1d, 0xd7, 0xf2,
                0xe6, 0xc7, 0xb3, 0x35, 0xdf, 0xdf, 0xcd, 0x5b, 0x39, 0x60,
                0xd5, 0xaf, 0x27, 0x08, 0x7f, 0x36, 0x72, 0xc1, 0xab, 0x27,
                0x0f, 0xb5, 0x29, 0x1f, 0x95, 0x87, 0x31, 0x60, 0x65, 0xc0,
                0x03, 0xed, 0x4e, 0xe5, 0xb1, 0x06, 0x3d, 0x50, 0x07)),
        [ header(':status', '200'), header('cache-control', 'private'),
          header('date', 'Mon, 21 Oct 2013 20:13:22 GMT'),
          header('location', 'https://www.example.com'),
          header('content-encoding', 'gzip'),
          header('set-cookie', 'foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1') ],
        'decoded third response header example (huffman coded)';
    is $decoder.dynamic-table-size, 215, 'correct dynamic table size';

    my $encoder = HTTP::HPACK::Encoder.new(:huffman, dynamic-table-limit => 256);
    is-deeply $encoder.encode-headers(
        [ header(':status', '302'), header('cache-control', 'private'),
          header('date', 'Mon, 21 Oct 2013 20:13:21 GMT'),
          header('location', 'https://www.example.com') ]),
        Buf.new(0x48, 0x82, 0x64, 0x02, 0x58, 0x85, 0xae, 0xc3, 0x77, 0x1a,
                0x