Browse Source

Document the commands based Websocket interface

The old interface with ok|reply|stop tuples is deprecated.
Loïc Hoguin 5 years ago
parent
commit
3977f2b96f

+ 12 - 13
doc/src/guide/ws_handlers.asciidoc

@@ -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
-list of frames to send:
+You can send many frames at the same time:
 
 
 [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}.
 ----
 ----

+ 46 - 5
doc/src/manual/cowboy_websocket.asciidoc

@@ -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
-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
 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.

+ 5 - 5
examples/websocket/src/ws_h.erl

@@ -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}.

+ 5 - 5
plugins.mk

@@ -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

+ 4 - 4
test/handlers/ws_deflate_opts_h.erl

@@ -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}.

+ 4 - 4
test/handlers/ws_dont_validate_utf8_h.erl

@@ -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}.

+ 12 - 14
test/handlers/ws_init_h.erl

@@ -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};
-do_websocket_init(State=stop) ->
-	{stop, State}.
+	{[{text, "Hello"}, close], State, hibernate}.
 
 
 websocket_handle(_, State) ->
 websocket_handle(_, State) ->
-	{ok, State}.
+	{[], State}.
 
 
 websocket_info(_, State) ->
 websocket_info(_, State) ->
-	{ok, State}.
+	{[], State}.

+ 0 - 7
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),
 		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}]),

+ 4 - 4
test/ws_SUITE_data/ws_echo.erl

@@ -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}.

+ 6 - 6
test/ws_SUITE_data/ws_echo_timer.erl

@@ -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}.

+ 4 - 8
test/ws_SUITE_data/ws_max_frame_size.erl

@@ -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}.

+ 3 - 3
test/ws_SUITE_data/ws_send_many.erl

@@ -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}.

+ 3 - 3
test/ws_SUITE_data/ws_timeout_cancel.erl

@@ -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}.