typhoeus 0.4.2 → 0.5.0.rc

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.md +86 -28
  2. data/Gemfile +17 -1
  3. data/README.md +20 -422
  4. data/Rakefile +21 -12
  5. data/lib/typhoeus/config.rb +47 -0
  6. data/lib/typhoeus/errors/no_stub.rb +12 -0
  7. data/lib/typhoeus/errors/typhoeus_error.rb +8 -0
  8. data/lib/typhoeus/errors.rb +9 -0
  9. data/lib/typhoeus/expectation.rb +174 -0
  10. data/lib/typhoeus/hydra/before.rb +30 -0
  11. data/lib/typhoeus/hydra/block_connection.rb +35 -0
  12. data/lib/typhoeus/hydra/easy_factory.rb +79 -0
  13. data/lib/typhoeus/hydra/easy_pool.rb +42 -0
  14. data/lib/typhoeus/hydra/memoizable.rb +55 -0
  15. data/lib/typhoeus/hydra/queueable.rb +48 -0
  16. data/lib/typhoeus/hydra/runnable.rb +21 -0
  17. data/lib/typhoeus/hydra/stubbable.rb +26 -0
  18. data/lib/typhoeus/hydra.rb +83 -236
  19. data/lib/typhoeus/request/actions.rb +125 -0
  20. data/lib/typhoeus/request/before.rb +30 -0
  21. data/lib/typhoeus/request/block_connection.rb +52 -0
  22. data/lib/typhoeus/request/callbacks.rb +98 -0
  23. data/lib/typhoeus/request/marshal.rb +21 -0
  24. data/lib/typhoeus/request/memoizable.rb +38 -0
  25. data/lib/typhoeus/request/operations.rb +71 -0
  26. data/lib/typhoeus/request/responseable.rb +29 -0
  27. data/lib/typhoeus/request/stubbable.rb +28 -0
  28. data/lib/typhoeus/request.rb +144 -241
  29. data/lib/typhoeus/response/header.rb +54 -0
  30. data/lib/typhoeus/response/informations.rb +205 -0
  31. data/lib/typhoeus/response/status.rb +80 -0
  32. data/lib/typhoeus/response.rb +45 -118
  33. data/lib/typhoeus/version.rb +3 -1
  34. data/lib/typhoeus.rb +87 -39
  35. metadata +37 -143
  36. data/lib/typhoeus/curl.rb +0 -453
  37. data/lib/typhoeus/easy/auth.rb +0 -14
  38. data/lib/typhoeus/easy/callbacks.rb +0 -33
  39. data/lib/typhoeus/easy/ffi_helper.rb +0 -61
  40. data/lib/typhoeus/easy/infos.rb +0 -90
  41. data/lib/typhoeus/easy/options.rb +0 -115
  42. data/lib/typhoeus/easy/proxy.rb +0 -20
  43. data/lib/typhoeus/easy/ssl.rb +0 -82
  44. data/lib/typhoeus/easy.rb +0 -115
  45. data/lib/typhoeus/filter.rb +0 -28
  46. data/lib/typhoeus/form.rb +0 -61
  47. data/lib/typhoeus/header.rb +0 -54
  48. data/lib/typhoeus/hydra/callbacks.rb +0 -24
  49. data/lib/typhoeus/hydra/connect_options.rb +0 -61
  50. data/lib/typhoeus/hydra/stubbing.rb +0 -68
  51. data/lib/typhoeus/hydra_mock.rb +0 -131
  52. data/lib/typhoeus/multi.rb +0 -146
  53. data/lib/typhoeus/param_processor.rb +0 -43
  54. data/lib/typhoeus/remote.rb +0 -306
  55. data/lib/typhoeus/remote_method.rb +0 -108
  56. data/lib/typhoeus/remote_proxy_object.rb +0 -50
  57. data/lib/typhoeus/utils.rb +0 -50
@@ -0,0 +1,71 @@
1
+ module Typhoeus
2
+ class Request
3
+
4
+ # This module contains everything what is necessary
5
+ # to make a single request.
6
+ module Operations
7
+
8
+ # :nodoc:
9
+ def self.included(base)
10
+ base.extend ClassMethods
11
+ end
12
+
13
+ module ClassMethods # :nodoc:
14
+
15
+ # Shortcut to perform a single request.
16
+ #
17
+ # @example Perform request.
18
+ # Request.run("www.example.com")
19
+ #
20
+ # @param [ String ] url The url to request.
21
+ # @param [ Hash ] options The options hash.
22
+ #
23
+ # @return [ Response ] The response.
24
+ #
25
+ # @deprecated
26
+ def run(url, options = {})
27
+ new(url, options).run
28
+ end
29
+ end
30
+
31
+ # Run a request.
32
+ #
33
+ # @example Run a request.
34
+ # Typhoeus::Request.new("www.example.com").run
35
+ #
36
+ # @return [ Response ] The response.
37
+ def run
38
+ easy = Typhoeus.get_easy
39
+ easy.http_request(
40
+ url,
41
+ options.fetch(:method, :get),
42
+ options.reject{|k,_| k==:method}
43
+ )
44
+ easy.prepare
45
+ easy.perform
46
+ finish(Response.new(easy.to_hash))
47
+ Typhoeus.release_easy(easy)
48
+ response
49
+ end
50
+
51
+ # Sets a response, the request on the response
52
+ # and executes the callbacks.
53
+ #
54
+ # @param [Typhoeus::Response] response The response.
55
+ # @param [Boolean] bypass_memoization Wether to bypass
56
+ # memoization or not. Decides how the response is set.
57
+ #
58
+ # @return [Typhoeus::Response] The response.
59
+ def finish(response, bypass_memoization = nil)
60
+ if bypass_memoization
61
+ @response = response
62
+ else
63
+ self.response = response
64
+ end
65
+ self.response.request = self
66
+ execute_callbacks
67
+ response
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,29 @@
1
+ module Typhoeus
2
+ class Request
3
+
4
+ # This module contains logic for having a reponse
5
+ # getter and setter.
6
+ module Responseable
7
+
8
+ # Set the response.
9
+ #
10
+ # @example Set response.
11
+ # request.response = response
12
+ #
13
+ # @param [ Response ] value The response to set.
14
+ def response=(value)
15
+ @response = value
16
+ end
17
+
18
+ # Return the response.
19
+ #
20
+ # @example Return response.
21
+ # request.response
22
+ #
23
+ # @return [ Response ] The response.
24
+ def response
25
+ @response ||= nil
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,28 @@
1
+ module Typhoeus
2
+ class Request
3
+
4
+ # This module handles stubbing on the request side.
5
+ # It plays well with the block_connection configuration,
6
+ # which raises when you make a request which is not stubbed.
7
+ #
8
+ # @api private
9
+ module Stubbable
10
+
11
+ # Override run in order to check for matching expecations.
12
+ # When an expecation is found, super is not called. Instead a
13
+ # canned response is assigned to the request.
14
+ #
15
+ # @example Run the request.
16
+ # request.run
17
+ #
18
+ # @return [ Response ] The response.
19
+ def run
20
+ if expectation = Expectation.find_by(self)
21
+ finish(expectation.response)
22
+ else
23
+ super
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -1,266 +1,169 @@
1
- require 'uri'
1
+ require 'typhoeus/request/actions'
2
+ require 'typhoeus/request/before'
3
+ require 'typhoeus/request/block_connection'
4
+ require 'typhoeus/request/callbacks'
5
+ require 'typhoeus/request/marshal'
6
+ require 'typhoeus/request/memoizable'
7
+ require 'typhoeus/request/operations'
8
+ require 'typhoeus/request/responseable'
9
+ require 'typhoeus/request/stubbable'
2
10
 
3
11
  module Typhoeus
12
+
13
+ # This class represents a request.
14
+ #
15
+ # @example (see #initialize)
16
+ #
17
+ # @example Make a request with the shortcut.
18
+ # response = Typhoeus.get("www.example.com")
19
+ #
20
+ # @see (see #initialize)
4
21
  class Request
5
- ACCESSOR_OPTIONS = [
6
- :method,
7
- :params,
8
- :body,
9
- :headers,
10
- :cache_key_basis,
11
- :connect_timeout,
12
- :timeout,
13
- :user_agent,
14
- :response,
15
- :cache_timeout,
16
- :follow_location,
17
- :max_redirects,
18
- :proxy,
19
- :proxy_username,
20
- :proxy_password,
21
- :disable_ssl_peer_verification,
22
- :disable_ssl_host_verification,
23
- :interface,
24
- :ssl_cert,
25
- :ssl_cert_type,
26
- :ssl_key,
27
- :ssl_key_type,
28
- :ssl_key_password,
29
- :ssl_cacert,
30
- :ssl_capath,
31
- :ssl_version,
32
- :verbose,
33
- :username,
34
- :password,
35
- :auth_method,
36
- :proxy_auth_method,
37
- :proxy_type
38
- ]
22
+ extend Request::Actions
23
+ include Request::Callbacks::Types
24
+ include Request::Callbacks
25
+ include Request::Marshal
26
+ include Request::Operations
27
+ include Request::Responseable
28
+ include Request::Memoizable
29
+ include Request::BlockConnection
30
+ include Request::Stubbable
31
+ include Request::Before
32
+
33
+ # Returns the provided url.
34
+ #
35
+ # @return [ String ]
36
+ attr_accessor :url
39
37
 
40
- attr_accessor *ACCESSOR_OPTIONS
38
+ # Returns options, which includes default parameters.
39
+ #
40
+ # @return [ Hash ]
41
+ attr_accessor :options
41
42
 
42
- # Initialize a new Request
43
+ # Returns the hydra the request ran into if any.
43
44
  #
44
- # Options:
45
- # * +url+ : Endpoint (URL) of the request
46
- # * +options+ : A hash containing options among :
47
- # ** +:method+ : :get (default) / :post / :put
48
- # ** +:params+ : params as a Hash
49
- # ** +:body+
50
- # ** +:timeout+ : timeout (ms)
51
- # ** +:interface+ : interface or ip address (string)
52
- # ** +:connect_timeout+ : connect timeout (ms)
53
- # ** +:headers+ : headers as Hash
54
- # ** +:cache_timeout+ : cache timeout (ms)
55
- # ** +:follow_location
56
- # ** +:max_redirects
57
- # ** +:proxy
58
- # ** +:disable_ssl_peer_verification
59
- # ** +:disable_ssl_host_verification
60
- # ** +:ssl_cert
61
- # ** +:ssl_cert_type
62
- # ** +:ssl_key
63
- # ** +:ssl_key_type
64
- # ** +:ssl_key_password
65
- # ** +:ssl_cacert
66
- # ** +:ssl_capath
67
- # ** +:verbose
68
- # ** +:username
69
- # ** +:password
70
- # ** +:auth_method
45
+ # @return [ Typhoeus::Hydra ]
71
46
  #
72
- def initialize(url, options = {})
73
- @url = url
74
- @method = options[:method] || :get
75
- @params = options[:params]
76
- @body = options[:body]
77
- @timeout = safe_to_i(options[:timeout])
78
- @connect_timeout = safe_to_i(options[:connect_timeout])
79
- @interface = options[:interface]
80
- @headers = options[:headers] || {}
81
-
82
- @cache_timeout = safe_to_i(options[:cache_timeout])
83
- @follow_location = options[:follow_location]
84
- @max_redirects = options[:max_redirects]
85
- @proxy = options[:proxy]
86
- @proxy_type = options[:proxy_type]
87
- @proxy_username = options[:proxy_username]
88
- @proxy_password = options[:proxy_password]
89
- @proxy_auth_method = options[:proxy_auth_method]
90
- @disable_ssl_peer_verification = options[:disable_ssl_peer_verification]
91
- @disable_ssl_host_verification = options[:disable_ssl_host_verification]
92
- @ssl_cert = options[:ssl_cert]
93
- @ssl_cert_type = options[:ssl_cert_type]
94
- @ssl_key = options[:ssl_key]
95
- @ssl_key_type = options[:ssl_key_type]
96
- @ssl_key_password = options[:ssl_key_password]
97
- @ssl_cacert = options[:ssl_cacert]
98
- @ssl_capath = options[:ssl_capath]
99
- @ssl_version = options[:ssl_version]
100
- @verbose = options[:verbose]
101
- @username = options[:username]
102
- @password = options[:password]
103
- @auth_method = options[:auth_method]
104
-
105
- @on_complete = nil
106
- @after_complete = nil
107
- @handled_response = nil
108
- end
109
-
110
- LOCALHOST_ALIASES = %w[ localhost 127.0.0.1 0.0.0.0 ]
111
-
112
- def localhost?
113
- LOCALHOST_ALIASES.include?(parsed_uri.host)
114
- end
47
+ # @api private
48
+ attr_accessor :hydra
115
49
 
116
- def user_agent
117
- headers['User-Agent']
118
- end
119
-
120
- def url
121
- if [:post, :put].include?(@method)
122
- @url
123
- else
124
- url = "#{@url}?#{params_string}"
125
- url += "&#{URI.escape(@body)}" if @body
126
- url.gsub("?&", "?").gsub(/\?$/, '')
127
- end
128
- end
129
-
130
- def parsed_uri
131
- @parsed_uri ||= URI.parse(@url)
132
- end
133
-
134
- def host
135
- slash_location = @url.index('/', 8)
136
- if slash_location
137
- @url.slice(0, slash_location)
138
- else
139
- query_string_location = @url.index('?')
140
- return query_string_location ? @url.slice(0, query_string_location) : @url
141
- end
142
- end
143
-
144
- def host_domain
145
- parsed_uri.host
146
- end
147
-
148
- def params_string
149
- return nil unless params
150
- traversal = Typhoeus::Utils.traverse_params_hash(params)
151
- Typhoeus::Utils.traversal_to_param_string(traversal)
152
- end
153
-
154
- def on_complete(&block)
155
- @on_complete = block
156
- end
157
-
158
- def on_complete=(proc)
159
- @on_complete = proc
160
- end
161
-
162
- def after_complete(&block)
163
- @after_complete = block
164
- end
50
+ # Returns the original options provided.
51
+ #
52
+ # @return [ Hash ]
53
+ #
54
+ # @api private
55
+ attr_accessor :original_options
165
56
 
166
- def after_complete=(proc)
167
- @after_complete = proc
168
- end
57
+ # @return [ Boolean ]
58
+ #
59
+ # @api private
60
+ attr_accessor :block_connection
169
61
 
170
- def call_handlers
171
- if @on_complete
172
- @handled_response = @on_complete.call(response)
173
- call_after_complete
174
- end
175
- end
62
+ # Create a new request.
63
+ #
64
+ # @example Simplest request.
65
+ # response = Typhoeus::Request.new("www.example.com").run
66
+ #
67
+ # @example Request with url parameters.
68
+ # response = Typhoeus::Request.new(
69
+ # "www.example.com",
70
+ # :params => {:a => 1}
71
+ # ).run
72
+ #
73
+ # @example Request with a body.
74
+ # response = Typhoeus::Request.new(
75
+ # "www.example.com",
76
+ # :body => {:b => 2}
77
+ # ).run
78
+ #
79
+ # @example Request with parameters and body.
80
+ # response = Typhoeus::Request.new(
81
+ # "www.example.com",
82
+ # :params => {:a => 1},
83
+ # :body => {:b => 2}
84
+ # ).run
85
+ #
86
+ # @example Create a request and allow follow redirections.
87
+ # response = Typhoeus::Request.new(
88
+ # "www.example.com",
89
+ # :followlocation => true
90
+ # ).run
91
+ #
92
+ # @param [ String ] url The url to request.
93
+ # @param [ options ] options The options.
94
+ #
95
+ # @option options [ Hash ] :params Translated
96
+ # into url parameters.
97
+ # @option options [ Hash ] :body Translated
98
+ # into HTTP POST request body.
99
+ #
100
+ # @return [ Typhoeus::Request ] The request.
101
+ #
102
+ # @note See {Ethon::Easy#initialize} for more options.
103
+ #
104
+ # @see Typhoeus::Hydra
105
+ # @see Typhoeus::Response
106
+ # @see Typhoeus::Request::Actions
107
+ def initialize(url, options = {})
108
+ @url = url
109
+ @original_options = options
110
+ @options = options.dup
176
111
 
177
- def call_after_complete
178
- @after_complete.call(@handled_response) if @after_complete
112
+ set_defaults
179
113
  end
180
114
 
181
- def handled_response=(val)
182
- @handled_response = val
115
+ # Returns wether other is equal to self.
116
+ #
117
+ # @example Are request equal?
118
+ # request.eql?(other_request)
119
+ #
120
+ # @param [ Object ] other The object to check.
121
+ #
122
+ # @return [ Boolean ] Returns true if equals, else false.
123
+ #
124
+ # @api private
125
+ def eql?(other)
126
+ self.class == other.class &&
127
+ self.url == other.url &&
128
+ fuzzy_hash_eql?(self.options, other.options)
183
129
  end
184
130
 
185
- def handled_response
186
- @handled_response || response
131
+ # Overrides Object#hash.
132
+ #
133
+ # @return [ Integer ] The integer representing the request.
134
+ #
135
+ # @api private
136
+ def hash
137
+ [ self.class, self.url, self.options ].hash
187
138
  end
188
139
 
189
- def inspect
190
- result = ":method => #{self.method.inspect},\n" <<
191
- "\t:url => #{URI.parse(self.url).to_s}"
192
- if self.body and !self.body.empty?
193
- result << ",\n\t:body => #{self.body.inspect}"
194
- end
195
-
196
- if self.params and !self.params.empty?
197
- result << ",\n\t:params => #{self.params.inspect}"
198
- end
140
+ private
199
141
 
200
- if self.headers and !self.headers.empty?
201
- result << ",\n\t:headers => #{self.headers.inspect}"
142
+ # Checks if two hashes are equal or not, discarding
143
+ # first-level hash order.
144
+ #
145
+ # @param [ Hash ] left
146
+ # @param [ Hash ] right hash to check for equality
147
+ #
148
+ # @return [ Boolean ] Returns true if hashes have
149
+ # same values for same keys and same length,
150
+ # even if the keys are given in a different order.
151
+ def fuzzy_hash_eql?(left, right)
152
+ return true if (left == right)
153
+
154
+ (left.count == right.count) && left.inject(true) do |res, kvp|
155
+ res && (kvp[1] == right[kvp[0]])
202
156
  end
203
-
204
- result
205
- end
206
-
207
- def cache_key
208
- Digest::SHA1.hexdigest(cache_key_basis || url)
209
157
  end
210
158
 
211
- def self.run(url, params)
212
- r = new(url, params)
213
- Typhoeus::Hydra.hydra.queue r
214
- Typhoeus::Hydra.hydra.run
215
- r.response
216
- end
217
-
218
- def self.get(url, params = {})
219
- run(url, params.merge(:method => :get))
220
- end
221
-
222
- def self.post(url, params = {})
223
- run(url, params.merge(:method => :post))
224
- end
225
-
226
- def self.put(url, params = {})
227
- run(url, params.merge(:method => :put))
228
- end
229
-
230
- def self.delete(url, params = {})
231
- run(url, params.merge(:method => :delete))
232
- end
233
-
234
- def self.head(url, params = {})
235
- run(url, params.merge(:method => :head))
236
- end
237
-
238
- protected
239
-
240
- # Return the important data needed to serialize this Request, except the
241
- # `on_complete` and `after_complete` handlers, since they cannot be
242
- # marshalled.
243
- def marshal_dump
244
- (instance_variables - ['@on_complete', '@after_complete', :@on_complete, :@after_complete]).map do |name|
245
- [name, instance_variable_get(name)]
159
+ # Sets default header and verbose when turned on.
160
+ def set_defaults
161
+ if @options[:headers]
162
+ @options[:headers] = {'User-Agent' => Typhoeus::USER_AGENT}.merge(options[:headers])
163
+ else
164
+ @options[:headers] = {'User-Agent' => Typhoeus::USER_AGENT}
246
165
  end
247
- end
248
-
249
- def marshal_load(attributes)
250
- attributes.each { |name, value| instance_variable_set(name, value) }
251
- end
252
-
253
- def self.options
254
- ACCESSOR_OPTIONS
255
- end
256
-
257
- private
258
-
259
- def safe_to_i(value)
260
- return value if value.is_a?(Fixnum)
261
- return nil if value.nil?
262
- return nil if value.respond_to?(:empty?) && value.empty?
263
- value.to_i
166
+ @options[:verbose] = Typhoeus::Config.verbose if @options[:verbose].nil? && !Typhoeus::Config.verbose.nil?
264
167
  end
265
168
  end
266
169
  end
@@ -0,0 +1,54 @@
1
+ module Typhoeus
2
+ class Response
3
+
4
+ # This class represents the response header.
5
+ # It can be accessed like a hash.
6
+ #
7
+ # @api private
8
+ class Header < Hash
9
+ # Create a new header.
10
+ #
11
+ # @example Create new header.
12
+ # Header.new(raw)
13
+ #
14
+ # @param [ String ] raw The raw header.
15
+ def initialize(raw)
16
+ @raw = raw
17
+ parse
18
+ end
19
+
20
+ # Parses the raw header.
21
+ #
22
+ # @example Parse header.
23
+ # header.parse
24
+ def parse
25
+ raw.lines.each do |header|
26
+ unless header =~ /^HTTP\/1.[01]/
27
+ parts = header.split(':', 2)
28
+ unless parts.empty?
29
+ parts.map(&:strip!)
30
+ if self.has_key?(parts[0])
31
+ self[parts[0]] = [self[parts[0]]] unless self[parts[0]].kind_of? Array
32
+ self[parts[0]] << parts[1]
33
+ else
34
+ self[parts[0]] = parts[1]
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ # Returns the raw header or empty string.
44
+ #
45
+ # @example Return raw header.
46
+ # header.raw
47
+ #
48
+ # @return [ String ] The raw header.
49
+ def raw
50
+ @raw ||= ''
51
+ end
52
+ end
53
+ end
54
+ end