]> scripts.mit.edu Git - autoinstalls/mediawiki.git/blob - tests/phpunit/includes/WebRequestTest.php
MediaWiki 1.30.2-scripts2
[autoinstalls/mediawiki.git] / tests / phpunit / includes / WebRequestTest.php
1 <?php
2
3 /**
4  * @group WebRequest
5  */
6 class WebRequestTest extends MediaWikiTestCase {
7         protected $oldServer;
8
9         protected function setUp() {
10                 parent::setUp();
11
12                 $this->oldServer = $_SERVER;
13         }
14
15         protected function tearDown() {
16                 $_SERVER = $this->oldServer;
17
18                 parent::tearDown();
19         }
20
21         /**
22          * @dataProvider provideDetectServer
23          * @covers WebRequest::detectServer
24          * @covers WebRequest::detectProtocol
25          */
26         public function testDetectServer( $expected, $input, $description ) {
27                 $this->setMwGlobals( 'wgAssumeProxiesUseDefaultProtocolPorts', true );
28
29                 $_SERVER = $input;
30                 $result = WebRequest::detectServer();
31                 $this->assertEquals( $expected, $result, $description );
32         }
33
34         public static function provideDetectServer() {
35                 return [
36                         [
37                                 'http://x',
38                                 [
39                                         'HTTP_HOST' => 'x'
40                                 ],
41                                 'Host header'
42                         ],
43                         [
44                                 'https://x',
45                                 [
46                                         'HTTP_HOST' => 'x',
47                                         'HTTPS' => 'on',
48                                 ],
49                                 'Host header with secure'
50                         ],
51                         [
52                                 'http://x',
53                                 [
54                                         'HTTP_HOST' => 'x',
55                                         'SERVER_PORT' => 80,
56                                 ],
57                                 'Default SERVER_PORT',
58                         ],
59                         [
60                                 'http://x',
61                                 [
62                                         'HTTP_HOST' => 'x',
63                                         'HTTPS' => 'off',
64                                 ],
65                                 'Secure off'
66                         ],
67                         [
68                                 'https://x',
69                                 [
70                                         'HTTP_HOST' => 'x',
71                                         'HTTP_X_FORWARDED_PROTO' => 'https',
72                                 ],
73                                 'Forwarded HTTPS'
74                         ],
75                         [
76                                 'https://x',
77                                 [
78                                         'HTTP_HOST' => 'x',
79                                         'HTTPS' => 'off',
80                                         'SERVER_PORT' => '81',
81                                         'HTTP_X_FORWARDED_PROTO' => 'https',
82                                 ],
83                                 'Forwarded HTTPS'
84                         ],
85                         [
86                                 'http://y',
87                                 [
88                                         'SERVER_NAME' => 'y',
89                                 ],
90                                 'Server name'
91                         ],
92                         [
93                                 'http://x',
94                                 [
95                                         'HTTP_HOST' => 'x',
96                                         'SERVER_NAME' => 'y',
97                                 ],
98                                 'Host server name precedence'
99                         ],
100                         [
101                                 'http://[::1]:81',
102                                 [
103                                         'HTTP_HOST' => '[::1]',
104                                         'SERVER_NAME' => '::1',
105                                         'SERVER_PORT' => '81',
106                                 ],
107                                 'Apache bug 26005'
108                         ],
109                         [
110                                 'http://localhost',
111                                 [
112                                         'SERVER_NAME' => '[2001'
113                                 ],
114                                 'Kind of like lighttpd per commit message in MW r83847',
115                         ],
116                         [
117                                 'http://[2a01:e35:2eb4:1::2]:777',
118                                 [
119                                         'SERVER_NAME' => '[2a01:e35:2eb4:1::2]:777'
120                                 ],
121                                 'Possible lighttpd environment per bug 14977 comment 13',
122                         ],
123                 ];
124         }
125
126         protected function mockWebRequest( $data = [] ) {
127                 // Cannot use PHPUnit getMockBuilder() as it does not support
128                 // overriding protected properties afterwards
129                 $reflection = new ReflectionClass( 'WebRequest' );
130                 $req = $reflection->newInstanceWithoutConstructor();
131
132                 $prop = $reflection->getProperty( 'data' );
133                 $prop->setAccessible( true );
134                 $prop->setValue( $req, $data );
135
136                 $prop = $reflection->getProperty( 'requestTime' );
137                 $prop->setAccessible( true );
138                 $prop->setValue( $req, microtime( true ) );
139
140                 return $req;
141         }
142
143         /**
144          * @covers WebRequest::getElapsedTime
145          */
146         public function testGetElapsedTime() {
147                 $req = $this->mockWebRequest();
148                 $this->assertGreaterThanOrEqual( 0.0, $req->getElapsedTime() );
149                 $this->assertEquals( 0.0, $req->getElapsedTime(), '', /*delta*/ 0.2 );
150         }
151
152         /**
153          * @covers WebRequest::getVal
154          * @covers WebRequest::getGPCVal
155          * @covers WebRequest::normalizeUnicode
156          */
157         public function testGetValNormal() {
158                 // Assert that WebRequest normalises GPC data using UtfNormal\Validator
159                 $input = "a \x00 null";
160                 $normal = "a \xef\xbf\xbd null";
161                 $req = $this->mockWebRequest( [ 'x' => $input, 'y' => [ $input, $input ] ] );
162                 $this->assertSame( $normal, $req->getVal( 'x' ) );
163                 $this->assertNotSame( $input, $req->getVal( 'x' ) );
164                 $this->assertSame( [ $normal, $normal ], $req->getArray( 'y' ) );
165         }
166
167         /**
168          * @covers WebRequest::getVal
169          * @covers WebRequest::getGPCVal
170          */
171         public function testGetVal() {
172                 $req = $this->mockWebRequest( [ 'x' => 'Value', 'y' => [ 'a' ], 'crlf' => "A\r\nb" ] );
173                 $this->assertSame( 'Value', $req->getVal( 'x' ), 'Simple value' );
174                 $this->assertSame( null, $req->getVal( 'z' ), 'Not found' );
175                 $this->assertSame( null, $req->getVal( 'y' ), 'Array is ignored' );
176                 $this->assertSame( "A\r\nb", $req->getVal( 'crlf' ), 'CRLF' );
177         }
178
179         /**
180          * @covers WebRequest::getRawVal
181          */
182         public function testGetRawVal() {
183                 $req = $this->mockWebRequest( [
184                         'x' => 'Value',
185                         'y' => [ 'a' ],
186                         'crlf' => "A\r\nb"
187                 ] );
188                 $this->assertSame( 'Value', $req->getRawVal( 'x' ) );
189                 $this->assertSame( null, $req->getRawVal( 'z' ), 'Not found' );
190                 $this->assertSame( null, $req->getRawVal( 'y' ), 'Array is ignored' );
191                 $this->assertSame( "A\r\nb", $req->getRawVal( 'crlf' ), 'CRLF' );
192         }
193
194         /**
195          * @covers WebRequest::getArray
196          */
197         public function testGetArray() {
198                 $req = $this->mockWebRequest( [ 'x' => 'Value', 'y' => [ 'a', 'b' ] ] );
199                 $this->assertSame( [ 'Value' ], $req->getArray( 'x' ), 'Value becomes array' );
200                 $this->assertSame( null, $req->getArray( 'z' ), 'Not found' );
201                 $this->assertSame( [ 'a', 'b' ], $req->getArray( 'y' ) );
202         }
203
204         /**
205          * @covers WebRequest::getIntArray
206          */
207         public function testGetIntArray() {
208                 $req = $this->mockWebRequest( [ 'x' => [ 'Value' ], 'y' => [ '0', '4.2', '-2' ] ] );
209                 $this->assertSame( [ 0 ], $req->getIntArray( 'x' ), 'Text becomes 0' );
210                 $this->assertSame( null, $req->getIntArray( 'z' ), 'Not found' );
211                 $this->assertSame( [ 0, 4, -2 ], $req->getIntArray( 'y' ) );
212         }
213
214         /**
215          * @covers WebRequest::getInt
216          */
217         public function testGetInt() {
218                 $req = $this->mockWebRequest( [
219                         'x' => 'Value',
220                         'y' => [ 'a' ],
221                         'zero' => '0',
222                         'answer' => '4.2',
223                         'neg' => '-2',
224                 ] );
225                 $this->assertSame( 0, $req->getInt( 'x' ), 'Text' );
226                 $this->assertSame( 0, $req->getInt( 'y' ), 'Array' );
227                 $this->assertSame( 0, $req->getInt( 'z' ), 'Not found' );
228                 $this->assertSame( 0, $req->getInt( 'zero' ) );
229                 $this->assertSame( 4, $req->getInt( 'answer' ) );
230                 $this->assertSame( -2, $req->getInt( 'neg' ) );
231         }
232
233         /**
234          * @covers WebRequest::getIntOrNull
235          */
236         public function testGetIntOrNull() {
237                 $req = $this->mockWebRequest( [
238                         'x' => 'Value',
239                         'y' => [ 'a' ],
240                         'zero' => '0',
241                         'answer' => '4.2',
242                         'neg' => '-2',
243                 ] );
244                 $this->assertSame( null, $req->getIntOrNull( 'x' ), 'Text' );
245                 $this->assertSame( null, $req->getIntOrNull( 'y' ), 'Array' );
246                 $this->assertSame( null, $req->getIntOrNull( 'z' ), 'Not found' );
247                 $this->assertSame( 0, $req->getIntOrNull( 'zero' ) );
248                 $this->assertSame( 4, $req->getIntOrNull( 'answer' ) );
249                 $this->assertSame( -2, $req->getIntOrNull( 'neg' ) );
250         }
251
252         /**
253          * @covers WebRequest::getFloat
254          */
255         public function testGetFloat() {
256                 $req = $this->mockWebRequest( [
257                         'x' => 'Value',
258                         'y' => [ 'a' ],
259                         'zero' => '0',
260                         'answer' => '4.2',
261                         'neg' => '-2',
262                 ] );
263                 $this->assertSame( 0.0, $req->getFloat( 'x' ), 'Text' );
264                 $this->assertSame( 0.0, $req->getFloat( 'y' ), 'Array' );
265                 $this->assertSame( 0.0, $req->getFloat( 'z' ), 'Not found' );
266                 $this->assertSame( 0.0, $req->getFloat( 'zero' ) );
267                 $this->assertSame( 4.2, $req->getFloat( 'answer' ) );
268                 $this->assertSame( -2.0, $req->getFloat( 'neg' ) );
269         }
270
271         /**
272          * @covers WebRequest::getBool
273          */
274         public function testGetBool() {
275                 $req = $this->mockWebRequest( [
276                         'x' => 'Value',
277                         'y' => [ 'a' ],
278                         'zero' => '0',
279                         'f' => 'false',
280                         't' => 'true',
281                 ] );
282                 $this->assertSame( true, $req->getBool( 'x' ), 'Text' );
283                 $this->assertSame( false, $req->getBool( 'y' ), 'Array' );
284                 $this->assertSame( false, $req->getBool( 'z' ), 'Not found' );
285                 $this->assertSame( false, $req->getBool( 'zero' ) );
286                 $this->assertSame( true, $req->getBool( 'f' ) );
287                 $this->assertSame( true, $req->getBool( 't' ) );
288         }
289
290         public static function provideFuzzyBool() {
291                 return [
292                         [ 'Text', true ],
293                         [ '', false, '(empty string)' ],
294                         [ '0', false ],
295                         [ '1', true ],
296                         [ 'false', false ],
297                         [ 'true', true ],
298                         [ 'False', false ],
299                         [ 'True', true ],
300                         [ 'FALSE', false ],
301                         [ 'TRUE', true ],
302                 ];
303         }
304
305         /**
306          * @dataProvider provideFuzzyBool
307          * @covers WebRequest::getFuzzyBool
308          */
309         public function testGetFuzzyBool( $value, $expected, $message = null ) {
310                 $req = $this->mockWebRequest( [ 'x' => $value ] );
311                 $this->assertSame( $expected, $req->getFuzzyBool( 'x' ), $message ?: "Value: '$value'" );
312         }
313
314         /**
315          * @covers WebRequest::getFuzzyBool
316          */
317         public function testGetFuzzyBoolDefault() {
318                 $req = $this->mockWebRequest();
319                 $this->assertSame( false, $req->getFuzzyBool( 'z' ), 'Not found' );
320         }
321
322         /**
323          * @covers WebRequest::getCheck
324          */
325         public function testGetCheck() {
326                 $req = $this->mockWebRequest( [ 'x' => 'Value', 'zero' => '0' ] );
327                 $this->assertSame( false, $req->getCheck( 'z' ), 'Not found' );
328                 $this->assertSame( true, $req->getCheck( 'x' ), 'Text' );
329                 $this->assertSame( true, $req->getCheck( 'zero' ) );
330         }
331
332         /**
333          * @covers WebRequest::getText
334          */
335         public function testGetText() {
336                 // Avoid FauxRequest (overrides getText)
337                 $req = $this->mockWebRequest( [ 'crlf' => "Va\r\nlue" ] );
338                 $this->assertSame( "Va\nlue", $req->getText( 'crlf' ), 'CR stripped' );
339         }
340
341         /**
342          * @covers WebRequest::getValues
343          */
344         public function testGetValues() {
345                 $values = [ 'x' => 'Value', 'y' => '' ];
346                 // Avoid FauxRequest (overrides getValues)
347                 $req = $this->mockWebRequest( $values );
348                 $this->assertSame( $values, $req->getValues() );
349                 $this->assertSame( [ 'x' => 'Value' ], $req->getValues( 'x' ), 'Specific keys' );
350         }
351
352         /**
353          * @covers WebRequest::getValueNames
354          */
355         public function testGetValueNames() {
356                 $req = $this->mockWebRequest( [ 'x' => 'Value', 'y' => '' ] );
357                 $this->assertSame( [ 'x', 'y' ], $req->getValueNames() );
358                 $this->assertSame( [ 'x' ], $req->getValueNames( [ 'y' ] ), 'Exclude keys' );
359         }
360
361         /**
362          * @dataProvider provideGetIP
363          * @covers WebRequest::getIP
364          */
365         public function testGetIP( $expected, $input, $squid, $xffList, $private, $description ) {
366                 $_SERVER = $input;
367                 $this->setMwGlobals( [
368                         'wgUsePrivateIPs' => $private,
369                         'wgHooks' => [
370                                 'IsTrustedProxy' => [
371                                         function ( &$ip, &$trusted ) use ( $xffList ) {
372                                                 $trusted = $trusted || in_array( $ip, $xffList );
373                                                 return true;
374                                         }
375                                 ]
376                         ]
377                 ] );
378
379                 $this->setService( 'ProxyLookup', new ProxyLookup( [], $squid ) );
380
381                 $request = new WebRequest();
382                 $result = $request->getIP();
383                 $this->assertEquals( $expected, $result, $description );
384         }
385
386         public static function provideGetIP() {
387                 return [
388                         [
389                                 '127.0.0.1',
390                                 [
391                                         'REMOTE_ADDR' => '127.0.0.1'
392                                 ],
393                                 [],
394                                 [],
395                                 false,
396                                 'Simple IPv4'
397                         ],
398                         [
399                                 '::1',
400                                 [
401                                         'REMOTE_ADDR' => '::1'
402                                 ],
403                                 [],
404                                 [],
405                                 false,
406                                 'Simple IPv6'
407                         ],
408                         [
409                                 '12.0.0.1',
410                                 [
411                                         'REMOTE_ADDR' => 'abcd:0001:002:03:4:555:6666:7777',
412                                         'HTTP_X_FORWARDED_FOR' => '12.0.0.1, abcd:0001:002:03:4:555:6666:7777',
413                                 ],
414                                 [ 'ABCD:1:2:3:4:555:6666:7777' ],
415                                 [],
416                                 false,
417                                 'IPv6 normalisation'
418                         ],
419                         [
420                                 '12.0.0.3',
421                                 [
422                                         'REMOTE_ADDR' => '12.0.0.1',
423                                         'HTTP_X_FORWARDED_FOR' => '12.0.0.3, 12.0.0.2'
424                                 ],
425                                 [ '12.0.0.1', '12.0.0.2' ],
426                                 [],
427                                 false,
428                                 'With X-Forwaded-For'
429                         ],
430                         [
431                                 '12.0.0.1',
432                                 [
433                                         'REMOTE_ADDR' => '12.0.0.1',
434                                         'HTTP_X_FORWARDED_FOR' => '12.0.0.3, 12.0.0.2'
435                                 ],
436                                 [],
437                                 [],
438                                 false,
439                                 'With X-Forwaded-For and disallowed server'
440                         ],
441                         [
442                                 '12.0.0.2',
443                                 [
444                                         'REMOTE_ADDR' => '12.0.0.1',
445                                         'HTTP_X_FORWARDED_FOR' => '12.0.0.3, 12.0.0.2'
446                                 ],
447                                 [ '12.0.0.1' ],
448                                 [],
449                                 false,
450                                 'With multiple X-Forwaded-For and only one allowed server'
451                         ],
452                         [
453                                 '10.0.0.3',
454                                 [
455                                         'REMOTE_ADDR' => '12.0.0.2',
456                                         'HTTP_X_FORWARDED_FOR' => '10.0.0.4, 10.0.0.3, 12.0.0.2'
457                                 ],
458                                 [ '12.0.0.1', '12.0.0.2' ],
459                                 [],
460                                 false,
461                                 'With X-Forwaded-For and private IP (from cache proxy)'
462                         ],
463                         [
464                                 '10.0.0.4',
465                                 [
466                                         'REMOTE_ADDR' => '12.0.0.2',
467                                         'HTTP_X_FORWARDED_FOR' => '10.0.0.4, 10.0.0.3, 12.0.0.2'
468                                 ],
469                                 [ '12.0.0.1', '12.0.0.2', '10.0.0.3' ],
470                                 [],
471                                 true,
472                                 'With X-Forwaded-For and private IP (allowed)'
473                         ],
474                         [
475                                 '10.0.0.4',
476                                 [
477                                         'REMOTE_ADDR' => '12.0.0.2',
478                                         'HTTP_X_FORWARDED_FOR' => '10.0.0.4, 10.0.0.3, 12.0.0.2'
479                                 ],
480                                 [ '12.0.0.1', '12.0.0.2' ],
481                                 [ '10.0.0.3' ],
482                                 true,
483                                 'With X-Forwaded-For and private IP (allowed)'
484                         ],
485                         [
486                                 '10.0.0.3',
487                                 [
488                                         'REMOTE_ADDR' => '12.0.0.2',
489                                         'HTTP_X_FORWARDED_FOR' => '10.0.0.4, 10.0.0.3, 12.0.0.2'
490                                 ],
491                                 [ '12.0.0.1', '12.0.0.2' ],
492                                 [ '10.0.0.3' ],
493                                 false,
494                                 'With X-Forwaded-For and private IP (disallowed)'
495                         ],
496                         [
497                                 '12.0.0.3',
498                                 [
499                                         'REMOTE_ADDR' => '12.0.0.1',
500                                         'HTTP_X_FORWARDED_FOR' => '12.0.0.3, 12.0.0.2'
501                                 ],
502                                 [],
503                                 [ '12.0.0.1', '12.0.0.2' ],
504                                 false,
505                                 'With X-Forwaded-For'
506                         ],
507                         [
508                                 '12.0.0.2',
509                                 [
510                                         'REMOTE_ADDR' => '12.0.0.1',
511                                         'HTTP_X_FORWARDED_FOR' => '12.0.0.3, 12.0.0.2'
512                                 ],
513                                 [],
514                                 [ '12.0.0.1' ],
515                                 false,
516                                 'With multiple X-Forwaded-For and only one allowed server'
517                         ],
518                         [
519                                 '12.0.0.2',
520                                 [
521                                         'REMOTE_ADDR' => '12.0.0.2',
522                                         'HTTP_X_FORWARDED_FOR' => '10.0.0.3, 12.0.0.2'
523                                 ],
524                                 [],
525                                 [ '12.0.0.2' ],
526                                 false,
527                                 'With X-Forwaded-For and private IP and hook (disallowed)'
528                         ],
529                         [
530                                 '12.0.0.1',
531                                 [
532                                         'REMOTE_ADDR' => 'abcd:0001:002:03:4:555:6666:7777',
533                                         'HTTP_X_FORWARDED_FOR' => '12.0.0.1, abcd:0001:002:03:4:555:6666:7777',
534                                 ],
535                                 [ 'ABCD:1:2:3::/64' ],
536                                 [],
537                                 false,
538                                 'IPv6 CIDR'
539                         ],
540                         [
541                                 '12.0.0.3',
542                                 [
543                                         'REMOTE_ADDR' => '12.0.0.1',
544                                         'HTTP_X_FORWARDED_FOR' => '12.0.0.3, 12.0.0.2'
545                                 ],
546                                 [ '12.0.0.0/24' ],
547                                 [],
548                                 false,
549                                 'IPv4 CIDR'
550                         ],
551                 ];
552         }
553
554         /**
555          * @expectedException MWException
556          * @covers WebRequest::getIP
557          */
558         public function testGetIpLackOfRemoteAddrThrowAnException() {
559                 // ensure that local install state doesn't interfere with test
560                 $this->setMwGlobals( [
561                         'wgSquidServersNoPurge' => [],
562                         'wgSquidServers' => [],
563                         'wgUsePrivateIPs' => false,
564                         'wgHooks' => [],
565                 ] );
566                 $this->setService( 'ProxyLookup', new ProxyLookup( [], [] ) );
567
568                 $request = new WebRequest();
569                 # Next call throw an exception about lacking an IP
570                 $request->getIP();
571         }
572
573         public static function provideLanguageData() {
574                 return [
575                         [ '', [], 'Empty Accept-Language header' ],
576                         [ 'en', [ 'en' => 1 ], 'One language' ],
577                         [ 'en, ar', [ 'en' => 1, 'ar' => 1 ], 'Two languages listed in appearance order.' ],
578                         [
579                                 'zh-cn,zh-tw',
580                                 [ 'zh-cn' => 1, 'zh-tw' => 1 ],
581                                 'Two equally prefered languages, listed in appearance order per rfc3282. Checks c9119'
582                         ],
583                         [
584                                 'es, en; q=0.5',
585                                 [ 'es' => 1, 'en' => '0.5' ],
586                                 'Spanish as first language and English and second'
587                         ],
588                         [ 'en; q=0.5, es', [ 'es' => 1, 'en' => '0.5' ], 'Less prefered language first' ],
589                         [ 'fr, en; q=0.5, es', [ 'fr' => 1, 'es' => 1, 'en' => '0.5' ], 'Three languages' ],
590                         [ 'en; q=0.5, es', [ 'es' => 1, 'en' => '0.5' ], 'Two languages' ],
591                         [ 'en, zh;q=0', [ 'en' => 1 ], "It's Chinese to me" ],
592                         [
593                                 'es; q=1, pt;q=0.7, it; q=0.6, de; q=0.1, ru;q=0',
594                                 [ 'es' => '1', 'pt' => '0.7', 'it' => '0.6', 'de' => '0.1' ],
595                                 'Preference for Romance languages'
596                         ],
597                         [
598                                 'en-gb, en-us; q=1',
599                                 [ 'en-gb' => 1, 'en-us' => '1' ],
600                                 'Two equally prefered English variants'
601                         ],
602                         [ '_', [], 'Invalid input' ],
603                 ];
604         }
605
606         /**
607          * @dataProvider provideLanguageData
608          * @covers WebRequest::getAcceptLang
609          */
610         public function testAcceptLang( $acceptLanguageHeader, $expectedLanguages, $description ) {
611                 $_SERVER = [ 'HTTP_ACCEPT_LANGUAGE' => $acceptLanguageHeader ];
612                 $request = new WebRequest();
613                 $this->assertSame( $request->getAcceptLang(), $expectedLanguages, $description );
614         }
615 }