Merge branch '1.7'
commit
db13cc4960
|
@ -8,16 +8,16 @@
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "composer/ca-bundle",
|
"name": "composer/ca-bundle",
|
||||||
"version": "1.1.1",
|
"version": "1.1.2",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/composer/ca-bundle.git",
|
"url": "https://github.com/composer/ca-bundle.git",
|
||||||
"reference": "d2c0a83b7533d6912e8d516756ebd34f893e9169"
|
"reference": "46afded9720f40b9dc63542af4e3e43a1177acb0"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/composer/ca-bundle/zipball/d2c0a83b7533d6912e8d516756ebd34f893e9169",
|
"url": "https://api.github.com/repos/composer/ca-bundle/zipball/46afded9720f40b9dc63542af4e3e43a1177acb0",
|
||||||
"reference": "d2c0a83b7533d6912e8d516756ebd34f893e9169",
|
"reference": "46afded9720f40b9dc63542af4e3e43a1177acb0",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -60,7 +60,7 @@
|
||||||
"ssl",
|
"ssl",
|
||||||
"tls"
|
"tls"
|
||||||
],
|
],
|
||||||
"time": "2018-03-29T19:57:20+00:00"
|
"time": "2018-08-08T08:57:40+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "composer/semver",
|
"name": "composer/semver",
|
||||||
|
|
|
@ -69,6 +69,13 @@ class JsonFormatter
|
||||||
$l = strlen($match[1]);
|
$l = strlen($match[1]);
|
||||||
|
|
||||||
if ($l % 2) {
|
if ($l % 2) {
|
||||||
|
$code = hexdec($match[2]);
|
||||||
|
// 0xD800..0xDFFF denotes UTF-16 surrogate pair which won't be unescaped
|
||||||
|
// see https://github.com/composer/composer/issues/7510
|
||||||
|
if (0xD800 <= $code && 0xDFFF >= $code) {
|
||||||
|
return $match[0];
|
||||||
|
}
|
||||||
|
|
||||||
return str_repeat('\\', $l - 1) . mb_convert_encoding(
|
return str_repeat('\\', $l - 1) . mb_convert_encoding(
|
||||||
pack('H*', $match[2]),
|
pack('H*', $match[2]),
|
||||||
'UTF-8',
|
'UTF-8',
|
||||||
|
|
|
@ -378,12 +378,7 @@ class GitHubDriver extends VcsDriver
|
||||||
return $this->attemptCloneFallback();
|
return $this->attemptCloneFallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
$rateLimited = false;
|
$rateLimited = $githubUtil->isRateLimited($e->getHeaders());
|
||||||
foreach ($e->getHeaders() as $header) {
|
|
||||||
if (preg_match('{^X-RateLimit-Remaining: *0$}i', trim($header))) {
|
|
||||||
$rateLimited = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->io->hasAuthentication($this->originUrl)) {
|
if (!$this->io->hasAuthentication($this->originUrl)) {
|
||||||
if (!$this->io->isInteractive()) {
|
if (!$this->io->isInteractive()) {
|
||||||
|
@ -397,7 +392,7 @@ class GitHubDriver extends VcsDriver
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($rateLimited) {
|
if ($rateLimited) {
|
||||||
$rateLimit = $this->getRateLimit($e->getHeaders());
|
$rateLimit = $githubUtil->getRateLimit($e->getHeaders());
|
||||||
$this->io->writeError(sprintf(
|
$this->io->writeError(sprintf(
|
||||||
'<error>GitHub API limit (%d calls/hr) is exhausted. You are already authorized so you have to wait until %s before doing more requests</error>',
|
'<error>GitHub API limit (%d calls/hr) is exhausted. You are already authorized so you have to wait until %s before doing more requests</error>',
|
||||||
$rateLimit['limit'],
|
$rateLimit['limit'],
|
||||||
|
@ -413,39 +408,6 @@ class GitHubDriver extends VcsDriver
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Extract ratelimit from response.
|
|
||||||
*
|
|
||||||
* @param array $headers Headers from Composer\Downloader\TransportException.
|
|
||||||
*
|
|
||||||
* @return array Associative array with the keys limit and reset.
|
|
||||||
*/
|
|
||||||
protected function getRateLimit(array $headers)
|
|
||||||
{
|
|
||||||
$rateLimit = array(
|
|
||||||
'limit' => '?',
|
|
||||||
'reset' => '?',
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach ($headers as $header) {
|
|
||||||
$header = trim($header);
|
|
||||||
if (false === strpos($header, 'X-RateLimit-')) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
list($type, $value) = explode(':', $header, 2);
|
|
||||||
switch ($type) {
|
|
||||||
case 'X-RateLimit-Limit':
|
|
||||||
$rateLimit['limit'] = (int) trim($value);
|
|
||||||
break;
|
|
||||||
case 'X-RateLimit-Reset':
|
|
||||||
$rateLimit['reset'] = date('Y-m-d H:i:s', (int) trim($value));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $rateLimit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch root identifier from GitHub
|
* Fetch root identifier from GitHub
|
||||||
*
|
*
|
||||||
|
|
|
@ -126,4 +126,55 @@ class GitHub
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract ratelimit from response.
|
||||||
|
*
|
||||||
|
* @param array $headers Headers from Composer\Downloader\TransportException.
|
||||||
|
*
|
||||||
|
* @return array Associative array with the keys limit and reset.
|
||||||
|
*/
|
||||||
|
public function getRateLimit(array $headers)
|
||||||
|
{
|
||||||
|
$rateLimit = array(
|
||||||
|
'limit' => '?',
|
||||||
|
'reset' => '?',
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($headers as $header) {
|
||||||
|
$header = trim($header);
|
||||||
|
if (false === strpos($header, 'X-RateLimit-')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
list($type, $value) = explode(':', $header, 2);
|
||||||
|
switch ($type) {
|
||||||
|
case 'X-RateLimit-Limit':
|
||||||
|
$rateLimit['limit'] = (int) trim($value);
|
||||||
|
break;
|
||||||
|
case 'X-RateLimit-Reset':
|
||||||
|
$rateLimit['reset'] = date('Y-m-d H:i:s', (int) trim($value));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rateLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds whether a request failed due to rate limiting
|
||||||
|
*
|
||||||
|
* @param array $headers Headers from Composer\Downloader\TransportException.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isRateLimited(array $headers)
|
||||||
|
{
|
||||||
|
foreach ($headers as $header) {
|
||||||
|
if (preg_match('{^X-RateLimit-Remaining: *0$}i', trim($header))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -327,7 +327,7 @@ class RemoteFilesystem
|
||||||
$warning = $data['warning'];
|
$warning = $data['warning'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->promptAuthAndRetry($statusCode, $this->findStatusMessage($http_response_header), $warning);
|
$this->promptAuthAndRetry($statusCode, $this->findStatusMessage($http_response_header), $warning, $http_response_header);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -639,11 +639,35 @@ class RemoteFilesystem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function promptAuthAndRetry($httpStatus, $reason = null, $warning = null)
|
protected function promptAuthAndRetry($httpStatus, $reason = null, $warning = null, $headers = array())
|
||||||
{
|
{
|
||||||
if ($this->config && in_array($this->originUrl, $this->config->get('github-domains'), true)) {
|
if ($this->config && in_array($this->originUrl, $this->config->get('github-domains'), true)) {
|
||||||
$message = "\n".'Could not fetch '.$this->fileUrl.', please create a GitHub OAuth token '.($httpStatus === 404 ? 'to access private repos' : 'to go over the API rate limit');
|
|
||||||
$gitHubUtil = new GitHub($this->io, $this->config, null);
|
$gitHubUtil = new GitHub($this->io, $this->config, null);
|
||||||
|
$message = "\n";
|
||||||
|
|
||||||
|
$rateLimited = $gitHubUtil->isRateLimited($headers);
|
||||||
|
if ($rateLimited) {
|
||||||
|
$rateLimit = $gitHubUtil->getRateLimit($headers);
|
||||||
|
if ($this->io->hasAuthentication($this->originUrl)) {
|
||||||
|
$message = 'Review your configured GitHub OAuth token or enter a new one to go over the API rate limit.';
|
||||||
|
} else {
|
||||||
|
$message = 'Create a GitHub OAuth token to go over the API rate limit.';
|
||||||
|
}
|
||||||
|
|
||||||
|
$message = sprintf(
|
||||||
|
'GitHub API limit (%d calls/hr) is exhausted, could not fetch '.$this->fileUrl.'. '.$message.' You can also wait until %s for the rate limit to reset.',
|
||||||
|
$rateLimit['limit'],
|
||||||
|
$rateLimit['reset']
|
||||||
|
)."\n";
|
||||||
|
} else {
|
||||||
|
$message .= 'Could not fetch '.$this->fileUrl.', please ';
|
||||||
|
if ($this->io->hasAuthentication($this->originUrl)) {
|
||||||
|
$message .= 'review your configured GitHub OAuth token or enter a new one to access private repos';
|
||||||
|
} else {
|
||||||
|
$message .= 'create a GitHub OAuth token to access private repos';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!$gitHubUtil->authorizeOAuth($this->originUrl)
|
if (!$gitHubUtil->authorizeOAuth($this->originUrl)
|
||||||
&& (!$this->io->isInteractive() || !$gitHubUtil->authorizeOAuthInteractively($this->originUrl, $message))
|
&& (!$this->io->isInteractive() || !$gitHubUtil->authorizeOAuthInteractively($this->originUrl, $message))
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -18,33 +18,31 @@ use PHPUnit\Framework\TestCase;
|
||||||
class JsonFormatterTest extends TestCase
|
class JsonFormatterTest extends TestCase
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Test if \u0119 (196+153) will get correctly formatted
|
* Test if \u0119 will get correctly formatted (unescaped)
|
||||||
* See ticket #2613
|
* https://github.com/composer/composer/issues/2613
|
||||||
*/
|
*/
|
||||||
public function testUnicodeWithPrependedSlash()
|
public function testUnicodeWithPrependedSlash()
|
||||||
{
|
{
|
||||||
if (!extension_loaded('mbstring')) {
|
if (!extension_loaded('mbstring')) {
|
||||||
$this->markTestSkipped('Test requires the mbstring extension');
|
$this->markTestSkipped('Test requires the mbstring extension');
|
||||||
}
|
}
|
||||||
|
$backslash = chr(92);
|
||||||
$data = '"' . chr(92) . chr(92) . chr(92) . 'u0119"';
|
$data = '"' . $backslash . $backslash . $backslash . 'u0119"';
|
||||||
$encodedData = JsonFormatter::format($data, true, true);
|
$expected = '"' . $backslash . $backslash . 'ę"';
|
||||||
$expected = '34+92+92+196+153+34';
|
$this->assertEquals($expected, JsonFormatter::format($data, true, true));
|
||||||
$this->assertEquals($expected, $this->getCharacterCodes($encodedData));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert string to character codes split by a plus sign
|
* Surrogate pairs are intentionally skipped and not unescaped
|
||||||
* @param string $string
|
* https://github.com/composer/composer/issues/7510
|
||||||
* @return string
|
|
||||||
*/
|
*/
|
||||||
protected function getCharacterCodes($string)
|
public function testUtf16SurrogatePair()
|
||||||
{
|
{
|
||||||
$codes = array();
|
if (!extension_loaded('mbstring')) {
|
||||||
for ($i = 0; $i < strlen($string); $i++) {
|
$this->markTestSkipped('Test requires the mbstring extension');
|
||||||
$codes[] = ord($string[$i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return implode('+', $codes);
|
$escaped = '"\ud83d\ude00"';
|
||||||
|
$this->assertEquals($escaped, JsonFormatter::format($escaped, true, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue