mongrel 0.3.6 → 0.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+