erlyaws / yaws

Yaws webserver
https://erlyaws.github.io
BSD 3-Clause "New" or "Revised" License
1.29k stars 267 forks source link

How successive websocket open commands are handled? #282

Closed chomwitt closed 8 years ago

chomwitt commented 8 years ago

In a simple erlang/yaws webapp i made if the client asks for a websocket connection while a websocket connection is already open the app will broke and yaws (interactive session) throws:

=ERROR REPORT==== 10-Aug-2016::02:55:32 === Failed to start websocket process: {{case_clause,<0.90.0>}, [{ws_server_side,init,1, [{file,"ws_server_side.erl"},{line,15}]}, {yaws_websockets,init,1, [{file,"yaws_websockets.erl"}, {line,167}]}, {gen_server,init_it,6, [{file,"gen_server.erl"},{line,306}]}, {proc_lib,init_p_do_apply,3, [{file,"proc_lib.erl"},{line,237}]}]}

My websocket callbackmodule's init function will just spawn and register a process.

vinoski commented 8 years ago

Can you show us your ws_server_side:init/1 function? It's getting a case clause exception, so there's some case for something it's not handling, and it crashes the process.

chomwitt commented 8 years ago

init(_Args) -> Pid = spawn(fun() -> loop(0) end), case whereis(counter) of undefined -> register(counter, Pid); Pid -> ok end, {ok,first}.

vinoski commented 8 years ago

Your whereis(counter) call is clearly not returning undefined, as you'd get no error in that case. It's returning a pid which is not equal to Pid, which means that an older process is already registered with the name counter, and so your case fails.

One way to fix this is to only start the new process when it's actually needed:

init(_Args) ->
    case whereis(counter) of
        undefined ->
            register(counter, spawn(fun() -> loop end));
        _Pid -> ok
    end,
    {ok,first}.

But an overall problem is that I'm not sure how you're stopping any existing counter processes. Ideally, they'd be supervised in some fashion to prevent them from leaking, rather than simply being spawned like this.

chomwitt commented 8 years ago

I see your point . in my counter webapp i have a exit button that will stop the counter process and unregister its name . But i wasnt sure in the case of succesive ' new Websocket(url)' calls what will happen. Is it sth that i must take care of or the websocket protocols would 1) either not allow a new websocket to the same server/port/resource from the same cliient while one is open 2)either will close the existing one and try to create a new websocket

chomwitt commented 8 years ago

From RFC 6445 p.15

 If the client already has a WebSocket connection to the remote
   host (IP address) identified by /host/ and port /port/ pair, even
   if the remote host is known by another name, the client MUST wait
   until that connection has been established or for that connection
   to have failed.  There MUST be no more than one connection in a
   CONNECTING state.  If multiple connections to the same IP address
   are attempted simultaneously, the client MUST serialize them so
   that there is no more than one connection at a time running
   through the following steps.
vinoski commented 8 years ago

You might consider using a supervisor to start a gen_server to implement your counter, and then your websocket callbacks can just assume the counter process is already there.

chomwitt commented 8 years ago

Sorry if i insist, but the more i think some basic use cases for an erlang/yaws websocket based webapp the more i get lost.

For example i read in : https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers#Keeping_track_of_clients

Keeping track of clients

This doesn't directly relate to the WebSocket protocol, but it's worth mentioning here: your server will have to keep track of clients' sockets so you don't keep handshaking again with clients who have already completed the handshake. The same client IP address can try to connect multiple times (but the server can deny them if they attempt too many connections in order to save itself from Denial-of-Service attacks).

So for a simple webapp like the basic echo in yaws'manual how can a server differentiate that 'two instances' of the webapp have started when a user just opened the same url in two tabs, meaning that the client endpoint(in websocket parlance) will be the same?

vinoski commented 8 years ago

Not sure I understand the question. An endpoint is determined by a socket representing a connection, and a connection consists of a 5-tuple that included protocol (in this case TCP), client IP address, client port, server IP address, and server port. The 5-tuple for each endpoint of the webapp will be unique even if it's coming from the same client host.