+ * @author Nicolas Grekas
+ */
+class CurlDownloader
+{
+ private $multiHandle;
+ private $shareHandle;
+ private $jobs = array();
+ /** @var IOInterface */
+ private $io;
+ /** @var Config */
+ private $config;
+ /** @var AuthHelper */
+ private $authHelper;
+ private $selectTimeout = 5.0;
+ private $maxRedirects = 20;
+ protected $multiErrors = array(
+ CURLM_BAD_HANDLE => array('CURLM_BAD_HANDLE', 'The passed-in handle is not a valid CURLM handle.'),
+ CURLM_BAD_EASY_HANDLE => array('CURLM_BAD_EASY_HANDLE', "An easy handle was not good/valid. It could mean that it isn't an easy handle at all, or possibly that the handle already is in used by this or another multi handle."),
+ CURLM_OUT_OF_MEMORY => array('CURLM_OUT_OF_MEMORY', 'You are doomed.'),
+ CURLM_INTERNAL_ERROR => array('CURLM_INTERNAL_ERROR', 'This can only be returned if libcurl bugs. Please report it to us!')
+ );
+
+ private static $options = array(
+ 'http' => array(
+ 'method' => CURLOPT_CUSTOMREQUEST,
+ 'content' => CURLOPT_POSTFIELDS,
+ 'proxy' => CURLOPT_PROXY,
+ 'header' => CURLOPT_HTTPHEADER,
+ ),
+ 'ssl' => array(
+ 'ciphers' => CURLOPT_SSL_CIPHER_LIST,
+ 'cafile' => CURLOPT_CAINFO,
+ 'capath' => CURLOPT_CAPATH,
+ ),
+ );
+
+ private static $timeInfo = array(
+ 'total_time' => true,
+ 'namelookup_time' => true,
+ 'connect_time' => true,
+ 'pretransfer_time' => true,
+ 'starttransfer_time' => true,
+ 'redirect_time' => true,
+ );
+
+ public function __construct(IOInterface $io, Config $config, array $options = array(), $disableTls = false)
+ {
+ $this->io = $io;
+ $this->config = $config;
+
+ $this->multiHandle = $mh = curl_multi_init();
+ if (function_exists('curl_multi_setopt')) {
+ curl_multi_setopt($mh, CURLMOPT_PIPELINING, /*CURLPIPE_HTTP1 | CURLPIPE_MULTIPLEX*/ 3);
+ if (defined('CURLMOPT_MAX_HOST_CONNECTIONS')) {
+ curl_multi_setopt($mh, CURLMOPT_MAX_HOST_CONNECTIONS, 8);
+ }
+ }
+
+ if (function_exists('curl_share_init')) {
+ $this->shareHandle = $sh = curl_share_init();
+ curl_share_setopt($sh, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE);
+ curl_share_setopt($sh, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
+ curl_share_setopt($sh, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
+ }
+
+ $this->authHelper = new AuthHelper($io, $config);
+ }
+
+ public function download($resolve, $reject, $origin, $url, $options, $copyTo = null)
+ {
+ $attributes = array();
+ if (isset($options['retry-auth-failure'])) {
+ $attributes['retryAuthFailure'] = $options['retry-auth-failure'];
+ unset($options['retry-auth-failure']);
+ }
+
+ return $this->initDownload($resolve, $reject, $origin, $url, $options, $copyTo, $attributes);
+ }
+
+ private function initDownload($resolve, $reject, $origin, $url, $options, $copyTo = null, array $attributes = array())
+ {
+ $attributes = array_merge(array(
+ 'retryAuthFailure' => true,
+ 'redirects' => 0,
+ 'storeAuth' => false,
+ ), $attributes);
+
+ $originalOptions = $options;
+
+ // check URL can be accessed (i.e. is not insecure), but allow insecure Packagist calls to $hashed providers as file integrity is verified with sha256
+ if (!preg_match('{^http://(repo\.)?packagist\.org/p/}', $url) || (false === strpos($url, '$') && false === strpos($url, '%24'))) {
+ $this->config->prohibitUrlByConfig($url, $this->io);
+ }
+
+ $curlHandle = curl_init();
+ $headerHandle = fopen('php://temp/maxmemory:32768', 'w+b');
+
+ if ($copyTo) {
+ $errorMessage = '';
+ set_error_handler(function ($code, $msg) use (&$errorMessage) {
+ if ($errorMessage) {
+ $errorMessage .= "\n";
+ }
+ $errorMessage .= preg_replace('{^fopen\(.*?\): }', '', $msg);
+ });
+ $bodyHandle = fopen($copyTo.'~', 'w+b');
+ restore_error_handler();
+ if (!$bodyHandle) {
+ throw new TransportException('The "'.$url.'" file could not be written to '.$copyTo.': '.$errorMessage);
+ }
+ } else {
+ $bodyHandle = @fopen('php://temp/maxmemory:524288', 'w+b');
+ }
+
+ curl_setopt($curlHandle, CURLOPT_URL, $url);
+ curl_setopt($curlHandle, CURLOPT_FOLLOWLOCATION, false);
+ //curl_setopt($curlHandle, CURLOPT_DNS_USE_GLOBAL_CACHE, false);
+ curl_setopt($curlHandle, CURLOPT_CONNECTTIMEOUT, 10);
+ curl_setopt($curlHandle, CURLOPT_TIMEOUT, 60);
+ curl_setopt($curlHandle, CURLOPT_WRITEHEADER, $headerHandle);
+ curl_setopt($curlHandle, CURLOPT_FILE, $bodyHandle);
+ curl_setopt($curlHandle, CURLOPT_ENCODING, "gzip");
+ curl_setopt($curlHandle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS);
+ if (defined('CURLOPT_SSL_FALSESTART')) {
+ curl_setopt($curlHandle, CURLOPT_SSL_FALSESTART, true);
+ }
+ if (function_exists('curl_share_init')) {
+ curl_setopt($curlHandle, CURLOPT_SHARE, $this->shareHandle);
+ }
+
+ if (!isset($options['http']['header'])) {
+ $options['http']['header'] = array();
+ }
+
+ $options['http']['header'] = array_diff($options['http']['header'], array('Connection: close'));
+ $options['http']['header'][] = 'Connection: keep-alive';
+
+ $version = curl_version();
+ $features = $version['features'];
+ if (0 === strpos($url, 'https://') && \defined('CURL_VERSION_HTTP2') && \defined('CURL_HTTP_VERSION_2_0') && (CURL_VERSION_HTTP2 & $features)) {
+ curl_setopt($curlHandle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
+ }
+
+ $options['http']['header'] = $this->authHelper->addAuthenticationHeader($options['http']['header'], $origin, $url);
+ $options = StreamContextFactory::initOptions($url, $options);
+
+ foreach (self::$options as $type => $curlOptions) {
+ foreach ($curlOptions as $name => $curlOption) {
+ if (isset($options[$type][$name])) {
+ curl_setopt($curlHandle, $curlOption, $options[$type][$name]);
+ }
+ }
+ }
+
+ $progress = array_diff_key(curl_getinfo($curlHandle), self::$timeInfo);
+
+ $this->jobs[(int) $curlHandle] = array(
+ 'url' => $url,
+ 'origin' => $origin,
+ 'attributes' => $attributes,
+ 'options' => $originalOptions,
+ 'progress' => $progress,
+ 'curlHandle' => $curlHandle,
+ 'filename' => $copyTo,
+ 'headerHandle' => $headerHandle,
+ 'bodyHandle' => $bodyHandle,
+ 'resolve' => $resolve,
+ 'reject' => $reject,
+ );
+
+ $usingProxy = !empty($options['http']['proxy']) ? ' using proxy ' . $options['http']['proxy'] : '';
+ $ifModified = false !== strpos(strtolower(implode(',', $options['http']['header'])), 'if-modified-since:') ? ' if modified' : '';
+ if ($attributes['redirects'] === 0) {
+ $this->io->writeError('Downloading ' . $url . $usingProxy . $ifModified, true, IOInterface::DEBUG);
+ }
+
+ $this->checkCurlResult(curl_multi_add_handle($this->multiHandle, $curlHandle));
+// TODO progress
+ //$params['notification'](STREAM_NOTIFY_RESOLVE, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0, false);
+ }
+
+ public function tick()
+ {
+ if (!$this->jobs) {
+ return;
+ }
+
+ $active = true;
+ $this->checkCurlResult(curl_multi_exec($this->multiHandle, $active));
+ if (-1 === curl_multi_select($this->multiHandle, $this->selectTimeout)) {
+ // sleep in case select returns -1 as it can happen on old php versions or some platforms where curl does not manage to do the select
+ usleep(150);
+ }
+
+ while ($progress = curl_multi_info_read($this->multiHandle)) {
+ $curlHandle = $progress['handle'];
+ $i = (int) $curlHandle;
+ if (!isset($this->jobs[$i])) {
+ continue;
+ }
+
+ $progress = array_diff_key(curl_getinfo($curlHandle), self::$timeInfo);
+ $job = $this->jobs[$i];
+ unset($this->jobs[$i]);
+ curl_multi_remove_handle($this->multiHandle, $curlHandle);
+ $error = curl_error($curlHandle);
+ $errno = curl_errno($curlHandle);
+ curl_close($curlHandle);
+
+ $headers = null;
+ $statusCode = null;
+ $response = null;
+ try {
+// TODO progress
+ //$this->onProgress($curlHandle, $job['callback'], $progress, $job['progress']);
+ if (CURLE_OK !== $errno || $error) {
+ throw new TransportException($error);
+ }
+
+ $statusCode = $progress['http_code'];
+ rewind($job['headerHandle']);
+ $headers = explode("\r\n", rtrim(stream_get_contents($job['headerHandle'])));
+ fclose($job['headerHandle']);
+
+ // prepare response object
+ if ($job['filename']) {
+ fclose($job['bodyHandle']);
+ $response = new Response(array('url' => $progress['url']), $statusCode, $headers, $job['filename'].'~');
+ $this->io->writeError('['.$statusCode.'] '.$progress['url'], true, IOInterface::DEBUG);
+ } else {
+ rewind($job['bodyHandle']);
+ $contents = stream_get_contents($job['bodyHandle']);
+ fclose($job['bodyHandle']);
+ $response = new Response(array('url' => $progress['url']), $statusCode, $headers, $contents);
+ $this->io->writeError('['.$statusCode.'] '.$progress['url'], true, IOInterface::DEBUG);
+ }
+
+ $result = $this->isAuthenticatedRetryNeeded($job, $response);
+ if ($result['retry']) {
+ if ($job['filename']) {
+ @unlink($job['filename'].'~');
+ }
+
+ $this->restartJob($job, $job['url'], array('storeAuth' => $result['storeAuth']));
+ continue;
+ }
+
+ // handle 3xx redirects, 304 Not Modified is excluded
+ if ($statusCode >= 300 && $statusCode <= 399 && $statusCode !== 304 && $job['attributes']['redirects'] < $this->maxRedirects) {
+ $location = $this->handleRedirect($job, $response);
+ if ($location) {
+ $this->restartJob($job, $location, array('redirects' => $job['attributes']['redirects'] + 1));
+ continue;
+ }
+ }
+
+ // fail 4xx and 5xx responses and capture the response
+ if ($statusCode >= 400 && $statusCode <= 599) {
+ throw $this->failResponse($job, $response, $response->getStatusMessage());
+// TODO progress
+// $this->io->overwriteError("Downloading (failed)", false);
+ }
+
+ if ($job['attributes']['storeAuth']) {
+ $this->authHelper->storeAuth($job['origin'], $job['attributes']['storeAuth']);
+ }
+
+ // resolve promise
+ if ($job['filename']) {
+ rename($job['filename'].'~', $job['filename']);
+ call_user_func($job['resolve'], $response);
+ } else {
+ call_user_func($job['resolve'], $response);
+ }
+ } catch (\Exception $e) {
+ if ($e instanceof TransportException && $headers) {
+ $e->setHeaders($headers);
+ $e->setStatusCode($statusCode);
+ }
+ if ($e instanceof TransportException && $response) {
+ $e->setResponse($response->getBody());
+ }
+
+ if (is_resource($job['headerHandle'])) {
+ fclose($job['headerHandle']);
+ }
+ if (is_resource($job['bodyHandle'])) {
+ fclose($job['bodyHandle']);
+ }
+ if ($job['filename']) {
+ @unlink($job['filename'].'~');
+ }
+ call_user_func($job['reject'], $e);
+ }
+ }
+
+ foreach ($this->jobs as $i => $curlHandle) {
+ if (!isset($this->jobs[$i])) {
+ continue;
+ }
+ $curlHandle = $this->jobs[$i]['curlHandle'];
+ $progress = array_diff_key(curl_getinfo($curlHandle), self::$timeInfo);
+
+ if ($this->jobs[$i]['progress'] !== $progress) {
+ $previousProgress = $this->jobs[$i]['progress'];
+ $this->jobs[$i]['progress'] = $progress;
+
+ // TODO
+ //$this->onProgress($curlHandle, $this->jobs[$i]['callback'], $progress, $previousProgress);
+ }
+ }
+ }
+
+ private function handleRedirect(array $job, Response $response)
+ {
+ if ($locationHeader = $response->getHeader('location')) {
+ if (parse_url($locationHeader, PHP_URL_SCHEME)) {
+ // Absolute URL; e.g. https://example.com/composer
+ $targetUrl = $locationHeader;
+ } elseif (parse_url($locationHeader, PHP_URL_HOST)) {
+ // Scheme relative; e.g. //example.com/foo
+ $targetUrl = parse_url($job['url'], PHP_URL_SCHEME).':'.$locationHeader;
+ } elseif ('/' === $locationHeader[0]) {
+ // Absolute path; e.g. /foo
+ $urlHost = parse_url($job['url'], PHP_URL_HOST);
+
+ // Replace path using hostname as an anchor.
+ $targetUrl = preg_replace('{^(.+(?://|@)'.preg_quote($urlHost).'(?::\d+)?)(?:[/\?].*)?$}', '\1'.$locationHeader, $job['url']);
+ } else {
+ // Relative path; e.g. foo
+ // This actually differs from PHP which seems to add duplicate slashes.
+ $targetUrl = preg_replace('{^(.+/)[^/?]*(?:\?.*)?$}', '\1'.$locationHeader, $job['url']);
+ }
+ }
+
+ if (!empty($targetUrl)) {
+ $this->io->writeError(sprintf('Following redirect (%u) %s', $job['attributes']['redirects'] + 1, $targetUrl), true, IOInterface::DEBUG);
+
+ return $targetUrl;
+ }
+
+ throw new TransportException('The "'.$job['url'].'" file could not be downloaded, got redirect without Location ('.$response->getStatusMessage().')');
+ }
+
+ private function isAuthenticatedRetryNeeded(array $job, Response $response)
+ {
+ if (in_array($response->getStatusCode(), array(401, 403)) && $job['attributes']['retryAuthFailure']) {
+ $warning = null;
+ if ($response->getHeader('content-type') === 'application/json') {
+ $data = json_decode($response->getBody(), true);
+ if (!empty($data['warning'])) {
+ $warning = $data['warning'];
+ }
+ }
+
+ $result = $this->authHelper->promptAuthIfNeeded($job['url'], $job['origin'], $response->getStatusCode(), $response->getStatusMessage(), $warning, $response->getHeaders());
+
+ if ($result['retry']) {
+ return $result;
+ }
+ }
+
+ $locationHeader = $response->getHeader('location');
+ $needsAuthRetry = false;
+
+ // check for bitbucket login page asking to authenticate
+ if (
+ $job['origin'] === 'bitbucket.org'
+ && !$this->authHelper->isPublicBitBucketDownload($job['url'])
+ && substr($job['url'], -4) === '.zip'
+ && (!$locationHeader || substr($locationHeader, -4) !== '.zip')
+ && preg_match('{^text/html\b}i', $response->getHeader('content-type'))
+ ) {
+ $needsAuthRetry = 'Bitbucket requires authentication and it was not provided';
+ }
+
+ // check for gitlab 404 when downloading archives
+ if (
+ $response->getStatusCode() === 404
+ && $this->config && in_array($job['origin'], $this->config->get('gitlab-domains'), true)
+ && false !== strpos($job['url'], 'archive.zip')
+ ) {
+ $needsAuthRetry = 'GitLab requires authentication and it was not provided';
+ }
+
+ if ($needsAuthRetry) {
+ if ($job['attributes']['retryAuthFailure']) {
+ $result = $this->authHelper->promptAuthIfNeeded($job['url'], $job['origin'], 401);
+ if ($result['retry']) {
+ return $result;
+ }
+ }
+
+ throw $this->failResponse($job, $response, $needsAuthRetry);
+ }
+
+ return array('retry' => false, 'storeAuth' => false);
+ }
+
+ private function restartJob(array $job, $url, array $attributes = array())
+ {
+ $attributes = array_merge($job['attributes'], $attributes);
+ $origin = Url::getOrigin($this->config, $url);
+
+ $this->initDownload($job['resolve'], $job['reject'], $origin, $url, $job['options'], $job['filename'], $attributes);
+ }
+
+ private function failResponse(array $job, Response $response, $errorMessage)
+ {
+ return new TransportException('The "'.$job['url'].'" file could not be downloaded ('.$errorMessage.')', $response->getStatusCode());
+ }
+
+ private function onProgress($curlHandle, callable $notify, array $progress, array $previousProgress)
+ {
+ // TODO add support for progress
+ if (300 <= $progress['http_code'] && $progress['http_code'] < 400) {
+ return;
+ }
+ if ($previousProgress['download_content_length'] < $progress['download_content_length']) {
+ $notify(STREAM_NOTIFY_FILE_SIZE_IS, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, (int) $progress['download_content_length'], false);
+ }
+ if ($previousProgress['size_download'] < $progress['size_download']) {
+ $notify(STREAM_NOTIFY_PROGRESS, STREAM_NOTIFY_SEVERITY_INFO, '', 0, (int) $progress['size_download'], (int) $progress['download_content_length'], false);
+ }
+ }
+
+ private function checkCurlResult($code)
+ {
+ if ($code != CURLM_OK && $code != CURLM_CALL_MULTI_PERFORM) {
+ throw new \RuntimeException(isset($this->multiErrors[$code])
+ ? "cURL error: {$code} ({$this->multiErrors[$code][0]}): cURL message: {$this->multiErrors[$code][1]}"
+ : 'Unexpected cURL error: ' . $code
+ );
+ }
+ }
+}
diff --git a/src/Composer/Util/Http/Response.php b/src/Composer/Util/Http/Response.php
new file mode 100644
index 000000000..d2774c938
--- /dev/null
+++ b/src/Composer/Util/Http/Response.php
@@ -0,0 +1,94 @@
+
+ * Jordi Boggiano
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Util\Http;
+
+use Composer\Json\JsonFile;
+
+class Response
+{
+ private $request;
+ private $code;
+ private $headers;
+ private $body;
+
+ public function __construct(array $request, $code, array $headers, $body)
+ {
+ if (!isset($request['url'])) {
+ throw new \LogicException('url key missing from request array');
+ }
+ $this->request = $request;
+ $this->code = (int) $code;
+ $this->headers = $headers;
+ $this->body = $body;
+ }
+
+ public function getStatusCode()
+ {
+ return $this->code;
+ }
+
+ /**
+ * @return string|null
+ */
+ public function getStatusMessage()
+ {
+ $value = null;
+ foreach ($this->headers as $header) {
+ if (preg_match('{^HTTP/\S+ \d+}i', $header)) {
+ // In case of redirects, headers contain the headers of all responses
+ // so we can not return directly and need to keep iterating
+ $value = $header;
+ }
+ }
+
+ return $value;
+ }
+
+ public function getHeaders()
+ {
+ return $this->headers;
+ }
+
+ public function getHeader($name)
+ {
+ $value = null;
+ foreach ($this->headers as $header) {
+ if (preg_match('{^'.$name.':\s*(.+?)\s*$}i', $header, $match)) {
+ $value = $match[1];
+ } elseif (preg_match('{^HTTP/}i', $header)) {
+ // TODO ideally redirects would be handled in CurlDownloader/RemoteFilesystem and this becomes unnecessary
+ //
+ // In case of redirects, headers contains the headers of all responses
+ // so we reset the flag when a new response is being parsed as we are only interested in the last response
+ $value = null;
+ }
+ }
+
+ return $value;
+ }
+
+ public function getBody()
+ {
+ return $this->body;
+ }
+
+ public function decodeJson()
+ {
+ return JsonFile::parseJson($this->body, $this->request['url']);
+ }
+
+ public function collect()
+ {
+ $this->request = $this->code = $this->headers = $this->body = null;
+ }
+}
diff --git a/src/Composer/Util/HttpDownloader.php b/src/Composer/Util/HttpDownloader.php
new file mode 100644
index 000000000..172ea875a
--- /dev/null
+++ b/src/Composer/Util/HttpDownloader.php
@@ -0,0 +1,318 @@
+
+ * Jordi Boggiano
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Util;
+
+use Composer\Config;
+use Composer\IO\IOInterface;
+use Composer\Downloader\TransportException;
+use Composer\CaBundle\CaBundle;
+use Composer\Util\Http\Response;
+use Psr\Log\LoggerInterface;
+use React\Promise\Promise;
+
+/**
+ * @author Jordi Boggiano
+ */
+class HttpDownloader
+{
+ const STATUS_QUEUED = 1;
+ const STATUS_STARTED = 2;
+ const STATUS_COMPLETED = 3;
+ const STATUS_FAILED = 4;
+
+ private $io;
+ private $config;
+ private $jobs = array();
+ private $options = array();
+ private $runningJobs = 0;
+ private $maxJobs = 10;
+ private $lastProgress;
+ private $disableTls = false;
+ private $curl;
+ private $rfs;
+ private $idGen = 0;
+ private $disabled;
+
+ /**
+ * @param IOInterface $io The IO instance
+ * @param Config $config The config
+ * @param array $options The options
+ * @param bool $disableTls
+ */
+ public function __construct(IOInterface $io, Config $config, array $options = array(), $disableTls = false)
+ {
+ $this->io = $io;
+
+ $this->disabled = (bool) getenv('COMPOSER_DISABLE_NETWORK');
+
+ // Setup TLS options
+ // The cafile option can be set via config.json
+ if ($disableTls === false) {
+ $logger = $io instanceof LoggerInterface ? $io : null;
+ $this->options = StreamContextFactory::getTlsDefaults($options, $logger);
+ } else {
+ $this->disableTls = true;
+ }
+
+ // handle the other externally set options normally.
+ $this->options = array_replace_recursive($this->options, $options);
+ $this->config = $config;
+
+ // TODO enable curl only on 5.6+ if older versions cause any problem
+ if (extension_loaded('curl')) {
+ $this->curl = new Http\CurlDownloader($io, $config, $options, $disableTls);
+ }
+
+ $this->rfs = new RemoteFilesystem($io, $config, $options, $disableTls);
+ }
+
+ public function get($url, $options = array())
+ {
+ list($job, $promise) = $this->addJob(array('url' => $url, 'options' => $options, 'copyTo' => false), true);
+ $this->wait($job['id']);
+
+ return $this->getResponse($job['id']);
+ }
+
+ public function add($url, $options = array())
+ {
+ list($job, $promise) = $this->addJob(array('url' => $url, 'options' => $options, 'copyTo' => false));
+
+ return $promise;
+ }
+
+ public function copy($url, $to, $options = array())
+ {
+ list($job, $promise) = $this->addJob(array('url' => $url, 'options' => $options, 'copyTo' => $to), true);
+ $this->wait($job['id']);
+
+ return $this->getResponse($job['id']);
+ }
+
+ public function addCopy($url, $to, $options = array())
+ {
+ list($job, $promise) = $this->addJob(array('url' => $url, 'options' => $options, 'copyTo' => $to));
+
+ return $promise;
+ }
+
+ /**
+ * Retrieve the options set in the constructor
+ *
+ * @return array Options
+ */
+ public function getOptions()
+ {
+ return $this->options;
+ }
+
+ /**
+ * Merges new options
+ *
+ * @return array $options
+ */
+ public function setOptions(array $options)
+ {
+ $this->options = array_replace_recursive($this->options, $options);
+ }
+
+ private function addJob($request, $sync = false)
+ {
+ $job = array(
+ 'id' => $this->idGen++,
+ 'status' => self::STATUS_QUEUED,
+ 'request' => $request,
+ 'sync' => $sync,
+ 'origin' => Url::getOrigin($this->config, $request['url']),
+ );
+
+ // capture username/password from URL if there is one
+ if (preg_match('{^https?://([^:/]+):([^@/]+)@([^/]+)}i', $request['url'], $match)) {
+ $this->io->setAuthentication($job['origin'], rawurldecode($match[1]), rawurldecode($match[2]));
+ }
+
+ $rfs = $this->rfs;
+
+ if ($this->curl && preg_match('{^https?://}i', $job['request']['url'])) {
+ $resolver = function ($resolve, $reject) use (&$job) {
+ $job['status'] = HttpDownloader::STATUS_QUEUED;
+ $job['resolve'] = $resolve;
+ $job['reject'] = $reject;
+ };
+ } else {
+ $resolver = function ($resolve, $reject) use (&$job, $rfs) {
+ // start job
+ $url = $job['request']['url'];
+ $options = $job['request']['options'];
+
+ $job['status'] = HttpDownloader::STATUS_STARTED;
+
+ if ($job['request']['copyTo']) {
+ $result = $rfs->copy($job['origin'], $url, $job['request']['copyTo'], false /* TODO progress */, $options);
+
+ $headers = $rfs->getLastHeaders();
+ $response = new Http\Response($job['request'], $rfs->findStatusCode($headers), $headers, $job['request']['copyTo'].'~');
+
+ $resolve($response);
+ } else {
+ $body = $rfs->getContents($job['origin'], $url, false /* TODO progress */, $options);
+ $headers = $rfs->getLastHeaders();
+ $response = new Http\Response($job['request'], $rfs->findStatusCode($headers), $headers, $body);
+
+ $resolve($response);
+ }
+ };
+ }
+
+ $downloader = $this;
+ $io = $this->io;
+
+ $canceler = function () {};
+
+ $promise = new Promise($resolver, $canceler);
+ $promise->then(function ($response) use (&$job, $downloader) {
+ $job['status'] = HttpDownloader::STATUS_COMPLETED;
+ $job['response'] = $response;
+
+ // TODO 3.0 this should be done directly on $this when PHP 5.3 is dropped
+ $downloader->markJobDone();
+ $downloader->scheduleNextJob();
+
+ return $response;
+ }, function ($e) use ($io, &$job, $downloader) {
+ $job['status'] = HttpDownloader::STATUS_FAILED;
+ $job['exception'] = $e;
+
+ $downloader->markJobDone();
+ $downloader->scheduleNextJob();
+
+ throw $e;
+ });
+ $this->jobs[$job['id']] =& $job;
+
+ if ($this->runningJobs < $this->maxJobs) {
+ $this->startJob($job['id']);
+ }
+
+ return array($job, $promise);
+ }
+
+ private function startJob($id)
+ {
+ $job =& $this->jobs[$id];
+ if ($job['status'] !== self::STATUS_QUEUED) {
+ return;
+ }
+
+ // start job
+ $job['status'] = self::STATUS_STARTED;
+ $this->runningJobs++;
+
+ $resolve = $job['resolve'];
+ $reject = $job['reject'];
+ $url = $job['request']['url'];
+ $options = $job['request']['options'];
+ $origin = $job['origin'];
+
+ if ($this->disabled) {
+ if (isset($job['request']['options']['http']['header']) && false !== stripos(implode('', $job['request']['options']['http']['header']), 'if-modified-since')) {
+ $resolve(new Response(array('url' => $url), 304, array(), ''));
+ } else {
+ $e = new TransportException('Network disabled', 499);
+ $e->setStatusCode(499);
+ $reject($e);
+ }
+ return;
+ }
+
+ if ($job['request']['copyTo']) {
+ $this->curl->download($resolve, $reject, $origin, $url, $options, $job['request']['copyTo']);
+ } else {
+ $this->curl->download($resolve, $reject, $origin, $url, $options);
+ }
+ }
+
+ /**
+ * @private
+ */
+ public function markJobDone()
+ {
+ $this->runningJobs--;
+ }
+
+ /**
+ * @private
+ */
+ public function scheduleNextJob()
+ {
+ foreach ($this->jobs as $job) {
+ if ($job['status'] === self::STATUS_QUEUED) {
+ $this->startJob($job['id']);
+ if ($this->runningJobs >= $this->maxJobs) {
+ return;
+ }
+ }
+ }
+ }
+
+ public function wait($index = null, $progress = false)
+ {
+ while (true) {
+ if ($this->curl) {
+ $this->curl->tick();
+ }
+
+ if (null !== $index) {
+ if ($this->jobs[$index]['status'] === self::STATUS_COMPLETED || $this->jobs[$index]['status'] === self::STATUS_FAILED) {
+ return;
+ }
+ } else {
+ $done = true;
+ foreach ($this->jobs as $job) {
+ if (!in_array($job['status'], array(self::STATUS_COMPLETED, self::STATUS_FAILED), true)) {
+ $done = false;
+ break;
+ } elseif (!$job['sync']) {
+ unset($this->jobs[$job['id']]);
+ }
+ }
+ if ($done) {
+ return;
+ }
+ }
+
+ usleep(1000);
+ }
+ }
+
+ private function getResponse($index)
+ {
+ if (!isset($this->jobs[$index])) {
+ throw new \LogicException('Invalid request id');
+ }
+
+ if ($this->jobs[$index]['status'] === self::STATUS_FAILED) {
+ throw $this->jobs[$index]['exception'];
+ }
+
+ if (!isset($this->jobs[$index]['response'])) {
+ throw new \LogicException('Response not available yet, call wait() first');
+ }
+
+ $resp = $this->jobs[$index]['response'];
+
+ unset($this->jobs[$index]);
+
+ return $resp;
+ }
+}
diff --git a/src/Composer/Util/Loop.php b/src/Composer/Util/Loop.php
new file mode 100644
index 000000000..1be7d478b
--- /dev/null
+++ b/src/Composer/Util/Loop.php
@@ -0,0 +1,47 @@
+
+ * Jordi Boggiano
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Util;
+
+use Composer\Util\HttpDownloader;
+use React\Promise\Promise;
+
+/**
+ * @author Jordi Boggiano
+ */
+class Loop
+{
+ private $io;
+
+ public function __construct(HttpDownloader $httpDownloader)
+ {
+ $this->httpDownloader = $httpDownloader;
+ }
+
+ public function wait(array $promises)
+ {
+ $uncaught = null;
+
+ \React\Promise\all($promises)->then(
+ function () { },
+ function ($e) use (&$uncaught) {
+ $uncaught = $e;
+ }
+ );
+
+ $this->httpDownloader->wait();
+
+ if ($uncaught) {
+ throw $uncaught;
+ }
+ }
+}
diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php
index ea18a9e30..2709f7006 100644
--- a/src/Composer/Util/RemoteFilesystem.php
+++ b/src/Composer/Util/RemoteFilesystem.php
@@ -41,6 +41,7 @@ class RemoteFilesystem
private $retryAuthFailure;
private $lastHeaders;
private $storeAuth;
+ private $authHelper;
private $degradedMode = false;
private $redirects;
private $maxRedirects = 20;
@@ -53,14 +54,15 @@ class RemoteFilesystem
* @param array $options The options
* @param bool $disableTls
*/
- public function __construct(IOInterface $io, Config $config = null, array $options = array(), $disableTls = false)
+ public function __construct(IOInterface $io, Config $config, array $options = array(), $disableTls = false)
{
$this->io = $io;
// Setup TLS options
// The cafile option can be set via config.json
if ($disableTls === false) {
- $this->options = $this->getTlsDefaults($options);
+ $logger = $io instanceof LoggerInterface ? $io : null;
+ $this->options = StreamContextFactory::getTlsDefaults($options, $logger);
} else {
$this->disableTls = true;
}
@@ -68,6 +70,7 @@ class RemoteFilesystem
// handle the other externally set options normally.
$this->options = array_replace_recursive($this->options, $options);
$this->config = $config;
+ $this->authHelper = new AuthHelper($io, $config);
}
/**
@@ -146,7 +149,7 @@ class RemoteFilesystem
* @param string $name header name (case insensitive)
* @return string|null
*/
- public function findHeaderValue(array $headers, $name)
+ public static function findHeaderValue(array $headers, $name)
{
$value = null;
foreach ($headers as $header) {
@@ -166,7 +169,7 @@ class RemoteFilesystem
* @param array $headers array of returned headers like from getLastHeaders()
* @return int|null
*/
- public function findStatusCode(array $headers)
+ public static function findStatusCode(array $headers)
{
$value = null;
foreach ($headers as $header) {
@@ -214,27 +217,6 @@ class RemoteFilesystem
*/
protected function get($originUrl, $fileUrl, $additionalOptions = array(), $fileName = null, $progress = true)
{
- if (strpos($originUrl, '.github.com') === (strlen($originUrl) - 11)) {
- $originUrl = 'github.com';
- }
-
- // Gitlab can be installed in a non-root context (i.e. gitlab.com/foo). When downloading archives the originUrl
- // is the host without the path, so we look for the registered gitlab-domains matching the host here
- if (
- $this->config
- && is_array($this->config->get('gitlab-domains'))
- && false === strpos($originUrl, '/')
- && !in_array($originUrl, $this->config->get('gitlab-domains'))
- ) {
- foreach ($this->config->get('gitlab-domains') as $gitlabDomain) {
- if (0 === strpos($gitlabDomain, $originUrl)) {
- $originUrl = $gitlabDomain;
- break;
- }
- }
- unset($gitlabDomain);
- }
-
$this->scheme = parse_url($fileUrl, PHP_URL_SCHEME);
$this->bytesMax = 0;
$this->originUrl = $originUrl;
@@ -246,11 +228,6 @@ class RemoteFilesystem
$this->lastHeaders = array();
$this->redirects = 1; // The first request counts.
- // capture username/password from URL if there is one
- if (preg_match('{^https?://([^:/]+):([^@/]+)@([^/]+)}i', $fileUrl, $match)) {
- $this->io->setAuthentication($originUrl, rawurldecode($match[1]), rawurldecode($match[2]));
- }
-
$tempAdditionalOptions = $additionalOptions;
if (isset($tempAdditionalOptions['retry-auth-failure'])) {
$this->retryAuthFailure = (bool) $tempAdditionalOptions['retry-auth-failure'];
@@ -271,14 +248,6 @@ class RemoteFilesystem
$origFileUrl = $fileUrl;
- if (isset($options['github-token'])) {
- // only add the access_token if it is actually a github URL (in case we were redirected to S3)
- if (preg_match('{^https?://([a-z0-9-]+\.)*github\.com/}', $fileUrl)) {
- $fileUrl .= (false === strpos($fileUrl, '?') ? '?' : '&') . 'access_token='.$options['github-token'];
- }
- unset($options['github-token']);
- }
-
if (isset($options['gitlab-token'])) {
$fileUrl .= (false === strpos($fileUrl, '?') ? '?' : '&') . 'access_token='.$options['gitlab-token'];
unset($options['gitlab-token']);
@@ -399,7 +368,7 @@ class RemoteFilesystem
// check for bitbucket login page asking to authenticate
if ($originUrl === 'bitbucket.org'
- && !$this->isPublicBitBucketDownload($fileUrl)
+ && !$this->authHelper->isPublicBitBucketDownload($fileUrl)
&& substr($fileUrl, -4) === '.zip'
&& (!$locationHeader || substr($locationHeader, -4) !== '.zip')
&& $contentType && preg_match('{^text/html\b}i', $contentType)
@@ -543,8 +512,7 @@ class RemoteFilesystem
$result = $this->get($this->originUrl, $this->fileUrl, $additionalOptions, $this->fileName, $this->progress);
if ($this->storeAuth && $this->config) {
- $authHelper = new AuthHelper($this->io, $this->config);
- $authHelper->storeAuth($this->originUrl, $this->storeAuth);
+ $this->authHelper->storeAuth($this->originUrl, $this->storeAuth);
$this->storeAuth = false;
}
@@ -649,111 +617,14 @@ class RemoteFilesystem
protected function promptAuthAndRetry($httpStatus, $reason = null, $warning = null, $headers = array())
{
- if ($this->config && in_array($this->originUrl, $this->config->get('github-domains'), true)) {
- $gitHubUtil = new GitHub($this->io, $this->config, null);
- $message = "\n";
+ $result = $this->authHelper->promptAuthIfNeeded($this->fileUrl, $this->originUrl, $httpStatus, $reason, $warning, $headers);
- $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.';
- }
+ $this->storeAuth = $result['storeAuth'];
+ $this->retry = $result['retry'];
- $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)
- && (!$this->io->isInteractive() || !$gitHubUtil->authorizeOAuthInteractively($this->originUrl, $message))
- ) {
- throw new TransportException('Could not authenticate against '.$this->originUrl, 401);
- }
- } elseif ($this->config && in_array($this->originUrl, $this->config->get('gitlab-domains'), true)) {
- $message = "\n".'Could not fetch '.$this->fileUrl.', enter your ' . $this->originUrl . ' credentials ' .($httpStatus === 401 ? 'to access private repos' : 'to go over the API rate limit');
- $gitLabUtil = new GitLab($this->io, $this->config, null);
-
- if ($this->io->hasAuthentication($this->originUrl) && ($auth = $this->io->getAuthentication($this->originUrl)) && $auth['password'] === 'private-token') {
- throw new TransportException("Invalid credentials for '" . $this->fileUrl . "', aborting.", $httpStatus);
- }
-
- if (!$gitLabUtil->authorizeOAuth($this->originUrl)
- && (!$this->io->isInteractive() || !$gitLabUtil->authorizeOAuthInteractively($this->scheme, $this->originUrl, $message))
- ) {
- throw new TransportException('Could not authenticate against '.$this->originUrl, 401);
- }
- } elseif ($this->config && $this->originUrl === 'bitbucket.org') {
- $askForOAuthToken = true;
- if ($this->io->hasAuthentication($this->originUrl)) {
- $auth = $this->io->getAuthentication($this->originUrl);
- if ($auth['username'] !== 'x-token-auth') {
- $bitbucketUtil = new Bitbucket($this->io, $this->config);
- $accessToken = $bitbucketUtil->requestToken($this->originUrl, $auth['username'], $auth['password']);
- if (!empty($accessToken)) {
- $this->io->setAuthentication($this->originUrl, 'x-token-auth', $accessToken);
- $askForOAuthToken = false;
- }
- } else {
- throw new TransportException('Could not authenticate against ' . $this->originUrl, 401);
- }
- }
-
- if ($askForOAuthToken) {
- $message = "\n".'Could not fetch ' . $this->fileUrl . ', please create a bitbucket OAuth token to ' . (($httpStatus === 401 || $httpStatus === 403) ? 'access private repos' : 'go over the API rate limit');
- $bitBucketUtil = new Bitbucket($this->io, $this->config);
- if (! $bitBucketUtil->authorizeOAuth($this->originUrl)
- && (! $this->io->isInteractive() || !$bitBucketUtil->authorizeOAuthInteractively($this->originUrl, $message))
- ) {
- throw new TransportException('Could not authenticate against ' . $this->originUrl, 401);
- }
- }
- } else {
- // 404s are only handled for github
- if ($httpStatus === 404) {
- return;
- }
-
- // fail if the console is not interactive
- if (!$this->io->isInteractive()) {
- if ($httpStatus === 401) {
- $message = "The '" . $this->fileUrl . "' URL required authentication.\nYou must be using the interactive console to authenticate";
- }
- if ($httpStatus === 403) {
- $message = "The '" . $this->fileUrl . "' URL could not be accessed: " . $reason;
- }
-
- throw new TransportException($message, $httpStatus);
- }
- // fail if we already have auth
- if ($this->io->hasAuthentication($this->originUrl)) {
- throw new TransportException("Invalid credentials for '" . $this->fileUrl . "', aborting.", $httpStatus);
- }
-
- $this->io->overwriteError('');
- if ($warning) {
- $this->io->writeError(' '.$warning.'');
- }
- $this->io->writeError(' Authentication required ('.parse_url($this->fileUrl, PHP_URL_HOST).'):');
- $username = $this->io->ask(' Username: ');
- $password = $this->io->askAndHideAnswer(' Password: ');
- $this->io->setAuthentication($this->originUrl, $username, $password);
- $this->storeAuth = $this->config->get('store-auths');
+ if ($this->retry) {
+ throw new TransportException('RETRY');
}
-
- $this->retry = true;
- throw new TransportException('RETRY');
}
protected function getOptionsForUrl($originUrl, $additionalOptions)
@@ -813,27 +684,7 @@ class RemoteFilesystem
$headers[] = 'Connection: close';
}
- if ($this->io->hasAuthentication($originUrl)) {
- $auth = $this->io->getAuthentication($originUrl);
- if ('github.com' === $originUrl && 'x-oauth-basic' === $auth['password']) {
- $options['github-token'] = $auth['username'];
- } elseif ($this->config && in_array($originUrl, $this->config->get('gitlab-domains'), true)) {
- if ($auth['password'] === 'oauth2') {
- $headers[] = 'Authorization: Bearer '.$auth['username'];
- } elseif ($auth['password'] === 'private-token') {
- $headers[] = 'PRIVATE-TOKEN: '.$auth['username'];
- }
- } elseif ('bitbucket.org' === $originUrl
- && $this->fileUrl !== Bitbucket::OAUTH2_ACCESS_TOKEN_URL && 'x-token-auth' === $auth['username']
- ) {
- if (!$this->isPublicBitBucketDownload($this->fileUrl)) {
- $headers[] = 'Authorization: Bearer ' . $auth['password'];
- }
- } else {
- $authStr = base64_encode($auth['username'] . ':' . $auth['password']);
- $headers[] = 'Authorization: Basic '.$authStr;
- }
- }
+ $headers = $this->authHelper->addAuthenticationHeader($headers, $originUrl, $this->fileUrl);
$options['http']['follow_location'] = 0;
@@ -891,111 +742,6 @@ class RemoteFilesystem
return false;
}
- /**
- * @param array $options
- *
- * @return array
- */
- private function getTlsDefaults(array $options)
- {
- $ciphers = implode(':', array(
- 'ECDHE-RSA-AES128-GCM-SHA256',
- 'ECDHE-ECDSA-AES128-GCM-SHA256',
- 'ECDHE-RSA-AES256-GCM-SHA384',
- 'ECDHE-ECDSA-AES256-GCM-SHA384',
- 'DHE-RSA-AES128-GCM-SHA256',
- 'DHE-DSS-AES128-GCM-SHA256',
- 'kEDH+AESGCM',
- 'ECDHE-RSA-AES128-SHA256',
- 'ECDHE-ECDSA-AES128-SHA256',
- 'ECDHE-RSA-AES128-SHA',
- 'ECDHE-ECDSA-AES128-SHA',
- 'ECDHE-RSA-AES256-SHA384',
- 'ECDHE-ECDSA-AES256-SHA384',
- 'ECDHE-RSA-AES256-SHA',
- 'ECDHE-ECDSA-AES256-SHA',
- 'DHE-RSA-AES128-SHA256',
- 'DHE-RSA-AES128-SHA',
- 'DHE-DSS-AES128-SHA256',
- 'DHE-RSA-AES256-SHA256',
- 'DHE-DSS-AES256-SHA',
- 'DHE-RSA-AES256-SHA',
- 'AES128-GCM-SHA256',
- 'AES256-GCM-SHA384',
- 'AES128-SHA256',
- 'AES256-SHA256',
- 'AES128-SHA',
- 'AES256-SHA',
- 'AES',
- 'CAMELLIA',
- 'DES-CBC3-SHA',
- '!aNULL',
- '!eNULL',
- '!EXPORT',
- '!DES',
- '!RC4',
- '!MD5',
- '!PSK',
- '!aECDH',
- '!EDH-DSS-DES-CBC3-SHA',
- '!EDH-RSA-DES-CBC3-SHA',
- '!KRB5-DES-CBC3-SHA',
- ));
-
- /**
- * CN_match and SNI_server_name are only known once a URL is passed.
- * They will be set in the getOptionsForUrl() method which receives a URL.
- *
- * cafile or capath can be overridden by passing in those options to constructor.
- */
- $defaults = array(
- 'ssl' => array(
- 'ciphers' => $ciphers,
- 'verify_peer' => true,
- 'verify_depth' => 7,
- 'SNI_enabled' => true,
- 'capture_peer_cert' => true,
- ),
- );
-
- if (isset($options['ssl'])) {
- $defaults['ssl'] = array_replace_recursive($defaults['ssl'], $options['ssl']);
- }
-
- $caBundleLogger = $this->io instanceof LoggerInterface ? $this->io : null;
-
- /**
- * Attempt to find a local cafile or throw an exception if none pre-set
- * The user may go download one if this occurs.
- */
- if (!isset($defaults['ssl']['cafile']) && !isset($defaults['ssl']['capath'])) {
- $result = CaBundle::getSystemCaRootBundlePath($caBundleLogger);
-
- if (is_dir($result)) {
- $defaults['ssl']['capath'] = $result;
- } else {
- $defaults['ssl']['cafile'] = $result;
- }
- }
-
- if (isset($defaults['ssl']['cafile']) && (!is_readable($defaults['ssl']['cafile']) || !CaBundle::validateCaFile($defaults['ssl']['cafile'], $caBundleLogger))) {
- throw new TransportException('The configured cafile was not valid or could not be read.');
- }
-
- if (isset($defaults['ssl']['capath']) && (!is_dir($defaults['ssl']['capath']) || !is_readable($defaults['ssl']['capath']))) {
- throw new TransportException('The configured capath was not valid or could not be read.');
- }
-
- /**
- * Disable TLS compression to prevent CRIME attacks where supported.
- */
- if (PHP_VERSION_ID >= 50413) {
- $defaults['ssl']['disable_compression'] = true;
- }
-
- return $defaults;
- }
-
/**
* Fetch certificate common name and fingerprint for validation of SAN.
*
@@ -1065,29 +811,4 @@ class RemoteFilesystem
return parse_url($url, PHP_URL_HOST).':'.$port;
}
-
- /**
- * @link https://github.com/composer/composer/issues/5584
- *
- * @param string $urlToBitBucketFile URL to a file at bitbucket.org.
- *
- * @return bool Whether the given URL is a public BitBucket download which requires no authentication.
- */
- private function isPublicBitBucketDownload($urlToBitBucketFile)
- {
- $domain = parse_url($urlToBitBucketFile, PHP_URL_HOST);
- if (strpos($domain, 'bitbucket.org') === false) {
- // Bitbucket downloads are hosted on amazonaws.
- // We do not need to authenticate there at all
- return true;
- }
-
- $path = parse_url($urlToBitBucketFile, PHP_URL_PATH);
-
- // Path for a public download follows this pattern /{user}/{repo}/downloads/{whatever}
- // {@link https://blog.bitbucket.org/2009/04/12/new-feature-downloads/}
- $pathParts = explode('/', $path);
-
- return count($pathParts) >= 4 && $pathParts[3] == 'downloads';
- }
}
diff --git a/src/Composer/Util/StreamContextFactory.php b/src/Composer/Util/StreamContextFactory.php
index 8dfd6624a..a87bc6d8b 100644
--- a/src/Composer/Util/StreamContextFactory.php
+++ b/src/Composer/Util/StreamContextFactory.php
@@ -13,6 +13,8 @@
namespace Composer\Util;
use Composer\Composer;
+use Composer\CaBundle\CaBundle;
+use Psr\Log\LoggerInterface;
/**
* Allows the creation of a basic context supporting http proxy
@@ -39,6 +41,32 @@ final class StreamContextFactory
'max_redirects' => 20,
));
+ $options = array_replace_recursive($options, self::initOptions($url, $defaultOptions));
+ unset($defaultOptions['http']['header']);
+ $options = array_replace_recursive($options, $defaultOptions);
+
+ if (isset($options['http']['header'])) {
+ $options['http']['header'] = self::fixHttpHeaderField($options['http']['header']);
+ }
+
+ return stream_context_create($options, $defaultParams);
+ }
+
+ /**
+ * @param string $url
+ * @param array $options
+ * @return array ['http' => ['header' => [...], 'proxy' => '..', 'request_fulluri' => bool]] formatted as a stream context array
+ */
+ public static function initOptions($url, array $options)
+ {
+ // Make sure the headers are in an array form
+ if (!isset($options['http']['header'])) {
+ $options['http']['header'] = array();
+ }
+ if (is_string($options['http']['header'])) {
+ $options['http']['header'] = explode("\r\n", $options['http']['header']);
+ }
+
// Handle HTTP_PROXY/http_proxy on CLI only for security reasons
if ((PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') && (!empty($_SERVER['HTTP_PROXY']) || !empty($_SERVER['http_proxy']))) {
$proxy = parse_url(!empty($_SERVER['http_proxy']) ? $_SERVER['http_proxy'] : $_SERVER['HTTP_PROXY']);
@@ -85,15 +113,15 @@ final class StreamContextFactory
// enabled request_fulluri unless it is explicitly disabled
switch (parse_url($url, PHP_URL_SCHEME)) {
- case 'http': // default request_fulluri to true
+ case 'http': // default request_fulluri to true for HTTP
$reqFullUriEnv = getenv('HTTP_PROXY_REQUEST_FULLURI');
if ($reqFullUriEnv === false || $reqFullUriEnv === '' || (strtolower($reqFullUriEnv) !== 'false' && (bool) $reqFullUriEnv)) {
$options['http']['request_fulluri'] = true;
}
break;
- case 'https': // default request_fulluri to true
+ case 'https': // default request_fulluri to false for HTTPS
$reqFullUriEnv = getenv('HTTPS_PROXY_REQUEST_FULLURI');
- if ($reqFullUriEnv === false || $reqFullUriEnv === '' || (strtolower($reqFullUriEnv) !== 'false' && (bool) $reqFullUriEnv)) {
+ if (strtolower($reqFullUriEnv) !== 'false' && (bool) $reqFullUriEnv) {
$options['http']['request_fulluri'] = true;
}
break;
@@ -115,42 +143,139 @@ final class StreamContextFactory
}
$auth = base64_encode($auth);
- // Preserve headers if already set in default options
- if (isset($defaultOptions['http']['header'])) {
- if (is_string($defaultOptions['http']['header'])) {
- $defaultOptions['http']['header'] = array($defaultOptions['http']['header']);
- }
- $defaultOptions['http']['header'][] = "Proxy-Authorization: Basic {$auth}";
- } else {
- $options['http']['header'] = array("Proxy-Authorization: Basic {$auth}");
- }
+ $options['http']['header'][] = "Proxy-Authorization: Basic {$auth}";
}
}
- $options = array_replace_recursive($options, $defaultOptions);
-
- if (isset($options['http']['header'])) {
- $options['http']['header'] = self::fixHttpHeaderField($options['http']['header']);
- }
-
if (defined('HHVM_VERSION')) {
$phpVersion = 'HHVM ' . HHVM_VERSION;
} else {
$phpVersion = 'PHP ' . PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION . '.' . PHP_RELEASE_VERSION;
}
+ if (extension_loaded('curl')) {
+ $curl = curl_version();
+ $httpVersion = 'curl '.$curl['version'];
+ } else {
+ $httpVersion = 'streams';
+ }
+
if (!isset($options['http']['header']) || false === stripos(implode('', $options['http']['header']), 'user-agent')) {
$options['http']['header'][] = sprintf(
- 'User-Agent: Composer/%s (%s; %s; %s%s)',
- Composer::VERSION === '@package_version@' ? 'source' : Composer::VERSION,
+ 'User-Agent: Composer/%s (%s; %s; %s; %s%s)',
+ Composer::VERSION === '@package_version@' ? Composer::SOURCE_VERSION : Composer::VERSION,
function_exists('php_uname') ? php_uname('s') : 'Unknown',
function_exists('php_uname') ? php_uname('r') : 'Unknown',
$phpVersion,
+ $httpVersion,
getenv('CI') ? '; CI' : ''
);
}
- return stream_context_create($options, $defaultParams);
+ return $options;
+ }
+
+ /**
+ * @param array $options
+ *
+ * @return array
+ */
+ public static function getTlsDefaults(array $options, LoggerInterface $logger = null)
+ {
+ $ciphers = implode(':', array(
+ 'ECDHE-RSA-AES128-GCM-SHA256',
+ 'ECDHE-ECDSA-AES128-GCM-SHA256',
+ 'ECDHE-RSA-AES256-GCM-SHA384',
+ 'ECDHE-ECDSA-AES256-GCM-SHA384',
+ 'DHE-RSA-AES128-GCM-SHA256',
+ 'DHE-DSS-AES128-GCM-SHA256',
+ 'kEDH+AESGCM',
+ 'ECDHE-RSA-AES128-SHA256',
+ 'ECDHE-ECDSA-AES128-SHA256',
+ 'ECDHE-RSA-AES128-SHA',
+ 'ECDHE-ECDSA-AES128-SHA',
+ 'ECDHE-RSA-AES256-SHA384',
+ 'ECDHE-ECDSA-AES256-SHA384',
+ 'ECDHE-RSA-AES256-SHA',
+ 'ECDHE-ECDSA-AES256-SHA',
+ 'DHE-RSA-AES128-SHA256',
+ 'DHE-RSA-AES128-SHA',
+ 'DHE-DSS-AES128-SHA256',
+ 'DHE-RSA-AES256-SHA256',
+ 'DHE-DSS-AES256-SHA',
+ 'DHE-RSA-AES256-SHA',
+ 'AES128-GCM-SHA256',
+ 'AES256-GCM-SHA384',
+ 'AES128-SHA256',
+ 'AES256-SHA256',
+ 'AES128-SHA',
+ 'AES256-SHA',
+ 'AES',
+ 'CAMELLIA',
+ 'DES-CBC3-SHA',
+ '!aNULL',
+ '!eNULL',
+ '!EXPORT',
+ '!DES',
+ '!RC4',
+ '!MD5',
+ '!PSK',
+ '!aECDH',
+ '!EDH-DSS-DES-CBC3-SHA',
+ '!EDH-RSA-DES-CBC3-SHA',
+ '!KRB5-DES-CBC3-SHA',
+ ));
+
+ /**
+ * CN_match and SNI_server_name are only known once a URL is passed.
+ * They will be set in the getOptionsForUrl() method which receives a URL.
+ *
+ * cafile or capath can be overridden by passing in those options to constructor.
+ */
+ $defaults = array(
+ 'ssl' => array(
+ 'ciphers' => $ciphers,
+ 'verify_peer' => true,
+ 'verify_depth' => 7,
+ 'SNI_enabled' => true,
+ 'capture_peer_cert' => true,
+ ),
+ );
+
+ if (isset($options['ssl'])) {
+ $defaults['ssl'] = array_replace_recursive($defaults['ssl'], $options['ssl']);
+ }
+
+ /**
+ * Attempt to find a local cafile or throw an exception if none pre-set
+ * The user may go download one if this occurs.
+ */
+ if (!isset($defaults['ssl']['cafile']) && !isset($defaults['ssl']['capath'])) {
+ $result = CaBundle::getSystemCaRootBundlePath($logger);
+
+ if (is_dir($result)) {
+ $defaults['ssl']['capath'] = $result;
+ } else {
+ $defaults['ssl']['cafile'] = $result;
+ }
+ }
+
+ if (isset($defaults['ssl']['cafile']) && (!is_readable($defaults['ssl']['cafile']) || !CaBundle::validateCaFile($defaults['ssl']['cafile'], $logger))) {
+ throw new TransportException('The configured cafile was not valid or could not be read.');
+ }
+
+ if (isset($defaults['ssl']['capath']) && (!is_dir($defaults['ssl']['capath']) || !is_readable($defaults['ssl']['capath']))) {
+ throw new TransportException('The configured capath was not valid or could not be read.');
+ }
+
+ /**
+ * Disable TLS compression to prevent CRIME attacks where supported.
+ */
+ if (PHP_VERSION_ID >= 50413) {
+ $defaults['ssl']['disable_compression'] = true;
+ }
+
+ return $defaults;
}
/**
diff --git a/src/Composer/Util/Url.php b/src/Composer/Util/Url.php
index 4a5d5f90c..c01677522 100644
--- a/src/Composer/Util/Url.php
+++ b/src/Composer/Util/Url.php
@@ -19,6 +19,12 @@ use Composer\Config;
*/
class Url
{
+ /**
+ * @param Config $config
+ * @param string $url
+ * @param string $ref
+ * @return string the updated URL
+ */
public static function updateDistReference(Config $config, $url, $ref)
{
$host = parse_url($url, PHP_URL_HOST);
@@ -52,4 +58,45 @@ class Url
return $url;
}
+
+ /**
+ * @param string $url
+ * @return string
+ */
+ public static function getOrigin(Config $config, $url)
+ {
+ if (0 === strpos($url, 'file://')) {
+ return $url;
+ }
+
+ $origin = (string) parse_url($url, PHP_URL_HOST);
+
+ if (strpos($origin, '.github.com') === (strlen($origin) - 11)) {
+ return 'github.com';
+ }
+
+ if ($origin === 'repo.packagist.org') {
+ return 'packagist.org';
+ }
+
+ if ($origin === '') {
+ $origin = $url;
+ }
+
+ // Gitlab can be installed in a non-root context (i.e. gitlab.com/foo). When downloading archives the originUrl
+ // is the host without the path, so we look for the registered gitlab-domains matching the host here
+ if (
+ is_array($config->get('gitlab-domains'))
+ && false === strpos($origin, '/')
+ && !in_array($origin, $config->get('gitlab-domains'))
+ ) {
+ foreach ($config->get('gitlab-domains') as $gitlabDomain) {
+ if (0 === strpos($gitlabDomain, $origin)) {
+ return $gitlabDomain;
+ }
+ }
+ }
+
+ return $origin;
+ }
}
diff --git a/tests/Composer/Test/ComposerTest.php b/tests/Composer/Test/ComposerTest.php
index c2c425e76..87270baae 100644
--- a/tests/Composer/Test/ComposerTest.php
+++ b/tests/Composer/Test/ComposerTest.php
@@ -57,7 +57,7 @@ class ComposerTest extends TestCase
public function testSetGetInstallationManager()
{
$composer = new Composer();
- $manager = $this->getMockBuilder('Composer\Installer\InstallationManager')->getMock();
+ $manager = $this->getMockBuilder('Composer\Installer\InstallationManager')->disableOriginalConstructor()->getMock();
$composer->setInstallationManager($manager);
$this->assertSame($manager, $composer->getInstallationManager());
diff --git a/tests/Composer/Test/Downloader/ArchiveDownloaderTest.php b/tests/Composer/Test/Downloader/ArchiveDownloaderTest.php
index 68852d8e0..d887ba103 100644
--- a/tests/Composer/Test/Downloader/ArchiveDownloaderTest.php
+++ b/tests/Composer/Test/Downloader/ArchiveDownloaderTest.php
@@ -29,7 +29,7 @@ class ArchiveDownloaderTest extends TestCase
$method->setAccessible(true);
$first = $method->invoke($downloader, $packageMock, '/path');
- $this->assertRegExp('#/path/[a-z0-9]+\.js#', $first);
+ $this->assertRegExp('#/path_[a-z0-9]+\.js#', $first);
$this->assertSame($first, $method->invoke($downloader, $packageMock, '/path'));
}
@@ -156,7 +156,11 @@ class ArchiveDownloaderTest extends TestCase
{
return $this->getMockForAbstractClass(
'Composer\Downloader\ArchiveDownloader',
- array($this->getMockBuilder('Composer\IO\IOInterface')->getMock(), $this->getMockBuilder('Composer\Config')->getMock())
+ array(
+ $io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock(),
+ $config = $this->getMockBuilder('Composer\Config')->getMock(),
+ new \Composer\Util\HttpDownloader($io, $config),
+ )
);
}
}
diff --git a/tests/Composer/Test/Downloader/DownloadManagerTest.php b/tests/Composer/Test/Downloader/DownloadManagerTest.php
index 222e541d7..307b2294f 100644
--- a/tests/Composer/Test/Downloader/DownloadManagerTest.php
+++ b/tests/Composer/Test/Downloader/DownloadManagerTest.php
@@ -50,7 +50,7 @@ class DownloadManagerTest extends TestCase
$this->setExpectedException('InvalidArgumentException');
- $manager->getDownloaderForInstalledPackage($package);
+ $manager->getDownloaderForPackage($package);
}
public function testGetDownloaderForCorrectlyInstalledDistPackage()
@@ -82,7 +82,7 @@ class DownloadManagerTest extends TestCase
->with('pear')
->will($this->returnValue($downloader));
- $this->assertSame($downloader, $manager->getDownloaderForInstalledPackage($package));
+ $this->assertSame($downloader, $manager->getDownloaderForPackage($package));
}
public function testGetDownloaderForIncorrectlyInstalledDistPackage()
@@ -116,7 +116,7 @@ class DownloadManagerTest extends TestCase
$this->setExpectedException('LogicException');
- $manager->getDownloaderForInstalledPackage($package);
+ $manager->getDownloaderForPackage($package);
}
public function testGetDownloaderForCorrectlyInstalledSourcePackage()
@@ -148,7 +148,7 @@ class DownloadManagerTest extends TestCase
->with('git')
->will($this->returnValue($downloader));
- $this->assertSame($downloader, $manager->getDownloaderForInstalledPackage($package));
+ $this->assertSame($downloader, $manager->getDownloaderForPackage($package));
}
public function testGetDownloaderForIncorrectlyInstalledSourcePackage()
@@ -182,7 +182,7 @@ class DownloadManagerTest extends TestCase
$this->setExpectedException('LogicException');
- $manager->getDownloaderForInstalledPackage($package);
+ $manager->getDownloaderForPackage($package);
}
public function testGetDownloaderForMetapackage()
@@ -195,7 +195,7 @@ class DownloadManagerTest extends TestCase
$manager = new DownloadManager($this->io, false, $this->filesystem);
- $this->assertNull($manager->getDownloaderForInstalledPackage($package));
+ $this->assertNull($manager->getDownloaderForPackage($package));
}
public function testFullPackageDownload()
@@ -223,11 +223,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue($downloader));
@@ -274,16 +274,16 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->at(0))
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue($downloaderFail));
$manager
->expects($this->at(1))
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue($downloaderSuccess));
@@ -333,11 +333,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue($downloader));
@@ -369,11 +369,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue($downloader));
@@ -399,11 +399,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue(null)); // There is no downloader for Metapackages.
@@ -435,11 +435,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue($downloader));
@@ -472,11 +472,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue($downloader));
@@ -509,11 +509,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue($downloader));
@@ -550,33 +550,30 @@ class DownloadManagerTest extends TestCase
$initial
->expects($this->once())
->method('getDistType')
- ->will($this->returnValue('pear'));
+ ->will($this->returnValue('zip'));
$target = $this->createPackageMock();
$target
->expects($this->once())
- ->method('getDistType')
- ->will($this->returnValue('pear'));
+ ->method('getInstallationSource')
+ ->will($this->returnValue('dist'));
$target
->expects($this->once())
- ->method('setInstallationSource')
- ->with('dist');
+ ->method('getDistType')
+ ->will($this->returnValue('zip'));
- $pearDownloader = $this->createDownloaderMock();
- $pearDownloader
+ $zipDownloader = $this->createDownloaderMock();
+ $zipDownloader
->expects($this->once())
->method('update')
->with($initial, $target, 'vendor/bundles/FOS/UserBundle');
+ $zipDownloader
+ ->expects($this->any())
+ ->method('getInstallationSource')
+ ->will($this->returnValue('dist'));
- $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
- ->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
- ->getMock();
- $manager
- ->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
- ->with($initial)
- ->will($this->returnValue($pearDownloader));
+ $manager = new DownloadManager($this->io, false, $this->filesystem);
+ $manager->setDownloader('zip', $zipDownloader);
$manager->update($initial, $target, 'vendor/bundles/FOS/UserBundle');
}
@@ -591,113 +588,89 @@ class DownloadManagerTest extends TestCase
$initial
->expects($this->once())
->method('getDistType')
- ->will($this->returnValue('pear'));
+ ->will($this->returnValue('xz'));
$target = $this->createPackageMock();
$target
- ->expects($this->once())
+ ->expects($this->any())
+ ->method('getInstallationSource')
+ ->will($this->returnValue('dist'));
+ $target
+ ->expects($this->any())
->method('getDistType')
- ->will($this->returnValue('composer'));
+ ->will($this->returnValue('zip'));
- $pearDownloader = $this->createDownloaderMock();
- $pearDownloader
+ $xzDownloader = $this->createDownloaderMock();
+ $xzDownloader
->expects($this->once())
->method('remove')
->with($initial, 'vendor/bundles/FOS/UserBundle');
+ $xzDownloader
+ ->expects($this->any())
+ ->method('getInstallationSource')
+ ->will($this->returnValue('dist'));
- $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
- ->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage', 'download'))
- ->getMock();
- $manager
+ $zipDownloader = $this->createDownloaderMock();
+ $zipDownloader
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
- ->with($initial)
- ->will($this->returnValue($pearDownloader));
- $manager
- ->expects($this->once())
- ->method('download')
- ->with($target, 'vendor/bundles/FOS/UserBundle', false);
+ ->method('install')
+ ->with($target, 'vendor/bundles/FOS/UserBundle');
+ $zipDownloader
+ ->expects($this->any())
+ ->method('getInstallationSource')
+ ->will($this->returnValue('dist'));
+
+ $manager = new DownloadManager($this->io, false, $this->filesystem);
+ $manager->setDownloader('xz', $xzDownloader);
+ $manager->setDownloader('zip', $zipDownloader);
$manager->update($initial, $target, 'vendor/bundles/FOS/UserBundle');
}
- public function testUpdateSourceWithEqualTypes()
+ /**
+ * @dataProvider updatesProvider
+ */
+ public function testGetAvailableSourcesUpdateSticksToSameSource($prevPkgSource, $prevPkgIsDev, $targetAvailable, $targetIsDev, $expected)
{
- $initial = $this->createPackageMock();
- $initial
- ->expects($this->once())
- ->method('getInstallationSource')
- ->will($this->returnValue('source'));
- $initial
- ->expects($this->once())
- ->method('getSourceType')
- ->will($this->returnValue('svn'));
+ $initial = null;
+ if ($prevPkgSource) {
+ $initial = $this->prophesize('Composer\Package\PackageInterface');
+ $initial->getInstallationSource()->willReturn($prevPkgSource);
+ $initial->isDev()->willReturn($prevPkgIsDev);
+ }
- $target = $this->createPackageMock();
- $target
- ->expects($this->once())
- ->method('getSourceType')
- ->will($this->returnValue('svn'));
+ $target = $this->prophesize('Composer\Package\PackageInterface');
+ $target->getSourceType()->willReturn(in_array('source', $targetAvailable, true) ? 'git' : null);
+ $target->getDistType()->willReturn(in_array('dist', $targetAvailable, true) ? 'zip' : null);
+ $target->isDev()->willReturn($targetIsDev);
- $svnDownloader = $this->createDownloaderMock();
- $svnDownloader
- ->expects($this->once())
- ->method('update')
- ->with($initial, $target, 'vendor/pkg');
-
- $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
- ->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage', 'download'))
- ->getMock();
- $manager
- ->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
- ->with($initial)
- ->will($this->returnValue($svnDownloader));
-
- $manager->update($initial, $target, 'vendor/pkg');
+ $manager = new DownloadManager($this->io, false, $this->filesystem);
+ $method = new \ReflectionMethod($manager, 'getAvailableSources');
+ $method->setAccessible(true);
+ $this->assertEquals($expected, $method->invoke($manager, $target->reveal(), $initial ? $initial->reveal() : null));
}
- public function testUpdateSourceWithNotEqualTypes()
+ public static function updatesProvider()
{
- $initial = $this->createPackageMock();
- $initial
- ->expects($this->once())
- ->method('getInstallationSource')
- ->will($this->returnValue('source'));
- $initial
- ->expects($this->once())
- ->method('getSourceType')
- ->will($this->returnValue('svn'));
-
- $target = $this->createPackageMock();
- $target
- ->expects($this->once())
- ->method('getSourceType')
- ->will($this->returnValue('git'));
-
- $svnDownloader = $this->createDownloaderMock();
- $svnDownloader
- ->expects($this->once())
- ->method('remove')
- ->with($initial, 'vendor/pkg');
-
- $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
- ->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage', 'download'))
- ->getMock();
- $manager
- ->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
- ->with($initial)
- ->will($this->returnValue($svnDownloader));
- $manager
- ->expects($this->once())
- ->method('download')
- ->with($target, 'vendor/pkg', true);
-
- $manager->update($initial, $target, 'vendor/pkg');
+ return array(
+ // prevPkg source, prevPkg isDev, pkg available, pkg isDev, expected
+ // updates keep previous source as preference
+ array('source', false, array('source', 'dist'), false, array('source', 'dist')),
+ array('dist', false, array('source', 'dist'), false, array('dist', 'source')),
+ // updates do not keep previous source if target package does not have it
+ array('source', false, array('dist'), false, array('dist')),
+ array('dist', false, array('source'), false, array('source')),
+ // updates do not keep previous source if target is dev and prev wasn't dev and installed from dist
+ array('source', false, array('source', 'dist'), true, array('source', 'dist')),
+ array('dist', false, array('source', 'dist'), true, array('source', 'dist')),
+ // install picks the right default
+ array(null, null, array('source', 'dist'), true, array('source', 'dist')),
+ array(null, null, array('dist'), true, array('dist')),
+ array(null, null, array('source'), true, array('source')),
+ array(null, null, array('source', 'dist'), false, array('dist', 'source')),
+ array(null, null, array('dist'), false, array('dist')),
+ array(null, null, array('source'), false, array('source')),
+ );
}
public function testUpdateMetapackage()
@@ -707,11 +680,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
- ->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->expects($this->exactly(2))
+ ->method('getDownloaderForPackage')
->with($initial)
->will($this->returnValue(null)); // There is no downloader for metapackages.
@@ -730,11 +703,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue($pearDownloader));
@@ -747,11 +720,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue(null)); // There is no downloader for metapackages.
@@ -790,11 +763,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue($downloader));
@@ -833,11 +806,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue($downloader));
@@ -879,11 +852,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue($downloader));
$manager->setPreferences(array('foo/*' => 'source'));
@@ -926,11 +899,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue($downloader));
$manager->setPreferences(array('foo/*' => 'source'));
@@ -973,11 +946,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue($downloader));
$manager->setPreferences(array('foo/*' => 'auto'));
@@ -1020,11 +993,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue($downloader));
$manager->setPreferences(array('foo/*' => 'auto'));
@@ -1063,11 +1036,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue($downloader));
$manager->setPreferences(array('foo/*' => 'source'));
@@ -1106,11 +1079,11 @@ class DownloadManagerTest extends TestCase
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setConstructorArgs(array($this->io, false, $this->filesystem))
- ->setMethods(array('getDownloaderForInstalledPackage'))
+ ->setMethods(array('getDownloaderForPackage'))
->getMock();
$manager
->expects($this->once())
- ->method('getDownloaderForInstalledPackage')
+ ->method('getDownloaderForPackage')
->with($package)
->will($this->returnValue($downloader));
$manager->setPreferences(array('foo/*' => 'dist'));
diff --git a/tests/Composer/Test/Downloader/FileDownloaderTest.php b/tests/Composer/Test/Downloader/FileDownloaderTest.php
index 476b9a8f7..9ce536474 100644
--- a/tests/Composer/Test/Downloader/FileDownloaderTest.php
+++ b/tests/Composer/Test/Downloader/FileDownloaderTest.php
@@ -15,16 +15,23 @@ namespace Composer\Test\Downloader;
use Composer\Downloader\FileDownloader;
use Composer\Test\TestCase;
use Composer\Util\Filesystem;
+use Composer\Util\Http\Response;
+use Composer\Util\Loop;
class FileDownloaderTest extends TestCase
{
- protected function getDownloader($io = null, $config = null, $eventDispatcher = null, $cache = null, $rfs = null, $filesystem = null)
+ protected function getDownloader($io = null, $config = null, $eventDispatcher = null, $cache = null, $httpDownloader = null, $filesystem = null)
{
$io = $io ?: $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
$config = $config ?: $this->getMockBuilder('Composer\Config')->getMock();
- $rfs = $rfs ?: $this->getMockBuilder('Composer\Util\RemoteFilesystem')->disableOriginalConstructor()->getMock();
+ $httpDownloader = $httpDownloader ?: $this->getMockBuilder('Composer\Util\HttpDownloader')->disableOriginalConstructor()->getMock();
+ $httpDownloader
+ ->expects($this->any())
+ ->method('addCopy')
+ ->will($this->returnValue(\React\Promise\resolve(new Response(array('url' => 'http://example.org/'), 200, array(), 'file~'))));
+ $this->httpDownloader = $httpDownloader;
- return new FileDownloader($io, $config, $eventDispatcher, $cache, $rfs, $filesystem);
+ return new FileDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache, $filesystem);
}
/**
@@ -84,7 +91,7 @@ class FileDownloaderTest extends TestCase
$method = new \ReflectionMethod($downloader, 'getFileName');
$method->setAccessible(true);
- $this->assertEquals('/path/script.js', $method->invoke($downloader, $packageMock, '/path'));
+ $this->assertEquals('/path_script.js', $method->invoke($downloader, $packageMock, '/path'));
}
public function testDownloadButFileIsUnsaved()
@@ -118,8 +125,11 @@ class FileDownloaderTest extends TestCase
$downloader = $this->getDownloader($ioMock);
try {
- $downloader->download($packageMock, $path);
- $this->fail();
+ $promise = $downloader->download($packageMock, $path);
+ $loop = new Loop($this->httpDownloader);
+ $loop->wait(array($promise));
+
+ $this->fail('Download was expected to throw');
} catch (\Exception $e) {
if (is_dir($path)) {
$fs = new Filesystem();
@@ -128,7 +138,7 @@ class FileDownloaderTest extends TestCase
unlink($path);
}
- $this->assertInstanceOf('UnexpectedValueException', $e);
+ $this->assertInstanceOf('UnexpectedValueException', $e, $e->getMessage());
$this->assertContains('could not be saved to', $e->getMessage());
}
}
@@ -188,11 +198,14 @@ class FileDownloaderTest extends TestCase
$path = $this->getUniqueTmpDirectory();
$downloader = $this->getDownloader(null, null, null, null, null, $filesystem);
// make sure the file expected to be downloaded is on disk already
- touch($path.'/script.js');
+ touch($path.'_script.js');
try {
- $downloader->download($packageMock, $path);
- $this->fail();
+ $promise = $downloader->download($packageMock, $path);
+ $loop = new Loop($this->httpDownloader);
+ $loop->wait(array($promise));
+
+ $this->fail('Download was expected to throw');
} catch (\Exception $e) {
if (is_dir($path)) {
$fs = new Filesystem();
@@ -201,7 +214,7 @@ class FileDownloaderTest extends TestCase
unlink($path);
}
- $this->assertInstanceOf('UnexpectedValueException', $e);
+ $this->assertInstanceOf('UnexpectedValueException', $e, $e->getMessage());
$this->assertContains('checksum verification', $e->getMessage());
}
}
@@ -232,17 +245,25 @@ class FileDownloaderTest extends TestCase
$ioMock = $this->getMock('Composer\IO\IOInterface');
$ioMock->expects($this->at(0))
+ ->method('writeError')
+ ->with($this->stringContains('Downloading'));
+
+ $ioMock->expects($this->at(1))
->method('writeError')
->with($this->stringContains('Downgrading'));
$path = $this->getUniqueTmpDirectory();
- touch($path.'/script.js');
+ touch($path.'_script.js');
$filesystem = $this->getMock('Composer\Util\Filesystem');
$filesystem->expects($this->once())
->method('removeDirectory')
->will($this->returnValue(true));
$downloader = $this->getDownloader($ioMock, null, null, null, null, $filesystem);
+ $promise = $downloader->download($newPackage, $path, $oldPackage);
+ $loop = new Loop($this->httpDownloader);
+ $loop->wait(array($promise));
+
$downloader->update($oldPackage, $newPackage, $path);
}
}
diff --git a/tests/Composer/Test/Downloader/FossilDownloaderTest.php b/tests/Composer/Test/Downloader/FossilDownloaderTest.php
index 623f7dec2..9ab7b6b84 100644
--- a/tests/Composer/Test/Downloader/FossilDownloaderTest.php
+++ b/tests/Composer/Test/Downloader/FossilDownloaderTest.php
@@ -56,7 +56,7 @@ class FossilDownloaderTest extends TestCase
->will($this->returnValue(null));
$downloader = $this->getDownloaderMock();
- $downloader->download($packageMock, '/path');
+ $downloader->install($packageMock, '/path');
}
public function testDownload()
@@ -89,7 +89,7 @@ class FossilDownloaderTest extends TestCase
->will($this->returnValue(0));
$downloader = $this->getDownloaderMock(null, null, $processExecutor);
- $downloader->download($packageMock, 'repo');
+ $downloader->install($packageMock, 'repo');
}
/**
diff --git a/tests/Composer/Test/Downloader/GitDownloaderTest.php b/tests/Composer/Test/Downloader/GitDownloaderTest.php
index c3cd31a4a..b5d0054de 100644
--- a/tests/Composer/Test/Downloader/GitDownloaderTest.php
+++ b/tests/Composer/Test/Downloader/GitDownloaderTest.php
@@ -79,7 +79,7 @@ class GitDownloaderTest extends TestCase
->will($this->returnValue(null));
$downloader = $this->getDownloaderMock();
- $downloader->download($packageMock, '/path');
+ $downloader->install($packageMock, '/path');
}
public function testDownload()
@@ -130,7 +130,7 @@ class GitDownloaderTest extends TestCase
->will($this->returnValue(0));
$downloader = $this->getDownloaderMock(null, null, $processExecutor);
- $downloader->download($packageMock, 'composerPath');
+ $downloader->install($packageMock, 'composerPath');
}
public function testDownloadWithCache()
@@ -195,7 +195,7 @@ class GitDownloaderTest extends TestCase
->will($this->returnValue(0));
$downloader = $this->getDownloaderMock(null, $config, $processExecutor);
- $downloader->download($packageMock, 'composerPath');
+ $downloader->install($packageMock, 'composerPath');
@rmdir($cachePath);
}
@@ -265,7 +265,7 @@ class GitDownloaderTest extends TestCase
->will($this->returnValue(0));
$downloader = $this->getDownloaderMock(null, new Config(), $processExecutor);
- $downloader->download($packageMock, 'composerPath');
+ $downloader->install($packageMock, 'composerPath');
}
public function pushUrlProvider()
@@ -329,7 +329,7 @@ class GitDownloaderTest extends TestCase
$config->merge(array('config' => array('github-protocols' => $protocols)));
$downloader = $this->getDownloaderMock(null, $config, $processExecutor);
- $downloader->download($packageMock, 'composerPath');
+ $downloader->install($packageMock, 'composerPath');
}
/**
@@ -360,7 +360,7 @@ class GitDownloaderTest extends TestCase
->will($this->returnValue(1));
$downloader = $this->getDownloaderMock(null, null, $processExecutor);
- $downloader->download($packageMock, 'composerPath');
+ $downloader->install($packageMock, 'composerPath');
}
/**
diff --git a/tests/Composer/Test/Downloader/HgDownloaderTest.php b/tests/Composer/Test/Downloader/HgDownloaderTest.php
index c71d463cb..a4219d143 100644
--- a/tests/Composer/Test/Downloader/HgDownloaderTest.php
+++ b/tests/Composer/Test/Downloader/HgDownloaderTest.php
@@ -56,7 +56,7 @@ class HgDownloaderTest extends TestCase
->will($this->returnValue(null));
$downloader = $this->getDownloaderMock();
- $downloader->download($packageMock, '/path');
+ $downloader->install($packageMock, '/path');
}
public function testDownload()
@@ -83,7 +83,7 @@ class HgDownloaderTest extends TestCase
->will($this->returnValue(0));
$downloader = $this->getDownloaderMock(null, null, $processExecutor);
- $downloader->download($packageMock, 'composerPath');
+ $downloader->install($packageMock, 'composerPath');
}
/**
diff --git a/tests/Composer/Test/Downloader/PerforceDownloaderTest.php b/tests/Composer/Test/Downloader/PerforceDownloaderTest.php
index ebb1f0456..d2b8fb753 100644
--- a/tests/Composer/Test/Downloader/PerforceDownloaderTest.php
+++ b/tests/Composer/Test/Downloader/PerforceDownloaderTest.php
@@ -17,6 +17,7 @@ use Composer\Config;
use Composer\Repository\VcsRepository;
use Composer\IO\IOInterface;
use Composer\Test\TestCase;
+use Composer\Factory;
use Composer\Util\Filesystem;
/**
@@ -96,7 +97,7 @@ class PerforceDownloaderTest extends TestCase
{
$repository = $this->getMockBuilder('Composer\Repository\VcsRepository')
->setMethods(array('getRepoConfig'))
- ->setConstructorArgs(array($repoConfig, $io, $config))
+ ->setConstructorArgs(array($repoConfig, $io, $config, Factory::createHttpDownloader($io, $config)))
->getMock();
$repository->expects($this->any())->method('getRepoConfig')->will($this->returnValue($repoConfig));
@@ -137,7 +138,7 @@ class PerforceDownloaderTest extends TestCase
$perforce->expects($this->at(5))->method('syncCodeBase')->with($label);
$perforce->expects($this->at(6))->method('cleanupClientSpec');
$this->downloader->setPerforce($perforce);
- $this->downloader->doDownload($this->package, $this->testPath, 'url');
+ $this->downloader->doInstall($this->package, $this->testPath, 'url');
}
/**
@@ -160,6 +161,6 @@ class PerforceDownloaderTest extends TestCase
$perforce->expects($this->at(5))->method('syncCodeBase')->with($label);
$perforce->expects($this->at(6))->method('cleanupClientSpec');
$this->downloader->setPerforce($perforce);
- $this->downloader->doDownload($this->package, $this->testPath, 'url');
+ $this->downloader->doInstall($this->package, $this->testPath, 'url');
}
}
diff --git a/tests/Composer/Test/Downloader/XzDownloaderTest.php b/tests/Composer/Test/Downloader/XzDownloaderTest.php
index 6df782ddb..4c2fdb2af 100644
--- a/tests/Composer/Test/Downloader/XzDownloaderTest.php
+++ b/tests/Composer/Test/Downloader/XzDownloaderTest.php
@@ -16,7 +16,8 @@ use Composer\Downloader\XzDownloader;
use Composer\Test\TestCase;
use Composer\Util\Filesystem;
use Composer\Util\Platform;
-use Composer\Util\RemoteFilesystem;
+use Composer\Util\Loop;
+use Composer\Util\HttpDownloader;
class XzDownloaderTest extends TestCase
{
@@ -66,10 +67,14 @@ class XzDownloaderTest extends TestCase
->method('get')
->with('vendor-dir')
->will($this->returnValue($this->testDir));
- $downloader = new XzDownloader($io, $config, null, null, null, new RemoteFilesystem($io));
+ $downloader = new XzDownloader($io, $config, $httpDownloader = new HttpDownloader($io, $this->getMockBuilder('Composer\Config')->getMock()), null, null, null);
try {
- $downloader->download($packageMock, $this->getUniqueTmpDirectory());
+ $promise = $downloader->download($packageMock, $this->testDir);
+ $loop = new Loop($httpDownloader);
+ $loop->wait(array($promise));
+ $downloader->install($packageMock, $this->testDir);
+
$this->fail('Download of invalid tarball should throw an exception');
} catch (\RuntimeException $e) {
$this->assertRegexp('/(File format not recognized|Unrecognized archive format)/i', $e->getMessage());
diff --git a/tests/Composer/Test/Downloader/ZipDownloaderTest.php b/tests/Composer/Test/Downloader/ZipDownloaderTest.php
index 466fd35c7..b754af607 100644
--- a/tests/Composer/Test/Downloader/ZipDownloaderTest.php
+++ b/tests/Composer/Test/Downloader/ZipDownloaderTest.php
@@ -16,6 +16,8 @@ use Composer\Downloader\ZipDownloader;
use Composer\Package\PackageInterface;
use Composer\Test\TestCase;
use Composer\Util\Filesystem;
+use Composer\Util\HttpDownloader;
+use Composer\Util\Loop;
class ZipDownloaderTest extends TestCase
{
@@ -26,12 +28,16 @@ class ZipDownloaderTest extends TestCase
private $prophet;
private $io;
private $config;
+ private $package;
public function setUp()
{
$this->testDir = $this->getUniqueTmpDirectory();
$this->io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
$this->config = $this->getMockBuilder('Composer\Config')->getMock();
+ $dlConfig = $this->getMockBuilder('Composer\Config')->getMock();
+ $this->httpDownloader = new HttpDownloader($this->io, $dlConfig);
+ $this->package = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
}
public function tearDown()
@@ -64,42 +70,33 @@ class ZipDownloaderTest extends TestCase
}
$this->config->expects($this->at(0))
- ->method('get')
- ->with('disable-tls')
- ->will($this->returnValue(false));
- $this->config->expects($this->at(1))
- ->method('get')
- ->with('cafile')
- ->will($this->returnValue(null));
- $this->config->expects($this->at(2))
- ->method('get')
- ->with('capath')
- ->will($this->returnValue(null));
- $this->config->expects($this->at(3))
->method('get')
->with('vendor-dir')
->will($this->returnValue($this->testDir));
- $packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
- $packageMock->expects($this->any())
+ $this->package->expects($this->any())
->method('getDistUrl')
->will($this->returnValue($distUrl = 'file://'.__FILE__))
;
- $packageMock->expects($this->any())
+ $this->package->expects($this->any())
->method('getDistUrls')
->will($this->returnValue(array($distUrl)))
;
- $packageMock->expects($this->atLeastOnce())
+ $this->package->expects($this->atLeastOnce())
->method('getTransportOptions')
->will($this->returnValue(array()))
;
- $downloader = new ZipDownloader($this->io, $this->config);
+ $downloader = new ZipDownloader($this->io, $this->config, $this->httpDownloader);
$this->setPrivateProperty('hasSystemUnzip', false);
try {
- $downloader->download($packageMock, sys_get_temp_dir().'/composer-zip-test');
+ $promise = $downloader->download($this->package, $path = sys_get_temp_dir().'/composer-zip-test');
+ $loop = new Loop($this->httpDownloader);
+ $loop->wait(array($promise));
+ $downloader->install($this->package, $path);
+
$this->fail('Download of invalid zip files should throw an exception');
} catch (\Exception $e) {
$this->assertContains('is not a zip archive', $e->getMessage());
@@ -118,8 +115,7 @@ class ZipDownloaderTest extends TestCase
$this->setPrivateProperty('hasSystemUnzip', false);
$this->setPrivateProperty('hasZipArchive', true);
- $downloader = new MockedZipDownloader($this->io, $this->config);
-
+ $downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader);
$zipArchive = $this->getMockBuilder('ZipArchive')->getMock();
$zipArchive->expects($this->at(0))
->method('open')
@@ -129,7 +125,7 @@ class ZipDownloaderTest extends TestCase
->will($this->returnValue(false));
$this->setPrivateProperty('zipArchiveObject', $zipArchive, $downloader);
- $downloader->extract('testfile.zip', 'vendor/dir');
+ $downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
}
/**
@@ -144,8 +140,7 @@ class ZipDownloaderTest extends TestCase
$this->setPrivateProperty('hasSystemUnzip', false);
$this->setPrivateProperty('hasZipArchive', true);
- $downloader = new MockedZipDownloader($this->io, $this->config);
-
+ $downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader);
$zipArchive = $this->getMockBuilder('ZipArchive')->getMock();
$zipArchive->expects($this->at(0))
->method('open')
@@ -155,7 +150,7 @@ class ZipDownloaderTest extends TestCase
->will($this->throwException(new \ErrorException('Not a directory')));
$this->setPrivateProperty('zipArchiveObject', $zipArchive, $downloader);
- $downloader->extract('testfile.zip', 'vendor/dir');
+ $downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
}
/**
@@ -169,8 +164,7 @@ class ZipDownloaderTest extends TestCase
$this->setPrivateProperty('hasSystemUnzip', false);
$this->setPrivateProperty('hasZipArchive', true);
- $downloader = new MockedZipDownloader($this->io, $this->config);
-
+ $downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader);
$zipArchive = $this->getMockBuilder('ZipArchive')->getMock();
$zipArchive->expects($this->at(0))
->method('open')
@@ -180,7 +174,7 @@ class ZipDownloaderTest extends TestCase
->will($this->returnValue(true));
$this->setPrivateProperty('zipArchiveObject', $zipArchive, $downloader);
- $downloader->extract('testfile.zip', 'vendor/dir');
+ $downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
}
/**
@@ -200,8 +194,8 @@ class ZipDownloaderTest extends TestCase
->method('execute')
->will($this->returnValue(1));
- $downloader = new MockedZipDownloader($this->io, $this->config, null, null, $processExecutor);
- $downloader->extract('testfile.zip', 'vendor/dir');
+ $downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader, null, null, $processExecutor);
+ $downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
}
public function testSystemUnzipOnlyGood()
@@ -217,8 +211,8 @@ class ZipDownloaderTest extends TestCase
->method('execute')
->will($this->returnValue(0));
- $downloader = new MockedZipDownloader($this->io, $this->config, null, null, $processExecutor);
- $downloader->extract('testfile.zip', 'vendor/dir');
+ $downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader, null, null, $processExecutor);
+ $downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
}
public function testNonWindowsFallbackGood()
@@ -244,9 +238,9 @@ class ZipDownloaderTest extends TestCase
->method('extractTo')
->will($this->returnValue(true));
- $downloader = new MockedZipDownloader($this->io, $this->config, null, null, $processExecutor);
+ $downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader, null, null, $processExecutor);
$this->setPrivateProperty('zipArchiveObject', $zipArchive, $downloader);
- $downloader->extract('testfile.zip', 'vendor/dir');
+ $downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
}
/**
@@ -276,9 +270,9 @@ class ZipDownloaderTest extends TestCase
->method('extractTo')
->will($this->returnValue(false));
- $downloader = new MockedZipDownloader($this->io, $this->config, null, null, $processExecutor);
+ $downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader, null, null, $processExecutor);
$this->setPrivateProperty('zipArchiveObject', $zipArchive, $downloader);
- $downloader->extract('testfile.zip', 'vendor/dir');
+ $downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
}
public function testWindowsFallbackGood()
@@ -304,9 +298,9 @@ class ZipDownloaderTest extends TestCase
->method('extractTo')
->will($this->returnValue(false));
- $downloader = new MockedZipDownloader($this->io, $this->config, null, null, $processExecutor);
+ $downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader, null, null, $processExecutor);
$this->setPrivateProperty('zipArchiveObject', $zipArchive, $downloader);
- $downloader->extract('testfile.zip', 'vendor/dir');
+ $downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
}
/**
@@ -336,9 +330,9 @@ class ZipDownloaderTest extends TestCase
->method('extractTo')
->will($this->returnValue(false));
- $downloader = new MockedZipDownloader($this->io, $this->config, null, null, $processExecutor);
+ $downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader, null, null, $processExecutor);
$this->setPrivateProperty('zipArchiveObject', $zipArchive, $downloader);
- $downloader->extract('testfile.zip', 'vendor/dir');
+ $downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
}
}
@@ -349,8 +343,13 @@ class MockedZipDownloader extends ZipDownloader
return;
}
- public function extract($file, $path)
+ public function install(PackageInterface $package, $path, $output = true)
{
- parent::extract($file, $path);
+ return;
+ }
+
+ public function extract(PackageInterface $package, $file, $path)
+ {
+ parent::extract($package, $file, $path);
}
}
diff --git a/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php b/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php
index 7786e7807..6d812e20a 100644
--- a/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php
+++ b/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php
@@ -101,7 +101,7 @@ class EventDispatcherTest extends TestCase
$composer->setPackage($package);
$composer->setRepositoryManager($this->getRepositoryManagerMockForDevModePassingTest());
- $composer->setInstallationManager($this->getMockBuilder('Composer\Installer\InstallationManager')->getMock());
+ $composer->setInstallationManager($this->getMockBuilder('Composer\Installer\InstallationManager')->disableOriginalConstructor()->getMock());
$dispatcher = new EventDispatcher(
$composer,
diff --git a/tests/Composer/Test/FactoryTest.php b/tests/Composer/Test/FactoryTest.php
index 6704e5b15..96b0e95d5 100644
--- a/tests/Composer/Test/FactoryTest.php
+++ b/tests/Composer/Test/FactoryTest.php
@@ -35,6 +35,6 @@ class FactoryTest extends TestCase
->with($this->equalTo('disable-tls'))
->will($this->returnValue(true));
- Factory::createRemoteFilesystem($ioMock, $config);
+ Factory::createHttpDownloader($ioMock, $config);
}
}
diff --git a/tests/Composer/Test/Installer/InstallationManagerTest.php b/tests/Composer/Test/Installer/InstallationManagerTest.php
index 86e860bc2..407a5b10f 100644
--- a/tests/Composer/Test/Installer/InstallationManagerTest.php
+++ b/tests/Composer/Test/Installer/InstallationManagerTest.php
@@ -13,6 +13,7 @@
namespace Composer\Test\Installer;
use Composer\Installer\InstallationManager;
+use Composer\Installer\NoopInstaller;
use Composer\DependencyResolver\Operation\InstallOperation;
use Composer\DependencyResolver\Operation\UpdateOperation;
use Composer\DependencyResolver\Operation\UninstallOperation;
@@ -21,9 +22,11 @@ use PHPUnit\Framework\TestCase;
class InstallationManagerTest extends TestCase
{
protected $repository;
+ protected $loop;
public function setUp()
{
+ $this->loop = $this->getMockBuilder('Composer\Util\Loop')->disableOriginalConstructor()->getMock();
$this->repository = $this->getMockBuilder('Composer\Repository\InstalledRepositoryInterface')->getMock();
}
@@ -38,7 +41,7 @@ class InstallationManagerTest extends TestCase
return $arg === 'vendor';
}));
- $manager = new InstallationManager();
+ $manager = new InstallationManager($this->loop);
$manager->addInstaller($installer);
$this->assertSame($installer, $manager->getInstaller('vendor'));
@@ -67,7 +70,7 @@ class InstallationManagerTest extends TestCase
return $arg === 'vendor';
}));
- $manager = new InstallationManager();
+ $manager = new InstallationManager($this->loop);
$manager->addInstaller($installer);
$this->assertSame($installer, $manager->getInstaller('vendor'));
@@ -80,16 +83,21 @@ class InstallationManagerTest extends TestCase
public function testExecute()
{
$manager = $this->getMockBuilder('Composer\Installer\InstallationManager')
+ ->setConstructorArgs(array($this->loop))
->setMethods(array('install', 'update', 'uninstall'))
->getMock();
- $installOperation = new InstallOperation($this->createPackageMock());
- $removeOperation = new UninstallOperation($this->createPackageMock());
+ $installOperation = new InstallOperation($package = $this->createPackageMock());
+ $removeOperation = new UninstallOperation($package);
$updateOperation = new UpdateOperation(
- $this->createPackageMock(),
- $this->createPackageMock()
+ $package,
+ $package
);
+ $package->expects($this->any())
+ ->method('getType')
+ ->will($this->returnValue('library'));
+
$manager
->expects($this->once())
->method('install')
@@ -103,6 +111,7 @@ class InstallationManagerTest extends TestCase
->method('update')
->with($this->repository, $updateOperation);
+ $manager->addInstaller(new NoopInstaller());
$manager->execute($this->repository, $installOperation);
$manager->execute($this->repository, $removeOperation);
$manager->execute($this->repository, $updateOperation);
@@ -111,7 +120,7 @@ class InstallationManagerTest extends TestCase
public function testInstall()
{
$installer = $this->createInstallerMock();
- $manager = new InstallationManager();
+ $manager = new InstallationManager($this->loop);
$manager->addInstaller($installer);
$package = $this->createPackageMock();
@@ -139,7 +148,7 @@ class InstallationManagerTest extends TestCase
public function testUpdateWithEqualTypes()
{
$installer = $this->createInstallerMock();
- $manager = new InstallationManager();
+ $manager = new InstallationManager($this->loop);
$manager->addInstaller($installer);
$initial = $this->createPackageMock();
@@ -173,18 +182,17 @@ class InstallationManagerTest extends TestCase
{
$libInstaller = $this->createInstallerMock();
$bundleInstaller = $this->createInstallerMock();
- $manager = new InstallationManager();
+ $manager = new InstallationManager($this->loop);
$manager->addInstaller($libInstaller);
$manager->addInstaller($bundleInstaller);
$initial = $this->createPackageMock();
- $target = $this->createPackageMock();
- $operation = new UpdateOperation($initial, $target, 'test');
-
$initial
->expects($this->once())
->method('getType')
->will($this->returnValue('library'));
+
+ $target = $this->createPackageMock();
$target
->expects($this->once())
->method('getType')
@@ -213,13 +221,14 @@ class InstallationManagerTest extends TestCase
->method('install')
->with($this->repository, $target);
+ $operation = new UpdateOperation($initial, $target, 'test');
$manager->update($this->repository, $operation);
}
public function testUninstall()
{
$installer = $this->createInstallerMock();
- $manager = new InstallationManager();
+ $manager = new InstallationManager($this->loop);
$manager->addInstaller($installer);
$package = $this->createPackageMock();
@@ -249,7 +258,7 @@ class InstallationManagerTest extends TestCase
$installer = $this->getMockBuilder('Composer\Installer\LibraryInstaller')
->disableOriginalConstructor()
->getMock();
- $manager = new InstallationManager();
+ $manager = new InstallationManager($this->loop);
$manager->addInstaller($installer);
$package = $this->createPackageMock();
@@ -281,7 +290,9 @@ class InstallationManagerTest extends TestCase
private function createPackageMock()
{
- return $this->getMockBuilder('Composer\Package\PackageInterface')
+ $mock = $this->getMockBuilder('Composer\Package\PackageInterface')
->getMock();
+
+ return $mock;
}
}
diff --git a/tests/Composer/Test/Installer/LibraryInstallerTest.php b/tests/Composer/Test/Installer/LibraryInstallerTest.php
index 772bb05c8..672f8eb0a 100644
--- a/tests/Composer/Test/Installer/LibraryInstallerTest.php
+++ b/tests/Composer/Test/Installer/LibraryInstallerTest.php
@@ -113,7 +113,7 @@ class LibraryInstallerTest extends TestCase
$this->dm
->expects($this->once())
- ->method('download')
+ ->method('install')
->with($package, $this->vendorDir.'/some/package');
$this->repository
diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php
index f21007281..acaf8f1ff 100644
--- a/tests/Composer/Test/InstallerTest.php
+++ b/tests/Composer/Test/InstallerTest.php
@@ -63,7 +63,9 @@ class InstallerTest extends TestCase
->getMock();
$config = $this->getMockBuilder('Composer\Config')->getMock();
- $repositoryManager = new RepositoryManager($io, $config);
+ $eventDispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')->disableOriginalConstructor()->getMock();
+ $httpDownloader = $this->getMockBuilder('Composer\Util\HttpDownloader')->disableOriginalConstructor()->getMock();
+ $repositoryManager = new RepositoryManager($io, $config, $httpDownloader, $eventDispatcher);
$repositoryManager->setLocalRepository(new InstalledArrayRepository());
if (!is_array($repositories)) {
@@ -76,7 +78,6 @@ class InstallerTest extends TestCase
$locker = $this->getMockBuilder('Composer\Package\Locker')->disableOriginalConstructor()->getMock();
$installationManager = new InstallationManagerMock();
- $eventDispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')->disableOriginalConstructor()->getMock();
$autoloadGenerator = $this->getMockBuilder('Composer\Autoload\AutoloadGenerator')->disableOriginalConstructor()->getMock();
$installer = new Installer($io, $config, clone $rootPackage, $downloadManager, $repositoryManager, $locker, $installationManager, $eventDispatcher, $autoloadGenerator);
diff --git a/tests/Composer/Test/Mock/FactoryMock.php b/tests/Composer/Test/Mock/FactoryMock.php
index 47683afcd..fcb93d2cc 100644
--- a/tests/Composer/Test/Mock/FactoryMock.php
+++ b/tests/Composer/Test/Mock/FactoryMock.php
@@ -20,6 +20,7 @@ use Composer\Repository\WritableRepositoryInterface;
use Composer\Installer;
use Composer\IO\IOInterface;
use Composer\Test\TestCase;
+use Composer\Util\Loop;
class FactoryMock extends Factory
{
@@ -39,9 +40,9 @@ class FactoryMock extends Factory
{
}
- protected function createInstallationManager()
+ public function createInstallationManager(Loop $loop)
{
- return new InstallationManagerMock;
+ return new InstallationManagerMock();
}
protected function createDefaultInstallers(Installer\InstallationManager $im, Composer $composer, IOInterface $io)
diff --git a/tests/Composer/Test/Mock/RemoteFilesystemMock.php b/tests/Composer/Test/Mock/HttpDownloaderMock.php
similarity index 73%
rename from tests/Composer/Test/Mock/RemoteFilesystemMock.php
rename to tests/Composer/Test/Mock/HttpDownloaderMock.php
index 5d4f52e54..1e2774af0 100644
--- a/tests/Composer/Test/Mock/RemoteFilesystemMock.php
+++ b/tests/Composer/Test/Mock/HttpDownloaderMock.php
@@ -12,13 +12,11 @@
namespace Composer\Test\Mock;
-use Composer\Util\RemoteFilesystem;
+use Composer\Util\HttpDownloader;
+use Composer\Util\Http\Response;
use Composer\Downloader\TransportException;
-/**
- * Remote filesystem mock
- */
-class RemoteFilesystemMock extends RemoteFilesystem
+class HttpDownloaderMock extends HttpDownloader
{
protected $contentMap;
@@ -30,10 +28,10 @@ class RemoteFilesystemMock extends RemoteFilesystem
$this->contentMap = $contentMap;
}
- public function getContents($originUrl, $fileUrl, $progress = true, $options = array())
+ public function get($fileUrl, $options = array())
{
if (!empty($this->contentMap[$fileUrl])) {
- return $this->contentMap[$fileUrl];
+ return new Response(array('url' => $fileUrl), 200, array(), $this->contentMap[$fileUrl]);
}
throw new TransportException('The "'.$fileUrl.'" file could not be downloaded (NOT FOUND)', 404);
diff --git a/tests/Composer/Test/Mock/InstallationManagerMock.php b/tests/Composer/Test/Mock/InstallationManagerMock.php
index de1de514b..21e717224 100644
--- a/tests/Composer/Test/Mock/InstallationManagerMock.php
+++ b/tests/Composer/Test/Mock/InstallationManagerMock.php
@@ -17,6 +17,7 @@ use Composer\Repository\RepositoryInterface;
use Composer\Repository\InstalledRepositoryInterface;
use Composer\Package\PackageInterface;
use Composer\DependencyResolver\Operation\InstallOperation;
+use Composer\DependencyResolver\Operation\OperationInterface;
use Composer\DependencyResolver\Operation\UpdateOperation;
use Composer\DependencyResolver\Operation\UninstallOperation;
use Composer\DependencyResolver\Operation\MarkAliasInstalledOperation;
@@ -29,6 +30,18 @@ class InstallationManagerMock extends InstallationManager
private $uninstalled = array();
private $trace = array();
+ public function __construct()
+ {
+
+ }
+
+ public function execute(RepositoryInterface $repo, OperationInterface $operation)
+ {
+ $method = $operation->getJobType();
+ // skipping download() step here for tests
+ $this->$method($repo, $operation);
+ }
+
public function getInstallPath(PackageInterface $package)
{
return '';
diff --git a/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php b/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php
index f9fe308fa..714c9b923 100644
--- a/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php
+++ b/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php
@@ -12,9 +12,12 @@
namespace Composer\Test\Package\Archiver;
+use Composer\IO\NullIO;
use Composer\Factory;
use Composer\Package\Archiver\ArchiveManager;
use Composer\Package\PackageInterface;
+use Composer\Util\Loop;
+use Composer\Test\Mock\FactoryMock;
class ArchiveManagerTest extends ArchiverTest
{
@@ -30,7 +33,13 @@ class ArchiveManagerTest extends ArchiverTest
parent::setUp();
$factory = new Factory();
- $this->manager = $factory->createArchiveManager($factory->createConfig());
+ $dm = $factory->createDownloadManager(
+ $io = new NullIO,
+ $config = FactoryMock::createConfig(),
+ $httpDownloader = $factory->createHttpDownloader($io, $config)
+ );
+ $loop = new Loop($httpDownloader);
+ $this->manager = $factory->createArchiveManager($factory->createConfig(), $dm, $loop);
$this->targetDir = $this->testDir.'/composer_archiver_tests';
}
diff --git a/tests/Composer/Test/Plugin/PluginInstallerTest.php b/tests/Composer/Test/Plugin/PluginInstallerTest.php
index 01832f94d..633c5ab18 100644
--- a/tests/Composer/Test/Plugin/PluginInstallerTest.php
+++ b/tests/Composer/Test/Plugin/PluginInstallerTest.php
@@ -89,7 +89,7 @@ class PluginInstallerTest extends TestCase
->method('getLocalRepository')
->will($this->returnValue($this->repository));
- $im = $this->getMockBuilder('Composer\Installer\InstallationManager')->getMock();
+ $im = $this->getMockBuilder('Composer\Installer\InstallationManager')->disableOriginalConstructor()->getMock();
$im->expects($this->any())
->method('getInstallPath')
->will($this->returnCallback(function ($package) {
diff --git a/tests/Composer/Test/Repository/ComposerRepositoryTest.php b/tests/Composer/Test/Repository/ComposerRepositoryTest.php
index 05e1afada..55ca6bf09 100644
--- a/tests/Composer/Test/Repository/ComposerRepositoryTest.php
+++ b/tests/Composer/Test/Repository/ComposerRepositoryTest.php
@@ -18,7 +18,7 @@ use Composer\Repository\RepositoryInterface;
use Composer\Test\Mock\FactoryMock;
use Composer\Test\TestCase;
use Composer\Package\Loader\ArrayLoader;
-use Composer\Semver\VersionParser;
+use Composer\Package\Version\VersionParser;
class ComposerRepositoryTest extends TestCase
{
@@ -32,11 +32,13 @@ class ComposerRepositoryTest extends TestCase
);
$repository = $this->getMockBuilder('Composer\Repository\ComposerRepository')
- ->setMethods(array('loadRootServerFile', 'createPackage'))
+ ->setMethods(array('loadRootServerFile', 'createPackages'))
->setConstructorArgs(array(
$repoConfig,
new NullIO,
FactoryMock::createConfig(),
+ $this->getMockBuilder('Composer\Util\HttpDownloader')->disableOriginalConstructor()->getMock(),
+ $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')->disableOriginalConstructor()->getMock()
))
->getMock();
@@ -45,16 +47,17 @@ class ComposerRepositoryTest extends TestCase
->method('loadRootServerFile')
->will($this->returnValue($repoPackages));
+ $stubs = array();
foreach ($expected as $at => $arg) {
- $stubPackage = $this->getPackage('stub/stub', '1.0.0');
-
- $repository
- ->expects($this->at($at + 2))
- ->method('createPackage')
- ->with($this->identicalTo($arg), $this->equalTo('Composer\Package\CompletePackage'))
- ->will($this->returnValue($stubPackage));
+ $stubs[] = $this->getPackage('stub/stub', '1.0.0');
}
+ $repository
+ ->expects($this->at(2))
+ ->method('createPackages')
+ ->with($this->identicalTo($expected), $this->equalTo('Composer\Package\CompletePackage'))
+ ->will($this->returnValue($stubs));
+
// Triggers initialization
$packages = $repository->getPackages();
@@ -143,19 +146,12 @@ class ComposerRepositoryTest extends TestCase
)));
$versionParser = new VersionParser();
- $repo->setRootAliases(array(
- 'a' => array(
- $versionParser->normalize('0.6') => array('alias' => 'dev-feature', 'alias_normalized' => $versionParser->normalize('dev-feature')),
- $versionParser->normalize('1.1.x-dev') => array('alias' => '1.0', 'alias_normalized' => $versionParser->normalize('1.0')),
- ),
- ));
+ $reflMethod = new \ReflectionMethod($repo, 'whatProvides');
+ $reflMethod->setAccessible(true);
+ $packages = $reflMethod->invoke($repo, 'a', array($this, 'isPackageAcceptableReturnTrue'));
- $packages = $repo->whatProvides('a', false, array($this, 'isPackageAcceptableReturnTrue'));
-
- $this->assertCount(7, $packages);
- $this->assertEquals(array('1', '1-alias', '2', '2-alias', '2-root', '3', '3-root'), array_keys($packages));
- $this->assertInstanceOf('Composer\Package\AliasPackage', $packages['2-root']);
- $this->assertSame($packages['2'], $packages['2-root']->getAliasOf());
+ $this->assertCount(5, $packages);
+ $this->assertEquals(array('1', '1-alias', '2', '2-alias', '3'), array_keys($packages));
$this->assertSame($packages['2'], $packages['2-alias']->getAliasOf());
}
@@ -179,21 +175,29 @@ class ComposerRepositoryTest extends TestCase
),
);
- $rfs = $this->getMockBuilder('Composer\Util\RemoteFilesystem')
+ $httpDownloader = $this->getMockBuilder('Composer\Util\HttpDownloader')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $eventDispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')
->disableOriginalConstructor()
->getMock();
- $rfs->expects($this->at(0))
- ->method('getContents')
- ->with('example.org', 'http://example.org/packages.json', false)
- ->willReturn(json_encode(array('search' => '/search.json?q=%query%&type=%type%')));
+ $httpDownloader->expects($this->at(0))
+ ->method('get')
+ ->with($url = 'http://example.org/packages.json')
+ ->willReturn(new \Composer\Util\Http\Response(array('url' => $url), 200, array(), json_encode(array('search' => '/search.json?q=%query%&type=%type%'))));
- $rfs->expects($this->at(1))
- ->method('getContents')
- ->with('example.org', 'http://example.org/search.json?q=foo&type=composer-plugin', false)
- ->willReturn(json_encode($result));
+ $httpDownloader->expects($this->at(1))
+ ->method('get')
+ ->with($url = 'http://example.org/search.json?q=foo&type=composer-plugin')
+ ->willReturn(new \Composer\Util\Http\Response(array('url' => $url), 200, array(), json_encode($result)));
- $repository = new ComposerRepository($repoConfig, new NullIO, FactoryMock::createConfig(), null, $rfs);
+ $httpDownloader->expects($this->at(2))
+ ->method('get')
+ ->with($url = 'http://example.org/search.json?q=foo&type=library')
+ ->willReturn(new \Composer\Util\Http\Response(array('url' => $url), 200, array(), json_encode(array())));
+
+ $repository = new ComposerRepository($repoConfig, new NullIO, FactoryMock::createConfig(), $httpDownloader, $eventDispatcher);
$this->assertSame(
array(array('name' => 'foo', 'description' => null)),
diff --git a/tests/Composer/Test/Repository/PathRepositoryTest.php b/tests/Composer/Test/Repository/PathRepositoryTest.php
index a9594257c..abe6063f4 100644
--- a/tests/Composer/Test/Repository/PathRepositoryTest.php
+++ b/tests/Composer/Test/Repository/PathRepositoryTest.php
@@ -14,8 +14,8 @@ namespace Composer\Test\Repository;
use Composer\Package\Loader\ArrayLoader;
use Composer\Repository\PathRepository;
-use Composer\Semver\VersionParser;
use Composer\Test\TestCase;
+use Composer\Package\Version\VersionParser;
class PathRepositoryTest extends TestCase
{
diff --git a/tests/Composer/Test/Repository/Pear/ChannelReaderTest.php b/tests/Composer/Test/Repository/Pear/ChannelReaderTest.php
index e766065a7..95e59e906 100644
--- a/tests/Composer/Test/Repository/Pear/ChannelReaderTest.php
+++ b/tests/Composer/Test/Repository/Pear/ChannelReaderTest.php
@@ -22,19 +22,19 @@ use Composer\Semver\VersionParser;
use Composer\Semver\Constraint\Constraint;
use Composer\Package\Link;
use Composer\Package\CompletePackage;
-use Composer\Test\Mock\RemoteFilesystemMock;
+use Composer\Test\Mock\HttpDownloaderMock;
class ChannelReaderTest extends TestCase
{
public function testShouldBuildPackagesFromPearSchema()
{
- $rfs = new RemoteFilesystemMock(array(
+ $httpDownloader = new HttpDownloaderMock(array(
'http://pear.net/channel.xml' => file_get_contents(__DIR__ . '/Fixtures/channel.1.1.xml'),
'http://test.loc/rest11/c/categories.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.1/categories.xml'),
'http://test.loc/rest11/c/Default/packagesinfo.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.1/packagesinfo.xml'),
));
- $reader = new \Composer\Repository\Pear\ChannelReader($rfs);
+ $reader = new \Composer\Repository\Pear\ChannelReader($httpDownloader);
$channelInfo = $reader->read('http://pear.net/');
$packages = $channelInfo->getPackages();
@@ -50,17 +50,21 @@ class ChannelReaderTest extends TestCase
public function testShouldSelectCorrectReader()
{
- $rfs = new RemoteFilesystemMock(array(
+ $httpDownloader = new HttpDownloaderMock(array(
'http://pear.1.0.net/channel.xml' => file_get_contents(__DIR__ . '/Fixtures/channel.1.0.xml'),
'http://test.loc/rest10/p/packages.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.0/packages.xml'),
'http://test.loc/rest10/p/http_client/info.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.0/http_client_info.xml'),
+ 'http://test.loc/rest10/r/http_client/allreleases.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.0/http_client_allreleases.xml'),
+ 'http://test.loc/rest10/r/http_client/deps.1.2.1.txt' => file_get_contents(__DIR__ . '/Fixtures/Rest1.0/http_client_deps.1.2.1.txt'),
'http://test.loc/rest10/p/http_request/info.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.0/http_request_info.xml'),
+ 'http://test.loc/rest10/r/http_request/allreleases.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.0/http_request_allreleases.xml'),
+ 'http://test.loc/rest10/r/http_request/deps.1.4.0.txt' => file_get_contents(__DIR__ . '/Fixtures/Rest1.0/http_request_deps.1.4.0.txt'),
'http://pear.1.1.net/channel.xml' => file_get_contents(__DIR__ . '/Fixtures/channel.1.1.xml'),
'http://test.loc/rest11/c/categories.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.1/categories.xml'),
'http://test.loc/rest11/c/Default/packagesinfo.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.1/packagesinfo.xml'),
));
- $reader = new \Composer\Repository\Pear\ChannelReader($rfs);
+ $reader = new \Composer\Repository\Pear\ChannelReader($httpDownloader);
$reader->read('http://pear.1.0.net/');
$reader->read('http://pear.1.1.net/');
diff --git a/tests/Composer/Test/Repository/Pear/ChannelRest10ReaderTest.php b/tests/Composer/Test/Repository/Pear/ChannelRest10ReaderTest.php
index 4aa7bbba2..3960c7858 100644
--- a/tests/Composer/Test/Repository/Pear/ChannelRest10ReaderTest.php
+++ b/tests/Composer/Test/Repository/Pear/ChannelRest10ReaderTest.php
@@ -13,13 +13,13 @@
namespace Composer\Test\Repository\Pear;
use Composer\Test\TestCase;
-use Composer\Test\Mock\RemoteFilesystemMock;
+use Composer\Test\Mock\HttpDownloaderMock;
class ChannelRest10ReaderTest extends TestCase
{
public function testShouldBuildPackagesFromPearSchema()
{
- $rfs = new RemoteFilesystemMock(array(
+ $httpDownloader = new HttpDownloaderMock(array(
'http://test.loc/rest10/p/packages.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.0/packages.xml'),
'http://test.loc/rest10/p/http_client/info.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.0/http_client_info.xml'),
'http://test.loc/rest10/r/http_client/allreleases.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.0/http_client_allreleases.xml'),
@@ -29,7 +29,7 @@ class ChannelRest10ReaderTest extends TestCase
'http://test.loc/rest10/r/http_request/deps.1.4.0.txt' => file_get_contents(__DIR__ . '/Fixtures/Rest1.0/http_request_deps.1.4.0.txt'),
));
- $reader = new \Composer\Repository\Pear\ChannelRest10Reader($rfs);
+ $reader = new \Composer\Repository\Pear\ChannelRest10Reader($httpDownloader);
/** @var \Composer\Package\PackageInterface[] $packages */
$packages = $reader->read('http://test.loc/rest10');
diff --git a/tests/Composer/Test/Repository/Pear/ChannelRest11ReaderTest.php b/tests/Composer/Test/Repository/Pear/ChannelRest11ReaderTest.php
index 04e48426e..684c59155 100644
--- a/tests/Composer/Test/Repository/Pear/ChannelRest11ReaderTest.php
+++ b/tests/Composer/Test/Repository/Pear/ChannelRest11ReaderTest.php
@@ -13,19 +13,19 @@
namespace Composer\Test\Repository\Pear;
use Composer\Test\TestCase;
-use Composer\Test\Mock\RemoteFilesystemMock;
+use Composer\Test\Mock\HttpDownloaderMock;
class ChannelRest11ReaderTest extends TestCase
{
public function testShouldBuildPackagesFromPearSchema()
{
- $rfs = new RemoteFilesystemMock(array(
+ $httpDownloader = new HttpDownloaderMock(array(
'http://pear.1.1.net/channel.xml' => file_get_contents(__DIR__ . '/Fixtures/channel.1.1.xml'),
'http://test.loc/rest11/c/categories.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.1/categories.xml'),
'http://test.loc/rest11/c/Default/packagesinfo.xml' => file_get_contents(__DIR__ . '/Fixtures/Rest1.1/packagesinfo.xml'),
));
- $reader = new \Composer\Repository\Pear\ChannelRest11Reader($rfs);
+ $reader = new \Composer\Repository\Pear\ChannelRest11Reader($httpDownloader);
/** @var \Composer\Package\PackageInterface[] $packages */
$packages = $reader->read('http://test.loc/rest11');
diff --git a/tests/Composer/Test/Repository/PearRepositoryTest.php b/tests/Composer/Test/Repository/PearRepositoryTest.php
index b1a3c0b5e..867d4978d 100644
--- a/tests/Composer/Test/Repository/PearRepositoryTest.php
+++ b/tests/Composer/Test/Repository/PearRepositoryTest.php
@@ -28,7 +28,7 @@ class PearRepositoryTest extends TestCase
/**
* @var \PHPUnit_Framework_MockObject_MockObject
*/
- private $remoteFilesystem;
+ private $httpDownloader;
public function testComposerShouldSetIncludePath()
{
@@ -133,7 +133,7 @@ class PearRepositoryTest extends TestCase
$config = new \Composer\Config();
- $this->remoteFilesystem = $this->getMockBuilder('Composer\Util\RemoteFilesystem')
+ $this->httpDownloader = $this->getMockBuilder('Composer\Util\HttpDownloader')
->disableOriginalConstructor()
->getMock();
@@ -143,6 +143,6 @@ class PearRepositoryTest extends TestCase
protected function tearDown()
{
$this->repository = null;
- $this->remoteFilesystem = null;
+ $this->httpDownloader = null;
}
}
diff --git a/tests/Composer/Test/Repository/RepositoryFactoryTest.php b/tests/Composer/Test/Repository/RepositoryFactoryTest.php
index e54624415..e0a854d46 100644
--- a/tests/Composer/Test/Repository/RepositoryFactoryTest.php
+++ b/tests/Composer/Test/Repository/RepositoryFactoryTest.php
@@ -21,7 +21,9 @@ class RepositoryFactoryTest extends TestCase
{
$manager = RepositoryFactory::manager(
$this->getMockBuilder('Composer\IO\IOInterface')->getMock(),
- $this->getMockBuilder('Composer\Config')->getMock()
+ $this->getMockBuilder('Composer\Config')->getMock(),
+ $this->getMockBuilder('Composer\Util\HttpDownloader')->disableOriginalConstructor()->getMock(),
+ $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')->disableOriginalConstructor()->getMock()
);
$ref = new \ReflectionProperty($manager, 'repositoryClasses');
diff --git a/tests/Composer/Test/Repository/RepositoryManagerTest.php b/tests/Composer/Test/Repository/RepositoryManagerTest.php
index 3774dd268..35afd91e2 100644
--- a/tests/Composer/Test/Repository/RepositoryManagerTest.php
+++ b/tests/Composer/Test/Repository/RepositoryManagerTest.php
@@ -38,6 +38,7 @@ class RepositoryManagerTest extends TestCase
$rm = new RepositoryManager(
$this->getMockBuilder('Composer\IO\IOInterface')->getMock(),
$this->getMockBuilder('Composer\Config')->getMock(),
+ $this->getMockBuilder('Composer\Util\HttpDownloader')->disableOriginalConstructor()->getMock(),
$this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')->disableOriginalConstructor()->getMock()
);
@@ -61,6 +62,7 @@ class RepositoryManagerTest extends TestCase
$rm = new RepositoryManager(
$this->getMockBuilder('Composer\IO\IOInterface')->getMock(),
$config = $this->getMockBuilder('Composer\Config')->setMethods(array('get'))->getMock(),
+ $this->getMockBuilder('Composer\Util\HttpDownloader')->disableOriginalConstructor()->getMock(),
$this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')->disableOriginalConstructor()->getMock()
);
diff --git a/tests/Composer/Test/Repository/Vcs/GitBitbucketDriverTest.php b/tests/Composer/Test/Repository/Vcs/GitBitbucketDriverTest.php
index 8d711e8f0..f0139970b 100644
--- a/tests/Composer/Test/Repository/Vcs/GitBitbucketDriverTest.php
+++ b/tests/Composer/Test/Repository/Vcs/GitBitbucketDriverTest.php
@@ -16,6 +16,8 @@ use Composer\Config;
use Composer\Repository\Vcs\GitBitbucketDriver;
use Composer\Test\TestCase;
use Composer\Util\Filesystem;
+use Composer\Util\ProcessExecutor;
+use Composer\Util\Http\Response;
/**
* @group bitbucket
@@ -26,8 +28,8 @@ class GitBitbucketDriverTest extends TestCase
private $io;
/** @type \Composer\Config */
private $config;
- /** @type \Composer\Util\RemoteFilesystem|\PHPUnit_Framework_MockObject_MockObject */
- private $rfs;
+ /** @type \Composer\Util\HttpDownloader|\PHPUnit_Framework_MockObject_MockObject */
+ private $httpDownloader;
/** @type string */
private $home;
/** @type string */
@@ -46,7 +48,7 @@ class GitBitbucketDriverTest extends TestCase
),
));
- $this->rfs = $this->getMockBuilder('Composer\Util\RemoteFilesystem')
+ $this->httpDownloader = $this->getMockBuilder('Composer\Util\HttpDownloader')
->disableOriginalConstructor()
->getMock();
}
@@ -67,8 +69,8 @@ class GitBitbucketDriverTest extends TestCase
$repoConfig,
$this->io,
$this->config,
- null,
- $this->rfs
+ $this->httpDownloader,
+ new ProcessExecutor($this->io)
);
$driver->initialize();
@@ -83,15 +85,14 @@ class GitBitbucketDriverTest extends TestCase
'https://bitbucket.org/user/repo.git does not appear to be a git repository, use https://bitbucket.org/user/repo if this is a mercurial bitbucket repository'
);
- $this->rfs->expects($this->once())
- ->method('getContents')
+ $this->httpDownloader->expects($this->once())
+ ->method('get')
->with(
- $this->originUrl,
- 'https://api.bitbucket.org/2.0/repositories/user/repo?fields=-project%2C-owner',
- false
+ $url = 'https://api.bitbucket.org/2.0/repositories/user/repo?fields=-project%2C-owner',
+ array()
)
->willReturn(
- '{"scm":"hg","website":"","has_wiki":false,"name":"repo","links":{"branches":{"href":"https:\/\/api.bitbucket.org\/2.0\/repositories\/user\/repo\/refs\/branches"},"tags":{"href":"https:\/\/api.bitbucket.org\/2.0\/repositories\/user\/repo\/refs\/tags"},"clone":[{"href":"https:\/\/user@bitbucket.org\/user\/repo","name":"https"},{"href":"ssh:\/\/hg@bitbucket.org\/user\/repo","name":"ssh"}],"html":{"href":"https:\/\/bitbucket.org\/user\/repo"}},"language":"php","created_on":"2015-02-18T16:22:24.688+00:00","updated_on":"2016-05-17T13:20:21.993+00:00","is_private":true,"has_issues":false}'
+ new Response(array('url' => $url), 200, array(), '{"scm":"hg","website":"","has_wiki":false,"name":"repo","links":{"branches":{"href":"https:\/\/api.bitbucket.org\/2.0\/repositories\/user\/repo\/refs\/branches"},"tags":{"href":"https:\/\/api.bitbucket.org\/2.0\/repositories\/user\/repo\/refs\/tags"},"clone":[{"href":"https:\/\/user@bitbucket.org\/user\/repo","name":"https"},{"href":"ssh:\/\/hg@bitbucket.org\/user\/repo","name":"ssh"}],"html":{"href":"https:\/\/bitbucket.org\/user\/repo"}},"language":"php","created_on":"2015-02-18T16:22:24.688+00:00","updated_on":"2016-05-17T13:20:21.993+00:00","is_private":true,"has_issues":false}')
);
$driver = $this->getDriver(array('url' => 'https://bitbucket.org/user/repo.git'));
@@ -103,47 +104,43 @@ class GitBitbucketDriverTest extends TestCase
{
$driver = $this->getDriver(array('url' => 'https://bitbucket.org/user/repo.git'));
- $this->rfs->expects($this->any())
- ->method('getContents')
+ $urls = array(
+ 'https://api.bitbucket.org/2.0/repositories/user/repo?fields=-project%2C-owner',
+ 'https://api.bitbucket.org/2.0/repositories/user/repo?fields=mainbranch',
+ 'https://api.bitbucket.org/2.0/repositories/user/repo/refs/tags?pagelen=100&fields=values.name%2Cvalues.target.hash%2Cnext&sort=-target.date',
+ 'https://api.bitbucket.org/2.0/repositories/user/repo/refs/branches?pagelen=100&fields=values.name%2Cvalues.target.hash%2Cvalues.heads%2Cnext&sort=-target.date',
+ 'https://api.bitbucket.org/2.0/repositories/user/repo/src/master/composer.json',
+ 'https://api.bitbucket.org/2.0/repositories/user/repo/commit/master?fields=date',
+ );
+ $this->httpDownloader->expects($this->any())
+ ->method('get')
->withConsecutive(
array(
- $this->originUrl,
- 'https://api.bitbucket.org/2.0/repositories/user/repo?fields=-project%2C-owner',
- false,
+ $urls[0], array()
),
array(
- $this->originUrl,
- 'https://api.bitbucket.org/2.0/repositories/user/repo?fields=mainbranch',
- false,
+ $urls[1], array()
),
array(
- $this->originUrl,
- 'https://api.bitbucket.org/2.0/repositories/user/repo/refs/tags?pagelen=100&fields=values.name%2Cvalues.target.hash%2Cnext&sort=-target.date',
- false,
+ $urls[2], array()
),
array(
- $this->originUrl,
- 'https://api.bitbucket.org/2.0/repositories/user/repo/refs/branches?pagelen=100&fields=values.name%2Cvalues.target.hash%2Cvalues.heads%2Cnext&sort=-target.date',
- false,
+ $urls[3], array()
),
array(
- $this->originUrl,
- 'https://api.bitbucket.org/2.0/repositories/user/repo/src/master/composer.json',
- false,
+ $urls[4], array()
),
array(
- $this->originUrl,
- 'https://api.bitbucket.org/2.0/repositories/user/repo/commit/master?fields=date',
- false,
+ $urls[5], array()
)
)
->willReturnOnConsecutiveCalls(
- '{"scm":"git","website":"","has_wiki":false,"name":"repo","links":{"branches":{"href":"https:\/\/api.bitbucket.org\/2.0\/repositories\/user\/repo\/refs\/branches"},"tags":{"href":"https:\/\/api.bitbucket.org\/2.0\/repositories\/user\/repo\/refs\/tags"},"clone":[{"href":"https:\/\/user@bitbucket.org\/user\/repo.git","name":"https"},{"href":"ssh:\/\/git@bitbucket.org\/user\/repo.git","name":"ssh"}],"html":{"href":"https:\/\/bitbucket.org\/user\/repo"}},"language":"php","created_on":"2015-02-18T16:22:24.688+00:00","updated_on":"2016-05-17T13:20:21.993+00:00","is_private":true,"has_issues":false}',
- '{"mainbranch": {"name": "master"}}',
- '{"values":[{"name":"1.0.1","target":{"hash":"9b78a3932143497c519e49b8241083838c8ff8a1"}},{"name":"1.0.0","target":{"hash":"d3393d514318a9267d2f8ebbf463a9aaa389f8eb"}}]}',
- '{"values":[{"name":"master","target":{"hash":"937992d19d72b5116c3e8c4a04f960e5fa270b22"}}]}',
- '{"name": "user/repo","description": "test repo","license": "GPL","authors": [{"name": "Name","email": "local@domain.tld"}],"require": {"creator/package": "^1.0"},"require-dev": {"phpunit/phpunit": "~4.8"}}',
- '{"date": "2016-05-17T13:19:52+00:00"}'
+ new Response(array('url' => $urls[0]), 200, array(), '{"scm":"git","website":"","has_wiki":false,"name":"repo","links":{"branches":{"href":"https:\/\/api.bitbucket.org\/2.0\/repositories\/user\/repo\/refs\/branches"},"tags":{"href":"https:\/\/api.bitbucket.org\/2.0\/repositories\/user\/repo\/refs\/tags"},"clone":[{"href":"https:\/\/user@bitbucket.org\/user\/repo.git","name":"https"},{"href":"ssh:\/\/git@bitbucket.org\/user\/repo.git","name":"ssh"}],"html":{"href":"https:\/\/bitbucket.org\/user\/repo"}},"language":"php","created_on":"2015-02-18T16:22:24.688+00:00","updated_on":"2016-05-17T13:20:21.993+00:00","is_private":true,"has_issues":false}'),
+ new Response(array('url' => $urls[1]), 200, array(), '{"mainbranch": {"name": "master"}}'),
+ new Response(array('url' => $urls[2]), 200, array(), '{"values":[{"name":"1.0.1","target":{"hash":"9b78a3932143497c519e49b8241083838c8ff8a1"}},{"name":"1.0.0","target":{"hash":"d3393d514318a9267d2f8ebbf463a9aaa389f8eb"}}]}'),
+ new Response(array('url' => $urls[3]), 200, array(), '{"values":[{"name":"master","target":{"hash":"937992d19d72b5116c3e8c4a04f960e5fa270b22"}}]}'),
+ new Response(array('url' => $urls[4]), 200, array(), '{"name": "user/repo","description": "test repo","license": "GPL","authors": [{"name": "Name","email": "local@domain.tld"}],"require": {"creator/package": "^1.0"},"require-dev": {"phpunit/phpunit": "~4.8"}}'),
+ new Response(array('url' => $urls[5]), 200, array(), '{"date": "2016-05-17T13:19:52+00:00"}')
);
$this->assertEquals(
diff --git a/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php b/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php
index ba9c6d4f7..977a4a7aa 100644
--- a/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php
+++ b/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php
@@ -16,6 +16,7 @@ use Composer\Downloader\TransportException;
use Composer\Repository\Vcs\GitHubDriver;
use Composer\Test\TestCase;
use Composer\Util\Filesystem;
+use Composer\Util\Http\Response;
use Composer\Config;
class GitHubDriverTest extends TestCase
@@ -53,8 +54,8 @@ class GitHubDriverTest extends TestCase
->method('isInteractive')
->will($this->returnValue(true));
- $remoteFilesystem = $this->getMockBuilder('Composer\Util\RemoteFilesystem')
- ->setConstructorArgs(array($io))
+ $httpDownloader = $this->getMockBuilder('Composer\Util\HttpDownloader')
+ ->setConstructorArgs(array($io, $this->config))
->getMock();
$process = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
@@ -62,9 +63,9 @@ class GitHubDriverTest extends TestCase
->method('execute')
->will($this->returnValue(1));
- $remoteFilesystem->expects($this->at(0))
- ->method('getContents')
- ->with($this->equalTo('github.com'), $this->equalTo($repoApiUrl), $this->equalTo(false))
+ $httpDownloader->expects($this->at(0))
+ ->method('get')
+ ->with($this->equalTo($repoApiUrl))
->will($this->throwException(new TransportException('HTTP/1.1 404 Not Found', 404)));
$io->expects($this->once())
@@ -76,15 +77,15 @@ class GitHubDriverTest extends TestCase
->method('setAuthentication')
->with($this->equalTo('github.com'), $this->matchesRegularExpression('{sometoken}'), $this->matchesRegularExpression('{x-oauth-basic}'));
- $remoteFilesystem->expects($this->at(1))
- ->method('getContents')
- ->with($this->equalTo('github.com'), $this->equalTo('https://api.github.com/'), $this->equalTo(false))
- ->will($this->returnValue('{}'));
+ $httpDownloader->expects($this->at(1))
+ ->method('get')
+ ->with($this->equalTo($url = 'https://api.github.com/'))
+ ->will($this->returnValue(new Response(array('url' => $url), 200, array(), '{}')));
- $remoteFilesystem->expects($this->at(2))
- ->method('getContents')
- ->with($this->equalTo('github.com'), $this->equalTo($repoApiUrl), $this->equalTo(false))
- ->will($this->returnValue('{"master_branch": "test_master", "private": true, "owner": {"login": "composer"}, "name": "packagist"}'));
+ $httpDownloader->expects($this->at(2))
+ ->method('get')
+ ->with($this->equalTo($url = $repoApiUrl))
+ ->will($this->returnValue(new Response(array('url' => $url), 200, array(), '{"master_branch": "test_master", "private": true, "owner": {"login": "composer"}, "name": "packagist"}')));
$configSource = $this->getMockBuilder('Composer\Config\ConfigSourceInterface')->getMock();
$authConfigSource = $this->getMockBuilder('Composer\Config\ConfigSourceInterface')->getMock();
@@ -95,7 +96,7 @@ class GitHubDriverTest extends TestCase
'url' => $repoUrl,
);
- $gitHubDriver = new GitHubDriver($repoConfig, $io, $this->config, $process, $remoteFilesystem);
+ $gitHubDriver = new GitHubDriver($repoConfig, $io, $this->config, $httpDownloader, $process);
$gitHubDriver->initialize();
$this->setAttribute($gitHubDriver, 'tags', array($identifier => $sha));
@@ -124,21 +125,25 @@ class GitHubDriverTest extends TestCase
->method('isInteractive')
->will($this->returnValue(true));
- $remoteFilesystem = $this->getMockBuilder('Composer\Util\RemoteFilesystem')
- ->setConstructorArgs(array($io))
+ $httpDownloader = $this->getMockBuilder('Composer\Util\HttpDownloader')
+ ->setConstructorArgs(array($io, $this->config))
->getMock();
- $remoteFilesystem->expects($this->at(0))
- ->method('getContents')
- ->with($this->equalTo('github.com'), $this->equalTo($repoApiUrl), $this->equalTo(false))
- ->will($this->returnValue('{"master_branch": "test_master", "owner": {"login": "composer"}, "name": "packagist"}'));
+ $httpDownloader->expects($this->at(0))
+ ->method('get')
+ ->with($this->equalTo($repoApiUrl))
+ ->will($this->returnValue(new Response(array('url' => $repoApiUrl), 200, array(), '{"master_branch": "test_master", "owner": {"login": "composer"}, "name": "packagist"}')));
$repoConfig = array(
'url' => $repoUrl,
);
$repoUrl = 'https://github.com/composer/packagist.git';
- $gitHubDriver = new GitHubDriver($repoConfig, $io, $this->config, null, $remoteFilesystem);
+ $process = $this->getMockBuilder('Composer\Util\ProcessExecutor')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $gitHubDriver = new GitHubDriver($repoConfig, $io, $this->config, $httpDownloader, $process);
$gitHubDriver->initialize();
$this->setAttribute($gitHubDriver, 'tags', array($identifier => $sha));
@@ -167,31 +172,35 @@ class GitHubDriverTest extends TestCase
->method('isInteractive')
->will($this->returnValue(true));
- $remoteFilesystem = $this->getMockBuilder('Composer\Util\RemoteFilesystem')
- ->setConstructorArgs(array($io))
+ $httpDownloader = $this->getMockBuilder('Composer\Util\HttpDownloader')
+ ->setConstructorArgs(array($io, $this->config))
->getMock();
- $remoteFilesystem->expects($this->at(0))
- ->method('getContents')
- ->with($this->equalTo('github.com'), $this->equalTo($repoApiUrl), $this->equalTo(false))
- ->will($this->returnValue('{"master_branch": "test_master", "owner": {"login": "composer"}, "name": "packagist"}'));
+ $httpDownloader->expects($this->at(0))
+ ->method('get')
+ ->with($this->equalTo($url = $repoApiUrl))
+ ->will($this->returnValue(new Response(array('url' => $url), 200, array(), '{"master_branch": "test_master", "owner": {"login": "composer"}, "name": "packagist"}')));
- $remoteFilesystem->expects($this->at(1))
- ->method('getContents')
- ->with($this->equalTo('github.com'), $this->equalTo('https://api.github.com/repos/composer/packagist/contents/composer.json?ref=feature%2F3.2-foo'), $this->equalTo(false))
- ->will($this->returnValue('{"encoding":"base64","content":"'.base64_encode('{"support": {"source": "'.$repoUrl.'" }}').'"}'));
+ $httpDownloader->expects($this->at(1))
+ ->method('get')
+ ->with($this->equalTo($url = 'https://api.github.com/repos/composer/packagist/contents/composer.json?ref=feature%2F3.2-foo'))
+ ->will($this->returnValue(new Response(array('url' => $url), 200, array(), '{"encoding":"base64","content":"'.base64_encode('{"support": {"source": "'.$repoUrl.'" }}').'"}')));
- $remoteFilesystem->expects($this->at(2))
- ->method('getContents')
- ->with($this->equalTo('github.com'), $this->equalTo('https://api.github.com/repos/composer/packagist/commits/feature%2F3.2-foo'), $this->equalTo(false))
- ->will($this->returnValue('{"commit": {"committer":{ "date": "2012-09-10"}}}'));
+ $httpDownloader->expects($this->at(2))
+ ->method('get')
+ ->with($this->equalTo($url = 'https://api.github.com/repos/composer/packagist/commits/feature%2F3.2-foo'))
+ ->will($this->returnValue(new Response(array('url' => $url), 200, array(), '{"commit": {"committer":{ "date": "2012-09-10"}}}')));
$repoConfig = array(
'url' => $repoUrl,
);
$repoUrl = 'https://github.com/composer/packagist.git';
- $gitHubDriver = new GitHubDriver($repoConfig, $io, $this->config, null, $remoteFilesystem);
+ $process = $this->getMockBuilder('Composer\Util\ProcessExecutor')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $gitHubDriver = new GitHubDriver($repoConfig, $io, $this->config,$httpDownloader, $process);
$gitHubDriver->initialize();
$this->setAttribute($gitHubDriver, 'tags', array($identifier => $sha));
@@ -227,13 +236,13 @@ class GitHubDriverTest extends TestCase
->method('isInteractive')
->will($this->returnValue(false));
- $remoteFilesystem = $this->getMockBuilder('Composer\Util\RemoteFilesystem')
- ->setConstructorArgs(array($io))
+ $httpDownloader = $this->getMockBuilder('Composer\Util\HttpDownloader')
+ ->setConstructorArgs(array($io, $this->config))
->getMock();
- $remoteFilesystem->expects($this->at(0))
- ->method('getContents')
- ->with($this->equalTo('github.com'), $this->equalTo($repoApiUrl), $this->equalTo(false))
+ $httpDownloader->expects($this->at(0))
+ ->method('get')
+ ->with($this->equalTo($repoApiUrl))
->will($this->throwException(new TransportException('HTTP/1.1 404 Not Found', 404)));
// clean local clone if present
@@ -278,7 +287,7 @@ class GitHubDriverTest extends TestCase
'url' => $repoUrl,
);
- $gitHubDriver = new GitHubDriver($repoConfig, $io, $this->config, $process, $remoteFilesystem);
+ $gitHubDriver = new GitHubDriver($repoConfig, $io, $this->config, $httpDownloader, $process);
$gitHubDriver->initialize();
$this->assertEquals('test_master', $gitHubDriver->getRootIdentifier());
diff --git a/tests/Composer/Test/Repository/Vcs/GitLabDriverTest.php b/tests/Composer/Test/Repository/Vcs/GitLabDriverTest.php
index a5eb799f2..0fd2fa956 100644
--- a/tests/Composer/Test/Repository/Vcs/GitLabDriverTest.php
+++ b/tests/Composer/Test/Repository/Vcs/GitLabDriverTest.php
@@ -17,6 +17,7 @@ use Composer\Config;
use Composer\Test\TestCase;
use Composer\Util\Filesystem;
use Prophecy\Argument;
+use Composer\Util\Http\Response;
/**
* @author Jérôme Tamarelle
@@ -27,7 +28,7 @@ class GitLabDriverTest extends TestCase
private $config;
private $io;
private $process;
- private $remoteFilesystem;
+ private $httpDownloader;
public function setUp()
{
@@ -47,7 +48,7 @@ class GitLabDriverTest extends TestCase
$this->io = $this->prophesize('Composer\IO\IOInterface');
$this->process = $this->prophesize('Composer\Util\ProcessExecutor');
- $this->remoteFilesystem = $this->prophesize('Composer\Util\RemoteFilesystem');
+ $this->httpDownloader = $this->prophesize('Composer\Util\HttpDownloader');
}
public function tearDown()
@@ -87,13 +88,11 @@ class GitLabDriverTest extends TestCase
}
JSON;
- $this->remoteFilesystem
- ->getContents('gitlab.com', $apiUrl, false, array())
- ->willReturn($projectData)
+ $this->mockResponse($apiUrl, array(), $projectData)
->shouldBeCalledTimes(1)
;
- $driver = new GitLabDriver(array('url' => $url), $this->io->reveal(), $this->config, $this->process->reveal(), $this->remoteFilesystem->reveal());
+ $driver = new GitLabDriver(array('url' => $url), $this->io->reveal(), $this->config, $this->httpDownloader->reveal(), $this->process->reveal());
$driver->initialize();
$this->assertEquals($apiUrl, $driver->getApiUrl(), 'API URL is derived from the repository URL');
@@ -126,13 +125,11 @@ JSON;
}
JSON;
- $this->remoteFilesystem
- ->getContents('gitlab.com', $apiUrl, false, array())
- ->willReturn($projectData)
+ $this->mockResponse($apiUrl, array(), $projectData)
->shouldBeCalledTimes(1)
;
- $driver = new GitLabDriver(array('url' => $url), $this->io->reveal(), $this->config, $this->process->reveal(), $this->remoteFilesystem->reveal());
+ $driver = new GitLabDriver(array('url' => $url), $this->io->reveal(), $this->config, $this->httpDownloader->reveal(), $this->process->reveal());
$driver->initialize();
$this->assertEquals($apiUrl, $driver->getApiUrl(), 'API URL is derived from the repository URL');
@@ -164,13 +161,11 @@ JSON;
}
JSON;
- $this->remoteFilesystem
- ->getContents('gitlab.com', $apiUrl, false, array())
- ->willReturn($projectData)
+ $this->mockResponse($apiUrl, array(), $projectData)
->shouldBeCalledTimes(1)
;
- $driver = new GitLabDriver(array('url' => $url), $this->io->reveal(), $this->config, $this->process->reveal(), $this->remoteFilesystem->reveal());
+ $driver = new GitLabDriver(array('url' => $url), $this->io->reveal(), $this->config, $this->httpDownloader->reveal(), $this->process->reveal());
$driver->initialize();
$this->assertEquals($apiUrl, $driver->getApiUrl(), 'API URL is derived from the repository URL');
@@ -206,12 +201,10 @@ JSON;
}
JSON;
- $this->remoteFilesystem
- ->getContents($domain, $apiUrl, false, array())
- ->willReturn(sprintf($projectData, $domain, $port, $namespace))
+ $this->mockResponse($apiUrl, array(), sprintf($projectData, $domain, $port, $namespace))
->shouldBeCalledTimes(1);
- $driver = new GitLabDriver(array('url' => $url), $this->io->reveal(), $this->config, $this->process->reveal(), $this->remoteFilesystem->reveal());
+ $driver = new GitLabDriver(array('url' => $url), $this->io->reveal(), $this->config, $this->httpDownloader->reveal(), $this->process->reveal());
$driver->initialize();
$this->assertEquals($apiUrl, $driver->getApiUrl(), 'API URL is derived from the repository URL');
@@ -289,15 +282,11 @@ JSON;
]
JSON;
- $this->remoteFilesystem
- ->getContents('gitlab.com', $apiUrl, false, array())
- ->willReturn($tagData)
+ $this->mockResponse($apiUrl, array(), $tagData)
->shouldBeCalledTimes(1)
;
- $this->remoteFilesystem->getLastHeaders()
- ->willReturn(array());
- $driver->setRemoteFilesystem($this->remoteFilesystem->reveal());
+ $driver->setHttpDownloader($this->httpDownloader->reveal());
$expected = array(
'v1.0.0' => '092ed2c762bbae331e3f51d4a17f67310bf99a81',
@@ -344,26 +333,20 @@ JSON;
$branchData = json_encode($branchData);
- $this->remoteFilesystem
- ->getContents('gitlab.com', $apiUrl, false, array())
- ->willReturn($branchData)
- ->shouldBeCalledTimes(1)
- ;
+ $headers = array('Link: ; rel="next", ; rel="first", ; rel="last"');
+ $this->httpDownloader
+ ->get($apiUrl, array())
+ ->willReturn(new Response(array('url' => $apiUrl), 200, $headers, $branchData))
+ ->shouldBeCalledTimes(1);
- $this->remoteFilesystem
- ->getContents('gitlab.com', "http://gitlab.com/api/v4/projects/mygroup%2Fmyproject/repository/tags?id=mygroup%2Fmyproject&page=2&per_page=20", false, array())
- ->willReturn($branchData)
- ->shouldBeCalledTimes(1)
- ;
+ $apiUrl = "http://gitlab.com/api/v4/projects/mygroup%2Fmyproject/repository/tags?id=mygroup%2Fmyproject&page=2&per_page=20";
+ $headers = array('Link: ; rel="prev", ; rel="first", ; rel="last"');
+ $this->httpDownloader
+ ->get($apiUrl, array())
+ ->willReturn(new Response(array('url' => $apiUrl), 200, $headers, $branchData))
+ ->shouldBeCalledTimes(1);
- $this->remoteFilesystem->getLastHeaders()
- ->willReturn(
- array('Link: ; rel="next", ; rel="first", ; rel="last"'),
- array('Link: ; rel="prev", ; rel="first", ; rel="last"')
- )
- ->shouldBeCalledTimes(2);
-
- $driver->setRemoteFilesystem($this->remoteFilesystem->reveal());
+ $driver->setHttpDownloader($this->httpDownloader->reveal());
$expected = array(
'mymaster' => '97eda36b5c1dd953a3792865c222d4e85e5f302e',
@@ -401,15 +384,11 @@ JSON;
]
JSON;
- $this->remoteFilesystem
- ->getContents('gitlab.com', $apiUrl, false, array())
- ->willReturn($branchData)
+ $this->mockResponse($apiUrl, array(), $branchData)
->shouldBeCalledTimes(1)
;
- $this->remoteFilesystem->getLastHeaders()
- ->willReturn(array());
- $driver->setRemoteFilesystem($this->remoteFilesystem->reveal());
+ $driver->setHttpDownloader($this->httpDownloader->reveal());
$expected = array(
'mymaster' => '97eda36b5c1dd953a3792865c222d4e85e5f302e',
@@ -474,13 +453,11 @@ JSON;
}
JSON;
- $this->remoteFilesystem
- ->getContents('mycompany.com/gitlab', $apiUrl, false, array())
- ->willReturn($projectData)
+ $this->mockResponse($apiUrl, array(), $projectData)
->shouldBeCalledTimes(1)
;
- $driver = new GitLabDriver(array('url' => $url), $this->io->reveal(), $this->config, $this->process->reveal(), $this->remoteFilesystem->reveal());
+ $driver = new GitLabDriver(array('url' => $url), $this->io->reveal(), $this->config, $this->httpDownloader->reveal(), $this->process->reveal());
$driver->initialize();
$this->assertEquals($apiUrl, $driver->getApiUrl(), 'API URL is derived from the repository URL');
@@ -507,13 +484,11 @@ JSON;
}
JSON;
- $this->remoteFilesystem
- ->getContents('gitlab.com', $apiUrl, false, array())
- ->willReturn($projectData)
+ $this->mockResponse($apiUrl, array(), $projectData)
->shouldBeCalledTimes(1)
;
- $driver = new GitLabDriver(array('url' => $url), $this->io->reveal(), $this->config, $this->process->reveal(), $this->remoteFilesystem->reveal());
+ $driver = new GitLabDriver(array('url' => $url), $this->io->reveal(), $this->config, $this->httpDownloader->reveal(), $this->process->reveal());
$driver->initialize();
$this->assertEquals($apiUrl, $driver->getApiUrl(), 'API URL is derived from the repository URL');
@@ -540,13 +515,11 @@ JSON;
}
JSON;
- $this->remoteFilesystem
- ->getContents('mycompany.com/gitlab', $apiUrl, false, array())
- ->willReturn($projectData)
+ $this->mockResponse($apiUrl, array(), $projectData)
->shouldBeCalledTimes(1)
;
- $driver = new GitLabDriver(array('url' => $url), $this->io->reveal(), $this->config, $this->process->reveal(), $this->remoteFilesystem->reveal());
+ $driver = new GitLabDriver(array('url' => $url), $this->io->reveal(), $this->config, $this->httpDownloader->reveal(), $this->process->reveal());
$driver->initialize();
$this->assertEquals($apiUrl, $driver->getApiUrl(), 'API URL is derived from the repository URL');
@@ -575,18 +548,23 @@ JSON;
}
JSON;
- $this->remoteFilesystem
- ->getContents(Argument::cetera(), $options)
- ->willReturn($projectData)
+ $this->mockResponse(Argument::cetera(), $options, $projectData)
->shouldBeCalled();
$driver = new GitLabDriver(
array('url' => 'https://gitlab.mycompany.local/mygroup/myproject', 'options' => $options),
$this->io->reveal(),
$this->config,
- $this->process->reveal(),
- $this->remoteFilesystem->reveal()
+ $this->httpDownloader->reveal(),
+ $this->process->reveal()
);
$driver->initialize();
}
+
+ private function mockResponse($url, $options, $return)
+ {
+ return $this->httpDownloader
+ ->get($url, $options)
+ ->willReturn(new Response(array('url' => $url), 200, array(), $return));
+ }
}
diff --git a/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php b/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php
index a5e5d4b4c..ff4e19121 100644
--- a/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php
+++ b/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php
@@ -26,7 +26,7 @@ class PerforceDriverTest extends TestCase
protected $config;
protected $io;
protected $process;
- protected $remoteFileSystem;
+ protected $httpDownloader;
protected $testPath;
protected $driver;
protected $repoConfig;
@@ -43,9 +43,9 @@ class PerforceDriverTest extends TestCase
$this->repoConfig = $this->getTestRepoConfig();
$this->io = $this->getMockIOInterface();
$this->process = $this->getMockProcessExecutor();
- $this->remoteFileSystem = $this->getMockRemoteFilesystem();
+ $this->httpDownloader = $this->getMockHttpDownloader();
$this->perforce = $this->getMockPerforce();
- $this->driver = new PerforceDriver($this->repoConfig, $this->io, $this->config, $this->process, $this->remoteFileSystem);
+ $this->driver = new PerforceDriver($this->repoConfig, $this->io, $this->config, $this->httpDownloader, $this->process);
$this->overrideDriverInternalPerforce($this->perforce);
}
@@ -56,7 +56,7 @@ class PerforceDriverTest extends TestCase
$fs->removeDirectory($this->testPath);
$this->driver = null;
$this->perforce = null;
- $this->remoteFileSystem = null;
+ $this->httpDownloader = null;
$this->process = null;
$this->io = null;
$this->repoConfig = null;
@@ -99,9 +99,9 @@ class PerforceDriverTest extends TestCase
return $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
}
- protected function getMockRemoteFilesystem()
+ protected function getMockHttpDownloader()
{
- return $this->getMockBuilder('Composer\Util\RemoteFilesystem')->disableOriginalConstructor()->getMock();
+ return $this->getMockBuilder('Composer\Util\HttpDownloader')->disableOriginalConstructor()->getMock();
}
protected function getMockPerforce()
@@ -113,7 +113,7 @@ class PerforceDriverTest extends TestCase
public function testInitializeCapturesVariablesFromRepoConfig()
{
- $driver = new PerforceDriver($this->repoConfig, $this->io, $this->config, $this->process, $this->remoteFileSystem);
+ $driver = new PerforceDriver($this->repoConfig, $this->io, $this->config, $this->httpDownloader, $this->process);
$driver->initialize();
$this->assertEquals(self::TEST_URL, $driver->getUrl());
$this->assertEquals(self::TEST_DEPOT, $driver->getDepot());
diff --git a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php
index 029d20160..946c198f2 100644
--- a/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php
+++ b/tests/Composer/Test/Repository/Vcs/SvnDriverTest.php
@@ -46,6 +46,7 @@ class SvnDriverTest extends TestCase
public function testWrongCredentialsInUrl()
{
$console = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
+ $httpDownloader = $this->getMockBuilder('Composer\Util\HttpDownloader')->disableOriginalConstructor()->getMock();
$output = "svn: OPTIONS of 'https://corp.svn.local/repo':";
$output .= " authorization failed: Could not authenticate to server:";
@@ -66,7 +67,7 @@ class SvnDriverTest extends TestCase
'url' => 'https://till:secret@corp.svn.local/repo',
);
- $svn = new SvnDriver($repoConfig, $console, $this->config, $process);
+ $svn = new SvnDriver($repoConfig, $console, $this->config, $httpDownloader, $process);
$svn->initialize();
}
diff --git a/tests/Composer/Test/Util/BitbucketTest.php b/tests/Composer/Test/Util/BitbucketTest.php
index 4323a15f5..f50bdd818 100644
--- a/tests/Composer/Test/Util/BitbucketTest.php
+++ b/tests/Composer/Test/Util/BitbucketTest.php
@@ -13,6 +13,7 @@
namespace Composer\Test\Util;
use Composer\Util\Bitbucket;
+use Composer\Util\Http\Response;
use PHPUnit\Framework\TestCase;
/**
@@ -30,8 +31,8 @@ class BitbucketTest extends TestCase
/** @type \Composer\IO\ConsoleIO|\PHPUnit_Framework_MockObject_MockObject */
private $io;
- /** @type \Composer\Util\RemoteFilesystem|\PHPUnit_Framework_MockObject_MockObject */
- private $rfs;
+ /** @type \Composer\Util\HttpDownloader|\PHPUnit_Framework_MockObject_MockObject */
+ private $httpDownloader;
/** @type \Composer\Config|\PHPUnit_Framework_MockObject_MockObject */
private $config;
/** @type Bitbucket */
@@ -47,8 +48,8 @@ class BitbucketTest extends TestCase
->getMock()
;
- $this->rfs = $this
- ->getMockBuilder('Composer\Util\RemoteFilesystem')
+ $this->httpDownloader = $this
+ ->getMockBuilder('Composer\Util\HttpDownloader')
->disableOriginalConstructor()
->getMock()
;
@@ -57,7 +58,7 @@ class BitbucketTest extends TestCase
$this->time = time();
- $this->bitbucket = new Bitbucket($this->io, $this->config, null, $this->rfs, $this->time);
+ $this->bitbucket = new Bitbucket($this->io, $this->config, null, $this->httpDownloader, $this->time);
}
public function testRequestAccessTokenWithValidOAuthConsumer()
@@ -66,12 +67,10 @@ class BitbucketTest extends TestCase
->method('setAuthentication')
->with($this->origin, $this->consumer_key, $this->consumer_secret);
- $this->rfs->expects($this->once())
- ->method('getContents')
+ $this->httpDownloader->expects($this->once())
+ ->method('get')
->with(
- $this->origin,
Bitbucket::OAUTH2_ACCESS_TOKEN_URL,
- false,
array(
'retry-auth-failure' => false,
'http' => array(
@@ -81,9 +80,14 @@ class BitbucketTest extends TestCase
)
)
->willReturn(
- sprintf(
- '{"access_token": "%s", "scopes": "repository", "expires_in": 3600, "refresh_token": "refreshtoken", "token_type": "bearer"}',
- $this->token
+ new Response(
+ array('url' => Bitbucket::OAUTH2_ACCESS_TOKEN_URL),
+ 200,
+ array(),
+ sprintf(
+ '{"access_token": "%s", "scopes": "repository", "expires_in": 3600, "refresh_token": "refreshtoken", "token_type": "bearer"}',
+ $this->token
+ )
)
);
@@ -142,12 +146,10 @@ class BitbucketTest extends TestCase
->method('setAuthentication')
->with($this->origin, $this->consumer_key, $this->consumer_secret);
- $this->rfs->expects($this->once())
- ->method('getContents')
+ $this->httpDownloader->expects($this->once())
+ ->method('get')
->with(
- $this->origin,
Bitbucket::OAUTH2_ACCESS_TOKEN_URL,
- false,
array(
'retry-auth-failure' => false,
'http' => array(
@@ -157,9 +159,14 @@ class BitbucketTest extends TestCase
)
)
->willReturn(
- sprintf(
- '{"access_token": "%s", "scopes": "repository", "expires_in": 3600, "refresh_token": "refreshtoken", "token_type": "bearer"}',
- $this->token
+ new Response(
+ array('url' => Bitbucket::OAUTH2_ACCESS_TOKEN_URL),
+ 200,
+ array(),
+ sprintf(
+ '{"access_token": "%s", "scopes": "repository", "expires_in": 3600, "refresh_token": "refreshtoken", "token_type": "bearer"}',
+ $this->token
+ )
)
);
@@ -186,12 +193,10 @@ class BitbucketTest extends TestCase
array('2. You are using an OAuth consumer, but didn\'t configure a (dummy) callback url')
);
- $this->rfs->expects($this->once())
- ->method('getContents')
+ $this->httpDownloader->expects($this->once())
+ ->method('get')
->with(
- $this->origin,
Bitbucket::OAUTH2_ACCESS_TOKEN_URL,
- false,
array(
'retry-auth-failure' => false,
'http' => array(
@@ -234,21 +239,24 @@ class BitbucketTest extends TestCase
)
->willReturnOnConsecutiveCalls($this->consumer_key, $this->consumer_secret);
- $this->rfs
+ $this->httpDownloader
->expects($this->once())
- ->method('getContents')
+ ->method('get')
->with(
- $this->equalTo($this->origin),
- $this->equalTo(sprintf('https://%s/site/oauth2/access_token', $this->origin)),
- $this->isFalse(),
+ $this->equalTo($url = sprintf('https://%s/site/oauth2/access_token', $this->origin)),
$this->anything()
)
->willReturn(
- sprintf(
- '{"access_token": "%s", "scopes": "repository", "expires_in": 3600, "refresh_token": "refresh_token", "token_type": "bearer"}',
- $this->token
+ new Response(
+ array('url' => $url),
+ 200,
+ array(),
+ sprintf(
+ '{"access_token": "%s", "scopes": "repository", "expires_in": 3600, "refresh_token": "refresh_token", "token_type": "bearer"}',
+ $this->token
+ )
)
- )
+ );
;
$this->setExpectationsForStoringAccessToken(true);
diff --git a/tests/Composer/Test/Util/GitHubTest.php b/tests/Composer/Test/Util/GitHubTest.php
index 28d00ce69..1893486e0 100644
--- a/tests/Composer/Test/Util/GitHubTest.php
+++ b/tests/Composer/Test/Util/GitHubTest.php
@@ -14,6 +14,7 @@ namespace Composer\Test\Util;
use Composer\Downloader\TransportException;
use Composer\Util\GitHub;
+use Composer\Util\Http\Response;
use PHPUnit\Framework\TestCase;
use RecursiveArrayIterator;
use RecursiveIteratorIterator;
@@ -45,17 +46,15 @@ class GitHubTest extends TestCase
->willReturn($this->password)
;
- $rfs = $this->getRemoteFilesystemMock();
- $rfs
+ $httpDownloader = $this->getHttpDownloaderMock();
+ $httpDownloader
->expects($this->once())
- ->method('getContents')
+ ->method('get')
->with(
- $this->equalTo($this->origin),
- $this->equalTo(sprintf('https://api.%s/', $this->origin)),
- $this->isFalse(),
+ $this->equalTo($url = sprintf('https://api.%s/', $this->origin)),
$this->anything()
)
- ->willReturn('{}')
+ ->willReturn(new Response(array('url' => $url), 200, array(), '{}'));
;
$config = $this->getConfigMock();
@@ -70,7 +69,7 @@ class GitHubTest extends TestCase
->willReturn($this->getConfJsonMock())
;
- $github = new GitHub($io, $config, null, $rfs);
+ $github = new GitHub($io, $config, null, $httpDownloader);
$this->assertTrue($github->authorizeOAuthInteractively($this->origin, $this->message));
}
@@ -85,10 +84,10 @@ class GitHubTest extends TestCase
->willReturn($this->password)
;
- $rfs = $this->getRemoteFilesystemMock();
- $rfs
+ $httpDownloader = $this->getHttpDownloaderMock();
+ $httpDownloader
->expects($this->exactly(1))
- ->method('getContents')
+ ->method('get')
->will($this->throwException(new TransportException('', 401)))
;
@@ -99,7 +98,7 @@ class GitHubTest extends TestCase
->willReturn($this->getAuthJsonMock())
;
- $github = new GitHub($io, $config, null, $rfs);
+ $github = new GitHub($io, $config, null, $httpDownloader);
$this->assertFalse($github->authorizeOAuthInteractively($this->origin));
}
@@ -120,15 +119,15 @@ class GitHubTest extends TestCase
return $this->getMockBuilder('Composer\Config')->getMock();
}
- private function getRemoteFilesystemMock()
+ private function getHttpDownloaderMock()
{
- $rfs = $this
- ->getMockBuilder('Composer\Util\RemoteFilesystem')
+ $httpDownloader = $this
+ ->getMockBuilder('Composer\Util\HttpDownloader')
->disableOriginalConstructor()
->getMock()
;
- return $rfs;
+ return $httpDownloader;
}
private function getAuthJsonMock()
diff --git a/tests/Composer/Test/Util/GitLabTest.php b/tests/Composer/Test/Util/GitLabTest.php
index 27f46b4ad..611b25256 100644
--- a/tests/Composer/Test/Util/GitLabTest.php
+++ b/tests/Composer/Test/Util/GitLabTest.php
@@ -14,6 +14,7 @@ namespace Composer\Test\Util;
use Composer\Downloader\TransportException;
use Composer\Util\GitLab;
+use Composer\Util\Http\Response;
use PHPUnit\Framework\TestCase;
/**
@@ -49,17 +50,15 @@ class GitLabTest extends TestCase
->willReturn($this->password)
;
- $rfs = $this->getRemoteFilesystemMock();
- $rfs
+ $httpDownloader = $this->getHttpDownloaderMock();
+ $httpDownloader
->expects($this->once())
- ->method('getContents')
+ ->method('get')
->with(
- $this->equalTo($this->origin),
- $this->equalTo(sprintf('http://%s/oauth/token', $this->origin)),
- $this->isFalse(),
+ $this->equalTo($url = sprintf('http://%s/oauth/token', $this->origin)),
$this->anything()
)
- ->willReturn(sprintf('{"access_token": "%s", "token_type": "bearer", "expires_in": 7200}', $this->token))
+ ->willReturn(new Response(array('url' => $url), 200, array(), sprintf('{"access_token": "%s", "token_type": "bearer", "expires_in": 7200}', $this->token)));
;
$config = $this->getConfigMock();
@@ -69,7 +68,7 @@ class GitLabTest extends TestCase
->willReturn($this->getAuthJsonMock())
;
- $gitLab = new GitLab($io, $config, null, $rfs);
+ $gitLab = new GitLab($io, $config, null, $httpDownloader);
$this->assertTrue($gitLab->authorizeOAuthInteractively('http', $this->origin, $this->message));
}
@@ -94,10 +93,10 @@ class GitLabTest extends TestCase
->willReturn($this->password)
;
- $rfs = $this->getRemoteFilesystemMock();
- $rfs
+ $httpDownloader = $this->getHttpDownloaderMock();
+ $httpDownloader
->expects($this->exactly(5))
- ->method('getContents')
+ ->method('get')
->will($this->throwException(new TransportException('', 401)))
;
@@ -108,7 +107,7 @@ class GitLabTest extends TestCase
->willReturn($this->getAuthJsonMock())
;
- $gitLab = new GitLab($io, $config, null, $rfs);
+ $gitLab = new GitLab($io, $config, null, $httpDownloader);
$gitLab->authorizeOAuthInteractively('https', $this->origin);
}
@@ -129,15 +128,15 @@ class GitLabTest extends TestCase
return $this->getMockBuilder('Composer\Config')->getMock();
}
- private function getRemoteFilesystemMock()
+ private function getHttpDownloaderMock()
{
- $rfs = $this
- ->getMockBuilder('Composer\Util\RemoteFilesystem')
+ $httpDownloader = $this
+ ->getMockBuilder('Composer\Util\HttpDownloader')
->disableOriginalConstructor()
->getMock()
;
- return $rfs;
+ return $httpDownloader;
}
private function getAuthJsonMock()
diff --git a/tests/Composer/Test/Util/HttpDownloaderTest.php b/tests/Composer/Test/Util/HttpDownloaderTest.php
new file mode 100644
index 000000000..b65aa760a
--- /dev/null
+++ b/tests/Composer/Test/Util/HttpDownloaderTest.php
@@ -0,0 +1,51 @@
+
+ * Jordi Boggiano
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Test\Util;
+
+use Composer\Util\HttpDownloader;
+use PHPUnit\Framework\TestCase;
+
+class HttpDownloaderTest extends TestCase
+{
+ private function getConfigMock()
+ {
+ $config = $this->getMockBuilder('Composer\Config')->getMock();
+ $config->expects($this->any())
+ ->method('get')
+ ->will($this->returnCallback(function ($key) {
+ if ($key === 'github-domains' || $key === 'gitlab-domains') {
+ return array();
+ }
+ }));
+
+ return $config;
+ }
+
+ /**
+ * @group slow
+ */
+ public function testCaptureAuthenticationParamsFromUrl()
+ {
+ $io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
+ $io->expects($this->once())
+ ->method('setAuthentication')
+ ->with($this->equalTo('github.com'), $this->equalTo('user'), $this->equalTo('pass'));
+
+ $fs = new HttpDownloader($io, $this->getConfigMock());
+ try {
+ $fs->get('https://user:pass@github.com/composer/composer/404');
+ } catch (\Composer\Downloader\TransportException $e) {
+ $this->assertNotEquals(200, $e->getCode());
+ }
+ }
+}
diff --git a/tests/Composer/Test/Util/RemoteFilesystemTest.php b/tests/Composer/Test/Util/RemoteFilesystemTest.php
index 7da88bc8a..2c7f3112a 100644
--- a/tests/Composer/Test/Util/RemoteFilesystemTest.php
+++ b/tests/Composer/Test/Util/RemoteFilesystemTest.php
@@ -17,6 +17,20 @@ use PHPUnit\Framework\TestCase;
class RemoteFilesystemTest extends TestCase
{
+ private function getConfigMock()
+ {
+ $config = $this->getMockBuilder('Composer\Config')->getMock();
+ $config->expects($this->any())
+ ->method('get')
+ ->will($this->returnCallback(function ($key) {
+ if ($key === 'github-domains' || $key === 'gitlab-domains') {
+ return array();
+ }
+ }));
+
+ return $config;
+ }
+
public function testGetOptionsForUrl()
{
$io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
@@ -101,7 +115,7 @@ class RemoteFilesystemTest extends TestCase
public function testCallbackGetFileSize()
{
- $fs = new RemoteFilesystem($this->getMockBuilder('Composer\IO\IOInterface')->getMock());
+ $fs = new RemoteFilesystem($this->getMockBuilder('Composer\IO\IOInterface')->getMock(), $this->getConfigMock());
$this->callCallbackGet($fs, STREAM_NOTIFY_FILE_SIZE_IS, 0, '', 0, 0, 20);
$this->assertAttributeEquals(20, 'bytesMax', $fs);
}
@@ -114,7 +128,7 @@ class RemoteFilesystemTest extends TestCase
->method('overwriteError')
;
- $fs = new RemoteFilesystem($io);
+ $fs = new RemoteFilesystem($io, $this->getConfigMock());
$this->setAttribute($fs, 'bytesMax', 20);
$this->setAttribute($fs, 'progress', true);
@@ -124,40 +138,21 @@ class RemoteFilesystemTest extends TestCase
public function testCallbackGetPassesThrough404()
{
- $fs = new RemoteFilesystem($this->getMockBuilder('Composer\IO\IOInterface')->getMock());
+ $fs = new RemoteFilesystem($this->getMockBuilder('Composer\IO\IOInterface')->getMock(), $this->getConfigMock());
$this->assertNull($this->callCallbackGet($fs, STREAM_NOTIFY_FAILURE, 0, 'HTTP/1.1 404 Not Found', 404, 0, 0));
}
- /**
- * @group slow
- */
- public function testCaptureAuthenticationParamsFromUrl()
- {
- $io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
- $io->expects($this->once())
- ->method('setAuthentication')
- ->with($this->equalTo('github.com'), $this->equalTo('user'), $this->equalTo('pass'));
-
- $fs = new RemoteFilesystem($io);
- try {
- $fs->getContents('github.com', 'https://user:pass@github.com/composer/composer/404');
- } catch (\Exception $e) {
- $this->assertInstanceOf('Composer\Downloader\TransportException', $e);
- $this->assertNotEquals(200, $e->getCode());
- }
- }
-
public function testGetContents()
{
- $fs = new RemoteFilesystem($this->getMockBuilder('Composer\IO\IOInterface')->getMock());
+ $fs = new RemoteFilesystem($this->getMockBuilder('Composer\IO\IOInterface')->getMock(), $this->getConfigMock());
$this->assertContains('testGetContents', $fs->getContents('http://example.org', 'file://'.__FILE__));
}
public function testCopy()
{
- $fs = new RemoteFilesystem($this->getMockBuilder('Composer\IO\IOInterface')->getMock());
+ $fs = new RemoteFilesystem($this->getMockBuilder('Composer\IO\IOInterface')->getMock(), $this->getConfigMock());
$file = tempnam(sys_get_temp_dir(), 'c');
$this->assertTrue($fs->copy('http://example.org', 'file://'.__FILE__, $file));
@@ -218,7 +213,7 @@ class RemoteFilesystemTest extends TestCase
->disableOriginalConstructor()
->getMock();
- $rfs = new RemoteFilesystem($io);
+ $rfs = new RemoteFilesystem($io, $this->getConfigMock());
$hostname = parse_url($url, PHP_URL_HOST);
$result = $rfs->getContents($hostname, $url, false);
@@ -240,14 +235,6 @@ class RemoteFilesystemTest extends TestCase
->disableOriginalConstructor()
->getMock();
- $config = $this
- ->getMockBuilder('Composer\Config')
- ->getMock();
- $config
- ->method('get')
- ->withAnyParameters()
- ->willReturn(array());
-
$domains = array();
$io
->expects($this->any())
@@ -267,7 +254,7 @@ class RemoteFilesystemTest extends TestCase
'password' => '1A0yeK5Po3ZEeiiRiMWLivS0jirLdoGuaSGq9NvESFx1Fsdn493wUDXC8rz_1iKVRTl1GINHEUCsDxGh5lZ=',
));
- $rfs = new RemoteFilesystem($io, $config);
+ $rfs = new RemoteFilesystem($io, $this->getConfigMock());
$hostname = parse_url($url, PHP_URL_HOST);
$result = $rfs->getContents($hostname, $url, false);
@@ -278,7 +265,7 @@ class RemoteFilesystemTest extends TestCase
protected function callGetOptionsForUrl($io, array $args = array(), array $options = array(), $fileUrl = '')
{
- $fs = new RemoteFilesystem($io, null, $options);
+ $fs = new RemoteFilesystem($io, $this->getConfigMock(), $options);
$ref = new \ReflectionMethod($fs, 'getOptionsForUrl');
$prop = new \ReflectionProperty($fs, 'fileUrl');
$ref->setAccessible(true);
diff --git a/tests/Composer/Test/Util/StreamContextFactoryTest.php b/tests/Composer/Test/Util/StreamContextFactoryTest.php
index 9bb04aaa1..3d60a9a38 100644
--- a/tests/Composer/Test/Util/StreamContextFactoryTest.php
+++ b/tests/Composer/Test/Util/StreamContextFactoryTest.php
@@ -142,7 +142,6 @@ class StreamContextFactoryTest extends TestCase
$expected = array(
'http' => array(
'proxy' => 'tcp://proxyserver.net:80',
- 'request_fulluri' => true,
'method' => 'GET',
'header' => array('User-Agent: foo', "Proxy-Authorization: Basic " . base64_encode('username:password')),
'max_redirects' => 20,
@@ -173,7 +172,6 @@ class StreamContextFactoryTest extends TestCase
$expected = array(
'http' => array(
'proxy' => 'ssl://woopproxy.net:443',
- 'request_fulluri' => true,
'method' => 'GET',
'max_redirects' => 20,
'follow_location' => 1,