0
Fork 0
mirror of https://github.com/ninenines/cowboy.git synced 2025-07-14 04:10:24 +00:00

Document the commands based Websocket interface

The old interface with ok|reply|stop tuples is deprecated.
This commit is contained in:
Loïc Hoguin 2019-10-06 16:51:27 +02:00
parent 2b38526351
commit 3977f2b96f
No known key found for this signature in database
GPG key ID: 8A9DF795F6FED764
13 changed files with 108 additions and 81 deletions

View file

@ -105,7 +105,7 @@ the upgrade:
[source,erlang] [source,erlang]
---- ----
websocket_init(State) -> websocket_init(State) ->
{reply, {text, <<"Hello!">>}, State}. {[{text, <<"Hello!">>}], State}.
---- ----
=== Receiving frames === Receiving frames
@ -122,7 +122,7 @@ ignores all others:
[source,erlang] [source,erlang]
---- ----
websocket_handle(Frame = {text, _}, State) -> websocket_handle(Frame = {text, _}, State) ->
{reply, Frame, State}; {[Frame], State};
websocket_handle(_Frame, State) -> websocket_handle(_Frame, State) ->
{ok, State}. {ok, State}.
---- ----
@ -145,7 +145,7 @@ and ignores all others:
[source,erlang] [source,erlang]
---- ----
websocket_info({log, Text}, State) -> websocket_info({log, Text}, State) ->
{reply, {text, Text}, State}; {[{text, Text}], State};
websocket_info(_Info, State) -> websocket_info(_Info, State) ->
{ok, State}. {ok, State}.
---- ----
@ -167,24 +167,23 @@ websocket_info(_Info, State) ->
{ok, State}. {ok, State}.
---- ----
To send one frame, return a reply tuple with the frame to send: To send one frame, return the frame to be sent:
[source,erlang] [source,erlang]
---- ----
websocket_info(_Info, State) -> websocket_info(_Info, State) ->
{reply, {text, <<"Hello!">>}, State}. {[{text, <<"Hello!">>}], State}.
---- ----
You can send frames of any type: text, binary, ping, pong You can send frames of any type: text, binary, ping, pong
or close frames. or close frames.
To send many frames at once, return a reply tuple with the You can send many frames at the same time:
list of frames to send:
[source,erlang] [source,erlang]
---- ----
websocket_info(_Info, State) -> websocket_info(_Info, State) ->
{reply, [ {[
{text, "Hello"}, {text, "Hello"},
{text, <<"world!">>}, {text, <<"world!">>},
{binary, <<0:8000>>} {binary, <<0:8000>>}
@ -246,18 +245,18 @@ Cowboy will have a more reasonable default.
The Websocket connection process can be set to hibernate The Websocket connection process can be set to hibernate
after the callback returns. after the callback returns.
Simply add an `hibernate` field to the ok or reply tuples: Simply add an `hibernate` field to the returned tuple:
[source,erlang] [source,erlang]
---- ----
websocket_init(State) -> websocket_init(State) ->
{ok, State, hibernate}. {[], State, hibernate}.
websocket_handle(_Frame, State) -> websocket_handle(_Frame, State) ->
{ok, State, hibernate}. {[], State, hibernate}.
websocket_info(_Info, State) -> websocket_info(_Info, State) ->
{reply, {text, <<"Hello!">>}, State, hibernate}. {[{text, <<"Hello!">>}], State, hibernate}.
---- ----
It is highly recommended to write your handlers with It is highly recommended to write your handlers with
@ -289,5 +288,5 @@ The following example sends a close frame with a reason message:
[source,erlang] [source,erlang]
---- ----
websocket_info(_Info, State) -> websocket_info(_Info, State) ->
{reply, {close, 1000, <<"some-reason">>}, State}. {[{close, 1000, <<"some-reason">>}], State}.
---- ----

View file

@ -32,14 +32,18 @@ PartialReq :: map()
State :: any() State :: any()
Opts :: cowboy_websocket:opts() Opts :: cowboy_websocket:opts()
InFrame :: ping | pong | {text | binary | ping | pong, binary()} InFrame :: ping | pong | {text | binary | ping | pong, binary()}
OutFrame :: cow_ws:frame() %% see types below
Info :: any() Info :: any()
CallResult :: {ok, State} CallResult :: {commands(), State}
| {commands(), State, hibernate}
| Deprecated
Deprecated :: {ok, State}
| {ok, State, hibernate} | {ok, State, hibernate}
| {reply, OutFrame | [OutFrame], State} | {reply, OutFrame | [OutFrame], State}
| {reply, OutFrame | [OutFrame], State, hibernate} | {reply, OutFrame | [OutFrame], State, hibernate}
| {stop, State} | {stop, State}
OutFrame :: cow_ws:frame() %% see types below
Reason :: normal | stop | timeout Reason :: normal | stop | timeout
| remote | {remote, cow_ws:close_code(), binary()} | remote | {remote, cow_ws:close_code(), binary()}
@ -69,9 +73,9 @@ frame received. The `websocket_info/2` callback will be
called for every Erlang message received. called for every Erlang message received.
All three Websocket callbacks may send one or more frames All three Websocket callbacks may send one or more frames
back to the client (by returning a `reply` tuple) or terminate back to the client, including close frames to terminate
the connection (by sending a `close` frame or returning a `stop` the connection; enable/disable active mode; enable/disable
tuple). compression for subsequent frames; or change Websocket options.
The optional `terminate/3` callback will ultimately be called The optional `terminate/3` callback will ultimately be called
with the reason for the termination of the connection. This with the reason for the termination of the connection. This
@ -128,6 +132,41 @@ timeout::
== Types == Types
=== commands()
[source,erlang]
----
commands() :: [Command]
Command :: {active, boolean()}
| {deflate, boolean()}
| {set_options, #{idle_timeout => timeout()}}
| Frame :: cow_ws:frame()
----
Commands that may be returned from Websocket callbacks.
The following commands are defined:
active::
Whether to disable or enable reading from the socket. This
can be used to apply flow control to a Websocket connection.
deflate::
Whether the subsequent frames should be compressed. Has no
effect on connections that did not negotiate compression.
set_options::
Set Websocket options. Currently only the option `idle_timeout`
may be updated from a Websocket handler.
Frame::
Send the corresponding Websocket frame.
=== cow_ws:frame() === cow_ws:frame()
[source,erlang] [source,erlang]
@ -224,6 +263,8 @@ normal circumstances if necessary.
== Changelog == Changelog
* *2.7*: The commands based interface has been added. The old
interface is now deprecated.
* *2.7*: The option `validate_utf8` has been added. * *2.7*: The option `validate_utf8` has been added.
* *2.6*: Deflate options can now be configured via `deflate_opts`. * *2.6*: Deflate options can now be configured via `deflate_opts`.
* *2.0*: The Req object is no longer passed to Websocket callbacks. * *2.0*: The Req object is no longer passed to Websocket callbacks.

View file

@ -10,15 +10,15 @@ init(Req, Opts) ->
websocket_init(State) -> websocket_init(State) ->
erlang:start_timer(1000, self(), <<"Hello!">>), erlang:start_timer(1000, self(), <<"Hello!">>),
{ok, State}. {[], State}.
websocket_handle({text, Msg}, State) -> websocket_handle({text, Msg}, State) ->
{reply, {text, << "That's what she said! ", Msg/binary >>}, State}; {[{text, << "That's what she said! ", Msg/binary >>}], State};
websocket_handle(_Data, State) -> websocket_handle(_Data, State) ->
{ok, State}. {[], State}.
websocket_info({timeout, _Ref, Msg}, State) -> websocket_info({timeout, _Ref, Msg}, State) ->
erlang:start_timer(1000, self(), <<"How' you doin'?">>), erlang:start_timer(1000, self(), <<"How' you doin'?">>),
{reply, {text, Msg}, State}; {[{text, Msg}], State};
websocket_info(_Info, State) -> websocket_info(_Info, State) ->
{ok, State}. {[], State}.

View file

@ -61,15 +61,15 @@ init(Req, State) ->
{cowboy_websocket, Req, State}. {cowboy_websocket, Req, State}.
websocket_init(State) -> websocket_init(State) ->
{ok, State}. {[], State}.
websocket_handle({text, Data}, State) -> websocket_handle({text, Data}, State) ->
{reply, {text, Data}, State}; {[{text, Data}], State};
websocket_handle({binary, Data}, State) -> websocket_handle({binary, Data}, State) ->
{reply, {binary, Data}, State}; {[{binary, Data}], State};
websocket_handle(_Frame, State) -> websocket_handle(_Frame, State) ->
{ok, State}. {[], State}.
websocket_info(_Info, State) -> websocket_info(_Info, State) ->
{ok, State}. {[], State}.
endef endef

View file

@ -26,11 +26,11 @@ init(Req=#{qs := Qs}, State) ->
}}. }}.
websocket_handle({text, Data}, State) -> websocket_handle({text, Data}, State) ->
{reply, {text, Data}, State}; {[{text, Data}], State};
websocket_handle({binary, Data}, State) -> websocket_handle({binary, Data}, State) ->
{reply, {binary, Data}, State}; {[{binary, Data}], State};
websocket_handle(_, State) -> websocket_handle(_, State) ->
{ok, State}. {[], State}.
websocket_info(_, State) -> websocket_info(_, State) ->
{ok, State}. {[], State}.

View file

@ -13,11 +13,11 @@ init(Req, State) ->
}}. }}.
websocket_handle({text, Data}, State) -> websocket_handle({text, Data}, State) ->
{reply, {text, Data}, State}; {[{text, Data}], State};
websocket_handle({binary, Data}, State) -> websocket_handle({binary, Data}, State) ->
{reply, {binary, Data}, State}; {[{binary, Data}], State};
websocket_handle(_, State) -> websocket_handle(_, State) ->
{ok, State}. {[], State}.
websocket_info(_, State) -> websocket_info(_, State) ->
{ok, State}. {[], State}.

View file

@ -18,30 +18,28 @@ websocket_init(State) ->
do_websocket_init(State). do_websocket_init(State).
do_websocket_init(State=ok) -> do_websocket_init(State=ok) ->
{ok, State}; {[], State};
do_websocket_init(State=ok_hibernate) -> do_websocket_init(State=ok_hibernate) ->
{ok, State, hibernate}; {[], State, hibernate};
do_websocket_init(State=reply) -> do_websocket_init(State=reply) ->
{reply, {text, "Hello"}, State}; {[{text, "Hello"}], State};
do_websocket_init(State=reply_hibernate) -> do_websocket_init(State=reply_hibernate) ->
{reply, {text, "Hello"}, State, hibernate}; {[{text, "Hello"}], State, hibernate};
do_websocket_init(State=reply_close) -> do_websocket_init(State=reply_close) ->
{reply, close, State}; {[close], State};
do_websocket_init(State=reply_close_hibernate) -> do_websocket_init(State=reply_close_hibernate) ->
{reply, close, State, hibernate}; {[close], State, hibernate};
do_websocket_init(State=reply_many) -> do_websocket_init(State=reply_many) ->
{reply, [{text, "Hello"}, {binary, "World"}], State}; {[{text, "Hello"}, {binary, "World"}], State};
do_websocket_init(State=reply_many_hibernate) -> do_websocket_init(State=reply_many_hibernate) ->
{reply, [{text, "Hello"}, {binary, "World"}], State, hibernate}; {[{text, "Hello"}, {binary, "World"}], State, hibernate};
do_websocket_init(State=reply_many_close) -> do_websocket_init(State=reply_many_close) ->
{reply, [{text, "Hello"}, close], State}; {[{text, "Hello"}, close], State};
do_websocket_init(State=reply_many_close_hibernate) -> do_websocket_init(State=reply_many_close_hibernate) ->
{reply, [{text, "Hello"}, close], State, hibernate}; {[{text, "Hello"}, close], State, hibernate}.
do_websocket_init(State=stop) ->
{stop, State}.
websocket_handle(_, State) -> websocket_handle(_, State) ->
{ok, State}. {[], State}.
websocket_info(_, State) -> websocket_info(_, State) ->
{ok, State}. {[], State}.

View file

@ -403,13 +403,6 @@ ws_init_return_reply_many_close_hibernate(Config) ->
1:1, 0:3, 8:4, 0:8 >>} = gen_tcp:recv(Socket, 9, 6000), 1:1, 0:3, 8:4, 0:8 >>} = gen_tcp:recv(Socket, 9, 6000),
ok. ok.
ws_init_return_stop(Config) ->
doc("Handler closes immediately after the handshake."),
{ok, Socket, _} = do_handshake("/ws_init?stop", Config),
{ok, << 1:1, 0:3, 8:4, 0:1, 2:7, 1000:16 >>} = gen_tcp:recv(Socket, 0, 6000),
{error, closed} = gen_tcp:recv(Socket, 0, 6000),
ok.
ws_init_shutdown_before_handshake(Config) -> ws_init_shutdown_before_handshake(Config) ->
doc("Handler stops before Websocket handshake."), doc("Handler stops before Websocket handshake."),
{ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]), {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]),

View file

@ -12,11 +12,11 @@ init(Req, _) ->
}}. }}.
websocket_handle({text, Data}, State) -> websocket_handle({text, Data}, State) ->
{reply, {text, Data}, State}; {[{text, Data}], State};
websocket_handle({binary, Data}, State) -> websocket_handle({binary, Data}, State) ->
{reply, {binary, Data}, State}; {[{binary, Data}], State};
websocket_handle(_Frame, State) -> websocket_handle(_Frame, State) ->
{ok, State}. {[], State}.
websocket_info(_Info, State) -> websocket_info(_Info, State) ->
{ok, State}. {[], State}.

View file

@ -12,17 +12,17 @@ init(Req, _) ->
websocket_init(State) -> websocket_init(State) ->
erlang:start_timer(1000, self(), <<"websocket_init">>), erlang:start_timer(1000, self(), <<"websocket_init">>),
{ok, State}. {[], State}.
websocket_handle({text, Data}, State) -> websocket_handle({text, Data}, State) ->
{reply, {text, Data}, State}; {[{text, Data}], State};
websocket_handle({binary, Data}, State) -> websocket_handle({binary, Data}, State) ->
{reply, {binary, Data}, State}; {[{binary, Data}], State};
websocket_handle(_Frame, State) -> websocket_handle(_Frame, State) ->
{ok, State}. {[], State}.
websocket_info({timeout, _Ref, Msg}, State) -> websocket_info({timeout, _Ref, Msg}, State) ->
erlang:start_timer(1000, self(), <<"websocket_handle">>), erlang:start_timer(1000, self(), <<"websocket_handle">>),
{reply, {text, Msg}, State}; {[{text, Msg}], State};
websocket_info(_Info, State) -> websocket_info(_Info, State) ->
{ok, State}. {[], State}.

View file

@ -1,22 +1,18 @@
-module(ws_max_frame_size). -module(ws_max_frame_size).
-export([init/2]). -export([init/2]).
-export([websocket_init/1]).
-export([websocket_handle/2]). -export([websocket_handle/2]).
-export([websocket_info/2]). -export([websocket_info/2]).
init(Req, State) -> init(Req, State) ->
{cowboy_websocket, Req, State, #{max_frame_size => 8}}. {cowboy_websocket, Req, State, #{max_frame_size => 8}}.
websocket_init(State) ->
{ok, State}.
websocket_handle({text, Data}, State) -> websocket_handle({text, Data}, State) ->
{reply, {text, Data}, State}; {[{text, Data}], State};
websocket_handle({binary, Data}, State) -> websocket_handle({binary, Data}, State) ->
{reply, {binary, Data}, State}; {[{binary, Data}], State};
websocket_handle(_Frame, State) -> websocket_handle(_Frame, State) ->
{ok, State}. {[], State}.
websocket_info(_Info, State) -> websocket_info(_Info, State) ->
{ok, State}. {[], State}.

View file

@ -12,10 +12,10 @@ init(Req, Opts) ->
websocket_init(State) -> websocket_init(State) ->
erlang:send_after(10, self(), send_many), erlang:send_after(10, self(), send_many),
{ok, State}. {[], State}.
websocket_handle(_Frame, State) -> websocket_handle(_Frame, State) ->
{ok, State}. {[], State}.
websocket_info(send_many, State = [{sequence, Sequence}]) -> websocket_info(send_many, State = [{sequence, Sequence}]) ->
{reply, Sequence, State}. {Sequence, State}.

View file

@ -13,10 +13,10 @@ init(Req, _) ->
}}. }}.
websocket_handle({text, Data}, State) -> websocket_handle({text, Data}, State) ->
{reply, {text, Data}, State}; {[{text, Data}], State};
websocket_handle({binary, Data}, State) -> websocket_handle({binary, Data}, State) ->
{reply, {binary, Data}, State}. {[{binary, Data}], State}.
websocket_info(_Info, State) -> websocket_info(_Info, State) ->
erlang:start_timer(500, self(), should_not_cancel_timer), erlang:start_timer(500, self(), should_not_cancel_timer),
{ok, State}. {[], State}.