isaacfeliu-aws-s3 0.4.0 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README +4 -1
- data/lib/aws/s3/authentication.rb +9 -8
- data/lib/aws/s3/base.rb +34 -4
- data/lib/aws/s3/bucket.rb +29 -2
- data/lib/aws/s3/connection.rb +8 -7
- data/lib/aws/s3/object.rb +1 -0
- data/lib/aws/s3/response.rb +1 -1
- metadata +1 -1
data/README
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
This Fork is to provide support to European Buckets of Amazon S3, which changes concepts like the "Host" header (from s3.amazonaws.com to bucket_name.amazonaws.com)
|
2
|
-
With this fork, you can connect to US Buckets as well as EU Buckets.
|
2
|
+
With this fork, you can connect to/create US Buckets as well as EU Buckets.
|
3
3
|
|
4
4
|
= AWS::S3
|
5
5
|
|
@@ -66,6 +66,9 @@ Buckets are containers for objects (the files you store on S3). To create a new
|
|
66
66
|
# Pick a unique name, or else you'll get an error
|
67
67
|
# if the name is already taken.
|
68
68
|
Bucket.create('jukebox')
|
69
|
+
|
70
|
+
# To create an European bucket.
|
71
|
+
Bucket.create('jukebox', :location => :eu)
|
69
72
|
|
70
73
|
Bucket names must be unique across the entire S3 system, sort of like domain names across the internet. If you try
|
71
74
|
to create a bucket with a name that is already taken, you will get an error.
|
@@ -50,11 +50,11 @@ module AWS
|
|
50
50
|
# parameterize these computations and arrange them in a string form appropriate to how they are used, in one case a http request
|
51
51
|
# header value, and in the other case key/value query string parameter pairs.
|
52
52
|
class Signature < String #:nodoc:
|
53
|
-
attr_reader :request, :access_key_id, :secret_access_key
|
53
|
+
attr_reader :request, :access_key_id, :secret_access_key, :current_host
|
54
54
|
|
55
|
-
def initialize(request, access_key_id, secret_access_key, options = {})
|
55
|
+
def initialize(request, access_key_id, secret_access_key, current_host = nil, options = {})
|
56
56
|
super()
|
57
|
-
@request, @access_key_id, @secret_access_key = request, access_key_id, secret_access_key
|
57
|
+
@request, @access_key_id, @secret_access_key, @current_host = request, access_key_id, secret_access_key, current_host
|
58
58
|
@options = options
|
59
59
|
end
|
60
60
|
|
@@ -63,7 +63,7 @@ module AWS
|
|
63
63
|
def canonical_string
|
64
64
|
options = {}
|
65
65
|
options[:expires] = expires if expires?
|
66
|
-
CanonicalString.new(request, options)
|
66
|
+
CanonicalString.new(request, current_host, options)
|
67
67
|
end
|
68
68
|
memoized :canonical_string
|
69
69
|
|
@@ -135,7 +135,7 @@ module AWS
|
|
135
135
|
end
|
136
136
|
|
137
137
|
def interesting_headers
|
138
|
-
['content-md5', 'content-type', 'date', amazon_header_prefix]
|
138
|
+
['content-md5', 'content-type', 'date', 'Host', amazon_header_prefix]
|
139
139
|
end
|
140
140
|
|
141
141
|
def amazon_header_prefix
|
@@ -143,11 +143,12 @@ module AWS
|
|
143
143
|
end
|
144
144
|
end
|
145
145
|
|
146
|
-
attr_reader :request, :headers
|
146
|
+
attr_reader :request, :headers, :current_host
|
147
147
|
|
148
|
-
def initialize(request, options = {})
|
148
|
+
def initialize(request, current_host, options = {})
|
149
149
|
super()
|
150
150
|
@request = request
|
151
|
+
@current_host = current_host
|
151
152
|
@headers = {}
|
152
153
|
@options = options
|
153
154
|
# "For non-authenticated or anonymous requests. A NotImplemented error result code will be returned if
|
@@ -210,7 +211,7 @@ module AWS
|
|
210
211
|
end
|
211
212
|
|
212
213
|
def only_path
|
213
|
-
(
|
214
|
+
(current_host.nil? ? '' : "/#{current_host}") << request.path[/^[^?]*/]
|
214
215
|
end
|
215
216
|
end
|
216
217
|
end
|
data/lib/aws/s3/base.rb
CHANGED
@@ -55,7 +55,10 @@ module AWS #:nodoc:
|
|
55
55
|
# details can be found in the docs for Connection::Management::ClassMethods.
|
56
56
|
#
|
57
57
|
# Extensive examples can be found in the README[link:files/README.html].
|
58
|
-
class Base
|
58
|
+
class Base
|
59
|
+
|
60
|
+
cattr_accessor :current_host
|
61
|
+
|
59
62
|
class << self
|
60
63
|
# Wraps the current connection's request method and picks the appropriate response class to wrap the response in.
|
61
64
|
# If the response is an error, it will raise that error as an exception. All such exceptions can be caught by rescuing
|
@@ -66,16 +69,26 @@ module AWS #:nodoc:
|
|
66
69
|
def request(verb, path, options = {}, body = nil, attempts = 0, &block)
|
67
70
|
Service.response = nil
|
68
71
|
process_options!(options, verb)
|
69
|
-
response = response_class.new(connection.request(verb, path, options, body, attempts, &block))
|
72
|
+
response = response_class.new(connection.request(verb, path, options, body, attempts, current_host, &block))
|
70
73
|
Service.response = response
|
71
74
|
|
72
75
|
Error::Response.new(response.response).error.raise if response.error?
|
76
|
+
if attempts > 0 && !current_host.match(".#{DEFAULT_HOST}")
|
77
|
+
establish_connection!(:server => DEFAULT_HOST)
|
78
|
+
end
|
73
79
|
response
|
74
80
|
# Once in a while, a request to S3 returns an internal error. A glitch in the matrix I presume. Since these
|
75
81
|
# errors are few and far between the request method will rescue InternalErrors the first three times they encouter them
|
76
82
|
# and will retry the request again. Most of the time the second attempt will work.
|
77
83
|
rescue *retry_exceptions
|
78
84
|
attempts == 3 ? raise : (attempts += 1; retry)
|
85
|
+
rescue
|
86
|
+
raise unless response
|
87
|
+
if response.redirect?
|
88
|
+
new_host = response.parsed['endpoint']
|
89
|
+
establish_connection!(:server => new_host)
|
90
|
+
end
|
91
|
+
attempts == 3 || !response.redirect? ? raise : (attempts += 1; retry)
|
79
92
|
end
|
80
93
|
|
81
94
|
[:get, :post, :put, :delete, :head].each do |verb|
|
@@ -98,10 +111,19 @@ module AWS #:nodoc:
|
|
98
111
|
#
|
99
112
|
# Rather than infering the current bucket from the subdomain, the current class' bucket can be explicitly set with
|
100
113
|
# set_current_bucket_to.
|
114
|
+
|
115
|
+
def current_host
|
116
|
+
@@current_host
|
117
|
+
end
|
118
|
+
|
119
|
+
def current_host=(host)
|
120
|
+
@@current_host = host
|
121
|
+
end
|
122
|
+
|
101
123
|
def current_bucket
|
102
124
|
connection.subdomain or raise CurrentBucketNotSpecified.new(connection.http.address)
|
103
125
|
end
|
104
|
-
|
126
|
+
|
105
127
|
# If you plan on always using a specific bucket for certain files, you can skip always having to specify the bucket by creating
|
106
128
|
# a subclass of Bucket or S3Object and telling it what bucket to use:
|
107
129
|
#
|
@@ -208,10 +230,18 @@ module AWS #:nodoc:
|
|
208
230
|
def initialize(attributes = {}) #:nodoc:
|
209
231
|
@attributes = attributes
|
210
232
|
end
|
211
|
-
|
233
|
+
|
212
234
|
private
|
213
235
|
attr_reader :attributes
|
214
236
|
|
237
|
+
def current_host
|
238
|
+
self.class.current_host
|
239
|
+
end
|
240
|
+
|
241
|
+
def current_host=(host)
|
242
|
+
self.class.current_host = host
|
243
|
+
end
|
244
|
+
|
215
245
|
def connection
|
216
246
|
self.class.connection
|
217
247
|
end
|
data/lib/aws/s3/bucket.rb
CHANGED
@@ -72,13 +72,38 @@ module AWS
|
|
72
72
|
#
|
73
73
|
# Bucket.create('internet_drop_box', :access => :public_read_write)
|
74
74
|
#
|
75
|
+
# By default new buckets will be created in US. You can override this using the <tt>:location</tt> option.
|
76
|
+
#
|
77
|
+
# Bucket.create('internet_drop_box', :location => :eu)
|
78
|
+
#
|
75
79
|
# The full list of access levels that you can set on Bucket and S3Object creation are listed in the README[link:files/README.html]
|
76
80
|
# in the section called 'Setting access levels'.
|
77
81
|
def create(name, options = {})
|
78
82
|
validate_name!(name)
|
79
|
-
|
83
|
+
self.current_host = name
|
84
|
+
|
85
|
+
put("/", options, BucketConfiguration.new(options[:location]).to_s).success?
|
80
86
|
end
|
81
87
|
|
88
|
+
class BucketConfiguration < XmlGenerator
|
89
|
+
attr_reader :location
|
90
|
+
|
91
|
+
def initialize(location)
|
92
|
+
@location = location
|
93
|
+
super()
|
94
|
+
end
|
95
|
+
|
96
|
+
def build
|
97
|
+
return nil unless location == :eu
|
98
|
+
|
99
|
+
xml.tag!('CreateBucketConfiguration') do
|
100
|
+
xml.LocationConstraint 'EU'
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
|
82
107
|
# Fetches the bucket named <tt>name</tt>.
|
83
108
|
#
|
84
109
|
# Bucket.find('jukebox')
|
@@ -166,6 +191,7 @@ module AWS
|
|
166
191
|
|
167
192
|
# List all your buckets. This is a convenient wrapper around AWS::S3::Service.buckets.
|
168
193
|
def list(reload = false)
|
194
|
+
self.current_host = nil
|
169
195
|
Service.buckets(reload)
|
170
196
|
end
|
171
197
|
|
@@ -179,7 +205,8 @@ module AWS
|
|
179
205
|
options = name
|
180
206
|
name = nil
|
181
207
|
end
|
182
|
-
|
208
|
+
self.current_host = bucket_name(name)
|
209
|
+
"/#{RequestOptions.process(options).to_query_string}"
|
183
210
|
end
|
184
211
|
end
|
185
212
|
|
data/lib/aws/s3/connection.rb
CHANGED
@@ -23,7 +23,7 @@ module AWS
|
|
23
23
|
connect
|
24
24
|
end
|
25
25
|
|
26
|
-
def request(verb, path, headers = {}, body = nil, attempts = 0, &block)
|
26
|
+
def request(verb, path, headers = {}, body = nil, attempts = 0, current_host = nil, &block)
|
27
27
|
body.rewind if body.respond_to?(:rewind) unless attempts.zero?
|
28
28
|
|
29
29
|
requester = Proc.new do
|
@@ -31,8 +31,8 @@ module AWS
|
|
31
31
|
request = request_method(verb).new(path, headers)
|
32
32
|
ensure_content_type!(request)
|
33
33
|
add_user_agent!(request)
|
34
|
-
set_host!(request)
|
35
|
-
authenticate!(request)
|
34
|
+
set_host!(request, current_host)
|
35
|
+
authenticate!(request, current_host)
|
36
36
|
if body
|
37
37
|
if body.respond_to?(:read)
|
38
38
|
request.body_stream = body
|
@@ -41,6 +41,7 @@ module AWS
|
|
41
41
|
request.body = body
|
42
42
|
end
|
43
43
|
end
|
44
|
+
|
44
45
|
http.request(request, &block)
|
45
46
|
end
|
46
47
|
|
@@ -119,16 +120,16 @@ module AWS
|
|
119
120
|
end
|
120
121
|
|
121
122
|
# Just do Header authentication for now
|
122
|
-
def authenticate!(request)
|
123
|
-
request['Authorization'] = Authentication::Header.new(request, access_key_id, secret_access_key)
|
123
|
+
def authenticate!(request, current_host)
|
124
|
+
request['Authorization'] = Authentication::Header.new(request, access_key_id, secret_access_key, current_host)
|
124
125
|
end
|
125
126
|
|
126
127
|
def add_user_agent!(request)
|
127
128
|
request['User-Agent'] ||= "AWS::S3/#{Version}"
|
128
129
|
end
|
129
130
|
|
130
|
-
def set_host!(request)
|
131
|
-
request['Host'] = http.address
|
131
|
+
def set_host!(request, host)
|
132
|
+
request['Host'] = (host && http.address.match(host)) ? http.address : host.nil? ? http.address : host.match(/amazonaws.com/) ? host : "#{host}.#{http.address}"
|
132
133
|
end
|
133
134
|
|
134
135
|
def query_string_authentication(request, options = {})
|
data/lib/aws/s3/object.rb
CHANGED
data/lib/aws/s3/response.rb
CHANGED