def test_request_examples_without_huffman(self): """ This section shows several consecutive header sets, corresponding to HTTP requests, on the same connection. """ d = Decoder() first_header_set = [ (':method', 'GET',), (':scheme', 'http',), (':path', '/',), (':authority', 'www.example.com'), ] # The first_header_table doesn't contain 'authority' first_header_table = first_header_set[::-1][1:] first_data = b'\x82\x87\x86\x44\x0fwww.example.com' assert d.decode(first_data) == set(first_header_set) assert list(d.header_table) == [ (n.encode('utf-8'), v.encode('utf-8')) for n, v in first_header_table ] # This request takes advantage of the differential encoding of header # sets. second_header_set = [ (':method', 'GET',), (':scheme', 'http',), (':path', '/',), (':authority', 'www.example.com',), ('cache-control', 'no-cache'), ] second_data = b'\x44\x0fwww.example.com\x5a\x08no-cache' assert d.decode(second_data) == set(second_header_set) assert list(d.header_table) == [ (n.encode('utf-8'), v.encode('utf-8')) for n, v in first_header_table ] # This request has not enough headers in common with the previous # request to take advantage of the differential encoding. Therefore, # the reference set is emptied before encoding the header fields. third_header_set = [ (':method', 'GET',), (':scheme', 'https',), (':path', '/index.html',), (':authority', 'www.example.com',), ('custom-key', 'custom-value'), ] third_data = ( b'\x80\x83\x8a\x89\x46\x0fwww.example.com' + b'\x00\x0acustom-key\x0ccustom-value' ) assert d.decode(third_data) == set(third_header_set) # Don't check the header table here, it's just too complex to be # reliable. Check its length though. assert len(d.header_table) == 6
def test_request_examples_with_huffman(self): """ This section shows the same examples as the previous section, but using Huffman encoding for the literal values. """ d = Decoder() # Patch the decoder to use the Request Huffman tables, not the Response # ones. d.huffman_coder = HuffmanDecoder(REQUEST_CODES, REQUEST_CODES_LENGTH) first_header_set = [(":method", "GET"), (":scheme", "http"), (":path", "/"), (":authority", "www.example.com")] first_header_table = first_header_set[::-1] first_data = b"\x82\x87\x86\x04\x8b\xdb\x6d\x88\x3e\x68\xd1\xcb\x12\x25\xba\x7f" assert d.decode(first_data) == set(first_header_set) assert list(d.header_table) == [(n.encode("utf-8"), v.encode("utf-8")) for n, v in first_header_table] # This request takes advantage of the differential encoding of header # sets. second_header_set = [ (":method", "GET"), (":scheme", "http"), (":path", "/"), (":authority", "www.example.com"), ("cache-control", "no-cache"), ] second_header_table = second_header_set[::-1] second_data = b"\x1b\x86\x63\x65\x4a\x13\x98\xff" assert d.decode(second_data) == set(second_header_set) assert list(d.header_table) == [(n.encode("utf-8"), v.encode("utf-8")) for n, v in second_header_table] # This request has not enough headers in common with the previous # request to take advantage of the differential encoding. Therefore, # the reference set is emptied before encoding the header fields. third_header_set = [ (":method", "GET"), (":scheme", "https"), (":path", "/index.html"), (":authority", "www.example.com"), ("custom-key", "custom-value"), ] third_data = ( b"\x80\x85\x8c\x8b\x84\x00\x88\x4e\xb0\x8b\x74\x97\x90\xfa\x7f\x89" b"\x4e\xb0\x8b\x74\x97\x9a\x17\xa8\xff" ) assert d.decode(third_data) == set(third_header_set) # Don't check the header table here, it's just too complex to be # reliable. Check its length though. assert len(d.header_table) == 8
def test_literal_header_field_without_indexing(self): """ The header field representation uses an indexed name and a literal value. """ d = Decoder() header_set = set([(':path', '/sample/path')]) data = b'\x44\x0c/sample/path' assert d.decode(data) == header_set assert list(d.header_table) == []
def test_literal_header_field_with_indexing(self): """ The header field representation uses a literal name and a literal value. """ d = Decoder() header_set = set([("custom-key", "custom-header")]) data = b"\x00\x0acustom-key\x0dcustom-header" assert d.decode(data) == header_set assert list(d.header_table) == [(n.encode("utf-8"), v.encode("utf-8")) for n, v in header_set]
def test_literal_header_field_with_indexing(self): """ The header field representation uses a literal name and a literal value. """ d = Decoder() header_set = set([('custom-key', 'custom-header')]) data = b'\x00\x0acustom-key\x0dcustom-header' assert d.decode(data) == header_set assert list(d.header_table) == [(n.encode('utf-8'), v.encode('utf-8')) for n, v in header_set]
def test_indexed_header_field(self): """ The header field representation uses an indexed header field, from the static table. Upon using it, the static table entry is copied into the header table. """ d = Decoder() header_set = set([(":method", "GET")]) data = b"\x82" assert d.decode(data) == header_set assert list(d.header_table) == [(n.encode("utf-8"), v.encode("utf-8")) for n, v in header_set]
def test_can_encode_a_story_with_huffman(self, raw_story): d = Decoder() e = Encoder() for case in raw_story['cases']: # The input headers are a list of dicts, which is annoying. input_headers = [(item[0], item[1]) for header in case['headers'] for item in header.items()] encoded = e.encode(input_headers, huffman=True) decoded_headers = d.decode(encoded) assert input_headers == decoded_headers
def test_indexed_header_field(self): """ The header field representation uses an indexed header field, from the static table. Upon using it, the static table entry is copied into the header table. """ d = Decoder() header_set = set([(':method', 'GET')]) data = b'\x82' assert d.decode(data) == header_set assert list(d.header_table) == [(n.encode('utf-8'), v.encode('utf-8')) for n, v in header_set]
def test_can_decode_a_story(self, story): d = Decoder() # We support draft 5 of the HPACK spec. assert story['draft'] == 5 if story['context'] == 'request': d.huffman_coder = HuffmanDecoder(REQUEST_CODES, REQUEST_CODES_LENGTH) for case in story['cases']: d.header_table_size = case['header_table_size'] decoded_headers = d.decode(unhexlify(case['wire'])) # The correct headers are a list of dicts, which is annoying. correct_headers = {(item[0], item[1]) for header in case['headers'] for item in header.items()} assert correct_headers == decoded_headers
def test_can_encode_a_story_with_huffman(self, raw_story): d = Decoder() e = Encoder() if raw_story['context'] == 'request': d.huffman_coder = HuffmanDecoder(REQUEST_CODES, REQUEST_CODES_LENGTH) else: e.huffman_coder = HuffmanEncoder(RESPONSE_CODES, RESPONSE_CODES_LENGTH) for case in raw_story['cases']: # The input headers are a list of dicts, which is annoying. input_headers = {(item[0], item[1]) for header in case['headers'] for item in header.items()} encoded = e.encode(input_headers, huffman=True) decoded_headers = d.decode(encoded) assert input_headers == decoded_headers
def test_can_decode_a_story(self, story): d = Decoder() # We support draft 9 of the HPACK spec. if story['draft'] != 9: skip("We support draft 9, not draft %d" % story['draft']) for case in story['cases']: try: d.header_table_size = case['header_table_size'] except KeyError: pass decoded_headers = d.decode(unhexlify(case['wire'])) # The correct headers are a list of dicts, which is annoying. correct_headers = [(item[0], item[1]) for header in case['headers'] for item in header.items()] correct_headers = correct_headers assert correct_headers == decoded_headers
def test_request_examples_with_huffman(self): """ This section shows the same examples as the previous section, but using Huffman encoding for the literal values. """ d = Decoder() # Patch the decoder to use the Request Huffman tables, not the Response # ones. d.huffman_coder = HuffmanDecoder(REQUEST_CODES, REQUEST_CODES_LENGTH) first_header_set = [ ( ':method', 'GET', ), ( ':scheme', 'http', ), ( ':path', '/', ), (':authority', 'www.example.com'), ] first_header_table = first_header_set[::-1] first_data = ( b'\x82\x87\x86\x04\x8b\xdb\x6d\x88\x3e\x68\xd1\xcb\x12\x25\xba\x7f' ) assert d.decode(first_data) == set(first_header_set) assert list(d.header_table) == [(n.encode('utf-8'), v.encode('utf-8')) for n, v in first_header_table] # This request takes advantage of the differential encoding of header # sets. second_header_set = [ ( ':method', 'GET', ), ( ':scheme', 'http', ), ( ':path', '/', ), ( ':authority', 'www.example.com', ), ('cache-control', 'no-cache'), ] second_header_table = second_header_set[::-1] second_data = b'\x1b\x86\x63\x65\x4a\x13\x98\xff' assert d.decode(second_data) == set(second_header_set) assert list(d.header_table) == [(n.encode('utf-8'), v.encode('utf-8')) for n, v in second_header_table] # This request has not enough headers in common with the previous # request to take advantage of the differential encoding. Therefore, # the reference set is emptied before encoding the header fields. third_header_set = [ ( ':method', 'GET', ), ( ':scheme', 'https', ), ( ':path', '/index.html', ), ( ':authority', 'www.example.com', ), ('custom-key', 'custom-value'), ] third_data = ( b'\x80\x85\x8c\x8b\x84\x00\x88\x4e\xb0\x8b\x74\x97\x90\xfa\x7f\x89' b'\x4e\xb0\x8b\x74\x97\x9a\x17\xa8\xff') assert d.decode(third_data) == set(third_header_set) # Don't check the header table here, it's just too complex to be # reliable. Check its length though. assert len(d.header_table) == 8
def test_request_examples_without_huffman(self): """ This section shows several consecutive header sets, corresponding to HTTP requests, on the same connection. """ d = Decoder() first_header_set = [ ( ':method', 'GET', ), ( ':scheme', 'http', ), ( ':path', '/', ), (':authority', 'www.example.com'), ] # The first_header_table doesn't contain 'authority' first_header_table = first_header_set[::-1][1:] first_data = b'\x82\x87\x86\x44\x0fwww.example.com' assert d.decode(first_data) == set(first_header_set) assert list(d.header_table) == [(n.encode('utf-8'), v.encode('utf-8')) for n, v in first_header_table] # This request takes advantage of the differential encoding of header # sets. second_header_set = [ ( ':method', 'GET', ), ( ':scheme', 'http', ), ( ':path', '/', ), ( ':authority', 'www.example.com', ), ('cache-control', 'no-cache'), ] second_data = b'\x44\x0fwww.example.com\x5a\x08no-cache' assert d.decode(second_data) == set(second_header_set) assert list(d.header_table) == [(n.encode('utf-8'), v.encode('utf-8')) for n, v in first_header_table] # This request has not enough headers in common with the previous # request to take advantage of the differential encoding. Therefore, # the reference set is emptied before encoding the header fields. third_header_set = [ ( ':method', 'GET', ), ( ':scheme', 'https', ), ( ':path', '/index.html', ), ( ':authority', 'www.example.com', ), ('custom-key', 'custom-value'), ] third_data = (b'\x80\x83\x8a\x89\x46\x0fwww.example.com' + b'\x00\x0acustom-key\x0ccustom-value') assert d.decode(third_data) == set(third_header_set) # Don't check the header table here, it's just too complex to be # reliable. Check its length though. assert len(d.header_table) == 6