mirror of
https://github.com/therootcompany/golib.git
synced 2026-04-24 20:58:00 +00:00
feat(check-ip): add /healthz + http.Server timeouts
- /healthz reports per-dataset loaded/size/loaded_at; 503 until inbound+outbound+geoip are all loaded, 200 after - ReadHeaderTimeout/ReadTimeout/WriteTimeout/IdleTimeout + 64KB max header — production defaults
This commit is contained in:
parent
bcda75d673
commit
1c99c8b831
@ -14,6 +14,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/therootcompany/golib/net/geoip"
|
"github.com/therootcompany/golib/net/geoip"
|
||||||
|
"github.com/therootcompany/golib/net/ipcohort"
|
||||||
|
"github.com/therootcompany/golib/sync/dataset"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Result is the JSON verdict for a single IP.
|
// Result is the JSON verdict for a single IP.
|
||||||
@ -106,15 +108,67 @@ func (c *IPCheck) handle(w http.ResponseWriter, r *http.Request) {
|
|||||||
c.writeText(w, res)
|
c.writeText(w, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type dsStatus struct {
|
||||||
|
Loaded bool `json:"loaded"`
|
||||||
|
Size int `json:"size,omitzero"`
|
||||||
|
LoadedAt time.Time `json:"loaded_at,omitzero"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// healthz reports per-dataset load state and an overall ready flag.
|
||||||
|
// Returns 200 when all required sets (inbound, outbound, geoip) are
|
||||||
|
// loaded, 503 while any are still empty.
|
||||||
|
func (c *IPCheck) healthz(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
cohortStatus := func(v *dataset.View[ipcohort.Cohort]) dsStatus {
|
||||||
|
s := dsStatus{LoadedAt: v.LoadedAt()}
|
||||||
|
if cur := v.Value(); cur != nil {
|
||||||
|
s.Loaded, s.Size = true, cur.Size()
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
datasets := map[string]dsStatus{
|
||||||
|
"inbound": cohortStatus(c.inbound),
|
||||||
|
"outbound": cohortStatus(c.outbound),
|
||||||
|
"geoip": {
|
||||||
|
Loaded: c.geo.Value() != nil,
|
||||||
|
LoadedAt: c.geo.LoadedAt(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if c.whitelist != nil {
|
||||||
|
datasets["whitelist"] = cohortStatus(c.whitelist)
|
||||||
|
}
|
||||||
|
|
||||||
|
ready := datasets["inbound"].Loaded && datasets["outbound"].Loaded && datasets["geoip"].Loaded
|
||||||
|
resp := struct {
|
||||||
|
Ready bool `json:"ready"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
Datasets map[string]dsStatus `json:"datasets"`
|
||||||
|
}{ready, version, datasets}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
if !ready {
|
||||||
|
w.WriteHeader(http.StatusServiceUnavailable)
|
||||||
|
}
|
||||||
|
enc := json.NewEncoder(w)
|
||||||
|
enc.SetIndent("", " ")
|
||||||
|
_ = enc.Encode(resp)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *IPCheck) serve(ctx context.Context) error {
|
func (c *IPCheck) serve(ctx context.Context) error {
|
||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
mux.HandleFunc("GET /check", c.handle)
|
mux.HandleFunc("GET /check", c.handle)
|
||||||
|
mux.HandleFunc("GET /healthz", c.healthz)
|
||||||
mux.HandleFunc("GET /{$}", c.handle)
|
mux.HandleFunc("GET /{$}", c.handle)
|
||||||
|
|
||||||
srv := &http.Server{
|
srv := &http.Server{
|
||||||
Addr: c.Bind,
|
Addr: c.Bind,
|
||||||
Handler: mux,
|
Handler: mux,
|
||||||
BaseContext: func(_ net.Listener) context.Context { return ctx },
|
BaseContext: func(_ net.Listener) context.Context { return ctx },
|
||||||
|
ReadHeaderTimeout: 5 * time.Second,
|
||||||
|
ReadTimeout: 10 * time.Second,
|
||||||
|
WriteTimeout: 15 * time.Second,
|
||||||
|
IdleTimeout: 60 * time.Second,
|
||||||
|
MaxHeaderBytes: 1 << 16, // 64KB
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user