stella 0.7.0.012 → 0.7.0.014
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/CHANGES.txt +1 -1
- data/bin/stella +29 -6
- data/examples/essentials/plan.rb +12 -20
- data/lib/stella.rb +1 -1
- data/lib/stella/cli.rb +48 -4
- data/lib/stella/client.rb +14 -9
- data/lib/stella/engine.rb +4 -8
- data/lib/stella/engine/functional.rb +17 -5
- data/lib/stella/engine/load.rb +20 -189
- data/lib/stella/mixins/numeric.rb +23 -0
- data/lib/stella/testplan.rb +10 -11
- data/lib/stella/testplan/usecase.rb +0 -2
- data/lib/stella/version.rb +1 -1
- data/stella.gemspec +5 -4
- data/{examples/example_webapp.rb → support/sample_webapp/app.rb} +19 -8
- data/support/sample_webapp/config.ru +12 -0
- data/vendor/httpclient-2.1.5.2/httpclient.rb +4 -3
- data/vendor/httpclient-2.1.5.2/httpclient/http.rb +14 -1
- data/vendor/httpclient-2.1.5.2/httpclient/session.rb +9 -2
- metadata +6 -5
- data/examples/example_webapp.ru +0 -9
data/CHANGES.txt
CHANGED
data/bin/stella
CHANGED
@@ -19,7 +19,8 @@
|
|
19
19
|
#--
|
20
20
|
|
21
21
|
# Put our local lib in first place
|
22
|
-
|
22
|
+
BASE_PATH = File.expand_path File.join(File.dirname(__FILE__), '..')
|
23
|
+
lib_dir = File.join(BASE_PATH, 'lib')
|
23
24
|
$:.unshift lib_dir
|
24
25
|
|
25
26
|
require 'drydock'
|
@@ -37,6 +38,9 @@ class Stella::CLI::Definition
|
|
37
38
|
|
38
39
|
global :A, :apikey, String, "API Key"
|
39
40
|
global :S, :secret, String, "Secret Key"
|
41
|
+
global :n, :nocolor, "Disable output colors" do
|
42
|
+
String.disable_color
|
43
|
+
end
|
40
44
|
global :q, :quiet, "Be quiet!" do
|
41
45
|
Stella.enable_quiet
|
42
46
|
end
|
@@ -55,6 +59,14 @@ class Stella::CLI::Definition
|
|
55
59
|
|
56
60
|
# ------------------------------------------------ STELLA --------
|
57
61
|
# ------------------------------------------------------------------
|
62
|
+
about "Instructions for running the example app and test"
|
63
|
+
usage "stella example"
|
64
|
+
command :example => Stella::CLI
|
65
|
+
|
66
|
+
about "Preview the test plan"
|
67
|
+
usage "stella preview [-p path/2/testplan.rb] "
|
68
|
+
option :p, :testplan, String, "Path to testplan"
|
69
|
+
command :preview => Stella::CLI
|
58
70
|
|
59
71
|
about "Run a functional test"
|
60
72
|
usage "stella verify http://stellaaahhhh.com/"
|
@@ -68,14 +80,25 @@ class Stella::CLI::Definition
|
|
68
80
|
usage "stella load http://stellaaahhhh.com:3114/"
|
69
81
|
usage "stella load --clients=10 --repetitions=2 http://stellaaahhhh.com/"
|
70
82
|
usage "stella load -p path/2/testplan.rb -u 100 -r 5 http://stellaaahhhh.com/"
|
71
|
-
option :w, :nowait, "Ignore wait times"
|
72
83
|
option :c, :clients, Integer, "Number of virtual clients"
|
73
84
|
option :r, :repetitions, Integer, "Number of times to repeat the testplan (per vclient)"
|
74
|
-
option :
|
75
|
-
option :
|
85
|
+
option :d, :duration, String, "Max duration to run test"
|
86
|
+
option :W, :nowait, "Ignore wait times"
|
87
|
+
option :w, :wait, Float, "Wait time (in seconds) between client requests (ignored if testplan supplied)"
|
76
88
|
option :p, :testplan, String, "Path to testplan"
|
77
89
|
command :load => Stella::CLI
|
78
90
|
|
91
|
+
about "Run a stress test"
|
92
|
+
usage "stella stress http://stellaaahhhh.com/"
|
93
|
+
usage "stella stress http://stellaaahhhh.com:3114/"
|
94
|
+
usage "stella stress --clients=10 --repetitions=2 http://stellaaahhhh.com/"
|
95
|
+
usage "stella stress -p path/2/testplan.rb -u 100 -r 5 http://stellaaahhhh.com/"
|
96
|
+
option :c, :clients, Integer, "Number of virtual clients"
|
97
|
+
option :r, :repetitions, Integer, "Number of times to repeat the testplan (per vclient)"
|
98
|
+
option :d, :duration, String, "Max duration to run test"
|
99
|
+
option :p, :testplan, String, "Path to testplan"
|
100
|
+
command :stress => Stella::CLI
|
101
|
+
|
79
102
|
about "Initialize Stella configuration"
|
80
103
|
command :init do
|
81
104
|
Stella::Config.init
|
@@ -119,11 +142,11 @@ rescue Drydock::InvalidArgument => ex
|
|
119
142
|
STDERR.puts ex.message
|
120
143
|
rescue Stella::Error => ex
|
121
144
|
STDERR.puts ex.message
|
122
|
-
STDERR.puts ex.backtrace if Drydock.debug?
|
145
|
+
STDERR.puts ex.backtrace if Stella.loglev > 2 || Drydock.debug?
|
123
146
|
rescue Interrupt
|
124
147
|
puts "#{$/}Exiting... "
|
125
148
|
exit 1
|
126
149
|
rescue => ex
|
127
150
|
STDERR.puts "ERROR (#{ex.class.to_s}): #{ex.message}"
|
128
|
-
STDERR.puts ex.backtrace if Drydock.debug?
|
151
|
+
STDERR.puts ex.backtrace if Stella.loglev > 2 || Drydock.debug?
|
129
152
|
end
|
data/examples/essentials/plan.rb
CHANGED
@@ -18,14 +18,14 @@
|
|
18
18
|
#
|
19
19
|
# This plan contains 3 scenarios:
|
20
20
|
#
|
21
|
-
# - Simple Search (
|
22
|
-
# - YAML API (
|
21
|
+
# - Simple Search (65%)
|
22
|
+
# - YAML API (25%)
|
23
23
|
# - Self-serve API (10%)
|
24
24
|
#
|
25
25
|
# The percentages represent the relative amount
|
26
26
|
# of traffic to be generated for each scenario.
|
27
|
-
# In a test with 100 virtual users,
|
28
|
-
# follow the Simple Search usecase,
|
27
|
+
# In a test with 100 virtual users, 65 would
|
28
|
+
# follow the Simple Search usecase, 25 the YAML
|
29
29
|
# API, and 10 would following the Self-Serve API.
|
30
30
|
#
|
31
31
|
#
|
@@ -40,17 +40,10 @@
|
|
40
40
|
# 3. START THE EXAMPLE APPLICATION
|
41
41
|
#
|
42
42
|
# You need to start the example web application before
|
43
|
-
# running this testplan. You can
|
44
|
-
#
|
43
|
+
# running this testplan. You can generate the commands
|
44
|
+
# for your local machine with this command:
|
45
45
|
#
|
46
|
-
# $
|
47
|
-
#
|
48
|
-
# OR
|
49
|
-
#
|
50
|
-
# $ thin -R examples/example_webapp.ru start
|
51
|
-
#
|
52
|
-
# You can check that it's running by going to:
|
53
|
-
# http://127.0.0.1:3000/
|
46
|
+
# $ stella example
|
54
47
|
#
|
55
48
|
#
|
56
49
|
# 4. RUNNING THE TEST PLAN
|
@@ -104,9 +97,9 @@ usecase 65, "Simple search" do
|
|
104
97
|
# homepage ("/").
|
105
98
|
#
|
106
99
|
get "/", "Homepage" do
|
107
|
-
# This tells Stella to wait between 1 and
|
100
|
+
# This tells Stella to wait between 1 and 3
|
108
101
|
# seconds before moving to the next request.
|
109
|
-
wait 1..
|
102
|
+
wait 1..3
|
110
103
|
end
|
111
104
|
|
112
105
|
# In this request, the user has entered a simple
|
@@ -120,7 +113,7 @@ usecase 65, "Simple search" do
|
|
120
113
|
# get "http://example.com:8000/search"
|
121
114
|
#
|
122
115
|
get "/search", "Search Results" do
|
123
|
-
wait 2..
|
116
|
+
wait 2..3
|
124
117
|
|
125
118
|
# Two URI parameters will be included with this
|
126
119
|
# request. Notice that the values for the what
|
@@ -176,7 +169,7 @@ usecase 65, "Simple search" do
|
|
176
169
|
#
|
177
170
|
get "/listing/:lid" do
|
178
171
|
desc "Selected listing"
|
179
|
-
wait 1..
|
172
|
+
wait 1..4
|
180
173
|
end
|
181
174
|
|
182
175
|
end
|
@@ -241,7 +234,6 @@ usecase 10, "Self-serve API" do
|
|
241
234
|
# identical to the ones you've seen above.
|
242
235
|
#
|
243
236
|
post "/listing/add", "Add a listing" do
|
244
|
-
wait 1..4
|
245
237
|
param :name => random(8)
|
246
238
|
param :city => random(['Toronto', 'Vancouver', 'Montreal'])
|
247
239
|
param :logo => file('logo.png')
|
@@ -252,4 +244,4 @@ usecase 10, "Self-serve API" do
|
|
252
244
|
|
253
245
|
end
|
254
246
|
|
255
|
-
#
|
247
|
+
# 278a014c48dd64d2f2aca0588fa281b9553e825f
|
data/lib/stella.rb
CHANGED
data/lib/stella/cli.rb
CHANGED
@@ -27,13 +27,57 @@ class Stella::CLI < Drydock::Command
|
|
27
27
|
def load
|
28
28
|
opts = {}
|
29
29
|
opts[:hosts] = @hosts
|
30
|
-
[:nowait, :clients, :repetitions, :
|
30
|
+
[:nowait, :clients, :repetitions, :wait, :duration].each do |opt|
|
31
31
|
opts[opt] = @option.send(opt) unless @option.send(opt).nil?
|
32
32
|
end
|
33
33
|
ret = Stella::Engine::Load.run @testplan, opts
|
34
34
|
@exit_code = (ret ? 0 : 1)
|
35
35
|
end
|
36
|
-
|
36
|
+
|
37
|
+
def stress_valid?
|
38
|
+
create_testplan
|
39
|
+
end
|
40
|
+
|
41
|
+
def stress
|
42
|
+
opts = {}
|
43
|
+
opts[:hosts] = @hosts
|
44
|
+
[:clients, :repetitions, :duration].each do |opt|
|
45
|
+
opts[opt] = @option.send(opt) unless @option.send(opt).nil?
|
46
|
+
end
|
47
|
+
ret = Stella::Engine::Stress.run @testplan, opts
|
48
|
+
@exit_code = (ret ? 0 : 1)
|
49
|
+
end
|
50
|
+
|
51
|
+
def example
|
52
|
+
base_path = File.expand_path(File.join(Stella::LIB_HOME, '..'))
|
53
|
+
thin_path = File.join(base_path, 'support', 'sample_webapp', 'config.ru')
|
54
|
+
webrick_path = File.join(base_path, 'support', 'sample_webapp', 'app.rb')
|
55
|
+
tp_path = File.join(base_path, 'examples', 'essentials', 'plan.rb')
|
56
|
+
puts "1. Start the web app:".bright
|
57
|
+
puts %Q{
|
58
|
+
$ thin -p 3114 -R #{thin_path} start
|
59
|
+
OR
|
60
|
+
$ ruby #{webrick_path}
|
61
|
+
}
|
62
|
+
puts "2. Check the web app in your browser".bright
|
63
|
+
puts %Q{
|
64
|
+
http://127.0.0.1:3114/
|
65
|
+
}
|
66
|
+
puts "3. Run a functional test:".bright
|
67
|
+
puts %Q{
|
68
|
+
$ stella verify -p #{tp_path} 127.0.0.1:3114
|
69
|
+
}
|
70
|
+
puts "4. Run a load test:".bright
|
71
|
+
puts %Q{
|
72
|
+
$ stella load -p #{tp_path} 127.0.0.1:3114
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
def preview
|
77
|
+
create_testplan
|
78
|
+
Stella.li @testplan.pretty(Stella.loglev > 1)
|
79
|
+
end
|
80
|
+
|
37
81
|
private
|
38
82
|
def create_testplan
|
39
83
|
unless @option.testplan.nil? || File.exists?(@option.testplan)
|
@@ -47,11 +91,11 @@ class Stella::CLI < Drydock::Command
|
|
47
91
|
@testplan = Stella::Testplan.load_file @option.testplan
|
48
92
|
else
|
49
93
|
opts = {}
|
50
|
-
opts[:delay] = @option.
|
94
|
+
opts[:delay] = @option.wait if @option.wait
|
51
95
|
@testplan = Stella::Testplan.new(@argv, opts)
|
52
96
|
end
|
53
97
|
@testplan.check! # raise errors, update usecase ratios
|
54
|
-
Stella.
|
98
|
+
Stella.li2 " #{@option.testplan || @testplan.desc} (#{@testplan.digest})"
|
55
99
|
true
|
56
100
|
end
|
57
101
|
|
data/lib/stella/client.rb
CHANGED
@@ -37,31 +37,35 @@ module Stella
|
|
37
37
|
headers = prepare_headers(container, req.headers)
|
38
38
|
uri = build_request_uri uri_obj, params, container
|
39
39
|
raise NoHostDefined, uri_obj if uri.host.nil? || uri.host.empty?
|
40
|
-
stella_id = [Time.now, self.gibbler_cache, req, params, headers, counter].gibbler
|
40
|
+
stella_id = [Time.now, self.gibbler_cache, req.gibbler_cache, params, headers, counter].gibbler
|
41
41
|
|
42
42
|
Benelux.add_thread_tags :request => req.gibbler_cache
|
43
43
|
Benelux.add_thread_tags :retry => counter
|
44
44
|
Benelux.add_thread_tags :stella_id => stella_id
|
45
45
|
|
46
|
-
params['__stella'] = stella_id
|
47
|
-
headers['X-Stella-ID'] = stella_id
|
46
|
+
params['__stella'] = stella_id.short
|
47
|
+
headers['X-Stella-ID'] = stella_id.short
|
48
48
|
|
49
49
|
meth = req.http_method.to_s.downcase
|
50
50
|
Stella.ld "#{req.http_method}: " << "#{uri_obj.to_s} " << params.inspect
|
51
51
|
|
52
52
|
begin
|
53
53
|
send_request http_client, usecase, meth, uri, req, params, headers, container, counter
|
54
|
-
|
55
|
-
|
54
|
+
|
55
|
+
Benelux.add_thread_tags :status => container.status
|
56
|
+
Benelux.thread_timeline.add_count :request_header_size, container.response.request.header.dump.size
|
57
|
+
Benelux.thread_timeline.add_count :request_content_size, container.response.request.body.content.size
|
58
|
+
Benelux.thread_timeline.add_count :response_headers_size, container.response.header.dump.size
|
59
|
+
Benelux.thread_timeline.add_count :response_content_size, container.response.body.content.size
|
60
|
+
ret = execute_response_handler container, req
|
61
|
+
Benelux.remove_thread_tags :status
|
62
|
+
|
56
63
|
rescue => ex
|
64
|
+
Benelux.thread_timeline.add_count :failed, 1
|
57
65
|
update(:request_error, usecase, uri, req, params, ex)
|
58
66
|
next
|
59
67
|
end
|
60
68
|
|
61
|
-
Benelux.add_thread_tags :status => container.status
|
62
|
-
ret = execute_response_handler container, req
|
63
|
-
Benelux.remove_thread_tags :status
|
64
|
-
|
65
69
|
Stella.lflush
|
66
70
|
|
67
71
|
run_sleeper(req.wait) if req.wait && !nowait?
|
@@ -116,6 +120,7 @@ module Stella
|
|
116
120
|
http_client.set_proxy_auth(@proxy.user, @proxy.pass) if @proxy.user
|
117
121
|
http_client.debug_dev = STDOUT if Stella.debug? && Stella.loglev > 3
|
118
122
|
#http_client.set_cookie_store @cookie_file.path
|
123
|
+
http_client.protocol_version = "HTTP/1.1"
|
119
124
|
http_client
|
120
125
|
end
|
121
126
|
|
data/lib/stella/engine.rb
CHANGED
@@ -4,7 +4,7 @@ module Stella::Engine
|
|
4
4
|
|
5
5
|
# These commented out timers are not very revealing.
|
6
6
|
#Benelux.add_timer Stella::Client, :execute
|
7
|
-
|
7
|
+
Benelux.add_counter Stella::Client, :execute_response_handler
|
8
8
|
#Benelux.add_timer HTTPClient, :create_request
|
9
9
|
|
10
10
|
# These timers are interesting from a reporting perspective.
|
@@ -15,7 +15,9 @@ module Stella::Engine
|
|
15
15
|
Benelux.add_timer HTTPClient::Session, :query
|
16
16
|
Benelux.add_timer HTTPClient::Session, :socket_gets_first_byte
|
17
17
|
Benelux.add_timer HTTPClient::Session, :get_body
|
18
|
-
|
18
|
+
|
19
|
+
#Benelux.add_counter Stella::Client, :execute_response_handler
|
20
|
+
|
19
21
|
module Base
|
20
22
|
extend self
|
21
23
|
|
@@ -33,18 +35,12 @@ module Stella::Engine
|
|
33
35
|
|
34
36
|
|
35
37
|
def update_quit_usecase client_id, msg
|
36
|
-
Stella.li2 " Client-%s QUIT %s" % [client_id.shorter, msg]
|
37
38
|
end
|
38
39
|
|
39
|
-
|
40
40
|
def update_repeat_request client_id, counter, total
|
41
|
-
Stella.li3 " Client-%s REPEAT %d of %d" % [client_id.shorter, counter, total]
|
42
41
|
end
|
43
42
|
|
44
43
|
def update_stats client_id, http_client, usecase, req
|
45
|
-
#range = Thread.current.timeline.ranges(:do_request).last
|
46
|
-
#Thread.current.stathash
|
47
|
-
#Stella.li "Client-%s: %s-%s %s %.4f" % [client_id.short, usecase.gibbler.short, req.gibbler.short, req.desc, range.duration]
|
48
44
|
end
|
49
45
|
|
50
46
|
def update_prepare_request(*args) raise end
|
@@ -12,7 +12,6 @@ module Stella::Engine
|
|
12
12
|
}.merge! opts
|
13
13
|
Stella.ld "OPTIONS: #{opts.inspect}"
|
14
14
|
Stella.li2 "Hosts: " << opts[:hosts].join(', ') if !opts[:hosts].empty?
|
15
|
-
Stella.li plan.pretty
|
16
15
|
|
17
16
|
client = Stella::Client.new opts[:hosts].first
|
18
17
|
client.add_observer(self)
|
@@ -29,13 +28,18 @@ module Stella::Engine
|
|
29
28
|
Stella.rescue { client.execute uc }
|
30
29
|
end
|
31
30
|
|
32
|
-
|
31
|
+
#Benelux.update_all_track_timelines
|
32
|
+
#tl = Benelux.timeline
|
33
|
+
|
34
|
+
# errors?
|
35
|
+
|
33
36
|
end
|
34
37
|
|
35
38
|
|
36
39
|
def update_prepare_request(client_id, usecase, req, counter)
|
37
40
|
notice = "repeat: #{counter-1}" if counter > 1
|
38
|
-
|
41
|
+
desc = "#{req.desc} (#{req.gibbler_cache.shorter}) "
|
42
|
+
Stella.li2 " %-46s %16s ".bright % [desc, notice]
|
39
43
|
end
|
40
44
|
|
41
45
|
def update_receive_response(client_id, usecase, uri, req, counter, container)
|
@@ -62,16 +66,24 @@ module Stella::Engine
|
|
62
66
|
|
63
67
|
def update_error_execute_response_handler(client_id, ex, req, container)
|
64
68
|
Stella.le ex.message
|
65
|
-
Stella.
|
69
|
+
Stella.li3 ex.backtrace
|
66
70
|
end
|
67
71
|
|
68
72
|
def update_request_error(client_id, usecase, uri, req, params, ex)
|
69
73
|
desc = "#{usecase.desc} > #{req.desc}"
|
70
74
|
Stella.le ' Client-%s %-45s %s' % [client_id.short, desc, ex.message]
|
71
|
-
Stella.
|
75
|
+
Stella.li3 ex.backtrace
|
76
|
+
end
|
77
|
+
|
78
|
+
def update_quit_usecase client_id, msg
|
79
|
+
Stella.li4 " Client-%s QUIT %s" % [client_id.shorter, msg]
|
72
80
|
end
|
73
81
|
|
74
82
|
|
83
|
+
def update_repeat_request client_id, counter, total
|
84
|
+
Stella.li4 " Client-%s REPEAT %d of %d" % [client_id.shorter, counter, total]
|
85
|
+
end
|
86
|
+
|
75
87
|
end
|
76
88
|
end
|
77
89
|
|
data/lib/stella/engine/load.rb
CHANGED
@@ -3,211 +3,42 @@ module Stella::Engine
|
|
3
3
|
module Load
|
4
4
|
extend Stella::Engine::Base
|
5
5
|
extend self
|
6
|
-
|
6
|
+
|
7
|
+
@timers = [:do_request]
|
8
|
+
@counts = [:response_content_size]
|
9
|
+
|
10
|
+
class << self
|
11
|
+
attr_accessor :timers, :counts
|
12
|
+
end
|
13
|
+
|
7
14
|
def run(plan, opts={})
|
8
15
|
opts = {
|
9
|
-
:hosts
|
16
|
+
:hosts => [],
|
10
17
|
:clients => 1,
|
11
|
-
:
|
12
|
-
:nowait
|
13
|
-
:repetitions
|
18
|
+
:duration => nil,
|
19
|
+
:nowait => false,
|
20
|
+
:repetitions => 1
|
14
21
|
}.merge! opts
|
15
22
|
opts[:clients] = plan.usecases.size if opts[:clients] < plan.usecases.size
|
16
23
|
opts[:clients] = 1000 if opts[:clients] > 1000
|
17
24
|
|
18
|
-
Stella.
|
19
|
-
Stella.li3 "Hosts: " << opts[:hosts].join(', ')
|
20
|
-
Stella.li2 plan.pretty
|
21
|
-
|
22
|
-
counts = calculate_usecase_clients plan, opts
|
25
|
+
Stella.li3 " Options: #{opts.inspect}"
|
26
|
+
Stella.li3 " Hosts: " << opts[:hosts].join(', ')
|
23
27
|
|
24
|
-
|
25
|
-
Stella.lflush
|
26
|
-
packages = build_thread_package plan, opts, counts
|
28
|
+
#wait_for_reporter
|
27
29
|
|
28
|
-
|
29
|
-
Stella.lflush
|
30
|
+
# errors?
|
30
31
|
|
31
|
-
begin
|
32
|
-
execute_test_plan packages, opts[:repetitions]
|
33
|
-
rescue Interrupt
|
34
|
-
Stella.li "Stopping test...", $/
|
35
|
-
Stella.abort!
|
36
|
-
ensure
|
37
|
-
Stella.li "Processing statistics...", $/
|
38
|
-
Stella.lflush
|
39
|
-
generate_report plan
|
40
|
-
|
41
|
-
rep_stats = self.timeline.ranges(:build_thread_package).first
|
42
|
-
Stella.li "%20s %0.4f" % ['Prep time:', rep_stats.duration]
|
43
|
-
|
44
|
-
rep_stats = self.timeline.ranges(:execute_test_plan).first
|
45
|
-
Stella.li "%20s %0.4f" % ['Test time:', rep_stats.duration]
|
46
|
-
|
47
|
-
rep_stats = self.timeline.ranges(:generate_report).first
|
48
|
-
Stella.li "%20s %0.4f" % ['Reporting time:', rep_stats.duration]
|
49
|
-
Stella.li $/
|
50
|
-
end
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
!plan.errors?
|
55
|
-
end
|
56
|
-
|
57
|
-
|
58
|
-
protected
|
59
|
-
class ThreadPackage
|
60
|
-
attr_accessor :index
|
61
|
-
attr_accessor :client
|
62
|
-
attr_accessor :usecase
|
63
|
-
def initialize(i, c, u)
|
64
|
-
@index, @client, @usecase = i, c, u
|
65
|
-
end
|
66
32
|
end
|
67
33
|
|
68
|
-
def
|
69
|
-
|
70
|
-
plan.usecases.each_with_index do |usecase,i|
|
71
|
-
count = case opts[:clients]
|
72
|
-
when 0..9
|
73
|
-
if (opts[:clients] % plan.usecases.size > 0)
|
74
|
-
msg = "Client count does not evenly match usecase count"
|
75
|
-
raise Stella::Testplan::WackyRatio, msg
|
76
|
-
else
|
77
|
-
(opts[:clients] / plan.usecases.size)
|
78
|
-
end
|
79
|
-
else
|
80
|
-
(opts[:clients] * usecase.ratio).to_i
|
81
|
-
end
|
82
|
-
counts[usecase.gibbler_cache] = count
|
83
|
-
counts[:total] += count
|
84
|
-
end
|
85
|
-
counts
|
34
|
+
def wait_for_reporter
|
35
|
+
Benelux.reporter.wait
|
86
36
|
end
|
87
37
|
|
88
|
-
|
89
|
-
packages, pointer = Array.new(counts[:total]), 0
|
90
|
-
plan.usecases.each do |usecase|
|
91
|
-
count = counts[usecase.gibbler_cache]
|
92
|
-
Stella.ld "THREAD PACKAGE: #{usecase.desc} (#{pointer} + #{count})"
|
93
|
-
# Fill the thread_package with the contents of the block
|
94
|
-
packages.fill(pointer, count) do |index|
|
95
|
-
Stella.li3 "Creating client ##{index+1} "
|
96
|
-
client = Stella::Client.new opts[:hosts].first, index+1
|
97
|
-
client.add_observer(self)
|
98
|
-
client.enable_nowait_mode if opts[:nowait]
|
99
|
-
ThreadPackage.new(index+1, client, usecase)
|
100
|
-
end
|
101
|
-
pointer += count
|
102
|
-
end
|
103
|
-
packages.compact # TODO: Why one nil element sometimes?
|
104
|
-
end
|
105
|
-
|
106
|
-
def execute_test_plan(packages, reps=1)
|
107
|
-
Thread.ify packages, :threads => packages.size do |package|
|
108
|
-
|
109
|
-
# This thread will stay on this one track.
|
110
|
-
Benelux.current_track package.client.gibbler
|
111
|
-
Benelux.add_thread_tags :usecase => package.usecase.gibbler_cache
|
112
|
-
|
113
|
-
(1..reps).to_a.each do |rep|
|
114
|
-
Benelux.add_thread_tags :rep => rep
|
115
|
-
Stella::Engine::Load.rescue(package.client.gibbler_cache) {
|
116
|
-
print '.' if Stella.loglev == 1
|
117
|
-
stats = package.client.execute package.usecase
|
118
|
-
break if Stella.abort?
|
119
|
-
}
|
120
|
-
Benelux.remove_thread_tags :rep
|
121
|
-
end
|
122
|
-
|
123
|
-
Benelux.remove_thread_tags :usecase
|
124
|
-
end
|
125
|
-
Stella.li $/, $/
|
126
|
-
end
|
127
|
-
|
128
|
-
def generate_report(plan)
|
129
|
-
Benelux.update_all_track_timelines
|
130
|
-
global_timeline = Benelux.timeline
|
131
|
-
|
132
|
-
Stella.li $/, " %-72s ".att(:reverse) % ["#{plan.desc} (#{plan.gibbler_cache.shorter})"]
|
133
|
-
plan.usecases.uniq.each_with_index do |uc,i|
|
134
|
-
|
135
|
-
# TODO: Create Ranges object, like Stats object
|
136
|
-
# global_timeline.ranges(:do_request)[:usecase => '1111']
|
137
|
-
# The following returns globl do_request ranges.
|
138
|
-
requests = 0 #global_timeline.ranges(:do_request).size
|
139
|
-
|
140
|
-
desc = uc.desc || "Usecase ##{i+1} "
|
141
|
-
desc << " (#{uc.gibbler_cache.shorter}) "
|
142
|
-
str = ' ' << " %-66s %s %d%% ".bright.att(:reverse)
|
143
|
-
Stella.li str % [desc, '', uc.ratio_pretty]
|
144
|
-
|
145
|
-
uc.requests.each do |req|
|
146
|
-
desc = req.desc
|
147
|
-
Stella.li " %-72s ".bright % ["#{req.desc} (#{req.gibbler_cache.shorter})"]
|
148
|
-
Stella.li " %s" % [req.to_s]
|
149
|
-
global_timeline.stats.each_pair do |n,stat|
|
150
|
-
filter = {
|
151
|
-
:usecase => uc.gibbler_cache,
|
152
|
-
:request => req.gibbler_cache
|
153
|
-
}
|
154
|
-
stats = stat[filter]
|
155
|
-
Stella.li ' %-30s %.3f <= %.3f >= %.3f; %.3f(SD) %d(N)' % [n, stats.min, stats.mean, stats.max, stats.sd, stats.n]
|
156
|
-
Stella.lflush
|
157
|
-
end
|
158
|
-
Stella.li $/
|
159
|
-
end
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
|
164
|
-
def update_prepare_request(client_id, usecase, req, counter)
|
165
|
-
|
166
|
-
end
|
167
|
-
|
168
|
-
def update_receive_response(client_id, usecase, uri, req, counter, container)
|
169
|
-
desc = "#{usecase.desc} > #{req.desc}"
|
170
|
-
Stella.li2 ' Client-%s %3d %-6s %-45s' % [client_id.shorter, container.status, req.http_method, uri]
|
171
|
-
end
|
172
|
-
|
173
|
-
def update_execute_response_handler(client_id, req, container)
|
174
|
-
end
|
175
|
-
|
176
|
-
def update_error_execute_response_handler(client_id, ex, req, container)
|
177
|
-
desc = "#{container.usecase.desc} > #{req.desc}"
|
178
|
-
Stella.li $/ if Stella.loglev == 1
|
179
|
-
Stella.le ' Client-%s %-45s %s' % [client_id.shorter, desc, ex.message]
|
180
|
-
Stella.ld ex.backtrace
|
181
|
-
end
|
182
|
-
|
183
|
-
def update_request_error(client_id, usecase, uri, req, params, ex)
|
184
|
-
desc = "#{usecase.desc} > #{req.desc}"
|
185
|
-
Stella.li $/ if Stella.loglev == 1
|
186
|
-
Stella.le ' Client-%s %-45s %s' % [client_id.shorter, desc, ex.message]
|
187
|
-
Stella.ld ex.backtrace
|
188
|
-
end
|
189
|
-
|
190
|
-
|
191
|
-
def self.rescue(client_id, &blk)
|
192
|
-
blk.call
|
193
|
-
rescue => ex
|
194
|
-
Stella.le ' Error in Client-%s: %s' % [client_id.shorter, ex.message]
|
195
|
-
Stella.ld ex.backtrace
|
196
|
-
end
|
38
|
+
protected
|
197
39
|
|
40
|
+
#Benelux.add_timer Stella::Engine::Load, :build_thread_package
|
198
41
|
|
199
|
-
Benelux.add_timer Stella::Engine::Load, :generate_report
|
200
|
-
Benelux.add_timer Stella::Engine::Load, :build_thread_package
|
201
|
-
Benelux.add_timer Stella::Engine::Load, :execute_test_plan
|
202
|
-
Benelux.add_timer Stella::Client, :execute_response_handler
|
203
42
|
end
|
204
43
|
end
|
205
44
|
|
206
|
-
__END__
|
207
|
-
|
208
|
-
|
209
|
-
$ stella verify -p examples/basic/plan.rb http://localhost:3114
|
210
|
-
$ stella load -p examples/basic/plan.rb http://localhost:3114
|
211
|
-
$ stella remote-load -p examples/basic/plan.rb http://localhost:3114
|
212
|
-
$ stella remote-verify -p examples/basic/plan.rb http://localhost:3114
|
213
|
-
|
@@ -0,0 +1,23 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
class Numeric
|
4
|
+
# TODO: Use 1024
|
5
|
+
def to_bytes
|
6
|
+
args = case self.abs.to_i
|
7
|
+
when 0..1_000
|
8
|
+
[(self).to_s, 'B']
|
9
|
+
when 1_000..1_000_000
|
10
|
+
[(self / 1000).to_s, 'KB']
|
11
|
+
when 1_000_000..1_000_000_000
|
12
|
+
[(self / (1000**2)).to_s, 'MB']
|
13
|
+
when 1_000_000_000..1_000_000_000_000
|
14
|
+
[(self / (1000**3)).to_s, 'GB']
|
15
|
+
when 1_000_000_000_000..1_000_000_000_000_000
|
16
|
+
[(self / (1000**4)).to_s, 'TB']
|
17
|
+
else
|
18
|
+
[self, 'B']
|
19
|
+
end
|
20
|
+
'%3.2f%s' % args
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
data/lib/stella/testplan.rb
CHANGED
@@ -14,7 +14,7 @@ class Testplan
|
|
14
14
|
attr_reader :stats
|
15
15
|
|
16
16
|
def initialize(uris=[], opts={})
|
17
|
-
@desc, @usecases = "
|
17
|
+
@desc, @usecases = "Test plan", []
|
18
18
|
@testplan_current_ratio = 0
|
19
19
|
@stats = Stella::Testplan::Stats.new
|
20
20
|
|
@@ -27,6 +27,8 @@ class Testplan
|
|
27
27
|
uri.path = '/' if uri.path.nil? || uri.path.empty?
|
28
28
|
req = usecase.add_request :get, uri.path
|
29
29
|
req.wait = opts[:delay] if opts[:delay]
|
30
|
+
req.gibbler
|
31
|
+
req.freeze
|
30
32
|
end
|
31
33
|
self.add_usecase usecase
|
32
34
|
end
|
@@ -43,12 +45,6 @@ class Testplan
|
|
43
45
|
plan
|
44
46
|
end
|
45
47
|
|
46
|
-
# Were there any errors in any of the usecases?
|
47
|
-
def errors?
|
48
|
-
Stella.ld "TODO: tally use case errors"
|
49
|
-
false
|
50
|
-
end
|
51
|
-
|
52
48
|
def check!
|
53
49
|
# Adjust ratios if necessary
|
54
50
|
needy = @usecases.select { |u| u.ratio == -1 }
|
@@ -93,15 +89,18 @@ class Testplan
|
|
93
89
|
@desc
|
94
90
|
end
|
95
91
|
|
96
|
-
def pretty
|
92
|
+
def pretty(long=false)
|
97
93
|
str = []
|
98
|
-
|
94
|
+
dig = long ? self.gibbler_cache : self.gibbler_cache.shorter
|
95
|
+
str << " %-66s ".att(:reverse) % ["#{@desc} (#{dig})"]
|
99
96
|
@usecases.each_with_index do |uc,i|
|
97
|
+
dig = long ? uc.gibbler_cache : uc.gibbler_cache.shorter
|
100
98
|
desc = uc.desc || "Usecase ##{i+1}"
|
101
|
-
desc += " (#{
|
99
|
+
desc += " (#{dig}) "
|
102
100
|
str << (' ' << " %-61s %s%% ".att(:reverse).bright) % [desc, uc.ratio_pretty]
|
103
101
|
requests = uc.requests.each do |r|
|
104
|
-
|
102
|
+
dig = long ? r.gibbler_cache : r.gibbler_cache.shorter
|
103
|
+
str << " %-62s".bright % ["#{r.desc} (#{dig})"]
|
105
104
|
str << " %s" % [r]
|
106
105
|
if Stella.loglev > 2
|
107
106
|
[:wait].each { |i| str << " %s: %s" % [i, r.send(i)] }
|
data/lib/stella/version.rb
CHANGED
data/stella.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
@spec = Gem::Specification.new do |s|
|
2
2
|
s.name = "stella"
|
3
3
|
s.rubyforge_project = 'stella'
|
4
|
-
s.version = "0.7.0.
|
4
|
+
s.version = "0.7.0.014"
|
5
5
|
s.summary = "Stella: Perform load tests on your web applications with beauty and brute strength."
|
6
6
|
s.description = s.summary
|
7
7
|
s.author = "Delano Mandelbaum"
|
@@ -15,7 +15,7 @@
|
|
15
15
|
|
16
16
|
s.executables = %w[stella]
|
17
17
|
|
18
|
-
s.add_dependency 'benelux', '>= 0.
|
18
|
+
s.add_dependency 'benelux', '>= 0.4.0'
|
19
19
|
s.add_dependency 'drydock', '>= 0.6.8'
|
20
20
|
s.add_dependency 'gibbler', '>= 0.6.3'
|
21
21
|
s.add_dependency 'storable', '>= 0.5.7'
|
@@ -34,8 +34,6 @@
|
|
34
34
|
examples/essentials/logo.png
|
35
35
|
examples/essentials/plan.rb
|
36
36
|
examples/essentials/search_terms.csv
|
37
|
-
examples/example_webapp.rb
|
38
|
-
examples/example_webapp.ru
|
39
37
|
examples/exceptions/plan.rb
|
40
38
|
lib/stella.rb
|
41
39
|
lib/stella/cli.rb
|
@@ -54,6 +52,7 @@
|
|
54
52
|
lib/stella/exceptions.rb
|
55
53
|
lib/stella/guidelines.rb
|
56
54
|
lib/stella/mixins.rb
|
55
|
+
lib/stella/mixins/numeric.rb
|
57
56
|
lib/stella/mixins/thread.rb
|
58
57
|
lib/stella/stats.rb
|
59
58
|
lib/stella/testplan.rb
|
@@ -64,6 +63,8 @@
|
|
64
63
|
lib/stella/version.rb
|
65
64
|
lib/threadify.rb
|
66
65
|
stella.gemspec
|
66
|
+
support/sample_webapp/app.rb
|
67
|
+
support/sample_webapp/config.ru
|
67
68
|
support/useragents.txt
|
68
69
|
vendor/httpclient-2.1.5.2/httpclient.rb
|
69
70
|
vendor/httpclient-2.1.5.2/httpclient/auth.rb
|
@@ -1,16 +1,27 @@
|
|
1
1
|
#!/usr/bin/ruby
|
2
2
|
|
3
|
+
# Stella Sample Web Application
|
4
|
+
#
|
5
|
+
# This application plays nicely with the example
|
6
|
+
# test plans (see examples/).
|
7
|
+
#
|
8
|
+
# Usage:
|
9
|
+
#
|
10
|
+
# $ ruby support/sample_webapp/app.rb
|
11
|
+
# OR
|
12
|
+
# $ thin -R support/sample_webapp/config.ru -p 3114 start
|
13
|
+
#
|
14
|
+
|
3
15
|
require "rubygems"
|
4
16
|
require "sinatra"
|
17
|
+
require "yaml"
|
5
18
|
|
6
|
-
|
7
|
-
|
8
|
-
set :
|
9
|
-
set :
|
10
|
-
set :
|
11
|
-
set :
|
12
|
-
set :reload => true
|
13
|
-
set :max_listings => 1000
|
19
|
+
set :run => ($0 == __FILE__)
|
20
|
+
set :environment => :development
|
21
|
+
set :dump_errors => true
|
22
|
+
set :port => 3114
|
23
|
+
set :reload => true
|
24
|
+
set :max_listings => 1000
|
14
25
|
|
15
26
|
LISTINGS = [
|
16
27
|
{ :id => 1000, :name => 'John West Smoked Oysters', :city => 'Toronto' },
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# Stella Sample Web Application
|
2
|
+
#
|
3
|
+
# This application plays nicely with the example
|
4
|
+
# test plans (see examples/).
|
5
|
+
#
|
6
|
+
# Usage:
|
7
|
+
#
|
8
|
+
# $ thin -R support/sample_webapp/config.ru -p 3114 start
|
9
|
+
|
10
|
+
dir = ::File.expand_path(::File.dirname(__FILE__))
|
11
|
+
require ::File.join(dir, 'app.rb')
|
12
|
+
run Sinatra::Application
|
@@ -773,10 +773,10 @@ private
|
|
773
773
|
retry_count -= 1
|
774
774
|
end
|
775
775
|
end
|
776
|
-
|
777
776
|
res
|
778
777
|
end
|
779
|
-
|
778
|
+
|
779
|
+
# NOTE: Not tracked by Benelux / Stella
|
780
780
|
def do_request_async(method, uri, query, body, extheader)
|
781
781
|
conn = Connection.new
|
782
782
|
t = Thread.new(conn) { |tconn|
|
@@ -954,10 +954,10 @@ private
|
|
954
954
|
res = HTTP::Message.new_response(content)
|
955
955
|
@debug_dev << "= Request\n\n" if @debug_dev
|
956
956
|
sess = @session_manager.query(req, proxy)
|
957
|
-
|
958
957
|
res.peer_cert = sess.ssl_peer_cert
|
959
958
|
@debug_dev << "\n\n= Response\n\n" if @debug_dev
|
960
959
|
do_get_header(req, res, sess)
|
960
|
+
res.header.size = sess.headers_bytes
|
961
961
|
conn.push(res)
|
962
962
|
sess.get_body do |part|
|
963
963
|
if block
|
@@ -1005,6 +1005,7 @@ private
|
|
1005
1005
|
|
1006
1006
|
def do_get_header(req, res, sess)
|
1007
1007
|
res.version, res.status, res.reason, headers = sess.get_header
|
1008
|
+
#Benelux.thread_timeline.add_count :response_header_size, res.header
|
1008
1009
|
headers.each do |key, value|
|
1009
1010
|
res.header.add(key, value)
|
1010
1011
|
end
|
@@ -95,6 +95,9 @@ module HTTP
|
|
95
95
|
|
96
96
|
# Represents HTTP message header.
|
97
97
|
class Headers
|
98
|
+
# Size of Header content (original string). Set from session.headers_bytes.
|
99
|
+
attr_accessor :size
|
100
|
+
|
98
101
|
# HTTP version in a HTTP header. Float.
|
99
102
|
attr_accessor :http_version
|
100
103
|
# Size of body. nil when size is unknown (e.g. chunked response).
|
@@ -429,6 +432,8 @@ module HTTP
|
|
429
432
|
# If no dev (the second argument) given, this method returns a dumped
|
430
433
|
# String.
|
431
434
|
def dump(header = '', dev = '')
|
435
|
+
#header_size = header.size
|
436
|
+
#body_size = 0
|
432
437
|
if @body.is_a?(Parts)
|
433
438
|
dev << header
|
434
439
|
buf = ''
|
@@ -436,17 +441,24 @@ module HTTP
|
|
436
441
|
if Message.file?(part)
|
437
442
|
reset_pos(part)
|
438
443
|
while !part.read(@chunk_size, buf).nil?
|
444
|
+
#body_size += buf.size
|
439
445
|
dev << buf
|
440
446
|
end
|
441
447
|
else
|
448
|
+
#body_size += part.size
|
442
449
|
dev << part
|
443
450
|
end
|
444
451
|
end
|
445
452
|
elsif @body
|
453
|
+
#body_size = @body.size
|
446
454
|
dev << header + @body
|
447
455
|
else
|
448
456
|
dev << header
|
449
457
|
end
|
458
|
+
|
459
|
+
#Benelux.thread_timeline.add_count :data_sent, header_size, :type => :header
|
460
|
+
#Benelux.thread_timeline.add_count :data_sent, body_size, :type => :body
|
461
|
+
|
450
462
|
dev
|
451
463
|
end
|
452
464
|
|
@@ -726,7 +738,7 @@ module HTTP
|
|
726
738
|
# Returns true if the given HTTP version allows keep alive connection.
|
727
739
|
# version:: Float
|
728
740
|
def keep_alive_enabled?(version)
|
729
|
-
version >= 1.1
|
741
|
+
version.to_f >= 1.1
|
730
742
|
end
|
731
743
|
|
732
744
|
# Returns true if the given query (or body) has a multiple parameter.
|
@@ -800,6 +812,7 @@ module HTTP
|
|
800
812
|
# dev needs to respond to <<.
|
801
813
|
def dump(dev = '')
|
802
814
|
str = header.dump + CRLF
|
815
|
+
body_size = body ? body.size : 0
|
803
816
|
if header.chunked
|
804
817
|
dev = body.dump_chunked(str, dev)
|
805
818
|
elsif body
|
@@ -481,7 +481,10 @@ class HTTPClient
|
|
481
481
|
attr_accessor :ssl_config
|
482
482
|
attr_reader :ssl_peer_cert
|
483
483
|
attr_accessor :test_loopback_http_response
|
484
|
-
|
484
|
+
|
485
|
+
attr_reader :headers_bytes
|
486
|
+
attr_reader :content_bytes
|
487
|
+
|
485
488
|
def initialize(client, dest, agent_name, from)
|
486
489
|
@client = client
|
487
490
|
@dest = dest
|
@@ -512,7 +515,8 @@ class HTTPClient
|
|
512
515
|
@status = nil
|
513
516
|
@reason = nil
|
514
517
|
@headers = []
|
515
|
-
|
518
|
+
@headers_bytes = 0
|
519
|
+
@content_bytes = 0
|
516
520
|
@socket = nil
|
517
521
|
@readbuf = nil
|
518
522
|
end
|
@@ -752,6 +756,7 @@ class HTTPClient
|
|
752
756
|
if initial_line.nil?
|
753
757
|
raise KeepAliveDisconnected.new
|
754
758
|
end
|
759
|
+
@headers_bytes += initial_line.size
|
755
760
|
if StatusParseRegexp !~ initial_line
|
756
761
|
@version = '0.9'
|
757
762
|
@status = nil
|
@@ -774,6 +779,7 @@ class HTTPClient
|
|
774
779
|
key, value = line.split(/\s*:\s*/, 2)
|
775
780
|
parse_keepalive_header(key, value)
|
776
781
|
@headers << [key, value]
|
782
|
+
@headers_bytes += line.size
|
777
783
|
end
|
778
784
|
end while (@version == '1.1' && @status == 100)
|
779
785
|
end
|
@@ -811,6 +817,7 @@ class HTTPClient
|
|
811
817
|
end
|
812
818
|
if buf && buf.length > 0
|
813
819
|
@content_length -= buf.length
|
820
|
+
@content_bytes += buf.length
|
814
821
|
yield buf
|
815
822
|
else
|
816
823
|
@content_length = 0
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stella
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.0.
|
4
|
+
version: 0.7.0.014
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Delano Mandelbaum
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-10-
|
12
|
+
date: 2009-10-06 00:00:00 -04:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -20,7 +20,7 @@ dependencies:
|
|
20
20
|
requirements:
|
21
21
|
- - ">="
|
22
22
|
- !ruby/object:Gem::Version
|
23
|
-
version: 0.
|
23
|
+
version: 0.4.0
|
24
24
|
version:
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: drydock
|
@@ -92,8 +92,6 @@ files:
|
|
92
92
|
- examples/essentials/logo.png
|
93
93
|
- examples/essentials/plan.rb
|
94
94
|
- examples/essentials/search_terms.csv
|
95
|
-
- examples/example_webapp.rb
|
96
|
-
- examples/example_webapp.ru
|
97
95
|
- examples/exceptions/plan.rb
|
98
96
|
- lib/stella.rb
|
99
97
|
- lib/stella/cli.rb
|
@@ -112,6 +110,7 @@ files:
|
|
112
110
|
- lib/stella/exceptions.rb
|
113
111
|
- lib/stella/guidelines.rb
|
114
112
|
- lib/stella/mixins.rb
|
113
|
+
- lib/stella/mixins/numeric.rb
|
115
114
|
- lib/stella/mixins/thread.rb
|
116
115
|
- lib/stella/stats.rb
|
117
116
|
- lib/stella/testplan.rb
|
@@ -122,6 +121,8 @@ files:
|
|
122
121
|
- lib/stella/version.rb
|
123
122
|
- lib/threadify.rb
|
124
123
|
- stella.gemspec
|
124
|
+
- support/sample_webapp/app.rb
|
125
|
+
- support/sample_webapp/config.ru
|
125
126
|
- support/useragents.txt
|
126
127
|
- vendor/httpclient-2.1.5.2/httpclient.rb
|
127
128
|
- vendor/httpclient-2.1.5.2/httpclient/auth.rb
|
data/examples/example_webapp.ru
DELETED