Skip to content

Commit 08d9ba0

Browse files
Merge pull request #2251 from nextcloud/dependabot/composer/aws/aws-sdk-php-3.369.3
build(deps): bump aws/aws-sdk-php from 3.349.3 to 3.369.9
2 parents 086aa07 + 5a603e3 commit 08d9ba0

File tree

165 files changed

+12456
-663
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

165 files changed

+12456
-663
lines changed

aws/aws-sdk-php/src/Api/ErrorParser/JsonParserTrait.php

Lines changed: 106 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,41 +12,133 @@ trait JsonParserTrait
1212
{
1313
use PayloadParserTrait;
1414

15-
private function genericHandler(ResponseInterface $response)
15+
private function genericHandler(ResponseInterface $response): array
1616
{
1717
$code = (string) $response->getStatusCode();
18+
$error_code = null;
19+
$error_type = null;
20+
21+
// Parse error code and type for query compatible services
1822
if ($this->api
1923
&& !is_null($this->api->getMetadata('awsQueryCompatible'))
20-
&& $response->getHeaderLine('x-amzn-query-error')
24+
&& $response->hasHeader('x-amzn-query-error')
2125
) {
22-
$queryError = $response->getHeaderLine('x-amzn-query-error');
23-
$parts = explode(';', $queryError);
24-
if (isset($parts) && count($parts) == 2 && $parts[0] && $parts[1]) {
25-
$error_code = $parts[0];
26-
$error_type = $parts[1];
26+
$awsQueryError = $this->parseAwsQueryCompatibleHeader($response);
27+
if ($awsQueryError) {
28+
$error_code = $awsQueryError['code'];
29+
$error_type = $awsQueryError['type'];
2730
}
2831
}
32+
33+
// Parse error code from X-Amzn-Errortype header
34+
if (!$error_code && $response->hasHeader('X-Amzn-Errortype')) {
35+
$error_code = $this->extractErrorCode(
36+
$response->getHeaderLine('X-Amzn-Errortype')
37+
);
38+
}
39+
40+
$parsedBody = null;
41+
$body = $response->getBody();
42+
if (!$body->isSeekable() || $body->getSize()) {
43+
$parsedBody = $this->parseJson((string) $body, $response);
44+
}
45+
46+
// Parse error code from response body
47+
if (!$error_code && $parsedBody) {
48+
$error_code = $this->parseErrorFromBody($parsedBody);
49+
}
50+
2951
if (!isset($error_type)) {
3052
$error_type = $code[0] == '4' ? 'client' : 'server';
3153
}
3254

3355
return [
34-
'request_id' => (string) $response->getHeaderLine('x-amzn-requestid'),
35-
'code' => isset($error_code) ? $error_code : null,
56+
'request_id' => $response->getHeaderLine('x-amzn-requestid'),
57+
'code' => $error_code ?? null,
3658
'message' => null,
3759
'type' => $error_type,
38-
'parsed' => $this->parseJson($response->getBody(), $response)
60+
'parsed' => $parsedBody
3961
];
4062
}
4163

64+
/**
65+
* Parse AWS Query Compatible error from header
66+
*
67+
* @param ResponseInterface $response
68+
* @return array|null Returns ['code' => string, 'type' => string] or null
69+
*/
70+
private function parseAwsQueryCompatibleHeader(ResponseInterface $response): ?array
71+
{
72+
$queryError = $response->getHeaderLine('x-amzn-query-error');
73+
$parts = explode(';', $queryError);
74+
75+
if (count($parts) === 2 && $parts[0] && $parts[1]) {
76+
return [
77+
'code' => $parts[0],
78+
'type' => $parts[1]
79+
];
80+
}
81+
82+
return null;
83+
}
84+
85+
/**
86+
* Parse error code from response body
87+
*
88+
* @param array|null $parsedBody
89+
* @return string|null
90+
*/
91+
private function parseErrorFromBody(?array $parsedBody): ?string
92+
{
93+
if (!$parsedBody
94+
|| (!isset($parsedBody['code']) && !isset($parsedBody['__type']))
95+
) {
96+
return null;
97+
}
98+
99+
$error_code = $parsedBody['code'] ?? $parsedBody['__type'];
100+
return $this->extractErrorCode($error_code);
101+
}
102+
103+
/**
104+
* Extract error code from raw error string containing # and/or : delimiters
105+
*
106+
* @param string $rawErrorCode
107+
* @return string
108+
*/
109+
private function extractErrorCode(string $rawErrorCode): string
110+
{
111+
// Handle format with both # and uri (e.g., "namespace#http://foo-bar")
112+
if (str_contains($rawErrorCode, ':') && str_contains($rawErrorCode, '#')) {
113+
$start = strpos($rawErrorCode, '#') + 1;
114+
$end = strpos($rawErrorCode, ':', $start);
115+
return substr($rawErrorCode, $start, $end - $start);
116+
}
117+
118+
// Handle format with uri only : (e.g., "ErrorCode:http://foo-bar.com/baz")
119+
if (str_contains($rawErrorCode, ':')) {
120+
return substr($rawErrorCode, 0, strpos($rawErrorCode, ':'));
121+
}
122+
123+
// Handle format with only # (e.g., "namespace#ErrorCode")
124+
if (str_contains($rawErrorCode, '#')) {
125+
return substr($rawErrorCode, strpos($rawErrorCode, '#') + 1);
126+
}
127+
128+
return $rawErrorCode;
129+
}
130+
42131
protected function payload(
43132
ResponseInterface $response,
44133
StructureShape $member
45134
) {
46-
$jsonBody = $this->parseJson($response->getBody(), $response);
47-
48-
if ($jsonBody) {
49-
return $this->parser->parse($member, $jsonBody);
135+
$body = $response->getBody();
136+
if (!$body->isSeekable() || $body->getSize()) {
137+
$jsonBody = $this->parseJson($body, $response);
138+
} else {
139+
$jsonBody = (string) $body;
50140
}
141+
142+
return $this->parser->parse($member, $jsonBody);
51143
}
52144
}

aws/aws-sdk-php/src/Api/ErrorParser/RestJsonErrorParser.php

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,26 +30,17 @@ public function __invoke(
3030

3131
// Merge in error data from the JSON body
3232
if ($json = $data['parsed']) {
33-
$data = array_replace($data, $json);
33+
$data = array_replace($json, $data);
3434
}
3535

3636
// Correct error type from services like Amazon Glacier
3737
if (!empty($data['type'])) {
3838
$data['type'] = strtolower($data['type']);
3939
}
4040

41-
// Retrieve the error code from services like Amazon Elastic Transcoder
42-
if ($code = $response->getHeaderLine('x-amzn-errortype')) {
43-
$colon = strpos($code, ':');
44-
$data['code'] = $colon ? substr($code, 0, $colon) : $code;
45-
}
46-
4741
// Retrieve error message directly
48-
$data['message'] = isset($data['parsed']['message'])
49-
? $data['parsed']['message']
50-
: (isset($data['parsed']['Message'])
51-
? $data['parsed']['Message']
52-
: null);
42+
$data['message'] = $data['parsed']['message']
43+
?? ($data['parsed']['Message'] ?? null);
5344

5445
$this->populateShape($data, $response, $command);
5546

aws/aws-sdk-php/src/Api/Parser/AbstractRestParser.php

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,9 @@ public function __invoke(
5555
}
5656
}
5757

58+
$body = $response->getBody();
5859
if (!$payload
59-
&& $response->getBody()->getSize() > 0
60+
&& (!$body->isSeekable() || $body->getSize())
6061
&& count($output->getMembers()) > 0
6162
) {
6263
// if no payload was found, then parse the contents of the body
@@ -73,20 +74,26 @@ private function extractPayload(
7374
array &$result
7475
) {
7576
$member = $output->getMember($payload);
77+
$body = $response->getBody();
7678

7779
if (!empty($member['eventstream'])) {
7880
$result[$payload] = new EventParsingIterator(
79-
$response->getBody(),
81+
$body,
8082
$member,
8183
$this
8284
);
83-
} else if ($member instanceof StructureShape) {
84-
// Structure members parse top-level data into a specific key.
85+
} elseif ($member instanceof StructureShape) {
86+
//Unions must have at least one member set to a non-null value
87+
// If the body is empty, we can assume it is unset
88+
if (!empty($member['union']) && ($body->isSeekable() && !$body->getSize())) {
89+
return;
90+
}
91+
8592
$result[$payload] = [];
8693
$this->payload($response, $member, $result[$payload]);
8794
} else {
88-
// Streaming data is just the stream from the response body.
89-
$result[$payload] = $response->getBody();
95+
// Always set the payload to the body stream, regardless of content
96+
$result[$payload] = $body;
9097
}
9198
}
9299

@@ -100,13 +107,21 @@ private function extractHeader(
100107
&$result
101108
) {
102109
$value = $response->getHeaderLine($shape['locationName'] ?: $name);
110+
// Empty headers should not be deserialized
111+
if ($value === null || $value === '') {
112+
return;
113+
}
103114

104115
switch ($shape->getType()) {
105116
case 'float':
106117
case 'double':
107-
$value = (float) $value;
118+
$value = match ($value) {
119+
'NaN', 'Infinity', '-Infinity' => $value,
120+
default => (float) $value
121+
};
108122
break;
109123
case 'long':
124+
case 'integer':
110125
$value = (int) $value;
111126
break;
112127
case 'boolean':
@@ -143,6 +158,23 @@ private function extractHeader(
143158
//output structure.
144159
return;
145160
}
161+
case 'list':
162+
$listMember = $shape->getMember();
163+
$type = $listMember->getType();
164+
165+
// Only boolean lists require special handling
166+
// other types can be returned as-is
167+
if ($type !== 'boolean') {
168+
break;
169+
}
170+
171+
$items = array_map('trim', explode(',', $value));
172+
$value = array_map(
173+
static fn($item) => filter_var($item, FILTER_VALIDATE_BOOLEAN),
174+
$items
175+
);
176+
177+
break;
146178
}
147179

148180
$result[$name] = $value;

aws/aws-sdk-php/src/Api/Parser/JsonParser.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,10 @@ public function parse(Shape $shape, $value)
5050
$values = $shape->getValue();
5151
$target = [];
5252
foreach ($value as $k => $v) {
53-
$target[$k] = $this->parse($values, $v);
53+
// null map values should not be deserialized
54+
if (!is_null($v)) {
55+
$target[$k] = $this->parse($values, $v);
56+
}
5457
}
5558
return $target;
5659

aws/aws-sdk-php/src/Api/Parser/MetadataParserTrait.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,18 @@ protected function extractHeader(
1717
&$result
1818
) {
1919
$value = $response->getHeaderLine($shape['locationName'] ?: $name);
20+
// Empty values should not be deserialized
21+
if ($value === null || $value === '') {
22+
return;
23+
}
2024

2125
switch ($shape->getType()) {
2226
case 'float':
2327
case 'double':
2428
$value = (float) $value;
2529
break;
2630
case 'long':
31+
case 'integer':
2732
$value = (int) $value;
2833
break;
2934
case 'boolean':

aws/aws-sdk-php/src/Api/Parser/NonSeekableStreamDecodingEventStreamIterator.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,21 @@ protected function readAndHashBytes($num): string
6464
while (!empty($this->tempBuffer) && $num > 0) {
6565
$byte = array_shift($this->tempBuffer);
6666
$bytes .= $byte;
67-
$num = $num - 1;
67+
$num -= 1;
68+
}
69+
70+
// Loop until we've read the expected number of bytes
71+
while ($num > 0 && !$this->stream->eof()) {
72+
$chunk = $this->stream->read($num);
73+
$chunkLen = strlen($chunk);
74+
$bytes .= $chunk;
75+
$num -= $chunkLen;
76+
77+
if ($chunkLen === 0) {
78+
break; // Prevent infinite loop on unexpected EOF
79+
}
6880
}
6981

70-
$bytes = $bytes . $this->stream->read($num);
7182
hash_update($this->hashContext, $bytes);
7283

7384
return $bytes;

aws/aws-sdk-php/src/Api/Parser/QueryParser.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,15 @@ public function __invoke(
4040
ResponseInterface $response
4141
) {
4242
$output = $this->api->getOperation($command->getName())->getOutput();
43-
$xml = $this->parseXml($response->getBody(), $response);
43+
$body = $response->getBody();
44+
$xml = !$body->isSeekable() || $body->getSize()
45+
? $this->parseXml($body, $response)
46+
: null;
47+
48+
// Empty request bodies should not be deserialized.
49+
if (is_null($xml)) {
50+
return new Result();
51+
}
4452

4553
if ($this->honorResultWrapper && $output['resultWrapper']) {
4654
$xml = $xml->{$output['resultWrapper']};

aws/aws-sdk-php/src/Api/Parser/RestJsonParser.php

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,25 @@ protected function payload(
2828
StructureShape $member,
2929
array &$result
3030
) {
31-
$jsonBody = $this->parseJson($response->getBody(), $response);
31+
$responseBody = (string) $response->getBody();
3232

33-
if ($jsonBody) {
34-
$result += $this->parser->parse($member, $jsonBody);
33+
// Parse JSON if we have content
34+
$parsedJson = null;
35+
if (!empty($responseBody)) {
36+
$parsedJson = $this->parseJson($responseBody, $response);
37+
} else {
38+
// An empty response body should be deserialized as null
39+
$result = $parsedJson;
40+
return;
41+
}
42+
43+
$parsedBody = $this->parser->parse($member, $parsedJson);
44+
if (is_string($parsedBody) && $member['document']) {
45+
// Document types can be strings: replace entire result
46+
$result = $parsedBody;
47+
} else {
48+
// Merge array/object results into existing result
49+
$result = array_merge($result, (array) $parsedBody);
3550
}
3651
}
3752

0 commit comments

Comments
 (0)