From 3fb9c66ffa0bf87cbd7cc1b5f4129f3447e94c13 Mon Sep 17 00:00:00 2001 From: "Gabriel A. Giovanini" Date: Wed, 1 May 2024 20:27:00 +0200 Subject: feat: Initial http server code --- Makefile | 5 +++ go.mod | 5 +++ go.sum | 2 ++ main.go | 35 +++++++++++++++++++ pkg/worker/http.go | 26 +++++++++++++++ pkg/worker/worker.go | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 167 insertions(+) create mode 100644 Makefile create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100644 pkg/worker/http.go create mode 100644 pkg/worker/worker.go diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7a0e125 --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +build: + go build -o bin/cerrado + +run: + go run . diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..cca006e --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module git.gabrielgio.me/cerrado + +go 1.22.2 + +require golang.org/x/sync v0.7.0 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e8ef4a3 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= diff --git a/main.go b/main.go new file mode 100644 index 0000000..7c80564 --- /dev/null +++ b/main.go @@ -0,0 +1,35 @@ +package main + +import ( + "context" + "log/slog" + "net/http" + "os" + "os/signal" + "time" + + "git.gabrielgio.me/cerrado/pkg/worker" +) + +func main() { + ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill) + defer stop() + if err := run(ctx); err != nil { + os.Exit(1) + } +} + +func run(ctx context.Context) error { + mux := http.NewServeMux() + mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { + if _, err := w.Write([]byte("Hello world!")); err != nil { + slog.Error("Error handling index", "error", err) + } + }) + serverTask := worker.NewServerTask(&http.Server{Handler: mux, Addr: "0.0.0.0:8080"}) + + pool := worker.NewTaskPool() + pool.AddTask("http-server", 5*time.Second, serverTask) + + return pool.Start(ctx) +} diff --git a/pkg/worker/http.go b/pkg/worker/http.go new file mode 100644 index 0000000..1d56f86 --- /dev/null +++ b/pkg/worker/http.go @@ -0,0 +1,26 @@ +package worker + +import ( + "context" + "net/http" +) + +type ServerTask struct { + server *http.Server +} + +func NewServerTask(server *http.Server) *ServerTask { + return &ServerTask{ + server: server, + } +} + +func (self *ServerTask) Start(ctx context.Context) error { + go func() { + // nolint: errcheck + self.server.ListenAndServe() + }() + + <-ctx.Done() + return self.server.Shutdown(ctx) +} diff --git a/pkg/worker/worker.go b/pkg/worker/worker.go new file mode 100644 index 0000000..6b5c21c --- /dev/null +++ b/pkg/worker/worker.go @@ -0,0 +1,94 @@ +package worker + +import ( + "context" + "errors" + "log/slog" + "time" + + "golang.org/x/sync/errgroup" +) + +type ( + Task interface { + Start(context.Context) error + } + + Work struct { + Name string + Task Task + wait time.Duration + } + + TaskPool struct { + tasks []*Work + } +) + +const ( + format = "2006.01.02 15:04:05" +) + +func NewTaskPool() *TaskPool { + return &TaskPool{} +} + +func (w *Work) run(ctx context.Context) error { + // first time fire from the get go + timer := time.NewTimer(time.Nanosecond) + + for { + select { + case <-ctx.Done(): + return ctx.Err() + case <-timer.C: + if err := w.Task.Start(ctx); err != nil && !errors.Is(err, context.Canceled) { + return err + } + } + timer.Reset(w.wait) + } +} + +func (self *TaskPool) AddTask(name string, wait time.Duration, task Task) { + self.tasks = append(self.tasks, &Work{ + Name: name, + Task: task, + wait: wait, + }) +} + +func (self *TaskPool) Start(ctx context.Context) error { + var g errgroup.Group + + for _, w := range self.tasks { + g.Go(func(w *Work) func() error { + return func() error { + slog.Info("Process starting", "time", time.Now().Format(format), "name", w.Name) + now := time.Now() + if err := w.run(ctx); err != nil && !errors.Is(context.Canceled, err) { + since := time.Since(now) + slog.Error( + "Process erred", + "time", time.Now().Format(format), + "name", w.Name, + "error", err, + "duration", since, + ) + return err + } else { + since := time.Since(now) + slog.Info( + "Process ended", + "time", time.Now().Format(format), + "name", w.Name, + "duration", since, + ) + } + return nil + } + }(w)) + } + + return g.Wait() +} -- cgit v1.2.3