diff options
| author | Damien Neil <dneil@google.com> | 2024-05-29 09:24:20 -0700 |
|---|---|---|
| committer | Damien Neil <dneil@google.com> | 2024-11-05 22:14:59 +0000 |
| commit | bfc8f28068c4aff44aded67aef12e56ecc843717 (patch) | |
| tree | d689a3e8af0036e9276aa45c5193df8f1349d6f3 /src/net/http/server.go | |
| parent | 635c2dce04259f2c84aeac543f0305b3e7c8ed7b (diff) | |
| download | go-bfc8f28068c4aff44aded67aef12e56ecc843717.tar.xz | |
net/http: add Protocols field to Server and Transport
Support configuring which HTTP version(s) a server or client use
via an explicit set of protocols. The Protocols field takes
precedence over TLSNextProto and ForceAttemptHTTP2.
Fixes #67814
Change-Id: I09ece88f78ad4d98ca1f213157b5f62ae11e063f
Reviewed-on: https://go-review.googlesource.com/c/go/+/607496
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Diffstat (limited to 'src/net/http/server.go')
| -rw-r--r-- | src/net/http/server.go | 79 |
1 files changed, 71 insertions, 8 deletions
diff --git a/src/net/http/server.go b/src/net/http/server.go index db44e7c5c2..2c9774a7a5 100644 --- a/src/net/http/server.go +++ b/src/net/http/server.go @@ -2979,6 +2979,13 @@ type Server struct { // See https://go.dev/issue/67813. HTTP2 *HTTP2Config + // Protocols is the set of protocols accepted by the server. + // + // If Protocols is nil, the default is usually HTTP/1 and HTTP/2. + // If TLSNextProto is non-nil and does not contain an "h2" entry, + // the default is HTTP/1 only. + Protocols *Protocols + inShutdown atomic.Bool // true when server is in shutdown disableKeepAlives atomic.Bool @@ -3389,9 +3396,7 @@ func (s *Server) ServeTLS(l net.Listener, certFile, keyFile string) error { } config := cloneTLSConfig(s.TLSConfig) - if !slices.Contains(config.NextProtos, "http/1.1") { - config.NextProtos = append(config.NextProtos, "http/1.1") - } + config.NextProtos = adjustNextProtos(config.NextProtos, s.protocols()) configHasCert := len(config.Certificates) > 0 || config.GetCertificate != nil || config.GetConfigForClient != nil if !configHasCert || certFile != "" || keyFile != "" { @@ -3407,6 +3412,59 @@ func (s *Server) ServeTLS(l net.Listener, certFile, keyFile string) error { return s.Serve(tlsListener) } +func (s *Server) protocols() Protocols { + if s.Protocols != nil { + return *s.Protocols // user-configured set + } + + // The historic way of disabling HTTP/2 is to set TLSNextProto to + // a non-nil map with no "h2" entry. + _, hasH2 := s.TLSNextProto["h2"] + http2Disabled := s.TLSNextProto != nil && !hasH2 + + // If GODEBUG=http2server=0, then HTTP/2 is disabled unless + // the user has manually added an "h2" entry to TLSNextProto + // (probably by using x/net/http2 directly). + if http2server.Value() == "0" && !hasH2 { + http2Disabled = true + } + + var p Protocols + p.SetHTTP1(true) // default always includes HTTP/1 + if !http2Disabled { + p.SetHTTP2(true) + } + return p +} + +// adjustNextProtos adds or removes "http/1.1" and "h2" entries from +// a tls.Config.NextProtos list, according to the set of protocols in protos. +func adjustNextProtos(nextProtos []string, protos Protocols) []string { + var have Protocols + nextProtos = slices.DeleteFunc(nextProtos, func(s string) bool { + switch s { + case "http/1.1": + if !protos.HTTP1() { + return true + } + have.SetHTTP1(true) + case "h2": + if !protos.HTTP2() { + return true + } + have.SetHTTP2(true) + } + return false + }) + if protos.HTTP2() && !have.HTTP2() { + nextProtos = append(nextProtos, "h2") + } + if protos.HTTP1() && !have.HTTP1() { + nextProtos = append(nextProtos, "http/1.1") + } + return nextProtos +} + // trackListener adds or removes a net.Listener to the set of tracked // listeners. // @@ -3600,16 +3658,21 @@ func (s *Server) onceSetNextProtoDefaults() { if omitBundledHTTP2 { return } + if !s.protocols().HTTP2() { + return + } if http2server.Value() == "0" { http2server.IncNonDefault() return } - // Enable HTTP/2 by default if the user hasn't otherwise - // configured their TLSNextProto map. - if s.TLSNextProto == nil { - conf := &http2Server{} - s.nextProtoErr = http2ConfigureServer(s, conf) + if _, ok := s.TLSNextProto["h2"]; ok { + // TLSNextProto already contains an HTTP/2 implementation. + // The user probably called golang.org/x/net/http2.ConfigureServer + // to add it. + return } + conf := &http2Server{} + s.nextProtoErr = http2ConfigureServer(s, conf) } // TimeoutHandler returns a [Handler] that runs h with the given time limit. |
