func (wp *wsPty) Start() { var err error args := flag.Args() wp.Cmd = exec.Command(cmdFlag, args...) env := os.Environ() env = append(env, "TERM=xterm") wp.Cmd.Env = env wp.Pty, err = pty.Start(wp.Cmd) if err != nil { log.Fatalf("Failed to start command: %s\n", err) } //Set the size of the pty pty.Setsize(wp.Pty, 60, 200) }
func ptyHandler(w http.ResponseWriter, r *http.Request) { conn, err := upgrader.Upgrade(w, r, nil) if err != nil { log.Fatalf("Websocket upgrade failed: %s\n", err) } defer conn.Close() wp := wsPty{} // TODO: check for errors, return 500 on fail wp.Start() // copy everything from the pty master to the websocket // using base64 encoding for now due to limitations in term.js go func() { buf := make([]byte, 8192) reader := bufio.NewReader(wp.Pty) var buffer bytes.Buffer // TODO: more graceful exit on socket close / process exit for { n, err := reader.Read(buf) if err != nil { log.Printf("Failed to read from pty master: %s", err) return } //read byte array as Unicode code points (rune in go) bufferBytes := buffer.Bytes() runeReader := bufio.NewReader(bytes.NewReader(append(bufferBytes[:], buf[:n]...))) buffer.Reset() i := 0 for i < n { char, charLen, e := runeReader.ReadRune() if e != nil { log.Printf("Failed to read from pty master: %s", err) return } if char == utf8.RuneError { runeReader.UnreadRune() break } i += charLen buffer.WriteRune(char) } err = conn.WriteMessage(websocket.TextMessage, buffer.Bytes()) if err != nil { log.Printf("Failed to send UTF8 char: %s", err) return } buffer.Reset() if i < n { buffer.Write(buf[i:n]) } } }() type Message struct { Type string `json:"type"` Data json.RawMessage `json:"data"` } // read from the web socket, copying to the pty master // messages are expected to be text and base64 encoded for { mt, payload, err := conn.ReadMessage() if err != nil { if err != io.EOF { log.Printf("conn.ReadMessage failed: %s\n", err) return } } var msg Message switch mt { case websocket.BinaryMessage: log.Printf("Ignoring binary message: %q\n", payload) case websocket.TextMessage: err := json.Unmarshal(payload, &msg) if err != nil { log.Printf("Invalid message %s\n", err) continue } switch msg.Type { case "resize": var size []float64 err := json.Unmarshal(msg.Data, &size) if err != nil { log.Printf("Invalid resize message: %s\n", err) } else { pty.Setsize(wp.Pty, uint16(size[1]), uint16(size[0])) } case "data": var dat string err := json.Unmarshal(msg.Data, &dat) if err != nil { log.Printf("Invalid data message %s\n", err) } else { wp.Pty.Write([]byte(dat)) } default: log.Printf("Invalid message type %d\n", mt) return } default: log.Printf("Invalid message type %d\n", mt) return } } wp.Stop() }