Пример #1
0
 async def run_client_ping():
     configuration = QuicConfiguration(is_client=True)
     configuration.load_verify_locations(cafile=SERVER_CACERTFILE)
     async with connect(self.server_host,
                        self.server_port,
                        configuration=configuration) as client:
         await client.ping()
         await client.ping()
Пример #2
0
 async def run_client_key_update(host, port=4433):
     configuration = QuicConfiguration(is_client=True)
     configuration.load_verify_locations(cafile=SERVER_CACERTFILE)
     async with connect(host, port,
                        configuration=configuration) as client:
         await client.ping()
         client.request_key_update()
         await client.ping()
Пример #3
0
 async def run_client_ping():
     configuration = QuicConfiguration(is_client=True)
     configuration.load_verify_locations(cafile=SERVER_CACERTFILE)
     async with connect(self.server_host,
                        self.server_port,
                        configuration=configuration) as client:
         coros = [client.ping() for x in range(16)]
         await asyncio.gather(*coros)
Пример #4
0
def init_context(certfile, keyfile, pass_phrase):
    from aioquic.h3.connection import H3_ALPN
    from aioquic.quic.configuration import QuicConfiguration
    import ssl

    ctx = QuicConfiguration(alpn_protocols=H3_ALPN, is_client=False)
    ctx.load_cert_chain(certfile, keyfile, pass_phrase)
    ctx.verify_mode = ssl.CERT_NONE
    return ctx
Пример #5
0
async def run_server(configuration=None, **kwargs):
    if configuration is None:
        configuration = QuicConfiguration(is_client=False)
        configuration.load_cert_chain(SERVER_CERTFILE, SERVER_KEYFILE)
    return await serve(host="::",
                       port="4433",
                       configuration=configuration,
                       stream_handler=handle_stream,
                       **kwargs)
Пример #6
0
 async def run_server(self, configuration=None, host="::", **kwargs):
     if configuration is None:
         configuration = QuicConfiguration(is_client=False)
         configuration.load_cert_chain(SERVER_CERTFILE, SERVER_KEYFILE)
     self.server = await serve(host=host,
                               port=self.server_port,
                               configuration=configuration,
                               stream_handler=handle_stream,
                               **kwargs)
     return self.server
Пример #7
0
        async def run_client_writelines(host, port=4433):
            configuration = QuicConfiguration(is_client=True)
            configuration.load_verify_locations(cafile=SERVER_CACERTFILE)
            async with connect(host, port, configuration=configuration) as client:
                reader, writer = await client.create_stream()
                assert writer.can_write_eof() is True

                writer.writelines([b"01234567", b"89012345"])
                writer.write_eof()

                return await reader.read()
Пример #8
0
async def test_throughput(server: Server, configuration: QuicConfiguration):
    failures = 0
    if server.throughput_path is None:
        return

    for size in [5000000, 10000000]:
        path = server.throughput_path % {"size": size}
        print("Testing %d bytes download: %s" % (size, path))

        # perform HTTP request over TCP
        start = time.time()
        response = httpx.get("https://" + server.host + path, verify=False)
        tcp_octets = len(response.content)
        tcp_elapsed = time.time() - start
        assert tcp_octets == size, "HTTP/TCP response size mismatch"

        # perform HTTP request over QUIC
        if server.http3:
            configuration.alpn_protocols = H3_ALPN
            port = server.http3_port or server.port
        else:
            configuration.alpn_protocols = H0_ALPN
            port = server.port
        start = time.time()
        async with connect(
                server.host,
                port,
                configuration=configuration,
                create_protocol=HttpClient,
        ) as protocol:
            protocol = cast(HttpClient, protocol)

            http_events = await protocol.get("https://{}:{}{}".format(
                server.host, server.port, path))
            quic_elapsed = time.time() - start
            quic_octets = 0
            for http_event in http_events:
                if isinstance(http_event, DataReceived):
                    quic_octets += len(http_event.data)
        assert quic_octets == size, "HTTP/QUIC response size mismatch"

        print(" - HTTP/TCP  completed in %.3f s" % tcp_elapsed)
        print(" - HTTP/QUIC completed in %.3f s" % quic_elapsed)

        if quic_elapsed > 1.1 * tcp_elapsed:
            failures += 1
            print(" => FAIL")
        else:
            print(" => PASS")

    if failures == 0:
        server.result |= Result.T
Пример #9
0
def run_server(server_port):
    logging.info('Starting server at localhost:%s', server_port)

    configuration = QuicConfiguration(is_client=False)

    certificates_path = Path(__file__).parent / 'certificates'
    configuration.load_cert_chain(certificates_path / 'ssl_cert.pem',
                                  certificates_path / 'ssl_key.pem')

    return rsocket_serve(host='localhost',
                         port=server_port,
                         configuration=configuration,
                         handler_factory=Handler)
Пример #10
0
    def test_combined_key(self):
        config1 = QuicConfiguration()
        config2 = QuicConfiguration()
        config1.load_cert_chain(SERVER_CERTFILE, SERVER_KEYFILE)
        config2.load_cert_chain(SERVER_COMBINEDFILE)

        self.assertEqual(config1.certificate, config2.certificate)
Пример #11
0
    def create_server(self):
        configuration = QuicConfiguration(is_client=False)
        configuration.load_cert_chain(SERVER_CERTFILE, SERVER_KEYFILE)

        server = Context(is_client=False)
        server.certificate = configuration.certificate
        server.certificate_private_key = configuration.private_key
        server.handshake_extensions = [
            (
                tls.ExtensionType.QUIC_TRANSPORT_PARAMETERS,
                SERVER_QUIC_TRANSPORT_PARAMETERS,
            )
        ]
        self.assertEqual(server.state, State.SERVER_EXPECT_CLIENT_HELLO)
        return server
Пример #12
0
    def __init__ (self, endpoint):
        self._calls = []
        self.endpoint = endpoint

         # prepare configuration
        self.configuration = QuicConfiguration(
            is_client=True, alpn_protocols=H3_ALPN
        )
        self.configuration.load_verify_locations(os.path.join (os.path.dirname (__file__), 'pycacert.pem'))
        self.configuration.verify_mode = ssl.CERT_NONE
        try:
            with open(SESSION_TICKET, "rb") as fp:
                self.configuration.session_ticket = pickle.load(fp)
        except FileNotFoundError:
            pass
Пример #13
0
    def __init__(
        self,
        config: Config,
        server: Optional[Tuple[str, int]],
        spawn_app: Callable[[dict, Callable], Awaitable[Callable]],
        send: Callable[[Event], Awaitable[None]],
        call_at: Callable[[float, Callable], None],
        now: Callable[[], float],
    ) -> None:
        self.call_at = call_at
        self.config = config
        self.connections: Dict[bytes, QuicConnection] = {}
        self.http_connections: Dict[QuicConnection, H3Protocol] = {}
        self.now = now
        self.send = send
        self.server = server
        self.spawn_app = spawn_app

        with open(config.certfile, "rb") as fp:
            certificate = x509.load_pem_x509_certificate(fp.read(), backend=default_backend())

        with open(config.keyfile, "rb") as fp:
            private_key = serialization.load_pem_private_key(
                fp.read(), password=None, backend=default_backend()
            )

        self.quic_config = QuicConfiguration(
            alpn_protocols=["h3-22"],
            certificate=certificate,
            is_client=False,
            private_key=private_key,
        )
Пример #14
0
    def test_connect_and_serve_with_session_ticket(self):
        # start server
        client_ticket = None
        store = SessionTicketStore()

        def save_ticket(t):
            nonlocal client_ticket
            client_ticket = t

        run(
            self.run_server(session_ticket_fetcher=store.pop,
                            session_ticket_handler=store.add))

        # first request
        response = run(
            self.run_client("127.0.0.1", session_ticket_handler=save_ticket), )
        self.assertEqual(response, b"gnip")

        self.assertIsNotNone(client_ticket)

        # second request
        run(
            self.run_client(
                "127.0.0.1",
                configuration=QuicConfiguration(is_client=True,
                                                session_ticket=client_ticket),
            ))
        self.assertEqual(response, b"gnip")
Пример #15
0
async def run(servers, tests, quic_log=False, secrets_log_file=None) -> None:
    for server in servers:
        for test_name, test_func in tests:
            print("\n=== %s %s ===\n" % (server.name, test_name))
            configuration = QuicConfiguration(
                alpn_protocols=["h3-23", "h3-22", "hq-23", "hq-22"],
                is_client=True,
                quic_logger=QuicLogger(),
                secrets_log_file=secrets_log_file,
            )
            if test_name == "test_throughput":
                timeout = 60
            else:
                timeout = 5
            try:
                await asyncio.wait_for(test_func(server, configuration),
                                       timeout=timeout)
            except Exception as exc:
                print(exc)

            if quic_log:
                with open("%s-%s.qlog" % (server.name, test_name),
                          "w") as logger_fp:
                    json.dump(configuration.quic_logger.to_dict(),
                              logger_fp,
                              indent=4)

        print("")
        print_result(server)

    # print summary
    if len(servers) > 1:
        print("SUMMARY")
        for server in servers:
            print_result(server)
Пример #16
0
    async def probe(self) -> ProbeResult:
        configuration = QuicConfiguration(
            alpn_protocols=h3c.H3_ALPN,
            is_client=True,
            server_name=self._url.hostname,
            max_datagram_frame_size=32+MTU,
        )

        def create_protocol(*args, **kwargs):
            return H3Client(self._url, ORIGIN, MTU, *args, **kwargs)

        try:
            ip, port = self._resolve_dns()
            async with connect(
                host=ip,
                port=port,
                configuration=configuration,
                create_protocol=create_protocol,
                wait_connected=False,
            ) as client:
                client = T.cast(H3Client, client)
                await aio.wait_for(client.wait_connected(), CONNECT_TIMEOUT)
                self._result.connected = True
                self._result.probes = [ProbeNameResult(
                    ok=False, error="timeout") for name in self._names]
                self._send_interests(client)
                received = 0
                for _ in range(int(INTEREST_TIMEOUT // INTEREST_TIMEOUT_STEP)):
                    await aio.sleep(INTEREST_TIMEOUT_STEP)
                    received += self._process_received(client)
                    if received >= len(self._names):
                        break
        except Exception as err:
            self._result.connectError = str(err)
        return self._result
Пример #17
0
async def start_quic_service(waiter: asyncio.Event, container, port: int, generate_test_certificates):
    index_iterator = iter(range(1, 3))
    certificate, private_key = generate_test_certificates
    server_configuration = QuicConfiguration(
        certificate=certificate,
        private_key=private_key,
        is_client=False
    )

    def handler_factory(*args, **kwargs):
        return IdentifiedHandlerFactory(
            next(index_iterator),
            ServerHandler,
            delay=timedelta(seconds=1)).factory(*args, **kwargs)

    def on_server_create(server):
        container.server = server
        container.transport = server._transport
        waiter.set()

    quic_server = await rsocket_serve(host='localhost',
                                      port=port,
                                      configuration=server_configuration,
                                      on_server_create=on_server_create,
                                      handler_factory=handler_factory)
    return sync(quic_server.close)
Пример #18
0
async def run(servers, tests, secrets_log_file=None) -> None:
    for server in servers:
        for test_name, test_func in tests:
            print("\n=== %s %s ===\n" % (server.name, test_name))
            configuration = QuicConfiguration(
                alpn_protocols=["hq-22", "h3-22"],
                is_client=True,
                quic_logger=QuicLogger(),
                secrets_log_file=secrets_log_file,
            )
            if test_name == "test_throughput":
                timeout = 60
            else:
                timeout = 5
            try:
                await asyncio.wait_for(test_func(server, configuration),
                                       timeout=timeout)
            except Exception as exc:
                print(exc)
        print("")
        print_result(server)

    # print summary
    if len(servers) > 1:
        print("SUMMARY")
        for server in servers:
            print_result(server)
Пример #19
0
async def test_session_resumption(server: Server, configuration: QuicConfiguration):
    port = server.session_resumption_port or server.port
    saved_ticket = None

    def session_ticket_handler(ticket):
        nonlocal saved_ticket
        saved_ticket = ticket

    # connect a first time, receive a ticket
    async with connect(
        server.host,
        port,
        configuration=configuration,
        session_ticket_handler=session_ticket_handler,
    ) as protocol:
        await protocol.ping()

        # some servers don't send the ticket immediately
        await asyncio.sleep(1)

    # connect a second time, with the ticket
    if saved_ticket is not None:
        configuration.session_ticket = saved_ticket
        async with connect(server.host, port, configuration=configuration) as protocol:
            await protocol.ping()

            # check session was resumed
            if protocol._quic.tls.session_resumed:
                server.result |= Result.R

            # check early data was accepted
            if protocol._quic.tls.early_data_accepted:
                server.result |= Result.Z
Пример #20
0
async def run(servers, tests, quic_log=False, secrets_log_file=None) -> None:
    for server in servers:
        if server.structured_logging:
            server.result |= Result.L
        for test_name, test_func in tests:
            print("\n=== %s %s ===\n" % (server.name, test_name))
            configuration = QuicConfiguration(
                alpn_protocols=H3_ALPN + H0_ALPN,
                is_client=True,
                quic_logger=QuicDirectoryLogger(quic_log) if quic_log else QuicLogger(),
                secrets_log_file=secrets_log_file,
                verify_mode=server.verify_mode,
            )
            if test_name == "test_throughput":
                timeout = 120
            else:
                timeout = 10
            try:
                await asyncio.wait_for(
                    test_func(server, configuration), timeout=timeout
                )
            except Exception as exc:
                print(exc)

        print("")
        print_result(server)

    # print summary
    if len(servers) > 1:
        print("SUMMARY")
        for server in servers:
            print_result(server)
Пример #21
0
async def test_http_3(server: Server, configuration: QuicConfiguration):
    if server.path is None:
        return

    configuration.alpn_protocols = H3_ALPN
    async with connect(
            server.host,
            server.port,
            configuration=configuration,
            create_protocol=HttpClient,
    ) as protocol:
        protocol = cast(HttpClient, protocol)

        # perform HTTP request
        events = await protocol.get("https://{}:{}{}".format(
            server.host, server.port, server.path))
        if events and isinstance(events[0], HeadersReceived):
            server.result |= Result.D
            server.result |= Result.three

        # perform more HTTP requests to use QPACK dynamic tables
        for i in range(2):
            events = await protocol.get("https://{}:{}{}".format(
                server.host, server.port, server.path))
        if events and isinstance(events[0], HeadersReceived):
            http = cast(H3Connection, protocol._http)
            protocol._quic._logger.info(
                "QPACK decoder bytes RX %d TX %d",
                http._decoder_bytes_received,
                http._decoder_bytes_sent,
            )
            protocol._quic._logger.info(
                "QPACK encoder bytes RX %d TX %d",
                http._encoder_bytes_received,
                http._encoder_bytes_sent,
            )
            if (http._decoder_bytes_received and http._decoder_bytes_sent
                    and http._encoder_bytes_received
                    and http._encoder_bytes_sent):
                server.result |= Result.d

        # check push support
        if server.push_path is not None:
            protocol.pushes.clear()
            await protocol.get("https://{}:{}{}".format(
                server.host, server.port, server.push_path))
            await asyncio.sleep(0.5)
            for push_id, events in protocol.pushes.items():
                if (len(events) >= 3
                        and isinstance(events[0], PushPromiseReceived)
                        and isinstance(events[1], HeadersReceived)
                        and isinstance(events[2], DataReceived)):
                    protocol._quic._logger.info(
                        "Push promise %d for %s received (status %s)",
                        push_id,
                        dict(events[0].headers)[b":path"].decode("ascii"),
                        int(dict(events[1].headers)[b":status"]),
                    )

                    server.result |= Result.p
Пример #22
0
async def test_quantum_readiness(server: Server, configuration: QuicConfiguration):
    configuration.quantum_readiness_test = True
    async with connect(
        server.host, server.port, configuration=configuration
    ) as protocol:
        await protocol.ping()
        server.result |= Result.Q
Пример #23
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,
            )
Пример #24
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",
            ),
        )
Пример #25
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",
            ),
        )
Пример #26
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,
                ),
            ],
        )
Пример #27
0
 def test_connect_timeout(self):
     with self.assertRaises(ConnectionError):
         run(
             self.run_client(
                 port=self.bogus_port,
                 configuration=QuicConfiguration(is_client=True,
                                                 idle_timeout=5),
             ))
Пример #28
0
    def test_connect_and_serve_with_retry_bad_token(self, mock_validate):
        mock_validate.side_effect = ValueError("Decryption failed.")

        run(self.run_server(retry=True))
        with self.assertRaises(ConnectionError):
            run(
                self.run_client(configuration=QuicConfiguration(
                    is_client=True, idle_timeout=4.0), ))
Пример #29
0
    async def connection(self, message):

        if message.unresolved_remote is None:
            host = message.opt.uri_host
            port = message.opt.uri_port or self.default_port
            if host is None:
                raise ValueError(
                    "No location found to send message to (neither in .opt.uri_host nor in .remote)"
                )
        else:
            host, port = util.hostportsplit(message.unresolved_remote)
            port = port or self.default_port

        try:
            ipaddress.ip_address(host)
            server_name = None
        except ValueError as ve:
            server_name = host

        infos = await self.loop.getaddrinfo(host, port, type=socket.SOCK_DGRAM)
        self.addr = infos[0][4]

        config = QuicConfiguration(is_client=True,
                                   alpn_protocols='coap',
                                   idle_timeout=864000,
                                   server_name=server_name)
        config.verify_mode = ssl.CERT_NONE

        if config.server_name is None:
            config.server_name = server_name

        connection = QuicConnection(configuration=config)

        self.quic = Quic(connection)
        self.quic.ctx = self

        try:
            transport, protocol = await self.loop.create_datagram_endpoint(
                lambda: self.quic, remote_addr=(host, port))
            protocol.connect(self.addr)
            await protocol.wait_connected()
            self.con = True
        except OSError:
            raise error.NetworkError("Connection failed to %r" % host)

        return protocol
Пример #30
0
async def _connect_to_server(host: str, port: int) -> None:
    configuration = QuicConfiguration(
        alpn_protocols=H3_ALPN,
        is_client=True,
        verify_mode=ssl.CERT_NONE,
    )

    async with connect(host, port, configuration=configuration) as protocol:
        await protocol.ping()