solutious-stella 0.5.5 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. data/CHANGES.txt +39 -2
  2. data/LICENSE.txt +19 -0
  3. data/README.rdoc +85 -0
  4. data/Rakefile +54 -59
  5. data/bin/example_test.rb +82 -0
  6. data/bin/example_webapp.rb +63 -0
  7. data/lib/{stella/logger.rb → logger.rb} +6 -11
  8. data/lib/stella.rb +76 -58
  9. data/lib/stella/clients.rb +161 -0
  10. data/lib/stella/command/base.rb +4 -24
  11. data/lib/stella/command/form.rb +36 -0
  12. data/lib/stella/command/get.rb +44 -0
  13. data/lib/stella/common.rb +53 -0
  14. data/lib/stella/crypto.rb +88 -0
  15. data/lib/stella/data/domain.rb +2 -2
  16. data/lib/stella/data/http.rb +164 -36
  17. data/lib/stella/environment.rb +66 -0
  18. data/lib/stella/functest.rb +105 -0
  19. data/lib/stella/loadtest.rb +186 -0
  20. data/lib/{utils → stella}/stats.rb +16 -20
  21. data/lib/stella/testplan.rb +237 -0
  22. data/lib/stella/testrunner.rb +64 -0
  23. data/lib/storable.rb +280 -0
  24. data/lib/threadify.rb +171 -0
  25. data/lib/timeunits.rb +65 -0
  26. data/lib/util/httputil.rb +266 -0
  27. data/stella.gemspec +69 -0
  28. data/tryouts/drb/drb_test.rb +65 -0
  29. data/tryouts/drb/open4.rb +19 -0
  30. data/tryouts/drb/slave.rb +27 -0
  31. data/tryouts/oo_tryout.rb +30 -0
  32. metadata +39 -107
  33. data/README.textile +0 -162
  34. data/bin/stella +0 -12
  35. data/bin/stella.bat +0 -12
  36. data/lib/daemonize.rb +0 -56
  37. data/lib/pcaplet.rb +0 -180
  38. data/lib/stella/adapter/ab.rb +0 -337
  39. data/lib/stella/adapter/base.rb +0 -106
  40. data/lib/stella/adapter/httperf.rb +0 -305
  41. data/lib/stella/adapter/pcap_watcher.rb +0 -221
  42. data/lib/stella/adapter/proxy_watcher.rb +0 -76
  43. data/lib/stella/adapter/siege.rb +0 -341
  44. data/lib/stella/cli.rb +0 -258
  45. data/lib/stella/cli/agents.rb +0 -73
  46. data/lib/stella/cli/base.rb +0 -55
  47. data/lib/stella/cli/language.rb +0 -18
  48. data/lib/stella/cli/localtest.rb +0 -78
  49. data/lib/stella/cli/sysinfo.rb +0 -16
  50. data/lib/stella/cli/watch.rb +0 -278
  51. data/lib/stella/command/localtest.rb +0 -358
  52. data/lib/stella/response.rb +0 -85
  53. data/lib/stella/storable.rb +0 -201
  54. data/lib/stella/support.rb +0 -276
  55. data/lib/stella/sysinfo.rb +0 -257
  56. data/lib/stella/test/definition.rb +0 -79
  57. data/lib/stella/test/run/summary.rb +0 -70
  58. data/lib/stella/test/stats.rb +0 -114
  59. data/lib/stella/text.rb +0 -64
  60. data/lib/stella/text/resource.rb +0 -38
  61. data/lib/utils/crypto-key.rb +0 -84
  62. data/lib/utils/domainutil.rb +0 -47
  63. data/lib/utils/escape.rb +0 -302
  64. data/lib/utils/fileutil.rb +0 -78
  65. data/lib/utils/httputil.rb +0 -266
  66. data/lib/utils/mathutil.rb +0 -15
  67. data/lib/utils/textgraph.rb +0 -267
  68. data/lib/utils/timerutil.rb +0 -58
  69. data/lib/win32/Console.rb +0 -970
  70. data/lib/win32/Console/ANSI.rb +0 -305
  71. data/support/kvm.h +0 -91
  72. data/support/ruby-pcap-takuma-notes.txt +0 -19
  73. data/support/ruby-pcap-takuma-patch.txt +0 -30
  74. data/support/text/en.yaml +0 -80
  75. data/support/text/nl.yaml +0 -7
  76. data/support/useragents.txt +0 -75
  77. data/tests/01-util_test.rb +0 -0
  78. data/tests/02-stella-util_test.rb +0 -42
  79. data/tests/10-stella_test.rb +0 -104
  80. data/tests/11-stella-storable_test.rb +0 -68
  81. data/tests/60-stella-command_test.rb +0 -248
  82. data/tests/80-stella-cli_test.rb +0 -45
  83. data/tests/spec-helper.rb +0 -31
@@ -1,106 +0,0 @@
1
-
2
-
3
- module Stella::Adapter
4
- class CommandNotReady < RuntimeError
5
- def initialize(name="")
6
- super(Stella::TEXT.msg(:error_adapter_command_not_ready, name))
7
- end
8
- end
9
-
10
- class Base
11
-
12
-
13
- attr_accessor :working_directory
14
- attr_reader :load_factor, :arguments
15
-
16
- def initialize(options={}, arguments=[])
17
- if options.is_a? Array
18
- self.process_arguments(options)
19
- else
20
- self.options = options
21
- self.arguments = arguments
22
- end
23
- end
24
-
25
- def load_factor=(load_factor)
26
- @load_factor = load_factor
27
- end
28
- def stdout_path
29
- File.join(@working_directory, 'STDOUT.txt')
30
- end
31
-
32
- def stderr_path
33
- File.join(@working_directory, 'STDERR.txt')
34
- end
35
-
36
- def summary_path(ext='yaml')
37
- File.join(@working_directory, "SUMMARY.#{ext}")
38
- end
39
-
40
- # process_arguments
41
- #
42
- # This method must be overridden by the implementing class. This is intended
43
- # for processing the command-specific command-line arguments
44
- def process_arguments
45
- raise Stella::TEXT.msg(:error_class_must_override, 'process_options')
46
- end
47
-
48
- # options=
49
- #
50
- # Takes a hash, OpenStruct and applies the values to the instance variables.
51
- # The keys should conincide with with the command line argument names.
52
- # by process_options first and
53
- # i.e. The key for --help should be :help
54
- def options=(options={})
55
- options = options.marshal_dump if options.is_a? OpenStruct
56
-
57
- unless options.nil? || options.empty?
58
- options.each_pair do |name,value|
59
- next if @private_variables.member?(name)
60
- Stella::LOGGER.info(:error_class_unknown_argument, name) && next unless self.respond_to?("#{name.to_s}=")
61
- instance_variable_set("@#{name.to_s}", value)
62
- end
63
- end
64
- end
65
-
66
- def arguments=(arguments=[])
67
- @arguments = arguments unless arguments.nil?
68
- end
69
-
70
- def available?
71
- (version.to_f > 0)
72
- end
73
-
74
- def name
75
- @name
76
- end
77
-
78
- def rate
79
- @rate || 0
80
- end
81
- def vuser_rate
82
- "#{vusers}/#{rate}"
83
- end
84
-
85
- def command
86
- raise Stella::TEXT.msg(:error_class_must_override, 'command')
87
- end
88
-
89
- def summary
90
- raise Stella::TEXT.msg(:error_class_must_override, 'summary')
91
- end
92
-
93
- def add_header
94
- raise Stella::TEXT.msg(:error_class_must_override, 'add_header')
95
- end
96
-
97
- def user_agent=
98
- raise Stella::TEXT.msg(:error_class_must_override, 'user_agent=')
99
- end
100
-
101
- private
102
-
103
-
104
- end
105
- end
106
-
@@ -1,305 +0,0 @@
1
-
2
-
3
- module Stella
4
- module Adapter
5
-
6
- #Usage: httperf [-hdvV] [--add-header S] [--burst-length N] [--client N/N]
7
- # [--close-with-reset] [--debug N] [--failure-status N]
8
- # [--help] [--hog] [--http-version S] [--max-connections N]
9
- # [--max-piped-calls N] [--method S] [--no-host-hdr]
10
- # [--num-calls N] [--num-conns N] [--period [d|u|e]T1[,T2]]
11
- # [--port N] [--print-reply [header|body]] [--print-request [header|body]]
12
- # [--rate X] [--recv-buffer N] [--retry-on-failure] [--send-buffer N]
13
- # [--server S] [--server-name S] [--session-cookies]
14
- # [--ssl] [--ssl-ciphers L] [--ssl-no-reuse]
15
- # [--think-timeout X] [--timeout X] [--uri S] [--verbose] [--version]
16
- # [--wlog y|n,file] [--wsess N,N,X] [--wsesslog N,X,file]
17
- # [--wset N,X]
18
- #
19
- class Httperf < Stella::Adapter::Base
20
-
21
-
22
-
23
- attr_accessor :hog, :server, :uri, :num_conns, :num_calls, :rate, :timeout, :think_timeout, :port
24
- attr_accessor :burst_length, :client, :close_with_reset, :debug, :failure_status
25
- attr_accessor :help, :http_version, :max_connections, :max_piped_calls, :method, :no_host_hdr
26
- attr_accessor :period, :print_reply, :print_request, :recv_buffer, :retry_on_failure, :send_buffer
27
- attr_accessor :server_name, :session_cookies, :ssl, :ssl_ciphers, :ssl_no_reuse, :verbose
28
-
29
- attr_writer :version, :add_header, :wlog, :wsess, :wsesslog, :wset
30
-
31
- def initialize(options={}, arguments=[])
32
-
33
- @name = 'httperf'
34
- @private_variables = ['private_variables', 'name', 'arguments', 'load_factor', 'working_directory']
35
- @load_factor = 1
36
-
37
- super(options, arguments)
38
- end
39
-
40
-
41
-
42
- def error
43
- (File.exists? stderr_path) ? FileUtil.read_file(stderr_path) : "Unknown error"
44
- end
45
-
46
- # Before calling run
47
- def before
48
-
49
-
50
- end
51
- def command
52
- raise CommandNotReady.new(self.class.to_s) unless ready?
53
-
54
- command = "#{@name} "
55
-
56
- instance_variables.each do |name|
57
- canon = name.tr('@', '') # instance_variables returns '@name'
58
- next if @private_variables.member?(canon)
59
-
60
- # It's important that we take the value from the getter method
61
- # because it applies the load factor.
62
- value = self.send(canon)
63
- if (value.is_a? Array)
64
- value.each { |el| command << "--#{canon.tr('_', '-')} #{EscapeUtil.shell_single_word(el.to_s)} " }
65
- else
66
- command << "--#{canon.tr('_', '-')} #{EscapeUtil.shell_single_word(value.to_s)} "
67
- end
68
-
69
- end
70
-
71
- command << (@arguments.map { |uri| "'#{uri}'" }).join(' ') unless @arguments.empty?
72
- command
73
- end
74
-
75
- # After calling run
76
- def after
77
-
78
-
79
- end
80
-
81
- def process_arguments(arguments)
82
-
83
- opts = OptionParser.new
84
- opts.on('--hog') do @hog = true end
85
- opts.on('--server=S', String) do |v| @server = v end
86
- opts.on('--server-name=S', String) do |v| @server_name = v end
87
- opts.on('--port=N', Integer) do |v| @port = v end
88
- opts.on('--uri=S', String) do |v| @uri = v end
89
- opts.on('--num-conns=N', Integer) do |v| @num_conns = v end
90
- opts.on('--num-calls=N', Integer) do |v| @num_calls = v end
91
- opts.on('--rate=N', Integer) do |v| @rate = v end
92
- opts.on('--timeout=N', Integer) do |v| @timeout = v end
93
- opts.on('--think-timeout=N', Integer) do |v| @think_timeout = v end
94
-
95
- opts.on('-h', '--help') do |v| @help = true end
96
- opts.on('-v', '--verbose') do |v| @verbose = true end
97
- opts.on('-V', '--version') do |v| @version = true end
98
- opts.on('--close-with-reset') do |v| @close_with_reset = true end
99
- opts.on('--session-cookies') do |v| @session_cookies = true end
100
- opts.on('--ssl') do |v| @ssl = true end
101
- opts.on('--ssl-ciphers') do |v| @ssl_ciphers = true end
102
- opts.on('--ssl-no-reuse') do |v| @ssl_no_reuse = true end
103
- opts.on('--no-host-hdr') do |v| @no_host_hdr = true end
104
- opts.on('--retry-on-failure') do |v| @retry_on_failure = true end
105
-
106
- opts.on('--add-header=S', String) do |v| @add_header ||= []; @add_header << v; end
107
- opts.on('--burst-length=N', Integer) do |v| @burst_length = v end
108
- opts.on('--client=S', String) do |v| @client = v end
109
- opts.on('-d N', '--debug=N', Integer) do |v| @debug ||= 0; @debug = v end
110
- opts.on('--failure-status=N', Integer) do |v| @failure_status = v end
111
-
112
- opts.on('--http-version=S', String) do |v| @http_version = v end
113
-
114
- opts.on('--max-connections=N', Integer) do |v| @max_connections = v end
115
- opts.on('--max-piped-calls=N', Integer) do |v| @max_piped_calls = v end
116
- opts.on('--method=S', String) do |v| @method = v end
117
-
118
- opts.on('--period=S', String) do |v| @period = v end # TODO: Requires parsing
119
- opts.on('--print-reply=[S]', String) do |v| @print_reply = v end
120
- opts.on('--print-request=[S]', String) do |v| @print_request = v end
121
-
122
- opts.on('--recv-buffer=N', Integer) do |v| @recv_buffer = v end
123
- opts.on('--send-buffer=N', Integer) do |v| @send_buffer = v end
124
-
125
-
126
- opts.on('--wlog=S', String) do |v| @wlog = Stella::Util::expand_str(v) end
127
- opts.on('--wsess=S', String) do |v| @wsess = Stella::Util::expand_str(v, Integer) end
128
- opts.on('--wsesslog=S', String) do |v| @wsesslog = Stella::Util::expand_str(v) end
129
- opts.on('--wset=S', String) do |v| @wset = Stella::Util::expand_str(v) end
130
-
131
- if @wsess
132
-
133
- end
134
-
135
- # parse! removes the options it finds.
136
- # It also fails when it finds unknown switches (i.e. -X)
137
- # Which should leave only the remaining arguments (URIs in this case)
138
- opts.parse!(arguments)
139
-
140
- self.arguments = arguments
141
-
142
- rescue OptionParser::InvalidOption => ex
143
- # We want to replace this text so we grab just the name of the argument
144
- badarg = ex.message.gsub('invalid option: ', '')
145
- raise InvalidArgument.new(badarg)
146
- end
147
-
148
-
149
- def version
150
- vsn = 0
151
- Stella::Util.capture_output("#{@name} --version") do |stdout, stderr|
152
- stdout.join.scan(/httperf\-([\d\.]+)\s/) { |v| vsn = v[0] }
153
- end
154
- vsn
155
- end
156
-
157
- # loadtest
158
- #
159
- # True or false: is the call to siege a load test? If it's a call to help or version or
160
- # to display the config this with return false. It's no reason for someone to make this
161
- # call through Stella but it's here for goodness sake.
162
- def loadtest?
163
- @uri && !@uri.empty?
164
- end
165
- def ready?
166
- @name && !instance_variables.empty?
167
- end
168
-
169
- def add_header(name=false, value=false)
170
- # This is a hack since we have an instance variable called add_header.
171
- # I figure this is the best of two evils because I'd rather keep the
172
- # instance variable naming consistent.
173
- return @add_header if !name && !value
174
- @add_header ||= []
175
- @add_header << "#{name}: #{value}"
176
- end
177
-
178
- def user_agent=(list=[])
179
- return unless list && !list.empty?
180
- list = list.to_ary
181
- list.each do |agent|
182
- add_header("User-Agent", agent)
183
- end
184
- end
185
- def vusers
186
- @wsess[1]
187
- end
188
- def vuser_rate
189
- "#{vusers}/#{rate}"
190
- end
191
-
192
- def vusers=(v)
193
- 0
194
- end
195
- def requests
196
- @num_conns || (@wsess[0] * @wsess[1])
197
- end
198
- def requests=(v)
199
- 0
200
- end
201
- def vuser_requests
202
- 0
203
- end
204
- def wsess
205
- @wsess.join(',')
206
- end
207
-
208
- def wset
209
- @wset.join(',')
210
- end
211
-
212
-
213
- def wsesslog
214
- @wsesslog.join(',')
215
- end
216
- def wlog
217
- @wlog.join(',')
218
- end
219
-
220
- #def concurrent
221
- # (@concurrent * @load_factor).to_i
222
- #end
223
- #def concurrent_f
224
- # (@concurrent * @load_factor).to_f
225
- #end
226
- #def reps
227
- # @reps
228
- #end
229
-
230
-
231
-
232
- # Siege writes the summary to STDERR
233
- def summary_file
234
- File.new(stdout_path) if File.exists?(stdout_path)
235
- end
236
-
237
- def rc_file
238
- File.join(@working_directory, "siegerc")
239
- end
240
-
241
- def log_file
242
- File.join(@working_directory, "siege.log")
243
- end
244
-
245
- def uris_file
246
- File.join(@working_directory, File.basename(@file))
247
- end
248
-
249
- # httperf --hog --timeout=30 --client=0/1 --server=127.0.0.1 --port=5600 --uri=/ --send-buffer=4096 --recv-buffer=16384 --num-conns=5 --num-calls=1
250
- # httperf: warning: open file limit > FD_SETSIZE; limiting max. # of open files to FD_SETSIZE
251
- # Maximum connect burst length: 1
252
- #
253
- # Total: connections 5 requests 5 replies 5 test-duration 0.513 s
254
- #
255
- # Connection rate: 9.7 conn/s (102.7 ms/conn, <=1 concurrent connections)
256
- # Connection time [ms]: min 102.1 avg 102.7 max 104.1 median 102.5 stddev 0.8
257
- # Connection time [ms]: connect 0.2
258
- # Connection length [replies/conn]: 1.000
259
- #
260
- # Request rate: 9.7 req/s (102.7 ms/req)
261
- # Request size [B]: 62.0
262
- #
263
- # Reply rate [replies/s]: min 0.0 avg 0.0 max 0.0 stddev 0.0 (0 samples)
264
- # Reply time [ms]: response 102.3 transfer 0.1
265
- # Reply size [B]: header 136.0 content 96.0 footer 0.0 (total 232.0)
266
- # Reply status: 1xx=0 2xx=5 3xx=0 4xx=0 5xx=0
267
- #
268
- # CPU time [s]: user 0.12 system 0.39 (user 22.5% system 75.3% total 97.8%)
269
- # Net I/O: 2.8 KB/s (0.0*10^6 bps)
270
- #
271
- # Errors: total 0 client-timo 0 socket-timo 0 connrefused 0 connreset 0
272
- # Errors: fd-unavail 0 addrunavail 0 ftab-full 0 other 0
273
-
274
- def summary
275
- return unless summary_file
276
-
277
- raw = summary_file.readlines.join
278
- stats = Stella::Test::Run::Summary.new
279
-
280
- raw.scan(/Request rate: (\d+?\.\d+?) req.s .(\d+?\.\d+?) ms.req./) do |rate,time|
281
- stats.transaction_rate = rate.to_f
282
- stats.response_time = (time.to_f) / 1000
283
- end
284
-
285
- raw.scan(/connections (\d+?) requests (\d+?) replies (\d+?) test-duration (\d+\.\d+?) s/) do |conn,req,rep,time|
286
- stats.elapsed_time = time.to_f
287
- stats.successful = rep.to_i
288
- stats.failed = conn.to_i - rep.to_i # maybe this should be from the Error line
289
- stats.transactions = conn.to_i
290
- end
291
-
292
- raw.scan(/header (\d+\.\d+?)\s+.+?\s+.total (\d+\.\d+?)./) do |h,t|
293
- stats.data_transferred = ((t.to_f || 0 ) / 1_048_576).to_f
294
- stats.headers_transferred = ((h.to_f || 0 ) / 1_048_576).to_f
295
- end
296
- stats.vusers = self.vusers
297
-
298
- stats
299
- end
300
-
301
-
302
-
303
- end
304
- end
305
- end
@@ -1,221 +0,0 @@
1
-
2
- require 'webrick'
3
- require 'stringio'
4
- require 'net/dns/packet'
5
-
6
-
7
- module Stella
8
- module Adapter
9
- # Make sure Stella's lib directory is before the system defined ones.
10
- # We are using a modified version of pcaplet.rb.
11
- require 'pcap'
12
- require 'observer'
13
-
14
- # Stella::Adapter::PcapWatcher
15
- #
16
- # Record HTTP or DNS events with Pcap (TCP sniffer). This requires ruby-pcap and the C pcap
17
- # library as well as root acceess (TCP packet sniffing requires root privileges). If you're
18
- # running Ruby 1.9, JRuby, or Windows this will not be available on your system.
19
- # To sniff traffic, you must be on either the machine sending the requests or the machine
20
- # receiving the requests.
21
- class PcapWatcher
22
- include Observable
23
-
24
- # Building Ruby::Pcap with Ruby 1.9.1
25
- # RSTRING()->len ia now RSTRING_LEN(), ...
26
- # see: http://gnufied.org/2007/12/21/mysql-c-bindings-for-ruby-19/#comment-3133
27
- # see: http://www.rubyinside.com/ruby-1-9-1-preview-released-why-its-a-big-deal-1280.html#comment-37223
28
- # TRAP_BEG and TRAP_END are also fucked. But the fix is not clear.
29
- # Basically Ruby::PCap is not ready for 1.9
30
- # See: http://d.hatena.ne.jp/takuma104/20080210/1202638583
31
-
32
- # Network interface device ID. eri0, en0, lo0, etc... /sbin/ifconfig -a will tell you.
33
- attr_accessor :device
34
- # Buffer size, in bytes, to read from each packet (default: 1500)
35
- attr_accessor :snaplen
36
- # Port of the machine sending requests (default: 80)
37
- attr_accessor :sport
38
- # Port of the machine receiving requests (default: 80)
39
- attr_accessor :dport
40
- # dns or http
41
- attr_accessor :service
42
- # udp or tcp
43
- attr_accessor :protocol
44
- # Maximum number of packets to sniff
45
- attr_accessor :maxpacks
46
-
47
- attr_reader :pcaplet
48
-
49
- def initialize(options={})
50
- # The proper service name for dns is "domain"
51
- @service = options[:service] || 'http'
52
- @service = 'domain' if options[:service] == 'dns'
53
-
54
- if @service == 'domain'
55
- @protocol = 'udp'
56
- else
57
- @protocol = options[:protocol] || 'tcp'
58
- end
59
-
60
- @dport = options[:port] || Socket::getservbyname(@service)
61
- @sport = options[:port] || @dport
62
-
63
- @device = options[:device] || guess_device
64
- @snaplen = options[:snaplen] || 10000 # 10KB
65
- @maxpacks = options[:maxpacks] || 100_000
66
-
67
- Stella::LOGGER.info("Watching interface #{@device} for #{@service} activity on #{@protocol} port #{@dport}")
68
- end
69
-
70
- def guess_device
71
- # NOTE: This should be passed in as a value, not called from the global
72
- case Stella::SYSINFO.implementation
73
- when :osx
74
- "en1" # Pcap.lookupdev returns en0
75
- else
76
- Pcap.lookupdev
77
- end
78
- end
79
-
80
- def run
81
-
82
- if (respond_to? "monitor_#{@service}")
83
- self.send("monitor_#{@service}")
84
- else
85
- raise "Unknown service type (#{@service})"
86
- end
87
-
88
- end
89
-
90
-
91
-
92
- # monitor_domain
93
- #
94
- # Use Ruby-Pcap to sniff packets off the network interface.
95
- #
96
- # DNS monitor based on: http://www.linuxjournal.com/article/9614
97
- # Install http://rubyforge.org/projects/net-dns
98
- #
99
- # NOTE: Is there a better way to match up a request packet with a
100
- # response packet?
101
- # We keep connect a request with the response using the domain name.
102
- # It's possible that two (or more) requests to be made for the same domain
103
- # at the same time and the responses could be mixed up. This will affect
104
- # the exact response time but probably not by much.
105
- def monitor_domain
106
-
107
- @pcaplet = Pcaplet.new(:device => @device, :count => @maxpacks)
108
-
109
- req_filter = Pcap::Filter.new("#{@protocol} and dst port #{@dport}", @pcaplet.capture)
110
- resp_filter = Pcap::Filter.new("#{@protocol} and src port #{@dport}", @pcaplet.capture)
111
- @pcaplet.add_filter(req_filter | resp_filter)
112
- @pcaplet.each_packet do |packet|
113
- data = packet.udp_data
114
- case packet
115
- when req_filter
116
- dobj = Stella::Data::DomainRequest.new(data)
117
- dobj.time = packet.time
118
- dobj.client_ip = packet.ip_src
119
- dobj.server_ip = packet.ip_dst
120
-
121
- changed
122
- notify_observers(:domain_request, dobj)
123
-
124
- when resp_filter
125
- dobj = Stella::Data::DomainResponse.new(data)
126
- dobj.time = packet.time
127
- dobj.client_ip = packet.ip_dst
128
- dobj.server_ip = packet.ip_src
129
-
130
- changed
131
- notify_observers(:domain_response, dobj)
132
- end
133
-
134
- end
135
-
136
- rescue Interrupt
137
- after
138
- exit
139
- end
140
-
141
-
142
- def monitor_http
143
-
144
- @pcaplet = Pcaplet.new(:device => @device, :count => @maxpacks)
145
-
146
- begin
147
- req_filter = Pcap::Filter.new("#{@protocol} and dst port #{@dport}", @pcaplet.capture)
148
- resp_filter = Pcap::Filter.new("#{@protocol} and src port #{@sport}", @pcaplet.capture)
149
- @pcaplet.add_filter(req_filter | resp_filter)
150
- @pcaplet.each_packet do |packet|
151
- data = packet.tcp_data
152
- next if data.nil?
153
-
154
-
155
- # NOTE: With HTTP 1.1 keep alive connections, multiple requests can be passed
156
- # through single connection. This makes it difficult to match responses with
157
- # requests.
158
- # NOTE: We don't parse the body of POST and PUT requests because the data can
159
- # be (and likely is), split across numerous packets. We also only grab 1500
160
- # bytes from each packet.
161
- # NOTE: The hostname is taken from the Host header. Requests made without
162
- # this header (including HTTP 1.0) will contain the local hostname instead.
163
- # TODO: resolve the hostname from the IP address.
164
- # There are some helpful methods for doing some of this stuff:
165
- # http://www.goto.info.waseda.ac.jp/~fukusima/ruby/pcap/doc/TCPPacket.html
166
- case packet
167
- when req_filter
168
- next unless data and data =~ /^(GET|POST|HEAD|DELETE|PUT)\s+(.+?)\s+(HTTP.+?)$/
169
- dobj = Stella::Data::HTTPRequest.new(data.gsub(/\r?\n/, $/)) # Use the system's line terminators
170
- dobj.time = packet.time
171
- dobj.client_ip = packet.ip_src
172
- dobj.server_ip = packet.ip_dst
173
-
174
- changed
175
- notify_observers(:http_request, dobj)
176
-
177
- when resp_filter
178
- # NOTE: Some responses do not contain a body in the first packet.
179
- # TODO: investigate further. Try: http://www.ruby-doc.org/core/classes/Enumerable.html
180
- next unless data and data =~ /^(HTTP.+)$/
181
- dobj = Stella::Data::HTTPResponse.new(data.gsub(/\r?\n/, $/))
182
- dobj.time = packet.time
183
- dobj.client_ip = packet.ip_dst
184
- dobj.server_ip = packet.ip_src
185
-
186
- changed
187
- notify_observers(:http_response, dobj)
188
-
189
- end
190
-
191
- end
192
- rescue Interrupt
193
- after
194
- exit
195
- rescue => ex
196
- Stella::LOGGER.error(ex)
197
- end
198
-
199
-
200
- end
201
-
202
- def after
203
- STDOUT.flush
204
- stat = @pcaplet.capture.stats
205
- if stat
206
- Stella::LOGGER.info("#{$/}#{stat.recv} packets received by filter");
207
- Stella::LOGGER.info("#{stat.drop} packets dropped by kernel", ''); # with an extra line
208
- end
209
- STDOUT.flush
210
- @pcaplet.capture.close
211
- delete_observers
212
- rescue
213
-
214
- # Ignore errors
215
- end
216
-
217
-
218
- end
219
- end
220
- end
221
-