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:
parent
2b38526351
commit
3977f2b96f
13 changed files with 108 additions and 81 deletions
|
@ -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}.
|
||||||
----
|
----
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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}.
|
||||||
|
|
10
plugins.mk
10
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
|
||||||
|
|
|
@ -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}.
|
||||||
|
|
|
@ -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}.
|
||||||
|
|
|
@ -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}.
|
||||||
|
|
|
@ -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}]),
|
||||||
|
|
|
@ -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}.
|
||||||
|
|
|
@ -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}.
|
||||||
|
|
|
@ -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}.
|
||||||
|
|
|
@ -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}.
|
||||||
|
|
|
@ -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}.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue