From 1b7b5ca356609574dac30dbec846dbd75718624e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Mon, 10 Feb 2020 09:48:14 +0100 Subject: Handle cow_http2_machine timeouts --- src/gun.erl | 4 ++++ src/gun_http2.erl | 9 +++++++++ test/rfc7540_SUITE.erl | 25 +++++++++++++++++++++++++ 3 files changed, 38 insertions(+) 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, <>} = 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). -- cgit v1.2.3