MediaWiki REL1_30
WebRequestTest.php
Go to the documentation of this file.
1<?php
2
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
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
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
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
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
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
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
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
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
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
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
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
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
317 public function testGetFuzzyBoolDefault() {
318 $req = $this->mockWebRequest();
319 $this->assertSame( false, $req->getFuzzyBool( 'z' ), 'Not found' );
320 }
321
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
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
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
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
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
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
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}
setMwGlobals( $pairs, $value=null)
Sets a global, maintaining a stashed version of the previous global to be restored in tearDown.
setService( $name, $object)
Sets a service, maintaining a stashed version of the previous service to be restored in tearDown.
testGetText()
WebRequest::getText.
testGetIpLackOfRemoteAddrThrowAnException()
MWException WebRequest::getIP.
mockWebRequest( $data=[])
testDetectServer( $expected, $input, $description)
provideDetectServer WebRequest::detectServer WebRequest::detectProtocol
static provideLanguageData()
testGetValues()
WebRequest::getValues.
testGetRawVal()
WebRequest::getRawVal.
testGetIntOrNull()
WebRequest::getIntOrNull.
testGetBool()
WebRequest::getBool.
testGetCheck()
WebRequest::getCheck.
static provideFuzzyBool()
testGetValueNames()
WebRequest::getValueNames.
testGetFuzzyBoolDefault()
WebRequest::getFuzzyBool.
testGetFuzzyBool( $value, $expected, $message=null)
provideFuzzyBool WebRequest::getFuzzyBool
testGetElapsedTime()
WebRequest::getElapsedTime.
testGetValNormal()
WebRequest::getVal WebRequest::getGPCVal WebRequest::normalizeUnicode.
static provideDetectServer()
testGetArray()
WebRequest::getArray.
testGetFloat()
WebRequest::getFloat.
testGetIP( $expected, $input, $squid, $xffList, $private, $description)
provideGetIP WebRequest::getIP
testGetInt()
WebRequest::getInt.
testGetIntArray()
WebRequest::getIntArray.
testAcceptLang( $acceptLanguageHeader, $expectedLanguages, $description)
provideLanguageData WebRequest::getAcceptLang
testGetVal()
WebRequest::getVal WebRequest::getGPCVal.
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form stripping il...
this hook is for auditing only $req
Definition hooks.txt:988
do that in ParserLimitReportFormat instead use this to modify the parameters of the image all existing parser cache entries will be invalid To avoid you ll need to handle that somehow(e.g. with the RejectParserCacheValue hook) because MediaWiki won 't do it for you. & $defaults also a ContextSource after deleting those rows but within the same transaction you ll probably need to make sure the header is varied on $request
Definition hooks.txt:2775
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return true
Definition hooks.txt:1976
processing should stop and the error should be shown to the user * false
Definition hooks.txt:187
if(is_array($mode)) switch( $mode) $input