mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-28 00:10:57 +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.';
|
||||
}
|
||||
|
||||
/**
|
||||
* @phutil-external-symbol class PhabricatorStartup
|
||||
*/
|
||||
public function generateData() {
|
||||
|
||||
$log = PhutilServiceProfiler::getInstance()->getServiceCallLog();
|
||||
|
@ -130,7 +133,7 @@ final class DarkConsoleServicesPlugin extends DarkConsolePlugin {
|
|||
}
|
||||
|
||||
return array(
|
||||
'start' => $GLOBALS['__start__'],
|
||||
'start' => PhabricatorStartup::getStartTime(),
|
||||
'end' => microtime(true),
|
||||
'log' => $log,
|
||||
);
|
||||
|
|
|
@ -19,6 +19,8 @@ final class PhabricatorRequestOverseer {
|
|||
* 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
|
||||
* any attempt to generate $_POST would be fragile.
|
||||
*
|
||||
* @phutil-external-symbol class PhabricatorStartup
|
||||
*/
|
||||
private function detectPostMaxSizeTriggered() {
|
||||
// If this wasn't a POST, we're fine.
|
||||
|
@ -83,7 +85,7 @@ final class PhabricatorRequestOverseer {
|
|||
// populated into $_POST, but it wasn't.
|
||||
|
||||
$config = ini_get('post_max_size');
|
||||
$this->fatal(
|
||||
PhabricatorStartup::didFatal(
|
||||
"As received by the server, this request had a nonzero content length ".
|
||||
"but no POST data.\n\n".
|
||||
"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}'.");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
$__start__ = microtime(true);
|
||||
require_once dirname(dirname(__FILE__)).'/support/PhabricatorStartup.php';
|
||||
PhabricatorStartup::didStartup();
|
||||
|
||||
$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
|
||||
if (!$env) {
|
||||
if (isset($_ENV['PHABRICATOR_ENV'])) {
|
||||
|
@ -23,31 +13,12 @@ if (!$env) {
|
|||
}
|
||||
|
||||
if (!$env) {
|
||||
phabricator_fatal_config_error(
|
||||
PhabricatorStartup::didFatal(
|
||||
"The 'PHABRICATOR_ENV' environmental variable is not defined. Modify ".
|
||||
"your httpd.conf to include 'SetEnv PHABRICATOR_ENV <env>', where '<env>' ".
|
||||
"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';
|
||||
|
||||
|
@ -81,6 +52,7 @@ try {
|
|||
PhabricatorAccessLog::init();
|
||||
$access_log = PhabricatorAccessLog::getLog();
|
||||
if ($access_log) {
|
||||
PhabricatorStartup::setGlobal('log.access', $access_log);
|
||||
$access_log->setData(
|
||||
array(
|
||||
'R' => idx($_SERVER, 'HTTP_REFERER', '-'),
|
||||
|
@ -193,7 +165,7 @@ try {
|
|||
$ex,
|
||||
));
|
||||
}
|
||||
phabricator_fatal('[Rendering Exception] '.$ex->getMessage());
|
||||
PhabricatorStartup::didFatal('[Rendering Exception] '.$ex->getMessage());
|
||||
}
|
||||
|
||||
$write_guard->dispose();
|
||||
|
@ -211,10 +183,11 @@ try {
|
|||
$sink->writeData($response_string);
|
||||
|
||||
if ($access_log) {
|
||||
$request_start = PhabricatorStartup::getStartTime();
|
||||
$access_log->setData(
|
||||
array(
|
||||
'c' => $response->getHTTPResponseCode(),
|
||||
'T' => (int)(1000000 * (microtime(true) - $__start__)),
|
||||
'T' => (int)(1000000 * (microtime(true) - $request_start)),
|
||||
));
|
||||
$access_log->write();
|
||||
}
|
||||
|
@ -240,7 +213,7 @@ try {
|
|||
}
|
||||
|
||||
} 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() {
|
||||
$conf = PhabricatorEnv::getEnvConfig('phabricator.base-uri');
|
||||
$uri = new PhutilURI($conf);
|
||||
|
@ -287,13 +256,14 @@ function phabricator_detect_bad_base_uri() {
|
|||
case 'https':
|
||||
break;
|
||||
default:
|
||||
return phabricator_fatal_config_error(
|
||||
PhabricatorStartup::didFatal(
|
||||
"'phabricator.base-uri' is set to '{$conf}', which is invalid. ".
|
||||
"The URI must start with 'http://' or 'https://'.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (strpos($uri->getDomain(), '.') === false) {
|
||||
phabricator_fatal_config_error(
|
||||
PhabricatorStartup::didFatal(
|
||||
"'phabricator.base-uri' is set to '{$conf}', which is invalid. The URI ".
|
||||
"must contain a dot ('.'), like 'http://example.com/', not just ".
|
||||
"'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