right_aws 1.4.3 → 1.5.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.
- 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
|
|