Skip to content

Commit

Permalink
Merge branch 'artax-4-v2'
Browse files Browse the repository at this point in the history
  • Loading branch information
Bilge committed Nov 15, 2020
2 parents f2b5267 + ccd3541 commit c8730d5
Show file tree
Hide file tree
Showing 9 changed files with 74 additions and 67 deletions.
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ addons:
language: php

php:
- 7.1
- 7.2
- 7.3
- 7.4
Expand Down
7 changes: 3 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
],
"license": "LGPL-3.0",
"require": {
"amphp/http-client": "^3",
"php": "^7.2",
"amphp/http-client": "^4.1",
"amphp/http-client-cookies": "^1",
"connectors/ssl": "^2",
"phptype/string": "^1",
"scriptfusion/porter": "^5"
Expand All @@ -35,9 +37,6 @@
"test": "phpunit -c test"
},
"config": {
"platform": {
"php": "7.1.99"
},
"sort-packages": true
}
}
61 changes: 41 additions & 20 deletions src/AsyncHttpConnector.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,20 @@

namespace ScriptFUSION\Porter\Net\Http;

use Amp\Artax\Cookie\ArrayCookieJar;
use Amp\Artax\Cookie\CookieJar;
use Amp\Artax\DefaultClient;
use Amp\Artax\DnsException;
use Amp\Artax\Request;
use Amp\Artax\Response;
use Amp\Artax\SocketException;
use Amp\Artax\TimeoutException;
use Amp\ByteStream\StreamException;
use Amp\Dns\DnsException;
use Amp\Http\Client\Connection\UnlimitedConnectionPool;
use Amp\Http\Client\Cookie\CookieInterceptor;
use Amp\Http\Client\Cookie\CookieJar;
use Amp\Http\Client\Cookie\InMemoryCookieJar;
use Amp\Http\Client\HttpClient;
use Amp\Http\Client\HttpClientBuilder;
use Amp\Http\Client\Request;
use Amp\Http\Client\Response;
use Amp\Http\Client\SocketException;
use Amp\Http\Client\TimeoutException;
use Amp\Promise;
use Amp\Socket\CryptoException;
use Amp\Socket\TlsException;
use ScriptFUSION\Porter\Connector\AsyncConnector;
use ScriptFUSION\Porter\Connector\AsyncDataSource;
use function Amp\call;
Expand All @@ -24,16 +27,20 @@ class AsyncHttpConnector implements AsyncConnector

private $cookieJar;

private $pool;

public function __construct(AsyncHttpOptions $options = null, CookieJar $cookieJar = null)
{
$this->options = $options ?: new AsyncHttpOptions;
$this->cookieJar = $cookieJar ?: new ArrayCookieJar;
$this->cookieJar = $cookieJar ?: new InMemoryCookieJar;
$this->pool = new UnlimitedConnectionPool();
}

public function __clone()
{
$this->options = clone $this->options;
$this->cookieJar = clone $this->cookieJar;
// Sharing the pool is intended and should be harmless.
}

public function fetchAsync(AsyncDataSource $source): Promise
Expand All @@ -43,20 +50,19 @@ public function fetchAsync(AsyncDataSource $source): Promise
throw new \InvalidArgumentException('Source must be of type: AsyncHttpDataSource.');
}

$client = new DefaultClient($this->cookieJar);
$client->setOptions($this->options->extractArtaxOptions());
$client = $this->createClient();

try {
/** @var Response $response */
$response = yield $client->request($this->createRequest($source));
$body = yield $response->getBody();
// Retry HTTP timeouts, socket timeouts, DNS resolution, crypto negotiation and connection reset errors.
} catch (TimeoutException|SocketException|DnsException|CryptoException|StreamException $exception) {
$body = yield $response->getBody()->buffer();
// Retry HTTP timeouts, socket timeouts, DNS resolution, TLS negotiation and connection reset errors.
} catch (TimeoutException|SocketException|DnsException|TlsException|StreamException $exception) {
// Convert exception to recoverable exception.
throw new HttpConnectionException($exception->getMessage(), $exception->getCode(), $exception);
}

$response = HttpResponse::fromArtaxResponse($response, $body);
$response = HttpResponse::fromAmpResponse($response, $body);

$code = $response->getStatusCode();
if ($code < 200 || $code >= 400) {
Expand All @@ -71,14 +77,29 @@ public function fetchAsync(AsyncDataSource $source): Promise
});
}

private function createRequest(AsyncHttpDataSource $source): Request
private function createClient(): HttpClient
{
return (new Request($source->getUrl(), $source->getMethod()))
->withBody($source->getBody())
->withHeaders($source->getHeaders())
return (new HttpClientBuilder())
->usingPool($this->pool)
->interceptNetwork(new CookieInterceptor($this->cookieJar))
->followRedirects($this->options->getMaxRedirects())
// We have our own retry implementation.
->retry(0)
->build()
;
}

private function createRequest(AsyncHttpDataSource $source): Request
{
$request = new Request($source->getUrl(), $source->getMethod());
$request->setBody($source->getBody());
$request->setHeaders($source->getHeaders());
$request->setTransferTimeout($this->options->getTransferTimeout());
$request->setBodySizeLimit($this->options->getMaxBodyLength());

return $request;
}

public function getOptions(): AsyncHttpOptions
{
return $this->options;
Expand Down
2 changes: 1 addition & 1 deletion src/AsyncHttpDataSource.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace ScriptFUSION\Porter\Net\Http;

use Amp\Artax\RequestBody;
use Amp\Http\Client\RequestBody;
use Amp\Promise;
use ScriptFUSION\Porter\Connector\AsyncDataSource;
use function Amp\call;
Expand Down
27 changes: 0 additions & 27 deletions src/AsyncHttpOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@

namespace ScriptFUSION\Porter\Net\Http;

use Amp\Artax\Client;

/**
* Encapsulates async HTTP client options.
*/
Expand All @@ -16,9 +14,6 @@ final class AsyncHttpOptions
// Number of redirects to follow, or 0 to disable redirects.
private $maxRedirects = 5;

// Automatically add a "Referer" header on redirect.
private $autoReferrer = true;

// Maximum body length in bytes. Default 10MiB.
private $maxBodyLength = 0x100000 * 10;

Expand Down Expand Up @@ -46,18 +41,6 @@ public function setMaxRedirects(int $maxRedirects): self
return $this;
}

public function getAutoReferrer(): bool
{
return $this->autoReferrer;
}

public function setAutoReferrer(bool $autoReferer): self
{
$this->autoReferrer = $autoReferer;

return $this;
}

public function getMaxBodyLength(): int
{
return $this->maxBodyLength;
Expand All @@ -69,14 +52,4 @@ public function setMaxBodyLength($maxBodyLength): self

return $this;
}

public function extractArtaxOptions(): array
{
return [
Client::OP_AUTO_REFERER => $this->autoReferrer,
Client::OP_MAX_REDIRECTS => $this->maxRedirects,
Client::OP_TRANSFER_TIMEOUT => $this->transferTimeout,
Client::OP_MAX_BODY_BYTES => $this->maxBodyLength,
];
}
}
12 changes: 6 additions & 6 deletions src/HttpResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace ScriptFUSION\Porter\Net\Http;

use Amp\Artax\Response;
use Amp\Http\Client\Response;
use ScriptFUSION\Type\StringType;

/**
Expand Down Expand Up @@ -38,14 +38,14 @@ public static function fromPhpWrapper(array $headers, string $body = null): self
return $response;
}

public static function fromArtaxResponse(Response $artax, string $body): self
public static function fromAmpResponse(Response $ampResponse, string $body): self
{
$response = new self;

$response->headers = $artax->getHeaders();
$response->version = $artax->getProtocolVersion();
$response->statusCode = $artax->getStatus();
$response->statusPhrase = $artax->getReason();
$response->headers = $ampResponse->getHeaders();
$response->version = $ampResponse->getProtocolVersion();
$response->statusCode = $ampResponse->getStatus();
$response->statusPhrase = $ampResponse->getReason();
$response->body = $body;

return $response;
Expand Down
25 changes: 18 additions & 7 deletions test/Functional/HttpConnectorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@

namespace ScriptFUSIONTest\Functional\Porter\Net\Http;

use Amp\Artax\HttpException;
use Amp\Artax\ParseException;
use Amp\Artax\StringBody;
use Amp\Http\Client\Body\StringBody;
use Amp\Http\Client\HttpException;
use Amp\Http\Cookie\CookieAttributes;
use Amp\Http\Cookie\ResponseCookie;
use PHPUnit\Framework\TestCase;
use ScriptFUSION\Porter\Connector\AsyncDataSource;
use ScriptFUSION\Porter\Connector\Connector;
Expand Down Expand Up @@ -92,6 +93,15 @@ public function testAsyncConnectionToLocalWebserver(): void

$this->connector = new AsyncHttpConnector;

// Test cookies are sent.
$this->connector->getCookieJar()->store(
new ResponseCookie(
$cookieName = uniqid(),
$cookievalue = 'Alfa',
CookieAttributes::default()->withDomain(explode(':', self::HOST)[0])
)
);

try {
$response = $this->fetchAsync(
self::buildAsyncDataSource()
Expand All @@ -109,8 +119,9 @@ public function testAsyncConnectionToLocalWebserver(): void
self::assertSame('1.1', $response->getProtocolVersion());
self::assertTrue($response->hasHeader('x-powered-by'));
self::assertRegExp('[\APOST \Q' . self::HOST . '/' . self::URI . '\E HTTP/\d+\.\d+$]m', $response->getBody());
self::assertRegExp("[^$headerName: $headerValue$]m", $response->getBody());
self::assertStringEndsWith("\n\n$body", $response->getBody());
self::assertRegExp("[^$headerName: $headerValue$]m", $response->getBody(), 'Headers sent.');
self::assertRegExp("[^Cookie: \Q$cookieName=$cookievalue\E$]m", $response->getBody(), 'Cookies sent.');
self::assertStringEndsWith("\n\n$body", $response->getBody(), 'Body sent.');
}

public function testConnectionTimeout(): void
Expand All @@ -129,8 +140,8 @@ public function testErrorResponse(): void
try {
$this->fetch(self::buildDataSource('404.php'));
} catch (HttpServerException $exception) {
$this->assertStringEndsWith('foo', $exception->getMessage());
$this->assertSame('foo', $exception->getResponse()->getBody());
self::assertStringEndsWith('foo', $exception->getMessage());
self::assertSame('foo', $exception->getResponse()->getBody());

throw $exception;
} finally {
Expand Down
4 changes: 4 additions & 0 deletions test/Functional/servers/feedback.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,8 @@
echo "$name: $value\n";
}

foreach ($_COOKIE as $name => $value) {
echo "Cookie: $name=$value\n";
}

echo "\n", file_get_contents('php://input');
2 changes: 1 addition & 1 deletion test/Unit/AsyncHttpDataSourceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace ScriptFUSIONTest\Unit;

use Amp\Artax\StringBody;
use Amp\Http\Client\Body\StringBody;
use Amp\PHPUnit\AsyncTestCase;
use ScriptFUSION\Porter\Net\Http\AsyncHttpDataSource;

Expand Down

0 comments on commit c8730d5

Please sign in to comment.