You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP实现HTTP Proxy:响应CONNECT请求后无后续客户端请求问题排查

问题分析与解决方案

你的核心问题是没有正确处理CONNECT请求的TCP隧道逻辑——CONNECT不是普通的HTTP请求,它的作用是让代理在客户端和目标HTTPS服务器之间建立一个透明的TCP隧道,而非返回响应后就结束连接。

为什么返回200后没收到后续请求?

客户端在收到代理返回的200 Connection Established后,会立即向代理发送TLS握手数据(比如Client Hello),但如果你的代理在返回200后就关闭连接或未监听转发数据,这些TLS数据无法被接收和转发,自然看起来没有后续请求。

正确的处理步骤

  1. 解析CONNECT目标地址:从客户端请求行提取目标主机和端口(比如CONNECT example.com:443 HTTP/1.1里的example.com和443)。
  2. 建立与目标服务器的TCP连接:用PHP套接字函数(如stream_socket_client)连接目标服务器对应端口。
  3. 返回200响应:连接成功后,向客户端返回HTTP/1.1 200 Connection Established\r\n\r\n,注意必须包含HTTP头结束符(两个换行)。
  4. 双向数据透传:保持客户端和目标服务器的套接字连接,实时转发两端数据,直到任意一端关闭连接。

简化代码示例

// 假设已通过套接字获取到客户端连接$clientSocket
$requestLine = fgets($clientSocket);
if (preg_match('/CONNECT (\S+):(\d+) HTTP\/1.1/', $requestLine, $matches)) {
    $targetHost = $matches[1];
    $targetPort = (int)$matches[2];

    // 连接目标服务器
    $targetSocket = stream_socket_client(
        "$targetHost:$targetPort",
        $errno,
        $errstr,
        10 // 连接超时时间
    );

    if (!$targetSocket) {
        // 连接失败,返回错误响应
        fwrite($clientSocket, "HTTP/1.1 503 Service Unavailable\r\n\r\n");
        fclose($clientSocket);
        exit;
    }

    // 返回成功响应,通知客户端可以开始TLS握手
    fwrite($clientSocket, "HTTP/1.1 200 Connection Established\r\n\r\n");

    // 开启双向数据转发
    $sockets = [$clientSocket, $targetSocket];
    while (true) {
        $read = $sockets;
        $write = null;
        $except = null;

        // 监听套接字可读事件
        if (stream_select($read, $write, $except, null) === false) {
            break;
        }

        // 处理客户端到目标服务器的数据
        if (in_array($clientSocket, $read)) {
            $data = fread($clientSocket, 8192);
            if (empty($data)) break;
            fwrite($targetSocket, $data);
        }

        // 处理目标服务器到客户端的数据
        if (in_array($targetSocket, $read)) {
            $data = fread($targetSocket, 8192);
            if (empty($data)) break;
            fwrite($clientSocket, $data);
        }
    }

    // 关闭所有连接
    fclose($clientSocket);
    fclose($targetSocket);
}

注意事项

  • 必须使用CLI模式运行代理脚本,PHP的FastCGI或内置服务器环境不适合处理长连接的隧道转发,会因超时限制中断连接。
  • 可通过stream_set_blocking设置套接字为非阻塞模式,配合stream_select提升转发效率。
  • 要处理异常情况:比如目标服务器连接失败、数据转发出错,及时关闭连接并返回对应HTTP错误码。

内容的提问来源于stack exchange,提问作者Reza Alizade

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 12:51:36