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)
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()
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()
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()
async def pipe_factory_quic(generate_test_certificates, unused_tcp_port, client_arguments=None, server_arguments=None): certificate, private_key = generate_test_certificates server_configuration = QuicConfiguration(certificate=certificate, private_key=private_key, is_client=False) client_configuration = QuicConfiguration(is_client=True) ca_data = certificate.public_bytes(serialization.Encoding.PEM) client_configuration.load_verify_locations(cadata=ca_data, cafile=None) server: Optional[RSocketBase] = None wait_for_server = Event() def store_server(new_server): nonlocal server server = new_server wait_for_server.set() quic_server = await rsocket_serve(host='localhost', port=unused_tcp_port, configuration=server_configuration, on_server_create=store_server, **(server_arguments or {})) # from datetime import timedelta # test_overrides = {'keep_alive_period': timedelta(minutes=20)} client_arguments = client_arguments or {} # client_arguments.update(test_overrides) async with rsocket_connect( 'localhost', unused_tcp_port, configuration=client_configuration) as transport: async with RSocketClient(single_transport_provider(transport), **client_arguments) as client: await wait_for_server.wait() yield server, client await server.close() assert_no_open_streams(client, server) quic_server.close()
async def run_client(host, port=4433, cadata=None, cafile=SERVER_CACERTFILE, configuration=None, request=b"ping", **kwargs): if configuration is None: configuration = QuicConfiguration(is_client=True) configuration.load_verify_locations(cadata=cadata, cafile=cafile) async with connect(host, port, configuration=configuration, **kwargs) as client: reader, writer = await client.create_stream() assert writer.can_write_eof() is True assert writer.get_extra_info("stream_id") == 0 writer.write(request) writer.write_eof() return await reader.read()
class MultiCall: 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 @property def control_event_history (self): global EVENT_HISTORY eh, EVENT_HISTORY = EVENT_HISTORY, [] return eh def get (self, url, headers = {}, allow_push = True, repeat = 1): self._calls.append (HttpRequest ('GET', self.endpoint + url, b'', headers, allow_push)) def post (self, url, data, headers = {}, allow_push = True, repeat = 1): self._calls.append (HttpRequest ('POST', self.endpoint + url, data, headers, allow_push)) def request (self): loop = asyncio.get_event_loop() try: loop.run_until_complete(run(self.configuration, self._calls)) except RuntimeError: pass return [req.response for req in self._calls]
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()
def make_client(): client_configuration = QuicConfiguration(is_client=True) client_configuration.load_verify_locations(cafile=SERVER_CACERTFILE) client = QuicConnection(configuration=client_configuration) client._ack_delay = 0 return client
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)
class Connection: session_ticket = '/tmp/http3-session-ticket.pik' socket_timeout = 1 def __init__ (self, addr, enable_push = True): # prepare configuration self.netloc = addr try: host, port = addr.split (":", 1) port = int (port) except ValueError: host, port = addr, 443 self.addr = (host, port) 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 self.load_session_ticket () self.socket = socket.socket (socket.AF_INET, socket.SOCK_DGRAM) self.socket.settimeout (self.socket_timeout) self._connected = False self._closed = False self._history = [] self._allow_push = enable_push self._response = None def connect (self): host, port = self.addr try: ipaddress.ip_address(host) server_name = None except ValueError: server_name = host if server_name is not None: self.configuration.server_name = server_name self._quic = QuicConnection ( configuration = self.configuration, session_ticket_handler = self.save_session_ticket ) self._http = H3Connection(self._quic) self._quic.connect(self.addr, now=time.monotonic ()) self.transmit () def close (self): self.transmit () self.recv () #self.socket.close () self._closed = True def transmit (self): dts = self._quic.datagrams_to_send(now=time.monotonic ()) if not dts: return for data, addr in dts: #print ('<---', len (data), repr (data [:30])) sent = self.socket.sendto (data, self.addr) self.recv () def recv (self): while 1: try: data, addr = self.socket.recvfrom (4096) except socket.timeout: break #print ('--->', len (data), repr (data [:30])) self._quic.receive_datagram(data, addr, now = time.monotonic ()) self._process_events() self.transmit () def save_session_ticket (self, ticket): with open(self.session_ticket, "wb") as fp: pickle.dump(ticket, fp) def load_session_ticket (self): try: with open(self.session_ticket, "rb") as fp: self.configuration.session_ticket = pickle.load(fp) except FileNotFoundError: pass def _process_events(self): event = self._quic.next_event() while event is not None: if isinstance(event, events.ConnectionTerminated): self._connected = False self.close () elif isinstance(event, events.HandshakeCompleted): self._connected = True elif isinstance(event, events.PingAcknowledged): pass self.quic_event_received(event) event = self._quic.next_event() def http_event_received (self, http_event): if isinstance(http_event, HeadersReceived): self._response.headers = {k: v for k, v in http_event.headers} elif isinstance(http_event, DataReceived): if http_event.stream_id % 4 == 0: self._response.data += http_event.data elif isinstance(http_event, PushPromiseReceived): push_headers = {} for k, v in http_event.headers: push_headers [k.decode ()] = v.decode () self._response.promises.append (push_headers) def quic_event_received (self, event): # pass event to the HTTP layer if self._response and not isinstance (event, events.StreamDataReceived): self._response.events.append (event) for http_event in self._http.handle_event(event): if not isinstance (http_event, (HeadersReceived, PushPromiseReceived, DataReceived)): # logging only control frames self._response.events.append (http_event) self.http_event_received(http_event) def handle_request (self, request): if self._closed: raise ConnectionClosed if not self._connected: self.connect () self._response = request.response stream_id = self._quic.get_next_available_stream_id() self._response.stream_id = stream_id self._http.send_headers( stream_id=stream_id, headers = [ (b":method", request.method.encode("utf8")), (b":scheme", request.url.scheme.encode("utf8")), (b":authority", request.url.authority.encode("utf8")), (b":path", request.url.full_path.encode("utf8")), (b"user-agent", b"aioquic"), ] + [ (k.encode("utf8"), v.encode("utf8")) for (k, v) in request.headers.items() ], ) self._http.send_data (stream_id=stream_id, data=request.content, end_stream=True) self.transmit() self._response = None return request.response def get (self, url, headers = {}): req = HttpRequest ('GET', 'https://{}{}'.format (self.netloc, url), b'', headers) return self.handle_request (req) def post (self, url, data, headers = {}): req = HttpRequest ('POST', 'https://{}{}'.format (self.netloc, url), data, headers) return self.handle_request (req) def request (self, method, url, data = b'', headers = {}): req = HttpRequest (method, 'https://{}{}'.format (self.netloc, url), data, headers) return self.handle_request (req)