mongrel 1.2.0.pre2-x86-mswin32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. data/COPYING +55 -0
  2. data/History.txt +68 -0
  3. data/LICENSE +55 -0
  4. data/Manifest.txt +69 -0
  5. data/README.txt +80 -0
  6. data/Rakefile +8 -0
  7. data/TODO +5 -0
  8. data/bin/mongrel_rails +284 -0
  9. data/examples/builder.rb +29 -0
  10. data/examples/camping/README +3 -0
  11. data/examples/camping/blog.rb +294 -0
  12. data/examples/camping/tepee.rb +149 -0
  13. data/examples/httpd.conf +474 -0
  14. data/examples/mime.yaml +3 -0
  15. data/examples/mongrel.conf +9 -0
  16. data/examples/monitrc +57 -0
  17. data/examples/random_thrash.rb +19 -0
  18. data/examples/simpletest.rb +52 -0
  19. data/examples/webrick_compare.rb +20 -0
  20. data/ext/http11/Http11Service.java +13 -0
  21. data/ext/http11/ext_help.h +15 -0
  22. data/ext/http11/extconf.rb +6 -0
  23. data/ext/http11/http11.c +534 -0
  24. data/ext/http11/http11_parser.c +1243 -0
  25. data/ext/http11/http11_parser.h +49 -0
  26. data/ext/http11/http11_parser.java.rl +159 -0
  27. data/ext/http11/http11_parser.rl +153 -0
  28. data/ext/http11/http11_parser_common.rl +54 -0
  29. data/ext/http11/org/jruby/mongrel/Http11.java +241 -0
  30. data/ext/http11/org/jruby/mongrel/Http11Parser.java +486 -0
  31. data/lib/1.8/http11.so +0 -0
  32. data/lib/1.9/http11.so +0 -0
  33. data/lib/mongrel.rb +366 -0
  34. data/lib/mongrel/camping.rb +107 -0
  35. data/lib/mongrel/cgi.rb +181 -0
  36. data/lib/mongrel/command.rb +220 -0
  37. data/lib/mongrel/configurator.rb +388 -0
  38. data/lib/mongrel/const.rb +110 -0
  39. data/lib/mongrel/debug.rb +203 -0
  40. data/lib/mongrel/gems.rb +22 -0
  41. data/lib/mongrel/handlers.rb +468 -0
  42. data/lib/mongrel/header_out.rb +28 -0
  43. data/lib/mongrel/http_request.rb +155 -0
  44. data/lib/mongrel/http_response.rb +166 -0
  45. data/lib/mongrel/init.rb +10 -0
  46. data/lib/mongrel/mime_types.yml +616 -0
  47. data/lib/mongrel/rails.rb +185 -0
  48. data/lib/mongrel/stats.rb +89 -0
  49. data/lib/mongrel/tcphack.rb +18 -0
  50. data/lib/mongrel/uri_classifier.rb +76 -0
  51. data/setup.rb +1585 -0
  52. data/tasks/gem.rake +28 -0
  53. data/tasks/native.rake +24 -0
  54. data/tasks/ragel.rake +20 -0
  55. data/test/mime.yaml +3 -0
  56. data/test/mongrel.conf +1 -0
  57. data/test/test_cgi_wrapper.rb +26 -0
  58. data/test/test_command.rb +86 -0
  59. data/test/test_conditional.rb +107 -0
  60. data/test/test_configurator.rb +87 -0
  61. data/test/test_debug.rb +25 -0
  62. data/test/test_handlers.rb +135 -0
  63. data/test/test_http11.rb +156 -0
  64. data/test/test_redirect_handler.rb +44 -0
  65. data/test/test_request_progress.rb +99 -0
  66. data/test/test_response.rb +127 -0
  67. data/test/test_stats.rb +35 -0
  68. data/test/test_uriclassifier.rb +261 -0
  69. data/test/test_ws.rb +117 -0
  70. data/test/testhelp.rb +71 -0
  71. data/tools/trickletest.rb +45 -0
  72. metadata +197 -0
@@ -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 :head
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'] = head['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,220 @@
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
+ puts "Version #{Mongrel::Const::MONGREL_VERSION}"
65
+ end
66
+
67
+ @opt.parse! argv
68
+ end
69
+
70
+ def configure
71
+ options []
72
+ end
73
+
74
+ # Returns true/false depending on whether the command is configured properly.
75
+ def validate
76
+ return @valid
77
+ end
78
+
79
+ # Returns a help message. Defaults to OptionParser#help which should be good.
80
+ def help
81
+ @opt.help
82
+ end
83
+
84
+ # Runs the command doing it's job. You should implement this otherwise it will
85
+ # throw a NotImplementedError as a reminder.
86
+ def run
87
+ raise NotImplementedError
88
+ end
89
+
90
+
91
+ # Validates the given expression is true and prints the message if not, exiting.
92
+ def valid?(exp, message)
93
+ if not @done_validating and (not exp)
94
+ failure message
95
+ @valid = false
96
+ @done_validating = true
97
+ end
98
+ end
99
+
100
+ # Validates that a file exists and if not displays the message
101
+ def valid_exists?(file, message)
102
+ valid?(file != nil && File.exist?(file), message)
103
+ end
104
+
105
+
106
+ # Validates that the file is a file and not a directory or something else.
107
+ def valid_file?(file, message)
108
+ valid?(file != nil && File.file?(file), message)
109
+ end
110
+
111
+ # Validates that the given directory exists
112
+ def valid_dir?(file, message)
113
+ valid?(file != nil && File.directory?(file), message)
114
+ end
115
+
116
+ def valid_user?(user)
117
+ valid?(@group, "You must also specify a group.")
118
+ begin
119
+ Etc.getpwnam(user)
120
+ rescue
121
+ failure "User does not exist: #{user}"
122
+ @valid = false
123
+ end
124
+ end
125
+
126
+ def valid_group?(group)
127
+ valid?(@user, "You must also specify a user.")
128
+ begin
129
+ Etc.getgrnam(group)
130
+ rescue
131
+ failure "Group does not exist: #{group}"
132
+ @valid = false
133
+ end
134
+ end
135
+
136
+ # Just a simple method to display failure until something better is developed.
137
+ def failure(message)
138
+ STDERR.puts "!!! #{message}"
139
+ end
140
+ end
141
+
142
+ # A Singleton class that manages all of the available commands
143
+ # and handles running them.
144
+ class Registry
145
+ include Singleton
146
+
147
+ # Builds a list of possible commands from the Command derivates list
148
+ def commands
149
+ pmgr = GemPlugin::Manager.instance
150
+ list = pmgr.plugins["/commands"].keys
151
+ return list.sort
152
+ end
153
+
154
+ # Prints a list of available commands.
155
+ def print_command_list
156
+ puts "#{Mongrel::Command::BANNER}\nAvailable commands are:\n\n"
157
+
158
+ self.commands.each do |name|
159
+ if /mongrel::/ =~ name
160
+ name = name[9 .. -1]
161
+ end
162
+
163
+ puts " - #{name[1 .. -1]}\n"
164
+ end
165
+
166
+ puts "\nEach command takes -h as an option to get help."
167
+
168
+ end
169
+
170
+
171
+ # Runs the args against the first argument as the command name.
172
+ # If it has any errors it returns a false, otherwise it return true.
173
+ def run(args)
174
+ # find the command
175
+ cmd_name = args.shift
176
+
177
+ if !cmd_name or cmd_name == "?" or cmd_name == "help"
178
+ print_command_list
179
+ return true
180
+ elsif cmd_name == "--version"
181
+ puts "Mongrel Web Server #{Mongrel::Const::MONGREL_VERSION}"
182
+ return true
183
+ end
184
+
185
+ begin
186
+ # quick hack so that existing commands will keep working but the Mongrel:: ones can be moved
187
+ if ["start", "stop", "restart"].include? cmd_name
188
+ cmd_name = "mongrel::" + cmd_name
189
+ end
190
+
191
+ command = GemPlugin::Manager.instance.create("/commands/#{cmd_name}", :argv => args)
192
+ rescue OptionParser::InvalidOption
193
+ STDERR.puts "#$! for command '#{cmd_name}'"
194
+ STDERR.puts "Try #{cmd_name} -h to get help."
195
+ return false
196
+ rescue
197
+ STDERR.puts "ERROR RUNNING '#{cmd_name}': #$!"
198
+ STDERR.puts "Use help command to get help"
199
+ return false
200
+ end
201
+
202
+ # Normally the command is NOT valid right after being created
203
+ # but sometimes (like with -h or -v) there's no further processing
204
+ # needed so the command is already valid so we can skip it.
205
+ if not command.done_validating
206
+ if not command.validate
207
+ STDERR.puts "#{cmd_name} reported an error. Use mongrel_rails #{cmd_name} -h to get help."
208
+ return false
209
+ else
210
+ command.run
211
+ end
212
+ end
213
+
214
+ return true
215
+ end
216
+
217
+ end
218
+ end
219
+ end
220
+
@@ -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 !~ /mingw|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 !~ /mingw|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}.binding) }
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 !~ /mingw|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