| // Copyright 2026 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| //go:build !nethttpomithttp2 && !nethttpomithttp2server | |
| package http | |
| import ( | |
| "context" | |
| "crypto/tls" | |
| "io" | |
| "log" | |
| "net" | |
| "net/http/internal/http2" | |
| "time" | |
| ) | |
| // This file connects the net/http Server to the HTTP/2 server | |
| // in the net/http/internal/http2 package. It is omitted when | |
| // the nethttpomithttp2 or nethttpomithttp2server build tag is set. | |
| const omitHTTP2Server = false | |
| type http2Server = http2.Server | |
| func (s *Server) configureHTTP2() { | |
| h2srv := &http2.Server{} | |
| // Historically, we've configured the HTTP/2 idle timeout in this fashion: | |
| // Set once at configuration time. | |
| if s.IdleTimeout != 0 { | |
| s.h2IdleTimeout = s.IdleTimeout | |
| } else { | |
| s.h2IdleTimeout = s.ReadTimeout | |
| } | |
| if s.TLSConfig == nil { | |
| s.TLSConfig = &tls.Config{} | |
| } | |
| s.nextProtoErr = h2srv.Configure(http2ServerConfig{s}, s.TLSConfig) | |
| if s.nextProtoErr != nil { | |
| return | |
| } | |
| s.RegisterOnShutdown(h2srv.GracefulShutdown) | |
| if s.TLSNextProto == nil { | |
| s.TLSNextProto = make(map[string]func(*Server, *tls.Conn, Handler)) | |
| } | |
| // Historically, the presence of a TLSNextProto["h2"] key has been the signal to | |
| // enable/disable HTTP/2 support. Set a value in the map, but we'll never use it. | |
| s.TLSNextProto["h2"] = func(hs *Server, c *tls.Conn, h Handler) { | |
| c.Close() | |
| } | |
| s.h2 = h2srv | |
| } | |
| func (s *Server) setHTTP2Config(conf http2ExternalServerConfig) { | |
| if s.h2Config != nil { | |
| panic("http: HTTP/2 Server already registered") | |
| } | |
| s.h2Config = conf | |
| s.h2Config.ServeConnFunc(func(ctx context.Context, nc net.Conn, h Handler, sawClientPreface bool, upgradeReq *Request, settings []byte) { | |
| s.serveHTTP2Conn(ctx, nc, h, sawClientPreface, upgradeReq, settings, nil) | |
| }) | |
| s.configureHTTP2() | |
| } | |
| // serveHTTP2Conn serves nc with the HTTP/2 server. It may return before the | |
| // connection is done being served (an idle HTTP/2 connection doesn't hold | |
| // onto a goroutine); onClose, if non-nil, runs once the connection is done | |
| // and has been closed. | |
| func (s *Server) serveHTTP2Conn(ctx context.Context, nc net.Conn, h Handler, sawClientPreface bool, upgradeReq *Request, settings []byte, onClose func()) { | |
| s.setupHTTP2_ServeTLS() | |
| var serverUpgradeReq *http2.ServerRequest | |
| if upgradeReq != nil { | |
| serverUpgradeReq = http2ServerRequestFromRequest(upgradeReq) | |
| } | |
| nc.SetReadDeadline(time.Time{}) | |
| nc.SetWriteDeadline(time.Time{}) | |
| s.h2.ServeConn(nc, &http2.ServeConnOpts{ | |
| Context: ctx, | |
| Handler: http2Handler{h}, | |
| BaseConfig: http2ServerConfig{s}, | |
| SawClientPreface: sawClientPreface, | |
| UpgradeRequest: serverUpgradeReq, | |
| Settings: settings, | |
| OnClose: onClose, | |
| }) | |
| } | |
| func http2ServerRequestFromRequest(req *Request) *http2.ServerRequest { | |
| return &http2.ServerRequest{ | |
| Context: req.Context(), | |
| Proto: req.Proto, | |
| ProtoMajor: req.ProtoMajor, | |
| ProtoMinor: req.ProtoMinor, | |
| Method: req.Method, | |
| URL: req.URL, | |
| Header: http2.Header(req.Header), | |
| Trailer: http2.Header(req.Trailer), | |
| Body: req.Body, | |
| Host: req.Host, | |
| ContentLength: req.ContentLength, | |
| RemoteAddr: req.RemoteAddr, | |
| RequestURI: req.RequestURI, | |
| TLS: req.TLS, | |
| } | |
| } | |
| type http2Handler struct { | |
| h Handler | |
| } | |
| func (h http2Handler) ServeHTTP(w *http2.ResponseWriter, req *http2.ServerRequest) { | |
| h.h.ServeHTTP(http2ResponseWriter{w}, &Request{ | |
| ctx: req.Context, | |
| Proto: "HTTP/2.0", | |
| ProtoMajor: 2, | |
| ProtoMinor: 0, | |
| Method: req.Method, | |
| URL: req.URL, | |
| Header: Header(req.Header), | |
| RequestURI: req.RequestURI, | |
| Trailer: Header(req.Trailer), | |
| Body: req.Body, | |
| Host: req.Host, | |
| ContentLength: req.ContentLength, | |
| RemoteAddr: req.RemoteAddr, | |
| TLS: req.TLS, | |
| }) | |
| } | |
| type http2ResponseWriter struct { | |
| *http2.ResponseWriter | |
| } | |
| // Optional http.ResponseWriter interfaces implemented. | |
| var ( | |
| _ CloseNotifier = http2ResponseWriter{} | |
| _ Flusher = http2ResponseWriter{} | |
| _ io.StringWriter = http2ResponseWriter{} | |
| ) | |
| func (w http2ResponseWriter) Flush() { w.ResponseWriter.FlushError() } | |
| func (w http2ResponseWriter) FlushError() error { return w.ResponseWriter.FlushError() } | |
| func (w http2ResponseWriter) Header() Header { return Header(w.ResponseWriter.Header()) } | |
| func (w http2ResponseWriter) Push(target string, opts *PushOptions) error { | |
| var ( | |
| method string | |
| header http2.Header | |
| ) | |
| if opts != nil { | |
| method = opts.Method | |
| header = http2.Header(opts.Header) | |
| } | |
| err := w.ResponseWriter.Push(target, method, header) | |
| if err == http2.ErrNotSupported { | |
| err = ErrNotSupported | |
| } | |
| return err | |
| } | |
| type http2ServerConfig struct { | |
| s *Server | |
| } | |
| func (s http2ServerConfig) MaxHeaderBytes() int { return s.s.MaxHeaderBytes } | |
| func (s http2ServerConfig) MaxHeaderValueCount() int { return s.s.maxHeaderValueCount() } | |
| func (s http2ServerConfig) ConnState(c net.Conn, st http2.ConnState) { | |
| if s.s.ConnState != nil { | |
| s.s.ConnState(c, ConnState(st)) | |
| } | |
| } | |
| func (s http2ServerConfig) DoKeepAlives() bool { return s.s.doKeepAlives() } | |
| func (s http2ServerConfig) WriteTimeout() time.Duration { return s.s.WriteTimeout } | |
| func (s http2ServerConfig) SendPingTimeout() time.Duration { return s.s.ReadTimeout } | |
| func (s http2ServerConfig) ErrorLog() *log.Logger { return s.s.ErrorLog } | |
| func (s http2ServerConfig) ReadTimeout() time.Duration { return s.s.ReadTimeout } | |
| func (s http2ServerConfig) DisableClientPriority() bool { return s.s.DisableClientPriority } | |
| func (s http2ServerConfig) IdleTimeout() time.Duration { | |
| if s.s.h2Config != nil { | |
| return s.s.h2Config.IdleTimeout() | |
| } | |
| return s.s.h2IdleTimeout | |
| } | |
| func (s http2ServerConfig) HTTP2Config() http2.Config { | |
| return mergeHTTP2Config(s.s.HTTP2, s.s.h2Config) | |
| } | |
| // http2ExternalServerConfig is an HTTP/2 configuration provided by x/net/http2. | |
| // | |
| // When a x/net/http2.Server wraps a net/http.Server, we need to support the user | |
| // setting configuration settings on the x/net Server: | |
| // | |
| // s1 := &http.Server{} | |
| // s2 := &http2.Server{} | |
| // http2.ConfigureServer(s1, s2) | |
| // | |
| // // This setting needs to affect s1: | |
| // s2.MaxReadFrameSize = 10000 | |
| // | |
| // We handle this by having http2.ConfigureServer pass us an http2ExternalServerConfig | |
| // (see http.Server.Serve) which we can use to query the current state of the http2.Server. | |
| type http2ExternalServerConfig interface { | |
| // Various configuration settings: | |
| HTTP2Config() HTTP2Config | |
| IdleTimeout() time.Duration | |
| // ServeConnFunc provides a function to the x/net/http2.Server which it | |
| // can use to serve a new connection. | |
| ServeConnFunc(func(ctx context.Context, nc net.Conn, h Handler, sawClientPreface bool, upgradeReq *Request, settings []byte)) | |
| } |