@@ -632,8 +632,13 @@ handle_async(
632632) ->
633633 ? LOG_WARNING (" S3 request timed out on ~tw /~tw " , [Conn , StreamRef ]),
634634 counters :add (counter (), ? C_REQUEST_TIMEOUTS , 1 ),
635- gun :cancel (Conn , StreamRef ),
636- finish_async (State ),
635+ % % gun cannot cancel an HTTP/1.1 stream on the wire - it only stops
636+ % % forwarding events to the owner. The cancelled stream continues to
637+ % % occupy the connection, blocking subsequent requests until its full
638+ % % response is received. Close the whole connection instead; the pool
639+ % % observes the 'DOWN' and opens a fresh replacement.
640+ gun :close (Conn ),
641+ finish_async_close (State ),
637642 {done_cancel , {error , timeout }}.
638643
639644- spec cancel_async (async_req (), async_state ()) -> ok .
@@ -679,6 +684,27 @@ finish_async(#{conn := Conn, pool := Pool} = State) ->
679684 counters :sub (counter (), ? C_ACTIVE_REQUESTS , 1 ),
680685 ok = rabbitmq_stream_s3_api_aws_pool :checkin (Pool , Conn ).
681686
687+ % % Finish an async request when the connection is being closed (e.g. on
688+ % % timeout). Same bookkeeping as `finish_async` but omits the pool checkin -
689+ % % the pool's 'DOWN' handler removes the conn and grows a replacement.
690+ finish_async_close (State ) ->
691+ case State of
692+ #{timer_ref := TimerRef , stream_ref := StreamRef } ->
693+ case erlang :cancel_timer (TimerRef ) of
694+ false ->
695+ receive
696+ {request_timeout , StreamRef } -> ok
697+ after 0 -> ok
698+ end ;
699+ _ ->
700+ ok
701+ end ;
702+ _ ->
703+ ok
704+ end ,
705+ counters :sub (counter (), ? C_ACTIVE_REQUESTS , 1 ),
706+ ok .
707+
682708- spec request (http_method (), key (), req_headers (), iodata (), request_opts ()) ->
683709 {ok , http_response ()}
684710 | {error , any ()}.
0 commit comments