<?php
namespace Aws\Route53Domains;

use Aws\AwsClient;

/**
 * This client is used to interact with the **Amazon Route 53 Domains** service.
 *
 * @method \Aws\Result acceptDomainTransferFromAnotherAwsAccount(array $args = [])
 * @method \GuzzleHttp\Promise\Promise acceptDomainTransferFromAnotherAwsAccountAsync(array $args = [])
 * @method \Aws\Result associateDelegationSignerToDomain(array $args = [])
 * @method \GuzzleHttp\Promise\Promise associateDelegationSignerToDomainAsync(array $args = [])
 * @method \Aws\Result cancelDomainTransferToAnotherAwsAccount(array $args = [])
 * @method \GuzzleHttp\Promise\Promise cancelDomainTransferToAnotherAwsAccountAsync(array $args = [])
 * @method \Aws\Result checkDomainAvailability(array $args = [])
 * @method \GuzzleHttp\Promise\Promise checkDomainAvailabilityAsync(array $args = [])
 * @method \Aws\Result checkDomainTransferability(array $args = [])
 * @method \GuzzleHttp\Promise\Promise checkDomainTransferabilityAsync(array $args = [])
 * @method \Aws\Result deleteDomain(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteDomainAsync(array $args = [])
 * @method \Aws\Result deleteTagsForDomain(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteTagsForDomainAsync(array $args = [])
 * @method \Aws\Result disableDomainAutoRenew(array $args = [])
 * @method \GuzzleHttp\Promise\Promise disableDomainAutoRenewAsync(array $args = [])
 * @method \Aws\Result disableDomainTransferLock(array $args = [])
 * @method \GuzzleHttp\Promise\Promise disableDomainTransferLockAsync(array $args = [])
 * @method \Aws\Result disassociateDelegationSignerFromDomain(array $args = [])
 * @method \GuzzleHttp\Promise\Promise disassociateDelegationSignerFromDomainAsync(array $args = [])
 * @method \Aws\Result enableDomainAutoRenew(array $args = [])
 * @method \GuzzleHttp\Promise\Promise enableDomainAutoRenewAsync(array $args = [])
 * @method \Aws\Result enableDomainTransferLock(array $args = [])
 * @method \GuzzleHttp\Promise\Promise enableDomainTransferLockAsync(array $args = [])
 * @method \Aws\Result getContactReachabilityStatus(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getContactReachabilityStatusAsync(array $args = [])
 * @method \Aws\Result getDomainDetail(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getDomainDetailAsync(array $args = [])
 * @method \Aws\Result getDomainSuggestions(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getDomainSuggestionsAsync(array $args = [])
 * @method \Aws\Result getOperationDetail(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getOperationDetailAsync(array $args = [])
 * @method \Aws\Result listDomains(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listDomainsAsync(array $args = [])
 * @method \Aws\Result listOperations(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listOperationsAsync(array $args = [])
 * @method \Aws\Result listPrices(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listPricesAsync(array $args = [])
 * @method \Aws\Result listTagsForDomain(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listTagsForDomainAsync(array $args = [])
 * @method \Aws\Result pushDomain(array $args = [])
 * @method \GuzzleHttp\Promise\Promise pushDomainAsync(array $args = [])
 * @method \Aws\Result registerDomain(array $args = [])
 * @method \GuzzleHttp\Promise\Promise registerDomainAsync(array $args = [])
 * @method \Aws\Result rejectDomainTransferFromAnotherAwsAccount(array $args = [])
 * @method \GuzzleHttp\Promise\Promise rejectDomainTransferFromAnotherAwsAccountAsync(array $args = [])
 * @method \Aws\Result renewDomain(array $args = [])
 * @method \GuzzleHttp\Promise\Promise renewDomainAsync(array $args = [])
 * @method \Aws\Result resendContactReachabilityEmail(array $args = [])
 * @method \GuzzleHttp\Promise\Promise resendContactReachabilityEmailAsync(array $args = [])
 * @method \Aws\Result resendOperationAuthorization(array $args = [])
 * @method \GuzzleHttp\Promise\Promise resendOperationAuthorizationAsync(array $args = [])
 * @method \Aws\Result retrieveDomainAuthCode(array $args = [])
 * @method \GuzzleHttp\Promise\Promise retrieveDomainAuthCodeAsync(array $args = [])
 * @method \Aws\Result transferDomain(array $args = [])
 * @method \GuzzleHttp\Promise\Promise transferDomainAsync(array $args = [])
 * @method \Aws\Result transferDomainToAnotherAwsAccount(array $args = [])
 * @method \GuzzleHttp\Promise\Promise transferDomainToAnotherAwsAccountAsync(array $args = [])
 * @method \Aws\Result updateDomainContact(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateDomainContactAsync(array $args = [])
 * @method \Aws\Result updateDomainContactPrivacy(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateDomainContactPrivacyAsync(array $args = [])
 * @method \Aws\Result updateDomainNameservers(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateDomainNameserversAsync(array $args = [])
 * @method \Aws\Result updateTagsForDomain(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateTagsForDomainAsync(array $args = [])
 * @method \Aws\Result viewBilling(array $args = [])
 * @method \GuzzleHttp\Promise\Promise viewBillingAsync(array $args = [])
 */
class Route53DomainsClient extends AwsClient {}


<?php
namespace Aws\Route53Domains\Exception;

use Aws\Exception\AwsException;

/**
 * Represents an error interacting with the Amazon Route 53 Domains service.
 */
class Route53DomainsException extends AwsException {}


<?php
namespace Aws\Route53RecoveryCluster;

use Aws\AwsClient;

/**
 * This client is used to interact with the **Route53 Recovery Cluster** service.
 * @method \Aws\Result getRoutingControlState(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getRoutingControlStateAsync(array $args = [])
 * @method \Aws\Result listRoutingControls(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listRoutingControlsAsync(array $args = [])
 * @method \Aws\Result updateRoutingControlState(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateRoutingControlStateAsync(array $args = [])
 * @method \Aws\Result updateRoutingControlStates(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateRoutingControlStatesAsync(array $args = [])
 */
class Route53RecoveryClusterClient extends AwsClient {}


<?php
namespace Aws\Route53RecoveryCluster\Exception;

use Aws\Exception\AwsException;

/**
 * Represents an error interacting with the **Route53 Recovery Cluster** service.
 */
class Route53RecoveryClusterException extends AwsException {}


<?php
namespace Aws\Route53RecoveryControlConfig;

use Aws\AwsClient;

/**
 * This client is used to interact with the **AWS Route53 Recovery Control Config** service.
 * @method \Aws\Result createCluster(array $args = [])
 * @method \GuzzleHttp\Promise\Promise createClusterAsync(array $args = [])
 * @method \Aws\Result createControlPanel(array $args = [])
 * @method \GuzzleHttp\Promise\Promise createControlPanelAsync(array $args = [])
 * @method \Aws\Result createRoutingControl(array $args = [])
 * @method \GuzzleHttp\Promise\Promise createRoutingControlAsync(array $args = [])
 * @method \Aws\Result createSafetyRule(array $args = [])
 * @method \GuzzleHttp\Promise\Promise createSafetyRuleAsync(array $args = [])
 * @method \Aws\Result deleteCluster(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteClusterAsync(array $args = [])
 * @method \Aws\Result deleteControlPanel(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteControlPanelAsync(array $args = [])
 * @method \Aws\Result deleteRoutingControl(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteRoutingControlAsync(array $args = [])
 * @method \Aws\Result deleteSafetyRule(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteSafetyRuleAsync(array $args = [])
 * @method \Aws\Result describeCluster(array $args = [])
 * @method \GuzzleHttp\Promise\Promise describeClusterAsync(array $args = [])
 * @method \Aws\Result describeControlPanel(array $args = [])
 * @method \GuzzleHttp\Promise\Promise describeControlPanelAsync(array $args = [])
 * @method \Aws\Result describeRoutingControl(array $args = [])
 * @method \GuzzleHttp\Promise\Promise describeRoutingControlAsync(array $args = [])
 * @method \Aws\Result describeSafetyRule(array $args = [])
 * @method \GuzzleHttp\Promise\Promise describeSafetyRuleAsync(array $args = [])
 * @method \Aws\Result listAssociatedRoute53HealthChecks(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listAssociatedRoute53HealthChecksAsync(array $args = [])
 * @method \Aws\Result listClusters(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listClustersAsync(array $args = [])
 * @method \Aws\Result listControlPanels(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listControlPanelsAsync(array $args = [])
 * @method \Aws\Result listRoutingControls(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listRoutingControlsAsync(array $args = [])
 * @method \Aws\Result listSafetyRules(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listSafetyRulesAsync(array $args = [])
 * @method \Aws\Result listTagsForResource(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listTagsForResourceAsync(array $args = [])
 * @method \Aws\Result tagResource(array $args = [])
 * @method \GuzzleHttp\Promise\Promise tagResourceAsync(array $args = [])
 * @method \Aws\Result untagResource(array $args = [])
 * @method \GuzzleHttp\Promise\Promise untagResourceAsync(array $args = [])
 * @method \Aws\Result updateControlPanel(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateControlPanelAsync(array $args = [])
 * @method \Aws\Result updateRoutingControl(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateRoutingControlAsync(array $args = [])
 * @method \Aws\Result updateSafetyRule(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateSafetyRuleAsync(array $args = [])
 */
class Route53RecoveryControlConfigClient extends AwsClient {}


<?php
namespace Aws\Route53RecoveryControlConfig\Exception;

use Aws\Exception\AwsException;

/**
 * Represents an error interacting with the **AWS Route53 Recovery Control Config** service.
 */
class Route53RecoveryControlConfigException extends AwsException {}


<?php
namespace Aws\Route53RecoveryReadiness;

use Aws\AwsClient;

/**
 * This client is used to interact with the **AWS Route53 Recovery Readiness** service.
 * @method \Aws\Result createCell(array $args = [])
 * @method \GuzzleHttp\Promise\Promise createCellAsync(array $args = [])
 * @method \Aws\Result createCrossAccountAuthorization(array $args = [])
 * @method \GuzzleHttp\Promise\Promise createCrossAccountAuthorizationAsync(array $args = [])
 * @method \Aws\Result createReadinessCheck(array $args = [])
 * @method \GuzzleHttp\Promise\Promise createReadinessCheckAsync(array $args = [])
 * @method \Aws\Result createRecoveryGroup(array $args = [])
 * @method \GuzzleHttp\Promise\Promise createRecoveryGroupAsync(array $args = [])
 * @method \Aws\Result createResourceSet(array $args = [])
 * @method \GuzzleHttp\Promise\Promise createResourceSetAsync(array $args = [])
 * @method \Aws\Result deleteCell(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteCellAsync(array $args = [])
 * @method \Aws\Result deleteCrossAccountAuthorization(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteCrossAccountAuthorizationAsync(array $args = [])
 * @method \Aws\Result deleteReadinessCheck(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteReadinessCheckAsync(array $args = [])
 * @method \Aws\Result deleteRecoveryGroup(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteRecoveryGroupAsync(array $args = [])
 * @method \Aws\Result deleteResourceSet(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteResourceSetAsync(array $args = [])
 * @method \Aws\Result getArchitectureRecommendations(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getArchitectureRecommendationsAsync(array $args = [])
 * @method \Aws\Result getCell(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getCellAsync(array $args = [])
 * @method \Aws\Result getCellReadinessSummary(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getCellReadinessSummaryAsync(array $args = [])
 * @method \Aws\Result getReadinessCheck(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getReadinessCheckAsync(array $args = [])
 * @method \Aws\Result getReadinessCheckResourceStatus(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getReadinessCheckResourceStatusAsync(array $args = [])
 * @method \Aws\Result getReadinessCheckStatus(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getReadinessCheckStatusAsync(array $args = [])
 * @method \Aws\Result getRecoveryGroup(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getRecoveryGroupAsync(array $args = [])
 * @method \Aws\Result getRecoveryGroupReadinessSummary(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getRecoveryGroupReadinessSummaryAsync(array $args = [])
 * @method \Aws\Result getResourceSet(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getResourceSetAsync(array $args = [])
 * @method \Aws\Result listCells(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listCellsAsync(array $args = [])
 * @method \Aws\Result listCrossAccountAuthorizations(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listCrossAccountAuthorizationsAsync(array $args = [])
 * @method \Aws\Result listReadinessChecks(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listReadinessChecksAsync(array $args = [])
 * @method \Aws\Result listRecoveryGroups(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listRecoveryGroupsAsync(array $args = [])
 * @method \Aws\Result listResourceSets(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listResourceSetsAsync(array $args = [])
 * @method \Aws\Result listRules(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listRulesAsync(array $args = [])
 * @method \Aws\Result listTagsForResources(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listTagsForResourcesAsync(array $args = [])
 * @method \Aws\Result tagResource(array $args = [])
 * @method \GuzzleHttp\Promise\Promise tagResourceAsync(array $args = [])
 * @method \Aws\Result untagResource(array $args = [])
 * @method \GuzzleHttp\Promise\Promise untagResourceAsync(array $args = [])
 * @method \Aws\Result updateCell(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateCellAsync(array $args = [])
 * @method \Aws\Result updateReadinessCheck(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateReadinessCheckAsync(array $args = [])
 * @method \Aws\Result updateRecoveryGroup(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateRecoveryGroupAsync(array $args = [])
 * @method \Aws\Result updateResourceSet(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateResourceSetAsync(array $args = [])
 */
class Route53RecoveryReadinessClient extends AwsClient {}


<?php
namespace Aws\Route53RecoveryReadiness\Exception;

use Aws\Exception\AwsException;

/**
 * Represents an error interacting with the **AWS Route53 Recovery Readiness** service.
 */
class Route53RecoveryReadinessException extends AwsException {}


<?php
namespace Aws\Route53Resolver;

use Aws\AwsClient;

/**
 * This client is used to interact with the **Amazon Route 53 Resolver** service.
 * @method \Aws\Result associateFirewallRuleGroup(array $args = [])
 * @method \GuzzleHttp\Promise\Promise associateFirewallRuleGroupAsync(array $args = [])
 * @method \Aws\Result associateResolverEndpointIpAddress(array $args = [])
 * @method \GuzzleHttp\Promise\Promise associateResolverEndpointIpAddressAsync(array $args = [])
 * @method \Aws\Result associateResolverQueryLogConfig(array $args = [])
 * @method \GuzzleHttp\Promise\Promise associateResolverQueryLogConfigAsync(array $args = [])
 * @method \Aws\Result associateResolverRule(array $args = [])
 * @method \GuzzleHttp\Promise\Promise associateResolverRuleAsync(array $args = [])
 * @method \Aws\Result createFirewallDomainList(array $args = [])
 * @method \GuzzleHttp\Promise\Promise createFirewallDomainListAsync(array $args = [])
 * @method \Aws\Result createFirewallRule(array $args = [])
 * @method \GuzzleHttp\Promise\Promise createFirewallRuleAsync(array $args = [])
 * @method \Aws\Result createFirewallRuleGroup(array $args = [])
 * @method \GuzzleHttp\Promise\Promise createFirewallRuleGroupAsync(array $args = [])
 * @method \Aws\Result createResolverEndpoint(array $args = [])
 * @method \GuzzleHttp\Promise\Promise createResolverEndpointAsync(array $args = [])
 * @method \Aws\Result createResolverQueryLogConfig(array $args = [])
 * @method \GuzzleHttp\Promise\Promise createResolverQueryLogConfigAsync(array $args = [])
 * @method \Aws\Result createResolverRule(array $args = [])
 * @method \GuzzleHttp\Promise\Promise createResolverRuleAsync(array $args = [])
 * @method \Aws\Result deleteFirewallDomainList(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteFirewallDomainListAsync(array $args = [])
 * @method \Aws\Result deleteFirewallRule(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteFirewallRuleAsync(array $args = [])
 * @method \Aws\Result deleteFirewallRuleGroup(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteFirewallRuleGroupAsync(array $args = [])
 * @method \Aws\Result deleteResolverEndpoint(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteResolverEndpointAsync(array $args = [])
 * @method \Aws\Result deleteResolverQueryLogConfig(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteResolverQueryLogConfigAsync(array $args = [])
 * @method \Aws\Result deleteResolverRule(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteResolverRuleAsync(array $args = [])
 * @method \Aws\Result disassociateFirewallRuleGroup(array $args = [])
 * @method \GuzzleHttp\Promise\Promise disassociateFirewallRuleGroupAsync(array $args = [])
 * @method \Aws\Result disassociateResolverEndpointIpAddress(array $args = [])
 * @method \GuzzleHttp\Promise\Promise disassociateResolverEndpointIpAddressAsync(array $args = [])
 * @method \Aws\Result disassociateResolverQueryLogConfig(array $args = [])
 * @method \GuzzleHttp\Promise\Promise disassociateResolverQueryLogConfigAsync(array $args = [])
 * @method \Aws\Result disassociateResolverRule(array $args = [])
 * @method \GuzzleHttp\Promise\Promise disassociateResolverRuleAsync(array $args = [])
 * @method \Aws\Result getFirewallConfig(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getFirewallConfigAsync(array $args = [])
 * @method \Aws\Result getFirewallDomainList(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getFirewallDomainListAsync(array $args = [])
 * @method \Aws\Result getFirewallRuleGroup(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getFirewallRuleGroupAsync(array $args = [])
 * @method \Aws\Result getFirewallRuleGroupAssociation(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getFirewallRuleGroupAssociationAsync(array $args = [])
 * @method \Aws\Result getFirewallRuleGroupPolicy(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getFirewallRuleGroupPolicyAsync(array $args = [])
 * @method \Aws\Result getResolverConfig(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getResolverConfigAsync(array $args = [])
 * @method \Aws\Result getResolverDnssecConfig(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getResolverDnssecConfigAsync(array $args = [])
 * @method \Aws\Result getResolverEndpoint(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getResolverEndpointAsync(array $args = [])
 * @method \Aws\Result getResolverQueryLogConfig(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getResolverQueryLogConfigAsync(array $args = [])
 * @method \Aws\Result getResolverQueryLogConfigAssociation(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getResolverQueryLogConfigAssociationAsync(array $args = [])
 * @method \Aws\Result getResolverQueryLogConfigPolicy(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getResolverQueryLogConfigPolicyAsync(array $args = [])
 * @method \Aws\Result getResolverRule(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getResolverRuleAsync(array $args = [])
 * @method \Aws\Result getResolverRuleAssociation(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getResolverRuleAssociationAsync(array $args = [])
 * @method \Aws\Result getResolverRulePolicy(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getResolverRulePolicyAsync(array $args = [])
 * @method \Aws\Result importFirewallDomains(array $args = [])
 * @method \GuzzleHttp\Promise\Promise importFirewallDomainsAsync(array $args = [])
 * @method \Aws\Result listFirewallConfigs(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listFirewallConfigsAsync(array $args = [])
 * @method \Aws\Result listFirewallDomainLists(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listFirewallDomainListsAsync(array $args = [])
 * @method \Aws\Result listFirewallDomains(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listFirewallDomainsAsync(array $args = [])
 * @method \Aws\Result listFirewallRuleGroupAssociations(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listFirewallRuleGroupAssociationsAsync(array $args = [])
 * @method \Aws\Result listFirewallRuleGroups(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listFirewallRuleGroupsAsync(array $args = [])
 * @method \Aws\Result listFirewallRules(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listFirewallRulesAsync(array $args = [])
 * @method \Aws\Result listResolverConfigs(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listResolverConfigsAsync(array $args = [])
 * @method \Aws\Result listResolverDnssecConfigs(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listResolverDnssecConfigsAsync(array $args = [])
 * @method \Aws\Result listResolverEndpointIpAddresses(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listResolverEndpointIpAddressesAsync(array $args = [])
 * @method \Aws\Result listResolverEndpoints(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listResolverEndpointsAsync(array $args = [])
 * @method \Aws\Result listResolverQueryLogConfigAssociations(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listResolverQueryLogConfigAssociationsAsync(array $args = [])
 * @method \Aws\Result listResolverQueryLogConfigs(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listResolverQueryLogConfigsAsync(array $args = [])
 * @method \Aws\Result listResolverRuleAssociations(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listResolverRuleAssociationsAsync(array $args = [])
 * @method \Aws\Result listResolverRules(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listResolverRulesAsync(array $args = [])
 * @method \Aws\Result listTagsForResource(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listTagsForResourceAsync(array $args = [])
 * @method \Aws\Result putFirewallRuleGroupPolicy(array $args = [])
 * @method \GuzzleHttp\Promise\Promise putFirewallRuleGroupPolicyAsync(array $args = [])
 * @method \Aws\Result putResolverQueryLogConfigPolicy(array $args = [])
 * @method \GuzzleHttp\Promise\Promise putResolverQueryLogConfigPolicyAsync(array $args = [])
 * @method \Aws\Result putResolverRulePolicy(array $args = [])
 * @method \GuzzleHttp\Promise\Promise putResolverRulePolicyAsync(array $args = [])
 * @method \Aws\Result tagResource(array $args = [])
 * @method \GuzzleHttp\Promise\Promise tagResourceAsync(array $args = [])
 * @method \Aws\Result untagResource(array $args = [])
 * @method \GuzzleHttp\Promise\Promise untagResourceAsync(array $args = [])
 * @method \Aws\Result updateFirewallConfig(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateFirewallConfigAsync(array $args = [])
 * @method \Aws\Result updateFirewallDomains(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateFirewallDomainsAsync(array $args = [])
 * @method \Aws\Result updateFirewallRule(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateFirewallRuleAsync(array $args = [])
 * @method \Aws\Result updateFirewallRuleGroupAssociation(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateFirewallRuleGroupAssociationAsync(array $args = [])
 * @method \Aws\Result updateResolverConfig(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateResolverConfigAsync(array $args = [])
 * @method \Aws\Result updateResolverDnssecConfig(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateResolverDnssecConfigAsync(array $args = [])
 * @method \Aws\Result updateResolverEndpoint(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateResolverEndpointAsync(array $args = [])
 * @method \Aws\Result updateResolverRule(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateResolverRuleAsync(array $args = [])
 */
class Route53ResolverClient extends AwsClient {}


<?php
namespace Aws\Route53Resolver\Exception;

use Aws\Exception\AwsException;

/**
 * Represents an error interacting with the **Amazon Route 53 Resolver** service.
 */
class Route53ResolverException extends AwsException {}


<?php
namespace Aws\S3;

use Aws\Api\Parser\AbstractParser;
use Aws\Api\Parser\Exception\ParserException;
use Aws\Api\StructureShape;
use Aws\CommandInterface;
use Aws\Exception\AwsException;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;

/**
 * Converts errors returned with a status code of 200 to a retryable error type.
 *
 * @internal
 */
class AmbiguousSuccessParser extends AbstractParser
{
    private static $ambiguousSuccesses = [
        'UploadPart' => true,
        'UploadPartCopy' => true,
        'CopyObject' => true,
        'CompleteMultipartUpload' => true,
    ];

    /** @var callable */
    private $errorParser;
    /** @var string */
    private $exceptionClass;

    public function __construct(
        callable $parser,
        callable $errorParser,
        $exceptionClass = AwsException::class
    ) {
        $this->parser = $parser;
        $this->errorParser = $errorParser;
        $this->exceptionClass = $exceptionClass;
    }

    public function __invoke(
        CommandInterface $command,
        ResponseInterface $response
    ) {
        if (200 === $response->getStatusCode()
            && isset(self::$ambiguousSuccesses[$command->getName()])
        ) {
            $errorParser = $this->errorParser;
            try {
                $parsed = $errorParser($response);
            } catch (ParserException $e) {
                $parsed = [
                    'code' => 'ConnectionError',
                    'message' => "An error connecting to the service occurred"
                        . " while performing the " . $command->getName()
                        . " operation."
                ];
            }
            if (isset($parsed['code']) && isset($parsed['message'])) {
                throw new $this->exceptionClass(
                    $parsed['message'],
                    $command,
                    ['connection_error' => true]
                );
            }
        }

        $fn = $this->parser;
        return $fn($command, $response);
    }

    public function parseMemberFromStream(
        StreamInterface $stream,
        StructureShape $member,
        $response
    ) {
        return $this->parser->parseMemberFromStream($stream, $member, $response);
    }
}


<?php
namespace Aws\S3;

use Aws\Api\Service;
use Aws\CommandInterface;
use GuzzleHttp\Psr7;
use InvalidArgumentException;
use Psr\Http\Message\RequestInterface;

/**
 * Apply required or optional checksums to requests before sending.
 *
 * IMPORTANT: This middleware must be added after the "build" step.
 *
 * @internal
 */
class ApplyChecksumMiddleware
{
    use CalculatesChecksumTrait;
    private static $sha256AndMd5 = [
        'PutObject',
        'UploadPart',
    ];

    /** @var Service */
    private $api;

    private $nextHandler;

    /**
     * Create a middleware wrapper function.
     *
     * @param Service $api
     * @return callable
     */
    public static function wrap(Service $api)
    {
        return function (callable $handler) use ($api) {
            return new self($handler, $api);
        };
    }

    public function __construct(callable $nextHandler, Service $api)
    {
        $this->api = $api;
        $this->nextHandler = $nextHandler;
    }

    public function __invoke(
        CommandInterface $command,
        RequestInterface $request
    ) {
        $next = $this->nextHandler;
        $name = $command->getName();
        $body = $request->getBody();

        //Checks if AddContentMD5 has been specified for PutObject or UploadPart
        $addContentMD5 = isset($command['AddContentMD5'])
            ?  $command['AddContentMD5']
            : null;

        $op = $this->api->getOperation($command->getName());

        $checksumInfo = isset($op['httpChecksum'])
            ? $op['httpChecksum']
            : [];
        $checksumMemberName = array_key_exists('requestAlgorithmMember', $checksumInfo)
            ? $checksumInfo['requestAlgorithmMember']
            : "";
        $requestedAlgorithm = isset($command[$checksumMemberName])
            ? $command[$checksumMemberName]
            : null;
        if (!empty($checksumMemberName) && !empty($requestedAlgorithm)) {
            $requestedAlgorithm = strtolower($requestedAlgorithm);
            $checksumMember = $op->getInput()->getMember($checksumMemberName);
            $supportedAlgorithms = isset($checksumMember['enum'])
                ? array_map('strtolower', $checksumMember['enum'])
                : null;
            if (is_array($supportedAlgorithms)
                && in_array($requestedAlgorithm, $supportedAlgorithms)
            ) {
                $headerName = "x-amz-checksum-{$requestedAlgorithm}";
                $encoded = $this->getEncodedValue($requestedAlgorithm, $body);
                if (!$request->hasHeader($headerName)) {
                    $request = $request->withHeader($headerName, $encoded);
                }
            } else {
                throw new InvalidArgumentException(
                    "Unsupported algorithm supplied for input variable {$checksumMemberName}."
                    . "  Supported checksums for this operation include: "
                    . implode(", ", $supportedAlgorithms) . "."
                );
            }
            return $next($command, $request);
        }

        if (!empty($checksumInfo)) {
        //if the checksum member is absent, check if it's required
        $checksumRequired = isset($checksumInfo['requestChecksumRequired'])
            ? $checksumInfo['requestChecksumRequired']
            : null;
            if ((!empty($checksumRequired) && !$request->hasHeader('Content-MD5'))
                || (in_array($name, self::$sha256AndMd5) && $addContentMD5)
            ) {
                // Set the content MD5 header for operations that require it.
                $request = $request->withHeader(
                    'Content-MD5',
                    base64_encode(Psr7\Utils::hash($body, 'md5', true))
                );
                return $next($command, $request);
            }
        }

        if (in_array($name, self::$sha256AndMd5) && $command['ContentSHA256']) {
            // Set the content hash header if provided in the parameters.
            $request = $request->withHeader(
                'X-Amz-Content-Sha256',
                $command['ContentSHA256']
            );
        }

        return $next($command, $request);
    }
}


<?php
namespace Aws\S3;

use Aws\AwsClientInterface;
use Aws\S3\Exception\DeleteMultipleObjectsException;
use GuzzleHttp\Promise;
use GuzzleHttp\Promise\PromisorInterface;
use GuzzleHttp\Promise\PromiseInterface;

/**
 * Efficiently deletes many objects from a single Amazon S3 bucket using an
 * iterator that yields keys. Deletes are made using the DeleteObjects API
 * operation.
 *
 *     $s3 = new Aws\S3\Client([
 *         'region' => 'us-west-2',
 *         'version' => 'latest'
 *     ]);
 *
 *     $listObjectsParams = ['Bucket' => 'foo', 'Prefix' => 'starts/with/'];
 *     $delete = Aws\S3\BatchDelete::fromListObjects($s3, $listObjectsParams);
 *     // Asynchronously delete
 *     $promise = $delete->promise();
 *     // Force synchronous completion
 *     $delete->delete();
 *
 * When using one of the batch delete creational static methods, you can supply
 * an associative array of options:
 *
 * - before: Function invoked before executing a command. The function is
 *   passed the command that is about to be executed. This can be useful
 *   for logging, adding custom request headers, etc.
 * - batch_size: The size of each delete batch. Defaults to 1000.
 *
 * @link http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html
 */
class BatchDelete implements PromisorInterface
{
    private $bucket;
    /** @var AwsClientInterface */
    private $client;
    /** @var callable */
    private $before;
    /** @var PromiseInterface */
    private $cachedPromise;
    /** @var callable */
    private $promiseCreator;
    private $batchSize = 1000;
    private $queue = [];

    /**
     * Creates a BatchDelete object from all of the paginated results of a
     * ListObjects operation. Each result that is returned by the ListObjects
     * operation will be deleted.
     *
     * @param AwsClientInterface $client            AWS Client to use.
     * @param array              $listObjectsParams ListObjects API parameters
     * @param array              $options           BatchDelete options.
     *
     * @return BatchDelete
     */
    public static function fromListObjects(
        AwsClientInterface $client,
        array $listObjectsParams,
        array $options = []
    ) {
        $iter = $client->getPaginator('ListObjects', $listObjectsParams);
        $bucket = $listObjectsParams['Bucket'];
        $fn = function (BatchDelete $that) use ($iter) {
            return $iter->each(function ($result) use ($that) {
                $promises = [];
                if (is_array($result['Contents'])) {
                    foreach ($result['Contents'] as $object) {
                        if ($promise = $that->enqueue($object)) {
                            $promises[] = $promise;
                        }
                    }
                }
                return $promises ? Promise\Utils::all($promises) : null;
            });
        };

        return new self($client, $bucket, $fn, $options);
    }

    /**
     * Creates a BatchDelete object from an iterator that yields results.
     *
     * @param AwsClientInterface $client  AWS Client to use to execute commands
     * @param string             $bucket  Bucket where the objects are stored
     * @param \Iterator          $iter    Iterator that yields assoc arrays
     * @param array              $options BatchDelete options
     *
     * @return BatchDelete
     */
    public static function fromIterator(
        AwsClientInterface $client,
        $bucket,
        \Iterator $iter,
        array $options = []
    ) {
        $fn = function (BatchDelete $that) use ($iter) {
            return Promise\Coroutine::of(function () use ($that, $iter) {
                foreach ($iter as $obj) {
                    if ($promise = $that->enqueue($obj)) {
                        yield $promise;
                    }
                }
            });
        };

        return new self($client, $bucket, $fn, $options);
    }

    /**
     * @return PromiseInterface
     */
    public function promise()
    {
        if (!$this->cachedPromise) {
            $this->cachedPromise = $this->createPromise();
        }

        return $this->cachedPromise;
    }

    /**
     * Synchronously deletes all of the objects.
     *
     * @throws DeleteMultipleObjectsException on error.
     */
    public function delete()
    {
        $this->promise()->wait();
    }

    /**
     * @param AwsClientInterface $client    Client used to transfer the requests
     * @param string             $bucket    Bucket to delete from.
     * @param callable           $promiseFn Creates a promise.
     * @param array              $options   Hash of options used with the batch
     *
     * @throws \InvalidArgumentException if the provided batch_size is <= 0
     */
    private function __construct(
        AwsClientInterface $client,
        $bucket,
        callable $promiseFn,
        array $options = []
    ) {
        $this->client = $client;
        $this->bucket = $bucket;
        $this->promiseCreator = $promiseFn;

        if (isset($options['before'])) {
            if (!is_callable($options['before'])) {
                throw new \InvalidArgumentException('before must be callable');
            }
            $this->before = $options['before'];
        }

        if (isset($options['batch_size'])) {
            if ($options['batch_size'] <= 0) {
                throw new \InvalidArgumentException('batch_size is not > 0');
            }
            $this->batchSize = min($options['batch_size'], 1000);
        }
    }

    private function enqueue(array $obj)
    {
        $this->queue[] = $obj;
        return count($this->queue) >= $this->batchSize
            ? $this->flushQueue()
            : null;
    }

    private function flushQueue()
    {
        static $validKeys = ['Key' => true, 'VersionId' => true];

        if (count($this->queue) === 0) {
            return null;
        }

        $batch = [];
        while ($obj = array_shift($this->queue)) {
            $batch[] = array_intersect_key($obj, $validKeys);
        }

        $command = $this->client->getCommand('DeleteObjects', [
            'Bucket' => $this->bucket,
            'Delete' => ['Objects' => $batch]
        ]);

        if ($this->before) {
            call_user_func($this->before, $command);
        }

        return $this->client->executeAsync($command)
            ->then(function ($result) {
                if (!empty($result['Errors'])) {
                    throw new DeleteMultipleObjectsException(
                        $result['Deleted'] ?: [],
                        $result['Errors']
                    );
                }
                return $result;
            });
    }

    /**
     * Returns a promise that will clean up any references when it completes.
     *
     * @return PromiseInterface
     */
    private function createPromise()
    {
        // Create the promise
        $promise = call_user_func($this->promiseCreator, $this);
        $this->promiseCreator = null;

        // Cleans up the promise state and references.
        $cleanup = function () {
            $this->before = $this->client = $this->queue = null;
        };

        // When done, ensure cleanup and that any remaining are processed.
        return $promise->then(
            function () use ($cleanup)  {
                return Promise\Create::promiseFor($this->flushQueue())
                    ->then($cleanup);
            },
            function ($reason) use ($cleanup)  {
                $cleanup();
                return Promise\Create::rejectionFor($reason);
            }
        );
    }
}


<?php
namespace Aws\S3;

use Aws\Api\Service;
use Aws\Arn\AccessPointArnInterface;
use Aws\Arn\ArnParser;
use Aws\Arn\ObjectLambdaAccessPointArn;
use Aws\Arn\Exception\InvalidArnException;
use Aws\Arn\AccessPointArn as BaseAccessPointArn;
use Aws\Arn\S3\OutpostsAccessPointArn;
use Aws\Arn\S3\MultiRegionAccessPointArn;
use Aws\Arn\S3\OutpostsArnInterface;
use Aws\CommandInterface;
use Aws\Endpoint\PartitionEndpointProvider;
use Aws\Exception\InvalidRegionException;
use Aws\Exception\UnresolvedEndpointException;
use Aws\S3\Exception\S3Exception;
use InvalidArgumentException;
use Psr\Http\Message\RequestInterface;

/**
 * Checks for access point ARN in members targeting BucketName, modifying
 * endpoint as appropriate
 *
 * @internal
 */
class BucketEndpointArnMiddleware
{
    use EndpointRegionHelperTrait;

    /** @var callable */
    private $nextHandler;

    /** @var array */
    private $nonArnableCommands = ['CreateBucket'];

    /** @var boolean */
    private $isUseEndpointV2;

    /**
     * Create a middleware wrapper function.
     *
     * @param Service $service
     * @param $region
     * @param array $config
     * @return callable
     */
    public static function wrap(
        Service $service,
        $region,
        array $config,
        $isUseEndpointV2
    ) {
        return function (callable $handler) use ($service, $region, $config, $isUseEndpointV2) {
            return new self($handler, $service, $region, $config, $isUseEndpointV2);
        };
    }

    public function __construct(
        callable $nextHandler,
        Service $service,
        $region,
        array $config = [],
        $isUseEndpointV2 = false
    ) {
        $this->partitionProvider = PartitionEndpointProvider::defaultProvider();
        $this->region = $region;
        $this->service = $service;
        $this->config = $config;
        $this->nextHandler = $nextHandler;
        $this->isUseEndpointV2 = $isUseEndpointV2;
    }

    public function __invoke(CommandInterface $cmd, RequestInterface $req)
    {
        $nextHandler = $this->nextHandler;

        $op = $this->service->getOperation($cmd->getName())->toArray();
        if (!empty($op['input']['shape'])) {
            $service = $this->service->toArray();
            if (!empty($input = $service['shapes'][$op['input']['shape']])) {
                foreach ($input['members'] as $key => $member) {
                    if ($member['shape'] === 'BucketName') {
                        $arnableKey = $key;
                        break;
                    }
                }

                if (!empty($arnableKey) && ArnParser::isArn($cmd[$arnableKey])) {

                    try {
                        // Throw for commands that do not support ARN inputs
                        if (in_array($cmd->getName(), $this->nonArnableCommands)) {
                            throw new S3Exception(
                                'ARN values cannot be used in the bucket field for'
                                . ' the ' . $cmd->getName() . ' operation.',
                                $cmd
                            );
                        }

                        if (!$this->isUseEndpointV2) {
                            $arn = ArnParser::parse($cmd[$arnableKey]);
                            $partition = $this->validateArn($arn);
                            $host = $this->generateAccessPointHost($arn, $req);
                        }
                        // Remove encoded bucket string from path
                        $path = $req->getUri()->getPath();
                        $encoded = rawurlencode($cmd[$arnableKey]);
                        $len = strlen($encoded) + 1;
                        if (trim(substr($path, 0, $len), '/') === "{$encoded}") {
                            $path = substr($path, $len);
                            if (substr($path, 0, 1) !== "/") {
                                $path = '/' . $path;
                            }
                        }
                        if (empty($path)) {
                            $path = '';
                        }

                        // Set modified request
                        if ($this->isUseEndpointV2) {
                            $req = $req->withUri(
                                $req->getUri()->withPath($path)

                            );
                            goto next;
                        }

                        $req = $req->withUri(
                            $req->getUri()->withPath($path)->withHost($host)
                        );

                        // Update signing region based on ARN data if configured to do so
                        if ($this->config['use_arn_region']->isUseArnRegion()
                            && !$this->config['use_fips_endpoint']->isUseFipsEndpoint()
                        ) {
                            $region = $arn->getRegion();
                        } else {
                            $region = $this->region;
                        }
                        $endpointData = $partition([
                            'region' => $region,
                            'service' => $arn->getService()
                        ]);
                        $cmd['@context']['signing_region'] = $endpointData['signingRegion'];

                        // Update signing service for Outposts and Lambda ARNs
                        if ($arn instanceof OutpostsArnInterface
                            || $arn instanceof ObjectLambdaAccessPointArn
                        ) {
                            $cmd['@context']['signing_service'] = $arn->getService();
                        }
                    } catch (InvalidArnException $e) {
                        // Add context to ARN exception
                        throw new S3Exception(
                            'Bucket parameter parsed as ARN and failed with: '
                            . $e->getMessage(),
                            $cmd,
                            [],
                            $e
                        );
                    }
                }
            }
        }
        next:
            return $nextHandler($cmd, $req);
    }

    private function generateAccessPointHost(
        BaseAccessPointArn $arn,
        RequestInterface $req
    ) {
        if ($arn instanceof OutpostsAccessPointArn) {
            $accesspointName = $arn->getAccesspointName();
        } else {
            $accesspointName = $arn->getResourceId();
        }

        if ($arn instanceof MultiRegionAccessPointArn) {
            $partition = $this->partitionProvider->getPartitionByName(
                $arn->getPartition(),
                's3'
            );
            $dnsSuffix = $partition->getDnsSuffix();
            return "{$accesspointName}.accesspoint.s3-global.{$dnsSuffix}";
        }

        $host = "{$accesspointName}-" . $arn->getAccountId();

        $useFips = $this->config['use_fips_endpoint']->isUseFipsEndpoint();
        $fipsString = $useFips ? "-fips" : "";

        if ($arn instanceof OutpostsAccessPointArn) {
            $host .= '.' . $arn->getOutpostId() . '.s3-outposts';
        } else if ($arn instanceof ObjectLambdaAccessPointArn) {
            if (!empty($this->config['endpoint'])) {
                return $host . '.' . $this->config['endpoint'];
            } else {
                $host .= ".s3-object-lambda{$fipsString}";
            }
        } else {
            $host .= ".s3-accesspoint{$fipsString}";
            if (!empty($this->config['dual_stack'])) {
                $host .= '.dualstack';
            }
        }

        if (!empty($this->config['use_arn_region']->isUseArnRegion())) {
            $region = $arn->getRegion();
        } else {
            $region = $this->region;
        }
        $region = \Aws\strip_fips_pseudo_regions($region);
        $host .= '.' . $region . '.' . $this->getPartitionSuffix($arn, $this->partitionProvider);
        return $host;
    }

    /**
     * Validates an ARN, returning a partition object corresponding to the ARN
     * if successful
     *
     * @param $arn
     * @return \Aws\Endpoint\Partition
     */
    private function validateArn($arn)
    {
        if ($arn instanceof AccessPointArnInterface) {

            // Dualstack is not supported with Outposts access points
            if ($arn instanceof OutpostsAccessPointArn
                && !empty($this->config['dual_stack'])
            ) {
                throw new UnresolvedEndpointException(
                    'Dualstack is currently not supported with S3 Outposts access'
                    . ' points. Please disable dualstack or do not supply an'
                    . ' access point ARN.');
            }
            if ($arn instanceof MultiRegionAccessPointArn) {
                if (!empty($this->config['disable_multiregion_access_points'])) {
                    throw new UnresolvedEndpointException(
                        'Multi-Region Access Point ARNs are disabled, but one was provided.  Please'
                        . ' enable them or provide a different ARN.'
                    );
                }
                if (!empty($this->config['dual_stack'])) {
                    throw new UnresolvedEndpointException(
                        'Multi-Region Access Point ARNs do not currently support dual stack. Please'
                        . ' disable dual stack or provide a different ARN.'
                    );
                }
            }
            // Accelerate is not supported with access points
            if (!empty($this->config['accelerate'])) {
                throw new UnresolvedEndpointException(
                    'Accelerate is currently not supported with access points.'
                    . ' Please disable accelerate or do not supply an access'
                    . ' point ARN.');
            }

            // Path-style is not supported with access points
            if (!empty($this->config['path_style'])) {
                throw new UnresolvedEndpointException(
                    'Path-style addressing is currently not supported with'
                    . ' access points. Please disable path-style or do not'
                    . ' supply an access point ARN.');
            }

            // Custom endpoint is not supported with access points
            if (!is_null($this->config['endpoint'])
                && !$arn instanceof  ObjectLambdaAccessPointArn
            ) {
                throw new UnresolvedEndpointException(
                    'A custom endpoint has been supplied along with an access'
                    . ' point ARN, and these are not compatible with each other.'
                    . ' Please only use one or the other.');
            }

            // Dualstack is not supported with object lambda access points
            if ($arn instanceof ObjectLambdaAccessPointArn
                && !empty($this->config['dual_stack'])
            ) {
                throw new UnresolvedEndpointException(
                    'Dualstack is currently not supported with Object Lambda access'
                    . ' points. Please disable dualstack or do not supply an'
                    . ' access point ARN.');
            }
            // Global endpoints do not support cross-region requests
            if ($this->isGlobal($this->region)
                && $this->config['use_arn_region']->isUseArnRegion() == false
                && $arn->getRegion() != $this->region
                && !$arn instanceof MultiRegionAccessPointArn
            ) {
                throw new UnresolvedEndpointException(
                    'Global endpoints do not support cross region requests.'
                    . ' Please enable use_arn_region or do not supply a global region'
                    . ' with a different region in the ARN.');
            }

            // Get partitions for ARN and client region
            $arnPart = $this->partitionProvider->getPartition(
                $arn->getRegion(),
                's3'
            );
            $clientPart = $this->partitionProvider->getPartition(
                $this->region,
                's3'
            );

            // If client partition not found, try removing pseudo-region qualifiers
            if (!($clientPart->isRegionMatch($this->region, 's3'))) {
                $clientPart = $this->partitionProvider->getPartition(
                    \Aws\strip_fips_pseudo_regions($this->region),
                    's3'
                );
            }
            if (!$arn instanceof MultiRegionAccessPointArn) {
                // Verify that the partition matches for supplied partition and region
                if ($arn->getPartition() !== $clientPart->getName()) {
                    throw new InvalidRegionException('The supplied ARN partition'
                        . " does not match the client's partition.");
                }
                if ($clientPart->getName() !== $arnPart->getName()) {
                    throw new InvalidRegionException('The corresponding partition'
                        . ' for the supplied ARN region does not match the'
                        . " client's partition.");
                }

                // Ensure ARN region matches client region unless
                // configured for using ARN region over client region
                $this->validateMatchingRegion($arn);

                // Ensure it is not resolved to fips pseudo-region for S3 Outposts
                $this->validateFipsConfigurations($arn);
            }

            return $arnPart;
        }

        throw new InvalidArnException('Provided ARN was not a valid S3 access'
            . ' point ARN or S3 Outposts access point ARN.');
    }

    /**
     * Checks if a region is global
     *
     * @param $region
     * @return bool
     */
    private function isGlobal($region)
    {
        return $region == 's3-external-1' || $region == 'aws-global';
    }
}


<?php
namespace Aws\S3;

use Aws\CommandInterface;
use Psr\Http\Message\RequestInterface;

/**
 * Used to update the host used for S3 requests in the case of using a
 * "bucket endpoint" or CNAME bucket.
 *
 * IMPORTANT: this middleware must be added after the "build" step.
 *
 * @internal
 */
class BucketEndpointMiddleware
{
    private static $exclusions = ['GetBucketLocation' => true];
    private $nextHandler;

    /**
     * Create a middleware wrapper function.
     *
     * @return callable
     */
    public static function wrap()
    {
        return function (callable $handler) {
            return new self($handler);
        };
    }

    public function __construct(callable $nextHandler)
    {
        $this->nextHandler = $nextHandler;
    }

    public function __invoke(CommandInterface $command, RequestInterface $request)
    {
        $nextHandler = $this->nextHandler;
        $bucket = $command['Bucket'];

        if ($bucket && !isset(self::$exclusions[$command->getName()])) {
            $request = $this->modifyRequest($request, $command);
        }

        return $nextHandler($command, $request);
    }

    /**
     * Performs a one-time removal of Bucket from path, then if
     * the bucket name is duplicated in the path, performs additional
     * removal which is dependent on the number of occurrences of the bucket
     * name in a path-like format in the key name.
     *
     * @return string
     */
    private function removeBucketFromPath($path, $bucket, $key)
    {
        $occurrencesInKey = $this->getBucketNameOccurrencesInKey($key, $bucket);
        do {
            $len = strlen($bucket) + 1;
            if (substr($path, 0, $len) === "/{$bucket}") {
                $path = substr($path, $len);
            }
        } while (substr_count($path, "/{$bucket}") > $occurrencesInKey + 1);

        return $path ?: '/';
    }

    private function removeDuplicateBucketFromHost($host, $bucket)
    {
        if (substr_count($host, $bucket) > 1) {
            while (strpos($host, "{$bucket}.{$bucket}") === 0) {
                $hostArr = explode('.', $host);
                array_shift($hostArr);
                $host = implode('.', $hostArr);
            }
        }
        return $host;
    }

    private function getBucketNameOccurrencesInKey($key, $bucket)
    {
        $occurrences = 0;
        if (empty($key)) {
            return $occurrences;
        }

        $segments = explode('/', $key);
        foreach($segments as $segment) {
            if (strpos($segment, $bucket) === 0) {
                $occurrences++;
            }
        }
        return $occurrences;
    }

    private function modifyRequest(
        RequestInterface $request,
        CommandInterface $command
    ) {
        $key = isset($command['Key']) ? $command['Key'] : null;
        $uri = $request->getUri();
        $path = $uri->getPath();
        $host = $uri->getHost();
        $bucket = $command['Bucket'];
        $path = $this->removeBucketFromPath($path, $bucket, $key);
        $host = $this->removeDuplicateBucketFromHost($host, $bucket);

        // Modify the Key to make sure the key is encoded, but slashes are not.
        if ($key) {
            $path = S3Client::encodeKey(rawurldecode($path));
        }

        return $request->withUri(
            $uri->withHost($host)
                ->withPath($path)
        );
    }
}


<?php
namespace Aws\S3;

use AWS\CRT\CRT;
use Aws\Exception\CommonRuntimeException;
use GuzzleHttp\Psr7;
use InvalidArgumentException;

trait CalculatesChecksumTrait
{
    /**
     * @param string $requestedAlgorithm  the algorithm to encode with
     * @param string $value               the value to be encoded
     * @return string
     */
    public static function getEncodedValue($requestedAlgorithm, $value) {
        $requestedAlgorithm = strtolower($requestedAlgorithm);
        $useCrt = extension_loaded('awscrt');
        if ($useCrt) {
            $crt = new Crt();
            switch ($requestedAlgorithm) {
                case 'crc32c':
                    return base64_encode(pack('N*',($crt->crc32c($value))));
                case 'crc32':
                    return base64_encode(pack('N*',($crt->crc32($value))));
                case 'sha256':
                case 'sha1':
                    return base64_encode(Psr7\Utils::hash($value, $requestedAlgorithm, true));
                default:
                    break;
                throw new InvalidArgumentException(
                    "Invalid checksum requested: {$requestedAlgorithm}."
                    . "  Valid algorithms are CRC32C, CRC32, SHA256, and SHA1."
                );
            }
        }  else {
            if ($requestedAlgorithm == 'crc32c') {
                throw new CommonRuntimeException("crc32c is not supported for checksums "
                    . "without use of the common runtime for php.  Please enable the CRT or choose "
                    . "a different algorithm."
                );
            }
            if ($requestedAlgorithm == "crc32") {
                $requestedAlgorithm = "crc32b";
            }
            return base64_encode(Psr7\Utils::hash($value, $requestedAlgorithm, true));
        }
    }

}


<?php
namespace Aws\S3;

use Aws\Api\Service;
use Aws\Arn\ArnInterface;
use Aws\Arn\S3\OutpostsArnInterface;
use Aws\Endpoint\PartitionEndpointProvider;
use Aws\Exception\InvalidRegionException;

/**
 * @internal
 */
trait EndpointRegionHelperTrait
{
    /** @var array */
    private $config;

    /** @var PartitionEndpointProvider */
    private $partitionProvider;

    /** @var string */
    private $region;

    /** @var Service */
    private $service;

    private function getPartitionSuffix(
        ArnInterface $arn,
        PartitionEndpointProvider $provider
    ) {
        $partition = $provider->getPartition(
            $arn->getRegion(),
            $arn->getService()
        );
        return $partition->getDnsSuffix();
    }

    private function getSigningRegion(
        $region,
        $service,
        PartitionEndpointProvider $provider
    ) {
        $partition = $provider->getPartition($region, $service);
        $data = $partition->toArray();
        if (isset($data['services'][$service]['endpoints'][$region]['credentialScope']['region'])) {
            return $data['services'][$service]['endpoints'][$region]['credentialScope']['region'];
        }
        return $region;
    }

    private function isMatchingSigningRegion(
        $arnRegion,
        $clientRegion,
        $service,
        PartitionEndpointProvider $provider
    ) {
        $arnRegion = \Aws\strip_fips_pseudo_regions(strtolower($arnRegion));
        $clientRegion = strtolower($clientRegion);
        if ($arnRegion === $clientRegion) {
            return true;
        }
        if ($this->getSigningRegion($clientRegion, $service, $provider) === $arnRegion) {
            return true;
        }
        return false;
    }

    private function validateFipsConfigurations(ArnInterface $arn)
    {
        $useFipsEndpoint = !empty($this->config['use_fips_endpoint']);
        if ($arn instanceof OutpostsArnInterface) {
            if (empty($this->config['use_arn_region'])
                || !($this->config['use_arn_region']->isUseArnRegion())
            ) {
                $region = $this->region;
            } else {
                $region = $arn->getRegion();
            }
            if (\Aws\is_fips_pseudo_region($region)) {
                throw new InvalidRegionException(
                    'Fips is currently not supported with S3 Outposts access'
                    . ' points. Please provide a non-fips region or do not supply an'
                    . ' access point ARN.');
            }
        }
    }

    private function validateMatchingRegion(ArnInterface $arn)
    {
        if (!($this->isMatchingSigningRegion(
            $arn->getRegion(),
            $this->region,
            $this->service->getEndpointPrefix(),
            $this->partitionProvider)
        )) {
            if (empty($this->config['use_arn_region'])
                || !($this->config['use_arn_region']->isUseArnRegion())
            ) {
                throw new InvalidRegionException('The region'
                    . " specified in the ARN (" . $arn->getRegion()
                    . ") does not match the client region ("
                    . "{$this->region}).");
            }
        }
    }
}


<?php
namespace Aws\S3;

use Aws\Api\Parser\AbstractParser;
use Aws\Api\StructureShape;
use Aws\CommandInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;

/**
 * @internal Decorates a parser for the S3 service to correctly handle the
 *           GetBucketLocation operation.
 */
class GetBucketLocationParser extends AbstractParser
{
    /**
     * @param callable $parser Parser to wrap.
     */
    public function __construct(callable $parser)
    {
        $this->parser = $parser;
    }

    public function __invoke(
        CommandInterface $command,
        ResponseInterface $response
    ) {
        $fn = $this->parser;
        $result = $fn($command, $response);

        if ($command->getName() === 'GetBucketLocation') {
            $location = 'us-east-1';
            if (preg_match('/>(.+?)<\/LocationConstraint>/', $response->getBody(), $matches)) {
                $location = $matches[1] === 'EU' ? 'eu-west-1' : $matches[1];
            }
            $result['LocationConstraint'] = $location;
        }

        return $result;
    }

    public function parseMemberFromStream(
        StreamInterface $stream,
        StructureShape $member,
        $response
    ) {
        return $this->parser->parseMemberFromStream($stream, $member, $response);
    }
}


<?php

namespace Aws\S3;

use Aws\Arn\ArnParser;
use Aws\Multipart\AbstractUploadManager;
use Aws\ResultInterface;
use GuzzleHttp\Psr7;

class MultipartCopy extends AbstractUploadManager
{
    use MultipartUploadingTrait;

    /** @var string|array */
    private $source;
    /** @var string */
    private $sourceVersionId;
    /** @var ResultInterface */
    private $sourceMetadata;

    /**
     * Creates a multipart upload for copying an S3 object.
     *
     * The valid configuration options are as follows:
     *
     * - acl: (string) ACL to set on the object being upload. Objects are
     *   private by default.
     * - before_complete: (callable) Callback to invoke before the
     *   `CompleteMultipartUpload` operation. The callback should have a
     *   function signature like `function (Aws\Command $command) {...}`.
     * - before_initiate: (callable) Callback to invoke before the
     *   `CreateMultipartUpload` operation. The callback should have a function
     *   signature like `function (Aws\Command $command) {...}`.
     * - before_upload: (callable) Callback to invoke before `UploadPartCopy`
     *   operations. The callback should have a function signature like
     *   `function (Aws\Command $command) {...}`.
     * - bucket: (string, required) Name of the bucket to which the object is
     *   being uploaded.
     * - concurrency: (int, default=int(5)) Maximum number of concurrent
     *   `UploadPart` operations allowed during the multipart upload.
     * - key: (string, required) Key to use for the object being uploaded.
     * - params: (array) An array of key/value parameters that will be applied
     *   to each of the sub-commands run by the uploader as a base.
     *   Auto-calculated options will override these parameters. If you need
     *   more granularity over parameters to each sub-command, use the before_*
     *   options detailed above to update the commands directly.
     * - part_size: (int, default=int(5242880)) Part size, in bytes, to use when
     *   doing a multipart upload. This must between 5 MB and 5 GB, inclusive.
     * - state: (Aws\Multipart\UploadState) An object that represents the state
     *   of the multipart upload and that is used to resume a previous upload.
     *   When this option is provided, the `bucket`, `key`, and `part_size`
     *   options are ignored.
     * - source_metadata: (Aws\ResultInterface) An object that represents the
     *   result of executing a HeadObject command on the copy source.
     *
     * @param S3ClientInterface $client Client used for the upload.
     * @param string|array $source Location of the data to be copied (in the
     *                       form /<bucket>/<key>).  If the key contains a '?'
     *                       character, instead pass an array of source_key,
     *                       source_bucket, and source_version_id.
     * @param array $config Configuration used to perform the upload.
     */
    public function __construct(
        S3ClientInterface $client,
        $source,
        array $config = []
    ) {
        if (is_array($source)) {
            $this->source = $source;
        } else {
            $this->source = $this->getInputSource($source);
        }

        parent::__construct(
            $client,
            array_change_key_case($config) + ['source_metadata' => null]
        );
    }

    /**
     * An alias of the self::upload method.
     *
     * @see self::upload
     */
    public function copy()
    {
        return $this->upload();
    }

    protected function loadUploadWorkflowInfo()
    {
        return [
            'command' => [
                'initiate' => 'CreateMultipartUpload',
                'upload' => 'UploadPartCopy',
                'complete' => 'CompleteMultipartUpload',
            ],
            'id' => [
                'bucket' => 'Bucket',
                'key' => 'Key',
                'upload_id' => 'UploadId',
            ],
            'part_num' => 'PartNumber',
        ];
    }

    protected function getUploadCommands(callable $resultHandler)
    {
        $parts = ceil($this->getSourceSize() / $this->determinePartSize());

        for ($partNumber = 1; $partNumber <= $parts; $partNumber++) {
            // If we haven't already uploaded this part, yield a new part.
            if (!$this->state->hasPartBeenUploaded($partNumber)) {
                $command = $this->client->getCommand(
                    $this->info['command']['upload'],
                    $this->createPart($partNumber, $parts) + $this->getState()->getId()
                );
                $command->getHandlerList()->appendSign($resultHandler, 'mup');
                yield $command;
            }
        }
    }

    private function createPart($partNumber, $partsCount)
    {
        $data = [];

        // Apply custom params to UploadPartCopy data
        $config = $this->getConfig();
        $params = isset($config['params']) ? $config['params'] : [];
        foreach ($params as $k => $v) {
            $data[$k] = $v;
        }
        // The source parameter here is usually a string, but can be overloaded as an array
        // if the key contains a '?' character to specify where the query parameters start
        if (is_array($this->source)) {
            $key = str_replace('%2F', '/', rawurlencode($this->source['source_key']));
            $bucket = $this->source['source_bucket'];
        } else {
            list($bucket, $key) = explode('/', ltrim($this->source, '/'), 2);
            $key = implode(
                '/',
                array_map(
                    'urlencode',
                    explode('/', rawurldecode($key))
                )
            );
        }

        $uri = ArnParser::isArn($bucket) ? '' : '/';
        $uri .= $bucket . '/' . $key;
        $data['CopySource'] = $uri;
        $data['PartNumber'] = $partNumber;
        if (!empty($this->sourceVersionId)) {
            $data['CopySource'] .= "?versionId=" . $this->sourceVersionId;
        }

        $defaultPartSize = $this->determinePartSize();
        $startByte = $defaultPartSize * ($partNumber - 1);
        $data['ContentLength'] = $partNumber < $partsCount
            ? $defaultPartSize
            : $this->getSourceSize() - ($defaultPartSize * ($partsCount - 1));
        $endByte = $startByte + $data['ContentLength'] - 1;
        $data['CopySourceRange'] = "bytes=$startByte-$endByte";

        return $data;
    }

    protected function extractETag(ResultInterface $result)
    {
        return $result->search('CopyPartResult.ETag');
    }

    protected function getSourceMimeType()
    {
        return $this->getSourceMetadata()['ContentType'];
    }

    protected function getSourceSize()
    {
        return $this->getSourceMetadata()['ContentLength'];
    }

    private function getSourceMetadata()
    {
        if (empty($this->sourceMetadata)) {
            $this->sourceMetadata = $this->fetchSourceMetadata();
        }

        return $this->sourceMetadata;
    }

    private function fetchSourceMetadata()
    {
        if ($this->config['source_metadata'] instanceof ResultInterface) {
            return $this->config['source_metadata'];
        }
        //if the source variable was overloaded with an array, use the inputs for key and bucket
        if (is_array($this->source)) {
            $headParams = [
                'Key' => $this->source['source_key'],
                'Bucket' => $this->source['source_bucket']
            ];
            if (isset($this->source['source_version_id'])) {
                $this->sourceVersionId = $this->source['source_version_id'];
                $headParams['VersionId'] = $this->sourceVersionId;
            }
        //otherwise, use the default source parsing behavior
        } else {
            list($bucket, $key) = explode('/', ltrim($this->source, '/'), 2);
            $headParams = [
                'Bucket' => $bucket,
                'Key' => $key,
            ];
            if (strpos($key, '?')) {
                list($key, $query) = explode('?', $key, 2);
                $headParams['Key'] = $key;
                $query = Psr7\Query::parse($query, false);
                if (isset($query['versionId'])) {
                    $this->sourceVersionId = $query['versionId'];
                    $headParams['VersionId'] = $this->sourceVersionId;
                }
            }
        }
        return $this->client->headObject($headParams);
    }

    /**
     * Get the url decoded input source, starting with a slash if it is not an
     * ARN to standardize the source location syntax.
     *
     * @param string $inputSource The source that was passed to the constructor
     * @return string The source, starting with a slash if it's not an arn
     */
    private function getInputSource($inputSource)
    {
        $sourceBuilder = ArnParser::isArn($inputSource) ? '' : '/';
        $sourceBuilder .= ltrim(rawurldecode($inputSource), '/');
        return $sourceBuilder;
    }

}


<?php
namespace Aws\S3;

use Aws\HashingStream;
use Aws\Multipart\AbstractUploader;
use Aws\PhpHash;
use Aws\ResultInterface;
use GuzzleHttp\Psr7;
use Psr\Http\Message\StreamInterface as Stream;
use Aws\S3\Exception\S3MultipartUploadException;

/**
 * Encapsulates the execution of a multipart upload to S3 or Glacier.
 */
class MultipartUploader extends AbstractUploader
{
    use MultipartUploadingTrait;

    const PART_MIN_SIZE = 5242880;
    const PART_MAX_SIZE = 5368709120;
    const PART_MAX_NUM = 10000;

    /**
     * Creates a multipart upload for an S3 object.
     *
     * The valid configuration options are as follows:
     *
     * - acl: (string) ACL to set on the object being upload. Objects are
     *   private by default.
     * - before_complete: (callable) Callback to invoke before the
     *   `CompleteMultipartUpload` operation. The callback should have a
     *   function signature like `function (Aws\Command $command) {...}`.
     * - before_initiate: (callable) Callback to invoke before the
     *   `CreateMultipartUpload` operation. The callback should have a function
     *   signature like `function (Aws\Command $command) {...}`.
     * - before_upload: (callable) Callback to invoke before any `UploadPart`
     *   operations. The callback should have a function signature like
     *   `function (Aws\Command $command) {...}`.
     * - bucket: (string, required) Name of the bucket to which the object is
     *   being uploaded, or an S3 access point ARN.
     * - concurrency: (int, default=int(5)) Maximum number of concurrent
     *   `UploadPart` operations allowed during the multipart upload.
     * - key: (string, required) Key to use for the object being uploaded.
     * - params: (array) An array of key/value parameters that will be applied
     *   to each of the sub-commands run by the uploader as a base.
     *   Auto-calculated options will override these parameters. If you need
     *   more granularity over parameters to each sub-command, use the before_*
     *   options detailed above to update the commands directly.
     * - part_size: (int, default=int(5242880)) Part size, in bytes, to use when
     *   doing a multipart upload. This must between 5 MB and 5 GB, inclusive.
     * - prepare_data_source: (callable) Callback to invoke before starting the
     *   multipart upload workflow. The callback should have a function
     *   signature like `function () {...}`.
     * - state: (Aws\Multipart\UploadState) An object that represents the state
     *   of the multipart upload and that is used to resume a previous upload.
     *   When this option is provided, the `bucket`, `key`, and `part_size`
     *   options are ignored.
     *
     * @param S3ClientInterface $client Client used for the upload.
     * @param mixed             $source Source of the data to upload.
     * @param array             $config Configuration used to perform the upload.
     */
    public function __construct(
        S3ClientInterface $client,
        $source,
        array $config = []
    ) {
        parent::__construct($client, $source, array_change_key_case($config) + [
            'bucket' => null,
            'key'    => null,
            'exception_class' => S3MultipartUploadException::class,
        ]);
    }

    protected function loadUploadWorkflowInfo()
    {
        return [
            'command' => [
                'initiate' => 'CreateMultipartUpload',
                'upload'   => 'UploadPart',
                'complete' => 'CompleteMultipartUpload',
            ],
            'id' => [
                'bucket'    => 'Bucket',
                'key'       => 'Key',
                'upload_id' => 'UploadId',
            ],
            'part_num' => 'PartNumber',
        ];
    }

    protected function createPart($seekable, $number)
    {
        // Initialize the array of part data that will be returned.
        $data = [];

        // Apply custom params to UploadPart data
        $config = $this->getConfig();
        $params = isset($config['params']) ? $config['params'] : [];
        foreach ($params as $k => $v) {
            $data[$k] = $v;
        }

        $data['PartNumber'] = $number;

        // Read from the source to create the body stream.
        if ($seekable) {
            // Case 1: Source is seekable, use lazy stream to defer work.
            $body = $this->limitPartStream(
                new Psr7\LazyOpenStream($this->source->getMetadata('uri'), 'r')
            );
        } else {
            // Case 2: Stream is not seekable; must store in temp stream.
            $source = $this->limitPartStream($this->source);
            $source = $this->decorateWithHashes($source, $data);
            $body = Psr7\Utils::streamFor();
            Psr7\Utils::copyToStream($source, $body);
        }

        $contentLength = $body->getSize();

        // Do not create a part if the body size is zero.
        if ($contentLength === 0) {
            return false;
        }

        $body->seek(0);
        $data['Body'] = $body;

        if (isset($config['add_content_md5'])
            && $config['add_content_md5'] === true
        ) {
            $data['AddContentMD5'] = true;
        }

        $data['ContentLength'] = $contentLength;

        return $data;
    }

    protected function extractETag(ResultInterface $result)
    {
        return $result['ETag'];
    }

    protected function getSourceMimeType()
    {
        if ($uri = $this->source->getMetadata('uri')) {
            return Psr7\MimeType::fromFilename($uri)
                ?: 'application/octet-stream';
        }
    }

    protected function getSourceSize()
    {
        return $this->source->getSize();
    }

    /**
     * Decorates a stream with a sha256 linear hashing stream.
     *
     * @param Stream $stream Stream to decorate.
     * @param array  $data   Part data to augment with the hash result.
     *
     * @return Stream
     */
    private function decorateWithHashes(Stream $stream, array &$data)
    {
        // Decorate source with a hashing stream
        $hash = new PhpHash('sha256');
        return new HashingStream($stream, $hash, function ($result) use (&$data) {
            $data['ContentSHA256'] = bin2hex($result);
        });
    }
}


<?php
namespace Aws\S3;

use Aws\CommandInterface;
use Aws\Multipart\UploadState;
use Aws\ResultInterface;

trait MultipartUploadingTrait
{
    /**
     * Creates an UploadState object for a multipart upload by querying the
     * service for the specified upload's information.
     *
     * @param S3ClientInterface $client   S3Client used for the upload.
     * @param string            $bucket   Bucket for the multipart upload.
     * @param string            $key      Object key for the multipart upload.
     * @param string            $uploadId Upload ID for the multipart upload.
     *
     * @return UploadState
     */
    public static function getStateFromService(
        S3ClientInterface $client,
        $bucket,
        $key,
        $uploadId
    ) {
        $state = new UploadState([
            'Bucket'   => $bucket,
            'Key'      => $key,
            'UploadId' => $uploadId,
        ]);

        foreach ($client->getPaginator('ListParts', $state->getId()) as $result) {
            // Get the part size from the first part in the first result.
            if (!$state->getPartSize()) {
                $state->setPartSize($result->search('Parts[0].Size'));
            }
            // Mark all the parts returned by ListParts as uploaded.
            foreach ($result['Parts'] as $part) {
                $state->markPartAsUploaded($part['PartNumber'], [
                    'PartNumber' => $part['PartNumber'],
                    'ETag'       => $part['ETag']
                ]);
            }
        }

        $state->setStatus(UploadState::INITIATED);

        return $state;
    }

    protected function handleResult(CommandInterface $command, ResultInterface $result)
    {
        $this->getState()->markPartAsUploaded($command['PartNumber'], [
            'PartNumber' => $command['PartNumber'],
            'ETag'       => $this->extractETag($result),
        ]);
    }

    abstract protected function extractETag(ResultInterface $result);

    protected function getCompleteParams()
    {
        $config = $this->getConfig();
        $params = isset($config['params']) ? $config['params'] : [];

        $params['MultipartUpload'] = [
            'Parts' => $this->getState()->getUploadedParts()
        ];

        return $params;
    }

    protected function determinePartSize()
    {
        // Make sure the part size is set.
        $partSize = $this->getConfig()['part_size'] ?: MultipartUploader::PART_MIN_SIZE;

        // Adjust the part size to be larger for known, x-large uploads.
        if ($sourceSize = $this->getSourceSize()) {
            $partSize = (int) max(
                $partSize,
                ceil($sourceSize / MultipartUploader::PART_MAX_NUM)
            );
        }

        // Ensure that the part size follows the rules: 5 MB <= size <= 5 GB.
        if ($partSize < MultipartUploader::PART_MIN_SIZE || $partSize > MultipartUploader::PART_MAX_SIZE) {
            throw new \InvalidArgumentException('The part size must be no less '
                . 'than 5 MB and no greater than 5 GB.');
        }

        return $partSize;
    }

    protected function getInitiateParams()
    {
        $config = $this->getConfig();
        $params = isset($config['params']) ? $config['params'] : [];

        if (isset($config['acl'])) {
            $params['ACL'] = $config['acl'];
        }

        // Set the ContentType if not already present
        if (empty($params['ContentType']) && $type = $this->getSourceMimeType()) {
            $params['ContentType'] = $type;
        }

        return $params;
    }

    /**
     * @return UploadState
     */
    abstract protected function getState();

    /**
     * @return array
     */
    abstract protected function getConfig();

    /**
     * @return int
     */
    abstract protected function getSourceSize();

    /**
     * @return string|null
     */
    abstract protected function getSourceMimeType();
}


<?php
namespace Aws\S3;

use Aws\Arn\ArnParser;
use Aws\Arn\S3\AccessPointArn;
use Aws\Exception\MultipartUploadException;
use Aws\Result;
use Aws\S3\Exception\S3Exception;
use GuzzleHttp\Promise\Coroutine;
use GuzzleHttp\Promise\PromisorInterface;
use InvalidArgumentException;

/**
 * Copies objects from one S3 location to another, utilizing a multipart copy
 * when appropriate.
 */
class ObjectCopier implements PromisorInterface
{
    const DEFAULT_MULTIPART_THRESHOLD = MultipartUploader::PART_MAX_SIZE;

    private $client;
    private $source;
    private $destination;
    private $acl;
    private $options;

    private static $defaults = [
        'before_lookup' => null,
        'before_upload' => null,
        'concurrency'   => 5,
        'mup_threshold' => self::DEFAULT_MULTIPART_THRESHOLD,
        'params'        => [],
        'part_size'     => null,
        'version_id'    => null,
    ];

    /**
     * @param S3ClientInterface $client         The S3 Client used to execute
     *                                          the copy command(s).
     * @param array             $source         The object to copy, specified as
     *                                          an array with a 'Bucket' and
     *                                          'Key' keys. Provide a
     *                                          'VersionID' key to copy a
     *                                          specified version of an object.
     * @param array             $destination    The bucket and key to which to
     *                                          copy the $source, specified as
     *                                          an array with a 'Bucket' and
     *                                          'Key' keys.
     * @param string            $acl            ACL to apply to the copy
     *                                          (default: private).
     * @param array             $options        Options used to configure the
     *                                          copy process. Options passed in
     *                                          through 'params' are added to
     *                                          the sub commands.
     *
     * @throws InvalidArgumentException
     */
    public function __construct(
        S3ClientInterface $client,
        array $source,
        array $destination,
        $acl = 'private',
        array $options = []
    ) {
        $this->validateLocation($source);
        $this->validateLocation($destination);

        $this->client = $client;
        $this->source = $source;
        $this->destination = $destination;
        $this->acl = $acl;
        $this->options = $options + self::$defaults;
    }

    /**
     * Perform the configured copy asynchronously. Returns a promise that is
     * fulfilled with the result of the CompleteMultipartUpload or CopyObject
     * operation or rejected with an exception.
     *
     * @return Coroutine
     */
    public function promise()
    {
        return Coroutine::of(function () {
            $headObjectCommand = $this->client->getCommand(
                'HeadObject',
                $this->options['params'] + $this->source
            );
            if (is_callable($this->options['before_lookup'])) {
                $this->options['before_lookup']($headObjectCommand);
            }
            $objectStats = (yield $this->client->executeAsync(
                $headObjectCommand
            ));

            if ($objectStats['ContentLength'] > $this->options['mup_threshold']) {
                $mup = new MultipartCopy(
                    $this->client,
                    $this->getSourcePath(),
                    ['source_metadata' => $objectStats, 'acl' => $this->acl]
                        + $this->destination
                        + $this->options
                );

                yield $mup->promise();
            } else {
                $defaults = [
                    'ACL' => $this->acl,
                    'MetadataDirective' => 'COPY',
                    'CopySource' => $this->getSourcePath(),
                ];

                $params = array_diff_key($this->options, self::$defaults)
                    + $this->destination + $defaults + $this->options['params'];

                yield $this->client->executeAsync(
                    $this->client->getCommand('CopyObject', $params)
                );
            }
        });
    }

    /**
     * Perform the configured copy synchronously. Returns the result of the
     * CompleteMultipartUpload or CopyObject operation.
     *
     * @return Result
     *
     * @throws S3Exception
     * @throws MultipartUploadException
     */
    public function copy()
    {
        return $this->promise()->wait();
    }

    private function validateLocation(array $location)
    {
        if (empty($location['Bucket']) || empty($location['Key'])) {
            throw new \InvalidArgumentException('Locations provided to an'
                . ' Aws\S3\ObjectCopier must have a non-empty Bucket and Key');
        }
    }

    private function getSourcePath()
    {
        $path = "/{$this->source['Bucket']}/";
        if (ArnParser::isArn($this->source['Bucket'])) {
            try {
                new AccessPointArn($this->source['Bucket']);
                $path = "{$this->source['Bucket']}/object/";
            } catch (\Exception $e) {
                throw new \InvalidArgumentException(
                    'Provided ARN was a not a valid S3 access point ARN ('
                    . $e->getMessage() . ')',
                    0,
                    $e
                );
            }
        }

        $sourcePath = $path . rawurlencode($this->source['Key']);
        if (isset($this->source['VersionId'])) {
            $sourcePath .= "?versionId={$this->source['VersionId']}";
        }

        return $sourcePath;
    }
}


<?php
namespace Aws\S3;

use GuzzleHttp\Promise\PromiseInterface;
use GuzzleHttp\Promise\PromisorInterface;
use GuzzleHttp\Psr7;
use Psr\Http\Message\StreamInterface;

/**
 * Uploads an object to S3, using a PutObject command or a multipart upload as
 * appropriate.
 */
class ObjectUploader implements PromisorInterface
{
    const DEFAULT_MULTIPART_THRESHOLD = 16777216;

    private $client;
    private $bucket;
    private $key;
    private $body;
    private $acl;
    private $options;
    private static $defaults = [
        'before_upload' => null,
        'concurrency'   => 3,
        'mup_threshold' => self::DEFAULT_MULTIPART_THRESHOLD,
        'params'        => [],
        'part_size'     => null,
    ];
    private $addContentMD5;

    /**
     * @param S3ClientInterface $client         The S3 Client used to execute
     *                                          the upload command(s).
     * @param string            $bucket         Bucket to upload the object, or
     *                                          an S3 access point ARN.
     * @param string            $key            Key of the object.
     * @param mixed             $body           Object data to upload. Can be a
     *                                          StreamInterface, PHP stream
     *                                          resource, or a string of data to
     *                                          upload.
     * @param string            $acl            ACL to apply to the copy
     *                                          (default: private).
     * @param array             $options        Options used to configure the
     *                                          copy process. Options passed in
     *                                          through 'params' are added to
     *                                          the sub command(s).
     */
    public function __construct(
        S3ClientInterface $client,
        $bucket,
        $key,
        $body,
        $acl = 'private',
        array $options = []
    ) {
        $this->client = $client;
        $this->bucket = $bucket;
        $this->key = $key;
        $this->body = Psr7\Utils::streamFor($body);
        $this->acl = $acl;
        $this->options = $options + self::$defaults;
        // Handle "add_content_md5" option.
        $this->addContentMD5 = isset($options['add_content_md5'])
            && $options['add_content_md5'] === true;
    }

    /**
     * @return PromiseInterface
     */
    public function promise()
    {
        /** @var int $mup_threshold */
        $mup_threshold = $this->options['mup_threshold'];
        if ($this->requiresMultipart($this->body, $mup_threshold)) {
            // Perform a multipart upload.
            return (new MultipartUploader($this->client, $this->body, [
                    'bucket' => $this->bucket,
                    'key'    => $this->key,
                    'acl'    => $this->acl
                ] + $this->options))->promise();
        }

        // Perform a regular PutObject operation.
        $command = $this->client->getCommand('PutObject', [
                'Bucket' => $this->bucket,
                'Key'    => $this->key,
                'Body'   => $this->body,
                'ACL'    => $this->acl,
                'AddContentMD5' => $this->addContentMD5
            ] + $this->options['params']);
        if (is_callable($this->options['before_upload'])) {
            $this->options['before_upload']($command);
        }
        return $this->client->executeAsync($command);
    }

    public function upload()
    {
        return $this->promise()->wait();
    }

    /**
     * Determines if the body should be uploaded using PutObject or the
     * Multipart Upload System. It also modifies the passed-in $body as needed
     * to support the upload.
     *
     * @param StreamInterface $body      Stream representing the body.
     * @param integer             $threshold Minimum bytes before using Multipart.
     *
     * @return bool
     */
    private function requiresMultipart(StreamInterface &$body, $threshold)
    {
        // If body size known, compare to threshold to determine if Multipart.
        if ($body->getSize() !== null) {
            return $body->getSize() >= $threshold;
        }

        /**
         * Handle the situation where the body size is unknown.
         * Read up to 5MB into a buffer to determine how to upload the body.
         * @var StreamInterface $buffer
         */
        $buffer = Psr7\Utils::streamFor();
        Psr7\Utils::copyToStream($body, $buffer, MultipartUploader::PART_MIN_SIZE);

        // If body < 5MB, use PutObject with the buffer.
        if ($buffer->getSize() < MultipartUploader::PART_MIN_SIZE) {
            $buffer->seek(0);
            $body = $buffer;
            return false;
        }

        // If body >= 5 MB, then use multipart. [YES]
        if ($body->isSeekable() && $body->getMetadata('uri') !== 'php://input') {
            // If the body is seekable, just rewind the body.
            $body->seek(0);
        } else {
            // If the body is non-seekable, stitch the rewind the buffer and
            // the partially read body together into one stream. This avoids
            // unnecessary disc usage and does not require seeking on the
            // original stream.
            $buffer->seek(0);
            $body = new Psr7\AppendStream([$buffer, $body]);
        }

        return true;
    }
}


<?php
namespace Aws\S3;

use Aws\CommandInterface;
use Aws\ResultInterface;
use Aws\S3\Exception\PermanentRedirectException;
use Psr\Http\Message\RequestInterface;

/**
 * Throws a PermanentRedirectException exception when a 301 redirect is
 * encountered.
 *
 * @internal
 */
class PermanentRedirectMiddleware
{
    /** @var callable  */
    private $nextHandler;

    /**
     * Create a middleware wrapper function.
     *
     * @return callable
     */
    public static function wrap()
    {
        return function (callable $handler) {
            return new self($handler);
        };
    }

    /**
     * @param callable $nextHandler Next handler to invoke.
     */
    public function __construct(callable $nextHandler)
    {
        $this->nextHandler = $nextHandler;
    }

    public function __invoke(CommandInterface $command, RequestInterface $request = null)
    {
        $next = $this->nextHandler;
        return $next($command, $request)->then(
            function (ResultInterface $result) use ($command) {
                $status = isset($result['@metadata']['statusCode'])
                    ? $result['@metadata']['statusCode']
                    : null;
                if ($status == 301) {
                    throw new PermanentRedirectException(
                        'Encountered a permanent redirect while requesting '
                        . $result->search('"@metadata".effectiveUri') . '. '
                        . 'Are you sure you are using the correct region for '
                        . 'this bucket?',
                        $command,
                        ['result' => $result]
                    );
                }
                return $result;
            }
        );
    }
}


<?php
namespace Aws\S3;

use Aws\Credentials\CredentialsInterface;
use GuzzleHttp\Psr7\Uri;

/**
 * @deprecated
 */
class PostObject
{
    private $client;
    private $bucket;
    private $formAttributes;
    private $formInputs;
    private $jsonPolicy;

    /**
     * Constructs the PostObject.
     *
     * @param S3ClientInterface $client     Client used with the POST object
     * @param string            $bucket     Bucket to use
     * @param array             $formInputs Associative array of form input
     *                                      fields.
     * @param string|array      $jsonPolicy JSON encoded POST policy document.
     *                                      The policy will be base64 encoded
     *                                      and applied to the form on your
     *                                      behalf.
     */
    public function __construct(
        S3ClientInterface $client,
        $bucket,
        array $formInputs,
        $jsonPolicy
    ) {
        $this->client = $client;
        $this->bucket = $bucket;

        if (is_array($jsonPolicy)) {
            $jsonPolicy = json_encode($jsonPolicy);
        }

        $this->jsonPolicy = $jsonPolicy;
        $this->formAttributes = [
            'action'  => $this->generateUri(),
            'method'  => 'POST',
            'enctype' => 'multipart/form-data'
        ];

        $this->formInputs = $formInputs + ['key' => '${filename}'];
        $credentials = $client->getCredentials()->wait();
        $this->formInputs += $this->getPolicyAndSignature($credentials);
    }

    /**
     * Gets the S3 client.
     *
     * @return S3ClientInterface
     */
    public function getClient()
    {
        return $this->client;
    }

    /**
     * Gets the bucket name.
     *
     * @return string
     */
    public function getBucket()
    {
        return $this->bucket;
    }

    /**
     * Gets the form attributes as an array.
     *
     * @return array
     */
    public function getFormAttributes()
    {
        return $this->formAttributes;
    }

    /**
     * Set a form attribute.
     *
     * @param string $attribute Form attribute to set.
     * @param string $value     Value to set.
     */
    public function setFormAttribute($attribute, $value)
    {
        $this->formAttributes[$attribute] = $value;
    }

    /**
     * Gets the form inputs as an array.
     *
     * @return array
     */
    public function getFormInputs()
    {
        return $this->formInputs;
    }

    /**
     * Set a form input.
     *
     * @param string $field Field name to set
     * @param string $value Value to set.
     */
    public function setFormInput($field, $value)
    {
        $this->formInputs[$field] = $value;
    }

    /**
     * Gets the raw JSON policy.
     *
     * @return string
     */
    public function getJsonPolicy()
    {
        return $this->jsonPolicy;
    }

    private function generateUri()
    {
        $uri = new Uri($this->client->getEndpoint());

        if ($this->client->getConfig('use_path_style_endpoint') === true
            || ($uri->getScheme() === 'https'
            && strpos($this->bucket, '.') !== false)
        ) {
            // Use path-style URLs
            $uri = $uri->withPath("/{$this->bucket}");
        } else {
            // Use virtual-style URLs
            $uri = $uri->withHost($this->bucket . '.' . $uri->getHost());
        }

        return (string) $uri;
    }

    protected function getPolicyAndSignature(CredentialsInterface $creds)
    {
        $jsonPolicy64 = base64_encode($this->jsonPolicy);

        return [
            'AWSAccessKeyId' => $creds->getAccessKeyId(),
            'policy'    => $jsonPolicy64,
            'signature' => base64_encode(hash_hmac(
                'sha1',
                $jsonPolicy64,
                $creds->getSecretKey(),
                true
            ))
        ];
    }
}