Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85651248
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
8 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/lib/pleroma/reverse_proxy/client/hackney.ex b/lib/pleroma/reverse_proxy/client/hackney.ex
index 7ccd28bb1..7e1fca80d 100644
--- a/lib/pleroma/reverse_proxy/client/hackney.ex
+++ b/lib/pleroma/reverse_proxy/client/hackney.ex
@@ -1,71 +1,74 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.ReverseProxy.Client.Hackney do
@behaviour Pleroma.ReverseProxy.Client
- # redirect handler from Pleb, slightly modified to work with Hackney
+ # In-app redirect handler to avoid Hackney redirect bugs:
+ # - https://github.com/benoitc/hackney/issues/527 (relative/protocol-less redirects can crash Hackney)
+ # - https://github.com/benoitc/hackney/issues/273 (redirects not followed when using HTTP proxy)
+ #
+ # Based on a redirect handler from Pleb, slightly modified to work with Hackney:
# https://declin.eu/objects/d4f38e62-5429-4614-86d1-e8fc16e6bf33
- # https://github.com/benoitc/hackney/issues/273
@redirect_statuses [301, 302, 303, 307, 308]
defp absolute_redirect_url(original_url, resp_headers) do
location =
Enum.find(resp_headers, fn {header, _location} ->
String.downcase(header) == "location"
end)
URI.merge(original_url, elem(location, 1))
|> URI.to_string()
end
@impl true
def request(method, url, headers, body, opts \\ []) do
opts =
Keyword.put_new(opts, :path_encode_fun, fn path ->
path
end)
if opts[:follow_redirect] != false do
{_state, req_opts} = Access.get_and_update(opts, :follow_redirect, fn a -> {a, false} end)
res = :hackney.request(method, url, headers, body, req_opts)
case res do
{:ok, code, resp_headers, _client} when code in @redirect_statuses ->
:hackney.request(
method,
absolute_redirect_url(url, resp_headers),
headers,
body,
req_opts
)
{:ok, code, resp_headers} when code in @redirect_statuses ->
:hackney.request(
method,
absolute_redirect_url(url, resp_headers),
headers,
body,
req_opts
)
_ ->
res
end
else
:hackney.request(method, url, headers, body, opts)
end
end
@impl true
def stream_body(ref) do
case :hackney.stream_body(ref) do
:done -> :done
{:ok, data} -> {:ok, data, ref}
{:error, error} -> {:error, error}
end
end
@impl true
def close(ref), do: :hackney.close(ref)
end
diff --git a/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex b/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex
index b0d07a6f8..43c2c0449 100644
--- a/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex
@@ -1,64 +1,71 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do
@moduledoc "Preloads any attachments in the MediaProxy cache by prefetching them"
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
alias Pleroma.HTTP
alias Pleroma.Web.MediaProxy
require Logger
@impl true
def history_awareness, do: :auto
defp prefetch(url) do
# Fetching only proxiable resources
if MediaProxy.enabled?() and MediaProxy.url_proxiable?(url) do
# If preview proxy is enabled, it'll also hit media proxy (so we're caching both requests)
prefetch_url = MediaProxy.preview_url(url)
Logger.debug("Prefetching #{inspect(url)} as #{inspect(prefetch_url)}")
fetch(prefetch_url)
end
end
defp fetch(url) do
- http_client_opts = Pleroma.Config.get([:media_proxy, :proxy_opts, :http], pool: :media)
+ # This module uses Tesla (Pleroma.HTTP) to fetch the MediaProxy URL.
+ # Redirect following is handled by Tesla middleware, so we must not enable
+ # adapter-level redirect logic (Hackney can crash on relative redirects when proxied).
+ http_client_opts =
+ [:media_proxy, :proxy_opts, :http]
+ |> Pleroma.Config.get(pool: :media)
+ |> Keyword.drop([:follow_redirect, :force_redirect])
+
HTTP.get(url, [], http_client_opts)
end
defp preload(%{"object" => %{"attachment" => attachments}} = _activity) do
Enum.each(attachments, fn
%{"url" => url} when is_list(url) ->
url
|> Enum.each(fn
%{"href" => href} ->
prefetch(href)
x ->
Logger.debug("Unhandled attachment URL object #{inspect(x)}")
end)
x ->
Logger.debug("Unhandled attachment #{inspect(x)}")
end)
end
@impl true
def filter(%{"type" => type, "object" => %{"attachment" => attachments} = _object} = activity)
when type in ["Create", "Update"] and is_list(attachments) and length(attachments) > 0 do
preload(activity)
{:ok, activity}
end
@impl true
def filter(activity), do: {:ok, activity}
@impl true
def describe, do: {:ok, %{}}
end
diff --git a/test/pleroma/web/activity_pub/mrf/media_proxy_warming_policy_test.exs b/test/pleroma/web/activity_pub/mrf/media_proxy_warming_policy_test.exs
index 0da3afa3b..4b94b9ac7 100644
--- a/test/pleroma/web/activity_pub/mrf/media_proxy_warming_policy_test.exs
+++ b/test/pleroma/web/activity_pub/mrf/media_proxy_warming_policy_test.exs
@@ -1,106 +1,101 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicyTest do
use ExUnit.Case
use Pleroma.Tests.Helpers
alias Pleroma.HTTP
alias Pleroma.UnstubbedConfigMock, as: ConfigMock
alias Pleroma.Web.ActivityPub.MRF
alias Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy
import Mock
import Mox
@message %{
"type" => "Create",
"object" => %{
"type" => "Note",
"content" => "content",
"attachment" => [
%{"url" => [%{"href" => "http://example.com/image.jpg"}]}
]
}
}
@message_with_history %{
"type" => "Create",
"object" => %{
"type" => "Note",
"content" => "content",
"formerRepresentations" => %{
"orderedItems" => [
%{
"type" => "Note",
"content" => "content",
"attachment" => [
%{"url" => [%{"href" => "http://example.com/image.jpg"}]}
]
}
]
}
}
}
setup do
ConfigMock
|> stub_with(Pleroma.Test.StaticConfig)
:ok
end
setup do: clear_config([:media_proxy, :enabled], true)
test "it prefetches media proxy URIs" do
- Tesla.Mock.mock(fn %{method: :get, url: "http://example.com/image.jpg"} ->
- {:ok, %Tesla.Env{status: 200, body: ""}}
- end)
-
- with_mock HTTP, get: fn _, _, _ -> {:ok, []} end do
+ with_mock HTTP,
+ get: fn _, _, opts ->
+ send(self(), {:prefetch_opts, opts})
+ {:ok, []}
+ end do
MediaProxyWarmingPolicy.filter(@message)
assert called(HTTP.get(:_, :_, :_))
+ assert_receive {:prefetch_opts, opts}
+ refute Keyword.has_key?(opts, :follow_redirect)
+ refute Keyword.has_key?(opts, :force_redirect)
end
end
test "it does nothing when no attachments are present" do
object =
@message["object"]
|> Map.delete("attachment")
message =
@message
|> Map.put("object", object)
with_mock HTTP, get: fn _, _, _ -> {:ok, []} end do
MediaProxyWarmingPolicy.filter(message)
refute called(HTTP.get(:_, :_, :_))
end
end
test "history-aware" do
- Tesla.Mock.mock(fn %{method: :get, url: "http://example.com/image.jpg"} ->
- {:ok, %Tesla.Env{status: 200, body: ""}}
- end)
-
with_mock HTTP, get: fn _, _, _ -> {:ok, []} end do
MRF.filter_one(MediaProxyWarmingPolicy, @message_with_history)
assert called(HTTP.get(:_, :_, :_))
end
end
test "works with Updates" do
- Tesla.Mock.mock(fn %{method: :get, url: "http://example.com/image.jpg"} ->
- {:ok, %Tesla.Env{status: 200, body: ""}}
- end)
-
with_mock HTTP, get: fn _, _, _ -> {:ok, []} end do
MRF.filter_one(MediaProxyWarmingPolicy, @message_with_history |> Map.put("type", "Update"))
assert called(HTTP.get(:_, :_, :_))
end
end
end
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Aug 30, 3:08 PM (1 d, 12 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1738046
Default Alt Text
(8 KB)
Attached To
Mode
rPUBE pleroma-upstream
Attached
Detach File
Event Timeline
Log In to Comment