golang gin 代理和改包
说明
目前仅支持http协议,其他协议需要用socket
使用 gin 做客户端和服务端的中间代理
快速搭建环境
- 创建项目文件夹
创建 ginProxy 文件夹后,进入该文件夹
mkdir ginProxy
cd ginProxy
- 初始化mod
ginProxy 为包的名称,在该目录下会生成 go.mod 文件
go mod init ginProxy
- 下载安装 gin 框架
参数 -u 为本地有该包就更新,没有则下载
go get -u github.com/gin-gonic/gin
- 实现 gin 的 hello world
gin 实现代理
代理实现有多种方式
- 直接代理
不做任何修改,直接发往目标服务器
client --> request --> gin --> gin_request --> service --> gin_response --> gin --> response --> client
- 修改代理
修改请求内容再发往目标服务器,收到返回内容修改后再返回给客户端
client --> request --> gin --> modify_request --> gin_request --> service --> gin_response --> gin --> modify_response --> response --> client
- 返回代理
直接返回给客户端,不经过目标服务器,类似私服模式
这个就是传统的请求和回应,把代理当目标服务器
client --> request --> gin --> response --> client
直接代理
不做任何修改,直接把原始请求给代理请求 request = c.Request
c.Request 为 客户端发给 gin 的请求
request为 gin 向目标服务器的请求
router.POST("/test01", func(c *gin.Context) {
proxy := httputil.ReverseProxy{Director: func(request *http.Request) {
request = c.Request
}}
proxy.ServeHTTP(c.Writer, c.Request)
})
修改代理
- 修改请求头
获取请求头
c.GetHeader("sn")
修改请求头
c.Request.Header.Set("sn","123456789")
- 修改请求体
如果加密了需要先解密,修改后再加密
首先需要查看 body 的类型为 io.ReadCloser类型
Body io.ReadCloser
这里有个知识点:post 的 body 数据不是和 header 一起发送的,是分开来发送的。get 是一起发送的
所以需要把 body 数据给读取过来,存到内存中
接着对 body 数据进行操作,读完就可以关闭了
body,err := ioutil.ReadAll(c.Request.Body)
if err != nil{
panic(err)
}
if err := c.Request.Body.Close(); err != nil{
panic(err)
}
// 对 body 数据进行操作
对数据操作完之后,需要把数据发送给目标服务器,即创建 io.ReadCloser
等待服务端读取内存中的数据
也就是说每次请求响应的数据都要存到内存中,比较费内存
c.Request.Body = ioutil.NopCloser(bytes.NewReader(body))
- 修改响应头
如果加密了需要先解密,修改后再加密
目标服务器返回后的回调,response 即为目标服务器的响应
proxy.ModifyResponse = func(response *http.Response) error {
// 修改返回给客户端的 response
return nil
}
响应头和响应体的修改和上面请求的一样,把请求改为响应
比如获取响应头
response.Header.Get("k")
比如设置响应头
response.Header.Set("sn","123456789")
返回代理
标准的请求返回,查看 gin 文档
主要就是返回的内容要符合客户端的加密解密操作