typhoeus 0.1.6 → 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.
- data/.gitignore +3 -0
- data/CHANGELOG.markdown +31 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +30 -0
- data/README.textile +333 -0
- data/Rakefile +38 -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 +6 -0
- data/ext/typhoeus/extconf.rb +3 -2
- data/ext/typhoeus/native.h +7 -0
- data/ext/typhoeus/typhoeus_multi.c +1 -2
- data/lib/typhoeus/.gitignore +1 -0
- data/lib/typhoeus/easy.rb +172 -42
- data/lib/typhoeus/hydra/callbacks.rb +24 -0
- data/lib/typhoeus/hydra/connect_options.rb +45 -0
- data/lib/typhoeus/hydra/stubbing.rb +52 -0
- data/lib/typhoeus/hydra.rb +85 -59
- data/lib/typhoeus/hydra_mock.rb +131 -0
- data/lib/typhoeus/multi.rb +4 -3
- data/lib/typhoeus/normalized_header_hash.rb +58 -0
- data/lib/typhoeus/remote_proxy_object.rb +6 -6
- data/lib/typhoeus/request.rb +118 -31
- data/lib/typhoeus/response.rb +61 -4
- data/lib/typhoeus/service.rb +20 -0
- data/lib/typhoeus/utils.rb +24 -0
- data/lib/typhoeus.rb +11 -8
- data/profilers/valgrind.rb +24 -0
- data/spec/fixtures/result_set.xml +60 -0
- data/spec/servers/app.rb +48 -1
- data/spec/spec_helper.rb +1 -1
- data/spec/typhoeus/easy_spec.rb +126 -4
- data/spec/typhoeus/hydra_mock_spec.rb +300 -0
- data/spec/typhoeus/hydra_spec.rb +456 -0
- data/spec/typhoeus/normalized_header_hash_spec.rb +41 -0
- data/spec/typhoeus/remote_spec.rb +2 -2
- data/spec/typhoeus/request_spec.rb +247 -0
- data/spec/typhoeus/response_spec.rb +104 -1
- data/spec/typhoeus/utils_spec.rb +22 -0
- data/typhoeus.gemspec +123 -0
- metadata +145 -34
- data/ext/typhoeus/Makefile +0 -157
data/lib/typhoeus/request.rb
CHANGED
|
@@ -1,22 +1,87 @@
|
|
|
1
|
+
require 'uri'
|
|
2
|
+
|
|
1
3
|
module Typhoeus
|
|
2
4
|
class Request
|
|
3
|
-
attr_accessor :method, :params, :body, :headers, :timeout, :user_agent, :response, :cache_timeout
|
|
4
5
|
attr_reader :url
|
|
5
|
-
|
|
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, :disable_ssl_peer_verification,
|
|
10
|
+
:ssl_cert, :ssl_cert_type, :ssl_key, :ssl_key_type,
|
|
11
|
+
:ssl_key_password, :ssl_cacert, :ssl_capath, :verbose,
|
|
12
|
+
:username, :password, :auth_method, :user_agent
|
|
13
|
+
|
|
14
|
+
# Initialize a new Request
|
|
15
|
+
#
|
|
16
|
+
# Options:
|
|
17
|
+
# * +url+ : Endpoint (URL) of the request
|
|
18
|
+
# * +options+ : A hash containing options among :
|
|
19
|
+
# ** +:method+ : :get (default) / :post / :put
|
|
20
|
+
# ** +:params+ : params as a Hash
|
|
21
|
+
# ** +:body+
|
|
22
|
+
# ** +:timeout+ : timeout (ms)
|
|
23
|
+
# ** +:connect_timeout+ : connect timeout (ms)
|
|
24
|
+
# ** +:headers+ : headers as Hash
|
|
25
|
+
# ** +:user_agent+ : user agent (string)
|
|
26
|
+
# ** +:cache_timeout+ : cache timeout (ms)
|
|
27
|
+
# ** +:follow_location
|
|
28
|
+
# ** +:max_redirects
|
|
29
|
+
# ** +:proxy
|
|
30
|
+
# ** +:disable_ssl_peer_verification
|
|
31
|
+
# ** +:ssl_cert
|
|
32
|
+
# ** +:ssl_cert_type
|
|
33
|
+
# ** +:ssl_key
|
|
34
|
+
# ** +:ssl_key_type
|
|
35
|
+
# ** +:ssl_key_password
|
|
36
|
+
# ** +:ssl_cacert
|
|
37
|
+
# ** +:ssl_capath
|
|
38
|
+
# ** +:verbose
|
|
39
|
+
# ** +:username
|
|
40
|
+
# ** +:password
|
|
41
|
+
# ** +:auth_method
|
|
42
|
+
#
|
|
6
43
|
def initialize(url, options = {})
|
|
7
44
|
@method = options[:method] || :get
|
|
8
45
|
@params = options[:params]
|
|
9
46
|
@body = options[:body]
|
|
10
47
|
@timeout = options[:timeout]
|
|
48
|
+
@connect_timeout = options[:connect_timeout]
|
|
11
49
|
@headers = options[:headers] || {}
|
|
12
50
|
@user_agent = options[:user_agent] || Typhoeus::USER_AGENT
|
|
13
51
|
@cache_timeout = options[:cache_timeout]
|
|
14
|
-
@
|
|
52
|
+
@follow_location = options[:follow_location]
|
|
53
|
+
@max_redirects = options[:max_redirects]
|
|
54
|
+
@proxy = options[:proxy]
|
|
55
|
+
@disable_ssl_peer_verification = options[:disable_ssl_peer_verification]
|
|
56
|
+
@ssl_cert = options[:ssl_cert]
|
|
57
|
+
@ssl_cert_type = options[:ssl_cert_type]
|
|
58
|
+
@ssl_key = options[:ssl_key]
|
|
59
|
+
@ssl_key_type = options[:ssl_key_type]
|
|
60
|
+
@ssl_key_password = options[:ssl_key_password]
|
|
61
|
+
@ssl_cacert = options[:ssl_cacert]
|
|
62
|
+
@ssl_capath = options[:ssl_capath]
|
|
63
|
+
@verbose = options[:verbose]
|
|
64
|
+
@username = options[:username]
|
|
65
|
+
@password = options[:password]
|
|
66
|
+
@auth_method = options[:auth_method]
|
|
67
|
+
|
|
68
|
+
if @method == :post
|
|
69
|
+
@url = url
|
|
70
|
+
else
|
|
71
|
+
@url = @params ? "#{url}?#{params_string}" : url
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
@parsed_uri = URI.parse(@url)
|
|
75
|
+
|
|
15
76
|
@on_complete = nil
|
|
16
77
|
@after_complete = nil
|
|
17
78
|
@handled_response = nil
|
|
18
79
|
end
|
|
19
|
-
|
|
80
|
+
|
|
81
|
+
def localhost?
|
|
82
|
+
%(localhost 127.0.0.1 0.0.0.0).include?(@parsed_uri.host)
|
|
83
|
+
end
|
|
84
|
+
|
|
20
85
|
def host
|
|
21
86
|
slash_location = @url.index('/', 8)
|
|
22
87
|
if slash_location
|
|
@@ -26,86 +91,108 @@ module Typhoeus
|
|
|
26
91
|
return query_string_location ? @url.slice(0, query_string_location) : @url
|
|
27
92
|
end
|
|
28
93
|
end
|
|
29
|
-
|
|
94
|
+
|
|
30
95
|
def headers
|
|
31
96
|
@headers["User-Agent"] = @user_agent
|
|
32
97
|
@headers
|
|
33
98
|
end
|
|
34
|
-
|
|
99
|
+
|
|
35
100
|
def params_string
|
|
36
|
-
params.keys.sort.collect do |k|
|
|
101
|
+
params.keys.sort { |a, b| a.to_s <=> b.to_s }.collect do |k|
|
|
37
102
|
value = params[k]
|
|
38
103
|
if value.is_a? Hash
|
|
39
|
-
value.keys.collect {|sk|
|
|
104
|
+
value.keys.collect {|sk| Typhoeus::Utils.escape("#{k}[#{sk}]") + "=" + Typhoeus::Utils.escape(value[sk].to_s)}
|
|
40
105
|
elsif value.is_a? Array
|
|
41
|
-
key =
|
|
42
|
-
value.collect { |v| "#{key}=#{
|
|
106
|
+
key = Typhoeus::Utils.escape(k.to_s)
|
|
107
|
+
value.collect { |v| "#{key}[]=#{Typhoeus::Utils.escape(v.to_s)}" }.join('&')
|
|
43
108
|
else
|
|
44
|
-
"#{
|
|
109
|
+
"#{Typhoeus::Utils.escape(k.to_s)}=#{Typhoeus::Utils.escape(params[k].to_s)}"
|
|
45
110
|
end
|
|
46
111
|
end.flatten.join("&")
|
|
47
112
|
end
|
|
48
|
-
|
|
113
|
+
|
|
49
114
|
def on_complete(&block)
|
|
50
115
|
@on_complete = block
|
|
51
116
|
end
|
|
52
|
-
|
|
117
|
+
|
|
53
118
|
def on_complete=(proc)
|
|
54
119
|
@on_complete = proc
|
|
55
120
|
end
|
|
56
|
-
|
|
121
|
+
|
|
57
122
|
def after_complete(&block)
|
|
58
123
|
@after_complete = block
|
|
59
124
|
end
|
|
60
|
-
|
|
125
|
+
|
|
61
126
|
def after_complete=(proc)
|
|
62
127
|
@after_complete = proc
|
|
63
128
|
end
|
|
64
|
-
|
|
129
|
+
|
|
65
130
|
def call_handlers
|
|
66
131
|
if @on_complete
|
|
67
132
|
@handled_response = @on_complete.call(response)
|
|
68
133
|
call_after_complete
|
|
69
134
|
end
|
|
70
135
|
end
|
|
71
|
-
|
|
136
|
+
|
|
72
137
|
def call_after_complete
|
|
73
|
-
|
|
138
|
+
@after_complete.call(@handled_response) if @after_complete
|
|
74
139
|
end
|
|
75
|
-
|
|
140
|
+
|
|
76
141
|
def handled_response=(val)
|
|
77
142
|
@handled_response = val
|
|
78
143
|
end
|
|
79
|
-
|
|
144
|
+
|
|
80
145
|
def handled_response
|
|
81
146
|
@handled_response || response
|
|
82
147
|
end
|
|
83
|
-
|
|
148
|
+
|
|
149
|
+
def inspect
|
|
150
|
+
result = ":method => #{self.method.inspect},\n" <<
|
|
151
|
+
"\t:url => #{URI.parse(self.url).to_s}"
|
|
152
|
+
if self.body and !self.body.empty?
|
|
153
|
+
result << ",\n\t:body => #{self.body.inspect}"
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
if self.params and !self.params.empty?
|
|
157
|
+
result << ",\n\t:params => #{self.params.inspect}"
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
if self.headers and !self.headers.empty?
|
|
161
|
+
result << ",\n\t:headers => #{self.headers.inspect}"
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
result
|
|
165
|
+
end
|
|
166
|
+
|
|
84
167
|
def cache_key
|
|
85
168
|
Digest::SHA1.hexdigest(url)
|
|
86
169
|
end
|
|
87
|
-
|
|
170
|
+
|
|
88
171
|
def self.run(url, params)
|
|
89
172
|
r = new(url, params)
|
|
90
173
|
Typhoeus::Hydra.hydra.queue r
|
|
91
174
|
Typhoeus::Hydra.hydra.run
|
|
92
175
|
r.response
|
|
93
176
|
end
|
|
94
|
-
|
|
95
|
-
def self.get(url, params)
|
|
177
|
+
|
|
178
|
+
def self.get(url, params = {})
|
|
96
179
|
run(url, params.merge(:method => :get))
|
|
97
180
|
end
|
|
98
|
-
|
|
99
|
-
def self.post(url, params)
|
|
181
|
+
|
|
182
|
+
def self.post(url, params = {})
|
|
100
183
|
run(url, params.merge(:method => :post))
|
|
101
184
|
end
|
|
102
|
-
|
|
103
|
-
def self.put(url, params)
|
|
185
|
+
|
|
186
|
+
def self.put(url, params = {})
|
|
104
187
|
run(url, params.merge(:method => :put))
|
|
105
188
|
end
|
|
106
|
-
|
|
107
|
-
def self.delete(url, params)
|
|
189
|
+
|
|
190
|
+
def self.delete(url, params = {})
|
|
108
191
|
run(url, params.merge(:method => :delete))
|
|
109
192
|
end
|
|
193
|
+
|
|
194
|
+
def self.head(url, params = {})
|
|
195
|
+
run(url, params.merge(:method => :head))
|
|
196
|
+
end
|
|
110
197
|
end
|
|
111
|
-
end
|
|
198
|
+
end
|
data/lib/typhoeus/response.rb
CHANGED
|
@@ -1,19 +1,76 @@
|
|
|
1
1
|
module Typhoeus
|
|
2
2
|
class Response
|
|
3
|
-
attr_accessor :request
|
|
3
|
+
attr_accessor :request, :mock
|
|
4
4
|
attr_reader :code, :headers, :body, :time,
|
|
5
5
|
:requested_url, :requested_remote_method,
|
|
6
|
-
:requested_http_method, :start_time
|
|
7
|
-
|
|
6
|
+
:requested_http_method, :start_time,
|
|
7
|
+
:effective_url
|
|
8
|
+
attr_writer :headers_hash
|
|
9
|
+
|
|
8
10
|
def initialize(params = {})
|
|
9
11
|
@code = params[:code]
|
|
10
|
-
@
|
|
12
|
+
@status_message = params[:status_message]
|
|
13
|
+
@http_version = params[:http_version]
|
|
14
|
+
@headers = params[:headers] || ''
|
|
11
15
|
@body = params[:body]
|
|
12
16
|
@time = params[:time]
|
|
13
17
|
@requested_url = params[:requested_url]
|
|
14
18
|
@requested_http_method = params[:requested_http_method]
|
|
15
19
|
@start_time = params[:start_time]
|
|
16
20
|
@request = params[:request]
|
|
21
|
+
@effective_url = params[:effective_url]
|
|
22
|
+
@mock = params[:mock] || false # default
|
|
23
|
+
@headers_hash = NormalizedHeaderHash.new(params[:headers_hash]) if params[:headers_hash]
|
|
17
24
|
end
|
|
25
|
+
|
|
26
|
+
# Returns true if this is a mock response.
|
|
27
|
+
def mock?
|
|
28
|
+
@mock
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def headers_hash
|
|
32
|
+
@headers_hash ||= begin
|
|
33
|
+
headers.split("\n").map {|o| o.strip}.inject(Typhoeus::NormalizedHeaderHash.new) do |hash, o|
|
|
34
|
+
if o.empty? || o =~ /^HTTP\/[\d\.]+/
|
|
35
|
+
hash
|
|
36
|
+
else
|
|
37
|
+
i = o.index(":") || o.size
|
|
38
|
+
key = o.slice(0, i)
|
|
39
|
+
value = o.slice(i + 1, o.size)
|
|
40
|
+
value = value.strip unless value.nil?
|
|
41
|
+
if hash.has_key? key
|
|
42
|
+
hash[key] = [hash[key], value].flatten
|
|
43
|
+
else
|
|
44
|
+
hash[key] = value
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
hash
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def status_message
|
|
54
|
+
# http://rubular.com/r/eAr1oVYsVa
|
|
55
|
+
@status_message ||= first_header_line ? first_header_line[/\d{3} (.*)$/, 1].chomp : nil
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def http_version
|
|
59
|
+
@http_version ||= first_header_line ? first_header_line[/HTTP\/(\S+)/, 1] : nil
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def success?
|
|
63
|
+
@code >= 200 && @code < 300
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def modified?
|
|
67
|
+
@code != 304
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def first_header_line
|
|
73
|
+
@first_header_line ||= headers.split("\n").first
|
|
74
|
+
end
|
|
18
75
|
end
|
|
19
76
|
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
|
data/lib/typhoeus.rb
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
$LOAD_PATH.unshift(File.dirname(__FILE__)) unless $LOAD_PATH.include?(File.dirname(__FILE__))
|
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__) + "/../ext")
|
|
2
3
|
|
|
3
|
-
require 'rack/utils'
|
|
4
4
|
require 'digest/sha2'
|
|
5
|
+
require 'typhoeus/utils'
|
|
6
|
+
require 'typhoeus/normalized_header_hash'
|
|
5
7
|
require 'typhoeus/easy'
|
|
6
8
|
require 'typhoeus/multi'
|
|
7
9
|
require 'typhoeus/native'
|
|
@@ -12,25 +14,26 @@ require 'typhoeus/remote_proxy_object'
|
|
|
12
14
|
require 'typhoeus/response'
|
|
13
15
|
require 'typhoeus/request'
|
|
14
16
|
require 'typhoeus/hydra'
|
|
17
|
+
require 'typhoeus/hydra_mock'
|
|
15
18
|
|
|
16
19
|
module Typhoeus
|
|
17
|
-
VERSION =
|
|
18
|
-
|
|
20
|
+
VERSION = File.read(File.dirname(__FILE__) + "/../VERSION").chomp
|
|
21
|
+
|
|
19
22
|
def self.easy_object_pool
|
|
20
23
|
@easy_objects ||= []
|
|
21
24
|
end
|
|
22
|
-
|
|
25
|
+
|
|
23
26
|
def self.init_easy_object_pool
|
|
24
27
|
20.times do
|
|
25
28
|
easy_object_pool << Typhoeus::Easy.new
|
|
26
29
|
end
|
|
27
30
|
end
|
|
28
|
-
|
|
31
|
+
|
|
29
32
|
def self.release_easy_object(easy)
|
|
30
33
|
easy.reset
|
|
31
34
|
easy_object_pool << easy
|
|
32
35
|
end
|
|
33
|
-
|
|
36
|
+
|
|
34
37
|
def self.get_easy_object
|
|
35
38
|
if easy_object_pool.empty?
|
|
36
39
|
Typhoeus::Easy.new
|
|
@@ -38,12 +41,12 @@ module Typhoeus
|
|
|
38
41
|
easy_object_pool.pop
|
|
39
42
|
end
|
|
40
43
|
end
|
|
41
|
-
|
|
44
|
+
|
|
42
45
|
def self.add_easy_request(easy_object)
|
|
43
46
|
Thread.current[:curl_multi] ||= Typhoeus::Multi.new
|
|
44
47
|
Thread.current[:curl_multi].add(easy_object)
|
|
45
48
|
end
|
|
46
|
-
|
|
49
|
+
|
|
47
50
|
def self.perform_easy_requests
|
|
48
51
|
multi = Thread.current[:curl_multi]
|
|
49
52
|
start_time = Time.now
|
|
@@ -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>
|
data/spec/servers/app.rb
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
1
2
|
require 'rubygems'
|
|
2
3
|
require 'sinatra'
|
|
3
4
|
require 'json'
|
|
5
|
+
require 'zlib'
|
|
4
6
|
|
|
5
7
|
@@fail_count = 0
|
|
6
8
|
get '/fail/:number' do
|
|
@@ -16,11 +18,56 @@ get '/fail_forever' do
|
|
|
16
18
|
error 500, "oh noes!"
|
|
17
19
|
end
|
|
18
20
|
|
|
21
|
+
get '/redirect' do
|
|
22
|
+
redirect '/'
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
get '/bad_redirect' do
|
|
26
|
+
redirect '/bad_redirect'
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
get '/auth_basic/:username/:password' do
|
|
30
|
+
@auth ||= Rack::Auth::Basic::Request.new(request.env)
|
|
31
|
+
# Check that we've got a basic auth, and that it's credentials match the ones
|
|
32
|
+
# provided in the request
|
|
33
|
+
if @auth.provided? && @auth.basic? && @auth.credentials == [ params[:username], params[:password] ]
|
|
34
|
+
# auth is valid - confirm it
|
|
35
|
+
true
|
|
36
|
+
else
|
|
37
|
+
# invalid auth - request the authentication
|
|
38
|
+
response['WWW-Authenticate'] = %(Basic realm="Testing HTTP Auth")
|
|
39
|
+
throw(:halt, [401, "Not authorized\n"])
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
get '/auth_ntlm' do
|
|
44
|
+
# we're just checking for the existence if NTLM auth header here. It's validation
|
|
45
|
+
# is too troublesome and really doesn't bother is much, it's up to libcurl to make
|
|
46
|
+
# it valid
|
|
47
|
+
response['WWW-Authenticate'] = 'NTLM'
|
|
48
|
+
is_ntlm_auth = /^NTLM/ =~ request.env['HTTP_AUTHORIZATION']
|
|
49
|
+
true if is_ntlm_auth
|
|
50
|
+
throw(:halt, [401, "Not authorized\n"]) if !is_ntlm_auth
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
get '/gzipped' do
|
|
54
|
+
req_env = request.env.to_json
|
|
55
|
+
z = Zlib::Deflate.new
|
|
56
|
+
gzipped_env = z.deflate(req_env, Zlib::FINISH)
|
|
57
|
+
z.close
|
|
58
|
+
response['Content-Encoding'] = 'gzip'
|
|
59
|
+
gzipped_env
|
|
60
|
+
end
|
|
61
|
+
|
|
19
62
|
get '/**' do
|
|
20
63
|
sleep params["delay"].to_i if params.has_key?("delay")
|
|
21
64
|
request.env.merge!(:body => request.body.read).to_json
|
|
22
65
|
end
|
|
23
66
|
|
|
67
|
+
head '/**' do
|
|
68
|
+
sleep params["delay"].to_i if params.has_key?("delay")
|
|
69
|
+
end
|
|
70
|
+
|
|
24
71
|
put '/**' do
|
|
25
72
|
puts request.inspect
|
|
26
73
|
request.env.merge!(:body => request.body.read).to_json
|
|
@@ -34,4 +81,4 @@ end
|
|
|
34
81
|
delete '/**' do
|
|
35
82
|
puts request.inspect
|
|
36
83
|
request.env.merge!(:body => request.body.read).to_json
|
|
37
|
-
end
|
|
84
|
+
end
|
data/spec/spec_helper.rb
CHANGED