diff options
author | Pratik Naik <pratiknaik@gmail.com> | 2008-12-19 14:10:42 +0000 |
---|---|---|
committer | Pratik Naik <pratiknaik@gmail.com> | 2008-12-19 14:10:42 +0000 |
commit | 6c375d95054169e7fa02401a1838664e56a195ae (patch) | |
tree | bb1ae42b6534fcecf71a4e992f516e7b4592fda4 /actionpack/lib | |
parent | 46702e6a6d26b66b67076129fcf9c59c1c27bbeb (diff) | |
parent | 89b75814045e811d52b19278ae27b5f45c6d9dd6 (diff) | |
download | rails-6c375d95054169e7fa02401a1838664e56a195ae.tar.gz rails-6c375d95054169e7fa02401a1838664e56a195ae.tar.bz2 rails-6c375d95054169e7fa02401a1838664e56a195ae.zip |
Merge commit 'mainstream/master'
Diffstat (limited to 'actionpack/lib')
-rw-r--r-- | actionpack/lib/action_controller.rb | 1 | ||||
-rw-r--r-- | actionpack/lib/action_controller/base.rb | 5 | ||||
-rw-r--r-- | actionpack/lib/action_controller/cookies.rb | 2 | ||||
-rw-r--r-- | actionpack/lib/action_controller/dispatcher.rb | 59 | ||||
-rw-r--r-- | actionpack/lib/action_controller/failsafe.rb | 2 | ||||
-rw-r--r-- | actionpack/lib/action_controller/integration.rb | 29 | ||||
-rw-r--r-- | actionpack/lib/action_controller/lock.rb | 16 | ||||
-rw-r--r-- | actionpack/lib/action_controller/middleware_stack.rb | 34 | ||||
-rw-r--r-- | actionpack/lib/action_controller/rack_process.rb | 9 | ||||
-rw-r--r-- | actionpack/lib/action_controller/rescue.rb | 2 | ||||
-rw-r--r-- | actionpack/lib/action_controller/routing/recognition_optimisation.rb | 2 | ||||
-rw-r--r-- | actionpack/lib/action_controller/session/abstract_store.rb | 29 | ||||
-rw-r--r-- | actionpack/lib/action_controller/session/cookie_store.rb | 60 | ||||
-rw-r--r-- | actionpack/lib/action_controller/session_management.rb | 29 | ||||
-rw-r--r-- | actionpack/lib/action_view/template.rb | 10 | ||||
-rw-r--r-- | actionpack/lib/action_view/template_handlers.rb | 25 |
16 files changed, 176 insertions, 138 deletions
diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index c170e4dd2a..eaf7779f1e 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -56,6 +56,7 @@ module ActionController autoload :Integration, 'action_controller/integration' autoload :IntegrationTest, 'action_controller/integration' autoload :Layout, 'action_controller/layout' + autoload :Lock, 'action_controller/lock' autoload :MiddlewareStack, 'action_controller/middleware_stack' autoload :MimeResponds, 'action_controller/mime_responds' autoload :PolymorphicRoutes, 'action_controller/polymorphic_routes' diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 0b32da55d5..454ef4ffac 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -1160,13 +1160,8 @@ module ActionController #:nodoc: def reset_session #:doc: request.reset_session @_session = request.session - #http://rails.lighthouseapp.com/projects/8994/tickets/1558-memory-problem-on-reset_session-in-around_filter#ticket-1558-1 - #MRI appears to have a GC related memory leak to do with the finalizer that is defined on CGI::Session - ObjectSpace.undefine_finalizer(@_session) - response.session = @_session end - private def render_for_file(template_path, status = nil, layout = nil, locals = {}) #:nodoc: logger.info("Rendering #{template_path}" + (status ? " (#{status})" : '')) if logger diff --git a/actionpack/lib/action_controller/cookies.rb b/actionpack/lib/action_controller/cookies.rb index 0428f2a23d..0e058085ec 100644 --- a/actionpack/lib/action_controller/cookies.rb +++ b/actionpack/lib/action_controller/cookies.rb @@ -67,6 +67,8 @@ module ActionController #:nodoc: cookie = @cookies[name.to_s] if cookie && cookie.respond_to?(:value) cookie.size > 1 ? cookie.value : cookie.value[0] + else + cookie end end diff --git a/actionpack/lib/action_controller/dispatcher.rb b/actionpack/lib/action_controller/dispatcher.rb index c9a9264b6d..11c4f057d8 100644 --- a/actionpack/lib/action_controller/dispatcher.rb +++ b/actionpack/lib/action_controller/dispatcher.rb @@ -2,8 +2,6 @@ module ActionController # Dispatches requests to the appropriate controller and takes care of # reloading the app after each request when Dependencies.load? is true. class Dispatcher - @@guard = Mutex.new - class << self def define_dispatcher_callbacks(cache_classes) unless cache_classes @@ -46,40 +44,49 @@ module ActionController cattr_accessor :middleware self.middleware = MiddlewareStack.new do |middleware| + middleware.use "ActionController::Lock", :if => lambda { + !ActionController::Base.allow_concurrency + } middleware.use "ActionController::Failsafe" - middleware.use "ActionController::SessionManagement::Middleware" + + ["ActionController::Session::CookieStore", + "ActionController::Session::MemCacheStore", + "ActiveRecord::SessionStore"].each do |store| + middleware.use(store, ActionController::Base.session_options, + :if => lambda { + if session_store = ActionController::Base.session_store + session_store.name == store + end + } + ) + end end include ActiveSupport::Callbacks define_callbacks :prepare_dispatch, :before_dispatch, :after_dispatch - # DEPRECATE: Remove arguments + # DEPRECATE: Remove arguments, since they are only used by CGI def initialize(output = $stdout, request = nil, response = nil) - @output, @request, @response = output, request, response + @output = output @app = @@middleware.build(lambda { |env| self.dup._call(env) }) end - def dispatch_unlocked + def dispatch begin run_callbacks :before_dispatch - handle_request + controller = Routing::Routes.recognize(@request) + controller.process(@request, @response).to_a rescue Exception => exception - failsafe_rescue exception + if controller ||= (::ApplicationController rescue Base) + controller.process_with_exception(@request, @response, exception).to_a + else + raise exception + end ensure run_callbacks :after_dispatch, :enumerator => :reverse_each end end - def dispatch - if ActionController::Base.allow_concurrency - dispatch_unlocked - else - @@guard.synchronize do - dispatch_unlocked - end - end - end - # DEPRECATE: Remove CGI support def dispatch_cgi(cgi, session_options) CGIHandler.dispatch_cgi(self, cgi, @output) @@ -118,22 +125,8 @@ module ActionController def checkin_connections # Don't return connection (and peform implicit rollback) if this request is a part of integration test # TODO: This callback should have direct access to env - return if @request.key?("action_controller.test") + return if @request.key?("rack.test") ActiveRecord::Base.clear_active_connections! end - - protected - def handle_request - @controller = Routing::Routes.recognize(@request) - @controller.process(@request, @response).out - end - - def failsafe_rescue(exception) - if @controller ||= (::ApplicationController rescue Base) - @controller.process_with_exception(@request, @response, exception).out - else - raise exception - end - end end end diff --git a/actionpack/lib/action_controller/failsafe.rb b/actionpack/lib/action_controller/failsafe.rb index b1e9957b49..567581142c 100644 --- a/actionpack/lib/action_controller/failsafe.rb +++ b/actionpack/lib/action_controller/failsafe.rb @@ -11,7 +11,7 @@ module ActionController @app.call(env) rescue Exception => exception # Reraise exception in test environment - if env["action_controller.test"] + if env["rack.test"] raise exception else failsafe_response(exception) diff --git a/actionpack/lib/action_controller/integration.rb b/actionpack/lib/action_controller/integration.rb index 1b0543033b..7590d5d710 100644 --- a/actionpack/lib/action_controller/integration.rb +++ b/actionpack/lib/action_controller/integration.rb @@ -276,6 +276,7 @@ module ActionController "SCRIPT_NAME" => "", "REQUEST_URI" => path, + "PATH_INFO" => path, "HTTP_HOST" => host, "REMOTE_ADDR" => remote_addr, "CONTENT_TYPE" => "application/x-www-form-urlencoded", @@ -290,7 +291,7 @@ module ActionController "rack.multiprocess" => true, "rack.run_once" => false, - "action_controller.test" => true + "rack.test" => true ) (headers || {}).each do |key, value| @@ -310,16 +311,6 @@ module ActionController status, headers, body = app.call(env) @request_count += 1 - if @controller = ActionController::Base.last_instantiation - @request = @controller.request - @response = @controller.response - - # Decorate the response with the standard behavior of the - # TestResponse so that things like assert_response can be - # used in integration tests. - @response.extend(TestResponseBehavior) - end - @html_document = nil @status = status.to_i @@ -335,6 +326,22 @@ module ActionController @body = "" body.each { |part| @body << part } + if @controller = ActionController::Base.last_instantiation + @request = @controller.request + @response = @controller.response + else + # Decorate responses from Rack Middleware and Rails Metal + # as an AbstractResponse for the purposes of integration testing + @response = AbstractResponse.new + @response.headers = @headers.merge('Status' => status.to_s) + @response.body = @body + end + + # Decorate the response with the standard behavior of the + # TestResponse so that things like assert_response can be + # used in integration tests. + @response.extend(TestResponseBehavior) + return @status rescue MultiPartNeededException boundary = "----------XnJLe9ZIbbGUYtzPQJ16u1" diff --git a/actionpack/lib/action_controller/lock.rb b/actionpack/lib/action_controller/lock.rb new file mode 100644 index 0000000000..c50762216e --- /dev/null +++ b/actionpack/lib/action_controller/lock.rb @@ -0,0 +1,16 @@ +module ActionController + class Lock + FLAG = 'rack.multithread'.freeze + + def initialize(app, lock = Mutex.new) + @app, @lock = app, lock + end + + def call(env) + old, env[FLAG] = env[FLAG], false + @lock.synchronize { @app.call(env) } + ensure + env[FLAG] = old + end + end +end diff --git a/actionpack/lib/action_controller/middleware_stack.rb b/actionpack/lib/action_controller/middleware_stack.rb index a6597a6fec..74f28565c0 100644 --- a/actionpack/lib/action_controller/middleware_stack.rb +++ b/actionpack/lib/action_controller/middleware_stack.rb @@ -1,19 +1,39 @@ module ActionController class MiddlewareStack < Array class Middleware - attr_reader :klass, :args, :block + attr_reader :args, :block def initialize(klass, *args, &block) - if klass.is_a?(Class) - @klass = klass + @klass = klass + + options = args.extract_options! + if options.has_key?(:if) + @conditional = options.delete(:if) else - @klass = klass.to_s.constantize + @conditional = true end + args << options unless options.empty? @args = args @block = block end + def klass + if @klass.is_a?(Class) + @klass + else + @klass.to_s.constantize + end + end + + def active? + if @conditional.respond_to?(:call) + @conditional.call + else + @conditional + end + end + def ==(middleware) case middleware when Middleware @@ -50,8 +70,12 @@ module ActionController push(middleware) end + def active + find_all { |middleware| middleware.active? } + end + def build(app) - reverse.inject(app) { |a, e| e.build(a) } + active.reverse.inject(app) { |a, e| e.build(a) } end end end diff --git a/actionpack/lib/action_controller/rack_process.rb b/actionpack/lib/action_controller/rack_process.rb index e783839f34..8483f8e289 100644 --- a/actionpack/lib/action_controller/rack_process.rb +++ b/actionpack/lib/action_controller/rack_process.rb @@ -83,11 +83,7 @@ module ActionController #:nodoc: @status || super end - def out(&block) - # Nasty hack because CGI sessions are closed after the normal - # prepare! statement - set_cookies! - + def to_a(&block) @block = block @status = headers.delete("Status") if [204, 304].include?(status.to_i) @@ -97,7 +93,6 @@ module ActionController #:nodoc: [status, headers.to_hash, self] end end - alias to_a out def each(&callback) if @body.respond_to?(:call) @@ -132,7 +127,7 @@ module ActionController #:nodoc: convert_language! convert_expires! set_status! - # set_cookies! + set_cookies! end private diff --git a/actionpack/lib/action_controller/rescue.rb b/actionpack/lib/action_controller/rescue.rb index 24ee160ee8..b8b0175b5f 100644 --- a/actionpack/lib/action_controller/rescue.rb +++ b/actionpack/lib/action_controller/rescue.rb @@ -104,7 +104,7 @@ module ActionController #:nodoc: status = interpret_status(status_code) path = "#{Rails.public_path}/#{status[0,3]}.html" if File.exist?(path) - render :file => path, :status => status + render :file => path, :status => status, :content_type => Mime::HTML else head status end diff --git a/actionpack/lib/action_controller/routing/recognition_optimisation.rb b/actionpack/lib/action_controller/routing/recognition_optimisation.rb index 3b98b16683..ebc553512f 100644 --- a/actionpack/lib/action_controller/routing/recognition_optimisation.rb +++ b/actionpack/lib/action_controller/routing/recognition_optimisation.rb @@ -56,7 +56,7 @@ module ActionController result = recognize_optimized(path, environment) and return result # Route was not recognized. Try to find out why (maybe wrong verb). - allows = HTTP_METHODS.select { |verb| routes.find { |r| r.recognize(path, :method => verb) } } + allows = HTTP_METHODS.select { |verb| routes.find { |r| r.recognize(path, environment.merge(:method => verb)) } } if environment[:method] && !HTTP_METHODS.include?(environment[:method]) raise NotImplemented.new(*allows) diff --git a/actionpack/lib/action_controller/session/abstract_store.rb b/actionpack/lib/action_controller/session/abstract_store.rb index c6dd865fad..d4b185aaa2 100644 --- a/actionpack/lib/action_controller/session/abstract_store.rb +++ b/actionpack/lib/action_controller/session/abstract_store.rb @@ -11,6 +11,7 @@ module ActionController class SessionHash < Hash def initialize(by, env) + super() @by = by @env = env @loaded = false @@ -21,6 +22,13 @@ module ActionController @id end + def session_id + ActiveSupport::Deprecation.warn( + "ActionController::Session::AbstractStore::SessionHash#session_id" + + "has been deprecated.Please use #id instead.", caller) + id + end + def [](key) load! unless @loaded super @@ -37,6 +45,13 @@ module ActionController h end + def data + ActiveSupport::Deprecation.warn( + "ActionController::Session::AbstractStore::SessionHash#data" + + "has been deprecated.Please use #to_hash instead.", caller) + to_hash + end + private def load! @id, session = @by.send(:load_session, @env) @@ -46,7 +61,7 @@ module ActionController end DEFAULT_OPTIONS = { - :key => 'rack.session', + :key => '_session_id', :path => '/', :domain => nil, :expire_after => nil, @@ -56,6 +71,18 @@ module ActionController } def initialize(app, options = {}) + # Process legacy CGI options + options = options.symbolize_keys + if options.has_key?(:session_path) + options[:path] = options.delete(:session_path) + end + if options.has_key?(:session_key) + options[:key] = options.delete(:session_key) + end + if options.has_key?(:session_http_only) + options[:httponly] = options.delete(:session_http_only) + end + @app = app @default_options = DEFAULT_OPTIONS.merge(options) @key = @default_options[:key] diff --git a/actionpack/lib/action_controller/session/cookie_store.rb b/actionpack/lib/action_controller/session/cookie_store.rb index f4089bfa8b..ba63f8521f 100644 --- a/actionpack/lib/action_controller/session/cookie_store.rb +++ b/actionpack/lib/action_controller/session/cookie_store.rb @@ -41,9 +41,11 @@ module ActionController SECRET_MIN_LENGTH = 30 # characters DEFAULT_OPTIONS = { - :domain => nil, - :path => "/", - :expire_after => nil + :key => '_session_id', + :domain => nil, + :path => "/", + :expire_after => nil, + :httponly => false }.freeze ENV_SESSION_KEY = "rack.session".freeze @@ -56,6 +58,18 @@ module ActionController def initialize(app, options = {}) options = options.dup + # Process legacy CGI options + options = options.symbolize_keys + if options.has_key?(:session_path) + options[:path] = options.delete(:session_path) + end + if options.has_key?(:session_key) + options[:key] = options.delete(:session_key) + end + if options.has_key?(:session_http_only) + options[:httponly] = options.delete(:session_http_only) + end + @app = app # The session_key option is required. @@ -74,21 +88,12 @@ module ActionController freeze end - class SessionHash < AbstractStore::SessionHash - private - def load! - session = @by.send(:load_session, @env) - replace(session) - @loaded = true - end - end - def call(env) - session_data = SessionHash.new(self, env) + session_data = AbstractStore::SessionHash.new(self, env) original_value = session_data.dup env[ENV_SESSION_KEY] = session_data - env[ENV_SESSION_OPTIONS_KEY] = @default_options.dup + env[ENV_SESSION_OPTIONS_KEY] = @default_options status, headers, body = @app.call(env) @@ -142,17 +147,18 @@ module ActionController def load_session(env) request = Rack::Request.new(env) session_data = request.cookies[@key] - unmarshal(session_data) || {} + data = unmarshal(session_data) || persistent_session_id!({}) + [data[:session_id], data] end # Marshal a session hash into safe cookie data. Include an integrity hash. def marshal(session) - @verifier.generate(session) + @verifier.generate( persistent_session_id!(session)) end # Unmarshal cookie data to a hash and verify its integrity. def unmarshal(cookie) - @verifier.verify(cookie) if cookie + persistent_session_id!(@verifier.verify(cookie)) if cookie rescue ActiveSupport::MessageVerifier::InvalidSignature nil end @@ -195,6 +201,26 @@ module ActionController key = secret.respond_to?(:call) ? secret.call : secret ActiveSupport::MessageVerifier.new(key, digest) end + + def generate_sid + ActiveSupport::SecureRandom.hex(16) + end + + def persistent_session_id!(data) + (data ||= {}).merge!(inject_persistent_session_id(data)) + end + + def inject_persistent_session_id(data) + requires_session_id?(data) ? { :session_id => generate_sid } : {} + end + + def requires_session_id?(data) + if data + data.respond_to?(:key?) && !data.key?(:session_id) + else + true + end + end end end end diff --git a/actionpack/lib/action_controller/session_management.rb b/actionpack/lib/action_controller/session_management.rb index a9989d8198..f06a0da75c 100644 --- a/actionpack/lib/action_controller/session_management.rb +++ b/actionpack/lib/action_controller/session_management.rb @@ -6,35 +6,6 @@ module ActionController #:nodoc: end end - class Middleware - DEFAULT_OPTIONS = { - :path => "/", - :key => "_session_id", - :httponly => true, - }.freeze - - def self.new(app) - cgi_options = ActionController::Base.session_options - options = cgi_options.symbolize_keys - options = DEFAULT_OPTIONS.merge(options) - if options.has_key?(:session_path) - options[:path] = options.delete(:session_path) - end - if options.has_key?(:session_key) - options[:key] = options.delete(:session_key) - end - if options.has_key?(:session_http_only) - options[:httponly] = options.delete(:session_http_only) - end - - if store = ActionController::Base.session_store - store.new(app, options) - else # Sessions disabled - lambda { |env| app.call(env) } - end - end - end - module ClassMethods # Set the session store to be used for keeping the session data between requests. # By default, sessions are stored in browser cookies (<tt>:cookie_store</tt>), diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb index 8f4ca433c0..93748638c3 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -98,6 +98,10 @@ module ActionView #:nodoc: end private + def valid_extension?(extension) + Template.template_handler_extensions.include?(extension) + end + def find_full_path(path, load_paths) load_paths = Array(load_paths) + [nil] load_paths.each do |load_path| @@ -111,11 +115,11 @@ module ActionView #:nodoc: # [base_path, name, format, extension] def split(file) if m = file.match(/^(.*\/)?([^\.]+)\.?(\w+)?\.?(\w+)?\.?(\w+)?$/) - if Template.valid_extension?(m[5]) # Multipart formats + if valid_extension?(m[5]) # Multipart formats [m[1], m[2], "#{m[3]}.#{m[4]}", m[5]] - elsif Template.valid_extension?(m[4]) # Single format + elsif valid_extension?(m[4]) # Single format [m[1], m[2], m[3], m[4]] - elsif Template.valid_extension?(m[3]) # No format + elsif valid_extension?(m[3]) # No format [m[1], m[2], nil, m[3]] else # No extension [m[1], m[2], m[3], nil] diff --git a/actionpack/lib/action_view/template_handlers.rb b/actionpack/lib/action_view/template_handlers.rb index c50a51b0d1..d06ddd5fb5 100644 --- a/actionpack/lib/action_view/template_handlers.rb +++ b/actionpack/lib/action_view/template_handlers.rb @@ -28,10 +28,6 @@ module ActionView #:nodoc: @@template_handlers[extension.to_sym] = klass end - def valid_extension?(extension) - template_handler_extensions.include?(extension) || init_path_for_extension(extension) - end - def template_handler_extensions @@template_handlers.keys.map(&:to_s).sort end @@ -42,26 +38,7 @@ module ActionView #:nodoc: end def handler_class_for_extension(extension) - (extension && @@template_handlers[extension.to_sym] || autoload_handler_class(extension)) || - @@default_template_handlers + (extension && @@template_handlers[extension.to_sym]) || @@default_template_handlers end - - private - def autoload_handler_class(extension) - return if Gem.loaded_specs[extension] - return unless init_path = init_path_for_extension(extension) - Gem.activate(extension) - load(init_path) - handler_class_for_extension(extension) - end - - # Returns the path to the rails/init.rb file for the given extension, - # or nil if no gem provides it. - def init_path_for_extension(extension) - return unless spec = Gem.searcher.find(extension.to_s) - returning File.join(spec.full_gem_path, 'rails', 'init.rb') do |path| - return unless File.file?(path) - end - end end end |