gravis-typhoeus 0.1.29
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/.gitignore +2 -0
- data/README.textile +301 -0
- data/Rakefile +39 -0
- data/VERSION +1 -0
- data/benchmarks/profile.rb +25 -0
- data/benchmarks/vs_nethttp.rb +35 -0
- data/examples/twitter.rb +21 -0
- data/ext/typhoeus/.gitignore +5 -0
- data/ext/typhoeus/Makefile +157 -0
- data/ext/typhoeus/extconf.rb +65 -0
- data/ext/typhoeus/native.c +11 -0
- data/ext/typhoeus/native.h +21 -0
- data/ext/typhoeus/typhoeus_easy.c +207 -0
- data/ext/typhoeus/typhoeus_easy.h +19 -0
- data/ext/typhoeus/typhoeus_multi.c +225 -0
- data/ext/typhoeus/typhoeus_multi.h +16 -0
- data/lib/typhoeus/.gitignore +1 -0
- data/lib/typhoeus/easy.rb +322 -0
- data/lib/typhoeus/filter.rb +28 -0
- data/lib/typhoeus/hydra.rb +227 -0
- data/lib/typhoeus/multi.rb +35 -0
- data/lib/typhoeus/remote.rb +306 -0
- data/lib/typhoeus/remote_method.rb +108 -0
- data/lib/typhoeus/remote_proxy_object.rb +48 -0
- data/lib/typhoeus/request.rb +124 -0
- data/lib/typhoeus/response.rb +49 -0
- data/lib/typhoeus/service.rb +20 -0
- data/lib/typhoeus.rb +55 -0
- data/profilers/valgrind.rb +24 -0
- data/spec/fixtures/result_set.xml +60 -0
- data/spec/servers/app.rb +73 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +11 -0
- data/spec/typhoeus/easy_spec.rb +228 -0
- data/spec/typhoeus/filter_spec.rb +35 -0
- data/spec/typhoeus/hydra_spec.rb +311 -0
- data/spec/typhoeus/multi_spec.rb +74 -0
- data/spec/typhoeus/remote_method_spec.rb +141 -0
- data/spec/typhoeus/remote_proxy_object_spec.rb +65 -0
- data/spec/typhoeus/remote_spec.rb +695 -0
- data/spec/typhoeus/request_spec.rb +169 -0
- data/spec/typhoeus/response_spec.rb +63 -0
- data/typhoeus.gemspec +112 -0
- metadata +203 -0
@@ -0,0 +1,322 @@
|
|
1
|
+
module Typhoeus
|
2
|
+
class Easy
|
3
|
+
attr_reader :response_body, :response_header, :method, :headers, :url
|
4
|
+
attr_accessor :start_time
|
5
|
+
|
6
|
+
# These integer codes are available in curl/curl.h
|
7
|
+
CURLINFO_STRING = 1048576
|
8
|
+
OPTION_VALUES = {
|
9
|
+
:CURLOPT_URL => 10002,
|
10
|
+
:CURLOPT_HTTPGET => 80,
|
11
|
+
:CURLOPT_HTTPPOST => 10024,
|
12
|
+
:CURLOPT_UPLOAD => 46,
|
13
|
+
:CURLOPT_CUSTOMREQUEST => 10036,
|
14
|
+
:CURLOPT_POSTFIELDS => 10015,
|
15
|
+
:CURLOPT_POSTFIELDSIZE => 60,
|
16
|
+
:CURLOPT_USERAGENT => 10018,
|
17
|
+
:CURLOPT_TIMEOUT_MS => 155,
|
18
|
+
:CURLOPT_NOSIGNAL => 99,
|
19
|
+
:CURLOPT_HTTPHEADER => 10023,
|
20
|
+
:CURLOPT_FOLLOWLOCATION => 52,
|
21
|
+
:CURLOPT_MAXREDIRS => 68,
|
22
|
+
:CURLOPT_HTTPAUTH => 107,
|
23
|
+
:CURLOPT_USERPWD => 10000 + 5,
|
24
|
+
:CURLOPT_VERBOSE => 41,
|
25
|
+
:CURLOPT_PROXY => 10004,
|
26
|
+
:CURLOPT_VERIFYPEER => 64,
|
27
|
+
:CURLOPT_NOBODY => 44,
|
28
|
+
:CURLOPT_ENCODING => 102,
|
29
|
+
:CURLOPT_SSLCERT => 10025,
|
30
|
+
:CURLOPT_SSLCERTTYPE => 10086,
|
31
|
+
:CURLOPT_SSLKEY => 10087,
|
32
|
+
:CURLOPT_SSLKEYTYPE => 10088,
|
33
|
+
:CURLOPT_KEYPASSWD => 10026,
|
34
|
+
:CURLOPT_CAINFO => 10065,
|
35
|
+
:CURLOPT_CAPATH => 10097
|
36
|
+
}
|
37
|
+
INFO_VALUES = {
|
38
|
+
:CURLINFO_RESPONSE_CODE => 2097154,
|
39
|
+
:CURLINFO_TOTAL_TIME => 3145731,
|
40
|
+
:CURLINFO_HTTPAUTH_AVAIL => 0x200000 + 23,
|
41
|
+
:CURLINFO_EFFECTIVE_URL => 0x100000 + 1
|
42
|
+
}
|
43
|
+
AUTH_TYPES = {
|
44
|
+
:CURLAUTH_BASIC => 1,
|
45
|
+
:CURLAUTH_DIGEST => 2,
|
46
|
+
:CURLAUTH_GSSNEGOTIATE => 4,
|
47
|
+
:CURLAUTH_NTLM => 8,
|
48
|
+
:CURLAUTH_DIGEST_IE => 16
|
49
|
+
}
|
50
|
+
|
51
|
+
def initialize
|
52
|
+
@method = :get
|
53
|
+
@post_dat_set = nil
|
54
|
+
@headers = {}
|
55
|
+
|
56
|
+
set_option(OPTION_VALUES[:CURLOPT_ENCODING], 'zlib') if supports_zlib?
|
57
|
+
end
|
58
|
+
|
59
|
+
def headers=(hash)
|
60
|
+
@headers = hash
|
61
|
+
end
|
62
|
+
|
63
|
+
def proxy=(proxy)
|
64
|
+
set_option(OPTION_VALUES[:CURLOPT_PROXY], proxy)
|
65
|
+
end
|
66
|
+
|
67
|
+
def auth=(authinfo)
|
68
|
+
set_option(OPTION_VALUES[:CURLOPT_USERPWD], "#{authinfo[:username]}:#{authinfo[:password]}")
|
69
|
+
set_option(OPTION_VALUES[:CURLOPT_HTTPAUTH], authinfo[:method]) if authinfo[:method]
|
70
|
+
end
|
71
|
+
|
72
|
+
def auth_methods
|
73
|
+
get_info_long(INFO_VALUES[:CURLINFO_HTTPAUTH_AVAIL])
|
74
|
+
end
|
75
|
+
|
76
|
+
def verbose=(boolean)
|
77
|
+
set_option(OPTION_VALUES[:CURLOPT_VERBOSE], !!boolean ? 1 : 0)
|
78
|
+
end
|
79
|
+
|
80
|
+
def total_time_taken
|
81
|
+
get_info_double(INFO_VALUES[:CURLINFO_TOTAL_TIME])
|
82
|
+
end
|
83
|
+
|
84
|
+
def effective_url
|
85
|
+
get_info_string(INFO_VALUES[:CURLINFO_EFFECTIVE_URL])
|
86
|
+
end
|
87
|
+
|
88
|
+
def response_code
|
89
|
+
get_info_long(INFO_VALUES[:CURLINFO_RESPONSE_CODE])
|
90
|
+
end
|
91
|
+
|
92
|
+
def follow_location=(boolean)
|
93
|
+
if boolean
|
94
|
+
set_option(OPTION_VALUES[:CURLOPT_FOLLOWLOCATION], 1)
|
95
|
+
else
|
96
|
+
set_option(OPTION_VALUES[:CURLOPT_FOLLOWLOCATION], 0)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def max_redirects=(redirects)
|
101
|
+
set_option(OPTION_VALUES[:CURLOPT_MAXREDIRS], redirects)
|
102
|
+
end
|
103
|
+
|
104
|
+
def timeout=(milliseconds)
|
105
|
+
@timeout = milliseconds
|
106
|
+
set_option(OPTION_VALUES[:CURLOPT_NOSIGNAL], 1)
|
107
|
+
set_option(OPTION_VALUES[:CURLOPT_TIMEOUT_MS], milliseconds)
|
108
|
+
end
|
109
|
+
|
110
|
+
def timed_out?
|
111
|
+
@timeout && total_time_taken > @timeout && response_code == 0
|
112
|
+
end
|
113
|
+
|
114
|
+
def supports_zlib?
|
115
|
+
!!(curl_version.match(/zlib/))
|
116
|
+
end
|
117
|
+
|
118
|
+
def request_body=(request_body)
|
119
|
+
@request_body = request_body
|
120
|
+
if @method == :put
|
121
|
+
easy_set_request_body(@request_body)
|
122
|
+
headers["Transfer-Encoding"] = ""
|
123
|
+
headers["Expect"] = ""
|
124
|
+
else
|
125
|
+
self.post_data = request_body
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def user_agent=(user_agent)
|
130
|
+
set_option(OPTION_VALUES[:CURLOPT_USERAGENT], user_agent)
|
131
|
+
end
|
132
|
+
|
133
|
+
def url=(url)
|
134
|
+
@url = url
|
135
|
+
set_option(OPTION_VALUES[:CURLOPT_URL], url)
|
136
|
+
end
|
137
|
+
|
138
|
+
def disable_ssl_peer_verification
|
139
|
+
set_option(OPTION_VALUES[:CURLOPT_VERIFYPEER], 0)
|
140
|
+
end
|
141
|
+
|
142
|
+
def method=(method)
|
143
|
+
@method = method
|
144
|
+
if method == :get
|
145
|
+
set_option(OPTION_VALUES[:CURLOPT_HTTPGET], 1)
|
146
|
+
elsif method == :post
|
147
|
+
set_option(OPTION_VALUES[:CURLOPT_HTTPPOST], 1)
|
148
|
+
self.post_data = ""
|
149
|
+
elsif method == :put
|
150
|
+
set_option(OPTION_VALUES[:CURLOPT_UPLOAD], 1)
|
151
|
+
self.request_body = "" unless @request_body
|
152
|
+
elsif method == :head
|
153
|
+
set_option(OPTION_VALUES[:CURLOPT_NOBODY], 1)
|
154
|
+
else
|
155
|
+
set_option(OPTION_VALUES[:CURLOPT_CUSTOMREQUEST], "DELETE")
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def post_data=(data)
|
160
|
+
@post_data_set = true
|
161
|
+
set_option(OPTION_VALUES[:CURLOPT_POSTFIELDS], data)
|
162
|
+
set_option(OPTION_VALUES[:CURLOPT_POSTFIELDSIZE], data.length)
|
163
|
+
end
|
164
|
+
|
165
|
+
def params=(params)
|
166
|
+
params_string = params.keys.collect do |k|
|
167
|
+
value = params[k]
|
168
|
+
if value.is_a? Hash
|
169
|
+
value.keys.collect {|sk| Rack::Utils.escape("#{k}[#{sk}]") + "=" + Rack::Utils.escape(value[sk].to_s)}
|
170
|
+
elsif value.is_a? Array
|
171
|
+
key = Rack::Utils.escape(k.to_s)
|
172
|
+
value.collect { |v| "#{key}=#{Rack::Utils.escape(v.to_s)}" }.join('&')
|
173
|
+
else
|
174
|
+
"#{Rack::Utils.escape(k.to_s)}=#{Rack::Utils.escape(params[k].to_s)}"
|
175
|
+
end
|
176
|
+
end.flatten.join("&")
|
177
|
+
|
178
|
+
if method == :post
|
179
|
+
self.post_data = params_string
|
180
|
+
else
|
181
|
+
self.url = "#{url}?#{params_string}"
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# Set SSL certificate
|
186
|
+
# " The string should be the file name of your certificate. "
|
187
|
+
# The default format is "PEM" and can be changed with ssl_cert_type=
|
188
|
+
def ssl_cert=(cert, cert_type = "PEM")
|
189
|
+
set_option(OPTION_VALUES[:CURLOPT_SSLCERT], cert)
|
190
|
+
end
|
191
|
+
|
192
|
+
# Set SSL certificate type
|
193
|
+
# " The string should be the format of your certificate. Supported formats are "PEM" and "DER" "
|
194
|
+
def ssl_cert_type=(cert_type)
|
195
|
+
raise "Invalid ssl cert type : '#{cert_type}'..." unless %w(PEM DER).include?(cert_type)
|
196
|
+
set_option(OPTION_VALUES[:CURLOPT_SSLCERTTYPE], cert_type)
|
197
|
+
end
|
198
|
+
|
199
|
+
# Set SSL Key file
|
200
|
+
# " The string should be the file name of your private key. "
|
201
|
+
# The default format is "PEM" and can be changed with ssl_key_type=
|
202
|
+
#
|
203
|
+
def ssl_key=(key, options = {})
|
204
|
+
set_option(OPTION_VALUES[:CURLOPT_SSLKEY], key)
|
205
|
+
end
|
206
|
+
|
207
|
+
# Set SSL Key type
|
208
|
+
# " The string should be the format of your private key. Supported formats are "PEM", "DER" and "ENG". "
|
209
|
+
#
|
210
|
+
def ssk_key_type=(key_type)
|
211
|
+
raise "Invalid ssl key type : '#{key_type}'..." unless %w(PEM DER ENG).include?(cert_type)
|
212
|
+
set_option(OPTION_VALUES[:CURLOPT_SSLKEYTYPE], key_type)
|
213
|
+
end
|
214
|
+
|
215
|
+
def ssl_key_password=(key_password)
|
216
|
+
set_option(OPTION_VALUES[:CURLOPT_KEYPASSWD], key_password)
|
217
|
+
end
|
218
|
+
|
219
|
+
# Set SSL CACERT
|
220
|
+
# " File holding one or more certificates to verify the peer with. "
|
221
|
+
#
|
222
|
+
def ssl_cacert=(cacert)
|
223
|
+
set_option(OPTION_VALUES[:CURLOPT_CAINFO], cacert)
|
224
|
+
end
|
225
|
+
|
226
|
+
# Set CAPATH
|
227
|
+
# " directory holding multiple CA certificates to verify the peer with. The certificate directory must be prepared using the openssl c_rehash utility. "
|
228
|
+
#
|
229
|
+
def ssl_capath=(capath)
|
230
|
+
set_option(OPTION_VALUES[:CURLOPT_CAPATH], capath)
|
231
|
+
end
|
232
|
+
|
233
|
+
def set_option(option, value)
|
234
|
+
if value.class == String
|
235
|
+
easy_setopt_string(option, value)
|
236
|
+
else
|
237
|
+
easy_setopt_long(option, value)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def perform
|
242
|
+
set_headers()
|
243
|
+
easy_perform()
|
244
|
+
response_code()
|
245
|
+
end
|
246
|
+
|
247
|
+
def set_headers
|
248
|
+
headers.each_pair do |key, value|
|
249
|
+
easy_add_header("#{key}: #{value}")
|
250
|
+
end
|
251
|
+
easy_set_headers() unless headers.empty?
|
252
|
+
end
|
253
|
+
|
254
|
+
# gets called when finished and response code is 200-299
|
255
|
+
def success
|
256
|
+
@success.call(self) if @success
|
257
|
+
end
|
258
|
+
|
259
|
+
def on_success(&block)
|
260
|
+
@success = block
|
261
|
+
end
|
262
|
+
|
263
|
+
def on_success=(block)
|
264
|
+
@success = block
|
265
|
+
end
|
266
|
+
|
267
|
+
# gets called when finished and response code is 300-599
|
268
|
+
def failure
|
269
|
+
@failure.call(self) if @failure
|
270
|
+
end
|
271
|
+
|
272
|
+
def on_failure(&block)
|
273
|
+
@failure = block
|
274
|
+
end
|
275
|
+
|
276
|
+
def on_failure=(block)
|
277
|
+
@failure = block
|
278
|
+
end
|
279
|
+
|
280
|
+
def retries
|
281
|
+
@retries ||= 0
|
282
|
+
end
|
283
|
+
|
284
|
+
def increment_retries
|
285
|
+
@retries ||= 0
|
286
|
+
@retries += 1
|
287
|
+
end
|
288
|
+
|
289
|
+
def max_retries
|
290
|
+
@max_retries ||= 40
|
291
|
+
end
|
292
|
+
|
293
|
+
def max_retries?
|
294
|
+
retries >= max_retries
|
295
|
+
end
|
296
|
+
|
297
|
+
def reset
|
298
|
+
@retries = 0
|
299
|
+
@response_code = 0
|
300
|
+
@response_header = ""
|
301
|
+
@response_body = ""
|
302
|
+
easy_reset()
|
303
|
+
end
|
304
|
+
|
305
|
+
def get_info_string(option)
|
306
|
+
easy_getinfo_string(option)
|
307
|
+
end
|
308
|
+
|
309
|
+
def get_info_long(option)
|
310
|
+
easy_getinfo_long(option)
|
311
|
+
end
|
312
|
+
|
313
|
+
def get_info_double(option)
|
314
|
+
easy_getinfo_double(option)
|
315
|
+
end
|
316
|
+
|
317
|
+
def curl_version
|
318
|
+
version
|
319
|
+
end
|
320
|
+
|
321
|
+
end
|
322
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Typhoeus
|
2
|
+
class Filter
|
3
|
+
attr_reader :method_name
|
4
|
+
|
5
|
+
def initialize(method_name, options = {})
|
6
|
+
@method_name = method_name
|
7
|
+
@options = options
|
8
|
+
end
|
9
|
+
|
10
|
+
def apply_filter?(method_name)
|
11
|
+
if @options[:only]
|
12
|
+
if @options[:only].instance_of? Symbol
|
13
|
+
@options[:only] == method_name
|
14
|
+
else
|
15
|
+
@options[:only].include?(method_name)
|
16
|
+
end
|
17
|
+
elsif @options[:except]
|
18
|
+
if @options[:except].instance_of? Symbol
|
19
|
+
@options[:except] != method_name
|
20
|
+
else
|
21
|
+
!@options[:except].include?(method_name)
|
22
|
+
end
|
23
|
+
else
|
24
|
+
true
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,227 @@
|
|
1
|
+
module Typhoeus
|
2
|
+
class Hydra
|
3
|
+
def initialize(options = {})
|
4
|
+
@memoize_requests = true
|
5
|
+
@multi = Multi.new
|
6
|
+
@easy_pool = []
|
7
|
+
initial_pool_size = options[:initial_pool_size] || 10
|
8
|
+
@max_concurrency = options[:max_concurrency] || 200
|
9
|
+
initial_pool_size.times { @easy_pool << Easy.new }
|
10
|
+
@stubs = []
|
11
|
+
@memoized_requests = {}
|
12
|
+
@retrieved_from_cache = {}
|
13
|
+
@queued_requests = []
|
14
|
+
@running_requests = 0
|
15
|
+
@stubbed_request_count = 0
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.hydra
|
19
|
+
@hydra ||= new
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.hydra=(val)
|
23
|
+
@hydra = val
|
24
|
+
end
|
25
|
+
|
26
|
+
def clear_cache_callbacks
|
27
|
+
@cache_setter = nil
|
28
|
+
@cache_getter = nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def clear_stubs
|
32
|
+
@stubs = []
|
33
|
+
end
|
34
|
+
|
35
|
+
def fire_and_forget
|
36
|
+
@queued_requests.each {|r| queue(r, false)}
|
37
|
+
@multi.fire_and_forget
|
38
|
+
end
|
39
|
+
|
40
|
+
def queue(request, obey_concurrency_limit = true)
|
41
|
+
return if assign_to_stub(request)
|
42
|
+
|
43
|
+
if @running_requests >= @max_concurrency && obey_concurrency_limit
|
44
|
+
@queued_requests << request
|
45
|
+
else
|
46
|
+
if request.method == :get
|
47
|
+
if @memoize_requests && @memoized_requests.has_key?(request.url)
|
48
|
+
if response = @retrieved_from_cache[request.url]
|
49
|
+
request.response = response
|
50
|
+
request.call_handlers
|
51
|
+
else
|
52
|
+
@memoized_requests[request.url] << request
|
53
|
+
end
|
54
|
+
else
|
55
|
+
@memoized_requests[request.url] = [] if @memoize_requests
|
56
|
+
get_from_cache_or_queue(request)
|
57
|
+
end
|
58
|
+
else
|
59
|
+
get_from_cache_or_queue(request)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def run
|
65
|
+
while @stubbed_request_count > 0
|
66
|
+
@stubs.each do |m|
|
67
|
+
while request = m.requests.shift
|
68
|
+
@stubbed_request_count -= 1
|
69
|
+
m.response.request = request
|
70
|
+
handle_request(request, m.response)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
@multi.perform
|
76
|
+
@memoized_requests = {}
|
77
|
+
@retrieved_from_cache = {}
|
78
|
+
end
|
79
|
+
|
80
|
+
def disable_memoization
|
81
|
+
@memoize_requests = false
|
82
|
+
end
|
83
|
+
|
84
|
+
def cache_getter(&block)
|
85
|
+
@cache_getter = block
|
86
|
+
end
|
87
|
+
|
88
|
+
def cache_setter(&block)
|
89
|
+
@cache_setter = block
|
90
|
+
end
|
91
|
+
|
92
|
+
def on_complete(&block)
|
93
|
+
@on_complete = block
|
94
|
+
end
|
95
|
+
|
96
|
+
def on_complete=(proc)
|
97
|
+
@on_complete = proc
|
98
|
+
end
|
99
|
+
|
100
|
+
def stub(method, url)
|
101
|
+
@stubs << HydraMock.new(url, method)
|
102
|
+
@stubs.last
|
103
|
+
end
|
104
|
+
|
105
|
+
def assign_to_stub(request)
|
106
|
+
m = @stubs.detect {|stub| stub.matches? request}
|
107
|
+
if m
|
108
|
+
m.add_request(request)
|
109
|
+
@stubbed_request_count += 1
|
110
|
+
else
|
111
|
+
nil
|
112
|
+
end
|
113
|
+
end
|
114
|
+
private :assign_to_stub
|
115
|
+
|
116
|
+
def get_from_cache_or_queue(request)
|
117
|
+
if @cache_getter
|
118
|
+
val = @cache_getter.call(request)
|
119
|
+
if val
|
120
|
+
@retrieved_from_cache[request.url] = val
|
121
|
+
handle_request(request, val, false)
|
122
|
+
else
|
123
|
+
@multi.add(get_easy_object(request))
|
124
|
+
end
|
125
|
+
else
|
126
|
+
@multi.add(get_easy_object(request))
|
127
|
+
end
|
128
|
+
end
|
129
|
+
private :get_from_cache_or_queue
|
130
|
+
|
131
|
+
def get_easy_object(request)
|
132
|
+
@running_requests += 1
|
133
|
+
|
134
|
+
easy = @easy_pool.pop || Easy.new
|
135
|
+
easy.url = request.url
|
136
|
+
easy.method = request.method
|
137
|
+
easy.params = request.params if request.method == :post && !request.params.nil?
|
138
|
+
easy.headers = request.headers if request.headers
|
139
|
+
easy.request_body = request.body if request.body
|
140
|
+
easy.timeout = request.timeout if request.timeout
|
141
|
+
easy.follow_location = request.follow_location if request.follow_location
|
142
|
+
easy.max_redirects = request.max_redirects if request.max_redirects
|
143
|
+
easy.proxy = request.proxy if request.proxy
|
144
|
+
easy.disable_ssl_peer_verification if request.disable_ssl_peer_verification
|
145
|
+
|
146
|
+
easy.on_success do |easy|
|
147
|
+
queue_next
|
148
|
+
handle_request(request, response_from_easy(easy, request))
|
149
|
+
release_easy_object(easy)
|
150
|
+
end
|
151
|
+
easy.on_failure do |easy|
|
152
|
+
queue_next
|
153
|
+
handle_request(request, response_from_easy(easy, request))
|
154
|
+
release_easy_object(easy)
|
155
|
+
end
|
156
|
+
easy.set_headers
|
157
|
+
easy
|
158
|
+
end
|
159
|
+
private :get_easy_object
|
160
|
+
|
161
|
+
def queue_next
|
162
|
+
@running_requests -= 1
|
163
|
+
queue(@queued_requests.pop) unless @queued_requests.empty?
|
164
|
+
end
|
165
|
+
private :queue_next
|
166
|
+
|
167
|
+
def release_easy_object(easy)
|
168
|
+
easy.reset
|
169
|
+
@easy_pool.push easy
|
170
|
+
end
|
171
|
+
private :release_easy_object
|
172
|
+
|
173
|
+
def handle_request(request, response, live_request = true)
|
174
|
+
request.response = response
|
175
|
+
|
176
|
+
if live_request && request.cache_timeout && @cache_setter
|
177
|
+
@cache_setter.call(request)
|
178
|
+
end
|
179
|
+
@on_complete.call(response) if @on_complete
|
180
|
+
|
181
|
+
request.call_handlers
|
182
|
+
if requests = @memoized_requests[request.url]
|
183
|
+
requests.each do |r|
|
184
|
+
r.response = response
|
185
|
+
r.call_handlers
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
private :handle_request
|
190
|
+
|
191
|
+
def response_from_easy(easy, request)
|
192
|
+
Response.new(:code => easy.response_code,
|
193
|
+
:headers => easy.response_header,
|
194
|
+
:body => easy.response_body,
|
195
|
+
:time => easy.total_time_taken,
|
196
|
+
:effective_url => easy.effective_url,
|
197
|
+
:request => request)
|
198
|
+
end
|
199
|
+
private :response_from_easy
|
200
|
+
end
|
201
|
+
|
202
|
+
class HydraMock
|
203
|
+
attr_reader :url, :method, :response, :requests
|
204
|
+
|
205
|
+
def initialize(url, method)
|
206
|
+
@url = url
|
207
|
+
@method = method
|
208
|
+
@requests = []
|
209
|
+
end
|
210
|
+
|
211
|
+
def add_request(request)
|
212
|
+
@requests << request
|
213
|
+
end
|
214
|
+
|
215
|
+
def and_return(val)
|
216
|
+
@response = val
|
217
|
+
end
|
218
|
+
|
219
|
+
def matches?(request)
|
220
|
+
if url.kind_of?(String)
|
221
|
+
request.method == method && request.url == url
|
222
|
+
else
|
223
|
+
request.method == method && url =~ request.url
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Typhoeus
|
2
|
+
class Multi
|
3
|
+
attr_reader :easy_handles
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
reset_easy_handles
|
7
|
+
end
|
8
|
+
|
9
|
+
def remove(easy)
|
10
|
+
multi_remove_handle(easy)
|
11
|
+
end
|
12
|
+
|
13
|
+
def add(easy)
|
14
|
+
easy.set_headers()
|
15
|
+
@easy_handles << easy
|
16
|
+
multi_add_handle(easy)
|
17
|
+
end
|
18
|
+
|
19
|
+
def perform()
|
20
|
+
while active_handle_count > 0 do
|
21
|
+
multi_perform
|
22
|
+
end
|
23
|
+
reset_easy_handles
|
24
|
+
end
|
25
|
+
|
26
|
+
def cleanup()
|
27
|
+
multi_cleanup
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
def reset_easy_handles
|
32
|
+
@easy_handles = []
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|