gRPC header问题

问题概述

在开发过程中,无意间升级了grpc 的版本到1.42.0,之后所有的接口就开始抛出这个错误

Screen Shot 20220614 at 11.28.17.png
而且非常奇怪的是服务端这里没有任何额外信息,只有一行请求被拒绝的日志
经过各种尝试我发现如果请求时不带Connection: keep-alive这个header,就可以正常访问接口
花费了大量时间,查阅了各种资料后,在官方github仓库的 grpc-go 1.42.0 release note 我找到了答案

问题原因

在grpc升级到1.42.0后,grpc go server会把connection header认为是 malformed header 而拒绝

而如果项目使用了grpc-gateway来将Http请求反代到gRPC server,grpc-gateway会把合法的http header加上自己的prefix后透传给grpc server用来区分http header

显而易见 http connection header 被认为是不合法的,所以 如果通过grpc-gateway接收的http请求包含 Connection: keep-alive 这个header,服务器会直接拒绝该请求

解决办法

替换grpc-gateway默认的headerMatcher, 将connect header加上prefix即可

// new http mux server时,加入headerMatcher中间件
gwMux := runtime.NewServeMux(
                 // 加入自定义的header matcher
		runtime.WithIncomingHeaderMatcher(func(s string) (string, bool) {
			return myDefaultHeaderMatcher(s)
		}),
		runtime.WithOutgoingHeaderMatcher(func(s string) (string, bool) {
			return s, true
		}),
		runtime.WithForwardResponseOption(httpResponseModifier),
		runtime.WithMarshalerOption(runtime.MIMEWildcard, &gateway.JSONPb{
			OrigName:     true,
			EmitDefaults: true,
		}),
	)
// myDefaultHeaderMatcher,修改自github.com/grpc-ecosystem/grpc-gateway/runtime.WithHeaderMatcher中的函数
// replacement for default HeaderMatcher
// same as runtime.WithHeaderMatcher
// did some modification for header matcher
// see https://github.com/grpc-ecosystem/grpc-gateway/issues/2447
myDefaultHeaderMatcher := func(key string) (string, bool) {
	key = textproto.CanonicalMIMEHeaderKey(key)
	// use modifier http header function
	if isPermanentHTTPHeader(key) {
		return runtime.MetadataPrefix + key, true
	} else if strings.HasPrefix(key, runtime.MetadataHeaderPrefix) {
		return key[len(runtime.MetadataPrefix):], true
	}
	// return header no matter what
	return key, true
}

// 自定义的http header判断函数, 加入connection header
func isPermanentHTTPHeader(hdr string) bool {
	// after google.golang.org/grpc v1.42.0, grpc will reject connection header
	// we add it to header modifier manually
	// see https://github.com/grpc/grpc-go/issues/5175
	switch hdr {
	case
		"Accept",
		"Accept-Charset",
		"Accept-Language",
		"Accept-Ranges",
		"Authorization",
		"Cache-Control",
		"Content-Type",
		"Cookie",
		"Date",
		"Expect",
		"From",
		"Host",
		"If-Match",
		"If-Modified-Since",
		"If-None-Match",
		"If-Schedule-Tag-Match",
		"If-Unmodified-Since",
		"Max-Forwards",
		"Origin",
		"Pragma",
		"Referer",
		"User-Agent",
		"Via",
		"Warning",
		// add connection header

		"Connection":
		return true
	}
	return false
}

参考