Merge branch 'develop' into feature/swiftfs

This commit is contained in:
Matt Farina
2012-05-10 16:26:59 -04:00
22 changed files with 328 additions and 306 deletions

1
.gitignore vendored
View File

@@ -5,3 +5,4 @@ test/settings.ini*
.DS_Store
doc/api
curl-output.txt
hpcloud-logo.png

View File

@@ -1,156 +0,0 @@
# Using MetaPhing
This short document explains how you, the developer, can use MetaPhing to build a project.
## Getting Started
To create a new package, type:
metaphing MyProject
(where MyProject is the name of your project).
This will create build files, a source code repository tree, and stubs for many important files, including licenses, READMEs and other things. Here's an abbreviated overview of what your directories should look like:
MyProject/
|
|---- bin/ (Tools that are *not* part of your releasable code)
|
|---- data/ (Data to be included in releases)
|
|---- dist/ (Packages that Phing builds for you when you cut a release)
|
|---- doc/ (Documentation you write or PhpDocumentor generates for you)
|
|---- examples/ (Example code that you write)
|
|---- scripts/ (Scripts that *are* part of releasable code. Will be included in releases.)
|
|---- src/ (The PHP code that you write)
|
|---- test/ (Unit tests, functional tests, reports, coverage, etc.)
|
|---- README.md (Your README file for your project's users)
|
|---- COPYING-MIT.txt (The license file. We use MIT by default, but you can use whatever)
|
|---- CREDITS (Credits file in the PEAR2 format. Necessary if you use Pyrus)
|
|---- README.pear (Info that will be bundled into the PEAR archive)
|
|---- RELEASE (Release notes/Changelog. Used to build PEAR packages)
|
|---- API (API changelog. Used to build PEAR packages)
|
|---- build.xml (The Phing build.xml file)
|
|---- project.properties (Your project's configuration properties. EDIT THIS.)
|
|---- .gitignore (A stub containing Git ignore directives.)
|
|---- config.doxy (Doxygen documentation configuration file.)
The first thing to do with your new project is edit the `project.properties` file. This contains many pieces of metadata and information about your new project. This pieces are used to automatically generate packages, documentation, and unit tests.
Once you have this edited, you can run `phing info` to double-check your settings.
You should probably also write a little basic info in `README.md`. This file is a plain text file that allows Markdown formatting. We follow this convention primarily because we use GitHub, and GitHub uses Markdown.
From here, you can begin adding code, writing tests, and doing your thing.
### Compatibility with Vanilla Phing
metaphing uses a number of tasks that are not included with generic Phing. If you do not want to use these tasks, you may want to copy `compatible_build.xml` to `build.xml` and begin from there.
## Source Code
All of your PHP source code should go into the `src/` filter. You may structure your code however you want, using whatever your typical conventions are. Automated tasks attempt to ignore the details of code structure.
## Documentation, Tutorials, and Examples
Documentation goes in `doc/`. We use doxygen to generate documentation. Doxygen should be adequately configured to generate PHP API documentation.
Generated documentation will go in `doc/api`
To write documentation:
* Add inline documentation blocks in your code
* Add narrative documentation (descriptions, references, etc.) in `doc/*.php`
* Put any source code examples in `examples`
If you put code in the `examples/` directory, it will also be included in the documentation output.
## Building and Packaging
When you run the build command (`phing build -Dversion=1.2.3`), Phing will first make a release copy of your code, stored in `bin/build`, and then package that code into the form you requested. By default, it will build a Gzipped Tar archive in the form of a PEAR package.
NOTE:
* We use Pyrus to build packages. Pyrus is available from [pear2.php.net](http://pear2.php.net)
* We use Pirum to register a package with a channel. Pirum is available from [Pirum-Project.org](http://pirum-project.org)
When your code is packaged, the tgz and zip files will be placed in `dist/`. Example:
dist/
|
|---- MyPackage-1.2.4.tgz
|
|---- MyPackage-1.2.4.zip
The TGZ will be a PEAR package.
What does a PEAR package look like? It's a tgz file with the following format:
MyProject-1.2.3.tgz
|
|---- package.xml (the PEAR package file)
|
|---- MyProject-1.2.3
|
|---- src/
| |
| |---- index.php (and all other code)
|
|---- doc/
| |
| |---- index.html (and all other docs)
|
|---- data/
|
|---- examples/
|
|---- test/
In most cases, this is suitable for standard distributions as well as hosting your code as a PEAR package on a PEAR channel server.
(Note: The above may have changed with the new Pyrus system.)
## Testing
All testing material should go in the `test/`.
Unit Tests should go in `test/Tests`.
Coverage and analytic reports will be stored in `tests/reports`.
Other testing data can be included where it is convenient.
## Data
The `data/` directory is for supporting data.
## Things you can delete
You can delete this file, and most README.txt files throughout here. If you are not using Git, you can delete `.gitignore`. If you do not use examples, testing, documentation, data, or scripts, you can delete any or all of those directories. None of them are required. Note, however, that if you delete doc directories, you cannot run `phing doc`. Likewise, if you delete test directories, you cannot run `phing test`.
And feel free to hack on build.xml. Nothing is written in stone.
## About Conventions
The conventions followed when building MetaPhing are derived from two sources:
* Project layout follows the PEAR recommendation (http://pear.php.net).
* Source code follows the Drupal coding standard, which differs only slightly from the PEAR coding standard.
While Zend-style source layouts will work, MetaPhing does not *assume* any of the conventions that typify Zend-style source code.

View File

@@ -6,6 +6,7 @@ You can use this library to:
* Authenticate your application to the HP Cloud.
* Interact with Object Storage (aka Swift).
* Interact with CDN service (Content Delivery Network).
Coming soon:
@@ -21,17 +22,50 @@ Coming soon:
* Enable the cURL extension for full protocol support.
We also have support for using PHP's native HTTP stream wrapper, but it
is not as reliable. We recommend cURL.
## Installation
There are two methods for installing HPCloud-PHP. You may manually
install, or you may use the PEAR installer.
There are currently two methods of installation. We've been considering
PEAR and Phar releases, but have currently limited to only Composer and
builds because these cover our needs.
## Usage
#### Method #1:
### Importing the Library
Use [Composer](http://getcomposer.org) to download and install the
latest version of HPCloud-PHP.
The HPCloud PHP library follows the PHP 5.3 recommended practices for
including and loading. In short: Use an autoloader.
#### Method #2:
Download a tagged release and include it in your project.
## Features
#### Identity Services
Authenticate, authorize service usage, and retrieve account information.
#### Object Storage
Store files or other data objects in containers on your HP Cloud object
storage instance. Create, modify and delete containers. Manage ACLs.
Read, write, and delete objects. Expose objects in your object storage
to other services.
With full stream wrapper support, you can use built-in
PHP functions like `file_get_contents()`, `fopen()`, and `stat()` for
reading and writing files into object storage.
#### CDN
With CDN service enabled, objects in Object Storage can be pushed onto
the HP Cloud edge server network.
With this library, manage CDN integration for object storage containers,
and manage individual objects. The library allows you to fetch cached
objects either from object storage or from the CDN cache.
#### Autoloading
@@ -40,47 +74,14 @@ which means that it should work with any PSR-0 autoloader. However,
it also comes with its own autoloader for apps that don't yet make use
of a standard autoloader.
##### PSR-0 Autoloading
#### Composer Support
For any PSR-0 autoloader, just ensure that the `HPCloud` directory (in
`src`) is available in your PHP include path. A PSR-0 autoloader can
take it from there.
HPCloud-PHP is available as part of the Packagist archive, which means
you can use Composer to automatically download, install, and manage
revisions to HPCloud-PHP from within your project.
#### Using the Built-In Autoloader
We're big fans of [Composer](http://getcomposer.org).
If your project does not include its own autoloader, you can use the one
that comes built-in. This is not a full autoloader. It's a
special-purpose one that works only for the HPCloud source (and this is
by design -- it's supposed to play nicely with other autoloaders).
To use it, you can do the following:
```php
<?php
require_once 'HPCloud/Bootstrap.php';
\HPCloud\Bootstrap::useAutoloader();
?>
```
This will register the autoloader as an SPL autoloader. From here,
HPCloud classes should "just work", with no further `require` statements
necessary.
You can see this in action in `test/TestCase.php`, the base class for
unit tests.
### Authenticating
As the Component Services framework is rolled out, a unified
authentication layer will become available.
Prior to that, however, each service may have its own authentication.
### Working with Object Storage
The central class for Object Storage is, appropriately enough,
`\HPCloud\Storage\ObjectStorage`.
## More information
@@ -90,5 +91,9 @@ storage, and a host of hosted services.
This library provides access to those services.
The best source of documentation is the official API documentation,
which is available at
http://hpcloud.github.com/HPCloud-PHP/doc/api/html/index.html
----
HPCloud-PHP is maintained by HP Cloud Services.
HPCloud-PHP is maintained by the Developer Experience team at HP Cloud Services.

View File

@@ -16,5 +16,8 @@
},
"suggest": {
"curl": ">=7.0.0"
},
"autoload": {
"psr-0": { "HPCloud": "src" }
}
}

View File

@@ -21,15 +21,14 @@
* - account ID and secret key: For cases where you want account-wide
* authentication/authorization.
* - username/password: Typically, this is the same username/password you use
* to access the HPCloud management console.
* to access the HPCloud console.
* - tenant ID: This associates an account or user with a bundle of services.
* You can find this information in your management console.
* You can find this information in your console.
* - endpoint: You will need the URL to the HPCloud endpoint responsible for
* <i>authenticating users</i>. This can be found in your management
* console.
* <i>authenticating users</i>. This can be found in your console.
*
* (If you are not sure what the "HPCloud Management Console" is, head over to
* http://build.hpcloud.com. There you will find some articles and videos
* (If you are not sure what the "HPCloud Console" is, head over to
* http://docs.hpcloud.com. There you will find some articles and videos
* explaining the HPCloud structure.)
*
* @section where_to_start Where To Start
@@ -56,7 +55,7 @@
* screencasts, a knowledge base, and active community forums are
* just a click away.
*
* Head over to http://build.hpcloud.com to find these and other resources.
* Head over to http://docs.hpcloud.com to find these and other resources.
*
* Or maybe you'd just like to see a couple of examples.
*
@@ -79,7 +78,7 @@
*
* // Create a stream context. You can get this
* // information (including tenant ID) from your
* // HPCloud management console.
* // HPCloud console.
* $cxt = stream_context_create(array(
* 'username' => 'matthew.butcher@hp.com',
* 'password' => 'secret',
@@ -128,12 +127,12 @@
*
* // Create a new identity service object, and tell it where to
* // go to authenticate. This URL can be found in your HPCloud
* // management console.
* // console.
* $identity = new IdentityServices('http://get.url.from.hpcloud.com');
*
* // You can authenticate either with username/password (IdentityServices::authenticateAsUser())
* // or as an account/secret key (IdentityServices::authenticateAsAccount()). In either
* // case you can get the info you need from the management console.
* // case you can get the info you need from the console.
* $account = '123456789098765';
* $secret = 'dgasgasd';
* $tenantId = '56545654';
@@ -144,7 +143,7 @@
*
* // Get a listing of all of the services you currently have configured on
* // HPCloud.
* $catalog = $itentity->serviceCatalog();
* $catalog = $identity->serviceCatalog();
*
* var_dump($catalog);
*
@@ -160,7 +159,7 @@
* - HPCloud::Services::IdentityServices::__construct() tells the object where to connect.
* - HPCloud::Services::IdentityServices::authenticateAsUser() lets you log
* in with username and password.
* - HPCloud::Services::IdentityServices::authenticateAsUser() lets you log
* - HPCloud::Services::IdentityServices::authenticateAsAccount() lets you log
* in with account number and secret key.
* - HPCloud::Services::IdentityServices::serviceCatalog() tells you about
* the services you have activated on this account.

View File

@@ -23,7 +23,7 @@ There are two ways to authenticate to Object Storage:
For legacy swift authentication, you will need to use your Account ID
and your secret Key, along with the URL to the Object Storage endpoint.
If you are an existing HP Cloud customer, you can find all of this
information on your management dashboard.
information on your console dashboard.
### Using Stream Wrappers

View File

@@ -65,7 +65,7 @@ look at [the PHP documentation](http://us3.php.net/manual/en/language.namespaces
or you may just prefer to keep on reading and learn by example. We don't
do anything really fancy with namespaces.
**In this document, we sometimes replace the backslash (\) with double
**In this document, we sometimes replace the backslash (\\) with double
colons (`::`) so that links are automatically generated.** So
`\HPCloud\Bootstrap` may appear as HPCloud::Bootstrap. The reason for
this is [explained elsewhere](@ref styleguide).
@@ -151,8 +151,8 @@ pieces of information:
Before you issue a forlorn sigh, envisioning some laborious task, let us
point out that all of this information is available in one place, Log
into [the management console](https://manage.hpcloud.com) and go to the
`API Keys` page. It's all there.
into [the console](https://console.hpcloud.com) and go to the `API Keys`
page. It's all there.
### Identity Services
@@ -164,7 +164,7 @@ network resources. And behind the scenes, account management would be
difficult on the server side.
That's where Identity Services comes in. It is a central service that
handles all things authorization and authentication related. Rouughly,
handles all things authorization and authentication related. Roughly,
it works as follows:
- The client sends an authentication request
@@ -227,7 +227,7 @@ dumping it with `var_dump()`, should you so desire.
At this point, we have what we need from Identity Services. It's time to
look at Object Storage.
### IdentityServices in a Nushell
### IdentityServices in a Nutshell
Instances of HPCloud::Services::IdentityServices are responsible for:
@@ -319,7 +319,7 @@ $container = $store->container('Example');
~~~
Recall that `$store` is the name of our `ObjectStorage` instance. In the
first of the two lines above, we create a new containe named `Example`.
first of the two lines above, we create a new container named `Example`.
Then in the second line, we get that container.
Why is this two steps? The answer is that the HPCloud PHP library mimics
@@ -372,7 +372,7 @@ with a local copy and a remote copy. If our code isn't constructed
correctly, it is possible for these two to get out of sync.
Earlier, we created a container directly on the remote side, and then
featched the container. As we create an object, we are going to do the
fetched the container. As we create an object, we are going to do the
opposite: We will create a local object, and then save it to the remote
storage. Later, we will fetch the remote object.

View File

@@ -92,8 +92,7 @@ of information:
looks something like this: `https://region-a.geo-1.identity.hpcloudsvc.com:35357`
All four of these pieces of information can be found in the **API Keys**
section of your [management console](https://manage.hpcloud.com)
account.
section of your [console](https://console.hpcloud.com) account.
(Note: You can use your username and password instead of account and
key, but you still must supply the tenant ID. Instead of supplying
@@ -153,7 +152,7 @@ The URL above has three important parts, in the form
`swift://CONTAINER/OBJECT_NAME`.
- *swift://*: This is the schema. This part of the URL tells PHP to pass
the request to the HPCloud stream wrapper. (Siwft, by the way, is the
the request to the HPCloud stream wrapper. (Swift, by the way, is the
[OpenStack name for object storage](http://openstack.org/projects/storage/).
- *Example*: This is the *container name*. In Object Storage parlance, a
container is a place to store documents. One account can have lots of

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -75,8 +75,8 @@ namespace HPCloud\Services;
* A list of tenants associated with this account can be obtain programatically
* using the tenants() method on this object.
*
* HPCloud customers can find their tenant ID in the management console along
* with their account ID and secret key.
* HPCloud customers can find their tenant ID in the console along with their
* account ID and secret key.
*
* @b EXAMPLE
*
@@ -311,7 +311,7 @@ class IdentityServices {
* A password string.
* @param string $tenantId
* The tenant ID for this account. This can be obtained through the
* HPCloud management console.
* HPCloud console.
* @throws HPCloud::Transport::AuthorizationException
* If authentication failed.
* @throws HPCloud::Exception
@@ -340,7 +340,7 @@ class IdentityServices {
* used with other HPCloud services, such as Object Storage (aka Swift).
*
* The account ID and access key information can be found in the account
* section of the management console.
* section of the console.
*
* The third paramater allows you to specify a tenant ID. In order to access
* services, this object will need a tenant ID. If none is specified, it can

View File

@@ -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
* ),
* );

View File

@@ -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;

View File

@@ -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.
@@ -335,9 +336,12 @@ 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) {
public function useCDN($url, $sslUrl) {
$this->cdnUrl = $url;
$this->cdnSslUrl = $sslUrl;
}
/**
@@ -681,13 +685,18 @@ 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);
$cdnSsl = self::objectUrl($this->cdnSslUrl, $name);
$headers = array();
// Auth token.
@@ -699,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) {
@@ -710,7 +721,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 +759,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,
);
@@ -759,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) {
@@ -771,7 +783,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 +956,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;
}
/**

View File

@@ -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;
}

View File

@@ -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
@@ -827,14 +830,16 @@ 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) {
$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);
$this->obj = $this->container->object($objectName);
$this->container->useCDN($cdnUrl, $cdnSslUrl);
$this->obj = $this->container->object($objectName, $requireSSL);
$this->objStream = $this->obj->stream();
return TRUE;
@@ -1569,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);

View File

@@ -55,6 +55,26 @@ class CURLTransport implements Transporter {
const HTTP_USER_AGENT_SUFFIX = ' (c93c0a) CURL/1.0';
protected $curlInst = NULL;
/**
* The curl_multi instance.
*
* By using curl_multi to wrap CURL requests, we can re-use the same
* connection for multiple requests. This has tremendous value for
* cases where several transactions occur in short order.
*/
protected $multi = NULL;
/*
public function curl($uri) {
//if (empty($this->curlInst)) {
$this->curlInst = curl_init();
//}
curl_setopt($this->curlInst, CURLOPT_URL, $uri);
return $this->curlInst;
}
*/
public function doRequest($uri, $method = 'GET', $headers = array(), $body = NULL) {
$in = NULL;
@@ -109,49 +129,6 @@ class CURLTransport implements Transporter {
//syslog(LOG_WARNING, "Real Operation: $method $uri");
//$urlParts = parse_url($uri);
// Write to in-mem handle backed by a temp file.
$out = fopen('php://temp', 'wb+');
$headerFile = fopen('php://temp', 'w+');
$curl = curl_init($uri);
// Set method
$this->determineMethod($curl, $method);
// Set the upload
$copy = NULL;
// If we get a string, we send the string
// data.
if (is_string($in)) {
curl_setopt($curl, CURLOPT_POSTFIELDS, $in);
if (!isset($headers['Content-Length'])) {
$headers['Content-Length'] = strlen($in);
}
}
// If we get a resource, we treat it like a stream
// and pass it into CURL as a file.
elseif (is_resource($in)) {
curl_setopt($curl, CURLOPT_INFILE, $in);
// Tell CURL about the content length if we know it.
if (!empty($headers['Content-Length'])) {
curl_setopt($curl, CURLOPT_INFILESIZE, $headers['Content-Length']);
unset($headers['Content-Length']);
}
}
// Set headers.
$this->setHeaders($curl, $headers);
// Get the output.
curl_setopt($curl, CURLOPT_FILE, $out);
// We need to capture the headers, too.
curl_setopt($curl, CURLOPT_WRITEHEADER, $headerFile);
$opts = array(
CURLOPT_USERAGENT => self::HTTP_USER_AGENT . self::HTTP_USER_AGENT_SUFFIX,
// CURLOPT_RETURNTRANSFER => TRUE, // Make curl_exec return the results.
@@ -170,25 +147,82 @@ class CURLTransport implements Transporter {
// Limit curl to only these protos.
// CURLOPT_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS,
// (Re-)set defaults.
//CURLOPT_POSTFIELDS => NULL,
//CURLOPT_INFILE => NULL,
//CURLOPT_INFILESIZE => NULL,
);
curl_setopt_array($curl, $opts);
// Write to in-mem handle backed by a temp file.
$out = fopen('php://temp', 'wb+');
$headerFile = fopen('php://temp', 'w+');
$curl = curl_init($uri);
//$curl = $this->curl($uri);
// Set method
$this->determineMethod($curl, $method);
// Set the upload
$copy = NULL;
// If we get a string, we send the string
// data.
if (is_string($in)) {
//curl_setopt($curl, CURLOPT_POSTFIELDS, $in);
$opts[CURLOPT_POSTFIELDS] = $in;
if (!isset($headers['Content-Length'])) {
$headers['Content-Length'] = strlen($in);
}
}
// If we get a resource, we treat it like a stream
// and pass it into CURL as a file.
elseif (is_resource($in)) {
//curl_setopt($curl, CURLOPT_INFILE, $in);
$opts[CURLOPT_INFILE] = $in;
// Tell CURL about the content length if we know it.
if (!empty($headers['Content-Length'])) {
//curl_setopt($curl, CURLOPT_INFILESIZE, $headers['Content-Length']);
$opts[CURLOPT_INFILESIZE] = $headers['Content-Length'];
unset($headers['Content-Length']);
}
}
// Set headers.
$this->setHeaders($curl, $headers);
// Get the output.
//curl_setopt($curl, CURLOPT_FILE, $out);
$opts[CURLOPT_FILE] = $out;
// We need to capture the headers, too.
//curl_setopt($curl, CURLOPT_WRITEHEADER, $headerFile);
$opts[CURLOPT_WRITEHEADER] = $headerFile;
if (Bootstrap::hasConfig('transport.debug')) {
$debug = Bootstrap::config('transport.debug', NULL);
curl_setopt($curl, CURLOPT_VERBOSE, (int) $debug);
//curl_setopt($curl, CURLOPT_VERBOSE, (int) $debug);
$opts[CURLOPT_VERBOSE] = (int) $debug;
}
if (Bootstrap::hasConfig('transport.timeout')) {
curl_setopt($curl, CURLOPT_TIMEOUT, (int) Bootstrap::config('transport.timeout'));
//curl_setopt($curl, CURLOPT_TIMEOUT, (int) Bootstrap::config('transport.timeout'));
$opts[CURLOPT_TIMEOUT] = (int) Bootstrap::config('transport.timeout');
}
if (Bootstrap::hasConfig('transport.ssl.verify')) {
$validate = (boolean) Bootstrap::config('transport.ssl.verify', TRUE);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $validate);
//curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $validate);
$opts[CURLOPT_SSL_VERIFYPEER] = $validate;
}
$ret = curl_exec($curl);
// Set all of the curl opts and then execute.
curl_setopt_array($curl, $opts);
$ret = $this->execCurl($curl);//curl_exec($curl);
$info = curl_getinfo($curl);
$status = $info['http_code'];
@@ -213,7 +247,7 @@ class CURLTransport implements Transporter {
// Now we need to build a response.
$resp = new Response($out, $info, $responseHeaders);
curl_close($curl);
//curl_close($curl);
if (is_resource($copy)) {
fclose($copy);
}
@@ -221,6 +255,57 @@ class CURLTransport implements Transporter {
return $resp;
}
/**
* Poor man's connection pooling.
*
* Instead of using curl_exec(), we use curl_multi_* to
* handle the processing The CURL multi library tracks connections, and
* basically provides connection sharing across requests. So two requests made to
* the same server will use the same connection (even when they are executed
* separately) assuming that the remote server supports this.
*
* We've noticed that this improves performance substantially, especially since
* SSL requests only require the SSL handshake once.
*
* @param resource $handle
* A CURL handle from curl_init().
* @retval boolean
* Returns a boolean value indicating whether or not CURL could process the
* request.
*/
protected function execCurl($handle) {
if (empty($this->multi)) {
$multi = curl_multi_init();
$this->multi = $multi;
// fwrite(STDOUT, "Creating MULTI handle.\n");
}
else {
// fwrite(STDOUT, "Reusing MULTI handle.\n");
$multi = $this->multi;
}
curl_multi_add_handle($multi, $handle);
$active = NULL;
do {
$ret = curl_multi_exec($multi, $active);
} while ($ret == CURLM_CALL_MULTI_PERFORM);
while ($active && $ret == CURLM_OK) {
if (curl_multi_select($multi) != -1) {
do {
$mrc = curl_multi_exec($multi, $active);
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
}
}
curl_multi_remove_handle($multi, $handle);
//curl_multi_close($multi);
return TRUE;
}
/**
* This function reads the header file into an array.
*

View File

@@ -81,7 +81,7 @@ class Response {
switch ($code) {
case '403':
throw new \HPCloud\Transport\ForibiddenException($err);
throw new \HPCloud\Transport\ForbiddenException($err);
case '401':
throw new \HPCloud\Transport\UnauthorizedException($err);
case '404':

View File

@@ -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);
}

View File

@@ -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());
@@ -96,8 +115,31 @@ $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;
print file_get_contents('swift://' . TEST_CONTAINER . '/CDNTest.txt', FALSE, $cxt);
$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)),
);
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;

View File

@@ -38,8 +38,8 @@ including:
* URL: The Endpoint URL.
All four pieces of information can be found by logging into [the
management console](https://manage.hpcloud.com) and going to the section
called *Storage*. There should be a link on that page that says *Get
console](https://console.hpcloud.com) and going to the section called
*Storage*. There should be a link on that page that says *Get
Storage API Keys*. That page displays all four pieces of required
information.
@@ -91,7 +91,7 @@ hpcloud.identity.key = 9878787
```
You will need to add all of the `hpcloud.identity` settings, and all of
this information can be found on your management console.
this information can be found on your console.
The hpcloud.swift.account, key, and url params are no longer required
for the basic tests, but are required if you are also running the tests

View File

@@ -102,7 +102,8 @@ class TestCase extends \PHPUnit_Framework_TestCase {
$user = self::$settings['hpcloud.swift.account'];
$key = self::$settings['hpcloud.swift.key'];
$url = self::$settings['hpcloud.swift.url'];
// $url = self::$settings['hpcloud.swift.url'];
$url = self::$settings['hpcloud.identity.url'];
return \HPCloud\Storage\ObjectStorage::newFromSwiftAuth($user, $key, $url);

View File

@@ -28,7 +28,7 @@ hpcloud.identity.password =
; For authentication by account ID.
hpcloud.identity.account =
hpcloud.identity.key =
hpcloud.identity.secret =
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Configuration Parameters ;