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
data/README CHANGED
@@ -14,7 +14,8 @@ scream without too many portability issues.
14
14
  The 0.3.6 release supports Ruby On Rails much better than previously, and also
15
15
  sports the beginning of a command and plugin infrastructure. There is now a more
16
16
  complete CGIWrapper that handles most of the CGI usage, but still doesn't do the
17
- MIME decoding or file upload/send (it leaves that to CGI).
17
+ MIME decoding or file upload/send (it leaves that to CGI). Finally, there's a
18
+ great mongrel_rails_service script for running under Win32 as a service.
18
19
 
19
20
  After you've installed (either with gem install mongrel or via source) you should
20
21
  have the mongrel_rails command available in your PATH. Then you just do the following:
@@ -42,6 +43,32 @@ There are also many more new options for configuring the rails runner including
42
43
  changing to a different directory, adding more MIME types, and setting processor
43
44
  threads and timeouts.
44
45
 
46
+
47
+ === Win32 Service Support
48
+
49
+ Mongrel now has support for running as a Win32 service right out of the
50
+ box. The support is still rough but works well enough that we decided
51
+ to release it. You can thank Luis Lavena for working on this and making
52
+ it so nice.
53
+
54
+ After you do the gem install, find a Rails application you want to run
55
+ and do:
56
+
57
+ $ mongrel_rails_service install -n myapp \
58
+ -r c:\my\path\to\myapp -p 4000 -e production
59
+ $ mongrel_rails_service start -n myapp
60
+
61
+ Now hit the port and poof, works. *Stopping the service is a little problematic right now.*
62
+
63
+ If you run into an app that's not running right, my suggestion is to run it with
64
+ the regular mongrel_rails runner:
65
+
66
+ $ cd c:\my\path\to\myapp
67
+ $ mongrel_rails start -p 4500
68
+
69
+ Since that will spit out error messages and stuff to the console. *Use CTRL-Pause/Break to stop.*
70
+
71
+
45
72
  == Install
46
73
 
47
74
  It doesn't explicitly require Camping, but if you want to run the examples/camping/
data/Rakefile CHANGED
@@ -30,7 +30,7 @@ end
30
30
 
31
31
  setup_extension("http11", "http11")
32
32
 
33
- version="0.3.6"
33
+ version="0.3.7"
34
34
  summary = "A small fast HTTP library and server that runs Rails, Camping, and Nitro apps."
35
35
  test_file = "test/test_ws.rb"
36
36
  author="Zed A. Shaw"
@@ -44,7 +44,7 @@ end
44
44
  desc "Build a binary gem for Win32"
45
45
  task :win32_gem => [:clean, :compile, :test, :rerdoc, :package_win32]
46
46
 
47
- scripts_win32 = scripts + ['mongrel_rails_service','mongrel_rails_svc']
47
+ scripts_win32 = scripts + ['mongrel_rails_service']
48
48
  task :package_win32 do
49
49
  setup_win32_gem(name, version, version, summary, scripts_win32, test_file) do |spec|
50
50
  spec.add_dependency('win32-service', '>= 0.5.0')
@@ -1,61 +1,24 @@
1
1
  require 'rubygems'
2
- require 'mongrel'
3
- require 'mongrel/command'
2
+ require 'mongrel/rails'
4
3
 
5
4
 
6
- class RailsHandler < Mongrel::HttpHandler
7
-
8
- def initialize(dir, mime_map = {})
9
- @files = Mongrel::DirHandler.new(dir,false)
10
- @guard = Mutex.new
11
-
12
- # register the requested mime types
13
- mime_map.each {|k,v| Mongrel::DirHandler::add_mime_type(k,v) }
14
- end
15
-
16
- def process(request, response)
17
- # not static, need to talk to rails
18
- return if response.socket.closed?
19
-
20
- if @files.can_serve(request.params["PATH_INFO"])
21
- @files.process(request,response)
22
- else
23
- cgi = Mongrel::CGIWrapper.new(request, response)
24
-
25
- begin
26
- @guard.synchronize do
27
- # Rails is not thread safe so must be run entirely within synchronize
28
- Dispatcher.dispatch(cgi, ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, response.body)
29
- end
30
-
31
- # This finalizes the output using the proper HttpResponse way
32
- cgi.out {""}
33
- rescue Object => rails_error
34
- STDERR.puts "calling Dispatcher.dispatch #{rails_error}"
35
- STDERR.puts rails_error.backtrace.join("\n")
36
- end
37
- end
38
-
39
- end
40
- end
41
-
42
-
43
-
44
- class StartCommand < Mongrel::Command::Command
5
+ class Start < Mongrel::Plugin "/commands"
6
+ include Mongrel::Command::Base
45
7
 
46
8
  def configure
47
9
  options [
48
- ["-e", "--environment ENV", "Rails environment to run as", :@environment, ENV['RAILS_ENV'] || "development"],
49
- ["-d", "--daemonize", "Whether to run in the background or not", :@daemon, false],
50
- ['-p', '--port PORT', "Which port to bind to", :@port, 3000],
51
- ['-a', '--address ADDR', "Address to bind to", :@address, "0.0.0.0"],
52
- ['-l', '--log FILE', "Where to write log messages", :@log_file, "log/mongrel.log"],
53
- ['-P', '--pid FILE', "Where to write the PID", :@pid_file, "log/mongrel.pid"],
54
- ['-n', '--num-procs INT', "Number of processor threads to use", :@num_procs, 20],
55
- ['-t', '--timeout SECONDS', "Timeout all requests after SECONDS time", :@timeout, 120],
56
- ['-m', '--mime PATH', "A YAML file that lists additional MIME types", :@mime_map, nil],
57
- ['-c', '--chdir PATH', "Change to dir before starting (will be expanded)", :@cwd, Dir.pwd],
58
- ['-r', '--root PATH', "Set the document root (default 'public')", :@docroot, "public"],
10
+ ["-e", "--environment ENV", "Rails environment to run as", :@environment, ENV['RAILS_ENV'] || "development"],
11
+ ["-d", "--daemonize", "Whether to run in the background or not", :@daemon, false],
12
+ ['-p', '--port PORT', "Which port to bind to", :@port, 3000],
13
+ ['-a', '--address ADDR', "Address to bind to", :@address, "0.0.0.0"],
14
+ ['-l', '--log FILE', "Where to write log messages", :@log_file, "log/mongrel.log"],
15
+ ['-P', '--pid FILE', "Where to write the PID", :@pid_file, "log/mongrel.pid"],
16
+ ['-n', '--num-procs INT', "Number of processor threads to use", :@num_procs, 20],
17
+ ['-t', '--timeout SECONDS', "Timeout all requests after SECONDS time", :@timeout, 120],
18
+ ['-m', '--mime PATH', "A YAML file that lists additional MIME types", :@mime_map, nil],
19
+ ['-c', '--chdir PATH', "Change to dir before starting (will be expanded)", :@cwd, Dir.pwd],
20
+ ['-r', '--root PATH', "Set the document root (default 'public')", :@docroot, "public"],
21
+ ['-L', '--load PATH', "Loads plugins from the given directory", :@load_from, nil],
59
22
  ]
60
23
  end
61
24
 
@@ -71,9 +34,12 @@ class StartCommand < Mongrel::Command::Command
71
34
  valid_dir? @docroot, "Path to docroot not valid: #@docroot"
72
35
  valid_exists? @mime_map, "MIME mapping file does not exist: #@mime_map" if @mime_map
73
36
 
37
+ valid_dir? @load_from, "Plugin directory path does not exist" if @load_from
38
+
74
39
  return @valid
75
40
  end
76
41
 
42
+
77
43
  def daemonize
78
44
  # save this for later since daemonize will hose it
79
45
  if @daemon and RUBY_PLATFORM !~ /mswin/
@@ -117,22 +83,47 @@ class StartCommand < Mongrel::Command::Command
117
83
  end
118
84
 
119
85
  def start_mongrel(rails)
120
- # start up mongrel with the right configurations
86
+ @restart = false
87
+
121
88
  server = Mongrel::HttpServer.new(@address, @port, @num_procs.to_i, @timeout.to_i)
122
89
  server.register("/", rails)
123
90
  server.run
124
- trap("INT") { server.stop }
91
+
92
+ # signal trapping just applies to posix systems
93
+ # TERM is a valid signal, but still doesn't gracefuly shutdown on win32.
94
+ if RUBY_PLATFORM !~ /mswin/
95
+ # graceful shutdown
96
+ trap("TERM") {
97
+ server.stop
98
+ }
99
+
100
+ # rails reload
101
+ trap("HUP") {
102
+ server.stop
103
+ @restart = true
104
+ }
105
+
106
+ # restart
107
+ trap("USR2") {
108
+ server.stop
109
+ @restart = true
110
+ }
111
+ end
125
112
 
126
113
  begin
127
- puts "Server ready."
114
+ STDERR.puts "Server ready."
128
115
  server.acceptor.join
129
116
  rescue Interrupt
130
- puts "Interrupted."
117
+ STDERR.puts "Interrupted."
131
118
  raise
132
119
  end
120
+
121
+ # daemonize makes restart easy
122
+ run if @restart
133
123
  end
134
124
 
135
125
  def run
126
+ Mongrel::PluginManager.instance.load(@load_from) if @load_from
136
127
  daemonize
137
128
  rails = configure_rails
138
129
  start_mongrel(rails)
@@ -140,12 +131,26 @@ class StartCommand < Mongrel::Command::Command
140
131
  end
141
132
 
142
133
 
134
+ def send_signal(signal, pid_file)
135
+ pid = open(pid_file).read.to_i
136
+ print "Sending #{signal} to Mongrel at PID #{pid}..."
137
+ begin
138
+ Process.kill(signal, pid)
139
+ rescue Errno::ESRCH
140
+ puts "Process does not exist. Not running."
141
+ end
142
+
143
+ puts "Done."
144
+ end
145
+
143
146
 
144
- class StopCommand < Mongrel::Command::Command
147
+ class Stop < Mongrel::Plugin "/commands"
148
+ include Mongrel::Command::Base
145
149
 
146
150
  def configure
147
- options [
151
+ options [
148
152
  ['-c', '--chdir PATH', "Change to dir before starting (will be expanded)", :@cwd, Dir.pwd],
153
+ ['-f', '--force', "Force the shutdown.", :@force, false],
149
154
  ['-P', '--pid FILE', "Where to write the PID", :@pid_file, "log/mongrel.pid"]
150
155
  ]
151
156
  end
@@ -162,16 +167,48 @@ class StopCommand < Mongrel::Command::Command
162
167
 
163
168
 
164
169
  def run
165
- pid = open(@pid_file).read.to_i
166
- print "Stopping Mongrel at PID #{pid}..."
167
- begin
168
- Process.kill("INT", pid)
169
- rescue Errno::ESRCH
170
- puts "Process does not exist. Not running."
170
+ if @force
171
+ send_signal("KILL", @pid_file)
172
+ else
173
+ send_signal("TERM", @pid_file)
174
+ end
175
+
176
+ File.unlink(@pid_file)
177
+ end
178
+ end
179
+
180
+
181
+
182
+ class Restart < Mongrel::Plugin "/commands"
183
+ include Mongrel::Command::Base
184
+
185
+ def configure
186
+ options [
187
+ ['-c', '--chdir PATH', "Change to dir before starting (will be expanded)", :@cwd, Dir.pwd],
188
+ ['-s', '--soft', "Do a soft restart rather than a process exit restart", :@soft, false],
189
+ ['-P', '--pid FILE', "Where to write the PID", :@pid_file, "log/mongrel.pid"]
190
+ ]
191
+ end
192
+
193
+ def validate
194
+ @cwd = File.expand_path(@cwd)
195
+ valid_dir? @cwd, "Invalid path to change to during daemon mode: #@cwd"
196
+
197
+ @pid_file = File.join(@cwd,@pid_file)
198
+ valid_exists? @pid_file, "PID file #@pid_file does not exist. Not running?"
199
+
200
+ return @valid
201
+ end
202
+
203
+
204
+ def run
205
+ if @soft
206
+ send_signal("HUP", @pid_file)
207
+ else
208
+ send_signal("USR2", @pid_file)
171
209
  end
172
210
 
173
211
  File.unlink(@pid_file)
174
- puts "Done."
175
212
  end
176
213
  end
177
214
 
@@ -7,8 +7,6 @@
7
7
  ###############################################
8
8
  require 'rubygems'
9
9
  require 'mongrel'
10
- require 'mongrel/command'
11
-
12
10
  require 'win32/service'
13
11
  include Win32
14
12
 
@@ -32,7 +30,8 @@ module GenericCommand
32
30
  end
33
31
  end
34
32
 
35
- class InstallCommand < Mongrel::Command::Command
33
+ class Install < Mongrel::Plugin "/commands"
34
+ include Mongrel::Command::Base
36
35
 
37
36
  # Default every option to nil so only the defined ones get passed to service
38
37
  # (which will override ServiceCommand defaults).
@@ -41,12 +40,13 @@ class InstallCommand < Mongrel::Command::Command
41
40
  ['-n', '--name SVC_NAME', "Required name for the service to be registered/installed.", :@svc_name, nil],
42
41
  ['-d', '--display SVC_DISPLAY', "Adjust the display name of the service.", :@svc_display, nil],
43
42
  ['-r', '--root PATH', "Set the root path where your rails app resides.", :@rails_root, Dir.pwd],
44
- ['-e', '--environment ENV', "Rails environment to run as", :@environment, 'production'],
43
+ ['-e', '--environment ENV', "Rails environment to run as. (default: production)", :@environment, 'production'],
45
44
  ['-b', '--binding ADDR', "Address to bind to", :@ip, nil],
46
45
  ['-p', '--port PORT', "Which port to bind to", :@port, 3000],
47
46
  ['-m', '--mime PATH', "A YAML file that lists additional MIME types", :@mime_map, nil],
48
47
  ['-P', '--num-procs INT', "Number of processor threads to use", :@num_procs, nil],
49
48
  ['-t', '--timeout SECONDS', "Timeout all requests after SECONDS time", :@timeout, nil],
49
+ ['-c', '--cpu CPU', "Bind the process to specific cpu, starting from 1.", :@cpu, nil]
50
50
  ]
51
51
  end
52
52
 
@@ -70,6 +70,9 @@ class InstallCommand < Mongrel::Command::Command
70
70
  valid? app_exist == true, "The root of rails app isn't valid, please verify."
71
71
  valid_exists? @mime_map, "MIME mapping file does not exist: #@mime_map" if @mime_map
72
72
 
73
+ # Validate the number of cpu to bind to.
74
+ valid? @cpu.to_i > 0, "You must specify a numeric value for cpu. (1..8)" if @cpu
75
+
73
76
  # We should validate service existance here, right Zed?
74
77
  begin
75
78
  valid? !Service.exists?(@svc_name), "The service already exist, please uninstall it first."
@@ -112,6 +115,9 @@ class InstallCommand < Mongrel::Command::Command
112
115
 
113
116
  # timeout
114
117
  @params << "-t #{@timeout.to_i} " if @timeout
118
+
119
+ # cpu
120
+ @params << "-c #{@cpu.to_i} " if @cpu
115
121
  end
116
122
 
117
123
  def install_service
@@ -168,7 +174,8 @@ class InstallCommand < Mongrel::Command::Command
168
174
  end
169
175
  end
170
176
 
171
- class DeleteCommand < Mongrel::Command::Command
177
+ class Delete < Mongrel::Plugin "/commands"
178
+ include Mongrel::Command::Base
172
179
  include GenericCommand
173
180
 
174
181
  def run
@@ -186,7 +193,8 @@ class DeleteCommand < Mongrel::Command::Command
186
193
  end
187
194
  end
188
195
 
189
- class StartCommand < Mongrel::Command::Command
196
+ class Start < Mongrel::Plugin "/commands"
197
+ include Mongrel::Command::Base
190
198
  include GenericCommand
191
199
 
192
200
  def run
@@ -210,7 +218,8 @@ class StartCommand < Mongrel::Command::Command
210
218
  end
211
219
  end
212
220
 
213
- class StopCommand < Mongrel::Command::Command
221
+ class Stop < Mongrel::Plugin "/commands"
222
+ include Mongrel::Command::Base
214
223
  include GenericCommand
215
224
 
216
225
  def run
@@ -4,75 +4,57 @@
4
4
  # This is where Win32::Daemon resides.
5
5
  ###############################################
6
6
  require 'rubygems'
7
- require 'mongrel'
8
-
7
+ require 'mongrel/rails'
9
8
  require 'optparse'
10
-
11
9
  require 'win32/service'
12
10
 
13
- DEBUG_LOG_FILE = File.expand_path(File.dirname(__FILE__) + '/debug.log')
14
- #STDERR.reopen(DEBUG_LOG_FILE)
15
-
16
- # There are need for SimpleHandler
17
- require 'yaml'
18
- require 'zlib'
19
-
20
- class RailsHandler < Mongrel::HttpHandler
21
-
22
- def initialize(dir, mime_map = {})
23
- @files = Mongrel::DirHandler.new(dir,false)
24
- @guard = Mutex.new
25
-
26
- # register the requested mime types
27
- mime_map.each {|k,v| Mongrel::DirHandler::add_mime_type(k,v) }
11
+ # We need to use OpenProcess and SetProcessAffinityMask on WinNT/2K/XP for
12
+ # binding the process to each cpu.
13
+ # Kernel32 Module Just for Win32 :D
14
+ require 'dl/win32'
15
+
16
+ module Kernel32
17
+ [
18
+ %w/OpenProcess LLL L/,
19
+ %w/SetProcessAffinityMask LL L/,
20
+ ].each do |fn|
21
+ const_set fn[0].intern, Win32API.new('kernel32.dll', *fn)
28
22
  end
29
23
 
30
- def process(request, response)
31
- # not static, need to talk to rails
32
- return if response.socket.closed?
33
-
34
- if @files.can_serve(request.params["PATH_INFO"])
35
- @files.process(request,response)
36
- else
37
- cgi = Mongrel::CGIWrapper.new(request, response)
38
-
39
- begin
40
- @guard.synchronize do
41
- # Rails is not thread safe so must be run entirely within synchronize
42
- Dispatcher.dispatch(cgi, ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, response.body)
43
- end
44
-
45
- # This finalizes the output using the proper HttpResponse way
46
- cgi.out {""}
47
- rescue Object => rails_error
48
- STDERR.puts "calling Dispatcher.dispatch #{rails_error}"
49
- STDERR.puts rails_error.backtrace.join("\n")
50
- end
51
- end
52
- end
24
+ PROCESS_ALL_ACCESS = 0x1f0fff
25
+
26
+ module_function
53
27
 
28
+ def set_affinity(pid, cpu)
29
+ handle = OpenProcess.call(PROCESS_ALL_ACCESS, 0, pid)
30
+
31
+ # CPU mask is a bit weird, hehehe :)
32
+ # default mask for CPU 1
33
+ mask = 1
34
+ mask = %w{1 2 4 8 16 32 64 128}[cpu.to_i - 1] if cpu.to_i.between?(1, 8)
35
+
36
+ SetProcessAffinityMask.call(handle, mask.to_i)
37
+ end
54
38
  end
39
+ # End Kernel32 Module
55
40
 
56
- class SimpleHandler < Mongrel::HttpHandler
57
- def process(request, response)
58
- response.start do |head,out|
59
- head["Content-Type"] = "text/html"
60
- results = "<html><body>Your request:<br /><pre>#{request.params.to_yaml}</pre><a href=\"/files\">View the files.</a></body></html>"
61
- if request.params["HTTP_ACCEPT_ENCODING"] == "gzip,deflate"
62
- head["Content-Encoding"] = "deflate"
63
- # send it back deflated
64
- out << Zlib::Deflate.deflate(results)
65
- else
66
- # no gzip supported, send it back normal
67
- out << results
68
- end
69
- end
70
- end
41
+ DEBUG_LOG_FILE = File.expand_path(File.dirname(__FILE__) + '/debug.log')
42
+ DEBUG_THREAD_LOG_FILE = File.expand_path(File.dirname(__FILE__) + '/debug_thread.log')
43
+
44
+ def dbg(msg)
45
+ File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - #{msg}") }
46
+ end
47
+
48
+ def dbg_th(msg)
49
+ File.open(DEBUG_THREAD_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - #{msg}") }
71
50
  end
72
51
 
73
- class RailsDaemon < Win32::Daemon
52
+ # This class encapsulate the handler registering, http_server and working thread
53
+ # Is standalone, so using MongrelRails in your app get everything runnig
54
+ # (in case you don't want use mongrel_rails script)
55
+ class MongrelRails
74
56
  def initialize(ip, port, rails_root, docroot, environment, mime_map, num_procs, timeout)
75
- File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - daemon_initialize entered") }
57
+ dbg "mongrelrails_initialize entered"
76
58
 
77
59
  @ip = ip
78
60
  @port = port
@@ -83,11 +65,23 @@ class RailsDaemon < Win32::Daemon
83
65
  @num_procs = num_procs
84
66
  @timeout = timeout
85
67
 
86
- File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - daemon_initialize left") }
68
+ dbg "mongrelrails_initialize left"
87
69
  end
70
+
71
+ def delayed_initialize
72
+ dbg "delayed_initialize entered"
73
+
74
+ @rails = configure_rails
75
+
76
+ # start up mongrel with the right configurations
77
+ @server = Mongrel::HttpServer.new(@ip, @port, @num_procs.to_i, @timeout.to_i)
78
+ @server.register("/", @rails)
88
79
 
80
+ dbg "delayed_initialize left"
81
+ end
82
+
89
83
  def load_mime_map
90
- File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - load_mime_map entered") }
84
+ dbg "load_mime_map entered"
91
85
 
92
86
  mime = {}
93
87
 
@@ -100,13 +94,13 @@ class RailsDaemon < Win32::Daemon
100
94
  mime.each {|k,v| puts "WARNING: MIME type #{k} must start with '.'" if k.index(".") != 0 }
101
95
  end
102
96
 
103
- File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - load_mime_map left") }
97
+ dbg "load_mime_map left"
104
98
 
105
99
  return mime
106
100
  end
107
101
 
108
102
  def configure_rails
109
- File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - configure_rails entered") }
103
+ dbg "configure_rails entered"
110
104
 
111
105
  Dir.chdir(@rails_root)
112
106
 
@@ -116,45 +110,85 @@ class RailsDaemon < Win32::Daemon
116
110
  # configure the rails handler
117
111
  rails = RailsHandler.new(@docroot, load_mime_map)
118
112
 
119
- File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - configure_rails left") }
113
+ dbg "configure_rails left"
120
114
 
121
115
  return rails
122
116
  end
123
117
 
124
- def service_init
125
- File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - service_init entered") }
118
+ def start_serve
119
+ dbg "start_serve entered"
126
120
 
127
- @rails = configure_rails
128
- #@rails = SimpleHandler.new
121
+ @runner = Thread.new do
122
+ dbg_th "runner_thread suspended"
123
+ Thread.stop
124
+
125
+ dbg_th "runner_thread resumed"
126
+ dbg_th "runner_thread acceptor.join"
127
+ @server.acceptor.join
128
+ end
129
+
130
+ dbg "server.run"
131
+ @server.run
132
+
133
+ dbg "runner.run"
134
+ @runner.run
129
135
 
130
- # start up mongrel with the right configurations
131
- @server = Mongrel::HttpServer.new(@ip, @port, @num_procs.to_i, @timeout.to_i)
132
- @server.register("/", @rails)
136
+ dbg "start_serve left"
137
+ end
138
+
139
+ def stop_serve
140
+ dbg "stop_serve entered"
141
+
142
+ if @runner.alive?
143
+ dbg "killing thread"
144
+ @runner.kill
145
+ end
133
146
 
134
- File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - service_init left") }
147
+ @server.stop
148
+
149
+ dbg "stop_serve left"
150
+ end
151
+ end
152
+
153
+ class RailsDaemon < Win32::Daemon
154
+ def initialize(rails)
155
+ dbg "daemon_initialize entered"
156
+
157
+ @rails = rails
158
+
159
+ dbg "daemon_initialize left"
160
+ end
161
+
162
+ def service_init
163
+ dbg "service_init entered"
164
+
165
+ @rails.delayed_initialize
166
+
167
+ dbg "service_init left"
135
168
  end
136
169
 
137
170
  def service_main
138
- File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - service_main entered") }
171
+ dbg "service_main entered"
139
172
 
140
- File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - server.run") }
141
- @server.run
173
+ dbg "rails.start_serve"
174
+ @rails.start_serve
142
175
 
143
- File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - while RUNNING") }
176
+ dbg "while RUNNING"
144
177
  while state == RUNNING
145
178
  sleep 1
146
179
  end
180
+ dbg "state !RUNNING"
147
181
 
148
- File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - service_main left") }
182
+ dbg "rails.stop_serve"
183
+ @rails.stop_serve
184
+
185
+ dbg "service_main left"
149
186
  end
150
187
 
151
188
  def service_stop
152
- File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - service_stop entered") }
153
-
154
- #File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - server.stop") }
155
- #@server.stop
156
-
157
- File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - service_stop left") }
189
+ dbg "service_stop entered"
190
+
191
+ dbg "service_stop left"
158
192
  end
159
193
  end
160
194
 
@@ -170,25 +204,88 @@ if ARGV[0] == 'service'
170
204
  :port => 3000,
171
205
  :mime_map => nil,
172
206
  :num_procs => 20,
173
- :timeout => 120
207
+ :timeout => 120,
208
+ :cpu => nil
174
209
  }
175
210
 
176
211
  ARGV.options do |opts|
177
212
  opts.on('-r', '--root PATH', "Set the root path where your rails app resides.") { |OPTIONS[:rails_root]| }
178
- opts.on('-e', '--environment ENV', "Rails environment to run as.") { |OPTIONS[:environment]| }
213
+ opts.on('-e', '--environment ENV', "Rails environment to run as. (default: production)") { |OPTIONS[:environment]| }
179
214
  opts.on('-b', '--binding ADDR', "Address to bind to") { |OPTIONS[:ip]| }
180
215
  opts.on('-p', '--port PORT', "Which port to bind to") { |OPTIONS[:port]| }
181
216
  opts.on('-m', '--mime PATH', "A YAML file that lists additional MIME types") { |OPTIONS[:mime_map]| }
182
217
  opts.on('-P', '--num-procs INT', "Number of processor threads to use") { |OPTIONS[:num_procs]| }
183
218
  opts.on('-t', '--timeout SECONDS', "Timeout all requests after SECONDS time") { |OPTIONS[:timeout]| }
184
-
219
+ opts.on('-c', '--cpu CPU', "Bind the process to specific cpu") { |OPTIONS[:cpu]| }
220
+
185
221
  opts.parse!
186
222
  end
187
223
 
224
+ #expand RAILS_ROOT
225
+ OPTIONS[:rails_root] = File.expand_path(OPTIONS[:rails_root])
226
+
188
227
  OPTIONS[:docroot] = File.expand_path(OPTIONS[:rails_root] + '/public')
189
228
 
190
- rails_svc = RailsDaemon.new(OPTIONS[:ip], OPTIONS[:port], OPTIONS[:rails_root], OPTIONS[:docroot], OPTIONS[:environment], OPTIONS[:mime_map], OPTIONS[:num_procs].to_i, OPTIONS[:timeout].to_i)
229
+ # We must bind to a specific cpu?
230
+ if OPTIONS[:cpu]
231
+ Kernel32.set_affinity(Process.pid, OPTIONS[:cpu])
232
+ end
233
+
234
+ rails = MongrelRails.new(OPTIONS[:ip], OPTIONS[:port], OPTIONS[:rails_root], OPTIONS[:docroot], OPTIONS[:environment], OPTIONS[:mime_map], OPTIONS[:num_procs].to_i, OPTIONS[:timeout].to_i)
235
+ rails_svc = RailsDaemon.new(rails)
191
236
  rails_svc.mainloop
192
237
 
193
- end
238
+ elsif ARGV[0] == 'debug'
239
+ ARGV.shift
194
240
 
241
+ # default options
242
+ OPTIONS = {
243
+ :rails_root => Dir.pwd,
244
+ :environment => 'production',
245
+ :ip => '0.0.0.0',
246
+ :port => 3000,
247
+ :mime_map => nil,
248
+ :num_procs => 20,
249
+ :timeout => 120,
250
+ :cpu => nil
251
+ }
252
+
253
+ ARGV.options do |opts|
254
+ opts.on('-r', '--root PATH', "Set the root path where your rails app resides.") { |OPTIONS[:rails_root]| }
255
+ opts.on('-e', '--environment ENV', "Rails environment to run as.") { |OPTIONS[:environment]| }
256
+ opts.on('-b', '--binding ADDR', "Address to bind to") { |OPTIONS[:ip]| }
257
+ opts.on('-p', '--port PORT', "Which port to bind to") { |OPTIONS[:port]| }
258
+ opts.on('-m', '--mime PATH', "A YAML file that lists additional MIME types") { |OPTIONS[:mime_map]| }
259
+ opts.on('-P', '--num-procs INT', "Number of processor threads to use") { |OPTIONS[:num_procs]| }
260
+ opts.on('-t', '--timeout SECONDS', "Timeout all requests after SECONDS time") { |OPTIONS[:timeout]| }
261
+ opts.on('-c', '--cpu CPU', "Bind the process to specific cpu") { |OPTIONS[:cpu]| }
262
+
263
+ opts.parse!
264
+ end
265
+
266
+ #expand RAILS_ROOT
267
+ OPTIONS[:rails_root] = File.expand_path(OPTIONS[:rails_root])
268
+
269
+ OPTIONS[:docroot] = File.expand_path(OPTIONS[:rails_root] + '/public')
270
+
271
+ # We must bind to a specific cpu?
272
+ if OPTIONS[:cpu]
273
+ Kernel32.set_affinity(Process.pid, OPTIONS[:cpu])
274
+ end
275
+
276
+ rails = MongrelRails.new(OPTIONS[:ip], OPTIONS[:port], OPTIONS[:rails_root], OPTIONS[:docroot], OPTIONS[:environment], OPTIONS[:mime_map], OPTIONS[:num_procs].to_i, OPTIONS[:timeout].to_i)
277
+ rails.delayed_initialize
278
+ rails.start_serve
279
+
280
+ begin
281
+ sleep
282
+ rescue Interrupt
283
+ puts "graceful shutdown?"
284
+ end
285
+
286
+ begin
287
+ rails.stop_serve
288
+ rescue
289
+ end
290
+
291
+ end