diff options
| author | Mel <einebeere@gmail.com> | 2024-12-29 17:21:06 +0100 |
|---|---|---|
| committer | Mel <einebeere@gmail.com> | 2024-12-29 17:21:06 +0100 |
| commit | 5025fab41ac37665ad35967f1f03c16588461605 (patch) | |
| tree | 526196063f63a88e5a7877bc136e7cb993d1bf82 | |
| parent | 54b021e352c6e8be605f4d223916ebf97a59f9a4 (diff) | |
| download | specimen-5025fab41ac37665ad35967f1f03c16588461605.tar.zst specimen-5025fab41ac37665ad35967f1f03c16588461605.zip | |
Simple Go HTTP server with graceful signal handling
Signed-off-by: Mel <einebeere@gmail.com>
| -rw-r--r-- | application/cmd/specimen/main.go | 58 | ||||
| -rw-r--r-- | application/pkg/specimen/handler.go | 19 | ||||
| -rw-r--r-- | application/pkg/specimen/server.go | 54 | ||||
| -rw-r--r-- | application/pkg/specimen/specimen.go | 5 | ||||
| -rw-r--r-- | application/pkg/util/slog.go | 7 |
5 files changed, 136 insertions, 7 deletions
diff --git a/application/cmd/specimen/main.go b/application/cmd/specimen/main.go index 8522b03..cf7ff36 100644 --- a/application/cmd/specimen/main.go +++ b/application/cmd/specimen/main.go @@ -1,10 +1,64 @@ package main import ( - "fmt" + "context" + "log/slog" + "os" + "os/signal" + "time" + "git.rnrd.eu/specimen/pkg/specimen" + "git.rnrd.eu/specimen/pkg/util" ) +const version = "3.3.3" + func main() { - fmt.Println(specimen.ASimpleMessage()) + ctx := context.Background() + logger := createLogger() + + if err := start(ctx, logger); err != nil { + logger.Error("failed to start the specimen application :(", util.SlogErr(err)) + os.Exit(1) + } +} + +func start(ctx context.Context, logger *slog.Logger) error { + // listen for SIGINT (or others on other systems, but who cares about those.. :3) + ctx, cancel := signal.NotifyContext(ctx, os.Interrupt) + defer cancel() + + logger.Info("starting the specimen application!", slog.String("version", version)) + + // start the specimen server! + server := specimen.NewServer(logger) + go server.Start(ctx) + + // listen for signals to gracefully shutdown the server + return listenForSignals(ctx, server, logger) +} + +func listenForSignals(ctx context.Context, server *specimen.Server, logger *slog.Logger) error { + // waiting for the global context to be done (happens on SIGINT) + <-ctx.Done() + logger.Info("received signal to shutdown the server, trying to gracefully shutdown...") + + shutdownCtx, cancelShutdown := context.WithTimeout(context.Background(), 5*time.Second) + defer cancelShutdown() + + if err := server.Shutdown(shutdownCtx); err != nil { + logger.Error("failed to gracefully shutdown the server :( forcing it.", util.SlogErr(err)) + os.Exit(1) + } + + logger.Info("specimen has been gracefully shutdown! goodbye!") + return nil +} + +func createLogger() *slog.Logger { + handlerOptions := slog.HandlerOptions{ + Level: slog.LevelDebug, + } + consoleHandler := slog.NewTextHandler(os.Stderr, &handlerOptions) + return slog.New(consoleHandler) } diff --git a/application/pkg/specimen/handler.go b/application/pkg/specimen/handler.go new file mode 100644 index 0000000..05a56ec --- /dev/null +++ b/application/pkg/specimen/handler.go @@ -0,0 +1,19 @@ +package specimen + +import ( + "log/slog" + "net/http" +) + +type requestHandler struct { + logger *slog.Logger +} + +func newRequestHandler(logger *slog.Logger) *requestHandler { + return &requestHandler{logger: logger} +} + +func (h *requestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + h.logger.Info("received a request!", slog.String("method", r.Method), slog.String("url", r.URL.String())) + w.WriteHeader(http.StatusOK) +} diff --git a/application/pkg/specimen/server.go b/application/pkg/specimen/server.go new file mode 100644 index 0000000..cccf6e2 --- /dev/null +++ b/application/pkg/specimen/server.go @@ -0,0 +1,54 @@ +package specimen + +import ( + "context" + "fmt" + "log/slog" + "net/http" + "time" +) + +const port = 4444 + +type Server struct { + logger *slog.Logger + server *http.Server + handler *requestHandler +} + +func NewServer(logger *slog.Logger) *Server { + handler := newRequestHandler(logger) + + return &Server{ + logger: logger, + server: &http.Server{ + Addr: fmt.Sprintf(":%d", port), + Handler: handler, + ReadTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + }, + } +} + +func (s *Server) Start(ctx context.Context) { + s.logger.Info("listening for incoming connections...") + err := s.server.ListenAndServe() + + switch err { + case http.ErrServerClosed: + s.logger.Info("listening has been successfully stopped.") + default: + s.logger.Error("failed to start the http server.", slog.Any("error", err)) + } +} + +func (s *Server) Shutdown(shutdownCtx context.Context) error { + s.logger.Info("shutting down the http server...") + if err := s.server.Shutdown(shutdownCtx); err != nil { + s.logger.Error("failed to shutdown the server :(", slog.Any("error", err)) + return err + } + + s.logger.Info("http server has been shutdown!") + return nil +} diff --git a/application/pkg/specimen/specimen.go b/application/pkg/specimen/specimen.go deleted file mode 100644 index 3e11364..0000000 --- a/application/pkg/specimen/specimen.go +++ /dev/null @@ -1,5 +0,0 @@ -package specimen - -func ASimpleMessage() string { - return "hi! everything is still working!! :)" -} diff --git a/application/pkg/util/slog.go b/application/pkg/util/slog.go new file mode 100644 index 0000000..ebae96f --- /dev/null +++ b/application/pkg/util/slog.go @@ -0,0 +1,7 @@ +package util + +import "log/slog" + +func SlogErr(err error) slog.Attr { + return slog.Any("error", err) +} |
