forked from mongrel/mongrel
-
Notifications
You must be signed in to change notification settings - Fork 1
/
rails.rb
185 lines (159 loc) · 6.99 KB
/
rails.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# 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.
require 'mongrel'
require 'cgi'
module Mongrel
module Rails
# 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.
# * Finally, construct a Mongrel::CGIWrapper and run Dispatcher.dispatch to have Rails go.
#
# 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 a static
# server like Apache or Litespeed).
class RailsHandler < Mongrel::HttpHandler
attr_reader :files
attr_reader :guard
@@file_only_methods = ["GET","HEAD"]
def initialize(dir, mime_map = {})
@files = Mongrel::DirHandler.new(dir,false)
@guard = Mutex.new
# Register the requested MIME types
mime_map.each {|k,v| Mongrel::DirHandler::add_mime_type(k,v) }
end
# 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.
# * Finally, construct a Mongrel::CGIWrapper and run Dispatcher.dispatch to have Rails go.
def process(request, response)
return if response.socket.closed?
path_info = request.params[Mongrel::Const::PATH_INFO]
rest_operator = request.params[Mongrel::Const::REQUEST_URI][/^#{Regexp.escape path_info}(;[^\?]+)/, 1].to_s
path_info.chomp!("/")
page_cached = path_info + rest_operator + ActionController::Base.page_cache_extension
get_or_head = @@file_only_methods.include? request.params[Mongrel::Const::REQUEST_METHOD]
if get_or_head and @files.can_serve(path_info)
# File exists as-is so serve it up
@files.process(request,response)
elsif get_or_head and @files.can_serve(page_cached)
# 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
# We don't want the output to be really final until we're out of the lock
cgi.default_really_final = false
@guard.synchronize {
@active_request_path = request.params[Mongrel::Const::PATH_INFO]
Dispatcher.dispatch(cgi, ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, response.body)
@active_request_path = nil
}
# This finalizes the output using the proper HttpResponse way
cgi.out("text/html",true) {""}
rescue Errno::EPIPE
response.socket.close
rescue Object => rails_error
STDERR.puts "#{Time.now.httpdate}: Error calling Dispatcher.dispatch #{rails_error.inspect}"
STDERR.puts rails_error.backtrace.join("\n")
end
end
end
# 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!
begin
@guard.synchronize {
$".replace $orig_dollar_quote
GC.start
Dispatcher.reset_application!
ActionController::Routing::Routes.reload
}
end
end
end
# Creates Rails specific configuration options for people to use
# instead of the base Configurator.
class RailsConfigurator < Mongrel::Configurator
# 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.
# * :cwd => The change to working directory
#
# 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.
# This lets you attach Rails to any URI(s) you want,
# but it still protects you from threads destroying
# your handler.
def rails(options={})
return @rails_handler if @rails_handler
ops = resolve_defaults(options)
# fix up some defaults
ops[:environment] ||= "development"
ops[:docroot] ||= "public"
ops[:mime] ||= {}
$orig_dollar_quote = $".clone
ENV['RAILS_ENV'] = ops[:environment]
env_location = "#{ops[:cwd]}/config/environment"
require env_location
require 'dispatcher'
require 'mongrel/rails'
ActionController::AbstractRequest.relative_url_root = ops[:prefix] if ops[:prefix]
@rails_handler = RailsHandler.new(ops[:docroot], ops[:mime])
end
# Reloads Rails. This isn't too reliable really, but it
# should work for most minimal reload purposes. The only reliable
# way to reload properly is to stop and then start the process.
def reload!
if not @rails_handler
raise "Rails was not configured. Read the docs for RailsConfigurator."
end
Mongrel.log("Reloading Rails...")
@rails_handler.reload!
Mongrel.log("Done reloading Rails.")
end
# 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)
setup_signals(options)
unless RUBY_PLATFORM =~ /djgpp|(cyg|ms|bcc)win|mingw/
# rails reload
trap("HUP") { Mongrel.log("HUP signal received."); reload! }
Mongrel.log("Rails signals registered. HUP => reload (without restart). It might not work well.")
end
end
end
end
end