diff options
Diffstat (limited to 'vendor/aws/aws-sdk-php/src/Api/Serializer/RestSerializer.php')
-rw-r--r-- | vendor/aws/aws-sdk-php/src/Api/Serializer/RestSerializer.php | 307 |
1 files changed, 307 insertions, 0 deletions
diff --git a/vendor/aws/aws-sdk-php/src/Api/Serializer/RestSerializer.php b/vendor/aws/aws-sdk-php/src/Api/Serializer/RestSerializer.php new file mode 100644 index 0000000..dbd925a --- /dev/null +++ b/vendor/aws/aws-sdk-php/src/Api/Serializer/RestSerializer.php @@ -0,0 +1,307 @@ +<?php +namespace Aws\Api\Serializer; + +use Aws\Api\MapShape; +use Aws\Api\Service; +use Aws\Api\Operation; +use Aws\Api\Shape; +use Aws\Api\StructureShape; +use Aws\Api\TimestampShape; +use Aws\CommandInterface; +use Aws\EndpointV2\EndpointProviderV2; +use Aws\EndpointV2\EndpointV2SerializerTrait; +use GuzzleHttp\Psr7; +use GuzzleHttp\Psr7\Request; +use GuzzleHttp\Psr7\Uri; +use GuzzleHttp\Psr7\UriResolver; +use Psr\Http\Message\RequestInterface; + +/** + * Serializes HTTP locations like header, uri, payload, etc... + * @internal + */ +abstract class RestSerializer +{ + use EndpointV2SerializerTrait; + + /** @var Service */ + private $api; + + /** @var Uri */ + private $endpoint; + + /** + * @param Service $api Service API description + * @param string $endpoint Endpoint to connect to + */ + public function __construct(Service $api, $endpoint) + { + $this->api = $api; + $this->endpoint = Psr7\Utils::uriFor($endpoint); + } + + /** + * @param CommandInterface $command Command to serialize into a request. + * @param $endpointProvider Provider used for dynamic endpoint resolution. + * @param $clientArgs Client arguments used for dynamic endpoint resolution. + * + * @return RequestInterface + */ + public function __invoke( + CommandInterface $command, + $endpointProvider = null, + $clientArgs = null + ) + { + $operation = $this->api->getOperation($command->getName()); + $commandArgs = $command->toArray(); + $opts = $this->serialize($operation, $commandArgs); + $headers = isset($opts['headers']) ? $opts['headers'] : []; + + if ($endpointProvider instanceof EndpointProviderV2) { + $this->setRequestOptions( + $endpointProvider, + $command, + $operation, + $commandArgs, + $clientArgs, + $headers + ); + $this->endpoint = new Uri($this->endpoint); + } + $uri = $this->buildEndpoint($operation, $commandArgs, $opts); + + return new Request( + $operation['http']['method'], + $uri, + $headers, + isset($opts['body']) ? $opts['body'] : null + ); + } + + /** + * Modifies a hash of request options for a payload body. + * + * @param StructureShape $member Member to serialize + * @param array $value Value to serialize + * @param array $opts Request options to modify. + */ + abstract protected function payload( + StructureShape $member, + array $value, + array &$opts + ); + + private function serialize(Operation $operation, array $args) + { + $opts = []; + $input = $operation->getInput(); + + // Apply the payload trait if present + if ($payload = $input['payload']) { + $this->applyPayload($input, $payload, $args, $opts); + } + + foreach ($args as $name => $value) { + if ($input->hasMember($name)) { + $member = $input->getMember($name); + $location = $member['location']; + if (!$payload && !$location) { + $bodyMembers[$name] = $value; + } elseif ($location == 'header') { + $this->applyHeader($name, $member, $value, $opts); + } elseif ($location == 'querystring') { + $this->applyQuery($name, $member, $value, $opts); + } elseif ($location == 'headers') { + $this->applyHeaderMap($name, $member, $value, $opts); + } + } + } + + if (isset($bodyMembers)) { + $this->payload($operation->getInput(), $bodyMembers, $opts); + } else if (!isset($opts['body']) && $this->hasPayloadParam($input, $payload)) { + $this->payload($operation->getInput(), [], $opts); + } + + return $opts; + } + + private function applyPayload(StructureShape $input, $name, array $args, array &$opts) + { + if (!isset($args[$name])) { + return; + } + + $m = $input->getMember($name); + + if ($m['streaming'] || + ($m['type'] == 'string' || $m['type'] == 'blob') + ) { + // Streaming bodies or payloads that are strings are + // always just a stream of data. + $opts['body'] = Psr7\Utils::streamFor($args[$name]); + return; + } + + $this->payload($m, $args[$name], $opts); + } + + private function applyHeader($name, Shape $member, $value, array &$opts) + { + if ($member->getType() === 'timestamp') { + $timestampFormat = !empty($member['timestampFormat']) + ? $member['timestampFormat'] + : 'rfc822'; + $value = TimestampShape::format($value, $timestampFormat); + } elseif ($member->getType() === 'boolean') { + $value = $value ? 'true' : 'false'; + } + + if ($member['jsonvalue']) { + $value = json_encode($value); + if (empty($value) && JSON_ERROR_NONE !== json_last_error()) { + throw new \InvalidArgumentException('Unable to encode the provided value' + . ' with \'json_encode\'. ' . json_last_error_msg()); + } + + $value = base64_encode($value); + } + + $opts['headers'][$member['locationName'] ?: $name] = $value; + } + + /** + * Note: This is currently only present in the Amazon S3 model. + */ + private function applyHeaderMap($name, Shape $member, array $value, array &$opts) + { + $prefix = $member['locationName']; + foreach ($value as $k => $v) { + $opts['headers'][$prefix . $k] = $v; + } + } + + private function applyQuery($name, Shape $member, $value, array &$opts) + { + if ($member instanceof MapShape) { + $opts['query'] = isset($opts['query']) && is_array($opts['query']) + ? $opts['query'] + $value + : $value; + } elseif ($value !== null) { + $type = $member->getType(); + if ($type === 'boolean') { + $value = $value ? 'true' : 'false'; + } elseif ($type === 'timestamp') { + $timestampFormat = !empty($member['timestampFormat']) + ? $member['timestampFormat'] + : 'iso8601'; + $value = TimestampShape::format($value, $timestampFormat); + } + + $opts['query'][$member['locationName'] ?: $name] = $value; + } + } + + private function buildEndpoint(Operation $operation, array $args, array $opts) + { + // Create an associative array of variable definitions used in expansions + $varDefinitions = $this->getVarDefinitions($operation, $args); + + $relative = preg_replace_callback( + '/\{([^\}]+)\}/', + function (array $matches) use ($varDefinitions) { + $isGreedy = substr($matches[1], -1, 1) == '+'; + $k = $isGreedy ? substr($matches[1], 0, -1) : $matches[1]; + if (!isset($varDefinitions[$k])) { + return ''; + } + + if ($isGreedy) { + return str_replace('%2F', '/', rawurlencode($varDefinitions[$k])); + } + + return rawurlencode($varDefinitions[$k]); + }, + $operation['http']['requestUri'] + ); + + // Add the query string variables or appending to one if needed. + if (!empty($opts['query'])) { + $relative = $this->appendQuery($opts['query'], $relative); + } + + $path = $this->endpoint->getPath(); + + //Accounts for trailing '/' in path when custom endpoint + //is provided to endpointProviderV2 + if ($this->api->isModifiedModel() + && $this->api->getServiceName() === 's3' + ) { + if (substr($path, -1) === '/' && $relative[0] === '/') { + $path = rtrim($path, '/'); + } + $relative = $path . $relative; + } + // If endpoint has path, remove leading '/' to preserve URI resolution. + if ($path && $relative[0] === '/') { + $relative = substr($relative, 1); + } + + //Append path to endpoint when leading '//...' present + // as uri cannot be properly resolved + if ($this->api->isModifiedModel() + && strpos($relative, '//') === 0 + ) { + return new Uri($this->endpoint . $relative); + } + + // Expand path place holders using Amazon's slightly different URI + // template syntax. + return UriResolver::resolve($this->endpoint, new Uri($relative)); + } + + /** + * @param StructureShape $input + */ + private function hasPayloadParam(StructureShape $input, $payload) + { + if ($payload) { + $potentiallyEmptyTypes = ['blob','string']; + if ($this->api->getMetadata('protocol') == 'rest-xml') { + $potentiallyEmptyTypes[] = 'structure'; + } + $payloadMember = $input->getMember($payload); + if (in_array($payloadMember['type'], $potentiallyEmptyTypes)) { + return false; + } + } + foreach ($input->getMembers() as $member) { + if (!isset($member['location'])) { + return true; + } + } + return false; + } + + private function appendQuery($query, $endpoint) + { + $append = Psr7\Query::build($query); + return $endpoint .= strpos($endpoint, '?') !== false ? "&{$append}" : "?{$append}"; + } + + private function getVarDefinitions($command, $args) + { + $varDefinitions = []; + + foreach ($command->getInput()->getMembers() as $name => $member) { + if ($member['location'] == 'uri') { + $varDefinitions[$member['locationName'] ?: $name] = + isset($args[$name]) + ? $args[$name] + : null; + } + } + return $varDefinitions; + } +} |