almodovar 0.1.0 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/vendor/resourceful-0.5.3-patched/MIT-LICENSE +21 -0
- data/vendor/resourceful-0.5.3-patched/Manifest +29 -0
- data/vendor/resourceful-0.5.3-patched/README.markdown +84 -0
- data/vendor/resourceful-0.5.3-patched/Rakefile +71 -0
- data/vendor/resourceful-0.5.3-patched/lib/resourceful.rb +18 -0
- data/vendor/resourceful-0.5.3-patched/lib/resourceful/authentication_manager.rb +108 -0
- data/vendor/resourceful-0.5.3-patched/lib/resourceful/cache_manager.rb +240 -0
- data/vendor/resourceful-0.5.3-patched/lib/resourceful/exceptions.rb +34 -0
- data/vendor/resourceful-0.5.3-patched/lib/resourceful/header.rb +126 -0
- data/vendor/resourceful-0.5.3-patched/lib/resourceful/http_accessor.rb +98 -0
- data/vendor/resourceful-0.5.3-patched/lib/resourceful/memcache_cache_manager.rb +75 -0
- data/vendor/resourceful-0.5.3-patched/lib/resourceful/net_http_adapter.rb +70 -0
- data/vendor/resourceful-0.5.3-patched/lib/resourceful/options_interpreter.rb +78 -0
- data/vendor/resourceful-0.5.3-patched/lib/resourceful/request.rb +230 -0
- data/vendor/resourceful-0.5.3-patched/lib/resourceful/resource.rb +163 -0
- data/vendor/resourceful-0.5.3-patched/lib/resourceful/response.rb +221 -0
- data/vendor/resourceful-0.5.3-patched/lib/resourceful/stubbed_resource_proxy.rb +47 -0
- data/vendor/resourceful-0.5.3-patched/lib/resourceful/util.rb +6 -0
- data/vendor/resourceful-0.5.3-patched/resourceful.gemspec +48 -0
- data/vendor/resourceful-0.5.3-patched/spec/acceptance/authorization_spec.rb +16 -0
- data/vendor/resourceful-0.5.3-patched/spec/acceptance/caching_spec.rb +192 -0
- data/vendor/resourceful-0.5.3-patched/spec/acceptance/header_spec.rb +24 -0
- data/vendor/resourceful-0.5.3-patched/spec/acceptance/redirecting_spec.rb +12 -0
- data/vendor/resourceful-0.5.3-patched/spec/acceptance/resource_spec.rb +84 -0
- data/vendor/resourceful-0.5.3-patched/spec/acceptance_shared_specs.rb +44 -0
- data/vendor/resourceful-0.5.3-patched/spec/old_acceptance_specs.rb +378 -0
- data/vendor/resourceful-0.5.3-patched/spec/simple_sinatra_server.rb +74 -0
- data/vendor/resourceful-0.5.3-patched/spec/simple_sinatra_server_spec.rb +98 -0
- data/vendor/resourceful-0.5.3-patched/spec/spec.opts +3 -0
- data/vendor/resourceful-0.5.3-patched/spec/spec_helper.rb +28 -0
- metadata +32 -2
@@ -0,0 +1,230 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'benchmark'
|
3
|
+
require 'resourceful/response'
|
4
|
+
require 'resourceful/net_http_adapter'
|
5
|
+
require 'resourceful/exceptions'
|
6
|
+
|
7
|
+
module Resourceful
|
8
|
+
|
9
|
+
class Request
|
10
|
+
|
11
|
+
REDIRECTABLE_METHODS = [:get, :head]
|
12
|
+
CACHEABLE_METHODS = [:get, :head]
|
13
|
+
INVALIDATING_METHODS = [:post, :put, :delete]
|
14
|
+
|
15
|
+
attr_accessor :method, :resource, :body, :header
|
16
|
+
attr_reader :request_time, :accessor
|
17
|
+
|
18
|
+
# @param [Symbol] http_method
|
19
|
+
# :get, :put, :post, :delete or :head
|
20
|
+
# @param [Resourceful::Resource] resource
|
21
|
+
# @param [String] body
|
22
|
+
# @param [Resourceful::Header, Hash] header
|
23
|
+
def initialize(http_method, resource, body = nil, header = nil)
|
24
|
+
@method, @resource, @body = http_method, resource, body
|
25
|
+
@accessor = @resource.accessor
|
26
|
+
@header = header.is_a?(Resourceful::Header) ? header : Resourceful::Header.new(header)
|
27
|
+
|
28
|
+
# Resourceful handled gzip encoding transparently, so set that up
|
29
|
+
@header.accept_encoding ||= 'gzip, identity'
|
30
|
+
|
31
|
+
# 'Host' is a required HTTP/1.1 header, overrides Host in user-provided headers
|
32
|
+
@header.host = @resource.host
|
33
|
+
|
34
|
+
# Setting the date isn't a bad idea, either
|
35
|
+
@header.date ||= Time.now.httpdate
|
36
|
+
|
37
|
+
# Add any auth credentials we might want
|
38
|
+
add_credentials!
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
# Uses the auth manager to add any valid credentials to this request
|
43
|
+
def add_credentials!
|
44
|
+
@accessor.auth_manager.add_credentials(self)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Performs all the work. Handles caching, redirects, auth retries, etc
|
48
|
+
def fetch_response
|
49
|
+
if cached_response
|
50
|
+
if needs_revalidation?(cached_response)
|
51
|
+
logger.info(" Cache needs revalidation")
|
52
|
+
set_validation_headers!(cached_response)
|
53
|
+
else
|
54
|
+
# We're done!
|
55
|
+
return cached_response
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
response = perform!
|
60
|
+
|
61
|
+
response = revalidate_cached_response(response) if cached_response && response.not_modified?
|
62
|
+
response = follow_redirect(response) if should_be_redirected?(response)
|
63
|
+
response = retry_with_auth(response) if needs_authorization?(response)
|
64
|
+
|
65
|
+
raise UnsuccessfulHttpRequestError.new(self, response) if response.error?
|
66
|
+
|
67
|
+
if cacheable?(response)
|
68
|
+
store_in_cache(response)
|
69
|
+
elsif invalidates_cache?
|
70
|
+
invalidate_cache
|
71
|
+
end
|
72
|
+
|
73
|
+
return response
|
74
|
+
end
|
75
|
+
|
76
|
+
# Should we look for a response to this request in the cache?
|
77
|
+
def skip_cache?
|
78
|
+
return true unless method.in? CACHEABLE_METHODS
|
79
|
+
header.cache_control && header.cache_control.include?('no-cache')
|
80
|
+
end
|
81
|
+
|
82
|
+
# The cached response
|
83
|
+
def cached_response
|
84
|
+
return if skip_cache?
|
85
|
+
return if @cached_response.nil? && @already_checked_cache
|
86
|
+
@cached_response ||= begin
|
87
|
+
@already_checked_cache = true
|
88
|
+
resp = accessor.cache_manager.lookup(self)
|
89
|
+
logger.info(" Retrieved from cache") if resp
|
90
|
+
resp
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Revalidate the cached response with what we got from a 304 response
|
95
|
+
def revalidate_cached_response(not_modified_response)
|
96
|
+
logger.info(" Resource not modified")
|
97
|
+
cached_response.revalidate!(not_modified_response)
|
98
|
+
cached_response
|
99
|
+
end
|
100
|
+
|
101
|
+
# Follow a redirect response
|
102
|
+
def follow_redirect(response)
|
103
|
+
raise MalformedServerResponse.new(self, response) unless response.header.location
|
104
|
+
if response.moved_permanently?
|
105
|
+
new_uri = response.header.location.first
|
106
|
+
logger.info(" Permanently redirected to #{new_uri} - Storing new location.")
|
107
|
+
resource.update_uri new_uri
|
108
|
+
@header.host = resource.host
|
109
|
+
response = fetch_response
|
110
|
+
elsif response.see_other? # Always use GET for this redirect, regardless of initial method
|
111
|
+
redirected_resource = Resourceful::Resource.new(self.accessor, response.header['Location'].first)
|
112
|
+
response = Request.new(:get, redirected_resource, body, header).fetch_response
|
113
|
+
else
|
114
|
+
redirected_resource = Resourceful::Resource.new(self.accessor, response.header['Location'].first)
|
115
|
+
logger.info(" Redirected to #{redirected_resource.uri} - Caching new location.")
|
116
|
+
response = Request.new(method, redirected_resource, body, header).fetch_response
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Add any auth headers from the response to the auth manager, and try the request again
|
121
|
+
def retry_with_auth(response)
|
122
|
+
@already_tried_with_auth = true
|
123
|
+
logger.info("Authentication Required. Retrying with auth info")
|
124
|
+
accessor.auth_manager.associate_auth_info(response)
|
125
|
+
add_credentials!
|
126
|
+
response = fetch_response
|
127
|
+
end
|
128
|
+
|
129
|
+
# Does this request need to be authorized? Will only be true if we haven't already tried with auth
|
130
|
+
def needs_authorization?(response)
|
131
|
+
!@already_tried_with_auth && response.unauthorized?
|
132
|
+
end
|
133
|
+
|
134
|
+
# Store the response to this request in the cache
|
135
|
+
def store_in_cache(response)
|
136
|
+
# RFC2618 - 14.18 : A received message that does not have a Date header
|
137
|
+
# field MUST be assigned one by the recipient if the message will be cached
|
138
|
+
# by that recipient.
|
139
|
+
response.header.date ||= response.response_time.httpdate
|
140
|
+
|
141
|
+
accessor.cache_manager.store(self, response)
|
142
|
+
end
|
143
|
+
|
144
|
+
# Invalidated the cache for this uri (eg, after a POST)
|
145
|
+
def invalidate_cache
|
146
|
+
accessor.cache_manager.invalidate(resource.uri)
|
147
|
+
end
|
148
|
+
|
149
|
+
# Is this request & response permitted to be stored in this (private) cache?
|
150
|
+
def cacheable?(response)
|
151
|
+
return false unless response.success?
|
152
|
+
return false unless method.in? CACHEABLE_METHODS
|
153
|
+
return false if header.cache_control && header.cache_control.include?('no-store')
|
154
|
+
true
|
155
|
+
end
|
156
|
+
|
157
|
+
# Does this request invalidate the cache?
|
158
|
+
def invalidates_cache?
|
159
|
+
return true if method.in? INVALIDATING_METHODS
|
160
|
+
end
|
161
|
+
|
162
|
+
# Perform the request, with no magic handling of anything.
|
163
|
+
def perform!
|
164
|
+
@request_time = Time.now
|
165
|
+
|
166
|
+
http_resp = NetHttpAdapter.make_request(@method, @resource.uri, @body, @header)
|
167
|
+
@response = Resourceful::Response.new(uri, *http_resp)
|
168
|
+
@response.request_time = @request_time
|
169
|
+
@response.authoritative = true
|
170
|
+
|
171
|
+
@response
|
172
|
+
end
|
173
|
+
|
174
|
+
# Is this a response a redirect, and are we permitted to follow it?
|
175
|
+
def should_be_redirected?(response)
|
176
|
+
return false unless response.redirect?
|
177
|
+
if resource.on_redirect.nil?
|
178
|
+
return true if method.in? REDIRECTABLE_METHODS
|
179
|
+
false
|
180
|
+
else
|
181
|
+
resource.on_redirect.call(self, response)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# Do we need to revalidate our cache?
|
186
|
+
def needs_revalidation?(response)
|
187
|
+
return true if forces_revalidation?
|
188
|
+
return true if response.stale?
|
189
|
+
return true if max_age && response.current_age > max_age
|
190
|
+
return true if response.must_be_revalidated?
|
191
|
+
false
|
192
|
+
end
|
193
|
+
|
194
|
+
# Set the validation headers of a request based on the response in the cache
|
195
|
+
def set_validation_headers!(response)
|
196
|
+
@header['If-None-Match'] = response.header['ETag'] if response.header.has_key?('ETag')
|
197
|
+
@header['If-Modified-Since'] = response.header['Last-Modified'] if response.header.has_key?('Last-Modified')
|
198
|
+
@header['Cache-Control'] = 'max-age=0' if response.must_be_revalidated?
|
199
|
+
end
|
200
|
+
|
201
|
+
# @return [String] The URI against which this request will be, or was, made.
|
202
|
+
def uri
|
203
|
+
resource.uri
|
204
|
+
end
|
205
|
+
|
206
|
+
# Does this request force us to revalidate the cache?
|
207
|
+
def forces_revalidation?
|
208
|
+
if max_age == 0 || header.cache_control && cc.include?('no-cache')
|
209
|
+
logger.info(" Client forced revalidation")
|
210
|
+
true
|
211
|
+
else
|
212
|
+
false
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
# Indicates the maxmimum response age in seconds we are willing to accept
|
217
|
+
#
|
218
|
+
# Returns nil if we don't care how old the response is
|
219
|
+
def max_age
|
220
|
+
if header['Cache-Control'] and header['Cache-Control'].include?('max-age')
|
221
|
+
header['Cache-Control'].split(',').grep(/max-age/).first.split('=').last.to_i
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def logger
|
226
|
+
resource.logger
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'resourceful/request'
|
3
|
+
|
4
|
+
module Resourceful
|
5
|
+
|
6
|
+
class Resource
|
7
|
+
attr_reader :accessor
|
8
|
+
attr_accessor :default_header
|
9
|
+
|
10
|
+
# Build a new resource for a uri
|
11
|
+
#
|
12
|
+
# @param accessor<HttpAccessor>
|
13
|
+
# The parent http accessor
|
14
|
+
# @param uri<String, Addressable::URI>
|
15
|
+
# The uri for the location of the resource
|
16
|
+
def initialize(accessor, uri, default_header = {})
|
17
|
+
@accessor, @uris = accessor, [uri]
|
18
|
+
@default_header = Resourceful::Header.new({'User-Agent' => Resourceful::RESOURCEFUL_USER_AGENT_TOKEN}.merge(default_header))
|
19
|
+
@on_redirect = nil
|
20
|
+
end
|
21
|
+
|
22
|
+
# The uri used to identify this resource. This is almost always the uri
|
23
|
+
# used to create the resource, but in the case of a permanent redirect, this
|
24
|
+
# will always reflect the lastest uri.
|
25
|
+
#
|
26
|
+
# @return Addressable::URI
|
27
|
+
# The current uri of the resource
|
28
|
+
def effective_uri
|
29
|
+
@uris.first
|
30
|
+
end
|
31
|
+
alias uri effective_uri
|
32
|
+
|
33
|
+
# Returns the host for this Resource's current uri
|
34
|
+
def host
|
35
|
+
Addressable::URI.parse(uri).host
|
36
|
+
end
|
37
|
+
|
38
|
+
# Updates the effective uri after following a permanent redirect
|
39
|
+
def update_uri(uri)
|
40
|
+
@uris.unshift(uri)
|
41
|
+
end
|
42
|
+
|
43
|
+
# When performing a redirect, this callback will be executed first. If the callback
|
44
|
+
# returns true, then the redirect is followed, otherwise it is not. The request that
|
45
|
+
# triggered the redirect and the response will be passed into the block. This can be
|
46
|
+
# used to update any links on the client side.
|
47
|
+
#
|
48
|
+
# Example:
|
49
|
+
#
|
50
|
+
# author_resource.on_redirect do |req, resp|
|
51
|
+
# post.author_uri = resp.header['Location']
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# @yieldparam callback<request, response>
|
55
|
+
# The action to be executed when a request results in a redirect. Yields the
|
56
|
+
# current request and result objects to the callback.
|
57
|
+
#
|
58
|
+
# @raise ArgumentError if called without a block
|
59
|
+
def on_redirect(&callback)
|
60
|
+
if block_given?
|
61
|
+
@on_redirect = callback
|
62
|
+
else
|
63
|
+
@on_redirect
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Performs a GET on the resource, following redirects as neccessary, and retriving
|
68
|
+
# it from the local cache if its available and valid.
|
69
|
+
#
|
70
|
+
# @return [Response] The Response to the final request made.
|
71
|
+
#
|
72
|
+
# @raise [UnsuccessfulHttpRequestError] unless the request is a
|
73
|
+
# success, ie the final request returned a 2xx response code
|
74
|
+
def get(header = {})
|
75
|
+
request(:get, nil, header)
|
76
|
+
end
|
77
|
+
|
78
|
+
# :call-seq:
|
79
|
+
# post(data = "", :content_type => mime_type)
|
80
|
+
#
|
81
|
+
# Performs a POST with the given data to the resource, following redirects as
|
82
|
+
# neccessary.
|
83
|
+
#
|
84
|
+
# @param [String] data
|
85
|
+
# The body of the data to be posted
|
86
|
+
# @param [Hash] options
|
87
|
+
# Options to pass into the request header. At the least, :content_type is required.
|
88
|
+
#
|
89
|
+
# @return [Response] The Response to the final request that was made.
|
90
|
+
#
|
91
|
+
# @raise [ArgumentError] unless :content-type is specified in options
|
92
|
+
# @raise [UnsuccessfulHttpRequestError] unless the request is a
|
93
|
+
# success, ie the final request returned a 2xx response code
|
94
|
+
def post(data = nil, header = {})
|
95
|
+
check_content_type_exists(data, header)
|
96
|
+
request(:post, data, header)
|
97
|
+
end
|
98
|
+
|
99
|
+
# :call-seq:
|
100
|
+
# put(data = "", :content_type => mime_type)
|
101
|
+
#
|
102
|
+
# Performs a PUT with the given data to the resource, following redirects as
|
103
|
+
# neccessary.
|
104
|
+
#
|
105
|
+
# @param [String] data
|
106
|
+
# The body of the data to be posted
|
107
|
+
# @param [Hash] options
|
108
|
+
# Options to pass into the request header. At the least, :content_type is required.
|
109
|
+
#
|
110
|
+
# @return [Response] The response to the final request made.
|
111
|
+
#
|
112
|
+
# @raise [ArgumentError] unless :content-type is specified in options
|
113
|
+
# @raise [UnsuccessfulHttpRequestError] unless the request is a
|
114
|
+
# success, ie the final request returned a 2xx response code
|
115
|
+
def put(data, header = {})
|
116
|
+
check_content_type_exists(data, header)
|
117
|
+
request(:put, data, header)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Performs a DELETE on the resource, following redirects as neccessary.
|
121
|
+
#
|
122
|
+
# @return <Response>
|
123
|
+
#
|
124
|
+
# @raise [UnsuccessfulHttpRequestError] unless the request is a
|
125
|
+
# success, ie the final request returned a 2xx response code
|
126
|
+
def delete(header = {})
|
127
|
+
request(:delete, nil, header)
|
128
|
+
end
|
129
|
+
|
130
|
+
def logger
|
131
|
+
accessor.logger
|
132
|
+
end
|
133
|
+
|
134
|
+
private
|
135
|
+
|
136
|
+
# Ensures that the request has a content type header
|
137
|
+
# TODO Move this to request
|
138
|
+
def check_content_type_exists(body, header)
|
139
|
+
if body
|
140
|
+
raise MissingContentType unless header.has_key?(:content_type) or default_header.has_key?(:content_type)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Actually make the request
|
145
|
+
def request(method, data, header)
|
146
|
+
log_request_with_time "#{method.to_s.upcase} [#{uri}]" do
|
147
|
+
request = Request.new(method, self, data, default_header.merge(header))
|
148
|
+
request.fetch_response
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Log it took the time to make the request
|
153
|
+
def log_request_with_time(msg, indent = 2)
|
154
|
+
logger.info(" " * indent + msg)
|
155
|
+
result = nil
|
156
|
+
time = Benchmark.measure { result = yield }
|
157
|
+
logger.info(" " * indent + "-> Returned #{result.code} in %.4fs" % time.real)
|
158
|
+
result
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
@@ -0,0 +1,221 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'time'
|
3
|
+
require 'zlib'
|
4
|
+
|
5
|
+
module Resourceful
|
6
|
+
|
7
|
+
class Response
|
8
|
+
REDIRECT_RESPONSE_CODES = [301,302,303,307]
|
9
|
+
NORMALLY_CACHEABLE_RESPONSE_CODES = [200, 203, 300, 301, 410]
|
10
|
+
|
11
|
+
attr_reader :uri, :code, :header, :body, :response_time
|
12
|
+
alias headers header
|
13
|
+
|
14
|
+
attr_accessor :authoritative, :request_time
|
15
|
+
alias authoritative? authoritative
|
16
|
+
|
17
|
+
def initialize(uri, code, header, body)
|
18
|
+
@uri, @code, @header, @body = uri, code, header, body
|
19
|
+
@response_time = Time.now
|
20
|
+
end
|
21
|
+
|
22
|
+
# Is this a cached response that has expired?
|
23
|
+
#
|
24
|
+
# @return true|false
|
25
|
+
def expired?
|
26
|
+
if header['Cache-Control'] and header['Cache-Control'].first.include?('max-age')
|
27
|
+
max_age = header['Cache-Control'].first.split(',').grep(/max-age/).first.split('=').last.to_i
|
28
|
+
return true if current_age > max_age
|
29
|
+
elsif header['Expire']
|
30
|
+
return true if Time.httpdate(header['Expire'].first) < Time.now
|
31
|
+
end
|
32
|
+
|
33
|
+
false
|
34
|
+
end
|
35
|
+
|
36
|
+
# Is this a cached response that is stale?
|
37
|
+
#
|
38
|
+
# @return true|false
|
39
|
+
def stale?
|
40
|
+
return true if expired?
|
41
|
+
if header['Cache-Control']
|
42
|
+
return true if header['Cache-Control'].include?('must-revalidate')
|
43
|
+
return true if header['Cache-Control'].include?('no-cache')
|
44
|
+
end
|
45
|
+
|
46
|
+
false
|
47
|
+
end
|
48
|
+
|
49
|
+
# Is this response cachable?
|
50
|
+
#
|
51
|
+
# @return true|false
|
52
|
+
def cacheable?
|
53
|
+
@cacheable ||= begin
|
54
|
+
@cacheable = true if NORMALLY_CACHEABLE_RESPONSE_CODES.include?(code.to_i)
|
55
|
+
@cacheable = false if header.vary && header.vary.include?('*')
|
56
|
+
@cacheable = false if header.cache_control && header.cache_control.include?('no-cache')
|
57
|
+
@cacheable = true if header.cache_control && header.cache_control.include?('public')
|
58
|
+
@cacheable = true if header.cache_control && header.cache_control.include?('private')
|
59
|
+
@cacheable || false
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Does this response force revalidation?
|
64
|
+
def must_be_revalidated?
|
65
|
+
header.cache_control && header.cache_control.include?('must-revalidate')
|
66
|
+
end
|
67
|
+
|
68
|
+
# Update our headers from a later 304 response
|
69
|
+
def revalidate!(not_modified_response)
|
70
|
+
header.merge!(not_modified_response.header)
|
71
|
+
request_time = not_modified_response.request_time
|
72
|
+
@authoritative = true
|
73
|
+
end
|
74
|
+
|
75
|
+
# Algorithm taken from RCF2616#13.2.3
|
76
|
+
def current_age
|
77
|
+
age_value = header['Age'] ? header['Age'].first.to_i : 0
|
78
|
+
date_value = Time.httpdate(header['Date'].first)
|
79
|
+
now = Time.now
|
80
|
+
|
81
|
+
apparent_age = [0, response_time - date_value].max
|
82
|
+
corrected_received_age = [apparent_age, age_value].max
|
83
|
+
current_age = corrected_received_age + (response_time - request_time) + (now - response_time)
|
84
|
+
end
|
85
|
+
|
86
|
+
def body
|
87
|
+
encoding = header['Content-Encoding'] && header['Content-Encoding'].first
|
88
|
+
case encoding
|
89
|
+
when nil
|
90
|
+
# body is identity encoded; just return it
|
91
|
+
@body
|
92
|
+
when /^\s*gzip\s*$/i
|
93
|
+
gz_in = ::Zlib::GzipReader.new(StringIO.new(@body, 'r'))
|
94
|
+
@body = gz_in.read
|
95
|
+
gz_in.close
|
96
|
+
header.delete('Content-Encoding')
|
97
|
+
@body
|
98
|
+
else
|
99
|
+
raise UnsupportedContentCoding, "Resourceful does not support #{encoding} content coding"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
CODE_NAMES = {
|
104
|
+
100 => "Continue".freeze,
|
105
|
+
101 => "Switching Protocols".freeze,
|
106
|
+
|
107
|
+
200 => "OK".freeze,
|
108
|
+
201 => "Created".freeze,
|
109
|
+
202 => "Accepted".freeze,
|
110
|
+
203 => "Non-Authoritative Information".freeze,
|
111
|
+
204 => "No Content".freeze,
|
112
|
+
205 => "Reset Content".freeze,
|
113
|
+
206 => "Partial Content".freeze,
|
114
|
+
|
115
|
+
300 => "Multiple Choices".freeze,
|
116
|
+
301 => "Moved Permanently".freeze,
|
117
|
+
302 => "Found".freeze,
|
118
|
+
303 => "See Other".freeze,
|
119
|
+
304 => "Not Modified".freeze,
|
120
|
+
305 => "Use Proxy".freeze,
|
121
|
+
307 => "Temporary Redirect".freeze,
|
122
|
+
|
123
|
+
400 => "Bad Request".freeze,
|
124
|
+
401 => "Unauthorized".freeze,
|
125
|
+
402 => "Payment Required".freeze,
|
126
|
+
403 => "Forbidden".freeze,
|
127
|
+
404 => "Not Found".freeze,
|
128
|
+
405 => "Method Not Allowed".freeze,
|
129
|
+
406 => "Not Acceptable".freeze,
|
130
|
+
407 => "Proxy Authentication Required".freeze,
|
131
|
+
408 => "Request Timeout".freeze,
|
132
|
+
409 => "Conflict".freeze,
|
133
|
+
410 => "Gone".freeze,
|
134
|
+
411 => "Length Required".freeze,
|
135
|
+
412 => "Precondition Failed".freeze,
|
136
|
+
413 => "Request Entity Too Large".freeze,
|
137
|
+
414 => "Request-URI Too Long".freeze,
|
138
|
+
415 => "Unsupported Media Type".freeze,
|
139
|
+
416 => "Requested Range Not Satisfiable".freeze,
|
140
|
+
417 => "Expectation Failed".freeze,
|
141
|
+
|
142
|
+
500 => "Internal Server Error".freeze,
|
143
|
+
501 => "Not Implemented".freeze,
|
144
|
+
502 => "Bad Gateway".freeze,
|
145
|
+
503 => "Service Unavailable".freeze,
|
146
|
+
504 => "Gateway Timeout".freeze,
|
147
|
+
505 => "HTTP Version Not Supported".freeze,
|
148
|
+
}.freeze
|
149
|
+
|
150
|
+
CODES = CODE_NAMES.keys
|
151
|
+
|
152
|
+
CODE_NAMES.each do |code, msg|
|
153
|
+
method_name = msg.downcase.gsub(/[- ]/, "_")
|
154
|
+
|
155
|
+
class_eval <<-RUBY
|
156
|
+
def #{method_name}? # def ok?
|
157
|
+
@code == #{code} # @code == 200
|
158
|
+
end # end
|
159
|
+
RUBY
|
160
|
+
end
|
161
|
+
|
162
|
+
# Is the response informational? True for
|
163
|
+
# 1xx series response codes
|
164
|
+
#
|
165
|
+
# @return true|false
|
166
|
+
def informational?
|
167
|
+
@code.in? 100..199
|
168
|
+
end
|
169
|
+
|
170
|
+
# Is the response code sucessful? True for only 2xx series
|
171
|
+
# response codes.
|
172
|
+
#
|
173
|
+
# @return true|false
|
174
|
+
def successful?
|
175
|
+
@code.in? 200..299
|
176
|
+
end
|
177
|
+
alias success? successful?
|
178
|
+
|
179
|
+
# Is the response a redirect? True for
|
180
|
+
# 3xx series response codes
|
181
|
+
#
|
182
|
+
# @return true|false
|
183
|
+
def redirection?
|
184
|
+
@code.in? 300..399
|
185
|
+
end
|
186
|
+
|
187
|
+
# Is the response a actual redirect? True for
|
188
|
+
# 301, 302, 303, 307 response codes
|
189
|
+
#
|
190
|
+
# @return true|false
|
191
|
+
def redirect?
|
192
|
+
@code.in? REDIRECT_RESPONSE_CODES
|
193
|
+
end
|
194
|
+
|
195
|
+
# Is the response the result of a client error? True for
|
196
|
+
# 4xx series response codes
|
197
|
+
#
|
198
|
+
# @return true|false
|
199
|
+
def client_error?
|
200
|
+
@code.in? 400..499
|
201
|
+
end
|
202
|
+
|
203
|
+
# Is the response the result of a server error? True for
|
204
|
+
# 5xx series response codes
|
205
|
+
#
|
206
|
+
# @return true|false
|
207
|
+
def server_error?
|
208
|
+
@code.in? 500..599
|
209
|
+
end
|
210
|
+
|
211
|
+
# Is the response the result of any kind of error? True for
|
212
|
+
# 4xx and 5xx series response codes
|
213
|
+
#
|
214
|
+
# @return true|false
|
215
|
+
def error?
|
216
|
+
server_error? || client_error?
|
217
|
+
end
|
218
|
+
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|