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()
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)
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)
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)
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"")
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")
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)
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" }, }], }, )
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")
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(
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:
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(
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)
def test_empty(self): logger = QuicLogger() self.assertEqual(logger.to_dict(), { "qlog_version": "draft-01", "traces": [] })