示例#1
0
    def send_push_promise(self, stream_id: int, headers: Headers) -> int:
        """
        Send a push promise related to the specified stream.

        Returns the stream ID on which headers and data can be sent.

        :param stream_id: The stream ID on which to send the data.
        :param headers: The HTTP request headers for this push.
        """
        assert not self._is_client, "Only servers may send a push promise."
        if self._max_push_id is None or self._next_push_id >= self._max_push_id:
            raise NoAvailablePushIDError

        # send push promise
        push_id = self._next_push_id
        self._next_push_id += 1
        self._quic.send_stream_data(
            stream_id,
            encode_frame(
                FrameType.PUSH_PROMISE,
                encode_uint_var(push_id) +
                self._encode_headers(stream_id, headers),
            ),
        )

        #  create push stream
        push_stream_id = self._create_uni_stream(StreamType.PUSH)
        self._quic.send_stream_data(push_stream_id, encode_uint_var(push_id))

        return push_stream_id
示例#2
0
    def test_handle_control_stream_duplicate(self):
        """
        We must only receive a single control stream.
        """
        quic_server = FakeQuicConnection(configuration=QuicConfiguration(
            is_client=False))
        h3_server = H3Connection(quic_server)

        # receive a first control stream
        h3_server.handle_event(
            StreamDataReceived(stream_id=2,
                               data=encode_uint_var(StreamType.CONTROL),
                               end_stream=False))

        # receive a second control stream
        h3_server.handle_event(
            StreamDataReceived(stream_id=6,
                               data=encode_uint_var(StreamType.CONTROL),
                               end_stream=False))
        self.assertEqual(
            quic_server.closed,
            (
                ErrorCode.HTTP_STREAM_CREATION_ERROR,
                "Only one control stream is allowed",
            ),
        )
示例#3
0
    def test_handle_qpack_encoder_duplicate(self):
        """
        We must only receive a single QPACK encoder stream.
        """
        quic_client = FakeQuicConnection(configuration=QuicConfiguration(
            is_client=True))
        h3_client = H3Connection(quic_client)

        # receive a first encoder stream
        h3_client.handle_event(
            StreamDataReceived(
                stream_id=11,
                data=encode_uint_var(StreamType.QPACK_ENCODER),
                end_stream=False,
            ))

        # receive a second encoder stream
        h3_client.handle_event(
            StreamDataReceived(
                stream_id=15,
                data=encode_uint_var(StreamType.QPACK_ENCODER),
                end_stream=False,
            ))
        self.assertEqual(
            quic_client.closed,
            (
                ErrorCode.HTTP_STREAM_CREATION_ERROR,
                "Only one QPACK encoder stream is allowed",
            ),
        )
示例#4
0
 def _create_uni_stream(self, stream_type: int) -> int:
     """
     Create an unidirectional stream of the given type.
     """
     stream_id = self._quic.get_next_available_stream_id(is_unidirectional=True)
     self._quic.send_stream_data(stream_id, encode_uint_var(stream_type))
     return stream_id
示例#5
0
    def test_send_stream_data_over_max_streams_uni(self):
        with client_and_server() as (client, server):
            # create streams
            for i in range(128):
                stream_id = i * 4 + 2
                client.send_stream_data(stream_id, b"")
                self.assertFalse(client._streams[stream_id].is_blocked)
            self.assertEqual(len(client._streams_blocked_bidi), 0)
            self.assertEqual(len(client._streams_blocked_uni), 0)
            self.assertEqual(roundtrip(client, server), (0, 0))

            # create one too many -> STREAMS_BLOCKED
            stream_id = 128 * 4 + 2
            client.send_stream_data(stream_id, b"")
            self.assertTrue(client._streams[stream_id].is_blocked)
            self.assertEqual(len(client._streams_blocked_bidi), 0)
            self.assertEqual(len(client._streams_blocked_uni), 1)
            self.assertEqual(roundtrip(client, server), (1, 1))

            # peer raises max streams
            client._handle_max_streams_uni_frame(
                client_receive_context(client),
                QuicFrameType.MAX_STREAMS_UNI,
                Buffer(data=encode_uint_var(129)),
            )
            self.assertFalse(client._streams[stream_id].is_blocked)
示例#6
0
    def _init_connection(self) -> None:
        # send our settings
        self._local_control_stream_id = self._create_uni_stream(
            StreamType.CONTROL)
        self._quic.send_stream_data(
            self._local_control_stream_id,
            encode_frame(
                FrameType.SETTINGS,
                encode_settings({
                    Setting.QPACK_MAX_TABLE_CAPACITY:
                    self._max_table_capacity,
                    Setting.QPACK_BLOCKED_STREAMS:
                    self._blocked_streams,
                }),
            ),
        )
        if self._is_client and self._max_push_id is not None:
            self._quic.send_stream_data(
                self._local_control_stream_id,
                encode_frame(FrameType.MAX_PUSH_ID,
                             encode_uint_var(self._max_push_id)),
            )

        # create encoder and decoder streams
        self._local_encoder_stream_id = self._create_uni_stream(
            StreamType.QPACK_ENCODER)
        self._local_decoder_stream_id = self._create_uni_stream(
            StreamType.QPACK_DECODER)
示例#7
0
    def send_datagram(self, flow_id: int, data: bytes) -> None:
        """
        Send a datagram for the specified flow.

        :param flow_id: The flow ID.
        :param data: The HTTP/3 datagram payload.
        """
        self._quic.send_datagram_frame(encode_uint_var(flow_id) + data)
示例#8
0
 def send_goaway(self):
     """
     Send GOAWAY control frame.
     """
     self._quic.send_stream_data(
         self._local_control_stream_id,
         encode_frame(FrameType.GOAWAY, encode_uint_var(0)),
     )
示例#9
0
 def test_handle_stream_frame_over_max_stream_data(self):
     with client_and_server() as (client, server):
         # client receives STREAM frame
         frame_type = QuicFrameType.STREAM_BASE | 4
         stream_id = 1
         with self.assertRaises(QuicConnectionError) as cm:
             client._handle_stream_frame(
                 client_receive_context(client),
                 frame_type,
                 Buffer(data=encode_uint_var(stream_id) + encode_uint_var(
                     client._local_max_stream_data_bidi_remote + 1)),
             )
         self.assertEqual(cm.exception.error_code,
                          QuicErrorCode.FLOW_CONTROL_ERROR)
         self.assertEqual(cm.exception.frame_type, frame_type)
         self.assertEqual(cm.exception.reason_phrase,
                          "Over stream data limit")
示例#10
0
 def test_handle_data_blocked_frame(self):
     with client_and_server() as (client, server):
         # client receives DATA_BLOCKED: 12345
         client._handle_data_blocked_frame(
             client_receive_context(client),
             QuicFrameType.DATA_BLOCKED,
             Buffer(data=encode_uint_var(12345)),
         )
示例#11
0
    def test_handle_max_streams_uni_frame(self):
        with client_and_server() as (client, server):
            self.assertEqual(client._remote_max_streams_uni, 128)

            # client receives MAX_STREAMS_UNI raising limit
            client._handle_max_streams_uni_frame(
                client_receive_context(client),
                QuicFrameType.MAX_STREAMS_UNI,
                Buffer(data=encode_uint_var(129)),
            )
            self.assertEqual(client._remote_max_streams_uni, 129)

            # client receives MAX_STREAMS_UNI raising limit
            client._handle_max_streams_uni_frame(
                client_receive_context(client),
                QuicFrameType.MAX_STREAMS_UNI,
                Buffer(data=encode_uint_var(127)),
            )
            self.assertEqual(client._remote_max_streams_uni, 129)
示例#12
0
    def send_cancel_push(self, push_id):
        """
        Send CANCEL_PUSH control frame for cancellation of a server push.

        :param push_id: server push id to cancel
        """
        self._quic.send_stream_data(
            self._local_control_stream_id,
            encode_frame(FrameType.CANCEL_PUSH, encode_uint_var(push_id)),
        )
示例#13
0
    def test_handle_max_data_frame(self):
        with client_and_server() as (client, server):
            self.assertEqual(client._remote_max_data, 1048576)

            # client receives MAX_DATA raising limit
            client._handle_max_data_frame(
                client_receive_context(client),
                QuicFrameType.MAX_DATA,
                Buffer(data=encode_uint_var(1048577)),
            )
            self.assertEqual(client._remote_max_data, 1048577)
示例#14
0
    def create_webtransport_stream(self,
                                   session_id: int,
                                   is_unidirectional: bool = False) -> int:
        """
        Create a WebTransport stream and return the stream ID.

        :param session_id: The WebTransport session identifier.
        :param is_unidirectional: Whether to create a unidirectional stream.
        """
        if is_unidirectional:
            stream_id = self._create_uni_stream(StreamType.WEBTRANSPORT)
            self._quic.send_stream_data(stream_id, encode_uint_var(session_id))
        else:
            stream_id = self._quic.get_next_available_stream_id()
            self._quic.send_stream_data(
                stream_id,
                encode_uint_var(FrameType.WEBTRANSPORT_STREAM) +
                encode_uint_var(session_id),
            )
        return stream_id
示例#15
0
    def test_handle_max_stream_data_frame(self):
        with client_and_server() as (client, server):
            # client creates bidirectional stream 0
            stream = client._create_stream(stream_id=0)
            self.assertEqual(stream.max_stream_data_remote, 1048576)

            # client receives MAX_STREAM_DATA raising limit
            client._handle_max_stream_data_frame(
                client_receive_context(client),
                QuicFrameType.MAX_STREAM_DATA,
                Buffer(data=b"\x00" + encode_uint_var(1048577)),
            )
            self.assertEqual(stream.max_stream_data_remote, 1048577)

            # client receives MAX_STREAM_DATA lowering limit
            client._handle_max_stream_data_frame(
                client_receive_context(client),
                QuicFrameType.MAX_STREAM_DATA,
                Buffer(data=b"\x00" + encode_uint_var(1048575)),
            )
            self.assertEqual(stream.max_stream_data_remote, 1048577)
示例#16
0
    def test_handle_push_frame_wrong_frame_type(self):
        """
        We should not received SETTINGS on a push stream.
        """
        quic_client = FakeQuicConnection(configuration=QuicConfiguration(
            is_client=True))
        h3_client = H3Connection(quic_client)

        h3_client.handle_event(
            StreamDataReceived(
                stream_id=15,
                data=encode_uint_var(StreamType.PUSH) +
                encode_uint_var(0)  # push ID
                + encode_frame(FrameType.SETTINGS, b""),
                end_stream=False,
            ))
        self.assertEqual(
            quic_client.closed,
            (ErrorCode.HTTP_FRAME_UNEXPECTED,
             "Invalid frame type on push stream"),
        )
示例#17
0
 def test_handle_stream_frame_over_max_streams(self):
     with client_and_server() as (client, server):
         # client receives STREAM frame
         with self.assertRaises(QuicConnectionError) as cm:
             client._handle_stream_frame(
                 client_receive_context(client),
                 QuicFrameType.STREAM_BASE,
                 Buffer(data=encode_uint_var(
                     client._local_max_stream_data_uni * 4 + 3)),
             )
         self.assertEqual(cm.exception.error_code,
                          QuicErrorCode.STREAM_LIMIT_ERROR)
         self.assertEqual(cm.exception.frame_type,
                          QuicFrameType.STREAM_BASE)
         self.assertEqual(cm.exception.reason_phrase,
                          "Too many streams open")
示例#18
0
    def test_handle_qpack_encoder_stream_error(self):
        """
        Receiving garbage on the QPACK encoder stream triggers an exception.
        """
        quic_client = FakeQuicConnection(configuration=QuicConfiguration(
            is_client=True))
        h3_client = H3Connection(quic_client)

        h3_client.handle_event(
            StreamDataReceived(
                stream_id=7,
                data=encode_uint_var(StreamType.QPACK_ENCODER) + b"\x00",
                end_stream=False,
            ))
        self.assertEqual(quic_client.closed,
                         (ErrorCode.HTTP_QPACK_ENCODER_STREAM_ERROR, ""))
示例#19
0
    def test_handle_control_frame_headers(self):
        """
        We should not receive HEADERS on the control stream.
        """
        quic_server = FakeQuicConnection(configuration=QuicConfiguration(
            is_client=False))
        h3_server = H3Connection(quic_server)

        h3_server.handle_event(
            StreamDataReceived(
                stream_id=2,
                data=encode_uint_var(StreamType.CONTROL) +
                encode_frame(FrameType.HEADERS, b""),
                end_stream=False,
            ))
        self.assertEqual(
            quic_server.closed,
            (ErrorCode.HTTP_FRAME_UNEXPECTED,
             "Invalid frame type on control stream"),
        )
示例#20
0
    def test_handle_control_frame_max_push_id_from_server(self):
        """
        A client should not receive MAX_PUSH_ID on the control stream.
        """
        quic_client = FakeQuicConnection(configuration=QuicConfiguration(
            is_client=True))
        h3_client = H3Connection(quic_client)

        h3_client.handle_event(
            StreamDataReceived(
                stream_id=3,
                data=encode_uint_var(StreamType.CONTROL) +
                encode_frame(FrameType.MAX_PUSH_ID, b""),
                end_stream=False,
            ))
        self.assertEqual(
            quic_client.closed,
            (ErrorCode.HTTP_FRAME_UNEXPECTED,
             "Servers must not send MAX_PUSH_ID"),
        )
示例#21
0
    def _init_connection(self) -> None:
        # send our settings
        self._local_control_stream_id = self._create_uni_stream(
            StreamType.CONTROL)
        self._quic.send_stream_data(
            self._local_control_stream_id,
            encode_frame(FrameType.SETTINGS,
                         encode_settings(self._get_local_settings())),
        )
        if self._is_client and self._max_push_id is not None:
            self._quic.send_stream_data(
                self._local_control_stream_id,
                encode_frame(FrameType.MAX_PUSH_ID,
                             encode_uint_var(self._max_push_id)),
            )

        # create encoder and decoder streams
        self._local_encoder_stream_id = self._create_uni_stream(
            StreamType.QPACK_ENCODER)
        self._local_decoder_stream_id = self._create_uni_stream(
            StreamType.QPACK_DECODER)