almodovar 0.5.6 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/README.rdoc +4 -4
  2. data/lib/almodovar.rb +15 -181
  3. data/lib/almodovar/digest_auth.rb +4 -0
  4. data/lib/almodovar/http_accessor.rb +26 -0
  5. data/lib/almodovar/resource.rb +56 -0
  6. data/lib/almodovar/resource_collection.rb +31 -0
  7. data/lib/almodovar/resource_presenter.rb +110 -0
  8. data/lib/almodovar/resource_presenter/collection.rb +24 -0
  9. data/lib/almodovar/resource_presenter/link.rb +103 -0
  10. data/lib/almodovar/single_resource.rb +79 -0
  11. data/lib/almodovar/to_xml.rb +21 -0
  12. metadata +47 -91
  13. data/lib/to_xml.rb +0 -24
  14. data/vendor/resourceful-0.5.3-patched/MIT-LICENSE +0 -21
  15. data/vendor/resourceful-0.5.3-patched/Manifest +0 -29
  16. data/vendor/resourceful-0.5.3-patched/README.markdown +0 -84
  17. data/vendor/resourceful-0.5.3-patched/Rakefile +0 -71
  18. data/vendor/resourceful-0.5.3-patched/lib/resourceful.rb +0 -18
  19. data/vendor/resourceful-0.5.3-patched/lib/resourceful/authentication_manager.rb +0 -108
  20. data/vendor/resourceful-0.5.3-patched/lib/resourceful/cache_manager.rb +0 -240
  21. data/vendor/resourceful-0.5.3-patched/lib/resourceful/exceptions.rb +0 -34
  22. data/vendor/resourceful-0.5.3-patched/lib/resourceful/header.rb +0 -126
  23. data/vendor/resourceful-0.5.3-patched/lib/resourceful/http_accessor.rb +0 -98
  24. data/vendor/resourceful-0.5.3-patched/lib/resourceful/memcache_cache_manager.rb +0 -75
  25. data/vendor/resourceful-0.5.3-patched/lib/resourceful/net_http_adapter.rb +0 -70
  26. data/vendor/resourceful-0.5.3-patched/lib/resourceful/options_interpreter.rb +0 -78
  27. data/vendor/resourceful-0.5.3-patched/lib/resourceful/request.rb +0 -230
  28. data/vendor/resourceful-0.5.3-patched/lib/resourceful/resource.rb +0 -165
  29. data/vendor/resourceful-0.5.3-patched/lib/resourceful/response.rb +0 -221
  30. data/vendor/resourceful-0.5.3-patched/lib/resourceful/stubbed_resource_proxy.rb +0 -47
  31. data/vendor/resourceful-0.5.3-patched/lib/resourceful/util.rb +0 -6
  32. data/vendor/resourceful-0.5.3-patched/resourceful.gemspec +0 -48
  33. data/vendor/resourceful-0.5.3-patched/spec/acceptance/authorization_spec.rb +0 -16
  34. data/vendor/resourceful-0.5.3-patched/spec/acceptance/caching_spec.rb +0 -192
  35. data/vendor/resourceful-0.5.3-patched/spec/acceptance/header_spec.rb +0 -24
  36. data/vendor/resourceful-0.5.3-patched/spec/acceptance/redirecting_spec.rb +0 -12
  37. data/vendor/resourceful-0.5.3-patched/spec/acceptance/resource_spec.rb +0 -84
  38. data/vendor/resourceful-0.5.3-patched/spec/acceptance_shared_specs.rb +0 -44
  39. data/vendor/resourceful-0.5.3-patched/spec/old_acceptance_specs.rb +0 -378
  40. data/vendor/resourceful-0.5.3-patched/spec/simple_sinatra_server.rb +0 -74
  41. data/vendor/resourceful-0.5.3-patched/spec/simple_sinatra_server_spec.rb +0 -98
  42. data/vendor/resourceful-0.5.3-patched/spec/spec.opts +0 -3
  43. data/vendor/resourceful-0.5.3-patched/spec/spec_helper.rb +0 -28
@@ -1,230 +0,0 @@
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
@@ -1,165 +0,0 @@
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
- parsed_uri = URI.parse(uri)
36
- port_string = (parsed_uri.port == parsed_uri.default_port ? "" : ":#{parsed_uri.port}")
37
- "#{parsed_uri.host}#{port_string}"
38
- end
39
-
40
- # Updates the effective uri after following a permanent redirect
41
- def update_uri(uri)
42
- @uris.unshift(uri)
43
- end
44
-
45
- # When performing a redirect, this callback will be executed first. If the callback
46
- # returns true, then the redirect is followed, otherwise it is not. The request that
47
- # triggered the redirect and the response will be passed into the block. This can be
48
- # used to update any links on the client side.
49
- #
50
- # Example:
51
- #
52
- # author_resource.on_redirect do |req, resp|
53
- # post.author_uri = resp.header['Location']
54
- # end
55
- #
56
- # @yieldparam callback<request, response>
57
- # The action to be executed when a request results in a redirect. Yields the
58
- # current request and result objects to the callback.
59
- #
60
- # @raise ArgumentError if called without a block
61
- def on_redirect(&callback)
62
- if block_given?
63
- @on_redirect = callback
64
- else
65
- @on_redirect
66
- end
67
- end
68
-
69
- # Performs a GET on the resource, following redirects as neccessary, and retriving
70
- # it from the local cache if its available and valid.
71
- #
72
- # @return [Response] The Response to the final request made.
73
- #
74
- # @raise [UnsuccessfulHttpRequestError] unless the request is a
75
- # success, ie the final request returned a 2xx response code
76
- def get(header = {})
77
- request(:get, nil, header)
78
- end
79
-
80
- # :call-seq:
81
- # post(data = "", :content_type => mime_type)
82
- #
83
- # Performs a POST with the given data to the resource, following redirects as
84
- # neccessary.
85
- #
86
- # @param [String] data
87
- # The body of the data to be posted
88
- # @param [Hash] options
89
- # Options to pass into the request header. At the least, :content_type is required.
90
- #
91
- # @return [Response] The Response to the final request that was made.
92
- #
93
- # @raise [ArgumentError] unless :content-type is specified in options
94
- # @raise [UnsuccessfulHttpRequestError] unless the request is a
95
- # success, ie the final request returned a 2xx response code
96
- def post(data = nil, header = {})
97
- check_content_type_exists(data, header)
98
- request(:post, data, header)
99
- end
100
-
101
- # :call-seq:
102
- # put(data = "", :content_type => mime_type)
103
- #
104
- # Performs a PUT with the given data to the resource, following redirects as
105
- # neccessary.
106
- #
107
- # @param [String] data
108
- # The body of the data to be posted
109
- # @param [Hash] options
110
- # Options to pass into the request header. At the least, :content_type is required.
111
- #
112
- # @return [Response] The response to the final request made.
113
- #
114
- # @raise [ArgumentError] unless :content-type is specified in options
115
- # @raise [UnsuccessfulHttpRequestError] unless the request is a
116
- # success, ie the final request returned a 2xx response code
117
- def put(data, header = {})
118
- check_content_type_exists(data, header)
119
- request(:put, data, header)
120
- end
121
-
122
- # Performs a DELETE on the resource, following redirects as neccessary.
123
- #
124
- # @return <Response>
125
- #
126
- # @raise [UnsuccessfulHttpRequestError] unless the request is a
127
- # success, ie the final request returned a 2xx response code
128
- def delete(header = {})
129
- request(:delete, nil, header)
130
- end
131
-
132
- def logger
133
- accessor.logger
134
- end
135
-
136
- private
137
-
138
- # Ensures that the request has a content type header
139
- # TODO Move this to request
140
- def check_content_type_exists(body, header)
141
- if body
142
- raise MissingContentType unless header.has_key?(:content_type) or default_header.has_key?(:content_type)
143
- end
144
- end
145
-
146
- # Actually make the request
147
- def request(method, data, header)
148
- log_request_with_time "#{method.to_s.upcase} [#{uri}]" do
149
- request = Request.new(method, self, data, default_header.merge(header))
150
- request.fetch_response
151
- end
152
- end
153
-
154
- # Log it took the time to make the request
155
- def log_request_with_time(msg, indent = 2)
156
- logger.info(" " * indent + msg)
157
- result = nil
158
- time = Benchmark.measure { result = yield }
159
- logger.info(" " * indent + "-> Returned #{result.code} in %.4fs" % time.real)
160
- result
161
- end
162
-
163
- end
164
-
165
- end
@@ -1,221 +0,0 @@
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