dcu-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
@@ -0,0 +1,3 @@
1
+ module Typhoeus
2
+ VERSION = '0.4.0'
3
+ end
data/lib/typhoeus.rb ADDED
@@ -0,0 +1,57 @@
1
+ require 'digest/sha2'
2
+ require 'active_support/core_ext'
3
+
4
+ require 'typhoeus/utils'
5
+ require 'typhoeus/header'
6
+ require 'typhoeus/curl'
7
+ require 'typhoeus/easy'
8
+ require 'typhoeus/form'
9
+ require 'typhoeus/multi'
10
+ require 'typhoeus/filter'
11
+ require 'typhoeus/param_processor'
12
+ require 'typhoeus/remote'
13
+ require 'typhoeus/remote_proxy_object'
14
+ require 'typhoeus/response'
15
+ require 'typhoeus/request'
16
+ require 'typhoeus/hydra'
17
+ require 'typhoeus/hydra_mock'
18
+ require 'typhoeus/version'
19
+
20
+ module Typhoeus
21
+ def self.easy_object_pool
22
+ @easy_objects ||= []
23
+ end
24
+
25
+ def self.init_easy_object_pool
26
+ 20.times do
27
+ easy_object_pool << Typhoeus::Easy.new
28
+ end
29
+ end
30
+
31
+ def self.release_easy_object(easy)
32
+ easy.reset
33
+ easy_object_pool << easy
34
+ end
35
+
36
+ def self.get_easy_object
37
+ if easy_object_pool.empty?
38
+ Typhoeus::Easy.new
39
+ else
40
+ easy_object_pool.pop
41
+ end
42
+ end
43
+
44
+ def self.add_easy_request(easy_object)
45
+ Thread.current[:curl_multi] ||= Typhoeus::Multi.new
46
+ Thread.current[:curl_multi].add(easy_object)
47
+ end
48
+
49
+ def self.perform_easy_requests
50
+ multi = Thread.current[:curl_multi]
51
+ start_time = Time.now
52
+ multi.easy_handles.each do |easy|
53
+ easy.start_time = start_time
54
+ end
55
+ multi.perform
56
+ end
57
+ end
metadata ADDED
@@ -0,0 +1,179 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dcu-typhoeus
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - David Balatero
9
+ - Paul Dix
10
+ - Hans Hasselberg
11
+ autorequire:
12
+ bindir: bin
13
+ cert_chain: []
14
+ date: 2012-05-26 00:00:00.000000000 Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: ffi
18
+ requirement: &70327899192580 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ~>
22
+ - !ruby/object:Gem::Version
23
+ version: '1.0'
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: *70327899192580
27
+ - !ruby/object:Gem::Dependency
28
+ name: mime-types
29
+ requirement: &70327899192100 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ~>
33
+ - !ruby/object:Gem::Version
34
+ version: '1.18'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: *70327899192100
38
+ - !ruby/object:Gem::Dependency
39
+ name: activesupport
40
+ requirement: &70327899223600 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '3.2'
46
+ type: :runtime
47
+ prerelease: false
48
+ version_requirements: *70327899223600
49
+ - !ruby/object:Gem::Dependency
50
+ name: sinatra
51
+ requirement: &70327899223120 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ~>
55
+ - !ruby/object:Gem::Version
56
+ version: '1.3'
57
+ type: :development
58
+ prerelease: false
59
+ version_requirements: *70327899223120
60
+ - !ruby/object:Gem::Dependency
61
+ name: json
62
+ requirement: &70327899222640 !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ~>
66
+ - !ruby/object:Gem::Version
67
+ version: '1.7'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: *70327899222640
71
+ - !ruby/object:Gem::Dependency
72
+ name: rake
73
+ requirement: &70327899222160 !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ~>
77
+ - !ruby/object:Gem::Version
78
+ version: '0.9'
79
+ type: :development
80
+ prerelease: false
81
+ version_requirements: *70327899222160
82
+ - !ruby/object:Gem::Dependency
83
+ name: mocha
84
+ requirement: &70327899221680 !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: '0.10'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: *70327899221680
93
+ - !ruby/object:Gem::Dependency
94
+ name: rspec
95
+ requirement: &70327899221200 !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ~>
99
+ - !ruby/object:Gem::Version
100
+ version: '2.10'
101
+ type: :development
102
+ prerelease: false
103
+ version_requirements: *70327899221200
104
+ - !ruby/object:Gem::Dependency
105
+ name: guard-rspec
106
+ requirement: &70327899220720 !ruby/object:Gem::Requirement
107
+ none: false
108
+ requirements:
109
+ - - ~>
110
+ - !ruby/object:Gem::Version
111
+ version: '0.6'
112
+ type: :development
113
+ prerelease: false
114
+ version_requirements: *70327899220720
115
+ description: Like a modern code version of the mythical beast with 100 serpent heads,
116
+ Typhoeus runs HTTP requests in parallel while cleanly encapsulating handling logic.
117
+ email: hans.hasselberg@gmail.com
118
+ executables: []
119
+ extensions: []
120
+ extra_rdoc_files: []
121
+ files:
122
+ - lib/typhoeus/curl.rb
123
+ - lib/typhoeus/easy/auth.rb
124
+ - lib/typhoeus/easy/callbacks.rb
125
+ - lib/typhoeus/easy/ffi_helper.rb
126
+ - lib/typhoeus/easy/infos.rb
127
+ - lib/typhoeus/easy/options.rb
128
+ - lib/typhoeus/easy/proxy.rb
129
+ - lib/typhoeus/easy/ssl.rb
130
+ - lib/typhoeus/easy.rb
131
+ - lib/typhoeus/filter.rb
132
+ - lib/typhoeus/form.rb
133
+ - lib/typhoeus/header.rb
134
+ - lib/typhoeus/hydra/callbacks.rb
135
+ - lib/typhoeus/hydra/connect_options.rb
136
+ - lib/typhoeus/hydra/stubbing.rb
137
+ - lib/typhoeus/hydra.rb
138
+ - lib/typhoeus/hydra_mock.rb
139
+ - lib/typhoeus/multi.rb
140
+ - lib/typhoeus/param_processor.rb
141
+ - lib/typhoeus/remote.rb
142
+ - lib/typhoeus/remote_method.rb
143
+ - lib/typhoeus/remote_proxy_object.rb
144
+ - lib/typhoeus/request.rb
145
+ - lib/typhoeus/response.rb
146
+ - lib/typhoeus/utils.rb
147
+ - lib/typhoeus/version.rb
148
+ - lib/typhoeus.rb
149
+ - CHANGELOG.md
150
+ - Gemfile
151
+ - LICENSE
152
+ - README.md
153
+ - Rakefile
154
+ homepage: https://github.com/dbalatero/typhoeus
155
+ licenses: []
156
+ post_install_message:
157
+ rdoc_options: []
158
+ require_paths:
159
+ - /Users/krawek/Projects/OpenSource/typhoeus/lib
160
+ required_ruby_version: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ! '>='
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ required_rubygems_version: !ruby/object:Gem::Requirement
167
+ none: false
168
+ requirements:
169
+ - - ! '>='
170
+ - !ruby/object:Gem::Version
171
+ version: 1.3.6
172
+ requirements: []
173
+ rubyforge_project: ! '[none]'
174
+ rubygems_version: 1.8.11
175
+ signing_key:
176
+ specification_version: 3
177
+ summary: Parallel HTTP library on top of libcurl multi.
178
+ test_files: []
179
+ has_rdoc: