Lingering socket connection

Hi,

I’m using input from the user to connect to a socket and everything works fine as long as the user inputs the correct stuff the first time around.

let socket = new Socket(
  "ws://localhost:4000/socket",
  { params: { user: username,  pwd: password }  }
)

socket.connect()

If however the user fails to do so the connection fails, as intended, but then the browser continues to try this connection as long as the tab/session/whatever is running.

I guess it could be modified with reconnectAfterMs but I would still like to have that automatic reconnection when my (proper) connection is established.

Is there a way to just init the socket once and if it fails then just let it be?
Or kill off the old socket and start over?

Below is some example logging where the connection lingers even after a connection is established (app.js:33).

Firefox can’t establish a connection to the server at ws://localhost:4000/socket/websocket?user=usery&pwd=pwdz&vsn=2.0.0. phoenix.js:821
initating socket datasocket.js:11
connecting app.js:33
Firefox can’t establish a connection to the server at ws://localhost:4000/socket/websocket?user=usery&pwd=pwdz&vsn=2.0.0. phoenix.js:821
Firefox can’t establish a connection to the server at ws://localhost:4000/socket/websocket?user=usery&pwd=pwdz&vsn=2.0.0. phoenix.js:821
Firefox can’t establish a connection to the server at ws://localhost:4000/socket/websocket?user=usery&pwd=pwdz&vsn=2.0.0. phoenix.js:821

You should be able to achieve the behavior you want by tracking the authenticated state yourself, with something like:

class SocketSession {
  constructor(username, pass){
    this.hasConnected = false
    this.username = username
    this.pass = pass
    this.socket = new Socket("/socket", {params: () => this.params()})

    this.socket.onOpen(() => this.hasConnected = true)

    this.socket.onError(() => if(!this.hasConnected){ this.disconnect() })
  }

  login(username, pass){
    this.username = username
    this.pass = pass
    this.socket.connect()
  }

  params(){ return {username, pass}}

  disconnect() {
    this.socket.disconnect()
    this.displayUILoginError()
  }
}

Note: this approach will be unable to distinguish between an initial bad connection or a bad login, which may or may not be fine for you. It may suffice to present the user with “Login failed, please retry”. Due to the WebSocket spec not allowing a response, we cannot send back the reason the connect failed, so if you need that, you can allow all users to connect, then do your authentication inside a channel. Alternatively, and perhaps preferably, you can have the user authenticate via a regular HTTP endpoint, which then returns a signed user token containing the user id, and you verify that on UserSocket.connect.

3 Likes

This was my plan-B but I did not like it. I want to keep the channels focused on their task.

Agreed. I prefer not to misuse something if I can avoid. This will be it.