I have a webapp that talks to a Python server using websockets. I want the webapp to report if the server is available or not and auto connect if it suddenly becomes available at the given IP/port endpoint.
I have this logic that tries to open the socket, on failure it closes the socket, wait 500ms and try again. The failure is detected by the on_error
and on_close
callbacks given by the browser. Since most of the time, the server will be local to the webapp, it make no sense to have a long timeout and it looks like I can’T control the underlying socket timeout, therefore I force a connectimeout using a setTimeout
method that calls socket.close
if the socket isn’t conencted. It works OK, but when I use a TCP proxy, things become messy.
What I observe is that when I close the server, the loss of connection is detected and reported in my UI. But if I restart my server, the UI may not detect it or it may take a while before the connection works. During that time, I see in my logs that many error handlers are called and my UI tries again and again and at some point it works. Can take 30sec+
I tried 2 types of proxies.
- vscode port forwarding. I found out that there’s a bug in it that makes this approach unreliable. Confirmed by Microsoft : websocket and VSCode port forwarding
- SSH port formarding (-L option). This works generally better than vscode, but if I wait long enough, I start hitting the connect timeout and the longer I wait, the longer it takes for the UI to recover.
Making a step back, I wonder if I use the right approach to detect my server or if I should simply discourage the use of a proxy. My goal is simply to reliably detect the presence of my server and triggers a callback on connection/disconnection.
I personally use a proxy because I develop my server in a Linux VM and wants to use my windows browser.
Attached is the code that I use with some application specific logic purposely removed to keep things clear. Any suggestion to make this more resilient would be appreciated
create_socket(): void {
const that = this
this.close_socket() // Only closes if not already closed
this.reconnect_timeout_handle = null
this.socket = new WebSocket("ws://" + this.hostname + ":" + this.port)
this.socket.onmessage = function (e) {
that.on_socket_message_callback(e.data)
}
this.socket.onclose = function (e) {
that.on_socket_close_callback(e)
}
this.socket.onopen = function (e) {
that.on_socket_open_callback(e)
}
this.socket.onerror = function (e) {
that.on_socket_error_callback(e)
}
// My homemade timeout
this.connect_timeout_handle = setTimeout(
function () {
if (that.socket !== null) {
if (that.socket.readyState != that.socket.OPEN) {
that.close_socket()
}
}
} as Function,
this.connect_timeout // 1500ms
)
}
on_socket_open_callback(e: Event): void {
this.clear_connect_timeout()
// App specific code removed on purpose
}
clear_connect_timeout(): void {
if (this.connect_timeout_handle != null) {
clearTimeout(this.connect_timeout_handle)
this.connect_timeout_handle = null
}
}
on_socket_error_callback(e: Event): void {
this.clear_connect_timeout()
this.close_socket() // Closes only if not already closed
if (this.enable_reconnect) {
this.try_reconnect(this.reconnect_interval) // reconnect_interval = 500
}
}
try_reconnect(delay_ms: number): void {
// Register a callback for a connect attempt in x msec
const that = this
if (this.reconnect_timeout_handle == null) {
this.reconnect_timeout_handle = setTimeout(
function () {
that.create_socket()
} as Function,
delay_ms
)
}
}
on_socket_close_callback(e: CloseEvent): void {
this.clear_connect_timeout()
if (this.socket !== null) {
this.socket = null
}
if (this.enable_reconnect) {
this.try_reconnect(this.reconnect_interval)
}
}
Any idea how this could be more “reliable” and give a better user experience?