aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_dispatch/http/response.rb
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib/action_dispatch/http/response.rb')
-rw-r--r--actionpack/lib/action_dispatch/http/response.rb102
1 files changed, 64 insertions, 38 deletions
diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb
index fa4c54701a..7e50cb6d23 100644
--- a/actionpack/lib/action_dispatch/http/response.rb
+++ b/actionpack/lib/action_dispatch/http/response.rb
@@ -1,7 +1,9 @@
-require 'active_support/core_ext/module/attribute_accessors'
-require 'action_dispatch/http/filter_redirect'
-require 'action_dispatch/http/cache'
-require 'monitor'
+# frozen_string_literal: true
+
+require "active_support/core_ext/module/attribute_accessors"
+require "action_dispatch/http/filter_redirect"
+require "action_dispatch/http/cache"
+require "monitor"
module ActionDispatch # :nodoc:
# Represents an HTTP response generated by a controller action. Use it to
@@ -39,9 +41,9 @@ module ActionDispatch # :nodoc:
super(header)
end
- def []=(k,v)
+ def []=(k, v)
if @response.sending? || @response.sent?
- raise ActionDispatch::IllegalStateError, 'header already sent'
+ raise ActionDispatch::IllegalStateError, "header already sent"
end
super
@@ -67,19 +69,25 @@ module ActionDispatch # :nodoc:
alias_method :headers, :header
- delegate :[], :[]=, :to => :@header
- delegate :each, :to => :@stream
+ delegate :[], :[]=, to: :@header
+
+ def each(&block)
+ sending!
+ x = @stream.each(&block)
+ sent!
+ x
+ end
CONTENT_TYPE = "Content-Type".freeze
SET_COOKIE = "Set-Cookie".freeze
LOCATION = "Location".freeze
NO_CONTENT_CODES = [100, 101, 102, 204, 205, 304]
- cattr_accessor(:default_charset) { "utf-8" }
- cattr_accessor(:default_headers)
+ cattr_accessor :default_charset, default: "utf-8"
+ cattr_accessor :default_headers
include Rack::Response::Helpers
- # Aliasing these off because AD::Http::Cache::Response defines them
+ # Aliasing these off because AD::Http::Cache::Response defines them.
alias :_cache_control :cache_control
alias :_cache_control= :cache_control=
@@ -97,10 +105,10 @@ module ActionDispatch # :nodoc:
def body
@str_body ||= begin
- buf = ''
- each { |chunk| buf << chunk }
- buf
- end
+ buf = "".dup
+ each { |chunk| buf << chunk }
+ buf
+ end
end
def write(string)
@@ -112,10 +120,13 @@ module ActionDispatch # :nodoc:
end
def each(&block)
- @response.sending!
- x = @buf.each(&block)
- @response.sent!
- x
+ if @str_body
+ return enum_for(:each) unless block_given?
+
+ yield @str_body
+ else
+ each_chunk(&block)
+ end
end
def abort
@@ -129,6 +140,12 @@ module ActionDispatch # :nodoc:
def closed?
@closed
end
+
+ private
+
+ def each_chunk(&block)
+ @buf.each(&block)
+ end
end
def self.create(status = 200, header = {}, body = [], default_headers: self.default_headers)
@@ -209,8 +226,12 @@ module ActionDispatch # :nodoc:
# Sets the HTTP content type.
def content_type=(content_type)
- header_info = parse_content_type
- set_content_type content_type.to_s, header_info.charset || self.class.default_charset
+ return unless content_type
+ new_header_info = parse_content_type(content_type.to_s)
+ prev_header_info = parsed_content_type_header
+ charset = new_header_info.charset || prev_header_info.charset
+ charset ||= self.class.default_charset unless prev_header_info.mime_type
+ set_content_type new_header_info.mime_type, charset
end
# Sets the HTTP response's content MIME type. For example, in the controller
@@ -223,7 +244,7 @@ module ActionDispatch # :nodoc:
# information.
def content_type
- parse_content_type.mime_type
+ parsed_content_type_header.mime_type
end
def sending_file=(v)
@@ -232,17 +253,16 @@ module ActionDispatch # :nodoc:
end
end
- # Sets the HTTP character set. In case of nil parameter
- # it sets the charset to utf-8.
+ # Sets the HTTP character set. In case of +nil+ parameter
+ # it sets the charset to +default_charset+.
#
# response.charset = 'utf-16' # => 'utf-16'
# response.charset = nil # => 'utf-8'
def charset=(charset)
- header_info = parse_content_type
+ content_type = parsed_content_type_header.mime_type
if false == charset
- set_header CONTENT_TYPE, header_info.mime_type
+ set_content_type content_type, nil
else
- content_type = header_info.mime_type
set_content_type content_type, charset || self.class.default_charset
end
end
@@ -250,7 +270,7 @@ module ActionDispatch # :nodoc:
# The charset of the response. HTML wants to know the encoding of the
# content you're giving them, so we need to send that along.
def charset
- header_info = parse_content_type
+ header_info = parsed_content_type_header
header_info.charset || self.class.default_charset
end
@@ -314,7 +334,7 @@ module ActionDispatch # :nodoc:
# Stream the file's contents if Rack::Sendfile isn't present.
def each
- File.open(to_path, 'rb') do |file|
+ File.open(to_path, "rb") do |file|
while chunk = file.read(16384)
yield chunk
end
@@ -374,7 +394,7 @@ module ActionDispatch # :nodoc:
if header = get_header(SET_COOKIE)
header = header.split("\n") if header.respond_to?(:to_str)
header.each do |cookie|
- if pair = cookie.split(';').first
+ if pair = cookie.split(";").first
key, value = pair.split("=").map { |v| Rack::Utils.unescape(v) }
cookies[key] = value
end
@@ -388,26 +408,32 @@ module ActionDispatch # :nodoc:
ContentTypeHeader = Struct.new :mime_type, :charset
NullContentTypeHeader = ContentTypeHeader.new nil, nil
- def parse_content_type
- content_type = get_header CONTENT_TYPE
+ def parse_content_type(content_type)
if content_type
type, charset = content_type.split(/;\s*charset=/)
- type = nil if type.empty?
+ type = nil if type && type.empty?
ContentTypeHeader.new(type, charset)
else
NullContentTypeHeader
end
end
+ # Small internal convenience method to get the parsed version of the current
+ # content type header.
+ def parsed_content_type_header
+ parse_content_type(get_header(CONTENT_TYPE))
+ end
+
def set_content_type(content_type, charset)
- type = (content_type || '').dup
- type << "; charset=#{charset}" if charset
+ type = (content_type || "").dup
+ type << "; charset=#{charset.to_s.downcase}" if charset
set_header CONTENT_TYPE, type
end
def before_committed
return if committed?
assign_default_content_type_and_charset!
+ merge_and_normalize_cache_control!(@cache_control)
handle_conditional_get!
handle_no_content!
end
@@ -435,7 +461,7 @@ module ActionDispatch # :nodoc:
def assign_default_content_type_and_charset!
return if content_type
- ct = parse_content_type
+ ct = parsed_content_type_header
set_content_type(ct.mime_type || Mime[:html].to_s,
ct.charset || self.class.default_charset)
end
@@ -460,7 +486,7 @@ module ActionDispatch # :nodoc:
end
def respond_to?(method, include_private = false)
- if method.to_s == 'to_path'
+ if method.to_s == "to_path"
@response.stream.respond_to?(method)
else
super
@@ -479,7 +505,7 @@ module ActionDispatch # :nodoc:
def handle_no_content!
if NO_CONTENT_CODES.include?(@status)
@header.delete CONTENT_TYPE
- @header.delete 'Content-Length'
+ @header.delete "Content-Length"
end
end