Browse Source

Handle system messages in cowboy_websocket

Loïc Hoguin 7 years ago
parent
commit
f9092126fa
4 changed files with 39 additions and 5 deletions
  1. 2 0
      src/cowboy_http.erl
  2. 2 0
      src/cowboy_http2.erl
  3. 32 3
      src/cowboy_websocket.erl
  4. 3 2
      test/sys_SUITE.erl

+ 2 - 0
src/cowboy_http.erl

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

+ 2 - 0
src/cowboy_http2.erl

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

+ 32 - 3
src/cowboy_websocket.erl

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

+ 3 - 2
test/sys_SUITE.erl

@@ -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', Parent, shutdown},
+	Pid ! {'EXIT', self(), 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),