Skip to content

Commit

Permalink
Merge 110d87f into 52762dc
Browse files Browse the repository at this point in the history
  • Loading branch information
kelunik committed Dec 3, 2019
2 parents 52762dc + 110d87f commit ed7e7f0
Show file tree
Hide file tree
Showing 30 changed files with 2,762 additions and 1,965 deletions.
2 changes: 2 additions & 0 deletions README.md
Expand Up @@ -41,6 +41,8 @@ More extensive code examples reside in the [`examples`](./examples) directory.

`amphp/http-client` follows the [semver](http://semver.org/) semantic versioning specification like all other `amphp` packages.

Everything in an `Internal` namespace or marked as `@internal` is not public API and therefore not covered by BC guarantees.

##### 4.x

Under development.
Expand Down
8 changes: 4 additions & 4 deletions composer.json
@@ -1,12 +1,12 @@
{
"name": "amphp/http-client",
"homepage": "https://github.com/amphp/http-client",
"description": "Asynchronous parallel HTTP/2 and HTTP/1.1 client built on the Amp concurrency framework",
"description": "Asynchronous concurrent HTTP/2 and HTTP/1.1 client built on the Amp concurrency framework",
"keywords": [
"http",
"rest",
"client",
"parallel",
"concurrent",
"async",
"non-blocking"
],
Expand All @@ -30,7 +30,7 @@
"amphp/amp": "^2.4",
"amphp/byte-stream": "^1.6",
"amphp/hpack": "^2",
"amphp/http": "^1.3",
"amphp/http": "^1.5",
"amphp/socket": "^1",
"amphp/sync": "^1.3",
"league/uri": "^6",
Expand All @@ -47,7 +47,7 @@
"suggest": {
"ext-zlib": "*",
"ext-json": "*",
"amphp/file": "Required for file request bodies"
"amphp/file": "Required for file request bodies and HTTP archive logging"
},
"autoload": {
"psr-4": {
Expand Down
3 changes: 2 additions & 1 deletion examples/basic/5-unix-sockets.php
@@ -1,5 +1,6 @@
<?php

use Amp\Http\Client\Connection\DefaultConnectionFactory;
use Amp\Http\Client\Connection\UnlimitedConnectionPool;
use Amp\Http\Client\HttpClientBuilder;
use Amp\Http\Client\HttpException;
Expand All @@ -17,7 +18,7 @@
$connector = new StaticConnector("unix:///var/run/docker.sock", new DnsConnector);

$client = (new HttpClientBuilder)
->usingPool(new UnlimitedConnectionPool($connector))
->usingPool(new UnlimitedConnectionPool(new DefaultConnectionFactory($connector)))
->build();

// amphp/http-client requires a host, so just use a dummy one.
Expand Down
3 changes: 2 additions & 1 deletion examples/concurrency/3-benchmark.php
Expand Up @@ -5,6 +5,7 @@
// Infinite (10 x 100 requests): php examples/concurrency/3-benchmark.php 0
// Custom (10 x $count requests): php examples/concurrency/3-benchmark.php $count

use Amp\Http\Client\Connection\DefaultConnectionFactory;
use Amp\Http\Client\Connection\UnlimitedConnectionPool;
use Amp\Http\Client\HttpClientBuilder;
use Amp\Http\Client\Request;
Expand All @@ -28,7 +29,7 @@
->withTlsContext($tlsContext);

$client = (new HttpClientBuilder)
->usingPool(new UnlimitedConnectionPool(null, $connectContext))
->usingPool(new UnlimitedConnectionPool(new DefaultConnectionFactory(null, $connectContext)))
->build();

$handler = coroutine(static function (int $count) use ($client, $argv) {
Expand Down
79 changes: 79 additions & 0 deletions examples/streaming/2-http1-http2.php
@@ -0,0 +1,79 @@
<?php


use Amp\File\File;
use Amp\File\StatCache;
use Amp\Http\Client\HttpClientBuilder;
use Amp\Http\Client\HttpException;
use Amp\Http\Client\Request;
use Amp\Http\Client\Response;
use Amp\Loop;
use function Amp\getCurrentTime;

require __DIR__ . '/../.helper/functions.php';

// https://stackoverflow.com/a/2510540/2373138
function formatBytes(int $size, int $precision = 2)
{
$base = \log($size, 1024);
$suffixes = ['bytes', 'kB', 'MB', 'GB', 'TB'];

return \round(1024 ** ($base - \floor($base)), $precision) . ' ' . $suffixes[(int) $base];
}

function fetch(string $uri, array $protocolVersions): \Generator
{
try {
$start = getCurrentTime();

// Instantiate the HTTP client
$client = HttpClientBuilder::buildDefault();

$request = new Request($uri);
$request->setProtocolVersions($protocolVersions);
$request->setBodySizeLimit(16 * 1024 * 1024); // 128 MB
$request->setTransferTimeout(120 * 1000); // 120 seconds

/** @var Response $response */
$response = yield $client->request($request);

print "\n";

$path = \tempnam(\sys_get_temp_dir(), "artax-streaming-");

/** @var File $file */
$file = yield Amp\File\open($path, "w");

$bytes = 0;

while (null !== $chunk = yield $response->getBody()->read()) {
yield $file->write($chunk);
$bytes += \strlen($chunk);

print "\r" . formatBytes($bytes) . ' '; // blanks to remove previous output
}

yield $file->close();

print \sprintf(
"\rDone in %.2f seconds with peak memory usage of %.2fMB.\n",
(getCurrentTime() - $start) / 1000,
(float) \memory_get_peak_usage(true) / 1024 / 1024
);

// We need to clear the stat cache, as we have just written to the file
StatCache::clear($path);
$size = yield Amp\File\size($path);

print \sprintf("%s has a size of %.2fMB\r\n", $path, (float) $size / 1024 / 1024);
} catch (HttpException $error) {
// If something goes wrong Amp will throw the exception where the promise was yielded.
// The HttpClient::request() method itself will never throw directly, but returns a promise.
echo $error;
}
}

Loop::run(static function () {
yield from fetch('http://1153288396.rsc.cdn77.org//img/cdn77-test-14mb.jpg', ['1.1']);
yield from fetch('https://1906714720.rsc.cdn77.org/img/cdn77-test-14mb.jpg', ['2']);
});
27 changes: 27 additions & 0 deletions src/Connection/ConnectionFactory.php
@@ -0,0 +1,27 @@
<?php

namespace Amp\Http\Client\Connection;

use Amp\CancellationToken;
use Amp\Http\Client\Request;
use Amp\Promise;

interface ConnectionFactory
{
/**
* During connection establishment, the factory must call the {@see EventListener::startConnectionCreation()},
* {@see EventListener::startTlsNegotiation()}, {@see EventListener::completeTlsNegotiation()}, and
* {@see EventListener::completeConnectionCreation()} on all event listeners registered on the given request in the
* order defined by {@see Request::getEventListeners()} as appropriate (TLS events are only invoked if TLS is
* used). Before calling the next listener, the promise returned from the previous one must resolve successfully.
*
* Additionally, the factory may invoke {@see EventListener::startDnsResolution()} and
* {@see EventListener::completeDnsResolution()}, but is not required to implement such granular events.
*
* @param Request $request
* @param CancellationToken $cancellationToken
*
* @return Promise
*/
public function create(Request $request, CancellationToken $cancellationToken): Promise;
}
10 changes: 0 additions & 10 deletions src/Connection/ConnectionPool.php
Expand Up @@ -3,7 +3,6 @@
namespace Amp\Http\Client\Connection;

use Amp\CancellationToken;
use Amp\Http\Client\EventListener;
use Amp\Http\Client\Request;
use Amp\Promise;

Expand All @@ -12,15 +11,6 @@ interface ConnectionPool
/**
* Reserve a stream for a particular request.
*
* During connection establishment, the pool must call the {@see EventListener::startConnectionCreation()},
* {@see EventListener::startTlsNegotiation()}, and {@see EventListener::completeTlsNegotiation()} on all event
* listeners registered on the given request in the order defined by {@see Request::getEventListeners()} as
* appropriate. Before calling the next listener, the promise returned from the previous one must resolve
* successfully.
*
* Additionally, the pool may invoke {@see EventListener::startDnsResolution()} and
* {@see EventListener::completeDnsResolution()}, but is not required to implement such granular events.
*
* @param Request $request
* @param CancellationToken $token
*
Expand Down

0 comments on commit ed7e7f0

Please sign in to comment.