aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2020-02-10 09:48:14 +0100
committerLoïc Hoguin <[email protected]>2020-02-10 09:48:14 +0100
commit1b7b5ca356609574dac30dbec846dbd75718624e (patch)
tree51055238f2621f79b7520034b642a8a91e484255
parentbd6425ab87428cf4c95f4d23e0a48fd065fbd714 (diff)
downloadgun-1b7b5ca356609574dac30dbec846dbd75718624e.tar.gz
gun-1b7b5ca356609574dac30dbec846dbd75718624e.tar.bz2
gun-1b7b5ca356609574dac30dbec846dbd75718624e.zip
Handle cow_http2_machine timeouts
-rw-r--r--src/gun.erl4
-rw-r--r--src/gun_http2.erl9
-rw-r--r--test/rfc7540_SUITE.erl25
3 files changed, 38 insertions, 0 deletions
diff --git a/src/gun.erl b/src/gun.erl
index 507eef7..b08057f 100644
--- a/src/gun.erl
+++ b/src/gun.erl
@@ -1224,6 +1224,10 @@ handle_common_connected(cast, {data, ReplyTo, StreamRef, IsFin, Data}, _,
{ProtoState2, EvHandlerState} = Protocol:data(ProtoState,
StreamRef, ReplyTo, IsFin, Data, EvHandler, EvHandlerState0),
{keep_state, State#state{protocol_state=ProtoState2, event_handler_state=EvHandlerState}};
+handle_common_connected(info, {timeout, TRef, Name}, _,
+ State=#state{protocol=Protocol, protocol_state=ProtoState}) ->
+ Commands = Protocol:timeout(ProtoState, Name, TRef),
+ commands(Commands, State);
handle_common_connected(Type, Event, StateName, StateData) ->
handle_common_connected_no_input(Type, Event, StateName, StateData).
diff --git a/src/gun_http2.erl b/src/gun_http2.erl
index 8746b3e..b0397f0 100644
--- a/src/gun_http2.erl
+++ b/src/gun_http2.erl
@@ -30,6 +30,7 @@
-export([request/12]).
-export([data/7]).
-export([cancel/5]).
+-export([timeout/3]).
-export([stream_info/2]).
-export([down/1]).
@@ -709,6 +710,14 @@ cancel(State=#http2_state{socket=Socket, transport=Transport, http2_machine=HTTP
EvHandlerState0}
end.
+timeout(State=#http2_state{http2_machine=HTTP2Machine0}, {cow_http2_machine, Name}, TRef) ->
+ case cow_http2_machine:timeout(Name, TRef, HTTP2Machine0) of
+ {ok, HTTP2Machine} ->
+ {state, State#http2_state{http2_machine=HTTP2Machine}};
+ {error, Error={connection_error, _, _}, _HTTP2Machine} ->
+ connection_error(State, Error)
+ end.
+
stream_info(State, StreamRef) ->
case get_stream_by_ref(State, StreamRef) of
#stream{reply_to=ReplyTo} ->
diff --git a/test/rfc7540_SUITE.erl b/test/rfc7540_SUITE.erl
index 6f08ce3..f19ce34 100644
--- a/test/rfc7540_SUITE.erl
+++ b/test/rfc7540_SUITE.erl
@@ -155,3 +155,28 @@ headers_priority_flag(_) ->
StreamRef = gun:get(ConnPid, "/"),
{response, fin, 200, _} = gun:await(ConnPid, StreamRef),
gun:close(ConnPid).
+
+settings_ack_timeout(_) ->
+ doc("Failure to acknowledge the client's SETTINGS frame "
+ "results in a SETTINGS_TIMEOUT connection error. (RFC7540 6.5.3)"),
+ %% We use 'http' here because we are going to do the handshake manually.
+ {ok, _, Port} = init_origin(tcp, http, fun(_, Socket, Transport) ->
+ %% Send a valid preface.
+ ok = Transport:send(Socket, cow_http2:settings(#{})),
+ %% Receive the fixed sequence from the preface.
+ Preface = <<"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n">>,
+ {ok, Preface} = Transport:recv(Socket, byte_size(Preface), 5000),
+ %% Receive the SETTINGS from the preface.
+ {ok, <<Len:24>>} = Transport:recv(Socket, 3, 5000),
+ {ok, <<4:8, 0:40, _:Len/binary>>} = Transport:recv(Socket, 6 + Len, 5000),
+ %% Receive the WINDOW_UPDATE sent with the preface.
+ {ok, <<4:24, 8:8, 0:40, _:32>>} = Transport:recv(Socket, 13, 5000),
+ %% Receive the SETTINGS ack.
+ {ok, <<0:24, 4:8, 1:8, 0:32>>} = Transport:recv(Socket, 9, 5000),
+ %% Do not ack the client preface. Expect a GOAWAY with reason SETTINGS_TIMEOUT.
+ {ok, << _:24, 7:8, _:72, 4:32 >>} = Transport:recv(Socket, 17, 6000)
+ end),
+ {ok, ConnPid} = gun:open("localhost", Port, #{protocols => [http2]}),
+ {ok, http2} = gun:await_up(ConnPid),
+ timer:sleep(6000),
+ gun:close(ConnPid).