5
5
* @task response Response Handling
6
6
* @task exception Exception Handling
7
7
*/
8
- abstract class AphrontApplicationConfiguration extends Phobject {
8
+ final class AphrontApplicationConfiguration
9
+ extends Phobject {
9
10
10
11
private $ request ;
11
12
private $ host ;
12
13
private $ path ;
13
14
private $ console ;
14
15
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 ();
18
18
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 ) {
20
48
$ this ->request = $ request ;
21
49
return $ this ;
22
50
}
23
51
24
- final public function getRequest () {
52
+ public function getRequest () {
25
53
return $ this ->request ;
26
54
}
27
55
28
- final public function getConsole () {
56
+ public function getConsole () {
29
57
return $ this ->console ;
30
58
}
31
59
32
- final public function setConsole ($ console ) {
60
+ public function setConsole ($ console ) {
33
61
$ this ->console = $ console ;
34
62
return $ this ;
35
63
}
36
64
37
- final public function setHost ($ host ) {
65
+ public function setHost ($ host ) {
38
66
$ this ->host = $ host ;
39
67
return $ this ;
40
68
}
41
69
42
- final public function getHost () {
70
+ public function getHost () {
43
71
return $ this ->host ;
44
72
}
45
73
46
- final public function setPath ($ path ) {
74
+ public function setPath ($ path ) {
47
75
$ this ->path = $ path ;
48
76
return $ this ;
49
77
}
50
78
51
- final public function getPath () {
79
+ public function getPath () {
52
80
return $ this ->path ;
53
81
}
54
82
55
- public function willBuildRequest () {}
56
-
57
83
58
84
/**
59
85
* @phutil-external-symbol class PhabricatorStartup
@@ -126,6 +152,8 @@ public static function runHTTPRequest(AphrontHTTPSink $sink) {
126
152
'M ' => idx ($ _SERVER , 'REQUEST_METHOD ' , '- ' ),
127
153
));
128
154
155
+ self ::readHTTPPOSTData ();
156
+
129
157
DarkConsoleXHProfPluginAPI::hookProfiler ();
130
158
131
159
// We just activated the profiler, so we don't need to keep track of
@@ -142,16 +170,10 @@ public static function runHTTPRequest(AphrontHTTPSink $sink) {
142
170
$ host = AphrontRequest::getHTTPHeader ('Host ' );
143
171
$ path = $ _REQUEST ['__path__ ' ];
144
172
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 ();
151
174
152
175
$ application ->setHost ($ host );
153
176
$ application ->setPath ($ path );
154
- $ application ->willBuildRequest ();
155
177
$ request = $ application ->buildRequest ();
156
178
157
179
// Now that we have a request, convert the write guard into one which
@@ -313,7 +335,7 @@ private static function writeResponse(
313
335
* parameters.
314
336
* @task routing
315
337
*/
316
- final private function buildController () {
338
+ private function buildController () {
317
339
$ request = $ this ->getRequest ();
318
340
319
341
// If we're configured to operate in cluster mode, reject requests which
@@ -708,4 +730,84 @@ private static function newSelfCheckResponse() {
708
730
->setContent ($ result );
709
731
}
710
732
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
+
711
813
}
0 commit comments