Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F1037703
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
29 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
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)
Attached To
Mode
rPUBE pleroma-upstream
Attached
Detach File
Event Timeline
Log In to Comment