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.
- checksums.yaml +7 -0
- data/.yardopts +9 -0
- data/LICENSE.txt +0 -0
- data/README.md +192 -0
- data/bin/mss-rb +178 -0
- data/ca-bundle.crt +3554 -0
- data/lib/mss/core/async_handle.rb +89 -0
- data/lib/mss/core/cacheable.rb +76 -0
- data/lib/mss/core/client.rb +786 -0
- data/lib/mss/core/collection/simple.rb +81 -0
- data/lib/mss/core/collection/with_limit_and_next_token.rb +70 -0
- data/lib/mss/core/collection/with_next_token.rb +96 -0
- data/lib/mss/core/collection.rb +262 -0
- data/lib/mss/core/configuration.rb +527 -0
- data/lib/mss/core/credential_providers.rb +653 -0
- data/lib/mss/core/data.rb +251 -0
- data/lib/mss/core/deprecations.rb +83 -0
- data/lib/mss/core/endpoints.rb +36 -0
- data/lib/mss/core/http/connection_pool.rb +374 -0
- data/lib/mss/core/http/curb_handler.rb +150 -0
- data/lib/mss/core/http/handler.rb +88 -0
- data/lib/mss/core/http/net_http_handler.rb +144 -0
- data/lib/mss/core/http/patch.rb +98 -0
- data/lib/mss/core/http/request.rb +258 -0
- data/lib/mss/core/http/response.rb +80 -0
- data/lib/mss/core/indifferent_hash.rb +87 -0
- data/lib/mss/core/inflection.rb +55 -0
- data/lib/mss/core/ini_parser.rb +41 -0
- data/lib/mss/core/json_client.rb +46 -0
- data/lib/mss/core/json_parser.rb +75 -0
- data/lib/mss/core/json_request_builder.rb +34 -0
- data/lib/mss/core/json_response_parser.rb +78 -0
- data/lib/mss/core/lazy_error_classes.rb +107 -0
- data/lib/mss/core/log_formatter.rb +426 -0
- data/lib/mss/core/managed_file.rb +31 -0
- data/lib/mss/core/meta_utils.rb +44 -0
- data/lib/mss/core/model.rb +61 -0
- data/lib/mss/core/naming.rb +29 -0
- data/lib/mss/core/option_grammar.rb +737 -0
- data/lib/mss/core/options/json_serializer.rb +81 -0
- data/lib/mss/core/options/validator.rb +154 -0
- data/lib/mss/core/options/xml_serializer.rb +117 -0
- data/lib/mss/core/page_result.rb +74 -0
- data/lib/mss/core/policy.rb +938 -0
- data/lib/mss/core/query_client.rb +40 -0
- data/lib/mss/core/query_error_parser.rb +23 -0
- data/lib/mss/core/query_request_builder.rb +46 -0
- data/lib/mss/core/query_response_parser.rb +34 -0
- data/lib/mss/core/region.rb +84 -0
- data/lib/mss/core/region_collection.rb +79 -0
- data/lib/mss/core/resource.rb +412 -0
- data/lib/mss/core/resource_cache.rb +39 -0
- data/lib/mss/core/response.rb +214 -0
- data/lib/mss/core/response_cache.rb +49 -0
- data/lib/mss/core/rest_error_parser.rb +23 -0
- data/lib/mss/core/rest_json_client.rb +39 -0
- data/lib/mss/core/rest_request_builder.rb +153 -0
- data/lib/mss/core/rest_response_parser.rb +65 -0
- data/lib/mss/core/rest_xml_client.rb +46 -0
- data/lib/mss/core/service_interface.rb +82 -0
- data/lib/mss/core/signers/base.rb +45 -0
- data/lib/mss/core/signers/cloud_front.rb +55 -0
- data/lib/mss/core/signers/s3.rb +158 -0
- data/lib/mss/core/signers/version_2.rb +71 -0
- data/lib/mss/core/signers/version_3.rb +85 -0
- data/lib/mss/core/signers/version_3_https.rb +60 -0
- data/lib/mss/core/signers/version_4/chunk_signed_stream.rb +190 -0
- data/lib/mss/core/signers/version_4.rb +227 -0
- data/lib/mss/core/uri_escape.rb +43 -0
- data/lib/mss/core/xml/frame.rb +245 -0
- data/lib/mss/core/xml/frame_stack.rb +84 -0
- data/lib/mss/core/xml/grammar.rb +306 -0
- data/lib/mss/core/xml/parser.rb +69 -0
- data/lib/mss/core/xml/root_frame.rb +64 -0
- data/lib/mss/core/xml/sax_handlers/libxml.rb +46 -0
- data/lib/mss/core/xml/sax_handlers/nokogiri.rb +55 -0
- data/lib/mss/core/xml/sax_handlers/ox.rb +40 -0
- data/lib/mss/core/xml/sax_handlers/rexml.rb +46 -0
- data/lib/mss/core/xml/stub.rb +122 -0
- data/lib/mss/core.rb +602 -0
- data/lib/mss/errors.rb +161 -0
- data/lib/mss/rails.rb +194 -0
- data/lib/mss/s3/access_control_list.rb +262 -0
- data/lib/mss/s3/acl_object.rb +263 -0
- data/lib/mss/s3/acl_options.rb +200 -0
- data/lib/mss/s3/bucket.rb +757 -0
- data/lib/mss/s3/bucket_collection.rb +161 -0
- data/lib/mss/s3/bucket_lifecycle_configuration.rb +472 -0
- data/lib/mss/s3/bucket_region_cache.rb +51 -0
- data/lib/mss/s3/bucket_tag_collection.rb +110 -0
- data/lib/mss/s3/bucket_version_collection.rb +78 -0
- data/lib/mss/s3/cipher_io.rb +119 -0
- data/lib/mss/s3/client/xml.rb +265 -0
- data/lib/mss/s3/client.rb +2076 -0
- data/lib/mss/s3/config.rb +60 -0
- data/lib/mss/s3/cors_rule.rb +107 -0
- data/lib/mss/s3/cors_rule_collection.rb +193 -0
- data/lib/mss/s3/data_options.rb +190 -0
- data/lib/mss/s3/encryption_utils.rb +145 -0
- data/lib/mss/s3/errors.rb +93 -0
- data/lib/mss/s3/multipart_upload.rb +353 -0
- data/lib/mss/s3/multipart_upload_collection.rb +75 -0
- data/lib/mss/s3/object_collection.rb +355 -0
- data/lib/mss/s3/object_metadata.rb +102 -0
- data/lib/mss/s3/object_upload_collection.rb +76 -0
- data/lib/mss/s3/object_version.rb +153 -0
- data/lib/mss/s3/object_version_collection.rb +88 -0
- data/lib/mss/s3/paginated_collection.rb +74 -0
- data/lib/mss/s3/policy.rb +73 -0
- data/lib/mss/s3/prefix_and_delimiter_collection.rb +46 -0
- data/lib/mss/s3/prefixed_collection.rb +84 -0
- data/lib/mss/s3/presign_v4.rb +135 -0
- data/lib/mss/s3/presigned_post.rb +574 -0
- data/lib/mss/s3/region_detection.rb +75 -0
- data/lib/mss/s3/request.rb +61 -0
- data/lib/mss/s3/s3_object.rb +1795 -0
- data/lib/mss/s3/tree/branch_node.rb +67 -0
- data/lib/mss/s3/tree/child_collection.rb +103 -0
- data/lib/mss/s3/tree/leaf_node.rb +93 -0
- data/lib/mss/s3/tree/node.rb +21 -0
- data/lib/mss/s3/tree/parent.rb +86 -0
- data/lib/mss/s3/tree.rb +115 -0
- data/lib/mss/s3/uploaded_part.rb +81 -0
- data/lib/mss/s3/uploaded_part_collection.rb +83 -0
- data/lib/mss/s3/website_configuration.rb +101 -0
- data/lib/mss/s3.rb +161 -0
- data/lib/mss/version.rb +16 -0
- data/lib/mss-sdk.rb +2 -0
- data/lib/mss.rb +14 -0
- data/rails/init.rb +14 -0
- 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
|