From af7c43bb5f2c59496ab6ee887862feaaf9d760e0 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Wed, 2 May 2012 10:00:56 -0500 Subject: [PATCH 1/3] Fixing typos. --- test/AuthTest.php | 2 +- test/example.settings.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/AuthTest.php b/test/AuthTest.php index 9399e6e..0f4d8ca 100644 --- a/test/AuthTest.php +++ b/test/AuthTest.php @@ -95,7 +95,7 @@ else { } if (empty($token)) { - print "Authentication seemed to succeed, but no token was return." . PHP_EOL; + print "Authentication seemed to succeed, but no token was returned." . PHP_EOL; exit(1); } diff --git a/test/example.settings.ini b/test/example.settings.ini index 4c17c43..344b992 100644 --- a/test/example.settings.ini +++ b/test/example.settings.ini @@ -28,7 +28,7 @@ hpcloud.identity.password = ; For authentication by account ID. hpcloud.identity.account = -hpcloud.identity.key = +hpcloud.identity.secret = ;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Configuration Parameters ; From dc59b72c7dad1810c8e51791dee7a72673d286ea Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Fri, 4 May 2012 17:24:39 -0500 Subject: [PATCH 2/3] Support for SSL CDN URLs. SSL CDN URLs are now the default, too. The assumption is that security should be favored over speed by default. --- src/HPCloud/Storage/CDN.php | 2 ++ src/HPCloud/Storage/ObjectStorage.php | 19 ++++++++--- .../Storage/ObjectStorage/Container.php | 14 +++++--- .../Storage/ObjectStorage/RemoteObject.php | 20 ++++++++--- .../Storage/ObjectStorage/StreamWrapper.php | 5 +-- test/CDNTest.php | 34 +++++++++++++++++-- 6 files changed, 76 insertions(+), 18 deletions(-) diff --git a/src/HPCloud/Storage/CDN.php b/src/HPCloud/Storage/CDN.php index 9786722..cf1f17c 100644 --- a/src/HPCloud/Storage/CDN.php +++ b/src/HPCloud/Storage/CDN.php @@ -260,6 +260,7 @@ class CDN { * 'cdn_enabled' => 1 * 'name' => 'I♡HPCloud' * 'x-cdn-uri' => 'http://hcf937838.cdn.aw1.hpcloud.net' + * 'x-cdn-ssl-uri' => 'https://hcf937838.cdn.aw1.hpcloud.net' * 'ttl' => 1234 * ), * array( @@ -267,6 +268,7 @@ class CDN { * 'cdn_enabled' => 0 * 'name' => 'HPCloud2' * 'x-cdn-uri' => 'http://hcf9abc38.cdn.aw1.hpcloud.net' + * 'x-cdn-ssl-uri' => 'https://hcf937838.cdn.aw1.hpcloud.net' * 'ttl' => 1234 * ), * ); diff --git a/src/HPCloud/Storage/ObjectStorage.php b/src/HPCloud/Storage/ObjectStorage.php index dd1b760..5ede820 100644 --- a/src/HPCloud/Storage/ObjectStorage.php +++ b/src/HPCloud/Storage/ObjectStorage.php @@ -236,7 +236,10 @@ class ObjectStorage { // This is needed b/c of a bug in SOS that sometimes // returns disabled containers (DEVEX-1733). if ($item['cdn_enabled'] == 1) { - $buffer[$item['name']] = $item['x-cdn-uri']; + $buffer[$item['name']] = array( + 'url' => $item['x-cdn-uri'], + 'sslUrl' => $item['x-cdn-ssl-uri'], + ); } } $this->cdnContainers = $buffer; @@ -254,13 +257,17 @@ class ObjectStorage { * * @param string $containerName * The name of the container. + * @param boolean $ssl + * If this is TRUE (default), get the URL to the SSL CDN; + * otherwise get the URL to the plain HTTP CDN. * @retval string * The URL to the CDN container, or NULL if no such * URL is found. */ - public function cdnUrl($containerName) { + public function cdnUrl($containerName, $ssl = TRUE) { if (!empty($this->cdnContainers[$containerName])) { - return $this->cdnContainers[$containerName]; + $key = $ssl ? 'sslUrl' : 'url'; + return $this->cdnContainers[$containerName][$key]; } } @@ -339,7 +346,8 @@ class ObjectStorage { $containerList[$cname] = Container::newFromJSON($container, $this->token(), $this->url()); if (!empty($this->cdnContainers[$cname])) { - $containerList[$cname]->useCDN($this->cdnContainers[$cname]); + $cdnList = $this->cdnContainers[$cname]; + $containerList[$cname]->useCDN($cdnList['url'], $cdnList['sslUrl']); } } @@ -368,7 +376,8 @@ class ObjectStorage { $container = Container::newFromResponse($name, $data, $this->token(), $this->url()); if (isset($this->cdnContainers[$name])) { - $container->useCDN($this->cdnContainers[$name]); + $cdnList = $this->cdnContainers[$name]; + $container->useCDN($cdnList['url'], $cdnList['sslUrl']); } return $container; diff --git a/src/HPCloud/Storage/ObjectStorage/Container.php b/src/HPCloud/Storage/ObjectStorage/Container.php index 3769a81..6e6c9cd 100644 --- a/src/HPCloud/Storage/ObjectStorage/Container.php +++ b/src/HPCloud/Storage/ObjectStorage/Container.php @@ -95,6 +95,7 @@ class Container implements \Countable, \IteratorAggregate { // This is only set if CDN service is activated. protected $cdnUrl; + protected $cdnSslUrl; /** * Transform a metadata array into headers. @@ -336,8 +337,9 @@ class Container implements \Countable, \IteratorAggregate { * @param string $url * The URL to the CDN for this container. */ - public function useCDN($url) { + public function useCDN($url, $sslUrl) { $this->cdnUrl = $url; + $this->cdnSslUrl = $sslUrl; } /** @@ -688,6 +690,7 @@ class Container implements \Countable, \IteratorAggregate { $url = self::objectUrl($this->url, $name); $cdn = self::objectUrl($this->cdnUrl, $name); + $cdnSsl = self::objectUrl($this->cdnSslUrl, $name); $headers = array(); // Auth token. @@ -710,7 +713,7 @@ class Container implements \Countable, \IteratorAggregate { $remoteObject->setContent($response->content()); if (!empty($this->cdnUrl)) { - $remoteObject->useCDN($cdn); + $remoteObject->useCDN($cdn, $cdnSsl); } return $remoteObject; @@ -748,6 +751,7 @@ class Container implements \Countable, \IteratorAggregate { public function proxyObject($name) { $url = self::objectUrl($this->url, $name); $cdn = self::objectUrl($this->cdnUrl, $name); + $cdnSsl = self::objectUrl($this->cdnSslUrl, $name); $headers = array( 'X-Auth-Token' => $this->token, ); @@ -771,7 +775,7 @@ class Container implements \Countable, \IteratorAggregate { $obj = RemoteObject::newFromHeaders($name, $headers, $this->token, $url); if (!empty($this->cdnUrl)) { - $obj->useCDN($cdn); + $obj->useCDN($cdn, $cdnSsl); } return $obj; @@ -944,8 +948,8 @@ class Container implements \Countable, \IteratorAggregate { return $this->url; } - public function cdnUrl() { - return $this->cdnUrl; + public function cdnUrl($ssl = TRUE) { + return $ssl ? $this->cdnSslUrl : $this->cdnUrl; } /** diff --git a/src/HPCloud/Storage/ObjectStorage/RemoteObject.php b/src/HPCloud/Storage/ObjectStorage/RemoteObject.php index cfea15d..bda1543 100644 --- a/src/HPCloud/Storage/ObjectStorage/RemoteObject.php +++ b/src/HPCloud/Storage/ObjectStorage/RemoteObject.php @@ -68,6 +68,7 @@ class RemoteObject extends Object { protected $allHeaders; protected $cdnUrl; + protected $cdnSslUrl; /** * Create a new RemoteObject from JSON data. @@ -118,8 +119,10 @@ class RemoteObject extends Object { * subsequent requests. If this is set, this object may use * CDN to make subsequent requests. It may also return the * CDN URL when requested. + * @param string $cdnSslUrl + * The URL to the SSL-protected CDN version of the object. */ - public static function newFromHeaders($name, $headers, $token, $url, $cdnUrl = NULL) { + public static function newFromHeaders($name, $headers, $token, $url, $cdnUrl = NULL, $cdnSslUrl = NULL) { $object = new RemoteObject($name); $object->allHeaders = $headers; @@ -153,6 +156,7 @@ class RemoteObject extends Object { $object->token = $token; $object->url = $url; $object->cdnUrl = $cdnUrl; + $object->cdnSslUrl = $cdnSslUrl; return $object; } @@ -176,9 +180,12 @@ class RemoteObject extends Object { * * @param string $url * The URL to this object in CDN. + * @param string $sslUrl + * The SSL URL to this object in CDN. */ - public function useCDN($url) { + public function useCDN($url, $sslUrl) { $this->cdnUrl = $url; + $this->cdnSslUrl = $sslUrl; } /** @@ -196,6 +203,11 @@ class RemoteObject extends Object { * object. See ObjectStorage::useCDN(), Container::useCDN() and * RemoteObject::useCDN(). (Generally, using ObjectStorage::useCDN() * is all you need to do.) + * @param boolean $useSSL + * FOR CACHED URLS ONLY, there is an option for either SSL or non-SSL + * URLs. By default, we use SSL URLs because (a) it's safer, and + * (b) it mirrors non-CDN behavior. This can be turned off by setting + * $useSSL to FALSE. * @retval string * A URL to the object. The following considerations apply: * - If the container is public, this URL can be loaded without @@ -207,10 +219,10 @@ class RemoteObject extends Object { * - If this object has never been saved remotely, then there will be * no URL, and this will return NULL. */ - public function url($cached = FALSE) { + public function url($cached = FALSE, $useSSL = TRUE) { if ($cached && !empty($this->cdnUrl)) { - return $this->cdnUrl; + return $useSSL ? $this->cdnSslUrl : $this->cdnUrl; } return $this->url; } diff --git a/src/HPCloud/Storage/ObjectStorage/StreamWrapper.php b/src/HPCloud/Storage/ObjectStorage/StreamWrapper.php index 2bce8e2..85e36f0 100644 --- a/src/HPCloud/Storage/ObjectStorage/StreamWrapper.php +++ b/src/HPCloud/Storage/ObjectStorage/StreamWrapper.php @@ -827,13 +827,14 @@ class StreamWrapper { // EXPERIMENTAL: // If we can get the resource from CDN, we do so now. Note that we try to sidestep // the Container creation, which saves us an HTTP request. - $cdnUrl = $this->store->cdnUrl($containerName); + $cdnUrl = $this->store->cdnUrl($containerName, FALSE); + $cdnSslUrl = $this->store->cdnUrl($containerName, TRUE); if (!empty($cdnUrl) && !$this->isWriting && !$this->isAppending) { try { $newUrl = $this->store->url() . '/' . $containerName; $token = $this->store->token(); $this->container = new \HPCloud\Storage\ObjectStorage\Container($containerName, $newUrl, $token); - $this->container->useCDN($cdnUrl); + $this->container->useCDN($cdnUrl, $cdnSslUrl); $this->obj = $this->container->object($objectName); $this->objStream = $this->obj->stream(); diff --git a/test/CDNTest.php b/test/CDNTest.php index e9d5ce4..b75c7bb 100755 --- a/test/CDNTest.php +++ b/test/CDNTest.php @@ -66,20 +66,39 @@ print "***** TESTING CDN ENABLED" . PHP_EOL; if ($cdnData['cdn_enabled'] != 1) { die('Cannot test CDN: You must enable CDN on ' . $cname); } - $container = $objstore->container($cname); print "***** TESTING CDN URL" . PHP_EOL; +$cdnSsl = $objstore->cdnUrl($cname); +$cdnPlain = $objstore->cdnUrl($cname, FALSE); +if ($cdnSsl == $cdnPlain) { + die(sprintf("Surprise! %s matches %s\n", $cdnSsl, $cdnPlain)); +} +print 'SSL CDN: ' . $cdnSsl. PHP_EOL; +print 'Plain CDN: ' . $cdnPlain . PHP_EOL; +print 'Container CDN URL: ' . $container->cdnUrl() . PHP_EOL; if ($container->cdnUrl() == NULL) { die('No CDN URL for Container ' . $cname); } +if ($cdnSsl != $container->cdnUrl()) { + die(sprintf("Expected SSL CDN %s to match Container CDN %s\n", $cdnSsl, $container->cdnUrl())); +} + $o = new \HPCloud\Storage\ObjectStorage\Object('CDNTest.txt', 'TEST'); $container->save($o); $copy = $container->object($o->name()); +print "***** TESTING OBJECT CDN URLS." . PHP_EOL; +print "Object SSL URL: " . $copy->url() . PHP_EOL; +print "Object CDN SSL URL: " . $copy->url(TRUE) . PHP_EOL; +print "Object CDN URL: " . $copy->url(TRUE, FALSE) . PHP_EOL; +if ($copy->url(TRUE) == $copy->url(TRUE, FALSE)) { + die(sprintf("Object SSL URL %s should not match non-SSL URL %s\n", $copy->url(TRUE), $copy->url(TRUE, FALSE))); +} + print "***** TESTING THAT CDN WAS USED." . PHP_EOL; if ($copy->url() == $copy->url(TRUE)) { die('Object Storage not used for ' . $o->name()); @@ -98,6 +117,17 @@ $cxt = stream_context_create(array( )); print "***** TESTING RETURNED DATA" . PHP_EOL; -print file_get_contents('swift://' . TEST_CONTAINER . '/CDNTest.txt', FALSE, $cxt); +$res = array( + 'internal' => file_get_contents('swift://' . TEST_CONTAINER . '/CDNTest.txt', FALSE, $cxt), + 'external' => file_get_contents($copy->url()), + 'externalSslCdn' => file_get_contents($copy->url(TRUE)), + 'externalCdn' => file_get_contents($copy->url(TRUE, FALSE)), +); + +foreach ($res as $name => $val) { + if ($val != 'TEST') { + die(sprintf("Facility %s failed, returning '%s' instead of TEST.", $name, $val)); + } +} print PHP_EOL . "***** All tests passed." . PHP_EOL; From e2fdaffb74baca5963e101ca4a66d1674c78392b Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Fri, 4 May 2012 17:58:28 -0500 Subject: [PATCH 3/3] SSL CDN is configurable with Stream Wrappers now. --- src/HPCloud/Storage/ObjectStorage/Container.php | 14 +++++++++++--- .../Storage/ObjectStorage/StreamWrapper.php | 9 ++++++++- test/CDNTest.php | 12 ++++++++++++ 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/HPCloud/Storage/ObjectStorage/Container.php b/src/HPCloud/Storage/ObjectStorage/Container.php index 6e6c9cd..ead371b 100644 --- a/src/HPCloud/Storage/ObjectStorage/Container.php +++ b/src/HPCloud/Storage/ObjectStorage/Container.php @@ -336,6 +336,8 @@ class Container implements \Countable, \IteratorAggregate { * * @param string $url * The URL to the CDN for this container. + * @param string $sslUrl + * The SSL URL to the CDN for this container. */ public function useCDN($url, $sslUrl) { $this->cdnUrl = $url; @@ -683,10 +685,14 @@ class Container implements \Countable, \IteratorAggregate { * * @param string $name * The name of the object to load. + * @param boolean $requireSSL + * If this is TRUE (the default), then SSL will always be + * used. If this is FALSE, then CDN-based fetching will + * use non-SSL, which is faster. * @retval \HPCloud\Storage\ObjectStorage\RemoteObject * A remote object with the content already stored locally. */ - public function object($name) { + public function object($name, $requireSSL = TRUE) { $url = self::objectUrl($this->url, $name); $cdn = self::objectUrl($this->cdnUrl, $name); @@ -702,7 +708,9 @@ class Container implements \Countable, \IteratorAggregate { $response = $client->doRequest($url, 'GET', $headers); } else { - $response = $client->doRequest($cdn, 'GET', $headers); + $from = $requireSSL ? $cdnSsl : $cdn; + // print "Fetching object from $from\n"; + $response = $client->doRequest($from, 'GET', $headers); } if ($response->status() != 200) { @@ -763,7 +771,7 @@ class Container implements \Countable, \IteratorAggregate { $response = $client->doRequest($url, 'HEAD', $headers); } else { - $response = $client->doRequest($cdn, 'HEAD', $headers); + $response = $client->doRequest($cdnSsl, 'HEAD', $headers); } if ($response->status() != 200) { diff --git a/src/HPCloud/Storage/ObjectStorage/StreamWrapper.php b/src/HPCloud/Storage/ObjectStorage/StreamWrapper.php index 85e36f0..b6d5760 100644 --- a/src/HPCloud/Storage/ObjectStorage/StreamWrapper.php +++ b/src/HPCloud/Storage/ObjectStorage/StreamWrapper.php @@ -248,6 +248,9 @@ use \HPCloud\Storage\ObjectStorage; * - The container must have CDN enabled * - The CDN container must be active ("cdn-enabled") * - Authentication info must be accessible to the stream wrapper. + * - cdn_require_ssl: If this is set to FALSE, then CDN-based requests + * may use plain HTTP instead of HTTPS. This will spead up CDN + * fetches at the cost of security. * * @attention * ADVANCED: You can also pass an HPCloud::Storage::CDN object in use_cdn instead of @@ -830,12 +833,13 @@ class StreamWrapper { $cdnUrl = $this->store->cdnUrl($containerName, FALSE); $cdnSslUrl = $this->store->cdnUrl($containerName, TRUE); if (!empty($cdnUrl) && !$this->isWriting && !$this->isAppending) { + $requireSSL = (boolean) $this->cxt('cdn_require_ssl', TRUE); try { $newUrl = $this->store->url() . '/' . $containerName; $token = $this->store->token(); $this->container = new \HPCloud\Storage\ObjectStorage\Container($containerName, $newUrl, $token); $this->container->useCDN($cdnUrl, $cdnSslUrl); - $this->obj = $this->container->object($objectName); + $this->obj = $this->container->object($objectName, $requireSSL); $this->objStream = $this->obj->stream(); return TRUE; @@ -1570,6 +1574,9 @@ class StreamWrapper { * When use_cdn is set to TRUE, the wrapper tries to use CDN service. * In such cases, we need a handle to the CDN object. This initializes * that handle, which can later be used to get other information. + * + * Also note that CDN's default behavior is to fetch over SSL CDN. + * To disable this, set 'cdn_require_ssl' to FALSE. */ protected function initializeCDN($token, $catalog) { $cdn = $this->cxt('use_cdn', FALSE); diff --git a/test/CDNTest.php b/test/CDNTest.php index b75c7bb..2d7a18c 100755 --- a/test/CDNTest.php +++ b/test/CDNTest.php @@ -115,10 +115,22 @@ $cxt = stream_context_create(array( 'use_cdn' => TRUE, ), )); +$cxt2 = stream_context_create(array( + 'swift' => array( + //'token' => $token, + 'tenantid' => $ini['hpcloud.identity.tenantId'], + 'account' => $ini['hpcloud.identity.account'], + 'key' => $ini['hpcloud.identity.secret'], + 'endpoint' => $ini['hpcloud.identity.url'], + 'use_cdn' => TRUE, + 'cdn_require_ssl' => FALSE, + ), +)); print "***** TESTING RETURNED DATA" . PHP_EOL; $res = array( 'internal' => file_get_contents('swift://' . TEST_CONTAINER . '/CDNTest.txt', FALSE, $cxt), + 'internalNoSSL' => file_get_contents('swift://' . TEST_CONTAINER . '/CDNTest.txt', FALSE, $cxt2), 'external' => file_get_contents($copy->url()), 'externalSslCdn' => file_get_contents($copy->url(TRUE)), 'externalCdn' => file_get_contents($copy->url(TRUE, FALSE)),