clue / http-proxy-react
异步HTTP代理连接器,通过HTTP CONNECT代理服务器中继任何基于TCP/IP的协议,基于ReactPHP构建
Requires
- php: >=5.3
- react/promise: ^3 || ^2.1 || ^1.2.1
- react/socket: ^1.12
- ringcentral/psr7: ^1.2
Requires (Dev)
- phpunit/phpunit: ^9.6 || ^5.7 || ^4.8.36
- react/async: ^4 || ^3 || ^2
- react/event-loop: ^1.2
- react/http: ^1.5
- react/promise-timer: ^1.10
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代理服务器向google.com发送安全的HTTPS请求。
<?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客户端实现之外处理。
域名解析
默认情况下,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:[email protected]: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.9
有关版本升级的详细信息,请参阅变更日志
此项目旨在在任何平台上运行,因此不需要任何PHP扩展,并支持在旧版PHP 5.3到当前PHP 8+和HHVM上运行。强烈建议您使用此项目的最新支持版本
测试
要运行测试套件,您首先需要克隆此存储库,然后通过Composer安装所有依赖项(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。