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
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