NodeJS TCP代理服务器改造:实现请求转发与响应回写
简单代理服务器问题
现有运行在8080端口的Node.js TCP代理服务器代码,已将Firefox配置为通过该端口发送HTTP请求,目前能在命令行打印请求头,但需要实现以下功能:
- 将接收的请求数据转发至目标互联网服务器
- 获取目标服务器的响应并写回客户端套接字,实现正常网页浏览
原代码
const net = require('net') const PORT = 8080 var clients = [] let date_ob = new Date() let date = ("0" + date_ob.getDate()).slice(-2) let month = ("0" + (date_ob.getMonth() + 1)).slice(-2) let year = date_ob.getFullYear() let hours = date_ob.getHours() let minutes = date_ob.getMinutes() let seconds = date_ob.getSeconds() const server = net.createServer((socket)=>{ let sock_name = socket.remoteAddress + ":" + socket.remotePort clients.push(socket) socket.on('data', (data)=> { console.log(year + "-" + month + "-" + date + " " + hours + ":" + minutes + ":" + seconds) console.log(sock_name) /*This "data" should send to internet dynamically like java socket applications and get back the respose*/ console.log(""+data) }) socket.on('end', ()=>{ clients.splice(clients.indexOf(socket), 1) console.log(sock_name +" left\n") }) socket.on('error',(error)=>{ console.log(error) }) }).listen(PORT) server.on('error', (error) => { console.log(error) }) console.log("Server running at port 8080\n")
需求
- 将从浏览器接收的请求数据转发至互联网目标服务器
- 将目标服务器的响应写回客户端套接字
解决方案
要实现完整的TCP代理,需要处理普通HTTP请求和HTTPS CONNECT隧道两种场景,核心逻辑是解析目标地址、创建目标服务器连接、双向转发数据。修改后的完整代码如下:
const net = require('net') const PORT = 8080 const server = net.createServer((clientSocket) => { let clientId = `${clientSocket.remoteAddress}:${clientSocket.remotePort}` console.log(`新客户端连接: ${clientId}`) // 处理客户端发送的数据 clientSocket.on('data', (data) => { const dataStr = data.toString() // 优化日志日期格式 const now = new Date().toISOString().slice(0, 19).replace('T', ' ') console.log(`[${now}] ${clientId}`) console.log(dataStr) let targetHost, targetPort // 区分处理HTTPS CONNECT请求和普通HTTP请求 if (dataStr.startsWith('CONNECT')) { // 解析CONNECT请求中的目标地址 const connectMatch = dataStr.match(/CONNECT (\S+):(\d+) HTTP/) if (connectMatch) { targetHost = connectMatch[1] targetPort = parseInt(connectMatch[2]) // 返回隧道建立成功响应 clientSocket.write('HTTP/1.1 200 Connection Established\r\n\r\n') } } else { // 从HTTP请求的Host头解析目标地址 const hostMatch = dataStr.match(/Host: (\S+)/) if (hostMatch) { const hostPart = hostMatch[1].split(':') targetHost = hostPart[0] targetPort = hostPart.length > 1 ? parseInt(hostPart[1]) : 80 } } // 解析失败返回错误响应 if (!targetHost || !targetPort) { clientSocket.end('HTTP/1.1 400 Bad Request\r\n\r\n') return } // 创建到目标服务器的连接 const serverSocket = net.connect({ host: targetHost, port: targetPort }, () => { // 转发客户端数据到目标服务器 serverSocket.write(data) }) // 转发目标服务器响应回客户端 serverSocket.on('data', (serverData) => { clientSocket.write(serverData) }) // 目标服务器连接关闭时,关闭客户端连接 serverSocket.on('end', () => { console.log(`与目标服务器 ${targetHost}:${targetPort} 的连接已关闭`) clientSocket.end() }) // 处理目标服务器连接错误 serverSocket.on('error', (err) => { console.error(`目标服务器连接错误: ${err.message}`) clientSocket.end(`HTTP/1.1 502 Bad Gateway\r\n\r\n`) }) }) // 客户端连接关闭日志 clientSocket.on('end', () => { console.log(`客户端 ${clientId} 已断开连接`) }) // 客户端连接错误处理 clientSocket.on('error', (err) => { console.error(`客户端连接错误: ${err.message}`) }) }).listen(PORT) server.on('error', (err) => { console.error(`代理服务器启动失败: ${err.message}`) }) console.log(`代理服务器运行在端口 ${PORT}`)
关键改动说明
- 日志优化:使用
toISOString()简化日期格式生成,避免冗余代码 - 目标地址解析:分别处理HTTP请求的Host头和HTTPS的CONNECT请求,准确提取目标主机与端口
- 双向转发:建立客户端与目标服务器的双向数据通道,实现请求与响应的转发
- 错误处理:完善各类连接错误的捕获与处理,返回标准HTTP错误码
- HTTPS支持:处理CONNECT请求并建立隧道,支持HTTPS网页访问
内容的提问来源于stack exchange,提问作者user20537415
相关产品推荐
相关产品推荐

