blob: 7cd7446bb42032ef71649be2658b696ac9a7f333 [file] [log] [blame]
<?php
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
namespace Test\Thrift\Unit\Lib\Transport;
use phpmock\phpunit\PHPMock;
use PHPUnit\Framework\TestCase;
use Thrift\Exception\TTransportException;
use Thrift\Transport\TCurlClient;
class TCurlClientTest extends TestCase
{
use PHPMock;
public function testSetTimeoutSecs()
{
$host = 'localhost';
$transport = new TCurlClient($host);
$transport->setTimeoutSecs(1000);
$ref = new \ReflectionClass($transport);
$prop = $ref->getProperty('timeout_');
$prop->setAccessible(true);
$this->assertEquals(1000, $prop->getValue($transport));
}
public function testSetConnectionTimeoutSecs()
{
$host = 'localhost';
$transport = new TCurlClient($host);
$transport->setConnectionTimeoutSecs(1000);
$ref = new \ReflectionClass($transport);
$prop = $ref->getProperty('connectionTimeout_');
$prop->setAccessible(true);
$this->assertEquals(1000, $prop->getValue($transport));
}
public function testIsOpen()
{
$host = 'localhost';
$transport = new TCurlClient($host);
$this->assertTrue($transport->isOpen());
}
public function testOpen()
{
$host = 'localhost';
$transport = new TCurlClient($host);
$this->assertNull($transport->open());
}
public function testClose()
{
$host = 'localhost';
$transport = new TCurlClient($host);
$ref = new \ReflectionClass($transport);
$propRequest = $ref->getProperty('request_');
$propRequest->setAccessible(true);
$propRequest->setValue($transport, 'testRequest');
$propResponse = $ref->getProperty('response_');
$propResponse->setAccessible(true);
$propResponse->setValue($transport, 'testResponse');
$this->assertNull($transport->close());
$this->assertEmpty($propRequest->getValue($transport));
$this->assertEmpty($propResponse->getValue($transport));
}
public function testRead()
{
$host = 'localhost';
$transport = new TCurlClient($host);
$ref = new \ReflectionClass($transport);
$propResponse = $ref->getProperty('response_');
$propResponse->setAccessible(true);
$propResponse->setValue($transport, '1234567890');
$response = $transport->read(5);
$this->assertEquals('12345', $response);
$this->assertEquals('67890', $propResponse->getValue($transport));
$response = $transport->read(5);
$this->assertEquals('67890', $response);
# The response does not cleaned after reading full answer, maybe it should be fixed
$this->assertEquals('67890', $propResponse->getValue($transport));
}
public function testReadAll()
{
$host = 'localhost';
$transport = new TCurlClient($host);
$ref = new \ReflectionClass($transport);
$propResponse = $ref->getProperty('response_');
$propResponse->setAccessible(true);
$propResponse->setValue($transport, '1234567890');
$response = $transport->readAll(5);
$this->assertEquals('12345', $response);
$this->assertEquals('67890', $propResponse->getValue($transport));
}
public function testReadAll_THRIFT_4656()
{
$host = 'localhost';
$transport = new TCurlClient($host);
$ref = new \ReflectionClass($transport);
$propResponse = $ref->getProperty('response_');
$propResponse->setAccessible(true);
$propResponse->setValue($transport, '');
$this->expectException(TTransportException::class);
$this->expectExceptionMessage('TCurlClient could not read 5 bytes');
$this->expectExceptionCode(TTransportException::UNKNOWN);
$transport->readAll(5);
}
public function testWrite()
{
$host = 'localhost';
$transport = new TCurlClient($host);
$ref = new \ReflectionClass($transport);
$propRequest = $ref->getProperty('request_');
$propRequest->setAccessible(true);
$propRequest->setValue($transport, '1234567890');
$transport->write('12345');
$this->assertEquals('123456789012345', $propRequest->getValue($transport));
}
public function testAddHeaders()
{
$host = 'localhost';
$transport = new TCurlClient($host);
$ref = new \ReflectionClass($transport);
$propRequest = $ref->getProperty('headers_');
$propRequest->setAccessible(true);
$propRequest->setValue($transport, ['test' => '1234567890']);
$transport->addHeaders(['test2' => '12345']);
$this->assertEquals(['test' => '1234567890', 'test2' => '12345'], $propRequest->getValue($transport));
}
/**
* @dataProvider flushDataProvider
*/
public function testFlush(
$host,
$port,
$uri,
$scheme,
$headers,
$request,
$timeout,
$connectionTimeout,
$curlSetOptCalls,
$response,
$responseError,
$responseCode,
$expectedException = null,
$expectedMessage = null,
$expectedCode = null
) {
$this->getFunctionMock('Thrift\\Transport', 'register_shutdown_function')
->expects($this->once())
->with(
$this->callback(
function ($arg) {
return is_array(
$arg
) && $arg[0] === 'Thrift\\Transport\\TCurlClient' && $arg[1] === 'closeCurlHandle';
}
)
);
$this->getFunctionMock('Thrift\\Transport', 'curl_init')
->expects($this->once());
$this->getFunctionMock('Thrift\\Transport', 'curl_setopt')
->expects($this->any())
->withConsecutive(...$curlSetOptCalls)
->willReturn(true);
$this->getFunctionMock('Thrift\\Transport', 'curl_exec')
->expects($this->once())
->with($this->anything())
->willReturn($response);
$this->getFunctionMock('Thrift\\Transport', 'curl_error')
->expects($this->once())
->with($this->anything())
->willReturn($responseError);
$this->getFunctionMock('Thrift\\Transport', 'curl_getinfo')
->expects($this->once())
->with($this->anything(), CURLINFO_HTTP_CODE)
->willReturn($responseCode);
if (!is_null($expectedException)) {
$this->expectException($expectedException);
$this->expectExceptionMessage($expectedMessage);
$this->expectExceptionCode($expectedCode);
$this->getFunctionMock('Thrift\\Transport', 'curl_close')
->expects($this->once())
->with($this->anything());
}
$transport = new TCurlClient($host, $port, $uri, $scheme);
if (!empty($headers)) {
$transport->addHeaders($headers);
}
$transport->write($request);
if (!empty($timeout)) {
$transport->setTimeoutSecs($timeout);
}
if (!empty($connectionTimeout)) {
$transport->setConnectionTimeoutSecs($connectionTimeout);
}
$transport->flush();
}
public function flushDataProvider()
{
$request = 'request';
$default = [
'host' => 'localhost',
'port' => 80,
'uri' => '',
'scheme' => 'http',
'headers' => [],
'request' => $request,
'timeout' => null,
'connectionTimeout' => null,
'curlSetOptCalls' => [
[$this->anything(), CURLOPT_RETURNTRANSFER, true],
[$this->anything(), CURLOPT_USERAGENT, 'PHP/TCurlClient'],
[$this->anything(), CURLOPT_CUSTOMREQUEST, 'POST'],
[$this->anything(), CURLOPT_FOLLOWLOCATION, true],
[$this->anything(), CURLOPT_MAXREDIRS, 1],
[
$this->anything(),
CURLOPT_HTTPHEADER,
[
'Accept: application/x-thrift',
'Content-Type: application/x-thrift',
'Content-Length: ' . strlen($request),
],
],
[$this->anything(), CURLOPT_POSTFIELDS, $request],
[$this->anything(), CURLOPT_URL, 'http://localhost'],
],
'response' => 'response',
'responseError' => '',
'responseCode' => 200,
];
yield 'default' => $default;
yield 'additionalHeaders' => array_merge(
$default,
[
'headers' => ['test' => '1234567890'],
'curlSetOptCalls' => [
[$this->anything(), CURLOPT_RETURNTRANSFER, true],
[$this->anything(), CURLOPT_USERAGENT, 'PHP/TCurlClient'],
[$this->anything(), CURLOPT_CUSTOMREQUEST, 'POST'],
[$this->anything(), CURLOPT_FOLLOWLOCATION, true],
[$this->anything(), CURLOPT_MAXREDIRS, 1],
[
$this->anything(),
CURLOPT_HTTPHEADER,
[
'Accept: application/x-thrift',
'Content-Type: application/x-thrift',
'Content-Length: ' . strlen($request),
'test: 1234567890',
],
],
[$this->anything(), CURLOPT_POSTFIELDS, $request],
[$this->anything(), CURLOPT_URL, 'http://localhost'],
],
]
);
yield 'uri' => array_merge(
$default,
[
'uri' => 'test1234567890',
'curlSetOptCalls' => [
[$this->anything(), CURLOPT_RETURNTRANSFER, true],
[$this->anything(), CURLOPT_USERAGENT, 'PHP/TCurlClient'],
[$this->anything(), CURLOPT_CUSTOMREQUEST, 'POST'],
[$this->anything(), CURLOPT_FOLLOWLOCATION, true],
[$this->anything(), CURLOPT_MAXREDIRS, 1],
[
$this->anything(),
CURLOPT_HTTPHEADER,
[
'Accept: application/x-thrift',
'Content-Type: application/x-thrift',
'Content-Length: ' . strlen($request),
],
],
[$this->anything(), CURLOPT_POSTFIELDS, $request],
[$this->anything(), CURLOPT_URL, 'http://localhost/test1234567890'],
],
]
);
yield 'timeout' => array_merge(
$default,
[
'timeout' => 10,
'connectionTimeout' => 10,
'curlSetOptCalls' => [
[$this->anything(), CURLOPT_RETURNTRANSFER, true],
[$this->anything(), CURLOPT_USERAGENT, 'PHP/TCurlClient'],
[$this->anything(), CURLOPT_CUSTOMREQUEST, 'POST'],
[$this->anything(), CURLOPT_FOLLOWLOCATION, true],
[$this->anything(), CURLOPT_MAXREDIRS, 1],
[
$this->anything(),
CURLOPT_HTTPHEADER,
[
'Accept: application/x-thrift',
'Content-Type: application/x-thrift',
'Content-Length: ' . strlen($request),
],
],
[$this->anything(), CURLOPT_TIMEOUT, 10],
[$this->anything(), CURLOPT_CONNECTTIMEOUT, 10],
[$this->anything(), CURLOPT_POSTFIELDS, $request],
[$this->anything(), CURLOPT_URL, 'http://localhost'],
],
]
);
yield 'timeout msec' => array_merge(
$default,
[
'timeout' => 0.1,
'connectionTimeout' => 0.1,
'curlSetOptCalls' => [
[$this->anything(), CURLOPT_RETURNTRANSFER, true],
[$this->anything(), CURLOPT_USERAGENT, 'PHP/TCurlClient'],
[$this->anything(), CURLOPT_CUSTOMREQUEST, 'POST'],
[$this->anything(), CURLOPT_FOLLOWLOCATION, true],
[$this->anything(), CURLOPT_MAXREDIRS, 1],
[
$this->anything(),
CURLOPT_HTTPHEADER,
[
'Accept: application/x-thrift',
'Content-Type: application/x-thrift',
'Content-Length: ' . strlen($request),
],
],
[$this->anything(), CURLOPT_TIMEOUT_MS, 100],
[$this->anything(), CURLOPT_CONNECTTIMEOUT_MS, 100],
[$this->anything(), CURLOPT_POSTFIELDS, $request],
[$this->anything(), CURLOPT_URL, 'http://localhost'],
],
]
);
yield 'curl_exec return false' => array_merge(
$default,
[
'response' => false,
'expectedException' => TTransportException::class,
'expectedMessage' => 'TCurlClient: Could not connect to http://localhost',
'expectedCode' => TTransportException::UNKNOWN,
]
);
yield 'curl_exec return response code 403' => array_merge(
$default,
[
'responseError' => 'Access denied',
'responseCode' => 403,
'expectedException' => TTransportException::class,
'expectedMessage' => 'TCurlClient: Could not connect to http://localhost, Access denied, HTTP status code: 403',
'expectedCode' => TTransportException::UNKNOWN,
]
);
}
public function testCloseCurlHandle()
{
$this->getFunctionMock('Thrift\\Transport', 'curl_close')
->expects($this->once())
->with('testHandle');
$transport = new TCurlClient('localhost');
$ref = new \ReflectionClass($transport);
$prop = $ref->getProperty('curlHandle');
$prop->setAccessible(true);
$prop->setValue($transport, 'testHandle');
$transport::closeCurlHandle();
}
}