Files
ethereum-rpc-docker/logging-proxy/proxy.py
2025-04-27 22:56:10 +07:00

112 lines
4.3 KiB
Python

from flask import Flask, request, jsonify
import requests
import sys
import os
import json
from flask_sockets import Sockets
import websocket
import gevent
app = Flask(__name__)
sockets = Sockets(app)
# Target URLs
TARGET_URL_HTTP = os.getenv('TARGET_URL', 'http://host.docker.internal:8545')
# Derive WebSocket URL from HTTP URL
TARGET_URL_WS = TARGET_URL_HTTP.replace('http://', 'ws://').replace('https://', 'wss://')
@app.route('/', methods=['POST'])
def proxy():
incoming = request.get_json()
request_log = f"==> Request:\n{json.dumps(incoming, indent=2)}"
response = requests.post(TARGET_URL_HTTP, json=incoming)
outgoing = response.json()
log_lines = [request_log]
if 'error' in outgoing:
response_log = f"<== Response (Error):\n{json.dumps(outgoing, indent=2)}"
log_lines.append(response_log)
print('\n---\n'.join(log_lines), file=sys.stdout, flush=True)
return jsonify(outgoing)
# New WebSocket handler
@sockets.route('/')
def proxy_socket(ws):
"""Handles incoming WebSocket connections and relays messages."""
print("==> WebSocket connection received", file=sys.stdout, flush=True)
target_ws = None
try:
# Connect to the target WebSocket server
target_ws = websocket.create_connection(TARGET_URL_WS)
print(f"==> WebSocket connection established to {TARGET_URL_WS}", file=sys.stdout, flush=True)
# Use gevent greenlets to relay messages concurrently
from gevent import spawn
def relay_to_target():
"""Relay messages from the client to the target."""
try:
while not ws.closed and target_ws.connected:
message = ws.receive()
if message is not None:
target_ws.send(message)
else: # Client closed
break
except websocket.WebSocketConnectionClosedException:
print("<== WebSocket client connection closed.", file=sys.stdout, flush=True)
except Exception as e:
print(f"Error receiving from client or sending to target: {e}", file=sys.stderr, flush=True)
finally:
if target_ws and target_ws.connected:
target_ws.close()
def relay_to_client():
"""Relay messages from the target to the client."""
try:
while target_ws.connected and not ws.closed:
message = target_ws.recv()
if message:
ws.send(message)
else: # Target closed
break
except websocket.WebSocketConnectionClosedException:
print("<== WebSocket target connection closed.", file=sys.stdout, flush=True)
except Exception as e:
print(f"Error receiving from target or sending to client: {e}", file=sys.stderr, flush=True)
finally:
if not ws.closed:
ws.close()
# Start the relay greenlets
g_to_target = spawn(relay_to_target)
g_to_client = spawn(relay_to_client)
# Wait for both relays to complete
gevent.joinall([g_to_target, g_to_client], raise_error=False) # Don't raise errors here, already printed
except websocket.WebSocketException as e:
print(f"<== WebSocket connection to target {TARGET_URL_WS} failed: {e}", file=sys.stderr, flush=True)
except Exception as e:
print(f"An unexpected error occurred in WebSocket proxy: {e}", file=sys.stderr, flush=True)
finally:
# Ensure connections are closed
if target_ws and target_ws.connected:
target_ws.close()
if not ws.closed:
ws.close()
print("<== WebSocket handler finished", file=sys.stdout, flush=True)
if __name__ == '__main__':
# Use gevent-websocket server instead of Flask's default development server
from gevent import pywsgi
from geventwebsocket.handler import WebSocketHandler
print(f"Starting server on 0.0.0.0:8545 supporting HTTP and WebSocket...")
print(f"HTTP proxying to: {TARGET_URL_HTTP}")
print(f"WebSocket proxying to: {TARGET_URL_WS}")
server = pywsgi.WSGIServer(('0.0.0.0', 8545), app, handler_class=WebSocketHandler)
server.serve_forever()