Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ws): add ws endpoint #1015

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions home.html
Original file line number Diff line number Diff line change
@@ -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 = "<b>Your browser does not support WebSockets.</b>";
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 @@
<body>
<div id="container">
<div class="logs">
<pre id="ws-log"></pre>
<pre id="log"></pre>
<pre id="log-list"></pre>
</div>
103 changes: 103 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -22,14 +22,17 @@
_ "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 @@
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 @@
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 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) {

Check failure on line 596 in main.go

GitHub Actions / check-style (./)

exported function ServeWS should have comment or be unexported

Check failure on line 596 in main.go

GitHub Actions / check-style (./)

exported function ServeWS should have comment or be unexported
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 {

Check failure on line 627 in main.go

GitHub Actions / check-style (./)

exported type Hub should have comment or be unexported

Check failure on line 627 in main.go

GitHub Actions / check-style (./)

exported type Hub should have comment or be unexported
// 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)
}
}
}