hhry-typhoeus 0.4.0

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.
@@ -0,0 +1,278 @@
1
+ require 'uri'
2
+
3
+ module Typhoeus
4
+ 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
+ :user_agent,
37
+ :proxy_auth_method,
38
+ :proxy_type
39
+ ]
40
+
41
+ attr_accessor *ACCESSOR_OPTIONS
42
+
43
+ # Initialize a new Request
44
+ #
45
+ # Options:
46
+ # * +url+ : Endpoint (URL) of the request
47
+ # * +options+ : A hash containing options among :
48
+ # ** +:method+ : :get (default) / :post / :put
49
+ # ** +:params+ : params as a Hash
50
+ # ** +:body+
51
+ # ** +:timeout+ : timeout (ms)
52
+ # ** +:interface+ : interface or ip address (string)
53
+ # ** +:connect_timeout+ : connect timeout (ms)
54
+ # ** +:headers+ : headers as Hash
55
+ # ** +:cache_timeout+ : cache timeout (ms)
56
+ # ** +:follow_location
57
+ # ** +:max_redirects
58
+ # ** +:proxy
59
+ # ** +:disable_ssl_peer_verification
60
+ # ** +:disable_ssl_host_verification
61
+ # ** +:ssl_cert
62
+ # ** +:ssl_cert_type
63
+ # ** +:ssl_key
64
+ # ** +:ssl_key_type
65
+ # ** +:ssl_key_password
66
+ # ** +:ssl_cacert
67
+ # ** +:ssl_capath
68
+ # ** +:verbose
69
+ # ** +:username
70
+ # ** +:password
71
+ # ** +:auth_method
72
+ #
73
+ def initialize(url, options = {})
74
+ @url = url
75
+ @method = options[:method] || :get
76
+ @params = options[:params]
77
+ @body = options[:body]
78
+ @timeout = safe_to_i(options[:timeout])
79
+ @connect_timeout = safe_to_i(options[:connect_timeout])
80
+ @interface = options[:interface]
81
+ @headers = options[:headers] || {}
82
+
83
+ if options.key?(:user_agent)
84
+ @headers['User-Agent'] = options[:user_agent]
85
+ end
86
+
87
+ @cache_timeout = safe_to_i(options[:cache_timeout])
88
+ @follow_location = options[:follow_location]
89
+ @max_redirects = options[:max_redirects]
90
+ @proxy = options[:proxy]
91
+ @proxy_type = options[:proxy_type]
92
+ @proxy_username = options[:proxy_username]
93
+ @proxy_password = options[:proxy_password]
94
+ @proxy_auth_method = options[:proxy_auth_method]
95
+ @disable_ssl_peer_verification = options[:disable_ssl_peer_verification]
96
+ @disable_ssl_host_verification = options[:disable_ssl_host_verification]
97
+ @ssl_cert = options[:ssl_cert]
98
+ @ssl_cert_type = options[:ssl_cert_type]
99
+ @ssl_key = options[:ssl_key]
100
+ @ssl_key_type = options[:ssl_key_type]
101
+ @ssl_key_password = options[:ssl_key_password]
102
+ @ssl_cacert = options[:ssl_cacert]
103
+ @ssl_capath = options[:ssl_capath]
104
+ @ssl_version = options[:ssl_version]
105
+ @verbose = options[:verbose]
106
+ @username = options[:username]
107
+ @password = options[:password]
108
+ @auth_method = options[:auth_method]
109
+
110
+ if @method == :post
111
+ @params = @params ? Typhoeus::Utils.escape_params(@params) : nil
112
+ @url = url
113
+ end
114
+
115
+ @parsed_uri = URI.parse(@url)
116
+
117
+ @on_complete = nil
118
+ @after_complete = nil
119
+ @handled_response = nil
120
+ end
121
+
122
+ LOCALHOST_ALIASES = %w[ localhost 127.0.0.1 0.0.0.0 ]
123
+
124
+ def localhost?
125
+ LOCALHOST_ALIASES.include?(parsed_uri.host)
126
+ end
127
+
128
+ def user_agent
129
+ headers['User-Agent']
130
+ end
131
+
132
+ def url
133
+ if @method == :post
134
+ @url
135
+ else
136
+ url = "#{@url}?#{params_string}"
137
+ url += "&#{URI.escape(@body)}" if @body
138
+ url.gsub("?&", "?").gsub(/\?$/, '')
139
+ end
140
+ end
141
+
142
+ def parsed_uri
143
+ @parsed_uri ||= URI.parse(@url)
144
+ end
145
+
146
+ def host
147
+ slash_location = @url.index('/', 8)
148
+ if slash_location
149
+ @url.slice(0, slash_location)
150
+ else
151
+ query_string_location = @url.index('?')
152
+ return query_string_location ? @url.slice(0, query_string_location) : @url
153
+ end
154
+ end
155
+
156
+ def host_domain
157
+ parsed_uri.host
158
+ end
159
+
160
+ def params_string
161
+ return nil unless params
162
+ traversal = Typhoeus::Utils.traverse_params_hash(params)
163
+ Typhoeus::Utils.traversal_to_param_string(traversal)
164
+ end
165
+
166
+ def on_complete(&block)
167
+ @on_complete = block
168
+ end
169
+
170
+ def on_complete=(proc)
171
+ @on_complete = proc
172
+ end
173
+
174
+ def after_complete(&block)
175
+ @after_complete = block
176
+ end
177
+
178
+ def after_complete=(proc)
179
+ @after_complete = proc
180
+ end
181
+
182
+ def call_handlers
183
+ if @on_complete
184
+ @handled_response = @on_complete.call(response)
185
+ call_after_complete
186
+ end
187
+ end
188
+
189
+ def call_after_complete
190
+ @after_complete.call(@handled_response) if @after_complete
191
+ end
192
+
193
+ def handled_response=(val)
194
+ @handled_response = val
195
+ end
196
+
197
+ def handled_response
198
+ @handled_response || response
199
+ end
200
+
201
+ def inspect
202
+ result = ":method => #{self.method.inspect},\n" <<
203
+ "\t:url => #{URI.parse(self.url).to_s}"
204
+ if self.body and !self.body.empty?
205
+ result << ",\n\t:body => #{self.body.inspect}"
206
+ end
207
+
208
+ if self.params and !self.params.empty?
209
+ result << ",\n\t:params => #{self.params.inspect}"
210
+ end
211
+
212
+ if self.headers and !self.headers.empty?
213
+ result << ",\n\t:headers => #{self.headers.inspect}"
214
+ end
215
+
216
+ result
217
+ end
218
+
219
+ def cache_key
220
+ Digest::SHA1.hexdigest(cache_key_basis || url)
221
+ end
222
+
223
+ def self.run(url, params)
224
+ r = new(url, params)
225
+ Typhoeus::Hydra.hydra.queue r
226
+ Typhoeus::Hydra.hydra.run
227
+ r.response
228
+ end
229
+
230
+ def self.get(url, params = {})
231
+ run(url, params.merge(:method => :get))
232
+ end
233
+
234
+ def self.post(url, params = {})
235
+ run(url, params.merge(:method => :post))
236
+ end
237
+
238
+ def self.put(url, params = {})
239
+ run(url, params.merge(:method => :put))
240
+ end
241
+
242
+ def self.delete(url, params = {})
243
+ run(url, params.merge(:method => :delete))
244
+ end
245
+
246
+ def self.head(url, params = {})
247
+ run(url, params.merge(:method => :head))
248
+ end
249
+
250
+ protected
251
+
252
+ # Return the important data needed to serialize this Request, except the
253
+ # `on_complete` and `after_complete` handlers, since they cannot be
254
+ # marshalled.
255
+ def marshal_dump
256
+ (instance_variables - ['@on_complete', '@after_complete', :@on_complete, :@after_complete]).map do |name|
257
+ [name, instance_variable_get(name)]
258
+ end
259
+ end
260
+
261
+ def marshal_load(attributes)
262
+ attributes.each { |name, value| instance_variable_set(name, value) }
263
+ end
264
+
265
+ def self.options
266
+ ACCESSOR_OPTIONS
267
+ end
268
+
269
+ private
270
+
271
+ def safe_to_i(value)
272
+ return value if value.is_a?(Fixnum)
273
+ return nil if value.nil?
274
+ return nil if value.respond_to?(:empty?) && value.empty?
275
+ value.to_i
276
+ end
277
+ end
278
+ end
@@ -0,0 +1,122 @@
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
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
+ @mock = params[:mock] || false # default
36
+ @headers_hash = Header.new(params[:headers_hash]) if params[:headers_hash]
37
+ end
38
+
39
+ # Returns true if this is a mock response.
40
+ def mock?
41
+ @mock
42
+ end
43
+
44
+ def headers
45
+ @headers ||= @headers_hash ? construct_header_string : ''
46
+ end
47
+
48
+ def headers_hash
49
+ @headers_hash ||= begin
50
+ headers.split("\n").map {|o| o.strip}.inject(Typhoeus::Header.new) do |hash, o|
51
+ if o.empty? || o =~ /^HTTP\/[\d\.]+/
52
+ hash
53
+ else
54
+ i = o.index(":") || o.size
55
+ key = o.slice(0, i)
56
+ value = o.slice(i + 1, o.size)
57
+ value = value.strip unless value.nil?
58
+ if hash.key? key
59
+ hash[key] = [hash[key], value].flatten
60
+ else
61
+ hash[key] = value
62
+ end
63
+
64
+ hash
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ def status_message
71
+ return @status_message if @status_message != nil
72
+
73
+ # HTTP servers can choose not to include the explanation to HTTP codes. The RFC
74
+ # states this (http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4):
75
+ # Except when responding to a HEAD request, the server SHOULD include an entity containing
76
+ # an explanation of the error situation [...]
77
+ # This means 'HTTP/1.1 404' is as valid as 'HTTP/1.1 404 Not Found' and we have to handle it.
78
+
79
+ # Regexp doc: http://rubular.com/r/eAr1oVYsVa
80
+ if first_header_line != nil and first_header_line[/\d{3} (.*)$/, 1] != nil
81
+ @status_message = first_header_line[/\d{3} (.*)$/, 1].chomp
82
+ else
83
+ @status_message = nil
84
+ end
85
+ end
86
+
87
+ def http_version
88
+ @http_version ||= first_header_line ? first_header_line[/HTTP\/(\S+)/, 1] : nil
89
+ end
90
+
91
+ def success?
92
+ @code >= 200 && @code < 300
93
+ end
94
+
95
+ def modified?
96
+ @code != 304
97
+ end
98
+
99
+ def timed_out?
100
+ curl_return_code == 28
101
+ end
102
+
103
+ private
104
+
105
+ def first_header_line
106
+ @first_header_line ||= @headers.to_s.split("\n").first
107
+ end
108
+
109
+ def construct_header_string
110
+ lines = ["HTTP/#{http_version} #{code} #{status_message}"]
111
+
112
+ @headers_hash.each do |key, values|
113
+ [values].flatten.each do |value|
114
+ lines << "#{key}: #{value}"
115
+ end
116
+ end
117
+
118
+ lines << '' << ''
119
+ lines.join("\r\n")
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,58 @@
1
+ module Typhoeus
2
+ module Utils
3
+ # Taken from Rack::Utils, 1.2.1 to remove Rack dependency.
4
+ def escape(s)
5
+ s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/u) {
6
+ '%'+$1.unpack('H2'*bytesize($1)).join('%').upcase
7
+ }.tr(' ', '+')
8
+ end
9
+ module_function :escape
10
+
11
+ def escape_params(params)
12
+ traverse_params_hash(params)[:params].inject({}) do |memo, (k, v)|
13
+ memo[escape(k)] = escape(v)
14
+ memo
15
+ end
16
+ end
17
+ module_function :escape_params
18
+
19
+ # Params are NOT escaped.
20
+ def traverse_params_hash(hash, result = nil, current_key = nil)
21
+ result = ParamProcessor.traverse_params_hash hash, result, current_key
22
+ end
23
+ module_function :traverse_params_hash
24
+
25
+ def traversal_to_param_string(traversal, escape = true)
26
+ traversal[:params].collect { |param|
27
+ escape ? "#{Typhoeus::Utils.escape(param[0])}=#{Typhoeus::Utils.escape(param[1])}" : "#{param[0]}=#{param[1]}"
28
+ }.join('&')
29
+ end
30
+ module_function :traversal_to_param_string
31
+
32
+ # Return the bytesize of String; uses String#size under Ruby 1.8 and
33
+ # String#bytesize under 1.9.
34
+ if ''.respond_to?(:bytesize)
35
+ def bytesize(string)
36
+ string.bytesize
37
+ end
38
+ else
39
+ def bytesize(string)
40
+ string.size
41
+ end
42
+ end
43
+ module_function :bytesize
44
+
45
+ # Return a byteslice from a string; uses String#[] under Ruby 1.8 and
46
+ # String#byteslice under 1.9.
47
+ if ''.respond_to?(:byteslice)
48
+ def byteslice(string, *args)
49
+ string.byteslice(*args)
50
+ end
51
+ else
52
+ def byteslice(string, *args)
53
+ string[*args]
54
+ end
55
+ end
56
+ module_function :byteslice
57
+ end
58
+ end