mongrel 0.3.6 → 0.3.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. data/README +28 -1
  2. data/Rakefile +2 -2
  3. data/bin/mongrel_rails +102 -65
  4. data/bin/mongrel_rails_service +16 -7
  5. data/bin/mongrel_rails_svc +184 -87
  6. data/doc/rdoc/classes/Mongrel.html +1 -0
  7. data/doc/rdoc/classes/Mongrel/Const.html +3 -3
  8. data/doc/rdoc/classes/Mongrel/HeaderOut.html +10 -10
  9. data/doc/rdoc/classes/Mongrel/HeaderOut.src/{M000028.html → M000014.html} +4 -4
  10. data/doc/rdoc/classes/Mongrel/HeaderOut.src/{M000029.html → M000015.html} +7 -7
  11. data/doc/rdoc/classes/Mongrel/HttpParser.html +35 -35
  12. data/doc/rdoc/classes/Mongrel/HttpParser.src/{M000015.html → M000001.html} +0 -0
  13. data/doc/rdoc/classes/Mongrel/HttpParser.src/{M000016.html → M000002.html} +0 -0
  14. data/doc/rdoc/classes/Mongrel/HttpParser.src/{M000017.html → M000003.html} +0 -0
  15. data/doc/rdoc/classes/Mongrel/HttpParser.src/{M000018.html → M000004.html} +0 -0
  16. data/doc/rdoc/classes/Mongrel/HttpParser.src/{M000019.html → M000005.html} +0 -0
  17. data/doc/rdoc/classes/Mongrel/HttpParser.src/{M000020.html → M000006.html} +0 -0
  18. data/doc/rdoc/classes/Mongrel/HttpParser.src/{M000021.html → M000007.html} +0 -0
  19. data/doc/rdoc/classes/Mongrel/HttpRequest.html +5 -5
  20. data/doc/rdoc/classes/Mongrel/HttpRequest.src/{M000041.html → M000029.html} +25 -25
  21. data/doc/rdoc/classes/Mongrel/HttpResponse.html +36 -36
  22. data/doc/rdoc/classes/Mongrel/HttpResponse.src/{M000034.html → M000016.html} +7 -7
  23. data/doc/rdoc/classes/Mongrel/HttpResponse.src/{M000035.html → M000017.html} +6 -6
  24. data/doc/rdoc/classes/Mongrel/HttpResponse.src/{M000036.html → M000018.html} +5 -5
  25. data/doc/rdoc/classes/Mongrel/HttpResponse.src/{M000037.html → M000019.html} +5 -5
  26. data/doc/rdoc/classes/Mongrel/HttpResponse.src/{M000038.html → M000020.html} +6 -6
  27. data/doc/rdoc/classes/Mongrel/HttpResponse.src/{M000039.html → M000021.html} +6 -6
  28. data/doc/rdoc/classes/Mongrel/HttpResponse.src/{M000040.html → M000022.html} +6 -6
  29. data/doc/rdoc/classes/Mongrel/HttpServer.html +34 -35
  30. data/doc/rdoc/classes/Mongrel/HttpServer.src/M000008.html +36 -0
  31. data/doc/rdoc/classes/Mongrel/HttpServer.src/{M000023.html → M000009.html} +43 -43
  32. data/doc/rdoc/classes/Mongrel/HttpServer.src/{M000024.html → M000010.html} +31 -31
  33. data/doc/rdoc/classes/Mongrel/HttpServer.src/{M000025.html → M000011.html} +4 -4
  34. data/doc/rdoc/classes/Mongrel/HttpServer.src/{M000026.html → M000012.html} +4 -4
  35. data/doc/rdoc/classes/Mongrel/HttpServer.src/M000013.html +23 -0
  36. data/doc/rdoc/classes/{FactoryError.html → Mongrel/TimeoutWorker.html} +7 -34
  37. data/doc/rdoc/classes/Mongrel/URIClassifier.html +63 -20
  38. data/doc/rdoc/classes/Mongrel/URIClassifier.src/M000023.html +18 -0
  39. data/doc/rdoc/classes/Mongrel/URIClassifier.src/M000024.html +18 -0
  40. data/doc/rdoc/classes/Mongrel/URIClassifier.src/{M000030.html → M000025.html} +0 -0
  41. data/doc/rdoc/classes/Mongrel/URIClassifier.src/{M000031.html → M000026.html} +0 -0
  42. data/doc/rdoc/classes/Mongrel/URIClassifier.src/{M000032.html → M000027.html} +0 -0
  43. data/doc/rdoc/classes/Mongrel/URIClassifier.src/{M000033.html → M000028.html} +0 -0
  44. data/doc/rdoc/created.rid +1 -1
  45. data/doc/rdoc/files/README.html +35 -2
  46. data/doc/rdoc/files/lib/mongrel_rb.html +4 -1
  47. data/doc/rdoc/fr_class_index.html +1 -2
  48. data/doc/rdoc/fr_file_index.html +0 -1
  49. data/doc/rdoc/fr_method_index.html +29 -41
  50. data/lib/mongrel.rb +37 -9
  51. data/lib/mongrel/command.rb +13 -34
  52. data/lib/mongrel/plugins.rb +156 -0
  53. data/lib/mongrel/rails.rb +70 -0
  54. data/test/plugins/commands/test1.rb +19 -0
  55. data/test/test_plugins.rb +45 -0
  56. data/test/test_uriclassifier.rb +5 -1
  57. metadata +38 -49
  58. data/doc/rdoc/classes/FactoryError.src/M000001.html +0 -23
  59. data/doc/rdoc/classes/Mongrel/HttpServer.src/M000022.html +0 -33
  60. data/doc/rdoc/classes/Mongrel/HttpServer.src/M000027.html +0 -19
  61. data/doc/rdoc/classes/PluginFactory.html +0 -409
  62. data/doc/rdoc/classes/PluginFactory.src/M000002.html +0 -18
  63. data/doc/rdoc/classes/PluginFactory.src/M000003.html +0 -18
  64. data/doc/rdoc/classes/PluginFactory.src/M000004.html +0 -22
  65. data/doc/rdoc/classes/PluginFactory.src/M000005.html +0 -22
  66. data/doc/rdoc/classes/PluginFactory.src/M000006.html +0 -33
  67. data/doc/rdoc/classes/PluginFactory.src/M000007.html +0 -32
  68. data/doc/rdoc/classes/PluginFactory.src/M000008.html +0 -18
  69. data/doc/rdoc/classes/PluginFactory.src/M000009.html +0 -24
  70. data/doc/rdoc/classes/PluginFactory.src/M000010.html +0 -40
  71. data/doc/rdoc/classes/PluginFactory.src/M000011.html +0 -39
  72. data/doc/rdoc/classes/PluginFactory.src/M000012.html +0 -24
  73. data/doc/rdoc/classes/PluginFactory.src/M000013.html +0 -70
  74. data/doc/rdoc/classes/PluginFactory.src/M000014.html +0 -34
  75. data/doc/rdoc/files/lib/pluginfactory_rb.html +0 -132
  76. data/lib/pluginfactory.rb +0 -384
@@ -4,16 +4,38 @@ require 'thread'
4
4
  require 'stringio'
5
5
  require 'mongrel/cgi'
6
6
  require 'mongrel/handlers'
7
+ require 'mongrel/command'
8
+ require 'mongrel/plugins'
9
+ require 'timeout'
7
10
 
8
11
  # Mongrel module containing all of the classes (include C extensions) for running
9
12
  # a Mongrel web server. It contains a minimalist HTTP server with just enough
10
13
  # functionality to service web application requests fast as possible.
11
14
  module Mongrel
12
15
 
16
+ class URIClassifier
17
+ # Returns the URIs that have been registered with this classifier so far.
18
+ # The URIs returned should not be modified as this will cause a memory leak.
19
+ # You can use this to inspect the contents of the URIClassifier.
20
+ def uris
21
+ @handler_map.keys
22
+ end
23
+
24
+ # Simply does an inspect that looks like a Hash inspect.
25
+ def inspect
26
+ @handler_map.inspect
27
+ end
28
+ end
29
+
30
+
13
31
  # Used to stop the HttpServer via Thread.raise.
14
32
  class StopServer < Exception
15
33
  end
16
34
 
35
+ # Used to timeout worker threads that have taken too long
36
+ class TimeoutWorker < Exception
37
+ end
38
+
17
39
  # Every standard HTTP code mapped to the appropriate message. These are
18
40
  # used so frequently that they are placed directly in Mongrel for easy
19
41
  # access rather than Mongrel::Const.
@@ -118,7 +140,7 @@ module Mongrel
118
140
  SERVER_SOFTWARE='SERVER_SOFTWARE'
119
141
 
120
142
  # Current Mongrel version (used for SERVER_SOFTWARE and other response headers).
121
- MONGREL_VERSION='Mongrel 0.3.6'
143
+ MONGREL_VERSION='Mongrel 0.3.7'
122
144
 
123
145
  # The standard empty 404 response for bad requests. Use Error4040Handler for custom stuff.
124
146
  ERROR_404_RESPONSE="HTTP/1.1 404 Not Found\r\nConnection: close\r\nServer: #{MONGREL_VERSION}\r\n\r\nNOT FOUND"
@@ -320,24 +342,26 @@ module Mongrel
320
342
  # systems. If you find that you overload Mongrel too much
321
343
  # try changing it higher. If you find that responses are way too slow
322
344
  # try lowering it (after you've tuned your stuff of course).
323
- # Future versions of Mongrel will make this more dynamic (hopefully).
324
345
  def initialize(host, port, num_processors=20, timeout=120)
325
- @socket = TCPServer.new(host, port)
346
+ @socket = TCPServer.new(host, port)
326
347
 
327
348
  @classifier = URIClassifier.new
328
349
  @req_queue = Queue.new
329
350
  @host = host
330
351
  @port = port
331
352
  @processors = []
332
- @timeout = timeout
333
353
 
334
- num_processors.times {|i|
354
+ # create the worker threads
355
+ num_processors.times do |i|
335
356
  @processors << Thread.new do
336
357
  while client = @req_queue.deq
337
- process_client(client)
358
+ Timeout::timeout(timeout) do
359
+ process_client(client)
360
+ end
338
361
  end
339
362
  end
340
- }
363
+ end
364
+
341
365
  end
342
366
 
343
367
 
@@ -440,8 +464,12 @@ module Mongrel
440
464
  # Stops the acceptor thread and then causes the worker threads to finish
441
465
  # off the request queue before finally exiting.
442
466
  def stop
443
- @acceptor[:stopped] = true
444
- @acceptor.raise(StopServer.new)
467
+ stopper = Thread.new do
468
+ @acceptor[:stopped] = true
469
+ exc = StopServer.new
470
+ @acceptor.raise(exc)
471
+ end
472
+ stopper.priority = 10
445
473
  end
446
474
 
447
475
  end
@@ -1,7 +1,6 @@
1
1
  require 'singleton'
2
2
  require 'optparse'
3
- require 'pluginfactory'
4
-
3
+ require 'mongrel/plugins'
5
4
 
6
5
  module Mongrel
7
6
 
@@ -10,17 +9,10 @@ module Mongrel
10
9
 
11
10
  module Command
12
11
 
13
-
14
12
  # A Command pattern implementation used to create the set of command available to the user
15
13
  # from Mongrel. The script uses objects which implement this interface to do the
16
14
  # user's bidding.
17
- #
18
- # Creating a new command is very easy, and you can do it without modifying the source
19
- # of Mongrel thanks to PluginFactory. What you do is the following:
20
- #
21
- # 1.
22
- class Command
23
- include PluginFactory
15
+ module Base
24
16
 
25
17
  attr_reader :valid, :done_validating
26
18
 
@@ -39,11 +31,13 @@ module Mongrel
39
31
 
40
32
  # Called by the subclass to setup the command and parse the argv arguments.
41
33
  # The call is destructive on argv since it uses the OptionParser#parse! function.
42
- def initialize(argv)
34
+ def initialize(options={})
35
+ argv = options[:argv]
43
36
  @opt = OptionParser.new
44
37
  @valid = true
45
38
  # this is retarded, but it has to be done this way because -h and -v exit
46
39
  @done_validating = false
40
+ @original_args = argv.dup
47
41
 
48
42
  configure
49
43
 
@@ -61,13 +55,7 @@ module Mongrel
61
55
  end
62
56
  end
63
57
 
64
- @opt.parse! argv
65
- end
66
-
67
- # Tells the PluginFactory where to look for additional commands. By default
68
- # it's just a "plugins" directory wherever we are located.
69
- def self.derivativeDirs
70
- return ["plugins"]
58
+ @opt.parse! options[:argv]
71
59
  end
72
60
 
73
61
  # Returns true/false depending on whether the command is configured properly.
@@ -118,8 +106,6 @@ module Mongrel
118
106
  end
119
107
  end
120
108
 
121
-
122
-
123
109
  # A Singleton class that manages all of the available commands
124
110
  # and handles running them.
125
111
  class Registry
@@ -127,15 +113,9 @@ module Mongrel
127
113
 
128
114
  # Builds a list of possible commands from the Command derivates list
129
115
  def commands
130
- list = Command.derivatives()
131
- match = Regexp.new("(.*::.*)|(.*command.*)", Regexp::IGNORECASE)
132
-
133
- results = []
134
- list.keys.each do |key|
135
- results << key.to_s unless match.match(key.to_s)
136
- end
137
-
138
- return results.sort
116
+ pmgr = PluginManager.instance
117
+ list = pmgr.available["/commands"]
118
+ return list.sort
139
119
  end
140
120
 
141
121
  # Prints a list of available commands.
@@ -143,7 +123,7 @@ module Mongrel
143
123
  puts "Available commands are:\n\n"
144
124
 
145
125
  self.commands.each do |name|
146
- puts " - #{name}\n"
126
+ puts " - #{name[1 .. -1]}\n"
147
127
  end
148
128
 
149
129
  puts "\nEach command takes -h as an option to get help."
@@ -154,9 +134,8 @@ module Mongrel
154
134
  # Runs the args against the first argument as the command name.
155
135
  # If it has any errors it returns a false, otherwise it return true.
156
136
  def run(args)
157
- # find the command and change the program's name to reflect it
137
+ # find the command
158
138
  cmd_name = args.shift
159
- $0 = "#{cmd_name}"
160
139
 
161
140
  if !cmd_name or cmd_name == "?" or cmd_name == "help"
162
141
  print_command_list
@@ -165,8 +144,8 @@ module Mongrel
165
144
 
166
145
  # command exists, set it up and validate it
167
146
  begin
168
- command = Command.create(cmd_name, args)
169
- rescue FactoryError
147
+ command = PluginManager.instance.create("/commands/#{cmd_name}", :argv => args)
148
+ rescue
170
149
  STDERR.puts "INVALID COMMAND: #$!"
171
150
  print_command_list
172
151
  return
@@ -0,0 +1,156 @@
1
+ require 'singleton'
2
+
3
+ module Mongrel
4
+
5
+ # Implements the main method of managing plugins for Mongrel.
6
+ # "Plugins" in this sense are any classes which get registered
7
+ # with Mongrel for possible use when it's operating. These can
8
+ # be Handlers, Commands, or other classes. When you create a
9
+ # Plugin you register it into a URI-like namespace that makes
10
+ # it easy for you (and others) to reference it later during
11
+ # configuration.
12
+ #
13
+ # PluginManager is used as nothing more than a holder of all the
14
+ # plugins that have registered themselves. Let's say you have:
15
+ #
16
+ # class StopNow < Plugin "/commands"
17
+ # ...
18
+ # end
19
+ #
20
+ # Then you can get at this plugin with:
21
+ #
22
+ # cmd = PluginManager.create("/commands/stopnow")
23
+ #
24
+ # The funky syntax for StopNow is a weird trick borrowed from
25
+ # the Camping framework. See the Mongrel::Plugin *function* (yes,
26
+ # function). What this basically does is register it
27
+ # into the namespace for plugins at /commands. You could go
28
+ # as arbitrarily nested as you like.
29
+ #
30
+ # Why this strange almost second namespace? Why not just use
31
+ # the ObjectSpace and/or Modules? The main reason is speed and
32
+ # to avoid cluttering the Ruby namespace with what is really a
33
+ # configuration statement. This lets implementors put code
34
+ # into the Ruby structuring they need, and still have Plugins
35
+ # available to Mongrel via simple URI-like names.
36
+ #
37
+ # The alternative (as pluginfactory does it) is to troll through
38
+ # ObjectSpace looking for stuff that *might* be plugins every time
39
+ # one is needed. This alternative also means that you are stuck
40
+ # naming your commands in specific ways and putting them in specific
41
+ # modules in order to configure how Mongrel should use them.
42
+ #
43
+ # One downside to this is that you need to subclass plugin to
44
+ # make it work. In this case use mixins to add other functionality.
45
+ class PluginManager
46
+ include Singleton
47
+
48
+ def initialize
49
+ @plugins = URIClassifier.new
50
+ end
51
+
52
+ # Tell the PluginManager to scan the given path (recursively)
53
+ # and load the *.rb files found there. This is how you'd
54
+ # setup your own plugin directory.
55
+ def load(path)
56
+ Dir.chdir(path) do
57
+ Dir["**/*.rb"].each do |rbfile|
58
+ STDERR.puts "Loading plugins from #{rbfile}"
59
+ require rbfile
60
+ end
61
+ end
62
+ end
63
+
64
+ # Not necessary for you to call directly, but this is
65
+ # how Mongrel::PluginBase.inherited actually adds a
66
+ # plugin to a category.
67
+ def register(category, name, klass)
68
+ cat, ignored, map = @plugins.resolve(category)
69
+
70
+ if not cat or ignored.length > 0
71
+ map = {name => klass}
72
+ @plugins.register(category, map)
73
+ elsif not map
74
+ raise "Unknown category #{category}"
75
+ else
76
+ map[name] = klass
77
+ end
78
+ end
79
+
80
+ # Resolves the given name (should include /category/name) to
81
+ # find the plugin class and create an instance. It uses
82
+ # the same URIClassifier that the rest of Mongrel does so it
83
+ # is fast.
84
+ def create(name, options = {})
85
+ category, plugin, map = @plugins.resolve(name)
86
+
87
+ if category and plugin and plugin.length > 0
88
+ map[plugin].new(options)
89
+ else
90
+ raise "Plugin #{name} does not exist"
91
+ end
92
+ end
93
+
94
+ # Returns a map of URIs->[handlers] that you can
95
+ # use to investigate available handlers.
96
+ def available
97
+ map = {}
98
+ @plugins.uris.each do |u|
99
+ cat, name, plugins = @plugins.resolve(u)
100
+ map[cat] ||= []
101
+ map[cat] += plugins.keys
102
+ end
103
+
104
+ return map
105
+ end
106
+
107
+ end
108
+
109
+ # This base class for plugins reallys does nothing
110
+ # more than wire up the new class into the right category.
111
+ # It is not thread-safe yet but will be soon.
112
+ class PluginBase
113
+
114
+ attr_reader :options
115
+
116
+
117
+ # See Mongrel::Plugin for an explanation.
118
+ def PluginBase.inherited(klass)
119
+ name = "/" + klass.to_s.downcase
120
+ PluginManager.instance.register(@@category, name, klass)
121
+ @@category = nil
122
+ end
123
+
124
+ # See Mongrel::Plugin for an explanation.
125
+ def PluginBase.category=(category)
126
+ @@category = category
127
+ end
128
+
129
+ def initialize(options = {})
130
+ @options = options
131
+ end
132
+
133
+ end
134
+
135
+ # This nifty function works with the PluginBase to give you
136
+ # the syntax:
137
+ #
138
+ # class MyThing < Plugin "/things"
139
+ # ...
140
+ # end
141
+ #
142
+ # What it does is temporarily sets the PluginBase.category, and then
143
+ # returns PluginBase. Since the next immediate thing Ruby does is
144
+ # use this returned class to create the new class, PluginBase.inherited
145
+ # gets called. PluginBase.inherited then uses the set category, class name,
146
+ # and class to register the plugin in the right way.
147
+ def Mongrel::Plugin(c)
148
+ PluginBase.category = c
149
+ PluginBase
150
+ end
151
+
152
+ end
153
+
154
+
155
+
156
+
@@ -0,0 +1,70 @@
1
+ require 'mongrel'
2
+
3
+
4
+ # Implements a handler that can run Rails and serve files out of the
5
+ # Rails application's public directory. This lets you run your Rails
6
+ # application with Mongrel during development and testing, then use it
7
+ # also in production behind a server that's better at serving the
8
+ # static files.
9
+ #
10
+ # The RailsHandler takes a mime_map parameter which is a simple suffix=mimetype
11
+ # mapping that it should add to the list of valid mime types.
12
+ #
13
+ # It also supports page caching directly and will try to resolve a request
14
+ # in the following order:
15
+ #
16
+ # * If the requested exact PATH_INFO exists as a file then serve it.
17
+ # * If it exists at PATH_INFO+".html" exists then serve that.
18
+ # * Finally, construct a Mongrel::CGIWrapper and run Dispatcher.dispath to have Rails go.
19
+ #
20
+ # This means that if you are using page caching it will actually work with Mongrel
21
+ # and you should see a decent speed boost (but not as fast as if you use lighttpd).
22
+ class RailsHandler < Mongrel::HttpHandler
23
+ def initialize(dir, mime_map = {})
24
+ @files = Mongrel::DirHandler.new(dir,false)
25
+ @guard = Mutex.new
26
+
27
+ # register the requested mime types
28
+ mime_map.each {|k,v| Mongrel::DirHandler::add_mime_type(k,v) }
29
+ end
30
+
31
+ # Attempts to resolve the request as follows:
32
+ #
33
+ #
34
+ # * If the requested exact PATH_INFO exists as a file then serve it.
35
+ # * If it exists at PATH_INFO+".html" exists then serve that.
36
+ # * Finally, construct a Mongrel::CGIWrapper and run Dispatcher.dispath to have Rails go.
37
+ def process(request, response)
38
+ return if response.socket.closed?
39
+
40
+ path_info = request.params["PATH_INFO"]
41
+ page_cached = request.params["PATH_INFO"] + ".html"
42
+
43
+ if @files.can_serve(path_info)
44
+ # File exists as-is so serve it up
45
+ @files.process(request,response)
46
+ elsif @files.can_serve(page_cached)
47
+ # possible cached page, serve it up
48
+ request.params["PATH_INFO"] = page_cached
49
+ @files.process(request,response)
50
+ else
51
+ cgi = Mongrel::CGIWrapper.new(request, response)
52
+
53
+ begin
54
+ @guard.synchronize do
55
+ # Rails is not thread safe so must be run entirely within synchronize
56
+ Dispatcher.dispatch(cgi, ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, response.body)
57
+ end
58
+
59
+ # This finalizes the output using the proper HttpResponse way
60
+ cgi.out {""}
61
+ rescue Errno::EPIPE
62
+ # ignored
63
+ rescue Object => rails_error
64
+ STDERR.puts "Error calling Dispatcher.dispatch #{rails_error.inspect}"
65
+ STDERR.puts rails_error.backtrace.join("\n")
66
+ end
67
+ end
68
+ end
69
+
70
+ end
@@ -0,0 +1,19 @@
1
+
2
+ class First < Mongrel::Plugin "/commands"
3
+ def initialize(options = {})
4
+ puts "First with options: #{options.inspect}"
5
+ end
6
+ end
7
+
8
+ class Second < Mongrel::Plugin "/commands"
9
+ def initialize(options = {})
10
+ puts "Second with options: #{options.inspect}"
11
+ end
12
+ end
13
+
14
+ class Last < Mongrel::Plugin "/commands"
15
+ def initialize(options = {})
16
+ puts "Last with options: #{options.inspect}"
17
+ end
18
+ end
19
+