examples/http: Add HTTP example to demonstrate RunShutdownable
This commit is contained in:
parent
5e38cec135
commit
772e9ca290
90
examples/http/main.go
Normal file
90
examples/http/main.go
Normal file
@ -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, "<!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)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user