- Agent: neuer 'backup' Command (SHA256 Hash, Base64 Transfer) - Backend: config_backups Tabelle in SQLite - API: trigger, list, download (JSON/XML), delete, diff - Deduplizierung: gleicher Hash = kein neuer Eintrag - WebSocket ReadLimit auf 4MB erhoeht (grosse config.xml) - README aktualisiert mit Backup-Docs
157 lines
3.7 KiB
Go
157 lines
3.7 KiB
Go
package ws
|
|
|
|
import (
|
|
"errors"
|
|
"log"
|
|
"net/http"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/gorilla/websocket"
|
|
)
|
|
|
|
var (
|
|
ErrAgentNotConnected = errors.New("Agent nicht verbunden")
|
|
ErrAgentSendBlocked = errors.New("Agent Send-Channel blockiert")
|
|
)
|
|
|
|
type Connection struct {
|
|
AgentID string
|
|
Conn *websocket.Conn
|
|
Send chan []byte
|
|
BinarySend chan []byte
|
|
Hub *Hub
|
|
closeOnce sync.Once
|
|
}
|
|
|
|
var upgrader = websocket.Upgrader{
|
|
ReadBufferSize: 1024,
|
|
WriteBufferSize: 1024,
|
|
CheckOrigin: func(r *http.Request) bool {
|
|
// Hier könnte Origin-Checking implementiert werden
|
|
return true
|
|
},
|
|
}
|
|
|
|
func NewWebSocketHandler(hub *Hub) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
handleWebSocket(hub, w, r)
|
|
}
|
|
}
|
|
|
|
func handleWebSocket(hub *Hub, w http.ResponseWriter, r *http.Request) {
|
|
// Query-Parameter extrahieren
|
|
agentID := r.URL.Query().Get("agent_id")
|
|
apiKey := r.URL.Query().Get("api_key")
|
|
|
|
if agentID == "" || apiKey == "" {
|
|
http.Error(w, "agent_id und api_key erforderlich", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
// API-Key validieren (sollte bereits durch Middleware geschehen)
|
|
// Hier könnten zusätzliche Agent-spezifische Prüfungen stattfinden
|
|
|
|
// WebSocket-Upgrade
|
|
conn, err := upgrader.Upgrade(w, r, nil)
|
|
if err != nil {
|
|
log.Printf("WebSocket-Upgrade fehlgeschlagen: %v", err)
|
|
return
|
|
}
|
|
|
|
// Connection-Objekt erstellen
|
|
connection := &Connection{
|
|
AgentID: agentID,
|
|
Conn: conn,
|
|
Send: make(chan []byte, 256),
|
|
BinarySend: make(chan []byte, 256),
|
|
Hub: hub,
|
|
}
|
|
|
|
// Verbindung bei Hub registrieren
|
|
hub.RegisterAgent(agentID, connection)
|
|
|
|
// Goroutines für Read/Write starten
|
|
go connection.writePump()
|
|
go connection.readPump()
|
|
}
|
|
|
|
func (c *Connection) readPump() {
|
|
defer func() {
|
|
c.Hub.UnregisterAgent(c.AgentID, c)
|
|
}()
|
|
|
|
// Read-Timeouts setzen
|
|
c.Conn.SetReadLimit(4 * 1024 * 1024) // 4MB max message size (fuer config.xml Backups)
|
|
c.Conn.SetReadDeadline(time.Now().Add(60 * time.Second))
|
|
c.Conn.SetPongHandler(func(string) error {
|
|
c.Conn.SetReadDeadline(time.Now().Add(60 * time.Second))
|
|
return nil
|
|
})
|
|
|
|
for {
|
|
messageType, message, err := c.Conn.ReadMessage()
|
|
if err != nil {
|
|
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
|
|
log.Printf("Agent %s: WebSocket-Fehler: %v", c.AgentID, err)
|
|
}
|
|
break
|
|
}
|
|
|
|
// Message an Hub weiterleiten
|
|
if err := c.Hub.ProcessAgentMessage(c.AgentID, messageType, message); err != nil {
|
|
log.Printf("Agent %s: Message-Verarbeitung fehlgeschlagen: %v", c.AgentID, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *Connection) writePump() {
|
|
ticker := time.NewTicker(54 * time.Second) // Ping alle 54s
|
|
defer func() {
|
|
ticker.Stop()
|
|
c.Close()
|
|
}()
|
|
|
|
for {
|
|
select {
|
|
case message, ok := <-c.Send:
|
|
c.Conn.SetWriteDeadline(time.Now().Add(10 * time.Second))
|
|
if !ok {
|
|
c.Conn.WriteMessage(websocket.CloseMessage, nil)
|
|
return
|
|
}
|
|
|
|
if err := c.Conn.WriteMessage(websocket.TextMessage, message); err != nil {
|
|
log.Printf("Agent %s: Write-Fehler: %v", c.AgentID, err)
|
|
return
|
|
}
|
|
|
|
case binaryMessage, ok := <-c.BinarySend:
|
|
c.Conn.SetWriteDeadline(time.Now().Add(10 * time.Second))
|
|
if !ok {
|
|
c.Conn.WriteMessage(websocket.CloseMessage, nil)
|
|
return
|
|
}
|
|
|
|
if err := c.Conn.WriteMessage(websocket.BinaryMessage, binaryMessage); err != nil {
|
|
log.Printf("Agent %s: Binary-Write-Fehler: %v", c.AgentID, err)
|
|
return
|
|
}
|
|
|
|
case <-ticker.C:
|
|
c.Conn.SetWriteDeadline(time.Now().Add(10 * time.Second))
|
|
if err := c.Conn.WriteMessage(websocket.PingMessage, nil); err != nil {
|
|
log.Printf("Agent %s: Ping-Fehler: %v", c.AgentID, err)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *Connection) Close() {
|
|
c.closeOnce.Do(func() {
|
|
close(c.Send)
|
|
close(c.BinarySend)
|
|
c.Conn.Close()
|
|
})
|
|
} |