gontrol/src/server/websocket/websocketServer.go

177 lines
4.5 KiB
Go
Raw Normal View History

package websocketserver
import (
"encoding/json"
"log"
"net/http"
"sync"
"time"
"github.com/gorilla/websocket"
)
var responseChannels sync.Map // Key: agentName, Value: chan string
type webSocketHandler struct {
upgrader websocket.Upgrader
}
var agentSockets = make(map[string]*websocket.Conn)
var agentSocketsMutex sync.Mutex
var getAgentNames http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
agentSocketsMutex.Lock()
agentNames := make([]string, 0, len(agentSockets))
for agentName := range agentSockets {
agentNames = append(agentNames, agentName)
}
agentSocketsMutex.Unlock()
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(agentNames)
}
func (wsh webSocketHandler) ServeHTTP(w http.ResponseWriter, r *http.Request){
c, err := wsh.upgrader.Upgrade(w, r, nil)
if err != nil {
log.Printf("Error %s when upgrading connection to websocket", err)
return
}
agentName := r.URL.Query().Get("agentName")
agentIP := r.URL.Query().Get("IPv4Address")
if agentName == "" || agentIP == "" {
log.Printf("Missing agentName or IPv4Address in query parameters")
c.Close()
return
}
log.Printf("Agent connected: %s (%s)", agentName, agentIP)
agentSocketsMutex.Lock()
agentSockets[agentName] = c
agentSocketsMutex.Unlock()
defer func() {
agentSocketsMutex.Lock()
delete(agentSockets, agentName)
agentSocketsMutex.Unlock()
c.Close()
log.Printf("Agent disconnected: %s (%s)", agentName, agentIP)
}()
for {
_, message, err := c.ReadMessage()
if err != nil {
log.Printf("Error reading from agent %s: %v", agentName, err)
break
}
log.Printf("Message from agent %s: %s", agentName, message)
if ch, ok := responseChannels.Load(agentName); ok {
responseChan := ch.(chan string)
responseChan <- string(message)
}
}
}
type Message struct {
Type string `json:"type"`
Payload string `json:"payload"`
}
var executeCommand http.HandlerFunc = func(w http.ResponseWriter, r *http.Request){
err := r.ParseForm()
if err != nil {
http.Error(w, "Invalid form data", http.StatusBadRequest)
return
}
agentName := r.FormValue("agentName")
command := r.FormValue("command")
agentSocketsMutex.Lock()
conn, ok := agentSockets[agentName]
agentSocketsMutex.Unlock()
if !ok {
http.Error(w, "Agent not connected", http.StatusNotFound)
return
}
responseChan := make(chan string, 1)
responseChannels.Store(agentName, responseChan)
defer responseChannels.Delete(agentName)
message := Message {
Type: "command",
Payload: command,
}
messageBytes, _ := json.Marshal(message)
err = conn.WriteMessage(websocket.TextMessage, messageBytes)
if err != nil {
http.Error(w, "Failed to send command to the agent", http.StatusInternalServerError)
return
}
select {
case response := <-responseChan:
var parsedResponse map[string]string
if err := json.Unmarshal([]byte(response), &parsedResponse); err != nil {
http.Error(w, "Failed to parse response", http.StatusInternalServerError)
return
}
payload, ok := parsedResponse["payload"]
if !ok {
http.Error(w, "Invalid response structure", http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "text/plain")
w.Write([]byte(payload))
case <- time.After(10 * time.Second):
http.Error(w, "Agent repsonse timed out", http.StatusGatewayTimeout)
}
}
func Server() (*http.Server) {
webSocketHandler := webSocketHandler {
upgrader: websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
},
}
2025-01-20 16:32:13 +01:00
corsMiddleware := func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*") // Allow the WebUI origin
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, HX-Current-URL, HX-Request, HX-Target, HX-Trigger, HX-Trigger-Name")
if r.Method == "OPTIONS" {
// Handle preflight requests
w.WriteHeader(http.StatusOK)
return
}
next.ServeHTTP(w, r)
})
}
webSocketMux := http.NewServeMux()
webSocketMux.Handle("/data", webSocketHandler)
2025-01-20 16:32:13 +01:00
webSocketMux.Handle("/executeCommand", corsMiddleware(http.HandlerFunc(executeCommand)))
webSocketMux.Handle("/agentNames", corsMiddleware(http.HandlerFunc(getAgentNames)))
websocketServer := &http.Server{
Addr: ":5555",
Handler: webSocketMux,
}
return websocketServer
}