|
@@ -35,6 +35,7 @@ class RemoteFilesystem
|
|
private $options;
|
|
private $options;
|
|
private $disableTls = false;
|
|
private $disableTls = false;
|
|
private $retryTls = true;
|
|
private $retryTls = true;
|
|
|
|
+ private $retryAuthFailure;
|
|
|
|
|
|
/**
|
|
/**
|
|
* Constructor.
|
|
* Constructor.
|
|
@@ -128,12 +129,19 @@ class RemoteFilesystem
|
|
$this->fileName = $fileName;
|
|
$this->fileName = $fileName;
|
|
$this->progress = $progress;
|
|
$this->progress = $progress;
|
|
$this->lastProgress = null;
|
|
$this->lastProgress = null;
|
|
|
|
+ $this->retryAuthFailure = true;
|
|
|
|
|
|
// capture username/password from URL if there is one
|
|
// capture username/password from URL if there is one
|
|
if (preg_match('{^https?://(.+):(.+)@([^/]+)}i', $fileUrl, $match)) {
|
|
if (preg_match('{^https?://(.+):(.+)@([^/]+)}i', $fileUrl, $match)) {
|
|
$this->io->setAuthentication($originUrl, urldecode($match[1]), urldecode($match[2]));
|
|
$this->io->setAuthentication($originUrl, urldecode($match[1]), urldecode($match[2]));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (isset($additionalOptions['retry-auth-failure'])) {
|
|
|
|
+ $this->retryAuthFailure = (bool) $additionalOptions['retry-auth-failure'];
|
|
|
|
+
|
|
|
|
+ unset($additionalOptions['retry-auth-failure']);
|
|
|
|
+ }
|
|
|
|
+
|
|
$options = $this->getOptionsForUrl($originUrl, $additionalOptions, $expectedCommonName);
|
|
$options = $this->getOptionsForUrl($originUrl, $additionalOptions, $expectedCommonName);
|
|
|
|
|
|
if ($this->io->isDebug()) {
|
|
if ($this->io->isDebug()) {
|
|
@@ -143,6 +151,9 @@ class RemoteFilesystem
|
|
$fileUrl .= (false === strpos($fileUrl, '?') ? '?' : '&') . 'access_token='.$options['github-token'];
|
|
$fileUrl .= (false === strpos($fileUrl, '?') ? '?' : '&') . 'access_token='.$options['github-token'];
|
|
unset($options['github-token']);
|
|
unset($options['github-token']);
|
|
}
|
|
}
|
|
|
|
+ if (isset($options['http'])) {
|
|
|
|
+ $options['http']['ignore_errors'] = true;
|
|
|
|
+ }
|
|
$ctx = StreamContextFactory::getContext($fileUrl, $options, array('notification' => array($this, 'callbackGet')));
|
|
$ctx = StreamContextFactory::getContext($fileUrl, $options, array('notification' => array($this, 'callbackGet')));
|
|
|
|
|
|
if ($this->progress) {
|
|
if ($this->progress) {
|
|
@@ -164,6 +175,10 @@ class RemoteFilesystem
|
|
if ($e instanceof TransportException && !empty($http_response_header[0])) {
|
|
if ($e instanceof TransportException && !empty($http_response_header[0])) {
|
|
$e->setHeaders($http_response_header);
|
|
$e->setHeaders($http_response_header);
|
|
}
|
|
}
|
|
|
|
+ if ($e instanceof TransportException && $result !== false) {
|
|
|
|
+ $e->setResponse($result);
|
|
|
|
+ }
|
|
|
|
+ $result = false;
|
|
}
|
|
}
|
|
if ($errorMessage && !ini_get('allow_url_fopen')) {
|
|
if ($errorMessage && !ini_get('allow_url_fopen')) {
|
|
$errorMessage = 'allow_url_fopen must be enabled in php.ini ('.$errorMessage.')';
|
|
$errorMessage = 'allow_url_fopen must be enabled in php.ini ('.$errorMessage.')';
|
|
@@ -173,10 +188,16 @@ class RemoteFilesystem
|
|
throw $e;
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
|
|
- // fix for 5.4.0 https://bugs.php.net/bug.php?id=61336
|
|
|
|
|
|
+ // fail 4xx and 5xx responses and capture the response
|
|
if (!empty($http_response_header[0]) && preg_match('{^HTTP/\S+ ([45]\d\d)}i', $http_response_header[0], $match)) {
|
|
if (!empty($http_response_header[0]) && preg_match('{^HTTP/\S+ ([45]\d\d)}i', $http_response_header[0], $match)) {
|
|
- $result = false;
|
|
|
|
$errorCode = $match[1];
|
|
$errorCode = $match[1];
|
|
|
|
+ if (!$this->retry) {
|
|
|
|
+ $e = new TransportException('The "'.$this->fileUrl.'" file could not be downloaded ('.$http_response_header[0].')', $errorCode);
|
|
|
|
+ $e->setHeaders($http_response_header);
|
|
|
|
+ $e->setResponse($result);
|
|
|
|
+ throw $e;
|
|
|
|
+ }
|
|
|
|
+ $result = false;
|
|
}
|
|
}
|
|
|
|
|
|
// decode gzip
|
|
// decode gzip
|
|
@@ -272,6 +293,11 @@ class RemoteFilesystem
|
|
case STREAM_NOTIFY_FAILURE:
|
|
case STREAM_NOTIFY_FAILURE:
|
|
case STREAM_NOTIFY_AUTH_REQUIRED:
|
|
case STREAM_NOTIFY_AUTH_REQUIRED:
|
|
if (401 === $messageCode) {
|
|
if (401 === $messageCode) {
|
|
|
|
+ // Bail if the caller is going to handle authentication failures itself.
|
|
|
|
+ if (!$this->retryAuthFailure) {
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
if (!$this->io->isInteractive()) {
|
|
if (!$this->io->isInteractive()) {
|
|
$message = "The '" . $this->fileUrl . "' URL required authentication.\nYou must be using the interactive console";
|
|
$message = "The '" . $this->fileUrl . "' URL required authentication.\nYou must be using the interactive console";
|
|
|
|
|
|
@@ -281,12 +307,7 @@ class RemoteFilesystem
|
|
$this->promptAuthAndRetry();
|
|
$this->promptAuthAndRetry();
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
-
|
|
|
|
- if ($notificationCode === STREAM_NOTIFY_AUTH_REQUIRED) {
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- throw new TransportException('The "'.$this->fileUrl.'" file could not be downloaded ('.trim($message).')', $messageCode);
|
|
|
|
|
|
+ break;
|
|
|
|
|
|
case STREAM_NOTIFY_AUTH_RESULT:
|
|
case STREAM_NOTIFY_AUTH_RESULT:
|
|
if (403 === $messageCode) {
|
|
if (403 === $messageCode) {
|