aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib
diff options
context:
space:
mode:
authorPratik Naik <pratiknaik@gmail.com>2008-12-19 14:10:42 +0000
committerPratik Naik <pratiknaik@gmail.com>2008-12-19 14:10:42 +0000
commit6c375d95054169e7fa02401a1838664e56a195ae (patch)
treebb1ae42b6534fcecf71a4e992f516e7b4592fda4 /actionpack/lib
parent46702e6a6d26b66b67076129fcf9c59c1c27bbeb (diff)
parent89b75814045e811d52b19278ae27b5f45c6d9dd6 (diff)
downloadrails-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.rb1
-rw-r--r--actionpack/lib/action_controller/base.rb5
-rw-r--r--actionpack/lib/action_controller/cookies.rb2
-rw-r--r--actionpack/lib/action_controller/dispatcher.rb59
-rw-r--r--actionpack/lib/action_controller/failsafe.rb2
-rw-r--r--actionpack/lib/action_controller/integration.rb29
-rw-r--r--actionpack/lib/action_controller/lock.rb16
-rw-r--r--actionpack/lib/action_controller/middleware_stack.rb34
-rw-r--r--actionpack/lib/action_controller/rack_process.rb9
-rw-r--r--actionpack/lib/action_controller/rescue.rb2
-rw-r--r--actionpack/lib/action_controller/routing/recognition_optimisation.rb2
-rw-r--r--actionpack/lib/action_controller/session/abstract_store.rb29
-rw-r--r--actionpack/lib/action_controller/session/cookie_store.rb60
-rw-r--r--actionpack/lib/action_controller/session_management.rb29
-rw-r--r--actionpack/lib/action_view/template.rb10
-rw-r--r--actionpack/lib/action_view/template_handlers.rb25
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