Browse Source

Add the set_options Websocket command

It allows overriding the idle_timeout option only for now.
Loïc Hoguin 6 years ago
parent
commit
f5015cb14b
3 changed files with 59 additions and 1 deletions
  1. 9 0
      src/cowboy_websocket.erl
  2. 20 0
      test/handlers/ws_set_options_commands_h.erl
  3. 30 1
      test/ws_handler_SUITE.erl

+ 9 - 0
src/cowboy_websocket.erl

@@ -34,6 +34,7 @@
 -type commands() :: [cow_ws:frame()
 	| {active, boolean()}
 	| {deflate, boolean()}
+	| {set_options, map()}
 ].
 -export_type([commands/0]).
 
@@ -527,6 +528,14 @@ commands([{active, Active}|Tail], State, Data) when is_boolean(Active) ->
 	commands(Tail, State#state{active=Active}, Data);
 commands([{deflate, Deflate}|Tail], State, Data) when is_boolean(Deflate) ->
 	commands(Tail, State#state{deflate=Deflate}, Data);
+commands([{set_options, SetOpts}|Tail], State0=#state{opts=Opts}, Data) ->
+	State = case SetOpts of
+		#{idle_timeout := IdleTimeout} ->
+			loop_timeout(State0#state{opts=Opts#{idle_timeout => IdleTimeout}});
+		_ ->
+			State0
+	end,
+	commands(Tail, State, Data);
 commands([Frame|Tail], State, Data0) ->
 	Data = [frame(Frame, State)|Data0],
 	case is_close_frame(Frame) of

+ 20 - 0
test/handlers/ws_set_options_commands_h.erl

@@ -0,0 +1,20 @@
+%% This module sets options based on the frame received.
+
+-module(ws_set_options_commands_h).
+-behavior(cowboy_websocket).
+
+-export([init/2]).
+-export([websocket_handle/2]).
+-export([websocket_info/2]).
+
+init(Req, RunOrHibernate) ->
+	{cowboy_websocket, Req, RunOrHibernate,
+		#{idle_timeout => infinity}}.
+
+websocket_handle(Frame={text, <<"idle_timeout_short">>}, State=run) ->
+	{[{set_options, #{idle_timeout => 500}}, Frame], State};
+websocket_handle(Frame={text, <<"idle_timeout_short">>}, State=hibernate) ->
+	{[{set_options, #{idle_timeout => 500}}, Frame], State, hibernate}.
+
+websocket_info(_Info, State) ->
+	{[], State}.

+ 30 - 1
test/ws_handler_SUITE.erl

@@ -51,7 +51,8 @@ init_dispatch(Name) ->
 		{"/handle", ws_handle_commands_h, RunOrHibernate},
 		{"/info", ws_info_commands_h, RunOrHibernate},
 		{"/active", ws_active_commands_h, RunOrHibernate},
-		{"/deflate", ws_deflate_commands_h, RunOrHibernate}
+		{"/deflate", ws_deflate_commands_h, RunOrHibernate},
+		{"/set_options", ws_set_options_commands_h, RunOrHibernate}
 	]}]).
 
 %% Support functions for testing using Gun.
@@ -257,3 +258,31 @@ websocket_deflate_ignore_if_not_negotiated(Config) ->
 		{ok, {text, <<"Hello.">>}} = receive_ws(ConnPid, StreamRef)
 	end || _ <- lists:seq(1, 10)],
 	ok.
+
+websocket_set_options_idle_timeout(Config) ->
+	doc("The idle_timeout option can be modified using the "
+		"command {set_options, Opts} at runtime."),
+	ConnPid = gun_open(Config),
+	StreamRef = gun:ws_upgrade(ConnPid, "/set_options"),
+	receive
+		{gun_upgrade, ConnPid, StreamRef, [<<"websocket">>], _} ->
+			ok;
+		{gun_response, ConnPid, _, _, Status, Headers} ->
+			exit({ws_upgrade_failed, Status, Headers});
+		{gun_error, ConnPid, StreamRef, Reason} ->
+			exit({ws_upgrade_failed, Reason})
+	after 1000 ->
+		error(timeout)
+	end,
+	%% We don't send anything for a short while and confirm
+	%% that idle_timeout does not trigger.
+	{error, timeout} = gun:await(ConnPid, StreamRef, 2000),
+	%% Trigger the change in idle_timeout and confirm that
+	%% the connection gets closed soon after.
+	gun:ws_send(ConnPid, {text, <<"idle_timeout_short">>}),
+	receive
+		{gun_down, ConnPid, _, _, _, _} ->
+			ok
+	after 2000 ->
+		error(timeout)
+	end.