From 772e9ca29079a996617b2a121d4a2f944567fb46 Mon Sep 17 00:00:00 2001 From: Sasha Koshka Date: Thu, 30 Jan 2025 19:30:07 -0500 Subject: [PATCH] examples/http: Add HTTP example to demonstrate RunShutdownable --- examples/http/main.go | 90 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 examples/http/main.go diff --git a/examples/http/main.go b/examples/http/main.go new file mode 100644 index 0000000..834b522 --- /dev/null +++ b/examples/http/main.go @@ -0,0 +1,90 @@ +// Example http demonstrates the usage of [camfish.RunShutdowner] to run an http +// server. +package main + +import "fmt" +import "log" +import "iter" +import "errors" +import "context" +import "net/http" +import "git.tebibyte.media/sashakoshka/camfish" +import "git.tebibyte.media/sashakoshka/go-util/sync" + +func main() { + camfish.Run("http", + "Example http demonstrates the usage of " + + "camfish.RunShutdowner to run an http server", + new(httpServer), + new(database)) +} + +// httpServer serves data over http. +type httpServer struct { + server *http.Server + database *database +} +var _ camfish.RunShutdownable = new(httpServer) +var _ camfish.Initializable = new(httpServer) +func (this *httpServer) Type() string { return "http-server" } + +func (this *httpServer) Init(ctx context.Context) error { + this.server = &http.Server { + Addr: "localhost:8080", + Handler: this, + } + if actor, ok := camfish.Find("database").(*database); ok { + this.database = actor + } else { + return errors.New("could not locate database") + } + return ctx.Err() +} + +func (this *httpServer) Run() error { + log.Printf("[http] listening on %s", this.server.Addr) + err := this.server.ListenAndServe() + if errors.Is(err, http.ErrServerClosed) { return nil } + return err +} + +func (this *httpServer) Shutdown(ctx context.Context) error { + return this.server.Shutdown(ctx) +} + +func (this *httpServer) ServeHTTP(res http.ResponseWriter, req *http.Request) { + fmt.Fprintf(res, "inventory") + fmt.Fprintf(res, "") + for item, count := range this.database.Inventory() { + fmt.Fprintf(res, "", item, count) + } + fmt.Fprintf(res, "
ItemCount
%s%d
") + fmt.Fprintf(res, "") +} + +// database provides data that can be served. +type database struct { + inventory usync.RWMonitor[map[string] int] +} +func (this *database) Type() string { return "database" } + +func (this *database) Init(ctx context.Context) error { + this.inventory.Set(map[string] int { + "screws": 34, + "blood": 90, + "paperclips": 5230, + "wood": 3, + "grains of rice": 238409, + }) + return ctx.Err() +} + +func (this *database) Inventory() iter.Seq2[string, int] { + return func(yield func(string, int) bool) { + inventory, done := this.inventory.RBorrow() + defer done() + for item, amount := range inventory { + yield(item, amount) + } + } +}