| // 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 go1.27 && !http2legacy |
| |
| // Server wrapping a net/http.Server. |
| |
| package http2 |
| |
| import ( |
| "context" |
| "errors" |
| "net" |
| "net/http" |
| "sync" |
| "time" |
| ) |
| |
| type serverInternalState struct { |
| s1 *http.Server |
| initOnce sync.Once |
| serveConnFunc func(context.Context, net.Conn, http.Handler, bool, *http.Request, []byte) |
| } |
| |
| func configureServer(s *http.Server, conf *Server) error { |
| if s == nil { |
| panic("nil *http.Server") |
| } |
| if conf == nil { |
| conf = new(Server) |
| } |
| if conf.state != nil { |
| // This isn't a panic in the pre-wrapping implementation, |
| // but calling ConfigureServer twice with the same http2.Server |
| // overwrites internal state on the server. |
| // Make the error explicit and early here. |
| panic("ConfigureServer may be called only once per Server") |
| } |
| if h1, h2 := s, conf; h2.IdleTimeout == 0 { |
| if h1.IdleTimeout != 0 { |
| h2.IdleTimeout = h1.IdleTimeout |
| } else { |
| h2.IdleTimeout = h1.ReadTimeout |
| } |
| } |
| conf.state = &serverInternalState{ |
| s1: s, |
| } |
| sconfig := &serverConfig{s: conf} |
| if err := s.Serve(sconfig); err != nil || sconfig.serveConnFunc == nil { |
| panic("http2: net/http does not support this version of x/net/http2") |
| } |
| conf.state.serveConnFunc = sconfig.serveConnFunc |
| return nil |
| } |
| |
| type serverConfig struct { |
| s *Server |
| serveConnFunc func(context.Context, net.Conn, http.Handler, bool, *http.Request, []byte) |
| } |
| |
| func (*serverConfig) Accept() (net.Conn, error) { |
| return nil, errors.New("unexpected call to Accept") |
| } |
| func (*serverConfig) Close() error { |
| return nil |
| } |
| func (*serverConfig) Addr() net.Addr { |
| return nil |
| } |
| |
| func (s *serverConfig) ServeConnFunc(f func(context.Context, net.Conn, http.Handler, bool, *http.Request, []byte)) { |
| s.serveConnFunc = f |
| } |
| |
| func (s *serverConfig) HTTP2Config() http.HTTP2Config { |
| return http.HTTP2Config{ |
| MaxConcurrentStreams: int(s.s.MaxConcurrentStreams), |
| MaxDecoderHeaderTableSize: int(s.s.MaxDecoderHeaderTableSize), |
| MaxEncoderHeaderTableSize: int(s.s.MaxEncoderHeaderTableSize), |
| MaxReadFrameSize: int(s.s.MaxReadFrameSize), |
| PermitProhibitedCipherSuites: s.s.PermitProhibitedCipherSuites, |
| MaxReceiveBufferPerConnection: int(s.s.MaxUploadBufferPerConnection), |
| MaxReceiveBufferPerStream: int(s.s.MaxUploadBufferPerStream), |
| SendPingTimeout: s.s.ReadIdleTimeout, |
| PingTimeout: s.s.PingTimeout, |
| WriteByteTimeout: s.s.WriteByteTimeout, |
| CountError: s.s.CountError, |
| } |
| } |
| |
| func (s *serverConfig) IdleTimeout() time.Duration { |
| return s.s.IdleTimeout |
| } |
| |
| type serverConn struct{} |
| |
| func (s *Server) serveConn(c net.Conn, opts *ServeConnOpts, _ func(*serverConn)) { |
| var serveConnFunc func(context.Context, net.Conn, http.Handler, bool, *http.Request, []byte) |
| switch { |
| case opts.BaseConfig != nil: |
| // The user has provided us with an http.Server to take configuration from. |
| // |
| // We can't send our request to opts.BaseConfig, because an http.Server can |
| // only be associated with a single http2.Server and the user might |
| // use this one with several http.Servers. |
| // |
| // We can't send our request to s.state.s1, because it doesn't contain |
| // the right configuration. |
| // |
| // So create a one-off copy of opts.BaseConfig and use it. |
| h1 := &http.Server{ |
| TLSConfig: opts.BaseConfig.TLSConfig, |
| ReadTimeout: opts.BaseConfig.ReadTimeout, |
| ReadHeaderTimeout: opts.BaseConfig.ReadHeaderTimeout, |
| WriteTimeout: opts.BaseConfig.WriteTimeout, |
| IdleTimeout: opts.BaseConfig.IdleTimeout, |
| MaxHeaderBytes: opts.BaseConfig.MaxHeaderBytes, |
| ConnState: opts.BaseConfig.ConnState, |
| ErrorLog: opts.BaseConfig.ErrorLog, |
| BaseContext: opts.BaseConfig.BaseContext, |
| ConnContext: opts.BaseConfig.ConnContext, |
| HTTP2: opts.BaseConfig.HTTP2, |
| } |
| sconfig := &serverConfig{s: s} |
| if err := h1.Serve(sconfig); err != nil || sconfig.serveConnFunc == nil { |
| panic("http2: net/http does not support this version of x/net/http2") |
| } |
| serveConnFunc = sconfig.serveConnFunc |
| case s.state != nil: |
| serveConnFunc = s.state.serveConnFunc |
| default: |
| // Strange-but-true: Server has no concurrency-safe way to initialize |
| // its internal state, so historically ServeConn just doesn't use any |
| // persistent state if you don't call ConfigureServer first. |
| // |
| // If ConfigureServer hasn't been called, create a one-off http.Server |
| // for the connection, since we don't have any way to keep one around for reuse. |
| h1 := &http.Server{} |
| sconfig := &serverConfig{s: s} |
| if err := h1.Serve(sconfig); err != nil || sconfig.serveConnFunc == nil { |
| panic("http2: net/http does not support this version of x/net/http2") |
| } |
| serveConnFunc = sconfig.serveConnFunc |
| } |
| |
| ctx, cancel := serverConnBaseContext(c, opts) |
| defer cancel() |
| serveConnFunc(ctx, c, opts.handler(), opts.SawClientPreface, opts.UpgradeRequest, opts.Settings) |
| |
| } |
| |
| // FrameWriteRequest is a request to write a frame. |
| // |
| // Deprecated: User-provided write schedulers are deprecated. |
| type FrameWriteRequest struct { |
| // Ideally we'd define this in writesched_common.go, |
| // to avoid duplicating an exported symbol across two files, |
| // but the changes required to make this work are fairly large. |
| } |
| |
| func (wr FrameWriteRequest) StreamID() uint32 { |
| return 0 |
| } |
| |
| func (wr FrameWriteRequest) DataSize() int { |
| return 0 |
| } |
| |
| func (wr FrameWriteRequest) Consume(n int32) (FrameWriteRequest, FrameWriteRequest, int) { |
| return FrameWriteRequest{}, FrameWriteRequest{}, 0 |
| } |
| |
| func (wr FrameWriteRequest) String() string { |
| return "" |
| } |
| |
| // NewPriorityWriteScheduler is deprecated. |
| // |
| // Deprecated: User-provided write schedulers are deprecated. |
| func NewPriorityWriteScheduler(cfg *PriorityWriteSchedulerConfig) WriteScheduler { |
| return unsupportedWriteScheduler{} |
| } |
| |
| // NewRandomWriteScheduler is deprecated. |
| // |
| // Deprecated: User-provided write schedulers are deprecated. |
| func NewRandomWriteScheduler() WriteScheduler { |
| return unsupportedWriteScheduler{} |
| } |
| |
| type unsupportedWriteScheduler struct{} |
| |
| func (unsupportedWriteScheduler) OpenStream(streamID uint32, options OpenStreamOptions) {} |
| func (unsupportedWriteScheduler) CloseStream(streamID uint32) {} |
| func (unsupportedWriteScheduler) AdjustStream(streamID uint32, priority PriorityParam) {} |
| func (unsupportedWriteScheduler) Push(wr FrameWriteRequest) {} |
| func (unsupportedWriteScheduler) Pop() (wr FrameWriteRequest, ok bool) { |
| return FrameWriteRequest{}, false |
| } |