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"
"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("<!DOCTYPE html>\n<html>\n")
getStartPage(buffer, app.params)
getStartPage(buffer, app.nextSessionID(), app.params)
buffer.WriteString("\n</html>")
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
}
}
}
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) {
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
}
message := string(reqBody)
if ProtocolInDebugLog {
DebugLog(message)
}
@ -179,44 +194,44 @@ func (app *application) postHandler(w http.ResponseWriter, req *http.Request) {
return
}
sessionID, ok := getSessionID(obj)
if !ok {
return
}
var session Session = nil
var response chan string = 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 {
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()
session = app.createSession(obj, events, bridge, response)
if session == nil {
return
}
bridge.sendResponse()
setSessionIDCookie(w, session.ID())
startSession = true
go sessionEventHandler(session, events, bridge)
start := func() {
startSession(session, app.createContentFunc)
bridge.sendResponse()
}
go start()
}
if !startSession {
switch command {
case "startSession":
// do nothing
case "nop":
session.sendResponse()
@ -230,13 +245,27 @@ func (app *application) postHandler(w http.ResponseWriter, req *http.Request) {
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,19 +291,14 @@ 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 sessionID, ok := getSessionID(obj); ok {
if info, ok := app.sessions[sessionID]; ok {
session = info.session
session.setBridge(events, bridge)
@ -284,11 +308,6 @@ func (app *application) socketReader(bridge *wsBridge) {
} else {
DebugLogF("Session #%d not exists", sessionID)
}
} else {
ErrorLog(`strconv.Atoi(sessionText) error: ` + err.Error())
}
} 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{}

View File

@ -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) {

View File

@ -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(`<head>
<meta charset="utf-8">
<title>`)
@ -104,6 +106,11 @@ func getStartPage(buffer *strings.Builder, params AppParams) {
buffer.WriteString(appStyles)
buffer.WriteString(`</style>
<style id="ruiAnimations"></style>
<script>
const sessionID = `)
buffer.WriteString(strconv.Itoa(sessionID))
buffer.WriteString(`;
</script>
<script src="/script.js"></script>
</head>
<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)
text, ok := data.PropertyValue("error")
text, ok := data.PropertyValue("values")
if !ok {
ErrorLog("'values' property not found (command: storageValues)")
return

View File

@ -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)

View File

@ -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
}

View File

@ -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