Skip to content

Commit

Permalink
Merge e4362e8 into d4f24c9
Browse files Browse the repository at this point in the history
  • Loading branch information
remorhaz committed Jun 16, 2020
2 parents d4f24c9 + e4362e8 commit 5f65ed8
Show file tree
Hide file tree
Showing 15 changed files with 1,293 additions and 41 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Expand Up @@ -3,4 +3,6 @@ vendor
.idea
composer.lock
.php_cs.cache
.phpunit.result.cache
.phpunit.result.cache
/phpunit.xml
/psalm.xml
3 changes: 2 additions & 1 deletion .travis.yml
Expand Up @@ -28,14 +28,15 @@ install:
fi

- wget https://github.com/php-coveralls/php-coveralls/releases/download/v2.2.0/php-coveralls.phar
- wget https://github.com/maglnet/ComposerRequireChecker/releases/download/2.0.0/composer-require-checker.phar
- wget https://github.com/maglnet/ComposerRequireChecker/releases/download/2.1.0/composer-require-checker.phar

- composer show

script:
- vendor/bin/phpunit --coverage-text --verbose --coverage-clover build/logs/clover.xml;
- php composer-require-checker.phar check composer.json --config-file $PWD/composer-require-check.json
- PHP_CS_FIXER_IGNORE_ENV=1 php vendor/bin/php-cs-fixer --diff --dry-run -v fix
- vendor/bin/psalm

after_script:
- travis_retry php php-coveralls.phar -v
Expand Down
36 changes: 34 additions & 2 deletions README.md
Expand Up @@ -16,14 +16,46 @@ composer require amphp/http-client-psr7

## Usage

TODO
Create `Amp\Http\Client\Psr7\PsrAdapter` instance to convert client requests and responses between native Amp and PSR-7 formats. Adapter doesn't depend on any concrete PSR-7 implementation, so it requires PSR-17 factory interfaces to create PSR-7 requests and responses.

```php
<?php

// TODO
use Amp\Http\Client\Psr7\PsrAdapter;
use Amp\Loop;
use Laminas\Diactoros\RequestFactory;
use Laminas\Diactoros\ResponseFactory;

Loop::run(function () {
$psrAdapter = new PsrAdapter();

// PSR-17 request factory
$psrRequestFactory = new RequestFactory();
// PSR-17 response factory
$psrResponseFactory = new ResponseFactory();

// Convert PSR-7 request to Amp request
$psrRequest = $psrRequestFactory->createRequest('GET', 'https://google.com/');
$ampRequest = yield $psrAdapter->fromPsrRequest($psrRequest);

// Convert Amp request to PSR-7 request
$psrRequest = yield $psrAdapter->toPsrRequest($psrRequestFactory, $ampRequest);

// Convert PSR-7 response to Amp response
$psrResponse = $psrResponseFactory->createResponse();
$ampResponse = yield $psrAdapter->fromPsrResponse($psrResponse, $ampRequest);

// Convert Amp response to PSR-7 response
$psrResponse = yield $psrAdapter->toPsrResponse($psrResponseFactory, $ampResponse);
});

```

There are few incompatibilities between Amp and PSR-7 implementations that may requre special handling:

- PSR-7 request contains only one protocol version, but Amp request can contain several. In this case adapter checks if protocol version list contains version that is default for current PSR-7 implementation, otherwise it throws an exception. You may also set protocol version explicitly using optional argument of `toPsrRequest()` method.
- Amp response contains request istance, but PSR-7 response doesn't; so you need to provide request instance explicitly.

## Examples

More extensive code examples reside in the [`examples`](./examples) directory.
Expand Down
15 changes: 12 additions & 3 deletions composer.json
Expand Up @@ -15,20 +15,29 @@
{
"name": "Niklas Keller",
"email": "me@kelunik.com"
},
{
"name": "Edward Surov",
"email": "zoohie@gmail.com"
}
],
"require": {
"php": ">=7.2",
"amphp/amp": "^2.3",
"amphp/http": "^1.5",
"amphp/http-client": "^4",
"psr/http-message": "^1"
"amphp/byte-stream": "^1.7",
"psr/http-message": "^1",
"psr/http-factory": "^1"
},
"require-dev": {
"amphp/phpunit-util": "^1.4",
"amphp/php-cs-fixer-config": "dev-master",
"phpunit/phpunit": "^7 || ^8 || ^9",
"friendsofphp/php-cs-fixer": "^2.3"
"phpunit/phpunit": "^8 || ^9",
"friendsofphp/php-cs-fixer": "^2.3",
"laminas/laminas-diactoros": "^2.3",
"guzzlehttp/guzzle": "7.0.0-beta.2",
"vimeo/psalm": "^3.11"
},
"autoload": {
"psr-4": {
Expand Down
45 changes: 35 additions & 10 deletions examples/basic.php
@@ -1,21 +1,46 @@
<?php

use Amp\Http\Client\HttpClientBuilder;
use Amp\Http\Client\Psr7\PsrAdapter;
use Amp\Http\Client\Request;
use Amp\Http\Client\Response;
use Amp\Loop;
use GuzzleHttp\Client;
use Laminas\Diactoros\RequestFactory;
use Laminas\Diactoros\ResponseFactory;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

require __DIR__ . '/../vendor/autoload.php';

Loop::run(static function () {
$httpClient = (new HttpClientBuilder)
->build();
Loop::run(
static function () {
$httpClient = (new HttpClientBuilder)
->build();

/** @var Response $firstResponse */
$firstResponse = yield $httpClient->request(new Request('https://google.com/'));
yield $firstResponse->getBody()->buffer();
$psrAdapter = new PsrAdapter();
$psrResponseFactory = new ResponseFactory();
$psrRequestFactory = new RequestFactory();

/** @var Response $secondResponse */
$secondResponse = yield $httpClient->request(new Request('https://google.com/'));
yield $secondResponse->getBody()->buffer();
});
$firstPsrRequest = $psrRequestFactory->createRequest('GET', 'https://google.com/');
/** @var Request $firstAmpRequest */
$firstAmpRequest = yield $psrAdapter->fromPsrRequest($firstPsrRequest);
// TODO: Investigate if this client bug or if Host header must be cleaned automatically
$firstAmpRequest->removeHeader('Host');
/** @var Response $firstAmpResponse */
$firstAmpResponse = yield $httpClient->request($firstAmpRequest);
/** @var ResponseInterface $firstPsrResponse */
$firstPsrResponse = yield $psrAdapter->toPsrResponse($psrResponseFactory, $firstAmpResponse);
$body = $firstPsrResponse->getBody();
$body->rewind();
$body->getContents();

$secondAmpRequest = new Request('https://google.com/');
/** @var RequestInterface $secondPsrRequest */
$secondPsrRequest = yield $psrAdapter->toPsrRequest($psrRequestFactory, $secondAmpRequest);
$secondPsrResponse = (new Client)->send($secondPsrRequest);
/** @var Response $secondAmpResponse */
$secondAmpResponse = yield $psrAdapter->fromPsrResponse($secondPsrResponse, $secondAmpRequest);
yield $secondAmpResponse->getBody()->buffer();
}
);
16 changes: 16 additions & 0 deletions psalm.xml.dist
@@ -0,0 +1,16 @@
<?xml version="1.0"?>
<psalm
phpVersion="7.2"
resolveFromConfigFile="true"
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="examples" />
<directory name="src" />
<ignoreFiles>
<directory name="vendor" />
</ignoreFiles>
</projectFiles>
</psalm>
24 changes: 0 additions & 24 deletions src/DummyInterceptor.php

This file was deleted.

60 changes: 60 additions & 0 deletions src/Internal/PsrInputStream.php
@@ -0,0 +1,60 @@
<?php

namespace Amp\Http\Client\Psr7\Internal;

use Amp\ByteStream\InputStream;
use Amp\Promise;
use Amp\Success;
use Psr\Http\Message\StreamInterface;

/**
* @internal
*/
final class PsrInputStream implements InputStream
{
private const DEFAULT_CHUNK_SIZE = 8192;

/**
* @var StreamInterface
*/
private $stream;

/**
* @var int
*/
private $chunkSize;

/**
* @var bool
*/
private $tryRewind = true;

public function __construct(StreamInterface $stream, int $chunkSize = self::DEFAULT_CHUNK_SIZE)
{
if ($chunkSize < 1) {
throw new \Error("Invalid chunk size: {$chunkSize}");
}
$this->stream = $stream;
$this->chunkSize = $chunkSize;
}

public function read(): Promise
{
if (!$this->stream->isReadable()) {
return new Success();
}
if ($this->tryRewind) {
$this->tryRewind = false;
if ($this->stream->isSeekable()) {
$this->stream->rewind();
}
}
if ($this->stream->eof()) {
return new Success();
}

$data = $this->stream->read($this->chunkSize);

return new Success($data);
}
}

0 comments on commit 5f65ed8

Please sign in to comment.