Merge branch 'proxy-range-and-chunk' into 'develop'

ReverseProxy: Streaming and disable encoding if Range

Closes #1860 and #1823

See merge request pleroma/pleroma!2749
This commit is contained in:
feld 2020-07-10 16:18:28 +00:00
commit ce9514000d
2 changed files with 38 additions and 20 deletions

View File

@ -3,12 +3,13 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.ReverseProxy do defmodule Pleroma.ReverseProxy do
@range_headers ~w(range if-range)
@keep_req_headers ~w(accept user-agent accept-encoding cache-control if-modified-since) ++ @keep_req_headers ~w(accept user-agent accept-encoding cache-control if-modified-since) ++
~w(if-unmodified-since if-none-match if-range range) ~w(if-unmodified-since if-none-match) ++ @range_headers
@resp_cache_headers ~w(etag date last-modified) @resp_cache_headers ~w(etag date last-modified)
@keep_resp_headers @resp_cache_headers ++ @keep_resp_headers @resp_cache_headers ++
~w(content-type content-disposition content-encoding content-range) ++ ~w(content-length content-type content-disposition content-encoding) ++
~w(accept-ranges vary) ~w(content-range accept-ranges vary)
@default_cache_control_header "public, max-age=1209600" @default_cache_control_header "public, max-age=1209600"
@valid_resp_codes [200, 206, 304] @valid_resp_codes [200, 206, 304]
@max_read_duration :timer.seconds(30) @max_read_duration :timer.seconds(30)
@ -170,6 +171,8 @@ defp request(method, url, headers, opts) do
end end
defp response(conn, client, url, status, headers, opts) do defp response(conn, client, url, status, headers, opts) do
Logger.debug("#{__MODULE__} #{status} #{url} #{inspect(headers)}")
result = result =
conn conn
|> put_resp_headers(build_resp_headers(headers, opts)) |> put_resp_headers(build_resp_headers(headers, opts))
@ -220,7 +223,9 @@ defp chunk_reply(conn, client, opts, sent_so_far, duration) do
end end
end end
defp head_response(conn, _url, code, headers, opts) do defp head_response(conn, url, code, headers, opts) do
Logger.debug("#{__MODULE__} #{code} #{url} #{inspect(headers)}")
conn conn
|> put_resp_headers(build_resp_headers(headers, opts)) |> put_resp_headers(build_resp_headers(headers, opts))
|> send_resp(code, "") |> send_resp(code, "")
@ -262,9 +267,23 @@ defp build_req_headers(headers, opts) do
headers headers
|> downcase_headers() |> downcase_headers()
|> Enum.filter(fn {k, _} -> k in @keep_req_headers end) |> Enum.filter(fn {k, _} -> k in @keep_req_headers end)
|> (fn headers -> |> build_req_range_or_encoding_header(opts)
headers = headers ++ Keyword.get(opts, :req_headers, []) |> build_req_user_agent_header(opts)
|> Keyword.merge(Keyword.get(opts, :req_headers, []))
end
# Disable content-encoding if any @range_headers are requested (see #1823).
defp build_req_range_or_encoding_header(headers, _opts) do
range? = Enum.any?(headers, fn {header, _} -> Enum.member?(@range_headers, header) end)
if range? && List.keymember?(headers, "accept-encoding", 0) do
List.keydelete(headers, "accept-encoding", 0)
else
headers
end
end
defp build_req_user_agent_header(headers, opts) do
if Keyword.get(opts, :keep_user_agent, false) do if Keyword.get(opts, :keep_user_agent, false) do
List.keystore( List.keystore(
headers, headers,
@ -275,7 +294,6 @@ defp build_req_headers(headers, opts) do
else else
headers headers
end end
end).()
end end
defp build_resp_headers(headers, opts) do defp build_resp_headers(headers, opts) do
@ -283,7 +301,7 @@ defp build_resp_headers(headers, opts) do
|> Enum.filter(fn {k, _} -> k in @keep_resp_headers end) |> Enum.filter(fn {k, _} -> k in @keep_resp_headers end)
|> build_resp_cache_headers(opts) |> build_resp_cache_headers(opts)
|> build_resp_content_disposition_header(opts) |> build_resp_content_disposition_header(opts)
|> (fn headers -> headers ++ Keyword.get(opts, :resp_headers, []) end).() |> Keyword.merge(Keyword.get(opts, :resp_headers, []))
end end
defp build_resp_cache_headers(headers, _opts) do defp build_resp_cache_headers(headers, _opts) do

View File

@ -314,7 +314,7 @@ defp disposition_headers_mock(headers) do
test "not atachment", %{conn: conn} do test "not atachment", %{conn: conn} do
disposition_headers_mock([ disposition_headers_mock([
{"content-type", "image/gif"}, {"content-type", "image/gif"},
{"content-length", 0} {"content-length", "0"}
]) ])
conn = ReverseProxy.call(conn, "/disposition") conn = ReverseProxy.call(conn, "/disposition")
@ -325,7 +325,7 @@ test "not atachment", %{conn: conn} do
test "with content-disposition header", %{conn: conn} do test "with content-disposition header", %{conn: conn} do
disposition_headers_mock([ disposition_headers_mock([
{"content-disposition", "attachment; filename=\"filename.jpg\""}, {"content-disposition", "attachment; filename=\"filename.jpg\""},
{"content-length", 0} {"content-length", "0"}
]) ])
conn = ReverseProxy.call(conn, "/disposition") conn = ReverseProxy.call(conn, "/disposition")