miasma 0.2.0 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 70ab81fefcaa4ab8942ca15c53e1ad296a5d5938
4
- data.tar.gz: b2042bdf52407d095767388854adad5d8783526a
3
+ metadata.gz: 2d96b86dbb247d22f846177758907886638e4eec
4
+ data.tar.gz: 5c052aae2f76878d06868e1c60daa01b38921fd2
5
5
  SHA512:
6
- metadata.gz: 6cb01229d765fc2f5d66ba690848af6dc1af060641e4ce4d0d3d791d27051d0a35511077a2dcfdc7ef3cc6e74aee3a395cf61703f1a507b38ee130061b409b49
7
- data.tar.gz: f83fc149fb9b9bac850ef563a17eb133472d281a65ac6ce3db66c32da4bb4e12b5d367b761e1b040ad4f01305977067d07dc70cde63d9534752a6148f62fbd3f
6
+ metadata.gz: 13d34cdd67c7bfea1470f25ffcd0a37a225a9900c59d1c49fa69a61e302e5dfa733f51c4d85ddd04e402b43e261f847bd2ba83c8b0899302a6cae25b933df57e
7
+ data.tar.gz: e7e9a45b0ea4380ba9474b2ba6cdf26f3265bdcff1ce2ebe68a4a193bf4f931b83f347668a8e8ddd6f6d02660d7c04ba3a6813bc8f4579df5b32405c6279c854
@@ -1,3 +1,10 @@
1
+ # v0.2.2
2
+ * Add support for pre-signed AWS URLs
3
+ * Add `#url` method to `Storage::File` model
4
+ * Relax content-type checks on response for more reliable auto-parse (#2)
5
+ * Basic abstract spec coverage on storage
6
+ * Fix response body result by adding streaming detection
7
+
1
8
  # v0.2.0
2
9
  * Add initial OpenStack provider support
3
10
  * Refactor of Rackspace provider support (build off OpenStack)
@@ -156,23 +156,52 @@ module Miasma
156
156
  @service = service
157
157
  end
158
158
 
159
- # Generate the signature
159
+ # Generate the signature string for AUTH
160
160
  #
161
161
  # @param http_method [Symbol] HTTP request method
162
162
  # @param path [String] request path
163
163
  # @param opts [Hash] request options
164
164
  # @return [String] signature
165
165
  def generate(http_method, path, opts)
166
+ signature = generate_signature(http_method, path, opts)
167
+ "#{algorithm} Credential=#{access_key}/#{credential_scope}, SignedHeaders=#{signed_headers(opts[:headers])}, Signature=#{signature}"
168
+ end
169
+
170
+ # Generate URL with signed params
171
+ #
172
+ # @param http_method [Symbol] HTTP request method
173
+ # @param path [String] request path
174
+ # @param opts [Hash] request options
175
+ # @return [String] signature
176
+ def generate_url(http_method, path, opts)
177
+ opts[:params].merge!(
178
+ Smash.new(
179
+ 'X-Amz-SignedHeaders' => signed_headers(opts[:headers]),
180
+ 'X-Amz-Algorithm' => algorithm,
181
+ 'X-Amz-Credential' => "#{access_key}/#{credential_scope}"
182
+ )
183
+ )
184
+ signature = generate_signature(http_method, path, opts.merge(:body => 'UNSIGNED-PAYLOAD'))
185
+ params = opts[:params].merge('X-Amz-Signature' => signature)
186
+ "https://#{opts[:headers]['Host']}/#{path}?#{canonical_query(params)}"
187
+ end
188
+
189
+ # Generate the signature
190
+ #
191
+ # @param http_method [Symbol] HTTP request method
192
+ # @param path [String] request path
193
+ # @param opts [Hash] request options
194
+ # @return [String] signature
195
+ def generate_signature(http_method, path, opts)
166
196
  to_sign = [
167
197
  algorithm,
168
198
  AwsApiCore.time_iso8601,
169
199
  credential_scope,
170
200
  hashed_canonical_request(
171
- build_canonical_request(http_method, path, opts)
201
+ can_req = build_canonical_request(http_method, path, opts)
172
202
  )
173
203
  ].join("\n")
174
204
  signature = sign_request(to_sign)
175
- "#{algorithm} Credential=#{access_key}/#{credential_scope}, SignedHeaders=#{signed_headers(opts[:headers])}, Signature=#{signature}"
176
205
  end
177
206
 
178
207
  # Sign the request
@@ -226,6 +255,9 @@ module Miasma
226
255
  # @param opts [Hash] request options
227
256
  # @return [String] canonical request string
228
257
  def build_canonical_request(http_method, path, opts)
258
+ unless(path.start_with?('/'))
259
+ path = "/#{path}"
260
+ end
229
261
  [
230
262
  http_method.to_s.upcase,
231
263
  path,
@@ -281,7 +313,11 @@ module Miasma
281
313
  elsif(options[:form])
282
314
  body = URI.encode_www_form(options[:form])
283
315
  end
284
- hmac.hexdigest_of(body)
316
+ if(body == 'UNSIGNED-PAYLOAD')
317
+ body
318
+ else
319
+ hmac.hexdigest_of(body)
320
+ end
285
321
  end
286
322
 
287
323
  end
@@ -167,7 +167,7 @@ module Miasma
167
167
  end
168
168
  end.compact
169
169
  ]
170
- if(file.attributes[:body].is_a?(IO) && file.body.length >= 102400)
170
+ if(file.attributes[:body].is_a?(IO) && file.body.length >= Storage::MAX_BODY_SIZE_FOR_STRINGIFY)
171
171
  upload_id = request(
172
172
  args.merge(
173
173
  Smash.new(
@@ -182,7 +182,7 @@ module Miasma
182
182
  count = 1
183
183
  parts = []
184
184
  file.body.rewind
185
- while(content = file.body.read(102400))
185
+ while(content = file.body.read(Storage::READ_BODY_CHUNK_SIZE))
186
186
  parts << [
187
187
  count,
188
188
  request(
@@ -260,7 +260,7 @@ module Miasma
260
260
  :method => :delete,
261
261
  :path => file.name,
262
262
  :endpoint => bucket_endpoint(file.bucket),
263
- :expect => 204
263
+ :expects => 204
264
264
  )
265
265
  true
266
266
  else
@@ -293,6 +293,28 @@ module Miasma
293
293
  file
294
294
  end
295
295
 
296
+ # Create publicly accessible URL
297
+ #
298
+ # @param timeout_secs [Integer] seconds available
299
+ # @return [String] URL
300
+ def file_url(file, timeout_secs)
301
+ if(file.persisted?)
302
+ signer.generate_url(
303
+ :get, ::File.join(uri_escape(file.bucket.name), uri_escape(file.name)),
304
+ :headers => Smash.new(
305
+ 'Host' => aws_host
306
+ ),
307
+ :params => Smash.new(
308
+ 'X-Amz-Date' => Contrib::AwsApiCore.time_iso8601,
309
+ 'X-Amz-Expires' => timeout_secs
310
+ )
311
+
312
+ )
313
+ else
314
+ raise Error::ModelPersistError.new "#{file} has not been saved!"
315
+ end
316
+ end
317
+
296
318
  # Fetch the contents of the file
297
319
  #
298
320
  # @param file [Models::Storage::File]
@@ -220,6 +220,7 @@ module Miasma
220
220
  attribute :open_stack_tenant_name, String
221
221
  attribute :open_stack_domain, String
222
222
  attribute :open_stack_project, String
223
+
223
224
  end
224
225
  end
225
226
 
@@ -87,5 +87,8 @@ module Miasma
87
87
  # Invalid modification request
88
88
  class ImmutableError < Error; end
89
89
 
90
+ # Model has not been persisted
91
+ class ModelPersistError < Error; end
92
+
90
93
  end
91
94
  end
@@ -5,6 +5,11 @@ module Miasma
5
5
  # Abstract storage API
6
6
  class Storage < Types::Api
7
7
 
8
+ # @return [Integer] max bytes allowed for storing body in string
9
+ MAX_BODY_SIZE_FOR_STRINGIFY = 102400
10
+ # @return [Integer] chunking size for reading IO
11
+ READ_BODY_CHUNK_SIZE = 102400
12
+
8
13
  autoload :Buckets, 'miasma/models/storage/buckets'
9
14
  autoload :Bucket, 'miasma/models/storage/bucket'
10
15
  autoload :Files, 'miasma/models/storage/files'
@@ -45,7 +45,13 @@ module Miasma
45
45
  # @note object returned will provide #readpartial
46
46
  def body
47
47
  unless(attributes[:body])
48
- data[:body] ||= api.file_body(self)
48
+ begin
49
+ _body = api.file_body(self)
50
+ _body.stream!
51
+ data[:body] = api.file_body(self)
52
+ rescue HTTP::StateError
53
+ data[:body] = StringIO.new(_body.to_s)
54
+ end
49
55
  end
50
56
  attributes[:body]
51
57
  end
@@ -55,14 +61,37 @@ module Miasma
55
61
  # @param io [IO, String]
56
62
  # @return [IO]
57
63
  def body=(io)
58
- unless(io.is_a?(IO))
64
+ unless(io.respond_to?(:readpartial))
59
65
  io = StringIO.new(io)
60
66
  end
61
67
  dirty[:body] = io
62
68
  end
63
69
 
70
+ # Create accessible URL
71
+ #
72
+ # @param timeout_in_seconds [Integer] optional if private (default: 60)
73
+ # @return [String] URL
74
+ def url(timeout_in_seconds=60)
75
+ perform_file_url(timeout_in_seconds)
76
+ end
77
+
78
+ # Destroy file
79
+ #
80
+ # @return [self]
81
+ def destroy
82
+ perform_destroy
83
+ data.clear
84
+ dirty.clear
85
+ self
86
+ end
87
+
64
88
  protected
65
89
 
90
+ # Proxy URL action up to the API
91
+ def perform_file_url(secs)
92
+ api.file_url(self, secs)
93
+ end
94
+
66
95
  # Proxy reload action up to the API
67
96
  def perform_reload
68
97
  api.file_reload(self)
@@ -118,13 +118,13 @@ module Miasma
118
118
  # @return [Smash]
119
119
  def format_response(result)
120
120
  extracted_headers = Smash[result.headers.map{|k,v| [Utils.snake(k), v]}]
121
- if(extracted_headers[:content_type].to_s.end_with?('json'))
121
+ if(extracted_headers[:content_type].to_s.include?('json'))
122
122
  begin
123
123
  extracted_body = MultiJson.load(result.body.to_s).to_smash
124
124
  rescue MultiJson::ParseError
125
125
  extracted_body = result.body.to_s
126
126
  end
127
- elsif(extracted_headers[:content_type].to_s.end_with?('xml'))
127
+ elsif(extracted_headers[:content_type].to_s.include?('xml'))
128
128
  begin
129
129
  extracted_body = MultiXml.parse(result.body.to_s).to_smash
130
130
  rescue MultiXml::ParseError
@@ -1,4 +1,4 @@
1
1
  module Miasma
2
2
  # current library version
3
- VERSION = Gem::Version.new('0.2.0')
3
+ VERSION = Gem::Version.new('0.2.2')
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: miasma
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Roberts
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-11-19 00:00:00.000000000 Z
11
+ date: 2014-11-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hashie