Avoid starting all jobs immediately
parent
64384f8b15
commit
5d2b3276eb
|
@ -95,12 +95,17 @@ class CurlDownloader
|
||||||
|
|
||||||
public function download($resolve, $reject, $origin, $url, $options, $copyTo = null)
|
public function download($resolve, $reject, $origin, $url, $options, $copyTo = null)
|
||||||
{
|
{
|
||||||
return $this->initDownload($resolve, $reject, $origin, $url, $options, $copyTo);
|
$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())
|
private function initDownload($resolve, $reject, $origin, $url, $options, $copyTo = null, array $attributes = array())
|
||||||
{
|
{
|
||||||
// TODO allow setting attributes somehow
|
|
||||||
$attributes = array_merge(array(
|
$attributes = array_merge(array(
|
||||||
'retryAuthFailure' => true,
|
'retryAuthFailure' => true,
|
||||||
'redirects' => 1,
|
'redirects' => 1,
|
||||||
|
@ -193,12 +198,12 @@ class CurlDownloader
|
||||||
$this->io->writeError('Downloading ' . $url . $usingProxy . $ifModified, true, IOInterface::DEBUG);
|
$this->io->writeError('Downloading ' . $url . $usingProxy . $ifModified, true, IOInterface::DEBUG);
|
||||||
|
|
||||||
$this->checkCurlResult(curl_multi_add_handle($this->multiHandle, $curlHandle));
|
$this->checkCurlResult(curl_multi_add_handle($this->multiHandle, $curlHandle));
|
||||||
|
// TODO progress
|
||||||
//$params['notification'](STREAM_NOTIFY_RESOLVE, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0, false);
|
//$params['notification'](STREAM_NOTIFY_RESOLVE, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function tick()
|
public function tick()
|
||||||
{
|
{
|
||||||
// TODO check we have active handles before doing this
|
|
||||||
if (!$this->jobs) {
|
if (!$this->jobs) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -229,6 +234,7 @@ class CurlDownloader
|
||||||
$statusCode = null;
|
$statusCode = null;
|
||||||
$response = null;
|
$response = null;
|
||||||
try {
|
try {
|
||||||
|
// TODO progress
|
||||||
//$this->onProgress($curlHandle, $job['callback'], $progress, $job['progress']);
|
//$this->onProgress($curlHandle, $job['callback'], $progress, $job['progress']);
|
||||||
if (CURLE_OK !== $errno) {
|
if (CURLE_OK !== $errno) {
|
||||||
throw new TransportException($error);
|
throw new TransportException($error);
|
||||||
|
@ -263,7 +269,6 @@ class CurlDownloader
|
||||||
|
|
||||||
// handle 3xx redirects, 304 Not Modified is excluded
|
// handle 3xx redirects, 304 Not Modified is excluded
|
||||||
if ($statusCode >= 300 && $statusCode <= 399 && $statusCode !== 304 && $job['redirects'] < $this->maxRedirects) {
|
if ($statusCode >= 300 && $statusCode <= 399 && $statusCode !== 304 && $job['redirects'] < $this->maxRedirects) {
|
||||||
// TODO
|
|
||||||
$location = $this->handleRedirect($job, $response);
|
$location = $this->handleRedirect($job, $response);
|
||||||
if ($location) {
|
if ($location) {
|
||||||
$this->restartJob($job, $location, array('redirects' => $job['attributes']['redirects'] + 1));
|
$this->restartJob($job, $location, array('redirects' => $job['attributes']['redirects'] + 1));
|
||||||
|
@ -274,6 +279,7 @@ class CurlDownloader
|
||||||
// fail 4xx and 5xx responses and capture the response
|
// fail 4xx and 5xx responses and capture the response
|
||||||
if ($statusCode >= 400 && $statusCode <= 599) {
|
if ($statusCode >= 400 && $statusCode <= 599) {
|
||||||
throw $this->failResponse($job, $response, $response->getStatusMessage());
|
throw $this->failResponse($job, $response, $response->getStatusMessage());
|
||||||
|
// TODO progress
|
||||||
// $this->io->overwriteError("Downloading (<error>failed</error>)", false);
|
// $this->io->overwriteError("Downloading (<error>failed</error>)", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -320,24 +326,13 @@ class CurlDownloader
|
||||||
if ($this->jobs[$i]['progress'] !== $progress) {
|
if ($this->jobs[$i]['progress'] !== $progress) {
|
||||||
$previousProgress = $this->jobs[$i]['progress'];
|
$previousProgress = $this->jobs[$i]['progress'];
|
||||||
$this->jobs[$i]['progress'] = $progress;
|
$this->jobs[$i]['progress'] = $progress;
|
||||||
try {
|
|
||||||
//$this->onProgress($curlHandle, $this->jobs[$i]['callback'], $progress, $previousProgress);
|
|
||||||
} catch (TransportException $e) {
|
|
||||||
var_dump('Caught '.$e->getMessage());die;
|
|
||||||
unset($this->jobs[$i]);
|
|
||||||
curl_multi_remove_handle($this->multiHandle, $curlHandle);
|
|
||||||
curl_close($curlHandle);
|
|
||||||
|
|
||||||
fclose($job['headerHandle']);
|
// TODO
|
||||||
fclose($job['bodyHandle']);
|
//$this->onProgress($curlHandle, $this->jobs[$i]['callback'], $progress, $previousProgress);
|
||||||
if ($job['filename']) {
|
|
||||||
@unlink($job['filename'].'~');
|
|
||||||
}
|
|
||||||
call_user_func($job['reject'], $e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
|
// TODO
|
||||||
var_dump('Caught2', get_class($e), $e->getMessage(), $e);die;
|
var_dump('Caught2', get_class($e), $e->getMessage(), $e);die;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -444,13 +439,10 @@ class CurlDownloader
|
||||||
|
|
||||||
private function onProgress($curlHandle, callable $notify, array $progress, array $previousProgress)
|
private function onProgress($curlHandle, callable $notify, array $progress, array $previousProgress)
|
||||||
{
|
{
|
||||||
|
// TODO add support for progress
|
||||||
if (300 <= $progress['http_code'] && $progress['http_code'] < 400) {
|
if (300 <= $progress['http_code'] && $progress['http_code'] < 400) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!$previousProgress['http_code'] && $progress['http_code'] && $progress['http_code'] < 200 || 400 <= $progress['http_code']) {
|
|
||||||
$code = 403 === $progress['http_code'] ? STREAM_NOTIFY_AUTH_RESULT : STREAM_NOTIFY_FAILURE;
|
|
||||||
$notify($code, STREAM_NOTIFY_SEVERITY_ERR, curl_error($curlHandle), $progress['http_code'], 0, 0, false);
|
|
||||||
}
|
|
||||||
if ($previousProgress['download_content_length'] < $progress['download_content_length']) {
|
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);
|
$notify(STREAM_NOTIFY_FILE_SIZE_IS, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, (int) $progress['download_content_length'], false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,8 +33,8 @@ class HttpDownloader
|
||||||
private $config;
|
private $config;
|
||||||
private $jobs = array();
|
private $jobs = array();
|
||||||
private $options = array();
|
private $options = array();
|
||||||
private $index;
|
private $runningJobs = 0;
|
||||||
private $progress;
|
private $maxJobs = 10;
|
||||||
private $lastProgress;
|
private $lastProgress;
|
||||||
private $disableTls = false;
|
private $disableTls = false;
|
||||||
private $curl;
|
private $curl;
|
||||||
|
@ -42,8 +42,6 @@ class HttpDownloader
|
||||||
private $idGen = 0;
|
private $idGen = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
|
||||||
*
|
|
||||||
* @param IOInterface $io The IO instance
|
* @param IOInterface $io The IO instance
|
||||||
* @param Config $config The config
|
* @param Config $config The config
|
||||||
* @param array $options The options
|
* @param array $options The options
|
||||||
|
@ -131,35 +129,24 @@ class HttpDownloader
|
||||||
'status' => self::STATUS_QUEUED,
|
'status' => self::STATUS_QUEUED,
|
||||||
'request' => $request,
|
'request' => $request,
|
||||||
'sync' => $sync,
|
'sync' => $sync,
|
||||||
|
'origin' => Url::getOrigin($this->config, $request['url']),
|
||||||
);
|
);
|
||||||
|
|
||||||
$origin = Url::getOrigin($this->config, $job['request']['url']);
|
|
||||||
|
|
||||||
// capture username/password from URL if there is one
|
// capture username/password from URL if there is one
|
||||||
if (preg_match('{^https?://([^:/]+):([^@/]+)@([^/]+)}i', $request['url'], $match)) {
|
if (preg_match('{^https?://([^:/]+):([^@/]+)@([^/]+)}i', $request['url'], $match)) {
|
||||||
$this->io->setAuthentication($origin, rawurldecode($match[1]), rawurldecode($match[2]));
|
$this->io->setAuthentication($job['origin'], rawurldecode($match[1]), rawurldecode($match[2]));
|
||||||
}
|
}
|
||||||
|
|
||||||
$curl = $this->curl;
|
|
||||||
$rfs = $this->rfs;
|
$rfs = $this->rfs;
|
||||||
$io = $this->io;
|
|
||||||
|
|
||||||
if ($curl && preg_match('{^https?://}i', $job['request']['url'])) {
|
if ($this->curl && preg_match('{^https?://}i', $job['request']['url'])) {
|
||||||
$resolver = function ($resolve, $reject) use (&$job, $curl, $origin) {
|
$resolver = function ($resolve, $reject) use (&$job) {
|
||||||
// start job
|
$job['status'] = HttpDownloader::STATUS_QUEUED;
|
||||||
$url = $job['request']['url'];
|
$job['resolve'] = $resolve;
|
||||||
$options = $job['request']['options'];
|
$job['reject'] = $reject;
|
||||||
|
|
||||||
$job['status'] = HttpDownloader::STATUS_STARTED;
|
|
||||||
|
|
||||||
if ($job['request']['copyTo']) {
|
|
||||||
$curl->download($resolve, $reject, $origin, $url, $options, $job['request']['copyTo']);
|
|
||||||
} else {
|
|
||||||
$curl->download($resolve, $reject, $origin, $url, $options);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
$resolver = function ($resolve, $reject) use (&$job, $rfs, $curl, $origin) {
|
$resolver = function ($resolve, $reject) use (&$job, $rfs) {
|
||||||
// start job
|
// start job
|
||||||
$url = $job['request']['url'];
|
$url = $job['request']['url'];
|
||||||
$options = $job['request']['options'];
|
$options = $job['request']['options'];
|
||||||
|
@ -167,11 +154,11 @@ class HttpDownloader
|
||||||
$job['status'] = HttpDownloader::STATUS_STARTED;
|
$job['status'] = HttpDownloader::STATUS_STARTED;
|
||||||
|
|
||||||
if ($job['request']['copyTo']) {
|
if ($job['request']['copyTo']) {
|
||||||
$result = $rfs->copy($origin, $url, $job['request']['copyTo'], false /* TODO progress */, $options);
|
$result = $rfs->copy($job['origin'], $url, $job['request']['copyTo'], false /* TODO progress */, $options);
|
||||||
|
|
||||||
$resolve($result);
|
$resolve($result);
|
||||||
} else {
|
} else {
|
||||||
$body = $rfs->getContents($origin, $url, false /* TODO progress */, $options);
|
$body = $rfs->getContents($job['origin'], $url, false /* TODO progress */, $options);
|
||||||
$headers = $rfs->getLastHeaders();
|
$headers = $rfs->getLastHeaders();
|
||||||
$response = new Http\Response($job['request'], $rfs->findStatusCode($headers), $headers, $body);
|
$response = new Http\Response($job['request'], $rfs->findStatusCode($headers), $headers, $body);
|
||||||
|
|
||||||
|
@ -180,26 +167,85 @@ class HttpDownloader
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$downloader = $this;
|
||||||
|
$io = $this->io;
|
||||||
|
|
||||||
$canceler = function () {};
|
$canceler = function () {};
|
||||||
|
|
||||||
$promise = new Promise($resolver, $canceler);
|
$promise = new Promise($resolver, $canceler);
|
||||||
$promise->then(function ($response) use (&$job) {
|
$promise->then(function ($response) use (&$job, $downloader) {
|
||||||
$job['status'] = HttpDownloader::STATUS_COMPLETED;
|
$job['status'] = HttpDownloader::STATUS_COMPLETED;
|
||||||
$job['response'] = $response;
|
$job['response'] = $response;
|
||||||
// TODO look for more jobs to start once we throttle to max X jobs
|
|
||||||
}, function ($e) use ($io, &$job) {
|
// TODO 3.0 this should be done directly on $this when PHP 5.3 is dropped
|
||||||
// var_dump(__CLASS__ . __LINE__);
|
$downloader->markJobDone();
|
||||||
// var_dump(get_class($e));
|
$downloader->scheduleNextJob();
|
||||||
// var_dump($e->getMessage());
|
|
||||||
// die;
|
return $response;
|
||||||
|
}, function ($e) use ($io, &$job, $downloader) {
|
||||||
$job['status'] = HttpDownloader::STATUS_FAILED;
|
$job['status'] = HttpDownloader::STATUS_FAILED;
|
||||||
$job['exception'] = $e;
|
$job['exception'] = $e;
|
||||||
|
|
||||||
|
$downloader->markJobDone();
|
||||||
|
|
||||||
|
throw $e;
|
||||||
});
|
});
|
||||||
$this->jobs[$job['id']] =& $job;
|
$this->jobs[$job['id']] =& $job;
|
||||||
|
|
||||||
|
if ($this->runningJobs < $this->maxJobs) {
|
||||||
|
$this->startJob($job['id']);
|
||||||
|
}
|
||||||
|
|
||||||
return array($job, $promise);
|
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 ($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)
|
public function wait($index = null, $progress = false)
|
||||||
{
|
{
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|
Loading…
Reference in New Issue