resourceful 0.2
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/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
|
+
|