Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
namespace Aws\Api\Serializer;
3
 
4
use Aws\Api\MapShape;
5
use Aws\Api\Service;
6
use Aws\Api\Operation;
7
use Aws\Api\Shape;
8
use Aws\Api\StructureShape;
9
use Aws\Api\TimestampShape;
10
use Aws\CommandInterface;
11
use Aws\EndpointV2\EndpointProviderV2;
12
use Aws\EndpointV2\EndpointV2SerializerTrait;
13
use GuzzleHttp\Psr7;
14
use GuzzleHttp\Psr7\Request;
15
use GuzzleHttp\Psr7\Uri;
16
use GuzzleHttp\Psr7\UriResolver;
17
use Psr\Http\Message\RequestInterface;
18
 
19
/**
20
 * Serializes HTTP locations like header, uri, payload, etc...
21
 * @internal
22
 */
23
abstract class RestSerializer
24
{
25
    use EndpointV2SerializerTrait;
26
 
27
    /** @var Service */
28
    private $api;
29
 
30
    /** @var Uri */
31
    private $endpoint;
32
 
33
    /**
34
     * @param Service $api      Service API description
35
     * @param string  $endpoint Endpoint to connect to
36
     */
37
    public function __construct(Service $api, $endpoint)
38
    {
39
        $this->api = $api;
40
        $this->endpoint = Psr7\Utils::uriFor($endpoint);
41
    }
42
 
43
    /**
44
     * @param CommandInterface $command Command to serialize into a request.
45
     * @param $endpointProvider Provider used for dynamic endpoint resolution.
46
     * @param $clientArgs Client arguments used for dynamic endpoint resolution.
47
     *
48
     * @return RequestInterface
49
     */
50
    public function __invoke(
51
        CommandInterface $command,
52
        $endpointProvider = null,
53
        $clientArgs = null
54
    )
55
    {
56
        $operation = $this->api->getOperation($command->getName());
57
        $commandArgs = $command->toArray();
58
        $opts = $this->serialize($operation, $commandArgs);
59
        $headers = isset($opts['headers']) ? $opts['headers'] : [];
60
 
61
        if ($endpointProvider instanceof EndpointProviderV2) {
62
            $this->setRequestOptions(
63
                $endpointProvider,
64
                $command,
65
                $operation,
66
                $commandArgs,
67
                $clientArgs,
68
                $headers
69
            );
70
            $this->endpoint = new Uri($this->endpoint);
71
        }
72
        $uri = $this->buildEndpoint($operation, $commandArgs, $opts);
73
 
74
        return new Request(
75
            $operation['http']['method'],
76
            $uri,
77
            $headers,
78
            isset($opts['body']) ? $opts['body'] : null
79
        );
80
    }
81
 
82
    /**
83
     * Modifies a hash of request options for a payload body.
84
     *
85
     * @param StructureShape   $member  Member to serialize
86
     * @param array            $value   Value to serialize
87
     * @param array            $opts    Request options to modify.
88
     */
89
    abstract protected function payload(
90
        StructureShape $member,
91
        array $value,
92
        array &$opts
93
    );
94
 
95
    private function serialize(Operation $operation, array $args)
96
    {
97
        $opts = [];
98
        $input = $operation->getInput();
99
 
100
        // Apply the payload trait if present
101
        if ($payload = $input['payload']) {
102
            $this->applyPayload($input, $payload, $args, $opts);
103
        }
104
 
105
        foreach ($args as $name => $value) {
106
            if ($input->hasMember($name)) {
107
                $member = $input->getMember($name);
108
                $location = $member['location'];
109
                if (!$payload && !$location) {
110
                    $bodyMembers[$name] = $value;
111
                } elseif ($location == 'header') {
112
                    $this->applyHeader($name, $member, $value, $opts);
113
                } elseif ($location == 'querystring') {
114
                    $this->applyQuery($name, $member, $value, $opts);
115
                } elseif ($location == 'headers') {
116
                    $this->applyHeaderMap($name, $member, $value, $opts);
117
                }
118
            }
119
        }
120
 
121
        if (isset($bodyMembers)) {
122
            $this->payload($operation->getInput(), $bodyMembers, $opts);
123
        } else if (!isset($opts['body']) && $this->hasPayloadParam($input, $payload)) {
124
            $this->payload($operation->getInput(), [], $opts);
125
        }
126
 
127
        return $opts;
128
    }
129
 
130
    private function applyPayload(StructureShape $input, $name, array $args, array &$opts)
131
    {
132
        if (!isset($args[$name])) {
133
            return;
134
        }
135
 
136
        $m = $input->getMember($name);
137
 
138
        if ($m['streaming'] ||
139
           ($m['type'] == 'string' || $m['type'] == 'blob')
140
        ) {
141
            // Streaming bodies or payloads that are strings are
142
            // always just a stream of data.
143
            $opts['body'] = Psr7\Utils::streamFor($args[$name]);
144
            return;
145
        }
146
 
147
        $this->payload($m, $args[$name], $opts);
148
    }
149
 
150
    private function applyHeader($name, Shape $member, $value, array &$opts)
151
    {
152
        if ($member->getType() === 'timestamp') {
153
            $timestampFormat = !empty($member['timestampFormat'])
154
                ? $member['timestampFormat']
155
                : 'rfc822';
156
            $value = TimestampShape::format($value, $timestampFormat);
157
        } elseif ($member->getType() === 'boolean') {
158
            $value = $value ? 'true' : 'false';
159
        }
160
 
161
        if ($member['jsonvalue']) {
162
            $value = json_encode($value);
163
            if (empty($value) && JSON_ERROR_NONE !== json_last_error()) {
164
                throw new \InvalidArgumentException('Unable to encode the provided value'
165
                    . ' with \'json_encode\'. ' . json_last_error_msg());
166
            }
167
 
168
            $value = base64_encode($value);
169
        }
170
 
171
        $opts['headers'][$member['locationName'] ?: $name] = $value;
172
    }
173
 
174
    /**
175
     * Note: This is currently only present in the Amazon S3 model.
176
     */
177
    private function applyHeaderMap($name, Shape $member, array $value, array &$opts)
178
    {
179
        $prefix = $member['locationName'];
180
        foreach ($value as $k => $v) {
181
            $opts['headers'][$prefix . $k] = $v;
182
        }
183
    }
184
 
185
    private function applyQuery($name, Shape $member, $value, array &$opts)
186
    {
187
        if ($member instanceof MapShape) {
188
            $opts['query'] = isset($opts['query']) && is_array($opts['query'])
189
                ? $opts['query'] + $value
190
                : $value;
191
        } elseif ($value !== null) {
192
            $type = $member->getType();
193
            if ($type === 'boolean') {
194
                $value = $value ? 'true' : 'false';
195
            } elseif ($type === 'timestamp') {
196
                $timestampFormat = !empty($member['timestampFormat'])
197
                    ? $member['timestampFormat']
198
                    : 'iso8601';
199
                $value = TimestampShape::format($value, $timestampFormat);
200
            }
201
 
202
            $opts['query'][$member['locationName'] ?: $name] = $value;
203
        }
204
    }
205
 
206
    private function buildEndpoint(Operation $operation, array $args, array $opts)
207
    {
208
        // Create an associative array of variable definitions used in expansions
209
        $varDefinitions = $this->getVarDefinitions($operation, $args);
210
 
211
        $relative = preg_replace_callback(
212
            '/\{([^\}]+)\}/',
213
            function (array $matches) use ($varDefinitions) {
214
                $isGreedy = substr($matches[1], -1, 1) == '+';
215
                $k = $isGreedy ? substr($matches[1], 0, -1) : $matches[1];
216
                if (!isset($varDefinitions[$k])) {
217
                    return '';
218
                }
219
 
220
                if ($isGreedy) {
221
                    return str_replace('%2F', '/', rawurlencode($varDefinitions[$k]));
222
                }
223
 
224
                return rawurlencode($varDefinitions[$k]);
225
            },
226
            $operation['http']['requestUri']
227
        );
228
 
229
        // Add the query string variables or appending to one if needed.
230
        if (!empty($opts['query'])) {
231
           $relative = $this->appendQuery($opts['query'], $relative);
232
        }
233
 
234
        $path = $this->endpoint->getPath();
235
 
236
        //Accounts for trailing '/' in path when custom endpoint
237
        //is provided to endpointProviderV2
238
        if ($this->api->isModifiedModel()
239
            && $this->api->getServiceName() === 's3'
240
        ) {
241
            if (substr($path, -1) === '/' && $relative[0] === '/') {
242
                $path = rtrim($path, '/');
243
            }
244
            $relative = $path . $relative;
245
        }
246
        // If endpoint has path, remove leading '/' to preserve URI resolution.
247
        if ($path && $relative[0] === '/') {
248
            $relative = substr($relative, 1);
249
        }
250
 
251
        //Append path to endpoint when leading '//...' present
252
        // as uri cannot be properly resolved
253
        if ($this->api->isModifiedModel()
254
            && strpos($relative, '//') === 0
255
        ) {
256
            return new Uri($this->endpoint . $relative);
257
        }
258
 
259
        // Expand path place holders using Amazon's slightly different URI
260
        // template syntax.
261
        return UriResolver::resolve($this->endpoint, new Uri($relative));
262
    }
263
 
264
    /**
265
     * @param StructureShape $input
266
     */
267
    private function hasPayloadParam(StructureShape $input, $payload)
268
    {
269
        if ($payload) {
270
            $potentiallyEmptyTypes = ['blob','string'];
271
            if ($this->api->getMetadata('protocol') == 'rest-xml') {
272
                $potentiallyEmptyTypes[] = 'structure';
273
            }
274
            $payloadMember = $input->getMember($payload);
275
            if (in_array($payloadMember['type'], $potentiallyEmptyTypes)) {
276
                return false;
277
            }
278
        }
279
        foreach ($input->getMembers() as $member) {
280
            if (!isset($member['location'])) {
281
                return true;
282
            }
283
        }
284
        return false;
285
    }
286
 
287
    private function appendQuery($query, $endpoint)
288
    {
289
        $append = Psr7\Query::build($query);
290
        return $endpoint .= strpos($endpoint, '?') !== false ? "&{$append}" : "?{$append}";
291
    }
292
 
293
    private function getVarDefinitions($command, $args)
294
    {
295
        $varDefinitions = [];
296
 
297
        foreach ($command->getInput()->getMembers() as $name => $member) {
298
            if ($member['location'] == 'uri') {
299
                $varDefinitions[$member['locationName'] ?: $name] =
300
                    isset($args[$name])
301
                        ? $args[$name]
302
                        : null;
303
            }
304
        }
305
        return $varDefinitions;
306
    }
307
}