feat: harden experimental HTTP2 request limits

This commit is contained in:
2026-09-22 22:38:37 +08:00
parent 1a20ab53ac
commit 1487a96f67
4 changed files with 206 additions and 6 deletions

View File

@@ -23,6 +23,7 @@ import (
const (
defaultShutdownTimeout = 10 * time.Second
defaultTLSHandshake = 10 * time.Second
defaultMaxBodyBytes = 8 << 20
http2ALPNProtocol = "h2"
)
@@ -34,6 +35,7 @@ type Server struct {
MaxConnections int
MaxConcurrentRequests int
MaxConcurrentStreams uint32
MaxBodyBytes int64
IdleTimeout time.Duration
TLSHandshakeTimeout time.Duration
ShutdownTimeout time.Duration
@@ -71,7 +73,11 @@ type Stats struct {
// New creates an HTTP/2 transport with the supplied handler. The handler may
// also be supplied by App.RunWith through the Runner interface.
func New(handler http.Handler) *Server {
return &Server{handler: handler, conns: make(map[net.Conn]struct{})}
return &Server{
handler: handler,
conns: make(map[net.Conn]struct{}),
MaxBodyBytes: defaultMaxBodyBytes,
}
}
// Stats returns a lock-free snapshot suitable for a metrics handler.
@@ -203,7 +209,18 @@ func (s *Server) Serve(ctx context.Context, listener net.Listener) error {
}
s.mu.Unlock()
gate := &requestGate{server: s, next: s.handler, slots: s.requestSlots}
requestHandler := http.Handler(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
if s.MaxBodyBytes > 0 {
if request.ContentLength > s.MaxBodyBytes {
_ = request.Body.Close()
http.Error(writer, "request body too large\n", http.StatusRequestEntityTooLarge)
return
}
request.Body = http.MaxBytesReader(writer, request.Body, s.MaxBodyBytes)
}
s.handler.ServeHTTP(writer, request)
}))
gate := &requestGate{server: s, next: requestHandler, slots: s.requestSlots}
protocolServer := &xhttp2.Server{
MaxConcurrentStreams: s.MaxConcurrentStreams,
IdleTimeout: s.IdleTimeout,