kerryb-right_aws 1.7.5 → 1.7.6
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.
- data/lib/ec2/right_ec2.rb +2 -1
- data/lib/s3/right_s3.rb +38 -0
- data/lib/s3/right_s3_interface.rb +244 -57
- metadata +1 -1
data/lib/ec2/right_ec2.rb
CHANGED
@@ -122,7 +122,8 @@ module RightAws
|
|
122
122
|
service_hash = {"Action" => action,
|
123
123
|
"AWSAccessKeyId" => @aws_access_key_id,
|
124
124
|
"Version" => @@api,
|
125
|
-
|
125
|
+
# MODIFIED: from Time.now.utc.stf... for eucalyptus
|
126
|
+
"Timestamp" => Time.now.strftime("%Y-%m-%dT%H:%M:%S.000Z"),
|
126
127
|
"SignatureVersion" => signature_version }
|
127
128
|
service_hash.update(params)
|
128
129
|
# prepare string to sight
|
data/lib/s3/right_s3.rb
CHANGED
@@ -53,6 +53,14 @@ module RightAws
|
|
53
53
|
# Create a new handle to an S3 account. All handles share the same per process or per thread
|
54
54
|
# HTTP connection to Amazon S3. Each handle is for a specific account.
|
55
55
|
# The +params+ are passed through as-is to RightAws::S3Interface.new
|
56
|
+
#
|
57
|
+
# Params is a hash:
|
58
|
+
#
|
59
|
+
# {:server => 's3.amazonaws.com' # Amazon service host: 's3.amazonaws.com'(default)
|
60
|
+
# :port => 443 # Amazon service port: 80 or 443(default)
|
61
|
+
# :protocol => 'https' # Amazon service protocol: 'http' or 'https'(default)
|
62
|
+
# :multi_thread => true|false # Multi-threaded (connection per each thread): true or false(default)
|
63
|
+
# :logger => Logger Object} # Logger instance: logs to STDOUT if omitted }
|
56
64
|
def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})
|
57
65
|
@interface = S3Interface.new(aws_access_key_id, aws_secret_access_key, params)
|
58
66
|
end
|
@@ -161,6 +169,36 @@ module RightAws
|
|
161
169
|
def location
|
162
170
|
@location ||= @s3.interface.bucket_location(@name)
|
163
171
|
end
|
172
|
+
|
173
|
+
# Retrieves the logging configuration for a bucket.
|
174
|
+
# Returns a hash of {:enabled, :targetbucket, :targetprefix}
|
175
|
+
#
|
176
|
+
# bucket.logging_info()
|
177
|
+
# => {:enabled=>true, :targetbucket=>"mylogbucket", :targetprefix=>"loggylogs/"}
|
178
|
+
def logging_info
|
179
|
+
@s3.interface.get_logging_parse(:bucket => @name)
|
180
|
+
end
|
181
|
+
|
182
|
+
# Enables S3 server access logging on a bucket. The target bucket must have been properly configured to receive server
|
183
|
+
# access logs.
|
184
|
+
# Params:
|
185
|
+
# :targetbucket - either the target bucket object or the name of the target bucket
|
186
|
+
# :targetprefix - the prefix under which all logs should be stored
|
187
|
+
#
|
188
|
+
# bucket.enable_logging(:targetbucket=>"mylogbucket", :targetprefix=>"loggylogs/")
|
189
|
+
# => true
|
190
|
+
def enable_logging(params)
|
191
|
+
AwsUtils.mandatory_arguments([:targetbucket, :targetprefix], params)
|
192
|
+
AwsUtils.allow_only([:targetbucket, :targetprefix], params)
|
193
|
+
xmldoc = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><BucketLoggingStatus xmlns=\"http://doc.s3.amazonaws.com/2006-03-01\"><LoggingEnabled><TargetBucket>#{params[:targetbucket]}</TargetBucket><TargetPrefix>#{params[:targetprefix]}</TargetPrefix></LoggingEnabled></BucketLoggingStatus>"
|
194
|
+
@s3.interface.put_logging(:bucket => @name, :xmldoc => xmldoc)
|
195
|
+
end
|
196
|
+
|
197
|
+
# Disables S3 server access logging on a bucket. Takes no arguments.
|
198
|
+
def disable_logging
|
199
|
+
xmldoc = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><BucketLoggingStatus xmlns=\"http://doc.s3.amazonaws.com/2006-03-01\"></BucketLoggingStatus>"
|
200
|
+
@s3.interface.put_logging(:bucket => @name, :xmldoc => xmldoc)
|
201
|
+
end
|
164
202
|
|
165
203
|
# Retrieve a group of keys from Amazon.
|
166
204
|
# +options+ is a hash: { 'prefix'=>'', 'marker'=>'', 'max-keys'=>5, 'delimiter'=>'' }).
|
@@ -32,6 +32,7 @@ module RightAws
|
|
32
32
|
DEFAULT_HOST = 's3.amazonaws.com'
|
33
33
|
DEFAULT_PORT = 443
|
34
34
|
DEFAULT_PROTOCOL = 'https'
|
35
|
+
DEFAULT_SERVICE = '/'
|
35
36
|
REQUEST_TTL = 30
|
36
37
|
DEFAULT_EXPIRES_AFTER = 1 * 24 * 60 * 60 # One day's worth of seconds
|
37
38
|
ONE_YEAR_IN_SECONDS = 365 * 24 * 60 * 60
|
@@ -49,7 +50,7 @@ module RightAws
|
|
49
50
|
|
50
51
|
# Creates new RightS3 instance.
|
51
52
|
#
|
52
|
-
# s3 = RightAws::S3Interface.new('1E3GDYEOGFJPIT7XXXXXX','hgTHt68JY07JKUY08ftHYtERkjgtfERn57XXXXXX', {:multi_thread => true, :logger => Logger.new('/tmp/x.log')}) #=> #<
|
53
|
+
# s3 = RightAws::S3Interface.new('1E3GDYEOGFJPIT7XXXXXX','hgTHt68JY07JKUY08ftHYtERkjgtfERn57XXXXXX', {:multi_thread => true, :logger => Logger.new('/tmp/x.log')}) #=> #<RightAws::S3Interface:0xb7b3c27c>
|
53
54
|
#
|
54
55
|
# Params is a hash:
|
55
56
|
#
|
@@ -62,7 +63,8 @@ module RightAws
|
|
62
63
|
def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})
|
63
64
|
init({ :name => 'S3',
|
64
65
|
:default_host => ENV['S3_URL'] ? URI.parse(ENV['S3_URL']).host : DEFAULT_HOST,
|
65
|
-
:default_port => ENV['S3_URL'] ? URI.parse(ENV['S3_URL']).port : DEFAULT_PORT,
|
66
|
+
:default_port => ENV['S3_URL'] ? URI.parse(ENV['S3_URL']).port : DEFAULT_PORT,
|
67
|
+
:default_service => ENV['S3_URL'] ? URI.parse(ENV['S3_URL']).path : DEFAULT_SERVICE,
|
66
68
|
:default_protocol => ENV['S3_URL'] ? URI.parse(ENV['S3_URL']).scheme : DEFAULT_PROTOCOL },
|
67
69
|
aws_access_key_id || ENV['AWS_ACCESS_KEY_ID'],
|
68
70
|
aws_secret_access_key || ENV['AWS_SECRET_ACCESS_KEY'],
|
@@ -95,10 +97,11 @@ module RightAws
|
|
95
97
|
out_string << '?acl' if path[/[&?]acl($|&|=)/]
|
96
98
|
out_string << '?torrent' if path[/[&?]torrent($|&|=)/]
|
97
99
|
out_string << '?location' if path[/[&?]location($|&|=)/]
|
98
|
-
|
100
|
+
out_string << '?logging' if path[/[&?]logging($|&|=)/] # this one is beta, no support for now
|
99
101
|
out_string
|
100
102
|
end
|
101
103
|
|
104
|
+
# http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?BucketRestrictions.html
|
102
105
|
def is_dns_bucket?(bucket_name)
|
103
106
|
bucket_name = bucket_name.to_s
|
104
107
|
return nil unless (3..63) === bucket_name.size
|
@@ -107,29 +110,34 @@ module RightAws
|
|
107
110
|
end
|
108
111
|
true
|
109
112
|
end
|
110
|
-
|
111
|
-
|
112
|
-
# Assumes that headers[:url] is URL encoded (use CGI::escape)
|
113
|
-
def generate_rest_request(method, headers) # :nodoc:
|
113
|
+
|
114
|
+
def fetch_request_params(headers) #:nodoc:
|
114
115
|
# default server to use
|
115
|
-
server
|
116
|
-
|
117
|
-
|
118
|
-
path_to_sign = "/#{path_to_sign}" unless path_to_sign[/^\//]
|
116
|
+
server = @params[:server]
|
117
|
+
service = @params[:service].to_s
|
118
|
+
service.chop! if service[%r{/$}] # remove trailing '/' from service
|
119
119
|
# extract bucket name and check it's dns compartibility
|
120
|
-
|
120
|
+
headers[:url].to_s[%r{^([a-z0-9._-]*)(/[^?]*)?(\?.+)?}i]
|
121
121
|
bucket_name, key_path, params_list = $1, $2, $3
|
122
122
|
# select request model
|
123
123
|
if is_dns_bucket?(bucket_name)
|
124
|
-
#
|
124
|
+
# fix a path
|
125
125
|
server = "#{bucket_name}.#{server}"
|
126
|
-
|
127
|
-
path = "#{key_path
|
128
|
-
# refactor the path (add '/' before params_list if the key is empty)
|
129
|
-
path_to_sign = "/#{bucket_name}#{path}"
|
126
|
+
key_path ||= '/'
|
127
|
+
path = "#{service}#{key_path}#{params_list}"
|
130
128
|
else
|
131
|
-
path =
|
129
|
+
path = "#{service}/#{bucket_name}#{key_path}#{params_list}"
|
132
130
|
end
|
131
|
+
path_to_sign = "#{service}/#{bucket_name}#{key_path}#{params_list}"
|
132
|
+
# path_to_sign = "/#{bucket_name}#{key_path}#{params_list}"
|
133
|
+
[ server, path, path_to_sign ]
|
134
|
+
end
|
135
|
+
|
136
|
+
# Generates request hash for REST API.
|
137
|
+
# Assumes that headers[:url] is URL encoded (use CGI::escape)
|
138
|
+
def generate_rest_request(method, headers) # :nodoc:
|
139
|
+
# calculate request data
|
140
|
+
server, path, path_to_sign = fetch_request_params(headers)
|
133
141
|
data = headers[:data]
|
134
142
|
# remove unset(==optional) and symbolyc keys
|
135
143
|
headers.each{ |key, value| headers.delete(key) if (value.nil? || key.is_a?(Symbol)) }
|
@@ -147,10 +155,12 @@ module RightAws
|
|
147
155
|
# set other headers
|
148
156
|
request['Authorization'] = "AWS #{@aws_access_key_id}:#{signature}"
|
149
157
|
# prepare output hash
|
158
|
+
|
150
159
|
{ :request => request,
|
151
160
|
:server => server,
|
152
161
|
:port => @params[:port],
|
153
|
-
:protocol => @params[:protocol]
|
162
|
+
:protocol => @params[:protocol],
|
163
|
+
:proxy => @params[:proxy] }
|
154
164
|
end
|
155
165
|
|
156
166
|
# Sends request to Amazon and parses the response.
|
@@ -189,8 +199,8 @@ module RightAws
|
|
189
199
|
unless headers[:location].blank?
|
190
200
|
data = "<CreateBucketConfiguration><LocationConstraint>#{headers[:location].to_s.upcase}</LocationConstraint></CreateBucketConfiguration>"
|
191
201
|
end
|
192
|
-
req_hash = generate_rest_request('PUT', headers.merge(:url=>bucket, :data => data
|
193
|
-
request_info(req_hash,
|
202
|
+
req_hash = generate_rest_request('PUT', headers.merge(:url=>bucket, :data => data))
|
203
|
+
request_info(req_hash, RightHttp2xxParser.new)
|
194
204
|
rescue Exception => e
|
195
205
|
# if the bucket exists AWS returns an error for the location constraint interface. Drop it
|
196
206
|
e.is_a?(RightAws::AwsError) && e.message.include?('BucketAlreadyOwnedByYou') ? true : on_exception
|
@@ -211,6 +221,37 @@ module RightAws
|
|
211
221
|
on_exception
|
212
222
|
end
|
213
223
|
|
224
|
+
# Retrieves the logging configuration for a bucket.
|
225
|
+
# Returns a hash of {:enabled, :targetbucket, :targetprefix}
|
226
|
+
#
|
227
|
+
# s3.interface.get_logging_parse(:bucket => "asset_bucket")
|
228
|
+
# => {:enabled=>true, :targetbucket=>"mylogbucket", :targetprefix=>"loggylogs/"}
|
229
|
+
#
|
230
|
+
#
|
231
|
+
def get_logging_parse(params)
|
232
|
+
AwsUtils.mandatory_arguments([:bucket], params)
|
233
|
+
AwsUtils.allow_only([:bucket, :headers], params)
|
234
|
+
params[:headers] = {} unless params[:headers]
|
235
|
+
req_hash = generate_rest_request('GET', params[:headers].merge(:url=>"#{params[:bucket]}?logging"))
|
236
|
+
request_info(req_hash, S3LoggingParser.new)
|
237
|
+
rescue
|
238
|
+
on_exception
|
239
|
+
end
|
240
|
+
|
241
|
+
# Sets logging configuration for a bucket from the XML configuration document.
|
242
|
+
# params:
|
243
|
+
# :bucket
|
244
|
+
# :xmldoc
|
245
|
+
def put_logging(params)
|
246
|
+
AwsUtils.mandatory_arguments([:bucket,:xmldoc], params)
|
247
|
+
AwsUtils.allow_only([:bucket,:xmldoc, :headers], params)
|
248
|
+
params[:headers] = {} unless params[:headers]
|
249
|
+
req_hash = generate_rest_request('PUT', params[:headers].merge(:url=>"#{params[:bucket]}?logging", :data => params[:xmldoc]))
|
250
|
+
request_info(req_hash, S3TrueParser.new)
|
251
|
+
rescue
|
252
|
+
on_exception
|
253
|
+
end
|
254
|
+
|
214
255
|
# Deletes new bucket. Bucket must be empty! Returns +true+ or an exception.
|
215
256
|
#
|
216
257
|
# s3.delete_bucket('my_awesome_bucket') #=> true
|
@@ -219,7 +260,7 @@ module RightAws
|
|
219
260
|
#
|
220
261
|
def delete_bucket(bucket, headers={})
|
221
262
|
req_hash = generate_rest_request('DELETE', headers.merge(:url=>bucket))
|
222
|
-
request_info(req_hash,
|
263
|
+
request_info(req_hash, RightHttp2xxParser.new)
|
223
264
|
rescue
|
224
265
|
on_exception
|
225
266
|
end
|
@@ -349,6 +390,7 @@ module RightAws
|
|
349
390
|
# a text mode IO object is passed to PUT, it will be converted to binary
|
350
391
|
# mode.
|
351
392
|
#
|
393
|
+
|
352
394
|
def put(bucket, key, data=nil, headers={})
|
353
395
|
# On Windows, if someone opens a file in text mode, we must reset it so
|
354
396
|
# to binary mode for streaming to work properly
|
@@ -360,11 +402,91 @@ module RightAws
|
|
360
402
|
headers['expect'] = '100-continue'
|
361
403
|
end
|
362
404
|
req_hash = generate_rest_request('PUT', headers.merge(:url=>"#{bucket}/#{CGI::escape key}", :data=>data))
|
363
|
-
request_info(req_hash,
|
405
|
+
request_info(req_hash, RightHttp2xxParser.new)
|
406
|
+
rescue
|
407
|
+
on_exception
|
408
|
+
end
|
409
|
+
|
410
|
+
|
411
|
+
|
412
|
+
# New experimental API for uploading objects, introduced in RightAws 1.8.1.
|
413
|
+
# store_object is similar in function to the older function put, but returns the full response metadata. It also allows for optional verification
|
414
|
+
# of object md5 checksums on upload. Parameters are passed as hash entries and are checked for completeness as well as for spurious arguments.
|
415
|
+
# The hash of the response headers contains useful information like the Amazon request ID and the object ETag (MD5 checksum).
|
416
|
+
#
|
417
|
+
# If the optional :md5 argument is provided, store_object verifies that the given md5 matches the md5 returned by S3. The :verified_md5 field in the response hash is
|
418
|
+
# set true or false depending on the outcome of this check. If no :md5 argument is given, :verified_md5 will be false in the response.
|
419
|
+
#
|
420
|
+
# The optional argument of :headers allows the caller to specify arbitrary request header values.
|
421
|
+
#
|
422
|
+
# s3.store_object(:bucket => "foobucket", :key => "foo", :md5 => "a507841b1bc8115094b00bbe8c1b2954", :data => "polemonium" )
|
423
|
+
# => {"x-amz-id-2"=>"SVsnS2nfDaR+ixyJUlRKM8GndRyEMS16+oZRieamuL61pPxPaTuWrWtlYaEhYrI/",
|
424
|
+
# "etag"=>"\"a507841b1bc8115094b00bbe8c1b2954\"",
|
425
|
+
# "date"=>"Mon, 29 Sep 2008 18:57:46 GMT",
|
426
|
+
# :verified_md5=>true,
|
427
|
+
# "x-amz-request-id"=>"63916465939995BA",
|
428
|
+
# "server"=>"AmazonS3",
|
429
|
+
# "content-length"=>"0"}
|
430
|
+
#
|
431
|
+
# s3.store_object(:bucket => "foobucket", :key => "foo", :data => "polemonium" )
|
432
|
+
# => {"x-amz-id-2"=>"MAt9PLjgLX9UYJ5tV2fI/5dBZdpFjlzRVpWgBDpvZpl+V+gJFcBMW2L+LBstYpbR",
|
433
|
+
# "etag"=>"\"a507841b1bc8115094b00bbe8c1b2954\"",
|
434
|
+
# "date"=>"Mon, 29 Sep 2008 18:58:56 GMT",
|
435
|
+
# :verified_md5=>false,
|
436
|
+
# "x-amz-request-id"=>"3B25A996BC2CDD3B",
|
437
|
+
# "server"=>"AmazonS3",
|
438
|
+
# "content-length"=>"0"}
|
439
|
+
|
440
|
+
def store_object(params)
|
441
|
+
AwsUtils.allow_only([:bucket, :key, :data, :headers, :md5], params)
|
442
|
+
AwsUtils.mandatory_arguments([:bucket, :key, :data], params)
|
443
|
+
params[:headers] = {} unless params[:headers]
|
444
|
+
|
445
|
+
params[:data].binmode if(params[:data].respond_to?(:binmode)) # On Windows, if someone opens a file in text mode, we must reset it to binary mode for streaming to work properly
|
446
|
+
if (params[:data].respond_to?(:lstat) && params[:data].lstat.size >= USE_100_CONTINUE_PUT_SIZE) ||
|
447
|
+
(params[:data].respond_to?(:size) && params[:data].size >= USE_100_CONTINUE_PUT_SIZE)
|
448
|
+
params[:headers]['expect'] = '100-continue'
|
449
|
+
end
|
450
|
+
|
451
|
+
req_hash = generate_rest_request('PUT', params[:headers].merge(:url=>"#{params[:bucket]}/#{CGI::escape params[:key]}", :data=>params[:data]))
|
452
|
+
resp = request_info(req_hash, S3HttpResponseHeadParser.new)
|
453
|
+
if(params[:md5])
|
454
|
+
resp[:verified_md5] = (resp['etag'].gsub(/\"/, '') == params[:md5]) ? true : false
|
455
|
+
else
|
456
|
+
resp[:verified_md5] = false
|
457
|
+
end
|
458
|
+
resp
|
364
459
|
rescue
|
365
460
|
on_exception
|
366
461
|
end
|
367
462
|
|
463
|
+
# Identical in function to store_object, but requires verification that the returned ETag is identical to the checksum passed in by the user as the 'md5' argument.
|
464
|
+
# If the check passes, returns the response metadata with the "verified_md5" field set true. Raises an exception if the checksums conflict.
|
465
|
+
# This call is implemented as a wrapper around store_object and the user may gain different semantics by creating a custom wrapper.
|
466
|
+
#
|
467
|
+
# s3.store_object_and_verify(:bucket => "foobucket", :key => "foo", :md5 => "a507841b1bc8115094b00bbe8c1b2954", :data => "polemonium" )
|
468
|
+
# => {"x-amz-id-2"=>"IZN3XsH4FlBU0+XYkFTfHwaiF1tNzrm6dIW2EM/cthKvl71nldfVC0oVQyydzWpb",
|
469
|
+
# "etag"=>"\"a507841b1bc8115094b00bbe8c1b2954\"",
|
470
|
+
# "date"=>"Mon, 29 Sep 2008 18:38:32 GMT",
|
471
|
+
# :verified_md5=>true,
|
472
|
+
# "x-amz-request-id"=>"E8D7EA4FE00F5DF7",
|
473
|
+
# "server"=>"AmazonS3",
|
474
|
+
# "content-length"=>"0"}
|
475
|
+
#
|
476
|
+
# s3.store_object_and_verify(:bucket => "foobucket", :key => "foo", :md5 => "a507841b1bc8115094b00bbe8c1b2953", :data => "polemonium" )
|
477
|
+
# RightAws::AwsError: Uploaded object failed MD5 checksum verification: {"x-amz-id-2"=>"HTxVtd2bf7UHHDn+WzEH43MkEjFZ26xuYvUzbstkV6nrWvECRWQWFSx91z/bl03n",
|
478
|
+
# "etag"=>"\"a507841b1bc8115094b00bbe8c1b2954\"",
|
479
|
+
# "date"=>"Mon, 29 Sep 2008 18:38:41 GMT",
|
480
|
+
# :verified_md5=>false,
|
481
|
+
# "x-amz-request-id"=>"0D7ADE09F42606F2",
|
482
|
+
# "server"=>"AmazonS3",
|
483
|
+
# "content-length"=>"0"}
|
484
|
+
def store_object_and_verify(params)
|
485
|
+
AwsUtils.mandatory_arguments([:md5], params)
|
486
|
+
r = store_object(params)
|
487
|
+
r[:verified_md5] ? (return r) : (raise AwsError.new("Uploaded object failed MD5 checksum verification: #{r.inspect}"))
|
488
|
+
end
|
489
|
+
|
368
490
|
# Retrieves object data from Amazon. Returns a +hash+ or an exception.
|
369
491
|
#
|
370
492
|
# s3.get('my_awesome_bucket', 'log/curent/1.log') #=>
|
@@ -398,6 +520,72 @@ module RightAws
|
|
398
520
|
rescue
|
399
521
|
on_exception
|
400
522
|
end
|
523
|
+
|
524
|
+
# New experimental API for retrieving objects, introduced in RightAws 1.8.1.
|
525
|
+
# retrieve_object is similar in function to the older function get. It allows for optional verification
|
526
|
+
# of object md5 checksums on retrieval. Parameters are passed as hash entries and are checked for completeness as well as for spurious arguments.
|
527
|
+
#
|
528
|
+
# If the optional :md5 argument is provided, retrieve_object verifies that the given md5 matches the md5 returned by S3. The :verified_md5 field in the response hash is
|
529
|
+
# set true or false depending on the outcome of this check. If no :md5 argument is given, :verified_md5 will be false in the response.
|
530
|
+
#
|
531
|
+
# The optional argument of :headers allows the caller to specify arbitrary request header values.
|
532
|
+
# Mandatory arguments:
|
533
|
+
# :bucket - the bucket in which the object is stored
|
534
|
+
# :key - the object address (or path) within the bucket
|
535
|
+
# Optional arguments:
|
536
|
+
# :headers - hash of additional HTTP headers to include with the request
|
537
|
+
# :md5 - MD5 checksum against which to verify the retrieved object
|
538
|
+
#
|
539
|
+
# s3.retrieve_object(:bucket => "foobucket", :key => "foo")
|
540
|
+
# => {:verified_md5=>false,
|
541
|
+
# :headers=>{"last-modified"=>"Mon, 29 Sep 2008 18:58:56 GMT",
|
542
|
+
# "x-amz-id-2"=>"2Aj3TDz6HP5109qly//18uHZ2a1TNHGLns9hyAtq2ved7wmzEXDOPGRHOYEa3Qnp",
|
543
|
+
# "content-type"=>"",
|
544
|
+
# "etag"=>"\"a507841b1bc8115094b00bbe8c1b2954\"",
|
545
|
+
# "date"=>"Tue, 30 Sep 2008 00:52:44 GMT",
|
546
|
+
# "x-amz-request-id"=>"EE4855DE27A2688C",
|
547
|
+
# "server"=>"AmazonS3",
|
548
|
+
# "content-length"=>"10"},
|
549
|
+
# :object=>"polemonium"}
|
550
|
+
#
|
551
|
+
# s3.retrieve_object(:bucket => "foobucket", :key => "foo", :md5=>'a507841b1bc8115094b00bbe8c1b2954')
|
552
|
+
# => {:verified_md5=>true,
|
553
|
+
# :headers=>{"last-modified"=>"Mon, 29 Sep 2008 18:58:56 GMT",
|
554
|
+
# "x-amz-id-2"=>"mLWQcI+VuKVIdpTaPXEo84g0cz+vzmRLbj79TS8eFPfw19cGFOPxuLy4uGYVCvdH",
|
555
|
+
# "content-type"=>"", "etag"=>"\"a507841b1bc8115094b00bbe8c1b2954\"",
|
556
|
+
# "date"=>"Tue, 30 Sep 2008 00:53:08 GMT",
|
557
|
+
# "x-amz-request-id"=>"6E7F317356580599",
|
558
|
+
# "server"=>"AmazonS3",
|
559
|
+
# "content-length"=>"10"},
|
560
|
+
# :object=>"polemonium"}
|
561
|
+
# If a block is provided, yields incrementally to the block as
|
562
|
+
# the response is read. For large responses, this function is ideal as
|
563
|
+
# the response can be 'streamed'. The hash containing header fields is
|
564
|
+
# still returned.
|
565
|
+
def retrieve_object(params, &block)
|
566
|
+
AwsUtils.mandatory_arguments([:bucket, :key], params)
|
567
|
+
AwsUtils.allow_only([:bucket, :key, :headers, :md5], params)
|
568
|
+
params[:headers] = {} unless params[:headers]
|
569
|
+
req_hash = generate_rest_request('GET', params[:headers].merge(:url=>"#{params[:bucket]}/#{CGI::escape params[:key]}"))
|
570
|
+
resp = request_info(req_hash, S3HttpResponseBodyParser.new, &block)
|
571
|
+
resp[:verified_md5] = false
|
572
|
+
if(params[:md5] && (resp[:headers]['etag'].gsub(/\"/,'') == params[:md5]))
|
573
|
+
resp[:verified_md5] = true
|
574
|
+
end
|
575
|
+
resp
|
576
|
+
rescue
|
577
|
+
on_exception
|
578
|
+
end
|
579
|
+
|
580
|
+
# Identical in function to retrieve_object, but requires verification that the returned ETag is identical to the checksum passed in by the user as the 'md5' argument.
|
581
|
+
# If the check passes, returns the response metadata with the "verified_md5" field set true. Raises an exception if the checksums conflict.
|
582
|
+
# This call is implemented as a wrapper around retrieve_object and the user may gain different semantics by creating a custom wrapper.
|
583
|
+
def retrieve_object_and_verify(params, &block)
|
584
|
+
AwsUtils.mandatory_arguments([:md5], params)
|
585
|
+
resp = retrieve_object(params, &block)
|
586
|
+
return resp if resp[:verified_md5]
|
587
|
+
raise AwsError.new("Retrieved object failed MD5 checksum verification: #{resp.inspect}")
|
588
|
+
end
|
401
589
|
|
402
590
|
# Retrieves object metadata. Returns a +hash+ of http_response_headers.
|
403
591
|
#
|
@@ -425,7 +613,7 @@ module RightAws
|
|
425
613
|
#
|
426
614
|
def delete(bucket, key='', headers={})
|
427
615
|
req_hash = generate_rest_request('DELETE', headers.merge(:url=>"#{bucket}/#{CGI::escape key}"))
|
428
|
-
request_info(req_hash,
|
616
|
+
request_info(req_hash, RightHttp2xxParser.new)
|
429
617
|
rescue
|
430
618
|
on_exception
|
431
619
|
end
|
@@ -446,7 +634,7 @@ module RightAws
|
|
446
634
|
def copy(src_bucket, src_key, dest_bucket, dest_key=nil, directive=:copy, headers={})
|
447
635
|
dest_key ||= src_key
|
448
636
|
headers['x-amz-metadata-directive'] = directive.to_s.upcase
|
449
|
-
headers['x-amz-copy-source'] = "#{src_bucket}/#{src_key}"
|
637
|
+
headers['x-amz-copy-source'] = "#{src_bucket}/#{CGI::escape src_key}"
|
450
638
|
req_hash = generate_rest_request('PUT', headers.merge(:url=>"#{dest_bucket}/#{CGI::escape dest_key}"))
|
451
639
|
request_info(req_hash, S3CopyParser.new)
|
452
640
|
rescue
|
@@ -629,26 +817,9 @@ module RightAws
|
|
629
817
|
|
630
818
|
# Generates link for QUERY API
|
631
819
|
def generate_link(method, headers={}, expires=nil) #:nodoc:
|
632
|
-
|
633
|
-
server =
|
634
|
-
|
635
|
-
path_to_sign = headers[:url]
|
636
|
-
path_to_sign = "/#{path_to_sign}" unless path_to_sign[/^\//]
|
637
|
-
# extract bucket name and check it's dns compartibility
|
638
|
-
path_to_sign[%r{^/([a-z0-9._-]*)(/[^?]*)?(\?.+)?}i]
|
639
|
-
bucket_name, key_path, params_list = $1, $2, $3
|
640
|
-
# select request model
|
641
|
-
if is_dns_bucket?(bucket_name)
|
642
|
-
# add backet to a server name
|
643
|
-
server = "#{bucket_name}.#{server}"
|
644
|
-
# remove bucket from the path
|
645
|
-
path = "#{key_path || '/'}#{params_list}"
|
646
|
-
# refactor the path (add '/' before params_list if the key is empty)
|
647
|
-
path_to_sign = "/#{bucket_name}#{path}"
|
648
|
-
else
|
649
|
-
path = path_to_sign
|
650
|
-
end
|
651
|
-
# expiration time
|
820
|
+
# calculate request data
|
821
|
+
server, path, path_to_sign = fetch_request_params(headers)
|
822
|
+
# expiration time
|
652
823
|
expires ||= DEFAULT_EXPIRES_AFTER
|
653
824
|
expires = Time.now.utc + expires if expires.is_a?(Fixnum) && (expires < ONE_YEAR_IN_SECONDS)
|
654
825
|
expires = expires.to_i
|
@@ -711,7 +882,7 @@ module RightAws
|
|
711
882
|
# s3.put_link('my_awesome_bucket',key, object) #=> url string
|
712
883
|
#
|
713
884
|
def put_link(bucket, key, data=nil, expires=nil, headers={})
|
714
|
-
generate_link('PUT', headers.merge(:url=>"#{bucket}/#{
|
885
|
+
generate_link('PUT', headers.merge(:url=>"#{bucket}/#{AwsUtils::URLencode key}", :data=>data), expires)
|
715
886
|
rescue
|
716
887
|
on_exception
|
717
888
|
end
|
@@ -729,7 +900,7 @@ module RightAws
|
|
729
900
|
#
|
730
901
|
# see http://docs.amazonwebservices.com/AmazonS3/2006-03-01/VirtualHosting.html
|
731
902
|
def get_link(bucket, key, expires=nil, headers={})
|
732
|
-
generate_link('GET', headers.merge(:url=>"#{bucket}/#{
|
903
|
+
generate_link('GET', headers.merge(:url=>"#{bucket}/#{AwsUtils::URLencode key}"), expires)
|
733
904
|
rescue
|
734
905
|
on_exception
|
735
906
|
end
|
@@ -739,7 +910,7 @@ module RightAws
|
|
739
910
|
# s3.head_link('my_awesome_bucket',key) #=> url string
|
740
911
|
#
|
741
912
|
def head_link(bucket, key, expires=nil, headers={})
|
742
|
-
generate_link('HEAD', headers.merge(:url=>"#{bucket}/#{
|
913
|
+
generate_link('HEAD', headers.merge(:url=>"#{bucket}/#{AwsUtils::URLencode key}"), expires)
|
743
914
|
rescue
|
744
915
|
on_exception
|
745
916
|
end
|
@@ -749,7 +920,7 @@ module RightAws
|
|
749
920
|
# s3.delete_link('my_awesome_bucket',key) #=> url string
|
750
921
|
#
|
751
922
|
def delete_link(bucket, key, expires=nil, headers={})
|
752
|
-
generate_link('DELETE', headers.merge(:url=>"#{bucket}/#{
|
923
|
+
generate_link('DELETE', headers.merge(:url=>"#{bucket}/#{AwsUtils::URLencode key}"), expires)
|
753
924
|
rescue
|
754
925
|
on_exception
|
755
926
|
end
|
@@ -760,7 +931,7 @@ module RightAws
|
|
760
931
|
# s3.get_acl_link('my_awesome_bucket',key) #=> url string
|
761
932
|
#
|
762
933
|
def get_acl_link(bucket, key='', headers={})
|
763
|
-
return generate_link('GET', headers.merge(:url=>"#{bucket}/#{
|
934
|
+
return generate_link('GET', headers.merge(:url=>"#{bucket}/#{AwsUtils::URLencode key}?acl"))
|
764
935
|
rescue
|
765
936
|
on_exception
|
766
937
|
end
|
@@ -770,7 +941,7 @@ module RightAws
|
|
770
941
|
# s3.put_acl_link('my_awesome_bucket',key) #=> url string
|
771
942
|
#
|
772
943
|
def put_acl_link(bucket, key='', headers={})
|
773
|
-
return generate_link('PUT', headers.merge(:url=>"#{bucket}/#{
|
944
|
+
return generate_link('PUT', headers.merge(:url=>"#{bucket}/#{AwsUtils::URLencode key}?acl"))
|
774
945
|
rescue
|
775
946
|
on_exception
|
776
947
|
end
|
@@ -932,6 +1103,28 @@ module RightAws
|
|
932
1103
|
end
|
933
1104
|
end
|
934
1105
|
end
|
1106
|
+
|
1107
|
+
class S3LoggingParser < RightAWSParser # :nodoc:
|
1108
|
+
def reset
|
1109
|
+
@result = {:enabled => false, :targetbucket => '', :targetprefix => ''}
|
1110
|
+
@current_grantee = {}
|
1111
|
+
end
|
1112
|
+
def tagend(name)
|
1113
|
+
case name
|
1114
|
+
# service info
|
1115
|
+
when 'TargetBucket'
|
1116
|
+
if @xmlpath == 'BucketLoggingStatus/LoggingEnabled'
|
1117
|
+
@result[:targetbucket] = @text
|
1118
|
+
@result[:enabled] = true
|
1119
|
+
end
|
1120
|
+
when 'TargetPrefix'
|
1121
|
+
if @xmlpath == 'BucketLoggingStatus/LoggingEnabled'
|
1122
|
+
@result[:targetprefix] = @text
|
1123
|
+
@result[:enabled] = true
|
1124
|
+
end
|
1125
|
+
end
|
1126
|
+
end
|
1127
|
+
end
|
935
1128
|
|
936
1129
|
class S3CopyParser < RightAWSParser # :nodoc:
|
937
1130
|
def reset
|
@@ -964,12 +1157,6 @@ module RightAws
|
|
964
1157
|
end
|
965
1158
|
end
|
966
1159
|
|
967
|
-
class S3TrueParser < S3HttpResponseParser # :nodoc:
|
968
|
-
def parse(response)
|
969
|
-
@result = response.is_a?(Net::HTTPSuccess)
|
970
|
-
end
|
971
|
-
end
|
972
|
-
|
973
1160
|
class S3HttpResponseBodyParser < S3HttpResponseParser # :nodoc:
|
974
1161
|
def parse(response)
|
975
1162
|
@result = {
|