package server import ( "errors" "net" "net/http" "strconv" "time" ) const ( // httpReadTimeout is the maximum duration for reading the // entire request, including the body. httpReadTimeout = 10 * time.Second // httpWriteTimeout is the maximum duration before timing out // writes of the response. It must stay above the router's // requestTimeout (60s, in routes.go) so the middleware timeout // fires first and returns a clean 503, rather than the transport // cutting the connection at the socket write deadline. httpWriteTimeout = 65 * time.Second // httpMaxHeaderBytes is the maximum number of bytes the // server will read parsing the request headers. httpMaxHeaderBytes = 1 << 20 ) // listenAddr renders the address the HTTP listener binds. // // The host half is always present: an empty host would be the // wildcard, and the whole point of BIND_ADDRESS is that binding every // interface is a choice the operator makes rather than one the // process makes for them. Config guarantees a literal, so // JoinHostPort's bracketing is enough to make IPv6 well formed. func (s *Server) listenAddr() string { return net.JoinHostPort( s.params.Config.BindAddress, strconv.Itoa(s.params.Config.Port), ) } // newHTTPServer builds the HTTP server for this Server's // configuration. // // It is called from New, on the constructing goroutine, rather than // from the serving goroutine that used to assign s.httpServer // directly. Two goroutines reach that field — the serving goroutine // and the fx stop hook, which calls Shutdown on it — with nothing // ordering them. Constructing it during New puts the write before // every hook fx will later run, which both removes the race and rules // out the nil dereference a stop that arrived before the serving // goroutine had run would have caused. func (s *Server) newHTTPServer() *http.Server { return &http.Server{ Addr: s.listenAddr(), ReadTimeout: httpReadTimeout, WriteTimeout: httpWriteTimeout, MaxHeaderBytes: httpMaxHeaderBytes, Handler: s, } } func (s *Server) serveUntilShutdown() { // add routes // this does any necessary setup in each handler 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) s.shutdownWithFailure() } } // ServeHTTP delegates to the router. func (s *Server) ServeHTTP( w http.ResponseWriter, r *http.Request, ) { s.router.ServeHTTP(w, r) }