kronk 1.7.8 → 1.8.0

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.
@@ -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