mirror of
https://github.com/ninenines/cowboy.git
synced 2025-07-16 05:00:24 +00:00
Add a commands-based interface to Websocket handlers
This feature is currently experimental. It will become the preferred way to use Websocket handlers once it becomes documented. A commands-based interface enables adding commands without having to change the interface much. It mirrors the interface of stream handlers or gen_statem. It will enable adding commands that have been needed for some time but were not implemented for fear of making the interface too complex.
This commit is contained in:
parent
4b385749f2
commit
8404b1c908
5 changed files with 342 additions and 4 deletions
|
@ -31,7 +31,12 @@
|
|||
-export([system_terminate/4]).
|
||||
-export([system_code_change/4]).
|
||||
|
||||
-type call_result(State) :: {ok, State}
|
||||
-type commands() :: [cow_ws:frame()].
|
||||
-export_type([commands/0]).
|
||||
|
||||
-type call_result(State) :: {commands(), State} | {commands(), State, hibernate}.
|
||||
|
||||
-type deprecated_call_result(State) :: {ok, State}
|
||||
| {ok, State, hibernate}
|
||||
| {reply, cow_ws:frame() | [cow_ws:frame()], State}
|
||||
| {reply, cow_ws:frame() | [cow_ws:frame()], State, hibernate}
|
||||
|
@ -48,13 +53,13 @@
|
|||
when Req::cowboy_req:req().
|
||||
|
||||
-callback websocket_init(State)
|
||||
-> call_result(State) when State::any().
|
||||
-> call_result(State) | deprecated_call_result(State) when State::any().
|
||||
-optional_callbacks([websocket_init/1]).
|
||||
|
||||
-callback websocket_handle(ping | pong | {text | binary | ping | pong, binary()}, State)
|
||||
-> call_result(State) when State::any().
|
||||
-> call_result(State) | deprecated_call_result(State) when State::any().
|
||||
-callback websocket_info(any(), State)
|
||||
-> call_result(State) when State::any().
|
||||
-> call_result(State) | deprecated_call_result(State) when State::any().
|
||||
|
||||
-callback terminate(any(), cowboy_req:req(), any()) -> ok.
|
||||
-optional_callbacks([terminate/3]).
|
||||
|
@ -457,6 +462,13 @@ handler_call(State=#state{handler=Handler}, HandlerState,
|
|||
websocket_init -> Handler:websocket_init(HandlerState);
|
||||
_ -> Handler:Callback(Message, HandlerState)
|
||||
end of
|
||||
{Commands, HandlerState2} when is_list(Commands) ->
|
||||
handler_call_result(State,
|
||||
HandlerState2, ParseState, NextState, Commands);
|
||||
{Commands, HandlerState2, hibernate} when is_list(Commands) ->
|
||||
handler_call_result(State#state{hibernate=true},
|
||||
HandlerState2, ParseState, NextState, Commands);
|
||||
%% The following call results are deprecated.
|
||||
{ok, HandlerState2} ->
|
||||
NextState(State, HandlerState2, ParseState);
|
||||
{ok, HandlerState2, hibernate} ->
|
||||
|
@ -488,6 +500,32 @@ handler_call(State=#state{handler=Handler}, HandlerState,
|
|||
erlang:raise(Class, Reason, erlang:get_stacktrace())
|
||||
end.
|
||||
|
||||
-spec handler_call_result(#state{}, any(), parse_state(), fun(), commands()) -> no_return().
|
||||
handler_call_result(State0, HandlerState, ParseState, NextState, Commands) ->
|
||||
case commands(Commands, State0, []) of
|
||||
{ok, State} ->
|
||||
NextState(State, HandlerState, ParseState);
|
||||
{stop, State} ->
|
||||
terminate(State, HandlerState, stop);
|
||||
{Error = {error, _}, State} ->
|
||||
terminate(State, HandlerState, Error)
|
||||
end.
|
||||
|
||||
commands([], State, []) ->
|
||||
{ok, State};
|
||||
commands([], State, Data) ->
|
||||
Result = transport_send(State, nofin, lists:reverse(Data)),
|
||||
{Result, State};
|
||||
commands([Frame|Tail], State=#state{extensions=Extensions}, Data0) ->
|
||||
Data = [cow_ws:frame(Frame, Extensions)|Data0],
|
||||
case is_close_frame(Frame) of
|
||||
true ->
|
||||
_ = transport_send(State, fin, lists:reverse(Data)),
|
||||
{stop, State};
|
||||
false ->
|
||||
commands(Tail, State, Data)
|
||||
end.
|
||||
|
||||
transport_send(#state{socket=Stream={Pid, _}, transport=undefined}, IsFin, Data) ->
|
||||
Pid ! {Stream, {data, IsFin, Data}},
|
||||
ok;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue