embark-journey 0.0.20 → 0.0.21
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 +4 -4
- data/CHANGELOG.md +3 -0
- data/lib/active_resource/connection.rb +285 -38
- data/lib/journey/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a5787f183545667833aa70931e85b09333bed067
|
4
|
+
data.tar.gz: 52ca2385cd5805ac574a76913539afddab4dafcc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 56f34fb17571bcd21a5cdde76b1f19e2ff07bb711d059f736405d1d5c327edda811cecc3357b139c5553245f452bb3f9018d7c39f10820db1c8fa55d77cc17e8
|
7
|
+
data.tar.gz: 10255614494e016c5f896adddc998849fe8fb67ad86b68e60bc7a8fdd8a744ae9b0c78f024aba16fe15f88fe16ea12719c234f04069f158ea064889639d6f780
|
data/CHANGELOG.md
CHANGED
@@ -1,47 +1,294 @@
|
|
1
|
-
require '
|
2
|
-
|
1
|
+
require 'active_support/core_ext/benchmark'
|
2
|
+
require 'active_support/core_ext/uri'
|
3
|
+
require 'active_support/core_ext/object/inclusion'
|
4
|
+
require 'net/https'
|
5
|
+
require 'date'
|
6
|
+
require 'time'
|
7
|
+
require 'uri'
|
3
8
|
|
4
9
|
module ActiveResource
|
10
|
+
# Class to handle connections to remote web services.
|
11
|
+
# This class is used by ActiveResource::Base to interface with REST
|
12
|
+
# services.
|
5
13
|
class Connection
|
6
14
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
when 400
|
22
|
-
raise(BadRequest.new(response))
|
23
|
-
when 401
|
24
|
-
raise(UnauthorizedAccess.new(response))
|
25
|
-
when 403
|
26
|
-
raise(ForbiddenAccess.new(response))
|
27
|
-
when 404
|
28
|
-
raise(ResourceNotFound.new(response))
|
29
|
-
when 405
|
30
|
-
raise(MethodNotAllowed.new(response))
|
31
|
-
when 409
|
32
|
-
raise(ResourceConflict.new(response))
|
33
|
-
when 410
|
34
|
-
raise(ResourceGone.new(response))
|
35
|
-
when 422
|
36
|
-
raise(ResourceInvalid.new(response))
|
37
|
-
when 401...500
|
38
|
-
raise(ClientError.new(response))
|
39
|
-
when 500...600
|
40
|
-
raise(ServerError.new(response))
|
41
|
-
else
|
42
|
-
raise(ConnectionError.new(response, "Unknown response code: #{response.code}"))
|
15
|
+
HTTP_FORMAT_HEADER_NAMES = { :get => 'Accept',
|
16
|
+
:put => 'Content-Type',
|
17
|
+
:post => 'Content-Type',
|
18
|
+
:patch => 'Content-Type',
|
19
|
+
:delete => 'Accept',
|
20
|
+
:head => 'Accept'
|
21
|
+
}
|
22
|
+
|
23
|
+
attr_reader :site, :user, :password, :auth_type, :timeout, :proxy, :ssl_options
|
24
|
+
attr_accessor :format
|
25
|
+
|
26
|
+
class << self
|
27
|
+
def requests
|
28
|
+
@@requests ||= []
|
43
29
|
end
|
44
30
|
end
|
45
31
|
|
32
|
+
# The +site+ parameter is required and will set the +site+
|
33
|
+
# attribute to the URI for the remote resource service.
|
34
|
+
def initialize(site, format = ActiveResource::Formats::JsonFormat)
|
35
|
+
raise ArgumentError, 'Missing site URI' unless site
|
36
|
+
@proxy = @user = @password = nil
|
37
|
+
self.site = site
|
38
|
+
self.format = format
|
39
|
+
end
|
40
|
+
|
41
|
+
# Set URI for remote service.
|
42
|
+
def site=(site)
|
43
|
+
@site = site.is_a?(URI) ? site : URI.parse(site)
|
44
|
+
@ssl_options ||= {} if @site.is_a?(URI::HTTPS)
|
45
|
+
@user = URI.parser.unescape(@site.user) if @site.user
|
46
|
+
@password = URI.parser.unescape(@site.password) if @site.password
|
47
|
+
end
|
48
|
+
|
49
|
+
# Set the proxy for remote service.
|
50
|
+
def proxy=(proxy)
|
51
|
+
@proxy = proxy.is_a?(URI) ? proxy : URI.parse(proxy)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Sets the user for remote service.
|
55
|
+
def user=(user)
|
56
|
+
@user = user
|
57
|
+
end
|
58
|
+
|
59
|
+
# Sets the password for remote service.
|
60
|
+
def password=(password)
|
61
|
+
@password = password
|
62
|
+
end
|
63
|
+
|
64
|
+
# Sets the auth type for remote service.
|
65
|
+
def auth_type=(auth_type)
|
66
|
+
@auth_type = legitimize_auth_type(auth_type)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Sets the number of seconds after which HTTP requests to the remote service should time out.
|
70
|
+
def timeout=(timeout)
|
71
|
+
@timeout = timeout
|
72
|
+
end
|
73
|
+
|
74
|
+
# Hash of options applied to Net::HTTP instance when +site+ protocol is 'https'.
|
75
|
+
def ssl_options=(options)
|
76
|
+
@ssl_options = options
|
77
|
+
end
|
78
|
+
|
79
|
+
# Executes a GET request.
|
80
|
+
# Used to get (find) resources.
|
81
|
+
def get(path, headers = {})
|
82
|
+
with_auth { request(:get, path, build_request_headers(headers, :get, self.site.merge(path))) }
|
83
|
+
end
|
84
|
+
|
85
|
+
# Executes a DELETE request (see HTTP protocol documentation if unfamiliar).
|
86
|
+
# Used to delete resources.
|
87
|
+
def delete(path, headers = {})
|
88
|
+
with_auth { request(:delete, path, build_request_headers(headers, :delete, self.site.merge(path))) }
|
89
|
+
end
|
90
|
+
|
91
|
+
# Executes a PATCH request (see HTTP protocol documentation if unfamiliar).
|
92
|
+
# Used to update resources.
|
93
|
+
def patch(path, body = '', headers = {})
|
94
|
+
with_auth { request(:patch, path, body.to_s, build_request_headers(headers, :patch, self.site.merge(path))) }
|
95
|
+
end
|
96
|
+
|
97
|
+
# Executes a PUT request (see HTTP protocol documentation if unfamiliar).
|
98
|
+
# Used to update resources.
|
99
|
+
def put(path, body = '', headers = {})
|
100
|
+
with_auth { request(:put, path, body.to_s, build_request_headers(headers, :put, self.site.merge(path))) }
|
101
|
+
end
|
102
|
+
|
103
|
+
# Executes a POST request.
|
104
|
+
# Used to create new resources.
|
105
|
+
def post(path, body = '', headers = {})
|
106
|
+
with_auth { request(:post, path, body.to_s, build_request_headers(headers, :post, self.site.merge(path))) }
|
107
|
+
end
|
108
|
+
|
109
|
+
# Executes a HEAD request.
|
110
|
+
# Used to obtain meta-information about resources, such as whether they exist and their size (via response headers).
|
111
|
+
def head(path, headers = {})
|
112
|
+
with_auth { request(:head, path, build_request_headers(headers, :head, self.site.merge(path))) }
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
# Makes a request to the remote service.
|
117
|
+
def request(method, path, *arguments)
|
118
|
+
result = ActiveSupport::Notifications.instrument("request.active_resource") do |payload|
|
119
|
+
payload[:method] = method
|
120
|
+
payload[:request_uri] = "#{site.scheme}://#{site.host}:#{site.port}#{path}"
|
121
|
+
payload[:result] = http.send(method, path, *arguments)
|
122
|
+
end
|
123
|
+
handle_response(result)
|
124
|
+
rescue Timeout::Error => e
|
125
|
+
raise TimeoutError.new(e.message)
|
126
|
+
rescue OpenSSL::SSL::SSLError => e
|
127
|
+
raise SSLError.new(e.message)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Handles response and error codes from the remote service.
|
131
|
+
def handle_response(response)
|
132
|
+
if response.respond_to?(:header) && (response.header["content-encoding"] == 'gzip')
|
133
|
+
begin
|
134
|
+
response.instance_variable_set('@body', ActiveSupport::Gzip.decompress(response.body))
|
135
|
+
rescue Exception => e
|
136
|
+
raise(BadRequest.new(response))
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
|
141
|
+
case response.code.to_i
|
142
|
+
when 301, 302, 303, 307
|
143
|
+
raise(Redirection.new(response))
|
144
|
+
when 200...400
|
145
|
+
response
|
146
|
+
when 400
|
147
|
+
raise(BadRequest.new(response))
|
148
|
+
when 401
|
149
|
+
raise(UnauthorizedAccess.new(response))
|
150
|
+
when 403
|
151
|
+
raise(ForbiddenAccess.new(response))
|
152
|
+
when 404
|
153
|
+
raise(ResourceNotFound.new(response))
|
154
|
+
when 405
|
155
|
+
raise(MethodNotAllowed.new(response))
|
156
|
+
when 409
|
157
|
+
raise(ResourceConflict.new(response))
|
158
|
+
when 410
|
159
|
+
raise(ResourceGone.new(response))
|
160
|
+
when 422
|
161
|
+
raise(ResourceInvalid.new(response))
|
162
|
+
when 401...500
|
163
|
+
raise(ClientError.new(response))
|
164
|
+
when 500...600
|
165
|
+
raise(ServerError.new(response))
|
166
|
+
else
|
167
|
+
raise(ConnectionError.new(response, "Unknown response code: #{response.code}"))
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# Creates new Net::HTTP instance for communication with the
|
172
|
+
# remote service and resources.
|
173
|
+
def http
|
174
|
+
configure_http(new_http)
|
175
|
+
end
|
176
|
+
|
177
|
+
def new_http
|
178
|
+
if @proxy
|
179
|
+
Net::HTTP.new(@site.host, @site.port, @proxy.host, @proxy.port, @proxy.user, @proxy.password)
|
180
|
+
else
|
181
|
+
Net::HTTP.new(@site.host, @site.port)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def configure_http(http)
|
186
|
+
apply_ssl_options(http).tap do |https|
|
187
|
+
# Net::HTTP timeouts default to 60 seconds.
|
188
|
+
if defined? @timeout
|
189
|
+
https.open_timeout = @timeout
|
190
|
+
https.read_timeout = @timeout
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def apply_ssl_options(http)
|
196
|
+
http.tap do |https|
|
197
|
+
# Skip config if site is already a https:// URI.
|
198
|
+
if defined? @ssl_options
|
199
|
+
http.use_ssl = true
|
200
|
+
|
201
|
+
# Default to no cert verification (WTF? FIXME)
|
202
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
203
|
+
|
204
|
+
# All the SSL options have corresponding http settings.
|
205
|
+
@ssl_options.each { |key, value| http.send "#{key}=", value }
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def default_header
|
211
|
+
@default_header ||= {}
|
212
|
+
end
|
213
|
+
|
214
|
+
# Builds headers for request to remote service.
|
215
|
+
def build_request_headers(headers, http_method, uri)
|
216
|
+
authorization_header(http_method, uri).update(default_header).update(http_format_header(http_method)).update(headers)
|
217
|
+
end
|
218
|
+
|
219
|
+
def response_auth_header
|
220
|
+
@response_auth_header ||= ""
|
221
|
+
end
|
222
|
+
|
223
|
+
def with_auth
|
224
|
+
retried ||= false
|
225
|
+
yield
|
226
|
+
rescue UnauthorizedAccess => e
|
227
|
+
raise if retried || auth_type != :digest
|
228
|
+
@response_auth_header = e.response['WWW-Authenticate']
|
229
|
+
retried = true
|
230
|
+
retry
|
231
|
+
end
|
232
|
+
|
233
|
+
def authorization_header(http_method, uri)
|
234
|
+
if @user || @password
|
235
|
+
if auth_type == :digest
|
236
|
+
{ 'Authorization' => digest_auth_header(http_method, uri) }
|
237
|
+
else
|
238
|
+
{ 'Authorization' => 'Basic ' + ["#{@user}:#{@password}"].pack('m').delete("\r\n") }
|
239
|
+
end
|
240
|
+
else
|
241
|
+
{}
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
def digest_auth_header(http_method, uri)
|
246
|
+
params = extract_params_from_response
|
247
|
+
|
248
|
+
request_uri = uri.path
|
249
|
+
request_uri << "?#{uri.query}" if uri.query
|
250
|
+
|
251
|
+
ha1 = Digest::MD5.hexdigest("#{@user}:#{params['realm']}:#{@password}")
|
252
|
+
ha2 = Digest::MD5.hexdigest("#{http_method.to_s.upcase}:#{request_uri}")
|
253
|
+
|
254
|
+
params.merge!('cnonce' => client_nonce)
|
255
|
+
request_digest = Digest::MD5.hexdigest([ha1, params['nonce'], "0", params['cnonce'], params['qop'], ha2].join(":"))
|
256
|
+
"Digest #{auth_attributes_for(uri, request_digest, params)}"
|
257
|
+
end
|
258
|
+
|
259
|
+
def client_nonce
|
260
|
+
Digest::MD5.hexdigest("%x" % (Time.now.to_i + rand(65535)))
|
261
|
+
end
|
262
|
+
|
263
|
+
def extract_params_from_response
|
264
|
+
params = {}
|
265
|
+
if response_auth_header =~ /^(\w+) (.*)/
|
266
|
+
$2.gsub(/(\w+)="(.*?)"/) { params[$1] = $2 }
|
267
|
+
end
|
268
|
+
params
|
269
|
+
end
|
270
|
+
|
271
|
+
def auth_attributes_for(uri, request_digest, params)
|
272
|
+
[
|
273
|
+
%Q(username="#{@user}"),
|
274
|
+
%Q(realm="#{params['realm']}"),
|
275
|
+
%Q(qop="#{params['qop']}"),
|
276
|
+
%Q(uri="#{uri.path}"),
|
277
|
+
%Q(nonce="#{params['nonce']}"),
|
278
|
+
%Q(nc="0"),
|
279
|
+
%Q(cnonce="#{params['cnonce']}"),
|
280
|
+
%Q(opaque="#{params['opaque']}"),
|
281
|
+
%Q(response="#{request_digest}")].join(", ")
|
282
|
+
end
|
283
|
+
|
284
|
+
def http_format_header(http_method)
|
285
|
+
{HTTP_FORMAT_HEADER_NAMES[http_method] => format.mime_type}
|
286
|
+
end
|
287
|
+
|
288
|
+
def legitimize_auth_type(auth_type)
|
289
|
+
return :basic if auth_type.nil?
|
290
|
+
auth_type = auth_type.to_sym
|
291
|
+
auth_type.in?([:basic, :digest]) ? auth_type : :basic
|
292
|
+
end
|
46
293
|
end
|
47
294
|
end
|
data/lib/journey/version.rb
CHANGED