mirror of
https://github.com/varun-r-mallya/py-libp2p.git
synced 2026-02-12 16:10:57 +00:00
fix: succesfull echo example completed
This commit is contained in:
@ -55,7 +55,7 @@ async def run_server(port: int, seed: int | None = None) -> None:
|
|||||||
# QUIC transport configuration
|
# QUIC transport configuration
|
||||||
quic_config = QUICTransportConfig(
|
quic_config = QUICTransportConfig(
|
||||||
idle_timeout=30.0,
|
idle_timeout=30.0,
|
||||||
max_concurrent_streams=1000,
|
max_concurrent_streams=100,
|
||||||
connection_timeout=10.0,
|
connection_timeout=10.0,
|
||||||
enable_draft29=False,
|
enable_draft29=False,
|
||||||
)
|
)
|
||||||
@ -68,16 +68,21 @@ async def run_server(port: int, seed: int | None = None) -> None:
|
|||||||
|
|
||||||
# Server mode: start listener
|
# Server mode: start listener
|
||||||
async with host.run(listen_addrs=[listen_addr]):
|
async with host.run(listen_addrs=[listen_addr]):
|
||||||
print(f"I am {host.get_id().to_string()}")
|
try:
|
||||||
host.set_stream_handler(PROTOCOL_ID, _echo_stream_handler)
|
print(f"I am {host.get_id().to_string()}")
|
||||||
|
host.set_stream_handler(PROTOCOL_ID, _echo_stream_handler)
|
||||||
|
|
||||||
print(
|
print(
|
||||||
"Run this from the same folder in another console:\n\n"
|
"Run this from the same folder in another console:\n\n"
|
||||||
f"python3 ./examples/echo/echo_quic.py "
|
f"python3 ./examples/echo/echo_quic.py "
|
||||||
f"-d {host.get_addrs()[0]}\n"
|
f"-d {host.get_addrs()[0]}\n"
|
||||||
)
|
)
|
||||||
print("Waiting for incoming QUIC connections...")
|
print("Waiting for incoming QUIC connections...")
|
||||||
await trio.sleep_forever()
|
await trio.sleep_forever()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("Closing server gracefully...")
|
||||||
|
await host.close()
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
async def run_client(destination: str, seed: int | None = None) -> None:
|
async def run_client(destination: str, seed: int | None = None) -> None:
|
||||||
@ -96,7 +101,7 @@ async def run_client(destination: str, seed: int | None = None) -> None:
|
|||||||
# QUIC transport configuration
|
# QUIC transport configuration
|
||||||
quic_config = QUICTransportConfig(
|
quic_config = QUICTransportConfig(
|
||||||
idle_timeout=30.0,
|
idle_timeout=30.0,
|
||||||
max_concurrent_streams=1000,
|
max_concurrent_streams=100,
|
||||||
connection_timeout=10.0,
|
connection_timeout=10.0,
|
||||||
enable_draft29=False,
|
enable_draft29=False,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -299,9 +299,7 @@ class BasicHost(IHost):
|
|||||||
)
|
)
|
||||||
except MultiselectError as error:
|
except MultiselectError as error:
|
||||||
peer_id = net_stream.muxed_conn.peer_id
|
peer_id = net_stream.muxed_conn.peer_id
|
||||||
logger.debug(
|
print("failed to accept a stream from peer %s, error=%s", peer_id, error)
|
||||||
"failed to accept a stream from peer %s, error=%s", peer_id, error
|
|
||||||
)
|
|
||||||
await net_stream.reset()
|
await net_stream.reset()
|
||||||
return
|
return
|
||||||
if protocol is None:
|
if protocol is None:
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
from builtins import AssertionError
|
||||||
|
|
||||||
from libp2p.abc import (
|
from libp2p.abc import (
|
||||||
IMultiselectCommunicator,
|
IMultiselectCommunicator,
|
||||||
)
|
)
|
||||||
@ -36,7 +38,8 @@ class MultiselectCommunicator(IMultiselectCommunicator):
|
|||||||
msg_bytes = encode_delim(msg_str.encode())
|
msg_bytes = encode_delim(msg_str.encode())
|
||||||
try:
|
try:
|
||||||
await self.read_writer.write(msg_bytes)
|
await self.read_writer.write(msg_bytes)
|
||||||
except IOException as error:
|
# Handle for connection close during ongoing negotiation in QUIC
|
||||||
|
except (IOException, AssertionError, ValueError) as error:
|
||||||
raise MultiselectCommunicatorError(
|
raise MultiselectCommunicatorError(
|
||||||
"fail to write to multiselect communicator"
|
"fail to write to multiselect communicator"
|
||||||
) from error
|
) from error
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
from typing import Literal
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Configuration classes for QUIC transport.
|
Configuration classes for QUIC transport.
|
||||||
"""
|
"""
|
||||||
@ -64,7 +66,7 @@ class QUICTransportConfig:
|
|||||||
alpn_protocols: list[str] = field(default_factory=lambda: ["libp2p"])
|
alpn_protocols: list[str] = field(default_factory=lambda: ["libp2p"])
|
||||||
|
|
||||||
# Performance settings
|
# Performance settings
|
||||||
max_concurrent_streams: int = 1000 # Maximum concurrent streams per connection
|
max_concurrent_streams: int = 100 # Maximum concurrent streams per connection
|
||||||
connection_window: int = 1024 * 1024 # Connection flow control window
|
connection_window: int = 1024 * 1024 # Connection flow control window
|
||||||
stream_window: int = 64 * 1024 # Stream flow control window
|
stream_window: int = 64 * 1024 # Stream flow control window
|
||||||
|
|
||||||
@ -299,10 +301,11 @@ class QUICStreamMetricsConfig:
|
|||||||
self.metrics_aggregation_interval = metrics_aggregation_interval
|
self.metrics_aggregation_interval = metrics_aggregation_interval
|
||||||
|
|
||||||
|
|
||||||
# Factory function for creating optimized configurations
|
def create_stream_config_for_use_case(
|
||||||
|
use_case: Literal[
|
||||||
|
"high_throughput", "low_latency", "many_streams", "memory_constrained"
|
||||||
def create_stream_config_for_use_case(use_case: str) -> QUICTransportConfig:
|
],
|
||||||
|
) -> QUICTransportConfig:
|
||||||
"""
|
"""
|
||||||
Create optimized stream configuration for specific use cases.
|
Create optimized stream configuration for specific use cases.
|
||||||
|
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import trio
|
|||||||
from libp2p.abc import IMuxedConn, IRawConnection
|
from libp2p.abc import IMuxedConn, IRawConnection
|
||||||
from libp2p.custom_types import TQUICStreamHandlerFn
|
from libp2p.custom_types import TQUICStreamHandlerFn
|
||||||
from libp2p.peer.id import ID
|
from libp2p.peer.id import ID
|
||||||
|
from libp2p.stream_muxer.exceptions import MuxedConnUnavailable
|
||||||
|
|
||||||
from .exceptions import (
|
from .exceptions import (
|
||||||
QUICConnectionClosedError,
|
QUICConnectionClosedError,
|
||||||
@ -64,8 +65,7 @@ class QUICConnection(IRawConnection, IMuxedConn):
|
|||||||
- COMPLETE connection ID management (fixes the original issue)
|
- COMPLETE connection ID management (fixes the original issue)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Configuration constants based on research
|
MAX_CONCURRENT_STREAMS = 100
|
||||||
MAX_CONCURRENT_STREAMS = 1000
|
|
||||||
MAX_INCOMING_STREAMS = 1000
|
MAX_INCOMING_STREAMS = 1000
|
||||||
MAX_OUTGOING_STREAMS = 1000
|
MAX_OUTGOING_STREAMS = 1000
|
||||||
STREAM_ACCEPT_TIMEOUT = 30.0
|
STREAM_ACCEPT_TIMEOUT = 30.0
|
||||||
@ -76,7 +76,7 @@ class QUICConnection(IRawConnection, IMuxedConn):
|
|||||||
self,
|
self,
|
||||||
quic_connection: QuicConnection,
|
quic_connection: QuicConnection,
|
||||||
remote_addr: tuple[str, int],
|
remote_addr: tuple[str, int],
|
||||||
peer_id: ID,
|
remote_peer_id: ID | None,
|
||||||
local_peer_id: ID,
|
local_peer_id: ID,
|
||||||
is_initiator: bool,
|
is_initiator: bool,
|
||||||
maddr: multiaddr.Multiaddr,
|
maddr: multiaddr.Multiaddr,
|
||||||
@ -91,7 +91,7 @@ class QUICConnection(IRawConnection, IMuxedConn):
|
|||||||
Args:
|
Args:
|
||||||
quic_connection: aioquic QuicConnection instance
|
quic_connection: aioquic QuicConnection instance
|
||||||
remote_addr: Remote peer address
|
remote_addr: Remote peer address
|
||||||
peer_id: Remote peer ID (may be None initially)
|
remote_peer_id: Remote peer ID (may be None initially)
|
||||||
local_peer_id: Local peer ID
|
local_peer_id: Local peer ID
|
||||||
is_initiator: Whether this is the connection initiator
|
is_initiator: Whether this is the connection initiator
|
||||||
maddr: Multiaddr for this connection
|
maddr: Multiaddr for this connection
|
||||||
@ -103,8 +103,9 @@ class QUICConnection(IRawConnection, IMuxedConn):
|
|||||||
"""
|
"""
|
||||||
self._quic = quic_connection
|
self._quic = quic_connection
|
||||||
self._remote_addr = remote_addr
|
self._remote_addr = remote_addr
|
||||||
self.peer_id = peer_id
|
self._remote_peer_id = remote_peer_id
|
||||||
self._local_peer_id = local_peer_id
|
self._local_peer_id = local_peer_id
|
||||||
|
self.peer_id = remote_peer_id or local_peer_id
|
||||||
self.__is_initiator = is_initiator
|
self.__is_initiator = is_initiator
|
||||||
self._maddr = maddr
|
self._maddr = maddr
|
||||||
self._transport = transport
|
self._transport = transport
|
||||||
@ -134,7 +135,7 @@ class QUICConnection(IRawConnection, IMuxedConn):
|
|||||||
self._accept_queue_lock = trio.Lock()
|
self._accept_queue_lock = trio.Lock()
|
||||||
|
|
||||||
# Connection state
|
# Connection state
|
||||||
self._closed = False
|
self._closed: bool = False
|
||||||
self._established = False
|
self._established = False
|
||||||
self._started = False
|
self._started = False
|
||||||
self._handshake_completed = False
|
self._handshake_completed = False
|
||||||
@ -179,7 +180,7 @@ class QUICConnection(IRawConnection, IMuxedConn):
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f"Created QUIC connection to {peer_id} "
|
f"Created QUIC connection to {remote_peer_id} "
|
||||||
f"(initiator: {is_initiator}, addr: {remote_addr}, "
|
f"(initiator: {is_initiator}, addr: {remote_addr}, "
|
||||||
"security: {security_manager is not None})"
|
"security: {security_manager is not None})"
|
||||||
)
|
)
|
||||||
@ -238,7 +239,7 @@ class QUICConnection(IRawConnection, IMuxedConn):
|
|||||||
|
|
||||||
def remote_peer_id(self) -> ID | None:
|
def remote_peer_id(self) -> ID | None:
|
||||||
"""Get the remote peer ID."""
|
"""Get the remote peer ID."""
|
||||||
return self.peer_id
|
return self._remote_peer_id
|
||||||
|
|
||||||
# *** NEW: Connection ID management methods ***
|
# *** NEW: Connection ID management methods ***
|
||||||
def get_connection_id_stats(self) -> dict[str, Any]:
|
def get_connection_id_stats(self) -> dict[str, Any]:
|
||||||
@ -277,7 +278,7 @@ class QUICConnection(IRawConnection, IMuxedConn):
|
|||||||
|
|
||||||
self._started = True
|
self._started = True
|
||||||
self.event_started.set()
|
self.event_started.set()
|
||||||
logger.debug(f"Starting QUIC connection to {self.peer_id}")
|
logger.debug(f"Starting QUIC connection to {self._remote_peer_id}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# If this is a client connection, we need to establish the connection
|
# If this is a client connection, we need to establish the connection
|
||||||
@ -288,7 +289,7 @@ class QUICConnection(IRawConnection, IMuxedConn):
|
|||||||
self._established = True
|
self._established = True
|
||||||
self._connected_event.set()
|
self._connected_event.set()
|
||||||
|
|
||||||
logger.debug(f"QUIC connection to {self.peer_id} started")
|
logger.debug(f"QUIC connection to {self._remote_peer_id} started")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to start connection: {e}")
|
logger.error(f"Failed to start connection: {e}")
|
||||||
@ -360,7 +361,7 @@ class QUICConnection(IRawConnection, IMuxedConn):
|
|||||||
await self._verify_peer_identity_with_security()
|
await self._verify_peer_identity_with_security()
|
||||||
|
|
||||||
self._established = True
|
self._established = True
|
||||||
logger.info(f"QUIC connection established with {self.peer_id}")
|
logger.info(f"QUIC connection established with {self._remote_peer_id}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to establish connection: {e}")
|
logger.error(f"Failed to establish connection: {e}")
|
||||||
@ -495,16 +496,16 @@ class QUICConnection(IRawConnection, IMuxedConn):
|
|||||||
# Verify peer identity using security manager
|
# Verify peer identity using security manager
|
||||||
verified_peer_id = self._security_manager.verify_peer_identity(
|
verified_peer_id = self._security_manager.verify_peer_identity(
|
||||||
self._peer_certificate,
|
self._peer_certificate,
|
||||||
self.peer_id, # Expected peer ID for outbound connections
|
self._remote_peer_id, # Expected peer ID for outbound connections
|
||||||
)
|
)
|
||||||
|
|
||||||
# Update peer ID if it wasn't known (inbound connections)
|
# Update peer ID if it wasn't known (inbound connections)
|
||||||
if not self.peer_id:
|
if not self._remote_peer_id:
|
||||||
self.peer_id = verified_peer_id
|
self._remote_peer_id = verified_peer_id
|
||||||
logger.info(f"Discovered peer ID from certificate: {verified_peer_id}")
|
logger.info(f"Discovered peer ID from certificate: {verified_peer_id}")
|
||||||
elif self.peer_id != verified_peer_id:
|
elif self._remote_peer_id != verified_peer_id:
|
||||||
raise QUICPeerVerificationError(
|
raise QUICPeerVerificationError(
|
||||||
f"Peer ID mismatch: expected {self.peer_id}, got {verified_peer_id}"
|
f"Peer ID mismatch: expected {self._remote_peer_id}, got {verified_peer_id}"
|
||||||
)
|
)
|
||||||
|
|
||||||
self._peer_verified = True
|
self._peer_verified = True
|
||||||
@ -608,7 +609,7 @@ class QUICConnection(IRawConnection, IMuxedConn):
|
|||||||
info: dict[str, bool | Any | None] = {
|
info: dict[str, bool | Any | None] = {
|
||||||
"peer_verified": self._peer_verified,
|
"peer_verified": self._peer_verified,
|
||||||
"handshake_complete": self._handshake_completed,
|
"handshake_complete": self._handshake_completed,
|
||||||
"peer_id": str(self.peer_id) if self.peer_id else None,
|
"peer_id": str(self._remote_peer_id) if self._remote_peer_id else None,
|
||||||
"local_peer_id": str(self._local_peer_id),
|
"local_peer_id": str(self._local_peer_id),
|
||||||
"is_initiator": self.__is_initiator,
|
"is_initiator": self.__is_initiator,
|
||||||
"has_certificate": self._peer_certificate is not None,
|
"has_certificate": self._peer_certificate is not None,
|
||||||
@ -742,6 +743,9 @@ class QUICConnection(IRawConnection, IMuxedConn):
|
|||||||
|
|
||||||
with trio.move_on_after(timeout):
|
with trio.move_on_after(timeout):
|
||||||
while True:
|
while True:
|
||||||
|
if self._closed:
|
||||||
|
raise MuxedConnUnavailable("QUIC connection is closed")
|
||||||
|
|
||||||
async with self._accept_queue_lock:
|
async with self._accept_queue_lock:
|
||||||
if self._stream_accept_queue:
|
if self._stream_accept_queue:
|
||||||
stream = self._stream_accept_queue.pop(0)
|
stream = self._stream_accept_queue.pop(0)
|
||||||
@ -749,15 +753,20 @@ class QUICConnection(IRawConnection, IMuxedConn):
|
|||||||
return stream
|
return stream
|
||||||
|
|
||||||
if self._closed:
|
if self._closed:
|
||||||
raise QUICConnectionClosedError(
|
raise MuxedConnUnavailable(
|
||||||
"Connection closed while accepting stream"
|
"Connection closed while accepting stream"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Wait for new streams
|
# Wait for new streams
|
||||||
await self._stream_accept_event.wait()
|
await self._stream_accept_event.wait()
|
||||||
self._stream_accept_event = trio.Event()
|
|
||||||
|
|
||||||
raise QUICStreamTimeoutError(f"Stream accept timed out after {timeout}s")
|
print(
|
||||||
|
f"{id(self)} ACCEPT STREAM TIMEOUT: CONNECTION STATE {self._closed_event.is_set() or self._closed}"
|
||||||
|
)
|
||||||
|
if self._closed_event.is_set() or self._closed:
|
||||||
|
raise MuxedConnUnavailable("QUIC connection closed during timeout")
|
||||||
|
else:
|
||||||
|
raise QUICStreamTimeoutError(f"Stream accept timed out after {timeout}s")
|
||||||
|
|
||||||
def set_stream_handler(self, handler_function: TQUICStreamHandlerFn) -> None:
|
def set_stream_handler(self, handler_function: TQUICStreamHandlerFn) -> None:
|
||||||
"""
|
"""
|
||||||
@ -979,6 +988,11 @@ class QUICConnection(IRawConnection, IMuxedConn):
|
|||||||
self._closed = True
|
self._closed = True
|
||||||
self._closed_event.set()
|
self._closed_event.set()
|
||||||
|
|
||||||
|
self._stream_accept_event.set()
|
||||||
|
print(f"✅ TERMINATION: Woke up pending accept_stream() calls, {id(self)}")
|
||||||
|
|
||||||
|
await self._notify_parent_of_termination()
|
||||||
|
|
||||||
async def _handle_stream_data(self, event: events.StreamDataReceived) -> None:
|
async def _handle_stream_data(self, event: events.StreamDataReceived) -> None:
|
||||||
"""Handle stream data events - create streams and add to accept queue."""
|
"""Handle stream data events - create streams and add to accept queue."""
|
||||||
stream_id = event.stream_id
|
stream_id = event.stream_id
|
||||||
@ -1191,7 +1205,7 @@ class QUICConnection(IRawConnection, IMuxedConn):
|
|||||||
return
|
return
|
||||||
|
|
||||||
self._closed = True
|
self._closed = True
|
||||||
logger.debug(f"Closing QUIC connection to {self.peer_id}")
|
logger.debug(f"Closing QUIC connection to {self._remote_peer_id}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Close all streams gracefully
|
# Close all streams gracefully
|
||||||
@ -1233,11 +1247,62 @@ class QUICConnection(IRawConnection, IMuxedConn):
|
|||||||
self._streams.clear()
|
self._streams.clear()
|
||||||
self._closed_event.set()
|
self._closed_event.set()
|
||||||
|
|
||||||
logger.debug(f"QUIC connection to {self.peer_id} closed")
|
logger.debug(f"QUIC connection to {self._remote_peer_id} closed")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error during connection close: {e}")
|
logger.error(f"Error during connection close: {e}")
|
||||||
|
|
||||||
|
async def _notify_parent_of_termination(self) -> None:
|
||||||
|
"""
|
||||||
|
Notify the parent listener/transport to remove this connection from tracking.
|
||||||
|
|
||||||
|
This ensures that terminated connections are cleaned up from the
|
||||||
|
'established connections' list.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if self._transport:
|
||||||
|
await self._transport._cleanup_terminated_connection(self)
|
||||||
|
logger.debug("Notified transport of connection termination")
|
||||||
|
return
|
||||||
|
|
||||||
|
for listener in self._transport._listeners:
|
||||||
|
try:
|
||||||
|
await listener._remove_connection_by_object(self)
|
||||||
|
logger.debug(
|
||||||
|
"Found and notified listener of connection termination"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Method 4: Use connection ID if we have one (most reliable)
|
||||||
|
if self._current_connection_id:
|
||||||
|
await self._cleanup_by_connection_id(self._current_connection_id)
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.warning(
|
||||||
|
"Could not notify parent of connection termination - no parent reference found"
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error notifying parent of connection termination: {e}")
|
||||||
|
|
||||||
|
async def _cleanup_by_connection_id(self, connection_id: bytes) -> None:
|
||||||
|
"""Cleanup using connection ID as a fallback method."""
|
||||||
|
try:
|
||||||
|
for listener in self._transport._listeners:
|
||||||
|
for tracked_cid, tracked_conn in list(listener._connections.items()):
|
||||||
|
if tracked_conn is self:
|
||||||
|
await listener._remove_connection(tracked_cid)
|
||||||
|
logger.debug(
|
||||||
|
f"Removed connection {tracked_cid.hex()} by object reference"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.debug("Fallback cleanup by connection ID completed")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error in fallback cleanup: {e}")
|
||||||
|
|
||||||
# IRawConnection interface (for compatibility)
|
# IRawConnection interface (for compatibility)
|
||||||
|
|
||||||
def get_remote_address(self) -> tuple[str, int]:
|
def get_remote_address(self) -> tuple[str, int]:
|
||||||
@ -1333,7 +1398,7 @@ class QUICConnection(IRawConnection, IMuxedConn):
|
|||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return (
|
return (
|
||||||
f"QUICConnection(peer={self.peer_id}, "
|
f"QUICConnection(peer={self._remote_peer_id}, "
|
||||||
f"addr={self._remote_addr}, "
|
f"addr={self._remote_addr}, "
|
||||||
f"initiator={self.__is_initiator}, "
|
f"initiator={self.__is_initiator}, "
|
||||||
f"verified={self._peer_verified}, "
|
f"verified={self._peer_verified}, "
|
||||||
@ -1343,4 +1408,4 @@ class QUICConnection(IRawConnection, IMuxedConn):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"QUICConnection({self.peer_id})"
|
return f"QUICConnection({self._remote_peer_id})"
|
||||||
|
|||||||
@ -880,42 +880,49 @@ class QUICListener(IListener):
|
|||||||
|
|
||||||
async def _promote_pending_connection(
|
async def _promote_pending_connection(
|
||||||
self, quic_conn: QuicConnection, addr: tuple[str, int], dest_cid: bytes
|
self, quic_conn: QuicConnection, addr: tuple[str, int], dest_cid: bytes
|
||||||
) -> None:
|
):
|
||||||
"""Promote a pending connection to an established connection."""
|
"""Promote pending connection - avoid duplicate creation."""
|
||||||
try:
|
try:
|
||||||
# Remove from pending connections
|
# Remove from pending connections
|
||||||
self._pending_connections.pop(dest_cid, None)
|
self._pending_connections.pop(dest_cid, None)
|
||||||
|
|
||||||
# Create multiaddr for this connection
|
# CHECK: Does QUICConnection already exist?
|
||||||
host, port = addr
|
if dest_cid in self._connections:
|
||||||
quic_version = "quic"
|
connection = self._connections[dest_cid]
|
||||||
remote_maddr = create_quic_multiaddr(host, port, f"/{quic_version}")
|
print(
|
||||||
|
f"🔄 PROMOTION: Using existing QUICConnection {id(connection)} for {dest_cid.hex()}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
from .connection import QUICConnection
|
||||||
|
|
||||||
from .connection import QUICConnection
|
host, port = addr
|
||||||
|
quic_version = "quic"
|
||||||
|
remote_maddr = create_quic_multiaddr(host, port, f"/{quic_version}")
|
||||||
|
|
||||||
connection = QUICConnection(
|
connection = QUICConnection(
|
||||||
quic_connection=quic_conn,
|
quic_connection=quic_conn,
|
||||||
remote_addr=addr,
|
remote_addr=addr,
|
||||||
peer_id=None,
|
remote_peer_id=None,
|
||||||
local_peer_id=self._transport._peer_id,
|
local_peer_id=self._transport._peer_id,
|
||||||
is_initiator=False,
|
is_initiator=False,
|
||||||
maddr=remote_maddr,
|
maddr=remote_maddr,
|
||||||
transport=self._transport,
|
transport=self._transport,
|
||||||
security_manager=self._security_manager,
|
security_manager=self._security_manager,
|
||||||
listener_socket=self._socket,
|
listener_socket=self._socket,
|
||||||
)
|
)
|
||||||
|
|
||||||
print(
|
print(
|
||||||
f"🔧 PROMOTION: Created connection with socket: {self._socket is not None}"
|
f"🔄 PROMOTION: Created NEW QUICConnection {id(connection)} for {dest_cid.hex()}"
|
||||||
)
|
)
|
||||||
print(
|
|
||||||
f"🔧 PROMOTION: Socket type: {type(self._socket) if self._socket else 'None'}"
|
|
||||||
)
|
|
||||||
|
|
||||||
self._connections[dest_cid] = connection
|
# Store the connection
|
||||||
|
self._connections[dest_cid] = connection
|
||||||
|
|
||||||
|
# Update mappings
|
||||||
self._addr_to_cid[addr] = dest_cid
|
self._addr_to_cid[addr] = dest_cid
|
||||||
self._cid_to_addr[dest_cid] = addr
|
self._cid_to_addr[dest_cid] = addr
|
||||||
|
|
||||||
|
# Rest of the existing promotion code...
|
||||||
if self._nursery:
|
if self._nursery:
|
||||||
await connection.connect(self._nursery)
|
await connection.connect(self._nursery)
|
||||||
|
|
||||||
@ -932,10 +939,11 @@ class QUICListener(IListener):
|
|||||||
await connection.close()
|
await connection.close()
|
||||||
return
|
return
|
||||||
|
|
||||||
# Call the connection handler
|
if self._transport._swarm:
|
||||||
if self._nursery:
|
print(f"🔄 PROMOTION: Adding connection {id(connection)} to swarm")
|
||||||
self._nursery.start_soon(
|
await self._transport._swarm.add_conn(connection)
|
||||||
self._handle_new_established_connection, connection
|
print(
|
||||||
|
f"🔄 PROMOTION: Successfully added connection {id(connection)} to swarm"
|
||||||
)
|
)
|
||||||
|
|
||||||
self._stats["connections_accepted"] += 1
|
self._stats["connections_accepted"] += 1
|
||||||
@ -946,7 +954,6 @@ class QUICListener(IListener):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"❌ Error promoting connection {dest_cid.hex()}: {e}")
|
logger.error(f"❌ Error promoting connection {dest_cid.hex()}: {e}")
|
||||||
await self._remove_connection(dest_cid)
|
await self._remove_connection(dest_cid)
|
||||||
self._stats["connections_rejected"] += 1
|
|
||||||
|
|
||||||
async def _remove_connection(self, dest_cid: bytes) -> None:
|
async def _remove_connection(self, dest_cid: bytes) -> None:
|
||||||
"""Remove connection by connection ID."""
|
"""Remove connection by connection ID."""
|
||||||
@ -1220,6 +1227,32 @@ class QUICListener(IListener):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error closing listener: {e}")
|
logger.error(f"Error closing listener: {e}")
|
||||||
|
|
||||||
|
async def _remove_connection_by_object(self, connection_obj) -> None:
|
||||||
|
"""Remove a connection by object reference (called when connection terminates)."""
|
||||||
|
try:
|
||||||
|
# Find the connection ID for this object
|
||||||
|
connection_cid = None
|
||||||
|
for cid, tracked_connection in self._connections.items():
|
||||||
|
if tracked_connection is connection_obj:
|
||||||
|
connection_cid = cid
|
||||||
|
break
|
||||||
|
|
||||||
|
if connection_cid:
|
||||||
|
await self._remove_connection(connection_cid)
|
||||||
|
logger.debug(
|
||||||
|
f"✅ TERMINATION: Removed connection {connection_cid.hex()} by object reference"
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
f"✅ TERMINATION: Removed connection {connection_cid.hex()} by object reference"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
logger.warning("⚠️ TERMINATION: Connection object not found in tracking")
|
||||||
|
print("⚠️ TERMINATION: Connection object not found in tracking")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"❌ TERMINATION: Error removing connection by object: {e}")
|
||||||
|
print(f"❌ TERMINATION: Error removing connection by object: {e}")
|
||||||
|
|
||||||
def get_addresses(self) -> list[Multiaddr]:
|
def get_addresses(self) -> list[Multiaddr]:
|
||||||
"""Get the bound addresses."""
|
"""Get the bound addresses."""
|
||||||
return self._bound_addresses.copy()
|
return self._bound_addresses.copy()
|
||||||
|
|||||||
@ -218,13 +218,11 @@ class QUICTransport(ITransport):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# Access attributes directly from QUICTLSSecurityConfig
|
|
||||||
config.certificate = tls_config.certificate
|
config.certificate = tls_config.certificate
|
||||||
config.private_key = tls_config.private_key
|
config.private_key = tls_config.private_key
|
||||||
config.certificate_chain = tls_config.certificate_chain
|
config.certificate_chain = tls_config.certificate_chain
|
||||||
config.alpn_protocols = tls_config.alpn_protocols
|
config.alpn_protocols = tls_config.alpn_protocols
|
||||||
|
|
||||||
# Set verification mode (though libp2p typically doesn't verify)
|
|
||||||
config.verify_mode = tls_config.verify_mode
|
config.verify_mode = tls_config.verify_mode
|
||||||
|
|
||||||
config.verify_mode = ssl.CERT_NONE
|
config.verify_mode = ssl.CERT_NONE
|
||||||
@ -285,12 +283,12 @@ class QUICTransport(ITransport):
|
|||||||
connection = QUICConnection(
|
connection = QUICConnection(
|
||||||
quic_connection=native_quic_connection,
|
quic_connection=native_quic_connection,
|
||||||
remote_addr=(host, port),
|
remote_addr=(host, port),
|
||||||
peer_id=peer_id,
|
remote_peer_id=peer_id,
|
||||||
local_peer_id=self._peer_id,
|
local_peer_id=self._peer_id,
|
||||||
is_initiator=True,
|
is_initiator=True,
|
||||||
maddr=maddr,
|
maddr=maddr,
|
||||||
transport=self,
|
transport=self,
|
||||||
security_manager=self._security_manager, # Pass security manager
|
security_manager=self._security_manager,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Establish connection using trio
|
# Establish connection using trio
|
||||||
@ -389,7 +387,7 @@ class QUICTransport(ITransport):
|
|||||||
handler_function=handler_function,
|
handler_function=handler_function,
|
||||||
quic_configs=server_configs,
|
quic_configs=server_configs,
|
||||||
config=self._config,
|
config=self._config,
|
||||||
security_manager=self._security_manager, # Pass security manager
|
security_manager=self._security_manager,
|
||||||
)
|
)
|
||||||
|
|
||||||
self._listeners.append(listener)
|
self._listeners.append(listener)
|
||||||
@ -456,6 +454,17 @@ class QUICTransport(ITransport):
|
|||||||
|
|
||||||
print("QUIC transport closed")
|
print("QUIC transport closed")
|
||||||
|
|
||||||
|
async def _cleanup_terminated_connection(self, connection) -> None:
|
||||||
|
"""Clean up a terminated connection from all listeners."""
|
||||||
|
try:
|
||||||
|
for listener in self._listeners:
|
||||||
|
await listener._remove_connection_by_object(connection)
|
||||||
|
logger.debug(
|
||||||
|
"✅ TRANSPORT: Cleaned up terminated connection from all listeners"
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"❌ TRANSPORT: Error cleaning up terminated connection: {e}")
|
||||||
|
|
||||||
def get_stats(self) -> dict[str, int | list[str] | object]:
|
def get_stats(self) -> dict[str, int | list[str] | object]:
|
||||||
"""Get transport statistics including security info."""
|
"""Get transport statistics including security info."""
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -69,7 +69,7 @@ class TestQUICConnection:
|
|||||||
return QUICConnection(
|
return QUICConnection(
|
||||||
quic_connection=mock_quic_connection,
|
quic_connection=mock_quic_connection,
|
||||||
remote_addr=("127.0.0.1", 4001),
|
remote_addr=("127.0.0.1", 4001),
|
||||||
peer_id=peer_id,
|
remote_peer_id=None,
|
||||||
local_peer_id=peer_id,
|
local_peer_id=peer_id,
|
||||||
is_initiator=True,
|
is_initiator=True,
|
||||||
maddr=Multiaddr("/ip4/127.0.0.1/udp/4001/quic"),
|
maddr=Multiaddr("/ip4/127.0.0.1/udp/4001/quic"),
|
||||||
@ -87,7 +87,7 @@ class TestQUICConnection:
|
|||||||
return QUICConnection(
|
return QUICConnection(
|
||||||
quic_connection=mock_quic_connection,
|
quic_connection=mock_quic_connection,
|
||||||
remote_addr=("127.0.0.1", 4001),
|
remote_addr=("127.0.0.1", 4001),
|
||||||
peer_id=peer_id,
|
remote_peer_id=peer_id,
|
||||||
local_peer_id=peer_id,
|
local_peer_id=peer_id,
|
||||||
is_initiator=False,
|
is_initiator=False,
|
||||||
maddr=Multiaddr("/ip4/127.0.0.1/udp/4001/quic"),
|
maddr=Multiaddr("/ip4/127.0.0.1/udp/4001/quic"),
|
||||||
@ -117,7 +117,7 @@ class TestQUICConnection:
|
|||||||
client_conn = QUICConnection(
|
client_conn = QUICConnection(
|
||||||
quic_connection=Mock(),
|
quic_connection=Mock(),
|
||||||
remote_addr=("127.0.0.1", 4001),
|
remote_addr=("127.0.0.1", 4001),
|
||||||
peer_id=None,
|
remote_peer_id=None,
|
||||||
local_peer_id=Mock(),
|
local_peer_id=Mock(),
|
||||||
is_initiator=True,
|
is_initiator=True,
|
||||||
maddr=Multiaddr("/ip4/127.0.0.1/udp/4001/quic"),
|
maddr=Multiaddr("/ip4/127.0.0.1/udp/4001/quic"),
|
||||||
@ -129,7 +129,7 @@ class TestQUICConnection:
|
|||||||
server_conn = QUICConnection(
|
server_conn = QUICConnection(
|
||||||
quic_connection=Mock(),
|
quic_connection=Mock(),
|
||||||
remote_addr=("127.0.0.1", 4001),
|
remote_addr=("127.0.0.1", 4001),
|
||||||
peer_id=None,
|
remote_peer_id=None,
|
||||||
local_peer_id=Mock(),
|
local_peer_id=Mock(),
|
||||||
is_initiator=False,
|
is_initiator=False,
|
||||||
maddr=Multiaddr("/ip4/127.0.0.1/udp/4001/quic"),
|
maddr=Multiaddr("/ip4/127.0.0.1/udp/4001/quic"),
|
||||||
|
|||||||
Reference in New Issue
Block a user