#218 and WebSocket bug fix
This commit is contained in:
parent
b758ec854e
commit
a119370a23
9
main.go
9
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()}
|
||||
|
||||
|
|
|
@ -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}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ var notification = {
|
|||
};
|
||||
|
||||
notificationWS.onerror = function (e) {
|
||||
console.log('[notification onerror] ' + JSON.parse(e));
|
||||
console.log('[notification onerror]');
|
||||
};
|
||||
}
|
||||
};
|
|
@ -186,7 +186,7 @@ var session = {
|
|||
$(".notification-count").show();
|
||||
};
|
||||
sessionWS.onerror = function (e) {
|
||||
console.log('[session onerror] ' + JSON.parse(e));
|
||||
console.log('[session onerror]');
|
||||
};
|
||||
}
|
||||
};
|
|
@ -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();
|
||||
|
|
|
@ -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}}',
|
||||
|
|
|
@ -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}},
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue