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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0d0674e156d1762795bd1ce22e56e4bc30873ae8
4
- data.tar.gz: c09c498cb961accad66bb24489b6f569793be451
3
+ metadata.gz: a5787f183545667833aa70931e85b09333bed067
4
+ data.tar.gz: 52ca2385cd5805ac574a76913539afddab4dafcc
5
5
  SHA512:
6
- metadata.gz: ba9f9ec74f97df6ee03fffb6e5a5ac913dd27ecbae74be32e5164c6076a5c76f8b55d330bd75dfe39b474e1998c0de9b490f1095f3dd894705e7a89d023b586d
7
- data.tar.gz: 0f841fce49fc92b25e5515f829c3a78360575e9307275dc94603339ce66ca6ee2fa71a65d29cc9ea02c7a292fe6d52a999b251b5096831623579effb16ad7617
6
+ metadata.gz: 56f34fb17571bcd21a5cdde76b1f19e2ff07bb711d059f736405d1d5c327edda811cecc3357b139c5553245f452bb3f9018d7c39f10820db1c8fa55d77cc17e8
7
+ data.tar.gz: 10255614494e016c5f896adddc998849fe8fb67ad86b68e60bc7a8fdd8a744ae9b0c78f024aba16fe15f88fe16ea12719c234f04069f158ea064889639d6f780
data/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ v0.0.21
2
+ - stomped out Rails load order bug by copying over entire Connection class (hacky)
3
+
1
4
  v0.0.20
2
5
  - fixes Rails load order bug with Connection (related to gzip patch)
3
6
 
@@ -1,47 +1,294 @@
1
- require 'active_resource/connection'
2
- ActiveResource::Connection
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
- def handle_response(response)
8
- if response.respond_to?(:header) && (response.header["content-encoding"] == 'gzip')
9
- begin
10
- response.instance_variable_set('@body', ActiveSupport::Gzip.decompress(response.body))
11
- rescue Exception => e
12
- raise(BadRequest.new(response))
13
- end
14
- end
15
-
16
- case response.code.to_i
17
- when 301, 302, 303, 307
18
- raise(Redirection.new(response))
19
- when 200...400
20
- response
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
@@ -2,7 +2,7 @@ module Journey
2
2
  module VERSION
3
3
  MAJOR = 0
4
4
  MINOR = 0
5
- TINY = 20
5
+ TINY = 21
6
6
  PRE = nil
7
7
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
8
8
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: embark-journey
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.20
4
+ version: 0.0.21
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dan Davey