Esempio n. 1
0
    def test_bidirectional_stream(self):
        with h3_client_and_server(QUIC_CONFIGURATION_OPTIONS) as (
            quic_client,
            quic_server,
        ):
            h3_client = H3Connection(quic_client, enable_webtransport=True)
            h3_server = H3Connection(quic_server, enable_webtransport=True)

            # create session
            session_id = self._make_session(h3_client, h3_server)

            # send data on bidirectional stream
            stream_id = h3_client.create_webtransport_stream(session_id)
            quic_client.send_stream_data(stream_id, b"foo", end_stream=True)

            # receive data
            events = h3_transfer(quic_client, h3_server)
            self.assertEqual(
                events,
                [
                    WebTransportStreamDataReceived(
                        data=b"foo",
                        session_id=session_id,
                        stream_ended=True,
                        stream_id=stream_id,
                    )
                ],
            )
Esempio n. 2
0
    def test_request_with_server_push_max_push_id(self):
        with h3_client_and_server() as (quic_client, quic_server):
            h3_client = H3Connection(quic_client)
            h3_server = H3Connection(quic_server)

            # send request
            stream_id = quic_client.get_next_available_stream_id()
            h3_client.send_headers(
                stream_id=stream_id,
                headers=[
                    (b":method", b"GET"),
                    (b":scheme", b"https"),
                    (b":authority", b"localhost"),
                    (b":path", b"/"),
                ],
                end_stream=True,
            )

            # receive request
            events = h3_transfer(quic_client, h3_server)
            self.assertEqual(
                events,
                [
                    HeadersReceived(
                        headers=[
                            (b":method", b"GET"),
                            (b":scheme", b"https"),
                            (b":authority", b"localhost"),
                            (b":path", b"/"),
                        ],
                        stream_id=stream_id,
                        stream_ended=True,
                    )
                ],
            )

            # send push promises
            for i in range(0, 8):
                h3_server.send_push_promise(
                    stream_id=stream_id,
                    headers=[
                        (b":method", b"GET"),
                        (b":scheme", b"https"),
                        (b":authority", b"localhost"),
                        (b":path", "/{}.css".format(i).encode("ascii")),
                    ],
                )

            # send one too many
            with self.assertRaises(NoAvailablePushIDError):
                h3_server.send_push_promise(
                    stream_id=stream_id,
                    headers=[
                        (b":method", b"GET"),
                        (b":scheme", b"https"),
                        (b":authority", b"localhost"),
                        (b":path", b"/8.css"),
                    ],
                )
Esempio n. 3
0
    def test_request(self):
        with h3_client_and_server() as (quic_client, quic_server):
            h3_client = H3Connection(quic_client)
            h3_server = H3Connection(quic_server)

            # make first request
            self._make_request(h3_client, h3_server)

            # make second request
            self._make_request(h3_client, h3_server)

            # make third request -> dynamic table
            self._make_request(h3_client, h3_server)
Esempio n. 4
0
    def _consume_events(self, connection: QuicConnection) -> None:
        # process events
        event = connection.next_event()
        while event is not None:
            if isinstance(event, aioquic.events.HandshakeCompleted):
                if event.alpn_protocol == "h3-20":
                    self._http[connection] = H3Connection(connection)
                elif event.alpn_protocol == "hq-20":
                    self._http[connection] = H0Connection(connection)
            elif isinstance(event, aioquic.events.ConnectionIdIssued):
                self._connections[event.connection_id] = connection
            elif isinstance(event, aioquic.events.ConnectionIdRetired):
                assert self._connections[event.connection_id] == connection
                del self._connections[event.connection_id]

            #  pass event to the HTTP layer
            http = self._http.get(connection)
            if http is not None:
                for http_event in http.handle_event(event):
                    handle_http_event(http, http_event)

            event = connection.next_event()

        # send datagrams
        for data, addr in connection.datagrams_to_send(now=self._loop.time()):
            self._transport.sendto(data, addr)

        # re-arm timer
        """
Esempio n. 5
0
    def quic_event_received(self, event: QuicEvent) -> None:
        global totalQuicEvents
        totalQuicEvents += 1
        # logger.info('quic event recieved:{}'.format(totalQuicEvents))
        if isinstance(event, ProtocolNegotiated):
            if event.alpn_protocol.startswith("h3-"):
                self._http = H3Connection(self._quic)
            elif event.alpn_protocol.startswith("hq-"):
                self._http = H0Connection(self._quic)
            elif event.alpn_protocol.startswith("quic"):
                self.quic_client = True

        if isinstance(event, DatagramFrameReceived):
            if event.data == b'quic':
                self._quic.send_datagram_frame(b'quic-ack')

        if isinstance(event, StreamDataReceived):
            # logger.info('aakash kuch to aaya')
            if self.quic_client is True:
                print(f"print event {event.data}")
                data = b'quic stream-data recv'
                end_stream = False
                self._quic.send_stream_data(event.stream_id, data, end_stream)
            else:
                #TODO: Yet to handle a http_client streamDataReceived event
                pass

        #  pass event to the HTTP layer
        if self._http is not None:
            for http_event in self._http.handle_event(event):
                logger.debug('http event recieved')
                self.http_event_received(http_event)
Esempio n. 6
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",
            ),
        )
Esempio n. 7
0
    def quic_event_received(self, event: QuicEvent) -> None:
        if isinstance(event, ProtocolNegotiated):
            self._http = H3Connection(self._quic, enable_webtransport=True)

        if self._http is not None:
            for http_event in self._http.handle_event(event):
                self._h3_event_received(http_event)
Esempio n. 8
0
    def test_uni_stream_type(self):
        with h3_client_and_server() as (quic_client, quic_server):
            h3_server = H3Connection(quic_server)

            # unknown stream type 9
            stream_id = quic_client.get_next_available_stream_id(
                is_unidirectional=True)
            self.assertEqual(stream_id, 2)
            quic_client.send_stream_data(stream_id, b"\x09")
            self.assertEqual(h3_transfer(quic_client, h3_server), [])
            self.assertEqual(list(h3_server._stream.keys()), [2])
            self.assertEqual(h3_server._stream[2].buffer, b"")
            self.assertEqual(h3_server._stream[2].stream_type, 9)

            # unknown stream type 64, one byte at a time
            stream_id = quic_client.get_next_available_stream_id(
                is_unidirectional=True)
            self.assertEqual(stream_id, 6)

            quic_client.send_stream_data(stream_id, b"\x40")
            self.assertEqual(h3_transfer(quic_client, h3_server), [])
            self.assertEqual(list(h3_server._stream.keys()), [2, 6])
            self.assertEqual(h3_server._stream[2].buffer, b"")
            self.assertEqual(h3_server._stream[2].stream_type, 9)
            self.assertEqual(h3_server._stream[6].buffer, b"\x40")
            self.assertEqual(h3_server._stream[6].stream_type, None)

            quic_client.send_stream_data(stream_id, b"\x40")
            self.assertEqual(h3_transfer(quic_client, h3_server), [])
            self.assertEqual(list(h3_server._stream.keys()), [2, 6])
            self.assertEqual(h3_server._stream[2].buffer, b"")
            self.assertEqual(h3_server._stream[2].stream_type, 9)
            self.assertEqual(h3_server._stream[6].buffer, b"")
            self.assertEqual(h3_server._stream[6].stream_type, 64)
Esempio n. 9
0
    def test_uni_stream_grease(self):
        with h3_client_and_server() as (quic_client, quic_server):
            h3_server = H3Connection(quic_server)

            quic_client.send_stream_data(
                14, b"\xff\xff\xff\xff\xff\xff\xff\xfeGREASE is the word")
            self.assertEqual(h3_transfer(quic_client, h3_server), [])
Esempio n. 10
0
    def test_send_headers_after_trailers(self):
        """
        We should not send HEADERS after trailers.
        """
        quic_client = FakeQuicConnection(configuration=QuicConfiguration(
            is_client=True))
        h3_client = H3Connection(quic_client)

        stream_id = quic_client.get_next_available_stream_id()
        h3_client.send_headers(
            stream_id=stream_id,
            headers=[
                (b":method", b"GET"),
                (b":scheme", b"https"),
                (b":authority", b"localhost"),
                (b":path", b"/"),
            ],
        )
        h3_client.send_headers(stream_id=stream_id,
                               headers=[(b"x-some-trailer", b"foo")],
                               end_stream=False)
        with self.assertRaises(FrameUnexpected):
            h3_client.send_headers(
                stream_id=stream_id,
                headers=[(b"x-other-trailer", b"foo")],
                end_stream=False,
            )
Esempio n. 11
0
    def test_request(self):
        with client_and_server(
                client_options={"alpn_protocols": H3_ALPN},
                server_options={"alpn_protocols": H3_ALPN},
        ) as (quic_client, quic_server):
            h3_client = H3Connection(quic_client)
            h3_server = H3Connection(quic_server)

            # make first request
            self._make_request(h3_client, h3_server)

            # make second request
            self._make_request(h3_client, h3_server)

            # make third request -> dynamic table
            self._make_request(h3_client, h3_server)
Esempio n. 12
0
    def test_uni_stream_type(self):
        with client_and_server(
                client_options={"alpn_protocols": ["h3-20"]},
                server_options={"alpn_protocols": ["h3-20"]},
        ) as (quic_client, quic_server):
            h3_server = H3Connection(quic_server)

            # unknown stream type 9
            stream_id = quic_client.get_next_available_stream_id(
                is_unidirectional=True)
            self.assertEqual(stream_id, 2)
            quic_client.send_stream_data(stream_id, b"\x09")
            self.assertEqual(h3_transfer(quic_client, h3_server), [])
            self.assertEqual(h3_server._stream_buffers, {2: b""})
            self.assertEqual(h3_server._stream_types, {2: 9})

            # unknown stream type 64, one byte at a time
            stream_id = quic_client.get_next_available_stream_id(
                is_unidirectional=True)
            self.assertEqual(stream_id, 6)

            quic_client.send_stream_data(stream_id, b"\x40")
            self.assertEqual(h3_transfer(quic_client, h3_server), [])
            self.assertEqual(h3_server._stream_buffers, {2: b"", 6: b"\x40"})
            self.assertEqual(h3_server._stream_types, {2: 9})

            quic_client.send_stream_data(stream_id, b"\x40")
            self.assertEqual(h3_transfer(quic_client, h3_server), [])
            self.assertEqual(h3_server._stream_buffers, {2: b"", 6: b""})
            self.assertEqual(h3_server._stream_types, {2: 9, 6: 64})
Esempio n. 13
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",
            ),
        )
Esempio n. 14
0
    def test_blocked_stream(self):
        quic_client = FakeQuicConnection(configuration=QuicConfiguration(
            is_client=True))
        h3_client = H3Connection(quic_client)

        h3_client.handle_event(
            StreamDataReceived(
                stream_id=3,
                data=binascii.unhexlify(
                    "0004170150000680020000074064091040bcc0000000faceb00c"),
                end_stream=False,
            ))
        h3_client.handle_event(
            StreamDataReceived(stream_id=7, data=b"\x02", end_stream=False))
        h3_client.handle_event(
            StreamDataReceived(stream_id=11, data=b"\x03", end_stream=False))
        h3_client.handle_event(
            StreamDataReceived(stream_id=0,
                               data=binascii.unhexlify("01040280d910"),
                               end_stream=False))
        h3_client.handle_event(
            StreamDataReceived(
                stream_id=0,
                data=binascii.unhexlify(
                    "00408d796f752072656163686564206d766673742e6e65742c20726561636820"
                    "746865202f6563686f20656e64706f696e7420666f7220616e206563686f2072"
                    "6573706f6e7365207175657279202f3c6e756d6265723e20656e64706f696e74"
                    "7320666f722061207661726961626c652073697a6520726573706f6e73652077"
                    "6974682072616e646f6d206279746573"),
                end_stream=True,
            ))
        self.assertEqual(
            h3_client.handle_event(
                StreamDataReceived(
                    stream_id=7,
                    data=binascii.unhexlify(
                        "3fe101c696d07abe941094cb6d0a08017d403971966e32ca98b46f"
                    ),
                    end_stream=False,
                )),
            [
                HeadersReceived(
                    headers=[
                        (b":status", b"200"),
                        (b"date", b"Mon, 22 Jul 2019 06:33:33 GMT"),
                    ],
                    stream_id=0,
                    stream_ended=False,
                ),
                DataReceived(
                    data=
                    (b"you reached mvfst.net, reach the /echo endpoint for an "
                     b"echo response query /<number> endpoints for a variable "
                     b"size response with random bytes"),
                    stream_id=0,
                    stream_ended=True,
                ),
            ],
        )
Esempio n. 15
0
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)

        self._http = H3Connection(self._quic)
        self._request_events = {}
        self._request_waiter = {}

        self.terminated = False
Esempio n. 16
0
    def test_uni_stream_grease(self):
        with client_and_server(
                client_options={"alpn_protocols": H3_ALPN},
                server_options={"alpn_protocols": H3_ALPN},
        ) as (quic_client, quic_server):
            h3_server = H3Connection(quic_server)

            quic_client.send_stream_data(
                14, b"\xff\xff\xff\xff\xff\xff\xff\xfeGREASE is the word")
            self.assertEqual(h3_transfer(quic_client, h3_server), [])
Esempio n. 17
0
    def quic_event_received(self, event: QuicEvent):
        if isinstance(event, ProtocolNegotiated):
            if event.alpn_protocol == "h3-22":
                self._http = H3Connection(self._quic)
            elif event.alpn_protocol == "hq-22":
                self._http = H0Connection(self._quic)

        #  pass event to the HTTP layer
        if self._http is not None:
            for http_event in self._http.handle_event(event):
                self.http_event_received(http_event)
Esempio n. 18
0
    def test_datagram(self):
        with h3_client_and_server(QUIC_CONFIGURATION_OPTIONS) as (
            quic_client,
            quic_server,
        ):
            h3_client = H3Connection(quic_client, enable_webtransport=True)
            h3_server = H3Connection(quic_server, enable_webtransport=True)

            # create session
            session_id = self._make_session(h3_client, h3_server)

            # send datagram
            h3_client.send_datagram(data=b"foo", flow_id=session_id)

            # receive datagram
            events = h3_transfer(quic_client, h3_server)
            self.assertEqual(
                events,
                [DatagramReceived(data=b"foo", flow_id=session_id)],
            )
Esempio n. 19
0
    def quic_event_received(self, event: QuicEvent) -> None:
        if isinstance(event, ProtocolNegotiated):
            self._http = H3Connection(self._quic, enable_webtransport=True)
        elif isinstance(event, StreamReset) and self._handler is not None:
            # Streams in QUIC can be closed in two ways: normal (FIN) and
            # abnormal (resets).  FIN is handled by the handler; the code
            # below handles the resets.
            self._handler.stream_closed(event.stream_id)

        if self._http is not None:
            for h3_event in self._http.handle_event(event):
                self._h3_event_received(h3_event)
Esempio n. 20
0
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self._http: Optional[HttpConnection] = None
        self._request_events: Dict[int, Deque[H3Event]] = {}
        self._request_waiter: Dict[int, asyncio.Future[Deque[H3Event]]] = {}
        self._websockets: Dict[int, WebSocket] = {}

        if self._quic.configuration.alpn_protocols[0].startswith("hq-"):
            self._http = H0Connection(self._quic)
        else:
            self._http = H3Connection(self._quic)
Esempio n. 21
0
def run(url: str) -> None:
    # parse URL
    parsed = urlparse(url)
    assert parsed.scheme == "https", "Only HTTPS URLs are supported."
    if ":" in parsed.netloc:
        server_name, port_str = parsed.netloc.split(":")
        port = int(port_str)
    else:
        server_name = parsed.netloc
        port = 443

    # prepare socket
    server_addr = (socket.gethostbyname(server_name), port)
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    # prepare QUIC connection
    quic = QuicConnection(configuration=QuicConfiguration(
        alpn_protocols=["h3-20"],
        is_client=True,
        secrets_log_file=open("/tmp/ssl.log", "w"),
        server_name=server_name,
    ))
    quic.connect(server_addr, now=time.time())

    # send request
    http = H3Connection(quic)
    stream_id = quic.get_next_available_stream_id()
    http.send_headers(
        stream_id=stream_id,
        headers=[
            (b":method", b"GET"),
            (b":scheme", parsed.scheme.encode("utf8")),
            (b":authority", parsed.netloc.encode("utf8")),
            (b":path", parsed.path.encode("utf8")),
        ],
    )
    http.send_data(stream_id=stream_id, data=b"", end_stream=True)
    for data, addr in quic.datagrams_to_send(now=time.time()):
        sock.sendto(data, addr)

    # handle events
    stream_ended = False
    while not stream_ended:
        data, addr = sock.recvfrom(2048)
        quic.receive_datagram(data, addr, now=time.time())
        for event in http.handle_events():
            print(event)
            if isinstance(event, (DataReceived, ResponseReceived)):
                stream_ended = event.stream_ended

        for data, addr in quic.datagrams_to_send(now=time.time()):
            sock.sendto(data, addr)
Esempio n. 22
0
    def test_send_data_before_headers(self):
        """
        We should not send DATA before headers.
        """
        quic_client = FakeQuicConnection(configuration=QuicConfiguration(
            is_client=True))
        h3_client = H3Connection(quic_client)

        stream_id = quic_client.get_next_available_stream_id()
        with self.assertRaises(FrameUnexpected):
            h3_client.send_data(stream_id=stream_id,
                                data=b"hello",
                                end_stream=False)
Esempio n. 23
0
    def test_handle_request_frame_headers_after_trailers(self):
        """
        We should not receive HEADERS after receiving trailers.
        """
        quic_client = FakeQuicConnection(configuration=QuicConfiguration(
            is_client=True))
        quic_server = FakeQuicConnection(configuration=QuicConfiguration(
            is_client=False))

        h3_client = H3Connection(quic_client)
        h3_server = H3Connection(quic_server)

        stream_id = quic_client.get_next_available_stream_id()
        h3_client.send_headers(
            stream_id=stream_id,
            headers=[
                (b":method", b"GET"),
                (b":scheme", b"https"),
                (b":authority", b"localhost"),
                (b":path", b"/"),
            ],
        )
        h3_client.send_headers(stream_id=stream_id,
                               headers=[(b"x-some-trailer", b"foo")],
                               end_stream=True)
        h3_transfer(quic_client, h3_server)

        h3_server.handle_event(
            StreamDataReceived(stream_id=0,
                               data=encode_frame(FrameType.HEADERS, b""),
                               end_stream=False))
        self.assertEqual(
            quic_server.closed,
            (
                ErrorCode.HTTP_FRAME_UNEXPECTED,
                "HEADERS frame is not allowed in this state",
            ),
        )
Esempio n. 24
0
    def quic_event_received(self, event: QuicEvent) -> None:
        if isinstance(event, ProtocolNegotiated):
            if event.alpn_protocol in H3_ALPN:
                self._http = H3Connection(self._quic, enable_webtransport=True)
            elif event.alpn_protocol in H0_ALPN:
                self._http = H0Connection(self._quic)
        elif isinstance(event, DatagramFrameReceived):
            if event.data == b"quack":
                self._quic.send_datagram_frame(b"quack-ack")

        #  pass event to the HTTP layer
        if self._http is not None:
            for http_event in self._http.handle_event(event):
                self.http_event_received(http_event)
Esempio n. 25
0
    def quic_event_received(self, event: QuicEvent) -> None:
        if isinstance(event, ProtocolNegotiated):
            if event.alpn_protocol.startswith("h3-"):
                self._http = H3Connection(self._quic)
            elif event.alpn_protocol.startswith("hq-"):
                self._http = H0Connection(self._quic)
        elif isinstance(event, DatagramFrameReceived):
            if event.data == b"quack":
                self._quic.send_datagram_frame(b"quack-ack")

        #  pass event to the HTTP layer
        if self._http is not None:
            for http_event in self._http.handle_event(event):
                self.http_event_received(http_event)
Esempio n. 26
0
    def test_handle_request_frame_bad_headers(self):
        """
        We should not receive HEADERS which cannot be decoded.
        """
        quic_server = FakeQuicConnection(configuration=QuicConfiguration(
            is_client=False))
        h3_server = H3Connection(quic_server)

        h3_server.handle_event(
            StreamDataReceived(stream_id=0,
                               data=encode_frame(FrameType.HEADERS, b""),
                               end_stream=False))
        self.assertEqual(quic_server.closed,
                         (ErrorCode.HTTP_QPACK_DECOMPRESSION_FAILED, ""))
Esempio n. 27
0
    def test_handle_datagram_truncated(self):
        quic_server = FakeQuicConnection(
            configuration=QuicConfiguration(is_client=False)
        )
        h3_server = H3Connection(quic_server)

        # receive a datagram with a truncated session ID
        h3_server.handle_event(DatagramFrameReceived(data=b"\xff"))
        self.assertEqual(
            quic_server.closed,
            (
                ErrorCode.H3_GENERAL_PROTOCOL_ERROR,
                "Could not parse flow ID",
            ),
        )
Esempio n. 28
0
 def __init__(
     self,
     config: Config,
     client: Optional[Tuple[str, int]],
     server: Optional[Tuple[str, int]],
     spawn_app: Callable[[dict, Callable], Awaitable[Callable]],
     quic: QuicConnection,
     send: Callable[[], Awaitable[None]],
 ) -> None:
     self.client = client
     self.config = config
     self.connection = H3Connection(quic)
     self.send = send
     self.server = server
     self.spawn_app = spawn_app
     self.streams: Dict[int, Union[HTTPStream, WSStream]] = {}
Esempio n. 29
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, ""))
Esempio n. 30
0
    def test_handle_request_frame_wrong_frame_type(self):
        quic_server = FakeQuicConnection(configuration=QuicConfiguration(
            is_client=False))
        h3_server = H3Connection(quic_server)

        h3_server.handle_event(
            StreamDataReceived(
                stream_id=0,
                data=encode_frame(FrameType.SETTINGS, b""),
                end_stream=False,
            ))
        self.assertEqual(
            quic_server.closed,
            (ErrorCode.HTTP_FRAME_UNEXPECTED,
             "Invalid frame type on request stream"),
        )