require 'action_controller/cgi_ext/cgi_ext' require 'action_controller/cgi_ext/cookie_performance_fix' require 'action_controller/cgi_ext/raw_post_data_fix' module ActionController #:nodoc: class Base # Process a request extracted from an CGI object and return a response. Pass false as session_options to disable # sessions (large performance increase if sessions are not needed). The session_options are the same as for CGI::Session: # # * :database_manager - standard options are CGI::Session::FileStore, CGI::Session::MemoryStore, and CGI::Session::PStore # (default). Additionally, there is CGI::Session::DRbStore and CGI::Session::ActiveRecordStore. Read more about these in # lib/action_controller/session. # * :session_key - the parameter name used for the session id. Defaults to '_session_id'. # * :session_id - the session id to use. If not provided, then it is retrieved from the +session_key+ parameter # of the request, or automatically generated for a new session. # * :new_session - if true, force creation of a new session. If not set, a new session is only created if none currently # exists. If false, a new session is never created, and if none currently exists and the +session_id+ option is not set, # an ArgumentError is raised. # * :session_expires - the time the current session expires, as a +Time+ object. If not set, the session will continue # indefinitely. # * :session_domain - the hostname domain for which this session is valid. If not set, defaults to the hostname of the # server. # * :session_secure - if +true+, this session will only work over HTTPS. # * :session_path - the path for which this session applies. Defaults to the directory of the CGI script. def self.process_cgi(cgi = CGI.new, session_options = {}) new.process_cgi(cgi, session_options) end def process_cgi(cgi, session_options = {}) #:nodoc: process(CgiRequest.new(cgi, session_options), CgiResponse.new(cgi)).out end end class CgiRequest < AbstractRequest #:nodoc: attr_accessor :cgi, :session_options DEFAULT_SESSION_OPTIONS = { :database_manager => CGI::Session::PStore, :prefix => "ruby_sess.", :session_path => "/" } unless const_defined?(:DEFAULT_SESSION_OPTIONS) def initialize(cgi, session_options = {}) @cgi = cgi @session_options = session_options @env = @cgi.send(:env_table) super() end def query_string if (qs = @cgi.query_string) && !qs.empty? qs elsif uri = @env['REQUEST_URI'] parts = uri.split('?') parts.shift parts.join('?') else @env['QUERY_STRING'] || '' end end def query_parameters (qs = self.query_string).empty? ? {} : CGIMethods.parse_query_parameters(qs) end def request_parameters if ActionController::Base.param_parsers.has_key?(content_type) CGIMethods.parse_formatted_request_parameters(content_type, @env['RAW_POST_DATA']) else CGIMethods.parse_request_parameters(@cgi.params) end end def cookies @cgi.cookies.freeze end def host_with_port if forwarded = env["HTTP_X_FORWARDED_HOST"] forwarded.split(/,\s?/).last elsif http_host = env['HTTP_HOST'] http_host elsif server_name = env['SERVER_NAME'] server_name else "#{env['SERVER_ADDR']}:#{env['SERVER_PORT']}" end end def host host_with_port[/^[^:]+/] end def port if host_with_port =~ /:(\d+)$/ $1.to_i else standard_port end end def session unless @session if @session_options == false @session = Hash.new else stale_session_check! do if session_options_with_string_keys['new_session'] == true @session = new_session else @session = CGI::Session.new(@cgi, session_options_with_string_keys) end @session['__valid_session'] end end end @session end def reset_session @session.delete if CGI::Session === @session @session = new_session end def method_missing(method_id, *arguments) @cgi.send(method_id, *arguments) rescue super end private # Delete an old session if it exists then create a new one. def new_session if @session_options == false Hash.new else CGI::Session.new(@cgi, session_options_with_string_keys.merge("new_session" => false)).delete rescue nil CGI::Session.new(@cgi, session_options_with_string_keys.merge("new_session" => true)) end end def stale_session_check! yield rescue ArgumentError => argument_error if argument_error.message =~ %r{undefined class/module (\w+)} begin Module.const_missing($1) rescue LoadError, NameError => const_error raise ActionController::SessionRestoreError, < e # lost connection to the FCGI process -- ignore the output, then end end private def convert_content_type!(headers) if header = headers.delete("Content-Type") headers["type"] = header end if header = headers.delete("Content-type") headers["type"] = header end if header = headers.delete("content-type") headers["type"] = header end end end end