@@ -15,8 +15,6 @@ import (
1515 "time"
1616
1717 "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
18- "golang.org/x/net/http2"
19- "golang.org/x/net/http2/h2c"
2018 "google.golang.org/grpc"
2119
2220 "chainguard.dev/go-grpc-kit/pkg/interceptors/clientid"
@@ -25,15 +23,14 @@ import (
2523)
2624
2725// handler routes inbound requests to either the gRPC server or the gateway MUX
28- // based on the request content type, served over h2c so gRPC works on a
29- // cleartext port. Each request is counted while it runs, so Shutdown can wait
30- // for in-flight requests to finish: the gRPC requests are served over hijacked
31- // h2c connections that http.Server.Shutdown does not track, so counting them
32- // here is the only way to know when they have drained.
26+ // based on the request content type, served over cleartext HTTP/2 (h2c) so gRPC
27+ // works on a cleartext port. Unencrypted HTTP/2 is enabled on the http.Server
28+ // via its Protocols field (see httpServerInstance). Each request is counted
29+ // while it runs, so Shutdown can wait for in-flight requests to finish.
3330// See also, https://grpc-ecosystem.github.io/grpc-gateway/
3431// This is based on: https://github.com/philips/grpc-gateway-example/issues/22#issuecomment-490733965
3532func (d * Duplex ) handler () http.Handler {
36- return h2c . NewHandler ( http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
33+ return http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
3734 d .inflight .add ()
3835 defer d .inflight .done ()
3936
@@ -43,7 +40,7 @@ func (d *Duplex) handler() http.Handler {
4340 }
4441
4542 d .MUX .ServeHTTP (w , r )
46- }), & http2. Server {})
43+ })
4744}
4845
4946// allowedHeaders are HTTP headers that should be forwarded as gRPC metadata
@@ -160,9 +157,15 @@ func (d *Duplex) Serve(_ context.Context, listener net.Listener) error {
160157// first use.
161158func (d * Duplex ) httpServerInstance () * http.Server {
162159 d .httpServerOnce .Do (func () {
160+ // Enable cleartext HTTP/2 (h2c) alongside HTTP/1 so gRPC works on the
161+ // cleartext port. This replaces the deprecated x/net/http2/h2c handler.
162+ protocols := new (http.Protocols )
163+ protocols .SetHTTP1 (true )
164+ protocols .SetUnencryptedHTTP2 (true )
163165 d .httpServer = & http.Server {
164166 Handler : d .handler (),
165167 ReadHeaderTimeout : 10 * time .Second ,
168+ Protocols : protocols ,
166169 }
167170 })
168171
@@ -178,10 +181,9 @@ func (d *Duplex) httpServerInstance() *http.Server {
178181// context with a deadline to cap it, or a long-lived request will hold shutdown
179182// open indefinitely.
180183//
181- // gRPC is served over h2c, whose connections http.Server.Shutdown does not
182- // track once hijacked, so its return does not imply gRPC has drained; and
184+ // gRPC is served over cleartext HTTP/2 via a grpc.Server.ServeHTTP handler, and
183185// grpc.Server.GracefulStop panics on a ServeHTTP-backed transport, so it cannot
184- // drain them either . Shutdown therefore stops the listeners via the HTTP
186+ // drain those requests . Shutdown therefore stops the listeners via the HTTP
185187// server, waits on its own in-flight counter for requests of both kinds to
186188// finish, then stops the gRPC server with grpc.Server.Stop and closes the HTTP
187189// server. Those final steps run on every path: after a clean drain they release
@@ -190,11 +192,11 @@ func (d *Duplex) httpServerInstance() *http.Server {
190192func (d * Duplex ) Shutdown (ctx context.Context ) error {
191193 server := d .httpServerInstance ()
192194
193- // Stop accepting new connections and drain the gateway 's tracked
194- // connections. This does not wait for hijacked h2c (gRPC) connections, so
195- // run it in the background while the in-flight counter is drained below . Its
196- // error is dropped deliberately: the wait below reports the drain outcome,
197- // and the Close below is the backstop.
195+ // Stop accepting new connections and drain the HTTP server 's connections.
196+ // Run it in the background while the in-flight counter is drained below,
197+ // which is what reports the outcome for requests of both kinds . Its error is
198+ // dropped deliberately: the wait below reports the drain outcome, and the
199+ // Close below is the backstop.
198200 go func () { _ = server .Shutdown (ctx ) }()
199201
200202 err := d .inflight .wait (ctx )
0 commit comments