from hypertor import AsyncClient, TimeoutError, HypertorError
import asyncio
import json
import time
async def polling_realtime_example():
print("🧅 Real-time Updates via Long Polling")
print("-" * 40)
print()
print("Long polling pattern over Tor:")
print()
print(" 1. Client sends request to server")
print(" 2. Server holds connection until data is available (or timeout)")
print(" 3. Server responds with new data")
print(" 4. Client immediately sends new request")
print()
async with AsyncClient(timeout=30) as client:
print("📡 Demonstrating polling with httpbin.org via Tor...")
print()
for i in range(3):
start = time.time()
response = await client.get(
f"https://httpbin.org/delay/1" )
elapsed = time.time() - start
if response.status_code == 200:
print(f" 📥 Poll {i+1}: Got response after {elapsed:.1f}s")
else:
print(f" ⚠️ Poll {i+1}: Status {response.status_code}")
if i < 2:
await asyncio.sleep(0.5)
print()
print(" ✅ Polling demonstration complete")
async def event_stream_pattern():
print("\n🧅 Server-Sent Events (SSE) Pattern")
print("-" * 40)
print()
print("SSE is ideal for server→client streaming over Tor:")
print()
print("""
Example SSE Server (using OnionApp):
from hypertor import OnionApp, Request
app = OnionApp()
@app.route("/events")
async def event_stream(request: Request):
async def generate():
while True:
data = await get_next_event()
yield f"data: {json.dumps(data)}\\n\\n"
return StreamingResponse(
generate(),
media_type="text/event-stream"
)
Example SSE Client:
async with AsyncClient(timeout=300) as client:
# Long timeout for streaming
response = await client.get("http://xxx.onion/events")
# Process events as they arrive
async for line in response.iter_lines():
if line.startswith("data: "):
event = json.loads(line[6:])
process_event(event)
""")
async def bidirectional_pattern():
print("\n🧅 Bidirectional Communication Pattern")
print("-" * 40)
print()
print("Achieve WebSocket-like bidirectional communication:")
print()
print("""
Pattern: Separate send/receive channels
async def chat_client(onion_address: str):
async with AsyncClient(timeout=60) as client:
# Task 1: Send messages
async def sender():
while True:
message = await get_user_input()
await client.post(
f"http://{onion_address}/send",
json=json.dumps({"text": message})
)
# Task 2: Receive messages (long polling)
async def receiver():
last_id = 0
while True:
response = await client.get(
f"http://{onion_address}/poll?after={last_id}"
)
messages = response.json()
for msg in messages:
print(f"Received: {msg['text']}")
last_id = max(last_id, msg['id'])
# Run both concurrently
await asyncio.gather(sender(), receiver())
""")
async def websocket_with_socks_example():
print("\n🧅 True WebSockets over Tor (External Libraries)")
print("-" * 40)
print()
print("For true WebSocket support, use these libraries with Tor's SOCKS proxy:")
print()
print("""
Option 1: websockets + python-socks
import asyncio
from python_socks.async_.asyncio.v2 import Proxy
from websockets import connect
async def ws_over_tor():
proxy = Proxy.from_url("socks5://127.0.0.1:9050")
async with connect(
"wss://echo.websocket.events",
proxy=proxy
) as ws:
await ws.send("Hello via Tor!")
response = await ws.recv()
print(f"Received: {response}")
Option 2: aiohttp with SOCKS
import aiohttp
from aiohttp_socks import ProxyConnector
async def ws_over_tor():
connector = ProxyConnector.from_url("socks5://127.0.0.1:9050")
async with aiohttp.ClientSession(connector=connector) as session:
async with session.ws_connect("wss://echo.websocket.events") as ws:
await ws.send_str("Hello via Tor!")
msg = await ws.receive()
print(f"Received: {msg.data}")
Note: These require Tor daemon running with SOCKS proxy on port 9050.
hypertor uses arti (Rust Tor implementation) which doesn't expose a SOCKS proxy.
""")
async def demonstrate_http_chat():
print("\n🧅 Live Demo: HTTP Request/Response over Tor")
print("-" * 40)
async with AsyncClient(timeout=60) as client:
print("\n📡 Sending message to httpbin.org via Tor...")
message = {
"user": "anonymous",
"text": "Hello from Tor!",
"timestamp": int(time.time())
}
response = await client.post(
"https://httpbin.org/post",
json=json.dumps(message)
)
if response.status_code == 200:
data = response.json()
print(f" ✅ Message sent successfully")
print(f" 📍 Via Tor exit: {data.get('origin', 'unknown')}")
print(f" 📦 Server received: {data.get('json', {})}")
else:
print(f" ❌ Error: {response.status_code}")
async def main():
print("🧅 hypertor - Real-time Communication Patterns")
print("=" * 50)
print()
print("While hypertor focuses on HTTP, you can achieve real-time")
print("communication patterns. This example shows how.")
print()
try:
await polling_realtime_example()
await event_stream_pattern()
await bidirectional_pattern()
await websocket_with_socks_example()
await demonstrate_http_chat()
print("\n" + "=" * 50)
print("✅ Real-time patterns demonstration completed!")
print()
print("Key takeaways:")
print(" • Long polling works well for real-time over Tor")
print(" • SSE provides server→client streaming")
print(" • Bidirectional achieved with send/receive channels")
print(" • True WebSockets need external libs + SOCKS proxy")
except KeyboardInterrupt:
print("\n\n🛑 Interrupted by user")
except TimeoutError:
print("\n❌ Request timed out (Tor network may be slow)")
except HypertorError as e:
print(f"\n❌ Hypertor error: {e}")
except Exception as e:
print(f"\n❌ Error: {e}")
if __name__ == "__main__":
asyncio.run(main())