lib/roda/response.rb in roda-3.69.0 vs lib/roda/response.rb in roda-3.70.0

- old
+ new

@@ -4,10 +4,29 @@ require 'rack/headers' rescue LoadError end class Roda + # Contains constants for response headers. This approach is used so that all + # headers used internally by Roda can be lower case on Rack 3, so that it is + # possible to use a plain hash of response headers instead of using Rack::Headers. + module RodaResponseHeaders + headers = %w'Allow Cache-Control Content-Disposition Content-Encoding Content-Length + Content-Security-Policy Content-Security-Policy-Report-Only Content-Type + ETag Expires Last-Modified Link Location Set-Cookie Transfer-Encoding Vary'.freeze.each(&:freeze) + + if defined?(Rack::Headers) && Rack::Headers.is_a?(Class) + headers.each do |mixed_case| + const_set(mixed_case.gsub('-', '_').upcase!.to_sym, mixed_case.downcase.freeze) + end + else + headers.each do |mixed_case| + const_set(mixed_case.gsub('-', '_').upcase!.to_sym, mixed_case.freeze) + end + end + end + # Base class used for Roda responses. The instance methods for this # class are added by Roda::RodaPlugins::Base::ResponseMethods, the class # methods are added by Roda::RodaPlugins::Base::ResponseClassMethods. class RodaResponse @roda_class = ::Roda @@ -28,11 +47,11 @@ end end # Instance methods for RodaResponse module ResponseMethods - DEFAULT_HEADERS = {"Content-Type" => "text/html".freeze}.freeze + DEFAULT_HEADERS = {RodaResponseHeaders::CONTENT_TYPE => "text/html".freeze}.freeze # The body for the current response. attr_reader :body # The hash of response headers for the current response. @@ -40,24 +59,15 @@ # The status code to use for the response. If none is given, will use 200 # code for non-empty responses and a 404 code for empty responses. attr_accessor :status - if defined?(Rack::Headers) && Rack::Headers.is_a?(Class) - # Set the default headers when creating a response. - def initialize - @headers = Rack::Headers.new - @body = [] - @length = 0 - end - else - # Set the default headers when creating a response. - def initialize - @headers = {} - @body = [] - @length = 0 - end + # Set the default headers when creating a response. + def initialize + @headers = _initialize_headers + @body = [] + @length = 0 end # Return the response header with the given key. Example: # # response['Content-Type'] # => 'text/html' @@ -106,19 +116,19 @@ h = @headers if b.empty? s = @status || 404 if (s == 304 || s == 204 || (s >= 100 && s <= 199)) - h.delete("Content-Type") + h.delete(RodaResponseHeaders::CONTENT_TYPE) elsif s == 205 empty_205_headers(h) else - h["Content-Length"] ||= '0' + h[RodaResponseHeaders::CONTENT_LENGTH] ||= '0' end else s = @status || default_status - h["Content-Length"] ||= @length.to_s + h[RodaResponseHeaders::CONTENT_LENGTH] ||= @length.to_s end [s, h, b] end @@ -147,11 +157,11 @@ # to the given status. Example: # # response.redirect('foo', 301) # response.redirect('bar') def redirect(path, status = 302) - @headers["Location"] = path + @headers[RodaResponseHeaders::LOCATION] = path @status = status nil end # Return the Roda class related to this response. @@ -169,21 +179,33 @@ nil end private + if defined?(Rack::Headers) && Rack::Headers.is_a?(Class) + # Use Rack::Headers for headers by default on Rack 3 + def _initialize_headers + Rack::Headers.new + end + else + # Use plain hash for headers by default on Rack 1-2 + def _initialize_headers + {} + end + end + if Rack.release < '2.0.2' # Don't use a content length for empty 205 responses on # rack 1, as it violates Rack::Lint in that version. def empty_205_headers(headers) - headers.delete("Content-Type") - headers.delete("Content-Length") + headers.delete(RodaResponseHeaders::CONTENT_TYPE) + headers.delete(RodaResponseHeaders::CONTENT_LENGTH) end else # Set the content length for empty 205 responses to 0 def empty_205_headers(headers) - headers.delete("Content-Type") - headers["Content-Length"] = '0' + headers.delete(RodaResponseHeaders::CONTENT_TYPE) + headers[RodaResponseHeaders::CONTENT_LENGTH] = '0' end end # For each default header, if a header has not already been set for the # response, set the header in the response.