Fix #119
This commit is contained in:
parent
7ec8a77005
commit
cfd5367919
|
@ -1,4 +1,4 @@
|
|||
// 编辑器操作.
|
||||
// Editor manipulations.
|
||||
package editor
|
||||
|
||||
import (
|
||||
|
@ -11,6 +11,7 @@ import (
|
|||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/b3log/wide/conf"
|
||||
"github.com/b3log/wide/file"
|
||||
|
@ -20,23 +21,24 @@ import (
|
|||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
var editorWS = map[string]*websocket.Conn{}
|
||||
|
||||
// 建立编辑器通道.
|
||||
// WSHandler handles request of creating editor channel.
|
||||
func WSHandler(w http.ResponseWriter, r *http.Request) {
|
||||
session, _ := session.HTTPSession.Get(r, "wide-session")
|
||||
sid := session.Values["id"].(string)
|
||||
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
||||
sid := httpSession.Values["id"].(string)
|
||||
|
||||
editorWS[sid], _ = websocket.Upgrade(w, r, nil, 1024, 1024)
|
||||
conn, _ := websocket.Upgrade(w, r, nil, 1024, 1024)
|
||||
editorChan := util.WSChannel{Sid: sid, Conn: conn, Request: r, Time: time.Now()}
|
||||
|
||||
session.EditorWS[sid] = &editorChan
|
||||
|
||||
ret := map[string]interface{}{"output": "Editor initialized", "cmd": "init-editor"}
|
||||
editorWS[sid].WriteJSON(&ret)
|
||||
editorChan.Conn.WriteJSON(&ret)
|
||||
|
||||
glog.Infof("Open a new [Editor] with session [%s], %d", sid, len(editorWS))
|
||||
glog.Infof("Open a new [Editor] with session [%s], %d", sid, len(session.EditorWS))
|
||||
|
||||
args := map[string]interface{}{}
|
||||
for {
|
||||
if err := editorWS[sid].ReadJSON(&args); err != nil {
|
||||
if err := session.EditorWS[sid].Conn.ReadJSON(&args); err != nil {
|
||||
if err.Error() == "EOF" {
|
||||
return
|
||||
}
|
||||
|
@ -73,14 +75,14 @@ func WSHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
ret = map[string]interface{}{"output": string(output.Bytes()), "cmd": "autocomplete"}
|
||||
|
||||
if err := editorWS[sid].WriteJSON(&ret); err != nil {
|
||||
if err := session.EditorWS[sid].Conn.WriteJSON(&ret); err != nil {
|
||||
glog.Error("Editor WS ERROR: " + err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 自动完成(代码补全).
|
||||
// AutocompleteHandler handles request of code autocompletion.
|
||||
func AutocompleteHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var args map[string]interface{}
|
||||
|
||||
|
@ -133,7 +135,7 @@ func AutocompleteHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
glog.V(5).Infof("gocode set lib-path %s", libPath)
|
||||
|
||||
// FIXME: 使用 gocode set lib-path 在多工作空间环境下肯定是有问题的,需要考虑其他实现方式
|
||||
// FIXME: using gocode set lib-path has some issues while accrossing workspaces
|
||||
gocode := conf.Wide.GetExecutableInGOBIN("gocode")
|
||||
argv := []string{"set", "lib-path", libPath}
|
||||
exec.Command(gocode, argv...).Run()
|
||||
|
@ -157,7 +159,7 @@ func AutocompleteHandler(w http.ResponseWriter, r *http.Request) {
|
|||
w.Write(output)
|
||||
}
|
||||
|
||||
// 查看表达式信息.
|
||||
// GetExprInfoHandler handles request of getting expression infomation.
|
||||
func GetExprInfoHandler(w http.ResponseWriter, r *http.Request) {
|
||||
data := map[string]interface{}{"succ": true}
|
||||
defer util.RetJSON(w, r, data)
|
||||
|
@ -203,7 +205,6 @@ func GetExprInfoHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// glog.Infof("offset [%d]", offset)
|
||||
|
||||
// TODO: 目前是调用 liteide_stub 工具来查找声明,后续需要重新实现
|
||||
ide_stub := conf.Wide.GetExecutableInGOBIN("ide_stub")
|
||||
argv := []string{"type", "-cursor", filename + ":" + strconv.Itoa(offset), "-info", "."}
|
||||
cmd := exec.Command(ide_stub, argv...)
|
||||
|
@ -229,7 +230,7 @@ func GetExprInfoHandler(w http.ResponseWriter, r *http.Request) {
|
|||
data["info"] = exprInfo
|
||||
}
|
||||
|
||||
// 查找声明.
|
||||
// FindDeclarationHandler handles request of finding declaration.
|
||||
func FindDeclarationHandler(w http.ResponseWriter, r *http.Request) {
|
||||
data := map[string]interface{}{"succ": true}
|
||||
defer util.RetJSON(w, r, data)
|
||||
|
@ -275,7 +276,6 @@ func FindDeclarationHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// glog.Infof("offset [%d]", offset)
|
||||
|
||||
// TODO: 目前是调用 liteide_stub 工具来查找声明,后续需要重新实现
|
||||
ide_stub := conf.Wide.GetExecutableInGOBIN("ide_stub")
|
||||
argv := []string{"type", "-cursor", filename + ":" + strconv.Itoa(offset), "-def", "."}
|
||||
cmd := exec.Command(ide_stub, argv...)
|
||||
|
@ -309,7 +309,7 @@ func FindDeclarationHandler(w http.ResponseWriter, r *http.Request) {
|
|||
data["cursorCh"] = cursorCh
|
||||
}
|
||||
|
||||
// 查找使用.
|
||||
// FindUsagesHandler handles request of finding usages.
|
||||
func FindUsagesHandler(w http.ResponseWriter, r *http.Request) {
|
||||
data := map[string]interface{}{"succ": true}
|
||||
defer util.RetJSON(w, r, data)
|
||||
|
@ -355,7 +355,6 @@ func FindUsagesHandler(w http.ResponseWriter, r *http.Request) {
|
|||
offset := getCursorOffset(code, line, ch)
|
||||
// glog.Infof("offset [%d]", offset)
|
||||
|
||||
// TODO: 目前是调用 liteide_stub 工具来查找使用,后续需要重新实现
|
||||
ide_stub := conf.Wide.GetExecutableInGOBIN("ide_stub")
|
||||
argv := []string{"type", "-cursor", filename + ":" + strconv.Itoa(offset), "-use", "."}
|
||||
cmd := exec.Command(ide_stub, argv...)
|
||||
|
@ -396,18 +395,19 @@ func FindUsagesHandler(w http.ResponseWriter, r *http.Request) {
|
|||
data["founds"] = usages
|
||||
}
|
||||
|
||||
// 计算光标偏移位置.
|
||||
// getCursorOffset calculates the cursor offset.
|
||||
//
|
||||
// line 指定了行号(第一行为 0),ch 指定了列号(第一列为 0).
|
||||
// line is the line number, starts with 0 that means the first line
|
||||
// ch is the column number, starts with 0 that means the first column
|
||||
func getCursorOffset(code string, line, ch int) (offset int) {
|
||||
lines := strings.Split(code, "\n")
|
||||
|
||||
// 计算前几行长度
|
||||
// calculate sum length of lines before
|
||||
for i := 0; i < line; i++ {
|
||||
offset += len(lines[i])
|
||||
}
|
||||
|
||||
// 计算当前行、当前列长度
|
||||
// calculate length of the current line and column
|
||||
curLine := lines[line]
|
||||
var buffer bytes.Buffer
|
||||
r := []rune(curLine)
|
||||
|
@ -415,8 +415,8 @@ func getCursorOffset(code string, line, ch int) (offset int) {
|
|||
buffer.WriteString(string(r[i]))
|
||||
}
|
||||
|
||||
offset += line // 加换行符
|
||||
offset += len(buffer.String()) // 加当前行列偏移
|
||||
offset += len(buffer.String()) // append length of current line
|
||||
offset += line // append number of '\n'
|
||||
|
||||
return offset
|
||||
}
|
||||
|
|
|
@ -15,8 +15,9 @@ import (
|
|||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// 格式化 Go 源码文件.
|
||||
// 根据用户的 GoFormat 配置选择格式化工具:
|
||||
// GoFmtHandler handles request of formatting Go source code.
|
||||
//
|
||||
// This function will select a format tooll based on user's configuration:
|
||||
// 1. gofmt
|
||||
// 2. goimports
|
||||
func GoFmtHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -38,8 +39,8 @@ func GoFmtHandler(w http.ResponseWriter, r *http.Request) {
|
|||
filePath := args["file"].(string)
|
||||
|
||||
apiPath := runtime.GOROOT() + conf.PathSeparator + "src" + conf.PathSeparator + "pkg"
|
||||
if strings.HasPrefix(filePath, apiPath) { // 如果是 Go API 源码文件
|
||||
// 忽略修改
|
||||
if strings.HasPrefix(filePath, apiPath) { // if it is Go API source code
|
||||
// ignore it
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -88,8 +89,8 @@ func GoFmtHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
// 格式化 HTML 文件.
|
||||
// FIXME:依赖的工具 gohtml 格式化 HTML 时有问题
|
||||
// HTMLFmtHandler handles request of formatting HTML source code.
|
||||
// FIXME: gohtml has some issues...
|
||||
func HTMLFmtHandler(w http.ResponseWriter, r *http.Request) {
|
||||
data := map[string]interface{}{"succ": true}
|
||||
defer util.RetJSON(w, r, data)
|
||||
|
@ -143,68 +144,3 @@ func HTMLFmtHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化 JSON 文件.
|
||||
func JSONFmtHandler(w http.ResponseWriter, r *http.Request) {
|
||||
data := map[string]interface{}{"succ": true}
|
||||
defer util.RetJSON(w, r, data)
|
||||
|
||||
var args map[string]interface{}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
|
||||
glog.Error(err)
|
||||
data["succ"] = false
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
filePath := args["file"].(string)
|
||||
|
||||
fout, err := os.Create(filePath)
|
||||
|
||||
if nil != err {
|
||||
glog.Error(err)
|
||||
data["succ"] = false
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
code := args["code"].(string)
|
||||
|
||||
fout.WriteString(code)
|
||||
if err := fout.Close(); nil != err {
|
||||
glog.Error(err)
|
||||
data["succ"] = false
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
obj := new(interface{})
|
||||
if err := json.Unmarshal([]byte(code), &obj); nil != err {
|
||||
glog.Error(err)
|
||||
data["succ"] = false
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
glog.Info(obj)
|
||||
|
||||
bytes, err := json.MarshalIndent(obj, "", " ")
|
||||
if nil != err {
|
||||
data["succ"] = false
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
code = string(bytes)
|
||||
data["code"] = code
|
||||
|
||||
fout, err = os.Create(filePath)
|
||||
fout.WriteString(code)
|
||||
if err := fout.Close(); nil != err {
|
||||
glog.Error(err)
|
||||
data["succ"] = false
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,53 +1,52 @@
|
|||
// 事件处理.
|
||||
// Event manipulations.
|
||||
package event
|
||||
|
||||
import "github.com/golang/glog"
|
||||
|
||||
const (
|
||||
EvtCodeGOPATHNotFound = iota // 事件代码:找不到环境变量 $GOPATH
|
||||
EvtCodeGOROOTNotFound // 事件代码:找不到环境变量 $GOROOT
|
||||
EvtCodeGocodeNotFound // 事件代码:找不到 gocode
|
||||
EvtCodeIDEStubNotFound // 事件代码:找不到 IDE stub
|
||||
EvtCodeServerInternalError // 事件代码:服务器内部错误
|
||||
EvtCodeGOPATHNotFound = iota // event code: not found $GOPATH env variable
|
||||
EvtCodeGOROOTNotFound // event code: not found $GOROOT env variable
|
||||
EvtCodeGocodeNotFound // event code: not found gocode
|
||||
EvtCodeIDEStubNotFound // event code: not found ide_stub
|
||||
EvtCodeServerInternalError // event code: server internal error
|
||||
)
|
||||
|
||||
// 事件队列最大长度.
|
||||
// Max length of queue.
|
||||
const MaxQueueLength = 10
|
||||
|
||||
// 事件结构.
|
||||
// Event.
|
||||
type Event struct {
|
||||
Code int `json:"code"` // 事件代码
|
||||
Sid string `json:"sid"` // 用户会话 id
|
||||
Data interface{} `json:"data"` // 事件数据
|
||||
Code int `json:"code"` // event code
|
||||
Sid string `json:"sid"` // wide session id related
|
||||
Data interface{} `json:"data"` // event data
|
||||
}
|
||||
|
||||
// 全局事件队列.
|
||||
// Global event queue.
|
||||
//
|
||||
// 入队的事件将分发到每个用户的事件队列中.
|
||||
// Every event in this queue will be dispatched to each user event queue.
|
||||
var EventQueue = make(chan *Event, MaxQueueLength)
|
||||
|
||||
// 用户事件队列.
|
||||
// User event queue.
|
||||
type UserEventQueue struct {
|
||||
Sid string // 关联的会话 id
|
||||
Queue chan *Event // 队列
|
||||
Handlers []Handler // 事件处理器集
|
||||
Sid string // wide session id related
|
||||
Queue chan *Event // queue
|
||||
Handlers []Handler // event handlers
|
||||
}
|
||||
|
||||
// 事件队列集类型.
|
||||
type Queues map[string]*UserEventQueue
|
||||
|
||||
// 用户事件队列集.
|
||||
// User event queues.
|
||||
//
|
||||
// <sid, *UserEventQueue>
|
||||
var UserEventQueues = Queues{}
|
||||
|
||||
// 加载事件处理.
|
||||
// Load initializes the event handling.
|
||||
func Load() {
|
||||
go func() {
|
||||
for event := range EventQueue {
|
||||
glog.V(5).Infof("收到全局事件 [%d]", event.Code)
|
||||
glog.V(5).Infof("Received a global event [code=%d]", event.Code)
|
||||
|
||||
// 将事件分发到每个用户的事件队列里
|
||||
// dispatch the event to each user event queue
|
||||
for _, userQueue := range UserEventQueues {
|
||||
event.Sid = userQueue.Sid
|
||||
|
||||
|
@ -57,14 +56,14 @@ func Load() {
|
|||
}()
|
||||
}
|
||||
|
||||
// 为用户队列添加事件处理器.
|
||||
// AddHandler adds the specified handlers to user event queues.
|
||||
func (uq *UserEventQueue) AddHandler(handlers ...Handler) {
|
||||
for _, handler := range handlers {
|
||||
uq.Handlers = append(uq.Handlers, handler)
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化一个用户事件队列.
|
||||
// New initializes a user event queue with the specified wide session id.
|
||||
func (ueqs Queues) New(sid string) *UserEventQueue {
|
||||
q := ueqs[sid]
|
||||
if nil != q {
|
||||
|
@ -80,11 +79,11 @@ func (ueqs Queues) New(sid string) *UserEventQueue {
|
|||
|
||||
ueqs[sid] = q
|
||||
|
||||
go func() { // 队列开始监听事件
|
||||
go func() { // start listening
|
||||
for evt := range q.Queue {
|
||||
glog.V(5).Infof("Session [%s] received a event [%d]", sid, evt.Code)
|
||||
|
||||
// 将事件交给事件处理器进行处理
|
||||
// process event by each handlers
|
||||
for _, handler := range q.Handlers {
|
||||
handler.Handle(evt)
|
||||
}
|
||||
|
@ -94,7 +93,7 @@ func (ueqs Queues) New(sid string) *UserEventQueue {
|
|||
return q
|
||||
}
|
||||
|
||||
// 关闭一个用户事件队列.
|
||||
// Close closes a user event queue with the specified wide session id.
|
||||
func (ueqs Queues) Close(sid string) {
|
||||
q := ueqs[sid]
|
||||
if nil == q {
|
||||
|
@ -104,15 +103,15 @@ func (ueqs Queues) Close(sid string) {
|
|||
delete(ueqs, sid)
|
||||
}
|
||||
|
||||
// 事件处理接口.
|
||||
// Type of event handler.
|
||||
type Handler interface {
|
||||
Handle(event *Event)
|
||||
}
|
||||
|
||||
// 函数指针包装.
|
||||
// Type of handler function.
|
||||
type HandleFunc func(event *Event)
|
||||
|
||||
// 事件处理默认实现.
|
||||
// Default implementation of event handling.
|
||||
func (fn HandleFunc) Handle(event *Event) {
|
||||
fn(event)
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ type FileNode struct {
|
|||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
IconSkin string `json:"iconSkin"` // Value should be end with a space
|
||||
Type string `json:"type"` // "f": file,"d": directory
|
||||
Type string `json:"type"` // "f": file, "d": directory
|
||||
Mode string `json:"mode"`
|
||||
FileNodes []*FileNode `json:"children"`
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// 国际化操作.
|
||||
// Internationalization manipulations.
|
||||
package i18n
|
||||
|
||||
import (
|
||||
|
@ -10,16 +10,17 @@ import (
|
|||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// Locale.
|
||||
type locale struct {
|
||||
Name string
|
||||
Langs map[string]interface{}
|
||||
TimeZone string
|
||||
}
|
||||
|
||||
// 所有的 locales.
|
||||
// All locales.
|
||||
var Locales = map[string]locale{}
|
||||
|
||||
// 加载国际化配置.
|
||||
// Load loads i18n message configurations.
|
||||
func Load() {
|
||||
f, _ := os.Open("i18n")
|
||||
names, _ := f.Readdirnames(-1)
|
||||
|
@ -57,12 +58,12 @@ func load(localeStr string) {
|
|||
glog.V(5).Infof("Loaded [%s] locale configuration", localeStr)
|
||||
}
|
||||
|
||||
// 获取语言配置项.
|
||||
// Get gets message with the specified locale and key.
|
||||
func Get(locale, key string) interface{} {
|
||||
return Locales[locale].Langs[key]
|
||||
}
|
||||
|
||||
// 获取语言配置.
|
||||
// GetAll gets all messages with the specified locale.
|
||||
func GetAll(locale string) map[string]interface{} {
|
||||
return Locales[locale].Langs
|
||||
}
|
||||
|
|
1
main.go
1
main.go
|
@ -309,7 +309,6 @@ func main() {
|
|||
http.HandleFunc("/find/decl", handlerWrapper(editor.FindDeclarationHandler))
|
||||
http.HandleFunc("/find/usages", handlerWrapper(editor.FindUsagesHandler))
|
||||
http.HandleFunc("/html/fmt", handlerWrapper(editor.HTMLFmtHandler))
|
||||
http.HandleFunc("/json/fmt", handlerWrapper(editor.JSONFmtHandler))
|
||||
|
||||
// shell
|
||||
http.HandleFunc("/shell/ws", handlerWrapper(shell.WSHandler))
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// 通知.
|
||||
// Notification manipulations.
|
||||
package notification
|
||||
|
||||
import (
|
||||
|
@ -16,15 +16,15 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
Error = "ERROR" // 通知.严重程度:ERROR
|
||||
Warn = "WARN" // 通知.严重程度:WARN
|
||||
Info = "INFO" // 通知.严重程度:INFO
|
||||
Error = "ERROR" // notification.severity: ERROR
|
||||
Warn = "WARN" // notification.severity: WARN
|
||||
Info = "INFO" // notification.severity: INFO
|
||||
|
||||
Setup = "Setup" // 通知.类型:安装
|
||||
Server = "Server" // 通知.类型:服务器
|
||||
Setup = "Setup" // notification.type: setup
|
||||
Server = "Server" // notification.type: server
|
||||
)
|
||||
|
||||
// 通知结构.
|
||||
// Notification.
|
||||
type Notification struct {
|
||||
event *event.Event
|
||||
Type string `json:"type"`
|
||||
|
@ -32,9 +32,9 @@ type Notification struct {
|
|||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// 用户事件处理:将事件转为通知,并通过通知通道推送给前端.
|
||||
// event2Notification processes user event by converting the specified event to a notification, and then push it to front
|
||||
// browser with notification channel.
|
||||
//
|
||||
// 当用户事件队列接收到事件时将会调用该函数进行处理.
|
||||
func event2Notification(e *event.Event) {
|
||||
if nil == session.NotificationWS[e.Sid] {
|
||||
return
|
||||
|
@ -71,7 +71,7 @@ func event2Notification(e *event.Event) {
|
|||
wsChannel.Refresh()
|
||||
}
|
||||
|
||||
// 建立通知通道.
|
||||
// WSHandler handles request of creating notification channel.
|
||||
func WSHandler(w http.ResponseWriter, r *http.Request) {
|
||||
sid := r.URL.Query()["sid"][0]
|
||||
|
||||
|
@ -89,7 +89,7 @@ func WSHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
glog.V(4).Infof("Open a new [Notification] with session [%s], %d", sid, len(session.NotificationWS))
|
||||
|
||||
// 添加用户事件处理器
|
||||
// add user event handler
|
||||
wSession.EventQueue.AddHandler(event.HandleFunc(event2Notification))
|
||||
|
||||
input := map[string]interface{}{}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// 构建、运行、go tool 操作.
|
||||
// Build, run and go tool manipulations.
|
||||
package output
|
||||
|
||||
import (
|
||||
|
@ -25,11 +25,11 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
lintSeverityError = "error" // Lint 严重级别:错误
|
||||
lintSeverityWarn = "warning" // Lint 严重级别:警告
|
||||
lintSeverityError = "error" // lint severity: error
|
||||
lintSeverityWarn = "warning" // lint severity: warning
|
||||
)
|
||||
|
||||
// 代码 Lint 结构.
|
||||
// Code lint.
|
||||
type Lint struct {
|
||||
File string `json:"file"`
|
||||
LineNo int `json:"lineNo"`
|
||||
|
@ -37,7 +37,7 @@ type Lint struct {
|
|||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
// 建立输出通道.
|
||||
// WSHandler handles request of creating output channel.
|
||||
func WSHandler(w http.ResponseWriter, r *http.Request) {
|
||||
sid := r.URL.Query()["sid"][0]
|
||||
|
||||
|
@ -52,7 +52,7 @@ func WSHandler(w http.ResponseWriter, r *http.Request) {
|
|||
glog.V(4).Infof("Open a new [Output] with session [%s], %d", sid, len(session.OutputWS))
|
||||
}
|
||||
|
||||
// 运行一个可执行文件.
|
||||
// RunHandler handles request of executing a binary file.
|
||||
func RunHandler(w http.ResponseWriter, r *http.Request) {
|
||||
data := map[string]interface{}{"succ": true}
|
||||
defer util.RetJSON(w, r, data)
|
||||
|
@ -105,7 +105,7 @@ func RunHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
// 添加到用户进程集中
|
||||
// add the process to user's process set
|
||||
processes.add(wSession, cmd.Process)
|
||||
|
||||
channelRet := map[string]interface{}{}
|
||||
|
@ -117,7 +117,7 @@ func RunHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
glog.V(3).Infof("Session [%s] is running [id=%d, file=%s]", sid, runningId, filePath)
|
||||
|
||||
// 在读取程序输出前先返回一次,使前端获取到 run 状态与 pid
|
||||
// push once for front-end to get the 'run' state and pid
|
||||
if nil != session.OutputWS[sid] {
|
||||
wsChannel := session.OutputWS[sid]
|
||||
|
||||
|
@ -129,15 +129,14 @@ func RunHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
// 更新通道最近使用时间
|
||||
wsChannel.Time = time.Now()
|
||||
wsChannel.Refresh()
|
||||
}
|
||||
|
||||
for {
|
||||
buf, err := reader.ReadBytes('\n')
|
||||
|
||||
if nil != err || 0 == len(buf) {
|
||||
// 从用户进程集中移除这个执行完毕(或是被主动停止)的进程
|
||||
// remove the exited process from user process set
|
||||
processes.remove(wSession, cmd.Process)
|
||||
|
||||
glog.V(3).Infof("Session [%s] 's running [id=%d, file=%s] has done", sid, runningId, filePath)
|
||||
|
@ -153,8 +152,7 @@ func RunHandler(w http.ResponseWriter, r *http.Request) {
|
|||
break
|
||||
}
|
||||
|
||||
// 更新通道最近使用时间
|
||||
wsChannel.Time = time.Now()
|
||||
wsChannel.Refresh()
|
||||
}
|
||||
|
||||
break
|
||||
|
@ -170,15 +168,14 @@ func RunHandler(w http.ResponseWriter, r *http.Request) {
|
|||
break
|
||||
}
|
||||
|
||||
// 更新通道最近使用时间
|
||||
wsChannel.Time = time.Now()
|
||||
wsChannel.Refresh()
|
||||
}
|
||||
}
|
||||
}
|
||||
}(rand.Int())
|
||||
}
|
||||
|
||||
// 构建可执行文件.
|
||||
// BuildHandler handles request of building.
|
||||
func BuildHandler(w http.ResponseWriter, r *http.Request) {
|
||||
data := map[string]interface{}{"succ": true}
|
||||
defer util.RetJSON(w, r, data)
|
||||
|
@ -237,7 +234,7 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
executable = filepath.Join(curDir, executable)
|
||||
|
||||
// 先把可执行文件删了
|
||||
// remove executable file before building
|
||||
err = os.RemoveAll(executable)
|
||||
if nil != err {
|
||||
glog.Info(err)
|
||||
|
@ -269,7 +266,7 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
|
|||
channelRet := map[string]interface{}{}
|
||||
|
||||
if nil != session.OutputWS[sid] {
|
||||
// 在前端 output 中显示“开始构建”
|
||||
// display "START [go build]" in front-end browser
|
||||
|
||||
channelRet["output"] = "<span class='start-build'>" + i18n.Get(locale, "start-build").(string) + "</span>\n"
|
||||
channelRet["cmd"] = "start-build"
|
||||
|
@ -282,8 +279,7 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
// 更新通道最近使用时间
|
||||
wsChannel.Time = time.Now()
|
||||
wsChannel.Refresh()
|
||||
}
|
||||
|
||||
reader := bufio.NewReader(io.MultiReader(stdout, stderr))
|
||||
|
@ -301,19 +297,18 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
glog.V(3).Infof("Session [%s] is building [id=%d, dir=%s]", sid, runningId, curDir)
|
||||
|
||||
// 一次性读取
|
||||
// read all
|
||||
buf, _ := ioutil.ReadAll(reader)
|
||||
|
||||
channelRet := map[string]interface{}{}
|
||||
channelRet["cmd"] = "build"
|
||||
channelRet["executable"] = executable
|
||||
|
||||
if 0 == len(buf) { // 说明构建成功,没有错误信息输出
|
||||
// 设置下一次执行命令(前端会根据该参数发送请求)
|
||||
if 0 == len(buf) { // build success
|
||||
channelRet["nextCmd"] = args["nextCmd"]
|
||||
channelRet["output"] = "<span class='build-succ'>" + i18n.Get(locale, "build-succ").(string) + "</span>\n"
|
||||
|
||||
go func() { // 运行 go install,生成的库用于 gocode lib-path
|
||||
go func() { // go install, for subsequent gocode lib-path
|
||||
cmd := exec.Command("go", "install")
|
||||
cmd.Dir = curDir
|
||||
|
||||
|
@ -324,15 +319,16 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
|
|||
glog.Warning(string(out))
|
||||
}
|
||||
}()
|
||||
} else { // 构建失败
|
||||
// 解析错误信息,返回给编辑器 gutter lint
|
||||
} else { // build error
|
||||
// build gutter lint
|
||||
|
||||
errOut := string(buf)
|
||||
channelRet["output"] = "<span class='build-error'>" + i18n.Get(locale, "build-error").(string) + "</span>\n" + errOut
|
||||
|
||||
lines := strings.Split(errOut, "\n")
|
||||
|
||||
if lines[0][0] == '#' {
|
||||
lines = lines[1:] // 跳过第一行
|
||||
lines = lines[1:] // skip the first line
|
||||
}
|
||||
|
||||
lints := []*Lint{}
|
||||
|
@ -343,7 +339,7 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
if line[0] == '\t' {
|
||||
// 添加到上一个 lint 中
|
||||
// append to the last lint
|
||||
last := len(lints)
|
||||
msg := lints[last-1].Msg
|
||||
msg += line
|
||||
|
@ -385,14 +381,13 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
|
|||
glog.Error(err)
|
||||
}
|
||||
|
||||
// 更新通道最近使用时间
|
||||
wsChannel.Time = time.Now()
|
||||
wsChannel.Refresh()
|
||||
}
|
||||
|
||||
}(rand.Int())
|
||||
}
|
||||
|
||||
// go test.
|
||||
// GoTestHandler handles request of go test.
|
||||
func GoTestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
data := map[string]interface{}{"succ": true}
|
||||
defer util.RetJSON(w, r, data)
|
||||
|
@ -443,7 +438,7 @@ func GoTestHandler(w http.ResponseWriter, r *http.Request) {
|
|||
channelRet := map[string]interface{}{}
|
||||
|
||||
if nil != session.OutputWS[sid] {
|
||||
// 在前端 output 中显示“开始 go test
|
||||
// display "START [go test]" in front-end browser
|
||||
|
||||
channelRet["output"] = "<span class='start-test'>" + i18n.Get(locale, "start-test").(string) + "</span>\n"
|
||||
channelRet["cmd"] = "start-test"
|
||||
|
@ -456,8 +451,7 @@ func GoTestHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
// 更新通道最近使用时间
|
||||
wsChannel.Time = time.Now()
|
||||
wsChannel.Refresh()
|
||||
}
|
||||
|
||||
reader := bufio.NewReader(io.MultiReader(stdout, stderr))
|
||||
|
@ -477,10 +471,10 @@ func GoTestHandler(w http.ResponseWriter, r *http.Request) {
|
|||
channelRet := map[string]interface{}{}
|
||||
channelRet["cmd"] = "go test"
|
||||
|
||||
// 一次性读取
|
||||
// read all
|
||||
buf, _ := ioutil.ReadAll(reader)
|
||||
|
||||
// 同步点,等待 go test 执行完成
|
||||
// waiting for go test finished
|
||||
cmd.Wait()
|
||||
|
||||
if !cmd.ProcessState.Success() {
|
||||
|
@ -501,13 +495,12 @@ func GoTestHandler(w http.ResponseWriter, r *http.Request) {
|
|||
glog.Error(err)
|
||||
}
|
||||
|
||||
// 更新通道最近使用时间
|
||||
wsChannel.Time = time.Now()
|
||||
wsChannel.Refresh()
|
||||
}
|
||||
}(rand.Int())
|
||||
}
|
||||
|
||||
// go install.
|
||||
// GoInstallHandler handles request of go install.
|
||||
func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
|
||||
data := map[string]interface{}{"succ": true}
|
||||
defer util.RetJSON(w, r, data)
|
||||
|
@ -560,7 +553,7 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
|
|||
channelRet := map[string]interface{}{}
|
||||
|
||||
if nil != session.OutputWS[sid] {
|
||||
// 在前端 output 中显示“开始 go install”
|
||||
// display "START [go install]" in front-end browser
|
||||
|
||||
channelRet["output"] = "<span class='start-install'>" + i18n.Get(locale, "start-install").(string) + "</span>\n"
|
||||
channelRet["cmd"] = "start-install"
|
||||
|
@ -573,8 +566,7 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
// 更新通道最近使用时间
|
||||
wsChannel.Time = time.Now()
|
||||
wsChannel.Refresh()
|
||||
}
|
||||
|
||||
reader := bufio.NewReader(io.MultiReader(stdout, stderr))
|
||||
|
@ -592,19 +584,20 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
glog.V(3).Infof("Session [%s] is running [go install] [id=%d, dir=%s]", sid, runningId, curDir)
|
||||
|
||||
// 一次性读取
|
||||
// read all
|
||||
buf, _ := ioutil.ReadAll(reader)
|
||||
|
||||
channelRet := map[string]interface{}{}
|
||||
channelRet["cmd"] = "go install"
|
||||
|
||||
if 0 != len(buf) { // 构建失败
|
||||
// 解析错误信息,返回给编辑器 gutter lint
|
||||
if 0 != len(buf) { // build error
|
||||
// build gutter lint
|
||||
|
||||
errOut := string(buf)
|
||||
lines := strings.Split(errOut, "\n")
|
||||
|
||||
if lines[0][0] == '#' {
|
||||
lines = lines[1:] // 跳过第一行
|
||||
lines = lines[1:] // skip the first line
|
||||
}
|
||||
|
||||
lints := []*Lint{}
|
||||
|
@ -615,7 +608,7 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
if line[0] == '\t' {
|
||||
// 添加到上一个 lint 中
|
||||
// append to the last lint
|
||||
last := len(lints)
|
||||
msg := lints[last-1].Msg
|
||||
msg += line
|
||||
|
@ -661,14 +654,13 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
|
|||
glog.Error(err)
|
||||
}
|
||||
|
||||
// 更新通道最近使用时间
|
||||
wsChannel.Time = time.Now()
|
||||
wsChannel.Refresh()
|
||||
}
|
||||
|
||||
}(rand.Int())
|
||||
}
|
||||
|
||||
// go get.
|
||||
// GoGetHandler handles request of go get.
|
||||
func GoGetHandler(w http.ResponseWriter, r *http.Request) {
|
||||
data := map[string]interface{}{"succ": true}
|
||||
defer util.RetJSON(w, r, data)
|
||||
|
@ -719,7 +711,7 @@ func GoGetHandler(w http.ResponseWriter, r *http.Request) {
|
|||
channelRet := map[string]interface{}{}
|
||||
|
||||
if nil != session.OutputWS[sid] {
|
||||
// 在前端 output 中显示“开始 go get
|
||||
// display "START [go get]" in front-end browser
|
||||
|
||||
channelRet["output"] = "<span class='start-get'>" + i18n.Get(locale, "start-get").(string) + "</span>\n"
|
||||
channelRet["cmd"] = "start-get"
|
||||
|
@ -732,8 +724,7 @@ func GoGetHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
// 更新通道最近使用时间
|
||||
wsChannel.Time = time.Now()
|
||||
wsChannel.Refresh()
|
||||
}
|
||||
|
||||
reader := bufio.NewReader(io.MultiReader(stdout, stderr))
|
||||
|
@ -754,7 +745,7 @@ func GoGetHandler(w http.ResponseWriter, r *http.Request) {
|
|||
channelRet := map[string]interface{}{}
|
||||
channelRet["cmd"] = "go get"
|
||||
|
||||
// 一次性读取
|
||||
// read all
|
||||
buf, _ := ioutil.ReadAll(reader)
|
||||
|
||||
if 0 != len(buf) {
|
||||
|
@ -776,13 +767,12 @@ func GoGetHandler(w http.ResponseWriter, r *http.Request) {
|
|||
glog.Error(err)
|
||||
}
|
||||
|
||||
// 更新通道最近使用时间
|
||||
wsChannel.Time = time.Now()
|
||||
wsChannel.Refresh()
|
||||
}
|
||||
}(rand.Int())
|
||||
}
|
||||
|
||||
// 结束正在运行的进程.
|
||||
// StopHandler handles request of stoping a running process.
|
||||
func StopHandler(w http.ResponseWriter, r *http.Request) {
|
||||
data := map[string]interface{}{"succ": true}
|
||||
defer util.RetJSON(w, r, data)
|
||||
|
|
|
@ -8,18 +8,18 @@ import (
|
|||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// 进程集类型.
|
||||
// Type of process set.
|
||||
type procs map[string][]*os.Process
|
||||
|
||||
// 所有用户正在运行的程序进程集.
|
||||
// Processse of all users.
|
||||
//
|
||||
// <sid, []*os.Process>
|
||||
var processes = procs{}
|
||||
|
||||
// 排它锁,防止并发修改.
|
||||
// Exclusive lock.
|
||||
var mutex sync.Mutex
|
||||
|
||||
// 添加用户执行进程.
|
||||
// add adds the specified process to the user process set.
|
||||
func (procs *procs) add(wSession *session.WideSession, proc *os.Process) {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
@ -30,13 +30,13 @@ func (procs *procs) add(wSession *session.WideSession, proc *os.Process) {
|
|||
userProcesses = append(userProcesses, proc)
|
||||
(*procs)[sid] = userProcesses
|
||||
|
||||
// 会话关联进程
|
||||
// bind process with wide session
|
||||
wSession.SetProcesses(userProcesses)
|
||||
|
||||
glog.V(3).Infof("Session [%s] has [%d] processes", sid, len((*procs)[sid]))
|
||||
}
|
||||
|
||||
// 移除用户执行进程.
|
||||
// remove removes the specified process from the user process set.
|
||||
func (procs *procs) remove(wSession *session.WideSession, proc *os.Process) {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
@ -48,10 +48,10 @@ func (procs *procs) remove(wSession *session.WideSession, proc *os.Process) {
|
|||
var newProcesses []*os.Process
|
||||
for i, p := range userProcesses {
|
||||
if p.Pid == proc.Pid {
|
||||
newProcesses = append(userProcesses[:i], userProcesses[i+1:]...)
|
||||
newProcesses = append(userProcesses[:i], userProcesses[i+1:]...) // remove it
|
||||
(*procs)[sid] = newProcesses
|
||||
|
||||
// 会话关联进程
|
||||
// bind process with wide session
|
||||
wSession.SetProcesses(newProcesses)
|
||||
|
||||
glog.V(3).Infof("Session [%s] has [%d] processes", sid, len((*procs)[sid]))
|
||||
|
@ -61,7 +61,7 @@ func (procs *procs) remove(wSession *session.WideSession, proc *os.Process) {
|
|||
}
|
||||
}
|
||||
|
||||
// 结束用户正在执行的进程.
|
||||
// kill kills a process specified by the given pid.
|
||||
func (procs *procs) kill(wSession *session.WideSession, pid int) {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
@ -80,7 +80,7 @@ func (procs *procs) kill(wSession *session.WideSession, pid int) {
|
|||
newProcesses = append(userProcesses[:i], userProcesses[i+1:]...)
|
||||
(*procs)[sid] = newProcesses
|
||||
|
||||
// 会话关联进程
|
||||
// bind process with wide session
|
||||
wSession.SetProcesses(newProcesses)
|
||||
|
||||
glog.V(3).Infof("Killed a process [pid=%d] of session [%s]", pid, sid)
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// 会话操作.
|
||||
// Session manipulations.
|
||||
//
|
||||
// Wide 服务器端需要维护两种会话:
|
||||
// Wide server side needs maintain two kinds of sessions:
|
||||
//
|
||||
// 1. HTTP 会话:主要用于验证登录
|
||||
// 2. Wide 会话:浏览器 tab 打开/刷新会创建一个,并和 HTTP 会话进行关联
|
||||
// 1. HTTP session: mainly used for login authentication
|
||||
// 2. Wide session: browser tab open/refresh will create one, and associates with HTTP session
|
||||
//
|
||||
// 当会话失效时:释放所有和该会话相关的资源,例如运行中的程序进程、事件队列等.
|
||||
// When a session gone: release all resources associated with it, such as running processes, event queues.
|
||||
package session
|
||||
|
||||
import (
|
||||
|
@ -26,52 +26,55 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
SessionStateActive = iota // 会话状态:活的
|
||||
SessionStateClosed // 会话状态:已关闭(这个状态目前暂时没有使用到)
|
||||
SessionStateActive = iota // session state: active
|
||||
SessionStateClosed // session state: closed (not used so far)
|
||||
)
|
||||
|
||||
var (
|
||||
// 会话通道. <sid, *util.WSChannel>
|
||||
// Session channels. <sid, *util.WSChannel>
|
||||
SessionWS = map[string]*util.WSChannel{}
|
||||
|
||||
// 输出通道. <sid, *util.WSChannel>
|
||||
// Editor channels. <sid, *util.WSChannel>
|
||||
EditorWS = map[string]*util.WSChannel{}
|
||||
|
||||
// Output channels. <sid, *util.WSChannel>
|
||||
OutputWS = map[string]*util.WSChannel{}
|
||||
|
||||
// 通知通道. <sid, *util.WSChannel>
|
||||
// Notification channels. <sid, *util.WSChannel>
|
||||
NotificationWS = map[string]*util.WSChannel{}
|
||||
)
|
||||
|
||||
// 用户 HTTP 会话,用于验证登录.
|
||||
// HTTP session store.
|
||||
var HTTPSession = sessions.NewCookieStore([]byte("BEYOND"))
|
||||
|
||||
// Wide 会话,对应一个浏览器 tab.
|
||||
// Wide session, associated with a browser tab.
|
||||
type WideSession struct {
|
||||
Id string // 唯一标识
|
||||
Username string // 用户名
|
||||
HTTPSession *sessions.Session // 关联的 HTTP 会话
|
||||
Processes []*os.Process // 关联的进程集
|
||||
EventQueue *event.UserEventQueue // 关联的事件队列
|
||||
State int // 状态
|
||||
Content *conf.LatestSessionContent // 最近一次会话内容
|
||||
Created time.Time // 创建时间
|
||||
Updated time.Time // 最近一次使用时间
|
||||
Id string // id
|
||||
Username string // username
|
||||
HTTPSession *sessions.Session // HTTP session related
|
||||
Processes []*os.Process // process set
|
||||
EventQueue *event.UserEventQueue // event queue
|
||||
State int // state
|
||||
Content *conf.LatestSessionContent // the latest session content
|
||||
Created time.Time // create time
|
||||
Updated time.Time // the latest use time
|
||||
}
|
||||
|
||||
// 会话集类型.
|
||||
// Type of wide sessions.
|
||||
type Sessions []*WideSession
|
||||
|
||||
// 所有 Wide 会话集.
|
||||
// Wide sessions.
|
||||
var WideSessions Sessions
|
||||
|
||||
// 排它锁,防止并发修改.
|
||||
// Exclusive lock.
|
||||
var mutex sync.Mutex
|
||||
|
||||
// 在一些特殊情况(例如浏览器不间断刷新/在源代码视图刷新)下 Wide 会话集内会出现无效会话,该函数定时(1 小时)检查并移除这些无效会话.
|
||||
// In some special cases (such as a browser uninterrupted refresh / refresh in the source code view) will occur
|
||||
// some invalid sessions, the function checks and removes these invalid sessions periodically (1 hour).
|
||||
//
|
||||
// 无效会话:在检查时间内 30 分钟都没有使用过的会话,参考 WideSession.Updated 字段.
|
||||
// Invalid sessions: sessions that not used within 30 minutes, refers to WideSession.Updated field.
|
||||
func FixedTimeRelease() {
|
||||
go func() {
|
||||
// 1 小时进行一次检查
|
||||
for _ = range time.Tick(time.Hour) {
|
||||
hour, _ := time.ParseDuration("-30m")
|
||||
threshold := time.Now().Add(hour)
|
||||
|
@ -87,7 +90,9 @@ func FixedTimeRelease() {
|
|||
}()
|
||||
}
|
||||
|
||||
// 建立会话通道. 通道断开时销毁会话状态,回收相关资源.
|
||||
// WSHandler handles request of creating session channel.
|
||||
//
|
||||
// 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)
|
||||
|
@ -129,7 +134,7 @@ func WSHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
// 会话内容保存.
|
||||
// SaveContent handles request of session content storing.
|
||||
func SaveContent(w http.ResponseWriter, r *http.Request) {
|
||||
data := map[string]interface{}{"succ": true}
|
||||
defer util.RetJSON(w, r, data)
|
||||
|
@ -157,7 +162,7 @@ func SaveContent(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
for _, user := range conf.Wide.Users {
|
||||
if user.Name == wSession.Username {
|
||||
// 更新配置(内存变量),conf.FixedTimeSave() 会负责定时持久化
|
||||
// update the variable in-memory, conf.FixedTimeSave() function will persist it periodically
|
||||
user.LatestSessionContent = wSession.Content
|
||||
|
||||
wSession.Refresh()
|
||||
|
@ -167,19 +172,19 @@ func SaveContent(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
// 设置会话关联的进程集.
|
||||
// SetProcesses binds process set with the wide session.
|
||||
func (s *WideSession) SetProcesses(ps []*os.Process) {
|
||||
s.Processes = ps
|
||||
|
||||
s.Refresh()
|
||||
}
|
||||
|
||||
// 刷新会话最近一次使用时间.
|
||||
// Refresh refreshes the channel by updating its use time.
|
||||
func (s *WideSession) Refresh() {
|
||||
s.Updated = time.Now()
|
||||
}
|
||||
|
||||
// 创建一个 Wide 会话.
|
||||
// New creates a wide session.
|
||||
func (sessions *Sessions) New(httpSession *sessions.Session) *WideSession {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
@ -189,7 +194,7 @@ func (sessions *Sessions) New(httpSession *sessions.Session) *WideSession {
|
|||
id := strconv.Itoa(rand.Int())
|
||||
now := time.Now()
|
||||
|
||||
// 创建用户事件队列
|
||||
// create user event queue
|
||||
userEventQueue := event.UserEventQueues.New(id)
|
||||
|
||||
ret := &WideSession{
|
||||
|
@ -208,7 +213,7 @@ func (sessions *Sessions) New(httpSession *sessions.Session) *WideSession {
|
|||
return ret
|
||||
}
|
||||
|
||||
// 获取 Wide 会话.
|
||||
// Get gets a wide session with the specified session id.
|
||||
func (sessions *Sessions) Get(sid string) *WideSession {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
@ -222,26 +227,26 @@ func (sessions *Sessions) Get(sid string) *WideSession {
|
|||
return nil
|
||||
}
|
||||
|
||||
// 移除 Wide 会话,释放相关资源.
|
||||
// Remove removes a wide session specified with the given session id, releases resources associated with it.
|
||||
//
|
||||
// 会话相关资源:
|
||||
// Session-related resources:
|
||||
//
|
||||
// 1. 用户事件队列
|
||||
// 2. 运行中的进程集
|
||||
// 3. WebSocket 通道
|
||||
// 1. user event queue
|
||||
// 2. process set
|
||||
// 3. websocket channels
|
||||
func (sessions *Sessions) Remove(sid string) {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
for i, s := range *sessions {
|
||||
if s.Id == sid {
|
||||
// 从会话集中移除
|
||||
// remove from session set
|
||||
*sessions = append((*sessions)[:i], (*sessions)[i+1:]...)
|
||||
|
||||
// 关闭用户事件队列
|
||||
// close user event queue
|
||||
event.UserEventQueues.Close(sid)
|
||||
|
||||
// 杀进程
|
||||
// kill processes
|
||||
for _, p := range s.Processes {
|
||||
if err := p.Kill(); nil != err {
|
||||
glog.Errorf("Can't kill process [%d] of session [%s]", p.Pid, sid)
|
||||
|
@ -250,7 +255,7 @@ func (sessions *Sessions) Remove(sid string) {
|
|||
}
|
||||
}
|
||||
|
||||
// 回收所有通道
|
||||
// close websocket channels
|
||||
if ws, ok := OutputWS[sid]; ok {
|
||||
ws.Close()
|
||||
delete(OutputWS, sid)
|
||||
|
@ -268,12 +273,13 @@ func (sessions *Sessions) Remove(sid string) {
|
|||
|
||||
glog.V(3).Infof("Removed a session [%s]", s.Id)
|
||||
|
||||
cnt := 0 // 统计当前 HTTP 会话关联的 Wide 会话数量
|
||||
cnt := 0 // count wide sessions associated with HTTP session
|
||||
for _, s := range *sessions {
|
||||
if s.HTTPSession.ID == s.HTTPSession.ID {
|
||||
cnt++
|
||||
}
|
||||
}
|
||||
|
||||
glog.V(3).Infof("User [%s] has [%d] sessions", s.Username, cnt)
|
||||
|
||||
return
|
||||
|
@ -281,7 +287,7 @@ func (sessions *Sessions) Remove(sid string) {
|
|||
}
|
||||
}
|
||||
|
||||
// 获取 username 指定的用户的所有 Wide 会话.
|
||||
// GetByUsername gets wide sessions.
|
||||
func (sessions *Sessions) GetByUsername(username string) []*WideSession {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package session
|
||||
|
||||
// TODO: 这个文件内的功能目前没有使用,只是开了个头 :p
|
||||
// TODO: this file not used currently, just a beginning :p
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
|
|
@ -18,12 +18,12 @@ import (
|
|||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
// Shell 通道.
|
||||
// Shell channel.
|
||||
//
|
||||
// <sid, *util.WSChannel>>
|
||||
var ShellWS = map[string]*util.WSChannel{}
|
||||
|
||||
// Shell 首页.
|
||||
// IndexHandler handles request of Shell index.
|
||||
func IndexHandler(w http.ResponseWriter, r *http.Request) {
|
||||
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
||||
|
||||
|
@ -36,7 +36,7 @@ func IndexHandler(w http.ResponseWriter, r *http.Request) {
|
|||
httpSession.Options.MaxAge = conf.Wide.HTTPSessionMaxAge
|
||||
httpSession.Save(r, w)
|
||||
|
||||
// 创建一个 Wide 会话
|
||||
// create a wide session
|
||||
wideSession := session.WideSessions.New(httpSession)
|
||||
|
||||
username := httpSession.Values["username"].(string)
|
||||
|
@ -61,7 +61,7 @@ func IndexHandler(w http.ResponseWriter, r *http.Request) {
|
|||
t.Execute(w, model)
|
||||
}
|
||||
|
||||
// 建立 Shell 通道.
|
||||
// WSHandler handles request of creating Shell channel.
|
||||
func WSHandler(w http.ResponseWriter, r *http.Request) {
|
||||
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
||||
username := httpSession.Values["username"].(string)
|
||||
|
@ -122,12 +122,10 @@ func WSHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
// 更新通道最近使用时间
|
||||
wsChan.Time = time.Now()
|
||||
wsChan.Refresh()
|
||||
}
|
||||
}
|
||||
|
||||
// 以管道方式执行多个命令.
|
||||
func pipeCommands(username string, commands ...*exec.Cmd) string {
|
||||
for i, command := range commands[:len(commands)-1] {
|
||||
setCmdEnv(command, username)
|
||||
|
@ -147,7 +145,7 @@ func pipeCommands(username string, commands ...*exec.Cmd) string {
|
|||
|
||||
out, err := last.CombinedOutput()
|
||||
|
||||
// 结束进程,释放资源
|
||||
// release resources
|
||||
for _, command := range commands[:len(commands)-1] {
|
||||
command.Wait()
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// 工具.
|
||||
// Utilities.
|
||||
package util
|
||||
|
||||
import (
|
||||
|
@ -8,10 +8,10 @@ import (
|
|||
|
||||
type mynet struct{}
|
||||
|
||||
// 网络工具.
|
||||
// Network utilities.
|
||||
var Net = mynet{}
|
||||
|
||||
// 获取第一块网卡的 IP 地址.
|
||||
// LocalIP gets the first NIC's IP address.
|
||||
func (*mynet) LocalIP() (string, error) {
|
||||
tt, err := net.Interfaces()
|
||||
|
||||
|
|
|
@ -9,15 +9,15 @@ import (
|
|||
|
||||
type myos struct{}
|
||||
|
||||
// 操作系统工具.
|
||||
// OS utilities.
|
||||
var OS = myos{}
|
||||
|
||||
// 判断是否是 Windows 操作系统.
|
||||
// IsWindows determines whether current OS is Windows.
|
||||
func (*myos) IsWindows() bool {
|
||||
return "windows" == runtime.GOOS
|
||||
}
|
||||
|
||||
// 获取当前执行程序的工作目录的绝对路径.
|
||||
// Pwd gets the path of current working directory.
|
||||
func (*myos) Pwd() string {
|
||||
file, _ := exec.LookPath(os.Args[0])
|
||||
pwd, _ := filepath.Abs(file)
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// panic 恢复.
|
||||
// Recover recovers a panic.
|
||||
func Recover() {
|
||||
if re := recover(); re != nil {
|
||||
glog.Errorf("PANIC RECOVERED:\n %v, %s", re, debug.Stack())
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// HTTP 返回 JSON 统一处理.
|
||||
// RetJSON writes HTTP response with "Content-Type, application/json".
|
||||
func RetJSON(w http.ResponseWriter, r *http.Request, res map[string]interface{}) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
|
|
|
@ -7,20 +7,20 @@ import (
|
|||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
// 一个用户会话的 WebSocket 通道结构.
|
||||
// WebSocket channel.
|
||||
type WSChannel struct {
|
||||
Sid string // 用户会话 id
|
||||
Conn *websocket.Conn // WebSocket 连接
|
||||
Request *http.Request // 关联的 HTTP 请求
|
||||
Time time.Time // 该通道最近一次使用时间
|
||||
Sid string // wide session id
|
||||
Conn *websocket.Conn // websocket connection
|
||||
Request *http.Request // HTTP request related
|
||||
Time time.Time // the latest use time
|
||||
}
|
||||
|
||||
// 关闭通道.
|
||||
// Close closed the channel.
|
||||
func (c *WSChannel) Close() {
|
||||
c.Conn.Close()
|
||||
}
|
||||
|
||||
// Refresh refreshes the channel by updating its Time.
|
||||
// Refresh refreshes the channel by updating its use time.
|
||||
func (c *WSChannel) Refresh() {
|
||||
c.Time = time.Now()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue