cloudfiles 1.4.18 → 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/CHANGELOG +6 -0
- data/CONTRIBUTORS +1 -0
- data/cloudfiles.gemspec +5 -3
- data/lib/client.rb +620 -0
- data/lib/cloudfiles.rb +1 -1
- data/lib/cloudfiles/authentication.rb +10 -25
- data/lib/cloudfiles/connection.rb +55 -123
- data/lib/cloudfiles/container.rb +89 -73
- data/lib/cloudfiles/storage_object.rb +52 -36
- data/lib/cloudfiles/version.rb +1 -1
- data/test/cloudfiles_authentication_test.rb +17 -22
- data/test/cloudfiles_client_test.rb +797 -0
- data/test/cloudfiles_connection_test.rb +53 -194
- data/test/cloudfiles_container_test.rb +253 -121
- data/test/cloudfiles_storage_object_test.rb +51 -65
- data/test/test_helper.rb +1 -0
- metadata +12 -10
data/lib/cloudfiles.rb
CHANGED
@@ -30,7 +30,6 @@ module CloudFiles
|
|
30
30
|
require 'digest/md5'
|
31
31
|
require 'time'
|
32
32
|
require 'rubygems'
|
33
|
-
require 'mime/types'
|
34
33
|
|
35
34
|
unless "".respond_to? :each_char
|
36
35
|
require "jcode"
|
@@ -38,6 +37,7 @@ module CloudFiles
|
|
38
37
|
end
|
39
38
|
|
40
39
|
$:.unshift(File.dirname(__FILE__))
|
40
|
+
require 'client'
|
41
41
|
require 'cloudfiles/version'
|
42
42
|
require 'cloudfiles/exception'
|
43
43
|
require 'cloudfiles/authentication'
|
@@ -8,54 +8,39 @@ module CloudFiles
|
|
8
8
|
# cdmmgmtpath, storagehost, storagepath, authtoken, and authok variables on the connection. If it fails, it raises
|
9
9
|
# an CloudFiles::Exception::Authentication exception.
|
10
10
|
#
|
11
|
-
# Should
|
11
|
+
# Should never be called directly.
|
12
12
|
def initialize(connection)
|
13
|
-
parsed_auth_url = URI.parse(connection.auth_url)
|
14
|
-
path = parsed_auth_url.path
|
15
|
-
hdrhash = { "X-Auth-User" => connection.authuser, "X-Auth-Key" => connection.authkey }
|
16
13
|
begin
|
17
|
-
|
18
|
-
|
19
|
-
if parsed_auth_url.scheme == "https"
|
20
|
-
server.use_ssl = true
|
21
|
-
server.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
22
|
-
end
|
23
|
-
server.start
|
24
|
-
rescue Exception => e
|
14
|
+
storage_url, auth_token, headers = SwiftClient.get_auth(connection.auth_url, connection.authuser, connection.authkey, connection.snet?)
|
15
|
+
rescue => e
|
25
16
|
# uncomment if you suspect a problem with this branch of code
|
26
|
-
#
|
27
|
-
raise CloudFiles::Exception::Connection, "Unable to connect to #{
|
17
|
+
# $stderr.puts "got error #{e.class}: #{e.message.inspect}\n" << e.traceback.map{|n| "\t#{n}"}.join("\n")
|
18
|
+
raise CloudFiles::Exception::Connection, "Unable to connect to #{connection.auth_url}", caller
|
28
19
|
end
|
29
|
-
|
30
|
-
|
31
|
-
if response["x-cdn-management-url"]
|
20
|
+
if auth_token
|
21
|
+
if headers["x-cdn-management-url"]
|
32
22
|
connection.cdn_available = true
|
33
|
-
parsed_cdn_url = URI.parse(
|
23
|
+
parsed_cdn_url = URI.parse(headers["x-cdn-management-url"])
|
34
24
|
connection.cdnmgmthost = parsed_cdn_url.host
|
35
25
|
connection.cdnmgmtpath = parsed_cdn_url.path
|
36
26
|
connection.cdnmgmtport = parsed_cdn_url.port
|
37
27
|
connection.cdnmgmtscheme = parsed_cdn_url.scheme
|
38
28
|
end
|
39
|
-
parsed_storage_url = URI.parse(
|
29
|
+
parsed_storage_url = URI.parse(headers["x-storage-url"])
|
40
30
|
connection.storagehost = set_snet(connection, parsed_storage_url.host)
|
41
31
|
connection.storagepath = parsed_storage_url.path
|
42
32
|
connection.storageport = parsed_storage_url.port
|
43
33
|
connection.storagescheme = parsed_storage_url.scheme
|
44
|
-
connection.authtoken =
|
34
|
+
connection.authtoken = headers["x-auth-token"]
|
45
35
|
connection.authok = true
|
46
36
|
else
|
47
37
|
connection.authtoken = false
|
48
38
|
raise CloudFiles::Exception::Authentication, "Authentication failed"
|
49
39
|
end
|
50
|
-
server.finish
|
51
40
|
end
|
52
41
|
|
53
42
|
private
|
54
43
|
|
55
|
-
def get_server(connection, parsed_auth_url)
|
56
|
-
Net::HTTP::Proxy(connection.proxy_host, connection.proxy_port).new(parsed_auth_url.host, parsed_auth_url.port)
|
57
|
-
end
|
58
|
-
|
59
44
|
def set_snet(connection, hostname)
|
60
45
|
if connection.snet?
|
61
46
|
"snet-#{hostname}"
|
@@ -132,12 +132,18 @@ module CloudFiles
|
|
132
132
|
# => {:count=>8, :bytes=>42438527}
|
133
133
|
# cf.bytes
|
134
134
|
# => 42438527
|
135
|
+
# Hostname of the storage server
|
136
|
+
|
135
137
|
def get_info
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
138
|
+
begin
|
139
|
+
raise CloudFiles::Exception::AuthenticationException, "Not authenticated" unless self.authok?
|
140
|
+
response = SwiftClient.head_account(storageurl, self.authtoken)
|
141
|
+
@bytes = response["x-account-bytes-used"].to_i
|
142
|
+
@count = response["x-account-container-count"].to_i
|
143
|
+
{:bytes => @bytes, :count => @count}
|
144
|
+
rescue ClientException => e
|
145
|
+
raise CloudFiles::Exception::InvalidResponse, "Unable to obtain account size" unless (e.status.to_s == "204")
|
146
|
+
end
|
141
147
|
end
|
142
148
|
|
143
149
|
# The total size in bytes under this connection
|
@@ -162,14 +168,13 @@ module CloudFiles
|
|
162
168
|
#
|
163
169
|
# cf.containers(2,'cftest')
|
164
170
|
# => ["test", "video"]
|
165
|
-
def containers(limit =
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
CloudFiles.lines(response.body)
|
171
|
+
def containers(limit = nil, marker = nil)
|
172
|
+
begin
|
173
|
+
response = SwiftClient.get_account(storageurl, self.authtoken, marker, limit)
|
174
|
+
response[1].collect{|c| c['name']}
|
175
|
+
rescue ClientException => e
|
176
|
+
raise CloudFiles::Exception::InvalidResponse, "Invalid response code #{e.status.to_s}" unless (e.status.to_s == "200")
|
177
|
+
end
|
173
178
|
end
|
174
179
|
alias :list_containers :containers
|
175
180
|
|
@@ -183,20 +188,13 @@ module CloudFiles
|
|
183
188
|
# cf.containers_detail
|
184
189
|
# => { "container1" => { :bytes => "36543", :count => "146" },
|
185
190
|
# "container2" => { :bytes => "105943", :count => "25" } }
|
186
|
-
def containers_detail(limit =
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
doc = REXML::Document.new(response.body)
|
194
|
-
detailhash = {}
|
195
|
-
doc.elements.each("account/container/") { |c|
|
196
|
-
detailhash[c.elements["name"].text] = { :bytes => c.elements["bytes"].text, :count => c.elements["count"].text }
|
197
|
-
}
|
198
|
-
doc = nil
|
199
|
-
return detailhash
|
191
|
+
def containers_detail(limit = nil, marker = nil)
|
192
|
+
begin
|
193
|
+
response = SwiftClient.get_account(storageurl, self.authtoken, marker, limit)
|
194
|
+
Hash[*response[1].collect{|c| [c['name'], {:bytes => c['bytes'], :count => c['count']}]}.flatten]
|
195
|
+
rescue ClientException => e
|
196
|
+
raise CloudFiles::Exception::InvalidResponse, "Invalid response code #{e.status.to_s}" unless (e.status.to_s == "200")
|
197
|
+
end
|
200
198
|
end
|
201
199
|
alias :list_containers_info :containers_detail
|
202
200
|
|
@@ -208,14 +206,18 @@ module CloudFiles
|
|
208
206
|
# cf.container_exists?('bad_container')
|
209
207
|
# => false
|
210
208
|
def container_exists?(containername)
|
211
|
-
|
212
|
-
|
209
|
+
begin
|
210
|
+
response = SwiftClient.head_container(storageurl, self.authtoken, containername)
|
211
|
+
true
|
212
|
+
rescue ClientException => e
|
213
|
+
false
|
214
|
+
end
|
213
215
|
end
|
214
216
|
|
215
217
|
# Creates a new container and returns the CloudFiles::Container object. Throws an InvalidResponseException if the
|
216
218
|
# request fails.
|
217
219
|
#
|
218
|
-
#
|
220
|
+
# "/" is not valid in a container name. The container name is limited to
|
219
221
|
# 256 characters or less.
|
220
222
|
#
|
221
223
|
# container = cf.create_container('new_container')
|
@@ -223,13 +225,16 @@ module CloudFiles
|
|
223
225
|
# => "new_container"
|
224
226
|
#
|
225
227
|
# container = cf.create_container('bad/name')
|
226
|
-
# => SyntaxException: Container name cannot contain
|
228
|
+
# => SyntaxException: Container name cannot contain '/'
|
227
229
|
def create_container(containername)
|
228
|
-
raise CloudFiles::Exception::Syntax, "Container name cannot contain
|
230
|
+
raise CloudFiles::Exception::Syntax, "Container name cannot contain '/'" if containername.match("/")
|
229
231
|
raise CloudFiles::Exception::Syntax, "Container name is limited to 256 characters" if containername.length > 256
|
230
|
-
|
231
|
-
|
232
|
-
|
232
|
+
begin
|
233
|
+
SwiftClient.put_container(storageurl, self.authtoken, containername)
|
234
|
+
CloudFiles::Container.new(self, containername)
|
235
|
+
rescue ClientException => e
|
236
|
+
raise CloudFiles::Exception::InvalidResponse, "Unable to create container #{containername}" unless (e.status.to_s == "201" || e.status.to_s == "202")
|
237
|
+
end
|
233
238
|
end
|
234
239
|
|
235
240
|
# Deletes a container from the account. Throws a NonEmptyContainerException if the container still contains
|
@@ -244,9 +249,12 @@ module CloudFiles
|
|
244
249
|
# cf.delete_container('nonexistent')
|
245
250
|
# => NoSuchContainerException: Container nonexistent does not exist
|
246
251
|
def delete_container(containername)
|
247
|
-
|
248
|
-
|
249
|
-
|
252
|
+
begin
|
253
|
+
SwiftClient.delete_container(storageurl, self.authtoken, containername)
|
254
|
+
rescue ClientException => e
|
255
|
+
raise CloudFiles::Exception::NonEmptyContainer, "Container #{containername} is not empty" if (e.status.to_s == "409")
|
256
|
+
raise CloudFiles::Exception::NoSuchContainer, "Container #{containername} does not exist" unless (e.status.to_s == "204")
|
257
|
+
end
|
250
258
|
true
|
251
259
|
end
|
252
260
|
|
@@ -260,95 +268,19 @@ module CloudFiles
|
|
260
268
|
# cf.public_containers
|
261
269
|
# => ["video", "webpics"]
|
262
270
|
def public_containers(enabled_only = false)
|
263
|
-
paramstr = enabled_only == true ? "enabled_only=true" : ""
|
264
|
-
response = cdn_request("GET", "?#{paramstr}")
|
265
|
-
return [] if (response.code == "204")
|
266
|
-
raise CloudFiles::Exception::InvalidResponse, "Invalid response code #{response.code}" unless (response.code == "200")
|
267
|
-
CloudFiles.lines(response.body)
|
268
|
-
end
|
269
|
-
|
270
|
-
def storage_request(method, path = "", headers = {}, data = nil, attempts = 0, &block)
|
271
|
-
path = "#{@storagepath}/#{path}" unless (path[0,1] == '/')
|
272
|
-
cfreq(method, @storagehost, path, @storageport, @storagescheme, headers, data, attempts, &block)
|
273
|
-
end
|
274
|
-
|
275
|
-
def cdn_request(method, path = "", headers = {}, data = nil, attempts = 0, &block)
|
276
|
-
path = "#{@cdnmgmtpath}/#{path}" unless (path[0,1] == '/')
|
277
|
-
cfreq(method, @cdnmgmthost, path, @cdnmgmtport, @cdnmgmtscheme, headers, data, attempts, &block)
|
278
|
-
end
|
279
|
-
|
280
|
-
# This method actually makes the HTTP calls out to the server
|
281
|
-
def cfreq(method, server, path, port, scheme, headers = {}, data = nil, attempts = 0, &block) # :nodoc:
|
282
|
-
start = Time.now
|
283
|
-
headers['Transfer-Encoding'] = "chunked" if data.respond_to?(:read)
|
284
|
-
hdrhash = headerprep(headers)
|
285
|
-
start_http(server, path, port, scheme, hdrhash)
|
286
|
-
request = Net::HTTP.const_get(method.to_s.capitalize).new(path, hdrhash)
|
287
|
-
data.rewind if data.respond_to?(:rewind)
|
288
|
-
if data
|
289
|
-
if data.respond_to?(:read)
|
290
|
-
request.body_stream = data
|
291
|
-
else
|
292
|
-
request.body = data
|
293
|
-
end
|
294
|
-
unless data.is_a?(IO)
|
295
|
-
request.content_length = data.respond_to?(:lstat) ? data.stat.size : data.size
|
296
|
-
end
|
297
|
-
else
|
298
|
-
request.content_length = 0
|
299
|
-
end
|
300
|
-
response = @http[server].request(request, &block)
|
301
|
-
raise CloudFiles::Exception::ExpiredAuthToken if response.code == "401"
|
302
|
-
response
|
303
|
-
rescue Errno::EPIPE, Timeout::Error, Errno::EINVAL, EOFError, IOError
|
304
|
-
# Server closed the connection, retry
|
305
|
-
if attempts >= 5
|
306
|
-
host_str = server.respond_to?(:address) ? server.address : server
|
307
|
-
raise CloudFiles::Exception::Connection, "Unable to reconnect to #{host_str} after #{attempts} attempts", caller
|
308
|
-
end
|
309
|
-
|
310
|
-
attempts += 1
|
311
271
|
begin
|
312
|
-
|
313
|
-
|
314
|
-
|
272
|
+
response = SwiftClient.get_account(cdnurl, self.authtoken)
|
273
|
+
response[1].collect{|c| c['name']}
|
274
|
+
rescue ClientException => e
|
275
|
+
raise CloudFiles::Exception::InvalidResponse, "Invalid response code #{e.status.to_s}" unless (e.status.to_s == "200")
|
315
276
|
end
|
316
|
-
start_http(server, path, port, scheme, headers)
|
317
|
-
retry
|
318
|
-
rescue ExpiredAuthTokenException
|
319
|
-
raise CloudFiles::Exception::Connection, "Authentication token expired and you have requested not to retry", caller if @retry_auth == false
|
320
|
-
CloudFiles::Authentication.new(self)
|
321
|
-
retry
|
322
277
|
end
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
# Sets up standard HTTP headers
|
327
|
-
def headerprep(headers = {}) # :nodoc:
|
328
|
-
default_headers = {}
|
329
|
-
default_headers["X-Auth-Token"] = @authtoken if (authok? && @account.nil?)
|
330
|
-
default_headers["X-Storage-Token"] = @authtoken if (authok? && !@account.nil?)
|
331
|
-
default_headers["Connection"] = "Keep-Alive"
|
332
|
-
default_headers["User-Agent"] = "CloudFiles Ruby API #{VERSION}"
|
333
|
-
default_headers.merge(headers)
|
278
|
+
|
279
|
+
def storageurl
|
280
|
+
"#{self.storagescheme}://#{self.storagehost}:#{self.storageport.to_s}#{self.storagepath}"
|
334
281
|
end
|
335
|
-
|
336
|
-
|
337
|
-
def start_http(server, path, port, scheme, headers) # :nodoc:
|
338
|
-
if (@http[server].nil?)
|
339
|
-
begin
|
340
|
-
@http[server] = Net::HTTP::Proxy(self.proxy_host, self.proxy_port).new(server, port)
|
341
|
-
if scheme == "https"
|
342
|
-
@http[server].use_ssl = true
|
343
|
-
@http[server].verify_mode = OpenSSL::SSL::VERIFY_NONE
|
344
|
-
end
|
345
|
-
@http[server].start
|
346
|
-
rescue Exception
|
347
|
-
raise CloudFiles::Exception::Connection, "Unable to connect to #{server.address}", caller
|
348
|
-
end
|
349
|
-
end
|
282
|
+
def cdnurl
|
283
|
+
"#{self.cdnmgmtscheme}://#{self.cdnmgmthost}:#{self.cdnmgmtport.to_s}#{self.cdnmgmtpath}"
|
350
284
|
end
|
351
|
-
|
352
285
|
end
|
353
|
-
|
354
286
|
end
|
data/lib/cloudfiles/container.rb
CHANGED
@@ -41,11 +41,14 @@ module CloudFiles
|
|
41
41
|
# Retrieves Metadata for the container
|
42
42
|
def container_metadata
|
43
43
|
@metadata ||= (
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
44
|
+
begin
|
45
|
+
response = SwiftClient.head_container(self.connection.storageurl, self.connection.authtoken, escaped_name)
|
46
|
+
resphash = {}
|
47
|
+
response.to_hash.select { |k,v| k.match(/^x-container-meta/) }.each { |x| resphash[x[0]] = x[1].to_s }
|
48
|
+
{:bytes => response["x-container-bytes-used"].to_i, :count => response["x-container-object-count"].to_i, :metadata => resphash, :container_read => response["x-container-read"], :container_write => response["x-container-write"]}
|
49
|
+
rescue ClientException => e
|
50
|
+
raise CloudFiles::Exception::NoSuchContainer, "Container #{@name} does not exist" unless (e.status.to_s =~ /^20/)
|
51
|
+
end
|
49
52
|
)
|
50
53
|
end
|
51
54
|
|
@@ -54,18 +57,20 @@ module CloudFiles
|
|
54
57
|
return @cdn_metadata if @cdn_metadata
|
55
58
|
if cdn_available?
|
56
59
|
@cdn_metadata = (
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
60
|
+
begin
|
61
|
+
response = SwiftClient.head_container(self.connection.cdnurl, self.connection.authtoken, escaped_name)
|
62
|
+
cdn_enabled = ((response["x-cdn-enabled"] || "").downcase == "true") ? true : false
|
63
|
+
{
|
64
|
+
:cdn_enabled => cdn_enabled,
|
65
|
+
:cdn_ttl => cdn_enabled ? response["x-ttl"].to_i : nil,
|
66
|
+
:cdn_url => cdn_enabled ? response["x-cdn-uri"] : nil,
|
67
|
+
:cdn_ssl_url => cdn_enabled ? response["x-cdn-ssl-uri"] : nil,
|
68
|
+
:cdn_streaming_url => cdn_enabled ? response["x-cdn-streaming-uri"] : nil,
|
69
|
+
:cdn_log => (cdn_enabled and response["x-log-retention"] == "True") ? true : false
|
70
|
+
}
|
71
|
+
rescue ClientException => e
|
72
|
+
raise CloudFiles::Exception::NoSuchContainer, "Container #{@name} does not exist" unless (e.status.to_s =~ /^20/)
|
73
|
+
end
|
69
74
|
)
|
70
75
|
else
|
71
76
|
@cdn_metadata = {}
|
@@ -91,10 +96,14 @@ module CloudFiles
|
|
91
96
|
def set_metadata(metadatahash)
|
92
97
|
headers = {}
|
93
98
|
metadatahash.each{ |key, value| headers['X-Container-Meta-' + CloudFiles.escape(key.to_s.capitalize)] = value.to_s }
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
99
|
+
begin
|
100
|
+
SwiftClient.post_container(self.connection.storageurl, self.connection.authtoken, escaped_name, headers)
|
101
|
+
self.refresh
|
102
|
+
true
|
103
|
+
rescue ClientException => e
|
104
|
+
raise CloudFiles::Exception::NoSuchObject, "Container #{@name} does not exist" if (e.status.to_s == "404")
|
105
|
+
raise CloudFiles::Exception::InvalidResponse, "Invalid response code #{e.status}" unless (e.status.to_s =~ /^20/)
|
106
|
+
end
|
98
107
|
end
|
99
108
|
|
100
109
|
# Size of the container (in bytes)
|
@@ -136,21 +145,12 @@ module CloudFiles
|
|
136
145
|
def cdn_ssl_url
|
137
146
|
self.cdn_metadata[:cdn_ssl_url]
|
138
147
|
end
|
148
|
+
|
139
149
|
# CDN Streaming container URL (if container is public)
|
140
|
-
def
|
150
|
+
def cdn_streaming_url
|
141
151
|
self.cdn_metadata[:cdn_streaming_url]
|
142
152
|
end
|
143
153
|
|
144
|
-
# The container ACL on the User Agent
|
145
|
-
def user_agent_acl
|
146
|
-
self.cdn_metadata[:user_agent_acl]
|
147
|
-
end
|
148
|
-
|
149
|
-
# The container ACL on the site Referrer
|
150
|
-
def referrer_acl
|
151
|
-
self.cdn_metadata[:referrer_acl]
|
152
|
-
end
|
153
|
-
|
154
154
|
#used by openstack swift
|
155
155
|
def read_acl
|
156
156
|
self.container_metadata[:container_read]
|
@@ -168,15 +168,19 @@ module CloudFiles
|
|
168
168
|
alias :log_retention? :cdn_log
|
169
169
|
alias :cdn_log? :cdn_log
|
170
170
|
|
171
|
+
|
171
172
|
# Change the log retention status for this container. Values are true or false.
|
172
173
|
#
|
173
174
|
# These logs will be periodically (at unpredictable intervals) compressed and uploaded
|
174
175
|
# to a ".CDN_ACCESS_LOGS" container in the form of "container_name.YYYYMMDDHH-XXXX.gz".
|
175
176
|
def log_retention=(value)
|
176
177
|
raise Exception::CDNNotAvailable unless cdn_available?
|
177
|
-
|
178
|
-
|
179
|
-
|
178
|
+
begin
|
179
|
+
SwiftClient.post_container(self.connection.cdnurl, self.connection.authtoken, escaped_name, {"x-log-retention" => value.to_s.capitalize})
|
180
|
+
true
|
181
|
+
rescue ClientException => e
|
182
|
+
raise CloudFiles::Exception::InvalidResponse, "Invalid response code #{e.status}" unless (e.status.to_s == "201" or e.status.to_s == "202")
|
183
|
+
end
|
180
184
|
end
|
181
185
|
|
182
186
|
|
@@ -221,10 +225,12 @@ module CloudFiles
|
|
221
225
|
query << "#{param}=#{CloudFiles.escape(value.to_s)}"
|
222
226
|
end
|
223
227
|
end
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
+
begin
|
229
|
+
response = SwiftClient.get_container(self.connection.storageurl, self.connection.authtoken, escaped_name, params[:marker], params[:limit], params[:prefix], params[:delimiter])
|
230
|
+
return response[1].collect{|o| o['name']}
|
231
|
+
rescue ClientException => e
|
232
|
+
raise CloudFiles::Exception::InvalidResponse, "Invalid response code #{e.status}" unless (e.status.to_s == "200")
|
233
|
+
end
|
228
234
|
end
|
229
235
|
alias :list_objects :objects
|
230
236
|
|
@@ -254,16 +260,12 @@ module CloudFiles
|
|
254
260
|
query << "#{param}=#{CloudFiles.escape(value.to_s)}"
|
255
261
|
end
|
256
262
|
end
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
detailhash[o.elements["name"].text] = { :bytes => o.elements["bytes"].text, :hash => o.elements["hash"].text, :content_type => o.elements["content_type"].text, :last_modified => DateTime.parse(o.elements["last_modified"].text) }
|
264
|
-
}
|
265
|
-
doc = nil
|
266
|
-
return detailhash
|
263
|
+
begin
|
264
|
+
response = SwiftClient.get_container(self.connection.storageurl, self.connection.authtoken, escaped_name, params[:marker], params[:limit], params[:prefix], params[:delimiter])
|
265
|
+
return Hash[*response[1].collect{|o| [o['name'],{ :bytes => o["bytes"], :hash => o["hash"], :content_type => o["content_type"], :last_modified => DateTime.parse(o["last_modified"])}] }.flatten]
|
266
|
+
rescue ClientException => e
|
267
|
+
raise CloudFiles::Exception::InvalidResponse, "Invalid response code #{e.status}" unless (e.status.to_s == "200")
|
268
|
+
end
|
267
269
|
end
|
268
270
|
alias :list_objects_info :objects_detail
|
269
271
|
|
@@ -286,8 +288,12 @@ module CloudFiles
|
|
286
288
|
# container.object_exists?('badfile.txt')
|
287
289
|
# => false
|
288
290
|
def object_exists?(objectname)
|
289
|
-
|
290
|
-
|
291
|
+
begin
|
292
|
+
response = SwiftClient.head_object(self.connection.storageurl, self.connection.authtoken, escaped_name, objectname)
|
293
|
+
true
|
294
|
+
rescue ClientException => e
|
295
|
+
false
|
296
|
+
end
|
291
297
|
end
|
292
298
|
|
293
299
|
# Creates a new CloudFiles::StorageObject in the current container.
|
@@ -311,10 +317,13 @@ module CloudFiles
|
|
311
317
|
# container.delete_object('nonexistent_file.txt')
|
312
318
|
# => NoSuchObjectException: Object nonexistent_file.txt does not exist
|
313
319
|
def delete_object(objectname)
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
320
|
+
begin
|
321
|
+
SwiftClient.delete_object(self.connection.storageurl, self.connection.authtoken, escaped_name, objectname)
|
322
|
+
true
|
323
|
+
rescue ClientException => e
|
324
|
+
raise CloudFiles::Exception::NoSuchObject, "Object #{objectname} does not exist" if (e.status.to_s == "404")
|
325
|
+
raise CloudFiles::Exception::InvalidResponse, "Invalid response code #{e.status}" unless (e.status.to_s =~ /^20/)
|
326
|
+
end
|
318
327
|
end
|
319
328
|
|
320
329
|
# Makes a container publicly available via the Cloud Files CDN and returns true upon success. Throws NoSuchContainerException
|
@@ -338,14 +347,17 @@ module CloudFiles
|
|
338
347
|
options = {:ttl => ttl}
|
339
348
|
end
|
340
349
|
|
341
|
-
|
342
|
-
|
343
|
-
|
350
|
+
begin
|
351
|
+
SwiftClient.put_container(self.connection.cdnurl, self.connection.authtoken, escaped_name)
|
352
|
+
rescue ClientException => e
|
353
|
+
raise CloudFiles::Exception::NoSuchContainer, "Container #{@name} does not exist" unless (e.status.to_s == "201" || e.status.to_s == "202")
|
354
|
+
end
|
344
355
|
headers = { "X-TTL" => options[:ttl].to_s , "X-CDN-Enabled" => "True" }
|
345
356
|
headers["X-User-Agent-ACL"] = options[:user_agent_acl] if options[:user_agent_acl]
|
346
357
|
headers["X-Referrer-ACL"] = options[:referrer_acl] if options[:referrer_acl]
|
347
|
-
|
348
|
-
|
358
|
+
|
359
|
+
post_with_headers(headers)
|
360
|
+
# raise CloudFiles::Exception::NoSuchContainer, "Container #{@name} does not exist" unless (response.code == "201" || response.code == "202")
|
349
361
|
refresh
|
350
362
|
true
|
351
363
|
end
|
@@ -367,13 +379,11 @@ module CloudFiles
|
|
367
379
|
end
|
368
380
|
|
369
381
|
def post_with_headers(headers = {})
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
response
|
382
|
+
begin
|
383
|
+
SwiftClient.post_container(cdn_enabled? ? self.connection.cdnurl : self.connection.storageurl, self.connection.authtoken, escaped_name, headers)
|
384
|
+
rescue ClientException => e
|
385
|
+
raise CloudFiles::Exception::NoSuchContainer, "Container #{@name} does not exist (response code: #{e.status.to_s})" unless (e.status.to_s =~ /^20/)
|
374
386
|
end
|
375
|
-
raise CloudFiles::Exception::NoSuchContainer, "Container #{@name} does not exist (response code: #{response.code})" unless (response.code =~ /^20/)
|
376
|
-
response
|
377
387
|
end
|
378
388
|
|
379
389
|
# Makes a container private and returns true upon success. Throws NoSuchContainerException
|
@@ -386,10 +396,13 @@ module CloudFiles
|
|
386
396
|
def make_private
|
387
397
|
raise Exception::CDNNotAvailable unless cdn_available?
|
388
398
|
headers = { "X-CDN-Enabled" => "False" }
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
399
|
+
begin
|
400
|
+
SwiftClient.post_container(self.connection.cdnurl, self.connection.authtoken, escaped_name, headers)
|
401
|
+
refresh
|
402
|
+
true
|
403
|
+
rescue ClientException => e
|
404
|
+
raise CloudFiles::Exception::NoSuchContainer, "Container #{@name} does not exist" unless (e.status.to_s == "201" || e.status.to_s == "202")
|
405
|
+
end
|
393
406
|
end
|
394
407
|
|
395
408
|
# Purges CDN Edge Cache for all objects inside of this container
|
@@ -413,9 +426,12 @@ module CloudFiles
|
|
413
426
|
raise Exception::CDNNotAvailable unless cdn_available?
|
414
427
|
headers = {}
|
415
428
|
headers = {"X-Purge-Email" => email} if email
|
416
|
-
|
417
|
-
|
418
|
-
|
429
|
+
begin
|
430
|
+
SwiftClient.delete_container(self.connection.cdnurl, self.connection.authtoken, escaped_name, headers)
|
431
|
+
true
|
432
|
+
rescue ClientException => e
|
433
|
+
raise CloudFiles::Exception::Connection, "Error Unable to Purge Container: #{@name}" unless (e.status.to_s > "200" && e.status.to_s < "299")
|
434
|
+
end
|
419
435
|
end
|
420
436
|
|
421
437
|
def to_s # :nodoc:
|