Add HTTPReadHeaderTimeout (10s) and HTTPIdleTimeout (120s) constants and wire them onto the server. ReadHeaderTimeout bounds the header-read phase specifically, dropping a slowloris client that dribbles headers; ReadTimeout alone bounds the whole request but not that phase. IdleTimeout bounds keep-alive reuse so idle connections cannot accumulate without limit. Server construction moves into newHTTPServer so the timeout configuration is assertable without binding a listener. Model: opus-4-8
61 lines
1.7 KiB
Go
61 lines
1.7 KiB
Go
package server
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
)
|
|
|
|
// HTTP server configuration constants.
|
|
const (
|
|
HTTPReadTimeout = 30 * time.Second
|
|
// HTTPReadHeaderTimeout bounds the request-header read on its own,
|
|
// short, so a slowloris client dribbling headers is dropped well
|
|
// before it ties up a connection for the whole ReadTimeout window.
|
|
HTTPReadHeaderTimeout = 10 * time.Second
|
|
HTTPWriteTimeout = 60 * time.Second
|
|
// HTTPIdleTimeout bounds how long an idle keep-alive connection is
|
|
// held open, so idle connections cannot accumulate without limit on a
|
|
// service targeting high concurrency.
|
|
HTTPIdleTimeout = 120 * time.Second
|
|
HTTPMaxHeaderBytes = 8 << 10 // 8KB
|
|
)
|
|
|
|
// newHTTPServer builds the http.Server with the hardening timeouts and
|
|
// limits applied. It is separate from serveUntilShutdown so the
|
|
// configuration can be asserted in a test without binding a listener.
|
|
func (s *Server) newHTTPServer() *http.Server {
|
|
return &http.Server{
|
|
Addr: fmt.Sprintf(":%d", s.config.Port),
|
|
ReadTimeout: HTTPReadTimeout,
|
|
ReadHeaderTimeout: HTTPReadHeaderTimeout,
|
|
WriteTimeout: HTTPWriteTimeout,
|
|
IdleTimeout: HTTPIdleTimeout,
|
|
MaxHeaderBytes: HTTPMaxHeaderBytes,
|
|
Handler: s,
|
|
}
|
|
}
|
|
|
|
func (s *Server) serveUntilShutdown() {
|
|
s.httpServer = s.newHTTPServer()
|
|
|
|
s.SetupRoutes()
|
|
|
|
s.log.Info("http begin listen", "listenaddr", s.httpServer.Addr)
|
|
|
|
err := s.httpServer.ListenAndServe()
|
|
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
|
s.log.Error("listen error", "error", err)
|
|
|
|
if s.cancelFunc != nil {
|
|
s.cancelFunc()
|
|
}
|
|
}
|
|
}
|
|
|
|
// ServeHTTP implements http.Handler.
|
|
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
s.router.ServeHTTP(w, r)
|
|
}
|