diff options
Diffstat (limited to 'actionpack/lib/action_dispatch/middleware')
5 files changed, 107 insertions, 43 deletions
diff --git a/actionpack/lib/action_dispatch/middleware/closed_error.rb b/actionpack/lib/action_dispatch/middleware/closed_error.rb new file mode 100644 index 0000000000..0a4db47f4b --- /dev/null +++ b/actionpack/lib/action_dispatch/middleware/closed_error.rb @@ -0,0 +1,7 @@ +module ActionDispatch + class ClosedError < StandardError #:nodoc: + def initialize(kind) + super "Cannot modify #{kind} because it was closed. This means it was already streamed back to the client or converted to HTTP headers." + end + end +end diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index 7ac608f0a8..24ebb8fed7 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -83,7 +83,7 @@ module ActionDispatch # Raised when storing more than 4K of session data. class CookieOverflow < StandardError; end - class CookieJar < Hash #:nodoc: + class CookieJar #:nodoc: # This regular expression is used to split the levels of a domain. # The top level domain can be any string without a period or @@ -115,13 +115,22 @@ module ActionDispatch @delete_cookies = {} @host = host @secure = secure - - super() + @closed = false + @cookies = {} end + attr_reader :closed + alias :closed? :closed + def close!; @closed = true end + # Returns the value of the cookie by +name+, or +nil+ if no such cookie exists. def [](name) - super(name.to_s) + @cookies[name.to_s] + end + + def update(other_hash) + @cookies.update other_hash + self end def handle_options(options) #:nodoc: @@ -145,6 +154,7 @@ module ActionDispatch # Sets the cookie named +name+. The second argument may be the very cookie # value, or a hash of options as documented above. def []=(key, options) + raise ClosedError, :cookies if closed? if options.is_a?(Hash) options.symbolize_keys! value = options[:value] @@ -153,7 +163,7 @@ module ActionDispatch options = { :value => value } end - value = super(key.to_s, value) + value = @cookies[key.to_s] = value handle_options(options) @@ -170,7 +180,7 @@ module ActionDispatch handle_options(options) - value = super(key.to_s) + value = @cookies.delete(key.to_s) @delete_cookies[key] = options value end @@ -225,6 +235,7 @@ module ActionDispatch end def []=(key, options) + raise ClosedError, :cookies if closed? if options.is_a?(Hash) options.symbolize_keys! else @@ -263,6 +274,7 @@ module ActionDispatch end def []=(key, options) + raise ClosedError, :cookies if closed? if options.is_a?(Hash) options.symbolize_keys! options[:value] = @verifier.generate(options[:value]) @@ -305,6 +317,7 @@ module ActionDispatch end def call(env) + cookie_jar = nil status, headers, body = @app.call(env) if cookie_jar = env['action_dispatch.cookies'] @@ -315,6 +328,9 @@ module ActionDispatch end [status, headers, body] + ensure + cookie_jar = ActionDispatch::Request.new(env).cookie_jar unless cookie_jar + cookie_jar.close! end end end diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb index 21aeeb217a..027ff7f8ac 100644 --- a/actionpack/lib/action_dispatch/middleware/flash.rb +++ b/actionpack/lib/action_dispatch/middleware/flash.rb @@ -43,9 +43,15 @@ module ActionDispatch class FlashNow #:nodoc: def initialize(flash) @flash = flash + @closed = false end + attr_reader :closed + alias :closed? :closed + def close!; @closed = true end + def []=(k, v) + raise ClosedError, :flash if closed? @flash[k] = v @flash.discard(k) v @@ -66,27 +72,70 @@ module ActionDispatch end end - class FlashHash < Hash + class FlashHash + include Enumerable + def initialize #:nodoc: - super - @used = Set.new + @used = Set.new + @closed = false + @flashes = {} end + attr_reader :closed + alias :closed? :closed + def close!; @closed = true end + def []=(k, v) #:nodoc: + raise ClosedError, :flash if closed? keep(k) - super + @flashes[k] = v + end + + def [](k) + @flashes[k] end def update(h) #:nodoc: h.keys.each { |k| keep(k) } - super + @flashes.update h + self + end + + def keys + @flashes.keys + end + + def key?(name) + @flashes.key? name + end + + def delete(key) + @flashes.delete key + self + end + + def to_hash + @flashes.dup + end + + def empty? + @flashes.empty? + end + + def clear + @flashes.clear + end + + def each(&block) + @flashes.each(&block) end alias :merge! :update def replace(h) #:nodoc: @used = Set.new - super + @flashes.replace h + self end # Sets a flash that will not be available to the next action, only to the current. @@ -100,7 +149,7 @@ module ActionDispatch # # Entries set via <tt>now</tt> are accessed the same way as standard entries: <tt>flash['my-key']</tt>. def now - FlashNow.new(self) + @now ||= FlashNow.new(self) end # Keeps either the entire current flash or a specific flash entry available for the next action: @@ -184,8 +233,11 @@ module ActionDispatch session = env['rack.session'] || {} flash_hash = env['action_dispatch.request.flash_hash'] - if flash_hash && (!flash_hash.empty? || session.key?('flash')) + if flash_hash + if !flash_hash.empty? || session.key?('flash') session["flash"] = flash_hash + end + flash_hash.close! end if session.key?('flash') && session['flash'].empty? diff --git a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb index 64d3a87fd0..1a811ce1b1 100644 --- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb @@ -29,7 +29,9 @@ module ActionDispatch end def generate_sid - ActiveSupport::SecureRandom.hex(16) + sid = ActiveSupport::SecureRandom.hex(16) + sid.encode!('UTF-8') if sid.respond_to?(:encode!) + sid end protected diff --git a/actionpack/lib/action_dispatch/middleware/static.rb b/actionpack/lib/action_dispatch/middleware/static.rb index c57f694c4d..348f7b86b8 100644 --- a/actionpack/lib/action_dispatch/middleware/static.rb +++ b/actionpack/lib/action_dispatch/middleware/static.rb @@ -2,25 +2,23 @@ require 'rack/utils' module ActionDispatch class FileHandler - def initialize(at, root) - @at, @root = at.chomp('/'), root.chomp('/') - @compiled_at = @at.blank? ? nil : /^#{Regexp.escape(at)}/ + def initialize(root) + @root = root.chomp('/') @compiled_root = /^#{Regexp.escape(root)}/ @file_server = ::Rack::File.new(@root) end def match?(path) path = path.dup - if !@compiled_at || path.sub!(@compiled_at, '') - full_path = path.empty? ? @root : File.join(@root, ::Rack::Utils.unescape(path)) - paths = "#{full_path}#{ext}" - matches = Dir[paths] - match = matches.detect { |m| File.file?(m) } - if match - match.sub!(@compiled_root, '') - match - end + full_path = path.empty? ? @root : File.join(@root, ::Rack::Utils.unescape(path)) + paths = "#{full_path}#{ext}" + + matches = Dir[paths] + match = matches.detect { |m| File.file?(m) } + if match + match.sub!(@compiled_root, '') + match end end @@ -39,9 +37,9 @@ module ActionDispatch class Static FILE_METHODS = %w(GET HEAD).freeze - def initialize(app, roots) + def initialize(app, path) @app = app - @file_handlers = create_file_handlers(roots) + @file_handler = FileHandler.new(path) end def call(env) @@ -49,24 +47,13 @@ module ActionDispatch method = env['REQUEST_METHOD'] if FILE_METHODS.include?(method) - @file_handlers.each do |file_handler| - if match = file_handler.match?(path) - env["PATH_INFO"] = match - return file_handler.call(env) - end + if match = @file_handler.match?(path) + env["PATH_INFO"] = match + return @file_handler.call(env) end end @app.call(env) end - - private - def create_file_handlers(roots) - roots = { '' => roots } unless roots.is_a?(Hash) - - roots.map do |at, root| - FileHandler.new(at, root) if File.exist?(root) - end.compact - end end end |