mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
move file sending to the response object
Just a slight refactor that delegates file sending to the response object. This gives us the advantage that if a webserver (in the future) provides a response object that knows how to do accelerated file serving, it can implement this method.
This commit is contained in:
parent
7669844145
commit
69009f4473
6 changed files with 50 additions and 30 deletions
|
@ -22,13 +22,13 @@ module AbstractController
|
|||
# :api: public
|
||||
def render(*args, &block)
|
||||
options = _normalize_render(*args, &block)
|
||||
self.response_body = render_to_body(options)
|
||||
rendered_body = render_to_body(options)
|
||||
if options[:html]
|
||||
_set_html_content_type
|
||||
else
|
||||
_set_rendered_content_type rendered_format
|
||||
end
|
||||
self.response_body
|
||||
self.response_body = rendered_body
|
||||
end
|
||||
|
||||
# Raw rendering of a template to a string.
|
||||
|
|
|
@ -174,7 +174,11 @@ module ActionController
|
|||
|
||||
def response_body=(body)
|
||||
body = [body] unless body.nil? || body.respond_to?(:each)
|
||||
response.body = body
|
||||
response.reset_body!
|
||||
body.each { |part|
|
||||
next if part.empty?
|
||||
response.write part
|
||||
}
|
||||
super
|
||||
end
|
||||
|
||||
|
|
|
@ -72,31 +72,7 @@ module ActionController #:nodoc:
|
|||
|
||||
self.status = options[:status] || 200
|
||||
self.content_type = options[:content_type] if options.key?(:content_type)
|
||||
self.response_body = FileBody.new(path)
|
||||
end
|
||||
|
||||
# Avoid having to pass an open file handle as the response body.
|
||||
# Rack::Sendfile will usually intercept the response and uses
|
||||
# the path directly, so there is no reason to open the file.
|
||||
class FileBody #:nodoc:
|
||||
attr_reader :to_path
|
||||
|
||||
def initialize(path)
|
||||
@to_path = path
|
||||
end
|
||||
|
||||
def body
|
||||
File.binread(to_path)
|
||||
end
|
||||
|
||||
# Stream the file's contents if Rack::Sendfile isn't present.
|
||||
def each
|
||||
File.open(to_path, 'rb') do |file|
|
||||
while chunk = file.read(16384)
|
||||
yield chunk
|
||||
end
|
||||
end
|
||||
end
|
||||
response.send_file path
|
||||
end
|
||||
|
||||
# Sends the given binary data to the browser. This method is similar to
|
||||
|
|
|
@ -39,7 +39,7 @@ module ActionDispatch # :nodoc:
|
|||
end
|
||||
|
||||
def []=(k,v)
|
||||
if @response.committed?
|
||||
if @response.sending? || @response.sent?
|
||||
raise ActionDispatch::IllegalStateError, 'header already sent'
|
||||
end
|
||||
|
||||
|
@ -279,6 +279,10 @@ module ActionDispatch # :nodoc:
|
|||
@stream.body
|
||||
end
|
||||
|
||||
def write(string)
|
||||
@stream.write string
|
||||
end
|
||||
|
||||
EMPTY = " "
|
||||
|
||||
# Allows you to manually set or override the response body.
|
||||
|
@ -294,6 +298,40 @@ module ActionDispatch # :nodoc:
|
|||
end
|
||||
end
|
||||
|
||||
# Avoid having to pass an open file handle as the response body.
|
||||
# Rack::Sendfile will usually intercept the response and uses
|
||||
# the path directly, so there is no reason to open the file.
|
||||
class FileBody #:nodoc:
|
||||
attr_reader :to_path
|
||||
|
||||
def initialize(path)
|
||||
@to_path = path
|
||||
end
|
||||
|
||||
def body
|
||||
File.binread(to_path)
|
||||
end
|
||||
|
||||
# Stream the file's contents if Rack::Sendfile isn't present.
|
||||
def each
|
||||
File.open(to_path, 'rb') do |file|
|
||||
while chunk = file.read(16384)
|
||||
yield chunk
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Send the file stored at +path+ as the response body.
|
||||
def send_file(path)
|
||||
commit!
|
||||
@stream = FileBody.new(path)
|
||||
end
|
||||
|
||||
def reset_body!
|
||||
@stream = build_buffer(self, [])
|
||||
end
|
||||
|
||||
def body_parts
|
||||
parts = []
|
||||
@stream.each { |x| parts << x }
|
||||
|
|
|
@ -195,7 +195,7 @@ class SendFileTest < ActionController::TestCase
|
|||
%w(file data).each do |method|
|
||||
define_method "test_send_#{method}_status" do
|
||||
@controller.options = { :stream => false, :status => 500 }
|
||||
assert_nothing_raised { assert_not_nil process(method) }
|
||||
assert_not_nil process(method)
|
||||
assert_equal 500, @response.status
|
||||
end
|
||||
|
||||
|
|
|
@ -83,6 +83,8 @@ module ActionController
|
|||
|
||||
def test_headers_cannot_be_written_after_close
|
||||
@response.stream.close
|
||||
# we can add data until it's actually written, which happens on `each`
|
||||
@response.each { |x| }
|
||||
|
||||
e = assert_raises(ActionDispatch::IllegalStateError) do
|
||||
@response.headers['Content-Length'] = "zomg"
|
||||
|
|
Loading…
Reference in a new issue