Added CDN support.

This commit is contained in:
Matt Butcher
2012-03-13 18:02:48 -05:00
parent 7a47c96e1e
commit 23b49594e7

View File

@@ -49,6 +49,7 @@ namespace HPCloud\Storage\ObjectStorage;
* container.
*
* @todo Add support for container metadata.
* @todo Add CDN support fo container listings.
*/
class Container implements \Countable, \IteratorAggregate {
/**
@@ -61,8 +62,10 @@ class Container implements \Countable, \IteratorAggregate {
//protected $properties = array();
protected $name = NULL;
protected $count = 0;
protected $bytes = 0;
// These were both changed from 0 to NULL to allow
// lazy loading.
protected $count = NULL;
protected $bytes = NULL;
protected $token;
protected $url;
@@ -70,6 +73,9 @@ class Container implements \Countable, \IteratorAggregate {
protected $acl;
protected $metadata;
// This is only set if CDN service is activated.
protected $cdnUrl;
/**
* Transform a metadata array into headers.
*
@@ -251,7 +257,8 @@ class Container implements \Countable, \IteratorAggregate {
* @attention
* Typically a container should be created by ObjectStorage::createContainer().
* Get existing containers with ObjectStorage::container() or
* ObjectStorage::containers(). Do not use this unless you know what you are doing.
* ObjectStorage::containers(). Using the constructor directly has some
* side effects of which you should be aware.
*
* Simply creating a container does not save the container remotely.
*
@@ -259,6 +266,28 @@ class Container implements \Countable, \IteratorAggregate {
* constructing a Container in no way guarantees that such a container exists
* on the origin object store.
*
* The constructor involves a selective lazy loading. If a new container is created,
* and one of its accessors is called before the accessed values are initialized, then
* this will make a network round-trip to get the container from the remote server.
*
* Containers loaded from ObjectStorage::container() or Container::newFromRemote()
* will have all of the necessary values set, and thus will not require an extra network
* transaction to fetch properties.
*
* The practical result of this:
*
* - If you are creating a new container, it is best to do so with
* ObjectStorage::createContainer().
* - If you are manipulating an existing container, it is best to load the
* container with ObjectStorage::container().
* - If you are simply using the container to fetch resources from the
* container, you may wish to use `new Container($name, $url, $token)`
* and then load objects from that container. Note, however, that
* manipulating the container directly will likely involve an extra HTTP
* transaction to load the container data.
* - When in doubt, use the ObjectStorage methods. That is always the safer
* option.
*
* @param string $name
* The name.
* @param string $url
@@ -273,6 +302,24 @@ class Container implements \Countable, \IteratorAggregate {
$this->token = $token;
}
/**
* Set the URL of the CDN to use.
*
* If this is set, the Container will attempt to fetch objects
* from the CDN instead of the Swift storage whenever possible.
*
* If ObjectStorage::useCDN() is already called, this is not necessary.
*
* Setting this to NULL will have the effect of turning off CDN for this
* container.
*
* @param string $url
* The URL to the CDN for this container.
*/
public function useCDN($url) {
$this->cdnUrl = $url;
}
/**
* Get the name of this container.
*
@@ -290,13 +337,16 @@ class Container implements \Countable, \IteratorAggregate {
* The number of bytes in this container.
*/
public function bytes() {
if (is_null($this->bytes)) {
$this->loadExtraData();
}
return $this->bytes;
}
/**
* Get the container metadata.
*
* Metadata (also called tags) are name/value pairs that can be
* Metadata (also called tags) are name/value pairs that can be
* attached to a container.
*
* Names can be no longer than 128 characters, and values can be no
@@ -359,6 +409,9 @@ class Container implements \Countable, \IteratorAggregate {
* The number of items in this container.
*/
public function count() {
if (is_null($this->count)) {
$this->loadExtraData();
}
return $this->count;
}
@@ -602,6 +655,10 @@ class Container implements \Countable, \IteratorAggregate {
* - If-Modified-Since/If-Unmodified-Since
* - If-Match/If-None-Match
*
* If a CDN has been specified either using useCDN() or
* ObjectStorage::useCDN(), this will attempt to fetch the object
* from the CDN.
*
* @param string $name
* The name of the object to load.
* @retval \HPCloud\Storage\ObjectStorage\RemoteObject
@@ -610,13 +667,20 @@ class Container implements \Countable, \IteratorAggregate {
public function object($name) {
$url = self::objectUrl($this->url, $name);
$cdn = self::objectUrl($this->cdnUrl, $name);
$headers = array();
// Auth token.
$headers['X-Auth-Token'] = $this->token;
$client = \HPCloud\Transport::instance();
$response = $client->doRequest($url, 'GET', $headers);
if (empty($this->cdnUrl)) {
$response = $client->doRequest($url, 'GET', $headers);
}
else {
$response = $client->doRequest($cdn, 'GET', $headers);
}
if ($response->status() != 200) {
throw new \HPCloud\Exception('An unknown error occurred while saving: ' . $response->status());
@@ -625,6 +689,10 @@ class Container implements \Countable, \IteratorAggregate {
$remoteObject = RemoteObject::newFromHeaders($name, $response->headers(), $this->token, $url);
$remoteObject->setContent($response->content());
if (!empty($this->cdnUrl)) {
$remoteObject->useCDN($cdn);
}
return $remoteObject;
}
@@ -659,13 +727,20 @@ class Container implements \Countable, \IteratorAggregate {
*/
public function remoteObject($name) {
$url = self::objectUrl($this->url, $name);
$cdn = self::objectUrl($this->cdnUrl, $name);
$headers = array(
'X-Auth-Token' => $this->token,
);
$client = \HPCloud\Transport::instance();
$response = $client->doRequest($url, 'HEAD', $headers);
if (empty($this->cdnUrl)) {
$response = $client->doRequest($url, 'HEAD', $headers);
}
else {
$response = $client->doRequest($cdn, 'HEAD', $headers);
}
if ($response->status() != 200) {
throw new \HPCloud\Exception('An unknown error occurred while saving: ' . $response->status());
@@ -673,7 +748,13 @@ class Container implements \Countable, \IteratorAggregate {
$headers = $response->headers();
return RemoteObject::newFromHeaders($name, $headers, $this->token, $url);
$obj = RemoteObject::newFromHeaders($name, $headers, $this->token, $url);
if (!empty($this->cdnUrl)) {
$obj->useCDN($cdn);
}
return $obj;
}
/**
@@ -836,6 +917,10 @@ class Container implements \Countable, \IteratorAggregate {
return $this->url;
}
public function cdnUrl() {
return $this->cdnUrl;
}
/**
* Get the ACL.
*
@@ -867,19 +952,31 @@ class Container implements \Countable, \IteratorAggregate {
* called to "fill in" missing fields.
*/
protected function loadExtraData() {
// Do a GET on $url to fetch headers.
$client = \HPCloud\Transport::instance();
$headers = array(
'X-Auth-Token' => $this->token,
);
$response = $client->doRequest($this->url, 'GET', $headers);
// Get ACL.
$this->acl = ACL::newFromHeaders($response->headers());
// If URL and token are empty, we are dealing with
// a local item that has not been saved, and was not
// created with Container::createContainer(). We treat
// this as an error condition.
if (empty($this->url) || empty($this->token)) {
throw new \HPCloud\Exception('Remote data cannot be fetched. Tokena and endpoint URL are required.');
}
// Do a GET on $url to fetch headers.
$client = \HPCloud\Transport::instance();
$headers = array(
'X-Auth-Token' => $this->token,
);
$response = $client->doRequest($this->url, 'GET', $headers);
// Get metadata.
$prefix = Container::CONTAINER_METADATA_HEADER_PREFIX;
$this->setMetadata(Container::extractHeaderAttributes($response->headers(), $prefix));
// Get ACL.
$this->acl = ACL::newFromHeaders($response->headers());
// Update size and count.
$this->bytes = $response->header('X-Container-Bytes-Used', 0);
$this->count = $response->header('X-Container-Object-Count', 0);
// Get metadata.
$prefix = Container::CONTAINER_METADATA_HEADER_PREFIX;
$this->setMetadata(Container::extractHeaderAttributes($response->headers(), $prefix));
}