@@ -123,6 +123,7 @@ defmodule Mint.HTTP do
123123
124124 alias Mint . { Types , TunnelProxy , UnsafeProxy }
125125 alias Mint.Core . { Transport , Util }
126+ require Util
126127
127128 @ behaviour Mint.Core.Conn
128129
@@ -872,6 +873,100 @@ defmodule Mint.HTTP do
872873 | { :error , t ( ) , Types . error ( ) , [ Types . response ( ) ] }
873874 def recv ( conn , byte_count , timeout ) , do: conn_module ( conn ) . recv ( conn , byte_count , timeout )
874875
876+ @ version Mix.Project . config ( ) [ :version ]
877+
878+ @ doc """
879+ Receives response from the socket in a blocking way.
880+
881+ This function receives a response for the request identified by `request_ref` on the connection
882+ `conn`.
883+
884+ `timeout` is the maximum time to wait before returning an error.
885+
886+ This function is a convenience for repeatedly calling `Mint.HTTP.recv/3`. The result is either:
887+
888+ * `{:ok, conn, response}` where `conn` is the updated connection and `response` is a map
889+ with the following keys:
890+
891+ * `:status` - HTTP response status, an integer.
892+
893+ * `:headers` - HTTP response headers, a list of tuples `{header_name, header_value}`.
894+
895+ * `:body` - HTTP response body, a binary.
896+
897+ * `{:error, conn, reason}` where `conn` is the updated connection and `reason` is the cause
898+ of the error. It is important to store the returned connection over the old connection in
899+ case of errors too, because the state of the connection might change when there are errors
900+ as well. An error when sending a request **does not** necessarily mean that the connection
901+ is closed. Use `open?/1` to verify that the connection is open.
902+
903+ ## Examples
904+
905+ iex> {:ok, conn} = Mint.HTTP.connect(:https, "httpbin.org", 443, mode: :passive)
906+ iex> {:ok, conn, request_ref} = Mint.HTTP.request(conn, "GET", "/user-agent", [], nil)
907+ iex> {:ok, _conn, response} = Mint.HTTP.recv_response(conn, request_ref, 5000)
908+ iex> response
909+ %{
910+ status: 200,
911+ headers: [{"date", ...}, ...],
912+ body: "{\\ n \\ "user-agent\\ ": \\ "#{ @ version } \\ "\\ n}\\ n"
913+ }
914+ """
915+ @ spec recv_response ( t ( ) , Types . request_ref ( ) , timeout ( ) ) ::
916+ { :ok , t ( ) , response } | { :error , Types . error ( ) }
917+ when response: % {
918+ status: non_neg_integer ( ) ,
919+ headers: [ { binary ( ) , binary ( ) } ] ,
920+ body: binary ( )
921+ }
922+ def recv_response ( conn , ref , timeout )
923+ when is_reference ( ref ) and Util . is_timeout ( timeout ) do
924+ recv_response ( [ ] , % { status: nil , headers: [ ] , body: "" } , conn , ref , timeout )
925+ end
926+
927+ defp recv_response ( [ { :status , ref , status } | rest ] , acc , conn , ref , timeout ) do
928+ acc = put_in ( acc . status , status )
929+ recv_response ( rest , acc , conn , ref , timeout )
930+ end
931+
932+ defp recv_response ( [ { :headers , ref , headers } | rest ] , acc , conn , ref , timeout ) do
933+ acc = update_in ( acc . headers , & ( & 1 ++ headers ) )
934+ recv_response ( rest , acc , conn , ref , timeout )
935+ end
936+
937+ defp recv_response ( [ { :data , ref , data } | rest ] , acc , conn , ref , timeout ) do
938+ acc = update_in ( acc . body , & ( & 1 <> data ) )
939+ recv_response ( rest , acc , conn , ref , timeout )
940+ end
941+
942+ defp recv_response ( [ { :done , ref } | _rest ] , acc , conn , ref , _timeout ) do
943+ { :ok , conn , acc }
944+ end
945+
946+ defp recv_response ( [ { :error , ref , error } | _rest ] , _acc , conn , ref , _timeout ) do
947+ { :error , conn , error }
948+ end
949+
950+ # Ignore entries from other requests.
951+ defp recv_response ( [ _entry | rest ] , acc , conn , ref , timeout ) do
952+ recv_response ( rest , acc , conn , ref , timeout )
953+ end
954+
955+ defp recv_response ( [ ] , acc , conn , ref , timeout ) do
956+ start_time = System . monotonic_time ( :millisecond )
957+
958+ with { :ok , conn , entries } <- recv ( conn , 0 , timeout ) do
959+ timeout =
960+ if is_integer ( timeout ) do
961+ timeout - System . monotonic_time ( :millisecond ) - start_time
962+ else
963+ timeout
964+ end
965+
966+ recv_response ( entries , acc , conn , ref , timeout )
967+ end
968+ end
969+
875970 @ doc """
876971 Changes the mode of the underlying socket.
877972
0 commit comments