2006-06-30 20:42:12 +00:00
# Copyright (c) 2005 Zed A. Shaw
# You can redistribute it and/or modify it under the same terms as Ruby.
#
# Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html
# for more information.
2006-05-21 14:46:42 +00:00
2006-02-28 07:04:41 +00:00
require 'mongrel'
2006-03-08 00:52:20 +00:00
require 'cgi'
2006-08-26 03:05:05 +00:00
require 'sync'
2006-03-08 00:52:20 +00:00
2006-08-26 03:05:05 +00:00
class Sync
2006-08-22 06:01:35 +00:00
# modified to open the waiting list for reporting purposes
2006-08-26 03:05:05 +00:00
attr_accessor :sync_waiting
2006-08-22 06:01:35 +00:00
end
2006-03-26 20:01:50 +00:00
module Mongrel
module Rails
2006-03-27 06:10:07 +00:00
# Implements a handler that can run Rails and serve files out of the
# Rails application's public directory. This lets you run your Rails
# application with Mongrel during development and testing, then use it
# also in production behind a server that's better at serving the
# static files.
#
# The RailsHandler takes a mime_map parameter which is a simple suffix=mimetype
# mapping that it should add to the list of valid mime types.
#
# It also supports page caching directly and will try to resolve a request
# in the following order:
#
# * If the requested exact PATH_INFO exists as a file then serve it.
# * If it exists at PATH_INFO+".html" exists then serve that.
2006-05-20 21:21:29 +00:00
# * Finally, construct a Mongrel::CGIWrapper and run Dispatcher.dispatch to have Rails go.
2006-03-27 06:10:07 +00:00
#
# This means that if you are using page caching it will actually work with Mongrel
# and you should see a decent speed boost (but not as fast as if you use lighttpd).
#
# An additional feature you can use is
class RailsHandler < Mongrel :: HttpHandler
attr_reader :files
attr_reader :guard
2006-06-05 09:35:34 +00:00
@@file_only_methods = [ " GET " , " HEAD " ]
2006-05-20 02:56:30 +00:00
2006-03-27 06:10:07 +00:00
def initialize ( dir , mime_map = { } )
@files = Mongrel :: DirHandler . new ( dir , false )
2006-08-26 03:05:05 +00:00
@guard = Sync . new
2006-08-22 06:01:35 +00:00
@tick = Time . now
2006-05-20 02:56:30 +00:00
2006-03-27 06:10:07 +00:00
# register the requested mime types
mime_map . each { | k , v | Mongrel :: DirHandler :: add_mime_type ( k , v ) }
end
2006-05-20 02:56:30 +00:00
2006-03-27 06:10:07 +00:00
# Attempts to resolve the request as follows:
#
#
# * If the requested exact PATH_INFO exists as a file then serve it.
# * If it exists at PATH_INFO+".html" exists then serve that.
2006-05-20 21:21:29 +00:00
# * Finally, construct a Mongrel::CGIWrapper and run Dispatcher.dispatch to have Rails go.
2006-03-27 06:10:07 +00:00
def process ( request , response )
return if response . socket . closed?
2006-05-20 02:56:30 +00:00
2006-03-27 06:10:07 +00:00
path_info = request . params [ Mongrel :: Const :: PATH_INFO ]
2006-05-23 03:42:37 +00:00
page_cached = path_info + " .html "
2006-06-05 09:35:34 +00:00
get_or_head = @@file_only_methods . include? request . params [ Mongrel :: Const :: REQUEST_METHOD ]
2006-05-20 02:56:30 +00:00
2006-06-05 09:35:34 +00:00
if get_or_head and @files . can_serve ( path_info )
2006-05-20 02:56:30 +00:00
# File exists as-is so serve it up
2006-03-27 06:10:07 +00:00
@files . process ( request , response )
2006-06-05 09:35:34 +00:00
elsif get_or_head and @files . can_serve ( page_cached )
2006-03-27 06:10:07 +00:00
# possible cached page, serve it up
request . params [ Mongrel :: Const :: PATH_INFO ] = page_cached
@files . process ( request , response )
else
begin
cgi = Mongrel :: CGIWrapper . new ( request , response )
cgi . handler = self
2006-08-12 19:47:22 +00:00
# we don't want the output to be really final until we're out of the lock
cgi . default_really_final = false
2006-08-12 19:01:06 +00:00
2006-08-26 03:05:05 +00:00
log_threads_waiting_for ( request . params [ " PATH_INFO " ] )
2006-06-05 09:35:34 +00:00
2006-08-26 03:05:05 +00:00
@guard . synchronize ( :EX ) {
Dispatcher . dispatch ( cgi , ActionController :: CgiRequest :: DEFAULT_SESSION_OPTIONS , response . body )
}
2006-08-12 19:01:06 +00:00
2006-03-27 06:10:07 +00:00
# This finalizes the output using the proper HttpResponse way
2006-08-12 23:12:46 +00:00
cgi . out ( " text/html " , true ) { " " }
2006-03-27 06:10:07 +00:00
rescue Errno :: EPIPE
# ignored
rescue Object = > rails_error
2006-08-03 20:49:11 +00:00
STDERR . puts " #{ Time . now } : Error calling Dispatcher.dispatch #{ rails_error . inspect } "
2006-03-27 06:10:07 +00:00
STDERR . puts rails_error . backtrace . join ( " \n " )
end
end
end
2006-05-20 02:56:30 +00:00
2006-08-22 06:01:35 +00:00
def log_threads_waiting_for ( event )
if $mongrel_debug_client and ( Time . now - @tick > 10 )
2006-08-26 03:05:05 +00:00
STDERR . puts " #{ Time . now } : #{ @guard . sync_waiting . length } threads sync_waiting for #{ event } , #{ self . listener . workers . list . length } still active in mongrel. "
2006-08-22 06:01:35 +00:00
@tick = Time . now
end
end
2006-03-27 06:10:07 +00:00
# Does the internal reload for Rails. It might work for most cases, but
# sometimes you get exceptions. In that case just do a real restart.
def reload!
2006-08-12 19:10:57 +00:00
begin
2006-08-26 03:05:05 +00:00
@guard . synchronize ( :EX ) {
$" . replace $orig_dollar_quote
GC . start
Dispatcher . reset_application!
ActionController :: Routing :: Routes . reload
}
2006-03-27 06:10:07 +00:00
end
end
end
2006-03-26 20:01:50 +00:00
# Creates Rails specific configuration options for people to use
# instead of the base Configurator.
class RailsConfigurator < Mongrel :: Configurator
2006-05-20 02:56:30 +00:00
2006-03-26 20:01:50 +00:00
# Creates a single rails handler and returns it so you
# can add it to a uri. You can actually attach it to
# as many URIs as you want, but this returns the
# same RailsHandler for each call.
#
# Requires the following options:
#
# * :docroot => The public dir to serve from.
# * :environment => Rails environment to use.
2006-03-28 02:56:33 +00:00
# * :cwd => The change to working directory
2006-03-26 20:01:50 +00:00
#
# And understands the following optional settings:
#
# * :mime => A map of mime types.
#
# Because of how Rails is designed you can only have
# one installed per Ruby interpreter (talk to them
# about thread safety). Because of this the first
# time you call this function it does all the config
# needed to get your rails working. After that
# it returns the one handler you've configured.
2006-05-20 21:21:29 +00:00
# This lets you attach Rails to any URI (and multiple)
2006-03-26 20:01:50 +00:00
# you want, but still protects you from threads destroying
# your handler.
def rails ( options = { } )
2006-05-20 02:56:30 +00:00
2006-03-26 20:01:50 +00:00
return @rails_handler if @rails_handler
2006-05-20 02:56:30 +00:00
2006-03-26 20:01:50 +00:00
ops = resolve_defaults ( options )
2006-05-20 02:56:30 +00:00
2006-03-26 20:01:50 +00:00
# fix up some defaults
ops [ :environment ] || = " development "
ops [ :docroot ] || = " public "
ops [ :mime ] || = { }
2006-05-20 02:56:30 +00:00
2006-03-26 20:01:50 +00:00
$orig_dollar_quote = $" . clone
ENV [ 'RAILS_ENV' ] = ops [ :environment ]
2006-04-03 15:22:17 +00:00
env_location = " #{ ops [ :cwd ] } /config/environment "
require env_location
2006-03-26 20:01:50 +00:00
require 'dispatcher'
require 'mongrel/rails'
2006-05-20 02:56:30 +00:00
2006-07-14 16:13:51 +00:00
ActionController :: AbstractRequest . relative_url_root = ops [ :prefix ] if ops [ :prefix ]
2006-07-13 22:34:59 +00:00
2006-03-26 20:01:50 +00:00
@rails_handler = RailsHandler . new ( ops [ :docroot ] , ops [ :mime ] )
end
2006-05-20 02:56:30 +00:00
2006-03-26 20:01:50 +00:00
# Reloads rails. This isn't too reliable really, but
# should work for most minimal reload purposes. Only reliable
# way it so stop then start the process.
def reload!
if not @rails_handler
raise " Rails was not configured. Read the docs for RailsConfigurator. "
2006-02-28 07:04:41 +00:00
end
2006-05-20 02:56:30 +00:00
2006-03-26 20:01:50 +00:00
log " Reloading rails... "
@rails_handler . reload!
log " Done reloading rails. "
2006-05-20 02:56:30 +00:00
2006-02-28 07:04:41 +00:00
end
2006-05-20 02:56:30 +00:00
2006-03-26 20:01:50 +00:00
# Takes the exact same configuration as Mongrel::Configurator (and actually calls that)
# but sets up the additional HUP handler to call reload!.
def setup_rails_signals ( options = { } )
ops = resolve_defaults ( options )
2006-05-20 07:56:38 +00:00
setup_signals ( options )
2006-06-05 09:35:34 +00:00
2006-03-26 20:01:50 +00:00
if RUBY_PLATFORM !~ / mswin /
# rails reload
2006-05-20 02:56:30 +00:00
trap ( " HUP " ) { log " HUP signal received. " ; reload! }
2006-03-26 20:01:50 +00:00
log " Rails signals registered. HUP => reload (without restart). It might not work well. "
end
end
2006-03-25 21:15:30 +00:00
end
end
end