This commit is contained in:
mmacia committed 2026-05-26 21:10:54 +00:00
1 parent 8908a6adf0
commit fd317f2af4
1 file changed
+40 -4
+40 -4
View File
@@ -2,9 +2,44 @@ defmodule FeedReader.Transport do
@moduledoc """ @moduledoc """
Injectable HTTP transport boundary used by remote feed operations. Injectable HTTP transport boundary used by remote feed operations.
Implementations return raw response chunks as an enumerable. The response is Implementations return raw response chunks as an enumerable. They must not
intentionally not decompressed here; the download layer owns decoded-body automatically decompress the response: the downloader owns decompression,
checksums and decompression. decoded-byte accounting, and checksums. `final_url` must include redirects;
a 304 response has an empty body and is returned without downloading content.
`request/2` receives the full option list passed to the remote downloader.
The default `FeedReader.ReqTransport` recognizes these transport options:
- `:http_client` — module implementing this behaviour, selected by
`Transport.request/2`; defaults to `FeedReader.ReqTransport`. The selected
module receives the full options list, including this option.
- `:http_options` — Req request options forwarded to `Req.run/2`. The transport
enforces streamed raw bodies, disables automatic decompression, and sets the
retry and redirect options described below; redirects are limited to five.
A custom `:headers` list replaces the defaults. Otherwise, headers default to
`user-agent: feedex/1` and `accept-encoding: gzip, deflate, identity`.
- `:previous_etag` — if binary, sent verbatim as `If-None-Match` on the
GET request. It replaces any existing `If-None-Match` header. A 304 response
returns `{:ok, response}` with `status: 304` and `body: []`; no HEAD request
is made.
- `:http_retry_attempts` — non-negative number of retries after the initial
request; defaults to `5`. The Req transport retries status 400 and 500 and
most transport errors. TLS alerts and redirect failures are terminal.
- `:retry_delay` — non-negative delay in milliseconds or an arity-one Req
retry-delay function; defaults to `0`. Invalid values use the default.
The downloader also passes these recognized options through to custom
transports, but consumes them itself rather than using them to configure HTTP:
- `:previous_checksum` — decoded-body checksum used for the unchanged result.
- `:max_compressed_bytes` — maximum total raw response bytes, default 50 MiB.
- `:max_decoded_bytes` — maximum total decompressed bytes, default 50 MiB.
- `:max_temp_bytes` — maximum bytes written to the owned temporary file,
default 50 MiB.
- `:max_expansion_ratio` — maximum decoded-to-compressed byte ratio, default
`100`.
Other options remain available to injected transport implementations.
""" """
@type response :: %{ @type response :: %{
@@ -14,10 +49,11 @@ defmodule FeedReader.Transport do
required(:body) => Enumerable.t() required(:body) => Enumerable.t()
} }
@doc "Performs a GET and returns raw response chunks and response metadata."
@callback request(url :: String.t(), opts :: keyword()) :: @callback request(url :: String.t(), opts :: keyword()) ::
{:ok, response()} | {:error, term()} {:ok, response()} | {:error, term()}
@doc "Dispatches a request to the configured or default HTTP client." @doc "Dispatches the request to `:http_client`, or `FeedReader.ReqTransport` by default."
@spec request(String.t(), keyword()) :: {:ok, response()} | {:error, term()} @spec request(String.t(), keyword()) :: {:ok, response()} | {:error, term()}
def request(url, opts \\ []) do def request(url, opts \\ []) do
client = Keyword.get(opts, :http_client, FeedReader.ReqTransport) client = Keyword.get(opts, :http_client, FeedReader.ReqTransport)