3 class MockCookie
extends Cookie
{
4 public function canServeDomain( $arg ) { return parent
::canServeDomain( $arg ); }
5 public function canServePath( $arg ) { return parent
::canServePath( $arg ); }
6 public function isUnExpired() { return parent
::isUnExpired(); }
12 class HttpTest
extends MediaWikiTestCase
{
17 static $has_proxy = false;
18 static $proxy = "http://hulk:8080/";
19 var $test_geturl = array(
20 "http://en.wikipedia.org/robots.txt",
21 "https://secure.wikimedia.org/",
22 "http://pecl.php.net/feeds/pkg_apc.rss",
23 "http://meta.wikimedia.org/w/index.php?title=Interwiki_map&action=raw",
24 "http://www.mediawiki.org/w/api.php?action=query&list=categorymembers&cmtitle=Category:MediaWiki_hooks&format=php",
26 var $test_requesturl = array( "http://en.wikipedia.org/wiki/Special:Export/User:MarkAHershberger" );
28 var $test_posturl = array( "http://www.comp.leeds.ac.uk/cgi-bin/Perl/environment-example" => "review=test" );
31 putenv( "http_proxy" ); /* Remove any proxy env var, so curl doesn't get confused */
32 if ( is_array( self
::$content ) ) {
35 self
::$has_curl = function_exists( 'curl_init' );
36 self
::$has_fopen = wfIniGetBool( 'allow_url_fopen' );
38 if ( !file_exists( "/usr/bin/curl" ) ) {
39 $this->markTestIncomplete( "This test requires the curl binary at /usr/bin/curl. If you have curl, please file a bug on this test, or, better yet, provide a patch." );
42 $content = tempnam( wfTempDir(), "" );
43 $headers = tempnam( wfTempDir(), "" );
44 if ( !$content && !$headers ) {
45 die( "Couldn't create temp file!" );
48 // This probably isn't the best test for a proxy, but it works on my system!
49 system( "curl -0 -o $content -s " . self
::$proxy );
50 $out = file_get_contents( $content );
52 self
::$has_proxy = true;
55 /* Maybe use wget instead of curl here ... just to use a different codebase? */
56 foreach ( $this->test_geturl
as $u ) {
57 system( "curl -0 -s -D $headers '$u' -o $content" );
58 self
::$content["GET $u"] = file_get_contents( $content );
59 self
::$headers["GET $u"] = file_get_contents( $headers );
61 foreach ( $this->test_requesturl
as $u ) {
62 system( "curl -0 -s -X POST -H 'Content-Length: 0' -D $headers '$u' -o $content" );
63 self
::$content["POST $u"] = file_get_contents( $content );
64 self
::$headers["POST $u"] = file_get_contents( $headers );
66 foreach ( $this->test_posturl
as $u => $postData ) {
67 system( "curl -0 -s -X POST -d '$postData' -D $headers '$u' -o $content" );
68 self
::$content["POST $u => $postData"] = file_get_contents( $content );
69 self
::$headers["POST $u => $postData"] = file_get_contents( $headers );
76 function testInstantiation() {
77 Http
::$httpEngine = false;
79 $r = MWHttpRequest
::factory( "http://www.example.com/" );
80 if ( self
::$has_curl ) {
81 $this->assertThat( $r, $this->isInstanceOf( 'CurlHttpRequest' ) );
83 $this->assertThat( $r, $this->isInstanceOf( 'PhpHttpRequest' ) );
87 if ( !self
::$has_fopen ) {
88 $this->setExpectedException( 'MWException' );
90 Http
::$httpEngine = 'php';
91 $r = MWHttpRequest
::factory( "http://www.example.com/" );
92 $this->assertThat( $r, $this->isInstanceOf( 'PhpHttpRequest' ) );
95 if ( !self
::$has_curl ) {
96 $this->setExpectedException( 'MWException' );
98 Http
::$httpEngine = 'curl';
99 $r = MWHttpRequest
::factory( "http://www.example.com/" );
100 if ( self
::$has_curl ) {
101 $this->assertThat( $r, $this->isInstanceOf( 'CurlHttpRequest' ) );
105 function runHTTPFailureChecks() {
106 // Each of the following requests should result in a failure.
109 $start_time = time();
110 $r = Http
::get( "http://www.example.com:1/", $timeout );
112 $this->assertLessThan( $timeout +
2, $end_time - $start_time,
113 "Request took less than {$timeout}s via " . Http
::$httpEngine );
114 $this->assertEquals( $r, false, "false -- what we get on error from Http::get()" );
116 $r = Http
::get( "http://www.mediawiki.org/xml/made-up-url", $timeout );
117 $this->assertFalse( $r, "False on 404s" );
120 $r = MWHttpRequest
::factory( "http://www.mediawiki.org/xml/made-up-url" );
122 if ( $r instanceof PhpHttpRequest
&& version_compare( '5.2.10', phpversion(), '>' ) ) {
123 $this->assertRegexp( "/HTTP request failed/", $er->getWikiText() );
125 $this->assertRegexp( "/404 Not Found/", $er->getWikiText() );
129 function testFailureDefault() {
130 Http
::$httpEngine = false;
131 $this->runHTTPFailureChecks();
134 function testFailurePhp() {
135 if ( !self
::$has_fopen ) {
136 $this->markTestIncomplete( "This test requires allow_url_fopen=true." );
139 Http
::$httpEngine = "php";
140 $this->runHTTPFailureChecks();
143 function testFailureCurl() {
144 if ( !self
::$has_curl ) {
145 $this->markTestIncomplete( "This test requires curl." );
148 Http
::$httpEngine = "curl";
149 $this->runHTTPFailureChecks();
152 /* ./phase3/includes/Import.php:1108: $data = Http::request( $method, $url ); */
153 /* ./includes/Import.php:1124: $link = Title::newFromText( "$interwiki:Special:Export/$page" ); */
154 /* ./includes/Import.php:1134: return ImportStreamSource::newFromURL( $url, "POST" ); */
155 function runHTTPRequests( $proxy = null ) {
159 $opt['proxy'] = $proxy;
160 } elseif ( $proxy === false ) {
161 $opt['noProxy'] = true;
164 /* no postData here because the only request I could find in code so far didn't have any */
165 foreach ( $this->test_requesturl
as $u ) {
166 $r = Http
::request( "POST", $u, $opt );
167 $this->assertEquals( self
::$content["POST $u"], "$r", "POST $u with " . Http
::$httpEngine );
171 function testRequestDefault() {
172 Http
::$httpEngine = false;
173 $this->runHTTPRequests();
176 function testRequestPhp() {
177 if ( !self
::$has_fopen ) {
178 $this->markTestIncomplete( "This test requires allow_url_fopen=true." );
181 Http
::$httpEngine = "php";
182 $this->runHTTPRequests();
185 function testRequestCurl() {
186 if ( !self
::$has_curl ) {
187 $this->markTestIncomplete( "This test requires curl." );
190 Http
::$httpEngine = "curl";
191 $this->runHTTPRequests();
194 function runHTTPGets( $proxy = null ) {
198 $opt['proxy'] = $proxy;
199 } elseif ( $proxy === false ) {
200 $opt['noProxy'] = true;
203 foreach ( $this->test_geturl
as $u ) {
204 $r = Http
::get( $u, 30, $opt ); /* timeout of 30s */
205 $this->assertEquals( self
::$content["GET $u"], "$r", "Get $u with " . Http
::$httpEngine );
209 function testGetDefault() {
210 Http
::$httpEngine = false;
211 $this->runHTTPGets();
214 function testGetPhp() {
215 if ( !self
::$has_fopen ) {
216 $this->markTestIncomplete( "This test requires allow_url_fopen=true." );
219 Http
::$httpEngine = "php";
220 $this->runHTTPGets();
223 function testGetCurl() {
224 if ( !self
::$has_curl ) {
225 $this->markTestIncomplete( "This test requires curl." );
228 Http
::$httpEngine = "curl";
229 $this->runHTTPGets();
232 function runHTTPPosts( $proxy = null ) {
236 $opt['proxy'] = $proxy;
237 } elseif ( $proxy === false ) {
238 $opt['noProxy'] = true;
241 foreach ( $this->test_posturl
as $u => $postData ) {
242 $opt['postData'] = $postData;
243 $r = Http
::post( $u, $opt );
244 $this->assertEquals( self
::$content["POST $u => $postData"], "$r",
245 "POST $u (postData=$postData) with " . Http
::$httpEngine );
249 function testPostDefault() {
250 Http
::$httpEngine = false;
251 $this->runHTTPPosts();
254 function testPostPhp() {
255 if ( !self
::$has_fopen ) {
256 $this->markTestIncomplete( "This test requires allow_url_fopen=true." );
259 Http
::$httpEngine = "php";
260 $this->runHTTPPosts();
263 function testPostCurl() {
264 if ( !self
::$has_curl ) {
265 $this->markTestIncomplete( "This test requires curl." );
268 Http
::$httpEngine = "curl";
269 $this->runHTTPPosts();
272 function runProxyRequests() {
273 if ( !self
::$has_proxy ) {
274 $this->markTestIncomplete( "This test requires a proxy." );
276 $this->runHTTPGets( self
::$proxy );
277 $this->runHTTPPosts( self
::$proxy );
278 $this->runHTTPRequests( self
::$proxy );
280 // Set false here to do noProxy
281 $this->runHTTPGets( false );
282 $this->runHTTPPosts( false );
283 $this->runHTTPRequests( false );
286 function testProxyDefault() {
287 Http
::$httpEngine = false;
288 $this->runProxyRequests();
291 function testProxyPhp() {
292 if ( !self
::$has_fopen ) {
293 $this->markTestIncomplete( "This test requires allow_url_fopen=true." );
296 Http
::$httpEngine = 'php';
297 $this->runProxyRequests();
300 function testProxyCurl() {
301 if ( !self
::$has_curl ) {
302 $this->markTestIncomplete( "This test requires curl." );
305 Http
::$httpEngine = 'curl';
306 $this->runProxyRequests();
309 function testIsLocalUrl() {
312 /* ./extensions/DonationInterface/payflowpro_gateway/payflowpro_gateway.body.php:559: $user_agent = Http::userAgent(); */
313 function testUserAgent() {
316 function testIsValidUrl() {
320 * @dataProvider cookieDomains
322 function testValidateCookieDomain( $expected, $domain, $origin=null ) {
324 $ok = Cookie
::validateCookieDomain( $domain, $origin );
325 $msg = "$domain against origin $origin";
327 $ok = Cookie
::validateCookieDomain( $domain );
330 $this->assertEquals( $expected, $ok, $msg );
333 function cookieDomains() {
335 array( false, "org"),
336 array( false, ".org"),
337 array( true, "wikipedia.org"),
338 array( true, ".wikipedia.org"),
339 array( false, "co.uk" ),
340 array( false, ".co.uk" ),
341 array( false, "gov.uk" ),
342 array( false, ".gov.uk" ),
343 array( true, "supermarket.uk" ),
344 array( false, "uk" ),
345 array( false, ".uk" ),
346 array( false, "127.0.0." ),
347 array( false, "127." ),
348 array( false, "127.0.0.1." ),
349 array( true, "127.0.0.1" ),
350 array( false, "333.0.0.1" ),
351 array( true, "example.com" ),
352 array( false, "example.com." ),
353 array( true, ".example.com" ),
355 array( true, ".example.com", "www.example.com" ),
356 array( false, "example.com", "www.example.com" ),
357 array( true, "127.0.0.1", "127.0.0.1" ),
358 array( false, "127.0.0.1", "localhost" ),
362 function testSetCooke() {
363 $c = new MockCookie( "name", "value",
368 $this->assertFalse( $c->canServeDomain( "ac.th" ) );
370 $c = new MockCookie( "name", "value",
372 "domain" => "example.com",
376 $this->assertTrue( $c->canServeDomain( "example.com" ) );
377 $this->assertFalse( $c->canServeDomain( "www.example.com" ) );
379 $c = new MockCookie( "name", "value",
381 "domain" => ".example.com",
385 $this->assertFalse( $c->canServeDomain( "www.example.net" ) );
386 $this->assertFalse( $c->canServeDomain( "example.com" ) );
387 $this->assertTrue( $c->canServeDomain( "www.example.com" ) );
389 $this->assertFalse( $c->canServePath( "/" ) );
390 $this->assertFalse( $c->canServePath( "/bogus/path/" ) );
391 $this->assertFalse( $c->canServePath( "/path" ) );
392 $this->assertTrue( $c->canServePath( "/path/" ) );
394 $this->assertTrue( $c->isUnExpired() );
396 $this->assertEquals( "", $c->serializeToHttpRequest( "/path/", "www.example.net" ) );
397 $this->assertEquals( "", $c->serializeToHttpRequest( "/", "www.example.com" ) );
398 $this->assertEquals( "name=value", $c->serializeToHttpRequest( "/path/", "www.example.com" ) );
400 $c = new MockCookie( "name", "value",
402 "domain" => "www.example.com",
405 $this->assertFalse( $c->canServeDomain( "example.com" ) );
406 $this->assertFalse( $c->canServeDomain( "www.example.net" ) );
407 $this->assertTrue( $c->canServeDomain( "www.example.com" ) );
409 $c = new MockCookie( "name", "value",
411 "domain" => ".example.com",
413 "expires" => "-1 day",
415 $this->assertFalse( $c->isUnExpired() );
416 $this->assertEquals( "", $c->serializeToHttpRequest( "/path/", "www.example.com" ) );
418 $c = new MockCookie( "name", "value",
420 "domain" => ".example.com",
422 "expires" => "+1 day",
424 $this->assertTrue( $c->isUnExpired() );
425 $this->assertEquals( "name=value", $c->serializeToHttpRequest( "/path/", "www.example.com" ) );
428 function testCookieJarSetCookie() {
430 $cj->setCookie( "name", "value",
432 "domain" => ".example.com",
435 $cj->setCookie( "name2", "value",
437 "domain" => ".example.com",
438 "path" => "/path/sub",
440 $cj->setCookie( "name3", "value",
442 "domain" => ".example.com",
445 $cj->setCookie( "name4", "value",
447 "domain" => ".example.net",
450 $cj->setCookie( "name5", "value",
452 "domain" => ".example.net",
454 "expires" => "-1 day",
457 $this->assertEquals( "name4=value", $cj->serializeToHttpRequest( "/path/", "www.example.net" ) );
458 $this->assertEquals( "name3=value", $cj->serializeToHttpRequest( "/", "www.example.com" ) );
459 $this->assertEquals( "name=value; name3=value", $cj->serializeToHttpRequest( "/path/", "www.example.com" ) );
461 $cj->setCookie( "name5", "value",
463 "domain" => ".example.net",
465 "expires" => "+1 day",
467 $this->assertEquals( "name4=value; name5=value", $cj->serializeToHttpRequest( "/path/", "www.example.net" ) );
469 $cj->setCookie( "name4", "value",
471 "domain" => ".example.net",
473 "expires" => "-1 day",
475 $this->assertEquals( "name5=value", $cj->serializeToHttpRequest( "/path/", "www.example.net" ) );
478 function testParseResponseHeader() {
481 $h[] = "Set-Cookie: name4=value; domain=.example.com; path=/; expires=Mon, 09-Dec-2029 13:46:00 GMT";
482 $cj->parseCookieResponseHeader( $h[0], "www.example.com" );
483 $this->assertEquals( "name4=value", $cj->serializeToHttpRequest( "/", "www.example.com" ) );
485 $h[] = "name4=value2; domain=.example.com; path=/path/; expires=Mon, 09-Dec-2029 13:46:00 GMT";
486 $cj->parseCookieResponseHeader( $h[1], "www.example.com" );
487 $this->assertEquals( "", $cj->serializeToHttpRequest( "/", "www.example.com" ) );
488 $this->assertEquals( "name4=value2", $cj->serializeToHttpRequest( "/path/", "www.example.com" ) );
490 $h[] = "name5=value3; domain=.example.com; path=/path/; expires=Mon, 09-Dec-2029 13:46:00 GMT";
491 $cj->parseCookieResponseHeader( $h[2], "www.example.com" );
492 $this->assertEquals( "name4=value2; name5=value3", $cj->serializeToHttpRequest( "/path/", "www.example.com" ) );
494 $h[] = "name6=value3; domain=.example.net; path=/path/; expires=Mon, 09-Dec-2029 13:46:00 GMT";
495 $cj->parseCookieResponseHeader( $h[3], "www.example.com" );
496 $this->assertEquals( "", $cj->serializeToHttpRequest( "/path/", "www.example.net" ) );
498 $h[] = "name6=value0; domain=.example.net; path=/path/; expires=Mon, 09-Dec-1999 13:46:00 GMT";
499 $cj->parseCookieResponseHeader( $h[4], "www.example.net" );
500 $this->assertEquals( "", $cj->serializeToHttpRequest( "/path/", "www.example.net" ) );
502 $h[] = "name6=value4; domain=.example.net; path=/path/; expires=Mon, 09-Dec-2029 13:46:00 GMT";
503 $cj->parseCookieResponseHeader( $h[5], "www.example.net" );
504 $this->assertEquals( "name6=value4", $cj->serializeToHttpRequest( "/path/", "www.example.net" ) );
507 function runCookieRequests() {
508 $r = MWHttpRequest
::factory( "http://www.php.net/manual", array( 'followRedirects' => true ) );
511 $jar = $r->getCookieJar();
512 $this->assertThat( $jar, $this->isInstanceOf( 'CookieJar' ) );
514 $serialized = $jar->serializeToHttpRequest( "/search?q=test", "www.php.net" );
515 $this->assertRegExp( '/\bCOUNTRY=[^=;]+/', $serialized );
516 $this->assertRegExp( '/\bLAST_LANG=[^=;]+/', $serialized );
517 $this->assertEquals( '', $jar->serializeToHttpRequest( "/search?q=test", "www.php.com" ) );
520 function testCookieRequestDefault() {
521 Http
::$httpEngine = false;
522 $this->runCookieRequests();
524 function testCookieRequestPhp() {
525 if ( !self
::$has_fopen ) {
526 $this->markTestIncomplete( "This test requires allow_url_fopen=true." );
529 Http
::$httpEngine = 'php';
530 $this->runCookieRequests();
532 function testCookieRequestCurl() {
533 if ( !self
::$has_curl ) {
534 $this->markTestIncomplete( "This test requires curl." );
537 Http
::$httpEngine = 'curl';
538 $this->runCookieRequests();
542 * Test Http::isValidURI()
543 * @bug 27854 : Http::isValidURI is to lax
544 *@dataProvider provideURI */
545 function testIsValidUri( $expect, $URI, $message = '' ) {
548 (bool) Http
::isValidURI( $URI ),
554 * Feeds URI to test a long regular expression in Http::isValidURI
556 function provideURI() {
557 /** Format: 'boolean expectation', 'URI to test', 'Optional message' */
559 array( false, '¿non sens before!! http://a', 'Allow anything before URI' ),
561 # (ftp|http|https) - only three schemes allowed
562 array( true, 'http://www.example.org/' ),
563 array( true, 'https://www.example.org/' ),
564 array( true, 'ftp://www.example.org/' ),
565 array( true, 'http://www.example.org', 'URI without directory' ),
566 array( true, 'http://a', 'Short name' ),
567 array( true, 'http://étoile', 'Allow UTF-8 in hostname' ), # 'étoile' is french for 'star'
568 array( false, '\\host\directory', 'CIFS share' ),
569 array( false, 'gopher://host/dir', 'Reject gopher scheme' ),
570 array( false, 'telnet://host', 'Reject telnet scheme' ),
572 # :\/\/ - double slashes
573 array( false, 'http//example.org', 'Reject missing colon in protocol' ),
574 array( false, 'http:/example.org', 'Reject missing slash in protocol' ),
575 array( false, 'http:example.org', 'Must have two slashes' ),
576 # Following fail since hostname can be made of anything
577 array( false, 'http:///example.org', 'Must have exactly two slashes, not three' ),
579 # (\w+:{0,1}\w*@)? - optional user:pass
580 array( true, 'http://user@host', 'Username provided' ),
581 array( true, 'http://user:@host', 'Username provided, no password' ),
582 array( true, 'http://user:pass@host', 'Username and password provided' ),
584 # (\S+) - host part is made of anything not whitespaces
585 array( false, 'http://!"èèè¿¿¿~~\'', 'hostname is made of any non whitespace' ),
586 array( false, 'http://exam:ple.org/', 'hostname can not use colons!' ),
588 # (:[0-9]+)? - port number
589 array( true, 'http://example.org:80/' ),
590 array( true, 'https://example.org:80/' ),
591 array( true, 'http://example.org:443/' ),
592 array( true, 'https://example.org:443/' ),
593 array( true, 'ftp://example.org:1/', 'Minimum' ),
594 array( true, 'ftp://example.org:65535/', 'Maximum port number' ),
596 # Part after the hostname is / or / with something else
597 array( true, 'http://example/#' ),
598 array( true, 'http://example/!' ),
599 array( true, 'http://example/:' ),
600 array( true, 'http://example/.' ),
601 array( true, 'http://example/?' ),
602 array( true, 'http://example/+' ),
603 array( true, 'http://example/=' ),
604 array( true, 'http://example/&' ),
605 array( true, 'http://example/%' ),
606 array( true, 'http://example/@' ),
607 array( true, 'http://example/-' ),
608 array( true, 'http://example//' ),
609 array( true, 'http://example/&' ),
612 array( true, 'http://exam#ple.org', ), # This one is valid, really!
613 array( true, 'http://example.org:80#anchor' ),
614 array( true, 'http://example.org/?id#anchor' ),
615 array( true, 'http://example.org/?#anchor' ),
617 array( false, 'http://a ¿non !!sens after', 'Allow anything after URI' ),