% HTTP/2 Campaign
# Frames expressed as binary str were generated using the solicit and hpack-rs
# Rust crates (https://github.com/mlalic/solicit, https://github.com/mlalic/hpack-rs)
# except Continuation Frames, Priority Frames and Push Promise Frames that we generated
# using Go x/net/http2 and x/net/http2/hpack modules.

+ Syntax check
= Configuring Scapy
~ http2 frame hpack build dissect data headers priority settings rststream pushpromise ping goaway winupdate continuation hpackhdrtable helpers

import scapy.config
scapy.config.conf.debug_dissector=True
import scapy.packet
import scapy.fields
import scapy.contrib.http2 as h2
import re
flags_bit_pattern = re.compile(r'''^\s+flags\s+=\s+\[.*['"]bit [0-9]+['"].*\]$''', re.M)
def expect_exception(e, c):
    try:
        eval(c)
        return False
    except e:
        return True

+ HTTP/2 UVarIntField Test Suite

= HTTP/2 UVarIntField.any2i
~ http2 frame field uvarintfield

f = h2.UVarIntField('value', 0, 5)
expect_exception(AssertionError, 'f.any2i(None, None)')
assert(f.any2i(None, 0) == 0)
assert(f.any2i(None, 3) == 3)
assert(f.any2i(None, 1<<5) == 1<<5)
assert(f.any2i(None, 1<<16) == 1<<16)
f = h2.UVarIntField('value', 0, 8)
assert(f.any2i(None, b'\x1E') == 30)

= HTTP/2 UVarIntField.m2i on full byte
~ http2 frame field uvarintfield

f = h2.UVarIntField('value', 0, 8)
assert(f.m2i(None, b'\x00') == 0)
assert(f.m2i(None, b'\x03') == 3)
assert(f.m2i(None, b'\xFE') == 254)
assert(f.m2i(None, b'\xFF\x00') == 255)
assert(f.m2i(None, b'\xFF\xFF\x03') == 766) #0xFF + (0xFF ^ 0x80) + (3<<7)

= HTTP/2 UVarIntField.m2i on partial byte
~ http2 frame field uvarintfield

f = h2.UVarIntField('value', 0, 5)
assert(f.m2i(None, (b'\x00', 3)) == 0)
assert(f.m2i(None, (b'\x03', 3)) == 3)
assert(f.m2i(None, (b'\x1e', 3)) == 30)
assert(f.m2i(None, (b'\x1f\x00', 3)) == 31)
assert(f.m2i(None, (b'\x1f\xe1\xff\x03', 3)) == 65536)

= HTTP/2 UVarIntField.getfield on full byte
~ http2 frame field uvarintfield

f = h2.UVarIntField('value', 0, 8)

r = f.getfield(None, b'\x00\x00')
assert(r[0] == b'\x00')
assert(r[1] == 0)

r = f.getfield(None, b'\x03\x00')
assert(r[0] == b'\x00')
assert(r[1] == 3)

r = f.getfield(None, b'\xFE\x00')
assert(r[0] == b'\x00')
assert(r[1] == 254)

r = f.getfield(None, b'\xFF\x00\x00')
assert(r[0] == b'\x00')
assert(r[1] == 255)

r = f.getfield(None, b'\xFF\xFF\x03\x00')
assert(r[0] == b'\x00')
assert(r[1] == 766)

= HTTP/2 UVarIntField.getfield on partial byte
~ http2 frame field uvarintfield

f = h2.UVarIntField('value', 0, 5)

r = f.getfield(None, (b'\x00\x00', 3))
assert(r[0] == b'\x00')
assert(r[1] == 0)

r = f.getfield(None, (b'\x03\x00', 3))
assert(r[0] == b'\x00')
assert(r[1] == 3)

r = f.getfield(None, (b'\x1e\x00', 3))
assert(r[0] == b'\x00')
assert(r[1] == 30)

r = f.getfield(None, (b'\x1f\x00\x00', 3))
assert(r[0] == b'\x00')
assert(r[1] == 31)

r = f.getfield(None, (b'\x1f\xe1\xff\x03\x00', 3))
assert(r[0] == b'\x00')
assert(r[1] == 65536)

= HTTP/2 UVarIntField.i2m on full byte
~ http2 frame field uvarintfield

f = h2.UVarIntField('value', 0, 8)
assert(f.i2m(None, 0) == b'\x00')
assert(f.i2m(None, 3) == b'\x03')
assert(f.i2m(None, 254).lower() == b'\xfe')
assert(f.i2m(None, 255).lower() == b'\xff\x00')
assert(f.i2m(None, 766).lower() == b'\xff\xff\x03')

= HTTP/2 UVarIntField.i2m on partial byte
~ http2 frame field uvarintfield

f = h2.UVarIntField('value', 0, 5)
assert(f.i2m(None, 0) == b'\x00')
assert(f.i2m(None, 3) == b'\x03')
assert(f.i2m(None, 30).lower() == b'\x1e')
assert(f.i2m(None, 31).lower() == b'\x1f\x00')
assert(f.i2m(None, 65536).lower() == b'\x1f\xe1\xff\x03')

= HTTP/2 UVarIntField.addfield on full byte
~ http2 frame field uvarintfield

f = h2.UVarIntField('value', 0, 8)

assert(f.addfield(None, b'Toto', 0) == b'Toto\x00')
assert(f.addfield(None, b'Toto', 3) == b'Toto\x03')
assert(f.addfield(None, b'Toto', 254).lower() == b'toto\xfe')
assert(f.addfield(None, b'Toto', 255).lower() == b'toto\xff\x00')
assert(f.addfield(None, b'Toto', 766).lower() == b'toto\xff\xff\x03')

= HTTP/2 UVarIntField.addfield on partial byte
~ http2 frame field uvarintfield

f = h2.UVarIntField('value', 0, 5)

assert(f.addfield(None, (b'Toto', 3, 4), 0) == b'Toto\x80')
assert(f.addfield(None, (b'Toto', 3, 4), 3) == b'Toto\x83')
assert(f.addfield(None, (b'Toto', 3, 4), 30).lower() == b'toto\x9e')
assert(f.addfield(None, (b'Toto', 3, 4), 31).lower() == b'toto\x9f\x00')
assert(f.addfield(None, (b'Toto', 3, 4), 65536).lower() == b'toto\x9f\xe1\xff\x03')

= HTTP/2 UVarIntField.i2len on full byte
~ http2 frame field uvarintfield

f = h2.UVarIntField('value', 0, 8)

assert(f.i2len(None, 0) == 1)
assert(f.i2len(None, 3) == 1)
assert(f.i2len(None, 254) == 1)
assert(f.i2len(None, 255) == 2)
assert(f.i2len(None, 766) == 3)

= HTTP/2 UVarIntField.i2len on partial byte
~ http2 frame field uvarintfield

f = h2.UVarIntField('value', 0, 5)

assert(f.i2len(None, 0) == 1)
assert(f.i2len(None, 3) == 1)
assert(f.i2len(None, 30) == 1)
assert(f.i2len(None, 31) == 2)
assert(f.i2len(None, 65536) == 4)

+ HTTP/2 FieldUVarLenField Test Suite

= HTTP/2 FieldUVarLenField.i2m without adjustment
~ http2 frame field fielduvarlenfield


f = h2.FieldUVarLenField('len', None, 8, length_of='data')
class TrivialPacket(Packet):
    name = 'Trivial Packet'
    fields_desc= [
        f,
        StrField('data', '')
    ]

assert(f.i2m(TrivialPacket(data='a'*5), None) == b'\x05')
assert(f.i2m(TrivialPacket(data='a'*255), None).lower() == b'\xff\x00')
assert(f.i2m(TrivialPacket(data='a'), 2) == b'\x02')
assert(f.i2m(None, 2) == b'\x02')
assert(f.i2m(None, 0) == b'\x00')

= HTTP/2 FieldUVarLenField.i2m with adjustment
~ http2 frame field fielduvarlenfield

class TrivialPacket(Packet):
    name = 'Trivial Packet'
    fields_desc= [
        f,
        StrField('data', '')
    ]

f = h2.FieldUVarLenField('value', None, 8, length_of='data', adjust=lambda x: x-1)
assert(f.i2m(TrivialPacket(data='a'*5), None) == b'\x04')
assert(f.i2m(TrivialPacket(data='a'*255), None).lower() == b'\xfe')
#Adjustment does not affect non-None value!
assert(f.i2m(TrivialPacket(data='a'*3), 2) == b'\x02')

+ HTTP/2 HPackZString Test Suite

= HTTP/2 HPackZString Compression
~ http2 hpack huffman

string = 'Test'
s = h2.HPackZString(string)
assert(len(s) == 3)
assert(raw(s) == b"\xdeT'")
assert(s.origin() == string)

string = 'a'*65535
s = h2.HPackZString(string)
assert(len(s) == 40960)
assert(raw(s) == (b'\x18\xc61\x8cc' * 8191) + b'\x18\xc61\x8c\x7f')
assert(s.origin() == string)

= HTTP/2 HPackZString Decompression
~ http2 hpack huffman

s = b"\xdeT'"
i, ibl = h2.HPackZString.huffman_conv2bitstring(s)
assert(b'Test' == h2.HPackZString.huffman_decode(i, ibl))

s = (b'\x18\xc61\x8cc' * 8191) + b'\x18\xc61\x8c\x7f'
i, ibl = h2.HPackZString.huffman_conv2bitstring(s)
assert(b'a'*65535 == h2.HPackZString.huffman_decode(i, ibl))

assert(
    expect_exception(h2.InvalidEncodingException,
    'h2.HPackZString.huffman_decode(*h2.HPackZString.huffman_conv2bitstring(b"\\xdeT"))')
)

+ HTTP/2 HPackStrLenField Test Suite

= HTTP/2 HPackStrLenField.m2i
~ http2 hpack field hpackstrlenfield

f = h2.HPackStrLenField('data', h2.HPackLiteralString(''), length_from=lambda p: p.len, type_from='type')
class TrivialPacket(Packet):
    name = 'Trivial Packet'
    fields_desc = [
        IntField('type', None),
        IntField('len', None),
        f
    ]

s = f.m2i(TrivialPacket(type=0, len=4), b'Test')
assert(isinstance(s, h2.HPackLiteralString))
assert(s.origin() == 'Test')

s = f.m2i(TrivialPacket(type=1, len=3), b"\xdeT'")
assert(isinstance(s, h2.HPackZString))
assert(s.origin() == 'Test')

= HTTP/2 HPackStrLenField.any2i
~ http2 hpack field hpackstrlenfield

f = h2.HPackStrLenField('data', h2.HPackLiteralString(''), length_from=lambda p: p.len, type_from='type')
class TrivialPacket(Packet):
    name = 'Trivial Packet'
    fields_desc = [
        IntField('type', None),
        IntField('len', None),
        f
    ]

s = f.any2i(TrivialPacket(type=0, len=4), b'Test')
assert(isinstance(s, h2.HPackLiteralString))
assert(s.origin() == 'Test')

s = f.any2i(TrivialPacket(type=1, len=3), b"\xdeT'")
assert(isinstance(s, h2.HPackZString))
assert(s.origin() == 'Test')

s = h2.HPackLiteralString('Test')
s2 = f.any2i(TrivialPacket(type=0, len=4), s)
assert(s.origin() == s2.origin())

s = h2.HPackZString('Test')
s2 = f.any2i(TrivialPacket(type=1, len=3), s)
assert(s.origin() == s2.origin())

s = h2.HPackLiteralString('Test')
s2 = f.any2i(None, s)
assert(s.origin() == s2.origin())

s = h2.HPackZString('Test')
s2 = f.any2i(None, s)
assert(s.origin() == s2.origin())

# Verifies that one can fuzz
s = h2.HPackLiteralString('Test')
s2 = f.any2i(TrivialPacket(type=1, len=1), s)
assert(s.origin() == s2.origin())

= HTTP/2 HPackStrLenField.i2m
~ http2 hpack field hpackstrlenfield

f = h2.HPackStrLenField('data', h2.HPackLiteralString(''), length_from=lambda p: p.len, type_from='type')

s = b'Test'
s2 = f.i2m(None, h2.HPackLiteralString(s))
assert(s == s2)

s = b'Test'
s2 = f.i2m(None, h2.HPackZString(s))
assert(s2 == b"\xdeT'")

= HTTP/2 HPackStrLenField.addfield
~ http2 hpack field hpackstrlenfield

f = h2.HPackStrLenField('data', h2.HPackLiteralString(''), length_from=lambda p: p.len, type_from='type')

s = b'Test'
s2 = f.addfield(None, b'Toto', h2.HPackLiteralString(s))
assert(b'Toto' + s == s2)

s = b'Test'
s2 = f.addfield(None, b'Toto', h2.HPackZString(s))
assert(s2 == b"Toto\xdeT'")

= HTTP/2 HPackStrLenField.getfield
~ http2 hpack field hpackstrlenfield

f = h2.HPackStrLenField('data', h2.HPackLiteralString(''), length_from=lambda p: p.len, type_from='type')
class TrivialPacket(Packet):
    name = 'Trivial Packet'
    fields_desc = [
        IntField('type', None),
        IntField('len', None),
        f
    ]

r = f.getfield(TrivialPacket(type=0, len=4), b'TestToto')
assert(isinstance(r, tuple))
assert(r[0] == b'Toto')
assert(isinstance(r[1], h2.HPackLiteralString))
assert(r[1].origin() == 'Test')

r = f.getfield(TrivialPacket(type=1, len=3), b"\xdeT'Toto")
assert(isinstance(r, tuple))
assert(r[0] == b'Toto')
assert(isinstance(r[1], h2.HPackZString))
assert(r[1].origin() == 'Test')

= HTTP/2 HPackStrLenField.i2h / i2repr
~ http2 hpack field hpackstrlenfield

f = h2.HPackStrLenField('data', h2.HPackLiteralString(''), length_from=lambda p: p.len, type_from='type')

s = b'Test'
assert(f.i2h(None, h2.HPackLiteralString(s)) == 'HPackLiteralString(Test)')
assert(f.i2repr(None, h2.HPackLiteralString(s)) == repr('HPackLiteralString(Test)'))

assert(f.i2h(None, h2.HPackZString(s)) == 'HPackZString(Test)')
assert(f.i2repr(None, h2.HPackZString(s)) == repr('HPackZString(Test)'))

= HTTP/2 HPackStrLenField.i2len
~ http2 hpack field hpackstrlenfield

f = h2.HPackStrLenField('data', h2.HPackLiteralString(''), length_from=lambda p: p.len, type_from='type')

s = b'Test'
assert(f.i2len(None, h2.HPackLiteralString(s)) == 4)
assert(f.i2len(None, h2.HPackZString(s)) == 3)

+ HTTP/2 HPackMagicBitField Test Suite
# Magic bits are not supposed to be modified and if they are anyway, they must
# be assigned the magic|default value only...

= HTTP/2 HPackMagicBitField.addfield
~ http2 hpack field hpackmagicbitfield

f = h2.HPackMagicBitField('value', 3, 2)
r = f.addfield(None, b'Toto', 3)
assert(isinstance(r, tuple))
assert(r[0] == b'Toto')
assert(r[1] == 2)
assert(r[2] == 3)

r = f.addfield(None, (b'Toto', 2, 1) , 3)
assert(isinstance(r, tuple))
assert(r[0] == b'Toto')
assert(r[1] == 4)
assert(r[2] == 7)

assert(expect_exception(AssertionError, 'f.addfield(None, "toto", 2)'))

= HTTP/2 HPackMagicBitField.getfield
~ http2 hpack field hpackmagicbitfield

f = h2.HPackMagicBitField('value', 3, 2)

r = f.getfield(None, b'\xc0')
assert(isinstance(r, tuple))
assert(len(r) == 2)
assert(isinstance(r[0], tuple))
assert(len(r[0]) == 2)
assert(r[0][0] == b'\xc0')
assert(r[0][1] == 2)
assert(r[1] == 3)

r = f.getfield(None, (b'\x03', 6))
assert(isinstance(r, tuple))
assert(len(r) == 2)
assert(isinstance(r[0], bytes))
assert(r[0] == b'')
assert(r[1] == 3)

expect_exception(AssertionError, 'f.getfield(None, b"\\x80")')

= HTTP/2 HPackMagicBitField.h2i
~ http2 hpack field hpackmagicbitfield

f = h2.HPackMagicBitField('value', 3, 2)
assert(f.h2i(None, 3) == 3)
expect_exception(AssertionError, 'f.h2i(None, 2)')

= HTTP/2 HPackMagicBitField.m2i
~ http2 hpack field hpackmagicbitfield

f = h2.HPackMagicBitField('value', 3, 2)
assert(f.m2i(None, 3) == 3)
expect_exception(AssertionError, 'f.m2i(None, 2)')

= HTTP/2 HPackMagicBitField.i2m
~ http2 hpack field hpackmagicbitfield

f = h2.HPackMagicBitField('value', 3, 2)
assert(f.i2m(None, 3) == 3)
expect_exception(AssertionError, 'f.i2m(None, 2)')

= HTTP/2 HPackMagicBitField.any2i
~ http2 hpack field hpackmagicbitfield

f = h2.HPackMagicBitField('value', 3, 2)
assert(f.any2i(None, 3) == 3)
expect_exception(AssertionError, 'f.any2i(None, 2)')

+ HTTP/2 HPackHdrString Test Suite

= HTTP/2 Dissect HPackHdrString
~ http2 pack dissect hpackhdrstring

p = h2.HPackHdrString(b'\x04Test')
assert(p.type == 0)
assert(p.len == 4)
assert(isinstance(p.getfieldval('data'), h2.HPackLiteralString))
assert(p.getfieldval('data').origin() == 'Test')

p = h2.HPackHdrString(b"\x83\xdeT'")
assert(p.type == 1)
assert(p.len == 3)
assert(isinstance(p.getfieldval('data'), h2.HPackZString))
assert(p.getfieldval('data').origin() == 'Test')

= HTTP/2 Build HPackHdrString
~ http2 hpack build hpackhdrstring

p = h2.HPackHdrString(data=h2.HPackLiteralString('Test'))
assert(raw(p) == b'\x04Test')

p = h2.HPackHdrString(data=h2.HPackZString('Test'))
assert(raw(p) == b"\x83\xdeT'")

#Fuzzing-able tests
p = h2.HPackHdrString(type=1, len=3, data=h2.HPackLiteralString('Test'))
assert(raw(p) == b'\x83Test')

+ HTTP/2 HPackIndexedHdr Test Suite

= HTTP/2 Dissect HPackIndexedHdr
~ http2 hpack dissect hpackindexedhdr

p = h2.HPackIndexedHdr(b'\x80')
assert(p.magic == 1)
assert(p.index == 0)

p = h2.HPackIndexedHdr(b'\xFF\x00')
assert(p.magic == 1)
assert(p.index == 127)

= HTTP/2 Build HPackIndexedHdr
~ http2 hpack build hpackindexedhdr

p = h2.HPackIndexedHdr(index=0)
assert(raw(p) == b'\x80')

p = h2.HPackIndexedHdr(index=127)
assert(raw(p) == b'\xFF\x00')

+ HTTP/2 HPackLitHdrFldWithIncrIndexing Test Suite

= HTTP/2 Dissect HPackLitHdrFldWithIncrIndexing without indexed name
~ http2 hpack dissect hpacklithdrfldwithincrindexing

p = h2.HPackLitHdrFldWithIncrIndexing(b'\x40\x04Test\x04Toto')
assert(p.magic == 1)
assert(p.index == 0)
assert(isinstance(p.hdr_name, h2.HPackHdrString))
assert(p.hdr_name.type == 0)
assert(p.hdr_name.len == 4)
assert(p.hdr_name.getfieldval('data').origin() == 'Test')
assert(isinstance(p.hdr_value, h2.HPackHdrString))
assert(p.hdr_value.type == 0)
assert(p.hdr_value.len == 4)
assert(p.hdr_value.getfieldval('data').origin() == 'Toto')

= HTTP/2 Dissect HPackLitHdrFldWithIncrIndexing with indexed name
~ http2 hpack dissect hpacklithdrfldwithincrindexing

p = h2.HPackLitHdrFldWithIncrIndexing(b'\x41\x04Toto')
assert(p.magic == 1)
assert(p.index == 1)
assert(p.hdr_name is None)
assert(isinstance(p.hdr_value, h2.HPackHdrString))
assert(p.hdr_value.type == 0)
assert(p.hdr_value.len == 4)
assert(p.hdr_value.getfieldval('data').origin() == 'Toto')


= HTTP/2 Build HPackLitHdrFldWithIncrIndexing without indexed name
~ http2 hpack build hpacklithdrfldwithincrindexing

p = h2.HPackLitHdrFldWithIncrIndexing(
    hdr_name=h2.HPackHdrString(data=h2.HPackLiteralString('Test')),
    hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString(b'Toto'))
)
assert(raw(p) == b'\x40\x04Test\x04Toto')

= HTTP/2 Build HPackLitHdrFldWithIncrIndexing with indexed name
~ http2 hpack build hpacklithdrfldwithincrindexing

p = h2.HPackLitHdrFldWithIncrIndexing(
    index=1,
    hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString(b'Toto'))
)
assert(raw(p) == b'\x41\x04Toto')

+ HTTP/2 HPackLitHdrFldWithoutIndexing Test Suite

= HTTP/2 Dissect HPackLitHdrFldWithoutIndexing : don't index and no index
~ http2 hpack dissect hpacklithdrfldwithoutindexing

p = h2.HPackLitHdrFldWithoutIndexing(b'\x00\x04Test\x04Toto')
assert(p.magic == 0)
assert(p.never_index == 0)
assert(p.index == 0)
assert(isinstance(p.hdr_name, h2.HPackHdrString))
assert(p.hdr_name.type == 0)
assert(p.hdr_name.len == 4)
assert(isinstance(p.hdr_name.getfieldval('data'), h2.HPackLiteralString))
assert(p.hdr_name.getfieldval('data').origin() == 'Test')
assert(isinstance(p.hdr_value, h2.HPackHdrString))
assert(p.hdr_value.type == 0)
assert(p.hdr_value.len == 4)
assert(isinstance(p.hdr_value.getfieldval('data'), h2.HPackLiteralString))
assert(p.hdr_value.getfieldval('data').origin() == 'Toto')

= HTTP/2 Dissect HPackLitHdrFldWithoutIndexing : never index index and no index
~ http2 hpack dissect hpacklithdrfldwithoutindexing

p = h2.HPackLitHdrFldWithoutIndexing(b'\x10\x04Test\x04Toto')
assert(p.magic == 0)
assert(p.never_index == 1)
assert(p.index == 0)
assert(isinstance(p.hdr_name, h2.HPackHdrString))
assert(p.hdr_name.type == 0)
assert(p.hdr_name.len == 4)
assert(isinstance(p.hdr_name.getfieldval('data'), h2.HPackLiteralString))
assert(p.hdr_name.getfieldval('data').origin() == 'Test')
assert(isinstance(p.hdr_value, h2.HPackHdrString))
assert(p.hdr_value.type == 0)
assert(p.hdr_value.len == 4)
assert(isinstance(p.hdr_value.getfieldval('data'), h2.HPackLiteralString))
assert(p.hdr_value.getfieldval('data').origin() == 'Toto')

= HTTP/2 Dissect HPackLitHdrFldWithoutIndexing : never index and indexed name
~ http2 hpack dissect hpacklithdrfldwithoutindexing

p = h2.HPackLitHdrFldWithoutIndexing(b'\x11\x04Toto')
assert(p.magic == 0)
assert(p.never_index == 1)
assert(p.index == 1)
assert(p.hdr_name is None)
assert(isinstance(p.hdr_value, h2.HPackHdrString))
assert(p.hdr_value.type == 0)
assert(p.hdr_value.len == 4)
assert(isinstance(p.hdr_value.getfieldval('data'), h2.HPackLiteralString))
assert(p.hdr_value.getfieldval('data').origin() == 'Toto')

= HTTP/2 Build HPackLitHdrFldWithoutIndexing : don't index and no index
~ http2 hpack build hpacklithdrfldwithoutindexing

p = h2.HPackLitHdrFldWithoutIndexing(
    hdr_name=h2.HPackHdrString(data=h2.HPackLiteralString('Test')),
    hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString(b'Toto'))
)
assert(raw(p) == b'\x00\x04Test\x04Toto')

= HTTP/2 Build HPackLitHdrFldWithoutIndexing : never index index and no index
~ http2 hpack build hpacklithdrfldwithoutindexing

p = h2.HPackLitHdrFldWithoutIndexing(
    never_index=1,
    hdr_name=h2.HPackHdrString(data=h2.HPackLiteralString('Test')),
    hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString(b'Toto'))
)
assert(raw(p) == b'\x10\x04Test\x04Toto')

= HTTP/2 Build HPackLitHdrFldWithoutIndexing : never index and indexed name
~ http2 hpack build hpacklithdrfldwithoutindexing

p = h2.HPackLitHdrFldWithoutIndexing(
    never_index=1,
    index=1,
    hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString(b'Toto'))
)
assert(raw(p) == b'\x11\x04Toto')

+ HTTP/2 HPackDynamicSizeUpdate Test Suite

= HTTP/2 Dissect HPackDynamicSizeUpdate
~ http2 hpack dissect hpackdynamicsizeupdate

p = h2.HPackDynamicSizeUpdate(b'\x25')
assert(p.magic == 1)
assert(p.max_size == 5)
p = h2.HPackDynamicSizeUpdate(b'\x3F\x00')
assert(p.magic == 1)
assert(p.max_size == 31)

= HTTP/2 Build HPackDynamicSizeUpdate
~ http2 hpack build hpackdynamicsizeupdate

p = h2.HPackDynamicSizeUpdate(max_size=5)
assert(raw(p) == b'\x25')
p = h2.HPackDynamicSizeUpdate(max_size=31)
assert(raw(p) == b'\x3F\x00')

+ HTTP/2 Data Frame Test Suite

= HTTP/2 Dissect Data Frame: Simple data frame
~ http2 frame dissect data

pkt = h2.H2Frame(b'\x00\x00\x04\x00\x00\x00\x00\x00\x01ABCD')
assert(pkt.type == 0)
assert(pkt.len == 4)
assert(len(pkt.flags) == 0)
assert(pkt.reserved == 0)
assert(pkt.stream_id == 1)
assert(isinstance(pkt.payload, h2.H2DataFrame))
assert(pkt[h2.H2DataFrame])
assert(pkt.payload.data == b'ABCD')
assert(isinstance(pkt.payload.payload, scapy.packet.NoPayload))

= HTTP/2 Build Data Frame: Simple data frame
~ http2 frame build data

pkt = h2.H2Frame(stream_id = 1)/h2.H2DataFrame(data='ABCD')
assert(raw(pkt) == b'\x00\x00\x04\x00\x00\x00\x00\x00\x01ABCD')
try:
    pkt.show2(dump=True)
    assert(True)
except Exception:
    assert(False)

= HTTP/2 Dissect Data Frame: Simple data frame with padding
~ http2 frame dissect data

pkt = h2.H2Frame(b'\x00\x00\r\x00\x08\x00\x00\x00\x01\x08ABCD\x00\x00\x00\x00\x00\x00\x00\x00') #Padded data frame
assert(pkt.type == 0)
assert(pkt.len == 13)
assert(len(pkt.flags) ==  1)
assert('P' in pkt.flags)
assert(pkt.reserved == 0)
assert(pkt.stream_id == 1)
assert(isinstance(pkt.payload, h2.H2PaddedDataFrame))
assert(pkt[h2.H2PaddedDataFrame])
assert(pkt.payload.padlen == 8)
assert(pkt.payload.data == b'ABCD')
assert(pkt.payload.padding == b'\x00'*8)
assert(flags_bit_pattern.search(pkt.show(dump=True)) is None)
assert(isinstance(pkt.payload.payload, scapy.packet.NoPayload))

= HTTP/2 Build Data Frame: Simple data frame with padding
~ http2 frame build data

pkt = h2.H2Frame(flags = {'P'}, stream_id = 1)/h2.H2PaddedDataFrame(data='ABCD', padding=b'\x00'*8)
assert(raw(pkt) == b'\x00\x00\r\x00\x08\x00\x00\x00\x01\x08ABCD\x00\x00\x00\x00\x00\x00\x00\x00')
try:
    pkt.show2(dump=True)
    assert(True)
except Exception:
    assert(False)

= HTTP/2 Dissect Data Frame: Simple data frame with padding and end stream flag
~ http2 frame dissect data

pkt = h2.H2Frame(b'\x00\x00\r\x00\t\x00\x00\x00\x01\x08ABCD\x00\x00\x00\x00\x00\x00\x00\x00') #Padded data frame with end stream flag
assert(pkt.type == 0)
assert(pkt.len == 13)
assert(len(pkt.flags) == 2)
assert('P' in pkt.flags)
assert('ES' in pkt.flags)
assert(pkt.reserved == 0)
assert(pkt.stream_id == 1)
assert(isinstance(pkt.payload, h2.H2PaddedDataFrame))
assert(pkt[h2.H2PaddedDataFrame])
assert(pkt.payload.padlen == 8)
assert(pkt.payload.data == b'ABCD')
assert(pkt.payload.padding == b'\x00'*8)
assert(flags_bit_pattern.search(pkt.show(dump=True)) is None)
assert(isinstance(pkt.payload.payload, scapy.packet.NoPayload))

= HTTP/2 Build Data Frame: Simple data frame with padding and end stream flag
~ http2 frame build data

pkt = h2.H2Frame(flags = {'P', 'ES'}, stream_id=1)/h2.H2PaddedDataFrame(data='ABCD', padding=b'\x00'*8)
assert(raw(pkt) == b'\x00\x00\r\x00\t\x00\x00\x00\x01\x08ABCD\x00\x00\x00\x00\x00\x00\x00\x00')
try:
    pkt.show2(dump=True)
    assert(True)
except Exception:
    assert(False)

+ HTTP/2 Headers Frame Test Suite

= HTTP/2 Dissect Headers Frame: Simple header frame
~ http2 frame dissect headers

pkt = h2.H2Frame(b'\x00\x00\x0e\x01\x00\x00\x00\x00\x01\x88\x0f\x10\ntext/plain') #Header frame
assert(pkt.type == 1)
assert(pkt.len == 14)
assert(len(pkt.flags) == 0)
assert(pkt.reserved == 0)
assert(pkt.stream_id == 1)
assert(isinstance(pkt.payload, h2.H2HeadersFrame))
assert(pkt[h2.H2HeadersFrame])
hf = pkt[h2.H2HeadersFrame]
assert(len(hf.hdrs) == 2)
assert(isinstance(hf.hdrs[0], h2.HPackIndexedHdr))
assert(hf.hdrs[0].magic == 1)
assert(hf.hdrs[0].index == 8)
assert(isinstance(hf.hdrs[1], h2.HPackLitHdrFldWithoutIndexing))
assert(hf.hdrs[1].magic == 0)
assert(hf.hdrs[1].never_index == 0)
assert(hf.hdrs[1].index == 31)
assert(hf.hdrs[1].hdr_name is None)
assert(expect_exception(AttributeError, 'hf.hdrs[1].non_existant'))
assert(isinstance(hf.hdrs[1].hdr_value, h2.HPackHdrString))
s = hf.hdrs[1].hdr_value
assert(s.type == 0)
assert(s.len == 10)
assert(s.getfieldval('data').origin() == 'text/plain')
assert(isinstance(hf.payload, scapy.packet.NoPayload))

= HTTP/2 Build Headers Frame: Simple header frame
~ http2 frame build headers

p = h2.H2Frame(stream_id=1)/h2.H2HeadersFrame(hdrs=[
        h2.HPackIndexedHdr(index=8),
        h2.HPackLitHdrFldWithoutIndexing(
            index=31,
            hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('text/plain'))
        )
    ]
)
assert(raw(p) == b'\x00\x00\x0e\x01\x00\x00\x00\x00\x01\x88\x0f\x10\ntext/plain')

= HTTP/2 Dissect Headers Frame: Header frame with padding
~ http2 frame dissect headers

pkt = h2.H2Frame(b'\x00\x00\x17\x01\x08\x00\x00\x00\x01\x08\x88\x0f\x10\ntext/plain\x00\x00\x00\x00\x00\x00\x00\x00') #Header frame with padding
assert(pkt.type == 1)
assert(pkt.len == 23)
assert(len(pkt.flags) == 1)
assert('P' in pkt.flags)
assert(pkt.reserved == 0)
assert(pkt.stream_id == 1)
assert(isinstance(pkt.payload, h2.H2PaddedHeadersFrame))
assert(flags_bit_pattern.search(pkt.show(dump=True)) is None)
assert(pkt[h2.H2PaddedHeadersFrame])
hf = pkt[h2.H2PaddedHeadersFrame]
assert(hf.padlen == 8)
assert(hf.padding == b'\x00' * 8)
assert(len(hf.hdrs) == 2)
assert(isinstance(hf.hdrs[0], h2.HPackIndexedHdr))
assert(hf.hdrs[0].magic == 1)
assert(hf.hdrs[0].index == 8)
assert(isinstance(hf.hdrs[1], h2.HPackLitHdrFldWithoutIndexing))
assert(hf.hdrs[1].magic == 0)
assert(hf.hdrs[1].never_index == 0)
assert(hf.hdrs[1].index == 31)
assert(hf.hdrs[1].hdr_name is None)
assert(expect_exception(AttributeError, 'hf.hdrs[1].non_existant'))
assert(isinstance(hf.hdrs[1].hdr_value, h2.HPackHdrString))
s = hf.hdrs[1].hdr_value
assert(s.type == 0)
assert(s.len == 10)
assert(s.getfieldval('data').origin() == 'text/plain')
assert(isinstance(hf.payload, scapy.packet.NoPayload))

= HTTP/2 Build Headers Frame: Header frame with padding
~ http2 frame build headers

p = h2.H2Frame(flags={'P'}, stream_id=1)/h2.H2PaddedHeadersFrame(
    hdrs=[
        h2.HPackIndexedHdr(index=8),
        h2.HPackLitHdrFldWithoutIndexing(
            index=31,
            hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('text/plain'))
        )
    ],
    padding=b'\x00'*8,
)
assert(raw(p) == b'\x00\x00\x17\x01\x08\x00\x00\x00\x01\x08\x88\x0f\x10\ntext/plain\x00\x00\x00\x00\x00\x00\x00\x00')

= HTTP/2 Dissect Headers Frame: Header frame with priority
~ http2 frame dissect headers

pkt = h2.H2Frame(b'\x00\x00\x13\x01 \x00\x00\x00\x01\x00\x00\x00\x02d\x88\x0f\x10\ntext/plain') #Header frame with priority
assert(pkt.type == 1)
assert(pkt.len == 19)
assert(len(pkt.flags) == 1)
assert('+' in pkt.flags)
assert(pkt.reserved == 0)
assert(pkt.stream_id == 1)
assert(isinstance(pkt.payload, h2.H2PriorityHeadersFrame))
assert(flags_bit_pattern.search(pkt.show(dump=True)) is None)
assert(pkt[h2.H2PriorityHeadersFrame])
hf = pkt[h2.H2PriorityHeadersFrame]
assert(hf.exclusive == 0)
assert(hf.stream_dependency == 2)
assert(hf.weight == 100)
assert(len(hf.hdrs) == 2)
assert(isinstance(hf.hdrs[0], h2.HPackIndexedHdr))
assert(hf.hdrs[0].magic == 1)
assert(hf.hdrs[0].index == 8)
assert(isinstance(hf.hdrs[1], h2.HPackLitHdrFldWithoutIndexing))
assert(hf.hdrs[1].magic == 0)
assert(hf.hdrs[1].never_index == 0)
assert(hf.hdrs[1].index == 31)
assert(hf.hdrs[1].hdr_name is None)
assert(expect_exception(AttributeError, 'hf.hdrs[1].non_existant'))
assert(isinstance(hf.hdrs[1].hdr_value, h2.HPackHdrString))
s = hf.hdrs[1].hdr_value
assert(s.type == 0)
assert(s.len == 10)
assert(s.getfieldval('data').origin() == 'text/plain')
assert(isinstance(hf.payload, scapy.packet.NoPayload))

= HTTP/2 Build Headers Frame: Header frame with priority
~ http2 frame build headers

p = h2.H2Frame(flags={'+'}, stream_id=1)/h2.H2PriorityHeadersFrame(
    exclusive=0,
    stream_dependency=2,
    weight=100,
    hdrs=[
        h2.HPackIndexedHdr(index=8),
        h2.HPackLitHdrFldWithoutIndexing(
            index=31,
            hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('text/plain'))
        )
    ]
)
assert(raw(p) == b'\x00\x00\x13\x01 \x00\x00\x00\x01\x00\x00\x00\x02d\x88\x0f\x10\ntext/plain')

= HTTP/2 Dissect Headers Frame: Header frame with priority and padding and flags
~ http2 frame dissect headers

pkt = h2.H2Frame(b'\x00\x00\x1c\x01-\x00\x00\x00\x01\x08\x00\x00\x00\x02d\x88\x0f\x10\ntext/plain\x00\x00\x00\x00\x00\x00\x00\x00') #Header frame with priority and padding and flags ES|EH
assert(pkt.type == 1)
assert(pkt.len == 28)
assert(len(pkt.flags) == 4)
assert('+' in pkt.flags)
assert('P' in pkt.flags)
assert('ES' in pkt.flags)
assert('EH' in pkt.flags)
assert(pkt.reserved == 0)
assert(pkt.stream_id == 1)
assert(isinstance(pkt.payload, h2.H2PaddedPriorityHeadersFrame))
assert(flags_bit_pattern.search(pkt.show(dump=True)) is None)
assert(pkt[h2.H2PaddedPriorityHeadersFrame])
hf = pkt[h2.H2PaddedPriorityHeadersFrame]
assert(hf.padlen == 8)
assert(hf.padding == b'\x00' * 8)
assert(hf.exclusive == 0)
assert(hf.stream_dependency == 2)
assert(hf.weight == 100)
assert(len(hf.hdrs) == 2)
assert(isinstance(hf.hdrs[0], h2.HPackIndexedHdr))
assert(hf.hdrs[0].magic == 1)
assert(hf.hdrs[0].index == 8)
assert(isinstance(hf.hdrs[1], h2.HPackLitHdrFldWithoutIndexing))
assert(hf.hdrs[1].magic == 0)
assert(hf.hdrs[1].never_index == 0)
assert(hf.hdrs[1].index == 31)
assert(hf.hdrs[1].hdr_name is None)
assert(expect_exception(AttributeError, 'hf.hdrs[1].non_existant'))
assert(isinstance(hf.hdrs[1].hdr_value, h2.HPackHdrString))
s = hf.hdrs[1].hdr_value
assert(s.type == 0)
assert(s.len == 10)
assert(s.getfieldval('data').origin() == 'text/plain')
assert(isinstance(hf.payload, scapy.packet.NoPayload))

= HTTP/2 Build Headers Frame: Header frame with priority and padding and flags
~ http2 frame build headers

p = h2.H2Frame(flags={'P', '+', 'ES', 'EH'}, stream_id=1)/h2.H2PaddedPriorityHeadersFrame(
    exclusive=0,
    stream_dependency=2,
    weight=100,
    padding=b'\x00'*8,
    hdrs=[
        h2.HPackIndexedHdr(index=8),
        h2.HPackLitHdrFldWithoutIndexing(
            index=31,
            hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('text/plain'))
        )
    ]
)

+ HTTP/2 Priority Frame Test Suite

= HTTP/2 Dissect Priority Frame
~ http2 frame dissect priority

pkt = h2.H2Frame(b'\x00\x00\x05\x02\x00\x00\x00\x00\x03\x80\x00\x00\x01d')
assert(pkt.type == 2)
assert(pkt.len == 5)
assert(len(pkt.flags) == 0)
assert(pkt.reserved == 0)
assert(pkt.stream_id == 3)
assert(isinstance(pkt.payload, h2.H2PriorityFrame))
assert(pkt[h2.H2PriorityFrame])
pp = pkt[h2.H2PriorityFrame]
assert(pp.stream_dependency == 1)
assert(pp.exclusive == 1)
assert(pp.weight == 100)

= HTTP/2 Build Priority Frame
~ http2 frame build priority

p = h2.H2Frame(stream_id=3)/h2.H2PriorityFrame(
    exclusive=1,
    stream_dependency=1,
    weight=100
)
assert(raw(p) == b'\x00\x00\x05\x02\x00\x00\x00\x00\x03\x80\x00\x00\x01d')

+ HTTP/2 Reset Stream Frame Test Suite

= HTTP/2 Dissect Reset Stream Frame: Protocol Error
~ http2 frame dissect rststream

pkt = h2.H2Frame(b'\x00\x00\x04\x03\x00\x00\x00\x00\x01\x00\x00\x00\x01') #Reset stream with protocol error
assert(pkt.type == 3)
assert(pkt.len == 4)
assert(len(pkt.flags) == 0)
assert(pkt.reserved == 0)
assert(pkt.stream_id == 1)
assert(isinstance(pkt.payload, h2.H2ResetFrame))
assert(pkt[h2.H2ResetFrame])
rf = pkt[h2.H2ResetFrame]
assert(rf.error == 1)
assert(isinstance(rf.payload, scapy.packet.NoPayload))

= HTTP/2 Build Reset Stream Frame: Protocol Error
~ http2 frame build rststream

p = h2.H2Frame(stream_id=1)/h2.H2ResetFrame(error='Protocol error')
assert(raw(p) == b'\x00\x00\x04\x03\x00\x00\x00\x00\x01\x00\x00\x00\x01')

p = h2.H2Frame(stream_id=1)/h2.H2ResetFrame(error=1)
assert(raw(p) == b'\x00\x00\x04\x03\x00\x00\x00\x00\x01\x00\x00\x00\x01')

= HTTP/2 Dissect Reset Stream Frame: Raw 123456 error
~ http2 frame dissect rststream

pkt = h2.H2Frame(b'\x00\x00\x04\x03\x00\x00\x00\x00\x01\x00\x01\xe2@') #Reset stream with raw error
assert(pkt.type == 3)
assert(pkt.len == 4)
assert(len(pkt.flags) == 0)
assert(pkt.reserved == 0)
assert(pkt.stream_id == 1)
assert(isinstance(pkt.payload, h2.H2ResetFrame))
assert(pkt[h2.H2ResetFrame])
rf = pkt[h2.H2ResetFrame]
assert(rf.error == 123456)
assert(isinstance(rf.payload, scapy.packet.NoPayload))

= HTTP/2 Dissect Reset Stream Frame: Raw 123456 error
~ http2 frame dissect rststream

p = h2.H2Frame(stream_id=1)/h2.H2ResetFrame(error=123456)
assert(raw(p) == b'\x00\x00\x04\x03\x00\x00\x00\x00\x01\x00\x01\xe2@')

+ HTTP/2 Settings Frame Test Suite

= HTTP/2 Dissect Settings Frame: Settings Frame
~ http2 frame dissect settings

pkt = h2.H2Frame(b'\x00\x00$\x04\x00\x00\x00\x00\x00\x00\x01\x07[\xcd\x15\x00\x02\x00\x00\x00\x01\x00\x03\x00\x00\x00{\x00\x04\x00\x12\xd6\x87\x00\x05\x00\x01\xe2@\x00\x06\x00\x00\x00{') #Settings frame
assert(pkt.type == 4)
assert(pkt.len == 36)
assert(len(pkt.flags) == 0)
assert(pkt.reserved == 0)
assert(pkt.stream_id == 0)
assert(isinstance(pkt.payload, h2.H2SettingsFrame))
assert(pkt[h2.H2SettingsFrame])
sf = pkt[h2.H2SettingsFrame]
assert(len(sf.settings) == 6)
assert(isinstance(sf.settings[0], h2.H2Setting))
assert(sf.settings[0].id == 1)
assert(sf.settings[0].value == 123456789)
assert(isinstance(sf.settings[1], h2.H2Setting))
assert(sf.settings[1].id == 2)
assert(sf.settings[1].value == 1)
assert(isinstance(sf.settings[2], h2.H2Setting))
assert(sf.settings[2].id == 3)
assert(sf.settings[2].value == 123)
assert(isinstance(sf.settings[3], h2.H2Setting))
assert(sf.settings[3].id == 4)
assert(sf.settings[3].value == 1234567)
assert(isinstance(sf.settings[4], h2.H2Setting))
assert(sf.settings[4].id == 5)
assert(sf.settings[4].value == 123456)
assert(isinstance(sf.settings[5], h2.H2Setting))
assert(sf.settings[5].id == 6)
assert(sf.settings[5].value == 123)
assert(isinstance(sf.payload, scapy.packet.NoPayload))

= HTTP/2 Build Settings Frame: Settings Frame
~ http2 frame build settings

p = h2.H2Frame()/h2.H2SettingsFrame(settings=[
        h2.H2Setting(id='Header table size',value=123456789),
        h2.H2Setting(id='Enable push', value=1),
        h2.H2Setting(id='Max concurrent streams', value=123),
        h2.H2Setting(id='Initial window size', value=1234567),
        h2.H2Setting(id='Max frame size', value=123456),
        h2.H2Setting(id='Max header list size', value=123)
    ]
)
assert(raw(p) == b'\x00\x00$\x04\x00\x00\x00\x00\x00\x00\x01\x07[\xcd\x15\x00\x02\x00\x00\x00\x01\x00\x03\x00\x00\x00{\x00\x04\x00\x12\xd6\x87\x00\x05\x00\x01\xe2@\x00\x06\x00\x00\x00{')

= HTTP/2 Dissect Settings Frame: Incomplete Settings Frame
~ http2 frame dissect settings

#We use here the decode('hex') method because null-bytes are rejected by eval()
assert(expect_exception(AssertionError, 'h2.H2Frame(bytes_hex("0000240400000000000001075bcd1500020000000100030000007b00040012d68700050001e2400006000000"))'))

= HTTP/2 Dissect Settings Frame: Settings Frame acknowledgement
~ http2 frame dissect settings

pkt = h2.H2Frame(b'\x00\x00\x00\x04\x01\x00\x00\x00\x00') #Settings frame w/ ack flag
assert(pkt.type == 4)
assert(pkt.len == 0)
assert(len(pkt.flags) == 1)
assert('A' in pkt.flags)
assert(pkt.reserved == 0)
assert(pkt.stream_id == 0)
assert(flags_bit_pattern.search(pkt.show(dump=True)) is None)
assert(isinstance(pkt.payload, scapy.packet.NoPayload))

= HTTP/2 Build Settings Frame: Settings Frame acknowledgement
~ http2 frame build settings

p = h2.H2Frame(type=h2.H2SettingsFrame.type_id, flags={'A'})
assert(raw(p) == b'\x00\x00\x00\x04\x01\x00\x00\x00\x00')

+ HTTP/2 Push Promise Frame Test Suite

= HTTP/2 Dissect Push Promise Frame: no flag & headers with compression and hdr_name
~ http2 frame dissect pushpromise

pkt = h2.H2Frame(b'\x00\x00\x15\x05\x00\x00\x00\x00\x01\x00\x00\x00\x03@\x8c\xfc[i{ZT$\xb2-\xc8\xc9\x9f\x02Me')
assert(pkt.type == 5)
assert(pkt.len == 21)
assert(len(pkt.flags) == 0)
assert(pkt.reserved == 0)
assert(pkt.stream_id == 1)
assert(isinstance(pkt.payload, h2.H2PushPromiseFrame))
assert(pkt[h2.H2PushPromiseFrame])
pf = pkt[h2.H2PushPromiseFrame]
assert(pf.reserved == 0)
assert(pf.stream_id == 3)
assert(len(pf.hdrs) == 1)
assert(isinstance(pf.payload, scapy.packet.NoPayload))
hdr = pf.hdrs[0]
assert(isinstance(hdr, h2.HPackLitHdrFldWithIncrIndexing))
assert(hdr.magic == 1)
assert(hdr.index == 0)
assert(isinstance(hdr.hdr_name, h2.HPackHdrString))
assert(hdr.hdr_name.type == 1)
assert(hdr.hdr_name.len == 12)
assert(hdr.hdr_name.getfieldval('data').origin() == 'X-Requested-With')
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.type == 0)
assert(hdr.hdr_value.len == 2)
assert(hdr.hdr_value.getfieldval('data').origin() == 'Me')

= HTTP/2 Build Push Promise Frame: no flag & headers with compression and hdr_name
~ http2 frame build pushpromise

p = h2.H2Frame(stream_id=1)/h2.H2PushPromiseFrame(stream_id=3,hdrs=[
    h2.HPackLitHdrFldWithIncrIndexing(
        hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Requested-With')),
        hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('Me')),
    )
])
assert(raw(p) == b'\x00\x00\x15\x05\x00\x00\x00\x00\x01\x00\x00\x00\x03@\x8c\xfc[i{ZT$\xb2-\xc8\xc9\x9f\x02Me')

= HTTP/2 Dissect Push Promise Frame: with padding, the flag END_Header & headers with compression and hdr_name
~ http2 frame dissect pushpromise

pkt = h2.H2Frame(b'\x00\x00\x1e\x05\x0c\x00\x00\x00\x01\x08\x00\x00\x00\x03@\x8c\xfc[i{ZT$\xb2-\xc8\xc9\x9f\x02Me\x00\x00\x00\x00\x00\x00\x00\x00')
assert(pkt.type == 5)
assert(pkt.len == 30)
assert(len(pkt.flags) == 2)
assert('P' in pkt.flags)
assert('EH' in pkt.flags)
assert(pkt.reserved == 0)
assert(pkt.stream_id == 1)
assert(flags_bit_pattern.search(pkt.show(dump=True)) is None)
assert(isinstance(pkt.payload, h2.H2PaddedPushPromiseFrame))
assert(pkt[h2.H2PaddedPushPromiseFrame])
pf = pkt[h2.H2PaddedPushPromiseFrame]
assert(pf.padlen == 8)
assert(pf.padding == b'\x00'*8)
assert(pf.stream_id == 3)
assert(len(pf.hdrs) == 1)
hdr = pf.hdrs[0]
assert(isinstance(hdr, h2.HPackLitHdrFldWithIncrIndexing))
assert(hdr.magic == 1)
assert(hdr.index == 0)
assert(isinstance(hdr.hdr_name, h2.HPackHdrString))
assert(hdr.hdr_name.type == 1)
assert(hdr.hdr_name.len == 12)
assert(hdr.hdr_name.getfieldval('data').origin() == 'X-Requested-With')
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.type == 0)
assert(hdr.hdr_value.len == 2)
assert(hdr.hdr_value.getfieldval('data').origin() == 'Me')

= HTTP/2 Build Push Promise Frame: with padding, the flag END_Header & headers with compression and hdr_name
~ http2 frame build pushpromise

p = h2.H2Frame(flags={'P', 'EH'}, stream_id=1)/h2.H2PaddedPushPromiseFrame(
    stream_id=3,
    hdrs=[
        h2.HPackLitHdrFldWithIncrIndexing(
            hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Requested-With')),
            hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('Me'))
        )
    ],
    padding=b'\x00'*8
)
assert(raw(p) == b'\x00\x00\x1e\x05\x0c\x00\x00\x00\x01\x08\x00\x00\x00\x03@\x8c\xfc[i{ZT$\xb2-\xc8\xc9\x9f\x02Me\x00\x00\x00\x00\x00\x00\x00\x00')

+ HTTP/2 Ping Frame Test Suite

= HTTP/2 Dissect Ping Frame: Ping frame
~ http2 frame dissect ping

pkt = h2.H2Frame(b'\x00\x00\x08\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xe2@') #Ping frame with payload
assert(pkt.type == 6)
assert(pkt.len == 8)
assert(len(pkt.flags) == 0)
assert(pkt.reserved == 0)
assert(pkt.stream_id == 0)
assert(isinstance(pkt.payload, h2.H2PingFrame))
assert(pkt[h2.H2PingFrame])
pf = pkt[h2.H2PingFrame]
assert(pf.opaque == 123456)
assert(isinstance(pf.payload, scapy.packet.NoPayload))

= HTTP/2 Build Ping Frame: Ping frame
~ http2 frame build ping

p = h2.H2Frame()/h2.H2PingFrame(opaque=123456)
assert(raw(p) == b'\x00\x00\x08\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xe2@')

= HTTP/2 Dissect Ping Frame: Pong frame
~ http2 frame dissect ping

pkt = h2.H2Frame(b'\x00\x00\x08\x06\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xe2@') #Pong frame
assert(pkt.type == 6)
assert(pkt.len == 8)
assert(len(pkt.flags) == 1)
assert('A' in pkt.flags)
assert(pkt.reserved == 0)
assert(pkt.stream_id == 0)
assert(isinstance(pkt.payload, h2.H2PingFrame))
assert(flags_bit_pattern.search(pkt.show(dump=True)) is None)
assert(pkt[h2.H2PingFrame])
pf = pkt[h2.H2PingFrame]
assert(pf.opaque == 123456)
assert(isinstance(pf.payload, scapy.packet.NoPayload))

= HTTP/2 Dissect Ping Frame: Pong frame
~ http2 frame dissect ping

p = h2.H2Frame(flags={'A'})/h2.H2PingFrame(opaque=123456)
assert(raw(p) == b'\x00\x00\x08\x06\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xe2@')

+ HTTP/2 Go Away Frame Test Suite

= HTTP/2 Dissect Go Away Frame: No error
~ http2 frame dissect goaway

pkt = h2.H2Frame(b'\x00\x00\x08\x07\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00') #Go Away for no particular reason :)
assert(pkt.type == 7)
assert(pkt.len == 8)
assert(len(pkt.flags) == 0)
assert(pkt.reserved == 0)
assert(pkt.stream_id == 0)
assert(isinstance(pkt.payload, h2.H2GoAwayFrame))
assert(pkt[h2.H2GoAwayFrame])
gf = pkt[h2.H2GoAwayFrame]
assert(gf.reserved == 0)
assert(gf.last_stream_id == 1)
assert(gf.error == 0)
assert(len(gf.additional_data) == 0)
assert(isinstance(gf.payload, scapy.packet.NoPayload))

= HTTP/2 Build Go Away Frame: No error
~ http2 frame build goaway

p = h2.H2Frame()/h2.H2GoAwayFrame(last_stream_id=1, error='No error')
assert(raw(p) == b'\x00\x00\x08\x07\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00')

= HTTP/2 Dissect Go Away Frame: Arbitrary error with additional data
~ http2 frame dissect goaway

pkt = h2.H2Frame(b'\x00\x00\x10\x07\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01\xe2@\x00\x00\x00\x00\x00\x00\x00\x00') #Go Away with debug data
assert(pkt.type == 7)
assert(pkt.len == 16)
assert(len(pkt.flags) == 0)
assert(pkt.reserved == 0)
assert(pkt.stream_id == 0)
assert(isinstance(pkt.payload, h2.H2GoAwayFrame))
assert(pkt[h2.H2GoAwayFrame])
gf = pkt[h2.H2GoAwayFrame]
assert(gf.reserved == 0)
assert(gf.last_stream_id == 2)
assert(gf.error == 123456)
assert(gf.additional_data == 8*b'\x00')
assert(isinstance(gf.payload, scapy.packet.NoPayload))

= HTTP/2 Build Go Away Frame: Arbitrary error with additional data
~ http2 frame build goaway

p = h2.H2Frame()/h2.H2GoAwayFrame(
    last_stream_id=2,
    error=123456,
    additional_data=b'\x00'*8
)
assert(raw(p) == b'\x00\x00\x10\x07\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01\xe2@\x00\x00\x00\x00\x00\x00\x00\x00')

+ HTTP/2 Window Update Frame Test Suite

= HTTP/2 Dissect Window Update Frame: global
~ http2 frame dissect winupdate

pkt = h2.H2Frame(b'\x00\x00\x04\x08\x00\x00\x00\x00\x00\x00\x01\xe2@') #Window update with increment for connection
assert(pkt.type == 8)
assert(pkt.len == 4)
assert(len(pkt.flags) == 0)
assert(pkt.reserved == 0)
assert(pkt.stream_id == 0)
assert(isinstance(pkt.payload, h2.H2WindowUpdateFrame))
assert(pkt[h2.H2WindowUpdateFrame])
wf = pkt[h2.H2WindowUpdateFrame]
assert(wf.reserved == 0)
assert(wf.win_size_incr == 123456)
assert(isinstance(wf.payload, scapy.packet.NoPayload))

= HTTP/2 Build Window Update Frame: global
~ http2 frame build winupdate

p = h2.H2Frame()/h2.H2WindowUpdateFrame(win_size_incr=123456)
assert(raw(p) == b'\x00\x00\x04\x08\x00\x00\x00\x00\x00\x00\x01\xe2@')

= HTTP/2 Dissect Window Update Frame: a stream
~ http2 frame dissect winupdate

pkt = h2.H2Frame(b'\x00\x00\x04\x08\x00\x00\x00\x00\x01\x00\x01\xe2@') #Window update with increment for a stream
assert(pkt.type == 8)
assert(pkt.len == 4)
assert(len(pkt.flags) == 0)
assert(pkt.reserved == 0)
assert(pkt.stream_id == 1)
assert(isinstance(pkt.payload, h2.H2WindowUpdateFrame))
assert(pkt[h2.H2WindowUpdateFrame])
wf = pkt[h2.H2WindowUpdateFrame]
assert(wf.reserved == 0)
assert(wf.win_size_incr == 123456)
assert(isinstance(wf.payload, scapy.packet.NoPayload))

= HTTP/2 Build Window Update Frame: a stream
~ http2 frame build winupdate

p = h2.H2Frame(stream_id=1)/h2.H2WindowUpdateFrame(win_size_incr=123456)
assert(raw(p) == b'\x00\x00\x04\x08\x00\x00\x00\x00\x01\x00\x01\xe2@')

+ HTTP/2 Continuation Frame Test Suite

= HTTP/2 Dissect Continuation Frame: no flag & headers with compression and hdr_name
~ http2 frame dissect continuation

pkt = h2.H2Frame(b'\x00\x00\x11\t\x00\x00\x00\x00\x01@\x8c\xfc[i{ZT$\xb2-\xc8\xc9\x9f\x02Me')
assert(pkt.type == 9)
assert(pkt.len == 17)
assert(len(pkt.flags) == 0)
assert(pkt.reserved == 0)
assert(pkt.stream_id == 1)
assert(isinstance(pkt.payload, h2.H2ContinuationFrame))
assert(pkt[h2.H2ContinuationFrame])
hf = pkt[h2.H2ContinuationFrame]
assert(len(hf.hdrs) == 1)
assert(isinstance(hf.payload, scapy.packet.NoPayload))
hdr = hf.hdrs[0]
assert(isinstance(hdr, h2.HPackLitHdrFldWithIncrIndexing))
assert(hdr.magic == 1)
assert(hdr.index == 0)
assert(isinstance(hdr.hdr_name, h2.HPackHdrString))
assert(hdr.hdr_name.type == 1)
assert(hdr.hdr_name.len == 12)
assert(hdr.hdr_name.getfieldval('data').origin() == 'X-Requested-With')
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.type == 0)
assert(hdr.hdr_value.len == 2)
assert(hdr.hdr_value.getfieldval('data').origin() == 'Me')

= HTTP/2 Build Continuation Frame: no flag & headers with compression and hdr_name
~ http2 frame build continuation

p = h2.H2Frame(stream_id=1)/h2.H2ContinuationFrame(
    hdrs=[
        h2.HPackLitHdrFldWithIncrIndexing(
            hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Requested-With')),
            hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('Me'))
        )
    ]
)
assert(raw(p) == b'\x00\x00\x11\t\x00\x00\x00\x00\x01@\x8c\xfc[i{ZT$\xb2-\xc8\xc9\x9f\x02Me')

= HTTP/2 Dissect Continuation Frame: flag END_Header & headers with compression, sensitive flag and hdr_name
~ http2 frame dissect continuation

pkt = h2.H2Frame(b'\x00\x00\x11\t\x04\x00\x00\x00\x01\x10\x8c\xfc[i{ZT$\xb2-\xc8\xc9\x9f\x02Me')
assert(pkt.type == 9)
assert(pkt.len == 17)
assert(len(pkt.flags) == 1)
assert('EH' in pkt.flags)
assert(pkt.reserved == 0)
assert(pkt.stream_id == 1)
assert(flags_bit_pattern.search(pkt.show(dump=True)) is None)
assert(isinstance(pkt.payload, h2.H2ContinuationFrame))
assert(pkt[h2.H2ContinuationFrame])
hf = pkt[h2.H2ContinuationFrame]
assert(len(hf.hdrs) == 1)
assert(isinstance(hf.payload, scapy.packet.NoPayload))
hdr = hf.hdrs[0]
assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
assert(hdr.magic == 0)
assert(hdr.never_index == 1)
assert(hdr.index == 0)
assert(isinstance(hdr.hdr_name, h2.HPackHdrString))
assert(hdr.hdr_name.type == 1)
assert(hdr.hdr_name.len == 12)
assert(hdr.hdr_name.getfieldval('data').origin() == 'X-Requested-With')
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.type == 0)
assert(hdr.hdr_value.len == 2)
assert(hdr.hdr_value.getfieldval('data').origin() == 'Me')

= HTTP/2 Build Continuation Frame: flag END_Header & headers with compression, sensitive flag and hdr_name
~ http2 frame build continuation

p = h2.H2Frame(flags={'EH'}, stream_id=1)/h2.H2ContinuationFrame(
    hdrs=[
        h2.HPackLitHdrFldWithoutIndexing(
            never_index=1,
            hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Requested-With')),
            hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('Me'))
        )
    ]
)
assert(raw(p) == b'\x00\x00\x11\t\x04\x00\x00\x00\x01\x10\x8c\xfc[i{ZT$\xb2-\xc8\xc9\x9f\x02Me')

+ HTTP/2 HPackHdrTable Test Suite

= HTTP/2 HPackHdrEntry Tests
~ http2 hpack hpackhdrtable

n = 'X-Requested-With'
v = 'Me'
h = h2.HPackHdrEntry(n, v)
assert(len(h) == 32 + len(n) + len(v))
assert(h.name() == n.lower())
assert(h.value() == v)
assert(str(h) == '{}: {}'.format(n.lower(), v))

n = ':status'
v = '200'
h = h2.HPackHdrEntry(n, v)
assert(len(h) == 32 + len(n) + len(v))
assert(h.name() == n.lower())
assert(h.value() == v)
assert(str(h) == '{} {}'.format(n.lower(), v))

= HTTP/2 HPackHdrTable : Querying Static Entries
~ http2 hpack hpackhdrtable

# In RFC7541, the table is 1-based
assert(expect_exception(KeyError, 'h2.HPackHdrTable()[0]'))

h = h2.HPackHdrTable()
assert(h[1].name() == ':authority')
assert(h[7].name() == ':scheme')
assert(h[7].value() == 'https')
assert(str(h[14]) == ':status 500')
assert(str(h[16]) == 'accept-encoding: gzip, deflate')

assert(expect_exception(KeyError, 'h2.HPackHdrTable()[h2.HPackHdrTable._static_entries_last_idx+1]'))

= HTTP/2 HPackHdrTable : Addind Dynamic Entries without overflowing the table
~ http2 hpack hpackhdrtable

tbl = h2.HPackHdrTable(dynamic_table_max_size=1<<32, dynamic_table_cap_size=1<<32)
hdr = h2.HPackLitHdrFldWithIncrIndexing(
    index=32,
    hdr_value=h2.HPackHdrString(data=h2.HPackZString('PHPSESSID=abcdef0123456789'))
)
tbl.register(hdr)

tbl = h2.HPackHdrTable(dynamic_table_max_size=1<<32, dynamic_table_cap_size=1<<32)
hdr2 = h2.HPackLitHdrFldWithIncrIndexing(
    index=32,
    hdr_value=h2.HPackHdrString(data=h2.HPackZString('JSESSID=abcdef0123456789'))
)
tbl.register([hdr,hdr2])

tbl = h2.HPackHdrTable(dynamic_table_max_size=1<<32, dynamic_table_cap_size=1<<32)
hdr3 = h2.HPackLitHdrFldWithIncrIndexing(
    index=32,
    hdr_value=h2.HPackHdrString(data=h2.HPackZString('Test=abcdef0123456789'))
)
frm = h2.H2Frame(stream_id=1)/h2.H2HeadersFrame(hdrs=[hdr, hdr2, hdr3])
tbl.register(frm)


= HTTP/2 HPackHdrTable : Querying Dynamic Entries
~ http2 hpack hpackhdrtable

tbl = h2.HPackHdrTable(dynamic_table_max_size=1<<32, dynamic_table_cap_size=1<<32)
hdrv = 'PHPSESSID=abcdef0123456789'
hdr = h2.HPackLitHdrFldWithIncrIndexing(
    index=32,
    hdr_value=h2.HPackHdrString(data=h2.HPackZString(hdrv))
)
tbl.register(hdr)

hdrv2 = 'JSESSID=abcdef0123456789'
hdr2 = h2.HPackLitHdrFldWithIncrIndexing(
    index=32,
    hdr_value=h2.HPackHdrString(data=h2.HPackZString(hdrv2))
)
tbl.register(hdr2)

hdr3 = h2.HPackLitHdrFldWithIncrIndexing(
    index=0,
    hdr_name=h2.HPackHdrString(data=h2.HPackLiteralString('x-requested-by')),
    hdr_value=h2.HPackHdrString(data=h2.HPackZString('me'))
)
tbl.register(hdr3)

assert(tbl.get_idx_by_name('x-requested-by') == h2.HPackHdrTable._static_entries_last_idx+1)
assert(tbl[h2.HPackHdrTable._static_entries_last_idx+2].value() == hdrv2)
assert(tbl[h2.HPackHdrTable._static_entries_last_idx+3].value() == hdrv)

= HTTP/2 HPackHdrTable : Addind already registered Dynamic Entries without overflowing the table
~ http2 hpack hpackhdrtable

tbl = h2.HPackHdrTable(dynamic_table_max_size=1<<32, dynamic_table_cap_size=1<<32)

assert(len(tbl) == 0)

hdrv = 'PHPSESSID=abcdef0123456789'
hdr = h2.HPackLitHdrFldWithIncrIndexing(
    index=32,
    hdr_value=h2.HPackHdrString(data=h2.HPackZString(hdrv))
)
tbl.register(hdr)
assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv)

hdr2v = 'JSESSID=abcdef0123456789'
hdr2 = h2.HPackLitHdrFldWithIncrIndexing(
    index=32,
    hdr_value=h2.HPackHdrString(data=h2.HPackZString(hdr2v))
)
tbl.register(hdr2)
assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdr2v)

l = len(tbl)
tbl.register(hdr)
assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv)
assert(tbl[h2.HPackHdrTable._static_entries_last_idx+2].value() == hdr2v)
assert(tbl[h2.HPackHdrTable._static_entries_last_idx+3].value() == hdrv)

= HTTP/2 HPackHdrTable : Addind Dynamic Entries and overflowing the table
~ http2 hpack hpackhdrtable

tbl = h2.HPackHdrTable(dynamic_table_max_size=80, dynamic_table_cap_size=80)
hdrv = 'PHPSESSID=abcdef0123456789'
hdr = h2.HPackLitHdrFldWithIncrIndexing(
    index=32,
    hdr_value=h2.HPackHdrString(data=h2.HPackZString(hdrv))
)
tbl.register(hdr)
assert(len(tbl) <= 80)
assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv)

hdrv2 = 'JSESSID=abcdef0123456789'
hdr2 = h2.HPackLitHdrFldWithIncrIndexing(
    index=32,
    hdr_value=h2.HPackHdrString(data=h2.HPackZString(hdrv2))
)
tbl.register(hdr2)
assert(len(tbl) <= 80)
assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv2)
try:
    tbl[h2.HPackHdrTable._static_entries_last_idx+2]
    ret = False
except Exception:
    ret = True

assert(ret)


= HTTP/2 HPackHdrTable : Resizing
~ http2 hpack hpackhdrtable

tbl = h2.HPackHdrTable()
hdrv = 'PHPSESSID=abcdef0123456789'
hdr = h2.HPackLitHdrFldWithIncrIndexing(
    index=32,
    hdr_value=h2.HPackHdrString(data=h2.HPackZString(hdrv))
)
tbl.register(hdr)
assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv)

hdrv2 = 'JSESSID=abcdef0123456789'
hdr2 = h2.HPackLitHdrFldWithIncrIndexing(
    index=32,
    hdr_value=h2.HPackHdrString(data=h2.HPackZString(hdrv2))
)
tbl.register(hdr2)
assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv2)
assert(tbl[h2.HPackHdrTable._static_entries_last_idx+2].value() == hdrv)

#Resizing to a value higher than cap (default:4096)
try:
    tbl.resize(8192)
    ret = False
except AssertionError:
    ret = True

assert(ret)
#Resizing to a lower value by that is not small enough to cause eviction
tbl.resize(1024)
assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv2)
assert(tbl[h2.HPackHdrTable._static_entries_last_idx+2].value() == hdrv)
#Resizing to a higher value but thatt is lower than cap
tbl.resize(2048)
assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv2)
assert(tbl[h2.HPackHdrTable._static_entries_last_idx+2].value() == hdrv)
#Resizing to a lower value that causes eviction
tbl.resize(80)
assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv2)
try:
    tbl[h2.HPackHdrTable._static_entries_last_idx+2]
    ret = False
except Exception:
    ret = True

assert(ret)

= HTTP/2 HPackHdrTable : Recapping
~ http2 hpack hpackhdrtable

tbl = h2.HPackHdrTable()
hdrv = 'PHPSESSID=abcdef0123456789'
hdr = h2.HPackLitHdrFldWithIncrIndexing(
    index=32,
    hdr_value=h2.HPackHdrString(data=h2.HPackZString(hdrv))
)
tbl.register(hdr)
assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv)

hdrv2 = 'JSESSID=abcdef0123456789'
hdr2 = h2.HPackLitHdrFldWithIncrIndexing(
    index=32,
    hdr_value=h2.HPackHdrString(data=h2.HPackZString(hdrv2))
)
tbl.register(hdr2)
assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv2)
assert(tbl[h2.HPackHdrTable._static_entries_last_idx+2].value() == hdrv)

#Recapping to a higher value
tbl.recap(8192)
assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv2)
assert(tbl[h2.HPackHdrTable._static_entries_last_idx+2].value() == hdrv)

#Recapping to a low value but without causing eviction
tbl.recap(1024)
assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv2)
assert(tbl[h2.HPackHdrTable._static_entries_last_idx+2].value() == hdrv)

#Recapping to a low value that causes evictiontbl.recap(1024)
tbl.recap(80)
assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv2)
try:
    tbl[h2.HPackHdrTable._static_entries_last_idx+2]
    ret = False
except Exception:
    ret = True

assert(ret)

= HTTP/2 HPackHdrTable : Generating Textual Representation
~ http2 hpack hpackhdrtable helpers

h = h2.HPackHdrTable()
h.register(h2.HPackLitHdrFldWithIncrIndexing(
        hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Generation-Date')),
        hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('2016-08-11'))
))

hdrs_lst = [
    h2.HPackIndexedHdr(index=2), #Method Get
    h2.HPackLitHdrFldWithIncrIndexing(
        index=h.get_idx_by_name(':path'),
        hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('/index.php'))
    ),
    h2.HPackIndexedHdr(index=7), #Scheme HTTPS
    h2.HPackIndexedHdr(index=h2.HPackHdrTable._static_entries_last_idx+2),
    h2.HPackLitHdrFldWithIncrIndexing(
        index=58,
        hdr_value=h2.HPackHdrString(data=h2.HPackZString('Mozilla/5.0 Generated by hand'))
    ),
    h2.HPackLitHdrFldWithoutIndexing(
        hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Generated-By')),
        hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('Me'))
    )
]

p = h2.H2Frame(stream_id = 1)/h2.H2HeadersFrame(hdrs=hdrs_lst)

expected_output = ''':method GET
:path /index.php
:scheme https
x-generation-date: 2016-08-11
user-agent: Mozilla/5.0 Generated by hand
X-Generated-By: Me'''

assert(h.gen_txt_repr(p) == expected_output)

= HTTP/2 HPackHdrTable : Parsing Textual Representation
~ http2 hpack hpackhdrtable helpers

body = b'login=titi&passwd=toto'
hdrs = ''':method POST
:path /login.php
:scheme https
content-type: application/x-www-form-urlencoded
content-length: {}
user-agent: Mozilla/5.0 Generated by hand
x-generated-by: Me
x-generation-date: 2016-08-11
x-generation-software: scapy
'''.format(len(body)).encode()

h = h2.HPackHdrTable()
h.register(h2.HPackLitHdrFldWithIncrIndexing(
        hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Generation-Date')),
        hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('2016-08-11'))
))
seq = h.parse_txt_hdrs(
    hdrs,
    stream_id=1,
    body=body,
    should_index=lambda name: name in ['user-agent', 'x-generation-software'],
    is_sensitive=lambda name, value: name in ['x-generated-by', ':path']
)
assert(isinstance(seq, h2.H2Seq))
assert(len(seq.frames) == 2)
p = seq.frames[0]
assert(isinstance(p, h2.H2Frame))
assert(p.type == 1)
assert(len(p.flags) == 1)
assert('EH' in p.flags)
assert(p.stream_id == 1)
assert(isinstance(p.payload, h2.H2HeadersFrame))
hdrs_frm = p[h2.H2HeadersFrame]
assert(len(p.hdrs) == 9)
hdr = p.hdrs[0]
assert(isinstance(hdr, h2.HPackIndexedHdr))
assert(hdr.magic == 1)
assert(hdr.index == 3)
hdr = p.hdrs[1]
assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
assert(hdr.magic == 0)
assert(hdr.never_index == 1)
assert(hdr.index in [4, 5])
assert(hdr.hdr_name is None)
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.data == 'HPackZString(/login.php)')
hdr = p.hdrs[2]
assert(isinstance(hdr, h2.HPackIndexedHdr))
assert(hdr.magic == 1)
assert(hdr.index == 7)
hdr = p.hdrs[3]
assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
assert(hdr.magic == 0)
assert(hdr.never_index == 0)
assert(hdr.index == 31)
assert(hdr.hdr_name is None)
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.data == 'HPackZString(application/x-www-form-urlencoded)')
hdr = p.hdrs[4]
assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
assert(hdr.magic == 0)
assert(hdr.never_index == 0)
assert(hdr.index == 28)
assert(hdr.hdr_name is None)
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.data == 'HPackLiteralString(22)')
hdr = p.hdrs[5]
assert(isinstance(hdr, h2.HPackLitHdrFldWithIncrIndexing))
assert(hdr.magic == 1)
assert(hdr.index == 58)
assert(hdr.hdr_name is None)
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.data == 'HPackZString(Mozilla/5.0 Generated by hand)')
hdr = p.hdrs[6]
assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
assert(hdr.magic == 0)
assert(hdr.never_index == 1)
assert(hdr.index == 0)
assert(isinstance(hdr.hdr_name, h2.HPackHdrString))
assert(hdr.hdr_name.data == 'HPackZString(x-generated-by)')
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.data == 'HPackLiteralString(Me)')
hdr = p.hdrs[7]
assert(isinstance(hdr, h2.HPackIndexedHdr))
assert(hdr.magic == 1)
assert(hdr.index == 63)
hdr = p.hdrs[8]
assert(isinstance(hdr, h2.HPackLitHdrFldWithIncrIndexing))
assert(hdr.magic == 1)
assert(hdr.index == 0)
assert(isinstance(hdr.hdr_name, h2.HPackHdrString))
assert(hdr.hdr_name.data == 'HPackZString(x-generation-software)')
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.data == 'HPackZString(scapy)')

p = seq.frames[1]
assert(isinstance(p, h2.H2Frame))
assert(p.type == 0)
assert(len(p.flags) == 1)
assert('ES' in p.flags)
assert(p.stream_id == 1)
assert(isinstance(p.payload, h2.H2DataFrame))
pay = p[h2.H2DataFrame]
assert(pay.data == body)

= HTTP/2 HPackHdrTable : Parsing Textual Representation without body
~ http2 hpack hpackhdrtable helpers

hdrs = b''':method POST
:path /login.php
:scheme https
user-agent: Mozilla/5.0 Generated by hand
x-generated-by: Me
x-generation-date: 2016-08-11
'''

h = h2.HPackHdrTable()
h.register(h2.HPackLitHdrFldWithIncrIndexing(
        hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Generation-Date')),
        hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('2016-08-11'))
))

# Without body
seq = h.parse_txt_hdrs(hdrs, stream_id=1)
assert(isinstance(seq, h2.H2Seq))
#This is the first major difference with the first test
assert(len(seq.frames) == 1)
p = seq.frames[0]
assert(isinstance(p, h2.H2Frame))
assert(p.type == 1)
assert(len(p.flags) == 2)
assert('EH' in p.flags)
#This is the second major difference with the first test
assert('ES' in p.flags)
assert(p.stream_id == 1)
assert(isinstance(p.payload, h2.H2HeadersFrame))
hdrs_frm = p[h2.H2HeadersFrame]
assert(len(p.hdrs) == 6)
hdr = p.hdrs[0]
assert(isinstance(hdr, h2.HPackIndexedHdr))
assert(hdr.magic == 1)
assert(hdr.index == 3)
hdr = p.hdrs[1]
assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
assert(hdr.magic == 0)
assert(hdr.never_index == 0)
assert(hdr.index in [4, 5])
assert(hdr.hdr_name is None)
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.data == 'HPackZString(/login.php)')
hdr = p.hdrs[2]
assert(isinstance(hdr, h2.HPackIndexedHdr))
assert(hdr.magic == 1)
assert(hdr.index == 7)
hdr = p.hdrs[3]
assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
assert(hdr.magic == 0)
assert(hdr.never_index == 0)
assert(hdr.index == 58)
assert(hdr.hdr_name is None)
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.data == 'HPackZString(Mozilla/5.0 Generated by hand)')
hdr = p.hdrs[4]
assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
assert(hdr.magic == 0)
assert(hdr.never_index == 0)
assert(hdr.index == 0)
assert(isinstance(hdr.hdr_name, h2.HPackHdrString))
assert(hdr.hdr_name.data == 'HPackZString(x-generated-by)')
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.data == 'HPackLiteralString(Me)')
hdr = p.hdrs[5]
assert(isinstance(hdr, h2.HPackIndexedHdr))
assert(hdr.magic == 1)
assert(hdr.index == 62)


= HTTP/2 HPackHdrTable : Parsing Textual Representation with too small max frame
~ http2 hpack hpackhdrtable helpers

body = b'login=titi&passwd=toto'
hdrs = ''':method POST
:path /login.php
:scheme https
content-type: application/x-www-form-urlencoded
content-length: {}
user-agent: Mozilla/5.0 Generated by hand
x-generated-by: Me
x-generation-date: 2016-08-11
x-long-header: {}
'''.format(len(body), 'a'*5000).encode()

h = h2.HPackHdrTable()
h.register(h2.HPackLitHdrFldWithIncrIndexing(
        hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Generation-Date')),
        hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('2016-08-11'))
))

#x-long-header is too long to fit in any frames (whose default max size is 4096)
expect_exception(Exception, "seq = h.parse_txt_hdrs('''{}''', stream_id=1".format(hdrs))

= HTTP/2 HPackHdrTable : Parsing Textual Representation with very large header and a large authorized frame size
~ http2 hpack hpackhdrtable helpers

body = b'login=titi&passwd=toto'
hdrs = ''':method POST
:path /login.php
:scheme https
content-type: application/x-www-form-urlencoded
content-length: {}
user-agent: Mozilla/5.0 Generated by hand
x-generated-by: Me
x-generation-date: 2016-08-11
x-long-header: {}
'''.format(len(body), 'a'*5000).encode()

h = h2.HPackHdrTable()
h.register(h2.HPackLitHdrFldWithIncrIndexing(
        hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Generation-Date')),
        hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('2016-08-11'))
))

# Now trying to parse it with a max frame size large enough for x-long-header to
# fit in a frame
seq = h.parse_txt_hdrs(hdrs, stream_id=1, max_frm_sz=8192)
assert(isinstance(seq, h2.H2Seq))
assert(len(seq.frames) == 1)
p = seq.frames[0]
assert(isinstance(p, h2.H2Frame))
assert(p.type == 1)
assert(len(p.flags) == 2)
assert('EH' in p.flags)
assert('ES' in p.flags)
assert(p.stream_id == 1)
assert(isinstance(p.payload, h2.H2HeadersFrame))
hdrs_frm = p[h2.H2HeadersFrame]
assert(len(p.hdrs) == 9)
hdr = p.hdrs[0]
assert(isinstance(hdr, h2.HPackIndexedHdr))
assert(hdr.magic == 1)
assert(hdr.index == 3)
hdr = p.hdrs[1]
assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
assert(hdr.magic == 0)
assert(hdr.never_index == 0)
assert(hdr.index in [4, 5])
assert(hdr.hdr_name is None)
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.data == 'HPackZString(/login.php)')
hdr = p.hdrs[2]
assert(isinstance(hdr, h2.HPackIndexedHdr))
assert(hdr.magic == 1)
assert(hdr.index == 7)
hdr = p.hdrs[3]
assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
assert(hdr.magic == 0)
assert(hdr.never_index == 0)
assert(hdr.index == 31)
assert(hdr.hdr_name is None)
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.data == 'HPackZString(application/x-www-form-urlencoded)')
hdr = p.hdrs[4]
assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
assert(hdr.magic == 0)
assert(hdr.never_index == 0)
assert(hdr.index == 28)
assert(hdr.hdr_name is None)
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.data == 'HPackLiteralString(22)')
hdr = p.hdrs[5]
assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
assert(hdr.magic == 0)
assert(hdr.never_index == 0)
assert(hdr.index == 58)
assert(hdr.hdr_name is None)
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.data == 'HPackZString(Mozilla/5.0 Generated by hand)')
hdr = p.hdrs[6]
assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
assert(hdr.magic == 0)
assert(hdr.never_index == 0)
assert(hdr.index == 0)
assert(isinstance(hdr.hdr_name, h2.HPackHdrString))
assert(hdr.hdr_name.data == 'HPackZString(x-generated-by)')
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.data == 'HPackLiteralString(Me)')
hdr = p.hdrs[7]
assert(isinstance(hdr, h2.HPackIndexedHdr))
assert(hdr.magic == 1)
assert(hdr.index == 62)
hdr = p.hdrs[8]
assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
assert(hdr.magic == 0)
assert(hdr.never_index == 0)
assert(hdr.index == 0)
assert(isinstance(hdr.hdr_name, h2.HPackHdrString))
assert(hdr.hdr_name.data == 'HPackZString(x-long-header)')
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.data == 'HPackZString({})'.format('a'*5000))

= HTTP/2 HPackHdrTable : Parsing Textual Representation with two very large headers and a large authorized frame size
~ http2 hpack hpackhdrtable helpers

body = b'login=titi&passwd=toto'
hdrs = ''':method POST
:path /login.php
:scheme https
content-type: application/x-www-form-urlencoded
content-length: {}
user-agent: Mozilla/5.0 Generated by hand
x-generated-by: Me
x-generation-date: 2016-08-11
x-long-header: {}
x-long-header: {}
'''.format(len(body), 'a'*5000, 'b'*5000).encode()

h = h2.HPackHdrTable()
h.register(h2.HPackLitHdrFldWithIncrIndexing(
        hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Generation-Date')),
        hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('2016-08-11'))
))

# Now trying to parse it with a max frame size large enough for x-long-header to
# fit in a frame but a maximum header fragment size that is not large enough to
# store two x-long-header
seq = h.parse_txt_hdrs(hdrs, stream_id=1, max_frm_sz=8192)
assert(isinstance(seq, h2.H2Seq))
assert(len(seq.frames) == 2)
p = seq.frames[0]
assert(isinstance(p, h2.H2Frame))
assert(p.type == 1)
assert(len(p.flags) == 1)
assert('ES' in p.flags)
assert(p.stream_id == 1)
assert(isinstance(p.payload, h2.H2HeadersFrame))
hdrs_frm = p[h2.H2HeadersFrame]
assert(len(p.hdrs) == 9)
hdr = p.hdrs[0]
assert(isinstance(hdr, h2.HPackIndexedHdr))
assert(hdr.magic == 1)
assert(hdr.index == 3)
hdr = p.hdrs[1]
assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
assert(hdr.magic == 0)
assert(hdr.never_index == 0)
assert(hdr.index in [4, 5])
assert(hdr.hdr_name is None)
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.data == 'HPackZString(/login.php)')
hdr = p.hdrs[2]
assert(isinstance(hdr, h2.HPackIndexedHdr))
assert(hdr.magic == 1)
assert(hdr.index == 7)
hdr = p.hdrs[3]
assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
assert(hdr.magic == 0)
assert(hdr.never_index == 0)
assert(hdr.index == 31)
assert(hdr.hdr_name is None)
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.data == 'HPackZString(application/x-www-form-urlencoded)')
hdr = p.hdrs[4]
assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
assert(hdr.magic == 0)
assert(hdr.never_index == 0)
assert(hdr.index == 28)
assert(hdr.hdr_name is None)
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.data == 'HPackLiteralString(22)')
hdr = p.hdrs[5]
assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
assert(hdr.magic == 0)
assert(hdr.never_index == 0)
assert(hdr.index == 58)
assert(hdr.hdr_name is None)
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.data == 'HPackZString(Mozilla/5.0 Generated by hand)')
hdr = p.hdrs[6]
assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
assert(hdr.magic == 0)
assert(hdr.never_index == 0)
assert(hdr.index == 0)
assert(isinstance(hdr.hdr_name, h2.HPackHdrString))
assert(hdr.hdr_name.data == 'HPackZString(x-generated-by)')
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.data == 'HPackLiteralString(Me)')
hdr = p.hdrs[7]
assert(isinstance(hdr, h2.HPackIndexedHdr))
assert(hdr.magic == 1)
assert(hdr.index == 62)
hdr = p.hdrs[8]
assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
assert(hdr.magic == 0)
assert(hdr.never_index == 0)
assert(hdr.index == 0)
assert(isinstance(hdr.hdr_name, h2.HPackHdrString))
assert(hdr.hdr_name.data == 'HPackZString(x-long-header)')
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.data == 'HPackZString({})'.format('a'*5000))
p = seq.frames[1]
assert(isinstance(p, h2.H2Frame))
assert(p.type == 9)
assert(len(p.flags) == 1)
assert('EH' in p.flags)
assert(p.stream_id == 1)
assert(isinstance(p.payload, h2.H2ContinuationFrame))
hdrs_frm = p[h2.H2ContinuationFrame]
assert(len(p.hdrs) == 1)
hdr = p.hdrs[0]
assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
assert(hdr.magic == 0)
assert(hdr.never_index == 0)
assert(hdr.index == 0)
assert(isinstance(hdr.hdr_name, h2.HPackHdrString))
assert(hdr.hdr_name.data == 'HPackZString(x-long-header)')
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.data == 'HPackZString({})'.format('b'*5000))

= HTTP/2 HPackHdrTable : Parsing Textual Representation with two very large headers, a large authorized frame size and a "small" max header list size
~ http2 hpack hpackhdrtable helpers

body = b'login=titi&passwd=toto'
hdrs = ''':method POST
:path /login.php
:scheme https
content-type: application/x-www-form-urlencoded
content-length: {}
user-agent: Mozilla/5.0 Generated by hand
x-generated-by: Me
x-generation-date: 2016-08-11
x-long-header: {}
x-long-header: {}
'''.format(len(body), 'a'*5000, 'b'*5000).encode()

h = h2.HPackHdrTable()
h.register(h2.HPackLitHdrFldWithIncrIndexing(
        hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Generation-Date')),
        hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('2016-08-11'))
))

# Now trying to parse it with a max frame size large enough for x-long-header to
# fit in a frame but and a max header list size that is large enough to fit one
# but not two
seq = h.parse_txt_hdrs(hdrs, stream_id=1, max_frm_sz=8192, max_hdr_lst_sz=5050)
assert(isinstance(seq, h2.H2Seq))
assert(len(seq.frames) == 3)
p = seq.frames[0]
assert(isinstance(p, h2.H2Frame))
assert(p.type == 1)
assert(len(p.flags) == 1)
assert('ES' in p.flags)
assert(p.stream_id == 1)
assert(isinstance(p.payload, h2.H2HeadersFrame))
hdrs_frm = p[h2.H2HeadersFrame]
assert(len(p.hdrs) == 8)
hdr = p.hdrs[0]
assert(isinstance(hdr, h2.HPackIndexedHdr))
assert(hdr.magic == 1)
assert(hdr.index == 3)
hdr = p.hdrs[1]
assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
assert(hdr.magic == 0)
assert(hdr.never_index == 0)
assert(hdr.index in [4, 5])
assert(hdr.hdr_name is None)
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.data == 'HPackZString(/login.php)')
hdr = p.hdrs[2]
assert(isinstance(hdr, h2.HPackIndexedHdr))
assert(hdr.magic == 1)
assert(hdr.index == 7)
hdr = p.hdrs[3]
assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
assert(hdr.magic == 0)
assert(hdr.never_index == 0)
assert(hdr.index == 31)
assert(hdr.hdr_name is None)
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.data == 'HPackZString(application/x-www-form-urlencoded)')
hdr = p.hdrs[4]
assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
assert(hdr.magic == 0)
assert(hdr.never_index == 0)
assert(hdr.index == 28)
assert(hdr.hdr_name is None)
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.data == 'HPackLiteralString(22)')
hdr = p.hdrs[5]
assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
assert(hdr.magic == 0)
assert(hdr.never_index == 0)
assert(hdr.index == 58)
assert(hdr.hdr_name is None)
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.data == 'HPackZString(Mozilla/5.0 Generated by hand)')
hdr = p.hdrs[6]
assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
assert(hdr.magic == 0)
assert(hdr.never_index == 0)
assert(hdr.index == 0)
assert(isinstance(hdr.hdr_name, h2.HPackHdrString))
assert(hdr.hdr_name.data == 'HPackZString(x-generated-by)')
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.data == 'HPackLiteralString(Me)')
hdr = p.hdrs[7]
assert(isinstance(hdr, h2.HPackIndexedHdr))
assert(hdr.magic == 1)
assert(hdr.index == 62)
p = seq.frames[1]
assert(isinstance(p, h2.H2Frame))
assert(p.type == 9)
assert(len(p.flags) == 0)
assert(p.stream_id == 1)
assert(isinstance(p.payload, h2.H2ContinuationFrame))
hdrs_frm = p[h2.H2ContinuationFrame]
assert(len(p.hdrs) == 1)
hdr = p.hdrs[0]
assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
assert(hdr.magic == 0)
assert(hdr.never_index == 0)
assert(hdr.index == 0)
assert(isinstance(hdr.hdr_name, h2.HPackHdrString))
assert(hdr.hdr_name.data == 'HPackZString(x-long-header)')
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.data == 'HPackZString({})'.format('a'*5000))
p = seq.frames[2]
assert(isinstance(p, h2.H2Frame))
assert(p.type == 9)
assert(len(p.flags) == 1)
assert('EH' in p.flags)
assert(p.stream_id == 1)
assert(isinstance(p.payload, h2.H2ContinuationFrame))
hdrs_frm = p[h2.H2ContinuationFrame]
assert(len(p.hdrs) == 1)
hdr = p.hdrs[0]
assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
assert(hdr.magic == 0)
assert(hdr.never_index == 0)
assert(hdr.index == 0)
assert(isinstance(hdr.hdr_name, h2.HPackHdrString))
assert(hdr.hdr_name.data == 'HPackZString(x-long-header)')
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.data == 'HPackZString({})'.format('b'*5000))

= HTTP/2 HPackHdrTable : Parsing Textual Representation with sensitive headers and non-indexable ones
~ http2 hpack hpackhdrtable helpers

hdrs = ''':method POST
:path /login.php
:scheme https
content-type: application/x-www-form-urlencoded
content-length: {}
user-agent: Mozilla/5.0 Generated by hand
x-generated-by: Me
x-generation-date: 2016-08-11
'''.format(len(body)).encode()

h = h2.HPackHdrTable()
seq = h.parse_txt_hdrs(hdrs, stream_id=1, body=body, is_sensitive=lambda n,v: n in ['x-generation-date'], should_index=lambda x: x != 'x-generated-by')
assert(isinstance(seq, h2.H2Seq))
assert(len(seq.frames) == 2)
p = seq.frames[0]
assert(isinstance(p, h2.H2Frame))
assert(p.type == 1)
assert(len(p.flags) == 1)
assert('EH' in p.flags)
assert(p.stream_id == 1)
assert(isinstance(p.payload, h2.H2HeadersFrame))
hdrs_frm = p[h2.H2HeadersFrame]
assert(len(p.hdrs) == 8)
hdr = p.hdrs[0]
assert(isinstance(hdr, h2.HPackIndexedHdr))
assert(hdr.magic == 1)
assert(hdr.index == 3)
hdr = p.hdrs[1]
assert(isinstance(hdr, h2.HPackLitHdrFldWithIncrIndexing))
assert(hdr.magic == 1)
assert(hdr.index in [4, 5])
assert(hdr.hdr_name is None)
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.data == 'HPackZString(/login.php)')
hdr = p.hdrs[2]
assert(isinstance(hdr, h2.HPackIndexedHdr))
assert(hdr.magic == 1)
assert(hdr.index == 7)
hdr = p.hdrs[3]
assert(isinstance(hdr, h2.HPackLitHdrFldWithIncrIndexing))
assert(hdr.magic == 1)
assert(hdr.index == 31)
assert(hdr.hdr_name is None)
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.data == 'HPackZString(application/x-www-form-urlencoded)')
hdr = p.hdrs[4]
assert(isinstance(hdr, h2.HPackLitHdrFldWithIncrIndexing))
assert(hdr.magic == 1)
assert(hdr.index == 28)
assert(hdr.hdr_name is None)
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.data == 'HPackLiteralString(22)')
hdr = p.hdrs[5]
assert(isinstance(hdr, h2.HPackLitHdrFldWithIncrIndexing))
assert(hdr.magic == 1)
assert(hdr.index == 58)
assert(hdr.hdr_name is None)
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.data == 'HPackZString(Mozilla/5.0 Generated by hand)')
hdr = p.hdrs[6]
assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
assert(hdr.magic == 0)
assert(hdr.never_index == 0)
assert(hdr.index == 0)
assert(isinstance(hdr.hdr_name, h2.HPackHdrString))
assert(hdr.hdr_name.data == 'HPackZString(x-generated-by)')
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.data == 'HPackLiteralString(Me)')
hdr = p.hdrs[7]
assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
assert(hdr.magic == 0)
assert(hdr.never_index == 1)
assert(hdr.index == 0)
assert(isinstance(hdr.hdr_name, h2.HPackHdrString))
assert(hdr.hdr_name.data == 'HPackZString(x-generation-date)')
assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
assert(hdr.hdr_value.data == 'HPackZString(2016-08-11)')
p = seq.frames[1]
assert(isinstance(p, h2.H2Frame))
assert(p.type == 0)
assert(len(p.flags) == 1)
assert('ES' in p.flags)
assert(p.stream_id == 1)
assert(isinstance(p.payload, h2.H2DataFrame))
pay = p[h2.H2DataFrame]
assert(pay.data == body)
