Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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\CacheInterface;
9
use Aws\ClientResolver;
10
use Aws\Command;
1441 ariadna 11
use Aws\CommandInterface;
12
use Aws\Configuration\ConfigurationResolver;
1 efrain 13
use Aws\Exception\AwsException;
14
use Aws\HandlerList;
1441 ariadna 15
use Aws\Identity\S3\S3ExpressIdentityProvider;
1 efrain 16
use Aws\InputValidationMiddleware;
17
use Aws\Middleware;
1441 ariadna 18
use Aws\ResultInterface;
1 efrain 19
use Aws\Retry\QuotaManager;
20
use Aws\RetryMiddleware;
21
use Aws\RetryMiddlewareV2;
1441 ariadna 22
use Aws\S3\Parser\GetBucketLocationResultMutator;
23
use Aws\S3\Parser\S3Parser;
24
use Aws\S3\Parser\ValidateResponseChecksumResultMutator;
25
use Aws\S3\RegionalEndpoint\ConfigurationProvider;
1 efrain 26
use Aws\S3\UseArnRegion\Configuration;
27
use Aws\S3\UseArnRegion\ConfigurationInterface;
28
use Aws\S3\UseArnRegion\ConfigurationProvider as UseArnRegionConfigurationProvider;
29
use GuzzleHttp\Exception\RequestException;
30
use GuzzleHttp\Promise\PromiseInterface;
31
use Psr\Http\Message\RequestInterface;
32
 
33
/**
34
 * Client used to interact with **Amazon Simple Storage Service (Amazon S3)**.
35
 *
36
 * @method \Aws\Result abortMultipartUpload(array $args = [])
37
 * @method \GuzzleHttp\Promise\Promise abortMultipartUploadAsync(array $args = [])
38
 * @method \Aws\Result completeMultipartUpload(array $args = [])
39
 * @method \GuzzleHttp\Promise\Promise completeMultipartUploadAsync(array $args = [])
40
 * @method \Aws\Result copyObject(array $args = [])
41
 * @method \GuzzleHttp\Promise\Promise copyObjectAsync(array $args = [])
42
 * @method \Aws\Result createBucket(array $args = [])
43
 * @method \GuzzleHttp\Promise\Promise createBucketAsync(array $args = [])
1441 ariadna 44
 * @method \Aws\Result createBucketMetadataTableConfiguration(array $args = [])
45
 * @method \GuzzleHttp\Promise\Promise createBucketMetadataTableConfigurationAsync(array $args = [])
1 efrain 46
 * @method \Aws\Result createMultipartUpload(array $args = [])
47
 * @method \GuzzleHttp\Promise\Promise createMultipartUploadAsync(array $args = [])
1441 ariadna 48
 * @method \Aws\Result createSession(array $args = [])
49
 * @method \GuzzleHttp\Promise\Promise createSessionAsync(array $args = [])
1 efrain 50
 * @method \Aws\Result deleteBucket(array $args = [])
51
 * @method \GuzzleHttp\Promise\Promise deleteBucketAsync(array $args = [])
52
 * @method \Aws\Result deleteBucketAnalyticsConfiguration(array $args = [])
53
 * @method \GuzzleHttp\Promise\Promise deleteBucketAnalyticsConfigurationAsync(array $args = [])
54
 * @method \Aws\Result deleteBucketCors(array $args = [])
55
 * @method \GuzzleHttp\Promise\Promise deleteBucketCorsAsync(array $args = [])
56
 * @method \Aws\Result deleteBucketEncryption(array $args = [])
57
 * @method \GuzzleHttp\Promise\Promise deleteBucketEncryptionAsync(array $args = [])
58
 * @method \Aws\Result deleteBucketIntelligentTieringConfiguration(array $args = [])
59
 * @method \GuzzleHttp\Promise\Promise deleteBucketIntelligentTieringConfigurationAsync(array $args = [])
60
 * @method \Aws\Result deleteBucketInventoryConfiguration(array $args = [])
61
 * @method \GuzzleHttp\Promise\Promise deleteBucketInventoryConfigurationAsync(array $args = [])
62
 * @method \Aws\Result deleteBucketLifecycle(array $args = [])
63
 * @method \GuzzleHttp\Promise\Promise deleteBucketLifecycleAsync(array $args = [])
1441 ariadna 64
 * @method \Aws\Result deleteBucketMetadataTableConfiguration(array $args = [])
65
 * @method \GuzzleHttp\Promise\Promise deleteBucketMetadataTableConfigurationAsync(array $args = [])
1 efrain 66
 * @method \Aws\Result deleteBucketMetricsConfiguration(array $args = [])
67
 * @method \GuzzleHttp\Promise\Promise deleteBucketMetricsConfigurationAsync(array $args = [])
68
 * @method \Aws\Result deleteBucketOwnershipControls(array $args = [])
69
 * @method \GuzzleHttp\Promise\Promise deleteBucketOwnershipControlsAsync(array $args = [])
70
 * @method \Aws\Result deleteBucketPolicy(array $args = [])
71
 * @method \GuzzleHttp\Promise\Promise deleteBucketPolicyAsync(array $args = [])
72
 * @method \Aws\Result deleteBucketReplication(array $args = [])
73
 * @method \GuzzleHttp\Promise\Promise deleteBucketReplicationAsync(array $args = [])
74
 * @method \Aws\Result deleteBucketTagging(array $args = [])
75
 * @method \GuzzleHttp\Promise\Promise deleteBucketTaggingAsync(array $args = [])
76
 * @method \Aws\Result deleteBucketWebsite(array $args = [])
77
 * @method \GuzzleHttp\Promise\Promise deleteBucketWebsiteAsync(array $args = [])
78
 * @method \Aws\Result deleteObject(array $args = [])
79
 * @method \GuzzleHttp\Promise\Promise deleteObjectAsync(array $args = [])
80
 * @method \Aws\Result deleteObjectTagging(array $args = [])
81
 * @method \GuzzleHttp\Promise\Promise deleteObjectTaggingAsync(array $args = [])
82
 * @method \Aws\Result deleteObjects(array $args = [])
83
 * @method \GuzzleHttp\Promise\Promise deleteObjectsAsync(array $args = [])
84
 * @method \Aws\Result deletePublicAccessBlock(array $args = [])
85
 * @method \GuzzleHttp\Promise\Promise deletePublicAccessBlockAsync(array $args = [])
86
 * @method \Aws\Result getBucketAccelerateConfiguration(array $args = [])
87
 * @method \GuzzleHttp\Promise\Promise getBucketAccelerateConfigurationAsync(array $args = [])
88
 * @method \Aws\Result getBucketAcl(array $args = [])
89
 * @method \GuzzleHttp\Promise\Promise getBucketAclAsync(array $args = [])
90
 * @method \Aws\Result getBucketAnalyticsConfiguration(array $args = [])
91
 * @method \GuzzleHttp\Promise\Promise getBucketAnalyticsConfigurationAsync(array $args = [])
92
 * @method \Aws\Result getBucketCors(array $args = [])
93
 * @method \GuzzleHttp\Promise\Promise getBucketCorsAsync(array $args = [])
94
 * @method \Aws\Result getBucketEncryption(array $args = [])
95
 * @method \GuzzleHttp\Promise\Promise getBucketEncryptionAsync(array $args = [])
96
 * @method \Aws\Result getBucketIntelligentTieringConfiguration(array $args = [])
97
 * @method \GuzzleHttp\Promise\Promise getBucketIntelligentTieringConfigurationAsync(array $args = [])
98
 * @method \Aws\Result getBucketInventoryConfiguration(array $args = [])
99
 * @method \GuzzleHttp\Promise\Promise getBucketInventoryConfigurationAsync(array $args = [])
100
 * @method \Aws\Result getBucketLifecycle(array $args = [])
101
 * @method \GuzzleHttp\Promise\Promise getBucketLifecycleAsync(array $args = [])
102
 * @method \Aws\Result getBucketLifecycleConfiguration(array $args = [])
103
 * @method \GuzzleHttp\Promise\Promise getBucketLifecycleConfigurationAsync(array $args = [])
104
 * @method \Aws\Result getBucketLocation(array $args = [])
105
 * @method \GuzzleHttp\Promise\Promise getBucketLocationAsync(array $args = [])
106
 * @method \Aws\Result getBucketLogging(array $args = [])
107
 * @method \GuzzleHttp\Promise\Promise getBucketLoggingAsync(array $args = [])
1441 ariadna 108
 * @method \Aws\Result getBucketMetadataTableConfiguration(array $args = [])
109
 * @method \GuzzleHttp\Promise\Promise getBucketMetadataTableConfigurationAsync(array $args = [])
1 efrain 110
 * @method \Aws\Result getBucketMetricsConfiguration(array $args = [])
111
 * @method \GuzzleHttp\Promise\Promise getBucketMetricsConfigurationAsync(array $args = [])
112
 * @method \Aws\Result getBucketNotification(array $args = [])
113
 * @method \GuzzleHttp\Promise\Promise getBucketNotificationAsync(array $args = [])
114
 * @method \Aws\Result getBucketNotificationConfiguration(array $args = [])
115
 * @method \GuzzleHttp\Promise\Promise getBucketNotificationConfigurationAsync(array $args = [])
116
 * @method \Aws\Result getBucketOwnershipControls(array $args = [])
117
 * @method \GuzzleHttp\Promise\Promise getBucketOwnershipControlsAsync(array $args = [])
118
 * @method \Aws\Result getBucketPolicy(array $args = [])
119
 * @method \GuzzleHttp\Promise\Promise getBucketPolicyAsync(array $args = [])
120
 * @method \Aws\Result getBucketPolicyStatus(array $args = [])
121
 * @method \GuzzleHttp\Promise\Promise getBucketPolicyStatusAsync(array $args = [])
122
 * @method \Aws\Result getBucketReplication(array $args = [])
123
 * @method \GuzzleHttp\Promise\Promise getBucketReplicationAsync(array $args = [])
124
 * @method \Aws\Result getBucketRequestPayment(array $args = [])
125
 * @method \GuzzleHttp\Promise\Promise getBucketRequestPaymentAsync(array $args = [])
126
 * @method \Aws\Result getBucketTagging(array $args = [])
127
 * @method \GuzzleHttp\Promise\Promise getBucketTaggingAsync(array $args = [])
128
 * @method \Aws\Result getBucketVersioning(array $args = [])
129
 * @method \GuzzleHttp\Promise\Promise getBucketVersioningAsync(array $args = [])
130
 * @method \Aws\Result getBucketWebsite(array $args = [])
131
 * @method \GuzzleHttp\Promise\Promise getBucketWebsiteAsync(array $args = [])
132
 * @method \Aws\Result getObject(array $args = [])
133
 * @method \GuzzleHttp\Promise\Promise getObjectAsync(array $args = [])
134
 * @method \Aws\Result getObjectAcl(array $args = [])
135
 * @method \GuzzleHttp\Promise\Promise getObjectAclAsync(array $args = [])
136
 * @method \Aws\Result getObjectAttributes(array $args = [])
137
 * @method \GuzzleHttp\Promise\Promise getObjectAttributesAsync(array $args = [])
138
 * @method \Aws\Result getObjectLegalHold(array $args = [])
139
 * @method \GuzzleHttp\Promise\Promise getObjectLegalHoldAsync(array $args = [])
140
 * @method \Aws\Result getObjectLockConfiguration(array $args = [])
141
 * @method \GuzzleHttp\Promise\Promise getObjectLockConfigurationAsync(array $args = [])
142
 * @method \Aws\Result getObjectRetention(array $args = [])
143
 * @method \GuzzleHttp\Promise\Promise getObjectRetentionAsync(array $args = [])
144
 * @method \Aws\Result getObjectTagging(array $args = [])
145
 * @method \GuzzleHttp\Promise\Promise getObjectTaggingAsync(array $args = [])
146
 * @method \Aws\Result getObjectTorrent(array $args = [])
147
 * @method \GuzzleHttp\Promise\Promise getObjectTorrentAsync(array $args = [])
148
 * @method \Aws\Result getPublicAccessBlock(array $args = [])
149
 * @method \GuzzleHttp\Promise\Promise getPublicAccessBlockAsync(array $args = [])
150
 * @method \Aws\Result headBucket(array $args = [])
151
 * @method \GuzzleHttp\Promise\Promise headBucketAsync(array $args = [])
152
 * @method \Aws\Result headObject(array $args = [])
153
 * @method \GuzzleHttp\Promise\Promise headObjectAsync(array $args = [])
154
 * @method \Aws\Result listBucketAnalyticsConfigurations(array $args = [])
155
 * @method \GuzzleHttp\Promise\Promise listBucketAnalyticsConfigurationsAsync(array $args = [])
156
 * @method \Aws\Result listBucketIntelligentTieringConfigurations(array $args = [])
157
 * @method \GuzzleHttp\Promise\Promise listBucketIntelligentTieringConfigurationsAsync(array $args = [])
158
 * @method \Aws\Result listBucketInventoryConfigurations(array $args = [])
159
 * @method \GuzzleHttp\Promise\Promise listBucketInventoryConfigurationsAsync(array $args = [])
160
 * @method \Aws\Result listBucketMetricsConfigurations(array $args = [])
161
 * @method \GuzzleHttp\Promise\Promise listBucketMetricsConfigurationsAsync(array $args = [])
162
 * @method \Aws\Result listBuckets(array $args = [])
163
 * @method \GuzzleHttp\Promise\Promise listBucketsAsync(array $args = [])
1441 ariadna 164
 * @method \Aws\Result listDirectoryBuckets(array $args = [])
165
 * @method \GuzzleHttp\Promise\Promise listDirectoryBucketsAsync(array $args = [])
1 efrain 166
 * @method \Aws\Result listMultipartUploads(array $args = [])
167
 * @method \GuzzleHttp\Promise\Promise listMultipartUploadsAsync(array $args = [])
168
 * @method \Aws\Result listObjectVersions(array $args = [])
169
 * @method \GuzzleHttp\Promise\Promise listObjectVersionsAsync(array $args = [])
170
 * @method \Aws\Result listObjects(array $args = [])
171
 * @method \GuzzleHttp\Promise\Promise listObjectsAsync(array $args = [])
172
 * @method \Aws\Result listObjectsV2(array $args = [])
173
 * @method \GuzzleHttp\Promise\Promise listObjectsV2Async(array $args = [])
174
 * @method \Aws\Result listParts(array $args = [])
175
 * @method \GuzzleHttp\Promise\Promise listPartsAsync(array $args = [])
176
 * @method \Aws\Result putBucketAccelerateConfiguration(array $args = [])
177
 * @method \GuzzleHttp\Promise\Promise putBucketAccelerateConfigurationAsync(array $args = [])
178
 * @method \Aws\Result putBucketAcl(array $args = [])
179
 * @method \GuzzleHttp\Promise\Promise putBucketAclAsync(array $args = [])
180
 * @method \Aws\Result putBucketAnalyticsConfiguration(array $args = [])
181
 * @method \GuzzleHttp\Promise\Promise putBucketAnalyticsConfigurationAsync(array $args = [])
182
 * @method \Aws\Result putBucketCors(array $args = [])
183
 * @method \GuzzleHttp\Promise\Promise putBucketCorsAsync(array $args = [])
184
 * @method \Aws\Result putBucketEncryption(array $args = [])
185
 * @method \GuzzleHttp\Promise\Promise putBucketEncryptionAsync(array $args = [])
186
 * @method \Aws\Result putBucketIntelligentTieringConfiguration(array $args = [])
187
 * @method \GuzzleHttp\Promise\Promise putBucketIntelligentTieringConfigurationAsync(array $args = [])
188
 * @method \Aws\Result putBucketInventoryConfiguration(array $args = [])
189
 * @method \GuzzleHttp\Promise\Promise putBucketInventoryConfigurationAsync(array $args = [])
190
 * @method \Aws\Result putBucketLifecycle(array $args = [])
191
 * @method \GuzzleHttp\Promise\Promise putBucketLifecycleAsync(array $args = [])
192
 * @method \Aws\Result putBucketLifecycleConfiguration(array $args = [])
193
 * @method \GuzzleHttp\Promise\Promise putBucketLifecycleConfigurationAsync(array $args = [])
194
 * @method \Aws\Result putBucketLogging(array $args = [])
195
 * @method \GuzzleHttp\Promise\Promise putBucketLoggingAsync(array $args = [])
196
 * @method \Aws\Result putBucketMetricsConfiguration(array $args = [])
197
 * @method \GuzzleHttp\Promise\Promise putBucketMetricsConfigurationAsync(array $args = [])
198
 * @method \Aws\Result putBucketNotification(array $args = [])
199
 * @method \GuzzleHttp\Promise\Promise putBucketNotificationAsync(array $args = [])
200
 * @method \Aws\Result putBucketNotificationConfiguration(array $args = [])
201
 * @method \GuzzleHttp\Promise\Promise putBucketNotificationConfigurationAsync(array $args = [])
202
 * @method \Aws\Result putBucketOwnershipControls(array $args = [])
203
 * @method \GuzzleHttp\Promise\Promise putBucketOwnershipControlsAsync(array $args = [])
204
 * @method \Aws\Result putBucketPolicy(array $args = [])
205
 * @method \GuzzleHttp\Promise\Promise putBucketPolicyAsync(array $args = [])
206
 * @method \Aws\Result putBucketReplication(array $args = [])
207
 * @method \GuzzleHttp\Promise\Promise putBucketReplicationAsync(array $args = [])
208
 * @method \Aws\Result putBucketRequestPayment(array $args = [])
209
 * @method \GuzzleHttp\Promise\Promise putBucketRequestPaymentAsync(array $args = [])
210
 * @method \Aws\Result putBucketTagging(array $args = [])
211
 * @method \GuzzleHttp\Promise\Promise putBucketTaggingAsync(array $args = [])
212
 * @method \Aws\Result putBucketVersioning(array $args = [])
213
 * @method \GuzzleHttp\Promise\Promise putBucketVersioningAsync(array $args = [])
214
 * @method \Aws\Result putBucketWebsite(array $args = [])
215
 * @method \GuzzleHttp\Promise\Promise putBucketWebsiteAsync(array $args = [])
216
 * @method \Aws\Result putObject(array $args = [])
217
 * @method \GuzzleHttp\Promise\Promise putObjectAsync(array $args = [])
218
 * @method \Aws\Result putObjectAcl(array $args = [])
219
 * @method \GuzzleHttp\Promise\Promise putObjectAclAsync(array $args = [])
220
 * @method \Aws\Result putObjectLegalHold(array $args = [])
221
 * @method \GuzzleHttp\Promise\Promise putObjectLegalHoldAsync(array $args = [])
222
 * @method \Aws\Result putObjectLockConfiguration(array $args = [])
223
 * @method \GuzzleHttp\Promise\Promise putObjectLockConfigurationAsync(array $args = [])
224
 * @method \Aws\Result putObjectRetention(array $args = [])
225
 * @method \GuzzleHttp\Promise\Promise putObjectRetentionAsync(array $args = [])
226
 * @method \Aws\Result putObjectTagging(array $args = [])
227
 * @method \GuzzleHttp\Promise\Promise putObjectTaggingAsync(array $args = [])
228
 * @method \Aws\Result putPublicAccessBlock(array $args = [])
229
 * @method \GuzzleHttp\Promise\Promise putPublicAccessBlockAsync(array $args = [])
230
 * @method \Aws\Result restoreObject(array $args = [])
231
 * @method \GuzzleHttp\Promise\Promise restoreObjectAsync(array $args = [])
232
 * @method \Aws\Result selectObjectContent(array $args = [])
233
 * @method \GuzzleHttp\Promise\Promise selectObjectContentAsync(array $args = [])
234
 * @method \Aws\Result uploadPart(array $args = [])
235
 * @method \GuzzleHttp\Promise\Promise uploadPartAsync(array $args = [])
236
 * @method \Aws\Result uploadPartCopy(array $args = [])
237
 * @method \GuzzleHttp\Promise\Promise uploadPartCopyAsync(array $args = [])
238
 * @method \Aws\Result writeGetObjectResponse(array $args = [])
239
 * @method \GuzzleHttp\Promise\Promise writeGetObjectResponseAsync(array $args = [])
240
 */
241
class S3Client extends AwsClient implements S3ClientInterface
242
{
1441 ariadna 243
    private const DIRECTORY_BUCKET_REGEX = '/^[a-zA-Z0-9_-]+--[a-z0-9]+-az\d+--x-s3'
244
                                            .'(?!.*(?:-s3alias|--ol-s3|\.mrap))$/';
1 efrain 245
    use S3ClientTrait;
246
 
247
    /** @var array */
248
    private static $mandatoryAttributes = ['Bucket', 'Key'];
249
 
1441 ariadna 250
    /** @var array */
251
    private static $checksumOptionEnum = [
252
        'when_supported' => true,
253
        'when_required' => true
254
    ];
255
 
1 efrain 256
    public static function getArguments()
257
    {
258
        $args = parent::getArguments();
259
        $args['retries']['fn'] = [__CLASS__, '_applyRetryConfig'];
260
        $args['api_provider']['fn'] = [__CLASS__, '_applyApiProvider'];
261
 
1441 ariadna 262
        return
263
            [
264
                'request_checksum_calculation' => [
265
                    'type' => 'config',
266
                    'valid' => ['string'],
267
                    'doc' => 'Valid values are `when_supported` and `when_required`. Default is `when_supported`.'
268
                        . ' `when_supported` results in checksum calculation when an operation has modeled checksum support.'
269
                        . ' `when_required` results in checksum calculation when an operation has modeled checksum support and'
270
                        . ' request checksums are modeled as required.',
271
                    'fn' => [__CLASS__, '_apply_request_checksum_calculation'],
272
                    'default' => [__CLASS__, '_default_request_checksum_calculation'],
1 efrain 273
                ],
1441 ariadna 274
                'response_checksum_validation' => [
275
                    'type' => 'config',
276
                    'valid' => ['string'],
277
                    'doc' => 'Valid values are `when_supported` and `when_required`. Default is `when_supported`.'
278
                        . ' `when_supported` results in checksum validation when an operation has modeled checksum support.'
279
                        . ' `when_required` results in checksum validation when an operation has modeled checksum support and'
280
                        . ' `CheckSumMode` is set to `enabled`.',
281
                    'fn' => [__CLASS__, '_apply_response_checksum_validation'],
282
                    'default' => [__CLASS__, '_default_response_checksum_validation'],
283
                ]
284
            ]
285
            + $args + [
286
                'bucket_endpoint' => [
287
                    'type'    => 'config',
288
                    'valid'   => ['bool'],
289
                    'doc'     => 'Set to true to send requests to a hardcoded '
290
                        . 'bucket endpoint rather than create an endpoint as a '
291
                        . 'result of injecting the bucket into the URL. This '
292
                        . 'option is useful for interacting with CNAME endpoints.',
293
                ],
294
                'use_arn_region' => [
295
                    'type'    => 'config',
296
                    'valid'   => [
297
                        'bool',
298
                        Configuration::class,
299
                        CacheInterface::class,
300
                        'callable'
301
                    ],
302
                    'doc'     => 'Set to true to allow passed in ARNs to override'
303
                        . ' client region. Accepts...',
304
                    'fn' => [__CLASS__, '_apply_use_arn_region'],
305
                    'default' => [UseArnRegionConfigurationProvider::class, 'defaultProvider'],
306
                ],
307
                'use_accelerate_endpoint' => [
308
                    'type' => 'config',
309
                    'valid' => ['bool'],
310
                    'doc' => 'Set to true to send requests to an S3 Accelerate'
311
                        . ' endpoint by default. Can be enabled or disabled on'
312
                        . ' individual operations by setting'
313
                        . ' \'@use_accelerate_endpoint\' to true or false. Note:'
314
                        . ' you must enable S3 Accelerate on a bucket before it can'
315
                        . ' be accessed via an Accelerate endpoint.',
316
                    'default' => false,
317
                ],
318
                'use_path_style_endpoint' => [
319
                    'type' => 'config',
320
                    'valid' => ['bool'],
321
                    'doc' => 'Set to true to send requests to an S3 path style'
322
                        . ' endpoint by default.'
323
                        . ' Can be enabled or disabled on individual operations by setting'
324
                        . ' \'@use_path_style_endpoint\' to true or false.',
325
                    'default' => false,
326
                ],
327
                'disable_multiregion_access_points' => [
328
                    'type' => 'config',
329
                    'valid' => ['bool'],
330
                    'doc' => 'Set to true to disable the usage of'
331
                        . ' multi region access points. These are enabled by default.'
332
                        . ' Can be enabled or disabled on individual operations by setting'
333
                        . ' \'@disable_multiregion_access_points\' to true or false.',
334
                    'default' => false,
335
                ],
336
                'disable_express_session_auth' => [
337
                    'type' => 'config',
338
                    'valid' => ['bool'],
339
                    'doc' => 'Set to true to disable the usage of'
340
                        . ' s3 express session authentication. This is enabled by default.',
341
                    'default' => [__CLASS__, '_default_disable_express_session_auth'],
342
                ],
343
                's3_express_identity_provider' => [
344
                    'type'    => 'config',
345
                    'valid'   => [
346
                        'bool',
347
                        'callable'
348
                    ],
349
                    'doc'     => 'Specifies the provider used to generate identities to sign s3 express requests.  '
350
                        . 'Set to `false` to disable s3 express auth, or a callable provider used to create s3 express '
351
                        . 'identities or return null.',
352
                    'default' => [__CLASS__, '_default_s3_express_identity_provider'],
1 efrain 353
            ],
354
        ];
355
    }
356
 
357
    /**
358
     * {@inheritdoc}
359
     *
360
     * In addition to the options available to
361
     * {@see Aws\AwsClient::__construct}, S3Client accepts the following
362
     * options:
363
     *
364
     * - bucket_endpoint: (bool) Set to true to send requests to a
365
     *   hardcoded bucket endpoint rather than create an endpoint as a result
366
     *   of injecting the bucket into the URL. This option is useful for
367
     *   interacting with CNAME endpoints. Note: if you are using version 2.243.0
368
     *   and above and do not expect the bucket name to appear in the host, you will
369
     *   also need to set `use_path_style_endpoint` to `true`.
370
     * - calculate_md5: (bool) Set to false to disable calculating an MD5
371
     *   for all Amazon S3 signed uploads.
372
     * - s3_us_east_1_regional_endpoint:
373
     *   (Aws\S3\RegionalEndpoint\ConfigurationInterface|Aws\CacheInterface\|callable|string|array)
374
     *   Specifies whether to use regional or legacy endpoints for the us-east-1
375
     *   region. Provide an Aws\S3\RegionalEndpoint\ConfigurationInterface object, an
376
     *   instance of Aws\CacheInterface, a callable configuration provider used
377
     *   to create endpoint configuration, a string value of `legacy` or
378
     *   `regional`, or an associative array with the following keys:
379
     *   endpoint_types: (string)  Set to `legacy` or `regional`, defaults to
380
     *   `legacy`
381
     * - use_accelerate_endpoint: (bool) Set to true to send requests to an S3
382
     *   Accelerate endpoint by default. Can be enabled or disabled on
383
     *   individual operations by setting '@use_accelerate_endpoint' to true or
384
     *   false. Note: you must enable S3 Accelerate on a bucket before it can be
385
     *   accessed via an Accelerate endpoint.
386
     * - use_arn_region: (Aws\S3\UseArnRegion\ConfigurationInterface,
387
     *   Aws\CacheInterface, bool, callable) Set to true to enable the client
388
     *   to use the region from a supplied ARN argument instead of the client's
389
     *   region. Provide an instance of Aws\S3\UseArnRegion\ConfigurationInterface,
390
     *   an instance of Aws\CacheInterface, a callable that provides a promise for
391
     *   a Configuration object, or a boolean value. Defaults to false (i.e.
392
     *   the SDK will not follow the ARN region if it conflicts with the client
393
     *   region and instead throw an error).
394
     * - use_dual_stack_endpoint: (bool) Set to true to send requests to an S3
395
     *   Dual Stack endpoint by default, which enables IPv6 Protocol.
396
     *   Can be enabled or disabled on individual operations by setting
397
     *   '@use_dual_stack_endpoint\' to true or false. Note:
398
     *   you cannot use it together with an accelerate endpoint.
399
     * - use_path_style_endpoint: (bool) Set to true to send requests to an S3
400
     *   path style endpoint by default.
401
     *   Can be enabled or disabled on individual operations by setting
402
     *   '@use_path_style_endpoint\' to true or false. Note:
403
     *   you cannot use it together with an accelerate endpoint.
404
     * - disable_multiregion_access_points: (bool) Set to true to disable
405
     *   sending multi region requests.  They are enabled by default.
406
     *   Can be enabled or disabled on individual operations by setting
407
     *   '@disable_multiregion_access_points\' to true or false. Note:
408
     *   you cannot use it together with an accelerate or dualstack endpoint.
409
     *
410
     * @param array $args
411
     */
412
    public function __construct(array $args)
413
    {
414
        if (
415
            !isset($args['s3_us_east_1_regional_endpoint'])
416
            || $args['s3_us_east_1_regional_endpoint'] instanceof CacheInterface
417
        ) {
418
            $args['s3_us_east_1_regional_endpoint'] = ConfigurationProvider::defaultProvider($args);
419
        }
420
        $this->addBuiltIns($args);
421
        parent::__construct($args);
422
        $stack = $this->getHandlerList();
423
        $stack->appendInit(SSECMiddleware::wrap($this->getEndpoint()->getScheme()), 's3.ssec');
424
        $stack->appendBuild(
1441 ariadna 425
            ApplyChecksumMiddleware::wrap($this->getApi(), $this->getConfig()),
426
            's3.checksum'
427
        );
428
        $stack->appendBuild(
1 efrain 429
            Middleware::contentType(['PutObject', 'UploadPart']),
430
            's3.content_type'
431
        );
432
 
433
        if ($this->getConfig('bucket_endpoint')) {
434
            $stack->appendBuild(BucketEndpointMiddleware::wrap(), 's3.bucket_endpoint');
435
        } elseif (!$this->isUseEndpointV2()) {
436
            $stack->appendBuild(
437
                S3EndpointMiddleware::wrap(
438
                    $this->getRegion(),
439
                    $this->getConfig('endpoint_provider'),
440
                    [
441
                        'accelerate' => $this->getConfig('use_accelerate_endpoint'),
442
                        'path_style' => $this->getConfig('use_path_style_endpoint'),
443
                        'use_fips_endpoint' => $this->getConfig('use_fips_endpoint'),
444
                        'dual_stack' =>
445
                            $this->getConfig('use_dual_stack_endpoint')->isUseDualStackEndpoint(),
446
 
447
                    ]
448
                ),
449
                's3.endpoint_middleware'
450
            );
451
        }
452
 
453
        $stack->appendBuild(
454
            BucketEndpointArnMiddleware::wrap(
455
                $this->getApi(),
456
                $this->getRegion(),
457
                [
458
                    'use_arn_region' => $this->getConfig('use_arn_region'),
459
                    'accelerate' => $this->getConfig('use_accelerate_endpoint'),
460
                    'path_style' => $this->getConfig('use_path_style_endpoint'),
461
                    'dual_stack' =>
462
                        $this->getConfig('use_dual_stack_endpoint')->isUseDualStackEndpoint(),
463
                    'use_fips_endpoint' => $this->getConfig('use_fips_endpoint'),
464
                    'disable_multiregion_access_points' =>
465
                        $this->getConfig('disable_multiregion_access_points'),
1441 ariadna 466
                    'endpoint' => $args['endpoint'] ?? null
1 efrain 467
                ],
468
                $this->isUseEndpointV2()
469
            ),
470
            's3.bucket_endpoint_arn'
471
        );
1441 ariadna 472
        if ($this->getConfig('disable_express_session_auth')) {
473
            $stack->prependSign(
474
                $this->getDisableExpressSessionAuthMiddleware(),
475
                's3.disable_express_session_auth'
476
            );
477
        }
1 efrain 478
 
479
        $stack->appendValidate(
480
            InputValidationMiddleware::wrap($this->getApi(), self::$mandatoryAttributes),
481
            'input_validation_middleware'
482
        );
1441 ariadna 483
        $stack->appendSign(ExpiresParsingMiddleware::wrap(), 's3.expires_parsing');
1 efrain 484
        $stack->appendSign(PutObjectUrlMiddleware::wrap(), 's3.put_object_url');
485
        $stack->appendSign(PermanentRedirectMiddleware::wrap(), 's3.permanent_redirect');
486
        $stack->appendInit(Middleware::sourceFile($this->getApi()), 's3.source_file');
487
        $stack->appendInit($this->getSaveAsParameter(), 's3.save_as');
488
        $stack->appendInit($this->getLocationConstraintMiddleware(), 's3.location');
489
        $stack->appendInit($this->getEncodingTypeMiddleware(), 's3.auto_encode');
490
        $stack->appendInit($this->getHeadObjectMiddleware(), 's3.head_object');
1441 ariadna 491
        $this->processModel($this->isUseEndpointV2());
1 efrain 492
        if ($this->isUseEndpointV2()) {
1441 ariadna 493
            $stack->after('builder',
1 efrain 494
                's3.check_empty_path_with_query',
495
                $this->getEmptyPathWithQuery());
496
        }
497
    }
498
 
499
    /**
500
     * Determine if a string is a valid name for a DNS compatible Amazon S3
501
     * bucket.
502
     *
503
     * DNS compatible bucket names can be used as a subdomain in a URL (e.g.,
504
     * "<bucket>.s3.amazonaws.com").
505
     *
506
     * @param string $bucket Bucket name to check.
507
     *
508
     * @return bool
509
     */
510
    public static function isBucketDnsCompatible($bucket)
511
    {
512
        if (!is_string($bucket)) {
513
            return false;
514
        }
515
        $bucketLen = strlen($bucket);
516
 
517
        return ($bucketLen >= 3 && $bucketLen <= 63) &&
518
            // Cannot look like an IP address
519
            !filter_var($bucket, FILTER_VALIDATE_IP) &&
520
            preg_match('/^[a-z0-9]([a-z0-9\-\.]*[a-z0-9])?$/', $bucket);
521
    }
522
 
523
    public static function _apply_use_arn_region($value, array &$args, HandlerList $list)
524
    {
525
        if ($value instanceof CacheInterface) {
526
            $value = UseArnRegionConfigurationProvider::defaultProvider($args);
527
        }
528
        if (is_callable($value)) {
529
            $value = $value();
530
        }
531
        if ($value instanceof PromiseInterface) {
532
            $value = $value->wait();
533
        }
534
        if ($value instanceof ConfigurationInterface) {
535
            $args['use_arn_region'] = $value;
536
        } else {
537
            // The Configuration class itself will validate other inputs
538
            $args['use_arn_region'] = new Configuration($value);
539
        }
540
    }
541
 
1441 ariadna 542
    public static function _default_request_checksum_calculation(array $args): string
543
    {
544
        return ConfigurationResolver::resolve(
545
            'request_checksum_calculation',
546
            ApplyChecksumMiddleware::DEFAULT_CALCULATION_MODE,
547
            'string',
548
            $args
549
        );
550
    }
551
 
552
    public static function _apply_request_checksum_calculation(
553
        string $value,
554
        array &$args
555
    ): void
556
    {
557
        $value = strtolower($value);
558
        if (array_key_exists($value, self::$checksumOptionEnum)) {
559
            $args['request_checksum_calculation'] = $value;
560
        } else {
561
            $validValues = implode(' | ', array_keys(self::$checksumOptionEnum));
562
            throw new \InvalidArgumentException(
563
                'invalid value provided for `request_checksum_calculation`.'
564
                . ' valid values are: ' . $validValues . '.'
565
            );
566
        }
567
    }
568
 
569
    public static function _default_response_checksum_validation(array $args): string
570
    {
571
        return ConfigurationResolver::resolve(
572
            'response_checksum_validation',
573
            ValidateResponseChecksumResultMutator::DEFAULT_VALIDATION_MODE,
574
            'string',
575
            $args
576
        );
577
    }
578
 
579
    public static function _apply_response_checksum_validation(
580
        $value,
581
        array &$args
582
    ): void
583
    {
584
        $value = strtolower($value);
585
        if (array_key_exists($value, self::$checksumOptionEnum)) {
586
            $args['response_checksum_validation'] = $value;
587
        } else {
588
            $validValues = implode(' | ', array_keys(self::$checksumOptionEnum));
589
            throw new \InvalidArgumentException(
590
                'invalid value provided for `response_checksum_validation`.'
591
                . ' valid values are: ' . $validValues . '.'
592
            );
593
        }
594
    }
595
 
596
    public static function _default_disable_express_session_auth(array &$args)
597
    {
598
        return ConfigurationResolver::resolve(
599
            's3_disable_express_session_auth',
600
            false,
601
            'bool',
602
            $args
603
        );
604
    }
605
 
606
    public static function _default_s3_express_identity_provider(array $args)
607
    {
608
        if ($args['config']['disable_express_session_auth']) {
609
            return false;
610
        }
611
        return new S3ExpressIdentityProvider($args['region']);
612
    }
613
 
1 efrain 614
    public function createPresignedRequest(CommandInterface $command, $expires, array $options = [])
615
    {
616
        $command = clone $command;
1441 ariadna 617
        $list = $command->getHandlerList();
618
        $list->remove('signer');
619
 
620
        //Removes checksum calculation behavior by default
621
        if (empty($command['ChecksumAlgorithm'])
622
            && empty($command['AddContentMD5'])
623
        ) {
624
            $list->remove('s3.checksum');
625
        }
626
 
1 efrain 627
        $request = \Aws\serialize($command);
628
 
1441 ariadna 629
        //Applies ContentSHA256 parameter, if provided and not applied
630
        // by middleware
631
        $commandName = $command->getName();
632
        if (!empty($command['ContentSHA256']
633
            && isset(ApplyChecksumMiddleware::$sha256[$commandName])
634
            && !$request->hasHeader('X-Amz-Content-Sha256')
635
        )) {
636
            $request = $request->withHeader(
637
                'X-Amz-Content-Sha256',
638
                $command['ContentSHA256']
639
            );
640
        }
641
 
642
        $signing_name = $command['@context']['signing_service']
643
            ?? $this->getSigningName($request->getUri()->getHost());
644
        $signature_version = $this->getSignatureVersionFromCommand($command);
645
 
1 efrain 646
        /** @var \Aws\Signature\SignatureInterface $signer */
647
        $signer = call_user_func(
648
            $this->getSignatureProvider(),
649
            $signature_version,
650
            $signing_name,
651
            $this->getConfig('signing_region')
652
        );
1441 ariadna 653
        if ($signature_version == 'v4-s3express') {
654
            $provider = $this->getConfig('s3_express_identity_provider');
655
            $credentials = $provider($command)->wait();
656
        } else {
657
            $credentials = $this->getCredentials()->wait();
658
        }
1 efrain 659
        return $signer->presign(
660
            $request,
1441 ariadna 661
            $credentials,
1 efrain 662
            $expires,
663
            $options
664
        );
665
    }
666
 
667
    /**
668
     * Returns the URL to an object identified by its bucket and key.
669
     *
670
     * The URL returned by this method is not signed nor does it ensure that the
671
     * bucket and key given to the method exist. If you need a signed URL, then
672
     * use the {@see \Aws\S3\S3Client::createPresignedRequest} method and get
673
     * the URI of the signed request.
674
     *
675
     * @param string $bucket  The name of the bucket where the object is located
676
     * @param string $key     The key of the object
677
     *
678
     * @return string The URL to the object
679
     */
680
    public function getObjectUrl($bucket, $key)
681
    {
682
        $command = $this->getCommand('GetObject', [
683
            'Bucket' => $bucket,
684
            'Key'    => $key
685
        ]);
686
 
687
        return (string) \Aws\serialize($command)->getUri();
688
    }
689
 
690
    /**
691
     * Raw URL encode a key and allow for '/' characters
692
     *
693
     * @param string $key Key to encode
694
     *
695
     * @return string Returns the encoded key
696
     */
697
    public static function encodeKey($key)
698
    {
699
        return str_replace('%2F', '/', rawurlencode($key));
700
    }
701
 
702
    /**
703
     * Provides a middleware that removes the need to specify LocationConstraint on CreateBucket.
704
     *
705
     * @return \Closure
706
     */
707
    private function getLocationConstraintMiddleware()
708
    {
709
        $region = $this->getRegion();
710
        return static function (callable $handler) use ($region) {
711
            return function (Command $command, $request = null) use ($handler, $region) {
1441 ariadna 712
                if ($command->getName() === 'CreateBucket'
713
                    && !self::isDirectoryBucket($command['Bucket'])
714
                ) {
715
                    $locationConstraint = $command['CreateBucketConfiguration']['LocationConstraint']
716
                        ?? null;
1 efrain 717
 
718
                    if ($locationConstraint === 'us-east-1') {
719
                        unset($command['CreateBucketConfiguration']);
720
                    } elseif ('us-east-1' !== $region && empty($locationConstraint)) {
1441 ariadna 721
                        if (isset($command['CreateBucketConfiguration'])) {
722
                            $command['CreateBucketConfiguration']['LocationConstraint'] = $region;
723
                        } else {
724
                            $command['CreateBucketConfiguration'] = ['LocationConstraint' => $region];
725
                        }
1 efrain 726
                    }
727
                }
728
 
729
                return $handler($command, $request);
730
            };
731
        };
732
    }
733
 
734
    /**
735
     * Provides a middleware that supports the `SaveAs` parameter.
736
     *
737
     * @return \Closure
738
     */
739
    private function getSaveAsParameter()
740
    {
741
        return static function (callable $handler) {
742
            return function (Command $command, $request = null) use ($handler) {
743
                if ($command->getName() === 'GetObject' && isset($command['SaveAs'])) {
744
                    $command['@http']['sink'] = $command['SaveAs'];
745
                    unset($command['SaveAs']);
746
                }
747
 
748
                return $handler($command, $request);
749
            };
750
        };
751
    }
752
 
753
    /**
754
     * Provides a middleware that disables content decoding on HeadObject
755
     * commands.
756
     *
757
     * @return \Closure
758
     */
759
    private function getHeadObjectMiddleware()
760
    {
761
        return static function (callable $handler) {
762
            return function (
763
                CommandInterface $command,
1441 ariadna 764
                ?RequestInterface $request = null
1 efrain 765
            ) use ($handler) {
766
                if ($command->getName() === 'HeadObject'
767
                    && !isset($command['@http']['decode_content'])
768
                ) {
769
                    $command['@http']['decode_content'] = false;
770
                }
771
 
772
                return $handler($command, $request);
773
            };
774
        };
775
    }
776
 
777
    /**
778
     * Provides a middleware that autopopulates the EncodingType parameter on
779
     * ListObjects commands.
780
     *
781
     * @return \Closure
782
     */
783
    private function getEncodingTypeMiddleware()
784
    {
785
        return static function (callable $handler) {
786
            return function (Command $command, $request = null) use ($handler) {
787
                $autoSet = false;
788
                if ($command->getName() === 'ListObjects'
789
                    && empty($command['EncodingType'])
790
                ) {
791
                    $command['EncodingType'] = 'url';
792
                    $autoSet = true;
793
                }
794
 
795
                return $handler($command, $request)
796
                    ->then(function (ResultInterface $result) use ($autoSet) {
797
                        if ($result['EncodingType'] === 'url' && $autoSet) {
798
                            static $topLevel = [
799
                                'Delimiter',
800
                                'Marker',
801
                                'NextMarker',
802
                                'Prefix',
803
                            ];
804
                            static $nested = [
805
                                ['Contents', 'Key'],
806
                                ['CommonPrefixes', 'Prefix'],
807
                            ];
808
 
809
                            foreach ($topLevel as $key) {
810
                                if (isset($result[$key])) {
811
                                    $result[$key] = urldecode($result[$key]);
812
                                }
813
                            }
814
                            foreach ($nested as $steps) {
815
                                if (isset($result[$steps[0]])) {
816
                                    foreach ($result[$steps[0]] as $key => $part) {
817
                                        if (isset($part[$steps[1]])) {
818
                                            $result[$steps[0]][$key][$steps[1]]
819
                                                = urldecode($part[$steps[1]]);
820
                                        }
821
                                    }
822
                                }
823
                            }
824
 
825
                        }
826
 
827
                        return $result;
828
                    });
829
            };
830
        };
831
    }
832
 
833
    /**
834
     * Provides a middleware that checks for an empty path and a
835
     * non-empty query string.
836
     *
837
     * @return \Closure
838
     */
839
    private function getEmptyPathWithQuery()
840
    {
841
        return static function (callable $handler) {
842
            return function (Command $command, RequestInterface $request) use ($handler) {
843
                $uri = $request->getUri();
844
                if (empty($uri->getPath()) && !empty($uri->getQuery())) {
845
                    $uri = $uri->withPath('/');
846
                    $request = $request->withUri($uri);
847
                }
848
 
849
                return $handler($command, $request);
850
            };
851
        };
852
    }
853
 
854
    /**
1441 ariadna 855
     * Provides a middleware that disables express session auth when
856
     * customers opt out of it.
857
     *
858
     * @return \Closure
859
     */
860
    private function getDisableExpressSessionAuthMiddleware()
861
    {
862
        return function (callable $handler) {
863
            return function (
864
                CommandInterface $command,
865
                ?RequestInterface $request = null
866
            ) use ($handler) {
867
                if (!empty($command['@context']['signature_version'])
868
                    && $command['@context']['signature_version'] === 'v4-s3express'
869
                ) {
870
                    $command['@context']['signature_version'] = 's3v4';
871
                }
872
                return $handler($command, $request);
873
            };
874
        };
875
    }
876
 
877
    /**
1 efrain 878
     * Special handling for when the service name is s3-object-lambda.
879
     * So, if the host contains s3-object-lambda, then the service name
880
     * returned is s3-object-lambda, otherwise the default signing service is returned.
881
     * @param string $host The host to validate if is a s3-object-lambda URL.
882
     * @return string returns the signing service name to be used
883
     */
884
    private function getSigningName($host)
885
    {
886
        if (strpos( $host, 's3-object-lambda')) {
887
            return 's3-object-lambda';
888
        }
889
 
890
        return $this->getConfig('signing_name');
891
    }
892
 
893
    /**
1441 ariadna 894
     * If EndpointProviderV2 is used, removes `Bucket` from request URIs.
1 efrain 895
     * This is now handled by the endpoint ruleset.
896
     *
1441 ariadna 897
     * Additionally adds a synthetic shape `ExpiresString` and modifies
898
     * `Expires` type to ensure it remains set to `timestamp`.
899
     *
900
     * @param array $args
1 efrain 901
     * @return void
902
     *
903
     * @internal
904
     */
1441 ariadna 905
    private function processModel(bool $isUseEndpointV2): void
1 efrain 906
    {
907
        $definition = $this->getApi()->getDefinition();
908
 
1441 ariadna 909
        if ($isUseEndpointV2) {
910
            foreach($definition['operations'] as &$operation) {
911
                if (isset($operation['http']['requestUri'])) {
912
                    $requestUri = $operation['http']['requestUri'];
913
                    if ($requestUri === "/{Bucket}") {
914
                        $requestUri = str_replace('/{Bucket}', '/', $requestUri);
915
                    } else {
916
                        $requestUri = str_replace('/{Bucket}', '', $requestUri);
917
                    }
918
                    $operation['http']['requestUri'] = $requestUri;
1 efrain 919
                }
920
            }
921
        }
1441 ariadna 922
 
923
        foreach ($definition['shapes'] as $key => &$value) {
924
            $suffix = 'Output';
925
            if (substr($key, -strlen($suffix)) === $suffix) {
926
                if (isset($value['members']['Expires'])) {
927
                    $value['members']['Expires']['deprecated'] = true;
928
                    $value['members']['ExpiresString'] = [
929
                        'shape' => 'ExpiresString',
930
                        'location' => 'header',
931
                        'locationName' => 'Expires'
932
                    ];
933
                }
934
            }
935
        }
936
        $definition['shapes']['ExpiresString']['type'] = 'string';
937
        $definition['shapes']['Expires']['type'] = 'timestamp';
938
 
1 efrain 939
        $this->getApi()->setDefinition($definition);
940
    }
941
 
942
    /**
943
     * Adds service-specific client built-in values
944
     *
945
     * @return void
946
     */
947
    private function addBuiltIns($args)
948
    {
1441 ariadna 949
        if (isset($args['region'])
950
            && $args['region'] !== 'us-east-1'
951
        ) {
1 efrain 952
            return false;
953
        }
1441 ariadna 954
 
955
        if (!isset($args['region'])
956
            && ConfigurationResolver::resolve('region', '', 'string') !== 'us-east-1'
957
        ) {
958
            return false;
959
        }
960
 
1 efrain 961
        $key = 'AWS::S3::UseGlobalEndpoint';
962
        $result = $args['s3_us_east_1_regional_endpoint'] instanceof \Closure ?
963
            $args['s3_us_east_1_regional_endpoint']()->wait() : $args['s3_us_east_1_regional_endpoint'];
964
 
965
        if (is_string($result)) {
966
            if ($result === 'regional') {
967
                $value = false;
968
            } else if ($result === 'legacy') {
969
                $value = true;
970
            } else {
971
                return;
972
            }
973
        } else {
974
            if ($result->isFallback()
975
                || $result->getEndpointsType() === 'legacy'
976
            ) {
977
                $value = true;
978
            } else {
979
                $value = false;
980
            }
981
        }
982
        $this->clientBuiltIns[$key] = $value;
983
    }
984
 
1441 ariadna 985
    /**
986
     * Determines whether a bucket is a directory bucket.
987
     * Only considers the availability zone/suffix format
988
     *
989
     * @param string $bucket
990
     * @return bool
991
     */
992
    public static function isDirectoryBucket(string $bucket): bool
993
    {
994
        return preg_match(self::DIRECTORY_BUCKET_REGEX, $bucket) === 1;
995
    }
996
 
1 efrain 997
    /** @internal */
998
    public static function _applyRetryConfig($value, $args, HandlerList $list)
999
    {
1000
        if ($value) {
1001
            $config = \Aws\Retry\ConfigurationProvider::unwrap($value);
1002
 
1003
            if ($config->getMode() === 'legacy') {
1004
                $maxRetries = $config->getMaxAttempts() - 1;
1005
                $decider = RetryMiddleware::createDefaultDecider($maxRetries);
1006
                $decider = function ($retries, $command, $request, $result, $error) use ($decider, $maxRetries) {
1441 ariadna 1007
                    $maxRetries = $command['@retries'] ?? $maxRetries;
1 efrain 1008
 
1009
                    if ($decider($retries, $command, $request, $result, $error)) {
1010
                        return true;
1011
                    }
1012
 
1013
                    if ($error instanceof AwsException
1014
                        && $retries < $maxRetries
1015
                    ) {
1016
                        if ($error->getResponse()
1017
                            && $error->getResponse()->getStatusCode() >= 400
1018
                        ) {
1019
                            return strpos(
1020
                                    $error->getResponse()->getBody(),
1021
                                    'Your socket connection to the server'
1022
                                ) !== false;
1023
                        }
1024
 
1025
                        if ($error->getPrevious() instanceof RequestException) {
1026
                            // All commands except CompleteMultipartUpload are
1027
                            // idempotent and may be retried without worry if a
1028
                            // networking error has occurred.
1029
                            return $command->getName() !== 'CompleteMultipartUpload';
1030
                        }
1031
                    }
1032
 
1033
                    return false;
1034
                };
1035
 
1036
                $delay = [RetryMiddleware::class, 'exponentialDelay'];
1037
                $list->appendSign(Middleware::retry($decider, $delay), 'retry');
1038
            } else {
1039
                $defaultDecider = RetryMiddlewareV2::createDefaultDecider(
1040
                    new QuotaManager(),
1041
                    $config->getMaxAttempts()
1042
                );
1043
 
1044
                $list->appendSign(
1045
                    RetryMiddlewareV2::wrap(
1046
                        $config,
1047
                        [
1048
                            'collect_stats' => $args['stats']['retries'],
1049
                            'decider' => function(
1050
                                $attempts,
1051
                                CommandInterface $cmd,
1052
                                $result
1053
                            ) use ($defaultDecider, $config) {
1054
                                $isRetryable = $defaultDecider($attempts, $cmd, $result);
1055
                                if (!$isRetryable
1056
                                    && $result instanceof AwsException
1057
                                    && $attempts < $config->getMaxAttempts()
1058
                                ) {
1059
                                    if (!empty($result->getResponse())
1060
                                        && $result->getResponse()->getStatusCode() >= 400
1061
                                    ) {
1062
                                        return strpos(
1063
                                                $result->getResponse()->getBody(),
1064
                                                'Your socket connection to the server'
1065
                                            ) !== false;
1066
                                    }
1067
 
1068
                                    if ($result->getPrevious() instanceof RequestException
1069
                                        && $cmd->getName() !== 'CompleteMultipartUpload'
1070
                                    ) {
1071
                                        $isRetryable = true;
1072
                                    }
1073
                                }
1074
 
1075
                                return $isRetryable;
1076
                            }
1077
                        ]
1078
                    ),
1079
                    'retry'
1080
                );
1081
            }
1082
        }
1083
    }
1084
 
1085
    /** @internal */
1086
    public static function _applyApiProvider($value, array &$args, HandlerList $list)
1087
    {
1088
        ClientResolver::_apply_api_provider($value, $args);
1441 ariadna 1089
        $s3Parser = new S3Parser(
1090
            $args['parser'],
1091
            $args['error_parser'],
1092
            $args['api'],
1093
            $args['exception_class']
1094
        );
1095
        $s3Parser->addS3ResultMutator(
1096
            'get-bucket-location',
1097
            new GetBucketLocationResultMutator()
1098
        );
1099
        $s3Parser->addS3ResultMutator(
1100
            'validate-response-checksum',
1101
            new ValidateResponseChecksumResultMutator(
1102
                $args['api'],
1103
                ['response_checksum_validation' => $args['response_checksum_validation']]
1 efrain 1104
            )
1105
        );
1441 ariadna 1106
        $args['parser'] = $s3Parser;
1 efrain 1107
    }
1108
 
1109
    /**
1110
     * @internal
1111
     * @codeCoverageIgnore
1112
     */
1113
    public static function applyDocFilters(array $api, array $docs)
1114
    {
1115
        $b64 = '<div class="alert alert-info">This value will be base64 encoded on your behalf.</div>';
1116
        $opt = '<div class="alert alert-info">This value will be computed for you it is not supplied.</div>';
1117
 
1118
        // Add a note on the CopyObject docs
1119
         $s3ExceptionRetryMessage = "<p>Additional info on response behavior: if there is"
1120
            . " an internal error in S3 after the request was successfully recieved,"
1121
            . " a 200 response will be returned with an <code>S3Exception</code> embedded"
1122
            . " in it; this will still be caught and retried by"
1123
            . " <code>RetryMiddleware.</code></p>";
1124
 
1125
        $docs['operations']['CopyObject'] .=  $s3ExceptionRetryMessage;
1126
        $docs['operations']['CompleteMultipartUpload'] .=  $s3ExceptionRetryMessage;
1127
        $docs['operations']['UploadPartCopy'] .=  $s3ExceptionRetryMessage;
1128
        $docs['operations']['UploadPart'] .=  $s3ExceptionRetryMessage;
1129
 
1130
        // Add note about stream ownership in the putObject call
1131
        $guzzleStreamMessage = "<p>Additional info on behavior of the stream"
1132
            . " parameters: Psr7 takes ownership of streams and will automatically close"
1133
            . " streams when this method is called with a stream as the <code>Body</code>"
1134
            . " parameter.  To prevent this, set the <code>Body</code> using"
1135
            . " <code>GuzzleHttp\Psr7\stream_for</code> method with a is an instance of"
1136
            . " <code>Psr\Http\Message\StreamInterface</code>, and it will be returned"
1137
            . " unmodified. This will allow you to keep the stream in scope. </p>";
1138
        $docs['operations']['PutObject'] .=  $guzzleStreamMessage;
1139
 
1140
        // Add the SourceFile parameter.
1141
        $docs['shapes']['SourceFile']['base'] = 'The path to a file on disk to use instead of the Body parameter.';
1142
        $api['shapes']['SourceFile'] = ['type' => 'string'];
1143
        $api['shapes']['PutObjectRequest']['members']['SourceFile'] = ['shape' => 'SourceFile'];
1144
        $api['shapes']['UploadPartRequest']['members']['SourceFile'] = ['shape' => 'SourceFile'];
1145
 
1146
        // Add the ContentSHA256 parameter.
1147
        $docs['shapes']['ContentSHA256']['base'] = 'A SHA256 hash of the body content of the request.';
1148
        $api['shapes']['ContentSHA256'] = ['type' => 'string'];
1149
        $api['shapes']['PutObjectRequest']['members']['ContentSHA256'] = ['shape' => 'ContentSHA256'];
1150
        $api['shapes']['UploadPartRequest']['members']['ContentSHA256'] = ['shape' => 'ContentSHA256'];
1151
        $docs['shapes']['ContentSHA256']['append'] = $opt;
1152
 
1153
        // Add the AddContentMD5 parameter.
1154
        $docs['shapes']['AddContentMD5']['base'] = 'Set to true to calculate the ContentMD5 for the upload.';
1155
        $api['shapes']['AddContentMD5'] = ['type' => 'boolean'];
1156
        $api['shapes']['PutObjectRequest']['members']['AddContentMD5'] = ['shape' => 'AddContentMD5'];
1157
        $api['shapes']['UploadPartRequest']['members']['AddContentMD5'] = ['shape' => 'AddContentMD5'];
1158
 
1159
        // Add the SaveAs parameter.
1160
        $docs['shapes']['SaveAs']['base'] = 'The path to a file on disk to save the object data.';
1161
        $api['shapes']['SaveAs'] = ['type' => 'string'];
1162
        $api['shapes']['GetObjectRequest']['members']['SaveAs'] = ['shape' => 'SaveAs'];
1163
 
1164
        // Several SSECustomerKey documentation updates.
1165
        $docs['shapes']['SSECustomerKey']['append'] = $b64;
1166
        $docs['shapes']['CopySourceSSECustomerKey']['append'] = $b64;
1167
        $docs['shapes']['SSECustomerKeyMd5']['append'] = $opt;
1168
 
1169
        // Add the ObjectURL to various output shapes and documentation.
1170
        $docs['shapes']['ObjectURL']['base'] = 'The URI of the created object.';
1171
        $api['shapes']['ObjectURL'] = ['type' => 'string'];
1172
        $api['shapes']['PutObjectOutput']['members']['ObjectURL'] = ['shape' => 'ObjectURL'];
1173
        $api['shapes']['CopyObjectOutput']['members']['ObjectURL'] = ['shape' => 'ObjectURL'];
1174
        $api['shapes']['CompleteMultipartUploadOutput']['members']['ObjectURL'] = ['shape' => 'ObjectURL'];
1175
 
1176
        // Fix references to Location Constraint.
1177
        unset($api['shapes']['CreateBucketRequest']['payload']);
1178
        $api['shapes']['BucketLocationConstraint']['enum'] = [
1179
            "ap-northeast-1",
1180
            "ap-southeast-2",
1181
            "ap-southeast-1",
1182
            "cn-north-1",
1183
            "eu-central-1",
1184
            "eu-west-1",
1185
            "us-east-1",
1186
            "us-west-1",
1187
            "us-west-2",
1188
            "sa-east-1",
1189
        ];
1190
 
1191
        // Add a note that the ContentMD5 is automatically computed, except for with PutObject and UploadPart
1192
        $docs['shapes']['ContentMD5']['append'] = '<div class="alert alert-info">The value will be computed on '
1193
            . 'your behalf.</div>';
1194
        $docs['shapes']['ContentMD5']['excludeAppend'] = ['PutObjectRequest', 'UploadPartRequest'];
1195
 
1196
        //Add a note to ContentMD5 for PutObject and UploadPart that specifies the value is required
1197
        // When uploading to a bucket with object lock enabled and that it is not computed automatically
1198
        $objectLock = '<div class="alert alert-info">This value is required if uploading to a bucket '
1199
            . 'which has Object Lock enabled. It will not be calculated for you automatically. If you wish to have '
1200
            . 'the value calculated for you, use the `AddContentMD5` parameter.</div>';
1201
        $docs['shapes']['ContentMD5']['appendOnly'] = [
1202
            'message' => $objectLock,
1203
            'shapes' => ['PutObjectRequest', 'UploadPartRequest']
1204
        ];
1205
 
1441 ariadna 1206
        // Add `ExpiresString` shape to output structures which contain `Expires`
1207
        // Deprecate existing `Expires` shapes in output structures
1208
        // Add/Update documentation for both `ExpiresString` and `Expires`
1209
        // Ensure `Expires` type remains timestamp
1210
        foreach ($api['shapes'] as $key => &$value) {
1211
            $suffix = 'Output';
1212
            if (substr($key, -strlen($suffix)) === $suffix) {
1213
                if (isset($value['members']['Expires'])) {
1214
                    $value['members']['Expires']['deprecated'] = true;
1215
                    $value['members']['ExpiresString'] = [
1216
                        'shape' => 'ExpiresString',
1217
                        'location' => 'header',
1218
                        'locationName' => 'Expires'
1219
                    ];
1220
                    $docs['shapes']['Expires']['refs'][$key . '$Expires']
1221
                        .= '<p>This output shape has been deprecated. Please refer to <code>ExpiresString</code> instead.</p>.';
1222
                }
1223
            }
1224
        }
1225
        $api['shapes']['ExpiresString']['type'] = 'string';
1226
        $docs['shapes']['ExpiresString']['base'] = 'The unparsed string value of the <code>Expires</code> output member.';
1227
        $api['shapes']['Expires']['type'] = 'timestamp';
1228
 
1 efrain 1229
        return [
1230
            new Service($api, ApiProvider::defaultProvider()),
1231
            new DocModel($docs)
1232
        ];
1233
    }
1234
 
1235
    /**
1236
     * @internal
1237
     * @codeCoverageIgnore
1238
     */
1239
    public static function addDocExamples($examples)
1240
    {
1241
        $getObjectExample = [
1242
            'input' => [
1243
                'Bucket' => 'arn:aws:s3:us-east-1:123456789012:accesspoint:myaccesspoint',
1244
                'Key' => 'my-key'
1245
            ],
1246
            'output' => [
1247
                'Body' => 'class GuzzleHttp\Psr7\Stream#208 (7) {...}',
1248
                'ContentLength' => '11',
1249
                'ContentType' => 'application/octet-stream',
1250
            ],
1251
            'comments' => [
1252
                'input' => '',
1253
                'output' => 'Simplified example output'
1254
            ],
1255
            'description' => 'The following example retrieves an object by referencing the bucket via an S3 accesss point ARN. Result output is simplified for the example.',
1256
            'id' => '',
1257
            'title' => 'To get an object via an S3 access point ARN'
1258
        ];
1259
        if (isset($examples['GetObject'])) {
1260
            $examples['GetObject'] []= $getObjectExample;
1261
        } else {
1262
            $examples['GetObject'] = [$getObjectExample];
1263
        }
1264
 
1265
        $putObjectExample = [
1266
            'input' => [
1267
                'Bucket' => 'arn:aws:s3:us-east-1:123456789012:accesspoint:myaccesspoint',
1268
                'Key' => 'my-key',
1269
                'Body' => 'my-body',
1270
            ],
1271
            'output' => [
1272
                'ObjectURL' => 'https://my-bucket.s3.us-east-1.amazonaws.com/my-key'
1273
            ],
1274
            'comments' => [
1275
                'input' => '',
1276
                'output' => 'Simplified example output'
1277
            ],
1278
            'description' => 'The following example uploads an object by referencing the bucket via an S3 accesss point ARN. Result output is simplified for the example.',
1279
            'id' => '',
1280
            'title' => 'To upload an object via an S3 access point ARN'
1281
        ];
1282
        if (isset($examples['PutObject'])) {
1283
            $examples['PutObject'] []= $putObjectExample;
1284
        } else {
1285
            $examples['PutObject'] = [$putObjectExample];
1286
        }
1287
 
1288
        return $examples;
1289
    }
1441 ariadna 1290
 
1291
    /**
1292
     * @param CommandInterface $command
1293
     * @return array|mixed|null
1294
     */
1295
    private function getSignatureVersionFromCommand(CommandInterface $command)
1296
    {
1297
        return $command['@context']['signature_version']
1298
            ?? $this->getConfig('signature_version');
1299
    }
1 efrain 1300
}