blob: 9099464c8e56c920d49f2b7409f91c5214406859 [file]
// 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))
}