resourceful 0.3.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest +24 -28
- data/Rakefile +44 -14
- data/lib/resourceful.rb +11 -21
- data/lib/resourceful/authentication_manager.rb +3 -2
- data/lib/resourceful/cache_manager.rb +58 -1
- data/lib/resourceful/exceptions.rb +34 -0
- data/lib/resourceful/header.rb +95 -0
- data/lib/resourceful/http_accessor.rb +0 -2
- data/lib/resourceful/memcache_cache_manager.rb +3 -13
- data/lib/resourceful/net_http_adapter.rb +15 -5
- data/lib/resourceful/request.rb +180 -18
- data/lib/resourceful/resource.rb +38 -141
- data/lib/resourceful/response.rb +142 -95
- data/resourceful.gemspec +9 -7
- data/spec/acceptance/authorization_spec.rb +16 -0
- data/spec/acceptance/caching_spec.rb +192 -0
- data/spec/acceptance/header_spec.rb +24 -0
- data/spec/acceptance/redirecting_spec.rb +12 -0
- data/spec/acceptance/resource_spec.rb +84 -0
- data/spec/acceptance_shared_specs.rb +12 -17
- data/spec/{acceptance_spec.rb → old_acceptance_specs.rb} +27 -57
- data/spec/simple_sinatra_server.rb +74 -0
- data/spec/simple_sinatra_server_spec.rb +98 -0
- data/spec/spec_helper.rb +21 -7
- metadata +50 -42
- data/spec/resourceful/authentication_manager_spec.rb +0 -249
- data/spec/resourceful/cache_manager_spec.rb +0 -223
- data/spec/resourceful/header_spec.rb +0 -38
- data/spec/resourceful/http_accessor_spec.rb +0 -164
- data/spec/resourceful/memcache_cache_manager_spec.rb +0 -111
- data/spec/resourceful/net_http_adapter_spec.rb +0 -96
- data/spec/resourceful/options_interpreter_spec.rb +0 -102
- data/spec/resourceful/request_spec.rb +0 -186
- data/spec/resourceful/resource_spec.rb +0 -600
- data/spec/resourceful/response_spec.rb +0 -238
- data/spec/resourceful/stubbed_resource_proxy_spec.rb +0 -58
- data/spec/simple_http_server_shared_spec.rb +0 -162
- data/spec/simple_http_server_shared_spec_spec.rb +0 -212
data/lib/resourceful/response.rb
CHANGED
@@ -5,13 +5,10 @@ require 'facets/kernel/ergo'
|
|
5
5
|
require 'zlib'
|
6
6
|
|
7
7
|
module Resourceful
|
8
|
-
# Exception indicating that the server used a content coding scheme
|
9
|
-
# that Resourceful is unable to handle.
|
10
|
-
class UnsupportedContentCoding < Exception
|
11
|
-
end
|
12
8
|
|
13
9
|
class Response
|
14
10
|
REDIRECT_RESPONSE_CODES = [301,302,303,307]
|
11
|
+
NORMALLY_CACHEABLE_RESPONSE_CODES = [200, 203, 300, 301, 410]
|
15
12
|
|
16
13
|
attr_reader :uri, :code, :header, :body, :response_time
|
17
14
|
alias headers header
|
@@ -24,98 +21,15 @@ module Resourceful
|
|
24
21
|
@response_time = Time.now
|
25
22
|
end
|
26
23
|
|
27
|
-
# Is the response code sucessful? True for only 2xx series
|
28
|
-
# response codes.
|
29
|
-
#
|
30
|
-
# @return true|false
|
31
|
-
def is_success?
|
32
|
-
@code.in? 200..299
|
33
|
-
end
|
34
|
-
alias was_successful? is_success?
|
35
|
-
|
36
|
-
# Is the response the result of a server error? True for
|
37
|
-
# 5xx series response codes
|
38
|
-
#
|
39
|
-
# @return true|false
|
40
|
-
def is_server_error?
|
41
|
-
@code.in? 500..599
|
42
|
-
end
|
43
|
-
alias was_server_error? is_server_error?
|
44
|
-
|
45
|
-
# Is the response the result of a client error? True for
|
46
|
-
# 4xx series response codes
|
47
|
-
#
|
48
|
-
# @return true|false
|
49
|
-
def is_client_error?
|
50
|
-
@code.in? 400..499
|
51
|
-
end
|
52
|
-
alias was_client_error? is_client_error?
|
53
|
-
|
54
|
-
# Is the response the result of any kind of error? True for
|
55
|
-
# 4xx and 5xx series response codes
|
56
|
-
#
|
57
|
-
# @return true|false
|
58
|
-
def is_error?
|
59
|
-
is_server_error? || is_client_error?
|
60
|
-
end
|
61
|
-
alias was_error? is_error?
|
62
|
-
|
63
|
-
# Is the response not a success? True for
|
64
|
-
# 3xx, 4xx and 5xx series response codes
|
65
|
-
#
|
66
|
-
# @return true|false
|
67
|
-
def is_unsuccesful?
|
68
|
-
is_error? || is_redirect?
|
69
|
-
end
|
70
|
-
alias was_unsuccessful? is_unsuccesful?
|
71
|
-
|
72
|
-
# Is the response a redirect response code? True for
|
73
|
-
# 3xx codes that are redirects (301, 302, 303, 307)
|
74
|
-
#
|
75
|
-
# @return true|false
|
76
|
-
def is_redirect?
|
77
|
-
@code.in? REDIRECT_RESPONSE_CODES
|
78
|
-
end
|
79
|
-
alias was_redirect? is_redirect?
|
80
|
-
|
81
|
-
# Is the response a Permanent Redirect (301) ?
|
82
|
-
#
|
83
|
-
# @return true|false
|
84
|
-
def is_permanent_redirect?
|
85
|
-
@code == 301
|
86
|
-
end
|
87
|
-
|
88
|
-
# Is the response a Temporary Redirect (anything but 301) ?
|
89
|
-
#
|
90
|
-
# @return true|false
|
91
|
-
def is_temporary_redirect?
|
92
|
-
is_redirect? and not is_permanent_redirect?
|
93
|
-
end
|
94
|
-
|
95
|
-
# Is the response a client error of Not Authorized (401) ?
|
96
|
-
#
|
97
|
-
# @return true|false
|
98
|
-
def is_not_authorized?
|
99
|
-
@code == 401
|
100
|
-
end
|
101
|
-
|
102
|
-
# Is the response not modified (304) ?
|
103
|
-
#
|
104
|
-
# @return true|false
|
105
|
-
def is_not_modified?
|
106
|
-
@code == 304
|
107
|
-
end
|
108
|
-
|
109
24
|
# Is this a cached response that has expired?
|
110
25
|
#
|
111
26
|
# @return true|false
|
112
27
|
def expired?
|
113
|
-
if header['Expire']
|
114
|
-
return true if Time.httpdate(header['Expire'].first) < Time.now
|
115
|
-
end
|
116
28
|
if header['Cache-Control'] and header['Cache-Control'].first.include?('max-age')
|
117
29
|
max_age = header['Cache-Control'].first.split(',').grep(/max-age/).first.split('=').last.to_i
|
118
30
|
return true if current_age > max_age
|
31
|
+
elsif header['Expire']
|
32
|
+
return true if Time.httpdate(header['Expire'].first) < Time.now
|
119
33
|
end
|
120
34
|
|
121
35
|
false
|
@@ -137,21 +51,37 @@ module Resourceful
|
|
137
51
|
# Is this response cachable?
|
138
52
|
#
|
139
53
|
# @return true|false
|
140
|
-
def
|
141
|
-
|
142
|
-
|
54
|
+
def cacheable?
|
55
|
+
@cacheable ||= begin
|
56
|
+
@cacheable = true if NORMALLY_CACHEABLE_RESPONSE_CODES.include?(code.to_i)
|
57
|
+
@cacheable = false if header.vary && header.vary.include?('*')
|
58
|
+
@cacheable = false if header.cache_control && header.cache_control.include?('no-cache')
|
59
|
+
@cacheable = true if header.cache_control && header.cache_control.include?('public')
|
60
|
+
@cacheable = true if header.cache_control && header.cache_control.include?('private')
|
61
|
+
@cacheable || false
|
62
|
+
end
|
63
|
+
end
|
143
64
|
|
144
|
-
|
65
|
+
# Does this response force revalidation?
|
66
|
+
def must_be_revalidated?
|
67
|
+
header.cache_control && header.cache_control.include?('must-revalidate')
|
68
|
+
end
|
69
|
+
|
70
|
+
# Update our headers from a later 304 response
|
71
|
+
def revalidate!(not_modified_response)
|
72
|
+
header.merge!(not_modified_response.header)
|
73
|
+
request_time = not_modified_response.request_time
|
74
|
+
@authoritative = true
|
145
75
|
end
|
146
76
|
|
147
77
|
# Algorithm taken from RCF2616#13.2.3
|
148
78
|
def current_age
|
149
|
-
age_value =
|
79
|
+
age_value = header['Age'] ? header['Age'].first.to_i : 0
|
150
80
|
date_value = Time.httpdate(header['Date'].first)
|
151
81
|
now = Time.now
|
152
82
|
|
153
83
|
apparent_age = [0, response_time - date_value].max
|
154
|
-
corrected_received_age = [apparent_age, age_value
|
84
|
+
corrected_received_age = [apparent_age, age_value].max
|
155
85
|
current_age = corrected_received_age + (response_time - request_time) + (now - response_time)
|
156
86
|
end
|
157
87
|
|
@@ -170,6 +100,123 @@ module Resourceful
|
|
170
100
|
raise UnsupportedContentCoding, "Resourceful does not support #{header['Content-Encoding'].ergo.first} content coding"
|
171
101
|
end
|
172
102
|
end
|
103
|
+
|
104
|
+
CODE_NAMES = {
|
105
|
+
100 => "Continue".freeze,
|
106
|
+
101 => "Switching Protocols".freeze,
|
107
|
+
|
108
|
+
200 => "OK".freeze,
|
109
|
+
201 => "Created".freeze,
|
110
|
+
202 => "Accepted".freeze,
|
111
|
+
203 => "Non-Authoritative Information".freeze,
|
112
|
+
204 => "No Content".freeze,
|
113
|
+
205 => "Reset Content".freeze,
|
114
|
+
206 => "Partial Content".freeze,
|
115
|
+
|
116
|
+
300 => "Multiple Choices".freeze,
|
117
|
+
301 => "Moved Permanently".freeze,
|
118
|
+
302 => "Found".freeze,
|
119
|
+
303 => "See Other".freeze,
|
120
|
+
304 => "Not Modified".freeze,
|
121
|
+
305 => "Use Proxy".freeze,
|
122
|
+
307 => "Temporary Redirect".freeze,
|
123
|
+
|
124
|
+
400 => "Bad Request".freeze,
|
125
|
+
401 => "Unauthorized".freeze,
|
126
|
+
402 => "Payment Required".freeze,
|
127
|
+
403 => "Forbidden".freeze,
|
128
|
+
404 => "Not Found".freeze,
|
129
|
+
405 => "Method Not Allowed".freeze,
|
130
|
+
406 => "Not Acceptable".freeze,
|
131
|
+
407 => "Proxy Authentication Required".freeze,
|
132
|
+
408 => "Request Timeout".freeze,
|
133
|
+
409 => "Conflict".freeze,
|
134
|
+
410 => "Gone".freeze,
|
135
|
+
411 => "Length Required".freeze,
|
136
|
+
412 => "Precondition Failed".freeze,
|
137
|
+
413 => "Request Entity Too Large".freeze,
|
138
|
+
414 => "Request-URI Too Long".freeze,
|
139
|
+
415 => "Unsupported Media Type".freeze,
|
140
|
+
416 => "Requested Range Not Satisfiable".freeze,
|
141
|
+
417 => "Expectation Failed".freeze,
|
142
|
+
|
143
|
+
500 => "Internal Server Error".freeze,
|
144
|
+
501 => "Not Implemented".freeze,
|
145
|
+
502 => "Bad Gateway".freeze,
|
146
|
+
503 => "Service Unavailable".freeze,
|
147
|
+
504 => "Gateway Timeout".freeze,
|
148
|
+
505 => "HTTP Version Not Supported".freeze,
|
149
|
+
}.freeze
|
150
|
+
|
151
|
+
CODES = CODE_NAMES.keys
|
152
|
+
|
153
|
+
CODE_NAMES.each do |code, msg|
|
154
|
+
method_name = msg.downcase.gsub(/[- ]/, "_")
|
155
|
+
|
156
|
+
class_eval <<-RUBY
|
157
|
+
def #{method_name}? # def ok?
|
158
|
+
@code == #{code} # @code == 200
|
159
|
+
end # end
|
160
|
+
RUBY
|
161
|
+
end
|
162
|
+
|
163
|
+
# Is the response informational? True for
|
164
|
+
# 1xx series response codes
|
165
|
+
#
|
166
|
+
# @return true|false
|
167
|
+
def informational?
|
168
|
+
@code.in? 100..199
|
169
|
+
end
|
170
|
+
|
171
|
+
# Is the response code sucessful? True for only 2xx series
|
172
|
+
# response codes.
|
173
|
+
#
|
174
|
+
# @return true|false
|
175
|
+
def successful?
|
176
|
+
@code.in? 200..299
|
177
|
+
end
|
178
|
+
alias success? successful?
|
179
|
+
|
180
|
+
# Is the response a redirect? True for
|
181
|
+
# 3xx series response codes
|
182
|
+
#
|
183
|
+
# @return true|false
|
184
|
+
def redirection?
|
185
|
+
@code.in? 300..399
|
186
|
+
end
|
187
|
+
|
188
|
+
# Is the response a actual redirect? True for
|
189
|
+
# 301, 302, 303, 307 response codes
|
190
|
+
#
|
191
|
+
# @return true|false
|
192
|
+
def redirect?
|
193
|
+
@code.in? REDIRECT_RESPONSE_CODES
|
194
|
+
end
|
195
|
+
|
196
|
+
# Is the response the result of a client error? True for
|
197
|
+
# 4xx series response codes
|
198
|
+
#
|
199
|
+
# @return true|false
|
200
|
+
def client_error?
|
201
|
+
@code.in? 400..499
|
202
|
+
end
|
203
|
+
|
204
|
+
# Is the response the result of a server error? True for
|
205
|
+
# 5xx series response codes
|
206
|
+
#
|
207
|
+
# @return true|false
|
208
|
+
def server_error?
|
209
|
+
@code.in? 500..599
|
210
|
+
end
|
211
|
+
|
212
|
+
# Is the response the result of any kind of error? True for
|
213
|
+
# 4xx and 5xx series response codes
|
214
|
+
#
|
215
|
+
# @return true|false
|
216
|
+
def error?
|
217
|
+
server_error? || client_error?
|
218
|
+
end
|
219
|
+
|
173
220
|
end
|
174
221
|
|
175
222
|
end
|
data/resourceful.gemspec
CHANGED
@@ -2,26 +2,25 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{resourceful}
|
5
|
-
s.version = "0.
|
5
|
+
s.version = "0.5.0"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Paul Sadauskas"]
|
9
|
-
s.date = %q{
|
9
|
+
s.date = %q{2009-07-13}
|
10
10
|
s.description = %q{An HTTP library for Ruby that takes advantage of everything HTTP has to offer.}
|
11
11
|
s.email = %q{psadauskas@gmail.com}
|
12
|
-
s.extra_rdoc_files = ["lib/resourceful.rb", "lib/resourceful/
|
13
|
-
s.files = ["
|
14
|
-
s.has_rdoc = true
|
12
|
+
s.extra_rdoc_files = ["README.markdown", "lib/resourceful/cache_manager.rb", "lib/resourceful/exceptions.rb", "lib/resourceful/net_http_adapter.rb", "lib/resourceful/stubbed_resource_proxy.rb", "lib/resourceful/header.rb", "lib/resourceful/authentication_manager.rb", "lib/resourceful/request.rb", "lib/resourceful/resource.rb", "lib/resourceful/response.rb", "lib/resourceful/util.rb", "lib/resourceful/http_accessor.rb", "lib/resourceful/options_interpreter.rb", "lib/resourceful/memcache_cache_manager.rb", "lib/resourceful.rb"]
|
13
|
+
s.files = ["Manifest", "spec/spec.opts", "spec/simple_sinatra_server_spec.rb", "spec/simple_sinatra_server.rb", "spec/acceptance/header_spec.rb", "spec/acceptance/caching_spec.rb", "spec/acceptance/resource_spec.rb", "spec/acceptance/redirecting_spec.rb", "spec/acceptance/authorization_spec.rb", "spec/acceptance_shared_specs.rb", "spec/spec_helper.rb", "spec/old_acceptance_specs.rb", "README.markdown", "resourceful.gemspec", "lib/resourceful/cache_manager.rb", "lib/resourceful/exceptions.rb", "lib/resourceful/net_http_adapter.rb", "lib/resourceful/stubbed_resource_proxy.rb", "lib/resourceful/header.rb", "lib/resourceful/authentication_manager.rb", "lib/resourceful/request.rb", "lib/resourceful/resource.rb", "lib/resourceful/response.rb", "lib/resourceful/util.rb", "lib/resourceful/http_accessor.rb", "lib/resourceful/options_interpreter.rb", "lib/resourceful/memcache_cache_manager.rb", "lib/resourceful.rb", "Rakefile", "MIT-LICENSE"]
|
15
14
|
s.homepage = %q{http://github.com/paul/resourceful}
|
16
15
|
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Resourceful", "--main", "README.markdown"]
|
17
16
|
s.require_paths = ["lib"]
|
18
17
|
s.rubyforge_project = %q{resourceful}
|
19
|
-
s.rubygems_version = %q{1.3.
|
18
|
+
s.rubygems_version = %q{1.3.3}
|
20
19
|
s.summary = %q{An HTTP library for Ruby that takes advantage of everything HTTP has to offer.}
|
21
20
|
|
22
21
|
if s.respond_to? :specification_version then
|
23
22
|
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
24
|
-
s.specification_version =
|
23
|
+
s.specification_version = 3
|
25
24
|
|
26
25
|
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
27
26
|
s.add_runtime_dependency(%q<addressable>, [">= 0"])
|
@@ -31,6 +30,7 @@ Gem::Specification.new do |s|
|
|
31
30
|
s.add_runtime_dependency(%q<andand>, [">= 0"])
|
32
31
|
s.add_development_dependency(%q<thin>, [">= 0"])
|
33
32
|
s.add_development_dependency(%q<yard>, [">= 0"])
|
33
|
+
s.add_development_dependency(%q<sinatra>, [">= 0"])
|
34
34
|
else
|
35
35
|
s.add_dependency(%q<addressable>, [">= 0"])
|
36
36
|
s.add_dependency(%q<httpauth>, [">= 0"])
|
@@ -39,6 +39,7 @@ Gem::Specification.new do |s|
|
|
39
39
|
s.add_dependency(%q<andand>, [">= 0"])
|
40
40
|
s.add_dependency(%q<thin>, [">= 0"])
|
41
41
|
s.add_dependency(%q<yard>, [">= 0"])
|
42
|
+
s.add_dependency(%q<sinatra>, [">= 0"])
|
42
43
|
end
|
43
44
|
else
|
44
45
|
s.add_dependency(%q<addressable>, [">= 0"])
|
@@ -48,5 +49,6 @@ Gem::Specification.new do |s|
|
|
48
49
|
s.add_dependency(%q<andand>, [">= 0"])
|
49
50
|
s.add_dependency(%q<thin>, [">= 0"])
|
50
51
|
s.add_dependency(%q<yard>, [">= 0"])
|
52
|
+
s.add_dependency(%q<sinatra>, [">= 0"])
|
51
53
|
end
|
52
54
|
end
|
@@ -0,0 +1,192 @@
|
|
1
|
+
|
2
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
3
|
+
require 'resourceful'
|
4
|
+
require 'addressable/template'
|
5
|
+
|
6
|
+
describe Resourceful do
|
7
|
+
|
8
|
+
describe "caching" do
|
9
|
+
|
10
|
+
before do
|
11
|
+
@http = Resourceful::HttpAccessor.new(:cache_manager => Resourceful::InMemoryCacheManager.new)
|
12
|
+
if ENV['SPEC_LOGGING']
|
13
|
+
@http.logger = Resourceful::StdOutLogger.new
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def get_with_errors(resource)
|
18
|
+
begin
|
19
|
+
resp = resource.get
|
20
|
+
rescue Resourceful::UnsuccessfulHttpRequestError => e
|
21
|
+
resp = e.http_response
|
22
|
+
end
|
23
|
+
resp
|
24
|
+
end
|
25
|
+
|
26
|
+
def uri_plus_params(uri, params = {})
|
27
|
+
uri = uri.is_a?(Addressable::URI) ? uri : Addressable::URI.parse(uri)
|
28
|
+
uri.query_values = params
|
29
|
+
uri
|
30
|
+
end
|
31
|
+
|
32
|
+
def uri_for_code(code, params = {})
|
33
|
+
template = Addressable::Template.new("http://localhost:3000/code/{code}")
|
34
|
+
uri = template.expand("code" => code.to_s)
|
35
|
+
|
36
|
+
uri_plus_params(uri, params)
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "response cacheability" do
|
40
|
+
Resourceful::Response::NORMALLY_CACHEABLE_RESPONSE_CODES.each do |code|
|
41
|
+
describe "response code #{code}" do
|
42
|
+
it "should normally be cached" do
|
43
|
+
resource = @http.resource(uri_for_code(code))
|
44
|
+
|
45
|
+
resp = get_with_errors(resource)
|
46
|
+
resp.should be_cacheable
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should not be cached if Vary: *" do
|
50
|
+
resource = @http.resource(uri_for_code(200, "Vary" => "*"))
|
51
|
+
|
52
|
+
resp = get_with_errors(resource)
|
53
|
+
resp.should_not be_cacheable
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should not be cached if Cache-Control: no-cache'" do
|
57
|
+
resource = @http.resource(uri_for_code(200, "Cache-Control" => "no-cache"))
|
58
|
+
|
59
|
+
resp = get_with_errors(resource)
|
60
|
+
resp.should_not be_cacheable
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# I would prefer to do all other codes, but some of them do some magic stuff (100),
|
66
|
+
# so I'll just spot check.
|
67
|
+
[201, 206, 302, 307, 404, 500].each do |code|
|
68
|
+
describe "response code #{code}" do
|
69
|
+
it "should not normally be cached" do
|
70
|
+
resource = @http.resource(uri_for_code(code))
|
71
|
+
|
72
|
+
resp = get_with_errors(resource)
|
73
|
+
resp.should_not be_cacheable
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should be cached if Cache-Control: public" do
|
77
|
+
resource = @http.resource(uri_for_code(code, "Cache-Control" => "public"))
|
78
|
+
|
79
|
+
resp = get_with_errors(resource)
|
80
|
+
resp.should be_cacheable
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should be cached if Cache-Control: private" do
|
84
|
+
resource = @http.resource(uri_for_code(code, "Cache-Control" => "private"))
|
85
|
+
|
86
|
+
resp = get_with_errors(resource)
|
87
|
+
resp.should be_cacheable
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
describe "expiration" do
|
95
|
+
it 'should use the cached response if Expire: is in the future' do
|
96
|
+
in_the_future = (Time.now + 60).httpdate
|
97
|
+
resource = @http.resource(uri_for_code(200, "Expire" => in_the_future))
|
98
|
+
|
99
|
+
resp = resource.get
|
100
|
+
resp.should_not be_expired
|
101
|
+
|
102
|
+
resp = resource.get
|
103
|
+
resp.should be_ok
|
104
|
+
resp.should_not be_authoritative
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'should revalidate the cached response if the response is expired' do
|
108
|
+
in_the_past = (Time.now - 60).httpdate
|
109
|
+
resource = @http.resource(uri_for_code(200, "Expire" => in_the_past))
|
110
|
+
|
111
|
+
resp = resource.get
|
112
|
+
resp.should be_expired
|
113
|
+
|
114
|
+
resp = resource.get
|
115
|
+
resp.should be_ok
|
116
|
+
resp.should be_authoritative
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe 'authoritative' do
|
121
|
+
|
122
|
+
it "should be authoritative if the response is directly from the server" do
|
123
|
+
resource = @http.resource(
|
124
|
+
uri_plus_params('http://localhost:3000/', "Cache-Control" => 'max-age=10')
|
125
|
+
)
|
126
|
+
|
127
|
+
response = resource.get
|
128
|
+
response.should be_authoritative
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should be authoritative if a cached response was revalidated with the server" do
|
132
|
+
now = Time.now.httpdate
|
133
|
+
resource = @http.resource(
|
134
|
+
uri_plus_params('http://localhost:3000/cached',
|
135
|
+
"modified" => now,
|
136
|
+
"Cache-Control" => 'max-age=0')
|
137
|
+
)
|
138
|
+
|
139
|
+
resource.get
|
140
|
+
response = resource.get("Cache-Control" => "max-age=0")
|
141
|
+
response.should be_authoritative
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should not be authoritative if the cached response was not revalidated" do
|
145
|
+
now = Time.now.httpdate
|
146
|
+
resource = @http.resource(
|
147
|
+
uri_plus_params('http://localhost:3000/cached',
|
148
|
+
"modified" => now,
|
149
|
+
"Cache-Control" => 'max-age=10')
|
150
|
+
)
|
151
|
+
|
152
|
+
resource.get
|
153
|
+
response = resource.get
|
154
|
+
response.should_not be_authoritative
|
155
|
+
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
159
|
+
|
160
|
+
describe "Not Modified responses" do
|
161
|
+
before do
|
162
|
+
now = Time.now.httpdate
|
163
|
+
|
164
|
+
resource = @http.resource(
|
165
|
+
uri_plus_params('http://localhost:3000/cached',
|
166
|
+
"modified" => now,
|
167
|
+
"Cache-Control" => 'max-age=0')
|
168
|
+
)
|
169
|
+
|
170
|
+
@first_response = resource.get
|
171
|
+
@second_response = resource.get("Cache-Control" => "max-age=0") # Force revalidation
|
172
|
+
end
|
173
|
+
|
174
|
+
it "should replace the 304 response with whats in the cache" do
|
175
|
+
@second_response.code.should == @first_response.code
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should provide a body identical to the original response" do
|
179
|
+
@second_response.body.should == @first_response.body
|
180
|
+
end
|
181
|
+
|
182
|
+
it "should override any cached headers with new ones"
|
183
|
+
end
|
184
|
+
|
185
|
+
describe "cache invalidation" do
|
186
|
+
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
|