nitro 0.23.0 → 0.24.0
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.
- data/CHANGELOG +350 -0
- data/INSTALL +2 -2
- data/ProjectInfo +61 -0
- data/README +5 -4
- data/Rakefile +5 -4
- data/bin/nitrogen +3 -1
- data/doc/AUTHORS +27 -3
- data/doc/RELEASES +193 -0
- data/doc/lhttpd.txt +4 -0
- data/lib/nitro.rb +1 -1
- data/lib/nitro/adapter/cgi.rb +6 -321
- data/lib/nitro/adapter/fastcgi.rb +2 -14
- data/lib/nitro/adapter/scgi.rb +237 -71
- data/lib/nitro/adapter/webrick.rb +25 -7
- data/lib/nitro/caching.rb +1 -0
- data/lib/nitro/cgi.rb +296 -0
- data/lib/nitro/{cookie.rb → cgi/cookie.rb} +0 -0
- data/lib/nitro/cgi/http.rb +62 -0
- data/lib/nitro/{request.rb → cgi/request.rb} +4 -1
- data/lib/nitro/{response.rb → cgi/response.rb} +0 -0
- data/lib/nitro/cgi/stream.rb +43 -0
- data/lib/nitro/cgi/utils.rb +38 -0
- data/lib/nitro/compiler.rb +23 -11
- data/lib/nitro/compiler/css.rb +8 -0
- data/lib/nitro/compiler/morphing.rb +66 -0
- data/lib/nitro/context.rb +21 -30
- data/lib/nitro/controller.rb +23 -100
- data/lib/nitro/dispatcher.rb +18 -8
- data/lib/nitro/element.rb +6 -2
- data/lib/nitro/flash.rb +2 -2
- data/lib/nitro/mixin/buffer.rb +2 -2
- data/lib/nitro/mixin/form.rb +204 -93
- data/lib/nitro/mixin/javascript.rb +170 -11
- data/lib/nitro/mixin/markup.rb +1 -0
- data/lib/nitro/mixin/pager.rb +7 -4
- data/lib/nitro/mixin/rss.rb +2 -0
- data/lib/nitro/mixin/table.rb +23 -6
- data/lib/nitro/mixin/xhtml.rb +2 -2
- data/lib/nitro/render.rb +19 -5
- data/lib/nitro/scaffold.rb +12 -6
- data/lib/nitro/server.rb +4 -6
- data/lib/nitro/server/runner.rb +2 -2
- data/lib/nitro/session.rb +8 -1
- data/lib/nitro/session/file.rb +40 -0
- data/lib/part/admin.rb +2 -0
- data/lib/part/admin/controller.rb +7 -3
- data/lib/part/admin/skin.rb +8 -1
- data/lib/part/admin/template/index.xhtml +39 -1
- data/proto/public/error.xhtml +5 -3
- data/proto/public/js/behaviour.js +254 -254
- data/proto/public/js/controls.js +427 -165
- data/proto/public/js/dragdrop.js +255 -276
- data/proto/public/js/effects.js +476 -277
- data/proto/public/js/prototype.js +561 -127
- data/proto/public/js/scaffold.js +74 -0
- data/proto/public/js/scriptaculous.js +44 -0
- data/proto/public/js/util.js +548 -0
- data/proto/public/scaffold/list.xhtml +4 -1
- data/proto/scgi.rb +333 -0
- data/script/scgi_ctl +221 -0
- data/script/scgi_service +120 -0
- data/test/nitro/adapter/raw_post1.bin +0 -0
- data/test/nitro/{tc_cookie.rb → cgi/tc_cookie.rb} +1 -1
- data/test/nitro/{tc_request.rb → cgi/tc_request.rb} +1 -1
- data/test/nitro/mixin/tc_xhtml.rb +1 -1
- data/test/nitro/{adapter/tc_cgi.rb → tc_cgi.rb} +12 -12
- data/test/nitro/tc_controller.rb +9 -5
- metadata +159 -169
- data/benchmark/bench.rb +0 -5
- data/benchmark/simple-webrick-n-200.txt +0 -44
- data/benchmark/static-webrick-n-200.txt +0 -43
- data/benchmark/tiny-lhttpd-n-200-c-5.txt +0 -43
- data/benchmark/tiny-webrick-n-200-c-5.txt +0 -44
- data/benchmark/tiny-webrick-n-200.txt +0 -44
- data/benchmark/tiny2-webrick-n-200.txt +0 -44
- data/examples/README +0 -7
@@ -1,9 +1,12 @@
|
|
1
1
|
<SystemPage name="List of %list_name%">
|
2
2
|
<h2><a href="#@base/new_%name%">New %name%</a></h2>
|
3
|
+
<form action="search">
|
4
|
+
Search %list_name%: <input type="text" name="q" /> <input type="submit" value="Search" /> (not implemented yet)
|
5
|
+
</form>
|
3
6
|
<table>
|
4
7
|
<?r for obj in @%list_name% ?>
|
5
8
|
<tr>
|
6
|
-
<td width="100%">#{obj.to_s}</td>
|
9
|
+
<td width="100%"><a href="#@base/edit_%name%/#{obj.oid}">#{obj.to_s}</a></td>
|
7
10
|
<?r if obj.respond_to?(:update_time) ?>
|
8
11
|
<td nowrap="1">#{obj.update_time.stamp(:db)}</td>
|
9
12
|
<?r end ?>
|
data/proto/scgi.rb
ADDED
@@ -0,0 +1,333 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'stringio'
|
4
|
+
require 'yaml'
|
5
|
+
require 'digest/sha1'
|
6
|
+
require 'logger'
|
7
|
+
require 'fileutils'
|
8
|
+
require 'socket'
|
9
|
+
require 'cgi'
|
10
|
+
require 'rubygems'
|
11
|
+
require 'cmdparse'
|
12
|
+
require 'monitor'
|
13
|
+
|
14
|
+
def log(msg)
|
15
|
+
$stderr.print msg,"\n"
|
16
|
+
end
|
17
|
+
|
18
|
+
def error(msg, exc=nil)
|
19
|
+
if exc
|
20
|
+
$stderr.print "ERROR: #{msg}: #{exc}\n"
|
21
|
+
$stderr.puts exc.backtrace
|
22
|
+
else
|
23
|
+
$stderr.print "ERROR: #{msg}\n"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
# Modifies CGI so that we can use it.
|
29
|
+
class SCGIFixed < ::CGI
|
30
|
+
public :env_table
|
31
|
+
|
32
|
+
def initialize(params, data, out, *args)
|
33
|
+
@env_table = params
|
34
|
+
@args = *args
|
35
|
+
@input = StringIO.new(data)
|
36
|
+
@out = out
|
37
|
+
super(*args)
|
38
|
+
end
|
39
|
+
def args
|
40
|
+
@args
|
41
|
+
end
|
42
|
+
def env_table
|
43
|
+
@env_table
|
44
|
+
end
|
45
|
+
def stdinput
|
46
|
+
@input
|
47
|
+
end
|
48
|
+
def stdoutput
|
49
|
+
@out
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
class SCGIProcessor < Monitor
|
55
|
+
|
56
|
+
def initialize(settings)
|
57
|
+
@env = settings[:env] || "development"
|
58
|
+
@debug = settings[:debug] || false
|
59
|
+
@host = settings[:host] || "127.0.0.1"
|
60
|
+
@port = settings[:port] || "9999"
|
61
|
+
@children = settings[:children] || 1
|
62
|
+
@pid_file = settings[:pid_file] || "children.yaml"
|
63
|
+
@status_dir = settings[:status_dir] || "/tmp"
|
64
|
+
@log_file = settings[:logfile] || "log/scgi.log"
|
65
|
+
@maxconns = settings[:maxconns]
|
66
|
+
@busy_msg = settings[:busy_msg] || "BUSY"
|
67
|
+
@settings = settings
|
68
|
+
@started = Time.now
|
69
|
+
@conns = 0
|
70
|
+
@total_conns = 0
|
71
|
+
@errors = 0
|
72
|
+
|
73
|
+
if @maxconns
|
74
|
+
@maxconns = @maxconns.to_i
|
75
|
+
else
|
76
|
+
@maxconns = 2**30-1
|
77
|
+
end
|
78
|
+
|
79
|
+
if settings[:conns_second]
|
80
|
+
@throttle_sleep = 1.0/settings[:conns_second].to_i
|
81
|
+
end
|
82
|
+
|
83
|
+
super()
|
84
|
+
end
|
85
|
+
|
86
|
+
def run
|
87
|
+
ENV['NITRO_ENV'] = @env
|
88
|
+
|
89
|
+
require 'nitro'
|
90
|
+
server = TCPServer.new(@host, @port)
|
91
|
+
|
92
|
+
if @debug
|
93
|
+
log("Listening for connections on #@host:#@port")
|
94
|
+
listen(server)
|
95
|
+
else
|
96
|
+
childpids = []
|
97
|
+
@children.to_i.times do
|
98
|
+
# fork each child listening to the same port. very simple yet effective way to spread the load
|
99
|
+
# to multiple processes without using threads and still using high performance libevent
|
100
|
+
begin
|
101
|
+
pid = fork do
|
102
|
+
$stderr = open(@log_file,"w")
|
103
|
+
$stderr.sync = false
|
104
|
+
listen(server)
|
105
|
+
end
|
106
|
+
childpids << pid
|
107
|
+
Process.detach(pid)
|
108
|
+
rescue Object
|
109
|
+
error("Could not fork child processes. Your system might not support fork. Use -D instead.", $!)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# tell the user what the sha1 is so they can check for modification later
|
114
|
+
log("#@pid_file will have SHA1 #{Digest::SHA1.hexdigest(YAML.dump(childpids))}")
|
115
|
+
log("Record this somewhere so you know if it was modified later by someone else.")
|
116
|
+
# all children forked and the pids are now ready to write to the pid file
|
117
|
+
open(@pid_file,"w") { |f| f.write(YAML.dump(childpids)) }
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
def listen(socket)
|
123
|
+
thread = Thread.new do
|
124
|
+
while true
|
125
|
+
handle_client(socket.accept)
|
126
|
+
sleep @throttle_sleep if @throttle_sleep
|
127
|
+
|
128
|
+
@total_conns += 1
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
begin
|
133
|
+
thread.join
|
134
|
+
rescue Interrupt
|
135
|
+
log("Shutting down from SIGINT.")
|
136
|
+
rescue Object
|
137
|
+
error("while listening for connections on #@host:#@port", $!)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
def handle_client(socket)
|
143
|
+
Thread.new do
|
144
|
+
begin
|
145
|
+
synchronize { @conns += 1}
|
146
|
+
|
147
|
+
len = ""
|
148
|
+
# we only read 10 bytes of the length. any request longer than this is invalid
|
149
|
+
while len.length <= 10
|
150
|
+
c = socket.read(1)
|
151
|
+
if c == ':'
|
152
|
+
# found the terminal, len now has a length in it so read the payload
|
153
|
+
break
|
154
|
+
else
|
155
|
+
len << c
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# we should now either have a payload length to get
|
160
|
+
payload = socket.read(len.to_i)
|
161
|
+
if (c = socket.read(1)) != ','
|
162
|
+
error("Malformed request, does not end with ','")
|
163
|
+
else
|
164
|
+
read_header(socket, payload, @conns)
|
165
|
+
end
|
166
|
+
rescue IOError
|
167
|
+
error("received IOError #$! when handling client. Your web server doesn't like me.")
|
168
|
+
rescue Object
|
169
|
+
@errors += 1
|
170
|
+
error("after accepting client #@host:#@port -- #{$!.class}", $!)
|
171
|
+
ensure
|
172
|
+
synchronize { @conns -= 1}
|
173
|
+
socket.close if not socket.closed?
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
|
179
|
+
|
180
|
+
def read_header(socket, payload, conns)
|
181
|
+
return if socket.closed?
|
182
|
+
request = split_body(payload)
|
183
|
+
if request and request["CONTENT_LENGTH"]
|
184
|
+
length = request["CONTENT_LENGTH"].to_i
|
185
|
+
if length > 0
|
186
|
+
body = socket.read(length)
|
187
|
+
else
|
188
|
+
body = ""
|
189
|
+
end
|
190
|
+
|
191
|
+
if @conns > @maxconns
|
192
|
+
socket.write("Content-type: text/plain\r\n\r\n")
|
193
|
+
socket.write(@busy_msg)
|
194
|
+
else
|
195
|
+
process_request(request, body, socket)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
|
201
|
+
def process_request(request, body, socket)
|
202
|
+
return if socket.closed?
|
203
|
+
cgi = SCGIFixed.new(request, body, socket)
|
204
|
+
begin
|
205
|
+
synchronize do
|
206
|
+
# unfortuneatly, the dependencies.rb file is not thread safe and will throw exceptions
|
207
|
+
# claiming that Dispatcher is not defined, or that other classes are missing. We have
|
208
|
+
# to sync the dispatch call to get around this.
|
209
|
+
Dispatcher.dispatch(cgi, ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, cgi.stdoutput)
|
210
|
+
end
|
211
|
+
rescue IOError
|
212
|
+
error("received IOError #$! when handling client. Your web server doesn't like me.")
|
213
|
+
rescue Object => nitro_error
|
214
|
+
error("calling Dispatcher.dispatch", nitro_error)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
|
219
|
+
def split_body(data)
|
220
|
+
result = {}
|
221
|
+
el = data.split("\0")
|
222
|
+
i = 0
|
223
|
+
len = el.length
|
224
|
+
while i < len
|
225
|
+
result[el[i]] = el[i+1]
|
226
|
+
i += 1
|
227
|
+
end
|
228
|
+
|
229
|
+
return result
|
230
|
+
end
|
231
|
+
|
232
|
+
def status
|
233
|
+
pid = Process.pid
|
234
|
+
open("#@status_dir/scgi_nitro_status.#{pid}","w") do |f|
|
235
|
+
status = {
|
236
|
+
'time' => Time.now, 'pid' => pid, 'settings' => @settings,
|
237
|
+
'env' => @env, 'status_dir' => @status_dir, 'started' => @started,
|
238
|
+
'max_conns' => @maxconns, 'total_conns' => @total_conns,
|
239
|
+
'conns' => @conns, 'errors' => @errors, 'systimes' => Process.times
|
240
|
+
}
|
241
|
+
f.write(YAML.dump(status))
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
|
247
|
+
def signal_children(pidfile, signal)
|
248
|
+
if not File.exists? pidfile
|
249
|
+
log("No #{pidfile} as specified. Probably nothing running or wrong path.")
|
250
|
+
exit 1
|
251
|
+
end
|
252
|
+
|
253
|
+
childpids = YAML.load_file(pidfile)
|
254
|
+
childpids.each do |pid|
|
255
|
+
begin
|
256
|
+
log("Signaling pid #{pid}")
|
257
|
+
Process.kill(signal, pid)
|
258
|
+
rescue Object
|
259
|
+
log("Couldn't send #{signal} signal to #{pid} pid.")
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
|
265
|
+
def make_command(parent, name, desc, options)
|
266
|
+
cmd = CmdParse::Command.new(name, false )
|
267
|
+
cmd.short_desc = desc
|
268
|
+
settings = {}
|
269
|
+
cmd.options = CmdParse::OptionParserWrapper.new do |opt|
|
270
|
+
options.each do |short, long, info, symbol|
|
271
|
+
opt.on(short, long, info) {|val| settings[symbol] = val}
|
272
|
+
end
|
273
|
+
end
|
274
|
+
cmd.set_execution_block do |args|
|
275
|
+
yield(settings, args)
|
276
|
+
end
|
277
|
+
parent.add_command(cmd)
|
278
|
+
end
|
279
|
+
|
280
|
+
|
281
|
+
cmd = CmdParse::CommandParser.new( true )
|
282
|
+
cmd.program_name = 'scgi'
|
283
|
+
cmd.program_version = [0, 2, 1]
|
284
|
+
cmd.options = CmdParse::OptionParserWrapper.new do |opt|
|
285
|
+
opt.separator "Global options:"
|
286
|
+
opt.on("--verbose", "Be verbose when outputting info") {|t| $verbose = true }
|
287
|
+
end
|
288
|
+
|
289
|
+
cmd.add_command( CmdParse::HelpCommand.new )
|
290
|
+
cmd.add_command( CmdParse::VersionCommand.new )
|
291
|
+
|
292
|
+
make_command(cmd, 'start', "Start Nitro Application",
|
293
|
+
[['-e','--env STRING','Nitro environment', :env],
|
294
|
+
['-D','--[no-]debug', 'Do not fork children, stay in foreground.', :debug],
|
295
|
+
['-h','--host STRING', 'IP address to bind as server', :host],
|
296
|
+
['-p','--port NUMBER', 'Port to bind to', :port],
|
297
|
+
['-c','--children NUMBER', 'Number of children to start (not win32)', :children],
|
298
|
+
['-f','--pid-file PATH', 'Where to read the list of running children', :pid_file],
|
299
|
+
['-l','--log-file PATH', 'Use a different log from from log/scgi.log', :logfile],
|
300
|
+
['-t','--throttle NUMBER', 'Max conn/second to allow.', :conns_second],
|
301
|
+
['-m','--max-conns NUMBER', 'Max simultaneous connections before the busy message', :maxconns],
|
302
|
+
['-b','--busy-msg', 'Busy message given to clients over the max connections ("busy")', :busy_msg],
|
303
|
+
['-s','--status-dir PATH', 'Where to put the status files', :status_dir]]) do |settings, args|
|
304
|
+
scgi = SCGIProcessor.new(settings)
|
305
|
+
begin
|
306
|
+
trap("HUP") { scgi.status }
|
307
|
+
rescue Object
|
308
|
+
error("Could not setup a SIGHUP handler. You won't be able to get status.")
|
309
|
+
end
|
310
|
+
|
311
|
+
scgi.run
|
312
|
+
end
|
313
|
+
|
314
|
+
|
315
|
+
make_command(cmd, 'status', "Get status from all running children",
|
316
|
+
[['-s','--status-dir PATH', 'Where to put the status files', :status_dir],
|
317
|
+
['-f','--pid-file PATH', 'Where to read the list of running children', :pid_file]]) do |settings, args|
|
318
|
+
signal_children(settings[:pid_file] || 'children.yaml', "HUP")
|
319
|
+
log("Status files for each child should show up in the configured status directory (/tmp by default).")
|
320
|
+
end
|
321
|
+
|
322
|
+
make_command(cmd, 'stop', "Stop all running children",
|
323
|
+
[['-s','--sig SIGNAL', 'Where to put the status files', :signal],
|
324
|
+
['-n','--[no-]delete', 'Keep the children.yaml file rather than delete', :nodelete],
|
325
|
+
['-f','--pid-file PATH', 'Where to read the list of running children', :pid_file]]) do |settings, args|
|
326
|
+
pid_file = settings[:pid_file] || "children.yaml"
|
327
|
+
signal_children(pid_file, settings[:signal] || "INT")
|
328
|
+
if not settings[:nodelete] and File.exist?(pid_file)
|
329
|
+
File.unlink(pid_file)
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
cmd.parse
|
data/script/scgi_ctl
ADDED
@@ -0,0 +1,221 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'drb'
|
3
|
+
require 'rubygems'
|
4
|
+
require 'cmdparse'
|
5
|
+
require 'yaml'
|
6
|
+
require 'highline'
|
7
|
+
|
8
|
+
UI = HighLine.new
|
9
|
+
DEFAULT_CONFIG = "conf/scgi.yaml"
|
10
|
+
|
11
|
+
def safe(error)
|
12
|
+
begin
|
13
|
+
yield
|
14
|
+
rescue Object
|
15
|
+
STDERR.puts("ERROR: #{error}: #$!")
|
16
|
+
exit 1
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def load_config(file)
|
21
|
+
safe("Could not load config") do
|
22
|
+
return YAML.load_file(file)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def connect(url)
|
27
|
+
conn = nil
|
28
|
+
safe("Can't connect to #{url}") do
|
29
|
+
conn = DRbObject.new(nil, url)
|
30
|
+
end
|
31
|
+
|
32
|
+
safe("Failed communicating with #{url}") do
|
33
|
+
yield conn
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def deduce_url(settings)
|
38
|
+
settings[:control_url] || load_config(settings[:config] || DEFAULT_CONFIG)[:control_url]
|
39
|
+
end
|
40
|
+
|
41
|
+
def defaults(settings)
|
42
|
+
defaults = nil
|
43
|
+
if settings[:merge]
|
44
|
+
UI.say("Merging with previous settings.")
|
45
|
+
defaults = load_config(settings[:config] || DEFAULT_CONFIG)
|
46
|
+
else
|
47
|
+
defaults = {
|
48
|
+
:env => "production",
|
49
|
+
:host => "127.0.0.1",
|
50
|
+
:port => 9999,
|
51
|
+
:logfile => "log/scgi.log",
|
52
|
+
:config => DEFAULT_CONFIG }
|
53
|
+
end
|
54
|
+
|
55
|
+
settings = defaults.merge(settings)
|
56
|
+
# fix up the stuff that's not quite right yet
|
57
|
+
settings[:control_url] = "druby://127.0.0.1:#{settings[:port]-1000}"
|
58
|
+
settings[:port] = settings[:port].to_i
|
59
|
+
settings[:throttle] = settings[:throttle].to_i if settings[:throttle]
|
60
|
+
settings[:maxconns] = settings[:maxconns].to_i if settings[:maxconns]
|
61
|
+
|
62
|
+
return settings
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
def configure(settings)
|
67
|
+
settings = defaults(settings)
|
68
|
+
|
69
|
+
pass = settings[:password] || UI.ask("What password do you want? ")
|
70
|
+
salting = ('a' .. 'z').to_a + ('A' .. 'Z').to_a + ('0' .. '9').to_a
|
71
|
+
settings[:password] = pass.crypt(salting[rand(salting.length)] + salting[rand(salting.length)])
|
72
|
+
|
73
|
+
# great, they are not idiots. Well, they can read --help at least.
|
74
|
+
safe("Could not write config") do
|
75
|
+
open(settings[:config],"w") {|f| f.write(YAML.dump(settings)) }
|
76
|
+
UI.say("Configuration settings written to #{settings[:config]}")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def password
|
81
|
+
if not $password
|
82
|
+
$password = UI.ask("Password: ")
|
83
|
+
end
|
84
|
+
$password
|
85
|
+
end
|
86
|
+
|
87
|
+
def start(cmd, config)
|
88
|
+
fork do
|
89
|
+
exec cmd, config
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def status(url)
|
94
|
+
connect(url) do |conn|
|
95
|
+
s = conn.status(password)
|
96
|
+
times = s[:systimes]
|
97
|
+
|
98
|
+
puts <<-END
|
99
|
+
#{UI.color("Status as of #{s[:time]}:",:green,:bold)}
|
100
|
+
PID: #{s[:pid]}\tStarted: #{s[:started]}\tEnvironment: #{s[:env]}
|
101
|
+
Connected Requests: #{s[:conns]}
|
102
|
+
Conns/Second: #{s[:conns_second] || "Not Set"}
|
103
|
+
Max Simultaneous Conns: #{s[:max_conns]}
|
104
|
+
Shutdown Started: #{s[:shutdown]}
|
105
|
+
Processing Time: #{times.utime} #{times.stime} #{times.cutime} #{times.cstime}
|
106
|
+
Current Settings:
|
107
|
+
#{s[:settings].to_yaml.gsub(/:([a-z])/, ' \1')}
|
108
|
+
END
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def reconfigure(url)
|
113
|
+
connect(url) do |conn|
|
114
|
+
conn.reconfigure(password)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def stop(url, force)
|
119
|
+
connect(url) do |conn|
|
120
|
+
conn.shutdown(password, force)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def restart(url, force)
|
125
|
+
connect(url) do |conn|
|
126
|
+
conn.restart(password, force)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def monitor(url)
|
131
|
+
connect(url) do |conn|
|
132
|
+
while true
|
133
|
+
sleep 3
|
134
|
+
puts "\e[2J"
|
135
|
+
status(url)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
|
141
|
+
def make_command(parent, name, desc, options)
|
142
|
+
cmd = CmdParse::Command.new(name, false )
|
143
|
+
cmd.short_desc = desc
|
144
|
+
settings = {}
|
145
|
+
cmd.options = CmdParse::OptionParserWrapper.new do |opt|
|
146
|
+
options.each do |short, long, info, symbol|
|
147
|
+
opt.on(short, long, info) {|val| settings[symbol] = val}
|
148
|
+
end
|
149
|
+
end
|
150
|
+
cmd.set_execution_block do |args|
|
151
|
+
yield(settings, args)
|
152
|
+
end
|
153
|
+
parent.add_command(cmd)
|
154
|
+
end
|
155
|
+
|
156
|
+
|
157
|
+
cmd = CmdParse::CommandParser.new( true )
|
158
|
+
cmd.program_name = "scgi_nitro"
|
159
|
+
cmd.program_version = [0, 4, 0]
|
160
|
+
cmd.options = CmdParse::OptionParserWrapper.new do |opt|
|
161
|
+
opt.separator "Global options:"
|
162
|
+
opt.on("--verbose", "Be verbose when outputting info") {|t| $verbose = true }
|
163
|
+
end
|
164
|
+
|
165
|
+
cmd.add_command( CmdParse::HelpCommand.new )
|
166
|
+
cmd.add_command( CmdParse::VersionCommand.new )
|
167
|
+
|
168
|
+
make_command(cmd, 'config', "Configure the SCGI servers",
|
169
|
+
[['-e','--env STRING','Nitro environment', :env],
|
170
|
+
['-h','--host STRING', 'IP address to bind as server', :host],
|
171
|
+
['-p','--port NUMBER', 'Port to bind to (starts at 9999)', :port],
|
172
|
+
['-u','--control-url URL', 'DRuby URL to run control on (same as SCGI -1000)', :control_url],
|
173
|
+
['-l','--log-file PATH', 'Use a different log from from log/scgi.log', :logfile],
|
174
|
+
['-t','--throttle NUMBER', 'Max conn/second to allow.', :conns_second],
|
175
|
+
['-m','--max-conns NUMBER', 'Max simultaneous connections before the busy message', :maxconns],
|
176
|
+
['-P','--moron-mode PASSWORD', 'You are an idiot and you want your password on the command line', :password],
|
177
|
+
['-M','--merge', 'Merge new settings with previous rather than defaults', :merge],
|
178
|
+
['-c','--config PATH', 'Config file to use (#{DEFAULT_CONFIG})', :config]]) do |settings, args|
|
179
|
+
configure(settings)
|
180
|
+
end
|
181
|
+
|
182
|
+
make_command(cmd, 'start', "Start the application",
|
183
|
+
[['-u','--control-url URL', 'DRuby URL to run control on (same as SCGI -1000)', :control_url],
|
184
|
+
['-c','--config PATH', 'Config file to use (#{DEFAULT_CONFIG})', :config]]) do |settings, args|
|
185
|
+
cmd = File.dirname(__FILE__) + "/scgi_service"
|
186
|
+
start(cmd, settings[:config] || DEFAULT_CONFIG)
|
187
|
+
end
|
188
|
+
|
189
|
+
make_command(cmd, 'reconfig', "Reconfigure the SCGI servers with a new config",
|
190
|
+
[['-u','--control-url URL', 'DRuby URL to run control on (same as SCGI -1000)', :control_url],
|
191
|
+
['-c','--config PATH', 'Config file to use (#{DEFAULT_CONFIG})', :config]]) do |settings, args|
|
192
|
+
reconfigure(deduce_url(settings))
|
193
|
+
end
|
194
|
+
|
195
|
+
make_command(cmd, 'status', "Get status",
|
196
|
+
[['-u','--control-url URL', 'DRuby URL to run control on (same as SCGI -1000)', :control_url],
|
197
|
+
['-c','--config PATH', 'Config file to use (#{DEFAULT_CONFIG})', :config]]) do |settings, args|
|
198
|
+
status(deduce_url(settings))
|
199
|
+
end
|
200
|
+
|
201
|
+
make_command(cmd, 'stop', "Stop the application",
|
202
|
+
[['-u','--control-url URL', 'DRuby URL to run control on (same as SCGI -1000)', :control_url],
|
203
|
+
['-c','--config PATH', 'Config file to use (#{DEFAULT_CONFIG})', :config],
|
204
|
+
['-f','--force', 'Forced shutdown rather than graceful (default graceful)', :force]]) do |settings, args|
|
205
|
+
stop(deduce_url(settings), settings[:force] || false)
|
206
|
+
end
|
207
|
+
|
208
|
+
make_command(cmd, 'restart', "Restart the application",
|
209
|
+
[['-u','--control-url URL', 'DRuby URL to run control on (same as SCGI -1000)', :control_url],
|
210
|
+
['-c','--config PATH', 'Config file to use (#{DEFAULT_CONFIG})', :config],
|
211
|
+
['-f','--force', 'Forced shutdown rather than graceful (default graceful)', :force]]) do |settings, args|
|
212
|
+
restart(deduce_url(settings), settings[:force] || false)
|
213
|
+
end
|
214
|
+
|
215
|
+
make_command(cmd, 'monitor', "Monitor the application",
|
216
|
+
[['-u','--control-url URL', 'DRuby URL to run control on (same as SCGI -1000)', :control_url],
|
217
|
+
['-c','--config PATH', 'Config file to use (#{DEFAULT_CONFIG})', :config]]) do |settings, args|
|
218
|
+
monitor(deduce_url(settings))
|
219
|
+
end
|
220
|
+
|
221
|
+
cmd.parse
|