wrest 3.0.0 → 4.0.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.
- checksums.yaml +5 -5
- data/CHANGELOG +3 -0
- data/LICENCE +1 -1
- data/README.md +29 -28
- data/bin/wrest +2 -1
- data/bin/wrest_shell.rb +10 -8
- data/lib/wrest/async_request/event_machine_backend.rb +3 -1
- data/lib/wrest/async_request/thread_backend.rb +5 -2
- data/lib/wrest/async_request/thread_pool.rb +4 -2
- data/lib/wrest/async_request.rb +7 -6
- data/lib/wrest/cache_proxy.rb +39 -28
- data/lib/wrest/caching/memcached.rb +21 -18
- data/lib/wrest/caching/redis.rb +22 -22
- data/lib/wrest/caching.rb +16 -14
- data/lib/wrest/callback.rb +19 -16
- data/lib/wrest/components/container/alias_accessors.rb +51 -47
- data/lib/wrest/components/container/typecaster.rb +146 -96
- data/lib/wrest/components/container.rb +171 -152
- data/lib/wrest/components/mutators/base.rb +43 -34
- data/lib/wrest/components/mutators/camel_to_snake_case.rb +7 -3
- data/lib/wrest/components/mutators/{xml_mini_type_caster.rb → xml_type_caster.rb} +29 -16
- data/lib/wrest/components/mutators.rb +21 -19
- data/lib/wrest/components/translators/content_types.rb +20 -16
- data/lib/wrest/components/translators/json.rb +19 -16
- data/lib/wrest/components/translators/txt.rb +19 -15
- data/lib/wrest/components/translators/xml/conversions.rb +56 -0
- data/lib/wrest/components/translators/xml.rb +60 -18
- data/lib/wrest/components/translators.rb +7 -6
- data/lib/wrest/components.rb +11 -8
- data/lib/wrest/core_ext/hash/conversions.rb +10 -10
- data/lib/wrest/core_ext/hash.rb +4 -2
- data/lib/wrest/core_ext/string/conversions.rb +14 -13
- data/lib/wrest/core_ext/string.rb +5 -3
- data/lib/wrest/exceptions.rb +4 -2
- data/lib/wrest/hash_with_case_insensitive_access.rb +8 -8
- data/lib/wrest/hash_with_indifferent_access.rb +442 -0
- data/lib/wrest/http_codes.rb +20 -19
- data/lib/wrest/http_shared/headers.rb +2 -0
- data/lib/wrest/http_shared/standard_headers.rb +2 -2
- data/lib/wrest/http_shared/standard_tokens.rb +8 -6
- data/lib/wrest/http_shared.rb +5 -3
- data/lib/wrest/multipart.rb +20 -11
- data/lib/wrest/native/connection_factory.rb +15 -11
- data/lib/wrest/native/delete.rb +15 -11
- data/lib/wrest/native/get.rb +60 -56
- data/lib/wrest/native/options.rb +15 -11
- data/lib/wrest/native/patch.rb +16 -12
- data/lib/wrest/native/post.rb +15 -11
- data/lib/wrest/native/post_multipart.rb +22 -18
- data/lib/wrest/native/put.rb +16 -12
- data/lib/wrest/native/put_multipart.rb +22 -18
- data/lib/wrest/native/redirection.rb +13 -12
- data/lib/wrest/native/request.rb +144 -108
- data/lib/wrest/native/response.rb +87 -78
- data/lib/wrest/native/session.rb +49 -40
- data/lib/wrest/native.rb +14 -12
- data/lib/wrest/test/request_patches.rb +10 -3
- data/lib/wrest/test.rb +3 -1
- data/lib/wrest/uri/builders.rb +14 -12
- data/lib/wrest/uri.rb +70 -52
- data/lib/wrest/uri_template.rb +11 -7
- data/lib/wrest/utils.rb +129 -0
- data/lib/wrest/version.rb +3 -1
- data/lib/wrest.rb +31 -33
- data/lib/wrest_no_ext.rb +2 -0
- metadata +98 -48
- data/lib/wrest/components/mutators/xml_simple_type_caster.rb +0 -37
- data/lib/wrest/xml_mini/jdom/xpath_filter.rb +0 -17
- data/lib/wrest/xml_mini/jdom.rb +0 -6
- data/lib/wrest/xml_mini/libxml/xpath_filter.rb +0 -12
- data/lib/wrest/xml_mini/libxml.rb +0 -8
- data/lib/wrest/xml_mini/nokogiri/xpath_filter.rb +0 -15
- data/lib/wrest/xml_mini/nokogiri.rb +0 -7
- data/lib/wrest/xml_mini/rexml/xpath_filter.rb +0 -15
- data/lib/wrest/xml_mini/rexml.rb +0 -8
- data/lib/wrest/xml_mini.rb +0 -8
data/lib/wrest/native/request.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Copyright 2009 Sidu Ponnappa
|
2
4
|
|
3
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
@@ -7,119 +9,153 @@
|
|
7
9
|
# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
8
10
|
# See the License for the specific language governing permissions and limitations under the License.
|
9
11
|
|
10
|
-
module Wrest
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
# :username => String, defaults to nil
|
20
|
-
# :password => String, defaults to nil
|
21
|
-
# :follow_redirects => Boolean, defaults to true for Get, false for anything else
|
22
|
-
# :follow_redirects_limit => Integer, defaults to 5. This is the number of redirects
|
23
|
-
# that Wrest will automatically follow before raising an
|
24
|
-
# Wrest::Exceptions::AutoRedirectLimitExceeded exception.
|
25
|
-
# For example, if you set this to 1, the very first redirect
|
26
|
-
# will raise the exception.
|
27
|
-
# :follow_redirects_count => Integer, defaults to 0. This is a count of the hops made to
|
28
|
-
# get to this request and increases by one for every redirect
|
29
|
-
# until the follow_redirects_limit is hit. You should never set
|
30
|
-
# this option yourself.
|
31
|
-
# :timeout => The period, in seconds, after which a Timeout::Error is raised
|
32
|
-
# in the event of a connection failing to open. Defaulted to 60 by Uri#create_connection.
|
33
|
-
# :connection => The HTTP Connection object to use. This is how a keep-alive connection can be
|
34
|
-
# used for multiple requests.
|
35
|
-
# :cache_store => The object which should be used as cache store for cacheable responses. If not supplied, caching will be disabled.
|
36
|
-
# :callback => A Hash whose keys are the response codes (or Range of response codes),
|
37
|
-
# and the values are the callback functions to be executed.
|
38
|
-
# eg: { <response code> => lambda { |response| some_operation } }
|
39
|
-
# The following options are Net::HTTP specific config options
|
40
|
-
# :detailed_http_logging => nil/$stdout/$stderr or File/Logger/IO object. Defaults to nil (recommended).
|
41
|
-
# *WARNING* : detailed_http_logging causes a serious security hole. Never use it in production code.
|
42
|
-
# :verify_mode => The verification mode to be used for Net::HTTP https connections. Defaults to OpenSSL::SSL::VERIFY_PEER
|
43
|
-
# :ca_path => The path to the certificates
|
44
|
-
def initialize(wrest_uri, http_request_klass, parameters = {}, body = nil, headers = {}, options = {})
|
45
|
-
@uri = wrest_uri
|
46
|
-
@headers = headers.stringify_keys
|
47
|
-
@parameters = parameters
|
48
|
-
@body = body
|
49
|
-
@options = options.clone
|
50
|
-
@username = @options[:username]
|
51
|
-
@password = @options[:password]
|
52
|
-
@follow_redirects = (@options[:follow_redirects] ||= false)
|
53
|
-
@follow_redirects_count = (@options[:follow_redirects_count] ||= 0)
|
54
|
-
@follow_redirects_limit = (@options[:follow_redirects_limit] ||= 5)
|
55
|
-
@timeout = @options[:timeout]
|
56
|
-
@connection = @options[:connection]
|
57
|
-
@http_request = self.build_request(http_request_klass, @uri, @parameters, @headers)
|
58
|
-
@cache_store = @options[:cache_store]
|
59
|
-
@verify_mode = @options[:verify_mode]
|
60
|
-
@ca_path = @options[:ca_path]
|
61
|
-
@detailed_http_logging = options[:detailed_http_logging]
|
62
|
-
@callback = @options[:callback] || Wrest::Callback.new({})
|
63
|
-
@callback = @callback.merge(Wrest::Callback.new(@options[:callback_block] || {}))
|
64
|
-
end
|
12
|
+
module Wrest
|
13
|
+
module Native
|
14
|
+
# This represents a HTTP request. Typically you will never need to instantiate
|
15
|
+
# one of these yourself - you can use one of the more conveient APIs via Wrest::Uri
|
16
|
+
# or Wrest::Native::Get etc. instead.
|
17
|
+
class Request
|
18
|
+
attr_reader :http_request, :uri, :body, :headers, :username, :password, :follow_redirects,
|
19
|
+
:follow_redirects_limit, :follow_redirects_count, :timeout, :connection, :parameters,
|
20
|
+
:cache_store, :verify_mode, :options, :ca_path
|
65
21
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
22
|
+
# Valid tuples for the options are:
|
23
|
+
# :username => String, defaults to nil
|
24
|
+
# :password => String, defaults to nil
|
25
|
+
# :follow_redirects => Boolean, defaults to true for Get, false for anything else
|
26
|
+
# :follow_redirects_limit => Integer, defaults to 5. This is the number of redirects
|
27
|
+
# that Wrest will automatically follow before raising an
|
28
|
+
# Wrest::Exceptions::AutoRedirectLimitExceeded exception.
|
29
|
+
# For example, if you set this to 1, the very first redirect
|
30
|
+
# will raise the exception.
|
31
|
+
# :follow_redirects_count => Integer, defaults to 0. This is a count of the hops made to
|
32
|
+
# get to this request and increases by one for every redirect
|
33
|
+
# until the follow_redirects_limit is hit. You should never set
|
34
|
+
# this option yourself.
|
35
|
+
# :timeout => The period, in seconds, after which a Timeout::Error is raised
|
36
|
+
# in the event of a connection failing to open. Defaulted to 60 by Uri#create_connection.
|
37
|
+
# :connection => The HTTP Connection object to use. This is how a keep-alive connection can be
|
38
|
+
# used for multiple requests.
|
39
|
+
# :cache_store => The object which should be used as cache store for cacheable responses. If not supplied, caching will be disabled.
|
40
|
+
# :callback => A Hash whose keys are the response codes (or Range of response codes),
|
41
|
+
# and the values are the callback functions to be executed.
|
42
|
+
# eg: { <response code> => lambda { |response| some_operation } }
|
43
|
+
# The following options are Net::HTTP specific config options
|
44
|
+
# :detailed_http_logging => nil/$stdout/$stderr or File/Logger/IO object. Defaults to nil (recommended).
|
45
|
+
# *WARNING* : detailed_http_logging causes a serious security hole. Never use it in production code.
|
46
|
+
# :verify_mode => The verification mode to be used for Net::HTTP https connections. Defaults to OpenSSL::SSL::VERIFY_PEER
|
47
|
+
# :ca_path => The path to the certificates
|
48
|
+
def initialize(wrest_uri, http_request_klass, parameters = {}, body = nil, headers = {}, options = {}) # rubocop:disable Metrics/ParameterLists
|
49
|
+
setup_request_state!(body, headers, parameters, wrest_uri)
|
50
|
+
setup_options_state!(options)
|
51
|
+
@detailed_http_logging = options[:detailed_http_logging]
|
52
|
+
@follow_redirects = (@options[:follow_redirects] ||= false)
|
53
|
+
@follow_redirects_count = (@options[:follow_redirects_count] ||= 0)
|
54
|
+
@follow_redirects_limit = (@options[:follow_redirects_limit] ||= 5)
|
55
|
+
@callback = @options[:callback] || Wrest::Callback.new({})
|
56
|
+
@callback = @callback.merge(Wrest::Callback.new(@options[:callback_block] || {}))
|
57
|
+
@http_request = build_request(http_request_klass, @uri, @parameters, @headers)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Makes a request, runs the appropriate callback if any and
|
61
|
+
# returns a Wrest::Native::Response.
|
62
|
+
#
|
63
|
+
# Data about the request is and logged to Wrest.logger
|
64
|
+
# The log entry contains the following information:
|
65
|
+
#
|
66
|
+
# <- indicates a request
|
67
|
+
# -> indicates a response
|
68
|
+
#
|
69
|
+
# The type of request is mentioned in caps, followed by a hash
|
70
|
+
# uniquely identifying a particular request/response pair.
|
71
|
+
# In a multi-process or multi-threaded scenario, this can be used
|
72
|
+
# to identify request-response pairs.
|
73
|
+
#
|
74
|
+
# The request hash is followed by a connection hash; requests using the
|
75
|
+
# same connection (effectively a keep-alive connection) will have the
|
76
|
+
# same connection hash.
|
77
|
+
#
|
78
|
+
# Passing nil for either username or password will skip HTTP authentication
|
79
|
+
#
|
80
|
+
# This is followed by the response code, the payload size and the time taken.
|
81
|
+
def invoke
|
82
|
+
response = nil
|
83
|
+
setup_connection!
|
84
|
+
|
85
|
+
response = execute_request(response)
|
86
|
+
|
87
|
+
execute_callback_if_any(response)
|
105
88
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
request_klass.new(parameters.empty? ? "#{uri.uri_path}?#{uri.query}" : "#{uri.uri_path}?#{uri.query}&#{parameters.to_query}", headers)
|
110
|
-
else
|
111
|
-
request_klass.new(parameters.empty? ? "#{uri.uri_path}" : "#{uri.uri_path}?#{parameters.to_query}", headers)
|
89
|
+
@follow_redirects ? response.follow(@options) : response
|
90
|
+
rescue Timeout::Error => e
|
91
|
+
raise Wrest::Exceptions::Timeout, e
|
112
92
|
end
|
113
|
-
end
|
114
93
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
94
|
+
# :nodoc:
|
95
|
+
def build_request(request_klass, uri, parameters, headers)
|
96
|
+
if uri.query.empty?
|
97
|
+
request_klass.new(parameters.empty? ? uri.uri_path.to_s : "#{uri.uri_path}?#{Utils.hash_to_param(parameters)}", headers)
|
98
|
+
else
|
99
|
+
request_klass.new(
|
100
|
+
parameters.empty? ? "#{uri.uri_path}?#{uri.query}" : "#{uri.uri_path}?#{uri.query}&#{Utils.hash_to_param(parameters)}", headers
|
101
|
+
)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# :nodoc:
|
106
|
+
def do_request
|
107
|
+
@connection.request(@http_request, @body)
|
108
|
+
end
|
109
|
+
|
110
|
+
# :nodoc:
|
111
|
+
def execute_callback_if_any(actual_response)
|
112
|
+
@callback.execute(actual_response)
|
113
|
+
end
|
119
114
|
|
120
|
-
|
121
|
-
|
122
|
-
|
115
|
+
private
|
116
|
+
|
117
|
+
def setup_connection!
|
118
|
+
@connection ||= @uri.create_connection(timeout: timeout, verify_mode: verify_mode, ca_path: ca_path)
|
119
|
+
@connection.set_debug_output @detailed_http_logging
|
120
|
+
http_request.basic_auth(username, password) unless username.nil? || password.nil?
|
121
|
+
end
|
122
|
+
|
123
|
+
def execute_request(response)
|
124
|
+
prefix = "#{http_request.method} #{hash} #{@connection.hash} #{Thread.current.object_id}"
|
125
|
+
|
126
|
+
log_before_request(prefix)
|
127
|
+
time = Benchmark.realtime { response = Wrest::Native::Response.new(do_request) }
|
128
|
+
log_after_request(prefix, time)
|
129
|
+
|
130
|
+
response
|
131
|
+
end
|
132
|
+
|
133
|
+
def log_after_request(prefix, time)
|
134
|
+
Wrest.logger.debug "<- (#{prefix}) Time: #{time}"
|
135
|
+
end
|
136
|
+
|
137
|
+
def log_before_request(prefix)
|
138
|
+
Wrest.logger.debug "<- (#{prefix}) #{@uri.protocol}://#{@uri.host}:#{@uri.port}#{@http_request.path}"
|
139
|
+
Wrest.logger.debug "<- (#{prefix}) Body: #{@body}"
|
140
|
+
end
|
141
|
+
|
142
|
+
def setup_request_state!(body, headers, parameters, wrest_uri)
|
143
|
+
@uri = wrest_uri
|
144
|
+
@headers = headers.transform_keys(&:to_s)
|
145
|
+
@parameters = parameters
|
146
|
+
@body = body
|
147
|
+
end
|
148
|
+
|
149
|
+
def setup_options_state!(options)
|
150
|
+
@options = options.clone
|
151
|
+
@username = @options[:username]
|
152
|
+
@password = @options[:password]
|
153
|
+
@timeout = @options[:timeout]
|
154
|
+
@connection = @options[:connection]
|
155
|
+
@cache_store = @options[:cache_store]
|
156
|
+
@verify_mode = @options[:verify_mode]
|
157
|
+
@ca_path = @options[:ca_path]
|
158
|
+
end
|
123
159
|
end
|
124
160
|
end
|
125
161
|
end
|
@@ -1,14 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Copyright 2009 Sidu Ponnappa
|
2
4
|
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
-
# you may not use this file except in compliance with the License.
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
5
7
|
# You may obtain a copy of the License at Http://www.apache.org/licenses/LICENSE-2.0
|
6
|
-
# Unless required by applicable law or agreed to in writing, software distributed under the License
|
7
|
-
# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
8
|
-
# See the License for the specific language governing permissions and limitations under the License.
|
9
|
-
require
|
10
|
-
module Wrest
|
11
|
-
module Native
|
8
|
+
# Unless required by applicable law or agreed to in writing, software distributed under the License
|
9
|
+
# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
10
|
+
# See the License for the specific language governing permissions and limitations under the License.
|
11
|
+
require 'rexml/document'
|
12
|
+
module Wrest # :nodoc:
|
13
|
+
module Native # :nodoc:
|
12
14
|
# Decorates a response providing support for deserialisation.
|
13
15
|
#
|
14
16
|
# The following methods are also available (unlisted by rdoc because they're forwarded to Net::HTTP::Response):
|
@@ -25,11 +27,12 @@ module Wrest #:nodoc:
|
|
25
27
|
class Response
|
26
28
|
attr_reader :http_response
|
27
29
|
attr_accessor :deserialised_body
|
30
|
+
|
28
31
|
include HttpCodes
|
29
|
-
|
32
|
+
|
30
33
|
extend Forwardable
|
31
|
-
def_delegators
|
32
|
-
|
34
|
+
def_delegators :@http_response, :code, :message, :body, :http_version,
|
35
|
+
:content_length, :content_type
|
33
36
|
|
34
37
|
def_delegators :headers, :[]
|
35
38
|
|
@@ -42,7 +45,7 @@ module Wrest #:nodoc:
|
|
42
45
|
# on the response code.
|
43
46
|
def self.new(http_response)
|
44
47
|
code = http_response.code.to_i
|
45
|
-
instance = ((300..303).include?(code) || (305..399).include?(code)
|
48
|
+
instance = ((300..303).include?(code) || (305..399).include?(code) ? Wrest::Native::Redirection : self).allocate
|
46
49
|
instance.send :initialize, http_response
|
47
50
|
instance
|
48
51
|
end
|
@@ -57,32 +60,29 @@ module Wrest #:nodoc:
|
|
57
60
|
|
58
61
|
# Checks equality between two Wrest::Native::Response objects.
|
59
62
|
def ==(other)
|
60
|
-
return true if
|
63
|
+
return true if equal?(other)
|
61
64
|
return false unless other.class == self.class
|
62
|
-
return true if
|
63
|
-
|
64
|
-
self.http_version == other.http_version and
|
65
|
-
self.message == other.message and
|
66
|
-
self.body == other.body
|
65
|
+
return true if these_fields_are_equal(other)
|
66
|
+
|
67
67
|
false
|
68
68
|
end
|
69
69
|
|
70
70
|
# Return the hash of a Wrest::Native::Response object.
|
71
71
|
def hash
|
72
|
-
|
72
|
+
[code, message, headers, http_version, body].hash
|
73
73
|
end
|
74
74
|
|
75
|
-
|
76
75
|
def deserialise(options = {})
|
77
|
-
@deserialised_body ||= deserialise_using(Wrest::Components::Translators.lookup(@http_response.content_type),
|
76
|
+
@deserialised_body ||= deserialise_using(Wrest::Components::Translators.lookup(@http_response.content_type),
|
77
|
+
options)
|
78
78
|
end
|
79
79
|
|
80
80
|
def deserialize(options = {})
|
81
81
|
deserialise(options)
|
82
82
|
end
|
83
83
|
|
84
|
-
def deserialise_using(translator,options = {})
|
85
|
-
translator.deserialise(@http_response,options)
|
84
|
+
def deserialise_using(translator, options = {})
|
85
|
+
translator.deserialise(@http_response, options)
|
86
86
|
end
|
87
87
|
|
88
88
|
def deserialize_using(options = {})
|
@@ -93,13 +93,11 @@ module Wrest #:nodoc:
|
|
93
93
|
def headers
|
94
94
|
return @headers if @headers
|
95
95
|
|
96
|
-
nethttp_headers_with_string_values
|
97
|
-
|
98
|
-
|
99
|
-
}
|
100
|
-
|
101
|
-
@headers=Wrest::HashWithCaseInsensitiveAccess.new(nethttp_headers_with_string_values)
|
96
|
+
nethttp_headers_with_string_values = @http_response.to_hash.transform_values do |old_value|
|
97
|
+
old_value.is_a?(Array) ? old_value.join(',') : old_value
|
98
|
+
end
|
102
99
|
|
100
|
+
@headers = Wrest::HashWithCaseInsensitiveAccess.new(nethttp_headers_with_string_values)
|
103
101
|
end
|
104
102
|
|
105
103
|
# A null object implementation - invoking this method on
|
@@ -107,7 +105,7 @@ module Wrest #:nodoc:
|
|
107
105
|
# the response is Redirection (code 3xx), in which case a
|
108
106
|
# get is invoked on the url stored in the response headers
|
109
107
|
# under the key 'location' and the new Response is returned.
|
110
|
-
def follow(
|
108
|
+
def follow(_redirect_request_options = {})
|
111
109
|
self
|
112
110
|
end
|
113
111
|
|
@@ -115,77 +113,74 @@ module Wrest #:nodoc:
|
|
115
113
|
self[Native::StandardHeaders::Connection].downcase == Native::StandardTokens::Close.downcase
|
116
114
|
end
|
117
115
|
|
118
|
-
# Returns whether this response is cacheable.
|
116
|
+
# Returns whether this response is cacheable.
|
119
117
|
def cacheable?
|
120
|
-
|
121
|
-
|
122
|
-
|
118
|
+
cache_configs_set? &&
|
119
|
+
(!max_age.nil? or (expires_not_in_our_past? && expires_not_in_its_past?)) && pragma_nocache_not_set? &&
|
120
|
+
vary_header_valid?
|
123
121
|
end
|
124
122
|
|
125
|
-
|
123
|
+
# :nodoc:
|
126
124
|
def code_cacheable?
|
127
|
-
!code.nil? &&
|
125
|
+
!code.nil? && [200, 203, 300, 301, 302, 304, 307].include?(code.to_i)
|
128
126
|
end
|
129
127
|
|
130
|
-
|
128
|
+
# :nodoc:
|
131
129
|
def vary_header_valid?
|
132
130
|
headers['vary'] != '*'
|
133
131
|
end
|
134
132
|
|
135
|
-
|
133
|
+
# :nodoc:
|
136
134
|
def max_age
|
137
135
|
return @max_age if @max_age
|
138
136
|
|
139
|
-
max_age
|
137
|
+
max_age = cache_control_headers.grep(/max-age/)
|
140
138
|
|
141
|
-
@max_age = unless max_age.empty?
|
142
|
-
max_age.first.split('=').last.to_i
|
143
|
-
else
|
144
|
-
nil
|
145
|
-
end
|
139
|
+
@max_age = (max_age.first.split('=').last.to_i unless max_age.empty?)
|
146
140
|
end
|
147
141
|
|
148
142
|
def no_cache_flag_not_set?
|
149
|
-
|
143
|
+
!cache_control_headers.include?('no-cache')
|
150
144
|
end
|
151
145
|
|
152
146
|
def no_store_flag_not_set?
|
153
|
-
|
147
|
+
!cache_control_headers.include?('no-store')
|
154
148
|
end
|
155
149
|
|
156
150
|
def pragma_nocache_not_set?
|
157
|
-
headers['pragma'].nil? || (
|
151
|
+
headers['pragma'].nil? || (!headers['pragma'].include? 'no-cache')
|
158
152
|
end
|
159
153
|
|
160
154
|
# Returns the Date from the response headers.
|
161
155
|
def response_date
|
162
156
|
return @response_date if @response_date
|
163
|
-
@response_date = parse_datefield(headers, "date")
|
164
|
-
end
|
165
157
|
|
158
|
+
@response_date = parse_datefield(headers, 'date')
|
159
|
+
end
|
166
160
|
|
167
161
|
# Returns the Expires date from the response headers.
|
168
162
|
def expires
|
169
163
|
return @expires if @expires
|
170
|
-
|
164
|
+
|
165
|
+
@expires = parse_datefield(headers, 'expires')
|
171
166
|
end
|
172
167
|
|
173
|
-
# Returns whether the Expires header of this response is earlier than current time.
|
168
|
+
# Returns whether the Expires header of this response is earlier than current time.
|
174
169
|
def expires_not_in_our_past?
|
175
170
|
if expires.nil?
|
176
171
|
false
|
177
172
|
else
|
178
|
-
expires
|
173
|
+
Utils.datetime_to_i(expires) > Time.now.to_i
|
179
174
|
end
|
180
175
|
end
|
181
176
|
|
182
177
|
# Is the Expires of this response earlier than its Date header.
|
183
178
|
def expires_not_in_its_past?
|
184
179
|
# Invalid header value for Date or Expires means the response is not cacheable
|
185
|
-
if
|
180
|
+
if expires.nil? || response_date.nil?
|
186
181
|
false
|
187
182
|
else
|
188
|
-
|
183
|
+
expires > response_date
|
189
184
|
end
|
190
185
|
end
|
191
186
|
|
@@ -194,10 +189,14 @@ module Wrest #:nodoc:
|
|
194
189
|
current_time = Time.now.to_i
|
195
190
|
|
196
191
|
# RFC 2616 13.2.3 Age Calculations. TODO: include response_delay in the calculation as defined in RFC. For this, include original Request with Response.
|
197
|
-
date_value
|
198
|
-
|
192
|
+
date_value = begin
|
193
|
+
Utils.datetime_to_i(DateTime.parse(headers['date']))
|
194
|
+
rescue StandardError
|
195
|
+
current_time
|
196
|
+
end
|
197
|
+
age_value = headers['age'].to_i || 0
|
199
198
|
|
200
|
-
apparent_age
|
199
|
+
apparent_age = current_time - date_value
|
201
200
|
|
202
201
|
[apparent_age, age_value].max
|
203
202
|
end
|
@@ -207,9 +206,11 @@ module Wrest #:nodoc:
|
|
207
206
|
@cache_control_headers ||= recalculate_cache_control_headers
|
208
207
|
end
|
209
208
|
|
210
|
-
|
209
|
+
# :nodoc:
|
211
210
|
def recalculate_cache_control_headers
|
212
|
-
headers['cache-control'].split(
|
211
|
+
headers['cache-control'].split(',').collect(&:strip)
|
212
|
+
rescue StandardError
|
213
|
+
[]
|
213
214
|
end
|
214
215
|
|
215
216
|
# How long (in seconds) is this response expected to be fresh
|
@@ -217,22 +218,20 @@ module Wrest #:nodoc:
|
|
217
218
|
@freshness_lifetime ||= recalculate_freshness_lifetime
|
218
219
|
end
|
219
220
|
|
220
|
-
|
221
|
+
# :nodoc:
|
221
222
|
def recalculate_freshness_lifetime
|
222
223
|
return max_age if max_age
|
223
224
|
|
224
|
-
response_date = DateTime.parse(headers['date'])
|
225
|
-
expires_date
|
225
|
+
response_date = Utils.datetime_to_i(DateTime.parse(headers['date']))
|
226
|
+
expires_date = Utils.datetime_to_i(DateTime.parse(headers['expires']))
|
226
227
|
|
227
|
-
|
228
|
+
(expires_date - response_date)
|
228
229
|
end
|
229
230
|
|
230
231
|
# Has this response expired? The expiry is calculated from the Max-Age/Expires header.
|
231
232
|
def expired?
|
232
|
-
freshness=freshness_lifetime
|
233
|
-
if freshness <= 0
|
234
|
-
return true
|
235
|
-
end
|
233
|
+
freshness = freshness_lifetime
|
234
|
+
return true if freshness <= 0
|
236
235
|
|
237
236
|
freshness <= current_age
|
238
237
|
end
|
@@ -244,26 +243,36 @@ module Wrest #:nodoc:
|
|
244
243
|
# Can this response be validated by sending a validation request to the server. The response need to have either
|
245
244
|
# Last-Modified or ETag header (or both) for it to be validatable.
|
246
245
|
def can_be_validated?
|
247
|
-
|
246
|
+
!(last_modified.nil? and headers['etag'].nil?)
|
248
247
|
end
|
249
248
|
|
250
|
-
|
251
|
-
#:nodoc:
|
249
|
+
# :nodoc:
|
252
250
|
# helper function. Used to parse date fields.
|
253
251
|
# this function is used and tested by the expires and response_date methods
|
254
252
|
def parse_datefield(hash, key)
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
end
|
262
|
-
else
|
253
|
+
return unless hash[key]
|
254
|
+
|
255
|
+
# Can't trust external input. Do not crash even if invalid dates are passed.
|
256
|
+
begin
|
257
|
+
DateTime.parse(hash[key].to_s)
|
258
|
+
rescue ArgumentError
|
263
259
|
nil
|
264
260
|
end
|
265
261
|
end
|
266
262
|
|
263
|
+
private
|
264
|
+
|
265
|
+
def cache_configs_set?
|
266
|
+
code_cacheable? && no_cache_flag_not_set? && no_store_flag_not_set?
|
267
|
+
end
|
268
|
+
|
269
|
+
def these_fields_are_equal(other)
|
270
|
+
(code == other.code) &&
|
271
|
+
(headers == other.headers) &&
|
272
|
+
(http_version == other.http_version) &&
|
273
|
+
(message == other.message) &&
|
274
|
+
(body == other.body)
|
275
|
+
end
|
267
276
|
end
|
268
277
|
end
|
269
278
|
end
|