Version Description
- Updated AWS library
- Added support of set_sql_mode by dbcluster
- Improved support for webserver running on non-default port with disk-enhanced
- Improved menu icons
- Fixed php warning when remote service cannot be loaded
- Fixed php warnings on support page
Download this release
Release Info
Developer | fredericktownes |
Plugin | W3 Total Cache |
Version | 0.9.7.5 |
Comparing to | |
See all releases |
Code changes from version 0.9.7.4 to 0.9.7.5
- CdnEngine.php +2 -2
- CdnEngine_Azure.php +5 -11
- CdnEngine_Base.php +2 -8
- CdnEngine_CloudFront.php +345 -0
- CdnEngine_Mirror_CloudFront.php +324 -0
- CdnEngine_S3.php +188 -235
- CdnEngine_S3_Cf.php +0 -453
- CdnEngine_S3_Cf_Custom.php +0 -19
- CdnEngine_S3_Cf_S3.php +0 -10
- CdnEngine_S3_Compatible.php +6 -6
- Cdn_AdminActions.php +22 -17
- Cdn_Core_Admin.php +45 -57
- Cdn_Plugin_Admin.php +1 -1
- Cdn_Util.php +1 -0
- Cdnfsd_CloudFront_Api.php +0 -344
- Cdnfsd_CloudFront_Engine.php +46 -11
- Cdnfsd_CloudFront_Popup.php +114 -83
- Cdnfsd_CloudFront_Popup_View_Distribution.php +45 -47
- Config.php +13 -13
- ConfigKeys.php +0 -4
- DbCache_Wpdb.php +14 -0
- DbCache_WpdbInjection.php +8 -0
- Enterprise_CacheFlush_MakeSnsEvent.php +6 -3
- Enterprise_Dbcache_WpdbInjection_Cluster.php +1 -0
- Enterprise_SnsBase.php +13 -9
- Enterprise_SnsServer.php +14 -19
- Generic_Plugin.php +57 -34
- Generic_Plugin_Admin.php +4 -30
- Generic_WidgetServices_View.php +17 -17
- PgCache_ContentGrabber.php +3 -1
- PgCache_Flush.php +38 -18
- Root_AdminMenu.php +4 -4
- Support_Page.php +19 -22
- Support_Page_View_PageContent.php +10 -10
- Util_Admin.php +7 -21
- inc/mime/html.php +0 -1
- inc/options/browsercache.php +1 -1
- inc/options/general.php +285 -285
- lib/Aws/Aws/Api/AbstractModel.php +67 -0
- lib/Aws/Aws/Api/ApiProvider.php +244 -0
- lib/Aws/Aws/Api/DateTimeResult.php +41 -0
- lib/Aws/Aws/Api/DocModel.php +128 -0
- lib/Aws/Aws/Api/ErrorParser/JsonParserTrait.php +26 -0
- lib/Aws/Aws/Api/ErrorParser/JsonRpcErrorParser.php +31 -0
- lib/Aws/Aws/Api/ErrorParser/RestJsonErrorParser.php +35 -0
- lib/Aws/Aws/Api/ErrorParser/XmlErrorParser.php +82 -0
- lib/Aws/Aws/Api/ListShape.php +35 -0
- lib/Aws/Aws/Api/MapShape.php +54 -0
- lib/Aws/Aws/Api/Operation.php +97 -0
- lib/Aws/Aws/Api/Parser/AbstractParser.php +46 -0
- lib/Aws/Aws/Api/Parser/AbstractRestParser.php +173 -0
- lib/Aws/Aws/Api/Parser/Crc32ValidatingParser.php +54 -0
- lib/Aws/Aws/Api/Parser/DecodingEventStreamIterator.php +335 -0
- lib/Aws/Aws/Api/Parser/EventParsingIterator.php +107 -0
- lib/Aws/Aws/Api/Parser/Exception/ParserException.php +31 -0
- lib/Aws/Aws/Api/Parser/JsonParser.php +62 -0
- lib/Aws/Aws/Api/Parser/JsonRpcParser.php +51 -0
- lib/Aws/Aws/Api/Parser/PayloadParserTrait.php +61 -0
- lib/Aws/Aws/Api/Parser/QueryParser.php +60 -0
- lib/Aws/Aws/Api/Parser/RestJsonParser.php +49 -0
- lib/Aws/Aws/Api/Parser/RestXmlParser.php +42 -0
- lib/Aws/Aws/Api/Parser/XmlParser.php +138 -0
- lib/Aws/Aws/Api/Serializer/Ec2ParamBuilder.php +40 -0
- lib/Aws/Aws/Api/Serializer/JsonBody.php +96 -0
- lib/Aws/Aws/Api/Serializer/JsonRpcSerializer.php +69 -0
- lib/Aws/Aws/Api/Serializer/QueryParamBuilder.php +157 -0
- lib/Aws/Aws/Api/Serializer/QuerySerializer.php +69 -0
- lib/Aws/Aws/Api/Serializer/RestJsonSerializer.php +39 -0
- lib/Aws/Aws/Api/Serializer/RestSerializer.php +219 -0
- lib/Aws/Aws/Api/Serializer/RestXmlSerializer.php +34 -0
- lib/Aws/Aws/Api/Serializer/XmlBody.php +220 -0
- lib/Aws/Aws/Api/Service.php +448 -0
- lib/Aws/Aws/Api/Shape.php +69 -0
- lib/Aws/Aws/Api/ShapeMap.php +66 -0
- lib/Aws/Aws/Api/StructureShape.php +79 -0
- lib/Aws/Aws/Api/TimestampShape.php +48 -0
- lib/Aws/Aws/Api/Validator.php +286 -0
- lib/Aws/Aws/AwsClient.php +402 -0
- lib/Aws/Aws/AwsClientInterface.php +169 -0
- lib/Aws/Aws/AwsClientTrait.php +92 -0
- lib/Aws/Aws/CacheInterface.php +34 -0
- lib/Aws/Aws/ClientResolver.php +768 -0
- lib/Aws/Aws/ClientSideMonitoring/AbstractMonitoringMiddleware.php +275 -0
- lib/Aws/Aws/ClientSideMonitoring/ApiCallAttemptMonitoringMiddleware.php +262 -0
- lib/Aws/Aws/ClientSideMonitoring/ApiCallMonitoringMiddleware.php +176 -0
- lib/Aws/Aws/ClientSideMonitoring/Configuration.php +65 -0
- lib/Aws/Aws/ClientSideMonitoring/ConfigurationInterface.php +37 -0
- lib/Aws/Aws/ClientSideMonitoring/ConfigurationProvider.php +342 -0
- lib/Aws/Aws/ClientSideMonitoring/Exception/ConfigurationException.php +15 -0
- lib/Aws/Aws/ClientSideMonitoring/MonitoringMiddlewareInterface.php +35 -0
- lib/Aws/Aws/CloudFront/CloudFrontClient.php +190 -0
- lib/Aws/Aws/CloudFront/CookieSigner.php +65 -0
- lib/Aws/Aws/CloudFront/Exception/CloudFrontException.php +9 -0
- lib/Aws/Aws/CloudFront/Signer.php +117 -0
- lib/Aws/Aws/CloudFront/UrlSigner.php +119 -0
- lib/Aws/Aws/Command.php +62 -0
- lib/Aws/Aws/CommandInterface.php +42 -0
- lib/Aws/Aws/CommandPool.php +150 -0
- lib/Aws/Aws/Credentials/AssumeRoleCredentialProvider.php +64 -0
- lib/Aws/Aws/Credentials/CredentialProvider.php +488 -0
- lib/Aws/Aws/Credentials/Credentials.php +91 -0
- lib/Aws/Aws/Credentials/CredentialsInterface.php +52 -0
- lib/Aws/Aws/Credentials/EcsCredentialProvider.php +88 -0
- lib/Aws/Aws/Credentials/InstanceProfileProvider.php +118 -0
- lib/Aws/Aws/DoctrineCacheAdapter.php +55 -0
- lib/Aws/Aws/Endpoint/EndpointProvider.php +96 -0
- lib/Aws/Aws/Endpoint/Partition.php +183 -0
- lib/Aws/Aws/Endpoint/PartitionEndpointProvider.php +108 -0
- lib/Aws/Aws/Endpoint/PartitionInterface.php +56 -0
- lib/Aws/Aws/Endpoint/PatternEndpointProvider.php +51 -0
- lib/Aws/Aws/EndpointDiscovery/Configuration.php +48 -0
- lib/Aws/Aws/EndpointDiscovery/ConfigurationInterface.php +30 -0
- lib/Aws/Aws/EndpointDiscovery/ConfigurationProvider.php +333 -0
- lib/Aws/Aws/EndpointDiscovery/EndpointDiscoveryMiddleware.php +414 -0
- lib/Aws/Aws/EndpointDiscovery/EndpointList.php +85 -0
- lib/Aws/Aws/EndpointDiscovery/Exception/ConfigurationException.php +14 -0
- lib/Aws/Aws/EndpointParameterMiddleware.php +84 -0
- lib/Aws/Aws/Exception/AwsException.php +237 -0
- lib/Aws/Aws/Exception/CouldNotCreateChecksumException.php +25 -0
- lib/Aws/Aws/Exception/CredentialsException.php +11 -0
- lib/Aws/Aws/Exception/EventStreamDataException.php +38 -0
- lib/Aws/Aws/Exception/MultipartUploadException.php +63 -0
- lib/Aws/Aws/Exception/UnresolvedApiException.php +11 -0
- lib/Aws/Aws/Exception/UnresolvedEndpointException.php +11 -0
- lib/Aws/Aws/Exception/UnresolvedSignatureException.php +11 -0
- lib/Aws/Aws/Handler/GuzzleV5/GuzzleHandler.php +210 -0
- lib/Aws/Aws/Handler/GuzzleV5/GuzzleStream.php +24 -0
- lib/Aws/Aws/Handler/GuzzleV5/PsrStream.php +34 -0
- lib/Aws/Aws/Handler/GuzzleV6/GuzzleHandler.php +85 -0
- lib/Aws/Aws/HandlerList.php +451 -0
- lib/Aws/Aws/HasDataTrait.php +60 -0
- lib/Aws/Aws/HasMonitoringEventsTrait.php +39 -0
- lib/Aws/Aws/HashInterface.php +27 -0
- lib/Aws/Aws/HashingStream.php +60 -0
- lib/Aws/Aws/History.php +156 -0
- lib/Aws/Aws/IdempotencyTokenMiddleware.php +118 -0
- lib/Aws/Aws/JsonCompiler.php +25 -0
- lib/Aws/Aws/LruArrayCache.php +79 -0
- lib/Aws/Aws/Middleware.php +372 -0
- lib/Aws/Aws/MockHandler.php +145 -0
- lib/Aws/Aws/MonitoringEventsInterface.php +32 -0
- lib/Aws/Aws/MultiRegionClient.php +236 -0
- lib/Aws/Aws/PhpHash.php +81 -0
- lib/Aws/Aws/PresignUrlMiddleware.php +99 -0
- lib/Aws/Aws/Psr16CacheAdapter.php +30 -0
- lib/Aws/Aws/PsrCacheAdapter.php +38 -0
- lib/Aws/Aws/ResponseContainerInterface.php +13 -0
- lib/Aws/Aws/Result.php +57 -0
- lib/Aws/Aws/ResultInterface.php +54 -0
- lib/Aws/Aws/ResultPaginator.php +169 -0
- lib/Aws/Aws/RetryMiddleware.php +315 -0
- lib/Aws/Aws/S3/AmbiguousSuccessParser.php +68 -0
- lib/Aws/Aws/S3/ApplyChecksumMiddleware.php +78 -0
- lib/Aws/Aws/S3/BatchDelete.php +237 -0
- lib/Aws/Aws/S3/BucketEndpointMiddleware.php +75 -0
- lib/Aws/Aws/S3/Crypto/CryptoParamsTrait.php +75 -0
- lib/Aws/Aws/S3/Crypto/HeadersMetadataStrategy.php +52 -0
- lib/Aws/Aws/S3/Crypto/InstructionFileMetadataStrategy.php +90 -0
- lib/Aws/Aws/S3/Crypto/S3EncryptionClient.php +317 -0
- lib/Aws/Aws/S3/Crypto/S3EncryptionMultipartUploader.php +157 -0
- lib/Aws/Aws/S3/Exception/DeleteMultipleObjectsException.php +68 -0
- lib/Aws/Aws/S3/Exception/PermanentRedirectException.php +4 -0
- lib/Aws/Aws/S3/Exception/S3Exception.php +9 -0
- lib/Aws/Aws/S3/Exception/S3MultipartUploadException.php +84 -0
- lib/Aws/Aws/S3/GetBucketLocationParser.php +49 -0
- lib/Aws/Aws/S3/MultipartCopy.php +183 -0
- lib/Aws/Aws/S3/MultipartUploader.php +168 -0
- lib/Aws/Aws/S3/MultipartUploadingTrait.php +132 -0
- lib/Aws/Aws/S3/ObjectCopier.php +150 -0
- lib/Aws/Aws/S3/ObjectUploader.php +140 -0
- lib/Aws/Aws/S3/PermanentRedirectMiddleware.php +62 -0
- lib/Aws/Aws/S3/PostObject.php +160 -0
- lib/Aws/Aws/S3/PostObjectV4.php +195 -0
- lib/Aws/Aws/S3/PutObjectUrlMiddleware.php +57 -0
- lib/Aws/Aws/S3/RetryableMalformedResponseParser.php +56 -0
- lib/Aws/Aws/S3/S3Client.php +633 -0
- lib/Aws/Aws/S3/S3ClientInterface.php +322 -0
- lib/Aws/Aws/S3/S3ClientTrait.php +323 -0
- lib/Aws/Aws/S3/S3EndpointMiddleware.php +234 -0
- lib/Aws/Aws/S3/S3MultiRegionClient.php +339 -0
- lib/Aws/Aws/S3/S3UriParser.php +133 -0
- lib/Aws/Aws/S3/SSECMiddleware.php +75 -0
- lib/Aws/Aws/S3/StreamWrapper.php +958 -0
- lib/Aws/Aws/S3/Transfer.php +428 -0
- lib/Aws/Aws/Sdk.php +466 -0
- lib/Aws/Aws/Signature/AnonymousSignature.php +26 -0
- lib/Aws/Aws/Signature/S3SignatureV4.php +68 -0
- lib/Aws/Aws/Signature/SignatureInterface.php +44 -0
- lib/Aws/Aws/Signature/SignatureProvider.php +131 -0
- lib/Aws/Aws/Signature/SignatureTrait.php +49 -0
- lib/Aws/Aws/Signature/SignatureV4.php +412 -0
- lib/Aws/Aws/Sns/Exception/InvalidSnsMessageException.php +9 -0
- lib/Aws/Aws/Sns/Exception/SnsException.php +9 -0
- lib/Aws/Aws/Sns/Message.php +156 -0
- lib/Aws/Aws/Sns/MessageValidator.php +190 -0
- lib/Aws/Aws/Sns/SnsClient.php +76 -0
- lib/Aws/Aws/TraceMiddleware.php +314 -0
- lib/Aws/Aws/Waiter.php +262 -0
- lib/Aws/Aws/WrappedHttpHandler.php +203 -0
- lib/Aws/Aws/data/cloudfront/2015-07-27/api-2.json.php +3 -0
- lib/Aws/Aws/data/cloudfront/2015-07-27/paginators-1.json.php +3 -0
- lib/Aws/Aws/data/cloudfront/2015-07-27/waiters-2.json.php +3 -0
- lib/Aws/Aws/data/cloudfront/2016-01-28/api-2.json.php +3 -0
- lib/Aws/Aws/data/cloudfront/2016-01-28/paginators-1.json.php +3 -0
- lib/Aws/Aws/data/cloudfront/2016-01-28/waiters-2.json.php +3 -0
- lib/Aws/Aws/data/cloudfront/2016-08-01/api-2.json.php +3 -0
CdnEngine.php
CHANGED
@@ -32,11 +32,11 @@ class CdnEngine {
|
|
32 |
Â
break;
|
33 |
Â
|
34 |
Â
case 'cf':
|
35 |
-
$instances[$instance_key] = new
|
36 |
Â
break;
|
37 |
Â
|
38 |
Â
case 'cf2':
|
39 |
-
$instances[$instance_key] = new
|
40 |
Â
break;
|
41 |
Â
|
42 |
Â
case 'cotendo':
|
32 |
Â
break;
|
33 |
Â
|
34 |
Â
case 'cf':
|
35 |
+
$instances[$instance_key] = new CdnEngine_CloudFront( $config );
|
36 |
Â
break;
|
37 |
Â
|
38 |
Â
case 'cf2':
|
39 |
+
$instances[$instance_key] = new CdnEngine_Mirror_CloudFront( $config );
|
40 |
Â
break;
|
41 |
Â
|
42 |
Â
case 'cotendo':
|
CdnEngine_Azure.php
CHANGED
@@ -336,27 +336,24 @@ class CdnEngine_Azure extends CdnEngine_Base {
|
|
336 |
Â
/**
|
337 |
Â
* Creates bucket
|
338 |
Â
*
|
339 |
-
* @param string $container_id
|
340 |
Â
* @param string $error
|
341 |
Â
* @return boolean
|
342 |
Â
*/
|
343 |
-
function create_container(
|
344 |
Â
if ( !$this->_init( $error ) ) {
|
345 |
-
|
346 |
Â
}
|
347 |
Â
|
348 |
Â
try {
|
349 |
Â
$containers = $this->_client->listContainers();
|
350 |
Â
} catch ( \Exception $exception ) {
|
351 |
Â
$error = sprintf( 'Unable to list containers (%s).', $exception->getMessage() );
|
352 |
-
|
353 |
-
return false;
|
354 |
Â
}
|
355 |
Â
|
356 |
Â
if ( in_array( $this->_config['container'], (array) $containers ) ) {
|
357 |
Â
$error = sprintf( 'Container already exists: %s.', $this->_config['container'] );
|
358 |
-
|
359 |
-
return false;
|
360 |
Â
}
|
361 |
Â
|
362 |
Â
try {
|
@@ -367,11 +364,8 @@ class CdnEngine_Azure extends CdnEngine_Base {
|
|
367 |
Â
$this->_client->createContainer( $this->_config['container'], $createContainerOptions );
|
368 |
Â
} catch ( \Exception $exception ) {
|
369 |
Â
$error = sprintf( 'Unable to create container: %s (%s)', $this->_config['container'], $exception->getMessage() );
|
370 |
-
|
371 |
-
return false;
|
372 |
Â
}
|
373 |
-
|
374 |
-
return true;
|
375 |
Â
}
|
376 |
Â
|
377 |
Â
/**
|
336 |
Â
/**
|
337 |
Â
* Creates bucket
|
338 |
Â
*
|
Â
|
|
339 |
Â
* @param string $error
|
340 |
Â
* @return boolean
|
341 |
Â
*/
|
342 |
+
function create_container() {
|
343 |
Â
if ( !$this->_init( $error ) ) {
|
344 |
+
throw new \Exception( $error );
|
345 |
Â
}
|
346 |
Â
|
347 |
Â
try {
|
348 |
Â
$containers = $this->_client->listContainers();
|
349 |
Â
} catch ( \Exception $exception ) {
|
350 |
Â
$error = sprintf( 'Unable to list containers (%s).', $exception->getMessage() );
|
351 |
+
throw new \Exception( $error );
|
Â
|
|
352 |
Â
}
|
353 |
Â
|
354 |
Â
if ( in_array( $this->_config['container'], (array) $containers ) ) {
|
355 |
Â
$error = sprintf( 'Container already exists: %s.', $this->_config['container'] );
|
356 |
+
throw new \Exception( $error );
|
Â
|
|
357 |
Â
}
|
358 |
Â
|
359 |
Â
try {
|
364 |
Â
$this->_client->createContainer( $this->_config['container'], $createContainerOptions );
|
365 |
Â
} catch ( \Exception $exception ) {
|
366 |
Â
$error = sprintf( 'Unable to create container: %s (%s)', $this->_config['container'], $exception->getMessage() );
|
367 |
+
throw new \Exception( $error );
|
Â
|
|
368 |
Â
}
|
Â
|
|
Â
|
|
369 |
Â
}
|
370 |
Â
|
371 |
Â
/**
|
CdnEngine_Base.php
CHANGED
@@ -126,15 +126,9 @@ class CdnEngine_Base {
|
|
126 |
Â
|
127 |
Â
/**
|
128 |
Â
* Create bucket / container for some CDN engines
|
129 |
-
*
|
130 |
-
* @param string $container_id
|
131 |
-
* @param string $error
|
132 |
-
* @return boolean
|
133 |
Â
*/
|
134 |
-
function create_container(
|
135 |
-
|
136 |
-
|
137 |
-
return false;
|
138 |
Â
}
|
139 |
Â
|
140 |
Â
/**
|
126 |
Â
|
127 |
Â
/**
|
128 |
Â
* Create bucket / container for some CDN engines
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
129 |
Â
*/
|
130 |
+
function create_container() {
|
131 |
+
throw new \Exception( 'Not implemented.' );
|
Â
|
|
Â
|
|
132 |
Â
}
|
133 |
Â
|
134 |
Â
/**
|
CdnEngine_CloudFront.php
ADDED
@@ -0,0 +1,345 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace W3TC;
|
3 |
+
|
4 |
+
if ( !defined( 'W3TC_SKIPLIB_AWS' ) ) {
|
5 |
+
require_once W3TC_LIB_DIR . '/Aws/aws-autoloader.php';
|
6 |
+
}
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Amazon CloudFront (S3 origin) CDN engine
|
10 |
+
*/
|
11 |
+
class CdnEngine_CloudFront extends CdnEngine_Base {
|
12 |
+
private $s3;
|
13 |
+
private $api;
|
14 |
+
|
15 |
+
function __construct( $config = array() ) {
|
16 |
+
$config = array_merge( array(
|
17 |
+
'id' => ''
|
18 |
+
), $config );
|
19 |
+
|
20 |
+
parent::__construct( $config );
|
21 |
+
|
22 |
+
$this->s3 = new CdnEngine_S3( $config );
|
23 |
+
}
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Initialize
|
27 |
+
*/
|
28 |
+
function _init() {
|
29 |
+
if ( !is_null( $this->api ) ) {
|
30 |
+
return;
|
31 |
+
}
|
32 |
+
|
33 |
+
$credentials = new \Aws\Credentials\Credentials(
|
34 |
+
$this->_config['key'],
|
35 |
+
$this->_config['secret'] );
|
36 |
+
|
37 |
+
$this->api = new \Aws\CloudFront\CloudFrontClient( array(
|
38 |
+
'credentials' => $credentials,
|
39 |
+
'region' => $this->_config['bucket_location'],
|
40 |
+
'version' => '2018-11-05'
|
41 |
+
)
|
42 |
+
);
|
43 |
+
|
44 |
+
return true;
|
45 |
+
}
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Formats URL
|
49 |
+
*/
|
50 |
+
function _format_url( $path ) {
|
51 |
+
// the same limitation as S3 has
|
52 |
+
return $this->s3->_format_url( $path );
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Upload files
|
57 |
+
*
|
58 |
+
* @param array $files
|
59 |
+
* @param array $results
|
60 |
+
* @param boolean $force_rewrite
|
61 |
+
* @return boolean
|
62 |
+
*/
|
63 |
+
function upload( $files, &$results, $force_rewrite = false,
|
64 |
+
$timeout_time = NULL ) {
|
65 |
+
return $this->s3->upload( $files, $results, $force_rewrite,
|
66 |
+
$timeout_time );
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
* Delete files from CDN
|
71 |
+
*
|
72 |
+
* @param array $files
|
73 |
+
* @param array $results
|
74 |
+
* @return boolean
|
75 |
+
*/
|
76 |
+
function delete( $files, &$results ) {
|
77 |
+
return $this->s3->delete( $files, $results );
|
78 |
+
}
|
79 |
+
|
80 |
+
/**
|
81 |
+
* Purge files from CDN
|
82 |
+
*
|
83 |
+
* @param array $files
|
84 |
+
* @param array $results
|
85 |
+
* @return boolean
|
86 |
+
*/
|
87 |
+
function purge( $files, &$results ) {
|
88 |
+
if ( !$this->s3->upload( $files, $results, true ) ) {
|
89 |
+
return false;
|
90 |
+
}
|
91 |
+
|
92 |
+
try {
|
93 |
+
$this->_init();
|
94 |
+
$dist = $this->_get_distribution();
|
95 |
+
} catch ( \Exception $ex ) {
|
96 |
+
$results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, $ex->getMessage() );
|
97 |
+
return false;
|
98 |
+
}
|
99 |
+
|
100 |
+
$paths = array();
|
101 |
+
|
102 |
+
foreach ( $files as $file ) {
|
103 |
+
$remote_file = $file['remote_path'];
|
104 |
+
$paths[] = '/' . $remote_file;
|
105 |
+
}
|
106 |
+
|
107 |
+
try {
|
108 |
+
$invalidation = $this->api->createInvalidation( array(
|
109 |
+
'DistributionId' => $dist['Id'],
|
110 |
+
'InvalidationBatch' => array(
|
111 |
+
'CallerReference' => 'w3tc-' . microtime(),
|
112 |
+
'Paths' => array(
|
113 |
+
'Items' => $paths,
|
114 |
+
'Quantity' => count( $paths ),
|
115 |
+
),
|
116 |
+
)
|
117 |
+
)
|
118 |
+
);
|
119 |
+
} catch ( \Exception $ex ) {
|
120 |
+
$results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT,
|
121 |
+
sprintf( 'Unable to create invalidation batch (%s).',
|
122 |
+
$ex->getMessage() ) );
|
123 |
+
|
124 |
+
return false;
|
125 |
+
}
|
126 |
+
|
127 |
+
$results = $this->_get_results( $files, W3TC_CDN_RESULT_OK, 'OK' );
|
128 |
+
return true;
|
129 |
+
}
|
130 |
+
|
131 |
+
/**
|
132 |
+
* Returns origin
|
133 |
+
*/
|
134 |
+
function _get_origin() {
|
135 |
+
return sprintf( '%s.s3.amazonaws.com', $this->_config['bucket'] );
|
136 |
+
}
|
137 |
+
|
138 |
+
/**
|
139 |
+
* Returns array of CDN domains
|
140 |
+
*/
|
141 |
+
public function get_domains() {
|
142 |
+
if ( !empty( $this->_config['cname'] ) ) {
|
143 |
+
return (array) $this->_config['cname'];
|
144 |
+
} elseif ( !empty( $this->_config['id'] ) ) {
|
145 |
+
$domain = sprintf( '%s.cloudfront.net', $this->_config['id'] );
|
146 |
+
|
147 |
+
return array(
|
148 |
+
$domain
|
149 |
+
);
|
150 |
+
}
|
151 |
+
|
152 |
+
return array();
|
153 |
+
}
|
154 |
+
|
155 |
+
/**
|
156 |
+
* Test CDN connectivity
|
157 |
+
*/
|
158 |
+
function test( &$error ) {
|
159 |
+
$this->_init();
|
160 |
+
if ( !$this->s3->test( $error ) ) {
|
161 |
+
return false;
|
162 |
+
}
|
163 |
+
|
164 |
+
/**
|
165 |
+
* Search active CF distribution
|
166 |
+
*/
|
167 |
+
$dists = $this->api->listDistributions();
|
168 |
+
|
169 |
+
if ( !isset( $dists['DistributionList']['Items'] ) ) {
|
170 |
+
$error = 'Unable to list distributions.';
|
171 |
+
return false;
|
172 |
+
}
|
173 |
+
|
174 |
+
if ( !count( $dists['DistributionList']['Items'] ) ) {
|
175 |
+
$error = 'No distributions found.';
|
176 |
+
|
177 |
+
return false;
|
178 |
+
}
|
179 |
+
|
180 |
+
$dist = $this->_get_distribution( $dists );
|
181 |
+
if ( $dist["Status"] != 'Deployed' ) {
|
182 |
+
$error = sprintf( 'Distribution status is not Deployed, but "%s".', $dist["Status"] );
|
183 |
+
return false;
|
184 |
+
}
|
185 |
+
|
186 |
+
if ( !$dist['Enabled'] ) {
|
187 |
+
$error = sprintf( 'Distribution for origin "%s" is disabled.', $origin );
|
188 |
+
return false;
|
189 |
+
}
|
190 |
+
|
191 |
+
if ( !empty( $this->_config['cname'] ) ) {
|
192 |
+
$domains = (array) $this->_config['cname'];
|
193 |
+
$cnames = ( isset( $dist['Aliases']['Items'] ) ? (array) $dist['Aliases']['Items'] : array() );
|
194 |
+
|
195 |
+
foreach ( $domains as $domain ) {
|
196 |
+
$_domains = array_map( 'trim', explode( ',', $domain ) );
|
197 |
+
|
198 |
+
foreach ( $_domains as $_domain ) {
|
199 |
+
if ( !in_array( $_domain, $cnames ) ) {
|
200 |
+
$error = sprintf( 'Domain name %s is not in distribution <acronym title="Canonical Name">CNAME</acronym> list.', $_domain );
|
201 |
+
|
202 |
+
return false;
|
203 |
+
}
|
204 |
+
}
|
205 |
+
}
|
206 |
+
} elseif ( !empty( $this->_config['id'] ) ) {
|
207 |
+
$domain = $this->get_domain();
|
208 |
+
|
209 |
+
if ( $domain != $dist['DomainName'] ) {
|
210 |
+
$error = sprintf( 'Distribution domain name mismatch (%s != %s).', $domain, $dist['DomainName'] );
|
211 |
+
|
212 |
+
return false;
|
213 |
+
}
|
214 |
+
}
|
215 |
+
|
216 |
+
return true;
|
217 |
+
}
|
218 |
+
|
219 |
+
/**
|
220 |
+
* Create bucket
|
221 |
+
*/
|
222 |
+
function create_container() {
|
223 |
+
$this->_init();
|
224 |
+
$this->s3->create_container();
|
225 |
+
|
226 |
+
// plugin cant set CNAMEs list since it CloudFront requires
|
227 |
+
// certificate to be specified associated with it
|
228 |
+
$cnames = array();
|
229 |
+
|
230 |
+
// make distibution
|
231 |
+
$originDomain = $this->_get_origin();
|
232 |
+
|
233 |
+
try {
|
234 |
+
$result = $this->api->createDistribution(array(
|
235 |
+
'DistributionConfig' => array(
|
236 |
+
'CallerReference' => $originDomain,
|
237 |
+
'Comment' => 'Created by W3-Total-Cache',
|
238 |
+
'DefaultCacheBehavior' => array(
|
239 |
+
'AllowedMethods' => array(
|
240 |
+
'CachedMethods' => array(
|
241 |
+
'Items' => array( 'HEAD', 'GET' ),
|
242 |
+
'Quantity' => 2,
|
243 |
+
),
|
244 |
+
'Items' => array( 'HEAD', 'GET' ),
|
245 |
+
'Quantity' => 2,
|
246 |
+
),
|
247 |
+
'Compress' => true,
|
248 |
+
'DefaultTTL' => 86400,
|
249 |
+
'FieldLevelEncryptionId' => '',
|
250 |
+
'ForwardedValues' => array(
|
251 |
+
'Cookies' => array(
|
252 |
+
'Forward' => 'none',
|
253 |
+
),
|
254 |
+
'Headers' => array(
|
255 |
+
'Quantity' => 0,
|
256 |
+
),
|
257 |
+
'QueryString' => false,
|
258 |
+
'QueryStringCacheKeys' => array(
|
259 |
+
'Quantity' => 0,
|
260 |
+
),
|
261 |
+
),
|
262 |
+
'LambdaFunctionAssociations' => array( 'Quantity' => 0),
|
263 |
+
'MinTTL' => 0,
|
264 |
+
'SmoothStreaming' => false,
|
265 |
+
'TargetOriginId' => $originDomain,
|
266 |
+
'TrustedSigners' => array(
|
267 |
+
'Enabled' => false,
|
268 |
+
'Quantity' => 0,
|
269 |
+
),
|
270 |
+
'ViewerProtocolPolicy' => 'allow-all',
|
271 |
+
),
|
272 |
+
'Enabled' => true,
|
273 |
+
'Origins' => array(
|
274 |
+
'Items' => array(
|
275 |
+
array(
|
276 |
+
'DomainName' => $originDomain,
|
277 |
+
'Id' => $originDomain,
|
278 |
+
'OriginPath' => '',
|
279 |
+
'CustomHeaders' => array( 'Quantity' => 0 ),
|
280 |
+
'S3OriginConfig' => array(
|
281 |
+
'OriginAccessIdentity' => ''
|
282 |
+
),
|
283 |
+
),
|
284 |
+
),
|
285 |
+
'Quantity' => 1,
|
286 |
+
),
|
287 |
+
'Aliases' => array(
|
288 |
+
'Items' => $cnames,
|
289 |
+
'Quantity' => count( $cnames )
|
290 |
+
)
|
291 |
+
)
|
292 |
+
));
|
293 |
+
|
294 |
+
// extract domain dynamic part stored later in a config
|
295 |
+
$domain = $result['Distribution']['DomainName'];
|
296 |
+
$container_id = '';
|
297 |
+
if ( preg_match( '~^(.+)\.cloudfront\.net$~', $domain, $matches ) ) {
|
298 |
+
$container_id = $matches[1];
|
299 |
+
}
|
300 |
+
|
301 |
+
return $container_id;
|
302 |
+
|
303 |
+
} catch ( \Exception $ex ) {
|
304 |
+
throw new \Exception( sprintf(
|
305 |
+
'Unable to create distribution for origin %s: %s', $originDomain,
|
306 |
+
$ex->getMessage() ) );
|
307 |
+
}
|
308 |
+
}
|
309 |
+
|
310 |
+
/**
|
311 |
+
* Returns via string
|
312 |
+
*
|
313 |
+
* @return string
|
314 |
+
*/
|
315 |
+
function get_via() {
|
316 |
+
$domain = $this->get_domain();
|
317 |
+
$via = ( $domain ? $domain : 'N/A' );
|
318 |
+
|
319 |
+
return sprintf( 'Amazon Web Services: CloudFront: %s', $via );
|
320 |
+
}
|
321 |
+
|
322 |
+
private function _get_distribution( $dists = null ) {
|
323 |
+
if ( is_null( $dists ) ) {
|
324 |
+
$dists = $this->api->listDistributions();
|
325 |
+
}
|
326 |
+
|
327 |
+
if ( !isset( $dists['DistributionList']['Items'] ) ||
|
328 |
+
!count( $dists['DistributionList']['Items'] ) ) {
|
329 |
+
throw new \Exception( 'No distributions found.' );
|
330 |
+
}
|
331 |
+
|
332 |
+
$dist = false;
|
333 |
+
$origin = $this->_get_origin();
|
334 |
+
|
335 |
+
$items = $dists['DistributionList']['Items'];
|
336 |
+
foreach ( $items as $dist ) {
|
337 |
+
if ( isset( $dist['Origins']['Items'][0]['DomainName'] ) &&
|
338 |
+
$dist['Origins']['Items'][0]['DomainName'] == $origin ) {
|
339 |
+
return $dist;
|
340 |
+
}
|
341 |
+
}
|
342 |
+
|
343 |
+
throw new \Exception( sprintf( 'Distribution for origin "%s" not found.', $origin ) );
|
344 |
+
}
|
345 |
+
}
|
CdnEngine_Mirror_CloudFront.php
ADDED
@@ -0,0 +1,324 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace W3TC;
|
3 |
+
|
4 |
+
if ( !defined( 'W3TC_SKIPLIB_AWS' ) ) {
|
5 |
+
require_once W3TC_LIB_DIR . '/Aws/aws-autoloader.php';
|
6 |
+
}
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Amazon CloudFront (mirror) CDN engine
|
10 |
+
*/
|
11 |
+
class CdnEngine_Mirror_CloudFront extends CdnEngine_Mirror {
|
12 |
+
private $api;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Constructor
|
16 |
+
*/
|
17 |
+
function __construct( $config = array() ) {
|
18 |
+
parent::__construct( $config );
|
19 |
+
}
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Initializes S3 object
|
23 |
+
*
|
24 |
+
* @param string $error
|
25 |
+
* @return bool
|
26 |
+
*/
|
27 |
+
function _init() {
|
28 |
+
if ( !is_null( $this->api ) ) {
|
29 |
+
return;
|
30 |
+
}
|
31 |
+
|
32 |
+
$credentials = new \Aws\Credentials\Credentials(
|
33 |
+
$this->_config['key'],
|
34 |
+
$this->_config['secret'] );
|
35 |
+
|
36 |
+
$this->api = new \Aws\CloudFront\CloudFrontClient( array(
|
37 |
+
'credentials' => $credentials,
|
38 |
+
'region' => 'us-east-1',
|
39 |
+
'version' => '2018-11-05'
|
40 |
+
)
|
41 |
+
);
|
42 |
+
|
43 |
+
return true;
|
44 |
+
}
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Returns origin
|
48 |
+
*
|
49 |
+
* @return string
|
50 |
+
*/
|
51 |
+
function _get_origin() {
|
52 |
+
return Util_Environment::host_port();
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Purge files from CDN
|
57 |
+
*
|
58 |
+
* @param array $files
|
59 |
+
* @param array $results
|
60 |
+
* @return boolean
|
61 |
+
*/
|
62 |
+
function purge( $files, &$results ) {
|
63 |
+
try {
|
64 |
+
$this->_init();
|
65 |
+
$dist = $this->_get_distribution();
|
66 |
+
} catch ( \Exception $ex ) {
|
67 |
+
$results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, $ex->getMessage() );
|
68 |
+
return false;
|
69 |
+
}
|
70 |
+
|
71 |
+
$paths = array();
|
72 |
+
|
73 |
+
foreach ( $files as $file ) {
|
74 |
+
$remote_file = $file['remote_path'];
|
75 |
+
$paths[] = '/' . $remote_file;
|
76 |
+
}
|
77 |
+
|
78 |
+
try {
|
79 |
+
$invalidation = $this->api->createInvalidation( array(
|
80 |
+
'DistributionId' => $dist['Id'],
|
81 |
+
'InvalidationBatch' => array(
|
82 |
+
'CallerReference' => 'w3tc-' . microtime(),
|
83 |
+
'Paths' => array(
|
84 |
+
'Items' => $paths,
|
85 |
+
'Quantity' => count( $paths ),
|
86 |
+
),
|
87 |
+
)
|
88 |
+
)
|
89 |
+
);
|
90 |
+
} catch ( \Exception $ex ) {
|
91 |
+
$results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT,
|
92 |
+
sprintf( 'Unable to create invalidation batch (%s).',
|
93 |
+
$ex->getMessage() ) );
|
94 |
+
|
95 |
+
return false;
|
96 |
+
}
|
97 |
+
|
98 |
+
$results = $this->_get_results( $files, W3TC_CDN_RESULT_OK, 'OK' );
|
99 |
+
return true;
|
100 |
+
}
|
101 |
+
|
102 |
+
/**
|
103 |
+
* Purge CDN completely
|
104 |
+
*
|
105 |
+
* @param unknown $results
|
106 |
+
* @return bool
|
107 |
+
*/
|
108 |
+
function purge_all( &$results ) {
|
109 |
+
return $this->purge( array( 'remote_path' => '*' ), $results );
|
110 |
+
}
|
111 |
+
|
112 |
+
/**
|
113 |
+
* Returns array of CDN domains
|
114 |
+
*
|
115 |
+
* @return array
|
116 |
+
*/
|
117 |
+
function get_domains() {
|
118 |
+
if ( !empty( $this->_config['cname'] ) ) {
|
119 |
+
return (array) $this->_config['cname'];
|
120 |
+
} elseif ( !empty( $this->_config['id'] ) ) {
|
121 |
+
$domain = sprintf( '%s.cloudfront.net', $this->_config['id'] );
|
122 |
+
|
123 |
+
return array(
|
124 |
+
$domain
|
125 |
+
);
|
126 |
+
}
|
127 |
+
|
128 |
+
return array();
|
129 |
+
}
|
130 |
+
|
131 |
+
/**
|
132 |
+
* Tests CF
|
133 |
+
*
|
134 |
+
* @param string $error
|
135 |
+
* @return boolean
|
136 |
+
*/
|
137 |
+
function test( &$error ) {
|
138 |
+
$this->_init();
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Search active CF distribution
|
142 |
+
*/
|
143 |
+
$dists = $this->api->listDistributions();
|
144 |
+
|
145 |
+
if ( !isset( $dists['DistributionList']['Items'] ) ) {
|
146 |
+
$error = 'Unable to list distributions.';
|
147 |
+
return false;
|
148 |
+
}
|
149 |
+
|
150 |
+
if ( !count( $dists['DistributionList']['Items'] ) ) {
|
151 |
+
$error = 'No distributions found.';
|
152 |
+
|
153 |
+
return false;
|
154 |
+
}
|
155 |
+
|
156 |
+
$dist = $this->_get_distribution( $dists );
|
157 |
+
if ( $dist["Status"] != 'Deployed' ) {
|
158 |
+
$error = sprintf( 'Distribution status is not Deployed, but "%s".', $dist["Status"] );
|
159 |
+
return false;
|
160 |
+
}
|
161 |
+
|
162 |
+
if ( !$dist['Enabled'] ) {
|
163 |
+
$error = sprintf( 'Distribution for origin "%s" is disabled.', $origin );
|
164 |
+
return false;
|
165 |
+
}
|
166 |
+
|
167 |
+
if ( !empty( $this->_config['cname'] ) ) {
|
168 |
+
$domains = (array) $this->_config['cname'];
|
169 |
+
$cnames = ( isset( $dist['Aliases']['Items'] ) ? (array) $dist['Aliases']['Items'] : array() );
|
170 |
+
|
171 |
+
foreach ( $domains as $domain ) {
|
172 |
+
$_domains = array_map( 'trim', explode( ',', $domain ) );
|
173 |
+
|
174 |
+
foreach ( $_domains as $_domain ) {
|
175 |
+
if ( !in_array( $_domain, $cnames ) ) {
|
176 |
+
$error = sprintf( 'Domain name %s is not in distribution <acronym title="Canonical Name">CNAME</acronym> list.', $_domain );
|
177 |
+
|
178 |
+
return false;
|
179 |
+
}
|
180 |
+
}
|
181 |
+
}
|
182 |
+
} elseif ( !empty( $this->_config['id'] ) ) {
|
183 |
+
$domain = $this->get_domain();
|
184 |
+
|
185 |
+
if ( $domain != $dist['DomainName'] ) {
|
186 |
+
$error = sprintf( 'Distribution domain name mismatch (%s != %s).', $domain, $dist['DomainName'] );
|
187 |
+
|
188 |
+
return false;
|
189 |
+
}
|
190 |
+
}
|
191 |
+
|
192 |
+
return true;
|
193 |
+
}
|
194 |
+
|
195 |
+
/**
|
196 |
+
* Create distribution
|
197 |
+
*/
|
198 |
+
function create_container() {
|
199 |
+
$this->_init();
|
200 |
+
|
201 |
+
// plugin cant set CNAMEs list since it CloudFront requires
|
202 |
+
// certificate to be specified associated with it
|
203 |
+
$cnames = array();
|
204 |
+
|
205 |
+
// make distibution
|
206 |
+
$originDomain = $this->_get_origin();
|
207 |
+
|
208 |
+
try {
|
209 |
+
$result = $this->api->createDistribution( array(
|
210 |
+
'DistributionConfig' => array(
|
211 |
+
'CallerReference' => $originDomain,
|
212 |
+
'Comment' => 'Created by W3-Total-Cache',
|
213 |
+
'DefaultCacheBehavior' => array(
|
214 |
+
'AllowedMethods' => array(
|
215 |
+
'CachedMethods' => array(
|
216 |
+
'Items' => array( 'HEAD', 'GET' ),
|
217 |
+
'Quantity' => 2,
|
218 |
+
),
|
219 |
+
'Items' => array( 'HEAD', 'GET' ),
|
220 |
+
'Quantity' => 2,
|
221 |
+
),
|
222 |
+
'Compress' => true,
|
223 |
+
'DefaultTTL' => 86400,
|
224 |
+
'FieldLevelEncryptionId' => '',
|
225 |
+
'ForwardedValues' => array(
|
226 |
+
'Cookies' => array(
|
227 |
+
'Forward' => 'none',
|
228 |
+
),
|
229 |
+
'Headers' => array(
|
230 |
+
'Quantity' => 0,
|
231 |
+
),
|
232 |
+
'QueryString' => false,
|
233 |
+
'QueryStringCacheKeys' => array(
|
234 |
+
'Quantity' => 0,
|
235 |
+
),
|
236 |
+
),
|
237 |
+
'LambdaFunctionAssociations' => array( 'Quantity' => 0),
|
238 |
+
'MinTTL' => 0,
|
239 |
+
'SmoothStreaming' => false,
|
240 |
+
'TargetOriginId' => $originDomain,
|
241 |
+
'TrustedSigners' => array(
|
242 |
+
'Enabled' => false,
|
243 |
+
'Quantity' => 0,
|
244 |
+
),
|
245 |
+
'ViewerProtocolPolicy' => 'allow-all',
|
246 |
+
),
|
247 |
+
'Enabled' => true,
|
248 |
+
'Origins' => array(
|
249 |
+
'Items' => array(
|
250 |
+
array(
|
251 |
+
'DomainName' => $originDomain,
|
252 |
+
'Id' => $originDomain,
|
253 |
+
'OriginPath' => '',
|
254 |
+
'CustomHeaders' => array( 'Quantity' => 0 ),
|
255 |
+
'CustomOriginConfig' => array(
|
256 |
+
'HTTPPort' => 80,
|
257 |
+
'HTTPSPort' => 443,
|
258 |
+
'OriginProtocolPolicy' => 'match-viewer'
|
259 |
+
),
|
260 |
+
),
|
261 |
+
),
|
262 |
+
'Quantity' => 1,
|
263 |
+
),
|
264 |
+
'Aliases' => array(
|
265 |
+
'Items' => $cnames,
|
266 |
+
'Quantity' => count( $cnames )
|
267 |
+
)
|
268 |
+
)
|
269 |
+
));
|
270 |
+
|
271 |
+
// extract domain dynamic part stored later in a config
|
272 |
+
$domain = $result['Distribution']['DomainName'];
|
273 |
+
$container_id = '';
|
274 |
+
if ( preg_match( '~^(.+)\.cloudfront\.net$~', $domain, $matches ) ) {
|
275 |
+
$container_id = $matches[1];
|
276 |
+
}
|
277 |
+
|
278 |
+
return $container_id;
|
279 |
+
|
280 |
+
} catch ( \Aws\Exception\AwsException $ex ) {
|
281 |
+
throw new \Exception( sprintf(
|
282 |
+
'Unable to create distribution for origin %s: %s', $originDomain,
|
283 |
+
$ex->getAwsErrorMessage() ) );
|
284 |
+
} catch ( \Exception $ex ) {
|
285 |
+
throw new \Exception( sprintf(
|
286 |
+
'Unable to create distribution for origin %s: %s', $originDomain,
|
287 |
+
$ex->getMessage() ) );
|
288 |
+
}
|
289 |
+
}
|
290 |
+
|
291 |
+
/**
|
292 |
+
* Returns via string
|
293 |
+
*/
|
294 |
+
function get_via() {
|
295 |
+
$domain = $this->get_domain();
|
296 |
+
$via = ( $domain ? $domain : 'N/A' );
|
297 |
+
|
298 |
+
return sprintf( 'Amazon Web Services: CloudFront: %s', $via );
|
299 |
+
}
|
300 |
+
|
301 |
+
private function _get_distribution( $dists = null ) {
|
302 |
+
if ( is_null( $dists ) ) {
|
303 |
+
$dists = $this->api->listDistributions();
|
304 |
+
}
|
305 |
+
|
306 |
+
if ( !isset( $dists['DistributionList']['Items'] ) ||
|
307 |
+
!count( $dists['DistributionList']['Items'] ) ) {
|
308 |
+
throw new \Exception( 'No distributions found.' );
|
309 |
+
}
|
310 |
+
|
311 |
+
$dist = false;
|
312 |
+
$origin = $this->_get_origin();
|
313 |
+
|
314 |
+
$items = $dists['DistributionList']['Items'];
|
315 |
+
foreach ( $items as $dist ) {
|
316 |
+
if ( isset( $dist['Origins']['Items'][0]['DomainName'] ) &&
|
317 |
+
$dist['Origins']['Items'][0]['DomainName'] == $origin ) {
|
318 |
+
return $dist;
|
319 |
+
}
|
320 |
+
}
|
321 |
+
|
322 |
+
throw new \Exception( sprintf( 'Distribution for origin "%s" not found.', $origin ) );
|
323 |
+
}
|
324 |
+
}
|
CdnEngine_S3.php
CHANGED
@@ -1,31 +1,17 @@
|
|
1 |
Â
<?php
|
2 |
Â
namespace W3TC;
|
3 |
Â
|
4 |
-
|
5 |
-
|
6 |
-
*/
|
7 |
-
|
8 |
-
if ( !class_exists( 'S3' ) ) {
|
9 |
-
require_once W3TC_LIB_DIR . '/S3.php';
|
10 |
Â
}
|
11 |
Â
|
12 |
Â
/**
|
13 |
-
*
|
14 |
Â
*/
|
15 |
Â
class CdnEngine_S3 extends CdnEngine_Base {
|
16 |
-
|
17 |
-
* S3 object
|
18 |
-
*
|
19 |
-
* @var S3
|
20 |
-
*/
|
21 |
-
var $_s3 = null;
|
22 |
Â
|
23 |
-
|
24 |
-
* PHP5 Constructor
|
25 |
-
*
|
26 |
-
* @param array $config
|
27 |
-
*/
|
28 |
-
function __construct( $config = array() ) {
|
29 |
Â
$config = array_merge( array(
|
30 |
Â
'key' => '',
|
31 |
Â
'secret' => '',
|
@@ -39,9 +25,6 @@ class CdnEngine_S3 extends CdnEngine_Base {
|
|
39 |
Â
|
40 |
Â
/**
|
41 |
Â
* Formats URL
|
42 |
-
*
|
43 |
-
* @param string $path
|
44 |
-
* @return string
|
45 |
Â
*/
|
46 |
Â
function _format_url( $path ) {
|
47 |
Â
$domain = $this->get_domain( $path );
|
@@ -65,40 +48,33 @@ class CdnEngine_S3 extends CdnEngine_Base {
|
|
65 |
Â
* @param string $error
|
66 |
Â
* @return boolean
|
67 |
Â
*/
|
68 |
-
function _init(
|
69 |
-
if (
|
70 |
-
|
Â
|
|
71 |
Â
|
72 |
-
|
Â
|
|
73 |
Â
}
|
74 |
Â
|
75 |
Â
if ( empty( $this->_config['secret'] ) ) {
|
76 |
-
|
77 |
-
|
78 |
-
return false;
|
79 |
Â
}
|
80 |
Â
|
81 |
Â
if ( empty( $this->_config['bucket'] ) ) {
|
82 |
-
|
83 |
-
|
84 |
-
return false;
|
85 |
Â
}
|
86 |
Â
|
87 |
-
|
88 |
-
$
|
89 |
-
$
|
90 |
-
} else {
|
91 |
-
$region = $this->_config['bucket_location'];
|
92 |
-
$endpoint = 's3.dualstack.' . $region . '.amazonaws.com';
|
93 |
-
}
|
94 |
Â
|
95 |
-
$this->
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
return true;
|
102 |
Â
}
|
103 |
Â
|
104 |
Â
/**
|
@@ -109,13 +85,14 @@ class CdnEngine_S3 extends CdnEngine_Base {
|
|
109 |
Â
* @param boolean $force_rewrite
|
110 |
Â
* @return boolean
|
111 |
Â
*/
|
112 |
-
function upload( $files, &$results, $force_rewrite = false,
|
113 |
Â
$timeout_time = NULL ) {
|
114 |
Â
$error = null;
|
115 |
Â
|
116 |
-
|
117 |
-
$
|
118 |
-
|
Â
|
|
119 |
Â
return false;
|
120 |
Â
}
|
121 |
Â
|
@@ -148,7 +125,7 @@ class CdnEngine_S3 extends CdnEngine_Base {
|
|
148 |
Â
* @param boolean $force_rewrite
|
149 |
Â
* @return array
|
150 |
Â
*/
|
151 |
-
function _upload( $file, $force_rewrite = false ) {
|
152 |
Â
$local_path = $file['local_path'];
|
153 |
Â
$remote_path = $file['remote_path'];
|
154 |
Â
|
@@ -157,52 +134,51 @@ class CdnEngine_S3 extends CdnEngine_Base {
|
|
157 |
Â
W3TC_CDN_RESULT_ERROR, 'Source file not found.', $file );
|
158 |
Â
}
|
159 |
Â
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
172 |
Â
}
|
173 |
Â
}
|
174 |
-
}
|
175 |
Â
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
Â
|
|
Â
|
|
181 |
Â
|
182 |
-
if ( $result ) {
|
183 |
Â
return $this->_get_result( $local_path, $remote_path,
|
184 |
Â
W3TC_CDN_RESULT_OK, 'OK', $file );
|
185 |
-
}
|
Â
|
|
186 |
Â
|
187 |
-
|
188 |
-
|
189 |
-
} else {
|
190 |
-
$error = sprintf( 'Unable to put object (%s).', $this->_get_last_error() );
|
191 |
Â
}
|
192 |
-
|
193 |
-
return $this->_get_result( $local_path, $remote_path,
|
194 |
-
W3TC_CDN_RESULT_ERROR, $error, $file );
|
195 |
Â
}
|
196 |
Â
|
197 |
Â
/**
|
198 |
Â
* Uploads gzip version of file
|
199 |
-
*
|
200 |
-
* @param string $local_path
|
201 |
-
* @param string $remote_path
|
202 |
-
* @param boolean $force_rewrite
|
203 |
-
* @return array
|
204 |
Â
*/
|
205 |
-
function _upload_gzip( $file, $force_rewrite = false ) {
|
206 |
Â
$local_path = $file['local_path'];
|
207 |
Â
$remote_path = $file['remote_path_gzip'];
|
208 |
Â
|
@@ -225,45 +201,70 @@ class CdnEngine_S3 extends CdnEngine_Base {
|
|
225 |
Â
|
226 |
Â
$data = gzencode( $contents );
|
227 |
Â
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
240 |
Â
}
|
241 |
Â
}
|
242 |
-
}
|
243 |
Â
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
Â
|
|
Â
|
|
Â
|
|
253 |
Â
|
254 |
-
if ( $result ) {
|
255 |
Â
return $this->_get_result( $local_path, $remote_path,
|
256 |
Â
W3TC_CDN_RESULT_OK, 'OK', $file );
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
257 |
Â
}
|
Â
|
|
258 |
Â
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
263 |
Â
}
|
264 |
Â
|
265 |
-
return $this->
|
266 |
-
W3TC_CDN_RESULT_ERROR, $error, $file );
|
267 |
Â
}
|
268 |
Â
|
269 |
Â
/**
|
@@ -273,12 +274,13 @@ class CdnEngine_S3 extends CdnEngine_Base {
|
|
273 |
Â
* @param array $results
|
274 |
Â
* @return boolean
|
275 |
Â
*/
|
276 |
-
function delete( $files, &$results ) {
|
277 |
Â
$error = null;
|
278 |
Â
|
279 |
-
|
280 |
-
$
|
281 |
-
|
Â
|
|
282 |
Â
return false;
|
283 |
Â
}
|
284 |
Â
|
@@ -286,36 +288,36 @@ class CdnEngine_S3 extends CdnEngine_Base {
|
|
286 |
Â
$local_path = $file['local_path'];
|
287 |
Â
$remote_path = $file['remote_path'];
|
288 |
Â
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
Â
$results[] = $this->_get_result( $local_path, $remote_path,
|
295 |
Â
W3TC_CDN_RESULT_OK, 'OK', $file );
|
296 |
-
}
|
297 |
Â
$results[] = $this->_get_result( $local_path, $remote_path,
|
298 |
Â
W3TC_CDN_RESULT_ERROR,
|
299 |
Â
sprintf( 'Unable to delete object (%s).',
|
300 |
-
$
|
301 |
Â
$file );
|
302 |
Â
}
|
303 |
Â
|
304 |
Â
if ( $this->_config['compression'] ) {
|
305 |
Â
$remote_path_gzip = $remote_path . $this->_gzip_extension;
|
306 |
Â
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
$results[] = $this->_get_result( $local_path,
|
313 |
-
|
314 |
-
}
|
315 |
-
$results[] = $this->_get_result( $local_path,
|
316 |
-
|
317 |
Â
sprintf( 'Unable to delete object (%s).',
|
318 |
-
$
|
319 |
Â
$file );
|
320 |
Â
}
|
321 |
Â
}
|
@@ -325,80 +327,57 @@ class CdnEngine_S3 extends CdnEngine_Base {
|
|
325 |
Â
}
|
326 |
Â
|
327 |
Â
/**
|
328 |
-
*
|
329 |
-
*
|
330 |
-
* @param string $error
|
331 |
-
* @return boolean
|
332 |
Â
*/
|
333 |
-
function test( &$error ) {
|
334 |
Â
if ( !parent::test( $error ) ) {
|
335 |
Â
return false;
|
336 |
Â
}
|
337 |
Â
|
338 |
-
$
|
339 |
-
|
340 |
-
if ( !$this->_init( $error ) ) {
|
341 |
-
return false;
|
342 |
-
}
|
343 |
-
|
344 |
-
$this->_set_error_handler();
|
345 |
-
|
346 |
-
$buckets = @$this->_s3->listBuckets();
|
347 |
-
|
348 |
-
if ( $buckets === false ) {
|
349 |
-
$error = sprintf( 'Unable to list buckets (%s).', $this->_get_last_error() );
|
350 |
-
|
351 |
-
$this->_restore_error_handler();
|
352 |
-
|
353 |
-
return false;
|
354 |
-
}
|
355 |
-
|
356 |
-
if ( !in_array( $this->_config['bucket'], (array) $buckets ) ) {
|
357 |
-
$error = sprintf( 'Bucket doesn\'t exist: %s.', $this->_config['bucket'] );
|
358 |
Â
|
359 |
-
|
Â
|
|
360 |
Â
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
if ( strpos( $this->_get_last_error(), 'AWS4-HMAC-SHA256' ) !== false ) {
|
366 |
-
$error = "Bucket location region is incorrect. Please select the right one.";
|
367 |
-
} else {
|
368 |
-
$error = sprintf( 'Unable to put object (%s).', $this->_get_last_error() );
|
369 |
Â
}
|
370 |
-
|
371 |
-
$this->_restore_error_handler();
|
372 |
-
|
373 |
-
return false;
|
374 |
Â
}
|
375 |
Â
|
376 |
-
if (
|
377 |
-
|
378 |
-
|
379 |
-
$this->_restore_error_handler();
|
380 |
-
|
381 |
-
return false;
|
382 |
Â
}
|
383 |
Â
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
Â
|
|
Â
|
|
389 |
Â
|
390 |
-
|
391 |
-
|
Â
|
|
Â
|
|
392 |
Â
|
393 |
-
if (
|
394 |
-
$error =
|
395 |
Â
|
396 |
-
$this->
|
Â
|
|
Â
|
|
Â
|
|
397 |
Â
|
398 |
Â
return false;
|
399 |
Â
}
|
400 |
Â
|
401 |
-
$this->
|
Â
|
|
Â
|
|
Â
|
|
402 |
Â
|
403 |
Â
return true;
|
404 |
Â
}
|
@@ -408,7 +387,7 @@ class CdnEngine_S3 extends CdnEngine_Base {
|
|
408 |
Â
*
|
409 |
Â
* @return array
|
410 |
Â
*/
|
411 |
-
function get_domains() {
|
412 |
Â
if ( !empty( $this->_config['cname'] ) ) {
|
413 |
Â
return (array) $this->_config['cname'];
|
414 |
Â
} elseif ( !empty( $this->_config['bucket'] ) ) {
|
@@ -427,61 +406,35 @@ class CdnEngine_S3 extends CdnEngine_Base {
|
|
427 |
Â
*
|
428 |
Â
* @return string
|
429 |
Â
*/
|
430 |
-
function get_via() {
|
431 |
Â
return sprintf( 'Amazon Web Services: S3: %s', parent::get_via() );
|
432 |
Â
}
|
433 |
Â
|
434 |
Â
/**
|
435 |
Â
* Creates bucket
|
436 |
-
*
|
437 |
-
* @param string $container_id
|
438 |
-
* @param string $error
|
439 |
-
* @return boolean
|
440 |
Â
*/
|
441 |
-
function create_container(
|
442 |
-
|
443 |
-
return false;
|
444 |
-
}
|
445 |
-
|
446 |
-
$this->_set_error_handler();
|
447 |
-
|
448 |
-
$buckets = @$this->_s3->listBuckets();
|
449 |
-
|
450 |
-
if ( $buckets === false ) {
|
451 |
-
$error = sprintf( 'Unable to list buckets (%s).', $this->_get_last_error() );
|
452 |
-
|
453 |
-
$this->_restore_error_handler();
|
454 |
-
|
455 |
-
return false;
|
456 |
-
}
|
457 |
Â
|
458 |
-
|
459 |
-
$
|
460 |
-
|
461 |
-
$
|
462 |
-
|
463 |
-
return false;
|
464 |
Â
}
|
465 |
Â
|
466 |
-
|
467 |
-
$this->_config['
|
468 |
-
|
469 |
-
|
470 |
-
if ( !isset( $this->_config['bucket_location'] ) ) {
|
471 |
-
$this->_config['bucket_location'] = \S3::LOCATION_US;
|
472 |
Â
}
|
473 |
Â
|
474 |
-
|
475 |
-
$
|
476 |
-
|
477 |
-
|
478 |
-
|
479 |
-
|
480 |
Â
}
|
481 |
-
|
482 |
-
$this->_restore_error_handler();
|
483 |
-
|
484 |
-
return true;
|
485 |
Â
}
|
486 |
Â
|
487 |
Â
/**
|
@@ -489,7 +442,7 @@ class CdnEngine_S3 extends CdnEngine_Base {
|
|
489 |
Â
*
|
490 |
Â
* @return string W3TC_CDN_HEADER_NONE, W3TC_CDN_HEADER_UPLOADABLE, W3TC_CDN_HEADER_MIRRORING
|
491 |
Â
*/
|
492 |
-
function headers_support() {
|
493 |
Â
return W3TC_CDN_HEADER_UPLOADABLE;
|
494 |
Â
}
|
495 |
Â
}
|
1 |
Â
<?php
|
2 |
Â
namespace W3TC;
|
3 |
Â
|
4 |
+
if ( !defined( 'W3TC_SKIPLIB_AWS' ) ) {
|
5 |
+
require_once W3TC_LIB_DIR . '/Aws/aws-autoloader.php';
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
6 |
Â
}
|
7 |
Â
|
8 |
Â
/**
|
9 |
+
* CDN engine for S3 push type
|
10 |
Â
*/
|
11 |
Â
class CdnEngine_S3 extends CdnEngine_Base {
|
12 |
+
private $api;
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
13 |
Â
|
14 |
+
public function __construct( $config = array() ) {
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
15 |
Â
$config = array_merge( array(
|
16 |
Â
'key' => '',
|
17 |
Â
'secret' => '',
|
25 |
Â
|
26 |
Â
/**
|
27 |
Â
* Formats URL
|
Â
|
|
Â
|
|
Â
|
|
28 |
Â
*/
|
29 |
Â
function _format_url( $path ) {
|
30 |
Â
$domain = $this->get_domain( $path );
|
48 |
Â
* @param string $error
|
49 |
Â
* @return boolean
|
50 |
Â
*/
|
51 |
+
public function _init() {
|
52 |
+
if ( !is_null( $this->api ) ) {
|
53 |
+
return;
|
54 |
+
}
|
55 |
Â
|
56 |
+
if ( empty( $this->_config['key'] ) ) {
|
57 |
+
throw new \Exception( 'Empty access key.' );
|
58 |
Â
}
|
59 |
Â
|
60 |
Â
if ( empty( $this->_config['secret'] ) ) {
|
61 |
+
throw new \Exception( 'Empty secret key.' );
|
Â
|
|
Â
|
|
62 |
Â
}
|
63 |
Â
|
64 |
Â
if ( empty( $this->_config['bucket'] ) ) {
|
65 |
+
throw new \Exception( 'Empty bucket.' );
|
Â
|
|
Â
|
|
66 |
Â
}
|
67 |
Â
|
68 |
+
$credentials = new \Aws\Credentials\Credentials(
|
69 |
+
$this->_config['key'],
|
70 |
+
$this->_config['secret'] );
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
71 |
Â
|
72 |
+
$this->api = new \Aws\S3\S3Client( array(
|
73 |
+
'credentials' => $credentials,
|
74 |
+
'region' => $this->_config['bucket_location'],
|
75 |
+
'version' => '2006-03-01'
|
76 |
+
)
|
77 |
+
);
|
Â
|
|
78 |
Â
}
|
79 |
Â
|
80 |
Â
/**
|
85 |
Â
* @param boolean $force_rewrite
|
86 |
Â
* @return boolean
|
87 |
Â
*/
|
88 |
+
public function upload( $files, &$results, $force_rewrite = false,
|
89 |
Â
$timeout_time = NULL ) {
|
90 |
Â
$error = null;
|
91 |
Â
|
92 |
+
try {
|
93 |
+
$this->_init();
|
94 |
+
} catch ( \Exception $ex ) {
|
95 |
+
$results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, $ex->getMessage() );
|
96 |
Â
return false;
|
97 |
Â
}
|
98 |
Â
|
125 |
Â
* @param boolean $force_rewrite
|
126 |
Â
* @return array
|
127 |
Â
*/
|
128 |
+
private function _upload( $file, $force_rewrite = false ) {
|
129 |
Â
$local_path = $file['local_path'];
|
130 |
Â
$remote_path = $file['remote_path'];
|
131 |
Â
|
134 |
Â
W3TC_CDN_RESULT_ERROR, 'Source file not found.', $file );
|
135 |
Â
}
|
136 |
Â
|
137 |
+
try {
|
138 |
+
if ( !$force_rewrite ) {
|
139 |
+
try {
|
140 |
+
$info = $this->api->headObject( array(
|
141 |
+
'Bucket' => $this->_config['bucket'],
|
142 |
+
'Key' => $remote_path )
|
143 |
+
);
|
144 |
+
|
145 |
+
$hash = '"' . @md5_file( $local_path ) . '"';
|
146 |
+
$s3_hash = ( isset( $info['ETag'] ) ? $info['ETag'] : '' );
|
147 |
+
|
148 |
+
if ( $hash === $s3_hash ) {
|
149 |
+
return $this->_get_result( $local_path, $remote_path,
|
150 |
+
W3TC_CDN_RESULT_OK, 'Object up-to-date.', $file );
|
151 |
+
}
|
152 |
+
} catch ( \Aws\Exception\AwsException $ex ) {
|
153 |
+
if ( $ex->getAwsErrorCode() == 'NotFound' ) {
|
154 |
+
} else {
|
155 |
+
throw $ex;
|
156 |
+
}
|
157 |
Â
}
|
158 |
Â
}
|
Â
|
|
159 |
Â
|
160 |
+
$headers = $this->_get_headers( $file );
|
161 |
+
$result = $this->_put_object( array(
|
162 |
+
'Key' => $remote_path,
|
163 |
+
'SourceFile' => $local_path,
|
164 |
+
'Metadata' => $headers
|
165 |
+
)
|
166 |
+
);
|
167 |
Â
|
Â
|
|
168 |
Â
return $this->_get_result( $local_path, $remote_path,
|
169 |
Â
W3TC_CDN_RESULT_OK, 'OK', $file );
|
170 |
+
} catch ( \Exception $ex ) {
|
171 |
+
$error = sprintf( 'Unable to put object (%s).', $ex->getMessage() );
|
172 |
Â
|
173 |
+
return $this->_get_result( $local_path, $remote_path,
|
174 |
+
W3TC_CDN_RESULT_ERROR, $error, $file );
|
Â
|
|
Â
|
|
175 |
Â
}
|
Â
|
|
Â
|
|
Â
|
|
176 |
Â
}
|
177 |
Â
|
178 |
Â
/**
|
179 |
Â
* Uploads gzip version of file
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
180 |
Â
*/
|
181 |
+
private function _upload_gzip( $file, $force_rewrite = false ) {
|
182 |
Â
$local_path = $file['local_path'];
|
183 |
Â
$remote_path = $file['remote_path_gzip'];
|
184 |
Â
|
201 |
Â
|
202 |
Â
$data = gzencode( $contents );
|
203 |
Â
|
204 |
+
try {
|
205 |
+
if ( !$force_rewrite ) {
|
206 |
+
try {
|
207 |
+
$info = $this->api->headObject( array(
|
208 |
+
'Bucket' => $this->_config['bucket'],
|
209 |
+
'Key' => $remote_path )
|
210 |
+
);
|
211 |
+
|
212 |
+
$hash = '"' . md5( $data ) . '"';
|
213 |
+
$s3_hash = ( isset( $info['ETag'] ) ? $info['ETag'] : '' );
|
214 |
+
|
215 |
+
if ( $hash === $s3_hash ) {
|
216 |
+
return $this->_get_result( $local_path, $remote_path,
|
217 |
+
W3TC_CDN_RESULT_OK, 'Object up-to-date.', $file );
|
218 |
+
}
|
219 |
+
} catch ( \Aws\Exception\AwsException $ex ) {
|
220 |
+
if ( $ex->getAwsErrorCode() == 'NotFound' ) {
|
221 |
+
} else {
|
222 |
+
throw $ex;
|
223 |
+
}
|
224 |
Â
}
|
225 |
Â
}
|
Â
|
|
226 |
Â
|
227 |
+
$headers = $this->_get_headers( $file );
|
228 |
+
$headers = array_merge( $headers, array(
|
229 |
+
'Vary' => 'Accept-Encoding',
|
230 |
+
'Content-Encoding' => 'gzip'
|
231 |
+
) );
|
232 |
+
|
233 |
+
$result = $this->_put_object( array(
|
234 |
+
'Key' => $remote_path,
|
235 |
+
'Body' => $data,
|
236 |
+
'Metadata' => $headers
|
237 |
+
)
|
238 |
+
);
|
239 |
Â
|
Â
|
|
240 |
Â
return $this->_get_result( $local_path, $remote_path,
|
241 |
Â
W3TC_CDN_RESULT_OK, 'OK', $file );
|
242 |
+
} catch ( \Exception $ex ) {
|
243 |
+
$error = sprintf( 'Unable to put object (%s).', $ex->getMessage() );
|
244 |
+
|
245 |
+
return $this->_get_result( $local_path, $remote_path,
|
246 |
+
W3TC_CDN_RESULT_ERROR, $error, $file );
|
247 |
Â
}
|
248 |
+
}
|
249 |
Â
|
250 |
+
/**
|
251 |
+
* Wrapper to set headers well
|
252 |
+
*/
|
253 |
+
private function _put_object( $data ) {
|
254 |
+
$data['ACL'] = 'public-read';
|
255 |
+
$data['Bucket'] = $this->_config['bucket'];
|
256 |
+
|
257 |
+
if ( isset( $data['Metadata']['Content-Type'] ) ) {
|
258 |
+
$data['ContentType'] = $data['Metadata']['Content-Type'];
|
259 |
+
}
|
260 |
+
if ( isset( $data['Metadata']['Content-Encoding'] ) ) {
|
261 |
+
$data['ContentEncoding'] = $data['Metadata']['Content-Encoding'];
|
262 |
+
}
|
263 |
+
if ( isset( $data['Metadata']['Cache-Control'] ) ) {
|
264 |
+
$data['CacheControl'] = $data['Metadata']['Cache-Control'];
|
265 |
Â
}
|
266 |
Â
|
267 |
+
return $this->api->putObject( $data );
|
Â
|
|
268 |
Â
}
|
269 |
Â
|
270 |
Â
/**
|
274 |
Â
* @param array $results
|
275 |
Â
* @return boolean
|
276 |
Â
*/
|
277 |
+
public function delete( $files, &$results ) {
|
278 |
Â
$error = null;
|
279 |
Â
|
280 |
+
try {
|
281 |
+
$this->_init();
|
282 |
+
} catch ( \Exception $ex ) {
|
283 |
+
$results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, $ex->getMessage() );
|
284 |
Â
return false;
|
285 |
Â
}
|
286 |
Â
|
288 |
Â
$local_path = $file['local_path'];
|
289 |
Â
$remote_path = $file['remote_path'];
|
290 |
Â
|
291 |
+
try {
|
292 |
+
$this->api->deleteObject( array(
|
293 |
+
'Bucket' => $this->_config['bucket'],
|
294 |
+
'Key' => $remote_path
|
295 |
+
) );
|
296 |
Â
$results[] = $this->_get_result( $local_path, $remote_path,
|
297 |
Â
W3TC_CDN_RESULT_OK, 'OK', $file );
|
298 |
+
} catch ( \Exception $ex ) {
|
299 |
Â
$results[] = $this->_get_result( $local_path, $remote_path,
|
300 |
Â
W3TC_CDN_RESULT_ERROR,
|
301 |
Â
sprintf( 'Unable to delete object (%s).',
|
302 |
+
$ex->getMessage() ),
|
303 |
Â
$file );
|
304 |
Â
}
|
305 |
Â
|
306 |
Â
if ( $this->_config['compression'] ) {
|
307 |
Â
$remote_path_gzip = $remote_path . $this->_gzip_extension;
|
308 |
Â
|
309 |
+
try {
|
310 |
+
$this->api->deleteObject( array(
|
311 |
+
'Bucket' => $this->_config['bucket'],
|
312 |
+
'Key' => $remote_path_gzip
|
313 |
+
) );
|
314 |
+
$results[] = $this->_get_result( $local_path, $remote_path_gzip,
|
315 |
+
W3TC_CDN_RESULT_OK, 'OK', $file );
|
316 |
+
} catch ( \Exception $ex ) {
|
317 |
+
$results[] = $this->_get_result( $local_path, $remote_path_gzip,
|
318 |
+
W3TC_CDN_RESULT_ERROR,
|
319 |
Â
sprintf( 'Unable to delete object (%s).',
|
320 |
+
$ex->getMessage() ),
|
321 |
Â
$file );
|
322 |
Â
}
|
323 |
Â
}
|
327 |
Â
}
|
328 |
Â
|
329 |
Â
/**
|
330 |
+
* Test CDN connectivity works
|
Â
|
|
Â
|
|
Â
|
|
331 |
Â
*/
|
332 |
+
public function test( &$error ) {
|
333 |
Â
if ( !parent::test( $error ) ) {
|
334 |
Â
return false;
|
335 |
Â
}
|
336 |
Â
|
337 |
+
$key = 'test_s3_' . md5( time() );
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
338 |
Â
|
339 |
+
$this->_init();
|
340 |
+
$buckets = $this->api->listBuckets();
|
341 |
Â
|
342 |
+
$bucket_found = false;
|
343 |
+
foreach ( $buckets['Buckets'] as $bucket ) {
|
344 |
+
if ( $bucket['Name'] == $this->_config['bucket'] ) {
|
345 |
+
$bucket_found = true;
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
346 |
Â
}
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
347 |
Â
}
|
348 |
Â
|
349 |
+
if ( !$bucket_found ) {
|
350 |
+
throw new \Exception( 'Bucket doesn\'t exist: %s.', $this->_config['bucket'] );
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
351 |
Â
}
|
352 |
Â
|
353 |
+
$result = $this->api->putObject( array(
|
354 |
+
'ACL' => 'public-read',
|
355 |
+
'Bucket' => $this->_config['bucket'],
|
356 |
+
'Key' => $key,
|
357 |
+
'Body' => $key
|
358 |
+
)
|
359 |
+
);
|
360 |
Â
|
361 |
+
$object = $this->api->getObject( array(
|
362 |
+
'Bucket' => $this->_config['bucket'],
|
363 |
+
'Key' => $key
|
364 |
+
) );
|
365 |
Â
|
366 |
+
if ( $object['Body'] != $key ) {
|
367 |
+
$error = 'Objects are not equal.';
|
368 |
Â
|
369 |
+
$this->api->deleteObject( array(
|
370 |
+
'Bucket' => $this->_config['bucket'],
|
371 |
+
'Key' => $key
|
372 |
+
) );
|
373 |
Â
|
374 |
Â
return false;
|
375 |
Â
}
|
376 |
Â
|
377 |
+
$this->api->deleteObject( array(
|
378 |
+
'Bucket' => $this->_config['bucket'],
|
379 |
+
'Key' => $key
|
380 |
+
) );
|
381 |
Â
|
382 |
Â
return true;
|
383 |
Â
}
|
387 |
Â
*
|
388 |
Â
* @return array
|
389 |
Â
*/
|
390 |
+
public function get_domains() {
|
391 |
Â
if ( !empty( $this->_config['cname'] ) ) {
|
392 |
Â
return (array) $this->_config['cname'];
|
393 |
Â
} elseif ( !empty( $this->_config['bucket'] ) ) {
|
406 |
Â
*
|
407 |
Â
* @return string
|
408 |
Â
*/
|
409 |
+
public function get_via() {
|
410 |
Â
return sprintf( 'Amazon Web Services: S3: %s', parent::get_via() );
|
411 |
Â
}
|
412 |
Â
|
413 |
Â
/**
|
414 |
Â
* Creates bucket
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
415 |
Â
*/
|
416 |
+
public function create_container() {
|
417 |
+
$this->_init();
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
418 |
Â
|
419 |
+
try {
|
420 |
+
$buckets = $this->api->listBuckets();
|
421 |
+
} catch ( \Exception $ex ) {
|
422 |
+
throw new \Exception( 'Unable to list buckets: ' . $ex->getMessage() );
|
Â
|
|
Â
|
|
423 |
Â
}
|
424 |
Â
|
425 |
+
foreach ( $buckets['Buckets'] as $bucket ) {
|
426 |
+
if ( $bucket['Name'] == $this->_config['bucket'] ) {
|
427 |
+
throw new \Exception( 'Bucket already exists: ' . $this->_config['bucket'] );
|
428 |
+
}
|
Â
|
|
Â
|
|
429 |
Â
}
|
430 |
Â
|
431 |
+
try {
|
432 |
+
$result = $this->api->createBucket( array(
|
433 |
+
'Bucket' => $this->_config['bucket'],
|
434 |
+
) );
|
435 |
+
} catch ( \Exception $e) {
|
436 |
+
throw new \Exception( 'Failed to create bucket: ' . $ex->getMessage() );
|
437 |
Â
}
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
438 |
Â
}
|
439 |
Â
|
440 |
Â
/**
|
442 |
Â
*
|
443 |
Â
* @return string W3TC_CDN_HEADER_NONE, W3TC_CDN_HEADER_UPLOADABLE, W3TC_CDN_HEADER_MIRRORING
|
444 |
Â
*/
|
445 |
+
public function headers_support() {
|
446 |
Â
return W3TC_CDN_HEADER_UPLOADABLE;
|
447 |
Â
}
|
448 |
Â
}
|
CdnEngine_S3_Cf.php
DELETED
@@ -1,453 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
namespace W3TC;
|
3 |
-
|
4 |
-
/**
|
5 |
-
* Amazon CloudFront CDN engine
|
6 |
-
*/
|
7 |
-
|
8 |
-
define( 'W3TC_CDN_CF_TYPE_S3', 's3' );
|
9 |
-
define( 'W3TC_CDN_CF_TYPE_CUSTOM', 'custom' );
|
10 |
-
|
11 |
-
/**
|
12 |
-
* class CdnEngine_S3_Cf
|
13 |
-
*/
|
14 |
-
class CdnEngine_S3_Cf extends CdnEngine_S3 {
|
15 |
-
/**
|
16 |
-
* Type
|
17 |
-
*
|
18 |
-
* @var string
|
19 |
-
*/
|
20 |
-
var $type = '';
|
21 |
-
|
22 |
-
/**
|
23 |
-
* PHP5 Constructor
|
24 |
-
*
|
25 |
-
* @param array $config
|
26 |
-
*/
|
27 |
-
function __construct( $config = array() ) {
|
28 |
-
$config = array_merge( array(
|
29 |
-
'id' => ''
|
30 |
-
), $config );
|
31 |
-
|
32 |
-
parent::__construct( $config );
|
33 |
-
}
|
34 |
-
|
35 |
-
/**
|
36 |
-
* Initializes S3 object
|
37 |
-
*
|
38 |
-
* @param string $error
|
39 |
-
* @return bool
|
40 |
-
*/
|
41 |
-
function _init( &$error ) {
|
42 |
-
if ( empty( $this->type ) ) {
|
43 |
-
$error = 'Empty type.';
|
44 |
-
|
45 |
-
return false;
|
46 |
-
} elseif ( !in_array( $this->type, array( W3TC_CDN_CF_TYPE_S3, W3TC_CDN_CF_TYPE_CUSTOM ) ) ) {
|
47 |
-
$error = 'Invalid type.';
|
48 |
-
|
49 |
-
return false;
|
50 |
-
}
|
51 |
-
|
52 |
-
if ( empty( $this->_config['key'] ) ) {
|
53 |
-
$error = 'Empty access key.';
|
54 |
-
|
55 |
-
return false;
|
56 |
-
}
|
57 |
-
|
58 |
-
if ( empty( $this->_config['secret'] ) ) {
|
59 |
-
$error = 'Empty secret key.';
|
60 |
-
|
61 |
-
return false;
|
62 |
-
}
|
63 |
-
|
64 |
-
if ( $this->type == W3TC_CDN_CF_TYPE_S3 && empty( $this->_config['bucket'] ) ) {
|
65 |
-
$error = 'Empty bucket.';
|
66 |
-
|
67 |
-
return false;
|
68 |
-
}
|
69 |
-
|
70 |
-
if ( empty( $this->_config['bucket_location'] ) ) {
|
71 |
-
$region = '';
|
72 |
-
$endpoint = 's3.amazonaws.com';
|
73 |
-
} else {
|
74 |
-
$region = $this->_config['bucket_location'];
|
75 |
-
$endpoint = 's3.dualstack.'.$region.'.amazonaws.com';
|
76 |
-
}
|
77 |
-
|
78 |
-
$this->_s3 = new \S3( $this->_config['key'], $this->_config['secret'],
|
79 |
-
false, $endpoint, $region );
|
80 |
-
|
81 |
-
if ( empty( $region ) ) {
|
82 |
-
$this->_s3->setSignatureVersion( 'v2' );
|
83 |
-
}
|
84 |
-
|
85 |
-
return true;
|
86 |
-
}
|
87 |
-
|
88 |
-
/**
|
89 |
-
* Returns origin
|
90 |
-
*
|
91 |
-
* @return string
|
92 |
-
*/
|
93 |
-
function _get_origin() {
|
94 |
-
if ( $this->type == W3TC_CDN_CF_TYPE_S3 ) {
|
95 |
-
$origin = sprintf( '%s.s3.amazonaws.com', $this->_config['bucket'] );
|
96 |
-
} else {
|
97 |
-
$origin = Util_Environment::host_port();
|
98 |
-
}
|
99 |
-
|
100 |
-
return $origin;
|
101 |
-
}
|
102 |
-
|
103 |
-
/**
|
104 |
-
* Upload files
|
105 |
-
*
|
106 |
-
* @param array $files
|
107 |
-
* @param array $results
|
108 |
-
* @param boolean $force_rewrite
|
109 |
-
* @return boolean
|
110 |
-
*/
|
111 |
-
function upload( $files, &$results, $force_rewrite = false,
|
112 |
-
$timeout_time = NULL ) {
|
113 |
-
if ( $this->type == W3TC_CDN_CF_TYPE_S3 ) {
|
114 |
-
return parent::upload( $files, $results, $force_rewrite,
|
115 |
-
$timeout_time );
|
116 |
-
} else {
|
117 |
-
$results = $this->_get_results( $files, W3TC_CDN_RESULT_OK, 'OK' );
|
118 |
-
|
119 |
-
return true;
|
120 |
-
}
|
121 |
-
}
|
122 |
-
|
123 |
-
/**
|
124 |
-
* Delete files from CDN
|
125 |
-
*
|
126 |
-
* @param array $files
|
127 |
-
* @param array $results
|
128 |
-
* @return boolean
|
129 |
-
*/
|
130 |
-
function delete( $files, &$results ) {
|
131 |
-
if ( $this->type == W3TC_CDN_CF_TYPE_S3 ) {
|
132 |
-
return parent::delete( $files, $results );
|
133 |
-
} else {
|
134 |
-
$results = $this->_get_results( $files, W3TC_CDN_RESULT_OK, 'OK' );
|
135 |
-
|
136 |
-
return true;
|
137 |
-
}
|
138 |
-
}
|
139 |
-
|
140 |
-
/**
|
141 |
-
* Purge files from CDN
|
142 |
-
*
|
143 |
-
* @param array $files
|
144 |
-
* @param array $results
|
145 |
-
* @return boolean
|
146 |
-
*/
|
147 |
-
function purge( $files, &$results ) {
|
148 |
-
if ( parent::purge( $files, $results ) ) {
|
149 |
-
return $this->invalidate( $files, $results );
|
150 |
-
}
|
151 |
-
|
152 |
-
return false;
|
153 |
-
}
|
154 |
-
|
155 |
-
/**
|
156 |
-
* Invalidates files
|
157 |
-
*
|
158 |
-
* @param array $files
|
159 |
-
* @param array $results
|
160 |
-
* @return boolean
|
161 |
-
*/
|
162 |
-
function invalidate( $files, &$results ) {
|
163 |
-
if ( !$this->_init( $error ) ) {
|
164 |
-
$results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, $error );
|
165 |
-
|
166 |
-
return false;
|
167 |
-
}
|
168 |
-
|
169 |
-
$this->_set_error_handler();
|
170 |
-
$dists = @$this->_s3->listDistributions();
|
171 |
-
$this->_restore_error_handler();
|
172 |
-
|
173 |
-
if ( $dists === false ) {
|
174 |
-
$results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, sprintf( 'Unable to list distributions (%s).', $this->_get_last_error() ) );
|
175 |
-
|
176 |
-
return false;
|
177 |
-
}
|
178 |
-
|
179 |
-
if ( !count( $dists ) ) {
|
180 |
-
$results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, 'No distributions found.' );
|
181 |
-
|
182 |
-
return false;
|
183 |
-
}
|
184 |
-
|
185 |
-
$dist = false;
|
186 |
-
$origin = $this->_get_origin();
|
187 |
-
|
188 |
-
foreach ( (array) $dists as $_dist ) {
|
189 |
-
if ( isset( $_dist['origin'] ) && $_dist['origin'] == $origin ) {
|
190 |
-
$dist = $_dist;
|
191 |
-
break;
|
192 |
-
}
|
193 |
-
}
|
194 |
-
|
195 |
-
if ( !$dist ) {
|
196 |
-
$results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, sprintf( 'Distribution for origin "%s" not found.', $origin ) );
|
197 |
-
|
198 |
-
return false;
|
199 |
-
}
|
200 |
-
|
201 |
-
$paths = array();
|
202 |
-
|
203 |
-
foreach ( $files as $file ) {
|
204 |
-
$remote_file = $file['remote_path'];
|
205 |
-
$paths[] = '/' . $remote_file;
|
206 |
-
}
|
207 |
-
|
208 |
-
$this->_set_error_handler();
|
209 |
-
$invalidation = @$this->_s3->invalidateDistribution( $dist['id'], $paths );
|
210 |
-
$this->_restore_error_handler();
|
211 |
-
|
212 |
-
if ( !$invalidation ) {
|
213 |
-
$results = $this->_get_results( $files, W3TC_CDN_RESULT_HALT, sprintf( 'Unable to create invalidation bath (%s).', $this->_get_last_error() ) );
|
214 |
-
|
215 |
-
return false;
|
216 |
-
}
|
217 |
-
|
218 |
-
$results = $this->_get_results( $files, W3TC_CDN_RESULT_OK, 'OK' );
|
219 |
-
|
220 |
-
return true;
|
221 |
-
}
|
222 |
-
|
223 |
-
/**
|
224 |
-
* Returns array of CDN domains
|
225 |
-
*
|
226 |
-
* @return array
|
227 |
-
*/
|
228 |
-
function get_domains() {
|
229 |
-
if ( !empty( $this->_config['cname'] ) ) {
|
230 |
-
return (array) $this->_config['cname'];
|
231 |
-
} elseif ( !empty( $this->_config['id'] ) ) {
|
232 |
-
$domain = sprintf( '%s.cloudfront.net', $this->_config['id'] );
|
233 |
-
|
234 |
-
return array(
|
235 |
-
$domain
|
236 |
-
);
|
237 |
-
}
|
238 |
-
|
239 |
-
return array();
|
240 |
-
}
|
241 |
-
|
242 |
-
/**
|
243 |
-
* Tests CF
|
244 |
-
*
|
245 |
-
* @param string $error
|
246 |
-
* @return boolean
|
247 |
-
*/
|
248 |
-
function test( &$error ) {
|
249 |
-
if ( $this->type == W3TC_CDN_CF_TYPE_S3 ) {
|
250 |
-
if ( !parent::test( $error ) ) {
|
251 |
-
return false;
|
252 |
-
}
|
253 |
-
} elseif ( $this->type == W3TC_CDN_CF_TYPE_CUSTOM ) {
|
254 |
-
if ( !$this->_init( $error ) ) {
|
255 |
-
return false;
|
256 |
-
}
|
257 |
-
}
|
258 |
-
|
259 |
-
/**
|
260 |
-
* Search active CF distribution
|
261 |
-
*/
|
262 |
-
$this->_set_error_handler();
|
263 |
-
$dists = @$this->_s3->listDistributions();
|
264 |
-
$this->_restore_error_handler();
|
265 |
-
|
266 |
-
if ( $dists === false ) {
|
267 |
-
$error = sprintf( 'Unable to list distributions (%s).', $this->_get_last_error() );
|
268 |
-
|
269 |
-
return false;
|
270 |
-
}
|
271 |
-
|
272 |
-
if ( !count( $dists ) ) {
|
273 |
-
$error = 'No distributions found.';
|
274 |
-
|
275 |
-
return false;
|
276 |
-
}
|
277 |
-
|
278 |
-
$dist = false;
|
279 |
-
$origin = $this->_get_origin();
|
280 |
-
|
281 |
-
foreach ( (array) $dists as $_dist ) {
|
282 |
-
if ( isset( $_dist['origin'] ) && $_dist['origin'] == $origin ) {
|
283 |
-
$dist = $_dist;
|
284 |
-
break;
|
285 |
-
}
|
286 |
-
}
|
287 |
-
|
288 |
-
if ( !$dist ) {
|
289 |
-
$error = sprintf( 'Distribution for origin "%s" not found.', $origin );
|
290 |
-
|
291 |
-
return false;
|
292 |
-
}
|
293 |
-
|
294 |
-
if ( !$dist['enabled'] ) {
|
295 |
-
$error = sprintf( 'Distribution for origin "%s" is disabled.', $origin );
|
296 |
-
|
297 |
-
return false;
|
298 |
-
}
|
299 |
-
|
300 |
-
if ( !empty( $this->_config['cname'] ) ) {
|
301 |
-
$domains = (array) $this->_config['cname'];
|
302 |
-
$cnames = ( isset( $dist['cnames'] ) ? (array) $dist['cnames'] : array() );
|
303 |
-
|
304 |
-
foreach ( $domains as $domain ) {
|
305 |
-
$_domains = array_map( 'trim', explode( ',', $domain ) );
|
306 |
-
|
307 |
-
foreach ( $_domains as $_domain ) {
|
308 |
-
if ( !in_array( $_domain, $cnames ) ) {
|
309 |
-
$error = sprintf( 'Domain name %s is not in distribution <acronym title="Canonical Name">CNAME</acronym> list.', $_domain );
|
310 |
-
|
311 |
-
return false;
|
312 |
-
}
|
313 |
-
}
|
314 |
-
}
|
315 |
-
} elseif ( !empty( $this->_config['id'] ) ) {
|
316 |
-
$domain = $this->get_domain();
|
317 |
-
|
318 |
-
if ( $domain != $dist['domain'] ) {
|
319 |
-
$error = sprintf( 'Distribution domain name mismatch (%s != %s).', $domain, $dist['domain'] );
|
320 |
-
|
321 |
-
return false;
|
322 |
-
}
|
323 |
-
}
|
324 |
-
|
325 |
-
return true;
|
326 |
-
}
|
327 |
-
|
328 |
-
/**
|
329 |
-
* Create bucket
|
330 |
-
*
|
331 |
-
* @param string $container_id
|
332 |
-
* @param string $error
|
333 |
-
* @return boolean
|
334 |
-
*/
|
335 |
-
function create_container( &$container_id, &$error ) {
|
336 |
-
if ( $this->type == W3TC_CDN_CF_TYPE_S3 ) {
|
337 |
-
if ( !parent::create_container( $container_id, $error ) ) {
|
338 |
-
return false;
|
339 |
-
}
|
340 |
-
} elseif ( $this->type == W3TC_CDN_CF_TYPE_CUSTOM ) {
|
341 |
-
if ( !$this->_init( $error ) ) {
|
342 |
-
return false;
|
343 |
-
}
|
344 |
-
}
|
345 |
-
|
346 |
-
$cnames = array();
|
347 |
-
|
348 |
-
if ( !empty( $this->_config['cname'] ) ) {
|
349 |
-
$domains = (array) $this->_config['cname'];
|
350 |
-
|
351 |
-
foreach ( $domains as $domain ) {
|
352 |
-
$_domains = array_map( 'trim', explode( ',', $domain ) );
|
353 |
-
|
354 |
-
foreach ( $_domains as $_domain ) {
|
355 |
-
$cnames[] = $_domain;
|
356 |
-
}
|
357 |
-
}
|
358 |
-
}
|
359 |
-
|
360 |
-
$origin = $this->_get_origin();
|
361 |
-
|
362 |
-
$this->_set_error_handler();
|
363 |
-
$dist = @$this->_s3->createDistribution( $origin, $this->type, true, $cnames );
|
364 |
-
$this->_restore_error_handler();
|
365 |
-
|
366 |
-
if ( !$dist ) {
|
367 |
-
$error = sprintf( 'Unable to create distribution for origin %s (%s).', $origin, $this->_get_last_error() );
|
368 |
-
|
369 |
-
return false;
|
370 |
-
}
|
371 |
-
|
372 |
-
$matches = null;
|
373 |
-
|
374 |
-
if ( !empty( $dist['domain'] ) && preg_match( '~^(.+)\.cloudfront\.net$~', $dist['domain'], $matches ) ) {
|
375 |
-
$container_id = $matches[1];
|
376 |
-
}
|
377 |
-
|
378 |
-
return true;
|
379 |
-
}
|
380 |
-
|
381 |
-
/**
|
382 |
-
* Returns via string
|
383 |
-
*
|
384 |
-
* @return string
|
385 |
-
*/
|
386 |
-
function get_via() {
|
387 |
-
$domain = $this->get_domain();
|
388 |
-
|
389 |
-
$via = ( $domain ? $domain : 'N/A' );
|
390 |
-
|
391 |
-
return sprintf( 'Amazon Web Services: CloudFront: %s', $via );
|
392 |
-
}
|
393 |
-
|
394 |
-
/**
|
395 |
-
* Update distribution CNAMEs
|
396 |
-
*
|
397 |
-
* @param string $error
|
398 |
-
* @return boolean
|
399 |
-
*/
|
400 |
-
function update_cnames( &$error ) {
|
401 |
-
if ( !$this->_init( $error ) ) {
|
402 |
-
return false;
|
403 |
-
}
|
404 |
-
|
405 |
-
$this->_set_error_handler();
|
406 |
-
$dists = @$this->_s3->listDistributions();
|
407 |
-
$this->_restore_error_handler();
|
408 |
-
|
409 |
-
if ( $dists === false ) {
|
410 |
-
$error = sprintf( 'Unable to list distributions (%s).', $this->_get_last_error() );
|
411 |
-
|
412 |
-
return false;
|
413 |
-
}
|
414 |
-
|
415 |
-
$dist_id = false;
|
416 |
-
$origin = $this->_get_origin();
|
417 |
-
|
418 |
-
foreach ( (array) $dists as $dist ) {
|
419 |
-
if ( isset( $dist['origin'] ) && $dist['origin'] == $origin ) {
|
420 |
-
$dist_id = $dist['id'];
|
421 |
-
break;
|
422 |
-
}
|
423 |
-
}
|
424 |
-
|
425 |
-
if ( !$dist_id ) {
|
426 |
-
$error = sprintf( 'Distribution ID for origin "%s" not found.', $origin );
|
427 |
-
|
428 |
-
return false;
|
429 |
-
}
|
430 |
-
|
431 |
-
$this->_set_error_handler();
|
432 |
-
$dist = @$this->_s3->getDistribution( $dist_id );
|
433 |
-
$this->_restore_error_handler();
|
434 |
-
|
435 |
-
if ( !$dist ) {
|
436 |
-
$error = sprintf( 'Unable to get distribution by ID: %s (%s).', $dist_id, $this->_get_last_error() );
|
437 |
-
}
|
438 |
-
|
439 |
-
$dist['cnames'] = ( isset( $this->_config['cname'] ) ? (array) $this->_config['cname'] : array() );
|
440 |
-
|
441 |
-
$this->_set_error_handler();
|
442 |
-
$dist = @$this->_s3->updateDistribution( $dist );
|
443 |
-
$this->_restore_error_handler();
|
444 |
-
|
445 |
-
if ( !$dist ) {
|
446 |
-
$error = sprintf( 'Unable to update distribution: %s (%s).', json_encode( $dist ), $this->_get_last_error() );
|
447 |
-
|
448 |
-
return false;
|
449 |
-
}
|
450 |
-
|
451 |
-
return true;
|
452 |
-
}
|
453 |
-
}
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
CdnEngine_S3_Cf_Custom.php
DELETED
@@ -1,19 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
namespace W3TC;
|
3 |
-
|
4 |
-
/**
|
5 |
-
* Amazon CloudFront (Custom origin) CDN engine
|
6 |
-
*/
|
7 |
-
|
8 |
-
class CdnEngine_S3_Cf_Custom extends CdnEngine_S3_Cf {
|
9 |
-
var $type = W3TC_CDN_CF_TYPE_CUSTOM;
|
10 |
-
|
11 |
-
/**
|
12 |
-
* How and if headers should be set
|
13 |
-
*
|
14 |
-
* @return string W3TC_CDN_HEADER_NONE, W3TC_CDN_HEADER_UPLOADABLE, W3TC_CDN_HEADER_MIRRORING
|
15 |
-
*/
|
16 |
-
function headers_support() {
|
17 |
-
return W3TC_CDN_HEADER_MIRRORING;
|
18 |
-
}
|
19 |
-
}
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
CdnEngine_S3_Cf_S3.php
DELETED
@@ -1,10 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
namespace W3TC;
|
3 |
-
|
4 |
-
/**
|
5 |
-
* Amazon CloudFront (S3 origin) CDN engine
|
6 |
-
*/
|
7 |
-
|
8 |
-
class CdnEngine_S3_Cf_S3 extends CdnEngine_S3_Cf {
|
9 |
-
var $type = W3TC_CDN_CF_TYPE_S3;
|
10 |
-
}
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
CdnEngine_S3_Compatible.php
CHANGED
@@ -5,8 +5,8 @@ namespace W3TC;
|
|
5 |
Â
* Amazon S3 CDN engine
|
6 |
Â
*/
|
7 |
Â
|
8 |
-
if ( !class_exists( '
|
9 |
-
require_once W3TC_LIB_DIR . '/
|
10 |
Â
}
|
11 |
Â
|
12 |
Â
/**
|
@@ -31,7 +31,7 @@ class CdnEngine_S3_Compatible extends CdnEngine_Base {
|
|
31 |
Â
'cname' => array(),
|
32 |
Â
), $config );
|
33 |
Â
|
34 |
-
$this->_s3 = new \
|
35 |
Â
$config['api_host'] );
|
36 |
Â
$this->_s3->setSignatureVersion( 'v2' );
|
37 |
Â
|
@@ -133,7 +133,7 @@ class CdnEngine_S3_Compatible extends CdnEngine_Base {
|
|
133 |
Â
$this->_set_error_handler();
|
134 |
Â
$result = @$this->_s3->putObjectFile( $local_path,
|
135 |
Â
$this->_config['bucket'], $remote_path,
|
136 |
-
\
|
137 |
Â
$this->_restore_error_handler();
|
138 |
Â
|
139 |
Â
if ( $result ) {
|
@@ -199,7 +199,7 @@ class CdnEngine_S3_Compatible extends CdnEngine_Base {
|
|
199 |
Â
|
200 |
Â
$this->_set_error_handler();
|
201 |
Â
$result = @$this->_s3->putObjectString( $data, $this->_config['bucket'],
|
202 |
-
$remote_path, \
|
203 |
Â
$this->_restore_error_handler();
|
204 |
Â
|
205 |
Â
if ( $result )
|
@@ -280,7 +280,7 @@ class CdnEngine_S3_Compatible extends CdnEngine_Base {
|
|
280 |
Â
$this->_set_error_handler();
|
281 |
Â
|
282 |
Â
if ( !@$this->_s3->putObjectString( $string, $this->_config['bucket'],
|
283 |
-
$string, \
|
284 |
Â
$error = sprintf( 'Unable to put object (%s).',
|
285 |
Â
$this->_get_last_error() );
|
286 |
Â
|
5 |
Â
* Amazon S3 CDN engine
|
6 |
Â
*/
|
7 |
Â
|
8 |
+
if ( !class_exists( 'S3Compatible' ) ) {
|
9 |
+
require_once W3TC_LIB_DIR . '/S3Compatible.php';
|
10 |
Â
}
|
11 |
Â
|
12 |
Â
/**
|
31 |
Â
'cname' => array(),
|
32 |
Â
), $config );
|
33 |
Â
|
34 |
+
$this->_s3 = new \S3Compatible( $config['key'], $config['secret'], false,
|
35 |
Â
$config['api_host'] );
|
36 |
Â
$this->_s3->setSignatureVersion( 'v2' );
|
37 |
Â
|
133 |
Â
$this->_set_error_handler();
|
134 |
Â
$result = @$this->_s3->putObjectFile( $local_path,
|
135 |
Â
$this->_config['bucket'], $remote_path,
|
136 |
+
\S3Compatible::ACL_PUBLIC_READ, array(), $headers );
|
137 |
Â
$this->_restore_error_handler();
|
138 |
Â
|
139 |
Â
if ( $result ) {
|
199 |
Â
|
200 |
Â
$this->_set_error_handler();
|
201 |
Â
$result = @$this->_s3->putObjectString( $data, $this->_config['bucket'],
|
202 |
+
$remote_path, \S3Compatible::ACL_PUBLIC_READ, array(), $headers );
|
203 |
Â
$this->_restore_error_handler();
|
204 |
Â
|
205 |
Â
if ( $result )
|
280 |
Â
$this->_set_error_handler();
|
281 |
Â
|
282 |
Â
if ( !@$this->_s3->putObjectString( $string, $this->_config['bucket'],
|
283 |
+
$string, \S3Compatible::ACL_PUBLIC_READ ) ) {
|
284 |
Â
$error = sprintf( 'Unable to put object (%s).',
|
285 |
Â
$this->_get_last_error() );
|
286 |
Â
|
Cdn_AdminActions.php
CHANGED
@@ -423,12 +423,17 @@ class Cdn_AdminActions {
|
|
423 |
Â
|
424 |
Â
@set_time_limit( $this->_config->get_integer( 'timelimit.cdn_test' ) );
|
425 |
Â
|
426 |
-
|
427 |
-
$
|
428 |
-
|
429 |
-
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
430 |
Â
$result = false;
|
431 |
-
$error = sprintf( __( 'Error: %s', 'w3-total-cache' ), $
|
432 |
Â
}
|
433 |
Â
}
|
434 |
Â
|
@@ -454,8 +459,6 @@ class Cdn_AdminActions {
|
|
454 |
Â
'debug' => false
|
455 |
Â
) );
|
456 |
Â
|
457 |
-
$result = false;
|
458 |
-
$error = __( 'Incorrect type.', 'w3-total-cache' );
|
459 |
Â
$container_id = '';
|
460 |
Â
|
461 |
Â
switch ( $engine ) {
|
@@ -463,22 +466,24 @@ class Cdn_AdminActions {
|
|
463 |
Â
case 'cf':
|
464 |
Â
case 'cf2':
|
465 |
Â
case 'azure':
|
466 |
-
$result = true;
|
467 |
-
break;
|
468 |
-
}
|
469 |
-
|
470 |
-
if ( $result ) {
|
471 |
Â
$w3_cdn = CdnEngine::instance( $engine, $config );
|
472 |
Â
|
473 |
-
@set_time_limit( $this->_config->get_integer( 'timelimit.
|
474 |
Â
|
475 |
-
|
Â
|
|
Â
|
|
476 |
Â
$result = true;
|
477 |
Â
$error = __( 'Created successfully.', 'w3-total-cache' );
|
478 |
-
}
|
479 |
-
$
|
480 |
-
|
481 |
Â
}
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
482 |
Â
}
|
483 |
Â
|
484 |
Â
$response = array(
|
423 |
Â
|
424 |
Â
@set_time_limit( $this->_config->get_integer( 'timelimit.cdn_test' ) );
|
425 |
Â
|
426 |
+
try {
|
427 |
+
if ( $w3_cdn->test( $error ) ) {
|
428 |
+
$result = true;
|
429 |
+
$error = __( 'Test passed', 'w3-total-cache' );
|
430 |
+
} else {
|
431 |
+
$result = false;
|
432 |
+
$error = sprintf( __( 'Error: %s', 'w3-total-cache' ), $error );
|
433 |
+
}
|
434 |
+
} catch ( \Exception $ex ) {
|
435 |
Â
$result = false;
|
436 |
+
$error = sprintf( __( 'Error: %s', 'w3-total-cache' ), $ex->getMessage() );
|
437 |
Â
}
|
438 |
Â
}
|
439 |
Â
|
459 |
Â
'debug' => false
|
460 |
Â
) );
|
461 |
Â
|
Â
|
|
Â
|
|
462 |
Â
$container_id = '';
|
463 |
Â
|
464 |
Â
switch ( $engine ) {
|
466 |
Â
case 'cf':
|
467 |
Â
case 'cf2':
|
468 |
Â
case 'azure':
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
469 |
Â
$w3_cdn = CdnEngine::instance( $engine, $config );
|
470 |
Â
|
471 |
+
@set_time_limit( $this->_config->get_integer( 'timelimit.cdn_upload' ) );
|
472 |
Â
|
473 |
+
$result = false;
|
474 |
+
try {
|
475 |
+
$container_id = $w3_cdn->create_container();
|
476 |
Â
$result = true;
|
477 |
Â
$error = __( 'Created successfully.', 'w3-total-cache' );
|
478 |
+
} catch ( \Exception $ex ) {
|
479 |
+
$error = sprintf( __( 'Error: %s', 'w3-total-cache' ),
|
480 |
+
$ex->getMessage() );
|
481 |
Â
}
|
482 |
+
|
483 |
+
break;
|
484 |
+
default:
|
485 |
+
$result = false;
|
486 |
+
$error = __( 'Incorrect type.', 'w3-total-cache' );
|
487 |
Â
}
|
488 |
Â
|
489 |
Â
$response = array(
|
Cdn_Core_Admin.php
CHANGED
@@ -198,20 +198,20 @@ class Cdn_Core_Admin {
|
|
198 |
Â
|
199 |
Â
if ( $upload_info ) {
|
200 |
Â
$sql = sprintf( 'SELECT
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
Â
|
216 |
Â
if ( $limit ) {
|
217 |
Â
$sql .= sprintf( ' LIMIT %d', $limit );
|
@@ -287,17 +287,17 @@ class Cdn_Core_Admin {
|
|
287 |
Â
* Search for posts with links or images
|
288 |
Â
*/
|
289 |
Â
$sql = sprintf( 'SELECT
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
Â
|
302 |
Â
if ( $limit ) {
|
303 |
Â
$sql .= sprintf( ' LIMIT %d', $limit );
|
@@ -550,17 +550,17 @@ class Cdn_Core_Admin {
|
|
550 |
Â
|
551 |
Â
if ( $upload_info ) {
|
552 |
Â
$sql = sprintf( 'SELECT
|
553 |
-
|
554 |
-
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
Â
|
565 |
Â
if ( $limit ) {
|
566 |
Â
$sql .= sprintf( ' LIMIT %d', $limit );
|
@@ -636,15 +636,15 @@ WHERE p.post_type = "attachment" AND (pm.meta_value IS NOT NULL OR pm2.meta_valu
|
|
636 |
Â
global $wpdb;
|
637 |
Â
|
638 |
Â
$sql = sprintf( 'SELECT
|
639 |
-
|
640 |
-
|
641 |
-
|
642 |
-
|
643 |
-
|
644 |
-
|
645 |
-
|
646 |
-
|
647 |
-
|
648 |
Â
|
649 |
Â
return $wpdb->get_var( $sql );
|
650 |
Â
}
|
@@ -689,18 +689,6 @@ WHERE p.post_type = "attachment" AND (pm.meta_value IS NOT NULL OR pm2.meta_valu
|
|
689 |
Â
return $regexp;
|
690 |
Â
}
|
691 |
Â
|
692 |
-
/**
|
693 |
-
*
|
694 |
-
*
|
695 |
-
* @param unknown $error
|
696 |
-
*/
|
697 |
-
function update_cnames( &$error ) {
|
698 |
-
$common = Dispatcher::component( 'Cdn_Core' );
|
699 |
-
$cdn = $common->get_cdn();
|
700 |
-
$cdn->update_cnames( $error );
|
701 |
-
}
|
702 |
-
|
703 |
-
|
704 |
Â
/**
|
705 |
Â
* media_row_actions filter
|
706 |
Â
*
|
198 |
Â
|
199 |
Â
if ( $upload_info ) {
|
200 |
Â
$sql = sprintf( 'SELECT
|
201 |
+
pm.meta_value AS file,
|
202 |
+
pm2.meta_value AS metadata
|
203 |
+
FROM
|
204 |
+
%sposts AS p
|
205 |
+
LEFT JOIN
|
206 |
+
%spostmeta AS pm ON p.ID = pm.post_ID AND pm.meta_key = "_wp_attached_file"
|
207 |
+
LEFT JOIN
|
208 |
+
%spostmeta AS pm2 ON p.ID = pm2.post_ID AND pm2.meta_key = "_wp_attachment_metadata"
|
209 |
+
WHERE
|
210 |
+
p.post_type = "attachment" AND (pm.meta_value IS NOT NULL OR pm2.meta_value IS NOT NULL)
|
211 |
+
GROUP BY
|
212 |
+
p.ID
|
213 |
+
ORDER BY
|
214 |
+
p.ID', $wpdb->prefix, $wpdb->prefix, $wpdb->prefix );
|
215 |
Â
|
216 |
Â
if ( $limit ) {
|
217 |
Â
$sql .= sprintf( ' LIMIT %d', $limit );
|
287 |
Â
* Search for posts with links or images
|
288 |
Â
*/
|
289 |
Â
$sql = sprintf( 'SELECT
|
290 |
+
ID,
|
291 |
+
post_content,
|
292 |
+
post_date
|
293 |
+
FROM
|
294 |
+
%sposts
|
295 |
+
WHERE
|
296 |
+
post_status = "publish"
|
297 |
+
AND (post_type = "post" OR post_type = "page")
|
298 |
+
AND (post_content LIKE "%%src=%%"
|
299 |
+
OR post_content LIKE "%%href=%%")
|
300 |
+
', $wpdb->prefix );
|
301 |
Â
|
302 |
Â
if ( $limit ) {
|
303 |
Â
$sql .= sprintf( ' LIMIT %d', $limit );
|
550 |
Â
|
551 |
Â
if ( $upload_info ) {
|
552 |
Â
$sql = sprintf( 'SELECT
|
553 |
+
ID,
|
554 |
+
post_content,
|
555 |
+
post_date
|
556 |
+
FROM
|
557 |
+
%sposts
|
558 |
+
WHERE
|
559 |
+
post_status = "publish"
|
560 |
+
AND (post_type = "post" OR post_type = "page")
|
561 |
+
AND (post_content LIKE "%%src=%%"
|
562 |
+
OR post_content LIKE "%%href=%%")
|
563 |
+
', $wpdb->prefix );
|
564 |
Â
|
565 |
Â
if ( $limit ) {
|
566 |
Â
$sql .= sprintf( ' LIMIT %d', $limit );
|
636 |
Â
global $wpdb;
|
637 |
Â
|
638 |
Â
$sql = sprintf( 'SELECT
|
639 |
+
COUNT(*)
|
640 |
+
FROM
|
641 |
+
%sposts
|
642 |
+
WHERE
|
643 |
+
post_status = "publish"
|
644 |
+
AND (post_type = "post" OR post_type = "page")
|
645 |
+
AND (post_content LIKE "%%src=%%"
|
646 |
+
OR post_content LIKE "%%href=%%")
|
647 |
+
', $wpdb->prefix );
|
648 |
Â
|
649 |
Â
return $wpdb->get_var( $sql );
|
650 |
Â
}
|
689 |
Â
return $regexp;
|
690 |
Â
}
|
691 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
692 |
Â
/**
|
693 |
Â
* media_row_actions filter
|
694 |
Â
*
|
Cdn_Plugin_Admin.php
CHANGED
@@ -212,7 +212,7 @@ class Cdn_Plugin_Admin {
|
|
212 |
Â
);
|
213 |
Â
$engine_values['cf'] = array(
|
214 |
Â
'disabled' => ( !Util_Installed::curl() ? true : null ),
|
215 |
-
'label' => __( 'Amazon CloudFront', 'w3-total-cache' ),
|
216 |
Â
'optgroup' => $optgroup_push
|
217 |
Â
);
|
218 |
Â
$engine_values['s3'] = array(
|
212 |
Â
);
|
213 |
Â
$engine_values['cf'] = array(
|
214 |
Â
'disabled' => ( !Util_Installed::curl() ? true : null ),
|
215 |
+
'label' => __( 'Amazon CloudFront Over S3', 'w3-total-cache' ),
|
216 |
Â
'optgroup' => $optgroup_push
|
217 |
Â
);
|
218 |
Â
$engine_values['s3'] = array(
|
Cdn_Util.php
CHANGED
@@ -58,6 +58,7 @@ class Cdn_Util {
|
|
58 |
Â
static public function can_purge_all( $engine ) {
|
59 |
Â
return in_array( $engine, array(
|
60 |
Â
'att',
|
Â
|
|
61 |
Â
'cotendo',
|
62 |
Â
'edgecast',
|
63 |
Â
'highwinds',
|
58 |
Â
static public function can_purge_all( $engine ) {
|
59 |
Â
return in_array( $engine, array(
|
60 |
Â
'att',
|
61 |
+
'cf2',
|
62 |
Â
'cotendo',
|
63 |
Â
'edgecast',
|
64 |
Â
'highwinds',
|
Cdnfsd_CloudFront_Api.php
DELETED
@@ -1,344 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
namespace W3TC;
|
3 |
-
|
4 |
-
class Cdnfsd_CloudFront_Api {
|
5 |
-
private $access_key; // AWS Access key
|
6 |
-
private $secret_Key; // AWS Secret key
|
7 |
-
private $api_host; // AWS host where API is located
|
8 |
-
|
9 |
-
|
10 |
-
public function __construct( $access_key = null, $secret_key = null,
|
11 |
-
$api_host = 'cloudfront.amazonaws.com' ) {
|
12 |
-
$this->access_key = $access_key;
|
13 |
-
$this->secret_key = $secret_key;
|
14 |
-
$this->api_host = $api_host;
|
15 |
-
}
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
/**
|
20 |
-
* Get a list of CloudFront distributions
|
21 |
-
*/
|
22 |
-
public function distributions_list() {
|
23 |
-
$response = $this->request( 'GET', '/2014-11-06/distribution' );
|
24 |
-
$data = $response['body_array'];
|
25 |
-
|
26 |
-
$data = $this->fix_array( $data, array(
|
27 |
-
'Items', 'DistributionSummary' ) );
|
28 |
-
|
29 |
-
if ( isset( $data['Items']['DistributionSummary'] ) ) {
|
30 |
-
foreach ( $data['Items']['DistributionSummary'] as $key => $value ) {
|
31 |
-
$data['Items']['DistributionSummary'][$key] =
|
32 |
-
$this->fix_distribution(
|
33 |
-
$data['Items']['DistributionSummary'][$key] );
|
34 |
-
}
|
35 |
-
}
|
36 |
-
|
37 |
-
return $data;
|
38 |
-
}
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
/**
|
43 |
-
* Get a list of CloudFront distributions
|
44 |
-
*/
|
45 |
-
public function distribution_get( $id ) {
|
46 |
-
$response = $this->request( 'GET', '/2014-11-06/distribution/' . $id );
|
47 |
-
$data = $response['body_array'];
|
48 |
-
|
49 |
-
if ( isset( $data['DistributionConfig'] ) )
|
50 |
-
$data['DistributionConfig'] = $this->fix_distribution(
|
51 |
-
$data['DistributionConfig'] );
|
52 |
-
|
53 |
-
return $data;
|
54 |
-
}
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
/**
|
59 |
-
* $distribution
|
60 |
-
* origin
|
61 |
-
*/
|
62 |
-
public function distribution_create( $distribution ) {
|
63 |
-
if ( !isset( $distribution['CallerReference'] ) )
|
64 |
-
$distribution['CallerReference'] = rand();
|
65 |
-
if ( !isset( $distribution['Enabled'] ) )
|
66 |
-
$distribution['Enabled'] = 'true';
|
67 |
-
|
68 |
-
$data =
|
69 |
-
'<?xml version="1.0" encoding="UTF-8"?>' .
|
70 |
-
'<DistributionConfig xmlns="http://cloudfront.amazonaws.com/doc/2014-11-06/">' .
|
71 |
-
$this->array_to_xml( $distribution ) .
|
72 |
-
'</DistributionConfig>';
|
73 |
-
|
74 |
-
$response = $this->request( 'POST', '/2014-11-06/distribution', $data );
|
75 |
-
$response_data = $response['body_array'];
|
76 |
-
|
77 |
-
return $response_data;
|
78 |
-
}
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
/**
|
83 |
-
* $distribution
|
84 |
-
* origin
|
85 |
-
*/
|
86 |
-
public function distribution_update( $distribution_id, $distribution ) {
|
87 |
-
$get_response = $this->request( 'GET', '/2014-11-06/distribution/' .
|
88 |
-
$distribution_id );
|
89 |
-
$get_distribution = $get_response['body_array'];
|
90 |
-
|
91 |
-
$c = $get_distribution['DistributionConfig'];
|
92 |
-
|
93 |
-
// update values
|
94 |
-
foreach ( $distribution as $key => $value ) {
|
95 |
-
if ( $key == 'DefaultCacheBehavior' ) {
|
96 |
-
// inherit inner values of that setting too
|
97 |
-
foreach ( $distribution[$key] as $key2 => $value2 ) {
|
98 |
-
$c[$key][$key2] = $value2;
|
99 |
-
}
|
100 |
-
} else {
|
101 |
-
$c[$key] = $value;
|
102 |
-
}
|
103 |
-
}
|
104 |
-
|
105 |
-
$data =
|
106 |
-
'<?xml version="1.0" encoding="UTF-8"?>' .
|
107 |
-
'<DistributionConfig xmlns="http://cloudfront.amazonaws.com/doc/2014-11-06/">' .
|
108 |
-
$this->array_to_xml( $c ) .
|
109 |
-
'</DistributionConfig>';
|
110 |
-
|
111 |
-
$response = $this->request( 'PUT',
|
112 |
-
'/2014-11-06/distribution/' . $distribution_id . '/config', $data,
|
113 |
-
array( 'If-Match' => $get_response['headers']['etag'] ) );
|
114 |
-
$data = $response['body_array'];
|
115 |
-
|
116 |
-
if ( isset( $data['DistributionConfig'] ) )
|
117 |
-
$data['DistributionConfig'] = $this->fix_distribution(
|
118 |
-
$data['DistributionConfig'] );
|
119 |
-
|
120 |
-
return $data;
|
121 |
-
}
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
/**
|
126 |
-
* Invalidate cache
|
127 |
-
*/
|
128 |
-
public function invalidation_create( $distribution_id, $uris ) {
|
129 |
-
$data =
|
130 |
-
'<?xml version="1.0" encoding="UTF-8"?>' .
|
131 |
-
'<InvalidationBatch xmlns="http://cloudfront.amazonaws.com/doc/2014-11-06/">' .
|
132 |
-
'<Paths>' .
|
133 |
-
'<Quantity>' . count( $uris ) . '</Quantity>' .
|
134 |
-
$this->array_to_xml( array(
|
135 |
-
'Items' => array(
|
136 |
-
'Path' => $uris
|
137 |
-
)
|
138 |
-
) ) .
|
139 |
-
'</Paths>' .
|
140 |
-
'<CallerReference>' . rand() . '</CallerReference>' .
|
141 |
-
'</InvalidationBatch>';
|
142 |
-
|
143 |
-
$response = $this->request( 'POST',
|
144 |
-
'/2014-11-06/distribution/' . $distribution_id . '/invalidation', $data );
|
145 |
-
$response_data = $response['body_array'];
|
146 |
-
|
147 |
-
return $response_data;
|
148 |
-
}
|
149 |
-
|
150 |
-
/**
|
151 |
-
* Constructor
|
152 |
-
*
|
153 |
-
* @param string $verb Verb
|
154 |
-
* @param string $bucket Bucket name
|
155 |
-
* @param string $uri Object URI
|
156 |
-
* @return mixed
|
157 |
-
*/
|
158 |
-
private function request( $method, $uri = '', $data = '', $headers = array() ) {
|
159 |
-
$url = 'https://' . $this->api_host . $uri;
|
160 |
-
|
161 |
-
$headers['Host'] = $this->api_host;
|
162 |
-
$headers['x-amz-date'] = gmdate( 'Ymd\THis\Z', time() );
|
163 |
-
|
164 |
-
if ( $method == "POST" || $method == "PUT" ) {
|
165 |
-
$headers['Content-Type'] = 'application/xml; charset=utf-8';
|
166 |
-
$headers['Content-Length'] = strlen( $data );
|
167 |
-
} else {
|
168 |
-
$data = '';
|
169 |
-
}
|
170 |
-
|
171 |
-
$headers['Authorization'] = $this->calculateSignature( $method, $uri,
|
172 |
-
$headers, $data );
|
173 |
-
|
174 |
-
|
175 |
-
// do the request
|
176 |
-
$request = array(
|
177 |
-
'sslverify' => false,
|
178 |
-
'headers' => $headers,
|
179 |
-
'method' => $method,
|
180 |
-
'body' => $data
|
181 |
-
);
|
182 |
-
|
183 |
-
$response = wp_remote_request( $url, $request );
|
184 |
-
|
185 |
-
// handle response
|
186 |
-
if ( is_wp_error( $response ) ) {
|
187 |
-
throw new \Exception( 'Failed to reach CloudFront: ' .
|
188 |
-
implode( '; ', $response->get_error_messages() ) );
|
189 |
-
}
|
190 |
-
|
191 |
-
if ( substr( $response['body'], 0, 5 ) != '<?xml' )
|
192 |
-
throw new \Exception( 'Unexpected non-xml response from service received' );
|
193 |
-
|
194 |
-
$xml = simplexml_load_string( $response['body'] );
|
195 |
-
$json = json_encode( $xml );
|
196 |
-
$body_array = json_decode( $json, TRUE );
|
197 |
-
$response['body_array'] = $body_array;
|
198 |
-
|
199 |
-
if ( isset( $body_array['Error'] ) && isset( $body_array['Error']['Message'] ) )
|
200 |
-
throw new \Exception( $body_array['Error']['Message'] );
|
201 |
-
|
202 |
-
return $response;
|
203 |
-
}
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
public function calculateSignature( $method, $uri, $headers, $data ) {
|
208 |
-
$short_date = substr( $headers['x-amz-date'], 0, 8 );
|
209 |
-
|
210 |
-
// Parse the service and region or use one that is explicitly set
|
211 |
-
$region = 'us-east-1';
|
212 |
-
$service = 'cloudfront';
|
213 |
-
|
214 |
-
$credential_scope = $short_date . '/' . $region . '/' . $service .
|
215 |
-
'/aws4_request';
|
216 |
-
|
217 |
-
|
218 |
-
// calc payload
|
219 |
-
if ( empty( $data ) )
|
220 |
-
$payload = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855';
|
221 |
-
else
|
222 |
-
$payload = hash( 'sha256', $data );
|
223 |
-
|
224 |
-
|
225 |
-
// create canonical request
|
226 |
-
$canonical_headers = array(
|
227 |
-
'host:' . $headers['Host'],
|
228 |
-
'x-amz-date:' . $headers['x-amz-date']
|
229 |
-
);
|
230 |
-
|
231 |
-
$signed_headers = 'host;x-amz-date';
|
232 |
-
$canonical_request = $method . "\n" .
|
233 |
-
$this->canonicalize_uri( $uri ) . "\n" .
|
234 |
-
'' /* query string */ . "\n" .
|
235 |
-
implode( "\n", $canonical_headers ) . "\n\n" .
|
236 |
-
$signed_headers . "\n" .
|
237 |
-
$payload;
|
238 |
-
|
239 |
-
$string_to_sign =
|
240 |
-
"AWS4-HMAC-SHA256\n" .
|
241 |
-
$headers['x-amz-date'] . "\n" .
|
242 |
-
$credential_scope . "\n" .
|
243 |
-
hash( 'sha256', $canonical_request );
|
244 |
-
|
245 |
-
// Calculate the signing key using a series of derived keys
|
246 |
-
$signingKey = $this->get_signing_key( $short_date, $region, $service,
|
247 |
-
$this->secret_key );
|
248 |
-
$signature = hash_hmac( 'sha256', $string_to_sign, $signingKey );
|
249 |
-
|
250 |
-
return "AWS4-HMAC-SHA256 " .
|
251 |
-
"Credential={$this->access_key}/{$credential_scope}, " .
|
252 |
-
"SignedHeaders={$signed_headers}, Signature={$signature}";
|
253 |
-
}
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
private function canonicalize_uri( $uri ) {
|
259 |
-
$doubleEncoded = rawurlencode( ltrim( $uri, '/' ) );
|
260 |
-
return '/' . str_replace( '%2F', '/', $doubleEncoded );
|
261 |
-
}
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
private function get_signing_key( $short_date, $region, $service, $secretKey ) {
|
266 |
-
$dateKey = hash_hmac( 'sha256', $short_date, 'AWS4' . $secretKey, true );
|
267 |
-
$regionKey = hash_hmac( 'sha256', $region, $dateKey, true );
|
268 |
-
$serviceKey = hash_hmac( 'sha256', $service, $regionKey, true );
|
269 |
-
|
270 |
-
return hash_hmac( 'sha256', 'aws4_request', $serviceKey, true );
|
271 |
-
}
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
private function array_to_xml( $a ) {
|
276 |
-
if ( !is_array( $a ) )
|
277 |
-
return $a;
|
278 |
-
|
279 |
-
$s = '';
|
280 |
-
foreach ( $a as $key => $value ) {
|
281 |
-
if ( is_array( $value ) && isset( $value[0] ) ) {
|
282 |
-
// number-indexed array, serialized as list
|
283 |
-
foreach ( $value as $array_item ) {
|
284 |
-
$s .=
|
285 |
-
'<' . $key . '>' .
|
286 |
-
$this->array_to_xml( $array_item ) .
|
287 |
-
'</' . $key . '>';
|
288 |
-
}
|
289 |
-
} else {
|
290 |
-
$s .=
|
291 |
-
'<' . $key . '>' .
|
292 |
-
$this->array_to_xml( $value ) .
|
293 |
-
'</' . $key . '>';
|
294 |
-
}
|
295 |
-
}
|
296 |
-
|
297 |
-
return $s;
|
298 |
-
}
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
/**
|
303 |
-
* XML parsing suffers common problem of array recognition
|
304 |
-
* <items><item>a</></> is accessible via $data['items']['item']
|
305 |
-
* but
|
306 |
-
* <items><item>a</><item>b</></> is accessible
|
307 |
-
* via $data['items'][$n]['item']
|
308 |
-
*
|
309 |
-
* by knowing tag we can try to guess that it contains only 1 element
|
310 |
-
* and turn it to array so that it will be always accessible via 2nd way
|
311 |
-
*/
|
312 |
-
private function fix_array( $response, $keys ) {
|
313 |
-
$a = &$response;
|
314 |
-
|
315 |
-
for ( $n = 0; $n < count( $keys ) - 1; $n++ ) {
|
316 |
-
$key = $keys[$n];
|
317 |
-
if ( !isset( $a[$key] ) )
|
318 |
-
break;
|
319 |
-
$a = &$a[$key];
|
320 |
-
}
|
321 |
-
|
322 |
-
$last_key = $keys[count( $keys ) - 1];
|
323 |
-
if ( isset( $a[$last_key] ) ) {
|
324 |
-
if ( !isset( $a[$last_key][0] ) || is_string( $a[$last_key] ) ) {
|
325 |
-
$a[$last_key] = array(
|
326 |
-
$a[$last_key]
|
327 |
-
);
|
328 |
-
}
|
329 |
-
}
|
330 |
-
|
331 |
-
return $response;
|
332 |
-
}
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
private function fix_distribution( $data ) {
|
337 |
-
$data = $this->fix_array( $data, array( 'Aliases', 'Items', 'CNAME' ) );
|
338 |
-
$data = $this->fix_array( $data, array( 'Origins', 'Items', 'Origin' ) );
|
339 |
-
$data = $this->fix_array( $data, array( 'DefaultCacheBehavior',
|
340 |
-
'ForwardedValues', 'Headers', 'Items', 'Name' ) );
|
341 |
-
|
342 |
-
return $data;
|
343 |
-
}
|
344 |
-
}
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
Cdnfsd_CloudFront_Engine.php
CHANGED
@@ -1,6 +1,10 @@
|
|
1 |
Â
<?php
|
2 |
Â
namespace W3TC;
|
3 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
4 |
Â
|
5 |
Â
|
6 |
Â
class Cdnfsd_CloudFront_Engine {
|
@@ -10,7 +14,7 @@ class Cdnfsd_CloudFront_Engine {
|
|
10 |
Â
|
11 |
Â
|
12 |
Â
|
13 |
-
function __construct( $config = array() ) {
|
14 |
Â
$this->access_key = $config['access_key'];
|
15 |
Â
$this->secret_key = $config['secret_key'];
|
16 |
Â
$this->distribution_id = $config['distribution_id'];
|
@@ -18,12 +22,9 @@ class Cdnfsd_CloudFront_Engine {
|
|
18 |
Â
|
19 |
Â
|
20 |
Â
|
21 |
-
function flush_urls( $urls ) {
|
22 |
-
|
23 |
-
empty( $this->distribution_id ) )
|
24 |
-
throw new \Exception( __( 'Access key not specified.', 'w3-total-cache' ) );
|
25 |
Â
|
26 |
-
$api = new Cdnfsd_CloudFront_Api( $this->access_key, $this->secret_key );
|
27 |
Â
$uris = array();
|
28 |
Â
foreach ( $urls as $url ) {
|
29 |
Â
$parsed = parse_url( $url );
|
@@ -33,7 +34,17 @@ class Cdnfsd_CloudFront_Engine {
|
|
33 |
Â
$uris[] = $relative_url;
|
34 |
Â
}
|
35 |
Â
|
36 |
-
$api->
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
37 |
Â
}
|
38 |
Â
|
39 |
Â
|
@@ -41,14 +52,38 @@ class Cdnfsd_CloudFront_Engine {
|
|
41 |
Â
/**
|
42 |
Â
* Flushes CDN completely
|
43 |
Â
*/
|
44 |
-
function flush_all() {
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
45 |
Â
if ( empty( $this->access_key ) || empty( $this->secret_key ) ||
|
46 |
Â
empty( $this->distribution_id ) )
|
47 |
Â
throw new \Exception( __( 'Access key not specified.', 'w3-total-cache' ) );
|
48 |
Â
|
49 |
-
$
|
50 |
-
|
51 |
Â
|
52 |
-
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
53 |
Â
}
|
54 |
Â
}
|
1 |
Â
<?php
|
2 |
Â
namespace W3TC;
|
3 |
Â
|
4 |
+
if ( !defined( 'W3TC_SKIPLIB_AWS' ) ) {
|
5 |
+
require_once W3TC_LIB_DIR . '/Aws/aws-autoloader.php';
|
6 |
+
}
|
7 |
+
|
8 |
Â
|
9 |
Â
|
10 |
Â
class Cdnfsd_CloudFront_Engine {
|
14 |
Â
|
15 |
Â
|
16 |
Â
|
17 |
+
public function __construct( $config = array() ) {
|
18 |
Â
$this->access_key = $config['access_key'];
|
19 |
Â
$this->secret_key = $config['secret_key'];
|
20 |
Â
$this->distribution_id = $config['distribution_id'];
|
22 |
Â
|
23 |
Â
|
24 |
Â
|
25 |
+
public function flush_urls( $urls ) {
|
26 |
+
$api = $this->_api();
|
Â
|
|
Â
|
|
27 |
Â
|
Â
|
|
28 |
Â
$uris = array();
|
29 |
Â
foreach ( $urls as $url ) {
|
30 |
Â
$parsed = parse_url( $url );
|
34 |
Â
$uris[] = $relative_url;
|
35 |
Â
}
|
36 |
Â
|
37 |
+
$api->createInvalidation( array(
|
38 |
+
'DistributionId' => $this->distribution_id,
|
39 |
+
'InvalidationBatch' => array(
|
40 |
+
'CallerReference' => 'w3tc-' . microtime(),
|
41 |
+
'Paths' => array(
|
42 |
+
'Items' => $uris,
|
43 |
+
'Quantity' => count( $uris ),
|
44 |
+
),
|
45 |
+
)
|
46 |
+
)
|
47 |
+
);
|
48 |
Â
}
|
49 |
Â
|
50 |
Â
|
52 |
Â
/**
|
53 |
Â
* Flushes CDN completely
|
54 |
Â
*/
|
55 |
+
public function flush_all() {
|
56 |
+
$api = $this->_api();
|
57 |
+
$uris = array( '/*' );
|
58 |
+
|
59 |
+
$api->createInvalidation( array(
|
60 |
+
'DistributionId' => $this->distribution_id,
|
61 |
+
'InvalidationBatch' => array(
|
62 |
+
'CallerReference' => 'w3tc-' . microtime(),
|
63 |
+
'Paths' => array(
|
64 |
+
'Items' => $uris,
|
65 |
+
'Quantity' => count( $uris ),
|
66 |
+
),
|
67 |
+
)
|
68 |
+
)
|
69 |
+
);
|
70 |
+
}
|
71 |
+
|
72 |
+
|
73 |
+
|
74 |
+
private function _api() {
|
75 |
Â
if ( empty( $this->access_key ) || empty( $this->secret_key ) ||
|
76 |
Â
empty( $this->distribution_id ) )
|
77 |
Â
throw new \Exception( __( 'Access key not specified.', 'w3-total-cache' ) );
|
78 |
Â
|
79 |
+
$credentials = new \Aws\Credentials\Credentials(
|
80 |
+
$this->access_key, $this->secret_key );
|
81 |
Â
|
82 |
+
return new \Aws\CloudFront\CloudFrontClient( array(
|
83 |
+
'credentials' => $credentials,
|
84 |
+
'region' => 'us-east-1',
|
85 |
+
'version' => '2018-11-05'
|
86 |
+
)
|
87 |
+
);
|
88 |
Â
}
|
89 |
Â
}
|
Cdnfsd_CloudFront_Popup.php
CHANGED
@@ -1,6 +1,10 @@
|
|
1 |
Â
<?php
|
2 |
Â
namespace W3TC;
|
3 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
4 |
Â
|
5 |
Â
|
6 |
Â
class Cdnfsd_CloudFront_Popup {
|
@@ -44,7 +48,6 @@ class Cdnfsd_CloudFront_Popup {
|
|
44 |
Â
$access_key = $_REQUEST['access_key'];
|
45 |
Â
$secret_key = $_REQUEST['secret_key'];
|
46 |
Â
|
47 |
-
$api = new Cdnfsd_CloudFront_Api( $access_key, $secret_key );
|
48 |
Â
if ( empty( $access_key ) || empty( $secret_key ) ) {
|
49 |
Â
$this->render_intro( array(
|
50 |
Â
'error_message' => 'Can\'t authenticate: Access Key or Secret not valid'
|
@@ -53,7 +56,13 @@ class Cdnfsd_CloudFront_Popup {
|
|
53 |
Â
}
|
54 |
Â
|
55 |
Â
try {
|
56 |
-
$
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
57 |
Â
} catch ( \Exception $ex ) {
|
58 |
Â
$error_message = 'Can\'t authenticate: ' . $ex->getMessage();
|
59 |
Â
|
@@ -65,12 +74,14 @@ class Cdnfsd_CloudFront_Popup {
|
|
65 |
Â
|
66 |
Â
$items = array();
|
67 |
Â
|
68 |
-
if ( isset( $distributions['
|
69 |
-
foreach ( $distributions['
|
70 |
-
if ( empty( $i['Comment'] ) )
|
71 |
Â
$i['Comment'] = $i['DomainName'];
|
72 |
-
|
73 |
-
|
Â
|
|
Â
|
|
74 |
Â
|
75 |
Â
$items[] = $i;
|
76 |
Â
}
|
@@ -109,9 +120,6 @@ class Cdnfsd_CloudFront_Popup {
|
|
109 |
Â
),
|
110 |
Â
'forward_host' => array(
|
111 |
Â
'new' => true
|
112 |
-
),
|
113 |
-
'alias' => array(
|
114 |
-
'new' => Util_Environment::home_url_host()
|
115 |
Â
)
|
116 |
Â
);
|
117 |
Â
|
@@ -119,10 +127,13 @@ class Cdnfsd_CloudFront_Popup {
|
|
119 |
Â
// create new zone mode
|
120 |
Â
$details['distribution_comment'] = Util_Request::get( 'comment_new' );
|
121 |
Â
} else {
|
122 |
-
|
123 |
Â
|
124 |
Â
try {
|
125 |
-
$
|
Â
|
|
Â
|
|
Â
|
|
126 |
Â
} catch ( \Exception $ex ) {
|
127 |
Â
$this->render_intro( array(
|
128 |
Â
'error_message' => 'Can\'t obtain zone: ' . $ex->getMessage()
|
@@ -130,8 +141,8 @@ class Cdnfsd_CloudFront_Popup {
|
|
130 |
Â
exit();
|
131 |
Â
}
|
132 |
Â
|
133 |
-
if ( isset( $distribution['DistributionConfig'] ) )
|
134 |
-
$c = $distribution['DistributionConfig'];
|
135 |
Â
else
|
136 |
Â
$c = array();
|
137 |
Â
|
@@ -158,9 +169,6 @@ class Cdnfsd_CloudFront_Popup {
|
|
158 |
Â
( isset( $b['Cookies'] ) && isset( $b['Cookies']['Forward'] ) &&
|
159 |
Â
$b['Cookies']['Forward'] == 'all' );
|
160 |
Â
|
161 |
-
if ( isset( $c['Aliases']['Items']['CNAME'][0] ) )
|
162 |
-
$details['alias']['current'] = $c['Aliases']['Items']['CNAME'][0];
|
163 |
-
|
164 |
Â
$details['forward_host']['current'] = false;
|
165 |
Â
if ( isset( $b['Headers']['Items']['Name'] ) ) {
|
166 |
Â
foreach ( $b['Headers']['Items']['Name'] as $name )
|
@@ -247,78 +255,84 @@ class Cdnfsd_CloudFront_Popup {
|
|
247 |
Â
$origin_id = rand();
|
248 |
Â
|
249 |
Â
$distribution = array(
|
250 |
-
'
|
251 |
-
|
252 |
-
'
|
253 |
-
'
|
254 |
-
'
|
255 |
-
'
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
278 |
Â
),
|
279 |
-
'
|
280 |
-
'Quantity' => 1,
|
281 |
-
'Items' => array(
|
282 |
-
'Name' => 'Host'
|
283 |
-
)
|
284 |
-
)
|
285 |
Â
),
|
286 |
-
'
|
287 |
-
|
288 |
Â
'Items' => array(
|
289 |
-
|
290 |
-
'
|
291 |
-
'
|
292 |
-
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
293 |
Â
),
|
294 |
-
'
|
295 |
-
'Quantity' => 2,
|
296 |
-
'Items' => array(
|
297 |
-
'Method' => array(
|
298 |
-
'GET', 'HEAD'
|
299 |
-
)
|
300 |
-
)
|
301 |
-
)
|
302 |
Â
),
|
303 |
-
'
|
Â
|
|
Â
|
|
304 |
Â
)
|
305 |
Â
);
|
306 |
Â
|
307 |
Â
try {
|
308 |
-
$api =
|
309 |
Â
if ( empty( $distribution_id ) ) {
|
310 |
-
$distribution['DefaultCacheBehavior']['TrustedSigners'] = array(
|
311 |
-
'Enabled' => 'false',
|
312 |
-
'Quantity' => 0
|
313 |
-
);
|
314 |
-
$distribution['DefaultCacheBehavior']['ViewerProtocolPolicy'] =
|
315 |
-
'allow-all';
|
316 |
Â
|
317 |
-
$response = $api->
|
318 |
-
$distribution_id = $response['Id'];
|
319 |
Â
} else {
|
320 |
-
$
|
Â
|
|
321 |
Â
}
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
322 |
Â
} catch ( \Exception $ex ) {
|
323 |
Â
$this->render_intro( array(
|
324 |
Â
'error_message' => 'Failed to configure distribution: ' . $ex->getMessage()
|
@@ -326,17 +340,18 @@ class Cdnfsd_CloudFront_Popup {
|
|
326 |
Â
exit();
|
327 |
Â
}
|
328 |
Â
|
329 |
-
$distribution_domain = $response['DomainName'];
|
330 |
Â
|
331 |
Â
$c = Dispatcher::config();
|
332 |
Â
$c->set( 'cdnfsd.cloudfront.access_key', $access_key );
|
333 |
Â
$c->set( 'cdnfsd.cloudfront.secret_key', $secret_key );
|
334 |
Â
$c->set( 'cdnfsd.cloudfront.distribution_id', $distribution_id );
|
335 |
Â
$c->set( 'cdnfsd.cloudfront.distribution_domain', $distribution_domain );
|
Â
|
|
336 |
Â
$c->save();
|
337 |
Â
|
338 |
Â
$details = array(
|
339 |
-
'name' => $distribution['Comment'],
|
340 |
Â
'home_domain' => Util_Environment::home_url_host(),
|
341 |
Â
'dns_cname_target' => $distribution_domain,
|
342 |
Â
);
|
@@ -355,8 +370,10 @@ class Cdnfsd_CloudFront_Popup {
|
|
355 |
Â
$origin_id = rand();
|
356 |
Â
|
357 |
Â
try {
|
358 |
-
$api =
|
359 |
-
$distribution = $api->
|
Â
|
|
Â
|
|
360 |
Â
} catch ( \Exception $ex ) {
|
361 |
Â
$this->render_intro( array(
|
362 |
Â
'error_message' => 'Failed to configure distribution: ' . $ex->getMessage()
|
@@ -364,8 +381,8 @@ class Cdnfsd_CloudFront_Popup {
|
|
364 |
Â
exit();
|
365 |
Â
}
|
366 |
Â
|
367 |
-
if ( isset( $distribution['DomainName'] ) )
|
368 |
-
$distribution_domain = $distribution['DomainName'];
|
369 |
Â
else
|
370 |
Â
$distribution_domain = 'n/a';
|
371 |
Â
|
@@ -377,7 +394,7 @@ class Cdnfsd_CloudFront_Popup {
|
|
377 |
Â
$c->save();
|
378 |
Â
|
379 |
Â
$details = array(
|
380 |
-
'name' => $distribution['Comment'],
|
381 |
Â
'home_domain' => Util_Environment::home_url_host(),
|
382 |
Â
'dns_cname_target' => $distribution_domain,
|
383 |
Â
);
|
@@ -385,4 +402,18 @@ class Cdnfsd_CloudFront_Popup {
|
|
385 |
Â
include W3TC_DIR . '/Cdnfsd_CloudFront_Popup_View_Success.php';
|
386 |
Â
exit();
|
387 |
Â
}
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
388 |
Â
}
|
1 |
Â
<?php
|
2 |
Â
namespace W3TC;
|
3 |
Â
|
4 |
+
if ( !defined( 'W3TC_SKIPLIB_AWS' ) ) {
|
5 |
+
require_once W3TC_LIB_DIR . '/Aws/aws-autoloader.php';
|
6 |
+
}
|
7 |
+
|
8 |
Â
|
9 |
Â
|
10 |
Â
class Cdnfsd_CloudFront_Popup {
|
48 |
Â
$access_key = $_REQUEST['access_key'];
|
49 |
Â
$secret_key = $_REQUEST['secret_key'];
|
50 |
Â
|
Â
|
|
51 |
Â
if ( empty( $access_key ) || empty( $secret_key ) ) {
|
52 |
Â
$this->render_intro( array(
|
53 |
Â
'error_message' => 'Can\'t authenticate: Access Key or Secret not valid'
|
56 |
Â
}
|
57 |
Â
|
58 |
Â
try {
|
59 |
+
$api = $this->_api( $access_key, $secret_key );
|
60 |
+
$distributions = $api->listDistributions();
|
61 |
+
} catch ( \Aws\Exception\AwsException $ex ) {
|
62 |
+
$this->render_intro( array(
|
63 |
+
'error_message' => 'Can\'t authenticate: ' .
|
64 |
+
$ex->getAwsErrorMessage() ) );
|
65 |
+
exit();
|
66 |
Â
} catch ( \Exception $ex ) {
|
67 |
Â
$error_message = 'Can\'t authenticate: ' . $ex->getMessage();
|
68 |
Â
|
74 |
Â
|
75 |
Â
$items = array();
|
76 |
Â
|
77 |
+
if ( isset( $distributions['DistributionList']['Items'] ) ) {
|
78 |
+
foreach ( $distributions['DistributionList']['Items'] as $i ) {
|
79 |
+
if ( empty( $i['Comment'] ) ) {
|
80 |
Â
$i['Comment'] = $i['DomainName'];
|
81 |
+
}
|
82 |
+
if ( isset( $i['Origins']['Items'][0]['DomainName'] ) ) {
|
83 |
+
$i['Origin_DomainName'] = $i['Origins']['Items'][0]['DomainName'];
|
84 |
+
}
|
85 |
Â
|
86 |
Â
$items[] = $i;
|
87 |
Â
}
|
120 |
Â
),
|
121 |
Â
'forward_host' => array(
|
122 |
Â
'new' => true
|
Â
|
|
Â
|
|
Â
|
|
123 |
Â
)
|
124 |
Â
);
|
125 |
Â
|
127 |
Â
// create new zone mode
|
128 |
Â
$details['distribution_comment'] = Util_Request::get( 'comment_new' );
|
129 |
Â
} else {
|
130 |
+
|
131 |
Â
|
132 |
Â
try {
|
133 |
+
$api = $this->_api( $access_key, $secret_key );
|
134 |
+
$distribution = $api->getDistribution(
|
135 |
+
array( 'Id' => $distribution_id )
|
136 |
+
);
|
137 |
Â
} catch ( \Exception $ex ) {
|
138 |
Â
$this->render_intro( array(
|
139 |
Â
'error_message' => 'Can\'t obtain zone: ' . $ex->getMessage()
|
141 |
Â
exit();
|
142 |
Â
}
|
143 |
Â
|
144 |
+
if ( isset( $distribution['Distribution']['DistributionConfig'] ) )
|
145 |
+
$c = $distribution['Distribution']['DistributionConfig'];
|
146 |
Â
else
|
147 |
Â
$c = array();
|
148 |
Â
|
169 |
Â
( isset( $b['Cookies'] ) && isset( $b['Cookies']['Forward'] ) &&
|
170 |
Â
$b['Cookies']['Forward'] == 'all' );
|
171 |
Â
|
Â
|
|
Â
|
|
Â
|
|
172 |
Â
$details['forward_host']['current'] = false;
|
173 |
Â
if ( isset( $b['Headers']['Items']['Name'] ) ) {
|
174 |
Â
foreach ( $b['Headers']['Items']['Name'] as $name )
|
255 |
Â
$origin_id = rand();
|
256 |
Â
|
257 |
Â
$distribution = array(
|
258 |
+
'DistributionConfig' => array(
|
259 |
+
'CallerReference' => $origin_id,
|
260 |
+
'Comment' => Util_Request::get( 'distribution_comment' ),
|
261 |
+
'DefaultCacheBehavior' => array(
|
262 |
+
'AllowedMethods' => array(
|
263 |
+
'CachedMethods' => array(
|
264 |
+
'Items' => array( 'HEAD', 'GET' ),
|
265 |
+
'Quantity' => 2,
|
266 |
+
),
|
267 |
+
'Items' => array( 'HEAD', 'GET' ),
|
268 |
+
'Quantity' => 2,
|
269 |
+
),
|
270 |
+
'Compress' => true,
|
271 |
+
'DefaultTTL' => 86400,
|
272 |
+
'FieldLevelEncryptionId' => '',
|
273 |
+
'ForwardedValues' => array(
|
274 |
+
'Cookies' => array(
|
275 |
+
'Forward' => 'all',
|
276 |
+
),
|
277 |
+
'Headers' => array(
|
278 |
+
'Quantity' => 1,
|
279 |
+
'Items' => array(
|
280 |
+
'Name' => 'Host'
|
281 |
+
)
|
282 |
+
),
|
283 |
+
'QueryString' => true,
|
284 |
+
'QueryStringCacheKeys' => array(
|
285 |
+
'Quantity' => 0,
|
286 |
+
),
|
287 |
+
),
|
288 |
+
'LambdaFunctionAssociations' => array( 'Quantity' => 0),
|
289 |
+
'MinTTL' => 0,
|
290 |
+
'SmoothStreaming' => false,
|
291 |
+
'TargetOriginId' => $origin_id,
|
292 |
+
'TrustedSigners' => array(
|
293 |
+
'Enabled' => false,
|
294 |
+
'Quantity' => 0,
|
295 |
Â
),
|
296 |
+
'ViewerProtocolPolicy' => 'allow-all',
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
297 |
Â
),
|
298 |
+
'Enabled' => true,
|
299 |
+
'Origins' => array(
|
300 |
Â
'Items' => array(
|
301 |
+
array(
|
302 |
+
'DomainName' => Util_Request::get( 'origin' ),
|
303 |
+
'Id' => $origin_id,
|
304 |
+
'OriginPath' => '',
|
305 |
+
'CustomHeaders' => array( 'Quantity' => 0 ),
|
306 |
+
'CustomOriginConfig' => array(
|
307 |
+
'HTTPPort' => 80,
|
308 |
+
'HTTPSPort' => 443,
|
309 |
+
'OriginProtocolPolicy' => 'match-viewer'
|
310 |
+
),
|
311 |
+
),
|
312 |
Â
),
|
313 |
+
'Quantity' => 1,
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
314 |
Â
),
|
315 |
+
'Aliases' => array(
|
316 |
+
'Quantity' => 0
|
317 |
+
)
|
318 |
Â
)
|
319 |
Â
);
|
320 |
Â
|
321 |
Â
try {
|
322 |
+
$api = $this->_api( $access_key, $secret_key );
|
323 |
Â
if ( empty( $distribution_id ) ) {
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
324 |
Â
|
325 |
+
$response = $api->createDistribution( $distribution );
|
326 |
+
$distribution_id = $response['Distribution']['Id'];
|
327 |
Â
} else {
|
328 |
+
$distribution['Id'] = $distribution_id;
|
329 |
+
$response = $api->UpdateDistribution( $distribution );
|
330 |
Â
}
|
331 |
+
} catch ( \Aws\Exception\AwsException $ex ) {
|
332 |
+
$this->render_intro( array(
|
333 |
+
'error_message' => 'Unable to create distribution: ' .
|
334 |
+
$ex->getAwsErrorMessage() ) );
|
335 |
+
exit();
|
336 |
Â
} catch ( \Exception $ex ) {
|
337 |
Â
$this->render_intro( array(
|
338 |
Â
'error_message' => 'Failed to configure distribution: ' . $ex->getMessage()
|
340 |
Â
exit();
|
341 |
Â
}
|
342 |
Â
|
343 |
+
$distribution_domain = $response['Distribution']['DomainName'];
|
344 |
Â
|
345 |
Â
$c = Dispatcher::config();
|
346 |
Â
$c->set( 'cdnfsd.cloudfront.access_key', $access_key );
|
347 |
Â
$c->set( 'cdnfsd.cloudfront.secret_key', $secret_key );
|
348 |
Â
$c->set( 'cdnfsd.cloudfront.distribution_id', $distribution_id );
|
349 |
Â
$c->set( 'cdnfsd.cloudfront.distribution_domain', $distribution_domain );
|
350 |
+
|
351 |
Â
$c->save();
|
352 |
Â
|
353 |
Â
$details = array(
|
354 |
+
'name' => $distribution['DistributionConfig']['Comment'],
|
355 |
Â
'home_domain' => Util_Environment::home_url_host(),
|
356 |
Â
'dns_cname_target' => $distribution_domain,
|
357 |
Â
);
|
370 |
Â
$origin_id = rand();
|
371 |
Â
|
372 |
Â
try {
|
373 |
+
$api = $this->_api( $access_key, $secret_key );
|
374 |
+
$distribution = $api->getDistribution(
|
375 |
+
array( 'Id' => $distribution_id )
|
376 |
+
);
|
377 |
Â
} catch ( \Exception $ex ) {
|
378 |
Â
$this->render_intro( array(
|
379 |
Â
'error_message' => 'Failed to configure distribution: ' . $ex->getMessage()
|
381 |
Â
exit();
|
382 |
Â
}
|
383 |
Â
|
384 |
+
if ( isset( $distribution['Distribution']['DomainName'] ) )
|
385 |
+
$distribution_domain = $distribution['Distribution']['DomainName'];
|
386 |
Â
else
|
387 |
Â
$distribution_domain = 'n/a';
|
388 |
Â
|
394 |
Â
$c->save();
|
395 |
Â
|
396 |
Â
$details = array(
|
397 |
+
'name' => $distribution['Distribution']['Comment'],
|
398 |
Â
'home_domain' => Util_Environment::home_url_host(),
|
399 |
Â
'dns_cname_target' => $distribution_domain,
|
400 |
Â
);
|
402 |
Â
include W3TC_DIR . '/Cdnfsd_CloudFront_Popup_View_Success.php';
|
403 |
Â
exit();
|
404 |
Â
}
|
405 |
+
|
406 |
+
|
407 |
+
|
408 |
+
private function _api( $access_key, $secret_key ) {
|
409 |
+
$credentials = new \Aws\Credentials\Credentials(
|
410 |
+
$access_key, $secret_key );
|
411 |
+
|
412 |
+
return new \Aws\CloudFront\CloudFrontClient( array(
|
413 |
+
'credentials' => $credentials,
|
414 |
+
'region' => 'us-east-1',
|
415 |
+
'version' => '2018-11-05'
|
416 |
+
)
|
417 |
+
);
|
418 |
+
}
|
419 |
Â
}
|
Cdnfsd_CloudFront_Popup_View_Distribution.php
CHANGED
@@ -5,59 +5,57 @@ if ( !defined( 'W3TC' ) )
|
|
5 |
Â
die();
|
6 |
Â
?>
|
7 |
Â
<form class="w3tc_popup_form" method="post">
|
8 |
-
|
9 |
Â
Util_Ui::hidden( '', 'access_key', $details['access_key'] );
|
10 |
Â
Util_Ui::hidden( '', 'secret_key', $details['secret_key'] );
|
11 |
Â
Util_Ui::hidden( '', 'distribution_id', $details['distribution_id'] );
|
12 |
Â
Util_Ui::hidden( '', 'distribution_comment', $details['distribution_comment'] );
|
13 |
Â
?>
|
14 |
Â
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
Â
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
<th>Forward Host Header:</th>
|
49 |
-
<td><?php $this->render_zone_boolean_change( $details, 'forward_host' ) ?></td>
|
50 |
-
</tr>
|
51 |
-
</table>
|
52 |
Â
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
Â
|
|
Â
|
|
63 |
Â
</form>
|
5 |
Â
die();
|
6 |
Â
?>
|
7 |
Â
<form class="w3tc_popup_form" method="post">
|
8 |
+
<?php
|
9 |
Â
Util_Ui::hidden( '', 'access_key', $details['access_key'] );
|
10 |
Â
Util_Ui::hidden( '', 'secret_key', $details['secret_key'] );
|
11 |
Â
Util_Ui::hidden( '', 'distribution_id', $details['distribution_id'] );
|
12 |
Â
Util_Ui::hidden( '', 'distribution_comment', $details['distribution_comment'] );
|
13 |
Â
?>
|
14 |
Â
|
15 |
+
<div class="metabox-holder">
|
16 |
+
<?php Util_Ui::postbox_header( __( 'Configure distribution', 'w3-total-cache' ) ); ?>
|
17 |
+
<table class="form-table">
|
18 |
+
<tr>
|
19 |
+
<th>Distribution:</th>
|
20 |
+
<td><?php echo $details['distribution_comment'] ?></td>
|
21 |
+
</tr>
|
22 |
+
<tr>
|
23 |
+
<th>Origin:</th>
|
24 |
+
<td><?php $this->render_zone_ip_change( $details, 'origin' ) ?><br />
|
25 |
+
<span class="description">
|
26 |
+
Create an apex <acronym title="Domain Name System">DNS</acronym> record pointing to your WordPress host <acronym title="Internet Protocol">IP</acronym>.
|
27 |
+
CloudFront will use this host to mirror your site.
|
28 |
Â
|
29 |
+
Tip: If your real domain name is domain.com, then the host
|
30 |
+
for the apex record should be origin.domain.com with the host
|
31 |
+
<acronym title="Internet Protocol">IP</acronym> of domain.com, e.g.:
|
32 |
+
</span>
|
33 |
+
</td>
|
34 |
+
</tr>
|
35 |
+
<tr>
|
36 |
+
<th>Forward Cookies:</th>
|
37 |
+
<td><?php $this->render_zone_boolean_change( $details, 'forward_cookies' ) ?></td>
|
38 |
+
</tr>
|
39 |
+
<tr>
|
40 |
+
<th>Forward Query String:</th>
|
41 |
+
<td><?php $this->render_zone_boolean_change( $details, 'forward_querystring' ) ?></td>
|
42 |
+
</tr>
|
43 |
+
<tr>
|
44 |
+
<th>Forward Host Header:</th>
|
45 |
+
<td><?php $this->render_zone_boolean_change( $details, 'forward_host' ) ?></td>
|
46 |
+
</tr>
|
47 |
+
</table>
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
48 |
Â
|
49 |
+
<p class="submit">
|
50 |
+
<input type="button"
|
51 |
+
class="w3tc_cdn_cloudfront_fsd_configure_distribution w3tc-button-save button-primary"
|
52 |
+
value="<?php _e( 'Apply', 'w3-total-cache' ); ?>" />
|
53 |
+
<?php if ( !empty( $details['distribution_id'] ) ): ?>
|
54 |
+
<input type="button"
|
55 |
+
class="w3tc_cdn_cloudfront_fsd_configure_distribution_skip w3tc-button-save button"
|
56 |
+
value="<?php _e( 'Don\'t reconfigure, I know what I\'m doing', 'w3-total-cache' ); ?>" />
|
57 |
+
<?php endif ?>
|
58 |
+
</p>
|
59 |
+
<?php Util_Ui::postbox_footer(); ?>
|
60 |
+
</div>
|
61 |
Â
</form>
|
Config.php
CHANGED
@@ -7,16 +7,16 @@ namespace W3TC;
|
|
7 |
Â
*/
|
8 |
Â
class Config {
|
9 |
Â
/*
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
Â
private $_blog_id;
|
14 |
Â
private $_is_master;
|
15 |
Â
|
16 |
Â
/*
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
Â
private $_preview;
|
21 |
Â
|
22 |
Â
private $_md5;
|
@@ -71,9 +71,9 @@ class Config {
|
|
71 |
Â
|
72 |
Â
|
73 |
Â
/*
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
Â
static public function util_config_filename( $blog_id, $preview ) {
|
78 |
Â
$postfix = ( $preview ? '-preview' : '' ) . '.php';
|
79 |
Â
|
@@ -86,10 +86,10 @@ class Config {
|
|
86 |
Â
|
87 |
Â
|
88 |
Â
/*
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
Â
static public function util_config_filename_legacy_v2( $blog_id, $preview ) {
|
94 |
Â
$postfix = ( $preview ? '-preview' : '' ) . '.json';
|
95 |
Â
|
7 |
Â
*/
|
8 |
Â
class Config {
|
9 |
Â
/*
|
10 |
+
* blog id of loaded config
|
11 |
+
* @var integer
|
12 |
+
*/
|
13 |
Â
private $_blog_id;
|
14 |
Â
private $_is_master;
|
15 |
Â
|
16 |
Â
/*
|
17 |
+
* Is this preview config
|
18 |
+
* @var boolean
|
19 |
+
*/
|
20 |
Â
private $_preview;
|
21 |
Â
|
22 |
Â
private $_md5;
|
71 |
Â
|
72 |
Â
|
73 |
Â
/*
|
74 |
+
* Returns config filename
|
75 |
+
* Stored in this class to limit class loading
|
76 |
+
*/
|
77 |
Â
static public function util_config_filename( $blog_id, $preview ) {
|
78 |
Â
$postfix = ( $preview ? '-preview' : '' ) . '.php';
|
79 |
Â
|
86 |
Â
|
87 |
Â
|
88 |
Â
/*
|
89 |
+
* Returns config filename
|
90 |
+
* Stored in this class to limit class loading
|
91 |
+
* v = 0.9.5 - 0.9.5.1
|
92 |
+
*/
|
93 |
Â
static public function util_config_filename_legacy_v2( $blog_id, $preview ) {
|
94 |
Â
$postfix = ( $preview ? '-preview' : '' ) . '.json';
|
95 |
Â
|
ConfigKeys.php
CHANGED
@@ -2260,10 +2260,6 @@ $keys = array(
|
|
2260 |
Â
'type' => 'integer',
|
2261 |
Â
'default' => 300
|
2262 |
Â
),
|
2263 |
-
'timelimit.cdn_container_create' => array(
|
2264 |
-
'type' => 'integer',
|
2265 |
-
'default' => 300
|
2266 |
-
),
|
2267 |
Â
'timelimit.domain_rename' => array(
|
2268 |
Â
'type' => 'integer',
|
2269 |
Â
'default' => 120
|
2260 |
Â
'type' => 'integer',
|
2261 |
Â
'default' => 300
|
2262 |
Â
),
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
2263 |
Â
'timelimit.domain_rename' => array(
|
2264 |
Â
'type' => 'integer',
|
2265 |
Â
'default' => 120
|
DbCache_Wpdb.php
CHANGED
@@ -225,6 +225,13 @@ class DbCache_Wpdb extends DbCache_WpdbBase {
|
|
225 |
Â
return $this->active_processor->set_charset( $dbh, $charset, $collate );
|
226 |
Â
}
|
227 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
228 |
Â
/**
|
229 |
Â
* Overriten logic of wp_db by processor.
|
230 |
Â
*/
|
@@ -327,6 +334,13 @@ class DbCache_Wpdb extends DbCache_WpdbBase {
|
|
327 |
Â
return parent::set_charset( $dbh, $charset, $collate );
|
328 |
Â
}
|
329 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
330 |
Â
/**
|
331 |
Â
* Default implementation, calls wp_db apropriate method
|
332 |
Â
*/
|
225 |
Â
return $this->active_processor->set_charset( $dbh, $charset, $collate );
|
226 |
Â
}
|
227 |
Â
|
228 |
+
/**
|
229 |
+
* Overriten logic of wp_db by processor.
|
230 |
+
*/
|
231 |
+
public function set_sql_mode( $modes = array() ) {
|
232 |
+
return $this->active_processor->set_sql_mode( $modes );
|
233 |
+
}
|
234 |
+
|
235 |
Â
/**
|
236 |
Â
* Overriten logic of wp_db by processor.
|
237 |
Â
*/
|
334 |
Â
return parent::set_charset( $dbh, $charset, $collate );
|
335 |
Â
}
|
336 |
Â
|
337 |
+
/**
|
338 |
+
* Default implementation, calls wp_db apropriate method
|
339 |
+
*/
|
340 |
+
public function default_set_sql_mode( $modes = array() ) {
|
341 |
+
return parent::set_sql_mode( $modes );
|
342 |
+
}
|
343 |
+
|
344 |
Â
/**
|
345 |
Â
* Default implementation, calls wp_db apropriate method
|
346 |
Â
*/
|
DbCache_WpdbInjection.php
CHANGED
@@ -110,6 +110,14 @@ class DbCache_WpdbInjection {
|
|
110 |
Â
return $this->wpdb_mixin->default_set_charset( $dbh, $charset, $collate );
|
111 |
Â
}
|
112 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
113 |
Â
/**
|
114 |
Â
* Placeholder for apropriate wp_db method replacement.
|
115 |
Â
* By default calls wp_db implementation
|
110 |
Â
return $this->wpdb_mixin->default_set_charset( $dbh, $charset, $collate );
|
111 |
Â
}
|
112 |
Â
|
113 |
+
/**
|
114 |
+
* Placeholder for apropriate wp_db method replacement.
|
115 |
+
* By default calls wp_db implementation
|
116 |
+
*/
|
117 |
+
function set_sql_mode( $modes = array() ) {
|
118 |
+
return $this->wpdb_mixin->default_set_sql_mode( $modes );
|
119 |
+
}
|
120 |
+
|
121 |
Â
/**
|
122 |
Â
* Placeholder for apropriate wp_db method replacement.
|
123 |
Â
* By default calls wp_db implementation
|
Enterprise_CacheFlush_MakeSnsEvent.php
CHANGED
@@ -215,9 +215,12 @@ class Enterprise_CacheFlush_MakeSnsEvent extends Enterprise_SnsBase {
|
|
215 |
Â
}
|
216 |
Â
$this->_log( 'Backtrace ', $backtrace_optimized );
|
217 |
Â
|
218 |
-
$r = $api->publish(
|
219 |
-
|
220 |
-
|
Â
|
|
Â
|
|
Â
|
|
221 |
Â
return false;
|
222 |
Â
}
|
223 |
Â
} catch ( \Exception $e ) {
|
215 |
Â
}
|
216 |
Â
$this->_log( 'Backtrace ', $backtrace_optimized );
|
217 |
Â
|
218 |
+
$r = $api->publish( array(
|
219 |
+
'Message' => $v,
|
220 |
+
'TopicArn' => $this->_topic_arn ) );
|
221 |
+
if ( $r['@metadata']['statusCode'] != 200 ) {
|
222 |
+
$this->_log( "Error" );
|
223 |
+
$this->_log( json_encode($r) );
|
224 |
Â
return false;
|
225 |
Â
}
|
226 |
Â
} catch ( \Exception $e ) {
|
Enterprise_Dbcache_WpdbInjection_Cluster.php
CHANGED
@@ -447,6 +447,7 @@ class Enterprise_Dbcache_WpdbInjection_Cluster extends DbCache_WpdbInjection {
|
|
447 |
Â
$dbh = $this->_connections[$dbhname]['dbh'];
|
448 |
Â
$this->wpdb_mixin->dbh = $dbh; // needed by $wpdb->_real_escape()
|
449 |
Â
$this->set_charset( $dbh, $this->charset, $this->collate );
|
Â
|
|
450 |
Â
|
451 |
Â
return $dbh;
|
452 |
Â
}
|
447 |
Â
$dbh = $this->_connections[$dbhname]['dbh'];
|
448 |
Â
$this->wpdb_mixin->dbh = $dbh; // needed by $wpdb->_real_escape()
|
449 |
Â
$this->set_charset( $dbh, $this->charset, $this->collate );
|
450 |
+
$this->set_sql_mode();
|
451 |
Â
|
452 |
Â
return $dbh;
|
453 |
Â
}
|
Enterprise_SnsBase.php
CHANGED
@@ -1,14 +1,14 @@
|
|
1 |
Â
<?php
|
2 |
Â
namespace W3TC;
|
3 |
Â
|
Â
|
|
Â
|
|
Â
|
|
4 |
Â
|
5 |
Â
|
6 |
-
/**
|
7 |
-
* Purge using AmazonSNS object
|
8 |
-
*/
|
9 |
Â
|
10 |
Â
/**
|
11 |
-
* class Sns
|
12 |
Â
*/
|
13 |
Â
class Enterprise_SnsBase {
|
14 |
Â
/**
|
@@ -39,11 +39,15 @@ class Enterprise_SnsBase {
|
|
39 |
Â
if ( $this->_api_secret == '' )
|
40 |
Â
throw new \Exception( 'API Secret is not configured' );
|
41 |
Â
|
42 |
-
|
43 |
-
$
|
44 |
-
|
45 |
-
|
46 |
-
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
47 |
Â
}
|
48 |
Â
|
49 |
Â
return $this->_api;
|
1 |
Â
<?php
|
2 |
Â
namespace W3TC;
|
3 |
Â
|
4 |
+
if ( !defined( 'W3TC_SKIPLIB_AWS' ) ) {
|
5 |
+
require_once W3TC_LIB_DIR . '/Aws/aws-autoloader.php';
|
6 |
+
}
|
7 |
Â
|
8 |
Â
|
Â
|
|
Â
|
|
Â
|
|
9 |
Â
|
10 |
Â
/**
|
11 |
+
* Base class for Sns communication
|
12 |
Â
*/
|
13 |
Â
class Enterprise_SnsBase {
|
14 |
Â
/**
|
39 |
Â
if ( $this->_api_secret == '' )
|
40 |
Â
throw new \Exception( 'API Secret is not configured' );
|
41 |
Â
|
42 |
+
|
43 |
+
$credentials = new \Aws\Credentials\Credentials(
|
44 |
+
$this->_api_key, $this->_api_secret );
|
45 |
+
|
46 |
+
$this->_api = new \Aws\Sns\SnsClient( array(
|
47 |
+
'credentials' => $credentials,
|
48 |
+
'region' => $this->_region,
|
49 |
+
'version' => '2010-03-31'
|
50 |
+
) );
|
51 |
Â
}
|
52 |
Â
|
53 |
Â
return $this->_api;
|
Enterprise_SnsServer.php
CHANGED
@@ -4,13 +4,6 @@ namespace W3TC;
|
|
4 |
Â
/**
|
5 |
Â
* Purge using AmazonSNS object
|
6 |
Â
*/
|
7 |
-
|
8 |
-
require_once W3TC_LIB_DIR . '/SNS/services/MessageValidator/Message.php';
|
9 |
-
require_once W3TC_LIB_DIR . '/SNS/services/MessageValidator/MessageValidator.php';
|
10 |
-
|
11 |
-
/**
|
12 |
-
* class Sns
|
13 |
-
*/
|
14 |
Â
class Enterprise_SnsServer extends Enterprise_SnsBase {
|
15 |
Â
|
16 |
Â
/**
|
@@ -18,24 +11,24 @@ class Enterprise_SnsServer extends Enterprise_SnsBase {
|
|
18 |
Â
*
|
19 |
Â
* @throws Exception
|
20 |
Â
*/
|
21 |
-
function process_message() {
|
22 |
Â
$this->_log( 'Received message' );
|
23 |
Â
|
24 |
Â
try {
|
25 |
-
$message = \Message
|
26 |
-
$validator = new \MessageValidator();
|
27 |
Â
$error = '';
|
28 |
Â
if ( $validator->isValid( $message ) ) {
|
29 |
Â
$topic_arn = $this->_config->get_string( 'cluster.messagebus.sns.topic_arn' );
|
30 |
Â
|
31 |
-
if ( empty( $topic_arn ) || $topic_arn != $message
|
32 |
-
throw new \Exception
|
33 |
-
$message
|
34 |
Â
|
35 |
-
if ( $message
|
36 |
Â
$this->_subscription_confirmation( $message );
|
37 |
-
elseif ( $message
|
38 |
-
$this->_notification( $message
|
39 |
Â
} else {
|
40 |
Â
$this->_log( 'Error processing message it was not valid.' );
|
41 |
Â
}
|
@@ -55,10 +48,12 @@ class Enterprise_SnsServer extends Enterprise_SnsBase {
|
|
55 |
Â
$this->_log( 'Issuing confirm_subscription' );
|
56 |
Â
$topic_arn = $this->_config->get_string( 'cluster.messagebus.sns.topic_arn' );
|
57 |
Â
|
58 |
-
$response = $this->_get_api()->
|
59 |
-
|
Â
|
|
Â
|
|
60 |
Â
$this->_log( 'Subscription confirmed: ' .
|
61 |
-
( $response
|
62 |
Â
}
|
63 |
Â
|
64 |
Â
/**
|
4 |
Â
/**
|
5 |
Â
* Purge using AmazonSNS object
|
6 |
Â
*/
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
7 |
Â
class Enterprise_SnsServer extends Enterprise_SnsBase {
|
8 |
Â
|
9 |
Â
/**
|
11 |
Â
*
|
12 |
Â
* @throws Exception
|
13 |
Â
*/
|
14 |
+
function process_message( $message ) {
|
15 |
Â
$this->_log( 'Received message' );
|
16 |
Â
|
17 |
Â
try {
|
18 |
+
$message = new \Aws\Sns\Message( $message );
|
19 |
+
$validator = new \Aws\Sns\MessageValidator();
|
20 |
Â
$error = '';
|
21 |
Â
if ( $validator->isValid( $message ) ) {
|
22 |
Â
$topic_arn = $this->_config->get_string( 'cluster.messagebus.sns.topic_arn' );
|
23 |
Â
|
24 |
+
if ( empty( $topic_arn ) || $topic_arn != $message['TopicArn'] )
|
25 |
+
throw new \Exception( 'Not my Topic. Request came from ' .
|
26 |
+
$message['TopicArn'] );
|
27 |
Â
|
28 |
+
if ( $message['Type'] == 'SubscriptionConfirmation' )
|
29 |
Â
$this->_subscription_confirmation( $message );
|
30 |
+
elseif ( $message['Type'] == 'Notification' )
|
31 |
+
$this->_notification( $message['Message'] );
|
32 |
Â
} else {
|
33 |
Â
$this->_log( 'Error processing message it was not valid.' );
|
34 |
Â
}
|
48 |
Â
$this->_log( 'Issuing confirm_subscription' );
|
49 |
Â
$topic_arn = $this->_config->get_string( 'cluster.messagebus.sns.topic_arn' );
|
50 |
Â
|
51 |
+
$response = $this->_get_api()->confirmSubscription( array(
|
52 |
+
'Token' => $message['Token'],
|
53 |
+
'TopicArn' => $topic_arn
|
54 |
+
) );
|
55 |
Â
$this->_log( 'Subscription confirmed: ' .
|
56 |
+
( $response['@metadata']['statusCode'] == 200 ? 'OK' : 'Error' ) );
|
57 |
Â
}
|
58 |
Â
|
59 |
Â
/**
|
Generic_Plugin.php
CHANGED
@@ -36,6 +36,10 @@ class Generic_Plugin {
|
|
36 |
Â
$this,
|
37 |
Â
'admin_bar_menu'
|
38 |
Â
), 150 );
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
39 |
Â
|
40 |
Â
if ( isset( $_REQUEST['w3tc_theme'] ) && isset( $_SERVER['HTTP_USER_AGENT'] ) &&
|
41 |
Â
stristr( $_SERVER['HTTP_USER_AGENT'], W3TC_POWERED_BY ) !== false ) {
|
@@ -233,6 +237,27 @@ class Generic_Plugin {
|
|
233 |
Â
}
|
234 |
Â
}
|
235 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
236 |
Â
/**
|
237 |
Â
* Admin bar menu
|
238 |
Â
*
|
@@ -259,12 +284,10 @@ class Generic_Plugin {
|
|
259 |
Â
|
260 |
Â
$menu_items['00010.generic'] = array(
|
261 |
Â
'id' => 'w3tc',
|
262 |
-
'title' =>
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
__( 'Performance', 'w3-total-cache' ) .
|
267 |
-
$menu_postfix,
|
268 |
Â
'href' => network_admin_url( 'admin.php?page=w3tc_dashboard' )
|
269 |
Â
);
|
270 |
Â
|
@@ -366,13 +389,13 @@ class Generic_Plugin {
|
|
366 |
Â
'?action=w3tc_monitoring_score&' . md5( $_SERVER['REQUEST_URI'] );
|
367 |
Â
|
368 |
Â
?>
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
Â
}
|
377 |
Â
|
378 |
Â
/**
|
@@ -501,25 +524,25 @@ class Generic_Plugin {
|
|
501 |
Â
if ( Util_Environment::is_preview_mode() )
|
502 |
Â
$buffer .= "\r\n<!-- W3 Total Cache used in preview mode -->";
|
503 |
Â
|
504 |
-
|
505 |
Â
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
|
510 |
-
|
511 |
Â
|
512 |
-
|
513 |
Â
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
Â
|
519 |
-
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
Â
}
|
524 |
Â
|
525 |
Â
$buffer = Util_Bus::do_ob_callbacks(
|
@@ -635,12 +658,12 @@ class Generic_Plugin {
|
|
635 |
Â
|
636 |
Â
function popup_script() {
|
637 |
Â
?>
|
638 |
-
|
639 |
-
|
640 |
-
|
641 |
-
|
642 |
-
|
643 |
-
|
644 |
Â
}
|
645 |
Â
|
646 |
Â
private function is_debugging() {
|
36 |
Â
$this,
|
37 |
Â
'admin_bar_menu'
|
38 |
Â
), 150 );
|
39 |
+
add_action( 'admin_bar_init', array(
|
40 |
+
$this,
|
41 |
+
'admin_bar_init'
|
42 |
+
) );
|
43 |
Â
|
44 |
Â
if ( isset( $_REQUEST['w3tc_theme'] ) && isset( $_SERVER['HTTP_USER_AGENT'] ) &&
|
45 |
Â
stristr( $_SERVER['HTTP_USER_AGENT'], W3TC_POWERED_BY ) !== false ) {
|
237 |
Â
}
|
238 |
Â
}
|
239 |
Â
|
240 |
+
public function admin_bar_init() {
|
241 |
+
$font_base = plugins_url( 'pub/fonts/w3tc', W3TC_FILE );
|
242 |
+
$css = "
|
243 |
+
@font-face {
|
244 |
+
font-family: 'w3tc';
|
245 |
+
src: url('$font_base.eot');
|
246 |
+
src: url('$font_base.eot?#iefix') format('embedded-opentype'),
|
247 |
+
url('$font_base.woff') format('woff'),
|
248 |
+
url('$font_base.ttf') format('truetype'),
|
249 |
+
url('$font_base.svg#w3tc') format('svg');
|
250 |
+
font-weight: normal;
|
251 |
+
font-style: normal;
|
252 |
+
}
|
253 |
+
.w3tc-icon:before{
|
254 |
+
content:'\\0041'; top: 2px;
|
255 |
+
font-family: 'w3tc';
|
256 |
+
}";
|
257 |
+
|
258 |
+
wp_add_inline_style( 'admin-bar', $css);
|
259 |
+
}
|
260 |
+
|
261 |
Â
/**
|
262 |
Â
* Admin bar menu
|
263 |
Â
*
|
284 |
Â
|
285 |
Â
$menu_items['00010.generic'] = array(
|
286 |
Â
'id' => 'w3tc',
|
287 |
+
'title' => sprintf(
|
288 |
+
'<span class="w3tc-icon ab-icon"></span><span class="ab-label">%s</span>' .
|
289 |
+
$menu_postfix,
|
290 |
+
__( 'Performance', 'w3-total-cache' ) ),
|
Â
|
|
Â
|
|
291 |
Â
'href' => network_admin_url( 'admin.php?page=w3tc_dashboard' )
|
292 |
Â
);
|
293 |
Â
|
389 |
Â
'?action=w3tc_monitoring_score&' . md5( $_SERVER['REQUEST_URI'] );
|
390 |
Â
|
391 |
Â
?>
|
392 |
+
<script type= "text/javascript">
|
393 |
+
var w3tc_monitoring_score = document.createElement('script');
|
394 |
+
w3tc_monitoring_score.type = 'text/javascript';
|
395 |
+
w3tc_monitoring_score.src = '<?php echo $url ?>';
|
396 |
+
document.getElementsByTagName('HEAD')[0].appendChild(w3tc_monitoring_score);
|
397 |
+
</script>
|
398 |
+
<?php
|
399 |
Â
}
|
400 |
Â
|
401 |
Â
/**
|
524 |
Â
if ( Util_Environment::is_preview_mode() )
|
525 |
Â
$buffer .= "\r\n<!-- W3 Total Cache used in preview mode -->";
|
526 |
Â
|
527 |
+
$strings = array();
|
528 |
Â
|
529 |
+
if ( $this->_config->get_string( 'common.support' ) == '' &&
|
530 |
+
!$this->_config->get_boolean( 'common.tweeted' ) ) {
|
531 |
+
$strings[] = 'Performance optimized by W3 Total Cache. Learn more: https://www.w3-edge.com/products/';
|
532 |
+
$strings[] = '';
|
533 |
+
}
|
534 |
Â
|
535 |
+
$strings = apply_filters( 'w3tc_footer_comment', $strings );
|
536 |
Â
|
537 |
+
if ( count( $strings ) ) {
|
538 |
+
$strings[] = '';
|
539 |
+
$strings[] = sprintf( "Served from: %s @ %s by W3 Total Cache",
|
540 |
+
Util_Content::escape_comment( $host ), $date );
|
541 |
Â
|
542 |
+
$buffer .= "\r\n<!--\r\n" .
|
543 |
+
Util_Content::escape_comment( implode( "\r\n", $strings ) ) .
|
544 |
+
"\r\n-->";
|
545 |
+
}
|
546 |
Â
}
|
547 |
Â
|
548 |
Â
$buffer = Util_Bus::do_ob_callbacks(
|
658 |
Â
|
659 |
Â
function popup_script() {
|
660 |
Â
?>
|
661 |
+
<script type="text/javascript">
|
662 |
+
function w3tc_popupadmin_bar(url) {
|
663 |
+
return window.open(url, '', 'width=800,height=600,status=no,toolbar=no,menubar=no,scrollbars=yes');
|
664 |
+
}
|
665 |
+
</script>
|
666 |
+
<?php
|
667 |
Â
}
|
668 |
Â
|
669 |
Â
private function is_debugging() {
|
Generic_Plugin_Admin.php
CHANGED
@@ -296,36 +296,10 @@ class Generic_Plugin_Admin {
|
|
296 |
Â
|
297 |
Â
?>
|
298 |
Â
<style type="text/css" media="screen">
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
#toplevel_page_w3tc_dashboard.wp-has-current-submenu .wp-menu-image {
|
304 |
-
background-position:0 0 !important;
|
305 |
-
}
|
306 |
-
#icon-edit.icon32-posts-casestudy {
|
307 |
-
background: url(<?php echo plugins_url( 'pub/img/w3tc-sprite.png', W3TC_FILE ) ?>) no-repeat;
|
308 |
-
}
|
309 |
-
/**
|
310 |
-
* HiDPI Displays
|
311 |
-
*/
|
312 |
-
@media print,
|
313 |
-
(-o-min-device-pixel-ratio: 5/4),
|
314 |
-
(-webkit-min-device-pixel-ratio: 1.25),
|
315 |
-
(min-resolution: 120dpi) {
|
316 |
-
|
317 |
-
#toplevel_page_w3tc_dashboard .wp-menu-image {
|
318 |
-
background-image: url(<?php echo plugins_url( 'pub/img/w3tc-sprite-retina.png', W3TC_FILE )?>) !important;
|
319 |
-
background-size: 30px 64px !important;
|
320 |
-
}
|
321 |
-
#toplevel_page_w3tc_dashboard:hover .wp-menu-image,
|
322 |
-
#toplevel_page_w3tc_dashboard.wp-has-current-submenu .wp-menu-image {
|
323 |
-
background-position:0 0 !important;
|
324 |
-
}
|
325 |
-
#icon-edit.icon32-posts-casestudy {
|
326 |
-
background-image: url(<?php echo plugins_url( 'pub/img/w3tc-sprite-retina.png', W3TC_FILE ) ?>) !important;
|
327 |
-
background-size: 30px 64px !important;
|
328 |
-
}
|
329 |
Â
}
|
330 |
Â
</style>
|
331 |
Â
<?php
|
296 |
Â
|
297 |
Â
?>
|
298 |
Â
<style type="text/css" media="screen">
|
299 |
+
.toplevel_page_w3tc_dashboard .wp-menu-image:before{
|
300 |
+
content:'\0041';
|
301 |
+
top: 2px;
|
302 |
+
font-family: 'w3tc';
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
303 |
Â
}
|
304 |
Â
</style>
|
305 |
Â
<?php
|
Generic_WidgetServices_View.php
CHANGED
@@ -10,26 +10,26 @@ if ( !defined( 'W3TC' ) )
|
|
10 |
Â
|
11 |
Â
<ul>
|
12 |
Â
<?php
|
13 |
-
for ( $n = 0; $n < count( $items ); $n++ ): ?>
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
Â
<?php endfor ?>
|
28 |
Â
</ul>
|
29 |
Â
<div id="buy-w3-service-area"></div>
|
30 |
Â
<p>
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
Â
</p>
|
35 |
Â
</form>
|
10 |
Â
|
11 |
Â
<ul>
|
12 |
Â
<?php
|
13 |
+
for ( $n = 0; $n < ( is_array( $items ) ? count( $items ) : 0 ); $n++ ): ?>
|
14 |
+
<li>
|
15 |
+
<div class="w3tc_generic_widgetservice_radio_outer">
|
16 |
+
<input id="service<?php echo $n ?>"
|
17 |
+
type="radio"
|
18 |
+
class="w3tc_generic_widgetservice_radio w3tc-ignore-change"
|
19 |
+
name="service_item"
|
20 |
+
value="<?php echo $n ?>"
|
21 |
+
/>
|
22 |
+
</div>
|
23 |
+
<label for="service<?php echo $n ?>" class="w3tc_generic_widgetservice_label">
|
24 |
+
<?php echo htmlspecialchars( $items[$n]['name'] ) ?>
|
25 |
+
</label>
|
26 |
+
</li>
|
27 |
Â
<?php endfor ?>
|
28 |
Â
</ul>
|
29 |
Â
<div id="buy-w3-service-area"></div>
|
30 |
Â
<p>
|
31 |
+
<input id="buy-w3-service" name="buy-w3-service" type="submit"
|
32 |
+
class="button button-primary button-large"
|
33 |
+
value="<?php _e( 'Buy now', 'w3-total-cache' ) ?>" />
|
34 |
Â
</p>
|
35 |
Â
</form>
|
PgCache_ContentGrabber.php
CHANGED
@@ -121,7 +121,7 @@ class PgCache_ContentGrabber {
|
|
121 |
Â
$this->_config = Dispatcher::config();
|
122 |
Â
$this->_debug = $this->_config->get_boolean( 'pgcache.debug' );
|
123 |
Â
|
124 |
-
$request_host = Util_Environment::
|
125 |
Â
$this->_request_host = $request_host;
|
126 |
Â
|
127 |
Â
$this->_request_uri = $_SERVER['REQUEST_URI'];
|
@@ -1318,6 +1318,8 @@ class PgCache_ContentGrabber {
|
|
1318 |
Â
if ( $request_url ) {
|
1319 |
Â
$parts = parse_url( $request_url );
|
1320 |
Â
$key = $parts['host'] .
|
Â
|
|
Â
|
|
1321 |
Â
( isset( $parts['path'] ) ? $parts['path'] : '' ) .
|
1322 |
Â
( isset( $parts['query'] ) ? '?' . $parts['query'] : '' );
|
1323 |
Â
} else {
|
121 |
Â
$this->_config = Dispatcher::config();
|
122 |
Â
$this->_debug = $this->_config->get_boolean( 'pgcache.debug' );
|
123 |
Â
|
124 |
+
$request_host = Util_Environment::host_port();
|
125 |
Â
$this->_request_host = $request_host;
|
126 |
Â
|
127 |
Â
$this->_request_uri = $_SERVER['REQUEST_URI'];
|
1318 |
Â
if ( $request_url ) {
|
1319 |
Â
$parts = parse_url( $request_url );
|
1320 |
Â
$key = $parts['host'] .
|
1321 |
+
|
1322 |
+
( isset( $parts['port'] ) ? ':' . $parts['port'] : '' ) .
|
1323 |
Â
( isset( $parts['path'] ) ? $parts['path'] : '' ) .
|
1324 |
Â
( isset( $parts['query'] ) ? '?' . $parts['query'] : '' );
|
1325 |
Â
} else {
|
PgCache_Flush.php
CHANGED
@@ -200,27 +200,31 @@ class PgCache_Flush extends PgCache_ContentGrabber {
|
|
200 |
Â
* @param unknown $cache
|
201 |
Â
* @param unknown $mobile_groups
|
202 |
Â
* @param unknown $referrer_groups
|
Â
|
|
203 |
Â
* @param unknown $encryptions
|
204 |
Â
* @param unknown $compressions
|
205 |
Â
*/
|
206 |
Â
function _flush_url( $url, $cache, $mobile_groups, $referrer_groups,
|
207 |
-
$encryptions, $compressions ) {
|
208 |
Â
foreach ( $mobile_groups as $mobile_group ) {
|
209 |
Â
foreach ( $referrer_groups as $referrer_group ) {
|
210 |
-
foreach ( $
|
211 |
-
foreach ( $
|
212 |
-
$
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
Â
|
|
Â
|
|
Â
|
|
224 |
Â
}
|
225 |
Â
}
|
226 |
Â
}
|
@@ -233,7 +237,7 @@ class PgCache_Flush extends PgCache_ContentGrabber {
|
|
233 |
Â
* @param unknown $url
|
234 |
Â
*/
|
235 |
Â
function flush_url( $url ) {
|
236 |
-
static $cache, $mobile_groups, $referrer_groups, $encryptions;
|
237 |
Â
static $compressions;
|
238 |
Â
|
239 |
Â
if ( !isset( $cache ) )
|
@@ -242,12 +246,14 @@ class PgCache_Flush extends PgCache_ContentGrabber {
|
|
242 |
Â
$mobile_groups = $this->_get_mobile_groups();
|
243 |
Â
if ( !isset( $referrer_groups ) )
|
244 |
Â
$referrer_groups = $this->_get_referrer_groups();
|
Â
|
|
Â
|
|
245 |
Â
if ( !isset( $encryptions ) )
|
246 |
Â
$encryptions = $this->_get_encryptions();
|
247 |
Â
if ( !isset( $compressions ) )
|
248 |
Â
$compressions = $this->_get_compressions();
|
249 |
Â
$this->_flush_url( $url, $cache, $mobile_groups, $referrer_groups,
|
250 |
-
$encryptions, $compressions );
|
251 |
Â
}
|
252 |
Â
|
253 |
Â
/**
|
@@ -290,12 +296,13 @@ class PgCache_Flush extends PgCache_ContentGrabber {
|
|
290 |
Â
$cache = $this->_get_cache();
|
291 |
Â
$mobile_groups = $this->_get_mobile_groups();
|
292 |
Â
$referrer_groups = $this->_get_referrer_groups();
|
Â
|
|
293 |
Â
$encryptions = $this->_get_encryptions();
|
294 |
Â
$compressions = $this->_get_compressions();
|
295 |
Â
|
296 |
Â
foreach ( $this->queued_urls as $url => $flag ) {
|
297 |
Â
$this->_flush_url( $url, $cache, $mobile_groups,
|
298 |
-
$referrer_groups, $encryptions, $compressions );
|
299 |
Â
}
|
300 |
Â
|
301 |
Â
// Purge sitemaps if a sitemap option has a regex
|
@@ -343,6 +350,19 @@ class PgCache_Flush extends PgCache_ContentGrabber {
|
|
343 |
Â
return $referrer_groups;
|
344 |
Â
}
|
345 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
346 |
Â
/**
|
347 |
Â
* Returns array of encryptions
|
348 |
Â
*
|
200 |
Â
* @param unknown $cache
|
201 |
Â
* @param unknown $mobile_groups
|
202 |
Â
* @param unknown $referrer_groups
|
203 |
+
* @param unknown $cookies
|
204 |
Â
* @param unknown $encryptions
|
205 |
Â
* @param unknown $compressions
|
206 |
Â
*/
|
207 |
Â
function _flush_url( $url, $cache, $mobile_groups, $referrer_groups,
|
208 |
+
$cookies, $encryptions, $compressions ) {
|
209 |
Â
foreach ( $mobile_groups as $mobile_group ) {
|
210 |
Â
foreach ( $referrer_groups as $referrer_group ) {
|
211 |
+
foreach ( $cookies as $cookie ) {
|
212 |
+
foreach ( $encryptions as $encryption ) {
|
213 |
+
foreach ( $compressions as $compression ) {
|
214 |
+
$page_keys = array();
|
215 |
+
$page_keys[] = $this->_get_page_key( array(
|
216 |
+
'useragent' => $mobile_group,
|
217 |
+
'referrer' => $referrer_group,
|
218 |
+
'cookie' => $cookie,
|
219 |
+
'encryption' => $encryption,
|
220 |
+
'compression' => $compression ),
|
221 |
+
$url );
|
222 |
+
$page_keys = apply_filters(
|
223 |
+
'w3tc_pagecache_flush_url_keys', $page_keys );
|
224 |
+
|
225 |
+
foreach ( $page_keys as $page_key )
|
226 |
+
$cache->delete( $page_key );
|
227 |
+
}
|
228 |
Â
}
|
229 |
Â
}
|
230 |
Â
}
|
237 |
Â
* @param unknown $url
|
238 |
Â
*/
|
239 |
Â
function flush_url( $url ) {
|
240 |
+
static $cache, $mobile_groups, $referrer_groups, $cookies, $encryptions;
|
241 |
Â
static $compressions;
|
242 |
Â
|
243 |
Â
if ( !isset( $cache ) )
|
246 |
Â
$mobile_groups = $this->_get_mobile_groups();
|
247 |
Â
if ( !isset( $referrer_groups ) )
|
248 |
Â
$referrer_groups = $this->_get_referrer_groups();
|
249 |
+
if ( !isset( $cookies ) )
|
250 |
+
$cookies = $this->_get_cookies();
|
251 |
Â
if ( !isset( $encryptions ) )
|
252 |
Â
$encryptions = $this->_get_encryptions();
|
253 |
Â
if ( !isset( $compressions ) )
|
254 |
Â
$compressions = $this->_get_compressions();
|
255 |
Â
$this->_flush_url( $url, $cache, $mobile_groups, $referrer_groups,
|
256 |
+
$cookies, $encryptions, $compressions );
|
257 |
Â
}
|
258 |
Â
|
259 |
Â
/**
|
296 |
Â
$cache = $this->_get_cache();
|
297 |
Â
$mobile_groups = $this->_get_mobile_groups();
|
298 |
Â
$referrer_groups = $this->_get_referrer_groups();
|
299 |
+
$cookies = $this->_get_cookies();
|
300 |
Â
$encryptions = $this->_get_encryptions();
|
301 |
Â
$compressions = $this->_get_compressions();
|
302 |
Â
|
303 |
Â
foreach ( $this->queued_urls as $url => $flag ) {
|
304 |
Â
$this->_flush_url( $url, $cache, $mobile_groups,
|
305 |
+
$referrer_groups, $cookies, $encryptions, $compressions );
|
306 |
Â
}
|
307 |
Â
|
308 |
Â
// Purge sitemaps if a sitemap option has a regex
|
350 |
Â
return $referrer_groups;
|
351 |
Â
}
|
352 |
Â
|
353 |
+
/**
|
354 |
+
* Returns array of cookies
|
355 |
+
*
|
356 |
+
* @return array
|
357 |
+
*/
|
358 |
+
function _get_cookies() {
|
359 |
+
$cookies = array( '' );
|
360 |
+
|
361 |
+
$cookies = array_merge( $cookies, array_keys( $this->_config->get_array( 'pgcache.cookiegroups.groups' ) ) );
|
362 |
+
|
363 |
+
return $cookies;
|
364 |
+
}
|
365 |
+
|
366 |
Â
/**
|
367 |
Â
* Returns array of encryptions
|
368 |
Â
*
|
Root_AdminMenu.php
CHANGED
@@ -117,7 +117,7 @@ class Root_AdminMenu {
|
|
117 |
Â
$pages = $this->generate_menu_array();
|
118 |
Â
|
119 |
Â
uasort( $pages, function($a, $b) {
|
120 |
-
|
121 |
Â
}
|
122 |
Â
);
|
123 |
Â
|
@@ -125,7 +125,7 @@ class Root_AdminMenu {
|
|
125 |
Â
__( 'Performance', 'w3-total-cache' ),
|
126 |
Â
apply_filters( 'w3tc_capability_menu_w3tc_dashboard',
|
127 |
Â
$base_capability ),
|
128 |
-
'w3tc_dashboard', '', '
|
129 |
Â
|
130 |
Â
$submenu_pages = array();
|
131 |
Â
$is_master = ( is_network_admin() || !Util_Environment::is_wpmu() );
|
@@ -167,8 +167,8 @@ class Root_AdminMenu {
|
|
167 |
Â
$this->_page = 'w3tc_dashboard';
|
168 |
Â
|
169 |
Â
/*
|
170 |
-
|
171 |
-
|
172 |
Â
if ( isset( $_REQUEST['w3tc_dbcluster_config'] ) ) {
|
173 |
Â
$options_dbcache = new DbCache_Page();
|
174 |
Â
$options_dbcache->dbcluster_config();
|
117 |
Â
$pages = $this->generate_menu_array();
|
118 |
Â
|
119 |
Â
uasort( $pages, function($a, $b) {
|
120 |
+
return ($a['order'] - $b['order']);
|
121 |
Â
}
|
122 |
Â
);
|
123 |
Â
|
125 |
Â
__( 'Performance', 'w3-total-cache' ),
|
126 |
Â
apply_filters( 'w3tc_capability_menu_w3tc_dashboard',
|
127 |
Â
$base_capability ),
|
128 |
+
'w3tc_dashboard', '', 'none' );
|
129 |
Â
|
130 |
Â
$submenu_pages = array();
|
131 |
Â
$is_master = ( is_network_admin() || !Util_Environment::is_wpmu() );
|
167 |
Â
$this->_page = 'w3tc_dashboard';
|
168 |
Â
|
169 |
Â
/*
|
170 |
+
* Hidden pages
|
171 |
+
*/
|
172 |
Â
if ( isset( $_REQUEST['w3tc_dbcluster_config'] ) ) {
|
173 |
Â
$options_dbcache = new DbCache_Page();
|
174 |
Â
$options_dbcache->dbcluster_config();
|
Support_Page.php
CHANGED
@@ -11,22 +11,6 @@ class Support_Page {
|
|
11 |
Â
$url = substr( $url, 7 );
|
12 |
Â
elseif ( substr( $url, 0, 8 ) == 'https://' )
|
13 |
Â
$url = substr( $url, 8 );
|
14 |
-
// aw3tc-options script is already queued so attach to it
|
15 |
-
// just to make vars printed (while it's not related by semantics)
|
16 |
-
wp_localize_script( 'w3tc-options',
|
17 |
-
'w3tc_support_postprocess', urlencode( urlencode(
|
18 |
-
Util_Ui::admin_url(
|
19 |
-
wp_nonce_url( 'admin.php', 'w3tc' ) . '&page=w3tc_support&done'
|
20 |
-
) ) ) );
|
21 |
-
wp_localize_script( 'w3tc-options',
|
22 |
-
'w3tc_support_home_url', $url );
|
23 |
-
wp_localize_script( 'w3tc-options',
|
24 |
-
'w3tc_support_email', get_bloginfo( 'admin_email' ) );
|
25 |
-
$u = wp_get_current_user();
|
26 |
-
wp_localize_script( 'w3tc-options',
|
27 |
-
'w3tc_support_first_name', $u->first_name );
|
28 |
-
wp_localize_script( 'w3tc-options',
|
29 |
-
'w3tc_support_last_name', $u->last_name );
|
30 |
Â
|
31 |
Â
// values from widget
|
32 |
Â
$w3tc_support_form_hash = 'm5pom8z0qy59rm';
|
@@ -49,12 +33,25 @@ class Support_Page {
|
|
49 |
Â
}
|
50 |
Â
}
|
51 |
Â
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
wp_localize_script( 'w3tc-options', '
|
57 |
-
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
58 |
Â
}
|
59 |
Â
/**
|
60 |
Â
* Support tab
|
11 |
Â
$url = substr( $url, 7 );
|
12 |
Â
elseif ( substr( $url, 0, 8 ) == 'https://' )
|
13 |
Â
$url = substr( $url, 8 );
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
14 |
Â
|
15 |
Â
// values from widget
|
16 |
Â
$w3tc_support_form_hash = 'm5pom8z0qy59rm';
|
33 |
Â
}
|
34 |
Â
}
|
35 |
Â
|
36 |
+
$u = wp_get_current_user();
|
37 |
+
|
38 |
+
// w3tc-options script is already queued so attach to it
|
39 |
+
// just to make vars printed (while it's not related by semantics)
|
40 |
+
wp_localize_script( 'w3tc-options', 'w3tc_support_data',
|
41 |
+
array(
|
42 |
+
'home_url' => $url,
|
43 |
+
'email' => get_bloginfo( 'admin_email' ),
|
44 |
+
'first_name' => $u->first_name,
|
45 |
+
'last_name' => $u->last_name,
|
46 |
+
'form_hash' => $w3tc_support_form_hash,
|
47 |
+
'field_name' => $w3tc_support_field_name,
|
48 |
+
'field_value' => $w3tc_support_field_value,
|
49 |
+
'postprocess' => urlencode( urlencode(
|
50 |
+
Util_Ui::admin_url(
|
51 |
+
wp_nonce_url( 'admin.php', 'w3tc' ) . '&page=w3tc_support&done'
|
52 |
+
) ) )
|
53 |
+
)
|
54 |
+
);
|
55 |
Â
}
|
56 |
Â
/**
|
57 |
Â
* Support tab
|
Support_Page_View_PageContent.php
CHANGED
@@ -7,25 +7,25 @@
|
|
7 |
Â
var s = d.createElement(t);
|
8 |
Â
var options = {
|
9 |
Â
'userName':'w3edge',
|
10 |
-
'formHash':
|
11 |
Â
'autoResize':true,
|
12 |
Â
'height':'1145',
|
13 |
Â
'async':true,
|
14 |
Â
'host':'wufoo.com',
|
15 |
Â
'header':'show',
|
16 |
Â
'defaultValues':
|
17 |
-
'field221=' + encodeURI(
|
18 |
-
'&field6=' + encodeURI(
|
19 |
-
'&field7=' + encodeURI(
|
20 |
-
'&field8=' + encodeURI(
|
21 |
-
'&field9=' + encodeURI(
|
22 |
Â
'ssl':true
|
23 |
Â
};
|
24 |
Â
|
25 |
-
if (
|
26 |
-
options.defaultValues += '&' +
|
27 |
-
encodeURI(
|
28 |
-
encodeURI(
|
29 |
Â
|
30 |
Â
|
31 |
Â
s.src = ('https:' == d.location.protocol ? 'https://' : 'http://') + 'www.wufoo.com/scripts/embed/form.js';
|
7 |
Â
var s = d.createElement(t);
|
8 |
Â
var options = {
|
9 |
Â
'userName':'w3edge',
|
10 |
+
'formHash': w3tc_support_data.form_hash,
|
11 |
Â
'autoResize':true,
|
12 |
Â
'height':'1145',
|
13 |
Â
'async':true,
|
14 |
Â
'host':'wufoo.com',
|
15 |
Â
'header':'show',
|
16 |
Â
'defaultValues':
|
17 |
+
'field221=' + encodeURI(w3tc_support_data.postprocess) +
|
18 |
+
'&field6=' + encodeURI(w3tc_support_data.first_name) +
|
19 |
+
'&field7=' + encodeURI(w3tc_support_data.last_name) +
|
20 |
+
'&field8=' + encodeURI(w3tc_support_data.home_url) +
|
21 |
+
'&field9=' + encodeURI(w3tc_support_data.email),
|
22 |
Â
'ssl':true
|
23 |
Â
};
|
24 |
Â
|
25 |
+
if (w3tc_support_data.field_name.length > 0)
|
26 |
+
options.defaultValues += '&' +
|
27 |
+
encodeURI(w3tc_support_data.field_name) + '=' +
|
28 |
+
encodeURI(w3tc_support_data.field_value);
|
29 |
Â
|
30 |
Â
|
31 |
Â
s.src = ('https:' == d.location.protocol ? 'https://' : 'http://') + 'www.wufoo.com/scripts/embed/form.js';
|
Util_Admin.php
CHANGED
@@ -101,11 +101,11 @@ class Util_Admin {
|
|
101 |
Â
}
|
102 |
Â
|
103 |
Â
/*
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
Â
static public function single_system_item( $a ) {
|
110 |
Â
if ( !is_array( $a ) || count( $a ) != 1 )
|
111 |
Â
return false;
|
@@ -594,8 +594,6 @@ class Util_Admin {
|
|
594 |
Â
/**
|
595 |
Â
* Update CloudFront CNAMEs
|
596 |
Â
*/
|
597 |
-
$update_cf_cnames = false;
|
598 |
-
|
599 |
Â
if ( $new_config->get_boolean( 'cdn.enabled' ) && in_array( $new_config->get_string( 'cdn.engine' ), array( 'cf', 'cf2' ) ) ) {
|
600 |
Â
if ( $new_config->get_string( 'cdn.engine' ) == 'cf' ) {
|
601 |
Â
$old_cnames = $old_config->get_array( 'cdn.cf.cname' );
|
@@ -604,10 +602,6 @@ class Util_Admin {
|
|
604 |
Â
$old_cnames = $old_config->get_array( 'cdn.cf2.cname' );
|
605 |
Â
$new_cnames = $new_config->get_array( 'cdn.cf2.cname' );
|
606 |
Â
}
|
607 |
-
|
608 |
-
if ( count( $old_cnames ) != count( $new_cnames ) || count( array_diff( $old_cnames, $new_cnames ) ) ) {
|
609 |
-
$update_cf_cnames = true;
|
610 |
-
}
|
611 |
Â
}
|
612 |
Â
|
613 |
Â
/**
|
@@ -634,14 +628,6 @@ class Util_Admin {
|
|
634 |
Â
Util_Admin::cdn_upload_browsercache( $current_config );
|
635 |
Â
}
|
636 |
Â
|
637 |
-
/**
|
638 |
-
* Update CloudFront CNAMEs
|
639 |
-
*/
|
640 |
-
if ( $update_cf_cnames ) {
|
641 |
-
$error = null;
|
642 |
-
$w3_plugin_cdn->update_cnames( $error );
|
643 |
-
}
|
644 |
-
|
645 |
Â
return true;
|
646 |
Â
}
|
647 |
Â
|
@@ -735,8 +721,8 @@ class Util_Admin {
|
|
735 |
Â
}
|
736 |
Â
|
737 |
Â
/*
|
738 |
-
|
739 |
-
|
740 |
Â
static public function get_current_page() {
|
741 |
Â
$page = Util_Request::get_string( 'page' );
|
742 |
Â
|
101 |
Â
}
|
102 |
Â
|
103 |
Â
/*
|
104 |
+
* Checks if contains single message item
|
105 |
+
*
|
106 |
+
* @param $a array
|
107 |
+
* @return boolean
|
108 |
+
*/
|
109 |
Â
static public function single_system_item( $a ) {
|
110 |
Â
if ( !is_array( $a ) || count( $a ) != 1 )
|
111 |
Â
return false;
|
594 |
Â
/**
|
595 |
Â
* Update CloudFront CNAMEs
|
596 |
Â
*/
|
Â
|
|
Â
|
|
597 |
Â
if ( $new_config->get_boolean( 'cdn.enabled' ) && in_array( $new_config->get_string( 'cdn.engine' ), array( 'cf', 'cf2' ) ) ) {
|
598 |
Â
if ( $new_config->get_string( 'cdn.engine' ) == 'cf' ) {
|
599 |
Â
$old_cnames = $old_config->get_array( 'cdn.cf.cname' );
|
602 |
Â
$old_cnames = $old_config->get_array( 'cdn.cf2.cname' );
|
603 |
Â
$new_cnames = $new_config->get_array( 'cdn.cf2.cname' );
|
604 |
Â
}
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
605 |
Â
}
|
606 |
Â
|
607 |
Â
/**
|
628 |
Â
Util_Admin::cdn_upload_browsercache( $current_config );
|
629 |
Â
}
|
630 |
Â
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
631 |
Â
return true;
|
632 |
Â
}
|
633 |
Â
|
721 |
Â
}
|
722 |
Â
|
723 |
Â
/*
|
724 |
+
* Returns current w3tc admin page
|
725 |
+
*/
|
726 |
Â
static public function get_current_page() {
|
727 |
Â
$page = Util_Request::get_string( 'page' );
|
728 |
Â
|
inc/mime/html.php
CHANGED
@@ -6,7 +6,6 @@
|
|
6 |
Â
return array(
|
7 |
Â
'html|htm' => 'text/html',
|
8 |
Â
'rtf|rtx' => 'text/richtext',
|
9 |
-
'svg' => 'image/svg+xml',
|
10 |
Â
'txt' => 'text/plain',
|
11 |
Â
'xsd' => 'text/xsd',
|
12 |
Â
'xsl' => 'text/xsl',
|
6 |
Â
return array(
|
7 |
Â
'html|htm' => 'text/html',
|
8 |
Â
'rtf|rtx' => 'text/richtext',
|
Â
|
|
9 |
Â
'txt' => 'text/plain',
|
10 |
Â
'xsd' => 'text/xsd',
|
11 |
Â
'xsl' => 'text/xsl',
|
inc/options/browsercache.php
CHANGED
@@ -202,7 +202,7 @@ Util_Ui::config_item( array(
|
|
202 |
Â
<option value="cache_validation"<?php selected( $value, 'cache_validation' ); ?>><?php _e( 'cache with validation ("public, must-revalidate, proxy-revalidate")', 'w3-total-cache' ); ?></option>
|
203 |
Â
<option value="cache_maxage"<?php selected( $value, 'cache_maxage' ); disabled( $is_nginx && $cssjs_expires ); ?>><?php _e( 'cache with max-age and validation ("max-age=EXPIRES_SECONDS, public, must-revalidate, proxy-revalidate")', 'w3-total-cache' ); ?></option>
|
204 |
Â
<option value="cache_noproxy"<?php selected( $value, 'cache_noproxy' ); ?>><?php _e( 'cache without proxy ("private, must-revalidate")', 'w3-total-cache' ); ?></option>
|
205 |
-
<option value="no_cache"<?php selected( $value, 'no_cache' ); ?>><?php _e( '
|
206 |
Â
</select>
|
207 |
Â
<?php if ( $is_nginx && $cssjs_expires ) : ?>
|
208 |
Â
<br /><span class="description"><?php _e( 'The Expires header already sets the max-age.', 'w3-total-cache' ); ?></span>
|
202 |
Â
<option value="cache_validation"<?php selected( $value, 'cache_validation' ); ?>><?php _e( 'cache with validation ("public, must-revalidate, proxy-revalidate")', 'w3-total-cache' ); ?></option>
|
203 |
Â
<option value="cache_maxage"<?php selected( $value, 'cache_maxage' ); disabled( $is_nginx && $cssjs_expires ); ?>><?php _e( 'cache with max-age and validation ("max-age=EXPIRES_SECONDS, public, must-revalidate, proxy-revalidate")', 'w3-total-cache' ); ?></option>
|
204 |
Â
<option value="cache_noproxy"<?php selected( $value, 'cache_noproxy' ); ?>><?php _e( 'cache without proxy ("private, must-revalidate")', 'w3-total-cache' ); ?></option>
|
205 |
+
<option value="no_cache"<?php selected( $value, 'no_cache' ); ?>><?php _e( 'don\'t cache ("max-age=0, private, no-store, no-cache, must-revalidate")', 'w3-total-cache' ); ?></option>
|
206 |
Â
</select>
|
207 |
Â
<?php if ( $is_nginx && $cssjs_expires ) : ?>
|
208 |
Â
<br /><span class="description"><?php _e( 'The Expires header already sets the max-age.', 'w3-total-cache' ); ?></span>
|
inc/options/general.php
CHANGED
@@ -13,39 +13,39 @@ echo sprintf( 'The plugin is currently %1$s If an option is disabled it means th
|
|
13 |
Â
?>
|
14 |
Â
</p>
|
15 |
Â
<form id="w3tc_form" action="admin.php?page=<?php echo $this->_page; ?>" method="post">
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
Â
Util_Ui::postbox_header( __( 'Page Cache', 'w3-total-cache' ), '', 'page_cache' );
|
40 |
Â
Util_Ui::config_overloading_button( array(
|
41 |
Â
'key' => 'pgcache.configuration_overloaded'
|
42 |
Â
) );
|
43 |
Â
?>
|
44 |
Â
|
45 |
-
|
46 |
Â
|
47 |
-
|
48 |
-
|
49 |
Â
Util_Ui::config_item( array(
|
50 |
Â
'key' => 'pgcache.enabled',
|
51 |
Â
'control' => 'checkbox',
|
@@ -109,27 +109,27 @@ Util_Ui::config_item( array(
|
|
109 |
Â
)
|
110 |
Â
) );
|
111 |
Â
?>
|
112 |
-
|
113 |
Â
|
114 |
-
|
115 |
Â
Util_Ui::button_config_save( 'general_pagecache',
|
116 |
Â
'<input type="submit" name="w3tc_flush_pgcache" value="' .
|
117 |
Â
__( 'Empty cache', 'w3-total-cache' ) . '"' .
|
118 |
Â
( $pgcache_enabled ? '' : ' disabled="disabled" ' ) .
|
119 |
Â
' class="button" />' );
|
120 |
Â
?>
|
121 |
-
|
122 |
Â
|
123 |
-
|
124 |
Â
Util_Ui::postbox_header( __( 'Minify', 'w3-total-cache' ), '', 'minify' );
|
125 |
Â
Util_Ui::config_overloading_button( array(
|
126 |
Â
'key' => 'minify.configuration_overloaded'
|
127 |
Â
) );
|
128 |
Â
?>
|
129 |
-
|
130 |
Â
|
131 |
-
|
132 |
-
|
133 |
Â
Util_Ui::config_item( array(
|
134 |
Â
'key' => 'minify.enabled',
|
135 |
Â
'control' => 'checkbox',
|
@@ -182,31 +182,31 @@ Util_Ui::config_item( array(
|
|
182 |
Â
)
|
183 |
Â
) );
|
184 |
Â
?>
|
185 |
-
|
186 |
Â
|
187 |
-
|
188 |
Â
Util_Ui::button_config_save( 'general_minify',
|
189 |
Â
'<input type="submit" name="w3tc_flush_minify" value="' .
|
190 |
Â
__( 'Empty cache', 'w3-total-cache' ) . '" ' .
|
191 |
Â
( $minify_enabled ? '' : ' disabled="disabled" ' ) .
|
192 |
Â
' class="button" />' );
|
193 |
Â
?>
|
194 |
-
|
195 |
Â
|
196 |
Â
|
197 |
-
|
198 |
Â
|
199 |
Â
do_action( 'w3tc_settings_general_boxarea_system_opcache' ) ?>
|
200 |
-
|
201 |
Â
Util_Ui::postbox_header( __( 'Database Cache', 'w3-total-cache' ), '', 'database_cache' );
|
202 |
Â
Util_Ui::config_overloading_button( array(
|
203 |
Â
'key' => 'dbcache.configuration_overloaded'
|
204 |
Â
) );
|
205 |
Â
?>
|
206 |
-
|
207 |
Â
|
208 |
-
|
209 |
-
|
210 |
Â
Util_Ui::config_item( array(
|
211 |
Â
'key' => 'dbcache.enabled',
|
212 |
Â
'control' => 'checkbox',
|
@@ -218,30 +218,30 @@ Util_Ui::config_item_engine( array(
|
|
218 |
Â
) );
|
219 |
Â
?>
|
220 |
Â
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
Â
|
226 |
-
|
227 |
Â
Util_Ui::button_config_save( 'general_dbcache',
|
228 |
Â
'<input type="submit" name="w3tc_flush_dbcache" value="' .
|
229 |
Â
__( 'Empty cache', 'w3-total-cache' ) . '" ' .
|
230 |
Â
( $dbcache_enabled ? '' : ' disabled="disabled" ' ) .
|
231 |
Â
' class="button" />' );
|
232 |
Â
?>
|
233 |
-
|
234 |
Â
|
235 |
-
|
236 |
Â
Util_Ui::postbox_header( 'Object Cache', '', 'object_cache' );
|
237 |
Â
Util_Ui::config_overloading_button( array(
|
238 |
Â
'key' => 'objectcache.configuration_overloaded'
|
239 |
Â
) );
|
240 |
Â
?>
|
241 |
-
|
242 |
Â
|
243 |
-
|
244 |
-
|
245 |
Â
Util_Ui::config_item( array(
|
246 |
Â
'key' => 'objectcache.enabled',
|
247 |
Â
'control' => 'checkbox',
|
@@ -252,27 +252,27 @@ Util_Ui::config_item_engine( array(
|
|
252 |
Â
'key' => 'objectcache.engine'
|
253 |
Â
) );
|
254 |
Â
?>
|
255 |
-
|
256 |
Â
|
257 |
-
|
258 |
Â
Util_Ui::button_config_save( 'general_objectcache',
|
259 |
Â
'<input type="submit" name="w3tc_flush_objectcache" value="' .
|
260 |
Â
__( 'Empty cache', 'w3-total-cache' ) . '" ' .
|
261 |
Â
( $objectcache_enabled ? '' : ' disabled="disabled" ' ) .
|
262 |
Â
' class="button" />' );
|
263 |
Â
?>
|
264 |
-
|
265 |
Â
|
266 |
-
|
267 |
Â
Util_Ui::postbox_header( __( 'Browser Cache', 'w3-total-cache' ), '', 'browser_cache' );
|
268 |
Â
Util_Ui::config_overloading_button( array(
|
269 |
Â
'key' => 'browsercache.configuration_overloaded'
|
270 |
Â
) );
|
271 |
Â
?>
|
272 |
-
|
273 |
Â
|
274 |
-
|
275 |
-
|
276 |
Â
Util_Ui::config_item( array(
|
277 |
Â
'key' => 'browsercache.enabled',
|
278 |
Â
'control' => 'checkbox',
|
@@ -280,138 +280,138 @@ Util_Ui::config_item( array(
|
|
280 |
Â
'description' => __( 'Enable <acronym title="Hypertext Transfer Protocol">HTTP</acronym> compression and add headers to reduce server load and decrease file load time.', 'w3-total-cache' )
|
281 |
Â
) );
|
282 |
Â
?>
|
283 |
-
|
284 |
Â
|
285 |
-
|
286 |
-
|
287 |
Â
|
288 |
-
|
289 |
Â
|
290 |
-
|
291 |
Â
Util_Ui::postbox_header( __( 'Reverse Proxy', 'w3-total-cache' ), '', 'reverse_proxy' );
|
292 |
Â
Util_Ui::config_overloading_button( array(
|
293 |
Â
'key' => 'varnish.configuration_overloaded'
|
294 |
Â
) );
|
295 |
Â
?>
|
296 |
-
|
297 |
-
|
298 |
Â
echo sprintf(
|
299 |
Â
w3tc_er( 'reverseproxy.general.header', 'A reverse proxy adds scale to an server by handling requests before WordPress does. Purge settings are set on the <a href="%s">Page Cache settings</a> page and <a href="%s">Browser Cache settings</a> are set on the browser cache settings page.' ),
|
300 |
Â
self_admin_url( 'admin.php?page=w3tc_pgcache' ),
|
301 |
Â
self_admin_url( 'admin.php?page=w3tc_browsercache' ) );
|
302 |
Â
?>
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
Â
Util_Ui::button_config_save( 'general_varnish',
|
322 |
Â
'<input type="submit" name="w3tc_flush_varnish" value="' .
|
323 |
Â
__( 'Purge cache', 'w3-total-cache' ) . '"' .
|
324 |
Â
( $varnish_enabled ? '' : ' disabled="disabled" ' ) .
|
325 |
Â
' class="button" />' );
|
326 |
Â
?>
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
Â
foreach ( $custom_areas as $area )
|
389 |
Â
do_action( "w3tc_settings_general_boxarea_{$area['id']}" );
|
390 |
Â
?>
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
Â
Util_Ui::config_item( array(
|
416 |
Â
'key' => 'widget.pagespeed.enabled',
|
417 |
Â
'control' => 'checkbox',
|
@@ -420,21 +420,21 @@ Util_Ui::config_item( array(
|
|
420 |
Â
'style' => '2'
|
421 |
Â
) );
|
422 |
Â
?>
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
Â
Util_Ui::config_item( array(
|
439 |
Â
'key' => 'widget.pagespeed.show_in_admin_bar',
|
440 |
Â
'control' => 'checkbox',
|
@@ -443,45 +443,45 @@ Util_Ui::config_item( array(
|
|
443 |
Â
) );
|
444 |
Â
?>
|
445 |
Â
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
|
450 |
-
|
451 |
-
|
452 |
-
|
453 |
-
|
454 |
-
|
455 |
-
|
456 |
-
|
457 |
-
|
458 |
-
|
459 |
-
|
460 |
-
|
461 |
-
|
462 |
-
|
463 |
-
|
464 |
-
|
465 |
-
|
466 |
-
|
467 |
-
|
468 |
-
|
469 |
-
|
470 |
-
|
471 |
-
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
-
|
476 |
-
|
477 |
-
|
478 |
-
|
479 |
-
|
480 |
-
|
481 |
-
|
482 |
-
|
483 |
-
|
484 |
-
|
485 |
Â
Util_Ui::config_item( array(
|
486 |
Â
'key' => 'common.track_usage',
|
487 |
Â
'control' => 'checkbox',
|
@@ -490,71 +490,71 @@ Util_Ui::config_item( array(
|
|
490 |
Â
) );
|
491 |
Â
?>
|
492 |
Â
|
493 |
-
|
494 |
-
|
495 |
-
|
496 |
-
|
497 |
-
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
|
510 |
-
|
511 |
-
|
512 |
-
|
513 |
-
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
|
519 |
-
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
|
524 |
-
|
525 |
-
|
526 |
-
|
527 |
Â
</form>
|
528 |
Â
|
529 |
Â
<form action="admin.php?page=<?php echo $this->_page; ?>" method="post" enctype="multipart/form-data">
|
530 |
-
|
531 |
-
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
|
543 |
-
|
544 |
-
|
545 |
-
|
546 |
-
|
547 |
-
|
548 |
-
|
549 |
-
|
550 |
-
|
551 |
-
|
552 |
-
|
553 |
-
|
554 |
-
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
Â
</form>
|
560 |
Â
<?php include W3TC_INC_DIR . '/options/common/footer.php'; ?>
|
13 |
Â
?>
|
14 |
Â
</p>
|
15 |
Â
<form id="w3tc_form" action="admin.php?page=<?php echo $this->_page; ?>" method="post">
|
16 |
+
<div class="metabox-holder">
|
17 |
+
<?php Util_Ui::postbox_header( __( 'General', 'w3-total-cache' ), '' ); ?>
|
18 |
+
<table class="form-table">
|
19 |
+
<tr>
|
20 |
+
<th>Preview mode:</th>
|
21 |
+
<td>
|
22 |
+
<?php echo Util_Ui::nonce_field( 'w3tc' ); ?>
|
23 |
+
<?php if ( $this->_config->is_preview() ): ?>
|
24 |
+
<input type="submit" name="w3tc_config_preview_disable" class="button-primary" value="<?php _e( 'Disable', 'w3-total-cache' ); ?>" />
|
25 |
+
<?php echo Util_Ui::button_link( __( 'Deploy', 'w3-total-cache' ), wp_nonce_url( sprintf( 'admin.php?page=%s&w3tc_config_preview_deploy', $this->_page ), 'w3tc' ) ); ?>
|
26 |
+
<br /><span class="description"> <?php printf( __( 'To preview any changed settings (without deploying): %s', 'w3-total-cache' ), Util_Ui::preview_link() ) ?> </span>
|
27 |
+
<?php else: ?>
|
28 |
+
<input type="submit" name="w3tc_config_preview_enable" class="button-primary" value="<?php _e( 'Enable', 'w3-total-cache' ); ?>" />
|
29 |
+
<?php endif; ?>
|
30 |
+
<br /><span class="description"><?php _e( 'Use preview mode to test configuration scenarios prior to releasing them (deploy) on the actual site. Preview mode remains active even after deploying settings until the feature is disabled.', 'w3-total-cache' ); ?></span>
|
31 |
+
</td>
|
32 |
+
</tr>
|
33 |
+
</table>
|
34 |
+
|
35 |
+
<?php Util_Ui::button_config_save( 'general_general' ); ?>
|
36 |
+
<?php Util_Ui::postbox_footer(); ?>
|
37 |
+
|
38 |
+
<?php
|
39 |
Â
Util_Ui::postbox_header( __( 'Page Cache', 'w3-total-cache' ), '', 'page_cache' );
|
40 |
Â
Util_Ui::config_overloading_button( array(
|
41 |
Â
'key' => 'pgcache.configuration_overloaded'
|
42 |
Â
) );
|
43 |
Â
?>
|
44 |
Â
|
45 |
+
<p><?php _e( 'Enable page caching to decrease the response time of the site.', 'w3-total-cache' ); ?></p>
|
46 |
Â
|
47 |
+
<table class="form-table">
|
48 |
+
<?php
|
49 |
Â
Util_Ui::config_item( array(
|
50 |
Â
'key' => 'pgcache.enabled',
|
51 |
Â
'control' => 'checkbox',
|
109 |
Â
)
|
110 |
Â
) );
|
111 |
Â
?>
|
112 |
+
</table>
|
113 |
Â
|
114 |
+
<?php
|
115 |
Â
Util_Ui::button_config_save( 'general_pagecache',
|
116 |
Â
'<input type="submit" name="w3tc_flush_pgcache" value="' .
|
117 |
Â
__( 'Empty cache', 'w3-total-cache' ) . '"' .
|
118 |
Â
( $pgcache_enabled ? '' : ' disabled="disabled" ' ) .
|
119 |
Â
' class="button" />' );
|
120 |
Â
?>
|
121 |
+
<?php Util_Ui::postbox_footer(); ?>
|
122 |
Â
|
123 |
+
<?php
|
124 |
Â
Util_Ui::postbox_header( __( 'Minify', 'w3-total-cache' ), '', 'minify' );
|
125 |
Â
Util_Ui::config_overloading_button( array(
|
126 |
Â
'key' => 'minify.configuration_overloaded'
|
127 |
Â
) );
|
128 |
Â
?>
|
129 |
+
<p><?php w3tc_e( 'minify.general.header', 'Reduce load time by decreasing the size and number of <acronym title="Cascading Style Sheet">CSS</acronym> and <acronym title="JavaScript">JS</acronym> files. Automatically remove unncessary data from <acronym title="Cascading Style Sheet">CSS</acronym>, <acronym title="JavaScript">JS</acronym>, feed, page and post <acronym title="Hypertext Markup Language">HTML</acronym>.' ) ?></p>
|
130 |
Â
|
131 |
+
<table class="form-table">
|
132 |
+
<?php
|
133 |
Â
Util_Ui::config_item( array(
|
134 |
Â
'key' => 'minify.enabled',
|
135 |
Â
'control' => 'checkbox',
|
182 |
Â
)
|
183 |
Â
) );
|
184 |
Â
?>
|
185 |
+
</table>
|
186 |
Â
|
187 |
+
<?php
|
188 |
Â
Util_Ui::button_config_save( 'general_minify',
|
189 |
Â
'<input type="submit" name="w3tc_flush_minify" value="' .
|
190 |
Â
__( 'Empty cache', 'w3-total-cache' ) . '" ' .
|
191 |
Â
( $minify_enabled ? '' : ' disabled="disabled" ' ) .
|
192 |
Â
' class="button" />' );
|
193 |
Â
?>
|
194 |
+
<?php Util_Ui::postbox_footer(); ?>
|
195 |
Â
|
196 |
Â
|
197 |
+
<?php
|
198 |
Â
|
199 |
Â
do_action( 'w3tc_settings_general_boxarea_system_opcache' ) ?>
|
200 |
+
<?php
|
201 |
Â
Util_Ui::postbox_header( __( 'Database Cache', 'w3-total-cache' ), '', 'database_cache' );
|
202 |
Â
Util_Ui::config_overloading_button( array(
|
203 |
Â
'key' => 'dbcache.configuration_overloaded'
|
204 |
Â
) );
|
205 |
Â
?>
|
206 |
+
<p><?php _e( 'Enable database caching to reduce post, page and feed creation time.', 'w3-total-cache' ); ?></p>
|
207 |
Â
|
208 |
+
<table class="form-table">
|
209 |
+
<?php
|
210 |
Â
Util_Ui::config_item( array(
|
211 |
Â
'key' => 'dbcache.enabled',
|
212 |
Â
'control' => 'checkbox',
|
218 |
Â
) );
|
219 |
Â
?>
|
220 |
Â
|
221 |
+
<?php if ( Util_Environment::is_w3tc_pro() && is_network_admin() ): ?>
|
222 |
+
<?php include W3TC_INC_OPTIONS_DIR . '/enterprise/dbcluster_general_section.php' ?>
|
223 |
+
<?php endif; ?>
|
224 |
+
</table>
|
225 |
Â
|
226 |
+
<?php
|
227 |
Â
Util_Ui::button_config_save( 'general_dbcache',
|
228 |
Â
'<input type="submit" name="w3tc_flush_dbcache" value="' .
|
229 |
Â
__( 'Empty cache', 'w3-total-cache' ) . '" ' .
|
230 |
Â
( $dbcache_enabled ? '' : ' disabled="disabled" ' ) .
|
231 |
Â
' class="button" />' );
|
232 |
Â
?>
|
233 |
+
<?php Util_Ui::postbox_footer(); ?>
|
234 |
Â
|
235 |
+
<?php
|
236 |
Â
Util_Ui::postbox_header( 'Object Cache', '', 'object_cache' );
|
237 |
Â
Util_Ui::config_overloading_button( array(
|
238 |
Â
'key' => 'objectcache.configuration_overloaded'
|
239 |
Â
) );
|
240 |
Â
?>
|
241 |
+
<p><?php _e( 'Enable object caching to further reduce execution time for common operations.', 'w3-total-cache' ); ?></p>
|
242 |
Â
|
243 |
+
<table class="form-table">
|
244 |
+
<?php
|
245 |
Â
Util_Ui::config_item( array(
|
246 |
Â
'key' => 'objectcache.enabled',
|
247 |
Â
'control' => 'checkbox',
|
252 |
Â
'key' => 'objectcache.engine'
|
253 |
Â
) );
|
254 |
Â
?>
|
255 |
+
</table>
|
256 |
Â
|
257 |
+
<?php
|
258 |
Â
Util_Ui::button_config_save( 'general_objectcache',
|
259 |
Â
'<input type="submit" name="w3tc_flush_objectcache" value="' .
|
260 |
Â
__( 'Empty cache', 'w3-total-cache' ) . '" ' .
|
261 |
Â
( $objectcache_enabled ? '' : ' disabled="disabled" ' ) .
|
262 |
Â
' class="button" />' );
|
263 |
Â
?>
|
264 |
+
<?php Util_Ui::postbox_footer(); ?>
|
265 |
Â
|
266 |
+
<?php
|
267 |
Â
Util_Ui::postbox_header( __( 'Browser Cache', 'w3-total-cache' ), '', 'browser_cache' );
|
268 |
Â
Util_Ui::config_overloading_button( array(
|
269 |
Â
'key' => 'browsercache.configuration_overloaded'
|
270 |
Â
) );
|
271 |
Â
?>
|
272 |
+
<p><?php _e( 'Reduce server load and decrease response time by using the cache available in site visitor\'s web browser.', 'w3-total-cache' ); ?></p>
|
273 |
Â
|
274 |
+
<table class="form-table">
|
275 |
+
<?php
|
276 |
Â
Util_Ui::config_item( array(
|
277 |
Â
'key' => 'browsercache.enabled',
|
278 |
Â
'control' => 'checkbox',
|
280 |
Â
'description' => __( 'Enable <acronym title="Hypertext Transfer Protocol">HTTP</acronym> compression and add headers to reduce server load and decrease file load time.', 'w3-total-cache' )
|
281 |
Â
) );
|
282 |
Â
?>
|
283 |
+
</table>
|
284 |
Â
|
285 |
+
<?php Util_Ui::button_config_save( 'general_browsercache' ); ?>
|
286 |
+
<?php Util_Ui::postbox_footer(); ?>
|
287 |
Â
|
288 |
+
<?php do_action( 'w3tc_settings_general_boxarea_cdn' ); ?>
|
289 |
Â
|
290 |
+
<?php
|
291 |
Â
Util_Ui::postbox_header( __( 'Reverse Proxy', 'w3-total-cache' ), '', 'reverse_proxy' );
|
292 |
Â
Util_Ui::config_overloading_button( array(
|
293 |
Â
'key' => 'varnish.configuration_overloaded'
|
294 |
Â
) );
|
295 |
Â
?>
|
296 |
+
<p>
|
297 |
+
<?php
|
298 |
Â
echo sprintf(
|
299 |
Â
w3tc_er( 'reverseproxy.general.header', 'A reverse proxy adds scale to an server by handling requests before WordPress does. Purge settings are set on the <a href="%s">Page Cache settings</a> page and <a href="%s">Browser Cache settings</a> are set on the browser cache settings page.' ),
|
300 |
Â
self_admin_url( 'admin.php?page=w3tc_pgcache' ),
|
301 |
Â
self_admin_url( 'admin.php?page=w3tc_browsercache' ) );
|
302 |
Â
?>
|
303 |
+
</p>
|
304 |
+
<table class="form-table">
|
305 |
+
<tr>
|
306 |
+
<th colspan="2">
|
307 |
+
<?php $this->checkbox( 'varnish.enabled' ); ?> <?php Util_Ui::e_config_label( 'varnish.enabled' ) ?></label><br />
|
308 |
+
</th>
|
309 |
+
</tr>
|
310 |
+
<tr>
|
311 |
+
<th><label for="pgcache_varnish_servers"><?php Util_Ui::e_config_label( 'varnish.servers' ) ?></label></th>
|
312 |
+
<td>
|
313 |
+
<textarea id="pgcache_varnish_servers" name="varnish__servers"
|
314 |
+
cols="40" rows="5" <?php Util_Ui::sealing_disabled( 'varnish.' ); ?>><?php echo esc_textarea( implode( "\r\n", $this->_config->get_array( 'varnish.servers' ) ) ); ?></textarea><br />
|
315 |
+
<span class="description"><?php _e( 'Specify the IP addresses of your varnish instances above. The <acronym title="Varnish Configuration Language">VCL</acronym>\'s <acronym title="Access Control List">ACL</acronym> must allow this request.', 'w3-total-cache' ); ?></span>
|
316 |
+
</td>
|
317 |
+
</tr>
|
318 |
+
</table>
|
319 |
+
|
320 |
+
<?php
|
321 |
Â
Util_Ui::button_config_save( 'general_varnish',
|
322 |
Â
'<input type="submit" name="w3tc_flush_varnish" value="' .
|
323 |
Â
__( 'Purge cache', 'w3-total-cache' ) . '"' .
|
324 |
Â
( $varnish_enabled ? '' : ' disabled="disabled" ' ) .
|
325 |
Â
' class="button" />' );
|
326 |
Â
?>
|
327 |
+
<?php Util_Ui::postbox_footer(); ?>
|
328 |
+
|
329 |
+
<?php if ( $is_pro ): ?>
|
330 |
+
<?php Util_Ui::postbox_header( 'Message Bus', '', 'amazon_sns' ); ?>
|
331 |
+
<p>
|
332 |
+
Allows policy management to be shared between a dynamic pool of servers. For example, each server in a pool to use opcode caching (which is not a shared resource) and purging is then syncronized between any number of servers in real-time; each server therefore behaves identically even though resources are not shared.
|
333 |
+
</p>
|
334 |
+
<table class="form-table">
|
335 |
+
<tr>
|
336 |
+
<th colspan="2">
|
337 |
+
<input type="hidden" name="cluster__messagebus__enabled" value="0" />
|
338 |
+
<label><input class="enabled" type="checkbox" name="cluster__messagebus__enabled" value="1"<?php checked( $this->_config->get_boolean( 'cluster.messagebus.enabled' ), true ); ?> /> <?php Util_Ui::e_config_label( 'cluster.messagebus.enabled' ) ?></label><br />
|
339 |
+
</th>
|
340 |
+
</tr>
|
341 |
+
<tr>
|
342 |
+
<th><label for="cluster_messagebus_sns_region"><?php Util_Ui::e_config_label( 'cluster.messagebus.sns.region' ) ?></label></th>
|
343 |
+
<td>
|
344 |
+
<input id="cluster_messagebus_sns_region"
|
345 |
+
class="w3tc-ignore-change" type="text"
|
346 |
+
name="cluster__messagebus__sns__region"
|
347 |
+
value="<?php echo esc_attr( $this->_config->get_string( 'cluster.messagebus.sns.region' ) ); ?>" size="60" /><br />
|
348 |
+
<span class="description"><?php _e( 'Specify the Amazon <acronym title="Simple Notification Service">SNS</acronym> service endpoint hostname. If empty, then default "sns.us-east-1.amazonaws.com" will be used.', 'w3-total-cache' ); ?></span>
|
349 |
+
</td>
|
350 |
+
</tr>
|
351 |
+
<tr>
|
352 |
+
<th><label for="cluster_messagebus_sns_api_key"><?php Util_Ui::e_config_label( 'cluster.messagebus.sns.api_key' ) ?></label></th>
|
353 |
+
<td>
|
354 |
+
<input id="cluster_messagebus_sns_api_key"
|
355 |
+
class="w3tc-ignore-change" type="text"
|
356 |
+
name="cluster__messagebus__sns__api_key"
|
357 |
+
value="<?php echo esc_attr( $this->_config->get_string( 'cluster.messagebus.sns.api_key' ) ); ?>" size="60" /><br />
|
358 |
+
<span class="description"><?php _e( 'Specify the <acronym title="Application Programming Interface">API</acronym> Key.', 'w3-total-cache' ); ?></span>
|
359 |
+
</td>
|
360 |
+
</tr>
|
361 |
+
<tr>
|
362 |
+
<th><label for="cluster_messagebus_sns_api_secret"><?php Util_Ui::e_config_label( 'cluster.messagebus.sns.api_secret' ) ?></label></th>
|
363 |
+
<td>
|
364 |
+
<input id="cluster_messagebus_sns_api_secret"
|
365 |
+
class="w3tc-ignore-change" type="text"
|
366 |
+
name="cluster__messagebus__sns__api_secret"
|
367 |
+
value="<?php echo esc_attr( $this->_config->get_string( 'cluster.messagebus.sns.api_secret' ) ); ?>" size="60" /><br />
|
368 |
+
<span class="description"><?php _e( 'Specify the <acronym title="Application Programming Interface">API</acronym> secret.', 'w3-total-cache' ); ?></span>
|
369 |
+
</td>
|
370 |
+
</tr>
|
371 |
+
<tr>
|
372 |
+
<th><label for="cluster_messagebus_sns_topic_arn"><?php Util_Ui::e_config_label( 'cluster.messagebus.sns.topic_arn' ) ?></label></th>
|
373 |
+
<td>
|
374 |
+
<input id="cluster_messagebus_sns_topic_arn"
|
375 |
+
class="w3tc-ignore-change" type="text"
|
376 |
+
name="cluster__messagebus__sns__topic_arn"
|
377 |
+
value="<?php echo esc_attr( $this->_config->get_string( 'cluster.messagebus.sns.topic_arn' ) ); ?>" size="60" /><br />
|
378 |
+
<span class="description"><?php _e( 'Specify the <acronym title="Simple Notification Service">SNS</acronym> topic.', 'w3-total-cache' ); ?></span>
|
379 |
+
</td>
|
380 |
+
</tr>
|
381 |
+
</table>
|
382 |
+
|
383 |
+
<?php Util_Ui::button_config_save( 'general_dbcluster' ); ?>
|
384 |
+
<?php Util_Ui::postbox_footer(); ?>
|
385 |
+
<?php endif; ?>
|
386 |
+
|
387 |
+
<?php
|
388 |
Â
foreach ( $custom_areas as $area )
|
389 |
Â
do_action( "w3tc_settings_general_boxarea_{$area['id']}" );
|
390 |
Â
?>
|
391 |
+
<?php if ( $licensing_visible ): ?>
|
392 |
+
<?php Util_Ui::postbox_header( __( 'Licensing', 'w3-total-cache' ), '', 'licensing' ); ?>
|
393 |
+
<table class="form-table">
|
394 |
+
<tr>
|
395 |
+
<th>
|
396 |
+
<label for="plugin_license_key"><?php Util_Ui::e_config_label( 'plugin.license_key' ) ?></label>
|
397 |
+
</th>
|
398 |
+
<td>
|
399 |
+
<input id="plugin_license_key" name="plugin__license_key" type="text" value="<?php echo esc_attr( $this->_config->get_string( 'plugin.license_key' ) )?>" size="45"/>
|
400 |
+
<input id="plugin_license_key_verify" type="button" class="button" value="<?php _e( 'Verify license key', 'w3-total-cache' ) ?>"/>
|
401 |
+
<span class="w3tc_license_verification"></span>
|
402 |
+
<br />
|
403 |
+
<span class="description"><?php printf( __( 'Please enter the license key provided after %s.', 'w3-total-cache' ), '<a class="button-buy-plugin" href="#">' . __( 'upgrading', 'w3-total-cache' ) . '</a>' )?></span>
|
404 |
+
</td>
|
405 |
+
</tr>
|
406 |
+
|
407 |
+
</table>
|
408 |
+
<?php Util_Ui::button_config_save( 'general_licensing' ); ?>
|
409 |
+
<?php Util_Ui::postbox_footer(); ?>
|
410 |
+
<?php endif ?>
|
411 |
+
|
412 |
+
<?php Util_Ui::postbox_header( __( 'Miscellaneous', 'w3-total-cache' ), '', 'miscellaneous' ); ?>
|
413 |
+
<table class="form-table">
|
414 |
+
<?php
|
415 |
Â
Util_Ui::config_item( array(
|
416 |
Â
'key' => 'widget.pagespeed.enabled',
|
417 |
Â
'control' => 'checkbox',
|
420 |
Â
'style' => '2'
|
421 |
Â
) );
|
422 |
Â
?>
|
423 |
+
<tr>
|
424 |
+
<th><label for="widget_pagespeed_key"><?php Util_Ui::e_config_label( 'widget.pagespeed.key' ) ?></label></th>
|
425 |
+
<td>
|
426 |
+
<input id="widget_pagespeed_key" type="text" name="widget__pagespeed__key" value="<?php echo esc_attr( $this->_config->get_string( 'widget.pagespeed.key' ) ); ?>" <?php Util_Ui::sealing_disabled( 'common.' ) ?> size="60" /><br />
|
427 |
+
<span class="description"><?php _e( 'Learn more about obtaining a <a href="https://support.google.com/cloud/answer/6158862" target="_blank"><acronym title="Application Programming Interface">API</acronym> key here</a>.', 'w3-total-cache' ); ?></span>
|
428 |
+
</td>
|
429 |
+
</tr>
|
430 |
+
<tr>
|
431 |
+
<th><label for="widget_pagespeed_key"><?php Util_Ui::e_config_label( 'widget.pagespeed.key.restrict.referrer', 'general' ) ?></label></th>
|
432 |
+
<td>
|
433 |
+
<input id="widget_pagespeed_key_restrict_referrer" type="text" name="widget__pagespeed__key__restrict__referrer" value="<?php echo esc_attr( $this->_config->get_string( 'widget.pagespeed.key.restrict.referrer' ) ); ?>" size="60" /><br>
|
434 |
+
<span class="description">Although not required, to prevent unauthorized use and quota theft, you have the option to restrict your key using a designated HTTP referrer. If you decide to use it, you will need to set this referrer within the API Console's "Http Referrers (web sites)" key restriction area (under Credentials).</span>
|
435 |
+
</td>
|
436 |
+
</tr>
|
437 |
+
<?php
|
438 |
Â
Util_Ui::config_item( array(
|
439 |
Â
'key' => 'widget.pagespeed.show_in_admin_bar',
|
440 |
Â
'control' => 'checkbox',
|
443 |
Â
) );
|
444 |
Â
?>
|
445 |
Â
|
446 |
+
<?php if ( is_network_admin() ): ?>
|
447 |
+
<tr>
|
448 |
+
<th colspan="2">
|
449 |
+
<?php $this->checkbox( 'common.force_master' ) ?> <?php Util_Ui::e_config_label( 'common.force_master' ) ?></label>
|
450 |
+
<br /><span class="description"><?php _e( 'Only one configuration file for whole network will be created and used. Recommended if all sites have the same configuration.', 'w3-total-cache' ); ?></span>
|
451 |
+
</th>
|
452 |
+
</tr>
|
453 |
+
<?php endif; ?>
|
454 |
+
<?php if ( Util_Environment::is_nginx() ): ?>
|
455 |
+
<tr>
|
456 |
+
<th><?php Util_Ui::e_config_label( 'config.path' ) ?></th>
|
457 |
+
<td>
|
458 |
+
<input type="text" name="config__path" value="<?php echo esc_attr( $this->_config->get_string( 'config.path' ) ); ?>" size="80" <?php Util_Ui::sealing_disabled( 'common.' ) ?>/>
|
459 |
+
<br /><span class="description"><?php _e( 'If empty the default path will be used..', 'w3-total-cache' ); ?></span>
|
460 |
+
</td>
|
461 |
+
</tr>
|
462 |
+
<?php endif; ?>
|
463 |
+
<tr>
|
464 |
+
<th colspan="2">
|
465 |
+
<input type="hidden" name="config__check" value="0" <?php Util_Ui::sealing_disabled( 'common.' ) ?> />
|
466 |
+
<label><input type="checkbox" name="config__check" value="1"<?php checked( $this->_config->get_boolean( 'config.check' ), true ); Util_Ui::sealing_disabled( 'common.' ); ?> /> <?php Util_Ui::e_config_label( 'config.check' ) ?></label>
|
467 |
+
<br /><span class="description"><?php _e( 'Notify of server configuration errors, if this option is disabled, the server configuration for active settings can be found on the <a href="admin.php?page=w3tc_install">install</a> tab.', 'w3-total-cache' ); ?></span>
|
468 |
+
</th>
|
469 |
+
</tr>
|
470 |
+
<tr>
|
471 |
+
<th colspan="2">
|
472 |
+
<input type="hidden" name="file_locking" value="0"<?php Util_Ui::sealing_disabled( 'common.' ) ?> />
|
473 |
+
<label><input type="checkbox" name="file_locking" value="1"<?php checked( $file_locking, true ); Util_Ui::sealing_disabled( 'common.' ) ?> /> <?php _e( 'Enable file locking', 'w3-total-cache' ); ?></label>
|
474 |
+
<br /><span class="description"><?php _e( 'Not recommended for <acronym title="Network File System">NFS</acronym> systems.', 'w3-total-cache' ); ?></span>
|
475 |
+
</th>
|
476 |
+
</tr>
|
477 |
+
<tr>
|
478 |
+
<th colspan="2">
|
479 |
+
<input type="hidden" name="file_nfs" value="0" <?php Util_Ui::sealing_disabled( 'common.' ) ?> />
|
480 |
+
<label><input type="checkbox" name="file_nfs" value="1"<?php checked( $file_nfs, true ); Util_Ui::sealing_disabled( 'common.' ); ?> /> <?php _e( 'Optimize disk enhanced page and minify disk caching for <acronym title="Network File System">NFS</acronym>', 'w3-total-cache' ); ?></label>
|
481 |
+
<br /><span class="description"><?php _e( 'Try this option if your hosting environment uses a network based file system for a possible performance improvement.', 'w3-total-cache' ); ?></span>
|
482 |
+
</th>
|
483 |
+
</tr>
|
484 |
+
<?php
|
485 |
Â
Util_Ui::config_item( array(
|
486 |
Â
'key' => 'common.track_usage',
|
487 |
Â
'control' => 'checkbox',
|
490 |
Â
) );
|
491 |
Â
?>
|
492 |
Â
|
493 |
+
<?php do_action( 'w3tc_settings_general_boxarea_miscellaneous_content' ); ?>
|
494 |
+
</table>
|
495 |
+
|
496 |
+
<?php Util_Ui::button_config_save( 'general_misc' ); ?>
|
497 |
+
<?php Util_Ui::postbox_footer(); ?>
|
498 |
+
|
499 |
+
<?php Util_Ui::postbox_header( 'Debug', '', 'debug' ); ?>
|
500 |
+
<p><?php _e( 'Detailed information about each cache will be appended in (publicly available) <acronym title="Hypertext Markup Language">HTML</acronym> comments in the page\'s source code. Performance in this mode will not be optimal, use sparingly and disable when not in use.', 'w3-total-cache' ); ?></p>
|
501 |
+
|
502 |
+
<table class="form-table">
|
503 |
+
<tr>
|
504 |
+
<th><?php _e( 'Debug mode:', 'w3-total-cache' ); ?></th>
|
505 |
+
<td>
|
506 |
+
<?php $this->checkbox_debug( 'pgcache.debug' ) ?> <?php Util_Ui::e_config_label( 'pgcache.debug' ) ?></label><br />
|
507 |
+
<?php $this->checkbox_debug( 'minify.debug' ) ?> <?php Util_Ui::e_config_label( 'minify.debug' ) ?></label><br />
|
508 |
+
<?php $this->checkbox_debug( 'dbcache.debug' ) ?> <?php Util_Ui::e_config_label( 'dbcache.debug' ) ?></label><br />
|
509 |
+
<?php $this->checkbox_debug( 'objectcache.debug' ) ?> <?php Util_Ui::e_config_label( 'objectcache.debug' ) ?></label><br />
|
510 |
+
<?php if ( Util_Environment::is_w3tc_pro( $this->_config ) ): ?>
|
511 |
+
<?php $this->checkbox_debug( array( 'fragmentcache', 'debug' ) ) ?> <?php _e( 'Fragment Cache', 'w3-total-cache' ) ?></label><br />
|
512 |
+
<?php endif; ?>
|
513 |
+
<?php $this->checkbox_debug( 'cdn.debug' ) ?> <?php Util_Ui::e_config_label( 'cdn.debug' ) ?></label><br />
|
514 |
+
<?php $this->checkbox_debug( 'cdnfsd.debug' ) ?> <?php Util_Ui::e_config_label( 'cdnfsd.debug' ) ?></label><br />
|
515 |
+
<?php $this->checkbox_debug( 'varnish.debug' ) ?> <?php Util_Ui::e_config_label( 'varnish.debug' ) ?></label><br />
|
516 |
+
<?php if ( Util_Environment::is_w3tc_pro() ): ?>
|
517 |
+
<?php $this->checkbox_debug( 'cluster.messagebus.debug' ) ?> <?php Util_Ui::e_config_label( 'cluster.messagebus.debug' ) ?></label><br />
|
518 |
+
<?php endif; ?>
|
519 |
+
<span class="description"><?php _e( 'If selected, detailed caching information will appear at the end of each page in a <acronym title="Hypertext Markup Language">HTML</acronym> comment. View a page\'s source code to review.', 'w3-total-cache' ); ?></span>
|
520 |
+
</td>
|
521 |
+
</tr>
|
522 |
+
</table>
|
523 |
+
|
524 |
+
<?php Util_Ui::button_config_save( 'general_debug' ); ?>
|
525 |
+
<?php Util_Ui::postbox_footer(); ?>
|
526 |
+
</div>
|
527 |
Â
</form>
|
528 |
Â
|
529 |
Â
<form action="admin.php?page=<?php echo $this->_page; ?>" method="post" enctype="multipart/form-data">
|
530 |
+
<div class="metabox-holder">
|
531 |
+
<?php Util_Ui::postbox_header( __( 'Import / Export Settings', 'w3-total-cache' ), '', 'settings' ); ?>
|
532 |
+
<?php echo Util_Ui::nonce_field( 'w3tc' ); ?>
|
533 |
+
<table class="form-table">
|
534 |
+
<tr>
|
535 |
+
<th><?php _e( 'Import configuration:', 'w3-total-cache' ); ?></th>
|
536 |
+
<td>
|
537 |
+
<input type="file" name="config_file" />
|
538 |
+
<input type="submit" name="w3tc_config_import" class="w3tc-button-save button" value="<?php _e( 'Upload', 'w3-total-cache' ); ?>" />
|
539 |
+
<br /><span class="description"><?php _e( 'Upload and replace the active settings file.', 'w3-total-cache' ); ?></span>
|
540 |
+
</td>
|
541 |
+
</tr>
|
542 |
+
<tr>
|
543 |
+
<th><?php _e( 'Export configuration:', 'w3-total-cache' ); ?></th>
|
544 |
+
<td>
|
545 |
+
<input type="submit" name="w3tc_config_export" class="button" value="<?php _e( 'Download', 'w3-total-cache' ); ?>" />
|
546 |
+
<br /><span class="description"><?php _e( 'Download the active settings file.', 'w3-total-cache' ); ?></span>
|
547 |
+
</td>
|
548 |
+
</tr>
|
549 |
+
<tr>
|
550 |
+
<th><?php _e( 'Reset configuration:', 'w3-total-cache' ); ?></th>
|
551 |
+
<td>
|
552 |
+
<input type="submit" name="w3tc_config_reset" class="button" value="<?php _e( 'Restore Default Settings', 'w3-total-cache' ); ?>" />
|
553 |
+
<br /><span class="description"><?php _e( 'Revert all settings to the defaults. Any settings staged in preview mode will not be modified.', 'w3-total-cache' ); ?></span>
|
554 |
+
</td>
|
555 |
+
</tr>
|
556 |
+
</table>
|
557 |
+
<?php Util_Ui::postbox_footer(); ?>
|
558 |
+
</div>
|
559 |
Â
</form>
|
560 |
Â
<?php include W3TC_INC_DIR . '/options/common/footer.php'; ?>
|
lib/Aws/Aws/Api/AbstractModel.php
ADDED
@@ -0,0 +1,67 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Base class that is used by most API shapes
|
6 |
+
*/
|
7 |
+
abstract class AbstractModel implements \ArrayAccess
|
8 |
+
{
|
9 |
+
/** @var array */
|
10 |
+
protected $definition;
|
11 |
+
|
12 |
+
/** @var ShapeMap */
|
13 |
+
protected $shapeMap;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* @param array $definition Service description
|
17 |
+
* @param ShapeMap $shapeMap Shapemap used for creating shapes
|
18 |
+
*/
|
19 |
+
public function __construct(array $definition, ShapeMap $shapeMap)
|
20 |
+
{
|
21 |
+
$this->definition = $definition;
|
22 |
+
$this->shapeMap = $shapeMap;
|
23 |
+
}
|
24 |
+
|
25 |
+
public function toArray()
|
26 |
+
{
|
27 |
+
return $this->definition;
|
28 |
+
}
|
29 |
+
|
30 |
+
public function offsetGet($offset)
|
31 |
+
{
|
32 |
+
return isset($this->definition[$offset])
|
33 |
+
? $this->definition[$offset] : null;
|
34 |
+
}
|
35 |
+
|
36 |
+
public function offsetSet($offset, $value)
|
37 |
+
{
|
38 |
+
$this->definition[$offset] = $value;
|
39 |
+
}
|
40 |
+
|
41 |
+
public function offsetExists($offset)
|
42 |
+
{
|
43 |
+
return isset($this->definition[$offset]);
|
44 |
+
}
|
45 |
+
|
46 |
+
public function offsetUnset($offset)
|
47 |
+
{
|
48 |
+
unset($this->definition[$offset]);
|
49 |
+
}
|
50 |
+
|
51 |
+
protected function shapeAt($key)
|
52 |
+
{
|
53 |
+
if (!isset($this->definition[$key])) {
|
54 |
+
throw new \InvalidArgumentException('Expected shape definition at '
|
55 |
+
. $key);
|
56 |
+
}
|
57 |
+
|
58 |
+
return $this->shapeFor($this->definition[$key]);
|
59 |
+
}
|
60 |
+
|
61 |
+
protected function shapeFor(array $definition)
|
62 |
+
{
|
63 |
+
return isset($definition['shape'])
|
64 |
+
? $this->shapeMap->resolve($definition)
|
65 |
+
: Shape::create($definition, $this->shapeMap);
|
66 |
+
}
|
67 |
+
}
|
lib/Aws/Aws/Api/ApiProvider.php
ADDED
@@ -0,0 +1,244 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api;
|
3 |
+
|
4 |
+
use Aws\Exception\UnresolvedApiException;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* API providers.
|
8 |
+
*
|
9 |
+
* An API provider is a function that accepts a type, service, and version and
|
10 |
+
* returns an array of API data on success or NULL if no API data can be created
|
11 |
+
* for the provided arguments.
|
12 |
+
*
|
13 |
+
* You can wrap your calls to an API provider with the
|
14 |
+
* {@see ApiProvider::resolve} method to ensure that API data is created. If the
|
15 |
+
* API data is not created, then the resolve() method will throw a
|
16 |
+
* {@see Aws\Exception\UnresolvedApiException}.
|
17 |
+
*
|
18 |
+
* use Aws\Api\ApiProvider;
|
19 |
+
* $provider = ApiProvider::defaultProvider();
|
20 |
+
* // Returns an array or NULL.
|
21 |
+
* $data = $provider('api', 's3', '2006-03-01');
|
22 |
+
* // Returns an array or throws.
|
23 |
+
* $data = ApiProvider::resolve($provider, 'api', 'elasticfood', '2020-01-01');
|
24 |
+
*
|
25 |
+
* You can compose multiple providers into a single provider using
|
26 |
+
* {@see Aws\or_chain}. This method accepts providers as arguments and
|
27 |
+
* returns a new function that will invoke each provider until a non-null value
|
28 |
+
* is returned.
|
29 |
+
*
|
30 |
+
* $a = ApiProvider::filesystem(sys_get_temp_dir() . '/aws-beta-models');
|
31 |
+
* $b = ApiProvider::manifest();
|
32 |
+
*
|
33 |
+
* $c = \Aws\or_chain($a, $b);
|
34 |
+
* $data = $c('api', 'betaservice', '2015-08-08'); // $a handles this.
|
35 |
+
* $data = $c('api', 's3', '2006-03-01'); // $b handles this.
|
36 |
+
* $data = $c('api', 'invalid', '2014-12-15'); // Neither handles this.
|
37 |
+
*/
|
38 |
+
class ApiProvider
|
39 |
+
{
|
40 |
+
/** @var array A map of public API type names to their file suffix. */
|
41 |
+
private static $typeMap = [
|
42 |
+
'api' => 'api-2',
|
43 |
+
'paginator' => 'paginators-1',
|
44 |
+
'waiter' => 'waiters-2',
|
45 |
+
'docs' => 'docs-2',
|
46 |
+
];
|
47 |
+
|
48 |
+
/** @var array API manifest */
|
49 |
+
private $manifest;
|
50 |
+
|
51 |
+
/** @var string The directory containing service models. */
|
52 |
+
private $modelsDir;
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Resolves an API provider and ensures a non-null return value.
|
56 |
+
*
|
57 |
+
* @param callable $provider Provider function to invoke.
|
58 |
+
* @param string $type Type of data ('api', 'waiter', 'paginator').
|
59 |
+
* @param string $service Service name.
|
60 |
+
* @param string $version API version.
|
61 |
+
*
|
62 |
+
* @return array
|
63 |
+
* @throws UnresolvedApiException
|
64 |
+
*/
|
65 |
+
public static function resolve(callable $provider, $type, $service, $version)
|
66 |
+
{
|
67 |
+
// Execute the provider and return the result, if there is one.
|
68 |
+
$result = $provider($type, $service, $version);
|
69 |
+
if (is_array($result)) {
|
70 |
+
if (!isset($result['metadata']['serviceIdentifier'])) {
|
71 |
+
$result['metadata']['serviceIdentifier'] = $service;
|
72 |
+
}
|
73 |
+
return $result;
|
74 |
+
}
|
75 |
+
|
76 |
+
// Throw an exception with a message depending on the inputs.
|
77 |
+
if (!isset(self::$typeMap[$type])) {
|
78 |
+
$msg = "The type must be one of: " . implode(', ', self::$typeMap);
|
79 |
+
} elseif ($service) {
|
80 |
+
$msg = "The {$service} service does not have version: {$version}.";
|
81 |
+
} else {
|
82 |
+
$msg = "You must specify a service name to retrieve its API data.";
|
83 |
+
}
|
84 |
+
|
85 |
+
throw new UnresolvedApiException($msg);
|
86 |
+
}
|
87 |
+
|
88 |
+
/**
|
89 |
+
* Default SDK API provider.
|
90 |
+
*
|
91 |
+
* This provider loads pre-built manifest data from the `data` directory.
|
92 |
+
*
|
93 |
+
* @return self
|
94 |
+
*/
|
95 |
+
public static function defaultProvider()
|
96 |
+
{
|
97 |
+
return new self(__DIR__ . '/../data', \Aws\manifest());
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
* Loads API data after resolving the version to the latest, compatible,
|
102 |
+
* available version based on the provided manifest data.
|
103 |
+
*
|
104 |
+
* Manifest data is essentially an associative array of service names to
|
105 |
+
* associative arrays of API version aliases.
|
106 |
+
*
|
107 |
+
* [
|
108 |
+
* ...
|
109 |
+
* 'ec2' => [
|
110 |
+
* 'latest' => '2014-10-01',
|
111 |
+
* '2014-10-01' => '2014-10-01',
|
112 |
+
* '2014-09-01' => '2014-10-01',
|
113 |
+
* '2014-06-15' => '2014-10-01',
|
114 |
+
* ...
|
115 |
+
* ],
|
116 |
+
* 'ecs' => [...],
|
117 |
+
* 'elasticache' => [...],
|
118 |
+
* ...
|
119 |
+
* ]
|
120 |
+
*
|
121 |
+
* @param string $dir Directory containing service models.
|
122 |
+
* @param array $manifest The API version manifest data.
|
123 |
+
*
|
124 |
+
* @return self
|
125 |
+
*/
|
126 |
+
public static function manifest($dir, array $manifest)
|
127 |
+
{
|
128 |
+
return new self($dir, $manifest);
|
129 |
+
}
|
130 |
+
|
131 |
+
/**
|
132 |
+
* Loads API data from the specified directory.
|
133 |
+
*
|
134 |
+
* If "latest" is specified as the version, this provider must glob the
|
135 |
+
* directory to find which is the latest available version.
|
136 |
+
*
|
137 |
+
* @param string $dir Directory containing service models.
|
138 |
+
*
|
139 |
+
* @return self
|
140 |
+
* @throws \InvalidArgumentException if the provided `$dir` is invalid.
|
141 |
+
*/
|
142 |
+
public static function filesystem($dir)
|
143 |
+
{
|
144 |
+
return new self($dir);
|
145 |
+
}
|
146 |
+
|
147 |
+
/**
|
148 |
+
* Retrieves a list of valid versions for the specified service.
|
149 |
+
*
|
150 |
+
* @param string $service Service name
|
151 |
+
*
|
152 |
+
* @return array
|
153 |
+
*/
|
154 |
+
public function getVersions($service)
|
155 |
+
{
|
156 |
+
if (!isset($this->manifest)) {
|
157 |
+
$this->buildVersionsList($service);
|
158 |
+
}
|
159 |
+
|
160 |
+
if (!isset($this->manifest[$service]['versions'])) {
|
161 |
+
return [];
|
162 |
+
}
|
163 |
+
|
164 |
+
return array_values(array_unique($this->manifest[$service]['versions']));
|
165 |
+
}
|
166 |
+
|
167 |
+
/**
|
168 |
+
* Execute the the provider.
|
169 |
+
*
|
170 |
+
* @param string $type Type of data ('api', 'waiter', 'paginator').
|
171 |
+
* @param string $service Service name.
|
172 |
+
* @param string $version API version.
|
173 |
+
*
|
174 |
+
* @return array|null
|
175 |
+
*/
|
176 |
+
public function __invoke($type, $service, $version)
|
177 |
+
{
|
178 |
+
// Resolve the type or return null.
|
179 |
+
if (isset(self::$typeMap[$type])) {
|
180 |
+
$type = self::$typeMap[$type];
|
181 |
+
} else {
|
182 |
+
return null;
|
183 |
+
}
|
184 |
+
|
185 |
+
// Resolve the version or return null.
|
186 |
+
if (!isset($this->manifest)) {
|
187 |
+
$this->buildVersionsList($service);
|
188 |
+
}
|
189 |
+
|
190 |
+
if (!isset($this->manifest[$service]['versions'][$version])) {
|
191 |
+
return null;
|
192 |
+
}
|
193 |
+
|
194 |
+
$version = $this->manifest[$service]['versions'][$version];
|
195 |
+
$path = "{$this->modelsDir}/{$service}/{$version}/{$type}.json";
|
196 |
+
|
197 |
+
try {
|
198 |
+
return \Aws\load_compiled_json($path);
|
199 |
+
} catch (\InvalidArgumentException $e) {
|
200 |
+
return null;
|
201 |
+
}
|
202 |
+
}
|
203 |
+
|
204 |
+
/**
|
205 |
+
* @param string $modelsDir Directory containing service models.
|
206 |
+
* @param array $manifest The API version manifest data.
|
207 |
+
*/
|
208 |
+
private function __construct($modelsDir, array $manifest = null)
|
209 |
+
{
|
210 |
+
$this->manifest = $manifest;
|
211 |
+
$this->modelsDir = rtrim($modelsDir, '/');
|
212 |
+
if (!is_dir($this->modelsDir)) {
|
213 |
+
throw new \InvalidArgumentException(
|
214 |
+
"The specified models directory, {$modelsDir}, was not found."
|
215 |
+
);
|
216 |
+
}
|
217 |
+
}
|
218 |
+
|
219 |
+
/**
|
220 |
+
* Build the versions list for the specified service by globbing the dir.
|
221 |
+
*/
|
222 |
+
private function buildVersionsList($service)
|
223 |
+
{
|
224 |
+
$dir = "{$this->modelsDir}/{$service}/";
|
225 |
+
|
226 |
+
if (!is_dir($dir)) {
|
227 |
+
return;
|
228 |
+
}
|
229 |
+
|
230 |
+
// Get versions, remove . and .., and sort in descending order.
|
231 |
+
$results = array_diff(scandir($dir, SCANDIR_SORT_DESCENDING), ['..', '.']);
|
232 |
+
|
233 |
+
if (!$results) {
|
234 |
+
$this->manifest[$service] = ['versions' => []];
|
235 |
+
} else {
|
236 |
+
$this->manifest[$service] = [
|
237 |
+
'versions' => [
|
238 |
+
'latest' => $results[0]
|
239 |
+
]
|
240 |
+
];
|
241 |
+
$this->manifest[$service]['versions'] += array_combine($results, $results);
|
242 |
+
}
|
243 |
+
}
|
244 |
+
}
|
lib/Aws/Aws/Api/DateTimeResult.php
ADDED
@@ -0,0 +1,41 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* DateTime overrides that make DateTime work more seamlessly as a string,
|
6 |
+
* with JSON documents, and with JMESPath.
|
7 |
+
*/
|
8 |
+
class DateTimeResult extends \DateTime implements \JsonSerializable
|
9 |
+
{
|
10 |
+
/**
|
11 |
+
* Create a new DateTimeResult from a unix timestamp.
|
12 |
+
*
|
13 |
+
* @param $unixTimestamp
|
14 |
+
*
|
15 |
+
* @return DateTimeResult
|
16 |
+
*/
|
17 |
+
public static function fromEpoch($unixTimestamp)
|
18 |
+
{
|
19 |
+
return new self(gmdate('c', $unixTimestamp));
|
20 |
+
}
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Serialize the DateTimeResult as an ISO 8601 date string.
|
24 |
+
*
|
25 |
+
* @return string
|
26 |
+
*/
|
27 |
+
public function __toString()
|
28 |
+
{
|
29 |
+
return $this->format('c');
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Serialize the date as an ISO 8601 date when serializing as JSON.
|
34 |
+
*
|
35 |
+
* @return mixed|string
|
36 |
+
*/
|
37 |
+
public function jsonSerialize()
|
38 |
+
{
|
39 |
+
return (string) $this;
|
40 |
+
}
|
41 |
+
}
|
lib/Aws/Aws/Api/DocModel.php
ADDED
@@ -0,0 +1,128 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Encapsulates the documentation strings for a given service-version and
|
6 |
+
* provides methods for extracting the desired parts related to a service,
|
7 |
+
* operation, error, or shape (i.e., parameter).
|
8 |
+
*/
|
9 |
+
class DocModel
|
10 |
+
{
|
11 |
+
/** @var array */
|
12 |
+
private $docs;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* @param array $docs
|
16 |
+
*
|
17 |
+
* @throws \RuntimeException
|
18 |
+
*/
|
19 |
+
public function __construct(array $docs)
|
20 |
+
{
|
21 |
+
if (!extension_loaded('tidy')) {
|
22 |
+
throw new \RuntimeException('The "tidy" PHP extension is required.');
|
23 |
+
}
|
24 |
+
|
25 |
+
$this->docs = $docs;
|
26 |
+
}
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Convert the doc model to an array.
|
30 |
+
*
|
31 |
+
* @return array
|
32 |
+
*/
|
33 |
+
public function toArray()
|
34 |
+
{
|
35 |
+
return $this->docs;
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Retrieves documentation about the service.
|
40 |
+
*
|
41 |
+
* @return null|string
|
42 |
+
*/
|
43 |
+
public function getServiceDocs()
|
44 |
+
{
|
45 |
+
return isset($this->docs['service']) ? $this->docs['service'] : null;
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Retrieves documentation about an operation.
|
50 |
+
*
|
51 |
+
* @param string $operation Name of the operation
|
52 |
+
*
|
53 |
+
* @return null|string
|
54 |
+
*/
|
55 |
+
public function getOperationDocs($operation)
|
56 |
+
{
|
57 |
+
return isset($this->docs['operations'][$operation])
|
58 |
+
? $this->docs['operations'][$operation]
|
59 |
+
: null;
|
60 |
+
}
|
61 |
+
|
62 |
+
/**
|
63 |
+
* Retrieves documentation about an error.
|
64 |
+
*
|
65 |
+
* @param string $error Name of the error
|
66 |
+
*
|
67 |
+
* @return null|string
|
68 |
+
*/
|
69 |
+
public function getErrorDocs($error)
|
70 |
+
{
|
71 |
+
return isset($this->docs['shapes'][$error]['base'])
|
72 |
+
? $this->docs['shapes'][$error]['base']
|
73 |
+
: null;
|
74 |
+
}
|
75 |
+
|
76 |
+
/**
|
77 |
+
* Retrieves documentation about a shape, specific to the context.
|
78 |
+
*
|
79 |
+
* @param string $shapeName Name of the shape.
|
80 |
+
* @param string $parentName Name of the parent/context shape.
|
81 |
+
* @param string $ref Name used by the context to reference the shape.
|
82 |
+
*
|
83 |
+
* @return null|string
|
84 |
+
*/
|
85 |
+
public function getShapeDocs($shapeName, $parentName, $ref)
|
86 |
+
{
|
87 |
+
if (!isset($this->docs['shapes'][$shapeName])) {
|
88 |
+
return '';
|
89 |
+
}
|
90 |
+
|
91 |
+
$result = '';
|
92 |
+
$d = $this->docs['shapes'][$shapeName];
|
93 |
+
if (isset($d['refs']["{$parentName}\$${ref}"])) {
|
94 |
+
$result = $d['refs']["{$parentName}\$${ref}"];
|
95 |
+
} elseif (isset($d['base'])) {
|
96 |
+
$result = $d['base'];
|
97 |
+
}
|
98 |
+
|
99 |
+
if (isset($d['append'])) {
|
100 |
+
$result .= $d['append'];
|
101 |
+
}
|
102 |
+
|
103 |
+
return $this->clean($result);
|
104 |
+
}
|
105 |
+
|
106 |
+
private function clean($content)
|
107 |
+
{
|
108 |
+
if (!$content) {
|
109 |
+
return '';
|
110 |
+
}
|
111 |
+
|
112 |
+
$tidy = new \Tidy();
|
113 |
+
$tidy->parseString($content, [
|
114 |
+
'indent' => true,
|
115 |
+
'doctype' => 'omit',
|
116 |
+
'output-html' => true,
|
117 |
+
'show-body-only' => true,
|
118 |
+
'drop-empty-paras' => true,
|
119 |
+
'drop-font-tags' => true,
|
120 |
+
'drop-proprietary-attributes' => true,
|
121 |
+
'hide-comments' => true,
|
122 |
+
'logical-emphasis' => true
|
123 |
+
]);
|
124 |
+
$tidy->cleanRepair();
|
125 |
+
|
126 |
+
return (string) $content;
|
127 |
+
}
|
128 |
+
}
|
lib/Aws/Aws/Api/ErrorParser/JsonParserTrait.php
ADDED
@@ -0,0 +1,26 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api\ErrorParser;
|
3 |
+
|
4 |
+
use Aws\Api\Parser\PayloadParserTrait;
|
5 |
+
use Psr\Http\Message\ResponseInterface;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Provides basic JSON error parsing functionality.
|
9 |
+
*/
|
10 |
+
trait JsonParserTrait
|
11 |
+
{
|
12 |
+
use PayloadParserTrait;
|
13 |
+
|
14 |
+
private function genericHandler(ResponseInterface $response)
|
15 |
+
{
|
16 |
+
$code = (string) $response->getStatusCode();
|
17 |
+
|
18 |
+
return [
|
19 |
+
'request_id' => (string) $response->getHeaderLine('x-amzn-requestid'),
|
20 |
+
'code' => null,
|
21 |
+
'message' => null,
|
22 |
+
'type' => $code[0] == '4' ? 'client' : 'server',
|
23 |
+
'parsed' => $this->parseJson($response->getBody(), $response)
|
24 |
+
];
|
25 |
+
}
|
26 |
+
}
|
lib/Aws/Aws/Api/ErrorParser/JsonRpcErrorParser.php
ADDED
@@ -0,0 +1,31 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api\ErrorParser;
|
3 |
+
|
4 |
+
use Psr\Http\Message\ResponseInterface;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Parsers JSON-RPC errors.
|
8 |
+
*/
|
9 |
+
class JsonRpcErrorParser
|
10 |
+
{
|
11 |
+
use JsonParserTrait;
|
12 |
+
|
13 |
+
public function __invoke(ResponseInterface $response)
|
14 |
+
{
|
15 |
+
$data = $this->genericHandler($response);
|
16 |
+
// Make the casing consistent across services.
|
17 |
+
if ($data['parsed']) {
|
18 |
+
$data['parsed'] = array_change_key_case($data['parsed']);
|
19 |
+
}
|
20 |
+
|
21 |
+
if (isset($data['parsed']['__type'])) {
|
22 |
+
$parts = explode('#', $data['parsed']['__type']);
|
23 |
+
$data['code'] = isset($parts[1]) ? $parts[1] : $parts[0];
|
24 |
+
$data['message'] = isset($data['parsed']['message'])
|
25 |
+
? $data['parsed']['message']
|
26 |
+
: null;
|
27 |
+
}
|
28 |
+
|
29 |
+
return $data;
|
30 |
+
}
|
31 |
+
}
|
lib/Aws/Aws/Api/ErrorParser/RestJsonErrorParser.php
ADDED
@@ -0,0 +1,35 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api\ErrorParser;
|
3 |
+
|
4 |
+
use Psr\Http\Message\ResponseInterface;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Parses JSON-REST errors.
|
8 |
+
*/
|
9 |
+
class RestJsonErrorParser
|
10 |
+
{
|
11 |
+
use JsonParserTrait;
|
12 |
+
|
13 |
+
public function __invoke(ResponseInterface $response)
|
14 |
+
{
|
15 |
+
$data = $this->genericHandler($response);
|
16 |
+
|
17 |
+
// Merge in error data from the JSON body
|
18 |
+
if ($json = $data['parsed']) {
|
19 |
+
$data = array_replace($data, $json);
|
20 |
+
}
|
21 |
+
|
22 |
+
// Correct error type from services like Amazon Glacier
|
23 |
+
if (!empty($data['type'])) {
|
24 |
+
$data['type'] = strtolower($data['type']);
|
25 |
+
}
|
26 |
+
|
27 |
+
// Retrieve the error code from services like Amazon Elastic Transcoder
|
28 |
+
if ($code = $response->getHeaderLine('x-amzn-errortype')) {
|
29 |
+
$colon = strpos($code, ':');
|
30 |
+
$data['code'] = $colon ? substr($code, 0, $colon) : $code;
|
31 |
+
}
|
32 |
+
|
33 |
+
return $data;
|
34 |
+
}
|
35 |
+
}
|
lib/Aws/Aws/Api/ErrorParser/XmlErrorParser.php
ADDED
@@ -0,0 +1,82 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api\ErrorParser;
|
3 |
+
|
4 |
+
use Aws\Api\Parser\PayloadParserTrait;
|
5 |
+
use Psr\Http\Message\ResponseInterface;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Parses XML errors.
|
9 |
+
*/
|
10 |
+
class XmlErrorParser
|
11 |
+
{
|
12 |
+
use PayloadParserTrait;
|
13 |
+
|
14 |
+
public function __invoke(ResponseInterface $response)
|
15 |
+
{
|
16 |
+
$code = (string) $response->getStatusCode();
|
17 |
+
|
18 |
+
$data = [
|
19 |
+
'type' => $code[0] == '4' ? 'client' : 'server',
|
20 |
+
'request_id' => null,
|
21 |
+
'code' => null,
|
22 |
+
'message' => null,
|
23 |
+
'parsed' => null
|
24 |
+
];
|
25 |
+
|
26 |
+
$body = $response->getBody();
|
27 |
+
if ($body->getSize() > 0) {
|
28 |
+
$this->parseBody($this->parseXml($body, $response), $data);
|
29 |
+
} else {
|
30 |
+
$this->parseHeaders($response, $data);
|
31 |
+
}
|
32 |
+
|
33 |
+
return $data;
|
34 |
+
}
|
35 |
+
|
36 |
+
private function parseHeaders(ResponseInterface $response, array &$data)
|
37 |
+
{
|
38 |
+
if ($response->getStatusCode() == '404') {
|
39 |
+
$data['code'] = 'NotFound';
|
40 |
+
}
|
41 |
+
|
42 |
+
$data['message'] = $response->getStatusCode() . ' '
|
43 |
+
. $response->getReasonPhrase();
|
44 |
+
|
45 |
+
if ($requestId = $response->getHeaderLine('x-amz-request-id')) {
|
46 |
+
$data['request_id'] = $requestId;
|
47 |
+
$data['message'] .= " (Request-ID: $requestId)";
|
48 |
+
}
|
49 |
+
}
|
50 |
+
|
51 |
+
private function parseBody(\SimpleXMLElement $body, array &$data)
|
52 |
+
{
|
53 |
+
$data['parsed'] = $body;
|
54 |
+
|
55 |
+
$namespaces = $body->getDocNamespaces();
|
56 |
+
if (!isset($namespaces[''])) {
|
57 |
+
$prefix = '';
|
58 |
+
} else {
|
59 |
+
// Account for the default namespace being defined and PHP not
|
60 |
+
// being able to handle it :(.
|
61 |
+
$body->registerXPathNamespace('ns', $namespaces['']);
|
62 |
+
$prefix = 'ns:';
|
63 |
+
}
|
64 |
+
|
65 |
+
if ($tempXml = $body->xpath("//{$prefix}Code[1]")) {
|
66 |
+
$data['code'] = (string) $tempXml[0];
|
67 |
+
}
|
68 |
+
|
69 |
+
if ($tempXml = $body->xpath("//{$prefix}Message[1]")) {
|
70 |
+
$data['message'] = (string) $tempXml[0];
|
71 |
+
}
|
72 |
+
|
73 |
+
$tempXml = $body->xpath("//{$prefix}RequestId[1]");
|
74 |
+
if (empty($tempXml)) {
|
75 |
+
$tempXml = $body->xpath("//{$prefix}RequestID[1]");
|
76 |
+
}
|
77 |
+
|
78 |
+
if (isset($tempXml[0])) {
|
79 |
+
$data['request_id'] = (string) $tempXml[0];
|
80 |
+
}
|
81 |
+
}
|
82 |
+
}
|
lib/Aws/Aws/Api/ListShape.php
ADDED
@@ -0,0 +1,35 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Represents a list shape.
|
6 |
+
*/
|
7 |
+
class ListShape extends Shape
|
8 |
+
{
|
9 |
+
private $member;
|
10 |
+
|
11 |
+
public function __construct(array $definition, ShapeMap $shapeMap)
|
12 |
+
{
|
13 |
+
$definition['type'] = 'list';
|
14 |
+
parent::__construct($definition, $shapeMap);
|
15 |
+
}
|
16 |
+
|
17 |
+
/**
|
18 |
+
* @return Shape
|
19 |
+
* @throws \RuntimeException if no member is specified
|
20 |
+
*/
|
21 |
+
public function getMember()
|
22 |
+
{
|
23 |
+
if (!$this->member) {
|
24 |
+
if (!isset($this->definition['member'])) {
|
25 |
+
throw new \RuntimeException('No member attribute specified');
|
26 |
+
}
|
27 |
+
$this->member = Shape::create(
|
28 |
+
$this->definition['member'],
|
29 |
+
$this->shapeMap
|
30 |
+
);
|
31 |
+
}
|
32 |
+
|
33 |
+
return $this->member;
|
34 |
+
}
|
35 |
+
}
|
lib/Aws/Aws/Api/MapShape.php
ADDED
@@ -0,0 +1,54 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Represents a map shape.
|
6 |
+
*/
|
7 |
+
class MapShape extends Shape
|
8 |
+
{
|
9 |
+
/** @var Shape */
|
10 |
+
private $value;
|
11 |
+
|
12 |
+
/** @var Shape */
|
13 |
+
private $key;
|
14 |
+
|
15 |
+
public function __construct(array $definition, ShapeMap $shapeMap)
|
16 |
+
{
|
17 |
+
$definition['type'] = 'map';
|
18 |
+
parent::__construct($definition, $shapeMap);
|
19 |
+
}
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @return Shape
|
23 |
+
* @throws \RuntimeException if no value is specified
|
24 |
+
*/
|
25 |
+
public function getValue()
|
26 |
+
{
|
27 |
+
if (!$this->value) {
|
28 |
+
if (!isset($this->definition['value'])) {
|
29 |
+
throw new \RuntimeException('No value specified');
|
30 |
+
}
|
31 |
+
|
32 |
+
$this->value = Shape::create(
|
33 |
+
$this->definition['value'],
|
34 |
+
$this->shapeMap
|
35 |
+
);
|
36 |
+
}
|
37 |
+
|
38 |
+
return $this->value;
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* @return Shape
|
43 |
+
*/
|
44 |
+
public function getKey()
|
45 |
+
{
|
46 |
+
if (!$this->key) {
|
47 |
+
$this->key = isset($this->definition['key'])
|
48 |
+
? Shape::create($this->definition['key'], $this->shapeMap)
|
49 |
+
: new Shape(['type' => 'string'], $this->shapeMap);
|
50 |
+
}
|
51 |
+
|
52 |
+
return $this->key;
|
53 |
+
}
|
54 |
+
}
|
lib/Aws/Aws/Api/Operation.php
ADDED
@@ -0,0 +1,97 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Represents an API operation.
|
6 |
+
*/
|
7 |
+
class Operation extends AbstractModel
|
8 |
+
{
|
9 |
+
private $input;
|
10 |
+
private $output;
|
11 |
+
private $errors;
|
12 |
+
|
13 |
+
public function __construct(array $definition, ShapeMap $shapeMap)
|
14 |
+
{
|
15 |
+
$definition['type'] = 'structure';
|
16 |
+
|
17 |
+
if (!isset($definition['http']['method'])) {
|
18 |
+
$definition['http']['method'] = 'POST';
|
19 |
+
}
|
20 |
+
|
21 |
+
if (!isset($definition['http']['requestUri'])) {
|
22 |
+
$definition['http']['requestUri'] = '/';
|
23 |
+
}
|
24 |
+
|
25 |
+
parent::__construct($definition, $shapeMap);
|
26 |
+
}
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Returns an associative array of the HTTP attribute of the operation:
|
30 |
+
*
|
31 |
+
* - method: HTTP method of the operation
|
32 |
+
* - requestUri: URI of the request (can include URI template placeholders)
|
33 |
+
*
|
34 |
+
* @return array
|
35 |
+
*/
|
36 |
+
public function getHttp()
|
37 |
+
{
|
38 |
+
return $this->definition['http'];
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Get the input shape of the operation.
|
43 |
+
*
|
44 |
+
* @return StructureShape
|
45 |
+
*/
|
46 |
+
public function getInput()
|
47 |
+
{
|
48 |
+
if (!$this->input) {
|
49 |
+
if ($input = $this['input']) {
|
50 |
+
$this->input = $this->shapeFor($input);
|
51 |
+
} else {
|
52 |
+
$this->input = new StructureShape([], $this->shapeMap);
|
53 |
+
}
|
54 |
+
}
|
55 |
+
|
56 |
+
return $this->input;
|
57 |
+
}
|
58 |
+
|
59 |
+
/**
|
60 |
+
* Get the output shape of the operation.
|
61 |
+
*
|
62 |
+
* @return StructureShape
|
63 |
+
*/
|
64 |
+
public function getOutput()
|
65 |
+
{
|
66 |
+
if (!$this->output) {
|
67 |
+
if ($output = $this['output']) {
|
68 |
+
$this->output = $this->shapeFor($output);
|
69 |
+
} else {
|
70 |
+
$this->output = new StructureShape([], $this->shapeMap);
|
71 |
+
}
|
72 |
+
}
|
73 |
+
|
74 |
+
return $this->output;
|
75 |
+
}
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Get an array of operation error shapes.
|
79 |
+
*
|
80 |
+
* @return Shape[]
|
81 |
+
*/
|
82 |
+
public function getErrors()
|
83 |
+
{
|
84 |
+
if ($this->errors === null) {
|
85 |
+
if ($errors = $this['errors']) {
|
86 |
+
foreach ($errors as $key => $error) {
|
87 |
+
$errors[$key] = $this->shapeFor($error);
|
88 |
+
}
|
89 |
+
$this->errors = $errors;
|
90 |
+
} else {
|
91 |
+
$this->errors = [];
|
92 |
+
}
|
93 |
+
}
|
94 |
+
|
95 |
+
return $this->errors;
|
96 |
+
}
|
97 |
+
}
|
lib/Aws/Aws/Api/Parser/AbstractParser.php
ADDED
@@ -0,0 +1,46 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api\Parser;
|
3 |
+
|
4 |
+
use Aws\Api\Service;
|
5 |
+
use Aws\Api\StructureShape;
|
6 |
+
use Aws\CommandInterface;
|
7 |
+
use Aws\ResultInterface;
|
8 |
+
use Psr\Http\Message\ResponseInterface;
|
9 |
+
use Psr\Http\Message\StreamInterface;
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @internal
|
13 |
+
*/
|
14 |
+
abstract class AbstractParser
|
15 |
+
{
|
16 |
+
/** @var \Aws\Api\Service Representation of the service API*/
|
17 |
+
protected $api;
|
18 |
+
|
19 |
+
/** @var callable */
|
20 |
+
protected $parser;
|
21 |
+
|
22 |
+
/**
|
23 |
+
* @param Service $api Service description.
|
24 |
+
*/
|
25 |
+
public function __construct(Service $api)
|
26 |
+
{
|
27 |
+
$this->api = $api;
|
28 |
+
}
|
29 |
+
|
30 |
+
/**
|
31 |
+
* @param CommandInterface $command Command that was executed.
|
32 |
+
* @param ResponseInterface $response Response that was received.
|
33 |
+
*
|
34 |
+
* @return ResultInterface
|
35 |
+
*/
|
36 |
+
abstract public function __invoke(
|
37 |
+
CommandInterface $command,
|
38 |
+
ResponseInterface $response
|
39 |
+
);
|
40 |
+
|
41 |
+
abstract public function parseMemberFromStream(
|
42 |
+
StreamInterface $stream,
|
43 |
+
StructureShape $member,
|
44 |
+
$response
|
45 |
+
);
|
46 |
+
}
|
lib/Aws/Aws/Api/Parser/AbstractRestParser.php
ADDED
@@ -0,0 +1,173 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api\Parser;
|
3 |
+
|
4 |
+
use Aws\Api\DateTimeResult;
|
5 |
+
use Aws\Api\Shape;
|
6 |
+
use Aws\Api\StructureShape;
|
7 |
+
use Aws\Result;
|
8 |
+
use Aws\CommandInterface;
|
9 |
+
use Psr\Http\Message\ResponseInterface;
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @internal
|
13 |
+
*/
|
14 |
+
abstract class AbstractRestParser extends AbstractParser
|
15 |
+
{
|
16 |
+
use PayloadParserTrait;
|
17 |
+
/**
|
18 |
+
* Parses a payload from a response.
|
19 |
+
*
|
20 |
+
* @param ResponseInterface $response Response to parse.
|
21 |
+
* @param StructureShape $member Member to parse
|
22 |
+
* @param array $result Result value
|
23 |
+
*
|
24 |
+
* @return mixed
|
25 |
+
*/
|
26 |
+
abstract protected function payload(
|
27 |
+
ResponseInterface $response,
|
28 |
+
StructureShape $member,
|
29 |
+
array &$result
|
30 |
+
);
|
31 |
+
|
32 |
+
public function __invoke(
|
33 |
+
CommandInterface $command,
|
34 |
+
ResponseInterface $response
|
35 |
+
) {
|
36 |
+
$output = $this->api->getOperation($command->getName())->getOutput();
|
37 |
+
$result = [];
|
38 |
+
|
39 |
+
if ($payload = $output['payload']) {
|
40 |
+
$this->extractPayload($payload, $output, $response, $result);
|
41 |
+
}
|
42 |
+
|
43 |
+
foreach ($output->getMembers() as $name => $member) {
|
44 |
+
switch ($member['location']) {
|
45 |
+
case 'header':
|
46 |
+
$this->extractHeader($name, $member, $response, $result);
|
47 |
+
break;
|
48 |
+
case 'headers':
|
49 |
+
$this->extractHeaders($name, $member, $response, $result);
|
50 |
+
break;
|
51 |
+
case 'statusCode':
|
52 |
+
$this->extractStatus($name, $response, $result);
|
53 |
+
break;
|
54 |
+
}
|
55 |
+
}
|
56 |
+
|
57 |
+
if (!$payload
|
58 |
+
&& $response->getBody()->getSize() > 0
|
59 |
+
&& count($output->getMembers()) > 0
|
60 |
+
) {
|
61 |
+
// if no payload was found, then parse the contents of the body
|
62 |
+
$this->payload($response, $output, $result);
|
63 |
+
}
|
64 |
+
|
65 |
+
return new Result($result);
|
66 |
+
}
|
67 |
+
|
68 |
+
private function extractPayload(
|
69 |
+
$payload,
|
70 |
+
StructureShape $output,
|
71 |
+
ResponseInterface $response,
|
72 |
+
array &$result
|
73 |
+
) {
|
74 |
+
$member = $output->getMember($payload);
|
75 |
+
|
76 |
+
if (!empty($member['eventstream'])) {
|
77 |
+
$result[$payload] = new EventParsingIterator(
|
78 |
+
$response->getBody(),
|
79 |
+
$member,
|
80 |
+
$this
|
81 |
+
);
|
82 |
+
} else if ($member instanceof StructureShape) {
|
83 |
+
// Structure members parse top-level data into a specific key.
|
84 |
+
$result[$payload] = [];
|
85 |
+
$this->payload($response, $member, $result[$payload]);
|
86 |
+
} else {
|
87 |
+
// Streaming data is just the stream from the response body.
|
88 |
+
$result[$payload] = $response->getBody();
|
89 |
+
}
|
90 |
+
}
|
91 |
+
|
92 |
+
/**
|
93 |
+
* Extract a single header from the response into the result.
|
94 |
+
*/
|
95 |
+
private function extractHeader(
|
96 |
+
$name,
|
97 |
+
Shape $shape,
|
98 |
+
ResponseInterface $response,
|
99 |
+
&$result
|
100 |
+
) {
|
101 |
+
$value = $response->getHeaderLine($shape['locationName'] ?: $name);
|
102 |
+
|
103 |
+
switch ($shape->getType()) {
|
104 |
+
case 'float':
|
105 |
+
case 'double':
|
106 |
+
$value = (float) $value;
|
107 |
+
break;
|
108 |
+
case 'long':
|
109 |
+
$value = (int) $value;
|
110 |
+
break;
|
111 |
+
case 'boolean':
|
112 |
+
$value = filter_var($value, FILTER_VALIDATE_BOOLEAN);
|
113 |
+
break;
|
114 |
+
case 'blob':
|
115 |
+
$value = base64_decode($value);
|
116 |
+
break;
|
117 |
+
case 'timestamp':
|
118 |
+
try {
|
119 |
+
if (!empty($shape['timestampFormat'])
|
120 |
+
&& $shape['timestampFormat'] === 'unixTimestamp') {
|
121 |
+
$value = DateTimeResult::fromEpoch($value);
|
122 |
+
}
|
123 |
+
$value = new DateTimeResult($value);
|
124 |
+
break;
|
125 |
+
} catch (\Exception $e) {
|
126 |
+
// If the value cannot be parsed, then do not add it to the
|
127 |
+
// output structure.
|
128 |
+
return;
|
129 |
+
}
|
130 |
+
case 'string':
|
131 |
+
if ($shape['jsonvalue']) {
|
132 |
+
$value = $this->parseJson(base64_decode($value), $response);
|
133 |
+
}
|
134 |
+
break;
|
135 |
+
}
|
136 |
+
|
137 |
+
$result[$name] = $value;
|
138 |
+
}
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Extract a map of headers with an optional prefix from the response.
|
142 |
+
*/
|
143 |
+
private function extractHeaders(
|
144 |
+
$name,
|
145 |
+
Shape $shape,
|
146 |
+
ResponseInterface $response,
|
147 |
+
&$result
|
148 |
+
) {
|
149 |
+
// Check if the headers are prefixed by a location name
|
150 |
+
$result[$name] = [];
|
151 |
+
$prefix = $shape['locationName'];
|
152 |
+
$prefixLen = strlen($prefix);
|
153 |
+
|
154 |
+
foreach ($response->getHeaders() as $k => $values) {
|
155 |
+
if (!$prefixLen) {
|
156 |
+
$result[$name][$k] = implode(', ', $values);
|
157 |
+
} elseif (stripos($k, $prefix) === 0) {
|
158 |
+
$result[$name][substr($k, $prefixLen)] = implode(', ', $values);
|
159 |
+
}
|
160 |
+
}
|
161 |
+
}
|
162 |
+
|
163 |
+
/**
|
164 |
+
* Places the status code of the response into the result array.
|
165 |
+
*/
|
166 |
+
private function extractStatus(
|
167 |
+
$name,
|
168 |
+
ResponseInterface $response,
|
169 |
+
array &$result
|
170 |
+
) {
|
171 |
+
$result[$name] = (int) $response->getStatusCode();
|
172 |
+
}
|
173 |
+
}
|
lib/Aws/Aws/Api/Parser/Crc32ValidatingParser.php
ADDED
@@ -0,0 +1,54 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api\Parser;
|
3 |
+
|
4 |
+
use Aws\Api\StructureShape;
|
5 |
+
use Aws\CommandInterface;
|
6 |
+
use Aws\Exception\AwsException;
|
7 |
+
use Psr\Http\Message\ResponseInterface;
|
8 |
+
use Psr\Http\Message\StreamInterface;
|
9 |
+
use GuzzleHttp\Psr7;
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @internal Decorates a parser and validates the x-amz-crc32 header.
|
13 |
+
*/
|
14 |
+
class Crc32ValidatingParser extends AbstractParser
|
15 |
+
{
|
16 |
+
/**
|
17 |
+
* @param callable $parser Parser to wrap.
|
18 |
+
*/
|
19 |
+
public function __construct(callable $parser)
|
20 |
+
{
|
21 |
+
$this->parser = $parser;
|
22 |
+
}
|
23 |
+
|
24 |
+
public function __invoke(
|
25 |
+
CommandInterface $command,
|
26 |
+
ResponseInterface $response
|
27 |
+
) {
|
28 |
+
if ($expected = $response->getHeaderLine('x-amz-crc32')) {
|
29 |
+
$hash = hexdec(Psr7\hash($response->getBody(), 'crc32b'));
|
30 |
+
if ($expected != $hash) {
|
31 |
+
throw new AwsException(
|
32 |
+
"crc32 mismatch. Expected {$expected}, found {$hash}.",
|
33 |
+
$command,
|
34 |
+
[
|
35 |
+
'code' => 'ClientChecksumMismatch',
|
36 |
+
'connection_error' => true,
|
37 |
+
'response' => $response
|
38 |
+
]
|
39 |
+
);
|
40 |
+
}
|
41 |
+
}
|
42 |
+
|
43 |
+
$fn = $this->parser;
|
44 |
+
return $fn($command, $response);
|
45 |
+
}
|
46 |
+
|
47 |
+
public function parseMemberFromStream(
|
48 |
+
StreamInterface $stream,
|
49 |
+
StructureShape $member,
|
50 |
+
$response
|
51 |
+
) {
|
52 |
+
return $this->parser->parseMemberFromStream($stream, $member, $response);
|
53 |
+
}
|
54 |
+
}
|
lib/Aws/Aws/Api/Parser/DecodingEventStreamIterator.php
ADDED
@@ -0,0 +1,335 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Aws\Api\Parser;
|
4 |
+
|
5 |
+
use \Iterator;
|
6 |
+
use Aws\Api\DateTimeResult;
|
7 |
+
use GuzzleHttp\Psr7;
|
8 |
+
use Psr\Http\Message\StreamInterface;
|
9 |
+
use Aws\Api\Parser\Exception\ParserException;
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @internal Implements a decoder for a binary encoded event stream that will
|
13 |
+
* decode, validate, and provide individual events from the stream.
|
14 |
+
*/
|
15 |
+
class DecodingEventStreamIterator implements Iterator
|
16 |
+
{
|
17 |
+
const HEADERS = 'headers';
|
18 |
+
const PAYLOAD = 'payload';
|
19 |
+
|
20 |
+
const LENGTH_TOTAL = 'total_length';
|
21 |
+
const LENGTH_HEADERS = 'headers_length';
|
22 |
+
|
23 |
+
const CRC_PRELUDE = 'prelude_crc';
|
24 |
+
|
25 |
+
const BYTES_PRELUDE = 12;
|
26 |
+
const BYTES_TRAILING = 4;
|
27 |
+
|
28 |
+
private static $preludeFormat = [
|
29 |
+
self::LENGTH_TOTAL => 'decodeUint32',
|
30 |
+
self::LENGTH_HEADERS => 'decodeUint32',
|
31 |
+
self::CRC_PRELUDE => 'decodeUint32',
|
32 |
+
];
|
33 |
+
|
34 |
+
private static $lengthFormatMap = [
|
35 |
+
1 => 'decodeUint8',
|
36 |
+
2 => 'decodeUint16',
|
37 |
+
4 => 'decodeUint32',
|
38 |
+
8 => 'decodeUint64',
|
39 |
+
];
|
40 |
+
|
41 |
+
private static $headerTypeMap = [
|
42 |
+
0 => 'decodeBooleanTrue',
|
43 |
+
1 => 'decodeBooleanFalse',
|
44 |
+
2 => 'decodeInt8',
|
45 |
+
3 => 'decodeInt16',
|
46 |
+
4 => 'decodeInt32',
|
47 |
+
5 => 'decodeInt64',
|
48 |
+
6 => 'decodeBytes',
|
49 |
+
7 => 'decodeString',
|
50 |
+
8 => 'decodeTimestamp',
|
51 |
+
9 => 'decodeUuid',
|
52 |
+
];
|
53 |
+
|
54 |
+
/** @var StreamInterface Stream of eventstream shape to parse. */
|
55 |
+
private $stream;
|
56 |
+
|
57 |
+
/** @var array Currently parsed event. */
|
58 |
+
private $currentEvent;
|
59 |
+
|
60 |
+
/** @var int Current in-order event key. */
|
61 |
+
private $key;
|
62 |
+
|
63 |
+
/** @var resource|HashContext CRC32 hash context for event validation */
|
64 |
+
private $hashContext;
|
65 |
+
|
66 |
+
/** @var int $currentPosition */
|
67 |
+
private $currentPosition;
|
68 |
+
|
69 |
+
/**
|
70 |
+
* DecodingEventStreamIterator constructor.
|
71 |
+
*
|
72 |
+
* @param StreamInterface $stream
|
73 |
+
*/
|
74 |
+
public function __construct(StreamInterface $stream)
|
75 |
+
{
|
76 |
+
$this->stream = $stream;
|
77 |
+
$this->rewind();
|
78 |
+
}
|
79 |
+
|
80 |
+
private function parseHeaders($headerBytes)
|
81 |
+
{
|
82 |
+
$headers = [];
|
83 |
+
$bytesRead = 0;
|
84 |
+
|
85 |
+
while ($bytesRead < $headerBytes) {
|
86 |
+
list($key, $numBytes) = $this->decodeString(1);
|
87 |
+
$bytesRead += $numBytes;
|
88 |
+
|
89 |
+
list($type, $numBytes) = $this->decodeUint8();
|
90 |
+
$bytesRead += $numBytes;
|
91 |
+
|
92 |
+
$f = self::$headerTypeMap[$type];
|
93 |
+
list($value, $numBytes) = $this->{$f}();
|
94 |
+
$bytesRead += $numBytes;
|
95 |
+
|
96 |
+
if (isset($headers[$key])) {
|
97 |
+
throw new ParserException('Duplicate key in event headers.');
|
98 |
+
}
|
99 |
+
$headers[$key] = $value;
|
100 |
+
}
|
101 |
+
|
102 |
+
return [$headers, $bytesRead];
|
103 |
+
}
|
104 |
+
|
105 |
+
private function parsePrelude()
|
106 |
+
{
|
107 |
+
$prelude = [];
|
108 |
+
$bytesRead = 0;
|
109 |
+
|
110 |
+
$calculatedCrc = null;
|
111 |
+
foreach (self::$preludeFormat as $key => $decodeFunction) {
|
112 |
+
if ($key === self::CRC_PRELUDE) {
|
113 |
+
$hashCopy = hash_copy($this->hashContext);
|
114 |
+
$calculatedCrc = hash_final($this->hashContext, true);
|
115 |
+
$this->hashContext = $hashCopy;
|
116 |
+
}
|
117 |
+
list($value, $numBytes) = $this->{$decodeFunction}();
|
118 |
+
$bytesRead += $numBytes;
|
119 |
+
|
120 |
+
$prelude[$key] = $value;
|
121 |
+
}
|
122 |
+
|
123 |
+
if (unpack('N', $calculatedCrc)[1] !== $prelude[self::CRC_PRELUDE]) {
|
124 |
+
throw new ParserException('Prelude checksum mismatch.');
|
125 |
+
}
|
126 |
+
|
127 |
+
return [$prelude, $bytesRead];
|
128 |
+
}
|
129 |
+
|
130 |
+
private function parseEvent()
|
131 |
+
{
|
132 |
+
$event = [];
|
133 |
+
|
134 |
+
if ($this->stream->tell() < $this->stream->getSize()) {
|
135 |
+
$this->hashContext = hash_init('crc32b');
|
136 |
+
|
137 |
+
$bytesLeft = $this->stream->getSize() - $this->stream->tell();
|
138 |
+
list($prelude, $numBytes) = $this->parsePrelude();
|
139 |
+
if ($prelude[self::LENGTH_TOTAL] > $bytesLeft) {
|
140 |
+
throw new ParserException('Message length too long.');
|
141 |
+
}
|
142 |
+
$bytesLeft -= $numBytes;
|
143 |
+
|
144 |
+
if ($prelude[self::LENGTH_HEADERS] > $bytesLeft) {
|
145 |
+
throw new ParserException('Headers length too long.');
|
146 |
+
}
|
147 |
+
|
148 |
+
list(
|
149 |
+
$event[self::HEADERS],
|
150 |
+
$numBytes
|
151 |
+
) = $this->parseHeaders($prelude[self::LENGTH_HEADERS]);
|
152 |
+
|
153 |
+
$event[self::PAYLOAD] = Psr7\stream_for(
|
154 |
+
$this->readAndHashBytes(
|
155 |
+
$prelude[self::LENGTH_TOTAL] - self::BYTES_PRELUDE
|
156 |
+
- $numBytes - self::BYTES_TRAILING
|
157 |
+
)
|
158 |
+
);
|
159 |
+
|
160 |
+
$calculatedCrc = hash_final($this->hashContext, true);
|
161 |
+
$messageCrc = $this->stream->read(4);
|
162 |
+
if ($calculatedCrc !== $messageCrc) {
|
163 |
+
throw new ParserException('Message checksum mismatch.');
|
164 |
+
}
|
165 |
+
}
|
166 |
+
|
167 |
+
return $event;
|
168 |
+
}
|
169 |
+
|
170 |
+
// Iterator Functionality
|
171 |
+
|
172 |
+
/**
|
173 |
+
* @return array
|
174 |
+
*/
|
175 |
+
public function current()
|
176 |
+
{
|
177 |
+
return $this->currentEvent;
|
178 |
+
}
|
179 |
+
|
180 |
+
/**
|
181 |
+
* @return int
|
182 |
+
*/
|
183 |
+
public function key()
|
184 |
+
{
|
185 |
+
return $this->key;
|
186 |
+
}
|
187 |
+
|
188 |
+
public function next()
|
189 |
+
{
|
190 |
+
$this->currentPosition = $this->stream->tell();
|
191 |
+
if ($this->valid()) {
|
192 |
+
$this->key++;
|
193 |
+
$this->currentEvent = $this->parseEvent();
|
194 |
+
}
|
195 |
+
}
|
196 |
+
|
197 |
+
public function rewind()
|
198 |
+
{
|
199 |
+
$this->stream->rewind();
|
200 |
+
$this->key = 0;
|
201 |
+
$this->currentPosition = 0;
|
202 |
+
$this->currentEvent = $this->parseEvent();
|
203 |
+
}
|
204 |
+
|
205 |
+
/**
|
206 |
+
* @return bool
|
207 |
+
*/
|
208 |
+
public function valid()
|
209 |
+
{
|
210 |
+
return $this->currentPosition < $this->stream->getSize();
|
211 |
+
}
|
212 |
+
|
213 |
+
// Decoding Utilities
|
214 |
+
|
215 |
+
private function readAndHashBytes($num)
|
216 |
+
{
|
217 |
+
$bytes = $this->stream->read($num);
|
218 |
+
hash_update($this->hashContext, $bytes);
|
219 |
+
return $bytes;
|
220 |
+
}
|
221 |
+
|
222 |
+
private function decodeBooleanTrue()
|
223 |
+
{
|
224 |
+
return [true, 0];
|
225 |
+
}
|
226 |
+
|
227 |
+
private function decodeBooleanFalse()
|
228 |
+
{
|
229 |
+
return [false, 0];
|
230 |
+
}
|
231 |
+
|
232 |
+
private function uintToInt($val, $size)
|
233 |
+
{
|
234 |
+
$signedCap = pow(2, $size - 1);
|
235 |
+
if ($val > $signedCap) {
|
236 |
+
$val -= (2 * $signedCap);
|
237 |
+
}
|
238 |
+
return $val;
|
239 |
+
}
|
240 |
+
|
241 |
+
private function decodeInt8()
|
242 |
+
{
|
243 |
+
$val = (int)unpack('C', $this->readAndHashBytes(1))[1];
|
244 |
+
return [$this->uintToInt($val, 8), 1];
|
245 |
+
}
|
246 |
+
|
247 |
+
private function decodeUint8()
|
248 |
+
{
|
249 |
+
return [unpack('C', $this->readAndHashBytes(1))[1], 1];
|
250 |
+
}
|
251 |
+
|
252 |
+
private function decodeInt16()
|
253 |
+
{
|
254 |
+
$val = (int)unpack('n', $this->readAndHashBytes(2))[1];
|
255 |
+
return [$this->uintToInt($val, 16), 2];
|
256 |
+
}
|
257 |
+
|
258 |
+
private function decodeUint16()
|
259 |
+
{
|
260 |
+
return [unpack('n', $this->readAndHashBytes(2))[1], 2];
|
261 |
+
}
|
262 |
+
|
263 |
+
private function decodeInt32()
|
264 |
+
{
|
265 |
+
$val = (int)unpack('N', $this->readAndHashBytes(4))[1];
|
266 |
+
return [$this->uintToInt($val, 32), 4];
|
267 |
+
}
|
268 |
+
|
269 |
+
private function decodeUint32()
|
270 |
+
{
|
271 |
+
return [unpack('N', $this->readAndHashBytes(4))[1], 4];
|
272 |
+
}
|
273 |
+
|
274 |
+
private function decodeInt64()
|
275 |
+
{
|
276 |
+
$val = $this->unpackInt64($this->readAndHashBytes(8))[1];
|
277 |
+
return [$this->uintToInt($val, 64), 8];
|
278 |
+
}
|
279 |
+
|
280 |
+
private function decodeUint64()
|
281 |
+
{
|
282 |
+
return [$this->unpackInt64($this->readAndHashBytes(8))[1], 8];
|
283 |
+
}
|
284 |
+
|
285 |
+
private function unpackInt64($bytes)
|
286 |
+
{
|
287 |
+
if (version_compare(PHP_VERSION, '5.6.3', '<')) {
|
288 |
+
$d = unpack('N2', $bytes);
|
289 |
+
return [1 => $d[1] << 32 | $d[2]];
|
290 |
+
}
|
291 |
+
return unpack('J', $bytes);
|
292 |
+
}
|
293 |
+
|
294 |
+
private function decodeBytes($lengthBytes=2)
|
295 |
+
{
|
296 |
+
if (!isset(self::$lengthFormatMap[$lengthBytes])) {
|
297 |
+
throw new ParserException('Undefined variable length format.');
|
298 |
+
}
|
299 |
+
$f = self::$lengthFormatMap[$lengthBytes];
|
300 |
+
list($len, $bytes) = $this->{$f}();
|
301 |
+
return [$this->readAndHashBytes($len), $len + $bytes];
|
302 |
+
}
|
303 |
+
|
304 |
+
private function decodeString($lengthBytes=2)
|
305 |
+
{
|
306 |
+
if (!isset(self::$lengthFormatMap[$lengthBytes])) {
|
307 |
+
throw new ParserException('Undefined variable length format.');
|
308 |
+
}
|
309 |
+
$f = self::$lengthFormatMap[$lengthBytes];
|
310 |
+
list($len, $bytes) = $this->{$f}();
|
311 |
+
return [$this->readAndHashBytes($len), $len + $bytes];
|
312 |
+
}
|
313 |
+
|
314 |
+
private function decodeTimestamp()
|
315 |
+
{
|
316 |
+
list($val, $bytes) = $this->decodeInt64();
|
317 |
+
return [
|
318 |
+
DateTimeResult::createFromFormat('U.u', $val / 1000),
|
319 |
+
$bytes
|
320 |
+
];
|
321 |
+
}
|
322 |
+
|
323 |
+
private function decodeUuid()
|
324 |
+
{
|
325 |
+
$val = unpack('H32', $this->readAndHashBytes(16))[1];
|
326 |
+
return [
|
327 |
+
substr($val, 0, 8) . '-'
|
328 |
+
. substr($val, 8, 4) . '-'
|
329 |
+
. substr($val, 12, 4) . '-'
|
330 |
+
. substr($val, 16, 4) . '-'
|
331 |
+
. substr($val, 20, 12),
|
332 |
+
16
|
333 |
+
];
|
334 |
+
}
|
335 |
+
}
|
lib/Aws/Aws/Api/Parser/EventParsingIterator.php
ADDED
@@ -0,0 +1,107 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Aws\Api\Parser;
|
4 |
+
|
5 |
+
use \Iterator;
|
6 |
+
use Aws\Exception\EventStreamDataException;
|
7 |
+
use Aws\Api\Parser\Exception\ParserException;
|
8 |
+
use Aws\Api\StructureShape;
|
9 |
+
use Psr\Http\Message\StreamInterface;
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @internal Implements a decoder for a binary encoded event stream that will
|
13 |
+
* decode, validate, and provide individual events from the stream.
|
14 |
+
*/
|
15 |
+
class EventParsingIterator implements Iterator
|
16 |
+
{
|
17 |
+
/** @var StreamInterface */
|
18 |
+
private $decodingIterator;
|
19 |
+
|
20 |
+
/** @var StructureShape */
|
21 |
+
private $shape;
|
22 |
+
|
23 |
+
/** @var AbstractParser */
|
24 |
+
private $parser;
|
25 |
+
|
26 |
+
public function __construct(
|
27 |
+
StreamInterface $stream,
|
28 |
+
StructureShape $shape,
|
29 |
+
AbstractParser $parser
|
30 |
+
) {
|
31 |
+
$this->decodingIterator = new DecodingEventStreamIterator($stream);
|
32 |
+
$this->shape = $shape;
|
33 |
+
$this->parser = $parser;
|
34 |
+
}
|
35 |
+
|
36 |
+
public function current()
|
37 |
+
{
|
38 |
+
return $this->parseEvent($this->decodingIterator->current());
|
39 |
+
}
|
40 |
+
|
41 |
+
public function key()
|
42 |
+
{
|
43 |
+
return $this->decodingIterator->key();
|
44 |
+
}
|
45 |
+
|
46 |
+
public function next()
|
47 |
+
{
|
48 |
+
$this->decodingIterator->next();
|
49 |
+
}
|
50 |
+
|
51 |
+
public function rewind()
|
52 |
+
{
|
53 |
+
$this->decodingIterator->rewind();
|
54 |
+
}
|
55 |
+
|
56 |
+
public function valid()
|
57 |
+
{
|
58 |
+
return $this->decodingIterator->valid();
|
59 |
+
}
|
60 |
+
|
61 |
+
private function parseEvent(array $event)
|
62 |
+
{
|
63 |
+
if (!empty($event['headers'][':message-type'])) {
|
64 |
+
if ($event['headers'][':message-type'] === 'error') {
|
65 |
+
return $this->parseError($event);
|
66 |
+
}
|
67 |
+
if ($event['headers'][':message-type'] !== 'event') {
|
68 |
+
throw new ParserException('Failed to parse unknown message type.');
|
69 |
+
}
|
70 |
+
}
|
71 |
+
|
72 |
+
if (empty($event['headers'][':event-type'])) {
|
73 |
+
throw new ParserException('Failed to parse without event type.');
|
74 |
+
}
|
75 |
+
$eventShape = $this->shape->getMember($event['headers'][':event-type']);
|
76 |
+
|
77 |
+
$parsedEvent = [];
|
78 |
+
foreach ($eventShape['members'] as $shape => $details) {
|
79 |
+
if (!empty($details['eventpayload'])) {
|
80 |
+
$payloadShape = $eventShape->getMember($shape);
|
81 |
+
if ($payloadShape['type'] === 'blob') {
|
82 |
+
$parsedEvent[$shape] = $event['payload'];
|
83 |
+
} else {
|
84 |
+
$parsedEvent[$shape] = $this->parser->parseMemberFromStream(
|
85 |
+
$event['payload'],
|
86 |
+
$payloadShape,
|
87 |
+
null
|
88 |
+
);
|
89 |
+
}
|
90 |
+
} else {
|
91 |
+
$parsedEvent[$shape] = $event['headers'][$shape];
|
92 |
+
}
|
93 |
+
}
|
94 |
+
|
95 |
+
return [
|
96 |
+
$event['headers'][':event-type'] => $parsedEvent
|
97 |
+
];
|
98 |
+
}
|
99 |
+
|
100 |
+
private function parseError(array $event)
|
101 |
+
{
|
102 |
+
throw new EventStreamDataException(
|
103 |
+
$event['headers'][':error-code'],
|
104 |
+
$event['headers'][':error-message']
|
105 |
+
);
|
106 |
+
}
|
107 |
+
}
|
lib/Aws/Aws/Api/Parser/Exception/ParserException.php
ADDED
@@ -0,0 +1,31 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api\Parser\Exception;
|
3 |
+
|
4 |
+
use Aws\HasMonitoringEventsTrait;
|
5 |
+
use Aws\MonitoringEventsInterface;
|
6 |
+
use Aws\ResponseContainerInterface;
|
7 |
+
|
8 |
+
class ParserException extends \RuntimeException implements
|
9 |
+
MonitoringEventsInterface,
|
10 |
+
ResponseContainerInterface
|
11 |
+
{
|
12 |
+
use HasMonitoringEventsTrait;
|
13 |
+
|
14 |
+
private $response;
|
15 |
+
|
16 |
+
public function __construct($message = '', $code = 0, $previous = null, array $context = [])
|
17 |
+
{
|
18 |
+
$this->response = isset($context['response']) ? $context['response'] : null;
|
19 |
+
parent::__construct($message, $code, $previous);
|
20 |
+
}
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Get the received HTTP response if any.
|
24 |
+
*
|
25 |
+
* @return ResponseInterface|null
|
26 |
+
*/
|
27 |
+
public function getResponse()
|
28 |
+
{
|
29 |
+
return $this->response;
|
30 |
+
}
|
31 |
+
}
|
lib/Aws/Aws/Api/Parser/JsonParser.php
ADDED
@@ -0,0 +1,62 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api\Parser;
|
3 |
+
|
4 |
+
use Aws\Api\DateTimeResult;
|
5 |
+
use Aws\Api\Shape;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* @internal Implements standard JSON parsing.
|
9 |
+
*/
|
10 |
+
class JsonParser
|
11 |
+
{
|
12 |
+
public function parse(Shape $shape, $value)
|
13 |
+
{
|
14 |
+
if ($value === null) {
|
15 |
+
return $value;
|
16 |
+
}
|
17 |
+
|
18 |
+
switch ($shape['type']) {
|
19 |
+
case 'structure':
|
20 |
+
$target = [];
|
21 |
+
foreach ($shape->getMembers() as $name => $member) {
|
22 |
+
$locationName = $member['locationName'] ?: $name;
|
23 |
+
if (isset($value[$locationName])) {
|
24 |
+
$target[$name] = $this->parse($member, $value[$locationName]);
|
25 |
+
}
|
26 |
+
}
|
27 |
+
return $target;
|
28 |
+
|
29 |
+
case 'list':
|
30 |
+
$member = $shape->getMember();
|
31 |
+
$target = [];
|
32 |
+
foreach ($value as $v) {
|
33 |
+
$target[] = $this->parse($member, $v);
|
34 |
+
}
|
35 |
+
return $target;
|
36 |
+
|
37 |
+
case 'map':
|
38 |
+
$values = $shape->getValue();
|
39 |
+
$target = [];
|
40 |
+
foreach ($value as $k => $v) {
|
41 |
+
$target[$k] = $this->parse($values, $v);
|
42 |
+
}
|
43 |
+
return $target;
|
44 |
+
|
45 |
+
case 'timestamp':
|
46 |
+
if (!empty($shape['timestampFormat'])
|
47 |
+
&& $shape['timestampFormat'] !== 'unixTimestamp') {
|
48 |
+
return new DateTimeResult($value);
|
49 |
+
}
|
50 |
+
// The Unix epoch (or Unix time or POSIX time or Unix
|
51 |
+
// timestamp) is the number of seconds that have elapsed since
|
52 |
+
// January 1, 1970 (midnight UTC/GMT).
|
53 |
+
return DateTimeResult::fromEpoch($value);
|
54 |
+
|
55 |
+
case 'blob':
|
56 |
+
return base64_decode($value);
|
57 |
+
|
58 |
+
default:
|
59 |
+
return $value;
|
60 |
+
}
|
61 |
+
}
|
62 |
+
}
|
lib/Aws/Aws/Api/Parser/JsonRpcParser.php
ADDED
@@ -0,0 +1,51 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api\Parser;
|
3 |
+
|
4 |
+
use Aws\Api\StructureShape;
|
5 |
+
use Aws\Api\Service;
|
6 |
+
use Aws\Result;
|
7 |
+
use Aws\CommandInterface;
|
8 |
+
use Psr\Http\Message\ResponseInterface;
|
9 |
+
use Psr\Http\Message\StreamInterface;
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @internal Implements JSON-RPC parsing (e.g., DynamoDB)
|
13 |
+
*/
|
14 |
+
class JsonRpcParser extends AbstractParser
|
15 |
+
{
|
16 |
+
use PayloadParserTrait;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @param Service $api Service description
|
20 |
+
* @param JsonParser $parser JSON body builder
|
21 |
+
*/
|
22 |
+
public function __construct(Service $api, JsonParser $parser = null)
|
23 |
+
{
|
24 |
+
parent::__construct($api);
|
25 |
+
$this->parser = $parser ?: new JsonParser();
|
26 |
+
}
|
27 |
+
|
28 |
+
public function __invoke(
|
29 |
+
CommandInterface $command,
|
30 |
+
ResponseInterface $response
|
31 |
+
) {
|
32 |
+
$operation = $this->api->getOperation($command->getName());
|
33 |
+
$result = null === $operation['output']
|
34 |
+
? null
|
35 |
+
: $this->parseMemberFromStream(
|
36 |
+
$response->getBody(),
|
37 |
+
$operation->getOutput(),
|
38 |
+
$response
|
39 |
+
);
|
40 |
+
|
41 |
+
return new Result($result ?: []);
|
42 |
+
}
|
43 |
+
|
44 |
+
public function parseMemberFromStream(
|
45 |
+
StreamInterface $stream,
|
46 |
+
StructureShape $member,
|
47 |
+
$response
|
48 |
+
) {
|
49 |
+
return $this->parser->parse($member, $this->parseJson($stream, $response));
|
50 |
+
}
|
51 |
+
}
|
lib/Aws/Aws/Api/Parser/PayloadParserTrait.php
ADDED
@@ -0,0 +1,61 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api\Parser;
|
3 |
+
|
4 |
+
use Aws\Api\Parser\Exception\ParserException;
|
5 |
+
use Psr\Http\Message\ResponseInterface;
|
6 |
+
|
7 |
+
trait PayloadParserTrait
|
8 |
+
{
|
9 |
+
/**
|
10 |
+
* @param string $json
|
11 |
+
*
|
12 |
+
* @throws ParserException
|
13 |
+
*
|
14 |
+
* @return array
|
15 |
+
*/
|
16 |
+
private function parseJson($json, $response)
|
17 |
+
{
|
18 |
+
$jsonPayload = json_decode($json, true);
|
19 |
+
|
20 |
+
if (JSON_ERROR_NONE !== json_last_error()) {
|
21 |
+
throw new ParserException(
|
22 |
+
'Error parsing JSON: ' . json_last_error_msg(),
|
23 |
+
0,
|
24 |
+
null,
|
25 |
+
['response' => $response]
|
26 |
+
);
|
27 |
+
}
|
28 |
+
|
29 |
+
return $jsonPayload;
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* @param string $xml
|
34 |
+
*
|
35 |
+
* @throws ParserException
|
36 |
+
*
|
37 |
+
* @return \SimpleXMLElement
|
38 |
+
*/
|
39 |
+
private function parseXml($xml, $response)
|
40 |
+
{
|
41 |
+
$priorSetting = libxml_use_internal_errors(true);
|
42 |
+
try {
|
43 |
+
libxml_clear_errors();
|
44 |
+
$xmlPayload = new \SimpleXMLElement($xml);
|
45 |
+
if ($error = libxml_get_last_error()) {
|
46 |
+
throw new \RuntimeException($error->message);
|
47 |
+
}
|
48 |
+
} catch (\Exception $e) {
|
49 |
+
throw new ParserException(
|
50 |
+
"Error parsing XML: {$e->getMessage()}",
|
51 |
+
0,
|
52 |
+
$e,
|
53 |
+
['response' => $response]
|
54 |
+
);
|
55 |
+
} finally {
|
56 |
+
libxml_use_internal_errors($priorSetting);
|
57 |
+
}
|
58 |
+
|
59 |
+
return $xmlPayload;
|
60 |
+
}
|
61 |
+
}
|
lib/Aws/Aws/Api/Parser/QueryParser.php
ADDED
@@ -0,0 +1,60 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api\Parser;
|
3 |
+
|
4 |
+
use Aws\Api\Service;
|
5 |
+
use Aws\Api\StructureShape;
|
6 |
+
use Aws\Result;
|
7 |
+
use Aws\CommandInterface;
|
8 |
+
use Psr\Http\Message\ResponseInterface;
|
9 |
+
use Psr\Http\Message\StreamInterface;
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @internal Parses query (XML) responses (e.g., EC2, SQS, and many others)
|
13 |
+
*/
|
14 |
+
class QueryParser extends AbstractParser
|
15 |
+
{
|
16 |
+
use PayloadParserTrait;
|
17 |
+
|
18 |
+
/** @var bool */
|
19 |
+
private $honorResultWrapper;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @param Service $api Service description
|
23 |
+
* @param XmlParser $xmlParser Optional XML parser
|
24 |
+
* @param bool $honorResultWrapper Set to false to disable the peeling
|
25 |
+
* back of result wrappers from the
|
26 |
+
* output structure.
|
27 |
+
*/
|
28 |
+
public function __construct(
|
29 |
+
Service $api,
|
30 |
+
XmlParser $xmlParser = null,
|
31 |
+
$honorResultWrapper = true
|
32 |
+
) {
|
33 |
+
parent::__construct($api);
|
34 |
+
$this->parser = $xmlParser ?: new XmlParser();
|
35 |
+
$this->honorResultWrapper = $honorResultWrapper;
|
36 |
+
}
|
37 |
+
|
38 |
+
public function __invoke(
|
39 |
+
CommandInterface $command,
|
40 |
+
ResponseInterface $response
|
41 |
+
) {
|
42 |
+
$output = $this->api->getOperation($command->getName())->getOutput();
|
43 |
+
$xml = $this->parseXml($response->getBody(), $response);
|
44 |
+
|
45 |
+
if ($this->honorResultWrapper && $output['resultWrapper']) {
|
46 |
+
$xml = $xml->{$output['resultWrapper']};
|
47 |
+
}
|
48 |
+
|
49 |
+
return new Result($this->parser->parse($output, $xml));
|
50 |
+
}
|
51 |
+
|
52 |
+
public function parseMemberFromStream(
|
53 |
+
StreamInterface $stream,
|
54 |
+
StructureShape $member,
|
55 |
+
$response
|
56 |
+
) {
|
57 |
+
$xml = $this->parseXml($stream, $response);
|
58 |
+
return $this->parser->parse($member, $xml);
|
59 |
+
}
|
60 |
+
}
|
lib/Aws/Aws/Api/Parser/RestJsonParser.php
ADDED
@@ -0,0 +1,49 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api\Parser;
|
3 |
+
|
4 |
+
use Aws\Api\Service;
|
5 |
+
use Aws\Api\StructureShape;
|
6 |
+
use Psr\Http\Message\ResponseInterface;
|
7 |
+
use Psr\Http\Message\StreamInterface;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* @internal Implements REST-JSON parsing (e.g., Glacier, Elastic Transcoder)
|
11 |
+
*/
|
12 |
+
class RestJsonParser extends AbstractRestParser
|
13 |
+
{
|
14 |
+
use PayloadParserTrait;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @param Service $api Service description
|
18 |
+
* @param JsonParser $parser JSON body builder
|
19 |
+
*/
|
20 |
+
public function __construct(Service $api, JsonParser $parser = null)
|
21 |
+
{
|
22 |
+
parent::__construct($api);
|
23 |
+
$this->parser = $parser ?: new JsonParser();
|
24 |
+
}
|
25 |
+
|
26 |
+
protected function payload(
|
27 |
+
ResponseInterface $response,
|
28 |
+
StructureShape $member,
|
29 |
+
array &$result
|
30 |
+
) {
|
31 |
+
$jsonBody = $this->parseJson($response->getBody(), $response);
|
32 |
+
|
33 |
+
if ($jsonBody) {
|
34 |
+
$result += $this->parser->parse($member, $jsonBody);
|
35 |
+
}
|
36 |
+
}
|
37 |
+
|
38 |
+
public function parseMemberFromStream(
|
39 |
+
StreamInterface $stream,
|
40 |
+
StructureShape $member,
|
41 |
+
$response
|
42 |
+
) {
|
43 |
+
$jsonBody = $this->parseJson($stream, $response);
|
44 |
+
if ($jsonBody) {
|
45 |
+
return $this->parser->parse($member, $jsonBody);
|
46 |
+
}
|
47 |
+
return [];
|
48 |
+
}
|
49 |
+
}
|
lib/Aws/Aws/Api/Parser/RestXmlParser.php
ADDED
@@ -0,0 +1,42 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api\Parser;
|
3 |
+
|
4 |
+
use Aws\Api\StructureShape;
|
5 |
+
use Aws\Api\Service;
|
6 |
+
use Psr\Http\Message\ResponseInterface;
|
7 |
+
use Psr\Http\Message\StreamInterface;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* @internal Implements REST-XML parsing (e.g., S3, CloudFront, etc...)
|
11 |
+
*/
|
12 |
+
class RestXmlParser extends AbstractRestParser
|
13 |
+
{
|
14 |
+
use PayloadParserTrait;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @param Service $api Service description
|
18 |
+
* @param XmlParser $parser XML body parser
|
19 |
+
*/
|
20 |
+
public function __construct(Service $api, XmlParser $parser = null)
|
21 |
+
{
|
22 |
+
parent::__construct($api);
|
23 |
+
$this->parser = $parser ?: new XmlParser();
|
24 |
+
}
|
25 |
+
|
26 |
+
protected function payload(
|
27 |
+
ResponseInterface $response,
|
28 |
+
StructureShape $member,
|
29 |
+
array &$result
|
30 |
+
) {
|
31 |
+
$result += $this->parseMemberFromStream($response->getBody(), $member, $response);
|
32 |
+
}
|
33 |
+
|
34 |
+
public function parseMemberFromStream(
|
35 |
+
StreamInterface $stream,
|
36 |
+
StructureShape $member,
|
37 |
+
$response
|
38 |
+
) {
|
39 |
+
$xml = $this->parseXml($stream, $response);
|
40 |
+
return $this->parser->parse($member, $xml);
|
41 |
+
}
|
42 |
+
}
|
lib/Aws/Aws/Api/Parser/XmlParser.php
ADDED
@@ -0,0 +1,138 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api\Parser;
|
3 |
+
|
4 |
+
use Aws\Api\DateTimeResult;
|
5 |
+
use Aws\Api\ListShape;
|
6 |
+
use Aws\Api\MapShape;
|
7 |
+
use Aws\Api\Shape;
|
8 |
+
use Aws\Api\StructureShape;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* @internal Implements standard XML parsing for REST-XML and Query protocols.
|
12 |
+
*/
|
13 |
+
class XmlParser
|
14 |
+
{
|
15 |
+
public function parse(StructureShape $shape, \SimpleXMLElement $value)
|
16 |
+
{
|
17 |
+
return $this->dispatch($shape, $value);
|
18 |
+
}
|
19 |
+
|
20 |
+
private function dispatch($shape, \SimpleXMLElement $value)
|
21 |
+
{
|
22 |
+
static $methods = [
|
23 |
+
'structure' => 'parse_structure',
|
24 |
+
'list' => 'parse_list',
|
25 |
+
'map' => 'parse_map',
|
26 |
+
'blob' => 'parse_blob',
|
27 |
+
'boolean' => 'parse_boolean',
|
28 |
+
'integer' => 'parse_integer',
|
29 |
+
'float' => 'parse_float',
|
30 |
+
'double' => 'parse_float',
|
31 |
+
'timestamp' => 'parse_timestamp',
|
32 |
+
];
|
33 |
+
|
34 |
+
$type = $shape['type'];
|
35 |
+
if (isset($methods[$type])) {
|
36 |
+
return $this->{$methods[$type]}($shape, $value);
|
37 |
+
}
|
38 |
+
|
39 |
+
return (string) $value;
|
40 |
+
}
|
41 |
+
|
42 |
+
private function parse_structure(
|
43 |
+
StructureShape $shape,
|
44 |
+
\SimpleXMLElement $value
|
45 |
+
) {
|
46 |
+
$target = [];
|
47 |
+
|
48 |
+
foreach ($shape->getMembers() as $name => $member) {
|
49 |
+
// Extract the name of the XML node
|
50 |
+
$node = $this->memberKey($member, $name);
|
51 |
+
if (isset($value->{$node})) {
|
52 |
+
$target[$name] = $this->dispatch($member, $value->{$node});
|
53 |
+
}
|
54 |
+
}
|
55 |
+
|
56 |
+
return $target;
|
57 |
+
}
|
58 |
+
|
59 |
+
private function memberKey(Shape $shape, $name)
|
60 |
+
{
|
61 |
+
if (null !== $shape['locationName']) {
|
62 |
+
return $shape['locationName'];
|
63 |
+
}
|
64 |
+
|
65 |
+
if ($shape instanceof ListShape && $shape['flattened']) {
|
66 |
+
return $shape->getMember()['locationName'] ?: $name;
|
67 |
+
}
|
68 |
+
|
69 |
+
return $name;
|
70 |
+
}
|
71 |
+
|
72 |
+
private function parse_list(ListShape $shape, \SimpleXMLElement $value)
|
73 |
+
{
|
74 |
+
$target = [];
|
75 |
+
$member = $shape->getMember();
|
76 |
+
|
77 |
+
if (!$shape['flattened']) {
|
78 |
+
$value = $value->{$member['locationName'] ?: 'member'};
|
79 |
+
}
|
80 |
+
|
81 |
+
foreach ($value as $v) {
|
82 |
+
$target[] = $this->dispatch($member, $v);
|
83 |
+
}
|
84 |
+
|
85 |
+
return $target;
|
86 |
+
}
|
87 |
+
|
88 |
+
private function parse_map(MapShape $shape, \SimpleXMLElement $value)
|
89 |
+
{
|
90 |
+
$target = [];
|
91 |
+
|
92 |
+
if (!$shape['flattened']) {
|
93 |
+
$value = $value->entry;
|
94 |
+
}
|
95 |
+
|
96 |
+
$mapKey = $shape->getKey();
|
97 |
+
$mapValue = $shape->getValue();
|
98 |
+
$keyName = $shape->getKey()['locationName'] ?: 'key';
|
99 |
+
$valueName = $shape->getValue()['locationName'] ?: 'value';
|
100 |
+
|
101 |
+
foreach ($value as $node) {
|
102 |
+
$key = $this->dispatch($mapKey, $node->{$keyName});
|
103 |
+
$value = $this->dispatch($mapValue, $node->{$valueName});
|
104 |
+
$target[$key] = $value;
|
105 |
+
}
|
106 |
+
|
107 |
+
return $target;
|
108 |
+
}
|
109 |
+
|
110 |
+
private function parse_blob(Shape $shape, $value)
|
111 |
+
{
|
112 |
+
return base64_decode((string) $value);
|
113 |
+
}
|
114 |
+
|
115 |
+
private function parse_float(Shape $shape, $value)
|
116 |
+
{
|
117 |
+
return (float) (string) $value;
|
118 |
+
}
|
119 |
+
|
120 |
+
private function parse_integer(Shape $shape, $value)
|
121 |
+
{
|
122 |
+
return (int) (string) $value;
|
123 |
+
}
|
124 |
+
|
125 |
+
private function parse_boolean(Shape $shape, $value)
|
126 |
+
{
|
127 |
+
return $value == 'true';
|
128 |
+
}
|
129 |
+
|
130 |
+
private function parse_timestamp(Shape $shape, $value)
|
131 |
+
{
|
132 |
+
if (!empty($shape['timestampFormat'])
|
133 |
+
&& $shape['timestampFormat'] === 'unixTimestamp') {
|
134 |
+
return DateTimeResult::fromEpoch((string) $value);
|
135 |
+
}
|
136 |
+
return new DateTimeResult($value);
|
137 |
+
}
|
138 |
+
}
|
lib/Aws/Aws/Api/Serializer/Ec2ParamBuilder.php
ADDED
@@ -0,0 +1,40 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api\Serializer;
|
3 |
+
|
4 |
+
use Aws\Api\Shape;
|
5 |
+
use Aws\Api\ListShape;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* @internal
|
9 |
+
*/
|
10 |
+
class Ec2ParamBuilder extends QueryParamBuilder
|
11 |
+
{
|
12 |
+
protected function queryName(Shape $shape, $default = null)
|
13 |
+
{
|
14 |
+
return ($shape['queryName']
|
15 |
+
?: ucfirst($shape['locationName']))
|
16 |
+
?: $default;
|
17 |
+
}
|
18 |
+
|
19 |
+
protected function isFlat(Shape $shape)
|
20 |
+
{
|
21 |
+
return false;
|
22 |
+
}
|
23 |
+
|
24 |
+
protected function format_list(
|
25 |
+
ListShape $shape,
|
26 |
+
array $value,
|
27 |
+
$prefix,
|
28 |
+
&$query
|
29 |
+
) {
|
30 |
+
// Handle empty list serialization
|
31 |
+
if (!$value) {
|
32 |
+
$query[$prefix] = false;
|
33 |
+
} else {
|
34 |
+
$items = $shape->getMember();
|
35 |
+
foreach ($value as $k => $v) {
|
36 |
+
$this->format($items, $v, $prefix . '.' . ($k + 1), $query);
|
37 |
+
}
|
38 |
+
}
|
39 |
+
}
|
40 |
+
}
|
lib/Aws/Aws/Api/Serializer/JsonBody.php
ADDED
@@ -0,0 +1,96 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api\Serializer;
|
3 |
+
|
4 |
+
use Aws\Api\Service;
|
5 |
+
use Aws\Api\Shape;
|
6 |
+
use Aws\Api\TimestampShape;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Formats the JSON body of a JSON-REST or JSON-RPC operation.
|
10 |
+
* @internal
|
11 |
+
*/
|
12 |
+
class JsonBody
|
13 |
+
{
|
14 |
+
private $api;
|
15 |
+
|
16 |
+
public function __construct(Service $api)
|
17 |
+
{
|
18 |
+
$this->api = $api;
|
19 |
+
}
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Gets the JSON Content-Type header for a service API
|
23 |
+
*
|
24 |
+
* @param Service $service
|
25 |
+
*
|
26 |
+
* @return string
|
27 |
+
*/
|
28 |
+
public static function getContentType(Service $service)
|
29 |
+
{
|
30 |
+
return 'application/x-amz-json-'
|
31 |
+
. number_format($service->getMetadata('jsonVersion'), 1);
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Builds the JSON body based on an array of arguments.
|
36 |
+
*
|
37 |
+
* @param Shape $shape Operation being constructed
|
38 |
+
* @param array $args Associative array of arguments
|
39 |
+
*
|
40 |
+
* @return string
|
41 |
+
*/
|
42 |
+
public function build(Shape $shape, array $args)
|
43 |
+
{
|
44 |
+
$result = json_encode($this->format($shape, $args));
|
45 |
+
|
46 |
+
return $result == '[]' ? '{}' : $result;
|
47 |
+
}
|
48 |
+
|
49 |
+
private function format(Shape $shape, $value)
|
50 |
+
{
|
51 |
+
switch ($shape['type']) {
|
52 |
+
case 'structure':
|
53 |
+
$data = [];
|
54 |
+
foreach ($value as $k => $v) {
|
55 |
+
if ($v !== null && $shape->hasMember($k)) {
|
56 |
+
$valueShape = $shape->getMember($k);
|
57 |
+
$data[$valueShape['locationName'] ?: $k]
|
58 |
+
= $this->format($valueShape, $v);
|
59 |
+
}
|
60 |
+
}
|
61 |
+
if (empty($data)) {
|
62 |
+
return new \stdClass;
|
63 |
+
}
|
64 |
+
return $data;
|
65 |
+
|
66 |
+
case 'list':
|
67 |
+
$items = $shape->getMember();
|
68 |
+
foreach ($value as $k => $v) {
|
69 |
+
$value[$k] = $this->format($items, $v);
|
70 |
+
}
|
71 |
+
return $value;
|
72 |
+
|
73 |
+
case 'map':
|
74 |
+
if (empty($value)) {
|
75 |
+
return new \stdClass;
|
76 |
+
}
|
77 |
+
$values = $shape->getValue();
|
78 |
+
foreach ($value as $k => $v) {
|
79 |
+
$value[$k] = $this->format($values, $v);
|
80 |
+
}
|
81 |
+
return $value;
|
82 |
+
|
83 |
+
case 'blob':
|
84 |
+
return base64_encode($value);
|
85 |
+
|
86 |
+
case 'timestamp':
|
87 |
+
$timestampFormat = !empty($shape['timestampFormat'])
|
88 |
+
? $shape['timestampFormat']
|
89 |
+
: 'unixTimestamp';
|
90 |
+
return TimestampShape::format($value, $timestampFormat);
|
91 |
+
|
92 |
+
default:
|
93 |
+
return $value;
|
94 |
+
}
|
95 |
+
}
|
96 |
+
}
|
lib/Aws/Aws/Api/Serializer/JsonRpcSerializer.php
ADDED
@@ -0,0 +1,69 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api\Serializer;
|
3 |
+
|
4 |
+
use Aws\Api\Service;
|
5 |
+
use Aws\CommandInterface;
|
6 |
+
use GuzzleHttp\Psr7\Request;
|
7 |
+
use Psr\Http\Message\RequestInterface;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Prepares a JSON-RPC request for transfer.
|
11 |
+
* @internal
|
12 |
+
*/
|
13 |
+
class JsonRpcSerializer
|
14 |
+
{
|
15 |
+
/** @var JsonBody */
|
16 |
+
private $jsonFormatter;
|
17 |
+
|
18 |
+
/** @var string */
|
19 |
+
private $endpoint;
|
20 |
+
|
21 |
+
/** @var Service */
|
22 |
+
private $api;
|
23 |
+
|
24 |
+
/** @var string */
|
25 |
+
private $contentType;
|
26 |
+
|
27 |
+
/**
|
28 |
+
* @param Service $api Service description
|
29 |
+
* @param string $endpoint Endpoint to connect to
|
30 |
+
* @param JsonBody $jsonFormatter Optional JSON formatter to use
|
31 |
+
*/
|
32 |
+
public function __construct(
|
33 |
+
Service $api,
|
34 |
+
$endpoint,
|
35 |
+
JsonBody $jsonFormatter = null
|
36 |
+
) {
|
37 |
+
$this->endpoint = $endpoint;
|
38 |
+
$this->api = $api;
|
39 |
+
$this->jsonFormatter = $jsonFormatter ?: new JsonBody($this->api);
|
40 |
+
$this->contentType = JsonBody::getContentType($api);
|
41 |
+
}
|
42 |
+
|
43 |
+
/**
|
44 |
+
* When invoked with an AWS command, returns a serialization array
|
45 |
+
* containing "method", "uri", "headers", and "body" key value pairs.
|
46 |
+
*
|
47 |
+
* @param CommandInterface $command
|
48 |
+
*
|
49 |
+
* @return RequestInterface
|
50 |
+
*/
|
51 |
+
public function __invoke(CommandInterface $command)
|
52 |
+
{
|
53 |
+
$name = $command->getName();
|
54 |
+
$operation = $this->api->getOperation($name);
|
55 |
+
|
56 |
+
return new Request(
|
57 |
+
$operation['http']['method'],
|
58 |
+
$this->endpoint,
|
59 |
+
[
|
60 |
+
'X-Amz-Target' => $this->api->getMetadata('targetPrefix') . '.' . $name,
|
61 |
+
'Content-Type' => $this->contentType
|
62 |
+
],
|
63 |
+
$this->jsonFormatter->build(
|
64 |
+
$operation->getInput(),
|
65 |
+
$command->toArray()
|
66 |
+
)
|
67 |
+
);
|
68 |
+
}
|
69 |
+
}
|
lib/Aws/Aws/Api/Serializer/QueryParamBuilder.php
ADDED
@@ -0,0 +1,157 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api\Serializer;
|
3 |
+
|
4 |
+
use Aws\Api\StructureShape;
|
5 |
+
use Aws\Api\ListShape;
|
6 |
+
use Aws\Api\MapShape;
|
7 |
+
use Aws\Api\Shape;
|
8 |
+
use Aws\Api\TimestampShape;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* @internal
|
12 |
+
*/
|
13 |
+
class QueryParamBuilder
|
14 |
+
{
|
15 |
+
private $methods;
|
16 |
+
|
17 |
+
protected function queryName(Shape $shape, $default = null)
|
18 |
+
{
|
19 |
+
if (null !== $shape['queryName']) {
|
20 |
+
return $shape['queryName'];
|
21 |
+
}
|
22 |
+
|
23 |
+
if (null !== $shape['locationName']) {
|
24 |
+
return $shape['locationName'];
|
25 |
+
}
|
26 |
+
|
27 |
+
if ($this->isFlat($shape) && !empty($shape['member']['locationName'])) {
|
28 |
+
return $shape['member']['locationName'];
|
29 |
+
}
|
30 |
+
|
31 |
+
return $default;
|
32 |
+
}
|
33 |
+
|
34 |
+
protected function isFlat(Shape $shape)
|
35 |
+
{
|
36 |
+
return $shape['flattened'] === true;
|
37 |
+
}
|
38 |
+
|
39 |
+
public function __invoke(StructureShape $shape, array $params)
|
40 |
+
{
|
41 |
+
if (!$this->methods) {
|
42 |
+
$this->methods = array_fill_keys(get_class_methods($this), true);
|
43 |
+
}
|
44 |
+
|
45 |
+
$query = [];
|
46 |
+
$this->format_structure($shape, $params, '', $query);
|
47 |
+
|
48 |
+
return $query;
|
49 |
+
}
|
50 |
+
|
51 |
+
protected function format(Shape $shape, $value, $prefix, array &$query)
|
52 |
+
{
|
53 |
+
$type = 'format_' . $shape['type'];
|
54 |
+
if (isset($this->methods[$type])) {
|
55 |
+
$this->{$type}($shape, $value, $prefix, $query);
|
56 |
+
} else {
|
57 |
+
$query[$prefix] = (string) $value;
|
58 |
+
}
|
59 |
+
}
|
60 |
+
|
61 |
+
protected function format_structure(
|
62 |
+
StructureShape $shape,
|
63 |
+
array $value,
|
64 |
+
$prefix,
|
65 |
+
&$query
|
66 |
+
) {
|
67 |
+
if ($prefix) {
|
68 |
+
$prefix .= '.';
|
69 |
+
}
|
70 |
+
|
71 |
+
foreach ($value as $k => $v) {
|
72 |
+
if ($shape->hasMember($k)) {
|
73 |
+
$member = $shape->getMember($k);
|
74 |
+
$this->format(
|
75 |
+
$member,
|
76 |
+
$v,
|
77 |
+
$prefix . $this->queryName($member, $k),
|
78 |
+
$query
|
79 |
+
);
|
80 |
+
}
|
81 |
+
}
|
82 |
+
}
|
83 |
+
|
84 |
+
protected function format_list(
|
85 |
+
ListShape $shape,
|
86 |
+
array $value,
|
87 |
+
$prefix,
|
88 |
+
&$query
|
89 |
+
) {
|
90 |
+
// Handle empty list serialization
|
91 |
+
if (!$value) {
|
92 |
+
$query[$prefix] = '';
|
93 |
+
return;
|
94 |
+
}
|
95 |
+
|
96 |
+
$items = $shape->getMember();
|
97 |
+
|
98 |
+
if (!$this->isFlat($shape)) {
|
99 |
+
$locationName = $shape->getMember()['locationName'] ?: 'member';
|
100 |
+
$prefix .= ".$locationName";
|
101 |
+
} elseif ($name = $this->queryName($items)) {
|
102 |
+
$parts = explode('.', $prefix);
|
103 |
+
$parts[count($parts) - 1] = $name;
|
104 |
+
$prefix = implode('.', $parts);
|
105 |
+
}
|
106 |
+
|
107 |
+
foreach ($value as $k => $v) {
|
108 |
+
$this->format($items, $v, $prefix . '.' . ($k + 1), $query);
|
109 |
+
}
|
110 |
+
}
|
111 |
+
|
112 |
+
protected function format_map(
|
113 |
+
MapShape $shape,
|
114 |
+
array $value,
|
115 |
+
$prefix,
|
116 |
+
array &$query
|
117 |
+
) {
|
118 |
+
$vals = $shape->getValue();
|
119 |
+
$keys = $shape->getKey();
|
120 |
+
|
121 |
+
if (!$this->isFlat($shape)) {
|
122 |
+
$prefix .= '.entry';
|
123 |
+
}
|
124 |
+
|
125 |
+
$i = 0;
|
126 |
+
$keyName = '%s.%d.' . $this->queryName($keys, 'key');
|
127 |
+
$valueName = '%s.%s.' . $this->queryName($vals, 'value');
|
128 |
+
|
129 |
+
foreach ($value as $k => $v) {
|
130 |
+
$i++;
|
131 |
+
$this->format($keys, $k, sprintf($keyName, $prefix, $i), $query);
|
132 |
+
$this->format($vals, $v, sprintf($valueName, $prefix, $i), $query);
|
133 |
+
}
|
134 |
+
}
|
135 |
+
|
136 |
+
protected function format_blob(Shape $shape, $value, $prefix, array &$query)
|
137 |
+
{
|
138 |
+
$query[$prefix] = base64_encode($value);
|
139 |
+
}
|
140 |
+
|
141 |
+
protected function format_timestamp(
|
142 |
+
TimestampShape $shape,
|
143 |
+
$value,
|
144 |
+
$prefix,
|
145 |
+
array &$query
|
146 |
+
) {
|
147 |
+
$timestampFormat = !empty($shape['timestampFormat'])
|
148 |
+
? $shape['timestampFormat']
|
149 |
+
: 'iso8601';
|
150 |
+
$query[$prefix] = TimestampShape::format($value, $timestampFormat);
|
151 |
+
}
|
152 |
+
|
153 |
+
protected function format_boolean(Shape $shape, $value, $prefix, array &$query)
|
154 |
+
{
|
155 |
+
$query[$prefix] = ($value) ? 'true' : 'false';
|
156 |
+
}
|
157 |
+
}
|
lib/Aws/Aws/Api/Serializer/QuerySerializer.php
ADDED
@@ -0,0 +1,69 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api\Serializer;
|
3 |
+
|
4 |
+
use Aws\Api\Service;
|
5 |
+
use Aws\CommandInterface;
|
6 |
+
use GuzzleHttp\Psr7\Request;
|
7 |
+
use Psr\Http\Message\RequestInterface;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Serializes a query protocol request.
|
11 |
+
* @internal
|
12 |
+
*/
|
13 |
+
class QuerySerializer
|
14 |
+
{
|
15 |
+
private $endpoint;
|
16 |
+
private $api;
|
17 |
+
private $paramBuilder;
|
18 |
+
|
19 |
+
public function __construct(
|
20 |
+
Service $api,
|
21 |
+
$endpoint,
|
22 |
+
callable $paramBuilder = null
|
23 |
+
) {
|
24 |
+
$this->api = $api;
|
25 |
+
$this->endpoint = $endpoint;
|
26 |
+
$this->paramBuilder = $paramBuilder ?: new QueryParamBuilder();
|
27 |
+
}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* When invoked with an AWS command, returns a serialization array
|
31 |
+
* containing "method", "uri", "headers", and "body" key value pairs.
|
32 |
+
*
|
33 |
+
* @param CommandInterface $command
|
34 |
+
*
|
35 |
+
* @return RequestInterface
|
36 |
+
*/
|
37 |
+
public function __invoke(CommandInterface $command)
|
38 |
+
{
|
39 |
+
$operation = $this->api->getOperation($command->getName());
|
40 |
+
|
41 |
+
$body = [
|
42 |
+
'Action' => $command->getName(),
|
43 |
+
'Version' => $this->api->getMetadata('apiVersion')
|
44 |
+
];
|
45 |
+
|
46 |
+
$params = $command->toArray();
|
47 |
+
|
48 |
+
// Only build up the parameters when there are parameters to build
|
49 |
+
if ($params) {
|
50 |
+
$body += call_user_func(
|
51 |
+
$this->paramBuilder,
|
52 |
+
$operation->getInput(),
|
53 |
+
$params
|
54 |
+
);
|
55 |
+
}
|
56 |
+
|
57 |
+
$body = http_build_query($body, null, '&', PHP_QUERY_RFC3986);
|
58 |
+
|
59 |
+
return new Request(
|
60 |
+
'POST',
|
61 |
+
$this->endpoint,
|
62 |
+
[
|
63 |
+
'Content-Length' => strlen($body),
|
64 |
+
'Content-Type' => 'application/x-www-form-urlencoded'
|
65 |
+
],
|
66 |
+
$body
|
67 |
+
);
|
68 |
+
}
|
69 |
+
}
|
lib/Aws/Aws/Api/Serializer/RestJsonSerializer.php
ADDED
@@ -0,0 +1,39 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api\Serializer;
|
3 |
+
|
4 |
+
use Aws\Api\Service;
|
5 |
+
use Aws\Api\StructureShape;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Serializes requests for the REST-JSON protocol.
|
9 |
+
* @internal
|
10 |
+
*/
|
11 |
+
class RestJsonSerializer extends RestSerializer
|
12 |
+
{
|
13 |
+
/** @var JsonBody */
|
14 |
+
private $jsonFormatter;
|
15 |
+
|
16 |
+
/** @var string */
|
17 |
+
private $contentType;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* @param Service $api Service API description
|
21 |
+
* @param string $endpoint Endpoint to connect to
|
22 |
+
* @param JsonBody $jsonFormatter Optional JSON formatter to use
|
23 |
+
*/
|
24 |
+
public function __construct(
|
25 |
+
Service $api,
|
26 |
+
$endpoint,
|
27 |
+
JsonBody $jsonFormatter = null
|
28 |
+
) {
|
29 |
+
parent::__construct($api, $endpoint);
|
30 |
+
$this->contentType = 'application/json';
|
31 |
+
$this->jsonFormatter = $jsonFormatter ?: new JsonBody($api);
|
32 |
+
}
|
33 |
+
|
34 |
+
protected function payload(StructureShape $member, array $value, array &$opts)
|
35 |
+
{
|
36 |
+
$opts['headers']['Content-Type'] = $this->contentType;
|
37 |
+
$opts['body'] = (string) $this->jsonFormatter->build($member, $value);
|
38 |
+
}
|
39 |
+
}
|
lib/Aws/Aws/Api/Serializer/RestSerializer.php
ADDED
@@ -0,0 +1,219 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api\Serializer;
|
3 |
+
|
4 |
+
use Aws\Api\MapShape;
|
5 |
+
use Aws\Api\Service;
|
6 |
+
use Aws\Api\Operation;
|
7 |
+
use Aws\Api\Shape;
|
8 |
+
use Aws\Api\StructureShape;
|
9 |
+
use Aws\Api\TimestampShape;
|
10 |
+
use Aws\CommandInterface;
|
11 |
+
use GuzzleHttp\Psr7;
|
12 |
+
use GuzzleHttp\Psr7\Uri;
|
13 |
+
use GuzzleHttp\Psr7\UriResolver;
|
14 |
+
use Psr\Http\Message\RequestInterface;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Serializes HTTP locations like header, uri, payload, etc...
|
18 |
+
* @internal
|
19 |
+
*/
|
20 |
+
abstract class RestSerializer
|
21 |
+
{
|
22 |
+
/** @var Service */
|
23 |
+
private $api;
|
24 |
+
|
25 |
+
/** @var Psr7\Uri */
|
26 |
+
private $endpoint;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* @param Service $api Service API description
|
30 |
+
* @param string $endpoint Endpoint to connect to
|
31 |
+
*/
|
32 |
+
public function __construct(Service $api, $endpoint)
|
33 |
+
{
|
34 |
+
$this->api = $api;
|
35 |
+
$this->endpoint = Psr7\uri_for($endpoint);
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* @param CommandInterface $command Command to serialized
|
40 |
+
*
|
41 |
+
* @return RequestInterface
|
42 |
+
*/
|
43 |
+
public function __invoke(CommandInterface $command)
|
44 |
+
{
|
45 |
+
$operation = $this->api->getOperation($command->getName());
|
46 |
+
$args = $command->toArray();
|
47 |
+
$opts = $this->serialize($operation, $args);
|
48 |
+
$uri = $this->buildEndpoint($operation, $args, $opts);
|
49 |
+
|
50 |
+
return new Psr7\Request(
|
51 |
+
$operation['http']['method'],
|
52 |
+
$uri,
|
53 |
+
isset($opts['headers']) ? $opts['headers'] : [],
|
54 |
+
isset($opts['body']) ? $opts['body'] : null
|
55 |
+
);
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Modifies a hash of request options for a payload body.
|
60 |
+
*
|
61 |
+
* @param StructureShape $member Member to serialize
|
62 |
+
* @param array $value Value to serialize
|
63 |
+
* @param array $opts Request options to modify.
|
64 |
+
*/
|
65 |
+
abstract protected function payload(
|
66 |
+
StructureShape $member,
|
67 |
+
array $value,
|
68 |
+
array &$opts
|
69 |
+
);
|
70 |
+
|
71 |
+
private function serialize(Operation $operation, array $args)
|
72 |
+
{
|
73 |
+
$opts = [];
|
74 |
+
$input = $operation->getInput();
|
75 |
+
|
76 |
+
// Apply the payload trait if present
|
77 |
+
if ($payload = $input['payload']) {
|
78 |
+
$this->applyPayload($input, $payload, $args, $opts);
|
79 |
+
}
|
80 |
+
|
81 |
+
foreach ($args as $name => $value) {
|
82 |
+
if ($input->hasMember($name)) {
|
83 |
+
$member = $input->getMember($name);
|
84 |
+
$location = $member['location'];
|
85 |
+
if (!$payload && !$location) {
|
86 |
+
$bodyMembers[$name] = $value;
|
87 |
+
} elseif ($location == 'header') {
|
88 |
+
$this->applyHeader($name, $member, $value, $opts);
|
89 |
+
} elseif ($location == 'querystring') {
|
90 |
+
$this->applyQuery($name, $member, $value, $opts);
|
91 |
+
} elseif ($location == 'headers') {
|
92 |
+
$this->applyHeaderMap($name, $member, $value, $opts);
|
93 |
+
}
|
94 |
+
}
|
95 |
+
}
|
96 |
+
|
97 |
+
if (isset($bodyMembers)) {
|
98 |
+
$this->payload($operation->getInput(), $bodyMembers, $opts);
|
99 |
+
}
|
100 |
+
|
101 |
+
return $opts;
|
102 |
+
}
|
103 |
+
|
104 |
+
private function applyPayload(StructureShape $input, $name, array $args, array &$opts)
|
105 |
+
{
|
106 |
+
if (!isset($args[$name])) {
|
107 |
+
return;
|
108 |
+
}
|
109 |
+
|
110 |
+
$m = $input->getMember($name);
|
111 |
+
|
112 |
+
if ($m['streaming'] ||
|
113 |
+
($m['type'] == 'string' || $m['type'] == 'blob')
|
114 |
+
) {
|
115 |
+
// Streaming bodies or payloads that are strings are
|
116 |
+
// always just a stream of data.
|
117 |
+
$opts['body'] = Psr7\stream_for($args[$name]);
|
118 |
+
return;
|
119 |
+
}
|
120 |
+
|
121 |
+
$this->payload($m, $args[$name], $opts);
|
122 |
+
}
|
123 |
+
|
124 |
+
private function applyHeader($name, Shape $member, $value, array &$opts)
|
125 |
+
{
|
126 |
+
if ($member->getType() === 'timestamp') {
|
127 |
+
$timestampFormat = !empty($member['timestampFormat'])
|
128 |
+
? $member['timestampFormat']
|
129 |
+
: 'rfc822';
|
130 |
+
$value = TimestampShape::format($value, $timestampFormat);
|
131 |
+
}
|
132 |
+
if ($member['jsonvalue']) {
|
133 |
+
$value = json_encode($value);
|
134 |
+
if (empty($value) && JSON_ERROR_NONE !== json_last_error()) {
|
135 |
+
throw new \InvalidArgumentException('Unable to encode the provided value'
|
136 |
+
. ' with \'json_encode\'. ' . json_last_error_msg());
|
137 |
+
}
|
138 |
+
|
139 |
+
$value = base64_encode($value);
|
140 |
+
}
|
141 |
+
|
142 |
+
$opts['headers'][$member['locationName'] ?: $name] = $value;
|
143 |
+
}
|
144 |
+
|
145 |
+
/**
|
146 |
+
* Note: This is currently only present in the Amazon S3 model.
|
147 |
+
*/
|
148 |
+
private function applyHeaderMap($name, Shape $member, array $value, array &$opts)
|
149 |
+
{
|
150 |
+
$prefix = $member['locationName'];
|
151 |
+
foreach ($value as $k => $v) {
|
152 |
+
$opts['headers'][$prefix . $k] = $v;
|
153 |
+
}
|
154 |
+
}
|
155 |
+
|
156 |
+
private function applyQuery($name, Shape $member, $value, array &$opts)
|
157 |
+
{
|
158 |
+
if ($member instanceof MapShape) {
|
159 |
+
$opts['query'] = isset($opts['query']) && is_array($opts['query'])
|
160 |
+
? $opts['query'] + $value
|
161 |
+
: $value;
|
162 |
+
} elseif ($value !== null) {
|
163 |
+
$type = $member->getType();
|
164 |
+
if ($type === 'boolean') {
|
165 |
+
$value = $value ? 'true' : 'false';
|
166 |
+
} elseif ($type === 'timestamp') {
|
167 |
+
$timestampFormat = !empty($member['timestampFormat'])
|
168 |
+
? $member['timestampFormat']
|
169 |
+
: 'iso8601';
|
170 |
+
$value = TimestampShape::format($value, $timestampFormat);
|
171 |
+
}
|
172 |
+
|
173 |
+
$opts['query'][$member['locationName'] ?: $name] = $value;
|
174 |
+
}
|
175 |
+
}
|
176 |
+
|
177 |
+
private function buildEndpoint(Operation $operation, array $args, array $opts)
|
178 |
+
{
|
179 |
+
$varspecs = [];
|
180 |
+
|
181 |
+
// Create an associative array of varspecs used in expansions
|
182 |
+
foreach ($operation->getInput()->getMembers() as $name => $member) {
|
183 |
+
if ($member['location'] == 'uri') {
|
184 |
+
$varspecs[$member['locationName'] ?: $name] =
|
185 |
+
isset($args[$name])
|
186 |
+
? $args[$name]
|
187 |
+
: null;
|
188 |
+
}
|
189 |
+
}
|
190 |
+
|
191 |
+
$relative = preg_replace_callback(
|
192 |
+
'/\{([^\}]+)\}/',
|
193 |
+
function (array $matches) use ($varspecs) {
|
194 |
+
$isGreedy = substr($matches[1], -1, 1) == '+';
|
195 |
+
$k = $isGreedy ? substr($matches[1], 0, -1) : $matches[1];
|
196 |
+
if (!isset($varspecs[$k])) {
|
197 |
+
return '';
|
198 |
+
}
|
199 |
+
|
200 |
+
if ($isGreedy) {
|
201 |
+
return str_replace('%2F', '/', rawurlencode($varspecs[$k]));
|
202 |
+
}
|
203 |
+
|
204 |
+
return rawurlencode($varspecs[$k]);
|
205 |
+
},
|
206 |
+
$operation['http']['requestUri']
|
207 |
+
);
|
208 |
+
|
209 |
+
// Add the query string variables or appending to one if needed.
|
210 |
+
if (!empty($opts['query'])) {
|
211 |
+
$append = Psr7\build_query($opts['query']);
|
212 |
+
$relative .= strpos($relative, '?') ? "&{$append}" : "?$append";
|
213 |
+
}
|
214 |
+
|
215 |
+
// Expand path place holders using Amazon's slightly different URI
|
216 |
+
// template syntax.
|
217 |
+
return UriResolver::resolve($this->endpoint, new Uri($relative));
|
218 |
+
}
|
219 |
+
}
|
lib/Aws/Aws/Api/Serializer/RestXmlSerializer.php
ADDED
@@ -0,0 +1,34 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api\Serializer;
|
3 |
+
|
4 |
+
use Aws\Api\StructureShape;
|
5 |
+
use Aws\Api\Service;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* @internal
|
9 |
+
*/
|
10 |
+
class RestXmlSerializer extends RestSerializer
|
11 |
+
{
|
12 |
+
/** @var XmlBody */
|
13 |
+
private $xmlBody;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* @param Service $api Service API description
|
17 |
+
* @param string $endpoint Endpoint to connect to
|
18 |
+
* @param XmlBody $xmlBody Optional XML formatter to use
|
19 |
+
*/
|
20 |
+
public function __construct(
|
21 |
+
Service $api,
|
22 |
+
$endpoint,
|
23 |
+
XmlBody $xmlBody = null
|
24 |
+
) {
|
25 |
+
parent::__construct($api, $endpoint);
|
26 |
+
$this->xmlBody = $xmlBody ?: new XmlBody($api);
|
27 |
+
}
|
28 |
+
|
29 |
+
protected function payload(StructureShape $member, array $value, array &$opts)
|
30 |
+
{
|
31 |
+
$opts['headers']['Content-Type'] = 'application/xml';
|
32 |
+
$opts['body'] = (string) $this->xmlBody->build($member, $value);
|
33 |
+
}
|
34 |
+
}
|
lib/Aws/Aws/Api/Serializer/XmlBody.php
ADDED
@@ -0,0 +1,220 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api\Serializer;
|
3 |
+
|
4 |
+
use Aws\Api\MapShape;
|
5 |
+
use Aws\Api\Service;
|
6 |
+
use Aws\Api\Shape;
|
7 |
+
use Aws\Api\StructureShape;
|
8 |
+
use Aws\Api\ListShape;
|
9 |
+
use Aws\Api\TimestampShape;
|
10 |
+
use XMLWriter;
|
11 |
+
|
12 |
+
/**
|
13 |
+
* @internal Formats the XML body of a REST-XML services.
|
14 |
+
*/
|
15 |
+
class XmlBody
|
16 |
+
{
|
17 |
+
/** @var \Aws\Api\Service */
|
18 |
+
private $api;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* @param Service $api API being used to create the XML body.
|
22 |
+
*/
|
23 |
+
public function __construct(Service $api)
|
24 |
+
{
|
25 |
+
$this->api = $api;
|
26 |
+
}
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Builds the XML body based on an array of arguments.
|
30 |
+
*
|
31 |
+
* @param Shape $shape Operation being constructed
|
32 |
+
* @param array $args Associative array of arguments
|
33 |
+
*
|
34 |
+
* @return string
|
35 |
+
*/
|
36 |
+
public function build(Shape $shape, array $args)
|
37 |
+
{
|
38 |
+
$xml = new XMLWriter();
|
39 |
+
$xml->openMemory();
|
40 |
+
$xml->startDocument('1.0', 'UTF-8');
|
41 |
+
$this->format($shape, $shape['locationName'] ?: $shape['name'], $args, $xml);
|
42 |
+
$xml->endDocument();
|
43 |
+
|
44 |
+
return $xml->outputMemory();
|
45 |
+
}
|
46 |
+
|
47 |
+
private function startElement(Shape $shape, $name, XMLWriter $xml)
|
48 |
+
{
|
49 |
+
$xml->startElement($name);
|
50 |
+
|
51 |
+
if ($ns = $shape['xmlNamespace']) {
|
52 |
+
$xml->writeAttribute(
|
53 |
+
isset($ns['prefix']) ? "xmlns:{$ns['prefix']}" : 'xmlns',
|
54 |
+
$shape['xmlNamespace']['uri']
|
55 |
+
);
|
56 |
+
}
|
57 |
+
}
|
58 |
+
|
59 |
+
private function format(Shape $shape, $name, $value, XMLWriter $xml)
|
60 |
+
{
|
61 |
+
// Any method mentioned here has a custom serialization handler.
|
62 |
+
static $methods = [
|
63 |
+
'add_structure' => true,
|
64 |
+
'add_list' => true,
|
65 |
+
'add_blob' => true,
|
66 |
+
'add_timestamp' => true,
|
67 |
+
'add_boolean' => true,
|
68 |
+
'add_map' => true,
|
69 |
+
'add_string' => true
|
70 |
+
];
|
71 |
+
|
72 |
+
$type = 'add_' . $shape['type'];
|
73 |
+
if (isset($methods[$type])) {
|
74 |
+
$this->{$type}($shape, $name, $value, $xml);
|
75 |
+
} else {
|
76 |
+
$this->defaultShape($shape, $name, $value, $xml);
|
77 |
+
}
|
78 |
+
}
|
79 |
+
|
80 |
+
private function defaultShape(Shape $shape, $name, $value, XMLWriter $xml)
|
81 |
+
{
|
82 |
+
$this->startElement($shape, $name, $xml);
|
83 |
+
$xml->writeRaw($value);
|
84 |
+
$xml->endElement();
|
85 |
+
}
|
86 |
+
|
87 |
+
private function add_structure(
|
88 |
+
StructureShape $shape,
|
89 |
+
$name,
|
90 |
+
array $value,
|
91 |
+
\XMLWriter $xml
|
92 |
+
) {
|
93 |
+
$this->startElement($shape, $name, $xml);
|
94 |
+
|
95 |
+
foreach ($this->getStructureMembers($shape, $value) as $k => $definition) {
|
96 |
+
$this->format(
|
97 |
+
$definition['member'],
|
98 |
+
$definition['member']['locationName'] ?: $k,
|
99 |
+
$definition['value'],
|
100 |
+
$xml
|
101 |
+
);
|
102 |
+
}
|
103 |
+
|
104 |
+
$xml->endElement();
|
105 |
+
}
|
106 |
+
|
107 |
+
private function getStructureMembers(StructureShape $shape, array $value)
|
108 |
+
{
|
109 |
+
$members = [];
|
110 |
+
|
111 |
+
foreach ($value as $k => $v) {
|
112 |
+
if ($v !== null && $shape->hasMember($k)) {
|
113 |
+
$definition = [
|
114 |
+
'member' => $shape->getMember($k),
|
115 |
+
'value' => $v,
|
116 |
+
];
|
117 |
+
|
118 |
+
if ($definition['member']['xmlAttribute']) {
|
119 |
+
// array_unshift_associative
|
120 |
+
$members = [$k => $definition] + $members;
|
121 |
+
} else {
|
122 |
+
$members[$k] = $definition;
|
123 |
+
}
|
124 |
+
}
|
125 |
+
}
|
126 |
+
|
127 |
+
return $members;
|
128 |
+
}
|
129 |
+
|
130 |
+
private function add_list(
|
131 |
+
ListShape $shape,
|
132 |
+
$name,
|
133 |
+
array $value,
|
134 |
+
XMLWriter $xml
|
135 |
+
) {
|
136 |
+
$items = $shape->getMember();
|
137 |
+
|
138 |
+
if ($shape['flattened']) {
|
139 |
+
$elementName = $name;
|
140 |
+
} else {
|
141 |
+
$this->startElement($shape, $name, $xml);
|
142 |
+
$elementName = $items['locationName'] ?: 'member';
|
143 |
+
}
|
144 |
+
|
145 |
+
foreach ($value as $v) {
|
146 |
+
$this->format($items, $elementName, $v, $xml);
|
147 |
+
}
|
148 |
+
|
149 |
+
if (!$shape['flattened']) {
|
150 |
+
$xml->endElement();
|
151 |
+
}
|
152 |
+
}
|
153 |
+
|
154 |
+
private function add_map(
|
155 |
+
MapShape $shape,
|
156 |
+
$name,
|
157 |
+
array $value,
|
158 |
+
XMLWriter $xml
|
159 |
+
) {
|
160 |
+
$xmlEntry = $shape['flattened'] ? $shape['locationName'] : 'entry';
|
161 |
+
$xmlKey = $shape->getKey()['locationName'] ?: 'key';
|
162 |
+
$xmlValue = $shape->getValue()['locationName'] ?: 'value';
|
163 |
+
|
164 |
+
$this->startElement($shape, $name, $xml);
|
165 |
+
|
166 |
+
foreach ($value as $key => $v) {
|
167 |
+
$this->startElement($shape, $xmlEntry, $xml);
|
168 |
+
$this->format($shape->getKey(), $xmlKey, $key, $xml);
|
169 |
+
$this->format($shape->getValue(), $xmlValue, $v, $xml);
|
170 |
+
$xml->endElement();
|
171 |
+
}
|
172 |
+
|
173 |
+
$xml->endElement();
|
174 |
+
}
|
175 |
+
|
176 |
+
private function add_blob(Shape $shape, $name, $value, XMLWriter $xml)
|
177 |
+
{
|
178 |
+
$this->startElement($shape, $name, $xml);
|
179 |
+
$xml->writeRaw(base64_encode($value));
|
180 |
+
$xml->endElement();
|
181 |
+
}
|
182 |
+
|
183 |
+
private function add_timestamp(
|
184 |
+
TimestampShape $shape,
|
185 |
+
$name,
|
186 |
+
$value,
|
187 |
+
XMLWriter $xml
|
188 |
+
) {
|
189 |
+
$this->startElement($shape, $name, $xml);
|
190 |
+
$timestampFormat = !empty($shape['timestampFormat'])
|
191 |
+
? $shape['timestampFormat']
|
192 |
+
: 'iso8601';
|
193 |
+
$xml->writeRaw(TimestampShape::format($value, $timestampFormat));
|
194 |
+
$xml->endElement();
|
195 |
+
}
|
196 |
+
|
197 |
+
private function add_boolean(
|
198 |
+
Shape $shape,
|
199 |
+
$name,
|
200 |
+
$value,
|
201 |
+
XMLWriter $xml
|
202 |
+
) {
|
203 |
+
$this->startElement($shape, $name, $xml);
|
204 |
+
$xml->writeRaw($value ? 'true' : 'false');
|
205 |
+
$xml->endElement();
|
206 |
+
}
|
207 |
+
|
208 |
+
private function add_string(
|
209 |
+
Shape $shape,
|
210 |
+
$name,
|
211 |
+
$value,
|
212 |
+
XMLWriter $xml
|
213 |
+
) {
|
214 |
+
if ($shape['xmlAttribute']) {
|
215 |
+
$xml->writeAttribute($shape['locationName'] ?: $name, $value);
|
216 |
+
} else {
|
217 |
+
$this->defaultShape($shape, $name, $value, $xml);
|
218 |
+
}
|
219 |
+
}
|
220 |
+
}
|
lib/Aws/Aws/Api/Service.php
ADDED
@@ -0,0 +1,448 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api;
|
3 |
+
|
4 |
+
use Aws\Api\Serializer\QuerySerializer;
|
5 |
+
use Aws\Api\Serializer\Ec2ParamBuilder;
|
6 |
+
use Aws\Api\Parser\QueryParser;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Represents a web service API model.
|
10 |
+
*/
|
11 |
+
class Service extends AbstractModel
|
12 |
+
{
|
13 |
+
/** @var callable */
|
14 |
+
private $apiProvider;
|
15 |
+
|
16 |
+
/** @var string */
|
17 |
+
private $serviceName;
|
18 |
+
|
19 |
+
/** @var string */
|
20 |
+
private $apiVersion;
|
21 |
+
|
22 |
+
/** @var Operation[] */
|
23 |
+
private $operations = [];
|
24 |
+
|
25 |
+
/** @var array */
|
26 |
+
private $paginators = null;
|
27 |
+
|
28 |
+
/** @var array */
|
29 |
+
private $waiters = null;
|
30 |
+
|
31 |
+
/**
|
32 |
+
* @param array $definition
|
33 |
+
* @param callable $provider
|
34 |
+
*
|
35 |
+
* @internal param array $definition Service description
|
36 |
+
*/
|
37 |
+
public function __construct(array $definition, callable $provider)
|
38 |
+
{
|
39 |
+
static $defaults = [
|
40 |
+
'operations' => [],
|
41 |
+
'shapes' => [],
|
42 |
+
'metadata' => []
|
43 |
+
], $defaultMeta = [
|
44 |
+
'apiVersion' => null,
|
45 |
+
'serviceFullName' => null,
|
46 |
+
'serviceId' => null,
|
47 |
+
'endpointPrefix' => null,
|
48 |
+
'signingName' => null,
|
49 |
+
'signatureVersion' => null,
|
50 |
+
'protocol' => null,
|
51 |
+
'uid' => null
|
52 |
+
];
|
53 |
+
|
54 |
+
$definition += $defaults;
|
55 |
+
$definition['metadata'] += $defaultMeta;
|
56 |
+
$this->definition = $definition;
|
57 |
+
$this->apiProvider = $provider;
|
58 |
+
parent::__construct($definition, new ShapeMap($definition['shapes']));
|
59 |
+
|
60 |
+
if (isset($definition['metadata']['serviceIdentifier'])) {
|
61 |
+
$this->serviceName = $this->getServiceName();
|
62 |
+
} else {
|
63 |
+
$this->serviceName = $this->getEndpointPrefix();
|
64 |
+
}
|
65 |
+
|
66 |
+
$this->apiVersion = $this->getApiVersion();
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
* Creates a request serializer for the provided API object.
|
71 |
+
*
|
72 |
+
* @param Service $api API that contains a protocol.
|
73 |
+
* @param string $endpoint Endpoint to send requests to.
|
74 |
+
*
|
75 |
+
* @return callable
|
76 |
+
* @throws \UnexpectedValueException
|
77 |
+
*/
|
78 |
+
public static function createSerializer(Service $api, $endpoint)
|
79 |
+
{
|
80 |
+
static $mapping = [
|
81 |
+
'json' => 'Aws\Api\Serializer\JsonRpcSerializer',
|
82 |
+
'query' => 'Aws\Api\Serializer\QuerySerializer',
|
83 |
+
'rest-json' => 'Aws\Api\Serializer\RestJsonSerializer',
|
84 |
+
'rest-xml' => 'Aws\Api\Serializer\RestXmlSerializer'
|
85 |
+
];
|
86 |
+
|
87 |
+
$proto = $api->getProtocol();
|
88 |
+
|
89 |
+
if (isset($mapping[$proto])) {
|
90 |
+
return new $mapping[$proto]($api, $endpoint);
|
91 |
+
}
|
92 |
+
|
93 |
+
if ($proto == 'ec2') {
|
94 |
+
return new QuerySerializer($api, $endpoint, new Ec2ParamBuilder());
|
95 |
+
}
|
96 |
+
|
97 |
+
throw new \UnexpectedValueException(
|
98 |
+
'Unknown protocol: ' . $api->getProtocol()
|
99 |
+
);
|
100 |
+
}
|
101 |
+
|
102 |
+
/**
|
103 |
+
* Creates an error parser for the given protocol.
|
104 |
+
*
|
105 |
+
* @param string $protocol Protocol to parse (e.g., query, json, etc.)
|
106 |
+
*
|
107 |
+
* @return callable
|
108 |
+
* @throws \UnexpectedValueException
|
109 |
+
*/
|
110 |
+
public static function createErrorParser($protocol)
|
111 |
+
{
|
112 |
+
static $mapping = [
|
113 |
+
'json' => 'Aws\Api\ErrorParser\JsonRpcErrorParser',
|
114 |
+
'query' => 'Aws\Api\ErrorParser\XmlErrorParser',
|
115 |
+
'rest-json' => 'Aws\Api\ErrorParser\RestJsonErrorParser',
|
116 |
+
'rest-xml' => 'Aws\Api\ErrorParser\XmlErrorParser',
|
117 |
+
'ec2' => 'Aws\Api\ErrorParser\XmlErrorParser'
|
118 |
+
];
|
119 |
+
|
120 |
+
if (isset($mapping[$protocol])) {
|
121 |
+
return new $mapping[$protocol]();
|
122 |
+
}
|
123 |
+
|
124 |
+
throw new \UnexpectedValueException("Unknown protocol: $protocol");
|
125 |
+
}
|
126 |
+
|
127 |
+
/**
|
128 |
+
* Applies the listeners needed to parse client models.
|
129 |
+
*
|
130 |
+
* @param Service $api API to create a parser for
|
131 |
+
* @return callable
|
132 |
+
* @throws \UnexpectedValueException
|
133 |
+
*/
|
134 |
+
public static function createParser(Service $api)
|
135 |
+
{
|
136 |
+
static $mapping = [
|
137 |
+
'json' => 'Aws\Api\Parser\JsonRpcParser',
|
138 |
+
'query' => 'Aws\Api\Parser\QueryParser',
|
139 |
+
'rest-json' => 'Aws\Api\Parser\RestJsonParser',
|
140 |
+
'rest-xml' => 'Aws\Api\Parser\RestXmlParser'
|
141 |
+
];
|
142 |
+
|
143 |
+
$proto = $api->getProtocol();
|
144 |
+
if (isset($mapping[$proto])) {
|
145 |
+
return new $mapping[$proto]($api);
|
146 |
+
}
|
147 |
+
|
148 |
+
if ($proto == 'ec2') {
|
149 |
+
return new QueryParser($api, null, false);
|
150 |
+
}
|
151 |
+
|
152 |
+
throw new \UnexpectedValueException(
|
153 |
+
'Unknown protocol: ' . $api->getProtocol()
|
154 |
+
);
|
155 |
+
}
|
156 |
+
|
157 |
+
/**
|
158 |
+
* Get the full name of the service
|
159 |
+
*
|
160 |
+
* @return string
|
161 |
+
*/
|
162 |
+
public function getServiceFullName()
|
163 |
+
{
|
164 |
+
return $this->definition['metadata']['serviceFullName'];
|
165 |
+
}
|
166 |
+
|
167 |
+
/**
|
168 |
+
* Get the service id
|
169 |
+
*
|
170 |
+
* @return string
|
171 |
+
*/
|
172 |
+
public function getServiceId()
|
173 |
+
{
|
174 |
+
return $this->definition['metadata']['serviceId'];
|
175 |
+
}
|
176 |
+
|
177 |
+
/**
|
178 |
+
* Get the API version of the service
|
179 |
+
*
|
180 |
+
* @return string
|
181 |
+
*/
|
182 |
+
public function getApiVersion()
|
183 |
+
{
|
184 |
+
return $this->definition['metadata']['apiVersion'];
|
185 |
+
}
|
186 |
+
|
187 |
+
/**
|
188 |
+
* Get the API version of the service
|
189 |
+
*
|
190 |
+
* @return string
|
191 |
+
*/
|
192 |
+
public function getEndpointPrefix()
|
193 |
+
{
|
194 |
+
return $this->definition['metadata']['endpointPrefix'];
|
195 |
+
}
|
196 |
+
|
197 |
+
/**
|
198 |
+
* Get the signing name used by the service.
|
199 |
+
*
|
200 |
+
* @return string
|
201 |
+
*/
|
202 |
+
public function getSigningName()
|
203 |
+
{
|
204 |
+
return $this->definition['metadata']['signingName']
|
205 |
+
?: $this->definition['metadata']['endpointPrefix'];
|
206 |
+
}
|
207 |
+
|
208 |
+
/**
|
209 |
+
* Get the service name.
|
210 |
+
*
|
211 |
+
* @return string
|
212 |
+
*/
|
213 |
+
public function getServiceName()
|
214 |
+
{
|
215 |
+
return $this->definition['metadata']['serviceIdentifier'];
|
216 |
+
}
|
217 |
+
|
218 |
+
/**
|
219 |
+
* Get the default signature version of the service.
|
220 |
+
*
|
221 |
+
* Note: this method assumes "v4" when not specified in the model.
|
222 |
+
*
|
223 |
+
* @return string
|
224 |
+
*/
|
225 |
+
public function getSignatureVersion()
|
226 |
+
{
|
227 |
+
return $this->definition['metadata']['signatureVersion'] ?: 'v4';
|
228 |
+
}
|
229 |
+
|
230 |
+
/**
|
231 |
+
* Get the protocol used by the service.
|
232 |
+
*
|
233 |
+
* @return string
|
234 |
+
*/
|
235 |
+
public function getProtocol()
|
236 |
+
{
|
237 |
+
return $this->definition['metadata']['protocol'];
|
238 |
+
}
|
239 |
+
|
240 |
+
/**
|
241 |
+
* Get the uid string used by the service
|
242 |
+
*
|
243 |
+
* @return string
|
244 |
+
*/
|
245 |
+
public function getUid()
|
246 |
+
{
|
247 |
+
return $this->definition['metadata']['uid'];
|
248 |
+
}
|
249 |
+
|
250 |
+
/**
|
251 |
+
* Check if the description has a specific operation by name.
|
252 |
+
*
|
253 |
+
* @param string $name Operation to check by name
|
254 |
+
*
|
255 |
+
* @return bool
|
256 |
+
*/
|
257 |
+
public function hasOperation($name)
|
258 |
+
{
|
259 |
+
return isset($this['operations'][$name]);
|
260 |
+
}
|
261 |
+
|
262 |
+
/**
|
263 |
+
* Get an operation by name.
|
264 |
+
*
|
265 |
+
* @param string $name Operation to retrieve by name
|
266 |
+
*
|
267 |
+
* @return Operation
|
268 |
+
* @throws \InvalidArgumentException If the operation is not found
|
269 |
+
*/
|
270 |
+
public function getOperation($name)
|
271 |
+
{
|
272 |
+
if (!isset($this->operations[$name])) {
|
273 |
+
if (!isset($this->definition['operations'][$name])) {
|
274 |
+
throw new \InvalidArgumentException("Unknown operation: $name");
|
275 |
+
}
|
276 |
+
$this->operations[$name] = new Operation(
|
277 |
+
$this->definition['operations'][$name],
|
278 |
+
$this->shapeMap
|
279 |
+
);
|
280 |
+
}
|
281 |
+
|
282 |
+
return $this->operations[$name];
|
283 |
+
}
|
284 |
+
|
285 |
+
/**
|
286 |
+
* Get all of the operations of the description.
|
287 |
+
*
|
288 |
+
* @return Operation[]
|
289 |
+
*/
|
290 |
+
public function getOperations()
|
291 |
+
{
|
292 |
+
$result = [];
|
293 |
+
foreach ($this->definition['operations'] as $name => $definition) {
|
294 |
+
$result[$name] = $this->getOperation($name);
|
295 |
+
}
|
296 |
+
|
297 |
+
return $result;
|
298 |
+
}
|
299 |
+
|
300 |
+
/**
|
301 |
+
* Get all of the service metadata or a specific metadata key value.
|
302 |
+
*
|
303 |
+
* @param string|null $key Key to retrieve or null to retrieve all metadata
|
304 |
+
*
|
305 |
+
* @return mixed Returns the result or null if the key is not found
|
306 |
+
*/
|
307 |
+
public function getMetadata($key = null)
|
308 |
+
{
|
309 |
+
if (!$key) {
|
310 |
+
return $this['metadata'];
|
311 |
+
}
|
312 |
+
|
313 |
+
if (isset($this->definition['metadata'][$key])) {
|
314 |
+
return $this->definition['metadata'][$key];
|
315 |
+
}
|
316 |
+
|
317 |
+
return null;
|
318 |
+
}
|
319 |
+
|
320 |
+
/**
|
321 |
+
* Gets an associative array of available paginator configurations where
|
322 |
+
* the key is the name of the paginator, and the value is the paginator
|
323 |
+
* configuration.
|
324 |
+
*
|
325 |
+
* @return array
|
326 |
+
* @unstable The configuration format of paginators may change in the future
|
327 |
+
*/
|
328 |
+
public function getPaginators()
|
329 |
+
{
|
330 |
+
if (!isset($this->paginators)) {
|
331 |
+
$res = call_user_func(
|
332 |
+
$this->apiProvider,
|
333 |
+
'paginator',
|
334 |
+
$this->serviceName,
|
335 |
+
$this->apiVersion
|
336 |
+
);
|
337 |
+
$this->paginators = isset($res['pagination'])
|
338 |
+
? $res['pagination']
|
339 |
+
: [];
|
340 |
+
}
|
341 |
+
|
342 |
+
return $this->paginators;
|
343 |
+
}
|
344 |
+
|
345 |
+
/**
|
346 |
+
* Determines if the service has a paginator by name.
|
347 |
+
*
|
348 |
+
* @param string $name Name of the paginator.
|
349 |
+
*
|
350 |
+
* @return bool
|
351 |
+
*/
|
352 |
+
public function hasPaginator($name)
|
353 |
+
{
|
354 |
+
return isset($this->getPaginators()[$name]);
|
355 |
+
}
|
356 |
+
|
357 |
+
/**
|
358 |
+
* Retrieve a paginator by name.
|
359 |
+
*
|
360 |
+
* @param string $name Paginator to retrieve by name. This argument is
|
361 |
+
* typically the operation name.
|
362 |
+
* @return array
|
363 |
+
* @throws \UnexpectedValueException if the paginator does not exist.
|
364 |
+
* @unstable The configuration format of paginators may change in the future
|
365 |
+
*/
|
366 |
+
public function getPaginatorConfig($name)
|
367 |
+
{
|
368 |
+
static $defaults = [
|
369 |
+
'input_token' => null,
|
370 |
+
'output_token' => null,
|
371 |
+
'limit_key' => null,
|
372 |
+
'result_key' => null,
|
373 |
+
'more_results' => null,
|
374 |
+
];
|
375 |
+
|
376 |
+
if ($this->hasPaginator($name)) {
|
377 |
+
return $this->paginators[$name] + $defaults;
|
378 |
+
}
|
379 |
+
|
380 |
+
throw new \UnexpectedValueException("There is no {$name} "
|
381 |
+
. "paginator defined for the {$this->serviceName} service.");
|
382 |
+
}
|
383 |
+
|
384 |
+
/**
|
385 |
+
* Gets an associative array of available waiter configurations where the
|
386 |
+
* key is the name of the waiter, and the value is the waiter
|
387 |
+
* configuration.
|
388 |
+
*
|
389 |
+
* @return array
|
390 |
+
*/
|
391 |
+
public function getWaiters()
|
392 |
+
{
|
393 |
+
if (!isset($this->waiters)) {
|
394 |
+
$res = call_user_func(
|
395 |
+
$this->apiProvider,
|
396 |
+
'waiter',
|
397 |
+
$this->serviceName,
|
398 |
+
$this->apiVersion
|
399 |
+
);
|
400 |
+
$this->waiters = isset($res['waiters'])
|
401 |
+
? $res['waiters']
|
402 |
+
: [];
|
403 |
+
}
|
404 |
+
|
405 |
+
return $this->waiters;
|
406 |
+
}
|
407 |
+
|
408 |
+
/**
|
409 |
+
* Determines if the service has a waiter by name.
|
410 |
+
*
|
411 |
+
* @param string $name Name of the waiter.
|
412 |
+
*
|
413 |
+
* @return bool
|
414 |
+
*/
|
415 |
+
public function hasWaiter($name)
|
416 |
+
{
|
417 |
+
return isset($this->getWaiters()[$name]);
|
418 |
+
}
|
419 |
+
|
420 |
+
/**
|
421 |
+
* Get a waiter configuration by name.
|
422 |
+
*
|
423 |
+
* @param string $name Name of the waiter by name.
|
424 |
+
*
|
425 |
+
* @return array
|
426 |
+
* @throws \UnexpectedValueException if the waiter does not exist.
|
427 |
+
*/
|
428 |
+
public function getWaiterConfig($name)
|
429 |
+
{
|
430 |
+
// Error if the waiter is not defined
|
431 |
+
if ($this->hasWaiter($name)) {
|
432 |
+
return $this->waiters[$name];
|
433 |
+
}
|
434 |
+
|
435 |
+
throw new \UnexpectedValueException("There is no {$name} waiter "
|
436 |
+
. "defined for the {$this->serviceName} service.");
|
437 |
+
}
|
438 |
+
|
439 |
+
/**
|
440 |
+
* Get the shape map used by the API.
|
441 |
+
*
|
442 |
+
* @return ShapeMap
|
443 |
+
*/
|
444 |
+
public function getShapeMap()
|
445 |
+
{
|
446 |
+
return $this->shapeMap;
|
447 |
+
}
|
448 |
+
}
|
lib/Aws/Aws/Api/Shape.php
ADDED
@@ -0,0 +1,69 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Base class representing a modeled shape.
|
6 |
+
*/
|
7 |
+
class Shape extends AbstractModel
|
8 |
+
{
|
9 |
+
/**
|
10 |
+
* Get a concrete shape for the given definition.
|
11 |
+
*
|
12 |
+
* @param array $definition
|
13 |
+
* @param ShapeMap $shapeMap
|
14 |
+
*
|
15 |
+
* @return mixed
|
16 |
+
* @throws \RuntimeException if the type is invalid
|
17 |
+
*/
|
18 |
+
public static function create(array $definition, ShapeMap $shapeMap)
|
19 |
+
{
|
20 |
+
static $map = [
|
21 |
+
'structure' => 'Aws\Api\StructureShape',
|
22 |
+
'map' => 'Aws\Api\MapShape',
|
23 |
+
'list' => 'Aws\Api\ListShape',
|
24 |
+
'timestamp' => 'Aws\Api\TimestampShape',
|
25 |
+
'integer' => 'Aws\Api\Shape',
|
26 |
+
'double' => 'Aws\Api\Shape',
|
27 |
+
'float' => 'Aws\Api\Shape',
|
28 |
+
'long' => 'Aws\Api\Shape',
|
29 |
+
'string' => 'Aws\Api\Shape',
|
30 |
+
'byte' => 'Aws\Api\Shape',
|
31 |
+
'character' => 'Aws\Api\Shape',
|
32 |
+
'blob' => 'Aws\Api\Shape',
|
33 |
+
'boolean' => 'Aws\Api\Shape'
|
34 |
+
];
|
35 |
+
|
36 |
+
if (isset($definition['shape'])) {
|
37 |
+
return $shapeMap->resolve($definition);
|
38 |
+
}
|
39 |
+
|
40 |
+
if (!isset($map[$definition['type']])) {
|
41 |
+
throw new \RuntimeException('Invalid type: '
|
42 |
+
. print_r($definition, true));
|
43 |
+
}
|
44 |
+
|
45 |
+
$type = $map[$definition['type']];
|
46 |
+
|
47 |
+
return new $type($definition, $shapeMap);
|
48 |
+
}
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Get the type of the shape
|
52 |
+
*
|
53 |
+
* @return string
|
54 |
+
*/
|
55 |
+
public function getType()
|
56 |
+
{
|
57 |
+
return $this->definition['type'];
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Get the name of the shape
|
62 |
+
*
|
63 |
+
* @return string
|
64 |
+
*/
|
65 |
+
public function getName()
|
66 |
+
{
|
67 |
+
return $this->definition['name'];
|
68 |
+
}
|
69 |
+
}
|
lib/Aws/Aws/Api/ShapeMap.php
ADDED
@@ -0,0 +1,66 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Builds shape based on shape references.
|
6 |
+
*/
|
7 |
+
class ShapeMap
|
8 |
+
{
|
9 |
+
/** @var array */
|
10 |
+
private $definitions;
|
11 |
+
|
12 |
+
/** @var Shape[] */
|
13 |
+
private $simple;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* @param array $shapeModels Associative array of shape definitions.
|
17 |
+
*/
|
18 |
+
public function __construct(array $shapeModels)
|
19 |
+
{
|
20 |
+
$this->definitions = $shapeModels;
|
21 |
+
}
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Get an array of shape names.
|
25 |
+
*
|
26 |
+
* @return array
|
27 |
+
*/
|
28 |
+
public function getShapeNames()
|
29 |
+
{
|
30 |
+
return array_keys($this->definitions);
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Resolve a shape reference
|
35 |
+
*
|
36 |
+
* @param array $shapeRef Shape reference shape
|
37 |
+
*
|
38 |
+
* @return Shape
|
39 |
+
* @throws \InvalidArgumentException
|
40 |
+
*/
|
41 |
+
public function resolve(array $shapeRef)
|
42 |
+
{
|
43 |
+
$shape = $shapeRef['shape'];
|
44 |
+
|
45 |
+
if (!isset($this->definitions[$shape])) {
|
46 |
+
throw new \InvalidArgumentException('Shape not found: ' . $shape);
|
47 |
+
}
|
48 |
+
|
49 |
+
$isSimple = count($shapeRef) == 1;
|
50 |
+
if ($isSimple && isset($this->simple[$shape])) {
|
51 |
+
return $this->simple[$shape];
|
52 |
+
}
|
53 |
+
|
54 |
+
$definition = $shapeRef + $this->definitions[$shape];
|
55 |
+
$definition['name'] = $definition['shape'];
|
56 |
+
unset($definition['shape']);
|
57 |
+
|
58 |
+
$result = Shape::create($definition, $this);
|
59 |
+
|
60 |
+
if ($isSimple) {
|
61 |
+
$this->simple[$shape] = $result;
|
62 |
+
}
|
63 |
+
|
64 |
+
return $result;
|
65 |
+
}
|
66 |
+
}
|
lib/Aws/Aws/Api/StructureShape.php
ADDED
@@ -0,0 +1,79 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Represents a structure shape and resolve member shape references.
|
6 |
+
*/
|
7 |
+
class StructureShape extends Shape
|
8 |
+
{
|
9 |
+
/**
|
10 |
+
* @var Shape[]
|
11 |
+
*/
|
12 |
+
private $members;
|
13 |
+
|
14 |
+
public function __construct(array $definition, ShapeMap $shapeMap)
|
15 |
+
{
|
16 |
+
$definition['type'] = 'structure';
|
17 |
+
|
18 |
+
if (!isset($definition['members'])) {
|
19 |
+
$definition['members'] = [];
|
20 |
+
}
|
21 |
+
|
22 |
+
parent::__construct($definition, $shapeMap);
|
23 |
+
}
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Gets a list of all members
|
27 |
+
*
|
28 |
+
* @return Shape[]
|
29 |
+
*/
|
30 |
+
public function getMembers()
|
31 |
+
{
|
32 |
+
if (empty($this->members)) {
|
33 |
+
$this->generateMembersHash();
|
34 |
+
}
|
35 |
+
|
36 |
+
return $this->members;
|
37 |
+
}
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Check if a specific member exists by name.
|
41 |
+
*
|
42 |
+
* @param string $name Name of the member to check
|
43 |
+
*
|
44 |
+
* @return bool
|
45 |
+
*/
|
46 |
+
public function hasMember($name)
|
47 |
+
{
|
48 |
+
return isset($this->definition['members'][$name]);
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Retrieve a member by name.
|
53 |
+
*
|
54 |
+
* @param string $name Name of the member to retrieve
|
55 |
+
*
|
56 |
+
* @return Shape
|
57 |
+
* @throws \InvalidArgumentException if the member is not found.
|
58 |
+
*/
|
59 |
+
public function getMember($name)
|
60 |
+
{
|
61 |
+
$members = $this->getMembers();
|
62 |
+
|
63 |
+
if (!isset($members[$name])) {
|
64 |
+
throw new \InvalidArgumentException('Unknown member ' . $name);
|
65 |
+
}
|
66 |
+
|
67 |
+
return $members[$name];
|
68 |
+
}
|
69 |
+
|
70 |
+
|
71 |
+
private function generateMembersHash()
|
72 |
+
{
|
73 |
+
$this->members = [];
|
74 |
+
|
75 |
+
foreach ($this->definition['members'] as $name => $definition) {
|
76 |
+
$this->members[$name] = $this->shapeFor($definition);
|
77 |
+
}
|
78 |
+
}
|
79 |
+
}
|
lib/Aws/Aws/Api/TimestampShape.php
ADDED
@@ -0,0 +1,48 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Represents a timestamp shape.
|
6 |
+
*/
|
7 |
+
class TimestampShape extends Shape
|
8 |
+
{
|
9 |
+
public function __construct(array $definition, ShapeMap $shapeMap)
|
10 |
+
{
|
11 |
+
$definition['type'] = 'timestamp';
|
12 |
+
parent::__construct($definition, $shapeMap);
|
13 |
+
}
|
14 |
+
|
15 |
+
/**
|
16 |
+
* Formats a timestamp value for a service.
|
17 |
+
*
|
18 |
+
* @param mixed $value Value to format
|
19 |
+
* @param string $format Format used to serialize the value
|
20 |
+
*
|
21 |
+
* @return int|string
|
22 |
+
* @throws \UnexpectedValueException if the format is unknown.
|
23 |
+
* @throws \InvalidArgumentException if the value is an unsupported type.
|
24 |
+
*/
|
25 |
+
public static function format($value, $format)
|
26 |
+
{
|
27 |
+
if ($value instanceof \DateTime) {
|
28 |
+
$value = $value->getTimestamp();
|
29 |
+
} elseif (is_string($value)) {
|
30 |
+
$value = strtotime($value);
|
31 |
+
} elseif (!is_int($value)) {
|
32 |
+
throw new \InvalidArgumentException('Unable to handle the provided'
|
33 |
+
. ' timestamp type: ' . gettype($value));
|
34 |
+
}
|
35 |
+
|
36 |
+
switch ($format) {
|
37 |
+
case 'iso8601':
|
38 |
+
return gmdate('Y-m-d\TH:i:s\Z', $value);
|
39 |
+
case 'rfc822':
|
40 |
+
return gmdate('D, d M Y H:i:s \G\M\T', $value);
|
41 |
+
case 'unixTimestamp':
|
42 |
+
return $value;
|
43 |
+
default:
|
44 |
+
throw new \UnexpectedValueException('Unknown timestamp format: '
|
45 |
+
. $format);
|
46 |
+
}
|
47 |
+
}
|
48 |
+
}
|
lib/Aws/Aws/Api/Validator.php
ADDED
@@ -0,0 +1,286 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Api;
|
3 |
+
|
4 |
+
use Aws;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Validates a schema against a hash of input.
|
8 |
+
*/
|
9 |
+
class Validator
|
10 |
+
{
|
11 |
+
private $path = [];
|
12 |
+
private $errors = [];
|
13 |
+
private $constraints = [];
|
14 |
+
|
15 |
+
private static $defaultConstraints = [
|
16 |
+
'required' => true,
|
17 |
+
'min' => true,
|
18 |
+
'max' => false,
|
19 |
+
'pattern' => false
|
20 |
+
];
|
21 |
+
|
22 |
+
/**
|
23 |
+
* @param array $constraints Associative array of constraints to enforce.
|
24 |
+
* Accepts the following keys: "required", "min",
|
25 |
+
* "max", and "pattern". If a key is not
|
26 |
+
* provided, the constraint will assume false.
|
27 |
+
*/
|
28 |
+
public function __construct(array $constraints = null)
|
29 |
+
{
|
30 |
+
static $assumedFalseValues = [
|
31 |
+
'required' => false,
|
32 |
+
'min' => false,
|
33 |
+
'max' => false,
|
34 |
+
'pattern' => false
|
35 |
+
];
|
36 |
+
$this->constraints = empty($constraints)
|
37 |
+
? self::$defaultConstraints
|
38 |
+
: $constraints + $assumedFalseValues;
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Validates the given input against the schema.
|
43 |
+
*
|
44 |
+
* @param string $name Operation name
|
45 |
+
* @param Shape $shape Shape to validate
|
46 |
+
* @param array $input Input to validate
|
47 |
+
*
|
48 |
+
* @throws \InvalidArgumentException if the input is invalid.
|
49 |
+
*/
|
50 |
+
public function validate($name, Shape $shape, array $input)
|
51 |
+
{
|
52 |
+
$this->dispatch($shape, $input);
|
53 |
+
|
54 |
+
if ($this->errors) {
|
55 |
+
$message = sprintf(
|
56 |
+
"Found %d error%s while validating the input provided for the "
|
57 |
+
. "%s operation:\n%s",
|
58 |
+
count($this->errors),
|
59 |
+
count($this->errors) > 1 ? 's' : '',
|
60 |
+
$name,
|
61 |
+
implode("\n", $this->errors)
|
62 |
+
);
|
63 |
+
$this->errors = [];
|
64 |
+
|
65 |
+
throw new \InvalidArgumentException($message);
|
66 |
+
}
|
67 |
+
}
|
68 |
+
|
69 |
+
private function dispatch(Shape $shape, $value)
|
70 |
+
{
|
71 |
+
static $methods = [
|
72 |
+
'structure' => 'check_structure',
|
73 |
+
'list' => 'check_list',
|
74 |
+
'map' => 'check_map',
|
75 |
+
'blob' => 'check_blob',
|
76 |
+
'boolean' => 'check_boolean',
|
77 |
+
'integer' => 'check_numeric',
|
78 |
+
'float' => 'check_numeric',
|
79 |
+
'long' => 'check_numeric',
|
80 |
+
'string' => 'check_string',
|
81 |
+
'byte' => 'check_string',
|
82 |
+
'char' => 'check_string'
|
83 |
+
];
|
84 |
+
|
85 |
+
$type = $shape->getType();
|
86 |
+
if (isset($methods[$type])) {
|
87 |
+
$this->{$methods[$type]}($shape, $value);
|
88 |
+
}
|
89 |
+
}
|
90 |
+
|
91 |
+
private function check_structure(StructureShape $shape, $value)
|
92 |
+
{
|
93 |
+
if (!$this->checkAssociativeArray($value)) {
|
94 |
+
return;
|
95 |
+
}
|
96 |
+
|
97 |
+
if ($this->constraints['required'] && $shape['required']) {
|
98 |
+
foreach ($shape['required'] as $req) {
|
99 |
+
if (!isset($value[$req])) {
|
100 |
+
$this->path[] = $req;
|
101 |
+
$this->addError('is missing and is a required parameter');
|
102 |
+
array_pop($this->path);
|
103 |
+
}
|
104 |
+
}
|
105 |
+
}
|
106 |
+
|
107 |
+
foreach ($value as $name => $v) {
|
108 |
+
if ($shape->hasMember($name)) {
|
109 |
+
$this->path[] = $name;
|
110 |
+
$this->dispatch(
|
111 |
+
$shape->getMember($name),
|
112 |
+
isset($value[$name]) ? $value[$name] : null
|
113 |
+
);
|
114 |
+
array_pop($this->path);
|
115 |
+
}
|
116 |
+
}
|
117 |
+
}
|
118 |
+
|
119 |
+
private function check_list(ListShape $shape, $value)
|
120 |
+
{
|
121 |
+
if (!is_array($value)) {
|
122 |
+
$this->addError('must be an array. Found '
|
123 |
+
. Aws\describe_type($value));
|
124 |
+
return;
|
125 |
+
}
|
126 |
+
|
127 |
+
$this->validateRange($shape, count($value), "list element count");
|
128 |
+
|
129 |
+
$items = $shape->getMember();
|
130 |
+
foreach ($value as $index => $v) {
|
131 |
+
$this->path[] = $index;
|
132 |
+
$this->dispatch($items, $v);
|
133 |
+
array_pop($this->path);
|
134 |
+
}
|
135 |
+
}
|
136 |
+
|
137 |
+
private function check_map(MapShape $shape, $value)
|
138 |
+
{
|
139 |
+
if (!$this->checkAssociativeArray($value)) {
|
140 |
+
return;
|
141 |
+
}
|
142 |
+
|
143 |
+
$values = $shape->getValue();
|
144 |
+
foreach ($value as $key => $v) {
|
145 |
+
$this->path[] = $key;
|
146 |
+
$this->dispatch($values, $v);
|
147 |
+
array_pop($this->path);
|
148 |
+
}
|
149 |
+
}
|
150 |
+
|
151 |
+
private function check_blob(Shape $shape, $value)
|
152 |
+
{
|
153 |
+
static $valid = [
|
154 |
+
'string' => true,
|
155 |
+
'integer' => true,
|
156 |
+
'double' => true,
|
157 |
+
'resource' => true
|
158 |
+
];
|
159 |
+
|
160 |
+
$type = gettype($value);
|
161 |
+
if (!isset($valid[$type])) {
|
162 |
+
if ($type != 'object' || !method_exists($value, '__toString')) {
|
163 |
+
$this->addError('must be an fopen resource, a '
|
164 |
+
. 'GuzzleHttp\Stream\StreamInterface object, or something '
|
165 |
+
. 'that can be cast to a string. Found '
|
166 |
+
. Aws\describe_type($value));
|
167 |
+
}
|
168 |
+
}
|
169 |
+
}
|
170 |
+
|
171 |
+
private function check_numeric(Shape $shape, $value)
|
172 |
+
{
|
173 |
+
if (!is_numeric($value)) {
|
174 |
+
$this->addError('must be numeric. Found '
|
175 |
+
. Aws\describe_type($value));
|
176 |
+
return;
|
177 |
+
}
|
178 |
+
|
179 |
+
$this->validateRange($shape, $value, "numeric value");
|
180 |
+
}
|
181 |
+
|
182 |
+
private function check_boolean(Shape $shape, $value)
|
183 |
+
{
|
184 |
+
if (!is_bool($value)) {
|
185 |
+
$this->addError('must be a boolean. Found '
|
186 |
+
. Aws\describe_type($value));
|
187 |
+
}
|
188 |
+
}
|
189 |
+
|
190 |
+
private function check_string(Shape $shape, $value)
|
191 |
+
{
|
192 |
+
if ($shape['jsonvalue']) {
|
193 |
+
if (!self::canJsonEncode($value)) {
|
194 |
+
$this->addError('must be a value encodable with \'json_encode\'.'
|
195 |
+
. ' Found ' . Aws\describe_type($value));
|
196 |
+
}
|
197 |
+
return;
|
198 |
+
}
|
199 |
+
|
200 |
+
if (!$this->checkCanString($value)) {
|
201 |
+
$this->addError('must be a string or an object that implements '
|
202 |
+
. '__toString(). Found ' . Aws\describe_type($value));
|
203 |
+
return;
|
204 |
+
}
|
205 |
+
|
206 |
+
$this->validateRange($shape, strlen($value), "string length");
|
207 |
+
|
208 |
+
if ($this->constraints['pattern']) {
|
209 |
+
$pattern = $shape['pattern'];
|
210 |
+
if ($pattern && !preg_match("/$pattern/", $value)) {
|
211 |
+
$this->addError("Pattern /$pattern/ failed to match '$value'");
|
212 |
+
}
|
213 |
+
}
|
214 |
+
}
|
215 |
+
|
216 |
+
private function validateRange(Shape $shape, $length, $descriptor)
|
217 |
+
{
|
218 |
+
if ($this->constraints['min']) {
|
219 |
+
$min = $shape['min'];
|
220 |
+
if ($min && $length < $min) {
|
221 |
+
$this->addError("expected $descriptor to be >= $min, but "
|
222 |
+
. "found $descriptor of $length");
|
223 |
+
}
|
224 |
+
}
|
225 |
+
|
226 |
+
if ($this->constraints['max']) {
|
227 |
+
$max = $shape['max'];
|
228 |
+
if ($max && $length > $max) {
|
229 |
+
$this->addError("expected $descriptor to be <= $max, but "
|
230 |
+
. "found $descriptor of $length");
|
231 |
+
}
|
232 |
+
}
|
233 |
+
}
|
234 |
+
|
235 |
+
private function checkCanString($value)
|
236 |
+
{
|
237 |
+
static $valid = [
|
238 |
+
'string' => true,
|
239 |
+
'integer' => true,
|
240 |
+
'double' => true,
|
241 |
+
'NULL' => true,
|
242 |
+
];
|
243 |
+
|
244 |
+
$type = gettype($value);
|
245 |
+
|
246 |
+
return isset($valid[$type]) ||
|
247 |
+
($type == 'object' && method_exists($value, '__toString'));
|
248 |
+
}
|
249 |
+
|
250 |
+
private function checkAssociativeArray($value)
|
251 |
+
{
|
252 |
+
$isAssociative = false;
|
253 |
+
|
254 |
+
if (is_array($value)) {
|
255 |
+
$expectedIndex = 0;
|
256 |
+
$key = key($value);
|
257 |
+
|
258 |
+
do {
|
259 |
+
$isAssociative = $key !== $expectedIndex++;
|
260 |
+
next($value);
|
261 |
+
$key = key($value);
|
262 |
+
} while (!$isAssociative && null !== $key);
|
263 |
+
}
|
264 |
+
|
265 |
+
if (!$isAssociative) {
|
266 |
+
$this->addError('must be an associative array. Found '
|
267 |
+
. Aws\describe_type($value));
|
268 |
+
return false;
|
269 |
+
}
|
270 |
+
|
271 |
+
return true;
|
272 |
+
}
|
273 |
+
|
274 |
+
private function addError($message)
|
275 |
+
{
|
276 |
+
$this->errors[] =
|
277 |
+
implode('', array_map(function ($s) { return "[{$s}]"; }, $this->path))
|
278 |
+
. ' '
|
279 |
+
. $message;
|
280 |
+
}
|
281 |
+
|
282 |
+
private function canJsonEncode($data)
|
283 |
+
{
|
284 |
+
return !is_resource($data);
|
285 |
+
}
|
286 |
+
}
|
lib/Aws/Aws/AwsClient.php
ADDED
@@ -0,0 +1,402 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws;
|
3 |
+
|
4 |
+
use Aws\Api\ApiProvider;
|
5 |
+
use Aws\Api\DocModel;
|
6 |
+
use Aws\Api\Service;
|
7 |
+
use Aws\ClientSideMonitoring\ApiCallAttemptMonitoringMiddleware;
|
8 |
+
use Aws\ClientSideMonitoring\ApiCallMonitoringMiddleware;
|
9 |
+
use Aws\ClientSideMonitoring\ConfigurationProvider;
|
10 |
+
use Aws\EndpointDiscovery\EndpointDiscoveryMiddleware;
|
11 |
+
use Aws\Signature\SignatureProvider;
|
12 |
+
use GuzzleHttp\Psr7\Uri;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Default AWS client implementation
|
16 |
+
*/
|
17 |
+
class AwsClient implements AwsClientInterface
|
18 |
+
{
|
19 |
+
use AwsClientTrait;
|
20 |
+
|
21 |
+
/** @var array */
|
22 |
+
private $config;
|
23 |
+
|
24 |
+
/** @var string */
|
25 |
+
private $region;
|
26 |
+
|
27 |
+
/** @var string */
|
28 |
+
private $endpoint;
|
29 |
+
|
30 |
+
/** @var Service */
|
31 |
+
private $api;
|
32 |
+
|
33 |
+
/** @var callable */
|
34 |
+
private $signatureProvider;
|
35 |
+
|
36 |
+
/** @var callable */
|
37 |
+
private $credentialProvider;
|
38 |
+
|
39 |
+
/** @var HandlerList */
|
40 |
+
private $handlerList;
|
41 |
+
|
42 |
+
/** @var array*/
|
43 |
+
private $defaultRequestOptions;
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Get an array of client constructor arguments used by the client.
|
47 |
+
*
|
48 |
+
* @return array
|
49 |
+
*/
|
50 |
+
public static function getArguments()
|
51 |
+
{
|
52 |
+
return ClientResolver::getDefaultArguments();
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* The client constructor accepts the following options:
|
57 |
+
*
|
58 |
+
* - api_provider: (callable) An optional PHP callable that accepts a
|
59 |
+
* type, service, and version argument, and returns an array of
|
60 |
+
* corresponding configuration data. The type value can be one of api,
|
61 |
+
* waiter, or paginator.
|
62 |
+
* - credentials:
|
63 |
+
* (Aws\Credentials\CredentialsInterface|array|bool|callable) Specifies
|
64 |
+
* the credentials used to sign requests. Provide an
|
65 |
+
* Aws\Credentials\CredentialsInterface object, an associative array of
|
66 |
+
* "key", "secret", and an optional "token" key, `false` to use null
|
67 |
+
* credentials, or a callable credentials provider used to create
|
68 |
+
* credentials or return null. See Aws\Credentials\CredentialProvider for
|
69 |
+
* a list of built-in credentials providers. If no credentials are
|
70 |
+
* provided, the SDK will attempt to load them from the environment.
|
71 |
+
* - debug: (bool|array) Set to true to display debug information when
|
72 |
+
* sending requests. Alternatively, you can provide an associative array
|
73 |
+
* with the following keys: logfn: (callable) Function that is invoked
|
74 |
+
* with log messages; stream_size: (int) When the size of a stream is
|
75 |
+
* greater than this number, the stream data will not be logged (set to
|
76 |
+
* "0" to not log any stream data); scrub_auth: (bool) Set to false to
|
77 |
+
* disable the scrubbing of auth data from the logged messages; http:
|
78 |
+
* (bool) Set to false to disable the "debug" feature of lower level HTTP
|
79 |
+
* adapters (e.g., verbose curl output).
|
80 |
+
* - stats: (bool|array) Set to true to gather transfer statistics on
|
81 |
+
* requests sent. Alternatively, you can provide an associative array with
|
82 |
+
* the following keys: retries: (bool) Set to false to disable reporting
|
83 |
+
* on retries attempted; http: (bool) Set to true to enable collecting
|
84 |
+
* statistics from lower level HTTP adapters (e.g., values returned in
|
85 |
+
* GuzzleHttp\TransferStats). HTTP handlers must support an
|
86 |
+
* `http_stats_receiver` option for this to have an effect; timer: (bool)
|
87 |
+
* Set to true to enable a command timer that reports the total wall clock
|
88 |
+
* time spent on an operation in seconds.
|
89 |
+
* - disable_host_prefix_injection: (bool) Set to true to disable host prefix
|
90 |
+
* injection logic for services that use it. This disables the entire
|
91 |
+
* prefix injection, including the portions supplied by user-defined
|
92 |
+
* parameters. Setting this flag will have no effect on services that do
|
93 |
+
* not use host prefix injection.
|
94 |
+
* - endpoint: (string) The full URI of the webservice. This is only
|
95 |
+
* required when connecting to a custom endpoint (e.g., a local version
|
96 |
+
* of S3).
|
97 |
+
* - endpoint_discovery: (Aws\EndpointDiscovery\ConfigurationInterface,
|
98 |
+
* Aws\CacheInterface, array, callable) Settings for endpoint discovery.
|
99 |
+
* Provide an instance of Aws\EndpointDiscovery\ConfigurationInterface,
|
100 |
+
* an instance Aws\CacheInterface, a callable that provides a promise for
|
101 |
+
* a Configuration object, or an associative array with the following
|
102 |
+
* keys: enabled: (bool) Set to true to enable endpoint discovery,
|
103 |
+
* defaults to false; cache_limit: (int) The maximum number of keys in the
|
104 |
+
* endpoints cache, defaults to 1000.
|
105 |
+
* - endpoint_provider: (callable) An optional PHP callable that
|
106 |
+
* accepts a hash of options including a "service" and "region" key and
|
107 |
+
* returns NULL or a hash of endpoint data, of which the "endpoint" key
|
108 |
+
* is required. See Aws\Endpoint\EndpointProvider for a list of built-in
|
109 |
+
* providers.
|
110 |
+
* - handler: (callable) A handler that accepts a command object,
|
111 |
+
* request object and returns a promise that is fulfilled with an
|
112 |
+
* Aws\ResultInterface object or rejected with an
|
113 |
+
* Aws\Exception\AwsException. A handler does not accept a next handler
|
114 |
+
* as it is terminal and expected to fulfill a command. If no handler is
|
115 |
+
* provided, a default Guzzle handler will be utilized.
|
116 |
+
* - http: (array, default=array(0)) Set to an array of SDK request
|
117 |
+
* options to apply to each request (e.g., proxy, verify, etc.).
|
118 |
+
* - http_handler: (callable) An HTTP handler is a function that
|
119 |
+
* accepts a PSR-7 request object and returns a promise that is fulfilled
|
120 |
+
* with a PSR-7 response object or rejected with an array of exception
|
121 |
+
* data. NOTE: This option supersedes any provided "handler" option.
|
122 |
+
* - idempotency_auto_fill: (bool|callable) Set to false to disable SDK to
|
123 |
+
* populate parameters that enabled 'idempotencyToken' trait with a random
|
124 |
+
* UUID v4 value on your behalf. Using default value 'true' still allows
|
125 |
+
* parameter value to be overwritten when provided. Note: auto-fill only
|
126 |
+
* works when cryptographically secure random bytes generator functions
|
127 |
+
* (random_bytes, openssl_random_pseudo_bytes or mcrypt_create_iv) can be
|
128 |
+
* found. You may also provide a callable source of random bytes.
|
129 |
+
* - profile: (string) Allows you to specify which profile to use when
|
130 |
+
* credentials are created from the AWS credentials file in your HOME
|
131 |
+
* directory. This setting overrides the AWS_PROFILE environment
|
132 |
+
* variable. Note: Specifying "profile" will cause the "credentials" key
|
133 |
+
* to be ignored.
|
134 |
+
* - region: (string, required) Region to connect to. See
|
135 |
+
* http://docs.aws.amazon.com/general/latest/gr/rande.html for a list of
|
136 |
+
* available regions.
|
137 |
+
* - retries: (int, default=int(3)) Configures the maximum number of
|
138 |
+
* allowed retries for a client (pass 0 to disable retries).
|
139 |
+
* - scheme: (string, default=string(5) "https") URI scheme to use when
|
140 |
+
* connecting connect. The SDK will utilize "https" endpoints (i.e.,
|
141 |
+
* utilize SSL/TLS connections) by default. You can attempt to connect to
|
142 |
+
* a service over an unencrypted "http" endpoint by setting ``scheme`` to
|
143 |
+
* "http".
|
144 |
+
* - signature_provider: (callable) A callable that accepts a signature
|
145 |
+
* version name (e.g., "v4"), a service name, and region, and
|
146 |
+
* returns a SignatureInterface object or null. This provider is used to
|
147 |
+
* create signers utilized by the client. See
|
148 |
+
* Aws\Signature\SignatureProvider for a list of built-in providers
|
149 |
+
* - signature_version: (string) A string representing a custom
|
150 |
+
* signature version to use with a service (e.g., v4). Note that
|
151 |
+
* per/operation signature version MAY override this requested signature
|
152 |
+
* version.
|
153 |
+
* - validate: (bool, default=bool(true)) Set to false to disable
|
154 |
+
* client-side parameter validation.
|
155 |
+
* - version: (string, required) The version of the webservice to
|
156 |
+
* utilize (e.g., 2006-03-01).
|
157 |
+
*
|
158 |
+
* @param array $args Client configuration arguments.
|
159 |
+
*
|
160 |
+
* @throws \InvalidArgumentException if any required options are missing or
|
161 |
+
* the service is not supported.
|
162 |
+
*/
|
163 |
+
public function __construct(array $args)
|
164 |
+
{
|
165 |
+
list($service, $exceptionClass) = $this->parseClass();
|
166 |
+
if (!isset($args['service'])) {
|
167 |
+
$args['service'] = manifest($service)['endpoint'];
|
168 |
+
}
|
169 |
+
if (!isset($args['exception_class'])) {
|
170 |
+
$args['exception_class'] = $exceptionClass;
|
171 |
+
}
|
172 |
+
$this->handlerList = new HandlerList();
|
173 |
+
$resolver = new ClientResolver(static::getArguments());
|
174 |
+
$config = $resolver->resolve($args, $this->handlerList);
|
175 |
+
$this->api = $config['api'];
|
176 |
+
$this->signatureProvider = $config['signature_provider'];
|
177 |
+
$this->endpoint = new Uri($config['endpoint']);
|
178 |
+
$this->credentialProvider = $config['credentials'];
|
179 |
+
$this->region = isset($config['region']) ? $config['region'] : null;
|
180 |
+
$this->config = $config['config'];
|
181 |
+
$this->defaultRequestOptions = $config['http'];
|
182 |
+
$this->addSignatureMiddleware();
|
183 |
+
$this->addInvocationId();
|
184 |
+
$this->addClientSideMonitoring($args);
|
185 |
+
$this->addEndpointParameterMiddleware($args);
|
186 |
+
$this->addEndpointDiscoveryMiddleware($config, $args);
|
187 |
+
|
188 |
+
if (isset($args['with_resolved'])) {
|
189 |
+
$args['with_resolved']($config);
|
190 |
+
}
|
191 |
+
}
|
192 |
+
|
193 |
+
public function getHandlerList()
|
194 |
+
{
|
195 |
+
return $this->handlerList;
|
196 |
+
}
|
197 |
+
|
198 |
+
public function getConfig($option = null)
|
199 |
+
{
|
200 |
+
return $option === null
|
201 |
+
? $this->config
|
202 |
+
: (isset($this->config[$option])
|
203 |
+
? $this->config[$option]
|
204 |
+
: null);
|
205 |
+
}
|
206 |
+
|
207 |
+
public function getCredentials()
|
208 |
+
{
|
209 |
+
$fn = $this->credentialProvider;
|
210 |
+
return $fn();
|
211 |
+
}
|
212 |
+
|
213 |
+
public function getEndpoint()
|
214 |
+
{
|
215 |
+
return $this->endpoint;
|
216 |
+
}
|
217 |
+
|
218 |
+
public function getRegion()
|
219 |
+
{
|
220 |
+
return $this->region;
|
221 |
+
}
|
222 |
+
|
223 |
+
public function getApi()
|
224 |
+
{
|
225 |
+
return $this->api;
|
226 |
+
}
|
227 |
+
|
228 |
+
public function getCommand($name, array $args = [])
|
229 |
+
{
|
230 |
+
// Fail fast if the command cannot be found in the description.
|
231 |
+
if (!isset($this->getApi()['operations'][$name])) {
|
232 |
+
$name = ucfirst($name);
|
233 |
+
if (!isset($this->getApi()['operations'][$name])) {
|
234 |
+
throw new \InvalidArgumentException("Operation not found: $name");
|
235 |
+
}
|
236 |
+
}
|
237 |
+
|
238 |
+
if (!isset($args['@http'])) {
|
239 |
+
$args['@http'] = $this->defaultRequestOptions;
|
240 |
+
} else {
|
241 |
+
$args['@http'] += $this->defaultRequestOptions;
|
242 |
+
}
|
243 |
+
|
244 |
+
return new Command($name, $args, clone $this->getHandlerList());
|
245 |
+
}
|
246 |
+
|
247 |
+
public function __sleep()
|
248 |
+
{
|
249 |
+
throw new \RuntimeException('Instances of ' . static::class
|
250 |
+
. ' cannot be serialized');
|
251 |
+
}
|
252 |
+
|
253 |
+
/**
|
254 |
+
* Get the signature_provider function of the client.
|
255 |
+
*
|
256 |
+
* @return callable
|
257 |
+
*/
|
258 |
+
final protected function getSignatureProvider()
|
259 |
+
{
|
260 |
+
return $this->signatureProvider;
|
261 |
+
}
|
262 |
+
|
263 |
+
/**
|
264 |
+
* Parse the class name and setup the custom exception class of the client
|
265 |
+
* and return the "service" name of the client and "exception_class".
|
266 |
+
*
|
267 |
+
* @return array
|
268 |
+
*/
|
269 |
+
private function parseClass()
|
270 |
+
{
|
271 |
+
$klass = get_class($this);
|
272 |
+
|
273 |
+
if ($klass === __CLASS__) {
|
274 |
+
return ['', 'Aws\Exception\AwsException'];
|
275 |
+
}
|
276 |
+
|
277 |
+
$service = substr($klass, strrpos($klass, '\\') + 1, -6);
|
278 |
+
|
279 |
+
return [
|
280 |
+
strtolower($service),
|
281 |
+
"Aws\\{$service}\\Exception\\{$service}Exception"
|
282 |
+
];
|
283 |
+
}
|
284 |
+
|
285 |
+
private function addEndpointParameterMiddleware($args)
|
286 |
+
{
|
287 |
+
if (empty($args['disable_host_prefix_injection'])) {
|
288 |
+
$list = $this->getHandlerList();
|
289 |
+
$list->appendBuild(
|
290 |
+
EndpointParameterMiddleware::wrap(
|
291 |
+
$this->api
|
292 |
+
),
|
293 |
+
'endpoint_parameter'
|
294 |
+
);
|
295 |
+
}
|
296 |
+
}
|
297 |
+
|
298 |
+
private function addEndpointDiscoveryMiddleware($config, $args)
|
299 |
+
{
|
300 |
+
$list = $this->getHandlerList();
|
301 |
+
|
302 |
+
if (!isset($args['endpoint'])) {
|
303 |
+
$list->appendBuild(
|
304 |
+
EndpointDiscoveryMiddleware::wrap(
|
305 |
+
$this,
|
306 |
+
$args,
|
307 |
+
$config['endpoint_discovery']
|
308 |
+
),
|
309 |
+
'EndpointDiscoveryMiddleware'
|
310 |
+
);
|
311 |
+
}
|
312 |
+
}
|
313 |
+
|
314 |
+
private function addSignatureMiddleware()
|
315 |
+
{
|
316 |
+
$api = $this->getApi();
|
317 |
+
$provider = $this->signatureProvider;
|
318 |
+
$version = $this->config['signature_version'];
|
319 |
+
$name = $this->config['signing_name'];
|
320 |
+
$region = $this->config['signing_region'];
|
321 |
+
|
322 |
+
$resolver = static function (
|
323 |
+
CommandInterface $c
|
324 |
+
) use ($api, $provider, $name, $region, $version) {
|
325 |
+
$authType = $api->getOperation($c->getName())['authtype'];
|
326 |
+
switch ($authType){
|
327 |
+
case 'none':
|
328 |
+
$version = 'anonymous';
|
329 |
+
break;
|
330 |
+
case 'v4-unsigned-body':
|
331 |
+
$version = 'v4-unsigned-body';
|
332 |
+
break;
|
333 |
+
}
|
334 |
+
return SignatureProvider::resolve($provider, $version, $name, $region);
|
335 |
+
};
|
336 |
+
$this->handlerList->appendSign(
|
337 |
+
Middleware::signer($this->credentialProvider, $resolver),
|
338 |
+
'signer'
|
339 |
+
);
|
340 |
+
}
|
341 |
+
|
342 |
+
private function addInvocationId()
|
343 |
+
{
|
344 |
+
// Add invocation id to each request
|
345 |
+
$this->handlerList->prependSign(Middleware::invocationId(), 'invocation-id');
|
346 |
+
}
|
347 |
+
|
348 |
+
private function addClientSideMonitoring($args)
|
349 |
+
{
|
350 |
+
$options = ConfigurationProvider::defaultProvider($args);
|
351 |
+
|
352 |
+
$this->handlerList->appendBuild(
|
353 |
+
ApiCallMonitoringMiddleware::wrap(
|
354 |
+
$this->credentialProvider,
|
355 |
+
$options,
|
356 |
+
$this->region,
|
357 |
+
$this->getApi()->getServiceId()
|
358 |
+
),
|
359 |
+
'ApiCallMonitoringMiddleware'
|
360 |
+
);
|
361 |
+
|
362 |
+
$callAttemptMiddleware = ApiCallAttemptMonitoringMiddleware::wrap(
|
363 |
+
$this->credentialProvider,
|
364 |
+
$options,
|
365 |
+
$this->region,
|
366 |
+
$this->getApi()->getServiceId()
|
367 |
+
);
|
368 |
+
$this->handlerList->appendAttempt (
|
369 |
+
$callAttemptMiddleware,
|
370 |
+
'ApiCallAttemptMonitoringMiddleware'
|
371 |
+
);
|
372 |
+
}
|
373 |
+
|
374 |
+
/**
|
375 |
+
* Returns a service model and doc model with any necessary changes
|
376 |
+
* applied.
|
377 |
+
*
|
378 |
+
* @param array $api Array of service data being documented.
|
379 |
+
* @param array $docs Array of doc model data.
|
380 |
+
*
|
381 |
+
* @return array Tuple containing a [Service, DocModel]
|
382 |
+
*
|
383 |
+
* @internal This should only used to document the service API.
|
384 |
+
* @codeCoverageIgnore
|
385 |
+
*/
|
386 |
+
public static function applyDocFilters(array $api, array $docs)
|
387 |
+
{
|
388 |
+
return [
|
389 |
+
new Service($api, ApiProvider::defaultProvider()),
|
390 |
+
new DocModel($docs)
|
391 |
+
];
|
392 |
+
}
|
393 |
+
|
394 |
+
/**
|
395 |
+
* @deprecated
|
396 |
+
* @return static
|
397 |
+
*/
|
398 |
+
public static function factory(array $config = [])
|
399 |
+
{
|
400 |
+
return new static($config);
|
401 |
+
}
|
402 |
+
}
|
lib/Aws/Aws/AwsClientInterface.php
ADDED
@@ -0,0 +1,169 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws;
|
3 |
+
|
4 |
+
use Psr\Http\Message\UriInterface;
|
5 |
+
use GuzzleHttp\Promise\PromiseInterface;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Represents an AWS client.
|
9 |
+
*/
|
10 |
+
interface AwsClientInterface
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Creates and executes a command for an operation by name.
|
14 |
+
*
|
15 |
+
* Suffixing an operation name with "Async" will return a
|
16 |
+
* promise that can be used to execute commands asynchronously.
|
17 |
+
*
|
18 |
+
* @param string $name Name of the command to execute.
|
19 |
+
* @param array $arguments Arguments to pass to the getCommand method.
|
20 |
+
*
|
21 |
+
* @return ResultInterface
|
22 |
+
* @throws \Exception
|
23 |
+
*/
|
24 |
+
public function __call($name, array $arguments);
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Create a command for an operation name.
|
28 |
+
*
|
29 |
+
* Special keys may be set on the command to control how it behaves,
|
30 |
+
* including:
|
31 |
+
*
|
32 |
+
* - @http: Associative array of transfer specific options to apply to the
|
33 |
+
* request that is serialized for this command. Available keys include
|
34 |
+
* "proxy", "verify", "timeout", "connect_timeout", "debug", "delay", and
|
35 |
+
* "headers".
|
36 |
+
*
|
37 |
+
* @param string $name Name of the operation to use in the command
|
38 |
+
* @param array $args Arguments to pass to the command
|
39 |
+
*
|
40 |
+
* @return CommandInterface
|
41 |
+
* @throws \InvalidArgumentException if no command can be found by name
|
42 |
+
*/
|
43 |
+
public function getCommand($name, array $args = []);
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Execute a single command.
|
47 |
+
*
|
48 |
+
* @param CommandInterface $command Command to execute
|
49 |
+
*
|
50 |
+
* @return ResultInterface
|
51 |
+
* @throws \Exception
|
52 |
+
*/
|
53 |
+
public function execute(CommandInterface $command);
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Execute a command asynchronously.
|
57 |
+
*
|
58 |
+
* @param CommandInterface $command Command to execute
|
59 |
+
*
|
60 |
+
* @return \GuzzleHttp\Promise\PromiseInterface
|
61 |
+
*/
|
62 |
+
public function executeAsync(CommandInterface $command);
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Returns a promise that is fulfilled with an
|
66 |
+
* {@see \Aws\Credentials\CredentialsInterface} object.
|
67 |
+
*
|
68 |
+
* If you need the credentials synchronously, then call the wait() method
|
69 |
+
* on the returned promise.
|
70 |
+
*
|
71 |
+
* @return PromiseInterface
|
72 |
+
*/
|
73 |
+
public function getCredentials();
|
74 |
+
|
75 |
+
/**
|
76 |
+
* Get the region to which the client is configured to send requests.
|
77 |
+
*
|
78 |
+
* @return string
|
79 |
+
*/
|
80 |
+
public function getRegion();
|
81 |
+
|
82 |
+
/**
|
83 |
+
* Gets the default endpoint, or base URL, used by the client.
|
84 |
+
*
|
85 |
+
* @return UriInterface
|
86 |
+
*/
|
87 |
+
public function getEndpoint();
|
88 |
+
|
89 |
+
/**
|
90 |
+
* Get the service description associated with the client.
|
91 |
+
*
|
92 |
+
* @return \Aws\Api\Service
|
93 |
+
*/
|
94 |
+
public function getApi();
|
95 |
+
|
96 |
+
/**
|
97 |
+
* Get a client configuration value.
|
98 |
+
*
|
99 |
+
* @param string|null $option The option to retrieve. Pass null to retrieve
|
100 |
+
* all options.
|
101 |
+
* @return mixed|null
|
102 |
+
*/
|
103 |
+
public function getConfig($option = null);
|
104 |
+
|
105 |
+
/**
|
106 |
+
* Get the handler list used to transfer commands.
|
107 |
+
*
|
108 |
+
* This list can be modified to add middleware or to change the underlying
|
109 |
+
* handler used to send HTTP requests.
|
110 |
+
*
|
111 |
+
* @return HandlerList
|
112 |
+
*/
|
113 |
+
public function getHandlerList();
|
114 |
+
|
115 |
+
/**
|
116 |
+
* Get a resource iterator for the specified operation.
|
117 |
+
*
|
118 |
+
* @param string $name Name of the iterator to retrieve.
|
119 |
+
* @param array $args Command arguments to use with each command.
|
120 |
+
*
|
121 |
+
* @return \Iterator
|
122 |
+
* @throws \UnexpectedValueException if the iterator config is invalid.
|
123 |
+
*/
|
124 |
+
public function getIterator($name, array $args = []);
|
125 |
+
|
126 |
+
/**
|
127 |
+
* Get a result paginator for the specified operation.
|
128 |
+
*
|
129 |
+
* @param string $name Name of the operation used for iterator
|
130 |
+
* @param array $args Command args to be used with each command
|
131 |
+
*
|
132 |
+
* @return \Aws\ResultPaginator
|
133 |
+
* @throws \UnexpectedValueException if the iterator config is invalid.
|
134 |
+
*/
|
135 |
+
public function getPaginator($name, array $args = []);
|
136 |
+
|
137 |
+
/**
|
138 |
+
* Wait until a resource is in a particular state.
|
139 |
+
*
|
140 |
+
* @param string|callable $name Name of the waiter that defines the wait
|
141 |
+
* configuration and conditions.
|
142 |
+
* @param array $args Args to be used with each command executed
|
143 |
+
* by the waiter. Waiter configuration options
|
144 |
+
* can be provided in an associative array in
|
145 |
+
* the @waiter key.
|
146 |
+
* @return void
|
147 |
+
* @throws \UnexpectedValueException if the waiter is invalid.
|
148 |
+
*/
|
149 |
+
public function waitUntil($name, array $args = []);
|
150 |
+
|
151 |
+
/**
|
152 |
+
* Get a waiter that waits until a resource is in a particular state.
|
153 |
+
*
|
154 |
+
* Retrieving a waiter can be useful when you wish to wait asynchronously:
|
155 |
+
*
|
156 |
+
* $waiter = $client->getWaiter('foo', ['bar' => 'baz']);
|
157 |
+
* $waiter->promise()->then(function () { echo 'Done!'; });
|
158 |
+
*
|
159 |
+
* @param string|callable $name Name of the waiter that defines the wait
|
160 |
+
* configuration and conditions.
|
161 |
+
* @param array $args Args to be used with each command executed
|
162 |
+
* by the waiter. Waiter configuration options
|
163 |
+
* can be provided in an associative array in
|
164 |
+
* the @waiter key.
|
165 |
+
* @return \Aws\Waiter
|
166 |
+
* @throws \UnexpectedValueException if the waiter is invalid.
|
167 |
+
*/
|
168 |
+
public function getWaiter($name, array $args = []);
|
169 |
+
}
|
lib/Aws/Aws/AwsClientTrait.php
ADDED
@@ -0,0 +1,92 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws;
|
3 |
+
|
4 |
+
use Aws\Api\Service;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* A trait providing generic functionality for interacting with Amazon Web
|
8 |
+
* Services. This is meant to be used in classes implementing
|
9 |
+
* \Aws\AwsClientInterface
|
10 |
+
*/
|
11 |
+
trait AwsClientTrait
|
12 |
+
{
|
13 |
+
public function getPaginator($name, array $args = [])
|
14 |
+
{
|
15 |
+
$config = $this->getApi()->getPaginatorConfig($name);
|
16 |
+
|
17 |
+
return new ResultPaginator($this, $name, $args, $config);
|
18 |
+
}
|
19 |
+
|
20 |
+
public function getIterator($name, array $args = [])
|
21 |
+
{
|
22 |
+
$config = $this->getApi()->getPaginatorConfig($name);
|
23 |
+
if (!$config['result_key']) {
|
24 |
+
throw new \UnexpectedValueException(sprintf(
|
25 |
+
'There are no resources to iterate for the %s operation of %s',
|
26 |
+
$name, $this->getApi()['serviceFullName']
|
27 |
+
));
|
28 |
+
}
|
29 |
+
|
30 |
+
$key = is_array($config['result_key'])
|
31 |
+
? $config['result_key'][0]
|
32 |
+
: $config['result_key'];
|
33 |
+
|
34 |
+
if ($config['output_token'] && $config['input_token']) {
|
35 |
+
return $this->getPaginator($name, $args)->search($key);
|
36 |
+
}
|
37 |
+
|
38 |
+
$result = $this->execute($this->getCommand($name, $args))->search($key);
|
39 |
+
|
40 |
+
return new \ArrayIterator((array) $result);
|
41 |
+
}
|
42 |
+
|
43 |
+
public function waitUntil($name, array $args = [])
|
44 |
+
{
|
45 |
+
return $this->getWaiter($name, $args)->promise()->wait();
|
46 |
+
}
|
47 |
+
|
48 |
+
public function getWaiter($name, array $args = [])
|
49 |
+
{
|
50 |
+
$config = isset($args['@waiter']) ? $args['@waiter'] : [];
|
51 |
+
$config += $this->getApi()->getWaiterConfig($name);
|
52 |
+
|
53 |
+
return new Waiter($this, $name, $args, $config);
|
54 |
+
}
|
55 |
+
|
56 |
+
public function execute(CommandInterface $command)
|
57 |
+
{
|
58 |
+
return $this->executeAsync($command)->wait();
|
59 |
+
}
|
60 |
+
|
61 |
+
public function executeAsync(CommandInterface $command)
|
62 |
+
{
|
63 |
+
$handler = $command->getHandlerList()->resolve();
|
64 |
+
return $handler($command);
|
65 |
+
}
|
66 |
+
|
67 |
+
public function __call($name, array $args)
|
68 |
+
{
|
69 |
+
$params = isset($args[0]) ? $args[0] : [];
|
70 |
+
|
71 |
+
if (substr($name, -5) === 'Async') {
|
72 |
+
return $this->executeAsync(
|
73 |
+
$this->getCommand(substr($name, 0, -5), $params)
|
74 |
+
);
|
75 |
+
}
|
76 |
+
|
77 |
+
return $this->execute($this->getCommand($name, $params));
|
78 |
+
}
|
79 |
+
|
80 |
+
/**
|
81 |
+
* @param string $name
|
82 |
+
* @param array $args
|
83 |
+
*
|
84 |
+
* @return CommandInterface
|
85 |
+
*/
|
86 |
+
abstract public function getCommand($name, array $args = []);
|
87 |
+
|
88 |
+
/**
|
89 |
+
* @return Service
|
90 |
+
*/
|
91 |
+
abstract public function getApi();
|
92 |
+
}
|
lib/Aws/Aws/CacheInterface.php
ADDED
@@ -0,0 +1,34 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Represents a simple cache interface.
|
6 |
+
*/
|
7 |
+
interface CacheInterface
|
8 |
+
{
|
9 |
+
/**
|
10 |
+
* Get a cache item by key.
|
11 |
+
*
|
12 |
+
* @param string $key Key to retrieve.
|
13 |
+
*
|
14 |
+
* @return mixed|null Returns the value or null if not found.
|
15 |
+
*/
|
16 |
+
public function get($key);
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Set a cache key value.
|
20 |
+
*
|
21 |
+
* @param string $key Key to set
|
22 |
+
* @param mixed $value Value to set.
|
23 |
+
* @param int $ttl Number of seconds the item is allowed to live. Set
|
24 |
+
* to 0 to allow an unlimited lifetime.
|
25 |
+
*/
|
26 |
+
public function set($key, $value, $ttl = 0);
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Remove a cache key.
|
30 |
+
*
|
31 |
+
* @param string $key Key to remove.
|
32 |
+
*/
|
33 |
+
public function remove($key);
|
34 |
+
}
|
lib/Aws/Aws/ClientResolver.php
ADDED
@@ -0,0 +1,768 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws;
|
3 |
+
|
4 |
+
use Aws\Api\Validator;
|
5 |
+
use Aws\Api\ApiProvider;
|
6 |
+
use Aws\Api\Service;
|
7 |
+
use Aws\Credentials\Credentials;
|
8 |
+
use Aws\Credentials\CredentialsInterface;
|
9 |
+
use Aws\Endpoint\PartitionEndpointProvider;
|
10 |
+
use Aws\EndpointDiscovery\ConfigurationInterface;
|
11 |
+
use Aws\EndpointDiscovery\ConfigurationProvider;
|
12 |
+
use Aws\EndpointDiscovery\EndpointDiscoveryMiddleware;
|
13 |
+
use Aws\Signature\SignatureProvider;
|
14 |
+
use Aws\Endpoint\EndpointProvider;
|
15 |
+
use Aws\Credentials\CredentialProvider;
|
16 |
+
use InvalidArgumentException as IAE;
|
17 |
+
use Psr\Http\Message\RequestInterface;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* @internal Resolves a hash of client arguments to construct a client.
|
21 |
+
*/
|
22 |
+
class ClientResolver
|
23 |
+
{
|
24 |
+
/** @var array */
|
25 |
+
private $argDefinitions;
|
26 |
+
|
27 |
+
/** @var array Map of types to a corresponding function */
|
28 |
+
private static $typeMap = [
|
29 |
+
'resource' => 'is_resource',
|
30 |
+
'callable' => 'is_callable',
|
31 |
+
'int' => 'is_int',
|
32 |
+
'bool' => 'is_bool',
|
33 |
+
'string' => 'is_string',
|
34 |
+
'object' => 'is_object',
|
35 |
+
'array' => 'is_array',
|
36 |
+
];
|
37 |
+
|
38 |
+
private static $defaultArgs = [
|
39 |
+
'service' => [
|
40 |
+
'type' => 'value',
|
41 |
+
'valid' => ['string'],
|
42 |
+
'doc' => 'Name of the service to utilize. This value will be supplied by default when using one of the SDK clients (e.g., Aws\\S3\\S3Client).',
|
43 |
+
'required' => true,
|
44 |
+
'internal' => true
|
45 |
+
],
|
46 |
+
'exception_class' => [
|
47 |
+
'type' => 'value',
|
48 |
+
'valid' => ['string'],
|
49 |
+
'doc' => 'Exception class to create when an error occurs.',
|
50 |
+
'default' => 'Aws\Exception\AwsException',
|
51 |
+
'internal' => true
|
52 |
+
],
|
53 |
+
'scheme' => [
|
54 |
+
'type' => 'value',
|
55 |
+
'valid' => ['string'],
|
56 |
+
'default' => 'https',
|
57 |
+
'doc' => 'URI scheme to use when connecting connect. The SDK will utilize "https" endpoints (i.e., utilize SSL/TLS connections) by default. You can attempt to connect to a service over an unencrypted "http" endpoint by setting ``scheme`` to "http".',
|
58 |
+
],
|
59 |
+
'disable_host_prefix_injection' => [
|
60 |
+
'type' => 'value',
|
61 |
+
'valid' => ['bool'],
|
62 |
+
'doc' => 'Set to true to disable host prefix injection logic for services that use it. This disables the entire prefix injection, including the portions supplied by user-defined parameters. Setting this flag will have no effect on services that do not use host prefix injection.',
|
63 |
+
'default' => false,
|
64 |
+
],
|
65 |
+
'endpoint' => [
|
66 |
+
'type' => 'value',
|
67 |
+
'valid' => ['string'],
|
68 |
+
'doc' => 'The full URI of the webservice. This is only required when connecting to a custom endpoint (e.g., a local version of S3).',
|
69 |
+
'fn' => [__CLASS__, '_apply_endpoint'],
|
70 |
+
],
|
71 |
+
'region' => [
|
72 |
+
'type' => 'value',
|
73 |
+
'valid' => ['string'],
|
74 |
+
'required' => [__CLASS__, '_missing_region'],
|
75 |
+
'doc' => 'Region to connect to. See http://docs.aws.amazon.com/general/latest/gr/rande.html for a list of available regions.',
|
76 |
+
],
|
77 |
+
'version' => [
|
78 |
+
'type' => 'value',
|
79 |
+
'valid' => ['string'],
|
80 |
+
'required' => [__CLASS__, '_missing_version'],
|
81 |
+
'doc' => 'The version of the webservice to utilize (e.g., 2006-03-01).',
|
82 |
+
],
|
83 |
+
'signature_provider' => [
|
84 |
+
'type' => 'value',
|
85 |
+
'valid' => ['callable'],
|
86 |
+
'doc' => 'A callable that accepts a signature version name (e.g., "v4"), a service name, and region, and returns a SignatureInterface object or null. This provider is used to create signers utilized by the client. See Aws\\Signature\\SignatureProvider for a list of built-in providers',
|
87 |
+
'default' => [__CLASS__, '_default_signature_provider'],
|
88 |
+
],
|
89 |
+
'api_provider' => [
|
90 |
+
'type' => 'value',
|
91 |
+
'valid' => ['callable'],
|
92 |
+
'doc' => 'An optional PHP callable that accepts a type, service, and version argument, and returns an array of corresponding configuration data. The type value can be one of api, waiter, or paginator.',
|
93 |
+
'fn' => [__CLASS__, '_apply_api_provider'],
|
94 |
+
'default' => [ApiProvider::class, 'defaultProvider'],
|
95 |
+
],
|
96 |
+
'endpoint_provider' => [
|
97 |
+
'type' => 'value',
|
98 |
+
'valid' => ['callable'],
|
99 |
+
'fn' => [__CLASS__, '_apply_endpoint_provider'],
|
100 |
+
'doc' => 'An optional PHP callable that accepts a hash of options including a "service" and "region" key and returns NULL or a hash of endpoint data, of which the "endpoint" key is required. See Aws\\Endpoint\\EndpointProvider for a list of built-in providers.',
|
101 |
+
'default' => [__CLASS__, '_default_endpoint_provider'],
|
102 |
+
],
|
103 |
+
'serializer' => [
|
104 |
+
'default' => [__CLASS__, '_default_serializer'],
|
105 |
+
'fn' => [__CLASS__, '_apply_serializer'],
|
106 |
+
'internal' => true,
|
107 |
+
'type' => 'value',
|
108 |
+
'valid' => ['callable'],
|
109 |
+
],
|
110 |
+
'signature_version' => [
|
111 |
+
'type' => 'config',
|
112 |
+
'valid' => ['string'],
|
113 |
+
'doc' => 'A string representing a custom signature version to use with a service (e.g., v4). Note that per/operation signature version MAY override this requested signature version.',
|
114 |
+
'default' => [__CLASS__, '_default_signature_version'],
|
115 |
+
],
|
116 |
+
'signing_name' => [
|
117 |
+
'type' => 'config',
|
118 |
+
'valid' => ['string'],
|
119 |
+
'doc' => 'A string representing a custom service name to be used when calculating a request signature.',
|
120 |
+
'default' => [__CLASS__, '_default_signing_name'],
|
121 |
+
],
|
122 |
+
'signing_region' => [
|
123 |
+
'type' => 'config',
|
124 |
+
'valid' => ['string'],
|
125 |
+
'doc' => 'A string representing a custom region name to be used when calculating a request signature.',
|
126 |
+
'default' => [__CLASS__, '_default_signing_region'],
|
127 |
+
],
|
128 |
+
'profile' => [
|
129 |
+
'type' => 'config',
|
130 |
+
'valid' => ['string'],
|
131 |
+
'doc' => 'Allows you to specify which profile to use when credentials are created from the AWS credentials file in your HOME directory. This setting overrides the AWS_PROFILE environment variable. Note: Specifying "profile" will cause the "credentials" key to be ignored.',
|
132 |
+
'fn' => [__CLASS__, '_apply_profile'],
|
133 |
+
],
|
134 |
+
'credentials' => [
|
135 |
+
'type' => 'value',
|
136 |
+
'valid' => [CredentialsInterface::class, CacheInterface::class, 'array', 'bool', 'callable'],
|
137 |
+
'doc' => 'Specifies the credentials used to sign requests. Provide an Aws\Credentials\CredentialsInterface object, an associative array of "key", "secret", and an optional "token" key, `false` to use null credentials, or a callable credentials provider used to create credentials or return null. See Aws\\Credentials\\CredentialProvider for a list of built-in credentials providers. If no credentials are provided, the SDK will attempt to load them from the environment.',
|
138 |
+
'fn' => [__CLASS__, '_apply_credentials'],
|
139 |
+
'default' => [CredentialProvider::class, 'defaultProvider'],
|
140 |
+
],
|
141 |
+
'endpoint_discovery' => [
|
142 |
+
'type' => 'value',
|
143 |
+
'valid' => [ConfigurationInterface::class, CacheInterface::class, 'array', 'callable'],
|
144 |
+
'doc' => 'Specifies settings for endpoint discovery. Provide an instance of Aws\EndpointDiscovery\ConfigurationInterface, an instance Aws\CacheInterface, a callable that provides a promise for a Configuration object, or an associative array with the following keys: enabled: (bool) Set to true to enable endpoint discovery. Defaults to false; cache_limit: (int) The maximum number of keys in the endpoints cache. Defaults to 1000.',
|
145 |
+
'fn' => [__CLASS__, '_apply_endpoint_discovery'],
|
146 |
+
'default' => [__CLASS__, '_default_endpoint_discovery_provider']
|
147 |
+
],
|
148 |
+
'stats' => [
|
149 |
+
'type' => 'value',
|
150 |
+
'valid' => ['bool', 'array'],
|
151 |
+
'default' => false,
|
152 |
+
'doc' => 'Set to true to gather transfer statistics on requests sent. Alternatively, you can provide an associative array with the following keys: retries: (bool) Set to false to disable reporting on retries attempted; http: (bool) Set to true to enable collecting statistics from lower level HTTP adapters (e.g., values returned in GuzzleHttp\TransferStats). HTTP handlers must support an http_stats_receiver option for this to have an effect; timer: (bool) Set to true to enable a command timer that reports the total wall clock time spent on an operation in seconds.',
|
153 |
+
'fn' => [__CLASS__, '_apply_stats'],
|
154 |
+
],
|
155 |
+
'retries' => [
|
156 |
+
'type' => 'value',
|
157 |
+
'valid' => ['int'],
|
158 |
+
'doc' => 'Configures the maximum number of allowed retries for a client (pass 0 to disable retries). ',
|
159 |
+
'fn' => [__CLASS__, '_apply_retries'],
|
160 |
+
'default' => 3,
|
161 |
+
],
|
162 |
+
'validate' => [
|
163 |
+
'type' => 'value',
|
164 |
+
'valid' => ['bool', 'array'],
|
165 |
+
'default' => true,
|
166 |
+
'doc' => 'Set to false to disable client-side parameter validation. Set to true to utilize default validation constraints. Set to an associative array of validation options to enable specific validation constraints.',
|
167 |
+
'fn' => [__CLASS__, '_apply_validate'],
|
168 |
+
],
|
169 |
+
'debug' => [
|
170 |
+
'type' => 'value',
|
171 |
+
'valid' => ['bool', 'array'],
|
172 |
+
'doc' => 'Set to true to display debug information when sending requests. Alternatively, you can provide an associative array with the following keys: logfn: (callable) Function that is invoked with log messages; stream_size: (int) When the size of a stream is greater than this number, the stream data will not be logged (set to "0" to not log any stream data); scrub_auth: (bool) Set to false to disable the scrubbing of auth data from the logged messages; http: (bool) Set to false to disable the "debug" feature of lower level HTTP adapters (e.g., verbose curl output).',
|
173 |
+
'fn' => [__CLASS__, '_apply_debug'],
|
174 |
+
],
|
175 |
+
'http' => [
|
176 |
+
'type' => 'value',
|
177 |
+
'valid' => ['array'],
|
178 |
+
'default' => [],
|
179 |
+
'doc' => 'Set to an array of SDK request options to apply to each request (e.g., proxy, verify, etc.).',
|
180 |
+
],
|
181 |
+
'http_handler' => [
|
182 |
+
'type' => 'value',
|
183 |
+
'valid' => ['callable'],
|
184 |
+
'doc' => 'An HTTP handler is a function that accepts a PSR-7 request object and returns a promise that is fulfilled with a PSR-7 response object or rejected with an array of exception data. NOTE: This option supersedes any provided "handler" option.',
|
185 |
+
'fn' => [__CLASS__, '_apply_http_handler']
|
186 |
+
],
|
187 |
+
'handler' => [
|
188 |
+
'type' => 'value',
|
189 |
+
'valid' => ['callable'],
|
190 |
+
'doc' => 'A handler that accepts a command object, request object and returns a promise that is fulfilled with an Aws\ResultInterface object or rejected with an Aws\Exception\AwsException. A handler does not accept a next handler as it is terminal and expected to fulfill a command. If no handler is provided, a default Guzzle handler will be utilized.',
|
191 |
+
'fn' => [__CLASS__, '_apply_handler'],
|
192 |
+
'default' => [__CLASS__, '_default_handler']
|
193 |
+
],
|
194 |
+
'ua_append' => [
|
195 |
+
'type' => 'value',
|
196 |
+
'valid' => ['string', 'array'],
|
197 |
+
'doc' => 'Provide a string or array of strings to send in the User-Agent header.',
|
198 |
+
'fn' => [__CLASS__, '_apply_user_agent'],
|
199 |
+
'default' => [],
|
200 |
+
],
|
201 |
+
'idempotency_auto_fill' => [
|
202 |
+
'type' => 'value',
|
203 |
+
'valid' => ['bool', 'callable'],
|
204 |
+
'doc' => 'Set to false to disable SDK to populate parameters that enabled \'idempotencyToken\' trait with a random UUID v4 value on your behalf. Using default value \'true\' still allows parameter value to be overwritten when provided. Note: auto-fill only works when cryptographically secure random bytes generator functions(random_bytes, openssl_random_pseudo_bytes or mcrypt_create_iv) can be found. You may also provide a callable source of random bytes.',
|
205 |
+
'default' => true,
|
206 |
+
'fn' => [__CLASS__, '_apply_idempotency_auto_fill']
|
207 |
+
],
|
208 |
+
];
|
209 |
+
|
210 |
+
/**
|
211 |
+
* Gets an array of default client arguments, each argument containing a
|
212 |
+
* hash of the following:
|
213 |
+
*
|
214 |
+
* - type: (string, required) option type described as follows:
|
215 |
+
* - value: The default option type.
|
216 |
+
* - config: The provided value is made available in the client's
|
217 |
+
* getConfig() method.
|
218 |
+
* - valid: (array, required) Valid PHP types or class names. Note: null
|
219 |
+
* is not an allowed type.
|
220 |
+
* - required: (bool, callable) Whether or not the argument is required.
|
221 |
+
* Provide a function that accepts an array of arguments and returns a
|
222 |
+
* string to provide a custom error message.
|
223 |
+
* - default: (mixed) The default value of the argument if not provided. If
|
224 |
+
* a function is provided, then it will be invoked to provide a default
|
225 |
+
* value. The function is provided the array of options and is expected
|
226 |
+
* to return the default value of the option. The default value can be a
|
227 |
+
* closure and can not be a callable string that is not part of the
|
228 |
+
* defaultArgs array.
|
229 |
+
* - doc: (string) The argument documentation string.
|
230 |
+
* - fn: (callable) Function used to apply the argument. The function
|
231 |
+
* accepts the provided value, array of arguments by reference, and an
|
232 |
+
* event emitter.
|
233 |
+
*
|
234 |
+
* Note: Order is honored and important when applying arguments.
|
235 |
+
*
|
236 |
+
* @return array
|
237 |
+
*/
|
238 |
+
public static function getDefaultArguments()
|
239 |
+
{
|
240 |
+
return self::$defaultArgs;
|
241 |
+
}
|
242 |
+
|
243 |
+
/**
|
244 |
+
* @param array $argDefinitions Client arguments.
|
245 |
+
*/
|
246 |
+
public function __construct(array $argDefinitions)
|
247 |
+
{
|
248 |
+
$this->argDefinitions = $argDefinitions;
|
249 |
+
}
|
250 |
+
|
251 |
+
/**
|
252 |
+
* Resolves client configuration options and attached event listeners.
|
253 |
+
* Check for missing keys in passed arguments
|
254 |
+
*
|
255 |
+
* @param array $args Provided constructor arguments.
|
256 |
+
* @param HandlerList $list Handler list to augment.
|
257 |
+
*
|
258 |
+
* @return array Returns the array of provided options.
|
259 |
+
* @throws \InvalidArgumentException
|
260 |
+
* @see Aws\AwsClient::__construct for a list of available options.
|
261 |
+
*/
|
262 |
+
public function resolve(array $args, HandlerList $list)
|
263 |
+
{
|
264 |
+
$args['config'] = [];
|
265 |
+
foreach ($this->argDefinitions as $key => $a) {
|
266 |
+
// Add defaults, validate required values, and skip if not set.
|
267 |
+
if (!isset($args[$key])) {
|
268 |
+
if (isset($a['default'])) {
|
269 |
+
// Merge defaults in when not present.
|
270 |
+
if (is_callable($a['default'])
|
271 |
+
&& (
|
272 |
+
is_array($a['default'])
|
273 |
+
|| $a['default'] instanceof \Closure
|
274 |
+
)
|
275 |
+
) {
|
276 |
+
$args[$key] = $a['default']($args);
|
277 |
+
} else {
|
278 |
+
$args[$key] = $a['default'];
|
279 |
+
}
|
280 |
+
} elseif (empty($a['required'])) {
|
281 |
+
continue;
|
282 |
+
} else {
|
283 |
+
$this->throwRequired($args);
|
284 |
+
}
|
285 |
+
}
|
286 |
+
|
287 |
+
// Validate the types against the provided value.
|
288 |
+
foreach ($a['valid'] as $check) {
|
289 |
+
if (isset(self::$typeMap[$check])) {
|
290 |
+
$fn = self::$typeMap[$check];
|
291 |
+
if ($fn($args[$key])) {
|
292 |
+
goto is_valid;
|
293 |
+
}
|
294 |
+
} elseif ($args[$key] instanceof $check) {
|
295 |
+
goto is_valid;
|
296 |
+
}
|
297 |
+
}
|
298 |
+
|
299 |
+
$this->invalidType($key, $args[$key]);
|
300 |
+
|
301 |
+
// Apply the value
|
302 |
+
is_valid:
|
303 |
+
if (isset($a['fn'])) {
|
304 |
+
$a['fn']($args[$key], $args, $list);
|
305 |
+
}
|
306 |
+
|
307 |
+
if ($a['type'] === 'config') {
|
308 |
+
$args['config'][$key] = $args[$key];
|
309 |
+
}
|
310 |
+
}
|
311 |
+
|
312 |
+
return $args;
|
313 |
+
}
|
314 |
+
|
315 |
+
/**
|
316 |
+
* Creates a verbose error message for an invalid argument.
|
317 |
+
*
|
318 |
+
* @param string $name Name of the argument that is missing.
|
319 |
+
* @param array $args Provided arguments
|
320 |
+
* @param bool $useRequired Set to true to show the required fn text if
|
321 |
+
* available instead of the documentation.
|
322 |
+
* @return string
|
323 |
+
*/
|
324 |
+
private function getArgMessage($name, $args = [], $useRequired = false)
|
325 |
+
{
|
326 |
+
$arg = $this->argDefinitions[$name];
|
327 |
+
$msg = '';
|
328 |
+
$modifiers = [];
|
329 |
+
if (isset($arg['valid'])) {
|
330 |
+
$modifiers[] = implode('|', $arg['valid']);
|
331 |
+
}
|
332 |
+
if (isset($arg['choice'])) {
|
333 |
+
$modifiers[] = 'One of ' . implode(', ', $arg['choice']);
|
334 |
+
}
|
335 |
+
if ($modifiers) {
|
336 |
+
$msg .= '(' . implode('; ', $modifiers) . ')';
|
337 |
+
}
|
338 |
+
$msg = wordwrap("{$name}: {$msg}", 75, "\n ");
|
339 |
+
|
340 |
+
if ($useRequired && is_callable($arg['required'])) {
|
341 |
+
$msg .= "\n\n ";
|
342 |
+
$msg .= str_replace("\n", "\n ", call_user_func($arg['required'], $args));
|
343 |
+
} elseif (isset($arg['doc'])) {
|
344 |
+
$msg .= wordwrap("\n\n {$arg['doc']}", 75, "\n ");
|
345 |
+
}
|
346 |
+
|
347 |
+
return $msg;
|
348 |
+
}
|
349 |
+
|
350 |
+
/**
|
351 |
+
* Throw when an invalid type is encountered.
|
352 |
+
*
|
353 |
+
* @param string $name Name of the value being validated.
|
354 |
+
* @param mixed $provided The provided value.
|
355 |
+
* @throws \InvalidArgumentException
|
356 |
+
*/
|
357 |
+
private function invalidType($name, $provided)
|
358 |
+
{
|
359 |
+
$expected = implode('|', $this->argDefinitions[$name]['valid']);
|
360 |
+
$msg = "Invalid configuration value "
|
361 |
+
. "provided for \"{$name}\". Expected {$expected}, but got "
|
362 |
+
. describe_type($provided) . "\n\n"
|
363 |
+
. $this->getArgMessage($name);
|
364 |
+
throw new IAE($msg);
|
365 |
+
}
|
366 |
+
|
367 |
+
/**
|
368 |
+
* Throws an exception for missing required arguments.
|
369 |
+
*
|
370 |
+
* @param array $args Passed in arguments.
|
371 |
+
* @throws \InvalidArgumentException
|
372 |
+
*/
|
373 |
+
private function throwRequired(array $args)
|
374 |
+
{
|
375 |
+
$missing = [];
|
376 |
+
foreach ($this->argDefinitions as $k => $a) {
|
377 |
+
if (empty($a['required'])
|
378 |
+
|| isset($a['default'])
|
379 |
+
|| isset($args[$k])
|
380 |
+
) {
|
381 |
+
continue;
|
382 |
+
}
|
383 |
+
$missing[] = $this->getArgMessage($k, $args, true);
|
384 |
+
}
|
385 |
+
$msg = "Missing required client configuration options: \n\n";
|
386 |
+
$msg .= implode("\n\n", $missing);
|
387 |
+
throw new IAE($msg);
|
388 |
+
}
|
389 |
+
|
390 |
+
public static function _apply_retries($value, array &$args, HandlerList $list)
|
391 |
+
{
|
392 |
+
if ($value) {
|
393 |
+
$decider = RetryMiddleware::createDefaultDecider($value);
|
394 |
+
$list->appendSign(
|
395 |
+
Middleware::retry($decider, null, $args['stats']['retries']),
|
396 |
+
'retry'
|
397 |
+
);
|
398 |
+
}
|
399 |
+
}
|
400 |
+
|
401 |
+
public static function _apply_credentials($value, array &$args)
|
402 |
+
{
|
403 |
+
if (is_callable($value)) {
|
404 |
+
return;
|
405 |
+
}
|
406 |
+
|
407 |
+
if ($value instanceof CredentialsInterface) {
|
408 |
+
$args['credentials'] = CredentialProvider::fromCredentials($value);
|
409 |
+
} elseif (is_array($value)
|
410 |
+
&& isset($value['key'])
|
411 |
+
&& isset($value['secret'])
|
412 |
+
) {
|
413 |
+
$args['credentials'] = CredentialProvider::fromCredentials(
|
414 |
+
new Credentials(
|
415 |
+
$value['key'],
|
416 |
+
$value['secret'],
|
417 |
+
isset($value['token']) ? $value['token'] : null,
|
418 |
+
isset($value['expires']) ? $value['expires'] : null
|
419 |
+
)
|
420 |
+
);
|
421 |
+
} elseif ($value === false) {
|
422 |
+
$args['credentials'] = CredentialProvider::fromCredentials(
|
423 |
+
new Credentials('', '')
|
424 |
+
);
|
425 |
+
$args['config']['signature_version'] = 'anonymous';
|
426 |
+
} elseif ($value instanceof CacheInterface) {
|
427 |
+
$args['credentials'] = CredentialProvider::defaultProvider($args);
|
428 |
+
} else {
|
429 |
+
throw new IAE('Credentials must be an instance of '
|
430 |
+
. 'Aws\Credentials\CredentialsInterface, an associative '
|
431 |
+
. 'array that contains "key", "secret", and an optional "token" '
|
432 |
+
. 'key-value pairs, a credentials provider function, or false.');
|
433 |
+
}
|
434 |
+
}
|
435 |
+
|
436 |
+
public static function _apply_api_provider(callable $value, array &$args)
|
437 |
+
{
|
438 |
+
$api = new Service(
|
439 |
+
ApiProvider::resolve(
|
440 |
+
$value,
|
441 |
+
'api',
|
442 |
+
$args['service'],
|
443 |
+
$args['version']
|
444 |
+
),
|
445 |
+
$value
|
446 |
+
);
|
447 |
+
|
448 |
+
if (
|
449 |
+
empty($args['config']['signing_name'])
|
450 |
+
&& isset($api['metadata']['signingName'])
|
451 |
+
) {
|
452 |
+
$args['config']['signing_name'] = $api['metadata']['signingName'];
|
453 |
+
}
|
454 |
+
|
455 |
+
$args['api'] = $api;
|
456 |
+
$args['parser'] = Service::createParser($api);
|
457 |
+
$args['error_parser'] = Service::createErrorParser($api->getProtocol());
|
458 |
+
}
|
459 |
+
|
460 |
+
public static function _apply_endpoint_provider(callable $value, array &$args)
|
461 |
+
{
|
462 |
+
if (!isset($args['endpoint'])) {
|
463 |
+
$endpointPrefix = isset($args['api']['metadata']['endpointPrefix'])
|
464 |
+
? $args['api']['metadata']['endpointPrefix']
|
465 |
+
: $args['service'];
|
466 |
+
|
467 |
+
// Invoke the endpoint provider and throw if it does not resolve.
|
468 |
+
$result = EndpointProvider::resolve($value, [
|
469 |
+
'service' => $endpointPrefix,
|
470 |
+
'region' => $args['region'],
|
471 |
+
'scheme' => $args['scheme']
|
472 |
+
]);
|
473 |
+
|
474 |
+
$args['endpoint'] = $result['endpoint'];
|
475 |
+
|
476 |
+
if (
|
477 |
+
empty($args['config']['signature_version'])
|
478 |
+
&& isset($result['signatureVersion'])
|
479 |
+
) {
|
480 |
+
$args['config']['signature_version']
|
481 |
+
= $result['signatureVersion'];
|
482 |
+
}
|
483 |
+
|
484 |
+
if (
|
485 |
+
empty($args['config']['signing_region'])
|
486 |
+
&& isset($result['signingRegion'])
|
487 |
+
) {
|
488 |
+
$args['config']['signing_region'] = $result['signingRegion'];
|
489 |
+
}
|
490 |
+
|
491 |
+
if (
|
492 |
+
empty($args['config']['signing_name'])
|
493 |
+
&& isset($result['signingName'])
|
494 |
+
) {
|
495 |
+
$args['config']['signing_name'] = $result['signingName'];
|
496 |
+
}
|
497 |
+
}
|
498 |
+
}
|
499 |
+
|
500 |
+
public static function _apply_endpoint_discovery($value, array &$args) {
|
501 |
+
$args['endpoint_discovery'] = $value;
|
502 |
+
}
|
503 |
+
|
504 |
+
public static function _default_endpoint_discovery_provider(array $args)
|
505 |
+
{
|
506 |
+
return ConfigurationProvider::defaultProvider($args);
|
507 |
+
}
|
508 |
+
|
509 |
+
public static function _apply_serializer($value, array &$args, HandlerList $list)
|
510 |
+
{
|
511 |
+
$list->prependBuild(Middleware::requestBuilder($value), 'builder');
|
512 |
+
}
|
513 |
+
|
514 |
+
public static function _apply_debug($value, array &$args, HandlerList $list)
|
515 |
+
{
|
516 |
+
if ($value !== false) {
|
517 |
+
$list->interpose(new TraceMiddleware($value === true ? [] : $value));
|
518 |
+
}
|
519 |
+
}
|
520 |
+
|
521 |
+
public static function _apply_stats($value, array &$args, HandlerList $list)
|
522 |
+
{
|
523 |
+
// Create an array of stat collectors that are disabled (set to false)
|
524 |
+
// by default. If the user has passed in true, enable all stat
|
525 |
+
// collectors.
|
526 |
+
$defaults = array_fill_keys(
|
527 |
+
['http', 'retries', 'timer'],
|
528 |
+
$value === true
|
529 |
+
);
|
530 |
+
$args['stats'] = is_array($value)
|
531 |
+
? array_replace($defaults, $value)
|
532 |
+
: $defaults;
|
533 |
+
|
534 |
+
if ($args['stats']['timer']) {
|
535 |
+
$list->prependInit(Middleware::timer(), 'timer');
|
536 |
+
}
|
537 |
+
}
|
538 |
+
|
539 |
+
public static function _apply_profile($_, array &$args)
|
540 |
+
{
|
541 |
+
$args['credentials'] = CredentialProvider::ini($args['profile']);
|
542 |
+
}
|
543 |
+
|
544 |
+
public static function _apply_validate($value, array &$args, HandlerList $list)
|
545 |
+
{
|
546 |
+
if ($value === false) {
|
547 |
+
return;
|
548 |
+
}
|
549 |
+
|
550 |
+
$validator = $value === true
|
551 |
+
? new Validator()
|
552 |
+
: new Validator($value);
|
553 |
+
$list->appendValidate(
|
554 |
+
Middleware::validation($args['api'], $validator),
|
555 |
+
'validation'
|
556 |
+
);
|
557 |
+
}
|
558 |
+
|
559 |
+
public static function _apply_handler($value, array &$args, HandlerList $list)
|
560 |
+
{
|
561 |
+
$list->setHandler($value);
|
562 |
+
}
|
563 |
+
|
564 |
+
public static function _default_handler(array &$args)
|
565 |
+
{
|
566 |
+
return new WrappedHttpHandler(
|
567 |
+
default_http_handler(),
|
568 |
+
$args['parser'],
|
569 |
+
$args['error_parser'],
|
570 |
+
$args['exception_class'],
|
571 |
+
$args['stats']['http']
|
572 |
+
);
|
573 |
+
}
|
574 |
+
|
575 |
+
public static function _apply_http_handler($value, array &$args, HandlerList $list)
|
576 |
+
{
|
577 |
+
$args['handler'] = new WrappedHttpHandler(
|
578 |
+
$value,
|
579 |
+
$args['parser'],
|
580 |
+
$args['error_parser'],
|
581 |
+
$args['exception_class'],
|
582 |
+
$args['stats']['http']
|
583 |
+
);
|
584 |
+
}
|
585 |
+
|
586 |
+
public static function _apply_user_agent($value, array &$args, HandlerList $list)
|
587 |
+
{
|
588 |
+
if (!is_array($value)) {
|
589 |
+
$value = [$value];
|
590 |
+
}
|
591 |
+
|
592 |
+
$value = array_map('strval', $value);
|
593 |
+
|
594 |
+
if (defined('HHVM_VERSION')) {
|
595 |
+
array_unshift($value, 'HHVM/' . HHVM_VERSION);
|
596 |
+
}
|
597 |
+
array_unshift($value, 'aws-sdk-php/' . Sdk::VERSION);
|
598 |
+
$args['ua_append'] = $value;
|
599 |
+
|
600 |
+
$list->appendBuild(static function (callable $handler) use ($value) {
|
601 |
+
return function (
|
602 |
+
CommandInterface $command,
|
603 |
+
RequestInterface $request
|
604 |
+
) use ($handler, $value) {
|
605 |
+
return $handler($command, $request->withHeader(
|
606 |
+
'User-Agent',
|
607 |
+
implode(' ', array_merge(
|
608 |
+
$value,
|
609 |
+
$request->getHeader('User-Agent')
|
610 |
+
))
|
611 |
+
));
|
612 |
+
};
|
613 |
+
});
|
614 |
+
}
|
615 |
+
|
616 |
+
public static function _apply_endpoint($value, array &$args, HandlerList $list)
|
617 |
+
{
|
618 |
+
$parts = parse_url($value);
|
619 |
+
if (empty($parts['scheme']) || empty($parts['host'])) {
|
620 |
+
throw new IAE(
|
621 |
+
'Endpoints must be full URIs and include a scheme and host'
|
622 |
+
);
|
623 |
+
}
|
624 |
+
|
625 |
+
$args['endpoint'] = $value;
|
626 |
+
}
|
627 |
+
|
628 |
+
public static function _apply_idempotency_auto_fill(
|
629 |
+
$value,
|
630 |
+
array &$args,
|
631 |
+
HandlerList $list
|
632 |
+
) {
|
633 |
+
$enabled = false;
|
634 |
+
$generator = null;
|
635 |
+
|
636 |
+
|
637 |
+
if (is_bool($value)) {
|
638 |
+
$enabled = $value;
|
639 |
+
} elseif (is_callable($value)) {
|
640 |
+
$enabled = true;
|
641 |
+
$generator = $value;
|
642 |
+
}
|
643 |
+
|
644 |
+
if ($enabled) {
|
645 |
+
$list->prependInit(
|
646 |
+
IdempotencyTokenMiddleware::wrap($args['api'], $generator),
|
647 |
+
'idempotency_auto_fill'
|
648 |
+
);
|
649 |
+
}
|
650 |
+
}
|
651 |
+
|
652 |
+
public static function _default_endpoint_provider(array $args)
|
653 |
+
{
|
654 |
+
return PartitionEndpointProvider::defaultProvider()
|
655 |
+
->getPartition($args['region'], $args['service']);
|
656 |
+
}
|
657 |
+
|
658 |
+
public static function _default_serializer(array $args)
|
659 |
+
{
|
660 |
+
return Service::createSerializer(
|
661 |
+
$args['api'],
|
662 |
+
$args['endpoint']
|
663 |
+
);
|
664 |
+
}
|
665 |
+
|
666 |
+
public static function _default_signature_provider()
|
667 |
+
{
|
668 |
+
return SignatureProvider::defaultProvider();
|
669 |
+
}
|
670 |
+
|
671 |
+
public static function _default_signature_version(array &$args)
|
672 |
+
{
|
673 |
+
if (isset($args['config']['signature_version'])) {
|
674 |
+
return $args['config']['signature_version'];
|
675 |
+
}
|
676 |
+
|
677 |
+
$args['__partition_result'] = isset($args['__partition_result'])
|
678 |
+
? isset($args['__partition_result'])
|
679 |
+
: call_user_func(PartitionEndpointProvider::defaultProvider(), [
|
680 |
+
'service' => $args['service'],
|
681 |
+
'region' => $args['region'],
|
682 |
+
]);
|
683 |
+
|
684 |
+
return isset($args['__partition_result']['signatureVersion'])
|
685 |
+
? $args['__partition_result']['signatureVersion']
|
686 |
+
: $args['api']->getSignatureVersion();
|
687 |
+
}
|
688 |
+
|
689 |
+
public static function _default_signing_name(array &$args)
|
690 |
+
{
|
691 |
+
if (isset($args['config']['signing_name'])) {
|
692 |
+
return $args['config']['signing_name'];
|
693 |
+
}
|
694 |
+
|
695 |
+
$args['__partition_result'] = isset($args['__partition_result'])
|
696 |
+
? isset($args['__partition_result'])
|
697 |
+
: call_user_func(PartitionEndpointProvider::defaultProvider(), [
|
698 |
+
'service' => $args['service'],
|
699 |
+
'region' => $args['region'],
|
700 |
+
]);
|
701 |
+
|
702 |
+
if (isset($args['__partition_result']['signingName'])) {
|
703 |
+
return $args['__partition_result']['signingName'];
|
704 |
+
}
|
705 |
+
|
706 |
+
if ($signingName = $args['api']->getSigningName()) {
|
707 |
+
return $signingName;
|
708 |
+
}
|
709 |
+
|
710 |
+
return $args['service'];
|
711 |
+
}
|
712 |
+
|
713 |
+
public static function _default_signing_region(array &$args)
|
714 |
+
{
|
715 |
+
if (isset($args['config']['signing_region'])) {
|
716 |
+
return $args['config']['signing_region'];
|
717 |
+
}
|
718 |
+
|
719 |
+
$args['__partition_result'] = isset($args['__partition_result'])
|
720 |
+
? isset($args['__partition_result'])
|
721 |
+
: call_user_func(PartitionEndpointProvider::defaultProvider(), [
|
722 |
+
'service' => $args['service'],
|
723 |
+
'region' => $args['region'],
|
724 |
+
]);
|
725 |
+
|
726 |
+
return isset($args['__partition_result']['signingRegion'])
|
727 |
+
? $args['__partition_result']['signingRegion']
|
728 |
+
: $args['region'];
|
729 |
+
}
|
730 |
+
|
731 |
+
public static function _missing_version(array $args)
|
732 |
+
{
|
733 |
+
$service = isset($args['service']) ? $args['service'] : '';
|
734 |
+
$versions = ApiProvider::defaultProvider()->getVersions($service);
|
735 |
+
$versions = implode("\n", array_map(function ($v) {
|
736 |
+
return "* \"$v\"";
|
737 |
+
}, $versions)) ?: '* (none found)';
|
738 |
+
|
739 |
+
return <<<EOT
|
740 |
+
A "version" configuration value is required. Specifying a version constraint
|
741 |
+
ensures that your code will not be affected by a breaking change made to the
|
742 |
+
service. For example, when using Amazon S3, you can lock your API version to
|
743 |
+
"2006-03-01".
|
744 |
+
|
745 |
+
Your build of the SDK has the following version(s) of "{$service}": {$versions}
|
746 |
+
|
747 |
+
You may provide "latest" to the "version" configuration value to utilize the
|
748 |
+
most recent available API version that your client's API provider can find.
|
749 |
+
Note: Using 'latest' in a production application is not recommended.
|
750 |
+
|
751 |
+
A list of available API versions can be found on each client's API documentation
|
752 |
+
page: http://docs.aws.amazon.com/aws-sdk-php/v3/api/index.html. If you are
|
753 |
+
unable to load a specific API version, then you may need to update your copy of
|
754 |
+
the SDK.
|
755 |
+
EOT;
|
756 |
+
}
|
757 |
+
|
758 |
+
public static function _missing_region(array $args)
|
759 |
+
{
|
760 |
+
$service = isset($args['service']) ? $args['service'] : '';
|
761 |
+
|
762 |
+
return <<<EOT
|
763 |
+
A "region" configuration value is required for the "{$service}" service
|
764 |
+
(e.g., "us-west-2"). A list of available public regions and endpoints can be
|
765 |
+
found at http://docs.aws.amazon.com/general/latest/gr/rande.html.
|
766 |
+
EOT;
|
767 |
+
}
|
768 |
+
}
|
lib/Aws/Aws/ClientSideMonitoring/AbstractMonitoringMiddleware.php
ADDED
@@ -0,0 +1,275 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Aws\ClientSideMonitoring;
|
4 |
+
|
5 |
+
use Aws\CommandInterface;
|
6 |
+
use Aws\Exception\AwsException;
|
7 |
+
use Aws\MonitoringEventsInterface;
|
8 |
+
use Aws\ResponseContainerInterface;
|
9 |
+
use Aws\ResultInterface;
|
10 |
+
use GuzzleHttp\Promise;
|
11 |
+
use Psr\Http\Message\RequestInterface;
|
12 |
+
use Psr\Http\Message\ResponseInterface;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* @internal
|
16 |
+
*/
|
17 |
+
abstract class AbstractMonitoringMiddleware
|
18 |
+
implements MonitoringMiddlewareInterface
|
19 |
+
{
|
20 |
+
private static $socket;
|
21 |
+
|
22 |
+
private $nextHandler;
|
23 |
+
private $options;
|
24 |
+
protected $credentialProvider;
|
25 |
+
protected $region;
|
26 |
+
protected $service;
|
27 |
+
|
28 |
+
protected static function getAwsExceptionHeader(AwsException $e, $headerName)
|
29 |
+
{
|
30 |
+
$response = $e->getResponse();
|
31 |
+
if ($response !== null) {
|
32 |
+
$header = $response->getHeader($headerName);
|
33 |
+
if (!empty($header[0])) {
|
34 |
+
return $header[0];
|
35 |
+
}
|
36 |
+
}
|
37 |
+
return null;
|
38 |
+
}
|
39 |
+
|
40 |
+
protected static function getResultHeader(ResultInterface $result, $headerName)
|
41 |
+
{
|
42 |
+
if (isset($result['@metadata']['headers'][$headerName])) {
|
43 |
+
return $result['@metadata']['headers'][$headerName];
|
44 |
+
}
|
45 |
+
return null;
|
46 |
+
}
|
47 |
+
|
48 |
+
protected static function getExceptionHeader(\Exception $e, $headerName)
|
49 |
+
{
|
50 |
+
if ($e instanceof ResponseContainerInterface) {
|
51 |
+
$response = $e->getResponse();
|
52 |
+
if ($response instanceof ResponseInterface) {
|
53 |
+
$header = $response->getHeader($headerName);
|
54 |
+
if (!empty($header[0])) {
|
55 |
+
return $header[0];
|
56 |
+
}
|
57 |
+
}
|
58 |
+
}
|
59 |
+
return null;
|
60 |
+
}
|
61 |
+
|
62 |
+
/**
|
63 |
+
* Constructor stores the passed in handler and options.
|
64 |
+
*
|
65 |
+
* @param callable $handler
|
66 |
+
* @param callable $credentialProvider
|
67 |
+
* @param array $options
|
68 |
+
* @param $region
|
69 |
+
* @param $service
|
70 |
+
*/
|
71 |
+
public function __construct(
|
72 |
+
callable $handler,
|
73 |
+
callable $credentialProvider,
|
74 |
+
$options,
|
75 |
+
$region,
|
76 |
+
$service
|
77 |
+
) {
|
78 |
+
$this->nextHandler = $handler;
|
79 |
+
$this->credentialProvider = $credentialProvider;
|
80 |
+
$this->options = $options;
|
81 |
+
$this->region = $region;
|
82 |
+
$this->service = $service;
|
83 |
+
}
|
84 |
+
|
85 |
+
/**
|
86 |
+
* Standard invoke pattern for middleware execution to be implemented by
|
87 |
+
* child classes.
|
88 |
+
*
|
89 |
+
* @param CommandInterface $cmd
|
90 |
+
* @param RequestInterface $request
|
91 |
+
* @return Promise\PromiseInterface
|
92 |
+
*/
|
93 |
+
public function __invoke(CommandInterface $cmd, RequestInterface $request)
|
94 |
+
{
|
95 |
+
$handler = $this->nextHandler;
|
96 |
+
$eventData = null;
|
97 |
+
$enabled = $this->isEnabled();
|
98 |
+
|
99 |
+
if ($enabled) {
|
100 |
+
$cmd['@http']['collect_stats'] = true;
|
101 |
+
$eventData = $this->populateRequestEventData(
|
102 |
+
$cmd,
|
103 |
+
$request,
|
104 |
+
$this->getNewEvent($cmd, $request)
|
105 |
+
);
|
106 |
+
}
|
107 |
+
|
108 |
+
$g = function ($value) use ($eventData, $enabled) {
|
109 |
+
if ($enabled) {
|
110 |
+
$eventData = $this->populateResultEventData(
|
111 |
+
$value,
|
112 |
+
$eventData
|
113 |
+
);
|
114 |
+
$this->sendEventData($eventData);
|
115 |
+
|
116 |
+
if ($value instanceof MonitoringEventsInterface) {
|
117 |
+
$value->appendMonitoringEvent($eventData);
|
118 |
+
}
|
119 |
+
}
|
120 |
+
if ($value instanceof \Exception || $value instanceof \Throwable) {
|
121 |
+
return Promise\rejection_for($value);
|
122 |
+
}
|
123 |
+
return $value;
|
124 |
+
};
|
125 |
+
|
126 |
+
return Promise\promise_for($handler($cmd, $request))->then($g, $g);
|
127 |
+
}
|
128 |
+
|
129 |
+
private function getClientId()
|
130 |
+
{
|
131 |
+
return $this->unwrappedOptions()->getClientId();
|
132 |
+
}
|
133 |
+
|
134 |
+
private function getNewEvent(
|
135 |
+
CommandInterface $cmd,
|
136 |
+
RequestInterface $request
|
137 |
+
) {
|
138 |
+
$event = [
|
139 |
+
'Api' => $cmd->getName(),
|
140 |
+
'ClientId' => $this->getClientId(),
|
141 |
+
'Region' => $this->getRegion(),
|
142 |
+
'Service' => $this->getService(),
|
143 |
+
'Timestamp' => (int) floor(microtime(true) * 1000),
|
144 |
+
'UserAgent' => substr(
|
145 |
+
$request->getHeaderLine('User-Agent') . ' ' . \Aws\default_user_agent(),
|
146 |
+
0,
|
147 |
+
256
|
148 |
+
),
|
149 |
+
'Version' => 1
|
150 |
+
];
|
151 |
+
return $event;
|
152 |
+
}
|
153 |
+
|
154 |
+
private function getPort()
|
155 |
+
{
|
156 |
+
return $this->unwrappedOptions()->getPort();
|
157 |
+
}
|
158 |
+
|
159 |
+
private function getRegion()
|
160 |
+
{
|
161 |
+
return $this->region;
|
162 |
+
}
|
163 |
+
|
164 |
+
private function getService()
|
165 |
+
{
|
166 |
+
return $this->service;
|
167 |
+
}
|
168 |
+
|
169 |
+
/**
|
170 |
+
* Returns enabled flag from options, unwrapping options if necessary.
|
171 |
+
*
|
172 |
+
* @return bool
|
173 |
+
*/
|
174 |
+
private function isEnabled()
|
175 |
+
{
|
176 |
+
return $this->unwrappedOptions()->isEnabled();
|
177 |
+
}
|
178 |
+
|
179 |
+
/**
|
180 |
+
* Returns $eventData array with information from the request and command.
|
181 |
+
*
|
182 |
+
* @param CommandInterface $cmd
|
183 |
+
* @param RequestInterface $request
|
184 |
+
* @param array $event
|
185 |
+
* @return array
|
186 |
+
*/
|
187 |
+
protected function populateRequestEventData(
|
188 |
+
CommandInterface $cmd,
|
189 |
+
RequestInterface $request,
|
190 |
+
array $event
|
191 |
+
) {
|
192 |
+
$dataFormat = static::getRequestData($request);
|
193 |
+
foreach ($dataFormat as $eventKey => $value) {
|
194 |
+
if ($value !== null) {
|
195 |
+
$event[$eventKey] = $value;
|
196 |
+
}
|
197 |
+
}
|
198 |
+
return $event;
|
199 |
+
}
|
200 |
+
|
201 |
+
/**
|
202 |
+
* Returns $eventData array with information from the response, including
|
203 |
+
* the calculation for attempt latency.
|
204 |
+
*
|
205 |
+
* @param ResultInterface|\Exception $result
|
206 |
+
* @param array $event
|
207 |
+
* @return array
|
208 |
+
*/
|
209 |
+
protected function populateResultEventData(
|
210 |
+
$result,
|
211 |
+
array $event
|
212 |
+
) {
|
213 |
+
$dataFormat = static::getResponseData($result);
|
214 |
+
foreach ($dataFormat as $eventKey => $value) {
|
215 |
+
if ($value !== null) {
|
216 |
+
$event[$eventKey] = $value;
|
217 |
+
}
|
218 |
+
}
|
219 |
+
return $event;
|
220 |
+
}
|
221 |
+
|
222 |
+
/**
|
223 |
+
* Creates a UDP socket resource and stores it with the class, or retrieves
|
224 |
+
* it if already instantiated and connected. Handles error-checking and
|
225 |
+
* re-connecting if necessary. If $forceNewConnection is set to true, a new
|
226 |
+
* socket will be created.
|
227 |
+
*
|
228 |
+
* @param bool $forceNewConnection
|
229 |
+
* @return Resource
|
230 |
+
*/
|
231 |
+
private function prepareSocket($forceNewConnection = false)
|
232 |
+
{
|
233 |
+
if (!is_resource(self::$socket)
|
234 |
+
|| $forceNewConnection
|
235 |
+
|| socket_last_error(self::$socket)
|
236 |
+
) {
|
237 |
+
self::$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
|
238 |
+
socket_clear_error(self::$socket);
|
239 |
+
socket_connect(self::$socket, '127.0.0.1', $this->getPort());
|
240 |
+
}
|
241 |
+
|
242 |
+
return self::$socket;
|
243 |
+
}
|
244 |
+
|
245 |
+
/**
|
246 |
+
* Sends formatted monitoring event data via the UDP socket connection to
|
247 |
+
* the CSM agent endpoint.
|
248 |
+
*
|
249 |
+
* @param array $eventData
|
250 |
+
* @return int
|
251 |
+
*/
|
252 |
+
private function sendEventData(array $eventData)
|
253 |
+
{
|
254 |
+
$socket = $this->prepareSocket();
|
255 |
+
$datagram = json_encode($eventData);
|
256 |
+
$result = socket_write($socket, $datagram, strlen($datagram));
|
257 |
+
if ($result === false) {
|
258 |
+
$this->prepareSocket(true);
|
259 |
+
}
|
260 |
+
return $result;
|
261 |
+
}
|
262 |
+
|
263 |
+
/**
|
264 |
+
* Unwraps options, if needed, and returns them.
|
265 |
+
*
|
266 |
+
* @return ConfigurationInterface
|
267 |
+
*/
|
268 |
+
private function unwrappedOptions()
|
269 |
+
{
|
270 |
+
if (!($this->options instanceof ConfigurationInterface)) {
|
271 |
+
$this->options = ConfigurationProvider::unwrap($this->options);
|
272 |
+
}
|
273 |
+
return $this->options;
|
274 |
+
}
|
275 |
+
}
|
lib/Aws/Aws/ClientSideMonitoring/ApiCallAttemptMonitoringMiddleware.php
ADDED
@@ -0,0 +1,262 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Aws\ClientSideMonitoring;
|
4 |
+
|
5 |
+
use Aws\CommandInterface;
|
6 |
+
use Aws\Credentials\CredentialsInterface;
|
7 |
+
use Aws\Exception\AwsException;
|
8 |
+
use Aws\ResponseContainerInterface;
|
9 |
+
use Aws\ResultInterface;
|
10 |
+
use Psr\Http\Message\RequestInterface;
|
11 |
+
use Psr\Http\Message\ResponseInterface;
|
12 |
+
|
13 |
+
/**
|
14 |
+
* @internal
|
15 |
+
*/
|
16 |
+
class ApiCallAttemptMonitoringMiddleware extends AbstractMonitoringMiddleware
|
17 |
+
{
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Standard middleware wrapper function with CSM options passed in.
|
21 |
+
*
|
22 |
+
* @param callable $credentialProvider
|
23 |
+
* @param mixed $options
|
24 |
+
* @param string $region
|
25 |
+
* @param string $service
|
26 |
+
* @return callable
|
27 |
+
*/
|
28 |
+
public static function wrap(
|
29 |
+
callable $credentialProvider,
|
30 |
+
$options,
|
31 |
+
$region,
|
32 |
+
$service
|
33 |
+
) {
|
34 |
+
return function (callable $handler) use (
|
35 |
+
$credentialProvider,
|
36 |
+
$options,
|
37 |
+
$region,
|
38 |
+
$service
|
39 |
+
) {
|
40 |
+
return new static(
|
41 |
+
$handler,
|
42 |
+
$credentialProvider,
|
43 |
+
$options,
|
44 |
+
$region,
|
45 |
+
$service
|
46 |
+
);
|
47 |
+
};
|
48 |
+
}
|
49 |
+
|
50 |
+
/**
|
51 |
+
* {@inheritdoc}
|
52 |
+
*/
|
53 |
+
public static function getRequestData(RequestInterface $request)
|
54 |
+
{
|
55 |
+
return [
|
56 |
+
'Fqdn' => $request->getUri()->getHost(),
|
57 |
+
];
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* {@inheritdoc}
|
62 |
+
*/
|
63 |
+
public static function getResponseData($klass)
|
64 |
+
{
|
65 |
+
if ($klass instanceof ResultInterface) {
|
66 |
+
return [
|
67 |
+
'AttemptLatency' => self::getResultAttemptLatency($klass),
|
68 |
+
'DestinationIp' => self::getResultDestinationIp($klass),
|
69 |
+
'DnsLatency' => self::getResultDnsLatency($klass),
|
70 |
+
'HttpStatusCode' => self::getResultHttpStatusCode($klass),
|
71 |
+
'XAmzId2' => self::getResultHeader($klass, 'x-amz-id-2'),
|
72 |
+
'XAmzRequestId' => self::getResultHeader($klass, 'x-amz-request-id'),
|
73 |
+
'XAmznRequestId' => self::getResultHeader($klass, 'x-amzn-RequestId'),
|
74 |
+
];
|
75 |
+
}
|
76 |
+
if ($klass instanceof AwsException) {
|
77 |
+
return [
|
78 |
+
'AttemptLatency' => self::getAwsExceptionAttemptLatency($klass),
|
79 |
+
'AwsException' => substr(
|
80 |
+
self::getAwsExceptionErrorCode($klass),
|
81 |
+
0,
|
82 |
+
128
|
83 |
+
),
|
84 |
+
'AwsExceptionMessage' => substr(
|
85 |
+
self::getAwsExceptionMessage($klass),
|
86 |
+
0,
|
87 |
+
512
|
88 |
+
),
|
89 |
+
'DestinationIp' => self::getAwsExceptionDestinationIp($klass),
|
90 |
+
'DnsLatency' => self::getAwsExceptionDnsLatency($klass),
|
91 |
+
'HttpStatusCode' => self::getAwsExceptionHttpStatusCode($klass),
|
92 |
+
'XAmzId2' => self::getAwsExceptionHeader($klass, 'x-amz-id-2'),
|
93 |
+
'XAmzRequestId' => self::getAwsExceptionHeader(
|
94 |
+
$klass,
|
95 |
+
'x-amz-request-id'
|
96 |
+
),
|
97 |
+
'XAmznRequestId' => self::getAwsExceptionHeader(
|
98 |
+
$klass,
|
99 |
+
'x-amzn-RequestId'
|
100 |
+
),
|
101 |
+
];
|
102 |
+
}
|
103 |
+
if ($klass instanceof \Exception) {
|
104 |
+
return [
|
105 |
+
'HttpStatusCode' => self::getExceptionHttpStatusCode($klass),
|
106 |
+
'SdkException' => substr(
|
107 |
+
self::getExceptionCode($klass),
|
108 |
+
0,
|
109 |
+
128
|
110 |
+
),
|
111 |
+
'SdkExceptionMessage' => substr(
|
112 |
+
self::getExceptionMessage($klass),
|
113 |
+
0,
|
114 |
+
512
|
115 |
+
),
|
116 |
+
'XAmzId2' => self::getExceptionHeader($klass, 'x-amz-id-2'),
|
117 |
+
'XAmzRequestId' => self::getExceptionHeader($klass, 'x-amz-request-id'),
|
118 |
+
'XAmznRequestId' => self::getExceptionHeader($klass, 'x-amzn-RequestId'),
|
119 |
+
];
|
120 |
+
}
|
121 |
+
|
122 |
+
throw new \InvalidArgumentException('Parameter must be an instance of ResultInterface, AwsException or Exception.');
|
123 |
+
}
|
124 |
+
|
125 |
+
private static function getResultAttemptLatency(ResultInterface $result)
|
126 |
+
{
|
127 |
+
if (isset($result['@metadata']['transferStats']['http'])) {
|
128 |
+
$attempt = end($result['@metadata']['transferStats']['http']);
|
129 |
+
if (isset($attempt['total_time'])) {
|
130 |
+
return (int) floor($attempt['total_time'] * 1000);
|
131 |
+
}
|
132 |
+
}
|
133 |
+
return null;
|
134 |
+
}
|
135 |
+
|
136 |
+
private static function getResultDestinationIp(ResultInterface $result)
|
137 |
+
{
|
138 |
+
if (isset($result['@metadata']['transferStats']['http'])) {
|
139 |
+
$attempt = end($result['@metadata']['transferStats']['http']);
|
140 |
+
if (isset($attempt['primary_ip'])) {
|
141 |
+
return $attempt['primary_ip'];
|
142 |
+
}
|
143 |
+
}
|
144 |
+
return null;
|
145 |
+
}
|
146 |
+
|
147 |
+
private static function getResultDnsLatency(ResultInterface $result)
|
148 |
+
{
|
149 |
+
if (isset($result['@metadata']['transferStats']['http'])) {
|
150 |
+
$attempt = end($result['@metadata']['transferStats']['http']);
|
151 |
+
if (isset($attempt['namelookup_time'])) {
|
152 |
+
return (int) floor($attempt['namelookup_time'] * 1000);
|
153 |
+
}
|
154 |
+
}
|
155 |
+
return null;
|
156 |
+
}
|
157 |
+
|
158 |
+
private static function getResultHttpStatusCode(ResultInterface $result)
|
159 |
+
{
|
160 |
+
return $result['@metadata']['statusCode'];
|
161 |
+
}
|
162 |
+
|
163 |
+
private static function getAwsExceptionAttemptLatency(AwsException $e) {
|
164 |
+
$attempt = $e->getTransferInfo();
|
165 |
+
if (isset($attempt['total_time'])) {
|
166 |
+
return (int) floor($attempt['total_time'] * 1000);
|
167 |
+
}
|
168 |
+
return null;
|
169 |
+
}
|
170 |
+
|
171 |
+
private static function getAwsExceptionErrorCode(AwsException $e) {
|
172 |
+
return $e->getAwsErrorCode();
|
173 |
+
}
|
174 |
+
|
175 |
+
private static function getAwsExceptionMessage(AwsException $e) {
|
176 |
+
return $e->getAwsErrorMessage();
|
177 |
+
}
|
178 |
+
|
179 |
+
private static function getAwsExceptionDestinationIp(AwsException $e) {
|
180 |
+
$attempt = $e->getTransferInfo();
|
181 |
+
if (isset($attempt['primary_ip'])) {
|
182 |
+
return $attempt['primary_ip'];
|
183 |
+
}
|
184 |
+
return null;
|
185 |
+
}
|
186 |
+
|
187 |
+
private static function getAwsExceptionDnsLatency(AwsException $e) {
|
188 |
+
$attempt = $e->getTransferInfo();
|
189 |
+
if (isset($attempt['namelookup_time'])) {
|
190 |
+
return (int) floor($attempt['namelookup_time'] * 1000);
|
191 |
+
}
|
192 |
+
return null;
|
193 |
+
}
|
194 |
+
|
195 |
+
private static function getAwsExceptionHttpStatusCode(AwsException $e) {
|
196 |
+
$response = $e->getResponse();
|
197 |
+
if ($response !== null) {
|
198 |
+
return $response->getStatusCode();
|
199 |
+
}
|
200 |
+
return null;
|
201 |
+
}
|
202 |
+
|
203 |
+
private static function getExceptionHttpStatusCode(\Exception $e) {
|
204 |
+
if ($e instanceof ResponseContainerInterface) {
|
205 |
+
$response = $e->getResponse();
|
206 |
+
if ($response instanceof ResponseInterface) {
|
207 |
+
return $response->getStatusCode();
|
208 |
+
}
|
209 |
+
}
|
210 |
+
return null;
|
211 |
+
}
|
212 |
+
|
213 |
+
private static function getExceptionCode(\Exception $e) {
|
214 |
+
if (!($e instanceof AwsException)) {
|
215 |
+
return get_class($e);
|
216 |
+
}
|
217 |
+
return null;
|
218 |
+
}
|
219 |
+
|
220 |
+
private static function getExceptionMessage(\Exception $e) {
|
221 |
+
if (!($e instanceof AwsException)) {
|
222 |
+
return $e->getMessage();
|
223 |
+
}
|
224 |
+
return null;
|
225 |
+
}
|
226 |
+
|
227 |
+
/**
|
228 |
+
* {@inheritdoc}
|
229 |
+
*/
|
230 |
+
protected function populateRequestEventData(
|
231 |
+
CommandInterface $cmd,
|
232 |
+
RequestInterface $request,
|
233 |
+
array $event
|
234 |
+
) {
|
235 |
+
$event = parent::populateRequestEventData($cmd, $request, $event);
|
236 |
+
$event['Type'] = 'ApiCallAttempt';
|
237 |
+
return $event;
|
238 |
+
}
|
239 |
+
|
240 |
+
/**
|
241 |
+
* {@inheritdoc}
|
242 |
+
*/
|
243 |
+
protected function populateResultEventData(
|
244 |
+
$result,
|
245 |
+
array $event
|
246 |
+
) {
|
247 |
+
$event = parent::populateResultEventData($result, $event);
|
248 |
+
|
249 |
+
$provider = $this->credentialProvider;
|
250 |
+
/** @var CredentialsInterface $credentials */
|
251 |
+
$credentials = $provider()->wait();
|
252 |
+
$event['AccessKey'] = $credentials->getAccessKeyId();
|
253 |
+
$sessionToken = $credentials->getSecurityToken();
|
254 |
+
if ($sessionToken !== null) {
|
255 |
+
$event['SessionToken'] = $sessionToken;
|
256 |
+
}
|
257 |
+
if (empty($event['AttemptLatency'])) {
|
258 |
+
$event['AttemptLatency'] = (int) (floor(microtime(true) * 1000) - $event['Timestamp']);
|
259 |
+
}
|
260 |
+
return $event;
|
261 |
+
}
|
262 |
+
}
|
lib/Aws/Aws/ClientSideMonitoring/ApiCallMonitoringMiddleware.php
ADDED
@@ -0,0 +1,176 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Aws\ClientSideMonitoring;
|
4 |
+
|
5 |
+
use Aws\CommandInterface;
|
6 |
+
use Aws\Exception\AwsException;
|
7 |
+
use Aws\MonitoringEventsInterface;
|
8 |
+
use Aws\ResultInterface;
|
9 |
+
use Psr\Http\Message\RequestInterface;
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @internal
|
13 |
+
*/
|
14 |
+
class ApiCallMonitoringMiddleware extends AbstractMonitoringMiddleware
|
15 |
+
{
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Api Call Attempt event keys for each Api Call event key
|
19 |
+
*
|
20 |
+
* @var array
|
21 |
+
*/
|
22 |
+
private static $eventKeys = [
|
23 |
+
'FinalAwsException' => 'AwsException',
|
24 |
+
'FinalAwsExceptionMessage' => 'AwsExceptionMessage',
|
25 |
+
'FinalSdkException' => 'SdkException',
|
26 |
+
'FinalSdkExceptionMessage' => 'SdkExceptionMessage',
|
27 |
+
'FinalHttpStatusCode' => 'HttpStatusCode',
|
28 |
+
];
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Standard middleware wrapper function with CSM options passed in.
|
32 |
+
*
|
33 |
+
* @param callable $credentialProvider
|
34 |
+
* @param mixed $options
|
35 |
+
* @param string $region
|
36 |
+
* @param string $service
|
37 |
+
* @return callable
|
38 |
+
*/
|
39 |
+
public static function wrap(
|
40 |
+
callable $credentialProvider,
|
41 |
+
$options,
|
42 |
+
$region,
|
43 |
+
$service
|
44 |
+
) {
|
45 |
+
return function (callable $handler) use (
|
46 |
+
$credentialProvider,
|
47 |
+
$options,
|
48 |
+
$region,
|
49 |
+
$service
|
50 |
+
) {
|
51 |
+
return new static(
|
52 |
+
$handler,
|
53 |
+
$credentialProvider,
|
54 |
+
$options,
|
55 |
+
$region,
|
56 |
+
$service
|
57 |
+
);
|
58 |
+
};
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* {@inheritdoc}
|
63 |
+
*/
|
64 |
+
public static function getRequestData(RequestInterface $request)
|
65 |
+
{
|
66 |
+
return [];
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
* {@inheritdoc}
|
71 |
+
*/
|
72 |
+
public static function getResponseData($klass)
|
73 |
+
{
|
74 |
+
if ($klass instanceof ResultInterface) {
|
75 |
+
$data = [
|
76 |
+
'AttemptCount' => self::getResultAttemptCount($klass),
|
77 |
+
'MaxRetriesExceeded' => 0,
|
78 |
+
];
|
79 |
+
} elseif ($klass instanceof \Exception) {
|
80 |
+
$data = [
|
81 |
+
'AttemptCount' => self::getExceptionAttemptCount($klass),
|
82 |
+
'MaxRetriesExceeded' => self::getMaxRetriesExceeded($klass),
|
83 |
+
];
|
84 |
+
} else {
|
85 |
+
throw new \InvalidArgumentException('Parameter must be an instance of ResultInterface or Exception.');
|
86 |
+
}
|
87 |
+
|
88 |
+
return $data + self::getFinalAttemptData($klass);
|
89 |
+
}
|
90 |
+
|
91 |
+
private static function getResultAttemptCount(ResultInterface $result) {
|
92 |
+
if (isset($result['@metadata']['transferStats']['http'])) {
|
93 |
+
return count($result['@metadata']['transferStats']['http']);
|
94 |
+
}
|
95 |
+
return 1;
|
96 |
+
}
|
97 |
+
|
98 |
+
private static function getExceptionAttemptCount(\Exception $e) {
|
99 |
+
$attemptCount = 0;
|
100 |
+
if ($e instanceof MonitoringEventsInterface) {
|
101 |
+
foreach ($e->getMonitoringEvents() as $event) {
|
102 |
+
if (isset($event['Type']) &&
|
103 |
+
$event['Type'] === 'ApiCallAttempt') {
|
104 |
+
$attemptCount++;
|
105 |
+
}
|
106 |
+
}
|
107 |
+
|
108 |
+
}
|
109 |
+
return $attemptCount;
|
110 |
+
}
|
111 |
+
|
112 |
+
private static function getFinalAttemptData($klass)
|
113 |
+
{
|
114 |
+
$data = [];
|
115 |
+
if ($klass instanceof MonitoringEventsInterface) {
|
116 |
+
$finalAttempt = self::getFinalAttempt($klass->getMonitoringEvents());
|
117 |
+
|
118 |
+
if (!empty($finalAttempt)) {
|
119 |
+
foreach (self::$eventKeys as $callKey => $attemptKey) {
|
120 |
+
if (isset($finalAttempt[$attemptKey])) {
|
121 |
+
$data[$callKey] = $finalAttempt[$attemptKey];
|
122 |
+
}
|
123 |
+
}
|
124 |
+
}
|
125 |
+
}
|
126 |
+
|
127 |
+
return $data;
|
128 |
+
}
|
129 |
+
|
130 |
+
private static function getFinalAttempt(array $events)
|
131 |
+
{
|
132 |
+
for (end($events); key($events) !== null; prev($events)) {
|
133 |
+
$current = current($events);
|
134 |
+
if (isset($current['Type'])
|
135 |
+
&& $current['Type'] === 'ApiCallAttempt'
|
136 |
+
) {
|
137 |
+
return $current;
|
138 |
+
}
|
139 |
+
}
|
140 |
+
|
141 |
+
return null;
|
142 |
+
}
|
143 |
+
|
144 |
+
private static function getMaxRetriesExceeded($klass)
|
145 |
+
{
|
146 |
+
if ($klass instanceof AwsException && $klass->isMaxRetriesExceeded()) {
|
147 |
+
return 1;
|
148 |
+
}
|
149 |
+
return 0;
|
150 |
+
}
|
151 |
+
|
152 |
+
/**
|
153 |
+
* {@inheritdoc}
|
154 |
+
*/
|
155 |
+
protected function populateRequestEventData(
|
156 |
+
CommandInterface $cmd,
|
157 |
+
RequestInterface $request,
|
158 |
+
array $event
|
159 |
+
) {
|
160 |
+
$event = parent::populateRequestEventData($cmd, $request, $event);
|
161 |
+
$event['Type'] = 'ApiCall';
|
162 |
+
return $event;
|
163 |
+
}
|
164 |
+
|
165 |
+
/**
|
166 |
+
* {@inheritdoc}
|
167 |
+
*/
|
168 |
+
protected function populateResultEventData(
|
169 |
+
$result,
|
170 |
+
array $event
|
171 |
+
) {
|
172 |
+
$event = parent::populateResultEventData($result, $event);
|
173 |
+
$event['Latency'] = (int) (floor(microtime(true) * 1000) - $event['Timestamp']);
|
174 |
+
return $event;
|
175 |
+
}
|
176 |
+
}
|
lib/Aws/Aws/ClientSideMonitoring/Configuration.php
ADDED
@@ -0,0 +1,65 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\ClientSideMonitoring;
|
3 |
+
|
4 |
+
class Configuration implements ConfigurationInterface
|
5 |
+
{
|
6 |
+
private $clientId;
|
7 |
+
private $enabled;
|
8 |
+
private $port;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Constructs a new Configuration object with the specified CSM options set.
|
12 |
+
*
|
13 |
+
* @param mixed $enabled
|
14 |
+
* @param string|int $port
|
15 |
+
* @param string $clientId
|
16 |
+
*/
|
17 |
+
public function __construct($enabled, $port, $clientId = '')
|
18 |
+
{
|
19 |
+
$this->port = filter_var($port, FILTER_VALIDATE_INT);
|
20 |
+
if ($this->port === false) {
|
21 |
+
throw new \InvalidArgumentException(
|
22 |
+
"CSM 'port' value must be an integer!");
|
23 |
+
}
|
24 |
+
|
25 |
+
// Unparsable $enabled flag errors on the side of disabling CSM
|
26 |
+
$this->enabled = filter_var($enabled, FILTER_VALIDATE_BOOLEAN);
|
27 |
+
$this->clientId = trim($clientId);
|
28 |
+
}
|
29 |
+
|
30 |
+
/**
|
31 |
+
* {@inheritdoc}
|
32 |
+
*/
|
33 |
+
public function isEnabled()
|
34 |
+
{
|
35 |
+
return $this->enabled;
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* {@inheritdoc}
|
40 |
+
*/
|
41 |
+
public function getClientId()
|
42 |
+
{
|
43 |
+
return $this->clientId;
|
44 |
+
}
|
45 |
+
|
46 |
+
/**
|
47 |
+
* {@inheritdoc}
|
48 |
+
*/
|
49 |
+
public function getPort()
|
50 |
+
{
|
51 |
+
return $this->port;
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* {@inheritdoc}
|
56 |
+
*/
|
57 |
+
public function toArray()
|
58 |
+
{
|
59 |
+
return [
|
60 |
+
'client_id' => $this->getClientId(),
|
61 |
+
'enabled' => $this->isEnabled(),
|
62 |
+
'port' => $this->getPort()
|
63 |
+
];
|
64 |
+
}
|
65 |
+
}
|
lib/Aws/Aws/ClientSideMonitoring/ConfigurationInterface.php
ADDED
@@ -0,0 +1,37 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\ClientSideMonitoring;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Provides access to client-side monitoring configuration options:
|
6 |
+
* 'client_id', 'enabled', 'port'
|
7 |
+
*/
|
8 |
+
interface ConfigurationInterface
|
9 |
+
{
|
10 |
+
/**
|
11 |
+
* Checks whether or not client-side monitoring is enabled.
|
12 |
+
*
|
13 |
+
* @return bool
|
14 |
+
*/
|
15 |
+
public function isEnabled();
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Returns the Client ID, if available.
|
19 |
+
*
|
20 |
+
* @return string|null
|
21 |
+
*/
|
22 |
+
public function getClientId();
|
23 |
+
|
24 |
+
/**
|
25 |
+
* Returns the configured port.
|
26 |
+
*
|
27 |
+
* @return int|null
|
28 |
+
*/
|
29 |
+
public function getPort();
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Returns the configuration as an associative array.
|
33 |
+
*
|
34 |
+
* @return array
|
35 |
+
*/
|
36 |
+
public function toArray();
|
37 |
+
}
|
lib/Aws/Aws/ClientSideMonitoring/ConfigurationProvider.php
ADDED
@@ -0,0 +1,342 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\ClientSideMonitoring;
|
3 |
+
|
4 |
+
use Aws\CacheInterface;
|
5 |
+
use Aws\ClientSideMonitoring\Exception\ConfigurationException;
|
6 |
+
use GuzzleHttp\Promise;
|
7 |
+
use GuzzleHttp\Promise\PromiseInterface;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* A configuration provider is a function that accepts no arguments and returns
|
11 |
+
* a promise that is fulfilled with a {@see \Aws\ClientSideMonitoring\ConfigurationInterface}
|
12 |
+
* or rejected with an {@see \Aws\ClientSideMonitoring\Exception\ConfigurationException}.
|
13 |
+
*
|
14 |
+
* <code>
|
15 |
+
* use Aws\ClientSideMonitoring\ConfigurationProvider;
|
16 |
+
* $provider = ConfigurationProvider::defaultProvider();
|
17 |
+
* // Returns a ConfigurationInterface or throws.
|
18 |
+
* $config = $provider()->wait();
|
19 |
+
* </code>
|
20 |
+
*
|
21 |
+
* Configuration providers can be composed to create configuration using
|
22 |
+
* conditional logic that can create different configurations in different
|
23 |
+
* environments. You can compose multiple providers into a single provider using
|
24 |
+
* {@see Aws\ClientSideMonitoring\ConfigurationProvider::chain}. This function
|
25 |
+
* accepts providers as variadic arguments and returns a new function that will
|
26 |
+
* invoke each provider until a successful configuration is returned.
|
27 |
+
*
|
28 |
+
* <code>
|
29 |
+
* // First try an INI file at this location.
|
30 |
+
* $a = ConfigurationProvider::ini(null, '/path/to/file.ini');
|
31 |
+
* // Then try an INI file at this location.
|
32 |
+
* $b = ConfigurationProvider::ini(null, '/path/to/other-file.ini');
|
33 |
+
* // Then try loading from environment variables.
|
34 |
+
* $c = ConfigurationProvider::env();
|
35 |
+
* // Combine the three providers together.
|
36 |
+
* $composed = ConfigurationProvider::chain($a, $b, $c);
|
37 |
+
* // Returns a promise that is fulfilled with a configuration or throws.
|
38 |
+
* $promise = $composed();
|
39 |
+
* // Wait on the configuration to resolve.
|
40 |
+
* $config = $promise->wait();
|
41 |
+
* </code>
|
42 |
+
*/
|
43 |
+
class ConfigurationProvider
|
44 |
+
{
|
45 |
+
|
46 |
+
const CACHE_KEY = 'aws_cached_csm_config';
|
47 |
+
const DEFAULT_CLIENT_ID = '';
|
48 |
+
const DEFAULT_ENABLED = false;
|
49 |
+
const DEFAULT_PORT = 31000;
|
50 |
+
const ENV_CLIENT_ID = 'AWS_CSM_CLIENT_ID';
|
51 |
+
const ENV_ENABLED = 'AWS_CSM_ENABLED';
|
52 |
+
const ENV_PORT = 'AWS_CSM_PORT';
|
53 |
+
const ENV_PROFILE = 'AWS_PROFILE';
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Wraps a credential provider and saves provided credentials in an
|
57 |
+
* instance of Aws\CacheInterface. Forwards calls when no credentials found
|
58 |
+
* in cache and updates cache with the results.
|
59 |
+
*
|
60 |
+
* @param callable $provider Credentials provider function to wrap
|
61 |
+
* @param CacheInterface $cache Cache to store credentials
|
62 |
+
* @param string|null $cacheKey (optional) Cache key to use
|
63 |
+
*
|
64 |
+
* @return callable
|
65 |
+
*/
|
66 |
+
public static function cache(
|
67 |
+
callable $provider,
|
68 |
+
CacheInterface $cache,
|
69 |
+
$cacheKey = null
|
70 |
+
) {
|
71 |
+
$cacheKey = $cacheKey ?: self::CACHE_KEY;
|
72 |
+
|
73 |
+
return function () use ($provider, $cache, $cacheKey) {
|
74 |
+
$found = $cache->get($cacheKey);
|
75 |
+
if ($found instanceof ConfigurationInterface) {
|
76 |
+
return Promise\promise_for($found);
|
77 |
+
}
|
78 |
+
|
79 |
+
return $provider()
|
80 |
+
->then(function (ConfigurationInterface $config) use (
|
81 |
+
$cache,
|
82 |
+
$cacheKey
|
83 |
+
) {
|
84 |
+
$cache->set(
|
85 |
+
$cacheKey,
|
86 |
+
$config
|
87 |
+
);
|
88 |
+
|
89 |
+
return $config;
|
90 |
+
});
|
91 |
+
};
|
92 |
+
}
|
93 |
+
|
94 |
+
/**
|
95 |
+
* Creates an aggregate credentials provider that invokes the provided
|
96 |
+
* variadic providers one after the other until a provider returns
|
97 |
+
* credentials.
|
98 |
+
*
|
99 |
+
* @return callable
|
100 |
+
*/
|
101 |
+
public static function chain()
|
102 |
+
{
|
103 |
+
$links = func_get_args();
|
104 |
+
if (empty($links)) {
|
105 |
+
throw new \InvalidArgumentException('No providers in chain');
|
106 |
+
}
|
107 |
+
|
108 |
+
return function () use ($links) {
|
109 |
+
/** @var callable $parent */
|
110 |
+
$parent = array_shift($links);
|
111 |
+
$promise = $parent();
|
112 |
+
while ($next = array_shift($links)) {
|
113 |
+
$promise = $promise->otherwise($next);
|
114 |
+
}
|
115 |
+
return $promise;
|
116 |
+
};
|
117 |
+
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
* Create a default CSM config provider that first checks for environment
|
121 |
+
* variables, then checks for a specified profile in ~/.aws/config, then
|
122 |
+
* checks for the "aws_csm" profile in ~/.aws/config, and failing those uses
|
123 |
+
* a default fallback set of configuration options.
|
124 |
+
*
|
125 |
+
* This provider is automatically wrapped in a memoize function that caches
|
126 |
+
* previously provided config options.
|
127 |
+
*
|
128 |
+
* @param array $config Optional array of ecs/instance profile credentials
|
129 |
+
* provider options.
|
130 |
+
*
|
131 |
+
* @return callable
|
132 |
+
*/
|
133 |
+
public static function defaultProvider(array $config = [])
|
134 |
+
{
|
135 |
+
$configProviders = [
|
136 |
+
self::env(),
|
137 |
+
self::ini(),
|
138 |
+
self::fallback()
|
139 |
+
];
|
140 |
+
|
141 |
+
$memo = self::memoize(
|
142 |
+
call_user_func_array('self::chain', $configProviders)
|
143 |
+
);
|
144 |
+
|
145 |
+
if (isset($config['csm']) && $config['csm'] instanceof CacheInterface) {
|
146 |
+
return self::cache($memo, $config['csm'], self::CACHE_KEY);
|
147 |
+
}
|
148 |
+
|
149 |
+
return $memo;
|
150 |
+
}
|
151 |
+
|
152 |
+
/**
|
153 |
+
* Provider that creates CSM config from environment variables.
|
154 |
+
*
|
155 |
+
* @return callable
|
156 |
+
*/
|
157 |
+
public static function env()
|
158 |
+
{
|
159 |
+
return function () {
|
160 |
+
// Use credentials from environment variables, if available
|
161 |
+
$enabled = getenv(self::ENV_ENABLED);
|
162 |
+
if ($enabled !== false) {
|
163 |
+
return Promise\promise_for(
|
164 |
+
new Configuration(
|
165 |
+
$enabled,
|
166 |
+
getenv(self::ENV_PORT) ?: self::DEFAULT_PORT,
|
167 |
+
getenv(self:: ENV_CLIENT_ID) ?: self::DEFAULT_CLIENT_ID
|
168 |
+
)
|
169 |
+
);
|
170 |
+
}
|
171 |
+
|
172 |
+
return self::reject('Could not find environment variable CSM config'
|
173 |
+
. ' in ' . self::ENV_ENABLED. '/' . self::ENV_PORT . '/'
|
174 |
+
. self::ENV_CLIENT_ID);
|
175 |
+
};
|
176 |
+
}
|
177 |
+
|
178 |
+
/**
|
179 |
+
* Fallback config options when other sources are not set.
|
180 |
+
*
|
181 |
+
* @return callable
|
182 |
+
*/
|
183 |
+
public static function fallback()
|
184 |
+
{
|
185 |
+
return function() {
|
186 |
+
return Promise\promise_for(
|
187 |
+
new Configuration(
|
188 |
+
self::DEFAULT_ENABLED,
|
189 |
+
self::DEFAULT_PORT,
|
190 |
+
self::DEFAULT_CLIENT_ID
|
191 |
+
)
|
192 |
+
);
|
193 |
+
};
|
194 |
+
}
|
195 |
+
|
196 |
+
/**
|
197 |
+
* Gets the environment's HOME directory if available.
|
198 |
+
*
|
199 |
+
* @return null|string
|
200 |
+
*/
|
201 |
+
private static function getHomeDir()
|
202 |
+
{
|
203 |
+
// On Linux/Unix-like systems, use the HOME environment variable
|
204 |
+
if ($homeDir = getenv('HOME')) {
|
205 |
+
return $homeDir;
|
206 |
+
}
|
207 |
+
|
208 |
+
// Get the HOMEDRIVE and HOMEPATH values for Windows hosts
|
209 |
+
$homeDrive = getenv('HOMEDRIVE');
|
210 |
+
$homePath = getenv('HOMEPATH');
|
211 |
+
|
212 |
+
return ($homeDrive && $homePath) ? $homeDrive . $homePath : null;
|
213 |
+
}
|
214 |
+
|
215 |
+
/**
|
216 |
+
* CSM config provider that creates CSM config using an ini file stored
|
217 |
+
* in the current user's home directory.
|
218 |
+
*
|
219 |
+
* @param string|null $profile Profile to use. If not specified will use
|
220 |
+
* the "aws_csm" profile in "~/.aws/config".
|
221 |
+
* @param string|null $filename If provided, uses a custom filename rather
|
222 |
+
* than looking in the home directory.
|
223 |
+
*
|
224 |
+
* @return callable
|
225 |
+
*/
|
226 |
+
public static function ini($profile = null, $filename = null)
|
227 |
+
{
|
228 |
+
$filename = $filename ?: (self::getHomeDir() . '/.aws/config');
|
229 |
+
$profile = $profile ?: (getenv(self::ENV_PROFILE) ?: 'aws_csm');
|
230 |
+
|
231 |
+
return function () use ($profile, $filename) {
|
232 |
+
if (!is_readable($filename)) {
|
233 |
+
return self::reject("Cannot read CSM config from $filename");
|
234 |
+
}
|
235 |
+
$data = \Aws\parse_ini_file($filename, true);
|
236 |
+
if ($data === false) {
|
237 |
+
return self::reject("Invalid config file: $filename");
|
238 |
+
}
|
239 |
+
if (!isset($data[$profile])) {
|
240 |
+
return self::reject("'$profile' not found in config file");
|
241 |
+
}
|
242 |
+
if (!isset($data[$profile]['csm_enabled'])) {
|
243 |
+
return self::reject("Required CSM config values not present in
|
244 |
+
INI profile '{$profile}' ({$filename})");
|
245 |
+
}
|
246 |
+
|
247 |
+
// port is optional
|
248 |
+
if (empty($data[$profile]['csm_port'])) {
|
249 |
+
$data[$profile]['csm_port'] = self::DEFAULT_PORT;
|
250 |
+
}
|
251 |
+
|
252 |
+
// client_id is optional
|
253 |
+
if (empty($data[$profile]['csm_client_id'])) {
|
254 |
+
$data[$profile]['csm_client_id'] = self::DEFAULT_CLIENT_ID;
|
255 |
+
}
|
256 |
+
|
257 |
+
return Promise\promise_for(
|
258 |
+
new Configuration(
|
259 |
+
$data[$profile]['csm_enabled'],
|
260 |
+
$data[$profile]['csm_port'],
|
261 |
+
$data[$profile]['csm_client_id']
|
262 |
+
)
|
263 |
+
);
|
264 |
+
};
|
265 |
+
}
|
266 |
+
|
267 |
+
/**
|
268 |
+
* Wraps a CSM config provider and caches previously provided configuration.
|
269 |
+
*
|
270 |
+
* Ensures that cached configuration is refreshed when it expires.
|
271 |
+
*
|
272 |
+
* @param callable $provider CSM config provider function to wrap.
|
273 |
+
*
|
274 |
+
* @return callable
|
275 |
+
*/
|
276 |
+
public static function memoize(callable $provider)
|
277 |
+
{
|
278 |
+
return function () use ($provider) {
|
279 |
+
static $result;
|
280 |
+
static $isConstant;
|
281 |
+
|
282 |
+
// Constant config will be returned constantly.
|
283 |
+
if ($isConstant) {
|
284 |
+
return $result;
|
285 |
+
}
|
286 |
+
|
287 |
+
// Create the initial promise that will be used as the cached value
|
288 |
+
// until it expires.
|
289 |
+
if (null === $result) {
|
290 |
+
$result = $provider();
|
291 |
+
}
|
292 |
+
|
293 |
+
// Return config and set flag that provider is already set
|
294 |
+
return $result
|
295 |
+
->then(function (ConfigurationInterface $config) use (&$isConstant) {
|
296 |
+
$isConstant = true;
|
297 |
+
return $config;
|
298 |
+
});
|
299 |
+
};
|
300 |
+
}
|
301 |
+
|
302 |
+
/**
|
303 |
+
* Reject promise with standardized exception.
|
304 |
+
*
|
305 |
+
* @param $msg
|
306 |
+
* @return Promise\RejectedPromise
|
307 |
+
*/
|
308 |
+
private static function reject($msg)
|
309 |
+
{
|
310 |
+
return new Promise\RejectedPromise(new ConfigurationException($msg));
|
311 |
+
}
|
312 |
+
|
313 |
+
/**
|
314 |
+
* Unwraps a configuration object in whatever valid form it is in,
|
315 |
+
* always returning a ConfigurationInterface object.
|
316 |
+
*
|
317 |
+
* @param mixed $config
|
318 |
+
* @return ConfigurationInterface
|
319 |
+
* @throws \InvalidArgumentException
|
320 |
+
*/
|
321 |
+
public static function unwrap($config)
|
322 |
+
{
|
323 |
+
if (is_callable($config)) {
|
324 |
+
$config = $config();
|
325 |
+
}
|
326 |
+
if ($config instanceof PromiseInterface) {
|
327 |
+
$config = $config->wait();
|
328 |
+
}
|
329 |
+
if ($config instanceof ConfigurationInterface) {
|
330 |
+
return $config;
|
331 |
+
} elseif (is_array($config) && isset($config['enabled'])) {
|
332 |
+
$client_id = isset($config['client_id']) ? $config['client_id']
|
333 |
+
: self::DEFAULT_CLIENT_ID;
|
334 |
+
$port = isset($config['port']) ? $config['port']
|
335 |
+
: self::DEFAULT_PORT;
|
336 |
+
return new Configuration($config['enabled'], $port, $client_id);
|
337 |
+
}
|
338 |
+
|
339 |
+
throw new \InvalidArgumentException('Not a valid CSM configuration '
|
340 |
+
. 'argument.');
|
341 |
+
}
|
342 |
+
}
|
lib/Aws/Aws/ClientSideMonitoring/Exception/ConfigurationException.php
ADDED
@@ -0,0 +1,15 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\ClientSideMonitoring\Exception;
|
3 |
+
|
4 |
+
use Aws\HasMonitoringEventsTrait;
|
5 |
+
use Aws\MonitoringEventsInterface;
|
6 |
+
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Represents an error interacting with configuration for client-side monitoring.
|
10 |
+
*/
|
11 |
+
class ConfigurationException extends \RuntimeException implements
|
12 |
+
MonitoringEventsInterface
|
13 |
+
{
|
14 |
+
use HasMonitoringEventsTrait;
|
15 |
+
}
|
lib/Aws/Aws/ClientSideMonitoring/MonitoringMiddlewareInterface.php
ADDED
@@ -0,0 +1,35 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Aws\ClientSideMonitoring;
|
4 |
+
|
5 |
+
use Aws\CommandInterface;
|
6 |
+
use Aws\Exception\AwsException;
|
7 |
+
use Aws\ResultInterface;
|
8 |
+
use GuzzleHttp\Psr7\Request;
|
9 |
+
use Psr\Http\Message\RequestInterface;
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @internal
|
13 |
+
*/
|
14 |
+
interface MonitoringMiddlewareInterface
|
15 |
+
{
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Data for event properties to be sent to the monitoring agent.
|
19 |
+
*
|
20 |
+
* @param RequestInterface $request
|
21 |
+
* @return array
|
22 |
+
*/
|
23 |
+
public static function getRequestData(RequestInterface $request);
|
24 |
+
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Data for event properties to be sent to the monitoring agent.
|
28 |
+
*
|
29 |
+
* @param ResultInterface|AwsException|\Exception $klass
|
30 |
+
* @return array
|
31 |
+
*/
|
32 |
+
public static function getResponseData($klass);
|
33 |
+
|
34 |
+
public function __invoke(CommandInterface $cmd, RequestInterface $request);
|
35 |
+
}
|
lib/Aws/Aws/CloudFront/CloudFrontClient.php
ADDED
@@ -0,0 +1,190 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\CloudFront;
|
3 |
+
|
4 |
+
use Aws\AwsClient;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* This client is used to interact with the **Amazon CloudFront** service.
|
8 |
+
*
|
9 |
+
* @method \Aws\Result createCloudFrontOriginAccessIdentity(array $args = [])
|
10 |
+
* @method \GuzzleHttp\Promise\Promise createCloudFrontOriginAccessIdentityAsync(array $args = [])
|
11 |
+
* @method \Aws\Result createDistribution(array $args = [])
|
12 |
+
* @method \GuzzleHttp\Promise\Promise createDistributionAsync(array $args = [])
|
13 |
+
* @method \Aws\Result createInvalidation(array $args = [])
|
14 |
+
* @method \GuzzleHttp\Promise\Promise createInvalidationAsync(array $args = [])
|
15 |
+
* @method \Aws\Result createStreamingDistribution(array $args = [])
|
16 |
+
* @method \GuzzleHttp\Promise\Promise createStreamingDistributionAsync(array $args = [])
|
17 |
+
* @method \Aws\Result deleteCloudFrontOriginAccessIdentity(array $args = [])
|
18 |
+
* @method \GuzzleHttp\Promise\Promise deleteCloudFrontOriginAccessIdentityAsync(array $args = [])
|
19 |
+
* @method \Aws\Result deleteDistribution(array $args = [])
|
20 |
+
* @method \GuzzleHttp\Promise\Promise deleteDistributionAsync(array $args = [])
|
21 |
+
* @method \Aws\Result deleteStreamingDistribution(array $args = [])
|
22 |
+
* @method \GuzzleHttp\Promise\Promise deleteStreamingDistributionAsync(array $args = [])
|
23 |
+
* @method \Aws\Result getCloudFrontOriginAccessIdentity(array $args = [])
|
24 |
+
* @method \GuzzleHttp\Promise\Promise getCloudFrontOriginAccessIdentityAsync(array $args = [])
|
25 |
+
* @method \Aws\Result getCloudFrontOriginAccessIdentityConfig(array $args = [])
|
26 |
+
* @method \GuzzleHttp\Promise\Promise getCloudFrontOriginAccessIdentityConfigAsync(array $args = [])
|
27 |
+
* @method \Aws\Result getDistribution(array $args = [])
|
28 |
+
* @method \GuzzleHttp\Promise\Promise getDistributionAsync(array $args = [])
|
29 |
+
* @method \Aws\Result getDistributionConfig(array $args = [])
|
30 |
+
* @method \GuzzleHttp\Promise\Promise getDistributionConfigAsync(array $args = [])
|
31 |
+
* @method \Aws\Result getInvalidation(array $args = [])
|
32 |
+
* @method \GuzzleHttp\Promise\Promise getInvalidationAsync(array $args = [])
|
33 |
+
* @method \Aws\Result getStreamingDistribution(array $args = [])
|
34 |
+
* @method \GuzzleHttp\Promise\Promise getStreamingDistributionAsync(array $args = [])
|
35 |
+
* @method \Aws\Result getStreamingDistributionConfig(array $args = [])
|
36 |
+
* @method \GuzzleHttp\Promise\Promise getStreamingDistributionConfigAsync(array $args = [])
|
37 |
+
* @method \Aws\Result listCloudFrontOriginAccessIdentities(array $args = [])
|
38 |
+
* @method \GuzzleHttp\Promise\Promise listCloudFrontOriginAccessIdentitiesAsync(array $args = [])
|
39 |
+
* @method \Aws\Result listDistributions(array $args = [])
|
40 |
+
* @method \GuzzleHttp\Promise\Promise listDistributionsAsync(array $args = [])
|
41 |
+
* @method \Aws\Result listDistributionsByWebACLId(array $args = [])
|
42 |
+
* @method \GuzzleHttp\Promise\Promise listDistributionsByWebACLIdAsync(array $args = [])
|
43 |
+
* @method \Aws\Result listInvalidations(array $args = [])
|
44 |
+
* @method \GuzzleHttp\Promise\Promise listInvalidationsAsync(array $args = [])
|
45 |
+
* @method \Aws\Result listStreamingDistributions(array $args = [])
|
46 |
+
* @method \GuzzleHttp\Promise\Promise listStreamingDistributionsAsync(array $args = [])
|
47 |
+
* @method \Aws\Result updateCloudFrontOriginAccessIdentity(array $args = [])
|
48 |
+
* @method \GuzzleHttp\Promise\Promise updateCloudFrontOriginAccessIdentityAsync(array $args = [])
|
49 |
+
* @method \Aws\Result updateDistribution(array $args = [])
|
50 |
+
* @method \GuzzleHttp\Promise\Promise updateDistributionAsync(array $args = [])
|
51 |
+
* @method \Aws\Result updateStreamingDistribution(array $args = [])
|
52 |
+
* @method \GuzzleHttp\Promise\Promise updateStreamingDistributionAsync(array $args = [])
|
53 |
+
* @method \Aws\Result createDistributionWithTags(array $args = []) (supported in versions 2016-08-01, 2016-08-20, 2016-09-07, 2016-09-29, 2016-11-25, 2017-03-25, 2017-10-30, 2018-06-18, 2018-11-05)
|
54 |
+
* @method \GuzzleHttp\Promise\Promise createDistributionWithTagsAsync(array $args = []) (supported in versions 2016-08-01, 2016-08-20, 2016-09-07, 2016-09-29, 2016-11-25, 2017-03-25, 2017-10-30, 2018-06-18, 2018-11-05)
|
55 |
+
* @method \Aws\Result createStreamingDistributionWithTags(array $args = []) (supported in versions 2016-08-01, 2016-08-20, 2016-09-07, 2016-09-29, 2016-11-25, 2017-03-25, 2017-10-30, 2018-06-18, 2018-11-05)
|
56 |
+
* @method \GuzzleHttp\Promise\Promise createStreamingDistributionWithTagsAsync(array $args = []) (supported in versions 2016-08-01, 2016-08-20, 2016-09-07, 2016-09-29, 2016-11-25, 2017-03-25, 2017-10-30, 2018-06-18, 2018-11-05)
|
57 |
+
* @method \Aws\Result listTagsForResource(array $args = []) (supported in versions 2016-08-01, 2016-08-20, 2016-09-07, 2016-09-29, 2016-11-25, 2017-03-25, 2017-10-30, 2018-06-18, 2018-11-05)
|
58 |
+
* @method \GuzzleHttp\Promise\Promise listTagsForResourceAsync(array $args = []) (supported in versions 2016-08-01, 2016-08-20, 2016-09-07, 2016-09-29, 2016-11-25, 2017-03-25, 2017-10-30, 2018-06-18, 2018-11-05)
|
59 |
+
* @method \Aws\Result tagResource(array $args = []) (supported in versions 2016-08-01, 2016-08-20, 2016-09-07, 2016-09-29, 2016-11-25, 2017-03-25, 2017-10-30, 2018-06-18, 2018-11-05)
|
60 |
+
* @method \GuzzleHttp\Promise\Promise tagResourceAsync(array $args = []) (supported in versions 2016-08-01, 2016-08-20, 2016-09-07, 2016-09-29, 2016-11-25, 2017-03-25, 2017-10-30, 2018-06-18, 2018-11-05)
|
61 |
+
* @method \Aws\Result untagResource(array $args = []) (supported in versions 2016-08-01, 2016-08-20, 2016-09-07, 2016-09-29, 2016-11-25, 2017-03-25, 2017-10-30, 2018-06-18, 2018-11-05)
|
62 |
+
* @method \GuzzleHttp\Promise\Promise untagResourceAsync(array $args = []) (supported in versions 2016-08-01, 2016-08-20, 2016-09-07, 2016-09-29, 2016-11-25, 2017-03-25, 2017-10-30, 2018-06-18, 2018-11-05)
|
63 |
+
* @method \Aws\Result deleteServiceLinkedRole(array $args = []) (supported in versions 2017-03-25)
|
64 |
+
* @method \GuzzleHttp\Promise\Promise deleteServiceLinkedRoleAsync(array $args = []) (supported in versions 2017-03-25)
|
65 |
+
* @method \Aws\Result createFieldLevelEncryptionConfig(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
66 |
+
* @method \GuzzleHttp\Promise\Promise createFieldLevelEncryptionConfigAsync(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
67 |
+
* @method \Aws\Result createFieldLevelEncryptionProfile(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
68 |
+
* @method \GuzzleHttp\Promise\Promise createFieldLevelEncryptionProfileAsync(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
69 |
+
* @method \Aws\Result createPublicKey(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
70 |
+
* @method \GuzzleHttp\Promise\Promise createPublicKeyAsync(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
71 |
+
* @method \Aws\Result deleteFieldLevelEncryptionConfig(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
72 |
+
* @method \GuzzleHttp\Promise\Promise deleteFieldLevelEncryptionConfigAsync(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
73 |
+
* @method \Aws\Result deleteFieldLevelEncryptionProfile(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
74 |
+
* @method \GuzzleHttp\Promise\Promise deleteFieldLevelEncryptionProfileAsync(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
75 |
+
* @method \Aws\Result deletePublicKey(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
76 |
+
* @method \GuzzleHttp\Promise\Promise deletePublicKeyAsync(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
77 |
+
* @method \Aws\Result getFieldLevelEncryption(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
78 |
+
* @method \GuzzleHttp\Promise\Promise getFieldLevelEncryptionAsync(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
79 |
+
* @method \Aws\Result getFieldLevelEncryptionConfig(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
80 |
+
* @method \GuzzleHttp\Promise\Promise getFieldLevelEncryptionConfigAsync(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
81 |
+
* @method \Aws\Result getFieldLevelEncryptionProfile(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
82 |
+
* @method \GuzzleHttp\Promise\Promise getFieldLevelEncryptionProfileAsync(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
83 |
+
* @method \Aws\Result getFieldLevelEncryptionProfileConfig(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
84 |
+
* @method \GuzzleHttp\Promise\Promise getFieldLevelEncryptionProfileConfigAsync(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
85 |
+
* @method \Aws\Result getPublicKey(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
86 |
+
* @method \GuzzleHttp\Promise\Promise getPublicKeyAsync(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
87 |
+
* @method \Aws\Result getPublicKeyConfig(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
88 |
+
* @method \GuzzleHttp\Promise\Promise getPublicKeyConfigAsync(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
89 |
+
* @method \Aws\Result listFieldLevelEncryptionConfigs(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
90 |
+
* @method \GuzzleHttp\Promise\Promise listFieldLevelEncryptionConfigsAsync(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
91 |
+
* @method \Aws\Result listFieldLevelEncryptionProfiles(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
92 |
+
* @method \GuzzleHttp\Promise\Promise listFieldLevelEncryptionProfilesAsync(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
93 |
+
* @method \Aws\Result listPublicKeys(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
94 |
+
* @method \GuzzleHttp\Promise\Promise listPublicKeysAsync(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
95 |
+
* @method \Aws\Result updateFieldLevelEncryptionConfig(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
96 |
+
* @method \GuzzleHttp\Promise\Promise updateFieldLevelEncryptionConfigAsync(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
97 |
+
* @method \Aws\Result updateFieldLevelEncryptionProfile(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
98 |
+
* @method \GuzzleHttp\Promise\Promise updateFieldLevelEncryptionProfileAsync(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
99 |
+
* @method \Aws\Result updatePublicKey(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
100 |
+
* @method \GuzzleHttp\Promise\Promise updatePublicKeyAsync(array $args = []) (supported in versions 2017-10-30, 2018-06-18, 2018-11-05)
|
101 |
+
*/
|
102 |
+
class CloudFrontClient extends AwsClient
|
103 |
+
{
|
104 |
+
/**
|
105 |
+
* Create a signed Amazon CloudFront URL.
|
106 |
+
*
|
107 |
+
* This method accepts an array of configuration options:
|
108 |
+
*
|
109 |
+
* - url: (string) URL of the resource being signed (can include query
|
110 |
+
* string and wildcards). For example: rtmp://s5c39gqb8ow64r.cloudfront.net/videos/mp3_name.mp3
|
111 |
+
* http://d111111abcdef8.cloudfront.net/images/horizon.jpg?size=large&license=yes
|
112 |
+
* - policy: (string) JSON policy. Use this option when creating a signed
|
113 |
+
* URL for a custom policy.
|
114 |
+
* - expires: (int) UTC Unix timestamp used when signing with a canned
|
115 |
+
* policy. Not required when passing a custom 'policy' option.
|
116 |
+
* - key_pair_id: (string) The ID of the key pair used to sign CloudFront
|
117 |
+
* URLs for private distributions.
|
118 |
+
* - private_key: (string) The filepath ot the private key used to sign
|
119 |
+
* CloudFront URLs for private distributions.
|
120 |
+
*
|
121 |
+
* @param array $options Array of configuration options used when signing
|
122 |
+
*
|
123 |
+
* @return string Signed URL with authentication parameters
|
124 |
+
* @throws \InvalidArgumentException if url, key_pair_id, or private_key
|
125 |
+
* were not specified.
|
126 |
+
* @link http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/WorkingWithStreamingDistributions.html
|
127 |
+
*/
|
128 |
+
public function getSignedUrl(array $options)
|
129 |
+
{
|
130 |
+
foreach (['url', 'key_pair_id', 'private_key'] as $required) {
|
131 |
+
if (!isset($options[$required])) {
|
132 |
+
throw new \InvalidArgumentException("$required is required");
|
133 |
+
}
|
134 |
+
}
|
135 |
+
|
136 |
+
$urlSigner = new UrlSigner(
|
137 |
+
$options['key_pair_id'],
|
138 |
+
$options['private_key']
|
139 |
+
);
|
140 |
+
|
141 |
+
return $urlSigner->getSignedUrl(
|
142 |
+
$options['url'],
|
143 |
+
isset($options['expires']) ? $options['expires'] : null,
|
144 |
+
isset($options['policy']) ? $options['policy'] : null
|
145 |
+
);
|
146 |
+
}
|
147 |
+
|
148 |
+
/**
|
149 |
+
* Create a signed Amazon CloudFront cookie.
|
150 |
+
*
|
151 |
+
* This method accepts an array of configuration options:
|
152 |
+
*
|
153 |
+
* - url: (string) URL of the resource being signed (can include query
|
154 |
+
* string and wildcards). For example: http://d111111abcdef8.cloudfront.net/images/horizon.jpg?size=large&license=yes
|
155 |
+
* - policy: (string) JSON policy. Use this option when creating a signed
|
156 |
+
* URL for a custom policy.
|
157 |
+
* - expires: (int) UTC Unix timestamp used when signing with a canned
|
158 |
+
* policy. Not required when passing a custom 'policy' option.
|
159 |
+
* - key_pair_id: (string) The ID of the key pair used to sign CloudFront
|
160 |
+
* URLs for private distributions.
|
161 |
+
* - private_key: (string) The filepath ot the private key used to sign
|
162 |
+
* CloudFront URLs for private distributions.
|
163 |
+
*
|
164 |
+
* @param array $options Array of configuration options used when signing
|
165 |
+
*
|
166 |
+
* @return array Key => value pairs of signed cookies to set
|
167 |
+
* @throws \InvalidArgumentException if url, key_pair_id, or private_key
|
168 |
+
* were not specified.
|
169 |
+
* @link http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/WorkingWithStreamingDistributions.html
|
170 |
+
*/
|
171 |
+
public function getSignedCookie(array $options)
|
172 |
+
{
|
173 |
+
foreach (['key_pair_id', 'private_key'] as $required) {
|
174 |
+
if (!isset($options[$required])) {
|
175 |
+
throw new \InvalidArgumentException("$required is required");
|
176 |
+
}
|
177 |
+
}
|
178 |
+
|
179 |
+
$cookieSigner = new CookieSigner(
|
180 |
+
$options['key_pair_id'],
|
181 |
+
$options['private_key']
|
182 |
+
);
|
183 |
+
|
184 |
+
return $cookieSigner->getSignedCookie(
|
185 |
+
isset($options['url']) ? $options['url'] : null,
|
186 |
+
isset($options['expires']) ? $options['expires'] : null,
|
187 |
+
isset($options['policy']) ? $options['policy'] : null
|
188 |
+
);
|
189 |
+
}
|
190 |
+
}
|
lib/Aws/Aws/CloudFront/CookieSigner.php
ADDED
@@ -0,0 +1,65 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\CloudFront;
|
3 |
+
|
4 |
+
class CookieSigner
|
5 |
+
{
|
6 |
+
/** @var Signer */
|
7 |
+
private $signer;
|
8 |
+
|
9 |
+
private static $schemes = [
|
10 |
+
'http' => true,
|
11 |
+
'https' => true,
|
12 |
+
];
|
13 |
+
|
14 |
+
/**
|
15 |
+
* @param $keyPairId string ID of the key pair
|
16 |
+
* @param $privateKey string Path to the private key used for signing
|
17 |
+
*
|
18 |
+
* @throws \RuntimeException if the openssl extension is missing
|
19 |
+
* @throws \InvalidArgumentException if the private key cannot be found.
|
20 |
+
*/
|
21 |
+
public function __construct($keyPairId, $privateKey)
|
22 |
+
{
|
23 |
+
$this->signer = new Signer($keyPairId, $privateKey);
|
24 |
+
}
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Create a signed Amazon CloudFront Cookie.
|
28 |
+
*
|
29 |
+
* @param string $url URL to sign (can include query string
|
30 |
+
* and wildcards). Not required
|
31 |
+
* when passing a custom $policy.
|
32 |
+
* @param string|integer|null $expires UTC Unix timestamp used when signing
|
33 |
+
* with a canned policy. Not required
|
34 |
+
* when passing a custom $policy.
|
35 |
+
* @param string $policy JSON policy. Use this option when
|
36 |
+
* creating a signed cookie for a custom
|
37 |
+
* policy.
|
38 |
+
*
|
39 |
+
* @return array The authenticated cookie parameters
|
40 |
+
* @throws \InvalidArgumentException if the URL provided is invalid
|
41 |
+
* @link http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-signed-cookies.html
|
42 |
+
*/
|
43 |
+
public function getSignedCookie($url = null, $expires = null, $policy = null)
|
44 |
+
{
|
45 |
+
if ($url) {
|
46 |
+
$this->validateUrl($url);
|
47 |
+
}
|
48 |
+
|
49 |
+
$cookieParameters = [];
|
50 |
+
$signature = $this->signer->getSignature($url, $expires, $policy);
|
51 |
+
foreach ($signature as $key => $value) {
|
52 |
+
$cookieParameters["CloudFront-$key"] = $value;
|
53 |
+
}
|
54 |
+
|
55 |
+
return $cookieParameters;
|
56 |
+
}
|
57 |
+
|
58 |
+
private function validateUrl($url)
|
59 |
+
{
|
60 |
+
$scheme = str_replace('*', '', explode('://', $url)[0]);
|
61 |
+
if (empty(self::$schemes[strtolower($scheme)])) {
|
62 |
+
throw new \InvalidArgumentException('Invalid or missing URI scheme');
|
63 |
+
}
|
64 |
+
}
|
65 |
+
}
|
lib/Aws/Aws/CloudFront/Exception/CloudFrontException.php
ADDED
@@ -0,0 +1,9 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\CloudFront\Exception;
|
3 |
+
|
4 |
+
use Aws\Exception\AwsException;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents an error interacting with the Amazon CloudFront service.
|
8 |
+
*/
|
9 |
+
class CloudFrontException extends AwsException {}
|
lib/Aws/Aws/CloudFront/Signer.php
ADDED
@@ -0,0 +1,117 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\CloudFront;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* @internal
|
6 |
+
*/
|
7 |
+
class Signer
|
8 |
+
{
|
9 |
+
private $keyPairId;
|
10 |
+
private $pkHandle;
|
11 |
+
|
12 |
+
/**
|
13 |
+
* A signer for creating the signature values used in CloudFront signed URLs
|
14 |
+
* and signed cookies.
|
15 |
+
*
|
16 |
+
* @param $keyPairId string ID of the key pair
|
17 |
+
* @param $privateKey string Path to the private key used for signing
|
18 |
+
*
|
19 |
+
* @throws \RuntimeException if the openssl extension is missing
|
20 |
+
* @throws \InvalidArgumentException if the private key cannot be found.
|
21 |
+
*/
|
22 |
+
public function __construct($keyPairId, $privateKey)
|
23 |
+
{
|
24 |
+
if (!extension_loaded('openssl')) {
|
25 |
+
//@codeCoverageIgnoreStart
|
26 |
+
throw new \RuntimeException('The openssl extension is required to '
|
27 |
+
. 'sign CloudFront urls.');
|
28 |
+
//@codeCoverageIgnoreEnd
|
29 |
+
}
|
30 |
+
|
31 |
+
$this->keyPairId = $keyPairId;
|
32 |
+
|
33 |
+
if (!file_exists($privateKey)) {
|
34 |
+
throw new \InvalidArgumentException("PK file not found: $privateKey");
|
35 |
+
}
|
36 |
+
|
37 |
+
$this->pkHandle = openssl_pkey_get_private("file://$privateKey");
|
38 |
+
|
39 |
+
if (!$this->pkHandle) {
|
40 |
+
throw new \InvalidArgumentException(openssl_error_string());
|
41 |
+
}
|
42 |
+
}
|
43 |
+
|
44 |
+
public function __destruct()
|
45 |
+
{
|
46 |
+
$this->pkHandle && openssl_pkey_free($this->pkHandle);
|
47 |
+
}
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Create the values used to construct signed URLs and cookies.
|
51 |
+
*
|
52 |
+
* @param string $resource The CloudFront resource to which
|
53 |
+
* this signature will grant access.
|
54 |
+
* Not used when a custom policy is
|
55 |
+
* provided.
|
56 |
+
* @param string|integer|null $expires UTC Unix timestamp used when
|
57 |
+
* signing with a canned policy.
|
58 |
+
* Not required when passing a
|
59 |
+
* custom $policy.
|
60 |
+
* @param string $policy JSON policy. Use this option when
|
61 |
+
* creating a signature for a custom
|
62 |
+
* policy.
|
63 |
+
*
|
64 |
+
* @return array The values needed to construct a signed URL or cookie
|
65 |
+
* @throws \InvalidArgumentException when not provided either a policy or a
|
66 |
+
* resource and a expires
|
67 |
+
*
|
68 |
+
* @link http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-signed-cookies.html
|
69 |
+
*/
|
70 |
+
public function getSignature($resource = null, $expires = null, $policy = null)
|
71 |
+
{
|
72 |
+
$signatureHash = [];
|
73 |
+
if ($policy) {
|
74 |
+
$policy = preg_replace('/\s/s', '', $policy);
|
75 |
+
$signatureHash['Policy'] = $this->encode($policy);
|
76 |
+
} elseif ($resource && $expires) {
|
77 |
+
$expires = (int) $expires; // Handle epoch passed as string
|
78 |
+
$policy = $this->createCannedPolicy($resource, $expires);
|
79 |
+
$signatureHash['Expires'] = $expires;
|
80 |
+
} else {
|
81 |
+
throw new \InvalidArgumentException('Either a policy or a resource'
|
82 |
+
. ' and an expiration time must be provided.');
|
83 |
+
}
|
84 |
+
|
85 |
+
$signatureHash['Signature'] = $this->encode($this->sign($policy));
|
86 |
+
$signatureHash['Key-Pair-Id'] = $this->keyPairId;
|
87 |
+
|
88 |
+
return $signatureHash;
|
89 |
+
}
|
90 |
+
|
91 |
+
private function createCannedPolicy($resource, $expiration)
|
92 |
+
{
|
93 |
+
return json_encode([
|
94 |
+
'Statement' => [
|
95 |
+
[
|
96 |
+
'Resource' => $resource,
|
97 |
+
'Condition' => [
|
98 |
+
'DateLessThan' => ['AWS:EpochTime' => $expiration],
|
99 |
+
],
|
100 |
+
],
|
101 |
+
],
|
102 |
+
], JSON_UNESCAPED_SLASHES);
|
103 |
+
}
|
104 |
+
|
105 |
+
private function sign($policy)
|
106 |
+
{
|
107 |
+
$signature = '';
|
108 |
+
openssl_sign($policy, $signature, $this->pkHandle);
|
109 |
+
|
110 |
+
return $signature;
|
111 |
+
}
|
112 |
+
|
113 |
+
private function encode($policy)
|
114 |
+
{
|
115 |
+
return strtr(base64_encode($policy), '+=/', '-_~');
|
116 |
+
}
|
117 |
+
}
|
lib/Aws/Aws/CloudFront/UrlSigner.php
ADDED
@@ -0,0 +1,119 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\CloudFront;
|
3 |
+
|
4 |
+
use GuzzleHttp\Psr7;
|
5 |
+
use GuzzleHttp\Psr7\Uri;
|
6 |
+
use Psr\Http\Message\UriInterface;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Creates signed URLs for Amazon CloudFront resources.
|
10 |
+
*/
|
11 |
+
class UrlSigner
|
12 |
+
{
|
13 |
+
private $signer;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* @param $keyPairId string ID of the key pair
|
17 |
+
* @param $privateKey string Path to the private key used for signing
|
18 |
+
*
|
19 |
+
* @throws \RuntimeException if the openssl extension is missing
|
20 |
+
* @throws \InvalidArgumentException if the private key cannot be found.
|
21 |
+
*/
|
22 |
+
public function __construct($keyPairId, $privateKey)
|
23 |
+
{
|
24 |
+
$this->signer = new Signer($keyPairId, $privateKey);
|
25 |
+
}
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Create a signed Amazon CloudFront URL.
|
29 |
+
*
|
30 |
+
* Keep in mind that URLs meant for use in media/flash players may have
|
31 |
+
* different requirements for URL formats (e.g. some require that the
|
32 |
+
* extension be removed, some require the file name to be prefixed
|
33 |
+
* - mp4:<path>, some require you to add "/cfx/st" into your URL).
|
34 |
+
*
|
35 |
+
* @param string $url URL to sign (can include query
|
36 |
+
* string string and wildcards)
|
37 |
+
* @param string|integer|null $expires UTC Unix timestamp used when signing
|
38 |
+
* with a canned policy. Not required
|
39 |
+
* when passing a custom $policy.
|
40 |
+
* @param string $policy JSON policy. Use this option when
|
41 |
+
* creating a signed URL for a custom
|
42 |
+
* policy.
|
43 |
+
*
|
44 |
+
* @return string The file URL with authentication parameters
|
45 |
+
* @throws \InvalidArgumentException if the URL provided is invalid
|
46 |
+
* @link http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/WorkingWithStreamingDistributions.html
|
47 |
+
*/
|
48 |
+
public function getSignedUrl($url, $expires = null, $policy = null)
|
49 |
+
{
|
50 |
+
// Determine the scheme of the url
|
51 |
+
$urlSections = explode('://', $url);
|
52 |
+
|
53 |
+
if (count($urlSections) < 2) {
|
54 |
+
throw new \InvalidArgumentException("Invalid URL: {$url}");
|
55 |
+
}
|
56 |
+
|
57 |
+
// Get the real scheme by removing wildcards from the scheme
|
58 |
+
$scheme = str_replace('*', '', $urlSections[0]);
|
59 |
+
$uri = new Uri($scheme . '://' . $urlSections[1]);
|
60 |
+
$query = Psr7\parse_query($uri->getQuery(), PHP_QUERY_RFC3986);
|
61 |
+
$signature = $this->signer->getSignature(
|
62 |
+
$this->createResource($scheme, (string) $uri),
|
63 |
+
$expires,
|
64 |
+
$policy
|
65 |
+
);
|
66 |
+
$uri = $uri->withQuery(
|
67 |
+
http_build_query($query + $signature, null, '&', PHP_QUERY_RFC3986)
|
68 |
+
);
|
69 |
+
|
70 |
+
return $scheme === 'rtmp'
|
71 |
+
? $this->createRtmpUrl($uri)
|
72 |
+
: (string) $uri;
|
73 |
+
}
|
74 |
+
|
75 |
+
private function createRtmpUrl(UriInterface $uri)
|
76 |
+
{
|
77 |
+
// Use a relative URL when creating Flash player URLs
|
78 |
+
$result = ltrim($uri->getPath(), '/');
|
79 |
+
|
80 |
+
if ($query = $uri->getQuery()) {
|
81 |
+
$result .= '?' . $query;
|
82 |
+
}
|
83 |
+
|
84 |
+
return $result;
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* @param $scheme
|
89 |
+
* @param $url
|
90 |
+
*
|
91 |
+
* @return string
|
92 |
+
*/
|
93 |
+
private function createResource($scheme, $url)
|
94 |
+
{
|
95 |
+
switch ($scheme) {
|
96 |
+
case 'http':
|
97 |
+
case 'http*':
|
98 |
+
case 'https':
|
99 |
+
return $url;
|
100 |
+
case 'rtmp':
|
101 |
+
$parts = parse_url($url);
|
102 |
+
$pathParts = pathinfo($parts['path']);
|
103 |
+
$resource = ltrim(
|
104 |
+
$pathParts['dirname'] . '/' . $pathParts['basename'],
|
105 |
+
'/'
|
106 |
+
);
|
107 |
+
|
108 |
+
// Add a query string if present.
|
109 |
+
if (isset($parts['query'])) {
|
110 |
+
$resource .= "?{$parts['query']}";
|
111 |
+
}
|
112 |
+
|
113 |
+
return $resource;
|
114 |
+
}
|
115 |
+
|
116 |
+
throw new \InvalidArgumentException("Invalid URI scheme: {$scheme}. "
|
117 |
+
. "Scheme must be one of: http, https, or rtmp");
|
118 |
+
}
|
119 |
+
}
|
lib/Aws/Aws/Command.php
ADDED
@@ -0,0 +1,62 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* AWS command object.
|
6 |
+
*/
|
7 |
+
class Command implements CommandInterface
|
8 |
+
{
|
9 |
+
use HasDataTrait;
|
10 |
+
|
11 |
+
/** @var string */
|
12 |
+
private $name;
|
13 |
+
|
14 |
+
/** @var HandlerList */
|
15 |
+
private $handlerList;
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Accepts an associative array of command options, including:
|
19 |
+
*
|
20 |
+
* - @http: (array) Associative array of transfer options.
|
21 |
+
*
|
22 |
+
* @param string $name Name of the command
|
23 |
+
* @param array $args Arguments to pass to the command
|
24 |
+
* @param HandlerList $list Handler list
|
25 |
+
*/
|
26 |
+
public function __construct($name, array $args = [], HandlerList $list = null)
|
27 |
+
{
|
28 |
+
$this->name = $name;
|
29 |
+
$this->data = $args;
|
30 |
+
$this->handlerList = $list ?: new HandlerList();
|
31 |
+
|
32 |
+
if (!isset($this->data['@http'])) {
|
33 |
+
$this->data['@http'] = [];
|
34 |
+
}
|
35 |
+
}
|
36 |
+
|
37 |
+
public function __clone()
|
38 |
+
{
|
39 |
+
$this->handlerList = clone $this->handlerList;
|
40 |
+
}
|
41 |
+
|
42 |
+
public function getName()
|
43 |
+
{
|
44 |
+
return $this->name;
|
45 |
+
}
|
46 |
+
|
47 |
+
public function hasParam($name)
|
48 |
+
{
|
49 |
+
return array_key_exists($name, $this->data);
|
50 |
+
}
|
51 |
+
|
52 |
+
public function getHandlerList()
|
53 |
+
{
|
54 |
+
return $this->handlerList;
|
55 |
+
}
|
56 |
+
|
57 |
+
/** @deprecated */
|
58 |
+
public function get($name)
|
59 |
+
{
|
60 |
+
return $this[$name];
|
61 |
+
}
|
62 |
+
}
|
lib/Aws/Aws/CommandInterface.php
ADDED
@@ -0,0 +1,42 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* A command object encapsulates the input parameters used to control the
|
6 |
+
* creation of a HTTP request and processing of a HTTP response.
|
7 |
+
*
|
8 |
+
* Using the toArray() method will return the input parameters of the command
|
9 |
+
* as an associative array.
|
10 |
+
*/
|
11 |
+
interface CommandInterface extends \ArrayAccess, \Countable, \IteratorAggregate
|
12 |
+
{
|
13 |
+
/**
|
14 |
+
* Converts the command parameters to an array
|
15 |
+
*
|
16 |
+
* @return array
|
17 |
+
*/
|
18 |
+
public function toArray();
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Get the name of the command
|
22 |
+
*
|
23 |
+
* @return string
|
24 |
+
*/
|
25 |
+
public function getName();
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Check if the command has a parameter by name.
|
29 |
+
*
|
30 |
+
* @param string $name Name of the parameter to check
|
31 |
+
*
|
32 |
+
* @return bool
|
33 |
+
*/
|
34 |
+
public function hasParam($name);
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Get the handler list used to transfer the command.
|
38 |
+
*
|
39 |
+
* @return HandlerList
|
40 |
+
*/
|
41 |
+
public function getHandlerList();
|
42 |
+
}
|
lib/Aws/Aws/CommandPool.php
ADDED
@@ -0,0 +1,150 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws;
|
3 |
+
|
4 |
+
use GuzzleHttp\Promise\PromisorInterface;
|
5 |
+
use GuzzleHttp\Promise\EachPromise;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Sends and iterator of commands concurrently using a capped pool size.
|
9 |
+
*
|
10 |
+
* The pool will read command objects from an iterator until it is cancelled or
|
11 |
+
* until the iterator is consumed.
|
12 |
+
*/
|
13 |
+
class CommandPool implements PromisorInterface
|
14 |
+
{
|
15 |
+
/** @var EachPromise */
|
16 |
+
private $each;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* The CommandPool constructor accepts a hash of configuration options:
|
20 |
+
*
|
21 |
+
* - concurrency: (callable|int) Maximum number of commands to execute
|
22 |
+
* concurrently. Provide a function to resize the pool dynamically. The
|
23 |
+
* function will be provided the current number of pending requests and
|
24 |
+
* is expected to return an integer representing the new pool size limit.
|
25 |
+
* - before: (callable) function to invoke before sending each command. The
|
26 |
+
* before function accepts the command and the key of the iterator of the
|
27 |
+
* command. You can mutate the command as needed in the before function
|
28 |
+
* before sending the command.
|
29 |
+
* - fulfilled: (callable) Function to invoke when a promise is fulfilled.
|
30 |
+
* The function is provided the result object, id of the iterator that the
|
31 |
+
* result came from, and the aggregate promise that can be resolved/rejected
|
32 |
+
* if you need to short-circuit the pool.
|
33 |
+
* - rejected: (callable) Function to invoke when a promise is rejected.
|
34 |
+
* The function is provided an AwsException object, id of the iterator that
|
35 |
+
* the exception came from, and the aggregate promise that can be
|
36 |
+
* resolved/rejected if you need to short-circuit the pool.
|
37 |
+
* - preserve_iterator_keys: (bool) Retain the iterator key when generating
|
38 |
+
* the commands.
|
39 |
+
*
|
40 |
+
* @param AwsClientInterface $client Client used to execute commands.
|
41 |
+
* @param array|\Iterator $commands Iterable that yields commands.
|
42 |
+
* @param array $config Associative array of options.
|
43 |
+
*/
|
44 |
+
public function __construct(
|
45 |
+
AwsClientInterface $client,
|
46 |
+
$commands,
|
47 |
+
array $config = []
|
48 |
+
) {
|
49 |
+
if (!isset($config['concurrency'])) {
|
50 |
+
$config['concurrency'] = 25;
|
51 |
+
}
|
52 |
+
|
53 |
+
$before = $this->getBefore($config);
|
54 |
+
$mapFn = function ($commands) use ($client, $before, $config) {
|
55 |
+
foreach ($commands as $key => $command) {
|
56 |
+
if (!($command instanceof CommandInterface)) {
|
57 |
+
throw new \InvalidArgumentException('Each value yielded by '
|
58 |
+
. 'the iterator must be an Aws\CommandInterface.');
|
59 |
+
}
|
60 |
+
if ($before) {
|
61 |
+
$before($command, $key);
|
62 |
+
}
|
63 |
+
if (!empty($config['preserve_iterator_keys'])) {
|
64 |
+
yield $key => $client->executeAsync($command);
|
65 |
+
} else {
|
66 |
+
yield $client->executeAsync($command);
|
67 |
+
}
|
68 |
+
}
|
69 |
+
};
|
70 |
+
|
71 |
+
$this->each = new EachPromise($mapFn($commands), $config);
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* @return \GuzzleHttp\Promise\PromiseInterface
|
76 |
+
*/
|
77 |
+
public function promise()
|
78 |
+
{
|
79 |
+
return $this->each->promise();
|
80 |
+
}
|
81 |
+
|
82 |
+
/**
|
83 |
+
* Executes a pool synchronously and aggregates the results of the pool
|
84 |
+
* into an indexed array in the same order as the passed in array.
|
85 |
+
*
|
86 |
+
* @param AwsClientInterface $client Client used to execute commands.
|
87 |
+
* @param mixed $commands Iterable that yields commands.
|
88 |
+
* @param array $config Configuration options.
|
89 |
+
*
|
90 |
+
* @return array
|
91 |
+
* @see \Aws\CommandPool::__construct for available configuration options.
|
92 |
+
*/
|
93 |
+
public static function batch(
|
94 |
+
AwsClientInterface $client,
|
95 |
+
$commands,
|
96 |
+
array $config = []
|
97 |
+
) {
|
98 |
+
$results = [];
|
99 |
+
self::cmpCallback($config, 'fulfilled', $results);
|
100 |
+
self::cmpCallback($config, 'rejected', $results);
|
101 |
+
|
102 |
+
return (new self($client, $commands, $config))
|
103 |
+
->promise()
|
104 |
+
->then(static function () use (&$results) {
|
105 |
+
ksort($results);
|
106 |
+
return $results;
|
107 |
+
})
|
108 |
+
->wait();
|
109 |
+
}
|
110 |
+
|
111 |
+
/**
|
112 |
+
* @return callable
|
113 |
+
*/
|
114 |
+
private function getBefore(array $config)
|
115 |
+
{
|
116 |
+
if (!isset($config['before'])) {
|
117 |
+
return null;
|
118 |
+
}
|
119 |
+
|
120 |
+
if (is_callable($config['before'])) {
|
121 |
+
return $config['before'];
|
122 |
+
}
|
123 |
+
|
124 |
+
throw new \InvalidArgumentException('before must be callable');
|
125 |
+
}
|
126 |
+
|
127 |
+
/**
|
128 |
+
* Adds an onFulfilled or onRejected callback that aggregates results into
|
129 |
+
* an array. If a callback is already present, it is replaced with the
|
130 |
+
* composed function.
|
131 |
+
*
|
132 |
+
* @param array $config
|
133 |
+
* @param $name
|
134 |
+
* @param array $results
|
135 |
+
*/
|
136 |
+
private static function cmpCallback(array &$config, $name, array &$results)
|
137 |
+
{
|
138 |
+
if (!isset($config[$name])) {
|
139 |
+
$config[$name] = function ($v, $k) use (&$results) {
|
140 |
+
$results[$k] = $v;
|
141 |
+
};
|
142 |
+
} else {
|
143 |
+
$currentFn = $config[$name];
|
144 |
+
$config[$name] = function ($v, $k) use (&$results, $currentFn) {
|
145 |
+
$currentFn($v, $k);
|
146 |
+
$results[$k] = $v;
|
147 |
+
};
|
148 |
+
}
|
149 |
+
}
|
150 |
+
}
|
lib/Aws/Aws/Credentials/AssumeRoleCredentialProvider.php
ADDED
@@ -0,0 +1,64 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Credentials;
|
3 |
+
|
4 |
+
use Aws\Exception\CredentialsException;
|
5 |
+
use Aws\Result;
|
6 |
+
use Aws\Sts\StsClient;
|
7 |
+
use GuzzleHttp\Promise\PromiseInterface;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Credential provider that provides credentials via assuming a role
|
11 |
+
* More Information, see: http://docs.aws.amazon.com/aws-sdk-php/v3/api/api-sts-2011-06-15.html#assumerole
|
12 |
+
*/
|
13 |
+
class AssumeRoleCredentialProvider
|
14 |
+
{
|
15 |
+
const ERROR_MSG = "Missing required 'AssumeRoleCredentialProvider' configuration option: ";
|
16 |
+
|
17 |
+
/** @var StsClient */
|
18 |
+
private $client;
|
19 |
+
|
20 |
+
/** @var array */
|
21 |
+
private $assumeRoleParams;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* The constructor requires following configure parameters:
|
25 |
+
* - client: a StsClient
|
26 |
+
* - assume_role_params: Parameters used to make assumeRole call
|
27 |
+
*
|
28 |
+
* @param array $config Configuration options
|
29 |
+
* @throws \InvalidArgumentException
|
30 |
+
*/
|
31 |
+
public function __construct(array $config = [])
|
32 |
+
{
|
33 |
+
if (!isset($config['assume_role_params'])) {
|
34 |
+
throw new \InvalidArgumentException(self::ERROR_MSG . "'assume_role_params'.");
|
35 |
+
}
|
36 |
+
|
37 |
+
if (!isset($config['client'])) {
|
38 |
+
throw new \InvalidArgumentException(self::ERROR_MSG . "'client'.");
|
39 |
+
}
|
40 |
+
|
41 |
+
$this->client = $config['client'];
|
42 |
+
$this->assumeRoleParams = $config['assume_role_params'];
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Loads assume role credentials.
|
47 |
+
*
|
48 |
+
* @return PromiseInterface
|
49 |
+
*/
|
50 |
+
public function __invoke()
|
51 |
+
{
|
52 |
+
$client = $this->client;
|
53 |
+
return $client->assumeRoleAsync($this->assumeRoleParams)
|
54 |
+
->then(function (Result $result) {
|
55 |
+
return $this->client->createCredentials($result);
|
56 |
+
})->otherwise(function (\RuntimeException $exception) {
|
57 |
+
throw new CredentialsException(
|
58 |
+
"Error in retrieving assume role credentials.",
|
59 |
+
0,
|
60 |
+
$exception
|
61 |
+
);
|
62 |
+
});
|
63 |
+
}
|
64 |
+
}
|
lib/Aws/Aws/Credentials/CredentialProvider.php
ADDED
@@ -0,0 +1,488 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Credentials;
|
3 |
+
|
4 |
+
use Aws;
|
5 |
+
use Aws\Api\DateTimeResult;
|
6 |
+
use Aws\CacheInterface;
|
7 |
+
use Aws\Exception\CredentialsException;
|
8 |
+
use GuzzleHttp\Promise;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Credential providers are functions that accept no arguments and return a
|
12 |
+
* promise that is fulfilled with an {@see \Aws\Credentials\CredentialsInterface}
|
13 |
+
* or rejected with an {@see \Aws\Exception\CredentialsException}.
|
14 |
+
*
|
15 |
+
* <code>
|
16 |
+
* use Aws\Credentials\CredentialProvider;
|
17 |
+
* $provider = CredentialProvider::defaultProvider();
|
18 |
+
* // Returns a CredentialsInterface or throws.
|
19 |
+
* $creds = $provider()->wait();
|
20 |
+
* </code>
|
21 |
+
*
|
22 |
+
* Credential providers can be composed to create credentials using conditional
|
23 |
+
* logic that can create different credentials in different environments. You
|
24 |
+
* can compose multiple providers into a single provider using
|
25 |
+
* {@see Aws\Credentials\CredentialProvider::chain}. This function accepts
|
26 |
+
* providers as variadic arguments and returns a new function that will invoke
|
27 |
+
* each provider until a successful set of credentials is returned.
|
28 |
+
*
|
29 |
+
* <code>
|
30 |
+
* // First try an INI file at this location.
|
31 |
+
* $a = CredentialProvider::ini(null, '/path/to/file.ini');
|
32 |
+
* // Then try an INI file at this location.
|
33 |
+
* $b = CredentialProvider::ini(null, '/path/to/other-file.ini');
|
34 |
+
* // Then try loading from environment variables.
|
35 |
+
* $c = CredentialProvider::env();
|
36 |
+
* // Combine the three providers together.
|
37 |
+
* $composed = CredentialProvider::chain($a, $b, $c);
|
38 |
+
* // Returns a promise that is fulfilled with credentials or throws.
|
39 |
+
* $promise = $composed();
|
40 |
+
* // Wait on the credentials to resolve.
|
41 |
+
* $creds = $promise->wait();
|
42 |
+
* </code>
|
43 |
+
*/
|
44 |
+
class CredentialProvider
|
45 |
+
{
|
46 |
+
const ENV_KEY = 'AWS_ACCESS_KEY_ID';
|
47 |
+
const ENV_SECRET = 'AWS_SECRET_ACCESS_KEY';
|
48 |
+
const ENV_SESSION = 'AWS_SESSION_TOKEN';
|
49 |
+
const ENV_PROFILE = 'AWS_PROFILE';
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Create a default credential provider that first checks for environment
|
53 |
+
* variables, then checks for the "default" profile in ~/.aws/credentials,
|
54 |
+
* then checks for "profile default" profile in ~/.aws/config (which is
|
55 |
+
* the default profile of AWS CLI), then tries to make a GET Request to
|
56 |
+
* fetch credentials if Ecs environment variable is presented, then checks
|
57 |
+
* for credential_process in the "default" profile in ~/.aws/credentials,
|
58 |
+
* then for credential_process in the "default profile" profile in
|
59 |
+
* ~/.aws/config, and finally checks for EC2 instance profile credentials.
|
60 |
+
*
|
61 |
+
* This provider is automatically wrapped in a memoize function that caches
|
62 |
+
* previously provided credentials.
|
63 |
+
*
|
64 |
+
* @param array $config Optional array of ecs/instance profile credentials
|
65 |
+
* provider options.
|
66 |
+
*
|
67 |
+
* @return callable
|
68 |
+
*/
|
69 |
+
public static function defaultProvider(array $config = [])
|
70 |
+
{
|
71 |
+
$localCredentialProviders = self::localCredentialProviders();
|
72 |
+
$remoteCredentialProviders = self::remoteCredentialProviders($config);
|
73 |
+
|
74 |
+
return self::memoize(
|
75 |
+
call_user_func_array(
|
76 |
+
'self::chain',
|
77 |
+
array_merge($localCredentialProviders, $remoteCredentialProviders)
|
78 |
+
)
|
79 |
+
);
|
80 |
+
}
|
81 |
+
|
82 |
+
/**
|
83 |
+
* Create a credential provider function from a set of static credentials.
|
84 |
+
*
|
85 |
+
* @param CredentialsInterface $creds
|
86 |
+
*
|
87 |
+
* @return callable
|
88 |
+
*/
|
89 |
+
public static function fromCredentials(CredentialsInterface $creds)
|
90 |
+
{
|
91 |
+
$promise = Promise\promise_for($creds);
|
92 |
+
|
93 |
+
return function () use ($promise) {
|
94 |
+
return $promise;
|
95 |
+
};
|
96 |
+
}
|
97 |
+
|
98 |
+
/**
|
99 |
+
* Creates an aggregate credentials provider that invokes the provided
|
100 |
+
* variadic providers one after the other until a provider returns
|
101 |
+
* credentials.
|
102 |
+
*
|
103 |
+
* @return callable
|
104 |
+
*/
|
105 |
+
public static function chain()
|
106 |
+
{
|
107 |
+
$links = func_get_args();
|
108 |
+
if (empty($links)) {
|
109 |
+
throw new \InvalidArgumentException('No providers in chain');
|
110 |
+
}
|
111 |
+
|
112 |
+
return function () use ($links) {
|
113 |
+
/** @var callable $parent */
|
114 |
+
$parent = array_shift($links);
|
115 |
+
$promise = $parent();
|
116 |
+
while ($next = array_shift($links)) {
|
117 |
+
$promise = $promise->otherwise($next);
|
118 |
+
}
|
119 |
+
return $promise;
|
120 |
+
};
|
121 |
+
}
|
122 |
+
|
123 |
+
/**
|
124 |
+
* Wraps a credential provider and caches previously provided credentials.
|
125 |
+
*
|
126 |
+
* Ensures that cached credentials are refreshed when they expire.
|
127 |
+
*
|
128 |
+
* @param callable $provider Credentials provider function to wrap.
|
129 |
+
*
|
130 |
+
* @return callable
|
131 |
+
*/
|
132 |
+
public static function memoize(callable $provider)
|
133 |
+
{
|
134 |
+
return function () use ($provider) {
|
135 |
+
static $result;
|
136 |
+
static $isConstant;
|
137 |
+
|
138 |
+
// Constant credentials will be returned constantly.
|
139 |
+
if ($isConstant) {
|
140 |
+
return $result;
|
141 |
+
}
|
142 |
+
|
143 |
+
// Create the initial promise that will be used as the cached value
|
144 |
+
// until it expires.
|
145 |
+
if (null === $result) {
|
146 |
+
$result = $provider();
|
147 |
+
}
|
148 |
+
|
149 |
+
// Return credentials that could expire and refresh when needed.
|
150 |
+
return $result
|
151 |
+
->then(function (CredentialsInterface $creds) use ($provider, &$isConstant, &$result) {
|
152 |
+
// Determine if these are constant credentials.
|
153 |
+
if (!$creds->getExpiration()) {
|
154 |
+
$isConstant = true;
|
155 |
+
return $creds;
|
156 |
+
}
|
157 |
+
|
158 |
+
// Refresh expired credentials.
|
159 |
+
if (!$creds->isExpired()) {
|
160 |
+
return $creds;
|
161 |
+
}
|
162 |
+
// Refresh the result and forward the promise.
|
163 |
+
return $result = $provider();
|
164 |
+
})
|
165 |
+
->otherwise(function($reason) use (&$result) {
|
166 |
+
// Cleanup rejected promise.
|
167 |
+
$result = null;
|
168 |
+
return new Promise\RejectedPromise($reason);
|
169 |
+
});
|
170 |
+
};
|
171 |
+
}
|
172 |
+
|
173 |
+
/**
|
174 |
+
* Wraps a credential provider and saves provided credentials in an
|
175 |
+
* instance of Aws\CacheInterface. Forwards calls when no credentials found
|
176 |
+
* in cache and updates cache with the results.
|
177 |
+
*
|
178 |
+
* Defaults to using a simple file-based cache when none provided.
|
179 |
+
*
|
180 |
+
* @param callable $provider Credentials provider function to wrap
|
181 |
+
* @param CacheInterface $cache Cache to store credentials
|
182 |
+
* @param string|null $cacheKey (optional) Cache key to use
|
183 |
+
*
|
184 |
+
* @return callable
|
185 |
+
*/
|
186 |
+
public static function cache(
|
187 |
+
callable $provider,
|
188 |
+
CacheInterface $cache,
|
189 |
+
$cacheKey = null
|
190 |
+
) {
|
191 |
+
$cacheKey = $cacheKey ?: 'aws_cached_credentials';
|
192 |
+
|
193 |
+
return function () use ($provider, $cache, $cacheKey) {
|
194 |
+
$found = $cache->get($cacheKey);
|
195 |
+
if ($found instanceof CredentialsInterface && !$found->isExpired()) {
|
196 |
+
return Promise\promise_for($found);
|
197 |
+
}
|
198 |
+
|
199 |
+
return $provider()
|
200 |
+
->then(function (CredentialsInterface $creds) use (
|
201 |
+
$cache,
|
202 |
+
$cacheKey
|
203 |
+
) {
|
204 |
+
$cache->set(
|
205 |
+
$cacheKey,
|
206 |
+
$creds,
|
207 |
+
null === $creds->getExpiration() ?
|
208 |
+
0 : $creds->getExpiration() - time()
|
209 |
+
);
|
210 |
+
|
211 |
+
return $creds;
|
212 |
+
});
|
213 |
+
};
|
214 |
+
}
|
215 |
+
|
216 |
+
/**
|
217 |
+
* Provider that creates credentials from environment variables
|
218 |
+
* AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_SESSION_TOKEN.
|
219 |
+
*
|
220 |
+
* @return callable
|
221 |
+
*/
|
222 |
+
public static function env()
|
223 |
+
{
|
224 |
+
return function () {
|
225 |
+
// Use credentials from environment variables, if available
|
226 |
+
$key = getenv(self::ENV_KEY);
|
227 |
+
$secret = getenv(self::ENV_SECRET);
|
228 |
+
if ($key && $secret) {
|
229 |
+
return Promise\promise_for(
|
230 |
+
new Credentials($key, $secret, getenv(self::ENV_SESSION) ?: NULL)
|
231 |
+
);
|
232 |
+
}
|
233 |
+
|
234 |
+
return self::reject('Could not find environment variable '
|
235 |
+
. 'credentials in ' . self::ENV_KEY . '/' . self::ENV_SECRET);
|
236 |
+
};
|
237 |
+
}
|
238 |
+
|
239 |
+
/**
|
240 |
+
* Credential provider that creates credentials using instance profile
|
241 |
+
* credentials.
|
242 |
+
*
|
243 |
+
* @param array $config Array of configuration data.
|
244 |
+
*
|
245 |
+
* @return InstanceProfileProvider
|
246 |
+
* @see Aws\Credentials\InstanceProfileProvider for $config details.
|
247 |
+
*/
|
248 |
+
public static function instanceProfile(array $config = [])
|
249 |
+
{
|
250 |
+
return new InstanceProfileProvider($config);
|
251 |
+
}
|
252 |
+
|
253 |
+
/**
|
254 |
+
* Credential provider that creates credentials using
|
255 |
+
* ecs credentials by a GET request, whose uri is specified
|
256 |
+
* by environment variable
|
257 |
+
*
|
258 |
+
* @param array $config Array of configuration data.
|
259 |
+
*
|
260 |
+
* @return EcsCredentialProvider
|
261 |
+
* @see Aws\Credentials\EcsCredentialProvider for $config details.
|
262 |
+
*/
|
263 |
+
public static function ecsCredentials(array $config = [])
|
264 |
+
{
|
265 |
+
return new EcsCredentialProvider($config);
|
266 |
+
}
|
267 |
+
|
268 |
+
/**
|
269 |
+
* Credential provider that creates credentials using assume role
|
270 |
+
*
|
271 |
+
* @param array $config Array of configuration data
|
272 |
+
* @return callable
|
273 |
+
* @see Aws\Credentials\AssumeRoleCredentialProvider for $config details.
|
274 |
+
*/
|
275 |
+
public static function assumeRole(array $config=[])
|
276 |
+
{
|
277 |
+
return new AssumeRoleCredentialProvider($config);
|
278 |
+
}
|
279 |
+
|
280 |
+
/**
|
281 |
+
* Credentials provider that creates credentials using an ini file stored
|
282 |
+
* in the current user's home directory.
|
283 |
+
*
|
284 |
+
* @param string|null $profile Profile to use. If not specified will use
|
285 |
+
* the "default" profile in "~/.aws/credentials".
|
286 |
+
* @param string|null $filename If provided, uses a custom filename rather
|
287 |
+
* than looking in the home directory.
|
288 |
+
*
|
289 |
+
* @return callable
|
290 |
+
*/
|
291 |
+
public static function ini($profile = null, $filename = null)
|
292 |
+
{
|
293 |
+
$filename = $filename ?: (self::getHomeDir() . '/.aws/credentials');
|
294 |
+
$profile = $profile ?: (getenv(self::ENV_PROFILE) ?: 'default');
|
295 |
+
|
296 |
+
return function () use ($profile, $filename) {
|
297 |
+
if (!is_readable($filename)) {
|
298 |
+
return self::reject("Cannot read credentials from $filename");
|
299 |
+
}
|
300 |
+
$data = \Aws\parse_ini_file($filename, true, INI_SCANNER_RAW);
|
301 |
+
if ($data === false) {
|
302 |
+
return self::reject("Invalid credentials file: $filename");
|
303 |
+
}
|
304 |
+
if (!isset($data[$profile])) {
|
305 |
+
return self::reject("'$profile' not found in credentials file");
|
306 |
+
}
|
307 |
+
if (!isset($data[$profile]['aws_access_key_id'])
|
308 |
+
|| !isset($data[$profile]['aws_secret_access_key'])
|
309 |
+
) {
|
310 |
+
return self::reject("No credentials present in INI profile "
|
311 |
+
. "'$profile' ($filename)");
|
312 |
+
}
|
313 |
+
|
314 |
+
if (empty($data[$profile]['aws_session_token'])) {
|
315 |
+
$data[$profile]['aws_session_token']
|
316 |
+
= isset($data[$profile]['aws_security_token'])
|
317 |
+
? $data[$profile]['aws_security_token']
|
318 |
+
: null;
|
319 |
+
}
|
320 |
+
|
321 |
+
return Promise\promise_for(
|
322 |
+
new Credentials(
|
323 |
+
$data[$profile]['aws_access_key_id'],
|
324 |
+
$data[$profile]['aws_secret_access_key'],
|
325 |
+
$data[$profile]['aws_session_token']
|
326 |
+
)
|
327 |
+
);
|
328 |
+
};
|
329 |
+
}
|
330 |
+
|
331 |
+
/**
|
332 |
+
* Credentials provider that creates credentials using a process configured in
|
333 |
+
* ini file stored in the current user's home directory.
|
334 |
+
*
|
335 |
+
* @param string|null $profile Profile to use. If not specified will use
|
336 |
+
* the "default" profile in "~/.aws/credentials".
|
337 |
+
* @param string|null $filename If provided, uses a custom filename rather
|
338 |
+
* than looking in the home directory.
|
339 |
+
*
|
340 |
+
* @return callable
|
341 |
+
*/
|
342 |
+
public static function process($profile = null, $filename = null)
|
343 |
+
{
|
344 |
+
$filename = $filename ?: (self::getHomeDir() . '/.aws/credentials');
|
345 |
+
$profile = $profile ?: (getenv(self::ENV_PROFILE) ?: 'default');
|
346 |
+
|
347 |
+
return function () use ($profile, $filename) {
|
348 |
+
if (!is_readable($filename)) {
|
349 |
+
return self::reject("Cannot read process credentials from $filename");
|
350 |
+
}
|
351 |
+
$data = \Aws\parse_ini_file($filename, true, INI_SCANNER_RAW);
|
352 |
+
if ($data === false) {
|
353 |
+
return self::reject("Invalid credentials file: $filename");
|
354 |
+
}
|
355 |
+
if (!isset($data[$profile])) {
|
356 |
+
return self::reject("'$profile' not found in credentials file");
|
357 |
+
}
|
358 |
+
if (!isset($data[$profile]['credential_process'])
|
359 |
+
) {
|
360 |
+
return self::reject("No credential_process present in INI profile "
|
361 |
+
. "'$profile' ($filename)");
|
362 |
+
}
|
363 |
+
|
364 |
+
$credentialProcess = $data[$profile]['credential_process'];
|
365 |
+
$json = shell_exec($credentialProcess);
|
366 |
+
|
367 |
+
$processData = json_decode($json, true);
|
368 |
+
|
369 |
+
// Only support version 1
|
370 |
+
if (isset($processData['Version'])) {
|
371 |
+
if ($processData['Version'] !== 1) {
|
372 |
+
return self::reject("credential_process does not return Version == 1");
|
373 |
+
}
|
374 |
+
}
|
375 |
+
|
376 |
+
if (!isset($processData['AccessKeyId']) || !isset($processData['SecretAccessKey'])) {
|
377 |
+
return self::reject("credential_process does not return valid credentials");
|
378 |
+
}
|
379 |
+
|
380 |
+
if (isset($processData['Expiration'])) {
|
381 |
+
try {
|
382 |
+
$expiration = new DateTimeResult($processData['Expiration']);
|
383 |
+
} catch (\Exception $e) {
|
384 |
+
return self::reject("credential_process returned invalid expiration");
|
385 |
+
}
|
386 |
+
$now = new DateTimeResult();
|
387 |
+
if ($expiration < $now) {
|
388 |
+
return self::reject("credential_process returned expired credentials");
|
389 |
+
}
|
390 |
+
} else {
|
391 |
+
$processData['Expiration'] = null;
|
392 |
+
}
|
393 |
+
|
394 |
+
if (empty($processData['SessionToken'])) {
|
395 |
+
$processData['SessionToken'] = null;
|
396 |
+
}
|
397 |
+
|
398 |
+
return Promise\promise_for(
|
399 |
+
new Credentials(
|
400 |
+
$processData['AccessKeyId'],
|
401 |
+
$processData['SecretAccessKey'],
|
402 |
+
$processData['SessionToken'],
|
403 |
+
$processData['Expiration']
|
404 |
+
)
|
405 |
+
);
|
406 |
+
};
|
407 |
+
}
|
408 |
+
|
409 |
+
|
410 |
+
/**
|
411 |
+
* Local credential providers returns a list of local credential providers
|
412 |
+
* in following order:
|
413 |
+
* - credentials from environment variables
|
414 |
+
* - 'default' profile in '.aws/credentials' file
|
415 |
+
* - 'profile default' profile in '.aws/config' file
|
416 |
+
*
|
417 |
+
* @return array
|
418 |
+
*/
|
419 |
+
private static function localCredentialProviders()
|
420 |
+
{
|
421 |
+
return [
|
422 |
+
self::env(),
|
423 |
+
self::ini(),
|
424 |
+
self::ini('profile default', self::getHomeDir() . '/.aws/config')
|
425 |
+
];
|
426 |
+
}
|
427 |
+
|
428 |
+
/**
|
429 |
+
* Remote credential providers returns a list of credentials providers
|
430 |
+
* for the remote endpoints such as EC2 or ECS Roles.
|
431 |
+
*
|
432 |
+
* @param array $config Array of configuration data.
|
433 |
+
*
|
434 |
+
* @return array
|
435 |
+
* @see Aws\Credentials\InstanceProfileProvider for $config details.
|
436 |
+
* @see Aws\Credentials\EcsCredentialProvider for $config details.
|
437 |
+
*/
|
438 |
+
private static function remoteCredentialProviders(array $config = [])
|
439 |
+
{
|
440 |
+
if (!empty(getenv(EcsCredentialProvider::ENV_URI))) {
|
441 |
+
$providers['ecs'] = self::ecsCredentials($config);
|
442 |
+
}
|
443 |
+
$providers['process_credentials'] = self::process();
|
444 |
+
$providers['process_config'] = self::process(
|
445 |
+
'profile default',
|
446 |
+
self::getHomeDir() . '/.aws/config'
|
447 |
+
);
|
448 |
+
$providers['instance'] = self::instanceProfile($config);
|
449 |
+
|
450 |
+
if (isset($config['credentials'])
|
451 |
+
&& $config['credentials'] instanceof CacheInterface
|
452 |
+
) {
|
453 |
+
foreach ($providers as $key => $provider) {
|
454 |
+
$providers[$key] = self::cache(
|
455 |
+
$provider,
|
456 |
+
$config['credentials'],
|
457 |
+
'aws_cached_' . $key . '_credentials'
|
458 |
+
);
|
459 |
+
}
|
460 |
+
}
|
461 |
+
|
462 |
+
return $providers;
|
463 |
+
}
|
464 |
+
|
465 |
+
/**
|
466 |
+
* Gets the environment's HOME directory if available.
|
467 |
+
*
|
468 |
+
* @return null|string
|
469 |
+
*/
|
470 |
+
private static function getHomeDir()
|
471 |
+
{
|
472 |
+
// On Linux/Unix-like systems, use the HOME environment variable
|
473 |
+
if ($homeDir = getenv('HOME')) {
|
474 |
+
return $homeDir;
|
475 |
+
}
|
476 |
+
|
477 |
+
// Get the HOMEDRIVE and HOMEPATH values for Windows hosts
|
478 |
+
$homeDrive = getenv('HOMEDRIVE');
|
479 |
+
$homePath = getenv('HOMEPATH');
|
480 |
+
|
481 |
+
return ($homeDrive && $homePath) ? $homeDrive . $homePath : null;
|
482 |
+
}
|
483 |
+
|
484 |
+
private static function reject($msg)
|
485 |
+
{
|
486 |
+
return new Promise\RejectedPromise(new CredentialsException($msg));
|
487 |
+
}
|
488 |
+
}
|
lib/Aws/Aws/Credentials/Credentials.php
ADDED
@@ -0,0 +1,91 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Credentials;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Basic implementation of the AWS Credentials interface that allows callers to
|
6 |
+
* pass in the AWS Access Key and AWS Secret Access Key in the constructor.
|
7 |
+
*/
|
8 |
+
class Credentials implements CredentialsInterface, \Serializable
|
9 |
+
{
|
10 |
+
private $key;
|
11 |
+
private $secret;
|
12 |
+
private $token;
|
13 |
+
private $expires;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* Constructs a new BasicAWSCredentials object, with the specified AWS
|
17 |
+
* access key and AWS secret key
|
18 |
+
*
|
19 |
+
* @param string $key AWS access key ID
|
20 |
+
* @param string $secret AWS secret access key
|
21 |
+
* @param string $token Security token to use
|
22 |
+
* @param int $expires UNIX timestamp for when credentials expire
|
23 |
+
*/
|
24 |
+
public function __construct($key, $secret, $token = null, $expires = null)
|
25 |
+
{
|
26 |
+
$this->key = trim($key);
|
27 |
+
$this->secret = trim($secret);
|
28 |
+
$this->token = $token;
|
29 |
+
$this->expires = $expires;
|
30 |
+
}
|
31 |
+
|
32 |
+
public static function __set_state(array $state)
|
33 |
+
{
|
34 |
+
return new self(
|
35 |
+
$state['key'],
|
36 |
+
$state['secret'],
|
37 |
+
$state['token'],
|
38 |
+
$state['expires']
|
39 |
+
);
|
40 |
+
}
|
41 |
+
|
42 |
+
public function getAccessKeyId()
|
43 |
+
{
|
44 |
+
return $this->key;
|
45 |
+
}
|
46 |
+
|
47 |
+
public function getSecretKey()
|
48 |
+
{
|
49 |
+
return $this->secret;
|
50 |
+
}
|
51 |
+
|
52 |
+
public function getSecurityToken()
|
53 |
+
{
|
54 |
+
return $this->token;
|
55 |
+
}
|
56 |
+
|
57 |
+
public function getExpiration()
|
58 |
+
{
|
59 |
+
return $this->expires;
|
60 |
+
}
|
61 |
+
|
62 |
+
public function isExpired()
|
63 |
+
{
|
64 |
+
return $this->expires !== null && time() >= $this->expires;
|
65 |
+
}
|
66 |
+
|
67 |
+
public function toArray()
|
68 |
+
{
|
69 |
+
return [
|
70 |
+
'key' => $this->key,
|
71 |
+
'secret' => $this->secret,
|
72 |
+
'token' => $this->token,
|
73 |
+
'expires' => $this->expires
|
74 |
+
];
|
75 |
+
}
|
76 |
+
|
77 |
+
public function serialize()
|
78 |
+
{
|
79 |
+
return json_encode($this->toArray());
|
80 |
+
}
|
81 |
+
|
82 |
+
public function unserialize($serialized)
|
83 |
+
{
|
84 |
+
$data = json_decode($serialized, true);
|
85 |
+
|
86 |
+
$this->key = $data['key'];
|
87 |
+
$this->secret = $data['secret'];
|
88 |
+
$this->token = $data['token'];
|
89 |
+
$this->expires = $data['expires'];
|
90 |
+
}
|
91 |
+
}
|
lib/Aws/Aws/Credentials/CredentialsInterface.php
ADDED
@@ -0,0 +1,52 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Credentials;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Provides access to the AWS credentials used for accessing AWS services: AWS
|
6 |
+
* access key ID, secret access key, and security token. These credentials are
|
7 |
+
* used to securely sign requests to AWS services.
|
8 |
+
*/
|
9 |
+
interface CredentialsInterface
|
10 |
+
{
|
11 |
+
/**
|
12 |
+
* Returns the AWS access key ID for this credentials object.
|
13 |
+
*
|
14 |
+
* @return string
|
15 |
+
*/
|
16 |
+
public function getAccessKeyId();
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Returns the AWS secret access key for this credentials object.
|
20 |
+
*
|
21 |
+
* @return string
|
22 |
+
*/
|
23 |
+
public function getSecretKey();
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Get the associated security token if available
|
27 |
+
*
|
28 |
+
* @return string|null
|
29 |
+
*/
|
30 |
+
public function getSecurityToken();
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Get the UNIX timestamp in which the credentials will expire
|
34 |
+
*
|
35 |
+
* @return int|null
|
36 |
+
*/
|
37 |
+
public function getExpiration();
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Check if the credentials are expired
|
41 |
+
*
|
42 |
+
* @return bool
|
43 |
+
*/
|
44 |
+
public function isExpired();
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Converts the credentials to an associative array.
|
48 |
+
*
|
49 |
+
* @return array
|
50 |
+
*/
|
51 |
+
public function toArray();
|
52 |
+
}
|
lib/Aws/Aws/Credentials/EcsCredentialProvider.php
ADDED
@@ -0,0 +1,88 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Credentials;
|
3 |
+
|
4 |
+
use Aws\Exception\CredentialsException;
|
5 |
+
use GuzzleHttp\Psr7\Request;
|
6 |
+
use GuzzleHttp\Promise\PromiseInterface;
|
7 |
+
use Psr\Http\Message\ResponseInterface;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Credential provider that fetches credentials with GET request.
|
11 |
+
* ECS environment variable is used in constructing request URI.
|
12 |
+
*/
|
13 |
+
class EcsCredentialProvider
|
14 |
+
{
|
15 |
+
const SERVER_URI = 'http://169.254.170.2';
|
16 |
+
const ENV_URI = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI";
|
17 |
+
|
18 |
+
/** @var callable */
|
19 |
+
private $client;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* The constructor accepts following options:
|
23 |
+
* - timeout: (optional) Connection timeout, in seconds, default 1.0
|
24 |
+
* - client: An EcsClient to make request from
|
25 |
+
*
|
26 |
+
* @param array $config Configuration options
|
27 |
+
*/
|
28 |
+
public function __construct(array $config = [])
|
29 |
+
{
|
30 |
+
$this->timeout = isset($config['timeout']) ? $config['timeout'] : 1.0;
|
31 |
+
$this->client = isset($config['client'])
|
32 |
+
? $config['client']
|
33 |
+
: \Aws\default_http_handler();
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Load ECS credentials
|
38 |
+
*
|
39 |
+
* @return PromiseInterface
|
40 |
+
*/
|
41 |
+
public function __invoke()
|
42 |
+
{
|
43 |
+
$client = $this->client;
|
44 |
+
$request = new Request('GET', self::getEcsUri());
|
45 |
+
return $client(
|
46 |
+
$request,
|
47 |
+
[
|
48 |
+
'timeout' => $this->timeout,
|
49 |
+
'proxy' => '',
|
50 |
+
]
|
51 |
+
)->then(function (ResponseInterface $response) {
|
52 |
+
$result = $this->decodeResult((string) $response->getBody());
|
53 |
+
return new Credentials(
|
54 |
+
$result['AccessKeyId'],
|
55 |
+
$result['SecretAccessKey'],
|
56 |
+
$result['Token'],
|
57 |
+
strtotime($result['Expiration'])
|
58 |
+
);
|
59 |
+
})->otherwise(function ($reason) {
|
60 |
+
$reason = is_array($reason) ? $reason['exception'] : $reason;
|
61 |
+
$msg = $reason->getMessage();
|
62 |
+
throw new CredentialsException(
|
63 |
+
"Error retrieving credential from ECS ($msg)"
|
64 |
+
);
|
65 |
+
});
|
66 |
+
}
|
67 |
+
|
68 |
+
/**
|
69 |
+
* Fetch credential URI from ECS environment variable
|
70 |
+
*
|
71 |
+
* @return string Returns ECS URI
|
72 |
+
*/
|
73 |
+
private function getEcsUri()
|
74 |
+
{
|
75 |
+
$creds_uri = getenv(self::ENV_URI);
|
76 |
+
return self::SERVER_URI . $creds_uri;
|
77 |
+
}
|
78 |
+
|
79 |
+
private function decodeResult($response)
|
80 |
+
{
|
81 |
+
$result = json_decode($response, true);
|
82 |
+
|
83 |
+
if (!isset($result['AccessKeyId'])) {
|
84 |
+
throw new CredentialsException('Unexpected ECS credential value');
|
85 |
+
}
|
86 |
+
return $result;
|
87 |
+
}
|
88 |
+
}
|
lib/Aws/Aws/Credentials/InstanceProfileProvider.php
ADDED
@@ -0,0 +1,118 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Credentials;
|
3 |
+
|
4 |
+
use Aws\Exception\CredentialsException;
|
5 |
+
use Aws\Sdk;
|
6 |
+
use GuzzleHttp\Promise;
|
7 |
+
use GuzzleHttp\Psr7\Request;
|
8 |
+
use GuzzleHttp\Promise\PromiseInterface;
|
9 |
+
use Psr\Http\Message\ResponseInterface;
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Credential provider that provides credentials from the EC2 metadata server.
|
13 |
+
*/
|
14 |
+
class InstanceProfileProvider
|
15 |
+
{
|
16 |
+
const SERVER_URI = 'http://169.254.169.254/latest/';
|
17 |
+
const CRED_PATH = 'meta-data/iam/security-credentials/';
|
18 |
+
|
19 |
+
const ENV_DISABLE = 'AWS_EC2_METADATA_DISABLED';
|
20 |
+
|
21 |
+
/** @var string */
|
22 |
+
private $profile;
|
23 |
+
|
24 |
+
/** @var callable */
|
25 |
+
private $client;
|
26 |
+
|
27 |
+
/**
|
28 |
+
* The constructor accepts the following options:
|
29 |
+
*
|
30 |
+
* - timeout: Connection timeout, in seconds.
|
31 |
+
* - profile: Optional EC2 profile name, if known.
|
32 |
+
*
|
33 |
+
* @param array $config Configuration options.
|
34 |
+
*/
|
35 |
+
public function __construct(array $config = [])
|
36 |
+
{
|
37 |
+
$this->timeout = isset($config['timeout']) ? $config['timeout'] : 1.0;
|
38 |
+
$this->profile = isset($config['profile']) ? $config['profile'] : null;
|
39 |
+
$this->client = isset($config['client'])
|
40 |
+
? $config['client'] // internal use only
|
41 |
+
: \Aws\default_http_handler();
|
42 |
+
}
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Loads instance profile credentials.
|
46 |
+
*
|
47 |
+
* @return PromiseInterface
|
48 |
+
*/
|
49 |
+
public function __invoke()
|
50 |
+
{
|
51 |
+
return Promise\coroutine(function () {
|
52 |
+
if (!$this->profile) {
|
53 |
+
$this->profile = (yield $this->request(self::CRED_PATH));
|
54 |
+
}
|
55 |
+
$json = (yield $this->request(self::CRED_PATH . $this->profile));
|
56 |
+
$result = $this->decodeResult($json);
|
57 |
+
yield new Credentials(
|
58 |
+
$result['AccessKeyId'],
|
59 |
+
$result['SecretAccessKey'],
|
60 |
+
$result['Token'],
|
61 |
+
strtotime($result['Expiration'])
|
62 |
+
);
|
63 |
+
});
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* @param string $url
|
68 |
+
* @return PromiseInterface Returns a promise that is fulfilled with the
|
69 |
+
* body of the response as a string.
|
70 |
+
*/
|
71 |
+
private function request($url)
|
72 |
+
{
|
73 |
+
$disabled = getenv(self::ENV_DISABLE) ?: false;
|
74 |
+
if (strcasecmp($disabled, 'true') === 0) {
|
75 |
+
throw new CredentialsException(
|
76 |
+
$this->createErrorMessage('EC2 metadata server access disabled')
|
77 |
+
);
|
78 |
+
}
|
79 |
+
|
80 |
+
$fn = $this->client;
|
81 |
+
$request = new Request('GET', self::SERVER_URI . $url);
|
82 |
+
$userAgent = 'aws-sdk-php/' . Sdk::VERSION;
|
83 |
+
if (defined('HHVM_VERSION')) {
|
84 |
+
$userAgent .= ' HHVM/' . HHVM_VERSION;
|
85 |
+
}
|
86 |
+
$userAgent .= ' ' . \Aws\default_user_agent();
|
87 |
+
$request = $request->withHeader('User-Agent', $userAgent);
|
88 |
+
|
89 |
+
return $fn($request, ['timeout' => $this->timeout])
|
90 |
+
->then(function (ResponseInterface $response) {
|
91 |
+
return (string) $response->getBody();
|
92 |
+
})->otherwise(function (array $reason) {
|
93 |
+
$reason = $reason['exception'];
|
94 |
+
$msg = $reason->getMessage();
|
95 |
+
throw new CredentialsException(
|
96 |
+
$this->createErrorMessage($msg)
|
97 |
+
);
|
98 |
+
});
|
99 |
+
}
|
100 |
+
|
101 |
+
private function createErrorMessage($previous)
|
102 |
+
{
|
103 |
+
return "Error retrieving credentials from the instance profile "
|
104 |
+
. "metadata server. ({$previous})";
|
105 |
+
}
|
106 |
+
|
107 |
+
private function decodeResult($response)
|
108 |
+
{
|
109 |
+
$result = json_decode($response, true);
|
110 |
+
|
111 |
+
if ($result['Code'] !== 'Success') {
|
112 |
+
throw new CredentialsException('Unexpected instance profile '
|
113 |
+
. 'response code: ' . $result['Code']);
|
114 |
+
}
|
115 |
+
|
116 |
+
return $result;
|
117 |
+
}
|
118 |
+
}
|
lib/Aws/Aws/DoctrineCacheAdapter.php
ADDED
@@ -0,0 +1,55 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws;
|
3 |
+
|
4 |
+
use Doctrine\Common\Cache\Cache;
|
5 |
+
|
6 |
+
class DoctrineCacheAdapter implements CacheInterface, Cache
|
7 |
+
{
|
8 |
+
/** @var Cache */
|
9 |
+
private $cache;
|
10 |
+
|
11 |
+
public function __construct(Cache $cache)
|
12 |
+
{
|
13 |
+
$this->cache = $cache;
|
14 |
+
}
|
15 |
+
|
16 |
+
public function get($key)
|
17 |
+
{
|
18 |
+
return $this->cache->fetch($key);
|
19 |
+
}
|
20 |
+
|
21 |
+
public function fetch($key)
|
22 |
+
{
|
23 |
+
return $this->get($key);
|
24 |
+
}
|
25 |
+
|
26 |
+
public function set($key, $value, $ttl = 0)
|
27 |
+
{
|
28 |
+
return $this->cache->save($key, $value, $ttl);
|
29 |
+
}
|
30 |
+
|
31 |
+
public function save($key, $value, $ttl = 0)
|
32 |
+
{
|
33 |
+
return $this->set($key, $value, $ttl);
|
34 |
+
}
|
35 |
+
|
36 |
+
public function remove($key)
|
37 |
+
{
|
38 |
+
return $this->cache->delete($key);
|
39 |
+
}
|
40 |
+
|
41 |
+
public function delete($key)
|
42 |
+
{
|
43 |
+
return $this->remove($key);
|
44 |
+
}
|
45 |
+
|
46 |
+
public function contains($key)
|
47 |
+
{
|
48 |
+
return $this->cache->contains($key);
|
49 |
+
}
|
50 |
+
|
51 |
+
public function getStats()
|
52 |
+
{
|
53 |
+
return $this->cache->getStats();
|
54 |
+
}
|
55 |
+
}
|
lib/Aws/Aws/Endpoint/EndpointProvider.php
ADDED
@@ -0,0 +1,96 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Endpoint;
|
3 |
+
|
4 |
+
use Aws\Exception\UnresolvedEndpointException;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Endpoint providers.
|
8 |
+
*
|
9 |
+
* An endpoint provider is a function that accepts a hash of endpoint options,
|
10 |
+
* including but not limited to "service" and "region" key value pairs. The
|
11 |
+
* endpoint provider function returns a hash of endpoint data, which MUST
|
12 |
+
* include an "endpoint" key value pair that represents the resolved endpoint
|
13 |
+
* or NULL if an endpoint cannot be determined.
|
14 |
+
*
|
15 |
+
* You can wrap your calls to an endpoint provider with the
|
16 |
+
* {@see EndpointProvider::resolve} function to ensure that an endpoint hash is
|
17 |
+
* created. If an endpoint hash is not created, then the resolve() function
|
18 |
+
* will throw an {@see Aws\Exception\UnresolvedEndpointException}.
|
19 |
+
*
|
20 |
+
* use Aws\Endpoint\EndpointProvider;
|
21 |
+
* $provider = EndpointProvider::defaultProvider();
|
22 |
+
* // Returns an array or NULL.
|
23 |
+
* $endpoint = $provider(['service' => 'ec2', 'region' => 'us-west-2']);
|
24 |
+
* // Returns an endpoint array or throws.
|
25 |
+
* $endpoint = EndpointProvider::resolve($provider, [
|
26 |
+
* 'service' => 'ec2',
|
27 |
+
* 'region' => 'us-west-2'
|
28 |
+
* ]);
|
29 |
+
*
|
30 |
+
* You can compose multiple providers into a single provider using
|
31 |
+
* {@see Aws\or_chain}. This function accepts providers as arguments and
|
32 |
+
* returns a new function that will invoke each provider until a non-null value
|
33 |
+
* is returned.
|
34 |
+
*
|
35 |
+
* $a = function (array $args) {
|
36 |
+
* if ($args['region'] === 'my-test-region') {
|
37 |
+
* return ['endpoint' => 'http://localhost:123/api'];
|
38 |
+
* }
|
39 |
+
* };
|
40 |
+
* $b = EndpointProvider::defaultProvider();
|
41 |
+
* $c = \Aws\or_chain($a, $b);
|
42 |
+
* $config = ['service' => 'ec2', 'region' => 'my-test-region'];
|
43 |
+
* $res = $c($config); // $a handles this.
|
44 |
+
* $config['region'] = 'us-west-2';
|
45 |
+
* $res = $c($config); // $b handles this.
|
46 |
+
*/
|
47 |
+
class EndpointProvider
|
48 |
+
{
|
49 |
+
/**
|
50 |
+
* Resolves and endpoint provider and ensures a non-null return value.
|
51 |
+
*
|
52 |
+
* @param callable $provider Provider function to invoke.
|
53 |
+
* @param array $args Endpoint arguments to pass to the provider.
|
54 |
+
*
|
55 |
+
* @return array
|
56 |
+
* @throws UnresolvedEndpointException
|
57 |
+
*/
|
58 |
+
public static function resolve(callable $provider, array $args = [])
|
59 |
+
{
|
60 |
+
$result = $provider($args);
|
61 |
+
if (is_array($result)) {
|
62 |
+
return $result;
|
63 |
+
}
|
64 |
+
|
65 |
+
throw new UnresolvedEndpointException(
|
66 |
+
'Unable to resolve an endpoint using the provider arguments: '
|
67 |
+
. json_encode($args) . '. Note: you can provide an "endpoint" '
|
68 |
+
. 'option to a client constructor to bypass invoking an endpoint '
|
69 |
+
. 'provider.');
|
70 |
+
}
|
71 |
+
|
72 |
+
/**
|
73 |
+
* Creates and returns the default SDK endpoint provider.
|
74 |
+
*
|
75 |
+
* @deprecated Use an instance of \Aws\Endpoint\Partition instead.
|
76 |
+
*
|
77 |
+
* @return callable
|
78 |
+
*/
|
79 |
+
public static function defaultProvider()
|
80 |
+
{
|
81 |
+
return PartitionEndpointProvider::defaultProvider();
|
82 |
+
}
|
83 |
+
|
84 |
+
/**
|
85 |
+
* Creates and returns an endpoint provider that uses patterns from an
|
86 |
+
* array.
|
87 |
+
*
|
88 |
+
* @param array $patterns Endpoint patterns
|
89 |
+
*
|
90 |
+
* @return callable
|
91 |
+
*/
|
92 |
+
public static function patterns(array $patterns)
|
93 |
+
{
|
94 |
+
return new PatternEndpointProvider($patterns);
|
95 |
+
}
|
96 |
+
}
|
lib/Aws/Aws/Endpoint/Partition.php
ADDED
@@ -0,0 +1,183 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Endpoint;
|
3 |
+
|
4 |
+
use ArrayAccess;
|
5 |
+
use Aws\HasDataTrait;
|
6 |
+
use InvalidArgumentException as Iae;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Default implementation of an AWS partition.
|
10 |
+
*/
|
11 |
+
final class Partition implements ArrayAccess, PartitionInterface
|
12 |
+
{
|
13 |
+
use HasDataTrait;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* The partition constructor accepts the following options:
|
17 |
+
*
|
18 |
+
* - `partition`: (string, required) The partition name as specified in an
|
19 |
+
* ARN (e.g., `aws`)
|
20 |
+
* - `partitionName`: (string) The human readable name of the partition
|
21 |
+
* (e.g., "AWS Standard")
|
22 |
+
* - `dnsSuffix`: (string, required) The DNS suffix of the partition. This
|
23 |
+
* value is used to determine how endpoints in the partition are resolved.
|
24 |
+
* - `regionRegex`: (string) A PCRE regular expression that specifies the
|
25 |
+
* pattern that region names in the endpoint adhere to.
|
26 |
+
* - `regions`: (array, required) A map of the regions in the partition.
|
27 |
+
* Each key is the region as present in a hostname (e.g., `us-east-1`),
|
28 |
+
* and each value is a structure containing region information.
|
29 |
+
* - `defaults`: (array) A map of default key value pairs to apply to each
|
30 |
+
* endpoint of the partition. Any value in an `endpoint` definition will
|
31 |
+
* supersede any values specified in `defaults`.
|
32 |
+
* - `services`: (array, required) A map of service endpoint prefix name
|
33 |
+
* (the value found in a hostname) to information about the service.
|
34 |
+
*
|
35 |
+
* @param array $definition
|
36 |
+
*
|
37 |
+
* @throws Iae if any required options are missing
|
38 |
+
*/
|
39 |
+
public function __construct(array $definition)
|
40 |
+
{
|
41 |
+
foreach (['partition', 'regions', 'services', 'dnsSuffix'] as $key) {
|
42 |
+
if (!isset($definition[$key])) {
|
43 |
+
throw new Iae("Partition missing required $key field");
|
44 |
+
}
|
45 |
+
}
|
46 |
+
|
47 |
+
$this->data = $definition;
|
48 |
+
}
|
49 |
+
|
50 |
+
public function getName()
|
51 |
+
{
|
52 |
+
return $this->data['partition'];
|
53 |
+
}
|
54 |
+
|
55 |
+
public function isRegionMatch($region, $service)
|
56 |
+
{
|
57 |
+
if (isset($this->data['regions'][$region])
|
58 |
+
|| isset($this->data['services'][$service]['endpoints'][$region])
|
59 |
+
) {
|
60 |
+
return true;
|
61 |
+
}
|
62 |
+
|
63 |
+
if (isset($this->data['regionRegex'])) {
|
64 |
+
return (bool) preg_match(
|
65 |
+
"@{$this->data['regionRegex']}@",
|
66 |
+
$region
|
67 |
+
);
|
68 |
+
}
|
69 |
+
|
70 |
+
return false;
|
71 |
+
}
|
72 |
+
|
73 |
+
public function getAvailableEndpoints(
|
74 |
+
$service,
|
75 |
+
$allowNonRegionalEndpoints = false
|
76 |
+
) {
|
77 |
+
if ($this->isServicePartitionGlobal($service)) {
|
78 |
+
return [$this->getPartitionEndpoint($service)];
|
79 |
+
}
|
80 |
+
|
81 |
+
if (isset($this->data['services'][$service]['endpoints'])) {
|
82 |
+
$serviceRegions = array_keys(
|
83 |
+
$this->data['services'][$service]['endpoints']
|
84 |
+
);
|
85 |
+
|
86 |
+
return $allowNonRegionalEndpoints
|
87 |
+
? $serviceRegions
|
88 |
+
: array_intersect($serviceRegions, array_keys(
|
89 |
+
$this->data['regions']
|
90 |
+
));
|
91 |
+
}
|
92 |
+
|
93 |
+
return [];
|
94 |
+
}
|
95 |
+
|
96 |
+
public function __invoke(array $args = [])
|
97 |
+
{
|
98 |
+
$service = isset($args['service']) ? $args['service'] : '';
|
99 |
+
$region = isset($args['region']) ? $args['region'] : '';
|
100 |
+
$scheme = isset($args['scheme']) ? $args['scheme'] : 'https';
|
101 |
+
$data = $this->getEndpointData($service, $region);
|
102 |
+
|
103 |
+
return [
|
104 |
+
'endpoint' => "{$scheme}://" . $this->formatEndpoint(
|
105 |
+
isset($data['hostname']) ? $data['hostname'] : '',
|
106 |
+
$service,
|
107 |
+
$region
|
108 |
+
),
|
109 |
+
'signatureVersion' => $this->getSignatureVersion($data),
|
110 |
+
'signingRegion' => isset($data['credentialScope']['region'])
|
111 |
+
? $data['credentialScope']['region']
|
112 |
+
: $region,
|
113 |
+
'signingName' => isset($data['credentialScope']['service'])
|
114 |
+
? $data['credentialScope']['service']
|
115 |
+
: $service,
|
116 |
+
];
|
117 |
+
}
|
118 |
+
|
119 |
+
private function getEndpointData($service, $region)
|
120 |
+
{
|
121 |
+
|
122 |
+
$resolved = $this->resolveRegion($service, $region);
|
123 |
+
$data = isset($this->data['services'][$service]['endpoints'][$resolved])
|
124 |
+
? $this->data['services'][$service]['endpoints'][$resolved]
|
125 |
+
: [];
|
126 |
+
$data += isset($this->data['services'][$service]['defaults'])
|
127 |
+
? $this->data['services'][$service]['defaults']
|
128 |
+
: [];
|
129 |
+
$data += isset($this->data['defaults'])
|
130 |
+
? $this->data['defaults']
|
131 |
+
: [];
|
132 |
+
|
133 |
+
return $data;
|
134 |
+
}
|
135 |
+
|
136 |
+
private function getSignatureVersion(array $data)
|
137 |
+
{
|
138 |
+
static $supportedBySdk = [
|
139 |
+
's3v4',
|
140 |
+
'v4',
|
141 |
+
'anonymous',
|
142 |
+
];
|
143 |
+
|
144 |
+
$possibilities = array_intersect(
|
145 |
+
$supportedBySdk,
|
146 |
+
isset($data['signatureVersions'])
|
147 |
+
? $data['signatureVersions']
|
148 |
+
: ['v4']
|
149 |
+
);
|
150 |
+
|
151 |
+
return array_shift($possibilities);
|
152 |
+
}
|
153 |
+
|
154 |
+
private function resolveRegion($service, $region)
|
155 |
+
{
|
156 |
+
if ($this->isServicePartitionGlobal($service)) {
|
157 |
+
return $this->getPartitionEndpoint($service);
|
158 |
+
}
|
159 |
+
|
160 |
+
return $region;
|
161 |
+
}
|
162 |
+
|
163 |
+
private function isServicePartitionGlobal($service)
|
164 |
+
{
|
165 |
+
return isset($this->data['services'][$service]['isRegionalized'])
|
166 |
+
&& false === $this->data['services'][$service]['isRegionalized']
|
167 |
+
&& isset($this->data['services'][$service]['partitionEndpoint']);
|
168 |
+
}
|
169 |
+
|
170 |
+
private function getPartitionEndpoint($service)
|
171 |
+
{
|
172 |
+
return $this->data['services'][$service]['partitionEndpoint'];
|
173 |
+
}
|
174 |
+
|
175 |
+
private function formatEndpoint($template, $service, $region)
|
176 |
+
{
|
177 |
+
return strtr($template, [
|
178 |
+
'{service}' => $service,
|
179 |
+
'{region}' => $region,
|
180 |
+
'{dnsSuffix}' => $this->data['dnsSuffix'],
|
181 |
+
]);
|
182 |
+
}
|
183 |
+
}
|
lib/Aws/Aws/Endpoint/PartitionEndpointProvider.php
ADDED
@@ -0,0 +1,108 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Endpoint;
|
3 |
+
|
4 |
+
use JmesPath\Env;
|
5 |
+
|
6 |
+
class PartitionEndpointProvider
|
7 |
+
{
|
8 |
+
/** @var Partition[] */
|
9 |
+
private $partitions;
|
10 |
+
/** @var string */
|
11 |
+
private $defaultPartition;
|
12 |
+
|
13 |
+
public function __construct(array $partitions, $defaultPartition = 'aws')
|
14 |
+
{
|
15 |
+
$this->partitions = array_map(function (array $definition) {
|
16 |
+
return new Partition($definition);
|
17 |
+
}, array_values($partitions));
|
18 |
+
$this->defaultPartition = $defaultPartition;
|
19 |
+
}
|
20 |
+
|
21 |
+
public function __invoke(array $args = [])
|
22 |
+
{
|
23 |
+
$partition = $this->getPartition(
|
24 |
+
isset($args['region']) ? $args['region'] : '',
|
25 |
+
isset($args['service']) ? $args['service'] : ''
|
26 |
+
);
|
27 |
+
|
28 |
+
return $partition($args);
|
29 |
+
}
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Returns the partition containing the provided region or the default
|
33 |
+
* partition if no match is found.
|
34 |
+
*
|
35 |
+
* @param string $region
|
36 |
+
* @param string $service
|
37 |
+
*
|
38 |
+
* @return Partition
|
39 |
+
*/
|
40 |
+
public function getPartition($region, $service)
|
41 |
+
{
|
42 |
+
foreach ($this->partitions as $partition) {
|
43 |
+
if ($partition->isRegionMatch($region, $service)) {
|
44 |
+
return $partition;
|
45 |
+
}
|
46 |
+
}
|
47 |
+
|
48 |
+
return $this->getPartitionByName($this->defaultPartition);
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Returns the partition with the provided name or null if no partition with
|
53 |
+
* the provided name can be found.
|
54 |
+
*
|
55 |
+
* @param string $name
|
56 |
+
*
|
57 |
+
* @return Partition|null
|
58 |
+
*/
|
59 |
+
public function getPartitionByName($name)
|
60 |
+
{
|
61 |
+
foreach ($this->partitions as $partition) {
|
62 |
+
if ($name === $partition->getName()) {
|
63 |
+
return $partition;
|
64 |
+
}
|
65 |
+
}
|
66 |
+
}
|
67 |
+
|
68 |
+
/**
|
69 |
+
* Creates and returns the default SDK partition provider.
|
70 |
+
*
|
71 |
+
* @return PartitionEndpointProvider
|
72 |
+
*/
|
73 |
+
public static function defaultProvider()
|
74 |
+
{
|
75 |
+
$data = \Aws\load_compiled_json(__DIR__ . '/../data/endpoints.json');
|
76 |
+
$prefixData = \Aws\load_compiled_json(__DIR__ . '/../data/endpoints_prefix_history.json');
|
77 |
+
$mergedData = self::mergePrefixData($data, $prefixData);
|
78 |
+
|
79 |
+
return new self($mergedData['partitions']);
|
80 |
+
}
|
81 |
+
|
82 |
+
/**
|
83 |
+
* Copy endpoint data for other prefixes used by a given service
|
84 |
+
*
|
85 |
+
* @param $data
|
86 |
+
* @param $prefixData
|
87 |
+
* @return array
|
88 |
+
*/
|
89 |
+
public static function mergePrefixData($data, $prefixData)
|
90 |
+
{
|
91 |
+
$prefixGroups = $prefixData['prefix-groups'];
|
92 |
+
|
93 |
+
foreach ($data["partitions"] as $index => $partition) {
|
94 |
+
foreach ($prefixGroups as $current => $old) {
|
95 |
+
$serviceData = Env::search("services.{$current}", $partition);
|
96 |
+
if (!empty($serviceData)) {
|
97 |
+
foreach ($old as $prefix) {
|
98 |
+
if (empty(Env::search("services.{$prefix}", $partition))) {
|
99 |
+
$data["partitions"][$index]["services"][$prefix] = $serviceData;
|
100 |
+
}
|
101 |
+
}
|
102 |
+
}
|
103 |
+
}
|
104 |
+
}
|
105 |
+
|
106 |
+
return $data;
|
107 |
+
}
|
108 |
+
}
|
lib/Aws/Aws/Endpoint/PartitionInterface.php
ADDED
@@ -0,0 +1,56 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Endpoint;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Represents a section of the AWS cloud.
|
6 |
+
*/
|
7 |
+
interface PartitionInterface
|
8 |
+
{
|
9 |
+
/**
|
10 |
+
* Returns the partition's short name, e.g., 'aws,' 'aws-cn,' or
|
11 |
+
* 'aws-us-gov.'
|
12 |
+
*
|
13 |
+
* @return string
|
14 |
+
*/
|
15 |
+
public function getName();
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Determine if this partition contains the provided region. Include the
|
19 |
+
* name of the service to inspect non-regional endpoints
|
20 |
+
*
|
21 |
+
* @param string $region
|
22 |
+
* @param string $service
|
23 |
+
*
|
24 |
+
* @return bool
|
25 |
+
*/
|
26 |
+
public function isRegionMatch($region, $service);
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Return the endpoints supported by a given service.
|
30 |
+
*
|
31 |
+
* @param string $service Identifier of the service
|
32 |
+
* whose endpoints should be
|
33 |
+
* listed (e.g., 's3' or 'ses')
|
34 |
+
* @param bool $allowNonRegionalEndpoints Set to `true` to include
|
35 |
+
* endpoints that are not AWS
|
36 |
+
* regions (e.g., 'local' for
|
37 |
+
* DynamoDB or
|
38 |
+
* 'fips-us-gov-west-1' for S3)
|
39 |
+
*
|
40 |
+
* @return string[]
|
41 |
+
*/
|
42 |
+
public function getAvailableEndpoints(
|
43 |
+
$service,
|
44 |
+
$allowNonRegionalEndpoints = false
|
45 |
+
);
|
46 |
+
|
47 |
+
/**
|
48 |
+
* A partition must be invokable as an endpoint provider.
|
49 |
+
*
|
50 |
+
* @see EndpointProvider
|
51 |
+
*
|
52 |
+
* @param array $args
|
53 |
+
* @return array
|
54 |
+
*/
|
55 |
+
public function __invoke(array $args = []);
|
56 |
+
}
|
lib/Aws/Aws/Endpoint/PatternEndpointProvider.php
ADDED
@@ -0,0 +1,51 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Endpoint;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Provides endpoints based on an endpoint pattern configuration array.
|
6 |
+
*/
|
7 |
+
class PatternEndpointProvider
|
8 |
+
{
|
9 |
+
/** @var array */
|
10 |
+
private $patterns;
|
11 |
+
|
12 |
+
/**
|
13 |
+
* @param array $patterns Hash of endpoint patterns mapping to endpoint
|
14 |
+
* configurations.
|
15 |
+
*/
|
16 |
+
public function __construct(array $patterns)
|
17 |
+
{
|
18 |
+
$this->patterns = $patterns;
|
19 |
+
}
|
20 |
+
|
21 |
+
public function __invoke(array $args = [])
|
22 |
+
{
|
23 |
+
$service = isset($args['service']) ? $args['service'] : '';
|
24 |
+
$region = isset($args['region']) ? $args['region'] : '';
|
25 |
+
$keys = ["{$region}/{$service}", "{$region}/*", "*/{$service}", "*/*"];
|
26 |
+
|
27 |
+
foreach ($keys as $key) {
|
28 |
+
if (isset($this->patterns[$key])) {
|
29 |
+
return $this->expand(
|
30 |
+
$this->patterns[$key],
|
31 |
+
isset($args['scheme']) ? $args['scheme'] : 'https',
|
32 |
+
$service,
|
33 |
+
$region
|
34 |
+
);
|
35 |
+
}
|
36 |
+
}
|
37 |
+
|
38 |
+
return null;
|
39 |
+
}
|
40 |
+
|
41 |
+
private function expand(array $config, $scheme, $service, $region)
|
42 |
+
{
|
43 |
+
$config['endpoint'] = $scheme . '://'
|
44 |
+
. strtr($config['endpoint'], [
|
45 |
+
'{service}' => $service,
|
46 |
+
'{region}' => $region
|
47 |
+
]);
|
48 |
+
|
49 |
+
return $config;
|
50 |
+
}
|
51 |
+
}
|
lib/Aws/Aws/EndpointDiscovery/Configuration.php
ADDED
@@ -0,0 +1,48 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\EndpointDiscovery;
|
3 |
+
|
4 |
+
class Configuration implements ConfigurationInterface
|
5 |
+
{
|
6 |
+
private $cacheLimit;
|
7 |
+
private $enabled;
|
8 |
+
|
9 |
+
public function __construct($enabled, $cacheLimit = 1000)
|
10 |
+
{
|
11 |
+
$this->cacheLimit = filter_var($cacheLimit, FILTER_VALIDATE_INT);
|
12 |
+
if ($this->cacheLimit == false || $this->cacheLimit < 1) {
|
13 |
+
throw new \InvalidArgumentException(
|
14 |
+
"'cache_limit' value must be a positive integer."
|
15 |
+
);
|
16 |
+
}
|
17 |
+
|
18 |
+
// Unparsable $enabled flag errs on the side of disabling endpoint discovery
|
19 |
+
$this->enabled = filter_var($enabled, FILTER_VALIDATE_BOOLEAN);
|
20 |
+
}
|
21 |
+
|
22 |
+
/**
|
23 |
+
* {@inheritdoc}
|
24 |
+
*/
|
25 |
+
public function isEnabled()
|
26 |
+
{
|
27 |
+
return $this->enabled;
|
28 |
+
}
|
29 |
+
|
30 |
+
/**
|
31 |
+
* {@inheritdoc}
|
32 |
+
*/
|
33 |
+
public function getCacheLimit()
|
34 |
+
{
|
35 |
+
return $this->cacheLimit;
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* {@inheritdoc}
|
40 |
+
*/
|
41 |
+
public function toArray()
|
42 |
+
{
|
43 |
+
return [
|
44 |
+
'enabled' => $this->isEnabled(),
|
45 |
+
'cache_limit' => $this->getCacheLimit()
|
46 |
+
];
|
47 |
+
}
|
48 |
+
}
|
lib/Aws/Aws/EndpointDiscovery/ConfigurationInterface.php
ADDED
@@ -0,0 +1,30 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\EndpointDiscovery;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Provides access to endpoint discovery configuration options:
|
6 |
+
* 'enabled', 'cache_limit'
|
7 |
+
*/
|
8 |
+
interface ConfigurationInterface
|
9 |
+
{
|
10 |
+
/**
|
11 |
+
* Checks whether or not endpoint discovery is enabled.
|
12 |
+
*
|
13 |
+
* @return bool
|
14 |
+
*/
|
15 |
+
public function isEnabled();
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Returns the cache limit, if available.
|
19 |
+
*
|
20 |
+
* @return string|null
|
21 |
+
*/
|
22 |
+
public function getCacheLimit();
|
23 |
+
|
24 |
+
/**
|
25 |
+
* Returns the configuration as an associative array
|
26 |
+
*
|
27 |
+
* @return array
|
28 |
+
*/
|
29 |
+
public function toArray();
|
30 |
+
}
|
lib/Aws/Aws/EndpointDiscovery/ConfigurationProvider.php
ADDED
@@ -0,0 +1,333 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\EndpointDiscovery;
|
3 |
+
|
4 |
+
use Aws\CacheInterface;
|
5 |
+
use Aws\EndpointDiscovery\Exception\ConfigurationException;
|
6 |
+
use GuzzleHttp\Promise;
|
7 |
+
use GuzzleHttp\Promise\PromiseInterface;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* A configuration provider is a function that returns a promise that is
|
11 |
+
* fulfilled with a {@see \Aws\EndpointDiscovery\ConfigurationInterface}
|
12 |
+
* or rejected with an {@see \Aws\EndpointDiscovery\Exception\ConfigurationException}.
|
13 |
+
*
|
14 |
+
* <code>
|
15 |
+
* use Aws\EndpointDiscovery\ConfigurationProvider;
|
16 |
+
* $provider = ConfigurationProvider::defaultProvider();
|
17 |
+
* // Returns a ConfigurationInterface or throws.
|
18 |
+
* $config = $provider()->wait();
|
19 |
+
* </code>
|
20 |
+
*
|
21 |
+
* Configuration providers can be composed to create configuration using
|
22 |
+
* conditional logic that can create different configurations in different
|
23 |
+
* environments. You can compose multiple providers into a single provider using
|
24 |
+
* {@see Aws\EndpointDiscovery\ConfigurationProvider::chain}. This function
|
25 |
+
* accepts providers as variadic arguments and returns a new function that will
|
26 |
+
* invoke each provider until a successful configuration is returned.
|
27 |
+
*
|
28 |
+
* <code>
|
29 |
+
* // First try an INI file at this location.
|
30 |
+
* $a = ConfigurationProvider::ini(null, '/path/to/file.ini');
|
31 |
+
* // Then try an INI file at this location.
|
32 |
+
* $b = ConfigurationProvider::ini(null, '/path/to/other-file.ini');
|
33 |
+
* // Then try loading from environment variables.
|
34 |
+
* $c = ConfigurationProvider::env();
|
35 |
+
* // Combine the three providers together.
|
36 |
+
* $composed = ConfigurationProvider::chain($a, $b, $c);
|
37 |
+
* // Returns a promise that is fulfilled with a configuration or throws.
|
38 |
+
* $promise = $composed();
|
39 |
+
* // Wait on the configuration to resolve.
|
40 |
+
* $config = $promise->wait();
|
41 |
+
* </code>
|
42 |
+
*/
|
43 |
+
class ConfigurationProvider
|
44 |
+
{
|
45 |
+
const CACHE_KEY = 'aws_cached_endpoint_discovery_config';
|
46 |
+
const DEFAULT_ENABLED = false;
|
47 |
+
const DEFAULT_CACHE_LIMIT = 1000;
|
48 |
+
const ENV_ENABLED = 'AWS_ENDPOINT_DISCOVERY_ENABLED';
|
49 |
+
const ENV_ENABLED_ALT = 'AWS_ENABLE_ENDPOINT_DISCOVERY';
|
50 |
+
const ENV_PROFILE = 'AWS_PROFILE';
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Wraps a config provider and saves provided configuration in an
|
54 |
+
* instance of Aws\CacheInterface. Forwards calls when no config found
|
55 |
+
* in cache and updates cache with the results.
|
56 |
+
*
|
57 |
+
* @param callable $provider Configuration provider function to wrap
|
58 |
+
* @param CacheInterface $cache Cache to store credentials
|
59 |
+
* @param string|null $cacheKey (optional) Cache key to use
|
60 |
+
*
|
61 |
+
* @return callable
|
62 |
+
*/
|
63 |
+
public static function cache(
|
64 |
+
callable $provider,
|
65 |
+
CacheInterface $cache,
|
66 |
+
$cacheKey = null
|
67 |
+
) {
|
68 |
+
$cacheKey = $cacheKey ?: self::CACHE_KEY;
|
69 |
+
|
70 |
+
return function () use ($provider, $cache, $cacheKey) {
|
71 |
+
$found = $cache->get($cacheKey);
|
72 |
+
if ($found instanceof ConfigurationInterface) {
|
73 |
+
return Promise\promise_for($found);
|
74 |
+
}
|
75 |
+
|
76 |
+
return $provider()
|
77 |
+
->then(function (ConfigurationInterface $config) use (
|
78 |
+
$cache,
|
79 |
+
$cacheKey
|
80 |
+
) {
|
81 |
+
$cache->set($cacheKey, $config);
|
82 |
+
return $config;
|
83 |
+
});
|
84 |
+
};
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Creates an aggregate credentials provider that invokes the provided
|
89 |
+
* variadic providers one after the other until a provider returns
|
90 |
+
* credentials.
|
91 |
+
*
|
92 |
+
* @return callable
|
93 |
+
*/
|
94 |
+
public static function chain()
|
95 |
+
{
|
96 |
+
$links = func_get_args();
|
97 |
+
if (empty($links)) {
|
98 |
+
throw new \InvalidArgumentException('No providers in chain');
|
99 |
+
}
|
100 |
+
|
101 |
+
return function () use ($links) {
|
102 |
+
/** @var callable $parent */
|
103 |
+
$parent = array_shift($links);
|
104 |
+
$promise = $parent();
|
105 |
+
while ($next = array_shift($links)) {
|
106 |
+
$promise = $promise->otherwise($next);
|
107 |
+
}
|
108 |
+
return $promise;
|
109 |
+
};
|
110 |
+
}
|
111 |
+
|
112 |
+
/**
|
113 |
+
* Create a default config provider that first checks for environment
|
114 |
+
* variables, then checks for a specified profile in ~/.aws/config, then
|
115 |
+
* checks for the "default" profile in ~/.aws/config, and failing those uses
|
116 |
+
* a default fallback set of configuration options.
|
117 |
+
*
|
118 |
+
* This provider is automatically wrapped in a memoize function that caches
|
119 |
+
* previously provided config options.
|
120 |
+
*
|
121 |
+
* @param array $config Optional array of ecs/instance profile credentials
|
122 |
+
* provider options.
|
123 |
+
*
|
124 |
+
* @return callable
|
125 |
+
*/
|
126 |
+
public static function defaultProvider(array $config = [])
|
127 |
+
{
|
128 |
+
$configProviders = [
|
129 |
+
self::env(),
|
130 |
+
self::ini(),
|
131 |
+
self::fallback()
|
132 |
+
];
|
133 |
+
|
134 |
+
$memo = self::memoize(
|
135 |
+
call_user_func_array('self::chain', $configProviders)
|
136 |
+
);
|
137 |
+
|
138 |
+
if (isset($config['endpoint_discovery'])
|
139 |
+
&& $config['endpoint_discovery'] instanceof CacheInterface
|
140 |
+
) {
|
141 |
+
return self::cache($memo, $config['endpoint_discovery'], self::CACHE_KEY);
|
142 |
+
}
|
143 |
+
|
144 |
+
return $memo;
|
145 |
+
}
|
146 |
+
|
147 |
+
/**
|
148 |
+
* Provider that creates config from environment variables.
|
149 |
+
*
|
150 |
+
* @param $cacheLimit
|
151 |
+
* @return callable
|
152 |
+
*/
|
153 |
+
public static function env($cacheLimit = self::DEFAULT_CACHE_LIMIT)
|
154 |
+
{
|
155 |
+
return function () use ($cacheLimit) {
|
156 |
+
// Use config from environment variables, if available
|
157 |
+
$enabled = getenv(self::ENV_ENABLED);
|
158 |
+
if ($enabled === false || $enabled === '') {
|
159 |
+
$enabled = getenv(self::ENV_ENABLED_ALT);
|
160 |
+
}
|
161 |
+
if ($enabled !== false && $enabled !== '') {
|
162 |
+
return Promise\promise_for(
|
163 |
+
new Configuration($enabled, $cacheLimit)
|
164 |
+
);
|
165 |
+
}
|
166 |
+
|
167 |
+
return self::reject('Could not find environment variable config'
|
168 |
+
. ' in ' . self::ENV_ENABLED);
|
169 |
+
};
|
170 |
+
}
|
171 |
+
|
172 |
+
/**
|
173 |
+
* Fallback config options when other sources are not set.
|
174 |
+
*
|
175 |
+
* @return callable
|
176 |
+
*/
|
177 |
+
public static function fallback()
|
178 |
+
{
|
179 |
+
return function () {
|
180 |
+
return Promise\promise_for(
|
181 |
+
new Configuration(
|
182 |
+
self::DEFAULT_ENABLED,
|
183 |
+
self::DEFAULT_CACHE_LIMIT
|
184 |
+
)
|
185 |
+
);
|
186 |
+
};
|
187 |
+
}
|
188 |
+
|
189 |
+
/**
|
190 |
+
* Gets the environment's HOME directory if available.
|
191 |
+
*
|
192 |
+
* @return null|string
|
193 |
+
*/
|
194 |
+
private static function getHomeDir()
|
195 |
+
{
|
196 |
+
// On Linux/Unix-like systems, use the HOME environment variable
|
197 |
+
if ($homeDir = getenv('HOME')) {
|
198 |
+
return $homeDir;
|
199 |
+
}
|
200 |
+
|
201 |
+
// Get the HOMEDRIVE and HOMEPATH values for Windows hosts
|
202 |
+
$homeDrive = getenv('HOMEDRIVE');
|
203 |
+
$homePath = getenv('HOMEPATH');
|
204 |
+
|
205 |
+
return ($homeDrive && $homePath) ? $homeDrive . $homePath : null;
|
206 |
+
}
|
207 |
+
|
208 |
+
/**
|
209 |
+
* Config provider that creates config using an ini file stored
|
210 |
+
* in the current user's home directory.
|
211 |
+
*
|
212 |
+
* @param string|null $profile Profile to use. If not specified will use
|
213 |
+
* the "default" profile in "~/.aws/config".
|
214 |
+
* @param string|null $filename If provided, uses a custom filename rather
|
215 |
+
* than looking in the home directory.
|
216 |
+
* @param int $cacheLimit
|
217 |
+
*
|
218 |
+
* @return callable
|
219 |
+
*/
|
220 |
+
public static function ini(
|
221 |
+
$profile = null,
|
222 |
+
$filename = null,
|
223 |
+
$cacheLimit = self::DEFAULT_CACHE_LIMIT
|
224 |
+
) {
|
225 |
+
$filename = $filename ?: (self::getHomeDir() . '/.aws/config');
|
226 |
+
$profile = $profile ?: (getenv(self::ENV_PROFILE) ?: 'default');
|
227 |
+
|
228 |
+
return function () use ($profile, $filename, $cacheLimit) {
|
229 |
+
if (!is_readable($filename)) {
|
230 |
+
return self::reject("Cannot read configuration from $filename");
|
231 |
+
}
|
232 |
+
$data = \Aws\parse_ini_file($filename, true);
|
233 |
+
if ($data === false) {
|
234 |
+
return self::reject("Invalid config file: $filename");
|
235 |
+
}
|
236 |
+
if (!isset($data[$profile])) {
|
237 |
+
return self::reject("'$profile' not found in config file");
|
238 |
+
}
|
239 |
+
if (!isset($data[$profile]['endpoint_discovery_enabled'])) {
|
240 |
+
return self::reject("Required endpoint discovery config values
|
241 |
+
not present in INI profile '{$profile}' ({$filename})");
|
242 |
+
}
|
243 |
+
|
244 |
+
return Promise\promise_for(
|
245 |
+
new Configuration(
|
246 |
+
$data[$profile]['endpoint_discovery_enabled'],
|
247 |
+
$cacheLimit
|
248 |
+
)
|
249 |
+
);
|
250 |
+
};
|
251 |
+
}
|
252 |
+
|
253 |
+
/**
|
254 |
+
* Wraps a config provider and caches previously provided configuration.
|
255 |
+
*
|
256 |
+
* Ensures that cached configuration is refreshed when it expires.
|
257 |
+
*
|
258 |
+
* @param callable $provider Config provider function to wrap.
|
259 |
+
*
|
260 |
+
* @return callable
|
261 |
+
*/
|
262 |
+
public static function memoize(callable $provider)
|
263 |
+
{
|
264 |
+
return function () use ($provider) {
|
265 |
+
static $result;
|
266 |
+
static $isConstant;
|
267 |
+
|
268 |
+
// Constant config will be returned constantly.
|
269 |
+
if ($isConstant) {
|
270 |
+
return $result;
|
271 |
+
}
|
272 |
+
|
273 |
+
// Create the initial promise that will be used as the cached value
|
274 |
+
// until it expires.
|
275 |
+
if (null === $result) {
|
276 |
+
$result = $provider();
|
277 |
+
}
|
278 |
+
|
279 |
+
// Return config and set flag that provider is already set
|
280 |
+
return $result
|
281 |
+
->then(function (ConfigurationInterface $config) use (&$isConstant) {
|
282 |
+
$isConstant = true;
|
283 |
+
return $config;
|
284 |
+
});
|
285 |
+
};
|
286 |
+
}
|
287 |
+
|
288 |
+
/**
|
289 |
+
* Reject promise with standardized exception.
|
290 |
+
*
|
291 |
+
* @param $msg
|
292 |
+
* @return Promise\RejectedPromise
|
293 |
+
*/
|
294 |
+
private static function reject($msg)
|
295 |
+
{
|
296 |
+
return new Promise\RejectedPromise(new ConfigurationException($msg));
|
297 |
+
}
|
298 |
+
|
299 |
+
/**
|
300 |
+
* Unwraps a configuration object in whatever valid form it is in,
|
301 |
+
* always returning a ConfigurationInterface object.
|
302 |
+
*
|
303 |
+
* @param mixed $config
|
304 |
+
* @return ConfigurationInterface
|
305 |
+
* @throws \InvalidArgumentException
|
306 |
+
*/
|
307 |
+
public static function unwrap($config)
|
308 |
+
{
|
309 |
+
if (is_callable($config)) {
|
310 |
+
$config = $config();
|
311 |
+
}
|
312 |
+
if ($config instanceof PromiseInterface) {
|
313 |
+
$config = $config->wait();
|
314 |
+
}
|
315 |
+
if ($config instanceof ConfigurationInterface) {
|
316 |
+
return $config;
|
317 |
+
} elseif (is_array($config) && isset($config['enabled'])) {
|
318 |
+
if (isset($config['cache_limit'])) {
|
319 |
+
return new Configuration(
|
320 |
+
$config['enabled'],
|
321 |
+
$config['cache_limit']
|
322 |
+
);
|
323 |
+
}
|
324 |
+
return new Configuration(
|
325 |
+
$config['enabled'],
|
326 |
+
self::DEFAULT_CACHE_LIMIT
|
327 |
+
);
|
328 |
+
}
|
329 |
+
|
330 |
+
throw new \InvalidArgumentException('Not a valid endpoint_discovery '
|
331 |
+
. 'configuration argument.');
|
332 |
+
}
|
333 |
+
}
|
lib/Aws/Aws/EndpointDiscovery/EndpointDiscoveryMiddleware.php
ADDED
@@ -0,0 +1,414 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\EndpointDiscovery;
|
3 |
+
|
4 |
+
use Aws\AwsClient;
|
5 |
+
use Aws\CacheInterface;
|
6 |
+
use Aws\CommandInterface;
|
7 |
+
use Aws\Credentials\CredentialsInterface;
|
8 |
+
use Aws\Exception\AwsException;
|
9 |
+
use Aws\Exception\UnresolvedEndpointException;
|
10 |
+
use Aws\LruArrayCache;
|
11 |
+
use Aws\Middleware;
|
12 |
+
use Psr\Http\Message\RequestInterface;
|
13 |
+
use Psr\Http\Message\UriInterface;
|
14 |
+
|
15 |
+
class EndpointDiscoveryMiddleware
|
16 |
+
{
|
17 |
+
/**
|
18 |
+
* @var CacheInterface
|
19 |
+
*/
|
20 |
+
private static $cache;
|
21 |
+
private static $discoveryCooldown = 60;
|
22 |
+
|
23 |
+
private $args;
|
24 |
+
private $client;
|
25 |
+
private $config;
|
26 |
+
private $discoveryTimes = [];
|
27 |
+
private $nextHandler;
|
28 |
+
private $service;
|
29 |
+
|
30 |
+
public static function wrap(
|
31 |
+
$client,
|
32 |
+
$args,
|
33 |
+
$config
|
34 |
+
) {
|
35 |
+
return function (callable $handler) use (
|
36 |
+
$client,
|
37 |
+
$args,
|
38 |
+
$config
|
39 |
+
) {
|
40 |
+
return new static(
|
41 |
+
$handler,
|
42 |
+
$client,
|
43 |
+
$args,
|
44 |
+
$config
|
45 |
+
);
|
46 |
+
};
|
47 |
+
}
|
48 |
+
|
49 |
+
public function __construct(
|
50 |
+
callable $handler,
|
51 |
+
AwsClient $client,
|
52 |
+
array $args,
|
53 |
+
$config
|
54 |
+
) {
|
55 |
+
$this->nextHandler = $handler;
|
56 |
+
$this->client = $client;
|
57 |
+
$this->args = $args;
|
58 |
+
$this->service = $client->getApi();
|
59 |
+
$this->config = $config;
|
60 |
+
}
|
61 |
+
|
62 |
+
public function __invoke(CommandInterface $cmd, RequestInterface $request)
|
63 |
+
{
|
64 |
+
$nextHandler = $this->nextHandler;
|
65 |
+
$op = $this->service->getOperation($cmd->getName())->toArray();
|
66 |
+
|
67 |
+
// Continue only if endpointdiscovery trait is set
|
68 |
+
if (isset($op['endpointdiscovery'])) {
|
69 |
+
$config = ConfigurationProvider::unwrap($this->config);
|
70 |
+
$isRequired = !empty($op['endpointdiscovery']['required']);
|
71 |
+
|
72 |
+
// Continue only if required by operation or enabled by config
|
73 |
+
if ($isRequired || $config->isEnabled()) {
|
74 |
+
if (isset($op['endpointoperation'])) {
|
75 |
+
throw new UnresolvedEndpointException('This operation is '
|
76 |
+
. 'contradictorily marked both as using endpoint discovery '
|
77 |
+
. 'and being the endpoint discovery operation. Please '
|
78 |
+
. 'verify the accuracy of your model files.');
|
79 |
+
}
|
80 |
+
|
81 |
+
// Original endpoint may be used if discovery optional
|
82 |
+
$originalUri = $request->getUri();
|
83 |
+
|
84 |
+
$identifiers = $this->getIdentifiers($op);
|
85 |
+
|
86 |
+
$cacheKey = $this->getCacheKey(
|
87 |
+
$this->client->getCredentials()->wait(),
|
88 |
+
$cmd,
|
89 |
+
$identifiers
|
90 |
+
);
|
91 |
+
|
92 |
+
// Check/create cache
|
93 |
+
if (!isset(self::$cache)) {
|
94 |
+
self::$cache = new LruArrayCache($config->getCacheLimit());
|
95 |
+
}
|
96 |
+
|
97 |
+
if (empty($endpointList = self::$cache->get($cacheKey))) {
|
98 |
+
$endpointList = new EndpointList([]);
|
99 |
+
}
|
100 |
+
$endpoint = $endpointList->getActive();
|
101 |
+
|
102 |
+
// Retrieve endpoints if there is no active endpoint
|
103 |
+
if (empty($endpoint)) {
|
104 |
+
try {
|
105 |
+
$endpoint = $this->discoverEndpoint(
|
106 |
+
$cacheKey,
|
107 |
+
$cmd,
|
108 |
+
$identifiers
|
109 |
+
);
|
110 |
+
} catch (\Exception $e) {
|
111 |
+
// Use cached endpoint, expired or active, if any remain
|
112 |
+
$endpoint = $endpointList->getEndpoint();
|
113 |
+
|
114 |
+
if (empty($endpoint)) {
|
115 |
+
return $this->handleDiscoveryException(
|
116 |
+
$isRequired,
|
117 |
+
$originalUri,
|
118 |
+
$e,
|
119 |
+
$cmd,
|
120 |
+
$request
|
121 |
+
);
|
122 |
+
}
|
123 |
+
}
|
124 |
+
}
|
125 |
+
|
126 |
+
$request = $this->modifyRequest($request, $endpoint);
|
127 |
+
|
128 |
+
$g = function ($value) use (
|
129 |
+
$cacheKey,
|
130 |
+
$cmd,
|
131 |
+
$identifiers,
|
132 |
+
$isRequired,
|
133 |
+
$nextHandler,
|
134 |
+
$originalUri,
|
135 |
+
$request,
|
136 |
+
&$endpoint,
|
137 |
+
&$g
|
138 |
+
) {
|
139 |
+
if ($value instanceof AwsException
|
140 |
+
&& (
|
141 |
+
$value->getAwsErrorCode() == 'InvalidEndpointException'
|
142 |
+
|| $value->getStatusCode() == 421
|
143 |
+
)
|
144 |
+
) {
|
145 |
+
return $this->handleInvalidEndpoint(
|
146 |
+
$cacheKey,
|
147 |
+
$cmd,
|
148 |
+
$identifiers,
|
149 |
+
$isRequired,
|
150 |
+
$originalUri,
|
151 |
+
$request,
|
152 |
+
$value,
|
153 |
+
$endpoint,
|
154 |
+
$g
|
155 |
+
);
|
156 |
+
}
|
157 |
+
|
158 |
+
return $value;
|
159 |
+
};
|
160 |
+
|
161 |
+
return $nextHandler($cmd, $request)->otherwise($g);
|
162 |
+
}
|
163 |
+
}
|
164 |
+
|
165 |
+
return $nextHandler($cmd, $request);
|
166 |
+
}
|
167 |
+
|
168 |
+
private function discoverEndpoint(
|
169 |
+
$cacheKey,
|
170 |
+
CommandInterface $cmd,
|
171 |
+
array $identifiers
|
172 |
+
) {
|
173 |
+
$discCmd = $this->getDiscoveryCommand($cmd, $identifiers);
|
174 |
+
$this->discoveryTimes[$cacheKey] = time();
|
175 |
+
$result = $this->client->execute($discCmd);
|
176 |
+
|
177 |
+
if (isset($result['Endpoints'])) {
|
178 |
+
$endpointData = [];
|
179 |
+
foreach ($result['Endpoints'] as $datum) {
|
180 |
+
$endpointData[$datum['Address']] = time()
|
181 |
+
+ ($datum['CachePeriodInMinutes'] * 60);
|
182 |
+
}
|
183 |
+
$endpointList = new EndpointList($endpointData);
|
184 |
+
self::$cache->set($cacheKey, $endpointList);
|
185 |
+
return $endpointList->getEndpoint();
|
186 |
+
}
|
187 |
+
|
188 |
+
throw new UnresolvedEndpointException('The endpoint discovery operation '
|
189 |
+
. 'yielded a response that did not contain properly formatted '
|
190 |
+
. 'endpoint data.');
|
191 |
+
}
|
192 |
+
|
193 |
+
private function getCacheKey(
|
194 |
+
CredentialsInterface $creds,
|
195 |
+
CommandInterface $cmd,
|
196 |
+
array $identifiers
|
197 |
+
) {
|
198 |
+
$key = $this->service->getServiceName() . '_' . $creds->getAccessKeyId();
|
199 |
+
if (!empty($identifiers)) {
|
200 |
+
$key .= '_' . $cmd->getName();
|
201 |
+
foreach ($identifiers as $identifier) {
|
202 |
+
$key .= "_{$cmd[$identifier]}";
|
203 |
+
}
|
204 |
+
}
|
205 |
+
|
206 |
+
return $key;
|
207 |
+
}
|
208 |
+
|
209 |
+
private function getDiscoveryCommand(
|
210 |
+
CommandInterface $cmd,
|
211 |
+
array $identifiers
|
212 |
+
) {
|
213 |
+
foreach ($this->service->getOperations() as $op) {
|
214 |
+
if (isset($op['endpointoperation'])) {
|
215 |
+
$endpointOperation = $op->toArray()['name'];
|
216 |
+
break;
|
217 |
+
}
|
218 |
+
}
|
219 |
+
|
220 |
+
if (!isset($endpointOperation)) {
|
221 |
+
throw new UnresolvedEndpointException('This command is set to use '
|
222 |
+
. 'endpoint discovery, but no endpoint discovery operation was '
|
223 |
+
. 'found. Please verify the accuracy of your model files.');
|
224 |
+
}
|
225 |
+
|
226 |
+
$params = [];
|
227 |
+
if (!empty($identifiers)) {
|
228 |
+
$params['Operation'] = $cmd->getName();
|
229 |
+
$params['Identifiers'] = [];
|
230 |
+
foreach ($identifiers as $identifier) {
|
231 |
+
$params['Identifiers'][$identifier] = $cmd[$identifier];
|
232 |
+
}
|
233 |
+
}
|
234 |
+
$command = $this->client->getCommand($endpointOperation, $params);
|
235 |
+
$command->getHandlerList()->appendBuild(
|
236 |
+
Middleware::mapRequest(function (RequestInterface $r) {
|
237 |
+
return $r->withHeader(
|
238 |
+
'x-amz-api-version',
|
239 |
+
$this->service->getApiVersion()
|
240 |
+
);
|
241 |
+
}),
|
242 |
+
'x-amz-api-version-header'
|
243 |
+
);
|
244 |
+
|
245 |
+
return $command;
|
246 |
+
}
|
247 |
+
|
248 |
+
private function getIdentifiers(array $operation)
|
249 |
+
{
|
250 |
+
$inputShape = $this->service->getShapeMap()
|
251 |
+
->resolve($operation['input'])
|
252 |
+
->toArray();
|
253 |
+
$identifiers = [];
|
254 |
+
foreach ($inputShape['members'] as $key => $member) {
|
255 |
+
if (!empty($member['endpointdiscoveryid'])) {
|
256 |
+
$identifiers[] = $key;
|
257 |
+
}
|
258 |
+
}
|
259 |
+
return $identifiers;
|
260 |
+
}
|
261 |
+
|
262 |
+
private function handleDiscoveryException(
|
263 |
+
$isRequired,
|
264 |
+
$originalUri,
|
265 |
+
\Exception $e,
|
266 |
+
CommandInterface $cmd,
|
267 |
+
RequestInterface $request
|
268 |
+
) {
|
269 |
+
// If no cached endpoints and discovery required,
|
270 |
+
// throw exception
|
271 |
+
if ($isRequired) {
|
272 |
+
$message = 'The endpoint required for this service is currently '
|
273 |
+
. 'unable to be retrieved, and your request can not be fulfilled '
|
274 |
+
. 'unless you manually specify an endpoint.';
|
275 |
+
throw new AwsException(
|
276 |
+
$message,
|
277 |
+
$cmd,
|
278 |
+
[
|
279 |
+
'code' => 'EndpointDiscoveryException',
|
280 |
+
'message' => $message
|
281 |
+
],
|
282 |
+
$e
|
283 |
+
);
|
284 |
+
}
|
285 |
+
|
286 |
+
// If discovery isn't required, use original endpoint
|
287 |
+
return $this->useOriginalUri(
|
288 |
+
$originalUri,
|
289 |
+
$cmd,
|
290 |
+
$request
|
291 |
+
);
|
292 |
+
}
|
293 |
+
|
294 |
+
private function handleInvalidEndpoint(
|
295 |
+
$cacheKey,
|
296 |
+
$cmd,
|
297 |
+
$identifiers,
|
298 |
+
$isRequired,
|
299 |
+
$originalUri,
|
300 |
+
$request,
|
301 |
+
$value,
|
302 |
+
&$endpoint,
|
303 |
+
&$g
|
304 |
+
) {
|
305 |
+
$nextHandler = $this->nextHandler;
|
306 |
+
$endpointList = self::$cache->get($cacheKey);
|
307 |
+
if ($endpointList instanceof EndpointList) {
|
308 |
+
|
309 |
+
// Remove invalid endpoint from cached list
|
310 |
+
$endpointList->remove($endpoint);
|
311 |
+
|
312 |
+
// If possible, get another cached endpoint
|
313 |
+
$newEndpoint = $endpointList->getEndpoint();
|
314 |
+
}
|
315 |
+
if (empty($newEndpoint)) {
|
316 |
+
|
317 |
+
// If no more cached endpoints, make discovery call
|
318 |
+
// if none made within cooldown for given key
|
319 |
+
if (time() - $this->discoveryTimes[$cacheKey]
|
320 |
+
< self::$discoveryCooldown
|
321 |
+
) {
|
322 |
+
|
323 |
+
// If no more cached endpoints and it's required,
|
324 |
+
// fail with original exception
|
325 |
+
if ($isRequired) {
|
326 |
+
return $value;
|
327 |
+
}
|
328 |
+
|
329 |
+
// Use original endpoint if not required
|
330 |
+
return $this->useOriginalUri(
|
331 |
+
$originalUri,
|
332 |
+
$cmd,
|
333 |
+
$request
|
334 |
+
);
|
335 |
+
}
|
336 |
+
|
337 |
+
$newEndpoint = $this->discoverEndpoint(
|
338 |
+
$cacheKey,
|
339 |
+
$cmd,
|
340 |
+
$identifiers
|
341 |
+
);
|
342 |
+
}
|
343 |
+
$endpoint = $newEndpoint;
|
344 |
+
$request = $this->modifyRequest($request, $endpoint);
|
345 |
+
return $nextHandler($cmd, $request)->otherwise($g);
|
346 |
+
}
|
347 |
+
|
348 |
+
private function modifyRequest(RequestInterface $request, $endpoint)
|
349 |
+
{
|
350 |
+
$parsed = $this->parseEndpoint($endpoint);
|
351 |
+
if (!empty($request->getHeader('User-Agent'))) {
|
352 |
+
$userAgent = $request->getHeader('User-Agent')[0];
|
353 |
+
if (strpos($userAgent, 'endpoint-discovery') === false) {
|
354 |
+
$userAgent = $userAgent . ' endpoint-discovery';
|
355 |
+
}
|
356 |
+
} else {
|
357 |
+
$userAgent = 'endpoint-discovery';
|
358 |
+
}
|
359 |
+
|
360 |
+
return $request
|
361 |
+
->withUri(
|
362 |
+
$request->getUri()
|
363 |
+
->withHost($parsed['host'])
|
364 |
+
->withPath($parsed['path'])
|
365 |
+
)
|
366 |
+
->withHeader('User-Agent', $userAgent);
|
367 |
+
}
|
368 |
+
|
369 |
+
/**
|
370 |
+
* Parses an endpoint returned from the discovery API into an array with
|
371 |
+
* 'host' and 'path' keys.
|
372 |
+
*
|
373 |
+
* @param $endpoint
|
374 |
+
* @return array
|
375 |
+
*/
|
376 |
+
private function parseEndpoint($endpoint)
|
377 |
+
{
|
378 |
+
$parsed = parse_url($endpoint);
|
379 |
+
|
380 |
+
// parse_url() will correctly parse full URIs with schemes
|
381 |
+
if (isset($parsed['host'])) {
|
382 |
+
return $parsed;
|
383 |
+
}
|
384 |
+
|
385 |
+
// parse_url() will put host & path in 'path' if scheme is not provided
|
386 |
+
if (isset($parsed['path'])) {
|
387 |
+
$split = explode('/', $parsed['path'], 2);
|
388 |
+
$parsed['host'] = $split[0];
|
389 |
+
if (isset($split[1])) {
|
390 |
+
$parsed['path'] = $split[1];
|
391 |
+
} else {
|
392 |
+
$parsed['path'] = '';
|
393 |
+
}
|
394 |
+
return $parsed;
|
395 |
+
}
|
396 |
+
|
397 |
+
throw new UnresolvedEndpointException("The supplied endpoint '"
|
398 |
+
. "{$endpoint}' is invalid.");
|
399 |
+
}
|
400 |
+
|
401 |
+
private function useOriginalUri(
|
402 |
+
UriInterface $uri,
|
403 |
+
CommandInterface $cmd,
|
404 |
+
RequestInterface $request
|
405 |
+
) {
|
406 |
+
$nextHandler = $this->nextHandler;
|
407 |
+
$endpoint = $uri->getHost() . $uri->getPath();
|
408 |
+
$request = $this->modifyRequest(
|
409 |
+
$request,
|
410 |
+
$endpoint
|
411 |
+
);
|
412 |
+
return $nextHandler($cmd, $request);
|
413 |
+
}
|
414 |
+
}
|
lib/Aws/Aws/EndpointDiscovery/EndpointList.php
ADDED
@@ -0,0 +1,85 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\EndpointDiscovery;
|
3 |
+
|
4 |
+
class EndpointList
|
5 |
+
{
|
6 |
+
private $active;
|
7 |
+
private $expired = [];
|
8 |
+
|
9 |
+
public function __construct(array $endpoints)
|
10 |
+
{
|
11 |
+
$this->active = $endpoints;
|
12 |
+
reset($this->active);
|
13 |
+
}
|
14 |
+
|
15 |
+
/**
|
16 |
+
* Gets an active (unexpired) endpoint. Returns null if none found.
|
17 |
+
*
|
18 |
+
* @return null|string
|
19 |
+
*/
|
20 |
+
public function getActive()
|
21 |
+
{
|
22 |
+
if (count($this->active) < 1) {
|
23 |
+
return null;
|
24 |
+
}
|
25 |
+
while (time() > current($this->active)) {
|
26 |
+
$key = key($this->active);
|
27 |
+
$this->expired[$key] = current($this->active);
|
28 |
+
$this->increment($this->active);
|
29 |
+
unset($this->active[$key]);
|
30 |
+
if (count($this->active) < 1) {
|
31 |
+
return null;
|
32 |
+
}
|
33 |
+
}
|
34 |
+
$active = key($this->active);
|
35 |
+
$this->increment($this->active);
|
36 |
+
return $active;
|
37 |
+
}
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Gets an active endpoint if possible, then an expired endpoint if possible.
|
41 |
+
* Returns null if no endpoints found.
|
42 |
+
*
|
43 |
+
* @return null|string
|
44 |
+
*/
|
45 |
+
public function getEndpoint()
|
46 |
+
{
|
47 |
+
if (!empty($active = $this->getActive())) {
|
48 |
+
return $active;
|
49 |
+
}
|
50 |
+
return $this->getExpired();
|
51 |
+
}
|
52 |
+
|
53 |
+
/**
|
54 |
+
* Removes an endpoint from both lists.
|
55 |
+
*
|
56 |
+
* @param string $key
|
57 |
+
*/
|
58 |
+
public function remove($key)
|
59 |
+
{
|
60 |
+
unset($this->active[$key]);
|
61 |
+
unset($this->expired[$key]);
|
62 |
+
}
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Get an expired endpoint. Returns null if none found.
|
66 |
+
*
|
67 |
+
* @return null|string
|
68 |
+
*/
|
69 |
+
private function getExpired()
|
70 |
+
{
|
71 |
+
if (count($this->expired) < 1) {
|
72 |
+
return null;
|
73 |
+
}
|
74 |
+
$expired = key($this->expired);
|
75 |
+
$this->increment($this->expired);
|
76 |
+
return $expired;
|
77 |
+
}
|
78 |
+
|
79 |
+
private function increment(&$array)
|
80 |
+
{
|
81 |
+
if (next($array) === false) {
|
82 |
+
reset($array);
|
83 |
+
}
|
84 |
+
}
|
85 |
+
}
|
lib/Aws/Aws/EndpointDiscovery/Exception/ConfigurationException.php
ADDED
@@ -0,0 +1,14 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\EndpointDiscovery\Exception;
|
3 |
+
|
4 |
+
use Aws\HasMonitoringEventsTrait;
|
5 |
+
use Aws\MonitoringEventsInterface;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Represents an error interacting with configuration for endpoint discovery
|
9 |
+
*/
|
10 |
+
class ConfigurationException extends \RuntimeException implements
|
11 |
+
MonitoringEventsInterface
|
12 |
+
{
|
13 |
+
use HasMonitoringEventsTrait;
|
14 |
+
}
|
lib/Aws/Aws/EndpointParameterMiddleware.php
ADDED
@@ -0,0 +1,84 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws;
|
3 |
+
|
4 |
+
use Aws\Api\Service;
|
5 |
+
use Psr\Http\Message\RequestInterface;
|
6 |
+
use Psr\Log\InvalidArgumentException;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Used to update the host based on a modeled endpoint trait
|
10 |
+
*
|
11 |
+
* IMPORTANT: this middleware must be added after the "build" step.
|
12 |
+
*
|
13 |
+
* @internal
|
14 |
+
*/
|
15 |
+
class EndpointParameterMiddleware
|
16 |
+
{
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Create a middleware wrapper function
|
20 |
+
*
|
21 |
+
* @param Service $service
|
22 |
+
* @param array $args
|
23 |
+
* @return \Closure
|
24 |
+
*/
|
25 |
+
public static function wrap(Service $service)
|
26 |
+
{
|
27 |
+
return function (callable $handler) use ($service) {
|
28 |
+
return new self($handler, $service);
|
29 |
+
};
|
30 |
+
}
|
31 |
+
|
32 |
+
public function __construct(callable $nextHandler, Service $service)
|
33 |
+
{
|
34 |
+
$this->nextHandler = $nextHandler;
|
35 |
+
$this->service = $service;
|
36 |
+
}
|
37 |
+
|
38 |
+
public function __invoke(CommandInterface $command, RequestInterface $request)
|
39 |
+
{
|
40 |
+
$nextHandler = $this->nextHandler;
|
41 |
+
|
42 |
+
$operation = $this->service->getOperation($command->getName());
|
43 |
+
|
44 |
+
if (!empty($operation['endpoint']['hostPrefix'])) {
|
45 |
+
$prefix = $operation['endpoint']['hostPrefix'];
|
46 |
+
|
47 |
+
// Captures endpoint parameters stored in the modeled host.
|
48 |
+
// These are denoted by enclosure in braces, i.e. '{param}'
|
49 |
+
preg_match_all("/\{([a-zA-Z0-9]+)}/", $prefix, $parameters);
|
50 |
+
|
51 |
+
if (!empty($parameters[1])) {
|
52 |
+
|
53 |
+
// Captured parameters without braces stored in $parameters[1],
|
54 |
+
// which should correspond to members in the Command object
|
55 |
+
foreach ($parameters[1] as $index => $parameter) {
|
56 |
+
if (empty($command[$parameter])) {
|
57 |
+
throw new \InvalidArgumentException(
|
58 |
+
"The parameter '{$parameter}' must be set and not empty."
|
59 |
+
);
|
60 |
+
}
|
61 |
+
|
62 |
+
// Captured parameters with braces stored in $parameters[0],
|
63 |
+
// which are replaced by their corresponding Command value
|
64 |
+
$prefix = str_replace(
|
65 |
+
$parameters[0][$index],
|
66 |
+
$command[$parameter],
|
67 |
+
$prefix
|
68 |
+
);
|
69 |
+
}
|
70 |
+
}
|
71 |
+
|
72 |
+
$uri = $request->getUri();
|
73 |
+
$host = $prefix . $uri->getHost();
|
74 |
+
if (!\Aws\is_valid_hostname($host)) {
|
75 |
+
throw new \InvalidArgumentException(
|
76 |
+
"The supplied parameters result in an invalid hostname: '{$host}'."
|
77 |
+
);
|
78 |
+
}
|
79 |
+
$request = $request->withUri($uri->withHost($host));
|
80 |
+
}
|
81 |
+
|
82 |
+
return $nextHandler($command, $request);
|
83 |
+
}
|
84 |
+
}
|
lib/Aws/Aws/Exception/AwsException.php
ADDED
@@ -0,0 +1,237 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Exception;
|
3 |
+
|
4 |
+
use Aws\HasMonitoringEventsTrait;
|
5 |
+
use Aws\MonitoringEventsInterface;
|
6 |
+
use Aws\ResponseContainerInterface;
|
7 |
+
use Psr\Http\Message\ResponseInterface;
|
8 |
+
use Psr\Http\Message\RequestInterface;
|
9 |
+
use Aws\CommandInterface;
|
10 |
+
use Aws\ResultInterface;
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Represents an AWS exception that is thrown when a command fails.
|
14 |
+
*/
|
15 |
+
class AwsException extends \RuntimeException implements
|
16 |
+
MonitoringEventsInterface,
|
17 |
+
ResponseContainerInterface
|
18 |
+
{
|
19 |
+
use HasMonitoringEventsTrait;
|
20 |
+
|
21 |
+
/** @var ResponseInterface */
|
22 |
+
private $response;
|
23 |
+
private $request;
|
24 |
+
private $result;
|
25 |
+
private $command;
|
26 |
+
private $requestId;
|
27 |
+
private $errorType;
|
28 |
+
private $errorCode;
|
29 |
+
private $connectionError;
|
30 |
+
private $transferInfo;
|
31 |
+
private $errorMessage;
|
32 |
+
private $maxRetriesExceeded;
|
33 |
+
|
34 |
+
|
35 |
+
/**
|
36 |
+
* @param string $message Exception message
|
37 |
+
* @param CommandInterface $command
|
38 |
+
* @param array $context Exception context
|
39 |
+
* @param \Exception $previous Previous exception (if any)
|
40 |
+
*/
|
41 |
+
public function __construct(
|
42 |
+
$message,
|
43 |
+
CommandInterface $command,
|
44 |
+
array $context = [],
|
45 |
+
\Exception $previous = null
|
46 |
+
) {
|
47 |
+
$this->command = $command;
|
48 |
+
$this->response = isset($context['response']) ? $context['response'] : null;
|
49 |
+
$this->request = isset($context['request']) ? $context['request'] : null;
|
50 |
+
$this->requestId = isset($context['request_id'])
|
51 |
+
? $context['request_id']
|
52 |
+
: null;
|
53 |
+
$this->errorType = isset($context['type']) ? $context['type'] : null;
|
54 |
+
$this->errorCode = isset($context['code']) ? $context['code'] : null;
|
55 |
+
$this->connectionError = !empty($context['connection_error']);
|
56 |
+
$this->result = isset($context['result']) ? $context['result'] : null;
|
57 |
+
$this->transferInfo = isset($context['transfer_stats'])
|
58 |
+
? $context['transfer_stats']
|
59 |
+
: [];
|
60 |
+
$this->errorMessage = isset($context['message'])
|
61 |
+
? $context['message']
|
62 |
+
: null;
|
63 |
+
$this->monitoringEvents = [];
|
64 |
+
$this->maxRetriesExceeded = false;
|
65 |
+
parent::__construct($message, 0, $previous);
|
66 |
+
}
|
67 |
+
|
68 |
+
public function __toString()
|
69 |
+
{
|
70 |
+
if (!$this->getPrevious()) {
|
71 |
+
return parent::__toString();
|
72 |
+
}
|
73 |
+
|
74 |
+
// PHP strangely shows the innermost exception first before the outer
|
75 |
+
// exception message. It also has a default character limit for
|
76 |
+
// exception message strings such that the "next" exception (this one)
|
77 |
+
// might not even get shown, causing developers to attempt to catch
|
78 |
+
// the inner exception instead of the actual exception because they
|
79 |
+
// can't see the outer exception's __toString output.
|
80 |
+
return sprintf(
|
81 |
+
"exception '%s' with message '%s'\n\n%s",
|
82 |
+
get_class($this),
|
83 |
+
$this->getMessage(),
|
84 |
+
parent::__toString()
|
85 |
+
);
|
86 |
+
}
|
87 |
+
|
88 |
+
/**
|
89 |
+
* Get the command that was executed.
|
90 |
+
*
|
91 |
+
* @return CommandInterface
|
92 |
+
*/
|
93 |
+
public function getCommand()
|
94 |
+
{
|
95 |
+
return $this->command;
|
96 |
+
}
|
97 |
+
|
98 |
+
/**
|
99 |
+
* Get the concise error message if any.
|
100 |
+
*
|
101 |
+
* @return string|null
|
102 |
+
*/
|
103 |
+
public function getAwsErrorMessage()
|
104 |
+
{
|
105 |
+
return $this->errorMessage;
|
106 |
+
}
|
107 |
+
|
108 |
+
/**
|
109 |
+
* Get the sent HTTP request if any.
|
110 |
+
*
|
111 |
+
* @return RequestInterface|null
|
112 |
+
*/
|
113 |
+
public function getRequest()
|
114 |
+
{
|
115 |
+
return $this->request;
|
116 |
+
}
|
117 |
+
|
118 |
+
/**
|
119 |
+
* Get the received HTTP response if any.
|
120 |
+
*
|
121 |
+
* @return ResponseInterface|null
|
122 |
+
*/
|
123 |
+
public function getResponse()
|
124 |
+
{
|
125 |
+
return $this->response;
|
126 |
+
}
|
127 |
+
|
128 |
+
/**
|
129 |
+
* Get the result of the exception if available
|
130 |
+
*
|
131 |
+
* @return ResultInterface|null
|
132 |
+
*/
|
133 |
+
public function getResult()
|
134 |
+
{
|
135 |
+
return $this->result;
|
136 |
+
}
|
137 |
+
|
138 |
+
/**
|
139 |
+
* Returns true if this is a connection error.
|
140 |
+
*
|
141 |
+
* @return bool
|
142 |
+
*/
|
143 |
+
public function isConnectionError()
|
144 |
+
{
|
145 |
+
return $this->connectionError;
|
146 |
+
}
|
147 |
+
|
148 |
+
/**
|
149 |
+
* If available, gets the HTTP status code of the corresponding response
|
150 |
+
*
|
151 |
+
* @return int|null
|
152 |
+
*/
|
153 |
+
public function getStatusCode()
|
154 |
+
{
|
155 |
+
return $this->response ? $this->response->getStatusCode() : null;
|
156 |
+
}
|
157 |
+
|
158 |
+
/**
|
159 |
+
* Get the request ID of the error. This value is only present if a
|
160 |
+
* response was received and is not present in the event of a networking
|
161 |
+
* error.
|
162 |
+
*
|
163 |
+
* @return string|null Returns null if no response was received
|
164 |
+
*/
|
165 |
+
public function getAwsRequestId()
|
166 |
+
{
|
167 |
+
return $this->requestId;
|
168 |
+
}
|
169 |
+
|
170 |
+
/**
|
171 |
+
* Get the AWS error type.
|
172 |
+
*
|
173 |
+
* @return string|null Returns null if no response was received
|
174 |
+
*/
|
175 |
+
public function getAwsErrorType()
|
176 |
+
{
|
177 |
+
return $this->errorType;
|
178 |
+
}
|
179 |
+
|
180 |
+
/**
|
181 |
+
* Get the AWS error code.
|
182 |
+
*
|
183 |
+
* @return string|null Returns null if no response was received
|
184 |
+
*/
|
185 |
+
public function getAwsErrorCode()
|
186 |
+
{
|
187 |
+
return $this->errorCode;
|
188 |
+
}
|
189 |
+
|
190 |
+
/**
|
191 |
+
* Get all transfer information as an associative array if no $name
|
192 |
+
* argument is supplied, or gets a specific transfer statistic if
|
193 |
+
* a $name attribute is supplied (e.g., 'retries_attempted').
|
194 |
+
*
|
195 |
+
* @param string $name Name of the transfer stat to retrieve
|
196 |
+
*
|
197 |
+
* @return mixed|null|array
|
198 |
+
*/
|
199 |
+
public function getTransferInfo($name = null)
|
200 |
+
{
|
201 |
+
if (!$name) {
|
202 |
+
return $this->transferInfo;
|
203 |
+
}
|
204 |
+
|
205 |
+
return isset($this->transferInfo[$name])
|
206 |
+
? $this->transferInfo[$name]
|
207 |
+
: null;
|
208 |
+
}
|
209 |
+
|
210 |
+
/**
|
211 |
+
* Replace the transfer information associated with an exception.
|
212 |
+
*
|
213 |
+
* @param array $info
|
214 |
+
*/
|
215 |
+
public function setTransferInfo(array $info)
|
216 |
+
{
|
217 |
+
$this->transferInfo = $info;
|
218 |
+
}
|
219 |
+
|
220 |
+
/**
|
221 |
+
* Returns whether the max number of retries is exceeded.
|
222 |
+
*
|
223 |
+
* @return bool
|
224 |
+
*/
|
225 |
+
public function isMaxRetriesExceeded()
|
226 |
+
{
|
227 |
+
return $this->maxRetriesExceeded;
|
228 |
+
}
|
229 |
+
|
230 |
+
/**
|
231 |
+
* Sets the flag for max number of retries exceeded.
|
232 |
+
*/
|
233 |
+
public function setMaxRetriesExceeded()
|
234 |
+
{
|
235 |
+
$this->maxRetriesExceeded = true;
|
236 |
+
}
|
237 |
+
}
|
lib/Aws/Aws/Exception/CouldNotCreateChecksumException.php
ADDED
@@ -0,0 +1,25 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Exception;
|
3 |
+
|
4 |
+
use Aws\HasMonitoringEventsTrait;
|
5 |
+
use Aws\MonitoringEventsInterface;
|
6 |
+
|
7 |
+
class CouldNotCreateChecksumException extends \RuntimeException implements
|
8 |
+
MonitoringEventsInterface
|
9 |
+
{
|
10 |
+
use HasMonitoringEventsTrait;
|
11 |
+
|
12 |
+
public function __construct($algorithm, \Exception $previous = null)
|
13 |
+
{
|
14 |
+
$prefix = $algorithm === 'md5' ? "An" : "A";
|
15 |
+
parent::__construct("{$prefix} {$algorithm} checksum could not be "
|
16 |
+
. "calculated for the provided upload body, because it was not "
|
17 |
+
. "seekable. To prevent this error you can either 1) include the "
|
18 |
+
. "ContentMD5 or ContentSHA256 parameters with your request, 2) "
|
19 |
+
. "use a seekable stream for the body, or 3) wrap the non-seekable "
|
20 |
+
. "stream in a GuzzleHttp\\Psr7\\CachingStream object. You "
|
21 |
+
. "should be careful though and remember that the CachingStream "
|
22 |
+
. "utilizes PHP temp streams. This means that the stream will be "
|
23 |
+
. "temporarily stored on the local disk.", 0, $previous);
|
24 |
+
}
|
25 |
+
}
|
lib/Aws/Aws/Exception/CredentialsException.php
ADDED
@@ -0,0 +1,11 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Exception;
|
3 |
+
|
4 |
+
use Aws\HasMonitoringEventsTrait;
|
5 |
+
use Aws\MonitoringEventsInterface;
|
6 |
+
|
7 |
+
class CredentialsException extends \RuntimeException implements
|
8 |
+
MonitoringEventsInterface
|
9 |
+
{
|
10 |
+
use HasMonitoringEventsTrait;
|
11 |
+
}
|
lib/Aws/Aws/Exception/EventStreamDataException.php
ADDED
@@ -0,0 +1,38 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Exception;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Represents an exception that was supplied via an EventStream.
|
6 |
+
*/
|
7 |
+
class EventStreamDataException extends \RuntimeException
|
8 |
+
{
|
9 |
+
private $errorCode;
|
10 |
+
private $errorMessage;
|
11 |
+
|
12 |
+
public function __construct($code, $message)
|
13 |
+
{
|
14 |
+
$this->errorCode = $code;
|
15 |
+
$this->errorMessage = $message;
|
16 |
+
parent::__construct($message);
|
17 |
+
}
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Get the AWS error code.
|
21 |
+
*
|
22 |
+
* @return string|null Returns null if no response was received
|
23 |
+
*/
|
24 |
+
public function getAwsErrorCode()
|
25 |
+
{
|
26 |
+
return $this->errorCode;
|
27 |
+
}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Get the concise error message if any.
|
31 |
+
*
|
32 |
+
* @return string|null
|
33 |
+
*/
|
34 |
+
public function getAwsErrorMessage()
|
35 |
+
{
|
36 |
+
return $this->errorMessage;
|
37 |
+
}
|
38 |
+
}
|
lib/Aws/Aws/Exception/MultipartUploadException.php
ADDED
@@ -0,0 +1,63 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Exception;
|
3 |
+
|
4 |
+
use Aws\HasMonitoringEventsTrait;
|
5 |
+
use Aws\MonitoringEventsInterface;
|
6 |
+
use Aws\Multipart\UploadState;
|
7 |
+
|
8 |
+
class MultipartUploadException extends \RuntimeException implements
|
9 |
+
MonitoringEventsInterface
|
10 |
+
{
|
11 |
+
use HasMonitoringEventsTrait;
|
12 |
+
|
13 |
+
/** @var UploadState State of the erroneous transfer */
|
14 |
+
private $state;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @param UploadState $state Upload state at time of the exception.
|
18 |
+
* @param \Exception|array $prev Exception being thrown.
|
19 |
+
*/
|
20 |
+
public function __construct(UploadState $state, $prev = null) {
|
21 |
+
$msg = 'An exception occurred while performing a multipart upload';
|
22 |
+
|
23 |
+
if (is_array($prev)) {
|
24 |
+
$msg = strtr($msg, ['performing' => 'uploading parts to']);
|
25 |
+
$msg .= ". The following parts had errors:\n";
|
26 |
+
/** @var $error AwsException */
|
27 |
+
foreach ($prev as $part => $error) {
|
28 |
+
$msg .= "- Part {$part}: " . $error->getMessage(). "\n";
|
29 |
+
}
|
30 |
+
} elseif ($prev instanceof AwsException) {
|
31 |
+
switch ($prev->getCommand()->getName()) {
|
32 |
+
case 'CreateMultipartUpload':
|
33 |
+
case 'InitiateMultipartUpload':
|
34 |
+
$action = 'initiating';
|
35 |
+
break;
|
36 |
+
case 'CompleteMultipartUpload':
|
37 |
+
$action = 'completing';
|
38 |
+
break;
|
39 |
+
}
|
40 |
+
if (isset($action)) {
|
41 |
+
$msg = strtr($msg, ['performing' => $action]);
|
42 |
+
}
|
43 |
+
$msg .= ": {$prev->getMessage()}";
|
44 |
+
}
|
45 |
+
|
46 |
+
if (!$prev instanceof \Exception) {
|
47 |
+
$prev = null;
|
48 |
+
}
|
49 |
+
|
50 |
+
parent::__construct($msg, 0, $prev);
|
51 |
+
$this->state = $state;
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Get the state of the transfer
|
56 |
+
*
|
57 |
+
* @return UploadState
|
58 |
+
*/
|
59 |
+
public function getState()
|
60 |
+
{
|
61 |
+
return $this->state;
|
62 |
+
}
|
63 |
+
}
|
lib/Aws/Aws/Exception/UnresolvedApiException.php
ADDED
@@ -0,0 +1,11 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Exception;
|
3 |
+
|
4 |
+
use Aws\HasMonitoringEventsTrait;
|
5 |
+
use Aws\MonitoringEventsInterface;
|
6 |
+
|
7 |
+
class UnresolvedApiException extends \RuntimeException implements
|
8 |
+
MonitoringEventsInterface
|
9 |
+
{
|
10 |
+
use HasMonitoringEventsTrait;
|
11 |
+
}
|
lib/Aws/Aws/Exception/UnresolvedEndpointException.php
ADDED
@@ -0,0 +1,11 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Exception;
|
3 |
+
|
4 |
+
use Aws\HasMonitoringEventsTrait;
|
5 |
+
use Aws\MonitoringEventsInterface;
|
6 |
+
|
7 |
+
class UnresolvedEndpointException extends \RuntimeException implements
|
8 |
+
MonitoringEventsInterface
|
9 |
+
{
|
10 |
+
use HasMonitoringEventsTrait;
|
11 |
+
}
|
lib/Aws/Aws/Exception/UnresolvedSignatureException.php
ADDED
@@ -0,0 +1,11 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Exception;
|
3 |
+
|
4 |
+
use Aws\HasMonitoringEventsTrait;
|
5 |
+
use Aws\MonitoringEventsInterface;
|
6 |
+
|
7 |
+
class UnresolvedSignatureException extends \RuntimeException implements
|
8 |
+
MonitoringEventsInterface
|
9 |
+
{
|
10 |
+
use HasMonitoringEventsTrait;
|
11 |
+
}
|
lib/Aws/Aws/Handler/GuzzleV5/GuzzleHandler.php
ADDED
@@ -0,0 +1,210 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Handler\GuzzleV5;
|
3 |
+
|
4 |
+
use Exception;
|
5 |
+
use GuzzleHttp\Client;
|
6 |
+
use GuzzleHttp\ClientInterface;
|
7 |
+
use GuzzleHttp\Event\EndEvent;
|
8 |
+
use GuzzleHttp\Exception\ConnectException;
|
9 |
+
use GuzzleHttp\Exception\RequestException;
|
10 |
+
use GuzzleHttp\Message\ResponseInterface as GuzzleResponse;
|
11 |
+
use GuzzleHttp\Promise;
|
12 |
+
use GuzzleHttp\Psr7\Response as Psr7Response;
|
13 |
+
use GuzzleHttp\Stream\Stream;
|
14 |
+
use Psr\Http\Message\RequestInterface as Psr7Request;
|
15 |
+
use Psr\Http\Message\StreamInterface as Psr7StreamInterface;
|
16 |
+
|
17 |
+
/**
|
18 |
+
* A request handler that sends PSR-7-compatible requests with Guzzle 5.
|
19 |
+
*
|
20 |
+
* The handler accepts a PSR-7 Request object and an array of transfer options
|
21 |
+
* and returns a Guzzle 6 Promise. The promise is either resolved with a
|
22 |
+
* PSR-7 Response object or rejected with an array of error data.
|
23 |
+
*
|
24 |
+
* @codeCoverageIgnore
|
25 |
+
*/
|
26 |
+
class GuzzleHandler
|
27 |
+
{
|
28 |
+
private static $validOptions = [
|
29 |
+
'proxy' => true,
|
30 |
+
'expect' => true,
|
31 |
+
'verify' => true,
|
32 |
+
'timeout' => true,
|
33 |
+
'debug' => true,
|
34 |
+
'connect_timeout' => true,
|
35 |
+
'stream' => true,
|
36 |
+
'delay' => true,
|
37 |
+
'sink' => true,
|
38 |
+
];
|
39 |
+
|
40 |
+
/** @var ClientInterface */
|
41 |
+
private $client;
|
42 |
+
|
43 |
+
/**
|
44 |
+
* @param ClientInterface $client
|
45 |
+
*/
|
46 |
+
public function __construct(ClientInterface $client = null)
|
47 |
+
{
|
48 |
+
$this->client = $client ?: new Client();
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
* @param Psr7Request $request
|
53 |
+
* @param array $options
|
54 |
+
*
|
55 |
+
* @return Promise\Promise
|
56 |
+
*/
|
57 |
+
public function __invoke(Psr7Request $request, array $options = [])
|
58 |
+
{
|
59 |
+
// Create and send a Guzzle 5 request
|
60 |
+
$guzzlePromise = $this->client->send(
|
61 |
+
$this->createGuzzleRequest($request, $options)
|
62 |
+
);
|
63 |
+
|
64 |
+
$promise = new Promise\Promise(
|
65 |
+
function () use ($guzzlePromise) {
|
66 |
+
try {
|
67 |
+
$guzzlePromise->wait();
|
68 |
+
} catch (\Exception $e) {
|
69 |
+
// The promise is already delivered when the exception is
|
70 |
+
// thrown, so don't rethrow it.
|
71 |
+
}
|
72 |
+
},
|
73 |
+
[$guzzlePromise, 'cancel']
|
74 |
+
);
|
75 |
+
|
76 |
+
$guzzlePromise->then([$promise, 'resolve'], [$promise, 'reject']);
|
77 |
+
|
78 |
+
return $promise->then(
|
79 |
+
function (GuzzleResponse $response) {
|
80 |
+
// Adapt the Guzzle 5 Future to a Guzzle 6 ResponsePromise.
|
81 |
+
return $this->createPsr7Response($response);
|
82 |
+
},
|
83 |
+
function (Exception $exception) use ($options) {
|
84 |
+
// If we got a 'sink' that's a path, set the response body to
|
85 |
+
// the contents of the file. This will build the resulting
|
86 |
+
// exception with more information.
|
87 |
+
if ($exception instanceof RequestException) {
|
88 |
+
if (isset($options['sink'])) {
|
89 |
+
if (!($options['sink'] instanceof Psr7StreamInterface)) {
|
90 |
+
$exception->getResponse()->setBody(
|
91 |
+
Stream::factory(
|
92 |
+
file_get_contents($options['sink'])
|
93 |
+
)
|
94 |
+
);
|
95 |
+
}
|
96 |
+
}
|
97 |
+
}
|
98 |
+
// Reject with information about the error.
|
99 |
+
return new Promise\RejectedPromise($this->prepareErrorData($exception));
|
100 |
+
}
|
101 |
+
);
|
102 |
+
}
|
103 |
+
|
104 |
+
private function createGuzzleRequest(Psr7Request $psrRequest, array $options)
|
105 |
+
{
|
106 |
+
$ringConfig = [];
|
107 |
+
$statsCallback = isset($options['http_stats_receiver'])
|
108 |
+
? $options['http_stats_receiver']
|
109 |
+
: null;
|
110 |
+
unset($options['http_stats_receiver']);
|
111 |
+
|
112 |
+
// Remove unsupported options.
|
113 |
+
foreach (array_keys($options) as $key) {
|
114 |
+
if (!isset(self::$validOptions[$key])) {
|
115 |
+
unset($options[$key]);
|
116 |
+
}
|
117 |
+
}
|
118 |
+
|
119 |
+
// Handle delay option.
|
120 |
+
if (isset($options['delay'])) {
|
121 |
+
$ringConfig['delay'] = $options['delay'];
|
122 |
+
unset($options['delay']);
|
123 |
+
}
|
124 |
+
|
125 |
+
// Prepare sink option.
|
126 |
+
if (isset($options['sink'])) {
|
127 |
+
$ringConfig['save_to'] = ($options['sink'] instanceof Psr7StreamInterface)
|
128 |
+
? new GuzzleStream($options['sink'])
|
129 |
+
: $options['sink'];
|
130 |
+
unset($options['sink']);
|
131 |
+
}
|
132 |
+
|
133 |
+
// Ensure that all requests are async and lazy like Guzzle 6.
|
134 |
+
$options['future'] = 'lazy';
|
135 |
+
|
136 |
+
// Create the Guzzle 5 request from the provided PSR7 request.
|
137 |
+
$request = $this->client->createRequest(
|
138 |
+
$psrRequest->getMethod(),
|
139 |
+
$psrRequest->getUri(),
|
140 |
+
$options
|
141 |
+
);
|
142 |
+
|
143 |
+
if (is_callable($statsCallback)) {
|
144 |
+
$request->getEmitter()->on(
|
145 |
+
'end',
|
146 |
+
function (EndEvent $event) use ($statsCallback) {
|
147 |
+
$statsCallback($event->getTransferInfo());
|
148 |
+
}
|
149 |
+
);
|
150 |
+
}
|
151 |
+
|
152 |
+
// For the request body, adapt the PSR stream to a Guzzle stream.
|
153 |
+
$body = $psrRequest->getBody();
|
154 |
+
if ($body->getSize() === 0) {
|
155 |
+
$request->setBody(null);
|
156 |
+
} else {
|
157 |
+
$request->setBody(new GuzzleStream($body));
|
158 |
+
}
|
159 |
+
|
160 |
+
$request->setHeaders($psrRequest->getHeaders());
|
161 |
+
|
162 |
+
$request->setHeader(
|
163 |
+
'User-Agent',
|
164 |
+
$request->getHeader('User-Agent')
|
165 |
+
. ' ' . Client::getDefaultUserAgent()
|
166 |
+
);
|
167 |
+
|
168 |
+
// Make sure the delay is configured, if provided.
|
169 |
+
if ($ringConfig) {
|
170 |
+
foreach ($ringConfig as $k => $v) {
|
171 |
+
$request->getConfig()->set($k, $v);
|
172 |
+
}
|
173 |
+
}
|
174 |
+
|
175 |
+
return $request;
|
176 |
+
}
|
177 |
+
|
178 |
+
private function createPsr7Response(GuzzleResponse $response)
|
179 |
+
{
|
180 |
+
if ($body = $response->getBody()) {
|
181 |
+
$body = new PsrStream($body);
|
182 |
+
}
|
183 |
+
|
184 |
+
return new Psr7Response(
|
185 |
+
$response->getStatusCode(),
|
186 |
+
$response->getHeaders(),
|
187 |
+
$body,
|
188 |
+
$response->getReasonPhrase()
|
189 |
+
);
|
190 |
+
}
|
191 |
+
|
192 |
+
private function prepareErrorData(Exception $e)
|
193 |
+
{
|
194 |
+
$error = [
|
195 |
+
'exception' => $e,
|
196 |
+
'connection_error' => false,
|
197 |
+
'response' => null,
|
198 |
+
];
|
199 |
+
|
200 |
+
if ($e instanceof ConnectException) {
|
201 |
+
$error['connection_error'] = true;
|
202 |
+
}
|
203 |
+
|
204 |
+
if ($e instanceof RequestException && $e->getResponse()) {
|
205 |
+
$error['response'] = $this->createPsr7Response($e->getResponse());
|
206 |
+
}
|
207 |
+
|
208 |
+
return $error;
|
209 |
+
}
|
210 |
+
}
|
lib/Aws/Aws/Handler/GuzzleV5/GuzzleStream.php
ADDED
@@ -0,0 +1,24 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Handler\GuzzleV5;
|
3 |
+
|
4 |
+
use GuzzleHttp\Stream\StreamDecoratorTrait;
|
5 |
+
use GuzzleHttp\Stream\StreamInterface as GuzzleStreamInterface;
|
6 |
+
use Psr\Http\Message\StreamInterface as Psr7StreamInterface;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Adapts a PSR-7 Stream to a Guzzle 5 Stream.
|
10 |
+
*
|
11 |
+
* @codeCoverageIgnore
|
12 |
+
*/
|
13 |
+
class GuzzleStream implements GuzzleStreamInterface
|
14 |
+
{
|
15 |
+
use StreamDecoratorTrait;
|
16 |
+
|
17 |
+
/** @var Psr7StreamInterface */
|
18 |
+
private $stream;
|
19 |
+
|
20 |
+
public function __construct(Psr7StreamInterface $stream)
|
21 |
+
{
|
22 |
+
$this->stream = $stream;
|
23 |
+
}
|
24 |
+
}
|
lib/Aws/Aws/Handler/GuzzleV5/PsrStream.php
ADDED
@@ -0,0 +1,34 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Handler\GuzzleV5;
|
3 |
+
|
4 |
+
use GuzzleHttp\Stream\StreamDecoratorTrait;
|
5 |
+
use GuzzleHttp\Stream\StreamInterface as GuzzleStreamInterface;
|
6 |
+
use Psr\Http\Message\StreamInterface as Psr7StreamInterface;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Adapts a Guzzle 5 Stream to a PSR-7 Stream.
|
10 |
+
*
|
11 |
+
* @codeCoverageIgnore
|
12 |
+
*/
|
13 |
+
class PsrStream implements Psr7StreamInterface
|
14 |
+
{
|
15 |
+
use StreamDecoratorTrait;
|
16 |
+
|
17 |
+
/** @var GuzzleStreamInterface */
|
18 |
+
private $stream;
|
19 |
+
|
20 |
+
public function __construct(GuzzleStreamInterface $stream)
|
21 |
+
{
|
22 |
+
$this->stream = $stream;
|
23 |
+
}
|
24 |
+
|
25 |
+
public function rewind()
|
26 |
+
{
|
27 |
+
$this->stream->seek(0);
|
28 |
+
}
|
29 |
+
|
30 |
+
public function getContents()
|
31 |
+
{
|
32 |
+
return $this->stream->getContents();
|
33 |
+
}
|
34 |
+
}
|
lib/Aws/Aws/Handler/GuzzleV6/GuzzleHandler.php
ADDED
@@ -0,0 +1,85 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Handler\GuzzleV6;
|
3 |
+
|
4 |
+
use Exception;
|
5 |
+
use GuzzleHttp\Exception\ConnectException;
|
6 |
+
use GuzzleHttp\Exception\RequestException;
|
7 |
+
use GuzzleHttp\Promise;
|
8 |
+
use GuzzleHttp\Client;
|
9 |
+
use GuzzleHttp\ClientInterface;
|
10 |
+
use GuzzleHttp\TransferStats;
|
11 |
+
use Psr\Http\Message\RequestInterface as Psr7Request;
|
12 |
+
|
13 |
+
/**
|
14 |
+
* A request handler that sends PSR-7-compatible requests with Guzzle 6.
|
15 |
+
*/
|
16 |
+
class GuzzleHandler
|
17 |
+
{
|
18 |
+
/** @var ClientInterface */
|
19 |
+
private $client;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @param ClientInterface $client
|
23 |
+
*/
|
24 |
+
public function __construct(ClientInterface $client = null)
|
25 |
+
{
|
26 |
+
$this->client = $client ?: new Client();
|
27 |
+
}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* @param Psr7Request $request
|
31 |
+
* @param array $options
|
32 |
+
*
|
33 |
+
* @return Promise\Promise
|
34 |
+
*/
|
35 |
+
public function __invoke(Psr7Request $request, array $options = [])
|
36 |
+
{
|
37 |
+
$request = $request->withHeader(
|
38 |
+
'User-Agent',
|
39 |
+
$request->getHeaderLine('User-Agent')
|
40 |
+
. ' ' . \GuzzleHttp\default_user_agent()
|
41 |
+
);
|
42 |
+
|
43 |
+
return $this->client->sendAsync($request, $this->parseOptions($options))
|
44 |
+
->otherwise(
|
45 |
+
static function (\Exception $e) {
|
46 |
+
$error = [
|
47 |
+
'exception' => $e,
|
48 |
+
'connection_error' => $e instanceof ConnectException,
|
49 |
+
'response' => null,
|
50 |
+
];
|
51 |
+
|
52 |
+
if ($e instanceof RequestException && $e->getResponse()) {
|
53 |
+
$error['response'] = $e->getResponse();
|
54 |
+
}
|
55 |
+
|
56 |
+
return new Promise\RejectedPromise($error);
|
57 |
+
}
|
58 |
+
);
|
59 |
+
}
|
60 |
+
|
61 |
+
private function parseOptions(array $options)
|
62 |
+
{
|
63 |
+
if (isset($options['http_stats_receiver'])) {
|
64 |
+
$fn = $options['http_stats_receiver'];
|
65 |
+
unset($options['http_stats_receiver']);
|
66 |
+
|
67 |
+
$prev = isset($options['on_stats'])
|
68 |
+
? $options['on_stats']
|
69 |
+
: null;
|
70 |
+
|
71 |
+
$options['on_stats'] = static function (
|
72 |
+
TransferStats $stats
|
73 |
+
) use ($fn, $prev) {
|
74 |
+
if (is_callable($prev)) {
|
75 |
+
$prev($stats);
|
76 |
+
}
|
77 |
+
$transferStats = ['total_time' => $stats->getTransferTime()];
|
78 |
+
$transferStats += $stats->getHandlerStats();
|
79 |
+
$fn($transferStats);
|
80 |
+
};
|
81 |
+
}
|
82 |
+
|
83 |
+
return $options;
|
84 |
+
}
|
85 |
+
}
|
lib/Aws/Aws/HandlerList.php
ADDED
@@ -0,0 +1,451 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Builds a single handler function from zero or more middleware functions and
|
6 |
+
* a handler. The handler function is then used to send command objects and
|
7 |
+
* return a promise that is resolved with an AWS result object.
|
8 |
+
*
|
9 |
+
* The "front" of the list is invoked before the "end" of the list. You can add
|
10 |
+
* middleware to the front of the list using one of the "prepend" method, and
|
11 |
+
* the end of the list using one of the "append" method. The last function
|
12 |
+
* invoked in a handler list is the handler (a function that does not accept a
|
13 |
+
* next handler but rather is responsible for returning a promise that is
|
14 |
+
* fulfilled with an Aws\ResultInterface object).
|
15 |
+
*
|
16 |
+
* Handlers are ordered using a "step" that describes the step at which the
|
17 |
+
* SDK is when sending a command. The available steps are:
|
18 |
+
*
|
19 |
+
* - init: The command is being initialized, allowing you to do things like add
|
20 |
+
* default options.
|
21 |
+
* - validate: The command is being validated before it is serialized
|
22 |
+
* - build: The command is being serialized into an HTTP request. A middleware
|
23 |
+
* in this step MUST serialize an HTTP request and populate the "@request"
|
24 |
+
* parameter of a command with the request such that it is available to
|
25 |
+
* subsequent middleware.
|
26 |
+
* - sign: The request is being signed and prepared to be sent over the wire.
|
27 |
+
*
|
28 |
+
* Middleware can be registered with a name to allow you to easily add a
|
29 |
+
* middleware before or after another middleware by name. This also allows you
|
30 |
+
* to remove a middleware by name (in addition to removing by instance).
|
31 |
+
*/
|
32 |
+
class HandlerList implements \Countable
|
33 |
+
{
|
34 |
+
const INIT = 'init';
|
35 |
+
const VALIDATE = 'validate';
|
36 |
+
const BUILD = 'build';
|
37 |
+
const SIGN = 'sign';
|
38 |
+
const ATTEMPT = 'attempt';
|
39 |
+
|
40 |
+
/** @var callable */
|
41 |
+
private $handler;
|
42 |
+
|
43 |
+
/** @var array */
|
44 |
+
private $named = [];
|
45 |
+
|
46 |
+
/** @var array */
|
47 |
+
private $sorted;
|
48 |
+
|
49 |
+
/** @var callable|null */
|
50 |
+
private $interposeFn;
|
51 |
+
|
52 |
+
/** @var array Steps (in reverse order) */
|
53 |
+
private $steps = [
|
54 |
+
self::ATTEMPT => [],
|
55 |
+
self::SIGN => [],
|
56 |
+
self::BUILD => [],
|
57 |
+
self::VALIDATE => [],
|
58 |
+
self::INIT => [],
|
59 |
+
];
|
60 |
+
|
61 |
+
/**
|
62 |
+
* @param callable $handler HTTP handler.
|
63 |
+
*/
|
64 |
+
public function __construct(callable $handler = null)
|
65 |
+
{
|
66 |
+
$this->handler = $handler;
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
* Dumps a string representation of the list.
|
71 |
+
*
|
72 |
+
* @return string
|
73 |
+
*/
|
74 |
+
public function __toString()
|
75 |
+
{
|
76 |
+
$str = '';
|
77 |
+
$i = 0;
|
78 |
+
|
79 |
+
foreach (array_reverse($this->steps) as $k => $step) {
|
80 |
+
foreach (array_reverse($step) as $j => $tuple) {
|
81 |
+
$str .= "{$i}) Step: {$k}, ";
|
82 |
+
if ($tuple[1]) {
|
83 |
+
$str .= "Name: {$tuple[1]}, ";
|
84 |
+
}
|
85 |
+
$str .= "Function: " . $this->debugCallable($tuple[0]) . "\n";
|
86 |
+
$i++;
|
87 |
+
}
|
88 |
+
}
|
89 |
+
|
90 |
+
if ($this->handler) {
|
91 |
+
$str .= "{$i}) Handler: " . $this->debugCallable($this->handler) . "\n";
|
92 |
+
}
|
93 |
+
|
94 |
+
return $str;
|
95 |
+
}
|
96 |
+
|
97 |
+
/**
|
98 |
+
* Set the HTTP handler that actually returns a response.
|
99 |
+
*
|
100 |
+
* @param callable $handler Function that accepts a request and array of
|
101 |
+
* options and returns a Promise.
|
102 |
+
*/
|
103 |
+
public function setHandler(callable $handler)
|
104 |
+
{
|
105 |
+
$this->handler = $handler;
|
106 |
+
}
|
107 |
+
|
108 |
+
/**
|
109 |
+
* Returns true if the builder has a handler.
|
110 |
+
*
|
111 |
+
* @return bool
|
112 |
+
*/
|
113 |
+
public function hasHandler()
|
114 |
+
{
|
115 |
+
return (bool) $this->handler;
|
116 |
+
}
|
117 |
+
|
118 |
+
/**
|
119 |
+
* Append a middleware to the init step.
|
120 |
+
*
|
121 |
+
* @param callable $middleware Middleware function to add.
|
122 |
+
* @param string $name Name of the middleware.
|
123 |
+
*/
|
124 |
+
public function appendInit(callable $middleware, $name = null)
|
125 |
+
{
|
126 |
+
$this->add(self::INIT, $name, $middleware);
|
127 |
+
}
|
128 |
+
|
129 |
+
/**
|
130 |
+
* Prepend a middleware to the init step.
|
131 |
+
*
|
132 |
+
* @param callable $middleware Middleware function to add.
|
133 |
+
* @param string $name Name of the middleware.
|
134 |
+
*/
|
135 |
+
public function prependInit(callable $middleware, $name = null)
|
136 |
+
{
|
137 |
+
$this->add(self::INIT, $name, $middleware, true);
|
138 |
+
}
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Append a middleware to the validate step.
|
142 |
+
*
|
143 |
+
* @param callable $middleware Middleware function to add.
|
144 |
+
* @param string $name Name of the middleware.
|
145 |
+
*/
|
146 |
+
public function appendValidate(callable $middleware, $name = null)
|
147 |
+
{
|
148 |
+
$this->add(self::VALIDATE, $name, $middleware);
|
149 |
+
}
|
150 |
+
|
151 |
+
/**
|
152 |
+
* Prepend a middleware to the validate step.
|
153 |
+
*
|
154 |
+
* @param callable $middleware Middleware function to add.
|
155 |
+
* @param string $name Name of the middleware.
|
156 |
+
*/
|
157 |
+
public function prependValidate(callable $middleware, $name = null)
|
158 |
+
{
|
159 |
+
$this->add(self::VALIDATE, $name, $middleware, true);
|
160 |
+
}
|
161 |
+
|
162 |
+
/**
|
163 |
+
* Append a middleware to the build step.
|
164 |
+
*
|
165 |
+
* @param callable $middleware Middleware function to add.
|
166 |
+
* @param string $name Name of the middleware.
|
167 |
+
*/
|
168 |
+
public function appendBuild(callable $middleware, $name = null)
|
169 |
+
{
|
170 |
+
$this->add(self::BUILD, $name, $middleware);
|
171 |
+
}
|
172 |
+
|
173 |
+
/**
|
174 |
+
* Prepend a middleware to the build step.
|
175 |
+
*
|
176 |
+
* @param callable $middleware Middleware function to add.
|
177 |
+
* @param string $name Name of the middleware.
|
178 |
+
*/
|
179 |
+
public function prependBuild(callable $middleware, $name = null)
|
180 |
+
{
|
181 |
+
$this->add(self::BUILD, $name, $middleware, true);
|
182 |
+
}
|
183 |
+
|
184 |
+
/**
|
185 |
+
* Append a middleware to the sign step.
|
186 |
+
*
|
187 |
+
* @param callable $middleware Middleware function to add.
|
188 |
+
* @param string $name Name of the middleware.
|
189 |
+
*/
|
190 |
+
public function appendSign(callable $middleware, $name = null)
|
191 |
+
{
|
192 |
+
$this->add(self::SIGN, $name, $middleware);
|
193 |
+
}
|
194 |
+
|
195 |
+
/**
|
196 |
+
* Prepend a middleware to the sign step.
|
197 |
+
*
|
198 |
+
* @param callable $middleware Middleware function to add.
|
199 |
+
* @param string $name Name of the middleware.
|
200 |
+
*/
|
201 |
+
public function prependSign(callable $middleware, $name = null)
|
202 |
+
{
|
203 |
+
$this->add(self::SIGN, $name, $middleware, true);
|
204 |
+
}
|
205 |
+
|
206 |
+
/**
|
207 |
+
* Append a middleware to the attempt step.
|
208 |
+
*
|
209 |
+
* @param callable $middleware Middleware function to add.
|
210 |
+
* @param string $name Name of the middleware.
|
211 |
+
*/
|
212 |
+
public function appendAttempt(callable $middleware, $name = null)
|
213 |
+
{
|
214 |
+
$this->add(self::ATTEMPT, $name, $middleware);
|
215 |
+
}
|
216 |
+
|
217 |
+
/**
|
218 |
+
* Prepend a middleware to the attempt step.
|
219 |
+
*
|
220 |
+
* @param callable $middleware Middleware function to add.
|
221 |
+
* @param string $name Name of the middleware.
|
222 |
+
*/
|
223 |
+
public function prependAttempt(callable $middleware, $name = null)
|
224 |
+
{
|
225 |
+
$this->add(self::ATTEMPT, $name, $middleware, true);
|
226 |
+
}
|
227 |
+
|
228 |
+
/**
|
229 |
+
* Add a middleware before the given middleware by name.
|
230 |
+
*
|
231 |
+
* @param string|callable $findName Add before this
|
232 |
+
* @param string $withName Optional name to give the middleware
|
233 |
+
* @param callable $middleware Middleware to add.
|
234 |
+
*/
|
235 |
+
public function before($findName, $withName, callable $middleware)
|
236 |
+
{
|
237 |
+
$this->splice($findName, $withName, $middleware, true);
|
238 |
+
}
|
239 |
+
|
240 |
+
/**
|
241 |
+
* Add a middleware after the given middleware by name.
|
242 |
+
*
|
243 |
+
* @param string|callable $findName Add after this
|
244 |
+
* @param string $withName Optional name to give the middleware
|
245 |
+
* @param callable $middleware Middleware to add.
|
246 |
+
*/
|
247 |
+
public function after($findName, $withName, callable $middleware)
|
248 |
+
{
|
249 |
+
$this->splice($findName, $withName, $middleware, false);
|
250 |
+
}
|
251 |
+
|
252 |
+
/**
|
253 |
+
* Remove a middleware by name or by instance from the list.
|
254 |
+
*
|
255 |
+
* @param string|callable $nameOrInstance Middleware to remove.
|
256 |
+
*/
|
257 |
+
public function remove($nameOrInstance)
|
258 |
+
{
|
259 |
+
if (is_callable($nameOrInstance)) {
|
260 |
+
$this->removeByInstance($nameOrInstance);
|
261 |
+
} elseif (is_string($nameOrInstance)) {
|
262 |
+
$this->removeByName($nameOrInstance);
|
263 |
+
}
|
264 |
+
}
|
265 |
+
|
266 |
+
/**
|
267 |
+
* Interpose a function between each middleware (e.g., allowing for a trace
|
268 |
+
* through the middleware layers).
|
269 |
+
*
|
270 |
+
* The interpose function is a function that accepts a "step" argument as a
|
271 |
+
* string and a "name" argument string. This function must then return a
|
272 |
+
* function that accepts the next handler in the list. This function must
|
273 |
+
* then return a function that accepts a CommandInterface and optional
|
274 |
+
* RequestInterface and returns a promise that is fulfilled with an
|
275 |
+
* Aws\ResultInterface or rejected with an Aws\Exception\AwsException
|
276 |
+
* object.
|
277 |
+
*
|
278 |
+
* @param callable|null $fn Pass null to remove any previously set function
|
279 |
+
*/
|
280 |
+
public function interpose(callable $fn = null)
|
281 |
+
{
|
282 |
+
$this->sorted = null;
|
283 |
+
$this->interposeFn = $fn;
|
284 |
+
}
|
285 |
+
|
286 |
+
/**
|
287 |
+
* Compose the middleware and handler into a single callable function.
|
288 |
+
*
|
289 |
+
* @return callable
|
290 |
+
*/
|
291 |
+
public function resolve()
|
292 |
+
{
|
293 |
+
if (!($prev = $this->handler)) {
|
294 |
+
throw new \LogicException('No handler has been specified');
|
295 |
+
}
|
296 |
+
|
297 |
+
if ($this->sorted === null) {
|
298 |
+
$this->sortMiddleware();
|
299 |
+
}
|
300 |
+
|
301 |
+
foreach ($this->sorted as $fn) {
|
302 |
+
$prev = $fn($prev);
|
303 |
+
}
|
304 |
+
|
305 |
+
return $prev;
|
306 |
+
}
|
307 |
+
|
308 |
+
public function count()
|
309 |
+
{
|
310 |
+
return count($this->steps[self::INIT])
|
311 |
+
+ count($this->steps[self::VALIDATE])
|
312 |
+
+ count($this->steps[self::BUILD])
|
313 |
+
+ count($this->steps[self::SIGN])
|
314 |
+
+ count($this->steps[self::ATTEMPT]);
|
315 |
+
}
|
316 |
+
|
317 |
+
/**
|
318 |
+
* Splices a function into the middleware list at a specific position.
|
319 |
+
*
|
320 |
+
* @param $findName
|
321 |
+
* @param $withName
|
322 |
+
* @param callable $middleware
|
323 |
+
* @param $before
|
324 |
+
*/
|
325 |
+
private function splice($findName, $withName, callable $middleware, $before)
|
326 |
+
{
|
327 |
+
if (!isset($this->named[$findName])) {
|
328 |
+
throw new \InvalidArgumentException("$findName not found");
|
329 |
+
}
|
330 |
+
|
331 |
+
$idx = $this->sorted = null;
|
332 |
+
$step = $this->named[$findName];
|
333 |
+
|
334 |
+
if ($withName) {
|
335 |
+
$this->named[$withName] = $step;
|
336 |
+
}
|
337 |
+
|
338 |
+
foreach ($this->steps[$step] as $i => $tuple) {
|
339 |
+
if ($tuple[1] === $findName) {
|
340 |
+
$idx = $i;
|
341 |
+
break;
|
342 |
+
}
|
343 |
+
}
|
344 |
+
|
345 |
+
$replacement = $before
|
346 |
+
? [$this->steps[$step][$idx], [$middleware, $withName]]
|
347 |
+
: [[$middleware, $withName], $this->steps[$step][$idx]];
|
348 |
+
array_splice($this->steps[$step], $idx, 1, $replacement);
|
349 |
+
}
|
350 |
+
|
351 |
+
/**
|
352 |
+
* Provides a debug string for a given callable.
|
353 |
+
*
|
354 |
+
* @param array|callable $fn Function to write as a string.
|
355 |
+
*
|
356 |
+
* @return string
|
357 |
+
*/
|
358 |
+
private function debugCallable($fn)
|
359 |
+
{
|
360 |
+
if (is_string($fn)) {
|
361 |
+
return "callable({$fn})";
|
362 |
+
}
|
363 |
+
|
364 |
+
if (is_array($fn)) {
|
365 |
+
$ele = is_string($fn[0]) ? $fn[0] : get_class($fn[0]);
|
366 |
+
return "callable(['{$ele}', '{$fn[1]}'])";
|
367 |
+
}
|
368 |
+
|
369 |
+
return 'callable(' . spl_object_hash($fn) . ')';
|
370 |
+
}
|
371 |
+
|
372 |
+
/**
|
373 |
+
* Sort the middleware, and interpose if needed in the sorted list.
|
374 |
+
*/
|
375 |
+
private function sortMiddleware()
|
376 |
+
{
|
377 |
+
$this->sorted = [];
|
378 |
+
|
379 |
+
if (!$this->interposeFn) {
|
380 |
+
foreach ($this->steps as $step) {
|
381 |
+
foreach ($step as $fn) {
|
382 |
+
$this->sorted[] = $fn[0];
|
383 |
+
}
|
384 |
+
}
|
385 |
+
return;
|
386 |
+
}
|
387 |
+
|
388 |
+
$ifn = $this->interposeFn;
|
389 |
+
// Interpose the interposeFn into the handler stack.
|
390 |
+
foreach ($this->steps as $stepName => $step) {
|
391 |
+
foreach ($step as $fn) {
|
392 |
+
$this->sorted[] = $ifn($stepName, $fn[1]);
|
393 |
+
$this->sorted[] = $fn[0];
|
394 |
+
}
|
395 |
+
}
|
396 |
+
}
|
397 |
+
|
398 |
+
private function removeByName($name)
|
399 |
+
{
|
400 |
+
if (!isset($this->named[$name])) {
|
401 |
+
return;
|
402 |
+
}
|
403 |
+
|
404 |
+
$this->sorted = null;
|
405 |
+
$step = $this->named[$name];
|
406 |
+
$this->steps[$step] = array_values(
|
407 |
+
array_filter(
|
408 |
+
$this->steps[$step],
|
409 |
+
function ($tuple) use ($name) {
|
410 |
+
return $tuple[1] !== $name;
|
411 |
+
}
|
412 |
+
)
|
413 |
+
);
|
414 |
+
}
|
415 |
+
|
416 |
+
private function removeByInstance(callable $fn)
|
417 |
+
{
|
418 |
+
foreach ($this->steps as $k => $step) {
|
419 |
+
foreach ($step as $j => $tuple) {
|
420 |
+
if ($tuple[0] === $fn) {
|
421 |
+
$this->sorted = null;
|
422 |
+
unset($this->named[$this->steps[$k][$j][1]]);
|
423 |
+
unset($this->steps[$k][$j]);
|
424 |
+
}
|
425 |
+
}
|
426 |
+
}
|
427 |
+
}
|
428 |
+
|
429 |
+
/**
|
430 |
+
* Add a middleware to a step.
|
431 |
+
*
|
432 |
+
* @param string $step Middleware step.
|
433 |
+
* @param string $name Middleware name.
|
434 |
+
* @param callable $middleware Middleware function to add.
|
435 |
+
* @param bool $prepend Prepend instead of append.
|
436 |
+
*/
|
437 |
+
private function add($step, $name, callable $middleware, $prepend = false)
|
438 |
+
{
|
439 |
+
$this->sorted = null;
|
440 |
+
|
441 |
+
if ($prepend) {
|
442 |
+
$this->steps[$step][] = [$middleware, $name];
|
443 |
+
} else {
|
444 |
+
array_unshift($this->steps[$step], [$middleware, $name]);
|
445 |
+
}
|
446 |
+
|
447 |
+
if ($name) {
|
448 |
+
$this->named[$name] = $step;
|
449 |
+
}
|
450 |
+
}
|
451 |
+
}
|
lib/Aws/Aws/HasDataTrait.php
ADDED
@@ -0,0 +1,60 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Trait implementing ToArrayInterface, \ArrayAccess, \Countable, and
|
6 |
+
* \IteratorAggregate
|
7 |
+
*/
|
8 |
+
trait HasDataTrait
|
9 |
+
{
|
10 |
+
/** @var array */
|
11 |
+
private $data = [];
|
12 |
+
|
13 |
+
public function getIterator()
|
14 |
+
{
|
15 |
+
return new \ArrayIterator($this->data);
|
16 |
+
}
|
17 |
+
|
18 |
+
/**
|
19 |
+
* This method returns a reference to the variable to allow for indirect
|
20 |
+
* array modification (e.g., $foo['bar']['baz'] = 'qux').
|
21 |
+
*
|
22 |
+
* @param $offset
|
23 |
+
*
|
24 |
+
* @return mixed|null
|
25 |
+
*/
|
26 |
+
public function & offsetGet($offset)
|
27 |
+
{
|
28 |
+
if (isset($this->data[$offset])) {
|
29 |
+
return $this->data[$offset];
|
30 |
+
}
|
31 |
+
|
32 |
+
$value = null;
|
33 |
+
return $value;
|
34 |
+
}
|
35 |
+
|
36 |
+
public function offsetSet($offset, $value)
|
37 |
+
{
|
38 |
+
$this->data[$offset] = $value;
|
39 |
+
}
|
40 |
+
|
41 |
+
public function offsetExists($offset)
|
42 |
+
{
|
43 |
+
return isset($this->data[$offset]);
|
44 |
+
}
|
45 |
+
|
46 |
+
public function offsetUnset($offset)
|
47 |
+
{
|
48 |
+
unset($this->data[$offset]);
|
49 |
+
}
|
50 |
+
|
51 |
+
public function toArray()
|
52 |
+
{
|
53 |
+
return $this->data;
|
54 |
+
}
|
55 |
+
|
56 |
+
public function count()
|
57 |
+
{
|
58 |
+
return count($this->data);
|
59 |
+
}
|
60 |
+
}
|
lib/Aws/Aws/HasMonitoringEventsTrait.php
ADDED
@@ -0,0 +1,39 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws;
|
3 |
+
|
4 |
+
|
5 |
+
trait HasMonitoringEventsTrait
|
6 |
+
{
|
7 |
+
private $monitoringEvents = [];
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Get client-side monitoring events attached to this object. Each event is
|
11 |
+
* represented as an associative array within the returned array.
|
12 |
+
*
|
13 |
+
* @return array
|
14 |
+
*/
|
15 |
+
public function getMonitoringEvents()
|
16 |
+
{
|
17 |
+
return $this->monitoringEvents;
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Prepend a client-side monitoring event to this object's event list
|
22 |
+
*
|
23 |
+
* @param array $event
|
24 |
+
*/
|
25 |
+
public function prependMonitoringEvent(array $event)
|
26 |
+
{
|
27 |
+
array_unshift($this->monitoringEvents, $event);
|
28 |
+
}
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Append a client-side monitoring event to this object's event list
|
32 |
+
*
|
33 |
+
* @param array $event
|
34 |
+
*/
|
35 |
+
public function appendMonitoringEvent(array $event)
|
36 |
+
{
|
37 |
+
$this->monitoringEvents []= $event;
|
38 |
+
}
|
39 |
+
}
|
lib/Aws/Aws/HashInterface.php
ADDED
@@ -0,0 +1,27 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Interface that allows implementing various incremental hashes.
|
6 |
+
*/
|
7 |
+
interface HashInterface
|
8 |
+
{
|
9 |
+
/**
|
10 |
+
* Adds data to the hash.
|
11 |
+
*
|
12 |
+
* @param string $data Data to add to the hash
|
13 |
+
*/
|
14 |
+
public function update($data);
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Finalizes the incremental hash and returns the resulting digest.
|
18 |
+
*
|
19 |
+
* @return string
|
20 |
+
*/
|
21 |
+
public function complete();
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Removes all data from the hash, effectively starting a new hash.
|
25 |
+
*/
|
26 |
+
public function reset();
|
27 |
+
}
|
lib/Aws/Aws/HashingStream.php
ADDED
@@ -0,0 +1,60 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws;
|
3 |
+
|
4 |
+
use GuzzleHttp\Psr7\StreamDecoratorTrait;
|
5 |
+
use Psr\Http\Message\StreamInterface;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Stream decorator that calculates a rolling hash of the stream as it is read.
|
9 |
+
*/
|
10 |
+
class HashingStream implements StreamInterface
|
11 |
+
{
|
12 |
+
use StreamDecoratorTrait;
|
13 |
+
|
14 |
+
/** @var HashInterface */
|
15 |
+
private $hash;
|
16 |
+
|
17 |
+
/** @var callable|null */
|
18 |
+
private $callback;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* @param StreamInterface $stream Stream that is being read.
|
22 |
+
* @param HashInterface $hash Hash used to calculate checksum.
|
23 |
+
* @param callable $onComplete Optional function invoked when the
|
24 |
+
* hash calculation is completed.
|
25 |
+
*/
|
26 |
+
public function __construct(
|
27 |
+
StreamInterface $stream,
|
28 |
+
HashInterface $hash,
|
29 |
+
callable $onComplete = null
|
30 |
+
) {
|
31 |
+
$this->stream = $stream;
|
32 |
+
$this->hash = $hash;
|
33 |
+
$this->callback = $onComplete;
|
34 |
+
}
|
35 |
+
|
36 |
+
public function read($length)
|
37 |
+
{
|
38 |
+
$data = $this->stream->read($length);
|
39 |
+
$this->hash->update($data);
|
40 |
+
if ($this->eof()) {
|
41 |
+
$result = $this->hash->complete();
|
42 |
+
if ($this->callback) {
|
43 |
+
call_user_func($this->callback, $result);
|
44 |
+
}
|
45 |
+
}
|
46 |
+
|
47 |
+
return $data;
|
48 |
+
}
|
49 |
+
|
50 |
+
public function seek($offset, $whence = SEEK_SET)
|
51 |
+
{
|
52 |
+
if ($offset === 0) {
|
53 |
+
$this->hash->reset();
|
54 |
+
return $this->stream->seek($offset);
|
55 |
+
}
|
56 |
+
|
57 |
+
// Seeking arbitrarily is not supported.
|
58 |
+
return false;
|
59 |
+
}
|
60 |
+
}
|
lib/Aws/Aws/History.php
ADDED
@@ -0,0 +1,156 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws;
|
3 |
+
|
4 |
+
use Psr\Http\Message\RequestInterface;
|
5 |
+
use Aws\Exception\AwsException;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Represents a history container that is required when using the history
|
9 |
+
* middleware.
|
10 |
+
*/
|
11 |
+
class History implements \Countable, \IteratorAggregate
|
12 |
+
{
|
13 |
+
private $maxEntries;
|
14 |
+
private $entries = array();
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @param int $maxEntries Maximum number of entries to store.
|
18 |
+
*/
|
19 |
+
public function __construct($maxEntries = 10)
|
20 |
+
{
|
21 |
+
$this->maxEntries = $maxEntries;
|
22 |
+
}
|
23 |
+
|
24 |
+
public function count()
|
25 |
+
{
|
26 |
+
return count($this->entries);
|
27 |
+
}
|
28 |
+
|
29 |
+
public function getIterator()
|
30 |
+
{
|
31 |
+
return new \ArrayIterator(array_values($this->entries));
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Get the last finished command seen by the history container.
|
36 |
+
*
|
37 |
+
* @return CommandInterface
|
38 |
+
* @throws \LogicException if no commands have been seen.
|
39 |
+
*/
|
40 |
+
public function getLastCommand()
|
41 |
+
{
|
42 |
+
if (!$this->entries) {
|
43 |
+
throw new \LogicException('No commands received');
|
44 |
+
}
|
45 |
+
|
46 |
+
return end($this->entries)['command'];
|
47 |
+
}
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Get the last finished request seen by the history container.
|
51 |
+
*
|
52 |
+
* @return RequestInterface
|
53 |
+
* @throws \LogicException if no requests have been seen.
|
54 |
+
*/
|
55 |
+
public function getLastRequest()
|
56 |
+
{
|
57 |
+
if (!$this->entries) {
|
58 |
+
throw new \LogicException('No requests received');
|
59 |
+
}
|
60 |
+
|
61 |
+
return end($this->entries)['request'];
|
62 |
+
}
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Get the last received result or exception.
|
66 |
+
*
|
67 |
+
* @return ResultInterface|AwsException
|
68 |
+
* @throws \LogicException if no return values have been received.
|
69 |
+
*/
|
70 |
+
public function getLastReturn()
|
71 |
+
{
|
72 |
+
if (!$this->entries) {
|
73 |
+
throw new \LogicException('No entries');
|
74 |
+
}
|
75 |
+
|
76 |
+
$last = end($this->entries);
|
77 |
+
|
78 |
+
if (isset($last['result'])) {
|
79 |
+
return $last['result'];
|
80 |
+
}
|
81 |
+
|
82 |
+
if (isset($last['exception'])) {
|
83 |
+
return $last['exception'];
|
84 |
+
}
|
85 |
+
|
86 |
+
throw new \LogicException('No return value for last entry.');
|
87 |
+
}
|
88 |
+
|
89 |
+
/**
|
90 |
+
* Initiate an entry being added to the history.
|
91 |
+
*
|
92 |
+
* @param CommandInterface $cmd Command be executed.
|
93 |
+
* @param RequestInterface $req Request being sent.
|
94 |
+
*
|
95 |
+
* @return string Returns the ticket used to finish the entry.
|
96 |
+
*/
|
97 |
+
public function start(CommandInterface $cmd, RequestInterface $req)
|
98 |
+
{
|
99 |
+
$ticket = uniqid();
|
100 |
+
$this->entries[$ticket] = [
|
101 |
+
'command' => $cmd,
|
102 |
+
'request' => $req,
|
103 |
+
'result' => null,
|
104 |
+
'exception' => null,
|
105 |
+
];
|
106 |
+
|
107 |
+
return $ticket;
|
108 |
+
}
|
109 |
+
|
110 |
+
/**
|
111 |
+
* Finish adding an entry to the history container.
|
112 |
+
*
|
113 |
+
* @param string $ticket Ticket returned from the start call.
|
114 |
+
* @param mixed $result The result (an exception or AwsResult).
|
115 |
+
*/
|
116 |
+
public function finish($ticket, $result)
|
117 |
+
{
|
118 |
+
if (!isset($this->entries[$ticket])) {
|
119 |
+
throw new \InvalidArgumentException('Invalid history ticket');
|
120 |
+
}
|
121 |
+
|
122 |
+
if (isset($this->entries[$ticket]['result'])
|
123 |
+
|| isset($this->entries[$ticket]['exception'])
|
124 |
+
) {
|
125 |
+
throw new \LogicException('History entry is already finished');
|
126 |
+
}
|
127 |
+
|
128 |
+
if ($result instanceof \Exception) {
|
129 |
+
$this->entries[$ticket]['exception'] = $result;
|
130 |
+
} else {
|
131 |
+
$this->entries[$ticket]['result'] = $result;
|
132 |
+
}
|
133 |
+
|
134 |
+
if (count($this->entries) >= $this->maxEntries) {
|
135 |
+
$this->entries = array_slice($this->entries, -$this->maxEntries, null, true);
|
136 |
+
}
|
137 |
+
}
|
138 |
+
|
139 |
+
/**
|
140 |
+
* Flush the history
|
141 |
+
*/
|
142 |
+
public function clear()
|
143 |
+
{
|
144 |
+
$this->entries = [];
|
145 |
+
}
|
146 |
+
|
147 |
+
/**
|
148 |
+
* Converts the history to an array.
|
149 |
+
*
|
150 |
+
* @return array
|
151 |
+
*/
|
152 |
+
public function toArray()
|
153 |
+
{
|
154 |
+
return array_values($this->entries);
|
155 |
+
}
|
156 |
+
}
|
lib/Aws/Aws/IdempotencyTokenMiddleware.php
ADDED
@@ -0,0 +1,118 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws;
|
3 |
+
|
4 |
+
use Aws\Api\Service;
|
5 |
+
use Psr\Http\Message\RequestInterface;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* @internal Middleware that auto fills parameters with `idempotencyToken` trait
|
9 |
+
*/
|
10 |
+
class IdempotencyTokenMiddleware
|
11 |
+
{
|
12 |
+
/** @var Service */
|
13 |
+
private $service;
|
14 |
+
/** @var string */
|
15 |
+
private $bytesGenerator;
|
16 |
+
/** @var callable */
|
17 |
+
private $nextHandler;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Creates a middleware that populates operation parameter
|
21 |
+
* with trait 'idempotencyToken' enabled with a random UUIDv4
|
22 |
+
*
|
23 |
+
* One of following functions needs to be available
|
24 |
+
* in order to generate random bytes used for UUID
|
25 |
+
* (SDK will attempt to utilize function in following order):
|
26 |
+
* - random_bytes (requires PHP 7.0 or above)
|
27 |
+
* - openssl_random_pseudo_bytes (requires 'openssl' module enabled)
|
28 |
+
* - mcrypt_create_iv (requires 'mcrypt' module enabled)
|
29 |
+
*
|
30 |
+
* You may also supply a custom bytes generator as an optional second
|
31 |
+
* parameter.
|
32 |
+
*
|
33 |
+
* @param \Aws\Api\Service $service
|
34 |
+
* @param callable|null $bytesGenerator
|
35 |
+
*
|
36 |
+
* @return callable
|
37 |
+
*/
|
38 |
+
public static function wrap(
|
39 |
+
Service $service,
|
40 |
+
callable $bytesGenerator = null
|
41 |
+
) {
|
42 |
+
return function (callable $handler) use ($service, $bytesGenerator) {
|
43 |
+
return new self($handler, $service, $bytesGenerator);
|
44 |
+
};
|
45 |
+
}
|
46 |
+
|
47 |
+
public function __construct(
|
48 |
+
callable $nextHandler,
|
49 |
+
Service $service,
|
50 |
+
callable $bytesGenerator = null
|
51 |
+
) {
|
52 |
+
$this->bytesGenerator = $bytesGenerator
|
53 |
+
?: $this->findCompatibleRandomSource();
|
54 |
+
$this->service = $service;
|
55 |
+
$this->nextHandler = $nextHandler;
|
56 |
+
}
|
57 |
+
|
58 |
+
public function __invoke(
|
59 |
+
CommandInterface $command,
|
60 |
+
RequestInterface $request = null
|
61 |
+
) {
|
62 |
+
$handler = $this->nextHandler;
|
63 |
+
if ($this->bytesGenerator) {
|
64 |
+
$operation = $this->service->getOperation($command->getName());
|
65 |
+
$members = $operation->getInput()->getMembers();
|
66 |
+
foreach ($members as $member => $value) {
|
67 |
+
if ($value['idempotencyToken']) {
|
68 |
+
$bytes = call_user_func($this->bytesGenerator, 16);
|
69 |
+
// populating UUIDv4 only when the parameter is not set
|
70 |
+
$command[$member] = $command[$member]
|
71 |
+
?: $this->getUuidV4($bytes);
|
72 |
+
// only one member could have the trait enabled
|
73 |
+
break;
|
74 |
+
}
|
75 |
+
}
|
76 |
+
}
|
77 |
+
return $handler($command, $request);
|
78 |
+
}
|
79 |
+
|
80 |
+
/**
|
81 |
+
* This function generates a random UUID v4 string,
|
82 |
+
* which is used as auto filled token value.
|
83 |
+
*
|
84 |
+
* @param string $bytes 16 bytes of pseudo-random bytes
|
85 |
+
* @return string
|
86 |
+
* More information about UUID v4, see:
|
87 |
+
* https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_.28random.29
|
88 |
+
* https://tools.ietf.org/html/rfc4122#page-14
|
89 |
+
*/
|
90 |
+
private static function getUuidV4($bytes)
|
91 |
+
{
|
92 |
+
// set version to 0100
|
93 |
+
$bytes[6] = chr(ord($bytes[6]) & 0x0f | 0x40);
|
94 |
+
// set bits 6-7 to 10
|
95 |
+
$bytes[8] = chr(ord($bytes[8]) & 0x3f | 0x80);
|
96 |
+
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($bytes), 4));
|
97 |
+
}
|
98 |
+
|
99 |
+
/**
|
100 |
+
* This function decides the PHP function used in generating random bytes.
|
101 |
+
*
|
102 |
+
* @return callable|null
|
103 |
+
*/
|
104 |
+
private function findCompatibleRandomSource()
|
105 |
+
{
|
106 |
+
if (function_exists('random_bytes')) {
|
107 |
+
return 'random_bytes';
|
108 |
+
}
|
109 |
+
|
110 |
+
if (function_exists('openssl_random_pseudo_bytes')) {
|
111 |
+
return 'openssl_random_pseudo_bytes';
|
112 |
+
}
|
113 |
+
|
114 |
+
if (function_exists('mcrypt_create_iv')) {
|
115 |
+
return 'mcrypt_create_iv';
|
116 |
+
}
|
117 |
+
}
|
118 |
+
}
|
lib/Aws/Aws/JsonCompiler.php
ADDED
@@ -0,0 +1,25 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Loads JSON files and compiles them into PHP arrays.
|
6 |
+
*
|
7 |
+
* @internal Please use json_decode instead.
|
8 |
+
* @deprecated
|
9 |
+
*/
|
10 |
+
class JsonCompiler
|
11 |
+
{
|
12 |
+
const CACHE_ENV = 'AWS_PHP_CACHE_DIR';
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Loads a JSON file from cache or from the JSON file directly.
|
16 |
+
*
|
17 |
+
* @param string $path Path to the JSON file to load.
|
18 |
+
*
|
19 |
+
* @return mixed
|
20 |
+
*/
|
21 |
+
public function load($path)
|
22 |
+
{
|
23 |
+
return load_compiled_json($path);
|
24 |
+
}
|
25 |
+
}
|
lib/Aws/Aws/LruArrayCache.php
ADDED
@@ -0,0 +1,79 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Simple in-memory LRU cache that limits the number of cached entries.
|
6 |
+
*
|
7 |
+
* The LRU cache is implemented using PHP's ordered associative array. When
|
8 |
+
* accessing an element, the element is removed from the hash and re-added to
|
9 |
+
* ensure that recently used items are always at the end of the list while
|
10 |
+
* least recently used are at the beginning. When a value is added to the
|
11 |
+
* cache, if the number of cached items exceeds the allowed number, the first
|
12 |
+
* N number of items are removed from the array.
|
13 |
+
*/
|
14 |
+
class LruArrayCache implements CacheInterface, \Countable
|
15 |
+
{
|
16 |
+
/** @var int */
|
17 |
+
private $maxItems;
|
18 |
+
|
19 |
+
/** @var array */
|
20 |
+
private $items = array();
|
21 |
+
|
22 |
+
/**
|
23 |
+
* @param int $maxItems Maximum number of allowed cache items.
|
24 |
+
*/
|
25 |
+
public function __construct($maxItems = 1000)
|
26 |
+
{
|
27 |
+
$this->maxItems = $maxItems;
|
28 |
+
}
|
29 |
+
|
30 |
+
public function get($key)
|
31 |
+
{
|
32 |
+
if (!isset($this->items[$key])) {
|
33 |
+
return null;
|
34 |
+
}
|
35 |
+
|
36 |
+
$entry = $this->items[$key];
|
37 |
+
|
38 |
+
// Ensure the item is not expired.
|
39 |
+
if (!$entry[1] || time() < $entry[1]) {
|
40 |
+
// LRU: remove the item and push it to the end of the array.
|
41 |
+
unset($this->items[$key]);
|
42 |
+
$this->items[$key] = $entry;
|
43 |
+
return $entry[0];
|
44 |
+
}
|
45 |
+
|
46 |
+
unset($this->items[$key]);
|
47 |
+
return null;
|
48 |
+
}
|
49 |
+
|
50 |
+
public function set($key, $value, $ttl = 0)
|
51 |
+
{
|
52 |
+
// Only call time() if the TTL is not 0/false/null
|
53 |
+
$ttl = $ttl ? time() + $ttl : 0;
|
54 |
+
$this->items[$key] = [$value, $ttl];
|
55 |
+
|
56 |
+
// Determine if there are more items in the cache than allowed.
|
57 |
+
$diff = count($this->items) - $this->maxItems;
|
58 |
+
|
59 |
+
// Clear out least recently used items.
|
60 |
+
if ($diff > 0) {
|
61 |
+
// Reset to the beginning of the array and begin unsetting.
|
62 |
+
reset($this->items);
|
63 |
+
for ($i = 0; $i < $diff; $i++) {
|
64 |
+
unset($this->items[key($this->items)]);
|
65 |
+
next($this->items);
|
66 |
+
}
|
67 |
+
}
|
68 |
+
}
|
69 |
+
|
70 |
+
public function remove($key)
|
71 |
+
{
|
72 |
+
unset($this->items[$key]);
|
73 |
+
}
|
74 |
+
|
75 |
+
public function count()
|
76 |
+
{
|
77 |
+
return count($this->items);
|
78 |
+
}
|
79 |
+
}
|
lib/Aws/Aws/Middleware.php
ADDED
@@ -0,0 +1,372 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws;
|
3 |
+
|
4 |
+
use Aws\Api\Service;
|
5 |
+
use Aws\Api\Validator;
|
6 |
+
use Aws\Credentials\CredentialsInterface;
|
7 |
+
use Aws\Exception\AwsException;
|
8 |
+
use GuzzleHttp\Promise;
|
9 |
+
use GuzzleHttp\Psr7;
|
10 |
+
use GuzzleHttp\Psr7\LazyOpenStream;
|
11 |
+
use Psr\Http\Message\RequestInterface;
|
12 |
+
|
13 |
+
final class Middleware
|
14 |
+
{
|
15 |
+
/**
|
16 |
+
* Middleware used to allow a command parameter (e.g., "SourceFile") to
|
17 |
+
* be used to specify the source of data for an upload operation.
|
18 |
+
*
|
19 |
+
* @param Service $api
|
20 |
+
* @param string $bodyParameter
|
21 |
+
* @param string $sourceParameter
|
22 |
+
*
|
23 |
+
* @return callable
|
24 |
+
*/
|
25 |
+
public static function sourceFile(
|
26 |
+
Service $api,
|
27 |
+
$bodyParameter = 'Body',
|
28 |
+
$sourceParameter = 'SourceFile'
|
29 |
+
) {
|
30 |
+
return function (callable $handler) use (
|
31 |
+
$api,
|
32 |
+
$bodyParameter,
|
33 |
+
$sourceParameter
|
34 |
+
) {
|
35 |
+
return function (
|
36 |
+
CommandInterface $command,
|
37 |
+
RequestInterface $request = null)
|
38 |
+
use (
|
39 |
+
$handler,
|
40 |
+
$api,
|
41 |
+
$bodyParameter,
|
42 |
+
$sourceParameter
|
43 |
+
) {
|
44 |
+
$operation = $api->getOperation($command->getName());
|
45 |
+
$source = $command[$sourceParameter];
|
46 |
+
|
47 |
+
if ($source !== null
|
48 |
+
&& $operation->getInput()->hasMember($bodyParameter)
|
49 |
+
) {
|
50 |
+
$command[$bodyParameter] = new LazyOpenStream($source, 'r');
|
51 |
+
unset($command[$sourceParameter]);
|
52 |
+
}
|
53 |
+
|
54 |
+
return $handler($command, $request);
|
55 |
+
};
|
56 |
+
};
|
57 |
+
}
|
58 |
+
|
59 |
+
/**
|
60 |
+
* Adds a middleware that uses client-side validation.
|
61 |
+
*
|
62 |
+
* @param Service $api API being accessed.
|
63 |
+
*
|
64 |
+
* @return callable
|
65 |
+
*/
|
66 |
+
public static function validation(Service $api, Validator $validator = null)
|
67 |
+
{
|
68 |
+
$validator = $validator ?: new Validator();
|
69 |
+
return function (callable $handler) use ($api, $validator) {
|
70 |
+
return function (
|
71 |
+
CommandInterface $command,
|
72 |
+
RequestInterface $request = null
|
73 |
+
) use ($api, $validator, $handler) {
|
74 |
+
$operation = $api->getOperation($command->getName());
|
75 |
+
$validator->validate(
|
76 |
+
$command->getName(),
|
77 |
+
$operation->getInput(),
|
78 |
+
$command->toArray()
|
79 |
+
);
|
80 |
+
return $handler($command, $request);
|
81 |
+
};
|
82 |
+
};
|
83 |
+
}
|
84 |
+
|
85 |
+
/**
|
86 |
+
* Builds an HTTP request for a command.
|
87 |
+
*
|
88 |
+
* @param callable $serializer Function used to serialize a request for a
|
89 |
+
* command.
|
90 |
+
* @return callable
|
91 |
+
*/
|
92 |
+
public static function requestBuilder(callable $serializer)
|
93 |
+
{
|
94 |
+
return function (callable $handler) use ($serializer) {
|
95 |
+
return function (CommandInterface $command) use ($serializer, $handler) {
|
96 |
+
return $handler($command, $serializer($command));
|
97 |
+
};
|
98 |
+
};
|
99 |
+
}
|
100 |
+
|
101 |
+
/**
|
102 |
+
* Creates a middleware that signs requests for a command.
|
103 |
+
*
|
104 |
+
* @param callable $credProvider Credentials provider function that
|
105 |
+
* returns a promise that is resolved
|
106 |
+
* with a CredentialsInterface object.
|
107 |
+
* @param callable $signatureFunction Function that accepts a Command
|
108 |
+
* object and returns a
|
109 |
+
* SignatureInterface.
|
110 |
+
*
|
111 |
+
* @return callable
|
112 |
+
*/
|
113 |
+
public static function signer(callable $credProvider, callable $signatureFunction)
|
114 |
+
{
|
115 |
+
return function (callable $handler) use ($signatureFunction, $credProvider) {
|
116 |
+
return function (
|
117 |
+
CommandInterface $command,
|
118 |
+
RequestInterface $request
|
119 |
+
) use ($handler, $signatureFunction, $credProvider) {
|
120 |
+
$signer = $signatureFunction($command);
|
121 |
+
return $credProvider()->then(
|
122 |
+
function (CredentialsInterface $creds)
|
123 |
+
use ($handler, $command, $signer, $request) {
|
124 |
+
return $handler(
|
125 |
+
$command,
|
126 |
+
$signer->signRequest($request, $creds)
|
127 |
+
);
|
128 |
+
}
|
129 |
+
);
|
130 |
+
};
|
131 |
+
};
|
132 |
+
}
|
133 |
+
|
134 |
+
/**
|
135 |
+
* Creates a middleware that invokes a callback at a given step.
|
136 |
+
*
|
137 |
+
* The tap callback accepts a CommandInterface and RequestInterface as
|
138 |
+
* arguments but is not expected to return a new value or proxy to
|
139 |
+
* downstream middleware. It's simply a way to "tap" into the handler chain
|
140 |
+
* to debug or get an intermediate value.
|
141 |
+
*
|
142 |
+
* @param callable $fn Tap function
|
143 |
+
*
|
144 |
+
* @return callable
|
145 |
+
*/
|
146 |
+
public static function tap(callable $fn)
|
147 |
+
{
|
148 |
+
return function (callable $handler) use ($fn) {
|
149 |
+
return function (
|
150 |
+
CommandInterface $command,
|
151 |
+
RequestInterface $request = null
|
152 |
+
) use ($handler, $fn) {
|
153 |
+
$fn($command, $request);
|
154 |
+
return $handler($command, $request);
|
155 |
+
};
|
156 |
+
};
|
157 |
+
}
|
158 |
+
|
159 |
+
/**
|
160 |
+
* Middleware wrapper function that retries requests based on the boolean
|
161 |
+
* result of invoking the provided "decider" function.
|
162 |
+
*
|
163 |
+
* If no delay function is provided, a simple implementation of exponential
|
164 |
+
* backoff will be utilized.
|
165 |
+
*
|
166 |
+
* @param callable $decider Function that accepts the number of retries,
|
167 |
+
* a request, [result], and [exception] and
|
168 |
+
* returns true if the command is to be retried.
|
169 |
+
* @param callable $delay Function that accepts the number of retries and
|
170 |
+
* returns the number of milliseconds to delay.
|
171 |
+
* @param bool $stats Whether to collect statistics on retries and the
|
172 |
+
* associated delay.
|
173 |
+
*
|
174 |
+
* @return callable
|
175 |
+
*/
|
176 |
+
public static function retry(
|
177 |
+
callable $decider = null,
|
178 |
+
callable $delay = null,
|
179 |
+
$stats = false
|
180 |
+
) {
|
181 |
+
$decider = $decider ?: RetryMiddleware::createDefaultDecider();
|
182 |
+
$delay = $delay ?: [RetryMiddleware::class, 'exponentialDelay'];
|
183 |
+
|
184 |
+
return function (callable $handler) use ($decider, $delay, $stats) {
|
185 |
+
return new RetryMiddleware($decider, $delay, $handler, $stats);
|
186 |
+
};
|
187 |
+
}
|
188 |
+
/**
|
189 |
+
* Middleware wrapper function that adds an invocation id header to
|
190 |
+
* requests, which is only applied after the build step.
|
191 |
+
*
|
192 |
+
* This is a uniquely generated UUID to identify initial and subsequent
|
193 |
+
* retries as part of a complete request lifecycle.
|
194 |
+
*
|
195 |
+
* @return callable
|
196 |
+
*/
|
197 |
+
public static function invocationId()
|
198 |
+
{
|
199 |
+
return function (callable $handler) {
|
200 |
+
return function (
|
201 |
+
CommandInterface $command,
|
202 |
+
RequestInterface $request
|
203 |
+
) use ($handler){
|
204 |
+
return $handler($command, $request->withHeader(
|
205 |
+
'aws-sdk-invocation-id',
|
206 |
+
md5(uniqid(gethostname(), true))
|
207 |
+
));
|
208 |
+
};
|
209 |
+
};
|
210 |
+
}
|
211 |
+
/**
|
212 |
+
* Middleware wrapper function that adds a Content-Type header to requests.
|
213 |
+
* This is only done when the Content-Type has not already been set, and the
|
214 |
+
* request body's URI is available. It then checks the file extension of the
|
215 |
+
* URI to determine the mime-type.
|
216 |
+
*
|
217 |
+
* @param array $operations Operations that Content-Type should be added to.
|
218 |
+
*
|
219 |
+
* @return callable
|
220 |
+
*/
|
221 |
+
public static function contentType(array $operations)
|
222 |
+
{
|
223 |
+
return function (callable $handler) use ($operations) {
|
224 |
+
return function (
|
225 |
+
CommandInterface $command,
|
226 |
+
RequestInterface $request = null
|
227 |
+
) use ($handler, $operations) {
|
228 |
+
if (!$request->hasHeader('Content-Type')
|
229 |
+
&& in_array($command->getName(), $operations, true)
|
230 |
+
&& ($uri = $request->getBody()->getMetadata('uri'))
|
231 |
+
) {
|
232 |
+
$request = $request->withHeader(
|
233 |
+
'Content-Type',
|
234 |
+
Psr7\mimetype_from_filename($uri) ?: 'application/octet-stream'
|
235 |
+
);
|
236 |
+
}
|
237 |
+
|
238 |
+
return $handler($command, $request);
|
239 |
+
};
|
240 |
+
};
|
241 |
+
}
|
242 |
+
|
243 |
+
/**
|
244 |
+
* Tracks command and request history using a history container.
|
245 |
+
*
|
246 |
+
* This is useful for testing.
|
247 |
+
*
|
248 |
+
* @param History $history History container to store entries.
|
249 |
+
*
|
250 |
+
* @return callable
|
251 |
+
*/
|
252 |
+
public static function history(History $history)
|
253 |
+
{
|
254 |
+
return function (callable $handler) use ($history) {
|
255 |
+
return function (
|
256 |
+
CommandInterface $command,
|
257 |
+
RequestInterface $request = null
|
258 |
+
) use ($handler, $history) {
|
259 |
+
$ticket = $history->start($command, $request);
|
260 |
+
return $handler($command, $request)
|
261 |
+
->then(
|
262 |
+
function ($result) use ($history, $ticket) {
|
263 |
+
$history->finish($ticket, $result);
|
264 |
+
return $result;
|
265 |
+
},
|
266 |
+
function ($reason) use ($history, $ticket) {
|
267 |
+
$history->finish($ticket, $reason);
|
268 |
+
return Promise\rejection_for($reason);
|
269 |
+
}
|
270 |
+
);
|
271 |
+
};
|
272 |
+
};
|
273 |
+
}
|
274 |
+
|
275 |
+
/**
|
276 |
+
* Creates a middleware that applies a map function to requests as they
|
277 |
+
* pass through the middleware.
|
278 |
+
*
|
279 |
+
* @param callable $f Map function that accepts a RequestInterface and
|
280 |
+
* returns a RequestInterface.
|
281 |
+
*
|
282 |
+
* @return callable
|
283 |
+
*/
|
284 |
+
public static function mapRequest(callable $f)
|
285 |
+
{
|
286 |
+
return function (callable $handler) use ($f) {
|
287 |
+
return function (
|
288 |
+
CommandInterface $command,
|
289 |
+
RequestInterface $request = null
|
290 |
+
) use ($handler, $f) {
|
291 |
+
return $handler($command, $f($request));
|
292 |
+
};
|
293 |
+
};
|
294 |
+
}
|
295 |
+
|
296 |
+
/**
|
297 |
+
* Creates a middleware that applies a map function to commands as they
|
298 |
+
* pass through the middleware.
|
299 |
+
*
|
300 |
+
* @param callable $f Map function that accepts a command and returns a
|
301 |
+
* command.
|
302 |
+
*
|
303 |
+
* @return callable
|
304 |
+
*/
|
305 |
+
public static function mapCommand(callable $f)
|
306 |
+
{
|
307 |
+
return function (callable $handler) use ($f) {
|
308 |
+
return function (
|
309 |
+
CommandInterface $command,
|
310 |
+
RequestInterface $request = null
|
311 |
+
) use ($handler, $f) {
|
312 |
+
return $handler($f($command), $request);
|
313 |
+
};
|
314 |
+
};
|
315 |
+
}
|
316 |
+
|
317 |
+
/**
|
318 |
+
* Creates a middleware that applies a map function to results.
|
319 |
+
*
|
320 |
+
* @param callable $f Map function that accepts an Aws\ResultInterface and
|
321 |
+
* returns an Aws\ResultInterface.
|
322 |
+
*
|
323 |
+
* @return callable
|
324 |
+
*/
|
325 |
+
public static function mapResult(callable $f)
|
326 |
+
{
|
327 |
+
return function (callable $handler) use ($f) {
|
328 |
+
return function (
|
329 |
+
CommandInterface $command,
|
330 |
+
RequestInterface $request = null
|
331 |
+
) use ($handler, $f) {
|
332 |
+
return $handler($command, $request)->then($f);
|
333 |
+
};
|
334 |
+
};
|
335 |
+
}
|
336 |
+
|
337 |
+
public static function timer()
|
338 |
+
{
|
339 |
+
return function (callable $handler) {
|
340 |
+
return function (
|
341 |
+
CommandInterface $command,
|
342 |
+
RequestInterface $request = null
|
343 |
+
) use ($handler) {
|
344 |
+
$start = microtime(true);
|
345 |
+
return $handler($command, $request)
|
346 |
+
->then(
|
347 |
+
function (ResultInterface $res) use ($start) {
|
348 |
+
if (!isset($res['@metadata'])) {
|
349 |
+
$res['@metadata'] = [];
|
350 |
+
}
|
351 |
+
if (!isset($res['@metadata']['transferStats'])) {
|
352 |
+
$res['@metadata']['transferStats'] = [];
|
353 |
+
}
|
354 |
+
|
355 |
+
$res['@metadata']['transferStats']['total_time']
|
356 |
+
= microtime(true) - $start;
|
357 |
+
|
358 |
+
return $res;
|
359 |
+
},
|
360 |
+
function ($err) use ($start) {
|
361 |
+
if ($err instanceof AwsException) {
|
362 |
+
$err->setTransferInfo([
|
363 |
+
'total_time' => microtime(true) - $start,
|
364 |
+
] + $err->getTransferInfo());
|
365 |
+
}
|
366 |
+
return Promise\rejection_for($err);
|
367 |
+
}
|
368 |
+
);
|
369 |
+
};
|
370 |
+
};
|
371 |
+
}
|
372 |
+
}
|
lib/Aws/Aws/MockHandler.php
ADDED
@@ -0,0 +1,145 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws;
|
3 |
+
|
4 |
+
use Aws\Exception\AwsException;
|
5 |
+
use GuzzleHttp\Promise;
|
6 |
+
use GuzzleHttp\Promise\RejectedPromise;
|
7 |
+
use Psr\Http\Message\RequestInterface;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Returns promises that are rejected or fulfilled using a queue of
|
11 |
+
* Aws\ResultInterface and Aws\Exception\AwsException objects.
|
12 |
+
*/
|
13 |
+
class MockHandler implements \Countable
|
14 |
+
{
|
15 |
+
private $queue;
|
16 |
+
private $lastCommand;
|
17 |
+
private $lastRequest;
|
18 |
+
private $onFulfilled;
|
19 |
+
private $onRejected;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* The passed in value must be an array of {@see Aws\ResultInterface} or
|
23 |
+
* {@see AwsException} objects that acts as a queue of results or
|
24 |
+
* exceptions to return each time the handler is invoked.
|
25 |
+
*
|
26 |
+
* @param array $resultOrQueue
|
27 |
+
* @param callable $onFulfilled Callback to invoke when the return value is fulfilled.
|
28 |
+
* @param callable $onRejected Callback to invoke when the return value is rejected.
|
29 |
+
*/
|
30 |
+
public function __construct(
|
31 |
+
array $resultOrQueue = [],
|
32 |
+
callable $onFulfilled = null,
|
33 |
+
callable $onRejected = null
|
34 |
+
) {
|
35 |
+
$this->onFulfilled = $onFulfilled;
|
36 |
+
$this->onRejected = $onRejected;
|
37 |
+
|
38 |
+
if ($resultOrQueue) {
|
39 |
+
call_user_func_array([$this, 'append'], $resultOrQueue);
|
40 |
+
}
|
41 |
+
}
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Adds one or more variadic ResultInterface or AwsException objects to the
|
45 |
+
* queue.
|
46 |
+
*/
|
47 |
+
public function append()
|
48 |
+
{
|
49 |
+
foreach (func_get_args() as $value) {
|
50 |
+
if ($value instanceof ResultInterface
|
51 |
+
|| $value instanceof AwsException
|
52 |
+
|| is_callable($value)
|
53 |
+
) {
|
54 |
+
$this->queue[] = $value;
|
55 |
+
} else {
|
56 |
+
throw new \InvalidArgumentException('Expected an Aws\ResultInterface or Aws\Exception\AwsException.');
|
57 |
+
}
|
58 |
+
}
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Adds one or more \Exception or \Throwable to the queue
|
63 |
+
*/
|
64 |
+
public function appendException()
|
65 |
+
{
|
66 |
+
foreach (func_get_args() as $value) {
|
67 |
+
if ($value instanceof \Exception || $value instanceof \Throwable) {
|
68 |
+
$this->queue[] = $value;
|
69 |
+
} else {
|
70 |
+
throw new \InvalidArgumentException('Expected an \Exception or \Throwable.');
|
71 |
+
}
|
72 |
+
}
|
73 |
+
}
|
74 |
+
|
75 |
+
public function __invoke(
|
76 |
+
CommandInterface $command,
|
77 |
+
RequestInterface $request
|
78 |
+
) {
|
79 |
+
if (!$this->queue) {
|
80 |
+
$last = $this->lastCommand
|
81 |
+
? ' The last command sent was ' . $this->lastCommand->getName() . '.'
|
82 |
+
: '';
|
83 |
+
throw new \RuntimeException('Mock queue is empty. Trying to send a '
|
84 |
+
. $command->getName() . ' command failed.' . $last);
|
85 |
+
}
|
86 |
+
|
87 |
+
$this->lastCommand = $command;
|
88 |
+
$this->lastRequest = $request;
|
89 |
+
|
90 |
+
$result = array_shift($this->queue);
|
91 |
+
|
92 |
+
if (is_callable($result)) {
|
93 |
+
$result = $result($command, $request);
|
94 |
+
}
|
95 |
+
|
96 |
+
if ($result instanceof \Exception) {
|
97 |
+
$result = new RejectedPromise($result);
|
98 |
+
} else {
|
99 |
+
// Add an effective URI and statusCode if not present.
|
100 |
+
$meta = $result['@metadata'];
|
101 |
+
if (!isset($meta['effectiveUri'])) {
|
102 |
+
$meta['effectiveUri'] = (string) $request->getUri();
|
103 |
+
}
|
104 |
+
if (!isset($meta['statusCode'])) {
|
105 |
+
$meta['statusCode'] = 200;
|
106 |
+
}
|
107 |
+
$result['@metadata'] = $meta;
|
108 |
+
$result = Promise\promise_for($result);
|
109 |
+
}
|
110 |
+
|
111 |
+
$result->then($this->onFulfilled, $this->onRejected);
|
112 |
+
|
113 |
+
return $result;
|
114 |
+
}
|
115 |
+
|
116 |
+
/**
|
117 |
+
* Get the last received request.
|
118 |
+
*
|
119 |
+
* @return RequestInterface
|
120 |
+
*/
|
121 |
+
public function getLastRequest()
|
122 |
+
{
|
123 |
+
return $this->lastRequest;
|
124 |
+
}
|
125 |
+
|
126 |
+
/**
|
127 |
+
* Get the last received command.
|
128 |
+
*
|
129 |
+
* @return CommandInterface
|
130 |
+
*/
|
131 |
+
public function getLastCommand()
|
132 |
+
{
|
133 |
+
return $this->lastCommand;
|
134 |
+
}
|
135 |
+
|
136 |
+
/**
|
137 |
+
* Returns the number of remaining items in the queue.
|
138 |
+
*
|
139 |
+
* @return int
|
140 |
+
*/
|
141 |
+
public function count()
|
142 |
+
{
|
143 |
+
return count($this->queue);
|
144 |
+
}
|
145 |
+
}
|
lib/Aws/Aws/MonitoringEventsInterface.php
ADDED
@@ -0,0 +1,32 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Interface for adding and retrieving client-side monitoring events
|
6 |
+
*/
|
7 |
+
interface MonitoringEventsInterface
|
8 |
+
{
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Get client-side monitoring events attached to this object. Each event is
|
12 |
+
* represented as an associative array within the returned array.
|
13 |
+
*
|
14 |
+
* @return array
|
15 |
+
*/
|
16 |
+
public function getMonitoringEvents();
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Prepend a client-side monitoring event to this object's event list
|
20 |
+
*
|
21 |
+
* @param array $event
|
22 |
+
*/
|
23 |
+
public function prependMonitoringEvent(array $event);
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Append a client-side monitoring event to this object's event list
|
27 |
+
*
|
28 |
+
* @param array $event
|
29 |
+
*/
|
30 |
+
public function appendMonitoringEvent(array $event);
|
31 |
+
|
32 |
+
}
|
lib/Aws/Aws/MultiRegionClient.php
ADDED
@@ -0,0 +1,236 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws;
|
3 |
+
|
4 |
+
use Aws\Endpoint\PartitionEndpointProvider;
|
5 |
+
use Aws\Endpoint\PartitionInterface;
|
6 |
+
|
7 |
+
class MultiRegionClient implements AwsClientInterface
|
8 |
+
{
|
9 |
+
use AwsClientTrait;
|
10 |
+
|
11 |
+
/** @var AwsClientInterface[] A pool of clients keyed by region. */
|
12 |
+
private $clientPool = [];
|
13 |
+
/** @var callable */
|
14 |
+
private $factory;
|
15 |
+
/** @var PartitionInterface */
|
16 |
+
private $partition;
|
17 |
+
/** @var array */
|
18 |
+
private $args;
|
19 |
+
/** @var array */
|
20 |
+
private $config;
|
21 |
+
/** @var HandlerList */
|
22 |
+
private $handlerList;
|
23 |
+
|
24 |
+
public static function getArguments()
|
25 |
+
{
|
26 |
+
$args = array_intersect_key(
|
27 |
+
ClientResolver::getDefaultArguments(),
|
28 |
+
['service' => true, 'region' => true]
|
29 |
+
);
|
30 |
+
$args['region']['required'] = false;
|
31 |
+
|
32 |
+
return $args + [
|
33 |
+
'client_factory' => [
|
34 |
+
'type' => 'config',
|
35 |
+
'valid' => ['callable'],
|
36 |
+
'doc' => 'A callable that takes an array of client'
|
37 |
+
. ' configuration arguments and returns a regionalized'
|
38 |
+
. ' client.',
|
39 |
+
'required' => true,
|
40 |
+
'internal' => true,
|
41 |
+
'default' => function (array $args) {
|
42 |
+
$namespace = manifest($args['service'])['namespace'];
|
43 |
+
$klass = "Aws\\{$namespace}\\{$namespace}Client";
|
44 |
+
$region = isset($args['region']) ? $args['region'] : null;
|
45 |
+
|
46 |
+
return function (array $args) use ($klass, $region) {
|
47 |
+
if ($region && empty($args['region'])) {
|
48 |
+
$args['region'] = $region;
|
49 |
+
}
|
50 |
+
|
51 |
+
return new $klass($args);
|
52 |
+
};
|
53 |
+
},
|
54 |
+
],
|
55 |
+
'partition' => [
|
56 |
+
'type' => 'config',
|
57 |
+
'valid' => ['string', PartitionInterface::class],
|
58 |
+
'doc' => 'AWS partition to connect to. Valid partitions'
|
59 |
+
. ' include "aws," "aws-cn," and "aws-us-gov." Used to'
|
60 |
+
. ' restrict the scope of the mapRegions method.',
|
61 |
+
'default' => function (array $args) {
|
62 |
+
$region = isset($args['region']) ? $args['region'] : '';
|
63 |
+
return PartitionEndpointProvider::defaultProvider()
|
64 |
+
->getPartition($region, $args['service']);
|
65 |
+
},
|
66 |
+
'fn' => function ($value, array &$args) {
|
67 |
+
if (is_string($value)) {
|
68 |
+
$value = PartitionEndpointProvider::defaultProvider()
|
69 |
+
->getPartitionByName($value);
|
70 |
+
}
|
71 |
+
|
72 |
+
if (!$value instanceof PartitionInterface) {
|
73 |
+
throw new \InvalidArgumentException('No valid partition'
|
74 |
+
. ' was provided. Provide a concrete partition or'
|
75 |
+
. ' the name of a partition (e.g., "aws," "aws-cn,"'
|
76 |
+
. ' or "aws-us-gov").'
|
77 |
+
);
|
78 |
+
}
|
79 |
+
|
80 |
+
$args['partition'] = $value;
|
81 |
+
$args['endpoint_provider'] = $value;
|
82 |
+
}
|
83 |
+
],
|
84 |
+
];
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* The multi-region client constructor accepts the following options:
|
89 |
+
*
|
90 |
+
* - client_factory: (callable) An optional callable that takes an array of
|
91 |
+
* client configuration arguments and returns a regionalized client.
|
92 |
+
* - partition: (Aws\Endpoint\Partition|string) AWS partition to connect to.
|
93 |
+
* Valid partitions include "aws," "aws-cn," and "aws-us-gov." Used to
|
94 |
+
* restrict the scope of the mapRegions method.
|
95 |
+
* - region: (string) Region to connect to when no override is provided.
|
96 |
+
* Used to create the default client factory and determine the appropriate
|
97 |
+
* AWS partition when present.
|
98 |
+
*
|
99 |
+
* @param array $args Client configuration arguments.
|
100 |
+
*/
|
101 |
+
public function __construct(array $args = [])
|
102 |
+
{
|
103 |
+
if (!isset($args['service'])) {
|
104 |
+
$args['service'] = $this->parseClass();
|
105 |
+
}
|
106 |
+
|
107 |
+
$this->handlerList = new HandlerList(function (
|
108 |
+
CommandInterface $command
|
109 |
+
) {
|
110 |
+
list($region, $args) = $this->getRegionFromArgs($command->toArray());
|
111 |
+
$command = $this->getClientFromPool($region)
|
112 |
+
->getCommand($command->getName(), $args);
|
113 |
+
return $this->executeAsync($command);
|
114 |
+
});
|
115 |
+
|
116 |
+
$argDefinitions = static::getArguments();
|
117 |
+
$resolver = new ClientResolver($argDefinitions);
|
118 |
+
$args = $resolver->resolve($args, $this->handlerList);
|
119 |
+
$this->config = $args['config'];
|
120 |
+
$this->factory = $args['client_factory'];
|
121 |
+
$this->partition = $args['partition'];
|
122 |
+
$this->args = array_diff_key($args, $args['config']);
|
123 |
+
}
|
124 |
+
|
125 |
+
/**
|
126 |
+
* Get the region to which the client is configured to send requests by
|
127 |
+
* default.
|
128 |
+
*
|
129 |
+
* @return string
|
130 |
+
*/
|
131 |
+
public function getRegion()
|
132 |
+
{
|
133 |
+
return $this->getClientFromPool()->getRegion();
|
134 |
+
}
|
135 |
+
|
136 |
+
/**
|
137 |
+
* Create a command for an operation name.
|
138 |
+
*
|
139 |
+
* Special keys may be set on the command to control how it behaves,
|
140 |
+
* including:
|
141 |
+
*
|
142 |
+
* - @http: Associative array of transfer specific options to apply to the
|
143 |
+
* request that is serialized for this command. Available keys include
|
144 |
+
* "proxy", "verify", "timeout", "connect_timeout", "debug", "delay", and
|
145 |
+
* "headers".
|
146 |
+
* - @region: The region to which the command should be sent.
|
147 |
+
*
|
148 |
+
* @param string $name Name of the operation to use in the command
|
149 |
+
* @param array $args Arguments to pass to the command
|
150 |
+
*
|
151 |
+
* @return CommandInterface
|
152 |
+
* @throws \InvalidArgumentException if no command can be found by name
|
153 |
+
*/
|
154 |
+
public function getCommand($name, array $args = [])
|
155 |
+
{
|
156 |
+
return new Command($name, $args, clone $this->getHandlerList());
|
157 |
+
}
|
158 |
+
|
159 |
+
public function getConfig($option = null)
|
160 |
+
{
|
161 |
+
if (null === $option) {
|
162 |
+
return $this->config;
|
163 |
+
}
|
164 |
+
|
165 |
+
if (isset($this->config[$option])) {
|
166 |
+
return $this->config[$option];
|
167 |
+
}
|
168 |
+
|
169 |
+
return $this->getClientFromPool()->getConfig($option);
|
170 |
+
}
|
171 |
+
|
172 |
+
public function getCredentials()
|
173 |
+
{
|
174 |
+
return $this->getClientFromPool()->getCredentials();
|
175 |
+
}
|
176 |
+
|
177 |
+
public function getHandlerList()
|
178 |
+
{
|
179 |
+
return $this->handlerList;
|
180 |
+
}
|
181 |
+
|
182 |
+
public function getApi()
|
183 |
+
{
|
184 |
+
return $this->getClientFromPool()->getApi();
|
185 |
+
}
|
186 |
+
|
187 |
+
public function getEndpoint()
|
188 |
+
{
|
189 |
+
return $this->getClientFromPool()->getEndpoint();
|
190 |
+
}
|
191 |
+
|
192 |
+
/**
|
193 |
+
* @param string $region Omit this argument or pass in an empty string to
|
194 |
+
* allow the configured client factory to apply the
|
195 |
+
* region.
|
196 |
+
*
|
197 |
+
* @return AwsClientInterface
|
198 |
+
*/
|
199 |
+
protected function getClientFromPool($region = '')
|
200 |
+
{
|
201 |
+
if (empty($this->clientPool[$region])) {
|
202 |
+
$factory = $this->factory;
|
203 |
+
$this->clientPool[$region] = $factory(
|
204 |
+
array_replace($this->args, array_filter(['region' => $region]))
|
205 |
+
);
|
206 |
+
}
|
207 |
+
|
208 |
+
return $this->clientPool[$region];
|
209 |
+
}
|
210 |
+
|
211 |
+
/**
|
212 |
+
* Parse the class name and return the "service" name of the client.
|
213 |
+
*
|
214 |
+
* @return string
|
215 |
+
*/
|
216 |
+
private function parseClass()
|
217 |
+
{
|
218 |
+
$klass = get_class($this);
|
219 |
+
|
220 |
+
if ($klass === __CLASS__) {
|
221 |
+
return '';
|
222 |
+
}
|
223 |
+
|
224 |
+
return strtolower(substr($klass, strrpos($klass, '\\') + 1, -17));
|
225 |
+
}
|
226 |
+
|
227 |
+
private function getRegionFromArgs(array $args)
|
228 |
+
{
|
229 |
+
$region = isset($args['@region'])
|
230 |
+
? $args['@region']
|
231 |
+
: $this->getRegion();
|
232 |
+
unset($args['@region']);
|
233 |
+
|
234 |
+
return [$region, $args];
|
235 |
+
}
|
236 |
+
}
|
lib/Aws/Aws/PhpHash.php
ADDED
@@ -0,0 +1,81 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Incremental hashing using PHP's hash functions.
|
6 |
+
*/
|
7 |
+
class PhpHash implements HashInterface
|
8 |
+
{
|
9 |
+
/** @var resource|\HashContext */
|
10 |
+
private $context;
|
11 |
+
|
12 |
+
/** @var string */
|
13 |
+
private $algo;
|
14 |
+
|
15 |
+
/** @var array */
|
16 |
+
private $options;
|
17 |
+
|
18 |
+
/** @var string */
|
19 |
+
private $hash;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @param string $algo Hashing algorithm. One of PHP's hash_algos()
|
23 |
+
* return values (e.g. md5, sha1, etc...).
|
24 |
+
* @param array $options Associative array of hashing options:
|
25 |
+
* - key: Secret key used with the hashing algorithm.
|
26 |
+
* - base64: Set to true to base64 encode the value when complete.
|
27 |
+
*/
|
28 |
+
public function __construct($algo, array $options = [])
|
29 |
+
{
|
30 |
+
$this->algo = $algo;
|
31 |
+
$this->options = $options;
|
32 |
+
}
|
33 |
+
|
34 |
+
public function update($data)
|
35 |
+
{
|
36 |
+
if ($this->hash !== null) {
|
37 |
+
$this->reset();
|
38 |
+
}
|
39 |
+
|
40 |
+
hash_update($this->getContext(), $data);
|
41 |
+
}
|
42 |
+
|
43 |
+
public function complete()
|
44 |
+
{
|
45 |
+
if ($this->hash) {
|
46 |
+
return $this->hash;
|
47 |
+
}
|
48 |
+
|
49 |
+
$this->hash = hash_final($this->getContext(), true);
|
50 |
+
|
51 |
+
if (isset($this->options['base64']) && $this->options['base64']) {
|
52 |
+
$this->hash = base64_encode($this->hash);
|
53 |
+
}
|
54 |
+
|
55 |
+
return $this->hash;
|
56 |
+
}
|
57 |
+
|
58 |
+
public function reset()
|
59 |
+
{
|
60 |
+
$this->context = $this->hash = null;
|
61 |
+
}
|
62 |
+
|
63 |
+
/**
|
64 |
+
* Get a hash context or create one if needed
|
65 |
+
*
|
66 |
+
* @return resource|\HashContext
|
67 |
+
*/
|
68 |
+
private function getContext()
|
69 |
+
{
|
70 |
+
if (!$this->context) {
|
71 |
+
$key = isset($this->options['key']) ? $this->options['key'] : null;
|
72 |
+
$this->context = hash_init(
|
73 |
+
$this->algo,
|
74 |
+
$key ? HASH_HMAC : 0,
|
75 |
+
$key
|
76 |
+
);
|
77 |
+
}
|
78 |
+
|
79 |
+
return $this->context;
|
80 |
+
}
|
81 |
+
}
|
lib/Aws/Aws/PresignUrlMiddleware.php
ADDED
@@ -0,0 +1,99 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws;
|
3 |
+
|
4 |
+
use Aws\Signature\SignatureV4;
|
5 |
+
use Aws\Endpoint\EndpointProvider;
|
6 |
+
use GuzzleHttp\Psr7\Uri;
|
7 |
+
use Psr\Http\Message\RequestInterface;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* @internal Adds computed values to service operations that need presigned url.
|
11 |
+
*/
|
12 |
+
class PresignUrlMiddleware
|
13 |
+
{
|
14 |
+
private $client;
|
15 |
+
private $endpointProvider;
|
16 |
+
private $nextHandler;
|
17 |
+
/** @var array names of operations that require presign url */
|
18 |
+
private $commandPool;
|
19 |
+
/** @var string */
|
20 |
+
private $serviceName;
|
21 |
+
/** @var string */
|
22 |
+
private $presignParam;
|
23 |
+
/** @var bool */
|
24 |
+
private $requireDifferentRegion;
|
25 |
+
|
26 |
+
public function __construct(
|
27 |
+
array $options,
|
28 |
+
callable $endpointProvider,
|
29 |
+
AwsClientInterface $client,
|
30 |
+
callable $nextHandler
|
31 |
+
) {
|
32 |
+
$this->endpointProvider = $endpointProvider;
|
33 |
+
$this->client = $client;
|
34 |
+
$this->nextHandler = $nextHandler;
|
35 |
+
$this->commandPool = $options['operations'];
|
36 |
+
$this->serviceName = $options['service'];
|
37 |
+
$this->presignParam = $options['presign_param'];
|
38 |
+
$this->requireDifferentRegion = !empty($options['require_different_region']);
|
39 |
+
}
|
40 |
+
|
41 |
+
public static function wrap(
|
42 |
+
AwsClientInterface $client,
|
43 |
+
callable $endpointProvider,
|
44 |
+
array $options = []
|
45 |
+
) {
|
46 |
+
return function (callable $handler) use ($endpointProvider, $client, $options) {
|
47 |
+
$f = new PresignUrlMiddleware($options, $endpointProvider, $client, $handler);
|
48 |
+
return $f;
|
49 |
+
};
|
50 |
+
}
|
51 |
+
|
52 |
+
public function __invoke(CommandInterface $cmd, RequestInterface $request = null)
|
53 |
+
{
|
54 |
+
if (in_array($cmd->getName(), $this->commandPool)
|
55 |
+
&& (!isset($cmd->{'__skip' . $cmd->getName()}))
|
56 |
+
) {
|
57 |
+
$cmd['DestinationRegion'] = $this->client->getRegion();
|
58 |
+
if (!$this->requireDifferentRegion
|
59 |
+
|| (!empty($cmd['SourceRegion'])
|
60 |
+
&& $cmd['SourceRegion'] !== $cmd['DestinationRegion'])
|
61 |
+
) {
|
62 |
+
$cmd[$this->presignParam] = $this->createPresignedUrl($this->client, $cmd);
|
63 |
+
}
|
64 |
+
}
|
65 |
+
|
66 |
+
$f = $this->nextHandler;
|
67 |
+
return $f($cmd, $request);
|
68 |
+
}
|
69 |
+
|
70 |
+
private function createPresignedUrl(
|
71 |
+
AwsClientInterface $client,
|
72 |
+
CommandInterface $cmd
|
73 |
+
) {
|
74 |
+
$cmdName = $cmd->getName();
|
75 |
+
$newCmd = $client->getCommand($cmdName, $cmd->toArray());
|
76 |
+
// Avoid infinite recursion by flagging the new command.
|
77 |
+
$newCmd->{'__skip' . $cmdName} = true;
|
78 |
+
|
79 |
+
// Serialize a request for the operation.
|
80 |
+
$request = \Aws\serialize($newCmd);
|
81 |
+
// Create the new endpoint for the target endpoint.
|
82 |
+
$endpoint = EndpointProvider::resolve($this->endpointProvider, [
|
83 |
+
'region' => $cmd['SourceRegion'],
|
84 |
+
'service' => $this->serviceName,
|
85 |
+
])['endpoint'];
|
86 |
+
|
87 |
+
// Set the request to hit the target endpoint.
|
88 |
+
$uri = $request->getUri()->withHost((new Uri($endpoint))->getHost());
|
89 |
+
$request = $request->withUri($uri);
|
90 |
+
// Create a presigned URL for our generated request.
|
91 |
+
$signer = new SignatureV4($this->serviceName, $cmd['SourceRegion']);
|
92 |
+
|
93 |
+
return (string) $signer->presign(
|
94 |
+
SignatureV4::convertPostToGet($request),
|
95 |
+
$client->getCredentials()->wait(),
|
96 |
+
'+1 hour'
|
97 |
+
)->getUri();
|
98 |
+
}
|
99 |
+
}
|
lib/Aws/Aws/Psr16CacheAdapter.php
ADDED
@@ -0,0 +1,30 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws;
|
3 |
+
|
4 |
+
use Psr\SimpleCache\CacheInterface as SimpleCacheInterface;
|
5 |
+
|
6 |
+
class Psr16CacheAdapter implements CacheInterface
|
7 |
+
{
|
8 |
+
/** @var SimpleCacheInterface */
|
9 |
+
private $cache;
|
10 |
+
|
11 |
+
public function __construct(SimpleCacheInterface $cache)
|
12 |
+
{
|
13 |
+
$this->cache = $cache;
|
14 |
+
}
|
15 |
+
|
16 |
+
public function get($key)
|
17 |
+
{
|
18 |
+
return $this->cache->get($key);
|
19 |
+
}
|
20 |
+
|
21 |
+
public function set($key, $value, $ttl = 0)
|
22 |
+
{
|
23 |
+
$this->cache->set($key, $value, $ttl);
|
24 |
+
}
|
25 |
+
|
26 |
+
public function remove($key)
|
27 |
+
{
|
28 |
+
$this->cache->delete($key);
|
29 |
+
}
|
30 |
+
}
|
lib/Aws/Aws/PsrCacheAdapter.php
ADDED
@@ -0,0 +1,38 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws;
|
3 |
+
|
4 |
+
use Psr\Cache\CacheItemPoolInterface;
|
5 |
+
|
6 |
+
class PsrCacheAdapter implements CacheInterface
|
7 |
+
{
|
8 |
+
/** @var CacheItemPoolInterface */
|
9 |
+
private $pool;
|
10 |
+
|
11 |
+
public function __construct(CacheItemPoolInterface $pool)
|
12 |
+
{
|
13 |
+
$this->pool = $pool;
|
14 |
+
}
|
15 |
+
|
16 |
+
public function get($key)
|
17 |
+
{
|
18 |
+
$item = $this->pool->getItem($key);
|
19 |
+
|
20 |
+
return $item->isHit() ? $item->get() : null;
|
21 |
+
}
|
22 |
+
|
23 |
+
public function set($key, $value, $ttl = 0)
|
24 |
+
{
|
25 |
+
$item = $this->pool->getItem($key);
|
26 |
+
$item->set($value);
|
27 |
+
if ($ttl > 0) {
|
28 |
+
$item->expiresAfter($ttl);
|
29 |
+
}
|
30 |
+
|
31 |
+
$this->pool->save($item);
|
32 |
+
}
|
33 |
+
|
34 |
+
public function remove($key)
|
35 |
+
{
|
36 |
+
$this->pool->deleteItem($key);
|
37 |
+
}
|
38 |
+
}
|
lib/Aws/Aws/ResponseContainerInterface.php
ADDED
@@ -0,0 +1,13 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Aws;
|
4 |
+
|
5 |
+
interface ResponseContainerInterface
|
6 |
+
{
|
7 |
+
/**
|
8 |
+
* Get the received HTTP response if any.
|
9 |
+
*
|
10 |
+
* @return ResponseInterface|null
|
11 |
+
*/
|
12 |
+
public function getResponse();
|
13 |
+
}
|
lib/Aws/Aws/Result.php
ADDED
@@ -0,0 +1,57 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws;
|
3 |
+
|
4 |
+
use JmesPath\Env as JmesPath;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* AWS result.
|
8 |
+
*/
|
9 |
+
class Result implements ResultInterface, MonitoringEventsInterface
|
10 |
+
{
|
11 |
+
use HasDataTrait;
|
12 |
+
use HasMonitoringEventsTrait;
|
13 |
+
|
14 |
+
public function __construct(array $data = [])
|
15 |
+
{
|
16 |
+
$this->data = $data;
|
17 |
+
}
|
18 |
+
|
19 |
+
public function hasKey($name)
|
20 |
+
{
|
21 |
+
return isset($this->data[$name]);
|
22 |
+
}
|
23 |
+
|
24 |
+
public function get($key)
|
25 |
+
{
|
26 |
+
return $this[$key];
|
27 |
+
}
|
28 |
+
|
29 |
+
public function search($expression)
|
30 |
+
{
|
31 |
+
return JmesPath::search($expression, $this->toArray());
|
32 |
+
}
|
33 |
+
|
34 |
+
public function __toString()
|
35 |
+
{
|
36 |
+
$jsonData = json_encode($this->toArray(), JSON_PRETTY_PRINT);
|
37 |
+
return <<<EOT
|
38 |
+
Model Data
|
39 |
+
----------
|
40 |
+
Data can be retrieved from the model object using the get() method of the
|
41 |
+
model (e.g., `\$result->get(\$key)`) or "accessing the result like an
|
42 |
+
associative array (e.g. `\$result['key']`). You can also execute JMESPath
|
43 |
+
expressions on the result data using the search() method.
|
44 |
+
|
45 |
+
{$jsonData}
|
46 |
+
|
47 |
+
EOT;
|
48 |
+
}
|
49 |
+
|
50 |
+
/**
|
51 |
+
* @deprecated
|
52 |
+
*/
|
53 |
+
public function getPath($path)
|
54 |
+
{
|
55 |
+
return $this->search(str_replace('/', '.', $path));
|
56 |
+
}
|
57 |
+
}
|
lib/Aws/Aws/ResultInterface.php
ADDED
@@ -0,0 +1,54 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Represents an AWS result object that is returned from executing an operation.
|
6 |
+
*/
|
7 |
+
interface ResultInterface extends \ArrayAccess, \IteratorAggregate, \Countable
|
8 |
+
{
|
9 |
+
/**
|
10 |
+
* Provides debug information about the result object
|
11 |
+
*
|
12 |
+
* @return string
|
13 |
+
*/
|
14 |
+
public function __toString();
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Convert the result to an array.
|
18 |
+
*
|
19 |
+
* @return array
|
20 |
+
*/
|
21 |
+
public function toArray();
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Check if the model contains a key by name
|
25 |
+
*
|
26 |
+
* @param string $name Name of the key to retrieve
|
27 |
+
*
|
28 |
+
* @return bool
|
29 |
+
*/
|
30 |
+
public function hasKey($name);
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Get a specific key value from the result model.
|
34 |
+
*
|
35 |
+
* @param string $key Key to retrieve.
|
36 |
+
*
|
37 |
+
* @return mixed|null Value of the key or NULL if not found.
|
38 |
+
*/
|
39 |
+
public function get($key);
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Returns the result of executing a JMESPath expression on the contents
|
43 |
+
* of the Result model.
|
44 |
+
*
|
45 |
+
* $result = $client->execute($command);
|
46 |
+
* $jpResult = $result->search('foo.*.bar[?baz > `10`]');
|
47 |
+
*
|
48 |
+
* @param string $expression JMESPath expression to execute
|
49 |
+
*
|
50 |
+
* @return mixed Returns the result of the JMESPath expression.
|
51 |
+
* @link http://jmespath.readthedocs.org/en/latest/ JMESPath documentation
|
52 |
+
*/
|
53 |
+
public function search($expression);
|
54 |
+
};
|
lib/Aws/Aws/ResultPaginator.php
ADDED
@@ -0,0 +1,169 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws;
|
3 |
+
|
4 |
+
use GuzzleHttp\Promise;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Iterator that yields each page of results of a pageable operation.
|
8 |
+
*/
|
9 |
+
class ResultPaginator implements \Iterator
|
10 |
+
{
|
11 |
+
/** @var AwsClientInterface Client performing operations. */
|
12 |
+
private $client;
|
13 |
+
|
14 |
+
/** @var string Name of the operation being paginated. */
|
15 |
+
private $operation;
|
16 |
+
|
17 |
+
/** @var array Args for the operation. */
|
18 |
+
private $args;
|
19 |
+
|
20 |
+
/** @var array Configuration for the paginator. */
|
21 |
+
private $config;
|
22 |
+
|
23 |
+
/** @var Result Most recent result from the client. */
|
24 |
+
private $result;
|
25 |
+
|
26 |
+
/** @var string|array Next token to use for pagination. */
|
27 |
+
private $nextToken;
|
28 |
+
|
29 |
+
/** @var int Number of operations/requests performed. */
|
30 |
+
private $requestCount = 0;
|
31 |
+
|
32 |
+
/**
|
33 |
+
* @param AwsClientInterface $client
|
34 |
+
* @param string $operation
|
35 |
+
* @param array $args
|
36 |
+
* @param array $config
|
37 |
+
*/
|
38 |
+
public function __construct(
|
39 |
+
AwsClientInterface $client,
|
40 |
+
$operation,
|
41 |
+
array $args,
|
42 |
+
array $config
|
43 |
+
) {
|
44 |
+
$this->client = $client;
|
45 |
+
$this->operation = $operation;
|
46 |
+
$this->args = $args;
|
47 |
+
$this->config = $config;
|
48 |
+
}
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Runs a paginator asynchronously and uses a callback to handle results.
|
52 |
+
*
|
53 |
+
* The callback should have the signature: function (Aws\Result $result).
|
54 |
+
* A non-null return value from the callback will be yielded by the
|
55 |
+
* promise. This means that you can return promises from the callback that
|
56 |
+
* will need to be resolved before continuing iteration over the remaining
|
57 |
+
* items, essentially merging in other promises to the iteration. The last
|
58 |
+
* non-null value returned by the callback will be the result that fulfills
|
59 |
+
* the promise to any downstream promises.
|
60 |
+
*
|
61 |
+
* @param callable $handleResult Callback for handling each page of results.
|
62 |
+
* The callback accepts the result that was
|
63 |
+
* yielded as a single argument. If the
|
64 |
+
* callback returns a promise, the promise
|
65 |
+
* will be merged into the coroutine.
|
66 |
+
*
|
67 |
+
* @return Promise\Promise
|
68 |
+
*/
|
69 |
+
public function each(callable $handleResult)
|
70 |
+
{
|
71 |
+
return Promise\coroutine(function () use ($handleResult) {
|
72 |
+
$nextToken = null;
|
73 |
+
do {
|
74 |
+
$command = $this->createNextCommand($this->args, $nextToken);
|
75 |
+
$result = (yield $this->client->executeAsync($command));
|
76 |
+
$nextToken = $this->determineNextToken($result);
|
77 |
+
$retVal = $handleResult($result);
|
78 |
+
if ($retVal !== null) {
|
79 |
+
yield Promise\promise_for($retVal);
|
80 |
+
}
|
81 |
+
} while ($nextToken);
|
82 |
+
});
|
83 |
+
}
|
84 |
+
|
85 |
+
/**
|
86 |
+
* Returns an iterator that iterates over the values of applying a JMESPath
|
87 |
+
* search to each result yielded by the iterator as a flat sequence.
|
88 |
+
*
|
89 |
+
* @param string $expression JMESPath expression to apply to each result.
|
90 |
+
*
|
91 |
+
* @return \Iterator
|
92 |
+
*/
|
93 |
+
public function search($expression)
|
94 |
+
{
|
95 |
+
// Apply JMESPath expression on each result, but as a flat sequence.
|
96 |
+
return flatmap($this, function (Result $result) use ($expression) {
|
97 |
+
return (array) $result->search($expression);
|
98 |
+
});
|
99 |
+
}
|
100 |
+
|
101 |
+
/**
|
102 |
+
* @return Result
|
103 |
+
*/
|
104 |
+
public function current()
|
105 |
+
{
|
106 |
+
return $this->valid() ? $this->result : false;
|
107 |
+
}
|
108 |
+
|
109 |
+
public function key()
|
110 |
+
{
|
111 |
+
return $this->valid() ? $this->requestCount - 1 : null;
|
112 |
+
}
|
113 |
+
|
114 |
+
public function next()
|
115 |
+
{
|
116 |
+
$this->result = null;
|
117 |
+
}
|
118 |
+
|
119 |
+
public function valid()
|
120 |
+
{
|
121 |
+
if ($this->result) {
|
122 |
+
return true;
|
123 |
+
}
|
124 |
+
|
125 |
+
if ($this->nextToken || !$this->requestCount) {
|
126 |
+
$this->result = $this->client->execute(
|
127 |
+
$this->createNextCommand($this->args, $this->nextToken)
|
128 |
+
);
|
129 |
+
$this->nextToken = $this->determineNextToken($this->result);
|
130 |
+
$this->requestCount++;
|
131 |
+
return true;
|
132 |
+
}
|
133 |
+
|
134 |
+
return false;
|
135 |
+
}
|
136 |
+
|
137 |
+
public function rewind()
|
138 |
+
{
|
139 |
+
$this->requestCount = 0;
|
140 |
+
$this->nextToken = null;
|
141 |
+
$this->result = null;
|
142 |
+
}
|
143 |
+
|
144 |
+
private function createNextCommand(array $args, array $nextToken = null)
|
145 |
+
{
|
146 |
+
return $this->client->getCommand($this->operation, array_merge($args, ($nextToken ?: [])));
|
147 |
+
}
|
148 |
+
|
149 |
+
private function determineNextToken(Result $result)
|
150 |
+
{
|
151 |
+
if (!$this->config['output_token']) {
|
152 |
+
return null;
|
153 |
+
}
|
154 |
+
|
155 |
+
if ($this->config['more_results']
|
156 |
+
&& !$result->search($this->config['more_results'])
|
157 |
+
) {
|
158 |
+
return null;
|
159 |
+
}
|
160 |
+
|
161 |
+
$nextToken = is_scalar($this->config['output_token'])
|
162 |
+
? [$this->config['input_token'] => $this->config['output_token']]
|
163 |
+
: array_combine($this->config['input_token'], $this->config['output_token']);
|
164 |
+
|
165 |
+
return array_filter(array_map(function ($outputToken) use ($result) {
|
166 |
+
return $result->search($outputToken);
|
167 |
+
}, $nextToken));
|
168 |
+
}
|
169 |
+
}
|
lib/Aws/Aws/RetryMiddleware.php
ADDED
@@ -0,0 +1,315 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws;
|
3 |
+
|
4 |
+
use Aws\Exception\AwsException;
|
5 |
+
use GuzzleHttp\Exception\RequestException;
|
6 |
+
use Psr\Http\Message\RequestInterface;
|
7 |
+
use GuzzleHttp\Promise\PromiseInterface;
|
8 |
+
use GuzzleHttp\Promise;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* @internal Middleware that retries failures.
|
12 |
+
*/
|
13 |
+
class RetryMiddleware
|
14 |
+
{
|
15 |
+
private static $retryStatusCodes = [
|
16 |
+
500 => true,
|
17 |
+
502 => true,
|
18 |
+
503 => true,
|
19 |
+
504 => true
|
20 |
+
];
|
21 |
+
|
22 |
+
private static $retryCodes = [
|
23 |
+
// Throttling error
|
24 |
+
'RequestLimitExceeded' => true,
|
25 |
+
'Throttling' => true,
|
26 |
+
'ThrottlingException' => true,
|
27 |
+
'ThrottledException' => true,
|
28 |
+
'ProvisionedThroughputExceededException' => true,
|
29 |
+
'RequestThrottled' => true,
|
30 |
+
'BandwidthLimitExceeded' => true,
|
31 |
+
'RequestThrottledException' => true,
|
32 |
+
];
|
33 |
+
|
34 |
+
private $decider;
|
35 |
+
private $delay;
|
36 |
+
private $nextHandler;
|
37 |
+
private $collectStats;
|
38 |
+
|
39 |
+
public function __construct(
|
40 |
+
callable $decider,
|
41 |
+
callable $delay,
|
42 |
+
callable $nextHandler,
|
43 |
+
$collectStats = false
|
44 |
+
) {
|
45 |
+
$this->decider = $decider;
|
46 |
+
$this->delay = $delay;
|
47 |
+
$this->nextHandler = $nextHandler;
|
48 |
+
$this->collectStats = (bool) $collectStats;
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Creates a default AWS retry decider function.
|
53 |
+
*
|
54 |
+
* The optional $additionalRetryConfig parameter is an associative array
|
55 |
+
* that specifies additional retry conditions on top of the ones specified
|
56 |
+
* by default by the Aws\RetryMiddleware class, with the following keys:
|
57 |
+
*
|
58 |
+
* - errorCodes: (string[]) An indexed array of AWS exception codes to retry.
|
59 |
+
* Optional.
|
60 |
+
* - statusCodes: (int[]) An indexed array of HTTP status codes to retry.
|
61 |
+
* Optional.
|
62 |
+
* - curlErrors: (int[]) An indexed array of Curl error codes to retry. Note
|
63 |
+
* these should be valid Curl constants. Optional.
|
64 |
+
*
|
65 |
+
* @param int $maxRetries
|
66 |
+
* @param array $additionalRetryConfig
|
67 |
+
* @return callable
|
68 |
+
*/
|
69 |
+
public static function createDefaultDecider(
|
70 |
+
$maxRetries = 3,
|
71 |
+
$additionalRetryConfig = []
|
72 |
+
) {
|
73 |
+
$retryCurlErrors = [];
|
74 |
+
if (extension_loaded('curl')) {
|
75 |
+
$retryCurlErrors[CURLE_RECV_ERROR] = true;
|
76 |
+
}
|
77 |
+
|
78 |
+
return function (
|
79 |
+
$retries,
|
80 |
+
CommandInterface $command,
|
81 |
+
RequestInterface $request,
|
82 |
+
ResultInterface $result = null,
|
83 |
+
$error = null
|
84 |
+
) use ($maxRetries, $retryCurlErrors, $additionalRetryConfig) {
|
85 |
+
// Allow command-level options to override this value
|
86 |
+
$maxRetries = null !== $command['@retries'] ?
|
87 |
+
$command['@retries']
|
88 |
+
: $maxRetries;
|
89 |
+
|
90 |
+
$isRetryable = self::isRetryable(
|
91 |
+
$result,
|
92 |
+
$error,
|
93 |
+
$retryCurlErrors,
|
94 |
+
$additionalRetryConfig
|
95 |
+
);
|
96 |
+
|
97 |
+
if ($retries >= $maxRetries) {
|
98 |
+
if (!empty($error)
|
99 |
+
&& $error instanceof AwsException
|
100 |
+
&& $isRetryable
|
101 |
+
) {
|
102 |
+
$error->setMaxRetriesExceeded();
|
103 |
+
}
|
104 |
+
return false;
|
105 |
+
}
|
106 |
+
|
107 |
+
return $isRetryable;
|
108 |
+
};
|
109 |
+
}
|
110 |
+
|
111 |
+
private static function isRetryable(
|
112 |
+
$result,
|
113 |
+
$error,
|
114 |
+
$retryCurlErrors,
|
115 |
+
$additionalRetryConfig = []
|
116 |
+
) {
|
117 |
+
$errorCodes = self::$retryCodes;
|
118 |
+
if (!empty($additionalRetryConfig['errorCodes'])
|
119 |
+
&& is_array($additionalRetryConfig['errorCodes'])
|
120 |
+
) {
|
121 |
+
foreach($additionalRetryConfig['errorCodes'] as $code) {
|
122 |
+
$errorCodes[$code] = true;
|
123 |
+
}
|
124 |
+
}
|
125 |
+
|
126 |
+
$statusCodes = self::$retryStatusCodes;
|
127 |
+
if (!empty($additionalRetryConfig['statusCodes'])
|
128 |
+
&& is_array($additionalRetryConfig['statusCodes'])
|
129 |
+
) {
|
130 |
+
foreach($additionalRetryConfig['statusCodes'] as $code) {
|
131 |
+
$statusCodes[$code] = true;
|
132 |
+
}
|
133 |
+
}
|
134 |
+
|
135 |
+
if (!empty($additionalRetryConfig['curlErrors'])
|
136 |
+
&& is_array($additionalRetryConfig['curlErrors'])
|
137 |
+
) {
|
138 |
+
foreach($additionalRetryConfig['curlErrors'] as $code) {
|
139 |
+
$retryCurlErrors[$code] = true;
|
140 |
+
}
|
141 |
+
}
|
142 |
+
|
143 |
+
if (!$error) {
|
144 |
+
return isset($statusCodes[$result['@metadata']['statusCode']]);
|
145 |
+
}
|
146 |
+
|
147 |
+
if (!($error instanceof AwsException)) {
|
148 |
+
return false;
|
149 |
+
}
|
150 |
+
|
151 |
+
if ($error->isConnectionError()) {
|
152 |
+
return true;
|
153 |
+
}
|
154 |
+
|
155 |
+
if (isset($errorCodes[$error->getAwsErrorCode()])) {
|
156 |
+
return true;
|
157 |
+
}
|
158 |
+
|
159 |
+
if (isset($statusCodes[$error->getStatusCode()])) {
|
160 |
+
return true;
|
161 |
+
}
|
162 |
+
|
163 |
+
if (count($retryCurlErrors)
|
164 |
+
&& ($previous = $error->getPrevious())
|
165 |
+
&& $previous instanceof RequestException
|
166 |
+
) {
|
167 |
+
if (method_exists($previous, 'getHandlerContext')) {
|
168 |
+
$context = $previous->getHandlerContext();
|
169 |
+
return !empty($context['errno'])
|
170 |
+
&& isset($retryCurlErrors[$context['errno']]);
|
171 |
+
}
|
172 |
+
|
173 |
+
$message = $previous->getMessage();
|
174 |
+
foreach (array_keys($retryCurlErrors) as $curlError) {
|
175 |
+
if (strpos($message, 'cURL error ' . $curlError . ':') === 0) {
|
176 |
+
return true;
|
177 |
+
}
|
178 |
+
}
|
179 |
+
}
|
180 |
+
|
181 |
+
return false;
|
182 |
+
}
|
183 |
+
|
184 |
+
/**
|
185 |
+
* Delay function that calculates an exponential delay.
|
186 |
+
*
|
187 |
+
* Exponential backoff with jitter, 100ms base, 20 sec ceiling
|
188 |
+
*
|
189 |
+
* @param $retries - The number of retries that have already been attempted
|
190 |
+
*
|
191 |
+
* @return int
|
192 |
+
*
|
193 |
+
* @link https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/
|
194 |
+
*/
|
195 |
+
public static function exponentialDelay($retries)
|
196 |
+
{
|
197 |
+
return mt_rand(0, (int) min(20000, (int) pow(2, $retries) * 100));
|
198 |
+
}
|
199 |
+
|
200 |
+
/**
|
201 |
+
* @param CommandInterface $command
|
202 |
+
* @param RequestInterface $request
|
203 |
+
*
|
204 |
+
* @return PromiseInterface
|
205 |
+
*/
|
206 |
+
public function __invoke(
|
207 |
+
CommandInterface $command,
|
208 |
+
RequestInterface $request = null
|
209 |
+
) {
|
210 |
+
$retries = 0;
|
211 |
+
$requestStats = [];
|
212 |
+
$monitoringEvents = [];
|
213 |
+
$handler = $this->nextHandler;
|
214 |
+
$decider = $this->decider;
|
215 |
+
$delay = $this->delay;
|
216 |
+
|
217 |
+
$request = $this->addRetryHeader($request, 0, 0);
|
218 |
+
|
219 |
+
$g = function ($value) use (
|
220 |
+
$handler,
|
221 |
+
$decider,
|
222 |
+
$delay,
|
223 |
+
$command,
|
224 |
+
$request,
|
225 |
+
&$retries,
|
226 |
+
&$requestStats,
|
227 |
+
&$monitoringEvents,
|
228 |
+
&$g
|
229 |
+
) {
|
230 |
+
$this->updateHttpStats($value, $requestStats);
|
231 |
+
|
232 |
+
if ($value instanceof MonitoringEventsInterface) {
|
233 |
+
$reversedEvents = array_reverse($monitoringEvents);
|
234 |
+
$monitoringEvents = array_merge($monitoringEvents, $value->getMonitoringEvents());
|
235 |
+
foreach ($reversedEvents as $event) {
|
236 |
+
$value->prependMonitoringEvent($event);
|
237 |
+
}
|
238 |
+
}
|
239 |
+
if ($value instanceof \Exception || $value instanceof \Throwable) {
|
240 |
+
if (!$decider($retries, $command, $request, null, $value)) {
|
241 |
+
return Promise\rejection_for(
|
242 |
+
$this->bindStatsToReturn($value, $requestStats)
|
243 |
+
);
|
244 |
+
}
|
245 |
+
} elseif ($value instanceof ResultInterface
|
246 |
+
&& !$decider($retries, $command, $request, $value, null)
|
247 |
+
) {
|
248 |
+
return $this->bindStatsToReturn($value, $requestStats);
|
249 |
+
}
|
250 |
+
|
251 |
+
// Delay fn is called with 0, 1, ... so increment after the call.
|
252 |
+
$delayBy = $delay($retries++);
|
253 |
+
$command['@http']['delay'] = $delayBy;
|
254 |
+
if ($this->collectStats) {
|
255 |
+
$this->updateStats($retries, $delayBy, $requestStats);
|
256 |
+
}
|
257 |
+
|
258 |
+
// Update retry header with retry count and delayBy
|
259 |
+
$request = $this->addRetryHeader($request, $retries, $delayBy);
|
260 |
+
|
261 |
+
return $handler($command, $request)->then($g, $g);
|
262 |
+
};
|
263 |
+
|
264 |
+
return $handler($command, $request)->then($g, $g);
|
265 |
+
}
|
266 |
+
|
267 |
+
private function addRetryHeader($request, $retries, $delayBy)
|
268 |
+
{
|
269 |
+
return $request->withHeader('aws-sdk-retry', "{$retries}/{$delayBy}");
|
270 |
+
}
|
271 |
+
|
272 |
+
private function updateStats($retries, $delay, array &$stats)
|
273 |
+
{
|
274 |
+
if (!isset($stats['total_retry_delay'])) {
|
275 |
+
$stats['total_retry_delay'] = 0;
|
276 |
+
}
|
277 |
+
|
278 |
+
$stats['total_retry_delay'] += $delay;
|
279 |
+
$stats['retries_attempted'] = $retries;
|
280 |
+
}
|
281 |
+
|
282 |
+
private function updateHttpStats($value, array &$stats)
|
283 |
+
{
|
284 |
+
if (empty($stats['http'])) {
|
285 |
+
$stats['http'] = [];
|
286 |
+
}
|
287 |
+
|
288 |
+
if ($value instanceof AwsException) {
|
289 |
+
$resultStats = isset($value->getTransferInfo('http')[0])
|
290 |
+
? $value->getTransferInfo('http')[0]
|
291 |
+
: [];
|
292 |
+
$stats['http'] []= $resultStats;
|
293 |
+
} elseif ($value instanceof ResultInterface) {
|
294 |
+
$resultStats = isset($value['@metadata']['transferStats']['http'][0])
|
295 |
+
? $value['@metadata']['transferStats']['http'][0]
|
296 |
+
: [];
|
297 |
+
$stats['http'] []= $resultStats;
|
298 |
+
}
|
299 |
+
}
|
300 |
+
|
301 |
+
private function bindStatsToReturn($return, array $stats)
|
302 |
+
{
|
303 |
+
if ($return instanceof ResultInterface) {
|
304 |
+
if (!isset($return['@metadata'])) {
|
305 |
+
$return['@metadata'] = [];
|
306 |
+
}
|
307 |
+
|
308 |
+
$return['@metadata']['transferStats'] = $stats;
|
309 |
+
} elseif ($return instanceof AwsException) {
|
310 |
+
$return->setTransferInfo($stats);
|
311 |
+
}
|
312 |
+
|
313 |
+
return $return;
|
314 |
+
}
|
315 |
+
}
|
lib/Aws/Aws/S3/AmbiguousSuccessParser.php
ADDED
@@ -0,0 +1,68 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\S3;
|
3 |
+
|
4 |
+
use Aws\Api\Parser\AbstractParser;
|
5 |
+
use Aws\Api\StructureShape;
|
6 |
+
use Aws\CommandInterface;
|
7 |
+
use Aws\Exception\AwsException;
|
8 |
+
use Psr\Http\Message\ResponseInterface;
|
9 |
+
use Psr\Http\Message\StreamInterface;
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Converts errors returned with a status code of 200 to a retryable error type.
|
13 |
+
*
|
14 |
+
* @internal
|
15 |
+
*/
|
16 |
+
class AmbiguousSuccessParser extends AbstractParser
|
17 |
+
{
|
18 |
+
private static $ambiguousSuccesses = [
|
19 |
+
'UploadPartCopy' => true,
|
20 |
+
'CopyObject' => true,
|
21 |
+
'CompleteMultipartUpload' => true,
|
22 |
+
];
|
23 |
+
|
24 |
+
/** @var callable */
|
25 |
+
private $errorParser;
|
26 |
+
/** @var string */
|
27 |
+
private $exceptionClass;
|
28 |
+
|
29 |
+
public function __construct(
|
30 |
+
callable $parser,
|
31 |
+
callable $errorParser,
|
32 |
+
$exceptionClass = AwsException::class
|
33 |
+
) {
|
34 |
+
$this->parser = $parser;
|
35 |
+
$this->errorParser = $errorParser;
|
36 |
+
$this->exceptionClass = $exceptionClass;
|
37 |
+
}
|
38 |
+
|
39 |
+
public function __invoke(
|
40 |
+
CommandInterface $command,
|
41 |
+
ResponseInterface $response
|
42 |
+
) {
|
43 |
+
if (200 === $response->getStatusCode()
|
44 |
+
&& isset(self::$ambiguousSuccesses[$command->getName()])
|
45 |
+
) {
|
46 |
+
$errorParser = $this->errorParser;
|
47 |
+
$parsed = $errorParser($response);
|
48 |
+
if (isset($parsed['code']) && isset($parsed['message'])) {
|
49 |
+
throw new $this->exceptionClass(
|
50 |
+
$parsed['message'],
|
51 |
+
$command,
|
52 |
+
['connection_error' => true]
|
53 |
+
);
|
54 |
+
}
|
55 |
+
}
|
56 |
+
|
57 |
+
$fn = $this->parser;
|
58 |
+
return $fn($command, $response);
|
59 |
+
}
|
60 |
+
|
61 |
+
public function parseMemberFromStream(
|
62 |
+
StreamInterface $stream,
|
63 |
+
StructureShape $member,
|
64 |
+
$response
|
65 |
+
) {
|
66 |
+
return $this->parser->parseMemberFromStream($stream, $member, $response);
|
67 |
+
}
|
68 |
+
}
|
lib/Aws/Aws/S3/ApplyChecksumMiddleware.php
ADDED
@@ -0,0 +1,78 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\S3;
|
3 |
+
|
4 |
+
use Aws\CommandInterface;
|
5 |
+
use GuzzleHttp\Psr7;
|
6 |
+
use Psr\Http\Message\RequestInterface;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Apply required or optional MD5s to requests before sending.
|
10 |
+
*
|
11 |
+
* IMPORTANT: This middleware must be added after the "build" step.
|
12 |
+
*
|
13 |
+
* @internal
|
14 |
+
*/
|
15 |
+
class ApplyChecksumMiddleware
|
16 |
+
{
|
17 |
+
private static $md5 = [
|
18 |
+
'DeleteObjects',
|
19 |
+
'PutBucketCors',
|
20 |
+
'PutBucketLifecycle',
|
21 |
+
'PutBucketLifecycleConfiguration',
|
22 |
+
'PutBucketPolicy',
|
23 |
+
'PutBucketTagging',
|
24 |
+
'PutBucketReplication',
|
25 |
+
'PutObjectLegalHold',
|
26 |
+
'PutObjectRetention',
|
27 |
+
'PutObjectLockConfiguration',
|
28 |
+
];
|
29 |
+
|
30 |
+
private static $sha256 = [
|
31 |
+
'PutObject',
|
32 |
+
'UploadPart',
|
33 |
+
];
|
34 |
+
|
35 |
+
private $nextHandler;
|
36 |
+
|
37 |
+
/**
|
38 |
+
* Create a middleware wrapper function.
|
39 |
+
*
|
40 |
+
* @return callable
|
41 |
+
*/
|
42 |
+
public static function wrap()
|
43 |
+
{
|
44 |
+
return function (callable $handler) {
|
45 |
+
return new self($handler);
|
46 |
+
};
|
47 |
+
}
|
48 |
+
|
49 |
+
public function __construct(callable $nextHandler)
|
50 |
+
{
|
51 |
+
$this->nextHandler = $nextHandler;
|
52 |
+
}
|
53 |
+
|
54 |
+
public function __invoke(
|
55 |
+
CommandInterface $command,
|
56 |
+
RequestInterface $request
|
57 |
+
) {
|
58 |
+
$next = $this->nextHandler;
|
59 |
+
$name = $command->getName();
|
60 |
+
$body = $request->getBody();
|
61 |
+
|
62 |
+
if (in_array($name, self::$md5) && !$request->hasHeader('Content-MD5')) {
|
63 |
+
// Set the content MD5 header for operations that require it.
|
64 |
+
$request = $request->withHeader(
|
65 |
+
'Content-MD5',
|
66 |
+
base64_encode(Psr7\hash($body, 'md5', true))
|
67 |
+
);
|
68 |
+
} elseif (in_array($name, self::$sha256) && $command['ContentSHA256']) {
|
69 |
+
// Set the content hash header if provided in the parameters.
|
70 |
+
$request = $request->withHeader(
|
71 |
+
'X-Amz-Content-Sha256',
|
72 |
+
$command['ContentSHA256']
|
73 |
+
);
|
74 |
+
}
|
75 |
+
|
76 |
+
return $next($command, $request);
|
77 |
+
}
|
78 |
+
}
|
lib/Aws/Aws/S3/BatchDelete.php
ADDED
@@ -0,0 +1,237 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\S3;
|
3 |
+
|
4 |
+
use Aws\AwsClientInterface;
|
5 |
+
use Aws\S3\Exception\DeleteMultipleObjectsException;
|
6 |
+
use GuzzleHttp\Promise;
|
7 |
+
use GuzzleHttp\Promise\PromisorInterface;
|
8 |
+
use GuzzleHttp\Promise\PromiseInterface;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Efficiently deletes many objects from a single Amazon S3 bucket using an
|
12 |
+
* iterator that yields keys. Deletes are made using the DeleteObjects API
|
13 |
+
* operation.
|
14 |
+
*
|
15 |
+
* $s3 = new Aws\S3\Client([
|
16 |
+
* 'region' => 'us-west-2',
|
17 |
+
* 'version' => 'latest'
|
18 |
+
* ]);
|
19 |
+
*
|
20 |
+
* $listObjectsParams = ['Bucket' => 'foo', 'Prefix' => 'starts/with/'];
|
21 |
+
* $delete = Aws\S3\BatchDelete::fromListObjects($s3, $listObjectsParams);
|
22 |
+
* // Asynchronously delete
|
23 |
+
* $promise = $delete->promise();
|
24 |
+
* // Force synchronous completion
|
25 |
+
* $delete->delete();
|
26 |
+
*
|
27 |
+
* When using one of the batch delete creational static methods, you can supply
|
28 |
+
* an associative array of options:
|
29 |
+
*
|
30 |
+
* - before: Function invoked before executing a command. The function is
|
31 |
+
* passed the command that is about to be executed. This can be useful
|
32 |
+
* for logging, adding custom request headers, etc.
|
33 |
+
* - batch_size: The size of each delete batch. Defaults to 1000.
|
34 |
+
*
|
35 |
+
* @link http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html
|
36 |
+
*/
|
37 |
+
class BatchDelete implements PromisorInterface
|
38 |
+
{
|
39 |
+
private $bucket;
|
40 |
+
/** @var AwsClientInterface */
|
41 |
+
private $client;
|
42 |
+
/** @var callable */
|
43 |
+
private $before;
|
44 |
+
/** @var PromiseInterface */
|
45 |
+
private $cachedPromise;
|
46 |
+
/** @var callable */
|
47 |
+
private $promiseCreator;
|
48 |
+
private $batchSize = 1000;
|
49 |
+
private $queue = [];
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Creates a BatchDelete object from all of the paginated results of a
|
53 |
+
* ListObjects operation. Each result that is returned by the ListObjects
|
54 |
+
* operation will be deleted.
|
55 |
+
*
|
56 |
+
* @param AwsClientInterface $client AWS Client to use.
|
57 |
+
* @param array $listObjectsParams ListObjects API parameters
|
58 |
+
* @param array $options BatchDelete options.
|
59 |
+
*
|
60 |
+
* @return BatchDelete
|
61 |
+
*/
|
62 |
+
public static function fromListObjects(
|
63 |
+
AwsClientInterface $client,
|
64 |
+
array $listObjectsParams,
|
65 |
+
array $options = []
|
66 |
+
) {
|
67 |
+
$iter = $client->getPaginator('ListObjects', $listObjectsParams);
|
68 |
+
$bucket = $listObjectsParams['Bucket'];
|
69 |
+
$fn = function (BatchDelete $that) use ($iter) {
|
70 |
+
return $iter->each(function ($result) use ($that) {
|
71 |
+
$promises = [];
|
72 |
+
if (is_array($result['Contents'])) {
|
73 |
+
foreach ($result['Contents'] as $object) {
|
74 |
+
if ($promise = $that->enqueue($object)) {
|
75 |
+
$promises[] = $promise;
|
76 |
+
}
|
77 |
+
}
|
78 |
+
}
|
79 |
+
return $promises ? Promise\all($promises) : null;
|
80 |
+
});
|
81 |
+
};
|
82 |
+
|
83 |
+
return new self($client, $bucket, $fn, $options);
|
84 |
+
}
|
85 |
+
|
86 |
+
/**
|
87 |
+
* Creates a BatchDelete object from an iterator that yields results.
|
88 |
+
*
|
89 |
+
* @param AwsClientInterface $client AWS Client to use to execute commands
|
90 |
+
* @param string $bucket Bucket where the objects are stored
|
91 |
+
* @param \Iterator $iter Iterator that yields assoc arrays
|
92 |
+
* @param array $options BatchDelete options
|
93 |
+
*
|
94 |
+
* @return BatchDelete
|
95 |
+
*/
|
96 |
+
public static function fromIterator(
|
97 |
+
AwsClientInterface $client,
|
98 |
+
$bucket,
|
99 |
+
\Iterator $iter,
|
100 |
+
array $options = []
|
101 |
+
) {
|
102 |
+
$fn = function (BatchDelete $that) use ($iter) {
|
103 |
+
return Promise\coroutine(function () use ($that, $iter) {
|
104 |
+
foreach ($iter as $obj) {
|
105 |
+
if ($promise = $that->enqueue($obj)) {
|
106 |
+
yield $promise;
|
107 |
+
}
|
108 |
+
}
|
109 |
+
});
|
110 |
+
};
|
111 |
+
|
112 |
+
return new self($client, $bucket, $fn, $options);
|
113 |
+
}
|
114 |
+
|
115 |
+
public function promise()
|
116 |
+
{
|
117 |
+
if (!$this->cachedPromise) {
|
118 |
+
$this->cachedPromise = $this->createPromise();
|
119 |
+
}
|
120 |
+
|
121 |
+
return $this->cachedPromise;
|
122 |
+
}
|
123 |
+
|
124 |
+
/**
|
125 |
+
* Synchronously deletes all of the objects.
|
126 |
+
*
|
127 |
+
* @throws DeleteMultipleObjectsException on error.
|
128 |
+
*/
|
129 |
+
public function delete()
|
130 |
+
{
|
131 |
+
$this->promise()->wait();
|
132 |
+
}
|
133 |
+
|
134 |
+
/**
|
135 |
+
* @param AwsClientInterface $client Client used to transfer the requests
|
136 |
+
* @param string $bucket Bucket to delete from.
|
137 |
+
* @param callable $promiseFn Creates a promise.
|
138 |
+
* @param array $options Hash of options used with the batch
|
139 |
+
*
|
140 |
+
* @throws \InvalidArgumentException if the provided batch_size is <= 0
|
141 |
+
*/
|
142 |
+
private function __construct(
|
143 |
+
AwsClientInterface $client,
|
144 |
+
$bucket,
|
145 |
+
callable $promiseFn,
|
146 |
+
array $options = []
|
147 |
+
) {
|
148 |
+
$this->client = $client;
|
149 |
+
$this->bucket = $bucket;
|
150 |
+
$this->promiseCreator = $promiseFn;
|
151 |
+
|
152 |
+
if (isset($options['before'])) {
|
153 |
+
if (!is_callable($options['before'])) {
|
154 |
+
throw new \InvalidArgumentException('before must be callable');
|
155 |
+
}
|
156 |
+
$this->before = $options['before'];
|
157 |
+
}
|
158 |
+
|
159 |
+
if (isset($options['batch_size'])) {
|
160 |
+
if ($options['batch_size'] <= 0) {
|
161 |
+
throw new \InvalidArgumentException('batch_size is not > 0');
|
162 |
+
}
|
163 |
+
$this->batchSize = min($options['batch_size'], 1000);
|
164 |
+
}
|
165 |
+
}
|
166 |
+
|
167 |
+
private function enqueue(array $obj)
|
168 |
+
{
|
169 |
+
$this->queue[] = $obj;
|
170 |
+
return count($this->queue) >= $this->batchSize
|
171 |
+
? $this->flushQueue()
|
172 |
+
: null;
|
173 |
+
}
|
174 |
+
|
175 |
+
private function flushQueue()
|
176 |
+
{
|
177 |
+
static $validKeys = ['Key' => true, 'VersionId' => true];
|
178 |
+
|
179 |
+
if (count($this->queue) === 0) {
|
180 |
+
return null;
|
181 |
+
}
|
182 |
+
|
183 |
+
$batch = [];
|
184 |
+
while ($obj = array_shift($this->queue)) {
|
185 |
+
$batch[] = array_intersect_key($obj, $validKeys);
|
186 |
+
}
|
187 |
+
|
188 |
+
$command = $this->client->getCommand('DeleteObjects', [
|
189 |
+
'Bucket' => $this->bucket,
|
190 |
+
'Delete' => ['Objects' => $batch]
|
191 |
+
]);
|
192 |
+
|
193 |
+
if ($this->before) {
|
194 |
+
call_user_func($this->before, $command);
|
195 |
+
}
|
196 |
+
|
197 |
+
return $this->client->executeAsync($command)
|
198 |
+
->then(function ($result) {
|
199 |
+
if (!empty($result['Errors'])) {
|
200 |
+
throw new DeleteMultipleObjectsException(
|
201 |
+
$result['Deleted'] ?: [],
|
202 |
+
$result['Errors']
|
203 |
+
);
|
204 |
+
}
|
205 |
+
return $result;
|
206 |
+
});
|
207 |
+
}
|
208 |
+
|
209 |
+
/**
|
210 |
+
* Returns a promise that will clean up any references when it completes.
|
211 |
+
*
|
212 |
+
* @return PromiseInterface
|
213 |
+
*/
|
214 |
+
private function createPromise()
|
215 |
+
{
|
216 |
+
// Create the promise
|
217 |
+
$promise = call_user_func($this->promiseCreator, $this);
|
218 |
+
$this->promiseCreator = null;
|
219 |
+
|
220 |
+
// Cleans up the promise state and references.
|
221 |
+
$cleanup = function () {
|
222 |
+
$this->before = $this->client = $this->queue = null;
|
223 |
+
};
|
224 |
+
|
225 |
+
// When done, ensure cleanup and that any remaining are processed.
|
226 |
+
return $promise->then(
|
227 |
+
function () use ($cleanup) {
|
228 |
+
return Promise\promise_for($this->flushQueue())
|
229 |
+
->then($cleanup);
|
230 |
+
},
|
231 |
+
function ($reason) use ($cleanup) {
|
232 |
+
$cleanup();
|
233 |
+
return Promise\rejection_for($reason);
|
234 |
+
}
|
235 |
+
);
|
236 |
+
}
|
237 |
+
}
|
lib/Aws/Aws/S3/BucketEndpointMiddleware.php
ADDED
@@ -0,0 +1,75 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\S3;
|
3 |
+
|
4 |
+
use Aws\CommandInterface;
|
5 |
+
use Psr\Http\Message\RequestInterface;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Used to update the host used for S3 requests in the case of using a
|
9 |
+
* "bucket endpoint" or CNAME bucket.
|
10 |
+
*
|
11 |
+
* IMPORTANT: this middleware must be added after the "build" step.
|
12 |
+
*
|
13 |
+
* @internal
|
14 |
+
*/
|
15 |
+
class BucketEndpointMiddleware
|
16 |
+
{
|
17 |
+
private static $exclusions = ['GetBucketLocation' => true];
|
18 |
+
private $nextHandler;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Create a middleware wrapper function.
|
22 |
+
*
|
23 |
+
* @return callable
|
24 |
+
*/
|
25 |
+
public static function wrap()
|
26 |
+
{
|
27 |
+
return function (callable $handler) {
|
28 |
+
return new self($handler);
|
29 |
+
};
|
30 |
+
}
|
31 |
+
|
32 |
+
public function __construct(callable $nextHandler)
|
33 |
+
{
|
34 |
+
$this->nextHandler = $nextHandler;
|
35 |
+
}
|
36 |
+
|
37 |
+
public function __invoke(CommandInterface $command, RequestInterface $request)
|
38 |
+
{
|
39 |
+
$nextHandler = $this->nextHandler;
|
40 |
+
$bucket = $command['Bucket'];
|
41 |
+
|
42 |
+
if ($bucket && !isset(self::$exclusions[$command->getName()])) {
|
43 |
+
$request = $this->modifyRequest($request, $command);
|
44 |
+
}
|
45 |
+
|
46 |
+
return $nextHandler($command, $request);
|
47 |
+
}
|
48 |
+
|
49 |
+
private function removeBucketFromPath($path, $bucket)
|
50 |
+
{
|
51 |
+
$len = strlen($bucket) + 1;
|
52 |
+
if (substr($path, 0, $len) === "/{$bucket}") {
|
53 |
+
$path = substr($path, $len);
|
54 |
+
}
|
55 |
+
|
56 |
+
return $path ?: '/';
|
57 |
+
}
|
58 |
+
|
59 |
+
private function modifyRequest(
|
60 |
+
RequestInterface $request,
|
61 |
+
CommandInterface $command
|
62 |
+
) {
|
63 |
+
$uri = $request->getUri();
|
64 |
+
$path = $uri->getPath();
|
65 |
+
$bucket = $command['Bucket'];
|
66 |
+
$path = $this->removeBucketFromPath($path, $bucket);
|
67 |
+
|
68 |
+
// Modify the Key to make sure the key is encoded, but slashes are not.
|
69 |
+
if ($command['Key']) {
|
70 |
+
$path = S3Client::encodeKey(rawurldecode($path));
|
71 |
+
}
|
72 |
+
|
73 |
+
return $request->withUri($uri->withPath($path));
|
74 |
+
}
|
75 |
+
}
|
lib/Aws/Aws/S3/Crypto/CryptoParamsTrait.php
ADDED
@@ -0,0 +1,75 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\S3\Crypto;
|
3 |
+
|
4 |
+
use Aws\Crypto\MaterialsProvider;
|
5 |
+
use Aws\Crypto\MetadataEnvelope;
|
6 |
+
use Aws\Crypto\MetadataStrategyInterface;
|
7 |
+
|
8 |
+
trait CryptoParamsTrait
|
9 |
+
{
|
10 |
+
protected function getMaterialsProvider(array $args)
|
11 |
+
{
|
12 |
+
if ($args['@MaterialsProvider'] instanceof MaterialsProvider) {
|
13 |
+
return $args['@MaterialsProvider'];
|
14 |
+
}
|
15 |
+
|
16 |
+
throw new \InvalidArgumentException('An instance of MaterialsProvider'
|
17 |
+
. ' must be passed in the "MaterialsProvider" field.');
|
18 |
+
}
|
19 |
+
|
20 |
+
protected function getInstructionFileSuffix(array $args)
|
21 |
+
{
|
22 |
+
return !empty($args['@InstructionFileSuffix'])
|
23 |
+
? $args['@InstructionFileSuffix']
|
24 |
+
: $this->instructionFileSuffix;
|
25 |
+
}
|
26 |
+
|
27 |
+
protected function determineGetObjectStrategy(
|
28 |
+
$result,
|
29 |
+
$instructionFileSuffix
|
30 |
+
) {
|
31 |
+
if (isset($result['Metadata'][MetadataEnvelope::CONTENT_KEY_V2_HEADER])) {
|
32 |
+
return new HeadersMetadataStrategy();
|
33 |
+
}
|
34 |
+
|
35 |
+
return new InstructionFileMetadataStrategy(
|
36 |
+
$this->client,
|
37 |
+
$instructionFileSuffix
|
38 |
+
);
|
39 |
+
}
|
40 |
+
|
41 |
+
protected function getMetadataStrategy(array $args, $instructionFileSuffix)
|
42 |
+
{
|
43 |
+
if (!empty($args['@MetadataStrategy'])) {
|
44 |
+
if ($args['@MetadataStrategy'] instanceof MetadataStrategyInterface) {
|
45 |
+
return $args['@MetadataStrategy'];
|
46 |
+
}
|
47 |
+
|
48 |
+
if (is_string($args['@MetadataStrategy'])) {
|
49 |
+
switch ($args['@MetadataStrategy']) {
|
50 |
+
case HeadersMetadataStrategy::class:
|
51 |
+
return new HeadersMetadataStrategy();
|
52 |
+
case InstructionFileMetadataStrategy::class:
|
53 |
+
return new InstructionFileMetadataStrategy(
|
54 |
+
$this->client,
|
55 |
+
$instructionFileSuffix
|
56 |
+
);
|
57 |
+
default:
|
58 |
+
throw new \InvalidArgumentException('Could not match the'
|
59 |
+
. ' specified string in "MetadataStrategy" to a'
|
60 |
+
. ' predefined strategy.');
|
61 |
+
}
|
62 |
+
} else {
|
63 |
+
throw new \InvalidArgumentException('The metadata strategy that'
|
64 |
+
. ' was passed to "MetadataStrategy" was unrecognized.');
|
65 |
+
}
|
66 |
+
} elseif ($instructionFileSuffix) {
|
67 |
+
return new InstructionFileMetadataStrategy(
|
68 |
+
$this->client,
|
69 |
+
$instructionFileSuffix
|
70 |
+
);
|
71 |
+
}
|
72 |
+
|
73 |
+
return null;
|
74 |
+
}
|
75 |
+
}
|
lib/Aws/Aws/S3/Crypto/HeadersMetadataStrategy.php
ADDED
@@ -0,0 +1,52 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\S3\Crypto;
|
3 |
+
|
4 |
+
use \Aws\Crypto\MetadataStrategyInterface;
|
5 |
+
use \Aws\Crypto\MetadataEnvelope;
|
6 |
+
|
7 |
+
class HeadersMetadataStrategy implements MetadataStrategyInterface
|
8 |
+
{
|
9 |
+
/**
|
10 |
+
* Places the information in the MetadataEnvelope in to the Meatadata for
|
11 |
+
* the PutObject request of the encrypted object.
|
12 |
+
*
|
13 |
+
* @param MetadataEnvelope $envelope Encryption data to save according to
|
14 |
+
* the strategy.
|
15 |
+
* @param array $args Arguments for PutObject that can be manipulated to
|
16 |
+
* store strategy related information.
|
17 |
+
*
|
18 |
+
* @return array Updated arguments for PutObject.
|
19 |
+
*/
|
20 |
+
public function save(MetadataEnvelope $envelope, array $args)
|
21 |
+
{
|
22 |
+
foreach ($envelope as $header=>$value) {
|
23 |
+
$args['Metadata'][$header] = $value;
|
24 |
+
}
|
25 |
+
|
26 |
+
return $args;
|
27 |
+
}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Generates a MetadataEnvelope according to the Metadata headers from the
|
31 |
+
* GetObject result.
|
32 |
+
*
|
33 |
+
* @param array $args Arguments from Command and Result that contains
|
34 |
+
* S3 Object information, relevant headers, and command
|
35 |
+
* configuration.
|
36 |
+
*
|
37 |
+
* @return MetadataEnvelope
|
38 |
+
*/
|
39 |
+
public function load(array $args)
|
40 |
+
{
|
41 |
+
$envelope = new MetadataEnvelope();
|
42 |
+
$constantValues = MetadataEnvelope::getConstantValues();
|
43 |
+
|
44 |
+
foreach ($constantValues as $constant) {
|
45 |
+
if (!empty($args['Metadata'][$constant])) {
|
46 |
+
$envelope[$constant] = $args['Metadata'][$constant];
|
47 |
+
}
|
48 |
+
}
|
49 |
+
|
50 |
+
return $envelope;
|
51 |
+
}
|
52 |
+
}
|
lib/Aws/Aws/S3/Crypto/InstructionFileMetadataStrategy.php
ADDED
@@ -0,0 +1,90 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\S3\Crypto;
|
3 |
+
|
4 |
+
use \Aws\Crypto\MetadataStrategyInterface;
|
5 |
+
use \Aws\Crypto\MetadataEnvelope;
|
6 |
+
use \Aws\S3\S3Client;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Stores and reads encryption MetadataEnvelope information in a file on Amazon
|
10 |
+
* S3.
|
11 |
+
*
|
12 |
+
* A file with the contents of a MetadataEnvelope will be created or read from
|
13 |
+
* alongside the base file on Amazon S3. The provided client will be used for
|
14 |
+
* reading or writing this object. A specified suffix (default of '.instruction'
|
15 |
+
* will be applied to each of the operations involved with the instruction file.
|
16 |
+
*
|
17 |
+
* If there is a failure after an instruction file has been uploaded, it will
|
18 |
+
* not be automatically deleted.
|
19 |
+
*/
|
20 |
+
class InstructionFileMetadataStrategy implements MetadataStrategyInterface
|
21 |
+
{
|
22 |
+
const DEFAULT_FILE_SUFFIX = '.instruction';
|
23 |
+
|
24 |
+
private $client;
|
25 |
+
private $suffix;
|
26 |
+
|
27 |
+
/**
|
28 |
+
* @param S3Client $client Client for use in uploading the instruction file.
|
29 |
+
* @param string|null $suffix Optional override suffix for instruction file
|
30 |
+
* object keys.
|
31 |
+
*/
|
32 |
+
public function __construct(S3Client $client, $suffix = null)
|
33 |
+
{
|
34 |
+
$this->suffix = empty($suffix)
|
35 |
+
? self::DEFAULT_FILE_SUFFIX
|
36 |
+
: $suffix;
|
37 |
+
$this->client = $client;
|
38 |
+
}
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Places the information in the MetadataEnvelope to a location on S3.
|
42 |
+
*
|
43 |
+
* @param MetadataEnvelope $envelope Encryption data to save according to
|
44 |
+
* the strategy.
|
45 |
+
* @param array $args Starting arguments for PutObject, used for saving
|
46 |
+
* extra the instruction file.
|
47 |
+
*
|
48 |
+
* @return array Updated arguments for PutObject.
|
49 |
+
*/
|
50 |
+
public function save(MetadataEnvelope $envelope, array $args)
|
51 |
+
{
|
52 |
+
$this->client->putObject([
|
53 |
+
'Bucket' => $args['Bucket'],
|
54 |
+
'Key' => $args['Key'] . $this->suffix,
|
55 |
+
'Body' => json_encode($envelope)
|
56 |
+
]);
|
57 |
+
|
58 |
+
return $args;
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Uses the strategy's client to retrieve the instruction file from S3 and generates
|
63 |
+
* a MetadataEnvelope from its contents.
|
64 |
+
*
|
65 |
+
* @param array $args Arguments from Command and Result that contains
|
66 |
+
* S3 Object information, relevant headers, and command
|
67 |
+
* configuration.
|
68 |
+
*
|
69 |
+
* @return MetadataEnvelope
|
70 |
+
*/
|
71 |
+
public function load(array $args)
|
72 |
+
{
|
73 |
+
$result = $this->client->getObject([
|
74 |
+
'Bucket' => $args['Bucket'],
|
75 |
+
'Key' => $args['Key'] . $this->suffix
|
76 |
+
]);
|
77 |
+
|
78 |
+
$metadataHeaders = json_decode($result['Body'], true);
|
79 |
+
$envelope = new MetadataEnvelope();
|
80 |
+
$constantValues = MetadataEnvelope::getConstantValues();
|
81 |
+
|
82 |
+
foreach ($constantValues as $constant) {
|
83 |
+
if (!empty($metadataHeaders[$constant])) {
|
84 |
+
$envelope[$constant] = $metadataHeaders[$constant];
|
85 |
+
}
|
86 |
+
}
|
87 |
+
|
88 |
+
return $envelope;
|
89 |
+
}
|
90 |
+
}
|
lib/Aws/Aws/S3/Crypto/S3EncryptionClient.php
ADDED
@@ -0,0 +1,317 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\S3\Crypto;
|
3 |
+
|
4 |
+
use Aws\HashingStream;
|
5 |
+
use Aws\PhpHash;
|
6 |
+
use Aws\Crypto\AbstractCryptoClient;
|
7 |
+
use Aws\Crypto\EncryptionTrait;
|
8 |
+
use Aws\Crypto\DecryptionTrait;
|
9 |
+
use Aws\Crypto\MetadataEnvelope;
|
10 |
+
use Aws\Crypto\MaterialsProvider;
|
11 |
+
use Aws\Crypto\Cipher\CipherBuilderTrait;
|
12 |
+
use Aws\S3\S3Client;
|
13 |
+
use GuzzleHttp\Promise;
|
14 |
+
use GuzzleHttp\Promise\PromiseInterface;
|
15 |
+
use GuzzleHttp\Psr7;
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Provides a wrapper for an S3Client that supplies functionality to encrypt
|
19 |
+
* data on putObject[Async] calls and decrypt data on getObject[Async] calls.
|
20 |
+
*/
|
21 |
+
class S3EncryptionClient extends AbstractCryptoClient
|
22 |
+
{
|
23 |
+
use EncryptionTrait, DecryptionTrait, CipherBuilderTrait, CryptoParamsTrait;
|
24 |
+
|
25 |
+
private $client;
|
26 |
+
private $instructionFileSuffix;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* @param S3Client $client The S3Client to be used for true uploading and
|
30 |
+
* retrieving objects from S3 when using the
|
31 |
+
* encryption client.
|
32 |
+
* @param string|null $instructionFileSuffix Suffix for a client wide
|
33 |
+
* default when using instruction
|
34 |
+
* files for metadata storage.
|
35 |
+
*/
|
36 |
+
public function __construct(
|
37 |
+
S3Client $client,
|
38 |
+
$instructionFileSuffix = null
|
39 |
+
) {
|
40 |
+
$this->client = $client;
|
41 |
+
$this->instructionFileSuffix = $instructionFileSuffix;
|
42 |
+
}
|
43 |
+
|
44 |
+
private static function getDefaultStrategy()
|
45 |
+
{
|
46 |
+
return new HeadersMetadataStrategy();
|
47 |
+
}
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Encrypts the data in the 'Body' field of $args and promises to upload it
|
51 |
+
* to the specified location on S3.
|
52 |
+
*
|
53 |
+
* @param array $args Arguments for encrypting an object and uploading it
|
54 |
+
* to S3 via PutObject.
|
55 |
+
*
|
56 |
+
* The required configuration arguments are as follows:
|
57 |
+
*
|
58 |
+
* - @MaterialsProvider: (MaterialsProvider) Provides Cek, Iv, and Cek
|
59 |
+
* encrypting/decrypting for encryption metadata.
|
60 |
+
* - @CipherOptions: (array) Cipher options for encrypting data. Only the
|
61 |
+
* Cipher option is required. Accepts the following:
|
62 |
+
* - Cipher: (string) cbc|gcm
|
63 |
+
* See also: AbstractCryptoClient::$supportedCiphers
|
64 |
+
* - KeySize: (int) 128|192|256
|
65 |
+
* See also: MaterialsProvider::$supportedKeySizes
|
66 |
+
* - Aad: (string) Additional authentication data. This option is
|
67 |
+
* passed directly to OpenSSL when using gcm. It is ignored when
|
68 |
+
* using cbc.
|
69 |
+
*
|
70 |
+
* The optional configuration arguments are as follows:
|
71 |
+
*
|
72 |
+
* - @MetadataStrategy: (MetadataStrategy|string|null) Strategy for storing
|
73 |
+
* MetadataEnvelope information. Defaults to using a
|
74 |
+
* HeadersMetadataStrategy. Can either be a class implementing
|
75 |
+
* MetadataStrategy, a class name of a predefined strategy, or empty/null
|
76 |
+
* to default.
|
77 |
+
* - @InstructionFileSuffix: (string|null) Suffix used when writing to an
|
78 |
+
* instruction file if using an InstructionFileMetadataHandler.
|
79 |
+
*
|
80 |
+
* @return PromiseInterface
|
81 |
+
*
|
82 |
+
* @throws \InvalidArgumentException Thrown when arguments above are not
|
83 |
+
* passed or are passed incorrectly.
|
84 |
+
*/
|
85 |
+
public function putObjectAsync(array $args)
|
86 |
+
{
|
87 |
+
$provider = $this->getMaterialsProvider($args);
|
88 |
+
unset($args['@MaterialsProvider']);
|
89 |
+
|
90 |
+
$instructionFileSuffix = $this->getInstructionFileSuffix($args);
|
91 |
+
unset($args['@InstructionFileSuffix']);
|
92 |
+
|
93 |
+
$strategy = $this->getMetadataStrategy($args, $instructionFileSuffix);
|
94 |
+
unset($args['@MetadataStrategy']);
|
95 |
+
|
96 |
+
$envelope = new MetadataEnvelope();
|
97 |
+
|
98 |
+
return Promise\promise_for($this->encrypt(
|
99 |
+
Psr7\stream_for($args['Body']),
|
100 |
+
$args['@CipherOptions'] ?: [],
|
101 |
+
$provider,
|
102 |
+
$envelope
|
103 |
+
))->then(
|
104 |
+
function ($encryptedBodyStream) use ($args) {
|
105 |
+
$hash = new PhpHash('sha256');
|
106 |
+
$hashingEncryptedBodyStream = new HashingStream(
|
107 |
+
$encryptedBodyStream,
|
108 |
+
$hash,
|
109 |
+
self::getContentShaDecorator($args)
|
110 |
+
);
|
111 |
+
return [$hashingEncryptedBodyStream, $args];
|
112 |
+
}
|
113 |
+
)->then(
|
114 |
+
function ($putObjectContents) use ($strategy, $envelope) {
|
115 |
+
list($bodyStream, $args) = $putObjectContents;
|
116 |
+
if ($strategy === null) {
|
117 |
+
$strategy = self::getDefaultStrategy();
|
118 |
+
}
|
119 |
+
|
120 |
+
$updatedArgs = $strategy->save($envelope, $args);
|
121 |
+
$updatedArgs['Body'] = $bodyStream;
|
122 |
+
return $updatedArgs;
|
123 |
+
}
|
124 |
+
)->then(
|
125 |
+
function ($args) {
|
126 |
+
unset($args['@CipherOptions']);
|
127 |
+
return $this->client->putObjectAsync($args);
|
128 |
+
}
|
129 |
+
);
|
130 |
+
}
|
131 |
+
|
132 |
+
private static function getContentShaDecorator(&$args)
|
133 |
+
{
|
134 |
+
return function ($hash) use (&$args) {
|
135 |
+
$args['ContentSHA256'] = bin2hex($hash);
|
136 |
+
};
|
137 |
+
}
|
138 |
+
|
139 |
+
/**
|
140 |
+
* Encrypts the data in the 'Body' field of $args and uploads it to the
|
141 |
+
* specified location on S3.
|
142 |
+
*
|
143 |
+
* @param array $args Arguments for encrypting an object and uploading it
|
144 |
+
* to S3 via PutObject.
|
145 |
+
*
|
146 |
+
* The required configuration arguments are as follows:
|
147 |
+
*
|
148 |
+
* - @MaterialsProvider: (MaterialsProvider) Provides Cek, Iv, and Cek
|
149 |
+
* encrypting/decrypting for encryption metadata.
|
150 |
+
* - @CipherOptions: (array) Cipher options for encrypting data. A Cipher
|
151 |
+
* is required. Accepts the following options:
|
152 |
+
* - Cipher: (string) cbc|gcm
|
153 |
+
* See also: AbstractCryptoClient::$supportedCiphers
|
154 |
+
* - KeySize: (int) 128|192|256
|
155 |
+
* See also: MaterialsProvider::$supportedKeySizes
|
156 |
+
* - Aad: (string) Additional authentication data. This option is
|
157 |
+
* passed directly to OpenSSL when using gcm. It is ignored when
|
158 |
+
* using cbc.
|
159 |
+
*
|
160 |
+
* The optional configuration arguments are as follows:
|
161 |
+
*
|
162 |
+
* - @MetadataStrategy: (MetadataStrategy|string|null) Strategy for storing
|
163 |
+
* MetadataEnvelope information. Defaults to using a
|
164 |
+
* HeadersMetadataStrategy. Can either be a class implementing
|
165 |
+
* MetadataStrategy, a class name of a predefined strategy, or empty/null
|
166 |
+
* to default.
|
167 |
+
* - @InstructionFileSuffix: (string|null) Suffix used when writing to an
|
168 |
+
* instruction file if an using an InstructionFileMetadataHandler was
|
169 |
+
* determined.
|
170 |
+
*
|
171 |
+
* @return \Aws\Result PutObject call result with the details of uploading
|
172 |
+
* the encrypted file.
|
173 |
+
*
|
174 |
+
* @throws \InvalidArgumentException Thrown when arguments above are not
|
175 |
+
* passed or are passed incorrectly.
|
176 |
+
*/
|
177 |
+
public function putObject(array $args)
|
178 |
+
{
|
179 |
+
return $this->putObjectAsync($args)->wait();
|
180 |
+
}
|
181 |
+
|
182 |
+
/**
|
183 |
+
* Promises to retrieve an object from S3 and decrypt the data in the
|
184 |
+
* 'Body' field.
|
185 |
+
*
|
186 |
+
* @param array $args Arguments for retrieving an object from S3 via
|
187 |
+
* GetObject and decrypting it.
|
188 |
+
*
|
189 |
+
* The required configuration argument is as follows:
|
190 |
+
*
|
191 |
+
* - @MaterialsProvider: (MaterialsProvider) Provides Cek, Iv, and Cek
|
192 |
+
* encrypting/decrypting for decryption metadata. May have data loaded
|
193 |
+
* from the MetadataEnvelope upon decryption.
|
194 |
+
*
|
195 |
+
* The optional configuration arguments are as follows:
|
196 |
+
*
|
197 |
+
* - SaveAs: (string) The path to a file on disk to save the decrypted
|
198 |
+
* object data. This will be handled by file_put_contents instead of the
|
199 |
+
* Guzzle sink.
|
200 |
+
*
|
201 |
+
* - @MetadataStrategy: (MetadataStrategy|string|null) Strategy for reading
|
202 |
+
* MetadataEnvelope information. Defaults to determining based on object
|
203 |
+
* response headers. Can either be a class implementing MetadataStrategy,
|
204 |
+
* a class name of a predefined strategy, or empty/null to default.
|
205 |
+
* - @InstructionFileSuffix: (string) Suffix used when looking for an
|
206 |
+
* instruction file if an InstructionFileMetadataHandler is being used.
|
207 |
+
* - @CipherOptions: (array) Cipher options for decrypting data. A Cipher
|
208 |
+
* is required. Accepts the following options:
|
209 |
+
* - Aad: (string) Additional authentication data. This option is
|
210 |
+
* passed directly to OpenSSL when using gcm. It is ignored when
|
211 |
+
* using cbc.
|
212 |
+
*
|
213 |
+
* @return PromiseInterface
|
214 |
+
*
|
215 |
+
* @throws \InvalidArgumentException Thrown when required arguments are not
|
216 |
+
* passed or are passed incorrectly.
|
217 |
+
*/
|
218 |
+
public function getObjectAsync(array $args)
|
219 |
+
{
|
220 |
+
$provider = $this->getMaterialsProvider($args);
|
221 |
+
unset($args['@MaterialsProvider']);
|
222 |
+
|
223 |
+
$instructionFileSuffix = $this->getInstructionFileSuffix($args);
|
224 |
+
unset($args['@InstructionFileSuffix']);
|
225 |
+
|
226 |
+
$strategy = $this->getMetadataStrategy($args, $instructionFileSuffix);
|
227 |
+
unset($args['@MetadataStrategy']);
|
228 |
+
|
229 |
+
$saveAs = null;
|
230 |
+
if (!empty($args['SaveAs'])) {
|
231 |
+
$saveAs = $args['SaveAs'];
|
232 |
+
}
|
233 |
+
|
234 |
+
$promise = $this->client->getObjectAsync($args)
|
235 |
+
->then(
|
236 |
+
function ($result) use (
|
237 |
+
$provider,
|
238 |
+
$instructionFileSuffix,
|
239 |
+
$strategy,
|
240 |
+
$args
|
241 |
+
) {
|
242 |
+
if ($strategy === null) {
|
243 |
+
$strategy = $this->determineGetObjectStrategy(
|
244 |
+
$result,
|
245 |
+
$instructionFileSuffix
|
246 |
+
);
|
247 |
+
}
|
248 |
+
|
249 |
+
$envelope = $strategy->load($args + [
|
250 |
+
'Metadata' => $result['Metadata']
|
251 |
+
]);
|
252 |
+
|
253 |
+
$provider = $provider->fromDecryptionEnvelope($envelope);
|
254 |
+
|
255 |
+
$result['Body'] = $this->decrypt(
|
256 |
+
$result['Body'],
|
257 |
+
$provider,
|
258 |
+
$envelope,
|
259 |
+
isset($args['@CipherOptions'])
|
260 |
+
? $args['@CipherOptions']
|
261 |
+
: []
|
262 |
+
);
|
263 |
+
return $result;
|
264 |
+
}
|
265 |
+
)->then(
|
266 |
+
function ($result) use ($saveAs) {
|
267 |
+
if (!empty($saveAs)) {
|
268 |
+
file_put_contents(
|
269 |
+
$saveAs,
|
270 |
+
(string)$result['Body'],
|
271 |
+
LOCK_EX
|
272 |
+
);
|
273 |
+
}
|
274 |
+
return $result;
|
275 |
+
}
|
276 |
+
);
|
277 |
+
|
278 |
+
return $promise;
|
279 |
+
}
|
280 |
+
|
281 |
+
/**
|
282 |
+
* Retrieves an object from S3 and decrypts the data in the 'Body' field.
|
283 |
+
*
|
284 |
+
* @param array $args Arguments for retrieving an object from S3 via
|
285 |
+
* GetObject and decrypting it.
|
286 |
+
*
|
287 |
+
* The required configuration argument is as follows:
|
288 |
+
*
|
289 |
+
* - @MaterialsProvider: (MaterialsProvider) Provides Cek, Iv, and Cek
|
290 |
+
* encrypting/decrypting for decryption metadata. May have data loaded
|
291 |
+
* from the MetadataEnvelope upon decryption.
|
292 |
+
*
|
293 |
+
* The optional configuration arguments are as follows:
|
294 |
+
*
|
295 |
+
* - SaveAs: (string) The path to a file on disk to save the decrypted
|
296 |
+
* object data. This will be handled by file_put_contents instead of the
|
297 |
+
* Guzzle sink.
|
298 |
+
* - @InstructionFileSuffix: (string|null) Suffix used when looking for an
|
299 |
+
* instruction file if an InstructionFileMetadataHandler was detected.
|
300 |
+
* - @CipherOptions: (array) Cipher options for encrypting data. A Cipher
|
301 |
+
* is required. Accepts the following options:
|
302 |
+
* - Aad: (string) Additional authentication data. This option is
|
303 |
+
* passed directly to OpenSSL when using gcm. It is ignored when
|
304 |
+
* using cbc.
|
305 |
+
*
|
306 |
+
* @return \Aws\Result GetObject call result with the 'Body' field
|
307 |
+
* wrapped in a decryption stream with its metadata
|
308 |
+
* information.
|
309 |
+
*
|
310 |
+
* @throws \InvalidArgumentException Thrown when arguments above are not
|
311 |
+
* passed or are passed incorrectly.
|
312 |
+
*/
|
313 |
+
public function getObject(array $args)
|
314 |
+
{
|
315 |
+
return $this->getObjectAsync($args)->wait();
|
316 |
+
}
|
317 |
+
}
|
lib/Aws/Aws/S3/Crypto/S3EncryptionMultipartUploader.php
ADDED
@@ -0,0 +1,157 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\S3\Crypto;
|
3 |
+
|
4 |
+
use Aws\Crypto\AbstractCryptoClient;
|
5 |
+
use Aws\Crypto\EncryptionTrait;
|
6 |
+
use Aws\Crypto\MetadataEnvelope;
|
7 |
+
use Aws\Crypto\Cipher\CipherBuilderTrait;
|
8 |
+
use Aws\S3\MultipartUploader;
|
9 |
+
use Aws\S3\S3ClientInterface;
|
10 |
+
use GuzzleHttp\Promise;
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Encapsulates the execution of a multipart upload of an encrypted object to S3.
|
14 |
+
*/
|
15 |
+
class S3EncryptionMultipartUploader extends MultipartUploader
|
16 |
+
{
|
17 |
+
use EncryptionTrait, CipherBuilderTrait, CryptoParamsTrait;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Returns if the passed cipher name is supported for encryption by the SDK.
|
21 |
+
*
|
22 |
+
* @param string $cipherName The name of a cipher to verify is registered.
|
23 |
+
*
|
24 |
+
* @return bool If the cipher passed is in our supported list.
|
25 |
+
*/
|
26 |
+
public static function isSupportedCipher($cipherName)
|
27 |
+
{
|
28 |
+
return in_array($cipherName, AbstractCryptoClient::$supportedCiphers);
|
29 |
+
}
|
30 |
+
|
31 |
+
private $provider;
|
32 |
+
private $instructionFileSuffix;
|
33 |
+
private $strategy;
|
34 |
+
|
35 |
+
/**
|
36 |
+
* Creates a multipart upload for an S3 object after encrypting it.
|
37 |
+
*
|
38 |
+
* The required configuration options are as follows:
|
39 |
+
*
|
40 |
+
* - @MaterialsProvider: (MaterialsProvider) Provides Cek, Iv, and Cek
|
41 |
+
* encrypting/decrypting for encryption metadata.
|
42 |
+
* - @CipherOptions: (array) Cipher options for encrypting data. A Cipher
|
43 |
+
* is required. Accepts the following options:
|
44 |
+
* - Cipher: (string) cbc|gcm
|
45 |
+
* See also: AbstractCryptoClient::$supportedCiphers
|
46 |
+
* - KeySize: (int) 128|192|256
|
47 |
+
* See also: MaterialsProvider::$supportedKeySizes
|
48 |
+
* - Aad: (string) Additional authentication data. This option is
|
49 |
+
* passed directly to OpenSSL when using gcm. It is ignored when
|
50 |
+
* using cbc.
|
51 |
+
* - bucket: (string) Name of the bucket to which the object is
|
52 |
+
* being uploaded.
|
53 |
+
* - key: (string) Key to use for the object being uploaded.
|
54 |
+
*
|
55 |
+
* The optional configuration arguments are as follows:
|
56 |
+
*
|
57 |
+
* - @MetadataStrategy: (MetadataStrategy|string|null) Strategy for storing
|
58 |
+
* MetadataEnvelope information. Defaults to using a
|
59 |
+
* HeadersMetadataStrategy. Can either be a class implementing
|
60 |
+
* MetadataStrategy, a class name of a predefined strategy, or empty/null
|
61 |
+
* to default.
|
62 |
+
* - @InstructionFileSuffix: (string|null) Suffix used when writing to an
|
63 |
+
* instruction file if an using an InstructionFileMetadataHandler was
|
64 |
+
* determined.
|
65 |
+
* - acl: (string) ACL to set on the object being upload. Objects are
|
66 |
+
* private by default.
|
67 |
+
* - before_complete: (callable) Callback to invoke before the
|
68 |
+
* `CompleteMultipartUpload` operation. The callback should have a
|
69 |
+
* function signature like `function (Aws\Command $command) {...}`.
|
70 |
+
* - before_initiate: (callable) Callback to invoke before the
|
71 |
+
* `CreateMultipartUpload` operation. The callback should have a function
|
72 |
+
* signature like `function (Aws\Command $command) {...}`.
|
73 |
+
* - before_upload: (callable) Callback to invoke before any `UploadPart`
|
74 |
+
* operations. The callback should have a function signature like
|
75 |
+
* `function (Aws\Command $command) {...}`.
|
76 |
+
* - concurrency: (int, default=int(5)) Maximum number of concurrent
|
77 |
+
* `UploadPart` operations allowed during the multipart upload.
|
78 |
+
* - params: (array) An array of key/value parameters that will be applied
|
79 |
+
* to each of the sub-commands run by the uploader as a base.
|
80 |
+
* Auto-calculated options will override these parameters. If you need
|
81 |
+
* more granularity over parameters to each sub-command, use the before_*
|
82 |
+
* options detailed above to update the commands directly.
|
83 |
+
* - part_size: (int, default=int(5242880)) Part size, in bytes, to use when
|
84 |
+
* doing a multipart upload. This must between 5 MB and 5 GB, inclusive.
|
85 |
+
* - state: (Aws\Multipart\UploadState) An object that represents the state
|
86 |
+
* of the multipart upload and that is used to resume a previous upload.
|
87 |
+
* When this option is provided, the `bucket`, `key`, and `part_size`
|
88 |
+
* options are ignored.
|
89 |
+
*
|
90 |
+
* @param S3ClientInterface $client Client used for the upload.
|
91 |
+
* @param mixed $source Source of the data to upload.
|
92 |
+
* @param array $config Configuration used to perform the upload.
|
93 |
+
*/
|
94 |
+
public function __construct(
|
95 |
+
S3ClientInterface $client,
|
96 |
+
$source,
|
97 |
+
array $config = []
|
98 |
+
) {
|
99 |
+
$this->client = $client;
|
100 |
+
$config['params'] = [];
|
101 |
+
if (!empty($config['bucket'])) {
|
102 |
+
$config['params']['Bucket'] = $config['bucket'];
|
103 |
+
}
|
104 |
+
if (!empty($config['key'])) {
|
105 |
+
$config['params']['Key'] = $config['key'];
|
106 |
+
}
|
107 |
+
|
108 |
+
$this->provider = $this->getMaterialsProvider($config);
|
109 |
+
unset($config['@MaterialsProvider']);
|
110 |
+
|
111 |
+
$this->instructionFileSuffix = $this->getInstructionFileSuffix($config);
|
112 |
+
unset($config['@InstructionFileSuffix']);
|
113 |
+
$this->strategy = $this->getMetadataStrategy(
|
114 |
+
$config,
|
115 |
+
$this->instructionFileSuffix
|
116 |
+
);
|
117 |
+
if ($this->strategy === null) {
|
118 |
+
$this->strategy = self::getDefaultStrategy();
|
119 |
+
}
|
120 |
+
unset($config['@MetadataStrategy']);
|
121 |
+
|
122 |
+
$config['prepare_data_source'] = $this->getEncryptingDataPreparer();
|
123 |
+
|
124 |
+
parent::__construct($client, $source, $config);
|
125 |
+
}
|
126 |
+
|
127 |
+
private static function getDefaultStrategy()
|
128 |
+
{
|
129 |
+
return new HeadersMetadataStrategy();
|
130 |
+
}
|
131 |
+
|
132 |
+
private function getEncryptingDataPreparer()
|
133 |
+
{
|
134 |
+
return function() {
|
135 |
+
// Defer encryption work until promise is executed
|
136 |
+
$envelope = new MetadataEnvelope();
|
137 |
+
|
138 |
+
list($this->source, $params) = Promise\promise_for($this->encrypt(
|
139 |
+
$this->source,
|
140 |
+
$this->config['@cipheroptions'] ?: [],
|
141 |
+
$this->provider,
|
142 |
+
$envelope
|
143 |
+
))->then(
|
144 |
+
function ($bodyStream) use ($envelope) {
|
145 |
+
$params = $this->strategy->save(
|
146 |
+
$envelope,
|
147 |
+
$this->config['params']
|
148 |
+
);
|
149 |
+
return [$bodyStream, $params];
|
150 |
+
}
|
151 |
+
)->wait();
|
152 |
+
|
153 |
+
$this->source->rewind();
|
154 |
+
$this->config['params'] = $params;
|
155 |
+
};
|
156 |
+
}
|
157 |
+
}
|
lib/Aws/Aws/S3/Exception/DeleteMultipleObjectsException.php
ADDED
@@ -0,0 +1,68 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\S3\Exception;
|
3 |
+
|
4 |
+
use Aws\HasMonitoringEventsTrait;
|
5 |
+
use Aws\MonitoringEventsInterface;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Exception thrown when errors occur while deleting objects using a
|
9 |
+
* {@see S3\BatchDelete} object.
|
10 |
+
*/
|
11 |
+
class DeleteMultipleObjectsException extends \Exception implements
|
12 |
+
MonitoringEventsInterface
|
13 |
+
{
|
14 |
+
use HasMonitoringEventsTrait;
|
15 |
+
|
16 |
+
private $deleted = [];
|
17 |
+
private $errors = [];
|
18 |
+
|
19 |
+
/**
|
20 |
+
* @param array $deleted Array of successfully deleted keys
|
21 |
+
* @param array $errors Array of errors that were encountered
|
22 |
+
*/
|
23 |
+
public function __construct(array $deleted, array $errors)
|
24 |
+
{
|
25 |
+
$this->deleted = array_values($deleted);
|
26 |
+
$this->errors = array_values($errors);
|
27 |
+
parent::__construct('Unable to delete certain keys when executing a'
|
28 |
+
. ' DeleteMultipleObjects request: '
|
29 |
+
. self::createMessageFromErrors($errors));
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Create a single error message from multiple errors.
|
34 |
+
*
|
35 |
+
* @param array $errors Errors encountered
|
36 |
+
*
|
37 |
+
* @return string
|
38 |
+
*/
|
39 |
+
public static function createMessageFromErrors(array $errors)
|
40 |
+
{
|
41 |
+
return "\n- " . implode("\n- ", array_map(function ($key) {
|
42 |
+
return json_encode($key);
|
43 |
+
}, $errors));
|
44 |
+
}
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Get the errored objects
|
48 |
+
*
|
49 |
+
* @return array Returns an array of associative arrays, each containing
|
50 |
+
* a 'Code', 'Message', and 'Key' key.
|
51 |
+
*/
|
52 |
+
public function getErrors()
|
53 |
+
{
|
54 |
+
return $this->errors;
|
55 |
+
}
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Get the successfully deleted objects
|
59 |
+
*
|
60 |
+
* @return array Returns an array of associative arrays, each containing
|
61 |
+
* a 'Key' and optionally 'DeleteMarker' and
|
62 |
+
* 'DeleterMarkerVersionId'
|
63 |
+
*/
|
64 |
+
public function getDeleted()
|
65 |
+
{
|
66 |
+
return $this->deleted;
|
67 |
+
}
|
68 |
+
}
|
lib/Aws/Aws/S3/Exception/PermanentRedirectException.php
ADDED
@@ -0,0 +1,4 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\S3\Exception;
|
3 |
+
|
4 |
+
class PermanentRedirectException extends S3Exception {}
|
lib/Aws/Aws/S3/Exception/S3Exception.php
ADDED
@@ -0,0 +1,9 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\S3\Exception;
|
3 |
+
|
4 |
+
use Aws\Exception\AwsException;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents an error interacting with the Amazon Simple Storage Service.
|
8 |
+
*/
|
9 |
+
class S3Exception extends AwsException {}
|
lib/Aws/Aws/S3/Exception/S3MultipartUploadException.php
ADDED
@@ -0,0 +1,84 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\S3\Exception;
|
3 |
+
|
4 |
+
use Aws\CommandInterface;
|
5 |
+
use Aws\Exception\AwsException;
|
6 |
+
use Aws\Multipart\UploadState;
|
7 |
+
|
8 |
+
class S3MultipartUploadException extends \Aws\Exception\MultipartUploadException
|
9 |
+
{
|
10 |
+
/** @var string Bucket of the transfer object */
|
11 |
+
private $bucket;
|
12 |
+
/** @var string Key of the transfer object */
|
13 |
+
private $key;
|
14 |
+
/** @var string Source file name of the transfer object */
|
15 |
+
private $filename;
|
16 |
+
|
17 |
+
/**
|
18 |
+
* @param UploadState $state Upload state at time of the exception.
|
19 |
+
* @param \Exception|array $prev Exception being thrown. Could be an array of
|
20 |
+
* AwsExceptions being thrown when uploading parts
|
21 |
+
* for one object, or an instance of AwsException
|
22 |
+
* for a specific Multipart error being thrown in
|
23 |
+
* the MultipartUpload process.
|
24 |
+
*/
|
25 |
+
public function __construct(UploadState $state, $prev = null) {
|
26 |
+
if (is_array($prev) && $error = $prev[key($prev)]) {
|
27 |
+
$this->collectPathInfo($error->getCommand());
|
28 |
+
} elseif ($prev instanceof AwsException) {
|
29 |
+
$this->collectPathInfo($prev->getCommand());
|
30 |
+
}
|
31 |
+
parent::__construct($state, $prev);
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Get the Bucket information of the transfer object
|
36 |
+
*
|
37 |
+
* @return string|null Returns null when 'Bucket' information
|
38 |
+
* is unavailable.
|
39 |
+
*/
|
40 |
+
public function getBucket()
|
41 |
+
{
|
42 |
+
return $this->bucket;
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Get the Key information of the transfer object
|
47 |
+
*
|
48 |
+
* @return string|null Returns null when 'Key' information
|
49 |
+
* is unavailable.
|
50 |
+
*/
|
51 |
+
public function getKey()
|
52 |
+
{
|
53 |
+
return $this->key;
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Get the source file name of the transfer object
|
58 |
+
*
|
59 |
+
* @return string|null Returns null when metadata of the stream
|
60 |
+
* wrapped in 'Body' parameter is unavailable.
|
61 |
+
*/
|
62 |
+
public function getSourceFileName()
|
63 |
+
{
|
64 |
+
return $this->filename;
|
65 |
+
}
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Collect file path information when accessible. (Bucket, Key)
|
69 |
+
*
|
70 |
+
* @param CommandInterface $cmd
|
71 |
+
*/
|
72 |
+
private function collectPathInfo(CommandInterface $cmd)
|
73 |
+
{
|
74 |
+
if (empty($this->bucket) && isset($cmd['Bucket'])) {
|
75 |
+
$this->bucket = $cmd['Bucket'];
|
76 |
+
}
|
77 |
+
if (empty($this->key) && isset($cmd['Key'])) {
|
78 |
+
$this->key = $cmd['Key'];
|
79 |
+
}
|
80 |
+
if (empty($this->filename) && isset($cmd['Body'])) {
|
81 |
+
$this->filename = $cmd['Body']->getMetadata('uri');
|
82 |
+
}
|
83 |
+
}
|
84 |
+
}
|
lib/Aws/Aws/S3/GetBucketLocationParser.php
ADDED
@@ -0,0 +1,49 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\S3;
|
3 |
+
|
4 |
+
use Aws\Api\Parser\AbstractParser;
|
5 |
+
use Aws\Api\StructureShape;
|
6 |
+
use Aws\CommandInterface;
|
7 |
+
use Psr\Http\Message\ResponseInterface;
|
8 |
+
use Psr\Http\Message\StreamInterface;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* @internal Decorates a parser for the S3 service to correctly handle the
|
12 |
+
* GetBucketLocation operation.
|
13 |
+
*/
|
14 |
+
class GetBucketLocationParser extends AbstractParser
|
15 |
+
{
|
16 |
+
/**
|
17 |
+
* @param callable $parser Parser to wrap.
|
18 |
+
*/
|
19 |
+
public function __construct(callable $parser)
|
20 |
+
{
|
21 |
+
$this->parser = $parser;
|
22 |
+
}
|
23 |
+
|
24 |
+
public function __invoke(
|
25 |
+
CommandInterface $command,
|
26 |
+
ResponseInterface $response
|
27 |
+
) {
|
28 |
+
$fn = $this->parser;
|
29 |
+
$result = $fn($command, $response);
|
30 |
+
|
31 |
+
if ($command->getName() === 'GetBucketLocation') {
|
32 |
+
$location = 'us-east-1';
|
33 |
+
if (preg_match('/>(.+?)<\/LocationConstraint>/', $response->getBody(), $matches)) {
|
34 |
+
$location = $matches[1] === 'EU' ? 'eu-west-1' : $matches[1];
|
35 |
+
}
|
36 |
+
$result['LocationConstraint'] = $location;
|
37 |
+
}
|
38 |
+
|
39 |
+
return $result;
|
40 |
+
}
|
41 |
+
|
42 |
+
public function parseMemberFromStream(
|
43 |
+
StreamInterface $stream,
|
44 |
+
StructureShape $member,
|
45 |
+
$response
|
46 |
+
) {
|
47 |
+
return $this->parser->parseMemberFromStream($stream, $member, $response);
|
48 |
+
}
|
49 |
+
}
|
lib/Aws/Aws/S3/MultipartCopy.php
ADDED
@@ -0,0 +1,183 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\S3;
|
3 |
+
|
4 |
+
use Aws\Multipart\AbstractUploadManager;
|
5 |
+
use Aws\ResultInterface;
|
6 |
+
use GuzzleHttp\Psr7;
|
7 |
+
|
8 |
+
class MultipartCopy extends AbstractUploadManager
|
9 |
+
{
|
10 |
+
use MultipartUploadingTrait;
|
11 |
+
|
12 |
+
/** @var string */
|
13 |
+
private $source;
|
14 |
+
/** @var ResultInterface */
|
15 |
+
private $sourceMetadata;
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Creates a multipart upload for copying an S3 object.
|
19 |
+
*
|
20 |
+
* The valid configuration options are as follows:
|
21 |
+
*
|
22 |
+
* - acl: (string) ACL to set on the object being upload. Objects are
|
23 |
+
* private by default.
|
24 |
+
* - before_complete: (callable) Callback to invoke before the
|
25 |
+
* `CompleteMultipartUpload` operation. The callback should have a
|
26 |
+
* function signature like `function (Aws\Command $command) {...}`.
|
27 |
+
* - before_initiate: (callable) Callback to invoke before the
|
28 |
+
* `CreateMultipartUpload` operation. The callback should have a function
|
29 |
+
* signature like `function (Aws\Command $command) {...}`.
|
30 |
+
* - before_upload: (callable) Callback to invoke before `UploadPartCopy`
|
31 |
+
* operations. The callback should have a function signature like
|
32 |
+
* `function (Aws\Command $command) {...}`.
|
33 |
+
* - bucket: (string, required) Name of the bucket to which the object is
|
34 |
+
* being uploaded.
|
35 |
+
* - concurrency: (int, default=int(5)) Maximum number of concurrent
|
36 |
+
* `UploadPart` operations allowed during the multipart upload.
|
37 |
+
* - key: (string, required) Key to use for the object being uploaded.
|
38 |
+
* - params: (array) An array of key/value parameters that will be applied
|
39 |
+
* to each of the sub-commands run by the uploader as a base.
|
40 |
+
* Auto-calculated options will override these parameters. If you need
|
41 |
+
* more granularity over parameters to each sub-command, use the before_*
|
42 |
+
* options detailed above to update the commands directly.
|
43 |
+
* - part_size: (int, default=int(5242880)) Part size, in bytes, to use when
|
44 |
+
* doing a multipart upload. This must between 5 MB and 5 GB, inclusive.
|
45 |
+
* - state: (Aws\Multipart\UploadState) An object that represents the state
|
46 |
+
* of the multipart upload and that is used to resume a previous upload.
|
47 |
+
* When this option is provided, the `bucket`, `key`, and `part_size`
|
48 |
+
* options are ignored.
|
49 |
+
* - source_metadata: (Aws\ResultInterface) An object that represents the
|
50 |
+
* result of executing a HeadObject command on the copy source.
|
51 |
+
*
|
52 |
+
* @param S3ClientInterface $client Client used for the upload.
|
53 |
+
* @param string $source Location of the data to be copied
|
54 |
+
* (in the form /<bucket>/<key>).
|
55 |
+
* @param array $config Configuration used to perform the upload.
|
56 |
+
*/
|
57 |
+
public function __construct(
|
58 |
+
S3ClientInterface $client,
|
59 |
+
$source,
|
60 |
+
array $config = []
|
61 |
+
) {
|
62 |
+
$this->source = '/' . ltrim($source, '/');
|
63 |
+
parent::__construct($client, array_change_key_case($config) + [
|
64 |
+
'source_metadata' => null
|
65 |
+
]);
|
66 |
+
}
|
67 |
+
|
68 |
+
/**
|
69 |
+
* An alias of the self::upload method.
|
70 |
+
*
|
71 |
+
* @see self::upload
|
72 |
+
*/
|
73 |
+
public function copy()
|
74 |
+
{
|
75 |
+
return $this->upload();
|
76 |
+
}
|
77 |
+
|
78 |
+
protected function loadUploadWorkflowInfo()
|
79 |
+
{
|
80 |
+
return [
|
81 |
+
'command' => [
|
82 |
+
'initiate' => 'CreateMultipartUpload',
|
83 |
+
'upload' => 'UploadPartCopy',
|
84 |
+
'complete' => 'CompleteMultipartUpload',
|
85 |
+
],
|
86 |
+
'id' => [
|
87 |
+
'bucket' => 'Bucket',
|
88 |
+
'key' => 'Key',
|
89 |
+
'upload_id' => 'UploadId',
|
90 |
+
],
|
91 |
+
'part_num' => 'PartNumber',
|
92 |
+
];
|
93 |
+
}
|
94 |
+
|
95 |
+
protected function getUploadCommands(callable $resultHandler)
|
96 |
+
{
|
97 |
+
$parts = ceil($this->getSourceSize() / $this->determinePartSize());
|
98 |
+
|
99 |
+
for ($partNumber = 1; $partNumber <= $parts; $partNumber++) {
|
100 |
+
// If we haven't already uploaded this part, yield a new part.
|
101 |
+
if (!$this->state->hasPartBeenUploaded($partNumber)) {
|
102 |
+
$command = $this->client->getCommand(
|
103 |
+
$this->info['command']['upload'],
|
104 |
+
$this->createPart($partNumber, $parts)
|
105 |
+
+ $this->getState()->getId()
|
106 |
+
);
|
107 |
+
$command->getHandlerList()->appendSign($resultHandler, 'mup');
|
108 |
+
yield $command;
|
109 |
+
}
|
110 |
+
}
|
111 |
+
}
|
112 |
+
|
113 |
+
private function createPart($partNumber, $partsCount)
|
114 |
+
{
|
115 |
+
$data = [];
|
116 |
+
|
117 |
+
// Apply custom params to UploadPartCopy data
|
118 |
+
$config = $this->getConfig();
|
119 |
+
$params = isset($config['params']) ? $config['params'] : [];
|
120 |
+
foreach ($params as $k => $v) {
|
121 |
+
$data[$k] = $v;
|
122 |
+
}
|
123 |
+
|
124 |
+
$data['CopySource'] = $this->source;
|
125 |
+
$data['PartNumber'] = $partNumber;
|
126 |
+
|
127 |
+
$defaultPartSize = $this->determinePartSize();
|
128 |
+
$startByte = $defaultPartSize * ($partNumber - 1);
|
129 |
+
$data['ContentLength'] = $partNumber < $partsCount
|
130 |
+
? $defaultPartSize
|
131 |
+
: $this->getSourceSize() - ($defaultPartSize * ($partsCount - 1));
|
132 |
+
$endByte = $startByte + $data['ContentLength'] - 1;
|
133 |
+
$data['CopySourceRange'] = "bytes=$startByte-$endByte";
|
134 |
+
|
135 |
+
return $data;
|
136 |
+
}
|
137 |
+
|
138 |
+
protected function extractETag(ResultInterface $result)
|
139 |
+
{
|
140 |
+
return $result->search('CopyPartResult.ETag');
|
141 |
+
}
|
142 |
+
|
143 |
+
protected function getSourceMimeType()
|
144 |
+
{
|
145 |
+
return $this->getSourceMetadata()['ContentType'];
|
146 |
+
}
|
147 |
+
|
148 |
+
protected function getSourceSize()
|
149 |
+
{
|
150 |
+
return $this->getSourceMetadata()['ContentLength'];
|
151 |
+
}
|
152 |
+
|
153 |
+
private function getSourceMetadata()
|
154 |
+
{
|
155 |
+
if (empty($this->sourceMetadata)) {
|
156 |
+
$this->sourceMetadata = $this->fetchSourceMetadata();
|
157 |
+
}
|
158 |
+
|
159 |
+
return $this->sourceMetadata;
|
160 |
+
}
|
161 |
+
|
162 |
+
private function fetchSourceMetadata()
|
163 |
+
{
|
164 |
+
if ($this->config['source_metadata'] instanceof ResultInterface) {
|
165 |
+
return $this->config['source_metadata'];
|
166 |
+
}
|
167 |
+
|
168 |
+
list($bucket, $key) = explode('/', ltrim($this->source, '/'), 2);
|
169 |
+
$headParams = [
|
170 |
+
'Bucket' => $bucket,
|
171 |
+
'Key' => $key,
|
172 |
+
];
|
173 |
+
if (strpos($key, '?')) {
|
174 |
+
list($key, $query) = explode('?', $key, 2);
|
175 |
+
$headParams['Key'] = $key;
|
176 |
+
$query = Psr7\parse_query($query, false);
|
177 |
+
if (isset($query['versionId'])) {
|
178 |
+
$headParams['VersionId'] = $query['versionId'];
|
179 |
+
}
|
180 |
+
}
|
181 |
+
return $this->client->headObject($headParams);
|
182 |
+
}
|
183 |
+
}
|
lib/Aws/Aws/S3/MultipartUploader.php
ADDED
@@ -0,0 +1,168 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\S3;
|
3 |
+
|
4 |
+
use Aws\HashingStream;
|
5 |
+
use Aws\Multipart\AbstractUploader;
|
6 |
+
use Aws\PhpHash;
|
7 |
+
use Aws\ResultInterface;
|
8 |
+
use GuzzleHttp\Psr7;
|
9 |
+
use Psr\Http\Message\StreamInterface as Stream;
|
10 |
+
use Aws\S3\Exception\S3MultipartUploadException;
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Encapsulates the execution of a multipart upload to S3 or Glacier.
|
14 |
+
*/
|
15 |
+
class MultipartUploader extends AbstractUploader
|
16 |
+
{
|
17 |
+
use MultipartUploadingTrait;
|
18 |
+
|
19 |
+
const PART_MIN_SIZE = 5242880;
|
20 |
+
const PART_MAX_SIZE = 5368709120;
|
21 |
+
const PART_MAX_NUM = 10000;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Creates a multipart upload for an S3 object.
|
25 |
+
*
|
26 |
+
* The valid configuration options are as follows:
|
27 |
+
*
|
28 |
+
* - acl: (string) ACL to set on the object being upload. Objects are
|
29 |
+
* private by default.
|
30 |
+
* - before_complete: (callable) Callback to invoke before the
|
31 |
+
* `CompleteMultipartUpload` operation. The callback should have a
|
32 |
+
* function signature like `function (Aws\Command $command) {...}`.
|
33 |
+
* - before_initiate: (callable) Callback to invoke before the
|
34 |
+
* `CreateMultipartUpload` operation. The callback should have a function
|
35 |
+
* signature like `function (Aws\Command $command) {...}`.
|
36 |
+
* - before_upload: (callable) Callback to invoke before any `UploadPart`
|
37 |
+
* operations. The callback should have a function signature like
|
38 |
+
* `function (Aws\Command $command) {...}`.
|
39 |
+
* - bucket: (string, required) Name of the bucket to which the object is
|
40 |
+
* being uploaded.
|
41 |
+
* - concurrency: (int, default=int(5)) Maximum number of concurrent
|
42 |
+
* `UploadPart` operations allowed during the multipart upload.
|
43 |
+
* - key: (string, required) Key to use for the object being uploaded.
|
44 |
+
* - params: (array) An array of key/value parameters that will be applied
|
45 |
+
* to each of the sub-commands run by the uploader as a base.
|
46 |
+
* Auto-calculated options will override these parameters. If you need
|
47 |
+
* more granularity over parameters to each sub-command, use the before_*
|
48 |
+
* options detailed above to update the commands directly.
|
49 |
+
* - part_size: (int, default=int(5242880)) Part size, in bytes, to use when
|
50 |
+
* doing a multipart upload. This must between 5 MB and 5 GB, inclusive.
|
51 |
+
* - prepare_data_source: (callable) Callback to invoke before starting the
|
52 |
+
* multipart upload workflow. The callback should have a function
|
53 |
+
* signature like `function () {...}`.
|
54 |
+
* - state: (Aws\Multipart\UploadState) An object that represents the state
|
55 |
+
* of the multipart upload and that is used to resume a previous upload.
|
56 |
+
* When this option is provided, the `bucket`, `key`, and `part_size`
|
57 |
+
* options are ignored.
|
58 |
+
*
|
59 |
+
* @param S3ClientInterface $client Client used for the upload.
|
60 |
+
* @param mixed $source Source of the data to upload.
|
61 |
+
* @param array $config Configuration used to perform the upload.
|
62 |
+
*/
|
63 |
+
public function __construct(
|
64 |
+
S3ClientInterface $client,
|
65 |
+
$source,
|
66 |
+
array $config = []
|
67 |
+
) {
|
68 |
+
parent::__construct($client, $source, array_change_key_case($config) + [
|
69 |
+
'bucket' => null,
|
70 |
+
'key' => null,
|
71 |
+
'exception_class' => S3MultipartUploadException::class,
|
72 |
+
]);
|
73 |
+
}
|
74 |
+
|
75 |
+
protected function loadUploadWorkflowInfo()
|
76 |
+
{
|
77 |
+
return [
|
78 |
+
'command' => [
|
79 |
+
'initiate' => 'CreateMultipartUpload',
|
80 |
+
'upload' => 'UploadPart',
|
81 |
+
'complete' => 'CompleteMultipartUpload',
|
82 |
+
],
|
83 |
+
'id' => [
|
84 |
+
'bucket' => 'Bucket',
|
85 |
+
'key' => 'Key',
|
86 |
+
'upload_id' => 'UploadId',
|
87 |
+
],
|
88 |
+
'part_num' => 'PartNumber',
|
89 |
+
];
|
90 |
+
}
|
91 |
+
|
92 |
+
protected function createPart($seekable, $number)
|
93 |
+
{
|
94 |
+
// Initialize the array of part data that will be returned.
|
95 |
+
$data = [];
|
96 |
+
|
97 |
+
// Apply custom params to UploadPart data
|
98 |
+
$config = $this->getConfig();
|
99 |
+
$params = isset($config['params']) ? $config['params'] : [];
|
100 |
+
foreach ($params as $k => $v) {
|
101 |
+
$data[$k] = $v;
|
102 |
+
}
|
103 |
+
|
104 |
+
$data['PartNumber'] = $number;
|
105 |
+
|
106 |
+
// Read from the source to create the body stream.
|
107 |
+
if ($seekable) {
|
108 |
+
// Case 1: Source is seekable, use lazy stream to defer work.
|
109 |
+
$body = $this->limitPartStream(
|
110 |
+
new Psr7\LazyOpenStream($this->source->getMetadata('uri'), 'r')
|
111 |
+
);
|
112 |
+
} else {
|
113 |
+
// Case 2: Stream is not seekable; must store in temp stream.
|
114 |
+
$source = $this->limitPartStream($this->source);
|
115 |
+
$source = $this->decorateWithHashes($source, $data);
|
116 |
+
$body = Psr7\stream_for();
|
117 |
+
Psr7\copy_to_stream($source, $body);
|
118 |
+
}
|
119 |
+
|
120 |
+
$contentLength = $body->getSize();
|
121 |
+
|
122 |
+
// Do not create a part if the body size is zero.
|
123 |
+
if ($contentLength === 0) {
|
124 |
+
return false;
|
125 |
+
}
|
126 |
+
|
127 |
+
$body->seek(0);
|
128 |
+
$data['Body'] = $body;
|
129 |
+
$data['ContentLength'] = $contentLength;
|
130 |
+
|
131 |
+
return $data;
|
132 |
+
}
|
133 |
+
|
134 |
+
protected function extractETag(ResultInterface $result)
|
135 |
+
{
|
136 |
+
return $result['ETag'];
|
137 |
+
}
|
138 |
+
|
139 |
+
protected function getSourceMimeType()
|
140 |
+
{
|
141 |
+
if ($uri = $this->source->getMetadata('uri')) {
|
142 |
+
return Psr7\mimetype_from_filename($uri)
|
143 |
+
?: 'application/octet-stream';
|
144 |
+
}
|
145 |
+
}
|
146 |
+
|
147 |
+
protected function getSourceSize()
|
148 |
+
{
|
149 |
+
return $this->source->getSize();
|
150 |
+
}
|
151 |
+
|
152 |
+
/**
|
153 |
+
* Decorates a stream with a sha256 linear hashing stream.
|
154 |
+
*
|
155 |
+
* @param Stream $stream Stream to decorate.
|
156 |
+
* @param array $data Part data to augment with the hash result.
|
157 |
+
*
|
158 |
+
* @return Stream
|
159 |
+
*/
|
160 |
+
private function decorateWithHashes(Stream $stream, array &$data)
|
161 |
+
{
|
162 |
+
// Decorate source with a hashing stream
|
163 |
+
$hash = new PhpHash('sha256');
|
164 |
+
return new HashingStream($stream, $hash, function ($result) use (&$data) {
|
165 |
+
$data['ContentSHA256'] = bin2hex($result);
|
166 |
+
});
|
167 |
+
}
|
168 |
+
}
|
lib/Aws/Aws/S3/MultipartUploadingTrait.php
ADDED
@@ -0,0 +1,132 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\S3;
|
3 |
+
|
4 |
+
use Aws\CommandInterface;
|
5 |
+
use Aws\Multipart\UploadState;
|
6 |
+
use Aws\ResultInterface;
|
7 |
+
|
8 |
+
trait MultipartUploadingTrait
|
9 |
+
{
|
10 |
+
/**
|
11 |
+
* Creates an UploadState object for a multipart upload by querying the
|
12 |
+
* service for the specified upload's information.
|
13 |
+
*
|
14 |
+
* @param S3ClientInterface $client S3Client used for the upload.
|
15 |
+
* @param string $bucket Bucket for the multipart upload.
|
16 |
+
* @param string $key Object key for the multipart upload.
|
17 |
+
* @param string $uploadId Upload ID for the multipart upload.
|
18 |
+
*
|
19 |
+
* @return UploadState
|
20 |
+
*/
|
21 |
+
public static function getStateFromService(
|
22 |
+
S3ClientInterface $client,
|
23 |
+
$bucket,
|
24 |
+
$key,
|
25 |
+
$uploadId
|
26 |
+
) {
|
27 |
+
$state = new UploadState([
|
28 |
+
'Bucket' => $bucket,
|
29 |
+
'Key' => $key,
|
30 |
+
'UploadId' => $uploadId,
|
31 |
+
]);
|
32 |
+
|
33 |
+
foreach ($client->getPaginator('ListParts', $state->getId()) as $result) {
|
34 |
+
// Get the part size from the first part in the first result.
|
35 |
+
if (!$state->getPartSize()) {
|
36 |
+
$state->setPartSize($result->search('Parts[0].Size'));
|
37 |
+
}
|
38 |
+
// Mark all the parts returned by ListParts as uploaded.
|
39 |
+
foreach ($result['Parts'] as $part) {
|
40 |
+
$state->markPartAsUploaded($part['PartNumber'], [
|
41 |
+
'PartNumber' => $part['PartNumber'],
|
42 |
+
'ETag' => $part['ETag']
|
43 |
+
]);
|
44 |
+
}
|
45 |
+
}
|
46 |
+
|
47 |
+
$state->setStatus(UploadState::INITIATED);
|
48 |
+
|
49 |
+
return $state;
|
50 |
+
}
|
51 |
+
|
52 |
+
protected function handleResult(CommandInterface $command, ResultInterface $result)
|
53 |
+
{
|
54 |
+
$this->getState()->markPartAsUploaded($command['PartNumber'], [
|
55 |
+
'PartNumber' => $command['PartNumber'],
|
56 |
+
'ETag' => $this->extractETag($result),
|
57 |
+
]);
|
58 |
+
}
|
59 |
+
|
60 |
+
abstract protected function extractETag(ResultInterface $result);
|
61 |
+
|
62 |
+
protected function getCompleteParams()
|
63 |
+
{
|
64 |
+
$config = $this->getConfig();
|
65 |
+
$params = isset($config['params']) ? $config['params'] : [];
|
66 |
+
|
67 |
+
$params['MultipartUpload'] = [
|
68 |
+
'Parts' => $this->getState()->getUploadedParts()
|
69 |
+
];
|
70 |
+
|
71 |
+
return $params;
|
72 |
+
}
|
73 |
+
|
74 |
+
protected function determinePartSize()
|
75 |
+
{
|
76 |
+
// Make sure the part size is set.
|
77 |
+
$partSize = $this->getConfig()['part_size'] ?: MultipartUploader::PART_MIN_SIZE;
|
78 |
+
|
79 |
+
// Adjust the part size to be larger for known, x-large uploads.
|
80 |
+
if ($sourceSize = $this->getSourceSize()) {
|
81 |
+
$partSize = (int) max(
|
82 |
+
$partSize,
|
83 |
+
ceil($sourceSize / MultipartUploader::PART_MAX_NUM)
|
84 |
+
);
|
85 |
+
}
|
86 |
+
|
87 |
+
// Ensure that the part size follows the rules: 5 MB <= size <= 5 GB.
|
88 |
+
if ($partSize < MultipartUploader::PART_MIN_SIZE || $partSize > MultipartUploader::PART_MAX_SIZE) {
|
89 |
+
throw new \InvalidArgumentException('The part size must be no less '
|
90 |
+
. 'than 5 MB and no greater than 5 GB.');
|
91 |
+
}
|
92 |
+
|
93 |
+
return $partSize;
|
94 |
+
}
|
95 |
+
|
96 |
+
protected function getInitiateParams()
|
97 |
+
{
|
98 |
+
$config = $this->getConfig();
|
99 |
+
$params = isset($config['params']) ? $config['params'] : [];
|
100 |
+
|
101 |
+
if (isset($config['acl'])) {
|
102 |
+
$params['ACL'] = $config['acl'];
|
103 |
+
}
|
104 |
+
|
105 |
+
// Set the ContentType if not already present
|
106 |
+
if (empty($params['ContentType']) && $type = $this->getSourceMimeType()) {
|
107 |
+
$params['ContentType'] = $type;
|
108 |
+
}
|
109 |
+
|
110 |
+
return $params;
|
111 |
+
}
|
112 |
+
|
113 |
+
/**
|
114 |
+
* @return UploadState
|
115 |
+
*/
|
116 |
+
abstract protected function getState();
|
117 |
+
|
118 |
+
/**
|
119 |
+
* @return array
|
120 |
+
*/
|
121 |
+
abstract protected function getConfig();
|
122 |
+
|
123 |
+
/**
|
124 |
+
* @return int
|
125 |
+
*/
|
126 |
+
abstract protected function getSourceSize();
|
127 |
+
|
128 |
+
/**
|
129 |
+
* @return string|null
|
130 |
+
*/
|
131 |
+
abstract protected function getSourceMimeType();
|
132 |
+
}
|
lib/Aws/Aws/S3/ObjectCopier.php
ADDED
@@ -0,0 +1,150 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\S3;
|
3 |
+
|
4 |
+
use Aws\Exception\MultipartUploadException;
|
5 |
+
use Aws\Result;
|
6 |
+
use Aws\S3\Exception\S3Exception;
|
7 |
+
use GuzzleHttp\Promise\PromisorInterface;
|
8 |
+
use InvalidArgumentException;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Copies objects from one S3 location to another, utilizing a multipart copy
|
12 |
+
* when appropriate.
|
13 |
+
*/
|
14 |
+
class ObjectCopier implements PromisorInterface
|
15 |
+
{
|
16 |
+
const DEFAULT_MULTIPART_THRESHOLD = MultipartUploader::PART_MAX_SIZE;
|
17 |
+
|
18 |
+
private $client;
|
19 |
+
private $source;
|
20 |
+
private $destination;
|
21 |
+
private $acl;
|
22 |
+
private $options;
|
23 |
+
|
24 |
+
private static $defaults = [
|
25 |
+
'before_lookup' => null,
|
26 |
+
'before_upload' => null,
|
27 |
+
'concurrency' => 5,
|
28 |
+
'mup_threshold' => self::DEFAULT_MULTIPART_THRESHOLD,
|
29 |
+
'params' => [],
|
30 |
+
'part_size' => null,
|
31 |
+
'version_id' => null,
|
32 |
+
];
|
33 |
+
|
34 |
+
/**
|
35 |
+
* @param S3ClientInterface $client The S3 Client used to execute
|
36 |
+
* the copy command(s).
|
37 |
+
* @param array $source The object to copy, specified as
|
38 |
+
* an array with a 'Bucket' and
|
39 |
+
* 'Key' keys. Provide a
|
40 |
+
* 'VersionID' key to copy a
|
41 |
+
* specified version of an object.
|
42 |
+
* @param array $destination The bucket and key to which to
|
43 |
+
* copy the $source, specified as
|
44 |
+
* an array with a 'Bucket' and
|
45 |
+
* 'Key' keys.
|
46 |
+
* @param string $acl ACL to apply to the copy
|
47 |
+
* (default: private).
|
48 |
+
* @param array $options Options used to configure the
|
49 |
+
* copy process. Options passed in
|
50 |
+
* through 'params' are added to
|
51 |
+
* the sub commands.
|
52 |
+
*
|
53 |
+
* @throws InvalidArgumentException
|
54 |
+
*/
|
55 |
+
public function __construct(
|
56 |
+
S3ClientInterface $client,
|
57 |
+
array $source,
|
58 |
+
array $destination,
|
59 |
+
$acl = 'private',
|
60 |
+
array $options = []
|
61 |
+
) {
|
62 |
+
$this->validateLocation($source);
|
63 |
+
$this->validateLocation($destination);
|
64 |
+
|
65 |
+
$this->client = $client;
|
66 |
+
$this->source = $source;
|
67 |
+
$this->destination = $destination;
|
68 |
+
$this->acl = $acl;
|
69 |
+
$this->options = $options + self::$defaults;
|
70 |
+
}
|
71 |
+
|
72 |
+
/**
|
73 |
+
* Perform the configured copy asynchronously. Returns a promise that is
|
74 |
+
* fulfilled with the result of the CompleteMultipartUpload or CopyObject
|
75 |
+
* operation or rejected with an exception.
|
76 |
+
*/
|
77 |
+
public function promise()
|
78 |
+
{
|
79 |
+
return \GuzzleHttp\Promise\coroutine(function () {
|
80 |
+
$headObjectCommand = $this->client->getCommand(
|
81 |
+
'HeadObject',
|
82 |
+
$this->options['params'] + $this->source
|
83 |
+
);
|
84 |
+
if (is_callable($this->options['before_lookup'])) {
|
85 |
+
$this->options['before_lookup']($headObjectCommand);
|
86 |
+
}
|
87 |
+
$objectStats = (yield $this->client->executeAsync(
|
88 |
+
$headObjectCommand
|
89 |
+
));
|
90 |
+
|
91 |
+
if ($objectStats['ContentLength'] > $this->options['mup_threshold']) {
|
92 |
+
$mup = new MultipartCopy(
|
93 |
+
$this->client,
|
94 |
+
$this->getSourcePath(),
|
95 |
+
['source_metadata' => $objectStats, 'acl' => $this->acl]
|
96 |
+
+ $this->destination
|
97 |
+
+ $this->options
|
98 |
+
);
|
99 |
+
|
100 |
+
yield $mup->promise();
|
101 |
+
} else {
|
102 |
+
$defaults = [
|
103 |
+
'ACL' => $this->acl,
|
104 |
+
'MetadataDirective' => 'COPY',
|
105 |
+
'CopySource' => $this->getSourcePath(),
|
106 |
+
];
|
107 |
+
|
108 |
+
$params = array_diff_key($this->options, self::$defaults)
|
109 |
+
+ $this->destination + $defaults + $this->options['params'];
|
110 |
+
|
111 |
+
yield $this->client->executeAsync(
|
112 |
+
$this->client->getCommand('CopyObject', $params)
|
113 |
+
);
|
114 |
+
}
|
115 |
+
});
|
116 |
+
}
|
117 |
+
|
118 |
+
/**
|
119 |
+
* Perform the configured copy synchronously. Returns the result of the
|
120 |
+
* CompleteMultipartUpload or CopyObject operation.
|
121 |
+
*
|
122 |
+
* @return Result
|
123 |
+
*
|
124 |
+
* @throws S3Exception
|
125 |
+
* @throws MultipartUploadException
|
126 |
+
*/
|
127 |
+
public function copy()
|
128 |
+
{
|
129 |
+
return $this->promise()->wait();
|
130 |
+
}
|
131 |
+
|
132 |
+
private function validateLocation(array $location)
|
133 |
+
{
|
134 |
+
if (empty($location['Bucket']) || empty($location['Key'])) {
|
135 |
+
throw new \InvalidArgumentException('Locations provided to an'
|
136 |
+
. ' Aws\S3\ObjectCopier must have a non-empty Bucket and Key');
|
137 |
+
}
|
138 |
+
}
|
139 |
+
|
140 |
+
private function getSourcePath()
|
141 |
+
{
|
142 |
+
$sourcePath = "/{$this->source['Bucket']}/"
|
143 |
+
. rawurlencode($this->source['Key']);
|
144 |
+
if (isset($this->source['VersionId'])) {
|
145 |
+
$sourcePath .= "?versionId={$this->source['VersionId']}";
|
146 |
+
}
|
147 |
+
|
148 |
+
return $sourcePath;
|
149 |
+
}
|
150 |
+
}
|
lib/Aws/Aws/S3/ObjectUploader.php
ADDED
@@ -0,0 +1,140 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\S3;
|
3 |
+
|
4 |
+
use GuzzleHttp\Promise\PromisorInterface;
|
5 |
+
use GuzzleHttp\Psr7;
|
6 |
+
use Psr\Http\Message\StreamInterface;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Uploads an object to S3, using a PutObject command or a multipart upload as
|
10 |
+
* appropriate.
|
11 |
+
*/
|
12 |
+
class ObjectUploader implements PromisorInterface
|
13 |
+
{
|
14 |
+
const DEFAULT_MULTIPART_THRESHOLD = 16777216;
|
15 |
+
|
16 |
+
private $client;
|
17 |
+
private $bucket;
|
18 |
+
private $key;
|
19 |
+
private $body;
|
20 |
+
private $acl;
|
21 |
+
private $options;
|
22 |
+
private static $defaults = [
|
23 |
+
'before_upload' => null,
|
24 |
+
'concurrency' => 3,
|
25 |
+
'mup_threshold' => self::DEFAULT_MULTIPART_THRESHOLD,
|
26 |
+
'params' => [],
|
27 |
+
'part_size' => null,
|
28 |
+
];
|
29 |
+
|
30 |
+
/**
|
31 |
+
* @param S3ClientInterface $client The S3 Client used to execute
|
32 |
+
* the upload command(s).
|
33 |
+
* @param string $bucket Bucket to upload the object.
|
34 |
+
* @param string $key Key of the object.
|
35 |
+
* @param mixed $body Object data to upload. Can be a
|
36 |
+
* StreamInterface, PHP stream
|
37 |
+
* resource, or a string of data to
|
38 |
+
* upload.
|
39 |
+
* @param string $acl ACL to apply to the copy
|
40 |
+
* (default: private).
|
41 |
+
* @param array $options Options used to configure the
|
42 |
+
* copy process. Options passed in
|
43 |
+
* through 'params' are added to
|
44 |
+
* the sub command(s).
|
45 |
+
*/
|
46 |
+
public function __construct(
|
47 |
+
S3ClientInterface $client,
|
48 |
+
$bucket,
|
49 |
+
$key,
|
50 |
+
$body,
|
51 |
+
$acl = 'private',
|
52 |
+
array $options = []
|
53 |
+
) {
|
54 |
+
$this->client = $client;
|
55 |
+
$this->bucket = $bucket;
|
56 |
+
$this->key = $key;
|
57 |
+
$this->body = Psr7\stream_for($body);
|
58 |
+
$this->acl = $acl;
|
59 |
+
$this->options = $options + self::$defaults;
|
60 |
+
}
|
61 |
+
|
62 |
+
public function promise()
|
63 |
+
{
|
64 |
+
/** @var int $mup_threshold */
|
65 |
+
$mup_threshold = $this->options['mup_threshold'];
|
66 |
+
if ($this->requiresMultipart($this->body, $mup_threshold)) {
|
67 |
+
// Perform a multipart upload.
|
68 |
+
return (new MultipartUploader($this->client, $this->body, [
|
69 |
+
'bucket' => $this->bucket,
|
70 |
+
'key' => $this->key,
|
71 |
+
'acl' => $this->acl
|
72 |
+
] + $this->options))->promise();
|
73 |
+
}
|
74 |
+
|
75 |
+
// Perform a regular PutObject operation.
|
76 |
+
$command = $this->client->getCommand('PutObject', [
|
77 |
+
'Bucket' => $this->bucket,
|
78 |
+
'Key' => $this->key,
|
79 |
+
'Body' => $this->body,
|
80 |
+
'ACL' => $this->acl,
|
81 |
+
] + $this->options['params']);
|
82 |
+
if (is_callable($this->options['before_upload'])) {
|
83 |
+
$this->options['before_upload']($command);
|
84 |
+
}
|
85 |
+
return $this->client->executeAsync($command);
|
86 |
+
}
|
87 |
+
|
88 |
+
public function upload()
|
89 |
+
{
|
90 |
+
return $this->promise()->wait();
|
91 |
+
}
|
92 |
+
|
93 |
+
/**
|
94 |
+
* Determines if the body should be uploaded using PutObject or the
|
95 |
+
* Multipart Upload System. It also modifies the passed-in $body as needed
|
96 |
+
* to support the upload.
|
97 |
+
*
|
98 |
+
* @param StreamInterface $body Stream representing the body.
|
99 |
+
* @param integer $threshold Minimum bytes before using Multipart.
|
100 |
+
*
|
101 |
+
* @return bool
|
102 |
+
*/
|
103 |
+
private function requiresMultipart(StreamInterface &$body, $threshold)
|
104 |
+
{
|
105 |
+
// If body size known, compare to threshold to determine if Multipart.
|
106 |
+
if ($body->getSize() !== null) {
|
107 |
+
return $body->getSize() >= $threshold;
|
108 |
+
}
|
109 |
+
|
110 |
+
/**
|
111 |
+
* Handle the situation where the body size is unknown.
|
112 |
+
* Read up to 5MB into a buffer to determine how to upload the body.
|
113 |
+
* @var StreamInterface $buffer
|
114 |
+
*/
|
115 |
+
$buffer = Psr7\stream_for();
|
116 |
+
Psr7\copy_to_stream($body, $buffer, MultipartUploader::PART_MIN_SIZE);
|
117 |
+
|
118 |
+
// If body < 5MB, use PutObject with the buffer.
|
119 |
+
if ($buffer->getSize() < MultipartUploader::PART_MIN_SIZE) {
|
120 |
+
$buffer->seek(0);
|
121 |
+
$body = $buffer;
|
122 |
+
return false;
|
123 |
+
}
|
124 |
+
|
125 |
+
// If body >= 5 MB, then use multipart. [YES]
|
126 |
+
if ($body->isSeekable() && $body->getMetadata('uri') !== 'php://input') {
|
127 |
+
// If the body is seekable, just rewind the body.
|
128 |
+
$body->seek(0);
|
129 |
+
} else {
|
130 |
+
// If the body is non-seekable, stitch the rewind the buffer and
|
131 |
+
// the partially read body together into one stream. This avoids
|
132 |
+
// unnecessary disc usage and does not require seeking on the
|
133 |
+
// original stream.
|
134 |
+
$buffer->seek(0);
|
135 |
+
$body = new Psr7\AppendStream([$buffer, $body]);
|
136 |
+
}
|
137 |
+
|
138 |
+
return true;
|
139 |
+
}
|
140 |
+
}
|
lib/Aws/Aws/S3/PermanentRedirectMiddleware.php
ADDED
@@ -0,0 +1,62 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\S3;
|
3 |
+
|
4 |
+
use Aws\CommandInterface;
|
5 |
+
use Aws\ResultInterface;
|
6 |
+
use Aws\S3\Exception\PermanentRedirectException;
|
7 |
+
use Psr\Http\Message\RequestInterface;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Throws a PermanentRedirectException exception when a 301 redirect is
|
11 |
+
* encountered.
|
12 |
+
*
|
13 |
+
* @internal
|
14 |
+
*/
|
15 |
+
class PermanentRedirectMiddleware
|
16 |
+
{
|
17 |
+
/** @var callable */
|
18 |
+
private $nextHandler;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Create a middleware wrapper function.
|
22 |
+
*
|
23 |
+
* @return callable
|
24 |
+
*/
|
25 |
+
public static function wrap()
|
26 |
+
{
|
27 |
+
return function (callable $handler) {
|
28 |
+
return new self($handler);
|
29 |
+
};
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* @param callable $nextHandler Next handler to invoke.
|
34 |
+
*/
|
35 |
+
public function __construct(callable $nextHandler)
|
36 |
+
{
|
37 |
+
$this->nextHandler = $nextHandler;
|
38 |
+
}
|
39 |
+
|
40 |
+
public function __invoke(CommandInterface $command, RequestInterface $request = null)
|
41 |
+
{
|
42 |
+
$next = $this->nextHandler;
|
43 |
+
return $next($command, $request)->then(
|
44 |
+
function (ResultInterface $result) use ($command) {
|
45 |
+
$status = isset($result['@metadata']['statusCode'])
|
46 |
+
? $result['@metadata']['statusCode']
|
47 |
+
: null;
|
48 |
+
if ($status == 301) {
|
49 |
+
throw new PermanentRedirectException(
|
50 |
+
'Encountered a permanent redirect while requesting '
|
51 |
+
. $result->search('"@metadata".effectiveUri') . '. '
|
52 |
+
. 'Are you sure you are using the correct region for '
|
53 |
+
. 'this bucket?',
|
54 |
+
$command,
|
55 |
+
['result' => $result]
|
56 |
+
);
|
57 |
+
}
|
58 |
+
return $result;
|
59 |
+
}
|
60 |
+
);
|
61 |
+
}
|
62 |
+
}
|
lib/Aws/Aws/S3/PostObject.php
ADDED
@@ -0,0 +1,160 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\S3;
|
3 |
+
|
4 |
+
use Aws\Credentials\CredentialsInterface;
|
5 |
+
use GuzzleHttp\Psr7\Uri;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* @deprecated
|
9 |
+
*/
|
10 |
+
class PostObject
|
11 |
+
{
|
12 |
+
private $client;
|
13 |
+
private $bucket;
|
14 |
+
private $formAttributes;
|
15 |
+
private $formInputs;
|
16 |
+
private $jsonPolicy;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Constructs the PostObject.
|
20 |
+
*
|
21 |
+
* @param S3ClientInterface $client Client used with the POST object
|
22 |
+
* @param string $bucket Bucket to use
|
23 |
+
* @param array $formInputs Associative array of form input
|
24 |
+
* fields.
|
25 |
+
* @param string|array $jsonPolicy JSON encoded POST policy document.
|
26 |
+
* The policy will be base64 encoded
|
27 |
+
* and applied to the form on your
|
28 |
+
* behalf.
|
29 |
+
*/
|
30 |
+
public function __construct(
|
31 |
+
S3ClientInterface $client,
|
32 |
+
$bucket,
|
33 |
+
array $formInputs,
|
34 |
+
$jsonPolicy
|
35 |
+
) {
|
36 |
+
$this->client = $client;
|
37 |
+
$this->bucket = $bucket;
|
38 |
+
|
39 |
+
if (is_array($jsonPolicy)) {
|
40 |
+
$jsonPolicy = json_encode($jsonPolicy);
|
41 |
+
}
|
42 |
+
|
43 |
+
$this->jsonPolicy = $jsonPolicy;
|
44 |
+
$this->formAttributes = [
|
45 |
+
'action' => $this->generateUri(),
|
46 |
+
'method' => 'POST',
|
47 |
+
'enctype' => 'multipart/form-data'
|
48 |
+
];
|
49 |
+
|
50 |
+
$this->formInputs = $formInputs + ['key' => '${filename}'];
|
51 |
+
$credentials = $client->getCredentials()->wait();
|
52 |
+
$this->formInputs += $this->getPolicyAndSignature($credentials);
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Gets the S3 client.
|
57 |
+
*
|
58 |
+
* @return S3ClientInterface
|
59 |
+
*/
|
60 |
+
public function getClient()
|
61 |
+
{
|
62 |
+
return $this->client;
|
63 |
+
}
|
64 |
+
|
65 |
+
/**
|
66 |
+
* Gets the bucket name.
|
67 |
+
*
|
68 |
+
* @return string
|
69 |
+
*/
|
70 |
+
public function getBucket()
|
71 |
+
{
|
72 |
+
return $this->bucket;
|
73 |
+
}
|
74 |
+
|
75 |
+
/**
|
76 |
+
* Gets the form attributes as an array.
|
77 |
+
*
|
78 |
+
* @return array
|
79 |
+
*/
|
80 |
+
public function getFormAttributes()
|
81 |
+
{
|
82 |
+
return $this->formAttributes;
|
83 |
+
}
|
84 |
+
|
85 |
+
/**
|
86 |
+
* Set a form attribute.
|
87 |
+
*
|
88 |
+
* @param string $attribute Form attribute to set.
|
89 |
+
* @param string $value Value to set.
|
90 |
+
*/
|
91 |
+
public function setFormAttribute($attribute, $value)
|
92 |
+
{
|
93 |
+
$this->formAttributes[$attribute] = $value;
|
94 |
+
}
|
95 |
+
|
96 |
+
/**
|
97 |
+
* Gets the form inputs as an array.
|
98 |
+
*
|
99 |
+
* @return array
|
100 |
+
*/
|
101 |
+
public function getFormInputs()
|
102 |
+
{
|
103 |
+
return $this->formInputs;
|
104 |
+
}
|
105 |
+
|
106 |
+
/**
|
107 |
+
* Set a form input.
|
108 |
+
*
|
109 |
+
* @param string $field Field name to set
|
110 |
+
* @param string $value Value to set.
|
111 |
+
*/
|
112 |
+
public function setFormInput($field, $value)
|
113 |
+
{
|
114 |
+
$this->formInputs[$field] = $value;
|
115 |
+
}
|
116 |
+
|
117 |
+
/**
|
118 |
+
* Gets the raw JSON policy.
|
119 |
+
*
|
120 |
+
* @return string
|
121 |
+
*/
|
122 |
+
public function getJsonPolicy()
|
123 |
+
{
|
124 |
+
return $this->jsonPolicy;
|
125 |
+
}
|
126 |
+
|
127 |
+
private function generateUri()
|
128 |
+
{
|
129 |
+
$uri = new Uri($this->client->getEndpoint());
|
130 |
+
|
131 |
+
if ($this->client->getConfig('use_path_style_endpoint') === true
|
132 |
+
|| ($uri->getScheme() === 'https'
|
133 |
+
&& strpos($this->bucket, '.') !== false)
|
134 |
+
) {
|
135 |
+
// Use path-style URLs
|
136 |
+
$uri = $uri->withPath("/{$this->bucket}");
|
137 |
+
} else {
|
138 |
+
// Use virtual-style URLs
|
139 |
+
$uri = $uri->withHost($this->bucket . '.' . $uri->getHost());
|
140 |
+
}
|
141 |
+
|
142 |
+
return (string) $uri;
|
143 |
+
}
|
144 |
+
|
145 |
+
protected function getPolicyAndSignature(CredentialsInterface $creds)
|
146 |
+
{
|
147 |
+
$jsonPolicy64 = base64_encode($this->jsonPolicy);
|
148 |
+
|
149 |
+
return [
|
150 |
+
'AWSAccessKeyId' => $creds->getAccessKeyId(),
|
151 |
+
'policy' => $jsonPolicy64,
|
152 |
+
'signature' => base64_encode(hash_hmac(
|
153 |
+
'sha1',
|
154 |
+
$jsonPolicy64,
|
155 |
+
$creds->getSecretKey(),
|
156 |
+
true
|
157 |
+
))
|
158 |
+
];
|
159 |
+
}
|
160 |
+
}
|
lib/Aws/Aws/S3/PostObjectV4.php
ADDED
@@ -0,0 +1,195 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\S3;
|
3 |
+
|
4 |
+
use Aws\Credentials\CredentialsInterface;
|
5 |
+
use GuzzleHttp\Psr7\Uri;
|
6 |
+
use Aws\Signature\SignatureTrait;
|
7 |
+
use Aws\Signature\SignatureV4 as SignatureV4;
|
8 |
+
use Aws\Api\TimestampShape as TimestampShape;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Encapsulates the logic for getting the data for an S3 object POST upload form
|
12 |
+
*
|
13 |
+
* @link http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPOST.html
|
14 |
+
* @link http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-post-example.html
|
15 |
+
*/
|
16 |
+
class PostObjectV4
|
17 |
+
{
|
18 |
+
use SignatureTrait;
|
19 |
+
|
20 |
+
private $client;
|
21 |
+
private $bucket;
|
22 |
+
private $formAttributes;
|
23 |
+
private $formInputs;
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Constructs the PostObject.
|
27 |
+
*
|
28 |
+
* The options array accepts the following keys:
|
29 |
+
* @link http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html
|
30 |
+
*
|
31 |
+
* @param S3ClientInterface $client Client used with the POST object
|
32 |
+
* @param string $bucket Bucket to use
|
33 |
+
* @param array $formInputs Associative array of form input
|
34 |
+
* fields.
|
35 |
+
* @param array $options Policy condition options
|
36 |
+
* @param mixed $expiration Upload expiration time value. By
|
37 |
+
* default: 1 hour valid period.
|
38 |
+
*/
|
39 |
+
public function __construct(
|
40 |
+
S3ClientInterface $client,
|
41 |
+
$bucket,
|
42 |
+
array $formInputs,
|
43 |
+
array $options = [],
|
44 |
+
$expiration = '+1 hours'
|
45 |
+
) {
|
46 |
+
$this->client = $client;
|
47 |
+
$this->bucket = $bucket;
|
48 |
+
|
49 |
+
// setup form attributes
|
50 |
+
$this->formAttributes = [
|
51 |
+
'action' => $this->generateUri(),
|
52 |
+
'method' => 'POST',
|
53 |
+
'enctype' => 'multipart/form-data'
|
54 |
+
];
|
55 |
+
|
56 |
+
$credentials = $this->client->getCredentials()->wait();
|
57 |
+
|
58 |
+
if ($securityToken = $credentials->getSecurityToken()) {
|
59 |
+
array_push($options, ['x-amz-security-token' => $securityToken]);
|
60 |
+
$formInputs['X-Amz-Security-Token'] = $securityToken;
|
61 |
+
}
|
62 |
+
|
63 |
+
// setup basic policy
|
64 |
+
$policy = [
|
65 |
+
'expiration' => TimestampShape::format($expiration, 'iso8601'),
|
66 |
+
'conditions' => $options,
|
67 |
+
];
|
68 |
+
|
69 |
+
// setup basic formInputs
|
70 |
+
$this->formInputs = $formInputs + ['key' => '${filename}'];
|
71 |
+
|
72 |
+
// finalize policy and signature
|
73 |
+
|
74 |
+
$this->formInputs += $this->getPolicyAndSignature(
|
75 |
+
$credentials,
|
76 |
+
$policy
|
77 |
+
);
|
78 |
+
}
|
79 |
+
|
80 |
+
/**
|
81 |
+
* Gets the S3 client.
|
82 |
+
*
|
83 |
+
* @return S3ClientInterface
|
84 |
+
*/
|
85 |
+
public function getClient()
|
86 |
+
{
|
87 |
+
return $this->client;
|
88 |
+
}
|
89 |
+
|
90 |
+
/**
|
91 |
+
* Gets the bucket name.
|
92 |
+
*
|
93 |
+
* @return string
|
94 |
+
*/
|
95 |
+
public function getBucket()
|
96 |
+
{
|
97 |
+
return $this->bucket;
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
* Gets the form attributes as an array.
|
102 |
+
*
|
103 |
+
* @return array
|
104 |
+
*/
|
105 |
+
public function getFormAttributes()
|
106 |
+
{
|
107 |
+
return $this->formAttributes;
|
108 |
+
}
|
109 |
+
|
110 |
+
/**
|
111 |
+
* Set a form attribute.
|
112 |
+
*
|
113 |
+
* @param string $attribute Form attribute to set.
|
114 |
+
* @param string $value Value to set.
|
115 |
+
*/
|
116 |
+
public function setFormAttribute($attribute, $value)
|
117 |
+
{
|
118 |
+
$this->formAttributes[$attribute] = $value;
|
119 |
+
}
|
120 |
+
|
121 |
+
/**
|
122 |
+
* Gets the form inputs as an array.
|
123 |
+
*
|
124 |
+
* @return array
|
125 |
+
*/
|
126 |
+
public function getFormInputs()
|
127 |
+
{
|
128 |
+
return $this->formInputs;
|
129 |
+
}
|
130 |
+
|
131 |
+
/**
|
132 |
+
* Set a form input.
|
133 |
+
*
|
134 |
+
* @param string $field Field name to set
|
135 |
+
* @param string $value Value to set.
|
136 |
+
*/
|
137 |
+
public function setFormInput($field, $value)
|
138 |
+
{
|
139 |
+
$this->formInputs[$field] = $value;
|
140 |
+
}
|
141 |
+
|
142 |
+
private function generateUri()
|
143 |
+
{
|
144 |
+
$uri = new Uri($this->client->getEndpoint());
|
145 |
+
|
146 |
+
if ($this->client->getConfig('use_path_style_endpoint') === true
|
147 |
+
|| ($uri->getScheme() === 'https'
|
148 |
+
&& strpos($this->bucket, '.') !== false)
|
149 |
+
) {
|
150 |
+
// Use path-style URLs
|
151 |
+
$uri = $uri->withPath("/{$this->bucket}");
|
152 |
+
} else {
|
153 |
+
// Use virtual-style URLs if haven't been set up already
|
154 |
+
if (strpos($uri->getHost(), $this->bucket . '.') !== 0) {
|
155 |
+
$uri = $uri->withHost($this->bucket . '.' . $uri->getHost());
|
156 |
+
}
|
157 |
+
}
|
158 |
+
|
159 |
+
return (string) $uri;
|
160 |
+
}
|
161 |
+
|
162 |
+
protected function getPolicyAndSignature(
|
163 |
+
CredentialsInterface $credentials,
|
164 |
+
array $policy
|
165 |
+
){
|
166 |
+
$ldt = gmdate(SignatureV4::ISO8601_BASIC);
|
167 |
+
$sdt = substr($ldt, 0, 8);
|
168 |
+
$policy['conditions'][] = ['X-Amz-Date' => $ldt];
|
169 |
+
|
170 |
+
$region = $this->client->getRegion();
|
171 |
+
$scope = $this->createScope($sdt, $region, 's3');
|
172 |
+
$creds = "{$credentials->getAccessKeyId()}/$scope";
|
173 |
+
$policy['conditions'][] = ['X-Amz-Credential' => $creds];
|
174 |
+
|
175 |
+
$policy['conditions'][] = ['X-Amz-Algorithm' => "AWS4-HMAC-SHA256"];
|
176 |
+
|
177 |
+
$jsonPolicy64 = base64_encode(json_encode($policy));
|
178 |
+
$key = $this->getSigningKey(
|
179 |
+
$sdt,
|
180 |
+
$region,
|
181 |
+
's3',
|
182 |
+
$credentials->getSecretKey()
|
183 |
+
);
|
184 |
+
|
185 |
+
return [
|
186 |
+
'X-Amz-Credential' => $creds,
|
187 |
+
'X-Amz-Algorithm' => "AWS4-HMAC-SHA256",
|
188 |
+
'X-Amz-Date' => $ldt,
|
189 |
+
'Policy' => $jsonPolicy64,
|
190 |
+
'X-Amz-Signature' => bin2hex(
|
191 |
+
hash_hmac('sha256', $jsonPolicy64, $key, true)
|
192 |
+
),
|
193 |
+
];
|
194 |
+
}
|
195 |
+
}
|
lib/Aws/Aws/S3/PutObjectUrlMiddleware.php
ADDED
@@ -0,0 +1,57 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\S3;
|
3 |
+
|
4 |
+
use Aws\CommandInterface;
|
5 |
+
use Aws\ResultInterface;
|
6 |
+
use Psr\Http\Message\RequestInterface;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Injects ObjectURL into the result of the PutObject operation.
|
10 |
+
*
|
11 |
+
* @internal
|
12 |
+
*/
|
13 |
+
class PutObjectUrlMiddleware
|
14 |
+
{
|
15 |
+
/** @var callable */
|
16 |
+
private $nextHandler;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Create a middleware wrapper function.
|
20 |
+
*
|
21 |
+
* @return callable
|
22 |
+
*/
|
23 |
+
public static function wrap()
|
24 |
+
{
|
25 |
+
return function (callable $handler) {
|
26 |
+
return new self($handler);
|
27 |
+
};
|
28 |
+
}
|
29 |
+
|
30 |
+
/**
|
31 |
+
* @param callable $nextHandler Next handler to invoke.
|
32 |
+
*/
|
33 |
+
public function __construct(callable $nextHandler)
|
34 |
+
{
|
35 |
+
$this->nextHandler = $nextHandler;
|
36 |
+
}
|
37 |
+
|
38 |
+
public function __invoke(CommandInterface $command, RequestInterface $request = null)
|
39 |
+
{
|
40 |
+
$next = $this->nextHandler;
|
41 |
+
return $next($command, $request)->then(
|
42 |
+
function (ResultInterface $result) use ($command) {
|
43 |
+
$name = $command->getName();
|
44 |
+
switch ($name) {
|
45 |
+
case 'PutObject':
|
46 |
+
case 'CopyObject':
|
47 |
+
$result['ObjectURL'] = $result['@metadata']['effectiveUri'];
|
48 |
+
break;
|
49 |
+
case 'CompleteMultipartUpload':
|
50 |
+
$result['ObjectURL'] = $result['Location'];
|
51 |
+
break;
|
52 |
+
}
|
53 |
+
return $result;
|
54 |
+
}
|
55 |
+
);
|
56 |
+
}
|
57 |
+
}
|
lib/Aws/Aws/S3/RetryableMalformedResponseParser.php
ADDED
@@ -0,0 +1,56 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\S3;
|
3 |
+
|
4 |
+
use Aws\Api\Parser\AbstractParser;
|
5 |
+
use Aws\Api\StructureShape;
|
6 |
+
use Aws\Api\Parser\Exception\ParserException;
|
7 |
+
use Aws\CommandInterface;
|
8 |
+
use Aws\Exception\AwsException;
|
9 |
+
use Psr\Http\Message\ResponseInterface;
|
10 |
+
use Psr\Http\Message\StreamInterface;
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Converts malformed responses to a retryable error type.
|
14 |
+
*
|
15 |
+
* @internal
|
16 |
+
*/
|
17 |
+
class RetryableMalformedResponseParser extends AbstractParser
|
18 |
+
{
|
19 |
+
/** @var string */
|
20 |
+
private $exceptionClass;
|
21 |
+
|
22 |
+
public function __construct(
|
23 |
+
callable $parser,
|
24 |
+
$exceptionClass = AwsException::class
|
25 |
+
) {
|
26 |
+
$this->parser = $parser;
|
27 |
+
$this->exceptionClass = $exceptionClass;
|
28 |
+
}
|
29 |
+
|
30 |
+
public function __invoke(
|
31 |
+
CommandInterface $command,
|
32 |
+
ResponseInterface $response
|
33 |
+
) {
|
34 |
+
$fn = $this->parser;
|
35 |
+
|
36 |
+
try {
|
37 |
+
return $fn($command, $response);
|
38 |
+
} catch (ParserException $e) {
|
39 |
+
throw new $this->exceptionClass(
|
40 |
+
"Error parsing response for {$command->getName()}:"
|
41 |
+
. " AWS parsing error: {$e->getMessage()}",
|
42 |
+
$command,
|
43 |
+
['connection_error' => true, 'exception' => $e],
|
44 |
+
$e
|
45 |
+
);
|
46 |
+
}
|
47 |
+
}
|
48 |
+
|
49 |
+
public function parseMemberFromStream(
|
50 |
+
StreamInterface $stream,
|
51 |
+
StructureShape $member,
|
52 |
+
$response
|
53 |
+
) {
|
54 |
+
return $this->parser->parseMemberFromStream($stream, $member, $response);
|
55 |
+
}
|
56 |
+
}
|
lib/Aws/Aws/S3/S3Client.php
ADDED
@@ -0,0 +1,633 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\S3;
|
3 |
+
|
4 |
+
use Aws\Api\ApiProvider;
|
5 |
+
use Aws\Api\DocModel;
|
6 |
+
use Aws\Api\Service;
|
7 |
+
use Aws\AwsClient;
|
8 |
+
use Aws\ClientResolver;
|
9 |
+
use Aws\Command;
|
10 |
+
use Aws\Exception\AwsException;
|
11 |
+
use Aws\HandlerList;
|
12 |
+
use Aws\Middleware;
|
13 |
+
use Aws\RetryMiddleware;
|
14 |
+
use Aws\ResultInterface;
|
15 |
+
use Aws\CommandInterface;
|
16 |
+
use GuzzleHttp\Exception\RequestException;
|
17 |
+
use Psr\Http\Message\RequestInterface;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Client used to interact with **Amazon Simple Storage Service (Amazon S3)**.
|
21 |
+
*
|
22 |
+
* @method \Aws\Result abortMultipartUpload(array $args = [])
|
23 |
+
* @method \GuzzleHttp\Promise\Promise abortMultipartUploadAsync(array $args = [])
|
24 |
+
* @method \Aws\Result completeMultipartUpload(array $args = [])
|
25 |
+
* @method \GuzzleHttp\Promise\Promise completeMultipartUploadAsync(array $args = [])
|
26 |
+
* @method \Aws\Result copyObject(array $args = [])
|
27 |
+
* @method \GuzzleHttp\Promise\Promise copyObjectAsync(array $args = [])
|
28 |
+
* @method \Aws\Result createBucket(array $args = [])
|
29 |
+
* @method \GuzzleHttp\Promise\Promise createBucketAsync(array $args = [])
|
30 |
+
* @method \Aws\Result createMultipartUpload(array $args = [])
|
31 |
+
* @method \GuzzleHttp\Promise\Promise createMultipartUploadAsync(array $args = [])
|
32 |
+
* @method \Aws\Result deleteBucket(array $args = [])
|
33 |
+
* @method \GuzzleHttp\Promise\Promise deleteBucketAsync(array $args = [])
|
34 |
+
* @method \Aws\Result deleteBucketAnalyticsConfiguration(array $args = [])
|
35 |
+
* @method \GuzzleHttp\Promise\Promise deleteBucketAnalyticsConfigurationAsync(array $args = [])
|
36 |
+
* @method \Aws\Result deleteBucketCors(array $args = [])
|
37 |
+
* @method \GuzzleHttp\Promise\Promise deleteBucketCorsAsync(array $args = [])
|
38 |
+
* @method \Aws\Result deleteBucketEncryption(array $args = [])
|
39 |
+
* @method \GuzzleHttp\Promise\Promise deleteBucketEncryptionAsync(array $args = [])
|
40 |
+
* @method \Aws\Result deleteBucketInventoryConfiguration(array $args = [])
|
41 |
+
* @method \GuzzleHttp\Promise\Promise deleteBucketInventoryConfigurationAsync(array $args = [])
|
42 |
+
* @method \Aws\Result deleteBucketLifecycle(array $args = [])
|
43 |
+
* @method \GuzzleHttp\Promise\Promise deleteBucketLifecycleAsync(array $args = [])
|
44 |
+
* @method \Aws\Result deleteBucketMetricsConfiguration(array $args = [])
|
45 |
+
* @method \GuzzleHttp\Promise\Promise deleteBucketMetricsConfigurationAsync(array $args = [])
|
46 |
+
* @method \Aws\Result deleteBucketPolicy(array $args = [])
|
47 |
+
* @method \GuzzleHttp\Promise\Promise deleteBucketPolicyAsync(array $args = [])
|
48 |
+
* @method \Aws\Result deleteBucketReplication(array $args = [])
|
49 |
+
* @method \GuzzleHttp\Promise\Promise deleteBucketReplicationAsync(array $args = [])
|
50 |
+
* @method \Aws\Result deleteBucketTagging(array $args = [])
|
51 |
+
* @method \GuzzleHttp\Promise\Promise deleteBucketTaggingAsync(array $args = [])
|
52 |
+
* @method \Aws\Result deleteBucketWebsite(array $args = [])
|
53 |
+
* @method \GuzzleHttp\Promise\Promise deleteBucketWebsiteAsync(array $args = [])
|
54 |
+
* @method \Aws\Result deleteObject(array $args = [])
|
55 |
+
* @method \GuzzleHttp\Promise\Promise deleteObjectAsync(array $args = [])
|
56 |
+
* @method \Aws\Result deleteObjectTagging(array $args = [])
|
57 |
+
* @method \GuzzleHttp\Promise\Promise deleteObjectTaggingAsync(array $args = [])
|
58 |
+
* @method \Aws\Result deleteObjects(array $args = [])
|
59 |
+
* @method \GuzzleHttp\Promise\Promise deleteObjectsAsync(array $args = [])
|
60 |
+
* @method \Aws\Result deletePublicAccessBlock(array $args = [])
|
61 |
+
* @method \GuzzleHttp\Promise\Promise deletePublicAccessBlockAsync(array $args = [])
|
62 |
+
* @method \Aws\Result getBucketAccelerateConfiguration(array $args = [])
|
63 |
+
* @method \GuzzleHttp\Promise\Promise getBucketAccelerateConfigurationAsync(array $args = [])
|
64 |
+
* @method \Aws\Result getBucketAcl(array $args = [])
|
65 |
+
* @method \GuzzleHttp\Promise\Promise getBucketAclAsync(array $args = [])
|
66 |
+
* @method \Aws\Result getBucketAnalyticsConfiguration(array $args = [])
|
67 |
+
* @method \GuzzleHttp\Promise\Promise getBucketAnalyticsConfigurationAsync(array $args = [])
|
68 |
+
* @method \Aws\Result getBucketCors(array $args = [])
|
69 |
+
* @method \GuzzleHttp\Promise\Promise getBucketCorsAsync(array $args = [])
|
70 |
+
* @method \Aws\Result getBucketEncryption(array $args = [])
|
71 |
+
* @method \GuzzleHttp\Promise\Promise getBucketEncryptionAsync(array $args = [])
|
72 |
+
* @method \Aws\Result getBucketInventoryConfiguration(array $args = [])
|
73 |
+
* @method \GuzzleHttp\Promise\Promise getBucketInventoryConfigurationAsync(array $args = [])
|
74 |
+
* @method \Aws\Result getBucketLifecycle(array $args = [])
|
75 |
+
* @method \GuzzleHttp\Promise\Promise getBucketLifecycleAsync(array $args = [])
|
76 |
+
* @method \Aws\Result getBucketLifecycleConfiguration(array $args = [])
|
77 |
+
* @method \GuzzleHttp\Promise\Promise getBucketLifecycleConfigurationAsync(array $args = [])
|
78 |
+
* @method \Aws\Result getBucketLocation(array $args = [])
|
79 |
+
* @method \GuzzleHttp\Promise\Promise getBucketLocationAsync(array $args = [])
|
80 |
+
* @method \Aws\Result getBucketLogging(array $args = [])
|
81 |
+
* @method \GuzzleHttp\Promise\Promise getBucketLoggingAsync(array $args = [])
|
82 |
+
* @method \Aws\Result getBucketMetricsConfiguration(array $args = [])
|
83 |
+
* @method \GuzzleHttp\Promise\Promise getBucketMetricsConfigurationAsync(array $args = [])
|
84 |
+
* @method \Aws\Result getBucketNotification(array $args = [])
|
85 |
+
* @method \GuzzleHttp\Promise\Promise getBucketNotificationAsync(array $args = [])
|
86 |
+
* @method \Aws\Result getBucketNotificationConfiguration(array $args = [])
|
87 |
+
* @method \GuzzleHttp\Promise\Promise getBucketNotificationConfigurationAsync(array $args = [])
|
88 |
+
* @method \Aws\Result getBucketPolicy(array $args = [])
|
89 |
+
* @method \GuzzleHttp\Promise\Promise getBucketPolicyAsync(array $args = [])
|
90 |
+
* @method \Aws\Result getBucketPolicyStatus(array $args = [])
|
91 |
+
* @method \GuzzleHttp\Promise\Promise getBucketPolicyStatusAsync(array $args = [])
|
92 |
+
* @method \Aws\Result getBucketReplication(array $args = [])
|
93 |
+
* @method \GuzzleHttp\Promise\Promise getBucketReplicationAsync(array $args = [])
|
94 |
+
* @method \Aws\Result getBucketRequestPayment(array $args = [])
|
95 |
+
* @method \GuzzleHttp\Promise\Promise getBucketRequestPaymentAsync(array $args = [])
|
96 |
+
* @method \Aws\Result getBucketTagging(array $args = [])
|
97 |
+
* @method \GuzzleHttp\Promise\Promise getBucketTaggingAsync(array $args = [])
|
98 |
+
* @method \Aws\Result getBucketVersioning(array $args = [])
|
99 |
+
* @method \GuzzleHttp\Promise\Promise getBucketVersioningAsync(array $args = [])
|
100 |
+
* @method \Aws\Result getBucketWebsite(array $args = [])
|
101 |
+
* @method \GuzzleHttp\Promise\Promise getBucketWebsiteAsync(array $args = [])
|
102 |
+
* @method \Aws\Result getObject(array $args = [])
|
103 |
+
* @method \GuzzleHttp\Promise\Promise getObjectAsync(array $args = [])
|
104 |
+
* @method \Aws\Result getObjectAcl(array $args = [])
|
105 |
+
* @method \GuzzleHttp\Promise\Promise getObjectAclAsync(array $args = [])
|
106 |
+
* @method \Aws\Result getObjectLegalHold(array $args = [])
|
107 |
+
* @method \GuzzleHttp\Promise\Promise getObjectLegalHoldAsync(array $args = [])
|
108 |
+
* @method \Aws\Result getObjectLockConfiguration(array $args = [])
|
109 |
+
* @method \GuzzleHttp\Promise\Promise getObjectLockConfigurationAsync(array $args = [])
|
110 |
+
* @method \Aws\Result getObjectRetention(array $args = [])
|
111 |
+
* @method \GuzzleHttp\Promise\Promise getObjectRetentionAsync(array $args = [])
|
112 |
+
* @method \Aws\Result getObjectTagging(array $args = [])
|
113 |
+
* @method \GuzzleHttp\Promise\Promise getObjectTaggingAsync(array $args = [])
|
114 |
+
* @method \Aws\Result getObjectTorrent(array $args = [])
|
115 |
+
* @method \GuzzleHttp\Promise\Promise getObjectTorrentAsync(array $args = [])
|
116 |
+
* @method \Aws\Result getPublicAccessBlock(array $args = [])
|
117 |
+
* @method \GuzzleHttp\Promise\Promise getPublicAccessBlockAsync(array $args = [])
|
118 |
+
* @method \Aws\Result headBucket(array $args = [])
|
119 |
+
* @method \GuzzleHttp\Promise\Promise headBucketAsync(array $args = [])
|
120 |
+
* @method \Aws\Result headObject(array $args = [])
|
121 |
+
* @method \GuzzleHttp\Promise\Promise headObjectAsync(array $args = [])
|
122 |
+
* @method \Aws\Result listBucketAnalyticsConfigurations(array $args = [])
|
123 |
+
* @method \GuzzleHttp\Promise\Promise listBucketAnalyticsConfigurationsAsync(array $args = [])
|
124 |
+
* @method \Aws\Result listBucketInventoryConfigurations(array $args = [])
|
125 |
+
* @method \GuzzleHttp\Promise\Promise listBucketInventoryConfigurationsAsync(array $args = [])
|
126 |
+
* @method \Aws\Result listBucketMetricsConfigurations(array $args = [])
|
127 |
+
* @method \GuzzleHttp\Promise\Promise listBucketMetricsConfigurationsAsync(array $args = [])
|
128 |
+
* @method \Aws\Result listBuckets(array $args = [])
|
129 |
+
* @method \GuzzleHttp\Promise\Promise listBucketsAsync(array $args = [])
|
130 |
+
* @method \Aws\Result listMultipartUploads(array $args = [])
|
131 |
+
* @method \GuzzleHttp\Promise\Promise listMultipartUploadsAsync(array $args = [])
|
132 |
+
* @method \Aws\Result listObjectVersions(array $args = [])
|
133 |
+
* @method \GuzzleHttp\Promise\Promise listObjectVersionsAsync(array $args = [])
|
134 |
+
* @method \Aws\Result listObjects(array $args = [])
|
135 |
+
* @method \GuzzleHttp\Promise\Promise listObjectsAsync(array $args = [])
|
136 |
+
* @method \Aws\Result listObjectsV2(array $args = [])
|
137 |
+
* @method \GuzzleHttp\Promise\Promise listObjectsV2Async(array $args = [])
|
138 |
+
* @method \Aws\Result listParts(array $args = [])
|
139 |
+
* @method \GuzzleHttp\Promise\Promise listPartsAsync(array $args = [])
|
140 |
+
* @method \Aws\Result putBucketAccelerateConfiguration(array $args = [])
|
141 |
+
* @method \GuzzleHttp\Promise\Promise putBucketAccelerateConfigurationAsync(array $args = [])
|
142 |
+
* @method \Aws\Result putBucketAcl(array $args = [])
|
143 |
+
* @method \GuzzleHttp\Promise\Promise putBucketAclAsync(array $args = [])
|
144 |
+
* @method \Aws\Result putBucketAnalyticsConfiguration(array $args = [])
|
145 |
+
* @method \GuzzleHttp\Promise\Promise putBucketAnalyticsConfigurationAsync(array $args = [])
|
146 |
+
* @method \Aws\Result putBucketCors(array $args = [])
|
147 |
+
* @method \GuzzleHttp\Promise\Promise putBucketCorsAsync(array $args = [])
|
148 |
+
* @method \Aws\Result putBucketEncryption(array $args = [])
|
149 |
+
* @method \GuzzleHttp\Promise\Promise putBucketEncryptionAsync(array $args = [])
|
150 |
+
* @method \Aws\Result putBucketInventoryConfiguration(array $args = [])
|
151 |
+
* @method \GuzzleHttp\Promise\Promise putBucketInventoryConfigurationAsync(array $args = [])
|
152 |
+
* @method \Aws\Result putBucketLifecycle(array $args = [])
|
153 |
+
* @method \GuzzleHttp\Promise\Promise putBucketLifecycleAsync(array $args = [])
|
154 |
+
* @method \Aws\Result putBucketLifecycleConfiguration(array $args = [])
|
155 |
+
* @method \GuzzleHttp\Promise\Promise putBucketLifecycleConfigurationAsync(array $args = [])
|
156 |
+
* @method \Aws\Result putBucketLogging(array $args = [])
|
157 |
+
* @method \GuzzleHttp\Promise\Promise putBucketLoggingAsync(array $args = [])
|
158 |
+
* @method \Aws\Result putBucketMetricsConfiguration(array $args = [])
|
159 |
+
* @method \GuzzleHttp\Promise\Promise putBucketMetricsConfigurationAsync(array $args = [])
|
160 |
+
* @method \Aws\Result putBucketNotification(array $args = [])
|
161 |
+
* @method \GuzzleHttp\Promise\Promise putBucketNotificationAsync(array $args = [])
|
162 |
+
* @method \Aws\Result putBucketNotificationConfiguration(array $args = [])
|
163 |
+
* @method \GuzzleHttp\Promise\Promise putBucketNotificationConfigurationAsync(array $args = [])
|
164 |
+
* @method \Aws\Result putBucketPolicy(array $args = [])
|
165 |
+
* @method \GuzzleHttp\Promise\Promise putBucketPolicyAsync(array $args = [])
|
166 |
+
* @method \Aws\Result putBucketReplication(array $args = [])
|
167 |
+
* @method \GuzzleHttp\Promise\Promise putBucketReplicationAsync(array $args = [])
|
168 |
+
* @method \Aws\Result putBucketRequestPayment(array $args = [])
|
169 |
+
* @method \GuzzleHttp\Promise\Promise putBucketRequestPaymentAsync(array $args = [])
|
170 |
+
* @method \Aws\Result putBucketTagging(array $args = [])
|
171 |
+
* @method \GuzzleHttp\Promise\Promise putBucketTaggingAsync(array $args = [])
|
172 |
+
* @method \Aws\Result putBucketVersioning(array $args = [])
|
173 |
+
* @method \GuzzleHttp\Promise\Promise putBucketVersioningAsync(array $args = [])
|
174 |
+
* @method \Aws\Result putBucketWebsite(array $args = [])
|
175 |
+
* @method \GuzzleHttp\Promise\Promise putBucketWebsiteAsync(array $args = [])
|
176 |
+
* @method \Aws\Result putObject(array $args = [])
|
177 |
+
* @method \GuzzleHttp\Promise\Promise putObjectAsync(array $args = [])
|
178 |
+
* @method \Aws\Result putObjectAcl(array $args = [])
|
179 |
+
* @method \GuzzleHttp\Promise\Promise putObjectAclAsync(array $args = [])
|
180 |
+
* @method \Aws\Result putObjectLegalHold(array $args = [])
|
181 |
+
* @method \GuzzleHttp\Promise\Promise putObjectLegalHoldAsync(array $args = [])
|
182 |
+
* @method \Aws\Result putObjectLockConfiguration(array $args = [])
|
183 |
+
* @method \GuzzleHttp\Promise\Promise putObjectLockConfigurationAsync(array $args = [])
|
184 |
+
* @method \Aws\Result putObjectRetention(array $args = [])
|
185 |
+
* @method \GuzzleHttp\Promise\Promise putObjectRetentionAsync(array $args = [])
|
186 |
+
* @method \Aws\Result putObjectTagging(array $args = [])
|
187 |
+
* @method \GuzzleHttp\Promise\Promise putObjectTaggingAsync(array $args = [])
|
188 |
+
* @method \Aws\Result putPublicAccessBlock(array $args = [])
|
189 |
+
* @method \GuzzleHttp\Promise\Promise putPublicAccessBlockAsync(array $args = [])
|
190 |
+
* @method \Aws\Result restoreObject(array $args = [])
|
191 |
+
* @method \GuzzleHttp\Promise\Promise restoreObjectAsync(array $args = [])
|
192 |
+
* @method \Aws\Result selectObjectContent(array $args = [])
|
193 |
+
* @method \GuzzleHttp\Promise\Promise selectObjectContentAsync(array $args = [])
|
194 |
+
* @method \Aws\Result uploadPart(array $args = [])
|
195 |
+
* @method \GuzzleHttp\Promise\Promise uploadPartAsync(array $args = [])
|
196 |
+
* @method \Aws\Result uploadPartCopy(array $args = [])
|
197 |
+
* @method \GuzzleHttp\Promise\Promise uploadPartCopyAsync(array $args = [])
|
198 |
+
*/
|
199 |
+
class S3Client extends AwsClient implements S3ClientInterface
|
200 |
+
{
|
201 |
+
use S3ClientTrait;
|
202 |
+
|
203 |
+
public static function getArguments()
|
204 |
+
{
|
205 |
+
$args = parent::getArguments();
|
206 |
+
$args['retries']['fn'] = [__CLASS__, '_applyRetryConfig'];
|
207 |
+
$args['api_provider']['fn'] = [__CLASS__, '_applyApiProvider'];
|
208 |
+
|
209 |
+
return $args + [
|
210 |
+
'bucket_endpoint' => [
|
211 |
+
'type' => 'config',
|
212 |
+
'valid' => ['bool'],
|
213 |
+
'doc' => 'Set to true to send requests to a hardcoded '
|
214 |
+
. 'bucket endpoint rather than create an endpoint as a '
|
215 |
+
. 'result of injecting the bucket into the URL. This '
|
216 |
+
. 'option is useful for interacting with CNAME endpoints.',
|
217 |
+
],
|
218 |
+
'use_accelerate_endpoint' => [
|
219 |
+
'type' => 'config',
|
220 |
+
'valid' => ['bool'],
|
221 |
+
'doc' => 'Set to true to send requests to an S3 Accelerate'
|
222 |
+
. ' endpoint by default. Can be enabled or disabled on'
|
223 |
+
. ' individual operations by setting'
|
224 |
+
. ' \'@use_accelerate_endpoint\' to true or false. Note:'
|
225 |
+
. ' you must enable S3 Accelerate on a bucket before it can'
|
226 |
+
. ' be accessed via an Accelerate endpoint.',
|
227 |
+
'default' => false,
|
228 |
+
],
|
229 |
+
'use_dual_stack_endpoint' => [
|
230 |
+
'type' => 'config',
|
231 |
+
'valid' => ['bool'],
|
232 |
+
'doc' => 'Set to true to send requests to an S3 Dual Stack'
|
233 |
+
. ' endpoint by default, which enables IPv6 Protocol.'
|
234 |
+
. ' Can be enabled or disabled on individual operations by setting'
|
235 |
+
. ' \'@use_dual_stack_endpoint\' to true or false.',
|
236 |
+
'default' => false,
|
237 |
+
],
|
238 |
+
'use_path_style_endpoint' => [
|
239 |
+
'type' => 'config',
|
240 |
+
'valid' => ['bool'],
|
241 |
+
'doc' => 'Set to true to send requests to an S3 path style'
|
242 |
+
. ' endpoint by default.'
|
243 |
+
. ' Can be enabled or disabled on individual operations by setting'
|
244 |
+
. ' \'@use_path_style_endpoint\' to true or false.',
|
245 |
+
'default' => false,
|
246 |
+
],
|
247 |
+
];
|
248 |
+
}
|
249 |
+
|
250 |
+
/**
|
251 |
+
* {@inheritdoc}
|
252 |
+
*
|
253 |
+
* In addition to the options available to
|
254 |
+
* {@see Aws\AwsClient::__construct}, S3Client accepts the following
|
255 |
+
* options:
|
256 |
+
*
|
257 |
+
* - bucket_endpoint: (bool) Set to true to send requests to a
|
258 |
+
* hardcoded bucket endpoint rather than create an endpoint as a result
|
259 |
+
* of injecting the bucket into the URL. This option is useful for
|
260 |
+
* interacting with CNAME endpoints.
|
261 |
+
* - calculate_md5: (bool) Set to false to disable calculating an MD5
|
262 |
+
* for all Amazon S3 signed uploads.
|
263 |
+
* - use_accelerate_endpoint: (bool) Set to true to send requests to an S3
|
264 |
+
* Accelerate endpoint by default. Can be enabled or disabled on
|
265 |
+
* individual operations by setting '@use_accelerate_endpoint' to true or
|
266 |
+
* false. Note: you must enable S3 Accelerate on a bucket before it can be
|
267 |
+
* accessed via an Accelerate endpoint.
|
268 |
+
* - use_dual_stack_endpoint: (bool) Set to true to send requests to an S3
|
269 |
+
* Dual Stack endpoint by default, which enables IPv6 Protocol.
|
270 |
+
* Can be enabled or disabled on individual operations by setting
|
271 |
+
* '@use_dual_stack_endpoint\' to true or false. Note:
|
272 |
+
* you cannot use it together with an accelerate endpoint.
|
273 |
+
* - use_path_style_endpoint: (bool) Set to true to send requests to an S3
|
274 |
+
* path style endpoint by default.
|
275 |
+
* Can be enabled or disabled on individual operations by setting
|
276 |
+
* '@use_path_style_endpoint\' to true or false. Note:
|
277 |
+
* you cannot use it together with an accelerate endpoint.
|
278 |
+
*
|
279 |
+
* @param array $args
|
280 |
+
*/
|
281 |
+
public function __construct(array $args)
|
282 |
+
{
|
283 |
+
parent::__construct($args);
|
284 |
+
$stack = $this->getHandlerList();
|
285 |
+
$stack->appendInit(SSECMiddleware::wrap($this->getEndpoint()->getScheme()), 's3.ssec');
|
286 |
+
$stack->appendBuild(ApplyChecksumMiddleware::wrap(), 's3.checksum');
|
287 |
+
$stack->appendBuild(
|
288 |
+
Middleware::contentType(['PutObject', 'UploadPart']),
|
289 |
+
's3.content_type'
|
290 |
+
);
|
291 |
+
|
292 |
+
|
293 |
+
// Use the bucket style middleware when using a "bucket_endpoint" (for cnames)
|
294 |
+
if ($this->getConfig('bucket_endpoint')) {
|
295 |
+
$stack->appendBuild(BucketEndpointMiddleware::wrap(), 's3.bucket_endpoint');
|
296 |
+
} else {
|
297 |
+
$stack->appendBuild(
|
298 |
+
S3EndpointMiddleware::wrap(
|
299 |
+
$this->getRegion(),
|
300 |
+
[
|
301 |
+
'dual_stack' => $this->getConfig('use_dual_stack_endpoint'),
|
302 |
+
'accelerate' => $this->getConfig('use_accelerate_endpoint'),
|
303 |
+
'path_style' => $this->getConfig('use_path_style_endpoint')
|
304 |
+
]
|
305 |
+
),
|
306 |
+
's3.endpoint_middleware'
|
307 |
+
);
|
308 |
+
}
|
309 |
+
|
310 |
+
$stack->appendSign(PutObjectUrlMiddleware::wrap(), 's3.put_object_url');
|
311 |
+
$stack->appendSign(PermanentRedirectMiddleware::wrap(), 's3.permanent_redirect');
|
312 |
+
$stack->appendInit(Middleware::sourceFile($this->getApi()), 's3.source_file');
|
313 |
+
$stack->appendInit($this->getSaveAsParameter(), 's3.save_as');
|
314 |
+
$stack->appendInit($this->getLocationConstraintMiddleware(), 's3.location');
|
315 |
+
$stack->appendInit($this->getEncodingTypeMiddleware(), 's3.auto_encode');
|
316 |
+
$stack->appendInit($this->getHeadObjectMiddleware(), 's3.head_object');
|
317 |
+
}
|
318 |
+
|
319 |
+
/**
|
320 |
+
* Determine if a string is a valid name for a DNS compatible Amazon S3
|
321 |
+
* bucket.
|
322 |
+
*
|
323 |
+
* DNS compatible bucket names can be used as a subdomain in a URL (e.g.,
|
324 |
+
* "<bucket>.s3.amazonaws.com").
|
325 |
+
*
|
326 |
+
* @param string $bucket Bucket name to check.
|
327 |
+
*
|
328 |
+
* @return bool
|
329 |
+
*/
|
330 |
+
public static function isBucketDnsCompatible($bucket)
|
331 |
+
{
|
332 |
+
$bucketLen = strlen($bucket);
|
333 |
+
|
334 |
+
return ($bucketLen >= 3 && $bucketLen <= 63) &&
|
335 |
+
// Cannot look like an IP address
|
336 |
+
!filter_var($bucket, FILTER_VALIDATE_IP) &&
|
337 |
+
preg_match('/^[a-z0-9]([a-z0-9\-\.]*[a-z0-9])?$/', $bucket);
|
338 |
+
}
|
339 |
+
|
340 |
+
public function createPresignedRequest(CommandInterface $command, $expires)
|
341 |
+
{
|
342 |
+
$command = clone $command;
|
343 |
+
$command->getHandlerList()->remove('signer');
|
344 |
+
|
345 |
+
/** @var \Aws\Signature\SignatureInterface $signer */
|
346 |
+
$signer = call_user_func(
|
347 |
+
$this->getSignatureProvider(),
|
348 |
+
$this->getConfig('signature_version'),
|
349 |
+
$this->getConfig('signing_name'),
|
350 |
+
$this->getConfig('signing_region')
|
351 |
+
);
|
352 |
+
|
353 |
+
return $signer->presign(
|
354 |
+
\Aws\serialize($command),
|
355 |
+
$this->getCredentials()->wait(),
|
356 |
+
$expires
|
357 |
+
);
|
358 |
+
}
|
359 |
+
|
360 |
+
public function getObjectUrl($bucket, $key)
|
361 |
+
{
|
362 |
+
$command = $this->getCommand('GetObject', [
|
363 |
+
'Bucket' => $bucket,
|
364 |
+
'Key' => $key
|
365 |
+
]);
|
366 |
+
|
367 |
+
return (string) \Aws\serialize($command)->getUri();
|
368 |
+
}
|
369 |
+
|
370 |
+
/**
|
371 |
+
* Raw URL encode a key and allow for '/' characters
|
372 |
+
*
|
373 |
+
* @param string $key Key to encode
|
374 |
+
*
|
375 |
+
* @return string Returns the encoded key
|
376 |
+
*/
|
377 |
+
public static function encodeKey($key)
|
378 |
+
{
|
379 |
+
return str_replace('%2F', '/', rawurlencode($key));
|
380 |
+
}
|
381 |
+
|
382 |
+
/**
|
383 |
+
* Provides a middleware that removes the need to specify LocationConstraint on CreateBucket.
|
384 |
+
*
|
385 |
+
* @return \Closure
|
386 |
+
*/
|
387 |
+
private function getLocationConstraintMiddleware()
|
388 |
+
{
|
389 |
+
$region = $this->getRegion();
|
390 |
+
return static function (callable $handler) use ($region) {
|
391 |
+
return function (Command $command, $request = null) use ($handler, $region) {
|
392 |
+
if ($command->getName() === 'CreateBucket') {
|
393 |
+
$locationConstraint = isset($command['CreateBucketConfiguration']['LocationConstraint'])
|
394 |
+
? $command['CreateBucketConfiguration']['LocationConstraint']
|
395 |
+
: null;
|
396 |
+
|
397 |
+
if ($locationConstraint === 'us-east-1') {
|
398 |
+
unset($command['CreateBucketConfiguration']);
|
399 |
+
} elseif ('us-east-1' !== $region && empty($locationConstraint)) {
|
400 |
+
$command['CreateBucketConfiguration'] = ['LocationConstraint' => $region];
|
401 |
+
}
|
402 |
+
}
|
403 |
+
|
404 |
+
return $handler($command, $request);
|
405 |
+
};
|
406 |
+
};
|
407 |
+
}
|
408 |
+
|
409 |
+
/**
|
410 |
+
* Provides a middleware that supports the `SaveAs` parameter.
|
411 |
+
*
|
412 |
+
* @return \Closure
|
413 |
+
*/
|
414 |
+
private function getSaveAsParameter()
|
415 |
+
{
|
416 |
+
return static function (callable $handler) {
|
417 |
+
return function (Command $command, $request = null) use ($handler) {
|
418 |
+
if ($command->getName() === 'GetObject' && isset($command['SaveAs'])) {
|
419 |
+
$command['@http']['sink'] = $command['SaveAs'];
|
420 |
+
unset($command['SaveAs']);
|
421 |
+
}
|
422 |
+
|
423 |
+
return $handler($command, $request);
|
424 |
+
};
|
425 |
+
};
|
426 |
+
}
|
427 |
+
|
428 |
+
/**
|
429 |
+
* Provides a middleware that disables content decoding on HeadObject
|
430 |
+
* commands.
|
431 |
+
*
|
432 |
+
* @return \Closure
|
433 |
+
*/
|
434 |
+
private function getHeadObjectMiddleware()
|
435 |
+
{
|
436 |
+
return static function (callable $handler) {
|
437 |
+
return function (
|
438 |
+
CommandInterface $command,
|
439 |
+
RequestInterface $request = null
|
440 |
+
) use ($handler) {
|
441 |
+
if ($command->getName() === 'HeadObject'
|
442 |
+
&& !isset($command['@http']['decode_content'])
|
443 |
+
) {
|
444 |
+
$command['@http']['decode_content'] = false;
|
445 |
+
}
|
446 |
+
|
447 |
+
return $handler($command, $request);
|
448 |
+
};
|
449 |
+
};
|
450 |
+
}
|
451 |
+
|
452 |
+
/**
|
453 |
+
* Provides a middleware that autopopulates the EncodingType parameter on
|
454 |
+
* ListObjects commands.
|
455 |
+
*
|
456 |
+
* @return \Closure
|
457 |
+
*/
|
458 |
+
private function getEncodingTypeMiddleware()
|
459 |
+
{
|
460 |
+
return static function (callable $handler) {
|
461 |
+
return function (Command $command, $request = null) use ($handler) {
|
462 |
+
$autoSet = false;
|
463 |
+
if ($command->getName() === 'ListObjects'
|
464 |
+
&& empty($command['EncodingType'])
|
465 |
+
) {
|
466 |
+
$command['EncodingType'] = 'url';
|
467 |
+
$autoSet = true;
|
468 |
+
}
|
469 |
+
|
470 |
+
return $handler($command, $request)
|
471 |
+
->then(function (ResultInterface $result) use ($autoSet) {
|
472 |
+
if ($result['EncodingType'] === 'url' && $autoSet) {
|
473 |
+
static $topLevel = [
|
474 |
+
'Delimiter',
|
475 |
+
'Marker',
|
476 |
+
'NextMarker',
|
477 |
+
'Prefix',
|
478 |
+
];
|
479 |
+
static $nested = [
|
480 |
+
['Contents', 'Key'],
|
481 |
+
['CommonPrefixes', 'Prefix'],
|
482 |
+
];
|
483 |
+
|
484 |
+
foreach ($topLevel as $key) {
|
485 |
+
if (isset($result[$key])) {
|
486 |
+
$result[$key] = urldecode($result[$key]);
|
487 |
+
}
|
488 |
+
}
|
489 |
+
foreach ($nested as $steps) {
|
490 |
+
if (isset($result[$steps[0]])) {
|
491 |
+
foreach ($result[$steps[0]] as $key => $part) {
|
492 |
+
if (isset($part[$steps[1]])) {
|
493 |
+
$result[$steps[0]][$key][$steps[1]]
|
494 |
+
= urldecode($part[$steps[1]]);
|
495 |
+
}
|
496 |
+
}
|
497 |
+
}
|
498 |
+
}
|
499 |
+
|
500 |
+
}
|
501 |
+
|
502 |
+
return $result;
|
503 |
+
});
|
504 |
+
};
|
505 |
+
};
|
506 |
+
}
|
507 |
+
|
508 |
+
/** @internal */
|
509 |
+
public static function _applyRetryConfig($value, $_, HandlerList $list)
|
510 |
+
{
|
511 |
+
if (!$value) {
|
512 |
+
return;
|
513 |
+
}
|
514 |
+
|
515 |
+
$decider = RetryMiddleware::createDefaultDecider($value);
|
516 |
+
$decider = function ($retries, $command, $request, $result, $error) use ($decider, $value) {
|
517 |
+
$maxRetries = null !== $command['@retries']
|
518 |
+
? $command['@retries']
|
519 |
+
: $value;
|
520 |
+
|
521 |
+
if ($decider($retries, $command, $request, $result, $error)) {
|
522 |
+
return true;
|
523 |
+
}
|
524 |
+
|
525 |
+
if ($error instanceof AwsException
|
526 |
+
&& $retries < $maxRetries
|
527 |
+
) {
|
528 |
+
if ($error->getResponse()
|
529 |
+
&& $error->getResponse()->getStatusCode() >= 400
|
530 |
+
) {
|
531 |
+
return strpos(
|
532 |
+
$error->getResponse()->getBody(),
|
533 |
+
'Your socket connection to the server'
|
534 |
+
) !== false;
|
535 |
+
}
|
536 |
+
|
537 |
+
if ($error->getPrevious() instanceof RequestException) {
|
538 |
+
// All commands except CompleteMultipartUpload are
|
539 |
+
// idempotent and may be retried without worry if a
|
540 |
+
// networking error has occurred.
|
541 |
+
return $command->getName() !== 'CompleteMultipartUpload';
|
542 |
+
}
|
543 |
+
}
|
544 |
+
|
545 |
+
return false;
|
546 |
+
};
|
547 |
+
|
548 |
+
$delay = [RetryMiddleware::class, 'exponentialDelay'];
|
549 |
+
$list->appendSign(Middleware::retry($decider, $delay), 'retry');
|
550 |
+
}
|
551 |
+
|
552 |
+
/** @internal */
|
553 |
+
public static function _applyApiProvider($value, array &$args, HandlerList $list)
|
554 |
+
{
|
555 |
+
ClientResolver::_apply_api_provider($value, $args, $list);
|
556 |
+
$args['parser'] = new GetBucketLocationParser(
|
557 |
+
new AmbiguousSuccessParser(
|
558 |
+
new RetryableMalformedResponseParser(
|
559 |
+
$args['parser'],
|
560 |
+
$args['exception_class']
|
561 |
+
),
|
562 |
+
$args['error_parser'],
|
563 |
+
$args['exception_class']
|
564 |
+
)
|
565 |
+
);
|
566 |
+
}
|
567 |
+
|
568 |
+
/**
|
569 |
+
* @internal
|
570 |
+
* @codeCoverageIgnore
|
571 |
+
*/
|
572 |
+
public static function applyDocFilters(array $api, array $docs)
|
573 |
+
{
|
574 |
+
$b64 = '<div class="alert alert-info">This value will be base64 encoded on your behalf.</div>';
|
575 |
+
$opt = '<div class="alert alert-info">This value will be computed for you it is not supplied.</div>';
|
576 |
+
|
577 |
+
// Add the SourceFile parameter.
|
578 |
+
$docs['shapes']['SourceFile']['base'] = 'The path to a file on disk to use instead of the Body parameter.';
|
579 |
+
$api['shapes']['SourceFile'] = ['type' => 'string'];
|
580 |
+
$api['shapes']['PutObjectRequest']['members']['SourceFile'] = ['shape' => 'SourceFile'];
|
581 |
+
$api['shapes']['UploadPartRequest']['members']['SourceFile'] = ['shape' => 'SourceFile'];
|
582 |
+
|
583 |
+
// Add the ContentSHA256 parameter.
|
584 |
+
$docs['shapes']['ContentSHA256']['base'] = 'A SHA256 hash of the body content of the request.';
|
585 |
+
$api['shapes']['ContentSHA256'] = ['type' => 'string'];
|
586 |
+
$api['shapes']['PutObjectRequest']['members']['ContentSHA256'] = ['shape' => 'ContentSHA256'];
|
587 |
+
$api['shapes']['UploadPartRequest']['members']['ContentSHA256'] = ['shape' => 'ContentSHA256'];
|
588 |
+
unset($api['shapes']['PutObjectRequest']['members']['ContentMD5']);
|
589 |
+
unset($api['shapes']['UploadPartRequest']['members']['ContentMD5']);
|
590 |
+
$docs['shapes']['ContentSHA256']['append'] = $opt;
|
591 |
+
|
592 |
+
// Add the SaveAs parameter.
|
593 |
+
$docs['shapes']['SaveAs']['base'] = 'The path to a file on disk to save the object data.';
|
594 |
+
$api['shapes']['SaveAs'] = ['type' => 'string'];
|
595 |
+
$api['shapes']['GetObjectRequest']['members']['SaveAs'] = ['shape' => 'SaveAs'];
|
596 |
+
|
597 |
+
// Several SSECustomerKey documentation updates.
|
598 |
+
$docs['shapes']['SSECustomerKey']['append'] = $b64;
|
599 |
+
$docs['shapes']['CopySourceSSECustomerKey']['append'] = $b64;
|
600 |
+
$docs['shapes']['SSECustomerKeyMd5']['append'] = $opt;
|
601 |
+
|
602 |
+
// Add the ObjectURL to various output shapes and documentation.
|
603 |
+
$docs['shapes']['ObjectURL']['base'] = 'The URI of the created object.';
|
604 |
+
$api['shapes']['ObjectURL'] = ['type' => 'string'];
|
605 |
+
$api['shapes']['PutObjectOutput']['members']['ObjectURL'] = ['shape' => 'ObjectURL'];
|
606 |
+
$api['shapes']['CopyObjectOutput']['members']['ObjectURL'] = ['shape' => 'ObjectURL'];
|
607 |
+
$api['shapes']['CompleteMultipartUploadOutput']['members']['ObjectURL'] = ['shape' => 'ObjectURL'];
|
608 |
+
|
609 |
+
// Fix references to Location Constraint.
|
610 |
+
unset($api['shapes']['CreateBucketRequest']['payload']);
|
611 |
+
$api['shapes']['BucketLocationConstraint']['enum'] = [
|
612 |
+
"ap-northeast-1",
|
613 |
+
"ap-southeast-2",
|
614 |
+
"ap-southeast-1",
|
615 |
+
"cn-north-1",
|
616 |
+
"eu-central-1",
|
617 |
+
"eu-west-1",
|
618 |
+
"us-east-1",
|
619 |
+
"us-west-1",
|
620 |
+
"us-west-2",
|
621 |
+
"sa-east-1",
|
622 |
+
];
|
623 |
+
|
624 |
+
// Add a note that the ContentMD5 is optional.
|
625 |
+
$docs['shapes']['ContentMD5']['append'] = '<div class="alert alert-info">The value will be computed on '
|
626 |
+
. 'your behalf.</div>';
|
627 |
+
|
628 |
+
return [
|
629 |
+
new Service($api, ApiProvider::defaultProvider()),
|
630 |
+
new DocModel($docs)
|
631 |
+
];
|
632 |
+
}
|
633 |
+
}
|
lib/Aws/Aws/S3/S3ClientInterface.php
ADDED
@@ -0,0 +1,322 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\S3;
|
3 |
+
|
4 |
+
use Aws\AwsClientInterface;
|
5 |
+
use Aws\CommandInterface;
|
6 |
+
use Aws\ResultInterface;
|
7 |
+
use GuzzleHttp\Promise\PromiseInterface;
|
8 |
+
use Psr\Http\Message\RequestInterface;
|
9 |
+
|
10 |
+
interface S3ClientInterface extends AwsClientInterface
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Create a pre-signed URL for the given S3 command object.
|
14 |
+
*
|
15 |
+
* @param CommandInterface $command Command to create a pre-signed
|
16 |
+
* URL for.
|
17 |
+
* @param int|string|\DateTime $expires The time at which the URL should
|
18 |
+
* expire. This can be a Unix
|
19 |
+
* timestamp, a PHP DateTime object,
|
20 |
+
* or a string that can be evaluated
|
21 |
+
* by strtotime().
|
22 |
+
*
|
23 |
+
* @return RequestInterface
|
24 |
+
*/
|
25 |
+
public function createPresignedRequest(CommandInterface $command, $expires);
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Returns the URL to an object identified by its bucket and key.
|
29 |
+
*
|
30 |
+
* The URL returned by this method is not signed nor does it ensure the the
|
31 |
+
* bucket and key given to the method exist. If you need a signed URL, then
|
32 |
+
* use the {@see \Aws\S3\S3Client::createPresignedRequest} method and get
|
33 |
+
* the URI of the signed request.
|
34 |
+
*
|
35 |
+
* @param string $bucket The name of the bucket where the object is located
|
36 |
+
* @param string $key The key of the object
|
37 |
+
*
|
38 |
+
* @return string The URL to the object
|
39 |
+
*/
|
40 |
+
public function getObjectUrl($bucket, $key);
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Determines whether or not a bucket exists by name.
|
44 |
+
*
|
45 |
+
* @param string $bucket The name of the bucket
|
46 |
+
*
|
47 |
+
* @return bool
|
48 |
+
*/
|
49 |
+
public function doesBucketExist($bucket);
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Determines whether or not an object exists by name.
|
53 |
+
*
|
54 |
+
* @param string $bucket The name of the bucket
|
55 |
+
* @param string $key The key of the object
|
56 |
+
* @param array $options Additional options available in the HeadObject
|
57 |
+
* operation (e.g., VersionId).
|
58 |
+
*
|
59 |
+
* @return bool
|
60 |
+
*/
|
61 |
+
public function doesObjectExist($bucket, $key, array $options = []);
|
62 |
+
|
63 |
+
/**
|
64 |
+
* Register the Amazon S3 stream wrapper with this client instance.
|
65 |
+
*/
|
66 |
+
public function registerStreamWrapper();
|
67 |
+
|
68 |
+
/**
|
69 |
+
* Deletes objects from Amazon S3 that match the result of a ListObjects
|
70 |
+
* operation. For example, this allows you to do things like delete all
|
71 |
+
* objects that match a specific key prefix.
|
72 |
+
*
|
73 |
+
* @param string $bucket Bucket that contains the object keys
|
74 |
+
* @param string $prefix Optionally delete only objects under this key prefix
|
75 |
+
* @param string $regex Delete only objects that match this regex
|
76 |
+
* @param array $options Aws\S3\BatchDelete options array.
|
77 |
+
*
|
78 |
+
* @see Aws\S3\S3Client::listObjects
|
79 |
+
* @throws \RuntimeException if no prefix and no regex is given
|
80 |
+
*/
|
81 |
+
public function deleteMatchingObjects(
|
82 |
+
$bucket,
|
83 |
+
$prefix = '',
|
84 |
+
$regex = '',
|
85 |
+
array $options = []
|
86 |
+
);
|
87 |
+
|
88 |
+
/**
|
89 |
+
* Deletes objects from Amazon S3 that match the result of a ListObjects
|
90 |
+
* operation. For example, this allows you to do things like delete all
|
91 |
+
* objects that match a specific key prefix.
|
92 |
+
*
|
93 |
+
* @param string $bucket Bucket that contains the object keys
|
94 |
+
* @param string $prefix Optionally delete only objects under this key prefix
|
95 |
+
* @param string $regex Delete only objects that match this regex
|
96 |
+
* @param array $options Aws\S3\BatchDelete options array.
|
97 |
+
*
|
98 |
+
* @see Aws\S3\S3Client::listObjects
|
99 |
+
*
|
100 |
+
* @return PromiseInterface A promise that is settled when matching
|
101 |
+
* objects are deleted.
|
102 |
+
*/
|
103 |
+
public function deleteMatchingObjectsAsync(
|
104 |
+
$bucket,
|
105 |
+
$prefix = '',
|
106 |
+
$regex = '',
|
107 |
+
array $options = []
|
108 |
+
);
|
109 |
+
|
110 |
+
/**
|
111 |
+
* Upload a file, stream, or string to a bucket.
|
112 |
+
*
|
113 |
+
* If the upload size exceeds the specified threshold, the upload will be
|
114 |
+
* performed using concurrent multipart uploads.
|
115 |
+
*
|
116 |
+
* The options array accepts the following options:
|
117 |
+
*
|
118 |
+
* - before_upload: (callable) Callback to invoke before any upload
|
119 |
+
* operations during the upload process. The callback should have a
|
120 |
+
* function signature like `function (Aws\Command $command) {...}`.
|
121 |
+
* - concurrency: (int, default=int(3)) Maximum number of concurrent
|
122 |
+
* `UploadPart` operations allowed during a multipart upload.
|
123 |
+
* - mup_threshold: (int, default=int(16777216)) The size, in bytes, allowed
|
124 |
+
* before the upload must be sent via a multipart upload. Default: 16 MB.
|
125 |
+
* - params: (array, default=array([])) Custom parameters to use with the
|
126 |
+
* upload. For single uploads, they must correspond to those used for the
|
127 |
+
* `PutObject` operation. For multipart uploads, they correspond to the
|
128 |
+
* parameters of the `CreateMultipartUpload` operation.
|
129 |
+
* - part_size: (int) Part size to use when doing a multipart upload.
|
130 |
+
*
|
131 |
+
* @param string $bucket Bucket to upload the object.
|
132 |
+
* @param string $key Key of the object.
|
133 |
+
* @param mixed $body Object data to upload. Can be a
|
134 |
+
* StreamInterface, PHP stream resource, or a
|
135 |
+
* string of data to upload.
|
136 |
+
* @param string $acl ACL to apply to the object (default: private).
|
137 |
+
* @param array $options Options used to configure the upload process.
|
138 |
+
*
|
139 |
+
* @see Aws\S3\MultipartUploader for more info about multipart uploads.
|
140 |
+
* @return ResultInterface Returns the result of the upload.
|
141 |
+
*/
|
142 |
+
public function upload(
|
143 |
+
$bucket,
|
144 |
+
$key,
|
145 |
+
$body,
|
146 |
+
$acl = 'private',
|
147 |
+
array $options = []
|
148 |
+
);
|
149 |
+
|
150 |
+
/**
|
151 |
+
* Upload a file, stream, or string to a bucket asynchronously.
|
152 |
+
*
|
153 |
+
* @param string $bucket Bucket to upload the object.
|
154 |
+
* @param string $key Key of the object.
|
155 |
+
* @param mixed $body Object data to upload. Can be a
|
156 |
+
* StreamInterface, PHP stream resource, or a
|
157 |
+
* string of data to upload.
|
158 |
+
* @param string $acl ACL to apply to the object (default: private).
|
159 |
+
* @param array $options Options used to configure the upload process.
|
160 |
+
*
|
161 |
+
* @see self::upload
|
162 |
+
* @return PromiseInterface Returns a promise that will be fulfilled
|
163 |
+
* with the result of the upload.
|
164 |
+
*/
|
165 |
+
public function uploadAsync(
|
166 |
+
$bucket,
|
167 |
+
$key,
|
168 |
+
$body,
|
169 |
+
$acl = 'private',
|
170 |
+
array $options = []
|
171 |
+
);
|
172 |
+
|
173 |
+
/**
|
174 |
+
* Copy an object of any size to a different location.
|
175 |
+
*
|
176 |
+
* If the upload size exceeds the maximum allowable size for direct S3
|
177 |
+
* copying, a multipart copy will be used.
|
178 |
+
*
|
179 |
+
* The options array accepts the following options:
|
180 |
+
*
|
181 |
+
* - before_upload: (callable) Callback to invoke before any upload
|
182 |
+
* operations during the upload process. The callback should have a
|
183 |
+
* function signature like `function (Aws\Command $command) {...}`.
|
184 |
+
* - concurrency: (int, default=int(5)) Maximum number of concurrent
|
185 |
+
* `UploadPart` operations allowed during a multipart upload.
|
186 |
+
* - params: (array, default=array([])) Custom parameters to use with the
|
187 |
+
* upload. For single uploads, they must correspond to those used for the
|
188 |
+
* `CopyObject` operation. For multipart uploads, they correspond to the
|
189 |
+
* parameters of the `CreateMultipartUpload` operation.
|
190 |
+
* - part_size: (int) Part size to use when doing a multipart upload.
|
191 |
+
*
|
192 |
+
* @param string $fromBucket Bucket where the copy source resides.
|
193 |
+
* @param string $fromKey Key of the copy source.
|
194 |
+
* @param string $destBucket Bucket to which to copy the object.
|
195 |
+
* @param string $destKey Key to which to copy the object.
|
196 |
+
* @param string $acl ACL to apply to the copy (default: private).
|
197 |
+
* @param array $options Options used to configure the upload process.
|
198 |
+
*
|
199 |
+
* @see Aws\S3\MultipartCopy for more info about multipart uploads.
|
200 |
+
* @return ResultInterface Returns the result of the copy.
|
201 |
+
*/
|
202 |
+
public function copy(
|
203 |
+
$fromBucket,
|
204 |
+
$fromKey,
|
205 |
+
$destBucket,
|
206 |
+
$destKey,
|
207 |
+
$acl = 'private',
|
208 |
+
array $options = []
|
209 |
+
);
|
210 |
+
|
211 |
+
/**
|
212 |
+
* Copy an object of any size to a different location asynchronously.
|
213 |
+
*
|
214 |
+
* @param string $fromBucket Bucket where the copy source resides.
|
215 |
+
* @param string $fromKey Key of the copy source.
|
216 |
+
* @param string $destBucket Bucket to which to copy the object.
|
217 |
+
* @param string $destKey Key to which to copy the object.
|
218 |
+
* @param string $acl ACL to apply to the copy (default: private).
|
219 |
+
* @param array $options Options used to configure the upload process.
|
220 |
+
*
|
221 |
+
* @see self::copy for more info about the parameters above.
|
222 |
+
* @return PromiseInterface Returns a promise that will be fulfilled
|
223 |
+
* with the result of the copy.
|
224 |
+
*/
|
225 |
+
public function copyAsync(
|
226 |
+
$fromBucket,
|
227 |
+
$fromKey,
|
228 |
+
$destBucket,
|
229 |
+
$destKey,
|
230 |
+
$acl = 'private',
|
231 |
+
array $options = []
|
232 |
+
);
|
233 |
+
|
234 |
+
/**
|
235 |
+
* Recursively uploads all files in a given directory to a given bucket.
|
236 |
+
*
|
237 |
+
* @param string $directory Full path to a directory to upload
|
238 |
+
* @param string $bucket Name of the bucket
|
239 |
+
* @param string $keyPrefix Virtual directory key prefix to add to each upload
|
240 |
+
* @param array $options Options available in Aws\S3\Transfer::__construct
|
241 |
+
*
|
242 |
+
* @see Aws\S3\Transfer for more options and customization
|
243 |
+
*/
|
244 |
+
public function uploadDirectory(
|
245 |
+
$directory,
|
246 |
+
$bucket,
|
247 |
+
$keyPrefix = null,
|
248 |
+
array $options = []
|
249 |
+
);
|
250 |
+
|
251 |
+
/**
|
252 |
+
* Recursively uploads all files in a given directory to a given bucket.
|
253 |
+
*
|
254 |
+
* @param string $directory Full path to a directory to upload
|
255 |
+
* @param string $bucket Name of the bucket
|
256 |
+
* @param string $keyPrefix Virtual directory key prefix to add to each upload
|
257 |
+
* @param array $options Options available in Aws\S3\Transfer::__construct
|
258 |
+
*
|
259 |
+
* @see Aws\S3\Transfer for more options and customization
|
260 |
+
*
|
261 |
+
* @return PromiseInterface A promise that is settled when the upload is
|
262 |
+
* complete.
|
263 |
+
*/
|
264 |
+
public function uploadDirectoryAsync(
|
265 |
+
$directory,
|
266 |
+
$bucket,
|
267 |
+
$keyPrefix = null,
|
268 |
+
array $options = []
|
269 |
+
);
|
270 |
+
|
271 |
+
/**
|
272 |
+
* Downloads a bucket to the local filesystem
|
273 |
+
*
|
274 |
+
* @param string $directory Directory to download to
|
275 |
+
* @param string $bucket Bucket to download from
|
276 |
+
* @param string $keyPrefix Only download objects that use this key prefix
|
277 |
+
* @param array $options Options available in Aws\S3\Transfer::__construct
|
278 |
+
*/
|
279 |
+
public function downloadBucket(
|
280 |
+
$directory,
|
281 |
+
$bucket,
|
282 |
+
$keyPrefix = '',
|
283 |
+
array $options = []
|
284 |
+
);
|
285 |
+
|
286 |
+
/**
|
287 |
+
* Downloads a bucket to the local filesystem
|
288 |
+
*
|
289 |
+
* @param string $directory Directory to download to
|
290 |
+
* @param string $bucket Bucket to download from
|
291 |
+
* @param string $keyPrefix Only download objects that use this key prefix
|
292 |
+
* @param array $options Options available in Aws\S3\Transfer::__construct
|
293 |
+
*
|
294 |
+
* @return PromiseInterface A promise that is settled when the download is
|
295 |
+
* complete.
|
296 |
+
*/
|
297 |
+
public function downloadBucketAsync(
|
298 |
+
$directory,
|
299 |
+
$bucket,
|
300 |
+
$keyPrefix = '',
|
301 |
+
array $options = []
|
302 |
+
);
|
303 |
+
|
304 |
+
/**
|
305 |
+
* Returns the region in which a given bucket is located.
|
306 |
+
*
|
307 |
+
* @param string $bucketName
|
308 |
+
*
|
309 |
+
* @return string
|
310 |
+
*/
|
311 |
+
public function determineBucketRegion($bucketName);
|
312 |
+
|
313 |
+
/**
|
314 |
+
* Returns a promise fulfilled with the region in which a given bucket is
|
315 |
+
* located.
|
316 |
+
*
|
317 |
+
* @param string $bucketName
|
318 |
+
*
|
319 |
+
* @return PromiseInterface
|
320 |
+
*/
|
321 |
+
public function determineBucketRegionAsync($bucketName);
|
322 |
+
}
|
lib/Aws/Aws/S3/S3ClientTrait.php
ADDED
@@ -0,0 +1,323 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\S3;
|
3 |
+
|
4 |
+
use Aws\Api\Parser\PayloadParserTrait;
|
5 |
+
use Aws\CommandInterface;
|
6 |
+
use Aws\Exception\AwsException;
|
7 |
+
use Aws\HandlerList;
|
8 |
+
use Aws\ResultInterface;
|
9 |
+
use Aws\S3\Exception\S3Exception;
|
10 |
+
use GuzzleHttp\Promise\PromiseInterface;
|
11 |
+
use GuzzleHttp\Promise\RejectedPromise;
|
12 |
+
use Psr\Http\Message\ResponseInterface;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* A trait providing S3-specific functionality. This is meant to be used in
|
16 |
+
* classes implementing \Aws\S3\S3ClientInterface
|
17 |
+
*/
|
18 |
+
trait S3ClientTrait
|
19 |
+
{
|
20 |
+
use PayloadParserTrait;
|
21 |
+
|
22 |
+
/**
|
23 |
+
* @see S3ClientInterface::upload()
|
24 |
+
*/
|
25 |
+
public function upload(
|
26 |
+
$bucket,
|
27 |
+
$key,
|
28 |
+
$body,
|
29 |
+
$acl = 'private',
|
30 |
+
array $options = []
|
31 |
+
) {
|
32 |
+
return $this
|
33 |
+
->uploadAsync($bucket, $key, $body, $acl, $options)
|
34 |
+
->wait();
|
35 |
+
}
|
36 |
+
|
37 |
+
/**
|
38 |
+
* @see S3ClientInterface::uploadAsync()
|
39 |
+
*/
|
40 |
+
public function uploadAsync(
|
41 |
+
$bucket,
|
42 |
+
$key,
|
43 |
+
$body,
|
44 |
+
$acl = 'private',
|
45 |
+
array $options = []
|
46 |
+
) {
|
47 |
+
return (new ObjectUploader($this, $bucket, $key, $body, $acl, $options))
|
48 |
+
->promise();
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
* @see S3ClientInterface::copy()
|
53 |
+
*/
|
54 |
+
public function copy(
|
55 |
+
$fromB,
|
56 |
+
$fromK,
|
57 |
+
$destB,
|
58 |
+
$destK,
|
59 |
+
$acl = 'private',
|
60 |
+
array $opts = []
|
61 |
+
) {
|
62 |
+
return $this->copyAsync($fromB, $fromK, $destB, $destK, $acl, $opts)
|
63 |
+
->wait();
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* @see S3ClientInterface::copyAsync()
|
68 |
+
*/
|
69 |
+
public function copyAsync(
|
70 |
+
$fromB,
|
71 |
+
$fromK,
|
72 |
+
$destB,
|
73 |
+
$destK,
|
74 |
+
$acl = 'private',
|
75 |
+
array $opts = []
|
76 |
+
) {
|
77 |
+
$source = [
|
78 |
+
'Bucket' => $fromB,
|
79 |
+
'Key' => $fromK,
|
80 |
+
];
|
81 |
+
if (isset($opts['version_id'])) {
|
82 |
+
$source['VersionId'] = $opts['version_id'];
|
83 |
+
}
|
84 |
+
$destination = [
|
85 |
+
'Bucket' => $destB,
|
86 |
+
'Key' => $destK
|
87 |
+
];
|
88 |
+
|
89 |
+
return (new ObjectCopier($this, $source, $destination, $acl, $opts))
|
90 |
+
->promise();
|
91 |
+
}
|
92 |
+
|
93 |
+
/**
|
94 |
+
* @see S3ClientInterface::registerStreamWrapper()
|
95 |
+
*/
|
96 |
+
public function registerStreamWrapper()
|
97 |
+
{
|
98 |
+
StreamWrapper::register($this);
|
99 |
+
}
|
100 |
+
|
101 |
+
/**
|
102 |
+
* @see S3ClientInterface::deleteMatchingObjects()
|
103 |
+
*/
|
104 |
+
public function deleteMatchingObjects(
|
105 |
+
$bucket,
|
106 |
+
$prefix = '',
|
107 |
+
$regex = '',
|
108 |
+
array $options = []
|
109 |
+
) {
|
110 |
+
$this->deleteMatchingObjectsAsync($bucket, $prefix, $regex, $options)
|
111 |
+
->wait();
|
112 |
+
}
|
113 |
+
|
114 |
+
/**
|
115 |
+
* @see S3ClientInterface::deleteMatchingObjectsAsync()
|
116 |
+
*/
|
117 |
+
public function deleteMatchingObjectsAsync(
|
118 |
+
$bucket,
|
119 |
+
$prefix = '',
|
120 |
+
$regex = '',
|
121 |
+
array $options = []
|
122 |
+
) {
|
123 |
+
if (!$prefix && !$regex) {
|
124 |
+
return new RejectedPromise(
|
125 |
+
new \RuntimeException('A prefix or regex is required.')
|
126 |
+
);
|
127 |
+
}
|
128 |
+
|
129 |
+
$params = ['Bucket' => $bucket, 'Prefix' => $prefix];
|
130 |
+
$iter = $this->getIterator('ListObjects', $params);
|
131 |
+
|
132 |
+
if ($regex) {
|
133 |
+
$iter = \Aws\filter($iter, function ($c) use ($regex) {
|
134 |
+
return preg_match($regex, $c['Key']);
|
135 |
+
});
|
136 |
+
}
|
137 |
+
|
138 |
+
return BatchDelete::fromIterator($this, $bucket, $iter, $options)
|
139 |
+
->promise();
|
140 |
+
}
|
141 |
+
|
142 |
+
/**
|
143 |
+
* @see S3ClientInterface::uploadDirectory()
|
144 |
+
*/
|
145 |
+
public function uploadDirectory(
|
146 |
+
$directory,
|
147 |
+
$bucket,
|
148 |
+
$keyPrefix = null,
|
149 |
+
array $options = []
|
150 |
+
) {
|
151 |
+
$this->uploadDirectoryAsync($directory, $bucket, $keyPrefix, $options)
|
152 |
+
->wait();
|
153 |
+
}
|
154 |
+
|
155 |
+
/**
|
156 |
+
* @see S3ClientInterface::uploadDirectoryAsync()
|
157 |
+
*/
|
158 |
+
public function uploadDirectoryAsync(
|
159 |
+
$directory,
|
160 |
+
$bucket,
|
161 |
+
$keyPrefix = null,
|
162 |
+
array $options = []
|
163 |
+
) {
|
164 |
+
$d = "s3://$bucket" . ($keyPrefix ? '/' . ltrim($keyPrefix, '/') : '');
|
165 |
+
return (new Transfer($this, $directory, $d, $options))->promise();
|
166 |
+
}
|
167 |
+
|
168 |
+
/**
|
169 |
+
* @see S3ClientInterface::downloadBucket()
|
170 |
+
*/
|
171 |
+
public function downloadBucket(
|
172 |
+
$directory,
|
173 |
+
$bucket,
|
174 |
+
$keyPrefix = '',
|
175 |
+
array $options = []
|
176 |
+
) {
|
177 |
+
$this->downloadBucketAsync($directory, $bucket, $keyPrefix, $options)
|
178 |
+
->wait();
|
179 |
+
}
|
180 |
+
|
181 |
+
/**
|
182 |
+
* @see S3ClientInterface::downloadBucketAsync()
|
183 |
+
*/
|
184 |
+
public function downloadBucketAsync(
|
185 |
+
$directory,
|
186 |
+
$bucket,
|
187 |
+
$keyPrefix = '',
|
188 |
+
array $options = []
|
189 |
+
) {
|
190 |
+
$s = "s3://$bucket" . ($keyPrefix ? '/' . ltrim($keyPrefix, '/') : '');
|
191 |
+
return (new Transfer($this, $s, $directory, $options))->promise();
|
192 |
+
}
|
193 |
+
|
194 |
+
/**
|
195 |
+
* @see S3ClientInterface::determineBucketRegion()
|
196 |
+
*/
|
197 |
+
public function determineBucketRegion($bucketName)
|
198 |
+
{
|
199 |
+
return $this->determineBucketRegionAsync($bucketName)->wait();
|
200 |
+
}
|
201 |
+
|
202 |
+
/**
|
203 |
+
* @see S3ClientInterface::determineBucketRegionAsync()
|
204 |
+
*
|
205 |
+
* @param string $bucketName
|
206 |
+
*
|
207 |
+
* @return PromiseInterface
|
208 |
+
*/
|
209 |
+
public function determineBucketRegionAsync($bucketName)
|
210 |
+
{
|
211 |
+
$command = $this->getCommand('HeadBucket', ['Bucket' => $bucketName]);
|
212 |
+
$handlerList = clone $this->getHandlerList();
|
213 |
+
$handlerList->remove('s3.permanent_redirect');
|
214 |
+
$handlerList->remove('signer');
|
215 |
+
$handler = $handlerList->resolve();
|
216 |
+
|
217 |
+
return $handler($command)
|
218 |
+
->then(static function (ResultInterface $result) {
|
219 |
+
return $result['@metadata']['headers']['x-amz-bucket-region'];
|
220 |
+
}, function (AwsException $e) {
|
221 |
+
$response = $e->getResponse();
|
222 |
+
if ($response === null) {
|
223 |
+
throw $e;
|
224 |
+
}
|
225 |
+
|
226 |
+
if ($e->getAwsErrorCode() === 'AuthorizationHeaderMalformed') {
|
227 |
+
$region = $this->determineBucketRegionFromExceptionBody(
|
228 |
+
$response
|
229 |
+
);
|
230 |
+
if (!empty($region)) {
|
231 |
+
return $region;
|
232 |
+
}
|
233 |
+
throw $e;
|
234 |
+
}
|
235 |
+
|
236 |
+
return $response->getHeaderLine('x-amz-bucket-region');
|
237 |
+
});
|
238 |
+
}
|
239 |
+
|
240 |
+
private function determineBucketRegionFromExceptionBody(ResponseInterface $response)
|
241 |
+
{
|
242 |
+
try {
|
243 |
+
$element = $this->parseXml($response->getBody(), $response);
|
244 |
+
if (!empty($element->Region)) {
|
245 |
+
return (string)$element->Region;
|
246 |
+
}
|
247 |
+
} catch (\Exception $e) {
|
248 |
+
// Fallthrough on exceptions from parsing
|
249 |
+
}
|
250 |
+
return false;
|
251 |
+
}
|
252 |
+
|
253 |
+
/**
|
254 |
+
* @see S3ClientInterface::doesBucketExist()
|
255 |
+
*/
|
256 |
+
public function doesBucketExist($bucket)
|
257 |
+
{
|
258 |
+
return $this->checkExistenceWithCommand(
|
259 |
+
$this->getCommand('HeadBucket', ['Bucket' => $bucket])
|
260 |
+
);
|
261 |
+
}
|
262 |
+
|
263 |
+
/**
|
264 |
+
* @see S3ClientInterface::doesObjectExist()
|
265 |
+
*/
|
266 |
+
public function doesObjectExist($bucket, $key, array $options = [])
|
267 |
+
{
|
268 |
+
return $this->checkExistenceWithCommand(
|
269 |
+
$this->getCommand('HeadObject', [
|
270 |
+
'Bucket' => $bucket,
|
271 |
+
'Key' => $key
|
272 |
+
] + $options)
|
273 |
+
);
|
274 |
+
}
|
275 |
+
|
276 |
+
/**
|
277 |
+
* Determines whether or not a resource exists using a command
|
278 |
+
*
|
279 |
+
* @param CommandInterface $command Command used to poll for the resource
|
280 |
+
*
|
281 |
+
* @return bool
|
282 |
+
* @throws S3Exception|\Exception if there is an unhandled exception
|
283 |
+
*/
|
284 |
+
private function checkExistenceWithCommand(CommandInterface $command)
|
285 |
+
{
|
286 |
+
try {
|
287 |
+
$this->execute($command);
|
288 |
+
return true;
|
289 |
+
} catch (S3Exception $e) {
|
290 |
+
if ($e->getAwsErrorCode() == 'AccessDenied') {
|
291 |
+
return true;
|
292 |
+
}
|
293 |
+
if ($e->getStatusCode() >= 500) {
|
294 |
+
throw $e;
|
295 |
+
}
|
296 |
+
return false;
|
297 |
+
}
|
298 |
+
}
|
299 |
+
|
300 |
+
/**
|
301 |
+
* @see S3ClientInterface::execute()
|
302 |
+
*/
|
303 |
+
abstract public function execute(CommandInterface $command);
|
304 |
+
|
305 |
+
/**
|
306 |
+
* @see S3ClientInterface::getCommand()
|
307 |
+
*/
|
308 |
+
abstract public function getCommand($name, array $args = []);
|
309 |
+
|
310 |
+
/**
|
311 |
+
* @see S3ClientInterface::getHandlerList()
|
312 |
+
*
|
313 |
+
* @return HandlerList
|
314 |
+
*/
|
315 |
+
abstract public function getHandlerList();
|
316 |
+
|
317 |
+
/**
|
318 |
+
* @see S3ClientInterface::getIterator()
|
319 |
+
*
|
320 |
+
* @return \Iterator
|
321 |
+
*/
|
322 |
+
abstract public function getIterator($name, array $args = []);
|
323 |
+
}
|
lib/Aws/Aws/S3/S3EndpointMiddleware.php
ADDED
@@ -0,0 +1,234 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\S3;
|
3 |
+
|
4 |
+
use Aws\CommandInterface;
|
5 |
+
use Psr\Http\Message\RequestInterface;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Used to update the URL used for S3 requests to support:
|
9 |
+
* S3 Accelerate, S3 DualStack or Both. It will build to
|
10 |
+
* host style paths unless specified, including for S3
|
11 |
+
* DualStack.
|
12 |
+
*
|
13 |
+
* IMPORTANT: this middleware must be added after the "build" step.
|
14 |
+
*
|
15 |
+
* @internal
|
16 |
+
*/
|
17 |
+
class S3EndpointMiddleware
|
18 |
+
{
|
19 |
+
private static $exclusions = [
|
20 |
+
'CreateBucket' => true,
|
21 |
+
'DeleteBucket' => true,
|
22 |
+
'ListBuckets' => true,
|
23 |
+
];
|
24 |
+
|
25 |
+
const NO_PATTERN = 0;
|
26 |
+
const DUALSTACK = 1;
|
27 |
+
const ACCELERATE = 2;
|
28 |
+
const ACCELERATE_DUALSTACK = 3;
|
29 |
+
const PATH_STYLE = 4;
|
30 |
+
const HOST_STYLE = 5;
|
31 |
+
|
32 |
+
/** @var bool */
|
33 |
+
private $accelerateByDefault;
|
34 |
+
/** @var bool */
|
35 |
+
private $dualStackByDefault;
|
36 |
+
/** @var bool */
|
37 |
+
private $pathStyleByDefault;
|
38 |
+
/** @var string */
|
39 |
+
private $region;
|
40 |
+
/** @var callable */
|
41 |
+
private $nextHandler;
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Create a middleware wrapper function
|
45 |
+
*
|
46 |
+
* @param string $region
|
47 |
+
* @param array $options
|
48 |
+
*
|
49 |
+
* @return callable
|
50 |
+
*/
|
51 |
+
public static function wrap($region, array $options)
|
52 |
+
{
|
53 |
+
return function (callable $handler) use ($region, $options) {
|
54 |
+
return new self($handler, $region, $options);
|
55 |
+
};
|
56 |
+
}
|
57 |
+
|
58 |
+
public function __construct(
|
59 |
+
callable $nextHandler,
|
60 |
+
$region,
|
61 |
+
array $options
|
62 |
+
) {
|
63 |
+
$this->pathStyleByDefault = isset($options['path_style'])
|
64 |
+
? (bool) $options['path_style'] : false;
|
65 |
+
$this->dualStackByDefault = isset($options['dual_stack'])
|
66 |
+
? (bool) $options['dual_stack'] : false;
|
67 |
+
$this->accelerateByDefault = isset($options['accelerate'])
|
68 |
+
? (bool) $options['accelerate'] : false;
|
69 |
+
$this->region = (string) $region;
|
70 |
+
$this->nextHandler = $nextHandler;
|
71 |
+
}
|
72 |
+
|
73 |
+
public function __invoke(CommandInterface $command, RequestInterface $request)
|
74 |
+
{
|
75 |
+
switch ($this->endpointPatternDecider($command, $request)) {
|
76 |
+
case self::HOST_STYLE:
|
77 |
+
$request = $this->applyHostStyleEndpoint($command, $request);
|
78 |
+
break;
|
79 |
+
case self::NO_PATTERN:
|
80 |
+
case self::PATH_STYLE:
|
81 |
+
break;
|
82 |
+
case self::DUALSTACK:
|
83 |
+
$request = $this->applyDualStackEndpoint($command, $request);
|
84 |
+
break;
|
85 |
+
case self::ACCELERATE:
|
86 |
+
$request = $this->applyAccelerateEndpoint(
|
87 |
+
$command,
|
88 |
+
$request,
|
89 |
+
's3-accelerate'
|
90 |
+
);
|
91 |
+
break;
|
92 |
+
case self::ACCELERATE_DUALSTACK:
|
93 |
+
$request = $this->applyAccelerateEndpoint(
|
94 |
+
$command,
|
95 |
+
$request,
|
96 |
+
's3-accelerate.dualstack'
|
97 |
+
);
|
98 |
+
break;
|
99 |
+
}
|
100 |
+
|
101 |
+
$nextHandler = $this->nextHandler;
|
102 |
+
return $nextHandler($command, $request);
|
103 |
+
}
|
104 |
+
|
105 |
+
private static function isRequestHostStyleCompatible(
|
106 |
+
CommandInterface $command,
|
107 |
+
RequestInterface $request
|
108 |
+
) {
|
109 |
+
return S3Client::isBucketDnsCompatible($command['Bucket'])
|
110 |
+
&& (
|
111 |
+
$request->getUri()->getScheme() === 'http'
|
112 |
+
|| strpos($command['Bucket'], '.') === false
|
113 |
+
);
|
114 |
+
}
|
115 |
+
|
116 |
+
private function endpointPatternDecider(
|
117 |
+
CommandInterface $command,
|
118 |
+
RequestInterface $request
|
119 |
+
) {
|
120 |
+
$accelerate = isset($command['@use_accelerate_endpoint'])
|
121 |
+
? $command['@use_accelerate_endpoint'] : $this->accelerateByDefault;
|
122 |
+
$dualStack = isset($command['@use_dual_stack_endpoint'])
|
123 |
+
? $command['@use_dual_stack_endpoint'] : $this->dualStackByDefault;
|
124 |
+
$pathStyle = isset($command['@use_path_style_endpoint'])
|
125 |
+
? $command['@use_path_style_endpoint'] : $this->pathStyleByDefault;
|
126 |
+
|
127 |
+
if ($accelerate && $dualStack) {
|
128 |
+
// When try to enable both for operations excluded from s3-accelerate,
|
129 |
+
// only dualstack endpoints will be enabled.
|
130 |
+
return $this->canAccelerate($command)
|
131 |
+
? self::ACCELERATE_DUALSTACK
|
132 |
+
: self::DUALSTACK;
|
133 |
+
}
|
134 |
+
|
135 |
+
if ($accelerate && $this->canAccelerate($command)) {
|
136 |
+
return self::ACCELERATE;
|
137 |
+
}
|
138 |
+
|
139 |
+
if ($dualStack) {
|
140 |
+
return self::DUALSTACK;
|
141 |
+
}
|
142 |
+
|
143 |
+
if (!$pathStyle
|
144 |
+
&& self::isRequestHostStyleCompatible($command, $request)
|
145 |
+
) {
|
146 |
+
return self::HOST_STYLE;
|
147 |
+
}
|
148 |
+
|
149 |
+
return self::PATH_STYLE;
|
150 |
+
}
|
151 |
+
|
152 |
+
private function canAccelerate(CommandInterface $command)
|
153 |
+
{
|
154 |
+
return empty(self::$exclusions[$command->getName()])
|
155 |
+
&& S3Client::isBucketDnsCompatible($command['Bucket']);
|
156 |
+
}
|
157 |
+
|
158 |
+
private function getBucketStyleHost(CommandInterface $command, $host)
|
159 |
+
{
|
160 |
+
// For operations on the base host (e.g. ListBuckets)
|
161 |
+
if (!isset($command['Bucket'])) {
|
162 |
+
return $host;
|
163 |
+
}
|
164 |
+
|
165 |
+
return "{$command['Bucket']}.{$host}";
|
166 |
+
}
|
167 |
+
|
168 |
+
private function applyHostStyleEndpoint(
|
169 |
+
CommandInterface $command,
|
170 |
+
RequestInterface $request
|
171 |
+
) {
|
172 |
+
$uri = $request->getUri();
|
173 |
+
$request = $request->withUri(
|
174 |
+
$uri->withHost($this->getBucketStyleHost(
|
175 |
+
$command,
|
176 |
+
$uri->getHost()
|
177 |
+
))
|
178 |
+
->withPath($this->getBucketlessPath(
|
179 |
+
$uri->getPath(),
|
180 |
+
$command
|
181 |
+
))
|
182 |
+
);
|
183 |
+
return $request;
|
184 |
+
}
|
185 |
+
|
186 |
+
private function applyDualStackEndpoint(
|
187 |
+
CommandInterface $command,
|
188 |
+
RequestInterface $request
|
189 |
+
) {
|
190 |
+
$request = $request->withUri(
|
191 |
+
$request->getUri()
|
192 |
+
->withHost($this->getDualStackHost())
|
193 |
+
);
|
194 |
+
if (empty($command['@use_path_style_endpoint'])
|
195 |
+
&& !$this->pathStyleByDefault
|
196 |
+
&& self::isRequestHostStyleCompatible($command, $request)
|
197 |
+
) {
|
198 |
+
$request = $this->applyHostStyleEndpoint($command, $request);
|
199 |
+
}
|
200 |
+
return $request;
|
201 |
+
}
|
202 |
+
|
203 |
+
private function getDualStackHost()
|
204 |
+
{
|
205 |
+
return "s3.dualstack.{$this->region}.amazonaws.com";
|
206 |
+
}
|
207 |
+
|
208 |
+
private function applyAccelerateEndpoint(
|
209 |
+
CommandInterface $command,
|
210 |
+
RequestInterface $request,
|
211 |
+
$pattern
|
212 |
+
) {
|
213 |
+
$request = $request->withUri(
|
214 |
+
$request->getUri()
|
215 |
+
->withHost($this->getAccelerateHost($command, $pattern))
|
216 |
+
->withPath($this->getBucketlessPath(
|
217 |
+
$request->getUri()->getPath(),
|
218 |
+
$command
|
219 |
+
))
|
220 |
+
);
|
221 |
+
return $request;
|
222 |
+
}
|
223 |
+
|
224 |
+
private function getAccelerateHost(CommandInterface $command, $pattern)
|
225 |
+
{
|
226 |
+
return "{$command['Bucket']}.{$pattern}.amazonaws.com";
|
227 |
+
}
|
228 |
+
|
229 |
+
private function getBucketlessPath($path, CommandInterface $command)
|
230 |
+
{
|
231 |
+
$pattern = '/^\\/' . preg_quote($command['Bucket'], '/') . '/';
|
232 |
+
return preg_replace($pattern, '', $path) ?: '/';
|
233 |
+
}
|
234 |
+
}
|
lib/Aws/Aws/S3/S3MultiRegionClient.php
ADDED
@@ -0,0 +1,339 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\S3;
|
3 |
+
|
4 |
+
use Aws\CacheInterface;
|
5 |
+
use Aws\CommandInterface;
|
6 |
+
use Aws\LruArrayCache;
|
7 |
+
use Aws\MultiRegionClient as BaseClient;
|
8 |
+
use Aws\Exception\AwsException;
|
9 |
+
use Aws\S3\Exception\PermanentRedirectException;
|
10 |
+
use GuzzleHttp\Promise;
|
11 |
+
|
12 |
+
/**
|
13 |
+
* **Amazon Simple Storage Service** multi-region client.
|
14 |
+
*
|
15 |
+
* @method \Aws\Result abortMultipartUpload(array $args = [])
|
16 |
+
* @method \GuzzleHttp\Promise\Promise abortMultipartUploadAsync(array $args = [])
|
17 |
+
* @method \Aws\Result completeMultipartUpload(array $args = [])
|
18 |
+
* @method \GuzzleHttp\Promise\Promise completeMultipartUploadAsync(array $args = [])
|
19 |
+
* @method \Aws\Result copyObject(array $args = [])
|
20 |
+
* @method \GuzzleHttp\Promise\Promise copyObjectAsync(array $args = [])
|
21 |
+
* @method \Aws\Result createBucket(array $args = [])
|
22 |
+
* @method \GuzzleHttp\Promise\Promise createBucketAsync(array $args = [])
|
23 |
+
* @method \Aws\Result createMultipartUpload(array $args = [])
|
24 |
+
* @method \GuzzleHttp\Promise\Promise createMultipartUploadAsync(array $args = [])
|
25 |
+
* @method \Aws\Result deleteBucket(array $args = [])
|
26 |
+
* @method \GuzzleHttp\Promise\Promise deleteBucketAsync(array $args = [])
|
27 |
+
* @method \Aws\Result deleteBucketAnalyticsConfiguration(array $args = [])
|
28 |
+
* @method \GuzzleHttp\Promise\Promise deleteBucketAnalyticsConfigurationAsync(array $args = [])
|
29 |
+
* @method \Aws\Result deleteBucketCors(array $args = [])
|
30 |
+
* @method \GuzzleHttp\Promise\Promise deleteBucketCorsAsync(array $args = [])
|
31 |
+
* @method \Aws\Result deleteBucketEncryption(array $args = [])
|
32 |
+
* @method \GuzzleHttp\Promise\Promise deleteBucketEncryptionAsync(array $args = [])
|
33 |
+
* @method \Aws\Result deleteBucketInventoryConfiguration(array $args = [])
|
34 |
+
* @method \GuzzleHttp\Promise\Promise deleteBucketInventoryConfigurationAsync(array $args = [])
|
35 |
+
* @method \Aws\Result deleteBucketLifecycle(array $args = [])
|
36 |
+
* @method \GuzzleHttp\Promise\Promise deleteBucketLifecycleAsync(array $args = [])
|
37 |
+
* @method \Aws\Result deleteBucketMetricsConfiguration(array $args = [])
|
38 |
+
* @method \GuzzleHttp\Promise\Promise deleteBucketMetricsConfigurationAsync(array $args = [])
|
39 |
+
* @method \Aws\Result deleteBucketPolicy(array $args = [])
|
40 |
+
* @method \GuzzleHttp\Promise\Promise deleteBucketPolicyAsync(array $args = [])
|
41 |
+
* @method \Aws\Result deleteBucketReplication(array $args = [])
|
42 |
+
* @method \GuzzleHttp\Promise\Promise deleteBucketReplicationAsync(array $args = [])
|
43 |
+
* @method \Aws\Result deleteBucketTagging(array $args = [])
|
44 |
+
* @method \GuzzleHttp\Promise\Promise deleteBucketTaggingAsync(array $args = [])
|
45 |
+
* @method \Aws\Result deleteBucketWebsite(array $args = [])
|
46 |
+
* @method \GuzzleHttp\Promise\Promise deleteBucketWebsiteAsync(array $args = [])
|
47 |
+
* @method \Aws\Result deleteObject(array $args = [])
|
48 |
+
* @method \GuzzleHttp\Promise\Promise deleteObjectAsync(array $args = [])
|
49 |
+
* @method \Aws\Result deleteObjectTagging(array $args = [])
|
50 |
+
* @method \GuzzleHttp\Promise\Promise deleteObjectTaggingAsync(array $args = [])
|
51 |
+
* @method \Aws\Result deleteObjects(array $args = [])
|
52 |
+
* @method \GuzzleHttp\Promise\Promise deleteObjectsAsync(array $args = [])
|
53 |
+
* @method \Aws\Result deletePublicAccessBlock(array $args = [])
|
54 |
+
* @method \GuzzleHttp\Promise\Promise deletePublicAccessBlockAsync(array $args = [])
|
55 |
+
* @method \Aws\Result getBucketAccelerateConfiguration(array $args = [])
|
56 |
+
* @method \GuzzleHttp\Promise\Promise getBucketAccelerateConfigurationAsync(array $args = [])
|
57 |
+
* @method \Aws\Result getBucketAcl(array $args = [])
|
58 |
+
* @method \GuzzleHttp\Promise\Promise getBucketAclAsync(array $args = [])
|
59 |
+
* @method \Aws\Result getBucketAnalyticsConfiguration(array $args = [])
|
60 |
+
* @method \GuzzleHttp\Promise\Promise getBucketAnalyticsConfigurationAsync(array $args = [])
|
61 |
+
* @method \Aws\Result getBucketCors(array $args = [])
|
62 |
+
* @method \GuzzleHttp\Promise\Promise getBucketCorsAsync(array $args = [])
|
63 |
+
* @method \Aws\Result getBucketEncryption(array $args = [])
|
64 |
+
* @method \GuzzleHttp\Promise\Promise getBucketEncryptionAsync(array $args = [])
|
65 |
+
* @method \Aws\Result getBucketInventoryConfiguration(array $args = [])
|
66 |
+
* @method \GuzzleHttp\Promise\Promise getBucketInventoryConfigurationAsync(array $args = [])
|
67 |
+
* @method \Aws\Result getBucketLifecycle(array $args = [])
|
68 |
+
* @method \GuzzleHttp\Promise\Promise getBucketLifecycleAsync(array $args = [])
|
69 |
+
* @method \Aws\Result getBucketLifecycleConfiguration(array $args = [])
|
70 |
+
* @method \GuzzleHttp\Promise\Promise getBucketLifecycleConfigurationAsync(array $args = [])
|
71 |
+
* @method \Aws\Result getBucketLocation(array $args = [])
|
72 |
+
* @method \GuzzleHttp\Promise\Promise getBucketLocationAsync(array $args = [])
|
73 |
+
* @method \Aws\Result getBucketLogging(array $args = [])
|
74 |
+
* @method \GuzzleHttp\Promise\Promise getBucketLoggingAsync(array $args = [])
|
75 |
+
* @method \Aws\Result getBucketMetricsConfiguration(array $args = [])
|
76 |
+
* @method \GuzzleHttp\Promise\Promise getBucketMetricsConfigurationAsync(array $args = [])
|
77 |
+
* @method \Aws\Result getBucketNotification(array $args = [])
|
78 |
+
* @method \GuzzleHttp\Promise\Promise getBucketNotificationAsync(array $args = [])
|
79 |
+
* @method \Aws\Result getBucketNotificationConfiguration(array $args = [])
|
80 |
+
* @method \GuzzleHttp\Promise\Promise getBucketNotificationConfigurationAsync(array $args = [])
|
81 |
+
* @method \Aws\Result getBucketPolicy(array $args = [])
|
82 |
+
* @method \GuzzleHttp\Promise\Promise getBucketPolicyAsync(array $args = [])
|
83 |
+
* @method \Aws\Result getBucketPolicyStatus(array $args = [])
|
84 |
+
* @method \GuzzleHttp\Promise\Promise getBucketPolicyStatusAsync(array $args = [])
|
85 |
+
* @method \Aws\Result getBucketReplication(array $args = [])
|
86 |
+
* @method \GuzzleHttp\Promise\Promise getBucketReplicationAsync(array $args = [])
|
87 |
+
* @method \Aws\Result getBucketRequestPayment(array $args = [])
|
88 |
+
* @method \GuzzleHttp\Promise\Promise getBucketRequestPaymentAsync(array $args = [])
|
89 |
+
* @method \Aws\Result getBucketTagging(array $args = [])
|
90 |
+
* @method \GuzzleHttp\Promise\Promise getBucketTaggingAsync(array $args = [])
|
91 |
+
* @method \Aws\Result getBucketVersioning(array $args = [])
|
92 |
+
* @method \GuzzleHttp\Promise\Promise getBucketVersioningAsync(array $args = [])
|
93 |
+
* @method \Aws\Result getBucketWebsite(array $args = [])
|
94 |
+
* @method \GuzzleHttp\Promise\Promise getBucketWebsiteAsync(array $args = [])
|
95 |
+
* @method \Aws\Result getObject(array $args = [])
|
96 |
+
* @method \GuzzleHttp\Promise\Promise getObjectAsync(array $args = [])
|
97 |
+
* @method \Aws\Result getObjectAcl(array $args = [])
|
98 |
+
* @method \GuzzleHttp\Promise\Promise getObjectAclAsync(array $args = [])
|
99 |
+
* @method \Aws\Result getObjectLegalHold(array $args = [])
|
100 |
+
* @method \GuzzleHttp\Promise\Promise getObjectLegalHoldAsync(array $args = [])
|
101 |
+
* @method \Aws\Result getObjectLockConfiguration(array $args = [])
|
102 |
+
* @method \GuzzleHttp\Promise\Promise getObjectLockConfigurationAsync(array $args = [])
|
103 |
+
* @method \Aws\Result getObjectRetention(array $args = [])
|
104 |
+
* @method \GuzzleHttp\Promise\Promise getObjectRetentionAsync(array $args = [])
|
105 |
+
* @method \Aws\Result getObjectTagging(array $args = [])
|
106 |
+
* @method \GuzzleHttp\Promise\Promise getObjectTaggingAsync(array $args = [])
|
107 |
+
* @method \Aws\Result getObjectTorrent(array $args = [])
|
108 |
+
* @method \GuzzleHttp\Promise\Promise getObjectTorrentAsync(array $args = [])
|
109 |
+
* @method \Aws\Result getPublicAccessBlock(array $args = [])
|
110 |
+
* @method \GuzzleHttp\Promise\Promise getPublicAccessBlockAsync(array $args = [])
|
111 |
+
* @method \Aws\Result headBucket(array $args = [])
|
112 |
+
* @method \GuzzleHttp\Promise\Promise headBucketAsync(array $args = [])
|
113 |
+
* @method \Aws\Result headObject(array $args = [])
|
114 |
+
* @method \GuzzleHttp\Promise\Promise headObjectAsync(array $args = [])
|
115 |
+
* @method \Aws\Result listBucketAnalyticsConfigurations(array $args = [])
|
116 |
+
* @method \GuzzleHttp\Promise\Promise listBucketAnalyticsConfigurationsAsync(array $args = [])
|
117 |
+
* @method \Aws\Result listBucketInventoryConfigurations(array $args = [])
|
118 |
+
* @method \GuzzleHttp\Promise\Promise listBucketInventoryConfigurationsAsync(array $args = [])
|
119 |
+
* @method \Aws\Result listBucketMetricsConfigurations(array $args = [])
|
120 |
+
* @method \GuzzleHttp\Promise\Promise listBucketMetricsConfigurationsAsync(array $args = [])
|
121 |
+
* @method \Aws\Result listBuckets(array $args = [])
|
122 |
+
* @method \GuzzleHttp\Promise\Promise listBucketsAsync(array $args = [])
|
123 |
+
* @method \Aws\Result listMultipartUploads(array $args = [])
|
124 |
+
* @method \GuzzleHttp\Promise\Promise listMultipartUploadsAsync(array $args = [])
|
125 |
+
* @method \Aws\Result listObjectVersions(array $args = [])
|
126 |
+
* @method \GuzzleHttp\Promise\Promise listObjectVersionsAsync(array $args = [])
|
127 |
+
* @method \Aws\Result listObjects(array $args = [])
|
128 |
+
* @method \GuzzleHttp\Promise\Promise listObjectsAsync(array $args = [])
|
129 |
+
* @method \Aws\Result listObjectsV2(array $args = [])
|
130 |
+
* @method \GuzzleHttp\Promise\Promise listObjectsV2Async(array $args = [])
|
131 |
+
* @method \Aws\Result listParts(array $args = [])
|
132 |
+
* @method \GuzzleHttp\Promise\Promise listPartsAsync(array $args = [])
|
133 |
+
* @method \Aws\Result putBucketAccelerateConfiguration(array $args = [])
|
134 |
+
* @method \GuzzleHttp\Promise\Promise putBucketAccelerateConfigurationAsync(array $args = [])
|
135 |
+
* @method \Aws\Result putBucketAcl(array $args = [])
|
136 |
+
* @method \GuzzleHttp\Promise\Promise putBucketAclAsync(array $args = [])
|
137 |
+
* @method \Aws\Result putBucketAnalyticsConfiguration(array $args = [])
|
138 |
+
* @method \GuzzleHttp\Promise\Promise putBucketAnalyticsConfigurationAsync(array $args = [])
|
139 |
+
* @method \Aws\Result putBucketCors(array $args = [])
|
140 |
+
* @method \GuzzleHttp\Promise\Promise putBucketCorsAsync(array $args = [])
|
141 |
+
* @method \Aws\Result putBucketEncryption(array $args = [])
|
142 |
+
* @method \GuzzleHttp\Promise\Promise putBucketEncryptionAsync(array $args = [])
|
143 |
+
* @method \Aws\Result putBucketInventoryConfiguration(array $args = [])
|
144 |
+
* @method \GuzzleHttp\Promise\Promise putBucketInventoryConfigurationAsync(array $args = [])
|
145 |
+
* @method \Aws\Result putBucketLifecycle(array $args = [])
|
146 |
+
* @method \GuzzleHttp\Promise\Promise putBucketLifecycleAsync(array $args = [])
|
147 |
+
* @method \Aws\Result putBucketLifecycleConfiguration(array $args = [])
|
148 |
+
* @method \GuzzleHttp\Promise\Promise putBucketLifecycleConfigurationAsync(array $args = [])
|
149 |
+
* @method \Aws\Result putBucketLogging(array $args = [])
|
150 |
+
* @method \GuzzleHttp\Promise\Promise putBucketLoggingAsync(array $args = [])
|
151 |
+
* @method \Aws\Result putBucketMetricsConfiguration(array $args = [])
|
152 |
+
* @method \GuzzleHttp\Promise\Promise putBucketMetricsConfigurationAsync(array $args = [])
|
153 |
+
* @method \Aws\Result putBucketNotification(array $args = [])
|
154 |
+
* @method \GuzzleHttp\Promise\Promise putBucketNotificationAsync(array $args = [])
|
155 |
+
* @method \Aws\Result putBucketNotificationConfiguration(array $args = [])
|
156 |
+
* @method \GuzzleHttp\Promise\Promise putBucketNotificationConfigurationAsync(array $args = [])
|
157 |
+
* @method \Aws\Result putBucketPolicy(array $args = [])
|
158 |
+
* @method \GuzzleHttp\Promise\Promise putBucketPolicyAsync(array $args = [])
|
159 |
+
* @method \Aws\Result putBucketReplication(array $args = [])
|
160 |
+
* @method \GuzzleHttp\Promise\Promise putBucketReplicationAsync(array $args = [])
|
161 |
+
* @method \Aws\Result putBucketRequestPayment(array $args = [])
|
162 |
+
* @method \GuzzleHttp\Promise\Promise putBucketRequestPaymentAsync(array $args = [])
|
163 |
+
* @method \Aws\Result putBucketTagging(array $args = [])
|
164 |
+
* @method \GuzzleHttp\Promise\Promise putBucketTaggingAsync(array $args = [])
|
165 |
+
* @method \Aws\Result putBucketVersioning(array $args = [])
|
166 |
+
* @method \GuzzleHttp\Promise\Promise putBucketVersioningAsync(array $args = [])
|
167 |
+
* @method \Aws\Result putBucketWebsite(array $args = [])
|
168 |
+
* @method \GuzzleHttp\Promise\Promise putBucketWebsiteAsync(array $args = [])
|
169 |
+
* @method \Aws\Result putObject(array $args = [])
|
170 |
+
* @method \GuzzleHttp\Promise\Promise putObjectAsync(array $args = [])
|
171 |
+
* @method \Aws\Result putObjectAcl(array $args = [])
|
172 |
+
* @method \GuzzleHttp\Promise\Promise putObjectAclAsync(array $args = [])
|
173 |
+
* @method \Aws\Result putObjectLegalHold(array $args = [])
|
174 |
+
* @method \GuzzleHttp\Promise\Promise putObjectLegalHoldAsync(array $args = [])
|
175 |
+
* @method \Aws\Result putObjectLockConfiguration(array $args = [])
|
176 |
+
* @method \GuzzleHttp\Promise\Promise putObjectLockConfigurationAsync(array $args = [])
|
177 |
+
* @method \Aws\Result putObjectRetention(array $args = [])
|
178 |
+
* @method \GuzzleHttp\Promise\Promise putObjectRetentionAsync(array $args = [])
|
179 |
+
* @method \Aws\Result putObjectTagging(array $args = [])
|
180 |
+
* @method \GuzzleHttp\Promise\Promise putObjectTaggingAsync(array $args = [])
|
181 |
+
* @method \Aws\Result putPublicAccessBlock(array $args = [])
|
182 |
+
* @method \GuzzleHttp\Promise\Promise putPublicAccessBlockAsync(array $args = [])
|
183 |
+
* @method \Aws\Result restoreObject(array $args = [])
|
184 |
+
* @method \GuzzleHttp\Promise\Promise restoreObjectAsync(array $args = [])
|
185 |
+
* @method \Aws\Result selectObjectContent(array $args = [])
|
186 |
+
* @method \GuzzleHttp\Promise\Promise selectObjectContentAsync(array $args = [])
|
187 |
+
* @method \Aws\Result uploadPart(array $args = [])
|
188 |
+
* @method \GuzzleHttp\Promise\Promise uploadPartAsync(array $args = [])
|
189 |
+
* @method \Aws\Result uploadPartCopy(array $args = [])
|
190 |
+
* @method \GuzzleHttp\Promise\Promise uploadPartCopyAsync(array $args = [])
|
191 |
+
*/
|
192 |
+
class S3MultiRegionClient extends BaseClient implements S3ClientInterface
|
193 |
+
{
|
194 |
+
use S3ClientTrait;
|
195 |
+
|
196 |
+
/** @var CacheInterface */
|
197 |
+
private $cache;
|
198 |
+
|
199 |
+
public static function getArguments()
|
200 |
+
{
|
201 |
+
$args = parent::getArguments();
|
202 |
+
$regionDef = $args['region'] + ['default' => function (array &$args) {
|
203 |
+
$availableRegions = array_keys($args['partition']['regions']);
|
204 |
+
return end($availableRegions);
|
205 |
+
}];
|
206 |
+
unset($args['region']);
|
207 |
+
|
208 |
+
return $args + [
|
209 |
+
'bucket_region_cache' => [
|
210 |
+
'type' => 'config',
|
211 |
+
'valid' => [CacheInterface::class],
|
212 |
+
'doc' => 'Cache of regions in which given buckets are located.',
|
213 |
+
'default' => function () { return new LruArrayCache; },
|
214 |
+
],
|
215 |
+
'region' => $regionDef,
|
216 |
+
];
|
217 |
+
}
|
218 |
+
|
219 |
+
public function __construct(array $args)
|
220 |
+
{
|
221 |
+
parent::__construct($args);
|
222 |
+
$this->cache = $this->getConfig('bucket_region_cache');
|
223 |
+
|
224 |
+
$this->getHandlerList()->prependInit(
|
225 |
+
$this->determineRegionMiddleware(),
|
226 |
+
'determine_region'
|
227 |
+
);
|
228 |
+
}
|
229 |
+
|
230 |
+
private function determineRegionMiddleware()
|
231 |
+
{
|
232 |
+
return function (callable $handler) {
|
233 |
+
return function (CommandInterface $command) use ($handler) {
|
234 |
+
$cacheKey = $this->getCacheKey($command['Bucket']);
|
235 |
+
if (
|
236 |
+
empty($command['@region']) &&
|
237 |
+
$region = $this->cache->get($cacheKey)
|
238 |
+
) {
|
239 |
+
$command['@region'] = $region;
|
240 |
+
}
|
241 |
+
|
242 |
+
return Promise\coroutine(function () use (
|
243 |
+
$handler,
|
244 |
+
$command,
|
245 |
+
$cacheKey
|
246 |
+
) {
|
247 |
+
try {
|
248 |
+
yield $handler($command);
|
249 |
+
} catch (PermanentRedirectException $e) {
|
250 |
+
if (empty($command['Bucket'])) {
|
251 |
+
throw $e;
|
252 |
+
}
|
253 |
+
$result = $e->getResult();
|
254 |
+
$region = null;
|
255 |
+
if (isset($result['@metadata']['headers']['x-amz-bucket-region'])) {
|
256 |
+
$region = $result['@metadata']['headers']['x-amz-bucket-region'];
|
257 |
+
$this->cache->set($cacheKey, $region);
|
258 |
+
} else {
|
259 |
+
$region = (yield $this->determineBucketRegionAsync(
|
260 |
+
$command['Bucket']
|
261 |
+
));
|
262 |
+
}
|
263 |
+
|
264 |
+
$command['@region'] = $region;
|
265 |
+
yield $handler($command);
|
266 |
+
} catch (AwsException $e) {
|
267 |
+
if ($e->getAwsErrorCode() === 'AuthorizationHeaderMalformed') {
|
268 |
+
$region = $this->determineBucketRegionFromExceptionBody(
|
269 |
+
$e->getResponse()
|
270 |
+
);
|
271 |
+
if (!empty($region)) {
|
272 |
+
$this->cache->set($cacheKey, $region);
|
273 |
+
|
274 |
+
$command['@region'] = $region;
|
275 |
+
yield $handler($command);
|
276 |
+
} else {
|
277 |
+
throw $e;
|
278 |
+
}
|
279 |
+
} else {
|
280 |
+
throw $e;
|
281 |
+
}
|
282 |
+
}
|
283 |
+
});
|
284 |
+
};
|
285 |
+
};
|
286 |
+
}
|
287 |
+
|
288 |
+
public function createPresignedRequest(CommandInterface $command, $expires)
|
289 |
+
{
|
290 |
+
if (empty($command['Bucket'])) {
|
291 |
+
throw new \InvalidArgumentException('The S3\\MultiRegionClient'
|
292 |
+
. ' cannot create presigned requests for commands without a'
|
293 |
+
. ' specified bucket.');
|
294 |
+
}
|
295 |
+
|
296 |
+
/** @var S3ClientInterface $client */
|
297 |
+
$client = $this->getClientFromPool(
|
298 |
+
$this->determineBucketRegion($command['Bucket'])
|
299 |
+
);
|
300 |
+
return $client->createPresignedRequest(
|
301 |
+
$client->getCommand($command->getName(), $command->toArray()),
|
302 |
+
$expires
|
303 |
+
);
|
304 |
+
}
|
305 |
+
|
306 |
+
public function getObjectUrl($bucket, $key)
|
307 |
+
{
|
308 |
+
/** @var S3Client $regionalClient */
|
309 |
+
$regionalClient = $this->getClientFromPool(
|
310 |
+
$this->determineBucketRegion($bucket)
|
311 |
+
);
|
312 |
+
|
313 |
+
return $regionalClient->getObjectUrl($bucket, $key);
|
314 |
+
}
|
315 |
+
|
316 |
+
public function determineBucketRegionAsync($bucketName)
|
317 |
+
{
|
318 |
+
$cacheKey = $this->getCacheKey($bucketName);
|
319 |
+
if ($cached = $this->cache->get($cacheKey)) {
|
320 |
+
return Promise\promise_for($cached);
|
321 |
+
}
|
322 |
+
|
323 |
+
/** @var S3ClientInterface $regionalClient */
|
324 |
+
$regionalClient = $this->getClientFromPool();
|
325 |
+
return $regionalClient->determineBucketRegionAsync($bucketName)
|
326 |
+
->then(
|
327 |
+
function ($region) use ($cacheKey) {
|
328 |
+
$this->cache->set($cacheKey, $region);
|
329 |
+
|
330 |
+
return $region;
|
331 |
+
}
|
332 |
+
);
|
333 |
+
}
|
334 |
+
|
335 |
+
private function getCacheKey($bucketName)
|
336 |
+
{
|
337 |
+
return "aws:s3:{$bucketName}:location";
|
338 |
+
}
|
339 |
+
}
|
lib/Aws/Aws/S3/S3UriParser.php
ADDED
@@ -0,0 +1,133 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\S3;
|
3 |
+
|
4 |
+
use GuzzleHttp\Psr7;
|
5 |
+
use Psr\Http\Message\UriInterface;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Extracts a region, bucket, key, and and if a URI is in path-style
|
9 |
+
*/
|
10 |
+
class S3UriParser
|
11 |
+
{
|
12 |
+
private $pattern = '/^(.+\\.)?s3[.-]([A-Za-z0-9-]+)\\./';
|
13 |
+
private $streamWrapperScheme = 's3';
|
14 |
+
|
15 |
+
private static $defaultResult = [
|
16 |
+
'path_style' => true,
|
17 |
+
'bucket' => null,
|
18 |
+
'key' => null,
|
19 |
+
'region' => null
|
20 |
+
];
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Parses a URL or S3 StreamWrapper Uri (s3://) into an associative array
|
24 |
+
* of Amazon S3 data including:
|
25 |
+
*
|
26 |
+
* - bucket: The Amazon S3 bucket (null if none)
|
27 |
+
* - key: The Amazon S3 key (null if none)
|
28 |
+
* - path_style: Set to true if using path style, or false if not
|
29 |
+
* - region: Set to a string if a non-class endpoint is used or null.
|
30 |
+
*
|
31 |
+
* @param string|UriInterface $uri
|
32 |
+
*
|
33 |
+
* @return array
|
34 |
+
* @throws \InvalidArgumentException
|
35 |
+
*/
|
36 |
+
public function parse($uri)
|
37 |
+
{
|
38 |
+
$url = Psr7\uri_for($uri);
|
39 |
+
|
40 |
+
if ($url->getScheme() == $this->streamWrapperScheme) {
|
41 |
+
return $this->parseStreamWrapper($url);
|
42 |
+
}
|
43 |
+
|
44 |
+
if (!$url->getHost()) {
|
45 |
+
throw new \InvalidArgumentException('No hostname found in URI: '
|
46 |
+
. $uri);
|
47 |
+
}
|
48 |
+
|
49 |
+
if (!preg_match($this->pattern, $url->getHost(), $matches)) {
|
50 |
+
return $this->parseCustomEndpoint($url);
|
51 |
+
}
|
52 |
+
|
53 |
+
// Parse the URI based on the matched format (path / virtual)
|
54 |
+
$result = empty($matches[1])
|
55 |
+
? $this->parsePathStyle($url)
|
56 |
+
: $this->parseVirtualHosted($url, $matches);
|
57 |
+
|
58 |
+
// Add the region if one was found and not the classic endpoint
|
59 |
+
$result['region'] = $matches[2] == 'amazonaws' ? null : $matches[2];
|
60 |
+
|
61 |
+
return $result;
|
62 |
+
}
|
63 |
+
|
64 |
+
private function parseStreamWrapper(UriInterface $url)
|
65 |
+
{
|
66 |
+
$result = self::$defaultResult;
|
67 |
+
$result['path_style'] = false;
|
68 |
+
|
69 |
+
$result['bucket'] = $url->getHost();
|
70 |
+
if ($url->getPath()) {
|
71 |
+
$key = ltrim($url->getPath(), '/ ');
|
72 |
+
if (!empty($key)) {
|
73 |
+
$result['key'] = $key;
|
74 |
+
}
|
75 |
+
}
|
76 |
+
|
77 |
+
return $result;
|
78 |
+
}
|
79 |
+
|
80 |
+
private function parseCustomEndpoint(UriInterface $url)
|
81 |
+
{
|
82 |
+
$result = self::$defaultResult;
|
83 |
+
$path = ltrim($url->getPath(), '/ ');
|
84 |
+
$segments = explode('/', $path, 2);
|
85 |
+
|
86 |
+
if (isset($segments[0])) {
|
87 |
+
$result['bucket'] = $segments[0];
|
88 |
+
if (isset($segments[1])) {
|
89 |
+
$result['key'] = $segments[1];
|
90 |
+
}
|
91 |
+
}
|
92 |
+
|
93 |
+
return $result;
|
94 |
+
}
|
95 |
+
|
96 |
+
private function parsePathStyle(UriInterface $url)
|
97 |
+
{
|
98 |
+
$result = self::$defaultResult;
|
99 |
+
|
100 |
+
if ($url->getPath() != '/') {
|
101 |
+
$path = ltrim($url->getPath(), '/');
|
102 |
+
if ($path) {
|
103 |
+
$pathPos = strpos($path, '/');
|
104 |
+
if ($pathPos === false) {
|
105 |
+
// https://s3.amazonaws.com/bucket
|
106 |
+
$result['bucket'] = $path;
|
107 |
+
} elseif ($pathPos == strlen($path) - 1) {
|
108 |
+
// https://s3.amazonaws.com/bucket/
|
109 |
+
$result['bucket'] = substr($path, 0, -1);
|
110 |
+
} else {
|
111 |
+
// https://s3.amazonaws.com/bucket/key
|
112 |
+
$result['bucket'] = substr($path, 0, $pathPos);
|
113 |
+
$result['key'] = substr($path, $pathPos + 1) ?: null;
|
114 |
+
}
|
115 |
+
}
|
116 |
+
}
|
117 |
+
|
118 |
+
return $result;
|
119 |
+
}
|
120 |
+
|
121 |
+
private function parseVirtualHosted(UriInterface $url, array $matches)
|
122 |
+
{
|
123 |
+
$result = self::$defaultResult;
|
124 |
+
$result['path_style'] = false;
|
125 |
+
// Remove trailing "." from the prefix to get the bucket
|
126 |
+
$result['bucket'] = substr($matches[1], 0, -1);
|
127 |
+
$path = $url->getPath();
|
128 |
+
// Check if a key was present, and if so, removing the leading "/"
|
129 |
+
$result['key'] = !$path || $path == '/' ? null : substr($path, 1);
|
130 |
+
|
131 |
+
return $result;
|
132 |
+
}
|
133 |
+
}
|
lib/Aws/Aws/S3/SSECMiddleware.php
ADDED
@@ -0,0 +1,75 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\S3;
|
3 |
+
|
4 |
+
use Aws\CommandInterface;
|
5 |
+
use Psr\Http\Message\RequestInterface;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Simplifies the SSE-C process by encoding and hashing the key.
|
9 |
+
* @internal
|
10 |
+
*/
|
11 |
+
class SSECMiddleware
|
12 |
+
{
|
13 |
+
private $endpointScheme;
|
14 |
+
private $nextHandler;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Provide the URI scheme of the client sending requests.
|
18 |
+
*
|
19 |
+
* @param string $endpointScheme URI scheme (http/https).
|
20 |
+
*
|
21 |
+
* @return callable
|
22 |
+
*/
|
23 |
+
public static function wrap($endpointScheme)
|
24 |
+
{
|
25 |
+
return function (callable $handler) use ($endpointScheme) {
|
26 |
+
return new self($endpointScheme, $handler);
|
27 |
+
};
|
28 |
+
}
|
29 |
+
|
30 |
+
public function __construct($endpointScheme, callable $nextHandler)
|
31 |
+
{
|
32 |
+
$this->nextHandler = $nextHandler;
|
33 |
+
$this->endpointScheme = $endpointScheme;
|
34 |
+
}
|
35 |
+
|
36 |
+
public function __invoke(
|
37 |
+
CommandInterface $command,
|
38 |
+
RequestInterface $request = null
|
39 |
+
) {
|
40 |
+
// Allows only HTTPS connections when using SSE-C
|
41 |
+
if (($command['SSECustomerKey'] || $command['CopySourceSSECustomerKey'])
|
42 |
+
&& $this->endpointScheme !== 'https'
|
43 |
+
) {
|
44 |
+
throw new \RuntimeException('You must configure your S3 client to '
|
45 |
+
. 'use HTTPS in order to use the SSE-C features.');
|
46 |
+
}
|
47 |
+
|
48 |
+
// Prepare the normal SSE-CPK headers
|
49 |
+
if ($command['SSECustomerKey']) {
|
50 |
+
$this->prepareSseParams($command);
|
51 |
+
}
|
52 |
+
|
53 |
+
// If it's a copy operation, prepare the SSE-CPK headers for the source.
|
54 |
+
if ($command['CopySourceSSECustomerKey']) {
|
55 |
+
$this->prepareSseParams($command, 'CopySource');
|
56 |
+
}
|
57 |
+
|
58 |
+
$f = $this->nextHandler;
|
59 |
+
return $f($command, $request);
|
60 |
+
}
|
61 |
+
|
62 |
+
private function prepareSseParams(CommandInterface $command, $prefix = '')
|
63 |
+
{
|
64 |
+
// Base64 encode the provided key
|
65 |
+
$key = $command[$prefix . 'SSECustomerKey'];
|
66 |
+
$command[$prefix . 'SSECustomerKey'] = base64_encode($key);
|
67 |
+
|
68 |
+
// Base64 the provided MD5 or, generate an MD5 if not provided
|
69 |
+
if ($md5 = $command[$prefix . 'SSECustomerKeyMD5']) {
|
70 |
+
$command[$prefix . 'SSECustomerKeyMD5'] = base64_encode($md5);
|
71 |
+
} else {
|
72 |
+
$command[$prefix . 'SSECustomerKeyMD5'] = base64_encode(md5($key, true));
|
73 |
+
}
|
74 |
+
}
|
75 |
+
}
|
lib/Aws/Aws/S3/StreamWrapper.php
ADDED
@@ -0,0 +1,958 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\S3;
|
3 |
+
|
4 |
+
use Aws\CacheInterface;
|
5 |
+
use Aws\LruArrayCache;
|
6 |
+
use Aws\Result;
|
7 |
+
use Aws\S3\Exception\S3Exception;
|
8 |
+
use GuzzleHttp\Psr7;
|
9 |
+
use GuzzleHttp\Psr7\Stream;
|
10 |
+
use GuzzleHttp\Psr7\CachingStream;
|
11 |
+
use Psr\Http\Message\StreamInterface;
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Amazon S3 stream wrapper to use "s3://<bucket>/<key>" files with PHP
|
15 |
+
* streams, supporting "r", "w", "a", "x".
|
16 |
+
*
|
17 |
+
* # Opening "r" (read only) streams:
|
18 |
+
*
|
19 |
+
* Read only streams are truly streaming by default and will not allow you to
|
20 |
+
* seek. This is because data read from the stream is not kept in memory or on
|
21 |
+
* the local filesystem. You can force a "r" stream to be seekable by setting
|
22 |
+
* the "seekable" stream context option true. This will allow true streaming of
|
23 |
+
* data from Amazon S3, but will maintain a buffer of previously read bytes in
|
24 |
+
* a 'php://temp' stream to allow seeking to previously read bytes from the
|
25 |
+
* stream.
|
26 |
+
*
|
27 |
+
* You may pass any GetObject parameters as 's3' stream context options. These
|
28 |
+
* options will affect how the data is downloaded from Amazon S3.
|
29 |
+
*
|
30 |
+
* # Opening "w" and "x" (write only) streams:
|
31 |
+
*
|
32 |
+
* Because Amazon S3 requires a Content-Length header, write only streams will
|
33 |
+
* maintain a 'php://temp' stream to buffer data written to the stream until
|
34 |
+
* the stream is flushed (usually by closing the stream with fclose).
|
35 |
+
*
|
36 |
+
* You may pass any PutObject parameters as 's3' stream context options. These
|
37 |
+
* options will affect how the data is uploaded to Amazon S3.
|
38 |
+
*
|
39 |
+
* When opening an "x" stream, the file must exist on Amazon S3 for the stream
|
40 |
+
* to open successfully.
|
41 |
+
*
|
42 |
+
* # Opening "a" (write only append) streams:
|
43 |
+
*
|
44 |
+
* Similar to "w" streams, opening append streams requires that the data be
|
45 |
+
* buffered in a "php://temp" stream. Append streams will attempt to download
|
46 |
+
* the contents of an object in Amazon S3, seek to the end of the object, then
|
47 |
+
* allow you to append to the contents of the object. The data will then be
|
48 |
+
* uploaded using a PutObject operation when the stream is flushed (usually
|
49 |
+
* with fclose).
|
50 |
+
*
|
51 |
+
* You may pass any GetObject and/or PutObject parameters as 's3' stream
|
52 |
+
* context options. These options will affect how the data is downloaded and
|
53 |
+
* uploaded from Amazon S3.
|
54 |
+
*
|
55 |
+
* Stream context options:
|
56 |
+
*
|
57 |
+
* - "seekable": Set to true to create a seekable "r" (read only) stream by
|
58 |
+
* using a php://temp stream buffer
|
59 |
+
* - For "unlink" only: Any option that can be passed to the DeleteObject
|
60 |
+
* operation
|
61 |
+
*/
|
62 |
+
class StreamWrapper
|
63 |
+
{
|
64 |
+
/** @var resource|null Stream context (this is set by PHP) */
|
65 |
+
public $context;
|
66 |
+
|
67 |
+
/** @var StreamInterface Underlying stream resource */
|
68 |
+
private $body;
|
69 |
+
|
70 |
+
/** @var int Size of the body that is opened */
|
71 |
+
private $size;
|
72 |
+
|
73 |
+
/** @var array Hash of opened stream parameters */
|
74 |
+
private $params = [];
|
75 |
+
|
76 |
+
/** @var string Mode in which the stream was opened */
|
77 |
+
private $mode;
|
78 |
+
|
79 |
+
/** @var \Iterator Iterator used with opendir() related calls */
|
80 |
+
private $objectIterator;
|
81 |
+
|
82 |
+
/** @var string The bucket that was opened when opendir() was called */
|
83 |
+
private $openedBucket;
|
84 |
+
|
85 |
+
/** @var string The prefix of the bucket that was opened with opendir() */
|
86 |
+
private $openedBucketPrefix;
|
87 |
+
|
88 |
+
/** @var string Opened bucket path */
|
89 |
+
private $openedPath;
|
90 |
+
|
91 |
+
/** @var CacheInterface Cache for object and dir lookups */
|
92 |
+
private $cache;
|
93 |
+
|
94 |
+
/** @var string The opened protocol (e.g., "s3") */
|
95 |
+
private $protocol = 's3';
|
96 |
+
|
97 |
+
/** @var bool Keeps track of whether stream has been flushed since opening */
|
98 |
+
private $isFlushed = false;
|
99 |
+
|
100 |
+
/**
|
101 |
+
* Register the 's3://' stream wrapper
|
102 |
+
*
|
103 |
+
* @param S3ClientInterface $client Client to use with the stream wrapper
|
104 |
+
* @param string $protocol Protocol to register as.
|
105 |
+
* @param CacheInterface $cache Default cache for the protocol.
|
106 |
+
*/
|
107 |
+
public static function register(
|
108 |
+
S3ClientInterface $client,
|
109 |
+
$protocol = 's3',
|
110 |
+
CacheInterface $cache = null
|
111 |
+
) {
|
112 |
+
if (in_array($protocol, stream_get_wrappers())) {
|
113 |
+
stream_wrapper_unregister($protocol);
|
114 |
+
}
|
115 |
+
|
116 |
+
// Set the client passed in as the default stream context client
|
117 |
+
stream_wrapper_register($protocol, get_called_class(), STREAM_IS_URL);
|
118 |
+
$default = stream_context_get_options(stream_context_get_default());
|
119 |
+
$default[$protocol]['client'] = $client;
|
120 |
+
|
121 |
+
if ($cache) {
|
122 |
+
$default[$protocol]['cache'] = $cache;
|
123 |
+
} elseif (!isset($default[$protocol]['cache'])) {
|
124 |
+
// Set a default cache adapter.
|
125 |
+
$default[$protocol]['cache'] = new LruArrayCache();
|
126 |
+
}
|
127 |
+
|
128 |
+
stream_context_set_default($default);
|
129 |
+
}
|
130 |
+
|
131 |
+
public function stream_close()
|
132 |
+
{
|
133 |
+
if ($this->body->getSize() === 0 && !($this->isFlushed)) {
|
134 |
+
$this->stream_flush();
|
135 |
+
}
|
136 |
+
$this->body = $this->cache = null;
|
137 |
+
}
|
138 |
+
|
139 |
+
public function stream_open($path, $mode, $options, &$opened_path)
|
140 |
+
{
|
141 |
+
$this->initProtocol($path);
|
142 |
+
$this->isFlushed = false;
|
143 |
+
$this->params = $this->getBucketKey($path);
|
144 |
+
$this->mode = rtrim($mode, 'bt');
|
145 |
+
|
146 |
+
if ($errors = $this->validate($path, $this->mode)) {
|
147 |
+
return $this->triggerError($errors);
|
148 |
+
}
|
149 |
+
|
150 |
+
return $this->boolCall(function() use ($path) {
|
151 |
+
switch ($this->mode) {
|
152 |
+
case 'r': return $this->openReadStream($path);
|
153 |
+
case 'a': return $this->openAppendStream($path);
|
154 |
+
default: return $this->openWriteStream($path);
|
155 |
+
}
|
156 |
+
});
|
157 |
+
}
|
158 |
+
|
159 |
+
public function stream_eof()
|
160 |
+
{
|
161 |
+
return $this->body->eof();
|
162 |
+
}
|
163 |
+
|
164 |
+
public function stream_flush()
|
165 |
+
{
|
166 |
+
$this->isFlushed = true;
|
167 |
+
if ($this->mode == 'r') {
|
168 |
+
return false;
|
169 |
+
}
|
170 |
+
|
171 |
+
if ($this->body->isSeekable()) {
|
172 |
+
$this->body->seek(0);
|
173 |
+
}
|
174 |
+
$params = $this->getOptions(true);
|
175 |
+
$params['Body'] = $this->body;
|
176 |
+
|
177 |
+
// Attempt to guess the ContentType of the upload based on the
|
178 |
+
// file extension of the key
|
179 |
+
if (!isset($params['ContentType']) &&
|
180 |
+
($type = Psr7\mimetype_from_filename($params['Key']))
|
181 |
+
) {
|
182 |
+
$params['ContentType'] = $type;
|
183 |
+
}
|
184 |
+
|
185 |
+
$this->clearCacheKey("s3://{$params['Bucket']}/{$params['Key']}");
|
186 |
+
return $this->boolCall(function () use ($params) {
|
187 |
+
return (bool) $this->getClient()->putObject($params);
|
188 |
+
});
|
189 |
+
}
|
190 |
+
|
191 |
+
public function stream_read($count)
|
192 |
+
{
|
193 |
+
return $this->body->read($count);
|
194 |
+
}
|
195 |
+
|
196 |
+
public function stream_seek($offset, $whence = SEEK_SET)
|
197 |
+
{
|
198 |
+
return !$this->body->isSeekable()
|
199 |
+
? false
|
200 |
+
: $this->boolCall(function () use ($offset, $whence) {
|
201 |
+
$this->body->seek($offset, $whence);
|
202 |
+
return true;
|
203 |
+
});
|
204 |
+
}
|
205 |
+
|
206 |
+
public function stream_tell()
|
207 |
+
{
|
208 |
+
return $this->boolCall(function() { return $this->body->tell(); });
|
209 |
+
}
|
210 |
+
|
211 |
+
public function stream_write($data)
|
212 |
+
{
|
213 |
+
return $this->body->write($data);
|
214 |
+
}
|
215 |
+
|
216 |
+
public function unlink($path)
|
217 |
+
{
|
218 |
+
$this->initProtocol($path);
|
219 |
+
|
220 |
+
return $this->boolCall(function () use ($path) {
|
221 |
+
$this->clearCacheKey($path);
|
222 |
+
$this->getClient()->deleteObject($this->withPath($path));
|
223 |
+
return true;
|
224 |
+
});
|
225 |
+
}
|
226 |
+
|
227 |
+
public function stream_stat()
|
228 |
+
{
|
229 |
+
$stat = $this->getStatTemplate();
|
230 |
+
$stat[7] = $stat['size'] = $this->getSize();
|
231 |
+
$stat[2] = $stat['mode'] = $this->mode;
|
232 |
+
|
233 |
+
return $stat;
|
234 |
+
}
|
235 |
+
|
236 |
+
/**
|
237 |
+
* Provides information for is_dir, is_file, filesize, etc. Works on
|
238 |
+
* buckets, keys, and prefixes.
|
239 |
+
* @link http://www.php.net/manual/en/streamwrapper.url-stat.php
|
240 |
+
*/
|
241 |
+
public function url_stat($path, $flags)
|
242 |
+
{
|
243 |
+
$this->initProtocol($path);
|
244 |
+
|
245 |
+
// Some paths come through as S3:// for some reason.
|
246 |
+
$split = explode('://', $path);
|
247 |
+
$path = strtolower($split[0]) . '://' . $split[1];
|
248 |
+
|
249 |
+
// Check if this path is in the url_stat cache
|
250 |
+
if ($value = $this->getCacheStorage()->get($path)) {
|
251 |
+
return $value;
|
252 |
+
}
|
253 |
+
|
254 |
+
$stat = $this->createStat($path, $flags);
|
255 |
+
|
256 |
+
if (is_array($stat)) {
|
257 |
+
$this->getCacheStorage()->set($path, $stat);
|
258 |
+
}
|
259 |
+
|
260 |
+
return $stat;
|
261 |
+
}
|
262 |
+
|
263 |
+
/**
|
264 |
+
* Parse the protocol out of the given path.
|
265 |
+
*
|
266 |
+
* @param $path
|
267 |
+
*/
|
268 |
+
private function initProtocol($path)
|
269 |
+
{
|
270 |
+
$parts = explode('://', $path, 2);
|
271 |
+
$this->protocol = $parts[0] ?: 's3';
|
272 |
+
}
|
273 |
+
|
274 |
+
private function createStat($path, $flags)
|
275 |
+
{
|
276 |
+
$this->initProtocol($path);
|
277 |
+
$parts = $this->withPath($path);
|
278 |
+
|
279 |
+
if (!$parts['Key']) {
|
280 |
+
return $this->statDirectory($parts, $path, $flags);
|
281 |
+
}
|
282 |
+
|
283 |
+
return $this->boolCall(function () use ($parts, $path) {
|
284 |
+
try {
|
285 |
+
$result = $this->getClient()->headObject($parts);
|
286 |
+
if (substr($parts['Key'], -1, 1) == '/' &&
|
287 |
+
$result['ContentLength'] == 0
|
288 |
+
) {
|
289 |
+
// Return as if it is a bucket to account for console
|
290 |
+
// bucket objects (e.g., zero-byte object "foo/")
|
291 |
+
return $this->formatUrlStat($path);
|
292 |
+
}
|
293 |
+
|
294 |
+
// Attempt to stat and cache regular object
|
295 |
+
return $this->formatUrlStat($result->toArray());
|
296 |
+
} catch (S3Exception $e) {
|
297 |
+
// Maybe this isn't an actual key, but a prefix. Do a prefix
|
298 |
+
// listing of objects to determine.
|
299 |
+
$result = $this->getClient()->listObjects([
|
300 |
+
'Bucket' => $parts['Bucket'],
|
301 |
+
'Prefix' => rtrim($parts['Key'], '/') . '/',
|
302 |
+
'MaxKeys' => 1
|
303 |
+
]);
|
304 |
+
if (!$result['Contents'] && !$result['CommonPrefixes']) {
|
305 |
+
throw new \Exception("File or directory not found: $path");
|
306 |
+
}
|
307 |
+
return $this->formatUrlStat($path);
|
308 |
+
}
|
309 |
+
}, $flags);
|
310 |
+
}
|
311 |
+
|
312 |
+
private function statDirectory($parts, $path, $flags)
|
313 |
+
{
|
314 |
+
// Stat "directories": buckets, or "s3://"
|
315 |
+
if (!$parts['Bucket'] ||
|
316 |
+
$this->getClient()->doesBucketExist($parts['Bucket'])
|
317 |
+
) {
|
318 |
+
return $this->formatUrlStat($path);
|
319 |
+
}
|
320 |
+
|
321 |
+
return $this->triggerError("File or directory not found: $path", $flags);
|
322 |
+
}
|
323 |
+
|
324 |
+
/**
|
325 |
+
* Support for mkdir().
|
326 |
+
*
|
327 |
+
* @param string $path Directory which should be created.
|
328 |
+
* @param int $mode Permissions. 700-range permissions map to
|
329 |
+
* ACL_PUBLIC. 600-range permissions map to
|
330 |
+
* ACL_AUTH_READ. All other permissions map to
|
331 |
+
* ACL_PRIVATE. Expects octal form.
|
332 |
+
* @param int $options A bitwise mask of values, such as
|
333 |
+
* STREAM_MKDIR_RECURSIVE.
|
334 |
+
*
|
335 |
+
* @return bool
|
336 |
+
* @link http://www.php.net/manual/en/streamwrapper.mkdir.php
|
337 |
+
*/
|
338 |
+
public function mkdir($path, $mode, $options)
|
339 |
+
{
|
340 |
+
$this->initProtocol($path);
|
341 |
+
$params = $this->withPath($path);
|
342 |
+
$this->clearCacheKey($path);
|
343 |
+
if (!$params['Bucket']) {
|
344 |
+
return false;
|
345 |
+
}
|
346 |
+
|
347 |
+
if (!isset($params['ACL'])) {
|
348 |
+
$params['ACL'] = $this->determineAcl($mode);
|
349 |
+
}
|
350 |
+
|
351 |
+
return empty($params['Key'])
|
352 |
+
? $this->createBucket($path, $params)
|
353 |
+
: $this->createSubfolder($path, $params);
|
354 |
+
}
|
355 |
+
|
356 |
+
public function rmdir($path, $options)
|
357 |
+
{
|
358 |
+
$this->initProtocol($path);
|
359 |
+
$this->clearCacheKey($path);
|
360 |
+
$params = $this->withPath($path);
|
361 |
+
$client = $this->getClient();
|
362 |
+
|
363 |
+
if (!$params['Bucket']) {
|
364 |
+
return $this->triggerError('You must specify a bucket');
|
365 |
+
}
|
366 |
+
|
367 |
+
return $this->boolCall(function () use ($params, $path, $client) {
|
368 |
+
if (!$params['Key']) {
|
369 |
+
$client->deleteBucket(['Bucket' => $params['Bucket']]);
|
370 |
+
return true;
|
371 |
+
}
|
372 |
+
return $this->deleteSubfolder($path, $params);
|
373 |
+
});
|
374 |
+
}
|
375 |
+
|
376 |
+
/**
|
377 |
+
* Support for opendir().
|
378 |
+
*
|
379 |
+
* The opendir() method of the Amazon S3 stream wrapper supports a stream
|
380 |
+
* context option of "listFilter". listFilter must be a callable that
|
381 |
+
* accepts an associative array of object data and returns true if the
|
382 |
+
* object should be yielded when iterating the keys in a bucket.
|
383 |
+
*
|
384 |
+
* @param string $path The path to the directory
|
385 |
+
* (e.g. "s3://dir[</prefix>]")
|
386 |
+
* @param string $options Unused option variable
|
387 |
+
*
|
388 |
+
* @return bool true on success
|
389 |
+
* @see http://www.php.net/manual/en/function.opendir.php
|
390 |
+
*/
|
391 |
+
public function dir_opendir($path, $options)
|
392 |
+
{
|
393 |
+
$this->initProtocol($path);
|
394 |
+
$this->openedPath = $path;
|
395 |
+
$params = $this->withPath($path);
|
396 |
+
$delimiter = $this->getOption('delimiter');
|
397 |
+
/** @var callable $filterFn */
|
398 |
+
$filterFn = $this->getOption('listFilter');
|
399 |
+
$op = ['Bucket' => $params['Bucket']];
|
400 |
+
$this->openedBucket = $params['Bucket'];
|
401 |
+
|
402 |
+
if ($delimiter === null) {
|
403 |
+
$delimiter = '/';
|
404 |
+
}
|
405 |
+
|
406 |
+
if ($delimiter) {
|
407 |
+
$op['Delimiter'] = $delimiter;
|
408 |
+
}
|
409 |
+
|
410 |
+
if ($params['Key']) {
|
411 |
+
$params['Key'] = rtrim($params['Key'], $delimiter) . $delimiter;
|
412 |
+
$op['Prefix'] = $params['Key'];
|
413 |
+
}
|
414 |
+
|
415 |
+
$this->openedBucketPrefix = $params['Key'];
|
416 |
+
|
417 |
+
// Filter our "/" keys added by the console as directories, and ensure
|
418 |
+
// that if a filter function is provided that it passes the filter.
|
419 |
+
$this->objectIterator = \Aws\flatmap(
|
420 |
+
$this->getClient()->getPaginator('ListObjects', $op),
|
421 |
+
function (Result $result) use ($filterFn) {
|
422 |
+
$contentsAndPrefixes = $result->search('[Contents[], CommonPrefixes[]][]');
|
423 |
+
// Filter out dir place holder keys and use the filter fn.
|
424 |
+
return array_filter(
|
425 |
+
$contentsAndPrefixes,
|
426 |
+
function ($key) use ($filterFn) {
|
427 |
+
return (!$filterFn || call_user_func($filterFn, $key))
|
428 |
+
&& (!isset($key['Key']) || substr($key['Key'], -1, 1) !== '/');
|
429 |
+
}
|
430 |
+
);
|
431 |
+
}
|
432 |
+
);
|
433 |
+
|
434 |
+
return true;
|
435 |
+
}
|
436 |
+
|
437 |
+
/**
|
438 |
+
* Close the directory listing handles
|
439 |
+
*
|
440 |
+
* @return bool true on success
|
441 |
+
*/
|
442 |
+
public function dir_closedir()
|
443 |
+
{
|
444 |
+
$this->objectIterator = null;
|
445 |
+
gc_collect_cycles();
|
446 |
+
|
447 |
+
return true;
|
448 |
+
}
|
449 |
+
|
450 |
+
/**
|
451 |
+
* This method is called in response to rewinddir()
|
452 |
+
*
|
453 |
+
* @return boolean true on success
|
454 |
+
*/
|
455 |
+
public function dir_rewinddir()
|
456 |
+
{
|
457 |
+
return $this->boolCall(function() {
|
458 |
+
$this->objectIterator = null;
|
459 |
+
$this->dir_opendir($this->openedPath, null);
|
460 |
+
return true;
|
461 |
+
});
|
462 |
+
}
|
463 |
+
|
464 |
+
/**
|
465 |
+
* This method is called in response to readdir()
|
466 |
+
*
|
467 |
+
* @return string Should return a string representing the next filename, or
|
468 |
+
* false if there is no next file.
|
469 |
+
* @link http://www.php.net/manual/en/function.readdir.php
|
470 |
+
*/
|
471 |
+
public function dir_readdir()
|
472 |
+
{
|
473 |
+
// Skip empty result keys
|
474 |
+
if (!$this->objectIterator->valid()) {
|
475 |
+
return false;
|
476 |
+
}
|
477 |
+
|
478 |
+
// First we need to create a cache key. This key is the full path to
|
479 |
+
// then object in s3: protocol://bucket/key.
|
480 |
+
// Next we need to create a result value. The result value is the
|
481 |
+
// current value of the iterator without the opened bucket prefix to
|
482 |
+
// emulate how readdir() works on directories.
|
483 |
+
// The cache key and result value will depend on if this is a prefix
|
484 |
+
// or a key.
|
485 |
+
$cur = $this->objectIterator->current();
|
486 |
+
if (isset($cur['Prefix'])) {
|
487 |
+
// Include "directories". Be sure to strip a trailing "/"
|
488 |
+
// on prefixes.
|
489 |
+
$result = rtrim($cur['Prefix'], '/');
|
490 |
+
$key = $this->formatKey($result);
|
491 |
+
$stat = $this->formatUrlStat($key);
|
492 |
+
} else {
|
493 |
+
$result = $cur['Key'];
|
494 |
+
$key = $this->formatKey($cur['Key']);
|
495 |
+
$stat = $this->formatUrlStat($cur);
|
496 |
+
}
|
497 |
+
|
498 |
+
// Cache the object data for quick url_stat lookups used with
|
499 |
+
// RecursiveDirectoryIterator.
|
500 |
+
$this->getCacheStorage()->set($key, $stat);
|
501 |
+
$this->objectIterator->next();
|
502 |
+
|
503 |
+
// Remove the prefix from the result to emulate other stream wrappers.
|
504 |
+
return $this->openedBucketPrefix
|
505 |
+
? substr($result, strlen($this->openedBucketPrefix))
|
506 |
+
: $result;
|
507 |
+
}
|
508 |
+
|
509 |
+
private function formatKey($key)
|
510 |
+
{
|
511 |
+
$protocol = explode('://', $this->openedPath)[0];
|
512 |
+
return "{$protocol}://{$this->openedBucket}/{$key}";
|
513 |
+
}
|
514 |
+
|
515 |
+
/**
|
516 |
+
* Called in response to rename() to rename a file or directory. Currently
|
517 |
+
* only supports renaming objects.
|
518 |
+
*
|
519 |
+
* @param string $path_from the path to the file to rename
|
520 |
+
* @param string $path_to the new path to the file
|
521 |
+
*
|
522 |
+
* @return bool true if file was successfully renamed
|
523 |
+
* @link http://www.php.net/manual/en/function.rename.php
|
524 |
+
*/
|
525 |
+
public function rename($path_from, $path_to)
|
526 |
+
{
|
527 |
+
// PHP will not allow rename across wrapper types, so we can safely
|
528 |
+
// assume $path_from and $path_to have the same protocol
|
529 |
+
$this->initProtocol($path_from);
|
530 |
+
$partsFrom = $this->withPath($path_from);
|
531 |
+
$partsTo = $this->withPath($path_to);
|
532 |
+
$this->clearCacheKey($path_from);
|
533 |
+
$this->clearCacheKey($path_to);
|
534 |
+
|
535 |
+
if (!$partsFrom['Key'] || !$partsTo['Key']) {
|
536 |
+
return $this->triggerError('The Amazon S3 stream wrapper only '
|
537 |
+
. 'supports copying objects');
|
538 |
+
}
|
539 |
+
|
540 |
+
return $this->boolCall(function () use ($partsFrom, $partsTo) {
|
541 |
+
$options = $this->getOptions(true);
|
542 |
+
// Copy the object and allow overriding default parameters if
|
543 |
+
// desired, but by default copy metadata
|
544 |
+
$this->getClient()->copy(
|
545 |
+
$partsFrom['Bucket'],
|
546 |
+
$partsFrom['Key'],
|
547 |
+
$partsTo['Bucket'],
|
548 |
+
$partsTo['Key'],
|
549 |
+
isset($options['acl']) ? $options['acl'] : 'private',
|
550 |
+
$options
|
551 |
+
);
|
552 |
+
// Delete the original object
|
553 |
+
$this->getClient()->deleteObject([
|
554 |
+
'Bucket' => $partsFrom['Bucket'],
|
555 |
+
'Key' => $partsFrom['Key']
|
556 |
+
] + $options);
|
557 |
+
return true;
|
558 |
+
});
|
559 |
+
}
|
560 |
+
|
561 |
+
public function stream_cast($cast_as)
|
562 |
+
{
|
563 |
+
return false;
|
564 |
+
}
|
565 |
+
|
566 |
+
/**
|
567 |
+
* Validates the provided stream arguments for fopen and returns an array
|
568 |
+
* of errors.
|
569 |
+
*/
|
570 |
+
private function validate($path, $mode)
|
571 |
+
{
|
572 |
+
$errors = [];
|
573 |
+
|
574 |
+
if (!$this->getOption('Key')) {
|
575 |
+
$errors[] = 'Cannot open a bucket. You must specify a path in the '
|
576 |
+
. 'form of s3://bucket/key';
|
577 |
+
}
|
578 |
+
|
579 |
+
if (!in_array($mode, ['r', 'w', 'a', 'x'])) {
|
580 |
+
$errors[] = "Mode not supported: {$mode}. "
|
581 |
+
. "Use one 'r', 'w', 'a', or 'x'.";
|
582 |
+
}
|
583 |
+
|
584 |
+
// When using mode "x" validate if the file exists before attempting
|
585 |
+
// to read
|
586 |
+
if ($mode == 'x' &&
|
587 |
+
$this->getClient()->doesObjectExist(
|
588 |
+
$this->getOption('Bucket'),
|
589 |
+
$this->getOption('Key'),
|
590 |
+
$this->getOptions(true)
|
591 |
+
)
|
592 |
+
) {
|
593 |
+
$errors[] = "{$path} already exists on Amazon S3";
|
594 |
+
}
|
595 |
+
|
596 |
+
return $errors;
|
597 |
+
}
|
598 |
+
|
599 |
+
/**
|
600 |
+
* Get the stream context options available to the current stream
|
601 |
+
*
|
602 |
+
* @param bool $removeContextData Set to true to remove contextual kvp's
|
603 |
+
* like 'client' from the result.
|
604 |
+
*
|
605 |
+
* @return array
|
606 |
+
*/
|
607 |
+
private function getOptions($removeContextData = false)
|
608 |
+
{
|
609 |
+
// Context is not set when doing things like stat
|
610 |
+
if ($this->context === null) {
|
611 |
+
$options = [];
|
612 |
+
} else {
|
613 |
+
$options = stream_context_get_options($this->context);
|
614 |
+
$options = isset($options[$this->protocol])
|
615 |
+
? $options[$this->protocol]
|
616 |
+
: [];
|
617 |
+
}
|
618 |
+
|
619 |
+
$default = stream_context_get_options(stream_context_get_default());
|
620 |
+
$default = isset($default[$this->protocol])
|
621 |
+
? $default[$this->protocol]
|
622 |
+
: [];
|
623 |
+
$result = $this->params + $options + $default;
|
624 |
+
|
625 |
+
if ($removeContextData) {
|
626 |
+
unset($result['client'], $result['seekable'], $result['cache']);
|
627 |
+
}
|
628 |
+
|
629 |
+
return $result;
|
630 |
+
}
|
631 |
+
|
632 |
+
/**
|
633 |
+
* Get a specific stream context option
|
634 |
+
*
|
635 |
+
* @param string $name Name of the option to retrieve
|
636 |
+
*
|
637 |
+
* @return mixed|null
|
638 |
+
*/
|
639 |
+
private function getOption($name)
|
640 |
+
{
|
641 |
+
$options = $this->getOptions();
|
642 |
+
|
643 |
+
return isset($options[$name]) ? $options[$name] : null;
|
644 |
+
}
|
645 |
+
|
646 |
+
/**
|
647 |
+
* Gets the client from the stream context
|
648 |
+
*
|
649 |
+
* @return S3ClientInterface
|
650 |
+
* @throws \RuntimeException if no client has been configured
|
651 |
+
*/
|
652 |
+
private function getClient()
|
653 |
+
{
|
654 |
+
if (!$client = $this->getOption('client')) {
|
655 |
+
throw new \RuntimeException('No client in stream context');
|
656 |
+
}
|
657 |
+
|
658 |
+
return $client;
|
659 |
+
}
|
660 |
+
|
661 |
+
private function getBucketKey($path)
|
662 |
+
{
|
663 |
+
// Remove the protocol
|
664 |
+
$parts = explode('://', $path);
|
665 |
+
// Get the bucket, key
|
666 |
+
$parts = explode('/', $parts[1], 2);
|
667 |
+
|
668 |
+
return [
|
669 |
+
'Bucket' => $parts[0],
|
670 |
+
'Key' => isset($parts[1]) ? $parts[1] : null
|
671 |
+
];
|
672 |
+
}
|
673 |
+
|
674 |
+
/**
|
675 |
+
* Get the bucket and key from the passed path (e.g. s3://bucket/key)
|
676 |
+
*
|
677 |
+
* @param string $path Path passed to the stream wrapper
|
678 |
+
*
|
679 |
+
* @return array Hash of 'Bucket', 'Key', and custom params from the context
|
680 |
+
*/
|
681 |
+
private function withPath($path)
|
682 |
+
{
|
683 |
+
$params = $this->getOptions(true);
|
684 |
+
|
685 |
+
return $this->getBucketKey($path) + $params;
|
686 |
+
}
|
687 |
+
|
688 |
+
private function openReadStream()
|
689 |
+
{
|
690 |
+
$client = $this->getClient();
|
691 |
+
$command = $client->getCommand('GetObject', $this->getOptions(true));
|
692 |
+
$command['@http']['stream'] = true;
|
693 |
+
$result = $client->execute($command);
|
694 |
+
$this->size = $result['ContentLength'];
|
695 |
+
$this->body = $result['Body'];
|
696 |
+
|
697 |
+
// Wrap the body in a caching entity body if seeking is allowed
|
698 |
+
if ($this->getOption('seekable') && !$this->body->isSeekable()) {
|
699 |
+
$this->body = new CachingStream($this->body);
|
700 |
+
}
|
701 |
+
|
702 |
+
return true;
|
703 |
+
}
|
704 |
+
|
705 |
+
private function openWriteStream()
|
706 |
+
{
|
707 |
+
$this->body = new Stream(fopen('php://temp', 'r+'));
|
708 |
+
return true;
|
709 |
+
}
|
710 |
+
|
711 |
+
private function openAppendStream()
|
712 |
+
{
|
713 |
+
try {
|
714 |
+
// Get the body of the object and seek to the end of the stream
|
715 |
+
$client = $this->getClient();
|
716 |
+
$this->body = $client->getObject($this->getOptions(true))['Body'];
|
717 |
+
$this->body->seek(0, SEEK_END);
|
718 |
+
return true;
|
719 |
+
} catch (S3Exception $e) {
|
720 |
+
// The object does not exist, so use a simple write stream
|
721 |
+
return $this->openWriteStream();
|
722 |
+
}
|
723 |
+
}
|
724 |
+
|
725 |
+
/**
|
726 |
+
* Trigger one or more errors
|
727 |
+
*
|
728 |
+
* @param string|array $errors Errors to trigger
|
729 |
+
* @param mixed $flags If set to STREAM_URL_STAT_QUIET, then no
|
730 |
+
* error or exception occurs
|
731 |
+
*
|
732 |
+
* @return bool Returns false
|
733 |
+
* @throws \RuntimeException if throw_errors is true
|
734 |
+
*/
|
735 |
+
private function triggerError($errors, $flags = null)
|
736 |
+
{
|
737 |
+
// This is triggered with things like file_exists()
|
738 |
+
if ($flags & STREAM_URL_STAT_QUIET) {
|
739 |
+
return $flags & STREAM_URL_STAT_LINK
|
740 |
+
// This is triggered for things like is_link()
|
741 |
+
? $this->formatUrlStat(false)
|
742 |
+
: false;
|
743 |
+
}
|
744 |
+
|
745 |
+
// This is triggered when doing things like lstat() or stat()
|
746 |
+
trigger_error(implode("\n", (array) $errors), E_USER_WARNING);
|
747 |
+
|
748 |
+
return false;
|
749 |
+
}
|
750 |
+
|
751 |
+
/**
|
752 |
+
* Prepare a url_stat result array
|
753 |
+
*
|
754 |
+
* @param string|array $result Data to add
|
755 |
+
*
|
756 |
+
* @return array Returns the modified url_stat result
|
757 |
+
*/
|
758 |
+
private function formatUrlStat($result = null)
|
759 |
+
{
|
760 |
+
$stat = $this->getStatTemplate();
|
761 |
+
switch (gettype($result)) {
|
762 |
+
case 'NULL':
|
763 |
+
case 'string':
|
764 |
+
// Directory with 0777 access - see "man 2 stat".
|
765 |
+
$stat['mode'] = $stat[2] = 0040777;
|
766 |
+
break;
|
767 |
+
case 'array':
|
768 |
+
// Regular file with 0777 access - see "man 2 stat".
|
769 |
+
$stat['mode'] = $stat[2] = 0100777;
|
770 |
+
// Pluck the content-length if available.
|
771 |
+
if (isset($result['ContentLength'])) {
|
772 |
+
$stat['size'] = $stat[7] = $result['ContentLength'];
|
773 |
+
} elseif (isset($result['Size'])) {
|
774 |
+
$stat['size'] = $stat[7] = $result['Size'];
|
775 |
+
}
|
776 |
+
if (isset($result['LastModified'])) {
|
777 |
+
// ListObjects or HeadObject result
|
778 |
+
$stat['mtime'] = $stat[9] = $stat['ctime'] = $stat[10]
|
779 |
+
= strtotime($result['LastModified']);
|
780 |
+
}
|
781 |
+
}
|
782 |
+
|
783 |
+
return $stat;
|
784 |
+
}
|
785 |
+
|
786 |
+
/**
|
787 |
+
* Creates a bucket for the given parameters.
|
788 |
+
*
|
789 |
+
* @param string $path Stream wrapper path
|
790 |
+
* @param array $params A result of StreamWrapper::withPath()
|
791 |
+
*
|
792 |
+
* @return bool Returns true on success or false on failure
|
793 |
+
*/
|
794 |
+
private function createBucket($path, array $params)
|
795 |
+
{
|
796 |
+
if ($this->getClient()->doesBucketExist($params['Bucket'])) {
|
797 |
+
return $this->triggerError("Bucket already exists: {$path}");
|
798 |
+
}
|
799 |
+
|
800 |
+
return $this->boolCall(function () use ($params, $path) {
|
801 |
+
$this->getClient()->createBucket($params);
|
802 |
+
$this->clearCacheKey($path);
|
803 |
+
return true;
|
804 |
+
});
|
805 |
+
}
|
806 |
+
|
807 |
+
/**
|
808 |
+
* Creates a pseudo-folder by creating an empty "/" suffixed key
|
809 |
+
*
|
810 |
+
* @param string $path Stream wrapper path
|
811 |
+
* @param array $params A result of StreamWrapper::withPath()
|
812 |
+
*
|
813 |
+
* @return bool
|
814 |
+
*/
|
815 |
+
private function createSubfolder($path, array $params)
|
816 |
+
{
|
817 |
+
// Ensure the path ends in "/" and the body is empty.
|
818 |
+
$params['Key'] = rtrim($params['Key'], '/') . '/';
|
819 |
+
$params['Body'] = '';
|
820 |
+
|
821 |
+
// Fail if this pseudo directory key already exists
|
822 |
+
if ($this->getClient()->doesObjectExist(
|
823 |
+
$params['Bucket'],
|
824 |
+
$params['Key'])
|
825 |
+
) {
|
826 |
+
return $this->triggerError("Subfolder already exists: {$path}");
|
827 |
+
}
|
828 |
+
|
829 |
+
return $this->boolCall(function () use ($params, $path) {
|
830 |
+
$this->getClient()->putObject($params);
|
831 |
+
$this->clearCacheKey($path);
|
832 |
+
return true;
|
833 |
+
});
|
834 |
+
}
|
835 |
+
|
836 |
+
/**
|
837 |
+
* Deletes a nested subfolder if it is empty.
|
838 |
+
*
|
839 |
+
* @param string $path Path that is being deleted (e.g., 's3://a/b/c')
|
840 |
+
* @param array $params A result of StreamWrapper::withPath()
|
841 |
+
*
|
842 |
+
* @return bool
|
843 |
+
*/
|
844 |
+
private function deleteSubfolder($path, $params)
|
845 |
+
{
|
846 |
+
// Use a key that adds a trailing slash if needed.
|
847 |
+
$prefix = rtrim($params['Key'], '/') . '/';
|
848 |
+
$result = $this->getClient()->listObjects([
|
849 |
+
'Bucket' => $params['Bucket'],
|
850 |
+
'Prefix' => $prefix,
|
851 |
+
'MaxKeys' => 1
|
852 |
+
]);
|
853 |
+
|
854 |
+
// Check if the bucket contains keys other than the placeholder
|
855 |
+
if ($contents = $result['Contents']) {
|
856 |
+
return (count($contents) > 1 || $contents[0]['Key'] != $prefix)
|
857 |
+
? $this->triggerError('Subfolder is not empty')
|
858 |
+
: $this->unlink(rtrim($path, '/') . '/');
|
859 |
+
}
|
860 |
+
|
861 |
+
return $result['CommonPrefixes']
|
862 |
+
? $this->triggerError('Subfolder contains nested folders')
|
863 |
+
: true;
|
864 |
+
}
|
865 |
+
|
866 |
+
/**
|
867 |
+
* Determine the most appropriate ACL based on a file mode.
|
868 |
+
*
|
869 |
+
* @param int $mode File mode
|
870 |
+
*
|
871 |
+
* @return string
|
872 |
+
*/
|
873 |
+
private function determineAcl($mode)
|
874 |
+
{
|
875 |
+
switch (substr(decoct($mode), 0, 1)) {
|
876 |
+
case '7': return 'public-read';
|
877 |
+
case '6': return 'authenticated-read';
|
878 |
+
default: return 'private';
|
879 |
+
}
|
880 |
+
}
|
881 |
+
|
882 |
+
/**
|
883 |
+
* Gets a URL stat template with default values
|
884 |
+
*
|
885 |
+
* @return array
|
886 |
+
*/
|
887 |
+
private function getStatTemplate()
|
888 |
+
{
|
889 |
+
return [
|
890 |
+
0 => 0, 'dev' => 0,
|
891 |
+
1 => 0, 'ino' => 0,
|
892 |
+
2 => 0, 'mode' => 0,
|
893 |
+
3 => 0, 'nlink' => 0,
|
894 |
+
4 => 0, 'uid' => 0,
|
895 |
+
5 => 0, 'gid' => 0,
|
896 |
+
6 => -1, 'rdev' => -1,
|
897 |
+
7 => 0, 'size' => 0,
|
898 |
+
8 => 0, 'atime' => 0,
|
899 |
+
9 => 0, 'mtime' => 0,
|
900 |
+
10 => 0, 'ctime' => 0,
|
901 |
+
11 => -1, 'blksize' => -1,
|
902 |
+
12 => -1, 'blocks' => -1,
|
903 |
+
];
|
904 |
+
}
|
905 |
+
|
906 |
+
/**
|
907 |
+
* Invokes a callable and triggers an error if an exception occurs while
|
908 |
+
* calling the function.
|
909 |
+
*
|
910 |
+
* @param callable $fn
|
911 |
+
* @param int $flags
|
912 |
+
*
|
913 |
+
* @return bool
|
914 |
+
*/
|
915 |
+
private function boolCall(callable $fn, $flags = null)
|
916 |
+
{
|
917 |
+
try {
|
918 |
+
return $fn();
|
919 |
+
} catch (\Exception $e) {
|
920 |
+
return $this->triggerError($e->getMessage(), $flags);
|
921 |
+
}
|
922 |
+
}
|
923 |
+
|
924 |
+
/**
|
925 |
+
* @return LruArrayCache
|
926 |
+
*/
|
927 |
+
private function getCacheStorage()
|
928 |
+
{
|
929 |
+
if (!$this->cache) {
|
930 |
+
$this->cache = $this->getOption('cache') ?: new LruArrayCache();
|
931 |
+
}
|
932 |
+
|
933 |
+
return $this->cache;
|
934 |
+
}
|
935 |
+
|
936 |
+
/**
|
937 |
+
* Clears a specific stat cache value from the stat cache and LRU cache.
|
938 |
+
*
|
939 |
+
* @param string $key S3 path (s3://bucket/key).
|
940 |
+
*/
|
941 |
+
private function clearCacheKey($key)
|
942 |
+
{
|
943 |
+
clearstatcache(true, $key);
|
944 |
+
$this->getCacheStorage()->remove($key);
|
945 |
+
}
|
946 |
+
|
947 |
+
/**
|
948 |
+
* Returns the size of the opened object body.
|
949 |
+
*
|
950 |
+
* @return int|null
|
951 |
+
*/
|
952 |
+
private function getSize()
|
953 |
+
{
|
954 |
+
$size = $this->body->getSize();
|
955 |
+
|
956 |
+
return $size !== null ? $size : $this->size;
|
957 |
+
}
|
958 |
+
}
|
lib/Aws/Aws/S3/Transfer.php
ADDED
@@ -0,0 +1,428 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\S3;
|
3 |
+
|
4 |
+
use Aws;
|
5 |
+
use Aws\CommandInterface;
|
6 |
+
use Aws\Exception\AwsException;
|
7 |
+
use GuzzleHttp\Promise;
|
8 |
+
use GuzzleHttp\Promise\PromisorInterface;
|
9 |
+
use Iterator;
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Transfers files from the local filesystem to S3 or from S3 to the local
|
13 |
+
* filesystem.
|
14 |
+
*
|
15 |
+
* This class does not support copying from the local filesystem to somewhere
|
16 |
+
* else on the local filesystem or from one S3 bucket to another.
|
17 |
+
*/
|
18 |
+
class Transfer implements PromisorInterface
|
19 |
+
{
|
20 |
+
private $client;
|
21 |
+
private $promise;
|
22 |
+
private $source;
|
23 |
+
private $sourceMetadata;
|
24 |
+
private $destination;
|
25 |
+
private $concurrency;
|
26 |
+
private $mupThreshold;
|
27 |
+
private $before;
|
28 |
+
private $s3Args = [];
|
29 |
+
|
30 |
+
/**
|
31 |
+
* When providing the $source argument, you may provide a string referencing
|
32 |
+
* the path to a directory on disk to upload, an s3 scheme URI that contains
|
33 |
+
* the bucket and key (e.g., "s3://bucket/key"), or an \Iterator object
|
34 |
+
* that yields strings containing filenames that are the path to a file on
|
35 |
+
* disk or an s3 scheme URI. The "/key" portion of an s3 URI is optional.
|
36 |
+
*
|
37 |
+
* When providing an iterator for the $source argument, you must also
|
38 |
+
* provide a 'base_dir' key value pair in the $options argument.
|
39 |
+
*
|
40 |
+
* The $dest argument can be the path to a directory on disk or an s3
|
41 |
+
* scheme URI (e.g., "s3://bucket/key").
|
42 |
+
*
|
43 |
+
* The options array can contain the following key value pairs:
|
44 |
+
*
|
45 |
+
* - base_dir: (string) Base directory of the source, if $source is an
|
46 |
+
* iterator. If the $source option is not an array, then this option is
|
47 |
+
* ignored.
|
48 |
+
* - before: (callable) A callback to invoke before each transfer. The
|
49 |
+
* callback accepts a single argument: Aws\CommandInterface $command.
|
50 |
+
* The provided command will be either a GetObject, PutObject,
|
51 |
+
* InitiateMultipartUpload, or UploadPart command.
|
52 |
+
* - mup_threshold: (int) Size in bytes in which a multipart upload should
|
53 |
+
* be used instead of PutObject. Defaults to 20971520 (20 MB).
|
54 |
+
* - concurrency: (int, default=5) Number of files to upload concurrently.
|
55 |
+
* The ideal concurrency value will vary based on the number of files
|
56 |
+
* being uploaded and the average size of each file. Generally speaking,
|
57 |
+
* smaller files benefit from a higher concurrency while larger files
|
58 |
+
* will not.
|
59 |
+
* - debug: (bool) Set to true to print out debug information for
|
60 |
+
* transfers. Set to an fopen() resource to write to a specific stream
|
61 |
+
* rather than writing to STDOUT.
|
62 |
+
*
|
63 |
+
* @param S3ClientInterface $client Client used for transfers.
|
64 |
+
* @param string|Iterator $source Where the files are transferred from.
|
65 |
+
* @param string $dest Where the files are transferred to.
|
66 |
+
* @param array $options Hash of options.
|
67 |
+
*/
|
68 |
+
public function __construct(
|
69 |
+
S3ClientInterface $client,
|
70 |
+
$source,
|
71 |
+
$dest,
|
72 |
+
array $options = []
|
73 |
+
) {
|
74 |
+
$this->client = $client;
|
75 |
+
|
76 |
+
// Prepare the destination.
|
77 |
+
$this->destination = $this->prepareTarget($dest);
|
78 |
+
if ($this->destination['scheme'] === 's3') {
|
79 |
+
$this->s3Args = $this->getS3Args($this->destination['path']);
|
80 |
+
}
|
81 |
+
|
82 |
+
// Prepare the source.
|
83 |
+
if (is_string($source)) {
|
84 |
+
$this->sourceMetadata = $this->prepareTarget($source);
|
85 |
+
$this->source = $source;
|
86 |
+
} elseif ($source instanceof Iterator) {
|
87 |
+
if (empty($options['base_dir'])) {
|
88 |
+
throw new \InvalidArgumentException('You must provide the source'
|
89 |
+
. ' argument as a string or provide the "base_dir" option.');
|
90 |
+
}
|
91 |
+
|
92 |
+
$this->sourceMetadata = $this->prepareTarget($options['base_dir']);
|
93 |
+
$this->source = $source;
|
94 |
+
} else {
|
95 |
+
throw new \InvalidArgumentException('source must be the path to a '
|
96 |
+
. 'directory or an iterator that yields file names.');
|
97 |
+
}
|
98 |
+
|
99 |
+
// Validate schemes.
|
100 |
+
if ($this->sourceMetadata['scheme'] === $this->destination['scheme']) {
|
101 |
+
throw new \InvalidArgumentException("You cannot copy from"
|
102 |
+
. " {$this->sourceMetadata['scheme']} to"
|
103 |
+
. " {$this->destination['scheme']}."
|
104 |
+
);
|
105 |
+
}
|
106 |
+
|
107 |
+
// Handle multipart-related options.
|
108 |
+
$this->concurrency = isset($options['concurrency'])
|
109 |
+
? $options['concurrency']
|
110 |
+
: MultipartUploader::DEFAULT_CONCURRENCY;
|
111 |
+
$this->mupThreshold = isset($options['mup_threshold'])
|
112 |
+
? $options['mup_threshold']
|
113 |
+
: 16777216;
|
114 |
+
if ($this->mupThreshold < MultipartUploader::PART_MIN_SIZE) {
|
115 |
+
throw new \InvalidArgumentException('mup_threshold must be >= 5MB');
|
116 |
+
}
|
117 |
+
|
118 |
+
// Handle "before" callback option.
|
119 |
+
if (isset($options['before'])) {
|
120 |
+
$this->before = $options['before'];
|
121 |
+
if (!is_callable($this->before)) {
|
122 |
+
throw new \InvalidArgumentException('before must be a callable.');
|
123 |
+
}
|
124 |
+
}
|
125 |
+
|
126 |
+
// Handle "debug" option.
|
127 |
+
if (isset($options['debug'])) {
|
128 |
+
if ($options['debug'] === true) {
|
129 |
+
$options['debug'] = fopen('php://output', 'w');
|
130 |
+
}
|
131 |
+
$this->addDebugToBefore($options['debug']);
|
132 |
+
}
|
133 |
+
}
|
134 |
+
|
135 |
+
/**
|
136 |
+
* Transfers the files.
|
137 |
+
*/
|
138 |
+
public function promise()
|
139 |
+
{
|
140 |
+
// If the promise has been created, just return it.
|
141 |
+
if (!$this->promise) {
|
142 |
+
// Create an upload/download promise for the transfer.
|
143 |
+
$this->promise = $this->sourceMetadata['scheme'] === 'file'
|
144 |
+
? $this->createUploadPromise()
|
145 |
+
: $this->createDownloadPromise();
|
146 |
+
}
|
147 |
+
|
148 |
+
return $this->promise;
|
149 |
+
}
|
150 |
+
|
151 |
+
/**
|
152 |
+
* Transfers the files synchronously.
|
153 |
+
*/
|
154 |
+
public function transfer()
|
155 |
+
{
|
156 |
+
$this->promise()->wait();
|
157 |
+
}
|
158 |
+
|
159 |
+
private function prepareTarget($targetPath)
|
160 |
+
{
|
161 |
+
$target = [
|
162 |
+
'path' => $this->normalizePath($targetPath),
|
163 |
+
'scheme' => $this->determineScheme($targetPath),
|
164 |
+
];
|
165 |
+
|
166 |
+
if ($target['scheme'] !== 's3' && $target['scheme'] !== 'file') {
|
167 |
+
throw new \InvalidArgumentException('Scheme must be "s3" or "file".');
|
168 |
+
}
|
169 |
+
|
170 |
+
return $target;
|
171 |
+
}
|
172 |
+
|
173 |
+
/**
|
174 |
+
* Creates an array that contains Bucket and Key by parsing the filename.
|
175 |
+
*
|
176 |
+
* @param string $path Path to parse.
|
177 |
+
*
|
178 |
+
* @return array
|
179 |
+
*/
|
180 |
+
private function getS3Args($path)
|
181 |
+
{
|
182 |
+
$parts = explode('/', str_replace('s3://', '', $path), 2);
|
183 |
+
$args = ['Bucket' => $parts[0]];
|
184 |
+
if (isset($parts[1])) {
|
185 |
+
$args['Key'] = $parts[1];
|
186 |
+
}
|
187 |
+
|
188 |
+
return $args;
|
189 |
+
}
|
190 |
+
|
191 |
+
/**
|
192 |
+
* Parses the scheme from a filename.
|
193 |
+
*
|
194 |
+
* @param string $path Path to parse.
|
195 |
+
*
|
196 |
+
* @return string
|
197 |
+
*/
|
198 |
+
private function determineScheme($path)
|
199 |
+
{
|
200 |
+
return !strpos($path, '://') ? 'file' : explode('://', $path)[0];
|
201 |
+
}
|
202 |
+
|
203 |
+
/**
|
204 |
+
* Normalize a path so that it has UNIX-style directory separators and no trailing /
|
205 |
+
*
|
206 |
+
* @param string $path
|
207 |
+
*
|
208 |
+
* @return string
|
209 |
+
*/
|
210 |
+
private function normalizePath($path)
|
211 |
+
{
|
212 |
+
return rtrim(str_replace('\\', '/', $path), '/');
|
213 |
+
}
|
214 |
+
|
215 |
+
private function resolveUri($uri)
|
216 |
+
{
|
217 |
+
$resolved = [];
|
218 |
+
$sections = explode('/', $uri);
|
219 |
+
foreach ($sections as $section) {
|
220 |
+
if ($section === '.' || $section === '') {
|
221 |
+
continue;
|
222 |
+
}
|
223 |
+
if ($section === '..') {
|
224 |
+
array_pop($resolved);
|
225 |
+
} else {
|
226 |
+
$resolved []= $section;
|
227 |
+
}
|
228 |
+
}
|
229 |
+
|
230 |
+
return ($uri[0] === '/' ? '/' : '')
|
231 |
+
. implode('/', $resolved);
|
232 |
+
}
|
233 |
+
|
234 |
+
private function createDownloadPromise()
|
235 |
+
{
|
236 |
+
$parts = $this->getS3Args($this->sourceMetadata['path']);
|
237 |
+
$prefix = "s3://{$parts['Bucket']}/"
|
238 |
+
. (isset($parts['Key']) ? $parts['Key'] . '/' : '');
|
239 |
+
|
240 |
+
|
241 |
+
$commands = [];
|
242 |
+
foreach ($this->getDownloadsIterator() as $object) {
|
243 |
+
// Prepare the sink.
|
244 |
+
$objectKey = preg_replace('/^' . preg_quote($prefix, '/') . '/', '', $object);
|
245 |
+
|
246 |
+
$resolveSink = $this->destination['path'] . '/';
|
247 |
+
if (isset($parts['Key']) && strpos($objectKey, $parts['Key']) !== 0) {
|
248 |
+
$resolveSink .= $parts['Key'] . '/';
|
249 |
+
}
|
250 |
+
$resolveSink .= $objectKey;
|
251 |
+
$sink = $this->destination['path'] . '/' . $objectKey;
|
252 |
+
|
253 |
+
$command = $this->client->getCommand(
|
254 |
+
'GetObject',
|
255 |
+
$this->getS3Args($object) + ['@http' => ['sink' => $sink]]
|
256 |
+
);
|
257 |
+
|
258 |
+
if (strpos(
|
259 |
+
$this->resolveUri($resolveSink),
|
260 |
+
$this->destination['path']
|
261 |
+
) !== 0
|
262 |
+
) {
|
263 |
+
throw new AwsException(
|
264 |
+
'Cannot download key ' . $objectKey
|
265 |
+
. ', its relative path resolves outside the'
|
266 |
+
. ' parent directory', $command);
|
267 |
+
}
|
268 |
+
|
269 |
+
// Create the directory if needed.
|
270 |
+
$dir = dirname($sink);
|
271 |
+
if (!is_dir($dir) && !mkdir($dir, 0777, true)) {
|
272 |
+
throw new \RuntimeException("Could not create dir: {$dir}");
|
273 |
+
}
|
274 |
+
|
275 |
+
// Create the command.
|
276 |
+
$commands []= $command;
|
277 |
+
}
|
278 |
+
|
279 |
+
// Create a GetObject command pool and return the promise.
|
280 |
+
return (new Aws\CommandPool($this->client, $commands, [
|
281 |
+
'concurrency' => $this->concurrency,
|
282 |
+
'before' => $this->before,
|
283 |
+
'rejected' => function ($reason, $idx, Promise\PromiseInterface $p) {
|
284 |
+
$p->reject($reason);
|
285 |
+
}
|
286 |
+
]))->promise();
|
287 |
+
}
|
288 |
+
|
289 |
+
private function createUploadPromise()
|
290 |
+
{
|
291 |
+
// Map each file into a promise that performs the actual transfer.
|
292 |
+
$files = \Aws\map($this->getUploadsIterator(), function ($file) {
|
293 |
+
return (filesize($file) >= $this->mupThreshold)
|
294 |
+
? $this->uploadMultipart($file)
|
295 |
+
: $this->upload($file);
|
296 |
+
});
|
297 |
+
|
298 |
+
// Create an EachPromise, that will concurrently handle the upload
|
299 |
+
// operations' yielded promises from the iterator.
|
300 |
+
return Promise\each_limit_all($files, $this->concurrency);
|
301 |
+
}
|
302 |
+
|
303 |
+
/** @return Iterator */
|
304 |
+
private function getUploadsIterator()
|
305 |
+
{
|
306 |
+
if (is_string($this->source)) {
|
307 |
+
return Aws\filter(
|
308 |
+
Aws\recursive_dir_iterator($this->sourceMetadata['path']),
|
309 |
+
function ($file) { return !is_dir($file); }
|
310 |
+
);
|
311 |
+
}
|
312 |
+
|
313 |
+
return $this->source;
|
314 |
+
}
|
315 |
+
|
316 |
+
/** @return Iterator */
|
317 |
+
private function getDownloadsIterator()
|
318 |
+
{
|
319 |
+
if (is_string($this->source)) {
|
320 |
+
$listArgs = $this->getS3Args($this->sourceMetadata['path']);
|
321 |
+
if (isset($listArgs['Key'])) {
|
322 |
+
$listArgs['Prefix'] = $listArgs['Key'] . '/';
|
323 |
+
unset($listArgs['Key']);
|
324 |
+
}
|
325 |
+
|
326 |
+
$files = $this->client
|
327 |
+
->getPaginator('ListObjects', $listArgs)
|
328 |
+
->search('Contents[].Key');
|
329 |
+
$files = Aws\map($files, function ($key) use ($listArgs) {
|
330 |
+
return "s3://{$listArgs['Bucket']}/$key";
|
331 |
+
});
|
332 |
+
return Aws\filter($files, function ($key) {
|
333 |
+
return substr($key, -1, 1) !== '/';
|
334 |
+
});
|
335 |
+
}
|
336 |
+
|
337 |
+
return $this->source;
|
338 |
+
}
|
339 |
+
|
340 |
+
private function upload($filename)
|
341 |
+
{
|
342 |
+
$args = $this->s3Args;
|
343 |
+
$args['SourceFile'] = $filename;
|
344 |
+
$args['Key'] = $this->createS3Key($filename);
|
345 |
+
$command = $this->client->getCommand('PutObject', $args);
|
346 |
+
$this->before and call_user_func($this->before, $command);
|
347 |
+
|
348 |
+
return $this->client->executeAsync($command);
|
349 |
+
}
|
350 |
+
|
351 |
+
private function uploadMultipart($filename)
|
352 |
+
{
|
353 |
+
$args = $this->s3Args;
|
354 |
+
$args['Key'] = $this->createS3Key($filename);
|
355 |
+
|
356 |
+
return (new MultipartUploader($this->client, $filename, [
|
357 |
+
'bucket' => $args['Bucket'],
|
358 |
+
'key' => $args['Key'],
|
359 |
+
'before_initiate' => $this->before,
|
360 |
+
'before_upload' => $this->before,
|
361 |
+
'before_complete' => $this->before,
|
362 |
+
'concurrency' => $this->concurrency,
|
363 |
+
]))->promise();
|
364 |
+
}
|
365 |
+
|
366 |
+
private function createS3Key($filename)
|
367 |
+
{
|
368 |
+
$filename = $this->normalizePath($filename);
|
369 |
+
$relative_file_path = ltrim(
|
370 |
+
preg_replace('#^' . preg_quote($this->sourceMetadata['path']) . '#', '', $filename),
|
371 |
+
'/\\'
|
372 |
+
);
|
373 |
+
|
374 |
+
if (isset($this->s3Args['Key'])) {
|
375 |
+
return rtrim($this->s3Args['Key'], '/').'/'.$relative_file_path;
|
376 |
+
}
|
377 |
+
|
378 |
+
return $relative_file_path;
|
379 |
+
}
|
380 |
+
|
381 |
+
private function addDebugToBefore($debug)
|
382 |
+
{
|
383 |
+
$before = $this->before;
|
384 |
+
$sourcePath = $this->sourceMetadata['path'];
|
385 |
+
$s3Args = $this->s3Args;
|
386 |
+
|
387 |
+
$this->before = static function (
|
388 |
+
CommandInterface $command
|
389 |
+
) use ($before, $debug, $sourcePath, $s3Args) {
|
390 |
+
// Call the composed before function.
|
391 |
+
$before and $before($command);
|
392 |
+
|
393 |
+
// Determine the source and dest values based on operation.
|
394 |
+
switch ($operation = $command->getName()) {
|
395 |
+
case 'GetObject':
|
396 |
+
$source = "s3://{$command['Bucket']}/{$command['Key']}";
|
397 |
+
$dest = $command['@http']['sink'];
|
398 |
+
break;
|
399 |
+
case 'PutObject':
|
400 |
+
$source = $command['SourceFile'];
|
401 |
+
$dest = "s3://{$command['Bucket']}/{$command['Key']}";
|
402 |
+
break;
|
403 |
+
case 'UploadPart':
|
404 |
+
$part = $command['PartNumber'];
|
405 |
+
case 'CreateMultipartUpload':
|
406 |
+
case 'CompleteMultipartUpload':
|
407 |
+
$sourceKey = $command['Key'];
|
408 |
+
if (isset($s3Args['Key']) && strpos($sourceKey, $s3Args['Key']) === 0) {
|
409 |
+
$sourceKey = substr($sourceKey, strlen($s3Args['Key']) + 1);
|
410 |
+
}
|
411 |
+
$source = "{$sourcePath}/{$sourceKey}";
|
412 |
+
$dest = "s3://{$command['Bucket']}/{$command['Key']}";
|
413 |
+
break;
|
414 |
+
default:
|
415 |
+
throw new \UnexpectedValueException(
|
416 |
+
"Transfer encountered an unexpected operation: {$operation}."
|
417 |
+
);
|
418 |
+
}
|
419 |
+
|
420 |
+
// Print the debugging message.
|
421 |
+
$context = sprintf('%s -> %s (%s)', $source, $dest, $operation);
|
422 |
+
if (isset($part)) {
|
423 |
+
$context .= " : Part={$part}";
|
424 |
+
}
|
425 |
+
fwrite($debug, "Transferring {$context}\n");
|
426 |
+
};
|
427 |
+
}
|
428 |
+
}
|
lib/Aws/Aws/Sdk.php
ADDED
@@ -0,0 +1,466 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Builds AWS clients based on configuration settings.
|
6 |
+
*
|
7 |
+
* @method \Aws\ACMPCA\ACMPCAClient createACMPCA(array $args = [])
|
8 |
+
* @method \Aws\MultiRegionClient createMultiRegionACMPCA(array $args = [])
|
9 |
+
* @method \Aws\Acm\AcmClient createAcm(array $args = [])
|
10 |
+
* @method \Aws\MultiRegionClient createMultiRegionAcm(array $args = [])
|
11 |
+
* @method \Aws\AlexaForBusiness\AlexaForBusinessClient createAlexaForBusiness(array $args = [])
|
12 |
+
* @method \Aws\MultiRegionClient createMultiRegionAlexaForBusiness(array $args = [])
|
13 |
+
* @method \Aws\Amplify\AmplifyClient createAmplify(array $args = [])
|
14 |
+
* @method \Aws\MultiRegionClient createMultiRegionAmplify(array $args = [])
|
15 |
+
* @method \Aws\ApiGateway\ApiGatewayClient createApiGateway(array $args = [])
|
16 |
+
* @method \Aws\MultiRegionClient createMultiRegionApiGateway(array $args = [])
|
17 |
+
* @method \Aws\ApiGatewayManagementApi\ApiGatewayManagementApiClient createApiGatewayManagementApi(array $args = [])
|
18 |
+
* @method \Aws\MultiRegionClient createMultiRegionApiGatewayManagementApi(array $args = [])
|
19 |
+
* @method \Aws\ApiGatewayV2\ApiGatewayV2Client createApiGatewayV2(array $args = [])
|
20 |
+
* @method \Aws\MultiRegionClient createMultiRegionApiGatewayV2(array $args = [])
|
21 |
+
* @method \Aws\AppMesh\AppMeshClient createAppMesh(array $args = [])
|
22 |
+
* @method \Aws\MultiRegionClient createMultiRegionAppMesh(array $args = [])
|
23 |
+
* @method \Aws\AppSync\AppSyncClient createAppSync(array $args = [])
|
24 |
+
* @method \Aws\MultiRegionClient createMultiRegionAppSync(array $args = [])
|
25 |
+
* @method \Aws\ApplicationAutoScaling\ApplicationAutoScalingClient createApplicationAutoScaling(array $args = [])
|
26 |
+
* @method \Aws\MultiRegionClient createMultiRegionApplicationAutoScaling(array $args = [])
|
27 |
+
* @method \Aws\ApplicationDiscoveryService\ApplicationDiscoveryServiceClient createApplicationDiscoveryService(array $args = [])
|
28 |
+
* @method \Aws\MultiRegionClient createMultiRegionApplicationDiscoveryService(array $args = [])
|
29 |
+
* @method \Aws\Appstream\AppstreamClient createAppstream(array $args = [])
|
30 |
+
* @method \Aws\MultiRegionClient createMultiRegionAppstream(array $args = [])
|
31 |
+
* @method \Aws\Athena\AthenaClient createAthena(array $args = [])
|
32 |
+
* @method \Aws\MultiRegionClient createMultiRegionAthena(array $args = [])
|
33 |
+
* @method \Aws\AutoScaling\AutoScalingClient createAutoScaling(array $args = [])
|
34 |
+
* @method \Aws\MultiRegionClient createMultiRegionAutoScaling(array $args = [])
|
35 |
+
* @method \Aws\AutoScalingPlans\AutoScalingPlansClient createAutoScalingPlans(array $args = [])
|
36 |
+
* @method \Aws\MultiRegionClient createMultiRegionAutoScalingPlans(array $args = [])
|
37 |
+
* @method \Aws\Backup\BackupClient createBackup(array $args = [])
|
38 |
+
* @method \Aws\MultiRegionClient createMultiRegionBackup(array $args = [])
|
39 |
+
* @method \Aws\Batch\BatchClient createBatch(array $args = [])
|
40 |
+
* @method \Aws\MultiRegionClient createMultiRegionBatch(array $args = [])
|
41 |
+
* @method \Aws\Budgets\BudgetsClient createBudgets(array $args = [])
|
42 |
+
* @method \Aws\MultiRegionClient createMultiRegionBudgets(array $args = [])
|
43 |
+
* @method \Aws\Chime\ChimeClient createChime(array $args = [])
|
44 |
+
* @method \Aws\MultiRegionClient createMultiRegionChime(array $args = [])
|
45 |
+
* @method \Aws\Cloud9\Cloud9Client createCloud9(array $args = [])
|
46 |
+
* @method \Aws\MultiRegionClient createMultiRegionCloud9(array $args = [])
|
47 |
+
* @method \Aws\CloudDirectory\CloudDirectoryClient createCloudDirectory(array $args = [])
|
48 |
+
* @method \Aws\MultiRegionClient createMultiRegionCloudDirectory(array $args = [])
|
49 |
+
* @method \Aws\CloudFormation\CloudFormationClient createCloudFormation(array $args = [])
|
50 |
+
* @method \Aws\MultiRegionClient createMultiRegionCloudFormation(array $args = [])
|
51 |
+
* @method \Aws\CloudFront\CloudFrontClient createCloudFront(array $args = [])
|
52 |
+
* @method \Aws\MultiRegionClient createMultiRegionCloudFront(array $args = [])
|
53 |
+
* @method \Aws\CloudHSMV2\CloudHSMV2Client createCloudHSMV2(array $args = [])
|
54 |
+
* @method \Aws\MultiRegionClient createMultiRegionCloudHSMV2(array $args = [])
|
55 |
+
* @method \Aws\CloudHsm\CloudHsmClient createCloudHsm(array $args = [])
|
56 |
+
* @method \Aws\MultiRegionClient createMultiRegionCloudHsm(array $args = [])
|
57 |
+
* @method \Aws\CloudSearch\CloudSearchClient createCloudSearch(array $args = [])
|
58 |
+
* @method \Aws\MultiRegionClient createMultiRegionCloudSearch(array $args = [])
|
59 |
+
* @method \Aws\CloudSearchDomain\CloudSearchDomainClient createCloudSearchDomain(array $args = [])
|
60 |
+
* @method \Aws\MultiRegionClient createMultiRegionCloudSearchDomain(array $args = [])
|
61 |
+
* @method \Aws\CloudTrail\CloudTrailClient createCloudTrail(array $args = [])
|
62 |
+
* @method \Aws\MultiRegionClient createMultiRegionCloudTrail(array $args = [])
|
63 |
+
* @method \Aws\CloudWatch\CloudWatchClient createCloudWatch(array $args = [])
|
64 |
+
* @method \Aws\MultiRegionClient createMultiRegionCloudWatch(array $args = [])
|
65 |
+
* @method \Aws\CloudWatchEvents\CloudWatchEventsClient createCloudWatchEvents(array $args = [])
|
66 |
+
* @method \Aws\MultiRegionClient createMultiRegionCloudWatchEvents(array $args = [])
|
67 |
+
* @method \Aws\CloudWatchLogs\CloudWatchLogsClient createCloudWatchLogs(array $args = [])
|
68 |
+
* @method \Aws\MultiRegionClient createMultiRegionCloudWatchLogs(array $args = [])
|
69 |
+
* @method \Aws\CodeBuild\CodeBuildClient createCodeBuild(array $args = [])
|
70 |
+
* @method \Aws\MultiRegionClient createMultiRegionCodeBuild(array $args = [])
|
71 |
+
* @method \Aws\CodeCommit\CodeCommitClient createCodeCommit(array $args = [])
|
72 |
+
* @method \Aws\MultiRegionClient createMultiRegionCodeCommit(array $args = [])
|
73 |
+
* @method \Aws\CodeDeploy\CodeDeployClient createCodeDeploy(array $args = [])
|
74 |
+
* @method \Aws\MultiRegionClient createMultiRegionCodeDeploy(array $args = [])
|
75 |
+
* @method \Aws\CodePipeline\CodePipelineClient createCodePipeline(array $args = [])
|
76 |
+
* @method \Aws\MultiRegionClient createMultiRegionCodePipeline(array $args = [])
|
77 |
+
* @method \Aws\CodeStar\CodeStarClient createCodeStar(array $args = [])
|
78 |
+
* @method \Aws\MultiRegionClient createMultiRegionCodeStar(array $args = [])
|
79 |
+
* @method \Aws\CognitoIdentity\CognitoIdentityClient createCognitoIdentity(array $args = [])
|
80 |
+
* @method \Aws\MultiRegionClient createMultiRegionCognitoIdentity(array $args = [])
|
81 |
+
* @method \Aws\CognitoIdentityProvider\CognitoIdentityProviderClient createCognitoIdentityProvider(array $args = [])
|
82 |
+
* @method \Aws\MultiRegionClient createMultiRegionCognitoIdentityProvider(array $args = [])
|
83 |
+
* @method \Aws\CognitoSync\CognitoSyncClient createCognitoSync(array $args = [])
|
84 |
+
* @method \Aws\MultiRegionClient createMultiRegionCognitoSync(array $args = [])
|
85 |
+
* @method \Aws\Comprehend\ComprehendClient createComprehend(array $args = [])
|
86 |
+
* @method \Aws\MultiRegionClient createMultiRegionComprehend(array $args = [])
|
87 |
+
* @method \Aws\ComprehendMedical\ComprehendMedicalClient createComprehendMedical(array $args = [])
|
88 |
+
* @method \Aws\MultiRegionClient createMultiRegionComprehendMedical(array $args = [])
|
89 |
+
* @method \Aws\ConfigService\ConfigServiceClient createConfigService(array $args = [])
|
90 |
+
* @method \Aws\MultiRegionClient createMultiRegionConfigService(array $args = [])
|
91 |
+
* @method \Aws\Connect\ConnectClient createConnect(array $args = [])
|
92 |
+
* @method \Aws\MultiRegionClient createMultiRegionConnect(array $args = [])
|
93 |
+
* @method \Aws\CostExplorer\CostExplorerClient createCostExplorer(array $args = [])
|
94 |
+
* @method \Aws\MultiRegionClient createMultiRegionCostExplorer(array $args = [])
|
95 |
+
* @method \Aws\CostandUsageReportService\CostandUsageReportServiceClient createCostandUsageReportService(array $args = [])
|
96 |
+
* @method \Aws\MultiRegionClient createMultiRegionCostandUsageReportService(array $args = [])
|
97 |
+
* @method \Aws\DAX\DAXClient createDAX(array $args = [])
|
98 |
+
* @method \Aws\MultiRegionClient createMultiRegionDAX(array $args = [])
|
99 |
+
* @method \Aws\DLM\DLMClient createDLM(array $args = [])
|
100 |
+
* @method \Aws\MultiRegionClient createMultiRegionDLM(array $args = [])
|
101 |
+
* @method \Aws\DataPipeline\DataPipelineClient createDataPipeline(array $args = [])
|
102 |
+
* @method \Aws\MultiRegionClient createMultiRegionDataPipeline(array $args = [])
|
103 |
+
* @method \Aws\DataSync\DataSyncClient createDataSync(array $args = [])
|
104 |
+
* @method \Aws\MultiRegionClient createMultiRegionDataSync(array $args = [])
|
105 |
+
* @method \Aws\DatabaseMigrationService\DatabaseMigrationServiceClient createDatabaseMigrationService(array $args = [])
|
106 |
+
* @method \Aws\MultiRegionClient createMultiRegionDatabaseMigrationService(array $args = [])
|
107 |
+
* @method \Aws\DeviceFarm\DeviceFarmClient createDeviceFarm(array $args = [])
|
108 |
+
* @method \Aws\MultiRegionClient createMultiRegionDeviceFarm(array $args = [])
|
109 |
+
* @method \Aws\DirectConnect\DirectConnectClient createDirectConnect(array $args = [])
|
110 |
+
* @method \Aws\MultiRegionClient createMultiRegionDirectConnect(array $args = [])
|
111 |
+
* @method \Aws\DirectoryService\DirectoryServiceClient createDirectoryService(array $args = [])
|
112 |
+
* @method \Aws\MultiRegionClient createMultiRegionDirectoryService(array $args = [])
|
113 |
+
* @method \Aws\DocDB\DocDBClient createDocDB(array $args = [])
|
114 |
+
* @method \Aws\MultiRegionClient createMultiRegionDocDB(array $args = [])
|
115 |
+
* @method \Aws\DynamoDb\DynamoDbClient createDynamoDb(array $args = [])
|
116 |
+
* @method \Aws\MultiRegionClient createMultiRegionDynamoDb(array $args = [])
|
117 |
+
* @method \Aws\DynamoDbStreams\DynamoDbStreamsClient createDynamoDbStreams(array $args = [])
|
118 |
+
* @method \Aws\MultiRegionClient createMultiRegionDynamoDbStreams(array $args = [])
|
119 |
+
* @method \Aws\EKS\EKSClient createEKS(array $args = [])
|
120 |
+
* @method \Aws\MultiRegionClient createMultiRegionEKS(array $args = [])
|
121 |
+
* @method \Aws\Ec2\Ec2Client createEc2(array $args = [])
|
122 |
+
* @method \Aws\MultiRegionClient createMultiRegionEc2(array $args = [])
|
123 |
+
* @method \Aws\Ecr\EcrClient createEcr(array $args = [])
|
124 |
+
* @method \Aws\MultiRegionClient createMultiRegionEcr(array $args = [])
|
125 |
+
* @method \Aws\Ecs\EcsClient createEcs(array $args = [])
|
126 |
+
* @method \Aws\MultiRegionClient createMultiRegionEcs(array $args = [])
|
127 |
+
* @method \Aws\Efs\EfsClient createEfs(array $args = [])
|
128 |
+
* @method \Aws\MultiRegionClient createMultiRegionEfs(array $args = [])
|
129 |
+
* @method \Aws\ElastiCache\ElastiCacheClient createElastiCache(array $args = [])
|
130 |
+
* @method \Aws\MultiRegionClient createMultiRegionElastiCache(array $args = [])
|
131 |
+
* @method \Aws\ElasticBeanstalk\ElasticBeanstalkClient createElasticBeanstalk(array $args = [])
|
132 |
+
* @method \Aws\MultiRegionClient createMultiRegionElasticBeanstalk(array $args = [])
|
133 |
+
* @method \Aws\ElasticLoadBalancing\ElasticLoadBalancingClient createElasticLoadBalancing(array $args = [])
|
134 |
+
* @method \Aws\MultiRegionClient createMultiRegionElasticLoadBalancing(array $args = [])
|
135 |
+
* @method \Aws\ElasticLoadBalancingV2\ElasticLoadBalancingV2Client createElasticLoadBalancingV2(array $args = [])
|
136 |
+
* @method \Aws\MultiRegionClient createMultiRegionElasticLoadBalancingV2(array $args = [])
|
137 |
+
* @method \Aws\ElasticTranscoder\ElasticTranscoderClient createElasticTranscoder(array $args = [])
|
138 |
+
* @method \Aws\MultiRegionClient createMultiRegionElasticTranscoder(array $args = [])
|
139 |
+
* @method \Aws\ElasticsearchService\ElasticsearchServiceClient createElasticsearchService(array $args = [])
|
140 |
+
* @method \Aws\MultiRegionClient createMultiRegionElasticsearchService(array $args = [])
|
141 |
+
* @method \Aws\Emr\EmrClient createEmr(array $args = [])
|
142 |
+
* @method \Aws\MultiRegionClient createMultiRegionEmr(array $args = [])
|
143 |
+
* @method \Aws\FMS\FMSClient createFMS(array $args = [])
|
144 |
+
* @method \Aws\MultiRegionClient createMultiRegionFMS(array $args = [])
|
145 |
+
* @method \Aws\FSx\FSxClient createFSx(array $args = [])
|
146 |
+
* @method \Aws\MultiRegionClient createMultiRegionFSx(array $args = [])
|
147 |
+
* @method \Aws\Firehose\FirehoseClient createFirehose(array $args = [])
|
148 |
+
* @method \Aws\MultiRegionClient createMultiRegionFirehose(array $args = [])
|
149 |
+
* @method \Aws\GameLift\GameLiftClient createGameLift(array $args = [])
|
150 |
+
* @method \Aws\MultiRegionClient createMultiRegionGameLift(array $args = [])
|
151 |
+
* @method \Aws\Glacier\GlacierClient createGlacier(array $args = [])
|
152 |
+
* @method \Aws\MultiRegionClient createMultiRegionGlacier(array $args = [])
|
153 |
+
* @method \Aws\GlobalAccelerator\GlobalAcceleratorClient createGlobalAccelerator(array $args = [])
|
154 |
+
* @method \Aws\MultiRegionClient createMultiRegionGlobalAccelerator(array $args = [])
|
155 |
+
* @method \Aws\Glue\GlueClient createGlue(array $args = [])
|
156 |
+
* @method \Aws\MultiRegionClient createMultiRegionGlue(array $args = [])
|
157 |
+
* @method \Aws\Greengrass\GreengrassClient createGreengrass(array $args = [])
|
158 |
+
* @method \Aws\MultiRegionClient createMultiRegionGreengrass(array $args = [])
|
159 |
+
* @method \Aws\GuardDuty\GuardDutyClient createGuardDuty(array $args = [])
|
160 |
+
* @method \Aws\MultiRegionClient createMultiRegionGuardDuty(array $args = [])
|
161 |
+
* @method \Aws\Health\HealthClient createHealth(array $args = [])
|
162 |
+
* @method \Aws\MultiRegionClient createMultiRegionHealth(array $args = [])
|
163 |
+
* @method \Aws\Iam\IamClient createIam(array $args = [])
|
164 |
+
* @method \Aws\MultiRegionClient createMultiRegionIam(array $args = [])
|
165 |
+
* @method \Aws\ImportExport\ImportExportClient createImportExport(array $args = [])
|
166 |
+
* @method \Aws\MultiRegionClient createMultiRegionImportExport(array $args = [])
|
167 |
+
* @method \Aws\Inspector\InspectorClient createInspector(array $args = [])
|
168 |
+
* @method \Aws\MultiRegionClient createMultiRegionInspector(array $args = [])
|
169 |
+
* @method \Aws\IoT1ClickDevicesService\IoT1ClickDevicesServiceClient createIoT1ClickDevicesService(array $args = [])
|
170 |
+
* @method \Aws\MultiRegionClient createMultiRegionIoT1ClickDevicesService(array $args = [])
|
171 |
+
* @method \Aws\IoT1ClickProjects\IoT1ClickProjectsClient createIoT1ClickProjects(array $args = [])
|
172 |
+
* @method \Aws\MultiRegionClient createMultiRegionIoT1ClickProjects(array $args = [])
|
173 |
+
* @method \Aws\IoTAnalytics\IoTAnalyticsClient createIoTAnalytics(array $args = [])
|
174 |
+
* @method \Aws\MultiRegionClient createMultiRegionIoTAnalytics(array $args = [])
|
175 |
+
* @method \Aws\IoTJobsDataPlane\IoTJobsDataPlaneClient createIoTJobsDataPlane(array $args = [])
|
176 |
+
* @method \Aws\MultiRegionClient createMultiRegionIoTJobsDataPlane(array $args = [])
|
177 |
+
* @method \Aws\Iot\IotClient createIot(array $args = [])
|
178 |
+
* @method \Aws\MultiRegionClient createMultiRegionIot(array $args = [])
|
179 |
+
* @method \Aws\IotDataPlane\IotDataPlaneClient createIotDataPlane(array $args = [])
|
180 |
+
* @method \Aws\MultiRegionClient createMultiRegionIotDataPlane(array $args = [])
|
181 |
+
* @method \Aws\Kafka\KafkaClient createKafka(array $args = [])
|
182 |
+
* @method \Aws\MultiRegionClient createMultiRegionKafka(array $args = [])
|
183 |
+
* @method \Aws\Kinesis\KinesisClient createKinesis(array $args = [])
|
184 |
+
* @method \Aws\MultiRegionClient createMultiRegionKinesis(array $args = [])
|
185 |
+
* @method \Aws\KinesisAnalytics\KinesisAnalyticsClient createKinesisAnalytics(array $args = [])
|
186 |
+
* @method \Aws\MultiRegionClient createMultiRegionKinesisAnalytics(array $args = [])
|
187 |
+
* @method \Aws\KinesisAnalyticsV2\KinesisAnalyticsV2Client createKinesisAnalyticsV2(array $args = [])
|
188 |
+
* @method \Aws\MultiRegionClient createMultiRegionKinesisAnalyticsV2(array $args = [])
|
189 |
+
* @method \Aws\KinesisVideo\KinesisVideoClient createKinesisVideo(array $args = [])
|
190 |
+
* @method \Aws\MultiRegionClient createMultiRegionKinesisVideo(array $args = [])
|
191 |
+
* @method \Aws\KinesisVideoArchivedMedia\KinesisVideoArchivedMediaClient createKinesisVideoArchivedMedia(array $args = [])
|
192 |
+
* @method \Aws\MultiRegionClient createMultiRegionKinesisVideoArchivedMedia(array $args = [])
|
193 |
+
* @method \Aws\KinesisVideoMedia\KinesisVideoMediaClient createKinesisVideoMedia(array $args = [])
|
194 |
+
* @method \Aws\MultiRegionClient createMultiRegionKinesisVideoMedia(array $args = [])
|
195 |
+
* @method \Aws\Kms\KmsClient createKms(array $args = [])
|
196 |
+
* @method \Aws\MultiRegionClient createMultiRegionKms(array $args = [])
|
197 |
+
* @method \Aws\Lambda\LambdaClient createLambda(array $args = [])
|
198 |
+
* @method \Aws\MultiRegionClient createMultiRegionLambda(array $args = [])
|
199 |
+
* @method \Aws\LexModelBuildingService\LexModelBuildingServiceClient createLexModelBuildingService(array $args = [])
|
200 |
+
* @method \Aws\MultiRegionClient createMultiRegionLexModelBuildingService(array $args = [])
|
201 |
+
* @method \Aws\LexRuntimeService\LexRuntimeServiceClient createLexRuntimeService(array $args = [])
|
202 |
+
* @method \Aws\MultiRegionClient createMultiRegionLexRuntimeService(array $args = [])
|
203 |
+
* @method \Aws\LicenseManager\LicenseManagerClient createLicenseManager(array $args = [])
|
204 |
+
* @method \Aws\MultiRegionClient createMultiRegionLicenseManager(array $args = [])
|
205 |
+
* @method \Aws\Lightsail\LightsailClient createLightsail(array $args = [])
|
206 |
+
* @method \Aws\MultiRegionClient createMultiRegionLightsail(array $args = [])
|
207 |
+
* @method \Aws\MQ\MQClient createMQ(array $args = [])
|
208 |
+
* @method \Aws\MultiRegionClient createMultiRegionMQ(array $args = [])
|
209 |
+
* @method \Aws\MTurk\MTurkClient createMTurk(array $args = [])
|
210 |
+
* @method \Aws\MultiRegionClient createMultiRegionMTurk(array $args = [])
|
211 |
+
* @method \Aws\MachineLearning\MachineLearningClient createMachineLearning(array $args = [])
|
212 |
+
* @method \Aws\MultiRegionClient createMultiRegionMachineLearning(array $args = [])
|
213 |
+
* @method \Aws\Macie\MacieClient createMacie(array $args = [])
|
214 |
+
* @method \Aws\MultiRegionClient createMultiRegionMacie(array $args = [])
|
215 |
+
* @method \Aws\ManagedBlockchain\ManagedBlockchainClient createManagedBlockchain(array $args = [])
|
216 |
+
* @method \Aws\MultiRegionClient createMultiRegionManagedBlockchain(array $args = [])
|
217 |
+
* @method \Aws\MarketplaceCommerceAnalytics\MarketplaceCommerceAnalyticsClient createMarketplaceCommerceAnalytics(array $args = [])
|
218 |
+
* @method \Aws\MultiRegionClient createMultiRegionMarketplaceCommerceAnalytics(array $args = [])
|
219 |
+
* @method \Aws\MarketplaceEntitlementService\MarketplaceEntitlementServiceClient createMarketplaceEntitlementService(array $args = [])
|
220 |
+
* @method \Aws\MultiRegionClient createMultiRegionMarketplaceEntitlementService(array $args = [])
|
221 |
+
* @method \Aws\MarketplaceMetering\MarketplaceMeteringClient createMarketplaceMetering(array $args = [])
|
222 |
+
* @method \Aws\MultiRegionClient createMultiRegionMarketplaceMetering(array $args = [])
|
223 |
+
* @method \Aws\MediaConnect\MediaConnectClient createMediaConnect(array $args = [])
|
224 |
+
* @method \Aws\MultiRegionClient createMultiRegionMediaConnect(array $args = [])
|
225 |
+
* @method \Aws\MediaConvert\MediaConvertClient createMediaConvert(array $args = [])
|
226 |
+
* @method \Aws\MultiRegionClient createMultiRegionMediaConvert(array $args = [])
|
227 |
+
* @method \Aws\MediaLive\MediaLiveClient createMediaLive(array $args = [])
|
228 |
+
* @method \Aws\MultiRegionClient createMultiRegionMediaLive(array $args = [])
|
229 |
+
* @method \Aws\MediaPackage\MediaPackageClient createMediaPackage(array $args = [])
|
230 |
+
* @method \Aws\MultiRegionClient createMultiRegionMediaPackage(array $args = [])
|
231 |
+
* @method \Aws\MediaStore\MediaStoreClient createMediaStore(array $args = [])
|
232 |
+
* @method \Aws\MultiRegionClient createMultiRegionMediaStore(array $args = [])
|
233 |
+
* @method \Aws\MediaStoreData\MediaStoreDataClient createMediaStoreData(array $args = [])
|
234 |
+
* @method \Aws\MultiRegionClient createMultiRegionMediaStoreData(array $args = [])
|
235 |
+
* @method \Aws\MediaTailor\MediaTailorClient createMediaTailor(array $args = [])
|
236 |
+
* @method \Aws\MultiRegionClient createMultiRegionMediaTailor(array $args = [])
|
237 |
+
* @method \Aws\MigrationHub\MigrationHubClient createMigrationHub(array $args = [])
|
238 |
+
* @method \Aws\MultiRegionClient createMultiRegionMigrationHub(array $args = [])
|
239 |
+
* @method \Aws\Mobile\MobileClient createMobile(array $args = [])
|
240 |
+
* @method \Aws\MultiRegionClient createMultiRegionMobile(array $args = [])
|
241 |
+
* @method \Aws\Neptune\NeptuneClient createNeptune(array $args = [])
|
242 |
+
* @method \Aws\MultiRegionClient createMultiRegionNeptune(array $args = [])
|
243 |
+
* @method \Aws\OpsWorks\OpsWorksClient createOpsWorks(array $args = [])
|
244 |
+
* @method \Aws\MultiRegionClient createMultiRegionOpsWorks(array $args = [])
|
245 |
+
* @method \Aws\OpsWorksCM\OpsWorksCMClient createOpsWorksCM(array $args = [])
|
246 |
+
* @method \Aws\MultiRegionClient createMultiRegionOpsWorksCM(array $args = [])
|
247 |
+
* @method \Aws\Organizations\OrganizationsClient createOrganizations(array $args = [])
|
248 |
+
* @method \Aws\MultiRegionClient createMultiRegionOrganizations(array $args = [])
|
249 |
+
* @method \Aws\PI\PIClient createPI(array $args = [])
|
250 |
+
* @method \Aws\MultiRegionClient createMultiRegionPI(array $args = [])
|
251 |
+
* @method \Aws\Pinpoint\PinpointClient createPinpoint(array $args = [])
|
252 |
+
* @method \Aws\MultiRegionClient createMultiRegionPinpoint(array $args = [])
|
253 |
+
* @method \Aws\PinpointEmail\PinpointEmailClient createPinpointEmail(array $args = [])
|
254 |
+
* @method \Aws\MultiRegionClient createMultiRegionPinpointEmail(array $args = [])
|
255 |
+
* @method \Aws\PinpointSMSVoice\PinpointSMSVoiceClient createPinpointSMSVoice(array $args = [])
|
256 |
+
* @method \Aws\MultiRegionClient createMultiRegionPinpointSMSVoice(array $args = [])
|
257 |
+
* @method \Aws\Polly\PollyClient createPolly(array $args = [])
|
258 |
+
* @method \Aws\MultiRegionClient createMultiRegionPolly(array $args = [])
|
259 |
+
* @method \Aws\Pricing\PricingClient createPricing(array $args = [])
|
260 |
+
* @method \Aws\MultiRegionClient createMultiRegionPricing(array $args = [])
|
261 |
+
* @method \Aws\QuickSight\QuickSightClient createQuickSight(array $args = [])
|
262 |
+
* @method \Aws\MultiRegionClient createMultiRegionQuickSight(array $args = [])
|
263 |
+
* @method \Aws\RAM\RAMClient createRAM(array $args = [])
|
264 |
+
* @method \Aws\MultiRegionClient createMultiRegionRAM(array $args = [])
|
265 |
+
* @method \Aws\RDSDataService\RDSDataServiceClient createRDSDataService(array $args = [])
|
266 |
+
* @method \Aws\MultiRegionClient createMultiRegionRDSDataService(array $args = [])
|
267 |
+
* @method \Aws\Rds\RdsClient createRds(array $args = [])
|
268 |
+
* @method \Aws\MultiRegionClient createMultiRegionRds(array $args = [])
|
269 |
+
* @method \Aws\Redshift\RedshiftClient createRedshift(array $args = [])
|
270 |
+
* @method \Aws\MultiRegionClient createMultiRegionRedshift(array $args = [])
|
271 |
+
* @method \Aws\Rekognition\RekognitionClient createRekognition(array $args = [])
|
272 |
+
* @method \Aws\MultiRegionClient createMultiRegionRekognition(array $args = [])
|
273 |
+
* @method \Aws\ResourceGroups\ResourceGroupsClient createResourceGroups(array $args = [])
|
274 |
+
* @method \Aws\MultiRegionClient createMultiRegionResourceGroups(array $args = [])
|
275 |
+
* @method \Aws\ResourceGroupsTaggingAPI\ResourceGroupsTaggingAPIClient createResourceGroupsTaggingAPI(array $args = [])
|
276 |
+
* @method \Aws\MultiRegionClient createMultiRegionResourceGroupsTaggingAPI(array $args = [])
|
277 |
+
* @method \Aws\RoboMaker\RoboMakerClient createRoboMaker(array $args = [])
|
278 |
+
* @method \Aws\MultiRegionClient createMultiRegionRoboMaker(array $args = [])
|
279 |
+
* @method \Aws\Route53\Route53Client createRoute53(array $args = [])
|
280 |
+
* @method \Aws\MultiRegionClient createMultiRegionRoute53(array $args = [])
|
281 |
+
* @method \Aws\Route53Domains\Route53DomainsClient createRoute53Domains(array $args = [])
|
282 |
+
* @method \Aws\MultiRegionClient createMultiRegionRoute53Domains(array $args = [])
|
283 |
+
* @method \Aws\Route53Resolver\Route53ResolverClient createRoute53Resolver(array $args = [])
|
284 |
+
* @method \Aws\MultiRegionClient createMultiRegionRoute53Resolver(array $args = [])
|
285 |
+
* @method \Aws\S3\S3Client createS3(array $args = [])
|
286 |
+
* @method \Aws\S3\S3MultiRegionClient createMultiRegionS3(array $args = [])
|
287 |
+
* @method \Aws\S3Control\S3ControlClient createS3Control(array $args = [])
|
288 |
+
* @method \Aws\MultiRegionClient createMultiRegionS3Control(array $args = [])
|
289 |
+
* @method \Aws\SageMaker\SageMakerClient createSageMaker(array $args = [])
|
290 |
+
* @method \Aws\MultiRegionClient createMultiRegionSageMaker(array $args = [])
|
291 |
+
* @method \Aws\SageMakerRuntime\SageMakerRuntimeClient createSageMakerRuntime(array $args = [])
|
292 |
+
* @method \Aws\MultiRegionClient createMultiRegionSageMakerRuntime(array $args = [])
|
293 |
+
* @method \Aws\SecretsManager\SecretsManagerClient createSecretsManager(array $args = [])
|
294 |
+
* @method \Aws\MultiRegionClient createMultiRegionSecretsManager(array $args = [])
|
295 |
+
* @method \Aws\SecurityHub\SecurityHubClient createSecurityHub(array $args = [])
|
296 |
+
* @method \Aws\MultiRegionClient createMultiRegionSecurityHub(array $args = [])
|
297 |
+
* @method \Aws\ServerlessApplicationRepository\ServerlessApplicationRepositoryClient createServerlessApplicationRepository(array $args = [])
|
298 |
+
* @method \Aws\MultiRegionClient createMultiRegionServerlessApplicationRepository(array $args = [])
|
299 |
+
* @method \Aws\ServiceCatalog\ServiceCatalogClient createServiceCatalog(array $args = [])
|
300 |
+
* @method \Aws\MultiRegionClient createMultiRegionServiceCatalog(array $args = [])
|
301 |
+
* @method \Aws\ServiceDiscovery\ServiceDiscoveryClient createServiceDiscovery(array $args = [])
|
302 |
+
* @method \Aws\MultiRegionClient createMultiRegionServiceDiscovery(array $args = [])
|
303 |
+
* @method \Aws\Ses\SesClient createSes(array $args = [])
|
304 |
+
* @method \Aws\MultiRegionClient createMultiRegionSes(array $args = [])
|
305 |
+
* @method \Aws\Sfn\SfnClient createSfn(array $args = [])
|
306 |
+
* @method \Aws\MultiRegionClient createMultiRegionSfn(array $args = [])
|
307 |
+
* @method \Aws\Shield\ShieldClient createShield(array $args = [])
|
308 |
+
* @method \Aws\MultiRegionClient createMultiRegionShield(array $args = [])
|
309 |
+
* @method \Aws\Sms\SmsClient createSms(array $args = [])
|
310 |
+
* @method \Aws\MultiRegionClient createMultiRegionSms(array $args = [])
|
311 |
+
* @method \Aws\SnowBall\SnowBallClient createSnowBall(array $args = [])
|
312 |
+
* @method \Aws\MultiRegionClient createMultiRegionSnowBall(array $args = [])
|
313 |
+
* @method \Aws\Sns\SnsClient createSns(array $args = [])
|
314 |
+
* @method \Aws\MultiRegionClient createMultiRegionSns(array $args = [])
|
315 |
+
* @method \Aws\Sqs\SqsClient createSqs(array $args = [])
|
316 |
+
* @method \Aws\MultiRegionClient createMultiRegionSqs(array $args = [])
|
317 |
+
* @method \Aws\Ssm\SsmClient createSsm(array $args = [])
|
318 |
+
* @method \Aws\MultiRegionClient createMultiRegionSsm(array $args = [])
|
319 |
+
* @method \Aws\StorageGateway\StorageGatewayClient createStorageGateway(array $args = [])
|
320 |
+
* @method \Aws\MultiRegionClient createMultiRegionStorageGateway(array $args = [])
|
321 |
+
* @method \Aws\Sts\StsClient createSts(array $args = [])
|
322 |
+
* @method \Aws\MultiRegionClient createMultiRegionSts(array $args = [])
|
323 |
+
* @method \Aws\Support\SupportClient createSupport(array $args = [])
|
324 |
+
* @method \Aws\MultiRegionClient createMultiRegionSupport(array $args = [])
|
325 |
+
* @method \Aws\Swf\SwfClient createSwf(array $args = [])
|
326 |
+
* @method \Aws\MultiRegionClient createMultiRegionSwf(array $args = [])
|
327 |
+
* @method \Aws\Textract\TextractClient createTextract(array $args = [])
|
328 |
+
* @method \Aws\MultiRegionClient createMultiRegionTextract(array $args = [])
|
329 |
+
* @method \Aws\TranscribeService\TranscribeServiceClient createTranscribeService(array $args = [])
|
330 |
+
* @method \Aws\MultiRegionClient createMultiRegionTranscribeService(array $args = [])
|
331 |
+
* @method \Aws\Transfer\TransferClient createTransfer(array $args = [])
|
332 |
+
* @method \Aws\MultiRegionClient createMultiRegionTransfer(array $args = [])
|
333 |
+
* @method \Aws\Translate\TranslateClient createTranslate(array $args = [])
|
334 |
+
* @method \Aws\MultiRegionClient createMultiRegionTranslate(array $args = [])
|
335 |
+
* @method \Aws\Waf\WafClient createWaf(array $args = [])
|
336 |
+
* @method \Aws\MultiRegionClient createMultiRegionWaf(array $args = [])
|
337 |
+
* @method \Aws\WafRegional\WafRegionalClient createWafRegional(array $args = [])
|
338 |
+
* @method \Aws\MultiRegionClient createMultiRegionWafRegional(array $args = [])
|
339 |
+
* @method \Aws\WorkDocs\WorkDocsClient createWorkDocs(array $args = [])
|
340 |
+
* @method \Aws\MultiRegionClient createMultiRegionWorkDocs(array $args = [])
|
341 |
+
* @method \Aws\WorkLink\WorkLinkClient createWorkLink(array $args = [])
|
342 |
+
* @method \Aws\MultiRegionClient createMultiRegionWorkLink(array $args = [])
|
343 |
+
* @method \Aws\WorkMail\WorkMailClient createWorkMail(array $args = [])
|
344 |
+
* @method \Aws\MultiRegionClient createMultiRegionWorkMail(array $args = [])
|
345 |
+
* @method \Aws\WorkSpaces\WorkSpacesClient createWorkSpaces(array $args = [])
|
346 |
+
* @method \Aws\MultiRegionClient createMultiRegionWorkSpaces(array $args = [])
|
347 |
+
* @method \Aws\XRay\XRayClient createXRay(array $args = [])
|
348 |
+
* @method \Aws\MultiRegionClient createMultiRegionXRay(array $args = [])
|
349 |
+
* @method \Aws\signer\signerClient createsigner(array $args = [])
|
350 |
+
* @method \Aws\MultiRegionClient createMultiRegionsigner(array $args = [])
|
351 |
+
*/
|
352 |
+
class Sdk
|
353 |
+
{
|
354 |
+
const VERSION = '3.93.9';
|
355 |
+
|
356 |
+
/** @var array Arguments for creating clients */
|
357 |
+
private $args;
|
358 |
+
|
359 |
+
/**
|
360 |
+
* Constructs a new SDK object with an associative array of default
|
361 |
+
* client settings.
|
362 |
+
*
|
363 |
+
* @param array $args
|
364 |
+
*
|
365 |
+
* @throws \InvalidArgumentException
|
366 |
+
* @see Aws\AwsClient::__construct for a list of available options.
|
367 |
+
*/
|
368 |
+
public function __construct(array $args = [])
|
369 |
+
{
|
370 |
+
$this->args = $args;
|
371 |
+
|
372 |
+
if (!isset($args['handler']) && !isset($args['http_handler'])) {
|
373 |
+
$this->args['http_handler'] = default_http_handler();
|
374 |
+
}
|
375 |
+
}
|
376 |
+
|
377 |
+
public function __call($name, array $args)
|
378 |
+
{
|
379 |
+
$args = isset($args[0]) ? $args[0] : [];
|
380 |
+
if (strpos($name, 'createMultiRegion') === 0) {
|
381 |
+
return $this->createMultiRegionClient(substr($name, 17), $args);
|
382 |
+
}
|
383 |
+
|
384 |
+
if (strpos($name, 'create') === 0) {
|
385 |
+
return $this->createClient(substr($name, 6), $args);
|
386 |
+
}
|
387 |
+
|
388 |
+
throw new \BadMethodCallException("Unknown method: {$name}.");
|
389 |
+
}
|
390 |
+
|
391 |
+
/**
|
392 |
+
* Get a client by name using an array of constructor options.
|
393 |
+
*
|
394 |
+
* @param string $name Service name or namespace (e.g., DynamoDb, s3).
|
395 |
+
* @param array $args Arguments to configure the client.
|
396 |
+
*
|
397 |
+
* @return AwsClientInterface
|
398 |
+
* @throws \InvalidArgumentException if any required options are missing or
|
399 |
+
* the service is not supported.
|
400 |
+
* @see Aws\AwsClient::__construct for a list of available options for args.
|
401 |
+
*/
|
402 |
+
public function createClient($name, array $args = [])
|
403 |
+
{
|
404 |
+
// Get information about the service from the manifest file.
|
405 |
+
$service = manifest($name);
|
406 |
+
$namespace = $service['namespace'];
|
407 |
+
|
408 |
+
// Instantiate the client class.
|
409 |
+
$client = "Aws\\{$namespace}\\{$namespace}Client";
|
410 |
+
return new $client($this->mergeArgs($namespace, $service, $args));
|
411 |
+
}
|
412 |
+
|
413 |
+
public function createMultiRegionClient($name, array $args = [])
|
414 |
+
{
|
415 |
+
// Get information about the service from the manifest file.
|
416 |
+
$service = manifest($name);
|
417 |
+
$namespace = $service['namespace'];
|
418 |
+
|
419 |
+
$klass = "Aws\\{$namespace}\\{$namespace}MultiRegionClient";
|
420 |
+
$klass = class_exists($klass) ? $klass : 'Aws\\MultiRegionClient';
|
421 |
+
|
422 |
+
return new $klass($this->mergeArgs($namespace, $service, $args));
|
423 |
+
}
|
424 |
+
|
425 |
+
/**
|
426 |
+
* Clone existing SDK instance with ability to pass an associative array
|
427 |
+
* of extra client settings.
|
428 |
+
*
|
429 |
+
* @param array $args
|
430 |
+
*
|
431 |
+
* @return self
|
432 |
+
*/
|
433 |
+
public function copy(array $args = [])
|
434 |
+
{
|
435 |
+
return new self($args + $this->args);
|
436 |
+
}
|
437 |
+
|
438 |
+
private function mergeArgs($namespace, array $manifest, array $args = [])
|
439 |
+
{
|
440 |
+
// Merge provided args with stored, service-specific args.
|
441 |
+
if (isset($this->args[$namespace])) {
|
442 |
+
$args += $this->args[$namespace];
|
443 |
+
}
|
444 |
+
|
445 |
+
// Provide the endpoint prefix in the args.
|
446 |
+
if (!isset($args['service'])) {
|
447 |
+
$args['service'] = $manifest['endpoint'];
|
448 |
+
}
|
449 |
+
|
450 |
+
return $args + $this->args;
|
451 |
+
}
|
452 |
+
|
453 |
+
/**
|
454 |
+
* Determine the endpoint prefix from a client namespace.
|
455 |
+
*
|
456 |
+
* @param string $name Namespace name
|
457 |
+
*
|
458 |
+
* @return string
|
459 |
+
* @internal
|
460 |
+
* @deprecated Use the `\Aws\manifest()` function instead.
|
461 |
+
*/
|
462 |
+
public static function getEndpointPrefix($name)
|
463 |
+
{
|
464 |
+
return manifest($name)['endpoint'];
|
465 |
+
}
|
466 |
+
}
|
lib/Aws/Aws/Signature/AnonymousSignature.php
ADDED
@@ -0,0 +1,26 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Signature;
|
3 |
+
|
4 |
+
use Aws\Credentials\CredentialsInterface;
|
5 |
+
use Psr\Http\Message\RequestInterface;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Provides anonymous client access (does not sign requests).
|
9 |
+
*/
|
10 |
+
class AnonymousSignature implements SignatureInterface
|
11 |
+
{
|
12 |
+
public function signRequest(
|
13 |
+
RequestInterface $request,
|
14 |
+
CredentialsInterface $credentials
|
15 |
+
) {
|
16 |
+
return $request;
|
17 |
+
}
|
18 |
+
|
19 |
+
public function presign(
|
20 |
+
RequestInterface $request,
|
21 |
+
CredentialsInterface $credentials,
|
22 |
+
$expires
|
23 |
+
) {
|
24 |
+
return $request;
|
25 |
+
}
|
26 |
+
}
|
lib/Aws/Aws/Signature/S3SignatureV4.php
ADDED
@@ -0,0 +1,68 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Signature;
|
3 |
+
|
4 |
+
use Aws\Credentials\CredentialsInterface;
|
5 |
+
use Psr\Http\Message\RequestInterface;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Amazon S3 signature version 4 support.
|
9 |
+
*/
|
10 |
+
class S3SignatureV4 extends SignatureV4
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Always add a x-amz-content-sha-256 for data integrity.
|
14 |
+
*/
|
15 |
+
public function signRequest(
|
16 |
+
RequestInterface $request,
|
17 |
+
CredentialsInterface $credentials
|
18 |
+
) {
|
19 |
+
if (!$request->hasHeader('x-amz-content-sha256')) {
|
20 |
+
$request = $request->withHeader(
|
21 |
+
'X-Amz-Content-Sha256',
|
22 |
+
$this->getPayload($request)
|
23 |
+
);
|
24 |
+
}
|
25 |
+
|
26 |
+
return parent::signRequest($request, $credentials);
|
27 |
+
}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Always add a x-amz-content-sha-256 for data integrity.
|
31 |
+
*/
|
32 |
+
public function presign(
|
33 |
+
RequestInterface $request,
|
34 |
+
CredentialsInterface $credentials,
|
35 |
+
$expires,
|
36 |
+
array $options = []
|
37 |
+
) {
|
38 |
+
if (!$request->hasHeader('x-amz-content-sha256')) {
|
39 |
+
$request = $request->withHeader(
|
40 |
+
'X-Amz-Content-Sha256',
|
41 |
+
$this->getPresignedPayload($request)
|
42 |
+
);
|
43 |
+
}
|
44 |
+
|
45 |
+
return parent::presign($request, $credentials, $expires, $options);
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Override used to allow pre-signed URLs to be created for an
|
50 |
+
* in-determinate request payload.
|
51 |
+
*/
|
52 |
+
protected function getPresignedPayload(RequestInterface $request)
|
53 |
+
{
|
54 |
+
return SignatureV4::UNSIGNED_PAYLOAD;
|
55 |
+
}
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Amazon S3 does not double-encode the path component in the canonical request
|
59 |
+
*/
|
60 |
+
protected function createCanonicalizedPath($path)
|
61 |
+
{
|
62 |
+
// Only remove one slash in case of keys that have a preceding slash
|
63 |
+
if (substr($path, 0, 1) === '/') {
|
64 |
+
$path = substr($path, 1);
|
65 |
+
}
|
66 |
+
return '/' . $path;
|
67 |
+
}
|
68 |
+
}
|
lib/Aws/Aws/Signature/SignatureInterface.php
ADDED
@@ -0,0 +1,44 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Signature;
|
3 |
+
|
4 |
+
use Aws\Credentials\CredentialsInterface;
|
5 |
+
use Psr\Http\Message\RequestInterface;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Interface used to provide interchangeable strategies for signing requests
|
9 |
+
* using the various AWS signature protocols.
|
10 |
+
*/
|
11 |
+
interface SignatureInterface
|
12 |
+
{
|
13 |
+
/**
|
14 |
+
* Signs the specified request with an AWS signing protocol by using the
|
15 |
+
* provided AWS account credentials and adding the required headers to the
|
16 |
+
* request.
|
17 |
+
*
|
18 |
+
* @param RequestInterface $request Request to sign
|
19 |
+
* @param CredentialsInterface $credentials Signing credentials
|
20 |
+
*
|
21 |
+
* @return RequestInterface Returns the modified request.
|
22 |
+
*/
|
23 |
+
public function signRequest(
|
24 |
+
RequestInterface $request,
|
25 |
+
CredentialsInterface $credentials
|
26 |
+
);
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Create a pre-signed request.
|
30 |
+
*
|
31 |
+
* @param RequestInterface $request Request to sign
|
32 |
+
* @param CredentialsInterface $credentials Credentials used to sign
|
33 |
+
* @param int|string|\DateTime $expires The time at which the URL should
|
34 |
+
* expire. This can be a Unix timestamp, a PHP DateTime object, or a
|
35 |
+
* string that can be evaluated by strtotime.
|
36 |
+
*
|
37 |
+
* @return RequestInterface
|
38 |
+
*/
|
39 |
+
public function presign(
|
40 |
+
RequestInterface $request,
|
41 |
+
CredentialsInterface $credentials,
|
42 |
+
$expires
|
43 |
+
);
|
44 |
+
}
|
lib/Aws/Aws/Signature/SignatureProvider.php
ADDED
@@ -0,0 +1,131 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Signature;
|
3 |
+
|
4 |
+
use Aws\Exception\UnresolvedSignatureException;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Signature providers.
|
8 |
+
*
|
9 |
+
* A signature provider is a function that accepts a version, service, and
|
10 |
+
* region and returns a {@see SignatureInterface} object on success or NULL if
|
11 |
+
* no signature can be created from the provided arguments.
|
12 |
+
*
|
13 |
+
* You can wrap your calls to a signature provider with the
|
14 |
+
* {@see SignatureProvider::resolve} function to ensure that a signature object
|
15 |
+
* is created. If a signature object is not created, then the resolve()
|
16 |
+
* function will throw a {@see Aws\Exception\UnresolvedSignatureException}.
|
17 |
+
*
|
18 |
+
* use Aws\Signature\SignatureProvider;
|
19 |
+
* $provider = SignatureProvider::defaultProvider();
|
20 |
+
* // Returns a SignatureInterface or NULL.
|
21 |
+
* $signer = $provider('v4', 's3', 'us-west-2');
|
22 |
+
* // Returns a SignatureInterface or throws.
|
23 |
+
* $signer = SignatureProvider::resolve($provider, 'no', 's3', 'foo');
|
24 |
+
*
|
25 |
+
* You can compose multiple providers into a single provider using
|
26 |
+
* {@see Aws\or_chain}. This function accepts providers as arguments and
|
27 |
+
* returns a new function that will invoke each provider until a non-null value
|
28 |
+
* is returned.
|
29 |
+
*
|
30 |
+
* $a = SignatureProvider::defaultProvider();
|
31 |
+
* $b = function ($version, $service, $region) {
|
32 |
+
* if ($version === 'foo') {
|
33 |
+
* return new MyFooSignature();
|
34 |
+
* }
|
35 |
+
* };
|
36 |
+
* $c = \Aws\or_chain($a, $b);
|
37 |
+
* $signer = $c('v4', 'abc', '123'); // $a handles this.
|
38 |
+
* $signer = $c('foo', 'abc', '123'); // $b handles this.
|
39 |
+
* $nullValue = $c('???', 'abc', '123'); // Neither can handle this.
|
40 |
+
*/
|
41 |
+
class SignatureProvider
|
42 |
+
{
|
43 |
+
private static $s3v4SignedServices = [
|
44 |
+
's3' => true,
|
45 |
+
's3control' => true,
|
46 |
+
];
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Resolves and signature provider and ensures a non-null return value.
|
50 |
+
*
|
51 |
+
* @param callable $provider Provider function to invoke.
|
52 |
+
* @param string $version Signature version.
|
53 |
+
* @param string $service Service name.
|
54 |
+
* @param string $region Region name.
|
55 |
+
*
|
56 |
+
* @return SignatureInterface
|
57 |
+
* @throws UnresolvedSignatureException
|
58 |
+
*/
|
59 |
+
public static function resolve(callable $provider, $version, $service, $region)
|
60 |
+
{
|
61 |
+
$result = $provider($version, $service, $region);
|
62 |
+
if ($result instanceof SignatureInterface) {
|
63 |
+
return $result;
|
64 |
+
}
|
65 |
+
|
66 |
+
throw new UnresolvedSignatureException(
|
67 |
+
"Unable to resolve a signature for $version/$service/$region.\n"
|
68 |
+
. "Valid signature versions include v4 and anonymous."
|
69 |
+
);
|
70 |
+
}
|
71 |
+
|
72 |
+
/**
|
73 |
+
* Default SDK signature provider.
|
74 |
+
*
|
75 |
+
* @return callable
|
76 |
+
*/
|
77 |
+
public static function defaultProvider()
|
78 |
+
{
|
79 |
+
return self::memoize(self::version());
|
80 |
+
}
|
81 |
+
|
82 |
+
/**
|
83 |
+
* Creates a signature provider that caches previously created signature
|
84 |
+
* objects. The computed cache key is the concatenation of the version,
|
85 |
+
* service, and region.
|
86 |
+
*
|
87 |
+
* @param callable $provider Signature provider to wrap.
|
88 |
+
*
|
89 |
+
* @return callable
|
90 |
+
*/
|
91 |
+
public static function memoize(callable $provider)
|
92 |
+
{
|
93 |
+
$cache = [];
|
94 |
+
return function ($version, $service, $region) use (&$cache, $provider) {
|
95 |
+
$key = "($version)($service)($region)";
|
96 |
+
if (!isset($cache[$key])) {
|
97 |
+
$cache[$key] = $provider($version, $service, $region);
|
98 |
+
}
|
99 |
+
return $cache[$key];
|
100 |
+
};
|
101 |
+
}
|
102 |
+
|
103 |
+
/**
|
104 |
+
* Creates signature objects from known signature versions.
|
105 |
+
*
|
106 |
+
* This provider currently recognizes the following signature versions:
|
107 |
+
*
|
108 |
+
* - v4: Signature version 4.
|
109 |
+
* - anonymous: Does not sign requests.
|
110 |
+
*
|
111 |
+
* @return callable
|
112 |
+
*/
|
113 |
+
public static function version()
|
114 |
+
{
|
115 |
+
return function ($version, $service, $region) {
|
116 |
+
switch ($version) {
|
117 |
+
case 's3v4':
|
118 |
+
case 'v4':
|
119 |
+
return !empty(self::$s3v4SignedServices[$service])
|
120 |
+
? new S3SignatureV4($service, $region)
|
121 |
+
: new SignatureV4($service, $region);
|
122 |
+
case 'v4-unsigned-body':
|
123 |
+
return new SignatureV4($service, $region, ['unsigned-body' => 'true']);
|
124 |
+
case 'anonymous':
|
125 |
+
return new AnonymousSignature();
|
126 |
+
default:
|
127 |
+
return null;
|
128 |
+
}
|
129 |
+
};
|
130 |
+
}
|
131 |
+
}
|
lib/Aws/Aws/Signature/SignatureTrait.php
ADDED
@@ -0,0 +1,49 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Signature;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Provides signature calculation for SignatureV4.
|
6 |
+
*/
|
7 |
+
trait SignatureTrait
|
8 |
+
{
|
9 |
+
/** @var array Cache of previously signed values */
|
10 |
+
private $cache = [];
|
11 |
+
|
12 |
+
/** @var int Size of the hash cache */
|
13 |
+
private $cacheSize = 0;
|
14 |
+
|
15 |
+
private function createScope($shortDate, $region, $service)
|
16 |
+
{
|
17 |
+
return "$shortDate/$region/$service/aws4_request";
|
18 |
+
}
|
19 |
+
|
20 |
+
private function getSigningKey($shortDate, $region, $service, $secretKey)
|
21 |
+
{
|
22 |
+
$k = $shortDate . '_' . $region . '_' . $service . '_' . $secretKey;
|
23 |
+
|
24 |
+
if (!isset($this->cache[$k])) {
|
25 |
+
// Clear the cache when it reaches 50 entries
|
26 |
+
if (++$this->cacheSize > 50) {
|
27 |
+
$this->cache = [];
|
28 |
+
$this->cacheSize = 0;
|
29 |
+
}
|
30 |
+
|
31 |
+
$dateKey = hash_hmac(
|
32 |
+
'sha256',
|
33 |
+
$shortDate,
|
34 |
+
"AWS4{$secretKey}",
|
35 |
+
true
|
36 |
+
);
|
37 |
+
$regionKey = hash_hmac('sha256', $region, $dateKey, true);
|
38 |
+
$serviceKey = hash_hmac('sha256', $service, $regionKey, true);
|
39 |
+
$this->cache[$k] = hash_hmac(
|
40 |
+
'sha256',
|
41 |
+
'aws4_request',
|
42 |
+
$serviceKey,
|
43 |
+
true
|
44 |
+
);
|
45 |
+
}
|
46 |
+
|
47 |
+
return $this->cache[$k];
|
48 |
+
}
|
49 |
+
}
|
lib/Aws/Aws/Signature/SignatureV4.php
ADDED
@@ -0,0 +1,412 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Signature;
|
3 |
+
|
4 |
+
use Aws\Credentials\CredentialsInterface;
|
5 |
+
use Aws\Exception\CouldNotCreateChecksumException;
|
6 |
+
use GuzzleHttp\Psr7;
|
7 |
+
use Psr\Http\Message\RequestInterface;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Signature Version 4
|
11 |
+
* @link http://docs.aws.amazon.com/general/latest/gr/signature-version-4.html
|
12 |
+
*/
|
13 |
+
class SignatureV4 implements SignatureInterface
|
14 |
+
{
|
15 |
+
use SignatureTrait;
|
16 |
+
const ISO8601_BASIC = 'Ymd\THis\Z';
|
17 |
+
const UNSIGNED_PAYLOAD = 'UNSIGNED-PAYLOAD';
|
18 |
+
const AMZ_CONTENT_SHA256_HEADER = 'X-Amz-Content-Sha256';
|
19 |
+
|
20 |
+
/** @var string */
|
21 |
+
private $service;
|
22 |
+
|
23 |
+
/** @var string */
|
24 |
+
private $region;
|
25 |
+
|
26 |
+
/** @var bool */
|
27 |
+
private $unsigned;
|
28 |
+
|
29 |
+
/**
|
30 |
+
* The following headers are not signed because signing these headers
|
31 |
+
* would potentially cause a signature mismatch when sending a request
|
32 |
+
* through a proxy or if modified at the HTTP client level.
|
33 |
+
*
|
34 |
+
* @return array
|
35 |
+
*/
|
36 |
+
private function getHeaderBlacklist()
|
37 |
+
{
|
38 |
+
return [
|
39 |
+
'cache-control' => true,
|
40 |
+
'content-type' => true,
|
41 |
+
'content-length' => true,
|
42 |
+
'expect' => true,
|
43 |
+
'max-forwards' => true,
|
44 |
+
'pragma' => true,
|
45 |
+
'range' => true,
|
46 |
+
'te' => true,
|
47 |
+
'if-match' => true,
|
48 |
+
'if-none-match' => true,
|
49 |
+
'if-modified-since' => true,
|
50 |
+
'if-unmodified-since' => true,
|
51 |
+
'if-range' => true,
|
52 |
+
'accept' => true,
|
53 |
+
'authorization' => true,
|
54 |
+
'proxy-authorization' => true,
|
55 |
+
'from' => true,
|
56 |
+
'referer' => true,
|
57 |
+
'user-agent' => true,
|
58 |
+
'x-amzn-trace-id' => true,
|
59 |
+
'aws-sdk-invocation-id' => true,
|
60 |
+
'aws-sdk-retry' => true,
|
61 |
+
];
|
62 |
+
}
|
63 |
+
|
64 |
+
/**
|
65 |
+
* @param string $service Service name to use when signing
|
66 |
+
* @param string $region Region name to use when signing
|
67 |
+
* @param array $options Array of configuration options used when signing
|
68 |
+
* - unsigned-body: Flag to make request have unsigned payload.
|
69 |
+
* Unsigned body is used primarily for streaming requests.
|
70 |
+
*/
|
71 |
+
public function __construct($service, $region, array $options = [])
|
72 |
+
{
|
73 |
+
$this->service = $service;
|
74 |
+
$this->region = $region;
|
75 |
+
$this->unsigned = isset($options['unsigned-body']) ? $options['unsigned-body'] : false;
|
76 |
+
}
|
77 |
+
|
78 |
+
public function signRequest(
|
79 |
+
RequestInterface $request,
|
80 |
+
CredentialsInterface $credentials
|
81 |
+
) {
|
82 |
+
$ldt = gmdate(self::ISO8601_BASIC);
|
83 |
+
$sdt = substr($ldt, 0, 8);
|
84 |
+
$parsed = $this->parseRequest($request);
|
85 |
+
$parsed['headers']['X-Amz-Date'] = [$ldt];
|
86 |
+
|
87 |
+
if ($token = $credentials->getSecurityToken()) {
|
88 |
+
$parsed['headers']['X-Amz-Security-Token'] = [$token];
|
89 |
+
}
|
90 |
+
$cs = $this->createScope($sdt, $this->region, $this->service);
|
91 |
+
$payload = $this->getPayload($request);
|
92 |
+
|
93 |
+
if ($payload == self::UNSIGNED_PAYLOAD) {
|
94 |
+
$parsed['headers'][self::AMZ_CONTENT_SHA256_HEADER] = [$payload];
|
95 |
+
}
|
96 |
+
|
97 |
+
$context = $this->createContext($parsed, $payload);
|
98 |
+
$toSign = $this->createStringToSign($ldt, $cs, $context['creq']);
|
99 |
+
$signingKey = $this->getSigningKey(
|
100 |
+
$sdt,
|
101 |
+
$this->region,
|
102 |
+
$this->service,
|
103 |
+
$credentials->getSecretKey()
|
104 |
+
);
|
105 |
+
$signature = hash_hmac('sha256', $toSign, $signingKey);
|
106 |
+
$parsed['headers']['Authorization'] = [
|
107 |
+
"AWS4-HMAC-SHA256 "
|
108 |
+
. "Credential={$credentials->getAccessKeyId()}/{$cs}, "
|
109 |
+
. "SignedHeaders={$context['headers']}, Signature={$signature}"
|
110 |
+
];
|
111 |
+
|
112 |
+
return $this->buildRequest($parsed);
|
113 |
+
}
|
114 |
+
|
115 |
+
/**
|
116 |
+
* Get the headers that were used to pre-sign the request.
|
117 |
+
* Used for the X-Amz-SignedHeaders header.
|
118 |
+
*
|
119 |
+
* @param array $headers
|
120 |
+
* @return array
|
121 |
+
*/
|
122 |
+
private function getPresignHeaders(array $headers)
|
123 |
+
{
|
124 |
+
$presignHeaders = [];
|
125 |
+
$blacklist = $this->getHeaderBlacklist();
|
126 |
+
foreach ($headers as $name => $value) {
|
127 |
+
$lName = strtolower($name);
|
128 |
+
if (!isset($blacklist[$lName])
|
129 |
+
&& $name !== self::AMZ_CONTENT_SHA256_HEADER
|
130 |
+
) {
|
131 |
+
$presignHeaders[] = $lName;
|
132 |
+
}
|
133 |
+
}
|
134 |
+
return $presignHeaders;
|
135 |
+
}
|
136 |
+
|
137 |
+
public function presign(
|
138 |
+
RequestInterface $request,
|
139 |
+
CredentialsInterface $credentials,
|
140 |
+
$expires,
|
141 |
+
array $options = []
|
142 |
+
) {
|
143 |
+
|
144 |
+
$startTimestamp = isset($options['start_time'])
|
145 |
+
? $this->convertToTimestamp($options['start_time'], null)
|
146 |
+
: time();
|
147 |
+
|
148 |
+
$expiresTimestamp = $this->convertToTimestamp($expires, $startTimestamp);
|
149 |
+
|
150 |
+
$parsed = $this->createPresignedRequest($request, $credentials);
|
151 |
+
$payload = $this->getPresignedPayload($request);
|
152 |
+
$httpDate = gmdate(self::ISO8601_BASIC, $startTimestamp);
|
153 |
+
$shortDate = substr($httpDate, 0, 8);
|
154 |
+
$scope = $this->createScope($shortDate, $this->region, $this->service);
|
155 |
+
$credential = $credentials->getAccessKeyId() . '/' . $scope;
|
156 |
+
if ($credentials->getSecurityToken()) {
|
157 |
+
unset($parsed['headers']['X-Amz-Security-Token']);
|
158 |
+
}
|
159 |
+
$parsed['query']['X-Amz-Algorithm'] = 'AWS4-HMAC-SHA256';
|
160 |
+
$parsed['query']['X-Amz-Credential'] = $credential;
|
161 |
+
$parsed['query']['X-Amz-Date'] = gmdate('Ymd\THis\Z', $startTimestamp);
|
162 |
+
$parsed['query']['X-Amz-SignedHeaders'] = implode(';', $this->getPresignHeaders($parsed['headers']));
|
163 |
+
$parsed['query']['X-Amz-Expires'] = $this->convertExpires($expiresTimestamp, $startTimestamp);
|
164 |
+
$context = $this->createContext($parsed, $payload);
|
165 |
+
$stringToSign = $this->createStringToSign($httpDate, $scope, $context['creq']);
|
166 |
+
$key = $this->getSigningKey(
|
167 |
+
$shortDate,
|
168 |
+
$this->region,
|
169 |
+
$this->service,
|
170 |
+
$credentials->getSecretKey()
|
171 |
+
);
|
172 |
+
$parsed['query']['X-Amz-Signature'] = hash_hmac('sha256', $stringToSign, $key);
|
173 |
+
|
174 |
+
return $this->buildRequest($parsed);
|
175 |
+
}
|
176 |
+
|
177 |
+
/**
|
178 |
+
* Converts a POST request to a GET request by moving POST fields into the
|
179 |
+
* query string.
|
180 |
+
*
|
181 |
+
* Useful for pre-signing query protocol requests.
|
182 |
+
*
|
183 |
+
* @param RequestInterface $request Request to clone
|
184 |
+
*
|
185 |
+
* @return RequestInterface
|
186 |
+
* @throws \InvalidArgumentException if the method is not POST
|
187 |
+
*/
|
188 |
+
public static function convertPostToGet(RequestInterface $request)
|
189 |
+
{
|
190 |
+
if ($request->getMethod() !== 'POST') {
|
191 |
+
throw new \InvalidArgumentException('Expected a POST request but '
|
192 |
+
. 'received a ' . $request->getMethod() . ' request.');
|
193 |
+
}
|
194 |
+
|
195 |
+
$sr = $request->withMethod('GET')
|
196 |
+
->withBody(Psr7\stream_for(''))
|
197 |
+
->withoutHeader('Content-Type')
|
198 |
+
->withoutHeader('Content-Length');
|
199 |
+
|
200 |
+
// Move POST fields to the query if they are present
|
201 |
+
if ($request->getHeaderLine('Content-Type') === 'application/x-www-form-urlencoded') {
|
202 |
+
$body = (string) $request->getBody();
|
203 |
+
$sr = $sr->withUri($sr->getUri()->withQuery($body));
|
204 |
+
}
|
205 |
+
|
206 |
+
return $sr;
|
207 |
+
}
|
208 |
+
|
209 |
+
protected function getPayload(RequestInterface $request)
|
210 |
+
{
|
211 |
+
if ($this->unsigned && $request->getUri()->getScheme() == 'https') {
|
212 |
+
return self::UNSIGNED_PAYLOAD;
|
213 |
+
}
|
214 |
+
// Calculate the request signature payload
|
215 |
+
if ($request->hasHeader(self::AMZ_CONTENT_SHA256_HEADER)) {
|
216 |
+
// Handle streaming operations (e.g. Glacier.UploadArchive)
|
217 |
+
return $request->getHeaderLine(self::AMZ_CONTENT_SHA256_HEADER);
|
218 |
+
}
|
219 |
+
|
220 |
+
if (!$request->getBody()->isSeekable()) {
|
221 |
+
throw new CouldNotCreateChecksumException('sha256');
|
222 |
+
}
|
223 |
+
|
224 |
+
try {
|
225 |
+
return Psr7\hash($request->getBody(), 'sha256');
|
226 |
+
} catch (\Exception $e) {
|
227 |
+
throw new CouldNotCreateChecksumException('sha256', $e);
|
228 |
+
}
|
229 |
+
}
|
230 |
+
|
231 |
+
protected function getPresignedPayload(RequestInterface $request)
|
232 |
+
{
|
233 |
+
return $this->getPayload($request);
|
234 |
+
}
|
235 |
+
|
236 |
+
protected function createCanonicalizedPath($path)
|
237 |
+
{
|
238 |
+
$doubleEncoded = rawurlencode(ltrim($path, '/'));
|
239 |
+
|
240 |
+
return '/' . str_replace('%2F', '/', $doubleEncoded);
|
241 |
+
}
|
242 |
+
|
243 |
+
private function createStringToSign($longDate, $credentialScope, $creq)
|
244 |
+
{
|
245 |
+
$hash = hash('sha256', $creq);
|
246 |
+
|
247 |
+
return "AWS4-HMAC-SHA256\n{$longDate}\n{$credentialScope}\n{$hash}";
|
248 |
+
}
|
249 |
+
|
250 |
+
private function createPresignedRequest(
|
251 |
+
RequestInterface $request,
|
252 |
+
CredentialsInterface $credentials
|
253 |
+
) {
|
254 |
+
$parsedRequest = $this->parseRequest($request);
|
255 |
+
|
256 |
+
// Make sure to handle temporary credentials
|
257 |
+
if ($token = $credentials->getSecurityToken()) {
|
258 |
+
$parsedRequest['headers']['X-Amz-Security-Token'] = [$token];
|
259 |
+
}
|
260 |
+
|
261 |
+
return $this->moveHeadersToQuery($parsedRequest);
|
262 |
+
}
|
263 |
+
|
264 |
+
/**
|
265 |
+
* @param array $parsedRequest
|
266 |
+
* @param string $payload Hash of the request payload
|
267 |
+
* @return array Returns an array of context information
|
268 |
+
*/
|
269 |
+
private function createContext(array $parsedRequest, $payload)
|
270 |
+
{
|
271 |
+
$blacklist = $this->getHeaderBlacklist();
|
272 |
+
|
273 |
+
// Normalize the path as required by SigV4
|
274 |
+
$canon = $parsedRequest['method'] . "\n"
|
275 |
+
. $this->createCanonicalizedPath($parsedRequest['path']) . "\n"
|
276 |
+
. $this->getCanonicalizedQuery($parsedRequest['query']) . "\n";
|
277 |
+
|
278 |
+
// Case-insensitively aggregate all of the headers.
|
279 |
+
$aggregate = [];
|
280 |
+
foreach ($parsedRequest['headers'] as $key => $values) {
|
281 |
+
$key = strtolower($key);
|
282 |
+
if (!isset($blacklist[$key])) {
|
283 |
+
foreach ($values as $v) {
|
284 |
+
$aggregate[$key][] = $v;
|
285 |
+
}
|
286 |
+
}
|
287 |
+
}
|
288 |
+
|
289 |
+
ksort($aggregate);
|
290 |
+
$canonHeaders = [];
|
291 |
+
foreach ($aggregate as $k => $v) {
|
292 |
+
if (count($v) > 0) {
|
293 |
+
sort($v);
|
294 |
+
}
|
295 |
+
$canonHeaders[] = $k . ':' . preg_replace('/\s+/', ' ', implode(',', $v));
|
296 |
+
}
|
297 |
+
|
298 |
+
$signedHeadersString = implode(';', array_keys($aggregate));
|
299 |
+
$canon .= implode("\n", $canonHeaders) . "\n\n"
|
300 |
+
. $signedHeadersString . "\n"
|
301 |
+
. $payload;
|
302 |
+
|
303 |
+
return ['creq' => $canon, 'headers' => $signedHeadersString];
|
304 |
+
}
|
305 |
+
|
306 |
+
private function getCanonicalizedQuery(array $query)
|
307 |
+
{
|
308 |
+
unset($query['X-Amz-Signature']);
|
309 |
+
|
310 |
+
if (!$query) {
|
311 |
+
return '';
|
312 |
+
}
|
313 |
+
|
314 |
+
$qs = '';
|
315 |
+
ksort($query);
|
316 |
+
foreach ($query as $k => $v) {
|
317 |
+
if (!is_array($v)) {
|
318 |
+
$qs .= rawurlencode($k) . '=' . rawurlencode($v) . '&';
|
319 |
+
} else {
|
320 |
+
sort($v);
|
321 |
+
foreach ($v as $value) {
|
322 |
+
$qs .= rawurlencode($k) . '=' . rawurlencode($value) . '&';
|
323 |
+
}
|
324 |
+
}
|
325 |
+
}
|
326 |
+
|
327 |
+
return substr($qs, 0, -1);
|
328 |
+
}
|
329 |
+
|
330 |
+
private function convertToTimestamp($dateValue, $relativeTimeBase = null)
|
331 |
+
{
|
332 |
+
if ($dateValue instanceof \DateTimeInterface) {
|
333 |
+
$timestamp = $dateValue->getTimestamp();
|
334 |
+
} elseif (!is_numeric($dateValue)) {
|
335 |
+
$timestamp = strtotime($dateValue,
|
336 |
+
$relativeTimeBase === null ? time() : $relativeTimeBase
|
337 |
+
);
|
338 |
+
} else {
|
339 |
+
$timestamp = $dateValue;
|
340 |
+
}
|
341 |
+
|
342 |
+
return $timestamp;
|
343 |
+
}
|
344 |
+
|
345 |
+
private function convertExpires($expiresTimestamp, $startTimestamp)
|
346 |
+
{
|
347 |
+
$duration = $expiresTimestamp - $startTimestamp;
|
348 |
+
|
349 |
+
// Ensure that the duration of the signature is not longer than a week
|
350 |
+
if ($duration > 604800) {
|
351 |
+
throw new \InvalidArgumentException('The expiration date of a '
|
352 |
+
. 'signature version 4 presigned URL must be less than one '
|
353 |
+
. 'week');
|
354 |
+
}
|
355 |
+
|
356 |
+
return $duration;
|
357 |
+
}
|
358 |
+
|
359 |
+
private function moveHeadersToQuery(array $parsedRequest)
|
360 |
+
{
|
361 |
+
foreach ($parsedRequest['headers'] as $name => $header) {
|
362 |
+
$lname = strtolower($name);
|
363 |
+
if (substr($lname, 0, 5) == 'x-amz') {
|
364 |
+
$parsedRequest['query'][$name] = $header;
|
365 |
+
}
|
366 |
+
$blacklist = $this->getHeaderBlacklist();
|
367 |
+
if (isset($blacklist[$lname])
|
368 |
+
|| $lname === strtolower(self::AMZ_CONTENT_SHA256_HEADER)
|
369 |
+
) {
|
370 |
+
unset($parsedRequest['headers'][$name]);
|
371 |
+
}
|
372 |
+
}
|
373 |
+
|
374 |
+
return $parsedRequest;
|
375 |
+
}
|
376 |
+
|
377 |
+
private function parseRequest(RequestInterface $request)
|
378 |
+
{
|
379 |
+
// Clean up any previously set headers.
|
380 |
+
/** @var RequestInterface $request */
|
381 |
+
$request = $request
|
382 |
+
->withoutHeader('X-Amz-Date')
|
383 |
+
->withoutHeader('Date')
|
384 |
+
->withoutHeader('Authorization');
|
385 |
+
$uri = $request->getUri();
|
386 |
+
|
387 |
+
return [
|
388 |
+
'method' => $request->getMethod(),
|
389 |
+
'path' => $uri->getPath(),
|
390 |
+
'query' => Psr7\parse_query($uri->getQuery()),
|
391 |
+
'uri' => $uri,
|
392 |
+
'headers' => $request->getHeaders(),
|
393 |
+
'body' => $request->getBody(),
|
394 |
+
'version' => $request->getProtocolVersion()
|
395 |
+
];
|
396 |
+
}
|
397 |
+
|
398 |
+
private function buildRequest(array $req)
|
399 |
+
{
|
400 |
+
if ($req['query']) {
|
401 |
+
$req['uri'] = $req['uri']->withQuery(Psr7\build_query($req['query']));
|
402 |
+
}
|
403 |
+
|
404 |
+
return new Psr7\Request(
|
405 |
+
$req['method'],
|
406 |
+
$req['uri'],
|
407 |
+
$req['headers'],
|
408 |
+
$req['body'],
|
409 |
+
$req['version']
|
410 |
+
);
|
411 |
+
}
|
412 |
+
}
|
lib/Aws/Aws/Sns/Exception/InvalidSnsMessageException.php
ADDED
@@ -0,0 +1,9 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Sns\Exception;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Runtime exception thrown by the SNS Message Validator.
|
6 |
+
*/
|
7 |
+
class InvalidSnsMessageException extends \RuntimeException
|
8 |
+
{
|
9 |
+
}
|
lib/Aws/Aws/Sns/Exception/SnsException.php
ADDED
@@ -0,0 +1,9 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Sns\Exception;
|
3 |
+
|
4 |
+
use Aws\Exception\AwsException;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents an error interacting with the Amazon Simple Notification Service.
|
8 |
+
*/
|
9 |
+
class SnsException extends AwsException {}
|
lib/Aws/Aws/Sns/Message.php
ADDED
@@ -0,0 +1,156 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Sns;
|
3 |
+
|
4 |
+
use Psr\Http\Message\RequestInterface;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents an SNS message received over http(s).
|
8 |
+
*/
|
9 |
+
class Message implements \ArrayAccess, \IteratorAggregate
|
10 |
+
{
|
11 |
+
private static $requiredKeys = [
|
12 |
+
'Message',
|
13 |
+
'MessageId',
|
14 |
+
'Timestamp',
|
15 |
+
'TopicArn',
|
16 |
+
'Type',
|
17 |
+
'Signature',
|
18 |
+
['SigningCertURL', 'SigningCertUrl'],
|
19 |
+
'SignatureVersion',
|
20 |
+
];
|
21 |
+
|
22 |
+
private static $subscribeKeys = [
|
23 |
+
['SubscribeURL', 'SubscribeUrl'],
|
24 |
+
'Token'
|
25 |
+
];
|
26 |
+
|
27 |
+
/** @var array The message data */
|
28 |
+
private $data;
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Creates a Message object from the raw POST data
|
32 |
+
*
|
33 |
+
* @return Message
|
34 |
+
* @throws \RuntimeException If the POST data is absent, or not a valid JSON document
|
35 |
+
*/
|
36 |
+
public static function fromRawPostData()
|
37 |
+
{
|
38 |
+
// Make sure the SNS-provided header exists.
|
39 |
+
if (!isset($_SERVER['HTTP_X_AMZ_SNS_MESSAGE_TYPE'])) {
|
40 |
+
throw new \RuntimeException('SNS message type header not provided.');
|
41 |
+
}
|
42 |
+
|
43 |
+
// Read the raw POST data and JSON-decode it into a message.
|
44 |
+
return self::fromJsonString(file_get_contents('php://input'));
|
45 |
+
}
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Creates a Message object from a PSR-7 Request or ServerRequest object.
|
49 |
+
*
|
50 |
+
* @param RequestInterface $request
|
51 |
+
* @return Message
|
52 |
+
*/
|
53 |
+
public static function fromPsrRequest(RequestInterface $request)
|
54 |
+
{
|
55 |
+
return self::fromJsonString($request->getBody());
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Creates a Message object from a JSON-decodable string.
|
60 |
+
*
|
61 |
+
* @param string $requestBody
|
62 |
+
* @return Message
|
63 |
+
*/
|
64 |
+
private static function fromJsonString($requestBody)
|
65 |
+
{
|
66 |
+
$data = json_decode($requestBody, true);
|
67 |
+
if (JSON_ERROR_NONE !== json_last_error() || !is_array($data)) {
|
68 |
+
throw new \RuntimeException('Invalid POST data.');
|
69 |
+
}
|
70 |
+
|
71 |
+
return new Message($data);
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Creates a Message object from an array of raw message data.
|
76 |
+
*
|
77 |
+
* @param array $data The message data.
|
78 |
+
*
|
79 |
+
* @throws \InvalidArgumentException If a valid type is not provided or
|
80 |
+
* there are other required keys missing.
|
81 |
+
*/
|
82 |
+
public function __construct(array $data)
|
83 |
+
{
|
84 |
+
// Ensure that all the required keys for the message's type are present.
|
85 |
+
$this->validateRequiredKeys($data, self::$requiredKeys);
|
86 |
+
if ($data['Type'] === 'SubscriptionConfirmation'
|
87 |
+
|| $data['Type'] === 'UnsubscribeConfirmation'
|
88 |
+
) {
|
89 |
+
$this->validateRequiredKeys($data, self::$subscribeKeys);
|
90 |
+
}
|
91 |
+
|
92 |
+
$this->data = $data;
|
93 |
+
}
|
94 |
+
|
95 |
+
public function getIterator()
|
96 |
+
{
|
97 |
+
return new \ArrayIterator($this->data);
|
98 |
+
}
|
99 |
+
|
100 |
+
public function offsetExists($key)
|
101 |
+
{
|
102 |
+
return isset($this->data[$key]);
|
103 |
+
}
|
104 |
+
|
105 |
+
public function offsetGet($key)
|
106 |
+
{
|
107 |
+
return isset($this->data[$key]) ? $this->data[$key] : null;
|
108 |
+
}
|
109 |
+
|
110 |
+
public function offsetSet($key, $value)
|
111 |
+
{
|
112 |
+
$this->data[$key] = $value;
|
113 |
+
}
|
114 |
+
|
115 |
+
public function offsetUnset($key)
|
116 |
+
{
|
117 |
+
unset($this->data[$key]);
|
118 |
+
}
|
119 |
+
|
120 |
+
/**
|
121 |
+
* Get all the message data as a plain array.
|
122 |
+
*
|
123 |
+
* @return array
|
124 |
+
*/
|
125 |
+
public function toArray()
|
126 |
+
{
|
127 |
+
return $this->data;
|
128 |
+
}
|
129 |
+
|
130 |
+
private function validateRequiredKeys(array $data, array $keys)
|
131 |
+
{
|
132 |
+
foreach ($keys as $key) {
|
133 |
+
$keyIsArray = is_array($key);
|
134 |
+
if (!$keyIsArray) {
|
135 |
+
$found = isset($data[$key]);
|
136 |
+
} else {
|
137 |
+
$found = false;
|
138 |
+
foreach ($key as $keyOption) {
|
139 |
+
if (isset($data[$keyOption])) {
|
140 |
+
$found = true;
|
141 |
+
break;
|
142 |
+
}
|
143 |
+
}
|
144 |
+
}
|
145 |
+
|
146 |
+
if (!$found) {
|
147 |
+
if ($keyIsArray) {
|
148 |
+
$key = $key[0];
|
149 |
+
}
|
150 |
+
throw new \InvalidArgumentException(
|
151 |
+
"\"{$key}\" is required to verify the SNS Message."
|
152 |
+
);
|
153 |
+
}
|
154 |
+
}
|
155 |
+
}
|
156 |
+
}
|
lib/Aws/Aws/Sns/MessageValidator.php
ADDED
@@ -0,0 +1,190 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Sns;
|
3 |
+
|
4 |
+
use Aws\Sns\Exception\InvalidSnsMessageException;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Uses openssl to verify SNS messages to ensure that they were sent by AWS.
|
8 |
+
*/
|
9 |
+
class MessageValidator
|
10 |
+
{
|
11 |
+
const SIGNATURE_VERSION_1 = '1';
|
12 |
+
|
13 |
+
/**
|
14 |
+
* @var callable Callable used to download the certificate content.
|
15 |
+
*/
|
16 |
+
private $certClient;
|
17 |
+
|
18 |
+
/** @var string */
|
19 |
+
private $hostPattern;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @var string A pattern that will match all regional SNS endpoints, e.g.:
|
23 |
+
* - sns.<region>.amazonaws.com (AWS)
|
24 |
+
* - sns.us-gov-west-1.amazonaws.com (AWS GovCloud)
|
25 |
+
* - sns.cn-north-1.amazonaws.com.cn (AWS China)
|
26 |
+
*/
|
27 |
+
private static $defaultHostPattern
|
28 |
+
= '/^sns\.[a-zA-Z0-9\-]{3,}\.amazonaws\.com(\.cn)?$/';
|
29 |
+
|
30 |
+
private static function isLambdaStyle(Message $message)
|
31 |
+
{
|
32 |
+
return isset($message['SigningCertUrl']);
|
33 |
+
}
|
34 |
+
|
35 |
+
private static function convertLambdaMessage(Message $lambdaMessage)
|
36 |
+
{
|
37 |
+
$keyReplacements = [
|
38 |
+
'SigningCertUrl' => 'SigningCertURL',
|
39 |
+
'SubscribeUrl' => 'SubscribeURL',
|
40 |
+
'UnsubscribeUrl' => 'UnsubscribeURL',
|
41 |
+
];
|
42 |
+
|
43 |
+
$message = clone $lambdaMessage;
|
44 |
+
foreach ($keyReplacements as $lambdaKey => $canonicalKey) {
|
45 |
+
if (isset($message[$lambdaKey])) {
|
46 |
+
$message[$canonicalKey] = $message[$lambdaKey];
|
47 |
+
unset($message[$lambdaKey]);
|
48 |
+
}
|
49 |
+
}
|
50 |
+
|
51 |
+
return $message;
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Constructs the Message Validator object and ensures that openssl is
|
56 |
+
* installed.
|
57 |
+
*
|
58 |
+
* @param callable $certClient Callable used to download the certificate.
|
59 |
+
* Should have the following function signature:
|
60 |
+
* `function (string $certUrl) : string $certContent`
|
61 |
+
* @param string $hostNamePattern
|
62 |
+
*/
|
63 |
+
public function __construct(
|
64 |
+
callable $certClient = null,
|
65 |
+
$hostNamePattern = ''
|
66 |
+
) {
|
67 |
+
$this->certClient = $certClient ?: 'file_get_contents';
|
68 |
+
$this->hostPattern = $hostNamePattern ?: self::$defaultHostPattern;
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* Validates a message from SNS to ensure that it was delivered by AWS.
|
73 |
+
*
|
74 |
+
* @param Message $message Message to validate.
|
75 |
+
*
|
76 |
+
* @throws InvalidSnsMessageException If the cert cannot be retrieved or its
|
77 |
+
* source verified, or the message
|
78 |
+
* signature is invalid.
|
79 |
+
*/
|
80 |
+
public function validate(Message $message)
|
81 |
+
{
|
82 |
+
if (self::isLambdaStyle($message)) {
|
83 |
+
$message = self::convertLambdaMessage($message);
|
84 |
+
}
|
85 |
+
|
86 |
+
// Get the certificate.
|
87 |
+
$this->validateUrl($message['SigningCertURL']);
|
88 |
+
$certificate = call_user_func($this->certClient, $message['SigningCertURL']);
|
89 |
+
if ($certificate === false) {
|
90 |
+
throw new InvalidSnsMessageException(
|
91 |
+
"Cannot get the certificate from \"{$message['SigningCertURL']}\"."
|
92 |
+
);
|
93 |
+
}
|
94 |
+
|
95 |
+
// Extract the public key.
|
96 |
+
$key = openssl_get_publickey($certificate);
|
97 |
+
if (!$key) {
|
98 |
+
throw new InvalidSnsMessageException(
|
99 |
+
'Cannot get the public key from the certificate.'
|
100 |
+
);
|
101 |
+
}
|
102 |
+
|
103 |
+
// Verify the signature of the message.
|
104 |
+
$content = $this->getStringToSign($message);
|
105 |
+
$signature = base64_decode($message['Signature']);
|
106 |
+
if (openssl_verify($content, $signature, $key, OPENSSL_ALGO_SHA1) != 1) {
|
107 |
+
throw new InvalidSnsMessageException(
|
108 |
+
'The message signature is invalid.'
|
109 |
+
);
|
110 |
+
}
|
111 |
+
}
|
112 |
+
|
113 |
+
/**
|
114 |
+
* Determines if a message is valid and that is was delivered by AWS. This
|
115 |
+
* method does not throw exceptions and returns a simple boolean value.
|
116 |
+
*
|
117 |
+
* @param Message $message The message to validate
|
118 |
+
*
|
119 |
+
* @return bool
|
120 |
+
*/
|
121 |
+
public function isValid(Message $message)
|
122 |
+
{
|
123 |
+
try {
|
124 |
+
$this->validate($message);
|
125 |
+
return true;
|
126 |
+
} catch (InvalidSnsMessageException $e) {
|
127 |
+
return false;
|
128 |
+
}
|
129 |
+
}
|
130 |
+
|
131 |
+
/**
|
132 |
+
* Builds string-to-sign according to the SNS message spec.
|
133 |
+
*
|
134 |
+
* @param Message $message Message for which to build the string-to-sign.
|
135 |
+
*
|
136 |
+
* @return string
|
137 |
+
* @link http://docs.aws.amazon.com/sns/latest/gsg/SendMessageToHttp.verify.signature.html
|
138 |
+
*/
|
139 |
+
public function getStringToSign(Message $message)
|
140 |
+
{
|
141 |
+
static $signableKeys = [
|
142 |
+
'Message',
|
143 |
+
'MessageId',
|
144 |
+
'Subject',
|
145 |
+
'SubscribeURL',
|
146 |
+
'Timestamp',
|
147 |
+
'Token',
|
148 |
+
'TopicArn',
|
149 |
+
'Type',
|
150 |
+
];
|
151 |
+
|
152 |
+
if ($message['SignatureVersion'] !== self::SIGNATURE_VERSION_1) {
|
153 |
+
throw new InvalidSnsMessageException(
|
154 |
+
"The SignatureVersion \"{$message['SignatureVersion']}\" is not supported."
|
155 |
+
);
|
156 |
+
}
|
157 |
+
|
158 |
+
$stringToSign = '';
|
159 |
+
foreach ($signableKeys as $key) {
|
160 |
+
if (isset($message[$key])) {
|
161 |
+
$stringToSign .= "{$key}\n{$message[$key]}\n";
|
162 |
+
}
|
163 |
+
}
|
164 |
+
|
165 |
+
return $stringToSign;
|
166 |
+
}
|
167 |
+
|
168 |
+
/**
|
169 |
+
* Ensures that the URL of the certificate is one belonging to AWS, and not
|
170 |
+
* just something from the amazonaws domain, which could include S3 buckets.
|
171 |
+
*
|
172 |
+
* @param string $url Certificate URL
|
173 |
+
*
|
174 |
+
* @throws InvalidSnsMessageException if the cert url is invalid.
|
175 |
+
*/
|
176 |
+
private function validateUrl($url)
|
177 |
+
{
|
178 |
+
$parsed = parse_url($url);
|
179 |
+
if (empty($parsed['scheme'])
|
180 |
+
|| empty($parsed['host'])
|
181 |
+
|| $parsed['scheme'] !== 'https'
|
182 |
+
|| substr($url, -4) !== '.pem'
|
183 |
+
|| !preg_match($this->hostPattern, $parsed['host'])
|
184 |
+
) {
|
185 |
+
throw new InvalidSnsMessageException(
|
186 |
+
'The certificate is located on an invalid domain.'
|
187 |
+
);
|
188 |
+
}
|
189 |
+
}
|
190 |
+
}
|
lib/Aws/Aws/Sns/SnsClient.php
ADDED
@@ -0,0 +1,76 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws\Sns;
|
3 |
+
|
4 |
+
use Aws\AwsClient;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* This client is used to interact with the **Amazon Simple Notification Service (Amazon SNS)**.
|
8 |
+
*
|
9 |
+
* @method \Aws\Result addPermission(array $args = [])
|
10 |
+
* @method \GuzzleHttp\Promise\Promise addPermissionAsync(array $args = [])
|
11 |
+
* @method \Aws\Result checkIfPhoneNumberIsOptedOut(array $args = [])
|
12 |
+
* @method \GuzzleHttp\Promise\Promise checkIfPhoneNumberIsOptedOutAsync(array $args = [])
|
13 |
+
* @method \Aws\Result confirmSubscription(array $args = [])
|
14 |
+
* @method \GuzzleHttp\Promise\Promise confirmSubscriptionAsync(array $args = [])
|
15 |
+
* @method \Aws\Result createPlatformApplication(array $args = [])
|
16 |
+
* @method \GuzzleHttp\Promise\Promise createPlatformApplicationAsync(array $args = [])
|
17 |
+
* @method \Aws\Result createPlatformEndpoint(array $args = [])
|
18 |
+
* @method \GuzzleHttp\Promise\Promise createPlatformEndpointAsync(array $args = [])
|
19 |
+
* @method \Aws\Result createTopic(array $args = [])
|
20 |
+
* @method \GuzzleHttp\Promise\Promise createTopicAsync(array $args = [])
|
21 |
+
* @method \Aws\Result deleteEndpoint(array $args = [])
|
22 |
+
* @method \GuzzleHttp\Promise\Promise deleteEndpointAsync(array $args = [])
|
23 |
+
* @method \Aws\Result deletePlatformApplication(array $args = [])
|
24 |
+
* @method \GuzzleHttp\Promise\Promise deletePlatformApplicationAsync(array $args = [])
|
25 |
+
* @method \Aws\Result deleteTopic(array $args = [])
|
26 |
+
* @method \GuzzleHttp\Promise\Promise deleteTopicAsync(array $args = [])
|
27 |
+
* @method \Aws\Result getEndpointAttributes(array $args = [])
|
28 |
+
* @method \GuzzleHttp\Promise\Promise getEndpointAttributesAsync(array $args = [])
|
29 |
+
* @method \Aws\Result getPlatformApplicationAttributes(array $args = [])
|
30 |
+
* @method \GuzzleHttp\Promise\Promise getPlatformApplicationAttributesAsync(array $args = [])
|
31 |
+
* @method \Aws\Result getSMSAttributes(array $args = [])
|
32 |
+
* @method \GuzzleHttp\Promise\Promise getSMSAttributesAsync(array $args = [])
|
33 |
+
* @method \Aws\Result getSubscriptionAttributes(array $args = [])
|
34 |
+
* @method \GuzzleHttp\Promise\Promise getSubscriptionAttributesAsync(array $args = [])
|
35 |
+
* @method \Aws\Result getTopicAttributes(array $args = [])
|
36 |
+
* @method \GuzzleHttp\Promise\Promise getTopicAttributesAsync(array $args = [])
|
37 |
+
* @method \Aws\Result listEndpointsByPlatformApplication(array $args = [])
|
38 |
+
* @method \GuzzleHttp\Promise\Promise listEndpointsByPlatformApplicationAsync(array $args = [])
|
39 |
+
* @method \Aws\Result listPhoneNumbersOptedOut(array $args = [])
|
40 |
+
* @method \GuzzleHttp\Promise\Promise listPhoneNumbersOptedOutAsync(array $args = [])
|
41 |
+
* @method \Aws\Result listPlatformApplications(array $args = [])
|
42 |
+
* @method \GuzzleHttp\Promise\Promise listPlatformApplicationsAsync(array $args = [])
|
43 |
+
* @method \Aws\Result listSubscriptions(array $args = [])
|
44 |
+
* @method \GuzzleHttp\Promise\Promise listSubscriptionsAsync(array $args = [])
|
45 |
+
* @method \Aws\Result listSubscriptionsByTopic(array $args = [])
|
46 |
+
* @method \GuzzleHttp\Promise\Promise listSubscriptionsByTopicAsync(array $args = [])
|
47 |
+
* @method \Aws\Result listTagsForResource(array $args = [])
|
48 |
+
* @method \GuzzleHttp\Promise\Promise listTagsForResourceAsync(array $args = [])
|
49 |
+
* @method \Aws\Result listTopics(array $args = [])
|
50 |
+
* @method \GuzzleHttp\Promise\Promise listTopicsAsync(array $args = [])
|
51 |
+
* @method \Aws\Result optInPhoneNumber(array $args = [])
|
52 |
+
* @method \GuzzleHttp\Promise\Promise optInPhoneNumberAsync(array $args = [])
|
53 |
+
* @method \Aws\Result publish(array $args = [])
|
54 |
+
* @method \GuzzleHttp\Promise\Promise publishAsync(array $args = [])
|
55 |
+
* @method \Aws\Result removePermission(array $args = [])
|
56 |
+
* @method \GuzzleHttp\Promise\Promise removePermissionAsync(array $args = [])
|
57 |
+
* @method \Aws\Result setEndpointAttributes(array $args = [])
|
58 |
+
* @method \GuzzleHttp\Promise\Promise setEndpointAttributesAsync(array $args = [])
|
59 |
+
* @method \Aws\Result setPlatformApplicationAttributes(array $args = [])
|
60 |
+
* @method \GuzzleHttp\Promise\Promise setPlatformApplicationAttributesAsync(array $args = [])
|
61 |
+
* @method \Aws\Result setSMSAttributes(array $args = [])
|
62 |
+
* @method \GuzzleHttp\Promise\Promise setSMSAttributesAsync(array $args = [])
|
63 |
+
* @method \Aws\Result setSubscriptionAttributes(array $args = [])
|
64 |
+
* @method \GuzzleHttp\Promise\Promise setSubscriptionAttributesAsync(array $args = [])
|
65 |
+
* @method \Aws\Result setTopicAttributes(array $args = [])
|
66 |
+
* @method \GuzzleHttp\Promise\Promise setTopicAttributesAsync(array $args = [])
|
67 |
+
* @method \Aws\Result subscribe(array $args = [])
|
68 |
+
* @method \GuzzleHttp\Promise\Promise subscribeAsync(array $args = [])
|
69 |
+
* @method \Aws\Result tagResource(array $args = [])
|
70 |
+
* @method \GuzzleHttp\Promise\Promise tagResourceAsync(array $args = [])
|
71 |
+
* @method \Aws\Result unsubscribe(array $args = [])
|
72 |
+
* @method \GuzzleHttp\Promise\Promise unsubscribeAsync(array $args = [])
|
73 |
+
* @method \Aws\Result untagResource(array $args = [])
|
74 |
+
* @method \GuzzleHttp\Promise\Promise untagResourceAsync(array $args = [])
|
75 |
+
*/
|
76 |
+
class SnsClient extends AwsClient {}
|
lib/Aws/Aws/TraceMiddleware.php
ADDED
@@ -0,0 +1,314 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws;
|
3 |
+
|
4 |
+
use Aws\Exception\AwsException;
|
5 |
+
use GuzzleHttp\Promise\RejectedPromise;
|
6 |
+
use Psr\Http\Message\RequestInterface;
|
7 |
+
use Psr\Http\Message\ResponseInterface;
|
8 |
+
use Psr\Http\Message\StreamInterface;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Traces state changes between middlewares.
|
12 |
+
*/
|
13 |
+
class TraceMiddleware
|
14 |
+
{
|
15 |
+
private $prevOutput;
|
16 |
+
private $prevInput;
|
17 |
+
private $config;
|
18 |
+
|
19 |
+
private static $authHeaders = [
|
20 |
+
'X-Amz-Security-Token' => '[TOKEN]',
|
21 |
+
];
|
22 |
+
|
23 |
+
private static $authStrings = [
|
24 |
+
// S3Signature
|
25 |
+
'/AWSAccessKeyId=[A-Z0-9]{20}&/i' => 'AWSAccessKeyId=[KEY]&',
|
26 |
+
// SignatureV4 Signature and S3Signature
|
27 |
+
'/Signature=.+/i' => 'Signature=[SIGNATURE]',
|
28 |
+
// SignatureV4 access key ID
|
29 |
+
'/Credential=[A-Z0-9]{20}\//i' => 'Credential=[KEY]/',
|
30 |
+
// S3 signatures
|
31 |
+
'/AWS [A-Z0-9]{20}:.+/' => 'AWS AKI[KEY]:[SIGNATURE]',
|
32 |
+
// STS Presigned URLs
|
33 |
+
'/X-Amz-Security-Token=[^&]+/i' => 'X-Amz-Security-Token=[TOKEN]',
|
34 |
+
// Crypto *Stream Keys
|
35 |
+
'/\["key.{27,36}Stream.{9}\]=>\s+.{7}\d{2}\) "\X{16,64}"/U' => '["key":[CONTENT KEY]]',
|
36 |
+
];
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Configuration array can contain the following key value pairs.
|
40 |
+
*
|
41 |
+
* - logfn: (callable) Function that is invoked with log messages. By
|
42 |
+
* default, PHP's "echo" function will be utilized.
|
43 |
+
* - stream_size: (int) When the size of a stream is greater than this
|
44 |
+
* number, the stream data will not be logged. Set to "0" to not log any
|
45 |
+
* stream data.
|
46 |
+
* - scrub_auth: (bool) Set to false to disable the scrubbing of auth data
|
47 |
+
* from the logged messages.
|
48 |
+
* - http: (bool) Set to false to disable the "debug" feature of lower
|
49 |
+
* level HTTP adapters (e.g., verbose curl output).
|
50 |
+
* - auth_strings: (array) A mapping of authentication string regular
|
51 |
+
* expressions to scrubbed strings. These mappings are passed directly to
|
52 |
+
* preg_replace (e.g., preg_replace($key, $value, $debugOutput) if
|
53 |
+
* "scrub_auth" is set to true.
|
54 |
+
* - auth_headers: (array) A mapping of header names known to contain
|
55 |
+
* sensitive data to what the scrubbed value should be. The value of any
|
56 |
+
* headers contained in this array will be replaced with the if
|
57 |
+
* "scrub_auth" is set to true.
|
58 |
+
*/
|
59 |
+
public function __construct(array $config = [])
|
60 |
+
{
|
61 |
+
$this->config = $config + [
|
62 |
+
'logfn' => function ($value) { echo $value; },
|
63 |
+
'stream_size' => 524288,
|
64 |
+
'scrub_auth' => true,
|
65 |
+
'http' => true,
|
66 |
+
'auth_strings' => [],
|
67 |
+
'auth_headers' => [],
|
68 |
+
];
|
69 |
+
|
70 |
+
$this->config['auth_strings'] += self::$authStrings;
|
71 |
+
$this->config['auth_headers'] += self::$authHeaders;
|
72 |
+
}
|
73 |
+
|
74 |
+
public function __invoke($step, $name)
|
75 |
+
{
|
76 |
+
$this->prevOutput = $this->prevInput = [];
|
77 |
+
|
78 |
+
return function (callable $next) use ($step, $name) {
|
79 |
+
return function (
|
80 |
+
CommandInterface $command,
|
81 |
+
RequestInterface $request = null
|
82 |
+
) use ($next, $step, $name) {
|
83 |
+
$this->createHttpDebug($command);
|
84 |
+
$start = microtime(true);
|
85 |
+
$this->stepInput([
|
86 |
+
'step' => $step,
|
87 |
+
'name' => $name,
|
88 |
+
'request' => $this->requestArray($request),
|
89 |
+
'command' => $this->commandArray($command)
|
90 |
+
]);
|
91 |
+
|
92 |
+
return $next($command, $request)->then(
|
93 |
+
function ($value) use ($step, $name, $command, $start) {
|
94 |
+
$this->flushHttpDebug($command);
|
95 |
+
$this->stepOutput($start, [
|
96 |
+
'step' => $step,
|
97 |
+
'name' => $name,
|
98 |
+
'result' => $this->resultArray($value),
|
99 |
+
'error' => null
|
100 |
+
]);
|
101 |
+
return $value;
|
102 |
+
},
|
103 |
+
function ($reason) use ($step, $name, $start, $command) {
|
104 |
+
$this->flushHttpDebug($command);
|
105 |
+
$this->stepOutput($start, [
|
106 |
+
'step' => $step,
|
107 |
+
'name' => $name,
|
108 |
+
'result' => null,
|
109 |
+
'error' => $this->exceptionArray($reason)
|
110 |
+
]);
|
111 |
+
return new RejectedPromise($reason);
|
112 |
+
}
|
113 |
+
);
|
114 |
+
};
|
115 |
+
};
|
116 |
+
}
|
117 |
+
|
118 |
+
private function stepInput($entry)
|
119 |
+
{
|
120 |
+
static $keys = ['command', 'request'];
|
121 |
+
$this->compareStep($this->prevInput, $entry, '-> Entering', $keys);
|
122 |
+
$this->write("\n");
|
123 |
+
$this->prevInput = $entry;
|
124 |
+
}
|
125 |
+
|
126 |
+
private function stepOutput($start, $entry)
|
127 |
+
{
|
128 |
+
static $keys = ['result', 'error'];
|
129 |
+
$this->compareStep($this->prevOutput, $entry, '<- Leaving', $keys);
|
130 |
+
$totalTime = microtime(true) - $start;
|
131 |
+
$this->write(" Inclusive step time: " . $totalTime . "\n\n");
|
132 |
+
$this->prevOutput = $entry;
|
133 |
+
}
|
134 |
+
|
135 |
+
private function compareStep(array $a, array $b, $title, array $keys)
|
136 |
+
{
|
137 |
+
$changes = [];
|
138 |
+
foreach ($keys as $key) {
|
139 |
+
$av = isset($a[$key]) ? $a[$key] : null;
|
140 |
+
$bv = isset($b[$key]) ? $b[$key] : null;
|
141 |
+
$this->compareArray($av, $bv, $key, $changes);
|
142 |
+
}
|
143 |
+
$str = "\n{$title} step {$b['step']}, name '{$b['name']}'";
|
144 |
+
$str .= "\n" . str_repeat('-', strlen($str) - 1) . "\n\n ";
|
145 |
+
$str .= $changes
|
146 |
+
? implode("\n ", str_replace("\n", "\n ", $changes))
|
147 |
+
: 'no changes';
|
148 |
+
$this->write($str . "\n");
|
149 |
+
}
|
150 |
+
|
151 |
+
private function commandArray(CommandInterface $cmd)
|
152 |
+
{
|
153 |
+
return [
|
154 |
+
'instance' => spl_object_hash($cmd),
|
155 |
+
'name' => $cmd->getName(),
|
156 |
+
'params' => $cmd->toArray()
|
157 |
+
];
|
158 |
+
}
|
159 |
+
|
160 |
+
private function requestArray(RequestInterface $request = null)
|
161 |
+
{
|
162 |
+
return !$request ? [] : array_filter([
|
163 |
+
'instance' => spl_object_hash($request),
|
164 |
+
'method' => $request->getMethod(),
|
165 |
+
'headers' => $this->redactHeaders($request->getHeaders()),
|
166 |
+
'body' => $this->streamStr($request->getBody()),
|
167 |
+
'scheme' => $request->getUri()->getScheme(),
|
168 |
+
'port' => $request->getUri()->getPort(),
|
169 |
+
'path' => $request->getUri()->getPath(),
|
170 |
+
'query' => $request->getUri()->getQuery(),
|
171 |
+
]);
|
172 |
+
}
|
173 |
+
|
174 |
+
private function responseArray(ResponseInterface $response = null)
|
175 |
+
{
|
176 |
+
return !$response ? [] : [
|
177 |
+
'instance' => spl_object_hash($response),
|
178 |
+
'statusCode' => $response->getStatusCode(),
|
179 |
+
'headers' => $this->redactHeaders($response->getHeaders()),
|
180 |
+
'body' => $this->streamStr($response->getBody())
|
181 |
+
];
|
182 |
+
}
|
183 |
+
|
184 |
+
private function resultArray($value)
|
185 |
+
{
|
186 |
+
return $value instanceof ResultInterface
|
187 |
+
? [
|
188 |
+
'instance' => spl_object_hash($value),
|
189 |
+
'data' => $value->toArray()
|
190 |
+
] : $value;
|
191 |
+
}
|
192 |
+
|
193 |
+
private function exceptionArray($e)
|
194 |
+
{
|
195 |
+
if (!($e instanceof \Exception)) {
|
196 |
+
return $e;
|
197 |
+
}
|
198 |
+
|
199 |
+
$result = [
|
200 |
+
'instance' => spl_object_hash($e),
|
201 |
+
'class' => get_class($e),
|
202 |
+
'message' => $e->getMessage(),
|
203 |
+
'file' => $e->getFile(),
|
204 |
+
'line' => $e->getLine(),
|
205 |
+
'trace' => $e->getTraceAsString(),
|
206 |
+
];
|
207 |
+
|
208 |
+
if ($e instanceof AwsException) {
|
209 |
+
$result += [
|
210 |
+
'type' => $e->getAwsErrorType(),
|
211 |
+
'code' => $e->getAwsErrorCode(),
|
212 |
+
'requestId' => $e->getAwsRequestId(),
|
213 |
+
'statusCode' => $e->getStatusCode(),
|
214 |
+
'result' => $this->resultArray($e->getResult()),
|
215 |
+
'request' => $this->requestArray($e->getRequest()),
|
216 |
+
'response' => $this->responseArray($e->getResponse()),
|
217 |
+
];
|
218 |
+
}
|
219 |
+
|
220 |
+
return $result;
|
221 |
+
}
|
222 |
+
|
223 |
+
private function compareArray($a, $b, $path, array &$diff)
|
224 |
+
{
|
225 |
+
if ($a === $b) {
|
226 |
+
return;
|
227 |
+
}
|
228 |
+
|
229 |
+
if (is_array($a)) {
|
230 |
+
$b = (array) $b;
|
231 |
+
$keys = array_unique(array_merge(array_keys($a), array_keys($b)));
|
232 |
+
foreach ($keys as $k) {
|
233 |
+
if (!array_key_exists($k, $a)) {
|
234 |
+
$this->compareArray(null, $b[$k], "{$path}.{$k}", $diff);
|
235 |
+
} elseif (!array_key_exists($k, $b)) {
|
236 |
+
$this->compareArray($a[$k], null, "{$path}.{$k}", $diff);
|
237 |
+
} else {
|
238 |
+
$this->compareArray($a[$k], $b[$k], "{$path}.{$k}", $diff);
|
239 |
+
}
|
240 |
+
}
|
241 |
+
} elseif ($a !== null && $b === null) {
|
242 |
+
$diff[] = "{$path} was unset";
|
243 |
+
} elseif ($a === null && $b !== null) {
|
244 |
+
$diff[] = sprintf("%s was set to %s", $path, $this->str($b));
|
245 |
+
} else {
|
246 |
+
$diff[] = sprintf("%s changed from %s to %s", $path, $this->str($a), $this->str($b));
|
247 |
+
}
|
248 |
+
}
|
249 |
+
|
250 |
+
private function str($value)
|
251 |
+
{
|
252 |
+
if (is_scalar($value)) {
|
253 |
+
return (string) $value;
|
254 |
+
}
|
255 |
+
|
256 |
+
if ($value instanceof \Exception) {
|
257 |
+
$value = $this->exceptionArray($value);
|
258 |
+
}
|
259 |
+
|
260 |
+
ob_start();
|
261 |
+
var_dump($value);
|
262 |
+
return ob_get_clean();
|
263 |
+
}
|
264 |
+
|
265 |
+
private function streamStr(StreamInterface $body)
|
266 |
+
{
|
267 |
+
return $body->getSize() < $this->config['stream_size']
|
268 |
+
? (string) $body
|
269 |
+
: 'stream(size=' . $body->getSize() . ')';
|
270 |
+
}
|
271 |
+
|
272 |
+
private function createHttpDebug(CommandInterface $command)
|
273 |
+
{
|
274 |
+
if ($this->config['http'] && !isset($command['@http']['debug'])) {
|
275 |
+
$command['@http']['debug'] = fopen('php://temp', 'w+');
|
276 |
+
}
|
277 |
+
}
|
278 |
+
|
279 |
+
private function flushHttpDebug(CommandInterface $command)
|
280 |
+
{
|
281 |
+
if ($res = $command['@http']['debug']) {
|
282 |
+
rewind($res);
|
283 |
+
$this->write(stream_get_contents($res));
|
284 |
+
fclose($res);
|
285 |
+
$command['@http']['debug'] = null;
|
286 |
+
}
|
287 |
+
}
|
288 |
+
|
289 |
+
private function write($value)
|
290 |
+
{
|
291 |
+
if ($this->config['scrub_auth']) {
|
292 |
+
foreach ($this->config['auth_strings'] as $pattern => $replacement) {
|
293 |
+
$value = preg_replace_callback(
|
294 |
+
$pattern,
|
295 |
+
function ($matches) use ($replacement) {
|
296 |
+
return $replacement;
|
297 |
+
},
|
298 |
+
$value
|
299 |
+
);
|
300 |
+
}
|
301 |
+
}
|
302 |
+
|
303 |
+
call_user_func($this->config['logfn'], $value);
|
304 |
+
}
|
305 |
+
|
306 |
+
private function redactHeaders(array $headers)
|
307 |
+
{
|
308 |
+
if ($this->config['scrub_auth']) {
|
309 |
+
$headers = $this->config['auth_headers'] + $headers;
|
310 |
+
}
|
311 |
+
|
312 |
+
return $headers;
|
313 |
+
}
|
314 |
+
}
|
lib/Aws/Aws/Waiter.php
ADDED
@@ -0,0 +1,262 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws;
|
3 |
+
|
4 |
+
use Aws\Exception\AwsException;
|
5 |
+
use GuzzleHttp\Promise;
|
6 |
+
use GuzzleHttp\Promise\PromisorInterface;
|
7 |
+
use GuzzleHttp\Promise\RejectedPromise;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* "Waiters" are associated with an AWS resource (e.g., EC2 instance), and poll
|
11 |
+
* that resource and until it is in a particular state.
|
12 |
+
|
13 |
+
* The Waiter object produces a promise that is either a.) resolved once the
|
14 |
+
* waiting conditions are met, or b.) rejected if the waiting conditions cannot
|
15 |
+
* be met or has exceeded the number of allowed attempts at meeting the
|
16 |
+
* conditions. You can use waiters in a blocking or non-blocking way, depending
|
17 |
+
* on whether you call wait() on the promise.
|
18 |
+
|
19 |
+
* The configuration for the waiter must include information about the operation
|
20 |
+
* and the conditions for wait completion.
|
21 |
+
*/
|
22 |
+
class Waiter implements PromisorInterface
|
23 |
+
{
|
24 |
+
/** @var AwsClientInterface Client used to execute each attempt. */
|
25 |
+
private $client;
|
26 |
+
|
27 |
+
/** @var string Name of the waiter. */
|
28 |
+
private $name;
|
29 |
+
|
30 |
+
/** @var array Params to use with each attempt operation. */
|
31 |
+
private $args;
|
32 |
+
|
33 |
+
/** @var array Waiter configuration. */
|
34 |
+
private $config;
|
35 |
+
|
36 |
+
/** @var array Default configuration options. */
|
37 |
+
private static $defaults = ['initDelay' => 0, 'before' => null];
|
38 |
+
|
39 |
+
/** @var array Required configuration options. */
|
40 |
+
private static $required = [
|
41 |
+
'acceptors',
|
42 |
+
'delay',
|
43 |
+
'maxAttempts',
|
44 |
+
'operation',
|
45 |
+
];
|
46 |
+
|
47 |
+
/**
|
48 |
+
* The array of configuration options include:
|
49 |
+
*
|
50 |
+
* - acceptors: (array) Array of acceptor options
|
51 |
+
* - delay: (int) Number of seconds to delay between attempts
|
52 |
+
* - maxAttempts: (int) Maximum number of attempts before failing
|
53 |
+
* - operation: (string) Name of the API operation to use for polling
|
54 |
+
* - before: (callable) Invoked before attempts. Accepts command and tries.
|
55 |
+
*
|
56 |
+
* @param AwsClientInterface $client Client used to execute commands.
|
57 |
+
* @param string $name Waiter name.
|
58 |
+
* @param array $args Command arguments.
|
59 |
+
* @param array $config Waiter config that overrides defaults.
|
60 |
+
*
|
61 |
+
* @throws \InvalidArgumentException if the configuration is incomplete.
|
62 |
+
*/
|
63 |
+
public function __construct(
|
64 |
+
AwsClientInterface $client,
|
65 |
+
$name,
|
66 |
+
array $args = [],
|
67 |
+
array $config = []
|
68 |
+
) {
|
69 |
+
$this->client = $client;
|
70 |
+
$this->name = $name;
|
71 |
+
$this->args = $args;
|
72 |
+
|
73 |
+
// Prepare and validate config.
|
74 |
+
$this->config = $config + self::$defaults;
|
75 |
+
foreach (self::$required as $key) {
|
76 |
+
if (!isset($this->config[$key])) {
|
77 |
+
throw new \InvalidArgumentException(
|
78 |
+
'The provided waiter configuration was incomplete.'
|
79 |
+
);
|
80 |
+
}
|
81 |
+
}
|
82 |
+
if ($this->config['before'] && !is_callable($this->config['before'])) {
|
83 |
+
throw new \InvalidArgumentException(
|
84 |
+
'The provided "before" callback is not callable.'
|
85 |
+
);
|
86 |
+
}
|
87 |
+
}
|
88 |
+
|
89 |
+
public function promise()
|
90 |
+
{
|
91 |
+
return Promise\coroutine(function () {
|
92 |
+
$name = $this->config['operation'];
|
93 |
+
for ($state = 'retry', $attempt = 1; $state === 'retry'; $attempt++) {
|
94 |
+
// Execute the operation.
|
95 |
+
$args = $this->getArgsForAttempt($attempt);
|
96 |
+
$command = $this->client->getCommand($name, $args);
|
97 |
+
try {
|
98 |
+
if ($this->config['before']) {
|
99 |
+
$this->config['before']($command, $attempt);
|
100 |
+
}
|
101 |
+
$result = (yield $this->client->executeAsync($command));
|
102 |
+
} catch (AwsException $e) {
|
103 |
+
$result = $e;
|
104 |
+
}
|
105 |
+
|
106 |
+
// Determine the waiter's state and what to do next.
|
107 |
+
$state = $this->determineState($result);
|
108 |
+
if ($state === 'success') {
|
109 |
+
yield $command;
|
110 |
+
} elseif ($state === 'failed') {
|
111 |
+
$msg = "The {$this->name} waiter entered a failure state.";
|
112 |
+
if ($result instanceof \Exception) {
|
113 |
+
$msg .= ' Reason: ' . $result->getMessage();
|
114 |
+
}
|
115 |
+
yield new RejectedPromise(new \RuntimeException($msg));
|
116 |
+
} elseif ($state === 'retry'
|
117 |
+
&& $attempt >= $this->config['maxAttempts']
|
118 |
+
) {
|
119 |
+
$state = 'failed';
|
120 |
+
yield new RejectedPromise(new \RuntimeException(
|
121 |
+
"The {$this->name} waiter failed after attempt #{$attempt}."
|
122 |
+
));
|
123 |
+
}
|
124 |
+
}
|
125 |
+
});
|
126 |
+
}
|
127 |
+
|
128 |
+
/**
|
129 |
+
* Gets the operation arguments for the attempt, including the delay.
|
130 |
+
*
|
131 |
+
* @param $attempt Number of the current attempt.
|
132 |
+
*
|
133 |
+
* @return mixed integer
|
134 |
+
*/
|
135 |
+
private function getArgsForAttempt($attempt)
|
136 |
+
{
|
137 |
+
$args = $this->args;
|
138 |
+
|
139 |
+
// Determine the delay.
|
140 |
+
$delay = ($attempt === 1)
|
141 |
+
? $this->config['initDelay']
|
142 |
+
: $this->config['delay'];
|
143 |
+
if (is_callable($delay)) {
|
144 |
+
$delay = $delay($attempt);
|
145 |
+
}
|
146 |
+
|
147 |
+
// Set the delay. (Note: handlers except delay in milliseconds.)
|
148 |
+
if (!isset($args['@http'])) {
|
149 |
+
$args['@http'] = [];
|
150 |
+
}
|
151 |
+
$args['@http']['delay'] = $delay * 1000;
|
152 |
+
|
153 |
+
return $args;
|
154 |
+
}
|
155 |
+
|
156 |
+
/**
|
157 |
+
* Determines the state of the waiter attempt, based on the result of
|
158 |
+
* polling the resource. A waiter can have the state of "success", "failed",
|
159 |
+
* or "retry".
|
160 |
+
*
|
161 |
+
* @param mixed $result
|
162 |
+
*
|
163 |
+
* @return string Will be "success", "failed", or "retry"
|
164 |
+
*/
|
165 |
+
private function determineState($result)
|
166 |
+
{
|
167 |
+
foreach ($this->config['acceptors'] as $acceptor) {
|
168 |
+
$matcher = 'matches' . ucfirst($acceptor['matcher']);
|
169 |
+
if ($this->{$matcher}($result, $acceptor)) {
|
170 |
+
return $acceptor['state'];
|
171 |
+
}
|
172 |
+
}
|
173 |
+
|
174 |
+
return $result instanceof \Exception ? 'failed' : 'retry';
|
175 |
+
}
|
176 |
+
|
177 |
+
/**
|
178 |
+
* @param Result $result Result or exception.
|
179 |
+
* @param array $acceptor Acceptor configuration being checked.
|
180 |
+
*
|
181 |
+
* @return bool
|
182 |
+
*/
|
183 |
+
private function matchesPath($result, array $acceptor)
|
184 |
+
{
|
185 |
+
return !($result instanceof ResultInterface)
|
186 |
+
? false
|
187 |
+
: $acceptor['expected'] == $result->search($acceptor['argument']);
|
188 |
+
}
|
189 |
+
|
190 |
+
/**
|
191 |
+
* @param Result $result Result or exception.
|
192 |
+
* @param array $acceptor Acceptor configuration being checked.
|
193 |
+
*
|
194 |
+
* @return bool
|
195 |
+
*/
|
196 |
+
private function matchesPathAll($result, array $acceptor)
|
197 |
+
{
|
198 |
+
if (!($result instanceof ResultInterface)) {
|
199 |
+
return false;
|
200 |
+
}
|
201 |
+
|
202 |
+
$actuals = $result->search($acceptor['argument']) ?: [];
|
203 |
+
foreach ($actuals as $actual) {
|
204 |
+
if ($actual != $acceptor['expected']) {
|
205 |
+
return false;
|
206 |
+
}
|
207 |
+
}
|
208 |
+
|
209 |
+
return true;
|
210 |
+
}
|
211 |
+
|
212 |
+
/**
|
213 |
+
* @param Result $result Result or exception.
|
214 |
+
* @param array $acceptor Acceptor configuration being checked.
|
215 |
+
*
|
216 |
+
* @return bool
|
217 |
+
*/
|
218 |
+
private function matchesPathAny($result, array $acceptor)
|
219 |
+
{
|
220 |
+
if (!($result instanceof ResultInterface)) {
|
221 |
+
return false;
|
222 |
+
}
|
223 |
+
|
224 |
+
$actuals = $result->search($acceptor['argument']) ?: [];
|
225 |
+
return in_array($acceptor['expected'], $actuals);
|
226 |
+
}
|
227 |
+
|
228 |
+
/**
|
229 |
+
* @param Result $result Result or exception.
|
230 |
+
* @param array $acceptor Acceptor configuration being checked.
|
231 |
+
*
|
232 |
+
* @return bool
|
233 |
+
*/
|
234 |
+
private function matchesStatus($result, array $acceptor)
|
235 |
+
{
|
236 |
+
if ($result instanceof ResultInterface) {
|
237 |
+
return $acceptor['expected'] == $result['@metadata']['statusCode'];
|
238 |
+
}
|
239 |
+
|
240 |
+
if ($result instanceof AwsException && $response = $result->getResponse()) {
|
241 |
+
return $acceptor['expected'] == $response->getStatusCode();
|
242 |
+
}
|
243 |
+
|
244 |
+
return false;
|
245 |
+
}
|
246 |
+
|
247 |
+
/**
|
248 |
+
* @param Result $result Result or exception.
|
249 |
+
* @param array $acceptor Acceptor configuration being checked.
|
250 |
+
*
|
251 |
+
* @return bool
|
252 |
+
*/
|
253 |
+
private function matchesError($result, array $acceptor)
|
254 |
+
{
|
255 |
+
if ($result instanceof AwsException) {
|
256 |
+
return $result->isConnectionError()
|
257 |
+
|| $result->getAwsErrorCode() == $acceptor['expected'];
|
258 |
+
}
|
259 |
+
|
260 |
+
return false;
|
261 |
+
}
|
262 |
+
}
|
lib/Aws/Aws/WrappedHttpHandler.php
ADDED
@@ -0,0 +1,203 @@
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
namespace Aws;
|
3 |
+
|
4 |
+
use Aws\Api\Parser\Exception\ParserException;
|
5 |
+
use GuzzleHttp\Promise;
|
6 |
+
use Psr\Http\Message\RequestInterface;
|
7 |
+
use Psr\Http\Message\ResponseInterface;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Converts an HTTP handler into a Command HTTP handler.
|
11 |
+
*
|
12 |
+
* HTTP handlers have the following signature:
|
13 |
+
* function(RequestInterface $request, array $options) : PromiseInterface
|
14 |
+
*
|
15 |
+
* The promise returned form an HTTP handler must resolve to a PSR-7 response
|
16 |
+
* object when fulfilled or an error array when rejected. The error array
|
17 |
+
* can contain the following data:
|
18 |
+
*
|
19 |
+
* - exception: (required, Exception) Exception that was encountered.
|
20 |
+
* - response: (ResponseInterface) PSR-7 response that was received (if a
|
21 |
+
* response) was received.
|
22 |
+
* - connection_error: (bool) True if the error is the result of failing to
|
23 |
+
* connect.
|
24 |
+
*/
|
25 |
+
class WrappedHttpHandler
|
26 |
+
{
|
27 |
+
private $httpHandler;
|
28 |
+
private $parser;
|
29 |
+
private $errorParser;
|
30 |
+
private $exceptionClass;
|
31 |
+
private $collectStats;
|
32 |
+
|
33 |
+
/**
|
34 |
+
* @param callable $httpHandler Function that accepts a request and array
|
35 |
+
* of request options and returns a promise
|
36 |
+
* that fulfills with a response or rejects
|
37 |
+
* with an error array.
|
38 |
+
* @param callable $parser Function that accepts a response object
|
39 |
+
* and returns an AWS result object.
|
40 |
+
* @param callable $errorParser Function that parses a response object
|
41 |
+
* into AWS error data.
|
42 |
+
* @param string $exceptionClass Exception class to throw.
|
43 |
+
* @param bool $collectStats Whether to collect HTTP transfer
|
44 |
+
* information.
|
45 |
+
*/
|
46 |
+
public function __construct(
|
47 |
+
callable $httpHandler,
|
48 |
+
callable $parser,
|
49 |
+
callable $errorParser,
|
50 |
+
$exceptionClass = 'Aws\Exception\AwsException',
|
51 |
+
$collectStats = false
|
52 |
+
) {
|
53 |
+
$this->httpHandler = $httpHandler;
|
54 |
+
$this->parser = $parser;
|
55 |
+
$this->errorParser = $errorParser;
|
56 |
+
$this->exceptionClass = $exceptionClass;
|
57 |
+
$this->collectStats = $collectStats;
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Calls the simpler HTTP specific handler and wraps the returned promise
|
62 |
+
* with AWS specific values (e.g., a result object or AWS exception).
|
63 |
+
*
|
64 |
+
* @param CommandInterface $command Command being executed.
|
65 |
+
* @param RequestInterface $request Request to send.
|
66 |
+
*
|
67 |
+
* @return Promise\PromiseInterface
|
68 |
+
*/
|
69 |
+
public function __invoke(
|
70 |
+
CommandInterface $command,
|
71 |
+
RequestInterface $request
|
72 |
+
) {
|
73 |
+
$fn = $this->httpHandler;
|
74 |
+
$options = $command['@http'] ?: [];
|
75 |
+
$stats = [];
|
76 |
+
if ($this->collectStats || !empty($options['collect_stats'])) {
|
77 |
+
$options['http_stats_receiver'] = static function (
|
78 |
+
array $transferStats
|
79 |
+
) use (&$stats) {
|
80 |
+
$stats = $transferStats;
|
81 |
+
};
|
82 |
+
} elseif (isset($options['http_stats_receiver'])) {
|
83 |
+
throw new \InvalidArgumentException('Providing a custom HTTP stats'
|
84 |
+
. ' receiver to Aws\WrappedHttpHandler is not supported.');
|
85 |
+
}
|
86 |
+
|
87 |
+
return Promise\promise_for($fn($request, $options))
|
88 |
+
->then(
|
89 |
+
function (
|
90 |
+
ResponseInterface $res
|
91 |
+
) use ($command, $request, &$stats) {
|
92 |
+
return $this->parseResponse($command, $request, $res, $stats);
|
93 |
+
},
|
94 |
+
function ($err) use ($request, $command, &$stats) {
|
95 |
+
if (is_array($err)) {
|
96 |
+
$err = $this->parseError(
|
97 |
+
$err,
|
98 |
+
$request,
|
99 |
+
$command,
|
100 |
+
$stats
|
101 |
+
);
|
102 |
+
}
|
103 |
+
return new Promise\RejectedPromise($err);
|
104 |
+
}
|
105 |
+
);
|
106 |
+
}
|
107 |
+
|
108 |
+
/**
|
109 |
+
* @param CommandInterface $command
|
110 |
+
* @param RequestInterface $request
|
111 |
+
* @param ResponseInterface $response
|
112 |
+
* @param array $stats
|
113 |
+
*
|
114 |
+
* @return ResultInterface
|
115 |
+
*/
|
116 |
+
private function parseResponse(
|
117 |
+
CommandInterface $command,
|
118 |
+
RequestInterface $request,
|
119 |
+
ResponseInterface $response,
|
120 |
+
array $stats
|
121 |
+
) {
|
122 |
+
$parser = $this->parser;
|
123 |
+
$status = $response->getStatusCode();
|
124 |
+
$result = $status < 300
|
125 |
+
? $parser($command, $response)
|
126 |
+
: new Result();
|
127 |
+
|
128 |
+
$metadata = [
|
129 |
+
'statusCode' => $status,
|
130 |
+
'effectiveUri' => (string) $request->getUri(),
|
131 |
+
'headers' => [],
|
132 |
+
'transferStats' => [],
|
133 |
+
];
|
134 |
+
if (!empty($stats)) {
|
135 |
+
$metadata['transferStats']['http'] = [$stats];
|
136 |
+
}
|
137 |
+
|
138 |
+
// Bring headers into the metadata array.
|
139 |
+
foreach ($response->getHeaders() as $name => $values) {
|
140 |
+
$metadata['headers'][strtolower($name)] = $values[0];
|
141 |
+
}
|
142 |
+
|
143 |
+
$result['@metadata'] = $metadata;
|
144 |
+
|
145 |
+
return $result;
|
146 |
+
}
|
147 |
+
|
148 |
+
/**
|
149 |
+
* Parses a rejection into an AWS error.
|
150 |
+
*
|
151 |
+
* @param array $err Rejection error array.
|
152 |
+
* @param RequestInterface $request Request that was sent.
|
153 |
+
* @param CommandInterface $command Command being sent.
|
154 |
+
* @param array $stats Transfer statistics
|
155 |
+
*
|
156 |
+
* @return \Exception
|
157 |
+
*/
|
158 |
+
private function parseError(
|
159 |
+
array $err,
|
160 |
+
RequestInterface $request,
|
161 |
+
CommandInterface $command,
|
162 |
+
array $stats
|
163 |
+
) {
|
164 |
+
if (!isset($err['exception'])) {
|
165 |
+
throw new \RuntimeException('The HTTP handler was rejected without an "exception" key value pair.');
|
166 |
+
}
|
167 |
+
|
168 |
+
$serviceError = "AWS HTTP error: " . $err['exception']->getMessage();
|
169 |
+
|
170 |
+
if (!isset($err['response'])) {
|
171 |
+
$parts = ['response' => null];
|
172 |
+
} else {
|
173 |
+
try {
|
174 |
+
$parts = call_user_func($this->errorParser, $err['response']);
|
175 |
+
$serviceError .= " {$parts['code']} ({$parts['type']}): "
|
176 |
+
. "{$parts['message']} - " . $err['response']->getBody();
|
177 |
+
} catch (ParserException $e) {
|
178 |
+
$parts = [];
|
179 |
+
$serviceError .= ' Unable to parse error information from '
|
180 |
+
. "response - {$e->getMessage()}";
|
181 |
+
}
|
182 |
+
|
183 |
+
$parts['response'] = $err['response'];
|
184 |
+
}
|
185 |
+
|
186 |
+
$parts['exception'] = $err['exception'];
|
187 |
+
$parts['request'] = $request;
|
188 |
+
$parts['connection_error'] = !empty($err['connection_error']);
|
189 |
+
$parts['transfer_stats'] = $stats;
|
190 |
+
|
191 |
+
return new $this->exceptionClass(
|
192 |
+
sprintf(
|
193 |
+
'Error executing "%s" on "%s"; %s',
|
194 |
+
$command->getName(),
|
195 |
+
$request->getUri(),
|
196 |
+
$serviceError
|
197 |
+
),
|
198 |
+
$command,
|
199 |
+
$parts,
|
200 |
+
$err['exception']
|
201 |
+
);
|
202 |
+
}
|
203 |
+
}
|
lib/Aws/Aws/data/cloudfront/2015-07-27/api-2.json.php
ADDED
@@ -0,0 +1,3 @@
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
// This file was auto-generated from sdk-root/src/data/cloudfront/2015-07-27/api-2.json
|
3 |
+
return [ 'version' => '2.0', 'metadata' => [ 'apiVersion' => '2015-07-27', 'endpointPrefix' => 'cloudfront', 'globalEndpoint' => 'cloudfront.amazonaws.com', 'protocol' => 'rest-xml', 'serviceAbbreviation' => 'CloudFront', 'serviceFullName' => 'Amazon CloudFront', 'signatureVersion' => 'v4', ], 'operations' => [ 'CreateCloudFrontOriginAccessIdentity' => [ 'name' => 'CreateCloudFrontOriginAccessIdentity2015_07_27', 'http' => [ 'method' => 'POST', 'requestUri' => '/2015-07-27/origin-access-identity/cloudfront', 'responseCode' => 201, ], 'input' => [ 'shape' => 'CreateCloudFrontOriginAccessIdentityRequest', ], 'output' => [ 'shape' => 'CreateCloudFrontOriginAccessIdentityResult', ], 'errors' => [ [ 'shape' => 'CloudFrontOriginAccessIdentityAlreadyExists', 'error' => [ 'httpStatusCode' => 409, ], 'exception' => true, ], [ 'shape' => 'MissingBody', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'TooManyCloudFrontOriginAccessIdentities', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidArgument', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InconsistentQuantities', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], ], ], 'CreateDistribution' => [ 'name' => 'CreateDistribution2015_07_27', 'http' => [ 'method' => 'POST', 'requestUri' => '/2015-07-27/distribution', 'responseCode' => 201, ], 'input' => [ 'shape' => 'CreateDistributionRequest', ], 'output' => [ 'shape' => 'CreateDistributionResult', ], 'errors' => [ [ 'shape' => 'CNAMEAlreadyExists', 'error' => [ 'httpStatusCode' => 409, ], 'exception' => true, ], [ 'shape' => 'DistributionAlreadyExists', 'error' => [ 'httpStatusCode' => 409, ], 'exception' => true, ], [ 'shape' => 'InvalidOrigin', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidOriginAccessIdentity', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'AccessDenied', 'error' => [ 'httpStatusCode' => 403, ], 'exception' => true, ], [ 'shape' => 'TooManyTrustedSigners', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'TrustedSignerDoesNotExist', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidViewerCertificate', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidMinimumProtocolVersion', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'MissingBody', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'TooManyDistributionCNAMEs', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'TooManyDistributions', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidDefaultRootObject', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidRelativePath', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidErrorCode', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidResponseCode', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidArgument', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidRequiredProtocol', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'NoSuchOrigin', 'error' => [ 'httpStatusCode' => 404, ], 'exception' => true, ], [ 'shape' => 'TooManyOrigins', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'TooManyCacheBehaviors', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'TooManyCookieNamesInWhiteList', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidForwardCookies', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'TooManyHeadersInForwardedValues', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidHeadersForS3Origin', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InconsistentQuantities', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'TooManyCertificates', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidLocationCode', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidGeoRestrictionParameter', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidProtocolSettings', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidTTLOrder', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidWebACLId', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], ], ], 'CreateInvalidation' => [ 'name' => 'CreateInvalidation2015_07_27', 'http' => [ 'method' => 'POST', 'requestUri' => '/2015-07-27/distribution/{DistributionId}/invalidation', 'responseCode' => 201, ], 'input' => [ 'shape' => 'CreateInvalidationRequest', ], 'output' => [ 'shape' => 'CreateInvalidationResult', ], 'errors' => [ [ 'shape' => 'AccessDenied', 'error' => [ 'httpStatusCode' => 403, ], 'exception' => true, ], [ 'shape' => 'MissingBody', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidArgument', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'NoSuchDistribution', 'error' => [ 'httpStatusCode' => 404, ], 'exception' => true, ], [ 'shape' => 'BatchTooLarge', 'error' => [ 'httpStatusCode' => 413, ], 'exception' => true, ], [ 'shape' => 'TooManyInvalidationsInProgress', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InconsistentQuantities', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], ], ], 'CreateStreamingDistribution' => [ 'name' => 'CreateStreamingDistribution2015_07_27', 'http' => [ 'method' => 'POST', 'requestUri' => '/2015-07-27/streaming-distribution', 'responseCode' => 201, ], 'input' => [ 'shape' => 'CreateStreamingDistributionRequest', ], 'output' => [ 'shape' => 'CreateStreamingDistributionResult', ], 'errors' => [ [ 'shape' => 'CNAMEAlreadyExists', 'error' => [ 'httpStatusCode' => 409, ], 'exception' => true, ], [ 'shape' => 'StreamingDistributionAlreadyExists', 'error' => [ 'httpStatusCode' => 409, ], 'exception' => true, ], [ 'shape' => 'InvalidOrigin', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidOriginAccessIdentity', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'AccessDenied', 'error' => [ 'httpStatusCode' => 403, ], 'exception' => true, ], [ 'shape' => 'TooManyTrustedSigners', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'TrustedSignerDoesNotExist', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'MissingBody', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'TooManyStreamingDistributionCNAMEs', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'TooManyStreamingDistributions', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidArgument', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InconsistentQuantities', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], ], ], 'DeleteCloudFrontOriginAccessIdentity' => [ 'name' => 'DeleteCloudFrontOriginAccessIdentity2015_07_27', 'http' => [ 'method' => 'DELETE', 'requestUri' => '/2015-07-27/origin-access-identity/cloudfront/{Id}', 'responseCode' => 204, ], 'input' => [ 'shape' => 'DeleteCloudFrontOriginAccessIdentityRequest', ], 'errors' => [ [ 'shape' => 'AccessDenied', 'error' => [ 'httpStatusCode' => 403, ], 'exception' => true, ], [ 'shape' => 'InvalidIfMatchVersion', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'NoSuchCloudFrontOriginAccessIdentity', 'error' => [ 'httpStatusCode' => 404, ], 'exception' => true, ], [ 'shape' => 'PreconditionFailed', 'error' => [ 'httpStatusCode' => 412, ], 'exception' => true, ], [ 'shape' => 'CloudFrontOriginAccessIdentityInUse', 'error' => [ 'httpStatusCode' => 409, ], 'exception' => true, ], ], ], 'DeleteDistribution' => [ 'name' => 'DeleteDistribution2015_07_27', 'http' => [ 'method' => 'DELETE', 'requestUri' => '/2015-07-27/distribution/{Id}', 'responseCode' => 204, ], 'input' => [ 'shape' => 'DeleteDistributionRequest', ], 'errors' => [ [ 'shape' => 'AccessDenied', 'error' => [ 'httpStatusCode' => 403, ], 'exception' => true, ], [ 'shape' => 'DistributionNotDisabled', 'error' => [ 'httpStatusCode' => 409, ], 'exception' => true, ], [ 'shape' => 'InvalidIfMatchVersion', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'NoSuchDistribution', 'error' => [ 'httpStatusCode' => 404, ], 'exception' => true, ], [ 'shape' => 'PreconditionFailed', 'error' => [ 'httpStatusCode' => 412, ], 'exception' => true, ], ], ], 'DeleteStreamingDistribution' => [ 'name' => 'DeleteStreamingDistribution2015_07_27', 'http' => [ 'method' => 'DELETE', 'requestUri' => '/2015-07-27/streaming-distribution/{Id}', 'responseCode' => 204, ], 'input' => [ 'shape' => 'DeleteStreamingDistributionRequest', ], 'errors' => [ [ 'shape' => 'AccessDenied', 'error' => [ 'httpStatusCode' => 403, ], 'exception' => true, ], [ 'shape' => 'StreamingDistributionNotDisabled', 'error' => [ 'httpStatusCode' => 409, ], 'exception' => true, ], [ 'shape' => 'InvalidIfMatchVersion', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'NoSuchStreamingDistribution', 'error' => [ 'httpStatusCode' => 404, ], 'exception' => true, ], [ 'shape' => 'PreconditionFailed', 'error' => [ 'httpStatusCode' => 412, ], 'exception' => true, ], ], ], 'GetCloudFrontOriginAccessIdentity' => [ 'name' => 'GetCloudFrontOriginAccessIdentity2015_07_27', 'http' => [ 'method' => 'GET', 'requestUri' => '/2015-07-27/origin-access-identity/cloudfront/{Id}', ], 'input' => [ 'shape' => 'GetCloudFrontOriginAccessIdentityRequest', ], 'output' => [ 'shape' => 'GetCloudFrontOriginAccessIdentityResult', ], 'errors' => [ [ 'shape' => 'NoSuchCloudFrontOriginAccessIdentity', 'error' => [ 'httpStatusCode' => 404, ], 'exception' => true, ], [ 'shape' => 'AccessDenied', 'error' => [ 'httpStatusCode' => 403, ], 'exception' => true, ], ], ], 'GetCloudFrontOriginAccessIdentityConfig' => [ 'name' => 'GetCloudFrontOriginAccessIdentityConfig2015_07_27', 'http' => [ 'method' => 'GET', 'requestUri' => '/2015-07-27/origin-access-identity/cloudfront/{Id}/config', ], 'input' => [ 'shape' => 'GetCloudFrontOriginAccessIdentityConfigRequest', ], 'output' => [ 'shape' => 'GetCloudFrontOriginAccessIdentityConfigResult', ], 'errors' => [ [ 'shape' => 'NoSuchCloudFrontOriginAccessIdentity', 'error' => [ 'httpStatusCode' => 404, ], 'exception' => true, ], [ 'shape' => 'AccessDenied', 'error' => [ 'httpStatusCode' => 403, ], 'exception' => true, ], ], ], 'GetDistribution' => [ 'name' => 'GetDistribution2015_07_27', 'http' => [ 'method' => 'GET', 'requestUri' => '/2015-07-27/distribution/{Id}', ], 'input' => [ 'shape' => 'GetDistributionRequest', ], 'output' => [ 'shape' => 'GetDistributionResult', ], 'errors' => [ [ 'shape' => 'NoSuchDistribution', 'error' => [ 'httpStatusCode' => 404, ], 'exception' => true, ], [ 'shape' => 'AccessDenied', 'error' => [ 'httpStatusCode' => 403, ], 'exception' => true, ], ], ], 'GetDistributionConfig' => [ 'name' => 'GetDistributionConfig2015_07_27', 'http' => [ 'method' => 'GET', 'requestUri' => '/2015-07-27/distribution/{Id}/config', ], 'input' => [ 'shape' => 'GetDistributionConfigRequest', ], 'output' => [ 'shape' => 'GetDistributionConfigResult', ], 'errors' => [ [ 'shape' => 'NoSuchDistribution', 'error' => [ 'httpStatusCode' => 404, ], 'exception' => true, ], [ 'shape' => 'AccessDenied', 'error' => [ 'httpStatusCode' => 403, ], 'exception' => true, ], ], ], 'GetInvalidation' => [ 'name' => 'GetInvalidation2015_07_27', 'http' => [ 'method' => 'GET', 'requestUri' => '/2015-07-27/distribution/{DistributionId}/invalidation/{Id}', ], 'input' => [ 'shape' => 'GetInvalidationRequest', ], 'output' => [ 'shape' => 'GetInvalidationResult', ], 'errors' => [ [ 'shape' => 'NoSuchInvalidation', 'error' => [ 'httpStatusCode' => 404, ], 'exception' => true, ], [ 'shape' => 'NoSuchDistribution', 'error' => [ 'httpStatusCode' => 404, ], 'exception' => true, ], [ 'shape' => 'AccessDenied', 'error' => [ 'httpStatusCode' => 403, ], 'exception' => true, ], ], ], 'GetStreamingDistribution' => [ 'name' => 'GetStreamingDistribution2015_07_27', 'http' => [ 'method' => 'GET', 'requestUri' => '/2015-07-27/streaming-distribution/{Id}', ], 'input' => [ 'shape' => 'GetStreamingDistributionRequest', ], 'output' => [ 'shape' => 'GetStreamingDistributionResult', ], 'errors' => [ [ 'shape' => 'NoSuchStreamingDistribution', 'error' => [ 'httpStatusCode' => 404, ], 'exception' => true, ], [ 'shape' => 'AccessDenied', 'error' => [ 'httpStatusCode' => 403, ], 'exception' => true, ], ], ], 'GetStreamingDistributionConfig' => [ 'name' => 'GetStreamingDistributionConfig2015_07_27', 'http' => [ 'method' => 'GET', 'requestUri' => '/2015-07-27/streaming-distribution/{Id}/config', ], 'input' => [ 'shape' => 'GetStreamingDistributionConfigRequest', ], 'output' => [ 'shape' => 'GetStreamingDistributionConfigResult', ], 'errors' => [ [ 'shape' => 'NoSuchStreamingDistribution', 'error' => [ 'httpStatusCode' => 404, ], 'exception' => true, ], [ 'shape' => 'AccessDenied', 'error' => [ 'httpStatusCode' => 403, ], 'exception' => true, ], ], ], 'ListCloudFrontOriginAccessIdentities' => [ 'name' => 'ListCloudFrontOriginAccessIdentities2015_07_27', 'http' => [ 'method' => 'GET', 'requestUri' => '/2015-07-27/origin-access-identity/cloudfront', ], 'input' => [ 'shape' => 'ListCloudFrontOriginAccessIdentitiesRequest', ], 'output' => [ 'shape' => 'ListCloudFrontOriginAccessIdentitiesResult', ], 'errors' => [ [ 'shape' => 'InvalidArgument', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], ], ], 'ListDistributions' => [ 'name' => 'ListDistributions2015_07_27', 'http' => [ 'method' => 'GET', 'requestUri' => '/2015-07-27/distribution', ], 'input' => [ 'shape' => 'ListDistributionsRequest', ], 'output' => [ 'shape' => 'ListDistributionsResult', ], 'errors' => [ [ 'shape' => 'InvalidArgument', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], ], ], 'ListDistributionsByWebACLId' => [ 'name' => 'ListDistributionsByWebACLId2015_07_27', 'http' => [ 'method' => 'GET', 'requestUri' => '/2015-07-27/distributionsByWebACLId/{WebACLId}', ], 'input' => [ 'shape' => 'ListDistributionsByWebACLIdRequest', ], 'output' => [ 'shape' => 'ListDistributionsByWebACLIdResult', ], 'errors' => [ [ 'shape' => 'InvalidArgument', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidWebACLId', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], ], ], 'ListInvalidations' => [ 'name' => 'ListInvalidations2015_07_27', 'http' => [ 'method' => 'GET', 'requestUri' => '/2015-07-27/distribution/{DistributionId}/invalidation', ], 'input' => [ 'shape' => 'ListInvalidationsRequest', ], 'output' => [ 'shape' => 'ListInvalidationsResult', ], 'errors' => [ [ 'shape' => 'InvalidArgument', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'NoSuchDistribution', 'error' => [ 'httpStatusCode' => 404, ], 'exception' => true, ], [ 'shape' => 'AccessDenied', 'error' => [ 'httpStatusCode' => 403, ], 'exception' => true, ], ], ], 'ListStreamingDistributions' => [ 'name' => 'ListStreamingDistributions2015_07_27', 'http' => [ 'method' => 'GET', 'requestUri' => '/2015-07-27/streaming-distribution', ], 'input' => [ 'shape' => 'ListStreamingDistributionsRequest', ], 'output' => [ 'shape' => 'ListStreamingDistributionsResult', ], 'errors' => [ [ 'shape' => 'InvalidArgument', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], ], ], 'UpdateCloudFrontOriginAccessIdentity' => [ 'name' => 'UpdateCloudFrontOriginAccessIdentity2015_07_27', 'http' => [ 'method' => 'PUT', 'requestUri' => '/2015-07-27/origin-access-identity/cloudfront/{Id}/config', ], 'input' => [ 'shape' => 'UpdateCloudFrontOriginAccessIdentityRequest', ], 'output' => [ 'shape' => 'UpdateCloudFrontOriginAccessIdentityResult', ], 'errors' => [ [ 'shape' => 'AccessDenied', 'error' => [ 'httpStatusCode' => 403, ], 'exception' => true, ], [ 'shape' => 'IllegalUpdate', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidIfMatchVersion', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'MissingBody', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'NoSuchCloudFrontOriginAccessIdentity', 'error' => [ 'httpStatusCode' => 404, ], 'exception' => true, ], [ 'shape' => 'PreconditionFailed', 'error' => [ 'httpStatusCode' => 412, ], 'exception' => true, ], [ 'shape' => 'InvalidArgument', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InconsistentQuantities', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], ], ], 'UpdateDistribution' => [ 'name' => 'UpdateDistribution2015_07_27', 'http' => [ 'method' => 'PUT', 'requestUri' => '/2015-07-27/distribution/{Id}/config', ], 'input' => [ 'shape' => 'UpdateDistributionRequest', ], 'output' => [ 'shape' => 'UpdateDistributionResult', ], 'errors' => [ [ 'shape' => 'AccessDenied', 'error' => [ 'httpStatusCode' => 403, ], 'exception' => true, ], [ 'shape' => 'CNAMEAlreadyExists', 'error' => [ 'httpStatusCode' => 409, ], 'exception' => true, ], [ 'shape' => 'IllegalUpdate', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidIfMatchVersion', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'MissingBody', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'NoSuchDistribution', 'error' => [ 'httpStatusCode' => 404, ], 'exception' => true, ], [ 'shape' => 'PreconditionFailed', 'error' => [ 'httpStatusCode' => 412, ], 'exception' => true, ], [ 'shape' => 'TooManyDistributionCNAMEs', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidDefaultRootObject', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidRelativePath', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidErrorCode', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidResponseCode', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidArgument', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidOriginAccessIdentity', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'TooManyTrustedSigners', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'TrustedSignerDoesNotExist', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidViewerCertificate', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidMinimumProtocolVersion', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidRequiredProtocol', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'NoSuchOrigin', 'error' => [ 'httpStatusCode' => 404, ], 'exception' => true, ], [ 'shape' => 'TooManyOrigins', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'TooManyCacheBehaviors', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'TooManyCookieNamesInWhiteList', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidForwardCookies', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'TooManyHeadersInForwardedValues', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidHeadersForS3Origin', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InconsistentQuantities', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'TooManyCertificates', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidLocationCode', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidGeoRestrictionParameter', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidTTLOrder', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidWebACLId', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], ], ], 'UpdateStreamingDistribution' => [ 'name' => 'UpdateStreamingDistribution2015_07_27', 'http' => [ 'method' => 'PUT', 'requestUri' => '/2015-07-27/streaming-distribution/{Id}/config', ], 'input' => [ 'shape' => 'UpdateStreamingDistributionRequest', ], 'output' => [ 'shape' => 'UpdateStreamingDistributionResult', ], 'errors' => [ [ 'shape' => 'AccessDenied', 'error' => [ 'httpStatusCode' => 403, ], 'exception' => true, ], [ 'shape' => 'CNAMEAlreadyExists', 'error' => [ 'httpStatusCode' => 409, ], 'exception' => true, ], [ 'shape' => 'IllegalUpdate', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidIfMatchVersion', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'MissingBody', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'NoSuchStreamingDistribution', 'error' => [ 'httpStatusCode' => 404, ], 'exception' => true, ], [ 'shape' => 'PreconditionFailed', 'error' => [ 'httpStatusCode' => 412, ], 'exception' => true, ], [ 'shape' => 'TooManyStreamingDistributionCNAMEs', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidArgument', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InvalidOriginAccessIdentity', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'TooManyTrustedSigners', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'TrustedSignerDoesNotExist', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], [ 'shape' => 'InconsistentQuantities', 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], ], ], ], 'shapes' => [ 'AccessDenied' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 403, ], 'exception' => true, ], 'ActiveTrustedSigners' => [ 'type' => 'structure', 'required' => [ 'Enabled', 'Quantity', ], 'members' => [ 'Enabled' => [ 'shape' => 'boolean', ], 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'SignerList', ], ], ], 'AliasList' => [ 'type' => 'list', 'member' => [ 'shape' => 'string', 'locationName' => 'CNAME', ], ], 'Aliases' => [ 'type' => 'structure', 'required' => [ 'Quantity', ], 'members' => [ 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'AliasList', ], ], ], 'AllowedMethods' => [ 'type' => 'structure', 'required' => [ 'Quantity', 'Items', ], 'members' => [ 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'MethodsList', ], 'CachedMethods' => [ 'shape' => 'CachedMethods', ], ], ], 'AwsAccountNumberList' => [ 'type' => 'list', 'member' => [ 'shape' => 'string', 'locationName' => 'AwsAccountNumber', ], ], 'BatchTooLarge' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 413, ], 'exception' => true, ], 'CNAMEAlreadyExists' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 409, ], 'exception' => true, ], 'CacheBehavior' => [ 'type' => 'structure', 'required' => [ 'PathPattern', 'TargetOriginId', 'ForwardedValues', 'TrustedSigners', 'ViewerProtocolPolicy', 'MinTTL', ], 'members' => [ 'PathPattern' => [ 'shape' => 'string', ], 'TargetOriginId' => [ 'shape' => 'string', ], 'ForwardedValues' => [ 'shape' => 'ForwardedValues', ], 'TrustedSigners' => [ 'shape' => 'TrustedSigners', ], 'ViewerProtocolPolicy' => [ 'shape' => 'ViewerProtocolPolicy', ], 'MinTTL' => [ 'shape' => 'long', ], 'AllowedMethods' => [ 'shape' => 'AllowedMethods', ], 'SmoothStreaming' => [ 'shape' => 'boolean', ], 'DefaultTTL' => [ 'shape' => 'long', ], 'MaxTTL' => [ 'shape' => 'long', ], ], ], 'CacheBehaviorList' => [ 'type' => 'list', 'member' => [ 'shape' => 'CacheBehavior', 'locationName' => 'CacheBehavior', ], ], 'CacheBehaviors' => [ 'type' => 'structure', 'required' => [ 'Quantity', ], 'members' => [ 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'CacheBehaviorList', ], ], ], 'CachedMethods' => [ 'type' => 'structure', 'required' => [ 'Quantity', 'Items', ], 'members' => [ 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'MethodsList', ], ], ], 'CloudFrontOriginAccessIdentity' => [ 'type' => 'structure', 'required' => [ 'Id', 'S3CanonicalUserId', ], 'members' => [ 'Id' => [ 'shape' => 'string', ], 'S3CanonicalUserId' => [ 'shape' => 'string', ], 'CloudFrontOriginAccessIdentityConfig' => [ 'shape' => 'CloudFrontOriginAccessIdentityConfig', ], ], ], 'CloudFrontOriginAccessIdentityAlreadyExists' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 409, ], 'exception' => true, ], 'CloudFrontOriginAccessIdentityConfig' => [ 'type' => 'structure', 'required' => [ 'CallerReference', 'Comment', ], 'members' => [ 'CallerReference' => [ 'shape' => 'string', ], 'Comment' => [ 'shape' => 'string', ], ], ], 'CloudFrontOriginAccessIdentityInUse' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 409, ], 'exception' => true, ], 'CloudFrontOriginAccessIdentityList' => [ 'type' => 'structure', 'required' => [ 'Marker', 'MaxItems', 'IsTruncated', 'Quantity', ], 'members' => [ 'Marker' => [ 'shape' => 'string', ], 'NextMarker' => [ 'shape' => 'string', ], 'MaxItems' => [ 'shape' => 'integer', ], 'IsTruncated' => [ 'shape' => 'boolean', ], 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'CloudFrontOriginAccessIdentitySummaryList', ], ], ], 'CloudFrontOriginAccessIdentitySummary' => [ 'type' => 'structure', 'required' => [ 'Id', 'S3CanonicalUserId', 'Comment', ], 'members' => [ 'Id' => [ 'shape' => 'string', ], 'S3CanonicalUserId' => [ 'shape' => 'string', ], 'Comment' => [ 'shape' => 'string', ], ], ], 'CloudFrontOriginAccessIdentitySummaryList' => [ 'type' => 'list', 'member' => [ 'shape' => 'CloudFrontOriginAccessIdentitySummary', 'locationName' => 'CloudFrontOriginAccessIdentitySummary', ], ], 'CookieNameList' => [ 'type' => 'list', 'member' => [ 'shape' => 'string', 'locationName' => 'Name', ], ], 'CookieNames' => [ 'type' => 'structure', 'required' => [ 'Quantity', ], 'members' => [ 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'CookieNameList', ], ], ], 'CookiePreference' => [ 'type' => 'structure', 'required' => [ 'Forward', ], 'members' => [ 'Forward' => [ 'shape' => 'ItemSelection', ], 'WhitelistedNames' => [ 'shape' => 'CookieNames', ], ], ], 'CreateCloudFrontOriginAccessIdentityRequest' => [ 'type' => 'structure', 'required' => [ 'CloudFrontOriginAccessIdentityConfig', ], 'members' => [ 'CloudFrontOriginAccessIdentityConfig' => [ 'shape' => 'CloudFrontOriginAccessIdentityConfig', 'locationName' => 'CloudFrontOriginAccessIdentityConfig', 'xmlNamespace' => [ 'uri' => 'http://cloudfront.amazonaws.com/doc/2015-07-27/', ], ], ], 'payload' => 'CloudFrontOriginAccessIdentityConfig', ], 'CreateCloudFrontOriginAccessIdentityResult' => [ 'type' => 'structure', 'members' => [ 'CloudFrontOriginAccessIdentity' => [ 'shape' => 'CloudFrontOriginAccessIdentity', ], 'Location' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'Location', ], 'ETag' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'ETag', ], ], 'payload' => 'CloudFrontOriginAccessIdentity', ], 'CreateDistributionRequest' => [ 'type' => 'structure', 'required' => [ 'DistributionConfig', ], 'members' => [ 'DistributionConfig' => [ 'shape' => 'DistributionConfig', 'locationName' => 'DistributionConfig', 'xmlNamespace' => [ 'uri' => 'http://cloudfront.amazonaws.com/doc/2015-07-27/', ], ], ], 'payload' => 'DistributionConfig', ], 'CreateDistributionResult' => [ 'type' => 'structure', 'members' => [ 'Distribution' => [ 'shape' => 'Distribution', ], 'Location' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'Location', ], 'ETag' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'ETag', ], ], 'payload' => 'Distribution', ], 'CreateInvalidationRequest' => [ 'type' => 'structure', 'required' => [ 'DistributionId', 'InvalidationBatch', ], 'members' => [ 'DistributionId' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'DistributionId', ], 'InvalidationBatch' => [ 'shape' => 'InvalidationBatch', 'locationName' => 'InvalidationBatch', 'xmlNamespace' => [ 'uri' => 'http://cloudfront.amazonaws.com/doc/2015-07-27/', ], ], ], 'payload' => 'InvalidationBatch', ], 'CreateInvalidationResult' => [ 'type' => 'structure', 'members' => [ 'Location' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'Location', ], 'Invalidation' => [ 'shape' => 'Invalidation', ], ], 'payload' => 'Invalidation', ], 'CreateStreamingDistributionRequest' => [ 'type' => 'structure', 'required' => [ 'StreamingDistributionConfig', ], 'members' => [ 'StreamingDistributionConfig' => [ 'shape' => 'StreamingDistributionConfig', 'locationName' => 'StreamingDistributionConfig', 'xmlNamespace' => [ 'uri' => 'http://cloudfront.amazonaws.com/doc/2015-07-27/', ], ], ], 'payload' => 'StreamingDistributionConfig', ], 'CreateStreamingDistributionResult' => [ 'type' => 'structure', 'members' => [ 'StreamingDistribution' => [ 'shape' => 'StreamingDistribution', ], 'Location' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'Location', ], 'ETag' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'ETag', ], ], 'payload' => 'StreamingDistribution', ], 'CustomErrorResponse' => [ 'type' => 'structure', 'required' => [ 'ErrorCode', ], 'members' => [ 'ErrorCode' => [ 'shape' => 'integer', ], 'ResponsePagePath' => [ 'shape' => 'string', ], 'ResponseCode' => [ 'shape' => 'string', ], 'ErrorCachingMinTTL' => [ 'shape' => 'long', ], ], ], 'CustomErrorResponseList' => [ 'type' => 'list', 'member' => [ 'shape' => 'CustomErrorResponse', 'locationName' => 'CustomErrorResponse', ], ], 'CustomErrorResponses' => [ 'type' => 'structure', 'required' => [ 'Quantity', ], 'members' => [ 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'CustomErrorResponseList', ], ], ], 'CustomOriginConfig' => [ 'type' => 'structure', 'required' => [ 'HTTPPort', 'HTTPSPort', 'OriginProtocolPolicy', ], 'members' => [ 'HTTPPort' => [ 'shape' => 'integer', ], 'HTTPSPort' => [ 'shape' => 'integer', ], 'OriginProtocolPolicy' => [ 'shape' => 'OriginProtocolPolicy', ], ], ], 'DefaultCacheBehavior' => [ 'type' => 'structure', 'required' => [ 'TargetOriginId', 'ForwardedValues', 'TrustedSigners', 'ViewerProtocolPolicy', 'MinTTL', ], 'members' => [ 'TargetOriginId' => [ 'shape' => 'string', ], 'ForwardedValues' => [ 'shape' => 'ForwardedValues', ], 'TrustedSigners' => [ 'shape' => 'TrustedSigners', ], 'ViewerProtocolPolicy' => [ 'shape' => 'ViewerProtocolPolicy', ], 'MinTTL' => [ 'shape' => 'long', ], 'AllowedMethods' => [ 'shape' => 'AllowedMethods', ], 'SmoothStreaming' => [ 'shape' => 'boolean', ], 'DefaultTTL' => [ 'shape' => 'long', ], 'MaxTTL' => [ 'shape' => 'long', ], ], ], 'DeleteCloudFrontOriginAccessIdentityRequest' => [ 'type' => 'structure', 'required' => [ 'Id', ], 'members' => [ 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], 'IfMatch' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'If-Match', ], ], ], 'DeleteDistributionRequest' => [ 'type' => 'structure', 'required' => [ 'Id', ], 'members' => [ 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], 'IfMatch' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'If-Match', ], ], ], 'DeleteStreamingDistributionRequest' => [ 'type' => 'structure', 'required' => [ 'Id', ], 'members' => [ 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], 'IfMatch' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'If-Match', ], ], ], 'Distribution' => [ 'type' => 'structure', 'required' => [ 'Id', 'Status', 'LastModifiedTime', 'InProgressInvalidationBatches', 'DomainName', 'ActiveTrustedSigners', 'DistributionConfig', ], 'members' => [ 'Id' => [ 'shape' => 'string', ], 'Status' => [ 'shape' => 'string', ], 'LastModifiedTime' => [ 'shape' => 'timestamp', ], 'InProgressInvalidationBatches' => [ 'shape' => 'integer', ], 'DomainName' => [ 'shape' => 'string', ], 'ActiveTrustedSigners' => [ 'shape' => 'ActiveTrustedSigners', ], 'DistributionConfig' => [ 'shape' => 'DistributionConfig', ], ], ], 'DistributionAlreadyExists' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 409, ], 'exception' => true, ], 'DistributionConfig' => [ 'type' => 'structure', 'required' => [ 'CallerReference', 'Origins', 'DefaultCacheBehavior', 'Comment', 'Enabled', ], 'members' => [ 'CallerReference' => [ 'shape' => 'string', ], 'Aliases' => [ 'shape' => 'Aliases', ], 'DefaultRootObject' => [ 'shape' => 'string', ], 'Origins' => [ 'shape' => 'Origins', ], 'DefaultCacheBehavior' => [ 'shape' => 'DefaultCacheBehavior', ], 'CacheBehaviors' => [ 'shape' => 'CacheBehaviors', ], 'CustomErrorResponses' => [ 'shape' => 'CustomErrorResponses', ], 'Comment' => [ 'shape' => 'string', ], 'Logging' => [ 'shape' => 'LoggingConfig', ], 'PriceClass' => [ 'shape' => 'PriceClass', ], 'Enabled' => [ 'shape' => 'boolean', ], 'ViewerCertificate' => [ 'shape' => 'ViewerCertificate', ], 'Restrictions' => [ 'shape' => 'Restrictions', ], 'WebACLId' => [ 'shape' => 'string', ], ], ], 'DistributionList' => [ 'type' => 'structure', 'required' => [ 'Marker', 'MaxItems', 'IsTruncated', 'Quantity', ], 'members' => [ 'Marker' => [ 'shape' => 'string', ], 'NextMarker' => [ 'shape' => 'string', ], 'MaxItems' => [ 'shape' => 'integer', ], 'IsTruncated' => [ 'shape' => 'boolean', ], 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'DistributionSummaryList', ], ], ], 'DistributionNotDisabled' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 409, ], 'exception' => true, ], 'DistributionSummary' => [ 'type' => 'structure', 'required' => [ 'Id', 'Status', 'LastModifiedTime', 'DomainName', 'Aliases', 'Origins', 'DefaultCacheBehavior', 'CacheBehaviors', 'CustomErrorResponses', 'Comment', 'PriceClass', 'Enabled', 'ViewerCertificate', 'Restrictions', 'WebACLId', ], 'members' => [ 'Id' => [ 'shape' => 'string', ], 'Status' => [ 'shape' => 'string', ], 'LastModifiedTime' => [ 'shape' => 'timestamp', ], 'DomainName' => [ 'shape' => 'string', ], 'Aliases' => [ 'shape' => 'Aliases', ], 'Origins' => [ 'shape' => 'Origins', ], 'DefaultCacheBehavior' => [ 'shape' => 'DefaultCacheBehavior', ], 'CacheBehaviors' => [ 'shape' => 'CacheBehaviors', ], 'CustomErrorResponses' => [ 'shape' => 'CustomErrorResponses', ], 'Comment' => [ 'shape' => 'string', ], 'PriceClass' => [ 'shape' => 'PriceClass', ], 'Enabled' => [ 'shape' => 'boolean', ], 'ViewerCertificate' => [ 'shape' => 'ViewerCertificate', ], 'Restrictions' => [ 'shape' => 'Restrictions', ], 'WebACLId' => [ 'shape' => 'string', ], ], ], 'DistributionSummaryList' => [ 'type' => 'list', 'member' => [ 'shape' => 'DistributionSummary', 'locationName' => 'DistributionSummary', ], ], 'ForwardedValues' => [ 'type' => 'structure', 'required' => [ 'QueryString', 'Cookies', ], 'members' => [ 'QueryString' => [ 'shape' => 'boolean', ], 'Cookies' => [ 'shape' => 'CookiePreference', ], 'Headers' => [ 'shape' => 'Headers', ], ], ], 'GeoRestriction' => [ 'type' => 'structure', 'required' => [ 'RestrictionType', 'Quantity', ], 'members' => [ 'RestrictionType' => [ 'shape' => 'GeoRestrictionType', ], 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'LocationList', ], ], ], 'GeoRestrictionType' => [ 'type' => 'string', 'enum' => [ 'blacklist', 'whitelist', 'none', ], ], 'GetCloudFrontOriginAccessIdentityConfigRequest' => [ 'type' => 'structure', 'required' => [ 'Id', ], 'members' => [ 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], ], ], 'GetCloudFrontOriginAccessIdentityConfigResult' => [ 'type' => 'structure', 'members' => [ 'CloudFrontOriginAccessIdentityConfig' => [ 'shape' => 'CloudFrontOriginAccessIdentityConfig', ], 'ETag' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'ETag', ], ], 'payload' => 'CloudFrontOriginAccessIdentityConfig', ], 'GetCloudFrontOriginAccessIdentityRequest' => [ 'type' => 'structure', 'required' => [ 'Id', ], 'members' => [ 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], ], ], 'GetCloudFrontOriginAccessIdentityResult' => [ 'type' => 'structure', 'members' => [ 'CloudFrontOriginAccessIdentity' => [ 'shape' => 'CloudFrontOriginAccessIdentity', ], 'ETag' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'ETag', ], ], 'payload' => 'CloudFrontOriginAccessIdentity', ], 'GetDistributionConfigRequest' => [ 'type' => 'structure', 'required' => [ 'Id', ], 'members' => [ 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], ], ], 'GetDistributionConfigResult' => [ 'type' => 'structure', 'members' => [ 'DistributionConfig' => [ 'shape' => 'DistributionConfig', ], 'ETag' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'ETag', ], ], 'payload' => 'DistributionConfig', ], 'GetDistributionRequest' => [ 'type' => 'structure', 'required' => [ 'Id', ], 'members' => [ 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], ], ], 'GetDistributionResult' => [ 'type' => 'structure', 'members' => [ 'Distribution' => [ 'shape' => 'Distribution', ], 'ETag' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'ETag', ], ], 'payload' => 'Distribution', ], 'GetInvalidationRequest' => [ 'type' => 'structure', 'required' => [ 'DistributionId', 'Id', ], 'members' => [ 'DistributionId' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'DistributionId', ], 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], ], ], 'GetInvalidationResult' => [ 'type' => 'structure', 'members' => [ 'Invalidation' => [ 'shape' => 'Invalidation', ], ], 'payload' => 'Invalidation', ], 'GetStreamingDistributionConfigRequest' => [ 'type' => 'structure', 'required' => [ 'Id', ], 'members' => [ 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], ], ], 'GetStreamingDistributionConfigResult' => [ 'type' => 'structure', 'members' => [ 'StreamingDistributionConfig' => [ 'shape' => 'StreamingDistributionConfig', ], 'ETag' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'ETag', ], ], 'payload' => 'StreamingDistributionConfig', ], 'GetStreamingDistributionRequest' => [ 'type' => 'structure', 'required' => [ 'Id', ], 'members' => [ 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], ], ], 'GetStreamingDistributionResult' => [ 'type' => 'structure', 'members' => [ 'StreamingDistribution' => [ 'shape' => 'StreamingDistribution', ], 'ETag' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'ETag', ], ], 'payload' => 'StreamingDistribution', ], 'HeaderList' => [ 'type' => 'list', 'member' => [ 'shape' => 'string', 'locationName' => 'Name', ], ], 'Headers' => [ 'type' => 'structure', 'required' => [ 'Quantity', ], 'members' => [ 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'HeaderList', ], ], ], 'IllegalUpdate' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InconsistentQuantities' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidArgument' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidDefaultRootObject' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidErrorCode' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidForwardCookies' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidGeoRestrictionParameter' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidHeadersForS3Origin' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidIfMatchVersion' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidLocationCode' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidMinimumProtocolVersion' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidOrigin' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidOriginAccessIdentity' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidProtocolSettings' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidRelativePath' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidRequiredProtocol' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidResponseCode' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidTTLOrder' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidViewerCertificate' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidWebACLId' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'Invalidation' => [ 'type' => 'structure', 'required' => [ 'Id', 'Status', 'CreateTime', 'InvalidationBatch', ], 'members' => [ 'Id' => [ 'shape' => 'string', ], 'Status' => [ 'shape' => 'string', ], 'CreateTime' => [ 'shape' => 'timestamp', ], 'InvalidationBatch' => [ 'shape' => 'InvalidationBatch', ], ], ], 'InvalidationBatch' => [ 'type' => 'structure', 'required' => [ 'Paths', 'CallerReference', ], 'members' => [ 'Paths' => [ 'shape' => 'Paths', ], 'CallerReference' => [ 'shape' => 'string', ], ], ], 'InvalidationList' => [ 'type' => 'structure', 'required' => [ 'Marker', 'MaxItems', 'IsTruncated', 'Quantity', ], 'members' => [ 'Marker' => [ 'shape' => 'string', ], 'NextMarker' => [ 'shape' => 'string', ], 'MaxItems' => [ 'shape' => 'integer', ], 'IsTruncated' => [ 'shape' => 'boolean', ], 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'InvalidationSummaryList', ], ], ], 'InvalidationSummary' => [ 'type' => 'structure', 'required' => [ 'Id', 'CreateTime', 'Status', ], 'members' => [ 'Id' => [ 'shape' => 'string', ], 'CreateTime' => [ 'shape' => 'timestamp', ], 'Status' => [ 'shape' => 'string', ], ], ], 'InvalidationSummaryList' => [ 'type' => 'list', 'member' => [ 'shape' => 'InvalidationSummary', 'locationName' => 'InvalidationSummary', ], ], 'ItemSelection' => [ 'type' => 'string', 'enum' => [ 'none', 'whitelist', 'all', ], ], 'KeyPairIdList' => [ 'type' => 'list', 'member' => [ 'shape' => 'string', 'locationName' => 'KeyPairId', ], ], 'KeyPairIds' => [ 'type' => 'structure', 'required' => [ 'Quantity', ], 'members' => [ 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'KeyPairIdList', ], ], ], 'ListCloudFrontOriginAccessIdentitiesRequest' => [ 'type' => 'structure', 'members' => [ 'Marker' => [ 'shape' => 'string', 'location' => 'querystring', 'locationName' => 'Marker', ], 'MaxItems' => [ 'shape' => 'string', 'location' => 'querystring', 'locationName' => 'MaxItems', ], ], ], 'ListCloudFrontOriginAccessIdentitiesResult' => [ 'type' => 'structure', 'members' => [ 'CloudFrontOriginAccessIdentityList' => [ 'shape' => 'CloudFrontOriginAccessIdentityList', ], ], 'payload' => 'CloudFrontOriginAccessIdentityList', ], 'ListDistributionsByWebACLIdRequest' => [ 'type' => 'structure', 'required' => [ 'WebACLId', ], 'members' => [ 'Marker' => [ 'shape' => 'string', 'location' => 'querystring', 'locationName' => 'Marker', ], 'MaxItems' => [ 'shape' => 'string', 'location' => 'querystring', 'locationName' => 'MaxItems', ], 'WebACLId' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'WebACLId', ], ], ], 'ListDistributionsByWebACLIdResult' => [ 'type' => 'structure', 'members' => [ 'DistributionList' => [ 'shape' => 'DistributionList', ], ], 'payload' => 'DistributionList', ], 'ListDistributionsRequest' => [ 'type' => 'structure', 'members' => [ 'Marker' => [ 'shape' => 'string', 'location' => 'querystring', 'locationName' => 'Marker', ], 'MaxItems' => [ 'shape' => 'string', 'location' => 'querystring', 'locationName' => 'MaxItems', ], ], ], 'ListDistributionsResult' => [ 'type' => 'structure', 'members' => [ 'DistributionList' => [ 'shape' => 'DistributionList', ], ], 'payload' => 'DistributionList', ], 'ListInvalidationsRequest' => [ 'type' => 'structure', 'required' => [ 'DistributionId', ], 'members' => [ 'DistributionId' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'DistributionId', ], 'Marker' => [ 'shape' => 'string', 'location' => 'querystring', 'locationName' => 'Marker', ], 'MaxItems' => [ 'shape' => 'string', 'location' => 'querystring', 'locationName' => 'MaxItems', ], ], ], 'ListInvalidationsResult' => [ 'type' => 'structure', 'members' => [ 'InvalidationList' => [ 'shape' => 'InvalidationList', ], ], 'payload' => 'InvalidationList', ], 'ListStreamingDistributionsRequest' => [ 'type' => 'structure', 'members' => [ 'Marker' => [ 'shape' => 'string', 'location' => 'querystring', 'locationName' => 'Marker', ], 'MaxItems' => [ 'shape' => 'string', 'location' => 'querystring', 'locationName' => 'MaxItems', ], ], ], 'ListStreamingDistributionsResult' => [ 'type' => 'structure', 'members' => [ 'StreamingDistributionList' => [ 'shape' => 'StreamingDistributionList', ], ], 'payload' => 'StreamingDistributionList', ], 'LocationList' => [ 'type' => 'list', 'member' => [ 'shape' => 'string', 'locationName' => 'Location', ], ], 'LoggingConfig' => [ 'type' => 'structure', 'required' => [ 'Enabled', 'IncludeCookies', 'Bucket', 'Prefix', ], 'members' => [ 'Enabled' => [ 'shape' => 'boolean', ], 'IncludeCookies' => [ 'shape' => 'boolean', ], 'Bucket' => [ 'shape' => 'string', ], 'Prefix' => [ 'shape' => 'string', ], ], ], 'Method' => [ 'type' => 'string', 'enum' => [ 'GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'OPTIONS', 'DELETE', ], ], 'MethodsList' => [ 'type' => 'list', 'member' => [ 'shape' => 'Method', 'locationName' => 'Method', ], ], 'MinimumProtocolVersion' => [ 'type' => 'string', 'enum' => [ 'SSLv3', 'TLSv1', ], ], 'MissingBody' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'NoSuchCloudFrontOriginAccessIdentity' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 404, ], 'exception' => true, ], 'NoSuchDistribution' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 404, ], 'exception' => true, ], 'NoSuchInvalidation' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 404, ], 'exception' => true, ], 'NoSuchOrigin' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 404, ], 'exception' => true, ], 'NoSuchStreamingDistribution' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 404, ], 'exception' => true, ], 'Origin' => [ 'type' => 'structure', 'required' => [ 'Id', 'DomainName', ], 'members' => [ 'Id' => [ 'shape' => 'string', ], 'DomainName' => [ 'shape' => 'string', ], 'OriginPath' => [ 'shape' => 'string', ], 'S3OriginConfig' => [ 'shape' => 'S3OriginConfig', ], 'CustomOriginConfig' => [ 'shape' => 'CustomOriginConfig', ], ], ], 'OriginList' => [ 'type' => 'list', 'member' => [ 'shape' => 'Origin', 'locationName' => 'Origin', ], 'min' => 1, ], 'OriginProtocolPolicy' => [ 'type' => 'string', 'enum' => [ 'http-only', 'match-viewer', ], ], 'Origins' => [ 'type' => 'structure', 'required' => [ 'Quantity', ], 'members' => [ 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'OriginList', ], ], ], 'PathList' => [ 'type' => 'list', 'member' => [ 'shape' => 'string', 'locationName' => 'Path', ], ], 'Paths' => [ 'type' => 'structure', 'required' => [ 'Quantity', ], 'members' => [ 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'PathList', ], ], ], 'PreconditionFailed' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 412, ], 'exception' => true, ], 'PriceClass' => [ 'type' => 'string', 'enum' => [ 'PriceClass_100', 'PriceClass_200', 'PriceClass_All', ], ], 'Restrictions' => [ 'type' => 'structure', 'required' => [ 'GeoRestriction', ], 'members' => [ 'GeoRestriction' => [ 'shape' => 'GeoRestriction', ], ], ], 'S3Origin' => [ 'type' => 'structure', 'required' => [ 'DomainName', 'OriginAccessIdentity', ], 'members' => [ 'DomainName' => [ 'shape' => 'string', ], 'OriginAccessIdentity' => [ 'shape' => 'string', ], ], ], 'S3OriginConfig' => [ 'type' => 'structure', 'required' => [ 'OriginAccessIdentity', ], 'members' => [ 'OriginAccessIdentity' => [ 'shape' => 'string', ], ], ], 'SSLSupportMethod' => [ 'type' => 'string', 'enum' => [ 'sni-only', 'vip', ], ], 'Signer' => [ 'type' => 'structure', 'members' => [ 'AwsAccountNumber' => [ 'shape' => 'string', ], 'KeyPairIds' => [ 'shape' => 'KeyPairIds', ], ], ], 'SignerList' => [ 'type' => 'list', 'member' => [ 'shape' => 'Signer', 'locationName' => 'Signer', ], ], 'StreamingDistribution' => [ 'type' => 'structure', 'required' => [ 'Id', 'Status', 'DomainName', 'ActiveTrustedSigners', 'StreamingDistributionConfig', ], 'members' => [ 'Id' => [ 'shape' => 'string', ], 'Status' => [ 'shape' => 'string', ], 'LastModifiedTime' => [ 'shape' => 'timestamp', ], 'DomainName' => [ 'shape' => 'string', ], 'ActiveTrustedSigners' => [ 'shape' => 'ActiveTrustedSigners', ], 'StreamingDistributionConfig' => [ 'shape' => 'StreamingDistributionConfig', ], ], ], 'StreamingDistributionAlreadyExists' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 409, ], 'exception' => true, ], 'StreamingDistributionConfig' => [ 'type' => 'structure', 'required' => [ 'CallerReference', 'S3Origin', 'Comment', 'TrustedSigners', 'Enabled', ], 'members' => [ 'CallerReference' => [ 'shape' => 'string', ], 'S3Origin' => [ 'shape' => 'S3Origin', ], 'Aliases' => [ 'shape' => 'Aliases', ], 'Comment' => [ 'shape' => 'string', ], 'Logging' => [ 'shape' => 'StreamingLoggingConfig', ], 'TrustedSigners' => [ 'shape' => 'TrustedSigners', ], 'PriceClass' => [ 'shape' => 'PriceClass', ], 'Enabled' => [ 'shape' => 'boolean', ], ], ], 'StreamingDistributionList' => [ 'type' => 'structure', 'required' => [ 'Marker', 'MaxItems', 'IsTruncated', 'Quantity', ], 'members' => [ 'Marker' => [ 'shape' => 'string', ], 'NextMarker' => [ 'shape' => 'string', ], 'MaxItems' => [ 'shape' => 'integer', ], 'IsTruncated' => [ 'shape' => 'boolean', ], 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'StreamingDistributionSummaryList', ], ], ], 'StreamingDistributionNotDisabled' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 409, ], 'exception' => true, ], 'StreamingDistributionSummary' => [ 'type' => 'structure', 'required' => [ 'Id', 'Status', 'LastModifiedTime', 'DomainName', 'S3Origin', 'Aliases', 'TrustedSigners', 'Comment', 'PriceClass', 'Enabled', ], 'members' => [ 'Id' => [ 'shape' => 'string', ], 'Status' => [ 'shape' => 'string', ], 'LastModifiedTime' => [ 'shape' => 'timestamp', ], 'DomainName' => [ 'shape' => 'string', ], 'S3Origin' => [ 'shape' => 'S3Origin', ], 'Aliases' => [ 'shape' => 'Aliases', ], 'TrustedSigners' => [ 'shape' => 'TrustedSigners', ], 'Comment' => [ 'shape' => 'string', ], 'PriceClass' => [ 'shape' => 'PriceClass', ], 'Enabled' => [ 'shape' => 'boolean', ], ], ], 'StreamingDistributionSummaryList' => [ 'type' => 'list', 'member' => [ 'shape' => 'StreamingDistributionSummary', 'locationName' => 'StreamingDistributionSummary', ], ], 'StreamingLoggingConfig' => [ 'type' => 'structure', 'required' => [ 'Enabled', 'Bucket', 'Prefix', ], 'members' => [ 'Enabled' => [ 'shape' => 'boolean', ], 'Bucket' => [ 'shape' => 'string', ], 'Prefix' => [ 'shape' => 'string', ], ], ], 'TooManyCacheBehaviors' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TooManyCertificates' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TooManyCloudFrontOriginAccessIdentities' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TooManyCookieNamesInWhiteList' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TooManyDistributionCNAMEs' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TooManyDistributions' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TooManyHeadersInForwardedValues' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TooManyInvalidationsInProgress' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TooManyOrigins' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TooManyStreamingDistributionCNAMEs' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TooManyStreamingDistributions' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TooManyTrustedSigners' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TrustedSignerDoesNotExist' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TrustedSigners' => [ 'type' => 'structure', 'required' => [ 'Enabled', 'Quantity', ], 'members' => [ 'Enabled' => [ 'shape' => 'boolean', ], 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'AwsAccountNumberList', ], ], ], 'UpdateCloudFrontOriginAccessIdentityRequest' => [ 'type' => 'structure', 'required' => [ 'CloudFrontOriginAccessIdentityConfig', 'Id', ], 'members' => [ 'CloudFrontOriginAccessIdentityConfig' => [ 'shape' => 'CloudFrontOriginAccessIdentityConfig', 'locationName' => 'CloudFrontOriginAccessIdentityConfig', 'xmlNamespace' => [ 'uri' => 'http://cloudfront.amazonaws.com/doc/2015-07-27/', ], ], 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], 'IfMatch' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'If-Match', ], ], 'payload' => 'CloudFrontOriginAccessIdentityConfig', ], 'UpdateCloudFrontOriginAccessIdentityResult' => [ 'type' => 'structure', 'members' => [ 'CloudFrontOriginAccessIdentity' => [ 'shape' => 'CloudFrontOriginAccessIdentity', ], 'ETag' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'ETag', ], ], 'payload' => 'CloudFrontOriginAccessIdentity', ], 'UpdateDistributionRequest' => [ 'type' => 'structure', 'required' => [ 'DistributionConfig', 'Id', ], 'members' => [ 'DistributionConfig' => [ 'shape' => 'DistributionConfig', 'locationName' => 'DistributionConfig', 'xmlNamespace' => [ 'uri' => 'http://cloudfront.amazonaws.com/doc/2015-07-27/', ], ], 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], 'IfMatch' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'If-Match', ], ], 'payload' => 'DistributionConfig', ], 'UpdateDistributionResult' => [ 'type' => 'structure', 'members' => [ 'Distribution' => [ 'shape' => 'Distribution', ], 'ETag' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'ETag', ], ], 'payload' => 'Distribution', ], 'UpdateStreamingDistributionRequest' => [ 'type' => 'structure', 'required' => [ 'StreamingDistributionConfig', 'Id', ], 'members' => [ 'StreamingDistributionConfig' => [ 'shape' => 'StreamingDistributionConfig', 'locationName' => 'StreamingDistributionConfig', 'xmlNamespace' => [ 'uri' => 'http://cloudfront.amazonaws.com/doc/2015-07-27/', ], ], 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], 'IfMatch' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'If-Match', ], ], 'payload' => 'StreamingDistributionConfig', ], 'UpdateStreamingDistributionResult' => [ 'type' => 'structure', 'members' => [ 'StreamingDistribution' => [ 'shape' => 'StreamingDistribution', ], 'ETag' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'ETag', ], ], 'payload' => 'StreamingDistribution', ], 'ViewerCertificate' => [ 'type' => 'structure', 'members' => [ 'IAMCertificateId' => [ 'shape' => 'string', ], 'CloudFrontDefaultCertificate' => [ 'shape' => 'boolean', ], 'SSLSupportMethod' => [ 'shape' => 'SSLSupportMethod', ], 'MinimumProtocolVersion' => [ 'shape' => 'MinimumProtocolVersion', ], ], ], 'ViewerProtocolPolicy' => [ 'type' => 'string', 'enum' => [ 'allow-all', 'https-only', 'redirect-to-https', ], ], 'boolean' => [ 'type' => 'boolean', ], 'integer' => [ 'type' => 'integer', ], 'long' => [ 'type' => 'long', ], 'string' => [ 'type' => 'string', ], 'timestamp' => [ 'type' => 'timestamp', ], ],];
|
lib/Aws/Aws/data/cloudfront/2015-07-27/paginators-1.json.php
ADDED
@@ -0,0 +1,3 @@
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
// This file was auto-generated from sdk-root/src/data/cloudfront/2015-07-27/paginators-1.json
|
3 |
+
return [ 'pagination' => [ 'ListCloudFrontOriginAccessIdentities' => [ 'input_token' => 'Marker', 'limit_key' => 'MaxItems', 'more_results' => 'CloudFrontOriginAccessIdentityList.IsTruncated', 'output_token' => 'CloudFrontOriginAccessIdentityList.NextMarker', 'result_key' => 'CloudFrontOriginAccessIdentityList.Items', ], 'ListDistributions' => [ 'input_token' => 'Marker', 'limit_key' => 'MaxItems', 'more_results' => 'DistributionList.IsTruncated', 'output_token' => 'DistributionList.NextMarker', 'result_key' => 'DistributionList.Items', ], 'ListInvalidations' => [ 'input_token' => 'Marker', 'limit_key' => 'MaxItems', 'more_results' => 'InvalidationList.IsTruncated', 'output_token' => 'InvalidationList.NextMarker', 'result_key' => 'InvalidationList.Items', ], 'ListStreamingDistributions' => [ 'input_token' => 'Marker', 'limit_key' => 'MaxItems', 'more_results' => 'StreamingDistributionList.IsTruncated', 'output_token' => 'StreamingDistributionList.NextMarker', 'result_key' => 'StreamingDistributionList.Items', ], ],];
|
lib/Aws/Aws/data/cloudfront/2015-07-27/waiters-2.json.php
ADDED
@@ -0,0 +1,3 @@
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
// This file was auto-generated from sdk-root/src/data/cloudfront/2015-07-27/waiters-2.json
|
3 |
+
return [ 'version' => 2, 'waiters' => [ 'DistributionDeployed' => [ 'acceptors' => [ [ 'argument' => 'Distribution.Status', 'expected' => 'Deployed', 'matcher' => 'path', 'state' => 'success', ], ], 'delay' => 60, 'description' => 'Wait until a distribution is deployed.', 'maxAttempts' => 25, 'operation' => 'GetDistribution', ], 'InvalidationCompleted' => [ 'acceptors' => [ [ 'argument' => 'Invalidation.Status', 'expected' => 'Completed', 'matcher' => 'path', 'state' => 'success', ], ], 'delay' => 20, 'description' => 'Wait until an invalidation has completed.', 'maxAttempts' => 30, 'operation' => 'GetInvalidation', ], 'StreamingDistributionDeployed' => [ 'acceptors' => [ [ 'argument' => 'StreamingDistribution.Status', 'expected' => 'Deployed', 'matcher' => 'path', 'state' => 'success', ], ], 'delay' => 60, 'description' => 'Wait until a streaming distribution is deployed.', 'maxAttempts' => 25, 'operation' => 'GetStreamingDistribution', ], ],];
|
lib/Aws/Aws/data/cloudfront/2016-01-28/api-2.json.php
ADDED
@@ -0,0 +1,3 @@
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
// This file was auto-generated from sdk-root/src/data/cloudfront/2016-01-28/api-2.json
|
3 |
+
return [ 'version' => '2.0', 'metadata' => [ 'uid' => 'cloudfront-2016-01-28', 'apiVersion' => '2016-01-28', 'endpointPrefix' => 'cloudfront', 'globalEndpoint' => 'cloudfront.amazonaws.com', 'protocol' => 'rest-xml', 'serviceAbbreviation' => 'CloudFront', 'serviceFullName' => 'Amazon CloudFront', 'signatureVersion' => 'v4', ], 'operations' => [ 'CreateCloudFrontOriginAccessIdentity' => [ 'name' => 'CreateCloudFrontOriginAccessIdentity2016_01_28', 'http' => [ 'method' => 'POST', 'requestUri' => '/2016-01-28/origin-access-identity/cloudfront', 'responseCode' => 201, ], 'input' => [ 'shape' => 'CreateCloudFrontOriginAccessIdentityRequest', ], 'output' => [ 'shape' => 'CreateCloudFrontOriginAccessIdentityResult', ], 'errors' => [ [ 'shape' => 'CloudFrontOriginAccessIdentityAlreadyExists', ], [ 'shape' => 'MissingBody', ], [ 'shape' => 'TooManyCloudFrontOriginAccessIdentities', ], [ 'shape' => 'InvalidArgument', ], [ 'shape' => 'InconsistentQuantities', ], ], ], 'CreateDistribution' => [ 'name' => 'CreateDistribution2016_01_28', 'http' => [ 'method' => 'POST', 'requestUri' => '/2016-01-28/distribution', 'responseCode' => 201, ], 'input' => [ 'shape' => 'CreateDistributionRequest', ], 'output' => [ 'shape' => 'CreateDistributionResult', ], 'errors' => [ [ 'shape' => 'CNAMEAlreadyExists', ], [ 'shape' => 'DistributionAlreadyExists', ], [ 'shape' => 'InvalidOrigin', ], [ 'shape' => 'InvalidOriginAccessIdentity', ], [ 'shape' => 'AccessDenied', ], [ 'shape' => 'TooManyTrustedSigners', ], [ 'shape' => 'TrustedSignerDoesNotExist', ], [ 'shape' => 'InvalidViewerCertificate', ], [ 'shape' => 'InvalidMinimumProtocolVersion', ], [ 'shape' => 'MissingBody', ], [ 'shape' => 'TooManyDistributionCNAMEs', ], [ 'shape' => 'TooManyDistributions', ], [ 'shape' => 'InvalidDefaultRootObject', ], [ 'shape' => 'InvalidRelativePath', ], [ 'shape' => 'InvalidErrorCode', ], [ 'shape' => 'InvalidResponseCode', ], [ 'shape' => 'InvalidArgument', ], [ 'shape' => 'InvalidRequiredProtocol', ], [ 'shape' => 'NoSuchOrigin', ], [ 'shape' => 'TooManyOrigins', ], [ 'shape' => 'TooManyCacheBehaviors', ], [ 'shape' => 'TooManyCookieNamesInWhiteList', ], [ 'shape' => 'InvalidForwardCookies', ], [ 'shape' => 'TooManyHeadersInForwardedValues', ], [ 'shape' => 'InvalidHeadersForS3Origin', ], [ 'shape' => 'InconsistentQuantities', ], [ 'shape' => 'TooManyCertificates', ], [ 'shape' => 'InvalidLocationCode', ], [ 'shape' => 'InvalidGeoRestrictionParameter', ], [ 'shape' => 'InvalidProtocolSettings', ], [ 'shape' => 'InvalidTTLOrder', ], [ 'shape' => 'InvalidWebACLId', ], [ 'shape' => 'TooManyOriginCustomHeaders', ], ], ], 'CreateInvalidation' => [ 'name' => 'CreateInvalidation2016_01_28', 'http' => [ 'method' => 'POST', 'requestUri' => '/2016-01-28/distribution/{DistributionId}/invalidation', 'responseCode' => 201, ], 'input' => [ 'shape' => 'CreateInvalidationRequest', ], 'output' => [ 'shape' => 'CreateInvalidationResult', ], 'errors' => [ [ 'shape' => 'AccessDenied', ], [ 'shape' => 'MissingBody', ], [ 'shape' => 'InvalidArgument', ], [ 'shape' => 'NoSuchDistribution', ], [ 'shape' => 'BatchTooLarge', ], [ 'shape' => 'TooManyInvalidationsInProgress', ], [ 'shape' => 'InconsistentQuantities', ], ], ], 'CreateStreamingDistribution' => [ 'name' => 'CreateStreamingDistribution2016_01_28', 'http' => [ 'method' => 'POST', 'requestUri' => '/2016-01-28/streaming-distribution', 'responseCode' => 201, ], 'input' => [ 'shape' => 'CreateStreamingDistributionRequest', ], 'output' => [ 'shape' => 'CreateStreamingDistributionResult', ], 'errors' => [ [ 'shape' => 'CNAMEAlreadyExists', ], [ 'shape' => 'StreamingDistributionAlreadyExists', ], [ 'shape' => 'InvalidOrigin', ], [ 'shape' => 'InvalidOriginAccessIdentity', ], [ 'shape' => 'AccessDenied', ], [ 'shape' => 'TooManyTrustedSigners', ], [ 'shape' => 'TrustedSignerDoesNotExist', ], [ 'shape' => 'MissingBody', ], [ 'shape' => 'TooManyStreamingDistributionCNAMEs', ], [ 'shape' => 'TooManyStreamingDistributions', ], [ 'shape' => 'InvalidArgument', ], [ 'shape' => 'InconsistentQuantities', ], ], ], 'DeleteCloudFrontOriginAccessIdentity' => [ 'name' => 'DeleteCloudFrontOriginAccessIdentity2016_01_28', 'http' => [ 'method' => 'DELETE', 'requestUri' => '/2016-01-28/origin-access-identity/cloudfront/{Id}', 'responseCode' => 204, ], 'input' => [ 'shape' => 'DeleteCloudFrontOriginAccessIdentityRequest', ], 'errors' => [ [ 'shape' => 'AccessDenied', ], [ 'shape' => 'InvalidIfMatchVersion', ], [ 'shape' => 'NoSuchCloudFrontOriginAccessIdentity', ], [ 'shape' => 'PreconditionFailed', ], [ 'shape' => 'CloudFrontOriginAccessIdentityInUse', ], ], ], 'DeleteDistribution' => [ 'name' => 'DeleteDistribution2016_01_28', 'http' => [ 'method' => 'DELETE', 'requestUri' => '/2016-01-28/distribution/{Id}', 'responseCode' => 204, ], 'input' => [ 'shape' => 'DeleteDistributionRequest', ], 'errors' => [ [ 'shape' => 'AccessDenied', ], [ 'shape' => 'DistributionNotDisabled', ], [ 'shape' => 'InvalidIfMatchVersion', ], [ 'shape' => 'NoSuchDistribution', ], [ 'shape' => 'PreconditionFailed', ], ], ], 'DeleteStreamingDistribution' => [ 'name' => 'DeleteStreamingDistribution2016_01_28', 'http' => [ 'method' => 'DELETE', 'requestUri' => '/2016-01-28/streaming-distribution/{Id}', 'responseCode' => 204, ], 'input' => [ 'shape' => 'DeleteStreamingDistributionRequest', ], 'errors' => [ [ 'shape' => 'AccessDenied', ], [ 'shape' => 'StreamingDistributionNotDisabled', ], [ 'shape' => 'InvalidIfMatchVersion', ], [ 'shape' => 'NoSuchStreamingDistribution', ], [ 'shape' => 'PreconditionFailed', ], ], ], 'GetCloudFrontOriginAccessIdentity' => [ 'name' => 'GetCloudFrontOriginAccessIdentity2016_01_28', 'http' => [ 'method' => 'GET', 'requestUri' => '/2016-01-28/origin-access-identity/cloudfront/{Id}', ], 'input' => [ 'shape' => 'GetCloudFrontOriginAccessIdentityRequest', ], 'output' => [ 'shape' => 'GetCloudFrontOriginAccessIdentityResult', ], 'errors' => [ [ 'shape' => 'NoSuchCloudFrontOriginAccessIdentity', ], [ 'shape' => 'AccessDenied', ], ], ], 'GetCloudFrontOriginAccessIdentityConfig' => [ 'name' => 'GetCloudFrontOriginAccessIdentityConfig2016_01_28', 'http' => [ 'method' => 'GET', 'requestUri' => '/2016-01-28/origin-access-identity/cloudfront/{Id}/config', ], 'input' => [ 'shape' => 'GetCloudFrontOriginAccessIdentityConfigRequest', ], 'output' => [ 'shape' => 'GetCloudFrontOriginAccessIdentityConfigResult', ], 'errors' => [ [ 'shape' => 'NoSuchCloudFrontOriginAccessIdentity', ], [ 'shape' => 'AccessDenied', ], ], ], 'GetDistribution' => [ 'name' => 'GetDistribution2016_01_28', 'http' => [ 'method' => 'GET', 'requestUri' => '/2016-01-28/distribution/{Id}', ], 'input' => [ 'shape' => 'GetDistributionRequest', ], 'output' => [ 'shape' => 'GetDistributionResult', ], 'errors' => [ [ 'shape' => 'NoSuchDistribution', ], [ 'shape' => 'AccessDenied', ], ], ], 'GetDistributionConfig' => [ 'name' => 'GetDistributionConfig2016_01_28', 'http' => [ 'method' => 'GET', 'requestUri' => '/2016-01-28/distribution/{Id}/config', ], 'input' => [ 'shape' => 'GetDistributionConfigRequest', ], 'output' => [ 'shape' => 'GetDistributionConfigResult', ], 'errors' => [ [ 'shape' => 'NoSuchDistribution', ], [ 'shape' => 'AccessDenied', ], ], ], 'GetInvalidation' => [ 'name' => 'GetInvalidation2016_01_28', 'http' => [ 'method' => 'GET', 'requestUri' => '/2016-01-28/distribution/{DistributionId}/invalidation/{Id}', ], 'input' => [ 'shape' => 'GetInvalidationRequest', ], 'output' => [ 'shape' => 'GetInvalidationResult', ], 'errors' => [ [ 'shape' => 'NoSuchInvalidation', ], [ 'shape' => 'NoSuchDistribution', ], [ 'shape' => 'AccessDenied', ], ], ], 'GetStreamingDistribution' => [ 'name' => 'GetStreamingDistribution2016_01_28', 'http' => [ 'method' => 'GET', 'requestUri' => '/2016-01-28/streaming-distribution/{Id}', ], 'input' => [ 'shape' => 'GetStreamingDistributionRequest', ], 'output' => [ 'shape' => 'GetStreamingDistributionResult', ], 'errors' => [ [ 'shape' => 'NoSuchStreamingDistribution', ], [ 'shape' => 'AccessDenied', ], ], ], 'GetStreamingDistributionConfig' => [ 'name' => 'GetStreamingDistributionConfig2016_01_28', 'http' => [ 'method' => 'GET', 'requestUri' => '/2016-01-28/streaming-distribution/{Id}/config', ], 'input' => [ 'shape' => 'GetStreamingDistributionConfigRequest', ], 'output' => [ 'shape' => 'GetStreamingDistributionConfigResult', ], 'errors' => [ [ 'shape' => 'NoSuchStreamingDistribution', ], [ 'shape' => 'AccessDenied', ], ], ], 'ListCloudFrontOriginAccessIdentities' => [ 'name' => 'ListCloudFrontOriginAccessIdentities2016_01_28', 'http' => [ 'method' => 'GET', 'requestUri' => '/2016-01-28/origin-access-identity/cloudfront', ], 'input' => [ 'shape' => 'ListCloudFrontOriginAccessIdentitiesRequest', ], 'output' => [ 'shape' => 'ListCloudFrontOriginAccessIdentitiesResult', ], 'errors' => [ [ 'shape' => 'InvalidArgument', ], ], ], 'ListDistributions' => [ 'name' => 'ListDistributions2016_01_28', 'http' => [ 'method' => 'GET', 'requestUri' => '/2016-01-28/distribution', ], 'input' => [ 'shape' => 'ListDistributionsRequest', ], 'output' => [ 'shape' => 'ListDistributionsResult', ], 'errors' => [ [ 'shape' => 'InvalidArgument', ], ], ], 'ListDistributionsByWebACLId' => [ 'name' => 'ListDistributionsByWebACLId2016_01_28', 'http' => [ 'method' => 'GET', 'requestUri' => '/2016-01-28/distributionsByWebACLId/{WebACLId}', ], 'input' => [ 'shape' => 'ListDistributionsByWebACLIdRequest', ], 'output' => [ 'shape' => 'ListDistributionsByWebACLIdResult', ], 'errors' => [ [ 'shape' => 'InvalidArgument', ], [ 'shape' => 'InvalidWebACLId', ], ], ], 'ListInvalidations' => [ 'name' => 'ListInvalidations2016_01_28', 'http' => [ 'method' => 'GET', 'requestUri' => '/2016-01-28/distribution/{DistributionId}/invalidation', ], 'input' => [ 'shape' => 'ListInvalidationsRequest', ], 'output' => [ 'shape' => 'ListInvalidationsResult', ], 'errors' => [ [ 'shape' => 'InvalidArgument', ], [ 'shape' => 'NoSuchDistribution', ], [ 'shape' => 'AccessDenied', ], ], ], 'ListStreamingDistributions' => [ 'name' => 'ListStreamingDistributions2016_01_28', 'http' => [ 'method' => 'GET', 'requestUri' => '/2016-01-28/streaming-distribution', ], 'input' => [ 'shape' => 'ListStreamingDistributionsRequest', ], 'output' => [ 'shape' => 'ListStreamingDistributionsResult', ], 'errors' => [ [ 'shape' => 'InvalidArgument', ], ], ], 'UpdateCloudFrontOriginAccessIdentity' => [ 'name' => 'UpdateCloudFrontOriginAccessIdentity2016_01_28', 'http' => [ 'method' => 'PUT', 'requestUri' => '/2016-01-28/origin-access-identity/cloudfront/{Id}/config', ], 'input' => [ 'shape' => 'UpdateCloudFrontOriginAccessIdentityRequest', ], 'output' => [ 'shape' => 'UpdateCloudFrontOriginAccessIdentityResult', ], 'errors' => [ [ 'shape' => 'AccessDenied', ], [ 'shape' => 'IllegalUpdate', ], [ 'shape' => 'InvalidIfMatchVersion', ], [ 'shape' => 'MissingBody', ], [ 'shape' => 'NoSuchCloudFrontOriginAccessIdentity', ], [ 'shape' => 'PreconditionFailed', ], [ 'shape' => 'InvalidArgument', ], [ 'shape' => 'InconsistentQuantities', ], ], ], 'UpdateDistribution' => [ 'name' => 'UpdateDistribution2016_01_28', 'http' => [ 'method' => 'PUT', 'requestUri' => '/2016-01-28/distribution/{Id}/config', ], 'input' => [ 'shape' => 'UpdateDistributionRequest', ], 'output' => [ 'shape' => 'UpdateDistributionResult', ], 'errors' => [ [ 'shape' => 'AccessDenied', ], [ 'shape' => 'CNAMEAlreadyExists', ], [ 'shape' => 'IllegalUpdate', ], [ 'shape' => 'InvalidIfMatchVersion', ], [ 'shape' => 'MissingBody', ], [ 'shape' => 'NoSuchDistribution', ], [ 'shape' => 'PreconditionFailed', ], [ 'shape' => 'TooManyDistributionCNAMEs', ], [ 'shape' => 'InvalidDefaultRootObject', ], [ 'shape' => 'InvalidRelativePath', ], [ 'shape' => 'InvalidErrorCode', ], [ 'shape' => 'InvalidResponseCode', ], [ 'shape' => 'InvalidArgument', ], [ 'shape' => 'InvalidOriginAccessIdentity', ], [ 'shape' => 'TooManyTrustedSigners', ], [ 'shape' => 'TrustedSignerDoesNotExist', ], [ 'shape' => 'InvalidViewerCertificate', ], [ 'shape' => 'InvalidMinimumProtocolVersion', ], [ 'shape' => 'InvalidRequiredProtocol', ], [ 'shape' => 'NoSuchOrigin', ], [ 'shape' => 'TooManyOrigins', ], [ 'shape' => 'TooManyCacheBehaviors', ], [ 'shape' => 'TooManyCookieNamesInWhiteList', ], [ 'shape' => 'InvalidForwardCookies', ], [ 'shape' => 'TooManyHeadersInForwardedValues', ], [ 'shape' => 'InvalidHeadersForS3Origin', ], [ 'shape' => 'InconsistentQuantities', ], [ 'shape' => 'TooManyCertificates', ], [ 'shape' => 'InvalidLocationCode', ], [ 'shape' => 'InvalidGeoRestrictionParameter', ], [ 'shape' => 'InvalidTTLOrder', ], [ 'shape' => 'InvalidWebACLId', ], [ 'shape' => 'TooManyOriginCustomHeaders', ], ], ], 'UpdateStreamingDistribution' => [ 'name' => 'UpdateStreamingDistribution2016_01_28', 'http' => [ 'method' => 'PUT', 'requestUri' => '/2016-01-28/streaming-distribution/{Id}/config', ], 'input' => [ 'shape' => 'UpdateStreamingDistributionRequest', ], 'output' => [ 'shape' => 'UpdateStreamingDistributionResult', ], 'errors' => [ [ 'shape' => 'AccessDenied', ], [ 'shape' => 'CNAMEAlreadyExists', ], [ 'shape' => 'IllegalUpdate', ], [ 'shape' => 'InvalidIfMatchVersion', ], [ 'shape' => 'MissingBody', ], [ 'shape' => 'NoSuchStreamingDistribution', ], [ 'shape' => 'PreconditionFailed', ], [ 'shape' => 'TooManyStreamingDistributionCNAMEs', ], [ 'shape' => 'InvalidArgument', ], [ 'shape' => 'InvalidOriginAccessIdentity', ], [ 'shape' => 'TooManyTrustedSigners', ], [ 'shape' => 'TrustedSignerDoesNotExist', ], [ 'shape' => 'InconsistentQuantities', ], ], ], ], 'shapes' => [ 'AccessDenied' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 403, ], 'exception' => true, ], 'ActiveTrustedSigners' => [ 'type' => 'structure', 'required' => [ 'Enabled', 'Quantity', ], 'members' => [ 'Enabled' => [ 'shape' => 'boolean', ], 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'SignerList', ], ], ], 'AliasList' => [ 'type' => 'list', 'member' => [ 'shape' => 'string', 'locationName' => 'CNAME', ], ], 'Aliases' => [ 'type' => 'structure', 'required' => [ 'Quantity', ], 'members' => [ 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'AliasList', ], ], ], 'AllowedMethods' => [ 'type' => 'structure', 'required' => [ 'Quantity', 'Items', ], 'members' => [ 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'MethodsList', ], 'CachedMethods' => [ 'shape' => 'CachedMethods', ], ], ], 'AwsAccountNumberList' => [ 'type' => 'list', 'member' => [ 'shape' => 'string', 'locationName' => 'AwsAccountNumber', ], ], 'BatchTooLarge' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 413, ], 'exception' => true, ], 'CNAMEAlreadyExists' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 409, ], 'exception' => true, ], 'CacheBehavior' => [ 'type' => 'structure', 'required' => [ 'PathPattern', 'TargetOriginId', 'ForwardedValues', 'TrustedSigners', 'ViewerProtocolPolicy', 'MinTTL', ], 'members' => [ 'PathPattern' => [ 'shape' => 'string', ], 'TargetOriginId' => [ 'shape' => 'string', ], 'ForwardedValues' => [ 'shape' => 'ForwardedValues', ], 'TrustedSigners' => [ 'shape' => 'TrustedSigners', ], 'ViewerProtocolPolicy' => [ 'shape' => 'ViewerProtocolPolicy', ], 'MinTTL' => [ 'shape' => 'long', ], 'AllowedMethods' => [ 'shape' => 'AllowedMethods', ], 'SmoothStreaming' => [ 'shape' => 'boolean', ], 'DefaultTTL' => [ 'shape' => 'long', ], 'MaxTTL' => [ 'shape' => 'long', ], 'Compress' => [ 'shape' => 'boolean', ], ], ], 'CacheBehaviorList' => [ 'type' => 'list', 'member' => [ 'shape' => 'CacheBehavior', 'locationName' => 'CacheBehavior', ], ], 'CacheBehaviors' => [ 'type' => 'structure', 'required' => [ 'Quantity', ], 'members' => [ 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'CacheBehaviorList', ], ], ], 'CachedMethods' => [ 'type' => 'structure', 'required' => [ 'Quantity', 'Items', ], 'members' => [ 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'MethodsList', ], ], ], 'CertificateSource' => [ 'type' => 'string', 'enum' => [ 'cloudfront', 'iam', 'acm', ], ], 'CloudFrontOriginAccessIdentity' => [ 'type' => 'structure', 'required' => [ 'Id', 'S3CanonicalUserId', ], 'members' => [ 'Id' => [ 'shape' => 'string', ], 'S3CanonicalUserId' => [ 'shape' => 'string', ], 'CloudFrontOriginAccessIdentityConfig' => [ 'shape' => 'CloudFrontOriginAccessIdentityConfig', ], ], ], 'CloudFrontOriginAccessIdentityAlreadyExists' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 409, ], 'exception' => true, ], 'CloudFrontOriginAccessIdentityConfig' => [ 'type' => 'structure', 'required' => [ 'CallerReference', 'Comment', ], 'members' => [ 'CallerReference' => [ 'shape' => 'string', ], 'Comment' => [ 'shape' => 'string', ], ], ], 'CloudFrontOriginAccessIdentityInUse' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 409, ], 'exception' => true, ], 'CloudFrontOriginAccessIdentityList' => [ 'type' => 'structure', 'required' => [ 'Marker', 'MaxItems', 'IsTruncated', 'Quantity', ], 'members' => [ 'Marker' => [ 'shape' => 'string', ], 'NextMarker' => [ 'shape' => 'string', ], 'MaxItems' => [ 'shape' => 'integer', ], 'IsTruncated' => [ 'shape' => 'boolean', ], 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'CloudFrontOriginAccessIdentitySummaryList', ], ], ], 'CloudFrontOriginAccessIdentitySummary' => [ 'type' => 'structure', 'required' => [ 'Id', 'S3CanonicalUserId', 'Comment', ], 'members' => [ 'Id' => [ 'shape' => 'string', ], 'S3CanonicalUserId' => [ 'shape' => 'string', ], 'Comment' => [ 'shape' => 'string', ], ], ], 'CloudFrontOriginAccessIdentitySummaryList' => [ 'type' => 'list', 'member' => [ 'shape' => 'CloudFrontOriginAccessIdentitySummary', 'locationName' => 'CloudFrontOriginAccessIdentitySummary', ], ], 'CookieNameList' => [ 'type' => 'list', 'member' => [ 'shape' => 'string', 'locationName' => 'Name', ], ], 'CookieNames' => [ 'type' => 'structure', 'required' => [ 'Quantity', ], 'members' => [ 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'CookieNameList', ], ], ], 'CookiePreference' => [ 'type' => 'structure', 'required' => [ 'Forward', ], 'members' => [ 'Forward' => [ 'shape' => 'ItemSelection', ], 'WhitelistedNames' => [ 'shape' => 'CookieNames', ], ], ], 'CreateCloudFrontOriginAccessIdentityRequest' => [ 'type' => 'structure', 'required' => [ 'CloudFrontOriginAccessIdentityConfig', ], 'members' => [ 'CloudFrontOriginAccessIdentityConfig' => [ 'shape' => 'CloudFrontOriginAccessIdentityConfig', 'locationName' => 'CloudFrontOriginAccessIdentityConfig', 'xmlNamespace' => [ 'uri' => 'http://cloudfront.amazonaws.com/doc/2016-01-28/', ], ], ], 'payload' => 'CloudFrontOriginAccessIdentityConfig', ], 'CreateCloudFrontOriginAccessIdentityResult' => [ 'type' => 'structure', 'members' => [ 'CloudFrontOriginAccessIdentity' => [ 'shape' => 'CloudFrontOriginAccessIdentity', ], 'Location' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'Location', ], 'ETag' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'ETag', ], ], 'payload' => 'CloudFrontOriginAccessIdentity', ], 'CreateDistributionRequest' => [ 'type' => 'structure', 'required' => [ 'DistributionConfig', ], 'members' => [ 'DistributionConfig' => [ 'shape' => 'DistributionConfig', 'locationName' => 'DistributionConfig', 'xmlNamespace' => [ 'uri' => 'http://cloudfront.amazonaws.com/doc/2016-01-28/', ], ], ], 'payload' => 'DistributionConfig', ], 'CreateDistributionResult' => [ 'type' => 'structure', 'members' => [ 'Distribution' => [ 'shape' => 'Distribution', ], 'Location' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'Location', ], 'ETag' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'ETag', ], ], 'payload' => 'Distribution', ], 'CreateInvalidationRequest' => [ 'type' => 'structure', 'required' => [ 'DistributionId', 'InvalidationBatch', ], 'members' => [ 'DistributionId' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'DistributionId', ], 'InvalidationBatch' => [ 'shape' => 'InvalidationBatch', 'locationName' => 'InvalidationBatch', 'xmlNamespace' => [ 'uri' => 'http://cloudfront.amazonaws.com/doc/2016-01-28/', ], ], ], 'payload' => 'InvalidationBatch', ], 'CreateInvalidationResult' => [ 'type' => 'structure', 'members' => [ 'Location' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'Location', ], 'Invalidation' => [ 'shape' => 'Invalidation', ], ], 'payload' => 'Invalidation', ], 'CreateStreamingDistributionRequest' => [ 'type' => 'structure', 'required' => [ 'StreamingDistributionConfig', ], 'members' => [ 'StreamingDistributionConfig' => [ 'shape' => 'StreamingDistributionConfig', 'locationName' => 'StreamingDistributionConfig', 'xmlNamespace' => [ 'uri' => 'http://cloudfront.amazonaws.com/doc/2016-01-28/', ], ], ], 'payload' => 'StreamingDistributionConfig', ], 'CreateStreamingDistributionResult' => [ 'type' => 'structure', 'members' => [ 'StreamingDistribution' => [ 'shape' => 'StreamingDistribution', ], 'Location' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'Location', ], 'ETag' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'ETag', ], ], 'payload' => 'StreamingDistribution', ], 'CustomErrorResponse' => [ 'type' => 'structure', 'required' => [ 'ErrorCode', ], 'members' => [ 'ErrorCode' => [ 'shape' => 'integer', ], 'ResponsePagePath' => [ 'shape' => 'string', ], 'ResponseCode' => [ 'shape' => 'string', ], 'ErrorCachingMinTTL' => [ 'shape' => 'long', ], ], ], 'CustomErrorResponseList' => [ 'type' => 'list', 'member' => [ 'shape' => 'CustomErrorResponse', 'locationName' => 'CustomErrorResponse', ], ], 'CustomErrorResponses' => [ 'type' => 'structure', 'required' => [ 'Quantity', ], 'members' => [ 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'CustomErrorResponseList', ], ], ], 'CustomHeaders' => [ 'type' => 'structure', 'required' => [ 'Quantity', ], 'members' => [ 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'OriginCustomHeadersList', ], ], ], 'CustomOriginConfig' => [ 'type' => 'structure', 'required' => [ 'HTTPPort', 'HTTPSPort', 'OriginProtocolPolicy', ], 'members' => [ 'HTTPPort' => [ 'shape' => 'integer', ], 'HTTPSPort' => [ 'shape' => 'integer', ], 'OriginProtocolPolicy' => [ 'shape' => 'OriginProtocolPolicy', ], 'OriginSslProtocols' => [ 'shape' => 'OriginSslProtocols', ], ], ], 'DefaultCacheBehavior' => [ 'type' => 'structure', 'required' => [ 'TargetOriginId', 'ForwardedValues', 'TrustedSigners', 'ViewerProtocolPolicy', 'MinTTL', ], 'members' => [ 'TargetOriginId' => [ 'shape' => 'string', ], 'ForwardedValues' => [ 'shape' => 'ForwardedValues', ], 'TrustedSigners' => [ 'shape' => 'TrustedSigners', ], 'ViewerProtocolPolicy' => [ 'shape' => 'ViewerProtocolPolicy', ], 'MinTTL' => [ 'shape' => 'long', ], 'AllowedMethods' => [ 'shape' => 'AllowedMethods', ], 'SmoothStreaming' => [ 'shape' => 'boolean', ], 'DefaultTTL' => [ 'shape' => 'long', ], 'MaxTTL' => [ 'shape' => 'long', ], 'Compress' => [ 'shape' => 'boolean', ], ], ], 'DeleteCloudFrontOriginAccessIdentityRequest' => [ 'type' => 'structure', 'required' => [ 'Id', ], 'members' => [ 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], 'IfMatch' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'If-Match', ], ], ], 'DeleteDistributionRequest' => [ 'type' => 'structure', 'required' => [ 'Id', ], 'members' => [ 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], 'IfMatch' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'If-Match', ], ], ], 'DeleteStreamingDistributionRequest' => [ 'type' => 'structure', 'required' => [ 'Id', ], 'members' => [ 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], 'IfMatch' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'If-Match', ], ], ], 'Distribution' => [ 'type' => 'structure', 'required' => [ 'Id', 'Status', 'LastModifiedTime', 'InProgressInvalidationBatches', 'DomainName', 'ActiveTrustedSigners', 'DistributionConfig', ], 'members' => [ 'Id' => [ 'shape' => 'string', ], 'Status' => [ 'shape' => 'string', ], 'LastModifiedTime' => [ 'shape' => 'timestamp', ], 'InProgressInvalidationBatches' => [ 'shape' => 'integer', ], 'DomainName' => [ 'shape' => 'string', ], 'ActiveTrustedSigners' => [ 'shape' => 'ActiveTrustedSigners', ], 'DistributionConfig' => [ 'shape' => 'DistributionConfig', ], ], ], 'DistributionAlreadyExists' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 409, ], 'exception' => true, ], 'DistributionConfig' => [ 'type' => 'structure', 'required' => [ 'CallerReference', 'Origins', 'DefaultCacheBehavior', 'Comment', 'Enabled', ], 'members' => [ 'CallerReference' => [ 'shape' => 'string', ], 'Aliases' => [ 'shape' => 'Aliases', ], 'DefaultRootObject' => [ 'shape' => 'string', ], 'Origins' => [ 'shape' => 'Origins', ], 'DefaultCacheBehavior' => [ 'shape' => 'DefaultCacheBehavior', ], 'CacheBehaviors' => [ 'shape' => 'CacheBehaviors', ], 'CustomErrorResponses' => [ 'shape' => 'CustomErrorResponses', ], 'Comment' => [ 'shape' => 'string', ], 'Logging' => [ 'shape' => 'LoggingConfig', ], 'PriceClass' => [ 'shape' => 'PriceClass', ], 'Enabled' => [ 'shape' => 'boolean', ], 'ViewerCertificate' => [ 'shape' => 'ViewerCertificate', ], 'Restrictions' => [ 'shape' => 'Restrictions', ], 'WebACLId' => [ 'shape' => 'string', ], ], ], 'DistributionList' => [ 'type' => 'structure', 'required' => [ 'Marker', 'MaxItems', 'IsTruncated', 'Quantity', ], 'members' => [ 'Marker' => [ 'shape' => 'string', ], 'NextMarker' => [ 'shape' => 'string', ], 'MaxItems' => [ 'shape' => 'integer', ], 'IsTruncated' => [ 'shape' => 'boolean', ], 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'DistributionSummaryList', ], ], ], 'DistributionNotDisabled' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 409, ], 'exception' => true, ], 'DistributionSummary' => [ 'type' => 'structure', 'required' => [ 'Id', 'Status', 'LastModifiedTime', 'DomainName', 'Aliases', 'Origins', 'DefaultCacheBehavior', 'CacheBehaviors', 'CustomErrorResponses', 'Comment', 'PriceClass', 'Enabled', 'ViewerCertificate', 'Restrictions', 'WebACLId', ], 'members' => [ 'Id' => [ 'shape' => 'string', ], 'Status' => [ 'shape' => 'string', ], 'LastModifiedTime' => [ 'shape' => 'timestamp', ], 'DomainName' => [ 'shape' => 'string', ], 'Aliases' => [ 'shape' => 'Aliases', ], 'Origins' => [ 'shape' => 'Origins', ], 'DefaultCacheBehavior' => [ 'shape' => 'DefaultCacheBehavior', ], 'CacheBehaviors' => [ 'shape' => 'CacheBehaviors', ], 'CustomErrorResponses' => [ 'shape' => 'CustomErrorResponses', ], 'Comment' => [ 'shape' => 'string', ], 'PriceClass' => [ 'shape' => 'PriceClass', ], 'Enabled' => [ 'shape' => 'boolean', ], 'ViewerCertificate' => [ 'shape' => 'ViewerCertificate', ], 'Restrictions' => [ 'shape' => 'Restrictions', ], 'WebACLId' => [ 'shape' => 'string', ], ], ], 'DistributionSummaryList' => [ 'type' => 'list', 'member' => [ 'shape' => 'DistributionSummary', 'locationName' => 'DistributionSummary', ], ], 'ForwardedValues' => [ 'type' => 'structure', 'required' => [ 'QueryString', 'Cookies', ], 'members' => [ 'QueryString' => [ 'shape' => 'boolean', ], 'Cookies' => [ 'shape' => 'CookiePreference', ], 'Headers' => [ 'shape' => 'Headers', ], ], ], 'GeoRestriction' => [ 'type' => 'structure', 'required' => [ 'RestrictionType', 'Quantity', ], 'members' => [ 'RestrictionType' => [ 'shape' => 'GeoRestrictionType', ], 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'LocationList', ], ], ], 'GeoRestrictionType' => [ 'type' => 'string', 'enum' => [ 'blacklist', 'whitelist', 'none', ], ], 'GetCloudFrontOriginAccessIdentityConfigRequest' => [ 'type' => 'structure', 'required' => [ 'Id', ], 'members' => [ 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], ], ], 'GetCloudFrontOriginAccessIdentityConfigResult' => [ 'type' => 'structure', 'members' => [ 'CloudFrontOriginAccessIdentityConfig' => [ 'shape' => 'CloudFrontOriginAccessIdentityConfig', ], 'ETag' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'ETag', ], ], 'payload' => 'CloudFrontOriginAccessIdentityConfig', ], 'GetCloudFrontOriginAccessIdentityRequest' => [ 'type' => 'structure', 'required' => [ 'Id', ], 'members' => [ 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], ], ], 'GetCloudFrontOriginAccessIdentityResult' => [ 'type' => 'structure', 'members' => [ 'CloudFrontOriginAccessIdentity' => [ 'shape' => 'CloudFrontOriginAccessIdentity', ], 'ETag' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'ETag', ], ], 'payload' => 'CloudFrontOriginAccessIdentity', ], 'GetDistributionConfigRequest' => [ 'type' => 'structure', 'required' => [ 'Id', ], 'members' => [ 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], ], ], 'GetDistributionConfigResult' => [ 'type' => 'structure', 'members' => [ 'DistributionConfig' => [ 'shape' => 'DistributionConfig', ], 'ETag' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'ETag', ], ], 'payload' => 'DistributionConfig', ], 'GetDistributionRequest' => [ 'type' => 'structure', 'required' => [ 'Id', ], 'members' => [ 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], ], ], 'GetDistributionResult' => [ 'type' => 'structure', 'members' => [ 'Distribution' => [ 'shape' => 'Distribution', ], 'ETag' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'ETag', ], ], 'payload' => 'Distribution', ], 'GetInvalidationRequest' => [ 'type' => 'structure', 'required' => [ 'DistributionId', 'Id', ], 'members' => [ 'DistributionId' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'DistributionId', ], 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], ], ], 'GetInvalidationResult' => [ 'type' => 'structure', 'members' => [ 'Invalidation' => [ 'shape' => 'Invalidation', ], ], 'payload' => 'Invalidation', ], 'GetStreamingDistributionConfigRequest' => [ 'type' => 'structure', 'required' => [ 'Id', ], 'members' => [ 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], ], ], 'GetStreamingDistributionConfigResult' => [ 'type' => 'structure', 'members' => [ 'StreamingDistributionConfig' => [ 'shape' => 'StreamingDistributionConfig', ], 'ETag' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'ETag', ], ], 'payload' => 'StreamingDistributionConfig', ], 'GetStreamingDistributionRequest' => [ 'type' => 'structure', 'required' => [ 'Id', ], 'members' => [ 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], ], ], 'GetStreamingDistributionResult' => [ 'type' => 'structure', 'members' => [ 'StreamingDistribution' => [ 'shape' => 'StreamingDistribution', ], 'ETag' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'ETag', ], ], 'payload' => 'StreamingDistribution', ], 'HeaderList' => [ 'type' => 'list', 'member' => [ 'shape' => 'string', 'locationName' => 'Name', ], ], 'Headers' => [ 'type' => 'structure', 'required' => [ 'Quantity', ], 'members' => [ 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'HeaderList', ], ], ], 'IllegalUpdate' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InconsistentQuantities' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidArgument' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidDefaultRootObject' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidErrorCode' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidForwardCookies' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidGeoRestrictionParameter' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidHeadersForS3Origin' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidIfMatchVersion' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidLocationCode' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidMinimumProtocolVersion' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidOrigin' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidOriginAccessIdentity' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidProtocolSettings' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidRelativePath' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidRequiredProtocol' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidResponseCode' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidTTLOrder' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidViewerCertificate' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidWebACLId' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'Invalidation' => [ 'type' => 'structure', 'required' => [ 'Id', 'Status', 'CreateTime', 'InvalidationBatch', ], 'members' => [ 'Id' => [ 'shape' => 'string', ], 'Status' => [ 'shape' => 'string', ], 'CreateTime' => [ 'shape' => 'timestamp', ], 'InvalidationBatch' => [ 'shape' => 'InvalidationBatch', ], ], ], 'InvalidationBatch' => [ 'type' => 'structure', 'required' => [ 'Paths', 'CallerReference', ], 'members' => [ 'Paths' => [ 'shape' => 'Paths', ], 'CallerReference' => [ 'shape' => 'string', ], ], ], 'InvalidationList' => [ 'type' => 'structure', 'required' => [ 'Marker', 'MaxItems', 'IsTruncated', 'Quantity', ], 'members' => [ 'Marker' => [ 'shape' => 'string', ], 'NextMarker' => [ 'shape' => 'string', ], 'MaxItems' => [ 'shape' => 'integer', ], 'IsTruncated' => [ 'shape' => 'boolean', ], 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'InvalidationSummaryList', ], ], ], 'InvalidationSummary' => [ 'type' => 'structure', 'required' => [ 'Id', 'CreateTime', 'Status', ], 'members' => [ 'Id' => [ 'shape' => 'string', ], 'CreateTime' => [ 'shape' => 'timestamp', ], 'Status' => [ 'shape' => 'string', ], ], ], 'InvalidationSummaryList' => [ 'type' => 'list', 'member' => [ 'shape' => 'InvalidationSummary', 'locationName' => 'InvalidationSummary', ], ], 'ItemSelection' => [ 'type' => 'string', 'enum' => [ 'none', 'whitelist', 'all', ], ], 'KeyPairIdList' => [ 'type' => 'list', 'member' => [ 'shape' => 'string', 'locationName' => 'KeyPairId', ], ], 'KeyPairIds' => [ 'type' => 'structure', 'required' => [ 'Quantity', ], 'members' => [ 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'KeyPairIdList', ], ], ], 'ListCloudFrontOriginAccessIdentitiesRequest' => [ 'type' => 'structure', 'members' => [ 'Marker' => [ 'shape' => 'string', 'location' => 'querystring', 'locationName' => 'Marker', ], 'MaxItems' => [ 'shape' => 'string', 'location' => 'querystring', 'locationName' => 'MaxItems', ], ], ], 'ListCloudFrontOriginAccessIdentitiesResult' => [ 'type' => 'structure', 'members' => [ 'CloudFrontOriginAccessIdentityList' => [ 'shape' => 'CloudFrontOriginAccessIdentityList', ], ], 'payload' => 'CloudFrontOriginAccessIdentityList', ], 'ListDistributionsByWebACLIdRequest' => [ 'type' => 'structure', 'required' => [ 'WebACLId', ], 'members' => [ 'Marker' => [ 'shape' => 'string', 'location' => 'querystring', 'locationName' => 'Marker', ], 'MaxItems' => [ 'shape' => 'string', 'location' => 'querystring', 'locationName' => 'MaxItems', ], 'WebACLId' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'WebACLId', ], ], ], 'ListDistributionsByWebACLIdResult' => [ 'type' => 'structure', 'members' => [ 'DistributionList' => [ 'shape' => 'DistributionList', ], ], 'payload' => 'DistributionList', ], 'ListDistributionsRequest' => [ 'type' => 'structure', 'members' => [ 'Marker' => [ 'shape' => 'string', 'location' => 'querystring', 'locationName' => 'Marker', ], 'MaxItems' => [ 'shape' => 'string', 'location' => 'querystring', 'locationName' => 'MaxItems', ], ], ], 'ListDistributionsResult' => [ 'type' => 'structure', 'members' => [ 'DistributionList' => [ 'shape' => 'DistributionList', ], ], 'payload' => 'DistributionList', ], 'ListInvalidationsRequest' => [ 'type' => 'structure', 'required' => [ 'DistributionId', ], 'members' => [ 'DistributionId' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'DistributionId', ], 'Marker' => [ 'shape' => 'string', 'location' => 'querystring', 'locationName' => 'Marker', ], 'MaxItems' => [ 'shape' => 'string', 'location' => 'querystring', 'locationName' => 'MaxItems', ], ], ], 'ListInvalidationsResult' => [ 'type' => 'structure', 'members' => [ 'InvalidationList' => [ 'shape' => 'InvalidationList', ], ], 'payload' => 'InvalidationList', ], 'ListStreamingDistributionsRequest' => [ 'type' => 'structure', 'members' => [ 'Marker' => [ 'shape' => 'string', 'location' => 'querystring', 'locationName' => 'Marker', ], 'MaxItems' => [ 'shape' => 'string', 'location' => 'querystring', 'locationName' => 'MaxItems', ], ], ], 'ListStreamingDistributionsResult' => [ 'type' => 'structure', 'members' => [ 'StreamingDistributionList' => [ 'shape' => 'StreamingDistributionList', ], ], 'payload' => 'StreamingDistributionList', ], 'LocationList' => [ 'type' => 'list', 'member' => [ 'shape' => 'string', 'locationName' => 'Location', ], ], 'LoggingConfig' => [ 'type' => 'structure', 'required' => [ 'Enabled', 'IncludeCookies', 'Bucket', 'Prefix', ], 'members' => [ 'Enabled' => [ 'shape' => 'boolean', ], 'IncludeCookies' => [ 'shape' => 'boolean', ], 'Bucket' => [ 'shape' => 'string', ], 'Prefix' => [ 'shape' => 'string', ], ], ], 'Method' => [ 'type' => 'string', 'enum' => [ 'GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'OPTIONS', 'DELETE', ], ], 'MethodsList' => [ 'type' => 'list', 'member' => [ 'shape' => 'Method', 'locationName' => 'Method', ], ], 'MinimumProtocolVersion' => [ 'type' => 'string', 'enum' => [ 'SSLv3', 'TLSv1', ], ], 'MissingBody' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'NoSuchCloudFrontOriginAccessIdentity' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 404, ], 'exception' => true, ], 'NoSuchDistribution' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 404, ], 'exception' => true, ], 'NoSuchInvalidation' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 404, ], 'exception' => true, ], 'NoSuchOrigin' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 404, ], 'exception' => true, ], 'NoSuchStreamingDistribution' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 404, ], 'exception' => true, ], 'Origin' => [ 'type' => 'structure', 'required' => [ 'Id', 'DomainName', ], 'members' => [ 'Id' => [ 'shape' => 'string', ], 'DomainName' => [ 'shape' => 'string', ], 'OriginPath' => [ 'shape' => 'string', ], 'CustomHeaders' => [ 'shape' => 'CustomHeaders', ], 'S3OriginConfig' => [ 'shape' => 'S3OriginConfig', ], 'CustomOriginConfig' => [ 'shape' => 'CustomOriginConfig', ], ], ], 'OriginCustomHeader' => [ 'type' => 'structure', 'required' => [ 'HeaderName', 'HeaderValue', ], 'members' => [ 'HeaderName' => [ 'shape' => 'string', ], 'HeaderValue' => [ 'shape' => 'string', ], ], ], 'OriginCustomHeadersList' => [ 'type' => 'list', 'member' => [ 'shape' => 'OriginCustomHeader', 'locationName' => 'OriginCustomHeader', ], ], 'OriginList' => [ 'type' => 'list', 'member' => [ 'shape' => 'Origin', 'locationName' => 'Origin', ], 'min' => 1, ], 'OriginProtocolPolicy' => [ 'type' => 'string', 'enum' => [ 'http-only', 'match-viewer', 'https-only', ], ], 'OriginSslProtocols' => [ 'type' => 'structure', 'required' => [ 'Quantity', 'Items', ], 'members' => [ 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'SslProtocolsList', ], ], ], 'Origins' => [ 'type' => 'structure', 'required' => [ 'Quantity', ], 'members' => [ 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'OriginList', ], ], ], 'PathList' => [ 'type' => 'list', 'member' => [ 'shape' => 'string', 'locationName' => 'Path', ], ], 'Paths' => [ 'type' => 'structure', 'required' => [ 'Quantity', ], 'members' => [ 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'PathList', ], ], ], 'PreconditionFailed' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 412, ], 'exception' => true, ], 'PriceClass' => [ 'type' => 'string', 'enum' => [ 'PriceClass_100', 'PriceClass_200', 'PriceClass_All', ], ], 'Restrictions' => [ 'type' => 'structure', 'required' => [ 'GeoRestriction', ], 'members' => [ 'GeoRestriction' => [ 'shape' => 'GeoRestriction', ], ], ], 'S3Origin' => [ 'type' => 'structure', 'required' => [ 'DomainName', 'OriginAccessIdentity', ], 'members' => [ 'DomainName' => [ 'shape' => 'string', ], 'OriginAccessIdentity' => [ 'shape' => 'string', ], ], ], 'S3OriginConfig' => [ 'type' => 'structure', 'required' => [ 'OriginAccessIdentity', ], 'members' => [ 'OriginAccessIdentity' => [ 'shape' => 'string', ], ], ], 'SSLSupportMethod' => [ 'type' => 'string', 'enum' => [ 'sni-only', 'vip', ], ], 'Signer' => [ 'type' => 'structure', 'members' => [ 'AwsAccountNumber' => [ 'shape' => 'string', ], 'KeyPairIds' => [ 'shape' => 'KeyPairIds', ], ], ], 'SignerList' => [ 'type' => 'list', 'member' => [ 'shape' => 'Signer', 'locationName' => 'Signer', ], ], 'SslProtocol' => [ 'type' => 'string', 'enum' => [ 'SSLv3', 'TLSv1', 'TLSv1.1', 'TLSv1.2', ], ], 'SslProtocolsList' => [ 'type' => 'list', 'member' => [ 'shape' => 'SslProtocol', 'locationName' => 'SslProtocol', ], ], 'StreamingDistribution' => [ 'type' => 'structure', 'required' => [ 'Id', 'Status', 'DomainName', 'ActiveTrustedSigners', 'StreamingDistributionConfig', ], 'members' => [ 'Id' => [ 'shape' => 'string', ], 'Status' => [ 'shape' => 'string', ], 'LastModifiedTime' => [ 'shape' => 'timestamp', ], 'DomainName' => [ 'shape' => 'string', ], 'ActiveTrustedSigners' => [ 'shape' => 'ActiveTrustedSigners', ], 'StreamingDistributionConfig' => [ 'shape' => 'StreamingDistributionConfig', ], ], ], 'StreamingDistributionAlreadyExists' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 409, ], 'exception' => true, ], 'StreamingDistributionConfig' => [ 'type' => 'structure', 'required' => [ 'CallerReference', 'S3Origin', 'Comment', 'TrustedSigners', 'Enabled', ], 'members' => [ 'CallerReference' => [ 'shape' => 'string', ], 'S3Origin' => [ 'shape' => 'S3Origin', ], 'Aliases' => [ 'shape' => 'Aliases', ], 'Comment' => [ 'shape' => 'string', ], 'Logging' => [ 'shape' => 'StreamingLoggingConfig', ], 'TrustedSigners' => [ 'shape' => 'TrustedSigners', ], 'PriceClass' => [ 'shape' => 'PriceClass', ], 'Enabled' => [ 'shape' => 'boolean', ], ], ], 'StreamingDistributionList' => [ 'type' => 'structure', 'required' => [ 'Marker', 'MaxItems', 'IsTruncated', 'Quantity', ], 'members' => [ 'Marker' => [ 'shape' => 'string', ], 'NextMarker' => [ 'shape' => 'string', ], 'MaxItems' => [ 'shape' => 'integer', ], 'IsTruncated' => [ 'shape' => 'boolean', ], 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'StreamingDistributionSummaryList', ], ], ], 'StreamingDistributionNotDisabled' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 409, ], 'exception' => true, ], 'StreamingDistributionSummary' => [ 'type' => 'structure', 'required' => [ 'Id', 'Status', 'LastModifiedTime', 'DomainName', 'S3Origin', 'Aliases', 'TrustedSigners', 'Comment', 'PriceClass', 'Enabled', ], 'members' => [ 'Id' => [ 'shape' => 'string', ], 'Status' => [ 'shape' => 'string', ], 'LastModifiedTime' => [ 'shape' => 'timestamp', ], 'DomainName' => [ 'shape' => 'string', ], 'S3Origin' => [ 'shape' => 'S3Origin', ], 'Aliases' => [ 'shape' => 'Aliases', ], 'TrustedSigners' => [ 'shape' => 'TrustedSigners', ], 'Comment' => [ 'shape' => 'string', ], 'PriceClass' => [ 'shape' => 'PriceClass', ], 'Enabled' => [ 'shape' => 'boolean', ], ], ], 'StreamingDistributionSummaryList' => [ 'type' => 'list', 'member' => [ 'shape' => 'StreamingDistributionSummary', 'locationName' => 'StreamingDistributionSummary', ], ], 'StreamingLoggingConfig' => [ 'type' => 'structure', 'required' => [ 'Enabled', 'Bucket', 'Prefix', ], 'members' => [ 'Enabled' => [ 'shape' => 'boolean', ], 'Bucket' => [ 'shape' => 'string', ], 'Prefix' => [ 'shape' => 'string', ], ], ], 'TooManyCacheBehaviors' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TooManyCertificates' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TooManyCloudFrontOriginAccessIdentities' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TooManyCookieNamesInWhiteList' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TooManyDistributionCNAMEs' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TooManyDistributions' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TooManyHeadersInForwardedValues' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TooManyInvalidationsInProgress' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TooManyOriginCustomHeaders' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TooManyOrigins' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TooManyStreamingDistributionCNAMEs' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TooManyStreamingDistributions' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TooManyTrustedSigners' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TrustedSignerDoesNotExist' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TrustedSigners' => [ 'type' => 'structure', 'required' => [ 'Enabled', 'Quantity', ], 'members' => [ 'Enabled' => [ 'shape' => 'boolean', ], 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'AwsAccountNumberList', ], ], ], 'UpdateCloudFrontOriginAccessIdentityRequest' => [ 'type' => 'structure', 'required' => [ 'CloudFrontOriginAccessIdentityConfig', 'Id', ], 'members' => [ 'CloudFrontOriginAccessIdentityConfig' => [ 'shape' => 'CloudFrontOriginAccessIdentityConfig', 'locationName' => 'CloudFrontOriginAccessIdentityConfig', 'xmlNamespace' => [ 'uri' => 'http://cloudfront.amazonaws.com/doc/2016-01-28/', ], ], 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], 'IfMatch' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'If-Match', ], ], 'payload' => 'CloudFrontOriginAccessIdentityConfig', ], 'UpdateCloudFrontOriginAccessIdentityResult' => [ 'type' => 'structure', 'members' => [ 'CloudFrontOriginAccessIdentity' => [ 'shape' => 'CloudFrontOriginAccessIdentity', ], 'ETag' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'ETag', ], ], 'payload' => 'CloudFrontOriginAccessIdentity', ], 'UpdateDistributionRequest' => [ 'type' => 'structure', 'required' => [ 'DistributionConfig', 'Id', ], 'members' => [ 'DistributionConfig' => [ 'shape' => 'DistributionConfig', 'locationName' => 'DistributionConfig', 'xmlNamespace' => [ 'uri' => 'http://cloudfront.amazonaws.com/doc/2016-01-28/', ], ], 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], 'IfMatch' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'If-Match', ], ], 'payload' => 'DistributionConfig', ], 'UpdateDistributionResult' => [ 'type' => 'structure', 'members' => [ 'Distribution' => [ 'shape' => 'Distribution', ], 'ETag' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'ETag', ], ], 'payload' => 'Distribution', ], 'UpdateStreamingDistributionRequest' => [ 'type' => 'structure', 'required' => [ 'StreamingDistributionConfig', 'Id', ], 'members' => [ 'StreamingDistributionConfig' => [ 'shape' => 'StreamingDistributionConfig', 'locationName' => 'StreamingDistributionConfig', 'xmlNamespace' => [ 'uri' => 'http://cloudfront.amazonaws.com/doc/2016-01-28/', ], ], 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], 'IfMatch' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'If-Match', ], ], 'payload' => 'StreamingDistributionConfig', ], 'UpdateStreamingDistributionResult' => [ 'type' => 'structure', 'members' => [ 'StreamingDistribution' => [ 'shape' => 'StreamingDistribution', ], 'ETag' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'ETag', ], ], 'payload' => 'StreamingDistribution', ], 'ViewerCertificate' => [ 'type' => 'structure', 'members' => [ 'CloudFrontDefaultCertificate' => [ 'shape' => 'boolean', ], 'IAMCertificateId' => [ 'shape' => 'string', ], 'ACMCertificateArn' => [ 'shape' => 'string', ], 'SSLSupportMethod' => [ 'shape' => 'SSLSupportMethod', ], 'MinimumProtocolVersion' => [ 'shape' => 'MinimumProtocolVersion', ], 'Certificate' => [ 'shape' => 'string', 'deprecated' => true, ], 'CertificateSource' => [ 'shape' => 'CertificateSource', 'deprecated' => true, ], ], ], 'ViewerProtocolPolicy' => [ 'type' => 'string', 'enum' => [ 'allow-all', 'https-only', 'redirect-to-https', ], ], 'boolean' => [ 'type' => 'boolean', ], 'integer' => [ 'type' => 'integer', ], 'long' => [ 'type' => 'long', ], 'string' => [ 'type' => 'string', ], 'timestamp' => [ 'type' => 'timestamp', ], ],];
|
lib/Aws/Aws/data/cloudfront/2016-01-28/paginators-1.json.php
ADDED
@@ -0,0 +1,3 @@
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
// This file was auto-generated from sdk-root/src/data/cloudfront/2016-01-28/paginators-1.json
|
3 |
+
return [ 'pagination' => [ 'ListCloudFrontOriginAccessIdentities' => [ 'input_token' => 'Marker', 'output_token' => 'CloudFrontOriginAccessIdentityList.NextMarker', 'limit_key' => 'MaxItems', 'more_results' => 'CloudFrontOriginAccessIdentityList.IsTruncated', 'result_key' => 'CloudFrontOriginAccessIdentityList.Items', ], 'ListDistributions' => [ 'input_token' => 'Marker', 'output_token' => 'DistributionList.NextMarker', 'limit_key' => 'MaxItems', 'more_results' => 'DistributionList.IsTruncated', 'result_key' => 'DistributionList.Items', ], 'ListInvalidations' => [ 'input_token' => 'Marker', 'output_token' => 'InvalidationList.NextMarker', 'limit_key' => 'MaxItems', 'more_results' => 'InvalidationList.IsTruncated', 'result_key' => 'InvalidationList.Items', ], 'ListStreamingDistributions' => [ 'input_token' => 'Marker', 'output_token' => 'StreamingDistributionList.NextMarker', 'limit_key' => 'MaxItems', 'more_results' => 'StreamingDistributionList.IsTruncated', 'result_key' => 'StreamingDistributionList.Items', ], ],];
|
lib/Aws/Aws/data/cloudfront/2016-01-28/waiters-2.json.php
ADDED
@@ -0,0 +1,3 @@
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
// This file was auto-generated from sdk-root/src/data/cloudfront/2016-01-28/waiters-2.json
|
3 |
+
return [ 'version' => 2, 'waiters' => [ 'DistributionDeployed' => [ 'delay' => 60, 'operation' => 'GetDistribution', 'maxAttempts' => 25, 'description' => 'Wait until a distribution is deployed.', 'acceptors' => [ [ 'expected' => 'Deployed', 'matcher' => 'path', 'state' => 'success', 'argument' => 'Distribution.Status', ], ], ], 'InvalidationCompleted' => [ 'delay' => 20, 'operation' => 'GetInvalidation', 'maxAttempts' => 30, 'description' => 'Wait until an invalidation has completed.', 'acceptors' => [ [ 'expected' => 'Completed', 'matcher' => 'path', 'state' => 'success', 'argument' => 'Invalidation.Status', ], ], ], 'StreamingDistributionDeployed' => [ 'delay' => 60, 'operation' => 'GetStreamingDistribution', 'maxAttempts' => 25, 'description' => 'Wait until a streaming distribution is deployed.', 'acceptors' => [ [ 'expected' => 'Deployed', 'matcher' => 'path', 'state' => 'success', 'argument' => 'StreamingDistribution.Status', ], ], ], ],];
|
lib/Aws/Aws/data/cloudfront/2016-08-01/api-2.json.php
ADDED
@@ -0,0 +1,3 @@
|
|
Â
|
|
Â
|
|
Â
|
1 |
+
<?php
|
2 |
+
// This file was auto-generated from sdk-root/src/data/cloudfront/2016-08-01/api-2.json
|
3 |
+
return [ 'version' => '2.0', 'metadata' => [ 'uid' => 'cloudfront-2016-08-01', 'apiVersion' => '2016-08-01', 'endpointPrefix' => 'cloudfront', 'globalEndpoint' => 'cloudfront.amazonaws.com', 'protocol' => 'rest-xml', 'serviceAbbreviation' => 'CloudFront', 'serviceFullName' => 'Amazon CloudFront', 'signatureVersion' => 'v4', ], 'operations' => [ 'CreateCloudFrontOriginAccessIdentity' => [ 'name' => 'CreateCloudFrontOriginAccessIdentity2016_08_01', 'http' => [ 'method' => 'POST', 'requestUri' => '/2016-08-01/origin-access-identity/cloudfront', 'responseCode' => 201, ], 'input' => [ 'shape' => 'CreateCloudFrontOriginAccessIdentityRequest', ], 'output' => [ 'shape' => 'CreateCloudFrontOriginAccessIdentityResult', ], 'errors' => [ [ 'shape' => 'CloudFrontOriginAccessIdentityAlreadyExists', ], [ 'shape' => 'MissingBody', ], [ 'shape' => 'TooManyCloudFrontOriginAccessIdentities', ], [ 'shape' => 'InvalidArgument', ], [ 'shape' => 'InconsistentQuantities', ], ], ], 'CreateDistribution' => [ 'name' => 'CreateDistribution2016_08_01', 'http' => [ 'method' => 'POST', 'requestUri' => '/2016-08-01/distribution', 'responseCode' => 201, ], 'input' => [ 'shape' => 'CreateDistributionRequest', ], 'output' => [ 'shape' => 'CreateDistributionResult', ], 'errors' => [ [ 'shape' => 'CNAMEAlreadyExists', ], [ 'shape' => 'DistributionAlreadyExists', ], [ 'shape' => 'InvalidOrigin', ], [ 'shape' => 'InvalidOriginAccessIdentity', ], [ 'shape' => 'AccessDenied', ], [ 'shape' => 'TooManyTrustedSigners', ], [ 'shape' => 'TrustedSignerDoesNotExist', ], [ 'shape' => 'InvalidViewerCertificate', ], [ 'shape' => 'InvalidMinimumProtocolVersion', ], [ 'shape' => 'MissingBody', ], [ 'shape' => 'TooManyDistributionCNAMEs', ], [ 'shape' => 'TooManyDistributions', ], [ 'shape' => 'InvalidDefaultRootObject', ], [ 'shape' => 'InvalidRelativePath', ], [ 'shape' => 'InvalidErrorCode', ], [ 'shape' => 'InvalidResponseCode', ], [ 'shape' => 'InvalidArgument', ], [ 'shape' => 'InvalidRequiredProtocol', ], [ 'shape' => 'NoSuchOrigin', ], [ 'shape' => 'TooManyOrigins', ], [ 'shape' => 'TooManyCacheBehaviors', ], [ 'shape' => 'TooManyCookieNamesInWhiteList', ], [ 'shape' => 'InvalidForwardCookies', ], [ 'shape' => 'TooManyHeadersInForwardedValues', ], [ 'shape' => 'InvalidHeadersForS3Origin', ], [ 'shape' => 'InconsistentQuantities', ], [ 'shape' => 'TooManyCertificates', ], [ 'shape' => 'InvalidLocationCode', ], [ 'shape' => 'InvalidGeoRestrictionParameter', ], [ 'shape' => 'InvalidProtocolSettings', ], [ 'shape' => 'InvalidTTLOrder', ], [ 'shape' => 'InvalidWebACLId', ], [ 'shape' => 'TooManyOriginCustomHeaders', ], ], ], 'CreateDistributionWithTags' => [ 'name' => 'CreateDistributionWithTags2016_08_01', 'http' => [ 'method' => 'POST', 'requestUri' => '/2016-08-01/distribution?WithTags', 'responseCode' => 201, ], 'input' => [ 'shape' => 'CreateDistributionWithTagsRequest', ], 'output' => [ 'shape' => 'CreateDistributionWithTagsResult', ], 'errors' => [ [ 'shape' => 'CNAMEAlreadyExists', ], [ 'shape' => 'DistributionAlreadyExists', ], [ 'shape' => 'InvalidOrigin', ], [ 'shape' => 'InvalidOriginAccessIdentity', ], [ 'shape' => 'AccessDenied', ], [ 'shape' => 'TooManyTrustedSigners', ], [ 'shape' => 'TrustedSignerDoesNotExist', ], [ 'shape' => 'InvalidViewerCertificate', ], [ 'shape' => 'InvalidMinimumProtocolVersion', ], [ 'shape' => 'MissingBody', ], [ 'shape' => 'TooManyDistributionCNAMEs', ], [ 'shape' => 'TooManyDistributions', ], [ 'shape' => 'InvalidDefaultRootObject', ], [ 'shape' => 'InvalidRelativePath', ], [ 'shape' => 'InvalidErrorCode', ], [ 'shape' => 'InvalidResponseCode', ], [ 'shape' => 'InvalidArgument', ], [ 'shape' => 'InvalidRequiredProtocol', ], [ 'shape' => 'NoSuchOrigin', ], [ 'shape' => 'TooManyOrigins', ], [ 'shape' => 'TooManyCacheBehaviors', ], [ 'shape' => 'TooManyCookieNamesInWhiteList', ], [ 'shape' => 'InvalidForwardCookies', ], [ 'shape' => 'TooManyHeadersInForwardedValues', ], [ 'shape' => 'InvalidHeadersForS3Origin', ], [ 'shape' => 'InconsistentQuantities', ], [ 'shape' => 'TooManyCertificates', ], [ 'shape' => 'InvalidLocationCode', ], [ 'shape' => 'InvalidGeoRestrictionParameter', ], [ 'shape' => 'InvalidProtocolSettings', ], [ 'shape' => 'InvalidTTLOrder', ], [ 'shape' => 'InvalidWebACLId', ], [ 'shape' => 'TooManyOriginCustomHeaders', ], [ 'shape' => 'InvalidTagging', ], ], ], 'CreateInvalidation' => [ 'name' => 'CreateInvalidation2016_08_01', 'http' => [ 'method' => 'POST', 'requestUri' => '/2016-08-01/distribution/{DistributionId}/invalidation', 'responseCode' => 201, ], 'input' => [ 'shape' => 'CreateInvalidationRequest', ], 'output' => [ 'shape' => 'CreateInvalidationResult', ], 'errors' => [ [ 'shape' => 'AccessDenied', ], [ 'shape' => 'MissingBody', ], [ 'shape' => 'InvalidArgument', ], [ 'shape' => 'NoSuchDistribution', ], [ 'shape' => 'BatchTooLarge', ], [ 'shape' => 'TooManyInvalidationsInProgress', ], [ 'shape' => 'InconsistentQuantities', ], ], ], 'CreateStreamingDistribution' => [ 'name' => 'CreateStreamingDistribution2016_08_01', 'http' => [ 'method' => 'POST', 'requestUri' => '/2016-08-01/streaming-distribution', 'responseCode' => 201, ], 'input' => [ 'shape' => 'CreateStreamingDistributionRequest', ], 'output' => [ 'shape' => 'CreateStreamingDistributionResult', ], 'errors' => [ [ 'shape' => 'CNAMEAlreadyExists', ], [ 'shape' => 'StreamingDistributionAlreadyExists', ], [ 'shape' => 'InvalidOrigin', ], [ 'shape' => 'InvalidOriginAccessIdentity', ], [ 'shape' => 'AccessDenied', ], [ 'shape' => 'TooManyTrustedSigners', ], [ 'shape' => 'TrustedSignerDoesNotExist', ], [ 'shape' => 'MissingBody', ], [ 'shape' => 'TooManyStreamingDistributionCNAMEs', ], [ 'shape' => 'TooManyStreamingDistributions', ], [ 'shape' => 'InvalidArgument', ], [ 'shape' => 'InconsistentQuantities', ], ], ], 'CreateStreamingDistributionWithTags' => [ 'name' => 'CreateStreamingDistributionWithTags2016_08_01', 'http' => [ 'method' => 'POST', 'requestUri' => '/2016-08-01/streaming-distribution?WithTags', 'responseCode' => 201, ], 'input' => [ 'shape' => 'CreateStreamingDistributionWithTagsRequest', ], 'output' => [ 'shape' => 'CreateStreamingDistributionWithTagsResult', ], 'errors' => [ [ 'shape' => 'CNAMEAlreadyExists', ], [ 'shape' => 'StreamingDistributionAlreadyExists', ], [ 'shape' => 'InvalidOrigin', ], [ 'shape' => 'InvalidOriginAccessIdentity', ], [ 'shape' => 'AccessDenied', ], [ 'shape' => 'TooManyTrustedSigners', ], [ 'shape' => 'TrustedSignerDoesNotExist', ], [ 'shape' => 'MissingBody', ], [ 'shape' => 'TooManyStreamingDistributionCNAMEs', ], [ 'shape' => 'TooManyStreamingDistributions', ], [ 'shape' => 'InvalidArgument', ], [ 'shape' => 'InconsistentQuantities', ], [ 'shape' => 'InvalidTagging', ], ], ], 'DeleteCloudFrontOriginAccessIdentity' => [ 'name' => 'DeleteCloudFrontOriginAccessIdentity2016_08_01', 'http' => [ 'method' => 'DELETE', 'requestUri' => '/2016-08-01/origin-access-identity/cloudfront/{Id}', 'responseCode' => 204, ], 'input' => [ 'shape' => 'DeleteCloudFrontOriginAccessIdentityRequest', ], 'errors' => [ [ 'shape' => 'AccessDenied', ], [ 'shape' => 'InvalidIfMatchVersion', ], [ 'shape' => 'NoSuchCloudFrontOriginAccessIdentity', ], [ 'shape' => 'PreconditionFailed', ], [ 'shape' => 'CloudFrontOriginAccessIdentityInUse', ], ], ], 'DeleteDistribution' => [ 'name' => 'DeleteDistribution2016_08_01', 'http' => [ 'method' => 'DELETE', 'requestUri' => '/2016-08-01/distribution/{Id}', 'responseCode' => 204, ], 'input' => [ 'shape' => 'DeleteDistributionRequest', ], 'errors' => [ [ 'shape' => 'AccessDenied', ], [ 'shape' => 'DistributionNotDisabled', ], [ 'shape' => 'InvalidIfMatchVersion', ], [ 'shape' => 'NoSuchDistribution', ], [ 'shape' => 'PreconditionFailed', ], ], ], 'DeleteStreamingDistribution' => [ 'name' => 'DeleteStreamingDistribution2016_08_01', 'http' => [ 'method' => 'DELETE', 'requestUri' => '/2016-08-01/streaming-distribution/{Id}', 'responseCode' => 204, ], 'input' => [ 'shape' => 'DeleteStreamingDistributionRequest', ], 'errors' => [ [ 'shape' => 'AccessDenied', ], [ 'shape' => 'StreamingDistributionNotDisabled', ], [ 'shape' => 'InvalidIfMatchVersion', ], [ 'shape' => 'NoSuchStreamingDistribution', ], [ 'shape' => 'PreconditionFailed', ], ], ], 'GetCloudFrontOriginAccessIdentity' => [ 'name' => 'GetCloudFrontOriginAccessIdentity2016_08_01', 'http' => [ 'method' => 'GET', 'requestUri' => '/2016-08-01/origin-access-identity/cloudfront/{Id}', ], 'input' => [ 'shape' => 'GetCloudFrontOriginAccessIdentityRequest', ], 'output' => [ 'shape' => 'GetCloudFrontOriginAccessIdentityResult', ], 'errors' => [ [ 'shape' => 'NoSuchCloudFrontOriginAccessIdentity', ], [ 'shape' => 'AccessDenied', ], ], ], 'GetCloudFrontOriginAccessIdentityConfig' => [ 'name' => 'GetCloudFrontOriginAccessIdentityConfig2016_08_01', 'http' => [ 'method' => 'GET', 'requestUri' => '/2016-08-01/origin-access-identity/cloudfront/{Id}/config', ], 'input' => [ 'shape' => 'GetCloudFrontOriginAccessIdentityConfigRequest', ], 'output' => [ 'shape' => 'GetCloudFrontOriginAccessIdentityConfigResult', ], 'errors' => [ [ 'shape' => 'NoSuchCloudFrontOriginAccessIdentity', ], [ 'shape' => 'AccessDenied', ], ], ], 'GetDistribution' => [ 'name' => 'GetDistribution2016_08_01', 'http' => [ 'method' => 'GET', 'requestUri' => '/2016-08-01/distribution/{Id}', ], 'input' => [ 'shape' => 'GetDistributionRequest', ], 'output' => [ 'shape' => 'GetDistributionResult', ], 'errors' => [ [ 'shape' => 'NoSuchDistribution', ], [ 'shape' => 'AccessDenied', ], ], ], 'GetDistributionConfig' => [ 'name' => 'GetDistributionConfig2016_08_01', 'http' => [ 'method' => 'GET', 'requestUri' => '/2016-08-01/distribution/{Id}/config', ], 'input' => [ 'shape' => 'GetDistributionConfigRequest', ], 'output' => [ 'shape' => 'GetDistributionConfigResult', ], 'errors' => [ [ 'shape' => 'NoSuchDistribution', ], [ 'shape' => 'AccessDenied', ], ], ], 'GetInvalidation' => [ 'name' => 'GetInvalidation2016_08_01', 'http' => [ 'method' => 'GET', 'requestUri' => '/2016-08-01/distribution/{DistributionId}/invalidation/{Id}', ], 'input' => [ 'shape' => 'GetInvalidationRequest', ], 'output' => [ 'shape' => 'GetInvalidationResult', ], 'errors' => [ [ 'shape' => 'NoSuchInvalidation', ], [ 'shape' => 'NoSuchDistribution', ], [ 'shape' => 'AccessDenied', ], ], ], 'GetStreamingDistribution' => [ 'name' => 'GetStreamingDistribution2016_08_01', 'http' => [ 'method' => 'GET', 'requestUri' => '/2016-08-01/streaming-distribution/{Id}', ], 'input' => [ 'shape' => 'GetStreamingDistributionRequest', ], 'output' => [ 'shape' => 'GetStreamingDistributionResult', ], 'errors' => [ [ 'shape' => 'NoSuchStreamingDistribution', ], [ 'shape' => 'AccessDenied', ], ], ], 'GetStreamingDistributionConfig' => [ 'name' => 'GetStreamingDistributionConfig2016_08_01', 'http' => [ 'method' => 'GET', 'requestUri' => '/2016-08-01/streaming-distribution/{Id}/config', ], 'input' => [ 'shape' => 'GetStreamingDistributionConfigRequest', ], 'output' => [ 'shape' => 'GetStreamingDistributionConfigResult', ], 'errors' => [ [ 'shape' => 'NoSuchStreamingDistribution', ], [ 'shape' => 'AccessDenied', ], ], ], 'ListCloudFrontOriginAccessIdentities' => [ 'name' => 'ListCloudFrontOriginAccessIdentities2016_08_01', 'http' => [ 'method' => 'GET', 'requestUri' => '/2016-08-01/origin-access-identity/cloudfront', ], 'input' => [ 'shape' => 'ListCloudFrontOriginAccessIdentitiesRequest', ], 'output' => [ 'shape' => 'ListCloudFrontOriginAccessIdentitiesResult', ], 'errors' => [ [ 'shape' => 'InvalidArgument', ], ], ], 'ListDistributions' => [ 'name' => 'ListDistributions2016_08_01', 'http' => [ 'method' => 'GET', 'requestUri' => '/2016-08-01/distribution', ], 'input' => [ 'shape' => 'ListDistributionsRequest', ], 'output' => [ 'shape' => 'ListDistributionsResult', ], 'errors' => [ [ 'shape' => 'InvalidArgument', ], ], ], 'ListDistributionsByWebACLId' => [ 'name' => 'ListDistributionsByWebACLId2016_08_01', 'http' => [ 'method' => 'GET', 'requestUri' => '/2016-08-01/distributionsByWebACLId/{WebACLId}', ], 'input' => [ 'shape' => 'ListDistributionsByWebACLIdRequest', ], 'output' => [ 'shape' => 'ListDistributionsByWebACLIdResult', ], 'errors' => [ [ 'shape' => 'InvalidArgument', ], [ 'shape' => 'InvalidWebACLId', ], ], ], 'ListInvalidations' => [ 'name' => 'ListInvalidations2016_08_01', 'http' => [ 'method' => 'GET', 'requestUri' => '/2016-08-01/distribution/{DistributionId}/invalidation', ], 'input' => [ 'shape' => 'ListInvalidationsRequest', ], 'output' => [ 'shape' => 'ListInvalidationsResult', ], 'errors' => [ [ 'shape' => 'InvalidArgument', ], [ 'shape' => 'NoSuchDistribution', ], [ 'shape' => 'AccessDenied', ], ], ], 'ListStreamingDistributions' => [ 'name' => 'ListStreamingDistributions2016_08_01', 'http' => [ 'method' => 'GET', 'requestUri' => '/2016-08-01/streaming-distribution', ], 'input' => [ 'shape' => 'ListStreamingDistributionsRequest', ], 'output' => [ 'shape' => 'ListStreamingDistributionsResult', ], 'errors' => [ [ 'shape' => 'InvalidArgument', ], ], ], 'ListTagsForResource' => [ 'name' => 'ListTagsForResource2016_08_01', 'http' => [ 'method' => 'GET', 'requestUri' => '/2016-08-01/tagging', ], 'input' => [ 'shape' => 'ListTagsForResourceRequest', ], 'output' => [ 'shape' => 'ListTagsForResourceResult', ], 'errors' => [ [ 'shape' => 'AccessDenied', ], [ 'shape' => 'InvalidArgument', ], [ 'shape' => 'InvalidTagging', ], [ 'shape' => 'NoSuchResource', ], ], ], 'TagResource' => [ 'name' => 'TagResource2016_08_01', 'http' => [ 'method' => 'POST', 'requestUri' => '/2016-08-01/tagging?Operation=Tag', 'responseCode' => 204, ], 'input' => [ 'shape' => 'TagResourceRequest', ], 'errors' => [ [ 'shape' => 'AccessDenied', ], [ 'shape' => 'InvalidArgument', ], [ 'shape' => 'InvalidTagging', ], [ 'shape' => 'NoSuchResource', ], ], ], 'UntagResource' => [ 'name' => 'UntagResource2016_08_01', 'http' => [ 'method' => 'POST', 'requestUri' => '/2016-08-01/tagging?Operation=Untag', 'responseCode' => 204, ], 'input' => [ 'shape' => 'UntagResourceRequest', ], 'errors' => [ [ 'shape' => 'AccessDenied', ], [ 'shape' => 'InvalidArgument', ], [ 'shape' => 'InvalidTagging', ], [ 'shape' => 'NoSuchResource', ], ], ], 'UpdateCloudFrontOriginAccessIdentity' => [ 'name' => 'UpdateCloudFrontOriginAccessIdentity2016_08_01', 'http' => [ 'method' => 'PUT', 'requestUri' => '/2016-08-01/origin-access-identity/cloudfront/{Id}/config', ], 'input' => [ 'shape' => 'UpdateCloudFrontOriginAccessIdentityRequest', ], 'output' => [ 'shape' => 'UpdateCloudFrontOriginAccessIdentityResult', ], 'errors' => [ [ 'shape' => 'AccessDenied', ], [ 'shape' => 'IllegalUpdate', ], [ 'shape' => 'InvalidIfMatchVersion', ], [ 'shape' => 'MissingBody', ], [ 'shape' => 'NoSuchCloudFrontOriginAccessIdentity', ], [ 'shape' => 'PreconditionFailed', ], [ 'shape' => 'InvalidArgument', ], [ 'shape' => 'InconsistentQuantities', ], ], ], 'UpdateDistribution' => [ 'name' => 'UpdateDistribution2016_08_01', 'http' => [ 'method' => 'PUT', 'requestUri' => '/2016-08-01/distribution/{Id}/config', ], 'input' => [ 'shape' => 'UpdateDistributionRequest', ], 'output' => [ 'shape' => 'UpdateDistributionResult', ], 'errors' => [ [ 'shape' => 'AccessDenied', ], [ 'shape' => 'CNAMEAlreadyExists', ], [ 'shape' => 'IllegalUpdate', ], [ 'shape' => 'InvalidIfMatchVersion', ], [ 'shape' => 'MissingBody', ], [ 'shape' => 'NoSuchDistribution', ], [ 'shape' => 'PreconditionFailed', ], [ 'shape' => 'TooManyDistributionCNAMEs', ], [ 'shape' => 'InvalidDefaultRootObject', ], [ 'shape' => 'InvalidRelativePath', ], [ 'shape' => 'InvalidErrorCode', ], [ 'shape' => 'InvalidResponseCode', ], [ 'shape' => 'InvalidArgument', ], [ 'shape' => 'InvalidOriginAccessIdentity', ], [ 'shape' => 'TooManyTrustedSigners', ], [ 'shape' => 'TrustedSignerDoesNotExist', ], [ 'shape' => 'InvalidViewerCertificate', ], [ 'shape' => 'InvalidMinimumProtocolVersion', ], [ 'shape' => 'InvalidRequiredProtocol', ], [ 'shape' => 'NoSuchOrigin', ], [ 'shape' => 'TooManyOrigins', ], [ 'shape' => 'TooManyCacheBehaviors', ], [ 'shape' => 'TooManyCookieNamesInWhiteList', ], [ 'shape' => 'InvalidForwardCookies', ], [ 'shape' => 'TooManyHeadersInForwardedValues', ], [ 'shape' => 'InvalidHeadersForS3Origin', ], [ 'shape' => 'InconsistentQuantities', ], [ 'shape' => 'TooManyCertificates', ], [ 'shape' => 'InvalidLocationCode', ], [ 'shape' => 'InvalidGeoRestrictionParameter', ], [ 'shape' => 'InvalidTTLOrder', ], [ 'shape' => 'InvalidWebACLId', ], [ 'shape' => 'TooManyOriginCustomHeaders', ], ], ], 'UpdateStreamingDistribution' => [ 'name' => 'UpdateStreamingDistribution2016_08_01', 'http' => [ 'method' => 'PUT', 'requestUri' => '/2016-08-01/streaming-distribution/{Id}/config', ], 'input' => [ 'shape' => 'UpdateStreamingDistributionRequest', ], 'output' => [ 'shape' => 'UpdateStreamingDistributionResult', ], 'errors' => [ [ 'shape' => 'AccessDenied', ], [ 'shape' => 'CNAMEAlreadyExists', ], [ 'shape' => 'IllegalUpdate', ], [ 'shape' => 'InvalidIfMatchVersion', ], [ 'shape' => 'MissingBody', ], [ 'shape' => 'NoSuchStreamingDistribution', ], [ 'shape' => 'PreconditionFailed', ], [ 'shape' => 'TooManyStreamingDistributionCNAMEs', ], [ 'shape' => 'InvalidArgument', ], [ 'shape' => 'InvalidOriginAccessIdentity', ], [ 'shape' => 'TooManyTrustedSigners', ], [ 'shape' => 'TrustedSignerDoesNotExist', ], [ 'shape' => 'InconsistentQuantities', ], ], ], ], 'shapes' => [ 'AccessDenied' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 403, ], 'exception' => true, ], 'ActiveTrustedSigners' => [ 'type' => 'structure', 'required' => [ 'Enabled', 'Quantity', ], 'members' => [ 'Enabled' => [ 'shape' => 'boolean', ], 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'SignerList', ], ], ], 'AliasList' => [ 'type' => 'list', 'member' => [ 'shape' => 'string', 'locationName' => 'CNAME', ], ], 'Aliases' => [ 'type' => 'structure', 'required' => [ 'Quantity', ], 'members' => [ 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'AliasList', ], ], ], 'AllowedMethods' => [ 'type' => 'structure', 'required' => [ 'Quantity', 'Items', ], 'members' => [ 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'MethodsList', ], 'CachedMethods' => [ 'shape' => 'CachedMethods', ], ], ], 'AwsAccountNumberList' => [ 'type' => 'list', 'member' => [ 'shape' => 'string', 'locationName' => 'AwsAccountNumber', ], ], 'BatchTooLarge' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 413, ], 'exception' => true, ], 'CNAMEAlreadyExists' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 409, ], 'exception' => true, ], 'CacheBehavior' => [ 'type' => 'structure', 'required' => [ 'PathPattern', 'TargetOriginId', 'ForwardedValues', 'TrustedSigners', 'ViewerProtocolPolicy', 'MinTTL', ], 'members' => [ 'PathPattern' => [ 'shape' => 'string', ], 'TargetOriginId' => [ 'shape' => 'string', ], 'ForwardedValues' => [ 'shape' => 'ForwardedValues', ], 'TrustedSigners' => [ 'shape' => 'TrustedSigners', ], 'ViewerProtocolPolicy' => [ 'shape' => 'ViewerProtocolPolicy', ], 'MinTTL' => [ 'shape' => 'long', ], 'AllowedMethods' => [ 'shape' => 'AllowedMethods', ], 'SmoothStreaming' => [ 'shape' => 'boolean', ], 'DefaultTTL' => [ 'shape' => 'long', ], 'MaxTTL' => [ 'shape' => 'long', ], 'Compress' => [ 'shape' => 'boolean', ], ], ], 'CacheBehaviorList' => [ 'type' => 'list', 'member' => [ 'shape' => 'CacheBehavior', 'locationName' => 'CacheBehavior', ], ], 'CacheBehaviors' => [ 'type' => 'structure', 'required' => [ 'Quantity', ], 'members' => [ 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'CacheBehaviorList', ], ], ], 'CachedMethods' => [ 'type' => 'structure', 'required' => [ 'Quantity', 'Items', ], 'members' => [ 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'MethodsList', ], ], ], 'CertificateSource' => [ 'type' => 'string', 'enum' => [ 'cloudfront', 'iam', 'acm', ], ], 'CloudFrontOriginAccessIdentity' => [ 'type' => 'structure', 'required' => [ 'Id', 'S3CanonicalUserId', ], 'members' => [ 'Id' => [ 'shape' => 'string', ], 'S3CanonicalUserId' => [ 'shape' => 'string', ], 'CloudFrontOriginAccessIdentityConfig' => [ 'shape' => 'CloudFrontOriginAccessIdentityConfig', ], ], ], 'CloudFrontOriginAccessIdentityAlreadyExists' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 409, ], 'exception' => true, ], 'CloudFrontOriginAccessIdentityConfig' => [ 'type' => 'structure', 'required' => [ 'CallerReference', 'Comment', ], 'members' => [ 'CallerReference' => [ 'shape' => 'string', ], 'Comment' => [ 'shape' => 'string', ], ], ], 'CloudFrontOriginAccessIdentityInUse' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 409, ], 'exception' => true, ], 'CloudFrontOriginAccessIdentityList' => [ 'type' => 'structure', 'required' => [ 'Marker', 'MaxItems', 'IsTruncated', 'Quantity', ], 'members' => [ 'Marker' => [ 'shape' => 'string', ], 'NextMarker' => [ 'shape' => 'string', ], 'MaxItems' => [ 'shape' => 'integer', ], 'IsTruncated' => [ 'shape' => 'boolean', ], 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'CloudFrontOriginAccessIdentitySummaryList', ], ], ], 'CloudFrontOriginAccessIdentitySummary' => [ 'type' => 'structure', 'required' => [ 'Id', 'S3CanonicalUserId', 'Comment', ], 'members' => [ 'Id' => [ 'shape' => 'string', ], 'S3CanonicalUserId' => [ 'shape' => 'string', ], 'Comment' => [ 'shape' => 'string', ], ], ], 'CloudFrontOriginAccessIdentitySummaryList' => [ 'type' => 'list', 'member' => [ 'shape' => 'CloudFrontOriginAccessIdentitySummary', 'locationName' => 'CloudFrontOriginAccessIdentitySummary', ], ], 'CookieNameList' => [ 'type' => 'list', 'member' => [ 'shape' => 'string', 'locationName' => 'Name', ], ], 'CookieNames' => [ 'type' => 'structure', 'required' => [ 'Quantity', ], 'members' => [ 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'CookieNameList', ], ], ], 'CookiePreference' => [ 'type' => 'structure', 'required' => [ 'Forward', ], 'members' => [ 'Forward' => [ 'shape' => 'ItemSelection', ], 'WhitelistedNames' => [ 'shape' => 'CookieNames', ], ], ], 'CreateCloudFrontOriginAccessIdentityRequest' => [ 'type' => 'structure', 'required' => [ 'CloudFrontOriginAccessIdentityConfig', ], 'members' => [ 'CloudFrontOriginAccessIdentityConfig' => [ 'shape' => 'CloudFrontOriginAccessIdentityConfig', 'locationName' => 'CloudFrontOriginAccessIdentityConfig', 'xmlNamespace' => [ 'uri' => 'http://cloudfront.amazonaws.com/doc/2016-08-01/', ], ], ], 'payload' => 'CloudFrontOriginAccessIdentityConfig', ], 'CreateCloudFrontOriginAccessIdentityResult' => [ 'type' => 'structure', 'members' => [ 'CloudFrontOriginAccessIdentity' => [ 'shape' => 'CloudFrontOriginAccessIdentity', ], 'Location' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'Location', ], 'ETag' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'ETag', ], ], 'payload' => 'CloudFrontOriginAccessIdentity', ], 'CreateDistributionRequest' => [ 'type' => 'structure', 'required' => [ 'DistributionConfig', ], 'members' => [ 'DistributionConfig' => [ 'shape' => 'DistributionConfig', 'locationName' => 'DistributionConfig', 'xmlNamespace' => [ 'uri' => 'http://cloudfront.amazonaws.com/doc/2016-08-01/', ], ], ], 'payload' => 'DistributionConfig', ], 'CreateDistributionResult' => [ 'type' => 'structure', 'members' => [ 'Distribution' => [ 'shape' => 'Distribution', ], 'Location' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'Location', ], 'ETag' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'ETag', ], ], 'payload' => 'Distribution', ], 'CreateDistributionWithTagsRequest' => [ 'type' => 'structure', 'required' => [ 'DistributionConfigWithTags', ], 'members' => [ 'DistributionConfigWithTags' => [ 'shape' => 'DistributionConfigWithTags', 'locationName' => 'DistributionConfigWithTags', 'xmlNamespace' => [ 'uri' => 'http://cloudfront.amazonaws.com/doc/2016-08-01/', ], ], ], 'payload' => 'DistributionConfigWithTags', ], 'CreateDistributionWithTagsResult' => [ 'type' => 'structure', 'members' => [ 'Distribution' => [ 'shape' => 'Distribution', ], 'Location' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'Location', ], 'ETag' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'ETag', ], ], 'payload' => 'Distribution', ], 'CreateInvalidationRequest' => [ 'type' => 'structure', 'required' => [ 'DistributionId', 'InvalidationBatch', ], 'members' => [ 'DistributionId' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'DistributionId', ], 'InvalidationBatch' => [ 'shape' => 'InvalidationBatch', 'locationName' => 'InvalidationBatch', 'xmlNamespace' => [ 'uri' => 'http://cloudfront.amazonaws.com/doc/2016-08-01/', ], ], ], 'payload' => 'InvalidationBatch', ], 'CreateInvalidationResult' => [ 'type' => 'structure', 'members' => [ 'Location' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'Location', ], 'Invalidation' => [ 'shape' => 'Invalidation', ], ], 'payload' => 'Invalidation', ], 'CreateStreamingDistributionRequest' => [ 'type' => 'structure', 'required' => [ 'StreamingDistributionConfig', ], 'members' => [ 'StreamingDistributionConfig' => [ 'shape' => 'StreamingDistributionConfig', 'locationName' => 'StreamingDistributionConfig', 'xmlNamespace' => [ 'uri' => 'http://cloudfront.amazonaws.com/doc/2016-08-01/', ], ], ], 'payload' => 'StreamingDistributionConfig', ], 'CreateStreamingDistributionResult' => [ 'type' => 'structure', 'members' => [ 'StreamingDistribution' => [ 'shape' => 'StreamingDistribution', ], 'Location' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'Location', ], 'ETag' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'ETag', ], ], 'payload' => 'StreamingDistribution', ], 'CreateStreamingDistributionWithTagsRequest' => [ 'type' => 'structure', 'required' => [ 'StreamingDistributionConfigWithTags', ], 'members' => [ 'StreamingDistributionConfigWithTags' => [ 'shape' => 'StreamingDistributionConfigWithTags', 'locationName' => 'StreamingDistributionConfigWithTags', 'xmlNamespace' => [ 'uri' => 'http://cloudfront.amazonaws.com/doc/2016-08-01/', ], ], ], 'payload' => 'StreamingDistributionConfigWithTags', ], 'CreateStreamingDistributionWithTagsResult' => [ 'type' => 'structure', 'members' => [ 'StreamingDistribution' => [ 'shape' => 'StreamingDistribution', ], 'Location' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'Location', ], 'ETag' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'ETag', ], ], 'payload' => 'StreamingDistribution', ], 'CustomErrorResponse' => [ 'type' => 'structure', 'required' => [ 'ErrorCode', ], 'members' => [ 'ErrorCode' => [ 'shape' => 'integer', ], 'ResponsePagePath' => [ 'shape' => 'string', ], 'ResponseCode' => [ 'shape' => 'string', ], 'ErrorCachingMinTTL' => [ 'shape' => 'long', ], ], ], 'CustomErrorResponseList' => [ 'type' => 'list', 'member' => [ 'shape' => 'CustomErrorResponse', 'locationName' => 'CustomErrorResponse', ], ], 'CustomErrorResponses' => [ 'type' => 'structure', 'required' => [ 'Quantity', ], 'members' => [ 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'CustomErrorResponseList', ], ], ], 'CustomHeaders' => [ 'type' => 'structure', 'required' => [ 'Quantity', ], 'members' => [ 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'OriginCustomHeadersList', ], ], ], 'CustomOriginConfig' => [ 'type' => 'structure', 'required' => [ 'HTTPPort', 'HTTPSPort', 'OriginProtocolPolicy', ], 'members' => [ 'HTTPPort' => [ 'shape' => 'integer', ], 'HTTPSPort' => [ 'shape' => 'integer', ], 'OriginProtocolPolicy' => [ 'shape' => 'OriginProtocolPolicy', ], 'OriginSslProtocols' => [ 'shape' => 'OriginSslProtocols', ], ], ], 'DefaultCacheBehavior' => [ 'type' => 'structure', 'required' => [ 'TargetOriginId', 'ForwardedValues', 'TrustedSigners', 'ViewerProtocolPolicy', 'MinTTL', ], 'members' => [ 'TargetOriginId' => [ 'shape' => 'string', ], 'ForwardedValues' => [ 'shape' => 'ForwardedValues', ], 'TrustedSigners' => [ 'shape' => 'TrustedSigners', ], 'ViewerProtocolPolicy' => [ 'shape' => 'ViewerProtocolPolicy', ], 'MinTTL' => [ 'shape' => 'long', ], 'AllowedMethods' => [ 'shape' => 'AllowedMethods', ], 'SmoothStreaming' => [ 'shape' => 'boolean', ], 'DefaultTTL' => [ 'shape' => 'long', ], 'MaxTTL' => [ 'shape' => 'long', ], 'Compress' => [ 'shape' => 'boolean', ], ], ], 'DeleteCloudFrontOriginAccessIdentityRequest' => [ 'type' => 'structure', 'required' => [ 'Id', ], 'members' => [ 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], 'IfMatch' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'If-Match', ], ], ], 'DeleteDistributionRequest' => [ 'type' => 'structure', 'required' => [ 'Id', ], 'members' => [ 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], 'IfMatch' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'If-Match', ], ], ], 'DeleteStreamingDistributionRequest' => [ 'type' => 'structure', 'required' => [ 'Id', ], 'members' => [ 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], 'IfMatch' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'If-Match', ], ], ], 'Distribution' => [ 'type' => 'structure', 'required' => [ 'Id', 'ARN', 'Status', 'LastModifiedTime', 'InProgressInvalidationBatches', 'DomainName', 'ActiveTrustedSigners', 'DistributionConfig', ], 'members' => [ 'Id' => [ 'shape' => 'string', ], 'ARN' => [ 'shape' => 'string', ], 'Status' => [ 'shape' => 'string', ], 'LastModifiedTime' => [ 'shape' => 'timestamp', ], 'InProgressInvalidationBatches' => [ 'shape' => 'integer', ], 'DomainName' => [ 'shape' => 'string', ], 'ActiveTrustedSigners' => [ 'shape' => 'ActiveTrustedSigners', ], 'DistributionConfig' => [ 'shape' => 'DistributionConfig', ], ], ], 'DistributionAlreadyExists' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 409, ], 'exception' => true, ], 'DistributionConfig' => [ 'type' => 'structure', 'required' => [ 'CallerReference', 'Origins', 'DefaultCacheBehavior', 'Comment', 'Enabled', ], 'members' => [ 'CallerReference' => [ 'shape' => 'string', ], 'Aliases' => [ 'shape' => 'Aliases', ], 'DefaultRootObject' => [ 'shape' => 'string', ], 'Origins' => [ 'shape' => 'Origins', ], 'DefaultCacheBehavior' => [ 'shape' => 'DefaultCacheBehavior', ], 'CacheBehaviors' => [ 'shape' => 'CacheBehaviors', ], 'CustomErrorResponses' => [ 'shape' => 'CustomErrorResponses', ], 'Comment' => [ 'shape' => 'string', ], 'Logging' => [ 'shape' => 'LoggingConfig', ], 'PriceClass' => [ 'shape' => 'PriceClass', ], 'Enabled' => [ 'shape' => 'boolean', ], 'ViewerCertificate' => [ 'shape' => 'ViewerCertificate', ], 'Restrictions' => [ 'shape' => 'Restrictions', ], 'WebACLId' => [ 'shape' => 'string', ], ], ], 'DistributionConfigWithTags' => [ 'type' => 'structure', 'required' => [ 'DistributionConfig', 'Tags', ], 'members' => [ 'DistributionConfig' => [ 'shape' => 'DistributionConfig', ], 'Tags' => [ 'shape' => 'Tags', ], ], ], 'DistributionList' => [ 'type' => 'structure', 'required' => [ 'Marker', 'MaxItems', 'IsTruncated', 'Quantity', ], 'members' => [ 'Marker' => [ 'shape' => 'string', ], 'NextMarker' => [ 'shape' => 'string', ], 'MaxItems' => [ 'shape' => 'integer', ], 'IsTruncated' => [ 'shape' => 'boolean', ], 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'DistributionSummaryList', ], ], ], 'DistributionNotDisabled' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 409, ], 'exception' => true, ], 'DistributionSummary' => [ 'type' => 'structure', 'required' => [ 'Id', 'ARN', 'Status', 'LastModifiedTime', 'DomainName', 'Aliases', 'Origins', 'DefaultCacheBehavior', 'CacheBehaviors', 'CustomErrorResponses', 'Comment', 'PriceClass', 'Enabled', 'ViewerCertificate', 'Restrictions', 'WebACLId', ], 'members' => [ 'Id' => [ 'shape' => 'string', ], 'ARN' => [ 'shape' => 'string', ], 'Status' => [ 'shape' => 'string', ], 'LastModifiedTime' => [ 'shape' => 'timestamp', ], 'DomainName' => [ 'shape' => 'string', ], 'Aliases' => [ 'shape' => 'Aliases', ], 'Origins' => [ 'shape' => 'Origins', ], 'DefaultCacheBehavior' => [ 'shape' => 'DefaultCacheBehavior', ], 'CacheBehaviors' => [ 'shape' => 'CacheBehaviors', ], 'CustomErrorResponses' => [ 'shape' => 'CustomErrorResponses', ], 'Comment' => [ 'shape' => 'string', ], 'PriceClass' => [ 'shape' => 'PriceClass', ], 'Enabled' => [ 'shape' => 'boolean', ], 'ViewerCertificate' => [ 'shape' => 'ViewerCertificate', ], 'Restrictions' => [ 'shape' => 'Restrictions', ], 'WebACLId' => [ 'shape' => 'string', ], ], ], 'DistributionSummaryList' => [ 'type' => 'list', 'member' => [ 'shape' => 'DistributionSummary', 'locationName' => 'DistributionSummary', ], ], 'ForwardedValues' => [ 'type' => 'structure', 'required' => [ 'QueryString', 'Cookies', ], 'members' => [ 'QueryString' => [ 'shape' => 'boolean', ], 'Cookies' => [ 'shape' => 'CookiePreference', ], 'Headers' => [ 'shape' => 'Headers', ], ], ], 'GeoRestriction' => [ 'type' => 'structure', 'required' => [ 'RestrictionType', 'Quantity', ], 'members' => [ 'RestrictionType' => [ 'shape' => 'GeoRestrictionType', ], 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'LocationList', ], ], ], 'GeoRestrictionType' => [ 'type' => 'string', 'enum' => [ 'blacklist', 'whitelist', 'none', ], ], 'GetCloudFrontOriginAccessIdentityConfigRequest' => [ 'type' => 'structure', 'required' => [ 'Id', ], 'members' => [ 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], ], ], 'GetCloudFrontOriginAccessIdentityConfigResult' => [ 'type' => 'structure', 'members' => [ 'CloudFrontOriginAccessIdentityConfig' => [ 'shape' => 'CloudFrontOriginAccessIdentityConfig', ], 'ETag' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'ETag', ], ], 'payload' => 'CloudFrontOriginAccessIdentityConfig', ], 'GetCloudFrontOriginAccessIdentityRequest' => [ 'type' => 'structure', 'required' => [ 'Id', ], 'members' => [ 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], ], ], 'GetCloudFrontOriginAccessIdentityResult' => [ 'type' => 'structure', 'members' => [ 'CloudFrontOriginAccessIdentity' => [ 'shape' => 'CloudFrontOriginAccessIdentity', ], 'ETag' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'ETag', ], ], 'payload' => 'CloudFrontOriginAccessIdentity', ], 'GetDistributionConfigRequest' => [ 'type' => 'structure', 'required' => [ 'Id', ], 'members' => [ 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], ], ], 'GetDistributionConfigResult' => [ 'type' => 'structure', 'members' => [ 'DistributionConfig' => [ 'shape' => 'DistributionConfig', ], 'ETag' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'ETag', ], ], 'payload' => 'DistributionConfig', ], 'GetDistributionRequest' => [ 'type' => 'structure', 'required' => [ 'Id', ], 'members' => [ 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], ], ], 'GetDistributionResult' => [ 'type' => 'structure', 'members' => [ 'Distribution' => [ 'shape' => 'Distribution', ], 'ETag' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'ETag', ], ], 'payload' => 'Distribution', ], 'GetInvalidationRequest' => [ 'type' => 'structure', 'required' => [ 'DistributionId', 'Id', ], 'members' => [ 'DistributionId' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'DistributionId', ], 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], ], ], 'GetInvalidationResult' => [ 'type' => 'structure', 'members' => [ 'Invalidation' => [ 'shape' => 'Invalidation', ], ], 'payload' => 'Invalidation', ], 'GetStreamingDistributionConfigRequest' => [ 'type' => 'structure', 'required' => [ 'Id', ], 'members' => [ 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], ], ], 'GetStreamingDistributionConfigResult' => [ 'type' => 'structure', 'members' => [ 'StreamingDistributionConfig' => [ 'shape' => 'StreamingDistributionConfig', ], 'ETag' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'ETag', ], ], 'payload' => 'StreamingDistributionConfig', ], 'GetStreamingDistributionRequest' => [ 'type' => 'structure', 'required' => [ 'Id', ], 'members' => [ 'Id' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'Id', ], ], ], 'GetStreamingDistributionResult' => [ 'type' => 'structure', 'members' => [ 'StreamingDistribution' => [ 'shape' => 'StreamingDistribution', ], 'ETag' => [ 'shape' => 'string', 'location' => 'header', 'locationName' => 'ETag', ], ], 'payload' => 'StreamingDistribution', ], 'HeaderList' => [ 'type' => 'list', 'member' => [ 'shape' => 'string', 'locationName' => 'Name', ], ], 'Headers' => [ 'type' => 'structure', 'required' => [ 'Quantity', ], 'members' => [ 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'HeaderList', ], ], ], 'IllegalUpdate' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InconsistentQuantities' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidArgument' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidDefaultRootObject' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidErrorCode' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidForwardCookies' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidGeoRestrictionParameter' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidHeadersForS3Origin' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidIfMatchVersion' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidLocationCode' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidMinimumProtocolVersion' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidOrigin' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidOriginAccessIdentity' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidProtocolSettings' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidRelativePath' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidRequiredProtocol' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidResponseCode' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidTTLOrder' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidTagging' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidViewerCertificate' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'InvalidWebACLId' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'Invalidation' => [ 'type' => 'structure', 'required' => [ 'Id', 'Status', 'CreateTime', 'InvalidationBatch', ], 'members' => [ 'Id' => [ 'shape' => 'string', ], 'Status' => [ 'shape' => 'string', ], 'CreateTime' => [ 'shape' => 'timestamp', ], 'InvalidationBatch' => [ 'shape' => 'InvalidationBatch', ], ], ], 'InvalidationBatch' => [ 'type' => 'structure', 'required' => [ 'Paths', 'CallerReference', ], 'members' => [ 'Paths' => [ 'shape' => 'Paths', ], 'CallerReference' => [ 'shape' => 'string', ], ], ], 'InvalidationList' => [ 'type' => 'structure', 'required' => [ 'Marker', 'MaxItems', 'IsTruncated', 'Quantity', ], 'members' => [ 'Marker' => [ 'shape' => 'string', ], 'NextMarker' => [ 'shape' => 'string', ], 'MaxItems' => [ 'shape' => 'integer', ], 'IsTruncated' => [ 'shape' => 'boolean', ], 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'InvalidationSummaryList', ], ], ], 'InvalidationSummary' => [ 'type' => 'structure', 'required' => [ 'Id', 'CreateTime', 'Status', ], 'members' => [ 'Id' => [ 'shape' => 'string', ], 'CreateTime' => [ 'shape' => 'timestamp', ], 'Status' => [ 'shape' => 'string', ], ], ], 'InvalidationSummaryList' => [ 'type' => 'list', 'member' => [ 'shape' => 'InvalidationSummary', 'locationName' => 'InvalidationSummary', ], ], 'ItemSelection' => [ 'type' => 'string', 'enum' => [ 'none', 'whitelist', 'all', ], ], 'KeyPairIdList' => [ 'type' => 'list', 'member' => [ 'shape' => 'string', 'locationName' => 'KeyPairId', ], ], 'KeyPairIds' => [ 'type' => 'structure', 'required' => [ 'Quantity', ], 'members' => [ 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'KeyPairIdList', ], ], ], 'ListCloudFrontOriginAccessIdentitiesRequest' => [ 'type' => 'structure', 'members' => [ 'Marker' => [ 'shape' => 'string', 'location' => 'querystring', 'locationName' => 'Marker', ], 'MaxItems' => [ 'shape' => 'string', 'location' => 'querystring', 'locationName' => 'MaxItems', ], ], ], 'ListCloudFrontOriginAccessIdentitiesResult' => [ 'type' => 'structure', 'members' => [ 'CloudFrontOriginAccessIdentityList' => [ 'shape' => 'CloudFrontOriginAccessIdentityList', ], ], 'payload' => 'CloudFrontOriginAccessIdentityList', ], 'ListDistributionsByWebACLIdRequest' => [ 'type' => 'structure', 'required' => [ 'WebACLId', ], 'members' => [ 'Marker' => [ 'shape' => 'string', 'location' => 'querystring', 'locationName' => 'Marker', ], 'MaxItems' => [ 'shape' => 'string', 'location' => 'querystring', 'locationName' => 'MaxItems', ], 'WebACLId' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'WebACLId', ], ], ], 'ListDistributionsByWebACLIdResult' => [ 'type' => 'structure', 'members' => [ 'DistributionList' => [ 'shape' => 'DistributionList', ], ], 'payload' => 'DistributionList', ], 'ListDistributionsRequest' => [ 'type' => 'structure', 'members' => [ 'Marker' => [ 'shape' => 'string', 'location' => 'querystring', 'locationName' => 'Marker', ], 'MaxItems' => [ 'shape' => 'string', 'location' => 'querystring', 'locationName' => 'MaxItems', ], ], ], 'ListDistributionsResult' => [ 'type' => 'structure', 'members' => [ 'DistributionList' => [ 'shape' => 'DistributionList', ], ], 'payload' => 'DistributionList', ], 'ListInvalidationsRequest' => [ 'type' => 'structure', 'required' => [ 'DistributionId', ], 'members' => [ 'DistributionId' => [ 'shape' => 'string', 'location' => 'uri', 'locationName' => 'DistributionId', ], 'Marker' => [ 'shape' => 'string', 'location' => 'querystring', 'locationName' => 'Marker', ], 'MaxItems' => [ 'shape' => 'string', 'location' => 'querystring', 'locationName' => 'MaxItems', ], ], ], 'ListInvalidationsResult' => [ 'type' => 'structure', 'members' => [ 'InvalidationList' => [ 'shape' => 'InvalidationList', ], ], 'payload' => 'InvalidationList', ], 'ListStreamingDistributionsRequest' => [ 'type' => 'structure', 'members' => [ 'Marker' => [ 'shape' => 'string', 'location' => 'querystring', 'locationName' => 'Marker', ], 'MaxItems' => [ 'shape' => 'string', 'location' => 'querystring', 'locationName' => 'MaxItems', ], ], ], 'ListStreamingDistributionsResult' => [ 'type' => 'structure', 'members' => [ 'StreamingDistributionList' => [ 'shape' => 'StreamingDistributionList', ], ], 'payload' => 'StreamingDistributionList', ], 'ListTagsForResourceRequest' => [ 'type' => 'structure', 'required' => [ 'Resource', ], 'members' => [ 'Resource' => [ 'shape' => 'ResourceARN', 'location' => 'querystring', 'locationName' => 'Resource', ], ], ], 'ListTagsForResourceResult' => [ 'type' => 'structure', 'required' => [ 'Tags', ], 'members' => [ 'Tags' => [ 'shape' => 'Tags', ], ], 'payload' => 'Tags', ], 'LocationList' => [ 'type' => 'list', 'member' => [ 'shape' => 'string', 'locationName' => 'Location', ], ], 'LoggingConfig' => [ 'type' => 'structure', 'required' => [ 'Enabled', 'IncludeCookies', 'Bucket', 'Prefix', ], 'members' => [ 'Enabled' => [ 'shape' => 'boolean', ], 'IncludeCookies' => [ 'shape' => 'boolean', ], 'Bucket' => [ 'shape' => 'string', ], 'Prefix' => [ 'shape' => 'string', ], ], ], 'Method' => [ 'type' => 'string', 'enum' => [ 'GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'OPTIONS', 'DELETE', ], ], 'MethodsList' => [ 'type' => 'list', 'member' => [ 'shape' => 'Method', 'locationName' => 'Method', ], ], 'MinimumProtocolVersion' => [ 'type' => 'string', 'enum' => [ 'SSLv3', 'TLSv1', ], ], 'MissingBody' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'NoSuchCloudFrontOriginAccessIdentity' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 404, ], 'exception' => true, ], 'NoSuchDistribution' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 404, ], 'exception' => true, ], 'NoSuchInvalidation' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 404, ], 'exception' => true, ], 'NoSuchOrigin' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 404, ], 'exception' => true, ], 'NoSuchResource' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 404, ], 'exception' => true, ], 'NoSuchStreamingDistribution' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 404, ], 'exception' => true, ], 'Origin' => [ 'type' => 'structure', 'required' => [ 'Id', 'DomainName', ], 'members' => [ 'Id' => [ 'shape' => 'string', ], 'DomainName' => [ 'shape' => 'string', ], 'OriginPath' => [ 'shape' => 'string', ], 'CustomHeaders' => [ 'shape' => 'CustomHeaders', ], 'S3OriginConfig' => [ 'shape' => 'S3OriginConfig', ], 'CustomOriginConfig' => [ 'shape' => 'CustomOriginConfig', ], ], ], 'OriginCustomHeader' => [ 'type' => 'structure', 'required' => [ 'HeaderName', 'HeaderValue', ], 'members' => [ 'HeaderName' => [ 'shape' => 'string', ], 'HeaderValue' => [ 'shape' => 'string', ], ], ], 'OriginCustomHeadersList' => [ 'type' => 'list', 'member' => [ 'shape' => 'OriginCustomHeader', 'locationName' => 'OriginCustomHeader', ], ], 'OriginList' => [ 'type' => 'list', 'member' => [ 'shape' => 'Origin', 'locationName' => 'Origin', ], 'min' => 1, ], 'OriginProtocolPolicy' => [ 'type' => 'string', 'enum' => [ 'http-only', 'match-viewer', 'https-only', ], ], 'OriginSslProtocols' => [ 'type' => 'structure', 'required' => [ 'Quantity', 'Items', ], 'members' => [ 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'SslProtocolsList', ], ], ], 'Origins' => [ 'type' => 'structure', 'required' => [ 'Quantity', ], 'members' => [ 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'OriginList', ], ], ], 'PathList' => [ 'type' => 'list', 'member' => [ 'shape' => 'string', 'locationName' => 'Path', ], ], 'Paths' => [ 'type' => 'structure', 'required' => [ 'Quantity', ], 'members' => [ 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'PathList', ], ], ], 'PreconditionFailed' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 412, ], 'exception' => true, ], 'PriceClass' => [ 'type' => 'string', 'enum' => [ 'PriceClass_100', 'PriceClass_200', 'PriceClass_All', ], ], 'ResourceARN' => [ 'type' => 'string', 'pattern' => 'arn:aws:cloudfront::[0-9]+:.*', ], 'Restrictions' => [ 'type' => 'structure', 'required' => [ 'GeoRestriction', ], 'members' => [ 'GeoRestriction' => [ 'shape' => 'GeoRestriction', ], ], ], 'S3Origin' => [ 'type' => 'structure', 'required' => [ 'DomainName', 'OriginAccessIdentity', ], 'members' => [ 'DomainName' => [ 'shape' => 'string', ], 'OriginAccessIdentity' => [ 'shape' => 'string', ], ], ], 'S3OriginConfig' => [ 'type' => 'structure', 'required' => [ 'OriginAccessIdentity', ], 'members' => [ 'OriginAccessIdentity' => [ 'shape' => 'string', ], ], ], 'SSLSupportMethod' => [ 'type' => 'string', 'enum' => [ 'sni-only', 'vip', ], ], 'Signer' => [ 'type' => 'structure', 'members' => [ 'AwsAccountNumber' => [ 'shape' => 'string', ], 'KeyPairIds' => [ 'shape' => 'KeyPairIds', ], ], ], 'SignerList' => [ 'type' => 'list', 'member' => [ 'shape' => 'Signer', 'locationName' => 'Signer', ], ], 'SslProtocol' => [ 'type' => 'string', 'enum' => [ 'SSLv3', 'TLSv1', 'TLSv1.1', 'TLSv1.2', ], ], 'SslProtocolsList' => [ 'type' => 'list', 'member' => [ 'shape' => 'SslProtocol', 'locationName' => 'SslProtocol', ], ], 'StreamingDistribution' => [ 'type' => 'structure', 'required' => [ 'Id', 'ARN', 'Status', 'DomainName', 'ActiveTrustedSigners', 'StreamingDistributionConfig', ], 'members' => [ 'Id' => [ 'shape' => 'string', ], 'ARN' => [ 'shape' => 'string', ], 'Status' => [ 'shape' => 'string', ], 'LastModifiedTime' => [ 'shape' => 'timestamp', ], 'DomainName' => [ 'shape' => 'string', ], 'ActiveTrustedSigners' => [ 'shape' => 'ActiveTrustedSigners', ], 'StreamingDistributionConfig' => [ 'shape' => 'StreamingDistributionConfig', ], ], ], 'StreamingDistributionAlreadyExists' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 409, ], 'exception' => true, ], 'StreamingDistributionConfig' => [ 'type' => 'structure', 'required' => [ 'CallerReference', 'S3Origin', 'Comment', 'TrustedSigners', 'Enabled', ], 'members' => [ 'CallerReference' => [ 'shape' => 'string', ], 'S3Origin' => [ 'shape' => 'S3Origin', ], 'Aliases' => [ 'shape' => 'Aliases', ], 'Comment' => [ 'shape' => 'string', ], 'Logging' => [ 'shape' => 'StreamingLoggingConfig', ], 'TrustedSigners' => [ 'shape' => 'TrustedSigners', ], 'PriceClass' => [ 'shape' => 'PriceClass', ], 'Enabled' => [ 'shape' => 'boolean', ], ], ], 'StreamingDistributionConfigWithTags' => [ 'type' => 'structure', 'required' => [ 'StreamingDistributionConfig', 'Tags', ], 'members' => [ 'StreamingDistributionConfig' => [ 'shape' => 'StreamingDistributionConfig', ], 'Tags' => [ 'shape' => 'Tags', ], ], ], 'StreamingDistributionList' => [ 'type' => 'structure', 'required' => [ 'Marker', 'MaxItems', 'IsTruncated', 'Quantity', ], 'members' => [ 'Marker' => [ 'shape' => 'string', ], 'NextMarker' => [ 'shape' => 'string', ], 'MaxItems' => [ 'shape' => 'integer', ], 'IsTruncated' => [ 'shape' => 'boolean', ], 'Quantity' => [ 'shape' => 'integer', ], 'Items' => [ 'shape' => 'StreamingDistributionSummaryList', ], ], ], 'StreamingDistributionNotDisabled' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 409, ], 'exception' => true, ], 'StreamingDistributionSummary' => [ 'type' => 'structure', 'required' => [ 'Id', 'ARN', 'Status', 'LastModifiedTime', 'DomainName', 'S3Origin', 'Aliases', 'TrustedSigners', 'Comment', 'PriceClass', 'Enabled', ], 'members' => [ 'Id' => [ 'shape' => 'string', ], 'ARN' => [ 'shape' => 'string', ], 'Status' => [ 'shape' => 'string', ], 'LastModifiedTime' => [ 'shape' => 'timestamp', ], 'DomainName' => [ 'shape' => 'string', ], 'S3Origin' => [ 'shape' => 'S3Origin', ], 'Aliases' => [ 'shape' => 'Aliases', ], 'TrustedSigners' => [ 'shape' => 'TrustedSigners', ], 'Comment' => [ 'shape' => 'string', ], 'PriceClass' => [ 'shape' => 'PriceClass', ], 'Enabled' => [ 'shape' => 'boolean', ], ], ], 'StreamingDistributionSummaryList' => [ 'type' => 'list', 'member' => [ 'shape' => 'StreamingDistributionSummary', 'locationName' => 'StreamingDistributionSummary', ], ], 'StreamingLoggingConfig' => [ 'type' => 'structure', 'required' => [ 'Enabled', 'Bucket', 'Prefix', ], 'members' => [ 'Enabled' => [ 'shape' => 'boolean', ], 'Bucket' => [ 'shape' => 'string', ], 'Prefix' => [ 'shape' => 'string', ], ], ], 'Tag' => [ 'type' => 'structure', 'required' => [ 'Key', ], 'members' => [ 'Key' => [ 'shape' => 'TagKey', ], 'Value' => [ 'shape' => 'TagValue', ], ], ], 'TagKey' => [ 'type' => 'string', 'max' => 128, 'min' => 1, 'pattern' => '^([\\p{L}\\p{Z}\\p{N}_.:/=+\\-@]*)$', ], 'TagKeyList' => [ 'type' => 'list', 'member' => [ 'shape' => 'TagKey', 'locationName' => 'Key', ], ], 'TagKeys' => [ 'type' => 'structure', 'members' => [ 'Items' => [ 'shape' => 'TagKeyList', ], ], ], 'TagList' => [ 'type' => 'list', 'member' => [ 'shape' => 'Tag', 'locationName' => 'Tag', ], ], 'TagResourceRequest' => [ 'type' => 'structure', 'required' => [ 'Resource', 'Tags', ], 'members' => [ 'Resource' => [ 'shape' => 'ResourceARN', 'location' => 'querystring', 'locationName' => 'Resource', ], 'Tags' => [ 'shape' => 'Tags', 'locationName' => 'Tags', 'xmlNamespace' => [ 'uri' => 'http://cloudfront.amazonaws.com/doc/2016-08-01/', ], ], ], 'payload' => 'Tags', ], 'TagValue' => [ 'type' => 'string', 'max' => 256, 'min' => 0, 'pattern' => '^([\\p{L}\\p{Z}\\p{N}_.:/=+\\-@]*)$', ], 'Tags' => [ 'type' => 'structure', 'members' => [ 'Items' => [ 'shape' => 'TagList', ], ], ], 'TooManyCacheBehaviors' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TooManyCertificates' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TooManyCloudFrontOriginAccessIdentities' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TooManyCookieNamesInWhiteList' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TooManyDistributionCNAMEs' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TooManyDistributions' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TooManyHeadersInForwardedValues' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TooManyInvalidationsInProgress' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TooManyOriginCustomHeaders' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TooManyOrigins' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TooManyStreamingDistributionCNAMEs' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TooManyStreamingDistributions' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TooManyTrustedSigners' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TrustedSignerDoesNotExist' => [ 'type' => 'structure', 'members' => [ 'Message' => [ 'shape' => 'string', ], ], 'error' => [ 'httpStatusCode' => 400, ], 'exception' => true, ], 'TrustedSigners' => [ 'type' => 'structure', 'required' => [ 'Enabled', 'Quantity', ], 'members' => [ 'Enabled' => [ 'shape' => 'boolean', ], 'Quantity' => [ 'shape' => 'integer', ], 'Items'
|