From f5015cb14bbadef95285460f3d842fbbb05c33c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Fri, 16 Nov 2018 13:48:15 +0100 Subject: Add the set_options Websocket command It allows overriding the idle_timeout option only for now. --- src/cowboy_websocket.erl | 9 +++++++++ test/handlers/ws_set_options_commands_h.erl | 20 +++++++++++++++++++ test/ws_handler_SUITE.erl | 31 ++++++++++++++++++++++++++++- 3 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 test/handlers/ws_set_options_commands_h.erl diff --git a/src/cowboy_websocket.erl b/src/cowboy_websocket.erl index b460745..a94a5ce 100644 --- a/src/cowboy_websocket.erl +++ b/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 diff --git a/test/handlers/ws_set_options_commands_h.erl b/test/handlers/ws_set_options_commands_h.erl new file mode 100644 index 0000000..88d4e72 --- /dev/null +++ b/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}. diff --git a/test/ws_handler_SUITE.erl b/test/ws_handler_SUITE.erl index 6e2bb41..8c45dc7 100644 --- a/test/ws_handler_SUITE.erl +++ b/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. -- cgit v1.2.3