From c13b585d3a4a7cb0a97ad493bd79911b5c32425b Mon Sep 17 00:00:00 2001 From: Liang Ding Date: Thu, 16 May 2019 17:27:43 +0800 Subject: [PATCH] :art: #318 --- output/run.go | 189 ++++++++++------------------------------------ playground/run.go | 172 ++++++++++------------------------------- 2 files changed, 81 insertions(+), 280 deletions(-) diff --git a/output/run.go b/output/run.go index f034ea2..1949f5b 100644 --- a/output/run.go +++ b/output/run.go @@ -22,24 +22,12 @@ import ( "os/exec" "path/filepath" "strings" - "time" "github.com/b3log/wide/conf" "github.com/b3log/wide/session" "github.com/b3log/wide/util" ) -const ( - outputBufMax = 1024 // 1024 string(rune) - outputTimeout = 100 // 100ms - outputCountMax = 30 // 30 reads -) - -type outputBuf struct { - content string - millisecond int64 -} - // RunHandler handles request of executing a binary file. func RunHandler(w http.ResponseWriter, r *http.Request) { result := util.NewResult() @@ -89,24 +77,13 @@ func RunHandler(w http.ResponseWriter, r *http.Request) { logger.Error(err) result.Succ = false } - wsChannel := session.OutputWS[sid] - channelRet := map[string]interface{}{} - if !result.Succ { - if nil != wsChannel { - channelRet["cmd"] = "run-done" - channelRet["output"] = "" - - err := wsChannel.WriteJSON(&channelRet) - if nil != err { - logger.Warn(err) - return - } - - wsChannel.Refresh() - } + channelRet["cmd"] = "run-done" + channelRet["output"] = "" + wsChannel.WriteJSON(&channelRet) + wsChannel.Refresh() return } @@ -116,146 +93,60 @@ func RunHandler(w http.ResponseWriter, r *http.Request) { // add the process to user's process set Processes.Add(wSession, cmd.Process) + // push once for front-end to get the 'run' state and pid + if nil != wsChannel { + channelRet["cmd"] = "run" + channelRet["output"] = "" + wsChannel.WriteJSON(&channelRet) + wsChannel.Refresh() + } + go func(runningId int) { defer util.Recover() - defer cmd.Wait() logger.Debugf("User [%s, %s] is running [id=%d, file=%s]", wSession.Username, sid, runningId, filePath) - // push once for front-end to get the 'run' state and pid - if nil != wsChannel { - channelRet["cmd"] = "run" - channelRet["output"] = "" - err := wsChannel.WriteJSON(&channelRet) - if nil != err { - logger.Warn(err) - return - } - - wsChannel.Refresh() - } - go func() { defer util.Recover() - buf := outputBuf{} - count := 0 - - for { - wsChannel := session.OutputWS[sid] - if nil == wsChannel { - break - } - - r, _, err := outReader.ReadRune() - count++ - - if nil != err { - // remove the exited process from user's process set - Processes.Remove(wSession, cmd.Process) - - logger.Debugf("User [%s, %s] 's running [id=%d, file=%s] has done [stdout %v], ", - wSession.Username, sid, runningId, filePath, err) - - channelRet["cmd"] = "run-done" - channelRet["output"] = buf.content - err := wsChannel.WriteJSON(&channelRet) - if nil != err { - logger.Warn(err) - break - } - - wsChannel.Refresh() - - break - } - - oneRuneStr := string(r) + outScanner := bufio.NewScanner(outReader) + for outScanner.Scan() { + oneRuneStr := outScanner.Text() oneRuneStr = strings.Replace(oneRuneStr, "<", "<", -1) oneRuneStr = strings.Replace(oneRuneStr, ">", ">", -1) - - buf.content += oneRuneStr - now := time.Now().UnixNano() / int64(time.Millisecond) - if 0 == buf.millisecond { - buf.millisecond = now - } - - flood := count > outputCountMax - - if "\n" == oneRuneStr && !flood { - channelRet["cmd"] = "run" - channelRet["output"] = buf.content - buf = outputBuf{} // a new buffer - count = 0 // clear count - err = wsChannel.WriteJSON(&channelRet) - if nil != err { - logger.Warn(err) - break - } - - wsChannel.Refresh() - - continue - } - - if now-outputTimeout >= buf.millisecond || len(buf.content) > outputBufMax { - channelRet["cmd"] = "run" - channelRet["output"] = buf.content - buf = outputBuf{} // a new buffer - count = 0 // clear count - err = wsChannel.WriteJSON(&channelRet) - if nil != err { - logger.Warn(err) - break - } - - wsChannel.Refresh() - - continue - } + channelRet["cmd"] = "run" + channelRet["output"] = oneRuneStr + "\n" + wsChannel := session.OutputWS[sid] + wsChannel.WriteJSON(&channelRet) + wsChannel.Refresh() } }() - buf := outputBuf{} - for { - r, _, err := errReader.ReadRune() - - wsChannel := session.OutputWS[sid] - if nil != err || nil == wsChannel { - break - } - - oneRuneStr := string(r) + errScanner := bufio.NewScanner(errReader) + for errScanner.Scan() { + oneRuneStr := errScanner.Text() oneRuneStr = strings.Replace(oneRuneStr, "<", "<", -1) oneRuneStr = strings.Replace(oneRuneStr, ">", ">", -1) - - buf.content += oneRuneStr - now := time.Now().UnixNano() / int64(time.Millisecond) - if 0 == buf.millisecond { - buf.millisecond = now - } - - if now-outputTimeout >= buf.millisecond || len(buf.content) > outputBufMax || oneRuneStr == "\n" { - channelRet["cmd"] = "run" - channelRet["output"] = "" + buf.content + "" - buf = outputBuf{} // a new buffer - err = wsChannel.WriteJSON(&channelRet) - if nil != err { - logger.Warn(err) - break - } - - wsChannel.Refresh() - } - } - - cmd.Wait() - if 124 == cmd.ProcessState.ExitCode() { - channelRet["cmd"] = "run-done" - channelRet["output"] = "run program timeout in 5s\n" + channelRet["cmd"] = "run" + channelRet["output"] = "" + oneRuneStr + "" + wsChannel := session.OutputWS[sid] wsChannel.WriteJSON(&channelRet) wsChannel.Refresh() } + + cmd.Wait() + + // remove the exited process from user's process set + Processes.Remove(wSession, cmd.Process) + + channelRet["cmd"] = "run-done" + if 124 == cmd.ProcessState.ExitCode() { + channelRet["output"] = "run program timeout in 5s\n" + } else { + channelRet["output"] = "\nrun program complete\n" + } + wsChannel.WriteJSON(&channelRet) + wsChannel.Refresh() }(rand.Int()) } diff --git a/playground/run.go b/playground/run.go index 9b092ad..acba401 100644 --- a/playground/run.go +++ b/playground/run.go @@ -21,7 +21,7 @@ import ( "net/http" "os/exec" "path/filepath" - "time" + "strings" "github.com/b3log/wide/conf" "github.com/b3log/wide/output" @@ -29,17 +29,6 @@ import ( "github.com/b3log/wide/util" ) -const ( - outputBufMax = 1024 // 1024 string(rune) - outputTimeout = 100 // 100ms - outputCountMax = 30 // 30 reads -) - -type outputBuf struct { - content string - millisecond int64 -} - // RunHandler handles request of executing a binary file. func RunHandler(w http.ResponseWriter, r *http.Request) { result := util.NewResult() @@ -116,139 +105,60 @@ func RunHandler(w http.ResponseWriter, r *http.Request) { // add the process to user's process set output.Processes.Add(wSession, cmd.Process) + // push once for front-end to get the 'run' state and pid + if nil != wsChannel { + channelRet["cmd"] = "run" + channelRet["output"] = "" + wsChannel.WriteJSON(&channelRet) + wsChannel.Refresh() + } + go func(runningId int) { defer util.Recover() - defer cmd.Wait() logger.Debugf("User [%s, %s] is running [id=%d, file=%s]", wSession.Username, sid, runningId, filePath) - // push once for front-end to get the 'run' state and pid - if nil != wsChannel { - channelRet["cmd"] = "run" - channelRet["output"] = "" - err := wsChannel.WriteJSON(&channelRet) - if nil != err { - logger.Warn(err) - return - } - - wsChannel.Refresh() - } - go func() { defer util.Recover() - buf := outputBuf{} - count := 0 - - for { - wsChannel := session.PlaygroundWS[sid] - if nil == wsChannel { - break - } - - r, _, err := outReader.ReadRune() - count++ - - if nil != err { - // remove the exited process from user process set - output.Processes.Remove(wSession, cmd.Process) - - logger.Debugf("User [%s, %s] 's running [id=%d, file=%s] has done [stdout %v], ", wSession.Username, sid, runningId, filePath, err) - - channelRet["cmd"] = "run-done" - channelRet["output"] = buf.content - err := wsChannel.WriteJSON(&channelRet) - if nil != err { - logger.Warn(err) - break - } - - wsChannel.Refresh() - - break - } - - oneRuneStr := string(r) - buf.content += oneRuneStr - now := time.Now().UnixNano() / int64(time.Millisecond) - if 0 == buf.millisecond { - buf.millisecond = now - } - - flood := count > outputCountMax - - if "\n" == oneRuneStr && !flood { - channelRet["cmd"] = "run" - channelRet["output"] = buf.content - buf = outputBuf{} // a new buffer - count = 0 // clear count - err = wsChannel.WriteJSON(&channelRet) - if nil != err { - logger.Warn(err) - break - } - - wsChannel.Refresh() - - continue - } - - if now-outputTimeout >= buf.millisecond || len(buf.content) > outputBufMax { - channelRet["cmd"] = "run" - channelRet["output"] = buf.content - buf = outputBuf{} // a new buffer - count = 0 // clear count - err = wsChannel.WriteJSON(&channelRet) - if nil != err { - logger.Warn(err) - break - } - - wsChannel.Refresh() - - continue - } + outScanner := bufio.NewScanner(outReader) + for outScanner.Scan() { + oneRuneStr := outScanner.Text() + oneRuneStr = strings.Replace(oneRuneStr, "<", "<", -1) + oneRuneStr = strings.Replace(oneRuneStr, ">", ">", -1) + channelRet["cmd"] = "run" + channelRet["output"] = oneRuneStr + "\n" + wsChannel := session.OutputWS[sid] + wsChannel.WriteJSON(&channelRet) + wsChannel.Refresh() } }() - buf := outputBuf{} - for { - r, _, err := errReader.ReadRune() - - wsChannel := session.PlaygroundWS[sid] - if nil != err || nil == wsChannel { - break - } - - oneRuneStr := string(r) - buf.content += oneRuneStr - now := time.Now().UnixNano() / int64(time.Millisecond) - if 0 == buf.millisecond { - buf.millisecond = now - } - - if now-outputTimeout >= buf.millisecond || len(buf.content) > outputBufMax || oneRuneStr == "\n" { - channelRet["cmd"] = "run" - channelRet["output"] = buf.content - buf = outputBuf{} // a new buffer - err = wsChannel.WriteJSON(&channelRet) - if nil != err { - logger.Warn(err) - break - } - - wsChannel.Refresh() - } - } - - cmd.Wait() - if 124 == cmd.ProcessState.ExitCode() { - channelRet["cmd"] = "run-done" - channelRet["output"] = "run program timeout in 5s\n" + errScanner := bufio.NewScanner(errReader) + for errScanner.Scan() { + oneRuneStr := errScanner.Text() + oneRuneStr = strings.Replace(oneRuneStr, "<", "<", -1) + oneRuneStr = strings.Replace(oneRuneStr, ">", ">", -1) + channelRet["cmd"] = "run" + channelRet["output"] = "" + oneRuneStr + "" + wsChannel := session.OutputWS[sid] wsChannel.WriteJSON(&channelRet) wsChannel.Refresh() } + + cmd.Wait() + + // remove the exited process from user's process set + output.Processes.Remove(wSession, cmd.Process) + + channelRet["cmd"] = "run-done" + if 124 == cmd.ProcessState.ExitCode() { + channelRet["output"] = "run program timeout in 5s\n" + } else { + channelRet["output"] = "\nrun program complete\n" + } + wsChannel.WriteJSON(&channelRet) + wsChannel.Refresh() }(rand.Int()) }