diff options
Diffstat (limited to 'src/rhctl')
-rw-r--r-- | src/rhctl/main.go | 8 | ||||
-rw-r--r-- | src/rhctl/web.go | 2 | ||||
-rw-r--r-- | src/rhctl/web_socket.go | 139 |
3 files changed, 145 insertions, 4 deletions
diff --git a/src/rhctl/main.go b/src/rhctl/main.go index b0b57e6..3ba8c45 100644 --- a/src/rhctl/main.go +++ b/src/rhctl/main.go @@ -95,10 +95,10 @@ func main() { } servers = append(servers, server) } - if len(servers) <= 0 { - rhl.Printf("Error: there is no playout server configured...") - return - } + // if len(servers) <= 0 { + // rhl.Printf("Error: there is no playout server configured...") + // return + // } ctrl := SwitchControlInit(conf, sw, servers) diff --git a/src/rhctl/web.go b/src/rhctl/web.go index 3c29c56..c83a28e 100644 --- a/src/rhctl/web.go +++ b/src/rhctl/web.go @@ -90,6 +90,8 @@ func WebInit(conf *Config, ctrl *SwitchControl) (web *WebInterface) { rhdl.Printf("Web: will serving static files from '%s'", conf.Clients.Web.StaticDir) http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(conf.Clients.Web.StaticDir)))) } + http.Handle("/socket", webHandler{ctrl, webSocketHandler}) + http.Handle("/api/state", webHandler{ctrl, webGetStateHandler}) // TODO: add other handler diff --git a/src/rhctl/web_socket.go b/src/rhctl/web_socket.go new file mode 100644 index 0000000..8d68dc3 --- /dev/null +++ b/src/rhctl/web_socket.go @@ -0,0 +1,139 @@ +// +// rhctl +// +// Copyright (C) 2009-2016 Christian Pointner <equinox@helsinki.at> +// +// This file is part of rhctl. +// +// rhctl is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// rhctl is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with rhctl. If not, see <http://www.gnu.org/licenses/>. +// + +package main + +import ( + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + + "github.com/gorilla/websocket" +) + +type webSocketRequestData struct { + Command string `json:"COMMAND"` +} + +type webSocketResponseBaseData struct { + ResponseCode int `json:"RESPONSE_CODE"` + Type string `json:"TYPE"` + ErrorString string `json:"ERROR_STRING"` +} + +type webSocketResponseStateData struct { + webSocketResponseBaseData + State State `json:"STATE"` +} + +func sendWebSocketResponse(ws *websocket.Conn, rd interface{}) { + if err := ws.WriteJSON(rd); err != nil { + rhdl.Println("Web(socket) client", ws.RemoteAddr(), "write error:", err) + } +} + +func sendWebSocketErrorResponse(ws *websocket.Conn, code int, errStr string) { + rd := &webSocketResponseBaseData{} + rd.ResponseCode = code + rd.Type = "error" + rd.ErrorString = errStr + sendWebSocketResponse(ws, rd) +} + +func sendWebSocketStateResponse(ws *websocket.Conn, state State) { + rd := &webSocketResponseStateData{} + rd.ResponseCode = http.StatusOK + rd.Type = "state" + rd.ErrorString = "OK" + sendWebSocketResponse(ws, rd) +} + +func webSocketSessionHandler(reqchan <-chan webSocketRequestData, ws *websocket.Conn, ctrl *SwitchControl) { + defer ws.Close() + + for { + select { + case reqdata, ok := <-reqchan: + if !ok { + return + } + switch reqdata.Command { + case "state": + resp := make(chan interface{}) + ctrl.Commands <- &Command{Type: CmdState, Response: resp} + result := <-resp + switch result.(type) { + case State: + sendWebSocketStateResponse(ws, result.(State)) + case error: + sendWebSocketErrorResponse(ws, http.StatusInternalServerError, result.(error).Error()) + default: + sendWebSocketErrorResponse(ws, http.StatusInternalServerError, fmt.Sprintf("invalid response of type %T: %+v", result, result)) + } + default: + sendWebSocketErrorResponse(ws, http.StatusBadRequest, fmt.Sprintf("unknown command '%s'", reqdata.Command)) + } + } + } +} + +func webSocketHandler(ctrl *SwitchControl, w http.ResponseWriter, r *http.Request) { + ws, err := websocket.Upgrade(w, r, nil, 64*1024, 64*1024) + if _, ok := err.(websocket.HandshakeError); ok { + http.Error(w, "Not a websocket handshake", 400) + return + } else if err != nil { + rhdl.Println("Web(socket) client", ws.RemoteAddr(), "error:", err) + return + } + rhdl.Println("Web(socket) client", ws.RemoteAddr(), "connected") + reqchan := make(chan webSocketRequestData) + go webSocketSessionHandler(reqchan, ws, ctrl) + defer close(reqchan) + + for { + t, r, err := ws.NextReader() + if err != nil { + rhdl.Println("Web(socket) Client", ws.RemoteAddr(), "disconnected:", err) + return + } + + switch t { + case websocket.TextMessage: + var reqdata webSocketRequestData + if err := json.NewDecoder(r).Decode(&reqdata); err != nil { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + rhdl.Println("Web(socket) client", ws.RemoteAddr(), "request error:", err) + sendWebSocketErrorResponse(ws, http.StatusBadRequest, err.Error()) + return + } + // rhdl.Printf("Web(socket) client %s got: %+v", ws.RemoteAddr(), reqdata) + reqchan <- reqdata + case websocket.BinaryMessage: + sendWebSocketErrorResponse(ws, http.StatusBadRequest, "binary messages are not allowed") + io.Copy(ioutil.Discard, r) // consume all the data + } + } +} |