mirror of
https://github.com/ninenines/cowboy.git
synced 2025-07-14 20:30:23 +00:00
Handle system messages in cowboy_websocket
This commit is contained in:
parent
b9c8d86502
commit
f9092126fa
4 changed files with 39 additions and 5 deletions
|
@ -192,6 +192,7 @@ loop(State=#state{parent=Parent, socket=Socket, transport=Transport, opts=Opts,
|
||||||
loop(State, Buffer);
|
loop(State, Buffer);
|
||||||
%% System messages.
|
%% System messages.
|
||||||
{'EXIT', Parent, Reason} ->
|
{'EXIT', Parent, Reason} ->
|
||||||
|
%% @todo We should exit gracefully, if possible.
|
||||||
exit(Reason);
|
exit(Reason);
|
||||||
{system, From, Request} ->
|
{system, From, Request} ->
|
||||||
sys:handle_system_msg(Request, From, Parent, ?MODULE, [], {State, Buffer});
|
sys:handle_system_msg(Request, From, Parent, ?MODULE, [], {State, Buffer});
|
||||||
|
@ -1224,6 +1225,7 @@ system_continue(_, _, {State, Buffer}) ->
|
||||||
|
|
||||||
-spec system_terminate(any(), _, _, {#state{}, binary()}) -> no_return().
|
-spec system_terminate(any(), _, _, {#state{}, binary()}) -> no_return().
|
||||||
system_terminate(Reason, _, _, {State, _}) ->
|
system_terminate(Reason, _, _, {State, _}) ->
|
||||||
|
%% @todo We should exit gracefully, if possible.
|
||||||
terminate(State, Reason).
|
terminate(State, Reason).
|
||||||
|
|
||||||
-spec system_code_change(Misc, _, _, _) -> {ok, Misc} when Misc::{#state{}, binary()}.
|
-spec system_code_change(Misc, _, _, _) -> {ok, Misc} when Misc::{#state{}, binary()}.
|
||||||
|
|
|
@ -225,6 +225,7 @@ loop(State=#state{parent=Parent, socket=Socket, transport=Transport,
|
||||||
terminate(State, {socket_error, Reason, 'An error has occurred on the socket.'});
|
terminate(State, {socket_error, Reason, 'An error has occurred on the socket.'});
|
||||||
%% System messages.
|
%% System messages.
|
||||||
{'EXIT', Parent, Reason} ->
|
{'EXIT', Parent, Reason} ->
|
||||||
|
%% @todo We should exit gracefully.
|
||||||
exit(Reason);
|
exit(Reason);
|
||||||
{system, From, Request} ->
|
{system, From, Request} ->
|
||||||
sys:handle_system_msg(Request, From, Parent, ?MODULE, [], {State, Buffer});
|
sys:handle_system_msg(Request, From, Parent, ?MODULE, [], {State, Buffer});
|
||||||
|
@ -1125,6 +1126,7 @@ system_continue(_, _, {State, Buffer}) ->
|
||||||
|
|
||||||
-spec system_terminate(any(), _, _, {#state{}, binary()}) -> no_return().
|
-spec system_terminate(any(), _, _, {#state{}, binary()}) -> no_return().
|
||||||
system_terminate(Reason, _, _, {State, _}) ->
|
system_terminate(Reason, _, _, {State, _}) ->
|
||||||
|
%% @todo We should exit gracefully, if possible.
|
||||||
terminate(State, Reason).
|
terminate(State, Reason).
|
||||||
|
|
||||||
-spec system_code_change(Misc, _, _, _) -> {ok, Misc} when Misc::{#state{}, binary()}.
|
-spec system_code_change(Misc, _, _, _) -> {ok, Misc} when Misc::{#state{}, binary()}.
|
||||||
|
|
|
@ -22,6 +22,10 @@
|
||||||
-export([takeover/7]).
|
-export([takeover/7]).
|
||||||
-export([handler_loop/3]).
|
-export([handler_loop/3]).
|
||||||
|
|
||||||
|
-export([system_continue/3]).
|
||||||
|
-export([system_terminate/4]).
|
||||||
|
-export([system_code_change/4]).
|
||||||
|
|
||||||
-type call_result(State) :: {ok, State}
|
-type call_result(State) :: {ok, State}
|
||||||
| {ok, State, hibernate}
|
| {ok, State, hibernate}
|
||||||
| {reply, cow_ws:frame() | [cow_ws:frame()], State}
|
| {reply, cow_ws:frame() | [cow_ws:frame()], State}
|
||||||
|
@ -58,6 +62,8 @@
|
||||||
-export_type([opts/0]).
|
-export_type([opts/0]).
|
||||||
|
|
||||||
-record(state, {
|
-record(state, {
|
||||||
|
parent = undefined :: pid(),
|
||||||
|
ref :: ranch:ref(),
|
||||||
socket = undefined :: inet:socket() | undefined,
|
socket = undefined :: inet:socket() | undefined,
|
||||||
transport = undefined :: module(),
|
transport = undefined :: module(),
|
||||||
handler :: module(),
|
handler :: module(),
|
||||||
|
@ -199,11 +205,12 @@ websocket_handshake(State=#state{key=Key},
|
||||||
%% @todo Keep parent and handle system messages.
|
%% @todo Keep parent and handle system messages.
|
||||||
-spec takeover(pid(), ranch:ref(), inet:socket(), module(), any(), binary(),
|
-spec takeover(pid(), ranch:ref(), inet:socket(), module(), any(), binary(),
|
||||||
{#state{}, any()}) -> ok.
|
{#state{}, any()}) -> ok.
|
||||||
takeover(_Parent, Ref, Socket, Transport, _Opts, Buffer,
|
takeover(Parent, Ref, Socket, Transport, _Opts, Buffer,
|
||||||
{State0=#state{handler=Handler}, HandlerState}) ->
|
{State0=#state{handler=Handler}, HandlerState}) ->
|
||||||
%% @todo We should have an option to disable this behavior.
|
%% @todo We should have an option to disable this behavior.
|
||||||
ranch:remove_connection(Ref),
|
ranch:remove_connection(Ref),
|
||||||
State1 = handler_loop_timeout(State0#state{socket=Socket, transport=Transport}),
|
State1 = handler_loop_timeout(State0#state{parent=Parent,
|
||||||
|
ref=Ref, socket=Socket, transport=Transport}),
|
||||||
State = State1#state{key=undefined, messages=Transport:messages()},
|
State = State1#state{key=undefined, messages=Transport:messages()},
|
||||||
case erlang:function_exported(Handler, websocket_init, 1) of
|
case erlang:function_exported(Handler, websocket_init, 1) of
|
||||||
true -> handler_call(State, HandlerState, Buffer, websocket_init, undefined, fun handler_before_loop/3);
|
true -> handler_call(State, HandlerState, Buffer, websocket_init, undefined, fun handler_before_loop/3);
|
||||||
|
@ -235,7 +242,7 @@ handler_loop_timeout(State=#state{timeout=Timeout, timeout_ref=PrevRef}) ->
|
||||||
|
|
||||||
-spec handler_loop(#state{}, any(), binary())
|
-spec handler_loop(#state{}, any(), binary())
|
||||||
-> {ok, cowboy_middleware:env()}.
|
-> {ok, cowboy_middleware:env()}.
|
||||||
handler_loop(State=#state{socket=Socket, messages={OK, Closed, Error},
|
handler_loop(State=#state{parent=Parent, socket=Socket, messages={OK, Closed, Error},
|
||||||
timeout_ref=TRef}, HandlerState, SoFar) ->
|
timeout_ref=TRef}, HandlerState, SoFar) ->
|
||||||
receive
|
receive
|
||||||
{OK, Socket, Data} ->
|
{OK, Socket, Data} ->
|
||||||
|
@ -250,6 +257,13 @@ handler_loop(State=#state{socket=Socket, messages={OK, Closed, Error},
|
||||||
websocket_close(State, HandlerState, timeout);
|
websocket_close(State, HandlerState, timeout);
|
||||||
{timeout, OlderTRef, ?MODULE} when is_reference(OlderTRef) ->
|
{timeout, OlderTRef, ?MODULE} when is_reference(OlderTRef) ->
|
||||||
handler_loop(State, HandlerState, SoFar);
|
handler_loop(State, HandlerState, SoFar);
|
||||||
|
%% System messages.
|
||||||
|
{'EXIT', Parent, Reason} ->
|
||||||
|
%% @todo We should exit gracefully.
|
||||||
|
exit(Reason);
|
||||||
|
{system, From, Request} ->
|
||||||
|
sys:handle_system_msg(Request, From, Parent, ?MODULE, [],
|
||||||
|
{State, HandlerState, SoFar});
|
||||||
%% Calls from supervisor module.
|
%% Calls from supervisor module.
|
||||||
{'$gen_call', From, Call} ->
|
{'$gen_call', From, Call} ->
|
||||||
cowboy_children:handle_supervisor_call(Call, From, [], ?MODULE),
|
cowboy_children:handle_supervisor_call(Call, From, [], ?MODULE),
|
||||||
|
@ -441,3 +455,18 @@ terminate(State, HandlerState, Reason) ->
|
||||||
|
|
||||||
handler_terminate(#state{handler=Handler, req=Req}, HandlerState, Reason) ->
|
handler_terminate(#state{handler=Handler, req=Req}, HandlerState, Reason) ->
|
||||||
cowboy_handler:terminate(Reason, Req, HandlerState, Handler).
|
cowboy_handler:terminate(Reason, Req, HandlerState, Handler).
|
||||||
|
|
||||||
|
%% System callbacks.
|
||||||
|
|
||||||
|
-spec system_continue(_, _, {#state{}, any(), binary()}) -> ok.
|
||||||
|
system_continue(_, _, {State, HandlerState, SoFar}) ->
|
||||||
|
handler_loop(State, HandlerState, SoFar).
|
||||||
|
|
||||||
|
-spec system_terminate(any(), _, _, {#state{}, any(), binary()}) -> no_return().
|
||||||
|
system_terminate(Reason, _, _, {State, HandlerState, _}) ->
|
||||||
|
%% @todo We should exit gracefully, if possible.
|
||||||
|
terminate(State, HandlerState, Reason).
|
||||||
|
|
||||||
|
-spec system_code_change(Misc, _, _, _) -> {ok, Misc} when Misc::{#state{}, any(), binary()}.
|
||||||
|
system_code_change(Misc, _, _, _) ->
|
||||||
|
{ok, Misc}.
|
||||||
|
|
|
@ -27,6 +27,8 @@ groups() ->
|
||||||
[{sys, [parallel], ct_helper:all(?MODULE)}].
|
[{sys, [parallel], ct_helper:all(?MODULE)}].
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
|
ct:print("This test suite will produce error reports about "
|
||||||
|
"EXIT signals for unknown processes."),
|
||||||
ProtoOpts = #{
|
ProtoOpts = #{
|
||||||
env => #{dispatch => init_dispatch(Config)}
|
env => #{dispatch => init_dispatch(Config)}
|
||||||
},
|
},
|
||||||
|
@ -467,8 +469,7 @@ trap_exit_other_exit_ws(Config) ->
|
||||||
{ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
|
{ok, {http_response, {1, 1}, 101, _}, _} = erlang:decode_packet(http, Handshake, []),
|
||||||
timer:sleep(100),
|
timer:sleep(100),
|
||||||
Pid = do_get_remote_pid_tcp(Socket),
|
Pid = do_get_remote_pid_tcp(Socket),
|
||||||
Parent = do_get_parent_pid(Pid),
|
Pid ! {'EXIT', self(), shutdown},
|
||||||
Pid ! {'EXIT', Parent, shutdown},
|
|
||||||
%% The process stays alive.
|
%% The process stays alive.
|
||||||
{error, timeout} = gen_tcp:recv(Socket, 0, 1000),
|
{error, timeout} = gen_tcp:recv(Socket, 0, 1000),
|
||||||
true = is_process_alive(Pid),
|
true = is_process_alive(Pid),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue