golang反向代理设置host不生效的问题解决
作者:铁柱同学
一、背景
在使用golang
的httputil
做反向代理的时候,发现一个奇怪的现象,上游网关必须要设置host
才行,不设置host
的话,golang
服务反向代理请求下游会出现http 503
错误。服务调用顺序如下:
二、排查过程
1、打印req.header
差异字段主要是:Forwarded
和GFS.scg.ip
字段。
(1)GFS.scg.ip Gfs.scg.ip 是 Spring Cloud Gateway 中的一个自定义请求头部字段,用于在路由 中传递客户端的 IP 地址信息。 (2)Forwarded 在 HTTP 请求中,Forwarded 是一种标准化的请求头,用于识别原始客户端和代理之间 的连接信息。该字段的值包括一系列键值对,每个键值对都表示一个不同的属性。
此时为了排除网关设置header对请求的影响,在goproxy
中清空header
且重新尝试,发现请求依然是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 # 抓取本机发出的http请求 sudo tcpdump -i wlo1 -A dst host 下游域名
(1)先抓取8080端口的请求,查看header差异
host: xxx # 网关不设置header的时候,host是本机ip host: localization.xx # 网关设置header的时候,host为目标主机域名
如上所示,可以得到结论,host
本来就代表目标主机,网关那边设置host
的时候,golang
服务进行转发,会把host
带到下游python
服务,此时host
是符合预期的,因此可以请求成功。
网关不带host
的时候,host
地址为本机ip
,此时发送http
请求,对下游python
服务来说,host
是不符合预期的,因此请求失败。
因此,当网关不设置host
的时候,golang
服务必需要设置host
才能访问到算法服务。
(2)抓取目标域名请求体
1)网关没有配置header,且proxy清空header
# sudo tcpdump -i wlo1 -A dst host 下游python服务域名 ......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)网关配置header,且proxy清空header
# sudo tcpdump -i wlo1 -A dst host 下游python服务域名 ......P!.....Q`P...(...POST /location?map_id=test HTTP/1.1 Host: 下游python服务域名 Content-Length: 3013 Traceid: 972e155927b3fcf665550ab0824acb87 Traceparent: 00-972e155927b3fcf665550ab0824acb87-627675466a61796b-7462733d667273
可以发现,差异主要就在host
部分。也就说,网关设置了header
之后,tcpdump
抓包的host
是符合预期的。
我们在golang
服务中设置req.Header
发现依然没有效果,抓包结果显示host
不符合预期,预期是下游python
服务域名才对。
3、go设置host方式
目前go
中设置host
一共有三种方式:
req.Host req.Host 是一个字符串类型的字段,表示了 HTTP 请求头部中的 Host 信息。 如果该字段为空,则默认使用请求的目标地址(即 req.URL.Host)作为 Host 信息。 req.Header.Set("host") # 目前使用且设置host失效 header中的host字段。 req.URL.HOST request.URL.Host 是一个字符串类型的字段,表示 HTTP 请求中目标 地址的 HOST信息,跟URL是对应的。
代码中使用的是req.Header.Set("host")
的方式,但是没有生效。
proxy.Director = func(req *http.Request) { originalDirector(req) modifyRequest(req) // 添加自定义标头 req.Header.Set("traceId", traceId) req.Header.Set("host", "xxx") log.Infof("req.Header:(%#v)", req.Header) } 打印结果,没有host!!!
(1)打印req.URL.HOST
打印req.URL
,发现url
中的host
已经被替换了。因为httputil
包在使用反向代理的时候,会触发一个rewrite
方法,把target
的host
设置到req.URL.HOST
req.url:(\u0026url.URL{Scheme:\"http\", Opaque:\"\", User:(*url.Userinfo)(nil), Host:\"下游python服务域名\", 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)设置req.Host
设置之后进行tcpdump
抓包,发现host
已经被成功设置,http
请求成功。
......P.H......P...(...POST /location?map_id=test HTTP/1.1 Host: 下游python服务域名 Content-Length: 3013 Traceid: 447e2cc680bed4f2fbfd3941bcda4a42 Traceparent: 00-447e2cc680bed4f2fbfd3941bcda4a42-fffc5c1b967b3f70-7462733d667273
4、为什么设置header里面的host没有生效?
参考资料:
如何正确在 Golang 中在处理 Http Request 之前修改 Host 字段内容
这篇文章解释的很清晰了,go
官方的http
策略,大无语。
到此这篇关于golang反向代理设置host不生效的文章就介绍到这了,更多相关golang反向代理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!