aboutsummaryrefslogtreecommitdiff
path: root/src/net/http/server.go
diff options
context:
space:
mode:
authorNicholas S. Husin <nsh@golang.org>2026-01-26 18:15:57 -0500
committerNicholas Husin <nsh@golang.org>2026-03-04 06:38:17 -0800
commit0b9bcbc58c4cf127d5a42989d08cc0236faa71cc (patch)
treea57ec2091bd435af3af8c0e983ad43b7f088db72 /src/net/http/server.go
parentfdf3bee34261f383e394a06b1e4cf87fff684c1b (diff)
downloadgo-0b9bcbc58c4cf127d5a42989d08cc0236faa71cc.tar.xz
net/http: add basic unexported pluggable HTTP/3 support
Following #77440, this CL adds a basic support for plugging in an HTTP/3 implementation to net/http. As the proposal is not accepted yet, this CL does not add any exported symbols. Access to plug HTTP/3 support is locked behind net/http.protocolSetHTTP3, which can only be used via linkname by golang.org/x/net/internal/http3_test.protocolSetHTTP3. This will allow us to run our HTTP/3 implementation in x/net againts various tests in net/http to support development, without expanding the API surface for any users. Support for closeIdleConnectionser will be added separately in the future. For #77440 Change-Id: I6e3a0c2e9b329cef43e4682463ed5e2093d04256 Reviewed-on: https://go-review.googlesource.com/c/go/+/740120 Reviewed-by: Nicholas Husin <husin@google.com> Reviewed-by: Damien Neil <dneil@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Diffstat (limited to 'src/net/http/server.go')
-rw-r--r--src/net/http/server.go112
1 files changed, 99 insertions, 13 deletions
diff --git a/src/net/http/server.go b/src/net/http/server.go
index a388777d3a..fb167ac7a1 100644
--- a/src/net/http/server.go
+++ b/src/net/http/server.go
@@ -3476,6 +3476,22 @@ func (s *Server) Serve(l net.Listener) error {
}
}
+func (s *Server) setupTLSConfig(certFile, keyFile string, nextProtos []string) (*tls.Config, error) {
+ config := cloneTLSConfig(s.TLSConfig)
+ config.NextProtos = nextProtos
+
+ configHasCert := len(config.Certificates) > 0 || config.GetCertificate != nil || config.GetConfigForClient != nil
+ if !configHasCert || certFile != "" || keyFile != "" {
+ var err error
+ config.Certificates = make([]tls.Certificate, 1)
+ config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
+ if err != nil {
+ return nil, err
+ }
+ }
+ return config, nil
+}
+
// ServeTLS accepts incoming connections on the Listener l, creating a
// new service goroutine for each. The service goroutines perform TLS
// setup and then read requests, calling s.Handler to reply to them.
@@ -3497,17 +3513,9 @@ func (s *Server) ServeTLS(l net.Listener, certFile, keyFile string) error {
return err
}
- config := cloneTLSConfig(s.TLSConfig)
- config.NextProtos = adjustNextProtos(config.NextProtos, s.protocols())
-
- configHasCert := len(config.Certificates) > 0 || config.GetCertificate != nil || config.GetConfigForClient != nil
- if !configHasCert || certFile != "" || keyFile != "" {
- var err error
- config.Certificates = make([]tls.Certificate, 1)
- config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
- if err != nil {
- return err
- }
+ config, err := s.setupTLSConfig(certFile, keyFile, adjustNextProtos(s.TLSConfig.NextProtos, s.protocols()))
+ if err != nil {
+ return err
}
tlsListener := tls.NewListener(l, config)
@@ -3516,6 +3524,15 @@ func (s *Server) ServeTLS(l net.Listener, certFile, keyFile string) error {
func (s *Server) protocols() Protocols {
if s.Protocols != nil {
+ // Historically, even when Protocols for a Server was set to be empty,
+ // the Server can still run normally with just HTTP/1.
+ // To keep backward-compatibility, the zero value of Protocols is
+ // defined as having only HTTP/1 enabled.
+ if s.Protocols.empty() {
+ var p Protocols
+ p.SetHTTP1(true)
+ return p
+ }
return *s.Protocols // user-configured set
}
@@ -3696,6 +3713,51 @@ func ListenAndServeTLS(addr, certFile, keyFile string, handler Handler) error {
return server.ListenAndServeTLS(certFile, keyFile)
}
+// http3ServerHandler implements an interface in an external library that
+// supports HTTP/3, allowing an external implementation of HTTP/3 to be used
+// via net/http. See https://go.dev/issue/77440 for details.
+//
+// This is currently only used with golang.org/x/net/internal/http3, to allow
+// us to test our HTTP/3 implementation againts tests in net/http. HTTP/3 is
+// not yet accessible to end-users.
+type http3ServerHandler struct {
+ handler serverHandler
+ tlsConfig *tls.Config
+ baseCtx context.Context
+ errc chan error
+}
+
+// ServeHTTP ensures that http3ServerHandler implements the Handler interface,
+// and gives an HTTP/3 server implementation access to the net/http handler.
+func (h http3ServerHandler) ServeHTTP(w ResponseWriter, r *Request) {
+ h.handler.ServeHTTP(w, r)
+}
+
+// Addr gives an HTTP/3 server implementation the address that it should listen
+// on.
+func (h http3ServerHandler) Addr() string {
+ return h.handler.srv.Addr
+}
+
+// TLSConfig gives an HTTP/3 server implementation the *tls.Config that it
+// should use.
+func (h http3ServerHandler) TLSConfig() *tls.Config {
+ return h.tlsConfig
+}
+
+// BaseContext gives an HTTP/3 server implementation the base context to use
+// for server requests.
+func (h http3ServerHandler) BaseContext() context.Context {
+ return h.baseCtx
+}
+
+// ListenErrHook should be called by an HTTP/3 server implementation to
+// propagate any error it encounters when trying to listen, if any, to
+// net/http.
+func (h http3ServerHandler) ListenErrHook(err error) {
+ h.errc <- err
+}
+
// ListenAndServeTLS listens on the TCP network address s.Addr and
// then calls [ServeTLS] to handle requests on incoming TLS connections.
// Accepted connections are configured to enable TCP keep-alives.
@@ -3720,13 +3782,37 @@ func (s *Server) ListenAndServeTLS(certFile, keyFile string) error {
addr = ":https"
}
+ p := s.protocols()
+ if p.http3() {
+ fn, ok := s.TLSNextProto["http/3"]
+ if !ok {
+ return errors.New("http: Server.Protocols contains HTTP3, but Server does not support HTTP/3")
+ }
+ config, err := s.setupTLSConfig(certFile, keyFile, []string{"h3"})
+ if err != nil {
+ return err
+ }
+ errc := make(chan error, 1)
+ go fn(s, nil, http3ServerHandler{
+ handler: serverHandler{s},
+ tlsConfig: config,
+ baseCtx: context.WithValue(context.Background(), ServerContextKey, s),
+ errc: errc,
+ })
+ if err := <-errc; err != nil {
+ return err
+ }
+ }
+
+ // Only start a TCP listener if HTTP/1 or HTTP/2 is used.
+ if !p.HTTP1() && !p.HTTP2() && !p.UnencryptedHTTP2() {
+ return nil
+ }
ln, err := net.Listen("tcp", addr)
if err != nil {
return err
}
-
defer ln.Close()
-
return s.ServeTLS(ln, certFile, keyFile)
}