Bug fixing

This commit is contained in:
Alexei Anoshenko 2026-08-06 16:52:19 +03:00
parent a9e4eddb3f
commit b18c632a44
8 changed files with 187 additions and 100 deletions

View File

@ -128,6 +128,9 @@ func (app *application) ServeHTTP(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(http.StatusOK)
io.WriteString(w, app.getStartPage())
case "/e":
app.sseHandler(w, req)
case "/ws":
if bridge := createSocketBridge(w, req); bridge != nil {
go app.socketReader(bridge)
@ -157,6 +160,7 @@ func (app *application) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
}
/*
func setSessionIDCookie(w http.ResponseWriter, sessionID int) {
cookie := http.Cookie{
Name: "session",
@ -174,6 +178,7 @@ func getSessionIDCookie(req *http.Request) (int, error) {
return strconv.Atoi(cookie.Value)
}
*/
func (app *application) postHandler(w http.ResponseWriter, req *http.Request) {
@ -208,7 +213,12 @@ func (app *application) postHandler(w http.ResponseWriter, req *http.Request) {
command := obj.Tag()
if session == nil || command == "startSession" {
if session == nil {
if command != "start-session" {
io.WriteString(w, "reloadPage();")
return
}
events := make(chan DataObject, 1024)
bridge := createHttpBridge(req)
response = bridge.response
@ -219,30 +229,25 @@ func (app *application) postHandler(w http.ResponseWriter, req *http.Request) {
}
go sessionEventHandler(session, events, bridge)
start := func() {
startSession(session, app.createContentFunc)
bridge.sendResponse()
}
go start()
}
switch command {
case "startSession":
// do nothing
case "nop":
session.sendResponse()
case "session-close":
session.onFinish()
session.App().removeSession(session.ID())
return
case "nop":
if len(response) == 0 {
session.addToEventsQueue(obj)
}
default:
if !session.handleAnswer(command, obj) {
session.addToEventsQueue(obj)
} else {
io.WriteString(w, "sendNop();")
}
}
@ -252,6 +257,20 @@ func (app *application) postHandler(w http.ResponseWriter, req *http.Request) {
}
}
func (app *application) sseHandler(w http.ResponseWriter, req *http.Request) {
/*
sessionID, err := strconv.Atoi(req.URL.Query().Get("id"))
if err != nil {
ErrorLog("SessionID error: " + err.Error())
return
}
if sessionInfo, ok := app.sessions[sessionID]; ok {
}
*/
}
func getSessionID(obj DataObject) (int, bool) {
sessionText, ok := obj.PropertyValue("session")
if !ok {
@ -286,14 +305,14 @@ func (app *application) socketReader(bridge *wsBridge) {
obj, err := ParseDataText(message)
if err != nil {
ErrorLog(err.Error())
return
continue
}
switch command := obj.Tag(); command {
case "startSession":
case "start-session":
if session = app.createSession(obj, events, bridge, nil); session != nil {
go startSession(session, app.createContentFunc)
go sessionEventHandler(session, events, bridge)
events <- obj
}
case "reconnect":
@ -311,16 +330,6 @@ func (app *application) socketReader(bridge *wsBridge) {
}
if session == nil {
/* answer := ""
if session, answer = app.startSession(obj, events, bridge, nil); session != nil {
if !bridge.writeMessage(answer) {
return
}
session.onStart()
go sessionEventHandler(session, events, bridge)
bridge.writeMessage("restartSession();")
}
*/
bridge.writeMessage("reloadPage();")
return
}
@ -339,6 +348,7 @@ func sessionEventHandler(session Session, events chan DataObject, bridge bridge)
switch command := data.Tag(); command {
case "disconnect":
session.setBridge(nil, nil)
session.onDisconnect()
return
@ -348,8 +358,11 @@ func sessionEventHandler(session Session, events chan DataObject, bridge bridge)
bridge.close()
return
case "nop":
session.sendResponse()
default:
go session.handleEvent(command, data)
session.handleEvent(command, data)
}
}
}
@ -373,27 +386,6 @@ func (app *application) createSession(params DataObject, events chan DataObject,
return session
}
func startSession(session Session, createContentFunc func(Session) SessionContent) {
if !session.setContent(createContentFunc(session)) {
return
}
answer := allocStringBuilder()
defer freeStringBuilder(answer)
session.writeInitScript(answer)
answerText := answer.String()
if ProtocolInDebugLog {
DebugLog("Start session:")
DebugLog(answerText)
}
session.writeScript(answerText)
session.onStart()
}
var apps = []*application{}
// StartApp - create the new application and start it

View File

@ -1,5 +1,10 @@
let eventSource
async function sendMessage(message) {
if (!eventSource) {
createEventSource();
}
const response = await fetch('/', {
method : 'POST',
body : message,
@ -12,14 +17,43 @@ async function sendMessage(message) {
}
}
function createEventSource() {
/*
eventSource = new EventSource("e?id="+sessionID);
eventSource.onmessage = onEventMessage;
eventSource.onerror = onEventError;
*/
}
function onEventMessage(event) {
let script = base64ToString(event.data);
if (script != "") {
window.eval(script)
}
}
function onEventError(err) {
console.log(err);
eventSource = null;
}
window.onload = function() {
sendMessage( sessionInfo() );
sendMessage( sessionInfo("start-session") );
}
window.onfocus = function() {
windowFocus = true
sendMessage( "session-resume{}" );
windowFocus = true;
sendMessage( "session-resume{session=" + sessionID +"}" );
}
function closeSocket() {
if (eventSource) {
eventSource.close();
eventSource = null;
}
}
function sendNop() {
sendMessage( "nop{session=" + sessionID +"}" );
}

View File

@ -1,4 +1,3 @@
//let sessionID = "0"
let windowFocus = true
window.onresize = function() {
@ -18,10 +17,15 @@ function reloadPage() {
location.reload();
}
function sessionInfo() {
function sessionInfo(messageID) {
const touch_screen = (('ontouchstart' in document.documentElement) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0)) ? "1" : "0";
let message = "startSession{session=" + sessionID + ",touch=" + touch_screen
let message = messageID + "{session=" + sessionID
if (('ontouchstart' in document.documentElement) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0)) {
message += ",touch=1"
} else {
message += ",touch=0"
}
const style = window.getComputedStyle(document.body);
if (style) {
@ -65,8 +69,12 @@ function sessionInfo() {
return message + "}";
}
function sendReconnectMessage() {
sendMessage( sessionInfo("reconnect") );
}
function restartSession() {
sendMessage( sessionInfo() );
sendMessage( sessionInfo("start-session") );
}
function getIntAttribute(element, tag) {

View File

@ -1,9 +1,9 @@
let socket
let socket;
function sendMessage(message) {
if (!socket) {
createSocket(function() {
sendMessage( "reconnect{session=" + sessionID + "}" );
sendReconnectMessage();
if (!windowFocus) {
windowFocus = true;
sendMessage( "session-resume{session=" + sessionID +"}" );
@ -16,13 +16,13 @@ function sendMessage(message) {
}
function createSocket(onopen) {
let socketUrl = document.location.protocol == "https:" ? "wss://" : "ws://"
socketUrl += document.location.hostname
const port = document.location.port
let socketUrl = document.location.protocol == "https:" ? "wss://" : "ws://" ;
socketUrl += document.location.hostname;
const port = document.location.port;
if (port) {
socketUrl += ":" + port
socketUrl += ":" + port;
}
socketUrl += window.location.pathname + "ws"
socketUrl += window.location.pathname + "ws";
socket = new WebSocket(socketUrl);
socket.onopen = onopen;
@ -35,19 +35,19 @@ function createSocket(onopen) {
function closeSocket() {
if (socket) {
socket.close()
socket.close();
}
}
window.onload = createSocket(function() {
sendMessage( sessionInfo() );
sendMessage( sessionInfo("start-session") );
});
window.onfocus = function() {
windowFocus = true
windowFocus = true;
if (!socket) {
createSocket(function() {
sendMessage( "reconnect{session=" + sessionID + "}" );
sendReconnectMessage();
sendMessage( "session-resume{session=" + sessionID +"}" );
});
} else {
@ -55,18 +55,14 @@ window.onfocus = function() {
}
}
function onSocketReopen() {
sendMessage( "reconnect{session=" + sessionID + "}" );
}
function socketReconnect() {
if (!socket) {
createSocket(onSocketReopen);
createSocket(sendReconnectMessage);
}
}
function onSocketClose(event) {
console.log("socket closed")
console.log("socket closed");
socket = null;
if (!event.wasClean && windowFocus) {
window.setTimeout(socketReconnect, 10000);

View File

@ -130,8 +130,6 @@ func (storage *clientStorageData) handleEvent(command string, data DataObject) {
ErrorLog(text)
}
//case "storageSuccess":
case "storageValues":
fn, ok := storage.getResult[request]
if !ok {

View File

@ -4,6 +4,7 @@ import (
"iter"
"slices"
"strings"
"sync"
)
// Properties interface of properties map
@ -37,6 +38,7 @@ type Properties interface {
type propertyList struct {
properties map[PropertyName]any
normalize func(PropertyName) PropertyName
mutex sync.Mutex
}
type dataProperty struct {
@ -60,10 +62,15 @@ func (properties *propertyList) init() {
}
func (properties *propertyList) IsEmpty() bool {
properties.mutex.Lock()
defer properties.mutex.Unlock()
return len(properties.properties) == 0
}
func (properties *propertyList) getRaw(tag PropertyName) any {
properties.mutex.Lock()
defer properties.mutex.Unlock()
if value, ok := properties.properties[tag]; ok {
return value
}
@ -71,11 +78,13 @@ func (properties *propertyList) getRaw(tag PropertyName) any {
}
func (properties *propertyList) setRaw(tag PropertyName, value any) {
properties.mutex.Lock()
if value == nil {
delete(properties.properties, tag)
} else {
properties.properties[tag] = value
}
properties.mutex.Unlock()
}
/*
@ -83,8 +92,11 @@ func (properties *propertyList) setRaw(tag PropertyName, value any) {
properties.remove(properties, properties.normalize(tag))
}
*/
func (properties *propertyList) Clear() {
properties.mutex.Lock()
properties.properties = map[PropertyName]any{}
properties.mutex.Unlock()
}
func (properties *propertyList) All() iter.Seq2[PropertyName, any] {
@ -98,10 +110,13 @@ func (properties *propertyList) All() iter.Seq2[PropertyName, any] {
}
func (properties *propertyList) AllTags() []PropertyName {
properties.mutex.Lock()
tags := make([]PropertyName, 0, len(properties.properties))
for tag := range properties.properties {
tags = append(tags, tag)
}
properties.mutex.Unlock()
slices.Sort(tags)
return tags
}

View File

@ -169,7 +169,7 @@ type Session interface {
styleProperty(styleTag string, propertyTag PropertyName) any
setBridge(events chan DataObject, bridge bridge)
writeInitScript(writer *strings.Builder)
writeInitScript()
callFunc(funcName string, args ...any)
updateInnerHTML(htmlID, html string)
appendToInnerHTML(htmlID, html string)
@ -294,6 +294,9 @@ func (session *sessionData) ID() int {
}
func (session *sessionData) setBridge(events chan DataObject, bridge bridge) {
if session.events != nil {
close(session.events)
}
session.events = events
session.bridge = bridge
}
@ -351,23 +354,39 @@ func (session *sessionData) RootView() View {
return session.rootView
}
func (session *sessionData) writeInitScript(writer *strings.Builder) {
func (session *sessionData) writeInitScript() {
if session.bridge == nil {
return
}
if ProtocolInDebugLog {
DebugLog("Start session:")
}
if css := session.getCurrentTheme().cssText(session); css != "" {
css = strings.ReplaceAll(css, "\n", `\n`)
css = strings.ReplaceAll(css, "\t", `\t`)
writer.WriteString(`document.querySelector('style').textContent += "`)
writer.WriteString(css)
writer.WriteString("\";\n")
script := `document.querySelector('style').textContent += "` + css + "\";\n"
session.bridge.writeScript(script)
if ProtocolInDebugLog {
DebugLog(script)
}
}
if session.rootView != nil {
writer.WriteString(`document.getElementById('ruiRootView').innerHTML = '`)
buffer := allocStringBuilder()
defer freeStringBuilder(buffer)
viewHTML(session.rootView, buffer, "")
text := strings.ReplaceAll(buffer.String(), "'", `\'`)
writer.WriteString(text)
writer.WriteString("';\nscanElementsSize();")
html := buffer.String()
session.bridge.callFunc("updateInnerHTML", "ruiRootView", html)
if ProtocolInDebugLog {
DebugLog(html)
}
}
session.updateTooltipConstants()
@ -666,7 +685,7 @@ func (session *sessionData) handleAnswer(command string, data DataObject) bool {
switch command {
case "answer":
if session.bridge != nil {
session.bridge.answerReceived(data)
go session.bridge.answerReceived(data)
}
case "imageLoaded":
@ -675,6 +694,11 @@ func (session *sessionData) handleAnswer(command string, data DataObject) bool {
case "imageError":
session.imageManager().imageLoadError(data)
case "storageError", "storageValues":
if session.clientStorage != nil {
session.clientStorage.handleEvent(command, data)
}
default:
return false
}
@ -793,6 +817,12 @@ func (session *sessionData) handleSessionInfo(params DataObject) {
func (session *sessionData) handleEvent(command string, data DataObject) {
switch command {
case "start-session":
if session.setContent(session.App().getCreateContentFunc()(session)) {
session.writeInitScript()
session.onStart()
}
case "session-pause":
session.onPause()
@ -824,11 +854,6 @@ func (session *sessionData) handleEvent(command string, data DataObject) {
case "sessionInfo":
session.handleSessionInfo(data)
case "storageError", "storageSuccess", "storageValues":
if session.clientStorage != nil {
session.clientStorage.handleEvent(command, data)
}
default:
if viewID, ok := data.PropertyValue("id"); ok {
if viewID != "body" {
@ -931,7 +956,9 @@ func (session *sessionData) OpenURL(urlStr string) {
}
func (session *sessionData) addToEventsQueue(data DataObject) {
session.events <- data
if session.events != nil {
session.events <- data
}
}
func (session *sessionData) StartTimer(ms int, timerFunc func(Session)) int {

View File

@ -84,7 +84,7 @@ func createSocketBridge(w http.ResponseWriter, req *http.Request) *wsBridge {
func createHttpBridge(req *http.Request) *httpBridge {
bridge := new(httpBridge)
bridge.initBridge()
bridge.response = make(chan string, 10)
bridge.response = make(chan string, 100)
bridge.writeMessage = func(script string) bool {
if script != "" {
if ProtocolInDebugLog {
@ -139,14 +139,31 @@ func (bridge *webBridge) finishUpdateScript(htmlID string) {
func (bridge *webBridge) argToString(arg any) (string, bool) {
switch arg := arg.(type) {
case string:
arg = strings.ReplaceAll(arg, "\\", `\\`)
arg = strings.ReplaceAll(arg, "'", `\'`)
arg = strings.ReplaceAll(arg, "\n", `\n`)
arg = strings.ReplaceAll(arg, "\r", `\r`)
arg = strings.ReplaceAll(arg, "\t", `\t`)
arg = strings.ReplaceAll(arg, "\b", `\b`)
arg = strings.ReplaceAll(arg, "\f", `\f`)
arg = strings.ReplaceAll(arg, "\v", `\v`)
escChars := []struct{ esc, repl string }{
{esc: "\\", repl: `\\`},
{esc: "'", repl: `\'`},
{esc: "\b", repl: `\b`},
{esc: "\t", repl: `\t`},
{esc: "\n", repl: `\n`},
{esc: "\v", repl: `\v`},
{esc: "\r", repl: `\r`},
{esc: "\f", repl: `\f`},
}
for _, s := range escChars {
if strings.Contains(arg, s.esc) {
arg = strings.ReplaceAll(arg, s.esc, s.repl)
}
}
for n := range 0x20 {
esc := string([]rune{rune(n)})
if strings.Contains(arg, esc) {
repl := fmt.Sprintf(`\x%02d`, n)
arg = strings.ReplaceAll(arg, esc, repl)
}
}
return `'` + arg + `'`, true
case rune: