kronk 1.6.2 → 1.7.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.
- data/History.rdoc +29 -1
- data/Manifest.txt +6 -1
- data/README.rdoc +74 -28
- data/Rakefile +4 -3
- data/TODO.rdoc +7 -5
- data/bin/kronk +2 -11
- data/lib/kronk/async/em_ext.rb +34 -0
- data/lib/kronk/async/request.rb +73 -0
- data/lib/kronk/async/response.rb +70 -0
- data/lib/kronk/async.rb +118 -0
- data/lib/kronk/cmd.rb +111 -43
- data/lib/kronk/constants.rb +1 -0
- data/lib/kronk/core_ext.rb +1 -1
- data/lib/kronk/data_string.rb +251 -0
- data/lib/kronk/diff/output.rb +132 -100
- data/lib/kronk/diff.rb +20 -24
- data/lib/kronk/path/matcher.rb +8 -4
- data/lib/kronk/path/path_match.rb +48 -4
- data/lib/kronk/path/transaction.rb +74 -53
- data/lib/kronk/path.rb +11 -6
- data/lib/kronk/player/benchmark.rb +11 -12
- data/lib/kronk/player/input_reader.rb +40 -3
- data/lib/kronk/player/request_parser.rb +4 -1
- data/lib/kronk/player/stream.rb +2 -2
- data/lib/kronk/player/suite.rb +16 -9
- data/lib/kronk/player.rb +93 -143
- data/lib/kronk/queue_runner.rb +238 -0
- data/lib/kronk/request.rb +25 -20
- data/lib/kronk/response.rb +39 -10
- data/lib/kronk/test/assertions.rb +2 -2
- data/lib/kronk/test/helper_methods.rb +1 -1
- data/lib/kronk.rb +56 -24
- data/test/test_assertions.rb +4 -4
- data/test/test_cmd.rb +38 -10
- data/test/test_data_string.rb +242 -1
- data/test/test_diff.rb +8 -303
- data/test/test_helper.rb +1 -1
- data/test/test_kronk.rb +21 -28
- data/test/test_path.rb +29 -0
- data/test/test_path_match.rb +47 -2
- data/test/test_path_matcher.rb +42 -1
- data/test/test_player.rb +71 -72
- data/test/test_request.rb +31 -6
- data/test/test_request_parser.rb +7 -1
- data/test/test_response.rb +1 -1
- data/test/test_transaction.rb +78 -30
- metadata +64 -8
- data/lib/kronk/data_renderer.rb +0 -219
data/lib/kronk/player.rb
CHANGED
@@ -6,33 +6,57 @@ class Kronk
|
|
6
6
|
# Kronk includes a Suite (test-like) output, a Stream (chunked) output,
|
7
7
|
# and a Benchmark output.
|
8
8
|
|
9
|
-
class Player
|
9
|
+
class Player < QueueRunner
|
10
10
|
|
11
|
-
|
12
|
-
|
11
|
+
RESCUABLE = [Kronk::Exception, Errno::ECONNRESET, URI::InvalidURIError]
|
12
|
+
|
13
|
+
attr_accessor :input, :output
|
13
14
|
|
14
15
|
##
|
15
16
|
# Create a new Player for batch diff or response validation.
|
16
17
|
# Supported options are:
|
17
|
-
# :concurrency:: Fixnum - The maximum number of concurrent requests to make
|
18
|
-
# :number:: Fixnum - The number of requests to make
|
19
18
|
# :io:: IO - The IO instance to read from
|
20
19
|
# :output:: Class - The output class to use (see Player::Output)
|
21
20
|
# :parser:: Class - The IO parser to use.
|
22
21
|
|
23
22
|
def initialize opts={}
|
24
|
-
|
25
|
-
@concurrency = opts[:concurrency]
|
26
|
-
@concurrency = 1 if !@concurrency || @concurrency <= 0
|
23
|
+
super
|
27
24
|
self.output_from opts[:output] || Suite
|
28
25
|
|
29
|
-
@
|
30
|
-
@
|
31
|
-
@
|
32
|
-
|
33
|
-
@
|
26
|
+
@input = InputReader.new opts[:io], opts[:parser]
|
27
|
+
@use_output = true
|
28
|
+
@last_req = nil
|
29
|
+
|
30
|
+
@on_input = Proc.new do
|
31
|
+
stop_input! if !@number && @input.eof?
|
32
|
+
@last_req = @input.get_next || @queue.last || @last_req || {}
|
33
|
+
end
|
34
|
+
|
35
|
+
@on_result = Proc.new do |kronk, err, mutex|
|
36
|
+
err ? @output.error(err, kronk, mutex) :
|
37
|
+
@output.result(kronk, mutex)
|
38
|
+
end
|
34
39
|
|
35
|
-
|
40
|
+
on(:input, &@on_input)
|
41
|
+
on(:interrupt){
|
42
|
+
@output.completed if @use_output
|
43
|
+
exit 2
|
44
|
+
}
|
45
|
+
on(:start){ @output.start if @use_output }
|
46
|
+
on(:complete){ @output.completed if @use_output }
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
##
|
51
|
+
# Populate the queue by reading from the given IO instance and
|
52
|
+
# parsing it into kronk options.
|
53
|
+
#
|
54
|
+
# Default parser is RequestParser. See InputReader for parser requirements.
|
55
|
+
|
56
|
+
def from_io io, parser=nil
|
57
|
+
@input.io = io
|
58
|
+
@input.parser = parser if parser
|
59
|
+
@input
|
36
60
|
end
|
37
61
|
|
38
62
|
|
@@ -56,37 +80,17 @@ class Kronk
|
|
56
80
|
end
|
57
81
|
|
58
82
|
|
59
|
-
##
|
60
|
-
# Adds kronk request hash options to queue.
|
61
|
-
# See Kronk#compare for supported options.
|
62
|
-
|
63
|
-
def queue_req kronk_opts
|
64
|
-
@queue << kronk_opts
|
65
|
-
end
|
66
|
-
|
67
|
-
|
68
|
-
##
|
69
|
-
# Populate the queue by reading from the given IO instance and
|
70
|
-
# parsing it into kronk options.
|
71
|
-
#
|
72
|
-
# Default parser is RequestParser. See InputReader for parser requirements.
|
73
|
-
|
74
|
-
def from_io io, parser=nil
|
75
|
-
@input.io = io
|
76
|
-
@input.parser = parser if parser
|
77
|
-
@input
|
78
|
-
end
|
79
|
-
|
80
|
-
|
81
83
|
##
|
82
84
|
# Process the queue to compare two uris.
|
83
85
|
# If options are given, they are merged into every request.
|
84
86
|
|
85
|
-
def compare uri1, uri2, opts={}
|
87
|
+
def compare uri1, uri2, opts={}, &block
|
86
88
|
return Cmd.compare uri1, uri2, @queue.shift.merge(opts) if single_request?
|
87
89
|
|
88
|
-
|
89
|
-
|
90
|
+
method = self.class.async ? :process_one_async : :process_one
|
91
|
+
|
92
|
+
run do |kronk_opts, mutex|
|
93
|
+
send method, kronk_opts.merge(opts), 'compare', uri1, uri2, &block
|
90
94
|
end
|
91
95
|
end
|
92
96
|
|
@@ -95,139 +99,85 @@ class Kronk
|
|
95
99
|
# Process the queue to request uris.
|
96
100
|
# If options are given, they are merged into every request.
|
97
101
|
|
98
|
-
def request uri, opts={}
|
99
|
-
return Cmd.request
|
102
|
+
def request uri, opts={}, &block
|
103
|
+
return Cmd.request uri, @queue.shift.merge(opts) if single_request?
|
100
104
|
|
101
|
-
|
102
|
-
process_request uri, kronk_opts.merge(opts)
|
103
|
-
end
|
104
|
-
end
|
105
|
+
method = self.class.async ? :process_one_async : :process_one
|
105
106
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
# If so, yield a single item and return immediately.
|
110
|
-
|
111
|
-
def single_request?
|
112
|
-
@queue << next_request if @queue.empty? && (!@number || @number <= 1)
|
113
|
-
@queue.length == 1 && @input.eof?
|
107
|
+
run do |kronk_opts, mutex|
|
108
|
+
send method, kronk_opts.merge(opts), 'request', uri, &block
|
109
|
+
end
|
114
110
|
end
|
115
111
|
|
116
112
|
|
117
113
|
##
|
118
|
-
#
|
119
|
-
#
|
120
|
-
# once processing is finished.
|
114
|
+
# Run a single compare or request and call the Output#result or
|
115
|
+
# Output#error method.
|
121
116
|
#
|
122
|
-
#
|
123
|
-
#
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
trap 'INT' do
|
129
|
-
@threads.each{|t| t.kill}
|
130
|
-
@threads.clear
|
131
|
-
@reader_thread.kill
|
132
|
-
output_results
|
133
|
-
exit 2
|
134
|
-
end
|
135
|
-
|
136
|
-
@output.start
|
137
|
-
@count = 0
|
138
|
-
|
139
|
-
until finished?
|
140
|
-
@threads.delete_if{|t| !t.alive? }
|
141
|
-
next if @threads.length >= @concurrency || @queue.empty?
|
117
|
+
# If given a block, will yield the Kronk instance and error. If
|
118
|
+
# a third argument is given, mutex will also be passed and the
|
119
|
+
# block won't be called from a mutex lock.
|
120
|
+
#
|
121
|
+
# Returns the result of the block or of the called Output method.
|
142
122
|
|
143
|
-
|
144
|
-
|
145
|
-
|
123
|
+
def process_one opts={}, *args, &block
|
124
|
+
err = nil
|
125
|
+
kronk = Kronk.new opts
|
146
126
|
|
147
|
-
|
127
|
+
begin
|
128
|
+
kronk.send(*args)
|
129
|
+
rescue *RESCUABLE => e
|
130
|
+
err = e
|
148
131
|
end
|
149
132
|
|
150
|
-
|
151
|
-
@threads.clear
|
152
|
-
|
153
|
-
@reader_thread.kill
|
154
|
-
|
155
|
-
output_results
|
133
|
+
trigger_result kronk, err, &block
|
156
134
|
end
|
157
135
|
|
158
136
|
|
159
137
|
##
|
160
|
-
#
|
161
|
-
#
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
138
|
+
# Run a single compare or request and call the Output#result or
|
139
|
+
# Output#error method using EventMachine.
|
140
|
+
#
|
141
|
+
# If given a block, will yield the Kronk instance and error. If
|
142
|
+
# a third argument is given, mutex will also be passed and the
|
143
|
+
# block won't be called from a mutex lock.
|
144
|
+
#
|
145
|
+
# Returns either a EM::MultiRequest or an EM::Connection handler.
|
168
146
|
|
169
|
-
|
147
|
+
def process_one_async opts={}, *args, &block
|
148
|
+
kronk = Kronk.new opts
|
149
|
+
method = args.shift.to_s + '_async'
|
170
150
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
end
|
175
|
-
end
|
151
|
+
kronk.send(method, *args) do |obj, err|
|
152
|
+
raise err if err && !RESCUABLE.find{|eclass| eclass === err}
|
153
|
+
trigger_result kronk, err, &block
|
176
154
|
end
|
177
155
|
end
|
178
156
|
|
179
157
|
|
180
158
|
##
|
181
|
-
#
|
182
|
-
# Tries from input first, then from the last item in the queue.
|
183
|
-
# If both fail, returns an empty Hash.
|
184
|
-
|
185
|
-
def next_request
|
186
|
-
@last_req = @input.get_next || @queue.last || @last_req || Hash.new
|
187
|
-
end
|
159
|
+
# Trigger a single kronk result callback.
|
188
160
|
|
161
|
+
def trigger_result kronk, err, &block
|
162
|
+
block ||= @on_result
|
189
163
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
##
|
200
|
-
# Process and output the results.
|
201
|
-
# Calls Output#completed method.
|
202
|
-
|
203
|
-
def output_results
|
204
|
-
@output.completed
|
205
|
-
end
|
206
|
-
|
207
|
-
|
208
|
-
##
|
209
|
-
# Run a single compare and call the Output#result or Output#error method.
|
210
|
-
|
211
|
-
def process_compare uri1, uri2, opts={}
|
212
|
-
kronk = Kronk.new opts
|
213
|
-
kronk.compare uri1, uri2
|
214
|
-
@output.result kronk, @mutex
|
215
|
-
|
216
|
-
rescue Kronk::Exception, Response::MissingParser, Errno::ECONNRESET => e
|
217
|
-
@output.error e, kronk, @mutex
|
164
|
+
if block.arity > 2 || block.arity < 0
|
165
|
+
block.call kronk, err, @mutex
|
166
|
+
else
|
167
|
+
@mutex.synchronize do
|
168
|
+
block.call kronk, err
|
169
|
+
end
|
170
|
+
end
|
218
171
|
end
|
219
172
|
|
220
173
|
|
221
174
|
##
|
222
|
-
#
|
223
|
-
|
224
|
-
def process_request uri, opts={}
|
225
|
-
kronk = Kronk.new opts
|
226
|
-
kronk.retrieve uri
|
227
|
-
@output.result kronk, @mutex
|
175
|
+
# Check if w6ce5e2ce're only processing a single case.
|
176
|
+
# If so, yield a single item and return immediately.
|
228
177
|
|
229
|
-
|
230
|
-
@
|
178
|
+
def single_request?
|
179
|
+
@queue << trigger(:input) if @queue.empty? && (!@number || @number <= 1)
|
180
|
+
@queue.length == 1 && @triggers[:input] == @on_input && @input.eof?
|
231
181
|
end
|
232
182
|
end
|
233
183
|
end
|
@@ -0,0 +1,238 @@
|
|
1
|
+
class Kronk
|
2
|
+
|
3
|
+
class QueueRunner
|
4
|
+
|
5
|
+
##
|
6
|
+
# Define whether to use the EventMachine or the threaded behavior.
|
7
|
+
|
8
|
+
def self.async= value
|
9
|
+
@async = !!value
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
##
|
14
|
+
# Returns true if EventMachine is enabled
|
15
|
+
|
16
|
+
def self.async
|
17
|
+
@async
|
18
|
+
end
|
19
|
+
|
20
|
+
self.async = false
|
21
|
+
|
22
|
+
|
23
|
+
attr_accessor :number, :concurrency, :queue, :count,
|
24
|
+
:mutex, :threads, :reader_thread
|
25
|
+
|
26
|
+
##
|
27
|
+
# Create a new QueueRunner for batch multi-threaded processing.
|
28
|
+
# Supported options are:
|
29
|
+
# :concurrency:: Fixnum - Maximum number of concurrent items to process
|
30
|
+
# :number:: Fixnum - Total number of items to process
|
31
|
+
|
32
|
+
def initialize opts={}
|
33
|
+
@number = opts[:number]
|
34
|
+
@concurrency = opts[:concurrency]
|
35
|
+
@concurrency = 1 if !@concurrency || @concurrency <= 0
|
36
|
+
|
37
|
+
@count = 0
|
38
|
+
@queue = []
|
39
|
+
@threads = []
|
40
|
+
|
41
|
+
@reader_thread = nil
|
42
|
+
|
43
|
+
@triggers = {}
|
44
|
+
|
45
|
+
@mutex = Mutex.new
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
##
|
50
|
+
# Returns true if processing queue should be stopped, otherwise false.
|
51
|
+
|
52
|
+
def finished?
|
53
|
+
return true if @number && @count >= @number
|
54
|
+
|
55
|
+
@queue.empty? && @count > 0 &&
|
56
|
+
(!@reader_thread || !@reader_thread.alive?)
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
##
|
61
|
+
# Immediately end all runner processing and threads.
|
62
|
+
|
63
|
+
def kill
|
64
|
+
stop_input!
|
65
|
+
EM.stop if defined?(EM) && EM.reactor_running?
|
66
|
+
@threads.each{|t| t.kill}
|
67
|
+
@threads.clear
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
##
|
72
|
+
# Specify a block to run for a given trigger name.
|
73
|
+
# Supported triggers are:
|
74
|
+
# :complete:: Called after queue and input have been fully processed.
|
75
|
+
# :input:: Called every time the queue needs populating.
|
76
|
+
# :interrupt:: Called when SIGINT is captured.
|
77
|
+
# :start:: Called before queue starts being processed.
|
78
|
+
|
79
|
+
def on trigger_name, &block
|
80
|
+
@triggers[trigger_name] = block
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
##
|
85
|
+
# Start processing the queue and reading from IO if available.
|
86
|
+
#
|
87
|
+
# Yields queue item until queue and io (if available) are empty and the
|
88
|
+
# totaly number of requests to run is met (if number is set).
|
89
|
+
|
90
|
+
def process_queue
|
91
|
+
start_input!
|
92
|
+
@count = 0
|
93
|
+
|
94
|
+
until finished?
|
95
|
+
@threads.delete_if{|t| !t.alive? }
|
96
|
+
|
97
|
+
if @threads.length >= @concurrency || @queue.empty?
|
98
|
+
Thread.pass
|
99
|
+
next
|
100
|
+
end
|
101
|
+
|
102
|
+
@threads << Thread.new(@queue.shift) do |q_item|
|
103
|
+
yield q_item if block_given?
|
104
|
+
end
|
105
|
+
|
106
|
+
@count += 1
|
107
|
+
end
|
108
|
+
|
109
|
+
@threads.each{|t| t.join}
|
110
|
+
@threads.clear
|
111
|
+
|
112
|
+
stop_input!
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
##
|
117
|
+
# Start processing the queue and reading from IO if available.
|
118
|
+
#
|
119
|
+
# Yields queue item until queue and io (if available) are empty and the
|
120
|
+
# totaly number of requests to run is met (if number is set).
|
121
|
+
#
|
122
|
+
# Uses EventMachine to run asynchronously.
|
123
|
+
#
|
124
|
+
# Note: If the block given doesn't use EM, it will be blocking.
|
125
|
+
|
126
|
+
def process_queue_async &block
|
127
|
+
# TODO: Make input use EM from QueueRunner and Player IO.
|
128
|
+
require 'kronk/async' unless defined?(EM::HttpRequest)
|
129
|
+
Cmd.verbose "Running async"
|
130
|
+
|
131
|
+
start_input!
|
132
|
+
|
133
|
+
@count = 0
|
134
|
+
|
135
|
+
EM.run do
|
136
|
+
EM.add_periodic_timer do
|
137
|
+
if finished?
|
138
|
+
next if EM.connection_count > 0
|
139
|
+
kill
|
140
|
+
next
|
141
|
+
end
|
142
|
+
|
143
|
+
if @queue.empty? || EM.connection_count >= @concurrency
|
144
|
+
Thread.pass
|
145
|
+
next
|
146
|
+
end
|
147
|
+
|
148
|
+
yield @queue.shift
|
149
|
+
@count += 1
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
|
155
|
+
##
|
156
|
+
# Runs the queue and reads from input until it's exhausted or
|
157
|
+
# @number is reached. Yields a queue item and a mutex when to passed
|
158
|
+
# block:
|
159
|
+
#
|
160
|
+
# runner = QueueRunner.new :concurrency => 10
|
161
|
+
# runner.queue.concat %w{item1 item2 item3}
|
162
|
+
#
|
163
|
+
# runner.run do |q_item, mutex|
|
164
|
+
# # This block is run in its own thread.
|
165
|
+
# mutex.synchronize{ do_something_with q_item }
|
166
|
+
# end
|
167
|
+
#
|
168
|
+
# Calls the :start trigger before execution begins, calls :complete
|
169
|
+
# when the execution has ended or is interrupted, also calls :interrupt
|
170
|
+
# when execution is interrupted.
|
171
|
+
|
172
|
+
def run
|
173
|
+
trap 'INT' do
|
174
|
+
kill
|
175
|
+
(trigger(:interrupt) || exit(1))
|
176
|
+
end
|
177
|
+
|
178
|
+
trigger :start
|
179
|
+
|
180
|
+
method = self.class.async ? :process_queue_async : :process_queue
|
181
|
+
|
182
|
+
send method do |q_item|
|
183
|
+
yield q_item, @mutex if block_given?
|
184
|
+
end
|
185
|
+
|
186
|
+
trigger :complete
|
187
|
+
end
|
188
|
+
|
189
|
+
|
190
|
+
##
|
191
|
+
# Attempt to fill the queue by reading from the IO instance.
|
192
|
+
# Starts a new thread and returns the thread instance.
|
193
|
+
|
194
|
+
def start_input!
|
195
|
+
return unless @triggers[:input]
|
196
|
+
|
197
|
+
max_queue_size = @concurrency * 2
|
198
|
+
|
199
|
+
@reader_thread = Thread.new do
|
200
|
+
begin
|
201
|
+
loop do
|
202
|
+
if @queue.length >= max_queue_size
|
203
|
+
Thread.pass
|
204
|
+
next
|
205
|
+
end
|
206
|
+
|
207
|
+
while @queue.length < max_queue_size
|
208
|
+
@queue << trigger(:input)
|
209
|
+
end
|
210
|
+
Thread.pass
|
211
|
+
end
|
212
|
+
|
213
|
+
rescue => e
|
214
|
+
Thread.main.raise e
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
|
220
|
+
##
|
221
|
+
# Permanently stop input reading by killing the reader thread for a given
|
222
|
+
# QueueRunner#run or QueueRunner#process_queue session.
|
223
|
+
|
224
|
+
def stop_input!
|
225
|
+
Thread.pass
|
226
|
+
@reader_thread && @reader_thread.kill
|
227
|
+
end
|
228
|
+
|
229
|
+
|
230
|
+
##
|
231
|
+
# Run a previously defined callback. See QueueRunner#on.
|
232
|
+
|
233
|
+
def trigger name
|
234
|
+
t = @triggers[name]
|
235
|
+
t && t.call
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
data/lib/kronk/request.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
1
|
class Kronk
|
2
2
|
|
3
3
|
##
|
4
|
-
# Performs HTTP requests
|
4
|
+
# Performs HTTP requests and returns a Kronk::Response instance.
|
5
5
|
|
6
6
|
class Request
|
7
7
|
|
8
8
|
# Raised by Request.parse when parsing invalid http request string.
|
9
9
|
class ParseError < Kronk::Exception; end
|
10
10
|
|
11
|
-
# Matches the first line of an http request string
|
12
|
-
|
11
|
+
# Matches the first line of an http request string or a fully
|
12
|
+
# qualified URL.
|
13
|
+
REQUEST_LINE_MATCHER =
|
14
|
+
%r{(?:^|[\s'"])(?:([a-z]+)\s)?(?:(https?://[^/]+)(/[^\s'";]*)?|(/[^\s'";]*))}i
|
13
15
|
|
14
16
|
##
|
15
17
|
# Creates a query string from data.
|
@@ -45,7 +47,7 @@ class Kronk
|
|
45
47
|
# path and options.
|
46
48
|
|
47
49
|
def self.build_uri uri, options={}
|
48
|
-
uri ||= Kronk.config[:default_host]
|
50
|
+
uri ||= options[:host] || Kronk.config[:default_host]
|
49
51
|
suffix = options[:uri_suffix]
|
50
52
|
|
51
53
|
uri = "http://#{uri}" unless uri.to_s =~ %r{^(\w+://|/)}
|
@@ -64,6 +66,7 @@ class Kronk
|
|
64
66
|
|
65
67
|
##
|
66
68
|
# Parses a raw HTTP request-like string into a Kronk::Request instance.
|
69
|
+
# Options passed are used as default values for Request#new.
|
67
70
|
|
68
71
|
def self.parse str, opts={}
|
69
72
|
opts = parse_to_hash str, opts
|
@@ -75,7 +78,8 @@ class Kronk
|
|
75
78
|
|
76
79
|
##
|
77
80
|
# Parses a raw HTTP request-like string into a Kronk::Request options hash.
|
78
|
-
# Also parses most single access log entries.
|
81
|
+
# Also parses most single access log entries. Options passed are used
|
82
|
+
# as default values for Request#new.
|
79
83
|
|
80
84
|
def self.parse_to_hash str, opts={}
|
81
85
|
lines = str.split("\n")
|
@@ -86,7 +90,9 @@ class Kronk
|
|
86
90
|
opts[:headers] ||= {}
|
87
91
|
|
88
92
|
lines.shift.strip =~ REQUEST_LINE_MATCHER
|
89
|
-
opts
|
93
|
+
opts.merge! :http_method => $1,
|
94
|
+
:host => $2,
|
95
|
+
:uri_suffix => ($3 || $4)
|
90
96
|
|
91
97
|
lines.each_with_index do |line, i|
|
92
98
|
case line
|
@@ -105,6 +111,7 @@ class Kronk
|
|
105
111
|
|
106
112
|
opts[:data] = lines[body_start..-1].join("\n") if body_start
|
107
113
|
|
114
|
+
opts.delete(:host) if !opts[:host]
|
108
115
|
opts.delete(:uri_suffix) if !opts[:uri_suffix]
|
109
116
|
opts.delete(:headers) if opts[:headers].empty?
|
110
117
|
opts.delete(:http_method) if !opts[:http_method]
|
@@ -178,7 +185,7 @@ class Kronk
|
|
178
185
|
end
|
179
186
|
|
180
187
|
|
181
|
-
attr_accessor :body, :headers, :response, :timeout
|
188
|
+
attr_accessor :body, :headers, :proxy, :response, :timeout
|
182
189
|
|
183
190
|
attr_reader :http_method, :uri, :use_cookies
|
184
191
|
|
@@ -187,18 +194,16 @@ class Kronk
|
|
187
194
|
# Supports the following options:
|
188
195
|
# :data:: Hash/String - the data to pass to the http request
|
189
196
|
# :query:: Hash/String - the data to append to the http request path
|
190
|
-
# :follow_redirects:: Integer/Bool - number of times to follow redirects
|
191
197
|
# :user_agent:: String - user agent string or alias; defaults to 'kronk'
|
192
198
|
# :auth:: Hash - must contain :username and :password; defaults to nil
|
193
199
|
# :headers:: Hash - extra headers to pass to the request
|
194
200
|
# :http_method:: Symbol - the http method to use; defaults to :get
|
195
|
-
# :proxy:: Hash/String - http proxy to use; defaults to
|
201
|
+
# :proxy:: Hash/String - http proxy to use; defaults to {}
|
196
202
|
#
|
197
203
|
# Note: if no http method is specified and data is given, will default
|
198
204
|
# to using a post request.
|
199
205
|
|
200
206
|
def initialize uri, options={}
|
201
|
-
@HTTP = Net::HTTP
|
202
207
|
@auth = options[:auth]
|
203
208
|
|
204
209
|
@body = nil
|
@@ -213,18 +218,15 @@ class Kronk
|
|
213
218
|
|
214
219
|
@uri = self.class.build_uri uri, options
|
215
220
|
|
221
|
+
@proxy = options[:proxy] || {}
|
222
|
+
@proxy = {:host => @proxy} unless Hash === @proxy
|
223
|
+
|
216
224
|
self.user_agent ||= options[:user_agent]
|
217
225
|
|
218
226
|
self.http_method = options[:http_method] || (@body ? "POST" : "GET")
|
219
227
|
|
220
228
|
self.use_cookies = options.has_key?(:no_cookies) ?
|
221
229
|
!options[:no_cookies] : Kronk.config[:use_cookies]
|
222
|
-
|
223
|
-
if Hash === options[:proxy]
|
224
|
-
self.use_proxy options[:proxy][:address], options[:proxy]
|
225
|
-
else
|
226
|
-
self.use_proxy options[:proxy]
|
227
|
-
end
|
228
230
|
end
|
229
231
|
|
230
232
|
|
@@ -278,8 +280,8 @@ class Kronk
|
|
278
280
|
# The proxy_opts arg can be a uri String or a Hash with the :address key
|
279
281
|
# and optional :username and :password keys.
|
280
282
|
|
281
|
-
def
|
282
|
-
return
|
283
|
+
def http_proxy addr, opts={}
|
284
|
+
return Net::HTTP unless addr
|
283
285
|
|
284
286
|
host, port = addr.split ":"
|
285
287
|
port ||= opts[:port] || 8080
|
@@ -289,7 +291,7 @@ class Kronk
|
|
289
291
|
|
290
292
|
Kronk::Cmd.verbose "Using proxy #{addr}\n" if host
|
291
293
|
|
292
|
-
|
294
|
+
Net::HTTP::Proxy host, port, user, pass
|
293
295
|
end
|
294
296
|
|
295
297
|
|
@@ -355,7 +357,9 @@ class Kronk
|
|
355
357
|
# Retrieve this requests' response.
|
356
358
|
|
357
359
|
def retrieve
|
358
|
-
|
360
|
+
http_class = http_proxy @proxy[:host], @proxy
|
361
|
+
|
362
|
+
@_req = http_class.new @uri.host, @uri.port
|
359
363
|
|
360
364
|
@_req.read_timeout = @timeout if @timeout
|
361
365
|
@_req.use_ssl = true if @uri.scheme =~ /^https$/
|
@@ -424,3 +428,4 @@ class Kronk
|
|
424
428
|
end
|
425
429
|
end
|
426
430
|
end
|
431
|
+
|