typhoeus 0.4.2 → 0.5.0.pre

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.
Files changed (47) hide show
  1. data/CHANGELOG.md +74 -28
  2. data/README.md +20 -422
  3. data/Rakefile +21 -12
  4. data/lib/typhoeus/config.rb +11 -0
  5. data/lib/typhoeus/hydra.rb +26 -236
  6. data/lib/typhoeus/hydras/easy_factory.rb +67 -0
  7. data/lib/typhoeus/hydras/easy_pool.rb +40 -0
  8. data/lib/typhoeus/hydras/memoizable.rb +53 -0
  9. data/lib/typhoeus/hydras/queueable.rb +46 -0
  10. data/lib/typhoeus/hydras/runnable.rb +18 -0
  11. data/lib/typhoeus/request.rb +56 -241
  12. data/lib/typhoeus/requests/actions.rb +17 -0
  13. data/lib/typhoeus/requests/callbacks.rb +48 -0
  14. data/lib/typhoeus/requests/marshal.rb +21 -0
  15. data/lib/typhoeus/requests/memoizable.rb +36 -0
  16. data/lib/typhoeus/requests/operations.rb +51 -0
  17. data/lib/typhoeus/requests/responseable.rb +29 -0
  18. data/lib/typhoeus/response.rb +23 -118
  19. data/lib/typhoeus/responses/header.rb +50 -0
  20. data/lib/typhoeus/responses/informations.rb +43 -0
  21. data/lib/typhoeus/responses/legacy.rb +26 -0
  22. data/lib/typhoeus/responses/status.rb +77 -0
  23. data/lib/typhoeus/version.rb +3 -1
  24. data/lib/typhoeus.rb +31 -45
  25. metadata +45 -48
  26. data/lib/typhoeus/curl.rb +0 -453
  27. data/lib/typhoeus/easy/auth.rb +0 -14
  28. data/lib/typhoeus/easy/callbacks.rb +0 -33
  29. data/lib/typhoeus/easy/ffi_helper.rb +0 -61
  30. data/lib/typhoeus/easy/infos.rb +0 -90
  31. data/lib/typhoeus/easy/options.rb +0 -115
  32. data/lib/typhoeus/easy/proxy.rb +0 -20
  33. data/lib/typhoeus/easy/ssl.rb +0 -82
  34. data/lib/typhoeus/easy.rb +0 -115
  35. data/lib/typhoeus/filter.rb +0 -28
  36. data/lib/typhoeus/form.rb +0 -61
  37. data/lib/typhoeus/header.rb +0 -54
  38. data/lib/typhoeus/hydra/callbacks.rb +0 -24
  39. data/lib/typhoeus/hydra/connect_options.rb +0 -61
  40. data/lib/typhoeus/hydra/stubbing.rb +0 -68
  41. data/lib/typhoeus/hydra_mock.rb +0 -131
  42. data/lib/typhoeus/multi.rb +0 -146
  43. data/lib/typhoeus/param_processor.rb +0 -43
  44. data/lib/typhoeus/remote.rb +0 -306
  45. data/lib/typhoeus/remote_method.rb +0 -108
  46. data/lib/typhoeus/remote_proxy_object.rb +0 -50
  47. data/lib/typhoeus/utils.rb +0 -50
@@ -1,247 +1,37 @@
1
- require 'typhoeus/hydra/callbacks'
2
- require 'typhoeus/hydra/connect_options'
3
- require 'typhoeus/hydra/stubbing'
1
+ require 'typhoeus/hydras/easy_factory'
2
+ require 'typhoeus/hydras/easy_pool'
3
+ require 'typhoeus/hydras/memoizable'
4
+ require 'typhoeus/hydras/queueable'
5
+ require 'typhoeus/hydras/runnable'
4
6
 
5
7
  module Typhoeus
6
- class Hydra
7
- include ConnectOptions
8
- include Stubbing
9
- extend Callbacks
10
-
11
- def initialize(options = {})
12
- @memoize_requests = true
13
- @multi = Multi.new
14
- @easy_pool = []
15
- initial_pool_size = options[:initial_pool_size] || 10
16
- @max_concurrency = options[:max_concurrency] || 200
17
- initial_pool_size.times { @easy_pool << Easy.new }
18
- @memoized_requests = {}
19
- @retrieved_from_cache = {}
20
- @queued_requests = []
21
- @running_requests = 0
22
8
 
23
- self.stubs = []
24
- @active_stubs = []
25
- end
9
+ # Hydra manages making parallel HTTP requests. This
10
+ # is archived by using libcurls multi interface. The
11
+ # benefits are that you don't have to worry running
12
+ # the requests by yourself.
13
+ class Hydra
14
+ include Hydras::EasyPool
15
+ include Hydras::Queueable
16
+ include Hydras::Runnable
17
+ include Hydras::Memoizable
26
18
 
27
- def self.hydra
28
- @hydra ||= new
29
- end
30
-
31
- def self.hydra=(val)
32
- @hydra = val
33
- end
19
+ attr_reader :max_concurrency, :multi
34
20
 
21
+ # Create a new hydra.
35
22
  #
36
- # Abort the run on a best-effort basis.
23
+ # @example Create a hydra.
24
+ # Typhoeus::Hydra.new
37
25
  #
38
- # It won't abort the current burst of @max_concurrency requests,
39
- # however it won't fire the rest of the queued requests so the run
40
- # will be aborted as soon as possible...
26
+ # @param [ Hash ] options The options hash.
41
27
  #
42
- def abort
43
- @queued_requests.clear
44
- end
45
-
46
- def clear_cache_callbacks
47
- @cache_setter = nil
48
- @cache_getter = nil
49
- end
50
-
51
- def fire_and_forget
52
- @queued_requests.each {|r| queue(r, false)}
53
- @multi.fire_and_forget
54
- end
55
-
56
- def queue(request, obey_concurrency_limit = true)
57
- return if assign_to_stub(request)
58
-
59
- # At this point, we are running over live HTTP. Make sure we haven't
60
- # disabled live requests.
61
- check_allow_net_connect!(request)
62
-
63
- if @running_requests >= @max_concurrency && obey_concurrency_limit
64
- @queued_requests << request
65
- else
66
- if request.method == :get
67
- if @memoize_requests && @memoized_requests.key?(request.url)
68
- if response = @retrieved_from_cache[request.url]
69
- request.response = response
70
- request.call_handlers
71
- else
72
- @memoized_requests[request.url] << request
73
- end
74
- else
75
- @memoized_requests[request.url] = [] if @memoize_requests
76
- get_from_cache_or_queue(request)
77
- end
78
- else
79
- get_from_cache_or_queue(request)
80
- end
81
- end
82
- end
83
-
84
- def run
85
- while !@active_stubs.empty?
86
- m = @active_stubs.first
87
- while request = m.requests.shift
88
- response = m.response
89
- response.request = request
90
- handle_request(request, response)
91
- end
92
- @active_stubs.delete(m)
93
- end
94
-
95
- @multi.perform
96
- ensure
97
- @multi.reset_easy_handles{|easy| release_easy_object(easy)}
98
- @memoized_requests = {}
99
- @retrieved_from_cache = {}
100
- @running_requests = 0
101
- end
102
-
103
- def disable_memoization
104
- @memoize_requests = false
105
- end
106
-
107
- def cache_getter(&block)
108
- @cache_getter = block
109
- end
110
-
111
- def cache_setter(&block)
112
- @cache_setter = block
113
- end
114
-
115
- def on_complete(&block)
116
- @on_complete = block
117
- end
118
-
119
- def on_complete=(proc)
120
- @on_complete = proc
121
- end
122
-
123
- private
124
-
125
- def get_from_cache_or_queue(request)
126
- if @cache_getter
127
- val = @cache_getter.call(request)
128
- if val
129
- @retrieved_from_cache[request.url] = val
130
- queue_next
131
- handle_request(request, val, false)
132
- else
133
- @multi.add(get_easy_object(request))
134
- end
135
- else
136
- @multi.add(get_easy_object(request))
137
- end
138
- end
139
-
140
- def get_easy_object(request)
141
- @running_requests += 1
142
-
143
- easy = @easy_pool.pop || Easy.new
144
- easy.verbose = request.verbose
145
- if request.username || request.password
146
- auth = { :username => request.username, :password => request.password }
147
- auth[:method] = request.auth_method if request.auth_method
148
- easy.auth = auth
149
- end
150
-
151
- if request.proxy
152
- proxy = { :server => request.proxy }
153
- proxy[:type] = request.proxy_type if request.proxy_type
154
- easy.proxy = proxy if request.proxy
155
- end
156
-
157
- if request.proxy_username || request.proxy_password
158
- auth = { :username => request.proxy_username, :password => request.proxy_password }
159
- auth[:method] = request.proxy_auth_method if request.proxy_auth_method
160
- easy.proxy_auth = auth
161
- end
162
-
163
- easy.url = request.url
164
- easy.method = request.method
165
- easy.params = request.params if [:post, :put].include?(request.method) && !request.params.nil?
166
- easy.headers = request.headers if request.headers
167
- easy.request_body = request.body if [:post, :put].include?(request.method) && !request.body.nil?
168
- easy.timeout = request.timeout if request.timeout
169
- easy.connect_timeout = request.connect_timeout if request.connect_timeout
170
- easy.interface = request.interface if request.interface
171
- easy.follow_location = request.follow_location if request.follow_location
172
- easy.max_redirects = request.max_redirects if request.max_redirects
173
- easy.disable_ssl_peer_verification if request.disable_ssl_peer_verification
174
- easy.disable_ssl_host_verification if request.disable_ssl_host_verification
175
- easy.ssl_cert = request.ssl_cert
176
- easy.ssl_cert_type = request.ssl_cert_type
177
- easy.ssl_key = request.ssl_key
178
- easy.ssl_key_type = request.ssl_key_type
179
- easy.ssl_key_password = request.ssl_key_password
180
- easy.ssl_cacert = request.ssl_cacert
181
- easy.ssl_capath = request.ssl_capath
182
- easy.ssl_version = request.ssl_version || :default
183
- easy.verbose = request.verbose
184
-
185
- easy.on_success do |easy|
186
- queue_next
187
- handle_request(request, response_from_easy(easy, request))
188
- release_easy_object(easy)
189
- end
190
- easy.on_failure do |easy|
191
- queue_next
192
- handle_request(request, response_from_easy(easy, request))
193
- release_easy_object(easy)
194
- end
195
- easy.set_headers
196
- easy
197
- end
198
-
199
- def queue_next
200
- @running_requests -= 1
201
- queue(@queued_requests.shift) unless @queued_requests.empty?
202
- end
203
-
204
- def release_easy_object(easy)
205
- easy.reset
206
- @easy_pool.push easy
207
- end
208
-
209
- def handle_request(request, response, live_request = true)
210
- request.response = response
211
-
212
- self.class.run_global_hooks_for(:after_request_before_on_complete,
213
- request)
214
-
215
- if live_request && request.cache_timeout && @cache_setter
216
- @cache_setter.call(request)
217
- end
218
- @on_complete.call(response) if @on_complete
219
-
220
- request.call_handlers
221
- if requests = @memoized_requests[request.url]
222
- requests.each do |r|
223
- r.response = response
224
- r.call_handlers
225
- end
226
- end
227
- end
228
-
229
- def response_from_easy(easy, request)
230
- Response.new(:code => easy.response_code,
231
- :headers => easy.response_header,
232
- :body => easy.response_body,
233
- :time => easy.total_time_taken,
234
- :start_transfer_time => easy.start_transfer_time,
235
- :app_connect_time => easy.app_connect_time,
236
- :pretransfer_time => easy.pretransfer_time,
237
- :connect_time => easy.connect_time,
238
- :name_lookup_time => easy.name_lookup_time,
239
- :effective_url => easy.effective_url,
240
- :primary_ip => easy.primary_ip,
241
- :curl_return_code => easy.curl_return_code,
242
- :curl_error_message => easy.curl_error_message,
243
- :redirect_count => easy.redirect_count,
244
- :request => request)
28
+ # @option options :max_concurrency [ Integer ] Number
29
+ # of max concurrent connections to create. Default is
30
+ # 200.
31
+ def initialize(options = {})
32
+ @options = options
33
+ @max_concurrency = @options.fetch(:max_concurrency, 200)
34
+ @multi = Ethon::Multi.new
245
35
  end
246
36
  end
247
37
  end
@@ -0,0 +1,67 @@
1
+ module Typhoeus
2
+ module Hydras
3
+
4
+ # This is a Factory for easies to be used in the hydra.
5
+ # Before an easy is ready to be added to a multi, it needs
6
+ # to be prepared and the on_complete callback to be set.
7
+ # This is done by this class.
8
+ class EasyFactory
9
+ attr_reader :request, :hydra
10
+
11
+ # Create an easy factory.
12
+ #
13
+ # @example Create easy factory.
14
+ # Typhoeus::Hydras::EasyFactory.new(request, hydra)
15
+ #
16
+ # @param [ Request ] request The request to build an easy for.
17
+ # @param [ Hydra ] hydra The hydra to build an easy for.
18
+ def initialize(request, hydra)
19
+ @request = request
20
+ @hydra = hydra
21
+ end
22
+
23
+ # Return the easy in question.
24
+ #
25
+ # @example Return easy.
26
+ # easy_factory.easy
27
+ #
28
+ # @return [ Ethon::Easy ] The easy.
29
+ def easy
30
+ @easy ||= hydra.get_easy
31
+ end
32
+
33
+ # Fabricated and prepared easy.
34
+ #
35
+ # @example Prepared easy.
36
+ # easy_factory.get
37
+ #
38
+ # @return [ Ethon::Easy ] The prepared easy.
39
+ def get
40
+ easy.http_request(
41
+ request.url,
42
+ request.options.fetch(:method, :get),
43
+ request.options.reject{|k,_| k==:method}
44
+ )
45
+ easy.prepare
46
+ set_callback
47
+ easy
48
+ end
49
+
50
+ # Sets on_complete callback on easy in order to be able to
51
+ # track progress.
52
+ #
53
+ # @example Set callback.
54
+ # easy_factory.set_callback
55
+ #
56
+ # @return [ Ethon::Easy ] The easy.
57
+ def set_callback
58
+ easy.on_complete do |easy|
59
+ request.response = Response.new(easy.to_hash)
60
+ hydra.release_easy(easy)
61
+ hydra.queue(hydra.queued_requests.shift) unless hydra.queued_requests.empty?
62
+ request.complete
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,40 @@
1
+ module Typhoeus
2
+ module Hydras
3
+
4
+ # The easy pool stores already initialized
5
+ # easy handles for future use. This is useful
6
+ # because creating them is quite expensive.
7
+ module EasyPool
8
+
9
+ # Return the easy pool.
10
+ #
11
+ # @example Return easy pool.
12
+ # hydra.easy_pool
13
+ #
14
+ # @return [ Array ] The easy pool.
15
+ def easy_pool
16
+ @easy_pool ||= []
17
+ end
18
+
19
+ # Releases easy into pool. The easy handle is
20
+ # resetted before it gets back in.
21
+ #
22
+ # @example Release easy.
23
+ # hydra.release_easy(easy)
24
+ def release_easy(easy)
25
+ easy.reset
26
+ easy_pool << easy
27
+ end
28
+
29
+ # Return an easy from pool.
30
+ #
31
+ # @example Return easy.
32
+ # hydra.get_easy
33
+ #
34
+ # @return [ Ethon::Easy ] The easy.
35
+ def get_easy
36
+ easy_pool.pop || Ethon::Easy.new
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,53 @@
1
+ module Typhoeus
2
+ module Hydras
3
+
4
+ # This module handles the GET request memoization
5
+ # on the hydra side. Memoization needs to be turned
6
+ # on:
7
+ # Typhoeus.configre do |config|
8
+ # config.memoize = true
9
+ # end
10
+ module Memoizable
11
+
12
+ # Return the memory.
13
+ #
14
+ # @example Return the memory.
15
+ # hydra.memory
16
+ #
17
+ # @return [ Hash ] The memory.
18
+ def memory
19
+ @memory ||= {}
20
+ end
21
+
22
+ # Overrides queue in order to check before if request
23
+ # is memoizable and already in memory. If thats the case,
24
+ # super is not called, instead the response is set and
25
+ # the on_complete callback called.
26
+ #
27
+ # @example Queue the request.
28
+ # hydra.queue(request)
29
+ #
30
+ # @param [ Request ] request The request to enqueue.
31
+ #
32
+ # @return [ Request ] The queued request.
33
+ def queue(request)
34
+ if request.memoizable? && memory.has_key?(request)
35
+ request.instance_variable_set(:@response, memory[request])
36
+ request.complete
37
+ else
38
+ super
39
+ end
40
+ end
41
+
42
+ # Overrides run to clear the memory is cleared after
43
+ # each run.
44
+ #
45
+ # @example Run hydra.
46
+ # hydra.run
47
+ def run
48
+ super
49
+ memory.clear
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,46 @@
1
+ module Typhoeus
2
+ module Hydras # :nodoc:
3
+
4
+ # This module handles the request queueing on
5
+ # hydra.
6
+ module Queueable
7
+
8
+ # Return the queued requests.
9
+ #
10
+ # @example Return queued requests.
11
+ # hydra.queued_requests
12
+ #
13
+ # @return [ Array ] The queued requests.
14
+ def queued_requests
15
+ @queued_requests ||= []
16
+ end
17
+
18
+ # Abort the current hydra run as good as
19
+ # possible. This means that it only
20
+ # clears the queued requests and can't do
21
+ # anything about already running requests.
22
+ #
23
+ # @example Abort hydra.
24
+ # hydra.abort
25
+ def abort
26
+ queued_requests.clear
27
+ end
28
+
29
+ # Enqueues a request in order to be performed
30
+ # by the hydra. This can even be done while
31
+ # the hydra is running. Also sets hydra on
32
+ # request.
33
+ #
34
+ # @example Queue request.
35
+ # hydra.queue(request)
36
+ def queue(request)
37
+ request.hydra = self
38
+ if multi.easy_handles.size < max_concurrency
39
+ multi.add(Hydras::EasyFactory.new(request, self).get)
40
+ else
41
+ queued_requests << request
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,18 @@
1
+ module Typhoeus
2
+ module Hydras
3
+
4
+ # This module contains logic to run a hydra.
5
+ module Runnable
6
+
7
+ # Start the hydra run.
8
+ #
9
+ # @example Start hydra run.
10
+ # hydra.run
11
+ #
12
+ # @return [ Symbol ] Return value from multi.perform.
13
+ def run
14
+ multi.perform
15
+ end
16
+ end
17
+ end
18
+ end