HPCloud-PHP  1.2.0
PHP bindings for HPCloud and OpenStack services.
 All Classes Namespaces Files Functions Variables Pages
CDN.php
Go to the documentation of this file.
1 <?php
2 /* ============================================================================
3 (c) Copyright 2012 Hewlett-Packard Development Company, L.P.
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights to
7 use, copy, modify, merge,publish, distribute, sublicense, and/or sell copies of
8 the Software, and to permit persons to whom the Software is furnished to do so,
9 subject to the following conditions:
10 
11 The above copyright notice and this permission notice shall be included in all
12 copies or substantial portions of the Software.
13 
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 SOFTWARE.
21 ============================================================================ */
22 /**
23  * @file
24  *
25  * This file contains the CDN (Content Distribution Network) class.
26  */
27 
28 namespace HPCloud\Storage;
29 
30 /**
31  * Provides CDN services for ObjectStorage.
32  *
33  * CDN stands for "Content Distribution Network." It provides distributed
34  * caching in the cloud. When a Container is CDN-enabled, objects will be
35  * stored not @em just in the ObjectStorage, but temporary cached copies
36  * will be stored on servers around the world.
37  *
38  * @attention
39  * Caches are not protected by authentication. If you store an object in
40  * a CDN, cached versions of that object are publically accessible. Setting
41  * an ACL will have very little impact on this.
42  *
43  * CDN is an HPCloud extension service, and is not presently part of OpenStack
44  * proper. The current REST API documentation can be found at
45  * http://api-docs.hpcloud.com/
46  *
47  * <b>Usage</b>
48  *
49  * The CDN service functions as an <i>add-on</i> to ObjectStorage. It adds a
50  * caching layer. So terms used here, such as Container and Object, refer
51  * to the ObjectStorage items.
52  *
53  * For the most part, CDN operates on the Container level. You can choose to
54  * tell the CDN about a particular Container in ObjectStorage, and it will
55  * cache items in that container.
56  *
57  * The CDN service keeps a list of ObjectStorage Containers that it knows
58  * about. CDN does not automatically discover ObjectStorage Containers; you
59  * must tell CDN about the ObjectStorage instances you want it to know
60  * about. This is done using CDN::enable().
61  *
62  * Once the CDN service knows about an ObjectStorage Container, it will
63  * begin caching objects in that container.
64  *
65  * This library gives the the ability to do the following:
66  *
67  * - List the containers that CDN knows about: CDN::containers()
68  * - Retrieve the CDN properties for a particular Container: CDN::container().
69  * - Add and enable containers: CDN::enable().
70  * - Remove a Container from CDN: CDN::delete().
71  * - Modify the caching properties of a Container: CDN::update().
72  * - Temporarily enable or disable caching for a container with
73  * CDN::update().
74  *
75  * <b>Example</b>
76  *
77  * @code
78  * <?php
79  * // Authentication info:
80  * $endpoint = 'https://auth.example.com';
81  * $username = 'butcher@hp.com';
82  * $password = 'secret';
83  * $tenantId = '123456789';
84  *
85  * // First we need to authenticate:
86  * $identity = new \HPCloud\Services\IdentityServices($endpoint);
87  * $token = $identity->authenticateAsUser($username, $password, $tenantId);
88  *
89  * // Get the service catalog. We will try to have CDN build itself from
90  * // the service catalog.
91  * $catalog = $identity->serviceCatalog();
92  *
93  * // Get a new CDN instance:
94  * $cdn = CDN::newFromServiceCatalog($catalog, $token);
95  *
96  * // Add a container to CDN; set cache lifetime to an hour:
97  * $cdn->enable('myContainer', 3600);
98  *
99  * // Get a list of all containers that CDN knows about,
100  * // and print cache lifetime for each:
101  * foreach ($cdn->containers() as $container) {
102  * print $container['name'] . ':' . $container['ttl'] . PHP_EOL;
103  * }
104  *
105  * // Change the cache lifetime on our container
106  * $cdn->update('myContainer', array('ttl' => 7200));
107  *
108  * // Temporarily stop the container from caching:
109  * $cdn->update('myContainer', array('cdn_enabled' => FALSE);
110  *
111  * //This can be re-enabled again:
112  * $cdn->update('myContainer', array('cdn_enabled' => TRUE);
113  *
114  * // If we no longer want this Container in CDN, we
115  * // should delete it, not just disable it:
116  * $cdn->delete('myContainer');
117  *
118  * ?>
119  * @endcode
120  */
121 class CDN {
122 
123  /**
124  * The name of the CDN service type.
125  */
126  const SERVICE_TYPE = 'hpext:cdn';
127  /**
128  * The API version.
129  */
130  const API_VERSION = '1.0';
131 
132  const DEFAULT_REGION = 'region-a.geo-1';
133 
134  /**
135  * The URL to the CDN endpoint.
136  */
137  protected $url;
138  /**
139  * The authentication/authorization token.
140  */
141  protected $token;
142 
143  /**
144  * Create a new instance from an IdentityServices object.
145  *
146  * This builds a new CDN instance form an authenticated
147  * IdentityServices object.
148  *
149  * In the service catalog, this selects the first service entry
150  * for CDN. At this time, that is sufficient.
151  *
152  * @param HPCloud::Services::IdentityServices $identity
153  * The identity to use.
154  * @retval boolean
155  * @retval HPCloud::Storage::CDN
156  * @return \HPCloud\Storage\CDN|boolean
157  * A CDN object or FALSE if no CDN services could be found
158  * in the catalog.
159  */
160  public static function newFromIdentity($identity, $region = CDN::DEFAULT_REGION) {
161  $tok = $identity->token();
162  $cat = $identity->serviceCatalog();
163 
164  return self::newFromServiceCatalog($cat, $tok);
165  }
166 
167  /**
168  * Create a new CDN object based on a service catalog.
169  *
170  * The IdentityServices class contains a service catalog, which tracks all
171  * services that the present account can access. The service catalog
172  * contains data necessary to connect to a CDN endpoint. This builder
173  * simplifies the process of creating a new CDN by accepting a service
174  * catalog and discovering the CDN service automatically.
175  *
176  * In the vast majority of cases, this is the easiest way to proceed. If,
177  * however, a service catalog has multiple CDN instances (a possibility,
178  * though not currently supported), the present method has no means of
179  * determining which should be used. It simply chooses the first CDN
180  * service endpoint.
181  *
182  * This uses the tenant ID that is found in the service catalog.
183  *
184  * Either of the following work:
185  * @code
186  * <?php
187  *
188  * // Use a full service catalog:
189  * $fullCatalog = $identityService->serviceCatalog();
190  * $cdn = CDN::newFromServiceCatalog($fullCatalog);
191  *
192  * // Use a filtered service catalog:
193  * $catalog = $identitySerice->serviceCatalog(CDN::SERVICE_TYPE);
194  * $cdn = CDN::newFromServiceCatalog($catalog);
195  * ?>
196  * @endcode
197  *
198  * @param array $catalog
199  * A service catalog; see HPCloud::Services::IdentityServices::serviceCatalog().
200  * @param string $token
201  * The token.
202  * @retval boolean
203  * @retval HPCloud::Storage::CDN
204  * @return boolean|\HPCloud\Storage\CDN
205  * A CDN object or FALSE if no CDN services could be found
206  * in the catalog.
207  */
208  public static function newFromServiceCatalog($catalog, $token, $region = CDN::DEFAULT_REGION) {
209  $c = count($catalog);
210  for ($i = 0; $i < $c; ++$i) {
211  if ($catalog[$i]['type'] == self::SERVICE_TYPE) {
212  foreach ($catalog[$i]['endpoints'] as $endpoint) {
213  if (isset($endpoint['publicURL']) && $endpoint['region'] == $region) {
214  /*
215  $parts = parse_url($endpoint['publicURL']);
216  $base = $parts['scheme'] . '://' . $parts['host'];
217  if (isset($parts['port'])) {
218  $base .= ':' . $parts['port'];
219  }
220  //$base = $endpoint['publicURL'];
221  $cdn = new CDN($token, $base, $endpoint['tenantId']);
222  //$cdn->url = $endpoint['publicURL'];
223  */
224  $cdn = new CDN($token, $endpoint['publicURL']);
225 
226  return $cdn;
227  }
228  }
229  }
230  }
231  return FALSE;
232  }
233 
234  /**
235  * Build a new CDN object.
236  *
237  * This object facilitates communication with the CDN cloud service.
238  *
239  * This creates a new CDN object that will view as its endpoint the server
240  * with the URL $endpoint, which has the form:
241  *
242  * @code
243  * https://ENDPOINT/API_VERSION/ACCOUNT
244  * @endcode
245  *
246  *
247  * On older SwiftAuth-based services, the token should be the swauth token.
248  * On newer releaes, the token is retrieved from IdentityServices.
249  *
250  * @param string $endpoint
251  * The URL of the CDN service. It should look something like this:
252  * @c https://cdnmgmt.rndd.aw1.hpcloud.net/v1.0/72020596871800
253  * @param string $token
254  * The authentication token. This can be retrieved from IdentityServices::token().
255  */
256  public function __construct($token, $endpoint/*, $account*/) {
257  //$this->url = $endpoint . '/v' . self::API_VERSION . '/' . $account;
258  $this->url = $endpoint;
259  $this->token = $token;
260  }
261 
262  /**
263  * Get a list of containers that the CDN system knows of.
264  *
265  * This returns a list of ObjectStorage Containers that the
266  * CDN service knows of. These containers can be either enabled or
267  * disabled.
268  *
269  * The CDN service does not attempt to discover all of the containers
270  * from a Swift endpoint. Instead, it passively acquires a list of
271  * containers (added via, for example, enabledContainer()).
272  *
273  * Once a container has been added to the CDN service, it can be in
274  * one of two states:
275  *
276  * - enabled (\c cdn_enabled=TRUE)
277  * - disabled (\c cdn_enabled=FALSE)
278  *
279  * This listing will retrieve both enabled and disabled unless
280  * $enabledOnly is set to TRUE.
281  *
282  * Returned data is in this format:
283  * @code
284  * <?php
285  * array(
286  * array(
287  * 'log_retention' => 0
288  * 'cdn_enabled' => 1
289  * 'name' => 'I♡HPCloud'
290  * 'x-cdn-uri' => 'http://hcf937838.cdn.aw1.hpcloud.net'
291  * 'x-cdn-ssl-uri' => 'https://hcf937838.cdn.aw1.hpcloud.net'
292  * 'ttl' => 1234
293  * ),
294  * array(
295  * 'log_retention' => 0
296  * 'cdn_enabled' => 0
297  * 'name' => 'HPCloud2'
298  * 'x-cdn-uri' => 'http://hcf9abc38.cdn.aw1.hpcloud.net'
299  * 'x-cdn-ssl-uri' => 'https://hcf937838.cdn.aw1.hpcloud.net'
300  * 'ttl' => 1234
301  * ),
302  * );
303  * ?>
304  * @endcode
305  *
306  * @attention
307  * The $enabledOnly flag sendes \c enabled_only to the
308  * endpoint. The endpoint may or may not honor this.
309  *
310  * @param boolean $enabledOnly
311  * If this is set to TRUE, then only containers that are
312  * CDN-enabled will be returned.
313  * @retval array
314  * @return array
315  * An indexed array of associative arrays. The format of each
316  * associative array is explained on container().
317  * @throws HPCloud::Exception
318  * An HTTP-level exception on error.
319  */
320  public function containers($enabledOnly = NULL) {
321  $client = \HPCloud\Transport::instance();
322  $url = $this->url . '/?format=json';
323 
324  if ($enabledOnly) {
325  $url .= '&enabled_only=true';
326  }
327  // DEVEX-1733 suggests that this should result in the
328  // server listing only DISABLED containers.
329  elseif ($enabledOnly === FALSE) {
330  $url .= '&enabled_only=false';
331  }
332 
333  $headers = array(
334  'X-Auth-Token' => $this->token,
335  );
336 
337  $response = $client->doRequest($url, 'GET', $headers);
338 
339  $raw = $response->content();
340  $json = json_decode($raw, TRUE);
341 
342  return $json;
343  }
344 
345  /**
346  * Get a container by name.
347  *
348  * @todo The current (1.0) version does not support a verb for getting
349  * just one container, so we have to get the entire list of containers.
350  *
351  * Example return value:
352  * @code
353  * <?php
354  * array(
355  * 'log_retention' => 1
356  * 'cdn_enabled' => 1
357  * 'name' => 'I♡HPCloud'
358  * 'x-cdn-uri' => 'http://hcf937838.cdn.aw1.hpcloud.net'
359  * 'ttl' => 1234
360  * );
361  * ?>
362  * @endcode
363  *
364  * @param string $name
365  * The name of the container to fetch.
366  * @retval array
367  * @return array
368  * An associative array in the exact format as in containers.
369  */
370  public function container($name) {
371  //$result = $this->modifyContainer($name, 'GET', array(), '?format=json');
372 
373  $containers = $this->containers();
374  foreach ($containers as $container) {
375  if ($container['name'] == $name) {
376  return $container;
377  }
378  }
379  return FALSE;
380  }
381 
382  /**
383  * Enable a container.
384  *
385  * This adds the container to the CDN service and turns on caching.
386  *
387  * In the CDN API, there are two meanings for the term "enable":
388  *
389  * 1. To "CDN-enable" a container means to add that container to the CDN
390  * service. There is no "CDN-disable".
391  * 2. To "enable" a container means to cache that container's
392  * content in a publically available CDN server. There is also a
393  * way to "disable" in this sense -- which blocks a container from
394  * caching.
395  *
396  * This method does the first -- it adds a container to the CDN
397  * service. It so happens that adding a container also enables (in the
398  * second sense) the
399  * container. (This is a feature of the remote service, not the API).
400  *
401  * Enabling and disabling (in the second sense) are considered temporary operations
402  * to switch on and off caching on a particular container. Both of
403  * these operations are done with the update() method.
404  *
405  * The endpoint is supposed to return different results based on the above;
406  * accordingly this method should return TRUE if the container was added
407  * to the list, and FALSE if it was already there. HOWEVER, in some versions
408  * of the CDN service the endpoint returns the same code for both operations,
409  * so the result cannot be relied upon.
410  *
411  * @param string $name
412  * The name of the container.
413  * @param int $ttl
414  * Time to live.
415  * The number of seconds an object may stay in the cache. This is the
416  * maximum amount of time. There is, however, no assurance that the object
417  * will remain for the full TTL. 15 minutes is the minimum time. Five years
418  * is the max.
419  * @param boolean $created
420  * If this is passed, then its value will be set to TRUE if the
421  * container was created in the CDN, or FALSE if the container
422  * already existed in CDN.
423  * @retval string
424  * @return string
425  * TRUE if the container was created, FALSE if the container was already
426  * added to the CDN (and thus nothing happened).
427  * @throws HPCloud::Exception
428  * Several HTTP-level exceptions can be thrown.
429  * @see http://api-docs.hpcloud.com/hpcloud-cdn-storage/1.0/content/cdn-enable-container.html
430  */
431  public function enable($name, $ttl = NULL, &$created = FALSE) {
432  $headers = array();
433  if (!empty($ttl)) {
434  $headers['X-TTL'] = (int) $ttl;
435  }
436  $res = $this->modifyContainer($name, 'PUT', $headers);
437  $created = $res->status() == 201;
438 
439  $url = $res->header('X-Cdn-Uri', 'UNKNOWN');
440  return $url;
441  }
442 
443  /**
444  * Set attributes on a CDN container.
445  *
446  * This updates the attributes (that is, properties) of a container.
447  *
448  * The following attributes are supported:
449  *
450  * - 'ttl': Time to life in seconds (int).
451  * - 'cdn_enabled': Whether the CDN is enabled (boolean).
452  * - 'log_retention': Whether logs are retained (boolean). UNSUPPORTED.
453  *
454  * Future versions of the CDN service will likely provide other
455  * properties.
456  *
457  * @param string $name
458  * The name of the container.
459  * @param array $attrs
460  * An associative array of attributes.
461  * @retval boolean
462  * @return boolean
463  * TRUE if the update was successful.
464  * @throws HPCloud::Exception
465  * Possibly throws one of the HTTP exceptions.
466  */
467  public function update($name, $attrs) {
468 
469  $headers = array();
470  foreach ($attrs as $item => $val) {
471  switch ($item) {
472  case 'ttl':
473  $headers['X-TTL'] = (int) $val;
474  break;
475  case 'enabled':
476  case 'cdn_enabled':
477  if (isset($val) && $val == FALSE) {
478  $flag = 'False';
479  }
480  // Default is TRUE.
481  else {
482  $flag = 'True';
483  }
484  $headers['X-CDN-Enabled'] = $flag;
485  break;
486  case 'logs':
487  case 'log_retention':
488  // The default is TRUE.
489  if (isset($val) && $val == FALSE) {
490  $flag = 'False';
491  }
492  else {
493  $flag = 'True';
494  }
495  $headers['X-Log-Retention'] = $flag;
496  break;
497  default:
498  $headers[$item] = (string) $val;
499  break;
500  }
501 
502  }
503 
504  $response = $this->modifyContainer($name, 'POST', $headers);
505 
506  return $response->status() == 204;
507  }
508 
509  /*
510  * Temporarily disable CDN for a container.
511  *
512  * This will suspend caching on the named container. It is intended to be a
513  * temporary measure. See delete() for completely removing a container from
514  * CDN service.
515  *
516  * Disabled items will still show up in the list returned by containers(),
517  * and will also be retrievable via container().
518  *
519  * @param string $name
520  * The name of the container whose cache should be suspended.
521  * @retval boolean
522  * @return boolean
523  * TRUE if the container is disabled.
524  * @throws HPCloud::Exception
525  * HTTP exceptions may be thrown if an error occurs.
526  */
527  /*
528  public function disable($name) {
529  $headers = array('X-CDN-Enabled' => 'False');
530  $res = $this->modifyContainer($name, 'POST', $headers);
531  return $res->status() == 204;
532  }
533  */
534 
535  /**
536  * Attempt to remove a container from CDN.
537  *
538  * This will remove a container from CDN services,
539  * completely stopping all caching on that container.
540  *
541  * Deleted containers will no longer show up in the containers()
542  * list, nor will they be accessible via container().
543  *
544  * Deleted containers can be added back with enable().
545  *
546  * @param string $name
547  * The Container name.
548  * @retval boolean
549  * @return boolean
550  * TRUE if the container was successfully deleted,
551  * FALSE if the container was not removed, but no
552  * error occurred.
553  * @throws HPCloud::Exception
554  * Any of the HTTP error subclasses can be thrown.
555  */
556  public function delete($name) {
557  $res = $this->modifyContainer($name, 'DELETE');
558  return $res->status() == 204;
559  }
560 
561  /**
562  * Run the given method on the given container.
563  *
564  * Checks to see if the expected result is returned.
565  *
566  * @param string $name
567  * The name of the container.
568  * @param string $method
569  * The appropriate HTTP verb.
570  * @param int $expects
571  * The expected HTTP code.
572  */
573  protected function modifyContainer($name, $method, $headers = array(), $qstring = '') {
574  $url = $this->url . '/' . rawurlencode($name) . $qstring;
575  $headers['X-Auth-Token'] = $this->token;
576 
577  $client = \HPCloud\Transport::instance();
578  $response = $client->doRequest($url, $method, $headers);
579 
580  return $response;
581  }
582 }