htmgo/extensions/websocket/internal/wsutil/handler.go

117 lines
2.3 KiB
Go
Raw Normal View History

2024-10-16 20:23:36 +00:00
package wsutil
2024-10-08 17:48:28 +00:00
import (
"encoding/json"
"fmt"
"github.com/gobwas/ws"
"github.com/gobwas/ws/wsutil"
2024-10-16 20:43:34 +00:00
ws2 "github.com/maddalax/htmgo/extensions/websocket/opts"
2024-10-08 17:48:28 +00:00
"github.com/maddalax/htmgo/framework/h"
"github.com/maddalax/htmgo/framework/service"
"log/slog"
"net/http"
"sync"
"time"
)
2024-10-16 20:43:34 +00:00
func WsHttpHandler(opts *ws2.ExtensionOpts) http.HandlerFunc {
2024-11-04 16:37:41 +00:00
if opts.RoomName == nil {
opts.RoomName = func(ctx *h.RequestContext) string {
return "all"
}
}
2024-10-08 17:48:28 +00:00
return func(w http.ResponseWriter, r *http.Request) {
cc := r.Context().Value(h.RequestContextKey).(*h.RequestContext)
locator := cc.ServiceLocator()
manager := service.Get[SocketManager](locator)
2024-10-16 20:43:34 +00:00
sessionId := opts.SessionId(cc)
2024-10-08 17:48:28 +00:00
if sessionId == "" {
w.WriteHeader(http.StatusUnauthorized)
return
}
conn, _, _, err := ws.UpgradeHTTP(r, w)
if err != nil {
slog.Info("failed to upgrade", slog.String("error", err.Error()))
return
}
2024-11-04 16:37:41 +00:00
roomId := opts.RoomName(cc)
2024-10-08 17:48:28 +00:00
/*
Large buffer in case the client disconnects while we are writing
we don't want to block the writer
*/
done := make(chan bool, 1000)
writer := make(WriterChan, 1000)
wg := sync.WaitGroup{}
2024-11-04 16:37:41 +00:00
manager.Add(roomId, sessionId, writer, done)
2024-10-08 17:48:28 +00:00
/*
* This goroutine is responsible for writing messages to the client
*/
wg.Add(1)
go func() {
defer manager.Disconnect(sessionId)
defer wg.Done()
defer func() {
fmt.Printf("empting channels\n")
for len(writer) > 0 {
<-writer
}
for len(done) > 0 {
<-done
}
}()
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
for {
select {
case <-done:
fmt.Printf("closing connection: \n")
return
case <-ticker.C:
manager.Ping(sessionId)
case message := <-writer:
err = wsutil.WriteServerMessage(conn, ws.OpText, []byte(message))
if err != nil {
return
}
}
}
}()
/*
* This goroutine is responsible for reading messages from the client
*/
go func() {
defer conn.Close()
for {
msg, op, err := wsutil.ReadClientData(conn)
if err != nil {
return
}
if op != ws.OpText {
return
}
m := make(map[string]any)
err = json.Unmarshal(msg, &m)
if err != nil {
return
}
manager.OnMessage(sessionId, m)
}
}()
wg.Wait()
}
}