#218 and WebSocket bug fix

This commit is contained in:
Liang Ding 2015-09-27 07:36:34 +08:00
parent b758ec854e
commit a119370a23
10 changed files with 142 additions and 153 deletions

View File

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

View File

@ -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,7 +87,8 @@ 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,
"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}

View File

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

View File

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

View File

@ -55,7 +55,7 @@ var notification = {
};
notificationWS.onerror = function (e) {
console.log('[notification onerror] ' + JSON.parse(e));
console.log('[notification onerror]');
};
}
};

View File

@ -186,7 +186,7 @@ var session = {
$(".notification-count").show();
};
sessionWS.onerror = function (e) {
console.log('[session onerror] ' + JSON.parse(e));
console.log('[session onerror]');
};
}
};

View File

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

View File

@ -30,7 +30,7 @@
<link id="themesLink" rel="stylesheet" href="{{.conf.StaticServer}}/static/css/themes/{{.user.Theme}}.css?{{.conf.StaticResourceVersion}}">
<link rel="stylesheet" href="{{.conf.StaticServer}}/static/user/{{.session.Username}}/style.css?{{.conf.StaticResourceVersion}}">
<link rel="stylesheet" href="{{.conf.StaticServer}}/static/user/{{.username}}/style.css?{{.conf.StaticResourceVersion}}">
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
</head>
@ -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}}',

View File

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

View File

@ -24,7 +24,7 @@
channel: {
shell: '{{.conf.ShellChannel}}'
},
wideSessionId: {{.session.ID}}
wideSessionId: {{.sid}}
};</script>
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/jquery-2.1.1.min.js"></script>
<script type="text/javascript" src="{{.conf.StaticServer}}/static/js/lib/reconnecting-websocket.js"></script>