diff options
author | Yehuda Katz <wycats@gmail.com> | 2009-08-10 11:40:41 -0400 |
---|---|---|
committer | Yehuda Katz <wycats@gmail.com> | 2009-08-11 15:03:53 -0700 |
commit | 0adbeeb0c92c6de2e4a148e4b54d56cd4a325800 (patch) | |
tree | 07f255c361585ee7d6937aa986154a4fc7672b3f /actionpack/lib/action_dispatch/http | |
parent | 9e62d6d1c0c53e8b03c9659500e2b5549a1fd2ec (diff) | |
download | rails-0adbeeb0c92c6de2e4a148e4b54d56cd4a325800.tar.gz rails-0adbeeb0c92c6de2e4a148e4b54d56cd4a325800.tar.bz2 rails-0adbeeb0c92c6de2e4a148e4b54d56cd4a325800.zip |
Got overhead down from 127 to 85. All tests pass:
* Tentatively replaced HeaderHash with SimpleHeaderHash, which does not preserve
case but does handle converting Arrays to Strings in to_hash. This requires
further discussion.
* Moved default_charset to ActionDispatch::Response to avoid having to hop over
to ActionController. Ideally, this would be a constant on AD::Response, but
some tests expect to be able to change it dynamically and I didn't want to change
them yet.
* Completely override #initialize from Rack::Response. Previously, it was creating
a HeaderHash, and then we were creating an entirely new one. There is no way to
call super without incurring the overhead of creating a HeaderHash.
* Override #write from Rack::Response. Its implementation tracks Content-Length,
and doing so adds additional overhead that could be mooted if other middleware
changes the body. It is more efficiently done at the top-level server.
* Change sending_file to an instance_variable instead of header inspection. In
general, if a state is important, it should be set as a property of the response
not reconstructed later.
* Set the Etag to @body instead of .body. AS::Cache.expand_cache_key handles
Arrays fine, and it's more efficient to let it handle the body parts, since
it is not forced to create a joined String.
* If we detect the default cache control case, just set it, rather than setting
the constituent parts and then running the normal (expensive) code to generate
the string.
Diffstat (limited to 'actionpack/lib/action_dispatch/http')
-rw-r--r-- | actionpack/lib/action_dispatch/http/response.rb | 92 |
1 files changed, 56 insertions, 36 deletions
diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb index 03d1780b77..1d3cf63984 100644 --- a/actionpack/lib/action_dispatch/http/response.rb +++ b/actionpack/lib/action_dispatch/http/response.rb @@ -32,18 +32,42 @@ module ActionDispatch # :nodoc: # end # end class Response < Rack::Response + class SimpleHeaderHash < Hash + def to_hash + result = {} + each do |k,v| + v = v.join("\n") if v.is_a?(Array) + result[k] = v + end + result + end + end + attr_accessor :request attr_reader :cache_control - attr_writer :header + attr_writer :header, :sending_file alias_method :headers=, :header= - delegate :default_charset, :to => 'ActionController::Base' - def initialize - super + @status = 200 + @header = SimpleHeaderHash.new @cache_control = {} - @header = Rack::Utils::HeaderHash.new + + @writer = lambda { |x| @body << x } + @block = nil + @length = 0 + + @body = [] + @sending_file = false + + yield self if block_given? + end + + def write(str) + s = str.to_s + @writer.call s + str end def status=(status) @@ -128,20 +152,20 @@ module ActionDispatch # :nodoc: end end - def sending_file? - headers["Content-Transfer-Encoding"] == "binary" - end + CONTENT_TYPE = "Content-Type" + + cattr_accessor(:default_charset) { "utf-8" } def assign_default_content_type_and_charset! - return if !headers["Content-Type"].blank? + return if !headers[CONTENT_TYPE].blank? @content_type ||= Mime::HTML - @charset ||= default_charset + @charset ||= self.class.default_charset type = @content_type.to_s.dup - type << "; charset=#{@charset}" unless sending_file? + type << "; charset=#{@charset}" unless @sending_file - headers["Content-Type"] = type + headers[CONTENT_TYPE] = type end def prepare! @@ -168,17 +192,6 @@ module ActionDispatch # :nodoc: str end - def set_cookie(key, value) - if value.has_key?(:http_only) - ActiveSupport::Deprecation.warn( - "The :http_only option in ActionController::Response#set_cookie " + - "has been renamed. Please use :httponly instead.", caller) - value[:httponly] ||= value.delete(:http_only) - end - - super(key, value) - end - # Returns the response cookies, converted to a Hash of (name => value) pairs # # assert_equal 'AuthorOfNewPage', r.cookies['author'] @@ -201,7 +214,7 @@ module ActionDispatch # :nodoc: if etag? || last_modified? || !cache_control.empty? set_conditional_cache_control! elsif nonempty_ok_response? - self.etag = body + self.etag = @body if request && request.etag_matches?(etag) self.status = 304 @@ -214,30 +227,37 @@ module ActionDispatch # :nodoc: end end + EMPTY_RESPONSE = [" "] + def nonempty_ok_response? ok = !@status || @status == 200 - ok && string_body? + ok && string_body? && @body != EMPTY_RESPONSE end def string_body? !body_parts.respond_to?(:call) && body_parts.any? && body_parts.all? { |part| part.is_a?(String) } end + DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate" + def set_conditional_cache_control! - if cache_control.empty? - cache_control.merge!(:public => false, :max_age => 0, :must_revalidate => true) - end + control = cache_control - public_cache, max_age, must_revalidate, extras = - cache_control.values_at(:public, :max_age, :must_revalidate, :extras) + if control.empty? + headers["Cache-Control"] = DEFAULT_CACHE_CONTROL + else + extras = control[:extras] + max_age = control[:max_age] + + options = [] + options << "max-age=#{max_age}" if max_age + options << (control[:public] ? "public" : "private") + options << "must-revalidate" if control[:must_revalidate] + options.concat(extras) if extras - options = [] - options << "max-age=#{max_age}" if max_age - options << (public_cache ? "public" : "private") - options << "must-revalidate" if must_revalidate - options.concat(extras) if extras + headers["Cache-Control"] = options.join(", ") + end - headers["Cache-Control"] = options.join(", ") end end end |