tech-angels-typhoeus 0.1.36
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.
- data/.gitignore +2 -0
- data/README.textile +312 -0
- data/Rakefile +39 -0
- data/VERSION +1 -0
- data/benchmarks/profile.rb +25 -0
- data/benchmarks/vs_nethttp.rb +35 -0
- data/examples/twitter.rb +21 -0
- data/ext/typhoeus/.gitignore +5 -0
- data/ext/typhoeus/Makefile +157 -0
- data/ext/typhoeus/extconf.rb +65 -0
- data/ext/typhoeus/native.c +11 -0
- data/ext/typhoeus/native.h +21 -0
- data/ext/typhoeus/typhoeus_easy.c +207 -0
- data/ext/typhoeus/typhoeus_easy.h +19 -0
- data/ext/typhoeus/typhoeus_multi.c +225 -0
- data/ext/typhoeus/typhoeus_multi.h +16 -0
- data/lib/typhoeus.rb +55 -0
- data/lib/typhoeus/.gitignore +1 -0
- data/lib/typhoeus/easy.rb +329 -0
- data/lib/typhoeus/filter.rb +28 -0
- data/lib/typhoeus/hydra.rb +235 -0
- data/lib/typhoeus/multi.rb +35 -0
- data/lib/typhoeus/remote.rb +306 -0
- data/lib/typhoeus/remote_method.rb +108 -0
- data/lib/typhoeus/remote_proxy_object.rb +48 -0
- data/lib/typhoeus/request.rb +159 -0
- data/lib/typhoeus/response.rb +49 -0
- data/lib/typhoeus/service.rb +20 -0
- data/profilers/valgrind.rb +24 -0
- data/spec/fixtures/result_set.xml +60 -0
- data/spec/servers/app.rb +73 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +11 -0
- data/spec/typhoeus/easy_spec.rb +236 -0
- data/spec/typhoeus/filter_spec.rb +35 -0
- data/spec/typhoeus/hydra_spec.rb +311 -0
- data/spec/typhoeus/multi_spec.rb +74 -0
- data/spec/typhoeus/remote_method_spec.rb +141 -0
- data/spec/typhoeus/remote_proxy_object_spec.rb +65 -0
- data/spec/typhoeus/remote_spec.rb +695 -0
- data/spec/typhoeus/request_spec.rb +169 -0
- data/spec/typhoeus/response_spec.rb +63 -0
- data/typhoeus.gemspec +112 -0
- metadata +203 -0
@@ -0,0 +1,108 @@
|
|
1
|
+
module Typhoeus
|
2
|
+
class RemoteMethod
|
3
|
+
attr_accessor :http_method, :options, :base_uri, :path, :on_success, :on_failure, :cache_ttl
|
4
|
+
|
5
|
+
def initialize(options = {})
|
6
|
+
@http_method = options.delete(:method) || :get
|
7
|
+
@options = options
|
8
|
+
@base_uri = options.delete(:base_uri)
|
9
|
+
@path = options.delete(:path)
|
10
|
+
@on_success = options[:on_success]
|
11
|
+
@on_failure = options[:on_failure]
|
12
|
+
@cache_responses = options.delete(:cache_responses)
|
13
|
+
@memoize_responses = options.delete(:memoize_responses) || @cache_responses
|
14
|
+
@cache_ttl = @cache_responses == true ? 0 : @cache_responses
|
15
|
+
@keys = nil
|
16
|
+
|
17
|
+
clear_cache
|
18
|
+
end
|
19
|
+
|
20
|
+
def cache_responses?
|
21
|
+
@cache_responses
|
22
|
+
end
|
23
|
+
|
24
|
+
def memoize_responses?
|
25
|
+
@memoize_responses
|
26
|
+
end
|
27
|
+
|
28
|
+
def args_options_key(args, options)
|
29
|
+
"#{args.to_s}+#{options.to_s}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def calling(args, options)
|
33
|
+
@called_methods[args_options_key(args, options)] = true
|
34
|
+
end
|
35
|
+
|
36
|
+
def already_called?(args, options)
|
37
|
+
@called_methods.has_key? args_options_key(args, options)
|
38
|
+
end
|
39
|
+
|
40
|
+
def add_response_block(block, args, options)
|
41
|
+
@response_blocks[args_options_key(args, options)] << block
|
42
|
+
end
|
43
|
+
|
44
|
+
def call_response_blocks(result, args, options)
|
45
|
+
key = args_options_key(args, options)
|
46
|
+
@response_blocks[key].each {|block| block.call(result)}
|
47
|
+
@response_blocks.delete(key)
|
48
|
+
@called_methods.delete(key)
|
49
|
+
end
|
50
|
+
|
51
|
+
def clear_cache
|
52
|
+
@response_blocks = Hash.new {|h, k| h[k] = []}
|
53
|
+
@called_methods = {}
|
54
|
+
end
|
55
|
+
|
56
|
+
def merge_options(new_options)
|
57
|
+
merged = options.merge(new_options)
|
58
|
+
if options.has_key?(:params) && new_options.has_key?(:params)
|
59
|
+
merged[:params] = options[:params].merge(new_options[:params])
|
60
|
+
end
|
61
|
+
argument_names.each {|a| merged.delete(a)}
|
62
|
+
merged.delete(:on_success) if merged[:on_success].nil?
|
63
|
+
merged.delete(:on_failure) if merged[:on_failure].nil?
|
64
|
+
merged
|
65
|
+
end
|
66
|
+
|
67
|
+
def interpolate_path_with_arguments(args)
|
68
|
+
interpolated_path = @path
|
69
|
+
argument_names.each do |arg|
|
70
|
+
interpolated_path = interpolated_path.gsub(":#{arg}", args[arg].to_s)
|
71
|
+
end
|
72
|
+
interpolated_path
|
73
|
+
end
|
74
|
+
|
75
|
+
def argument_names
|
76
|
+
return @keys if @keys
|
77
|
+
pattern, keys = compile(@path)
|
78
|
+
@keys = keys.collect {|k| k.to_sym}
|
79
|
+
end
|
80
|
+
|
81
|
+
# rippped from Sinatra. clean up stuff we don't need later
|
82
|
+
def compile(path)
|
83
|
+
path ||= ""
|
84
|
+
keys = []
|
85
|
+
if path.respond_to? :to_str
|
86
|
+
special_chars = %w{. + ( )}
|
87
|
+
pattern =
|
88
|
+
path.gsub(/((:\w+)|[\*#{special_chars.join}])/) do |match|
|
89
|
+
case match
|
90
|
+
when "*"
|
91
|
+
keys << 'splat'
|
92
|
+
"(.*?)"
|
93
|
+
when *special_chars
|
94
|
+
Regexp.escape(match)
|
95
|
+
else
|
96
|
+
keys << $2[1..-1]
|
97
|
+
"([^/?&#]+)"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
[/^#{pattern}$/, keys]
|
101
|
+
elsif path.respond_to? :match
|
102
|
+
[path, keys]
|
103
|
+
else
|
104
|
+
raise TypeError, path
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Typhoeus
|
2
|
+
class RemoteProxyObject
|
3
|
+
instance_methods.each { |m| undef_method m unless m =~ /^__|object_id/ }
|
4
|
+
|
5
|
+
def initialize(clear_memoized_store_proc, easy, options = {})
|
6
|
+
@clear_memoized_store_proc = clear_memoized_store_proc
|
7
|
+
@easy = easy
|
8
|
+
@success = options[:on_success]
|
9
|
+
@failure = options[:on_failure]
|
10
|
+
@cache = options.delete(:cache)
|
11
|
+
@cache_key = options.delete(:cache_key)
|
12
|
+
@timeout = options.delete(:cache_timeout)
|
13
|
+
Typhoeus.add_easy_request(@easy)
|
14
|
+
end
|
15
|
+
|
16
|
+
def method_missing(sym, *args, &block)
|
17
|
+
unless @proxied_object
|
18
|
+
if @cache && @cache_key
|
19
|
+
@proxied_object = @cache.get(@cache_key) rescue nil
|
20
|
+
end
|
21
|
+
|
22
|
+
unless @proxied_object
|
23
|
+
Typhoeus.perform_easy_requests
|
24
|
+
response = Response.new(:code => @easy.response_code,
|
25
|
+
:headers => @easy.response_header,
|
26
|
+
:body => @easy.response_body,
|
27
|
+
:time => @easy.total_time_taken,
|
28
|
+
:requested_url => @easy.url,
|
29
|
+
:requested_http_method => @easy.method,
|
30
|
+
:start_time => @easy.start_time)
|
31
|
+
if @easy.response_code >= 200 && @easy.response_code < 300
|
32
|
+
Typhoeus.release_easy_object(@easy)
|
33
|
+
@proxied_object = @success.nil? ? response : @success.call(response)
|
34
|
+
|
35
|
+
if @cache && @cache_key
|
36
|
+
@cache.set(@cache_key, @proxied_object, @timeout)
|
37
|
+
end
|
38
|
+
else
|
39
|
+
@proxied_object = @failure.nil? ? response : @failure.call(response)
|
40
|
+
end
|
41
|
+
@clear_memoized_store_proc.call
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
@proxied_object.__send__(sym, *args, &block)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
module Typhoeus
|
2
|
+
class Request
|
3
|
+
attr_accessor :method, :params, :body, :headers, :timeout, :user_agent, :response, :cache_timeout, :follow_location, :max_redirects, :proxy, :disable_ssl_peer_verification, :ssl_cert, :ssl_cert_type, :ssl_key, :ssl_key_type, :ssl_key_password, :ssl_cacert, :ssl_capath, :verbose
|
4
|
+
|
5
|
+
|
6
|
+
attr_reader :url
|
7
|
+
|
8
|
+
# Initialize a new Request
|
9
|
+
#
|
10
|
+
# Options:
|
11
|
+
# * +url+ : Endpoint (URL) of the request
|
12
|
+
# * +options+ : A hash containing options among :
|
13
|
+
# ** +:method+ : :get (default) / :post / :put
|
14
|
+
# ** +:params+ : params as a Hash
|
15
|
+
# ** +:body+
|
16
|
+
# ** +:timeout+ : timeout (ms)
|
17
|
+
# ** +:headers+ : headers as Hash
|
18
|
+
# ** +:user_agent+ : user agent (string)
|
19
|
+
# ** +:cache_timeout+ : cache timeout (ms)
|
20
|
+
# ** +:follow_location
|
21
|
+
# ** +:max_redirects
|
22
|
+
# ** +:proxy
|
23
|
+
# ** +:disable_ssl_peer_verification
|
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
|
+
# ** +:verbose
|
32
|
+
#
|
33
|
+
def initialize(url, options = {})
|
34
|
+
@method = options[:method] || :get
|
35
|
+
@params = options[:params]
|
36
|
+
@body = options[:body]
|
37
|
+
@timeout = options[:timeout]
|
38
|
+
@headers = options[:headers] || {}
|
39
|
+
@user_agent = options[:user_agent] || Typhoeus::USER_AGENT
|
40
|
+
@cache_timeout = options[:cache_timeout]
|
41
|
+
@follow_location = options[:follow_location]
|
42
|
+
@max_redirects = options[:max_redirects]
|
43
|
+
@proxy = options[:proxy]
|
44
|
+
@disable_ssl_peer_verification = options[:disable_ssl_peer_verification]
|
45
|
+
@ssl_cert = options[:ssl_cert]
|
46
|
+
@ssl_cert_type = options[:ssl_cert_type]
|
47
|
+
@ssl_key = options[:ssl_key]
|
48
|
+
@ssl_key_type = options[:ssl_key_type]
|
49
|
+
@ssl_key_password = options[:ssl_key_password]
|
50
|
+
@ssl_cacert = options[:ssl_cacert]
|
51
|
+
@ssl_capath = options[:ssl_capath]
|
52
|
+
@verbose = options[:verbose]
|
53
|
+
|
54
|
+
if @method == :post
|
55
|
+
@url = url
|
56
|
+
else
|
57
|
+
@url = @params ? "#{url}?#{params_string}" : url
|
58
|
+
end
|
59
|
+
@on_complete = nil
|
60
|
+
@after_complete = nil
|
61
|
+
@handled_response = nil
|
62
|
+
end
|
63
|
+
|
64
|
+
def host
|
65
|
+
slash_location = @url.index('/', 8)
|
66
|
+
if slash_location
|
67
|
+
@url.slice(0, slash_location)
|
68
|
+
else
|
69
|
+
query_string_location = @url.index('?')
|
70
|
+
return query_string_location ? @url.slice(0, query_string_location) : @url
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def headers
|
75
|
+
@headers["User-Agent"] = @user_agent
|
76
|
+
@headers
|
77
|
+
end
|
78
|
+
|
79
|
+
def params_string
|
80
|
+
params.keys.sort { |a, b| a.to_s <=> b.to_s }.collect do |k|
|
81
|
+
value = params[k]
|
82
|
+
if value.is_a? Hash
|
83
|
+
value.keys.collect {|sk| Rack::Utils.escape("#{k}[#{sk}]") + "=" + Rack::Utils.escape(value[sk].to_s)}
|
84
|
+
elsif value.is_a? Array
|
85
|
+
key = Rack::Utils.escape(k.to_s)
|
86
|
+
value.collect { |v| "#{key}=#{Rack::Utils.escape(v.to_s)}" }.join('&')
|
87
|
+
else
|
88
|
+
"#{Rack::Utils.escape(k.to_s)}=#{Rack::Utils.escape(params[k].to_s)}"
|
89
|
+
end
|
90
|
+
end.flatten.join("&")
|
91
|
+
end
|
92
|
+
|
93
|
+
def on_complete(&block)
|
94
|
+
@on_complete = block
|
95
|
+
end
|
96
|
+
|
97
|
+
def on_complete=(proc)
|
98
|
+
@on_complete = proc
|
99
|
+
end
|
100
|
+
|
101
|
+
def after_complete(&block)
|
102
|
+
@after_complete = block
|
103
|
+
end
|
104
|
+
|
105
|
+
def after_complete=(proc)
|
106
|
+
@after_complete = proc
|
107
|
+
end
|
108
|
+
|
109
|
+
def call_handlers
|
110
|
+
if @on_complete
|
111
|
+
@handled_response = @on_complete.call(response)
|
112
|
+
call_after_complete
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def call_after_complete
|
117
|
+
@after_complete.call(@handled_response) if @after_complete
|
118
|
+
end
|
119
|
+
|
120
|
+
def handled_response=(val)
|
121
|
+
@handled_response = val
|
122
|
+
end
|
123
|
+
|
124
|
+
def handled_response
|
125
|
+
@handled_response || response
|
126
|
+
end
|
127
|
+
|
128
|
+
def cache_key
|
129
|
+
Digest::SHA1.hexdigest(url)
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.run(url, params)
|
133
|
+
r = new(url, params)
|
134
|
+
Typhoeus::Hydra.hydra.queue r
|
135
|
+
Typhoeus::Hydra.hydra.run
|
136
|
+
r.response
|
137
|
+
end
|
138
|
+
|
139
|
+
def self.get(url, params = {})
|
140
|
+
run(url, params.merge(:method => :get))
|
141
|
+
end
|
142
|
+
|
143
|
+
def self.post(url, params = {})
|
144
|
+
run(url, params.merge(:method => :post))
|
145
|
+
end
|
146
|
+
|
147
|
+
def self.put(url, params = {})
|
148
|
+
run(url, params.merge(:method => :put))
|
149
|
+
end
|
150
|
+
|
151
|
+
def self.delete(url, params = {})
|
152
|
+
run(url, params.merge(:method => :delete))
|
153
|
+
end
|
154
|
+
|
155
|
+
def self.head(url, params = {})
|
156
|
+
run(url, params.merge(:method => :head))
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Typhoeus
|
2
|
+
class Response
|
3
|
+
attr_accessor :request
|
4
|
+
attr_reader :code, :headers, :body, :time,
|
5
|
+
:requested_url, :requested_remote_method,
|
6
|
+
:requested_http_method, :start_time,
|
7
|
+
:effective_url
|
8
|
+
|
9
|
+
def initialize(params = {})
|
10
|
+
@code = params[:code]
|
11
|
+
@headers = params[:headers]
|
12
|
+
@body = params[:body]
|
13
|
+
@time = params[:time]
|
14
|
+
@requested_url = params[:requested_url]
|
15
|
+
@requested_http_method = params[:requested_http_method]
|
16
|
+
@start_time = params[:start_time]
|
17
|
+
@request = params[:request]
|
18
|
+
@effective_url = params[:effective_url]
|
19
|
+
end
|
20
|
+
|
21
|
+
def headers_hash
|
22
|
+
headers.split("\n").map {|o| o.strip}.inject({}) do |hash, o|
|
23
|
+
if o.empty?
|
24
|
+
hash
|
25
|
+
else
|
26
|
+
i = o.index(":") || o.size
|
27
|
+
key = o.slice(0, i)
|
28
|
+
value = o.slice(i + 1, o.size)
|
29
|
+
value = value.strip unless value.nil?
|
30
|
+
if hash.has_key? key
|
31
|
+
hash[key] = [hash[key], value].flatten
|
32
|
+
else
|
33
|
+
hash[key] = value
|
34
|
+
end
|
35
|
+
|
36
|
+
hash
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def success?
|
42
|
+
@code >= 200 && @code < 300
|
43
|
+
end
|
44
|
+
|
45
|
+
def modified?
|
46
|
+
@code != 304
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Typhoeus
|
2
|
+
class Service
|
3
|
+
def initialize(host, port)
|
4
|
+
@host = host
|
5
|
+
@port = port
|
6
|
+
end
|
7
|
+
|
8
|
+
def get(resource, params)
|
9
|
+
end
|
10
|
+
|
11
|
+
def put(resource, params)
|
12
|
+
end
|
13
|
+
|
14
|
+
def post(resource, params)
|
15
|
+
end
|
16
|
+
|
17
|
+
def delete(resource, params)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# go to ext/typhoeus and run ruby extconf.rb && make before running
|
3
|
+
# this.
|
4
|
+
|
5
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__) + "/../ext")
|
6
|
+
require File.dirname(__FILE__) + "/../lib/typhoeus"
|
7
|
+
|
8
|
+
klass = Class.new { include Typhoeus }
|
9
|
+
|
10
|
+
loops = ENV['LOOPS'].to_i
|
11
|
+
url = ARGV.first || (raise "requires URL!")
|
12
|
+
|
13
|
+
loops.times do |i|
|
14
|
+
puts "On loop #{i}" if i % 10 == 0
|
15
|
+
results = []
|
16
|
+
5.times do
|
17
|
+
results << klass.get(url)
|
18
|
+
end
|
19
|
+
|
20
|
+
# fire requests
|
21
|
+
results[0].code
|
22
|
+
end
|
23
|
+
|
24
|
+
puts "Ran #{loops} loops on #{url}!"
|
@@ -0,0 +1,60 @@
|
|
1
|
+
<result_set>
|
2
|
+
<ttl>20</ttl>
|
3
|
+
<result>
|
4
|
+
<id>1</id>
|
5
|
+
<name>hello</name>
|
6
|
+
<description>
|
7
|
+
this is a long description for a text field of some kind.
|
8
|
+
this is a long description for a text field of some kind.
|
9
|
+
this is a long description for a text field of some kind.
|
10
|
+
this is a long description for a text field of some kind.
|
11
|
+
this is a long description for a text field of some kind.
|
12
|
+
this is a long description for a text field of some kind.
|
13
|
+
this is a long description for a text field of some kind.
|
14
|
+
this is a long description for a text field of some kind.
|
15
|
+
this is a long description for a text field of some kind.
|
16
|
+
this is a long description for a text field of some kind.
|
17
|
+
this is a long description for a text field of some kind.
|
18
|
+
this is a long description for a text field of some kind.
|
19
|
+
this is a long description for a text field of some kind.
|
20
|
+
</description>
|
21
|
+
</result>
|
22
|
+
<result>
|
23
|
+
<id>2</id>
|
24
|
+
<name>hello</name>
|
25
|
+
<description>
|
26
|
+
this is a long description for a text field of some kind.
|
27
|
+
this is a long description for a text field of some kind.
|
28
|
+
this is a long description for a text field of some kind.
|
29
|
+
this is a long description for a text field of some kind.
|
30
|
+
this is a long description for a text field of some kind.
|
31
|
+
this is a long description for a text field of some kind.
|
32
|
+
this is a long description for a text field of some kind.
|
33
|
+
this is a long description for a text field of some kind.
|
34
|
+
this is a long description for a text field of some kind.
|
35
|
+
this is a long description for a text field of some kind.
|
36
|
+
this is a long description for a text field of some kind.
|
37
|
+
this is a long description for a text field of some kind.
|
38
|
+
this is a long description for a text field of some kind.
|
39
|
+
</description>
|
40
|
+
</result>
|
41
|
+
<result>
|
42
|
+
<id>3</id>
|
43
|
+
<name>hello</name>
|
44
|
+
<description>
|
45
|
+
this is a long description for a text field of some kind.
|
46
|
+
this is a long description for a text field of some kind.
|
47
|
+
this is a long description for a text field of some kind.
|
48
|
+
this is a long description for a text field of some kind.
|
49
|
+
this is a long description for a text field of some kind.
|
50
|
+
this is a long description for a text field of some kind.
|
51
|
+
this is a long description for a text field of some kind.
|
52
|
+
this is a long description for a text field of some kind.
|
53
|
+
this is a long description for a text field of some kind.
|
54
|
+
this is a long description for a text field of some kind.
|
55
|
+
this is a long description for a text field of some kind.
|
56
|
+
this is a long description for a text field of some kind.
|
57
|
+
this is a long description for a text field of some kind.
|
58
|
+
</description>
|
59
|
+
</result>
|
60
|
+
</result_set>
|