golang反向代理设置host不生效的问题解决

 更新时间:2023年05月27日 10:03:45   作者:铁柱同学  
在使用golang的httputil做反向代理的时候,发现一个奇怪的现象,上游网关必须要设置host才行,不设置host的话,golang服务反向代理请求下游会出现http 503错误,接下来通过本文给大家介绍golang反向代理设置host不生效问题,感兴趣的朋友一起看看吧

一、背景

在使用golanghttputil做反向代理的时候,发现一个奇怪的现象,上游网关必须要设置host才行,不设置host的话,golang服务反向代理请求下游会出现http 503错误。服务调用顺序如下:

在这里插入图片描述

二、排查过程

1、打印req.header

差异字段主要是:ForwardedGFS.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方法,把targethost设置到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反向代理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Golang开发动态库的实现

    Golang开发动态库的实现

    这篇文章主要介绍了Golang开发动态库的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-11-11
  • go mode tidy出现报错go: warning: “all“ matched no packages的解决方法

    go mode tidy出现报错go: warning: “all“ matched no package

    使用go的时候我们一般都会使用go mode管理,下面这篇文章主要给大家介绍了关于go mode tidy出现报错go: warning: “all“ matched no packages的解决方法,文中通过图文介绍的非常详细,需要的朋友可以参考下
    2022-08-08
  • go语言中struct标签详解

    go语言中struct标签详解

    这篇文章主要给大家介绍了关于go语言中struct标签的相关资料,文中通过实例代码介绍的非常详细,对大家学习或者使用go语言具有一定的参考学习价值,需要的朋友可以参考下
    2023-07-07
  • Golang Gin局部和全局中间件使用详解

    Golang Gin局部和全局中间件使用详解

    中间件是放在客户端和服务端的中间,当你的客户端对某个接口发起一个请求,但是在到达接口2之前,这里是有一层中间件的处理。本文详细介绍了Golang Gin局部、全局中间件使用方法,感兴趣的同学可以阅读本文
    2023-04-04
  • go语言中如何使用select的实现示例

    go语言中如何使用select的实现示例

    本文主要介绍了go语言中如何使用select的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-05-05
  • 一文带你熟悉Go语言中的分支结构

    一文带你熟悉Go语言中的分支结构

    这篇文章主要和大家分享一下Go语言中的分支结构(if - else-if - else、switch),文中的示例代码讲解详细,对我们学习Go语言有一定的帮助,需要的可以参考一下
    2022-11-11
  • Go保证并发安全底层实现详解

    Go保证并发安全底层实现详解

    这篇文章主要为大家介绍了Go保证并发安全底层实现详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-09-09
  • 解决golang时间字符串转time.Time的坑

    解决golang时间字符串转time.Time的坑

    这篇文章主要介绍了解决golang时间字符串转time.Time的坑,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-04-04
  • golang模板template自定义函数用法示例

    golang模板template自定义函数用法示例

    这篇文章主要介绍了golang模板template自定义函数用法,结合实例形式分析了Go语言模板自定义函数的基本定义与使用方法,需要的朋友可以参考下
    2016-07-07
  • Go语言读写锁RWMutex的源码分析

    Go语言读写锁RWMutex的源码分析

    本篇文章我们将一起来学习下Go语言中的读写锁sync.RWMutex。文中的示例讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以跟随小编一起学习一下
    2022-10-10

最新评论