package metrics import ( "encoding/json" "fmt" "net/http" "github.com/google/uuid" "github.com/cloudflare/cloudflared/tunnelstate" ) // ReadyServer serves HTTP 200 if the tunnel can serve traffic. Intended for k8s readiness checks. type ReadyServer struct { clientID uuid.UUID tracker *tunnelstate.ConnTracker } // NewReadyServer initializes a ReadyServer and starts listening for dis/connection events. func NewReadyServer( clientID uuid.UUID, tracker *tunnelstate.ConnTracker, ) *ReadyServer { return &ReadyServer{ clientID, tracker, } } type body struct { Status int `json:"status"` ReadyConnections uint `json:"readyConnections"` ConnectorID uuid.UUID `json:"connectorId"` } // ServeHTTP responds with HTTP 200 if the tunnel is connected to the edge. func (rs *ReadyServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { statusCode, readyConnections := rs.makeResponse() w.WriteHeader(statusCode) body := body{ Status: statusCode, ReadyConnections: readyConnections, ConnectorID: rs.clientID, } msg, err := json.Marshal(body) if err != nil { _, _ = fmt.Fprintf(w, `{"error": "%s"}`, err) } _, _ = w.Write(msg) } // This is the bulk of the logic for ServeHTTP, broken into its own pure function // to make unit testing easy. func (rs *ReadyServer) makeResponse() (statusCode int, readyConnections uint) { readyConnections = rs.tracker.CountActiveConns() if readyConnections > 0 { return http.StatusOK, readyConnections } else { return http.StatusServiceUnavailable, readyConnections } }