mss-sdk 1.0.0

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.
Files changed (131) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +9 -0
  3. data/LICENSE.txt +0 -0
  4. data/README.md +192 -0
  5. data/bin/mss-rb +178 -0
  6. data/ca-bundle.crt +3554 -0
  7. data/lib/mss/core/async_handle.rb +89 -0
  8. data/lib/mss/core/cacheable.rb +76 -0
  9. data/lib/mss/core/client.rb +786 -0
  10. data/lib/mss/core/collection/simple.rb +81 -0
  11. data/lib/mss/core/collection/with_limit_and_next_token.rb +70 -0
  12. data/lib/mss/core/collection/with_next_token.rb +96 -0
  13. data/lib/mss/core/collection.rb +262 -0
  14. data/lib/mss/core/configuration.rb +527 -0
  15. data/lib/mss/core/credential_providers.rb +653 -0
  16. data/lib/mss/core/data.rb +251 -0
  17. data/lib/mss/core/deprecations.rb +83 -0
  18. data/lib/mss/core/endpoints.rb +36 -0
  19. data/lib/mss/core/http/connection_pool.rb +374 -0
  20. data/lib/mss/core/http/curb_handler.rb +150 -0
  21. data/lib/mss/core/http/handler.rb +88 -0
  22. data/lib/mss/core/http/net_http_handler.rb +144 -0
  23. data/lib/mss/core/http/patch.rb +98 -0
  24. data/lib/mss/core/http/request.rb +258 -0
  25. data/lib/mss/core/http/response.rb +80 -0
  26. data/lib/mss/core/indifferent_hash.rb +87 -0
  27. data/lib/mss/core/inflection.rb +55 -0
  28. data/lib/mss/core/ini_parser.rb +41 -0
  29. data/lib/mss/core/json_client.rb +46 -0
  30. data/lib/mss/core/json_parser.rb +75 -0
  31. data/lib/mss/core/json_request_builder.rb +34 -0
  32. data/lib/mss/core/json_response_parser.rb +78 -0
  33. data/lib/mss/core/lazy_error_classes.rb +107 -0
  34. data/lib/mss/core/log_formatter.rb +426 -0
  35. data/lib/mss/core/managed_file.rb +31 -0
  36. data/lib/mss/core/meta_utils.rb +44 -0
  37. data/lib/mss/core/model.rb +61 -0
  38. data/lib/mss/core/naming.rb +29 -0
  39. data/lib/mss/core/option_grammar.rb +737 -0
  40. data/lib/mss/core/options/json_serializer.rb +81 -0
  41. data/lib/mss/core/options/validator.rb +154 -0
  42. data/lib/mss/core/options/xml_serializer.rb +117 -0
  43. data/lib/mss/core/page_result.rb +74 -0
  44. data/lib/mss/core/policy.rb +938 -0
  45. data/lib/mss/core/query_client.rb +40 -0
  46. data/lib/mss/core/query_error_parser.rb +23 -0
  47. data/lib/mss/core/query_request_builder.rb +46 -0
  48. data/lib/mss/core/query_response_parser.rb +34 -0
  49. data/lib/mss/core/region.rb +84 -0
  50. data/lib/mss/core/region_collection.rb +79 -0
  51. data/lib/mss/core/resource.rb +412 -0
  52. data/lib/mss/core/resource_cache.rb +39 -0
  53. data/lib/mss/core/response.rb +214 -0
  54. data/lib/mss/core/response_cache.rb +49 -0
  55. data/lib/mss/core/rest_error_parser.rb +23 -0
  56. data/lib/mss/core/rest_json_client.rb +39 -0
  57. data/lib/mss/core/rest_request_builder.rb +153 -0
  58. data/lib/mss/core/rest_response_parser.rb +65 -0
  59. data/lib/mss/core/rest_xml_client.rb +46 -0
  60. data/lib/mss/core/service_interface.rb +82 -0
  61. data/lib/mss/core/signers/base.rb +45 -0
  62. data/lib/mss/core/signers/cloud_front.rb +55 -0
  63. data/lib/mss/core/signers/s3.rb +158 -0
  64. data/lib/mss/core/signers/version_2.rb +71 -0
  65. data/lib/mss/core/signers/version_3.rb +85 -0
  66. data/lib/mss/core/signers/version_3_https.rb +60 -0
  67. data/lib/mss/core/signers/version_4/chunk_signed_stream.rb +190 -0
  68. data/lib/mss/core/signers/version_4.rb +227 -0
  69. data/lib/mss/core/uri_escape.rb +43 -0
  70. data/lib/mss/core/xml/frame.rb +245 -0
  71. data/lib/mss/core/xml/frame_stack.rb +84 -0
  72. data/lib/mss/core/xml/grammar.rb +306 -0
  73. data/lib/mss/core/xml/parser.rb +69 -0
  74. data/lib/mss/core/xml/root_frame.rb +64 -0
  75. data/lib/mss/core/xml/sax_handlers/libxml.rb +46 -0
  76. data/lib/mss/core/xml/sax_handlers/nokogiri.rb +55 -0
  77. data/lib/mss/core/xml/sax_handlers/ox.rb +40 -0
  78. data/lib/mss/core/xml/sax_handlers/rexml.rb +46 -0
  79. data/lib/mss/core/xml/stub.rb +122 -0
  80. data/lib/mss/core.rb +602 -0
  81. data/lib/mss/errors.rb +161 -0
  82. data/lib/mss/rails.rb +194 -0
  83. data/lib/mss/s3/access_control_list.rb +262 -0
  84. data/lib/mss/s3/acl_object.rb +263 -0
  85. data/lib/mss/s3/acl_options.rb +200 -0
  86. data/lib/mss/s3/bucket.rb +757 -0
  87. data/lib/mss/s3/bucket_collection.rb +161 -0
  88. data/lib/mss/s3/bucket_lifecycle_configuration.rb +472 -0
  89. data/lib/mss/s3/bucket_region_cache.rb +51 -0
  90. data/lib/mss/s3/bucket_tag_collection.rb +110 -0
  91. data/lib/mss/s3/bucket_version_collection.rb +78 -0
  92. data/lib/mss/s3/cipher_io.rb +119 -0
  93. data/lib/mss/s3/client/xml.rb +265 -0
  94. data/lib/mss/s3/client.rb +2076 -0
  95. data/lib/mss/s3/config.rb +60 -0
  96. data/lib/mss/s3/cors_rule.rb +107 -0
  97. data/lib/mss/s3/cors_rule_collection.rb +193 -0
  98. data/lib/mss/s3/data_options.rb +190 -0
  99. data/lib/mss/s3/encryption_utils.rb +145 -0
  100. data/lib/mss/s3/errors.rb +93 -0
  101. data/lib/mss/s3/multipart_upload.rb +353 -0
  102. data/lib/mss/s3/multipart_upload_collection.rb +75 -0
  103. data/lib/mss/s3/object_collection.rb +355 -0
  104. data/lib/mss/s3/object_metadata.rb +102 -0
  105. data/lib/mss/s3/object_upload_collection.rb +76 -0
  106. data/lib/mss/s3/object_version.rb +153 -0
  107. data/lib/mss/s3/object_version_collection.rb +88 -0
  108. data/lib/mss/s3/paginated_collection.rb +74 -0
  109. data/lib/mss/s3/policy.rb +73 -0
  110. data/lib/mss/s3/prefix_and_delimiter_collection.rb +46 -0
  111. data/lib/mss/s3/prefixed_collection.rb +84 -0
  112. data/lib/mss/s3/presign_v4.rb +135 -0
  113. data/lib/mss/s3/presigned_post.rb +574 -0
  114. data/lib/mss/s3/region_detection.rb +75 -0
  115. data/lib/mss/s3/request.rb +61 -0
  116. data/lib/mss/s3/s3_object.rb +1795 -0
  117. data/lib/mss/s3/tree/branch_node.rb +67 -0
  118. data/lib/mss/s3/tree/child_collection.rb +103 -0
  119. data/lib/mss/s3/tree/leaf_node.rb +93 -0
  120. data/lib/mss/s3/tree/node.rb +21 -0
  121. data/lib/mss/s3/tree/parent.rb +86 -0
  122. data/lib/mss/s3/tree.rb +115 -0
  123. data/lib/mss/s3/uploaded_part.rb +81 -0
  124. data/lib/mss/s3/uploaded_part_collection.rb +83 -0
  125. data/lib/mss/s3/website_configuration.rb +101 -0
  126. data/lib/mss/s3.rb +161 -0
  127. data/lib/mss/version.rb +16 -0
  128. data/lib/mss-sdk.rb +2 -0
  129. data/lib/mss.rb +14 -0
  130. data/rails/init.rb +14 -0
  131. metadata +201 -0
@@ -0,0 +1,2076 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright 2011-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License"). You
5
+ # may not use this file except in compliance with the License. A copy of
6
+ # the License is located at
7
+ #
8
+ #
9
+ # or in the "license" file accompanying this file. This file is
10
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
+ # ANY KIND, either express or implied. See the License for the specific
12
+ # language governing permissions and limitations under the License.
13
+
14
+ require 'rexml/document'
15
+ require 'pathname'
16
+ require 'stringio'
17
+ require 'json'
18
+ require 'digest/md5'
19
+ require 'base64'
20
+ require 'nokogiri'
21
+
22
+ module MSS
23
+ class S3
24
+
25
+ # Client class for Amazon Simple Storage Service (S3).
26
+ class Client < Core::Client
27
+
28
+ #include RegionDetection
29
+
30
+ def initialize(options = {})
31
+ super(options.merge(:http_continue_threshold => 0))
32
+ end
33
+
34
+ signature_version :S3
35
+
36
+ API_VERSION = '2006-03-01'
37
+
38
+ XMLNS = "http://s3.amazonmss.com/doc/#{API_VERSION}/"
39
+
40
+ autoload :XML, 'mss/s3/client/xml'
41
+
42
+ # @api private
43
+ EMPTY_BODY_ERRORS = {
44
+ 304 => Errors::NotModified,
45
+ 403 => Errors::Forbidden,
46
+ 400 => Errors::BadRequest,
47
+ 404 => Errors::NoSuchKey,
48
+ }
49
+
50
+ # @api private
51
+ CACHEABLE_REQUESTS = Set[]
52
+
53
+ include DataOptions
54
+ include Core::UriEscape
55
+
56
+ # @param [Core::Http::Request] request
57
+ # @api private
58
+ def sign_request request
59
+ case @config.s3_signature_version.to_sym
60
+ when :v4 then v4_signer.sign_request(request)
61
+ when :v3 then v3_signer.sign_request(request)
62
+ else
63
+ raise "invalid signature version #{@config.s3_signature_version.inspect}"
64
+ end
65
+ end
66
+
67
+ protected
68
+
69
+ # @return [Core::Signers::S3]
70
+ def v3_signer
71
+ @v3_signer ||= Core::Signers::S3.new(credential_provider)
72
+ end
73
+
74
+ # @return [Core::Signers::Version4]
75
+ def v4_signer
76
+ @v4_signer ||= begin
77
+ Core::Signers::Version4.new(credential_provider, 's3', @region)
78
+ end
79
+ end
80
+
81
+ # @param [Http::Request] req
82
+ # @return [Boolean]
83
+ def chunk_sign? req
84
+ req.http_method == 'PUT' &&
85
+ req.headers['content-length'].to_i > 2 * 1024 * 1024 # 2MB
86
+ end
87
+
88
+ def self.bucket_method(method_name, verb, *args, &block)
89
+
90
+ method_options = (args.pop if args.last.kind_of?(Hash)) || {}
91
+ xml_grammar = (args.pop if args.last.respond_to?(:rules))
92
+ verb = verb.to_s.upcase
93
+ subresource = args.first
94
+
95
+ add_client_request_method(method_name) do
96
+
97
+ configure_request do |req, options|
98
+
99
+ require_bucket_name!(options[:bucket_name])
100
+
101
+ req.http_method = verb
102
+ req.bucket = options[:bucket_name]
103
+ req.add_param(subresource) if subresource
104
+
105
+ if header_options = method_options[:header_options]
106
+ header_options.each do |(opt, header)|
107
+ if value = options[opt]
108
+ # for backwards compatability we translate canned acls
109
+ # header values from symbols to strings (e.g.
110
+ # :public_read translates to 'public-read')
111
+ value = (opt == :acl ? value.to_s.tr('_', '-') : value)
112
+ req.headers[header] = value
113
+ end
114
+ end
115
+ end
116
+
117
+ end
118
+
119
+ instance_eval(&block) if block
120
+
121
+ if xml_grammar
122
+
123
+ parser = Core::XML::Parser.new(xml_grammar.rules)
124
+
125
+ process_response do |resp|
126
+ resp.data = parser.parse(resp.http_response.body)
127
+ super(resp)
128
+ end
129
+
130
+ simulate_response do |resp|
131
+ resp.data = parser.simulate
132
+ super(resp)
133
+ end
134
+
135
+ end
136
+
137
+ end
138
+ end
139
+
140
+ protected
141
+
142
+ def set_metadata request, options
143
+ if metadata = options[:metadata]
144
+ Array(metadata).each do |name, value|
145
+ request.headers["x-amz-meta-#{name}"] = value
146
+ end
147
+ end
148
+ end
149
+
150
+ def set_copy_content_length request, options
151
+ request.headers["Content-Length"] = 0
152
+ end
153
+
154
+ def set_storage_class request, options
155
+ storage_class = options[:storage_class]
156
+ if storage_class.kind_of?(Symbol)
157
+ request.headers["x-amz-storage-class"] = storage_class.to_s.upcase
158
+ elsif storage_class
159
+ request.headers["x-amz-storage-class"] = storage_class
160
+ end
161
+ end
162
+
163
+ # def set_server_side_encryption request, options
164
+ # sse = options[:server_side_encryption]
165
+ # if sse.is_a?(Symbol)
166
+ # request.headers['x-amz-server-side-encryption'] = sse.to_s.upcase
167
+ # elsif sse
168
+ # request.headers['x-amz-server-side-encryption'] = sse
169
+ # end
170
+ # end
171
+
172
+ def extract_error_details response
173
+ if
174
+ (response.http_response.status >= 300 ||
175
+ response.request_type == :complete_multipart_upload) and
176
+ body = response.http_response.body and
177
+ error = Core::XML::Parser.parse(body) and
178
+ error[:code]
179
+ then
180
+ [error[:code], error[:message]]
181
+ end
182
+ end
183
+
184
+ def empty_response_body? response_body
185
+ response_body.nil? or response_body == ''
186
+ end
187
+
188
+ # There are a few of s3 requests that can generate empty bodies and
189
+ # yet still be errors. These return empty bodies to comply with the
190
+ # HTTP spec. We have to detect these errors specially.
191
+ def populate_error resp
192
+ code = resp.http_response.status
193
+ if EMPTY_BODY_ERRORS.include?(code) and empty_response_body?(resp.http_response.body)
194
+ error_class = EMPTY_BODY_ERRORS[code]
195
+ resp.error = error_class.new(resp.http_request, resp.http_response)
196
+ else
197
+ super
198
+ end
199
+ end
200
+
201
+ def retryable_error? response
202
+ super or
203
+ failed_multipart_upload?(response) or
204
+ response.error.is_a?(Errors::RequestTimeout)
205
+ end
206
+
207
+ # S3 may return a 200 response code in response to complete_multipart_upload
208
+ # and then start streaming whitespace until it knows the final result.
209
+ # At that time it sends an XML message with success or failure.
210
+ def failed_multipart_upload? response
211
+ response.request_type == :complete_multipart_upload &&
212
+ extract_error_details(response)
213
+ end
214
+
215
+ def new_request
216
+ req = S3::Request.new
217
+ req.force_path_style = config.s3_force_path_style?
218
+ req
219
+ end
220
+
221
+ # Previously the access control policy could be specified via :acl
222
+ # as a string or an object that responds to #to_xml. The prefered
223
+ # method now is to pass :access_control_policy an xml document.
224
+ def move_access_control_policy options
225
+ if acl = options[:acl]
226
+ if acl.is_a?(String) and is_xml?(acl)
227
+ options[:access_control_policy] = options.delete(:acl)
228
+ elsif acl.respond_to?(:to_xml)
229
+ options[:access_control_policy] = options.delete(:acl).to_xml
230
+ end
231
+ end
232
+ end
233
+
234
+ # @param [String] possible_xml
235
+ # @return [Boolean] Returns `true` if the given string is a valid xml
236
+ # document.
237
+ def is_xml? possible_xml
238
+ begin
239
+ REXML::Document.new(possible_xml).has_elements?
240
+ rescue
241
+ false
242
+ end
243
+ end
244
+
245
+ def md5 str
246
+ Base64.encode64(OpenSSL::Digest::MD5.digest(str)).strip
247
+ end
248
+
249
+ def parse_copy_part_response resp
250
+ doc = REXML::Document.new(resp.http_response.body)
251
+ resp[:etag] = doc.root.elements["ETag"].text
252
+ resp[:last_modified] = doc.root.elements["LastModified"].text
253
+ if header = resp.http_response.headers['x-amzn-requestid']
254
+ data[:request_id] = [header].flatten.first
255
+ end
256
+ end
257
+
258
+ def extract_object_headers resp
259
+ meta = {}
260
+ resp.http_response.headers.each_pair do |name,value|
261
+ if name =~ /^x-amz-meta-(.+)$/i
262
+ meta[$1] = [value].flatten.join
263
+ end
264
+ end
265
+ resp.data[:meta] = meta
266
+
267
+ if expiry = resp.http_response.headers['x-amz-expiration']
268
+ expiry.first =~ /^expiry-date="(.+)", rule-id="(.+)"$/
269
+ exp_date = DateTime.parse($1)
270
+ exp_rule_id = $2
271
+ else
272
+ exp_date = nil
273
+ exp_rule_id = nil
274
+ end
275
+ resp.data[:expiration_date] = exp_date if exp_date
276
+ resp.data[:expiration_rule_id] = exp_rule_id if exp_rule_id
277
+
278
+ restoring = false
279
+ restore_date = nil
280
+
281
+ if restore = resp.http_response.headers['x-amz-restore']
282
+ if restore.first =~ /ongoing-request="(.+?)", expiry-date="(.+?)"/
283
+ restoring = $1 == "true"
284
+ restore_date = $2 && DateTime.parse($2)
285
+ elsif restore.first =~ /ongoing-request="(.+?)"/
286
+ restoring = $1 == "true"
287
+ end
288
+ end
289
+ resp.data[:restore_in_progress] = restoring
290
+ resp.data[:restore_expiration_date] = restore_date if restore_date
291
+
292
+ {
293
+ 'x-amz-version-id' => :version_id,
294
+ 'content-type' => :content_type,
295
+ 'content-encoding' => :content_encoding,
296
+ 'cache-control' => :cache_control,
297
+ 'expires' => :expires,
298
+ 'etag' => :etag,
299
+ 'x-amz-website-redirect-location' => :website_redirect_location,
300
+ 'accept-ranges' => :accept_ranges,
301
+ 'x-amz-server-side-encryption-customer-algorithm' => :sse_customer_algorithm,
302
+ 'x-amz-server-side-encryption-customer-key-MD5' => :sse_customer_key_md5
303
+ }.each_pair do |header,method|
304
+ if value = resp.http_response.header(header)
305
+ resp.data[method] = value
306
+ end
307
+ end
308
+
309
+ if time = resp.http_response.header('Last-Modified')
310
+ resp.data[:last_modified] = Time.parse(time)
311
+ end
312
+
313
+ if length = resp.http_response.header('content-length')
314
+ resp.data[:content_length] = length.to_i
315
+ end
316
+
317
+ if sse = resp.http_response.header('x-amz-server-side-encryption')
318
+ resp.data[:server_side_encryption] = sse.downcase.to_sym
319
+ end
320
+
321
+ end
322
+
323
+ module Validators
324
+
325
+ # @return [Boolean] Returns true if the given bucket name is valid.
326
+ def valid_bucket_name?(bucket_name)
327
+ validate_bucket_name!(bucket_name) rescue false
328
+ end
329
+
330
+ # Returns true if the given `bucket_name` is DNS compatible.
331
+ #
332
+ # DNS compatible bucket names may be accessed like:
333
+ #
334
+ # http://dns.compat.bucket.name.s3.amazonmss.com/
335
+ #
336
+ # Whereas non-dns compatible bucket names must place the bucket
337
+ # name in the url path, like:
338
+ #
339
+ # http://s3.amazonmss.com/dns_incompat_bucket_name/
340
+ #
341
+ # @return [Boolean] Returns true if the given bucket name may be
342
+ # is dns compatible.
343
+ # this bucket n
344
+ #
345
+ def dns_compatible_bucket_name?(bucket_name)
346
+ return false if
347
+ !valid_bucket_name?(bucket_name) or
348
+
349
+ # Bucket names should be between 3 and 63 characters long
350
+ bucket_name.size > 63 or
351
+
352
+ # Bucket names must only contain lowercase letters, numbers, dots, and dashes
353
+ # and must start and end with a lowercase letter or a number
354
+ bucket_name !~ /^[a-z0-9][a-z0-9.-]+[a-z0-9]$/ or
355
+
356
+ # Bucket names should not be formatted like an IP address (e.g., 192.168.5.4)
357
+ bucket_name =~ /(\d+\.){3}\d+/ or
358
+
359
+ # Bucket names cannot contain two, adjacent periods
360
+ bucket_name['..'] or
361
+
362
+ # Bucket names cannot contain dashes next to periods
363
+ # (e.g., "my-.bucket.com" and "my.-bucket" are invalid)
364
+ (bucket_name['-.'] || bucket_name['.-'])
365
+
366
+ true
367
+ end
368
+
369
+ # Returns true if the bucket name must be used in the request
370
+ # path instead of as a sub-domain when making requests against
371
+ # S3.
372
+ #
373
+ # This can be an issue if the bucket name is DNS compatible but
374
+ # contains '.' (periods). These cause the SSL certificate to
375
+ # become invalid when making authenticated requets over SSL to the
376
+ # bucket name. The solution is to send this as a path argument
377
+ # instead.
378
+ #
379
+ # @return [Boolean] Returns true if the bucket name should be used
380
+ # as a path segement instead of dns prefix when making requests
381
+ # against s3.
382
+ #
383
+ def path_style_bucket_name? bucket_name
384
+ if dns_compatible_bucket_name?(bucket_name)
385
+ bucket_name =~ /\./ ? true : false
386
+ else
387
+ true
388
+ end
389
+ end
390
+
391
+ def validate! name, value, &block
392
+ if error_msg = yield
393
+ raise ArgumentError, "#{name} #{error_msg}"
394
+ end
395
+ value
396
+ end
397
+
398
+ def validate_key!(key)
399
+ validate!('key', key) do
400
+ case
401
+ when key.nil? || key == ''
402
+ 'may not be blank'
403
+ end
404
+ end
405
+ end
406
+
407
+ def require_bucket_name! bucket_name
408
+ if [nil, ''].include?(bucket_name)
409
+ raise ArgumentError, "bucket_name may not be blank"
410
+ end
411
+ end
412
+
413
+ # Returns true if the given bucket name is valid. If the name
414
+ # is invalid, an ArgumentError is raised.
415
+ def validate_bucket_name!(bucket_name)
416
+ validate!('bucket_name', bucket_name) do
417
+ case
418
+ when bucket_name.nil? || bucket_name == ''
419
+ 'may not be blank'
420
+ when bucket_name !~ /^[A-Za-z0-9._\-]+$/
421
+ 'may only contain uppercase letters, lowercase letters, numbers, periods (.), ' +
422
+ 'underscores (_), and dashes (-)'
423
+ when !(3..255).include?(bucket_name.size)
424
+ 'must be between 3 and 255 characters long'
425
+ when bucket_name =~ /\n/
426
+ 'must not contain a newline character'
427
+ end
428
+ end
429
+ end
430
+
431
+ def require_policy!(policy)
432
+ validate!('policy', policy) do
433
+ case
434
+ when policy.nil? || policy == ''
435
+ 'may not be blank'
436
+ else
437
+ json_validation_message(policy)
438
+ end
439
+ end
440
+ end
441
+
442
+ def require_acl! options
443
+ acl_options = [
444
+ :acl,
445
+ :grant_read,
446
+ :grant_write,
447
+ :grant_read_acp,
448
+ :grant_write_acp,
449
+ :grant_full_control,
450
+ :access_control_policy,
451
+ ]
452
+ unless options.keys.any?{|opt| acl_options.include?(opt) }
453
+ msg = "missing a required ACL option, must provide an ACL " +
454
+ "via :acl, :grant_* or :access_control_policy"
455
+ raise ArgumentError, msg
456
+ end
457
+ end
458
+
459
+ def set_body_stream_and_content_length request, options
460
+
461
+ unless options[:content_length]
462
+ msg = "S3 requires a content-length header, unable to determine "
463
+ msg << "the content length of the data provided, please set "
464
+ msg << ":content_length"
465
+ raise ArgumentError, msg
466
+ end
467
+
468
+ request.headers['content-length'] = options[:content_length]
469
+ request.body_stream = options[:data]
470
+
471
+ end
472
+
473
+ def require_upload_id!(upload_id)
474
+ validate!("upload_id", upload_id) do
475
+ "must not be blank" if upload_id.to_s.empty?
476
+ end
477
+ end
478
+
479
+ def require_part_number! part_number
480
+ validate!("part_number", part_number) do
481
+ "must not be blank" if part_number.to_s.empty?
482
+ end
483
+ end
484
+
485
+ def validate_parts!(parts)
486
+ validate!("parts", parts) do
487
+ if !parts.kind_of?(Array)
488
+ "must not be blank"
489
+ elsif parts.empty?
490
+ "must contain at least one entry"
491
+ elsif !parts.all? { |p| p.kind_of?(Hash) }
492
+ "must be an array of hashes"
493
+ elsif !parts.all? { |p| p[:part_number] }
494
+ "must contain part_number for each part"
495
+ elsif !parts.all? { |p| p[:etag] }
496
+ "must contain etag for each part"
497
+ elsif parts.any? { |p| p[:part_number].to_i < 1 }
498
+ "must not have part numbers less than 1"
499
+ end
500
+ end
501
+ end
502
+
503
+ def json_validation_message(obj)
504
+ if obj.respond_to?(:to_str)
505
+ obj = obj.to_str
506
+ elsif obj.respond_to?(:to_json)
507
+ obj = obj.to_json
508
+ end
509
+
510
+ error = nil
511
+ begin
512
+ JSON.parse(obj)
513
+ rescue => e
514
+ error = e
515
+ end
516
+ "contains invalid JSON: #{error}" if error
517
+ end
518
+
519
+ def require_allowed_methods!(allowed_methods)
520
+ validate!("allowed_methods", allowed_methods) do
521
+ if !allowed_methods.kind_of?(Array)
522
+ "must be an array"
523
+ elsif !allowed_methods.all? { |x| x.kind_of?(String) }
524
+ "must be an array of strings"
525
+ end
526
+ end
527
+ end
528
+
529
+ def require_allowed_origins!(allowed_origins)
530
+ validate!("allowed_origins", allowed_origins) do
531
+ if !allowed_origins.kind_of?(Array)
532
+ "must be an array"
533
+ elsif !allowed_origins.all? { |x| x.kind_of?(String) }
534
+ "must be an array of strings"
535
+ end
536
+ end
537
+ end
538
+
539
+ end
540
+
541
+ include Validators
542
+ extend Validators
543
+
544
+ end
545
+
546
+ class Client::V20060301 < Client
547
+
548
+ def self.object_method(method_name, verb, *args, &block)
549
+ bucket_method(method_name, verb, *args) do
550
+ configure_request do |req, options|
551
+ validate_key!(options[:key])
552
+ super(req, options)
553
+ req.key = options[:key]
554
+ end
555
+
556
+ instance_eval(&block) if block
557
+ end
558
+ end
559
+
560
+ public
561
+
562
+ # Creates a bucket.
563
+ # @overload create_bucket(options = {})
564
+ # @param [Hash] options
565
+ # @option options [required,String] :bucket_name
566
+ # @option options [String] :acl A canned ACL (e.g. 'private',
567
+ # 'public-read', etc). See the S3 API documentation for
568
+ # a complete list of valid values.
569
+ # @option options [String] :grant_read
570
+ # @option options [String] :grant_write
571
+ # @option options [String] :grant_read_acp
572
+ # @option options [String] :grant_write_acp
573
+ # @option options [String] :grant_full_control
574
+ # @return [Core::Response]
575
+ bucket_method(:create_bucket, :put, :header_options => {
576
+ :acl => 'x-amz-acl',
577
+ :grant_read => 'x-amz-grant-read',
578
+ :grant_write => 'x-amz-grant-write',
579
+ :grant_read_acp => 'x-amz-grant-read-acp',
580
+ :grant_write_acp => 'x-amz-grant-write-acp',
581
+ :grant_full_control => 'x-amz-grant-full-control',
582
+ }) do
583
+
584
+ configure_request do |req, options|
585
+ validate_bucket_name!(options[:bucket_name])
586
+ if location = options[:location_constraint]
587
+ xmlns = "http://s3.amazonmss.com/doc/#{API_VERSION}/"
588
+ req.body = <<-XML
589
+ <CreateBucketConfiguration xmlns="#{xmlns}">
590
+ <LocationConstraint>#{location}</LocationConstraint>
591
+ </CreateBucketConfiguration>
592
+ XML
593
+ end
594
+ super(req, options)
595
+ end
596
+
597
+ end
598
+ alias_method :put_bucket, :create_bucket
599
+
600
+ # @!method put_bucket_website(options = {})
601
+ # @param [Hash] options
602
+ # @option (see WebsiteConfiguration#initialize)
603
+ # @option options [required,String] :bucket_name
604
+ # @return [Core::Response]
605
+ bucket_method(:put_bucket_website, :put, 'website') do
606
+
607
+ configure_request do |req, options|
608
+ req.body = Nokogiri::XML::Builder.new do |xml|
609
+ xml.WebsiteConfiguration(:xmlns => XMLNS) do
610
+
611
+ if redirect = options[:redirect_all_requests_to]
612
+ xml.RedirectAllRequestsTo do
613
+ xml.HostName(redirect[:host_name])
614
+ xml.Protocol(redirect[:protocol]) if redirect[:protocol]
615
+ end
616
+ end
617
+
618
+ if indx = options[:index_document]
619
+ xml.IndexDocument do
620
+ xml.Suffix(indx[:suffix])
621
+ end
622
+ end
623
+
624
+ if err = options[:error_document]
625
+ xml.ErrorDocument do
626
+ xml.Key(err[:key])
627
+ end
628
+ end
629
+
630
+ rules = options[:routing_rules]
631
+ if rules.is_a?(Array) && !rules.empty?
632
+ xml.RoutingRules do
633
+ rules.each do |rule|
634
+ xml.RoutingRule do
635
+
636
+ redirect = rule[:redirect]
637
+ xml.Redirect do
638
+ xml.Protocol(redirect[:protocol]) if redirect[:protocol]
639
+ xml.HostName(redirect[:host_name]) if redirect[:host_name]
640
+ xml.ReplaceKeyPrefixWith(redirect[:replace_key_prefix_with]) if redirect[:replace_key_prefix_with]
641
+ xml.ReplaceKeyWith(redirect[:replace_key_with]) if redirect[:replace_key_with]
642
+ xml.HttpRedirectCode(redirect[:http_redirect_code]) if redirect[:http_redirect_code]
643
+ end
644
+
645
+ if condition = rule[:condition]
646
+ xml.Condition do
647
+ xml.KeyPrefixEquals(condition[:key_prefix_equals]) if condition[:key_prefix_equals]
648
+ xml.HttpErrorCodeReturnedEquals(condition[:http_error_code_returned_equals]) if condition[:http_error_code_returned_equals]
649
+ end
650
+ end
651
+
652
+ end
653
+ end
654
+ end
655
+ end
656
+
657
+ end
658
+ end.doc.root.to_xml
659
+ super(req, options)
660
+ end
661
+
662
+ end
663
+
664
+ # @overload get_bucket_website(options = {})
665
+ # @param [Hash] options
666
+ # @option options [required,String] :bucket_name
667
+ # @return [Core::Response]
668
+ # * `:index_document` - (Hash)
669
+ # * `:suffix` - (String)
670
+ # * `:error_document` - (Hash)
671
+ # * `:key` - (String)
672
+ bucket_method(:get_bucket_website, :get, 'website', XML::GetBucketWebsite)
673
+
674
+ # @overload delete_bucket_website(options = {})
675
+ # @param [Hash] options
676
+ # @option options [required,String] :bucket_name
677
+ # @return [Core::Response]
678
+ bucket_method(:delete_bucket_website, :delete, 'website')
679
+
680
+ # Deletes an empty bucket.
681
+ # @overload delete_bucket(options = {})
682
+ # @param [Hash] options
683
+ # @option options [required,String] :bucket_name
684
+ # @return [Core::Response]
685
+ bucket_method(:delete_bucket, :delete)
686
+
687
+ # @overload set_bucket_lifecycle_configuration(options = {})
688
+ # @param [Hash] options
689
+ # @option options [required,String] :bucket_name
690
+ # @option options [required,String] :lifecycle_configuration
691
+ # @return [Core::Response]
692
+ bucket_method(:set_bucket_lifecycle_configuration, :put) do
693
+
694
+ configure_request do |req, options|
695
+ xml = options[:lifecycle_configuration]
696
+ req.add_param('lifecycle')
697
+ req.body = xml
698
+ req.headers['content-md5'] = md5(xml)
699
+ super(req, options)
700
+ end
701
+
702
+ end
703
+
704
+ # @overload get_bucket_lifecycle_configuration(options = {})
705
+ # @param [Hash] options
706
+ # @option options [required,String] :bucket_name
707
+ # @return [Core::Response]
708
+ bucket_method(:get_bucket_lifecycle_configuration, :get) do
709
+
710
+ configure_request do |req, options|
711
+ req.add_param('lifecycle')
712
+ super(req, options)
713
+ end
714
+
715
+ process_response do |resp|
716
+ xml = resp.http_response.body
717
+ resp.data = XML::GetBucketLifecycleConfiguration.parse(xml)
718
+ end
719
+
720
+ end
721
+
722
+ # @overload delete_bucket_lifecycle_configuration(options = {})
723
+ # @param [Hash] options
724
+ # @option options [required,String] :bucket_name
725
+ # @return [Core::Response]
726
+ bucket_method(:delete_bucket_lifecycle_configuration, :delete) do
727
+
728
+ configure_request do |req, options|
729
+ req.add_param('lifecycle')
730
+ super(req, options)
731
+ end
732
+
733
+ end
734
+
735
+ # @overload put_bucket_cors(options = {})
736
+ # @param [Hash] options
737
+ # @option options [required,String] :bucket_name
738
+ # @option options [required,Array<Hash>] :rules An array of rule hashes.
739
+ # * `:id` - (String) A unique identifier for the rule. The ID
740
+ # value can be up to 255 characters long. The IDs help you find
741
+ # a rule in the configuration.
742
+ # * `:allowed_methods` - (required,Array<String>) A list of HTTP
743
+ # methods that you want to allow the origin to execute.
744
+ # Each rule must identify at least one method.
745
+ # * `:allowed_origins` - (required,Array<String>) A list of origins
746
+ # you want to allow cross-domain requests from. This can
747
+ # contain at most one * wild character.
748
+ # * `:allowed_headers` - (Array<String>) A list of headers allowed
749
+ # in a pre-flight OPTIONS request via the
750
+ # Access-Control-Request-Headers header. Each header name
751
+ # specified in the Access-Control-Request-Headers header must
752
+ # have a corresponding entry in the rule.
753
+ # Amazon S3 will send only the allowed headers in a response
754
+ # that were requested. This can contain at most one * wild
755
+ # character.
756
+ # * `:max_age_seconds` - (Integer) The time in seconds that your
757
+ # browser is to cache the preflight response for the specified
758
+ # resource.
759
+ # * `:expose_headers` - (Array<String>) One or more headers in
760
+ # the response that you want customers to be able to access
761
+ # from their applications (for example, from a JavaScript
762
+ # XMLHttpRequest object).
763
+ # @return [Core::Response]
764
+ # bucket_method(:put_bucket_cors, :put) do
765
+ # configure_request do |req, options|
766
+ #
767
+ # req.add_param('cors')
768
+ #
769
+ # options[:rules].each do |rule|
770
+ # require_allowed_methods!(rule[:allowed_methods])
771
+ # require_allowed_origins!(rule[:allowed_origins])
772
+ # end
773
+ #
774
+ # xml = Nokogiri::XML::Builder.new do |xml|
775
+ # xml.CORSConfiguration do
776
+ # options[:rules].each do |rule|
777
+ # xml.CORSRule do
778
+ #
779
+ # xml.ID(rule[:id]) if rule[:id]
780
+ #
781
+ # (rule[:allowed_methods] || []).each do |method|
782
+ # xml.AllowedMethod(method)
783
+ # end
784
+ #
785
+ # (rule[:allowed_origins] || []).each do |origin|
786
+ # xml.AllowedOrigin(origin)
787
+ # end
788
+ #
789
+ # (rule[:allowed_headers] || []).each do |header|
790
+ # xml.AllowedHeader(header)
791
+ # end
792
+ #
793
+ # xml.MaxAgeSeconds(rule[:max_age_seconds]) if
794
+ # rule[:max_age_seconds]
795
+ #
796
+ # (rule[:expose_headers] || []).each do |header|
797
+ # xml.ExposeHeader(header)
798
+ # end
799
+ #
800
+ # end
801
+ # end
802
+ # end
803
+ # end.doc.root.to_xml
804
+ #
805
+ # req.body = xml
806
+ # req.headers['content-md5'] = md5(xml)
807
+ #
808
+ # super(req, options)
809
+ #
810
+ # end
811
+ # end
812
+
813
+ # @overload get_bucket_cors(options = {})
814
+ # @param [Hash] options
815
+ # @option options [required,String] :bucket_name
816
+ # @return [Core::Response]
817
+ # bucket_method(:get_bucket_cors, :get) do
818
+
819
+ # configure_request do |req, options|
820
+ # req.add_param('cors')
821
+ # super(req, options)
822
+ # end
823
+
824
+ # process_response do |resp|
825
+ # resp.data = XML::GetBucketCors.parse(resp.http_response.body)
826
+ # end
827
+ #
828
+ # end
829
+
830
+ # @overload delete_bucket_cors(options = {})
831
+ # @param [Hash] options
832
+ # @option options [required,String] :bucket_name
833
+ # @return [Core::Response]
834
+ # bucket_method(:delete_bucket_cors, :delete) do
835
+ # configure_request do |req, options|
836
+ # req.add_param('cors')
837
+ # super(req, options)
838
+ # end
839
+ # end
840
+
841
+ # @overload put_bucket_tagging(options = {})
842
+ # @param [Hash] options
843
+ # @option options [required,String] :bucket_name
844
+ # @option options [Hash] :tags
845
+ # @return [Core::Response]
846
+ # bucket_method(:put_bucket_tagging, :put) do
847
+ # configure_request do |req, options|
848
+ #
849
+ # req.add_param('tagging')
850
+ #
851
+ # xml = Nokogiri::XML::Builder.new
852
+ # xml.Tagging do |xml|
853
+ # xml.TagSet do
854
+ # options[:tags].each_pair do |key,value|
855
+ # xml.Tag do
856
+ # xml.Key(key)
857
+ # xml.Value(value)
858
+ # end
859
+ # end
860
+ # end
861
+ # end
862
+ #
863
+ # xml = xml.doc.root.to_xml
864
+ # req.body = xml
865
+ # req.headers['content-md5'] = md5(xml)
866
+ #
867
+ # super(req, options)
868
+ #
869
+ # end
870
+ # end
871
+
872
+ # @overload get_bucket_tagging(options = {})
873
+ # @param [Hash] options
874
+ # @option options [required,String] :bucket_name
875
+ # @return [Core::Response]
876
+ # bucket_method(:get_bucket_tagging, :get) do
877
+ #
878
+ # configure_request do |req, options|
879
+ # req.add_param('tagging')
880
+ # super(req, options)
881
+ # end
882
+ #
883
+ # process_response do |resp|
884
+ # resp.data = XML::GetBucketTagging.parse(resp.http_response.body)
885
+ # end
886
+ #
887
+ # end
888
+
889
+ # @overload delete_bucket_tagging(options = {})
890
+ # @param [Hash] options
891
+ # @option options [required,String] :bucket_name
892
+ # @return [Core::Response]
893
+ # bucket_method(:delete_bucket_tagging, :delete) do
894
+ # configure_request do |req, options|
895
+ # req.add_param('tagging')
896
+ # super(req, options)
897
+ # end
898
+ # end
899
+
900
+ # @overload list_buckets(options = {})
901
+ # @param [Hash] options
902
+ # @return [Core::Response]
903
+ add_client_request_method(:list_buckets) do
904
+
905
+ configure_request do |req, options|
906
+ req.http_method = "GET"
907
+ end
908
+
909
+ process_response do |resp|
910
+ resp.data = XML::ListBuckets.parse(resp.http_response.body)
911
+ end
912
+
913
+ simulate_response do |resp|
914
+ resp.data = Core::XML::Parser.new(XML::ListBuckets.rules).simulate
915
+ end
916
+
917
+ end
918
+
919
+ # Sets the access policy for a bucket.
920
+ # @overload set_bucket_policy(options = {})
921
+ # @param [Hash] options
922
+ # @option options [required,String] :bucket_name
923
+ # @option options [required,String] :policy This can be a String
924
+ # or any object that responds to `#to_json`.
925
+ # @return [Core::Response]
926
+ # bucket_method(:set_bucket_policy, :put, 'policy') do
927
+ #
928
+ # configure_request do |req, options|
929
+ # require_policy!(options[:policy])
930
+ # super(req, options)
931
+ # policy = options[:policy]
932
+ # policy = policy.to_json unless policy.respond_to?(:to_str)
933
+ # req.body = policy
934
+ # end
935
+ #
936
+ # end
937
+
938
+ # Gets the access policy for a bucket.
939
+ # @overload get_bucket_policy(options = {})
940
+ # @param [Hash] options
941
+ # @option options [required,String] :bucket_name
942
+ # @return [Core::Response]
943
+ # bucket_method(:get_bucket_policy, :get, 'policy') do
944
+ #
945
+ # process_response do |resp|
946
+ # resp.data[:policy] = resp.http_response.body
947
+ # end
948
+ #
949
+ # end
950
+
951
+ # Deletes the access policy for a bucket.
952
+ # @overload delete_bucket_policy(options = {})
953
+ # @param [Hash] options
954
+ # @option options [required,String] :bucket_name
955
+ # @return [Core::Response]
956
+ # bucket_method(:delete_bucket_policy, :delete, 'policy')
957
+
958
+ # @overload set_bucket_versioning(options = {})
959
+ # @param [Hash] options
960
+ # @option options [required,String] :bucket_name
961
+ # @option options [required,String] :state
962
+ # @option options [String] :mfa_delete
963
+ # @option options [String] :mfa
964
+ # @return [Core::Response]
965
+ # bucket_method(:set_bucket_versioning, :put, 'versioning', :header_options => { :mfa => "x-amz-mfa" }) do
966
+ #
967
+ # configure_request do |req, options|
968
+ # state = options[:state].to_s.downcase.capitalize
969
+ # unless state =~ /^(Enabled|Suspended)$/
970
+ # raise ArgumentError, "invalid versioning state `#{state}`"
971
+ # end
972
+ #
973
+ # # Leave validation of MFA options to S3
974
+ # mfa_delete = options[:mfa_delete].to_s.downcase.capitalize if options[:mfa_delete]
975
+ #
976
+ # # Generate XML request for versioning
977
+ # req.body = Nokogiri::XML::Builder.new do |xml|
978
+ # xml.VersioningConfiguration('xmlns' => XMLNS) do
979
+ # xml.Status(state)
980
+ # xml.MfaDelete(mfa_delete) if mfa_delete
981
+ # end
982
+ # end.doc.root.to_xml
983
+ #
984
+ # super(req, options)
985
+ # end
986
+ #
987
+ # end
988
+
989
+ # Gets the bucket's location constraint.
990
+ # @overload get_bucket_location(options = {})
991
+ # @param [Hash] options
992
+ # @option options [required,String] :bucket_name
993
+ # @return [Core::Response]
994
+ # bucket_method(:get_bucket_location, :get, 'location') do
995
+ #
996
+ # process_response do |response|
997
+ # regex = />(.*)<\/LocationConstraint>/
998
+ # matches = response.http_response.body.to_s.match(regex)
999
+ # response.data[:location_constraint] = matches ? matches[1] : nil
1000
+ # end
1001
+ #
1002
+ # end
1003
+
1004
+ # @overload put_bucket_logging(options = {})
1005
+ # @param [Hash] options
1006
+ # @option options [required,String] :bucket_name
1007
+ # @option options [Boolean] :logging_enabled Set to true if turning on
1008
+ # bucket logging. If not set or false, all of the following options
1009
+ # will be ignored.
1010
+ # @option options [String] :target_bucket The name of the bucket in
1011
+ # which you want Amazon S3 to store server access logs. You can push
1012
+ # logs to any bucket you own, including the bucket being logged.
1013
+ # @option options [String] :target_prefix Allows you to specify a prefix
1014
+ # for the keys that the log files will be stored under. Recommended
1015
+ # if you will be writing logs from multiple buckets to the same target
1016
+ # bucket.
1017
+ # @option options [Array<Hash>] :grants An array of hashes specifying
1018
+ # permission grantees. For each hash, specify ONLY ONE of :id, :uri,
1019
+ # or :email_address.
1020
+ # * `:email_address` - (String) E-mail address of the person being
1021
+ # granted logging permissions.
1022
+ # * `:id` - (String) The canonical user ID of the grantee.
1023
+ # * `:uri` - (String) URI of the grantee group.
1024
+ # * `:permission` - (String) Logging permissions given to the Grantee
1025
+ # for the bucket. The bucket owner is automatically granted FULL_CONTROL
1026
+ # to all logs delivered to the bucket. This optional element enables
1027
+ # you grant access to others. Valid Values: FULL_CONTROL | READ | WRITE
1028
+ # @return [Core::Response]
1029
+ # bucket_method(:put_bucket_logging, :put) do
1030
+ # configure_request do |req, options|
1031
+ #
1032
+ # req.add_param('logging')
1033
+ #
1034
+ # xml = Nokogiri::XML::Builder.new
1035
+ # xml.BucketLoggingStatus('xmlns' => XMLNS) do |xml|
1036
+ # if options[:logging_enabled] == true
1037
+ # xml.LoggingEnabled do
1038
+ # xml.TargetBucket(options[:target_bucket])
1039
+ # xml.TargetPrefix(options[:target_prefix])
1040
+ # unless options[:grants].nil?
1041
+ #
1042
+ # xml.TargetGrants do
1043
+ # options[:grants].each do |grant|
1044
+ # xml.Grant do
1045
+ # if !grant[:email_address].nil?
1046
+ # xml.Grantee('xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
1047
+ # 'xsi:type' => 'AmazonCustomerByEmail') do
1048
+ # xml.EmailAddress(grant[:email_address])
1049
+ # end
1050
+ # elsif !grant[:uri].nil?
1051
+ # xml.Grantee('xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
1052
+ # 'xsi:type' => 'Group') do
1053
+ # xml.URI(grant[:uri])
1054
+ # end
1055
+ # elsif !grant[:id].nil?
1056
+ # xml.Grantee('xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
1057
+ # 'xsi:type' => 'CanonicalUser') do
1058
+ # xml.ID(grant[:id])
1059
+ # end
1060
+ # end
1061
+ #
1062
+ # xml.Permission(grant[:permission])
1063
+ # end
1064
+ # end
1065
+ # end
1066
+ # end
1067
+ # end
1068
+ # end
1069
+ # end
1070
+ #
1071
+ # xml = xml.doc.root.to_xml
1072
+ # req.body = xml
1073
+ # req.headers['content-md5'] = md5(xml)
1074
+ #
1075
+ # super(req, options)
1076
+ #
1077
+ # end
1078
+ # end
1079
+
1080
+ # Gets the bucket's logging status.
1081
+ # @overload get_bucket_logging(options = {})
1082
+ # @param [Hash] options
1083
+ # @option options [required,String] :bucket_name
1084
+ # @return [Core::Response]
1085
+ # bucket_method(:get_bucket_logging, :get, 'logging',
1086
+ # XML::GetBucketLogging)
1087
+
1088
+ # @overload get_bucket_versioning(options = {})
1089
+ # @param [Hash] options
1090
+ # @option options [required,String] :bucket_name
1091
+ # @return [Core::Response]
1092
+ bucket_method(:get_bucket_versioning, :get, 'versioning',
1093
+ XML::GetBucketVersioning)
1094
+
1095
+ # @overload list_object_versions(options = {})
1096
+ # @param [Hash] options
1097
+ # @option options [required,String] :bucket_name
1098
+ # @option options [String] :prefix
1099
+ # @option options [String] :delimiter
1100
+ # @option options [String] :max_keys
1101
+ # @option options [String] :key_marker
1102
+ # @option options [String] :version_id_marker
1103
+ # @return [Core::Response]
1104
+ # bucket_method(:list_object_versions, :get, 'versions',
1105
+ # XML::ListObjectVersions) do
1106
+ #
1107
+ # configure_request do |req, options|
1108
+ # super(req, options)
1109
+ # params = %w(delimiter key_marker max_keys prefix version_id_marker)
1110
+ # params.each do |param|
1111
+ # if options[param.to_sym]
1112
+ # req.add_param(param.gsub(/_/, '-'), options[param.to_sym])
1113
+ # end
1114
+ # end
1115
+ # end
1116
+ #
1117
+ # end
1118
+
1119
+ # Sets the access control list for a bucket. You must specify an ACL
1120
+ # via one of the following methods:
1121
+ #
1122
+ # * as a canned ACL (via `:acl`)
1123
+ # * as a list of grants (via the `:grant_*` options)
1124
+ # * as an access control policy document (via `:access_control_policy`)
1125
+ #
1126
+ # @example Using a canned acl
1127
+ # s3_client.put_bucket_acl(
1128
+ # :bucket_name => 'bucket-name',
1129
+ # :acl => 'public-read')
1130
+ #
1131
+ # @example Using grants
1132
+ # s3_client.put_bucket_acl(
1133
+ # :bucket_name => 'bucket-name',
1134
+ # :grant_read => 'uri="http://acs.amazonmss.com/groups/global/AllUsers"',
1135
+ #
1136
+ # @example Using an access control policy document
1137
+ # policy_xml = <<-XML
1138
+ # <AccessControlPolicy xmlns="http://s3.amazonmss.com/doc/2006-03-01/">
1139
+ # <Owner>
1140
+ # <ID>852b113e7a2f25102679df27bb0ae12b3f85be6BucketOwnerCanonicalUserID</ID>
1141
+ # <DisplayName>OwnerDisplayName</DisplayName>
1142
+ # </Owner>
1143
+ # <AccessControlList>
1144
+ # <Grant>
1145
+ # <Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CanonicalUser">
1146
+ # <ID>BucketOwnerCanonicalUserID</ID>
1147
+ # <DisplayName>OwnerDisplayName</DisplayName>
1148
+ # </Grantee>
1149
+ # <Permission>FULL_CONTROL</Permission>
1150
+ # </Grant>
1151
+ # <Grant>
1152
+ # <Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Group">
1153
+ # <URI xmlns="">http://acs.amazonmss.com/groups/global/AllUsers</URI>
1154
+ # </Grantee>
1155
+ # <Permission xmlns="">READ</Permission>
1156
+ # </Grant>
1157
+ # </AccessControlList>
1158
+ # </AccessControlPolicy>
1159
+ #
1160
+ # XML
1161
+ # s3_client.put_bucket_acl(
1162
+ # :bucket_name => 'bucket-name',
1163
+ # :access_control_policy => policy_xml)
1164
+ #
1165
+ # @overload put_bucket_acl(options = {})
1166
+ # @param [Hash] options
1167
+ # @option options [required,String] :bucket_name
1168
+ # @option options [String] :access_control_policy An access control
1169
+ # policy description as a string of XML. See the S3 API
1170
+ # documentation for a description.
1171
+ # @option options [String] :acl A canned ACL (e.g. 'private',
1172
+ # 'public-read', etc). See the S3 API documentation for
1173
+ # a complete list of valid values.
1174
+ # @option options [String] :grant_read
1175
+ # @option options [String] :grant_write
1176
+ # @option options [String] :grant_read_acp
1177
+ # @option options [String] :grant_write_acp
1178
+ # @option options [String] :grant_full_control
1179
+ # @return [Core::Response]
1180
+ bucket_method(:put_bucket_acl, :put, 'acl', :header_options => {
1181
+ :acl => 'x-amz-acl',
1182
+ :grant_read => 'x-amz-grant-read',
1183
+ :grant_write => 'x-amz-grant-write',
1184
+ :grant_read_acp => 'x-amz-grant-read-acp',
1185
+ :grant_write_acp => 'x-amz-grant-write-acp',
1186
+ :grant_full_control => 'x-amz-grant-full-control',
1187
+ }) do
1188
+
1189
+ configure_request do |req, options|
1190
+ move_access_control_policy(options)
1191
+ require_acl!(options)
1192
+ super(req, options)
1193
+ req.body = options[:access_control_policy] if
1194
+ options[:access_control_policy]
1195
+ end
1196
+
1197
+ end
1198
+ alias_method :set_bucket_acl, :put_bucket_acl
1199
+
1200
+ # Gets the access control list for a bucket.
1201
+ # @overload get_bucket_acl(options = {})
1202
+ # @param [Hash] options
1203
+ # @option options [required,String] :bucket_name
1204
+ # @return [Core::Response]
1205
+ bucket_method(:get_bucket_acl, :get, 'acl', XML::GetBucketAcl)
1206
+
1207
+ # Sets the access control list for an object. You must specify an ACL
1208
+ # via one of the following methods:
1209
+ #
1210
+ # * as a canned ACL (via `:acl`)
1211
+ # * as a list of grants (via the `:grant_*` options)
1212
+ # * as an access control policy document (via `:access_control_policy`)
1213
+ #
1214
+ # @example Using a canned acl
1215
+ # s3_client.put_object_acl(
1216
+ # :bucket_name => 'bucket-name',
1217
+ # :key => 'object-key',
1218
+ # :acl => 'public-read')
1219
+ #
1220
+ # @example Using grants
1221
+ # s3_client.put_bucket_acl(
1222
+ # :bucket_name => 'bucket-name',
1223
+ # :key => 'object-key',
1224
+ # :grant_read => 'uri="http://acs.amazonmss.com/groups/global/AllUsers"',
1225
+ #
1226
+ # @example Using an access control policy document
1227
+ # policy_xml = <<-XML
1228
+ # <AccessControlPolicy xmlns="http://s3.amazonmss.com/doc/2006-03-01/">
1229
+ # <Owner>
1230
+ # <ID>852b113e7a2f25102679df27bb0ae12b3f85be6BucketOwnerCanonicalUserID</ID>
1231
+ # <DisplayName>OwnerDisplayName</DisplayName>
1232
+ # </Owner>
1233
+ # <AccessControlList>
1234
+ # <Grant>
1235
+ # <Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CanonicalUser">
1236
+ # <ID>BucketOwnerCanonicalUserID</ID>
1237
+ # <DisplayName>OwnerDisplayName</DisplayName>
1238
+ # </Grantee>
1239
+ # <Permission>FULL_CONTROL</Permission>
1240
+ # </Grant>
1241
+ # <Grant>
1242
+ # <Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Group">
1243
+ # <URI xmlns="">http://acs.amazonmss.com/groups/global/AllUsers</URI>
1244
+ # </Grantee>
1245
+ # <Permission xmlns="">READ</Permission>
1246
+ # </Grant>
1247
+ # </AccessControlList>
1248
+ # </AccessControlPolicy>
1249
+ #
1250
+ # XML
1251
+ # s3_client.put_bucket_acl(
1252
+ # :bucket_name => 'bucket-name',
1253
+ # :key => 'object-key',
1254
+ # :access_control_policy => policy_xml)
1255
+ #
1256
+ # @overload put_object_acl(options = {})
1257
+ # @param [Hash] options
1258
+ # @option options [required,String] :bucket_name
1259
+ # @option options [required,String] :key
1260
+ # @option options [String] :access_control_policy An access control
1261
+ # policy description as a string of XML. See the S3 API
1262
+ # documentation for a description.
1263
+ # @option options [String] :acl A canned ACL (e.g. 'private',
1264
+ # 'public-read', etc). See the S3 API documentation for
1265
+ # a complete list of valid values.
1266
+ # @option options [String] :grant_read
1267
+ # @option options [String] :grant_write
1268
+ # @option options [String] :grant_read_acp
1269
+ # @option options [String] :grant_write_acp
1270
+ # @option options [String] :grant_full_control
1271
+ # @return [Core::Response]
1272
+ # object_method(:put_object_acl, :put, 'acl', :header_options => {
1273
+ # :acl => 'x-amz-acl',
1274
+ # :grant_read => 'x-amz-grant-read',
1275
+ # :grant_write => 'x-amz-grant-write',
1276
+ # :grant_read_acp => 'x-amz-grant-read-acp',
1277
+ # :grant_write_acp => 'x-amz-grant-write-acp',
1278
+ # :grant_full_control => 'x-amz-grant-full-control',
1279
+ # }) do
1280
+ #
1281
+ # configure_request do |req, options|
1282
+ # move_access_control_policy(options)
1283
+ # require_acl!(options)
1284
+ # super(req, options)
1285
+ # req.body = options[:access_control_policy] if
1286
+ # options[:access_control_policy]
1287
+ # end
1288
+ #
1289
+ # end
1290
+ # alias_method :set_object_acl, :put_object_acl
1291
+
1292
+ # Gets the access control list for an object.
1293
+ # @overload get_object_acl(options = {})
1294
+ # @param [Hash] options
1295
+ # @option options [required,String] :bucket_name
1296
+ # @option options [required,String] :key
1297
+ # @return [Core::Response]
1298
+ # object_method(:get_object_acl, :get, 'acl', XML::GetBucketAcl)
1299
+
1300
+ # Puts data into an object, replacing the current contents.
1301
+ #
1302
+ # s3_client.put_object({
1303
+ # :bucket_name => 'bucket-name',
1304
+ # :key => 'readme.txt',
1305
+ # :data => 'This is the readme for ...',
1306
+ # })
1307
+ #
1308
+ # @overload put_object(options = {})
1309
+ # @param [Hash] options
1310
+ # @option options [required,String] :bucket_name
1311
+ # @option options [required,String] :key
1312
+ # @option options [required,String,Pathname,File,IO] :data
1313
+ # The data to upload. This can be provided as a string,
1314
+ # a Pathname object, or any object that responds to
1315
+ # `#read` and `#eof?` (e.g. IO, File, Tempfile, StringIO, etc).
1316
+ # @option options [Integer] :content_length
1317
+ # Required if you are using block form to write data or if it is
1318
+ # not possible to determine the size of `:data`. A best effort
1319
+ # is made to determine the content length of strings, files,
1320
+ # tempfiles, io objects, and any object that responds
1321
+ # to `#length` or `#size`.
1322
+ # @option options [String] :website_redirect_location If the bucket is
1323
+ # configured as a website, redirects requests for this object to
1324
+ # another object in the same bucket or to an external URL.
1325
+ # @option options [Hash] :metadata
1326
+ # A hash of metadata to be included with the
1327
+ # object. These will be sent to S3 as headers prefixed with
1328
+ # `x-amz-meta`.
1329
+ # @option options [Symbol] :acl (:private) A canned access
1330
+ # control policy. Accepted values include:
1331
+ # * `:private`
1332
+ # * `:public_read`
1333
+ # * ...
1334
+ # @option options [String] :storage_class+ ('STANDARD')
1335
+ # Controls whether Reduced Redundancy Storage is enabled for
1336
+ # the object. Valid values are 'STANDARD' and
1337
+ # 'REDUCED_REDUNDANCY'.
1338
+ # @option options [Symbol,String] :server_side_encryption (nil) The
1339
+ # algorithm used to encrypt the object on the server side
1340
+ # (e.g. :aes256).
1341
+ # object on the server side, e.g. `:aes256`)
1342
+ # @option options [String] :cache_control
1343
+ # Can be used to specify caching behavior.
1344
+ # @option options [String] :content_disposition
1345
+ # Specifies presentational information.
1346
+ # @option options [String] :content_encoding
1347
+ # Specifies the content encoding.
1348
+ # @option options [String] :content_md5
1349
+ # The base64 encoded content md5 of the `:data`.
1350
+ # @option options [String] :content_type
1351
+ # Specifies the content type.
1352
+ # @option options [String] :expires The date and time at which the
1353
+ # object is no longer cacheable.
1354
+ # @option options [String] :acl A canned ACL (e.g. 'private',
1355
+ # 'public-read', etc). See the S3 API documentation for
1356
+ # a complete list of valid values.
1357
+ # @option options [String] :grant_read
1358
+ # @option options [String] :grant_write
1359
+ # @option options [String] :grant_read_acp
1360
+ # @option options [String] :grant_write_acp
1361
+ # @option options [String] :grant_full_control
1362
+ # @option options [String] :sse_customer_algorithm Specifies the
1363
+ # algorithm to use to when encrypting the object (e.g., AES256).
1364
+ # @option options [String] :sse_customer_key Specifies the
1365
+ # customer-provided encryption key for Amazon S3 to use in encrypting
1366
+ # data. This value is used to store the object and then it is
1367
+ # discarded; Amazon does not store the encryption key. The key must be
1368
+ # appropriate for use with the algorithm specified in the
1369
+ # `:sse_customer_algorithm` header.
1370
+ # @option options [String] :sse_customer_key_md5 Specifies the 128-bit
1371
+ # MD5 digest of the encryption key according to RFC 1321. Amazon S3
1372
+ # uses this header for a message integrity check to ensure the
1373
+ # encryption key was transmitted without error.
1374
+ # @return [Core::Response]
1375
+ #
1376
+ object_method(:put_object, :put, :header_options => {
1377
+ :website_redirect_location => 'x-amz-website-redirect-location',
1378
+ :acl => 'x-amz-acl',
1379
+ :grant_read => 'x-amz-grant-read',
1380
+ :grant_write => 'x-amz-grant-write',
1381
+ :grant_read_acp => 'x-amz-grant-read-acp',
1382
+ :grant_write_acp => 'x-amz-grant-write-acp',
1383
+ :grant_full_control => 'x-amz-grant-full-control',
1384
+ :content_md5 => 'Content-MD5',
1385
+ :cache_control => 'Cache-Control',
1386
+ :content_disposition => 'Content-Disposition',
1387
+ :content_encoding => 'Content-Encoding',
1388
+ :content_type => 'Content-Type',
1389
+ :expires => 'Expires',
1390
+ :sse_customer_algorithm => 'x-amz-server-side-encryption-customer-algorithm',
1391
+ :sse_customer_key => 'x-amz-server-side-encryption-customer-key',
1392
+ :sse_customer_key_md5 => 'x-amz-server-side-encryption-customer-key-MD5',
1393
+ }) do
1394
+
1395
+ configure_request do |request, options|
1396
+
1397
+ options = compute_write_options(options)
1398
+ set_body_stream_and_content_length(request, options)
1399
+
1400
+ set_metadata(request, options)
1401
+ set_storage_class(request, options)
1402
+ #set_server_side_encryption(request, options)
1403
+
1404
+ super(request, options)
1405
+
1406
+ end
1407
+
1408
+ process_response do |resp|
1409
+ extract_object_headers(resp)
1410
+ end
1411
+
1412
+ simulate_response do |response|
1413
+ response.data[:etag] = 'abc123'
1414
+ response.data[:version_id] = nil
1415
+ end
1416
+
1417
+ end
1418
+
1419
+ # Gets the data for a key.
1420
+ # @overload get_object(options = {})
1421
+ # @param [Hash] options
1422
+ # @option options [required,String] :bucket_name
1423
+ # @option options [required,String] :key
1424
+ # @option options [String] :request_payer If specified, the request
1425
+ # will contain the specified String value in the x-amz-request-payer
1426
+ # header. This is required for Requester Pays enabled buckets.
1427
+ # @option options [Time] :if_modified_since If specified, the
1428
+ # response will contain an additional `:modified` value that
1429
+ # returns true if the object was modified after the given
1430
+ # time. If `:modified` is false, then the response
1431
+ # `:data` value will be `nil`.
1432
+ # @option options [Time] :if_unmodified_since If specified, the
1433
+ # response will contain an additional `:unmodified` value
1434
+ # that is true if the object was not modified after the
1435
+ # given time. If `:unmodified` returns false, the `:data`
1436
+ # value will be `nil`.
1437
+ # @option options [String] :if_match If specified, the response
1438
+ # will contain an additional `:matches` value that is true
1439
+ # if the object ETag matches the value for this option. If
1440
+ # `:matches` is false, the `:data` value of the
1441
+ # response will be `nil`.
1442
+ # @option options [String] :if_none_match If specified, the
1443
+ # response will contain an additional `:matches` value that
1444
+ # is true if and only if the object ETag matches the value for
1445
+ # this option. If `:matches` is true, the `:data` value
1446
+ # of the response will be `nil`.
1447
+ # @option options [String] :sse_customer_algorithm Specifies the
1448
+ # algorithm to use to when encrypting the object (e.g., AES256).
1449
+ # @option options [String] :sse_customer_key Specifies the
1450
+ # customer-provided encryption key for Amazon S3 to use in encrypting
1451
+ # data. This value is used to store the object and then it is
1452
+ # discarded; Amazon does not store the encryption key. The key must be
1453
+ # appropriate for use with the algorithm specified in the
1454
+ # `:sse_customer_algorithm` header.
1455
+ # @option options [String] :sse_customer_key_md5 Specifies the 128-bit
1456
+ # MD5 digest of the encryption key according to RFC 1321. Amazon S3
1457
+ # uses this header for a message integrity check to ensure the
1458
+ # encryption key was transmitted without error.
1459
+ # @option options [Range<Integer>] :range A byte range of data to request.
1460
+ # @return [Core::Response]
1461
+ #
1462
+ object_method(:get_object, :get,
1463
+ :header_options => {
1464
+ :request_payer => "x-amz-request-payer",
1465
+ :if_modified_since => "If-Modified-Since",
1466
+ :if_unmodified_since => "If-Unmodified-Since",
1467
+ :if_match => "If-Match",
1468
+ :if_none_match => "If-None-Match",
1469
+ :sse_customer_algorithm => 'x-amz-server-side-encryption-customer-algorithm',
1470
+ :sse_customer_key => 'x-amz-server-side-encryption-customer-key',
1471
+ :sse_customer_key_md5 => 'x-amz-server-side-encryption-customer-key-MD5',
1472
+ }) do
1473
+ configure_request do |req, options|
1474
+
1475
+ super(req, options)
1476
+
1477
+ if options[:version_id]
1478
+ req.add_param('versionId', options[:version_id])
1479
+ end
1480
+
1481
+ ["If-Modified-Since",
1482
+ "If-Unmodified-Since"].each do |date_header|
1483
+ case value = req.headers[date_header]
1484
+ when DateTime
1485
+ req.headers[date_header] = Time.parse(value.to_s).rfc2822
1486
+ when Time
1487
+ req.headers[date_header] = value.rfc2822
1488
+ end
1489
+ end
1490
+
1491
+ if options[:range]
1492
+ range = options[:range]
1493
+ if range.is_a?(Range)
1494
+ offset = range.exclude_end? ? -1 : 0
1495
+ range = "bytes=#{range.first}-#{range.last + offset}"
1496
+ end
1497
+ req.headers['Range'] = range
1498
+ end
1499
+
1500
+ end
1501
+
1502
+ process_response do |resp|
1503
+ extract_object_headers(resp)
1504
+ resp.data[:data] = resp.http_response.body
1505
+ end
1506
+
1507
+ end
1508
+
1509
+ # Gets the torrent for a key.
1510
+ # @overload get_object_torrent(options = {})
1511
+ # @param [Hash] options
1512
+ # @option options [required,String] :bucket_name
1513
+ # @option options [required,String] :key
1514
+ # @return [Core::Response]
1515
+ #
1516
+ # object_method(:get_object_torrent, :get, 'torrent') do
1517
+ # process_response do |resp|
1518
+ # extract_object_headers(resp)
1519
+ # resp.data[:data] = resp.http_response.body
1520
+ # end
1521
+ # end
1522
+
1523
+ # @overload head_object(options = {})
1524
+ # @param [Hash] options
1525
+ # @option options [required,String] :bucket_name
1526
+ # @option options [required,String] :key
1527
+ # @option options [String] :version_id
1528
+ # @option options [Time] :if_modified_since If specified, the
1529
+ # response will contain an additional `:modified` value that
1530
+ # returns true if the object was modified after the given
1531
+ # time. If `:modified` is false, then the response
1532
+ # `:data` value will be `nil`.
1533
+ # @option options [Time] :if_unmodified_since If specified, the
1534
+ # response will contain an additional `:unmodified` value
1535
+ # that is true if the object was not modified after the
1536
+ # given time. If `:unmodified` returns false, the `:data`
1537
+ # value will be `nil`.
1538
+ # @option options [String] :if_match If specified, the response
1539
+ # will contain an additional `:matches` value that is true
1540
+ # if the object ETag matches the value for this option. If
1541
+ # `:matches` is false, the `:data` value of the
1542
+ # response will be `nil`.
1543
+ # @option options [String] :if_none_match If specified, the
1544
+ # response will contain an additional `:matches` value that
1545
+ # is true if and only if the object ETag matches the value for
1546
+ # this option. If `:matches` is true, the `:data` value
1547
+ # of the response will be `nil`.
1548
+ # @option options [String] :sse_customer_algorithm Specifies the
1549
+ # algorithm to use to when encrypting the object (e.g., AES256).
1550
+ # @option options [String] :sse_customer_key Specifies the
1551
+ # customer-provided encryption key for Amazon S3 to use in encrypting
1552
+ # data. This value is used to store the object and then it is
1553
+ # discarded; Amazon does not store the encryption key. The key must be
1554
+ # appropriate for use with the algorithm specified in the
1555
+ # `:sse_customer_algorithm` header.
1556
+ # @option options [String] :sse_customer_key_md5 Specifies the 128-bit
1557
+ # MD5 digest of the encryption key according to RFC 1321. Amazon S3
1558
+ # uses this header for a message integrity check to ensure the
1559
+ # encryption key was transmitted without error.
1560
+ # @option options [Range<Integer>] :range A byte range of data to request.
1561
+ # @return [Core::Response]
1562
+ object_method(:head_object, :head,
1563
+ :header_options => {
1564
+ :if_modified_since => "If-Modified-Since",
1565
+ :if_unmodified_since => "If-Unmodified-Since",
1566
+ :if_match => "If-Match",
1567
+ :if_none_match => "If-None-Match",
1568
+ :sse_customer_algorithm => 'x-amz-server-side-encryption-customer-algorithm',
1569
+ :sse_customer_key => 'x-amz-server-side-encryption-customer-key',
1570
+ :sse_customer_key_md5 => 'x-amz-server-side-encryption-customer-key-MD5',
1571
+ }) do
1572
+
1573
+ configure_request do |req, options|
1574
+ super(req, options)
1575
+ if options[:version_id]
1576
+ req.add_param('versionId', options[:version_id])
1577
+ end
1578
+
1579
+ ["If-Modified-Since",
1580
+ "If-Unmodified-Since"].each do |date_header|
1581
+ case value = req.headers[date_header]
1582
+ when DateTime
1583
+ req.headers[date_header] = Time.parse(value.to_s).rfc2822
1584
+ when Time
1585
+ req.headers[date_header] = value.rfc2822
1586
+ end
1587
+ end
1588
+
1589
+ if options[:range]
1590
+ range = options[:range]
1591
+ if range.is_a?(Range)
1592
+ offset = range.exclude_end? ? -1 : 0
1593
+ range = "bytes=#{range.first}-#{range.last + offset}"
1594
+ end
1595
+ req.headers['Range'] = range
1596
+ end
1597
+ end
1598
+
1599
+ process_response do |resp|
1600
+ extract_object_headers(resp)
1601
+ end
1602
+
1603
+ end
1604
+
1605
+ # @overload delete_object(options = {})
1606
+ # @param [Hash] options
1607
+ # @option options [required,String] :bucket_name
1608
+ # @option options [required,String] :key
1609
+ # @option options [String] :version_id
1610
+ # @option options [String] :mfa
1611
+ # @return [Core::Response]
1612
+ object_method(:delete_object, :delete, :header_options => { :mfa => "x-amz-mfa" }) do
1613
+
1614
+ configure_request do |req, options|
1615
+ super(req, options)
1616
+ if options[:version_id]
1617
+ req.add_param('versionId', options[:version_id])
1618
+ end
1619
+ end
1620
+
1621
+ process_response do |resp|
1622
+ resp.data[:version_id] = resp.http_response.header('x-amz-version-id')
1623
+ end
1624
+
1625
+ end
1626
+
1627
+ # @overload restore_object(options = {})
1628
+ # Restores a temporary copy of an archived object.
1629
+ # @param [Hash] options
1630
+ # @option options [required,String] :bucket_name
1631
+ # @option options [required,String] :key
1632
+ # @option options [required,Integer] :days the number of days to keep
1633
+ # the restored object.
1634
+ # @return [Core::Response]
1635
+ # @since 1.7.2
1636
+ # object_method(:restore_object, :post, 'restore',
1637
+ # :header_options => { :content_md5 => 'Content-MD5' }) do
1638
+ # configure_request do |req, options|
1639
+ # super(req, options)
1640
+ #
1641
+ # validate!(:days, options[:days]) do
1642
+ # "must be greater or equal to 1" if options[:days].to_i <= 0
1643
+ # end
1644
+ #
1645
+ # xml = Nokogiri::XML::Builder.new do |xml|
1646
+ # xml.RestoreRequest('xmlns' => XMLNS) do
1647
+ # xml.Days(options[:days].to_i) if options[:days]
1648
+ # end
1649
+ # end.doc.root.to_xml
1650
+ #
1651
+ # req.body = xml
1652
+ # req.headers['content-type'] = 'application/xml'
1653
+ # req.headers['content-md5'] = md5(xml)
1654
+ # end
1655
+ # end
1656
+
1657
+
1658
+ # @overload list_objects(options = {})
1659
+ # @param [Hash] options
1660
+ # @option options [required,String] :bucket_name
1661
+ # @option options [String] :delimiter
1662
+ # @option options [String] :marker
1663
+ # @option options [String] :max_keys
1664
+ # @option options [String] :prefix
1665
+ # @return [Core::Response]
1666
+ bucket_method(:list_objects, :get, XML::ListObjects) do
1667
+ configure_request do |req, options|
1668
+ super(req, options)
1669
+ params = %w(delimiter marker max_keys prefix)
1670
+ params.each do |param|
1671
+ if options[param.to_sym]
1672
+ req.add_param(param.gsub(/_/, '-'), options[param.to_sym])
1673
+ end
1674
+ end
1675
+ end
1676
+ end
1677
+
1678
+ alias_method :get_bucket, :list_objects
1679
+
1680
+ # @overload initiate_multipart_upload(options = {})
1681
+ # @param [Hash] options
1682
+ # @option options [required,String] :bucket_name
1683
+ # @option options [required,String] :key
1684
+ # @option options [String] :website_redirect_location If the bucket is
1685
+ # configured as a website, redirects requests for this object to
1686
+ # another object in the same bucket or to an external URL.
1687
+ # @option options [Hash] :metadata
1688
+ # @option options [Symbol] :acl
1689
+ # @option options [String] :cache_control
1690
+ # @option options [String] :content_disposition
1691
+ # @option options [String] :content_encoding
1692
+ # @option options [String] :content_type
1693
+ # @option options [String] :storage_class+ ('STANDARD')
1694
+ # Controls whether Reduced Redundancy Storage is enabled for
1695
+ # the object. Valid values are 'STANDARD' and
1696
+ # 'REDUCED_REDUNDANCY'.
1697
+ # @option options [Symbol,String] :server_side_encryption (nil) The
1698
+ # algorithm used to encrypt the object on the server side
1699
+ # (e.g. :aes256).
1700
+ # @option options [String] :expires The date and time at which the
1701
+ # object is no longer cacheable.
1702
+ # @option options [String] :acl A canned ACL (e.g. 'private',
1703
+ # 'public-read', etc). See the S3 API documentation for
1704
+ # a complete list of valid values.
1705
+ # @option options [String] :grant_read
1706
+ # @option options [String] :grant_write
1707
+ # @option options [String] :grant_read_acp
1708
+ # @option options [String] :grant_write_acp
1709
+ # @option options [String] :grant_full_control
1710
+ # @option options [String] :sse_customer_algorithm Specifies the
1711
+ # algorithm to use to when encrypting the object (e.g., AES256).
1712
+ # @option options [String] :sse_customer_key Specifies the
1713
+ # customer-provided encryption key for Amazon S3 to use in encrypting
1714
+ # data. This value is used to store the object and then it is
1715
+ # discarded; Amazon does not store the encryption key. The key must be
1716
+ # appropriate for use with the algorithm specified in the
1717
+ # `:sse_customer_algorithm` header.
1718
+ # @option options [String] :sse_customer_key_md5 Specifies the 128-bit
1719
+ # MD5 digest of the encryption key according to RFC 1321. Amazon S3
1720
+ # uses this header for a message integrity check to ensure the
1721
+ # encryption key was transmitted without error.
1722
+ # @return [Core::Response]
1723
+ object_method(:initiate_multipart_upload, :post, 'uploads',
1724
+ XML::InitiateMultipartUpload,
1725
+ :header_options => {
1726
+ :website_redirect_location => 'x-amz-website-redirect-location',
1727
+ :acl => 'x-amz-acl',
1728
+ :grant_read => 'x-amz-grant-read',
1729
+ :grant_write => 'x-amz-grant-write',
1730
+ :grant_read_acp => 'x-amz-grant-read-acp',
1731
+ :grant_write_acp => 'x-amz-grant-write-acp',
1732
+ :grant_full_control => 'x-amz-grant-full-control',
1733
+ :cache_control => 'Cache-Control',
1734
+ :content_disposition => 'Content-Disposition',
1735
+ :content_encoding => 'Content-Encoding',
1736
+ :content_type => 'Content-Type',
1737
+ :expires => 'Expires',
1738
+ :sse_customer_algorithm => 'x-amz-server-side-encryption-customer-algorithm',
1739
+ :sse_customer_key => 'x-amz-server-side-encryption-customer-key',
1740
+ :sse_customer_key_md5 => 'x-amz-server-side-encryption-customer-key-MD5',
1741
+ }) do
1742
+
1743
+ configure_request do |req, options|
1744
+ set_metadata(req, options)
1745
+ set_storage_class(req, options)
1746
+ #set_server_side_encryption(req, options)
1747
+ super(req, options)
1748
+ end
1749
+
1750
+ process_response do |resp|
1751
+ extract_object_headers(resp)
1752
+ end
1753
+
1754
+ end
1755
+
1756
+ # @overload list_multipart_uploads(options = {})
1757
+ # @param [Hash] options
1758
+ # @option options [required,String] :bucket_name
1759
+ # @option options [String] :delimiter
1760
+ # @option options [String] :key_marker
1761
+ # @option options [String] :max_keys
1762
+ # @option options [String] :upload_id_marker
1763
+ # @option options [String] :max_uploads
1764
+ # @option options [String] :prefix
1765
+ # @return [Core::Response]
1766
+ bucket_method(:list_multipart_uploads,
1767
+ :get, 'uploads',
1768
+ XML::ListMultipartUploads) do
1769
+ configure_request do |req, options|
1770
+ super(req, options)
1771
+ params = %w(delimiter key_marker max_keys) +
1772
+ %w(upload_id_marker max_uploads prefix)
1773
+ params.each do |param|
1774
+ if options[param.to_sym]
1775
+ req.add_param(param.gsub(/_/, '-'), options[param.to_sym])
1776
+ end
1777
+ end
1778
+ end
1779
+ end
1780
+
1781
+ # @overload delete_objects(options = {})
1782
+ # @param [Hash] options
1783
+ # @option options [required,String] :bucket_name
1784
+ # @option options [required,Array<Hash>] :objects Each entry should be
1785
+ # a hash with the following keys:
1786
+ # * `:key` - *required*
1787
+ # * `:version_id`
1788
+ # @option options [Boolean] :quiet (true)
1789
+ # @option options [String] :mfa
1790
+ # @return [Core::Response]
1791
+ bucket_method(:delete_objects, :post, 'delete', XML::DeleteObjects,
1792
+ :header_options => { :mfa => "x-amz-mfa" }
1793
+ ) do
1794
+
1795
+ configure_request do |req, options|
1796
+
1797
+ super(req, options)
1798
+
1799
+ req.body = Nokogiri::XML::Builder.new do |xml|
1800
+ xml.Delete do
1801
+ xml.Quiet(options.key?(:quiet) ? options[:quiet] : true)
1802
+ (options[:objects] || options[:keys]).each do |obj|
1803
+ xml.Object do
1804
+ xml.Key(obj[:key])
1805
+ xml.VersionId(obj[:version_id]) if obj[:version_id]
1806
+ end
1807
+ end
1808
+ end
1809
+ end.doc.root.to_xml
1810
+
1811
+ req.headers['content-md5'] = md5(req.body)
1812
+
1813
+ end
1814
+ end
1815
+
1816
+ # @overload upload_part(options = {})
1817
+ # @param [Hash] options
1818
+ # @option options [required,String] :bucket_name
1819
+ # @option options [required,String] :key
1820
+ # @option options [required,String] :upload_id
1821
+ # @option options [required,Integer] :part_number
1822
+ # @option options [required,String,Pathname,File,IO] :data
1823
+ # The data to upload. This can be provided as a string,
1824
+ # a Pathname object, or any object that responds to
1825
+ # `#read` and `#eof?` (e.g. IO, File, Tempfile, StringIO, etc).
1826
+ # @return [Core::Response]
1827
+ object_method(:upload_part, :put,
1828
+ :header_options => {
1829
+ :content_md5 => 'Content-MD5',
1830
+ :sse_customer_algorithm => 'x-amz-server-side-encryption-customer-algorithm',
1831
+ :sse_customer_key => 'x-amz-server-side-encryption-customer-key',
1832
+ :sse_customer_key_md5 => 'x-amz-server-side-encryption-customer-key-MD5',
1833
+ }) do
1834
+ configure_request do |request, options|
1835
+
1836
+ options = compute_write_options(options)
1837
+ set_body_stream_and_content_length(request, options)
1838
+
1839
+ require_upload_id!(options[:upload_id])
1840
+ request.add_param('uploadId', options[:upload_id])
1841
+
1842
+ require_part_number!(options[:part_number])
1843
+ request.add_param('partNumber', options[:part_number])
1844
+
1845
+ super(request, options)
1846
+
1847
+ end
1848
+
1849
+ process_response do |resp|
1850
+ extract_object_headers(resp)
1851
+ end
1852
+
1853
+ simulate_response do |response|
1854
+ response.data[:etag] = 'abc123'
1855
+ end
1856
+ end
1857
+
1858
+ # @overload complete_multipart_upload(options = {})
1859
+ # @param [Hash] options
1860
+ # @option options [required,String] :bucket_name
1861
+ # @option options [required,String] :key
1862
+ # @option options [required,String] :upload_id
1863
+ # @option options [required,Array<Hash>] :parts An array of hashes
1864
+ # with the following keys:
1865
+ # * `:part_number` [Integer] - *required*
1866
+ # * `:etag` [String] - *required*
1867
+ # @return [Core::Response]
1868
+ object_method(:complete_multipart_upload, :post,
1869
+ XML::CompleteMultipartUpload) do
1870
+ configure_request do |req, options|
1871
+ require_upload_id!(options[:upload_id])
1872
+ validate_parts!(options[:parts])
1873
+ super(req, options)
1874
+ req.add_param('uploadId', options[:upload_id])
1875
+
1876
+ req.body = Nokogiri::XML::Builder.new do |xml|
1877
+ xml.CompleteMultipartUpload do
1878
+ options[:parts].each do |part|
1879
+ xml.Part do
1880
+ xml.PartNumber(part[:part_number])
1881
+ xml.ETag(part[:etag])
1882
+ end
1883
+ end
1884
+ end
1885
+ end.doc.root.to_xml
1886
+
1887
+ end
1888
+
1889
+ process_response do |resp|
1890
+ extract_object_headers(resp)
1891
+ end
1892
+
1893
+ simulate_response do |response|
1894
+ response.data = {}
1895
+ end
1896
+
1897
+ end
1898
+
1899
+ # @overload abort_multipart_upload(options = {})
1900
+ # @param [Hash] options
1901
+ # @option options [required,String] :bucket_name
1902
+ # @option options [required,String] :key
1903
+ # @option options [required,String] :upload_id
1904
+ # @return [Core::Response]
1905
+ object_method(:abort_multipart_upload, :delete) do
1906
+ configure_request do |req, options|
1907
+ require_upload_id!(options[:upload_id])
1908
+ super(req, options)
1909
+ req.add_param('uploadId', options[:upload_id])
1910
+ end
1911
+ end
1912
+
1913
+ # @overload list_parts(options = {})
1914
+ # @param [Hash] options
1915
+ # @option options [required,String] :bucket_name
1916
+ # @option options [required,String] :key
1917
+ # @option options [required,String] :upload_id
1918
+ # @option options [Integer] :max_parts
1919
+ # @option options [Integer] :part_number_marker
1920
+ # @return [Core::Response]
1921
+ object_method(:list_parts, :get, XML::ListParts) do
1922
+
1923
+ configure_request do |req, options|
1924
+ require_upload_id!(options[:upload_id])
1925
+ super(req, options)
1926
+ req.add_param('uploadId', options[:upload_id])
1927
+ req.add_param('max-parts', options[:max_parts])
1928
+ req.add_param('part-number-marker', options[:part_number_marker])
1929
+ end
1930
+
1931
+ end
1932
+
1933
+ # Copies an object from one key to another.
1934
+ # @overload copy_object(options = {})
1935
+ # @param [Hash] options
1936
+ # @option options [required, String] :bucket_name Name of the bucket
1937
+ # to copy a object into.
1938
+ # @option options [required, String] :key Where (object key) in the
1939
+ # bucket the object should be copied to.
1940
+ # @option options [String] :website_redirect_location If the bucket is
1941
+ # configured as a website, redirects requests for this object to
1942
+ # another object in the same bucket or to an external URL.
1943
+ # @option options [required, String] :copy_source The source
1944
+ # bucket name and key, joined by a forward slash ('/').
1945
+ # This string must be URL-encoded. Additionally, you must
1946
+ # have read access to the source object.
1947
+ # @option options [String] :acl A canned ACL (e.g. 'private',
1948
+ # 'public-read', etc). See the S3 API documentation for
1949
+ # a complete list of valid values.
1950
+ # @option options [Symbol,String] :server_side_encryption (nil) The
1951
+ # algorithm used to encrypt the object on the server side
1952
+ # (e.g. :aes256).
1953
+ # @option options [String] :storage_class+ ('STANDARD')
1954
+ # Controls whether Reduced Redundancy Storage is enabled for
1955
+ # the object. Valid values are 'STANDARD' and
1956
+ # 'REDUCED_REDUNDANCY'.
1957
+ # @option options [String] :metadata_directive ('COPY') Specify 'COPY' or
1958
+ # 'REPLACE'.
1959
+ # @option options [String] :content_type
1960
+ # @option options [String] :content_encoding
1961
+ # @option options [String] :content_disposition
1962
+ # @option options [String] :cache_control
1963
+ # @option options [String] :expires The date and time at which the
1964
+ # object is no longer cacheable.
1965
+ # @option options [String] :grant_read
1966
+ # @option options [String] :grant_write
1967
+ # @option options [String] :grant_read_acp
1968
+ # @option options [String] :grant_write_acp
1969
+ # @option options [String] :grant_full_control
1970
+ # @option options [String] :sse_customer_algorithm Specifies the
1971
+ # algorithm to use to when encrypting the object (e.g., AES256).
1972
+ # @option options [String] :sse_customer_key Specifies the
1973
+ # customer-provided encryption key for Amazon S3 to use in encrypting
1974
+ # data. This value is used to store the object and then it is
1975
+ # discarded; Amazon does not store the encryption key. The key must be
1976
+ # appropriate for use with the algorithm specified in the
1977
+ # `:sse_customer_algorithm` header.
1978
+ # @option options [String] :sse_customer_key_md5 Specifies the 128-bit
1979
+ # MD5 digest of the encryption key according to RFC 1321. Amazon S3
1980
+ # uses this header for a message integrity check to ensure the
1981
+ # encryption key was transmitted without error.
1982
+ # @option options [String] :copy_source_sse_customer_algorithm Specifies
1983
+ # the algorithm to use when decrypting the source object (e.g.,
1984
+ # AES256).
1985
+ # @option options [String] :copy_source_sse_customer_key Specifies the
1986
+ # customer-provided encryption key for Amazon S3 to use to decrypt the
1987
+ # source object. The encryption key provided in this header must be
1988
+ # one that was used when the source object was created.
1989
+ # @option options [String] :copy_source_sse_customer_key_md5 Specifies
1990
+ # the 128-bit MD5 digest of the encryption key according to RFC 1321.
1991
+ # Amazon S3 uses this header for a message integrity check to ensure
1992
+ # the encryption key was transmitted without error.
1993
+ # @return [Core::Response]
1994
+ object_method(:copy_object, :put, :header_options => {
1995
+ :website_redirect_location => 'x-amz-website-redirect-location',
1996
+ :acl => 'x-amz-acl',
1997
+ :grant_read => 'x-amz-grant-read',
1998
+ :grant_write => 'x-amz-grant-write',
1999
+ :grant_read_acp => 'x-amz-grant-read-acp',
2000
+ :grant_write_acp => 'x-amz-grant-write-acp',
2001
+ :grant_full_control => 'x-amz-grant-full-control',
2002
+ :copy_source => 'x-amz-copy-source',
2003
+ :cache_control => 'Cache-Control',
2004
+ :metadata_directive => 'x-amz-metadata-directive',
2005
+ :content_type => 'Content-Type',
2006
+ :content_encoding => 'Content-Encoding',
2007
+ :content_disposition => 'Content-Disposition',
2008
+ :expires => 'Expires',
2009
+ :sse_customer_algorithm => 'x-amz-server-side-encryption-customer-algorithm',
2010
+ :sse_customer_key => 'x-amz-server-side-encryption-customer-key',
2011
+ :sse_customer_key_md5 => 'x-amz-server-side-encryption-customer-key-MD5',
2012
+ :copy_source_sse_customer_algorithm => 'x-amz-copy-source-server-side-encryption-customer-algorithm',
2013
+ :copy_source_sse_customer_key => 'x-amz-copy-source-server-side-encryption-customer-key',
2014
+ :copy_source_sse_customer_key_md5 => 'x-amz-copy-source-server-side-encryption-customer-key-MD5',
2015
+ }) do
2016
+
2017
+ configure_request do |req, options|
2018
+
2019
+ validate!(:copy_source, options[:copy_source]) do
2020
+ "may not be blank" if options[:copy_source].to_s.empty?
2021
+ end
2022
+
2023
+ options = options.merge(:copy_source => escape_path(options[:copy_source]))
2024
+ super(req, options)
2025
+ set_metadata(req, options)
2026
+ set_storage_class(req, options)
2027
+ set_copy_content_length(req, options)
2028
+ #set_server_side_encryption(req, options)
2029
+
2030
+ if options[:version_id]
2031
+ req.headers['x-amz-copy-source'] += "?versionId=#{options[:version_id]}"
2032
+ end
2033
+ end
2034
+
2035
+ process_response do |resp|
2036
+ extract_object_headers(resp)
2037
+ end
2038
+
2039
+ end
2040
+
2041
+ object_method(:copy_part, :put, XML::CopyPart, :header_options => {
2042
+ :copy_source => 'x-amz-copy-source',
2043
+ :copy_source_range => 'x-amz-copy-source-range',
2044
+ }) do
2045
+
2046
+ configure_request do |request, options|
2047
+
2048
+ validate!(:copy_source, options[:copy_source]) do
2049
+ "may not be blank" if options[:copy_source].to_s.empty?
2050
+ end
2051
+
2052
+ validate!(:copy_source_range, options[:copy_source_range]) do
2053
+ "must start with bytes=" if options[:copy_source_range] && !options[:copy_source_range].start_with?("bytes=")
2054
+ end
2055
+
2056
+ options = options.merge(:copy_source => escape_path(options[:copy_source]))
2057
+
2058
+ require_upload_id!(options[:upload_id])
2059
+ request.add_param('uploadId', options[:upload_id])
2060
+
2061
+ require_part_number!(options[:part_number])
2062
+ request.add_param('partNumber', options[:part_number])
2063
+
2064
+ super(request, options)
2065
+
2066
+ if options[:version_id]
2067
+ req.headers['x-amz-copy-source'] += "?versionId=#{options[:version_id]}"
2068
+ end
2069
+
2070
+ end
2071
+
2072
+ end
2073
+
2074
+ end
2075
+ end
2076
+ end