Class: CloudFiles::Connection

Inherits:
Object
  • Object
show all
Defined in:
lib/cloudfiles/connection.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(authuser, authkey, retry_auth = true) ⇒ Connection

Creates a new CloudFiles::Connection object. Uses CloudFiles::Authentication to perform the login for the connection. The authuser is the Mosso username, the authkey is the Mosso API key.

Setting the optional retry_auth variable to false will cause an exception to be thrown if your authorization token expires. Otherwise, it will attempt to reauthenticate.

This will likely be the base class for most operations.

cf = CloudFiles::Connection.new(MY_USERNAME, MY_API_KEY)


48
49
50
51
52
53
54
55
56
# File 'lib/cloudfiles/connection.rb', line 48

def initialize(authuser,authkey,retry_auth = true) 
  @authuser = authuser
  @authkey = authkey
  @retry_auth = retry_auth
  @authok = false
  @http = {}
  @reqlog = []
  CloudFiles::Authentication.new(self)
end

Instance Attribute Details

#authkeyObject (readonly)

Authentication key provided when the CloudFiles class was instantiated



7
8
9
# File 'lib/cloudfiles/connection.rb', line 7

def authkey
  @authkey
end

#authokObject

Instance variable that is set when authorization succeeds



31
32
33
# File 'lib/cloudfiles/connection.rb', line 31

def authok
  @authok
end

#authtokenObject

Token returned after a successful authentication



10
11
12
# File 'lib/cloudfiles/connection.rb', line 10

def authtoken
  @authtoken
end

#authuserObject (readonly)

Authentication username provided when the CloudFiles class was instantiated



13
14
15
# File 'lib/cloudfiles/connection.rb', line 13

def authuser
  @authuser
end

#bytesObject (readonly)

The total size in bytes under this connection



34
35
36
# File 'lib/cloudfiles/connection.rb', line 34

def bytes
  @bytes
end

#cdnmgmthostObject

Hostname of the CDN management server



16
17
18
# File 'lib/cloudfiles/connection.rb', line 16

def cdnmgmthost
  @cdnmgmthost
end

#cdnmgmtpathObject

Path for managing containers on the CDN management server



19
20
21
# File 'lib/cloudfiles/connection.rb', line 19

def cdnmgmtpath
  @cdnmgmtpath
end

#countObject (readonly)

The total number of containers under this connection



37
38
39
# File 'lib/cloudfiles/connection.rb', line 37

def count
  @count
end

#reqlogObject (readonly)

Array of requests that have been made so far



22
23
24
# File 'lib/cloudfiles/connection.rb', line 22

def reqlog
  @reqlog
end

#storagehostObject

Hostname of the storage server



25
26
27
# File 'lib/cloudfiles/connection.rb', line 25

def storagehost
  @storagehost
end

#storagepathObject

Path for managing containers/objects on the storage server



28
29
30
# File 'lib/cloudfiles/connection.rb', line 28

def storagepath
  @storagepath
end

Instance Method Details

#authok?Boolean

Returns true if the authentication was successful and returns false otherwise.

cf.authok?
=> true

Returns:

  • (Boolean)


62
63
64
# File 'lib/cloudfiles/connection.rb', line 62

def authok?
  @authok
end

#cfreq(method, server, path, headers = {}, data = nil, attempts = 0, &block) ⇒ Object

This method actually makes the HTTP calls out to the server



212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/cloudfiles/connection.rb', line 212

def cfreq(method,server,path,headers = {},data = nil,attempts = 0,&block) # :nodoc:
  if data == "STDIN" # Hack (see next comment)
    headers['Transfer-Encoding'] = "chunked"
  end     

  start = Time.now
  hdrhash = headerprep(headers)
  path = URI.escape(path)
  start_http(server,path,hdrhash)
  request = Net::HTTP.const_get(method.to_s.capitalize).new(path,hdrhash)
  
  # Hacked by Jon Stacey to enable piped data to be streamed
  if data
    if data == "STDIN"
      request.body_stream = $stdin
    elsif data.respond_to?(:read)
      request.body_stream = data
    else
      request.body = data
    end
    
    unless data == "STDIN"              
      request.content_length = data.respond_to?(:lstat) ? data.stat.size : data.size
    end
    
  else
    request.content_length = 0
  end # if data
  
  response = @http[server].request(request,&block)
  raise ExpiredAuthTokenException if response.code == "401"
  response
rescue Errno::EPIPE, Timeout::Error, Errno::EINVAL, EOFError
  # Server closed the connection, retry
  raise ConnectionException, "Unable to reconnect to #{server} after #{count} attempts" if attempts >= 5
  attempts += 1
  @http[server].finish
  start_http(server,path,headers)
  retry
rescue ExpiredAuthTokenException
  raise ConnectionException, "Authentication token expired and you have requested not to retry" if @retry_auth == false
  CloudFiles::Authentication.new(self)
  retry
end

#container(name) ⇒ Object Also known as: get_container

Returns an CloudFiles::Container object that can be manipulated easily. Throws a NoSuchContainerException if the container doesn’t exist.

container = cf.container('test')
container.count
=> 2


72
73
74
# File 'lib/cloudfiles/connection.rb', line 72

def container(name)
  CloudFiles::Container.new(self,name)
end

#container_exists?(containername) ⇒ Boolean

Returns true if the requested container exists and returns false otherwise.

cf.container_exists?('good_container')
=> true

cf.container_exists?('bad_container')
=> false

Returns:

  • (Boolean)


151
152
153
154
# File 'lib/cloudfiles/connection.rb', line 151

def container_exists?(containername)
  response = cfreq("HEAD",@storagehost,"#{@storagepath}/#{containername}")
  return (response.code == "204")? true : false ;
end

#containers(limit = 0, marker = "") ⇒ Object Also known as: list_containers

Gathers a list of the containers that exist for the account and returns the list of container names as an array. If no containers exist, an empty array is returned. Throws an InvalidResponseException if the request fails.

If you supply the optional limit and marker parameters, the call will return the number of containers specified in limit, starting after the object named in marker.

cf.containers
=> ["backup", "Books", "cftest", "test", "video", "webpics"] 

cf.containers(2,'cftest')
=> ["test", "video"]


104
105
106
107
108
109
110
111
112
113
# File 'lib/cloudfiles/connection.rb', line 104

def containers(limit=0,marker="")
  paramarr = []
  paramarr << ["limit=#{URI.encode(limit.to_s)}"] if limit.to_i > 0
  paramarr << ["offset=#{URI.encode(marker.to_s)}"] unless marker.to_s.empty?
  paramstr = (paramarr.size > 0)? paramarr.join("&") : "" ;
  response = cfreq("GET",@storagehost,"#{@storagepath}?#{paramstr}")
  return [] if (response.code == "204")
  raise InvalidResponseException, "Invalid response code #{response.code}" unless (response.code == "200")
  response.body.to_a.map { |x| x.chomp }
end

#containers_detail(limit = 0, marker = "") ⇒ Object Also known as: list_containers_info

Retrieves a list of containers on the account along with their sizes (in bytes) and counts of the objects held within them. If no containers exist, an empty hash is returned. Throws an InvalidResponseException if the request fails.

If you supply the optional limit and marker parameters, the call will return the number of containers specified in limit, starting after the object named in marker.

cf.containers_detail              
=> { "container1" => { :bytes => "36543", :count => "146" }, 
     "container2" => { :bytes => "105943", :count => "25" } }


126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/cloudfiles/connection.rb', line 126

def containers_detail(limit=0,marker="")
  paramarr = []
  paramarr << ["limit=#{URI.encode(limit.to_s)}"] if limit.to_i > 0
  paramarr << ["offset=#{URI.encode(marker.to_s)}"] unless marker.to_s.empty?
  paramstr = (paramarr.size > 0)? paramarr.join("&") : "" ;
  response = cfreq("GET",@storagehost,"#{@storagepath}?format=xml&#{paramstr}")
  return {} if (response.code == "204")
  raise InvalidResponseException, "Invalid response code #{response.code}" unless (response.code == "200")
  doc = REXML::Document.new(response.body)
  detailhash = {}
  doc.elements.each("account/container/") { |c|
    detailhash[c.elements["name"].text] = { :bytes => c.elements["bytes"].text, :count => c.elements["count"].text  }
  }
  doc = nil
  return detailhash
end

#create_container(containername) ⇒ Object

Creates a new container and returns the CloudFiles::Container object. Throws an InvalidResponseException if the request fails.

Slash (/) and question mark (?) are invalid characters, and will be stripped out. The container name is limited to 256 characters or less.

container = cf.create_container('new_container')
container.name
=> "new_container"

container = cf.create_container('bad/name')
=> SyntaxException: Container name cannot contain the characters '/' or '?'

Raises:



168
169
170
171
172
173
174
# File 'lib/cloudfiles/connection.rb', line 168

def create_container(containername)
  raise SyntaxException, "Container name cannot contain the characters '/' or '?'" if containername.match(/[\/\?]/)
  raise SyntaxException, "Container name is limited to 256 characters" if containername.length > 256
  response = cfreq("PUT",@storagehost,"#{@storagepath}/#{containername}")
  raise InvalidResponseException, "Unable to create container #{containername}" unless (response.code == "201" || response.code == "202")
  CloudFiles::Container.new(self,containername)
end

#delete_container(containername) ⇒ Object

Deletes a container from the account. Throws a NonEmptyContainerException if the container still contains objects. Throws a NoSuchContainerException if the container doesn’t exist.

cf.delete_container('new_container')
=> true

cf.delete_container('video')
=> NonEmptyContainerException: Container video is not empty

cf.delete_container('nonexistent')
=> NoSuchContainerException: Container nonexistent does not exist


187
188
189
190
191
192
# File 'lib/cloudfiles/connection.rb', line 187

def delete_container(containername)
  response = cfreq("DELETE",@storagehost,"#{@storagepath}/#{containername}")
  raise NonEmptyContainerException, "Container #{containername} is not empty" if (response.code == "409")
  raise NoSuchContainerException, "Container #{containername} does not exist" unless (response.code == "204")
  true
end

#get_infoObject

Sets instance variables for the bytes of storage used for this account/connection, as well as the number of containers stored under the account. Returns a hash with :bytes and :count keys, and also sets the instance variables.

cf.get_info
=> {:count=>8, :bytes=>42438527}
cf.bytes
=> 42438527


84
85
86
87
88
89
90
# File 'lib/cloudfiles/connection.rb', line 84

def get_info
  response = cfreq("HEAD",@storagehost,@storagepath)
  raise InvalidResponseException, "Unable to obtain account size" unless (response.code == "204")
  @bytes = response["x-account-bytes-used"].to_i
  @count = response["x-account-container-count"].to_i
  {:bytes => @bytes, :count => @count}
end

#public_containers(enabled_only = false) ⇒ Object

Gathers a list of public (CDN-enabled) containers that exist for an account and returns the list of container names as an array. If no containers are public, an empty array is returned. Throws a InvalidResponseException if the request fails.

If you pass the optional argument as true, it will only show containers that are CURRENTLY being shared on the CDN, as opposed to the default behavior which is to show all containers that have EVER been public.

cf.public_containers
=> ["video", "webpics"]


203
204
205
206
207
208
209
# File 'lib/cloudfiles/connection.rb', line 203

def public_containers(enabled_only = false)
  paramstr = enabled_only == true ? "enabled_only=true" : ""
  response = cfreq("GET",@cdnmgmthost,"#{@cdnmgmtpath}?#{paramstr}")
  return [] if (response.code == "204")
  raise InvalidResponseException, "Invalid response code #{response.code}" unless (response.code == "200")
  response.body.to_a.map { |x| x.chomp }
end