kronk 1.7.8 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,19 +1,19 @@
1
1
  class Kronk
2
2
 
3
3
  # Generic Request exception.
4
- class Exception < ::Exception; end
4
+ class Error < ::StandardError; end
5
5
 
6
6
  # Raised when parsing fails.
7
- class ParserError < Exception; end
7
+ class ParserError < Error; end
8
8
 
9
9
  # Raised when the URI was not resolvable.
10
- class NotFoundError < Exception; end
10
+ class NotFoundError < Error; end
11
11
 
12
12
  # Raised when HTTP times out.
13
- class TimeoutError < Exception; end
13
+ class TimeoutError < Error; end
14
14
 
15
15
  # Raised when a missing (but non-mandatory) dependency can't be loaded.
16
- class MissingDependency < Exception; end
16
+ class MissingDependency < Error; end
17
17
 
18
18
 
19
19
  # Config directory.
@@ -80,7 +80,6 @@ class Kronk
80
80
 
81
81
  # Default config to use.
82
82
  DEFAULT_CONFIG = {
83
- :async => 'auto',
84
83
  :content_types => DEFAULT_CONTENT_TYPES.dup,
85
84
  :cache_file => DEFAULT_CACHE_FILE,
86
85
  :context => 3,
data/lib/kronk/diff.rb CHANGED
@@ -96,6 +96,7 @@ class Kronk
96
96
  @meta.last[0] = str1.meta[0] if str1.respond_to?(:meta)
97
97
  @meta.last[1] = str2.meta[0] if str2.respond_to?(:meta)
98
98
  end
99
+
99
100
  diff_ary.concat arr1[c[1], c[0]]
100
101
 
101
102
  last_i1 = c[1] + c[0]
@@ -161,8 +162,7 @@ class Kronk
161
162
 
162
163
  arr2_map = {}
163
164
  arr2.each_with_index do |line, j|
164
- arr2_map[line] ||= []
165
- arr2_map[line] << j
165
+ (arr2_map[line] ||= []) << j
166
166
  end
167
167
 
168
168
  arr1.each_with_index do |line, i|
@@ -184,14 +184,7 @@ class Kronk
184
184
  end
185
185
 
186
186
  len = j - start_j
187
-
188
- sequences[len] ||= []
189
- sequences[len] << [len, i, start_j]
190
-
191
- if len > 1
192
- sequences[len-1] ||= []
193
- sequences[len-1] << [(len-1), i, start_j]
194
- end
187
+ (sequences[len] ||= []) << [len, i, start_j]
195
188
  end
196
189
  end
197
190
 
@@ -239,11 +232,17 @@ class Kronk
239
232
 
240
233
  private
241
234
 
242
- def yield_sequences sequences, dist=0, &block # :nodoc:
243
- while sequences.length > dist
235
+ def yield_sequences sequences, &block # :nodoc:
236
+ while sequences.length > 0
244
237
  item = sequences.pop
245
238
  next unless item
246
- item.each(&block)
239
+ item.each do |seq|
240
+ resp = yield seq
241
+ if !resp && seq[0] > 1
242
+ seq[0] = seq[0] - 1
243
+ (sequences[seq[0]] ||= []) << seq
244
+ end
245
+ end
247
246
  end
248
247
  end
249
248
  end
@@ -127,7 +127,7 @@ class Kronk::Diff
127
127
  Kronk.find_const name
128
128
 
129
129
  rescue NameError
130
- raise Kronk::Exception, "No such formatter: #{name.inspect}"
130
+ raise Kronk::Error, "No such formatter: #{name.inspect}"
131
131
  end
132
132
 
133
133
 
data/lib/kronk/http.rb ADDED
@@ -0,0 +1,105 @@
1
+ class Kronk
2
+
3
+ class HTTPBadResponse < Net::HTTPBadResponse; end
4
+
5
+ ##
6
+ # Wrapper for Net::HTTP
7
+
8
+ class HTTP < Net::HTTP
9
+
10
+ def request(req, body=nil, opts={}, &block) # :yield: +response+
11
+ unless started?
12
+ start {
13
+ req['connection'] ||= 'close'
14
+ return request(req, body, &block)
15
+ }
16
+ end
17
+ if proxy_user()
18
+ req.proxy_basic_auth proxy_user(), proxy_pass() unless use_ssl?
19
+ end
20
+ req.set_body_internal body
21
+ res = transport_request(req, true, opts, &block)
22
+ if sspi_auth?(res)
23
+ sspi_auth(req)
24
+ res = transport_request(req, true, opts, &block)
25
+ end
26
+ res
27
+ end
28
+
29
+
30
+ private
31
+
32
+ def transport_request(req, allow_retry=true, opts={})
33
+ # Check if previous request was made on same socket and needs
34
+ # to be completed before we can read the new response.
35
+ if Kronk::BufferedIO === @socket && !@socket.response.read?
36
+ @socket.response.body
37
+ end
38
+
39
+ begin_transport req
40
+ req.exec @socket, @curr_http_version, edit_path(req.path)
41
+
42
+ begin
43
+ opts[:timeout] ||= @socket.read_timeout
44
+ res = Kronk::Response.new(@socket.io, opts)
45
+ end while kronk_resp_type(res) == Net::HTTPContinue
46
+
47
+ if res.headless?
48
+ raise HTTPBadResponse, "Invalid HTTP response" unless allow_retry
49
+ @socket.io.close
50
+ res = transport_request(req, false, opts)
51
+ end
52
+
53
+ @socket = res.io
54
+
55
+ res.body {|chunk|
56
+ yield res, chunk
57
+ } if block_given?
58
+
59
+ end_transport req, res
60
+ res
61
+
62
+ rescue => exception
63
+ D "Conn close because of error #{exception}"
64
+ @socket.close if @socket and not @socket.closed?
65
+ raise exception
66
+ end
67
+
68
+
69
+ def end_transport(req, res)
70
+ @curr_http_version = res.http_version
71
+ if @socket.closed?
72
+ D 'Conn socket closed'
73
+ elsif keep_alive?(req, res)
74
+ D 'Conn keep-alive'
75
+ elsif not res.body and @close_on_empty_response
76
+ D 'Conn close'
77
+ @socket.close
78
+ else
79
+ D 'Conn close'
80
+ @socket.close
81
+ end
82
+ end
83
+
84
+
85
+ def sspi_auth?(res)
86
+ return false unless @sspi_enabled
87
+ if kronk_resp_type(res) == HTTPProxyAuthenticationRequired and
88
+ proxy? and res["Proxy-Authenticate"].include?("Negotiate")
89
+ begin
90
+ require 'win32/sspi'
91
+ true
92
+ rescue LoadError
93
+ false
94
+ end
95
+ else
96
+ false
97
+ end
98
+ end
99
+
100
+
101
+ def kronk_resp_type res
102
+ Net::HTTPResponse::CODE_TO_OBJ[res.code]
103
+ end
104
+ end
105
+ end
data/lib/kronk/player.rb CHANGED
@@ -2,46 +2,70 @@ class Kronk
2
2
 
3
3
  ##
4
4
  # The Player class is used for running multiple requests and comparisons and
5
- # providing useful output through Player::Output classes.
6
- # Kronk includes a Suite (test-like) output, a Stream (chunked) output,
5
+ # providing useful output through inherited Player classes.
6
+ # Kronk includes a Suite (test-like), a Stream (chunked),
7
7
  # and a Benchmark output.
8
8
 
9
9
  class Player < QueueRunner
10
10
 
11
- attr_accessor :input, :output
11
+ ##
12
+ # Instantiate a new type of player, typically :suite, :stream, or
13
+ # :benchmark.
14
+
15
+ def self.new_type type, opts={}
16
+ klass =
17
+ case type.to_s
18
+ when /^(Player::)?benchmark$/i then Benchmark
19
+ when /^(Player::)?stream$/i then Stream
20
+ when /^(Player::)?suite$/i then Suite
21
+ when /^(Player::)?tsv$/i then TSV
22
+ else
23
+ Kronk.find_const type.to_s
24
+ end
25
+
26
+ klass.new opts
27
+ end
28
+
29
+
30
+
31
+ attr_accessor :input, :qps, :concurrency, :mutex
12
32
 
13
33
  ##
14
34
  # Create a new Player for batch diff or response validation.
15
35
  # Supported options are:
16
36
  # :io:: IO - The IO instance to read from
17
- # :output:: Class - The output class to use (see Player::Output)
18
37
  # :parser:: Class - The IO parser to use.
38
+ # :concurrency:: Fixnum - Maximum number of concurrent items to process
39
+ # :qps:: Fixnum - Number of queries to process per second
19
40
 
20
41
  def initialize opts={}
21
42
  super
22
- self.output_from opts[:output] || Suite
43
+
44
+ @concurrency = opts[:concurrency]
45
+ @concurrency = 1 if !@concurrency || @concurrency <= 0
46
+ @qps = opts[:qps]
23
47
 
24
48
  @input = InputReader.new opts[:io], opts[:parser]
25
- @use_output = true
26
49
  @last_req = nil
50
+ @mutex = Mutex.new
27
51
 
28
52
  @on_input = Proc.new do
29
53
  stop_input! if !@number && @input.eof?
30
54
  @last_req = @input.get_next || @queue.last || @last_req || {}
31
55
  end
32
56
 
33
- @on_result = Proc.new do |kronk, err, mutex|
34
- err ? @output.error(err, kronk, mutex) :
35
- @output.result(kronk, mutex)
36
- end
37
-
38
57
  on(:input, &@on_input)
39
58
  on(:interrupt){
40
- @output.completed if @use_output
59
+ interrupt and return if respond_to?(:interrupt)
60
+ complete if respond_to?(:complete)
41
61
  exit 2
42
62
  }
43
- on(:start){ @output.start if @use_output }
44
- on(:complete){ @output.completed if @use_output }
63
+ on(:start){
64
+ start if respond_to?(:start)
65
+ }
66
+ on(:complete){
67
+ complete if respond_to?(:complete)
68
+ }
45
69
  end
46
70
 
47
71
 
@@ -58,26 +82,6 @@ class Kronk
58
82
  end
59
83
 
60
84
 
61
- ##
62
- # The kind of output to use. Typically Player::Suite or Player::Stream.
63
- # Takes an output class or a string that represents a class constant.
64
-
65
- def output_from new_output
66
- return @output = new_output.new(self) if Class === new_output
67
-
68
- klass =
69
- case new_output.to_s
70
- when /^(Player::)?benchmark$/i then Benchmark
71
- when /^(Player::)?stream$/i then Stream
72
- when /^(Player::)?suite$/i then Suite
73
- else
74
- Kronk.find_const new_output
75
- end
76
-
77
- @output = klass.new self if klass
78
- end
79
-
80
-
81
85
  ##
82
86
  # Process the queue to compare two uris.
83
87
  # If options are given, they are merged into every request.
@@ -85,10 +89,10 @@ class Kronk
85
89
  def compare uri1, uri2, opts={}, &block
86
90
  return Cmd.compare uri1, uri2, @queue.shift.merge(opts) if single_request?
87
91
 
88
- method = self.class.async ? :process_one_async : :process_one
92
+ on(:result){|(kronk, err)| trigger_result(kronk, err, &block) }
89
93
 
90
- run do |kronk_opts, mutex|
91
- send method, kronk_opts.merge(opts), 'compare', uri1, uri2, &block
94
+ run opts do |kronk|
95
+ kronk.compare uri1, uri2
92
96
  end
93
97
  end
94
98
 
@@ -100,55 +104,37 @@ class Kronk
100
104
  def request uri, opts={}, &block
101
105
  return Cmd.request uri, @queue.shift.merge(opts) if single_request?
102
106
 
103
- method = self.class.async ? :process_one_async : :process_one
107
+ on(:result){|(kronk, err)| trigger_result(kronk, err, &block) }
104
108
 
105
- run do |kronk_opts, mutex|
106
- send method, kronk_opts.merge(opts), 'request', uri, &block
109
+ run opts do |kronk|
110
+ kronk.request uri
107
111
  end
108
112
  end
109
113
 
110
114
 
111
115
  ##
112
- # Run a single compare or request and call the Output#result or
113
- # Output#error method.
114
- #
115
- # If given a block, will yield the Kronk instance and error. If
116
- # a third argument is given, mutex will also be passed and the
117
- # block won't be called from a mutex lock.
118
- #
119
- # Returns the result of the block or of the called Output method.
116
+ # Similar to QueueRunner#run but yields a Kronk instance.
120
117
 
121
- def process_one opts={}, *args, &block
122
- err = nil
123
- kronk = Kronk.new opts
124
-
125
- begin
126
- kronk.send(*args)
127
- rescue *Kronk::Cmd::RESCUABLE => e
128
- err = e
118
+ def run opts={}
119
+ if @qps
120
+ method = :periodically
121
+ arg = 1.0 / @qps.to_f
122
+ else
123
+ method = :concurrently
124
+ arg = @concurrency
129
125
  end
130
126
 
131
- trigger_result kronk, err, &block
132
- end
127
+ send method, arg do |kronk_opts|
128
+ err = nil
129
+ kronk = Kronk.new kronk_opts.merge(opts)
133
130
 
131
+ begin
132
+ yield kronk
133
+ rescue *Kronk::Cmd::RESCUABLE => e
134
+ err = e
135
+ end
134
136
 
135
- ##
136
- # Run a single compare or request and call the Output#result or
137
- # Output#error method using EventMachine.
138
- #
139
- # If given a block, will yield the Kronk instance and error. If
140
- # a third argument is given, mutex will also be passed and the
141
- # block won't be called from a mutex lock.
142
- #
143
- # Returns either a EM::MultiRequest or an EM::Connection handler.
144
-
145
- def process_one_async opts={}, *args, &block
146
- kronk = Kronk.new opts
147
- method = args.shift.to_s + '_async'
148
-
149
- kronk.send(method, *args) do |obj, err|
150
- raise err if err && !Kronk::Cmd::RESCUABLE.find{|eclass| eclass === err}
151
- trigger_result kronk, err, &block
137
+ [kronk, err]
152
138
  end
153
139
  end
154
140
 
@@ -157,14 +143,20 @@ class Kronk
157
143
  # Trigger a single kronk result callback.
158
144
 
159
145
  def trigger_result kronk, err, &block
160
- block ||= @on_result
161
-
162
- if block.arity > 2 || block.arity < 0
163
- block.call kronk, err, @mutex
164
- else
165
- @mutex.synchronize do
166
- block.call kronk, err
146
+ if block_given?
147
+ if block.arity > 2 || block.arity < 0
148
+ block.call kronk, err, @mutex
149
+ else
150
+ @mutex.synchronize do
151
+ block.call kronk, err
152
+ end
167
153
  end
154
+
155
+ elsif err && respond_to?(:error)
156
+ error err, kronk
157
+
158
+ elsif respond_to?(:result)
159
+ result kronk
168
160
  end
169
161
  end
170
162
 
@@ -13,18 +13,21 @@ class Kronk
13
13
  # * Percentage of requests within a certain time
14
14
  # * Slowest endpoints
15
15
 
16
- class Player::Benchmark < Player::Output
16
+ class Player::Benchmark < Player
17
17
 
18
18
  class ResultSet
19
19
 
20
+ attr_accessor :total_time
21
+
20
22
  attr_reader :byterate, :count, :fastest, :precision,
21
- :slowest, :total_bytes
23
+ :slowest, :total_bytes, :err_count
22
24
 
23
- def initialize start_time
24
- @times = Hash.new(0)
25
- @count = 0
26
- @r5XX = 0
27
- @r4XX = 0
25
+ def initialize
26
+ @times = Hash.new(0)
27
+ @count = 0
28
+ @r5XX = 0
29
+ @r4XX = 0
30
+ @err_count = 0
28
31
 
29
32
  @precision = 3
30
33
 
@@ -36,7 +39,6 @@ class Kronk
36
39
  @total_bytes = 0
37
40
  @byterate = 0
38
41
 
39
- @start_time = start_time
40
42
  @total_time = 0
41
43
  end
42
44
 
@@ -58,8 +60,6 @@ class Kronk
58
60
  @total_bytes += resp.raw.bytes.count
59
61
 
60
62
  @byterate = (@byterate * (@count-1) + resp.byterate) / @count
61
-
62
- @total_time = (Time.now - @start_time).to_f
63
63
  end
64
64
 
65
65
 
@@ -147,9 +147,11 @@ class Kronk
147
147
 
148
148
  def to_s
149
149
  out = <<-STR
150
+
150
151
  Completed: #{@count}
151
152
  400s: #{@r4XX}
152
153
  500s: #{@r5XX}
154
+ Errors: #{@err_count}
153
155
  Req/Sec: #{self.req_per_sec}
154
156
  Total Bytes: #{@total_bytes}
155
157
  Transfer Rate: #{self.transfer_rate} Kbytes/sec
@@ -175,7 +177,7 @@ Request Percentages (ms)
175
177
 
176
178
  unless slowest_reqs.empty?
177
179
  out << "
178
- Avg. Slowest Requests (ms, #)
180
+ Avg. Slowest Requests (ms, count)
179
181
  #{slowest_reqs.map{|arr| " #{arr[1].inspect} #{arr[0]}"}.join "\n" }"
180
182
  end
181
183
 
@@ -184,44 +186,39 @@ Avg. Slowest Requests (ms, #)
184
186
  end
185
187
 
186
188
 
187
- def initialize player
188
- @player = player
189
- @results = []
190
- @count = 0
191
-
192
- @div = nil
193
- @div = @player.number / 10 if @player.number
194
- @div = 100 if !@div || @div < 10
195
- end
196
-
197
-
198
189
  def start
190
+ @res_count = 0
191
+ @results = []
192
+ @div = @number / 10 if @number
193
+ @div = 100 if !@div || @div < 10
199
194
  puts "Benchmarking..."
200
- super
201
195
  end
202
196
 
203
197
 
204
- def result kronk, mutex
205
- kronk.responses.each_with_index do |resp, i|
206
- mutex.synchronize do
207
- @count += 1
208
- @results[i] ||= ResultSet.new(@start_time)
198
+ def result kronk
199
+ @mutex.synchronize do
200
+ kronk.responses.each_with_index do |resp, i|
201
+ @results[i] ||= ResultSet.new
209
202
  @results[i].add_result resp
210
-
211
- puts "#{@count} requests" if @count % @div == 0
212
203
  end
204
+
205
+ @res_count += 1
206
+ puts "#{@res_count} requests" if @res_count % @div == 0
213
207
  end
214
208
  end
215
209
 
216
210
 
217
- def error err, kronk, mutex
218
- mutex.synchronize do
219
- @count += 1
211
+ def error err, kronk
212
+ @mutex.synchronize do
213
+ @res_count += 1
214
+ @results.each do |res|
215
+ res.err_count += 1
216
+ end
220
217
  end
221
218
  end
222
219
 
223
220
 
224
- def completed
221
+ def complete
225
222
  puts "Finished!"
226
223
 
227
224
  render_head
@@ -232,6 +229,8 @@ Avg. Slowest Requests (ms, #)
232
229
 
233
230
 
234
231
  def render_body
232
+ @results.each{|res| res.total_time = @stop_time - @start_time }
233
+
235
234
  if @results.length > 1
236
235
  puts Diff.new(@results[0].to_s, @results[1].to_s).formatted
237
236
  else
@@ -245,7 +244,7 @@ Avg. Slowest Requests (ms, #)
245
244
 
246
245
  Benchmark Time: #{(Time.now - @start_time).to_f} sec
247
246
  Number of Requests: #{@count}
248
- Concurrency: #{@player.concurrency}
247
+ Concurrency: #{@qps ? "#{@qps} qps" : @concurrency}
249
248
  STR
250
249
  end
251
250
  end