resourceful 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +21 -0
- data/README.markdown +86 -0
- data/Rakefile +89 -0
- data/lib/resourceful.rb +28 -0
- data/lib/resourceful/authentication_manager.rb +85 -0
- data/lib/resourceful/cache_manager.rb +160 -0
- data/lib/resourceful/header.rb +31 -0
- data/lib/resourceful/http_accessor.rb +79 -0
- data/lib/resourceful/net_http_adapter.rb +57 -0
- data/lib/resourceful/options_interpreter.rb +78 -0
- data/lib/resourceful/request.rb +64 -0
- data/lib/resourceful/resource.rb +216 -0
- data/lib/resourceful/response.rb +100 -0
- data/lib/resourceful/stubbed_resource_proxy.rb +47 -0
- data/lib/resourceful/util.rb +6 -0
- data/lib/resourceful/version.rb +1 -0
- data/spec/acceptance_shared_specs.rb +49 -0
- data/spec/acceptance_spec.rb +344 -0
- data/spec/resourceful/authentication_manager_spec.rb +204 -0
- data/spec/resourceful/cache_manager_spec.rb +202 -0
- data/spec/resourceful/header_spec.rb +38 -0
- data/spec/resourceful/http_accessor_spec.rb +120 -0
- data/spec/resourceful/net_http_adapter_spec.rb +90 -0
- data/spec/resourceful/options_interpreter_spec.rb +94 -0
- data/spec/resourceful/request_spec.rb +261 -0
- data/spec/resourceful/resource_spec.rb +481 -0
- data/spec/resourceful/response_spec.rb +199 -0
- data/spec/resourceful/stubbed_resource_proxy_spec.rb +58 -0
- data/spec/simple_http_server_shared_spec.rb +151 -0
- data/spec/simple_http_server_shared_spec_spec.rb +195 -0
- data/spec/spec_helper.rb +12 -0
- metadata +129 -0
@@ -0,0 +1,199 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require Pathname(__FILE__).dirname + '../spec_helper'
|
3
|
+
|
4
|
+
require 'resourceful/response'
|
5
|
+
|
6
|
+
describe Resourceful::Response do
|
7
|
+
before do
|
8
|
+
@net_http = mock('net_http')
|
9
|
+
Net::HTTP::Get.stub!(:new).and_return(@net_http)
|
10
|
+
@uri = 'http://www.example.com'
|
11
|
+
|
12
|
+
@response = Resourceful::Response.new(@uri, 0, {}, "")
|
13
|
+
end
|
14
|
+
|
15
|
+
describe 'init' do
|
16
|
+
|
17
|
+
it 'should be instantiatable' do
|
18
|
+
@response.should be_instance_of(Resourceful::Response)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should take a [uri, code, header, body] array' do
|
22
|
+
r = Resourceful::Response.new(@uri, 200, {}, "")
|
23
|
+
r.code.should == 200
|
24
|
+
r.header.should == {}
|
25
|
+
r.body.should == ""
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should have a code' do
|
31
|
+
@response.should respond_to(:code)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should have a header' do
|
35
|
+
@response.should respond_to(:header)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should have header aliased as headers' do
|
39
|
+
@response.should respond_to(:headers)
|
40
|
+
@response.headers.should == @response.header
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "#is_success?" do
|
44
|
+
it 'should be true for 200' do
|
45
|
+
Resourceful::Response.new(@uri, 200, {}, "").is_success?.should == true
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should be true for any 2xx' do
|
49
|
+
Resourceful::Response.new(@uri, 299, {}, "").is_success?.should == true
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should not be true for 300' do
|
53
|
+
Resourceful::Response.new(@uri, 300, {}, "").is_success?.should == false
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should not be true for 199' do
|
57
|
+
Resourceful::Response.new(@uri, 199, {}, "").is_success?.should == false
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should know if it is a redirect' do
|
62
|
+
Resourceful::Response.new(@uri, 301, {}, "").is_redirect?.should == true
|
63
|
+
Resourceful::Response.new(@uri, 302, {}, "").is_redirect?.should == true
|
64
|
+
Resourceful::Response.new(@uri, 303, {}, "").is_redirect?.should == true
|
65
|
+
Resourceful::Response.new(@uri, 307, {}, "").is_redirect?.should == true
|
66
|
+
|
67
|
+
#aliased as was_redirect?
|
68
|
+
Resourceful::Response.new(@uri, 301, {}, "").was_redirect?.should == true
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should know if it is a permanent redirect' do
|
72
|
+
Resourceful::Response.new(@uri, 301, {}, "").is_permanent_redirect?.should == true
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'should know if it is a temporary redirect' do
|
76
|
+
Resourceful::Response.new(@uri, 303, {}, "").is_temporary_redirect?.should == true
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'should know if its authoritative' do
|
80
|
+
@response.should respond_to(:authoritative?)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'should allow authoritative to be set' do
|
84
|
+
@response.authoritative = true
|
85
|
+
@response.authoritative?.should be_true
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'should know if it is authorized' do
|
89
|
+
@response.should respond_to(:is_not_authorized?)
|
90
|
+
Resourceful::Response.new(@uri, 200, {}, "").is_not_authorized?.should == false
|
91
|
+
Resourceful::Response.new(@uri, 401, {}, "").is_not_authorized?.should == true
|
92
|
+
end
|
93
|
+
|
94
|
+
describe 'caching and expiration' do
|
95
|
+
before do
|
96
|
+
Time.stub!(:now).and_return(Time.utc(2008,5,15,18,0,1), Time.utc(2008,5,15,20,0,0))
|
97
|
+
|
98
|
+
@response = Resourceful::Response.new(@uri, 0, {'Date' => ['Thu, 15 May 2008 18:00:00 GMT']}, "")
|
99
|
+
@response.request_time = Time.utc(2008,5,15,17,59,59)
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'should know if its #stale?' do
|
103
|
+
@response.should respond_to(:stale?)
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'should be stale if it is expired' do
|
107
|
+
@response.should_receive(:expired?).and_return(true)
|
108
|
+
@response.should be_stale
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'should know if its #expired?' do
|
112
|
+
@response.should respond_to(:expired?)
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'should be expired if Now is after the "Expire" header' do
|
116
|
+
Time.stub!(:now).and_return(Time.utc(2008,5,23,18,0))
|
117
|
+
@response.header['Expire'] = [(Time.now - 60*60).httpdate]
|
118
|
+
|
119
|
+
@response.should be_expired
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'should have a #current_age' do
|
123
|
+
@response.should respond_to(:current_age)
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'should calculate the #current_age' do
|
127
|
+
@response.current_age.should == (2 * 60 * 60 + 2)
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'should know if its #cachable?' do
|
131
|
+
@response.should respond_to(:cachable?)
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'should normally be cachable' do
|
135
|
+
@response.cachable?.should be_true
|
136
|
+
end
|
137
|
+
|
138
|
+
def response_with_header(header = {})
|
139
|
+
Resourceful::Response.new(@uri, 200, header, "")
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'should not be cachable if the vary header has "*"' do
|
143
|
+
r = response_with_header('Vary' => ['*'])
|
144
|
+
r.cachable?.should be_false
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'should not be cachable if the Cache-Control header is set to no-store' do
|
148
|
+
r = response_with_header('Cache-Control' => ['no-store'])
|
149
|
+
r.cachable?.should be_false
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'should be stale if the Cache-Control header is set to must-revalidate' do
|
153
|
+
r = response_with_header('Cache-Control' => ['must-revalidate'])
|
154
|
+
r.should be_stale
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'should be stale if the Cache-Control header is set to no-cache' do
|
158
|
+
r = response_with_header('Cache-Control' => ['no-cache'])
|
159
|
+
r.should be_stale
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
|
164
|
+
describe '#body' do
|
165
|
+
it 'should have a body method' do
|
166
|
+
@response.should respond_to(:body)
|
167
|
+
end
|
168
|
+
|
169
|
+
require 'zlib'
|
170
|
+
['gzip', ' gzip', ' gzip ', 'GZIP', 'gzIP'].each do |gzip|
|
171
|
+
it "ungzip the body if content-encoding header field is #{gzip}" do
|
172
|
+
compressed_date = StringIO.new.tap do |out|
|
173
|
+
Zlib::GzipWriter.new(out).tap do |zout|
|
174
|
+
zout << "This is a test"
|
175
|
+
zout.close
|
176
|
+
end
|
177
|
+
end.string
|
178
|
+
|
179
|
+
@response = Resourceful::Response.new(@uri, 0, {'Content-Encoding' => [gzip]}, compressed_date)
|
180
|
+
|
181
|
+
@response.body.should == "This is a test"
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
it 'should leave body unmolested if Content-Encoding missing' do
|
186
|
+
@response = Resourceful::Response.new(@uri, 0, {}, "This is a test")
|
187
|
+
@response.body.should == "This is a test"
|
188
|
+
end
|
189
|
+
|
190
|
+
it 'should raise error if Content-Encoding is not supported' do
|
191
|
+
@response = Resourceful::Response.new(@uri, 0, {'Content-Encoding' => ['broken-identity']}, "This is a test")
|
192
|
+
lambda {
|
193
|
+
@response.body
|
194
|
+
}.should raise_error(Resourceful::UnsupportedContentCoding)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
end
|
199
|
+
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require Pathname(__FILE__).dirname + '../spec_helper'
|
3
|
+
|
4
|
+
require 'resourceful/stubbed_resource_proxy'
|
5
|
+
|
6
|
+
describe Resourceful::StubbedResourceProxy, "init" do
|
7
|
+
it 'should require real resource' do
|
8
|
+
lambda{
|
9
|
+
Resourceful::StubbedResourceProxy.new
|
10
|
+
}.should raise_error(ArgumentError)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should require canned responses hash' do
|
14
|
+
lambda{
|
15
|
+
Resourceful::StubbedResourceProxy.new(stub('resource'))
|
16
|
+
}.should raise_error(ArgumentError)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should be creatable with a Resource and canned responses' do
|
20
|
+
Resourceful::StubbedResourceProxy.new(stub('resource'), {})
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe Resourceful::StubbedResourceProxy do
|
25
|
+
before do
|
26
|
+
@resource = stub('resource', :effective_uri => "http://test.invalid/foo")
|
27
|
+
@stubbed_resource = Resourceful::StubbedResourceProxy.
|
28
|
+
new(@resource, [{:mime_type => 'application/xml',
|
29
|
+
:body => '<thing>1</thing>'}])
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should return canned response body for matching requests' do
|
33
|
+
resp = @stubbed_resource.get
|
34
|
+
resp.body.should == '<thing>1</thing>'
|
35
|
+
resp['content-type'].should == 'application/xml'
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should pass #get() through to base resource if no matching canned response is defined' do
|
39
|
+
@resource.should_receive(:get)
|
40
|
+
@stubbed_resource.get(:accept => 'application/unknown')
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should pass #post() through to base resource if no canned response is defined' do
|
44
|
+
@resource.should_receive(:post)
|
45
|
+
@stubbed_resource.post
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should pass #put() through to base resource if no canned response is defined' do
|
49
|
+
@resource.should_receive(:put)
|
50
|
+
@stubbed_resource.put
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should pass #effective_uri() through to base resource if no canned response is defined' do
|
54
|
+
@resource.should_receive(:effective_uri)
|
55
|
+
@stubbed_resource.effective_uri
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
# this sets up a very simple http server using thin to be used in specs.
|
4
|
+
SimpleGet = lambda do |env|
|
5
|
+
body = ["Hello, world!"]
|
6
|
+
[ 200, {'Content-Type' => 'text/plain', 'Content-Length' => body.join.size.to_s}, body ]
|
7
|
+
end unless defined? SimpleGet
|
8
|
+
|
9
|
+
SimplePost = lambda do |env|
|
10
|
+
body = [env['rack.input'].string]
|
11
|
+
[ 201, {'Content-Type' => 'text/plain', 'Content-Length' => body.join.size.to_s}, body ]
|
12
|
+
end unless defined? SimplePost
|
13
|
+
|
14
|
+
SimplePut = lambda do |env|
|
15
|
+
body = [env['rack.input'].string]
|
16
|
+
[ 200, {'Content-Type' => 'text/plain', 'Content-Length' => body.join.size.to_s}, body ]
|
17
|
+
end unless defined? SimplePut
|
18
|
+
|
19
|
+
SimpleDel = lambda do |env|
|
20
|
+
body = ["KABOOM!"]
|
21
|
+
[ 200, {'Content-Type' => 'text/plain', 'Content-Length' => body.join.size.to_s}, body ]
|
22
|
+
end unless defined? SimpleDel
|
23
|
+
|
24
|
+
# has the method used in the body of the response
|
25
|
+
MethodResponder = lambda do |env|
|
26
|
+
body = [env['REQUEST_METHOD']]
|
27
|
+
[ 200, {'Content-Type' => 'text/plain', 'Content-Length' => body.join.size.to_s}, body ]
|
28
|
+
end unless defined? MethodResponder
|
29
|
+
|
30
|
+
# has a response code of whatever it was given in the url /code/{123}
|
31
|
+
CodeResponder = lambda do |env|
|
32
|
+
code = env['PATH_INFO'] =~ /([\d]+)/ ? Integer($1) : 404
|
33
|
+
body = [code.to_s]
|
34
|
+
|
35
|
+
[ code, {'Content-Type' => 'text/plain', 'Content-Length' => body.join.size.to_s}, body ]
|
36
|
+
end unless defined? CodeResponder
|
37
|
+
|
38
|
+
# YAML-parses the quesy string (expected hash) and sets the header to that
|
39
|
+
HeaderResponder = lambda do |env|
|
40
|
+
header = YAML.load(URI.unescape(env['QUERY_STRING']))
|
41
|
+
body = [header.inspect]
|
42
|
+
|
43
|
+
header.merge!({
|
44
|
+
'Content-Type' => 'text/plain',
|
45
|
+
'Content-Length' => body.join.size.to_s
|
46
|
+
})
|
47
|
+
|
48
|
+
[ 200, header, body ]
|
49
|
+
end unless defined? HeaderResponder
|
50
|
+
|
51
|
+
# redirect. /redirect/{301|302}?{url}
|
52
|
+
Redirector = lambda do |env|
|
53
|
+
code = env['PATH_INFO'] =~ /([\d]+)/ ? Integer($1) : 404
|
54
|
+
location = env['QUERY_STRING']
|
55
|
+
body = [location]
|
56
|
+
|
57
|
+
[ code, {'Content-Type' => 'text/plain', 'Location' => location, 'Content-Length' => body.join.size.to_s}, body ]
|
58
|
+
end unless defined? Redirector
|
59
|
+
|
60
|
+
# Returns 304 if 'If-Modified-Since' is after given mod time
|
61
|
+
ModifiedResponder = lambda do |env|
|
62
|
+
modtime = Time.httpdate(URI.unescape(env['QUERY_STRING']))
|
63
|
+
|
64
|
+
code = 200
|
65
|
+
if env['HTTP_IF_MODIFIED_SINCE']
|
66
|
+
code = 304
|
67
|
+
end
|
68
|
+
body = [modtime.to_s]
|
69
|
+
|
70
|
+
header = {'Content-Type' => 'text/plain',
|
71
|
+
'Content-Length' => body.join.size.to_s,
|
72
|
+
'Last-Modified' => modtime.httpdate,
|
73
|
+
'Cache-Control' => 'must-revalidate'}
|
74
|
+
|
75
|
+
[ code, header, body ]
|
76
|
+
end unless defined? ModifiedResponder
|
77
|
+
|
78
|
+
require 'rubygems'
|
79
|
+
require 'httpauth'
|
80
|
+
AuthorizationResponder = lambda do |env|
|
81
|
+
authtype = env['QUERY_STRING']
|
82
|
+
header = {}
|
83
|
+
if auth_string = env['HTTP_AUTHORIZATION']
|
84
|
+
if authtype == "basic" &&
|
85
|
+
['admin', 'secret'] == HTTPAuth::Basic.unpack_authorization(auth_string)
|
86
|
+
code = 200
|
87
|
+
body = ["Authorized"]
|
88
|
+
elsif authtype == "digest" #&&
|
89
|
+
credentials = HTTPAuth::Digest::Credentials.from_header(auth_string) &&
|
90
|
+
credentials &&
|
91
|
+
credentials.validate(:password => 'secret', :method => 'GET')
|
92
|
+
code = 200
|
93
|
+
body = ["Authorized"]
|
94
|
+
else
|
95
|
+
code = 401
|
96
|
+
body = ["Not Authorized"]
|
97
|
+
end
|
98
|
+
else
|
99
|
+
code = 401
|
100
|
+
body = ["Not Authorized"]
|
101
|
+
if authtype == "basic"
|
102
|
+
header = {'WWW-Authenticate' => HTTPAuth::Basic.pack_challenge('Test Auth')}
|
103
|
+
elsif authtype == "digest"
|
104
|
+
chal = HTTPAuth::Digest::Credentials.new(:realm => 'Test Auth', :qop => 'auth')
|
105
|
+
header = {'WWW-Authenticate' => chal.to_header}
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
[ code, header.merge({'Content-Type' => 'text/plain', 'Content-Length' => body.join.size.to_s}), body ]
|
110
|
+
end unless defined? AuthorizationResponder
|
111
|
+
|
112
|
+
describe 'simple http server', :shared => true do
|
113
|
+
before(:all) do
|
114
|
+
#setup a thin http server we can connect to
|
115
|
+
require 'thin'
|
116
|
+
require 'rack'
|
117
|
+
require 'rack/lobster'
|
118
|
+
|
119
|
+
app = Rack::Builder.new do |env|
|
120
|
+
use Rack::ShowExceptions
|
121
|
+
|
122
|
+
map( '/get' ){ run SimpleGet }
|
123
|
+
map( '/post' ){ run SimplePost }
|
124
|
+
map( '/put' ){ run SimplePut }
|
125
|
+
map( '/delete' ){ run SimpleDel }
|
126
|
+
|
127
|
+
map( '/method' ){ run MethodResponder }
|
128
|
+
map( '/code' ){ run CodeResponder }
|
129
|
+
map( '/redirect' ){ run Redirector }
|
130
|
+
map( '/header' ){ run HeaderResponder }
|
131
|
+
map( '/modified' ){ run ModifiedResponder }
|
132
|
+
map( '/auth' ){ run AuthorizationResponder }
|
133
|
+
end
|
134
|
+
|
135
|
+
#spawn the server in a separate thread
|
136
|
+
@httpd = Thread.new do
|
137
|
+
Thin::Logging.silent = true
|
138
|
+
#Thin::Logging.debug = true
|
139
|
+
Thin::Server.start(app)
|
140
|
+
end
|
141
|
+
#give the server a chance to initialize
|
142
|
+
sleep 0.05
|
143
|
+
end
|
144
|
+
|
145
|
+
after(:all) do
|
146
|
+
# kill the server thread
|
147
|
+
@httpd.exit
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
end
|
@@ -0,0 +1,195 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require Pathname(__FILE__).dirname + 'spec_helper'
|
3
|
+
require 'rubygems'
|
4
|
+
require 'addressable/uri'
|
5
|
+
|
6
|
+
require 'resourceful/net_http_adapter'
|
7
|
+
|
8
|
+
describe 'http server' do
|
9
|
+
it_should_behave_like 'simple http server'
|
10
|
+
|
11
|
+
it 'should have a response code of 200 if the path is /get' do
|
12
|
+
Resourceful::NetHttpAdapter.make_request(:get, 'http://localhost:3000/get')[0].should == 200
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should reply with the posted document in the body if the path is /post' do
|
16
|
+
resp = Resourceful::NetHttpAdapter.make_request(:get, 'http://localhost:3000/post', 'Hello from POST!')
|
17
|
+
resp[2].should == 'Hello from POST!'
|
18
|
+
resp[0].should == 201
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should reply with the puted document in the body if the path is /put' do
|
22
|
+
resp = Resourceful::NetHttpAdapter.make_request(:put, 'http://localhost:3000/put', 'Hello from PUT!')
|
23
|
+
resp[2].should == 'Hello from PUT!'
|
24
|
+
resp[0].should == 200
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should reply with "KABOOM!" in the body if the path is /delete' do
|
28
|
+
resp = Resourceful::NetHttpAdapter.make_request(:delete, 'http://localhost:3000/delete')
|
29
|
+
resp[2].should == 'KABOOM!'
|
30
|
+
resp[0].should == 200
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should have a response code of whatever the path is' do
|
34
|
+
Resourceful::NetHttpAdapter.make_request(:get, 'http://localhost:3000/code/304')[0].should == 304
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should redirect to a given url' do
|
38
|
+
resp = Resourceful::NetHttpAdapter.make_request(:get, 'http://localhost:3000/redirect/301?http://localhost:3000/get')
|
39
|
+
|
40
|
+
resp[0].should == 301
|
41
|
+
resp[1]['Location'].should == ['http://localhost:3000/get']
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should respond with the request method in the body' do
|
45
|
+
resp = Resourceful::NetHttpAdapter.make_request(:delete, 'http://localhost:3000/method')
|
46
|
+
|
47
|
+
resp[0].should == 200
|
48
|
+
resp[2].should == "DELETE"
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should respond with the header set from the query string' do
|
52
|
+
uri = URI.escape('http://localhost:3000/header?{Foo: "bar"}')
|
53
|
+
resp = Resourceful::NetHttpAdapter.make_request(:get, uri)
|
54
|
+
|
55
|
+
resp[1].should have_key('Foo')
|
56
|
+
resp[1]['Foo'].should == ['bar']
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should parse escaped uris properly' do
|
60
|
+
uri = URI.escape("http://localhost:3000/header?{Expire: \"#{Time.now.httpdate}\"}")
|
61
|
+
|
62
|
+
resp = Resourceful::NetHttpAdapter.make_request(:get, uri)
|
63
|
+
|
64
|
+
resp[1].should have_key('Expire')
|
65
|
+
resp[1]['Expire'].first.should_not =~ /%/
|
66
|
+
end
|
67
|
+
|
68
|
+
describe '/modified' do
|
69
|
+
it 'should be 200 if no I-M-S header' do
|
70
|
+
uri = URI.escape("http://localhost:3000/modified?#{(Time.now + 3600).httpdate}")
|
71
|
+
|
72
|
+
resp = Resourceful::NetHttpAdapter.make_request(:get, uri)
|
73
|
+
|
74
|
+
resp[0].should == 200
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'should be 304 if I-M-S header is set' do
|
78
|
+
now = Time.utc(2008,5,29,12,00)
|
79
|
+
uri = URI.escape("http://localhost:3000/modified?#{(now + 3600).httpdate}")
|
80
|
+
|
81
|
+
resp = Resourceful::NetHttpAdapter.make_request(:get, uri, nil, {'If-Modified-Since' => now.httpdate})
|
82
|
+
|
83
|
+
resp[0].should == 304
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
describe '/auth' do
|
89
|
+
before do
|
90
|
+
@uri = "http://localhost:3000/auth?basic"
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'should return a 401 if no auth info is provided' do
|
94
|
+
resp = Resourceful::NetHttpAdapter.make_request(:get, @uri)
|
95
|
+
resp[0].should == 401
|
96
|
+
end
|
97
|
+
|
98
|
+
describe 'basic' do
|
99
|
+
before do
|
100
|
+
@uri = "http://localhost:3000/auth?basic"
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'should return a 401 if no auth info is provided' do
|
104
|
+
resp = Resourceful::NetHttpAdapter.make_request(:get, @uri)
|
105
|
+
resp[0].should == 401
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'should provide a WWW-Authenticate header when 401' do
|
109
|
+
resp = Resourceful::NetHttpAdapter.make_request(:get, @uri)
|
110
|
+
header = resp[1]
|
111
|
+
header.should have_key('WWW-Authenticate')
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'should set the scheme to "Basic"' do
|
115
|
+
resp = Resourceful::NetHttpAdapter.make_request(:get, @uri)
|
116
|
+
auth = resp[1]['WWW-Authenticate'].first
|
117
|
+
auth.should =~ /^Basic/
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'should set the realm to "Test Auth"' do
|
121
|
+
resp = Resourceful::NetHttpAdapter.make_request(:get, @uri)
|
122
|
+
auth = resp[1]['WWW-Authenticate'].first
|
123
|
+
auth.should =~ /realm="Test Auth"/
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'should authorize on u/p:admin/secret' do
|
127
|
+
creds = HTTPAuth::Basic.pack_authorization('admin', 'secret')
|
128
|
+
header = {'Authorization' => creds}
|
129
|
+
resp = Resourceful::NetHttpAdapter.make_request(:get, @uri, nil, header)
|
130
|
+
resp[0].should == 200
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'should authorize if u/p is incorrect' do
|
134
|
+
creds = HTTPAuth::Basic.pack_authorization('admin', 'not secret')
|
135
|
+
header = {'Authorization' => creds}
|
136
|
+
resp = Resourceful::NetHttpAdapter.make_request(:get, @uri, nil, header)
|
137
|
+
resp[0].should == 401
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
describe 'digest' do
|
143
|
+
before do
|
144
|
+
@uri = "http://localhost:3000/auth?digest"
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'should return a 401 if no auth info is provided' do
|
148
|
+
resp = Resourceful::NetHttpAdapter.make_request(:get, @uri)
|
149
|
+
resp[0].should == 401
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'should provide a WWW-Authenticate header when 401' do
|
153
|
+
resp = Resourceful::NetHttpAdapter.make_request(:get, @uri)
|
154
|
+
header = resp[1]
|
155
|
+
header.should have_key('WWW-Authenticate')
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'should set the scheme to "Digest"' do
|
159
|
+
resp = Resourceful::NetHttpAdapter.make_request(:get, @uri)
|
160
|
+
auth = resp[1]['WWW-Authenticate'].first
|
161
|
+
auth.should =~ /^Digest/
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'should set the realm to "Test Auth"' do
|
165
|
+
resp = Resourceful::NetHttpAdapter.make_request(:get, @uri)
|
166
|
+
auth = resp[1]['WWW-Authenticate'].first
|
167
|
+
auth.should =~ /realm="Test Auth"/
|
168
|
+
end
|
169
|
+
|
170
|
+
def challenge
|
171
|
+
resp = Resourceful::NetHttpAdapter.make_request(:get, @uri)
|
172
|
+
HTTPAuth::Digest::Challenge.from_header(resp[1]['WWW-Authenticate'].first)
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'should authorize on u/p:admin/secret' do
|
176
|
+
creds = HTTPAuth::Digest::Credentials.from_challenge(challenge, :username => 'admin', :password => 'secret', :uri => @uri)
|
177
|
+
header = {'Authorization' => creds.to_header}
|
178
|
+
resp = Resourceful::NetHttpAdapter.make_request(:get, @uri, nil, header)
|
179
|
+
resp[0].should == 200
|
180
|
+
end
|
181
|
+
|
182
|
+
it 'should not authorize if u/p is incorrect' do
|
183
|
+
pending
|
184
|
+
creds = HTTPAuth::Digest::Credentials.from_challenge(challenge, :username => 'admin', :password => 'not secret', :uri => @uri)
|
185
|
+
header = {'Authorization' => creds.to_header}
|
186
|
+
resp = Resourceful::NetHttpAdapter.make_request(:get, @uri, nil, header)
|
187
|
+
resp[0].should == 401
|
188
|
+
end
|
189
|
+
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|
193
|
+
|
194
|
+
end
|
195
|
+
|