Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Removed the mutex from the WEBrick adapter under the production envir…
…onment so concurrent requests can be served

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@1482 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information
dhh committed Jun 22, 2005
1 parent ce44079 commit 4e0ffab
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 39 deletions.
10 changes: 5 additions & 5 deletions actionpack/lib/action_controller/cgi_process.rb
Expand Up @@ -124,20 +124,20 @@ def initialize(cgi)
super()
end

def out
def out(output = $stdout)
convert_content_type!(@headers)
$stdout.binmode if $stdout.respond_to?(:binmode)
$stdout.sync = false
output.binmode if output.respond_to?(:binmode)
output.sync = false if output.respond_to?(:sync=)

begin
print @cgi.header(@headers)
output.write(@cgi.header(@headers))

if @cgi.send(:env_table)['REQUEST_METHOD'] == 'HEAD'
return
elsif @body.respond_to?(:call)
@body.call(self)
else
print @body
output.write(@body)
end
rescue Errno::EPIPE => e
# lost connection to the FCGI process -- ignore the output, then
Expand Down
2 changes: 2 additions & 0 deletions railties/CHANGELOG
@@ -1,5 +1,7 @@
*SVN*

* Removed the mutex from the WEBrick adapter under the production environment so concurrent requests can be served

* Fixed that mailer generator generated fixtures/plural while units expected fixtures/singular #1457 [Scott Barron]

* Added a 'whiny nil' that's aim to ensure that when users pass nil to methods where that isn't appropriate, instead of NoMethodError? and the name of some method used by the framework users will see a message explaining what type of object was expected. Only active in test and development environments by default #1209 [Michael Koziarski]
Expand Down
6 changes: 3 additions & 3 deletions railties/lib/dispatcher.rb
Expand Up @@ -25,13 +25,13 @@

class Dispatcher
class << self
def dispatch(cgi = CGI.new, session_options = ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS)
def dispatch(cgi = CGI.new, session_options = ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, output = $stdout)
begin
request, response = ActionController::CgiRequest.new(cgi, session_options), ActionController::CgiResponse.new(cgi)
prepare_application
ActionController::Routing::Routes.recognize!(request).process(request, response).out
ActionController::Routing::Routes.recognize!(request).process(request, response).out(output)
rescue Object => exception
ActionController::Base.process_with_exception(request, response, exception).out
ActionController::Base.process_with_exception(request, response, exception).out(output)
ensure
reset_application
end
Expand Down
96 changes: 65 additions & 31 deletions railties/lib/webrick_server.rb
Expand Up @@ -8,6 +8,24 @@

ABSOLUTE_RAILS_ROOT = File.expand_path(RAILS_ROOT)

class CGI
def stdinput
@stdin || $stdin
end

def stdinput=(input)
@stdin = input
end

def env_table
@env_table || ENV
end

def env_table=(table)
@env_table = table
end
end

class DispatchServlet < WEBrick::HTTPServlet::AbstractServlet
REQUEST_MUTEX = Mutex.new

Expand All @@ -18,6 +36,10 @@ def self.dispatch(options = {})
server.mount('/', DispatchServlet, options)

trap("INT") { server.shutdown }

require File.join(@server_options[:server_root], "..", "config", "environment") unless defined?(RAILS_ROOT)
require "dispatcher"

server.start
end

Expand All @@ -31,20 +53,21 @@ def initialize(server, options)
def service(req, res)
begin
unless handle_file(req, res)
REQUEST_MUTEX.lock
REQUEST_MUTEX.lock unless RAILS_ENV == 'production'
unless handle_dispatch(req, res)
raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found."
end
end
ensure
REQUEST_MUTEX.unlock if REQUEST_MUTEX.locked?
unless RAILS_ENV == 'production'
REQUEST_MUTEX.unlock if REQUEST_MUTEX.locked?
end
end
end

def handle_file(req, res)
begin
req = req.dup

path = req.path.dup

# Add .html if the last path piece has no . in it
Expand All @@ -63,35 +86,13 @@ def handle_file(req, res)
end
end

def handle_dispatch(req, res, origin = nil)
env = req.meta_vars.clone
env.delete "SCRIPT_NAME"
env["QUERY_STRING"] = req.request_uri.query
env["REQUEST_URI"] = origin if origin

data = nil
$old_stdin, $old_stdout = $stdin, $stdout
$stdin, $stdout = StringIO.new(req.body || ""), StringIO.new

begin
require 'cgi'
CGI.send(:define_method, :env_table) { env }

load File.join(@server_options[:server_root], "dispatch.rb")

$stdout.rewind
data = $stdout.read
ensure
$stdin, $stdout = $old_stdin, $old_stdout
end
def handle_dispatch(req, res, origin = nil)
data = StringIO.new
Dispatcher.dispatch(create_cgi(req, origin), ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, data)

raw_header, body = *data.split(/^[\xd\xa]+/on, 2)
header = WEBrick::HTTPUtils::parse_header(raw_header)
if /^(\d+)/ =~ header['status'][0]
res.status = $1.to_i
header.delete('status')
end
res.cookies.concat header.delete('set-cookie')
header, body = extract_header_and_body(data)
assign_status(res, header)
res.cookies.concat(header.delete('set-cookie'))
header.each { |key, val| res[key] = val.join(", ") }

res.body = body
Expand All @@ -100,4 +101,37 @@ def handle_dispatch(req, res, origin = nil)
p err, err.backtrace
return false
end

private
def create_cgi(req, origin)
cgi = CGI.new
cgi.env_table = create_env_table(req, origin)
cgi.stdinput = req.body || ""
return cgi
end

def create_env_table(req, origin)
env = req.meta_vars.clone
env.delete "SCRIPT_NAME"
env["QUERY_STRING"] = req.request_uri.query
env["REQUEST_URI"] = origin if origin
return env
end

def extract_header_and_body(data)
data.rewind
data = data.read

raw_header, body = *data.split(/^[\xd\xa]+/on, 2)
header = WEBrick::HTTPUtils::parse_header(raw_header)

return header, body
end

def assign_status(res, header)
if /^(\d+)/ =~ header['status'][0]
res.status = $1.to_i
header.delete('status')
end
end
end

0 comments on commit 4e0ffab

Please sign in to comment.