aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorViktor Söderqvist <[email protected]>2020-10-21 16:58:22 +0200
committerLoïc Hoguin <[email protected]>2020-11-12 14:21:57 +0100
commit30a971a039c2725726080ce6d50ce90e1108cb5a (patch)
tree772af8cfd71eb420ba53ee7c56cce00ad69140f7 /src
parent7dabafe7a93582c71cdabc3c41a609f582afd7d1 (diff)
downloadgun-30a971a039c2725726080ce6d50ce90e1108cb5a.tar.gz
gun-30a971a039c2725726080ce6d50ce90e1108cb5a.tar.bz2
gun-30a971a039c2725726080ce6d50ce90e1108cb5a.zip
Fail fast while closing if reconnect is off
If a request/headers/connect/ws_upgrade is created when a connection is in state 'closing', such as after receiving an HTTP/2 GOAWAY frame or an HTTP/1.1 "Connection: close" header, an error message is sent back to the caller immediately, if reconnect is off (that is if the option retry is set to 0). This allows an application to retry the request on another connection without waiting for all streams on the current connection to complete.
Diffstat (limited to 'src')
-rw-r--r--src/gun.erl21
-rw-r--r--src/gun_http.erl22
2 files changed, 39 insertions, 4 deletions
diff --git a/src/gun.erl b/src/gun.erl
index ac643e0..ddb5007 100644
--- a/src/gun.erl
+++ b/src/gun.erl
@@ -1332,6 +1332,27 @@ closing(state_timeout, closing_timeout, State=#state{status=Status}) ->
_ -> normal
end,
disconnect(State, Reason);
+%% When reconnect is disabled, fail HTTP/Websocket operations immediately.
+closing(cast, {headers, ReplyTo, StreamRef, _Method, _Path, _Headers, _InitialFlow},
+ State=#state{opts=#{retry := 0}}) ->
+ ReplyTo ! {gun_error, self(), StreamRef, closing},
+ {keep_state, State};
+closing(cast, {request, ReplyTo, StreamRef, _Method, _Path, _Headers, _Body, _InitialFlow},
+ State=#state{opts=#{retry := 0}}) ->
+ ReplyTo ! {gun_error, self(), StreamRef, closing},
+ {keep_state, State};
+closing(cast, {connect, ReplyTo, StreamRef, _Destination, _Headers, _InitialFlow},
+ State=#state{opts=#{retry := 0}}) ->
+ ReplyTo ! {gun_error, self(), StreamRef, closing},
+ {keep_state, State};
+closing(cast, {ws_upgrade, ReplyTo, StreamRef, _Path, _Headers},
+ State=#state{opts=#{retry := 0}}) ->
+ ReplyTo ! {gun_error, self(), StreamRef, closing},
+ {keep_state, State};
+closing(cast, {ws_upgrade, ReplyTo, StreamRef, _Path, _Headers, _WsOpts},
+ State=#state{opts=#{retry := 0}}) ->
+ ReplyTo ! {gun_error, self(), StreamRef, closing},
+ {keep_state, State};
closing(Type, Event, State) ->
handle_common_connected(Type, Event, ?FUNCTION_NAME, State).
diff --git a/src/gun_http.erl b/src/gun_http.erl
index 8cbeada..9c52f2d 100644
--- a/src/gun_http.erl
+++ b/src/gun_http.erl
@@ -418,7 +418,7 @@ handle_response(Rest, State=#http_state{version=ClientVersion, opts=Opts, connec
Status, Headers, Handlers0), EvHandlerState1}
end
end,
- EvHandlerState = case IsFin of
+ EvHandlerState3 = case IsFin of
nofin ->
EvHandlerState2;
fin ->
@@ -436,17 +436,31 @@ handle_response(Rest, State=#http_state{version=ClientVersion, opts=Opts, connec
%% We always reset in_state even if not chunked.
if
IsFin =:= fin, Conn2 =:= close ->
- {close, CookieStore, EvHandlerState};
+ {close, CookieStore, EvHandlerState3};
IsFin =:= fin ->
handle(Rest, end_stream(State#http_state{in=In,
in_state={0, 0}, connection=Conn2,
streams=[Stream#stream{handler_state=Handlers}|Tail]}),
- CookieStore, EvHandler, EvHandlerState);
+ CookieStore, EvHandler, EvHandlerState3);
+ Conn2 =:= close ->
+ close_streams(State, Tail, closing),
+ {CommandOrCommands, CookieStore1, EvHandlerState4} =
+ handle(Rest, State#http_state{in=In,
+ in_state={0, 0}, connection=Conn2,
+ streams=[Stream#stream{handler_state=Handlers}]},
+ CookieStore, EvHandler, EvHandlerState3),
+ Commands = if
+ is_list(CommandOrCommands) ->
+ CommandOrCommands ++ [closing(State)];
+ true ->
+ [CommandOrCommands, closing(State)]
+ end,
+ {Commands, CookieStore1, EvHandlerState4};
true ->
handle(Rest, State#http_state{in=In,
in_state={0, 0}, connection=Conn2,
streams=[Stream#stream{handler_state=Handlers}|Tail]},
- CookieStore, EvHandler, EvHandlerState)
+ CookieStore, EvHandler, EvHandlerState3)
end.
%% The state must be first in order to retrieve it when the stream ended.