@@ -52,6 +52,12 @@ if Code.ensure_loaded?(:hackney) do
5252 # Convert cacertfile to cacerts to fix SSL with custom CA certificates
5353 if Version . match? ( @ hackney_version , "~> 3.0" ) do
5454 defp process_options ( opts ) do
55+ opts
56+ |> process_ssl_options ( )
57+ |> process_timeout_options ( )
58+ end
59+
60+ defp process_ssl_options ( opts ) do
5561 case Keyword . get ( opts , :ssl_options ) do
5662 nil ->
5763 opts
@@ -77,6 +83,19 @@ if Code.ensure_loaded?(:hackney) do
7783 end
7884 end
7985 end
86+
87+ defp process_timeout_options ( opts ) do
88+ # Hackney 3.X pools connections with their original recv_timeout.
89+ # If user specifies a short timeout (<= 1000ms), disable pooling to ensure it's respected.
90+ # This is necessary because pooled connections can't have their timeout updated.
91+ case Keyword . get ( opts , :recv_timeout ) do
92+ timeout when is_integer ( timeout ) and timeout <= 1000 ->
93+ Keyword . put_new ( opts , :pool , false )
94+
95+ _ ->
96+ opts
97+ end
98+ end
8099 else
81100 defp process_options ( opts ) , do: opts
82101 end
@@ -159,10 +178,11 @@ if Code.ensure_loaded?(:hackney) do
159178
160179 if Version . match? ( @ hackney_version , "~> 3.0" ) do
161180 # Hackney 3.x: for streaming requests, :hackney.start_response returns handle as PID
162- # Must use :hackney_conn.body/1 to read body (max_body deprecated in 3.x)
163- defp handle ( { :ok , status , headers , handle } , _opts )
181+ # Must use :hackney_conn.body/2 to read body with timeout
182+ defp handle ( { :ok , status , headers , handle } , opts )
164183 when is_hackney_connection_handle ( handle ) do
165- with { :ok , body } <- :hackney_conn . body ( handle ) do
184+ timeout = Keyword . get ( opts , :recv_timeout , :infinity )
185+ with { :ok , body } <- :hackney_conn . body ( handle , timeout ) do
166186 { :ok , status , headers , body }
167187 end
168188 end
0 commit comments