From a119370a23343a0ec1acbe03ce0974a167f4c74b Mon Sep 17 00:00:00 2001 From: Liang Ding Date: Sun, 27 Sep 2015 07:36:34 +0800 Subject: [PATCH] #218 and WebSocket bug fix --- main.go | 9 +- playground/playgrounds.go | 10 +- session/sessions.go | 251 ++++++++++++++++++------------------ shell/shells.go | 9 +- static/js/notification.js | 2 +- static/js/session.js | 2 +- static/js/wide.js | 4 +- views/index.html | 4 +- views/playground/index.html | 2 +- views/shell.html | 2 +- 10 files changed, 142 insertions(+), 153 deletions(-) diff --git a/main.go b/main.go index 1782cc6..5a2880e 100644 --- a/main.go +++ b/main.go @@ -19,13 +19,11 @@ import ( "flag" "html/template" "io" - "math/rand" "mime" "net/http" _ "net/http/pprof" "os" "runtime" - "strconv" "strings" "time" @@ -222,11 +220,6 @@ func indexHandler(w http.ResponseWriter, r *http.Request) { } httpSession.Save(r, w) - // create a Wide session - rand.Seed(time.Now().UnixNano()) - sid := strconv.Itoa(rand.Int()) - wideSession := session.WideSessions.New(httpSession, sid) - user := conf.GetUser(username) if nil == user { logger.Warnf("Not found user [%s]", username) @@ -241,7 +234,7 @@ func indexHandler(w http.ResponseWriter, r *http.Request) { wideSessions := session.WideSessions.GetByUsername(username) model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(locale), "locale": locale, - "session": wideSession, "latestSessionContent": user.LatestSessionContent, + "username": username, "sid": session.WideSessions.GenId(), "latestSessionContent": user.LatestSessionContent, "pathSeparator": conf.PathSeparator, "codeMirrorVer": conf.CodeMirrorVer, "user": user, "editorThemes": conf.GetEditorThemes(), "crossPlatforms": util.Go.GetCrossPlatforms()} diff --git a/playground/playgrounds.go b/playground/playgrounds.go index cd0af41..c2d4f53 100644 --- a/playground/playgrounds.go +++ b/playground/playgrounds.go @@ -54,11 +54,6 @@ func IndexHandler(w http.ResponseWriter, r *http.Request) { username := httpSession.Values["username"].(string) - // create a wide session - rand.Seed(time.Now().UnixNano()) - sid := strconv.Itoa(rand.Int()) - wideSession := session.WideSessions.New(httpSession, sid) - locale := conf.Wide.Locale // try to load file @@ -92,8 +87,9 @@ func IndexHandler(w http.ResponseWriter, r *http.Request) { } model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(locale), "locale": locale, - "session": wideSession, "pathSeparator": conf.PathSeparator, "codeMirrorVer": conf.CodeMirrorVer, - "code": template.HTML(code), "ver": conf.WideVersion, "year": time.Now().Year(), + "sid": session.WideSessions.GenId(), "pathSeparator": conf.PathSeparator, + "codeMirrorVer": conf.CodeMirrorVer, + "code": template.HTML(code), "ver": conf.WideVersion, "year": time.Now().Year(), "embed": embed, "disqus": disqus, "fileName": fileName} wideSessions := session.WideSessions.GetByUsername(username) diff --git a/session/sessions.go b/session/sessions.go index f5132ce..3043723 100644 --- a/session/sessions.go +++ b/session/sessions.go @@ -25,6 +25,7 @@ package session import ( "bytes" "encoding/json" + "math/rand" "net/http" "os" "path/filepath" @@ -193,21 +194,6 @@ func (f userReports) Less(i, j int) bool { return f[i].processCnt > f[j].process // When a channel closed, releases all resources associated with it. func WSHandler(w http.ResponseWriter, r *http.Request) { sid := r.URL.Query()["sid"][0] - wSession := WideSessions.Get(sid) - if nil == wSession { - httpSession, _ := HTTPSession.Get(r, "wide-session") - - if httpSession.IsNew { - return - } - - httpSession.Options.MaxAge = conf.Wide.HTTPSessionMaxAge - httpSession.Save(r, w) - - wSession = WideSessions.New(httpSession, sid) - - logger.Tracef("Created a wide session [%s] for websocket reconnecting, user [%s]", sid, wSession.Username) - } conn, _ := websocket.Upgrade(w, r, nil, 1024, 1024) wsChan := util.WSChannel{Sid: sid, Conn: conn, Request: r, Time: time.Now()} @@ -220,6 +206,22 @@ func WSHandler(w http.ResponseWriter, r *http.Request) { SessionWS[sid] = &wsChan + wSession := WideSessions.Get(sid) + if nil == wSession { + httpSession, _ := HTTPSession.Get(r, "wide-session") + + if httpSession.IsNew { + return + } + + httpSession.Options.MaxAge = conf.Wide.HTTPSessionMaxAge + httpSession.Save(r, w) + + wSession = WideSessions.new(httpSession, sid) + + logger.Tracef("Created a wide session [%s] for websocket reconnecting, user [%s]", sid, wSession.Username) + } + logger.Tracef("Open a new [Session Channel] with session [%s], %d", sid, len(SessionWS)) input := map[string]interface{}{} @@ -297,114 +299,11 @@ func (s *WideSession) Refresh() { s.Updated = time.Now() } -// New creates a wide session. -func (sessions *wSessions) New(httpSession *sessions.Session, sid string) *WideSession { - mutex.Lock() - defer mutex.Unlock() +// GenId generates a wide session id. +func (sessions *wSessions) GenId() string { + rand.Seed(time.Now().UnixNano()) - username := httpSession.Values["username"].(string) - now := time.Now() - - ret := &WideSession{ - ID: sid, - Username: username, - HTTPSession: httpSession, - EventQueue: nil, - State: sessionStateActive, - Content: &conf.LatestSessionContent{}, - Created: now, - Updated: now, - } - - *sessions = append(*sessions, ret) - - if "playground" == username { - return ret - } - - // create user event queue - ret.EventQueue = event.UserEventQueues.New(sid) - - // add a filesystem watcher to notify front-end after the files changed - watcher, err := fsnotify.NewWatcher() - if err != nil { - logger.Error(err) - - return ret - } - - go func() { - defer util.Recover() - - for { - ch := SessionWS[sid] - if nil == ch { - return // release this gorutine - } - - select { - case event := <-watcher.Events: - path := event.Name - dir := filepath.Dir(path) - - ch = SessionWS[sid] - if nil == ch { - return // release this gorutine - } - - logger.Debug(event) - - if event.Op&fsnotify.Create == fsnotify.Create { - if err = watcher.Add(path); nil != err { - logger.Warn(err, path) - } - - logger.Tracef("File watcher added a file [%s]", path) - - cmd := map[string]interface{}{"path": path, "dir": dir, "cmd": "create-file"} - ch.WriteJSON(&cmd) - } else if event.Op&fsnotify.Remove == fsnotify.Remove { - cmd := map[string]interface{}{"path": path, "dir": dir, "cmd": "remove-file"} - ch.WriteJSON(&cmd) - - } else if event.Op&fsnotify.Rename == fsnotify.Rename { - cmd := map[string]interface{}{"path": path, "dir": dir, "cmd": "rename-file"} - ch.WriteJSON(&cmd) - } - case err := <-watcher.Errors: - if nil != err { - logger.Error("File watcher ERROR: ", err) - } - } - } - }() - - go func() { - defer util.Recover() - - workspaces := filepath.SplitList(conf.GetUserWorkspace(username)) - for _, workspace := range workspaces { - filepath.Walk(filepath.Join(workspace, "src"), func(dirPath string, f os.FileInfo, err error) error { - if ".git" == f.Name() { // XXX: discard other unconcered dirs - return filepath.SkipDir - } - - if f.IsDir() { - if err = watcher.Add(dirPath); nil != err { - logger.Error(err, dirPath) - } - - logger.Tracef("File watcher added a dir [%s]", dirPath) - } - - return nil - }) - } - - ret.FileWatcher = watcher - }() - - return ret + return strconv.Itoa(rand.Int()) } // Get gets a wide session with the specified session id. @@ -505,3 +404,111 @@ func (sessions *wSessions) GetByUsername(username string) []*WideSession { return ret } + +// new creates a wide session. +func (sessions *wSessions) new(httpSession *sessions.Session, sid string) *WideSession { + mutex.Lock() + defer mutex.Unlock() + + username := httpSession.Values["username"].(string) + now := time.Now() + + ret := &WideSession{ + ID: sid, + Username: username, + HTTPSession: httpSession, + EventQueue: nil, + State: sessionStateActive, + Content: &conf.LatestSessionContent{}, + Created: now, + Updated: now, + } + + *sessions = append(*sessions, ret) + + if "playground" == username { + return ret + } + + // create user event queue + ret.EventQueue = event.UserEventQueues.New(sid) + + // add a filesystem watcher to notify front-end after the files changed + watcher, err := fsnotify.NewWatcher() + if err != nil { + logger.Error(err) + + return ret + } + + go func() { + defer util.Recover() + + for { + ch := SessionWS[sid] + if nil == ch { + return // release this gorutine + } + + select { + case event := <-watcher.Events: + path := event.Name + dir := filepath.Dir(path) + + ch = SessionWS[sid] + if nil == ch { + return // release this gorutine + } + + logger.Trace(event) + + if event.Op&fsnotify.Create == fsnotify.Create { + if err = watcher.Add(path); nil != err { + logger.Warn(err, path) + } + + cmd := map[string]interface{}{"path": path, "dir": dir, "cmd": "create-file"} + ch.WriteJSON(&cmd) + } else if event.Op&fsnotify.Remove == fsnotify.Remove { + cmd := map[string]interface{}{"path": path, "dir": dir, "cmd": "remove-file"} + ch.WriteJSON(&cmd) + + } else if event.Op&fsnotify.Rename == fsnotify.Rename { + cmd := map[string]interface{}{"path": path, "dir": dir, "cmd": "rename-file"} + ch.WriteJSON(&cmd) + } + case err := <-watcher.Errors: + if nil != err { + logger.Error("File watcher ERROR: ", err) + } + } + } + }() + + go func() { + defer util.Recover() + + workspaces := filepath.SplitList(conf.GetUserWorkspace(username)) + for _, workspace := range workspaces { + filepath.Walk(filepath.Join(workspace, "src"), func(dirPath string, f os.FileInfo, err error) error { + if ".git" == f.Name() { // XXX: discard other unconcered dirs + return filepath.SkipDir + } + + if f.IsDir() { + if err = watcher.Add(dirPath); nil != err { + logger.Error(err, dirPath) + } + + logger.Tracef("File watcher added a dir [%s]", dirPath) + } + + return nil + }) + } + + ret.FileWatcher = watcher + }() + + return ret +} diff --git a/shell/shells.go b/shell/shells.go index 5938152..b1d8dd8 100644 --- a/shell/shells.go +++ b/shell/shells.go @@ -17,12 +17,10 @@ package shell import ( "html/template" - "math/rand" "net/http" "os" "os/exec" "runtime" - "strconv" "strings" "time" @@ -57,16 +55,11 @@ func IndexHandler(w http.ResponseWriter, r *http.Request) { } httpSession.Save(r, w) - // create a wide session - rand.Seed(time.Now().UnixNano()) - sid := strconv.Itoa(rand.Int()) - wideSession := session.WideSessions.New(httpSession, sid) - username := httpSession.Values["username"].(string) locale := conf.GetUser(username).Locale model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(locale), "locale": locale, - "session": wideSession} + "sid": session.WideSessions.GenId()} wideSessions := session.WideSessions.GetByUsername(username) diff --git a/static/js/notification.js b/static/js/notification.js index df5fc43..e595fd8 100644 --- a/static/js/notification.js +++ b/static/js/notification.js @@ -55,7 +55,7 @@ var notification = { }; notificationWS.onerror = function (e) { - console.log('[notification onerror] ' + JSON.parse(e)); + console.log('[notification onerror]'); }; } }; \ No newline at end of file diff --git a/static/js/session.js b/static/js/session.js index 5e08678..5720554 100644 --- a/static/js/session.js +++ b/static/js/session.js @@ -186,7 +186,7 @@ var session = { $(".notification-count").show(); }; sessionWS.onerror = function (e) { - console.log('[session onerror] ' + JSON.parse(e)); + console.log('[session onerror]'); }; } }; \ No newline at end of file diff --git a/static/js/wide.js b/static/js/wide.js index ed1bbd4..48249d1 100644 --- a/static/js/wide.js +++ b/static/js/wide.js @@ -540,7 +540,7 @@ var wide = { console.log('[output onclose] disconnected (' + e.code + ')'); }; outputWS.onerror = function (e) { - console.log('[output onerror] ' + e); + console.log('[output onerror]'); }; }, _initFooter: function () { @@ -812,8 +812,8 @@ $(document).ready(function () { tree.init(); menu.init(); hotkeys.init(); - notification.init(); session.init(); + notification.init(); editors.init(); windows.init(); bottomGroup.init(); diff --git a/views/index.html b/views/index.html index 4b9aed2..ed449da 100644 --- a/views/index.html +++ b/views/index.html @@ -30,7 +30,7 @@ - + @@ -613,7 +613,7 @@ "pathSeparator": '{{.pathSeparator}}', "label": {{.i18n}}, "channel": {{.conf.Channel}}, - "wideSessionId": '{{.session.ID}}', + "wideSessionId": '{{.sid}}', "editorTheme": '{{.user.Editor.Theme}}', "latestSessionContent": {{.latestSessionContent}}, "editorTabSize": '{{.user.Editor.TabSize}}', diff --git a/views/playground/index.html b/views/playground/index.html index f0301e1..b22a86d 100644 --- a/views/playground/index.html +++ b/views/playground/index.html @@ -100,7 +100,7 @@ "server": "{{.conf.Server}}", "staticServer": "{{.conf.StaticServer}}", "channel": "{{.conf.Channel}}", - "wideSessionId": "{{.session.ID}}", + "wideSessionId": "{{.sid}}", "label": {{.i18n}}, "autocomplete": {{.conf.Autocomplete}}, "embed": {{.embed}}, diff --git a/views/shell.html b/views/shell.html index 6d9a3d1..3fd5a77 100644 --- a/views/shell.html +++ b/views/shell.html @@ -24,7 +24,7 @@ channel: { shell: '{{.conf.ShellChannel}}' }, - wideSessionId: {{.session.ID}} + wideSessionId: {{.sid}} };