From fb5582892e136d878e65fd8c9cdfd86dd872ba6a Mon Sep 17 00:00:00 2001 From: Alexei Anoshenko <2277098+anoshenko@users.noreply.github.com> Date: Tue, 28 Jul 2026 12:26:03 +0300 Subject: [PATCH] Fixing and optimisation --- appServer.go | 220 ++++++++++++++++++++++++++--------------------- app_scripts.js | 4 +- application.go | 11 ++- clientStorage.go | 2 +- session.go | 10 +++ wasmBridge.go | 10 +++ webBridge.go | 4 + 7 files changed, 159 insertions(+), 102 deletions(-) diff --git a/appServer.go b/appServer.go index ab856da..5576876 100644 --- a/appServer.go +++ b/appServer.go @@ -12,6 +12,7 @@ import ( "net/http" "os/exec" "runtime" + "slices" "strconv" "strings" "time" @@ -43,6 +44,7 @@ type application struct { params AppParams createContentFunc func(Session) SessionContent sessions map[int]sessionInfo + usedSessionIds []int } func (app *application) getStartPage() string { @@ -50,7 +52,7 @@ func (app *application) getStartPage() string { defer freeStringBuilder(buffer) buffer.WriteString("\n\n") - getStartPage(buffer, app.params) + getStartPage(buffer, app.nextSessionID(), app.params) buffer.WriteString("\n") return buffer.String() } @@ -80,14 +82,23 @@ func (app *application) Finish() { } } +func (app *application) getCreateContentFunc() func(Session) SessionContent { + return app.createContentFunc +} + func (app *application) nextSessionID() int { - n := rand.Intn(0x7FFFFFFE) + 1 - _, ok := app.sessions[n] - for ok { - n = rand.Intn(0x7FFFFFFE) + 1 - _, ok = app.sessions[n] + if app.usedSessionIds == nil { + app.usedSessionIds = []int{} + } + + for { + n := rand.Intn(0x7FFFFFFE) + 1 + if !slices.Contains(app.usedSessionIds, n) { + app.usedSessionIds = append(app.usedSessionIds, n) + slices.Sort(app.usedSessionIds) + return n + } } - return n } func (app *application) removeSession(id int) { @@ -166,77 +177,95 @@ func getSessionIDCookie(req *http.Request) (int, error) { func (app *application) postHandler(w http.ResponseWriter, req *http.Request) { - if reqBody, err := io.ReadAll(req.Body); err == nil { - message := string(reqBody) + reqBody, err := io.ReadAll(req.Body) + if err != nil { + ErrorLog(err.Error()) + return + } - if ProtocolInDebugLog { - DebugLog(message) - } + message := string(reqBody) + if ProtocolInDebugLog { + DebugLog(message) + } - obj, err := ParseDataText(message) - if err != nil { - ErrorLog(err.Error()) + obj, err := ParseDataText(message) + if err != nil { + ErrorLog(err.Error()) + return + } + + sessionID, ok := getSessionID(obj) + if !ok { + return + } + + var session Session = nil + var response chan string = nil + if info, ok := app.sessions[sessionID]; ok && info.response != nil { + response = info.response + session = info.session + } + + command := obj.Tag() + + if session == nil || command == "startSession" { + events := make(chan DataObject, 1024) + bridge := createHttpBridge(req) + response = bridge.response + + session = app.createSession(obj, events, bridge, response) + if session == nil { return } - var session Session = nil - var response chan string = nil + go sessionEventHandler(session, events, bridge) - if cookie, err := req.Cookie("session"); err == nil { - sessionID, err := strconv.Atoi(cookie.Value) - if err != nil { - ErrorLog(err.Error()) - } else if info, ok := app.sessions[sessionID]; ok && info.response != nil { - response = info.response - session = info.session - } - } - - command := obj.Tag() - startSession := false - - if session == nil || command == "startSession" { - events := make(chan DataObject, 1024) - bridge := createHttpBridge(req) - response = bridge.response - answer := "" - session, answer = app.startSession(obj, events, bridge, response) - - bridge.writeMessage(answer) - session.onStart() - if command == "session-resume" { - session.onResume() - } + start := func() { + startSession(session, app.createContentFunc) bridge.sendResponse() - - setSessionIDCookie(w, session.ID()) - startSession = true - - go sessionEventHandler(session, events, bridge) } + go start() + } - if !startSession { - switch command { - case "nop": - session.sendResponse() + switch command { - case "session-close": - session.onFinish() - session.App().removeSession(session.ID()) - return + case "startSession": + // do nothing - default: - if !session.handleAnswer(command, obj) { - session.addToEventsQueue(obj) - } - } - } + case "nop": + session.sendResponse() - io.WriteString(w, <-response) - for len(response) > 0 { - io.WriteString(w, <-response) + case "session-close": + session.onFinish() + session.App().removeSession(session.ID()) + return + + default: + if !session.handleAnswer(command, obj) { + session.addToEventsQueue(obj) } } + + io.WriteString(w, <-response) + for len(response) > 0 { + io.WriteString(w, <-response) + } +} + +func getSessionID(obj DataObject) (int, bool) { + sessionText, ok := obj.PropertyValue("session") + if !ok { + ErrorLog(`"session" key not found`) + return 0, false + } + + sessionID, err := strconv.Atoi(sessionText) + if err != nil { + ErrorLog(`"session" key text strconv.Atoi error: ` + err.Error()) + return 0, false + } + + return sessionID, true } func (app *application) socketReader(bridge *wsBridge) { @@ -262,33 +291,23 @@ func (app *application) socketReader(bridge *wsBridge) { switch command := obj.Tag(); command { case "startSession": - answer := "" - if session, answer = app.startSession(obj, events, bridge, nil); session != nil { - if !bridge.writeMessage(answer) { - return - } - session.onStart() + if session = app.createSession(obj, events, bridge, nil); session != nil { + go startSession(session, app.createContentFunc) go sessionEventHandler(session, events, bridge) } case "reconnect": session = nil - if sessionText, ok := obj.PropertyValue("session"); ok { - if sessionID, err := strconv.Atoi(sessionText); err == nil { - if info, ok := app.sessions[sessionID]; ok { - session = info.session - session.setBridge(events, bridge) + if sessionID, ok := getSessionID(obj); ok { + if info, ok := app.sessions[sessionID]; ok { + session = info.session + session.setBridge(events, bridge) - go sessionEventHandler(session, events, bridge) - session.onReconnect() - } else { - DebugLogF("Session #%d not exists", sessionID) - } + go sessionEventHandler(session, events, bridge) + session.onReconnect() } else { - ErrorLog(`strconv.Atoi(sessionText) error: ` + err.Error()) + DebugLogF("Session #%d not exists", sessionID) } - } else { - ErrorLog(`"session" key not found`) } if session == nil { @@ -327,37 +346,42 @@ func sessionEventHandler(session Session, events chan DataObject, bridge bridge) session.onFinish() session.App().removeSession(session.ID()) bridge.close() + return default: - session.handleEvent(command, data) + go session.handleEvent(command, data) } } } -func (app *application) startSession(params DataObject, events chan DataObject, - bridge bridge, response chan string) (Session, string) { +func (app *application) createSession(params DataObject, events chan DataObject, + bridge bridge, response chan string) Session { - if app.createContentFunc == nil { - return nil, "" + sessionID, ok := getSessionID(params) + if !ok || app.createContentFunc == nil { + return nil } - session := newSession(app, app.nextSessionID(), "", params) + session := newSession(app, sessionID, "", params) session.setBridge(events, bridge) - if !session.setContent(app.createContentFunc(session)) { - return nil, "" - } - app.sessions[session.ID()] = sessionInfo{ + app.sessions[sessionID] = sessionInfo{ session: session, response: response, } + return session +} + +func startSession(session Session, createContentFunc func(Session) SessionContent) { + + if !session.setContent(createContentFunc(session)) { + return + } + answer := allocStringBuilder() defer freeStringBuilder(answer) - answer.WriteString("sessionID = '") - answer.WriteString(strconv.Itoa(session.ID())) - answer.WriteString("';\n") session.writeInitScript(answer) answerText := answer.String() @@ -365,7 +389,9 @@ func (app *application) startSession(params DataObject, events chan DataObject, DebugLog("Start session:") DebugLog(answerText) } - return session, answerText + + session.writeScript(answerText) + session.onStart() } var apps = []*application{} diff --git a/app_scripts.js b/app_scripts.js index 2ac2675..70f763c 100644 --- a/app_scripts.js +++ b/app_scripts.js @@ -1,4 +1,4 @@ -let sessionID = "0" +//let sessionID = "0" let windowFocus = true window.onresize = function() { @@ -21,7 +21,7 @@ function reloadPage() { function sessionInfo() { const touch_screen = (('ontouchstart' in document.documentElement) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0)) ? "1" : "0"; - let message = "startSession{touch=" + touch_screen + let message = "startSession{session=" + sessionID + ",touch=" + touch_screen const style = window.getComputedStyle(document.body); if (style) { diff --git a/application.go b/application.go index a4907f2..8f634aa 100644 --- a/application.go +++ b/application.go @@ -2,6 +2,7 @@ package rui import ( _ "embed" + "strconv" "strings" ) @@ -23,6 +24,7 @@ type Application interface { Params() AppParams removeSession(id int) + getCreateContentFunc() func(Session) SessionContent } // AppParams defines parameters of the app @@ -66,7 +68,7 @@ type AppParams struct { GoogleFonts string } -func getStartPage(buffer *strings.Builder, params AppParams) { +func getStartPage(buffer *strings.Builder, sessionID int, params AppParams) { buffer.WriteString(` `) @@ -104,7 +106,12 @@ func getStartPage(buffer *strings.Builder, params AppParams) { buffer.WriteString(appStyles) buffer.WriteString(`</style> <style id="ruiAnimations"></style> - <script src="/script.js"></script> + <script> +const sessionID = `) + buffer.WriteString(strconv.Itoa(sessionID)) + buffer.WriteString(`; + </script> + <script src="/script.js"></script> </head> <body id="body" onkeydown="keyDownEvent(this, event)"> <div class="ruiRoot" id="ruiRootView"></div> diff --git a/clientStorage.go b/clientStorage.go index 7aaaa43..059f6bf 100644 --- a/clientStorage.go +++ b/clientStorage.go @@ -139,7 +139,7 @@ func (storage *clientStorageData) handleEvent(command string, data DataObject) { } delete(storage.getResult, request) - text, ok := data.PropertyValue("error") + text, ok := data.PropertyValue("values") if !ok { ErrorLog("'values' property not found (command: storageValues)") return diff --git a/session.go b/session.go index 5e52950..8764fc5 100644 --- a/session.go +++ b/session.go @@ -11,6 +11,7 @@ import ( ) type bridge interface { + writeScript(script string) startUpdateScript(htmlID string) bool finishUpdateScript(htmlID string) callFunc(funcName string, args ...any) bool @@ -175,6 +176,7 @@ type Session interface { updateCSSProperty(htmlID, property, value string) updateProperty(htmlID, property string, value any) removeProperty(htmlID, property string) + writeScript(script string) startUpdateScript(htmlID string) bool finishUpdateScript(htmlID string) sendResponse() @@ -508,6 +510,14 @@ func (session *sessionData) removeProperty(htmlID, property string) { } } +func (session *sessionData) writeScript(script string) { + if session.bridge != nil { + session.bridge.writeScript(script) + } else { + ErrorLog("No connection") + } +} + func (session *sessionData) startUpdateScript(htmlID string) bool { if session.bridge != nil { return session.bridge.startUpdateScript(htmlID) diff --git a/wasmBridge.go b/wasmBridge.go index 7024763..4b39a1c 100644 --- a/wasmBridge.go +++ b/wasmBridge.go @@ -25,6 +25,16 @@ func createWasmBridge(close chan DataObject) bridge { return bridge } +func (bridge *wasmBridge) writeScript(script string) { + if ProtocolInDebugLog { + DebugLog("Run script:") + DebugLog(script) + } + + window := js.Global().Get("window") + window.Call("execScript", script) +} + func (bridge *wasmBridge) startUpdateScript(htmlID string) bool { return false } diff --git a/webBridge.go b/webBridge.go index c1fdfc5..2f67060 100644 --- a/webBridge.go +++ b/webBridge.go @@ -110,6 +110,10 @@ func (bridge *webBridge) initBridge() { bridge.updateScripts = map[string]*strings.Builder{} } +func (bridge *webBridge) writeScript(script string) { + bridge.writeMessage(script) +} + func (bridge *webBridge) startUpdateScript(htmlID string) bool { if _, ok := bridge.updateScripts[htmlID]; ok { return false