3232 % % Request/Response (sync)
3333 request /5 ,
3434 request /6 ,
35+ request /7 ,
3536 request_streaming /5 ,
3637 body /1 ,
3738 body /2 ,
127128 idle_timeout = ? IDLE_TIMEOUT :: timeout (),
128129 connect_options = [] :: list (),
129130 ssl_options = [] :: list (),
131+ inform_fun :: fun ((integer (), binary (), list ()) -> any ()) | undefined ,
130132
131133 % % Pool integration
132134 pool_pid :: pid () | undefined , % % If set, connection is from a pool
@@ -234,12 +236,20 @@ get_state(Pid) ->
234236-spec request (pid (), binary (), binary (), list (), binary () | iolist ()) ->
235237 {ok , integer (), list ()} | {ok , integer (), list (), binary ()} | {error , term ()}.
236238request (Pid , Method , Path , Headers , Body ) ->
237- request (Pid , Method , Path , Headers , Body , infinity ).
239+ request (Pid , Method , Path , Headers , Body , infinity , [] ).
238240
239241-spec request (pid (), binary (), binary (), list (), binary () | iolist (), timeout ()) ->
240242 {ok , integer (), list ()} | {ok , integer (), list (), binary ()} | {error , term ()}.
241243request (Pid , Method , Path , Headers , Body , Timeout ) ->
242- gen_statem :call (Pid , {request , Method , Path , Headers , Body }, Timeout ).
244+ request (Pid , Method , Path , Headers , Body , Timeout , []).
245+
246+ % % @doc Make an HTTP request with additional request options.
247+ % % Options:
248+ % % - inform_fun: fun(Status, Reason, Headers) - callback for 1xx responses
249+ -spec request (pid (), binary (), binary (), list (), binary () | iolist (), timeout (), list ()) ->
250+ {ok , integer (), list ()} | {ok , integer (), list (), binary ()} | {error , term ()}.
251+ request (Pid , Method , Path , Headers , Body , Timeout , ReqOpts ) ->
252+ gen_statem :call (Pid , {request , Method , Path , Headers , Body , ReqOpts }, Timeout ).
243253
244254% % @doc Send an HTTP/3 request and return headers immediately.
245255% % Returns {ok, Status, Headers} and allows subsequent stream_body/1 calls.
@@ -483,7 +493,8 @@ init([DefaultOwner, Opts]) ->
483493 ssl_options = maps :get (ssl_options , Opts , []),
484494 pool_pid = maps :get (pool_pid , Opts , undefined ),
485495 enable_push = maps :get (enable_push , Opts , false ),
486- no_reuse = maps :get (no_reuse , Opts , false )
496+ no_reuse = maps :get (no_reuse , Opts , false ),
497+ inform_fun = maps :get (inform_fun , Opts , undefined )
487498 },
488499
489500 % % If socket is provided, start in connected state; otherwise start in idle
@@ -761,12 +772,12 @@ connected({call, From}, is_upgraded_ssl, #conn_data{upgraded_ssl = Upgraded}) ->
761772connected ({call , From }, is_no_reuse , # conn_data {no_reuse = NoReuse }) ->
762773 {keep_state_and_data , [{reply , From , NoReuse }]};
763774
764- connected ({call , From }, {request , Method , Path , Headers , Body }, # conn_data {protocol = http2 } = Data ) ->
765- % % HTTP/2 request - use h2_machine
775+ connected ({call , From }, {request , Method , Path , Headers , Body , _ReqOpts }, # conn_data {protocol = http2 } = Data ) ->
776+ % % HTTP/2 request - use h2_machine (1xx not applicable for HTTP/2)
766777 do_h2_request (From , Method , Path , Headers , Body , Data );
767778
768- connected ({call , From }, {request , Method , Path , Headers , Body }, # conn_data {protocol = http3 } = Data ) ->
769- % % HTTP/3 request - use hackney_h3
779+ connected ({call , From }, {request , Method , Path , Headers , Body , _ReqOpts }, # conn_data {protocol = http3 } = Data ) ->
780+ % % HTTP/3 request - use hackney_h3 (1xx not applicable for HTTP/3)
770781 do_h3_request (From , Method , Path , Headers , Body , Data );
771782
772783connected ({call , From }, {request_async , Method , Path , Headers , Body , AsyncMode , StreamTo }, # conn_data {protocol = http3 } = Data ) ->
@@ -781,8 +792,9 @@ connected({call, From}, {request_streaming, Method, Path, Headers, Body}, #conn_
781792 % % HTTP/3 request with streaming body reads (returns headers, then stream_body for chunks)
782793 do_h3_request_streaming (From , Method , Path , Headers , Body , Data );
783794
784- connected ({call , From }, {request , Method , Path , Headers , Body }, Data ) ->
795+ connected ({call , From }, {request , Method , Path , Headers , Body , ReqOpts }, Data ) ->
785796 % % HTTP/1.1 request
797+ InformFun = proplists :get_value (inform_fun , ReqOpts , undefined ),
786798 NewData = Data # conn_data {
787799 request_from = From ,
788800 method = Method ,
@@ -795,7 +807,8 @@ connected({call, From}, {request, Method, Path, Headers, Body}, Data) ->
795807 buffer = <<>>,
796808 async = false ,
797809 async_ref = undefined ,
798- stream_to = undefined
810+ stream_to = undefined ,
811+ inform_fun = InformFun
799812 },
800813 {next_state , sending , NewData , [{next_event , internal , {send_request , Method , Path , Headers , Body }}]};
801814
@@ -1650,9 +1663,38 @@ send_body(Transport, Socket, Body) when is_binary(Body); is_list(Body) ->
16501663 Transport :send (Socket , Body ).
16511664
16521665% % @private Receive and parse response status and headers
1653- % % Note: 1XX informational responses are automatically skipped by the HTTP parser
1666+ % % Handles 1XX informational responses per RFC 7231
16541667recv_status_and_headers (Data ) ->
1655- recv_status (Data ).
1668+ recv_status_and_headers_loop (Data ).
1669+
1670+ recv_status_and_headers_loop (Data ) ->
1671+ case recv_status (Data ) of
1672+ {ok , Status , Headers , NewData } when Status >= 100 , Status < 200 ->
1673+ % % 1XX informational response per RFC 7231 Section 6.2
1674+ # conn_data {inform_fun = InformFun , reason = Reason , parser = OldParser ,
1675+ stream_to = StreamTo , async_ref = AsyncRef } = NewData ,
1676+ HeadersList = hackney_headers :to_list (Headers ),
1677+ % % Handle differently for sync vs async mode
1678+ case StreamTo of
1679+ undefined ->
1680+ % % Sync mode - call callback if provided
1681+ case InformFun of
1682+ undefined -> ok ;
1683+ Fun when is_function (Fun , 3 ) ->
1684+ Fun (Status , Reason , HeadersList )
1685+ end ;
1686+ _ ->
1687+ % % Async mode - send message to stream_to
1688+ StreamTo ! {hackney_response , AsyncRef , {informational , Status , Reason , HeadersList }}
1689+ end ,
1690+ % % Reset parser for next response, preserving any buffered data
1691+ % % The old parser's buffer may contain the start of the next response
1692+ OldBuffer = hackney_http :get (OldParser , buffer ),
1693+ NewParser = hackney_http :parser ([response ]),
1694+ recv_status_and_headers_loop (NewData # conn_data {parser = NewParser , buffer = OldBuffer });
1695+ Other ->
1696+ Other
1697+ end .
16561698
16571699recv_status (# conn_data {parser = Parser , buffer = Buffer } = Data ) ->
16581700 case hackney_http :execute (Parser , Buffer ) of
0 commit comments