karsthammer-passenger 2.2.4
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/README +45 -0
- data/bin/passenger-config +46 -0
- data/bin/passenger-install-apache2-module +217 -0
- data/bin/passenger-install-nginx-module +465 -0
- data/bin/passenger-make-enterprisey +83 -0
- data/bin/passenger-memory-stats +301 -0
- data/bin/passenger-spawn-server +68 -0
- data/bin/passenger-status +125 -0
- data/bin/passenger-stress-test +344 -0
- data/ext/phusion_passenger/extconf.rb +36 -0
- metadata +78 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# Phusion Passenger - http://www.modrails.com/
|
|
3
|
+
# Copyright (c) 2008, 2009 Phusion
|
|
4
|
+
#
|
|
5
|
+
# "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
|
|
6
|
+
#
|
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
# in the Software without restriction, including without limitation the rights
|
|
10
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
# furnished to do so, subject to the following conditions:
|
|
13
|
+
#
|
|
14
|
+
# The above copyright notice and this permission notice shall be included in
|
|
15
|
+
# all copies or substantial portions of the Software.
|
|
16
|
+
#
|
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
23
|
+
# THE SOFTWARE.
|
|
24
|
+
|
|
25
|
+
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
|
|
26
|
+
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../ext"))
|
|
27
|
+
require 'phusion_passenger/admin_tools/control_process'
|
|
28
|
+
require 'optparse'
|
|
29
|
+
|
|
30
|
+
include PhusionPassenger::AdminTools
|
|
31
|
+
|
|
32
|
+
# ANSI color codes
|
|
33
|
+
RESET = "\e[0m"
|
|
34
|
+
BOLD = "\e[1m"
|
|
35
|
+
YELLOW = "\e[33m"
|
|
36
|
+
BLACK_BG = "\e[40m"
|
|
37
|
+
BLUE_BG = "\e[44m"
|
|
38
|
+
|
|
39
|
+
def show_status(control_process, options = {})
|
|
40
|
+
case options[:show]
|
|
41
|
+
when 'pool'
|
|
42
|
+
begin
|
|
43
|
+
text = control_process.status
|
|
44
|
+
rescue SystemCallError => e
|
|
45
|
+
STDERR.puts "*** ERROR: Cannot query status for Passenger instance #{control_process.pid}:"
|
|
46
|
+
STDERR.puts e.to_s
|
|
47
|
+
exit 2
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Colorize output
|
|
51
|
+
text.gsub!(/^(----)(.*)$/, YELLOW + BLUE_BG + BOLD + '\1\2' + RESET)
|
|
52
|
+
|
|
53
|
+
puts text
|
|
54
|
+
|
|
55
|
+
when 'backtraces'
|
|
56
|
+
begin
|
|
57
|
+
text = control_process.backtraces
|
|
58
|
+
rescue SystemCallError => e
|
|
59
|
+
STDERR.puts "*** ERROR: Cannot query status for Passenger instance #{control_process.pid}:"
|
|
60
|
+
STDERR.puts e.to_s
|
|
61
|
+
exit 2
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Colorize output
|
|
65
|
+
text.gsub!(/^(Thread .*:)$/, BLACK_BG + YELLOW + '\1' + RESET)
|
|
66
|
+
text.gsub!(/^( +in '.*? )(.*?)\(/, '\1' + BOLD + '\2' + RESET + '(')
|
|
67
|
+
|
|
68
|
+
puts text
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def start
|
|
73
|
+
options = { :show => 'pool' }
|
|
74
|
+
parser = OptionParser.new do |opts|
|
|
75
|
+
opts.banner = "Usage: passenger-status [options] [Phusion Passenger's PID]"
|
|
76
|
+
opts.separator ""
|
|
77
|
+
opts.separator "Tool for inspecting Phusion Passenger's internal status."
|
|
78
|
+
opts.separator ""
|
|
79
|
+
|
|
80
|
+
opts.separator "Options:"
|
|
81
|
+
opts.on("--show=pool|backtraces", String,
|
|
82
|
+
"Whether to show the pool's contents or\n" <<
|
|
83
|
+
"#{' ' * 37}the backtraces of all threads.") do |what|
|
|
84
|
+
if what !~ /\A(pool|backtraces)\Z/
|
|
85
|
+
STDERR.puts "Invalid argument for --show."
|
|
86
|
+
exit 1
|
|
87
|
+
else
|
|
88
|
+
options[:show] = what
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
begin
|
|
93
|
+
parser.parse!
|
|
94
|
+
rescue OptionParser::ParseError => e
|
|
95
|
+
puts e
|
|
96
|
+
puts
|
|
97
|
+
puts "Please see '--help' for valid options."
|
|
98
|
+
exit 1
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
if ARGV.empty?
|
|
102
|
+
control_processes = ControlProcess.list
|
|
103
|
+
if control_processes.empty?
|
|
104
|
+
STDERR.puts("ERROR: Phusion Passenger doesn't seem to be running.")
|
|
105
|
+
exit 2
|
|
106
|
+
elsif control_processes.size == 1
|
|
107
|
+
show_status(control_processes.first, options)
|
|
108
|
+
else
|
|
109
|
+
puts "It appears that multiple Passenger instances are running. Please select a"
|
|
110
|
+
puts "specific one by running:"
|
|
111
|
+
puts
|
|
112
|
+
puts " passenger-status <PID>"
|
|
113
|
+
puts
|
|
114
|
+
puts "The following Passenger instances are running:"
|
|
115
|
+
control_processes.each do |control|
|
|
116
|
+
puts " PID: #{control.pid}"
|
|
117
|
+
end
|
|
118
|
+
exit 1
|
|
119
|
+
end
|
|
120
|
+
else
|
|
121
|
+
show_status(ControlProcess.for_pid(ARGV[0].to_i), options)
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
start
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# Phusion Passenger - http://www.modrails.com/
|
|
3
|
+
# Copyright (c) 2008, 2009 Phusion
|
|
4
|
+
#
|
|
5
|
+
# "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
|
|
6
|
+
#
|
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
# in the Software without restriction, including without limitation the rights
|
|
10
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
# furnished to do so, subject to the following conditions:
|
|
13
|
+
#
|
|
14
|
+
# The above copyright notice and this permission notice shall be included in
|
|
15
|
+
# all copies or substantial portions of the Software.
|
|
16
|
+
#
|
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
23
|
+
# THE SOFTWARE.
|
|
24
|
+
|
|
25
|
+
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
|
|
26
|
+
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../ext"))
|
|
27
|
+
require 'rubygems'
|
|
28
|
+
require 'optparse'
|
|
29
|
+
require 'socket'
|
|
30
|
+
require 'thread'
|
|
31
|
+
require 'phusion_passenger/platform_info'
|
|
32
|
+
require 'phusion_passenger/message_channel'
|
|
33
|
+
require 'phusion_passenger/utils'
|
|
34
|
+
|
|
35
|
+
include PhusionPassenger
|
|
36
|
+
include PhusionPassenger::Utils
|
|
37
|
+
include PlatformInfo
|
|
38
|
+
|
|
39
|
+
# A thread or a process, depending on the Ruby VM implementation.
|
|
40
|
+
class Subprocess
|
|
41
|
+
attr_accessor :channel
|
|
42
|
+
|
|
43
|
+
def initialize(name, &block)
|
|
44
|
+
if RUBY_PLATFORM == "java"
|
|
45
|
+
a, b = UNIXSocket.pair
|
|
46
|
+
@thread = Thread.new do
|
|
47
|
+
block.call(true, MessageChannel.new(b))
|
|
48
|
+
end
|
|
49
|
+
@channel = MessageChannel.new(a)
|
|
50
|
+
@thread_channel = b
|
|
51
|
+
else
|
|
52
|
+
a, b = UNIXSocket.pair
|
|
53
|
+
@pid = safe_fork(name) do
|
|
54
|
+
a.close
|
|
55
|
+
$0 = name
|
|
56
|
+
Process.setsid
|
|
57
|
+
block.call(false, MessageChannel.new(b))
|
|
58
|
+
end
|
|
59
|
+
b.close
|
|
60
|
+
@channel = MessageChannel.new(a)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def stop
|
|
65
|
+
if RUBY_PLATFORM == "java"
|
|
66
|
+
@thread.terminate
|
|
67
|
+
@channel.close
|
|
68
|
+
@thread_channel.close
|
|
69
|
+
else
|
|
70
|
+
Process.kill('SIGKILL', @pid) rescue nil
|
|
71
|
+
Process.waitpid(@pid) rescue nil
|
|
72
|
+
@channel.close
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
class StressTester
|
|
78
|
+
def start
|
|
79
|
+
@options = parse_options
|
|
80
|
+
load_hawler
|
|
81
|
+
|
|
82
|
+
Thread.abort_on_exception = true
|
|
83
|
+
if GC.respond_to?(:copy_on_write_friendly=)
|
|
84
|
+
GC.copy_on_write_friendly = true
|
|
85
|
+
end
|
|
86
|
+
@terminal_height = ENV['LINES'] ? ENV['LINES'].to_i : 24
|
|
87
|
+
@terminal_width = ENV['COLUMNS'] ? ENV['COLUMNS'].to_i : 80
|
|
88
|
+
|
|
89
|
+
if Process.euid != 0
|
|
90
|
+
puts "*** WARNING: This program might not be able to restart " <<
|
|
91
|
+
"Apache because it's not running as root. Please run " <<
|
|
92
|
+
"this tool as root."
|
|
93
|
+
puts
|
|
94
|
+
puts "Press Enter to continue..."
|
|
95
|
+
begin
|
|
96
|
+
STDIN.readline
|
|
97
|
+
rescue Interrupt
|
|
98
|
+
exit 1
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
run_crawlers
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def parse_options
|
|
106
|
+
options = {
|
|
107
|
+
:concurrency => 20,
|
|
108
|
+
:depth => 20,
|
|
109
|
+
:nice => true,
|
|
110
|
+
:apache_restart_interval => 24 * 60,
|
|
111
|
+
:app_restart_interval => 55
|
|
112
|
+
}
|
|
113
|
+
parser = OptionParser.new do |opts|
|
|
114
|
+
opts.banner = "Usage: passenger-stress-test <hostname> <app_root> [options]\n\n" <<
|
|
115
|
+
"Stress test the given (Passenger-powered) website by:\n" <<
|
|
116
|
+
" * crawling it with multiple concurrently running crawlers.\n" <<
|
|
117
|
+
" * gracefully restarting Apache at random times (please point the 'APXS2'\n" <<
|
|
118
|
+
" variable to your Apache's 'apxs' binary).\n" <<
|
|
119
|
+
" * restarting the target (Passenger-powered) application at random time.\n" <<
|
|
120
|
+
"\n" <<
|
|
121
|
+
"Example:\n" <<
|
|
122
|
+
" passenger-stress-test mywebsite.com /webapps/mywebsite\n" <<
|
|
123
|
+
"\n"
|
|
124
|
+
|
|
125
|
+
opts.separator "Options:"
|
|
126
|
+
opts.on("-c", "--concurrency N", Integer,
|
|
127
|
+
"Number of crawlers to start (default = #{options[:concurrency]})") do |v|
|
|
128
|
+
options[:concurrency] = v
|
|
129
|
+
end
|
|
130
|
+
opts.on("-p", "--apache-restart-interval N", Integer,
|
|
131
|
+
"Gracefully restart Apache after N minutes\n" <<
|
|
132
|
+
(" " * 37) << "(default = #{options[:apache_restart_interval]})") do |v|
|
|
133
|
+
options[:apache_restart_interval] = v
|
|
134
|
+
end
|
|
135
|
+
opts.on("-a", "--app-restart-interval N", Integer,
|
|
136
|
+
"Restart the application after N minutes\n" <<
|
|
137
|
+
(" " * 37) << "(default = #{options[:app_restart_interval]})") do |v|
|
|
138
|
+
options[:app_restart_interval] = v
|
|
139
|
+
end
|
|
140
|
+
opts.on("-h", "--help", "Show this message") do
|
|
141
|
+
puts opts
|
|
142
|
+
exit
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
parser.parse!
|
|
146
|
+
|
|
147
|
+
options[:host] = ARGV[0]
|
|
148
|
+
options[:app_root] = ARGV[1]
|
|
149
|
+
if !options[:host] || !options[:app_root]
|
|
150
|
+
puts parser
|
|
151
|
+
exit 1
|
|
152
|
+
end
|
|
153
|
+
return options
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def load_hawler
|
|
157
|
+
begin
|
|
158
|
+
require 'hawler'
|
|
159
|
+
rescue LoadError
|
|
160
|
+
STDERR.puts "This tool requires Hawler (http://tinyurl.com/ywgk6x). Please install it with:"
|
|
161
|
+
STDERR.puts
|
|
162
|
+
STDERR.puts " gem install --source http://spoofed.org/files/hawler/ hawler"
|
|
163
|
+
exit 1
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def run_crawlers
|
|
168
|
+
@started = false
|
|
169
|
+
@crawlers = []
|
|
170
|
+
|
|
171
|
+
# Start crawler processes.
|
|
172
|
+
GC.start if GC.copy_on_write_friendly?
|
|
173
|
+
@options[:concurrency].times do |i|
|
|
174
|
+
STDOUT.write("Starting crawler #{i + 1} of #{@options[:concurrency]}...\n")
|
|
175
|
+
STDOUT.flush
|
|
176
|
+
process = Subprocess.new("crawler #{i + 1}") do |is_thread, channel|
|
|
177
|
+
if !is_thread && @options[:nice]
|
|
178
|
+
system("renice 1 #{Process.pid} >/dev/null 2>/dev/null")
|
|
179
|
+
end
|
|
180
|
+
while true
|
|
181
|
+
crawl!(i + 1, channel)
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
@crawlers << {
|
|
185
|
+
:id => i + 1,
|
|
186
|
+
:process => process,
|
|
187
|
+
:channel => process.channel,
|
|
188
|
+
:mutex => Mutex.new,
|
|
189
|
+
:current_uri => nil,
|
|
190
|
+
:crawled => 0
|
|
191
|
+
}
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
puts
|
|
195
|
+
if RUBY_PLATFORM != "java"
|
|
196
|
+
# 'sleep' b0rks when running in JRuby?
|
|
197
|
+
sleep 1
|
|
198
|
+
end
|
|
199
|
+
begin
|
|
200
|
+
$0 = "Passenger Crawler: control process"
|
|
201
|
+
io_to_crawler = {}
|
|
202
|
+
ios = []
|
|
203
|
+
@crawlers.each do |crawler|
|
|
204
|
+
io_to_crawler[crawler[:channel].io] = crawler
|
|
205
|
+
ios << crawler[:channel].io
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Tell each crawler to start crawling.
|
|
209
|
+
@crawlers.each do |crawler|
|
|
210
|
+
crawler[:channel].write("start")
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Show progress periodically.
|
|
214
|
+
@start_time = Time.now
|
|
215
|
+
progress_reporter = Thread.new(&method(:report_progress))
|
|
216
|
+
@next_apache_restart = Time.now + @options[:apache_restart_interval] * 60
|
|
217
|
+
apache_restarter = Thread.new(&method(:restart_apache))
|
|
218
|
+
@next_app_restart = Time.now + @options[:app_restart_interval] * 60
|
|
219
|
+
app_restarter = Thread.new(&method(:restart_app))
|
|
220
|
+
|
|
221
|
+
while true
|
|
222
|
+
note_progress(ios, io_to_crawler)
|
|
223
|
+
end
|
|
224
|
+
rescue Interrupt
|
|
225
|
+
trap('SIGINT') {}
|
|
226
|
+
puts "Shutting down..."
|
|
227
|
+
@done = true
|
|
228
|
+
@crawlers.each do |crawler|
|
|
229
|
+
STDOUT.write("Stopping crawler #{crawler[:id]} of #{@options[:concurrency]}...\r")
|
|
230
|
+
STDOUT.flush
|
|
231
|
+
crawler[:process].stop
|
|
232
|
+
end
|
|
233
|
+
progress_reporter.join if progress_reporter
|
|
234
|
+
apache_restarter.join if apache_restarter
|
|
235
|
+
app_restarter.join if app_restarter
|
|
236
|
+
puts
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def note_progress(ios, io_to_crawler)
|
|
241
|
+
select(ios)[0].each do |io|
|
|
242
|
+
crawler = io_to_crawler[io]
|
|
243
|
+
uri = crawler[:channel].read[0]
|
|
244
|
+
crawler[:mutex].synchronize do
|
|
245
|
+
crawler[:current_uri] = uri
|
|
246
|
+
crawler[:crawled] += 1
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def report_progress
|
|
252
|
+
while !@done
|
|
253
|
+
output = "\n" * @terminal_height
|
|
254
|
+
output << "### Running for #{duration(Time.now.to_i - @start_time.to_i)}\n"
|
|
255
|
+
@crawlers.each do |crawler|
|
|
256
|
+
crawler[:mutex].synchronize do
|
|
257
|
+
line = sprintf("Crawler %-2d: %-3d -> %s",
|
|
258
|
+
crawler[:id],
|
|
259
|
+
crawler[:crawled],
|
|
260
|
+
crawler[:current_uri])
|
|
261
|
+
output << sprintf("%-#{@terminal_width}s\n", line)
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
output << "Next Apache restart: in #{duration(@next_apache_restart.to_i - Time.now.to_i)}\n"
|
|
265
|
+
output << "Next app restart : in #{duration(@next_app_restart.to_i - Time.now.to_i)}\n"
|
|
266
|
+
STDOUT.write(output)
|
|
267
|
+
sleep 0.5
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def restart_apache
|
|
272
|
+
while !@done
|
|
273
|
+
if Time.now > @next_apache_restart
|
|
274
|
+
@next_apache_restart = Time.now + @options[:apache_restart_interval] * 60
|
|
275
|
+
system("#{HTTPD} -k graceful")
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def restart_app
|
|
281
|
+
while !@done
|
|
282
|
+
if Time.now > @next_app_restart
|
|
283
|
+
@next_app_restart = Time.now + @options[:app_restart_interval] * 60
|
|
284
|
+
system("touch #{@options[:app_root]}/tmp/restart.txt")
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def duration(seconds)
|
|
290
|
+
result = ""
|
|
291
|
+
if seconds >= 60
|
|
292
|
+
minutes = (seconds / 60)
|
|
293
|
+
if minutes >= 60
|
|
294
|
+
hours = minutes / 60
|
|
295
|
+
minutes = minutes % 60
|
|
296
|
+
if hours == 1
|
|
297
|
+
result << "#{hours} hour "
|
|
298
|
+
else
|
|
299
|
+
result << "#{hours} hours "
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
seconds = seconds % 60
|
|
304
|
+
if minutes == 1
|
|
305
|
+
result << "#{minutes} minute "
|
|
306
|
+
else
|
|
307
|
+
result << "#{minutes} minutes "
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
result << "#{seconds} seconds"
|
|
311
|
+
return result
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
def crawl!(id, channel)
|
|
315
|
+
progress_reporter = lambda do |uri, referer, response|
|
|
316
|
+
begin
|
|
317
|
+
if !@started
|
|
318
|
+
# At the beginning, wait until the control process
|
|
319
|
+
# tells us to start.
|
|
320
|
+
@started = true
|
|
321
|
+
channel.read
|
|
322
|
+
end
|
|
323
|
+
channel.write(uri, referer, response)
|
|
324
|
+
rescue
|
|
325
|
+
if RUBY_PLATFORM == "java"
|
|
326
|
+
Thread.current.terminate
|
|
327
|
+
else
|
|
328
|
+
Process.kill('SIGKILL', Process.pid)
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
crawler = Hawler.new(@options[:host], progress_reporter)
|
|
333
|
+
if RUBY_PLATFORM == "java"
|
|
334
|
+
trap('SIGINT') do
|
|
335
|
+
raise Interrupt, "Interrupted"
|
|
336
|
+
end
|
|
337
|
+
end
|
|
338
|
+
crawler.recurse = true
|
|
339
|
+
crawler.depth = @options[:depth]
|
|
340
|
+
crawler.start
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
StressTester.new.start
|