server: expose Handle/HandleFunc for custom checker routes
Lets plugins register auxiliary endpoints (debug pages, webhooks, UI assets) on the SDK mux, with TrackWork as an opt-in for the /health load signal.
This commit is contained in:
parent
d847c71a50
commit
0c6a886e82
2 changed files with 50 additions and 11 deletions
26
README.md
26
README.md
|
|
@ -30,6 +30,32 @@ go get git.happydns.org/checker-sdk-go/checker
|
||||||
See [checker-dummy](https://git.happydns.org/checker-dummy) for a
|
See [checker-dummy](https://git.happydns.org/checker-dummy) for a
|
||||||
fully working, documented template.
|
fully working, documented template.
|
||||||
|
|
||||||
|
## Extending the server
|
||||||
|
|
||||||
|
`checker.Server` exposes the standard SDK routes (`/health`, `/collect`,
|
||||||
|
and, depending on the provider's optional interfaces, `/definition`,
|
||||||
|
`/evaluate`, `/report`). Plugins that need to serve auxiliary endpoints
|
||||||
|
(debug pages, webhooks, custom UI assets, …) can register them on the
|
||||||
|
same mux:
|
||||||
|
|
||||||
|
```go
|
||||||
|
srv := checker.NewServer(provider)
|
||||||
|
|
||||||
|
srv.HandleFunc("GET /debug/state", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// …
|
||||||
|
})
|
||||||
|
|
||||||
|
// Opt a custom route into the in-flight / load-average signal
|
||||||
|
// reported on /health:
|
||||||
|
srv.Handle("POST /webhook", srv.TrackWork(myWebhookHandler))
|
||||||
|
|
||||||
|
log.Fatal(srv.ListenAndServe(":8080"))
|
||||||
|
```
|
||||||
|
|
||||||
|
Patterns that collide with built-in routes panic at registration —
|
||||||
|
pick non-overlapping paths. Custom handlers are not wrapped by the
|
||||||
|
load-tracking middleware unless you opt in via `TrackWork`.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Apache License 2.0. See [LICENSE](LICENSE) and [NOTICE](NOTICE).
|
Apache License 2.0. See [LICENSE](LICENSE) and [NOTICE](NOTICE).
|
||||||
|
|
|
||||||
|
|
@ -111,19 +111,21 @@ func NewServer(provider ObservationProvider) *Server {
|
||||||
}
|
}
|
||||||
s.mux = http.NewServeMux()
|
s.mux = http.NewServeMux()
|
||||||
s.mux.HandleFunc("GET /health", s.handleHealth)
|
s.mux.HandleFunc("GET /health", s.handleHealth)
|
||||||
s.mux.Handle("POST /collect", s.trackWork(http.HandlerFunc(s.handleCollect)))
|
s.mux.Handle("POST /collect", s.TrackWork(http.HandlerFunc(s.handleCollect)))
|
||||||
|
|
||||||
if dp, ok := provider.(CheckerDefinitionProvider); ok {
|
if dp, ok := provider.(CheckerDefinitionProvider); ok {
|
||||||
s.definition = dp.Definition()
|
if def := dp.Definition(); def != nil {
|
||||||
s.definition.BuildRulesInfo()
|
s.definition = def
|
||||||
s.mux.HandleFunc("GET /definition", s.handleDefinition)
|
s.definition.BuildRulesInfo()
|
||||||
s.mux.Handle("POST /evaluate", s.trackWork(http.HandlerFunc(s.handleEvaluate)))
|
s.mux.HandleFunc("GET /definition", s.handleDefinition)
|
||||||
|
s.mux.Handle("POST /evaluate", s.TrackWork(http.HandlerFunc(s.handleEvaluate)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := provider.(CheckerHTMLReporter); ok {
|
if _, ok := provider.(CheckerHTMLReporter); ok {
|
||||||
s.mux.Handle("POST /report", s.trackWork(http.HandlerFunc(s.handleReport)))
|
s.mux.Handle("POST /report", s.TrackWork(http.HandlerFunc(s.handleReport)))
|
||||||
} else if _, ok := provider.(CheckerMetricsReporter); ok {
|
} else if _, ok := provider.(CheckerMetricsReporter); ok {
|
||||||
s.mux.Handle("POST /report", s.trackWork(http.HandlerFunc(s.handleReport)))
|
s.mux.Handle("POST /report", s.TrackWork(http.HandlerFunc(s.handleReport)))
|
||||||
}
|
}
|
||||||
|
|
||||||
go s.runSampler(ctx)
|
go s.runSampler(ctx)
|
||||||
|
|
@ -137,6 +139,18 @@ func (s *Server) Handler() http.Handler {
|
||||||
return requestLogger(s.mux)
|
return requestLogger(s.mux)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle registers an auxiliary handler on the server's mux. Must be called
|
||||||
|
// before ListenAndServe or Handler(). Custom handlers are not tracked by
|
||||||
|
// TrackWork; wrap them explicitly if you want them counted in /health load.
|
||||||
|
func (s *Server) Handle(pattern string, handler http.Handler) {
|
||||||
|
s.mux.Handle(pattern, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleFunc is the http.HandlerFunc-flavoured counterpart of Handle.
|
||||||
|
func (s *Server) HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request)) {
|
||||||
|
s.mux.HandleFunc(pattern, handler)
|
||||||
|
}
|
||||||
|
|
||||||
// ListenAndServe starts the HTTP server on the given address.
|
// ListenAndServe starts the HTTP server on the given address.
|
||||||
//
|
//
|
||||||
// ListenAndServe does not stop the background load-average sampler on return;
|
// ListenAndServe does not stop the background load-average sampler on return;
|
||||||
|
|
@ -158,10 +172,9 @@ func (s *Server) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// trackWork wraps a handler with in-flight and total-request accounting.
|
// TrackWork wraps a handler with in-flight and total-request accounting,
|
||||||
// It is applied only to "work" endpoints (/collect, /evaluate, /report) so
|
// opting custom routes into the load signal reported on /health.
|
||||||
// that /health polling traffic does not pollute the load signal.
|
func (s *Server) TrackWork(next http.Handler) http.Handler {
|
||||||
func (s *Server) trackWork(next http.Handler) http.Handler {
|
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
s.inFlight.Add(1)
|
s.inFlight.Add(1)
|
||||||
s.totalRequests.Add(1)
|
s.totalRequests.Add(1)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue