hhry-typhoeus 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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