1.前言
之前写过一篇PHP实现正向代理服务器的文章,用原生PHP只实现了http部分,考虑到以后PHP网络编程的发展方向,之后有关网络编程的文章会以swoole实现为主。
2.Web代理介绍
Web代理可以看作是一个中间商,但是它不赚差价,只为人民服务。负责将客户代理(浏览器)请求转发给目标web服务器。客户代理(浏览器)到Web服务器之间可以有多个代理进行转发,具体关于Http的详细介绍可以参考 https://www.pitmanhuang.com/archives/288
3.Web代理分类
1.http/https代理
http代理采用了 Method Absolute-URI Http-Version的请求行格式,数据传输没有加密,明文传输。
https代理采用了隧道机制connect发起请求,当代理与目标web服务器建立tls连接后,需要发送Connection:Established 响应头给客户端以告知连接已建立,可以发起后续的请求。此过程传输是tls加密的。
2.socks代理
它除了支持http以外,还支持tcp/udp的转发等功能,速度快,是http代理的升级版选择。
4.实现Web代理需要做哪些处理?
1.获取浏览器发送过来的http/https请求头
2.请求头预处理
3.转发预处理后的请求头到目标web服务器
https需要先和目标web服务器建立连接,然后通知客户端。
4.读取目标Web服务器的响应头
https在连接建立以后,直接将客户端的数据发送给目标Web服务器
5.将目标Web服务器的响应结果转发给客户代理
因此这些处理需要写一个Web代理服务器,这个服务器即充当服务端的角色,又充当客户端的角色。
5.代码实现
<?php
class WebProxyServer
{
protected $server;
protected $client;
public function __construct($address = '0.0.0.0', $port = 7777)
{
$this->server = new swoole_server('0.0.0.0', 7777);
$this->client = [];
$this->server->set([
'worker_num' => 1,
'reactor_num' => 2,
'backlog' => 128,
'max_request' => 50,
'dispatch_mode' => 4,
'buffer_output_size' => 1024 * 1024 * 20,
'socket_buffer_size' => 1024 * 1024 * 128,
]);
$this->registerEvent();
}
public function log($msg)
{
printf("服务器时间:%s : %s" . PHP_EOL, date('Y-m-d H:i:s', time()), $msg);
}
public function registerEvent()
{
$this->server->on('connect', [$this, 'onConnect']);
$this->server->on('receive', [$this, 'onReceive']);
$this->server->on('close', [$this, 'onClose']);
}
public function onConnect($server, $fd)
{
$this->log("server connect by fd:$fd");
}
public function onReceive($server, $fd, $reactor_id, $buffer)
{
if (!isset($this->client[$fd])) {
list($header, $entity_body) = explode("\r\n\r\n", $buffer, 2);
if (!$header) {
return $server->close($fd);
}
list($method, $uri) = explode(" ", $header, 3);
$this->client[$fd] = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); //异步非阻塞
if (strtolower($method) == 'connect') {
$this->client[$fd]->on('connect', function ($cli) use ($fd) {
$this->log('https连接成功');
$this->server->send($fd, "HTTP/1.1 200 Connection Established\r\n\r\n");
});
} else {
$this->client[$fd]->on('connect', function ($cli) use ($buffer) {
$this->log('http连接成功');
$cli->send($buffer);
});
}
$this->client[$fd]->on('receive', function ($cli, $data) use ($fd) {
if ($this->server->exist($fd)) {
$this->server->send($fd, $data);
}
});
$this->client[$fd]->on('error', function () {
$this->log('tls client error');
});
$this->client[$fd]->on('close', function () {
$this->log('tls client close');
});
$url = parse_url($uri);
if (!isset($url['host'])) {
return $this->server->close($fd);
}
$host = $url['host'];
$port = isset($url['port']) ? $url['port'] : 80;
foreach (swoole_get_local_ip() as $interface => $ip) {
$this->log($ip . '请求访问' . $host . ':' . $port);
}
$this->client[$fd]->connect($host, $port);
} else {
if ($this->client[$fd]->isConnected()) {
$this->client[$fd]->send($buffer);
}
}
}
public function onClose($server, $fd, $reactorId)
{
unset($this->client[$fd]);
}
public function run()
{
$this->server->start();
}
}
$proxy = new WebProxyServer();
$proxy->run();
6.实现效果
