Skip to content

Commit a24e6de

Browse files
author
epriestley
committedJan 30, 2019
Read "$_POST" before hooking the profiler, and remove "aphront.default-application-configuration-class"
Summary: Ref T4369. Ref T12297. Ref T13242. Ref PHI1010. I want to take a quick look at `transaction.search` and see if there's anything quick and obvious we can do to improve performance. On `secure`, the `__profile__` flag does not survive POST like it's supposed to: when you profile a page and then submit a form on the page, the result is supposed to be profiled. The intent is to make it easier to profile Conduit calls. I believe this is because we're hooking the profiler, then rebuilding POST data a little later -- so `$_POST['__profile__']` isn't set yet when the profiler checks. Move the POST rebuild a little earlier to fix this. Also, remove the very ancient "aphront.default-application-configuration-class". I believe this was only used by Facebook to do CIDR checks against corpnet or something like that. It is poorly named and long-obsolete now, and `AphrontSite` does everything we might reasonably have wanted it to do. Test Plan: Poked around locally without any issues. Will check if this fixes the issue on `secure`. Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13242, T12297, T4369 Differential Revision: https://secure.phabricator.com/D20046
1 parent 70b474e commit a24e6de

6 files changed

+129
-154
lines changed
 

‎src/__phutil_library_map__.php

-2
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,6 @@
183183
'AphrontCalendarEventView' => 'applications/calendar/view/AphrontCalendarEventView.php',
184184
'AphrontController' => 'aphront/AphrontController.php',
185185
'AphrontCursorPagerView' => 'view/control/AphrontCursorPagerView.php',
186-
'AphrontDefaultApplicationConfiguration' => 'aphront/configuration/AphrontDefaultApplicationConfiguration.php',
187186
'AphrontDialogResponse' => 'aphront/response/AphrontDialogResponse.php',
188187
'AphrontDialogView' => 'view/AphrontDialogView.php',
189188
'AphrontEpochHTTPParameterType' => 'aphront/httpparametertype/AphrontEpochHTTPParameterType.php',
@@ -5631,7 +5630,6 @@
56315630
'AphrontCalendarEventView' => 'AphrontView',
56325631
'AphrontController' => 'Phobject',
56335632
'AphrontCursorPagerView' => 'AphrontView',
5634-
'AphrontDefaultApplicationConfiguration' => 'AphrontApplicationConfiguration',
56355633
'AphrontDialogResponse' => 'AphrontResponse',
56365634
'AphrontDialogView' => array(
56375635
'AphrontView',

‎src/aphront/configuration/AphrontApplicationConfiguration.php

+124-22
Original file line numberDiff line numberDiff line change
@@ -5,55 +5,81 @@
55
* @task response Response Handling
66
* @task exception Exception Handling
77
*/
8-
abstract class AphrontApplicationConfiguration extends Phobject {
8+
final class AphrontApplicationConfiguration
9+
extends Phobject {
910

1011
private $request;
1112
private $host;
1213
private $path;
1314
private $console;
1415

15-
abstract public function buildRequest();
16-
abstract public function build404Controller();
17-
abstract public function buildRedirectController($uri, $external);
16+
public function buildRequest() {
17+
$parser = new PhutilQueryStringParser();
1818

19-
final public function setRequest(AphrontRequest $request) {
19+
$data = array();
20+
$data += $_POST;
21+
$data += $parser->parseQueryString(idx($_SERVER, 'QUERY_STRING', ''));
22+
23+
$cookie_prefix = PhabricatorEnv::getEnvConfig('phabricator.cookie-prefix');
24+
25+
$request = new AphrontRequest($this->getHost(), $this->getPath());
26+
$request->setRequestData($data);
27+
$request->setApplicationConfiguration($this);
28+
$request->setCookiePrefix($cookie_prefix);
29+
30+
return $request;
31+
}
32+
33+
public function build404Controller() {
34+
return array(new Phabricator404Controller(), array());
35+
}
36+
37+
public function buildRedirectController($uri, $external) {
38+
return array(
39+
new PhabricatorRedirectController(),
40+
array(
41+
'uri' => $uri,
42+
'external' => $external,
43+
),
44+
);
45+
}
46+
47+
public function setRequest(AphrontRequest $request) {
2048
$this->request = $request;
2149
return $this;
2250
}
2351

24-
final public function getRequest() {
52+
public function getRequest() {
2553
return $this->request;
2654
}
2755

28-
final public function getConsole() {
56+
public function getConsole() {
2957
return $this->console;
3058
}
3159

32-
final public function setConsole($console) {
60+
public function setConsole($console) {
3361
$this->console = $console;
3462
return $this;
3563
}
3664

37-
final public function setHost($host) {
65+
public function setHost($host) {
3866
$this->host = $host;
3967
return $this;
4068
}
4169

42-
final public function getHost() {
70+
public function getHost() {
4371
return $this->host;
4472
}
4573

46-
final public function setPath($path) {
74+
public function setPath($path) {
4775
$this->path = $path;
4876
return $this;
4977
}
5078

51-
final public function getPath() {
79+
public function getPath() {
5280
return $this->path;
5381
}
5482

55-
public function willBuildRequest() {}
56-
5783

5884
/**
5985
* @phutil-external-symbol class PhabricatorStartup
@@ -126,6 +152,8 @@ public static function runHTTPRequest(AphrontHTTPSink $sink) {
126152
'M' => idx($_SERVER, 'REQUEST_METHOD', '-'),
127153
));
128154

155+
self::readHTTPPOSTData();
156+
129157
DarkConsoleXHProfPluginAPI::hookProfiler();
130158

131159
// We just activated the profiler, so we don't need to keep track of
@@ -142,16 +170,10 @@ public static function runHTTPRequest(AphrontHTTPSink $sink) {
142170
$host = AphrontRequest::getHTTPHeader('Host');
143171
$path = $_REQUEST['__path__'];
144172

145-
switch ($host) {
146-
default:
147-
$config_key = 'aphront.default-application-configuration-class';
148-
$application = PhabricatorEnv::newObjectFromConfig($config_key);
149-
break;
150-
}
173+
$application = new self();
151174

152175
$application->setHost($host);
153176
$application->setPath($path);
154-
$application->willBuildRequest();
155177
$request = $application->buildRequest();
156178

157179
// Now that we have a request, convert the write guard into one which
@@ -313,7 +335,7 @@ private static function writeResponse(
313335
* parameters.
314336
* @task routing
315337
*/
316-
final private function buildController() {
338+
private function buildController() {
317339
$request = $this->getRequest();
318340

319341
// If we're configured to operate in cluster mode, reject requests which
@@ -708,4 +730,84 @@ private static function newSelfCheckResponse() {
708730
->setContent($result);
709731
}
710732

733+
private static function readHTTPPOSTData() {
734+
$request_method = idx($_SERVER, 'REQUEST_METHOD');
735+
if ($request_method === 'PUT') {
736+
// For PUT requests, do nothing: in particular, do NOT read input. This
737+
// allows us to stream input later and process very large PUT requests,
738+
// like those coming from Git LFS.
739+
return;
740+
}
741+
742+
743+
// For POST requests, we're going to read the raw input ourselves here
744+
// if we can. Among other things, this corrects variable names with
745+
// the "." character in them, which PHP normally converts into "_".
746+
747+
// There are two major considerations here: whether the
748+
// `enable_post_data_reading` option is set, and whether the content
749+
// type is "multipart/form-data" or not.
750+
751+
// If `enable_post_data_reading` is off, we're free to read the entire
752+
// raw request body and parse it -- and we must, because $_POST and
753+
// $_FILES are not built for us. If `enable_post_data_reading` is on,
754+
// which is the default, we may not be able to read the body (the
755+
// documentation says we can't, but empirically we can at least some
756+
// of the time).
757+
758+
// If the content type is "multipart/form-data", we need to build both
759+
// $_POST and $_FILES, which is involved. The body itself is also more
760+
// difficult to parse than other requests.
761+
$raw_input = PhabricatorStartup::getRawInput();
762+
$parser = new PhutilQueryStringParser();
763+
764+
if (strlen($raw_input)) {
765+
$content_type = idx($_SERVER, 'CONTENT_TYPE');
766+
$is_multipart = preg_match('@^multipart/form-data@i', $content_type);
767+
if ($is_multipart && !ini_get('enable_post_data_reading')) {
768+
$multipart_parser = id(new AphrontMultipartParser())
769+
->setContentType($content_type);
770+
771+
$multipart_parser->beginParse();
772+
$multipart_parser->continueParse($raw_input);
773+
$parts = $multipart_parser->endParse();
774+
775+
$query_string = array();
776+
foreach ($parts as $part) {
777+
if (!$part->isVariable()) {
778+
continue;
779+
}
780+
781+
$name = $part->getName();
782+
$value = $part->getVariableValue();
783+
784+
$query_string[] = urlencode($name).'='.urlencode($value);
785+
}
786+
$query_string = implode('&', $query_string);
787+
$post = $parser->parseQueryString($query_string);
788+
789+
$files = array();
790+
foreach ($parts as $part) {
791+
if ($part->isVariable()) {
792+
continue;
793+
}
794+
795+
$files[$part->getName()] = $part->getPHPFileDictionary();
796+
}
797+
$_FILES = $files;
798+
} else {
799+
$post = $parser->parseQueryString($raw_input);
800+
}
801+
802+
$_POST = $post;
803+
PhabricatorStartup::rebuildRequest();
804+
} else if ($_POST) {
805+
$post = filter_input_array(INPUT_POST, FILTER_UNSAFE_RAW);
806+
if (is_array($post)) {
807+
$_POST = $post;
808+
PhabricatorStartup::rebuildRequest();
809+
}
810+
}
811+
}
812+
711813
}

‎src/aphront/configuration/AphrontDefaultApplicationConfiguration.php

-121
This file was deleted.

‎src/applications/base/controller/__tests__/PhabricatorAccessControlTestCase.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public function testControllerAccessControls() {
1212
$root = dirname(phutil_get_library_root('phabricator'));
1313
require_once $root.'/support/startup/PhabricatorStartup.php';
1414

15-
$application_configuration = new AphrontDefaultApplicationConfiguration();
15+
$application_configuration = new AphrontApplicationConfiguration();
1616

1717
$host = 'meow.example.com';
1818

‎src/applications/config/check/PhabricatorExtraConfigSetupCheck.php

+4
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,10 @@ public static function getAncientConfig() {
416416
'metamta.pholio.subject-prefix' => $prefix_reason,
417417
'metamta.phriction.subject-prefix' => $prefix_reason,
418418

419+
'aphront.default-application-configuration-class' => pht(
420+
'This ancient extension point has been replaced with other '.
421+
'mechanisms, including "AphrontSite".'),
422+
419423
);
420424

421425
return $ancient_config;

‎src/applications/config/option/PhabricatorExtendingPhabricatorConfigOptions.php

-8
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,6 @@ public function getOptions() {
3636
'occur. Specify a list of classes which extend '.
3737
'PhabricatorEventListener here.'))
3838
->addExample('MyEventListener', pht('Valid Setting')),
39-
$this->newOption(
40-
'aphront.default-application-configuration-class',
41-
'class',
42-
'AphrontDefaultApplicationConfiguration')
43-
->setLocked(true)
44-
->setBaseClass('AphrontApplicationConfiguration')
45-
// TODO: This could probably use some better documentation.
46-
->setDescription(pht('Application configuration class.')),
4739
);
4840
}
4941

0 commit comments

Comments
 (0)
Failed to load comments.