right_aws 1.4.3 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +38 -0
- data/Manifest.txt +3 -0
- data/README.txt +23 -6
- data/Rakefile +8 -1
- data/lib/awsbase/right_awsbase.rb +113 -21
- data/lib/awsbase/support.rb +8 -0
- data/lib/ec2/right_ec2.rb +46 -76
- data/lib/right_aws.rb +4 -3
- data/lib/s3/right_s3.rb +85 -37
- data/lib/s3/right_s3_interface.rb +173 -56
- data/lib/sdb/right_sdb_interface.rb +470 -0
- data/test/s3/test_right_s3.rb +36 -13
- data/test/sdb/test_helper.rb +2 -0
- data/test/sdb/test_right_sdb.rb +128 -0
- data/test/ts_right_aws.rb +1 -1
- metadata +8 -5
data/lib/right_aws.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (c) 2007 RightScale Inc
|
2
|
+
# Copyright (c) 2007-2008 RightScale Inc
|
3
3
|
#
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
5
|
# a copy of this software and associated documentation files (the
|
@@ -43,13 +43,14 @@ require 's3/right_s3_interface'
|
|
43
43
|
require 's3/right_s3'
|
44
44
|
require 'sqs/right_sqs_interface'
|
45
45
|
require 'sqs/right_sqs'
|
46
|
+
require 'sdb/right_sdb_interface'
|
46
47
|
|
47
48
|
|
48
49
|
module RightAws #:nodoc:
|
49
50
|
module VERSION #:nodoc:
|
50
51
|
MAJOR = 1
|
51
|
-
MINOR =
|
52
|
-
TINY =
|
52
|
+
MINOR = 5
|
53
|
+
TINY = 0
|
53
54
|
|
54
55
|
STRING = [MAJOR, MINOR, TINY].join('.')
|
55
56
|
end
|
data/lib/s3/right_s3.rb
CHANGED
@@ -34,7 +34,19 @@ module RightAws
|
|
34
34
|
#
|
35
35
|
# Error handling: all operations raise an RightAws::AwsError in case
|
36
36
|
# of problems. Note that transient errors are automatically retried.
|
37
|
-
|
37
|
+
#
|
38
|
+
# It is a good way to use domain naming style getting a name for the buckets.
|
39
|
+
# See http://docs.amazonwebservices.com/AmazonS3/2006-03-01/UsingBucket.html
|
40
|
+
# about the naming convention for the buckets. This case they can be accessed using a virtual domains.
|
41
|
+
#
|
42
|
+
# Let assume you have 3 buckets: 'awesome-bucket', 'awesome_bucket' and 'AWEsomE-bucket'.
|
43
|
+
# The first ones objects can be accessed as: http:// awesome-bucket.s3.amazonaws.com/key/object
|
44
|
+
#
|
45
|
+
# But the rest have to be accessed as:
|
46
|
+
# http:// s3.amazonaws.com/awesome_bucket/key/object and http:// s3.amazonaws.com/AWEsomE-bucket/key/object
|
47
|
+
#
|
48
|
+
# See: http://docs.amazonwebservices.com/AmazonS3/2006-03-01/VirtualHosting.html for better explanation.
|
49
|
+
#
|
38
50
|
class S3
|
39
51
|
attr_reader :interface
|
40
52
|
|
@@ -60,22 +72,24 @@ module RightAws
|
|
60
72
|
|
61
73
|
# Retrieve an individual bucket.
|
62
74
|
# If the bucket does not exist and +create+ is set, a new bucket
|
63
|
-
# is created on S3.
|
64
|
-
# the bucket
|
75
|
+
# is created on S3. Launching this method with +create+=+true+ may
|
76
|
+
# affect on the bucket's ACL if the bucket already exists.
|
65
77
|
# Returns a RightAws::S3::Bucket instance or +nil+ if the bucket does not exist
|
66
78
|
# and +create+ is not set.
|
67
79
|
#
|
68
80
|
# s3 = RightAws::S3.new(aws_access_key_id, aws_secret_access_key)
|
69
|
-
# bucket1 = s3.bucket('
|
81
|
+
# bucket1 = s3.bucket('my_awesome_bucket_1')
|
70
82
|
# bucket1.keys #=> exception here if the bucket does not exists
|
71
83
|
# ...
|
72
|
-
# bucket2 = s3.bucket('
|
84
|
+
# bucket2 = s3.bucket('my_awesome_bucket_2', true)
|
73
85
|
# bucket2.keys #=> list of keys
|
74
|
-
#
|
86
|
+
# # create a bucket at the European location with public read access
|
87
|
+
# bucket3 = s3.bucket('my-awesome-bucket-3', true, 'public-read', :location => :eu)
|
88
|
+
#
|
75
89
|
# see http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html
|
76
90
|
# (section: Canned Access Policies)
|
77
91
|
#
|
78
|
-
def bucket(name, create=
|
92
|
+
def bucket(name, create=false, perms=nil, headers={})
|
79
93
|
headers['x-amz-acl'] = perms if perms
|
80
94
|
@interface.create_bucket(name, headers) if create
|
81
95
|
buckets.each { |bucket| return bucket if bucket.name == name }
|
@@ -86,28 +100,33 @@ module RightAws
|
|
86
100
|
class Bucket
|
87
101
|
attr_reader :s3, :name, :owner, :creation_date
|
88
102
|
|
89
|
-
|
103
|
+
# Create a Bucket instance.
|
90
104
|
# If the bucket does not exist and +create+ is set, a new bucket
|
91
|
-
# is created on S3.
|
92
|
-
# the bucket
|
105
|
+
# is created on S3. Launching this method with +create+=+true+ may
|
106
|
+
# affect on the bucket's ACL if the bucket already exists.
|
93
107
|
# Returns Bucket instance or +nil+ if the bucket does not exist
|
94
108
|
# and +create+ is not set.
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
109
|
+
#
|
110
|
+
# s3 = RightAws::S3.new(aws_access_key_id, aws_secret_access_key)
|
111
|
+
# ...
|
112
|
+
# bucket1 = RightAws::S3::Bucket.create(s3, 'my_awesome_bucket_1')
|
113
|
+
# bucket1.keys #=> exception here if the bucket does not exists
|
114
|
+
# ...
|
115
|
+
# bucket2 = RightAws::S3::Bucket.create(s3, 'my_awesome_bucket_2', true)
|
116
|
+
# bucket2.keys #=> list of keys
|
117
|
+
# # create a bucket at the European location with public read access
|
118
|
+
# bucket3 = RightAws::S3::Bucket.create(s3,'my-awesome-bucket-3', true, 'public-read', :location => :eu)
|
119
|
+
#
|
120
|
+
# see http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html
|
121
|
+
# (section: Canned Access Policies)
|
122
|
+
#
|
123
|
+
def self.create(s3, name, create=false, perms=nil, headers={})
|
105
124
|
s3.bucket(name, create, perms, headers)
|
106
125
|
end
|
107
126
|
|
108
127
|
|
109
128
|
# Create a bucket instance. In normal use this method should
|
110
|
-
|
129
|
+
# not be called directly.
|
111
130
|
# Use RightAws::S3::Bucket.create or RightAws::S3.bucket instead.
|
112
131
|
def initialize(s3, name, creation_date=nil, owner=nil)
|
113
132
|
@s3 = s3
|
@@ -137,6 +156,11 @@ module RightAws
|
|
137
156
|
params = @s3.interface.params
|
138
157
|
"#{params[:protocol]}://#{params[:server]}:#{params[:port]}/#{full_name}"
|
139
158
|
end
|
159
|
+
|
160
|
+
# Returns the bucket location
|
161
|
+
def location
|
162
|
+
@location ||= @s3.interface.bucket_location(@name)
|
163
|
+
end
|
140
164
|
|
141
165
|
# Retrieve a group of keys from Amazon.
|
142
166
|
# +options+ is a hash: { 'prefix'=>'', 'marker'=>'', 'max-keys'=>5, 'delimiter'=>'' }).
|
@@ -569,13 +593,15 @@ module RightAws
|
|
569
593
|
# Create a new Grantee instance.
|
570
594
|
# Grantee +id+ must exist on S3. If +action+ == :refresh, then retrieve
|
571
595
|
# permissions from S3 and update @perms. If +action+ == :apply, then apply
|
572
|
-
# perms to +thing+ at S3.
|
596
|
+
# perms to +thing+ at S3. If +action+ == :apply_and_refresh then it performs.
|
597
|
+
# both the actions. This is used for the new grantees that had no perms to
|
598
|
+
# this thing before. The default action is :refresh.
|
573
599
|
#
|
574
600
|
# bucket = s3.bucket('my_awesome_bucket', true, 'public-read')
|
575
601
|
# grantee1 = RightAws::S3::Grantee.new(bucket, 'a123b...223c', FULL_CONTROL)
|
576
602
|
# ...
|
577
603
|
# grantee2 = RightAws::S3::Grantee.new(bucket, 'abcde...asdf', [FULL_CONTROL, READ], :apply)
|
578
|
-
#
|
604
|
+
# grantee3 = RightAws::S3::Grantee.new(bucket, 'aaaaa...aaaa', 'READ', :apply_and_refresh)
|
579
605
|
#
|
580
606
|
def initialize(thing, id, perms=[], action=:refresh, name=nil)
|
581
607
|
@thing = thing
|
@@ -583,11 +609,20 @@ module RightAws
|
|
583
609
|
@name = name
|
584
610
|
@perms = perms.to_a
|
585
611
|
case action
|
586
|
-
when :apply
|
587
|
-
when :refresh
|
612
|
+
when :apply: apply
|
613
|
+
when :refresh: refresh
|
614
|
+
when :apply_and_refresh: apply; refresh
|
588
615
|
end
|
589
616
|
end
|
590
617
|
|
618
|
+
# Return +true+ if the grantee has any permissions to the thing.
|
619
|
+
def exists?
|
620
|
+
self.class.grantees(@thing).each do |grantee|
|
621
|
+
return true if @id == grantee.id
|
622
|
+
end
|
623
|
+
false
|
624
|
+
end
|
625
|
+
|
591
626
|
# Return Grantee type (+String+): "Group" or "CanonicalUser".
|
592
627
|
def type
|
593
628
|
@id[/^http:/] ? "Group" : "CanonicalUser"
|
@@ -603,11 +638,16 @@ module RightAws
|
|
603
638
|
# See http://docs.amazonwebservices.com/AmazonS3/2006-03-01/UsingPermissions.html .
|
604
639
|
# Returns +true+.
|
605
640
|
#
|
606
|
-
# grantee.grant('FULL_CONTROL')
|
607
|
-
#
|
608
|
-
|
609
|
-
|
610
|
-
|
641
|
+
# grantee.grant('FULL_CONTROL') #=> true
|
642
|
+
# grantee.grant('FULL_CONTROL','WRITE','READ') #=> true
|
643
|
+
# grantee.grant(['WRITE_ACP','READ','READ_ACP']) #=> true
|
644
|
+
#
|
645
|
+
def grant(*permissions)
|
646
|
+
permissions.flatten!
|
647
|
+
old_perms = @perms.dup
|
648
|
+
@perms += permissions
|
649
|
+
@perms.uniq!
|
650
|
+
return true if @perms == old_perms
|
611
651
|
apply
|
612
652
|
end
|
613
653
|
|
@@ -617,11 +657,16 @@ module RightAws
|
|
617
657
|
# Default value is 'FULL_CONTROL'.
|
618
658
|
# Returns +true+.
|
619
659
|
#
|
620
|
-
# grantee.revoke
|
660
|
+
# grantee.revoke('READ') #=> true
|
661
|
+
# grantee.revoke('FULL_CONTROL','WRITE') #=> true
|
662
|
+
# grantee.revoke(['READ_ACP','WRITE_ACP']) #=> true
|
621
663
|
#
|
622
|
-
def revoke(
|
623
|
-
|
624
|
-
@perms
|
664
|
+
def revoke(*permissions)
|
665
|
+
permissions.flatten!
|
666
|
+
old_perms = @perms.dup
|
667
|
+
@perms -= permissions
|
668
|
+
@perms.uniq!
|
669
|
+
return true if @perms == old_perms
|
625
670
|
apply
|
626
671
|
end
|
627
672
|
|
@@ -664,10 +709,13 @@ module RightAws
|
|
664
709
|
# grantee.apply #=> true
|
665
710
|
#
|
666
711
|
def apply
|
712
|
+
@perms.uniq!
|
667
713
|
owner, grantees = self.class.owner_and_grantees(@thing)
|
668
|
-
grantees
|
669
|
-
|
670
|
-
|
714
|
+
# walk through all the grantees and replace the data for the current one and ...
|
715
|
+
grantees.map! { |grantee| grantee.id == @id ? self : grantee }
|
716
|
+
# ... if this grantee is not known - add this bad boy to a list
|
717
|
+
grantees << self unless grantees.include?(self)
|
718
|
+
# set permissions
|
671
719
|
self.class.put_acl(@thing, owner, grantees)
|
672
720
|
end
|
673
721
|
|
@@ -24,6 +24,9 @@
|
|
24
24
|
module RightAws
|
25
25
|
|
26
26
|
class S3Interface < RightAwsBase
|
27
|
+
|
28
|
+
USE_100_CONTINUE_PUT_SIZE = 1_000_000
|
29
|
+
|
27
30
|
include RightAwsBaseInterface
|
28
31
|
|
29
32
|
DEFAULT_HOST = 's3.amazonaws.com'
|
@@ -88,15 +91,44 @@ module RightAws
|
|
88
91
|
# ignore everything after the question mark...
|
89
92
|
out_string << path.gsub(/\?.*$/, '')
|
90
93
|
# ...unless there is an acl or torrent parameter
|
91
|
-
out_string << '?acl'
|
92
|
-
out_string << '?torrent'if path[/[&?]torrent($|&|=)/]
|
94
|
+
out_string << '?acl' if path[/[&?]acl($|&|=)/]
|
95
|
+
out_string << '?torrent' if path[/[&?]torrent($|&|=)/]
|
96
|
+
out_string << '?location' if path[/[&?]location($|&|=)/]
|
97
|
+
# out_string << '?logging' if path[/[&?]logging($|&|=)/] # this one is beta, no support for now
|
93
98
|
out_string
|
94
99
|
end
|
95
100
|
|
101
|
+
def is_dns_bucket?(bucket_name)
|
102
|
+
bucket_name = bucket_name.to_s
|
103
|
+
return nil unless (3..63) === bucket_name.size
|
104
|
+
bucket_name.split('.').each do |component|
|
105
|
+
return nil unless component[/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/]
|
106
|
+
end
|
107
|
+
true
|
108
|
+
end
|
109
|
+
|
96
110
|
# Generates request hash for REST API.
|
111
|
+
# Assumes that headers[:url] is URL encoded (use CGI::escape)
|
97
112
|
def generate_rest_request(method, headers) # :nodoc:
|
98
|
-
|
99
|
-
|
113
|
+
# default server to use
|
114
|
+
server = @params[:server]
|
115
|
+
# fix path
|
116
|
+
path_to_sign = headers[:url]
|
117
|
+
path_to_sign = "/#{path_to_sign}" unless path_to_sign[/^\//]
|
118
|
+
# extract bucket name and check it's dns compartibility
|
119
|
+
path_to_sign[%r{^/([a-z0-9._-]*)(/[^?]*)?(\?.+)?}i]
|
120
|
+
bucket_name, key_path, params_list = $1, $2, $3
|
121
|
+
# select request model
|
122
|
+
if is_dns_bucket?(bucket_name)
|
123
|
+
# add backet to a server name
|
124
|
+
server = "#{bucket_name}.#{server}"
|
125
|
+
# remove bucket from the path
|
126
|
+
path = "#{key_path || '/'}#{params_list}"
|
127
|
+
# refactor the path (add '/' before params_list if the key is empty)
|
128
|
+
path_to_sign = "/#{bucket_name}#{path}"
|
129
|
+
else
|
130
|
+
path = path_to_sign
|
131
|
+
end
|
100
132
|
data = headers[:data]
|
101
133
|
# remove unset(==optional) and symbolyc keys
|
102
134
|
headers.each{ |key, value| headers.delete(key) if (value.nil? || key.is_a?(Symbol)) }
|
@@ -104,22 +136,22 @@ module RightAws
|
|
104
136
|
headers['content-type'] ||= ''
|
105
137
|
headers['date'] = Time.now.httpdate
|
106
138
|
# create request
|
107
|
-
request = "Net::HTTP::#{method.capitalize}".constantize.new(
|
139
|
+
request = "Net::HTTP::#{method.capitalize}".constantize.new(path)
|
108
140
|
request.body = data if data
|
109
141
|
# set request headers and meta headers
|
110
142
|
headers.each { |key, value| request[key.to_s] = value }
|
111
143
|
#generate auth strings
|
112
|
-
auth_string = canonical_string(request.method,
|
144
|
+
auth_string = canonical_string(request.method, path_to_sign, request.to_hash)
|
113
145
|
signature = AwsUtils::sign(@aws_secret_access_key, auth_string)
|
114
146
|
# set other headers
|
115
147
|
request['Authorization'] = "AWS #{@aws_access_key_id}:#{signature}"
|
116
148
|
# prepare output hash
|
117
149
|
{ :request => request,
|
118
|
-
:server =>
|
150
|
+
:server => server,
|
119
151
|
:port => @params[:port],
|
120
152
|
:protocol => @params[:protocol] }
|
121
|
-
|
122
|
-
|
153
|
+
end
|
154
|
+
|
123
155
|
# Sends request to Amazon and parses the response.
|
124
156
|
# Raises AwsError if any banana happened.
|
125
157
|
def request_info(request, parser, &block) # :nodoc:
|
@@ -146,11 +178,34 @@ module RightAws
|
|
146
178
|
|
147
179
|
# Creates new bucket. Returns +true+ or an exception.
|
148
180
|
#
|
149
|
-
#
|
181
|
+
# # create a bucket at American server
|
182
|
+
# s3.create_bucket('my-awesome-bucket-us') #=> true
|
183
|
+
# # create a bucket at European server
|
184
|
+
# s3.create_bucket('my-awesome-bucket-eu', :location => :eu) #=> true
|
150
185
|
#
|
151
186
|
def create_bucket(bucket, headers={})
|
152
|
-
|
187
|
+
data = nil
|
188
|
+
unless headers[:location].blank?
|
189
|
+
data = "<CreateBucketConfiguration><LocationConstraint>#{headers[:location].to_s.upcase}</LocationConstraint></CreateBucketConfiguration>"
|
190
|
+
end
|
191
|
+
req_hash = generate_rest_request('PUT', headers.merge(:url=>bucket, :data => data))
|
153
192
|
request_info(req_hash, S3TrueParser.new)
|
193
|
+
rescue Exception => e
|
194
|
+
# if the bucket exists AWS returns an error for the location constraint interface. Drop it
|
195
|
+
e.is_a?(RightAws::AwsError) && e.message.include?('BucketAlreadyOwnedByYou') ? true : on_exception
|
196
|
+
end
|
197
|
+
|
198
|
+
# Retrieve bucket location
|
199
|
+
#
|
200
|
+
# s3.create_bucket('my-awesome-bucket-us') #=> true
|
201
|
+
# puts s3.bucket_location('my-awesome-bucket-us') #=> '' (Amazon's default value assumed)
|
202
|
+
#
|
203
|
+
# s3.create_bucket('my-awesome-bucket-eu', :location => :eu) #=> true
|
204
|
+
# puts s3.bucket_location('my-awesome-bucket-eu') #=> 'EU'
|
205
|
+
#
|
206
|
+
def bucket_location(bucket, headers={})
|
207
|
+
req_hash = generate_rest_request('GET', headers.merge(:url=>"#{bucket}?location"))
|
208
|
+
request_info(req_hash, S3BucketLocationParser.new)
|
154
209
|
rescue
|
155
210
|
on_exception
|
156
211
|
end
|
@@ -220,7 +275,7 @@ module RightAws
|
|
220
275
|
# ]
|
221
276
|
# }
|
222
277
|
def incrementally_list_bucket(bucket, options={}, headers={}, &block)
|
223
|
-
internal_options = options.
|
278
|
+
internal_options = options.symbolize_keys
|
224
279
|
begin
|
225
280
|
internal_bucket = bucket.dup
|
226
281
|
internal_bucket += '?'+internal_options.map{|k, v| "#{k.to_s}=#{CGI::escape v.to_s}"}.join('&') unless internal_options.blank?
|
@@ -229,11 +284,11 @@ module RightAws
|
|
229
284
|
there_are_more_keys = response[:is_truncated]
|
230
285
|
if(there_are_more_keys)
|
231
286
|
if(response[:next_marker])
|
232
|
-
internal_options[
|
287
|
+
internal_options[:marker] = response[:next_marker]
|
233
288
|
else
|
234
|
-
internal_options[
|
289
|
+
internal_options[:marker] = response[:contents].last[:key]
|
235
290
|
end
|
236
|
-
internal_options['max-keys'] ? (internal_options['max-keys'] -= response[:contents].length) : nil
|
291
|
+
internal_options[:'max-keys'] ? (internal_options[:'max-keys'] -= response[:contents].length) : nil
|
237
292
|
end
|
238
293
|
yield response
|
239
294
|
end while there_are_more_keys
|
@@ -242,12 +297,49 @@ module RightAws
|
|
242
297
|
on_exception
|
243
298
|
end
|
244
299
|
|
245
|
-
# Saves object to Amazon. Returns +true+ or an exception.
|
246
|
-
# Any header starting with AMAZON_METADATA_PREFIX is considered
|
300
|
+
# Saves object to Amazon. Returns +true+ or an exception.
|
301
|
+
# Any header starting with AMAZON_METADATA_PREFIX is considered
|
302
|
+
# user metadata. It will be stored with the object and returned
|
303
|
+
# when you retrieve the object. The total size of the HTTP
|
304
|
+
# request, not including the body, must be less than 4 KB.
|
305
|
+
#
|
306
|
+
# s3.put('my_awesome_bucket', 'log/current/1.log', 'Ola-la!', 'x-amz-meta-family'=>'Woho556!') #=> true
|
307
|
+
#
|
308
|
+
# This method is capable of 'streaming' uploads; that is, it can upload
|
309
|
+
# data from a file or other IO object without first reading all the data
|
310
|
+
# into memory. This is most useful for large PUTs - it is difficult to read
|
311
|
+
# a 2 GB file entirely into memory before sending it to S3.
|
312
|
+
# To stream an upload, pass an object that responds to 'read' (like the read
|
313
|
+
# method of IO) and to either 'lstat' or 'size'. For files, this means
|
314
|
+
# streaming is enabled by simply making the call:
|
315
|
+
#
|
316
|
+
# s3.put(bucket_name, 'S3keyname.forthisfile', File.open('localfilename.dat'))
|
317
|
+
#
|
318
|
+
# If the IO object you wish to stream from responds to the read method but
|
319
|
+
# doesn't implement lstat or size, you can extend the object dynamically
|
320
|
+
# to implement these methods, or define your own class which defines these
|
321
|
+
# methods. Be sure that your class returns 'nil' from read() after having
|
322
|
+
# read 'size' bytes. Otherwise S3 will drop the socket after
|
323
|
+
# 'Content-Length' bytes have been uploaded, and HttpConnection will
|
324
|
+
# interpret this as an error.
|
247
325
|
#
|
248
|
-
#
|
326
|
+
# This method now supports very large PUTs, where very large
|
327
|
+
# is > 2 GB.
|
328
|
+
#
|
329
|
+
# For Win32 users: Files and IO objects should be opened in binary mode. If
|
330
|
+
# a text mode IO object is passed to PUT, it will be converted to binary
|
331
|
+
# mode.
|
249
332
|
#
|
250
333
|
def put(bucket, key, data=nil, headers={})
|
334
|
+
# On Windows, if someone opens a file in text mode, we must reset it so
|
335
|
+
# to binary mode for streaming to work properly
|
336
|
+
if(data.respond_to?(:binmode))
|
337
|
+
data.binmode
|
338
|
+
end
|
339
|
+
if (data.respond_to?(:lstat) && data.lstat.size >= USE_100_CONTINUE_PUT_SIZE) ||
|
340
|
+
(data.respond_to?(:size) && data.size >= USE_100_CONTINUE_PUT_SIZE)
|
341
|
+
headers['expect'] = '100-continue'
|
342
|
+
end
|
251
343
|
req_hash = generate_rest_request('PUT', headers.merge(:url=>"#{bucket}/#{CGI::escape key}", :data=>data))
|
252
344
|
request_info(req_hash, S3TrueParser.new)
|
253
345
|
rescue
|
@@ -470,8 +562,25 @@ module RightAws
|
|
470
562
|
|
471
563
|
# Generates link for QUERY API
|
472
564
|
def generate_link(method, headers={}, expires=nil) #:nodoc:
|
473
|
-
|
474
|
-
|
565
|
+
# default server to use
|
566
|
+
server = @params[:server]
|
567
|
+
# fix path
|
568
|
+
path_to_sign = headers[:url]
|
569
|
+
path_to_sign = "/#{path_to_sign}" unless path_to_sign[/^\//]
|
570
|
+
# extract bucket name and check it's dns compartibility
|
571
|
+
path_to_sign[%r{^/([a-z0-9._-]*)(/[^?]*)?(\?.+)?}i]
|
572
|
+
bucket_name, key_path, params_list = $1, $2, $3
|
573
|
+
# select request model
|
574
|
+
if is_dns_bucket?(bucket_name)
|
575
|
+
# add backet to a server name
|
576
|
+
server = "#{bucket_name}.#{server}"
|
577
|
+
# remove bucket from the path
|
578
|
+
path = "#{key_path || '/'}#{params_list}"
|
579
|
+
# refactor the path (add '/' before params_list if the key is empty)
|
580
|
+
path_to_sign = "/#{bucket_name}#{path}"
|
581
|
+
else
|
582
|
+
path = path_to_sign
|
583
|
+
end
|
475
584
|
# expiration time
|
476
585
|
expires ||= DEFAULT_EXPIRES_AFTER
|
477
586
|
expires = Time.now.utc.since(expires) if expires.is_a?(Fixnum) && (expires<1.year)
|
@@ -479,12 +588,12 @@ module RightAws
|
|
479
588
|
# remove unset(==optional) and symbolyc keys
|
480
589
|
headers.each{ |key, value| headers.delete(key) if (value.nil? || key.is_a?(Symbol)) }
|
481
590
|
#generate auth strings
|
482
|
-
auth_string = canonical_string(method,
|
591
|
+
auth_string = canonical_string(method, path_to_sign, headers, expires)
|
483
592
|
signature = CGI::escape(Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new("sha1"), @aws_secret_access_key, auth_string)).strip)
|
484
593
|
# path building
|
485
594
|
addon = "Signature=#{signature}&Expires=#{expires}&AWSAccessKeyId=#{@aws_access_key_id}"
|
486
595
|
path += path[/\?/] ? "&#{addon}" : "?#{addon}"
|
487
|
-
"#{@params[:protocol]}://#{
|
596
|
+
"#{@params[:protocol]}://#{server}:#{@params[:port]}#{path}"
|
488
597
|
rescue
|
489
598
|
on_exception
|
490
599
|
end
|
@@ -707,6 +816,48 @@ module RightAws
|
|
707
816
|
end
|
708
817
|
end
|
709
818
|
|
819
|
+
class S3BucketLocationParser < RightAWSParser # :nodoc:
|
820
|
+
def reset
|
821
|
+
@result = ''
|
822
|
+
end
|
823
|
+
def tagend(name)
|
824
|
+
@result = @text if name == 'LocationConstraint'
|
825
|
+
end
|
826
|
+
end
|
827
|
+
|
828
|
+
class S3AclParser < RightAWSParser # :nodoc:
|
829
|
+
def reset
|
830
|
+
@result = {:grantees=>[], :owner=>{}}
|
831
|
+
@current_grantee = {}
|
832
|
+
end
|
833
|
+
def tagstart(name, attributes)
|
834
|
+
@current_grantee = { :attributes => attributes } if name=='Grantee'
|
835
|
+
end
|
836
|
+
def tagend(name)
|
837
|
+
case name
|
838
|
+
# service info
|
839
|
+
when 'ID'
|
840
|
+
if @xmlpath == 'AccessControlPolicy/Owner'
|
841
|
+
@result[:owner][:id] = @text
|
842
|
+
else
|
843
|
+
@current_grantee[:id] = @text
|
844
|
+
end
|
845
|
+
when 'DisplayName'
|
846
|
+
if @xmlpath == 'AccessControlPolicy/Owner'
|
847
|
+
@result[:owner][:display_name] = @text
|
848
|
+
else
|
849
|
+
@current_grantee[:display_name] = @text
|
850
|
+
end
|
851
|
+
when 'URI'
|
852
|
+
@current_grantee[:uri] = @text
|
853
|
+
when 'Permission'
|
854
|
+
@current_grantee[:permissions] = @text
|
855
|
+
when 'Grant'
|
856
|
+
@result[:grantees] << @current_grantee
|
857
|
+
end
|
858
|
+
end
|
859
|
+
end
|
860
|
+
|
710
861
|
#-----------------------------------------------------------------
|
711
862
|
# PARSERS: Non XML
|
712
863
|
#-----------------------------------------------------------------
|
@@ -746,40 +897,6 @@ module RightAws
|
|
746
897
|
@result = headers_to_string(response.to_hash)
|
747
898
|
end
|
748
899
|
end
|
749
|
-
|
750
|
-
|
751
|
-
class S3AclParser < RightAWSParser # :nodoc:
|
752
|
-
def reset
|
753
|
-
@result = {:grantees=>[], :owner=>{}}
|
754
|
-
@current_grantee = {}
|
755
|
-
end
|
756
|
-
def tagstart(name, attributes)
|
757
|
-
@current_grantee = { :attributes => attributes } if name=='Grantee'
|
758
|
-
end
|
759
|
-
def tagend(name)
|
760
|
-
case name
|
761
|
-
# service info
|
762
|
-
when 'ID'
|
763
|
-
if @xmlpath == 'AccessControlPolicy/Owner'
|
764
|
-
@result[:owner][:id] = @text
|
765
|
-
else
|
766
|
-
@current_grantee[:id] = @text
|
767
|
-
end
|
768
|
-
when 'DisplayName'
|
769
|
-
if @xmlpath == 'AccessControlPolicy/Owner'
|
770
|
-
@result[:owner][:display_name] = @text
|
771
|
-
else
|
772
|
-
@current_grantee[:display_name] = @text
|
773
|
-
end
|
774
|
-
when 'URI'
|
775
|
-
@current_grantee[:uri] = @text
|
776
|
-
when 'Permission'
|
777
|
-
@current_grantee[:permissions] = @text
|
778
|
-
when 'Grant'
|
779
|
-
@result[:grantees] << @current_grantee
|
780
|
-
end
|
781
|
-
end
|
782
|
-
end
|
783
900
|
|
784
901
|
end
|
785
902
|
|