Page MenuHomePhorge

No OneTemporary

Size
29 KB
Referenced Files
None
Subscribers
None
diff --git a/lib/pleroma/gun/api/api.ex b/lib/pleroma/gun/api/api.ex
index 7e6d2f929..43ee7f354 100644
--- a/lib/pleroma/gun/api/api.ex
+++ b/lib/pleroma/gun/api/api.ex
@@ -1,25 +1,26 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Gun.API do
@callback open(charlist(), pos_integer(), map()) :: {:ok, pid()}
@callback info(pid()) :: map()
@callback close(pid()) :: :ok
+ @callback await_up(pid) :: {:ok, atom()} | {:error, atom()}
+ @callback connect(pid(), map()) :: reference()
+ @callback await(pid(), reference()) :: {:response, :fin, 200, []}
- def open(host, port, opts) do
- api().open(host, port, opts)
- end
+ def open(host, port, opts), do: api().open(host, port, opts)
- def info(pid) do
- api().info(pid)
- end
+ def info(pid), do: api().info(pid)
- def close(pid) do
- api().close(pid)
- end
+ def close(pid), do: api().close(pid)
- defp api do
- Pleroma.Config.get([Pleroma.Gun.API], Pleroma.Gun.API.Gun)
- end
+ def await_up(pid), do: api().await_up(pid)
+
+ def connect(pid, opts), do: api().connect(pid, opts)
+
+ def await(pid, ref), do: api().await(pid, ref)
+
+ defp api, do: Pleroma.Config.get([Pleroma.Gun.API], Pleroma.Gun.API.Gun)
end
diff --git a/lib/pleroma/gun/api/gun.ex b/lib/pleroma/gun/api/gun.ex
index d97f5a7c9..603dd700e 100644
--- a/lib/pleroma/gun/api/gun.ex
+++ b/lib/pleroma/gun/api/gun.ex
@@ -1,34 +1,43 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Gun.API.Gun do
@behaviour Pleroma.Gun.API
alias Pleroma.Gun.API
@gun_keys [
:connect_timeout,
:http_opts,
:http2_opts,
:protocols,
:retry,
:retry_timeout,
:trace,
:transport,
:tls_opts,
:tcp_opts,
:ws_opts
]
@impl API
def open(host, port, opts) do
:gun.open(host, port, Map.take(opts, @gun_keys))
end
@impl API
def info(pid), do: :gun.info(pid)
@impl API
def close(pid), do: :gun.close(pid)
+
+ @impl API
+ def await_up(pid), do: :gun.await_up(pid)
+
+ @impl API
+ def connect(pid, opts), do: :gun.connect(pid, opts)
+
+ @impl API
+ def await(pid, ref), do: :gun.await(pid, ref)
end
diff --git a/lib/pleroma/gun/api/mock.ex b/lib/pleroma/gun/api/mock.ex
index b1a30a73c..5e1bb8abc 100644
--- a/lib/pleroma/gun/api/mock.ex
+++ b/lib/pleroma/gun/api/mock.ex
@@ -1,83 +1,118 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Gun.API.Mock do
@behaviour Pleroma.Gun.API
alias Pleroma.Gun.API
@impl API
def open(domain, 80, %{genserver_pid: genserver_pid})
when domain in ['another-domain.com', 'some-domain.com'] do
{:ok, conn_pid} = Task.start_link(fn -> Process.sleep(1_000) end)
Registry.register(API.Mock, conn_pid, %{
origin_scheme: "http",
origin_host: domain,
origin_port: 80
})
send(genserver_pid, {:gun_up, conn_pid, :http})
{:ok, conn_pid}
end
@impl API
def open('some-domain.com', 443, %{genserver_pid: genserver_pid}) do
{:ok, conn_pid} = Task.start_link(fn -> Process.sleep(1_000) end)
Registry.register(API.Mock, conn_pid, %{
origin_scheme: "https",
origin_host: 'some-domain.com',
origin_port: 443
})
send(genserver_pid, {:gun_up, conn_pid, :http2})
{:ok, conn_pid}
end
@impl API
def open('gun_down.com', 80, %{genserver_pid: genserver_pid}) do
{:ok, conn_pid} = Task.start_link(fn -> Process.sleep(1_000) end)
Registry.register(API.Mock, conn_pid, %{
origin_scheme: "http",
origin_host: 'gun_down.com',
origin_port: 80
})
send(genserver_pid, {:gun_down, conn_pid, :http, nil, nil, nil})
{:ok, conn_pid}
end
@impl API
def open('gun_down_and_up.com', 80, %{genserver_pid: genserver_pid}) do
{:ok, conn_pid} = Task.start_link(fn -> Process.sleep(1_000) end)
Registry.register(API.Mock, conn_pid, %{
origin_scheme: "http",
origin_host: 'gun_down_and_up.com',
origin_port: 80
})
send(genserver_pid, {:gun_down, conn_pid, :http, nil, nil, nil})
{:ok, _} =
Task.start_link(fn ->
Process.sleep(500)
send(genserver_pid, {:gun_up, conn_pid, :http})
end)
{:ok, conn_pid}
end
+ @impl API
+ def open({127, 0, 0, 1}, 8123, _) do
+ Task.start_link(fn -> Process.sleep(1_000) end)
+ end
+
+ @impl API
+ def open('localhost', 9050, _) do
+ Task.start_link(fn -> Process.sleep(1_000) end)
+ end
+
+ @impl API
+ def await_up(_pid) do
+ {:ok, :http}
+ end
+
+ @impl API
+ def connect(pid, %{host: _, port: 80}) do
+ ref = make_ref()
+ Registry.register(API.Mock, ref, pid)
+ ref
+ end
+
+ @impl API
+ def connect(pid, %{host: _, port: 443, protocols: [:http2], transport: :tls}) do
+ ref = make_ref()
+ Registry.register(API.Mock, ref, pid)
+ ref
+ end
+
+ @impl API
+ def await(pid, ref) do
+ [{_, ^pid}] = Registry.lookup(API.Mock, ref)
+ {:response, :fin, 200, []}
+ end
+
@impl API
def info(pid) do
[{_, info}] = Registry.lookup(API.Mock, pid)
info
end
@impl API
def close(_pid), do: :ok
end
diff --git a/lib/pleroma/gun/connections.ex b/lib/pleroma/gun/connections.ex
index 3716d9f74..6cec4277a 100644
--- a/lib/pleroma/gun/connections.ex
+++ b/lib/pleroma/gun/connections.ex
@@ -1,167 +1,223 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Gun.Connections do
use GenServer
+ require Logger
@type domain :: String.t()
@type conn :: Pleroma.Gun.Conn.t()
@type t :: %__MODULE__{
conns: %{domain() => conn()},
opts: keyword()
}
defstruct conns: %{}, opts: []
alias Pleroma.Gun.API
@spec start_link({atom(), keyword()}) :: {:ok, pid()} | :ignore
def start_link({name, opts}) do
GenServer.start_link(__MODULE__, opts, name: name)
end
@impl true
def init(opts), do: {:ok, %__MODULE__{conns: %{}, opts: opts}}
@spec get_conn(String.t(), keyword(), atom()) :: pid()
def get_conn(url, opts \\ [], name \\ :default) do
opts = Enum.into(opts, %{})
uri = URI.parse(url)
opts =
if uri.scheme == "https" and uri.port != 443,
do: Map.put(opts, :transport, :tls),
else: opts
opts =
if uri.scheme == "https" do
host = uri.host |> to_charlist()
tls_opts =
Map.get(opts, :tls_opts, [])
|> Keyword.put(:server_name_indication, host)
Map.put(opts, :tls_opts, tls_opts)
else
opts
end
GenServer.call(
name,
{:conn, %{opts: opts, uri: uri}}
)
end
@spec alive?(atom()) :: boolean()
def alive?(name \\ :default) do
pid = Process.whereis(name)
if pid, do: Process.alive?(pid), else: false
end
@spec get_state(atom()) :: t()
def get_state(name \\ :default) do
GenServer.call(name, {:state})
end
@impl true
def handle_call({:conn, %{opts: opts, uri: uri}}, from, state) do
key = compose_key(uri)
case state.conns[key] do
%{conn: conn, state: conn_state, used: used} when conn_state == :up ->
state = put_in(state.conns[key].used, used + 1)
{:reply, conn, state}
%{state: conn_state, waiting_pids: pids} when conn_state in [:open, :down] ->
state = put_in(state.conns[key].waiting_pids, [from | pids])
{:noreply, state}
nil ->
max_connections = state.opts[:max_connections]
if Enum.count(state.conns) < max_connections do
open_conn(key, uri, from, state, opts)
else
[{close_key, least_used} | _conns] = Enum.sort_by(state.conns, fn {_k, v} -> v.used end)
:ok = API.close(least_used.conn)
state =
put_in(
state.conns,
Map.delete(state.conns, close_key)
)
open_conn(key, uri, from, state, opts)
end
end
end
@impl true
def handle_call({:state}, _from, state), do: {:reply, state, state}
@impl true
def handle_info({:gun_up, conn_pid, _protocol}, state) do
conn_key = compose_key_gun_info(conn_pid)
{key, conn} = find_conn(state.conns, conn_pid, conn_key)
# Send to all waiting processes connection pid
Enum.each(conn.waiting_pids, fn waiting_pid -> GenServer.reply(waiting_pid, conn_pid) end)
# Update state of the current connection and set waiting_pids to empty list
state =
put_in(state.conns[key], %{
conn
| state: :up,
waiting_pids: [],
used: conn.used + length(conn.waiting_pids)
})
{:noreply, state}
end
@impl true
def handle_info({:gun_down, conn_pid, _protocol, _reason, _killed, _unprocessed}, state) do
# we can't get info on this pid, because pid is dead
{key, conn} = find_conn(state.conns, conn_pid)
Enum.each(conn.waiting_pids, fn waiting_pid -> GenServer.reply(waiting_pid, nil) end)
state = put_in(state.conns[key].state, :down)
{:noreply, state}
end
defp compose_key(uri), do: "#{uri.scheme}:#{uri.host}:#{uri.port}"
defp compose_key_gun_info(pid) do
info = API.info(pid)
"#{info.origin_scheme}:#{info.origin_host}:#{info.origin_port}"
end
defp find_conn(conns, conn_pid) do
Enum.find(conns, fn {_key, conn} ->
conn.conn == conn_pid
end)
end
defp find_conn(conns, conn_pid, conn_key) do
Enum.find(conns, fn {key, conn} ->
key == conn_key and conn.conn == conn_pid
end)
end
defp open_conn(key, uri, from, state, opts) do
- {:ok, conn} = API.open(to_charlist(uri.host), uri.port, opts)
+ host = to_charlist(uri.host)
+ port = uri.port
+
+ result =
+ if opts[:proxy] do
+ with {proxy_host, proxy_port} <- opts[:proxy],
+ {:ok, conn} <- API.open(proxy_host, proxy_port, opts),
+ {:ok, _} <- API.await_up(conn) do
+ connect_opts = %{host: host, port: port}
+
+ connect_opts =
+ if uri.scheme == "https" do
+ Map.put(connect_opts, :protocols, [:http2])
+ |> Map.put(:transport, :tls)
+ else
+ connect_opts
+ end
+
+ with stream <- API.connect(conn, connect_opts),
+ {:response, :fin, 200, _} <- API.await(conn, stream) do
+ {:ok, conn, true}
+ end
+ else
+ {:error, error} ->
+ {:error, error}
- state =
- put_in(state.conns[key], %Pleroma.Gun.Conn{
- conn: conn,
- waiting_pids: [from]
- })
+ error ->
+ Logger.warn(inspect(error))
+ {:error, :error_connection_to_proxy}
+ end
+ else
+ with {:ok, conn} <- API.open(host, port, opts) do
+ {:ok, conn, false}
+ else
+ {:error, error} ->
+ {:error, error}
- {:noreply, state}
+ error ->
+ Logger.warn(inspect(error))
+ {:error, :error_connection}
+ end
+ end
+
+ case result do
+ {:ok, conn, is_up} ->
+ {from_list, used, conn_state} = if is_up, do: {[], 1, :up}, else: {[from], 0, :open}
+
+ state =
+ put_in(state.conns[key], %Pleroma.Gun.Conn{
+ conn: conn,
+ waiting_pids: from_list,
+ used: used,
+ state: conn_state
+ })
+
+ if is_up do
+ {:reply, conn, state}
+ else
+ {:noreply, state}
+ end
+
+ {:error, _error} ->
+ {:reply, nil, state}
+ end
end
end
diff --git a/lib/pleroma/http/connection.ex b/lib/pleroma/http/connection.ex
index fbf135bf9..39c0fff43 100644
--- a/lib/pleroma/http/connection.ex
+++ b/lib/pleroma/http/connection.ex
@@ -1,78 +1,132 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.HTTP.Connection do
@moduledoc """
Connection for http-requests.
"""
@options [
connect_timeout: 10_000,
timeout: 20_000,
pool: :federation,
version: :master
]
+ require Logger
+
@doc """
Configure a client connection
# Returns
Tesla.Env.client
"""
@spec new(Keyword.t()) :: Tesla.Env.client()
def new(opts \\ []) do
middleware = [Tesla.Middleware.FollowRedirects]
adapter = Application.get_env(:tesla, :adapter)
Tesla.client(middleware, {adapter, options(opts)})
end
# fetch http options
#
def options(opts) do
options = Keyword.get(opts, :adapter, [])
adapter_options = Pleroma.Config.get([:http, :adapter], [])
+
proxy_url = Pleroma.Config.get([:http, :proxy_url], nil)
+ proxy =
+ case parse_proxy(proxy_url) do
+ {:ok, proxy_host, proxy_port} -> {proxy_host, proxy_port}
+ _ -> nil
+ end
+
options =
@options
|> Keyword.merge(adapter_options)
|> Keyword.merge(options)
- |> Keyword.merge(proxy: proxy_url)
+ |> Keyword.merge(proxy: proxy)
pool = options[:pool]
url = options[:url]
if not is_nil(url) and not is_nil(pool) and Pleroma.Gun.Connections.alive?(pool) do
get_conn_for_gun(url, options, pool)
else
options
end
end
defp get_conn_for_gun(url, options, pool) do
case Pleroma.Gun.Connections.get_conn(url, options, pool) do
nil ->
options
conn ->
%{host: host, port: port} = URI.parse(url)
# verify sertificates opts for gun
tls_opts = [
verify: :verify_peer,
cacerts: :certifi.cacerts(),
depth: 20,
server_name_indication: to_charlist(host),
reuse_sessions: false,
verify_fun: {&:ssl_verify_hostname.verify_fun/3, [check_hostname: to_charlist(host)]}
]
Keyword.put(options, :conn, conn)
|> Keyword.put(:close_conn, false)
|> Keyword.put(:original, "#{host}:#{port}")
|> Keyword.put(:tls_opts, tls_opts)
end
end
+
+ @spec parse_proxy(String.t() | tuple() | nil) ::
+ {tuple, 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 in proxy fail #{inspect(proxy)}")
+ {:error, :error_parsing_port_in_proxy}
+
+ :error ->
+ Logger.warn("parsing port in proxy fail #{inspect(proxy)}")
+ {:error, :error_parsing_port_in_proxy}
+
+ _ ->
+ Logger.warn("parsing proxy fail #{inspect(proxy)}")
+ {:error, :error_parsing_proxy}
+ end
+ end
+
+ def parse_proxy(proxy) when is_tuple(proxy) do
+ with {_type, host, port} <- proxy do
+ {:ok, parse_host(host), port}
+ else
+ _ ->
+ Logger.warn("parsing proxy fail #{inspect(proxy)}")
+ {:error, :error_parsing_proxy}
+ end
+ end
+
+ @spec parse_host(String.t() | tuple()) :: charlist() | atom()
+ def parse_host(host) when is_atom(host), do: to_charlist(host)
+
+ def parse_host(host) when is_binary(host) do
+ host = to_charlist(host)
+
+ case :inet.parse_address(host) do
+ {:error, :einval} -> host
+ {:ok, ip} -> ip
+ end
+ end
end
diff --git a/test/gun/connections_test.exs b/test/gun/connections_test.exs
index 1e41e771b..4d84821a0 100644
--- a/test/gun/connections_test.exs
+++ b/test/gun/connections_test.exs
@@ -1,318 +1,440 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Gun.ConnectionsTest do
use ExUnit.Case
alias Pleroma.Gun.API
alias Pleroma.Gun.Conn
alias Pleroma.Gun.Connections
setup_all do
{:ok, _} = Registry.start_link(keys: :unique, name: API.Mock)
:ok
end
setup do
name = :test_gun_connections
adapter = Application.get_env(:tesla, :adapter)
Application.put_env(:tesla, :adapter, Tesla.Adapter.Gun)
on_exit(fn -> Application.put_env(:tesla, :adapter, adapter) end)
{:ok, pid} = Connections.start_link({name, [max_connections: 2, timeout: 10]})
{:ok, name: name, pid: pid}
end
describe "alive?/2" do
test "is alive", %{name: name} do
assert Connections.alive?(name)
end
test "returns false if not started" do
refute Connections.alive?(:some_random_name)
end
end
test "opens connection and reuse it on next request", %{name: name, pid: pid} do
conn = Connections.get_conn("http://some-domain.com", [genserver_pid: pid], name)
assert is_pid(conn)
assert Process.alive?(conn)
reused_conn = Connections.get_conn("http://some-domain.com", [genserver_pid: pid], name)
assert conn == reused_conn
%Connections{
conns: %{
"http:some-domain.com:80" => %Conn{
conn: ^conn,
state: :up,
waiting_pids: [],
used: 2
}
}
} = Connections.get_state(name)
end
test "reuses connection based on protocol", %{name: name, pid: pid} do
conn = Connections.get_conn("http://some-domain.com", [genserver_pid: pid], name)
assert is_pid(conn)
assert Process.alive?(conn)
https_conn = Connections.get_conn("https://some-domain.com", [genserver_pid: pid], name)
refute conn == https_conn
reused_https = Connections.get_conn("https://some-domain.com", [genserver_pid: pid], name)
refute conn == reused_https
assert reused_https == https_conn
%Connections{
conns: %{
"http:some-domain.com:80" => %Conn{
conn: ^conn,
state: :up,
waiting_pids: []
},
"https:some-domain.com:443" => %Conn{
conn: ^https_conn,
state: :up,
waiting_pids: []
}
}
} = Connections.get_state(name)
end
test "process gun_down message", %{name: name, pid: pid} do
conn = Connections.get_conn("http://gun_down.com", [genserver_pid: pid], name)
refute conn
%Connections{
conns: %{
"http:gun_down.com:80" => %Conn{
conn: _,
state: :down,
waiting_pids: _
}
}
} = Connections.get_state(name)
end
test "process gun_down message and then gun_up", %{name: name, pid: pid} do
conn = Connections.get_conn("http://gun_down_and_up.com", [genserver_pid: pid], name)
refute conn
%Connections{
conns: %{
"http:gun_down_and_up.com:80" => %Conn{
conn: _,
state: :down,
waiting_pids: _,
used: 0
}
}
} = Connections.get_state(name)
conn = Connections.get_conn("http://gun_down_and_up.com", [genserver_pid: pid], name)
assert is_pid(conn)
assert Process.alive?(conn)
%Connections{
conns: %{
"http:gun_down_and_up.com:80" => %Conn{
conn: _,
state: :up,
waiting_pids: [],
used: 2
}
}
} = Connections.get_state(name)
end
test "async processes get same conn for same domain", %{name: name, pid: pid} do
tasks =
for _ <- 1..5 do
Task.async(fn ->
Connections.get_conn("http://some-domain.com", [genserver_pid: pid], name)
end)
end
tasks_with_results = Task.yield_many(tasks)
results =
Enum.map(tasks_with_results, fn {task, res} ->
res || Task.shutdown(task, :brutal_kill)
end)
conns = for {:ok, value} <- results, do: value
%Connections{
conns: %{
"http:some-domain.com:80" => %Conn{
conn: conn,
state: :up,
waiting_pids: [],
used: 5
}
}
} = Connections.get_state(name)
assert Enum.all?(conns, fn res -> res == conn end)
end
test "remove frequently used", %{name: name, pid: pid} do
Connections.get_conn("https://some-domain.com", [genserver_pid: pid], name)
for _ <- 1..4 do
Connections.get_conn("http://some-domain.com", [genserver_pid: pid], name)
end
%Connections{
conns: %{
"http:some-domain.com:80" => %Conn{
conn: _,
state: :up,
waiting_pids: [],
used: 4
},
"https:some-domain.com:443" => %Conn{
conn: _,
state: :up,
waiting_pids: [],
used: 1
}
},
opts: [max_connections: 2, timeout: 10]
} = Connections.get_state(name)
conn = Connections.get_conn("http://another-domain.com", [genserver_pid: pid], name)
%Connections{
conns: %{
"http:another-domain.com:80" => %Conn{
conn: ^conn,
state: :up,
waiting_pids: [],
used: 1
},
"http:some-domain.com:80" => %Conn{
conn: _,
state: :up,
waiting_pids: [],
used: 4
}
},
opts: [max_connections: 2, timeout: 10]
} = Connections.get_state(name)
end
describe "integration test" do
@describetag :integration
test "opens connection and reuse it on next request", %{name: name} do
api = Pleroma.Config.get([API])
Pleroma.Config.put([API], API.Gun)
on_exit(fn -> Pleroma.Config.put([API], api) end)
conn = Connections.get_conn("http://httpbin.org", [], name)
assert is_pid(conn)
assert Process.alive?(conn)
reused_conn = Connections.get_conn("http://httpbin.org", [], name)
assert conn == reused_conn
%Connections{
conns: %{
"http:httpbin.org:80" => %Conn{
conn: ^conn,
state: :up,
waiting_pids: [],
used: 2
}
}
} = Connections.get_state(name)
end
test "opens ssl connection and reuse it on next request", %{name: name} do
api = Pleroma.Config.get([API])
Pleroma.Config.put([API], API.Gun)
on_exit(fn -> Pleroma.Config.put([API], api) end)
conn = Connections.get_conn("https://httpbin.org", [], name)
assert is_pid(conn)
assert Process.alive?(conn)
reused_conn = Connections.get_conn("https://httpbin.org", [], name)
assert conn == reused_conn
%Connections{
conns: %{
"https:httpbin.org:443" => %Conn{
conn: ^conn,
state: :up,
waiting_pids: [],
used: 2
}
}
} = Connections.get_state(name)
end
test "remove frequently used", %{name: name, pid: pid} do
api = Pleroma.Config.get([API])
Pleroma.Config.put([API], API.Gun)
on_exit(fn -> Pleroma.Config.put([API], api) end)
Connections.get_conn("https://www.google.com", [genserver_pid: pid], name)
for _ <- 1..4 do
Connections.get_conn("https://httpbin.org", [genserver_pid: pid], name)
end
%Connections{
conns: %{
"https:httpbin.org:443" => %Conn{
conn: _,
state: :up,
waiting_pids: [],
used: 4
},
"https:www.google.com:443" => %Conn{
conn: _,
state: :up,
waiting_pids: [],
used: 1
}
},
opts: [max_connections: 2, timeout: 10]
} = Connections.get_state(name)
conn = Connections.get_conn("http://httpbin.org", [genserver_pid: pid], name)
%Connections{
conns: %{
"http:httpbin.org:80" => %Conn{
conn: ^conn,
state: :up,
waiting_pids: [],
used: 1
},
"https:httpbin.org:443" => %Conn{
conn: _,
state: :up,
waiting_pids: [],
used: 4
}
},
opts: [max_connections: 2, timeout: 10]
} = Connections.get_state(name)
end
end
+
+ describe "with proxy usage" do
+ test "proxy as ip", %{name: name, pid: pid} do
+ conn =
+ Connections.get_conn(
+ "http://proxy_string.com",
+ [genserver_pid: pid, proxy: {{127, 0, 0, 1}, 8123}],
+ name
+ )
+
+ %Connections{
+ conns: %{
+ "http:proxy_string.com:80" => %Conn{
+ conn: ^conn,
+ state: :up,
+ waiting_pids: [],
+ used: 1
+ }
+ },
+ opts: [max_connections: 2, timeout: 10]
+ } = Connections.get_state(name)
+
+ reused_conn =
+ Connections.get_conn(
+ "http://proxy_string.com",
+ [genserver_pid: pid, proxy: {{127, 0, 0, 1}, 8123}],
+ name
+ )
+
+ assert reused_conn == conn
+ end
+
+ test "proxy as host", %{name: name, pid: pid} do
+ conn =
+ Connections.get_conn(
+ "http://proxy_tuple_atom.com",
+ [genserver_pid: pid, proxy: {'localhost', 9050}],
+ name
+ )
+
+ %Connections{
+ conns: %{
+ "http:proxy_tuple_atom.com:80" => %Conn{
+ conn: ^conn,
+ state: :up,
+ waiting_pids: [],
+ used: 1
+ }
+ },
+ opts: [max_connections: 2, timeout: 10]
+ } = Connections.get_state(name)
+
+ reused_conn =
+ Connections.get_conn(
+ "http://proxy_tuple_atom.com",
+ [genserver_pid: pid, proxy: {'localhost', 9050}],
+ name
+ )
+
+ assert reused_conn == conn
+ end
+
+ test "proxy as ip and ssl", %{name: name, pid: pid} do
+ conn =
+ Connections.get_conn(
+ "https://proxy_string.com",
+ [genserver_pid: pid, proxy: {{127, 0, 0, 1}, 8123}],
+ name
+ )
+
+ %Connections{
+ conns: %{
+ "https:proxy_string.com:443" => %Conn{
+ conn: ^conn,
+ state: :up,
+ waiting_pids: [],
+ used: 1
+ }
+ },
+ opts: [max_connections: 2, timeout: 10]
+ } = Connections.get_state(name)
+
+ reused_conn =
+ Connections.get_conn(
+ "https://proxy_string.com",
+ [genserver_pid: pid, proxy: {{127, 0, 0, 1}, 8123}],
+ name
+ )
+
+ assert reused_conn == conn
+ end
+
+ test "proxy as host and ssl", %{name: name, pid: pid} do
+ conn =
+ Connections.get_conn(
+ "https://proxy_tuple_atom.com",
+ [genserver_pid: pid, proxy: {'localhost', 9050}],
+ name
+ )
+
+ %Connections{
+ conns: %{
+ "https:proxy_tuple_atom.com:443" => %Conn{
+ conn: ^conn,
+ state: :up,
+ waiting_pids: [],
+ used: 1
+ }
+ },
+ opts: [max_connections: 2, timeout: 10]
+ } = Connections.get_state(name)
+
+ reused_conn =
+ Connections.get_conn(
+ "https://proxy_tuple_atom.com",
+ [genserver_pid: pid, proxy: {'localhost', 9050}],
+ name
+ )
+
+ assert reused_conn == conn
+ end
+ end
end
diff --git a/test/http/connection_test.exs b/test/http/connection_test.exs
new file mode 100644
index 000000000..99eab4026
--- /dev/null
+++ b/test/http/connection_test.exs
@@ -0,0 +1,65 @@
+defmodule Pleroma.HTTP.ConnectionTest do
+ use ExUnit.Case, async: true
+ import ExUnit.CaptureLog
+ alias Pleroma.HTTP.Connection
+
+ describe "parse_host/1" do
+ test "as atom" do
+ assert Connection.parse_host(:localhost) == 'localhost'
+ end
+
+ test "as string" do
+ assert Connection.parse_host("localhost.com") == 'localhost.com'
+ end
+
+ test "as string ip" do
+ assert Connection.parse_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({:socks5, :localhost, 9050}) == {:ok, 'localhost', 9050}
+ end
+
+ test "as tuple with string host" do
+ assert Connection.parse_proxy({:socks5, "localhost", 9050}) == {:ok, 'localhost', 9050}
+ end
+
+ test "ip without port" do
+ capture_log(fn ->
+ assert Connection.parse_proxy("127.0.0.1") == {:error, :error_parsing_proxy}
+ end) =~ "parsing proxy fail \"127.0.0.1\""
+ end
+
+ test "host without port" do
+ capture_log(fn ->
+ assert Connection.parse_proxy("localhost") == {:error, :error_parsing_proxy}
+ end) =~ "parsing proxy fail \"localhost\""
+ end
+
+ test "host with bad port" do
+ capture_log(fn ->
+ assert Connection.parse_proxy("localhost:port") == {:error, :error_parsing_port_in_proxy}
+ end) =~ "parsing port in proxy fail \"localhost:port\""
+ end
+
+ test "as tuple without port" do
+ capture_log(fn ->
+ assert Connection.parse_proxy({:socks5, :localhost}) == {:error, :error_parsing_proxy}
+ end) =~ "parsing proxy fail {:socks5, :localhost}"
+ end
+
+ test "with nil" do
+ assert Connection.parse_proxy(nil) == nil
+ end
+ end
+end

File Metadata

Mime Type
text/x-diff
Expires
Wed, May 14, 7:42 AM (1 d, 16 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
166797
Default Alt Text
(29 KB)

Event Timeline