diff --git a/socketserver/internal/server/backend.go b/socketserver/internal/server/backend.go index c1241752..7d782a29 100644 --- a/socketserver/internal/server/backend.go +++ b/socketserver/internal/server/backend.go @@ -64,7 +64,7 @@ func getCacheKey(remoteCommand, data string) string { // The backend can POST here to publish a message to clients with no caching. // The POST arguments are `cmd`, `args`, `channel`, and `scope`. // The `scope` argument is required because no attempt is made to infer the scope from the command, unlike /cached_pub. -func HBackendPublishRequest(w http.ResponseWriter, r *http.Request) { +func HTTPBackendUncachedPublish(w http.ResponseWriter, r *http.Request) { r.ParseForm() formData, err := UnsealRequest(r.Form) if err != nil { diff --git a/socketserver/internal/server/handlecore.go b/socketserver/internal/server/handlecore.go index b296f490..4548f682 100644 --- a/socketserver/internal/server/handlecore.go +++ b/socketserver/internal/server/handlecore.go @@ -60,9 +60,11 @@ func SetupServerAndHandle(config *ConfigFile, serveMux *http.ServeMux) { BannerHTML = bannerBytes serveMux.HandleFunc("/", HTTPHandleRootURL) + serveMux.HandleFunc("/stats", HTTPShowStatistics) + serveMux.HandleFunc("/drop_backlog", HTTPBackendDropBacklog) - serveMux.HandleFunc("/uncached_pub", HBackendPublishRequest) - serveMux.HandleFunc("/cached_pub", HBackendUpdateAndPublish) + serveMux.HandleFunc("/uncached_pub", HTTPBackendUncachedPublish) + serveMux.HandleFunc("/cached_pub", HTTPBackendCachedPublish) announceForm, err := SealRequest(url.Values{ "startup": []string{"1"}, @@ -118,25 +120,34 @@ func HTTPHandleRootURL(w http.ResponseWriter, r *http.Request) { // ErrProtocolGeneric is sent in a ErrorCommand Reply. var ErrProtocolGeneric error = errors.New("FFZ Socket protocol error.") + // ErrProtocolNegativeMsgID is sent in a ErrorCommand Reply when a negative MessageID is received. var ErrProtocolNegativeMsgID error = errors.New("FFZ Socket protocol error: negative or zero message ID.") + // ErrExpectedSingleString is sent in a ErrorCommand Reply when the Arguments are of the wrong type. var ErrExpectedSingleString = errors.New("Error: Expected single string as arguments.") + // ErrExpectedSingleInt is sent in a ErrorCommand Reply when the Arguments are of the wrong type. var ErrExpectedSingleInt = errors.New("Error: Expected single integer as arguments.") + // ErrExpectedTwoStrings is sent in a ErrorCommand Reply when the Arguments are of the wrong type. var ErrExpectedTwoStrings = errors.New("Error: Expected array of string, string as arguments.") + // ErrExpectedStringAndBool is sent in a ErrorCommand Reply when the Arguments are of the wrong type. var ErrExpectedStringAndBool = errors.New("Error: Expected array of string, bool as arguments.") + // ErrExpectedStringAndInt is sent in a ErrorCommand Reply when the Arguments are of the wrong type. var ErrExpectedStringAndInt = errors.New("Error: Expected array of string, int as arguments.") + // ErrExpectedStringAndIntGotFloat is sent in a ErrorCommand Reply when the Arguments are of the wrong type. var ErrExpectedStringAndIntGotFloat = errors.New("Error: Second argument was a float, expected an integer.") // CloseGotBinaryMessage is the termination reason when the client sends a binary websocket frame. var CloseGotBinaryMessage = websocket.CloseError{Code: websocket.CloseUnsupportedData, Text: "got binary packet"} + // CloseTimedOut is the termination reason when the client fails to send or respond to ping frames. var CloseTimedOut = websocket.CloseError{Code: websocket.CloseNoStatusReceived, Text: "no ping replies for 5 minutes"} + // CloseFirstMessageNotHello is the termination reason var CloseFirstMessageNotHello = websocket.CloseError{ Text: "Error - the first message sent must be a 'hello'", @@ -305,7 +316,7 @@ func getDeadline() time.Time { } func CloseConnection(conn *websocket.Conn, closeMsg *websocket.CloseError) { - Statistics.DisconnectCodes[closeMsg.Code]++ + Statistics.DisconnectCodes[strconv.Itoa(closeMsg.Code)]++ Statistics.DisconnectReasons[closeMsg.Text]++ conn.WriteControl(websocket.CloseMessage, websocket.FormatCloseMessage(closeMsg.Code, closeMsg.Text), getDeadline()) diff --git a/socketserver/internal/server/publisher.go b/socketserver/internal/server/publisher.go index d93618cf..9f9be8ff 100644 --- a/socketserver/internal/server/publisher.go +++ b/socketserver/internal/server/publisher.go @@ -351,7 +351,7 @@ func HTTPBackendDropBacklog(w http.ResponseWriter, r *http.Request) { // Publish a message to clients, and update the in-server cache for the message. // notes: // `scope` is implicit in the command -func HBackendUpdateAndPublish(w http.ResponseWriter, r *http.Request) { +func HTTPBackendCachedPublish(w http.ResponseWriter, r *http.Request) { r.ParseForm() formData, err := UnsealRequest(r.Form) if err != nil { diff --git a/socketserver/internal/server/stats.go b/socketserver/internal/server/stats.go index a917b160..9630171b 100644 --- a/socketserver/internal/server/stats.go +++ b/socketserver/internal/server/stats.go @@ -1,26 +1,48 @@ package server +import ( + "bytes" + "encoding/json" + "net/http" +) + type StatsData struct { - ClientConnectsTotal int64 - ClientDisconnectsTotal int64 + ClientConnectsTotal int64 + ClientDisconnectsTotal int64 FirstNotHelloDisconnects int64 - DisconnectCodes map[int]int64 + DisconnectCodes map[string]int64 DisconnectReasons map[string]int64 CommandsIssuedTotal int64 - CommandsIssuedMap map[Command]int64 + CommandsIssuedMap map[Command]int64 MessagesSent int64 + + Version int } +const StatsDataVersion = 1 + func newStatsData() *StatsData { return &StatsData{ CommandsIssuedMap: make(map[Command]int64), - DisconnectCodes: make(map[int]int64), + DisconnectCodes: make(map[string]int64), DisconnectReasons: make(map[string]int64), + Version: StatsDataVersion, } } // Statistics is several variables that get incremented during normal operation of the server. +// Its structure should be versioned as it is exposed via JSON. var Statistics = newStatsData() + +func HTTPShowStatistics(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + + jsonBytes, _ := json.Marshal(Statistics) + outBuf := bytes.NewBuffer(nil) + json.Indent(outBuf, jsonBytes, "", "\t") + + outBuf.WriteTo(w) +}