При использовании Rack :: Deflater потоковая передача HTTP в rails не работает - PullRequest
5 голосов
/ 02 ноября 2011

Я настроил единорога в rails 3.1, и потоковая передача 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

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

Спасибо за любую помощь.

1 Ответ

5 голосов
/ 15 мая 2012

Проблема в том, что Rails ActionController :: Streaming рендерится напрямую в Chunked :: Body.Это означает, что содержимое сначала разбивается на куски, а затем архивируется промежуточным программным обеспечением Rack :: Deflater вместо gzipped, а затем разбивается на куски.

В соответствии с 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

Не очень хорошее решение, оно, вероятно, сломает некоторые другие промежуточные программы, которые проверяют / модифицируют тело.Если у кого-то есть лучшее решение, я бы хотел услышать его.

Если вы используете новую реликвию, ее промежуточное программное обеспечение также должно быть отключено при потоковой передаче .

...