From dab96a3980f9dbef58a5f129be7f95a5a5588ce8 Mon Sep 17 00:00:00 2001 From: maddalax Date: Wed, 16 Oct 2024 15:43:34 -0500 Subject: [PATCH] refactor --- examples/sse-with-state/go.mod | 5 ++-- examples/sse-with-state/go.sum | 6 +++++ examples/sse-with-state/main.go | 8 +++++-- examples/sse-with-state/pages/index.go | 6 ++--- examples/sse-with-state/pages/root.go | 4 ++-- examples/sse-with-state/partials/index.go | 8 +++---- extensions/websocket/init.go | 20 ++++++++++------ .../websocket/internal/wsutil/handler.go | 5 ++-- .../websocket/internal/wsutil/manager.go | 5 +++- extensions/websocket/opts/opts.go | 8 +++++++ extensions/websocket/ws/handler.go | 6 ++--- extensions/websocket/ws/listener.go | 4 ++-- extensions/websocket/ws/register.go | 18 +++++++-------- .../state => framework/session}/state.go | 23 +++++++++---------- 14 files changed, 77 insertions(+), 49 deletions(-) create mode 100644 extensions/websocket/opts/opts.go rename {extensions/websocket/state => framework/session}/state.go (64%) diff --git a/examples/sse-with-state/go.mod b/examples/sse-with-state/go.mod index 97ae5e4..6c9506c 100644 --- a/examples/sse-with-state/go.mod +++ b/examples/sse-with-state/go.mod @@ -2,8 +2,7 @@ module sse-with-state go 1.23.0 -require github.com/maddalax/htmgo/framework v0.0.0-20241006162137-150c87b4560b -require github.com/maddalax/htmgo/extensions/ws v0.0.0-20241006162137-150c87b4560b +require github.com/maddalax/htmgo/framework v0.0.0-20241014151703-8503dffa4e7d require ( github.com/go-chi/chi/v5 v5.1.0 // indirect @@ -11,5 +10,7 @@ require ( github.com/gobwas/pool v0.2.1 // indirect github.com/gobwas/ws v1.4.0 // indirect github.com/google/uuid v1.6.0 // indirect + github.com/maddalax/htmgo/extensions/websocket v0.0.0-20241016202901-6783fcf25143 // indirect + github.com/puzpuzpuz/xsync/v3 v3.4.0 // indirect golang.org/x/sys v0.6.0 // indirect ) diff --git a/examples/sse-with-state/go.sum b/examples/sse-with-state/go.sum index 94d5634..1aaf5dc 100644 --- a/examples/sse-with-state/go.sum +++ b/examples/sse-with-state/go.sum @@ -10,10 +10,16 @@ github.com/gobwas/ws v1.4.0 h1:CTaoG1tojrh4ucGPcoJFiAQUAsEWekEWvLy7GsVNqGs= github.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/maddalax/htmgo/extensions/websocket v0.0.0-20241016202901-6783fcf25143 h1:Qzz4xsn2yfUV11La2lfXk90pSBpV0erp/gQoou1vh/k= +github.com/maddalax/htmgo/extensions/websocket v0.0.0-20241016202901-6783fcf25143/go.mod h1:r6/VqntLp7VlAUpIXy3MWZMHs2EkPKJP5rJdDL8lFP4= github.com/maddalax/htmgo/framework v0.0.0-20241006162137-150c87b4560b h1:LzZTNwIGe0RHiEJZlpnpN8GRnKg2lCZppMX+JIyeF/g= github.com/maddalax/htmgo/framework v0.0.0-20241006162137-150c87b4560b/go.mod h1:HYKI49Pb6oyY2opSJdTt145B1vWgfWIDohvlolynv80= +github.com/maddalax/htmgo/framework v0.0.0-20241014151703-8503dffa4e7d h1:oysEaiKB7/WbvEklkyQ7SEE1xmDeGLrBUvF3BAsBUns= +github.com/maddalax/htmgo/framework v0.0.0-20241014151703-8503dffa4e7d/go.mod h1:HYKI49Pb6oyY2opSJdTt145B1vWgfWIDohvlolynv80= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/puzpuzpuz/xsync/v3 v3.4.0 h1:DuVBAdXuGFHv8adVXjWWZ63pJq+NRXOWVXlKDBZ+mJ4= +github.com/puzpuzpuz/xsync/v3 v3.4.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= diff --git a/examples/sse-with-state/main.go b/examples/sse-with-state/main.go index 6f3f1bb..cb51e90 100644 --- a/examples/sse-with-state/main.go +++ b/examples/sse-with-state/main.go @@ -1,7 +1,8 @@ package main import ( - websocket "github.com/maddalax/htmgo/extensions/ws" + "github.com/maddalax/htmgo/extensions/websocket" + ws2 "github.com/maddalax/htmgo/extensions/websocket/opts" "github.com/maddalax/htmgo/framework/h" "github.com/maddalax/htmgo/framework/service" "io/fs" @@ -16,8 +17,11 @@ func main() { ServiceLocator: locator, LiveReload: true, Register: func(app *h.App) { - websocket.EnableExtension(app, websocket.WsExtensionOpts{ + websocket.EnableExtension(app, ws2.ExtensionOpts{ WsPath: "/ws", + SessionId: func(ctx *h.RequestContext) string { + return ctx.QueryParam("sessionId") + }, }) sub, err := fs.Sub(GetStaticAssets(), "assets/dist") diff --git a/examples/sse-with-state/pages/index.go b/examples/sse-with-state/pages/index.go index 6992ffc..cb29a84 100644 --- a/examples/sse-with-state/pages/index.go +++ b/examples/sse-with-state/pages/index.go @@ -2,19 +2,19 @@ package pages import ( "fmt" - "github.com/maddalax/htmgo/extensions/websocket/state" "github.com/maddalax/htmgo/extensions/websocket/ws" "github.com/maddalax/htmgo/framework/h" + "github.com/maddalax/htmgo/framework/session" "sse-with-state/partials" ) func IndexPage(ctx *h.RequestContext) *h.Page { - sessionId := state.GetSessionId(ctx) + sessionId := session.GetSessionId(ctx) return h.NewPage( RootPage( ctx, h.Div( - h.Attribute("ws-connect", fmt.Sprintf("/ws/test?sessionId=%s", sessionId)), + h.Attribute("ws-connect", fmt.Sprintf("/ws?sessionId=%s", sessionId)), h.Class("flex flex-col gap-4 items-center pt-24 min-h-screen bg-neutral-100"), h.H3(h.Id("intro-text"), h.Text("Repeater Example"), h.Class("text-2xl")), diff --git a/examples/sse-with-state/pages/root.go b/examples/sse-with-state/pages/root.go index edd844e..43d9076 100644 --- a/examples/sse-with-state/pages/root.go +++ b/examples/sse-with-state/pages/root.go @@ -1,12 +1,12 @@ package pages import ( - "github.com/maddalax/htmgo/extensions/websocket/state" "github.com/maddalax/htmgo/framework/h" + "github.com/maddalax/htmgo/framework/session" ) func RootPage(ctx *h.RequestContext, children ...h.Ren) h.Ren { - s := state.NewState(ctx) + s := session.NewState(ctx) return h.Html( h.Attribute("data-session-id", string(s.SessionId)), h.HxExtension(h.BaseExtensions()), diff --git a/examples/sse-with-state/partials/index.go b/examples/sse-with-state/partials/index.go index fcec0e8..300f9ed 100644 --- a/examples/sse-with-state/partials/index.go +++ b/examples/sse-with-state/partials/index.go @@ -1,9 +1,9 @@ package partials import ( - "github.com/maddalax/htmgo/extensions/websocket/state" "github.com/maddalax/htmgo/extensions/websocket/ws" "github.com/maddalax/htmgo/framework/h" + "github.com/maddalax/htmgo/framework/session" ) type Counter struct { @@ -12,8 +12,8 @@ type Counter struct { Decrement func() } -func UseCounter(sessionId state.SessionId, id string) Counter { - get, set := state.Use(sessionId, id, 0) +func UseCounter(sessionId session.Id, id string) Counter { + get, set := session.UseState(sessionId, id, 0) var increment = func() { set(get() + 1) @@ -38,7 +38,7 @@ func CounterForm(ctx *h.RequestContext, props CounterProps) *h.Element { if props.Id == "" { props.Id = h.GenId(6) } - counter := UseCounter(state.GetSessionId(ctx), props.Id) + counter := UseCounter(session.GetSessionId(ctx), props.Id) return h.Div( h.Attribute("hx-swap", "none"), diff --git a/extensions/websocket/init.go b/extensions/websocket/init.go index 4547b9e..54143df 100644 --- a/extensions/websocket/init.go +++ b/extensions/websocket/init.go @@ -2,22 +2,28 @@ package websocket import ( "github.com/maddalax/htmgo/extensions/websocket/internal/wsutil" + "github.com/maddalax/htmgo/extensions/websocket/opts" "github.com/maddalax/htmgo/extensions/websocket/ws" "github.com/maddalax/htmgo/framework/h" "github.com/maddalax/htmgo/framework/service" ) -type WsExtensionOpts struct { - WsPath string -} - -func EnableExtension(app *h.App, opts WsExtensionOpts) { +func EnableExtension(app *h.App, opts opts.ExtensionOpts) { if app.Opts.ServiceLocator == nil { app.Opts.ServiceLocator = service.NewLocator() } + + if opts.WsPath == "" { + panic("websocket: WsPath is required") + } + + if opts.SessionId == nil { + panic("websocket: SessionId func is required") + } + service.Set[wsutil.SocketManager](app.Opts.ServiceLocator, service.Singleton, func() *wsutil.SocketManager { - return wsutil.NewSocketManager() + return wsutil.NewSocketManager(&opts) }) ws.StartListener(app.Opts.ServiceLocator) - app.Router.Handle(opts.WsPath, wsutil.WsHttpHandler()) + app.Router.Handle(opts.WsPath, wsutil.WsHttpHandler(&opts)) } diff --git a/extensions/websocket/internal/wsutil/handler.go b/extensions/websocket/internal/wsutil/handler.go index 457aeb3..4beb0c3 100644 --- a/extensions/websocket/internal/wsutil/handler.go +++ b/extensions/websocket/internal/wsutil/handler.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/gobwas/ws" "github.com/gobwas/ws/wsutil" + ws2 "github.com/maddalax/htmgo/extensions/websocket/opts" "github.com/maddalax/htmgo/framework/h" "github.com/maddalax/htmgo/framework/service" "log/slog" @@ -13,13 +14,13 @@ import ( "time" ) -func WsHttpHandler() http.HandlerFunc { +func WsHttpHandler(opts *ws2.ExtensionOpts) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { cc := r.Context().Value(h.RequestContextKey).(*h.RequestContext) locator := cc.ServiceLocator() manager := service.Get[SocketManager](locator) - sessionId := r.URL.Query().Get("sessionId") + sessionId := opts.SessionId(cc) if sessionId == "" { w.WriteHeader(http.StatusUnauthorized) diff --git a/extensions/websocket/internal/wsutil/manager.go b/extensions/websocket/internal/wsutil/manager.go index 3505b3d..1c11daf 100644 --- a/extensions/websocket/internal/wsutil/manager.go +++ b/extensions/websocket/internal/wsutil/manager.go @@ -2,6 +2,7 @@ package wsutil import ( "fmt" + "github.com/maddalax/htmgo/extensions/websocket/opts" "github.com/puzpuzpuz/xsync/v3" "strings" "time" @@ -40,12 +41,14 @@ type SocketManager struct { sockets *xsync.MapOf[string, *xsync.MapOf[string, SocketConnection]] idToRoom *xsync.MapOf[string, string] listeners []chan SocketEvent + opts *opts.ExtensionOpts } -func NewSocketManager() *SocketManager { +func NewSocketManager(opts *opts.ExtensionOpts) *SocketManager { return &SocketManager{ sockets: xsync.NewMapOf[string, *xsync.MapOf[string, SocketConnection]](), idToRoom: xsync.NewMapOf[string, string](), + opts: opts, } } diff --git a/extensions/websocket/opts/opts.go b/extensions/websocket/opts/opts.go new file mode 100644 index 0000000..1019cfa --- /dev/null +++ b/extensions/websocket/opts/opts.go @@ -0,0 +1,8 @@ +package opts + +import "github.com/maddalax/htmgo/framework/h" + +type ExtensionOpts struct { + WsPath string + SessionId func(ctx *h.RequestContext) string +} diff --git a/extensions/websocket/ws/handler.go b/extensions/websocket/ws/handler.go index 6ad056e..72cdff8 100644 --- a/extensions/websocket/ws/handler.go +++ b/extensions/websocket/ws/handler.go @@ -3,7 +3,7 @@ package ws import ( "fmt" "github.com/maddalax/htmgo/extensions/websocket/internal/wsutil" - "github.com/maddalax/htmgo/extensions/websocket/state" + "github.com/maddalax/htmgo/framework/session" "sync" ) @@ -62,7 +62,7 @@ func (h *MessageHandler) OnServerSideEvent(e ServerSideEvent) { } } -func (h *MessageHandler) OnClientSideEvent(handlerId string, sessionId state.SessionId) { +func (h *MessageHandler) OnClientSideEvent(handlerId string, sessionId session.Id) { cb, ok := handlers.Load(handlerId) if ok { cb(HandlerData{ @@ -78,7 +78,7 @@ func (h *MessageHandler) OnDomElementRemoved(handlerId string) { } func (h *MessageHandler) OnSocketDisconnected(event wsutil.SocketEvent) { - sessionId := state.SessionId(event.SessionId) + sessionId := session.Id(event.SessionId) hashes, ok := sessionIdToHashes.Load(sessionId) if ok { for hash := range hashes { diff --git a/extensions/websocket/ws/listener.go b/extensions/websocket/ws/listener.go index 4d02d47..dff8e1a 100644 --- a/extensions/websocket/ws/listener.go +++ b/extensions/websocket/ws/listener.go @@ -3,8 +3,8 @@ package ws import ( "fmt" "github.com/maddalax/htmgo/extensions/websocket/internal/wsutil" - "github.com/maddalax/htmgo/extensions/websocket/state" "github.com/maddalax/htmgo/framework/service" + "github.com/maddalax/htmgo/framework/session" "time" ) @@ -34,7 +34,7 @@ func StartListener(locator *service.Locator) { case wsutil.MessageEvent: handlerId := event.Payload["id"].(string) eventName := event.Payload["event"].(string) - sessionId := state.SessionId(event.SessionId) + sessionId := session.Id(event.SessionId) if eventName == "dom-element-removed" { handler.OnDomElementRemoved(handlerId) continue diff --git a/extensions/websocket/ws/register.go b/extensions/websocket/ws/register.go index 222fa90..f712230 100644 --- a/extensions/websocket/ws/register.go +++ b/extensions/websocket/ws/register.go @@ -2,15 +2,15 @@ package ws import ( "github.com/maddalax/htmgo/extensions/websocket/internal/wsutil" - "github.com/maddalax/htmgo/extensions/websocket/state" "github.com/maddalax/htmgo/framework/h" + "github.com/maddalax/htmgo/framework/session" "github.com/puzpuzpuz/xsync/v3" "sync" "sync/atomic" ) type HandlerData struct { - SessionId state.SessionId + SessionId session.Id Socket *wsutil.SocketConnection Manager *wsutil.SocketManager } @@ -20,13 +20,13 @@ type Handler func(data HandlerData) type ServerSideEvent struct { Event string Payload map[string]any - SessionId state.SessionId + SessionId session.Id } type KeyHash = string var handlers = xsync.NewMapOf[KeyHash, Handler]() -var sessionIdToHashes = xsync.NewMapOf[state.SessionId, map[KeyHash]bool]() -var hashesToSessionId = xsync.NewMapOf[KeyHash, state.SessionId]() +var sessionIdToHashes = xsync.NewMapOf[session.Id, map[KeyHash]bool]() +var hashesToSessionId = xsync.NewMapOf[KeyHash, session.Id]() var serverEventNamesToHash = xsync.NewMapOf[string, map[KeyHash]bool]() var socketMessageListener = make(chan wsutil.SocketEvent, 100) @@ -44,7 +44,7 @@ func AddServerSideHandler(ctx *h.RequestContext, event string, handler Handler) if callingHandler.Load() { return h.NewAttributeMap() } - sessionId := state.GetSessionId(ctx) + sessionId := session.GetSessionId(ctx) hash := makeId() handlers.LoadOrStore(hash, handler) m, _ := serverEventNamesToHash.LoadOrCompute(event, func() map[KeyHash]bool { @@ -59,19 +59,19 @@ func AddServerSideHandler(ctx *h.RequestContext, event string, handler Handler) func AddClientSideHandler(ctx *h.RequestContext, event string, handler Handler) *h.AttributeMapOrdered { hash := makeId() handlers.LoadOrStore(hash, handler) - sessionId := state.GetSessionId(ctx) + sessionId := session.GetSessionId(ctx) storeHashForSession(sessionId, hash) storeSessionIdForHash(sessionId, hash) return h.AttributePairs("data-handler-id", hash, "data-handler-event", event) } -func storeHashForSession(sessionId state.SessionId, hash KeyHash) { +func storeHashForSession(sessionId session.Id, hash KeyHash) { m, _ := sessionIdToHashes.LoadOrCompute(sessionId, func() map[KeyHash]bool { return make(map[KeyHash]bool) }) m[hash] = true } -func storeSessionIdForHash(sessionId state.SessionId, hash KeyHash) { +func storeSessionIdForHash(sessionId session.Id, hash KeyHash) { hashesToSessionId.Store(hash, sessionId) } diff --git a/extensions/websocket/state/state.go b/framework/session/state.go similarity index 64% rename from extensions/websocket/state/state.go rename to framework/session/state.go index 039e47b..b3adbd2 100644 --- a/extensions/websocket/state/state.go +++ b/framework/session/state.go @@ -1,18 +1,17 @@ -package state +package session import ( "fmt" - "github.com/maddalax/htmgo/extensions/websocket/internal" "github.com/maddalax/htmgo/framework/h" "github.com/puzpuzpuz/xsync/v3" ) -type SessionId string +type Id string -var cache = xsync.NewMapOf[SessionId, *xsync.MapOf[string, any]]() +var cache = xsync.NewMapOf[Id, *xsync.MapOf[string, any]]() type State struct { - SessionId SessionId + SessionId Id } func NewState(ctx *h.RequestContext) *State { @@ -23,28 +22,28 @@ func NewState(ctx *h.RequestContext) *State { } } -func GetSessionId(ctx *h.RequestContext) SessionId { +func GetSessionId(ctx *h.RequestContext) Id { sessionIdRaw := ctx.Get("session-id") sessionId := "" if sessionIdRaw == "" || sessionIdRaw == nil { - sessionId = fmt.Sprintf("session-id-%s", internal.RandSeq(30)) + sessionId = fmt.Sprintf("session-id-%s", h.GenId(30)) ctx.Set("session-id", sessionId) } else { sessionId = sessionIdRaw.(string) } - return SessionId(sessionId) + return Id(sessionId) } -func Update[T any](sessionId SessionId, key string, compute func(prev T) T) T { +func Update[T any](sessionId Id, key string, compute func(prev T) T) T { actual := Get[T](sessionId, key, *new(T)) next := compute(actual) Set(sessionId, key, next) return next } -func Get[T any](sessionId SessionId, key string, fallback T) T { +func Get[T any](sessionId Id, key string, fallback T) T { actual, _ := cache.LoadOrCompute(sessionId, func() *xsync.MapOf[string, any] { return xsync.NewMapOf[string, any]() }) @@ -55,14 +54,14 @@ func Get[T any](sessionId SessionId, key string, fallback T) T { return fallback } -func Set(sessionId SessionId, key string, value any) { +func Set(sessionId Id, key string, value any) { actual, _ := cache.LoadOrCompute(sessionId, func() *xsync.MapOf[string, any] { return xsync.NewMapOf[string, any]() }) actual.Store(key, value) } -func Use[T any](sessionId SessionId, key string, initial T) (func() T, func(T)) { +func UseState[T any](sessionId Id, key string, initial T) (func() T, func(T)) { var get = func() T { return Get[T](sessionId, key, initial) }