package server import ( "log/slog" "net/http" "time" ) // HTTP server hardening limits. These are the connection-level bounds; // the per-request deadline is middleware.Timeout, driven by // REQUEST_TIMEOUT. const ( // readHeaderTimeout is the slowloris bound: request headers must // arrive within it, and it starts on connection accept. readHeaderTimeout = 5 * time.Second // readTimeout covers headers plus body. Bodies are capped by // MAX_REQUEST_BODY, which transfers well inside this even on a // slow mobile link. readTimeout = 30 * time.Second // writeTimeout must exceed the per-request timeout: it starts when // the headers are read, so it spans handler execution, and a // smaller value would cut the connection instead of letting the // request context deadline end the request with a status. The // margin is added to whatever REQUEST_TIMEOUT is configured to. writeTimeoutMargin = 15 * time.Second // idleTimeout bounds how long an idle keep-alive connection is // held; browsers reconnect transparently. idleTimeout = 120 * time.Second maxHeaderBytes = 1 << 20 ) // newHTTPServer builds the http.Server. // // ErrorLog is set on purpose: net/http internals (TLS handshake // errors, request parse errors, panics net/http itself recovers) write // through it, and unset they would emit plain text on stderr via the // default log package — a few lines of unstructured output in the // middle of a JSON log stream, which is exactly the kind of thing a log // pipeline drops on the floor. func (s *Server) newHTTPServer(listenAddr string) *http.Server { return &http.Server{ Addr: listenAddr, ReadHeaderTimeout: readHeaderTimeout, ReadTimeout: readTimeout, WriteTimeout: s.params.Config.RequestTimeout + writeTimeoutMargin, IdleTimeout: idleTimeout, MaxHeaderBytes: maxHeaderBytes, Handler: s, ErrorLog: slog.NewLogLogger(s.log.Handler(), slog.LevelError), } }