說明
目前僅支持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 文檔
主要就是返回的內容要符合客戶端的加密解密操作