From 3977f2b96fb8cc2164bfe28ee094b3e661a2fad9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Sun, 6 Oct 2019 16:51:27 +0200 Subject: Document the commands based Websocket interface The old interface with ok|reply|stop tuples is deprecated. --- doc/src/guide/ws_handlers.asciidoc | 25 ++++++++------- doc/src/manual/cowboy_websocket.asciidoc | 51 ++++++++++++++++++++++++++++--- examples/websocket/src/ws_h.erl | 10 +++--- plugins.mk | 10 +++--- test/handlers/ws_deflate_opts_h.erl | 8 ++--- test/handlers/ws_dont_validate_utf8_h.erl | 8 ++--- test/handlers/ws_init_h.erl | 26 ++++++++-------- test/ws_SUITE.erl | 7 ----- test/ws_SUITE_data/ws_echo.erl | 8 ++--- test/ws_SUITE_data/ws_echo_timer.erl | 12 ++++---- test/ws_SUITE_data/ws_max_frame_size.erl | 12 +++----- test/ws_SUITE_data/ws_send_many.erl | 6 ++-- test/ws_SUITE_data/ws_timeout_cancel.erl | 6 ++-- 13 files changed, 108 insertions(+), 81 deletions(-) diff --git a/doc/src/guide/ws_handlers.asciidoc b/doc/src/guide/ws_handlers.asciidoc index 71165af..5cfdcb1 100644 --- a/doc/src/guide/ws_handlers.asciidoc +++ b/doc/src/guide/ws_handlers.asciidoc @@ -105,7 +105,7 @@ the upgrade: [source,erlang] ---- websocket_init(State) -> - {reply, {text, <<"Hello!">>}, State}. + {[{text, <<"Hello!">>}], State}. ---- === Receiving frames @@ -122,7 +122,7 @@ ignores all others: [source,erlang] ---- websocket_handle(Frame = {text, _}, State) -> - {reply, Frame, State}; + {[Frame], State}; websocket_handle(_Frame, State) -> {ok, State}. ---- @@ -145,7 +145,7 @@ and ignores all others: [source,erlang] ---- websocket_info({log, Text}, State) -> - {reply, {text, Text}, State}; + {[{text, Text}], State}; websocket_info(_Info, State) -> {ok, State}. ---- @@ -167,24 +167,23 @@ websocket_info(_Info, 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] ---- websocket_info(_Info, State) -> - {reply, {text, <<"Hello!">>}, State}. + {[{text, <<"Hello!">>}], State}. ---- You can send frames of any type: text, binary, ping, pong or close frames. -To send many frames at once, return a reply tuple with the -list of frames to send: +You can send many frames at the same time: [source,erlang] ---- websocket_info(_Info, State) -> - {reply, [ + {[ {text, "Hello"}, {text, <<"world!">>}, {binary, <<0:8000>>} @@ -246,18 +245,18 @@ Cowboy will have a more reasonable default. The Websocket connection process can be set to hibernate 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] ---- websocket_init(State) -> - {ok, State, hibernate}. + {[], State, hibernate}. websocket_handle(_Frame, State) -> - {ok, State, hibernate}. + {[], State, hibernate}. websocket_info(_Info, State) -> - {reply, {text, <<"Hello!">>}, State, hibernate}. + {[{text, <<"Hello!">>}], State, hibernate}. ---- 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] ---- websocket_info(_Info, State) -> - {reply, {close, 1000, <<"some-reason">>}, State}. + {[{close, 1000, <<"some-reason">>}], State}. ---- diff --git a/doc/src/manual/cowboy_websocket.asciidoc b/doc/src/manual/cowboy_websocket.asciidoc index 2f01b8a..3a2264b 100644 --- a/doc/src/manual/cowboy_websocket.asciidoc +++ b/doc/src/manual/cowboy_websocket.asciidoc @@ -32,14 +32,18 @@ PartialReq :: map() State :: any() Opts :: cowboy_websocket:opts() InFrame :: ping | pong | {text | binary | ping | pong, binary()} -OutFrame :: cow_ws:frame() %% see types below Info :: any() -CallResult :: {ok, State} +CallResult :: {commands(), State} + | {commands(), State, hibernate} + | Deprecated + +Deprecated :: {ok, State} | {ok, State, hibernate} | {reply, OutFrame | [OutFrame], State} | {reply, OutFrame | [OutFrame], State, hibernate} | {stop, State} +OutFrame :: cow_ws:frame() %% see types below Reason :: normal | stop | timeout | 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. All three Websocket callbacks may send one or more frames -back to the client (by returning a `reply` tuple) or terminate -the connection (by sending a `close` frame or returning a `stop` -tuple). +back to the client, including close frames to terminate +the connection; enable/disable active mode; enable/disable +compression for subsequent frames; or change Websocket options. The optional `terminate/3` callback will ultimately be called with the reason for the termination of the connection. This @@ -128,6 +132,41 @@ timeout:: == 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() [source,erlang] @@ -224,6 +263,8 @@ normal circumstances if necessary. == 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.6*: Deflate options can now be configured via `deflate_opts`. * *2.0*: The Req object is no longer passed to Websocket callbacks. diff --git a/examples/websocket/src/ws_h.erl b/examples/websocket/src/ws_h.erl index 63055df..ba2b844 100644 --- a/examples/websocket/src/ws_h.erl +++ b/examples/websocket/src/ws_h.erl @@ -10,15 +10,15 @@ init(Req, Opts) -> websocket_init(State) -> erlang:start_timer(1000, self(), <<"Hello!">>), - {ok, State}. + {[], 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) -> - {ok, State}. + {[], State}. websocket_info({timeout, _Ref, Msg}, State) -> erlang:start_timer(1000, self(), <<"How' you doin'?">>), - {reply, {text, Msg}, State}; + {[{text, Msg}], State}; websocket_info(_Info, State) -> - {ok, State}. + {[], State}. diff --git a/plugins.mk b/plugins.mk index 587b0a6..3fb2f7e 100644 --- a/plugins.mk +++ b/plugins.mk @@ -61,15 +61,15 @@ init(Req, State) -> {cowboy_websocket, Req, State}. websocket_init(State) -> - {ok, State}. + {[], State}. websocket_handle({text, Data}, State) -> - {reply, {text, Data}, State}; + {[{text, Data}], State}; websocket_handle({binary, Data}, State) -> - {reply, {binary, Data}, State}; + {[{binary, Data}], State}; websocket_handle(_Frame, State) -> - {ok, State}. + {[], State}. websocket_info(_Info, State) -> - {ok, State}. + {[], State}. endef diff --git a/test/handlers/ws_deflate_opts_h.erl b/test/handlers/ws_deflate_opts_h.erl index 1c15efe..b70110b 100644 --- a/test/handlers/ws_deflate_opts_h.erl +++ b/test/handlers/ws_deflate_opts_h.erl @@ -26,11 +26,11 @@ init(Req=#{qs := Qs}, State) -> }}. websocket_handle({text, Data}, State) -> - {reply, {text, Data}, State}; + {[{text, Data}], State}; websocket_handle({binary, Data}, State) -> - {reply, {binary, Data}, State}; + {[{binary, Data}], State}; websocket_handle(_, State) -> - {ok, State}. + {[], State}. websocket_info(_, State) -> - {ok, State}. + {[], State}. diff --git a/test/handlers/ws_dont_validate_utf8_h.erl b/test/handlers/ws_dont_validate_utf8_h.erl index 6599c78..27be6ad 100644 --- a/test/handlers/ws_dont_validate_utf8_h.erl +++ b/test/handlers/ws_dont_validate_utf8_h.erl @@ -13,11 +13,11 @@ init(Req, State) -> }}. websocket_handle({text, Data}, State) -> - {reply, {text, Data}, State}; + {[{text, Data}], State}; websocket_handle({binary, Data}, State) -> - {reply, {binary, Data}, State}; + {[{binary, Data}], State}; websocket_handle(_, State) -> - {ok, State}. + {[], State}. websocket_info(_, State) -> - {ok, State}. + {[], State}. diff --git a/test/handlers/ws_init_h.erl b/test/handlers/ws_init_h.erl index 08971ae..db5307b 100644 --- a/test/handlers/ws_init_h.erl +++ b/test/handlers/ws_init_h.erl @@ -18,30 +18,28 @@ websocket_init(State) -> do_websocket_init(State). do_websocket_init(State=ok) -> - {ok, State}; + {[], State}; do_websocket_init(State=ok_hibernate) -> - {ok, State, hibernate}; + {[], State, hibernate}; do_websocket_init(State=reply) -> - {reply, {text, "Hello"}, State}; + {[{text, "Hello"}], State}; do_websocket_init(State=reply_hibernate) -> - {reply, {text, "Hello"}, State, hibernate}; + {[{text, "Hello"}], State, hibernate}; do_websocket_init(State=reply_close) -> - {reply, close, State}; + {[close], State}; do_websocket_init(State=reply_close_hibernate) -> - {reply, close, State, hibernate}; + {[close], State, hibernate}; do_websocket_init(State=reply_many) -> - {reply, [{text, "Hello"}, {binary, "World"}], State}; + {[{text, "Hello"}, {binary, "World"}], State}; 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) -> - {reply, [{text, "Hello"}, close], State}; + {[{text, "Hello"}, close], State}; do_websocket_init(State=reply_many_close_hibernate) -> - {reply, [{text, "Hello"}, close], State, hibernate}; -do_websocket_init(State=stop) -> - {stop, State}. + {[{text, "Hello"}, close], State, hibernate}. websocket_handle(_, State) -> - {ok, State}. + {[], State}. websocket_info(_, State) -> - {ok, State}. + {[], State}. diff --git a/test/ws_SUITE.erl b/test/ws_SUITE.erl index 64b8561..37f4012 100644 --- a/test/ws_SUITE.erl +++ b/test/ws_SUITE.erl @@ -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), 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) -> doc("Handler stops before Websocket handshake."), {ok, Socket} = gen_tcp:connect("localhost", config(port, Config), [binary, {active, false}]), diff --git a/test/ws_SUITE_data/ws_echo.erl b/test/ws_SUITE_data/ws_echo.erl index a94b4c0..efdc204 100644 --- a/test/ws_SUITE_data/ws_echo.erl +++ b/test/ws_SUITE_data/ws_echo.erl @@ -12,11 +12,11 @@ init(Req, _) -> }}. websocket_handle({text, Data}, State) -> - {reply, {text, Data}, State}; + {[{text, Data}], State}; websocket_handle({binary, Data}, State) -> - {reply, {binary, Data}, State}; + {[{binary, Data}], State}; websocket_handle(_Frame, State) -> - {ok, State}. + {[], State}. websocket_info(_Info, State) -> - {ok, State}. + {[], State}. diff --git a/test/ws_SUITE_data/ws_echo_timer.erl b/test/ws_SUITE_data/ws_echo_timer.erl index 7f37229..8157af3 100644 --- a/test/ws_SUITE_data/ws_echo_timer.erl +++ b/test/ws_SUITE_data/ws_echo_timer.erl @@ -12,17 +12,17 @@ init(Req, _) -> websocket_init(State) -> erlang:start_timer(1000, self(), <<"websocket_init">>), - {ok, State}. + {[], State}. websocket_handle({text, Data}, State) -> - {reply, {text, Data}, State}; + {[{text, Data}], State}; websocket_handle({binary, Data}, State) -> - {reply, {binary, Data}, State}; + {[{binary, Data}], State}; websocket_handle(_Frame, State) -> - {ok, State}. + {[], State}. websocket_info({timeout, _Ref, Msg}, State) -> erlang:start_timer(1000, self(), <<"websocket_handle">>), - {reply, {text, Msg}, State}; + {[{text, Msg}], State}; websocket_info(_Info, State) -> - {ok, State}. + {[], State}. diff --git a/test/ws_SUITE_data/ws_max_frame_size.erl b/test/ws_SUITE_data/ws_max_frame_size.erl index 2d34218..3d81497 100644 --- a/test/ws_SUITE_data/ws_max_frame_size.erl +++ b/test/ws_SUITE_data/ws_max_frame_size.erl @@ -1,22 +1,18 @@ -module(ws_max_frame_size). -export([init/2]). --export([websocket_init/1]). -export([websocket_handle/2]). -export([websocket_info/2]). init(Req, State) -> {cowboy_websocket, Req, State, #{max_frame_size => 8}}. -websocket_init(State) -> - {ok, State}. - websocket_handle({text, Data}, State) -> - {reply, {text, Data}, State}; + {[{text, Data}], State}; websocket_handle({binary, Data}, State) -> - {reply, {binary, Data}, State}; + {[{binary, Data}], State}; websocket_handle(_Frame, State) -> - {ok, State}. + {[], State}. websocket_info(_Info, State) -> - {ok, State}. + {[], State}. diff --git a/test/ws_SUITE_data/ws_send_many.erl b/test/ws_SUITE_data/ws_send_many.erl index d621a3d..2f6437f 100644 --- a/test/ws_SUITE_data/ws_send_many.erl +++ b/test/ws_SUITE_data/ws_send_many.erl @@ -12,10 +12,10 @@ init(Req, Opts) -> websocket_init(State) -> erlang:send_after(10, self(), send_many), - {ok, State}. + {[], State}. websocket_handle(_Frame, State) -> - {ok, State}. + {[], State}. websocket_info(send_many, State = [{sequence, Sequence}]) -> - {reply, Sequence, State}. + {Sequence, State}. diff --git a/test/ws_SUITE_data/ws_timeout_cancel.erl b/test/ws_SUITE_data/ws_timeout_cancel.erl index 587f2c5..481d5e6 100644 --- a/test/ws_SUITE_data/ws_timeout_cancel.erl +++ b/test/ws_SUITE_data/ws_timeout_cancel.erl @@ -13,10 +13,10 @@ init(Req, _) -> }}. websocket_handle({text, Data}, State) -> - {reply, {text, Data}, State}; + {[{text, Data}], State}; websocket_handle({binary, Data}, State) -> - {reply, {binary, Data}, State}. + {[{binary, Data}], State}. websocket_info(_Info, State) -> erlang:start_timer(500, self(), should_not_cancel_timer), - {ok, State}. + {[], State}. -- cgit v1.2.3