diff --git a/home.html b/home.html index ee523981..67a9b41a 100644 --- a/home.html +++ b/home.html @@ -25,6 +25,8 @@ const socket = setupWebsocket(); + const ws = newWebsocket(); + // Handle the form submission and send the message to the websocket. document .getElementById("form") @@ -39,10 +41,35 @@ return false; } socket.emit("command", input.value); + // emit to ws + ws.send(input.value); input.value = ""; + + }); }); + function newWebsocket() { + var conn; + if (window["WebSocket"]) { + console.log("Connecting to websocket", "ws://" + document.location.host + "/ws"); + conn = new WebSocket("ws://127.0.0.1:9001/ws"); + conn.onclose = function (evt) { + console.log("Closing: " + evt.data); + }; + conn.onmessage = function (evt) { + console.log("Received: " + evt.data); + const wsLog = document.getElementById("ws-log"); + wsLog.textContent = evt.data; + }; + } else { + var item = document.createElement("ws-log"); + item.innerHTML = "Your browser does not support WebSockets."; + appendLog(item); + } + return conn; + }; + function getListMsgVisibility() { // Check if the list visibility setting is saved in the localStorage. let savedSetting = localStorage.getItem(LOCAL_STORAGE_KEY); @@ -309,6 +336,7 @@
+

         

         

       
diff --git a/main.go b/main.go index 1ca857b0..88a8625f 100755 --- a/main.go +++ b/main.go @@ -22,14 +22,17 @@ import ( _ "embed" "encoding/json" "flag" + "fmt" "html/template" "io" + "net/http" "os" "regexp" "runtime" "runtime/debug" "strconv" "strings" + "sync" "time" cert "github.com/arduino/arduino-create-agent/certificates" @@ -45,6 +48,7 @@ import ( cors "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" "github.com/go-ini/ini" + "github.com/gorilla/websocket" log "github.com/sirupsen/logrus" //"github.com/sanbornm/go-selfupdate/selfupdate" #included in update.go to change heavily ) @@ -463,6 +467,16 @@ func loop() { r.POST("/pause", pauseHandler) r.POST("/update", updateHandler) + // TODO: temporary using a different port for the websocket server + hub := newHub() + go func() { + http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) { + ServeWS(hub, w, r) + }) + fmt.Println("Starting server and websocket on " + *address + ":9001") + log.Fatal(http.ListenAndServe(*address+":9001", nil)) + }() + // Mount goa handlers goa := v2.Server(config.GetDataDir().String(), Index) r.Any("/v2/*path", gin.WrapH(goa)) @@ -557,3 +571,92 @@ func installCertsKeyExists(filename string) (bool, error) { func promptInstallCertsSafari() bool { return utilities.UserPrompt("The Arduino Agent needs a local HTTPS certificate to work correctly with Safari.\nIf you use Safari, you need to install it.", "{\"Do not install\", \"Install the certificate for Safari\"}", "Install the certificate for Safari", "Install the certificate for Safari", "Arduino Agent: Install certificate") } + +var upgrader = websocket.Upgrader{ + CheckOrigin: func(r *http.Request) bool { + // TODO: check origin with the list of allowed origins + return true + }, +} + +const ( + // Time allowed to write a message to the peer. + writeWait = 10 * time.Second + + // Time allowed to read the next pong message from the peer. + pongWait = 60 * time.Second + + // Send pings to peer with this period. Must be less than pongWait. + pingPeriod = (pongWait * 9) / 10 + + // Maximum message size allowed from peer. + maxMessageSize = 512 +) + +func ServeWS(hub *Hub, w http.ResponseWriter, r *http.Request) { + conn, err := upgrader.Upgrade(w, r, nil) + if err != nil { + log.Error("upgrade:", err) + return + } + defer hub.unregister(conn) + + hub.register(conn) + + read(hub, conn) +} + +func read(hub *Hub, conn *websocket.Conn) { + + conn.SetReadLimit(maxMessageSize) + conn.SetReadDeadline(time.Now().Add(pongWait)) + conn.SetPongHandler(func(string) error { conn.SetReadDeadline(time.Now().Add(pongWait)); return nil }) + for { + _, message, err := conn.ReadMessage() + if err != nil { + if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) { + log.Printf("error: %v", err) + } + break + } + log.Info("Received message from client: " + string(message)) + hub.broadcast(message) + } +} + +type Hub struct { + // Registered clients. + clients map[*websocket.Conn]bool + mu sync.Mutex +} + +func newHub() *Hub { + return &Hub{ + clients: make(map[*websocket.Conn]bool), + } +} + +func (h *Hub) register(conn *websocket.Conn) { + defer h.mu.Unlock() + h.mu.Lock() + h.clients[conn] = true + conn.WriteMessage(websocket.TextMessage, []byte("Hello, client!")) +} + +func (h *Hub) unregister(conn *websocket.Conn) { + defer h.mu.Unlock() + h.mu.Lock() + delete(h.clients, conn) + conn.Close() +} + +func (h *Hub) broadcast(message []byte) { + for conn := range h.clients { + log.Info("Broadcasting message to client" + conn.RemoteAddr().String()) + err := conn.WriteMessage(websocket.TextMessage, message) + if err != nil { + // TODO: handle error + log.Println("write:", err) + } + } +}