Skip to content

Commit

Permalink
Added support for regional and path-style buckets to the S3 helper an…
Browse files Browse the repository at this point in the history
…d made some small content changes to README, CHANGELOG, and docs
  • Loading branch information
jeremeamia committed Jun 4, 2013
1 parent 6573b86 commit 1d8ea71
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 50 deletions.
15 changes: 9 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
# 1.1.0
CHANGELOG
=========

- Add CloudFront and S3 view helpers
## 1.0.2

# 1.0.1
* Added Amazon S3 and Amazon CloudFront view helpers for generating links

- Refactor module architecture
## 1.0.1

# 1.0.0
* Refactored module architecture

- Initial release
## 1.0.0

* Initial release
29 changes: 16 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Install the module using Composer into your application's vendor directory. Add
```json
{
"require": {
"aws/aws-sdk-php-zf2": "1.*"
"aws/aws-sdk-php-zf2": "1.0.*"
}
}
```
Expand Down Expand Up @@ -54,7 +54,7 @@ fetched from the Amazon EC2 instance automatically.
## Usage

You can get the AWS service builder object from anywhere that the ZF2 service locator is available (e.g. controller
classes). The following example instantiates an Amazon DynamoDB client and creates a table in DynamoDb.
classes). The following example instantiates an Amazon DynamoDB client and creates a table in DynamoDB.

```php
public function indexAction()
Expand Down Expand Up @@ -86,36 +86,38 @@ public function indexAction()
}
```

## View helpers
## View Helpers

Starting from version 1.1, AWS ZF2 module now provides two view helpers to generate S3 and CloudFront links.
Starting from version 1.0.2, the AWS SDK ZF2 Module now provides two view helpers to generate links for Amazon S3 and
Amazon CloudFront resources.

> Note: both view helpers generate a HTTPS URL by default. This is good for security, however please remember that
CloudFront charge more for HTTPS requests. You can turn off SSL by calling the `setUseSsl` on both helpers.
> **Note:** Both of the view helpers generate URLs with an HTTPS scheme by default. This is ideal for security, but
please keep in mind that Amazon CloudFront charges more for HTTPS requests. You can turn SSL off by calling the
`setUseSsl` method on both helpers.

### S3
### S3Link View Helper

To create a S3 link in your view:

```php
<?php echo $this->s3Link('my-object', 'my-bucket');
```

The default bucket can be set globally by using the `setDefautBucket` method:
The default bucket can be set globally by using the `setDefaultBucket` method:

```php
<?php
$this->s3Link->setDefaultBucket('my-bucket');
echo $this->s3Link('my-object');
```

You can also create signed URLs by passing a third argument which is the expiration date:
You can also create signed URLs for private content by passing a third argument which is the expiration date:

```php
<?php echo $this->s3Link('my-object', 'my-bucket', '+10 minutes');
```

### CloudFront
### CloudFrontLink View Helper

To create CloudFront link in your view:

Expand All @@ -131,17 +133,18 @@ The default domain can be set globally by using the `setDefaultDomain` method:
echo $this->cloudFrontLink('my-object');
```

You can also create signed URLs by passing a third argument which is the expiration date:
You can also create signed URLs for private content by passing a third argument which is the expiration date:

```php
<?php echo $this->cloudFrontLink('my-object', 'my-bucket', time() + 60);
```

## Related Modules

The following are some ZF2 modules that are built on top of the AWS SDK for PHP using this module:
The following are some ZF2 modules that use the AWS SDK for PHP by including this module:

* [SlmMail](https://github.com/juriansluiman/SlmMail) - Module that allow to send emails with various providers (including Amazon SES)
* [SlmMail](https://github.com/juriansluiman/SlmMail) - Module that allow to send emails with various providers
(including Amazon SES)
* [SlmQueueSqs](https://github.com/juriansluiman/SlmQueueSqs) – Module that simplifies the use of Amazon SQS

## Links
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"php": ">=5.3.3",
"aws/aws-sdk-php": "2.*",
"zendframework/zend-servicemanager": "2.*",
"zendframework/zend-version": "2.*"
"zendframework/zend-version": "2.*",
"zendframework/zend-view": "2.*"
},
"require-dev": {
"zendframework/zend-modulemanager": "2.*"
Expand Down
13 changes: 7 additions & 6 deletions src/Aws/View/Helper/CloudFrontLink.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@
class CloudFrontLink extends AbstractHelper
{
/**
* Amazon AWS endpoint
* The hostname for CloudFront domains
*/
const CLOUD_FRONT_ENDPOINT = 'cloudfront.net';
const CLOUDFRONT_HOSTNAME = '.cloudfront.net';

/**
* @var CloudFrontClient
Expand Down Expand Up @@ -113,14 +113,15 @@ public function __invoke($object, $domain = '', $expiration = '')

// If $domain is still empty, we throw an exception as it makes no sense
if (empty($domain)) {
throw new InvalidDomainNameException('An empty Cloud Front domain name was given');
throw new InvalidDomainNameException('An empty CloudFront domain name was given');
}

$url = sprintf(
'%s://%s.%s/%s',
'%s://%s%s/%s',
$this->useSsl ? 'https' : 'http',
str_replace('.cloudfront.net', '', rtrim($domain, '/')), // Remove .cloudfront.net if provided as we include it already
self::CLOUD_FRONT_ENDPOINT,
// Remove .cloudfront.net if provided as we include it already
str_replace(self::CLOUDFRONT_HOSTNAME, '', rtrim($domain, '/')),
self::CLOUDFRONT_HOSTNAME,
ltrim($object, '/')
);

Expand Down
47 changes: 24 additions & 23 deletions src/Aws/View/Helper/S3Link.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,18 @@

namespace Aws\View\Helper;

use Aws\Common\Aws;
use Aws\S3\BucketStyleListener;
use Aws\S3\S3Client;
use Aws\View\Exception\InvalidDomainNameException;
use Guzzle\Common\Event;
use Zend\View\Helper\AbstractHelper;

/**
* View helper that can render a link to a S3 object. It can also create signed URLs
*/
class S3Link extends AbstractHelper
{
/**
* Amazon AWS endpoint
*/
const S3_ENDPOINT = 's3.amazonaws.com';

/**
* @var S3Client
*/
Expand Down Expand Up @@ -108,29 +106,32 @@ public function getDefaultBucket()
*/
public function __invoke($object, $bucket = '', $expiration = '')
{
if (empty($bucket)) {
$bucket = $this->getDefaultBucket();
}

// If $bucket is still empty, we throw an exception as it makes no sense
$bucket = trim($bucket ?: $this->getDefaultBucket(), '/');
if (empty($bucket)) {
throw new InvalidDomainNameException('An empty bucket name was given');
}

$url = sprintf(
'%s://%s.%s/%s',
$this->useSsl ? 'https' : 'http',
trim($bucket, '/'),
self::S3_ENDPOINT,
ltrim($object, '/')
);

if (empty($expiration)) {
return $url;
// Create a command representing the get request
// Using a command will make sure the configured regional endpoint is used
$command = $this->client->getCommand('GetObject', array(
'Bucket' => $bucket,
'Key' => $object,
));

// Instead of executing the command, retrieve the request and make sure the scheme is set to what was specified
$request = $command->prepare()->setScheme($this->useSsl ? 'https' : 'http')->setPort(null);

// Ensure that the correct bucket URL style (virtual or path) is used based on the bucket name
// This addresses a bug in versions of the SDK less than or equal to 2.3.4
if (version_compare(Aws::VERSION, '2.3.4', '<=') && strpos($request->getHost(), $bucket) === false) {
$bucketStyleListener = new BucketStyleListener();
$bucketStyleListener->onCommandBeforeSend(new Event(array('command' => $command)));
}

$request = $this->client->get($url);

return $this->client->createPresignedUrl($request, $expiration);
if ($expiration) {
return $this->client->createPresignedUrl($request, $expiration);
} else {
return $request->getUrl();
}
}
}
2 changes: 1 addition & 1 deletion tests/Aws/Tests/ModuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public function testRegisterAwsModule()
$command = $s3->getCommand('ListBuckets');
$request = $command->prepare();
$s3->dispatch('command.before_send', array('command' => $command));
$this->assertRegExp('/.+ZF2\/.+/', $request->getHeader('User-Agent', true));

This comment has been minimized.

Copy link
@bakura10

bakura10 Jun 4, 2013

Contributor

Side note : I had this problem in another problem. Guzzle is really annoying to BC like this in the 3.x branch.

This comment has been minimized.

Copy link
@jeremeamia

jeremeamia Jun 4, 2013

Author Contributor

This was a change in 3.6. Had to update the SDK is various places as well.

$this->assertRegExp('/.+ZF2\/.+/', (string) $request->getHeader('User-Agent'));
}

/**
Expand Down
14 changes: 14 additions & 0 deletions tests/Aws/Tests/View/Helper/S3LinkTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,20 @@ public function testAssertGivenBucketOverrideDefaultBucket()
$this->assertEquals('https://my-overriden-bucket.s3.amazonaws.com/my-object', $link);
}

public function testCreatesUrlsForRegionalBuckets()
{
$this->s3Client->setRegion('sa-east-1');

$link = $this->viewHelper->__invoke('my-object', 'my-bucket');
$this->assertEquals('https://my-bucket.s3-sa-east-1.amazonaws.com/my-object', $link);
}

public function testCreatesUrlsForNonUrlCompatibleBucketNames()
{
$link = $this->viewHelper->__invoke('my-object', 'my.bucket');
$this->assertEquals('https://s3.amazonaws.com/my.bucket/my-object', $link);

This comment has been minimized.

Copy link
@bakura10

bakura10 Jun 4, 2013

Contributor

Shouldn't it generate my.bucket.s3.amazonaws.com/my-object for homogeneity purpose?

This comment has been minimized.

Copy link
@jeremeamia

jeremeamia Jun 4, 2013

Author Contributor

The reason I have a separate test for this is because it MUST be generated as https://s3.amazonaws.com/my.bucket/my-object in this case. Homogeneity doesn't even apply, because https//my.bucket.s3.amazonaws.com/my-object is not a valid S3 URL. S3 has some... peculiarities with URLs. The BucketStyleListener class in the SDK handles the funny edge cases, so that is why I made some changes to the helper.

This comment has been minimized.

Copy link
@bakura10

bakura10 Jun 4, 2013

Contributor

Ha, I see. Didn't know about those edge cases. So it's ok for me then :).

This comment has been minimized.

Copy link
@jeremeamia

jeremeamia Jun 4, 2013

Author Contributor

Yep. Funny business.

}

public function testGenerateSignedLink()
{
$timeTest = time() + 10;
Expand Down

0 comments on commit 1d8ea71

Please sign in to comment.