問題は、Rails ActionController::Streaming が Chunked::Body に直接レンダリングされることです。これは、gzip されてからチャンク化されるのではなく、Rack::Deflater ミドルウェアによってコンテンツが最初にチャンク化されてから gzip されることを意味します。
HTTP/1.1 RFC 6.2.1によると、チャンクは最後に転送にエンコードを適用する必要があります。
「チャンク」は、HTTP/1.1 受信者が理解する必要がある唯一の転送コーディングであるため、永続的な接続でメッセージを区切る上で重要な役割を果たします。転送コーディングが要求のペイロード本体に適用される場合は常に、適用される最終転送コーディングを「チャンク」する必要があります。
イニシャライザで ActionController::Streaming _process_options および _render_template メソッドにモンキー パッチを適用することで修正しました。これにより、本体が Chunked::Body でラップされず、代わりに Rack::Chunked ミドルウェアが実行できるようになります。
module GzipStreaming
def _process_options(options)
stream = options[:stream]
# delete the option to stop original implementation
options.delete(:stream)
super
if stream && env["HTTP_VERSION"] != "HTTP/1.0"
# Same as org implmenation except don't set the transfer-encoding header
# The Rack::Chunked middleware will handle it
headers["Cache-Control"] ||= "no-cache"
headers.delete('Content-Length')
options[:stream] = stream
end
end
def _render_template(options)
if options.delete(:stream)
# Just render, don't wrap in a Chunked::Body, let
# Rack::Chunked middleware handle it
view_renderer.render_body(view_context, options)
else
super
end
end
end
module ActionController
class Base
include GzipStreaming
end
end
そして、config.ruをそのままにしておきます
require ::File.expand_path('../config/environment', __FILE__)
use Rack::Chunked
use Rack::Deflater
run Roam7::Application
あまり良い解決策ではありません。おそらく、本体を検査/変更する他のミドルウェアを壊してしまうでしょう。誰かがより良い解決策を持っているなら、私はそれを聞きたいです.
new relic を使用している場合は、ストリーミング時にミドルウェアも無効にする必要があります。