wrest 4.0.0-universal-java-18
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 +7 -0
- data/CHANGELOG +169 -0
- data/LICENCE +7 -0
- data/README.md +436 -0
- data/bin/wrest +4 -0
- data/bin/wrest_shell.rb +23 -0
- data/lib/wrest/async_request/event_machine_backend.rb +32 -0
- data/lib/wrest/async_request/thread_backend.rb +34 -0
- data/lib/wrest/async_request/thread_pool.rb +29 -0
- data/lib/wrest/async_request.rb +51 -0
- data/lib/wrest/cache_proxy.rb +119 -0
- data/lib/wrest/caching/memcached.rb +37 -0
- data/lib/wrest/caching/redis.rb +38 -0
- data/lib/wrest/caching.rb +57 -0
- data/lib/wrest/callback.rb +70 -0
- data/lib/wrest/components/container/alias_accessors.rb +70 -0
- data/lib/wrest/components/container/typecaster.rb +178 -0
- data/lib/wrest/components/container.rb +204 -0
- data/lib/wrest/components/mutators/base.rb +65 -0
- data/lib/wrest/components/mutators/camel_to_snake_case.rb +26 -0
- data/lib/wrest/components/mutators/xml_type_caster.rb +56 -0
- data/lib/wrest/components/mutators.rb +42 -0
- data/lib/wrest/components/translators/content_types.rb +25 -0
- data/lib/wrest/components/translators/json.rb +36 -0
- data/lib/wrest/components/translators/txt.rb +35 -0
- data/lib/wrest/components/translators/xml/conversions.rb +56 -0
- data/lib/wrest/components/translators/xml.rb +77 -0
- data/lib/wrest/components/translators.rb +30 -0
- data/lib/wrest/components.rb +22 -0
- data/lib/wrest/core_ext/hash/conversions.rb +45 -0
- data/lib/wrest/core_ext/hash.rb +7 -0
- data/lib/wrest/core_ext/string/conversions.rb +38 -0
- data/lib/wrest/core_ext/string.rb +7 -0
- data/lib/wrest/exceptions.rb +38 -0
- data/lib/wrest/hash_with_case_insensitive_access.rb +52 -0
- data/lib/wrest/hash_with_indifferent_access.rb +442 -0
- data/lib/wrest/http_codes.rb +83 -0
- data/lib/wrest/http_shared/headers.rb +345 -0
- data/lib/wrest/http_shared/standard_headers.rb +22 -0
- data/lib/wrest/http_shared/standard_tokens.rb +21 -0
- data/lib/wrest/http_shared.rb +25 -0
- data/lib/wrest/multipart.rb +84 -0
- data/lib/wrest/native/connection_factory.rb +28 -0
- data/lib/wrest/native/delete.rb +27 -0
- data/lib/wrest/native/get.rb +83 -0
- data/lib/wrest/native/options.rb +27 -0
- data/lib/wrest/native/patch.rb +27 -0
- data/lib/wrest/native/post.rb +27 -0
- data/lib/wrest/native/post_multipart.rb +36 -0
- data/lib/wrest/native/put.rb +27 -0
- data/lib/wrest/native/put_multipart.rb +36 -0
- data/lib/wrest/native/redirection.rb +39 -0
- data/lib/wrest/native/request.rb +161 -0
- data/lib/wrest/native/response.rb +278 -0
- data/lib/wrest/native/session.rb +66 -0
- data/lib/wrest/native.rb +36 -0
- data/lib/wrest/test/request_patches.rb +12 -0
- data/lib/wrest/test.rb +3 -0
- data/lib/wrest/uri/builders.rb +48 -0
- data/lib/wrest/uri.rb +312 -0
- data/lib/wrest/uri_template.rb +63 -0
- data/lib/wrest/utils.rb +129 -0
- data/lib/wrest/version.rb +14 -0
- data/lib/wrest.rb +77 -0
- data/lib/wrest_no_ext.rb +7 -0
- metadata +286 -0
@@ -0,0 +1,278 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2009 Sidu Ponnappa
|
4
|
+
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at Http://www.apache.org/licenses/LICENSE-2.0
|
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:
|
14
|
+
# Decorates a response providing support for deserialisation.
|
15
|
+
#
|
16
|
+
# The following methods are also available (unlisted by rdoc because they're forwarded to Net::HTTP::Response):
|
17
|
+
#
|
18
|
+
# <tt>:@Http_response, :code, :message, :body, :Http_version,
|
19
|
+
# :[], :content_length, :content_type, :each_header, :each_name, :each_value, :fetch,
|
20
|
+
# :get_fields, :key?, :type_params</tt>
|
21
|
+
#
|
22
|
+
# They behave exactly like their Net::HttpResponse equivalents.
|
23
|
+
#
|
24
|
+
# Also provides set of HTTP response code checkers. For instance, the method ok? checks if the response was
|
25
|
+
# successful with HTTP code 200.
|
26
|
+
# See HttpCodes for a list of all such response checkers.
|
27
|
+
class Response
|
28
|
+
attr_reader :http_response
|
29
|
+
attr_accessor :deserialised_body
|
30
|
+
|
31
|
+
include HttpCodes
|
32
|
+
|
33
|
+
extend Forwardable
|
34
|
+
def_delegators :@http_response, :code, :message, :body, :http_version,
|
35
|
+
:content_length, :content_type
|
36
|
+
|
37
|
+
def_delegators :headers, :[]
|
38
|
+
|
39
|
+
# TODO : Are these needed in the Response namespace itself? Can be accessed from the headers method.
|
40
|
+
def_delegators :@http_response, :each_header, :each_name, :each_value, :fetch,
|
41
|
+
:get_fields, :key?, :type_params
|
42
|
+
|
43
|
+
# We're overriding :new to act as a factory so
|
44
|
+
# we can build the appropriate Response instance based
|
45
|
+
# on the response code.
|
46
|
+
def self.new(http_response)
|
47
|
+
code = http_response.code.to_i
|
48
|
+
instance = ((300..303).include?(code) || (305..399).include?(code) ? Wrest::Native::Redirection : self).allocate
|
49
|
+
instance.send :initialize, http_response
|
50
|
+
instance
|
51
|
+
end
|
52
|
+
|
53
|
+
def initialize(http_response)
|
54
|
+
@http_response = http_response
|
55
|
+
end
|
56
|
+
|
57
|
+
def initialize_copy(source)
|
58
|
+
@headers = source.headers.clone
|
59
|
+
end
|
60
|
+
|
61
|
+
# Checks equality between two Wrest::Native::Response objects.
|
62
|
+
def ==(other)
|
63
|
+
return true if equal?(other)
|
64
|
+
return false unless other.class == self.class
|
65
|
+
return true if these_fields_are_equal(other)
|
66
|
+
|
67
|
+
false
|
68
|
+
end
|
69
|
+
|
70
|
+
# Return the hash of a Wrest::Native::Response object.
|
71
|
+
def hash
|
72
|
+
[code, message, headers, http_version, body].hash
|
73
|
+
end
|
74
|
+
|
75
|
+
def deserialise(options = {})
|
76
|
+
@deserialised_body ||= deserialise_using(Wrest::Components::Translators.lookup(@http_response.content_type),
|
77
|
+
options)
|
78
|
+
end
|
79
|
+
|
80
|
+
def deserialize(options = {})
|
81
|
+
deserialise(options)
|
82
|
+
end
|
83
|
+
|
84
|
+
def deserialise_using(translator, options = {})
|
85
|
+
translator.deserialise(@http_response, options)
|
86
|
+
end
|
87
|
+
|
88
|
+
def deserialize_using(options = {})
|
89
|
+
deserialise_using(options)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Gives a hash of the response headers. The keys of the hash are case-insensitive.
|
93
|
+
def headers
|
94
|
+
return @headers if @headers
|
95
|
+
|
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
|
99
|
+
|
100
|
+
@headers = Wrest::HashWithCaseInsensitiveAccess.new(nethttp_headers_with_string_values)
|
101
|
+
end
|
102
|
+
|
103
|
+
# A null object implementation - invoking this method on
|
104
|
+
# a response simply returns the same response unless
|
105
|
+
# the response is Redirection (code 3xx), in which case a
|
106
|
+
# get is invoked on the url stored in the response headers
|
107
|
+
# under the key 'location' and the new Response is returned.
|
108
|
+
def follow(_redirect_request_options = {})
|
109
|
+
self
|
110
|
+
end
|
111
|
+
|
112
|
+
def connection_closed?
|
113
|
+
self[Native::StandardHeaders::Connection].downcase == Native::StandardTokens::Close.downcase
|
114
|
+
end
|
115
|
+
|
116
|
+
# Returns whether this response is cacheable.
|
117
|
+
def cacheable?
|
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?
|
121
|
+
end
|
122
|
+
|
123
|
+
# :nodoc:
|
124
|
+
def code_cacheable?
|
125
|
+
!code.nil? && [200, 203, 300, 301, 302, 304, 307].include?(code.to_i)
|
126
|
+
end
|
127
|
+
|
128
|
+
# :nodoc:
|
129
|
+
def vary_header_valid?
|
130
|
+
headers['vary'] != '*'
|
131
|
+
end
|
132
|
+
|
133
|
+
# :nodoc:
|
134
|
+
def max_age
|
135
|
+
return @max_age if @max_age
|
136
|
+
|
137
|
+
max_age = cache_control_headers.grep(/max-age/)
|
138
|
+
|
139
|
+
@max_age = (max_age.first.split('=').last.to_i unless max_age.empty?)
|
140
|
+
end
|
141
|
+
|
142
|
+
def no_cache_flag_not_set?
|
143
|
+
!cache_control_headers.include?('no-cache')
|
144
|
+
end
|
145
|
+
|
146
|
+
def no_store_flag_not_set?
|
147
|
+
!cache_control_headers.include?('no-store')
|
148
|
+
end
|
149
|
+
|
150
|
+
def pragma_nocache_not_set?
|
151
|
+
headers['pragma'].nil? || (!headers['pragma'].include? 'no-cache')
|
152
|
+
end
|
153
|
+
|
154
|
+
# Returns the Date from the response headers.
|
155
|
+
def response_date
|
156
|
+
return @response_date if @response_date
|
157
|
+
|
158
|
+
@response_date = parse_datefield(headers, 'date')
|
159
|
+
end
|
160
|
+
|
161
|
+
# Returns the Expires date from the response headers.
|
162
|
+
def expires
|
163
|
+
return @expires if @expires
|
164
|
+
|
165
|
+
@expires = parse_datefield(headers, 'expires')
|
166
|
+
end
|
167
|
+
|
168
|
+
# Returns whether the Expires header of this response is earlier than current time.
|
169
|
+
def expires_not_in_our_past?
|
170
|
+
if expires.nil?
|
171
|
+
false
|
172
|
+
else
|
173
|
+
Utils.datetime_to_i(expires) > Time.now.to_i
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# Is the Expires of this response earlier than its Date header.
|
178
|
+
def expires_not_in_its_past?
|
179
|
+
# Invalid header value for Date or Expires means the response is not cacheable
|
180
|
+
if expires.nil? || response_date.nil?
|
181
|
+
false
|
182
|
+
else
|
183
|
+
expires > response_date
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# Age of the response calculated according to RFC 2616 13.2.3
|
188
|
+
def current_age
|
189
|
+
current_time = Time.now.to_i
|
190
|
+
|
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.
|
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
|
198
|
+
|
199
|
+
apparent_age = current_time - date_value
|
200
|
+
|
201
|
+
[apparent_age, age_value].max
|
202
|
+
end
|
203
|
+
|
204
|
+
# The values in Cache-Control header as an array.
|
205
|
+
def cache_control_headers
|
206
|
+
@cache_control_headers ||= recalculate_cache_control_headers
|
207
|
+
end
|
208
|
+
|
209
|
+
# :nodoc:
|
210
|
+
def recalculate_cache_control_headers
|
211
|
+
headers['cache-control'].split(',').collect(&:strip)
|
212
|
+
rescue StandardError
|
213
|
+
[]
|
214
|
+
end
|
215
|
+
|
216
|
+
# How long (in seconds) is this response expected to be fresh
|
217
|
+
def freshness_lifetime
|
218
|
+
@freshness_lifetime ||= recalculate_freshness_lifetime
|
219
|
+
end
|
220
|
+
|
221
|
+
# :nodoc:
|
222
|
+
def recalculate_freshness_lifetime
|
223
|
+
return max_age if max_age
|
224
|
+
|
225
|
+
response_date = Utils.datetime_to_i(DateTime.parse(headers['date']))
|
226
|
+
expires_date = Utils.datetime_to_i(DateTime.parse(headers['expires']))
|
227
|
+
|
228
|
+
(expires_date - response_date)
|
229
|
+
end
|
230
|
+
|
231
|
+
# Has this response expired? The expiry is calculated from the Max-Age/Expires header.
|
232
|
+
def expired?
|
233
|
+
freshness = freshness_lifetime
|
234
|
+
return true if freshness <= 0
|
235
|
+
|
236
|
+
freshness <= current_age
|
237
|
+
end
|
238
|
+
|
239
|
+
def last_modified
|
240
|
+
headers['last-modified']
|
241
|
+
end
|
242
|
+
|
243
|
+
# Can this response be validated by sending a validation request to the server. The response need to have either
|
244
|
+
# Last-Modified or ETag header (or both) for it to be validatable.
|
245
|
+
def can_be_validated?
|
246
|
+
!(last_modified.nil? and headers['etag'].nil?)
|
247
|
+
end
|
248
|
+
|
249
|
+
# :nodoc:
|
250
|
+
# helper function. Used to parse date fields.
|
251
|
+
# this function is used and tested by the expires and response_date methods
|
252
|
+
def parse_datefield(hash, key)
|
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
|
259
|
+
nil
|
260
|
+
end
|
261
|
+
end
|
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
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2009 Sidu Ponnappa
|
4
|
+
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
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
|
+
|
12
|
+
module Wrest
|
13
|
+
module Native
|
14
|
+
# This class is a wrapper for a keep-alive HTTP connection. It simply passes the
|
15
|
+
# same connection instance as an option to all Wrest::Native::Request instances created using it.
|
16
|
+
#
|
17
|
+
# If at any point the server closes an existing connection during a Session by returning a
|
18
|
+
# Connection: Close header the current connection is destroyed and a fresh one created for the next
|
19
|
+
# request.
|
20
|
+
#
|
21
|
+
# The Session constructor can accept either a String URI or a Wrest::Uri as a parameter. If you
|
22
|
+
# need HTTP authentication, you should use a Wrest::Uri.
|
23
|
+
class Session
|
24
|
+
attr_reader :uri
|
25
|
+
|
26
|
+
def initialize(uri)
|
27
|
+
@uri = Wrest::Uri.new(uri)
|
28
|
+
@default_headers = { Wrest::Native::StandardHeaders::Connection => Wrest::Native::StandardTokens::KeepAlive }
|
29
|
+
|
30
|
+
yield(self) if block_given?
|
31
|
+
end
|
32
|
+
|
33
|
+
def connection
|
34
|
+
@connection ||= @uri.create_connection
|
35
|
+
end
|
36
|
+
|
37
|
+
def get(path = '', parameters = {}, headers = {})
|
38
|
+
maybe_destroy_connection @uri[path, { connection: connection }].get(parameters,
|
39
|
+
headers.merge(@default_headers))
|
40
|
+
end
|
41
|
+
|
42
|
+
def post(path = '', body = '', headers = {}, params = {})
|
43
|
+
maybe_destroy_connection @uri[path, { connection: connection }].post(body, headers.merge(@default_headers),
|
44
|
+
params)
|
45
|
+
end
|
46
|
+
|
47
|
+
def put(path = '', body = '', headers = {}, params = {})
|
48
|
+
maybe_destroy_connection @uri[path, { connection: connection }].put(body, headers.merge(@default_headers),
|
49
|
+
params)
|
50
|
+
end
|
51
|
+
|
52
|
+
def delete(path = '', parameters = {}, headers = {})
|
53
|
+
maybe_destroy_connection @uri[path, { connection: connection }].delete(parameters,
|
54
|
+
headers.merge(@default_headers))
|
55
|
+
end
|
56
|
+
|
57
|
+
def maybe_destroy_connection(response)
|
58
|
+
if response.connection_closed?
|
59
|
+
Wrest.logger.warn "Connection #{@connection.hash} has been closed by the server"
|
60
|
+
@connection = nil
|
61
|
+
end
|
62
|
+
response
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/lib/wrest/native.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2009 Sidu Ponnappa
|
4
|
+
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at native://www.apache.org/licenses/LICENSE-2.0
|
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
|
+
|
12
|
+
module Wrest
|
13
|
+
# Contains all native protocol related classes such as
|
14
|
+
# Get, Post, Request, Response etc. and uses the native
|
15
|
+
# Ruby Net::HTTP libraries.
|
16
|
+
#
|
17
|
+
# Wrest::Http points to this module by default.
|
18
|
+
module Native
|
19
|
+
include Wrest::HttpShared
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
require 'wrest/native/connection_factory'
|
24
|
+
require 'wrest/native/response'
|
25
|
+
require 'wrest/native/redirection'
|
26
|
+
require 'wrest/native/request'
|
27
|
+
require 'wrest/native/get'
|
28
|
+
require 'wrest/native/put'
|
29
|
+
require 'wrest/native/patch'
|
30
|
+
require 'wrest/native/post'
|
31
|
+
require 'wrest/native/delete'
|
32
|
+
require 'wrest/native/options'
|
33
|
+
require 'wrest/native/session'
|
34
|
+
|
35
|
+
# default to using Native libs
|
36
|
+
Wrest::Http = Wrest::Native
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wrest
|
4
|
+
module Native
|
5
|
+
class Request
|
6
|
+
def invoke
|
7
|
+
raise Wrest::Exceptions::RealRequestMadeInTestEnvironmet,
|
8
|
+
'A real HTTP request was made while running tests. Please avoid using live HTTP connections while testing and replace them with mocks.'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
data/lib/wrest/test.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Wrest
|
4
|
+
class Uri
|
5
|
+
# Contains methods that depend on Uri#clone to build
|
6
|
+
# new Uris configured in particular ways.
|
7
|
+
module Builders
|
8
|
+
# Returns a Uri object that uses threads to perform asynchronous requests.
|
9
|
+
def using_threads
|
10
|
+
clone(asynchronous_backend: Wrest::AsyncRequest::ThreadBackend.new)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns a Uri object that uses eventmachine to perform asynchronous requests.
|
14
|
+
# Remember to do Wrest::AsyncRequest.enable_em first so that
|
15
|
+
# EventMachine is available for use.
|
16
|
+
def using_em
|
17
|
+
clone(asynchronous_backend: Wrest::AsyncRequest::EventMachineBackend.new)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns a Uri object that uses hash for caching responses.
|
21
|
+
def using_hash
|
22
|
+
clone(cache_store: {})
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns a Uri object that uses memcached for caching responses.
|
26
|
+
# Remember to do Wrest::AsyncRequest.enable_memcached first so that
|
27
|
+
# memcached is available for use.
|
28
|
+
def using_memcached
|
29
|
+
clone(cache_store: Wrest::Caching::Memcached.new)
|
30
|
+
end
|
31
|
+
|
32
|
+
def using_redis
|
33
|
+
clone(cache_store: Wrest::Caching::Redis.new)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Disables using the globally configured cache for GET requests
|
37
|
+
# made using the Uri returned by this method.
|
38
|
+
def disable_cache
|
39
|
+
clone(disable_cache: true)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Sets the specified string as the cookie for the Uri
|
43
|
+
def using_cookie(cookie_string)
|
44
|
+
clone(default_headers: { Wrest::H::Cookie => cookie_string })
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|