check / check (push) Successful in 5m14s
fx alone handles SIGINT and SIGTERM; the server's own handler, which only cancelled a context that fx's stop did not wait for, is gone. runApp starts the fx app, waits for a signal or a shutdown request, stops the app and returns the exit code, which main passes to os.Exit. A listen error asks fx to shut down with exit code 1. A Sentry DSN that cannot be used fails the server's start hook, so fx stops what had already started. The server's stop hook stops the HTTP server, then waits for the images still being processed, both within ShutdownTimeout; images still being processed after that are logged and fail the stop, so the exit code is 1. Model: opus-5-5
62 lines
1.9 KiB
Go
62 lines
1.9 KiB
Go
package server
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
|
|
"go.uber.org/fx"
|
|
)
|
|
|
|
// 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
|
|
// 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: s.config.DownstreamTimeout,
|
|
IdleTimeout: HTTPIdleTimeout,
|
|
MaxHeaderBytes: HTTPMaxHeaderBytes,
|
|
Handler: s,
|
|
}
|
|
}
|
|
|
|
// serveUntilShutdown serves on s.httpServer until it is shut down. When it
|
|
// stops for any other reason, such as its port being in use, it asks fx to
|
|
// shut down with exit code 1.
|
|
func (s *Server) serveUntilShutdown() {
|
|
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)
|
|
|
|
err = s.shutdowner.Shutdown(fx.ExitCode(1))
|
|
if err != nil {
|
|
s.log.Error("shutdown request failed", "error", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// ServeHTTP implements http.Handler.
|
|
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
s.router.ServeHTTP(w, r)
|
|
}
|