Currently GuzzleHttpRequest is not sending any cookies. Furthermore, Guzzle expects its own format of CookieJar, which is not compatible with CookieJar used by MediaWiki. Solution is to add Guzzle Middleware that would obtain Cookie header from MediaWiki CookieJar and add it to outgoing Guzzle requests. Note: special handling of received cookies (Set-Cookie header from the server) is unnecessary, this is not Guzzle-specific code and is already done in MWHttpRequest::parseCookies(). Still, adding both a test of sending cookies and receiving cookies. Bug: T245644 Change-Id: If69840e65d5671989cf15450964da3c691fd164a
217 lines
6.6 KiB
PHP
217 lines
6.6 KiB
PHP
<?php
|
|
|
|
use GuzzleHttp\Handler\MockHandler;
|
|
use GuzzleHttp\HandlerStack;
|
|
use GuzzleHttp\Middleware;
|
|
use GuzzleHttp\Psr7\Request;
|
|
use GuzzleHttp\Psr7\Response;
|
|
|
|
/**
|
|
* class for tests of GuzzleHttpRequest
|
|
*
|
|
* No actual requests are made herein - all external communications are mocked
|
|
*
|
|
* @covers GuzzleHttpRequest
|
|
* @covers MWHttpRequest
|
|
*/
|
|
class GuzzleHttpRequestTest extends MediaWikiTestCase {
|
|
/**
|
|
* Placeholder url to use for various tests. This is never contacted, but we must use
|
|
* a url of valid format to avoid validation errors.
|
|
* @var string
|
|
*/
|
|
protected $exampleUrl = 'http://www.example.test';
|
|
|
|
/**
|
|
* Minimal example body text
|
|
* @var string
|
|
*/
|
|
protected $exampleBodyText = 'x';
|
|
|
|
/**
|
|
* For accumulating callback data for testing
|
|
* @var string
|
|
*/
|
|
protected $bodyTextReceived = '';
|
|
|
|
/**
|
|
* Callback: process a chunk of the result of a HTTP request
|
|
*
|
|
* @param mixed $req
|
|
* @param string $buffer
|
|
* @return int Number of bytes handled
|
|
*/
|
|
public function processHttpDataChunk( $req, $buffer ) {
|
|
$this->bodyTextReceived .= $buffer;
|
|
return strlen( $buffer );
|
|
}
|
|
|
|
public function testSuccess() {
|
|
$handler = HandlerStack::create( new MockHandler( [ new Response( 200, [
|
|
'status' => 200,
|
|
], $this->exampleBodyText ) ] ) );
|
|
$r = new GuzzleHttpRequest( $this->exampleUrl, [ 'handler' => $handler ] );
|
|
$r->execute();
|
|
|
|
$this->assertEquals( 200, $r->getStatus() );
|
|
$this->assertEquals( $this->exampleBodyText, $r->getContent() );
|
|
}
|
|
|
|
public function testSuccessConstructorCallback() {
|
|
$this->bodyTextReceived = '';
|
|
$handler = HandlerStack::create( new MockHandler( [ new Response( 200, [
|
|
'status' => 200,
|
|
], $this->exampleBodyText ) ] ) );
|
|
$r = new GuzzleHttpRequest( $this->exampleUrl, [
|
|
'callback' => [ $this, 'processHttpDataChunk' ],
|
|
'handler' => $handler,
|
|
] );
|
|
$r->execute();
|
|
|
|
$this->assertEquals( 200, $r->getStatus() );
|
|
$this->assertEquals( $this->exampleBodyText, $this->bodyTextReceived );
|
|
}
|
|
|
|
public function testSuccessSetCallback() {
|
|
$this->bodyTextReceived = '';
|
|
$handler = HandlerStack::create( new MockHandler( [ new Response( 200, [
|
|
'status' => 200,
|
|
], $this->exampleBodyText ) ] ) );
|
|
$r = new GuzzleHttpRequest( $this->exampleUrl, [
|
|
'handler' => $handler,
|
|
] );
|
|
$r->setCallback( [ $this, 'processHttpDataChunk' ] );
|
|
$r->execute();
|
|
|
|
$this->assertEquals( 200, $r->getStatus() );
|
|
$this->assertEquals( $this->exampleBodyText, $this->bodyTextReceived );
|
|
}
|
|
|
|
/**
|
|
* use a callback stream to pipe the mocked response data to our callback function
|
|
*/
|
|
public function testSuccessSink() {
|
|
$this->bodyTextReceived = '';
|
|
$handler = HandlerStack::create( new MockHandler( [ new Response( 200, [
|
|
'status' => 200,
|
|
], $this->exampleBodyText ) ] ) );
|
|
$r = new GuzzleHttpRequest( $this->exampleUrl, [
|
|
'handler' => $handler,
|
|
'sink' => new MWCallbackStream( [ $this, 'processHttpDataChunk' ] ),
|
|
] );
|
|
$r->execute();
|
|
|
|
$this->assertEquals( 200, $r->getStatus() );
|
|
$this->assertEquals( $this->exampleBodyText, $this->bodyTextReceived );
|
|
}
|
|
|
|
public function testBadUrl() {
|
|
$r = new GuzzleHttpRequest( '' );
|
|
$s = $r->execute();
|
|
$errorMsg = $s->getErrorsByType( 'error' )[0]['message'];
|
|
|
|
$this->assertSame( 0, $r->getStatus() );
|
|
$this->assertEquals( 'http-invalid-url', $errorMsg );
|
|
}
|
|
|
|
public function testConnectException() {
|
|
$handler = HandlerStack::create( new MockHandler( [ new GuzzleHttp\Exception\ConnectException(
|
|
'Mock Connection Exception', new Request( 'GET', $this->exampleUrl )
|
|
) ] ) );
|
|
$r = new GuzzleHttpRequest( $this->exampleUrl, [ 'handler' => $handler ] );
|
|
$s = $r->execute();
|
|
$errorMsg = $s->getErrorsByType( 'error' )[0]['message'];
|
|
|
|
$this->assertSame( 0, $r->getStatus() );
|
|
$this->assertEquals( 'http-request-error', $errorMsg );
|
|
}
|
|
|
|
public function testTimeout() {
|
|
$handler = HandlerStack::create( new MockHandler( [ new GuzzleHttp\Exception\RequestException(
|
|
'Connection timed out', new Request( 'GET', $this->exampleUrl )
|
|
) ] ) );
|
|
$r = new GuzzleHttpRequest( $this->exampleUrl, [ 'handler' => $handler ] );
|
|
$s = $r->execute();
|
|
$errorMsg = $s->getErrorsByType( 'error' )[0]['message'];
|
|
|
|
$this->assertSame( 0, $r->getStatus() );
|
|
$this->assertEquals( 'http-timed-out', $errorMsg );
|
|
}
|
|
|
|
public function testNotFound() {
|
|
$handler = HandlerStack::create( new MockHandler( [ new Response( 404, [
|
|
'status' => '404',
|
|
] ) ] ) );
|
|
$r = new GuzzleHttpRequest( $this->exampleUrl, [ 'handler' => $handler ] );
|
|
$s = $r->execute();
|
|
$errorMsg = $s->getErrorsByType( 'error' )[0]['message'];
|
|
|
|
$this->assertEquals( 404, $r->getStatus() );
|
|
$this->assertEquals( 'http-bad-status', $errorMsg );
|
|
}
|
|
|
|
/*
|
|
* Test of POST requests header
|
|
*/
|
|
public function testPostBody() {
|
|
$container = [];
|
|
$history = Middleware::history( $container );
|
|
$stack = HandlerStack::create( new MockHandler( [ new Response() ] ) );
|
|
$stack->push( $history );
|
|
$client = new GuzzleHttpRequest( $this->exampleUrl, [
|
|
'method' => 'POST',
|
|
'handler' => $stack,
|
|
'post' => 'key=value',
|
|
] );
|
|
$client->execute();
|
|
|
|
$request = $container[0]['request'];
|
|
$this->assertEquals( 'POST', $request->getMethod() );
|
|
$this->assertEquals( 'application/x-www-form-urlencoded',
|
|
$request->getHeader( 'Content-Type' )[0] );
|
|
}
|
|
|
|
/*
|
|
* Test that cookies from CookieJar were sent in the outgoing request.
|
|
*/
|
|
public function testCookieSent() {
|
|
$domain = wfParseUrl( $this->exampleUrl )['host'];
|
|
$expectedCookies = [ 'cookie1' => 'value1', 'anothercookie' => 'secondvalue' ];
|
|
$jar = new CookieJar;
|
|
foreach ( $expectedCookies as $key => $val ) {
|
|
$jar->setCookie( $key, $val, [ 'domain' => $domain ] );
|
|
}
|
|
|
|
$container = [];
|
|
$history = Middleware::history( $container );
|
|
$stack = HandlerStack::create( new MockHandler( [ new Response() ] ) );
|
|
$stack->push( $history );
|
|
$client = new GuzzleHttpRequest( $this->exampleUrl, [
|
|
'method' => 'POST',
|
|
'handler' => $stack,
|
|
'post' => 'key=value',
|
|
] );
|
|
$client->setCookieJar( $jar );
|
|
$client->execute();
|
|
|
|
$request = $container[0]['request'];
|
|
$this->assertEquals( [ 'cookie1=value1; anothercookie=secondvalue' ],
|
|
$request->getHeader( 'Cookie' ) );
|
|
}
|
|
|
|
/*
|
|
* Test that cookies returned by HTTP response were added back into the CookieJar.
|
|
*/
|
|
public function testCookieReceived() {
|
|
$handler = HandlerStack::create( new MockHandler( [ new Response( 200, [
|
|
'status' => 200,
|
|
'Set-Cookie' => [ 'cookie1=value1', 'anothercookie=secondvalue' ]
|
|
] ) ] ) );
|
|
$r = new GuzzleHttpRequest( $this->exampleUrl, [ 'handler' => $handler ] );
|
|
$r->execute();
|
|
|
|
$domain = wfParseUrl( $this->exampleUrl )['host'];
|
|
$this->assertEquals( 'cookie1=value1; anothercookie=secondvalue',
|
|
$r->getCookieJar()->serializeToHttpRequest( '/', $domain ) );
|
|
}
|
|
}
|