Skip to content

Commit

Permalink
Merge pull request aws#552 from aws/s3-with-regions
Browse files Browse the repository at this point in the history
Adding regions to S3Client and removing PathStyle as a per/command param...
  • Loading branch information
mtdowling committed Apr 23, 2015
2 parents fa9558b + e19ebd5 commit faf763b
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 54 deletions.
9 changes: 4 additions & 5 deletions src/S3/BucketStyleMiddleware.php
Expand Up @@ -72,13 +72,12 @@ private function modifyRequest(

if ($this->bucketEndpoint) {
$path = $this->removeBucketFromPath($path, $bucket);
} elseif (!$command['PathStyle']
&& S3Client::isBucketDnsCompatible($bucket)
} elseif (S3Client::isBucketDnsCompatible($bucket)
&& !($uri->getScheme() == 'https' && strpos($bucket, '.'))
) {
// Switch to virtual if PathStyle is disabled, or not a DNS
// compatible bucket name, or the scheme is https and there are no
// dots in the hostheader (avoids SSL issues).
// Switch to virtual if not a DNS compatible bucket name, or the
// scheme is https and there are no dots in the host header
// (avoids SSL issues).
$uri = $uri->withHost($bucket . '.' . $uri->getHost());
$path = $this->removeBucketFromPath($path, $bucket);
}
Expand Down
113 changes: 73 additions & 40 deletions src/S3/S3Client.php
Expand Up @@ -5,6 +5,7 @@
use Aws\Api\DocModel;
use Aws\Api\Service;
use Aws\AwsClient;
use Aws\ClientResolver;
use Aws\Exception\AwsException;
use Aws\HandlerList;
use Aws\Middleware;
Expand All @@ -24,53 +25,58 @@ class S3Client extends AwsClient
public static function getArguments()
{
$args = parent::getArguments();
// S3 does not require a region for the "classic" endpoint.
$args['region']['default'] = 'us-east-1';
// Apply custom retry strategy.
$args['retries']['fn'] = [__CLASS__, '_applyRetryConfig'];

// Show information about the "us-standard" region in the error message
$args['region']['required'] = function (array $args) {
$base = ClientResolver::_missing_region($args);
return "{$base}\nUse the 'us-standard' or 'us-east-1' region to "
. "send requests to the global\nAmazon S3 endpoint, which is "
. "generally able to send requests to any region\n(though you "
. "may need to use a region specific endpoint when sending "
. "requests\nover https to buckets with dots in them or "
. "contacting signature version\n4 only regions.";
};

return $args + [
'force_path_style' => [
'type' => 'config',
'valid' => ['bool'],
'doc' => 'Set to true to send requests using path style '
. 'bucket addressing (e.g., '
. 'https://s3.amazonaws.com/bucket/key).',
'fn' => function ($value, $_, HandlerList $stack) {
if ($value === true) {
$stack->append('init', function (callable $handler) {
return function ($cmd) use ($handler) {
$cmd['PathStyle'] = true;
return $handler($cmd);
};
});
}
},
],
'calculate_md5' => [
'type' => 'config',
'valid' => ['bool'],
'doc' => 'Set to false to disable calculating an MD5 for '
. 'all Amazon S3 signed uploads.',
. 'all Amazon S3 signed uploads.',
'default' => function (array &$args) {
// S3Client should calculate MD5 checksums for uploads
// unless explicitly disabled or using a v4 signer.
return $args['config']['signature_version'] != 'v4';
},
],
'bucket_endpoint' => [
'type' => 'config',
'valid' => ['bool'],
'doc' => 'Set to true to send requests to a hardcoded bucket '
. 'endpoint rather than create an endpoint as a '
. 'result of injecting the bucket into the URL. This '
. 'option is useful for interacting with CNAME '
. 'endpoints.',
]
'type' => 'config',
'valid' => ['bool'],
'doc' => 'Set to true to send requests to a hardcoded '
. 'bucket endpoint rather than create an endpoint as a '
. 'result of injecting the bucket into the URL. This '
. 'option is useful for interacting with CNAME endpoints.',
],
'force_path_style' => [
'type' => 'config',
'valid' => ['bool'],
'doc' => 'Set to true to send requests using path style '
. 'bucket addressing (e.g., '
. 'https://s3.amazonaws.com/bucket/key). All requests sent '
. 'to region specific endpoints will use path style by '
. 'default. This option is only relevant when you wish to '
. 'use the us-standard or us-east-1 region AND force a '
. 'path style request. This option has no effect when '
. 'using a "bucket_endpoint".'
],
];
}

/**
* {@inheritdoc}
*
* In addition to the options available to
* {@see Aws\AwsClient::__construct}, S3Client accepts the following
* options:
Expand All @@ -83,24 +89,59 @@ public static function getArguments()
* for all Amazon S3 signed uploads.
* - force_path_style: (bool) Set to true to send requests using path
* style bucket addressing (e.g., https://s3.amazonaws.com/bucket/key).
* All requests sent to region specific endpoints will use path style by
* default. This option is only relevant when you wish to use the
* us-standard or us-east-1 region AND force a path style request. This
* option has no effect when using a "bucket_endpoint".
*
* AwsClient options:
*
* {@inheritdoc}
* The S3Client requires that a region is provided. You can provide the
* "us-standard" or "us-east-1" region to use a custom region that connects
* to the global endpoint (s3.amazonaws.com). This region should be used
* when you do not know the actual region of the bucket or wish to attempt
* to use the same client to connect to multiple buckets. Using the
* "us-standard" region may encounter errors in specific cases. For example,
* we will attempt to place the bucket in the host header so that the
* us-standard region works with buckets created for a specific region.
* However, if the bucket contains dots ".", then the bucket must remain in
* the path of the URI to ensure that SSL certificate verification does not
* fail. In this case, you may receive a 301 redirect which will require
* you to use a client with a specific region setting. You may also be
* required to provide a region other than us-standard or us-east-1 when
* the bucket is in a signature version 4 only region.
*
* @param array $args
*/
public function __construct(array $args)
{
$region = isset($args['region']) ? $args['region'] : null;
$standard = ($region == 'us-standard' || $region == 'us-east-1');
// Rewrite us-standard to us-east-1 so that we can sign correctly.
if ($standard) {
$args['region'] = 'us-east-1';
}

parent::__construct($args);
$stack = $this->getHandlerList();
$stack->append('init:s3.ssec', SSECMiddleware::wrap($this->getEndpoint()->getScheme()));
$stack->append('build:s3.bucket_style', BucketStyleMiddleware::wrap($this->getConfig('bucket_endpoint')));
$stack->append('build:s3.md5', ApplyMd5Middleware::wrap($this->getConfig('calculate_md5')));
$stack->append(
'build:s3.content_type',
Middleware::contentType(['PutObject', 'UploadPart'])
);

// Use the bucket style middleware when using a "bucket_endpoint" or
// to move the bucket to the host if using us-standard and
// force_path_style is false.
$bucketEndpoint = $this->getConfig('bucket_endpoint');
if ($bucketEndpoint
|| ($standard && !$this->getConfig('force_path_style'))
) {
$stack->append(
'build:s3.bucket_style',
BucketStyleMiddleware::wrap($bucketEndpoint)
);
}

$stack->append('sign:s3.put_object_url', PutObjectUrlMiddleware::wrap());
$stack->append('sign:s3.permanent_redirect', PermanentRedirectMiddleware::wrap());
$stack->append('init:s3.source_file', Middleware::sourceFile($this->getApi()));
Expand Down Expand Up @@ -451,14 +492,6 @@ private function checkExistenceWithCommand(CommandInterface $command)
}
}

/**
* @deprecated Use Aws\S3\S3Client::isBucketDnsCompatible() directly
*/
public static function isValidBucketName($bucket)
{
return self::isBucketDnsCompatible($bucket);
}

/** @internal */
public static function _applyRetryConfig($value, $_, HandlerList $list)
{
Expand Down
7 changes: 3 additions & 4 deletions tests/S3/BucketStyleMiddlewareTest.php
Expand Up @@ -50,12 +50,11 @@ public function testUsesPathStyleWhenNotDnsCompatible()

public function testUsesPathStyleWhenForced()
{
$s3 = $this->getTestClient('s3');
$s3 = $this->getTestClient('s3', ['force_path_style' => true]);
$this->addMockResults($s3, [[]]);
$command = $s3->getCommand('GetObject', [
'Bucket' => 'foo',
'Key' => 'Bar',
'PathStyle' => true
'Bucket' => 'foo',
'Key' => 'Bar'
]);
$command->getHandlerList()->append(
'sign',
Expand Down
16 changes: 11 additions & 5 deletions tests/S3/S3ClientTest.php
Expand Up @@ -2,6 +2,7 @@
namespace Aws\Test\S3;

use Aws\Credentials\NullCredentials;
use Aws\MockHandler;
use Aws\Result;
use Aws\S3\S3Client;
use Aws\Test\UsesServiceTrait;
Expand All @@ -19,23 +20,28 @@ class S3ClientTest extends \PHPUnit_Framework_TestCase

public function testCanForcePathStyleOnAllOperations()
{
$mock = new MockHandler([new Result()]);
$c = new S3Client([
'region' => 'us-standard',
'version' => 'latest',
'force_path_style' => true
'force_path_style' => true,
'handler' => $mock
]);

$this->addMockResults($c, [[]]);
$command = $c->getCommand('GetObject', [
'Bucket' => 'foo',
'Key' => 'baz'
]);
$c->execute($command);
$this->assertTrue($command['PathStyle']);
$this->assertEquals(
'https://s3.amazonaws.com/foo/baz',
(string) $mock->getLastRequest()->getUri()
);
}

public function testCanUseBucketEndpoint()
{
$c = new S3Client([
'region' => 'us-standard',
'version' => 'latest',
'endpoint' => 'http://test.domain.com',
'bucket_endpoint' => true
Expand All @@ -49,6 +55,7 @@ public function testCanUseBucketEndpoint()
public function testAddsMd5ToConfig()
{
$c = new S3Client([
'region' => 'us-standard',
'version' => 'latest',
'calculate_md5' => true
]);
Expand Down Expand Up @@ -81,7 +88,6 @@ public function bucketNameProvider()
public function testValidatesDnsBucketNames($bucket, $valid)
{
$this->assertEquals($valid, S3Client::isBucketDnsCompatible($bucket));
$this->assertEquals($valid, S3Client::isValidBucketName($bucket));
}

/**
Expand Down

0 comments on commit faf763b

Please sign in to comment.