Current section

40 Versions

Jump to

Compare versions

6 files changed
+63 additions
-17 deletions
  @@ -1,5 +1,8 @@
1 1 # Changelog
2 2
3 + ## v0.6.0 (2020-12-15)
4 + * Add ability to stream the request body for HTTP/1.x requests.
5 +
3 6 ## v0.5.2 (2020-11-10)
4 7 * Fix deprecation in nimble_options.
  @@ -39,4 +39,4 @@
39 39 {<<"optional">>,false},
40 40 {<<"repository">>,<<"hexpm">>},
41 41 {<<"requirement">>,<<"~> 0.4">>}]]}.
42 - {<<"version">>,<<"0.5.2">>}.
42 + {<<"version">>,<<"0.6.0">>}.
  @@ -165,6 +165,10 @@ defmodule Finch do
165 165
166 166 @doc """
167 167 Builds an HTTP request to be sent with `request/3` or `stream/4`.
168 +
169 + When making HTTP/1.x requests, it is possible to send the request body in a streaming fashion.
170 + In order to do so, the `body` parameter needs to take form of a tuple `{:stream, body_stream}`,
171 + where `body_stream` is a `Stream`. This feature is not yet supported for HTTP/2 requests.
168 172 """
169 173 @spec build(Request.method(), Request.url(), Request.headers(), Request.body()) :: Request.t()
170 174 defdelegate build(method, url, headers \\ [], body \\ nil), to: Request
  @@ -108,29 +108,55 @@ defmodule Finch.Conn do
108 108
109 109 start_time = Telemetry.start(:request, metadata, extra_measurements)
110 110
111 - case HTTP1.request(conn.mint, req.method, full_path, req.headers, req.body) do
111 + case HTTP1.request(conn.mint, req.method, full_path, req.headers, stream_or_body(req.body)) do
112 112 {:ok, mint, ref} ->
113 - Telemetry.stop(:request, start_time, metadata, extra_measurements)
114 - start_time = Telemetry.start(:response, metadata, extra_measurements)
115 -
116 - case receive_response([], acc, fun, mint, ref, receive_timeout) do
117 - {:ok, mint, acc} ->
118 - Telemetry.stop(:response, start_time, metadata, extra_measurements)
119 - {:ok, %{conn | mint: mint}, acc}
113 + case maybe_stream_request_body(mint, ref, req.body, receive_timeout) do
114 + {:ok, mint} ->
115 + Telemetry.stop(:request, start_time, metadata, extra_measurements)
116 + start_time = Telemetry.start(:response, metadata, extra_measurements)
117 + response = receive_response([], acc, fun, mint, ref, receive_timeout)
118 + handle_response(response, conn, metadata, start_time, extra_measurements)
120 119
121 120 {:error, mint, error} ->
122 - metadata = Map.put(metadata, :error, error)
123 - Telemetry.stop(:response, start_time, metadata, extra_measurements)
124 - {:error, %{conn | mint: mint}, error}
121 + handle_request_error(
122 + conn,
123 + mint,
124 + error,
125 + metadata,
126 + start_time,
127 + extra_measurements
128 + )
125 129 end
126 130
127 131 {:error, mint, error} ->
128 - metadata = Map.put(metadata, :error, error)
129 - Telemetry.stop(:request, start_time, metadata, extra_measurements)
130 - {:error, %{conn | mint: mint}, error}
132 + handle_request_error(conn, mint, error, metadata, start_time, extra_measurements)
131 133 end
132 134 end
133 135
136 + defp stream_or_body({:stream, _}), do: :stream
137 + defp stream_or_body(body), do: body
138 +
139 + defp handle_request_error(conn, mint, error, metadata, start_time, extra_measurements) do
140 + metadata = Map.put(metadata, :error, error)
141 + Telemetry.stop(:request, start_time, metadata, extra_measurements)
142 + {:error, %{conn | mint: mint}, error}
143 + end
144 +
145 + defp maybe_stream_request_body(mint, ref, {:stream, stream}, _timeout) do
146 + with {:ok, mint} <- stream_request_body(mint, ref, stream) do
147 + HTTP1.stream_request_body(mint, ref, :eof)
148 + end
149 + end
150 +
151 + defp maybe_stream_request_body(mint, _, _, _), do: {:ok, mint}
152 +
153 + defp stream_request_body(mint, ref, stream) do
154 + Enum.reduce_while(stream, {:ok, mint}, fn
155 + chunk, {:ok, mint} -> {:cont, HTTP1.stream_request_body(mint, ref, chunk)}
156 + _chunk, error -> {:halt, error}
157 + end)
158 + end
159 +
134 160 def close(%{mint: nil} = conn), do: conn
135 161
136 162 def close(conn) do
  @@ -138,6 +164,19 @@ defmodule Finch.Conn do
138 164 %{conn | mint: mint}
139 165 end
140 166
167 + defp handle_response(response, conn, metadata, start_time, extra_measurements) do
168 + case response do
169 + {:ok, mint, acc} ->
170 + Telemetry.stop(:response, start_time, metadata, extra_measurements)
171 + {:ok, %{conn | mint: mint}, acc}
172 +
173 + {:error, mint, error} ->
174 + metadata = Map.put(metadata, :error, error)
175 + Telemetry.stop(:response, start_time, metadata, extra_measurements)
176 + {:error, %{conn | mint: mint}, error}
177 + end
178 + end
179 +
141 180 defp receive_response([], acc, fun, mint, ref, timeout) do
142 181 case HTTP1.recv(mint, 0, timeout) do
143 182 {:ok, mint, entries} ->
  @@ -47,7 +47,7 @@ defmodule Finch.Request do
47 47 @typedoc """
48 48 Optional request body.
49 49 """
50 - @type body() :: iodata() | nil
50 + @type body() :: iodata() | {:stream, Enumerable.t()} | nil
51 51
52 52 @type t :: %Finch.Request{}
Loading more files…