camfish/examples/http/main.go

91 lines
2.3 KiB
Go

// 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("(i) [http-server] 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, "<!DOCTYPE html><html><head><title>inventory</title></head><body>")
fmt.Fprintf(res, "<table><tr><th>Item</th><th>Count</th></tr>")
for item, count := range this.database.Inventory() {
fmt.Fprintf(res, "<tr><td>%s</td><td>%d</td></tr>", item, count)
}
fmt.Fprintf(res, "</table>")
fmt.Fprintf(res, "</body></html>")
}
// 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)
}
}
}