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 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
- ("/" + request['Host'].gsub("#{DEFAULT_HOST}","").gsub(/\.$/,'') + request.path[/^[^?]*/]).gsub("//","/")
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
- put("/#{name}", options).success?
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
- "/#{bucket_name(name)}#{RequestOptions.process(options).to_query_string}"
208
+ self.current_host = bucket_name(name)
209
+ "/#{RequestOptions.process(options).to_query_string}"
183
210
  end
184
211
  end
185
212
 
@@ -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
@@ -295,6 +295,7 @@ module AWS
295
295
  options.replace(bucket)
296
296
  bucket = nil
297
297
  end
298
+ self.current_host = bucket_name(bucket)
298
299
  "/#{name}"
299
300
  end
300
301
 
@@ -44,7 +44,7 @@ module AWS
44
44
  def error?
45
45
  !success? && response['content-type'] == 'application/xml' && parsed.root == 'error'
46
46
  end
47
-
47
+
48
48
  def error
49
49
  Error.new(parsed, self)
50
50
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: isaacfeliu-aws-s3
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marcel Molina Jr.