mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-25 16:22:43 +01:00
Move a lot of pre-request checks to PhabricatorStartup
Summary: We have a lot of mess to get through before we can load libphutil and enter Phabricator code properly. Move it to a dedicated class. I'm probably going to merge PhabricatorRequestOverseer into this, although the check that lives there now is kind of weird. It also does not really need to be a pre-load check and could be handled better. I stopped shoving stuff in here once I got to ENV stuff, I'm going to tackle that next. Test Plan: Ran phabricator normally; introduced fatals and misconfigurations. Grepped for changed symbols. Reviewers: btrahan, vrana Reviewed By: btrahan CC: aran, asherkin Maniphest Tasks: T2223 Differential Revision: https://secure.phabricator.com/D4282
This commit is contained in:
parent
a88b69a4b6
commit
ed58f6c5f4
4 changed files with 241 additions and 114 deletions
|
@ -15,6 +15,9 @@ final class DarkConsoleServicesPlugin extends DarkConsolePlugin {
|
||||||
return 'Information about services.';
|
return 'Information about services.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @phutil-external-symbol class PhabricatorStartup
|
||||||
|
*/
|
||||||
public function generateData() {
|
public function generateData() {
|
||||||
|
|
||||||
$log = PhutilServiceProfiler::getInstance()->getServiceCallLog();
|
$log = PhutilServiceProfiler::getInstance()->getServiceCallLog();
|
||||||
|
@ -130,7 +133,7 @@ final class DarkConsoleServicesPlugin extends DarkConsolePlugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'start' => $GLOBALS['__start__'],
|
'start' => PhabricatorStartup::getStartTime(),
|
||||||
'end' => microtime(true),
|
'end' => microtime(true),
|
||||||
'log' => $log,
|
'log' => $log,
|
||||||
);
|
);
|
||||||
|
|
|
@ -19,6 +19,8 @@ final class PhabricatorRequestOverseer {
|
||||||
* to the documentation the stream isn't available for "multipart/form-data"
|
* to the documentation the stream isn't available for "multipart/form-data"
|
||||||
* (on nginx + php-fpm it appears that it is available, though, at least) so
|
* (on nginx + php-fpm it appears that it is available, though, at least) so
|
||||||
* any attempt to generate $_POST would be fragile.
|
* any attempt to generate $_POST would be fragile.
|
||||||
|
*
|
||||||
|
* @phutil-external-symbol class PhabricatorStartup
|
||||||
*/
|
*/
|
||||||
private function detectPostMaxSizeTriggered() {
|
private function detectPostMaxSizeTriggered() {
|
||||||
// If this wasn't a POST, we're fine.
|
// If this wasn't a POST, we're fine.
|
||||||
|
@ -83,7 +85,7 @@ final class PhabricatorRequestOverseer {
|
||||||
// populated into $_POST, but it wasn't.
|
// populated into $_POST, but it wasn't.
|
||||||
|
|
||||||
$config = ini_get('post_max_size');
|
$config = ini_get('post_max_size');
|
||||||
$this->fatal(
|
PhabricatorStartup::didFatal(
|
||||||
"As received by the server, this request had a nonzero content length ".
|
"As received by the server, this request had a nonzero content length ".
|
||||||
"but no POST data.\n\n".
|
"but no POST data.\n\n".
|
||||||
"Normally, this indicates that it exceeds the 'post_max_size' setting ".
|
"Normally, this indicates that it exceeds the 'post_max_size' setting ".
|
||||||
|
@ -93,14 +95,4 @@ final class PhabricatorRequestOverseer {
|
||||||
"'post_max_size' is set to '{$config}'.");
|
"'post_max_size' is set to '{$config}'.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Defined in webroot/index.php.
|
|
||||||
* TODO: Move here.
|
|
||||||
*
|
|
||||||
* @phutil-external-symbol function phabricator_fatal
|
|
||||||
*/
|
|
||||||
public function fatal($message) {
|
|
||||||
phabricator_fatal('FATAL ERROR: '.$message);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
222
support/PhabricatorStartup.php
Normal file
222
support/PhabricatorStartup.php
Normal file
|
@ -0,0 +1,222 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle request startup, before loading the environment or libraries. This
|
||||||
|
* class bootstraps the request state up to the point where we can enter
|
||||||
|
* Phabricator code.
|
||||||
|
*
|
||||||
|
* NOTE: This class MUST NOT have any dependencies. It runs before libraries
|
||||||
|
* load.
|
||||||
|
*
|
||||||
|
* @task info Accessing Request Information
|
||||||
|
* @task hook Startup Hooks
|
||||||
|
* @task apocalypse In Case Of Apocalypse
|
||||||
|
* @task validation Validation
|
||||||
|
*/
|
||||||
|
final class PhabricatorStartup {
|
||||||
|
|
||||||
|
private static $startTime;
|
||||||
|
private static $globals = array();
|
||||||
|
|
||||||
|
|
||||||
|
/* -( Accessing Request Information )-------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @task info
|
||||||
|
*/
|
||||||
|
public static function getStartTime() {
|
||||||
|
return self::$startTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @task info
|
||||||
|
*/
|
||||||
|
public static function setGlobal($key, $value) {
|
||||||
|
self::validateGlobal($key);
|
||||||
|
|
||||||
|
self::$globals[$key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @task info
|
||||||
|
*/
|
||||||
|
public static function getGlobal($key, $default = null) {
|
||||||
|
self::validateGlobal($key);
|
||||||
|
|
||||||
|
if (!array_key_exists($key, self::$globals)) {
|
||||||
|
return $default;
|
||||||
|
}
|
||||||
|
return self::$globals[$key];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( Startup Hooks )------------------------------------------------------ */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @task hook
|
||||||
|
*/
|
||||||
|
public static function didStartup() {
|
||||||
|
self::$startTime = microtime(true);
|
||||||
|
self::$globals = array();
|
||||||
|
|
||||||
|
self::setupPHP();
|
||||||
|
self::verifyPHP();
|
||||||
|
|
||||||
|
self::verifyRewriteRules();
|
||||||
|
|
||||||
|
static $registered;
|
||||||
|
if (!$registered) {
|
||||||
|
// NOTE: This protects us against multiple calls to didStartup() in the
|
||||||
|
// same request, but also against repeated requests to the same
|
||||||
|
// interpreter state, which we may implement in the future.
|
||||||
|
register_shutdown_function(array(__CLASS__, 'didShutdown'));
|
||||||
|
$registered = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @task hook
|
||||||
|
*/
|
||||||
|
public static function didShutdown() {
|
||||||
|
$event = error_get_last();
|
||||||
|
|
||||||
|
if (!$event) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($event['type']) {
|
||||||
|
case E_ERROR:
|
||||||
|
case E_PARSE:
|
||||||
|
case E_COMPILE_ERROR:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$msg = ">>> UNRECOVERABLE FATAL ERROR <<<\n\n";
|
||||||
|
if ($event) {
|
||||||
|
// Even though we should be emitting this as text-plain, escape things
|
||||||
|
// just to be sure since we can't really be sure what the program state
|
||||||
|
// is when we get here.
|
||||||
|
$msg .= htmlspecialchars(
|
||||||
|
$event['message']."\n\n".$event['file'].':'.$event['line'],
|
||||||
|
ENT_QUOTES,
|
||||||
|
'UTF-8');
|
||||||
|
}
|
||||||
|
|
||||||
|
// flip dem tables
|
||||||
|
$msg .= "\n\n\n";
|
||||||
|
$msg .= "\xe2\x94\xbb\xe2\x94\x81\xe2\x94\xbb\x20\xef\xb8\xb5\x20\xc2\xaf".
|
||||||
|
"\x5c\x5f\x28\xe3\x83\x84\x29\x5f\x2f\xc2\xaf\x20\xef\xb8\xb5\x20".
|
||||||
|
"\xe2\x94\xbb\xe2\x94\x81\xe2\x94\xbb";
|
||||||
|
|
||||||
|
self::didFatal($msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( In Case of Apocalypse )---------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @task apocalypse
|
||||||
|
*/
|
||||||
|
public static function didFatal($message) {
|
||||||
|
$access_log = self::getGlobal('log.access');
|
||||||
|
|
||||||
|
if ($access_log) {
|
||||||
|
try {
|
||||||
|
$access_log->setData(
|
||||||
|
array(
|
||||||
|
'c' => 500,
|
||||||
|
));
|
||||||
|
$access_log->write();
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
$message .= "\n(Moreover, unable to write to access log.)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
header(
|
||||||
|
'Content-Type: text/plain; charset=utf-8',
|
||||||
|
$replace = true,
|
||||||
|
$http_error = 500);
|
||||||
|
|
||||||
|
error_log($message);
|
||||||
|
echo $message;
|
||||||
|
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( Validation )--------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @task valiation
|
||||||
|
*/
|
||||||
|
private static function setupPHP() {
|
||||||
|
error_reporting(E_ALL | E_STRICT);
|
||||||
|
ini_set('memory_limit', -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @task valiation
|
||||||
|
*/
|
||||||
|
private static function verifyPHP() {
|
||||||
|
$required_version = '5.2.3';
|
||||||
|
if (version_compare(PHP_VERSION, $required_version) < 0) {
|
||||||
|
self::didFatal(
|
||||||
|
"You are running PHP version '".PHP_VERSION."', which is older than ".
|
||||||
|
"the minimum version, '{$required_version}'. Update to at least ".
|
||||||
|
"'{$required_version}'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (get_magic_quotes_gpc()) {
|
||||||
|
self::didFatal(
|
||||||
|
"Your server is configured with PHP 'magic_quotes_gpc' enabled. This ".
|
||||||
|
"feature is 'highly discouraged' by PHP's developers and you must ".
|
||||||
|
"disable it to run Phabricator. Consult the PHP manual for ".
|
||||||
|
"instructions.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @task valiation
|
||||||
|
*/
|
||||||
|
private static function verifyRewriteRules() {
|
||||||
|
if (isset($_REQUEST['__path__'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (php_sapi_name() == 'cli-server') {
|
||||||
|
// Compatibility with PHP 5.4+ built-in web server.
|
||||||
|
$url = parse_url($_SERVER['REQUEST_URI']);
|
||||||
|
$_REQUEST['__path__'] = $url['path'];
|
||||||
|
} else {
|
||||||
|
self::didFatal(
|
||||||
|
"Request parameter '__path__' is not set. Your rewrite rules ".
|
||||||
|
"are not configured correctly.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @task valiation
|
||||||
|
*/
|
||||||
|
private static function validateGlobal($key) {
|
||||||
|
static $globals = array(
|
||||||
|
'log.access' => true,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (empty($globals[$key])) {
|
||||||
|
throw new Exception("Access to unknown startup global '{$key}'!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,20 +1,10 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
$__start__ = microtime(true);
|
require_once dirname(dirname(__FILE__)).'/support/PhabricatorStartup.php';
|
||||||
|
PhabricatorStartup::didStartup();
|
||||||
|
|
||||||
$access_log = null;
|
$access_log = null;
|
||||||
|
|
||||||
error_reporting(E_ALL | E_STRICT);
|
|
||||||
|
|
||||||
$required_version = '5.2.3';
|
|
||||||
if (version_compare(PHP_VERSION, $required_version) < 0) {
|
|
||||||
phabricator_fatal_config_error(
|
|
||||||
"You are running PHP version '".PHP_VERSION."', which is older than ".
|
|
||||||
"the minimum version, '{$required_version}'. Update to at least ".
|
|
||||||
"'{$required_version}'.");
|
|
||||||
}
|
|
||||||
|
|
||||||
ini_set('memory_limit', -1);
|
|
||||||
|
|
||||||
$env = getenv('PHABRICATOR_ENV'); // Apache
|
$env = getenv('PHABRICATOR_ENV'); // Apache
|
||||||
if (!$env) {
|
if (!$env) {
|
||||||
if (isset($_ENV['PHABRICATOR_ENV'])) {
|
if (isset($_ENV['PHABRICATOR_ENV'])) {
|
||||||
|
@ -23,31 +13,12 @@ if (!$env) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$env) {
|
if (!$env) {
|
||||||
phabricator_fatal_config_error(
|
PhabricatorStartup::didFatal(
|
||||||
"The 'PHABRICATOR_ENV' environmental variable is not defined. Modify ".
|
"The 'PHABRICATOR_ENV' environmental variable is not defined. Modify ".
|
||||||
"your httpd.conf to include 'SetEnv PHABRICATOR_ENV <env>', where '<env>' ".
|
"your httpd.conf to include 'SetEnv PHABRICATOR_ENV <env>', where '<env>' ".
|
||||||
"is one of 'development', 'production', or a custom environment.");
|
"is one of 'development', 'production', or a custom environment.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($_REQUEST['__path__'])) {
|
|
||||||
if (php_sapi_name() == 'cli-server') {
|
|
||||||
// Compatibility with PHP 5.4+ built-in web server.
|
|
||||||
$url = parse_url($_SERVER['REQUEST_URI']);
|
|
||||||
$_REQUEST['__path__'] = $url['path'];
|
|
||||||
} else {
|
|
||||||
phabricator_fatal_config_error(
|
|
||||||
"__path__ is not set. Your rewrite rules are not configured correctly.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (get_magic_quotes_gpc()) {
|
|
||||||
phabricator_fatal_config_error(
|
|
||||||
"Your server is configured with PHP 'magic_quotes_gpc' enabled. This ".
|
|
||||||
"feature is 'highly discouraged' by PHP's developers and you must ".
|
|
||||||
"disable it to run Phabricator. Consult the PHP manual for instructions.");
|
|
||||||
}
|
|
||||||
|
|
||||||
register_shutdown_function('phabricator_shutdown');
|
|
||||||
|
|
||||||
require_once dirname(dirname(__FILE__)).'/conf/__init_conf__.php';
|
require_once dirname(dirname(__FILE__)).'/conf/__init_conf__.php';
|
||||||
|
|
||||||
|
@ -81,6 +52,7 @@ try {
|
||||||
PhabricatorAccessLog::init();
|
PhabricatorAccessLog::init();
|
||||||
$access_log = PhabricatorAccessLog::getLog();
|
$access_log = PhabricatorAccessLog::getLog();
|
||||||
if ($access_log) {
|
if ($access_log) {
|
||||||
|
PhabricatorStartup::setGlobal('log.access', $access_log);
|
||||||
$access_log->setData(
|
$access_log->setData(
|
||||||
array(
|
array(
|
||||||
'R' => idx($_SERVER, 'HTTP_REFERER', '-'),
|
'R' => idx($_SERVER, 'HTTP_REFERER', '-'),
|
||||||
|
@ -193,7 +165,7 @@ try {
|
||||||
$ex,
|
$ex,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
phabricator_fatal('[Rendering Exception] '.$ex->getMessage());
|
PhabricatorStartup::didFatal('[Rendering Exception] '.$ex->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
$write_guard->dispose();
|
$write_guard->dispose();
|
||||||
|
@ -211,10 +183,11 @@ try {
|
||||||
$sink->writeData($response_string);
|
$sink->writeData($response_string);
|
||||||
|
|
||||||
if ($access_log) {
|
if ($access_log) {
|
||||||
|
$request_start = PhabricatorStartup::getStartTime();
|
||||||
$access_log->setData(
|
$access_log->setData(
|
||||||
array(
|
array(
|
||||||
'c' => $response->getHTTPResponseCode(),
|
'c' => $response->getHTTPResponseCode(),
|
||||||
'T' => (int)(1000000 * (microtime(true) - $__start__)),
|
'T' => (int)(1000000 * (microtime(true) - $request_start)),
|
||||||
));
|
));
|
||||||
$access_log->write();
|
$access_log->write();
|
||||||
}
|
}
|
||||||
|
@ -240,7 +213,7 @@ try {
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (Exception $ex) {
|
} catch (Exception $ex) {
|
||||||
phabricator_fatal("[Exception] ".$ex->getMessage());
|
PhabricatorStartup::didFatal("[Exception] ".$ex->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -275,10 +248,6 @@ function setup_aphront_basics() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function phabricator_fatal_config_error($msg) {
|
|
||||||
phabricator_fatal("CONFIG ERROR: ".$msg."\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
function phabricator_detect_bad_base_uri() {
|
function phabricator_detect_bad_base_uri() {
|
||||||
$conf = PhabricatorEnv::getEnvConfig('phabricator.base-uri');
|
$conf = PhabricatorEnv::getEnvConfig('phabricator.base-uri');
|
||||||
$uri = new PhutilURI($conf);
|
$uri = new PhutilURI($conf);
|
||||||
|
@ -287,13 +256,14 @@ function phabricator_detect_bad_base_uri() {
|
||||||
case 'https':
|
case 'https':
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return phabricator_fatal_config_error(
|
PhabricatorStartup::didFatal(
|
||||||
"'phabricator.base-uri' is set to '{$conf}', which is invalid. ".
|
"'phabricator.base-uri' is set to '{$conf}', which is invalid. ".
|
||||||
"The URI must start with 'http://' or 'https://'.");
|
"The URI must start with 'http://' or 'https://'.");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strpos($uri->getDomain(), '.') === false) {
|
if (strpos($uri->getDomain(), '.') === false) {
|
||||||
phabricator_fatal_config_error(
|
PhabricatorStartup::didFatal(
|
||||||
"'phabricator.base-uri' is set to '{$conf}', which is invalid. The URI ".
|
"'phabricator.base-uri' is set to '{$conf}', which is invalid. The URI ".
|
||||||
"must contain a dot ('.'), like 'http://example.com/', not just ".
|
"must contain a dot ('.'), like 'http://example.com/', not just ".
|
||||||
"'http://example/'. Some web browsers will not set cookies on domains ".
|
"'http://example/'. Some web browsers will not set cookies on domains ".
|
||||||
|
@ -304,63 +274,3 @@ function phabricator_detect_bad_base_uri() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function phabricator_shutdown() {
|
|
||||||
$event = error_get_last();
|
|
||||||
|
|
||||||
if (!$event) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ($event['type']) {
|
|
||||||
case E_ERROR:
|
|
||||||
case E_PARSE:
|
|
||||||
case E_COMPILE_ERROR:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$msg = ">>> UNRECOVERABLE FATAL ERROR <<<\n\n";
|
|
||||||
if ($event) {
|
|
||||||
// Even though we should be emitting this as text-plain, escape things just
|
|
||||||
// to be sure since we can't really be sure what the program state is when
|
|
||||||
// we get here.
|
|
||||||
$msg .= phutil_escape_html($event['message'])."\n\n";
|
|
||||||
$msg .= phutil_escape_html($event['file'].':'.$event['line']);
|
|
||||||
}
|
|
||||||
|
|
||||||
// flip dem tables
|
|
||||||
$msg .= "\n\n\n";
|
|
||||||
$msg .= "\xe2\x94\xbb\xe2\x94\x81\xe2\x94\xbb\x20\xef\xb8\xb5\x20\xc2\xaf".
|
|
||||||
"\x5c\x5f\x28\xe3\x83\x84\x29\x5f\x2f\xc2\xaf\x20\xef\xb8\xb5\x20".
|
|
||||||
"\xe2\x94\xbb\xe2\x94\x81\xe2\x94\xbb";
|
|
||||||
|
|
||||||
phabricator_fatal($msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
function phabricator_fatal($msg) {
|
|
||||||
|
|
||||||
global $access_log;
|
|
||||||
if ($access_log) {
|
|
||||||
try {
|
|
||||||
$access_log->setData(
|
|
||||||
array(
|
|
||||||
'c' => 500,
|
|
||||||
));
|
|
||||||
$access_log->write();
|
|
||||||
} catch (Exception $ex) {
|
|
||||||
$msg .= "\nMoreover unable to write to access log.";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
header(
|
|
||||||
'Content-Type: text/plain; charset=utf-8',
|
|
||||||
$replace = true,
|
|
||||||
$http_error = 500);
|
|
||||||
|
|
||||||
error_log($msg);
|
|
||||||
echo $msg;
|
|
||||||
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue