Esempio n. 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:
         coros = [client.ping() for x in range(16)]
         await asyncio.gather(*coros)
Esempio n. 2
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()
Esempio n. 3
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()
Esempio n. 4
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()
Esempio n. 5
0
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()
Esempio n. 6
0
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()
Esempio n. 7
0
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]
Esempio n. 8
0
                        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()
Esempio n. 9
0
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
Esempio n. 10
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)
Esempio n. 11
0
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)