Skip to content

Commit

Permalink
Upgrade to Amp v3
Browse files Browse the repository at this point in the history
  • Loading branch information
kelunik committed May 7, 2023
1 parent 074b70d commit ea27736
Show file tree
Hide file tree
Showing 12 changed files with 195 additions and 280 deletions.
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@ coverage
vendor
.idea
composer.lock
.php_cs.cache
.phpunit.result.cache
.*.cache
*.har
4 changes: 1 addition & 3 deletions .php_cs.dist → .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
->in(__DIR__ . '/src')
->in(__DIR__ . '/test');

$cacheDir = getenv('TRAVIS') ? getenv('HOME') . '/.php-cs-fixer' : __DIR__;

$config->setCacheFile($cacheDir . '/.php_cs.cache');
$config->setCacheFile(__DIR__ . '/.php-cs-fixer.cache');

return $config;
50 changes: 0 additions & 50 deletions .travis.yml

This file was deleted.

2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2019 amphp
Copyright (c) 2019-2023 amphp

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
Expand Down
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# http-tunnel

[![Build Status](https://img.shields.io/travis/amphp/http-tunnel/master.svg?style=flat-square)](https://travis-ci.org/amphp/http-tunnel)
[![CoverageStatus](https://img.shields.io/coveralls/amphp/http-tunnel/master.svg?style=flat-square)](https://coveralls.io/github/amphp/http-tunnel?branch=master)
![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)

This package provides an HTTP `CONNECT` tunnel for PHP based on [Amp](https://github.com/amphp/amp).
Expand Down
18 changes: 9 additions & 9 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,18 @@
}
],
"require": {
"php": ">=7.2",
"amphp/amp": "^2.4",
"amphp/byte-stream": "^1.7",
"amphp/http-client": "^4",
"amphp/socket": "^1"
"php": ">=8.1",
"amphp/amp": "^3",
"amphp/byte-stream": "^2",
"amphp/http-client": "^5-beta",
"amphp/socket": "^2"
},
"require-dev": {
"amphp/phpunit-util": "^1.1",
"amphp/php-cs-fixer-config": "dev-master",
"phpunit/phpunit": "^7 || ^8",
"amphp/phpunit-util": "^3",
"amphp/php-cs-fixer-config": "^2",
"phpunit/phpunit": "^9",
"leproxy/leproxy": "^0.2.2",
"amphp/react-adapter": "^2.1"
"revolt/event-loop-adapter-react": "^1"
},
"autoload": {
"psr-4": {
Expand Down
88 changes: 41 additions & 47 deletions examples/http-client-via-proxy.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,66 +5,60 @@
use Amp\Http\Client\HttpClientBuilder;
use Amp\Http\Client\HttpException;
use Amp\Http\Client\Request;
use Amp\Http\Client\Response;
use Amp\Http\Rfc7230;
use Amp\Http\Http1\Rfc7230;
use Amp\Http\Tunnel\Http1TunnelConnector;
use Amp\Loop;
use Amp\Socket\SocketAddress;

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

Loop::run(static function () use ($argv) {
try {
// If you need authentication, you can set a custom header (using Basic auth here)
// $connector = new Http1TunnelConnector(new SocketAddress('127.0.0.1', 5512), [
// 'proxy-authorization' => 'Basic ' . \base64_encode('user:pass'),
// ]);
try {
// If you need authentication, you can set a custom header (using Basic auth here)
// $connector = new Http1TunnelConnector(new SocketAddress('127.0.0.1', 5512), [
// 'proxy-authorization' => 'Basic ' . \base64_encode('user:pass'),
// ]);

// If you have a proxy accepting HTTPS connections, you need to use Https1TunnelConnector instead:
// $connector = new Https1TunnelConnector(new SocketAddress('proxy.example.com', 5512));
$connector = new Http1TunnelConnector(new SocketAddress('127.0.0.1', 5512));
// If you have a proxy accepting HTTPS connections, you need to use Https1TunnelConnector instead:
// $connector = new Https1TunnelConnector(new SocketAddress('proxy.example.com', 5512));
$socketConnector = new Http1TunnelConnector('127.0.0.1:5512');

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

$request = new Request('http://amphp.org/');
$request = new Request('http://amphp.org/');

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

$request = $response->getRequest();
$request = $response->getRequest();

\printf(
"%s %s HTTP/%s\r\n",
$request->getMethod(),
$request->getUri(),
\implode('+', $request->getProtocolVersions())
);
\printf(
"%s %s HTTP/%s\r\n",
$request->getMethod(),
$request->getUri(),
\implode('+', $request->getProtocolVersions())
);

print Rfc7230::formatHeaders($request->getHeaders()) . "\r\n\r\n";
print Rfc7230::formatHeaders($request->getHeaders()) . "\r\n\r\n";

\printf(
"HTTP/%s %d %s\r\n",
$response->getProtocolVersion(),
$response->getStatus(),
$response->getReason()
);
\printf(
"HTTP/%s %d %s\r\n",
$response->getProtocolVersion(),
$response->getStatus(),
$response->getReason()
);

print Rfc7230::formatHeaders($response->getHeaders()) . "\r\n\r\n";
print Rfc7230::formatHeaders($response->getHeaders()) . "\r\n\r\n";

$body = yield $response->getBody()->buffer();
$bodyLength = \strlen($body);
$body = $response->getBody()->buffer();
$bodyLength = \strlen($body);

if ($bodyLength < 250) {
print $body . "\r\n";
} else {
print \substr($body, 0, 250) . "\r\n\r\n";
print($bodyLength - 250) . " more bytes\r\n";
}
} 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;
if ($bodyLength < 250) {
print $body . "\r\n";
} else {
print \substr($body, 0, 250) . "\r\n\r\n";
print($bodyLength - 250) . " more bytes\r\n";
}
});
} 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;
}
82 changes: 35 additions & 47 deletions src/Http1TunnelConnector.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,81 +2,69 @@

namespace Amp\Http\Tunnel;

use Amp\CancellationToken;
use Amp\Cancellation;
use Amp\ForbidCloning;
use Amp\ForbidSerialization;
use Amp\Http\Client\Connection\Http1Connection;
use Amp\Http\Client\Connection\Stream;
use Amp\Http\Client\Internal\ForbidCloning;
use Amp\Http\Client\Internal\ForbidSerialization;
use Amp\Http\Client\Request;
use Amp\Http\Client\Response;
use Amp\NullCancellationToken;
use Amp\Promise;
use Amp\NullCancellation;
use Amp\Socket\ConnectContext;
use Amp\Socket\ConnectException;
use Amp\Socket\Connector;
use Amp\Socket\EncryptableSocket;
use Amp\Socket\Socket;
use Amp\Socket\SocketAddress;
use function Amp\call;
use function Amp\Socket\connector;
use Amp\Socket\SocketConnector;
use function Amp\Socket\socketConnector;

final class Http1TunnelConnector implements Connector
final class Http1TunnelConnector implements SocketConnector
{
use ForbidCloning;
use ForbidSerialization;

public static function tunnel(
EncryptableSocket $socket,
Socket $socket,
string $target,
array $customHeaders,
CancellationToken $cancellationToken
): Promise {
return call(static function () use ($socket, $target, $customHeaders, $cancellationToken) {
$request = new Request('http://' . \str_replace('tcp://', '', $target), 'CONNECT');
$request->setHeaders($customHeaders);
$request->setUpgradeHandler(static function (EncryptableSocket $socket) use (&$upgradedSocket) {
$upgradedSocket = $socket;
});

$connection = new Http1Connection($socket, 1000);
Cancellation $cancellation
): Socket {
$request = new Request('http://' . \str_replace('tcp://', '', $target), 'CONNECT');
$request->setHeaders($customHeaders);
$request->setUpgradeHandler(static function (Socket $socket) use (&$upgradedSocket) {
$upgradedSocket = $socket;
});

/** @var Stream $stream */
$stream = yield $connection->getStream($request);
$connection = new Http1Connection($socket, 1000);

/** @var Response $response */
$response = yield $stream->request($request, $cancellationToken);
/** @var Stream $stream */
$stream = $connection->getStream($request);
$response = $stream->request($request, $cancellation);

if ($response->getStatus() !== 200) {
throw new ConnectException('Failed to connect to proxy: Received a bad status code (' . $response->getStatus() . ')');
}
if ($response->getStatus() !== 200) {
throw new ConnectException('Failed to connect to proxy: Received a bad status code (' . $response->getStatus() . ')');
}

\assert($upgradedSocket !== null);
\assert($upgradedSocket !== null);

return $upgradedSocket;
});
return $upgradedSocket;
}

/** @var string */
private $proxyUri;
/** @var array */
private $customHeaders;
/** @var Connector|null */
private $connector;
private string $proxyAddress;
private array $customHeaders;
private ?SocketConnector $socketConnector;

public function __construct(SocketAddress $proxyAddress, array $customHeaders = [], ?Connector $connector = null)
public function __construct(string $proxyAddress, array $customHeaders = [], ?SocketConnector $socketConnector = null)
{
$this->proxyUri = (string) $proxyAddress;
$this->proxyAddress = $proxyAddress;
$this->customHeaders = $customHeaders;
$this->connector = $connector;
$this->socketConnector = $socketConnector;
}

public function connect(string $uri, ?ConnectContext $context = null, ?CancellationToken $token = null): Promise
public function connect(SocketAddress|string $uri, ?ConnectContext $context = null, ?Cancellation $cancellation = null): Socket
{
return call(function () use ($uri, $context, $token) {
$connector = $this->connector ?? connector();
$connector = $this->socketConnector ?? socketConnector();

$socket = yield $connector->connect($this->proxyUri, $context, $token);
$socket = $connector->connect($this->proxyAddress, $context, $cancellation);

return self::tunnel($socket, $uri, $this->customHeaders, $token ?? new NullCancellationToken);
});
return self::tunnel($socket, $uri, $this->customHeaders, $cancellation ?? new NullCancellation());
}
}
Loading

0 comments on commit ea27736

Please sign in to comment.