问题描述
我已经在rails 3.1中设置了独角兽,并且在启用Rack :: Deflater之前,http流媒体可以正常工作.我已经尝试过使用和不使用Rack :: Chunked.在curl中,我可以看到我的响应,而在chrome中,我会看到以下错误:ERR_INVALID_CHUNKED_ENCODING
I've setup unicorn in rails 3.1 and http streaming works until I enable Rack::Deflater.I've tried both with and without use Rack::Chunked. In curl I can see my response while in chrome I get the following errror: ERR_INVALID_CHUNKED_ENCODING
在其他浏览器(firefox,safari)以及开发(osx)和生产版(heroku)之间,结果是相同的.
The result is same in other browsers (firefox, safari) and between development (osx) and production (heroku).
config.ru:
config.ru:
require ::File.expand_path('../config/environment', __FILE__)
use Rack::Chunked
use Rack::Deflater
run Site::Application
unicorn.rb:
unicorn.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
感谢您的帮助.
推荐答案
问题是Rails ActionController :: Streaming直接将其渲染到Chunked :: Body中.这意味着首先将内容分块,然后由Rack :: Deflater中间件压缩,而不是先压缩然后分块.
The problem is that Rails ActionController::Streaming renders directly into a Chunked::Body. This means the content is first chunked and then gzipped by the Rack::Deflater middleware, instead of gzipped and then chunked.
根据 HTTP/1.1 RFC 6.2.1 ,分块必须最后应用于传输编码.
According to the HTTP/1.1 RFC 6.2.1, chunked must be last applied encoding to a transfer.
我通过猴子在初始化器中修补ActionController :: Streaming _process_options和_render_template方法为我们修复了此问题,因此它不会将主体包装在Chunked :: Body中,而由Rack :: Chunked中间件来代替.
I fixed it for us by monkey patching ActionController::Streaming _process_options and _render_template methods in an initializer so it does not wrap the body in a Chunked::Body, and lets the Rack::Chunked middleware do it instead.
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保留为
And leave your config.ru as
require ::File.expand_path('../config/environment', __FILE__)
use Rack::Chunked
use Rack::Deflater
run Roam7::Application
这不是一个很好的解决方案,它可能会破坏其他一些检查/修改主体的中间件.如果有人有更好的解决方案,我很想听听.
Not a very nice solution, it will probably break some other middlewares that inspect/modify the body. If someone has a better solution I'd love to hear it.
如果您使用的是新遗物,则其中间件也必须为在流式传输时禁用.
If you are using new relic, its middleware must also be disabled when streaming.
这篇关于使用Rack :: Deflater时Rails中的HTTP流无法正常工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!