arachni-typhoeus 0.2.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.
Files changed (55) hide show
  1. data/.gitignore +3 -0
  2. data/CHANGELOG.markdown +43 -0
  3. data/Gemfile +9 -0
  4. data/Gemfile.lock +30 -0
  5. data/README.textile +6 -0
  6. data/Rakefile +40 -0
  7. data/VERSION +1 -0
  8. data/benchmarks/profile.rb +25 -0
  9. data/benchmarks/vs_nethttp.rb +35 -0
  10. data/examples/twitter.rb +21 -0
  11. data/ext/typhoeus/.gitignore +7 -0
  12. data/ext/typhoeus/extconf.rb +65 -0
  13. data/ext/typhoeus/native.c +11 -0
  14. data/ext/typhoeus/native.h +21 -0
  15. data/ext/typhoeus/typhoeus_easy.c +220 -0
  16. data/ext/typhoeus/typhoeus_easy.h +19 -0
  17. data/ext/typhoeus/typhoeus_multi.c +211 -0
  18. data/ext/typhoeus/typhoeus_multi.h +16 -0
  19. data/lib/typhoeus.rb +58 -0
  20. data/lib/typhoeus/.gitignore +1 -0
  21. data/lib/typhoeus/easy.rb +366 -0
  22. data/lib/typhoeus/filter.rb +28 -0
  23. data/lib/typhoeus/hydra.rb +245 -0
  24. data/lib/typhoeus/hydra/callbacks.rb +24 -0
  25. data/lib/typhoeus/hydra/connect_options.rb +61 -0
  26. data/lib/typhoeus/hydra/stubbing.rb +52 -0
  27. data/lib/typhoeus/hydra_mock.rb +131 -0
  28. data/lib/typhoeus/multi.rb +37 -0
  29. data/lib/typhoeus/normalized_header_hash.rb +58 -0
  30. data/lib/typhoeus/remote.rb +306 -0
  31. data/lib/typhoeus/remote_method.rb +108 -0
  32. data/lib/typhoeus/remote_proxy_object.rb +50 -0
  33. data/lib/typhoeus/request.rb +210 -0
  34. data/lib/typhoeus/response.rb +91 -0
  35. data/lib/typhoeus/service.rb +20 -0
  36. data/lib/typhoeus/utils.rb +24 -0
  37. data/profilers/valgrind.rb +24 -0
  38. data/spec/fixtures/result_set.xml +60 -0
  39. data/spec/servers/app.rb +84 -0
  40. data/spec/spec.opts +2 -0
  41. data/spec/spec_helper.rb +11 -0
  42. data/spec/typhoeus/easy_spec.rb +284 -0
  43. data/spec/typhoeus/filter_spec.rb +35 -0
  44. data/spec/typhoeus/hydra_mock_spec.rb +300 -0
  45. data/spec/typhoeus/hydra_spec.rb +526 -0
  46. data/spec/typhoeus/multi_spec.rb +74 -0
  47. data/spec/typhoeus/normalized_header_hash_spec.rb +41 -0
  48. data/spec/typhoeus/remote_method_spec.rb +141 -0
  49. data/spec/typhoeus/remote_proxy_object_spec.rb +65 -0
  50. data/spec/typhoeus/remote_spec.rb +695 -0
  51. data/spec/typhoeus/request_spec.rb +276 -0
  52. data/spec/typhoeus/response_spec.rb +151 -0
  53. data/spec/typhoeus/utils_spec.rb +22 -0
  54. data/typhoeus.gemspec +123 -0
  55. metadata +196 -0
@@ -0,0 +1,50 @@
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
+ :curl_return_code => @easy.curl_return_code,
26
+ :curl_error_message => @easy.curl_error_message,
27
+ :headers => @easy.response_header,
28
+ :body => @easy.response_body,
29
+ :time => @easy.total_time_taken,
30
+ :requested_url => @easy.url,
31
+ :requested_http_method => @easy.method,
32
+ :start_time => @easy.start_time)
33
+ if @easy.response_code >= 200 && @easy.response_code < 300
34
+ Typhoeus.release_easy_object(@easy)
35
+ @proxied_object = @success.nil? ? response : @success.call(response)
36
+
37
+ if @cache && @cache_key
38
+ @cache.set(@cache_key, @proxied_object, @timeout)
39
+ end
40
+ else
41
+ @proxied_object = @failure.nil? ? response : @failure.call(response)
42
+ end
43
+ @clear_memoized_store_proc.call
44
+ end
45
+ end
46
+
47
+ @proxied_object.__send__(sym, *args, &block)
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,210 @@
1
+ require 'uri'
2
+
3
+ module Typhoeus
4
+ class Request
5
+ attr_reader :url
6
+ attr_writer :headers
7
+ attr_accessor :method, :params, :body, :connect_timeout, :timeout,
8
+ :user_agent, :response, :cache_timeout, :follow_location,
9
+ :max_redirects, :proxy, :proxy_username,:proxy_password,
10
+ :disable_ssl_peer_verification,
11
+ :ssl_cert, :ssl_cert_type, :ssl_key, :ssl_key_type,
12
+ :ssl_key_password, :ssl_cacert, :ssl_capath, :verbose,
13
+ :username, :password, :auth_method, :user_agent,
14
+ :proxy_auth_method, :proxy_type
15
+
16
+ # Initialize a new Request
17
+ #
18
+ # Options:
19
+ # * +url+ : Endpoint (URL) of the request
20
+ # * +options+ : A hash containing options among :
21
+ # ** +:method+ : :get (default) / :post / :put
22
+ # ** +:params+ : params as a Hash
23
+ # ** +:body+
24
+ # ** +:timeout+ : timeout (ms)
25
+ # ** +:connect_timeout+ : connect timeout (ms)
26
+ # ** +:headers+ : headers as Hash
27
+ # ** +:user_agent+ : user agent (string)
28
+ # ** +:cache_timeout+ : cache timeout (ms)
29
+ # ** +:follow_location
30
+ # ** +:max_redirects
31
+ # ** +:proxy
32
+ # ** +:disable_ssl_peer_verification
33
+ # ** +:ssl_cert
34
+ # ** +:ssl_cert_type
35
+ # ** +:ssl_key
36
+ # ** +:ssl_key_type
37
+ # ** +:ssl_key_password
38
+ # ** +:ssl_cacert
39
+ # ** +:ssl_capath
40
+ # ** +:verbose
41
+ # ** +:username
42
+ # ** +:password
43
+ # ** +:auth_method
44
+ #
45
+ def initialize(url, options = {})
46
+ @method = options[:method] || :get
47
+ @params = options[:params]
48
+ @body = options[:body]
49
+ @timeout = options[:timeout]
50
+ @connect_timeout = options[:connect_timeout]
51
+ @headers = options[:headers] || {}
52
+ @user_agent = options[:user_agent] || Typhoeus::USER_AGENT
53
+ @cache_timeout = options[:cache_timeout]
54
+ @follow_location = options[:follow_location]
55
+ @max_redirects = options[:max_redirects]
56
+ @proxy = options[:proxy]
57
+ @proxy_type = options[:proxy_type]
58
+ @proxy_username = options[:proxy_username]
59
+ @proxy_password = options[:proxy_password]
60
+ @proxy_auth_method = options[:proxy_auth_method]
61
+ @disable_ssl_peer_verification = options[:disable_ssl_peer_verification]
62
+ @ssl_cert = options[:ssl_cert]
63
+ @ssl_cert_type = options[:ssl_cert_type]
64
+ @ssl_key = options[:ssl_key]
65
+ @ssl_key_type = options[:ssl_key_type]
66
+ @ssl_key_password = options[:ssl_key_password]
67
+ @ssl_cacert = options[:ssl_cacert]
68
+ @ssl_capath = options[:ssl_capath]
69
+ @verbose = options[:verbose]
70
+ @username = options[:username]
71
+ @password = options[:password]
72
+ @auth_method = options[:auth_method]
73
+
74
+ if @method == :post
75
+ @url = url
76
+ else
77
+ @url = @params ? "#{url}?#{params_string}" : url
78
+ end
79
+
80
+ @parsed_uri = URI.parse(@url)
81
+
82
+ @on_complete = nil
83
+ @after_complete = nil
84
+ @handled_response = nil
85
+ end
86
+
87
+ LOCALHOST_ALIASES = %w[ localhost 127.0.0.1 0.0.0.0 ]
88
+
89
+ def localhost?
90
+ LOCALHOST_ALIASES.include?(@parsed_uri.host)
91
+ end
92
+
93
+ def host
94
+ slash_location = @url.index('/', 8)
95
+ if slash_location
96
+ @url.slice(0, slash_location)
97
+ else
98
+ query_string_location = @url.index('?')
99
+ return query_string_location ? @url.slice(0, query_string_location) : @url
100
+ end
101
+ end
102
+
103
+ def host_domain
104
+ @parsed_uri.host
105
+ end
106
+
107
+ def headers
108
+ @headers["User-Agent"] = @user_agent
109
+ @headers
110
+ end
111
+
112
+ def params_string
113
+ params.keys.sort { |a, b| a.to_s <=> b.to_s }.collect do |k|
114
+ value = params[k]
115
+ if value.is_a? Hash
116
+ value.keys.collect {|sk| Typhoeus::Utils.escape("#{k}[#{sk}]") + "=" + Typhoeus::Utils.escape(value[sk].to_s)}
117
+ elsif value.is_a? Array
118
+ key = Typhoeus::Utils.escape(k.to_s)
119
+ value.collect { |v| "#{key}[]=#{Typhoeus::Utils.escape(v.to_s)}" }.join('&')
120
+ else
121
+ "#{Typhoeus::Utils.escape(k.to_s)}=#{Typhoeus::Utils.escape(params[k].to_s)}"
122
+ end
123
+ end.flatten.join("&")
124
+ end
125
+
126
+ def on_complete(&block)
127
+ @on_complete = block
128
+ end
129
+
130
+ def on_complete=(proc)
131
+ @on_complete = proc
132
+ end
133
+
134
+ def after_complete(&block)
135
+ @after_complete = block
136
+ end
137
+
138
+ def after_complete=(proc)
139
+ @after_complete = proc
140
+ end
141
+
142
+ def call_handlers
143
+ if @on_complete
144
+ @handled_response = @on_complete.call(response)
145
+ call_after_complete
146
+ end
147
+ end
148
+
149
+ def call_after_complete
150
+ @after_complete.call(@handled_response) if @after_complete
151
+ end
152
+
153
+ def handled_response=(val)
154
+ @handled_response = val
155
+ end
156
+
157
+ def handled_response
158
+ @handled_response || response
159
+ end
160
+
161
+ def inspect
162
+ result = ":method => #{self.method.inspect},\n" <<
163
+ "\t:url => #{URI.parse(self.url).to_s}"
164
+ if self.body and !self.body.empty?
165
+ result << ",\n\t:body => #{self.body.inspect}"
166
+ end
167
+
168
+ if self.params and !self.params.empty?
169
+ result << ",\n\t:params => #{self.params.inspect}"
170
+ end
171
+
172
+ if self.headers and !self.headers.empty?
173
+ result << ",\n\t:headers => #{self.headers.inspect}"
174
+ end
175
+
176
+ result
177
+ end
178
+
179
+ def cache_key
180
+ Digest::SHA1.hexdigest(url)
181
+ end
182
+
183
+ def self.run(url, params)
184
+ r = new(url, params)
185
+ Typhoeus::Hydra.hydra.queue r
186
+ Typhoeus::Hydra.hydra.run
187
+ r.response
188
+ end
189
+
190
+ def self.get(url, params = {})
191
+ run(url, params.merge(:method => :get))
192
+ end
193
+
194
+ def self.post(url, params = {})
195
+ run(url, params.merge(:method => :post))
196
+ end
197
+
198
+ def self.put(url, params = {})
199
+ run(url, params.merge(:method => :put))
200
+ end
201
+
202
+ def self.delete(url, params = {})
203
+ run(url, params.merge(:method => :delete))
204
+ end
205
+
206
+ def self.head(url, params = {})
207
+ run(url, params.merge(:method => :head))
208
+ end
209
+ end
210
+ end
@@ -0,0 +1,91 @@
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
+
12
+ attr_writer :headers_hash
13
+
14
+ def initialize(params = {})
15
+ @code = params[:code]
16
+ @curl_return_code = params[:curl_return_code]
17
+ @curl_error_message = params[:curl_error_message]
18
+ @status_message = params[:status_message]
19
+ @http_version = params[:http_version]
20
+ @headers = params[:headers] || ''
21
+ @body = params[:body]
22
+ @time = params[:time]
23
+ @requested_url = params[:requested_url]
24
+ @requested_http_method = params[:requested_http_method]
25
+ @start_time = params[:start_time]
26
+ @start_transfer_time = params[:start_transfer_time]
27
+ @app_connect_time = params[:app_connect_time]
28
+ @pretransfer_time = params[:pretransfer_time]
29
+ @connect_time = params[:connect_time]
30
+ @name_lookup_time = params[:name_lookup_time]
31
+ @request = params[:request]
32
+ @effective_url = params[:effective_url]
33
+ @mock = params[:mock] || false # default
34
+ @headers_hash = NormalizedHeaderHash.new(params[:headers_hash]) if params[:headers_hash]
35
+ end
36
+
37
+ # Returns true if this is a mock response.
38
+ def mock?
39
+ @mock
40
+ end
41
+
42
+ def headers_hash
43
+ @headers_hash ||= begin
44
+ headers.split("\n").map {|o| o.strip}.inject(Typhoeus::NormalizedHeaderHash.new) do |hash, o|
45
+ if o.empty? || o =~ /^HTTP\/[\d\.]+/
46
+ hash
47
+ else
48
+ i = o.index(":") || o.size
49
+ key = o.slice(0, i)
50
+ value = o.slice(i + 1, o.size)
51
+ value = value.strip unless value.nil?
52
+ if hash.has_key? key
53
+ hash[key] = [hash[key], value].flatten
54
+ else
55
+ hash[key] = value
56
+ end
57
+
58
+ hash
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ def status_message
65
+ # http://rubular.com/r/eAr1oVYsVa
66
+ @status_message ||= first_header_line ? first_header_line[/\d{3} (.*)$/, 1].chomp : nil
67
+ end
68
+
69
+ def http_version
70
+ @http_version ||= first_header_line ? first_header_line[/HTTP\/(\S+)/, 1] : nil
71
+ end
72
+
73
+ def success?
74
+ @code >= 200 && @code < 300
75
+ end
76
+
77
+ def modified?
78
+ @code != 304
79
+ end
80
+
81
+ def timed_out?
82
+ curl_return_code == 28
83
+ end
84
+
85
+ private
86
+
87
+ def first_header_line
88
+ @first_header_line ||= headers.split("\n").first
89
+ end
90
+ end
91
+ 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
+ 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
+ # Return the bytesize of String; uses String#size under Ruby 1.8 and
12
+ # String#bytesize under 1.9.
13
+ if ''.respond_to?(:bytesize)
14
+ def bytesize(string)
15
+ string.bytesize
16
+ end
17
+ else
18
+ def bytesize(string)
19
+ string.size
20
+ end
21
+ end
22
+ module_function :bytesize
23
+ end
24
+ 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>