V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
wulv
V2EX  ›  Node.js

NodeJS 如何代理 ws 协议的请求?

  •  
  •   wulv · 6 天前 · 980 次点击

    背景:使用 NodeJS 实现一个代理服务器,把服务器 URL 设置为电脑的全局代理,这样浏览器访问链接,把流量劫持到代理服务器,如何实现代理 http 协议和 ws 协议?比如在浏览器访问 a.com ,可以被代理服务器转发的 b.com ;访问 ws://a.com 可以被转发到 ws://127.0.0.1:8000 目前实现:

    const http = require('http');
    const httpProxy = require('http-proxy');
    
    // 创建一个代理服务器
    const proxy = httpProxy.createProxyServer({
      ws: true, // 启用 WebSocket 支持
      changeOrigin: true // 修改源
    });
    
    // 创建 HTTP 服务器
    const server = http.createServer((req, res) => {
      console.log(`收到 HTTP 请求: ${req.method} ${req.url}`);
      // 处理 HTTP 请求
      proxy.web(req, res, { target: 'http://localhost:8080' }, (err) => {
        if (err) {
          console.error(`HTTP 代理错误: ${err.message}`);
        }
      });
    });
    
    // 处理 WebSocket 连接
    server.on('upgrade', (req, socket, head) => {
      console.log(`收到 WebSocket 升级请求: ${req.url}`);
      proxy.ws(req, socket, head, { target: 'ws://localhost:8080' }, (err) => {
        if (err) {
          console.error(`WebSocket 代理错误: ${err.message}`);
        }
      });
    });
    
    
    // 设置代理服务器监听的端口
    const PORT = 8002;
    
    server.listen(PORT, () => {
      console.log(`代理服务器已启动,正在监听端口 ${PORT}`);
    });
    
    // 错误处理
    proxy.on('error', (err, req, res) => {
      console.error('代理服务器错误:', err);
      if (!res.headersSent) {
        res.writeHead(500, {
          'Content-Type': 'text/plain'
        });
      }
      res.end('代理服务器出错');
    });
    
    // 添加代理事件监听器
    proxy.on('proxyReq', (proxyReq, req, res) => {
      console.log(`代理请求: ${req.method} ${req.url} -> ${proxyReq.path}`);
    });
    
    proxy.on('proxyRes', (proxyRes, req, res) => {
      console.log(`代理响应: ${req.url} - 状态码: ${proxyRes.statusCode}`);
    });
    

    在浏览器使用 SwitchyOmega ,设置代理 http://1270.0.0.1:8002

    访问 http 请求:get http://t.weather.sojson.com/api/weather/city/101030100 可以看到打印日志

    收到 HTTP request 请求: GET http://t.weather.sojson.com/api/weather/city/101030100
    

    但是访问 ws 请求:ws://t.weather.sojson.com 没有打印任何日志,也就是说 upgrade 事件并没有触发。

    问 AI ,回答可能是 WebSocket 握手过程中的协议升级可能在代理服务器之前就已经完成了。代理服务器可能只是在转发已经建立的 WebSocket 连接,而没有参与到协议升级的过程中。

    有没有大佬帮忙分析下? ps: 在设置自签名证书后,可以代理 https 协议和 wss 协议,但是就是 ws 协议没反应。

    5 条回复    2024-10-10 16:32:36 +08:00
    dolorain
        1
    dolorain  
       6 天前
    上周刚写的,我用的 VITE

    server: {
    // 端口号
    port: VITE_PORT,
    host: "0.0.0.0",
    // 本地跨域代理 https://cn.vitejs.dev/config/server-options.html#server-proxy
    proxy: {
    "/api": {
    // 这里填写后端地址
    target: `http://127.0.0.1:8080`,
    changeOrigin: true,
    rewrite: path => path.replace(/^\/api/, "/api/")
    },
    "/socket": {
    target: "ws://127.0.0.1:8080",
    changeOrigin: true,
    ws: true
    }
    },
    // 预热文件以提前转换和缓存结果,降低启动期间的初始页面加载时长并防止转换瀑布
    warmup: {
    clientFiles: ["./index.html", "./src/{views,components}/*"]
    }
    },
    wulv
        2
    wulv  
    OP
       6 天前
    @dolorain 我主要问题是代理服务器接受不到浏览器 ws 请求的流量,如果直接访问 ws://127.0.0.1:8002 ,是没问题的
    itning
        3
    itning  
       6 天前
    建议 Clash -> 你的程序
    这种 因为 http 代理并不支持 ws ,windows 应该也不支持直接配置 socks5
    所以前边套个 clash
    wulv
        4
    wulv  
    OP
       6 天前
    @itning 奇怪的是,wss 请求是可以代理的
    humbass
        5
    humbass  
       6 天前
    思路比较混乱,ws 最少给不同的(子)域名,最差也要给一个路径,如果不区分只能在 程序逻辑中实现了,相当麻烦。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3108 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 12:22 · PVG 20:22 · LAX 05:22 · JFK 08:22
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.