Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

simpler http handler tests #38

Merged
merged 1 commit into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions httpserver/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import (
"github.com/flashbots/go-template/metrics"
)

func (s *Server) handleAPI(w http.ResponseWriter, r *http.Request) {
m := s.metricsSrv.Float64Histogram(
func (srv *Server) handleAPI(w http.ResponseWriter, r *http.Request) {
m := srv.metricsSrv.Float64Histogram(
"request_duration_api",
"API request handling duration",
metrics.UomMicroseconds,
Expand All @@ -23,32 +23,32 @@ func (s *Server) handleAPI(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}

func (s *Server) handleLivenessCheck(w http.ResponseWriter, r *http.Request) {
func (srv *Server) handleLivenessCheck(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}

func (s *Server) handleReadinessCheck(w http.ResponseWriter, r *http.Request) {
if !s.isReady.Load() {
func (srv *Server) handleReadinessCheck(w http.ResponseWriter, r *http.Request) {
if !srv.isReady.Load() {
w.WriteHeader(http.StatusServiceUnavailable)
return
}

w.WriteHeader(http.StatusOK)
}

func (s *Server) handleDrain(w http.ResponseWriter, r *http.Request) {
if wasReady := s.isReady.Swap(false); !wasReady {
func (srv *Server) handleDrain(w http.ResponseWriter, r *http.Request) {
if wasReady := srv.isReady.Swap(false); !wasReady {
return
}
// l := logutils.ZapFromRequest(r)
s.log.Info("Server marked as not ready")
time.Sleep(s.cfg.DrainDuration) // Give LB enough time to detect us not ready
srv.log.Info("Server marked as not ready")
time.Sleep(srv.cfg.DrainDuration) // Give LB enough time to detect us not ready
}

func (s *Server) handleUndrain(w http.ResponseWriter, r *http.Request) {
if wasReady := s.isReady.Swap(true); wasReady {
func (srv *Server) handleUndrain(w http.ResponseWriter, r *http.Request) {
if wasReady := srv.isReady.Swap(true); wasReady {
return
}
// l := logutils.ZapFromRequest(r)
s.log.Info("Server marked as ready")
srv.log.Info("Server marked as ready")
}
18 changes: 18 additions & 0 deletions httpserver/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,21 @@ func Test_Handlers_Healthcheck_Drain_Undrain(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode, "Healthcheck must return `Ok` after undraining")
}
}

func Test_Handlers_Simple(t *testing.T) {
// This test doesn't need the server to actually start and serve. Instead it just tests the handlers.
//nolint: exhaustruct
srv, err := New(&HTTPServerConfig{
Log: getTestLogger(),
})
require.NoError(t, err)

{ // Check health
req, err := http.NewRequest(http.MethodGet, "/readyz", nil) //nolint:goconst,nolintlint
require.NoError(t, err)

rr := httptest.NewRecorder()
srv.getRouter().ServeHTTP(rr, req)
require.Equal(t, http.StatusOK, rr.Code)
}
}
63 changes: 33 additions & 30 deletions httpserver/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,72 +50,75 @@ func New(cfg *HTTPServerConfig) (srv *Server, err error) {
}
srv.isReady.Swap(true)

srv.srv = &http.Server{
Addr: cfg.ListenAddr,
Handler: srv.getRouter(),
ReadTimeout: cfg.ReadTimeout,
WriteTimeout: cfg.WriteTimeout,
}

return srv, nil
}

func (srv *Server) getRouter() http.Handler {
mux := chi.NewRouter()
mux.With(srv.httpLogger).Get("/api", srv.handleAPI) // Never serve at `/` (root) path
mux.With(srv.httpLogger).Get("/livez", srv.handleLivenessCheck)
mux.With(srv.httpLogger).Get("/readyz", srv.handleReadinessCheck)
mux.With(srv.httpLogger).Get("/drain", srv.handleDrain)
mux.With(srv.httpLogger).Get("/undrain", srv.handleUndrain)

if cfg.EnablePprof {
if srv.cfg.EnablePprof {
srv.log.Info("pprof API enabled")
mux.Mount("/debug", middleware.Profiler())
}

srv.srv = &http.Server{
Addr: cfg.ListenAddr,
Handler: mux,
ReadTimeout: cfg.ReadTimeout,
WriteTimeout: cfg.WriteTimeout,
}

return srv, nil
return mux
}

func (s *Server) httpLogger(next http.Handler) http.Handler {
return httplogger.LoggingMiddlewareSlog(s.log, next)
func (srv *Server) httpLogger(next http.Handler) http.Handler {
return httplogger.LoggingMiddlewareSlog(srv.log, next)
}

func (s *Server) RunInBackground() {
func (srv *Server) RunInBackground() {
// metrics
if s.cfg.MetricsAddr != "" {
if srv.cfg.MetricsAddr != "" {
go func() {
s.log.With("metricsAddress", s.cfg.MetricsAddr).Info("Starting metrics server")
err := s.metricsSrv.ListenAndServe()
srv.log.With("metricsAddress", srv.cfg.MetricsAddr).Info("Starting metrics server")
err := srv.metricsSrv.ListenAndServe()
if err != nil && !errors.Is(err, http.ErrServerClosed) {
s.log.Error("HTTP server failed", "err", err)
srv.log.Error("HTTP server failed", "err", err)
}
}()
}

// api
go func() {
s.log.Info("Starting HTTP server", "listenAddress", s.cfg.ListenAddr)
if err := s.srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
s.log.Error("HTTP server failed", "err", err)
srv.log.Info("Starting HTTP server", "listenAddress", srv.cfg.ListenAddr)
if err := srv.srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
srv.log.Error("HTTP server failed", "err", err)
}
}()
}

func (s *Server) Shutdown() {
func (srv *Server) Shutdown() {
// api
ctx, cancel := context.WithTimeout(context.Background(), s.cfg.GracefulShutdownDuration)
ctx, cancel := context.WithTimeout(context.Background(), srv.cfg.GracefulShutdownDuration)
defer cancel()
if err := s.srv.Shutdown(ctx); err != nil {
s.log.Error("Graceful HTTP server shutdown failed", "err", err)
if err := srv.srv.Shutdown(ctx); err != nil {
srv.log.Error("Graceful HTTP server shutdown failed", "err", err)
} else {
s.log.Info("HTTP server gracefully stopped")
srv.log.Info("HTTP server gracefully stopped")
}

// metrics
if len(s.cfg.MetricsAddr) != 0 {
ctx, cancel := context.WithTimeout(context.Background(), s.cfg.GracefulShutdownDuration)
if len(srv.cfg.MetricsAddr) != 0 {
ctx, cancel := context.WithTimeout(context.Background(), srv.cfg.GracefulShutdownDuration)
defer cancel()

if err := s.metricsSrv.Shutdown(ctx); err != nil {
s.log.Error("Graceful metrics server shutdown failed", "err", err)
if err := srv.metricsSrv.Shutdown(ctx); err != nil {
srv.log.Error("Graceful metrics server shutdown failed", "err", err)
} else {
s.log.Info("Metrics server gracefully stopped")
srv.log.Info("Metrics server gracefully stopped")
}
}
}
Loading