39 use ProtectedHookAccessorTrait;
49 private $postSendStrategy;
52 private const DEFER_FASTCGI_FINISH_REQUEST = 1;
54 private const DEFER_SET_LENGTH_AND_FLUSH = 2;
56 private const DEFER_CLI_MODE = 3;
62 $this->context = $context ?: RequestContext::getMain();
63 $this->config = $this->context->getConfig();
65 if ( $GLOBALS[
'wgCommandLineMode'] ) {
66 $this->postSendStrategy = self::DEFER_CLI_MODE;
67 } elseif ( function_exists(
'fastcgi_finish_request' ) ) {
68 $this->postSendStrategy = self::DEFER_FASTCGI_FINISH_REQUEST;
70 $this->postSendStrategy = self::DEFER_SET_LENGTH_AND_FLUSH;
80 private function parseTitle() {
81 $request = $this->context->getRequest();
82 $curid = $request->getInt(
'curid' );
83 $title = $request->getText(
'title' );
87 $ret = Title::newFromID( $curid );
90 if ( $ret !==
null ) {
93 if ( $ret->getNamespace() ===
NS_MEDIA ) {
99 $languageConverter = $services
100 ->getLanguageConverterFactory()
101 ->getLanguageConverter( $services->getContentLanguage() );
102 if ( $languageConverter->hasVariants() && !$ret->exists() ) {
103 $languageConverter->findVariantLink(
$title, $ret );
112 if ( $ret ===
null || !$ret->isSpecialPage() ) {
114 $oldid = $request->getInt(
'oldid' );
115 $oldid = $oldid ?: $request->getInt(
'diff' );
119 ->getRevisionLookup()
120 ->getRevisionById( $oldid );
123 $revRecord->getPageAsLinkTarget()
129 if ( $ret ===
null && $request->getCheck(
'search' ) ) {
138 && strval(
$title ) ===
''
139 && !$request->getCheck(
'curid' )
140 && $request->getRawVal(
'action' ) !==
'delete'
145 if ( $ret ===
null || ( $ret->getDBkey() ==
'' && !$ret->isExternal() ) ) {
160 if ( !$this->context->hasTitle() ) {
162 $this->context->setTitle( $this->parseTitle() );
167 return $this->context->getTitle();
176 if ( $this->action === null ) {
177 $this->action = $this->context->getActionName();
180 return $this->action;
195 private function performRequest() {
198 $request = $this->context->getRequest();
199 $output = $this->context->getOutput();
201 if ( $request->getRawVal(
'printable' ) ===
'yes' ) {
202 $output->setPrintable();
205 $user = $this->context->getUser();
206 $title = $this->context->getTitle();
208 $this->getHookRunner()->onBeforeInitialize(
$title,
null, $output, $user, $request, $this );
212 ||
$title->isSpecial(
'Badtitle' )
226 $permissionStatus = PermissionStatus::newEmpty();
227 if ( !$this->context->getAuthority()->authorizeRead(
'read',
$title, $permissionStatus ) ) {
238 $this->context->setTitle( $badTitle );
245 if (
$title->isExternal() ) {
246 $rdfrom = $request->getVal(
'rdfrom' );
248 $url =
$title->getFullURL( [
'rdfrom' => $rdfrom ] );
250 $query = $request->getValues();
251 unset( $query[
'title'] );
252 $url =
$title->getFullURL( $query );
255 if ( !preg_match(
'/^' . preg_quote( $this->config->get( MainConfigNames::Server ),
'/' ) .
'/', $url )
259 $output->redirect( $url, 301 );
271 } elseif ( !$this->tryNormaliseRedirect(
$title ) ) {
273 $spFactory = MediaWikiServices::getInstance()->getSpecialPageFactory();
274 if (
$title->isSpecialPage() ) {
275 $specialPage = $spFactory->getPage(
$title->getDBkey() );
277 $specialPage->setContext( $this->context );
278 if ( $this->config->get( MainConfigNames::HideIdentifiableRedirects )
279 && $specialPage->personallyIdentifiableTarget()
281 [ , $subpage ] = $spFactory->resolveAlias(
$title->getDBkey() );
282 $target = $specialPage->getRedirect( $subpage );
284 if ( $target instanceof
Title ) {
285 if ( $target->isExternal() ) {
289 'force/' . $target->getPrefixedDBkey()
293 $query = $specialPage->getRedirectQuery( $subpage ) ?: [];
295 $derivateRequest->setRequestURL( $request->getRequestURL() );
296 $this->context->setRequest( $derivateRequest );
298 $output->lowerCdnMaxage( 0 );
299 $this->context->setTitle( $target );
302 $this->action =
null;
304 $output->addJsConfigVars( [
305 'wgInternalRedirectTargetUrl' => $target->getLinkURL( $query ),
307 $output->addModules(
'mediawiki.action.view.redirect' );
314 if (
$title->isSpecialPage() ) {
316 $spFactory->executePath(
$title, $this->context );
320 $article = $this->initializeArticle();
321 if ( is_object( $article ) ) {
322 $this->performAction( $article, $requestTitle );
323 } elseif ( is_string( $article ) ) {
324 $output->redirect( $article );
326 throw new MWException(
"Shouldn't happen: MediaWiki::initializeArticle()"
327 .
" returned neither an object nor a URL" );
330 $output->considerCacheSettingsFinal();
356 private function tryNormaliseRedirect(
Title $title ) {
357 $request = $this->context->getRequest();
358 $output = $this->context->getOutput();
360 if ( $request->getRawVal(
'action',
'view' ) !=
'view'
361 || $request->wasPosted()
362 || ( $request->getCheck(
'title' )
363 &&
$title->getPrefixedDBkey() == $request->getText(
'title' ) )
364 || count( $request->getValueNames( [
'action',
'title' ] ) )
365 || !$this->getHookRunner()->onTestCanonicalRedirect( $request,
$title, $output )
370 if ( $this->config->get( MainConfigNames::MainPageIsDomainRoot ) && $request->getRequestURL() ===
'/' ) {
374 if (
$title->isSpecialPage() ) {
375 [ $name, $subpage ] = MediaWikiServices::getInstance()->getSpecialPageFactory()->
376 resolveAlias(
$title->getDBkey() );
383 if ( $targetUrl == $request->getFullRequestURL() ) {
384 $message =
"Redirect loop detected!\n\n" .
385 "This means the wiki got confused about what page was " .
386 "requested; this sometimes happens when moving a wiki " .
387 "to a new server or changing the server configuration.\n\n";
389 if ( $this->config->get( MainConfigNames::UsePathInfo ) ) {
390 $message .=
"The wiki is trying to interpret the page " .
391 "title from the URL path portion (PATH_INFO), which " .
392 "sometimes fails depending on the web server. Try " .
393 "setting \"\$wgUsePathInfo = false;\" in your " .
394 "LocalSettings.php, or check that \$wgArticlePath " .
397 $message .=
"Your web server was detected as possibly not " .
398 "supporting URL path components (PATH_INFO) correctly; " .
399 "check your LocalSettings.php for a customized " .
400 "\$wgArticlePath setting and/or toggle \$wgUsePathInfo " .
405 $output->setCdnMaxage( 1200 );
406 $output->redirect( $targetUrl,
'301' );
416 private function initializeArticle() {
417 $title = $this->context->getTitle();
418 $services = MediaWikiServices::getInstance();
419 if ( $this->context->canUseWikiPage() ) {
422 $page = $this->context->getWikiPage();
426 $page = $services->getWikiPageFactory()->newFromTitle(
$title );
427 $this->context->setWikiPage( $page );
428 wfWarn(
"RequestContext::canUseWikiPage() returned false" );
437 if ( !$page->getContentHandler()->supportsRedirects() ) {
441 $request = $this->context->getRequest();
445 $action = $request->getRawVal(
'action',
'view' );
447 if ( ( $action ==
'view' || $action ==
'render' )
448 && !$request->getCheck(
'oldid' )
449 && !$request->getCheck(
'diff' )
450 && $request->getRawVal(
'redirect' ) !==
'no'
452 && !( is_object(
$file ) &&
$file->exists() && !
$file->getRedirected() )
455 $ignoreRedirect = $target =
false;
457 $this->getHookRunner()->onInitializeArticleMaybeRedirect(
$title, $request,
459 $ignoreRedirect, $target, $article );
464 if ( !$ignoreRedirect && ( $target || $page->isRedirect() ) ) {
466 $target = $target ?: $page->followRedirect();
467 if ( is_string( $target ) && !$this->config->get( MainConfigNames::DisableHardRedirects ) ) {
471 if ( is_object( $target ) ) {
473 $rpage = $services->getWikiPageFactory()->newFromTitle( $target );
474 $rpage->loadPageData();
475 if ( $rpage->exists() || ( is_object(
$file ) && !
$file->isLocal() ) ) {
477 $rarticle->setRedirectedFrom(
$title );
479 $article = $rarticle;
480 $this->context->setTitle( $target );
481 $this->context->setWikiPage( $article->
getPage() );
486 $this->context->setTitle( $article->
getTitle() );
487 $this->context->setWikiPage( $article->
getPage() );
500 private function performAction(
Article $article,
Title $requestTitle ) {
501 $request = $this->context->getRequest();
502 $output = $this->context->getOutput();
504 $user = $this->context->getUser();
506 if ( !$this->getHookRunner()->onMediaWikiPerformAction(
507 $output, $article,
$title, $user, $request, $this )
512 $t = microtime(
true );
513 $actionName = $this->getAction();
514 $services = MediaWikiServices::getInstance();
515 $action = $services->getActionFactory()->getAction( $actionName, $article, $this->context );
517 if ( $action instanceof
Action ) {
519 if ( $action->needsReadRights() && !$user->isAllowed(
'read' ) ) {
524 if ( $request->wasPosted() && !$action->doesWrites() ) {
526 $trxLimits = $this->config->get( MainConfigNames::TrxProfilerLimits );
527 $trxProfiler->setExpectations( $trxLimits[
'POST-nonwrite'], __METHOD__ );
528 $request->markAsSafeRequest();
533 if ( $this->config->get( MainConfigNames::UseCdn ) ) {
534 $htmlCacheUpdater = $services->getHtmlCacheUpdater();
535 if ( $request->matchUrlForCdn( $htmlCacheUpdater->getUrls( $requestTitle ) ) ) {
536 $output->setCdnMaxage( $this->config->get( MainConfigNames::CdnMaxAge ) );
538 $output->setCdnMaxage( 3600 );
544 $runTime = microtime(
true ) -
$t;
545 $services->getStatsdDataFactory()->timing(
546 'action.' . strtr( $actionName,
'.',
'_' ) .
'.executeTiming',
553 $output->setStatusCode( 404 );
554 $output->showErrorPage(
'nosuchaction',
'nosuchactiontext' );
563 }
catch ( Exception $e ) {
564 $context = $this->context;
565 $action = $context->
getRequest()->getRawVal(
'action',
'view' );
568 $context->hasTitle() &&
570 in_array( $action, [
'view',
'history' ],
true ) &&
575 if (
$cache->isCached() ) {
577 print MWExceptionRenderer::getHTML( $e );
581 MWExceptionHandler::handleException( $e, MWExceptionHandler::CAUGHT_BY_ENTRYPOINT );
582 }
catch ( Throwable $e ) {
584 MWExceptionHandler::handleException( $e, MWExceptionHandler::CAUGHT_BY_ENTRYPOINT );
587 $this->doPostOutputShutdown();
593 private function schedulePostSendJobs() {
594 $jobRunRate = $this->config->get( MainConfigNames::JobRunRate );
597 $this->
getTitle()->isSpecial(
'RunJobs' ) ||
600 MediaWikiServices::getInstance()->getReadOnlyMode()->isReadOnly() ||
602 $this->context->getRequest()->getMethod() ===
'HEAD' ||
603 $this->context->getRequest()->getHeader(
'If-Modified-Since' )
608 if ( $jobRunRate < 1 ) {
609 $max = mt_getrandmax();
610 if ( mt_rand( 0, $max ) > $max * $jobRunRate ) {
615 $n = intval( $jobRunRate );
620 $logger = LoggerFactory::getInstance(
'runJobs' );
621 if ( $this->config->get( MainConfigNames::RunJobsAsync ) ) {
623 $invokedWithSuccess = $this->triggerAsyncJobs( $n, $logger );
624 if ( !$invokedWithSuccess ) {
626 $logger->warning(
"Jobs switched to blocking; Special:RunJobs disabled" );
627 $this->triggerSyncJobs( $n );
630 $this->triggerSyncJobs( $n );
639 private function doPreOutputCommit() {
640 self::preOutputCommit( $this->context );
660 $services = MediaWikiServices::getInstance();
661 $lbFactory = $services->getDBLoadBalancerFactory();
664 ignore_user_abort(
true );
667 $lbFactory->commitPrimaryChanges(
670 [
'maxWriteDuration' => $config->
get( MainConfigNames::MaxUserDBWriteDuration ) ]
672 wfDebug( __METHOD__ .
': primary transaction round committed' );
675 DeferredUpdates::doUpdates(
677 $config->
get( MainConfigNames::ForceDeferredUpdatesPreSend )
678 ? DeferredUpdates::ALL
679 : DeferredUpdates::PRESEND
682 wfDebug( __METHOD__ .
': pre-send deferred updates completed' );
685 $request->getSession()->save();
686 wfDebug( __METHOD__ .
': session changes committed' );
692 $isCrossWikiRedirect = (
693 $output->getRedirect() &&
694 $lbFactory->hasOrMadeRecentPrimaryChanges( INF ) &&
695 self::getUrlDomainDistance( $output->getRedirect() ) ===
'remote'
702 $lbFactory->shutdown(
703 $lbFactory::SHUTDOWN_NORMAL,
710 $allowHeaders = !( $output->isDisabled() || headers_sent() );
712 if ( $cpIndex > 0 ) {
713 if ( $allowHeaders ) {
714 $expires = $now + ChronologyProtector::POSITION_COOKIE_TTL;
715 $options = [
'prefix' =>
'' ];
716 $value = $lbFactory::makeCookieValueFromCPIndex( $cpIndex, $now, $cpClientId );
717 $request->response()->setCookie(
'cpPosIndex', $value, $expires, $options );
720 if ( $isCrossWikiRedirect ) {
721 if ( $output->getRedirect() ) {
722 $safeUrl = $lbFactory->appendShutdownCPIndexAsQuery(
723 $output->getRedirect(),
726 $output->redirect( $safeUrl );
728 MWExceptionHandler::logException(
729 new LogicException(
"No redirect; cannot append cpPosIndex parameter." ),
730 MWExceptionHandler::CAUGHT_BY_ENTRYPOINT
736 if ( $allowHeaders ) {
740 if ( $request->wasPosted() && $lbFactory->hasOrMadeRecentPrimaryChanges() ) {
741 $expires = $now + max(
742 ChronologyProtector::POSITION_COOKIE_TTL,
743 $config->
get( MainConfigNames::DataCenterUpdateStickTTL )
745 $options = [
'prefix' =>
'' ];
746 $request->response()->setCookie(
'UseDC',
'master', $expires, $options );
747 $request->response()->setCookie(
'UseCDNCache',
'false', $expires, $options );
752 if ( $lbFactory->laggedReplicaUsed() ) {
753 $maxAge = $config->
get( MainConfigNames::CdnMaxageLagged );
754 $output->lowerCdnMaxage( $maxAge );
755 $request->response()->header(
"X-Database-Lagged: true" );
757 "Lagged DB used; CDN cache TTL limited to $maxAge seconds" );
761 if ( $services->getMessageCache()->isDisabled() ) {
762 $maxAge = $config->
get( MainConfigNames::CdnMaxageSubstitute );
763 $output->lowerCdnMaxage( $maxAge );
764 $request->response()->header(
"X-Response-Substitute: true" );
767 if ( !$output->couldBePublicCached() || $output->haveCacheVaryCookies() ) {
779 $services->getBlockManager()
780 ->trackBlockWithCookie( $user, $request->response() );
789 private static function getUrlDomainDistance( $url ) {
790 $clusterWiki = WikiMap::getWikiFromUrl( $url );
791 if ( WikiMap::isCurrentWikiId( $clusterWiki ) ) {
794 if ( $clusterWiki !==
false ) {
812 $timing = $this->context->getTiming();
813 $timing->mark(
'requestShutdown' );
816 if ( $this->postSendStrategy === self::DEFER_FASTCGI_FINISH_REQUEST ) {
818 fastcgi_finish_request();
819 } elseif ( $this->postSendStrategy === self::DEFER_SET_LENGTH_AND_FLUSH ) {
821 if ( ob_get_level() ) {
832 WebResponse::disableForPostSend();
834 ob_start(
static function () {
838 $this->restInPeace();
839 }
catch ( Throwable $e ) {
840 MWExceptionHandler::rollbackPrimaryChangesAndLog(
842 MWExceptionHandler::CAUGHT_BY_ENTRYPOINT
845 $length = ob_get_length();
847 trigger_error( __METHOD__ .
": suppressed $length byte(s)", E_USER_NOTICE );
855 private function main() {
863 $request = $this->context->getRequest();
865 $trxLimits = $this->config->get( MainConfigNames::TrxProfilerLimits );
866 $trxProfiler = Profiler::instance()->getTransactionProfiler();
867 $trxProfiler->setLogger( LoggerFactory::getInstance(
'DBPerformance' ) );
868 if ( $request->hasSafeMethod() ) {
869 $trxProfiler->setExpectations( $trxLimits[
'GET'], __METHOD__ );
871 $trxProfiler->setExpectations( $trxLimits[
'POST'], __METHOD__ );
874 if ( $this->maybeDoHttpsRedirect() ) {
878 $output = $this->context->getOutput();
883 $action = $this->getAction();
886 if (
$cache->isCacheGood( ) ) {
888 $timestamp =
$cache->cacheTimestamp();
889 if ( !$output->checkLastModified( $timestamp ) ) {
890 $cache->loadFromFileCache( $this->context );
894 $this->context->getWikiPage()->doViewUpdates( $this->context->getAuthority() );
904 $this->performRequest();
907 $e->
report( ErrorPageError::STAGE_OUTPUT );
908 $output->considerCacheSettingsFinal();
917 $this->doPreOutputCommit();
919 $this->schedulePostSendJobs();
922 $this->outputResponsePayload( $output->output(
true ) );
931 private function shouldDoHttpRedirect() {
932 $request = $this->context->getRequest();
935 if ( $request->getProtocol() !==
'http' ) {
939 $force = $this->config->get( MainConfigNames::ForceHTTPS );
945 throw new RuntimeException(
'$wgForceHTTPS is true but the server is not HTTPS' );
956 return $request->getSession()->shouldForceHTTPS() ||
958 $request->getCookie(
'forceHTTPS',
'' ) ||
959 $this->context->getUser()->requiresHTTPS();
971 private function maybeDoHttpsRedirect() {
972 if ( !$this->shouldDoHttpRedirect() ) {
976 $request = $this->context->getRequest();
977 $oldUrl = $request->getFullRequestURL();
978 $redirUrl = preg_replace(
'#^http://#',
'https://', $oldUrl );
980 if ( $request->wasPosted() ) {
989 wfDebugLog(
'RedirectedPosts',
"Redirected from HTTP to HTTPS: $oldUrl" );
993 $this->context->setTitle(
$title );
995 $output = $this->context->getOutput();
996 $output->addVaryHeader(
'X-Forwarded-Proto' );
997 $output->redirect( $redirUrl );
1012 private function outputResponsePayload(
$content ) {
1031 if ( function_exists(
'apache_setenv' ) ) {
1033 @apache_setenv(
'no-gzip',
'1' );
1038 $this->postSendStrategy === self::DEFER_SET_LENGTH_AND_FLUSH &&
1040 in_array( http_response_code(), [ 200, 404 ],
true ) &&
1042 DeferredUpdates::pendingUpdatesCount() &&
1044 ob_get_level() <= 1 &&
1048 $response = $this->context->getRequest()->response();
1050 $obStatus = ob_get_status();
1051 if ( !isset( $obStatus[
'name'] ) ) {
1053 $response->header(
'Content-Length: ' . strlen(
$content ) );
1054 } elseif ( $obStatus[
'name'] ===
'default output handler' ) {
1057 $response->header(
'Content-Length: ' . ( ob_get_length() + strlen(
$content ) ) );
1069 if ( ( $_SERVER[
'SERVER_PROTOCOL'] ??
'' ) ===
'HTTP/1.1' ) {
1070 $response->header(
'Connection: close' );
1086 ignore_user_abort(
true );
1088 $services = MediaWikiServices::getInstance();
1089 $lbFactory = $services->getDBLoadBalancerFactory();
1091 $lbFactory->commitPrimaryChanges( __METHOD__ );
1094 $profiler = Profiler::instance();
1095 $trxProfiler = $profiler->getTransactionProfiler();
1096 $trxProfiler->redefineExpectations(
1097 $this->context->getRequest()->hasSafeMethod()
1098 ? $this->config->get( MainConfigNames::TrxProfilerLimits )[
'PostSend-GET']
1099 : $this->config->get( MainConfigNames::TrxProfilerLimits )[
'PostSend-POST'],
1104 DeferredUpdates::doUpdates();
1108 $profiler->logData();
1110 self::emitBufferedStatsdData(
1111 $services->getStatsdDataFactory(),
1116 $services->getMetricsFactory()->flush();
1119 $lbFactory->commitPrimaryChanges( __METHOD__ );
1120 $lbFactory->shutdown( $lbFactory::SHUTDOWN_NO_CHRONPROT );
1122 wfDebug(
"Request ended normally" );
1155 if ( $config->
get( MainConfigNames::StatsdServer ) && $stats->
hasData() ) {
1157 $statsdServer = explode(
':', $config->
get( MainConfigNames::StatsdServer ), 2 );
1158 $statsdHost = $statsdServer[0];
1159 $statsdPort = $statsdServer[1] ?? 8125;
1160 $statsdSender =
new SocketSender( $statsdHost, $statsdPort );
1162 $statsdClient->setSamplingRates( $config->
get( MainConfigNames::StatsdSamplingRates ) );
1163 $statsdClient->send( $stats->
getData() );
1164 }
catch ( Exception $e ) {
1165 MWExceptionHandler::logException( $e, MWExceptionHandler::CAUGHT_BY_ENTRYPOINT );
1175 private function triggerSyncJobs( $n ) {
1176 $scope = Profiler::instance()->getTransactionProfiler()->silenceForScope();
1177 $runner = MediaWikiServices::getInstance()->getJobRunner();
1178 $runner->run( [
'maxJobs' => $n ] );
1179 ScopedCallback::consume( $scope );
1187 private function triggerAsyncJobs( $n, LoggerInterface $runJobsLogger ) {
1188 $services = MediaWikiServices::getInstance();
1190 $group = $services->getJobQueueGroupFactory()->makeJobQueueGroup();
1191 if ( !$group->queuesHaveJobs( JobQueueGroup::TYPE_DEFAULT ) ) {
1195 $query = [
'title' =>
'Special:RunJobs',
1196 'tasks' =>
'jobs',
'maxjobs' => $n,
'sigexpiry' => time() + 5 ];
1198 $query, $this->config->get( MainConfigNames::SecretKey ) );
1200 $errno = $errstr =
null;
1201 $info =
wfParseUrl( $this->config->get( MainConfigNames::CanonicalServer ) );
1202 $host = $info ? $info[
'host'] :
null;
1204 if ( isset( $info[
'scheme'] ) && $info[
'scheme'] ==
'https' ) {
1205 $host =
"tls://" . $host;
1208 if ( isset( $info[
'port'] ) ) {
1209 $port = $info[
'port'];
1212 AtEase::suppressWarnings();
1213 $sock = $host ? fsockopen(
1221 AtEase::restoreWarnings();
1223 $invokedWithSuccess =
true;
1225 $special = $services->getSpecialPageFactory()->getPage(
'RunJobs' );
1226 $url = $special->getPageTitle()->getCanonicalURL( $query );
1228 "POST $url HTTP/1.1\r\n" .
1229 "Host: {$info['host']}\r\n" .
1230 "Connection: Close\r\n" .
1231 "Content-Length: 0\r\n\r\n"
1234 $runJobsLogger->info(
"Running $n job(s) via '$url'" );
1237 stream_set_timeout( $sock, 2 );
1238 $bytes = fwrite( $sock, $req );
1239 if ( $bytes !== strlen( $req ) ) {
1240 $invokedWithSuccess =
false;
1241 $runJobsLogger->error(
"Failed to start cron API (socket write error)" );
1245 $start = microtime(
true );
1246 $status = fgets( $sock );
1247 $sec = microtime(
true ) - $start;
1248 if ( !preg_match(
'#^HTTP/\d\.\d 202 #', $status ) ) {
1249 $invokedWithSuccess =
false;
1250 $runJobsLogger->error(
"Failed to start cron API: received '$status' ($sec)" );
1255 $invokedWithSuccess =
false;
1256 $runJobsLogger->error(
"Failed to start cron API (socket error $errno): $errstr" );
1259 return $invokedWithSuccess;