Beispiel #1
0
        def socket_handler(listener):
            sock = listener.accept()[0]

            e = Encoder()
            e.huffman_coder = HuffmanEncoder(REQUEST_CODES, REQUEST_CODES_LENGTH)

            # We get two messages for the connection open and then a HEADERS
            # frame.
            receive_preamble(sock)

            # Now, send the headers for the response. This response has no body.
            f = build_headers_frame([(':status', '200'), ('content-length', '0')], e)
            f.stream_id = 1
            sock.send(f.serialize())

            # Also send a data frame.
            f = DataFrame(1)
            f.data = b'have some data'
            sock.send(f.serialize())

            # Now, send a headers frame again, containing trailing headers.
            f = build_headers_frame([('trailing', 'sure'), (':res', 'no')], e)
            f.flags.add('END_STREAM')
            f.stream_id = 1
            sock.send(f.serialize())

            # Wait for the message from the main thread.
            recv_event.wait()
            sock.close()
Beispiel #2
0
 def get_encoder(self):
     """
     Returns a HPACK encoder set up for responses.
     """
     e = Encoder()
     e.huffman_coder = HuffmanEncoder(REQUEST_CODES, REQUEST_CODES_LENGTH)
     return e
Beispiel #3
0
def build_headers_frame(headers):
    f = HeadersFrame(1)
    e = Encoder()
    e.huffman_coder = HuffmanEncoder(RESPONSE_CODES, RESPONSE_CODES_LENGTH)
    f.data = e.encode(headers)
    f.flags.add('END_HEADERS')
    return f
Beispiel #4
0
def build_headers_frame(headers):
    f = HeadersFrame(1)
    e = Encoder()
    e.huffman_coder = HuffmanEncoder(RESPONSE_CODES, RESPONSE_CODES_LENGTH)
    f.data = e.encode(headers)
    f.flags.add('END_HEADERS')
    return f
Beispiel #5
0
    def test_indexed_header_field_from_static_table(self):
        e = Encoder()
        e.header_table_size = 0
        header_set = {':method': 'GET'}
        result = b'\x82'

        assert e.encode(header_set, huffman=False) == result
        assert list(e.header_table) == []
Beispiel #6
0
    def test_indexed_header_field_from_static_table(self):
        e = Encoder()
        e.header_table_size = 0
        header_set = {':method': 'GET'}
        result = b'\x82'

        assert e.encode(header_set, huffman=False) == result
        assert list(e.header_table) == []
Beispiel #7
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.
        """
        e = Encoder()
        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_result = (
            b'\x82\x87\x86\x44\x8b\xdb\x6d\x88\x3e\x68\xd1\xcb\x12\x25\xba\x7f'
        )

        assert e.encode(first_header_set, huffman=True) == first_result
        assert list(e.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_result = b'\x44\x8b\xdb\x6d\x88\x3e\x68\xd1\xcb\x12\x25\xba\x7f\x5a\x86\x63\x65\x4a\x13\x98\xff'

        assert e.encode(second_header_set, huffman=True) == second_result
        assert list(e.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_result = (
            b'\x80\x83\x8a\x89F\x8b\xdbm\x88>h\xd1\xcb\x12%\xba\x7f\x00\x88N'
            b'\xb0\x8bt\x97\x90\xfa\x7f\x89N\xb0\x8bt\x97\x9a\x17\xa8\xff'
        )

        assert e.encode(third_header_set, huffman=True) == third_result
        # Don't check the header table here, it's just too complex to be
        # reliable. Check its length though.
        assert len(e.header_table) == 6
Beispiel #8
0
def build_headers_frame(headers, encoder=None):
    f = HeadersFrame(1)
    e = encoder
    if e is None:
        e = Encoder()
        e.huffman_coder = HuffmanEncoder(REQUEST_CODES, REQUEST_CODES_LENGTH)
    f.data = e.encode(headers)
    f.flags.add('END_HEADERS')
    return f
Beispiel #9
0
    def test_request_examples_without_huffman(self):
        """
        This section shows several consecutive header sets, corresponding to
        HTTP requests, on the same connection.
        """
        e = Encoder()
        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_result = b'\x82\x87\x86\x44\x0fwww.example.com'

        assert e.encode(first_header_set, huffman=False) == first_result
        assert list(e.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_result = b'\x44\x0fwww.example.com\x5a\x08no-cache'

        assert e.encode(second_header_set, huffman=False) == second_result
        assert list(e.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_result = (
            b'\x80\x83\x8a\x89\x46\x0fwww.example.com' +
            b'\x00\x0acustom-key\x0ccustom-value'
        )

        assert e.encode(third_header_set, huffman=False) == third_result
        # Don't check the header table here, it's just too complex to be
        # reliable. Check its length though.
        assert len(e.header_table) == 6
Beispiel #10
0
    def test_literal_header_field_without_indexing(self):
        """
        The header field representation uses an indexed name and a literal
        value.
        """
        e = Encoder()
        header_set = {':path': '/sample/path'}
        result = b'\x44\x0c/sample/path'

        assert e.encode(header_set, huffman=False) == result
        assert list(e.header_table) == []
Beispiel #11
0
    def test_literal_header_field_with_indexing(self):
        """
        The header field representation uses a literal name and a literal
        value.
        """
        e = Encoder()
        header_set = {"custom-key": "custom-header"}
        result = b"\x00\x0acustom-key\x0dcustom-header"

        assert e.encode(header_set, huffman=False) == result
        assert list(e.header_table) == [(n.encode("utf-8"), v.encode("utf-8")) for n, v in header_set.items()]
Beispiel #12
0
def hpack():
    """
    This task generates HPACK test data suitable for use with
    https://github.com/http2jp/hpack-test-case

    The current format defines a JSON object with three keys: 'draft',
    'description' and 'cases'.

    The cases key has as its value a list of objects, with each object
    representing a set of headers and the output from the encoder. The object
    has the following keys:

    - 'header_table_size': the size of the header table used.
    - 'headers': A list of the headers as JSON objects.
    - 'wire': The output from the encoder in hexadecimal.
    """
    # A generator that contains the paths to all the raw data files and their
    # names.
    raw_story_files = ((os.path.join('test_fixtures/raw-data', name), name)
                       for name in os.listdir('test_fixtures/raw-data'))

    # For each file, build our output.
    for source, outname in raw_story_files:
        with open(source, 'rb') as f:
            indata = json.loads(f.read())

        # Prepare the output and the encoder.
        output = {
            'draft': DRAFT,
            'description':
            'Encoded by hyper. See github.com/Lukasa/hyper for more information.',
            'cases': []
        }
        e = Encoder()

        for case in indata['cases']:
            outcase = {'header_table_size': e.header_table_size}
            outcase['headers'] = case['headers']
            headers = []

            for header in case['headers']:
                key = header.keys()[0]
                header = (key, header[key])
                headers.append(header)

            outcase['wire'] = hexlify(e.encode(headers))
            output['cases'].append(outcase)

        with open(outname, 'wb') as f:
            f.write(
                json.dumps(output,
                           sort_keys=True,
                           indent=2,
                           separators=(',', ': ')))
Beispiel #13
0
    def test_literal_header_field_without_indexing(self):
        """
        The header field representation uses an indexed name and a literal
        value.
        """
        e = Encoder()
        header_set = {':path': '/sample/path'}
        result = b'\x44\x0c/sample/path'

        assert e.encode(header_set, huffman=False) == result
        assert list(e.header_table) == []
Beispiel #14
0
def hpack():
    """
    This task generates HPACK test data suitable for use with
    https://github.com/http2jp/hpack-test-case

    The current format defines a JSON object with three keys: 'draft',
    'description' and 'cases'.

    The cases key has as its value a list of objects, with each object
    representing a set of headers and the output from the encoder. The object
    has the following keys:

    - 'header_table_size': the size of the header table used.
    - 'headers': A list of the headers as JSON objects.
    - 'wire': The output from the encoder in hexadecimal.
    """
    # A generator that contains the paths to all the raw data files and their
    # names.
    raw_story_files = (
        (os.path.join('test/test_fixtures/raw-data', name), name)
        for name in os.listdir('test/test_fixtures/raw-data')
    )

    # For each file, build our output.
    for source, outname in raw_story_files:
        with open(source, 'rb') as f:
            indata = json.load(f)

        # Prepare the output and the encoder.
        output = {
            'description': 'Encoded by hyper. See github.com/Lukasa/hyper for more information.',
            'cases': []
        }
        e = Encoder()

        for case in indata['cases']:
            outcase = {
                'header_table_size': e.header_table_size,
                'headers': case['headers'],
            }
            headers = []

            for header in case['headers']:
                key = header.keys()[0]
                header = (key, header[key])
                headers.append(header)

            outcase['wire'] = hexlify(e.encode(headers))
            output['cases'].append(outcase)

        with open(outname, 'wb') as f:
            f.write(json.dumps(output, sort_keys=True,
                    indent=2, separators=(',', ': ')))
    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
Beispiel #16
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.
        """
        e = Encoder()
        header_set = {":method": "GET"}
        result = b"\x82"

        assert e.encode(header_set, huffman=False) == result
        assert list(e.header_table) == [(n.encode("utf-8"), v.encode("utf-8")) for n, v in header_set.items()]
Beispiel #17
0
    def test_literal_header_field_with_indexing(self):
        """
        The header field representation uses a literal name and a literal
        value.
        """
        e = Encoder()
        header_set = {'custom-key': 'custom-header'}
        result = b'\x00\x0acustom-key\x0dcustom-header'

        assert e.encode(header_set, huffman=False) == result
        assert list(e.header_table) == [(n.encode('utf-8'), v.encode('utf-8'))
                                        for n, v in header_set.items()]
Beispiel #18
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.
        """
        e = Encoder()
        header_set = {':method': 'GET'}
        result = b'\x82'

        assert e.encode(header_set, huffman=False) == result
        assert list(e.header_table) == [(n.encode('utf-8'), v.encode('utf-8'))
                                        for n, v in header_set.items()]
Beispiel #19
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
Beispiel #20
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
Beispiel #21
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
Beispiel #22
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.
        """
        e = Encoder()
        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_result = (
            b'\x82\x87\x86\x44\x8b\xdb\x6d\x88\x3e\x68\xd1\xcb\x12\x25\xba\x7f'
        )

        assert e.encode(first_header_set, huffman=True) == first_result
        assert list(e.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_result = b'\x44\x8b\xdb\x6d\x88\x3e\x68\xd1\xcb\x12\x25\xba\x7f\x5a\x86\x63\x65\x4a\x13\x98\xff'

        assert e.encode(second_header_set, huffman=True) == second_result
        assert list(e.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_result = (
            b'\x80\x83\x8a\x89F\x8b\xdbm\x88>h\xd1\xcb\x12%\xba\x7f\x00\x88N'
            b'\xb0\x8bt\x97\x90\xfa\x7f\x89N\xb0\x8bt\x97\x9a\x17\xa8\xff')

        assert e.encode(third_header_set, huffman=True) == third_result
        # Don't check the header table here, it's just too complex to be
        # reliable. Check its length though.
        assert len(e.header_table) == 6
Beispiel #23
0
    def test_request_examples_without_huffman(self):
        """
        This section shows several consecutive header sets, corresponding to
        HTTP requests, on the same connection.
        """
        e = Encoder()
        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_result = b'\x82\x87\x86\x44\x0fwww.example.com'

        assert e.encode(first_header_set, huffman=False) == first_result
        assert list(e.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_result = b'\x44\x0fwww.example.com\x5a\x08no-cache'

        assert e.encode(second_header_set, huffman=False) == second_result
        assert list(e.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_result = (b'\x80\x83\x8a\x89\x46\x0fwww.example.com' +
                        b'\x00\x0acustom-key\x0ccustom-value')

        assert e.encode(third_header_set, huffman=False) == third_result
        # Don't check the header table here, it's just too complex to be
        # reliable. Check its length though.
        assert len(e.header_table) == 6