Fixing and optimisation

This commit is contained in:
Alexei Anoshenko 2026-07-28 12:26:03 +03:00
parent 938432d738
commit fb5582892e
7 changed files with 159 additions and 102 deletions

View File

@ -12,6 +12,7 @@ import (
"net/http" "net/http"
"os/exec" "os/exec"
"runtime" "runtime"
"slices"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -43,6 +44,7 @@ type application struct {
params AppParams params AppParams
createContentFunc func(Session) SessionContent createContentFunc func(Session) SessionContent
sessions map[int]sessionInfo sessions map[int]sessionInfo
usedSessionIds []int
} }
func (app *application) getStartPage() string { func (app *application) getStartPage() string {
@ -50,7 +52,7 @@ func (app *application) getStartPage() string {
defer freeStringBuilder(buffer) defer freeStringBuilder(buffer)
buffer.WriteString("<!DOCTYPE html>\n<html>\n") buffer.WriteString("<!DOCTYPE html>\n<html>\n")
getStartPage(buffer, app.params) getStartPage(buffer, app.nextSessionID(), app.params)
buffer.WriteString("\n</html>") buffer.WriteString("\n</html>")
return buffer.String() 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 { func (app *application) nextSessionID() int {
n := rand.Intn(0x7FFFFFFE) + 1 if app.usedSessionIds == nil {
_, ok := app.sessions[n] app.usedSessionIds = []int{}
for ok {
n = rand.Intn(0x7FFFFFFE) + 1
_, ok = app.sessions[n]
} }
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) { func (app *application) removeSession(id int) {
@ -166,9 +177,13 @@ func getSessionIDCookie(req *http.Request) (int, error) {
func (app *application) postHandler(w http.ResponseWriter, req *http.Request) { func (app *application) postHandler(w http.ResponseWriter, req *http.Request) {
if reqBody, err := io.ReadAll(req.Body); err == nil { reqBody, err := io.ReadAll(req.Body)
message := string(reqBody) if err != nil {
ErrorLog(err.Error())
return
}
message := string(reqBody)
if ProtocolInDebugLog { if ProtocolInDebugLog {
DebugLog(message) DebugLog(message)
} }
@ -179,44 +194,44 @@ func (app *application) postHandler(w http.ResponseWriter, req *http.Request) {
return return
} }
sessionID, ok := getSessionID(obj)
if !ok {
return
}
var session Session = nil var session Session = nil
var response chan string = nil var response chan string = nil
if info, ok := app.sessions[sessionID]; ok && info.response != nil {
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 response = info.response
session = info.session session = info.session
} }
}
command := obj.Tag() command := obj.Tag()
startSession := false
if session == nil || command == "startSession" { if session == nil || command == "startSession" {
events := make(chan DataObject, 1024) events := make(chan DataObject, 1024)
bridge := createHttpBridge(req) bridge := createHttpBridge(req)
response = bridge.response response = bridge.response
answer := ""
session, answer = app.startSession(obj, events, bridge, response)
bridge.writeMessage(answer) session = app.createSession(obj, events, bridge, response)
session.onStart() if session == nil {
if command == "session-resume" { return
session.onResume()
} }
bridge.sendResponse()
setSessionIDCookie(w, session.ID())
startSession = true
go sessionEventHandler(session, events, bridge) go sessionEventHandler(session, events, bridge)
start := func() {
startSession(session, app.createContentFunc)
bridge.sendResponse()
}
go start()
} }
if !startSession {
switch command { switch command {
case "startSession":
// do nothing
case "nop": case "nop":
session.sendResponse() session.sendResponse()
@ -230,13 +245,27 @@ func (app *application) postHandler(w http.ResponseWriter, req *http.Request) {
session.addToEventsQueue(obj) session.addToEventsQueue(obj)
} }
} }
}
io.WriteString(w, <-response) io.WriteString(w, <-response)
for len(response) > 0 { for len(response) > 0 {
io.WriteString(w, <-response) 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) { func (app *application) socketReader(bridge *wsBridge) {
@ -262,19 +291,14 @@ func (app *application) socketReader(bridge *wsBridge) {
switch command := obj.Tag(); command { switch command := obj.Tag(); command {
case "startSession": case "startSession":
answer := "" if session = app.createSession(obj, events, bridge, nil); session != nil {
if session, answer = app.startSession(obj, events, bridge, nil); session != nil { go startSession(session, app.createContentFunc)
if !bridge.writeMessage(answer) {
return
}
session.onStart()
go sessionEventHandler(session, events, bridge) go sessionEventHandler(session, events, bridge)
} }
case "reconnect": case "reconnect":
session = nil session = nil
if sessionText, ok := obj.PropertyValue("session"); ok { if sessionID, ok := getSessionID(obj); ok {
if sessionID, err := strconv.Atoi(sessionText); err == nil {
if info, ok := app.sessions[sessionID]; ok { if info, ok := app.sessions[sessionID]; ok {
session = info.session session = info.session
session.setBridge(events, bridge) session.setBridge(events, bridge)
@ -284,11 +308,6 @@ func (app *application) socketReader(bridge *wsBridge) {
} else { } else {
DebugLogF("Session #%d not exists", sessionID) DebugLogF("Session #%d not exists", sessionID)
} }
} else {
ErrorLog(`strconv.Atoi(sessionText) error: ` + err.Error())
}
} else {
ErrorLog(`"session" key not found`)
} }
if session == nil { if session == nil {
@ -327,37 +346,42 @@ func sessionEventHandler(session Session, events chan DataObject, bridge bridge)
session.onFinish() session.onFinish()
session.App().removeSession(session.ID()) session.App().removeSession(session.ID())
bridge.close() bridge.close()
return
default: default:
session.handleEvent(command, data) go session.handleEvent(command, data)
} }
} }
} }
func (app *application) startSession(params DataObject, events chan DataObject, func (app *application) createSession(params DataObject, events chan DataObject,
bridge bridge, response chan string) (Session, string) { bridge bridge, response chan string) Session {
if app.createContentFunc == nil { sessionID, ok := getSessionID(params)
return nil, "" if !ok || app.createContentFunc == nil {
return nil
} }
session := newSession(app, app.nextSessionID(), "", params) session := newSession(app, sessionID, "", params)
session.setBridge(events, bridge) session.setBridge(events, bridge)
if !session.setContent(app.createContentFunc(session)) {
return nil, ""
}
app.sessions[session.ID()] = sessionInfo{ app.sessions[sessionID] = sessionInfo{
session: session, session: session,
response: response, response: response,
} }
return session
}
func startSession(session Session, createContentFunc func(Session) SessionContent) {
if !session.setContent(createContentFunc(session)) {
return
}
answer := allocStringBuilder() answer := allocStringBuilder()
defer freeStringBuilder(answer) defer freeStringBuilder(answer)
answer.WriteString("sessionID = '")
answer.WriteString(strconv.Itoa(session.ID()))
answer.WriteString("';\n")
session.writeInitScript(answer) session.writeInitScript(answer)
answerText := answer.String() answerText := answer.String()
@ -365,7 +389,9 @@ func (app *application) startSession(params DataObject, events chan DataObject,
DebugLog("Start session:") DebugLog("Start session:")
DebugLog(answerText) DebugLog(answerText)
} }
return session, answerText
session.writeScript(answerText)
session.onStart()
} }
var apps = []*application{} var apps = []*application{}

View File

@ -1,4 +1,4 @@
let sessionID = "0" //let sessionID = "0"
let windowFocus = true let windowFocus = true
window.onresize = function() { window.onresize = function() {
@ -21,7 +21,7 @@ function reloadPage() {
function sessionInfo() { function sessionInfo() {
const touch_screen = (('ontouchstart' in document.documentElement) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0)) ? "1" : "0"; 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); const style = window.getComputedStyle(document.body);
if (style) { if (style) {

View File

@ -2,6 +2,7 @@ package rui
import ( import (
_ "embed" _ "embed"
"strconv"
"strings" "strings"
) )
@ -23,6 +24,7 @@ type Application interface {
Params() AppParams Params() AppParams
removeSession(id int) removeSession(id int)
getCreateContentFunc() func(Session) SessionContent
} }
// AppParams defines parameters of the app // AppParams defines parameters of the app
@ -66,7 +68,7 @@ type AppParams struct {
GoogleFonts string GoogleFonts string
} }
func getStartPage(buffer *strings.Builder, params AppParams) { func getStartPage(buffer *strings.Builder, sessionID int, params AppParams) {
buffer.WriteString(`<head> buffer.WriteString(`<head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>`) <title>`)
@ -104,6 +106,11 @@ func getStartPage(buffer *strings.Builder, params AppParams) {
buffer.WriteString(appStyles) buffer.WriteString(appStyles)
buffer.WriteString(`</style> buffer.WriteString(`</style>
<style id="ruiAnimations"></style> <style id="ruiAnimations"></style>
<script>
const sessionID = `)
buffer.WriteString(strconv.Itoa(sessionID))
buffer.WriteString(`;
</script>
<script src="/script.js"></script> <script src="/script.js"></script>
</head> </head>
<body id="body" onkeydown="keyDownEvent(this, event)"> <body id="body" onkeydown="keyDownEvent(this, event)">

View File

@ -139,7 +139,7 @@ func (storage *clientStorageData) handleEvent(command string, data DataObject) {
} }
delete(storage.getResult, request) delete(storage.getResult, request)
text, ok := data.PropertyValue("error") text, ok := data.PropertyValue("values")
if !ok { if !ok {
ErrorLog("'values' property not found (command: storageValues)") ErrorLog("'values' property not found (command: storageValues)")
return return

View File

@ -11,6 +11,7 @@ import (
) )
type bridge interface { type bridge interface {
writeScript(script string)
startUpdateScript(htmlID string) bool startUpdateScript(htmlID string) bool
finishUpdateScript(htmlID string) finishUpdateScript(htmlID string)
callFunc(funcName string, args ...any) bool callFunc(funcName string, args ...any) bool
@ -175,6 +176,7 @@ type Session interface {
updateCSSProperty(htmlID, property, value string) updateCSSProperty(htmlID, property, value string)
updateProperty(htmlID, property string, value any) updateProperty(htmlID, property string, value any)
removeProperty(htmlID, property string) removeProperty(htmlID, property string)
writeScript(script string)
startUpdateScript(htmlID string) bool startUpdateScript(htmlID string) bool
finishUpdateScript(htmlID string) finishUpdateScript(htmlID string)
sendResponse() 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 { func (session *sessionData) startUpdateScript(htmlID string) bool {
if session.bridge != nil { if session.bridge != nil {
return session.bridge.startUpdateScript(htmlID) return session.bridge.startUpdateScript(htmlID)

View File

@ -25,6 +25,16 @@ func createWasmBridge(close chan DataObject) bridge {
return 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 { func (bridge *wasmBridge) startUpdateScript(htmlID string) bool {
return false return false
} }

View File

@ -110,6 +110,10 @@ func (bridge *webBridge) initBridge() {
bridge.updateScripts = map[string]*strings.Builder{} bridge.updateScripts = map[string]*strings.Builder{}
} }
func (bridge *webBridge) writeScript(script string) {
bridge.writeMessage(script)
}
func (bridge *webBridge) startUpdateScript(htmlID string) bool { func (bridge *webBridge) startUpdateScript(htmlID string) bool {
if _, ok := bridge.updateScripts[htmlID]; ok { if _, ok := bridge.updateScripts[htmlID]; ok {
return false return false