Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85823815
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Authored By
Unknown
Size
9 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/lib/pleroma/http.ex b/lib/pleroma/http.ex
index 1833f5f85..75570d281 100644
--- a/lib/pleroma/http.ex
+++ b/lib/pleroma/http.ex
@@ -1,128 +1,137 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.HTTP do
@moduledoc """
Wrapper for `Tesla.request/2`.
"""
alias Pleroma.HTTP.AdapterHelper
alias Pleroma.HTTP.Request
alias Pleroma.HTTP.RequestBuilder, as: Builder
alias Tesla.Client
alias Tesla.Env
require Logger
@type t :: __MODULE__
@type method() :: :get | :post | :put | :delete | :head
@doc """
Performs GET request.
See `Pleroma.HTTP.request/5`
"""
@spec get(Request.url() | nil, Request.headers(), keyword()) ::
nil | {:ok, Env.t()} | {:error, any()}
def get(url, headers \\ [], options \\ [])
def get(nil, _, _), do: nil
def get(url, headers, options), do: request(:get, url, "", headers, options)
@spec head(Request.url(), Request.headers(), keyword()) :: {:ok, Env.t()} | {:error, any()}
def head(url, headers \\ [], options \\ []), do: request(:head, url, "", headers, options)
@doc """
Performs POST request.
See `Pleroma.HTTP.request/5`
"""
@spec post(Request.url(), Tesla.Env.body(), Request.headers(), keyword()) ::
{:ok, Env.t()} | {:error, any()}
def post(url, body, headers \\ [], options \\ []),
do: request(:post, url, body, headers, options)
@doc """
Builds and performs http request.
# Arguments:
`method` - :get, :post, :put, :delete, :head
`url` - full url
`body` - request body
`headers` - a keyworld list of headers, e.g. `[{"content-type", "text/plain"}]`
`options` - custom, per-request middleware or adapter options
# Returns:
`{:ok, %Tesla.Env{}}` or `{:error, error}`
"""
@spec request(method(), Request.url(), Tesla.Env.body(), Request.headers(), keyword()) ::
{:ok, Env.t()} | {:error, any()}
def request(method, url, body, headers, options) when is_binary(url) do
uri = URI.parse(url)
adapter_opts = AdapterHelper.options(uri, options || [])
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)
extra_middleware = options[:tesla_middleware] || []
client = Tesla.client(adapter_middlewares(adapter, extra_middleware), adapter)
maybe_limit(
fn ->
request(client, request)
end,
adapter,
adapter_opts
)
end
@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
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
@prefix Pleroma.Gun.ConnectionPool
defp maybe_limit(fun, Tesla.Adapter.Gun, opts) do
ConcurrentLimiter.limit(:"#{@prefix}.#{opts[:pool] || :default}", fun)
end
defp maybe_limit(fun, _, _) do
fun.()
end
defp adapter_middlewares(Tesla.Adapter.Gun, extra_middleware) do
default_middleware() ++
[Pleroma.Tesla.Middleware.ConnectionPool] ++
extra_middleware
end
defp adapter_middlewares({Tesla.Adapter.Finch, _}, extra_middleware) do
default_middleware() ++ extra_middleware
end
defp adapter_middlewares(_, extra_middleware) do
- if Pleroma.Config.get(:env) == :test do
- # Emulate redirects in test env, which are handled by adapters in other environments
- default_middleware()
- else
- extra_middleware
+ # A lot of tests are written expecting unencoded URLs
+ # and the burden of fixing that is high. Also it makes
+ # them hard to read. Tests will opt-in when we want to validate
+ # the encoding is being done correctly.
+ cond do
+ Pleroma.Config.get(:env) == :test and Pleroma.Config.get(:test_url_encoding) ->
+ default_middleware()
+
+ Pleroma.Config.get(:env) == :test ->
+ # Emulate redirects in test env, which are handled by adapters in other environments
+ [Tesla.Middleware.FollowRedirects]
+
+ true ->
+ extra_middleware
end
end
defp default_middleware(),
do: [Tesla.Middleware.FollowRedirects, Pleroma.Tesla.Middleware.EncodeUrl]
end
diff --git a/lib/pleroma/tesla/middleware/encode_url.ex b/lib/pleroma/tesla/middleware/encode_url.ex
index df10e13c2..ee74c41a1 100644
--- a/lib/pleroma/tesla/middleware/encode_url.ex
+++ b/lib/pleroma/tesla/middleware/encode_url.ex
@@ -1,53 +1,56 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2025 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Tesla.Middleware.EncodeUrl do
@moduledoc """
Middleware to encode URLs properly
We must decode and then re-encode to ensure correct encoding.
If we only encode it will re-encode each % as %25 causing a space
already encoded as %20 to be %2520.
Similar problem for query parameters which need spaces to be the + character
"""
@behaviour Tesla.Middleware
@impl Tesla.Middleware
def call(%Tesla.Env{url: url} = env, next, _) do
- url =
- URI.parse(url)
- |> then(fn parsed ->
- path = encode_path(parsed.path)
- query = encode_query(parsed.query)
-
- %{parsed | path: path, query: query}
- end)
- |> URI.to_string()
+ url = encode_url(url)
env = %{env | url: url}
case Tesla.run(env, next) do
{:ok, env} -> {:ok, env}
err -> err
end
end
+ defp encode_url(url) when is_binary(url) do
+ URI.parse(url)
+ |> then(fn parsed ->
+ path = encode_path(parsed.path)
+ query = encode_query(parsed.query)
+
+ %{parsed | path: path, query: query}
+ end)
+ |> URI.to_string()
+ end
+
defp encode_path(nil), do: nil
defp encode_path(path) when is_binary(path) do
path
|> URI.decode()
|> URI.encode()
end
defp encode_query(nil), do: nil
defp encode_query(query) when is_binary(query) do
query
|> URI.decode_query()
|> URI.encode_query()
end
end
diff --git a/test/pleroma/http_test.exs b/test/pleroma/http_test.exs
index 058cb96c0..803bd451a 100644
--- a/test/pleroma/http_test.exs
+++ b/test/pleroma/http_test.exs
@@ -1,87 +1,89 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.HTTPTest do
use ExUnit.Case, async: true
use Pleroma.Tests.Helpers
import Tesla.Mock
alias Pleroma.HTTP
setup do
mock(fn
%{
method: :get,
url: "http://example.com/hello",
headers: [{"content-type", "application/json"}]
} ->
json(%{"my" => "data"})
%{method: :head, url: "http://example.com/hello"} ->
%Tesla.Env{status: 200, body: ""}
%{method: :get, url: "http://example.com/hello"} ->
%Tesla.Env{status: 200, body: "hello"}
%{method: :post, url: "http://example.com/world"} ->
%Tesla.Env{status: 200, body: "world"}
%{method: :get, url: "https://tsundere.love/emoji/Pack%201/koronebless.png"} ->
%Tesla.Env{status: 200, body: "emoji data"}
end)
:ok
end
describe "head/1" do
test "returns successfully result" do
assert HTTP.head("http://example.com/hello") == {:ok, %Tesla.Env{status: 200, body: ""}}
end
end
describe "get/1" do
test "returns successfully result" do
assert HTTP.get("http://example.com/hello") == {
:ok,
%Tesla.Env{status: 200, body: "hello"}
}
end
end
describe "get/2 (with headers)" do
test "returns successfully result for json content-type" do
assert HTTP.get("http://example.com/hello", [{"content-type", "application/json"}]) ==
{
:ok,
%Tesla.Env{
status: 200,
body: "{\"my\":\"data\"}",
headers: [{"content-type", "application/json"}]
}
}
end
end
describe "post/2" do
test "returns successfully result" do
assert HTTP.post("http://example.com/world", "") == {
:ok,
%Tesla.Env{status: 200, body: "world"}
}
end
end
test "URL encoding properly encodes URLs with spaces" do
+ clear_config(:test_url_encoding, true)
+
url_with_space = "https://tsundere.love/emoji/Pack 1/koronebless.png"
result = HTTP.get(url_with_space)
assert result == {:ok, %Tesla.Env{status: 200, body: "emoji data"}}
properly_encoded_url = "https://tsundere.love/emoji/Pack%201/koronebless.png"
result = HTTP.get(properly_encoded_url)
assert result == {:ok, %Tesla.Env{status: 200, body: "emoji data"}}
end
end
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Oct 29, 9:26 AM (19 h, 30 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1797858
Default Alt Text
(9 KB)
Attached To
Mode
rPUBE pleroma-upstream
Attached
Detach File
Event Timeline