I developed a dash app using dash-websockets.
This was working just fine for about a month, until an issue arose running on AWS. No changes to the code or the server were made.
Now I am seeing a bizarre behavior: It runs fine if both server and client are running on my local machine. However, now when I run it in my AWS EC2 instance, the dash-client is trying to make a websocket connection to my local machine: The dash client does not make a websocket connction with the server in the cloud, but I noticed when I run the server locally the client in the cloud connects to.
I don’t even understand how client knows about my local system??
I made a simple test client/server to demonstrate the websocket issue. I will post it below.
Can someone explain please how this is possible?
How can I debug this issue?
Here is the code:
server:
# main_app.py - Claude Version 5.1
import asyncio
import websockets
import json
import logging
import random
import argparse
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Parse command line arguments
parser = argparse.ArgumentParser(description="WebSocket Server")
parser.add_argument('--port', type=int, default=8765, help='WebSocket server port')
args = parser.parse_args()
WEBSOCKET_HOST = 'localhost'
WEBSOCKET_PORT = args.port
connected = set()
# Simulated data for the table
table_data = [
{"Exchange": "Binance", "Symbol": "BTC/USDT", "Price": 50000, "Volume": 100},
{"Exchange": "Coinbase", "Symbol": "ETH/USDT", "Price": 3000, "Volume": 200},
{"Exchange": "Kraken", "Symbol": "ADA/USDT", "Price": 2, "Volume": 5000},
]
async def update_table_data():
while True:
for row in table_data:
row["Price"] += random.uniform(-100, 100)
row["Volume"] += random.uniform(-1000, 1000)
if connected: # Only send updates if there are connected clients
message = json.dumps({"type": "table_data", "content": table_data})
websockets.broadcast(connected, message)
await asyncio.sleep(1) # Update every second
async def handle_websocket(websocket):
logger.info("New WebSocket connection")
connected.add(websocket)
try:
async for message in websocket:
data = json.loads(message)
if data['type'] == 'message':
response = {"type": "response", "content": f"Received: {data['content']}"}
await websocket.send(json.dumps(response))
except websockets.exceptions.ConnectionClosed:
logger.info("WebSocket connection closed")
finally:
connected.remove(websocket)
async def main():
server = await websockets.serve(
handle_websocket,
WEBSOCKET_HOST,
WEBSOCKET_PORT,
process_request=process_request
)
logger.info(f"WebSocket server started on {WEBSOCKET_HOST}:{WEBSOCKET_PORT}")
update_task = asyncio.create_task(update_table_data())
await server.wait_closed()
update_task.cancel()
try:
await update_task
except asyncio.CancelledError:
pass
async def process_request(path, headers):
if "Origin" in headers:
headers["Access-Control-Allow-Origin"] = headers["Origin"]
return None
if __name__ == "__main__":
try:
asyncio.run(main())
except KeyboardInterrupt:
logger.info("Received exit signal (CTRL+C)")
client:
# dash_app.py
import json
import logging
import argparse
import dash
import pandas as pd
from dash import dash_table, dcc, html
from dash.dependencies import Input, Output, State
from dash_extensions import WebSocket
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
# Parse command line arguments
parser = argparse.ArgumentParser(description="Dash WebSocket Client")
parser.add_argument('--port', type=int, default=8765, help='WebSocket server port')
args = parser.parse_args()
WEBSOCKET_HOST = 'localhost'
WEBSOCKET_PORT = args.port
app = dash.Dash(__name__)
app.layout = html.Div([
html.H1("Dash WebSocket Client with DataTable"),
dcc.Input(id='input-box', type='text', placeholder='Enter message'),
html.Button('Send', id='send-button'),
html.Div(id='output-div'),
dash_table.DataTable(
id='data-table',
columns=[{"name": i, "id": i} for i in ["Exchange", "Symbol", "Price", "Volume"]],
data=[],
style_table={'height': '300px', 'overflowY': 'auto'}
),
WebSocket(url=f"ws://{WEBSOCKET_HOST}:{WEBSOCKET_PORT}", id="ws")
])
@app.callback(
Output("ws", "send"),
Input('send-button', 'n_clicks'),
State('input-box', 'value'),
prevent_initial_call=False
)
def send_message(n_clicks, value):
if n_clicks:
logger.info(f"Sending message: {value}")
return json.dumps({"type": "message", "content": value})
@app.callback(
Output('output-div', 'children'),
Input("ws", "message")
)
def update_output(message):
if message is None:
return "No messages received yet."
data = json.loads(message["data"])
if data["type"] == "response":
return f"Received: {data['content']}"
return dash.no_update
@app.callback(
Output('data-table', 'data'),
Input("ws", "message")
)
def update_table(message):
if message is None:
return []
data = json.loads(message["data"])
if data["type"] == "table_data":
df = pd.DataFrame(data['content'])
return df.to_dict('records')
return dash.no_update
if __name__ == '__main__':
logger.info(f"Connecting to WebSocket server at ws://{WEBSOCKET_HOST}:{WEBSOCKET_PORT}")
app.run_server(debug=True, host='0.0.0.0', port=8050)