From 858d528cc1b551e23850a4b65eb43e44a2a18cf4 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 4 Jun 2024 22:44:56 -0400 Subject: [PATCH] Allow Cowboy to stream the response instead of chunk it --- changelog.d/cowboy-stream-chunked.fix | 1 + lib/pleroma/reverse_proxy.ex | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 changelog.d/cowboy-stream-chunked.fix diff --git a/changelog.d/cowboy-stream-chunked.fix b/changelog.d/cowboy-stream-chunked.fix new file mode 100644 index 000000000..07211bf18 --- /dev/null +++ b/changelog.d/cowboy-stream-chunked.fix @@ -0,0 +1 @@ +Restore Cowboy's ability to stream MediaProxy responses without Chunked encoding. diff --git a/lib/pleroma/reverse_proxy.ex b/lib/pleroma/reverse_proxy.ex index 4d13e51fc..8aec4ae58 100644 --- a/lib/pleroma/reverse_proxy.ex +++ b/lib/pleroma/reverse_proxy.ex @@ -8,7 +8,7 @@ defmodule Pleroma.ReverseProxy do ~w(if-unmodified-since if-none-match) ++ @range_headers @resp_cache_headers ~w(etag date last-modified) @keep_resp_headers @resp_cache_headers ++ - ~w(content-type content-disposition content-encoding) ++ + ~w(content-length content-type content-disposition content-encoding) ++ ~w(content-range accept-ranges vary) @default_cache_control_header "public, max-age=1209600" @valid_resp_codes [200, 206, 304] @@ -180,6 +180,7 @@ defmodule Pleroma.ReverseProxy do result = conn |> put_resp_headers(build_resp_headers(headers, opts)) + |> streaming_compat |> send_chunked(status) |> chunk_reply(client, opts) @@ -417,4 +418,17 @@ defmodule Pleroma.ReverseProxy do @cachex.put(:failed_proxy_url_cache, url, true, ttl: ttl) end + + # When Cowboy handles a chunked response with a content-length header it streams + # over HTTP 1.1 instead of chunking. Bandit cannot stream over HTTP 1.1 so the header + # must be stripped or it breaks RFC compliance for Transfer Encoding: Chunked. RFC9112ยง6.2 + # + # HTTP2 is always streamed for all adapters. + defp streaming_compat(conn) do + with Phoenix.Endpoint.Cowboy2Adapter <- Pleroma.Web.Endpoint.config(:adapter) do + conn + else + _ -> delete_resp_header(conn, "content-length") + end + end end