Просмотр исходного кода

Ensure unknown options are ignored in set_options command

Loïc Hoguin 6 лет назад
Родитель
Сommit
75045637fc

+ 4 - 2
src/cowboy_compress_h.erl

@@ -141,7 +141,8 @@ fold([Trailers={trailers, _}|Tail], State0=#state{compress=gzip}, Acc) ->
 	{{data, fin, Data}, State} = gzip_data({data, fin, <<>>}, State0),
 	fold(Tail, State, [Trailers, {data, nofin, Data}|Acc]);
 %% All the options from this handler can be updated for the current stream.
-fold([{set_options, Opts}|Tail], State=#state{
+%% The set_options command must be propagated as-is regardless.
+fold([SetOptions={set_options, Opts}|Tail], State=#state{
 		threshold=CompressThreshold0, deflate_flush=DeflateFlush0}, Acc) ->
 	CompressThreshold = maps:get(compress_threshold, Opts, CompressThreshold0),
 	DeflateFlush = case Opts of
@@ -150,7 +151,8 @@ fold([{set_options, Opts}|Tail], State=#state{
 		_ ->
 			DeflateFlush0
 	end,
-	fold(Tail, State#state{threshold=CompressThreshold, deflate_flush=DeflateFlush}, Acc);
+	fold(Tail, State#state{threshold=CompressThreshold, deflate_flush=DeflateFlush},
+		[SetOptions|Acc]);
 %% Otherwise, we have an unrelated command or compression is disabled.
 fold([Command|Tail], State, Acc) ->
 	fold(Tail, State, [Command|Acc]).

+ 3 - 0
src/cowboy_http2.erl

@@ -576,6 +576,9 @@ commands(State=#state{socket=Socket, transport=Transport, http2_init=upgrade},
 commands(State0, StreamID, [{switch_protocol, Headers, _Mod, _ModState}|Tail]) ->
 	State = info(State0, StreamID, {headers, 200, Headers}),
 	commands(State, StreamID, Tail);
+%% Set options dynamically.
+commands(State, StreamID, [{set_options, _Opts}|Tail]) ->
+	commands(State, StreamID, Tail);
 commands(State, StreamID, [stop|_Tail]) ->
 	%% @todo Do we want to run the commands after a stop?
 	%% @todo Do we even allow commands after?

+ 6 - 0
test/handlers/stream_handler_h.erl

@@ -34,6 +34,12 @@ init_commands(_, _, #state{test=crash_in_terminate}) ->
 	[{response, 200, #{<<"content-length">> => <<"12">>}, <<"Hello world!">>}, stop];
 init_commands(_, _, #state{test=crash_in_early_error}) ->
 	error(crash);
+init_commands(_, _, #state{test=set_options_ignore_unknown}) ->
+	[
+		{set_options, #{unknown_options => true}},
+		{response, 200, #{<<"content-length">> => <<"12">>}, <<"Hello world!">>},
+		stop
+	];
 init_commands(_, _, State=#state{test=shutdown_on_stream_stop}) ->
 	Spawn = init_process(false, State),
 	[{headers, 200, #{}}, {spawn, Spawn, 5000}, stop];

+ 18 - 0
test/stream_handler_SUITE.erl

@@ -224,6 +224,24 @@ do_crash_in_early_error_fatal(Config) ->
 	%% Confirm the connection gets closed.
 	gun_down(ConnPid).
 
+set_options_ignore_unknown(Config) ->
+	doc("Confirm that unknown options are ignored when using the set_options commands."),
+	Self = self(),
+	ConnPid = gun_open(Config),
+	Ref = gun:get(ConnPid, "/long_polling", [
+		{<<"accept-encoding">>, <<"gzip">>},
+		{<<"x-test-case">>, <<"set_options_ignore_unknown">>},
+		{<<"x-test-pid">>, pid_to_list(Self)}
+	]),
+	%% Confirm init/3 is called.
+	Pid = receive {Self, P, init, _, _, _} -> P after 1000 -> error(timeout) end,
+	%% Confirm terminate/3 is called, indicating the stream ended.
+	receive {Self, Pid, terminate, _, _, _} -> ok after 1000 -> error(timeout) end,
+	%% Confirm the response is sent.
+	{response, nofin, 200, _} = gun:await(ConnPid, Ref),
+	{ok, _} = gun:await_body(ConnPid, Ref),
+	ok.
+
 shutdown_on_stream_stop(Config) ->
 	doc("Confirm supervised processes are shutdown when stopping the stream."),
 	Self = self(),