cloudfiles 1.4.10 → 1.4.11
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +11 -0
- data/CONTRIBUTORS +28 -0
- data/COPYING +1 -1
- data/README.rdoc +9 -2
- data/VERSION +1 -1
- data/cloudfiles.gemspec +6 -5
- data/lib/cloudfiles.rb +20 -7
- data/lib/cloudfiles/authentication.rb +33 -28
- data/lib/cloudfiles/connection.rb +91 -85
- data/lib/cloudfiles/container.rb +149 -125
- data/lib/cloudfiles/exception.rb +64 -0
- data/lib/cloudfiles/storage_object.rb +140 -80
- data/test/cloudfiles_authentication_test.rb +7 -7
- data/test/cloudfiles_connection_test.rb +14 -13
- data/test/cloudfiles_container_test.rb +15 -14
- data/test/cloudfiles_storage_object_test.rb +10 -9
- metadata +9 -7
- data/Manifest +0 -16
data/CHANGELOG
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
================================================================================
|
2
|
+
1.4.1 (2011/02/04)
|
3
|
+
================================================================================
|
4
|
+
o Configurable :auth_url support for OpenStack Swift and non-US deployments. Add constants for Cloud Servers USA and UK. (Chmouel Boudjnah, Dan Prince)
|
5
|
+
o Moved exceptions under the CloudFiles::Exception scope
|
6
|
+
o Added support for configurable path delimiters (Corey Ward)
|
7
|
+
o Improvements in path escaping (Corey Ward)
|
8
|
+
o Support for the new COPY method on objects, via storage_object.copy and storage_object.move
|
9
|
+
o Reduced the number of API calls for loading metadata (Edmund Salvacion)
|
10
|
+
o Allow setting of content_type on more operations (suggestion by Bo Benson)
|
11
|
+
|
data/CONTRIBUTORS
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
The following people have contributed to this software, either with code, patches, bug reports, or suggestions.
|
2
|
+
|
3
|
+
Thank you for your help! If you have contributed and are not on the list, feel free to email minter@lunenburg.org
|
4
|
+
|
5
|
+
Major Hayden
|
6
|
+
H. Wade Minter
|
7
|
+
Niels Ganser
|
8
|
+
phillc
|
9
|
+
Todd Eichel
|
10
|
+
Cory Forsyth
|
11
|
+
Dan Prince
|
12
|
+
Carl Woodward
|
13
|
+
Conrad Weidenkeller
|
14
|
+
Chmouel Boudjnah
|
15
|
+
Corey Ward
|
16
|
+
Edmund Salvacion
|
17
|
+
mindgap
|
18
|
+
Bo Benson
|
19
|
+
Ryuujinx
|
20
|
+
Ivan Torres
|
21
|
+
creiht
|
22
|
+
Vladimir Zhukov
|
23
|
+
JCallicoat
|
24
|
+
Jeremy McNevin
|
25
|
+
Ryan Williams
|
26
|
+
drue
|
27
|
+
mkcode
|
28
|
+
megaphone
|
data/COPYING
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Unless otherwise noted, all files are released under the MIT license, exceptions contain licensing information in them.
|
2
2
|
|
3
|
-
Copyright (C)
|
3
|
+
Copyright (C) 2011 Rackspace US, Inc.
|
4
4
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
6
6
|
|
data/README.rdoc
CHANGED
@@ -37,6 +37,9 @@ See the class definitions for documentation on specific methods and operations.
|
|
37
37
|
# Log into the Cloud Files system
|
38
38
|
cf = CloudFiles::Connection.new(:username => "MY_USERNAME", :api_key => "MY_API_KEY")
|
39
39
|
|
40
|
+
# Or, if you want to access the United Kingdom cloud installations, there's a handy constant:
|
41
|
+
cf = CloudFiles::Connection.new(:username => "MY_USERNAME", :api_key => "MY_API_KEY", :auth_url => CloudFiles::AUTH_UK)
|
42
|
+
|
40
43
|
# Get a listing of all containers under this account
|
41
44
|
cf.containers
|
42
45
|
=> ["backup", "Books", "cftest", "test", "video", "webpics"]
|
@@ -47,6 +50,10 @@ See the class definitions for documentation on specific methods and operations.
|
|
47
50
|
# See how many objects are under this container
|
48
51
|
container.count
|
49
52
|
=> 3
|
53
|
+
|
54
|
+
# Upload a file
|
55
|
+
object = container.create_object 'filename.txt', false
|
56
|
+
object.write file
|
50
57
|
|
51
58
|
# List the objects
|
52
59
|
container.objects
|
@@ -63,9 +70,9 @@ See the class definitions for documentation on specific methods and operations.
|
|
63
70
|
|
64
71
|
Initial work by Major Hayden <major.hayden@rackspace.com>
|
65
72
|
|
66
|
-
Subsequent work by H. Wade Minter <minter@lunenburg.org>
|
73
|
+
Subsequent work by H. Wade Minter <minter@lunenburg.org> and Dan Prince <dan.prince@rackspace.com>
|
67
74
|
|
68
75
|
== License
|
69
76
|
|
70
77
|
See COPYING for license information.
|
71
|
-
Copyright (c)
|
78
|
+
Copyright (c) 2011, Rackspace US, Inc.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.4.
|
1
|
+
1.4.11
|
data/cloudfiles.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{cloudfiles}
|
8
|
-
s.version = "1.4.
|
8
|
+
s.version = "1.4.11"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["H. Wade Minter", "Rackspace Hosting"]
|
12
|
-
s.date = %q{
|
12
|
+
s.date = %q{2011-02-05}
|
13
13
|
s.description = %q{A Ruby version of the Rackspace Cloud Files API.}
|
14
14
|
s.email = %q{minter@lunenburg.org}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -18,8 +18,9 @@ Gem::Specification.new do |s|
|
|
18
18
|
]
|
19
19
|
s.files = [
|
20
20
|
".gitignore",
|
21
|
+
"CHANGELOG",
|
22
|
+
"CONTRIBUTORS",
|
21
23
|
"COPYING",
|
22
|
-
"Manifest",
|
23
24
|
"README.rdoc",
|
24
25
|
"Rakefile",
|
25
26
|
"TODO",
|
@@ -29,6 +30,7 @@ Gem::Specification.new do |s|
|
|
29
30
|
"lib/cloudfiles/authentication.rb",
|
30
31
|
"lib/cloudfiles/connection.rb",
|
31
32
|
"lib/cloudfiles/container.rb",
|
33
|
+
"lib/cloudfiles/exception.rb",
|
32
34
|
"lib/cloudfiles/storage_object.rb",
|
33
35
|
"test/cf-testunit.rb",
|
34
36
|
"test/cloudfiles_authentication_test.rb",
|
@@ -40,7 +42,7 @@ Gem::Specification.new do |s|
|
|
40
42
|
s.homepage = %q{http://www.rackspacecloud.com/cloud_hosting_products/files}
|
41
43
|
s.rdoc_options = ["--charset=UTF-8"]
|
42
44
|
s.require_paths = ["lib"]
|
43
|
-
s.rubygems_version = %q{1.
|
45
|
+
s.rubygems_version = %q{1.5.0}
|
44
46
|
s.summary = %q{A Ruby API into Rackspace Cloud Files}
|
45
47
|
s.test_files = [
|
46
48
|
"test/cf-testunit.rb",
|
@@ -52,7 +54,6 @@ Gem::Specification.new do |s|
|
|
52
54
|
]
|
53
55
|
|
54
56
|
if s.respond_to? :specification_version then
|
55
|
-
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
56
57
|
s.specification_version = 3
|
57
58
|
|
58
59
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
data/lib/cloudfiles.rb
CHANGED
@@ -1,27 +1,32 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
#
|
2
|
+
#
|
3
3
|
# == Cloud Files API
|
4
4
|
# ==== Connects Ruby Applications to Rackspace's {Cloud Files service}[http://www.rackspacecloud.com/cloud_hosting_products/files]
|
5
5
|
# Initial work by Major Hayden <major.hayden@rackspace.com>
|
6
|
-
#
|
7
|
-
# Subsequent work by H. Wade Minter <minter@lunenburg.org>
|
6
|
+
#
|
7
|
+
# Subsequent work by H. Wade Minter <minter@lunenburg.org> and Dan Prince <dan.prince@rackspace.com>
|
8
8
|
#
|
9
9
|
# See COPYING for license information.
|
10
|
-
# Copyright (c)
|
10
|
+
# Copyright (c) 2011, Rackspace US, Inc.
|
11
11
|
# ----
|
12
|
-
#
|
12
|
+
#
|
13
13
|
# === Documentation & Examples
|
14
|
-
# To begin reviewing the available methods and examples, peruse the README file, or begin by looking at documentation for the
|
14
|
+
# To begin reviewing the available methods and examples, peruse the README file, or begin by looking at documentation for the
|
15
15
|
# CloudFiles::Connection class.
|
16
16
|
#
|
17
17
|
# The CloudFiles class is the base class. Not much of note happens here.
|
18
|
-
# To create a new CloudFiles connection, use the CloudFiles::Connection.new('user_name', 'api_key') method.
|
18
|
+
# To create a new CloudFiles connection, use the CloudFiles::Connection.new(:username => 'user_name', :api_key => 'api_key') method.
|
19
|
+
|
19
20
|
module CloudFiles
|
20
21
|
|
22
|
+
AUTH_USA = "https://auth.api.rackspacecloud.com/v1.0"
|
23
|
+
AUTH_UK = "https://lon.auth.api.rackspacecloud.com/v1.0"
|
24
|
+
|
21
25
|
VERSION = IO.read(File.dirname(__FILE__) + '/../VERSION')
|
22
26
|
require 'net/http'
|
23
27
|
require 'net/https'
|
24
28
|
require 'rexml/document'
|
29
|
+
require 'cgi'
|
25
30
|
require 'uri'
|
26
31
|
require 'digest/md5'
|
27
32
|
require 'time'
|
@@ -34,6 +39,7 @@ module CloudFiles
|
|
34
39
|
end
|
35
40
|
|
36
41
|
$:.unshift(File.dirname(__FILE__))
|
42
|
+
require 'cloudfiles/exception'
|
37
43
|
require 'cloudfiles/authentication'
|
38
44
|
require 'cloudfiles/connection'
|
39
45
|
require 'cloudfiles/container'
|
@@ -42,6 +48,13 @@ module CloudFiles
|
|
42
48
|
def self.lines(str)
|
43
49
|
(str.respond_to?(:lines) ? str.lines : str).to_a.map { |x| x.chomp }
|
44
50
|
end
|
51
|
+
|
52
|
+
# CGI.escape, but without special treatment on spaces
|
53
|
+
def self.escape(str)
|
54
|
+
str.gsub(/([^a-zA-Z0-9_.-]+)/) do
|
55
|
+
'%' + $1.unpack('H2' * $1.bytesize).join('%').upcase
|
56
|
+
end
|
57
|
+
end
|
45
58
|
end
|
46
59
|
|
47
60
|
|
@@ -1,33 +1,38 @@
|
|
1
1
|
module CloudFiles
|
2
2
|
class Authentication
|
3
3
|
# See COPYING for license information.
|
4
|
-
# Copyright (c)
|
5
|
-
|
4
|
+
# Copyright (c) 2011, Rackspace US, Inc.
|
5
|
+
|
6
6
|
# Performs an authentication to the Cloud Files servers. Opens a new HTTP connection to the API server,
|
7
7
|
# sends the credentials, and looks for a successful authentication. If it succeeds, it sets the cdmmgmthost,
|
8
8
|
# cdmmgmtpath, storagehost, storagepath, authtoken, and authok variables on the connection. If it fails, it raises
|
9
|
-
# an
|
9
|
+
# an CloudFiles::Exception::Authentication exception.
|
10
10
|
#
|
11
11
|
# Should probably never be called directly.
|
12
12
|
def initialize(connection)
|
13
|
-
|
14
|
-
path =
|
13
|
+
parsed_auth_url = URI.parse(connection.auth_url)
|
14
|
+
path = parsed_auth_url.path
|
15
15
|
hdrhash = { "X-Auth-User" => connection.authuser, "X-Auth-Key" => connection.authkey }
|
16
16
|
begin
|
17
|
-
server = get_server(connection,
|
18
|
-
|
19
|
-
|
17
|
+
server = get_server(connection, parsed_auth_url)
|
18
|
+
|
19
|
+
if parsed_auth_url.scheme == "https"
|
20
|
+
server.use_ssl = true
|
21
|
+
server.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
22
|
+
end
|
20
23
|
server.start
|
21
24
|
rescue
|
22
|
-
raise
|
25
|
+
raise CloudFiles::Exception::Connection, "Unable to connect to #{server}"
|
23
26
|
end
|
24
|
-
response = server.get(path,hdrhash)
|
27
|
+
response = server.get(path, hdrhash)
|
25
28
|
if (response.code == "204")
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
29
|
+
if response["x-cdn-management-url"]
|
30
|
+
connection.cdnmgmthost = URI.parse(response["x-cdn-management-url"]).host
|
31
|
+
connection.cdnmgmtpath = URI.parse(response["x-cdn-management-url"]).path
|
32
|
+
connection.cdnmgmtport = URI.parse(response["x-cdn-management-url"]).port
|
33
|
+
connection.cdnmgmtscheme = URI.parse(response["x-cdn-management-url"]).scheme
|
34
|
+
end
|
35
|
+
connection.storagehost = set_snet(connection, URI.parse(response["x-storage-url"]).host)
|
31
36
|
connection.storagepath = URI.parse(response["x-storage-url"]).path
|
32
37
|
connection.storageport = URI.parse(response["x-storage-url"]).port
|
33
38
|
connection.storagescheme = URI.parse(response["x-storage-url"]).scheme
|
@@ -35,23 +40,23 @@ module CloudFiles
|
|
35
40
|
connection.authok = true
|
36
41
|
else
|
37
42
|
connection.authtoken = false
|
38
|
-
raise
|
43
|
+
raise CloudFiles::Exception::Authentication, "Authentication failed"
|
39
44
|
end
|
40
45
|
server.finish
|
41
46
|
end
|
42
|
-
|
47
|
+
|
43
48
|
private
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
49
|
+
|
50
|
+
def get_server(connection, parsed_auth_url)
|
51
|
+
Net::HTTP::Proxy(connection.proxy_host, connection.proxy_port).new(parsed_auth_url.host, parsed_auth_url.port)
|
52
|
+
end
|
53
|
+
|
54
|
+
def set_snet(connection, hostname)
|
55
|
+
if connection.snet?
|
56
|
+
"snet-#{hostname}"
|
57
|
+
else
|
58
|
+
hostname
|
59
|
+
end
|
54
60
|
end
|
55
|
-
end
|
56
61
|
end
|
57
62
|
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
module CloudFiles
|
2
2
|
class Connection
|
3
3
|
# See COPYING for license information.
|
4
|
-
# Copyright (c)
|
5
|
-
|
4
|
+
# Copyright (c) 2011, Rackspace US, Inc.
|
5
|
+
|
6
6
|
# Authentication key provided when the CloudFiles class was instantiated
|
7
7
|
attr_reader :authkey
|
8
8
|
|
@@ -11,19 +11,19 @@ module CloudFiles
|
|
11
11
|
|
12
12
|
# Authentication username provided when the CloudFiles class was instantiated
|
13
13
|
attr_reader :authuser
|
14
|
-
|
14
|
+
|
15
15
|
# API host to authenticate to
|
16
|
-
attr_reader :
|
16
|
+
attr_reader :auth_url
|
17
17
|
|
18
18
|
# Hostname of the CDN management server
|
19
19
|
attr_accessor :cdnmgmthost
|
20
20
|
|
21
21
|
# Path for managing containers on the CDN management server
|
22
22
|
attr_accessor :cdnmgmtpath
|
23
|
-
|
23
|
+
|
24
24
|
# Port number for the CDN server
|
25
25
|
attr_accessor :cdnmgmtport
|
26
|
-
|
26
|
+
|
27
27
|
# URI scheme for the CDN server
|
28
28
|
attr_accessor :cdnmgmtscheme
|
29
29
|
|
@@ -32,67 +32,65 @@ module CloudFiles
|
|
32
32
|
|
33
33
|
# Path for managing containers/objects on the storage server
|
34
34
|
attr_accessor :storagepath
|
35
|
-
|
35
|
+
|
36
36
|
# Port for managing the storage server
|
37
37
|
attr_accessor :storageport
|
38
|
-
|
38
|
+
|
39
39
|
# URI scheme for the storage server
|
40
40
|
attr_accessor :storagescheme
|
41
|
-
|
41
|
+
|
42
42
|
# Instance variable that is set when authorization succeeds
|
43
43
|
attr_accessor :authok
|
44
|
-
|
45
|
-
# The total size in bytes under this connection
|
46
|
-
attr_reader :bytes
|
47
|
-
|
48
|
-
# The total number of containers under this connection
|
49
|
-
attr_reader :count
|
50
|
-
|
44
|
+
|
51
45
|
# Optional proxy variables
|
52
46
|
attr_reader :proxy_host
|
53
47
|
attr_reader :proxy_port
|
54
|
-
|
48
|
+
|
55
49
|
# Creates a new CloudFiles::Connection object. Uses CloudFiles::Authentication to perform the login for the connection.
|
56
50
|
# The authuser is the Rackspace Cloud username, the authkey is the Rackspace Cloud API key.
|
57
51
|
#
|
58
|
-
# Setting the
|
52
|
+
# Setting the :retry_auth option to false will cause an exception to be thrown if your authorization token expires.
|
59
53
|
# Otherwise, it will attempt to reauthenticate.
|
60
54
|
#
|
61
|
-
# Setting the
|
62
|
-
# storage URLs to be returned with a prefix pointing them to the internal Rackspace service network, instead of a public URL.
|
55
|
+
# Setting the :snet option to true or setting an environment variable of RACKSPACE_SERVICENET to any value will cause
|
56
|
+
# storage URLs to be returned with a prefix pointing them to the internal Rackspace service network, instead of a public URL.
|
63
57
|
#
|
64
58
|
# This is useful if you are using the library on a Rackspace-hosted system, as it provides faster speeds, keeps traffic off of
|
65
59
|
# the public network, and the bandwidth is not billed.
|
66
60
|
#
|
67
|
-
# If you need to connect to a Cloud Files installation that is NOT the standard Rackspace one, set the :
|
68
|
-
# of your authentication endpoint. The default is https://auth.api.rackspacecloud.com/v1.0
|
61
|
+
# If you need to connect to a Cloud Files installation that is NOT the standard Rackspace one, set the :auth_url option to the URL
|
62
|
+
# of your authentication endpoint. The old option name of :authurl is deprecated. The default is CloudFiles::AUTH_USA (https://auth.api.rackspacecloud.com/v1.0)
|
63
|
+
#
|
64
|
+
# There are two predefined constants to represent the United States-based authentication endpoint and the United Kingdom-based endpoint:
|
65
|
+
# CloudFiles::AUTH_USA (the default) and CloudFiles::AUTH_UK - both can be passed to the :auth_url option to quickly choose one or the other.
|
69
66
|
#
|
70
67
|
# This will likely be the base class for most operations.
|
71
|
-
#
|
68
|
+
#
|
72
69
|
# With gem 1.4.8, the connection style has changed. It is now a hash of arguments. Note that the proxy options are currently only
|
73
70
|
# supported in the new style.
|
74
71
|
#
|
75
|
-
# cf = CloudFiles::Connection.new(:username => "MY_USERNAME", :api_key => "MY_API_KEY", :
|
72
|
+
# cf = CloudFiles::Connection.new(:username => "MY_USERNAME", :api_key => "MY_API_KEY", :auth_url => CloudFiles::AUTH_UK, :retry_auth => true, :snet => false, :proxy_host => "localhost", :proxy_port => "1234")
|
76
73
|
#
|
77
74
|
# The old style (positional arguments) is deprecated and will be removed at some point in the future.
|
78
|
-
#
|
75
|
+
#
|
79
76
|
# cf = CloudFiles::Connection.new(MY_USERNAME, MY_API_KEY, RETRY_AUTH, USE_SNET)
|
80
77
|
def initialize(*args)
|
81
78
|
if args[0].is_a?(Hash)
|
82
79
|
options = args[0]
|
83
|
-
@authuser = options[:username] ||( raise
|
84
|
-
@authkey = options[:api_key] || (raise
|
85
|
-
@
|
80
|
+
@authuser = options[:username] ||( raise CloudFiles::Exception::Authentication, "Must supply a :username")
|
81
|
+
@authkey = options[:api_key] || (raise CloudFiles::Exception::Authentication, "Must supply an :api_key")
|
82
|
+
@auth_url = options[:authurl] || CloudFiles::AUTH_USA
|
83
|
+
@auth_url = options[:auth_url] || CloudFiles::AUTH_USA
|
86
84
|
@retry_auth = options[:retry_auth] || true
|
87
85
|
@snet = ENV['RACKSPACE_SERVICENET'] || options[:snet]
|
88
86
|
@proxy_host = options[:proxy_host]
|
89
87
|
@proxy_port = options[:proxy_port]
|
90
88
|
elsif args[0].is_a?(String)
|
91
|
-
@authuser = args[0] ||( raise
|
92
|
-
@authkey = args[1] || (raise
|
89
|
+
@authuser = args[0] ||( raise CloudFiles::Exception::Authentication, "Must supply the username as the first argument")
|
90
|
+
@authkey = args[1] || (raise CloudFiles::Exception::Authentication, "Must supply the API key as the second argument")
|
93
91
|
@retry_auth = args[2] || true
|
94
92
|
@snet = (ENV['RACKSPACE_SERVICENET'] || args[3]) ? true : false
|
95
|
-
@
|
93
|
+
@auth_url = CloudFiles::AUTH_USA
|
96
94
|
end
|
97
95
|
@authok = false
|
98
96
|
@http = {}
|
@@ -106,7 +104,7 @@ module CloudFiles
|
|
106
104
|
def authok?
|
107
105
|
@authok
|
108
106
|
end
|
109
|
-
|
107
|
+
|
110
108
|
# Returns true if the library is requesting the use of the Rackspace service network
|
111
109
|
def snet?
|
112
110
|
@snet
|
@@ -119,7 +117,7 @@ module CloudFiles
|
|
119
117
|
# container.count
|
120
118
|
# => 2
|
121
119
|
def container(name)
|
122
|
-
CloudFiles::Container.new(self,name)
|
120
|
+
CloudFiles::Container.new(self, name)
|
123
121
|
end
|
124
122
|
alias :get_container :container
|
125
123
|
|
@@ -129,15 +127,25 @@ module CloudFiles
|
|
129
127
|
# cf.get_info
|
130
128
|
# => {:count=>8, :bytes=>42438527}
|
131
129
|
# cf.bytes
|
132
|
-
# => 42438527
|
130
|
+
# => 42438527
|
133
131
|
def get_info
|
134
|
-
response = cfreq("HEAD"
|
135
|
-
raise
|
132
|
+
response = cfreq("HEAD", @storagehost, @storagepath, @storageport, @storagescheme)
|
133
|
+
raise CloudFiles::Exception::InvalidResponse, "Unable to obtain account size" unless (response.code == "204")
|
136
134
|
@bytes = response["x-account-bytes-used"].to_i
|
137
135
|
@count = response["x-account-container-count"].to_i
|
138
136
|
{:bytes => @bytes, :count => @count}
|
139
137
|
end
|
140
138
|
|
139
|
+
# The total size in bytes under this connection
|
140
|
+
def bytes
|
141
|
+
get_info[:bytes]
|
142
|
+
end
|
143
|
+
|
144
|
+
# The total number of containers under this connection
|
145
|
+
def count
|
146
|
+
get_info[:count]
|
147
|
+
end
|
148
|
+
|
141
149
|
# Gathers a list of the containers that exist for the account and returns the list of container names
|
142
150
|
# as an array. If no containers exist, an empty array is returned. Throws an InvalidResponseException
|
143
151
|
# if the request fails.
|
@@ -146,18 +154,17 @@ module CloudFiles
|
|
146
154
|
# specified in limit, starting after the object named in marker.
|
147
155
|
#
|
148
156
|
# cf.containers
|
149
|
-
# => ["backup", "Books", "cftest", "test", "video", "webpics"]
|
157
|
+
# => ["backup", "Books", "cftest", "test", "video", "webpics"]
|
150
158
|
#
|
151
159
|
# cf.containers(2,'cftest')
|
152
160
|
# => ["test", "video"]
|
153
|
-
def containers(limit=0,marker="")
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
response = cfreq("GET",@storagehost,"#{@storagepath}?#{paramstr}",@storageport,@storagescheme)
|
161
|
+
def containers(limit = 0, marker = "")
|
162
|
+
query = []
|
163
|
+
query << "limit=#{CloudFiles.escape limit.to_s}" if limit.to_i > 0
|
164
|
+
query << "marker=#{CloudFiles.escape marker.to_s}" unless marker.to_s.empty?
|
165
|
+
response = cfreq("GET", @storagehost, "#{@storagepath}?#{query.join '&'}", @storageport, @storagescheme)
|
159
166
|
return [] if (response.code == "204")
|
160
|
-
raise
|
167
|
+
raise CloudFiles::Exception::InvalidResponse, "Invalid response code #{response.code}" unless (response.code == "200")
|
161
168
|
CloudFiles.lines(response.body)
|
162
169
|
end
|
163
170
|
alias :list_containers :containers
|
@@ -168,18 +175,17 @@ module CloudFiles
|
|
168
175
|
#
|
169
176
|
# If you supply the optional limit and marker parameters, the call will return the number of containers
|
170
177
|
# specified in limit, starting after the object named in marker.
|
171
|
-
#
|
172
|
-
# cf.containers_detail
|
173
|
-
# => { "container1" => { :bytes => "36543", :count => "146" },
|
178
|
+
#
|
179
|
+
# cf.containers_detail
|
180
|
+
# => { "container1" => { :bytes => "36543", :count => "146" },
|
174
181
|
# "container2" => { :bytes => "105943", :count => "25" } }
|
175
|
-
def containers_detail(limit=0,marker="")
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
response = cfreq("GET",@storagehost,"#{@storagepath}?format=xml&#{paramstr}",@storageport,@storagescheme)
|
182
|
+
def containers_detail(limit = 0, marker = "")
|
183
|
+
query = ['format=xml']
|
184
|
+
query << "limit=#{CloudFiles.escape limit.to_s}" if limit.to_i > 0
|
185
|
+
query << "marker=#{CloudFiles.escape marker.to_s}" unless marker.to_s.empty?
|
186
|
+
response = cfreq("GET", @storagehost, "#{@storagepath}?#{query.join '&'}", @storageport, @storagescheme)
|
181
187
|
return {} if (response.code == "204")
|
182
|
-
raise
|
188
|
+
raise CloudFiles::Exception::InvalidResponse, "Invalid response code #{response.code}" unless (response.code == "200")
|
183
189
|
doc = REXML::Document.new(response.body)
|
184
190
|
detailhash = {}
|
185
191
|
doc.elements.each("account/container/") { |c|
|
@@ -191,21 +197,21 @@ module CloudFiles
|
|
191
197
|
alias :list_containers_info :containers_detail
|
192
198
|
|
193
199
|
# Returns true if the requested container exists and returns false otherwise.
|
194
|
-
#
|
200
|
+
#
|
195
201
|
# cf.container_exists?('good_container')
|
196
202
|
# => true
|
197
|
-
#
|
203
|
+
#
|
198
204
|
# cf.container_exists?('bad_container')
|
199
205
|
# => false
|
200
206
|
def container_exists?(containername)
|
201
|
-
response = cfreq("HEAD"
|
207
|
+
response = cfreq("HEAD", @storagehost, "#{@storagepath}/#{CloudFiles.escape containername}", @storageport, @storagescheme)
|
202
208
|
return (response.code == "204")? true : false ;
|
203
209
|
end
|
204
210
|
|
205
|
-
# Creates a new container and returns the CloudFiles::Container object. Throws an InvalidResponseException if the
|
211
|
+
# Creates a new container and returns the CloudFiles::Container object. Throws an InvalidResponseException if the
|
206
212
|
# request fails.
|
207
213
|
#
|
208
|
-
# Slash (/) and question mark (?) are invalid characters, and will be stripped out. The container name is limited to
|
214
|
+
# Slash (/) and question mark (?) are invalid characters, and will be stripped out. The container name is limited to
|
209
215
|
# 256 characters or less.
|
210
216
|
#
|
211
217
|
# container = cf.create_container('new_container')
|
@@ -215,16 +221,16 @@ module CloudFiles
|
|
215
221
|
# container = cf.create_container('bad/name')
|
216
222
|
# => SyntaxException: Container name cannot contain the characters '/' or '?'
|
217
223
|
def create_container(containername)
|
218
|
-
raise
|
219
|
-
raise
|
220
|
-
response = cfreq("PUT"
|
221
|
-
raise
|
222
|
-
CloudFiles::Container.new(self,containername)
|
224
|
+
raise CloudFiles::Exception::Syntax, "Container name cannot contain the characters '/' or '?'" if containername.match(/[\/\?]/)
|
225
|
+
raise CloudFiles::Exception::Syntax, "Container name is limited to 256 characters" if containername.length > 256
|
226
|
+
response = cfreq("PUT", @storagehost, "#{@storagepath}/#{CloudFiles.escape containername}", @storageport, @storagescheme)
|
227
|
+
raise CloudFiles::Exception::InvalidResponse, "Unable to create container #{containername}" unless (response.code == "201" || response.code == "202")
|
228
|
+
CloudFiles::Container.new(self, containername)
|
223
229
|
end
|
224
230
|
|
225
231
|
# Deletes a container from the account. Throws a NonEmptyContainerException if the container still contains
|
226
232
|
# objects. Throws a NoSuchContainerException if the container doesn't exist.
|
227
|
-
#
|
233
|
+
#
|
228
234
|
# cf.delete_container('new_container')
|
229
235
|
# => true
|
230
236
|
#
|
@@ -234,9 +240,9 @@ module CloudFiles
|
|
234
240
|
# cf.delete_container('nonexistent')
|
235
241
|
# => NoSuchContainerException: Container nonexistent does not exist
|
236
242
|
def delete_container(containername)
|
237
|
-
response = cfreq("DELETE"
|
238
|
-
raise
|
239
|
-
raise
|
243
|
+
response = cfreq("DELETE", @storagehost, "#{@storagepath}/#{CloudFiles.escape containername}", @storageport, @storagescheme)
|
244
|
+
raise CloudFiles::Exception::NonEmptyContainer, "Container #{containername} is not empty" if (response.code == "409")
|
245
|
+
raise CloudFiles::Exception::NoSuchContainer, "Container #{containername} does not exist" unless (response.code == "204")
|
240
246
|
true
|
241
247
|
end
|
242
248
|
|
@@ -244,26 +250,26 @@ module CloudFiles
|
|
244
250
|
# as an array. If no containers are public, an empty array is returned. Throws a InvalidResponseException if
|
245
251
|
# the request fails.
|
246
252
|
#
|
247
|
-
# If you pass the optional argument as true, it will only show containers that are CURRENTLY being shared on the CDN,
|
253
|
+
# If you pass the optional argument as true, it will only show containers that are CURRENTLY being shared on the CDN,
|
248
254
|
# as opposed to the default behavior which is to show all containers that have EVER been public.
|
249
255
|
#
|
250
256
|
# cf.public_containers
|
251
257
|
# => ["video", "webpics"]
|
252
258
|
def public_containers(enabled_only = false)
|
253
259
|
paramstr = enabled_only == true ? "enabled_only=true" : ""
|
254
|
-
response = cfreq("GET"
|
260
|
+
response = cfreq("GET", @cdnmgmthost, "#{@cdnmgmtpath}?#{paramstr}", @cdnmgmtport, @cdnmgmtscheme)
|
255
261
|
return [] if (response.code == "204")
|
256
|
-
raise
|
262
|
+
raise CloudFiles::Exception::InvalidResponse, "Invalid response code #{response.code}" unless (response.code == "200")
|
257
263
|
CloudFiles.lines(response.body)
|
258
264
|
end
|
259
265
|
|
260
266
|
# This method actually makes the HTTP calls out to the server
|
261
|
-
def cfreq(method,server,path,port,scheme,headers = {},data = nil,attempts = 0
|
267
|
+
def cfreq(method, server, path, port, scheme, headers = {}, data = nil, attempts = 0, &block) # :nodoc:
|
262
268
|
start = Time.now
|
263
269
|
headers['Transfer-Encoding'] = "chunked" if data.is_a?(IO)
|
264
270
|
hdrhash = headerprep(headers)
|
265
|
-
start_http(server,path,port,scheme,hdrhash)
|
266
|
-
request = Net::HTTP.const_get(method.to_s.capitalize).new(path,hdrhash)
|
271
|
+
start_http(server, path, port, scheme, hdrhash)
|
272
|
+
request = Net::HTTP.const_get(method.to_s.capitalize).new(path, hdrhash)
|
267
273
|
if data
|
268
274
|
if data.respond_to?(:read)
|
269
275
|
request.body_stream = data
|
@@ -276,24 +282,24 @@ module CloudFiles
|
|
276
282
|
else
|
277
283
|
request.content_length = 0
|
278
284
|
end
|
279
|
-
response = @http[server].request(request
|
280
|
-
raise
|
285
|
+
response = @http[server].request(request, &block)
|
286
|
+
raise CloudFiles::Exception::ExpiredAuthToken if response.code == "401"
|
281
287
|
response
|
282
288
|
rescue Errno::EPIPE, Timeout::Error, Errno::EINVAL, EOFError
|
283
289
|
# Server closed the connection, retry
|
284
|
-
raise
|
290
|
+
raise CloudFiles::Exception::Connection, "Unable to reconnect to #{server} after #{count} attempts" if attempts >= 5
|
285
291
|
attempts += 1
|
286
292
|
@http[server].finish
|
287
|
-
start_http(server,path,port,scheme,headers)
|
293
|
+
start_http(server, path, port, scheme, headers)
|
288
294
|
retry
|
289
295
|
rescue ExpiredAuthTokenException
|
290
|
-
raise
|
296
|
+
raise CloudFiles::Exception::Connection, "Authentication token expired and you have requested not to retry" if @retry_auth == false
|
291
297
|
CloudFiles::Authentication.new(self)
|
292
298
|
retry
|
293
299
|
end
|
294
|
-
|
300
|
+
|
295
301
|
private
|
296
|
-
|
302
|
+
|
297
303
|
# Sets up standard HTTP headers
|
298
304
|
def headerprep(headers = {}) # :nodoc:
|
299
305
|
default_headers = {}
|
@@ -303,19 +309,19 @@ module CloudFiles
|
|
303
309
|
default_headers["User-Agent"] = "CloudFiles Ruby API #{VERSION}"
|
304
310
|
default_headers.merge(headers)
|
305
311
|
end
|
306
|
-
|
312
|
+
|
307
313
|
# Starts (or restarts) the HTTP connection
|
308
|
-
def start_http(server,path,port,scheme,headers) # :nodoc:
|
314
|
+
def start_http(server, path, port, scheme, headers) # :nodoc:
|
309
315
|
if (@http[server].nil?)
|
310
316
|
begin
|
311
|
-
@http[server] = Net::HTTP::Proxy(self.proxy_host, self.proxy_port).new(server,port)
|
317
|
+
@http[server] = Net::HTTP::Proxy(self.proxy_host, self.proxy_port).new(server, port)
|
312
318
|
if scheme == "https"
|
313
319
|
@http[server].use_ssl = true
|
314
320
|
@http[server].verify_mode = OpenSSL::SSL::VERIFY_NONE
|
315
321
|
end
|
316
322
|
@http[server].start
|
317
323
|
rescue
|
318
|
-
raise
|
324
|
+
raise CloudFiles::Exception::Connection, "Unable to connect to #{server}"
|
319
325
|
end
|
320
326
|
end
|
321
327
|
end
|