basic htmx with server sending content to the client over a websocket is now working. This only worked when text message where being sent so the websocket handling had to be made configurable with a 'text' boolean field.

This commit is contained in:
Erik Brakkee 2024-07-29 23:56:44 +02:00
parent 77cffde408
commit 39cf088a41
13 changed files with 106 additions and 20 deletions

View File

@ -258,7 +258,7 @@ func main() {
log.Println("WebSocket connection error:", err)
return
}
wsConn := websocketutil.NewWebSocketConn(conn)
wsConn := websocketutil.NewWebSocketConn(conn, false)
defer wsConn.Close()
serverInfo, err := comms.AgentInitialization(wsConn, comms.NewAgentInfo())

View File

@ -9,12 +9,11 @@ import (
"math/rand"
"net"
"net/http"
_ "net/http/pprof"
"os"
"regexp"
"strconv"
"strings"
_ "net/http/pprof"
)
func parsePublicId(path string) (publicId string, _ error) {
@ -97,6 +96,8 @@ func main() {
log.Printf("Using username '%s' and password '%s'", userPassword.Username, userPassword.Password)
admin := converge.NewAdmin()
// For agents connecting
registrationService := websocketutil.WebSocketService{
Handler: func(w http.ResponseWriter, r *http.Request, conn net.Conn) {
publicId, err := parsePublicId(r.URL.Path)
@ -112,6 +113,8 @@ func main() {
}
},
}
// For users connecting with ssh
clientService := websocketutil.WebSocketService{
Handler: func(w http.ResponseWriter, r *http.Request, conn net.Conn) {
publicId, err := parsePublicId(r.URL.Path)
@ -127,9 +130,16 @@ func main() {
},
}
// for the web browser getting live status updates.
sessionService := websocketutil.WebSocketService{
Handler: sessionHandler,
Text: true,
}
// websocket endpoints
http.HandleFunc("/agent/", registrationService.Handle)
http.HandleFunc("/client/", clientService.Handle)
http.HandleFunc("/ws/sessions", sessionService.Handle)
// create filehandler with templating for html files.
http.Handle("/docs/", http.StripPrefix("/docs/", http.HandlerFunc(pageHandler)))

View File

@ -32,7 +32,7 @@ func pageHandler(w http.ResponseWriter, r *http.Request) {
case "index.html":
templates.AboutTab().Render(r.Context(), w)
case "usage.html":
templates.UsageTab(secure, r.URL.Host, username).Render(r.Context(), w)
templates.UsageTab(secure, r.Host, username).Render(r.Context(), w)
case "downloads.html":
templates.DownloadsTab().Render(r.Context(), w)
case "sessions.html":

View File

@ -0,0 +1,55 @@
package main
import (
"encoding/json"
"log"
"net"
"net/http"
"strconv"
"time"
)
type Message struct {
Type string `json:"type"`
Content string `json:"content"`
}
func sessionHandler(w http.ResponseWriter, r *http.Request, conn net.Conn) {
log.Println("Got sessions websocket connection")
i := 0
for {
time.Sleep(1 * time.Second)
message := Message{
Type: "update",
Content: `
<div id="mycontent">
New data: ` + strconv.Itoa(i) + `
</div>
`,
}
_, err := json.Marshal(message)
if err != nil {
log.Printf("ERROR marshalling json: %v", err)
return
}
//conn.Write(data)
go func() {
for {
b := make([]byte, 1024)
_, err := conn.Read(b)
if err != nil {
return
}
}
}()
_, err = conn.Write([]byte(message.Content))
if err == nil {
_, err = conn.Write([]byte("\n"))
}
if err != nil {
log.Printf("ERROR sending message: %v", err)
return
}
i++
}
}

View File

@ -36,7 +36,7 @@ func handleConnection(conn net.Conn, wsURL string, insecure bool) {
log.Println("WebSocket connection error:", err)
return
}
wsConn := websocketutil.NewWebSocketConn(_wsConn)
wsConn := websocketutil.NewWebSocketConn(_wsConn, false)
defer wsConn.Close()
iowrappers.SynchronizeStreams(wsConn, conn)

View File

@ -70,7 +70,7 @@ func main() {
log.Println("WebSocket connection error:", err)
panic(err)
}
wsConn := websocketutil.NewWebSocketConn(_wsConn)
wsConn := websocketutil.NewWebSocketConn(_wsConn, false)
defer wsConn.Close()
iowrappers.SynchronizeStreams(wsConn, Stdio{})

View File

@ -35,7 +35,7 @@ func main() {
func handleWebSocket(w http.ResponseWriter, r *http.Request, tcpConn net.Conn) {
conn, err := upgrader.Upgrade(w, r, nil)
wsConn := websocketutil.NewWebSocketConn(conn)
wsConn := websocketutil.NewWebSocketConn(conn, false)
if err != nil {
log.Println("Error upgrading to WebSocket:", err)
return

View File

@ -7,3 +7,6 @@ services:
context: .
ports:
- 8000:8000
environment:
CONVERGE_USERNAME: abc
CONVERGE_PASSWORD: "123"

View File

@ -17,9 +17,17 @@ templ BasePage(tab int) {
<link rel="stylesheet" href="../static/css/bootstrap.min.css"
crossorigin="anonymous">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<script src="../static/js/htmx.1.9.12.js"></script>
<script src="../static/js/htmx.ws.1.9.12.js"></script>
<title>Converge</title>
</head>
<body>
<script>
htmx.logAll();
</script>
<script src="../static/js/bootstrap.bundle.min.js"
crossorigin="anonymous"></script>

View File

@ -2,9 +2,12 @@ package templates
templ Sessions() {
<div>
To be done
<div hx-ext="ws" ws-connect="ws://localhost:8000/ws/sessions">
<div id="mycontent">
Initial content
</div>
</div>
}

View File

@ -12,12 +12,12 @@ templ Usage(secure string, host string, username string) {
</p>
<pre>{`
# linux
`}curl http{secure}://{host}/docs/agent > agent{`
`}curl http{secure}://{host}/static/agent > agent{`
chmod 755 agent
`}./agent --id ID ws{secure}://{host}{`
# windows
`}curl http{secure}://{host}/docs/agent.exe > agent.exe{`
`}curl http{secure}://{host}/static/agent.exe > agent.exe{`
`}agent --id ID ws{secure}://{host}{`
`}</pre>
<p>

View File

@ -9,6 +9,11 @@ import (
type WebSocketConn struct {
conn *websocket.Conn
buf []byte
text bool
}
func NewWebSocketConn(conn *websocket.Conn, text bool) *WebSocketConn {
return &WebSocketConn{conn: conn, text: text}
}
func (websocketConn *WebSocketConn) Read(p []byte) (n int, err error) {
@ -26,12 +31,12 @@ func (websocketConn *WebSocketConn) Read(p []byte) (n int, err error) {
return n, err
}
func NewWebSocketConn(conn *websocket.Conn) *WebSocketConn {
return &WebSocketConn{conn: conn}
}
func (websocketConn *WebSocketConn) Write(p []byte) (n int, err error) {
err = websocketConn.conn.WriteMessage(websocket.BinaryMessage, p)
messageType := websocket.BinaryMessage
if websocketConn.text {
messageType = websocket.TextMessage
}
err = websocketConn.conn.WriteMessage(messageType, p)
if err == nil {
n = len(p)
}
@ -73,5 +78,5 @@ func ConnectWebSocket(conn net.Conn, urlStr string) (net.Conn, error) {
return nil, err
}
return NewWebSocketConn(wsConn), nil
return NewWebSocketConn(wsConn, false), nil
}

View File

@ -22,13 +22,14 @@ var upgrader = websocket.Upgrader{
}
func handleWebSocket(w http.ResponseWriter, r *http.Request,
handler func(w http.ResponseWriter, r *http.Request, websockerConnection net.Conn)) {
handler func(w http.ResponseWriter, r *http.Request, websockerConnection net.Conn),
text bool) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println("Error upgrading to WebSocket:", err)
return
}
wsConn := NewWebSocketConn(conn)
wsConn := NewWebSocketConn(conn, text)
defer wsConn.Close()
handler(w, r, wsConn)
@ -36,8 +37,9 @@ func handleWebSocket(w http.ResponseWriter, r *http.Request,
type WebSocketService struct {
Handler func(w http.ResponseWriter, r *http.Request, conn net.Conn)
Text bool
}
func (endpoint *WebSocketService) Handle(w http.ResponseWriter, r *http.Request) {
handleWebSocket(w, r, endpoint.Handler)
handleWebSocket(w, r, endpoint.Handler, endpoint.Text)
}