예제 #1
0
 def test_connect_and_serve_with_packet_loss(self, mock_sendto):
     """
     This test ensures handshake success and stream data is successfully sent
     and received in the presence of packet loss (randomized 25% in each direction).
     """
     data = b"Z" * 65536
     server, response = run(
         asyncio.gather(
             run_server(
                 configuration=QuicConfiguration(
                     certificate=SERVER_CERTIFICATE,
                     idle_timeout=300.0,
                     is_client=False,
                     private_key=SERVER_PRIVATE_KEY,
                     quic_logger=QuicLogger(),
                 ),
                 stateless_retry=True,
             ),
             run_client(
                 "127.0.0.1",
                 configuration=QuicConfiguration(
                     is_client=True, idle_timeout=300.0, quic_logger=QuicLogger()
                 ),
                 request=data,
             ),
         )
     )
     self.assertEqual(response, data)
     server.close()
예제 #2
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)
예제 #3
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)
예제 #4
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)
예제 #5
0
파일: test_h3.py 프로젝트: tianyaqu/aioquic
 def __init__(self, configuration):
     self.closed = None
     self.configuration = configuration
     self.stream_queue = []
     self._events = []
     self._next_stream_bidi = 0 if configuration.is_client else 1
     self._next_stream_uni = 2 if configuration.is_client else 3
     self._quic_logger = QuicLogger().start_trace(
         is_client=configuration.is_client, odcid=b"")
예제 #6
0
    def test_connect_and_serve_with_version_negotiation(self):
        run(self.run_server())

        # force version negotiation
        configuration = QuicConfiguration(is_client=True, quic_logger=QuicLogger())
        configuration.supported_versions.insert(0, 0x1A2A3A4A)

        response = run(self.run_client(configuration=configuration))
        self.assertEqual(response, b"gnip")
예제 #7
0
    def test_connect_and_serve_with_packet_loss(self, mock_sendto):
        """
        This test ensures handshake success and stream data is successfully sent
        and received in the presence of packet loss (randomized 25% in each direction).
        """
        data = b"Z" * 65536

        server_configuration = QuicConfiguration(is_client=False,
                                                 quic_logger=QuicLogger())
        server_configuration.load_cert_chain(SERVER_CERTFILE, SERVER_KEYFILE)
        run(self.run_server(configuration=server_configuration))

        response = run(
            self.run_client(
                configuration=QuicConfiguration(is_client=True,
                                                quic_logger=QuicLogger()),
                request=data,
            ))
        self.assertEqual(response, data)
예제 #8
0
 def test_empty_trace(self):
     logger = QuicLogger()
     trace = logger.start_trace(is_client=True, odcid=bytes(8))
     logger.end_trace(trace)
     self.assertEqual(
         logger.to_dict(),
         {
             "qlog_version":
             "draft-01",
             "traces": [{
                 "common_fields": {
                     "ODCID": "0000000000000000",
                     "reference_time": "0",
                 },
                 "configuration": {
                     "time_units": "us"
                 },
                 "event_fields": [
                     "relative_time",
                     "category",
                     "event_type",
                     "data",
                 ],
                 "events": [],
                 "vantage_point": {
                     "name": "aioquic",
                     "type": "client"
                 },
             }],
         },
     )
예제 #9
0
 def test_connect_and_serve_with_version_negotiation(self):
     run(self.run_server())
     response = run(
         self.run_client(
             "127.0.0.1",
             configuration=QuicConfiguration(
                 is_client=True,
                 quic_logger=QuicLogger(),
                 supported_versions=[0x1A2A3A4A, QuicProtocolVersion.DRAFT_23],
             ),
         ),
     )
     self.assertEqual(response, b"gnip")
예제 #10
0
    logging.basicConfig(
        format="%(asctime)s %(levelname)s %(name)s %(message)s",
        level=logging.DEBUG if args.verbose else logging.INFO,
    )

    # prepare configuration
    configuration = QuicConfiguration(
        is_client=True,
        alpn_protocols=H0_ALPN if args.legacy_http else H3_ALPN)
    if args.ca_certs:
        configuration.load_verify_locations(args.ca_certs)
    if args.insecure:
        configuration.verify_mode = ssl.CERT_NONE
    if args.quic_log:
        configuration.quic_logger = QuicLogger()
    if args.secrets_log:
        configuration.secrets_log_file = open(args.secrets_log, "a")
    if args.session_ticket:
        try:
            with open(args.session_ticket, "rb") as fp:
                configuration.session_ticket = pickle.load(fp)
        except FileNotFoundError:
            pass

    if uvloop is not None:
        uvloop.install()
    loop = asyncio.get_event_loop()
    try:
        loop.run_until_complete(
            run(
예제 #11
0
                        type=str,
                        help="log QUIC events to a file in QLOG format")
    parser.add_argument("-v",
                        "--verbose",
                        action="store_true",
                        help="increase logging verbosity")

    args = parser.parse_args()

    logging.basicConfig(
        format="%(asctime)s %(levelname)s %(name)s %(message)s",
        level=logging.DEBUG if args.verbose else logging.INFO,
    )

    if args.quic_log:
        quic_logger = QuicLogger()
    else:
        quic_logger = None

    configuration = QuicConfiguration(
        alpn_protocols=["dq"],
        is_client=False,
        max_datagram_frame_size=65536,
        quic_logger=quic_logger,
    )

    configuration.load_cert_chain(args.certificate, args.private_key)

    ticket_store = SessionTicketStore()

    if uvloop is not None:
예제 #12
0
                        help="increase logging verbosity")
    args = parser.parse_args()

    logging.basicConfig(
        format="%(asctime)s %(levelname)s %(name)s %(message)s",
        level=logging.DEBUG if args.verbose else logging.INFO,
    )

    # import ASGI application
    module_str, attr_str = args.app.split(":", maxsplit=1)
    module = importlib.import_module(module_str)
    application = getattr(module, attr_str)

    # create QUIC logger
    if args.quic_log:
        quic_logger = QuicLogger()
    else:
        quic_logger = None

    # open SSL log file
    if args.secrets_log:
        secrets_log_file = open(args.secrets_log, "a")
    else:
        secrets_log_file = None

    # load SSL certificate and key
    with open(args.certificate, "rb") as fp:
        certificate = x509.load_pem_x509_certificate(fp.read(),
                                                     backend=default_backend())
    with open(args.private_key, "rb") as fp:
        private_key = serialization.load_pem_private_key(
예제 #13
0
def transaction(topic_name, topic_text, parts_list):
    async def perform_http_request(client: HttpClient, url: str, data: str,
                                   print_response: bool) -> None:
        # perform request
        start = time.time()
        if data is not None:
            http_events = await client.post(
                url,
                data=data.encode(),
                headers={"content-type": "application/x-www-form-urlencoded"},
            )
        else:
            http_events = await client.get(url)
        elapsed = time.time() - start

        # print speed
        octets = 0
        for http_event in http_events:
            if isinstance(http_event, DataReceived):
                octets += len(http_event.data)
        logger.info("Received %d bytes in %.1f s (%.3f Mbps)" %
                    (octets, elapsed, octets * 8 / elapsed / 1000000))

        # print response
        if print_response:
            for http_event in http_events:
                if isinstance(http_event, HeadersReceived):
                    headers = b""
                    for k, v in http_event.headers:
                        headers += k + b": " + v + b"\r\n"
                    if headers:
                        sys.stderr.buffer.write(headers + b"\r\n")
                        sys.stderr.buffer.flush()
                elif isinstance(http_event, DataReceived):
                    sys.stdout.buffer.write(http_event.data)
                    sys.stdout.buffer.flush()

    def save_session_ticket(ticket):
        """
        Callback which is invoked by the TLS engine when a new session ticket
        is received.
        """
        logger.info("New session ticket received")
        if args.session_ticket:
            with open(args.session_ticket, "wb") as fp:
                pickle.dump(ticket, fp)

    async def run(
        configuration: QuicConfiguration,
        url: str,
        data: str,
        parallel: int,
        print_response: bool,
    ) -> None:
        # parse URL
        parsed = urlparse(url)
        assert parsed.scheme in (
            "https",
            "wss",
        ), "Only https:// or wss:// URLs are supported."
        if ":" in parsed.netloc:
            host, port_str = parsed.netloc.split(":")
            port = int(port_str)
        else:
            host = parsed.netloc
            port = 443

        async with connect(
                host,
                port,
                configuration=configuration,
                create_protocol=HttpClient,
                session_ticket_handler=save_session_ticket,
        ) as client:
            client = cast(HttpClient, client)

            if parsed.scheme == "wss":
                ws = await client.websocket(url,
                                            subprotocols=["chat", "superchat"])

                # print("Hint: To send a message type and press enter.")

                # ******************************
                # *** ASYNCHRONOUS THREADING ***
                def start_loop(loop):
                    asyncio.set_event_loop(loop)
                    loop.run_forever()

                new_loop = asyncio.new_event_loop()
                t = Thread(target=start_loop, args=(new_loop, ))
                t.start()

                async def read_user():
                    # while True:
                    message = add_item(topic_name, topic_text)
                    # message = add_item()
                    await ws.send(message)
                    # CheckLogin()

                asyncio.run_coroutine_threadsafe(read_user(), new_loop)

                # *** STAYS IN MAIN LOOP ***
                while True:
                    messageRec = await ws.recv()
                    print("< " + messageRec)
                    if messageRec == "inserted_item":
                        print("client update list")
                        root = Tk()
                        app = App(root)
                        app.update_text()
                        root.mainloop()

                # *** ASYNCHRONOUS THREADING ***
                # ******************************'''

                await ws.close()
            else:
                # perform request
                coros = [
                    perform_http_request(client=client,
                                         url=url,
                                         data=data,
                                         print_response=print_response)
                    for i in range(parallel)
                ]
                await asyncio.gather(*coros)

    if __name__ == "__main__":
        parser = argparse.ArgumentParser(description="HTTP/3 client")
        parser.add_argument("url",
                            type=str,
                            help="the URL to query (must be HTTPS)")
        parser.add_argument(
            "--ca-certs",
            type=str,
            help="load CA certificates from the specified file")
        parser.add_argument("-d",
                            "--data",
                            type=str,
                            help="send the specified data in a POST request")
        parser.add_argument(
            "-k",
            "--insecure",
            action="store_true",
            help="do not validate server certificate",
        )
        parser.add_argument("--legacy-http",
                            action="store_true",
                            help="use HTTP/0.9")
        parser.add_argument("-q",
                            "--quic-log",
                            type=str,
                            help="log QUIC events to a file in QLOG format")
        parser.add_argument(
            "-l",
            "--secrets-log",
            type=str,
            help="log secrets to a file, for use with Wireshark",
        )
        parser.add_argument("--parallel",
                            type=int,
                            default=1,
                            help="perform this many requests in parallel")
        parser.add_argument("--print-response",
                            action="store_true",
                            help="print response headers and body")
        parser.add_argument(
            "-s",
            "--session-ticket",
            type=str,
            help="read and write session ticket from the specified file",
        )
        parser.add_argument("-v",
                            "--verbose",
                            action="store_true",
                            help="increase logging verbosity")

        args = parser.parse_args()

        logging.basicConfig(
            format="%(asctime)s %(levelname)s %(name)s %(message)s",
            level=logging.DEBUG if args.verbose else logging.INFO,
        )

        # prepare configuration
        configuration = QuicConfiguration(
            is_client=True,
            alpn_protocols=H0_ALPN if args.legacy_http else H3_ALPN)
        if args.ca_certs:
            configuration.load_verify_locations(args.ca_certs)
        if args.insecure:
            configuration.verify_mode = ssl.CERT_NONE
        if args.quic_log:
            configuration.quic_logger = QuicLogger()
        if args.secrets_log:
            configuration.secrets_log_file = open(args.secrets_log, "a")
        if args.session_ticket:
            try:
                with open(args.session_ticket, "rb") as fp:
                    configuration.session_ticket = pickle.load(fp)
            except FileNotFoundError:
                pass

        if uvloop is not None:
            uvloop.install()
        loop = asyncio.get_event_loop()
        try:
            loop.run_until_complete(
                run(
                    configuration=configuration,
                    url=args.url,
                    data=args.data,
                    parallel=args.parallel,
                    print_response=args.print_response,
                ))
        finally:
            if configuration.quic_logger is not None:
                with open(args.quic_log, "w") as logger_fp:
                    json.dump(configuration.quic_logger.to_dict(),
                              logger_fp,
                              indent=4)
예제 #14
0
 def test_empty(self):
     logger = QuicLogger()
     self.assertEqual(logger.to_dict(), {
         "qlog_version": "draft-01",
         "traces": []
     })