Skip to content

Commit

Permalink
Merge pull request #33 from sunrise-php/release/v1.4.4
Browse files Browse the repository at this point in the history
v1.4.4
  • Loading branch information
fenric committed Apr 17, 2022
2 parents b2c4c41 + 8540c02 commit fd63989
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 65 deletions.
10 changes: 5 additions & 5 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,39 +10,39 @@ jobs:
steps:
- checkout
- run: php -v
- run: composer install --no-interaction --no-suggest --prefer-source
- run: composer install --no-interaction
- run: XDEBUG_MODE=coverage php vendor/bin/phpunit --coverage-text
php72:
docker:
- image: circleci/php:7.2-cli-node-browsers
steps:
- checkout
- run: php -v
- run: composer install --no-interaction --no-suggest --prefer-source
- run: composer install --no-interaction
- run: XDEBUG_MODE=coverage php vendor/bin/phpunit --coverage-text
php73:
docker:
- image: circleci/php:7.3-cli-node-browsers
steps:
- checkout
- run: php -v
- run: composer install --no-interaction --no-suggest --prefer-source
- run: composer install --no-interaction
- run: XDEBUG_MODE=coverage php vendor/bin/phpunit --coverage-text
php74:
docker:
- image: circleci/php:7.4-cli-node-browsers
steps:
- checkout
- run: php -v
- run: composer install --no-interaction --no-suggest --prefer-source
- run: composer install --no-interaction
- run: XDEBUG_MODE=coverage php vendor/bin/phpunit --coverage-text
php80:
docker:
- image: circleci/php:8.0-cli-node-browsers
steps:
- checkout
- run: php -v
- run: composer install --no-interaction --no-suggest --prefer-source
- run: composer install --no-interaction
- run: XDEBUG_MODE=coverage php vendor/bin/phpunit --coverage-text
workflows:
version: 2
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@
/phpbench.json
/phpcs.xml
/phpunit.xml
/psalm.xml
/vendor/
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# HTTP cURL client for PHP 7.1+ (incl. PHP 8) based on PSR-18
# Simple HTTP cURL client for PHP 7.1+ based on PSR-18

[![Build Status](https://circleci.com/gh/sunrise-php/http-client-curl.svg?style=shield)](https://circleci.com/gh/sunrise-php/http-client-curl)
[![Code Coverage](https://scrutinizer-ci.com/g/sunrise-php/http-client-curl/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/sunrise-php/http-client-curl/?branch=master)
Expand Down
5 changes: 3 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "sunrise/http-client-curl",
"homepage": "https://github.com/sunrise-php/http-client-curl",
"description": "Sunrise // HTTP cURL client for PHP 7.1+ (incl. PHP 8) based on PSR-18",
"description": "Simple HTTP cURL client for PHP 7.1+ based on PSR-18",
"license": "MIT",
"keywords": [
"fenric",
Expand All @@ -20,7 +20,7 @@
{
"name": "Anatoly Fenric",
"email": "[email protected]",
"homepage": "https://anatoly.fenric.ru/"
"homepage": "https://github.com/fenric"
},
{
"name": "李昀陞 (Peter)",
Expand Down Expand Up @@ -52,6 +52,7 @@
"scripts": {
"test": [
"phpcs",
"psalm",
"XDEBUG_MODE=coverage phpunit --coverage-text --colors=always"
],
"build": [
Expand Down
14 changes: 14 additions & 0 deletions psalm.xml.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0"?>
<psalm
errorLevel="1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
>
<projectFiles>
<directory name="src" />
<ignoreFiles>
<directory name="vendor" />
</ignoreFiles>
</projectFiles>
</psalm>
60 changes: 38 additions & 22 deletions src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
*/
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Sunrise\Http\Client\Curl\Exception\ClientException;
use Sunrise\Http\Client\Curl\Exception\NetworkException;

Expand All @@ -30,18 +30,18 @@
use function curl_exec;
use function curl_getinfo;
use function curl_init;
use function curl_multi_init;
use function curl_multi_exec;
use function curl_multi_add_handle;
use function curl_multi_remove_handle;
use function curl_multi_close;
use function curl_multi_exec;
use function curl_multi_init;
use function curl_multi_remove_handle;
use function curl_setopt_array;
use function explode;
use function in_array;
use function ltrim;
use function sprintf;
use function strpos;
use function substr;
use function trim;

/**
* Import constants
Expand Down Expand Up @@ -136,8 +136,8 @@ public function sendRequests(RequestInterface ...$requests) : array
}

do {
curl_multi_exec($curlMultiHandle, $active);
} while ($active);
curl_multi_exec($curlMultiHandle, $isActive);
} while ($isActive);

$responses = [];
foreach ($curlHandles as $i => $curlHandle) {
Expand All @@ -152,7 +152,7 @@ public function sendRequests(RequestInterface ...$requests) : array
}

/**
* Creates CurlHandle using the given request
* Creates a CurlHandle from the given request
*
* @param RequestInterface $request
*
Expand All @@ -174,6 +174,7 @@ private function createCurlHandleFromRequest(RequestInterface $request)
$curlOptions[CURLOPT_POSTFIELDS] = (string) $request->getBody();
}

$curlOptions[CURLOPT_HTTPHEADER] = [];
foreach ($request->getHeaders() as $name => $values) {
foreach ($values as $value) {
$curlOptions[CURLOPT_HTTPHEADER][] = sprintf('%s: %s', $name, $value);
Expand All @@ -194,29 +195,33 @@ private function createCurlHandleFromRequest(RequestInterface $request)
}

/**
* Creates response using the given CurlHandle
* Creates a response from the given CurlHandle
*
* @param resource $curlHandle
*
* @return ResponseInterface
*/
private function createResponseFromCurlHandle($curlHandle) : ResponseInterface
{
$rescode = curl_getinfo($curlHandle, CURLINFO_RESPONSE_CODE);
$response = $this->responseFactory->createResponse($rescode);
/** @var int */
$statusCode = curl_getinfo($curlHandle, CURLINFO_RESPONSE_CODE);
$response = $this->responseFactory->createResponse($statusCode);

$reqtime = curl_getinfo($curlHandle, CURLINFO_TOTAL_TIME);
$response = $response->withAddedHeader('X-Request-Time', sprintf('%.3f ms', $reqtime * 1000));
/** @var float */
$totalTime = curl_getinfo($curlHandle, CURLINFO_TOTAL_TIME);
$response = $response->withAddedHeader('X-Request-Time', sprintf('%.3f ms', $totalTime * 1000));

/** @var ?string */
$message = curl_multi_getcontent($curlHandle);
if ($message === null) {
return $response;
}

/** @var int */
$headerSize = curl_getinfo($curlHandle, CURLINFO_HEADER_SIZE);

$header = substr($message, 0, $headerSize);
$response = $this->fillResponseWithHeaderFields($response, $header);
$response = $this->populateResponseWithHeaderFields($response, $header);

$body = substr($message, $headerSize);
$response->getBody()->write($body);
Expand All @@ -225,27 +230,38 @@ private function createResponseFromCurlHandle($curlHandle) : ResponseInterface
}

/**
* Fills the given response with the header fields using the given header
* Populates the given response with the given header's fields
*
* @param ResponseInterface $response
* @param string $header
*
* @return ResponseInterface
*
* @link https://datatracker.ietf.org/doc/html/rfc2616#section-4.2
*/
private function fillResponseWithHeaderFields(ResponseInterface $response, string $header) : ResponseInterface
private function populateResponseWithHeaderFields(ResponseInterface $response, string $header) : ResponseInterface
{
$fields = explode("\n", $header);
$fields = explode("\r\n", $header);

foreach ($fields as $field) {
$colpos = strpos($field, ':');
if ($colpos === false) { // Status Line
// status line
if (0 === strpos($field, 'HTTP/')) {
continue;
} elseif ($colpos === 0) { // HTTP/2 Field
}

// HTTP/2 field
if (0 === strpos($field, ':')) {
continue;
}

// end...
if ('' === $field) {
continue;
}

list($name, $value) = explode(':', $field, 2);
[$name, $value] = explode(':', $field, 2);

$response = $response->withAddedHeader(trim($name), trim($value));
$response = $response->withAddedHeader($name, ltrim($value));
}

return $response;
Expand Down
51 changes: 16 additions & 35 deletions tests/ClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,41 +28,28 @@ public function testConstructor()

public function testSendRequest()
{
$url = 'https://raw.githubusercontent.com';
$url .= '/sunrise-php/http-client-curl/dea2ea60d8d5b9f0839d8dba8cd714213c1c2b50/LICENSE';
$client = new Client(new ResponseFactory());
$request = (new RequestFactory)->createRequest('GET', $url);
$request = (new RequestFactory)->createRequest('GET', 'https://www.php.net/');
$response = $client->sendRequest($request);
$this->assertInstanceOf(ResponseInterface::class, $response);
$this->assertSame(200, $response->getStatusCode());
$this->assertSame('text/plain; charset=utf-8', $response->getHeaderLine('content-type'));
$this->assertTrue($response->hasHeader('X-Request-Time'));
}

public function testSendRequests()
{
$requests = [];

$url = 'https://raw.githubusercontent.com';
$url .= '/sunrise-php/http-client-curl/dea2ea60d8d5b9f0839d8dba8cd714213c1c2b50/LICENSE';
$requests[] = (new RequestFactory)->createRequest('GET', $url);

$url = 'https://raw.githubusercontent.com';
$url .= '/sunrise-php/http-client-curl/b52d6cf88186101562ed93d9dbbb909ae82924d4/README.md';
$requests[] = (new RequestFactory)->createRequest('GET', $url);

$client = new Client(new ResponseFactory());
$requests = [];
$requests[] = (new RequestFactory)->createRequest('GET', 'https://www.php.net/');
$requests[] = (new RequestFactory)->createRequest('GET', 'https://www.php.net/');
$responses = $client->sendRequests(...$requests);

$this->assertInstanceOf(ResponseInterface::class, $responses[0]);
$this->assertSame(200, $responses[0]->getStatusCode());
$this->assertSame('OK', $responses[0]->getReasonPhrase());
$this->assertSame('text/plain; charset=utf-8', $responses[0]->getHeaderLine('content-type'));
$this->assertTrue($responses[0]->hasHeader('X-Request-Time'));

$this->assertInstanceOf(ResponseInterface::class, $responses[1]);
$this->assertSame(200, $responses[1]->getStatusCode());
$this->assertSame('text/plain; charset=utf-8', $responses[1]->getHeaderLine('content-type'));
$this->assertTrue($responses[1]->hasHeader('X-Request-Time'));
}

Expand All @@ -78,50 +65,44 @@ public function testSendRequestWithEmptyUri()

public function testClientException()
{
$message = 'foo';
$code = 1;
$previous = new RuntimeException('bar');
$previous = new RuntimeException();

$exception = new ClientException($message, $code, $previous);
$exception = new ClientException('foo', 42, $previous);
$this->assertInstanceOf(RuntimeException::class, $exception);
$this->assertInstanceOf(ClientExceptionInterface::class, $exception);

$this->assertSame($message, $exception->getMessage());
$this->assertSame($code, $exception->getCode());
$this->assertSame('foo', $exception->getMessage());
$this->assertSame(42, $exception->getCode());
$this->assertSame($previous, $exception->getPrevious());
}

public function testNetworkException()
{
$request = (new RequestFactory)->createRequest('GET', 'http://php.net/');
$message = 'foo';
$code = 1;
$previous = new RuntimeException('bar');
$previous = new RuntimeException();

$exception = new NetworkException($request, $message, $code, $previous);
$exception = new NetworkException($request, 'foo', 42, $previous);
$this->assertInstanceOf(ClientException::class, $exception);
$this->assertInstanceOf(NetworkExceptionInterface::class, $exception);

$this->assertSame($request, $exception->getRequest());
$this->assertSame($message, $exception->getMessage());
$this->assertSame($code, $exception->getCode());
$this->assertSame('foo', $exception->getMessage());
$this->assertSame(42, $exception->getCode());
$this->assertSame($previous, $exception->getPrevious());
}

public function testRequestException()
{
$request = (new RequestFactory)->createRequest('GET', 'http://php.net/');
$message = 'foo';
$code = 1;
$previous = new RuntimeException('bar');
$previous = new RuntimeException();

$exception = new RequestException($request, $message, $code, $previous);
$exception = new RequestException($request, 'foo', 42, $previous);
$this->assertInstanceOf(ClientException::class, $exception);
$this->assertInstanceOf(RequestExceptionInterface::class, $exception);

$this->assertSame($request, $exception->getRequest());
$this->assertSame($message, $exception->getMessage());
$this->assertSame($code, $exception->getCode());
$this->assertSame('foo', $exception->getMessage());
$this->assertSame(42, $exception->getCode());
$this->assertSame($previous, $exception->getPrevious());
}
}

0 comments on commit fd63989

Please sign in to comment.