Beispiel #1
0
 def test_parse_frame_header_unknown_type_strict(self):
     with pytest.raises(UnknownFrameError) as excinfo:
         Frame.parse_frame_header(b'\x00\x00\x59\xFF\x00\x00\x00\x00\x01',
                                  strict=True)
     exception = excinfo.value
     assert exception.frame_type == 0xFF
     assert exception.length == 0x59
     assert str(exception) == (
         "UnknownFrameError: Unknown frame type 0xFF received, "
         "length 89 bytes")
Beispiel #2
0
    def test_parse_frame_header_unknown_type(self):
        with pytest.raises(UnknownFrameError) as excinfo:
            Frame.parse_frame_header(b'\x00\x00\x59\xFF\x00\x00\x00\x00\x01')

        exception = excinfo.value
        assert exception.frame_type == 0xFF
        assert exception.length == 0x59
        assert str(exception) == (
            "UnknownFrameError: Unknown frame type 0xFF received, "
            "length 89 bytes"
        )
Beispiel #3
0
    def next(self):  # Python 2
        if len(self.data) < 9:
            raise StopIteration()

        try:
            f, length = Frame.parse_frame_header(self.data[:9])
        except UnknownFrameError as e:
            # Here we do something a bit odd. We want to consume the frame data
            # as consistently as possible, but we also don't ever want to yield
            # None. Instead, we make sure that, if there is no frame, we
            # recurse into ourselves.
            length = e.length
            f = None

        if len(self.data) < length + 9:
            raise StopIteration()

        # Don't try to parse the body if we didn't get a frame we know about:
        # there's nothing we can do with it anyway.
        if f is not None:
            f.parse_body(memoryview(self.data[9:9+length]))

        self.data = self.data[9+length:]

        # If we got a frame we didn't understand, rather than return None it'd
        # be better if we just tried to get the next frame in the sequence
        # instead. Recurse back into ourselves to do that.
        # This is safe because the amount of work we have to do here is
        # strictly bounded by the length of the buffer.
        return f if f is not None else self.next()
Beispiel #4
0
 def test_parse_frame_header_unknown_type(self):
     frame, length = Frame.parse_frame_header(
         b'\x00\x00\x59\xFF\x00\x00\x00\x00\x01')
     assert frame.type == 0xFF
     assert length == 0x59
     assert isinstance(frame, ExtensionFrame)
     assert frame.stream_id == 1
Beispiel #5
0
    def next(self):  # Python 2
        if len(self.data) < 9:
            raise StopIteration()

        try:
            f, length = Frame.parse_frame_header(self.data[:9])
        except UnknownFrameError as e:
            # Here we do something a bit odd. We want to consume the frame data
            # as consistently as possible, but we also don't ever want to yield
            # None. Instead, we make sure that, if there is no frame, we
            # recurse into ourselves.
            length = e.length
            f = None

        if len(self.data) < length + 9:
            raise StopIteration()

        # Don't try to parse the body if we didn't get a frame we know about:
        # there's nothing we can do with it anyway.
        if f is not None:
            f.parse_body(memoryview(self.data[9:9 + length]))

        self.data = self.data[9 + length:]

        # If we got a frame we didn't understand, rather than return None it'd
        # be better if we just tried to get the next frame in the sequence
        # instead. Recurse back into ourselves to do that.
        # This is safe because the amount of work we have to do here is
        # strictly bounded by the length of the buffer.
        return f if f is not None else self.next()
Beispiel #6
0
 def test_flags_are_persisted(self):
     f, l = Frame.parse_frame_header(
         b'\x00\x00\x59\xFF\x09\x00\x00\x00\x01'
     )
     assert f.type == 0xFF
     assert l == 0x59
     assert f.flag_byte == 0x09
    def _consume_single_frame(self):
        try:
            header = self._sock.recv(9)
        except Exception as e:
            self.logger.debug("%s _consume_single_frame:%r, inactive time:%d",
                              self.ip, e,
                              time.time() - self.last_recv_time)
            self.close("ConnectionReset:%r" % e)
            return
        self.last_recv_time = time.time()

        # Parse the header. We can use the returned memoryview directly here.
        frame, length = Frame.parse_frame_header(header)

        if length > FRAME_MAX_ALLOWED_LEN:
            self.logger.error(
                "%s Frame size exceeded on stream %d (received: %d, max: %d)",
                self.ip, frame.stream_id, length, FRAME_MAX_LEN)
            # self._send_rst_frame(frame.stream_id, 6) # 6 = FRAME_SIZE_ERROR

        try:
            data = self._recv_payload(length)
        except Exception as e:
            self.close("ConnectionReset:%r" % e)
            return

        self._consume_frame_payload(frame, data)
Beispiel #8
0
 def test_parse_frame_header_unknown_type(self):
     f, l = Frame.parse_frame_header(
         b'\x00\x00\x59\xFF\x00\x00\x00\x00\x01'
     )
     assert f.type == 0xFF
     assert l == 0x59
     assert isinstance(f, ExtensionFrame)
     assert f.stream_id == 1
Beispiel #9
0
    def _parse_frame_header(self, data):
        """
        Parses the frame header from the data. Either returns a tuple of
        (frame, length), or throws an exception. The returned frame may be None
        if the frame is of unknown type.
        """
        try:
            frame, length = Frame.parse_frame_header(data[:9])
        except ValueError as e:
            # The frame header is invalid. This is a ProtocolError
            raise ProtocolError("Invalid frame header received: %s" % str(e))

        return frame, length
Beispiel #10
0
    def test(self, tc_filepath):
        with open(os.path.join(root, tc_filepath)) as f:
            tc = json.load(f)

        data = bytes.fromhex(tc["wire"])

        if tc["error"] is None and tc["frame"]:
            check_valid_frame(tc, data)
        elif tc["error"] and tc["frame"] is None:
            with pytest.raises(Exception):
                new_frame, length = Frame.parse_frame_header(
                    data[:9],
                    strict=True
                )
                new_frame.parse_body(memoryview(data[9:9 + length]))
                assert length == new_frame.body_len
        else:
            pytest.fail("unexpected test case: {} {}".format(tc_filepath, tc))
Beispiel #11
0
def check_valid_frame(tc, data):  # noqa: C901
    new_frame, length = Frame.parse_frame_header(data[:9], strict=True)
    new_frame.parse_body(memoryview(data[9:9 + length]))

    assert tc["frame"]["length"] == length
    assert tc["frame"]["stream_identifier"] == new_frame.stream_id
    assert tc["frame"]["type"] == new_frame.type

    flags = 0
    for flag, flag_bit in new_frame.defined_flags:
        if flag in new_frame.flags:
            flags |= flag_bit
    assert tc["frame"]["flags"] == flags

    p = tc["frame"]["frame_payload"]
    if "header_block_fragment" in p:
        assert p["header_block_fragment"] == new_frame.data.decode()
    if "data" in p:
        assert p["data"] == new_frame.data.decode()
    if "padding" in p:
        # the padding data itself is not retained by hyperframe after parsing
        pass
    if "padding_length" in p and p["padding_length"]:
        assert p["padding_length"] == new_frame.pad_length
    if "error_code" in p:
        assert p["error_code"] == new_frame.error_code
    if "additional_debug_data" in p:
        assert p["additional_debug_data"].encode() == new_frame.additional_data
    if "last_stream_id" in p:
        assert p["last_stream_id"] == new_frame.last_stream_id
    if "stream_dependency" in p:
        assert p["stream_dependency"] or 0 == new_frame.depends_on
    if "weight" in p and p["weight"]:
        assert p["weight"] - 1 == new_frame.stream_weight
    if "exclusive" in p:
        assert (p["exclusive"] or False) == new_frame.exclusive
    if "opaque_data" in p:
        assert p["opaque_data"].encode() == new_frame.opaque_data
    if "promised_stream_id" in p:
        assert p["promised_stream_id"] == new_frame.promised_stream_id
    if "settings" in p:
        assert dict(p["settings"]) == new_frame.settings
    if "window_size_increment" in p:
        assert p["window_size_increment"] == new_frame.window_increment
Beispiel #12
0
    def __next__(self):
        # First, check that we have enough data to successfully parse the
        # next frame header. If not, bail. Otherwise, parse it.
        if len(self.data) < 9:
            raise StopIteration()

        try:
            f, length = Frame.parse_frame_header(self.data[:9])
        except (InvalidDataError, InvalidFrameError) as e:  # pragma: no cover
            raise ProtocolError(
                "Received frame with invalid header: %s" % str(e)
            )

        # Next, check that we have enough length to parse the frame body. If
        # not, bail, leaving the frame header data in the buffer for next time.
        if len(self.data) < length + 9:
            raise StopIteration()

        # Confirm the frame has an appropriate length.
        self._validate_frame_length(length)

        # Try to parse the frame body
        try:
            f.parse_body(memoryview(self.data[9:9+length]))
        except InvalidDataError:
            raise ProtocolError("Received frame with non-compliant data")
        except InvalidFrameError:
            raise FrameDataMissingError("Frame data missing or invalid")

        # At this point, as we know we'll use or discard the entire frame, we
        # can update the data.
        self.data = self.data[9+length:]

        # Pass the frame through the header buffer.
        f = self._update_header_buffer(f)

        # If we got a frame we didn't understand or shouldn't yield, rather
        # than return None it'd be better if we just tried to get the next
        # frame in the sequence instead. Recurse back into ourselves to do
        # that. This is safe because the amount of work we have to do here is
        # strictly bounded by the length of the buffer.
        return f if f is not None else self.__next__()
Beispiel #13
0
    def _parse_frame_header(self, data):
        """
        Parses the frame header from the data. Either returns a tuple of
        (frame, length), or throws an exception. The returned frame may be None
        if the frame is of unknown type.
        """
        try:
            frame, length = Frame.parse_frame_header(data[:9])
        except UnknownFrameError as e:
            # Here we do something a bit odd. We want to consume the frame data
            # as consistently as possible, but we also don't ever want to yield
            # None. Instead, we make sure that, if there is no frame, we
            # recurse into ourselves.
            length = e.length
            frame = None
        except ValueError as e:
            # The frame header is invalid. This is a ProtocolError
            raise ProtocolError("Invalid frame header received: %s" % str(e))

        return frame, length
Beispiel #14
0
    def _parse_frame_header(self, data):
        """
        Parses the frame header from the data. Either returns a tuple of
        (frame, length), or throws an exception. The returned frame may be None
        if the frame is of unknown type.
        """
        try:
            frame, length = Frame.parse_frame_header(data[:9])
        except UnknownFrameError as e:
            # Here we do something a bit odd. We want to consume the frame data
            # as consistently as possible, but we also don't ever want to yield
            # None. Instead, we make sure that, if there is no frame, we
            # recurse into ourselves.
            length = e.length
            frame = None
        except ValueError as e:
            # The frame header is invalid. This is a ProtocolError
            raise ProtocolError("Invalid frame header received: %s" % str(e))

        return frame, length
Beispiel #15
0
def read_frame(rfile, parse=True):
    """
    Reads a full HTTP/2 frame from a file-like object.

    Returns a parsed frame and the consumed bytes.
    """
    header = rfile.safe_read(9)
    length = int(codecs.encode(header[:3], 'hex_codec'), 16)

    if length == 4740180:
        raise exceptions.HttpException(
            "Length field looks more like HTTP/1.1:\n{}".format(
                rfile.read(-1)))

    body = rfile.safe_read(length)

    if parse:
        frame, _ = Frame.parse_frame_header(header)
        frame.parse_body(memoryview(body))
    else:
        frame = None

    return frame, b''.join([header, body])
def decode_frame(frame_data):
    f, length = Frame.parse_frame_header(frame_data[:9])
    f.parse_body(memoryview(frame_data[9:9 + length]))
    assert 9 + length == len(frame_data)
    return f
Beispiel #17
0
 def test_cannot_parse_invalid_frame_header(self):
     with pytest.raises(InvalidFrameError):
         Frame.parse_frame_header(b'\x00\x00\x08\x00\x01\x00\x00\x00')
Beispiel #18
0
 def test_cannot_parse_invalid_frame_header(self):
     with pytest.raises(InvalidFrameError):
         Frame.parse_frame_header(b'\x00\x00\x08\x00\x01\x00\x00\x00')
Beispiel #19
0
    def test_parse_frame_header_ignore_first_bit_of_stream_id(self):
        s = b'\x00\x00\x00\x06\x01\x80\x00\x00\x00'
        f, _ = Frame.parse_frame_header(s)

        assert f.stream_id == 0
Beispiel #20
0
def decode_frame(frame_data):
    f, length = Frame.parse_frame_header(frame_data[:9])
    f.parse_body(memoryview(frame_data[9:9 + length]))
    assert 9 + length == len(frame_data)
    return f
Beispiel #21
0
    def test_parse_frame_header_ignore_first_bit_of_stream_id(self):
        s = b'\x00\x00\x00\x06\x01\x80\x00\x00\x00'
        f, _ = Frame.parse_frame_header(s)

        assert f.stream_id == 0