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. * container.
* *
* @todo Add support for container metadata. * @todo Add support for container metadata.
* @todo Add CDN support fo container listings.
*/ */
class Container implements \Countable, \IteratorAggregate { class Container implements \Countable, \IteratorAggregate {
/** /**
@@ -61,8 +62,10 @@ class Container implements \Countable, \IteratorAggregate {
//protected $properties = array(); //protected $properties = array();
protected $name = NULL; protected $name = NULL;
protected $count = 0; // These were both changed from 0 to NULL to allow
protected $bytes = 0; // lazy loading.
protected $count = NULL;
protected $bytes = NULL;
protected $token; protected $token;
protected $url; protected $url;
@@ -70,6 +73,9 @@ class Container implements \Countable, \IteratorAggregate {
protected $acl; protected $acl;
protected $metadata; protected $metadata;
// This is only set if CDN service is activated.
protected $cdnUrl;
/** /**
* Transform a metadata array into headers. * Transform a metadata array into headers.
* *
@@ -251,7 +257,8 @@ class Container implements \Countable, \IteratorAggregate {
* @attention * @attention
* Typically a container should be created by ObjectStorage::createContainer(). * Typically a container should be created by ObjectStorage::createContainer().
* Get existing containers with ObjectStorage::container() or * 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. * 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 * constructing a Container in no way guarantees that such a container exists
* on the origin object store. * 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 * @param string $name
* The name. * The name.
* @param string $url * @param string $url
@@ -273,6 +302,24 @@ class Container implements \Countable, \IteratorAggregate {
$this->token = $token; $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. * Get the name of this container.
* *
@@ -290,6 +337,9 @@ class Container implements \Countable, \IteratorAggregate {
* The number of bytes in this container. * The number of bytes in this container.
*/ */
public function bytes() { public function bytes() {
if (is_null($this->bytes)) {
$this->loadExtraData();
}
return $this->bytes; return $this->bytes;
} }
@@ -359,6 +409,9 @@ class Container implements \Countable, \IteratorAggregate {
* The number of items in this container. * The number of items in this container.
*/ */
public function count() { public function count() {
if (is_null($this->count)) {
$this->loadExtraData();
}
return $this->count; return $this->count;
} }
@@ -602,6 +655,10 @@ class Container implements \Countable, \IteratorAggregate {
* - If-Modified-Since/If-Unmodified-Since * - If-Modified-Since/If-Unmodified-Since
* - If-Match/If-None-Match * - 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 * @param string $name
* The name of the object to load. * The name of the object to load.
* @retval \HPCloud\Storage\ObjectStorage\RemoteObject * @retval \HPCloud\Storage\ObjectStorage\RemoteObject
@@ -610,13 +667,20 @@ class Container implements \Countable, \IteratorAggregate {
public function object($name) { public function object($name) {
$url = self::objectUrl($this->url, $name); $url = self::objectUrl($this->url, $name);
$cdn = self::objectUrl($this->cdnUrl, $name);
$headers = array(); $headers = array();
// Auth token. // Auth token.
$headers['X-Auth-Token'] = $this->token; $headers['X-Auth-Token'] = $this->token;
$client = \HPCloud\Transport::instance(); $client = \HPCloud\Transport::instance();
if (empty($this->cdnUrl)) {
$response = $client->doRequest($url, 'GET', $headers); $response = $client->doRequest($url, 'GET', $headers);
}
else {
$response = $client->doRequest($cdn, 'GET', $headers);
}
if ($response->status() != 200) { if ($response->status() != 200) {
throw new \HPCloud\Exception('An unknown error occurred while saving: ' . $response->status()); 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 = RemoteObject::newFromHeaders($name, $response->headers(), $this->token, $url);
$remoteObject->setContent($response->content()); $remoteObject->setContent($response->content());
if (!empty($this->cdnUrl)) {
$remoteObject->useCDN($cdn);
}
return $remoteObject; return $remoteObject;
} }
@@ -659,13 +727,20 @@ class Container implements \Countable, \IteratorAggregate {
*/ */
public function remoteObject($name) { public function remoteObject($name) {
$url = self::objectUrl($this->url, $name); $url = self::objectUrl($this->url, $name);
$cdn = self::objectUrl($this->cdnUrl, $name);
$headers = array( $headers = array(
'X-Auth-Token' => $this->token, 'X-Auth-Token' => $this->token,
); );
$client = \HPCloud\Transport::instance(); $client = \HPCloud\Transport::instance();
if (empty($this->cdnUrl)) {
$response = $client->doRequest($url, 'HEAD', $headers); $response = $client->doRequest($url, 'HEAD', $headers);
}
else {
$response = $client->doRequest($cdn, 'HEAD', $headers);
}
if ($response->status() != 200) { if ($response->status() != 200) {
throw new \HPCloud\Exception('An unknown error occurred while saving: ' . $response->status()); throw new \HPCloud\Exception('An unknown error occurred while saving: ' . $response->status());
@@ -673,7 +748,13 @@ class Container implements \Countable, \IteratorAggregate {
$headers = $response->headers(); $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; return $this->url;
} }
public function cdnUrl() {
return $this->cdnUrl;
}
/** /**
* Get the ACL. * Get the ACL.
* *
@@ -867,6 +952,14 @@ class Container implements \Countable, \IteratorAggregate {
* called to "fill in" missing fields. * called to "fill in" missing fields.
*/ */
protected function loadExtraData() { protected function loadExtraData() {
// 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. // Do a GET on $url to fetch headers.
$client = \HPCloud\Transport::instance(); $client = \HPCloud\Transport::instance();
$headers = array( $headers = array(
@@ -877,6 +970,10 @@ class Container implements \Countable, \IteratorAggregate {
// Get ACL. // Get ACL.
$this->acl = ACL::newFromHeaders($response->headers()); $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. // Get metadata.
$prefix = Container::CONTAINER_METADATA_HEADER_PREFIX; $prefix = Container::CONTAINER_METADATA_HEADER_PREFIX;
$this->setMetadata(Container::extractHeaderAttributes($response->headers(), $prefix)); $this->setMetadata(Container::extractHeaderAttributes($response->headers(), $prefix));