5

我已经在 rails 3.1 中设置了 unicorn,并且 http 流可以正常工作,直到我启用 Rack::Deflater。我已经尝试过使用和不使用 Rack::Chunked。在 curl 中我可以看到我的响应,而在 chrome 中我收到以下错误:ERR_INVALID_CHUNKED_ENCODING

结果在其他浏览器(firefox、safari)以及开发(osx)和生产(heroku)之间是相同的。

配置.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

谢谢你的帮助。

4

1 回答 1

5

问题是 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,则在流式传输时也必须禁用其中间件。

于 2012-05-15T07:31:36.863 回答