marnen-typhoeus 0.3.4

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 (57) hide show
  1. data/CHANGELOG.markdown +84 -0
  2. data/Gemfile +3 -0
  3. data/Gemfile.lock +56 -0
  4. data/LICENSE +20 -0
  5. data/Rakefile +43 -0
  6. data/ext/typhoeus/.gitignore +7 -0
  7. data/ext/typhoeus/extconf.rb +65 -0
  8. data/ext/typhoeus/native.c +12 -0
  9. data/ext/typhoeus/native.h +22 -0
  10. data/ext/typhoeus/typhoeus_easy.c +232 -0
  11. data/ext/typhoeus/typhoeus_easy.h +20 -0
  12. data/ext/typhoeus/typhoeus_form.c +59 -0
  13. data/ext/typhoeus/typhoeus_form.h +13 -0
  14. data/ext/typhoeus/typhoeus_multi.c +217 -0
  15. data/ext/typhoeus/typhoeus_multi.h +16 -0
  16. data/lib/typhoeus.rb +58 -0
  17. data/lib/typhoeus/.gitignore +1 -0
  18. data/lib/typhoeus/easy.rb +413 -0
  19. data/lib/typhoeus/filter.rb +28 -0
  20. data/lib/typhoeus/form.rb +32 -0
  21. data/lib/typhoeus/hydra.rb +250 -0
  22. data/lib/typhoeus/hydra/callbacks.rb +24 -0
  23. data/lib/typhoeus/hydra/connect_options.rb +61 -0
  24. data/lib/typhoeus/hydra/stubbing.rb +68 -0
  25. data/lib/typhoeus/hydra_mock.rb +131 -0
  26. data/lib/typhoeus/multi.rb +37 -0
  27. data/lib/typhoeus/normalized_header_hash.rb +58 -0
  28. data/lib/typhoeus/remote.rb +306 -0
  29. data/lib/typhoeus/remote_method.rb +108 -0
  30. data/lib/typhoeus/remote_proxy_object.rb +50 -0
  31. data/lib/typhoeus/request.rb +269 -0
  32. data/lib/typhoeus/response.rb +122 -0
  33. data/lib/typhoeus/service.rb +20 -0
  34. data/lib/typhoeus/utils.rb +74 -0
  35. data/lib/typhoeus/version.rb +3 -0
  36. data/spec/fixtures/placeholder.gif +0 -0
  37. data/spec/fixtures/placeholder.txt +1 -0
  38. data/spec/fixtures/placeholder.ukn +0 -0
  39. data/spec/fixtures/result_set.xml +60 -0
  40. data/spec/servers/app.rb +97 -0
  41. data/spec/spec_helper.rb +19 -0
  42. data/spec/support/typhoeus_localhost_server.rb +58 -0
  43. data/spec/typhoeus/easy_spec.rb +391 -0
  44. data/spec/typhoeus/filter_spec.rb +35 -0
  45. data/spec/typhoeus/form_spec.rb +117 -0
  46. data/spec/typhoeus/hydra_mock_spec.rb +300 -0
  47. data/spec/typhoeus/hydra_spec.rb +602 -0
  48. data/spec/typhoeus/multi_spec.rb +74 -0
  49. data/spec/typhoeus/normalized_header_hash_spec.rb +41 -0
  50. data/spec/typhoeus/remote_method_spec.rb +141 -0
  51. data/spec/typhoeus/remote_proxy_object_spec.rb +65 -0
  52. data/spec/typhoeus/remote_spec.rb +695 -0
  53. data/spec/typhoeus/request_spec.rb +387 -0
  54. data/spec/typhoeus/response_spec.rb +192 -0
  55. data/spec/typhoeus/utils_spec.rb +22 -0
  56. data/typhoeus.gemspec +35 -0
  57. metadata +235 -0
@@ -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,32 @@
1
+ require 'mime/types'
2
+
3
+ module Typhoeus
4
+ class Form
5
+ attr_accessor :params
6
+ attr_reader :traversal
7
+
8
+ def initialize(params = {})
9
+ @params = params
10
+ end
11
+
12
+ def traversal
13
+ @traversal ||= Typhoeus::Utils.traverse_params_hash(params)
14
+ end
15
+
16
+ def process!
17
+ # add params
18
+ traversal[:params].each { |p| formadd_param(p[0], p[1]) }
19
+
20
+ # add files
21
+ traversal[:files].each { |file_args| formadd_file(*file_args) }
22
+ end
23
+
24
+ def multipart?
25
+ !traversal[:files].empty?
26
+ end
27
+
28
+ def to_s
29
+ Typhoeus::Utils.traversal_to_param_string(traversal, false)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,250 @@
1
+ require 'typhoeus/hydra/callbacks'
2
+ require 'typhoeus/hydra/connect_options'
3
+ require 'typhoeus/hydra/stubbing'
4
+
5
+ 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
+
23
+ self.stubs = []
24
+ @active_stubs = []
25
+ end
26
+
27
+ def self.hydra
28
+ @hydra ||= new
29
+ end
30
+
31
+ def self.hydra=(val)
32
+ @hydra = val
33
+ end
34
+
35
+ #
36
+ # Abort the run on a best-effort basis.
37
+ #
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...
41
+ #
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.has_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
+ def get_from_cache_or_queue(request)
124
+ if @cache_getter
125
+ val = @cache_getter.call(request)
126
+ if val
127
+ @retrieved_from_cache[request.url] = val
128
+ queue_next
129
+ handle_request(request, val, false)
130
+ else
131
+ @multi.add(get_easy_object(request))
132
+ end
133
+ else
134
+ @multi.add(get_easy_object(request))
135
+ end
136
+ end
137
+ private :get_from_cache_or_queue
138
+
139
+ def get_easy_object(request)
140
+ @running_requests += 1
141
+
142
+ easy = @easy_pool.pop || Easy.new
143
+ easy.verbose = request.verbose
144
+ if request.username || request.password
145
+ auth = { :username => request.username, :password => request.password }
146
+ auth[:method] = Typhoeus::Easy::AUTH_TYPES["CURLAUTH_#{request.auth_method.to_s.upcase}".to_sym] if request.auth_method
147
+ easy.auth = auth
148
+ end
149
+
150
+ if request.proxy
151
+ proxy = { :server => request.proxy }
152
+ proxy[:type] = Typhoeus::Easy::PROXY_TYPES["CURLPROXY_#{request.proxy_type.to_s.upcase}".to_sym] if request.proxy_type
153
+ easy.proxy = proxy if request.proxy
154
+ end
155
+
156
+ if request.proxy_username || request.proxy_password
157
+ auth = { :username => request.proxy_username, :password => request.proxy_password }
158
+ auth[:method] = Typhoeus::Easy::AUTH_TYPES["CURLAUTH_#{request.proxy_auth_method.to_s.upcase}".to_sym] if request.proxy_auth_method
159
+ easy.proxy_auth = auth
160
+ end
161
+
162
+ easy.url = request.url
163
+ easy.method = request.method
164
+ easy.params = request.params if request.method == :post && !request.params.nil?
165
+ easy.headers = request.headers if request.headers
166
+ easy.request_body = request.body if request.body
167
+ easy.timeout = request.timeout if request.timeout
168
+ easy.connect_timeout = request.connect_timeout if request.connect_timeout
169
+ easy.interface = request.interface if request.interface
170
+ easy.follow_location = request.follow_location if request.follow_location
171
+ easy.max_redirects = request.max_redirects if request.max_redirects
172
+ easy.disable_ssl_peer_verification if request.disable_ssl_peer_verification
173
+ easy.disable_ssl_host_verification if request.disable_ssl_host_verification
174
+ easy.ssl_cert = request.ssl_cert
175
+ easy.ssl_cert_type = request.ssl_cert_type
176
+ easy.ssl_key = request.ssl_key
177
+ easy.ssl_key_type = request.ssl_key_type
178
+ easy.ssl_key_password = request.ssl_key_password
179
+ easy.ssl_cacert = request.ssl_cacert
180
+ easy.ssl_capath = request.ssl_capath
181
+ easy.ssl_version = request.ssl_version || :default
182
+ easy.verbose = request.verbose
183
+
184
+ easy.on_success do |easy|
185
+ queue_next
186
+ handle_request(request, response_from_easy(easy, request))
187
+ release_easy_object(easy)
188
+ end
189
+ easy.on_failure do |easy|
190
+ queue_next
191
+ handle_request(request, response_from_easy(easy, request))
192
+ release_easy_object(easy)
193
+ end
194
+ easy.set_headers
195
+ easy
196
+ end
197
+ private :get_easy_object
198
+
199
+ def queue_next
200
+ @running_requests -= 1
201
+ queue(@queued_requests.shift) unless @queued_requests.empty?
202
+ end
203
+ private :queue_next
204
+
205
+ def release_easy_object(easy)
206
+ easy.reset
207
+ @easy_pool.push easy
208
+ end
209
+ private :release_easy_object
210
+
211
+ def handle_request(request, response, live_request = true)
212
+ request.response = response
213
+
214
+ self.class.run_global_hooks_for(:after_request_before_on_complete,
215
+ request)
216
+
217
+ if live_request && request.cache_timeout && @cache_setter
218
+ @cache_setter.call(request)
219
+ end
220
+ @on_complete.call(response) if @on_complete
221
+
222
+ request.call_handlers
223
+ if requests = @memoized_requests[request.url]
224
+ requests.each do |r|
225
+ r.response = response
226
+ r.call_handlers
227
+ end
228
+ end
229
+ end
230
+ private :handle_request
231
+
232
+ def response_from_easy(easy, request)
233
+ Response.new(:code => easy.response_code,
234
+ :headers => easy.response_header,
235
+ :body => easy.response_body,
236
+ :time => easy.total_time_taken,
237
+ :start_transfer_time => easy.start_transfer_time,
238
+ :app_connect_time => easy.app_connect_time,
239
+ :pretransfer_time => easy.pretransfer_time,
240
+ :connect_time => easy.connect_time,
241
+ :name_lookup_time => easy.name_lookup_time,
242
+ :effective_url => easy.effective_url,
243
+ :primary_ip => easy.primary_ip,
244
+ :curl_return_code => easy.curl_return_code,
245
+ :curl_error_message => easy.curl_error_message,
246
+ :request => request)
247
+ end
248
+ private :response_from_easy
249
+ end
250
+ end
@@ -0,0 +1,24 @@
1
+ module Typhoeus
2
+ class Hydra
3
+ module Callbacks
4
+ def self.extended(base)
5
+ class << base
6
+ attr_accessor :global_hooks
7
+ end
8
+ base.global_hooks = Hash.new { |h, k| h[k] = [] }
9
+ end
10
+
11
+ def after_request_before_on_complete(&block)
12
+ global_hooks[:after_request_before_on_complete] << block
13
+ end
14
+
15
+ def run_global_hooks_for(name, request)
16
+ global_hooks[name].each { |hook| hook.call(request) }
17
+ end
18
+
19
+ def clear_global_hooks
20
+ global_hooks.clear
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,61 @@
1
+ module Typhoeus
2
+ class Hydra
3
+ class NetConnectNotAllowedError < StandardError; end
4
+
5
+ module ConnectOptions
6
+ def self.included(base)
7
+ base.extend(ClassMethods)
8
+ end
9
+
10
+ # This method checks to see if we should raise an error on
11
+ # a request.
12
+ #
13
+ # @raises NetConnectNotAllowedError
14
+ def check_allow_net_connect!(request)
15
+ return if Typhoeus::Hydra.allow_net_connect?
16
+ return if Typhoeus::Hydra.ignore_hosts.include?(request.host_domain)
17
+
18
+ raise NetConnectNotAllowedError, "Real HTTP requests are not allowed. Unregistered request: #{request.inspect}"
19
+ end
20
+ private :check_allow_net_connect!
21
+
22
+ module ClassMethods
23
+ def self.extended(base)
24
+ class << base
25
+ attr_accessor :allow_net_connect
26
+ attr_accessor :ignore_localhost
27
+ end
28
+ base.allow_net_connect = true
29
+ base.ignore_localhost = false
30
+ end
31
+
32
+ # Returns whether we allow external HTTP connections.
33
+ # Useful for mocking/tests.
34
+ #
35
+ # @return [boolean] true/false
36
+ def allow_net_connect?
37
+ allow_net_connect
38
+ end
39
+
40
+ def ignore_localhost?
41
+ ignore_localhost
42
+ end
43
+
44
+ def ignore_hosts
45
+ @ignore_hosts ||= []
46
+
47
+ if ignore_localhost?
48
+ @ignore_hosts + Typhoeus::Request::LOCALHOST_ALIASES
49
+ else
50
+ @ignore_hosts
51
+ end
52
+ end
53
+
54
+ def ignore_hosts=(hosts)
55
+ @ignore_hosts = hosts
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+
@@ -0,0 +1,68 @@
1
+ module Typhoeus
2
+ class Hydra
3
+ module Stubbing
4
+ module SharedMethods
5
+ def stub(method, url, options = {})
6
+ stubs << HydraMock.new(url, method, options)
7
+ stubs.last
8
+ end
9
+
10
+ def clear_stubs
11
+ self.stubs = []
12
+ end
13
+
14
+ def register_stub_finder(&block)
15
+ stub_finders << block
16
+ end
17
+
18
+ def find_stub_from_request(request)
19
+ stub_finders.each do |finder|
20
+ if response = finder.call(request)
21
+ mock = HydraMock.new(/.*/, :any)
22
+ mock.and_return(response)
23
+ return mock
24
+ end
25
+ end
26
+
27
+ stubs.detect { |stub| stub.matches?(request) }
28
+ end
29
+
30
+ def stub_finders
31
+ @stub_finders ||= []
32
+ end
33
+
34
+ def self.extended(base)
35
+ class << base
36
+ attr_accessor :stubs
37
+ end
38
+ base.stubs = []
39
+ end
40
+ end
41
+
42
+ def self.included(base)
43
+ base.extend(SharedMethods)
44
+ base.class_eval do
45
+ attr_accessor :stubs
46
+ end
47
+ end
48
+
49
+ def assign_to_stub(request)
50
+ m = find_stub_from_request(request)
51
+
52
+ # Fallback to global stubs.
53
+ m ||= self.class.find_stub_from_request(request)
54
+
55
+ if m
56
+ m.add_request(request)
57
+ @active_stubs << m
58
+ m
59
+ else
60
+ nil
61
+ end
62
+ end
63
+ private :assign_to_stub
64
+
65
+ include SharedMethods
66
+ end
67
+ end
68
+ end