resourceful 0.3.1 → 0.5.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/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
|
+
|