Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Boot out CGI Processor.

* Add ActionController::CGIHandler as a backwards compatible CGI wrapper around Rack.
* Also pull failsafe responder into ActionController::Failsafe middleware.
  • Loading branch information...
commit 9c9da6c892d715ca22c3cf51f50deb1d80029c66 1 parent 27ebfd7
@josh josh authored
View
8 actionpack/lib/action_controller.rb
@@ -46,10 +46,9 @@ def self.load_all!
autoload :Base, 'action_controller/base'
autoload :Benchmarking, 'action_controller/benchmarking'
autoload :Caching, 'action_controller/caching'
- autoload :CgiRequest, 'action_controller/cgi_process'
- autoload :CgiResponse, 'action_controller/cgi_process'
autoload :Cookies, 'action_controller/cookies'
autoload :Dispatcher, 'action_controller/dispatcher'
+ autoload :Failsafe, 'action_controller/failsafe'
autoload :Filters, 'action_controller/filters'
autoload :Flash, 'action_controller/flash'
autoload :Helpers, 'action_controller/helpers'
@@ -89,6 +88,11 @@ module Assertions
module Http
autoload :Headers, 'action_controller/headers'
end
+
+ # DEPRECATE: Remove CGI support
+ autoload :CgiRequest, 'action_controller/cgi_process'
+ autoload :CgiResponse, 'action_controller/cgi_process'
+ autoload :CGIHandler, 'action_controller/cgi_process'
end
class CGI
View
216 actionpack/lib/action_controller/cgi_process.rb
@@ -1,184 +1,72 @@
require 'action_controller/cgi_ext'
module ActionController #:nodoc:
- class Base
- # Process a request extracted from a CGI object and return a response. Pass false as <tt>session_options</tt> to disable
- # sessions (large performance increase if sessions are not needed). The <tt>session_options</tt> are the same as for CGI::Session:
- #
- # * <tt>:database_manager</tt> - 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.
- # * <tt>:session_key</tt> - the parameter name used for the session id. Defaults to '_session_id'.
- # * <tt>:session_id</tt> - the session id to use. If not provided, then it is retrieved from the +session_key+ cookie, or
- # automatically generated for a new session.
- # * <tt>:new_session</tt> - 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.
- # * <tt>:session_expires</tt> - the time the current session expires, as a Time object. If not set, the session will continue
- # indefinitely.
- # * <tt>:session_domain</tt> - the hostname domain for which this session is valid. If not set, defaults to the hostname of the
- # server.
- # * <tt>:session_secure</tt> - if +true+, this session will only work over HTTPS.
- # * <tt>:session_path</tt> - the path for which this session applies. Defaults to the directory of the CGI script.
- # * <tt>:cookie_only</tt> - if +true+ (the default), session IDs will only be accepted from cookies and not from
- # the query string or POST parameters. This protects against session fixation attacks.
- 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
- class SessionFixationAttempt < StandardError #:nodoc:
- end
-
- DEFAULT_SESSION_OPTIONS = {
- :database_manager => CGI::Session::CookieStore, # store data in cookie
- :prefix => "ruby_sess.", # prefix session file names
- :session_path => "/", # available to all paths in app
- :session_key => "_session_id",
- :cookie_only => true,
- :session_http_only=> true
- }
-
- def initialize(cgi, session_options = {})
- @cgi = cgi
- @session_options = session_options
- @env = @cgi.__send__(:env_table)
- super()
- end
-
- def query_string
- qs = @cgi.query_string if @cgi.respond_to?(:query_string)
- if !qs.blank?
- qs
- else
- super
- end
- end
-
- def body_stream #:nodoc:
- @cgi.stdinput
- end
-
- def cookies
- @cgi.cookies.freeze
- end
-
- def session
- unless defined?(@session)
- if @session_options == false
- @session = Hash.new
- else
- stale_session_check! do
- if cookie_only? && query_parameters[session_options_with_string_keys['session_key']]
- raise SessionFixationAttempt
- end
- case value = session_options_with_string_keys['new_session']
- when true
- @session = new_session
- when false
- begin
- @session = CGI::Session.new(@cgi, session_options_with_string_keys)
- # CGI::Session raises ArgumentError if 'new_session' == false
- # and no session cookie or query param is present.
- rescue ArgumentError
- @session = Hash.new
- end
- when nil
- @session = CGI::Session.new(@cgi, session_options_with_string_keys)
- else
- raise ArgumentError, "Invalid new_session option: #{value}"
- end
- @session['__valid_session']
- end
+ class CGIHandler
+ module ProperStream
+ def each
+ while line = gets
+ yield line
end
end
- @session
- end
- def reset_session
- @session.delete if defined?(@session) && @session.is_a?(CGI::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
+ def read(*args)
+ if args.empty?
+ super || ""
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))
+ super
end
end
+ end
- def cookie_only?
- session_options_with_string_keys['cookie_only']
- end
+ def self.dispatch_cgi(app, cgi, out = $stdout)
+ env = cgi.__send__(:env_table)
+ env.delete "HTTP_CONTENT_LENGTH"
- def stale_session_check!
- yield
- rescue ArgumentError => argument_error
- if argument_error.message =~ %r{undefined class/module ([\w:]*\w)}
- begin
- # Note that the regexp does not allow $1 to end with a ':'
- $1.constantize
- rescue LoadError, NameError => const_error
- raise ActionController::SessionRestoreError, <<-end_msg
-Session contains objects whose class definition isn\'t available.
-Remember to require the classes for all objects kept in the session.
-(Original exception: #{const_error.message} [#{const_error.class}])
-end_msg
- end
+ cgi.stdinput.extend ProperStream
- retry
- else
- raise
- end
- end
+ env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
- def session_options_with_string_keys
- @session_options_with_string_keys ||= DEFAULT_SESSION_OPTIONS.merge(@session_options).stringify_keys
- end
- end
+ env.update({
+ "rack.version" => [0,1],
+ "rack.input" => cgi.stdinput,
+ "rack.errors" => $stderr,
+ "rack.multithread" => false,
+ "rack.multiprocess" => true,
+ "rack.run_once" => false,
+ "rack.url_scheme" => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http"
+ })
- class CgiResponse < AbstractResponse #:nodoc:
- def initialize(cgi)
- @cgi = cgi
- super()
- end
-
- def out(output = $stdout)
- output.binmode if output.respond_to?(:binmode)
- output.sync = false if output.respond_to?(:sync=)
+ env["QUERY_STRING"] ||= ""
+ env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
+ env["REQUEST_PATH"] ||= "/"
+ env.delete "PATH_INFO" if env["PATH_INFO"] == ""
+ status, headers, body = app.call(env)
begin
- output.write(@cgi.header(@headers))
-
- if @cgi.__send__(:env_table)['REQUEST_METHOD'] == 'HEAD'
- return
- elsif @body.respond_to?(:call)
- # Flush the output now in case the @body Proc uses
- # #syswrite.
- output.flush if output.respond_to?(:flush)
- @body.call(self, output)
- else
- output.write(@body)
- end
-
- output.flush if output.respond_to?(:flush)
- rescue Errno::EPIPE, Errno::ECONNRESET
- # lost connection to parent process, ignore output
+ out.binmode if out.respond_to?(:binmode)
+ out.sync = false if out.respond_to?(:sync=)
+
+ headers['Status'] = status.to_s
+ out.write(cgi.header(headers))
+
+ body.each { |part|
+ out.write part
+ out.flush if out.respond_to?(:flush)
+ }
+ ensure
+ body.close if body.respond_to?(:close)
end
end
end
+
+ class CgiRequest #:nodoc:
+ DEFAULT_SESSION_OPTIONS = {
+ :database_manager => CGI::Session::CookieStore,
+ :prefix => "ruby_sess.",
+ :session_path => "/",
+ :session_key => "_session_id",
+ :cookie_only => true,
+ :session_http_only => true
+ }
+ end
end
View
83 actionpack/lib/action_controller/dispatcher.rb
@@ -24,8 +24,7 @@ def define_dispatcher_callbacks(cache_classes)
end
end
- # Backward-compatible class method takes CGI-specific args. Deprecated
- # in favor of Dispatcher.new(output, request, response).dispatch.
+ # DEPRECATE: Remove CGI support
def dispatch(cgi = nil, session_options = CgiRequest::DEFAULT_SESSION_OPTIONS, output = $stdout)
new(output).dispatch_cgi(cgi, session_options)
end
@@ -43,57 +42,16 @@ def to_prepare(identifier = nil, &block)
callback = ActiveSupport::Callbacks::Callback.new(:prepare_dispatch, block, :identifier => identifier)
@prepare_dispatch_callbacks.replace_or_append!(callback)
end
-
- # If the block raises, send status code as a last-ditch response.
- def failsafe_response(fallback_output, status, originating_exception = nil)
- yield
- rescue Exception => exception
- begin
- log_failsafe_exception(status, originating_exception || exception)
- body = failsafe_response_body(status)
- fallback_output.write "Status: #{status}\r\nContent-Type: text/html\r\n\r\n#{body}"
- nil
- rescue Exception => failsafe_error # Logger or IO errors
- $stderr.puts "Error during failsafe response: #{failsafe_error}"
- $stderr.puts "(originally #{originating_exception})" if originating_exception
- end
- end
-
- private
- def failsafe_response_body(status)
- error_path = "#{error_file_path}/#{status.to_s[0..2]}.html"
-
- if File.exist?(error_path)
- File.read(error_path)
- else
- "<html><body><h1>#{status}</h1></body></html>"
- end
- end
-
- def log_failsafe_exception(status, exception)
- message = "/!\\ FAILSAFE /!\\ #{Time.now}\n Status: #{status}\n"
- message << " #{exception}\n #{exception.backtrace.join("\n ")}" if exception
- failsafe_logger.fatal message
- end
-
- def failsafe_logger
- if defined?(::RAILS_DEFAULT_LOGGER) && !::RAILS_DEFAULT_LOGGER.nil?
- ::RAILS_DEFAULT_LOGGER
- else
- Logger.new($stderr)
- end
- end
end
cattr_accessor :middleware
self.middleware = MiddlewareStack.new
-
- cattr_accessor :error_file_path
- self.error_file_path = Rails.public_path if defined?(Rails.public_path)
+ self.middleware.use "ActionController::Failsafe"
include ActiveSupport::Callbacks
define_callbacks :prepare_dispatch, :before_dispatch, :after_dispatch
+ # DEPRECATE: Remove arguments
def initialize(output = $stdout, request = nil, response = nil)
@output, @request, @response = output, request, response
@app = @@middleware.build(lambda { |env| self.dup._call(env) })
@@ -120,14 +78,9 @@ def dispatch
end
end
+ # DEPRECATE: Remove CGI support
def dispatch_cgi(cgi, session_options)
- if cgi ||= self.class.failsafe_response(@output, '400 Bad Request') { CGI.new }
- @request = CgiRequest.new(cgi, session_options)
- @response = CgiResponse.new(cgi)
- dispatch
- end
- rescue Exception => exception
- failsafe_rescue exception
+ CGIHandler.dispatch_cgi(self, cgi, @output)
end
def call(env)
@@ -160,40 +113,22 @@ def flush_logger
Base.logger.flush
end
- def mark_as_test_request!
- @test_request = true
- self
- end
-
- def test_request?
- @test_request
- end
-
def checkin_connections
# Don't return connection (and peform implicit rollback) if this request is a part of integration test
- return if test_request?
+ # TODO: This callback should have direct access to env
+ return if @request.key?("action_controller.test")
ActiveRecord::Base.clear_active_connections!
end
protected
def handle_request
@controller = Routing::Routes.recognize(@request)
- @controller.process(@request, @response).out(@output)
+ @controller.process(@request, @response).out
end
def failsafe_rescue(exception)
- if @test_request
- process_with_exception(exception)
- else
- self.class.failsafe_response(@output, '500 Internal Server Error', exception) do
- process_with_exception(exception)
- end
- end
- end
-
- def process_with_exception(exception)
if @controller ||= (::ApplicationController rescue Base)
- @controller.process_with_exception(@request, @response, exception).out(@output)
+ @controller.process_with_exception(@request, @response, exception).out
else
raise exception
end
View
52 actionpack/lib/action_controller/failsafe.rb
@@ -0,0 +1,52 @@
+module ActionController
+ class Failsafe
+ cattr_accessor :error_file_path
+ self.error_file_path = Rails.public_path if defined?(Rails.public_path)
+
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ @app.call(env)
+ rescue Exception => exception
+ # Reraise exception in test environment
+ if env["action_controller.test"]
+ raise exception
+ else
+ failsafe_response(exception)
+ end
+ end
+
+ private
+ def failsafe_response(exception)
+ log_failsafe_exception(exception)
+ [500, {'Content-Type' => 'text/html'}, failsafe_response_body]
+ rescue Exception => failsafe_error # Logger or IO errors
+ $stderr.puts "Error during failsafe response: #{failsafe_error}"
+ end
+
+ def failsafe_response_body
+ error_path = "#{self.class.error_file_path}/500.html"
+ if File.exist?(error_path)
+ File.read(error_path)
+ else
+ "<html><body><h1>500 Internal Server Error</h1></body></html>"
+ end
+ end
+
+ def log_failsafe_exception(exception)
+ message = "/!\\ FAILSAFE /!\\ #{Time.now}\n Status: 500 Internal Server Error\n"
+ message << " #{exception}\n #{exception.backtrace.join("\n ")}" if exception
+ failsafe_logger.fatal(message)
+ end
+
+ def failsafe_logger
+ if defined?(::RAILS_DEFAULT_LOGGER) && !::RAILS_DEFAULT_LOGGER.nil?
+ ::RAILS_DEFAULT_LOGGER
+ else
+ Logger.new($stderr)
+ end
+ end
+ end
+end
View
5 actionpack/lib/action_controller/integration.rb
@@ -257,7 +257,8 @@ def process(method, path, parameters = nil, headers = nil)
"CONTENT_LENGTH" => data ? data.length.to_s : nil,
"HTTP_COOKIE" => encode_cookies,
"HTTPS" => https? ? "on" : "off",
- "HTTP_ACCEPT" => accept
+ "HTTP_ACCEPT" => accept,
+ "action_controller.test" => true
)
(headers || {}).each do |key, value|
@@ -273,7 +274,7 @@ def process(method, path, parameters = nil, headers = nil)
ActionController::Base.clear_last_instantiation!
env['rack.input'] = data.is_a?(IO) ? data : StringIO.new(data || '')
- @status, @headers, result_body = ActionController::Dispatcher.new.mark_as_test_request!.call(env)
+ @status, @headers, result_body = ActionController::Dispatcher.new.call(env)
@request_count += 1
@controller = ActionController::Base.last_instantiation
View
2  actionpack/lib/action_controller/rack_process.rb
@@ -164,7 +164,7 @@ def status
@status || super
end
- def out(output = $stdout, &block)
+ def out(&block)
# Nasty hack because CGI sessions are closed after the normal
# prepare! statement
set_cookies!
View
262 actionpack/test/controller/cgi_test.rb
@@ -1,262 +0,0 @@
-require 'abstract_unit'
-
-class BaseCgiTest < Test::Unit::TestCase
- def setup
- @request_hash = {
- "HTTP_MAX_FORWARDS" => "10",
- "SERVER_NAME" => "glu.ttono.us:8007",
- "FCGI_ROLE" => "RESPONDER",
- "AUTH_TYPE" => "Basic",
- "HTTP_X_FORWARDED_HOST" => "glu.ttono.us",
- "HTTP_ACCEPT_CHARSET" => "UTF-8",
- "HTTP_ACCEPT_ENCODING" => "gzip, deflate",
- "HTTP_CACHE_CONTROL" => "no-cache, max-age=0",
- "HTTP_PRAGMA" => "no-cache",
- "HTTP_USER_AGENT" => "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en)",
- "PATH_INFO" => "/homepage/",
- "HTTP_ACCEPT_LANGUAGE" => "en",
- "HTTP_NEGOTIATE" => "trans",
- "HTTP_HOST" => "glu.ttono.us:8007",
- "HTTP_REFERER" => "http://www.google.com/search?q=glu.ttono.us",
- "HTTP_FROM" => "googlebot",
- "SERVER_PROTOCOL" => "HTTP/1.1",
- "REDIRECT_URI" => "/dispatch.fcgi",
- "SCRIPT_NAME" => "/dispatch.fcgi",
- "SERVER_ADDR" => "207.7.108.53",
- "REMOTE_ADDR" => "207.7.108.53",
- "REMOTE_HOST" => "google.com",
- "REMOTE_IDENT" => "kevin",
- "REMOTE_USER" => "kevin",
- "SERVER_SOFTWARE" => "lighttpd/1.4.5",
- "HTTP_COOKIE" => "_session_id=c84ace84796670c052c6ceb2451fb0f2; is_admin=yes",
- "HTTP_X_FORWARDED_SERVER" => "glu.ttono.us",
- "REQUEST_URI" => "/admin",
- "DOCUMENT_ROOT" => "/home/kevinc/sites/typo/public",
- "PATH_TRANSLATED" => "/home/kevinc/sites/typo/public/homepage/",
- "SERVER_PORT" => "8007",
- "QUERY_STRING" => "",
- "REMOTE_PORT" => "63137",
- "GATEWAY_INTERFACE" => "CGI/1.1",
- "HTTP_X_FORWARDED_FOR" => "65.88.180.234",
- "HTTP_ACCEPT" => "*/*",
- "SCRIPT_FILENAME" => "/home/kevinc/sites/typo/public/dispatch.fcgi",
- "REDIRECT_STATUS" => "200",
- "REQUEST_METHOD" => "GET"
- }
- # some Nokia phone browsers omit the space after the semicolon separator.
- # some developers have grown accustomed to using comma in cookie values.
- @alt_cookie_fmt_request_hash = {"HTTP_COOKIE"=>"_session_id=c84ace847,96670c052c6ceb2451fb0f2;is_admin=yes"}
- @cgi = CGI.new
- class << @cgi; attr_accessor :env_table end
- @cgi.env_table = @request_hash
- @request = ActionController::CgiRequest.new(@cgi)
- end
-
- def default_test; end
-
- private
-
- def set_content_data(data)
- @request.env['REQUEST_METHOD'] = 'POST'
- @request.env['CONTENT_LENGTH'] = data.length
- @request.env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded; charset=utf-8'
- @request.env['RAW_POST_DATA'] = data
- end
-end
-
-class CgiRequestTest < BaseCgiTest
- def test_proxy_request
- assert_equal 'glu.ttono.us', @request.host_with_port
- end
-
- def test_http_host
- @request_hash.delete "HTTP_X_FORWARDED_HOST"
- @request_hash['HTTP_HOST'] = "rubyonrails.org:8080"
- assert_equal "rubyonrails.org:8080", @request.host_with_port
-
- @request_hash['HTTP_X_FORWARDED_HOST'] = "www.firsthost.org, www.secondhost.org"
- assert_equal "www.secondhost.org", @request.host(true)
- end
-
- def test_http_host_with_default_port_overrides_server_port
- @request_hash.delete "HTTP_X_FORWARDED_HOST"
- @request_hash['HTTP_HOST'] = "rubyonrails.org"
- assert_equal "rubyonrails.org", @request.host_with_port
- end
-
- def test_host_with_port_defaults_to_server_name_if_no_host_headers
- @request_hash.delete "HTTP_X_FORWARDED_HOST"
- @request_hash.delete "HTTP_HOST"
- assert_equal "glu.ttono.us:8007", @request.host_with_port
- end
-
- def test_host_with_port_falls_back_to_server_addr_if_necessary
- @request_hash.delete "HTTP_X_FORWARDED_HOST"
- @request_hash.delete "HTTP_HOST"
- @request_hash.delete "SERVER_NAME"
- assert_equal "207.7.108.53:8007", @request.host_with_port
- end
-
- def test_host_with_port_if_http_standard_port_is_specified
- @request_hash['HTTP_X_FORWARDED_HOST'] = "glu.ttono.us:80"
- assert_equal "glu.ttono.us", @request.host_with_port
- end
-
- def test_host_with_port_if_https_standard_port_is_specified
- @request_hash['HTTP_X_FORWARDED_PROTO'] = "https"
- @request_hash['HTTP_X_FORWARDED_HOST'] = "glu.ttono.us:443"
- assert_equal "glu.ttono.us", @request.host_with_port
- end
-
- def test_host_if_ipv6_reference
- @request_hash.delete "HTTP_X_FORWARDED_HOST"
- @request_hash['HTTP_HOST'] = "[2001:1234:5678:9abc:def0::dead:beef]"
- assert_equal "[2001:1234:5678:9abc:def0::dead:beef]", @request.host
- end
-
- def test_host_if_ipv6_reference_with_port
- @request_hash.delete "HTTP_X_FORWARDED_HOST"
- @request_hash['HTTP_HOST'] = "[2001:1234:5678:9abc:def0::dead:beef]:8008"
- assert_equal "[2001:1234:5678:9abc:def0::dead:beef]", @request.host
- end
-
- def test_cgi_environment_variables
- assert_equal "Basic", @request.auth_type
- assert_equal 0, @request.content_length
- assert_equal nil, @request.content_type
- assert_equal "CGI/1.1", @request.gateway_interface
- assert_equal "*/*", @request.accept
- assert_equal "UTF-8", @request.accept_charset
- assert_equal "gzip, deflate", @request.accept_encoding
- assert_equal "en", @request.accept_language
- assert_equal "no-cache, max-age=0", @request.cache_control
- assert_equal "googlebot", @request.from
- assert_equal "glu.ttono.us", @request.host
- assert_equal "trans", @request.negotiate
- assert_equal "no-cache", @request.pragma
- assert_equal "http://www.google.com/search?q=glu.ttono.us", @request.referer
- assert_equal "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en)", @request.user_agent
- assert_equal "/homepage/", @request.path_info
- assert_equal "/home/kevinc/sites/typo/public/homepage/", @request.path_translated
- assert_equal "", @request.query_string
- assert_equal "207.7.108.53", @request.remote_addr
- assert_equal "google.com", @request.remote_host
- assert_equal "kevin", @request.remote_ident
- assert_equal "kevin", @request.remote_user
- assert_equal :get, @request.request_method
- assert_equal "/dispatch.fcgi", @request.script_name
- assert_equal "glu.ttono.us:8007", @request.server_name
- assert_equal 8007, @request.server_port
- assert_equal "HTTP/1.1", @request.server_protocol
- assert_equal "lighttpd", @request.server_software
- end
-
- def test_cookie_syntax_resilience
- cookies = CGI::Cookie::parse(@request_hash["HTTP_COOKIE"]);
- assert_equal ["c84ace84796670c052c6ceb2451fb0f2"], cookies["_session_id"], cookies.inspect
- assert_equal ["yes"], cookies["is_admin"], cookies.inspect
-
- alt_cookies = CGI::Cookie::parse(@alt_cookie_fmt_request_hash["HTTP_COOKIE"]);
- assert_equal ["c84ace847,96670c052c6ceb2451fb0f2"], alt_cookies["_session_id"], alt_cookies.inspect
- assert_equal ["yes"], alt_cookies["is_admin"], alt_cookies.inspect
- end
-end
-
-class CgiRequestParamsParsingTest < BaseCgiTest
- def test_doesnt_break_when_content_type_has_charset
- set_content_data 'flamenco=love'
-
- assert_equal({"flamenco"=> "love"}, @request.request_parameters)
- end
-
- def test_doesnt_interpret_request_uri_as_query_string_when_missing
- @request.env['REQUEST_URI'] = 'foo'
- assert_equal({}, @request.query_parameters)
- end
-end
-
-class CgiRequestContentTypeTest < BaseCgiTest
- def test_html_content_type_verification
- @request.env['CONTENT_TYPE'] = Mime::HTML.to_s
- assert @request.content_type.verify_request?
- end
-
- def test_xml_content_type_verification
- @request.env['CONTENT_TYPE'] = Mime::XML.to_s
- assert !@request.content_type.verify_request?
- end
-end
-
-class CgiRequestMethodTest < BaseCgiTest
- def test_get
- assert_equal :get, @request.request_method
- end
-
- def test_post
- @request.env['REQUEST_METHOD'] = 'POST'
- assert_equal :post, @request.request_method
- end
-
- def test_put
- set_content_data '_method=put'
-
- assert_equal :put, @request.request_method
- end
-
- def test_delete
- set_content_data '_method=delete'
-
- assert_equal :delete, @request.request_method
- end
-end
-
-class CgiRequestNeedsRewoundTest < BaseCgiTest
- def test_body_should_be_rewound
- data = 'foo'
- fake_cgi = Struct.new(:env_table, :query_string, :stdinput).new(@request_hash, '', StringIO.new(data))
- fake_cgi.env_table['CONTENT_LENGTH'] = data.length
- fake_cgi.env_table['CONTENT_TYPE'] = 'application/x-www-form-urlencoded; charset=utf-8'
-
- # Read the request body by parsing params.
- request = ActionController::CgiRequest.new(fake_cgi)
- request.request_parameters
-
- # Should have rewound the body.
- assert_equal 0, request.body.pos
- end
-end
-
-uses_mocha 'CGI Response' do
- class CgiResponseTest < BaseCgiTest
- def setup
- super
- @cgi.expects(:header).returns("HTTP/1.0 200 OK\nContent-Type: text/html\n")
- @response = ActionController::CgiResponse.new(@cgi)
- @output = StringIO.new('')
- end
-
- def test_simple_output
- @response.body = "Hello, World!"
-
- @response.out(@output)
- assert_equal "HTTP/1.0 200 OK\nContent-Type: text/html\nHello, World!", @output.string
- end
-
- def test_head_request
- @cgi.env_table['REQUEST_METHOD'] = 'HEAD'
- @response.body = "Hello, World!"
-
- @response.out(@output)
- assert_equal "HTTP/1.0 200 OK\nContent-Type: text/html\n", @output.string
- end
-
- def test_streaming_block
- @response.body = Proc.new do |response, output|
- 5.times { |n| output.write(n) }
- end
-
- @response.out(@output)
- assert_equal "HTTP/1.0 200 OK\nContent-Type: text/html\n01234", @output.string
- end
- end
-end
View
38 actionpack/test/controller/dispatcher_test.rb
@@ -6,7 +6,6 @@ class DispatcherTest < Test::Unit::TestCase
Dispatcher = ActionController::Dispatcher
def setup
- @output = StringIO.new
ENV['REQUEST_METHOD'] = 'GET'
# Clear callbacks as they are redefined by Dispatcher#define_dispatcher_callbacks
@@ -16,7 +15,7 @@ def setup
Dispatcher.stubs(:require_dependency)
- @dispatcher = Dispatcher.new(@output)
+ @dispatcher = Dispatcher.new
end
def teardown
@@ -25,17 +24,17 @@ def teardown
def test_clears_dependencies_after_dispatch_if_in_loading_mode
ActiveSupport::Dependencies.expects(:clear).once
- dispatch(@output, false)
+ dispatch(false)
end
def test_reloads_routes_before_dispatch_if_in_loading_mode
ActionController::Routing::Routes.expects(:reload).once
- dispatch(@output, false)
+ dispatch(false)
end
def test_clears_asset_tag_cache_before_dispatch_if_in_loading_mode
ActionView::Helpers::AssetTagHelper::AssetTag::Cache.expects(:clear).once
- dispatch(@output, false)
+ dispatch(false)
end
def test_leaves_dependencies_after_dispatch_if_not_in_loading_mode
@@ -51,12 +50,16 @@ def log_failsafe_exception(status, exception); end
end
def test_failsafe_response
- CGI.expects(:new).raises('some multipart parsing failure')
- Dispatcher.expects(:log_failsafe_exception)
-
- assert_nothing_raised { dispatch }
-
- assert_equal "Status: 400 Bad Request\r\nContent-Type: text/html\r\n\r\n<html><body><h1>400 Bad Request</h1></body></html>", @output.string
+ Dispatcher.any_instance.expects(:dispatch_unlocked).raises('b00m')
+ ActionController::Failsafe.any_instance.expects(:log_failsafe_exception)
+
+ assert_nothing_raised do
+ assert_equal [
+ 500,
+ {"Content-Type" => "text/html"},
+ "<html><body><h1>500 Internal Server Error</h1></body></html>"
+ ], dispatch
+ end
end
def test_prepare_callbacks
@@ -77,7 +80,7 @@ def test_prepare_callbacks
# Make sure they are only run once
a = b = c = nil
- @dispatcher.send :dispatch
+ dispatch
assert_nil a || b || c
end
@@ -92,15 +95,10 @@ def test_to_prepare_with_identifier_replaces
end
private
- def dispatch(output = @output, cache_classes = true)
- controller = mock
- controller.stubs(:process).returns(controller)
- controller.stubs(:out).with(output).returns('response')
-
- ActionController::Routing::Routes.stubs(:recognize).returns(controller)
-
+ def dispatch(cache_classes = true)
+ Dispatcher.any_instance.stubs(:handle_request).returns([200, {}, 'response'])
Dispatcher.define_dispatcher_callbacks(cache_classes)
- Dispatcher.dispatch(nil, {}, output)
+ @dispatcher.call({})
end
def assert_subclasses(howmany, klass, message = klass.subclasses.inspect)
View
10 actionpack/test/controller/rack_test.rb
@@ -230,14 +230,13 @@ class RackResponseTest < BaseRackTest
def setup
super
@response = ActionController::RackResponse.new(@request)
- @output = StringIO.new('')
end
def test_simple_output
@response.body = "Hello, World!"
@response.prepare!
- status, headers, body = @response.out(@output)
+ status, headers, body = @response.out
assert_equal "200 OK", status
assert_equal({
"Content-Type" => "text/html; charset=utf-8",
@@ -258,7 +257,7 @@ def test_streaming_block
end
@response.prepare!
- status, headers, body = @response.out(@output)
+ status, headers, body = @response.out
assert_equal "200 OK", status
assert_equal({"Content-Type" => "text/html; charset=utf-8", "Cache-Control" => "no-cache", "Set-Cookie" => []}, headers)
@@ -274,7 +273,7 @@ def test_set_session_cookie
@response.body = "Hello, World!"
@response.prepare!
- status, headers, body = @response.out(@output)
+ status, headers, body = @response.out
assert_equal "200 OK", status
assert_equal({
"Content-Type" => "text/html; charset=utf-8",
@@ -294,7 +293,6 @@ class RackResponseHeadersTest < BaseRackTest
def setup
super
@response = ActionController::RackResponse.new(@request)
- @output = StringIO.new('')
@response.headers['Status'] = "200 OK"
end
@@ -317,6 +315,6 @@ def test_status
private
def response_headers
@response.prepare!
- @response.out(@output)[1]
+ @response.out[1]
end
end
View
4 actionpack/test/controller/request_test.rb
@@ -637,7 +637,7 @@ def test_parse_params_with_file
input = {
"customers[boston][first][name]" => [ "David" ],
"something_else" => [ "blah" ],
- "logo" => [ File.new(File.dirname(__FILE__) + "/cgi_test.rb").path ]
+ "logo" => [ File.new(File.dirname(__FILE__) + "/rack_test.rb").path ]
}
expected_output = {
@@ -649,7 +649,7 @@ def test_parse_params_with_file
}
},
"something_else" => "blah",
- "logo" => File.new(File.dirname(__FILE__) + "/cgi_test.rb").path,
+ "logo" => File.new(File.dirname(__FILE__) + "/rack_test.rb").path,
}
assert_equal expected_output, ActionController::AbstractRequest.parse_request_parameters(input)

4 comments on commit 9c9da6c

@clemens

Finally …

@boblmartens

Excellent. Leaner and meaner!

@raggi

it’s not finished yet, but:

http://github.com/raggi/rails/commit/935d49846de3b194e61b049cd220e54fd11002cf

I’ll have to merge this one :-/

@josh
Collaborator

@raggi we need to get in touch. I don’t want to start hacking on some of that stuff if you’ve already done the hard work.

Please email me, josh at joshpeek dot com

Please sign in to comment.
Something went wrong with that request. Please try again.