From 18c168e2debe952e932e360ec3dbc9c58ad3cd3f Mon Sep 17 00:00:00 2001 From: Mel Date: Sun, 29 Dec 2024 18:06:59 +0100 Subject: Read configuration from flags and reply with configured name Signed-off-by: Mel --- application/cmd/specimen/cfg/config.go | 65 ++++++++++++++++++++++++++++++++++ application/cmd/specimen/main.go | 15 ++++++-- application/pkg/specimen/handler.go | 34 ++++++++++++++++-- application/pkg/specimen/server.go | 14 ++++---- 4 files changed, 116 insertions(+), 12 deletions(-) create mode 100644 application/cmd/specimen/cfg/config.go (limited to 'application') diff --git a/application/cmd/specimen/cfg/config.go b/application/cmd/specimen/cfg/config.go new file mode 100644 index 0000000..713ad96 --- /dev/null +++ b/application/cmd/specimen/cfg/config.go @@ -0,0 +1,65 @@ +package cfg + +import ( + "errors" + "flag" + "fmt" + "os" +) + +type Config struct { + Port int + Address string + + // since the server will be restarted when the name file changes, + // we can read the file directly after parsing the arguments, + // and pass the name to the server. no need to pass the path to the server. + Name string +} + +func ParseFromArguments() (Config, error) { + c := Config{} + var namePath string + + defineFlags(&c, &namePath) + flag.Parse() + + if err := validateAndSet(&c, namePath); err != nil { + return Config{}, err + } + + return c, nil +} + +func defineFlags(c *Config, namePath *string) { + flag.IntVar(&c.Port, "port", 4444, "port to listen on") + flag.StringVar(&c.Address, "address", "127.0.0.1", "address to listen on") + flag.StringVar(namePath, "name", "", "path to the name file") +} + +func validateAndSet(c *Config, namePath string) error { + if c.Port < 1 || c.Port > 65535 { + return errors.New("port is out of range") + } + + if namePath == "" { + return errors.New("name file path is required") + } + + file, err := os.Open(namePath) + if err != nil { + return fmt.Errorf("failed to open the name file. %w", err) + } + + name := make([]byte, 1024) + n, err := file.Read(name) + if err != nil { + return fmt.Errorf("failed to read the name file. %w", err) + } + + fmt.Printf("read %d bytes from the name file\n", n) + fmt.Printf("name: %s\n", string(name[:n])) + + c.Name = string(name[:n]) + return nil +} diff --git a/application/cmd/specimen/main.go b/application/cmd/specimen/main.go index cf7ff36..9b3cc03 100644 --- a/application/cmd/specimen/main.go +++ b/application/cmd/specimen/main.go @@ -2,11 +2,13 @@ package main import ( "context" + "flag" "log/slog" "os" "os/signal" "time" + "git.rnrd.eu/specimen/cmd/specimen/cfg" "git.rnrd.eu/specimen/pkg/specimen" "git.rnrd.eu/specimen/pkg/util" ) @@ -17,13 +19,20 @@ func main() { ctx := context.Background() logger := createLogger() - if err := start(ctx, logger); err != nil { + config, err := cfg.ParseFromArguments() + if err != nil { + logger.Error("argument error!", util.SlogErr(err)) + flag.Usage() + os.Exit(1) + } + + if err := start(ctx, logger, config); 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 { +func start(ctx context.Context, logger *slog.Logger, config cfg.Config) error { // listen for SIGINT (or others on other systems, but who cares about those.. :3) ctx, cancel := signal.NotifyContext(ctx, os.Interrupt) defer cancel() @@ -31,7 +40,7 @@ func start(ctx context.Context, logger *slog.Logger) error { logger.Info("starting the specimen application!", slog.String("version", version)) // start the specimen server! - server := specimen.NewServer(logger) + server := specimen.NewServer(logger, config) go server.Start(ctx) // listen for signals to gracefully shutdown the server diff --git a/application/pkg/specimen/handler.go b/application/pkg/specimen/handler.go index 05a56ec..6d4e733 100644 --- a/application/pkg/specimen/handler.go +++ b/application/pkg/specimen/handler.go @@ -1,19 +1,47 @@ package specimen import ( + "fmt" "log/slog" "net/http" ) type requestHandler struct { logger *slog.Logger + name string } -func newRequestHandler(logger *slog.Logger) *requestHandler { - return &requestHandler{logger: logger} +func newRequestHandler(logger *slog.Logger, name string) *requestHandler { + return &requestHandler{logger: logger, name: name} } 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())) + // NOTE: we could also write some logger middleware, + // but that probably would be overkill for this... + logger := h.logger.With(requestLogAttrs(r)) + logger.Info("incoming request.") + + reply := h.message() + + w.Header().Set("Content-Type", "text/plain") w.WriteHeader(http.StatusOK) + if _, err := w.Write([]byte(reply)); err != nil { + logger.Error("failed to write the response.", slog.Any("error", err)) + return + } + + logger.Info("request successfully handled.") +} + +func (h *requestHandler) message() string { + return fmt.Sprintf("hello %s", h.name) +} + +func requestLogAttrs(r *http.Request) slog.Attr { + return slog.Group( + "request", + slog.String("method", r.Method), + slog.String("url", r.URL.String()), + slog.String("remote_addr", r.RemoteAddr), + ) } diff --git a/application/pkg/specimen/server.go b/application/pkg/specimen/server.go index cccf6e2..2a222a3 100644 --- a/application/pkg/specimen/server.go +++ b/application/pkg/specimen/server.go @@ -6,23 +6,25 @@ import ( "log/slog" "net/http" "time" -) -const port = 4444 + "git.rnrd.eu/specimen/cmd/specimen/cfg" +) type Server struct { logger *slog.Logger + config cfg.Config server *http.Server handler *requestHandler } -func NewServer(logger *slog.Logger) *Server { - handler := newRequestHandler(logger) +func NewServer(logger *slog.Logger, config cfg.Config) *Server { + handler := newRequestHandler(logger, config.Name) return &Server{ logger: logger, + config: config, server: &http.Server{ - Addr: fmt.Sprintf(":%d", port), + Addr: fmt.Sprintf("%s:%d", config.Address, config.Port), Handler: handler, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, @@ -31,7 +33,7 @@ func NewServer(logger *slog.Logger) *Server { } func (s *Server) Start(ctx context.Context) { - s.logger.Info("listening for incoming connections...") + s.logger.Info("listening for incoming connections...", slog.String("address", s.server.Addr)) err := s.server.ListenAndServe() switch err { -- cgit 1.4.1