Changeset View
Changeset View
Standalone View
Standalone View
src/aphront/configuration/AphrontApplicationConfiguration.php
<?php | <?php | ||||
/** | /** | ||||
* @task routing URI Routing | * @task routing URI Routing | ||||
* @task response Response Handling | * @task response Response Handling | ||||
* @task exception Exception Handling | * @task exception Exception Handling | ||||
*/ | */ | ||||
abstract class AphrontApplicationConfiguration extends Phobject { | final class AphrontApplicationConfiguration | ||||
extends Phobject { | |||||
private $request; | private $request; | ||||
private $host; | private $host; | ||||
private $path; | private $path; | ||||
private $console; | private $console; | ||||
abstract public function buildRequest(); | public function buildRequest() { | ||||
abstract public function build404Controller(); | $parser = new PhutilQueryStringParser(); | ||||
abstract public function buildRedirectController($uri, $external); | |||||
final public function setRequest(AphrontRequest $request) { | $data = array(); | ||||
$data += $_POST; | |||||
$data += $parser->parseQueryString(idx($_SERVER, 'QUERY_STRING', '')); | |||||
$cookie_prefix = PhabricatorEnv::getEnvConfig('phabricator.cookie-prefix'); | |||||
$request = new AphrontRequest($this->getHost(), $this->getPath()); | |||||
$request->setRequestData($data); | |||||
$request->setApplicationConfiguration($this); | |||||
$request->setCookiePrefix($cookie_prefix); | |||||
return $request; | |||||
} | |||||
public function build404Controller() { | |||||
return array(new Phabricator404Controller(), array()); | |||||
} | |||||
public function buildRedirectController($uri, $external) { | |||||
return array( | |||||
new PhabricatorRedirectController(), | |||||
array( | |||||
'uri' => $uri, | |||||
'external' => $external, | |||||
), | |||||
); | |||||
} | |||||
public function setRequest(AphrontRequest $request) { | |||||
$this->request = $request; | $this->request = $request; | ||||
return $this; | return $this; | ||||
} | } | ||||
final public function getRequest() { | public function getRequest() { | ||||
return $this->request; | return $this->request; | ||||
} | } | ||||
final public function getConsole() { | public function getConsole() { | ||||
return $this->console; | return $this->console; | ||||
} | } | ||||
final public function setConsole($console) { | public function setConsole($console) { | ||||
$this->console = $console; | $this->console = $console; | ||||
return $this; | return $this; | ||||
} | } | ||||
final public function setHost($host) { | public function setHost($host) { | ||||
$this->host = $host; | $this->host = $host; | ||||
return $this; | return $this; | ||||
} | } | ||||
final public function getHost() { | public function getHost() { | ||||
return $this->host; | return $this->host; | ||||
} | } | ||||
final public function setPath($path) { | public function setPath($path) { | ||||
$this->path = $path; | $this->path = $path; | ||||
return $this; | return $this; | ||||
} | } | ||||
final public function getPath() { | public function getPath() { | ||||
return $this->path; | return $this->path; | ||||
} | } | ||||
public function willBuildRequest() {} | |||||
/** | /** | ||||
* @phutil-external-symbol class PhabricatorStartup | * @phutil-external-symbol class PhabricatorStartup | ||||
*/ | */ | ||||
public static function runHTTPRequest(AphrontHTTPSink $sink) { | public static function runHTTPRequest(AphrontHTTPSink $sink) { | ||||
if (isset($_SERVER['HTTP_X_PHABRICATOR_SELFCHECK'])) { | if (isset($_SERVER['HTTP_X_PHABRICATOR_SELFCHECK'])) { | ||||
$response = self::newSelfCheckResponse(); | $response = self::newSelfCheckResponse(); | ||||
return self::writeResponse($sink, $response); | return self::writeResponse($sink, $response); | ||||
▲ Show 20 Lines • Show All 56 Lines • ▼ Show 20 Lines | public static function runHTTPRequest(AphrontHTTPSink $sink) { | ||||
$access_log->setData( | $access_log->setData( | ||||
array( | array( | ||||
'R' => AphrontRequest::getHTTPHeader('Referer', '-'), | 'R' => AphrontRequest::getHTTPHeader('Referer', '-'), | ||||
'r' => $address_string, | 'r' => $address_string, | ||||
'M' => idx($_SERVER, 'REQUEST_METHOD', '-'), | 'M' => idx($_SERVER, 'REQUEST_METHOD', '-'), | ||||
)); | )); | ||||
self::readHTTPPOSTData(); | |||||
DarkConsoleXHProfPluginAPI::hookProfiler(); | DarkConsoleXHProfPluginAPI::hookProfiler(); | ||||
// We just activated the profiler, so we don't need to keep track of | // We just activated the profiler, so we don't need to keep track of | ||||
// startup phases anymore: it can take over from here. | // startup phases anymore: it can take over from here. | ||||
PhabricatorStartup::beginStartupPhase('startup.done'); | PhabricatorStartup::beginStartupPhase('startup.done'); | ||||
DarkConsoleErrorLogPluginAPI::registerErrorHandler(); | DarkConsoleErrorLogPluginAPI::registerErrorHandler(); | ||||
$response = PhabricatorSetupCheck::willProcessRequest(); | $response = PhabricatorSetupCheck::willProcessRequest(); | ||||
if ($response) { | if ($response) { | ||||
return self::writeResponse($sink, $response); | return self::writeResponse($sink, $response); | ||||
} | } | ||||
$host = AphrontRequest::getHTTPHeader('Host'); | $host = AphrontRequest::getHTTPHeader('Host'); | ||||
$path = $_REQUEST['__path__']; | $path = $_REQUEST['__path__']; | ||||
switch ($host) { | $application = new self(); | ||||
default: | |||||
$config_key = 'aphront.default-application-configuration-class'; | |||||
$application = PhabricatorEnv::newObjectFromConfig($config_key); | |||||
break; | |||||
} | |||||
$application->setHost($host); | $application->setHost($host); | ||||
$application->setPath($path); | $application->setPath($path); | ||||
$application->willBuildRequest(); | |||||
$request = $application->buildRequest(); | $request = $application->buildRequest(); | ||||
// Now that we have a request, convert the write guard into one which | // Now that we have a request, convert the write guard into one which | ||||
// actually checks CSRF tokens. | // actually checks CSRF tokens. | ||||
$write_guard->dispose(); | $write_guard->dispose(); | ||||
$write_guard = new AphrontWriteGuard(array($request, 'validateCSRF')); | $write_guard = new AphrontWriteGuard(array($request, 'validateCSRF')); | ||||
// Build the server URI implied by the request headers. If an administrator | // Build the server URI implied by the request headers. If an administrator | ||||
▲ Show 20 Lines • Show All 145 Lines • ▼ Show 20 Lines | /* -( URI Routing )-------------------------------------------------------- */ | ||||
/** | /** | ||||
* Build a controller to respond to the request. | * Build a controller to respond to the request. | ||||
* | * | ||||
* @return pair<AphrontController,dict> Controller and dictionary of request | * @return pair<AphrontController,dict> Controller and dictionary of request | ||||
* parameters. | * parameters. | ||||
* @task routing | * @task routing | ||||
*/ | */ | ||||
final private function buildController() { | private function buildController() { | ||||
$request = $this->getRequest(); | $request = $this->getRequest(); | ||||
// If we're configured to operate in cluster mode, reject requests which | // If we're configured to operate in cluster mode, reject requests which | ||||
// were not received on a cluster interface. | // were not received on a cluster interface. | ||||
// | // | ||||
// For example, a host may have an internal address like "170.0.0.1", and | // For example, a host may have an internal address like "170.0.0.1", and | ||||
// also have a public address like "51.23.95.16". Assuming the cluster | // also have a public address like "51.23.95.16". Assuming the cluster | ||||
// is configured on a range like "170.0.0.0/16", we want to reject the | // is configured on a range like "170.0.0.0/16", we want to reject the | ||||
▲ Show 20 Lines • Show All 378 Lines • ▼ Show 20 Lines | private static function newSelfCheckResponse() { | ||||
); | ); | ||||
return id(new AphrontJSONResponse()) | return id(new AphrontJSONResponse()) | ||||
->setAddJSONShield(false) | ->setAddJSONShield(false) | ||||
->setContent($result); | ->setContent($result); | ||||
} | } | ||||
private static function readHTTPPOSTData() { | |||||
$request_method = idx($_SERVER, 'REQUEST_METHOD'); | |||||
if ($request_method === 'PUT') { | |||||
// For PUT requests, do nothing: in particular, do NOT read input. This | |||||
// allows us to stream input later and process very large PUT requests, | |||||
// like those coming from Git LFS. | |||||
return; | |||||
} | |||||
// For POST requests, we're going to read the raw input ourselves here | |||||
// if we can. Among other things, this corrects variable names with | |||||
// the "." character in them, which PHP normally converts into "_". | |||||
// There are two major considerations here: whether the | |||||
// `enable_post_data_reading` option is set, and whether the content | |||||
// type is "multipart/form-data" or not. | |||||
// If `enable_post_data_reading` is off, we're free to read the entire | |||||
// raw request body and parse it -- and we must, because $_POST and | |||||
// $_FILES are not built for us. If `enable_post_data_reading` is on, | |||||
// which is the default, we may not be able to read the body (the | |||||
// documentation says we can't, but empirically we can at least some | |||||
// of the time). | |||||
// If the content type is "multipart/form-data", we need to build both | |||||
// $_POST and $_FILES, which is involved. The body itself is also more | |||||
// difficult to parse than other requests. | |||||
$raw_input = PhabricatorStartup::getRawInput(); | |||||
$parser = new PhutilQueryStringParser(); | |||||
if (strlen($raw_input)) { | |||||
$content_type = idx($_SERVER, 'CONTENT_TYPE'); | |||||
$is_multipart = preg_match('@^multipart/form-data@i', $content_type); | |||||
if ($is_multipart && !ini_get('enable_post_data_reading')) { | |||||
$multipart_parser = id(new AphrontMultipartParser()) | |||||
->setContentType($content_type); | |||||
$multipart_parser->beginParse(); | |||||
$multipart_parser->continueParse($raw_input); | |||||
$parts = $multipart_parser->endParse(); | |||||
$query_string = array(); | |||||
foreach ($parts as $part) { | |||||
if (!$part->isVariable()) { | |||||
continue; | |||||
} | |||||
$name = $part->getName(); | |||||
$value = $part->getVariableValue(); | |||||
$query_string[] = urlencode($name).'='.urlencode($value); | |||||
amckinleyUnsubmitted Not Done Inline Actionsamckinley: {F6167357} | |||||
} | |||||
$query_string = implode('&', $query_string); | |||||
$post = $parser->parseQueryString($query_string); | |||||
$files = array(); | |||||
foreach ($parts as $part) { | |||||
if ($part->isVariable()) { | |||||
continue; | |||||
} | |||||
$files[$part->getName()] = $part->getPHPFileDictionary(); | |||||
} | |||||
$_FILES = $files; | |||||
} else { | |||||
$post = $parser->parseQueryString($raw_input); | |||||
} | |||||
$_POST = $post; | |||||
PhabricatorStartup::rebuildRequest(); | |||||
} else if ($_POST) { | |||||
$post = filter_input_array(INPUT_POST, FILTER_UNSAFE_RAW); | |||||
if (is_array($post)) { | |||||
$_POST = $post; | |||||
PhabricatorStartup::rebuildRequest(); | |||||
} | |||||
} | |||||
} | |||||
} | } |