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,266 +1,81 @@
1
- require 'uri'
1
+ require 'typhoeus/requests/callbacks'
2
+ require 'typhoeus/requests/actions'
3
+ require 'typhoeus/requests/operations'
4
+ require 'typhoeus/requests/marshal'
5
+ require 'typhoeus/requests/responseable'
6
+ require 'typhoeus/requests/memoizable'
2
7
 
3
8
  module Typhoeus
9
+
10
+ # This class represents a request.
4
11
  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
- ]
12
+ include Requests::Callbacks
13
+ include Requests::Marshal
14
+ include Requests::Operations
15
+ extend Requests::Actions
16
+ include Requests::Responseable
17
+ include Requests::Memoizable
39
18
 
40
- attr_accessor *ACCESSOR_OPTIONS
19
+ attr_accessor :options, :url, :hydra
41
20
 
42
- # Initialize a new Request
21
+ # Create a new request.
22
+ #
23
+ # @example Create a request.
24
+ # Request.new("www.example.com")
43
25
  #
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
26
+ # @param [ String ] url The url to request.
27
+ # @param [ Hash ] options The options.
71
28
  #
29
+ # #return [ Request ] The new request.
72
30
  def initialize(url, options = {})
73
31
  @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
32
+ @options = options.dup
109
33
 
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
115
-
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)
34
+ if @options[:headers]
35
+ @options[:headers] = {'User-Agent' => Typhoeus::USER_AGENT}.merge(options[:headers])
138
36
  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
165
-
166
- def after_complete=(proc)
167
- @after_complete = proc
168
- end
169
-
170
- def call_handlers
171
- if @on_complete
172
- @handled_response = @on_complete.call(response)
173
- call_after_complete
174
- end
175
- end
176
-
177
- def call_after_complete
178
- @after_complete.call(@handled_response) if @after_complete
179
- end
180
-
181
- def handled_response=(val)
182
- @handled_response = val
183
- end
184
-
185
- def handled_response
186
- @handled_response || response
187
- end
188
-
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}"
37
+ @options[:headers] = {'User-Agent' => Typhoeus::USER_AGENT}
198
38
  end
199
-
200
- if self.headers and !self.headers.empty?
201
- result << ",\n\t:headers => #{self.headers.inspect}"
202
- end
203
-
204
- result
205
- end
206
-
207
- def cache_key
208
- Digest::SHA1.hexdigest(cache_key_basis || url)
209
- end
210
-
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
39
+ @options[:verbose] = Typhoeus::Config.verbose unless @options[:verbose]
216
40
  end
217
41
 
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))
42
+ # Returns wether other is equal to self.
43
+ #
44
+ # @example Are request equal?
45
+ # request.eql?(other_request)
46
+ #
47
+ # @param [ Object ] other The object to check.
48
+ #
49
+ # @return [ Boolean ] Returns true if equals, else false.
50
+ def eql?(other)
51
+ self.class == other.class &&
52
+ self.url == other.url &&
53
+ fuzzy_hash_eql?(self.options, other.options)
228
54
  end
229
55
 
230
- def self.delete(url, params = {})
231
- run(url, params.merge(:method => :delete))
56
+ # Overrides Object#hash.
57
+ #
58
+ # @return [ Integer ] The integer representing the request.
59
+ def hash
60
+ [ self.class, self.url, self.options ].hash
232
61
  end
233
62
 
234
- def self.head(url, params = {})
235
- run(url, params.merge(:method => :head))
236
- end
63
+ protected
237
64
 
238
- protected
65
+ # Checks if two hashes are equal or not, discarding first-level hash order
66
+ #
67
+ # @param [ Hash ] hash
68
+ # @param [ Hash ] other hash to check for equality
69
+ #
70
+ # @return [ Boolean ] Returns true if hashes have same values for same keys and same length,
71
+ # even if the keys are given in a different order.
72
+ def fuzzy_hash_eql?(left, right)
73
+ return true if (left == right)
239
74
 
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)]
75
+ (left.count == right.count) && left.inject(true) do |res, kvp|
76
+ res && (kvp[1] == right[kvp[0]])
246
77
  end
247
78
  end
248
79
 
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
264
- end
265
80
  end
266
81
  end
@@ -0,0 +1,17 @@
1
+ module Typhoeus
2
+ module Requests # :nodoc:
3
+
4
+ # Module containing logic about shortcuts to
5
+ # http methods. Like
6
+ # Typhoeus.get("www.example.com")
7
+ module Actions
8
+ [:get, :post, :put, :delete, :head, :patch, :options].each do |name|
9
+ define_method(name) do |*args|
10
+ url = args[0]
11
+ options = args.fetch(1, {})
12
+ Request.run(url, options.merge(:method => name))
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,48 @@
1
+ module Typhoeus
2
+ module Requests
3
+
4
+ # This module contains the logic for the response callbacks.
5
+ # The on_complete callback is the only one at the moment.
6
+ #
7
+ # You can set multiple callbacks, which are then executed
8
+ # in the same order.
9
+ #
10
+ # request.on_complete { p 1 }
11
+ # request.on_complete { p 2 }
12
+ # request.complete
13
+ # #=> 1
14
+ # #=> 2
15
+ #
16
+ # You can clear the callbacks:
17
+ #
18
+ # request.on_complete { p 1 }
19
+ # request.on_complete { p 2 }
20
+ # request.on_complete.clear
21
+ # request.on_complete
22
+ # #=> []
23
+ module Callbacks
24
+
25
+ # Set on_complete callback.
26
+ #
27
+ # @example Set on_complete.
28
+ # request.on_complete { p "yay" }
29
+ #
30
+ # @param [ Block ] block The block to execute.
31
+ def on_complete(&block)
32
+ @on_complete ||= []
33
+ @on_complete << block if block_given?
34
+ @on_complete
35
+ end
36
+
37
+ # Execute on_complete callbacks.
38
+ #
39
+ # @example Execute on_completes.
40
+ # request.complete
41
+ def complete
42
+ if defined?(@on_complete)
43
+ @on_complete.map{ |callback| callback.call(self) }
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,21 @@
1
+ module Typhoeus
2
+ module Requests
3
+
4
+ # This module contains custom serializer.
5
+ module Marshal
6
+
7
+ # Return the important data needed to serialize this Request, except the
8
+ # `on_complete` handler, since they cannot be marshalled.
9
+ def marshal_dump
10
+ (instance_variables - ['@on_complete', :@on_complete]).map do |name|
11
+ [name, instance_variable_get(name)]
12
+ end
13
+ end
14
+
15
+ # Load.
16
+ def marshal_load(attributes)
17
+ attributes.each { |name, value| instance_variable_set(name, value) }
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,36 @@
1
+ module Typhoeus
2
+ module Requests
3
+
4
+ # This module handles the GET request memoization
5
+ # on the request side. Memoization needs to be turned
6
+ # on:
7
+ # Typhoeus.configre do |config|
8
+ # config.memoize = true
9
+ # end
10
+ module Memoizable
11
+
12
+ # Override response setter and memoizes response
13
+ # if the request is memoizable.
14
+ #
15
+ # @param [ Response ] response The response to set.
16
+ #
17
+ # @example Set response.
18
+ # request.response = response
19
+ def response=(response)
20
+ hydra.memory[self] = response if memoizable?
21
+ super
22
+ end
23
+
24
+ # Return whether a request is memoizable.
25
+ #
26
+ # @example Is request memoizable?
27
+ # request.memoizable?
28
+ #
29
+ # @return [ Boolean ] Return true if memoizable, false else.
30
+ def memoizable?
31
+ Typhoeus::Config.memoize &&
32
+ (options[:method].nil? || options[:method] == :get)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,51 @@
1
+ module Typhoeus
2
+ module Requests
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
+ def run(url, options = {})
25
+ new(url, options).run
26
+ end
27
+ end
28
+
29
+ # Run a request.
30
+ #
31
+ # @example Run a request.
32
+ # request.run
33
+ #
34
+ # @return [ Response ] The response.
35
+ def run
36
+ easy = Typhoeus.get_easy
37
+ easy.http_request(
38
+ url,
39
+ options.fetch(:method, :get),
40
+ options.reject{|k,_| k==:method}
41
+ )
42
+ easy.prepare
43
+ easy.perform
44
+ @response = Response.new(easy.to_hash)
45
+ Typhoeus.release_easy(easy)
46
+ complete
47
+ @response
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,29 @@
1
+ module Typhoeus
2
+ module Requests
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
@@ -1,123 +1,28 @@
1
- module Typhoeus
2
- class Response
3
- attr_accessor :request, :mock
4
- attr_reader :code, :headers, :body, :time,
5
- :requested_url, :requested_remote_method,
6
- :requested_http_method, :start_time,
7
- :effective_url, :start_transfer_time,
8
- :app_connect_time, :pretransfer_time,
9
- :connect_time, :name_lookup_time,
10
- :curl_return_code, :curl_error_message,
11
- :primary_ip, :redirect_count
12
-
13
- attr_writer :headers_hash
14
-
15
- def initialize(params = {})
16
- @code = params[:code]
17
- @curl_return_code = params[:curl_return_code]
18
- @curl_error_message = params[:curl_error_message]
19
- @status_message = params[:status_message]
20
- @http_version = params[:http_version]
21
- @headers = params[:headers]
22
- @body = params[:body]
23
- @time = params[:time]
24
- @requested_url = params[:requested_url]
25
- @requested_http_method = params[:requested_http_method]
26
- @start_time = params[:start_time]
27
- @start_transfer_time = params[:start_transfer_time]
28
- @app_connect_time = params[:app_connect_time]
29
- @pretransfer_time = params[:pretransfer_time]
30
- @connect_time = params[:connect_time]
31
- @name_lookup_time = params[:name_lookup_time]
32
- @request = params[:request]
33
- @effective_url = params[:effective_url]
34
- @primary_ip = params[:primary_ip]
35
- @redirect_count = params[:redirect_count]
36
- @mock = params[:mock] || false # default
37
- @headers_hash = Header.new(params[:headers_hash]) if params[:headers_hash]
38
- end
39
-
40
- # Returns true if this is a mock response.
41
- def mock?
42
- @mock
43
- end
44
-
45
- def headers
46
- @headers ||= @headers_hash ? construct_header_string : ''
47
- end
48
-
49
- def headers_hash
50
- @headers_hash ||= begin
51
- headers.split("\n").map {|o| o.strip}.inject(Typhoeus::Header.new) do |hash, o|
52
- if o.empty? || o =~ /^HTTP\/[\d\.]+/
53
- hash
54
- else
55
- i = o.index(":") || o.size
56
- key = o.slice(0, i)
57
- value = o.slice(i + 1, o.size)
58
- value = value.strip unless value.nil?
59
- if hash.key? key
60
- hash[key] = [hash[key], value].flatten
61
- else
62
- hash[key] = value
63
- end
64
-
65
- hash
66
- end
67
- end
68
- end
69
- end
70
-
71
- def status_message
72
- return @status_message if @status_message != nil
73
-
74
- # HTTP servers can choose not to include the explanation to HTTP codes. The RFC
75
- # states this (http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4):
76
- # Except when responding to a HEAD request, the server SHOULD include an entity containing
77
- # an explanation of the error situation [...]
78
- # This means 'HTTP/1.1 404' is as valid as 'HTTP/1.1 404 Not Found' and we have to handle it.
1
+ require 'typhoeus/responses/legacy'
2
+ require 'typhoeus/responses/informations'
3
+ require 'typhoeus/responses/status'
4
+ require 'typhoeus/responses/header'
79
5
 
80
- # Regexp doc: http://rubular.com/r/eAr1oVYsVa
81
- if first_header_line != nil and first_header_line[/\d{3} (.*)$/, 1] != nil
82
- @status_message = first_header_line[/\d{3} (.*)$/, 1].chomp
83
- else
84
- @status_message = nil
85
- end
86
- end
87
-
88
- def http_version
89
- @http_version ||= first_header_line ? first_header_line[/HTTP\/(\S+)/, 1] : nil
90
- end
91
-
92
- def success?
93
- @code >= 200 && @code < 300
94
- end
95
-
96
- def modified?
97
- @code != 304
98
- end
6
+ module Typhoeus
99
7
 
100
- def timed_out?
101
- curl_return_code == 28
8
+ # This class respresents the response.
9
+ class Response
10
+ include Responses::Status
11
+ include Responses::Informations
12
+ include Responses::Legacy
13
+
14
+ attr_accessor :request, :options
15
+
16
+ # Create a new response.
17
+ #
18
+ # @example Create a response.
19
+ # Response.new
20
+ #
21
+ # @param [ Hash ] options The options hash.
22
+ #
23
+ # @return [ Response ] The new response.
24
+ def initialize(options = {})
25
+ @options = options
102
26
  end
103
-
104
- private
105
-
106
- def first_header_line
107
- @first_header_line ||= @headers.to_s.split("\n").first
108
- end
109
-
110
- def construct_header_string
111
- lines = ["HTTP/#{http_version} #{code} #{status_message}"]
112
-
113
- @headers_hash.each do |key, values|
114
- [values].flatten.each do |value|
115
- lines << "#{key}: #{value}"
116
- end
117
- end
118
-
119
- lines << '' << ''
120
- lines.join("\r\n")
121
- end
122
27
  end
123
28
  end