1
0
Fork 0

Fix race condition where multiple http requests requiring auth end up failing, fixes #10763

pull/10768/head
Jordi Boggiano 2022-05-11 13:06:59 +02:00
parent 1d0fa93495
commit aeb204bb1d
No known key found for this signature in database
GPG Key ID: 7BBD42C429EC80BC
3 changed files with 12 additions and 4 deletions

View File

@ -79,11 +79,12 @@ class AuthHelper
* @param int $statusCode HTTP status code that triggered this call * @param int $statusCode HTTP status code that triggered this call
* @param string|null $reason a message/description explaining why this was called * @param string|null $reason a message/description explaining why this was called
* @param string[] $headers * @param string[] $headers
* @param int $retryCount the amount of retries already done on this URL
* @return array|null containing retry (bool) and storeAuth (string|bool) keys, if retry is true the request should be * @return array|null containing retry (bool) and storeAuth (string|bool) keys, if retry is true the request should be
* retried, if storeAuth is true then on a successful retry the authentication should be persisted to auth.json * retried, if storeAuth is true then on a successful retry the authentication should be persisted to auth.json
* @phpstan-return ?array{retry: bool, storeAuth: string|bool} * @phpstan-return ?array{retry: bool, storeAuth: string|bool}
*/ */
public function promptAuthIfNeeded($url, $origin, $statusCode, $reason = null, $headers = array()) public function promptAuthIfNeeded($url, $origin, $statusCode, $reason = null, $headers = array(), $retryCount = 0)
{ {
$storeAuth = false; $storeAuth = false;
@ -200,8 +201,15 @@ class AuthHelper
throw new TransportException($message, $statusCode); throw new TransportException($message, $statusCode);
} }
// fail if we already have auth // fail if we already have auth
if ($this->io->hasAuthentication($origin)) { if ($this->io->hasAuthentication($origin)) {
// if two or more requests are started together for the same host, and the first
// received authentication already, we let the others retry before failing them
if ($retryCount === 0) {
return array('retry' => true, 'storeAuth' => false);
}
throw new TransportException("Invalid credentials for '" . $url . "', aborting.", $statusCode); throw new TransportException("Invalid credentials for '" . $url . "', aborting.", $statusCode);
} }

View File

@ -528,7 +528,7 @@ class CurlDownloader
private function isAuthenticatedRetryNeeded(array $job, Response $response) private function isAuthenticatedRetryNeeded(array $job, Response $response)
{ {
if (in_array($response->getStatusCode(), array(401, 403)) && $job['attributes']['retryAuthFailure']) { if (in_array($response->getStatusCode(), array(401, 403)) && $job['attributes']['retryAuthFailure']) {
$result = $this->authHelper->promptAuthIfNeeded($job['url'], $job['origin'], $response->getStatusCode(), $response->getStatusMessage(), $response->getHeaders()); $result = $this->authHelper->promptAuthIfNeeded($job['url'], $job['origin'], $response->getStatusCode(), $response->getStatusMessage(), $response->getHeaders(), $job['attributes']['retries']);
if ($result['retry']) { if ($result['retry']) {
return $result; return $result;
@ -560,7 +560,7 @@ class CurlDownloader
if ($needsAuthRetry) { if ($needsAuthRetry) {
if ($job['attributes']['retryAuthFailure']) { if ($job['attributes']['retryAuthFailure']) {
$result = $this->authHelper->promptAuthIfNeeded($job['url'], $job['origin'], 401); $result = $this->authHelper->promptAuthIfNeeded($job['url'], $job['origin'], 401, null, array(), $job['attributes']['retries']);
if ($result['retry']) { if ($result['retry']) {
return $result; return $result;
} }

View File

@ -645,7 +645,7 @@ class RemoteFilesystem
*/ */
protected function promptAuthAndRetry($httpStatus, $reason = null, $headers = array()) protected function promptAuthAndRetry($httpStatus, $reason = null, $headers = array())
{ {
$result = $this->authHelper->promptAuthIfNeeded($this->fileUrl, $this->originUrl, $httpStatus, $reason, $headers); $result = $this->authHelper->promptAuthIfNeeded($this->fileUrl, $this->originUrl, $httpStatus, $reason, $headers, 1 /** always pass 1 as RemoteFilesystem is single threaded there is no race condition possible */);
$this->storeAuth = $result['storeAuth']; $this->storeAuth = $result['storeAuth'];
$this->retry = $result['retry']; $this->retry = $result['retry'];