webman代理网络代理进程

<?php
namespace app\process;

use Workerman\Worker;
use Workerman\Connection\TcpConnection;
use Workerman\Connection\AsyncTcpConnection;
use support\Redis;

class Proxy
{
    public static function log($msg)
    {
        echo "[Proxy] " . date('H:i:s') . " $msg\n";
    }

    public function onWorkerStart(Worker $worker)
    {
        self::log("Worker started, socket={$worker->getSocketName()}, transport={$worker->transport}");

        $worker->onMessage = function (TcpConnection $conn, $data) {

            // ---------- TCP 缓冲:累积数据直到收到完整的 HTTP 头 ----------
            if (!isset($conn->proxyBuffer)) {
                $conn->proxyBuffer = '';
            }

            // 跳过空数据
            if (empty(trim($data))) {
                self::log("Empty data, ignoring");
                return;
            }

            $conn->proxyBuffer .= $data;
            self::log("onMessage, recv=" . strlen($data) . ", buffered=" . strlen($conn->proxyBuffer));

            // 取第一行做粗判
            $firstLine = strtok($conn->proxyBuffer, "\r\n");
            if ($firstLine === false) {
                self::log("Incomplete first line, waiting");
                return;
            }
            self::log("First line: " . substr($firstLine, 0, 200));

            // ---------- IP 白名单 ----------
            $ip = $conn->getRemoteIp();
            self::log("Connection from $ip");
            if (!Redis::sismember('ip_whitelist', $ip)) {
                self::log("IP $ip not in whitelist, closing");
                $conn->send("HTTP/1.1 403 Forbidden\r\n\r\n");
                $conn->close();
                return;
            }

            // 检查是否收齐 HTTP 头(\r\n\r\n)
            $headerEnd = strpos($conn->proxyBuffer, "\r\n\r\n");

            // ---------- CONNECT 隧道 ----------
            if (stripos($firstLine, 'CONNECT ') === 0) {
                if ($headerEnd === false) {
                    self::log("CONNECT headers incomplete, waiting...");
                    return;
                }

                if (!preg_match('#^CONNECT\s+([^:\s]+):(\d+)#i', $conn->proxyBuffer, $m)) {
                    self::log("Invalid CONNECT request, closing");
                    $conn->close();
                    return;
                }
                $host = $m[1];
                $port = (int)$m[2];
                self::log("CONNECT -> $host:$port");

                // 头之后的额外数据(如 TLS ClientHello 可能合包到达)
                $extraData = substr($conn->proxyBuffer, $headerEnd + 4);
                $conn->proxyBuffer = '';

                $remote = new AsyncTcpConnection('tcp://127.0.0.1:7892');
                $remote->onConnect = function ($r) use ($conn, $host, $port, $extraData) {
                    self::log("Clash connected for CONNECT $host:$port");
                    $r->send("CONNECT $host:$port HTTP/1.1\r\nHost: $host:$port\r\n\r\n");

                    $buf = '';
                    $r->onMessage = function ($r, $data) use ($conn, &$buf, $extraData) {
                        $buf .= $data;
                        if (strpos($buf, "\r\n\r\n") !== false) {
                            $r->onMessage = null;
                            $conn->send($buf);
                            if (!empty($extraData)) {
                                $r->send($extraData);
                            }
                            $conn->pipe($r);
                            $r->pipe($conn);
                        }
                    };
                };
                $remote->onClose = function () use ($conn) { $conn->close(); };
                $remote->onError = function ($r, $e, $m) use ($conn) {
                    self::log("Clash error: [$e] $m");
                    $conn->send("HTTP/1.1 502 Bad Gateway\r\n\r\n");
                    $conn->close();
                };
                $remote->connect();
                return;
            }

            // ---------- HTTP 代理请求 ----------
            if (preg_match('#^(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)\s+(https?://[^\s]+)\s+HTTP#i', $conn->proxyBuffer, $m)) {
                if ($headerEnd === false) {
                    self::log("HTTP headers incomplete, waiting...");
                    return;
                }

                $buffer = $conn->proxyBuffer;
                $conn->proxyBuffer = '';

                $parsed = parse_url($m[2]);
                $host = $parsed['host'];
                $port = $parsed['port'] ?? 80;
                $path = ($parsed['path'] ?? '/') . (isset($parsed['query']) ? '?' . $parsed['query'] : '');
                self::log("HTTP -> $host:$port $path");

                $buffer = preg_replace('#^(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)\s+https?://[^\s]+\s+HTTP#i', '$1 ' . $path . ' HTTP', $buffer);
                $buffer = preg_replace('/^Proxy-.*\r?\n/mi', '', $buffer);

                $remote = new AsyncTcpConnection('tcp://127.0.0.1:7892');
                $remote->onConnect = function ($r) use ($buffer) { $r->send($buffer); };
                $remote->onMessage = function ($r, $data) use ($conn) { $conn->send($data); };
                $remote->onClose = function () use ($conn) { $conn->close(); };
                $remote->onError = function ($r, $e, $m) use ($conn) {
                    self::log("Clash error: [$e] $m");
                    $conn->send("HTTP/1.1 502 Bad Gateway\r\n\r\n");
                    $conn->close();
                };
                $remote->connect();
                $conn->pipe($remote);
                return;
            }

            // ---------- 无法识别的请求 ----------
            self::log("Unrecognized request, raw: " . bin2hex(substr($conn->proxyBuffer, 0, 50)));
            $conn->proxyBuffer = '';
            $conn->close();
        };

        $worker->onClose = function (TcpConnection $conn) {
            self::log("Connection closed from {$conn->getRemoteIp()}");
        };
    }
}

评论 (0)

发表评论