Expose /metrics with counters for routed requests, routing errors, upstream outcomes, and upstream latency histograms. Instrument the router and proxy forwarding path and document the new metrics.
87 lines
2.2 KiB
Go
87 lines
2.2 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
)
|
|
|
|
func runServer() {
|
|
cfg, err := LoadConfig()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
metrics := NewMetrics(nil)
|
|
proxy := NewProxy(cfg, metrics)
|
|
mux := http.NewServeMux()
|
|
|
|
mux.HandleFunc("GET /", func(w http.ResponseWriter, _ *http.Request) {
|
|
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
|
_, _ = w.Write([]byte("Mattermost Push Proxy Router\n"))
|
|
})
|
|
|
|
mux.HandleFunc("GET /version", func(w http.ResponseWriter, _ *http.Request) {
|
|
writeJSON(w, map[string]string{"version": version})
|
|
})
|
|
|
|
mux.HandleFunc("GET /health", handleHealth)
|
|
mux.Handle("GET /metrics", metrics.Handler())
|
|
|
|
mux.HandleFunc("POST /api/v1/send_push", func(w http.ResponseWriter, r *http.Request) {
|
|
handleRoute(w, r, proxy, cfg, "/api/v1/send_push")
|
|
})
|
|
|
|
mux.HandleFunc("POST /api/v1/ack", func(w http.ResponseWriter, r *http.Request) {
|
|
handleRoute(w, r, proxy, cfg, "/api/v1/ack")
|
|
})
|
|
|
|
addr := ":" + cfg.Port
|
|
log.Printf("Push proxy router listening on %s", addr)
|
|
for _, route := range cfg.Routes {
|
|
log.Printf("Route %v -> %s", route.Prefixes, route.URL)
|
|
}
|
|
|
|
if err := http.ListenAndServe(addr, mux); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func handleRoute(w http.ResponseWriter, r *http.Request, proxy *Proxy, cfg Config, path string) {
|
|
body, err := io.ReadAll(r.Body)
|
|
if err != nil {
|
|
proxy.metrics.incRoutingError("read_body")
|
|
writePushError(w, "failed to read request body")
|
|
return
|
|
}
|
|
|
|
platform, err := extractPlatform(body)
|
|
if err != nil {
|
|
proxy.metrics.incRoutingError(routingErrorReason(err))
|
|
writePushError(w, err.Error())
|
|
return
|
|
}
|
|
|
|
backend, ok := cfg.BackendForPlatform(platform)
|
|
if !ok {
|
|
proxy.metrics.incRoutingError("unknown_platform")
|
|
writePushError(w, "no backend configured for platform="+platform)
|
|
return
|
|
}
|
|
|
|
proxy.metrics.incRequest(routePathLabel(path), platform)
|
|
log.Printf("route platform=%s path=%s backend=%s", platform, path, backend)
|
|
proxy.Forward(w, r, backend, path, body, platform)
|
|
}
|
|
|
|
func handleHealth(w http.ResponseWriter, _ *http.Request) {
|
|
writeJSON(w, map[string]string{"status": "ok"})
|
|
}
|
|
|
|
func writeJSON(w http.ResponseWriter, v any) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
enc := json.NewEncoder(w)
|
|
enc.SetEscapeHTML(false)
|
|
_ = enc.Encode(v)
|
|
}
|