使用 Rack::Deflater 时,Rails 中的 HTTP 流式传输不起作用
Posted
技术标签:
【中文标题】使用 Rack::Deflater 时,Rails 中的 HTTP 流式传输不起作用【英文标题】:HTTP streaming in rails not working when using Rack::Deflater 【发布时间】:2011-12-20 15:09:33 【问题描述】:我已经在 rails 3.1 中设置了 unicorn,并且 http 流可以正常工作,直到我启用 Rack::Deflater。 我尝试过使用和不使用 Rack::Chunked。在 curl 中我可以看到我的响应,而在 chrome 中我收到以下错误:ERR_INVALID_CHUNKED_ENCODING
结果在其他浏览器(firefox、safari)中以及在开发(osx)和生产(heroku)之间是相同的。
config.ru:
require ::File.expand_path('../config/environment', __FILE__)
use Rack::Chunked
use Rack::Deflater
run Site::Application
独角兽.rb:
listen 3001, :tcp_nopush => false
worker_processes 1 # amount of unicorn workers to spin up
timeout 30 # restarts workers that hang for 30 seconds
控制器:
render "someview", :stream => true
感谢您的帮助。
【问题讨论】:
【参考方案1】:问题在于 Rails ActionController::Streaming 直接渲染成 Chunked::Body。这意味着内容首先被 Rack::Deflater 中间件分块然后 gzip,而不是先 gzip 然后再分块。
根据HTTP/1.1 RFC 6.2.1,chunked 必须是最后一次对传输应用编码。
因为“分块”是唯一需要理解的传输编码 对于 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,它的中间件也必须是disabled when streaming。
【讨论】:
以上是关于使用 Rack::Deflater 时,Rails 中的 HTTP 流式传输不起作用的主要内容,如果未能解决你的问题,请参考以下文章
在创建新的 Rails 应用程序时,如何告诉 Rails 使用 RSpec 而不是 test-unit?
在 Rails 6 中使用 activestorage 时,如何在重新显示表单时保留文件?