-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
214 lines (180 loc) · 4.26 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
package main
import (
"bufio"
"fmt"
"github.com/gorilla/websocket"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"os/exec"
"strings"
)
func ServeTouist(w http.ResponseWriter, r *http.Request) {
args := r.PostFormValue("args")
stdin := r.PostFormValue("stdin")
log.Println("Got request with args: ", args)
argsArr := []string{"-"}
argsArr = append(argsArr, strings.Fields(args)...)
cmd := exec.Command("touist", argsArr...)
cmd.Stdin = strings.NewReader(stdin)
cmd.Stdout = w
cmd.Stderr = w
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Content-Type", "text")
w.WriteHeader(http.StatusOK)
err := cmd.Run()
if e, ok := err.(*exec.ExitError); ok {
log.Println("Failed with code ", e.ExitCode())
_, _ = fmt.Fprintln(w, "Exit code", e.ExitCode())
return
}
}
type InputMsg struct {
Args string `json:"args"`
Stdin string `json:"stdin"`
}
type AdditionalInputMsg struct {
Stdin string `json:"stdin"`
}
type OutMsg struct {
Type string `json:"type"`
Msg string `json:"msg"`
}
var upgrader = websocket.Upgrader{
EnableCompression: true,
CheckOrigin: func(r *http.Request) bool {
return true
},
}
func ServeTouistWs(w http.ResponseWriter, r *http.Request) {
c, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println("upgrade err:", err)
return
}
wsclosed := make(chan struct{}, 1)
c.SetCloseHandler(func(code int, text string) error {
wsclosed <- struct{}{}
return nil
})
var inp InputMsg
err = c.ReadJSON(&inp)
if err != nil {
log.Println("Invalid input json: ", err)
return
}
tmpfile, err := ioutil.TempFile("", "example.*.txt")
if err != nil {
log.Println("couldnt open tempfile: ", err)
return
}
name := tmpfile.Name()
defer os.Remove(name)
if _, err := io.WriteString(tmpfile, inp.Stdin); err != nil {
log.Println("coudnt write stdin to tempfile: ", err)
return
}
if err := tmpfile.Close(); err != nil {
log.Println("couldnt close tempfile: ", err)
return
}
log.Println("Got ws request with args: ", inp.Args)
argsArr := []string{name}
argsArr = append(argsArr, strings.Fields(inp.Args)...)
cmd := exec.Command("touist", argsArr...)
cmdWriter, err := cmd.StdinPipe()
if err != nil {
log.Println("Error creating StdinPipe for Cmd: ", err)
return
}
// create a pipe for the output of the script
cmdReader, err := cmd.StdoutPipe()
if err != nil {
log.Println("Error creating StdoutPipe for Cmd: ", err)
return
}
// create a pipe for the output of the script
cmdReaderErr, err := cmd.StderrPipe()
if err != nil {
log.Println("Error creating StderrPipe for Cmd: ", err)
return
}
scanner := bufio.NewScanner(cmdReader)
scannererr := bufio.NewScanner(cmdReaderErr)
err = cmd.Start()
if err != nil {
log.Println("Error starting cmd: ", err)
}
isDone := false
go func() {
_ = cmd.Wait()
isDone = true
}()
defer func() {
log.Println("ws over")
if !isDone {
_ = cmd.Process.Kill()
}
}()
msgdone := make(chan struct{}, 1)
stdoutmsgs := make(chan string, 1)
go func() {
for scanner.Scan() {
stdoutmsgs <- string(scanner.Bytes())
}
msgdone <- struct{}{}
}()
stderrmsgs := make(chan string, 1)
go func() {
for scannererr.Scan() {
stderrmsgs <- string(scannererr.Bytes())
}
}()
inmsgs := make(chan string, 1)
go func() {
var addInput AdditionalInputMsg
err := c.ReadJSON(&addInput)
inmsgs <- addInput.Stdin
for err == nil {
err = c.ReadJSON(&addInput)
inmsgs <- addInput.Stdin
}
}()
for {
select {
case inm := <-inmsgs:
_, err = io.WriteString(cmdWriter, inm)
if err != nil {
log.Println("Error trying to write to process: ", err)
}
case msg := <-stdoutmsgs:
err = c.WriteJSON(OutMsg{Type: "stdout", Msg: msg})
if err != nil {
return
}
case msg := <-stderrmsgs:
err = c.WriteJSON(OutMsg{Type: "stderr", Msg: msg})
if err != nil {
return
}
case <-msgdone:
err = c.Close()
return
case <-wsclosed:
log.Println("Closed early")
return
}
}
}
func ServeHomepage(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "touist_test.html")
}
func main() {
http.HandleFunc("/touist_cmd", ServeTouist)
http.HandleFunc("/index.html", ServeHomepage)
http.HandleFunc("/touist_ws", ServeTouistWs)
log.Println("Touist webservice started!")
log.Fatal(http.ListenAndServe(":7015", nil))
}