Skip to content

Commit

Permalink
Merge pull request aws#605 from aws/604-fixes
Browse files Browse the repository at this point in the history
Various enhancements related to aws#604
  • Loading branch information
mtdowling committed Jun 3, 2015
2 parents c194ec8 + 162e8f8 commit 4418fa6
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 26 deletions.
4 changes: 2 additions & 2 deletions src/Exception/CouldNotCreateChecksumException.php
Expand Up @@ -3,7 +3,7 @@

class CouldNotCreateChecksumException extends \RuntimeException
{
public function __construct($algorithm)
public function __construct($algorithm, \Exception $previous = null)
{
$prefix = $algorithm === 'md5' ? "An" : "A";
parent::__construct("{$prefix} {$algorithm} checksum could not be "
Expand All @@ -14,6 +14,6 @@ public function __construct($algorithm)
. "stream in a GuzzleHttp\\Stream\\CachingStream object. You "
. "should be careful though and remember that the CachingStream "
. "utilizes PHP temp streams. This means that the stream will be "
. "temporarily stored on the local disk.");
. "temporarily stored on the local disk.", 0, $previous);
}
}
27 changes: 17 additions & 10 deletions src/S3/StreamWrapper.php
Expand Up @@ -156,7 +156,7 @@ public function stream_flush()
if ($this->body->isSeekable()) {
$this->body->seek(0);
}
$params = $this->getOptions();
$params = $this->getOptions(true);
$params['Body'] = $this->body;

// Attempt to guess the ContentType of the upload based on the
Expand Down Expand Up @@ -502,7 +502,7 @@ public function rename($path_from, $path_to)
return $this->boolCall(function () use ($partsFrom, $partsTo) {
// Copy the object and allow overriding default parameters if
// desired, but by default copy metadata
$this->getClient()->copyObject($this->getOptions() + [
$this->getClient()->copyObject($this->getOptions(true) + [
'Bucket' => $partsTo['Bucket'],
'Key' => $partsTo['Key'],
'MetadataDirective' => 'COPY',
Expand All @@ -513,7 +513,7 @@ public function rename($path_from, $path_to)
$this->getClient()->deleteObject([
'Bucket' => $partsFrom['Bucket'],
'Key' => $partsFrom['Key']
] + $this->getOptions());
] + $this->getOptions(true));
return true;
});
}
Expand Down Expand Up @@ -547,7 +547,7 @@ private function validate($path, $mode)
$this->getClient()->doesObjectExist(
$this->getOption('Bucket'),
$this->getOption('Key'),
$this->getOptions()
$this->getOptions(true)
)
) {
$errors[] = "{$path} already exists on Amazon S3";
Expand All @@ -559,9 +559,12 @@ private function validate($path, $mode)
/**
* Get the stream context options available to the current stream
*
* @param bool $removeContextData Set to true to remove contextual kvp's
* like 'client' from the result.
*
* @return array
*/
private function getOptions()
private function getOptions($removeContextData = false)
{
// Context is not set when doing things like stat
if ($this->context === null) {
Expand All @@ -573,8 +576,13 @@ private function getOptions()

$default = stream_context_get_options(stream_context_get_default());
$default = isset($default['s3']) ? $default['s3'] : [];
$result = $this->params + $options + $default;

if ($removeContextData) {
unset($result['client'], $result['seekable'], $result['cache']);
}

return $this->params + $options + $default;
return $result;
}

/**
Expand Down Expand Up @@ -628,16 +636,15 @@ private function getBucketKey($path)
*/
private function withPath($path)
{
$params = $this->getOptions();
unset($params['seekable'], $params['client']);
$params = $this->getOptions(true);

return $this->getBucketKey($path) + $params;
}

private function openReadStream()
{
$client = $this->getClient();
$command = $client->getCommand('GetObject', $this->getOptions());
$command = $client->getCommand('GetObject', $this->getOptions(true));
$command['@http']['stream'] = true;
$result = $client->execute($command);
$this->body = $result['Body'];
Expand All @@ -661,7 +668,7 @@ private function openAppendStream()
try {
// Get the body of the object and seek to the end of the stream
$client = $this->getClient();
$this->body = $client->getObject($this->getOptions())['Body'];
$this->body = $client->getObject($this->getOptions(true))['Body'];
$this->body->seek(0, SEEK_END);
return true;
} catch (S3Exception $e) {
Expand Down
6 changes: 5 additions & 1 deletion src/Signature/SignatureV4.php
Expand Up @@ -142,7 +142,11 @@ protected function getPayload(RequestInterface $request)
throw new CouldNotCreateChecksumException('sha256');
}

return Psr7\hash($request->getBody(), 'sha256');
try {
return Psr7\hash($request->getBody(), 'sha256');
} catch (\Exception $e) {
throw new CouldNotCreateChecksumException('sha256', $e);
}
}

protected function getPresignedPayload(RequestInterface $request)
Expand Down
38 changes: 25 additions & 13 deletions src/TraceMiddleware.php
Expand Up @@ -160,22 +160,32 @@ private function resultArray($value)

private function exceptionArray($e)
{
if (!($e instanceof AwsException)) {
if (!($e instanceof \Exception)) {
return $e;
}

return [
$result = [
'instance' => spl_object_hash($e),
'class' => get_class($e),
'message' => $e->getMessage(),
'type' => $e->getAwsErrorType(),
'code' => $e->getAwsErrorCode(),
'requestId' => $e->getAwsRequestId(),
'statusCode' => $e->getStatusCode(),
'result' => $this->resultArray($e->getResult()),
'request' => $this->requestArray($e->getRequest()),
'response' => $this->responseArray($e->getResponse())
'file' => $e->getFile(),
'line' => $e->getLine(),
'trace' => $e->getTraceAsString(),
];

if ($e instanceof AwsException) {
$result += [
'type' => $e->getAwsErrorType(),
'code' => $e->getAwsErrorCode(),
'requestId' => $e->getAwsRequestId(),
'statusCode' => $e->getStatusCode(),
'result' => $this->resultArray($e->getResult()),
'request' => $this->requestArray($e->getRequest()),
'response' => $this->responseArray($e->getResponse()),
];
}

return $result;
}

private function compareArray($a, $b, $path, array &$diff)
Expand Down Expand Up @@ -207,11 +217,13 @@ private function str($value)
{
if (is_scalar($value)) {
return (string) $value;
} else {
ob_start();
var_dump($value);
return ob_get_clean();
} elseif ($value instanceof \Exception) {
$value = $this->exceptionArray($value);
}

ob_start();
var_dump($value);
return ob_get_clean();
}

private function streamStr(StreamInterface $body)
Expand Down
15 changes: 15 additions & 0 deletions tests/Signature/SignatureV4Test.php
Expand Up @@ -188,6 +188,21 @@ public function testEnsuresContentSha256CanBeCalculated()
$signature->signRequest($request, $credentials);
}

/**
* @expectedException \Aws\Exception\CouldNotCreateChecksumException
*/
public function testEnsuresContentSha256CanBeCalculatedWhenSeekFails()
{
list($request, $credentials, $signature) = $this->getFixtures();
$stream = Psr7\FnStream::decorate(Psr7\stream_for('foo'), [
'seek' => function () {
throw new \Exception('Could not seek');
}
]);
$request = $request->withBody($stream);
$signature->signRequest($request, $credentials);
}

public function testProvider()
{
return [
Expand Down
36 changes: 36 additions & 0 deletions tests/TraceMiddlewareTest.php
Expand Up @@ -57,6 +57,38 @@ public function testEmitsDebugInfo()
}

public function testTracksExceptions()
{
$str = '';
$logfn = function ($value) use (&$str) { $str .= $value; };
$list = new HandlerList();
$list->setHandler(function ($cmd, $req) {
return \GuzzleHttp\Promise\promise_for(new Result());
});
$list->appendInit(function ($handler) {
return function ($cmd, $req = null) use ($handler) {
$req = $req->withHeader('foo', 'bar');
return $handler($cmd, $req);
};
});
$list->appendValidate(function ($handler) {
return function ($cmd, $req = null) use ($handler) {
return new RejectedPromise(new \Exception('Oh no!'));
};
});
$list->interpose(new TraceMiddleware(['logfn' => $logfn]));
$handler = $list->resolve();
$command = new Command('foo');
$request = new Request('GET', 'http://foo.com');
$handler($command, $request);
Promise\queue()->run();
$this->assertContains('error was set to array', $str);
$this->assertContains('trace', $str);
$this->assertContains('class', $str);
$this->assertContains('message', $str);
$this->assertContains('string(6) "Oh no!"', $str);
}

public function testTracksAwsSpecificExceptions()
{
$str = '';
$logfn = function ($value) use (&$str) { $str .= $value; };
Expand Down Expand Up @@ -85,6 +117,10 @@ public function testTracksExceptions()
$handler($command, $request);
Promise\queue()->run();
$this->assertContains('error was set to array', $str);
$this->assertContains('trace', $str);
$this->assertContains('class', $str);
$this->assertContains('message', $str);
$this->assertContains('string(5) "error"', $str);
}

public function testScrubsAuthStrings()
Expand Down

0 comments on commit 4418fa6

Please sign in to comment.