wrest 3.0.0 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|