Compare commits

...

8 commits

13 changed files with 107 additions and 271 deletions

View file

@ -6,6 +6,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased] ## [Unreleased]
### Removed
- Non-finch HTTP adapters. `:tesla, :adapter` is now highly recommended to be set to the default.
## 2022.08
### Added ### Added
- extended runtime module support, see config cheatsheet - extended runtime module support, see config cheatsheet
- quote posting; quotes are limited to public posts - quote posting; quotes are limited to public posts

View file

@ -2598,9 +2598,10 @@
%{ %{
key: :proxy_url, key: :proxy_url,
label: "Proxy URL", label: "Proxy URL",
type: [:string, :tuple], type: :string,
description: "Proxy URL", description:
suggestions: ["localhost:9020", {:socks5, :localhost, 3090}] "Proxy URL - of the format http://host:port. Advise setting in .exs instead of admin-fe due to this being set at boot-time.",
suggestions: ["http://localhost:3128"]
}, },
%{ %{
key: :user_agent, key: :user_agent,

View file

@ -521,7 +521,7 @@ Available caches:
### :http ### :http
* `proxy_url`: an upstream proxy to fetch posts and/or media with, (default: `nil`) * `proxy_url`: an upstream proxy to fetch posts and/or media with, (default: `nil`); for example `http://127.0.0.1:3192`. Does not support SOCKS5 proxy, only http(s).
* `send_user_agent`: should we include a user agent with HTTP requests? (default: `true`) * `send_user_agent`: should we include a user agent with HTTP requests? (default: `true`)
* `user_agent`: what user agent should we use? (default: `:default`), must be string or `:default` * `user_agent`: what user agent should we use? (default: `:default`), must be string or `:default`
* `adapter`: array of adapter options * `adapter`: array of adapter options

View file

@ -49,12 +49,12 @@ you have to get the _private key_, which is actually used for authentication.
=== "OTP" === "OTP"
```sh ```sh
./bin/pleroma_ctl search.meilisearch show-keys <your master key here> ./bin/pleroma_ctl meilisearch show-keys <your master key here>
``` ```
=== "From Source" === "From Source"
```sh ```sh
mix pleroma.search.meilisearch show-keys <your master key here> mix pleroma.meilisearch show-keys <your master key here>
``` ```
You will see a "Default Admin API Key", this is the key you actually put into your configuration file. You will see a "Default Admin API Key", this is the key you actually put into your configuration file.
@ -79,12 +79,12 @@ To start the initial indexing, run the `index` command:
=== "OTP" === "OTP"
```sh ```sh
./bin/pleroma_ctl search.meilisearch index ./bin/pleroma_ctl meilisearch index
``` ```
=== "From Source" === "From Source"
```sh ```sh
mix pleroma.search.meilisearch index mix pleroma.meilisearch index
``` ```
This will show you the total amount of posts to index, and then show you the amount of posts indexed currently, until the numbers eventually This will show you the total amount of posts to index, and then show you the amount of posts indexed currently, until the numbers eventually
@ -94,12 +94,12 @@ of indexing and how many posts have actually been indexed, use the `stats` comma
=== "OTP" === "OTP"
```sh ```sh
./bin/pleroma_ctl search.meilisearch stats ./bin/pleroma_ctl meilisearch stats
``` ```
=== "From Source" === "From Source"
```sh ```sh
mix pleroma.search.meilisearch stats mix pleroma.meilisearch stats
``` ```
### Clearing the index ### Clearing the index
@ -109,12 +109,12 @@ use the `clear` command:
=== "OTP" === "OTP"
```sh ```sh
./bin/pleroma_ctl search.meilisearch clear ./bin/pleroma_ctl meilisearch clear
``` ```
=== "From Source" === "From Source"
```sh ```sh
mix pleroma.search.meilisearch clear mix pleroma.meilisearch clear
``` ```
This will clear **all** the posts from the search index. Note, that deleted posts are also removed from index by the instance itself, so This will clear **all** the posts from the search index. Note, that deleted posts are also removed from index by the instance itself, so

View file

@ -248,9 +248,13 @@ def limiters_setup do
end end
defp http_children do defp http_children do
proxy_url = Config.get([:http, :proxy_url])
proxy = Pleroma.HTTP.AdapterHelper.format_proxy(proxy_url)
config = config =
[:http, :adapter] [:http, :adapter]
|> Config.get([]) |> Config.get([])
|> Pleroma.HTTP.AdapterHelper.maybe_add_proxy_pool(proxy)
|> Keyword.put(:name, MyFinch) |> Keyword.put(:name, MyFinch)
[{Finch, config}] [{Finch, config}]

View file

@ -6,7 +6,7 @@ defmodule Pleroma.HTTP.AdapterHelper do
@moduledoc """ @moduledoc """
Configure Tesla.Client with default and customized adapter options. Configure Tesla.Client with default and customized adapter options.
""" """
@defaults [name: MyFinch, connect_timeout: 5_000, recv_timeout: 5_000] @defaults [name: MyFinch, pool_timeout: 5_000, receive_timeout: 5_000]
@type proxy_type() :: :socks4 | :socks5 @type proxy_type() :: :socks4 | :socks5
@type host() :: charlist() | :inet.ip_address() @type host() :: charlist() | :inet.ip_address()
@ -25,15 +25,58 @@ def format_proxy(nil), do: nil
def format_proxy(proxy_url) do def format_proxy(proxy_url) do
case parse_proxy(proxy_url) do case parse_proxy(proxy_url) do
{:ok, host, port} -> {host, port} {:ok, host, port} -> {:http, host, port, []}
{:ok, type, host, port} -> {type, host, port} {:ok, type, host, port} -> {type, host, port, []}
_ -> nil _ -> nil
end end
end end
@spec maybe_add_proxy(keyword(), proxy() | nil) :: keyword() @spec maybe_add_proxy(keyword(), proxy() | nil) :: keyword()
def maybe_add_proxy(opts, nil), do: opts def maybe_add_proxy(opts, nil), do: opts
def maybe_add_proxy(opts, proxy), do: Keyword.put_new(opts, :proxy, proxy)
def maybe_add_proxy(opts, proxy) do
Keyword.put(opts, :proxy, proxy)
end
def maybe_add_proxy_pool(opts, nil), do: opts
def maybe_add_proxy_pool(opts, proxy) do
Logger.info("Using HTTP Proxy: #{inspect(proxy)}")
opts
|> maybe_add_pools()
|> maybe_add_default_pool()
|> maybe_add_conn_opts()
|> put_in([:pools, :default, :conn_opts, :proxy], proxy)
end
defp maybe_add_pools(opts) do
if Keyword.has_key?(opts, :pools) do
opts
else
Keyword.put(opts, :pools, %{})
end
end
defp maybe_add_default_pool(opts) do
pools = Keyword.get(opts, :pools)
if Map.has_key?(pools, :default) do
opts
else
put_in(opts, [:pools, :default], [])
end
end
defp maybe_add_conn_opts(opts) do
conn_opts = get_in(opts, [:pools, :default, :conn_opts])
unless is_nil(conn_opts) do
opts
else
put_in(opts, [:pools, :default, :conn_opts], [])
end
end
@doc """ @doc """
Merge default connection & adapter options with received ones. Merge default connection & adapter options with received ones.
@ -46,36 +89,31 @@ def options(%URI{} = uri, opts \\ []) do
|> AdapterHelper.Default.options(uri) |> AdapterHelper.Default.options(uri)
end end
defp proxy_type("http"), do: {:ok, :http}
defp proxy_type("https"), do: {:ok, :https}
defp proxy_type(_), do: {:error, :unknown}
@spec parse_proxy(String.t() | tuple() | nil) :: @spec parse_proxy(String.t() | tuple() | nil) ::
{:ok, host(), pos_integer()} {:ok, host(), pos_integer()}
| {:ok, proxy_type(), host(), pos_integer()} | {:ok, proxy_type(), host(), pos_integer()}
| {:error, atom()} | {:error, atom()}
| nil | nil
def parse_proxy(nil), do: nil def parse_proxy(nil), do: nil
def parse_proxy(proxy) when is_binary(proxy) do def parse_proxy(proxy) when is_binary(proxy) do
with [host, port] <- String.split(proxy, ":"), with %URI{} = uri <- URI.parse(proxy),
{port, ""} <- Integer.parse(port) do {:ok, type} <- proxy_type(uri.scheme) do
{:ok, parse_host(host), port} {:ok, type, uri.host, uri.port}
else else
{_, _} -> e ->
Logger.warn("Parsing port failed #{inspect(proxy)}") Logger.warn("Parsing proxy failed #{inspect(proxy)}, #{inspect(e)}")
{: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} {:error, :invalid_proxy}
end end
end end
def parse_proxy(proxy) when is_tuple(proxy) do def parse_proxy(proxy) when is_tuple(proxy) do
with {type, host, port} <- proxy do with {type, host, port} <- proxy do
{:ok, type, parse_host(host), port} {:ok, type, host, port}
else else
_ -> _ ->
Logger.warn("Parsing proxy failed #{inspect(proxy)}") Logger.warn("Parsing proxy failed #{inspect(proxy)}")

View file

@ -9,7 +9,7 @@ defmodule Pleroma.HTTP.AdapterHelper.Default do
@spec options(keyword(), URI.t()) :: keyword() @spec options(keyword(), URI.t()) :: keyword()
def options(opts, _uri) do def options(opts, _uri) do
proxy = Pleroma.Config.get([:http, :proxy_url], nil) proxy = Pleroma.Config.get([:http, :proxy_url])
AdapterHelper.maybe_add_proxy(opts, AdapterHelper.format_proxy(proxy)) AdapterHelper.maybe_add_proxy(opts, AdapterHelper.format_proxy(proxy))
end end

View file

@ -1,82 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.HTTP.AdapterHelper.Gun do
@behaviour Pleroma.HTTP.AdapterHelper
alias Pleroma.Config
alias Pleroma.HTTP.AdapterHelper
require Logger
@defaults [
retry: 1,
retry_timeout: 1_000
]
@type pool() :: :federation | :upload | :media | :default
@spec options(keyword(), URI.t()) :: keyword()
def options(incoming_opts \\ [], %URI{} = uri) do
proxy =
[:http, :proxy_url]
|> Config.get()
|> AdapterHelper.format_proxy()
config_opts = Config.get([:http, :adapter], [])
@defaults
|> Keyword.merge(config_opts)
|> add_scheme_opts(uri)
|> AdapterHelper.maybe_add_proxy(proxy)
|> Keyword.merge(incoming_opts)
|> put_timeout()
end
defp add_scheme_opts(opts, %{scheme: "http"}), do: opts
defp add_scheme_opts(opts, %{scheme: "https"}) do
Keyword.put(opts, :certificates_verification, true)
end
defp put_timeout(opts) do
{recv_timeout, opts} = Keyword.pop(opts, :recv_timeout, pool_timeout(opts[:pool]))
# this is the timeout to receive a message from Gun
# `:timeout` key is used in Tesla
Keyword.put(opts, :timeout, recv_timeout)
end
@spec pool_timeout(pool()) :: non_neg_integer()
def pool_timeout(pool) do
default = Config.get([:pools, :default, :recv_timeout], 5_000)
Config.get([:pools, pool, :recv_timeout], default)
end
def limiter_setup do
prefix = Pleroma.Gun.ConnectionPool
wait = Config.get([:connections_pool, :connection_acquisition_wait])
retries = Config.get([:connections_pool, :connection_acquisition_retries])
:pools
|> Config.get([])
|> Enum.each(fn {name, opts} ->
max_running = Keyword.get(opts, :size, 50)
max_waiting = Keyword.get(opts, :max_waiting, 10)
result =
ConcurrentLimiter.new(:"#{prefix}.#{name}", max_running, max_waiting,
wait: wait,
max_retries: retries
)
case result do
:ok -> :ok
{:error, :existing} -> :ok
end
end)
:ok
end
end

View file

@ -1,40 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.HTTP.AdapterHelper.Hackney do
@behaviour Pleroma.HTTP.AdapterHelper
@defaults [
follow_redirect: true,
force_redirect: true
]
@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)
|> maybe_add_with_body()
|> Pleroma.HTTP.AdapterHelper.maybe_add_proxy(proxy)
end
defp add_scheme_opts(opts, %URI{scheme: "https"}) do
Keyword.put(opts, :ssl_options, versions: [:"tlsv1.3", :"tlsv1.2", :"tlsv1.1", :tlsv1])
end
defp add_scheme_opts(opts, _), do: opts
defp maybe_add_with_body(opts) do
if opts[:max_body] do
Keyword.put(opts, :with_body, true)
else
opts
end
end
end

View file

@ -4,7 +4,7 @@ defmodule Pleroma.Mixfile do
def project do def project do
[ [
app: :pleroma, app: :pleroma,
version: version("3.0.1"), version: version("3.1.0"),
elixir: "~> 1.9", elixir: "~> 1.9",
elixirc_paths: elixirc_paths(Mix.env()), elixirc_paths: elixirc_paths(Mix.env()),
compilers: [:phoenix, :gettext] ++ Mix.compilers(), compilers: [:phoenix, :gettext] ++ Mix.compilers(),

View file

@ -1,77 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 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.HTTP.AdapterHelper.Gun
setup :verify_on_exit!
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]
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]
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]
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 "parses string proxy host & port" do
clear_config([:http, :proxy_url], "localhost:8123")
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
clear_config([:http, :proxy_url], {:socks, 'localhost', 1234})
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
clear_config([:http, :proxy_url], {:socks5, 'localhost', 1234})
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
end

View file

@ -1,35 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.HTTP.AdapterHelper.HackneyTest do
use ExUnit.Case, async: true
use Pleroma.Tests.Helpers
alias Pleroma.HTTP.AdapterHelper.Hackney
setup_all do
uri = URI.parse("http://domain.com")
{:ok, uri: uri}
end
describe "options/2" do
setup do: clear_config([:http, :adapter], a: 1, b: 2)
test "add proxy and opts from config", %{uri: uri} do
opts = Hackney.options([proxy: "localhost:8123"], uri)
assert opts[:a] == 1
assert opts[:b] == 2
assert opts[:proxy] == "localhost:8123"
end
test "respect connection opts and no proxy", %{uri: uri} do
opts = Hackney.options([a: 2, b: 1], uri)
assert opts[:a] == 2
assert opts[:b] == 1
refute Keyword.has_key?(opts, :proxy)
end
end
end

View file

@ -13,16 +13,38 @@ test "with nil" do
end end
test "with string" do test "with string" do
assert AdapterHelper.format_proxy("127.0.0.1:8123") == {{127, 0, 0, 1}, 8123} assert AdapterHelper.format_proxy("http://127.0.0.1:8123") == {:http, "127.0.0.1", 8123, []}
end end
test "localhost with port" do test "localhost with port" do
assert AdapterHelper.format_proxy("localhost:8123") == {'localhost', 8123} assert AdapterHelper.format_proxy("https://localhost:8123") ==
{:https, "localhost", 8123, []}
end end
test "tuple" do test "tuple" do
assert AdapterHelper.format_proxy({:socks4, :localhost, 9050}) == assert AdapterHelper.format_proxy({:http, "localhost", 9050}) ==
{:socks4, 'localhost', 9050} {:http, "localhost", 9050, []}
end
end
describe "maybe_add_proxy_pool/1" do
test "should do nothing with nil" do
assert AdapterHelper.maybe_add_proxy_pool([], nil) == []
end
test "should create pools" do
assert AdapterHelper.maybe_add_proxy_pool([], "proxy") == [
pools: %{default: [conn_opts: [proxy: "proxy"]]}
]
end
test "should not override conn_opts if set" do
assert AdapterHelper.maybe_add_proxy_pool(
[pools: %{default: [conn_opts: [already: "set"]]}],
"proxy"
) == [
pools: %{default: [conn_opts: [proxy: "proxy", already: "set"]]}
]
end end
end end
end end