typhoeus 0.1.6 → 0.1.28

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