kerryb-right_aws 1.7.5 → 1.7.6
Sign up to get free protection for your applications and to get access to all the features.
- 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 = {
|