| // 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)) |
| } |