Example #1
0
    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
Example #2
0
    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) == []
Example #3
0
    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) == []
Example #4
0
    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]
Example #5
0
    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_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
Example #7
0
    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]
Example #8
0
    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]
Example #9
0
    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
Example #10
0
    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
Example #11
0
    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
Example #12
0
    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
Example #13
0
    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
Example #14
0
    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
Example #15
0
    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
Example #16
0
    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
Example #17
0
    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
Example #18
0
    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