kronk 1.8.7 → 1.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,82 @@
1
+ class Kronk
2
+
3
+ ##
4
+ # Builder for the body of a multipart request.
5
+
6
+ class Multipart
7
+
8
+ # An array of parts for the multipart body.
9
+ attr_reader :parts
10
+
11
+ # The separator used between parts.
12
+ attr_reader :boundary
13
+
14
+
15
+ def initialize boundary
16
+ @boundary = boundary
17
+ @parts = []
18
+ end
19
+
20
+
21
+ ##
22
+ # Add a new part to the body.
23
+
24
+ def add name, value, headers=nil
25
+ headers ||= {}
26
+
27
+ headers['content-disposition'] = "form-data; name=\"#{name}\""
28
+
29
+ if value.respond_to?(:path)
30
+ headers['content-disposition'] <<
31
+ "; filename=\"#{File.basename value.path}\""
32
+
33
+ headers['Content-Type'] ||= MIME::Types.of(value.path)[0]
34
+ headers['Content-Type'] &&= headers['Content-Type'].to_s
35
+ end
36
+
37
+ if value.respond_to?(:read)
38
+ headers['Content-Type'] ||= "application/octet-stream"
39
+ headers['Content-Transfer-Encoding'] ||= 'binary'
40
+ end
41
+
42
+ parts << [headers, value]
43
+ end
44
+
45
+
46
+ ##
47
+ # Convert the instance into a MultipartIO instance.
48
+
49
+ def to_io
50
+ io = Kronk::MultipartIO.new
51
+ buff = ""
52
+
53
+ parts.each do |(headers, value)|
54
+ buff << "--#{@boundary}\r\n"
55
+ buff << "content-disposition: #{headers['content-disposition']}\r\n"
56
+
57
+ headers.each do |hname, hvalue|
58
+ next if hname == 'content-disposition'
59
+ hvalue = hvalue.to_s.inspect if hvalue.to_s.index ":"
60
+ buff << "#{hname}: #{hvalue}\r\n"
61
+ end
62
+
63
+ buff << "\r\n"
64
+
65
+ if value.respond_to?(:read)
66
+ io.add buff.dup
67
+ io.add value
68
+ buff.replace ""
69
+ else
70
+ buff << value.to_s
71
+ end
72
+
73
+ buff << "\r\n"
74
+ end
75
+
76
+ buff << "--#{@boundary}--"
77
+ io.add buff
78
+
79
+ io
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,88 @@
1
+ class Kronk
2
+
3
+ class MultipartIO
4
+
5
+ attr_reader :parts, :curr_part
6
+
7
+ def initialize *parts
8
+ @parts = []
9
+ @curr_part = 0
10
+
11
+ parts.each do |part|
12
+ add part
13
+ end
14
+ end
15
+
16
+
17
+ def add part
18
+ if String === part
19
+ @parts << StringIO.new(part)
20
+
21
+ elsif part.respond_to?(:read)
22
+ @parts << part
23
+
24
+ else
25
+ raise ArgumentError, "Invalid part #{part.inspect}"
26
+ end
27
+
28
+ @curr_part ||= @parts.length - 1
29
+ @parts.last
30
+ end
31
+
32
+
33
+ def close
34
+ @parts.each(&:close)
35
+ nil
36
+ end
37
+
38
+
39
+ def read bytes=nil
40
+ return read_all if bytes.nil?
41
+ return if @parts.empty? || eof?
42
+
43
+ buff = ""
44
+
45
+ until @curr_part.nil?
46
+ bytes = bytes - buff.bytes.count
47
+ buff << @parts[@curr_part].read(bytes).to_s
48
+ break if buff.bytes.count >= bytes
49
+
50
+ @curr_part += 1
51
+ @curr_part = nil if @curr_part >= @parts.length
52
+ end
53
+
54
+ return if buff.empty?
55
+ buff
56
+ end
57
+
58
+
59
+ def read_all
60
+ return "" if eof?
61
+
62
+ out = @parts[@curr_part..-1].inject("") do |out, curr|
63
+ @curr_part += 1
64
+ out << curr.read
65
+ end
66
+
67
+ @curr_part = nil
68
+ out
69
+ end
70
+
71
+
72
+ def eof?
73
+ @curr_part.nil?
74
+ end
75
+
76
+
77
+ def size
78
+ total = 0
79
+
80
+ @parts.each do |part|
81
+ return nil unless part.respond_to?(:size) && part.size
82
+ total += part.size
83
+ end
84
+
85
+ total
86
+ end
87
+ end
88
+ end
@@ -16,6 +16,7 @@ class Kronk
16
16
  klass =
17
17
  case type.to_s
18
18
  when /^(Player::)?benchmark$/i then Benchmark
19
+ when /^(Player::)?download$/i then Download
19
20
  when /^(Player::)?stream$/i then Stream
20
21
  when /^(Player::)?suite$/i then Suite
21
22
  when /^(Player::)?tsv$/i then TSV
@@ -58,7 +59,6 @@ class Kronk
58
59
  on(:interrupt){
59
60
  interrupt and return if respond_to?(:interrupt)
60
61
  complete if respond_to?(:complete)
61
- kill
62
62
  exit 2
63
63
  }
64
64
  on(:start){
@@ -17,16 +17,17 @@ class Kronk
17
17
 
18
18
  class ResultSet
19
19
 
20
- attr_accessor :total_time
20
+ attr_accessor :total_time, :err_count
21
21
 
22
22
  attr_reader :byterate, :count, :fastest, :precision,
23
- :slowest, :total_bytes, :err_count
23
+ :slowest, :total_bytes
24
24
 
25
25
  def initialize
26
26
  @times = Hash.new(0)
27
27
  @count = 0
28
28
  @r5XX = 0
29
29
  @r4XX = 0
30
+ @r3XX = 0
30
31
  @err_count = 0
31
32
 
32
33
  @precision = 3
@@ -49,15 +50,18 @@ class Kronk
49
50
  @times[time] += 1
50
51
  @count += 1
51
52
 
52
- @r5XX += 1 if resp.code =~ /^5\d\d$/
53
- @r4XX += 1 if resp.code =~ /^4\d\d$/
53
+ case resp.code[0, 1]
54
+ when "5" then @r5XX += 1
55
+ when "4" then @r4XX += 1
56
+ when "3" then @r3XX += 1
57
+ end
54
58
 
55
59
  @slowest = time if !@slowest || @slowest < time
56
60
  @fastest = time if !@fastest || @fastest > time
57
61
 
58
62
  log_req resp.request, time if resp.request
59
63
 
60
- @total_bytes += resp.raw.bytes.count
64
+ @total_bytes += resp.total_bytes
61
65
 
62
66
  @byterate = (@byterate * (@count-1) + resp.byterate) / @count
63
67
  end
@@ -68,7 +72,6 @@ class Kronk
68
72
  uri.query = nil
69
73
  uri = "#{req.http_method} #{uri.to_s}"
70
74
 
71
- # TODO: Keep the number in @paths to 10.
72
75
  @paths[uri] ||= [0, 0]
73
76
  pcount = @paths[uri][1] + 1
74
77
  @paths[uri][0] = (@paths[uri][0] * @paths[uri][1] + time) / pcount
@@ -78,6 +81,7 @@ class Kronk
78
81
 
79
82
 
80
83
  def deviation
84
+ return 0 if @count == 0
81
85
  return @deviation if @deviation
82
86
 
83
87
  mdiff = @times.to_a.inject(0) do |sum, (time, count)|
@@ -89,11 +93,13 @@ class Kronk
89
93
 
90
94
 
91
95
  def mean
96
+ return 0 if @count == 0
92
97
  @mean ||= (self.sum / @count).round @precision
93
98
  end
94
99
 
95
100
 
96
101
  def median
102
+ return 0 if @count == 0
97
103
  @median ||= ((@slowest + @fastest) / 2).round @precision
98
104
  end
99
105
 
@@ -127,6 +133,7 @@ class Kronk
127
133
 
128
134
 
129
135
  def req_per_sec
136
+ return 0 if @count == 0
130
137
  (@count / @total_time).round @precision
131
138
  end
132
139
 
@@ -146,10 +153,23 @@ class Kronk
146
153
  end
147
154
 
148
155
 
156
+ def clear_caches
157
+ @percentages = nil
158
+ @slowest_reqs = nil
159
+ @sum = nil
160
+ @mean = nil
161
+ @median = nil
162
+ @deviation = nil
163
+ end
164
+
165
+
149
166
  def to_s
167
+ clear_caches
168
+
150
169
  out = <<-STR
151
170
 
152
171
  Completed: #{@count}
172
+ 300s: #{@r3XX}
153
173
  400s: #{@r4XX}
154
174
  500s: #{@r5XX}
155
175
  Errors: #{@err_count}
@@ -158,11 +178,11 @@ Total Bytes: #{@total_bytes}
158
178
  Transfer Rate: #{self.transfer_rate} Kbytes/sec
159
179
 
160
180
  Connection Times (ms)
161
- Min: #{self.fastest}
181
+ Min: #{self.fastest || 0}
162
182
  Mean: #{self.mean}
163
183
  [+/-sd]: #{self.deviation}
164
184
  Median: #{self.median}
165
- Max: #{self.slowest}
185
+ Max: #{self.slowest || 0}
166
186
 
167
187
  Request Percentages (ms)
168
188
  50% #{self.percentages[50]}
@@ -188,11 +208,15 @@ Avg. Slowest Requests (ms, count)
188
208
 
189
209
 
190
210
  def start
191
- @res_count = 0
192
- @results = []
193
- @div = @number / 10 if @number
194
- @div = 100 if !@div || @div < 10
195
- puts "Benchmarking..."
211
+ @interactive = $stdout.isatty
212
+ @res_count = 0
213
+ @results = [ResultSet.new]
214
+ @div = @number / 10 if @number
215
+ @div = 100 if !@div || @div < 10
216
+ @last_print = Time.now
217
+ @line_count = 0
218
+
219
+ puts "Benchmarking..." unless @interactive
196
220
  end
197
221
 
198
222
 
@@ -204,11 +228,21 @@ Avg. Slowest Requests (ms, count)
204
228
  end
205
229
 
206
230
  @res_count += 1
207
- puts "#{@res_count} requests" if @res_count % @div == 0
231
+
232
+ if @interactive
233
+ render if Time.now - @last_print > 0.5
234
+ else
235
+ puts "#{@res_count} requests" if @res_count % @div == 0
236
+ end
208
237
  end
209
238
  end
210
239
 
211
240
 
241
+ def clear_screen
242
+ $stdout.print "\e[2K\e[1A" * @line_count
243
+ end
244
+
245
+
212
246
  def error err, kronk
213
247
  @mutex.synchronize do
214
248
  @res_count += 1
@@ -220,32 +254,43 @@ Avg. Slowest Requests (ms, count)
220
254
 
221
255
 
222
256
  def complete
223
- puts "Finished!"
224
-
225
- render_head
226
- render_body
257
+ puts "Finished!" unless @interactive
227
258
 
259
+ render
228
260
  true
229
261
  end
230
262
 
231
263
 
232
- def render_body
233
- @results.each{|res| res.total_time = @stop_time - @start_time }
264
+ def render
265
+ clear_screen if @interactive
266
+ out = "#{head}#{body}\n"
267
+ @line_count = out.to_s.split("\n").length
268
+ @last_print = Time.now
269
+ $stdout.print out
270
+ $stdout.flush
271
+ end
272
+
273
+
274
+ def body
275
+ @results.each{|res| res.total_time = Time.now - @start_time }
234
276
 
235
277
  if @results.length > 1
236
- puts Diff.new(@results[0].to_s, @results[1].to_s).formatted
278
+ Diff.new(@results[0].to_s, @results[1].to_s, :context => false).
279
+ formatted
237
280
  else
238
- puts @results.first.to_s
281
+ @results.first.to_s
239
282
  end
240
283
  end
241
284
 
242
285
 
243
- def render_head
244
- puts <<-STR
286
+ def head
287
+ <<-STR
245
288
 
246
289
  Benchmark Time: #{(Time.now - @start_time).to_f} sec
247
290
  Number of Requests: #{@count}
248
291
  Concurrency: #{@qps ? "#{@qps} qps" : @concurrency}
292
+ #{"Current Connections: #{Kronk::HTTP.conn_count}\n" if @interactive}\
293
+ Total Connections: #{Kronk::HTTP.total_conn}
249
294
  STR
250
295
  end
251
296
  end
@@ -0,0 +1,87 @@
1
+ class Kronk
2
+
3
+ ##
4
+ # Outputs Player results as a list of files.
5
+ #
6
+ # player = Player::Download.new :dir => "foo/bar"
7
+ #
8
+ # Note: This output class will not render errors.
9
+
10
+ class Player::Download < Player
11
+
12
+ # Directory to write the files to.
13
+ attr_accessor :dir
14
+
15
+ def initialize opts={}
16
+ super
17
+
18
+ @counter = 0
19
+ @counter_mutex = Mutex.new
20
+
21
+ require 'fileutils'
22
+
23
+ default_dir = File.join Dir.pwd, "kronk-#{Time.now.to_i}"
24
+ @dir = File.expand_path(opts[:dir] || default_dir)
25
+
26
+ FileUtils.mkdir_p @dir
27
+ end
28
+
29
+
30
+ def result kronk
31
+ output, name, ext =
32
+ if kronk.diff
33
+ name = make_name kronk.responses[0].uri
34
+ name = "#{name}-#{make_name kronk.responses[1].uri}"
35
+ [kronk.diff.formatted, name, "diff"]
36
+
37
+ elsif kronk.response
38
+ name = make_name kronk.response.uri
39
+ [kronk.response.stringify, name, ext_for(kronk.response)]
40
+ end
41
+
42
+ return unless output && !output.empty?
43
+
44
+ filename = nil
45
+
46
+ @counter_mutex.synchronize do
47
+ @counter += 1
48
+ filename = File.join(@dir, "#{@counter}-#{name}.#{ext}")
49
+ end
50
+
51
+ File.open(filename, "w"){|file| file.write output}
52
+
53
+ @mutex.synchronize do
54
+ $stdout.puts filename
55
+ end
56
+ end
57
+
58
+
59
+ def make_name uri
60
+ return unless uri
61
+ parts = uri.path.to_s.sub(%r{^/}, "").split("/")
62
+ parts = parts[-2..-1] || parts
63
+ parts.join("-").sub(%r{[?.].*$}, "")
64
+ end
65
+
66
+
67
+ def ext_for resp
68
+ if will_parse(resp)
69
+ Kronk.config[:render_lang].to_s == "ruby" ? "rb" : "json"
70
+
71
+ elsif resp.stringify_opts[:show_headers]
72
+ "http"
73
+
74
+ else
75
+ resp.ext
76
+ end
77
+ end
78
+
79
+
80
+ def will_parse resp
81
+ (resp.parser || resp.stringify_opts[:parser] ||
82
+ (resp.stringify_opts[:no_body] && resp.stringify_opts[:show_headers])) &&
83
+ !resp.stringify_opts[:raw]
84
+ end
85
+ end
86
+ end
87
+