如果你之前接触过gRPC+gRPCGateway这两个组件,肯定会遇到这样的问题,即“同端口双流量为什么一定要开启TLS?难道不能开启吗?”?”或者“我不想用证书来实现这些功能,可以吗?”。这些问题我被无数人问过无数次,我也劝过很多人,但劝就是劝,不代表放弃。前年不行,不代表今年不行。今天希望把来龙去脉和具体实现方法分享给大家。原文地址:gRPC+gRPCGateway可以使用证书吗?为什么h2过去不起作用?因为net/http2只支持“h2”标识符,而“h2”表示HTTP/2必须使用传输层安全(TLS)协议。该标识符用于TLS应用层协议协商字段,标识HTTP/2overTLS。简单地说,net/http2必须使用TLS进行通信。一般来说,需要证书,当然不能支持非TLS的情况。如果你正在寻找h2c,那么这条路就不行了。让我们想想另一条路?那就是HTTP/2规范中的“h2c”标识符。“h2c”标识符允许HTTP/2协议在明文TCP上运行。此标识符用于HTTP/1.1升级标头字段并标识HTTP/2overTCP。但这条路径早在2015年就已经在issue中讨论过,当时@bradfitz明确表示“我不打算支持h2c,只支持TLS我已经很满意了,一年后再问我。”“原回复如下:我们不打算支持h2c。我不想收到被h2c弄乱的透明代理咬伤的用户的错误报告。此外,在获得广泛的浏览器支持之前,它并不有趣。我也不想成为获得浏览器支持的先有鸡或先有蛋。我对仅使用TLS的情况感到非常满意,像https://LetsEncrypt.org/这样的东西很快就会使TLS变得更容易(和自动化)。一年后再问我。考虑基于多路复用器soheilhy/cmuxStoakes/grpc-gateway-example的替代实现来使用cmux的其他方法。如果你对cmux的实现方式感兴趣,也可以看看《Golang: Run multiple services on one port》。使用第三方的h2veqryn/h2c,实现了h2c的逻辑,达到效果。现在经过社区的不断讨论,终于在2018年6月,代表“h2c”标志的golang.org/x/net/http2/h2c标准库被正式合并进来,从此我们就可以使用官方标准库(h2c),这个标准库实现了HTTP/2的不加密模式,所以我们可以使用这个标准库在同一个端口上同时提供HTTP/1.1和HTTP/2的功能。使用标准库h2cimport(..."golang.org/x/net/http2""golang.org/x/net/http2/h2c""google.golang.org/grpc""github.com/grpc-ecosystem/grpc-gateway/runtime"pb"github.com/EDDYCJY/go-grpc-example/proto")...funcgrpcHandlerFunc(grpcServer*grpc.Server,otherHandlerhttp.Handler)http.Handler{返回h2c.NewHandler(http.HandlerFunc(func(whttp.ResponseWriter,r*http.Request){ifr.ProtoMajor==2&&strings.Contains(r.Header.Get("Content-Type"),"application/grpc"){grpcServer.ServeHTTP(w,r)}else{otherHandler.ServeHTTP(w,r)}}),&http2.Server{})}funcmain(){server:=grpc.NewServer()pb.RegisterSearchServiceServer(server,&SearchService{})mux:=http.NewServeMux()gwmux:=runtime.NewServeMux()dopts:=[]grpc.DialOption{grpc.WithInsecure()}err:=pb.RegisterSearchServiceHandlerFromEndpoint(context.Background(),gwmux,"localhost:"+PORT,点ts)...mux.Handle("/",gwmux)http.ListenAndServe(":"+PORT,grpcHandlerFunc(server,mux))}可以看出关键点是调用h2c.NewHandler方法进行特殊处理处理,h2c.NewHandler会返回一个http.handler,主要内部逻辑是拦截所有的h2c流量,然后根据不同的请求流量类型进行劫持,重定向到对应的Hander去处理验证HTTP/1.1$curl-XGET'http://127.0.0.1:9005/search?request=EDDYCJY'{"response":"EDDYCJY"}HTTP/2(gRPC)...funcmain(){conn,err:=grpc.Dial(":"+PORT,grpc.WithInsecure())...client:=pb.NewSearchServiceClient(conn)resp,err:=client.Search(context.Background(),&pb.SearchRequest{Request:"gRPC",})}输出结果:$gorunmain.go2019/06/2120:04:09resp:gRPCh2cServer总结在这篇文章中,我介绍了大致的前因后果,并介绍了几种解决方案。建议你选择官方的h2c标准库,实现这个功能也很简单。最后,不管你是被这个问题困扰了很久,还是正在苦苦挣扎,希望这篇文章能对你有所帮助。参考https://github.com/golang/go/...https://github.com/golang/go/...https://github.com/golang/net...https://去-review.googlesourcec...
