arachni-typhoeus 0.2.0

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