diff --git a/2023f-wip/ice-traversal/client.py b/2023f-wip/ice-traversal/client.py new file mode 100644 index 000000000..41077da58 --- /dev/null +++ b/2023f-wip/ice-traversal/client.py @@ -0,0 +1,70 @@ +import asyncio +import json +import sys +from aiortc import RTCPeerConnection, RTCDataChannel, RTCSessionDescription +from aioquic.asyncio import connect +from aioquic.asyncio.protocol import QuicConnectionProtocol +from aioquic.quic.configuration import QuicConfiguration +from aioquic.quic.events import StreamDataReceived + +async def run(peer_connection: RTCPeerConnection, is_offer: bool): + if is_offer: + data_channel = peer_connection.createDataChannel("chat") + offer = await peer_connection.createOffer() + await peer_connection.setLocalDescription(offer) + print(json.dumps({"sdp": peer_connection.localDescription.sdp, "type": peer_connection.localDescription.type})) + answer = json.loads(input("Enter answer: ")) + await peer_connection.setRemoteDescription(RTCSessionDescription(sdp=answer["sdp"], type=answer["type"])) + else: + offer = json.loads(input("Enter offer: ")) + await peer_connection.setRemoteDescription(RTCSessionDescription(sdp=offer["sdp"], type=offer["type"])) + + answer = await peer_connection.createAnswer() + await peer_connection.setLocalDescription(answer) + print(json.dumps({"sdp": peer_connection.localDescription.sdp, "type": peer_connection.localDescription.type})) + + @peer_connection.on("icecandidate") + async def on_icecandidate(candidate): + print(json.dumps({"candidate": candidate})) + + @peer_connection.on("connectionstatechange") + async def on_connectionstatechange(): + print(f"Connection state is {peer_connection.connectionState}") + if peer_connection.connectionState in ["connected", "completed"]: + print("Connection established!") + + + await asyncio.sleep(30) + +async def main(): + is_offer = len(sys.argv) > 1 and sys.argv[1] == "offer" + peer_connection = RTCPeerConnection() + try: + await run(peer_connection, is_offer) + finally: + await peer_connection.close() + +if __name__ == "__main__": + asyncio.run(main()) + +class EchoClientProtocol(QuicConnectionProtocol): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.received_data = asyncio.Queue() + + def quic_event_received(self, event): + print("Received: ", event) + if isinstance(event, StreamDataReceived): + self.received_data.put_nowait(event.data) + if event.end_stream: + self.close() + +async def run_quic_client(): + configuration = QuicConfiguration(is_client=True) + configuration.load_verify_locations("../tests/pycacert.pem") + + async with connect("localhost", 4433, configuration=configuration, create_protocol=EchoClientProtocol, local_port=12345) as protocol: + stream_id = protocol._quic.get_next_available_stream_id() + protocol._quic.send_stream_data(stream_id, b"Hello!", end_stream=False) + received_data = await protocol.received_data.get() + print("Data Received:", received_data) diff --git a/quic-traversal/client.py b/2023f-wip/quic-traversal/client.py similarity index 77% rename from quic-traversal/client.py rename to 2023f-wip/quic-traversal/client.py index 8c255791f..cacce6787 100644 --- a/quic-traversal/client.py +++ b/2023f-wip/quic-traversal/client.py @@ -1,4 +1,5 @@ import asyncio +import m_socket from aioquic.asyncio import connect from aioquic.asyncio.protocol import QuicConnectionProtocol from aioquic.quic.configuration import QuicConfiguration @@ -18,9 +19,10 @@ def quic_event_received(self, event): async def run_quic_client(): configuration = QuicConfiguration(is_client=True) - configuration.load_verify_locations("../tests/pycacert.pem") - - async with connect("35.232.66.96", 12346, configuration=configuration, create_protocol=EchoClientProtocol, local_port=12345) as protocol: + configuration.load_verify_locations("../../tests/pycacert.pem") + sock = m_socket.create_socket("localhost", 12345) + # 35.232.66.96 + async with connect("localhost", 12346, configuration=configuration, create_protocol=EchoClientProtocol, local_port=1234, sock=sock) as protocol: stream_id = protocol._quic.get_next_available_stream_id() protocol._quic.send_stream_data(stream_id, b"Hello!", end_stream=False) received_data = await protocol.received_data.get() diff --git a/2023f-wip/quic-traversal/m_socket.py b/2023f-wip/quic-traversal/m_socket.py new file mode 100644 index 000000000..7df9e1996 --- /dev/null +++ b/2023f-wip/quic-traversal/m_socket.py @@ -0,0 +1,12 @@ +import socket + +def create_socket(local_host, local_port): + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + completed = False + try: + sock.bind((local_host, local_port)) + completed = True + finally: + if not completed: + sock.close() + return sock diff --git a/quic-traversal/server.py b/2023f-wip/quic-traversal/server.py similarity index 73% rename from quic-traversal/server.py rename to 2023f-wip/quic-traversal/server.py index b876ba233..114157883 100644 --- a/quic-traversal/server.py +++ b/2023f-wip/quic-traversal/server.py @@ -1,6 +1,5 @@ import asyncio -import os -import time +import m_socket from aioquic.asyncio import serve, connect from aioquic.asyncio.protocol import QuicConnectionProtocol from aioquic.quic.configuration import QuicConfiguration @@ -19,17 +18,19 @@ def quic_event_received(self, event): async def run_quic_server(): configuration = QuicConfiguration(is_client=False) - configuration.load_verify_locations("../tests/pycacert.pem") - configuration.load_cert_chain("../tests/ssl_cert.pem", "../tests/ssl_key.pem") - await serve("localhost", 12346, configuration=configuration, create_protocol=EchoQuicProtocol) - await run_quic_client() + configuration.load_verify_locations("../../tests/pycacert.pem") + configuration.load_cert_chain("../../tests/ssl_cert.pem", "../../tests/ssl_key.pem") + sock = m_socket.create_socket("localhost", 12346) + + await serve("localhost", 12346, configuration=configuration, create_protocol=EchoQuicProtocol, sock=sock) + await run_quic_client(sock=sock) await asyncio.Future() -async def run_quic_client(): +async def run_quic_client(sock): print("Running client") configuration = QuicConfiguration(is_client=True) - configuration.load_verify_locations("../tests/pycacert.pem") - async with connect("localhost", 12345, configuration=configuration, create_protocol=EchoQuicProtocol, local_port=12346) as protocol: + configuration.load_verify_locations("../../tests/pycacert.pem") + async with connect("localhost", 12345, configuration=configuration, create_protocol=EchoQuicProtocol, local_port=12346, sock=sock) as protocol: stream_id = protocol._quic.get_next_available_stream_id() protocol._quic.send_stream_data(stream_id, b"Hello!", end_stream=False) received_data = await protocol.received_data.get() diff --git a/src/aioquic/asyncio/client.py b/src/aioquic/asyncio/client.py index 18ae1c9e7..eab7846f7 100644 --- a/src/aioquic/asyncio/client.py +++ b/src/aioquic/asyncio/client.py @@ -23,6 +23,7 @@ async def connect( token_handler: Optional[QuicTokenHandler] = None, wait_connected: bool = True, local_port: int = 0, + sock = None ) -> AsyncGenerator[QuicConnectionProtocol, None]: """ Connect to a QUIC server at the given `host` and `port`. @@ -47,13 +48,15 @@ async def connect( * ``local_port`` is the UDP port number that this client wants to bind. """ loop = asyncio.get_event_loop() - local_host = "::" + local_host = "localhost" # lookup remote address - infos = await loop.getaddrinfo(host, port, type=socket.SOCK_DGRAM) + infos = await loop.getaddrinfo(host, port, type=socket.SOCK_DGRAM, family=socket.AF_INET) addr = infos[0][4] - if len(addr) == 2: - addr = ("::ffff:" + addr[0], addr[1], 0, 0) + print("addr:", addr) + if len(addr) == 4: + # addr = ("::ffff:" + addr[0], addr[1], 0, 0) + addr = (addr[0], addr[1]) # prepare QUIC connection if configuration is None: @@ -66,9 +69,9 @@ async def connect( token_handler=token_handler, ) - # explicitly enable IPv4/IPv6 dual stack - sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) - completed = False + # # explicitly enable IPv4/IPv6 dual stack + # sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) + # completed = False # try: # sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0) # sock.bind((local_host, local_port, 0, 0)) @@ -84,6 +87,7 @@ async def connect( protocol = cast(QuicConnectionProtocol, protocol) try: protocol.connect(addr) + await protocol.ping() if wait_connected: await protocol.wait_connected() yield protocol diff --git a/src/aioquic/asyncio/protocol.py b/src/aioquic/asyncio/protocol.py index 8b4a6b5e9..13b532a9b 100644 --- a/src/aioquic/asyncio/protocol.py +++ b/src/aioquic/asyncio/protocol.py @@ -13,7 +13,6 @@ def __init__( self, quic: QuicConnection, stream_handler: Optional[QuicStreamHandler] = None ): loop = asyncio.get_event_loop() - self._closed = asyncio.Event() self._connected = False self._connected_waiter: Optional[asyncio.Future[None]] = None @@ -97,10 +96,9 @@ def transmit(self) -> None: Send pending datagrams to the peer and arm the timer if needed. """ self._transmit_task = None - # send datagrams for data, addr in self._quic.datagrams_to_send(now=self._loop.time()): - print("Sending to", addr) + print("Sending to", addr, self.transmit_count) self._transport.sendto(data, addr) # re-arm timer diff --git a/src/aioquic/asyncio/server.py b/src/aioquic/asyncio/server.py index b5f6cd8d7..557f8fa24 100644 --- a/src/aioquic/asyncio/server.py +++ b/src/aioquic/asyncio/server.py @@ -1,5 +1,6 @@ import asyncio import os +import socket from functools import partial from typing import Callable, Dict, Optional, Text, Union, cast @@ -173,6 +174,7 @@ async def serve( session_ticket_handler: Optional[SessionTicketHandler] = None, retry: bool = False, stream_handler: QuicStreamHandler = None, + sock = None ) -> QuicServer: """ Start a QUIC server at the given `host` and `port`. @@ -198,9 +200,8 @@ async def serve( created. It must accept two arguments: a :class:`asyncio.StreamReader` and a :class:`asyncio.StreamWriter`. """ - + print("Serving") loop = asyncio.get_event_loop() - _, protocol = await loop.create_datagram_endpoint( lambda: QuicServer( configuration=configuration, @@ -210,6 +211,8 @@ async def serve( retry=retry, stream_handler=stream_handler, ), - local_addr=(host, port), + # local_addr=(host, port), + sock=sock, + # family=socket.AF_INET, ) return protocol