最近研究了一下怎么用Nodejs来写一个http代理服务器,起因就是因为公司网络管理太过严格,服务器访问其他服务器总是要申请权限。 我们NGINX服务器上设置了一些反向代理需要将请求转发到其他的后端服务器,然而我们刚新建的服务器没有权限,但是最近公司IT都放假了,申请权限也来不及了。 跟部门后台大佬讨论了一下,大佬一语点醒了我,他说可以用公司上网代理代理过去。服务器申请了访问上网代理服务器的权限,而且代理服务器也属于公司内网。 下面开始着手写一个http代理服务。

首先先了解一下HTTP代理的原理:

现在知道代理服务器就是浏览器将请求发送到代理服务器,代理服务器再去请求对应的地址。下一个问题就是浏览器是通过什么样的方式去告诉代理服务器目标地址是什么呢?这在《HTTP权威指南》第六章中6.5中有介绍:

客户端向服务器而不是代理发送请求时,HTTP请求报文中的URI会有所不同。客户端向Web服务器发送请求时,请求行中只包含部分URI(没有方案、主机或端口),但当客户端向代理发送请求时,请求行中则包含完整的URI

现在基本清楚了,开始着手撸代码了。

一、创建一个HTTP服务器

根据上面的了解,代理服务器既是一个HTTP服务器也是一个HTTP客户端,所以我们先创建一个HTTP服务器:

// index.js
const http = require('http')

const PORT = 3128
const server = http.createServer()

server.on('request', (req, res) => {
  console.log(req.url)
  let reqBody = ''
  req.on('data', chunk => {
    reqBody += chunk
  })
  req.on('end', () => {
    res.end('Request is receive.')
  })
})

server.on('error', err => {
  console.error(err)
})

server.listen(PORT, () => {
  console.log(`Server is running at port ${PORT}`)
})

运行起来:HTTP服务器运行状态

打开浏览器访问http://localhost:3128:访问http服务器

是我们预期的结果,OK! 上面这个服务器我们是用来做代理服务器的,我们一样画葫芦,再写一个http服务器,用于提供正常Web服务的,新建一个webHttpServer.js文件,并在index.js中导入,我就不再贴代码了,Web服务器运行在80端口。 在hosts文件中创建一条解析记录:127.0.0.1 www.monster.com 设置代理到localhost:3128浏览器图服务器图

这就相当于将到www.monster.com的请求发送到了代理服务器。

二、读取请求头信息,并发送到目标服务器

根据上面对代理服务器原理的介绍,我们需要读取http请求头中的URI(即req.url)和方法:

const http = require('http')
require('./webHttpServer');

const PORT = 3128
const server = http.createServer()

// 接收到客户端的请求
server.on('request', (req, res) => {
  console.log(req.method, req.url)
  // 接收客户端上传的数据
  let reqBody = ''
  req.on('data', chunk => {
    reqBody += chunk
  })
  // 请求完成
  req.on('end', () => {
    // res.end('Request is receive.')
    // 读取真实的URL和请求方法
    const { url, method } = req
    // 向目标服务器发送请求
    const proxyReq = http.request({
      method,
      path: url,
    }, targetRes => {
      // 接收目标服务器的响应数据
      let targetData = ''
      targetRes.on('data', chunk => {
        targetData += chunk
        console.log('data from target server: ', chunk.toString())
      })
      // 响应完毕,开始向客户端发送响应头和响应数据
      targetRes.on('end', () => {
        res.writeHead(
          targetRes.statusCode,
          targetRes.statusMessage,
          targetRes.headers
        )
        res.end(targetData)
      })
      // 监听错误
      targetRes.on('error', err => {
        console.error(err)
        res.writeHead(
          500,
          'Proxy Error'
        )
        res.end()
      })
    })
    // 向目标服务器发送数据
    proxyReq.write(reqBody)
    // 结束向目标服务器的请求
    proxyReq.end()
    console.log('rquest end.')
  })
})

server.on('error', err => {
  console.error(err)
})

server.listen(PORT, () => {
  console.log(`Proxy Server is running at port ${PORT}`)
})

效果:图片描述

图片描述

代理服务器开发完成,大功告成!

作者:|慕标3395159|,原文链接: http://www.imooc.com/article/334580

文章推荐

优秀的流程图应该怎样绘制呢?

AutoGPT:有手就会的安装教程

SpringBoot 自动扫描第三方包及spring.factories失效的问题

iOS APP启动广告实现方式 与 APP唤端调用

使用Ref还是Reactive?

C#写一套最全的MySQL帮助类(包括增删改查)

vue核心原理(Diff算法、虚拟dom)

node的readDirFiles、readFileSync 的一些实操脚本

css三种方案实现图片宽高自适应等比例缩放

【数据结构与算法学习】散列表(Hash Table,哈希表)

Matlab常用图像处理命令108例(二)

MySql触发器使用