clue / http-proxy-react

异步HTTP代理连接器,通过HTTP CONNECT代理服务器中继任何基于TCP/IP的协议,基于ReactPHP构建

v1.9.0 2024-04-10 14:46 UTC

This package is auto-updated.

Last update: 2024-09-10 15:37:38 UTC


README

CI status installs on Packagist

异步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