Restore debug tools and test client from original WebSocket implementation

- Added back debug_websocket_url.py for WebSocket URL testing
- Added back test_websocket_client.py for standalone WebSocket testing
- These tools complement the integrated WebSocket transport implementation
This commit is contained in:
acul71
2025-09-17 01:00:41 -04:00
parent cdbd80eeba
commit f4a4298c0f
2 changed files with 308 additions and 0 deletions

65
debug_websocket_url.py Normal file
View File

@ -0,0 +1,65 @@
#!/usr/bin/env python3
"""
Debug script to test WebSocket URL construction and basic connection.
"""
import logging
from multiaddr import Multiaddr
from libp2p.transport.websocket.multiaddr_utils import parse_websocket_multiaddr
# Configure logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
async def test_websocket_url():
"""Test WebSocket URL construction."""
# Test multiaddr from your JS node
maddr_str = "/ip4/127.0.0.1/tcp/35391/ws/p2p/12D3KooWQh7p5xP2ppr3CrhUFsawmsKNe9jgDbacQdWCYpuGfMVN"
maddr = Multiaddr(maddr_str)
logger.info(f"Testing multiaddr: {maddr}")
# Parse WebSocket multiaddr
parsed = parse_websocket_multiaddr(maddr)
logger.info(
f"Parsed: is_wss={parsed.is_wss}, sni={parsed.sni}, rest_multiaddr={parsed.rest_multiaddr}"
)
# Construct WebSocket URL
if parsed.is_wss:
protocol = "wss"
else:
protocol = "ws"
# Extract host and port from rest_multiaddr
host = parsed.rest_multiaddr.value_for_protocol("ip4")
port = parsed.rest_multiaddr.value_for_protocol("tcp")
websocket_url = f"{protocol}://{host}:{port}/"
logger.info(f"WebSocket URL: {websocket_url}")
# Test basic WebSocket connection
try:
from trio_websocket import open_websocket_url
logger.info("Testing basic WebSocket connection...")
async with open_websocket_url(websocket_url) as ws:
logger.info("✅ WebSocket connection successful!")
# Send a simple message
await ws.send_message(b"test")
logger.info("✅ Message sent successfully!")
except Exception as e:
logger.error(f"❌ WebSocket connection failed: {e}")
import traceback
logger.error(f"Traceback: {traceback.format_exc()}")
if __name__ == "__main__":
import trio
trio.run(test_websocket_url)

243
test_websocket_client.py Normal file
View File

@ -0,0 +1,243 @@
#!/usr/bin/env python3
"""
Standalone WebSocket client for testing py-libp2p WebSocket transport.
This script allows you to test the Python WebSocket client independently.
"""
import argparse
import logging
import sys
from multiaddr import Multiaddr
import trio
from libp2p import create_yamux_muxer_option, new_host
from libp2p.crypto.secp256k1 import create_new_key_pair
from libp2p.crypto.x25519 import create_new_key_pair as create_new_x25519_key_pair
from libp2p.custom_types import TProtocol
from libp2p.network.exceptions import SwarmException
from libp2p.peer.id import ID
from libp2p.peer.peerinfo import info_from_p2p_addr
from libp2p.security.noise.transport import (
PROTOCOL_ID as NOISE_PROTOCOL_ID,
Transport as NoiseTransport,
)
from libp2p.transport.websocket.multiaddr_utils import (
is_valid_websocket_multiaddr,
parse_websocket_multiaddr,
)
# Configure logging
logging.basicConfig(
level=logging.DEBUG, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)
# Enable debug logging for WebSocket transport
logging.getLogger("libp2p.transport.websocket").setLevel(logging.DEBUG)
logging.getLogger("libp2p.network.swarm").setLevel(logging.DEBUG)
PING_PROTOCOL_ID = TProtocol("/ipfs/ping/1.0.0")
async def test_websocket_connection(destination: str, timeout: int = 30) -> bool:
"""
Test WebSocket connection to a destination multiaddr.
Args:
destination: Multiaddr string (e.g., /ip4/127.0.0.1/tcp/8080/ws/p2p/...)
timeout: Connection timeout in seconds
Returns:
True if connection successful, False otherwise
"""
try:
# Parse the destination multiaddr
maddr = Multiaddr(destination)
logger.info(f"Testing connection to: {maddr}")
# Validate WebSocket multiaddr
if not is_valid_websocket_multiaddr(maddr):
logger.error(f"Invalid WebSocket multiaddr: {maddr}")
return False
# Parse WebSocket multiaddr
try:
parsed = parse_websocket_multiaddr(maddr)
logger.info(
f"Parsed WebSocket multiaddr: is_wss={parsed.is_wss}, sni={parsed.sni}, rest_multiaddr={parsed.rest_multiaddr}"
)
except Exception as e:
logger.error(f"Failed to parse WebSocket multiaddr: {e}")
return False
# Extract peer ID from multiaddr
try:
peer_id = ID.from_base58(maddr.value_for_protocol("p2p"))
logger.info(f"Target peer ID: {peer_id}")
except Exception as e:
logger.error(f"Failed to extract peer ID from multiaddr: {e}")
return False
# Create Python host using professional pattern
logger.info("Creating Python host...")
key_pair = create_new_key_pair()
py_peer_id = ID.from_pubkey(key_pair.public_key)
logger.info(f"Python Peer ID: {py_peer_id}")
# Generate X25519 keypair for Noise
noise_key_pair = create_new_x25519_key_pair()
# Create security options (following professional pattern)
security_options = {
NOISE_PROTOCOL_ID: NoiseTransport(
libp2p_keypair=key_pair,
noise_privkey=noise_key_pair.private_key,
early_data=None,
with_noise_pipes=False,
)
}
# Create muxer options
muxer_options = create_yamux_muxer_option()
# Create host with proper configuration
host = new_host(
key_pair=key_pair,
sec_opt=security_options,
muxer_opt=muxer_options,
listen_addrs=[
Multiaddr("/ip4/0.0.0.0/tcp/0/ws")
], # WebSocket listen address
)
logger.info(f"Python host created: {host}")
# Create peer info using professional helper
peer_info = info_from_p2p_addr(maddr)
logger.info(f"Connecting to: {peer_info}")
# Start the host
logger.info("Starting host...")
async with host.run(listen_addrs=[]):
# Wait a moment for host to be ready
await trio.sleep(1)
# Attempt connection with timeout
logger.info("Attempting to connect...")
try:
with trio.fail_after(timeout):
await host.connect(peer_info)
logger.info("✅ Successfully connected to peer!")
# Test ping protocol (following professional pattern)
logger.info("Testing ping protocol...")
try:
stream = await host.new_stream(
peer_info.peer_id, [PING_PROTOCOL_ID]
)
logger.info("✅ Successfully created ping stream!")
# Send ping (32 bytes as per libp2p ping protocol)
ping_data = b"\x01" * 32
await stream.write(ping_data)
logger.info(f"✅ Sent ping: {len(ping_data)} bytes")
# Wait for pong (should be same 32 bytes)
pong_data = await stream.read(32)
logger.info(f"✅ Received pong: {len(pong_data)} bytes")
if pong_data == ping_data:
logger.info("✅ Ping-pong test successful!")
return True
else:
logger.error(
f"❌ Unexpected pong data: expected {len(ping_data)} bytes, got {len(pong_data)} bytes"
)
return False
except Exception as e:
logger.error(f"❌ Ping protocol test failed: {e}")
return False
except trio.TooSlowError:
logger.error(f"❌ Connection timeout after {timeout} seconds")
return False
except SwarmException as e:
logger.error(f"❌ Connection failed with SwarmException: {e}")
# Log the underlying error details
if hasattr(e, "__cause__") and e.__cause__:
logger.error(f"Underlying error: {e.__cause__}")
return False
except Exception as e:
logger.error(f"❌ Connection failed with unexpected error: {e}")
import traceback
logger.error(f"Full traceback: {traceback.format_exc()}")
return False
except Exception as e:
logger.error(f"❌ Test failed with error: {e}")
return False
async def main():
"""Main function to run the WebSocket client test."""
parser = argparse.ArgumentParser(
description="Test py-libp2p WebSocket client connection",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
# Test connection to a WebSocket peer
python test_websocket_client.py /ip4/127.0.0.1/tcp/8080/ws/p2p/12D3KooW...
# Test with custom timeout
python test_websocket_client.py /ip4/127.0.0.1/tcp/8080/ws/p2p/12D3KooW... --timeout 60
# Test WSS connection
python test_websocket_client.py /ip4/127.0.0.1/tcp/8080/wss/p2p/12D3KooW...
""",
)
parser.add_argument(
"destination",
help="Destination multiaddr (e.g., /ip4/127.0.0.1/tcp/8080/ws/p2p/12D3KooW...)",
)
parser.add_argument(
"--timeout",
type=int,
default=30,
help="Connection timeout in seconds (default: 30)",
)
parser.add_argument(
"--verbose", "-v", action="store_true", help="Enable verbose logging"
)
args = parser.parse_args()
# Set logging level
if args.verbose:
logging.getLogger().setLevel(logging.DEBUG)
else:
logging.getLogger().setLevel(logging.INFO)
logger.info("🚀 Starting WebSocket client test...")
logger.info(f"Destination: {args.destination}")
logger.info(f"Timeout: {args.timeout}s")
# Run the test
success = await test_websocket_connection(args.destination, args.timeout)
if success:
logger.info("🎉 WebSocket client test completed successfully!")
sys.exit(0)
else:
logger.error("💥 WebSocket client test failed!")
sys.exit(1)
if __name__ == "__main__":
# Run with trio
trio.run(main)