aboutsummaryrefslogtreecommitdiffstats
path: root/doc/src/guide/ws_handlers.asciidoc
diff options
context:
space:
mode:
Diffstat (limited to 'doc/src/guide/ws_handlers.asciidoc')
-rw-r--r--doc/src/guide/ws_handlers.asciidoc196
1 files changed, 196 insertions, 0 deletions
diff --git a/doc/src/guide/ws_handlers.asciidoc b/doc/src/guide/ws_handlers.asciidoc
new file mode 100644
index 0000000..9ddddf4
--- /dev/null
+++ b/doc/src/guide/ws_handlers.asciidoc
@@ -0,0 +1,196 @@
+[[ws_handlers]]
+== Handling Websocket connections
+
+A special handler is required for handling Websocket connections.
+Websocket handlers allow you to initialize the connection,
+handle incoming frames from the socket, handle incoming Erlang
+messages and then clean up on termination.
+
+Websocket handlers essentially act as a bridge between the client
+and the Erlang system. They will typically do little more than
+socket communication and decoding/encoding of frames.
+
+=== Initialization
+
+First, the `init/2` callback is called. This callback is common
+to all handlers. To establish a Websocket connection, this function
+must return a `ws` tuple.
+
+[source,erlang]
+----
+init(Req, _Opts) ->
+ {cowboy_websocket, Req, #state{}}.
+----
+
+Upon receiving this tuple, Cowboy will switch to the code
+that handles Websocket connections and perform the handshake
+immediately.
+
+If the sec-websocket-protocol header was sent with the request
+for establishing a Websocket connection, then the Websocket
+handler *must* select one of these subprotocol and send it
+back to the client, otherwise the client might decide to close
+the connection, assuming no correct subprotocol was found.
+
+[source,erlang]
+----
+init(Req, _Opts) ->
+ case cowboy_req:parse_header(<<"sec-websocket-protocol">>, Req) of
+ undefined ->
+ {ok, Req, #state{}};
+ Subprotocols ->
+ case lists:keymember(<<"mychat2">>, 1, Subprotocols) of
+ true ->
+ Req2 = cowboy_req:set_resp_header(<<"sec-websocket-protocol">>,
+ <<"mychat2">>, Req),
+ {ok, Req2, #state{}};
+ false ->
+ {stop, Req, undefined}
+ end
+ end.
+----
+
+It is not recommended to wait too long inside the `init/2`
+function. Any extra initialization may be done after returning by
+sending yourself a message before doing anything. Any message sent
+to `self()` from `init/2` is guaranteed to arrive before
+any frames from the client.
+
+It is also very easy to ensure that this message arrives before
+any message from other processes by sending it before registering
+or enabling timers.
+
+[source,erlang]
+----
+init(Req, _Opts) ->
+ self() ! post_init,
+ %% Register process here...
+ {cowboy_websocket, Req, #state{}}.
+
+websocket_info(post_init, Req, State) ->
+ %% Perform post_init initialization here...
+ {ok, Req, State}.
+----
+
+=== Handling frames from the client
+
+Cowboy will call `websocket_handle/3` whenever a text, binary,
+ping or pong frame arrives from the client. Note that in the
+case of ping and pong frames, no action is expected as Cowboy
+automatically replies to ping frames.
+
+The handler can decide to send frames to the socket, stop
+or just continue without sending anything.
+
+The following snippet echoes back any text frame received and
+ignores all others.
+
+[source,erlang]
+----
+websocket_handle(Frame = {text, _}, Req, State) ->
+ {reply, Frame, Req, State};
+websocket_handle(_Frame, Req, State) ->
+ {ok, Req, State}.
+----
+
+=== Handling Erlang messages
+
+Cowboy will call `websocket_info/3` whenever an Erlang message
+arrives.
+
+The handler can decide to send frames to the socket, stop
+or just continue without sending anything.
+
+The following snippet forwards any `log` message to the socket
+and ignores all others.
+
+[source,erlang]
+----
+websocket_info({log, Text}, Req, State) ->
+ {reply, {text, Text}, Req, State};
+websocket_info(_Info, Req, State) ->
+ {ok, Req, State}.
+----
+
+=== Sending frames to the socket
+
+Cowboy allows sending either a single frame or a list of
+frames to the socket, in which case the frames are sent
+sequentially. Any frame can be sent: text, binary, ping,
+pong or close frames.
+
+The following example sends three frames using a single `reply`
+tuple.
+
+[source,erlang]
+----
+websocket_info(hello_world, Req, State) ->
+ {reply, [
+ {text, "Hello"},
+ {text, <<"world!">>},
+ {binary, <<0:8000>>}
+ ], Req, State};
+%% More websocket_info/3 clauses here...
+----
+
+Note that the payload for text and binary frames is of type
+`iodata()`, meaning it can be either a `binary()` or an
+`iolist()`.
+
+Sending a `close` frame will immediately initiate the closing
+of the Websocket connection. Be aware that any additional
+frames sent by the client or any Erlang messages waiting to
+be received will not be processed. Also note that when replying
+a list of frames that includes close, any frame found after the
+close frame will not be sent.
+
+=== Ping and timeout
+
+The biggest performance improvement you can do when dealing
+with a huge number of Websocket connections is to reduce the
+number of timers that are started on the server. A common use
+of timers when dealing with connections is for sending a ping
+every once in a while. This should be done exclusively on the
+client side. Indeed, a server handling one million Websocket
+connections will perform a lot better when it doesn't have to
+handle one million extra timers too!
+
+Cowboy will automatically respond to ping frames sent by the
+client. It will still forward the frame to the handler for
+informative purpose, but no further action is required.
+
+Cowboy can be configured to automatically close the Websocket
+connection when no data arrives on the socket. It is highly
+recommended to configure a timeout for it, as otherwise you
+may end up with zombie "half-connected" sockets that may
+leave the process alive forever.
+
+A good timeout value is 60 seconds.
+
+[source,erlang]
+----
+init(Req, _Opts) ->
+ {cowboy_websocket, Req, #state{}, 60000}.
+----
+
+This value cannot be changed once it is set. It defaults to
+`infinity`.
+
+=== Hibernate
+
+Most tuples returned from handler callbacks can include an
+extra value `hibernate`. After doing any necessary operations
+following the return of the callback, Cowboy will hibernate
+the process.
+
+It is highly recommended to hibernate processes that do not
+handle much traffic. It is a good idea to hibernate all
+connections by default and investigate only when you start
+noticing increased CPU usage.
+
+=== Supporting older browsers
+
+Unfortunately Websocket is a relatively recent technology,
+which means that not all browsers support it. A library like
+https://github.com/ninenines/bullet[Bullet] can be used to
+emulate Websocket connections on older browsers.