使用Rack :: Deflater时,rails中的HTTP流不能正常工作

Bav*_*sjo 5 rake ruby-on-rails unicorn http-streaming

我在rails 3.1中设置了独角兽,http流式传输工作直到我启用Rack :: Deflater.无论是否使用Rack :: Chunked,我都试过了.在卷曲中,我可以看到我在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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

控制器:

render "someview", :stream => true
Run Code Online (Sandbox Code Playgroud)

谢谢你的帮助.

Wea*_*key 5

问题在于,Rails ActionController :: Streaming直接将其渲染到Chunked :: Body中。这意味着首先将内容分块,然后由Rack :: Deflater中间件压缩,而不是压缩后再分块。

根据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
Run Code Online (Sandbox Code Playgroud)

并保留您的config.ru为

require ::File.expand_path('../config/environment',  __FILE__)
use Rack::Chunked
use Rack::Deflater
run Roam7::Application
Run Code Online (Sandbox Code Playgroud)

这不是一个很好的解决方案,它可能会破坏其他一些检查/修改主体的中间件。如果有人有更好的解决方案,我希望能听到。

如果您使用的是新遗物,则在流式传输时还必须禁用其中间件。