Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
70e86b9
feat: Enhance WebSocket transport with advanced features
yashksaini-coder Sep 27, 2025
951b823
feat: Add comprehensive WebSocket transport demos
yashksaini-coder Oct 2, 2025
7619411
refactor: Improve WebSocket connection management and error handling
yashksaini-coder Oct 2, 2025
db2cf2a
feat: Introduce a production-ready chat application using WebSocket t…
yashksaini-coder Oct 2, 2025
5d79560
fix: Update JS interop test to check for Node.js availability before …
yashksaini-coder Oct 2, 2025
fa4033d
fix: Add optional dependency handling for WebSocket and SOCKS support
yashksaini-coder Oct 2, 2025
b17a692
refactor: Standardize code formatting and improve readability across …
yashksaini-coder Oct 2, 2025
06861df
refactor: Clean up logging statements in demo files for improved clarity
yashksaini-coder Oct 2, 2025
a12fec5
refactor: Enhance type handling and improve code clarity in WebSocket…
yashksaini-coder Oct 2, 2025
021095e
refactor: Add type guards for optional dependencies in WebSocket list…
yashksaini-coder Oct 2, 2025
113d1be
refactor: Update WebSocket connection handling and improve timeout co…
yashksaini-coder Oct 2, 2025
bcb52af
feat: Add debug script for testing basic WebSocket functionality
yashksaini-coder Oct 2, 2025
9e97805
Merge branch 'main' into Feat/WebSocket-Transport
yashksaini-coder Oct 4, 2025
2516a12
Merge branch 'libp2p:main' into Feat/WebSocket-Transport
yashksaini-coder Oct 5, 2025
9981af9
Merge branch 'main' into Feat/WebSocket-Transport
yashksaini-coder Oct 6, 2025
4079f0f
fix: clean up temporary file handling in TLS transport and tests
yashksaini-coder Oct 9, 2025
c948186
fix: correct typos in bugfix documentation
yashksaini-coder Oct 9, 2025
5c5df0b
fix: update type hint for websocket server task to use TaskStatus[Any]
yashksaini-coder Oct 9, 2025
852bc13
feat: enhance WebSocket listener with TLS validation and improve test…
yashksaini-coder Oct 9, 2025
54b70c9
fix: improve error message formatting for WSS TLS configuration and u…
yashksaini-coder Oct 9, 2025
ee910d7
fix: use getattr for accessing server_info port and exceptions in tes…
yashksaini-coder Oct 9, 2025
8af52bb
Merge branch 'main' into Feat/WebSocket-Transport
yashksaini-coder Oct 10, 2025
a90eb66
Merge branch 'Feat/WebSocket-Transport' of https://github.com/yashksa…
yashksaini-coder Oct 11, 2025
3a893c4
Merge branch 'main' into Feat/WebSocket-Transport
yashksaini-coder Oct 12, 2025
ef5f376
Merge branch 'libp2p:main' into Feat/WebSocket-Transport
yashksaini-coder Oct 14, 2025
7227006
Interop Test Websocket between py-libp2p and js-libp2p
asmit27rai Oct 16, 2025
77137d6
All tests working
asmit27rai Oct 18, 2025
95eb004
Merge branch 'main' into Feat/WebSocket-Transport
yashksaini-coder Oct 20, 2025
0deb5e2
Merge branch 'main' into Feat/WebSocket-Transport
seetadev Oct 20, 2025
7e9ac8f
Merge branch 'main' into Feat/WebSocket-Transport
seetadev Oct 20, 2025
045cbdd
SOCKS Proxy Implemented
asmit27rai Oct 22, 2025
21fc993
Merge branch 'Feat/WebSocket-Transport' of https://github.com/yashksa…
asmit27rai Oct 22, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
463 changes: 463 additions & 0 deletions examples/browser_wss_demo.py

Large diffs are not rendered by default.

321 changes: 321 additions & 0 deletions examples/chat_websocket/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,321 @@
#!/usr/bin/env python3
"""
Production Chat Application using WebSocket Transport

This is a complete end-to-end chat application demonstrating:
- Real-time messaging between multiple peers
- WebSocket transport for browser compatibility
- Peer discovery and connection management
- Production-ready architecture
"""

import argparse
import logging
import time

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.custom_types import TProtocol
from libp2p.peer.id import ID
from libp2p.peer.peerinfo import info_from_p2p_addr
from libp2p.security.insecure.transport import (
PLAINTEXT_PROTOCOL_ID,
InsecureTransport,
)

# Enable debug logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("libp2p.chat")

# Chat protocol
CHAT_PROTOCOL_ID = TProtocol("/chat/1.0.0")


class ChatMessage:
"""Represents a chat message."""

def __init__(self, sender: str, content: str, timestamp: float | None = None):
self.sender = sender
self.content = content
self.timestamp = timestamp or time.time()

def to_bytes(self) -> bytes:
"""Serialize message to bytes."""
import json

data = {
"sender": self.sender,
"content": self.content,
"timestamp": self.timestamp,
}
return json.dumps(data).encode()

@classmethod
def from_bytes(cls, data: bytes) -> "ChatMessage":
"""Deserialize message from bytes."""
import json

obj = json.loads(data.decode())
return cls(
sender=obj["sender"], content=obj["content"], timestamp=obj["timestamp"]
)

def __str__(self) -> str:
timestamp_str = time.strftime("%H:%M:%S", time.localtime(self.timestamp))
return f"[{timestamp_str}] {self.sender}: {self.content}"


class ChatServer:
"""Chat server that handles multiple clients."""

def __init__(self, host, port: int):
self.host = host
self.port = port
self.connected_peers: set[str] = set()
self.message_history: list[ChatMessage] = []

async def handle_chat_stream(self, stream):
"""Handle incoming chat stream."""
try:
# Read the peer ID from the stream
peer_id = str(stream.muxed_conn.peer_id)
self.connected_peers.add(peer_id)

logger.info(f"👤 New peer connected: {peer_id}")
logger.info(f"📊 Total connected peers: {len(self.connected_peers)}")

# Send welcome message
welcome_msg = ChatMessage(
"Server", f"Welcome to the chat! You are peer {peer_id}"
)
await stream.write(welcome_msg.to_bytes())

# Send recent message history
for msg in self.message_history[-10:]: # Last 10 messages
await stream.write(msg.to_bytes())

# Handle incoming messages
while True:
try:
data = await stream.read(1024)
if not data:
break

# Parse incoming message
try:
incoming_msg = ChatMessage.from_bytes(data)
logger.info(
f"📥 Received from {peer_id}: {incoming_msg.content}"
)

# Store message in history
self.message_history.append(incoming_msg)

# Broadcast to all connected peers
await self.broadcast_message(incoming_msg, exclude_peer=peer_id)

except Exception as e:
logger.error(f"Failed to parse message: {e}")

except Exception as e:
logger.error(f"Error reading from stream: {e}")
break

except Exception as e:
logger.error(f"Error handling chat stream: {e}")
finally:
# Remove peer from connected list
if hasattr(stream, "muxed_conn") and hasattr(stream.muxed_conn, "peer_id"):
peer_id = str(stream.muxed_conn.peer_id)
self.connected_peers.discard(peer_id)
logger.info(f"👤 Peer disconnected: {peer_id}")
logger.info(f"📊 Total connected peers: {len(self.connected_peers)}")

await stream.close()

async def broadcast_message(
self,
message: ChatMessage,
exclude_peer: str | None = None,
):
"""Broadcast message to all connected peers."""
# In a real implementation, you'd maintain a list of active streams
# For this demo, we'll just log the broadcast
logger.info(f"📢 Broadcasting: {message}")
logger.info(f" (Would send to {len(self.connected_peers)} peers)")


class ChatClient:
"""Chat client that connects to a server."""

def __init__(self, host, server_address: str):
self.host = host
self.server_address = server_address
self.server_peer_id: ID | None = None

async def connect_to_server(self):
"""Connect to the chat server."""
try:
maddr = Multiaddr(self.server_address)
info = info_from_p2p_addr(maddr)
self.server_peer_id = info.peer_id

logger.info(f"🔗 Connecting to chat server: {self.server_address}")
await self.host.connect(info)
logger.info("✅ Connected to chat server!")
return True

except Exception as e:
logger.error(f"❌ Failed to connect to server: {e}")
return False

async def send_message(self, content: str):
"""Send a message to the server."""
if not self.server_peer_id:
logger.error("❌ Not connected to server")
return False

try:
# Create stream to server
stream = await self.host.new_stream(self.server_peer_id, [CHAT_PROTOCOL_ID])

# Create and send message
message = ChatMessage(str(self.host.get_id()), content)
await stream.write(message.to_bytes())
await stream.close()

logger.info(f"📤 Sent: {content}")
return True

except Exception as e:
logger.error(f"❌ Failed to send message: {e}")
return False


def create_chat_host():
"""Create a host for chat application."""
# Create key pair
key_pair = create_new_key_pair()

# Create host with WebSocket transport
host = new_host(
key_pair=key_pair,
sec_opt={PLAINTEXT_PROTOCOL_ID: InsecureTransport(key_pair)},
muxer_opt=create_yamux_muxer_option(),
listen_addrs=[Multiaddr("/ip4/0.0.0.0/tcp/0/ws")],
)

return host


async def run_server(port: int):
"""Run chat server."""
logger.info("🚀 Starting Chat Server...")

# Create host
host = create_chat_host()

# Create chat server
chat_server = ChatServer(host, port)

# Set up chat handler
host.set_stream_handler(CHAT_PROTOCOL_ID, chat_server.handle_chat_stream)

# Start listening
listen_addr = Multiaddr(f"/ip4/0.0.0.0/tcp/{port}/ws")

async with host.run(listen_addrs=[listen_addr]):
# Get the actual address
addrs = host.get_addrs()
if not addrs:
logger.error("❌ No addresses found for the host")
return

server_addr = str(addrs[0])
client_addr = server_addr.replace("/ip4/0.0.0.0/", "/ip4/127.0.0.1/")

logger.info("🌐 Chat Server Started Successfully!")
logger.info("=" * 50)
logger.info(f"📍 Server Address: {client_addr}")
logger.info("🔧 Protocol: /chat/1.0.0")
logger.info("🚀 Transport: WebSocket (/ws)")
logger.info(f"👤 Server Peer ID: {host.get_id()}")
logger.info("")
logger.info("📋 To connect clients, run:")
logger.info(f" python main.py -c {client_addr}")
logger.info("")
logger.info("⏳ Waiting for chat connections...")
logger.info("─" * 50)

# Wait indefinitely
await trio.sleep_forever()


async def run_client(server_address: str):
"""Run chat client."""
logger.info("🚀 Starting Chat Client...")

# Create host
host = create_chat_host()

# Create chat client
chat_client = ChatClient(host, server_address)

# Start the host
async with host.run(listen_addrs=[]):
# Connect to server
if not await chat_client.connect_to_server():
return

logger.info("💬 Chat Client Ready!")
logger.info("=" * 40)
logger.info("Type messages and press Enter to send")
logger.info("Type 'quit' to exit")
logger.info("─" * 40)

# Interactive chat loop
try:
while True:
# Get user input
message = input("You: ").strip()

if message.lower() == "quit":
logger.info("👋 Goodbye!")
break

if message:
await chat_client.send_message(message)

except KeyboardInterrupt:
logger.info("👋 Goodbye!")
except EOFError:
logger.info("👋 Goodbye!")


def main():
"""Main entry point."""
parser = argparse.ArgumentParser(
description="Production Chat Application using WebSocket Transport"
)
parser.add_argument(
"-p", "--port", default=8080, type=int, help="Server port (default: 8080)"
)
parser.add_argument(
"-c", "--connect", type=str, help="Connect to chat server (client mode)"
)

args = parser.parse_args()

if args.connect:
# Client mode
trio.run(run_client, args.connect)
else:
# Server mode
trio.run(run_server, args.port)


if __name__ == "__main__":
main()
Loading