fixing connection leaks
This commit is contained in:
parent
5ca7b9f690
commit
73b1b3ff5e
@ -92,13 +92,13 @@ defmodule Mix.Tasks.Pleroma.Benchmark do
|
||||
"Without conn and without pool" => fn ->
|
||||
{:ok, %Tesla.Env{}} =
|
||||
Pleroma.HTTP.get("https://httpbin.org/stream-bytes/1500", [],
|
||||
adapter: [pool: :no_pool, receive_conn: false]
|
||||
adapter: [pool: :no_pool, reuse_conn: false]
|
||||
)
|
||||
end,
|
||||
"Without conn and with pool" => fn ->
|
||||
{:ok, %Tesla.Env{}} =
|
||||
Pleroma.HTTP.get("https://httpbin.org/stream-bytes/1500", [],
|
||||
adapter: [receive_conn: false]
|
||||
adapter: [reuse_conn: false]
|
||||
)
|
||||
end,
|
||||
"With reused conn and without pool" => fn ->
|
||||
|
@ -72,14 +72,17 @@ defmodule Pleroma.Gun.Conn do
|
||||
defp maybe_add_tls_opts(opts, %URI{scheme: "http"}), do: opts
|
||||
|
||||
defp maybe_add_tls_opts(opts, %URI{scheme: "https", host: host}) do
|
||||
charlist_host =
|
||||
host
|
||||
|> to_charlist()
|
||||
|> :idna.encode()
|
||||
|
||||
tls_opts = [
|
||||
verify: :verify_peer,
|
||||
cacertfile: CAStore.file_path(),
|
||||
depth: 20,
|
||||
reuse_sessions: false,
|
||||
verify_fun:
|
||||
{&:ssl_verify_hostname.verify_fun/3,
|
||||
[check_hostname: Pleroma.HTTP.Connection.format_host(host)]}
|
||||
verify_fun: {&:ssl_verify_hostname.verify_fun/3, [check_hostname: charlist_host]}
|
||||
]
|
||||
|
||||
tls_opts =
|
||||
@ -153,7 +156,7 @@ defmodule Pleroma.Gun.Conn do
|
||||
end
|
||||
|
||||
defp do_open(%URI{host: host, port: port} = uri, opts) do
|
||||
host = Pleroma.HTTP.Connection.parse_host(host)
|
||||
host = Pleroma.HTTP.Connection.format_host(host)
|
||||
|
||||
with {:ok, conn} <- Gun.open(host, port, opts),
|
||||
{:ok, _} <- Gun.await_up(conn, opts[:await_up_timeout]) do
|
||||
@ -169,7 +172,7 @@ defmodule Pleroma.Gun.Conn do
|
||||
end
|
||||
|
||||
defp destination_opts(%URI{host: host, port: port}) do
|
||||
host = Pleroma.HTTP.Connection.parse_host(host)
|
||||
host = Pleroma.HTTP.Connection.format_host(host)
|
||||
%{host: host, port: port}
|
||||
end
|
||||
|
||||
|
@ -1,41 +0,0 @@
|
||||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.HTTP.AdapterHelper do
|
||||
alias Pleroma.HTTP.Connection
|
||||
|
||||
@type proxy ::
|
||||
{Connection.host(), pos_integer()}
|
||||
| {Connection.proxy_type(), Connection.host(), pos_integer()}
|
||||
|
||||
@callback options(keyword(), URI.t()) :: keyword()
|
||||
@callback after_request(keyword()) :: :ok
|
||||
|
||||
@spec options(keyword(), URI.t()) :: keyword()
|
||||
def options(opts, _uri) do
|
||||
proxy = Pleroma.Config.get([:http, :proxy_url], nil)
|
||||
maybe_add_proxy(opts, format_proxy(proxy))
|
||||
end
|
||||
|
||||
@spec maybe_get_conn(URI.t(), keyword()) :: keyword()
|
||||
def maybe_get_conn(_uri, opts), do: opts
|
||||
|
||||
@spec after_request(keyword()) :: :ok
|
||||
def after_request(_opts), do: :ok
|
||||
|
||||
@spec format_proxy(String.t() | tuple() | nil) :: proxy() | nil
|
||||
def format_proxy(nil), do: nil
|
||||
|
||||
def format_proxy(proxy_url) do
|
||||
case Connection.parse_proxy(proxy_url) do
|
||||
{:ok, host, port} -> {host, port}
|
||||
{:ok, type, host, port} -> {type, host, port}
|
||||
_ -> nil
|
||||
end
|
||||
end
|
||||
|
||||
@spec maybe_add_proxy(keyword(), proxy() | nil) :: keyword()
|
||||
def maybe_add_proxy(opts, nil), do: opts
|
||||
def maybe_add_proxy(opts, proxy), do: Keyword.put_new(opts, :proxy, proxy)
|
||||
end
|
@ -1,82 +0,0 @@
|
||||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.HTTP.AdapterHelper.Gun do
|
||||
@behaviour Pleroma.HTTP.AdapterHelper
|
||||
|
||||
alias Pleroma.HTTP.AdapterHelper
|
||||
alias Pleroma.Pool.Connections
|
||||
|
||||
require Logger
|
||||
|
||||
@defaults [
|
||||
connect_timeout: 5_000,
|
||||
domain_lookup_timeout: 5_000,
|
||||
tls_handshake_timeout: 5_000,
|
||||
retry: 1,
|
||||
retry_timeout: 1000,
|
||||
await_up_timeout: 5_000
|
||||
]
|
||||
|
||||
@spec options(keyword(), URI.t()) :: keyword()
|
||||
def options(incoming_opts \\ [], %URI{} = uri) do
|
||||
proxy =
|
||||
Pleroma.Config.get([:http, :proxy_url])
|
||||
|> AdapterHelper.format_proxy()
|
||||
|
||||
config_opts = Pleroma.Config.get([:http, :adapter], [])
|
||||
|
||||
@defaults
|
||||
|> Keyword.merge(config_opts)
|
||||
|> add_scheme_opts(uri)
|
||||
|> AdapterHelper.maybe_add_proxy(proxy)
|
||||
|> maybe_get_conn(uri, incoming_opts)
|
||||
end
|
||||
|
||||
@spec after_request(keyword()) :: :ok
|
||||
def after_request(opts) do
|
||||
if opts[:conn] && opts[:body_as] != :chunks do
|
||||
Connections.checkout(opts[:conn], self(), :gun_connections)
|
||||
end
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
defp add_scheme_opts(opts, %{scheme: "http"}), do: opts
|
||||
|
||||
defp add_scheme_opts(opts, %{scheme: "https"}) do
|
||||
tls_opts = [
|
||||
log_level: :warning,
|
||||
session_lifetime: 6000,
|
||||
session_cache_client_max: 250
|
||||
]
|
||||
|
||||
opts
|
||||
|> Keyword.put(:certificates_verification, true)
|
||||
|> Keyword.put(:tls_opts, tls_opts)
|
||||
end
|
||||
|
||||
defp maybe_get_conn(adapter_opts, uri, incoming_opts) do
|
||||
{receive_conn?, opts} =
|
||||
adapter_opts
|
||||
|> Keyword.merge(incoming_opts)
|
||||
|> Keyword.pop(:receive_conn, true)
|
||||
|
||||
if Connections.alive?(:gun_connections) and receive_conn? do
|
||||
checkin_conn(uri, opts)
|
||||
else
|
||||
opts
|
||||
end
|
||||
end
|
||||
|
||||
defp checkin_conn(uri, opts) do
|
||||
case Connections.checkin(uri, :gun_connections) do
|
||||
nil ->
|
||||
opts
|
||||
|
||||
conn when is_pid(conn) ->
|
||||
Keyword.merge(opts, conn: conn, close_conn: false)
|
||||
end
|
||||
end
|
||||
end
|
@ -1,43 +0,0 @@
|
||||
defmodule Pleroma.HTTP.AdapterHelper.Hackney do
|
||||
@behaviour Pleroma.HTTP.AdapterHelper
|
||||
|
||||
@defaults [
|
||||
connect_timeout: 10_000,
|
||||
recv_timeout: 20_000,
|
||||
follow_redirect: true,
|
||||
force_redirect: true,
|
||||
pool: :federation
|
||||
]
|
||||
|
||||
@spec options(keyword(), URI.t()) :: keyword()
|
||||
def options(connection_opts \\ [], %URI{} = uri) do
|
||||
proxy = Pleroma.Config.get([:http, :proxy_url])
|
||||
|
||||
config_opts = Pleroma.Config.get([:http, :adapter], [])
|
||||
|
||||
@defaults
|
||||
|> Keyword.merge(config_opts)
|
||||
|> Keyword.merge(connection_opts)
|
||||
|> add_scheme_opts(uri)
|
||||
|> Pleroma.HTTP.AdapterHelper.maybe_add_proxy(proxy)
|
||||
end
|
||||
|
||||
defp add_scheme_opts(opts, %URI{scheme: "http"}), do: opts
|
||||
|
||||
defp add_scheme_opts(opts, %URI{scheme: "https", host: host}) do
|
||||
ssl_opts = [
|
||||
ssl_options: [
|
||||
# Workaround for remote server certificate chain issues
|
||||
partial_chain: &:hackney_connect.partial_chain/1,
|
||||
|
||||
# We don't support TLS v1.3 yet
|
||||
versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"],
|
||||
server_name_indication: to_charlist(host)
|
||||
]
|
||||
]
|
||||
|
||||
Keyword.merge(opts, ssl_opts)
|
||||
end
|
||||
|
||||
def after_request(_), do: :ok
|
||||
end
|
@ -7,100 +7,31 @@ defmodule Pleroma.HTTP.Connection do
|
||||
Configure Tesla.Client with default and customized adapter options.
|
||||
"""
|
||||
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.HTTP.AdapterHelper
|
||||
|
||||
require Logger
|
||||
|
||||
@defaults [pool: :federation]
|
||||
|
||||
@type ip_address :: ipv4_address() | ipv6_address()
|
||||
@type ipv4_address :: {0..255, 0..255, 0..255, 0..255}
|
||||
@type ipv6_address ::
|
||||
{0..65_535, 0..65_535, 0..65_535, 0..65_535, 0..65_535, 0..65_535, 0..65_535, 0..65_535}
|
||||
@type proxy_type() :: :socks4 | :socks5
|
||||
@type host() :: charlist() | ip_address()
|
||||
@type host() :: charlist() | ipv4_address() | ipv6_address()
|
||||
|
||||
@doc """
|
||||
Merge default connection & adapter options with received ones.
|
||||
Merge default connection & adapter options with received options.
|
||||
"""
|
||||
|
||||
@spec options(URI.t(), keyword()) :: keyword()
|
||||
def options(%URI{} = uri, opts \\ []) do
|
||||
@defaults
|
||||
|> pool_timeout()
|
||||
adapter = Application.get_env(:tesla, :adapter)
|
||||
|
||||
[pool: :federation]
|
||||
|> Keyword.merge(opts)
|
||||
|> adapter_helper().options(uri)
|
||||
|> adapter_options(uri, adapter)
|
||||
end
|
||||
|
||||
defp pool_timeout(opts) do
|
||||
{config_key, default} =
|
||||
if adapter() == Tesla.Adapter.Gun do
|
||||
{:pools, Config.get([:pools, :default, :timeout])}
|
||||
else
|
||||
{:hackney_pools, 10_000}
|
||||
end
|
||||
@spec format_host(String.t() | atom() | charlist()) :: host()
|
||||
def format_host(host) when is_list(host), do: host
|
||||
def format_host(host) when is_atom(host), do: to_charlist(host)
|
||||
|
||||
timeout = Config.get([config_key, opts[:pool], :timeout], default)
|
||||
|
||||
Keyword.merge(opts, timeout: timeout)
|
||||
end
|
||||
|
||||
@spec after_request(keyword()) :: :ok
|
||||
def after_request(opts), do: adapter_helper().after_request(opts)
|
||||
|
||||
defp adapter, do: Application.get_env(:tesla, :adapter)
|
||||
|
||||
defp adapter_helper do
|
||||
case adapter() do
|
||||
Tesla.Adapter.Gun -> AdapterHelper.Gun
|
||||
Tesla.Adapter.Hackney -> AdapterHelper.Hackney
|
||||
_ -> AdapterHelper
|
||||
end
|
||||
end
|
||||
|
||||
@spec parse_proxy(String.t() | tuple() | nil) ::
|
||||
{:ok, host(), pos_integer()}
|
||||
| {:ok, proxy_type(), host(), pos_integer()}
|
||||
| {:error, atom()}
|
||||
| nil
|
||||
|
||||
def parse_proxy(nil), do: nil
|
||||
|
||||
def parse_proxy(proxy) when is_binary(proxy) do
|
||||
with [host, port] <- String.split(proxy, ":"),
|
||||
{port, ""} <- Integer.parse(port) do
|
||||
{:ok, parse_host(host), port}
|
||||
else
|
||||
{_, _} ->
|
||||
Logger.warn("Parsing port failed #{inspect(proxy)}")
|
||||
{:error, :invalid_proxy_port}
|
||||
|
||||
:error ->
|
||||
Logger.warn("Parsing port failed #{inspect(proxy)}")
|
||||
{:error, :invalid_proxy_port}
|
||||
|
||||
_ ->
|
||||
Logger.warn("Parsing proxy failed #{inspect(proxy)}")
|
||||
{:error, :invalid_proxy}
|
||||
end
|
||||
end
|
||||
|
||||
def parse_proxy(proxy) when is_tuple(proxy) do
|
||||
with {type, host, port} <- proxy do
|
||||
{:ok, type, parse_host(host), port}
|
||||
else
|
||||
_ ->
|
||||
Logger.warn("Parsing proxy failed #{inspect(proxy)}")
|
||||
{:error, :invalid_proxy}
|
||||
end
|
||||
end
|
||||
|
||||
@spec parse_host(String.t() | atom() | charlist()) :: charlist() | ip_address()
|
||||
def parse_host(host) when is_list(host), do: host
|
||||
def parse_host(host) when is_atom(host), do: to_charlist(host)
|
||||
|
||||
def parse_host(host) when is_binary(host) do
|
||||
def format_host(host) when is_binary(host) do
|
||||
host = to_charlist(host)
|
||||
|
||||
case :inet.parse_address(host) do
|
||||
@ -109,16 +40,10 @@ defmodule Pleroma.HTTP.Connection do
|
||||
end
|
||||
end
|
||||
|
||||
@spec format_host(String.t()) :: charlist()
|
||||
def format_host(host) do
|
||||
host_charlist = to_charlist(host)
|
||||
defp adapter_options(opts, uri, Tesla.Adapter.Gun), do: Pleroma.HTTP.Gun.options(opts, uri)
|
||||
|
||||
case :inet.parse_address(host_charlist) do
|
||||
{:error, :einval} ->
|
||||
:idna.encode(host_charlist)
|
||||
defp adapter_options(opts, uri, Tesla.Adapter.Hackney),
|
||||
do: Pleroma.HTTP.Hackney.options(opts, uri)
|
||||
|
||||
{:ok, _ip} ->
|
||||
host_charlist
|
||||
end
|
||||
end
|
||||
defp adapter_options(opts, _, _), do: Keyword.put(opts, :env, Pleroma.Config.get(:env))
|
||||
end
|
||||
|
65
lib/pleroma/http/gun.ex
Normal file
65
lib/pleroma/http/gun.ex
Normal file
@ -0,0 +1,65 @@
|
||||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.HTTP.Gun do
|
||||
alias Pleroma.Config
|
||||
|
||||
@spec options(keyword(), URI.t()) :: keyword()
|
||||
def options(opts \\ [], %URI{} = uri) do
|
||||
merge_with_defaults()
|
||||
|> add_scheme_opts(uri)
|
||||
|> maybe_add_proxy()
|
||||
|> Keyword.merge(opts)
|
||||
|> add_pool_timeout()
|
||||
|> add_reuse_conn_flag()
|
||||
|> add_pool_alive_flag()
|
||||
end
|
||||
|
||||
defp merge_with_defaults do
|
||||
config = Config.get([:http, :adapter], [])
|
||||
|
||||
defaults = [
|
||||
connect_timeout: 5_000,
|
||||
domain_lookup_timeout: 5_000,
|
||||
tls_handshake_timeout: 5_000,
|
||||
retry: 1,
|
||||
retry_timeout: 1000,
|
||||
await_up_timeout: 5_000
|
||||
]
|
||||
|
||||
Keyword.merge(defaults, config)
|
||||
end
|
||||
|
||||
defp add_scheme_opts(opts, %{scheme: "http"}), do: opts
|
||||
|
||||
defp add_scheme_opts(opts, %{scheme: "https"}) do
|
||||
tls_opts = [
|
||||
log_level: :warning,
|
||||
session_lifetime: 6000,
|
||||
session_cache_client_max: 250
|
||||
]
|
||||
|
||||
Keyword.merge(opts, certificates_verification: true, tls_opts: tls_opts)
|
||||
end
|
||||
|
||||
defp maybe_add_proxy(opts), do: Pleroma.HTTP.Proxy.maybe_add_proxy(opts)
|
||||
|
||||
defp add_pool_timeout(opts) do
|
||||
default_timeout = Config.get([:pools, :default, :timeout])
|
||||
timeout = Config.get([:pools, opts[:pool], :timeout], default_timeout)
|
||||
Keyword.put(opts, :timeout, timeout)
|
||||
end
|
||||
|
||||
defp add_reuse_conn_flag(opts) do
|
||||
Keyword.update(opts, :reuse_conn, true, fn flag? ->
|
||||
Pleroma.Pool.Connections.alive?(:gun_connections) and flag?
|
||||
end)
|
||||
end
|
||||
|
||||
defp add_pool_alive_flag(opts) do
|
||||
pid = Process.whereis(opts[:pool])
|
||||
pool_alive? = !is_nil(pid) && Process.alive?(pid)
|
||||
Keyword.put(opts, :pool_alive?, pool_alive?)
|
||||
end
|
||||
end
|
49
lib/pleroma/http/hackney.ex
Normal file
49
lib/pleroma/http/hackney.ex
Normal file
@ -0,0 +1,49 @@
|
||||
defmodule Pleroma.HTTP.Hackney do
|
||||
@spec options(keyword(), URI.t()) :: keyword()
|
||||
def options(opts \\ [], %URI{} = uri) do
|
||||
merge_with_defaults()
|
||||
|> add_scheme_opts(uri)
|
||||
|> maybe_add_proxy()
|
||||
|> merge_with_incoming_opts(opts)
|
||||
|> add_pool_timeout()
|
||||
end
|
||||
|
||||
defp merge_with_defaults do
|
||||
config = Pleroma.Config.get([:http, :adapter], [])
|
||||
|
||||
defaults = [
|
||||
connect_timeout: 10_000,
|
||||
recv_timeout: 20_000,
|
||||
follow_redirect: true,
|
||||
force_redirect: true
|
||||
]
|
||||
|
||||
Keyword.merge(defaults, config)
|
||||
end
|
||||
|
||||
defp add_scheme_opts(opts, %URI{scheme: "http"}), do: opts
|
||||
|
||||
defp add_scheme_opts(opts, %URI{scheme: "https", host: host}) do
|
||||
ssl_opts = [
|
||||
ssl_options: [
|
||||
# Workaround for remote server certificate chain issues
|
||||
partial_chain: &:hackney_connect.partial_chain/1,
|
||||
|
||||
# We don't support TLS v1.3 yet
|
||||
versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"],
|
||||
server_name_indication: to_charlist(host)
|
||||
]
|
||||
]
|
||||
|
||||
Keyword.merge(opts, ssl_opts)
|
||||
end
|
||||
|
||||
defp maybe_add_proxy(opts), do: Pleroma.HTTP.Proxy.maybe_add_proxy(opts)
|
||||
|
||||
defp merge_with_incoming_opts(opts, incoming), do: Keyword.merge(opts, incoming)
|
||||
|
||||
defp add_pool_timeout(opts) do
|
||||
timeout = Pleroma.Config.get([:hackney_pools, opts[:pool], :timeout], 10_000)
|
||||
Keyword.put(opts, :timeout, timeout)
|
||||
end
|
||||
end
|
@ -7,9 +7,8 @@ defmodule Pleroma.HTTP do
|
||||
Wrapper for `Tesla.request/2`.
|
||||
"""
|
||||
|
||||
alias Pleroma.HTTP.Connection
|
||||
alias Pleroma.HTTP.Request
|
||||
alias Pleroma.HTTP.RequestBuilder, as: Builder
|
||||
alias Pleroma.HTTP.Request.Builder
|
||||
alias Tesla.Client
|
||||
alias Tesla.Env
|
||||
|
||||
@ -56,44 +55,25 @@ defmodule Pleroma.HTTP do
|
||||
{:ok, Env.t()} | {:error, any()}
|
||||
def request(method, url, body, headers, options) when is_binary(url) do
|
||||
uri = URI.parse(url)
|
||||
adapter_opts = Connection.options(uri, options[:adapter] || [])
|
||||
adapter_opts = Pleroma.HTTP.Connection.options(uri, options[:adapter] || [])
|
||||
options = put_in(options[:adapter], adapter_opts)
|
||||
params = options[:params] || []
|
||||
request = build_request(method, headers, options, url, body, params)
|
||||
|
||||
adapter = Application.get_env(:tesla, :adapter)
|
||||
client = Tesla.client([Tesla.Middleware.FollowRedirects], adapter)
|
||||
|
||||
pid = Process.whereis(adapter_opts[:pool])
|
||||
client = Tesla.client([Pleroma.HTTP.Middleware.FollowRedirects], adapter)
|
||||
request = build_request(method, headers, options, url, body)
|
||||
|
||||
pool_alive? =
|
||||
if adapter == Tesla.Adapter.Gun && pid do
|
||||
Process.alive?(pid)
|
||||
else
|
||||
false
|
||||
end
|
||||
|
||||
request_opts =
|
||||
adapter_opts
|
||||
|> Enum.into(%{})
|
||||
|> Map.put(:env, Pleroma.Config.get([:env]))
|
||||
|> Map.put(:pool_alive?, pool_alive?)
|
||||
|
||||
response = request(client, request, request_opts)
|
||||
|
||||
Connection.after_request(adapter_opts)
|
||||
|
||||
response
|
||||
request(client, request, Enum.into(adapter_opts, %{}))
|
||||
end
|
||||
|
||||
@spec request(Client.t(), keyword(), map()) :: {:ok, Env.t()} | {:error, any()}
|
||||
def request(%Client{} = client, request, %{env: :test}), do: request(client, request)
|
||||
def request(client, request, %{env: :test}), do: request(client, request)
|
||||
|
||||
def request(%Client{} = client, request, %{body_as: :chunks}), do: request(client, request)
|
||||
def request(client, request, %{body_as: :chunks}), do: request(client, request)
|
||||
|
||||
def request(%Client{} = client, request, %{pool_alive?: false}), do: request(client, request)
|
||||
def request(client, request, %{pool_alive?: false}), do: request(client, request)
|
||||
|
||||
def request(%Client{} = client, request, %{pool: pool, timeout: timeout}) do
|
||||
def request(client, request, %{pool: pool, timeout: timeout}) do
|
||||
:poolboy.transaction(
|
||||
pool,
|
||||
&Pleroma.Pool.Request.execute(&1, client, request, timeout),
|
||||
@ -104,14 +84,13 @@ defmodule Pleroma.HTTP do
|
||||
@spec request(Client.t(), keyword()) :: {:ok, Env.t()} | {:error, any()}
|
||||
def request(client, request), do: Tesla.request(client, request)
|
||||
|
||||
defp build_request(method, headers, options, url, body, params) do
|
||||
defp build_request(method, headers, options, url, body) do
|
||||
Builder.new()
|
||||
|> Builder.method(method)
|
||||
|> Builder.headers(headers)
|
||||
|> Builder.opts(options)
|
||||
|> Builder.url(url)
|
||||
|> Builder.add_param(:body, :body, body)
|
||||
|> Builder.add_param(:query, :query, params)
|
||||
|> Builder.convert_to_keyword()
|
||||
end
|
||||
end
|
||||
|
109
lib/pleroma/http/middleware/follow_redirects.ex
Normal file
109
lib/pleroma/http/middleware/follow_redirects.ex
Normal file
@ -0,0 +1,109 @@
|
||||
defmodule Pleroma.HTTP.Middleware.FollowRedirects do
|
||||
@moduledoc """
|
||||
Follow 3xx redirects
|
||||
## Example
|
||||
```
|
||||
defmodule MyClient do
|
||||
use Tesla
|
||||
plug Tesla.Middleware.FollowRedirects, max_redirects: 3 # defaults to 5
|
||||
end
|
||||
```
|
||||
## Options
|
||||
- `:max_redirects` - limit number of redirects (default: `5`)
|
||||
"""
|
||||
|
||||
@behaviour Tesla.Middleware
|
||||
|
||||
@max_redirects 5
|
||||
@redirect_statuses [301, 302, 303, 307, 308]
|
||||
|
||||
@impl Tesla.Middleware
|
||||
def call(env, next, opts \\ []) do
|
||||
max = Keyword.get(opts, :max_redirects, @max_redirects)
|
||||
|
||||
redirect(env, next, max)
|
||||
end
|
||||
|
||||
defp redirect(env, next, left) do
|
||||
opts = env.opts[:adapter]
|
||||
|
||||
adapter_opts =
|
||||
if opts[:reuse_conn] do
|
||||
checkin_conn(env.url, opts)
|
||||
else
|
||||
opts
|
||||
end
|
||||
|
||||
env = %{env | opts: Keyword.put(env.opts, :adapter, adapter_opts)}
|
||||
|
||||
case Tesla.run(env, next) do
|
||||
{:ok, %{status: status} = res} when status in @redirect_statuses and left > 0 ->
|
||||
checkout_conn(adapter_opts)
|
||||
|
||||
case Tesla.get_header(res, "location") do
|
||||
nil ->
|
||||
{:ok, res}
|
||||
|
||||
location ->
|
||||
location = parse_location(location, res)
|
||||
|
||||
env
|
||||
|> new_request(res.status, location)
|
||||
|> redirect(next, left - 1)
|
||||
end
|
||||
|
||||
{:ok, %{status: status}} when status in @redirect_statuses ->
|
||||
checkout_conn(adapter_opts)
|
||||
{:error, {__MODULE__, :too_many_redirects}}
|
||||
|
||||
other ->
|
||||
unless adapter_opts[:body_as] == :chunks do
|
||||
checkout_conn(adapter_opts)
|
||||
end
|
||||
|
||||
other
|
||||
end
|
||||
end
|
||||
|
||||
defp checkin_conn(url, opts) do
|
||||
uri = URI.parse(url)
|
||||
|
||||
case Pleroma.Pool.Connections.checkin(uri, :gun_connections, opts) do
|
||||
nil ->
|
||||
opts
|
||||
|
||||
conn when is_pid(conn) ->
|
||||
Keyword.merge(opts, conn: conn, close_conn: false)
|
||||
end
|
||||
end
|
||||
|
||||
defp checkout_conn(opts) do
|
||||
if is_pid(opts[:conn]) do
|
||||
Pleroma.Pool.Connections.checkout(opts[:conn], self(), :gun_connections)
|
||||
end
|
||||
end
|
||||
|
||||
# The 303 (See Other) redirect was added in HTTP/1.1 to indicate that the originally
|
||||
# requested resource is not available, however a related resource (or another redirect)
|
||||
# available via GET is available at the specified location.
|
||||
# https://tools.ietf.org/html/rfc7231#section-6.4.4
|
||||
defp new_request(env, 303, location), do: %{env | url: location, method: :get, query: []}
|
||||
|
||||
# The 307 (Temporary Redirect) status code indicates that the target
|
||||
# resource resides temporarily under a different URI and the user agent
|
||||
# MUST NOT change the request method (...)
|
||||
# https://tools.ietf.org/html/rfc7231#section-6.4.7
|
||||
defp new_request(env, 307, location), do: %{env | url: location}
|
||||
|
||||
defp new_request(env, _, location), do: %{env | url: location, query: []}
|
||||
|
||||
defp parse_location("https://" <> _rest = location, _env), do: location
|
||||
defp parse_location("http://" <> _rest = location, _env), do: location
|
||||
|
||||
defp parse_location(location, env) do
|
||||
env.url
|
||||
|> URI.parse()
|
||||
|> URI.merge(location)
|
||||
|> URI.to_string()
|
||||
end
|
||||
end
|
67
lib/pleroma/http/proxy.ex
Normal file
67
lib/pleroma/http/proxy.ex
Normal file
@ -0,0 +1,67 @@
|
||||
defmodule Pleroma.HTTP.Proxy do
|
||||
require Logger
|
||||
|
||||
alias Pleroma.HTTP.Connection
|
||||
|
||||
@type proxy_type() :: :socks4 | :socks5
|
||||
|
||||
@spec parse_proxy(String.t() | tuple() | nil) ::
|
||||
{:ok, Connection.host(), pos_integer()}
|
||||
| {:ok, proxy_type(), Connection.host(), pos_integer()}
|
||||
| {:error, atom()}
|
||||
| nil
|
||||
|
||||
def parse_proxy(nil), do: nil
|
||||
|
||||
def parse_proxy(proxy) when is_binary(proxy) do
|
||||
with [host, port] <- String.split(proxy, ":"),
|
||||
{port, ""} <- Integer.parse(port) do
|
||||
{:ok, Connection.format_host(host), port}
|
||||
else
|
||||
{_, _} ->
|
||||
Logger.warn("Parsing port failed #{inspect(proxy)}")
|
||||
{:error, :invalid_proxy_port}
|
||||
|
||||
:error ->
|
||||
Logger.warn("Parsing port failed #{inspect(proxy)}")
|
||||
{:error, :invalid_proxy_port}
|
||||
|
||||
_ ->
|
||||
Logger.warn("Parsing proxy failed #{inspect(proxy)}")
|
||||
{:error, :invalid_proxy}
|
||||
end
|
||||
end
|
||||
|
||||
def parse_proxy(proxy) when is_tuple(proxy) do
|
||||
with {type, host, port} <- proxy do
|
||||
{:ok, type, Connection.format_host(host), port}
|
||||
else
|
||||
_ ->
|
||||
Logger.warn("Parsing proxy failed #{inspect(proxy)}")
|
||||
{:error, :invalid_proxy}
|
||||
end
|
||||
end
|
||||
|
||||
defp format_proxy(nil), do: nil
|
||||
|
||||
defp format_proxy(proxy_url) do
|
||||
case parse_proxy(proxy_url) do
|
||||
{:ok, host, port} -> {host, port}
|
||||
{:ok, type, host, port} -> {type, host, port}
|
||||
_ -> nil
|
||||
end
|
||||
end
|
||||
|
||||
@spec maybe_add_proxy(keyword()) :: keyword()
|
||||
def maybe_add_proxy(opts) do
|
||||
proxy =
|
||||
Pleroma.Config.get([:http, :proxy_url])
|
||||
|> format_proxy()
|
||||
|
||||
if proxy do
|
||||
Keyword.put_new(opts, :proxy, proxy)
|
||||
else
|
||||
opts
|
||||
end
|
||||
end
|
||||
end
|
@ -2,7 +2,7 @@
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.HTTP.RequestBuilder do
|
||||
defmodule Pleroma.HTTP.Request.Builder do
|
||||
@moduledoc """
|
||||
Helper functions for building Tesla requests
|
||||
"""
|
||||
@ -53,8 +53,6 @@ defmodule Pleroma.HTTP.RequestBuilder do
|
||||
Add optional parameters to the request
|
||||
"""
|
||||
@spec add_param(Request.t(), atom(), atom(), any()) :: Request.t()
|
||||
def add_param(request, :query, :query, values), do: %{request | query: values}
|
||||
|
||||
def add_param(request, :body, :body, value), do: %{request | body: value}
|
||||
|
||||
def add_param(request, :body, key, value) do
|
@ -26,7 +26,7 @@ defmodule Pleroma.ReverseProxy.Client.Tesla do
|
||||
url,
|
||||
body,
|
||||
headers,
|
||||
Keyword.put(opts, :adapter, opts)
|
||||
adapter: opts
|
||||
) do
|
||||
if is_map(response.body) and method != :head do
|
||||
{:ok, response.status, response.headers, response.body}
|
||||
@ -41,14 +41,8 @@ defmodule Pleroma.ReverseProxy.Client.Tesla do
|
||||
@impl true
|
||||
@spec stream_body(map()) ::
|
||||
{:ok, binary(), map()} | {:error, atom() | String.t()} | :done | no_return()
|
||||
def stream_body(%{pid: pid, opts: opts, fin: true}) do
|
||||
# if connection was reused, but in tesla were redirects,
|
||||
# tesla returns new opened connection, which must be closed manually
|
||||
if opts[:old_conn], do: Tesla.Adapter.Gun.close(pid)
|
||||
# if there were redirects we need to checkout old conn
|
||||
conn = opts[:old_conn] || opts[:conn]
|
||||
|
||||
if conn, do: :ok = Pleroma.Pool.Connections.checkout(conn, self(), :gun_connections)
|
||||
def stream_body(%{pid: pid, fin: true}) do
|
||||
:ok = Pleroma.Pool.Connections.checkout(pid, self(), :gun_connections)
|
||||
|
||||
:done
|
||||
end
|
||||
|
2
mix.exs
2
mix.exs
@ -131,7 +131,7 @@ defmodule Pleroma.Mixfile do
|
||||
# {:tesla, "~> 1.3", override: true},
|
||||
{:tesla,
|
||||
git: "https://git.pleroma.social/pleroma/elixir-libraries/tesla.git",
|
||||
ref: "61b7503cef33f00834f78ddfafe0d5d9dec2270b",
|
||||
ref: "e555341e7a6a60fc06712652feabcd90f51767f4",
|
||||
override: true},
|
||||
{:castore, "~> 0.1"},
|
||||
{:cowlib, "~> 2.8", override: true},
|
||||
|
2
mix.lock
2
mix.lock
@ -104,7 +104,7 @@
|
||||
"swoosh": {:hex, :swoosh, "0.23.5", "bfd9404bbf5069b1be2ffd317923ce57e58b332e25dbca2a35dedd7820dfee5a", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm", "e3928e1d2889a308aaf3e42755809ac21cffd77cb58eef01cbfdab4ce2fd1e21"},
|
||||
"syslog": {:hex, :syslog, "1.0.6", "995970c9aa7feb380ac493302138e308d6e04fd57da95b439a6df5bb3bf75076", [:rebar3], [], "hexpm", "769ddfabd0d2a16f3f9c17eb7509951e0ca4f68363fb26f2ee51a8ec4a49881a"},
|
||||
"telemetry": {:hex, :telemetry, "0.4.1", "ae2718484892448a24470e6aa341bc847c3277bfb8d4e9289f7474d752c09c7f", [:rebar3], [], "hexpm", "4738382e36a0a9a2b6e25d67c960e40e1a2c95560b9f936d8e29de8cd858480f"},
|
||||
"tesla": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/tesla.git", "61b7503cef33f00834f78ddfafe0d5d9dec2270b", [ref: "61b7503cef33f00834f78ddfafe0d5d9dec2270b"]},
|
||||
"tesla": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/tesla.git", "e555341e7a6a60fc06712652feabcd90f51767f4", [ref: "e555341e7a6a60fc06712652feabcd90f51767f4"]},
|
||||
"timex": {:hex, :timex, "3.6.1", "efdf56d0e67a6b956cc57774353b0329c8ab7726766a11547e529357ffdc1d56", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5 or ~> 1.0.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "f354efb2400dd7a80fd9eb6c8419068c4f632da4ac47f3d8822d6e33f08bc852"},
|
||||
"trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bd4fde4c15f3e993a999e019d64347489b91b7a9096af68b2bdadd192afa693f"},
|
||||
"tzdata": {:hex, :tzdata, "0.5.22", "f2ba9105117ee0360eae2eca389783ef7db36d533899b2e84559404dbc77ebb8", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "cd66c8a1e6a9e121d1f538b01bef459334bb4029a1ffb4eeeb5e4eae0337e7b6"},
|
||||
|
@ -1,233 +0,0 @@
|
||||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.HTTP.AdapterHelper.GunTest do
|
||||
use ExUnit.Case
|
||||
use Pleroma.Tests.Helpers
|
||||
|
||||
import Mox
|
||||
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.HTTP.AdapterHelper.Gun
|
||||
alias Pleroma.Pool.Connections
|
||||
|
||||
setup :verify_on_exit!
|
||||
setup :set_mox_global
|
||||
|
||||
defp gun_mock do
|
||||
Pleroma.GunMock
|
||||
|> stub(:open, fn _, _, _ ->
|
||||
Task.start_link(fn -> Process.sleep(1000) end)
|
||||
end)
|
||||
|> stub(:await_up, fn _, _ -> {:ok, :http} end)
|
||||
|> stub(:set_owner, fn _, _ -> :ok end)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
describe "options/1" do
|
||||
setup do: clear_config([:http, :adapter], a: 1, b: 2)
|
||||
|
||||
test "https url with default port" do
|
||||
uri = URI.parse("https://example.com")
|
||||
|
||||
opts = Gun.options([receive_conn: false], uri)
|
||||
assert opts[:certificates_verification]
|
||||
assert opts[:tls_opts][:log_level] == :warning
|
||||
end
|
||||
|
||||
test "https ipv4 with default port" do
|
||||
uri = URI.parse("https://127.0.0.1")
|
||||
|
||||
opts = Gun.options([receive_conn: false], uri)
|
||||
assert opts[:certificates_verification]
|
||||
assert opts[:tls_opts][:log_level] == :warning
|
||||
end
|
||||
|
||||
test "https ipv6 with default port" do
|
||||
uri = URI.parse("https://[2a03:2880:f10c:83:face:b00c:0:25de]")
|
||||
|
||||
opts = Gun.options([receive_conn: false], uri)
|
||||
assert opts[:certificates_verification]
|
||||
assert opts[:tls_opts][:log_level] == :warning
|
||||
end
|
||||
|
||||
test "https url with non standart port" do
|
||||
uri = URI.parse("https://example.com:115")
|
||||
|
||||
opts = Gun.options([receive_conn: false], uri)
|
||||
|
||||
assert opts[:certificates_verification]
|
||||
end
|
||||
|
||||
test "merges with defaul http adapter config" do
|
||||
defaults = Gun.options([receive_conn: false], URI.parse("https://example.com"))
|
||||
assert Keyword.has_key?(defaults, :a)
|
||||
assert Keyword.has_key?(defaults, :b)
|
||||
end
|
||||
|
||||
test "default ssl adapter opts with connection" do
|
||||
gun_mock()
|
||||
uri = URI.parse("https://some-domain.com")
|
||||
|
||||
opts = Gun.options(uri)
|
||||
|
||||
assert opts[:certificates_verification]
|
||||
refute opts[:tls_opts] == []
|
||||
|
||||
assert opts[:close_conn] == false
|
||||
assert is_pid(opts[:conn])
|
||||
end
|
||||
|
||||
test "parses string proxy host & port" do
|
||||
proxy = Config.get([:http, :proxy_url])
|
||||
Config.put([:http, :proxy_url], "localhost:8123")
|
||||
on_exit(fn -> Config.put([:http, :proxy_url], proxy) end)
|
||||
|
||||
uri = URI.parse("https://some-domain.com")
|
||||
opts = Gun.options([receive_conn: false], uri)
|
||||
assert opts[:proxy] == {'localhost', 8123}
|
||||
end
|
||||
|
||||
test "parses tuple proxy scheme host and port" do
|
||||
proxy = Config.get([:http, :proxy_url])
|
||||
Config.put([:http, :proxy_url], {:socks, 'localhost', 1234})
|
||||
on_exit(fn -> Config.put([:http, :proxy_url], proxy) end)
|
||||
|
||||
uri = URI.parse("https://some-domain.com")
|
||||
opts = Gun.options([receive_conn: false], uri)
|
||||
assert opts[:proxy] == {:socks, 'localhost', 1234}
|
||||
end
|
||||
|
||||
test "passed opts have more weight than defaults" do
|
||||
proxy = Config.get([:http, :proxy_url])
|
||||
Config.put([:http, :proxy_url], {:socks5, 'localhost', 1234})
|
||||
on_exit(fn -> Config.put([:http, :proxy_url], proxy) end)
|
||||
uri = URI.parse("https://some-domain.com")
|
||||
opts = Gun.options([receive_conn: false, proxy: {'example.com', 4321}], uri)
|
||||
|
||||
assert opts[:proxy] == {'example.com', 4321}
|
||||
end
|
||||
end
|
||||
|
||||
describe "options/1 with receive_conn parameter" do
|
||||
setup do: gun_mock()
|
||||
|
||||
test "receive conn by default" do
|
||||
uri = URI.parse("http://another-domain.com")
|
||||
|
||||
received_opts = Gun.options(uri)
|
||||
assert received_opts[:close_conn] == false
|
||||
assert is_pid(received_opts[:conn])
|
||||
end
|
||||
|
||||
test "don't receive conn if receive_conn is false" do
|
||||
uri = URI.parse("http://another-domain.com")
|
||||
|
||||
opts = [receive_conn: false]
|
||||
received_opts = Gun.options(opts, uri)
|
||||
assert received_opts[:close_conn] == nil
|
||||
assert received_opts[:conn] == nil
|
||||
end
|
||||
end
|
||||
|
||||
describe "after_request/1" do
|
||||
setup do: gun_mock()
|
||||
|
||||
test "body_as not chunks" do
|
||||
uri = URI.parse("http://some-domain-5.com")
|
||||
opts = Gun.options(uri)
|
||||
:ok = Gun.after_request(opts)
|
||||
conn = opts[:conn]
|
||||
|
||||
assert match?(
|
||||
%Connections{
|
||||
conns: %{
|
||||
"http:some-domain-5.com:80" => %Pleroma.Gun.Conn{
|
||||
conn: ^conn,
|
||||
conn_state: :idle,
|
||||
used_by: []
|
||||
}
|
||||
}
|
||||
},
|
||||
Connections.get_state(:gun_connections)
|
||||
)
|
||||
end
|
||||
|
||||
test "body_as chunks" do
|
||||
uri = URI.parse("http://some-domain-6.com")
|
||||
opts = Gun.options([body_as: :chunks], uri)
|
||||
:ok = Gun.after_request(opts)
|
||||
conn = opts[:conn]
|
||||
self = self()
|
||||
|
||||
assert match?(
|
||||
%Connections{
|
||||
conns: %{
|
||||
"http:some-domain-6.com:80" => %Pleroma.Gun.Conn{
|
||||
conn: ^conn,
|
||||
conn_state: :active,
|
||||
used_by: [{^self, _}]
|
||||
}
|
||||
}
|
||||
},
|
||||
Connections.get_state(:gun_connections)
|
||||
)
|
||||
end
|
||||
|
||||
test "with no connection" do
|
||||
uri = URI.parse("http://uniq-domain.com")
|
||||
opts = Gun.options([body_as: :chunks], uri)
|
||||
conn = opts[:conn]
|
||||
opts = Keyword.delete(opts, :conn)
|
||||
self = self()
|
||||
|
||||
:ok = Gun.after_request(opts)
|
||||
|
||||
assert %Connections{
|
||||
conns: %{
|
||||
"http:uniq-domain.com:80" => %Pleroma.Gun.Conn{
|
||||
conn: ^conn,
|
||||
conn_state: :active,
|
||||
used_by: [{^self, _}]
|
||||
}
|
||||
}
|
||||
} = Connections.get_state(:gun_connections)
|
||||
end
|
||||
|
||||
test "with ipv4" do
|
||||
uri = URI.parse("http://127.0.0.1")
|
||||
opts = Gun.options(uri)
|
||||
:ok = Gun.after_request(opts)
|
||||
conn = opts[:conn]
|
||||
|
||||
assert %Connections{
|
||||
conns: %{
|
||||
"http:127.0.0.1:80" => %Pleroma.Gun.Conn{
|
||||
conn: ^conn,
|
||||
conn_state: :idle,
|
||||
used_by: []
|
||||
}
|
||||
}
|
||||
} = Connections.get_state(:gun_connections)
|
||||
end
|
||||
|
||||
test "with ipv6" do
|
||||
uri = URI.parse("http://[2a03:2880:f10c:83:face:b00c:0:25de]")
|
||||
opts = Gun.options(uri)
|
||||
:ok = Gun.after_request(opts)
|
||||
conn = opts[:conn]
|
||||
|
||||
assert %Connections{
|
||||
conns: %{
|
||||
"http:2a03:2880:f10c:83:face:b00c:0:25de:80" => %Pleroma.Gun.Conn{
|
||||
conn: ^conn,
|
||||
conn_state: :idle,
|
||||
used_by: []
|
||||
}
|
||||
}
|
||||
} = Connections.get_state(:gun_connections)
|
||||
end
|
||||
end
|
||||
end
|
@ -1,28 +0,0 @@
|
||||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.HTTP.AdapterHelperTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
alias Pleroma.HTTP.AdapterHelper
|
||||
|
||||
describe "format_proxy/1" do
|
||||
test "with nil" do
|
||||
assert AdapterHelper.format_proxy(nil) == nil
|
||||
end
|
||||
|
||||
test "with string" do
|
||||
assert AdapterHelper.format_proxy("127.0.0.1:8123") == {{127, 0, 0, 1}, 8123}
|
||||
end
|
||||
|
||||
test "localhost with port" do
|
||||
assert AdapterHelper.format_proxy("localhost:8123") == {'localhost', 8123}
|
||||
end
|
||||
|
||||
test "tuple" do
|
||||
assert AdapterHelper.format_proxy({:socks4, :localhost, 9050}) ==
|
||||
{:socks4, 'localhost', 9050}
|
||||
end
|
||||
end
|
||||
end
|
@ -3,133 +3,51 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.HTTP.ConnectionTest do
|
||||
use ExUnit.Case, async: true
|
||||
use Pleroma.Tests.Helpers
|
||||
use ExUnit.Case
|
||||
|
||||
import ExUnit.CaptureLog
|
||||
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.HTTP.Connection
|
||||
|
||||
describe "parse_host/1" do
|
||||
describe "format_host/1" do
|
||||
test "as atom to charlist" do
|
||||
assert Connection.parse_host(:localhost) == 'localhost'
|
||||
assert Connection.format_host(:localhost) == 'localhost'
|
||||
end
|
||||
|
||||
test "as string to charlist" do
|
||||
assert Connection.parse_host("localhost.com") == 'localhost.com'
|
||||
assert Connection.format_host("localhost.com") == 'localhost.com'
|
||||
end
|
||||
|
||||
test "as string ip to tuple" do
|
||||
assert Connection.parse_host("127.0.0.1") == {127, 0, 0, 1}
|
||||
assert Connection.format_host("127.0.0.1") == {127, 0, 0, 1}
|
||||
end
|
||||
end
|
||||
|
||||
describe "parse_proxy/1" do
|
||||
test "ip with port" do
|
||||
assert Connection.parse_proxy("127.0.0.1:8123") == {:ok, {127, 0, 0, 1}, 8123}
|
||||
end
|
||||
|
||||
test "host with port" do
|
||||
assert Connection.parse_proxy("localhost:8123") == {:ok, 'localhost', 8123}
|
||||
end
|
||||
|
||||
test "as tuple" do
|
||||
assert Connection.parse_proxy({:socks4, :localhost, 9050}) ==
|
||||
{:ok, :socks4, 'localhost', 9050}
|
||||
end
|
||||
|
||||
test "as tuple with string host" do
|
||||
assert Connection.parse_proxy({:socks5, "localhost", 9050}) ==
|
||||
{:ok, :socks5, 'localhost', 9050}
|
||||
end
|
||||
end
|
||||
|
||||
describe "parse_proxy/1 errors" do
|
||||
test "ip without port" do
|
||||
capture_log(fn ->
|
||||
assert Connection.parse_proxy("127.0.0.1") == {:error, :invalid_proxy}
|
||||
end) =~ "parsing proxy fail \"127.0.0.1\""
|
||||
end
|
||||
|
||||
test "host without port" do
|
||||
capture_log(fn ->
|
||||
assert Connection.parse_proxy("localhost") == {:error, :invalid_proxy}
|
||||
end) =~ "parsing proxy fail \"localhost\""
|
||||
end
|
||||
|
||||
test "host with bad port" do
|
||||
capture_log(fn ->
|
||||
assert Connection.parse_proxy("localhost:port") == {:error, :invalid_proxy_port}
|
||||
end) =~ "parsing port in proxy fail \"localhost:port\""
|
||||
end
|
||||
|
||||
test "ip with bad port" do
|
||||
capture_log(fn ->
|
||||
assert Connection.parse_proxy("127.0.0.1:15.9") == {:error, :invalid_proxy_port}
|
||||
end) =~ "parsing port in proxy fail \"127.0.0.1:15.9\""
|
||||
end
|
||||
|
||||
test "as tuple without port" do
|
||||
capture_log(fn ->
|
||||
assert Connection.parse_proxy({:socks5, :localhost}) == {:error, :invalid_proxy}
|
||||
end) =~ "parsing proxy fail {:socks5, :localhost}"
|
||||
end
|
||||
|
||||
test "with nil" do
|
||||
assert Connection.parse_proxy(nil) == nil
|
||||
end
|
||||
end
|
||||
|
||||
describe "options/3" do
|
||||
setup do: clear_config([:http, :proxy_url])
|
||||
|
||||
test "without proxy_url in config" do
|
||||
Config.delete([:http, :proxy_url])
|
||||
|
||||
opts = Connection.options(%URI{})
|
||||
refute Keyword.has_key?(opts, :proxy)
|
||||
end
|
||||
|
||||
test "parses string proxy host & port" do
|
||||
Config.put([:http, :proxy_url], "localhost:8123")
|
||||
|
||||
opts = Connection.options(%URI{})
|
||||
assert opts[:proxy] == {'localhost', 8123}
|
||||
end
|
||||
|
||||
test "parses tuple proxy scheme host and port" do
|
||||
Config.put([:http, :proxy_url], {:socks, 'localhost', 1234})
|
||||
|
||||
opts = Connection.options(%URI{})
|
||||
assert opts[:proxy] == {:socks, 'localhost', 1234}
|
||||
describe "options/2" do
|
||||
test "defaults" do
|
||||
assert Connection.options(%URI{}) == [env: :test, pool: :federation]
|
||||
end
|
||||
|
||||
test "passed opts have more weight than defaults" do
|
||||
Config.put([:http, :proxy_url], {:socks5, 'localhost', 1234})
|
||||
|
||||
opts = Connection.options(%URI{}, proxy: {'example.com', 4321})
|
||||
|
||||
assert opts[:proxy] == {'example.com', 4321}
|
||||
end
|
||||
end
|
||||
|
||||
describe "format_host/1" do
|
||||
test "with domain" do
|
||||
assert Connection.format_host("example.com") == 'example.com'
|
||||
assert Connection.options(%URI{}, pool: :media) == [env: :test, pool: :media]
|
||||
end
|
||||
|
||||
test "with idna domain" do
|
||||
assert Connection.format_host("ですexample.com") == 'xn--example-183fne.com'
|
||||
test "adding defaults for hackney adapter" do
|
||||
initial = Application.get_env(:tesla, :adapter)
|
||||
Application.put_env(:tesla, :adapter, Tesla.Adapter.Hackney)
|
||||
on_exit(fn -> Application.put_env(:tesla, :adapter, initial) end)
|
||||
|
||||
refute %URI{scheme: "https", host: "example.com"}
|
||||
|> Connection.options()
|
||||
|> Keyword.delete(:pool) == []
|
||||
end
|
||||
|
||||
test "with ipv4" do
|
||||
assert Connection.format_host("127.0.0.1") == '127.0.0.1'
|
||||
end
|
||||
test "adding defaults for gun adapter" do
|
||||
initial = Application.get_env(:tesla, :adapter)
|
||||
Application.put_env(:tesla, :adapter, Tesla.Adapter.Gun)
|
||||
on_exit(fn -> Application.put_env(:tesla, :adapter, initial) end)
|
||||
|
||||
test "with ipv6" do
|
||||
assert Connection.format_host("2a03:2880:f10c:83:face:b00c:0:25de") ==
|
||||
'2a03:2880:f10c:83:face:b00c:0:25de'
|
||||
refute %URI{scheme: "https", host: "example.com"}
|
||||
|> Connection.options()
|
||||
|> Keyword.delete(:pool) == []
|
||||
end
|
||||
end
|
||||
end
|
||||
|
84
test/http/gun_test.exs
Normal file
84
test/http/gun_test.exs
Normal file
@ -0,0 +1,84 @@
|
||||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.HTTP.GunTest do
|
||||
use ExUnit.Case
|
||||
use Pleroma.Tests.Helpers
|
||||
|
||||
alias Pleroma.HTTP.Gun
|
||||
|
||||
describe "options/1" do
|
||||
test "https url with default port" do
|
||||
uri = URI.parse("https://example.com")
|
||||
|
||||
opts = Gun.options([reuse_conn: false], uri)
|
||||
assert opts[:certificates_verification]
|
||||
assert opts[:tls_opts][:log_level] == :warning
|
||||
assert opts[:reuse_conn] == false
|
||||
end
|
||||
|
||||
test "https ipv4 with default port" do
|
||||
uri = URI.parse("https://127.0.0.1")
|
||||
|
||||
opts = Gun.options([reuse_conn: false], uri)
|
||||
assert opts[:certificates_verification]
|
||||
end
|
||||
|
||||
test "https ipv6 with default port" do
|
||||
uri = URI.parse("https://[2a03:2880:f10c:83:face:b00c:0:25de]")
|
||||
|
||||
opts = Gun.options([reuse_conn: false], uri)
|
||||
assert opts[:certificates_verification]
|
||||
end
|
||||
|
||||
test "https url with non standart port" do
|
||||
uri = URI.parse("https://example.com:115")
|
||||
|
||||
opts = Gun.options([reuse_conn: false], uri)
|
||||
|
||||
assert opts[:certificates_verification]
|
||||
end
|
||||
|
||||
test "merges with defaul http adapter config" do
|
||||
clear_config([:http, :adapter], a: 1, b: 2)
|
||||
defaults = Gun.options([reuse_conn: false], URI.parse("https://example.com"))
|
||||
assert Keyword.has_key?(defaults, :a)
|
||||
assert Keyword.has_key?(defaults, :b)
|
||||
end
|
||||
|
||||
test "default ssl adapter opts with connection" do
|
||||
uri = URI.parse("https://some-domain.com")
|
||||
|
||||
opts = Gun.options(uri)
|
||||
|
||||
assert opts[:certificates_verification]
|
||||
assert opts[:reuse_conn]
|
||||
refute opts[:tls_opts] == []
|
||||
end
|
||||
|
||||
test "parses string proxy host & port" do
|
||||
clear_config([:http, :proxy_url], "localhost:8123")
|
||||
|
||||
uri = URI.parse("https://some-domain.com")
|
||||
opts = Gun.options([reuse_conn: false], uri)
|
||||
assert opts[:proxy] == {'localhost', 8123}
|
||||
end
|
||||
|
||||
test "parses tuple proxy scheme host and port" do
|
||||
clear_config([:http, :proxy_url], {:socks, 'localhost', 1234})
|
||||
|
||||
uri = URI.parse("https://some-domain.com")
|
||||
opts = Gun.options([reuse_conn: false], uri)
|
||||
assert opts[:proxy] == {:socks, 'localhost', 1234}
|
||||
end
|
||||
|
||||
test "passed opts have more weight than defaults" do
|
||||
clear_config([:http, :proxy_url], {:socks5, 'localhost', 1234})
|
||||
|
||||
uri = URI.parse("https://some-domain.com")
|
||||
opts = Gun.options([reuse_conn: false, proxy: {'example.com', 4321}], uri)
|
||||
assert opts[:proxy] == {'example.com', 4321}
|
||||
end
|
||||
end
|
||||
end
|
@ -2,11 +2,11 @@
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.HTTP.AdapterHelper.HackneyTest do
|
||||
defmodule Pleroma.HTTP.HackneyTest do
|
||||
use ExUnit.Case, async: true
|
||||
use Pleroma.Tests.Helpers
|
||||
|
||||
alias Pleroma.HTTP.AdapterHelper.Hackney
|
||||
alias Pleroma.HTTP.Hackney
|
||||
|
||||
setup_all do
|
||||
uri = URI.parse("http://domain.com")
|
284
test/http/middleware/follow_redirects_test.exs
Normal file
284
test/http/middleware/follow_redirects_test.exs
Normal file
@ -0,0 +1,284 @@
|
||||
defmodule Pleroma.HTTP.Middleware.FollowRedirectsTest do
|
||||
use ExUnit.Case
|
||||
|
||||
import Mox
|
||||
|
||||
alias Pleroma.Gun.Conn
|
||||
alias Pleroma.HTTP.Middleware.FollowRedirects
|
||||
alias Pleroma.Pool.Connections
|
||||
|
||||
setup :verify_on_exit!
|
||||
setup :set_mox_global
|
||||
|
||||
defp gun_mock do
|
||||
Pleroma.GunMock
|
||||
|> stub(:open, fn _, _, _ ->
|
||||
Task.start_link(fn -> Process.sleep(1000) end)
|
||||
end)
|
||||
|> stub(:await_up, fn _, _ -> {:ok, :http} end)
|
||||
|> stub(:set_owner, fn _, _ -> :ok end)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
setup do
|
||||
gun_mock()
|
||||
|
||||
env = %Tesla.Env{
|
||||
body: "",
|
||||
headers: [
|
||||
{"user-agent", "Pleroma"}
|
||||
],
|
||||
method: :get,
|
||||
opts: [
|
||||
adapter: [
|
||||
pool: :media,
|
||||
reuse_conn: true
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
{:ok, env: env}
|
||||
end
|
||||
|
||||
defmodule NoRedirect do
|
||||
def call(env, _opts) do
|
||||
opts = env.opts[:adapter]
|
||||
assert opts[:reuse_conn]
|
||||
assert opts[:conn]
|
||||
assert opts[:close_conn] == false
|
||||
|
||||
{:ok, %{env | status: 200, body: opts[:conn]}}
|
||||
end
|
||||
end
|
||||
|
||||
describe "checkin/checkout conn without redirects" do
|
||||
setup do
|
||||
next = [{NoRedirect, :call, [[]]}]
|
||||
{:ok, next: next}
|
||||
end
|
||||
|
||||
test "common", %{env: env, next: next} do
|
||||
env = %{env | url: "https://common.com/media/common.jpg"}
|
||||
assert {:ok, %{body: conn}} = FollowRedirects.call(env, next)
|
||||
|
||||
assert match?(
|
||||
%Connections{
|
||||
conns: %{
|
||||
"https:common.com:443" => %Conn{
|
||||
awaited_by: [],
|
||||
conn: ^conn,
|
||||
conn_state: :idle,
|
||||
gun_state: :up,
|
||||
retries: 0,
|
||||
used_by: []
|
||||
}
|
||||
}
|
||||
},
|
||||
Connections.get_state(:gun_connections)
|
||||
)
|
||||
end
|
||||
|
||||
test "reverse proxy call", %{env: env, next: next} do
|
||||
env =
|
||||
put_in(env.opts[:adapter][:body_as], :chunks)
|
||||
|> Map.put(:url, "https://chunks.com/media/chunks.jpg")
|
||||
|
||||
assert {:ok, %{body: conn}} = FollowRedirects.call(env, next)
|
||||
|
||||
self = self()
|
||||
|
||||
assert match?(
|
||||
%Connections{
|
||||
conns: %{
|
||||
"https:chunks.com:443" => %Conn{
|
||||
awaited_by: [],
|
||||
conn: ^conn,
|
||||
conn_state: :active,
|
||||
gun_state: :up,
|
||||
retries: 0,
|
||||
used_by: [{^self, _}]
|
||||
}
|
||||
}
|
||||
},
|
||||
Connections.get_state(:gun_connections)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
defmodule OneRedirect do
|
||||
def call(%{url: "https://first-redirect.com"} = env, _opts) do
|
||||
opts = env.opts[:adapter]
|
||||
assert opts[:reuse_conn]
|
||||
assert opts[:conn]
|
||||
assert opts[:close_conn] == false
|
||||
|
||||
{:ok, %{env | status: 302, body: opts[:conn], headers: [{"location", opts[:final_url]}]}}
|
||||
end
|
||||
|
||||
def call(env, _opts) do
|
||||
opts = env.opts[:adapter]
|
||||
assert opts[:reuse_conn]
|
||||
assert opts[:conn]
|
||||
assert opts[:close_conn] == false
|
||||
|
||||
{:ok, %{env | status: 200, body: opts[:conn]}}
|
||||
end
|
||||
end
|
||||
|
||||
describe "checkin/checkout with 1 redirect" do
|
||||
setup do
|
||||
next = [{OneRedirect, :call, [[]]}]
|
||||
|
||||
{:ok, next: next}
|
||||
end
|
||||
|
||||
test "common with redirect", %{env: env, next: next} do
|
||||
adapter_opts = Keyword.put(env.opts[:adapter], :final_url, "https://another-final-url.com")
|
||||
|
||||
env =
|
||||
put_in(env.opts[:adapter], adapter_opts)
|
||||
|> Map.put(:url, "https://first-redirect.com")
|
||||
|
||||
assert {:ok, %{body: conn}} = FollowRedirects.call(env, next)
|
||||
|
||||
assert match?(
|
||||
%Connections{
|
||||
conns: %{
|
||||
"https:first-redirect.com:443" => %Conn{
|
||||
awaited_by: [],
|
||||
conn: _,
|
||||
conn_state: :idle,
|
||||
gun_state: :up,
|
||||
retries: 0,
|
||||
used_by: []
|
||||
},
|
||||
"https:another-final-url.com:443" => %Conn{
|
||||
awaited_by: [],
|
||||
conn: ^conn,
|
||||
conn_state: :idle,
|
||||
gun_state: :up,
|
||||
retries: 0,
|
||||
used_by: []
|
||||
}
|
||||
}
|
||||
},
|
||||
Connections.get_state(:gun_connections)
|
||||
)
|
||||
end
|
||||
|
||||
test "reverse proxy with redirect", %{env: env, next: next} do
|
||||
adapter_opts =
|
||||
Keyword.merge(env.opts[:adapter], body_as: :chunks, final_url: "https://final-url.com")
|
||||
|
||||
env =
|
||||
put_in(env.opts[:adapter], adapter_opts)
|
||||
|> Map.put(:url, "https://first-redirect.com")
|
||||
|
||||
assert {:ok, %{body: conn}} = FollowRedirects.call(env, next)
|
||||
|
||||
self = self()
|
||||
|
||||
assert match?(
|
||||
%Connections{
|
||||
conns: %{
|
||||
"https:first-redirect.com:443" => %Conn{
|
||||
awaited_by: [],
|
||||
conn: _,
|
||||
conn_state: :idle,
|
||||
gun_state: :up,
|
||||
retries: 0,
|
||||
used_by: []
|
||||
},
|
||||
"https:final-url.com:443" => %Conn{
|
||||
awaited_by: [],
|
||||
conn: ^conn,
|
||||
conn_state: :active,
|
||||
gun_state: :up,
|
||||
retries: 0,
|
||||
used_by: [{^self, _}]
|
||||
}
|
||||
}
|
||||
},
|
||||
Connections.get_state(:gun_connections)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
defmodule TwoRedirect do
|
||||
def call(%{url: "https://1-redirect.com"} = env, _opts) do
|
||||
opts = env.opts[:adapter]
|
||||
assert opts[:reuse_conn]
|
||||
assert opts[:conn]
|
||||
assert opts[:close_conn] == false
|
||||
|
||||
{:ok,
|
||||
%{env | status: 302, body: opts[:conn], headers: [{"location", "https://2-redirect.com"}]}}
|
||||
end
|
||||
|
||||
def call(%{url: "https://2-redirect.com"} = env, _opts) do
|
||||
opts = env.opts[:adapter]
|
||||
assert opts[:reuse_conn]
|
||||
assert opts[:conn]
|
||||
assert opts[:close_conn] == false
|
||||
|
||||
{:ok, %{env | status: 302, body: opts[:conn], headers: [{"location", opts[:final_url]}]}}
|
||||
end
|
||||
|
||||
def call(env, _opts) do
|
||||
opts = env.opts[:adapter]
|
||||
assert opts[:reuse_conn]
|
||||
assert opts[:conn]
|
||||
assert opts[:close_conn] == false
|
||||
|
||||
{:ok, %{env | status: 200, body: opts[:conn]}}
|
||||
end
|
||||
end
|
||||
|
||||
describe "checkin/checkout conn with max redirects" do
|
||||
setup do
|
||||
next = [{TwoRedirect, :call, [[]]}]
|
||||
{:ok, next: next}
|
||||
end
|
||||
|
||||
test "common with max redirects", %{env: env, next: next} do
|
||||
adapter_opts =
|
||||
Keyword.merge(env.opts[:adapter],
|
||||
final_url: "https://some-final-url.com"
|
||||
)
|
||||
|
||||
env =
|
||||
put_in(env.opts[:adapter], adapter_opts)
|
||||
|> Map.put(:url, "https://1-redirect.com")
|
||||
|
||||
assert match?(
|
||||
{:error, {FollowRedirects, :too_many_redirects}},
|
||||
FollowRedirects.call(env, next, max_redirects: 1)
|
||||
)
|
||||
|
||||
assert match?(
|
||||
%Connections{
|
||||
conns: %{
|
||||
"https:1-redirect.com:443" => %Conn{
|
||||
awaited_by: [],
|
||||
conn: _,
|
||||
conn_state: :idle,
|
||||
gun_state: :up,
|
||||
retries: 0,
|
||||
used_by: []
|
||||
},
|
||||
"https:2-redirect.com:443" => %Conn{
|
||||
awaited_by: [],
|
||||
conn: _,
|
||||
conn_state: :idle,
|
||||
gun_state: :up,
|
||||
retries: 0,
|
||||
used_by: []
|
||||
}
|
||||
}
|
||||
},
|
||||
Connections.get_state(:gun_connections)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
90
test/http/proxy_test.exs
Normal file
90
test/http/proxy_test.exs
Normal file
@ -0,0 +1,90 @@
|
||||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.HTTP.ProxyTest do
|
||||
use ExUnit.Case, async: true
|
||||
use Pleroma.Tests.Helpers
|
||||
|
||||
import ExUnit.CaptureLog
|
||||
|
||||
alias Pleroma.HTTP.Proxy
|
||||
|
||||
describe "parse_proxy/1" do
|
||||
test "ip with port" do
|
||||
assert Proxy.parse_proxy("127.0.0.1:8123") == {:ok, {127, 0, 0, 1}, 8123}
|
||||
end
|
||||
|
||||
test "host with port" do
|
||||
assert Proxy.parse_proxy("localhost:8123") == {:ok, 'localhost', 8123}
|
||||
end
|
||||
|
||||
test "as tuple" do
|
||||
assert Proxy.parse_proxy({:socks4, :localhost, 9050}) ==
|
||||
{:ok, :socks4, 'localhost', 9050}
|
||||
end
|
||||
|
||||
test "as tuple with string host" do
|
||||
assert Proxy.parse_proxy({:socks5, "localhost", 9050}) ==
|
||||
{:ok, :socks5, 'localhost', 9050}
|
||||
end
|
||||
end
|
||||
|
||||
describe "parse_proxy/1 errors" do
|
||||
test "ip without port" do
|
||||
capture_log(fn ->
|
||||
assert Proxy.parse_proxy("127.0.0.1") == {:error, :invalid_proxy}
|
||||
end) =~ "parsing proxy fail \"127.0.0.1\""
|
||||
end
|
||||
|
||||
test "host without port" do
|
||||
capture_log(fn ->
|
||||
assert Proxy.parse_proxy("localhost") == {:error, :invalid_proxy}
|
||||
end) =~ "parsing proxy fail \"localhost\""
|
||||
end
|
||||
|
||||
test "host with bad port" do
|
||||
capture_log(fn ->
|
||||
assert Proxy.parse_proxy("localhost:port") == {:error, :invalid_proxy_port}
|
||||
end) =~ "parsing port in proxy fail \"localhost:port\""
|
||||
end
|
||||
|
||||
test "ip with bad port" do
|
||||
capture_log(fn ->
|
||||
assert Proxy.parse_proxy("127.0.0.1:15.9") == {:error, :invalid_proxy_port}
|
||||
end) =~ "parsing port in proxy fail \"127.0.0.1:15.9\""
|
||||
end
|
||||
|
||||
test "as tuple without port" do
|
||||
capture_log(fn ->
|
||||
assert Proxy.parse_proxy({:socks5, :localhost}) == {:error, :invalid_proxy}
|
||||
end) =~ "parsing proxy fail {:socks5, :localhost}"
|
||||
end
|
||||
|
||||
test "with nil" do
|
||||
assert Proxy.parse_proxy(nil) == nil
|
||||
end
|
||||
end
|
||||
|
||||
describe "maybe_add_proxy/1" do
|
||||
test "proxy as ip with port" do
|
||||
clear_config([:http, :proxy_url], "127.0.0.1:8123")
|
||||
|
||||
assert Proxy.maybe_add_proxy([]) == [proxy: {{127, 0, 0, 1}, 8123}]
|
||||
end
|
||||
|
||||
test "proxy as localhost with port" do
|
||||
clear_config([:http, :proxy_url], "localhost:8123")
|
||||
assert Proxy.maybe_add_proxy([]) == [proxy: {'localhost', 8123}]
|
||||
end
|
||||
|
||||
test "proxy as tuple" do
|
||||
clear_config([:http, :proxy_url], {:socks4, :localhost, 9050})
|
||||
assert Proxy.maybe_add_proxy([]) == [proxy: {:socks4, 'localhost', 9050}]
|
||||
end
|
||||
|
||||
test "without proxy" do
|
||||
assert Proxy.maybe_add_proxy([]) == []
|
||||
end
|
||||
end
|
||||
end
|
@ -2,26 +2,26 @@
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.HTTP.RequestBuilderTest do
|
||||
defmodule Pleroma.HTTP.Request.BuilderTest do
|
||||
use ExUnit.Case, async: true
|
||||
use Pleroma.Tests.Helpers
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.HTTP.Request
|
||||
alias Pleroma.HTTP.RequestBuilder
|
||||
alias Pleroma.HTTP.Request.Builder
|
||||
|
||||
describe "headers/2" do
|
||||
setup do: clear_config([:http, :send_user_agent])
|
||||
setup do: clear_config([:http, :user_agent])
|
||||
|
||||
test "don't send pleroma user agent" do
|
||||
assert RequestBuilder.headers(%Request{}, []) == %Request{headers: []}
|
||||
assert Builder.headers(%Request{}, []) == %Request{headers: []}
|
||||
end
|
||||
|
||||
test "send pleroma user agent" do
|
||||
Config.put([:http, :send_user_agent], true)
|
||||
Config.put([:http, :user_agent], :default)
|
||||
|
||||
assert RequestBuilder.headers(%Request{}, []) == %Request{
|
||||
assert Builder.headers(%Request{}, []) == %Request{
|
||||
headers: [{"user-agent", Pleroma.Application.user_agent()}]
|
||||
}
|
||||
end
|
||||
@ -30,7 +30,7 @@ defmodule Pleroma.HTTP.RequestBuilderTest do
|
||||
Config.put([:http, :send_user_agent], true)
|
||||
Config.put([:http, :user_agent], "totally-not-pleroma")
|
||||
|
||||
assert RequestBuilder.headers(%Request{}, []) == %Request{
|
||||
assert Builder.headers(%Request{}, []) == %Request{
|
||||
headers: [{"user-agent", "totally-not-pleroma"}]
|
||||
}
|
||||
end
|
||||
@ -55,7 +55,7 @@ defmodule Pleroma.HTTP.RequestBuilderTest do
|
||||
}
|
||||
]
|
||||
}
|
||||
} = RequestBuilder.add_param(%Request{}, :file, "filename.png", "some-path/filename.png")
|
||||
} = Builder.add_param(%Request{}, :file, "filename.png", "some-path/filename.png")
|
||||
end
|
||||
|
||||
test "add key to body" do
|
||||
@ -71,17 +71,17 @@ defmodule Pleroma.HTTP.RequestBuilderTest do
|
||||
}
|
||||
]
|
||||
}
|
||||
} = RequestBuilder.add_param(%{}, :body, "somekey", "someval")
|
||||
} = Builder.add_param(%{}, :body, "somekey", "someval")
|
||||
end
|
||||
|
||||
test "add form parameter" do
|
||||
assert RequestBuilder.add_param(%{}, :form, "somename", "someval") == %{
|
||||
assert Builder.add_param(%{}, :form, "somename", "someval") == %{
|
||||
body: %{"somename" => "someval"}
|
||||
}
|
||||
end
|
||||
|
||||
test "add for location" do
|
||||
assert RequestBuilder.add_param(%{}, :some_location, "somekey", "someval") == %{
|
||||
assert Builder.add_param(%{}, :some_location, "somekey", "someval") == %{
|
||||
some_location: [{"somekey", "someval"}]
|
||||
}
|
||||
end
|
Loading…
Reference in New Issue
Block a user