1::: Handling Websocket connections 2 3A special handler is required for handling Websocket connections. 4Websocket handlers allow you to initialize the connection, 5handle incoming frames from the socket, handle incoming Erlang 6messages and then clean up on termination. 7 8Websocket handlers essentially act as a bridge between the client 9and the Erlang system. They will typically do little more than 10socket communication and decoding/encoding of frames. 11 12:: Initialization 13 14First, the `init/3` callback is called. This callback is common 15to all handlers. To establish a Websocket connection, this function 16must return an `upgrade` tuple. 17 18``` erlang 19init(_, Req, Opts) -> 20 {upgrade, protocol, cowboy_websocket}. 21``` 22 23It is also possible to return an update Req object and options 24using the longer form of this tuple. 25 26``` erlang 27init(_Type, Req, Opts) -> 28 {upgrade, protocol, cowboy_websocket, Req, Opts}. 29``` 30 31Upon receiving this tuple, Cowboy will switch to the code 32that handles Websocket connections. It does not immediately 33perform the handshake however. First, it calls the `websocket_init/3` 34callback. 35 36This function must be used to initialize the state, and can 37also be used to register the process, start a timer, etc. 38As long as the function returns an `ok` tuple, then Cowboy 39performs the Websocket handshake. 40 41``` erlang 42websocket_init(_Type, Req, _Opts) -> 43 {ok, Req, #state{}}. 44``` 45 46A `shutdown` tuple can be returned to refuse to perform the 47handshake. When doing so, Cowboy will send a `400 Bad Request` 48response to the client and close the connection. 49 50``` erlang 51websocket_init(_Type, Req, _Opts) -> 52 {shutdown, Req}. 53``` 54 55It is also possible to perform a `cowboy_req:reply/{2,3,4}` 56before returning a `shutdown` tuple, allowing you to override 57the response sent back to the client. 58 59Note that browser support for handling Websocket connection 60failures may vary. 61 62If the sec-websocket-protocol header was sent with the request 63for establishing a Websocket connection, then the Websocket 64handler *must* select one of these subprotocol and send it 65back to the client, otherwise the client might decide to close 66the connection, assuming no correct subprotocol was found. 67 68``` erlang 69websocket_init(_Type, Req, _Opts) -> 70 case cowboy_req:parse_header(<<"sec-websocket-protocol">>, Req) of 71 {ok, undefined, Req2} -> 72 {ok, Req, #state{}}; 73 {ok, Subprotocols, Req2} -> 74 case lists:keymember(<<"mychat2">>, 1, Subprotocols) of 75 true -> 76 Req3 = cowboy_req:set_resp_header(<<"sec-websocket-protocol">>, 77 <<"mychat2">>, Req2), 78 {ok, Req3, #state{}}; 79 false -> 80 {shutdown, Req2} 81 end 82 end. 83``` 84 85It is not recommended to wait too long inside the `websocket_init/3` 86function. Any extra initialization may be done after returning by 87sending yourself a message before doing anything. Any message sent 88to `self()` from `websocket_init/3` is guaranteed to arrive before 89any frames from the client. 90 91It is also very easy to ensure that this message arrives before 92any message from other processes by sending it before registering 93or enabling timers. 94 95``` erlang 96websocket_init(_Type, Req, _Opts) -> 97 self() ! post_init, 98 %% Register process here... 99 {ok, Req, #state{}}. 100 101websocket_info(post_init, Req, State) -> 102 %% Perform post_init initialization here... 103 {ok, Req, State}. 104``` 105 106:: Handling frames from the client 107 108Cowboy will call `websocket_handle/3` whenever a text, binary, 109ping or pong frame arrives from the client. Note that in the 110case of ping and pong frames, no action is expected as Cowboy 111automatically replies to ping frames. 112 113The handler can decide to send frames to the socket, shutdown 114or just continue without sending anything. 115 116The following snippet echoes back any text frame received and 117ignores all others. 118 119``` erlang 120websocket_handle(Frame = {text, _}, Req, State) -> 121 {reply, Frame, Req, State}; 122websocket_handle(_Frame, Req, State) -> 123 {ok, Req, State}. 124``` 125 126:: Handling Erlang messages 127 128Cowboy will call `websocket_info/3` whenever an Erlang message 129arrives. 130 131The handler can decide to send frames to the socket, shutdown 132or just continue without sending anything. 133 134The following snippet forwards any `log` message to the socket 135and ignores all others. 136 137``` erlang 138websocket_info({log, Text}, Req, State) -> 139 {reply, {text, Text}, Req, State}; 140websocket_info(_Info, Req, State) -> 141 {ok, Req, State}. 142``` 143 144:: Sending frames to the socket 145 146Cowboy allows sending either a single frame or a list of 147frames to the socket. Any frame can be sent: text, binary, ping, 148pong or close frames. 149 150The following example sends three frames using a single `reply` 151tuple. 152 153``` erlang 154websocket_info(hello_world, Req, State) -> 155 {reply, [ 156 {text, "Hello"}, 157 {text, <<"world!">>}, 158 {binary, <<0:8000>>} 159 ], Req, State}; 160%% More websocket_info/3 clauses here... 161``` 162 163Note that the payload for text and binary frames is of type 164`iodata()`, meaning it can be either a `binary()` or an 165`iolist()`. 166 167Sending a `close` frame will immediately initiate the closing 168of the Websocket connection. Be aware that any additional 169frames sent by the client or any Erlang messages waiting to 170be received will not be processed. Also note that when replying 171a list of frames that includes close, any frame found after the 172close frame will not be sent. 173 174:: Ping and timeout 175 176The biggest performance improvement you can do when dealing 177with a huge number of Websocket connections is to reduce the 178number of timers that are started on the server. A common use 179of timers when dealing with connections is for sending a ping 180every once in a while. This should be done exclusively on the 181client side. Indeed, a server handling one million Websocket 182connections will perform a lot better when it doesn't have to 183handle one million extra timers too! 184 185Cowboy will automatically respond to ping frames sent by the 186client. It will still forward the frame to the handler for 187informative purpose, but no further action is required. 188 189Cowboy can be configured to automatically close the Websocket 190connection when no data arrives on the socket. It is highly 191recommended to configure a timeout for it, as otherwise you 192may end up with zombie "half-connected" sockets that may 193leave the process alive forever. 194 195A good timeout value is 60 seconds. 196 197``` erlang 198websocket_init(_Type, Req, _Opts) -> 199 {ok, Req, #state{}, 60000}. 200``` 201 202This value cannot be changed once it is set. It defaults to 203`infinity`. 204 205:: Hibernate 206 207Most tuples returned from handler callbacks can include an 208extra value `hibernate`. After doing any necessary operations 209following the return of the callback, Cowboy will hibernate 210the process. 211 212It is highly recommended to hibernate processes that do not 213handle much traffic. It is a good idea to hibernate all 214connections by default and investigate only when you start 215noticing increased CPU usage. 216 217:: Supporting older browsers 218 219Unfortunately Websocket is a relatively recent technology, 220which means that not all browsers support it. A library like 221^"Bullet^https://github.com/extend/bullet^ can be used to 222emulate Websocket connections on older browsers. 223