golang反向代理設(shè)置host不生效的問題解決
一、背景
在使用golang
的httputil
做反向代理的時候,發(fā)現(xiàn)一個奇怪的現(xiàn)象,上游網(wǎng)關(guān)必須要設(shè)置host
才行,不設(shè)置host
的話,golang
服務(wù)反向代理請求下游會出現(xiàn)http 503
錯誤。服務(wù)調(diào)用順序如下:
二、排查過程
1、打印req.header
差異字段主要是:Forwarded
和GFS.scg.ip
字段。
(1)GFS.scg.ip Gfs.scg.ip 是 Spring Cloud Gateway 中的一個自定義請求頭部字段,用于在路由 中傳遞客戶端的 IP 地址信息。 (2)Forwarded 在 HTTP 請求中,F(xiàn)orwarded 是一種標(biāo)準(zhǔn)化的請求頭,用于識別原始客戶端和代理之間 的連接信息。該字段的值包括一系列鍵值對,每個鍵值對都表示一個不同的屬性。
此時為了排除網(wǎng)關(guān)設(shè)置header對請求的影響,在goproxy
中清空header
且重新嘗試,發(fā)現(xiàn)請求依然是503
。
req.Header = http.Header{} req.Header.Set("traceId", traceId) req.Header.Set("host", "XXX")
2、tcpdump抓包分析
# 抓取請求本機8080端口的請求 sudo tcpdump -i wlo1 -A port 8080 # 抓取本機發(fā)出的http請求 sudo tcpdump -i wlo1 -A dst host 下游域名
(1)先抓取8080端口的請求,查看header差異
host: xxx # 網(wǎng)關(guān)不設(shè)置header的時候,host是本機ip host: localization.xx # 網(wǎng)關(guān)設(shè)置header的時候,host為目標(biāo)主機域名
如上所示,可以得到結(jié)論,host
本來就代表目標(biāo)主機,網(wǎng)關(guān)那邊設(shè)置host
的時候,golang
服務(wù)進(jìn)行轉(zhuǎn)發(fā),會把host
帶到下游python
服務(wù),此時host
是符合預(yù)期的,因此可以請求成功。
網(wǎng)關(guān)不帶host
的時候,host
地址為本機ip
,此時發(fā)送http
請求,對下游python
服務(wù)來說,host
是不符合預(yù)期的,因此請求失敗。
因此,當(dāng)網(wǎng)關(guān)不設(shè)置host
的時候,golang
服務(wù)必需要設(shè)置host
才能訪問到算法服務(wù)。
(2)抓取目標(biāo)域名請求體
1)網(wǎng)關(guān)沒有配置header,且proxy清空header
# sudo tcpdump -i wlo1 -A dst host 下游python服務(wù)域名 ......Pa.I.....P...(...POST /xxx?xxx=test HTTP/1.1 Host: 10.xx:8080 Content-Length: 3013 Traceid: f70cef79265d41be80002ab8ee10abf5 Traceparent: 00-f70cef79265d41be80002ab8ee10abf5-07f71cc5604c734a-00
2)網(wǎng)關(guān)配置header,且proxy清空header
# sudo tcpdump -i wlo1 -A dst host 下游python服務(wù)域名 ......P!.....Q`P...(...POST /location?map_id=test HTTP/1.1 Host: 下游python服務(wù)域名 Content-Length: 3013 Traceid: 972e155927b3fcf665550ab0824acb87 Traceparent: 00-972e155927b3fcf665550ab0824acb87-627675466a61796b-7462733d667273
可以發(fā)現(xiàn),差異主要就在host
部分。也就說,網(wǎng)關(guān)設(shè)置了header
之后,tcpdump
抓包的host
是符合預(yù)期的。
我們在golang
服務(wù)中設(shè)置req.Header
發(fā)現(xiàn)依然沒有效果,抓包結(jié)果顯示host
不符合預(yù)期,預(yù)期是下游python
服務(wù)域名才對。
3、go設(shè)置host方式
目前go
中設(shè)置host
一共有三種方式:
req.Host req.Host 是一個字符串類型的字段,表示了 HTTP 請求頭部中的 Host 信息。 如果該字段為空,則默認(rèn)使用請求的目標(biāo)地址(即 req.URL.Host)作為 Host 信息。 req.Header.Set("host") # 目前使用且設(shè)置host失效 header中的host字段。 req.URL.HOST request.URL.Host 是一個字符串類型的字段,表示 HTTP 請求中目標(biāo) 地址的 HOST信息,跟URL是對應(yīng)的。
代碼中使用的是req.Header.Set("host")
的方式,但是沒有生效。
proxy.Director = func(req *http.Request) { originalDirector(req) modifyRequest(req) // 添加自定義標(biāo)頭 req.Header.Set("traceId", traceId) req.Header.Set("host", "xxx") log.Infof("req.Header:(%#v)", req.Header) } 打印結(jié)果,沒有host!!!
(1)打印req.URL.HOST
打印req.URL
,發(fā)現(xiàn)url
中的host
已經(jīng)被替換了。因為httputil
包在使用反向代理的時候,會觸發(fā)一個rewrite
方法,把target
的host
設(shè)置到req.URL.HOST
req.url:(\u0026url.URL{Scheme:\"http\", Opaque:\"\", User:(*url.Userinfo)(nil), Host:\"下游python服務(wù)域名\", Path:\"/xx\", RawPath:\"\", OmitHost:false, ForceQuery:false, RawQuery:\"map_id=test\", Fragment:\"\", RawFragment:\"\"}
# proxy源碼 func NewSingleHostReverseProxy(target *url.URL) *ReverseProxy { director := func(req *http.Request) { rewriteRequestURL(req, target) } return &ReverseProxy{Director: director} } func rewriteRequestURL(req *http.Request, target *url.URL) { targetQuery := target.RawQuery req.URL.Scheme = target.Scheme req.URL.Host = target.Host req.URL.Path, req.URL.RawPath = joinURLPath(target, req.URL) if targetQuery == "" || req.URL.RawQuery == "" { req.URL.RawQuery = targetQuery + req.URL.RawQuery } else { req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery } }
(2)設(shè)置req.Host
設(shè)置之后進(jìn)行tcpdump
抓包,發(fā)現(xiàn)host
已經(jīng)被成功設(shè)置,http
請求成功。
......P.H......P...(...POST /location?map_id=test HTTP/1.1 Host: 下游python服務(wù)域名 Content-Length: 3013 Traceid: 447e2cc680bed4f2fbfd3941bcda4a42 Traceparent: 00-447e2cc680bed4f2fbfd3941bcda4a42-fffc5c1b967b3f70-7462733d667273
4、為什么設(shè)置header里面的host沒有生效?
參考資料:
如何正確在 Golang 中在處理 Http Request 之前修改 Host 字段內(nèi)容
這篇文章解釋的很清晰了,go
官方的http
策略,大無語。
到此這篇關(guān)于golang反向代理設(shè)置host不生效的文章就介紹到這了,更多相關(guān)golang反向代理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang 模塊引入及表格讀寫業(yè)務(wù)快速實現(xiàn)示例
這篇文章主要為大家介紹了Golang模塊引入及表格讀寫業(yè)務(wù)的快速實現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07ubuntu安裝golang并設(shè)置goproxy的方法步驟
在Ubuntu系統(tǒng)上安裝Go語言(Golang)有多種方法,包括使用包管理器、從源代碼編譯安裝以及使用版本管理工具如gvm,安裝完成后,為了方便管理Go語言項目依賴,需要設(shè)置GOPATH環(huán)境變量并配置Go代理,本文介紹ubuntu安裝golang并設(shè)置goproxy的方法,感興趣的朋友一起看看吧2024-10-10go內(nèi)存緩存BigCache實現(xiàn)BytesQueue源碼解讀
這篇文章主要為大家介紹了go內(nèi)存緩存BigCache實現(xiàn)BytesQueue源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09Golang使用Gin框架實現(xiàn)路由分類處理請求流程詳解
Gin是一個golang的微框架,封裝比較優(yōu)雅,具有快速靈活,容錯方便等特點,這篇文章主要介紹了Golang使用Gin框架實現(xiàn)路由分類處理請求,感興趣的同學(xué)可以參考下文2023-05-05使用Go實現(xiàn)一個百行聊天服務(wù)器的示例代碼
前段時間, redis作者整了個c語言版本的聊天服務(wù)器,代碼量攏共不過百行,于是, 心血來潮下, 我也整了個Go語言版本, 簡單來說就是實現(xiàn)了一個聊天室的功能,文中通過代碼示例給大家介紹的非常詳細(xì),需要的朋友可以參考下2023-12-12