Packages
req
0.3.3
0.6.3
0.6.2
0.6.1
0.6.0
0.5.18
0.5.17
0.5.16
0.5.15
0.5.14
0.5.13
0.5.12
0.5.11
0.5.10
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.14
0.4.13
0.4.12
0.4.11
0.4.10
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.12
0.3.11
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.2
0.2.1
0.2.0
0.1.2
0.1.1
0.1.0
Req is a batteries-included HTTP client for Elixir.
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/req.ex
defmodule Req do
@moduledoc """
The high-level API.
Req is composed of three main pieces:
* `Req` - the high-level API (you're here!)
* `Req.Request` - the low-level API and the request struct
* `Req.Steps` - the collection of built-in steps
The high-level API is what most users of Req will use most of the time.
## Examples
Making a GET request with `Req.get!/1`:
iex> Req.get!("https://api.github.com/repos/elixir-lang/elixir").body["description"]
"Elixir is a dynamic, functional language designed for building scalable and maintainable applications"
Same, but by explicitly building request struct first:
iex> req = Req.new(base_url: "https://api.github.com")
iex> Req.get!(req, url: "/repos/elixir-lang/elixir").body["description"]
"Elixir is a dynamic, functional language designed for building scalable and maintainable applications"
Making a POST request with `Req.post!/2`:
iex> Req.post!("https://httpbin.org/post", form: [comments: "hello!"]).body["form"]
%{"comments" => "hello!"}
"""
@type url() :: URI.t() | String.t()
@doc """
Returns a new request struct with built-in steps.
See `request/1` for a list of available options. See `Req.Request` module documentation
for more information on the underlying request struct.
## Examples
iex> req = Req.new(url: "https://elixir-lang.org")
iex> req.method
:get
iex> URI.to_string(req.url)
"https://elixir-lang.org"
"""
@spec new(options :: keyword()) :: Req.Request.t()
def new(options \\ []) do
options = Keyword.merge(default_options(), options)
{plugins, options} = Keyword.pop(options, :plugins, [])
%Req.Request{
registered_options:
MapSet.new([
:user_agent,
:compressed,
:range,
:http_errors,
:base_url,
:params,
:auth,
:form,
:json,
:compress_body,
:compressed,
:raw,
:decode_body,
:output,
:follow_redirects,
:location_trusted,
:max_redirects,
:retry,
:retry_delay,
:max_retries,
:cache,
:cache_dir,
:plug,
:finch,
:finch_request,
:connect_options,
:receive_timeout,
:pool_timeout,
:unix_socket
])
}
|> update(options)
|> Req.Request.prepend_request_steps(
put_user_agent: &Req.Steps.put_user_agent/1,
compressed: &Req.Steps.compressed/1,
encode_body: &Req.Steps.encode_body/1,
put_base_url: &Req.Steps.put_base_url/1,
auth: &Req.Steps.auth/1,
put_params: &Req.Steps.put_params/1,
put_range: &Req.Steps.put_range/1,
cache: &Req.Steps.cache/1,
put_plug: &Req.Steps.put_plug/1,
compress_body: &Req.Steps.compress_body/1
)
|> Req.Request.prepend_response_steps(
retry: &Req.Steps.retry/1,
follow_redirects: &Req.Steps.follow_redirects/1,
decompress_body: &Req.Steps.decompress_body/1,
decode_body: &Req.Steps.decode_body/1,
handle_http_errors: &Req.Steps.handle_http_errors/1,
output: &Req.Steps.output/1
)
|> Req.Request.prepend_error_steps(retry: &Req.Steps.retry/1)
|> run_plugins(plugins)
end
@doc """
Updates a request struct.
See `request/1` for a list of available options. See `Req.Request` module documentation
for more information on the underlying request struct.
## Examples
iex> req = Req.new(base_url: "https://httpbin.org")
iex> req = Req.update(req, auth: {"alice", "secret"})
iex> req.options
%{auth: {"alice", "secret"}, base_url: "https://httpbin.org"}
Passing `:headers` will automatically encode and merge them:
iex> req = Req.new(headers: [point_x: 1])
iex> req = Req.update(req, headers: [point_y: 2])
iex> req.headers
[{"point-x", "1"}, {"point-y", "2"}]
"""
@spec update(Req.Request.t(), options :: keyword()) :: Req.Request.t()
def update(%Req.Request{} = request, options) when is_list(options) do
request_option_names = [:method, :url, :headers, :body, :adapter]
{request_options, options} = Keyword.split(options, request_option_names)
registered =
MapSet.union(
request.registered_options,
MapSet.new(request_option_names)
)
Req.Request.validate_options(options, registered)
request_options =
if request_options[:headers] do
update_in(request_options[:headers], &encode_headers/1)
else
request_options
end
request =
Map.merge(request, Map.new(request_options), fn
:url, _, url ->
URI.parse(url)
:headers, old, new ->
old ++ new
_, _, value ->
value
end)
request = update_in(request.options, &Map.merge(&1, Map.new(options)))
if request.options[:output] do
update_in(request.options, &Map.put(&1, :decode_body, false))
else
request
end
end
@doc """
Makes a GET request.
See `request/1` for a list of supported options.
## Examples
With URL:
iex> Req.get!("https://api.github.com/repos/elixir-lang/elixir").body["description"]
"Elixir is a dynamic, functional language designed for building scalable and maintainable applications"
With request struct:
iex> req = Req.new(base_url: "https://api.github.com")
iex> Req.get!(req, url: "/repos/elixir-lang/elixir").status
200
"""
@spec get!(url() | Req.Request.t(), options :: keyword()) :: Req.Response.t()
def get!(url_or_request, options \\ []) do
case get(url_or_request, options) do
{:ok, response} -> response
{:error, exception} -> raise exception
end
end
@doc """
Makes a GET request.
See `request/1` for a list of supported options.
## Examples
With URL:
iex> {:ok, res} = Req.get("https://api.github.com/repos/elixir-lang/elixir")
iex> res.body["description"]
"Elixir is a dynamic, functional language designed for building scalable and maintainable applications"
With request struct:
iex> req = Req.new(base_url: "https://api.github.com")
iex> {:ok, res} = Req.get(req, url: "/repos/elixir-lang/elixir")
iex> res.status
200
"""
@spec get(url() | Req.Request.t(), options :: keyword()) ::
{:ok, Req.Response.t()} | {:error, Exception.t()}
def get(url_or_request, options \\ [])
def get(%Req.Request{} = request, options) do
request(%{request | method: :get}, options)
end
def get(url, options) do
request([method: :get, url: URI.parse(url)] ++ options)
end
@doc """
Makes a HEAD request.
See `request/1` for a list of supported options.
## Examples
With URL:
iex> Req.head!("https://httpbin.org/status/201").status
201
With request struct:
iex> req = Req.new(base_url: "https://httpbin.org")
iex> Req.head!(req, url: "/status/201").status
201
"""
@spec head!(url() | Req.Request.t(), options :: keyword()) :: Req.Response.t()
def head!(url_or_request, options \\ []) do
case head(url_or_request, options) do
{:ok, response} -> response
{:error, exception} -> raise exception
end
end
@doc """
Makes a HEAD request.
See `request/1` for a list of supported options.
## Examples
With URL:
iex> {:ok, res} = Req.head("https://httpbin.org/status/201")
iex> res.status
201
With request struct:
iex> req = Req.new(base_url: "https://httpbin.org")
iex> {:ok, res} = Req.head(req, url: "/status/201")
iex> res.status
201
"""
@spec head(url() | Req.Request.t(), options :: keyword()) :: Req.Response.t()
def head(url_or_request, options \\ [])
def head(%Req.Request{} = request, options) do
request(%{request | method: :head}, options)
end
def head(url, options) do
request([method: :head, url: URI.parse(url)] ++ options)
end
@doc """
Makes a POST request.
See `request/1` for a list of supported options.
## Examples
With URL:
iex> Req.post!("https://httpbin.org/anything", body: "hello!").body["data"]
"hello!"
iex> Req.post!("https://httpbin.org/anything", form: [x: 1]).body["form"]
%{"x" => "1"}
iex> Req.post!("https://httpbin.org/anything", json: %{x: 2}).body["json"]
%{"x" => 2}
With request struct:
iex> req = Req.new(url: "https://httpbin.org/anything")
iex> Req.post!(req, body: "hello!").body["data"]
"hello!"
"""
@spec post!(url() | Req.Request.t(), options :: keyword()) :: Req.Response.t()
def post!(url_or_request, options \\ []) do
if Keyword.keyword?(options) do
case post(url_or_request, options) do
{:ok, response} -> response
{:error, exception} -> raise exception
end
else
case options do
{:form, data} ->
IO.warn(
"Req.post!(url, {:form, data}) is deprecated in favour of " <>
"Req.post!(url, form: data)"
)
request!(method: :post, url: URI.parse(url_or_request), form: data)
{:json, data} ->
IO.warn(
"Req.post!(url, {:json, data}) is deprecated in favour of " <>
"Req.post!(url, json: data)"
)
request!(method: :post, url: URI.parse(url_or_request), json: data)
data ->
IO.warn("Req.post!(url, body) is deprecated in favour of Req.post!(url, body: body)")
request!(method: :post, url: URI.parse(url_or_request), body: data)
end
end
end
@doc false
def post!(url, body, options) do
case body do
{:form, data} ->
IO.warn(
"Req.post!(url, {:form, data}, options) is deprecated in favour of " <>
"Req.post!(url, [form: data] ++ options)"
)
request!([method: :post, url: URI.parse(url), form: data] ++ options)
{:json, data} ->
IO.warn(
"Req.post!(url, {:json, data}) is deprecated in favour of " <>
"Req.post!(url, [json: data] ++ options)"
)
request!([method: :post, url: URI.parse(url), json: data] ++ options)
data ->
IO.warn(
"Req.post!(url, body) is deprecated in favour of " <>
"Req.post!(url, [body: body] ++ options)"
)
request!([method: :post, url: URI.parse(url), body: data] ++ options)
end
end
@doc """
Makes a POST request.
See `request/1` for a list of supported options.
## Examples
With URL:
iex> {:ok, res} = Req.post("https://httpbin.org/anything", body: "hello!")
iex> res.body["data"]
"hello!"
iex> {:ok, res} = Req.post("https://httpbin.org/anything", form: [x: 1])
iex> res.body["form"]
%{"x" => "1"}
iex> {:ok, res} = Req.post("https://httpbin.org/anything", json: %{x: 2})
iex> res.body["json"]
%{"x" => 2}
With request struct:
iex> req = Req.new(url: "https://httpbin.org/anything")
iex> {:ok, res} = Req.post(req, body: "hello!")
iex> res.body["data"]
"hello!"
"""
@spec post(url() | Req.Request.t(), options :: keyword()) ::
{:ok, Req.Response.t()} | {:error, Exception.t()}
def post(url_or_request, options \\ [])
def post(%Req.Request{} = request, options) do
request(%{request | method: :post}, options)
end
def post(url, options) do
request([method: :post, url: URI.parse(url)] ++ options)
end
@doc """
Makes a PUT request.
See `request/1` for a list of supported options.
## Examples
With URL:
iex> Req.put!("https://httpbin.org/anything", body: "hello!").body["data"]
"hello!"
With request struct:
iex> req = Req.new(url: "https://httpbin.org/anything")
iex> Req.put!(req, body: "hello!").body["data"]
"hello!"
"""
@spec put!(url() | Req.Request.t(), options :: keyword()) :: Req.Response.t()
def put!(url_or_request, options \\ []) do
case put(url_or_request, options) do
{:ok, response} -> response
{:error, exception} -> raise exception
end
end
@doc false
def put!(%URI{} = url, {type, _} = body, options) when type in [:form, :json] do
IO.warn(
"Req.put!(url, {:#{type}, #{type}}, options) is deprecated in favour of " <>
"Req.put!(url, [#{type}: #{type}] ++ options)"
)
request!([method: :put, url: url, body: body] ++ options)
end
@doc """
Makes a PUT request.
See `request/1` for a list of supported options.
## Examples
With URL:
iex> {:ok, res} = Req.put("https://httpbin.org/anything", body: "hello!")
iex> res.body["data"]
"hello!"
With request struct:
iex> req = Req.new(url: "https://httpbin.org/anything")
iex> {:ok, res} = Req.put(req, body: "hello!")
iex> res.body["data"]
"hello!"
"""
@spec put(url() | Req.Request.t(), options :: keyword()) ::
{:ok, Req.Response.t()} | {:error, Exception.t()}
def put(url_or_request, options \\ [])
def put(%Req.Request{} = request, options) do
request(%{request | method: :put}, options)
end
def put(url, options) do
if Keyword.keyword?(options) do
request([method: :put, url: URI.parse(url)] ++ options)
else
IO.warn("Req.put!(url, body) is deprecated in favour of Req.put!(url, body: body)")
request(url: URI.parse(url), body: options)
end
end
@doc """
Makes a PATCH request.
See `request/1` for a list of supported options.
## Examples
With URL:
iex> Req.patch!("https://httpbin.org/anything", body: "hello!").body["data"]
"hello!"
With request struct:
iex> req = Req.new(url: "https://httpbin.org/anything")
iex> Req.patch!(req, body: "hello!").body["data"]
"hello!"
"""
@spec patch!(url() | Req.Request.t(), options :: keyword()) :: Req.Response.t()
def patch!(url_or_request, options \\ []) do
case patch(url_or_request, options) do
{:ok, response} -> response
{:error, exception} -> raise exception
end
end
@doc """
Makes a PATCH request.
See `request/1` for a list of supported options.
## Examples
With URL:
iex> {:ok, res} = Req.patch("https://httpbin.org/anything", body: "hello!")
iex> res.body["data"]
"hello!"
With request struct:
iex> req = Req.new(url: "https://httpbin.org/anything")
iex> {:ok, res} = Req.patch(req, body: "hello!")
iex> res.body["data"]
"hello!"
"""
@spec patch(url() | Req.Request.t(), options :: keyword()) ::
{:ok, Req.Response.t()} | {:error, Exception.t()}
def patch(url_or_request, options \\ [])
def patch(%Req.Request{} = request, options) do
request(%{request | method: :patch}, options)
end
def patch(url, options) do
request([method: :patch, url: url] ++ options)
end
@doc """
Makes a DELETE request.
See `request/1` for a list of supported options.
## Examples
With URL:
iex> Req.delete!("https://httpbin.org/anything").body["method"]
"DELETE"
With request struct:
iex> req = Req.new(url: "https://httpbin.org/anything")
iex> Req.delete!(req).body["method"]
"DELETE"
"""
@spec delete!(url() | Req.Request.t(), options :: keyword()) :: Req.Response.t()
def delete!(url_or_request, options \\ []) do
case delete(url_or_request, options) do
{:ok, response} -> response
{:error, exception} -> raise exception
end
end
@doc """
Makes a DELETE request.
See `request/1` for a list of supported options.
## Examples
With URL:
iex> {:ok, res} = Req.delete("https://httpbin.org/anything")
iex> res.body["method"]
"DELETE"
With request struct:
iex> req = Req.new(url: "https://httpbin.org/anything")
iex> {:ok, res} = Req.delete(req)
iex> res.body["method"]
"DELETE"
"""
@spec delete(url() | Req.Request.t(), options :: keyword()) ::
{:ok, Req.Response.t()} | {:error, Exception.t()}
def delete(url_or_request, options \\ [])
def delete(%Req.Request{} = request, options) do
request(%{request | method: :delete}, options)
end
def delete(url, options) do
request([method: :delete, url: url] ++ options)
end
@doc """
Makes an HTTP request.
`request/1` and `request/2` functions give three ways of making requests:
1. With a list of options, for example:
```
iex> Req.request(url: url)
```
2. With a request struct, for example:
```
iex> Req.new(url: url) |> Req.request()
```
3. With a request struct and more options, for example:
```
iex> Req.new(base_url: base_url) |> Req.request(url: url)
```
This function as well as all the other ones in this module accept the same set of options described below.
## Options
Basic request options:
* `:method` - the request method, defaults to `:get`.
* `:url` - the request URL.
* `:headers` - the request headers.
The headers are automatically encoded using these rules:
* atom header names are turned into strings, replacing `-` with `_`. For example,
`:user_agent` becomes `"user-agent"`
* string header names are left as is. Because header keys are case-insensitive
in both HTTP/1.1 and HTTP/2, it is recommended for header keys to be in
lowercase, to avoid sending duplicate keys in a request.
* `NaiveDateTime` and `DateTime` header values are encoded as "HTTP date". Otherwise,
the header value is encoded with `String.Chars.to_string/1`.
If you set `:headers` options both in `Req.new/1` and `request/2`, the header lists are merged.
* `:body` - the request body.
Additional URL options:
* `:base_url` - if set, the request URL is prepended with this base URL (via
[`put_base_url`](`Req.Steps.put_base_url/1`) step).
* `:params` - if set, appends parameters to the request query string (via
[`put_params`](`Req.Steps.put_params/1`) step).
Authentication options:
* `:auth` - sets request authentication (via [`auth`](`Req.Steps.auth/1`) step).
Request body options:
* `:form` - if set, encodes the request body as form data ([`encode_body`](`Req.Steps.encode_body/1`) step).
* `:json` - if set, encodes the request body as JSON ([`encode_body`](`Req.Steps.encode_body/1`) step).
* `:compress_body` - if set to `true`, compresses the request body using gzip (via [`compress_body`](`Req.Steps.compress_body/1`) step).
Defaults to `false`.
Response body options:
* `:compressed` - if set to `true`, asks the server to return compressed response.
(via [`compressed`](`Req.Steps.compressed/1`) step). Defaults to `true`.
* `:raw` - if set to `true`, disables automatic body decompression
([`decompress_body`](`Req.Steps.decompress_body/1`) step) and decoding
([`decode_body`](`Req.Steps.decode_body/1`) step). Defaults to `false`.
* `:decode_body` - if set to `false`, disables automatic response body decoding.
Defaults to `true`.
* `:output` - if set, writes the response body to a file (via
[`output`](`Req.Steps.output/1`) step). Can be set to a string path or an atom
`:remote_name` which would use the remote name as the filename in the current working
directory. Once the file is written, the response body is replaced with `""`.
Setting `:output` also sets the `decode_body: false` option to prevent decoding the
response before writing it to the file.
Response redirect options ([`follow_redirects`](`Req.Steps.follow_redirects/1`) step):
* `:follow_redirects` - if set to `false`, disables automatic response redirects. Defaults to `true`.
* `:location_trusted` - by default, authorization credentials are only sent
on redirects with the same host, scheme and port. If `:location_trusted` is set to `true`, credentials
will be sent to any host.
* `:max_redirects` - the maximum number of redirects, defaults to `10`.
Retry options ([`retry`](`Req.Steps.retry/1`) step):
* `:retry`: can be set to: `:safe` (default) to only retry GET/HEAD requests on HTTP 408/5xx
responses or exceptions, `false` to never retry, and `fun` - a 1-arity function that accepts
either a `Req.Response` or an exception struct and returns boolean whether to retry
* `:retry_delay` - a function that receives the retry count (starting at 0) and returns the delay, the
number of milliseconds to sleep before making another attempt.
Defaults to a simple exponential backoff: 1s, 2s, 4s, 8s, ...
* `:max_retries` - maximum number of retry attempts, defaults to `3` (for a total of `4`
requests to the server, including the initial one.)
Caching options ([`cache`](`Req.Steps.cache/1`) step):
* `:cache` - if `true`, performs HTTP caching. Defaults to `false`.
* `:cache_dir` - the directory to store the cache, defaults to `<user_cache_dir>/req`
(see: `:filename.basedir/3`)
Request adapters:
* `:adapter` - adapter to use to make the actual HTTP request. See `:adapter` field description
in the `Req.Request` module documentation for more information. Defaults to calling [`run_finch`](`Req.Steps.run_finch/1`).
* `:plug` - if set, calls the given Plug instead of making an HTTP request over the network (via [`put_plug`](`Req.Steps.put_plug/1`) step).
Finch options ([`run_finch`](`Req.Steps.run_finch/1`) step)
* `:finch` - the Finch pool to use. Defaults to pool automatically started by `Req`.
* `:connect_options` - dynamically starts (or re-uses already started) Finch pool with
the given connection options:
* `:timeout` - socket connect timeout in milliseconds, defaults to `30_000`.
* `:protocol` - the HTTP protocol to use, defaults to `:http1`.
* `:hostname` - Mint explicit hostname
* `:transport_opts` - Mint transport options
* `:proxy_headers` - Mint proxy headers
* `:proxy` - Mint HTTP/1 proxy settings, a `{schema, address, port, options}` tuple.
* `:client_settings` - Mint HTTP/2 client settings
* `:pool_timeout` - pool checkout timeout in milliseconds, defaults to `5000`.
* `:receive_timeout` - socket receive timeout in milliseconds, defaults to `15_000`.
* `:unix_socket` - if set, connect through the given UNIX domain socket
* `:finch_request` - a function to modify the built Finch request before execution. This function takes a
Finch request and returns a Finch request. If not provided, the finch request will not be modified
## Examples
With options keywords list:
iex> {:ok, response} = Req.request(url: "https://api.github.com/repos/elixir-lang/elixir")
iex> response.status
200
iex> response.body["description"]
"Elixir is a dynamic, functional language designed for building scalable and maintainable applications"
With request struct:
iex> req = Req.new(url: "https://api.github.com/repos/elixir-lang/elixir")
iex> {:ok, response} = Req.request(req)
iex> response.status
200
With request struct and options:
iex> req = Req.new(base_url: "https://api.github.com")
iex> {:ok, response} = Req.request(req, url: "/repos/elixir-lang/elixir")
iex> response.status
200
With mock adapter:
iex> adapter = fn request ->
...> response = %Req.Response{status: 200, body: "it works!"}
...> {request, response}
...> end
iex>
iex> {:ok, response} = Req.request(adapter: adapter, url: "http://example")
iex> response.body
"it works!"
"""
@spec request(Req.Request.t() | keyword()) :: {:ok, Req.Response.t()} | {:error, Exception.t()}
def request(request_or_options)
def request(%Req.Request{} = request) do
request(request, [])
end
def request(options) do
request(Req.new(options), [])
end
@doc """
Makes an HTTP request.
See `request/1` for more information.
"""
@spec request(Req.Request.t(), options :: keyword()) ::
{:ok, Req.Response.t()} | {:error, Exception.t()}
def request(request, options) when is_list(options) do
{plugins, options} = Keyword.pop(options, :plugins, [])
request
|> Req.update(options)
|> run_plugins(plugins)
|> Req.Request.run()
end
@doc false
def request(method, url, options) do
IO.warn(
"Req.request(method, url, options) is deprecated in favour of " <>
"Req.request!([method: method, url: url] ++ options)"
)
request([method: method, url: URI.parse(url)] ++ options)
end
@doc """
Makes an HTTP request and returns a response or raises an error.
See `request/1` for more information.
## Examples
iex> Req.request!(url: "https://api.github.com/repos/elixir-lang/elixir").status
200
"""
@spec request!(Req.Request.t() | keyword()) :: Req.Response.t()
def request!(request_or_options) do
case request(request_or_options) do
{:ok, response} -> response
{:error, exception} -> raise exception
end
end
@doc """
Makes an HTTP request and returns a response or raises an error.
See `request/1` for more information.
## Examples
iex> req = Req.new(base_url: "https://api.github.com")
iex> Req.request!(req, url: "/repos/elixir-lang/elixir").status
200
"""
@spec request!(Req.Request.t(), options :: keyword()) :: Req.Response.t()
def request!(request, options) do
case request(request, options) do
{:ok, response} -> response
{:error, exception} -> raise exception
end
end
@doc """
Returns default options.
See `default_options/1` for more information.
"""
@spec default_options() :: keyword()
def default_options() do
Application.get_env(:req, :default_options, [])
end
@doc """
Sets default options for `Req.new/1`.
Avoid setting default options in libraries as they are global.
## Examples
iex> Req.default_options(base_url: "https://httpbin.org")
iex> Req.get!("/statuses/201").status
201
iex> Req.new() |> Req.get!(url: "/statuses/201").status
201
"""
@spec default_options(keyword()) :: :ok
def default_options(options) do
Application.put_env(:req, :default_options, options)
end
defp encode_headers(headers) do
for {name, value} <- headers do
name =
case name do
atom when is_atom(atom) ->
atom |> Atom.to_string() |> String.replace("_", "-")
binary when is_binary(binary) ->
binary
end
value =
case value do
%NaiveDateTime{} = naive_datetime ->
Req.Steps.format_http_datetime(naive_datetime)
%DateTime{} = datetime ->
datetime |> DateTime.shift_zone!("Etc/UTC") |> Req.Steps.format_http_datetime()
_ ->
String.Chars.to_string(value)
end
{name, value}
end
end
# Plugins support is experimental, undocumented, and likely won't make the new release.
defp run_plugins(request, [plugin | rest]) when is_atom(plugin) do
if Code.ensure_loaded?(plugin) and function_exported?(plugin, :attach, 1) do
run_plugins(plugin.attach(request), rest)
else
run_plugins(plugin.run(request), rest)
end
end
defp run_plugins(request, [plugin | rest]) when is_function(plugin, 1) do
run_plugins(plugin.(request), rest)
end
defp run_plugins(request, []) do
request
end
@doc false
@deprecated "Manually build Req.Request struct instead"
def build(method, url, options \\ []) do
%Req.Request{
method: method,
url: URI.parse(url),
headers: Keyword.get(options, :headers, []),
body: Keyword.get(options, :body, "")
}
end
end