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
Show file tree
Hide file tree
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
Expand Up @@ -25,6 +25,8 @@

const socket = setupWebsocket();

const ws = newWebsocket();

// Handle the form submission and send the message to the websocket.
document
.getElementById("form")
Expand All @@ -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);
Expand Down Expand Up @@ -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>
Expand Down
103 changes: 103 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
)
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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

View workflow job for this annotation

GitHub Actions / check-style (./)

exported function ServeWS should have comment or be unexported

Check failure on line 596 in main.go

View workflow job for this annotation

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

View workflow job for this annotation

GitHub Actions / check-style (./)

exported type Hub should have comment or be unexported

Check failure on line 627 in main.go

View workflow job for this annotation

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)
}
}
}
Loading