diff options
Diffstat (limited to 'actionpack/lib/action_dispatch/http/response.rb')
-rw-r--r-- | actionpack/lib/action_dispatch/http/response.rb | 134 |
1 files changed, 110 insertions, 24 deletions
diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb index 84732085f0..0f808ac9cf 100644 --- a/actionpack/lib/action_dispatch/http/response.rb +++ b/actionpack/lib/action_dispatch/http/response.rb @@ -1,7 +1,6 @@ require 'digest/md5' -require 'active_support/core_ext/module/delegation' -require 'active_support/core_ext/object/blank' require 'active_support/core_ext/class/attribute_accessors' +require 'monitor' module ActionDispatch # :nodoc: # Represents an HTTP response generated by a controller action. Use it to @@ -29,7 +28,7 @@ module ActionDispatch # :nodoc: # class DemoControllerTest < ActionDispatch::IntegrationTest # def test_print_root_path_to_console # get('/') - # puts @response.body + # puts response.body # end # end class Response @@ -41,7 +40,7 @@ module ActionDispatch # :nodoc: alias_method :headers, :header delegate :[], :[]=, :to => :@header - delegate :each, :to => :@body + delegate :each, :to => :@stream # Sets the HTTP response's content MIME type. For example, in the controller # you could write this: @@ -51,22 +50,64 @@ module ActionDispatch # :nodoc: # If a character set has been defined for this response (see charset=) then # the character set information will also be included in the content type # information. - attr_accessor :charset, :content_type + attr_accessor :charset + attr_reader :content_type CONTENT_TYPE = "Content-Type".freeze SET_COOKIE = "Set-Cookie".freeze LOCATION = "Location".freeze - + cattr_accessor(:default_charset) { "utf-8" } + cattr_accessor(:default_headers) include Rack::Response::Helpers + include ActionDispatch::Http::FilterRedirect include ActionDispatch::Http::Cache::Response + include MonitorMixin + + class Buffer # :nodoc: + def initialize(response, buf) + @response = response + @buf = buf + @closed = false + end + + def write(string) + raise IOError, "closed stream" if closed? + + @response.commit! + @buf.push string + end + + def each(&block) + @buf.each(&block) + end + + def close + @response.commit! + @closed = true + end + + def closed? + @closed + end + end + + attr_reader :stream def initialize(status = 200, header = {}, body = []) + super() + + header = merge_default_headers(header, self.class.default_headers) + self.body, self.header, self.status = body, header, status @sending_file = false - @blank = false + @blank = false + @cv = new_cond + @committed = false + @content_type = nil + @charset = nil if content_type = self[CONTENT_TYPE] type, charset = content_type.split(/;\s*charset=/) @@ -79,10 +120,31 @@ module ActionDispatch # :nodoc: yield self if block_given? end + def await_commit + synchronize do + @cv.wait_until { @committed } + end + end + + def commit! + synchronize do + @committed = true + @cv.broadcast + end + end + + def committed? + @committed + end + def status=(status) @status = Rack::Utils.status_code(status) end + def content_type=(content_type) + @content_type = content_type.to_s + end + # The response code of the request def response_code @status @@ -100,14 +162,14 @@ module ActionDispatch # :nodoc: def respond_to?(method) if method.to_sym == :to_path - @body.respond_to?(:to_path) + stream.respond_to?(:to_path) else super end end def to_path - @body.to_path + stream.to_path end def body @@ -121,11 +183,17 @@ module ActionDispatch # :nodoc: def body=(body) @blank = true if body == EMPTY - @body = body.respond_to?(:each) ? body : [body] + if body.respond_to?(:to_path) + @stream = body + else + @stream = build_buffer self, munge_body_object(body) + end end def body_parts - @body + parts = [] + @stream.each { |x| parts << x } + parts end def set_cookie(key, value) @@ -146,21 +214,11 @@ module ActionDispatch # :nodoc: end def close - @body.close if @body.respond_to?(:close) + stream.close if stream.respond_to?(:close) end def to_a - assign_default_content_type_and_charset! - handle_conditional_get! - - @header[SET_COOKIE] = @header[SET_COOKIE].join("\n") if @header[SET_COOKIE].respond_to?(:join) - - if [204, 304].include?(@status) - @header.delete CONTENT_TYPE - [@status, @header, []] - else - [@status, @header, self] - end + rack_response @status, @header.to_hash end alias prepare! to_a alias to_ary to_a # For implicit splat on 1.9.2 @@ -184,7 +242,21 @@ module ActionDispatch # :nodoc: private - def assign_default_content_type_and_charset! + def merge_default_headers(original, default) + return original unless default.respond_to?(:merge) + + default.merge(original) + end + + def build_buffer(response, body) + Buffer.new response, body + end + + def munge_body_object(body) + body.respond_to?(:each) ? body : [body] + end + + def assign_default_content_type_and_charset!(headers) return if headers[CONTENT_TYPE].present? @content_type ||= Mime::HTML @@ -195,5 +267,19 @@ module ActionDispatch # :nodoc: headers[CONTENT_TYPE] = type end + + def rack_response(status, header) + assign_default_content_type_and_charset!(header) + handle_conditional_get! + + header[SET_COOKIE] = header[SET_COOKIE].join("\n") if header[SET_COOKIE].respond_to?(:join) + + if [204, 304].include?(@status) + header.delete CONTENT_TYPE + [status, header, []] + else + [status, header, self] + end + end end end |