PHP-Swoole实现web正向代理服务器

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.实现效果

如无特殊说明,文章均为本站原创,转载请注明出处。如发现有什么不对的地方,希望得到您的指点。

发表评论

电子邮件地址不会被公开。 必填项已用*标注