mongrel 1.1.2-java
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of mongrel might be problematic. Click here for more details.
- data.tar.gz.sig +1 -0
- data/CHANGELOG +12 -0
- data/COPYING +55 -0
- data/LICENSE +55 -0
- data/Manifest +69 -0
- data/README +74 -0
- data/TODO +5 -0
- data/bin/mongrel_rails +283 -0
- data/examples/builder.rb +29 -0
- data/examples/camping/README +3 -0
- data/examples/camping/blog.rb +294 -0
- data/examples/camping/tepee.rb +149 -0
- data/examples/httpd.conf +474 -0
- data/examples/mime.yaml +3 -0
- data/examples/mongrel.conf +9 -0
- data/examples/mongrel_simple_ctrl.rb +92 -0
- data/examples/mongrel_simple_service.rb +116 -0
- data/examples/monitrc +57 -0
- data/examples/random_thrash.rb +19 -0
- data/examples/simpletest.rb +52 -0
- data/examples/webrick_compare.rb +20 -0
- data/ext/http11/ext_help.h +14 -0
- data/ext/http11/extconf.rb +6 -0
- data/ext/http11/http11.c +402 -0
- data/ext/http11/http11_parser.c +1221 -0
- data/ext/http11/http11_parser.h +49 -0
- data/ext/http11/http11_parser.java.rl +170 -0
- data/ext/http11/http11_parser.rl +152 -0
- data/ext/http11/http11_parser_common.rl +54 -0
- data/ext/http11_java/Http11Service.java +13 -0
- data/ext/http11_java/org/jruby/mongrel/Http11.java +266 -0
- data/ext/http11_java/org/jruby/mongrel/Http11Parser.java +572 -0
- data/lib/http11.jar +0 -0
- data/lib/mongrel.rb +355 -0
- data/lib/mongrel/camping.rb +107 -0
- data/lib/mongrel/cgi.rb +181 -0
- data/lib/mongrel/command.rb +222 -0
- data/lib/mongrel/configurator.rb +388 -0
- data/lib/mongrel/const.rb +110 -0
- data/lib/mongrel/debug.rb +203 -0
- data/lib/mongrel/gems.rb +22 -0
- data/lib/mongrel/handlers.rb +468 -0
- data/lib/mongrel/header_out.rb +28 -0
- data/lib/mongrel/http_request.rb +155 -0
- data/lib/mongrel/http_response.rb +163 -0
- data/lib/mongrel/init.rb +10 -0
- data/lib/mongrel/mime_types.yml +616 -0
- data/lib/mongrel/rails.rb +185 -0
- data/lib/mongrel/stats.rb +89 -0
- data/lib/mongrel/tcphack.rb +18 -0
- data/lib/mongrel/uri_classifier.rb +76 -0
- data/mongrel-public_cert.pem +20 -0
- data/mongrel.gemspec +263 -0
- data/setup.rb +1585 -0
- data/test/mime.yaml +3 -0
- data/test/mongrel.conf +1 -0
- data/test/test_cgi_wrapper.rb +26 -0
- data/test/test_command.rb +86 -0
- data/test/test_conditional.rb +107 -0
- data/test/test_configurator.rb +87 -0
- data/test/test_debug.rb +25 -0
- data/test/test_handlers.rb +103 -0
- data/test/test_http11.rb +156 -0
- data/test/test_redirect_handler.rb +44 -0
- data/test/test_request_progress.rb +99 -0
- data/test/test_response.rb +127 -0
- data/test/test_stats.rb +35 -0
- data/test/test_uriclassifier.rb +261 -0
- data/test/test_ws.rb +115 -0
- data/test/testhelp.rb +66 -0
- data/tools/trickletest.rb +45 -0
- metadata +186 -0
- metadata.gz.sig +4 -0
data/lib/mongrel/cgi.rb
ADDED
@@ -0,0 +1,181 @@
|
|
1
|
+
# Copyright (c) 2005 Zed A. Shaw
|
2
|
+
# You can redistribute it and/or modify it under the same terms as Ruby.
|
3
|
+
#
|
4
|
+
# Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html
|
5
|
+
# for more information.
|
6
|
+
|
7
|
+
require 'cgi'
|
8
|
+
|
9
|
+
module Mongrel
|
10
|
+
# The beginning of a complete wrapper around Mongrel's internal HTTP processing
|
11
|
+
# system but maintaining the original Ruby CGI module. Use this only as a crutch
|
12
|
+
# to get existing CGI based systems working. It should handle everything, but please
|
13
|
+
# notify me if you see special warnings. This work is still very alpha so I need
|
14
|
+
# testers to help work out the various corner cases.
|
15
|
+
#
|
16
|
+
# The CGIWrapper.handler attribute is normally not set and is available for
|
17
|
+
# frameworks that need to get back to the handler. Rails uses this to give
|
18
|
+
# people access to the RailsHandler#files (DirHandler really) so they can
|
19
|
+
# look-up paths and do other things with the files managed there.
|
20
|
+
#
|
21
|
+
# In Rails you can get the real file for a request with:
|
22
|
+
#
|
23
|
+
# path = @request.cgi.handler.files.can_serve(@request['PATH_INFO'])
|
24
|
+
#
|
25
|
+
# Which is ugly but does the job. Feel free to write a Rails helper for that.
|
26
|
+
# Refer to DirHandler#can_serve for more information on this.
|
27
|
+
class CGIWrapper < ::CGI
|
28
|
+
public :env_table
|
29
|
+
attr_reader :options
|
30
|
+
attr_accessor :handler
|
31
|
+
# Set this to false if you want calls to CGIWrapper.out to not actually send
|
32
|
+
# the response until you force it.
|
33
|
+
attr_accessor :default_really_final
|
34
|
+
|
35
|
+
# these are stripped out of any keys passed to CGIWrapper.header function
|
36
|
+
REMOVED_KEYS = [ "nph","status","server","connection","type",
|
37
|
+
"charset","length","language","expires"]
|
38
|
+
|
39
|
+
# Takes an HttpRequest and HttpResponse object, plus any additional arguments
|
40
|
+
# normally passed to CGI. These are used internally to create a wrapper around
|
41
|
+
# the real CGI while maintaining Mongrel's view of the world.
|
42
|
+
def initialize(request, response, *args)
|
43
|
+
@request = request
|
44
|
+
@response = response
|
45
|
+
@args = *args
|
46
|
+
@input = request.body
|
47
|
+
@head = {}
|
48
|
+
@out_called = false
|
49
|
+
@default_really_final=true
|
50
|
+
super(*args)
|
51
|
+
end
|
52
|
+
|
53
|
+
# The header is typically called to send back the header. In our case we
|
54
|
+
# collect it into a hash for later usage.
|
55
|
+
#
|
56
|
+
# nph -- Mostly ignored. It'll output the date.
|
57
|
+
# connection -- Completely ignored. Why is CGI doing this?
|
58
|
+
# length -- Ignored since Mongrel figures this out from what you write to output.
|
59
|
+
#
|
60
|
+
def header(options = "text/html")
|
61
|
+
# if they pass in a string then just write the Content-Type
|
62
|
+
if options.class == String
|
63
|
+
@head['Content-Type'] = options unless @head['Content-Type']
|
64
|
+
else
|
65
|
+
# convert the given options into what Mongrel wants
|
66
|
+
@head['Content-Type'] = options['type'] || "text/html"
|
67
|
+
@head['Content-Type'] += "; charset=" + options['charset'] if options.has_key? "charset" if options['charset']
|
68
|
+
|
69
|
+
# setup date only if they use nph
|
70
|
+
@head['Date'] = CGI::rfc1123_date(Time.now) if options['nph']
|
71
|
+
|
72
|
+
# setup the server to use the default or what they set
|
73
|
+
@head['Server'] = options['server'] || env_table['SERVER_SOFTWARE']
|
74
|
+
|
75
|
+
# remaining possible options they can give
|
76
|
+
@head['Status'] = options['status'] if options['status']
|
77
|
+
@head['Content-Language'] = options['language'] if options['language']
|
78
|
+
@head['Expires'] = options['expires'] if options['expires']
|
79
|
+
|
80
|
+
# drop the keys we don't want anymore
|
81
|
+
REMOVED_KEYS.each {|k| options.delete(k) }
|
82
|
+
|
83
|
+
# finally just convert the rest raw (which puts 'cookie' directly)
|
84
|
+
# 'cookie' is translated later as we write the header out
|
85
|
+
options.each{|k,v| @head[k] = v}
|
86
|
+
end
|
87
|
+
|
88
|
+
# doing this fakes out the cgi library to think the headers are empty
|
89
|
+
# we then do the real headers in the out function call later
|
90
|
+
""
|
91
|
+
end
|
92
|
+
|
93
|
+
# Takes any 'cookie' setting and sends it over the Mongrel header,
|
94
|
+
# then removes the setting from the options. If cookie is an
|
95
|
+
# Array or Hash then it sends those on with .to_s, otherwise
|
96
|
+
# it just calls .to_s on it and hopefully your "cookie" can
|
97
|
+
# write itself correctly.
|
98
|
+
def send_cookies(to)
|
99
|
+
# convert the cookies based on the myriad of possible ways to set a cookie
|
100
|
+
if @head['cookie']
|
101
|
+
cookie = @head['cookie']
|
102
|
+
case cookie
|
103
|
+
when Array
|
104
|
+
cookie.each {|c| to['Set-Cookie'] = c.to_s }
|
105
|
+
when Hash
|
106
|
+
cookie.each_value {|c| to['Set-Cookie'] = c.to_s}
|
107
|
+
else
|
108
|
+
to['Set-Cookie'] = options['cookie'].to_s
|
109
|
+
end
|
110
|
+
|
111
|
+
@head.delete('cookie')
|
112
|
+
end
|
113
|
+
|
114
|
+
# @output_cookies seems to never be used, but we'll process it just in case
|
115
|
+
@output_cookies.each {|c| to['Set-Cookie'] = c.to_s } if @output_cookies
|
116
|
+
end
|
117
|
+
|
118
|
+
# The dumb thing is people can call header or this or both and in any order.
|
119
|
+
# So, we just reuse header and then finalize the HttpResponse the right way.
|
120
|
+
# Status is taken from the various options and converted to what Mongrel needs
|
121
|
+
# via the CGIWrapper.status function.
|
122
|
+
#
|
123
|
+
# We also prevent Rails from actually doing the final send by adding a
|
124
|
+
# second parameter "really_final". Only Mongrel calls this after Rails
|
125
|
+
# is done. Since this will break other frameworks, it defaults to
|
126
|
+
# a different setting for rails (false) and (true) for others.
|
127
|
+
def out(options = "text/html", really_final=@default_really_final)
|
128
|
+
if @out_called || !really_final
|
129
|
+
# don't do it more than once or if it's not the really final call
|
130
|
+
return
|
131
|
+
end
|
132
|
+
|
133
|
+
header(options)
|
134
|
+
|
135
|
+
@response.start status do |head, body|
|
136
|
+
send_cookies(head)
|
137
|
+
|
138
|
+
@head.each {|k,v| head[k] = v}
|
139
|
+
body.write(yield || "")
|
140
|
+
end
|
141
|
+
|
142
|
+
@out_called = true
|
143
|
+
end
|
144
|
+
|
145
|
+
# Computes the status once, but lazily so that people who call header twice
|
146
|
+
# don't get penalized. Because CGI insists on including the options status
|
147
|
+
# message in the status we have to do a bit of parsing.
|
148
|
+
def status
|
149
|
+
if not @status
|
150
|
+
stat = @head["Status"]
|
151
|
+
stat = stat.split(' ')[0] if stat
|
152
|
+
|
153
|
+
@status = stat || "200"
|
154
|
+
end
|
155
|
+
|
156
|
+
@status
|
157
|
+
end
|
158
|
+
|
159
|
+
# Used to wrap the normal args variable used inside CGI.
|
160
|
+
def args
|
161
|
+
@args
|
162
|
+
end
|
163
|
+
|
164
|
+
# Used to wrap the normal env_table variable used inside CGI.
|
165
|
+
def env_table
|
166
|
+
@request.params
|
167
|
+
end
|
168
|
+
|
169
|
+
# Used to wrap the normal stdinput variable used inside CGI.
|
170
|
+
def stdinput
|
171
|
+
@input
|
172
|
+
end
|
173
|
+
|
174
|
+
# The stdoutput should be completely bypassed but we'll drop a warning just in case
|
175
|
+
def stdoutput
|
176
|
+
STDERR.puts "WARNING: Your program is doing something not expected. Please tell Zed that stdoutput was used and what software you are running. Thanks."
|
177
|
+
@response.body
|
178
|
+
end
|
179
|
+
|
180
|
+
end
|
181
|
+
end
|
@@ -0,0 +1,222 @@
|
|
1
|
+
# Copyright (c) 2005 Zed A. Shaw
|
2
|
+
# You can redistribute it and/or modify it under the same terms as Ruby.
|
3
|
+
#
|
4
|
+
# Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html
|
5
|
+
# for more information.
|
6
|
+
|
7
|
+
require 'singleton'
|
8
|
+
require 'optparse'
|
9
|
+
|
10
|
+
require 'mongrel/gems'
|
11
|
+
Mongrel::Gems.require 'gem_plugin'
|
12
|
+
|
13
|
+
module Mongrel
|
14
|
+
|
15
|
+
# Contains all of the various commands that are used with
|
16
|
+
# Mongrel servers.
|
17
|
+
|
18
|
+
module Command
|
19
|
+
|
20
|
+
BANNER = "Usage: mongrel_rails <command> [options]"
|
21
|
+
|
22
|
+
# A Command pattern implementation used to create the set of command available to the user
|
23
|
+
# from Mongrel. The script uses objects which implement this interface to do the
|
24
|
+
# user's bidding.
|
25
|
+
module Base
|
26
|
+
|
27
|
+
attr_reader :valid, :done_validating, :original_args
|
28
|
+
|
29
|
+
# Called by the implemented command to set the options for that command.
|
30
|
+
# Every option has a short and long version, a description, a variable to
|
31
|
+
# set, and a default value. No exceptions.
|
32
|
+
def options(opts)
|
33
|
+
# process the given options array
|
34
|
+
opts.each do |short, long, help, variable, default|
|
35
|
+
self.instance_variable_set(variable, default)
|
36
|
+
@opt.on(short, long, help) do |arg|
|
37
|
+
self.instance_variable_set(variable, arg)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Called by the subclass to setup the command and parse the argv arguments.
|
43
|
+
# The call is destructive on argv since it uses the OptionParser#parse! function.
|
44
|
+
def initialize(options={})
|
45
|
+
argv = options[:argv] || []
|
46
|
+
@opt = OptionParser.new
|
47
|
+
@opt.banner = Mongrel::Command::BANNER
|
48
|
+
@valid = true
|
49
|
+
# this is retarded, but it has to be done this way because -h and -v exit
|
50
|
+
@done_validating = false
|
51
|
+
@original_args = argv.dup
|
52
|
+
|
53
|
+
configure
|
54
|
+
|
55
|
+
# I need to add my own -h definition to prevent the -h by default from exiting.
|
56
|
+
@opt.on_tail("-h", "--help", "Show this message") do
|
57
|
+
@done_validating = true
|
58
|
+
puts @opt
|
59
|
+
end
|
60
|
+
|
61
|
+
# I need to add my own -v definition to prevent the -v from exiting by default as well.
|
62
|
+
@opt.on_tail("--version", "Show version") do
|
63
|
+
@done_validating = true
|
64
|
+
if VERSION
|
65
|
+
puts "Version #{Mongrel::Const::MONGREL_VERSION}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
@opt.parse! argv
|
70
|
+
end
|
71
|
+
|
72
|
+
def configure
|
73
|
+
options []
|
74
|
+
end
|
75
|
+
|
76
|
+
# Returns true/false depending on whether the command is configured properly.
|
77
|
+
def validate
|
78
|
+
return @valid
|
79
|
+
end
|
80
|
+
|
81
|
+
# Returns a help message. Defaults to OptionParser#help which should be good.
|
82
|
+
def help
|
83
|
+
@opt.help
|
84
|
+
end
|
85
|
+
|
86
|
+
# Runs the command doing it's job. You should implement this otherwise it will
|
87
|
+
# throw a NotImplementedError as a reminder.
|
88
|
+
def run
|
89
|
+
raise NotImplementedError
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
# Validates the given expression is true and prints the message if not, exiting.
|
94
|
+
def valid?(exp, message)
|
95
|
+
if not @done_validating and (not exp)
|
96
|
+
failure message
|
97
|
+
@valid = false
|
98
|
+
@done_validating = true
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Validates that a file exists and if not displays the message
|
103
|
+
def valid_exists?(file, message)
|
104
|
+
valid?(file != nil && File.exist?(file), message)
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
# Validates that the file is a file and not a directory or something else.
|
109
|
+
def valid_file?(file, message)
|
110
|
+
valid?(file != nil && File.file?(file), message)
|
111
|
+
end
|
112
|
+
|
113
|
+
# Validates that the given directory exists
|
114
|
+
def valid_dir?(file, message)
|
115
|
+
valid?(file != nil && File.directory?(file), message)
|
116
|
+
end
|
117
|
+
|
118
|
+
def valid_user?(user)
|
119
|
+
valid?(@group, "You must also specify a group.")
|
120
|
+
begin
|
121
|
+
Etc.getpwnam(user)
|
122
|
+
rescue
|
123
|
+
failure "User does not exist: #{user}"
|
124
|
+
@valid = false
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def valid_group?(group)
|
129
|
+
valid?(@user, "You must also specify a user.")
|
130
|
+
begin
|
131
|
+
Etc.getgrnam(group)
|
132
|
+
rescue
|
133
|
+
failure "Group does not exist: #{group}"
|
134
|
+
@valid = false
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# Just a simple method to display failure until something better is developed.
|
139
|
+
def failure(message)
|
140
|
+
STDERR.puts "!!! #{message}"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# A Singleton class that manages all of the available commands
|
145
|
+
# and handles running them.
|
146
|
+
class Registry
|
147
|
+
include Singleton
|
148
|
+
|
149
|
+
# Builds a list of possible commands from the Command derivates list
|
150
|
+
def commands
|
151
|
+
pmgr = GemPlugin::Manager.instance
|
152
|
+
list = pmgr.plugins["/commands"].keys
|
153
|
+
return list.sort
|
154
|
+
end
|
155
|
+
|
156
|
+
# Prints a list of available commands.
|
157
|
+
def print_command_list
|
158
|
+
puts "#{Mongrel::Command::BANNER}\nAvailable commands are:\n\n"
|
159
|
+
|
160
|
+
self.commands.each do |name|
|
161
|
+
if /mongrel::/ =~ name
|
162
|
+
name = name[9 .. -1]
|
163
|
+
end
|
164
|
+
|
165
|
+
puts " - #{name[1 .. -1]}\n"
|
166
|
+
end
|
167
|
+
|
168
|
+
puts "\nEach command takes -h as an option to get help."
|
169
|
+
|
170
|
+
end
|
171
|
+
|
172
|
+
|
173
|
+
# Runs the args against the first argument as the command name.
|
174
|
+
# If it has any errors it returns a false, otherwise it return true.
|
175
|
+
def run(args)
|
176
|
+
# find the command
|
177
|
+
cmd_name = args.shift
|
178
|
+
|
179
|
+
if !cmd_name or cmd_name == "?" or cmd_name == "help"
|
180
|
+
print_command_list
|
181
|
+
return true
|
182
|
+
elsif cmd_name == "--version"
|
183
|
+
puts "Mongrel Web Server #{Mongrel::Const::MONGREL_VERSION}"
|
184
|
+
return true
|
185
|
+
end
|
186
|
+
|
187
|
+
begin
|
188
|
+
# quick hack so that existing commands will keep working but the Mongrel:: ones can be moved
|
189
|
+
if ["start", "stop", "restart"].include? cmd_name
|
190
|
+
cmd_name = "mongrel::" + cmd_name
|
191
|
+
end
|
192
|
+
|
193
|
+
command = GemPlugin::Manager.instance.create("/commands/#{cmd_name}", :argv => args)
|
194
|
+
rescue OptionParser::InvalidOption
|
195
|
+
STDERR.puts "#$! for command '#{cmd_name}'"
|
196
|
+
STDERR.puts "Try #{cmd_name} -h to get help."
|
197
|
+
return false
|
198
|
+
rescue
|
199
|
+
STDERR.puts "ERROR RUNNING '#{cmd_name}': #$!"
|
200
|
+
STDERR.puts "Use help command to get help"
|
201
|
+
return false
|
202
|
+
end
|
203
|
+
|
204
|
+
# Normally the command is NOT valid right after being created
|
205
|
+
# but sometimes (like with -h or -v) there's no further processing
|
206
|
+
# needed so the command is already valid so we can skip it.
|
207
|
+
if not command.done_validating
|
208
|
+
if not command.validate
|
209
|
+
STDERR.puts "#{cmd_name} reported an error. Use mongrel_rails #{cmd_name} -h to get help."
|
210
|
+
return false
|
211
|
+
else
|
212
|
+
command.run
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
return true
|
217
|
+
end
|
218
|
+
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
@@ -0,0 +1,388 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'etc'
|
3
|
+
|
4
|
+
module Mongrel
|
5
|
+
# Implements a simple DSL for configuring a Mongrel server for your
|
6
|
+
# purposes. More used by framework implementers to setup Mongrel
|
7
|
+
# how they like, but could be used by regular folks to add more things
|
8
|
+
# to an existing mongrel configuration.
|
9
|
+
#
|
10
|
+
# It is used like this:
|
11
|
+
#
|
12
|
+
# require 'mongrel'
|
13
|
+
# config = Mongrel::Configurator.new :host => "127.0.0.1" do
|
14
|
+
# listener :port => 3000 do
|
15
|
+
# uri "/app", :handler => Mongrel::DirHandler.new(".", load_mime_map("mime.yaml"))
|
16
|
+
# end
|
17
|
+
# run
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# This will setup a simple DirHandler at the current directory and load additional
|
21
|
+
# mime types from mimy.yaml. The :host => "127.0.0.1" is actually not
|
22
|
+
# specific to the servers but just a hash of default parameters that all
|
23
|
+
# server or uri calls receive.
|
24
|
+
#
|
25
|
+
# When you are inside the block after Mongrel::Configurator.new you can simply
|
26
|
+
# call functions that are part of Configurator (like server, uri, daemonize, etc)
|
27
|
+
# without having to refer to anything else. You can also call these functions on
|
28
|
+
# the resulting object directly for additional configuration.
|
29
|
+
#
|
30
|
+
# A major thing about Configurator is that it actually lets you configure
|
31
|
+
# multiple listeners for any hosts and ports you want. These are kept in a
|
32
|
+
# map config.listeners so you can get to them.
|
33
|
+
#
|
34
|
+
# * :pid_file => Where to write the process ID.
|
35
|
+
class Configurator
|
36
|
+
attr_reader :listeners
|
37
|
+
attr_reader :defaults
|
38
|
+
attr_reader :needs_restart
|
39
|
+
|
40
|
+
# You pass in initial defaults and then a block to continue configuring.
|
41
|
+
def initialize(defaults={}, &block)
|
42
|
+
@listener = nil
|
43
|
+
@listener_name = nil
|
44
|
+
@listeners = {}
|
45
|
+
@defaults = defaults
|
46
|
+
@needs_restart = false
|
47
|
+
@pid_file = defaults[:pid_file]
|
48
|
+
|
49
|
+
if block
|
50
|
+
cloaker(&block).bind(self).call
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Change privileges of the process to specified user and group.
|
55
|
+
def change_privilege(user, group)
|
56
|
+
begin
|
57
|
+
uid, gid = Process.euid, Process.egid
|
58
|
+
target_uid = Etc.getpwnam(user).uid if user
|
59
|
+
target_gid = Etc.getgrnam(group).gid if group
|
60
|
+
|
61
|
+
if uid != target_uid or gid != target_gid
|
62
|
+
log "Initiating groups for #{user.inspect}:#{group.inspect}."
|
63
|
+
Process.initgroups(user, target_gid)
|
64
|
+
|
65
|
+
log "Changing group to #{group.inspect}."
|
66
|
+
Process::GID.change_privilege(target_gid)
|
67
|
+
|
68
|
+
log "Changing user to #{user.inspect}."
|
69
|
+
Process::UID.change_privilege(target_uid)
|
70
|
+
end
|
71
|
+
rescue Errno::EPERM => e
|
72
|
+
log "Couldn't change user and group to #{user.inspect}:#{group.inspect}: #{e.to_s}."
|
73
|
+
log "Mongrel failed to start."
|
74
|
+
exit 1
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def remove_pid_file
|
79
|
+
File.unlink(@pid_file) if @pid_file and File.exists?(@pid_file)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Writes the PID file if we're not on Windows.
|
83
|
+
def write_pid_file
|
84
|
+
if RUBY_PLATFORM !~ /mswin/
|
85
|
+
log "Writing PID file to #{@pid_file}"
|
86
|
+
open(@pid_file,"w") {|f| f.write(Process.pid) }
|
87
|
+
open(@pid_file,"w") do |f|
|
88
|
+
f.write(Process.pid)
|
89
|
+
File.chmod(0644, @pid_file)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Generates a class for cloaking the current self and making the DSL nicer.
|
95
|
+
def cloaking_class
|
96
|
+
class << self
|
97
|
+
self
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Do not call this. You were warned.
|
102
|
+
def cloaker(&block)
|
103
|
+
cloaking_class.class_eval do
|
104
|
+
define_method :cloaker_, &block
|
105
|
+
meth = instance_method( :cloaker_ )
|
106
|
+
remove_method :cloaker_
|
107
|
+
meth
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# This will resolve the given options against the defaults.
|
112
|
+
# Normally just used internally.
|
113
|
+
def resolve_defaults(options)
|
114
|
+
options.merge(@defaults)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Starts a listener block. This is the only one that actually takes
|
118
|
+
# a block and then you make Configurator.uri calls in order to setup
|
119
|
+
# your URIs and handlers. If you write your Handlers as GemPlugins
|
120
|
+
# then you can use load_plugins and plugin to load them.
|
121
|
+
#
|
122
|
+
# It expects the following options (or defaults):
|
123
|
+
#
|
124
|
+
# * :host => Host name to bind.
|
125
|
+
# * :port => Port to bind.
|
126
|
+
# * :num_processors => The maximum number of concurrent threads allowed.
|
127
|
+
# * :throttle => Time to pause (in hundredths of a second) between accepting clients.
|
128
|
+
# * :timeout => Time to wait (in seconds) before killing a stalled thread.
|
129
|
+
# * :user => User to change to, must have :group as well.
|
130
|
+
# * :group => Group to change to, must have :user as well.
|
131
|
+
#
|
132
|
+
def listener(options={},&block)
|
133
|
+
raise "Cannot call listener inside another listener block." if (@listener or @listener_name)
|
134
|
+
ops = resolve_defaults(options)
|
135
|
+
ops[:num_processors] ||= 950
|
136
|
+
ops[:throttle] ||= 0
|
137
|
+
ops[:timeout] ||= 60
|
138
|
+
|
139
|
+
@listener = Mongrel::HttpServer.new(ops[:host], ops[:port].to_i, ops[:num_processors].to_i, ops[:throttle].to_i, ops[:timeout].to_i)
|
140
|
+
@listener_name = "#{ops[:host]}:#{ops[:port]}"
|
141
|
+
@listeners[@listener_name] = @listener
|
142
|
+
|
143
|
+
if ops[:user] and ops[:group]
|
144
|
+
change_privilege(ops[:user], ops[:group])
|
145
|
+
end
|
146
|
+
|
147
|
+
# Does the actual cloaking operation to give the new implicit self.
|
148
|
+
if block
|
149
|
+
cloaker(&block).bind(self).call
|
150
|
+
end
|
151
|
+
|
152
|
+
# all done processing this listener setup, reset implicit variables
|
153
|
+
@listener = nil
|
154
|
+
@listener_name = nil
|
155
|
+
end
|
156
|
+
|
157
|
+
|
158
|
+
# Called inside a Configurator.listener block in order to
|
159
|
+
# add URI->handler mappings for that listener. Use this as
|
160
|
+
# many times as you like. It expects the following options
|
161
|
+
# or defaults:
|
162
|
+
#
|
163
|
+
# * :handler => HttpHandler -- Handler to use for this location.
|
164
|
+
# * :in_front => true/false -- Rather than appending, it prepends this handler.
|
165
|
+
def uri(location, options={})
|
166
|
+
ops = resolve_defaults(options)
|
167
|
+
@listener.register(location, ops[:handler], ops[:in_front])
|
168
|
+
end
|
169
|
+
|
170
|
+
|
171
|
+
# Daemonizes the current Ruby script turning all the
|
172
|
+
# listeners into an actual "server" or detached process.
|
173
|
+
# You must call this *before* frameworks that open files
|
174
|
+
# as otherwise the files will be closed by this function.
|
175
|
+
#
|
176
|
+
# Does not work for Win32 systems (the call is silently ignored).
|
177
|
+
#
|
178
|
+
# Requires the following options or defaults:
|
179
|
+
#
|
180
|
+
# * :cwd => Directory to change to.
|
181
|
+
# * :log_file => Where to write STDOUT and STDERR.
|
182
|
+
#
|
183
|
+
# It is safe to call this on win32 as it will only require the daemons
|
184
|
+
# gem/library if NOT win32.
|
185
|
+
def daemonize(options={})
|
186
|
+
ops = resolve_defaults(options)
|
187
|
+
# save this for later since daemonize will hose it
|
188
|
+
if RUBY_PLATFORM !~ /mswin/
|
189
|
+
require 'daemons/daemonize'
|
190
|
+
|
191
|
+
logfile = ops[:log_file]
|
192
|
+
if logfile[0].chr != "/"
|
193
|
+
logfile = File.join(ops[:cwd],logfile)
|
194
|
+
if not File.exist?(File.dirname(logfile))
|
195
|
+
log "!!! Log file directory not found at full path #{File.dirname(logfile)}. Update your configuration to use a full path."
|
196
|
+
exit 1
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
Daemonize.daemonize(logfile)
|
201
|
+
|
202
|
+
# change back to the original starting directory
|
203
|
+
Dir.chdir(ops[:cwd])
|
204
|
+
|
205
|
+
else
|
206
|
+
log "WARNING: Win32 does not support daemon mode."
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
|
211
|
+
# Uses the GemPlugin system to easily load plugins based on their
|
212
|
+
# gem dependencies. You pass in either an :includes => [] or
|
213
|
+
# :excludes => [] setting listing the names of plugins to include
|
214
|
+
# or exclude from the determining the dependencies.
|
215
|
+
def load_plugins(options={})
|
216
|
+
ops = resolve_defaults(options)
|
217
|
+
|
218
|
+
load_settings = {}
|
219
|
+
if ops[:includes]
|
220
|
+
ops[:includes].each do |plugin|
|
221
|
+
load_settings[plugin] = GemPlugin::INCLUDE
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
if ops[:excludes]
|
226
|
+
ops[:excludes].each do |plugin|
|
227
|
+
load_settings[plugin] = GemPlugin::EXCLUDE
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
GemPlugin::Manager.instance.load(load_settings)
|
232
|
+
end
|
233
|
+
|
234
|
+
|
235
|
+
# Easy way to load a YAML file and apply default settings.
|
236
|
+
def load_yaml(file, default={})
|
237
|
+
default.merge(YAML.load_file(file))
|
238
|
+
end
|
239
|
+
|
240
|
+
|
241
|
+
# Loads the MIME map file and checks that it is correct
|
242
|
+
# on loading. This is commonly passed to Mongrel::DirHandler
|
243
|
+
# or any framework handler that uses DirHandler to serve files.
|
244
|
+
# You can also include a set of default MIME types as additional
|
245
|
+
# settings. See Mongrel::DirHandler for how the MIME types map
|
246
|
+
# is organized.
|
247
|
+
def load_mime_map(file, mime={})
|
248
|
+
# configure any requested mime map
|
249
|
+
mime = load_yaml(file, mime)
|
250
|
+
|
251
|
+
# check all the mime types to make sure they are the right format
|
252
|
+
mime.each {|k,v| log "WARNING: MIME type #{k} must start with '.'" if k.index(".") != 0 }
|
253
|
+
|
254
|
+
return mime
|
255
|
+
end
|
256
|
+
|
257
|
+
|
258
|
+
# Loads and creates a plugin for you based on the given
|
259
|
+
# name and configured with the selected options. The options
|
260
|
+
# are merged with the defaults prior to passing them in.
|
261
|
+
def plugin(name, options={})
|
262
|
+
ops = resolve_defaults(options)
|
263
|
+
GemPlugin::Manager.instance.create(name, ops)
|
264
|
+
end
|
265
|
+
|
266
|
+
# Lets you do redirects easily as described in Mongrel::RedirectHandler.
|
267
|
+
# You use it inside the configurator like this:
|
268
|
+
#
|
269
|
+
# redirect("/test", "/to/there") # simple
|
270
|
+
# redirect("/to", /t/, 'w') # regexp
|
271
|
+
# redirect("/hey", /(w+)/) {|match| ...} # block
|
272
|
+
#
|
273
|
+
def redirect(from, pattern, replacement = nil, &block)
|
274
|
+
uri from, :handler => Mongrel::RedirectHandler.new(pattern, replacement, &block)
|
275
|
+
end
|
276
|
+
|
277
|
+
# Works like a meta run method which goes through all the
|
278
|
+
# configured listeners. Use the Configurator.join method
|
279
|
+
# to prevent Ruby from exiting until each one is done.
|
280
|
+
def run
|
281
|
+
@listeners.each {|name,s|
|
282
|
+
s.run
|
283
|
+
}
|
284
|
+
|
285
|
+
$mongrel_sleeper_thread = Thread.new { loop { sleep 1 } }
|
286
|
+
end
|
287
|
+
|
288
|
+
# Calls .stop on all the configured listeners so they
|
289
|
+
# stop processing requests (gracefully). By default it
|
290
|
+
# assumes that you don't want to restart.
|
291
|
+
def stop(needs_restart=false, synchronous=false)
|
292
|
+
@listeners.each do |name,s|
|
293
|
+
s.stop(synchronous)
|
294
|
+
end
|
295
|
+
@needs_restart = needs_restart
|
296
|
+
end
|
297
|
+
|
298
|
+
|
299
|
+
# This method should actually be called *outside* of the
|
300
|
+
# Configurator block so that you can control it. In other words
|
301
|
+
# do it like: config.join.
|
302
|
+
def join
|
303
|
+
@listeners.values.each {|s| s.acceptor.join }
|
304
|
+
end
|
305
|
+
|
306
|
+
|
307
|
+
# Calling this before you register your URIs to the given location
|
308
|
+
# will setup a set of handlers that log open files, objects, and the
|
309
|
+
# parameters for each request. This helps you track common problems
|
310
|
+
# found in Rails applications that are either slow or become unresponsive
|
311
|
+
# after a little while.
|
312
|
+
#
|
313
|
+
# You can pass an extra parameter *what* to indicate what you want to
|
314
|
+
# debug. For example, if you just want to dump rails stuff then do:
|
315
|
+
#
|
316
|
+
# debug "/", what = [:rails]
|
317
|
+
#
|
318
|
+
# And it will only produce the log/mongrel_debug/rails.log file.
|
319
|
+
# Available options are: :access, :files, :objects, :threads, :rails
|
320
|
+
#
|
321
|
+
# NOTE: Use [:files] to get accesses dumped to stderr like with WEBrick.
|
322
|
+
def debug(location, what = [:access, :files, :objects, :threads, :rails])
|
323
|
+
require 'mongrel/debug'
|
324
|
+
handlers = {
|
325
|
+
:access => "/handlers/requestlog::access",
|
326
|
+
:files => "/handlers/requestlog::files",
|
327
|
+
:objects => "/handlers/requestlog::objects",
|
328
|
+
:threads => "/handlers/requestlog::threads",
|
329
|
+
:rails => "/handlers/requestlog::params"
|
330
|
+
}
|
331
|
+
|
332
|
+
# turn on the debugging infrastructure, and ObjectTracker is a pig
|
333
|
+
MongrelDbg.configure
|
334
|
+
|
335
|
+
# now we roll through each requested debug type, turn it on and load that plugin
|
336
|
+
what.each do |type|
|
337
|
+
MongrelDbg.begin_trace type
|
338
|
+
uri location, :handler => plugin(handlers[type])
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
# Used to allow you to let users specify their own configurations
|
343
|
+
# inside your Configurator setup. You pass it a script name and
|
344
|
+
# it reads it in and does an eval on the contents passing in the right
|
345
|
+
# binding so they can put their own Configurator statements.
|
346
|
+
def run_config(script)
|
347
|
+
open(script) {|f| eval(f.read, proc {self}) }
|
348
|
+
end
|
349
|
+
|
350
|
+
# Sets up the standard signal handlers that are used on most Ruby
|
351
|
+
# It only configures if the platform is not win32 and doesn't do
|
352
|
+
# a HUP signal since this is typically framework specific.
|
353
|
+
#
|
354
|
+
# Requires a :pid_file option given to Configurator.new to indicate a file to delete.
|
355
|
+
# It sets the MongrelConfig.needs_restart attribute if
|
356
|
+
# the start command should reload. It's up to you to detect this
|
357
|
+
# and do whatever is needed for a "restart".
|
358
|
+
#
|
359
|
+
# This command is safely ignored if the platform is win32 (with a warning)
|
360
|
+
def setup_signals(options={})
|
361
|
+
ops = resolve_defaults(options)
|
362
|
+
|
363
|
+
# forced shutdown, even if previously restarted (actually just like TERM but for CTRL-C)
|
364
|
+
trap("INT") { log "INT signal received."; stop(false) }
|
365
|
+
|
366
|
+
# clean up the pid file always
|
367
|
+
at_exit { remove_pid_file }
|
368
|
+
|
369
|
+
if RUBY_PLATFORM !~ /mswin/
|
370
|
+
# graceful shutdown
|
371
|
+
trap("TERM") { log "TERM signal received."; stop }
|
372
|
+
trap("USR1") { log "USR1 received, toggling $mongrel_debug_client to #{!$mongrel_debug_client}"; $mongrel_debug_client = !$mongrel_debug_client }
|
373
|
+
# restart
|
374
|
+
trap("USR2") { log "USR2 signal received."; stop(true) }
|
375
|
+
|
376
|
+
log "Signals ready. TERM => stop. USR2 => restart. INT => stop (no restart)."
|
377
|
+
else
|
378
|
+
log "Signals ready. INT => stop (no restart)."
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
# Logs a simple message to STDERR (or the mongrel log if in daemon mode).
|
383
|
+
def log(msg)
|
384
|
+
STDERR.print "** ", msg, "\n"
|
385
|
+
end
|
386
|
+
|
387
|
+
end
|
388
|
+
end
|