mjcodebase / http-proxy-react
异步 HTTP 代理连接器,通过 HTTP CONNECT 代理服务器隧道任何基于 TCP/IP 的协议,基于 ReactPHP 构建
Requires
- php: >=5.3
- mjcodebase/psr7: ^1.0
- react/promise: ^2.7
- react/socket: ^1.3
Requires (Dev)
- clue/block-react: ^1.5
- phpunit/phpunit: ^9.6 || ^5.7 || ^4.8.36
- react/event-loop: ^1.2
- react/http: ^1.5
README
异步 HTTP 代理连接器,通过 HTTP CONNECT 代理服务器隧道任何基于 TCP/IP 的协议,基于 ReactPHP 构建。
HTTP CONNECT 代理服务器(通常也称为“HTTPS 代理”或“SSL 代理”)通常用于通过中介(“代理”)隧道 HTTPS 流量,以隐藏原始地址(匿名性)或绕过地址阻止(地理封锁)。虽然许多(公共)HTTP CONNECT 代理服务器通常仅限于 HTTPS 端口 443
,但实际上可以使用它来隧道任何基于 TCP/IP 的协议(HTTP、SMTP、IMAP 等)。这个库提供了一个简单的 API 来为您创建这些隧道连接。因为它实现了 ReactPHP 的标准 ConnectorInterface
,它可以简单地替换正常连接器。这使得将 HTTP CONNECT 代理支持添加到几乎所有现有的高级协议实现变得相当简单。
- 异步执行连接 - 并行发送任意数量的 HTTP CONNECT 请求,并在结果到来时立即处理它们的响应。基于 Promise 的设计提供了处理无序响应和可能的连接错误的合理接口。
- 标准接口 - 通过实现 ReactPHP 的标准
ConnectorInterface
,允许轻松集成现有高级组件。 - 轻量级、SOLID 设计 - 提供了一个足够好的薄抽象层,不会干扰您。它建立在经过良好测试的组件和经过良好建立的概念之上,而不是重新发明轮子。
- 良好的测试覆盖率 - 附带自动测试套件,并定期与野外的实际代理服务器进行测试。
目录
支持我们
我们在开发、维护和更新我们出色的开源项目上投入了大量时间。您可以通过 在 GitHub 上成为赞助商 来帮助我们维持我们工作的这一高质量。赞助商将获得许多回报,有关详细信息,请参阅我们的 赞助页面。
让我们共同努力将这些项目提升到下一个水平!🚀
快速入门示例
以下示例代码演示了如何使用此库通过本地 HTTP 代理服务器发送安全 HTTPS 请求到 google.com。
<?php require __DIR__ . '/vendor/autoload.php'; $proxy = new Clue\React\HttpProxy\ProxyConnector('127.0.0.1:8080'); $connector = new React\Socket\Connector(array( 'tcp' => $proxy, 'dns' => false )); $browser = new React\Http\Browser($connector); $browser->get('https://google.com/')->then(function (Psr\Http\Message\ResponseInterface $response) { var_dump($response->getHeaders(), (string) $response->getBody()); }, function (Exception $e) { echo 'Error: ' . $e->getMessage() . PHP_EOL; });
还请参阅 示例。
使用方法
ProxyConnector
ProxyConnector
负责通过使用中介 HTTP CONNECT 代理创建到任何目的地的普通 TCP/IP 连接。
[you] -> [proxy] -> [destination]
它的构造函数简单地接受一个带有代理服务器地址的 HTTP 代理 URL。
$proxy = new Clue\React\HttpProxy\ProxyConnector('127.0.0.1:8080');
代理URL可能包含或不包含方案和端口定义。默认端口对于HTTP是80
(或HTTPS的443
),但许多常见的HTTP代理服务器使用自定义端口(通常是备用HTTP端口8080
)。
如果您需要自定义连接器设置(DNS解析、TLS参数、超时、代理服务器等),您可以显式传递一个自定义的ConnectorInterface
实例
$connector = new React\Socket\Connector(array( 'dns' => '127.0.0.1', 'tcp' => array( 'bindto' => '192.168.10.1:0' ), 'tls' => array( 'verify_peer' => false, 'verify_peer_name' => false ) )); $proxy = new Clue\React\HttpProxy\ProxyConnector('127.0.0.1:8080', $connector);
这是本包中的主要类。因为它实现了ReactPHP的标准ConnectorInterface
,所以它可以简单地替换为正常连接器。相应地,它只提供了一个公共方法,即connect()
方法。使用connect(string $uri): PromiseInterface
方法可以建立流式连接。它返回一个Promise,成功时使用ConnectionInterface满足,错误时使用Exception
拒绝。
这使得向几乎所有高级组件添加HTTP CONNECT代理支持变得相当简单
- $acme = new AcmeApi($connector); + $proxy = new Clue\React\HttpProxy\ProxyConnector('127.0.0.1:8080', $connector); + $acme = new AcmeApi($proxy);
平常的 TCP 连接
HTTP CONNECT代理通常用于向您的目的地发出HTTPS请求。然而,这实际上是在更高协议层上执行的,而这个连接器实际上是一个通用的纯TCP/IP连接器。如上所述,您可以简单地调用它的connect()
方法来建立流式纯TCP/IP连接,并使用任何高级协议,如下所示
$proxy = new Clue\React\HttpProxy\ProxyConnector('127.0.0.1:8080'); $proxy->connect('tcp://smtp.googlemail.com:587')->then(function (React\Socket\ConnectionInterface $connection) { $connection->write("EHLO local\r\n"); $connection->on('data', function ($chunk) use ($connection) { echo $chunk; }); }, function (Exception $e) { echo 'Error: ' . $e->getMessage() . PHP_EOL; });
您可以直接使用ProxyConnector
,或者您可能希望将其包装在ReactPHP的Connector
中
$proxy = new Clue\React\HttpProxy\ProxyConnector('127.0.0.1:8080'); $connector = new React\Socket\Connector(array( 'tcp' => $proxy, 'dns' => false )); $connector->connect('tcp://smtp.googlemail.com:587')->then(function (React\Socket\ConnectionInterface $connection) { $connection->write("EHLO local\r\n"); $connection->on('data', function ($chunk) use ($connection) { echo $chunk; }); }, function (Exception $e) { echo 'Error: ' . $e->getMessage() . PHP_EOL; });
请注意,HTTP CONNECT代理通常会限制可以连接到的端口。许多(公共)代理服务器实际上将此限制为仅HTTPS(443)。
安全 TLS 连接
如果您想与您的目的地建立安全的TLS连接(以前称为SSL),例如使用安全的HTTPS连接到您的目的地网站,此类也可以使用。您只需将此连接器包装在ReactPHP的Connector
中
$proxy = new Clue\React\HttpProxy\ProxyConnector('127.0.0.1:8080'); $connector = new React\Socket\Connector(array( 'tcp' => $proxy, 'dns' => false )); $connector->connect('tls://smtp.googlemail.com:465')->then(function (React\Socket\ConnectionInterface $connection) { $connection->write("EHLO local\r\n"); $connection->on('data', function ($chunk) use ($connection) { echo $chunk; }); }, function (Exception $e) { echo 'Error: ' . $e->getMessage() . PHP_EOL; });
请注意,安全TLS连接实际上完全由HTTP CONNECT客户端实现之外处理。
HTTP 请求
此库还允许您通过HTTP CONNECT代理服务器发送HTTP请求。
为了发送HTTP请求,您首先必须添加对ReactPHP的异步HTTP客户端的依赖。这允许您发送纯HTTP和TLS加密的HTTPS请求,如下所示
$proxy = new Clue\React\HttpProxy\ProxyConnector('127.0.0.1:8080'); $connector = new React\Socket\Connector(array( 'tcp' => $proxy, 'dns' => false )); $browser = new React\Http\Browser($connector); $browser->get('https://example.com/')->then(function (Psr\Http\Message\ResponseInterface $response) { var_dump($response->getHeaders(), (string) $response->getBody()); }, function (Exception $e) { echo 'Error: ' . $e->getMessage() . PHP_EOL; });
有关更多详细信息,请参阅ReactPHP的HTTP客户端和示例。
连接超时
默认情况下,ProxyConnector
不实现任何建立远程连接的超时。您的底层操作系统可能会对挂起和/或空闲的TCP/IP连接施加限制,范围从几分钟到几个小时不等。
许多用例需要对超时进行更多控制,并且可能需要更小的值,通常只有几秒钟。
您可以使用ReactPHP的Connector
装饰任何给定的ConnectorInterface
实例。它提供相同的connect()
方法,但如果底层连接尝试花费太长时间,则会自动拒绝
$proxy = new Clue\React\HttpProxy\ProxyConnector('127.0.0.1:8080'); $connector = new React\Socket\Connector(array( 'tcp' => $proxy, 'dns' => false, 'timeout' => 3.0 )); $connector->connect('tcp://google.com:80')->then(function ($connection) { // connection succeeded within 3.0 seconds }, function (Exception $e) { echo 'Error: ' . $e->getMessage() . PHP_EOL; });
有关示例,请参阅示例。
请注意,连接超时实际上完全由HTTP CONNECT客户端实现之外处理。
DNS 解析
默认情况下,ProxyConnector
不执行任何DNS解析,只是简单地将您试图连接的任何主机名转发到远程代理服务器。因此,远程代理服务器负责通过DNS查找任何主机名(这种默认模式因此被称为远程DNS解析)。
作为替代,您也可以将目标IP发送到远程代理服务器。在这种模式下,您只能使用IP(这通常不可行),或者本地执行任何DNS查找并仅传输解析后的目标IP(这种模式因此被称为本地DNS解析)。
默认的远程DNS解析在您的本地ProxyConnector
无法解析目标主机名(因为它无法直接访问互联网)或其出站DNS流量可能会被拦截时非常有用。
如上所述,ProxyConnector
默认使用远程DNS解析。然而,将ProxyConnector
包裹在ReactPHP的Connector
中实际上执行本地DNS解析,除非明确指定否则。鉴于远程DNS解析被认为是首选模式,所有其他示例都明确禁用DNS解析,如下所示
$proxy = new Clue\React\HttpProxy\ProxyConnector('127.0.0.1:8080'); $connector = new React\Socket\Connector(array( 'tcp' => $proxy, 'dns' => false ));
如果您想显式使用本地DNS解析,可以使用以下代码
$proxy = new Clue\React\HttpProxy\ProxyConnector('127.0.0.1:8080'); // set up Connector which uses Google's public DNS (8.8.8.8) $connector = new React\Socket\Connector(array( 'tcp' => $proxy, 'dns' => '8.8.8.8' ));
注意,本地DNS解析实际上完全由这个HTTP CONNECT客户端实现之外处理。
身份验证
如果您的HTTP代理服务器需要认证,您可以将用户名和密码作为HTTP代理URL的一部分传递,如下所示
$proxy = new Clue\React\HttpProxy\ProxyConnector('alice:password@127.0.0.1:8080');
请注意,如果用户名和密码包含特殊字符,它们必须进行百分编码
$user = 'he:llo'; $pass = 'p@ss'; $url = rawurlencode($user) . ':' . rawurlencode($pass) . '@127.0.0.1:8080'; $proxy = new Clue\React\HttpProxy\ProxyConnector($url);
认证细节将被用于基本认证,并将传输在每个连接尝试的
Proxy-Authorization
HTTP请求头中。如果认证细节缺失或未由远程HTTP代理服务器接受,它应拒绝每个连接尝试,并返回407
(代理认证所需)响应状态码和异常错误码SOCKET_EACCES
(13)。
高级 HTTP 标头
ProxyConnector
构造函数接受一个可选的自定义请求头数组,用于发送到CONNECT
请求。如果您使用的是自定义代理设置或认证方案,如果代理服务器不支持上述文档中记录的基本认证,这很有用。这很少在实际中用到,但对于某些更高级的使用案例可能很有用。在这种情况下,您可以简单地传递一个包含附加请求头的关联数组,如下所示
$proxy = new Clue\React\HttpProxy\ProxyConnector( '127.0.0.1:8080', null, array( 'Proxy-Authorization' => 'Bearer abc123', 'User-Agent' => 'ReactPHP' ) );
高级安全代理连接
请注意,客户端和代理之间的通信通常是通过未加密的、纯TCP/IP HTTP连接进行的。请注意,这是最常见的设置,因为您仍然可以像上面那样与目的地主机建立TLS连接。
如果您想连接到(相当罕见的)HTTPS代理,您可能想使用https://
方案(HTTPS默认端口443)与代理建立安全连接
$proxy = new Clue\React\HttpProxy\ProxyConnector('https://127.0.0.1:443'); $proxy->connect('tcp://smtp.googlemail.com:587');
高级 Unix 域套接字
HTTP CONNECT代理服务器支持转发基于TCP/IP的连接和高级协议。在某些高级情况下,让您的HTTP CONNECT代理服务器监听Unix域套接字(UDS)路径而不是IP:端口组合可能很有用。例如,这允许您依赖于文件系统权限,而不是依赖于显式的认证。
您可以使用以下方式简单地使用http+unix://
URI方案
$proxy = new Clue\React\HttpProxy\ProxyConnector('http+unix:///tmp/proxy.sock'); $proxy->connect('tcp://google.com:80')->then(function (React\Socket\ConnectionInterface $connection) { // connected… }, function (Exception $e) { echo 'Error: ' . $e->getMessage() . PHP_EOL; });
类似地,您也可以将其与认证结合使用,如下所示
$proxy = new Clue\React\HttpProxy\ProxyConnector('http+unix://alice:password@/tmp/proxy.sock');
请注意,Unix域套接字(UDS)被视为高级用法,PHP仅对此有限制支持。特别是,启用安全TLS连接可能不受支持。
请注意,HTTP CONNECT协议不支持UDS路径的概念。上面的方法运行得相当不错,因为UDS仅用于客户端与代理服务器之间的连接,路径实际上并不会通过协议传递。这意味着不支持连接到UDS目标路径。
安装
建议通过Composer安装此库。你是Composer的新手吗?
此项目遵循SemVer。这将安装最新支持的版本
composer require clue/http-proxy-react:^1.8
有关版本升级的详细信息,请参阅变更日志。
此项目旨在在所有平台上运行,因此不需要任何PHP扩展,并支持在旧版PHP 5.3到当前PHP 8+和HHVM上运行。强烈建议为此项目使用最新的支持版本。
测试
要运行测试套件,首先需要克隆此存储库,然后通过Composer安装所有依赖项
composer install
要运行测试套件,请转到项目根目录并运行
vendor/bin/phpunit
测试套件包含依赖于有效互联网连接的测试,您也可以这样运行它
vendor/bin/phpunit --exclude-group internet
许可证
本项目采用宽松的MIT许可证。
你知道吗?我提供定制开发服务,并开具关于发布赞助和贡献的发票。如有需要,请联系我(@clue)了解详情。
更多
- 如果您想了解
ConnectorInterface
及其常用实现的样子,请参阅底层react/socket组件的文档。 - 如果您想了解更多关于处理数据流的信息,请参阅底层react/stream组件的文档。
- 作为HTTP CONNECT代理的替代方案,您可能还希望考虑使用SOCKS(SOCKS4/SOCKS5)代理。您可以使用clue/reactphp-socks,它还提供了相同的
ConnectorInterface
实现,因此支持任何代理协议应该相当简单。 - 作为HTTP CONNECT代理的替代方案,您可能还希望考虑使用SSH代理(SSH隧道)代替。您可以使用clue/reactphp-ssh-proxy,它还提供了相同的
ConnectorInterface
实现,因此支持任何代理协议应该相当简单。 - 如果您正在处理公共代理,您可能不得不与质量参差不齐且不可靠的代理一起工作。您可能想考虑使用clue/reactphp-connection-manager-extra,它允许重试不可靠的代理,意味着连接超时、同时使用多个连接器等等。
- 如果您正在寻找面向最终用户的HTTP CONNECT代理服务器守护程序,您可能想使用LeProxy。