|
|
@@ -0,0 +1,156 @@
|
|
|
+// Copyright 2015 The Gorilla WebSocket Authors. All rights reserved.
|
|
|
+// Use of this source code is governed by a BSD-style
|
|
|
+// license that can be found in the LICENSE file.
|
|
|
+
|
|
|
+package main
|
|
|
+
|
|
|
+import (
|
|
|
+ "bufio"
|
|
|
+ "flag"
|
|
|
+ "io"
|
|
|
+ "log"
|
|
|
+ "net/http"
|
|
|
+ "os"
|
|
|
+ "os/exec"
|
|
|
+ "text/template"
|
|
|
+ "time"
|
|
|
+
|
|
|
+ "github.com/gorilla/websocket"
|
|
|
+)
|
|
|
+
|
|
|
+var (
|
|
|
+ addr = flag.String("addr", "127.0.0.1:8080", "http service address")
|
|
|
+ homeTempl = template.Must(template.ParseFiles("home.html"))
|
|
|
+)
|
|
|
+
|
|
|
+const (
|
|
|
+ // Time allowed to write a message to the peer.
|
|
|
+ writeWait = 10 * time.Second
|
|
|
+
|
|
|
+ // Maximum message size allowed from peer.
|
|
|
+ maxMessageSize = 8192
|
|
|
+)
|
|
|
+
|
|
|
+// connection is an middleman between the websocket connection and the command.
|
|
|
+type connection struct {
|
|
|
+ ws *websocket.Conn
|
|
|
+ stdout io.ReadCloser
|
|
|
+ stdin io.WriteCloser
|
|
|
+ cmd *exec.Cmd
|
|
|
+}
|
|
|
+
|
|
|
+func (c *connection) pumpStdin() {
|
|
|
+ defer c.ws.Close()
|
|
|
+ c.ws.SetReadLimit(maxMessageSize)
|
|
|
+ for {
|
|
|
+ _, message, err := c.ws.ReadMessage()
|
|
|
+ if err != nil {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ message = append(message, '\n')
|
|
|
+ if _, err := c.stdin.Write(message); err != nil {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ c.stdin.Close()
|
|
|
+ log.Println("exit stdin pump")
|
|
|
+}
|
|
|
+
|
|
|
+func (c *connection) pumpStdout() {
|
|
|
+ defer c.ws.Close()
|
|
|
+ s := bufio.NewScanner(c.stdout)
|
|
|
+ for s.Scan() {
|
|
|
+ c.ws.SetWriteDeadline(time.Now().Add(writeWait))
|
|
|
+ if err := c.ws.WriteMessage(websocket.TextMessage, s.Bytes()); err != nil {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if s.Err() != nil {
|
|
|
+ log.Println("scan:", s.Err())
|
|
|
+ }
|
|
|
+ c.stdout.Close()
|
|
|
+ log.Println("exit stdout pump")
|
|
|
+}
|
|
|
+
|
|
|
+func internalError(ws *websocket.Conn, fmt string, err error) {
|
|
|
+ log.Println(fmt, err)
|
|
|
+ ws.WriteMessage(websocket.TextMessage, []byte("Internal server error."))
|
|
|
+}
|
|
|
+
|
|
|
+var upgrader = websocket.Upgrader{}
|
|
|
+
|
|
|
+func serveWs(w http.ResponseWriter, r *http.Request) {
|
|
|
+ if r.Method != "GET" {
|
|
|
+ http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ ws, err := upgrader.Upgrade(w, r, nil)
|
|
|
+ if err != nil {
|
|
|
+ log.Println(err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ c := &connection{
|
|
|
+ cmd: exec.Command(flag.Args()[0], flag.Args()[1:]...),
|
|
|
+ ws: ws,
|
|
|
+ }
|
|
|
+
|
|
|
+ c.stdout, err = c.cmd.StdoutPipe()
|
|
|
+ if err != nil {
|
|
|
+ internalError(ws, "stdout: %v", err)
|
|
|
+ ws.Close()
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ c.stdin, err = c.cmd.StdinPipe()
|
|
|
+ if err != nil {
|
|
|
+ internalError(ws, "stdin: %v", err)
|
|
|
+ c.stdout.Close()
|
|
|
+ if closer, ok := c.cmd.Stdout.(io.Closer); ok {
|
|
|
+ closer.Close()
|
|
|
+ }
|
|
|
+ ws.Close()
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if err := c.cmd.Start(); err != nil {
|
|
|
+ internalError(ws, "start: %v", err)
|
|
|
+ c.stdout.Close()
|
|
|
+ c.stdin.Close()
|
|
|
+ ws.Close()
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ go c.pumpStdout()
|
|
|
+ c.pumpStdin()
|
|
|
+
|
|
|
+ c.cmd.Process.Signal(os.Interrupt)
|
|
|
+ if err := c.cmd.Wait(); err != nil {
|
|
|
+ log.Println("wait:", err)
|
|
|
+ }
|
|
|
+ log.Println("exit serveWs")
|
|
|
+}
|
|
|
+
|
|
|
+func serveHome(w http.ResponseWriter, r *http.Request) {
|
|
|
+ if r.URL.Path != "/" {
|
|
|
+ http.Error(w, "Not found", 404)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if r.Method != "GET" {
|
|
|
+ http.Error(w, "Method not allowed", 405)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
|
|
+ homeTempl.Execute(w, r.Host)
|
|
|
+}
|
|
|
+
|
|
|
+func main() {
|
|
|
+ flag.Parse()
|
|
|
+ if len(flag.Args()) < 1 {
|
|
|
+ log.Fatal("must specify at least one argument")
|
|
|
+ }
|
|
|
+ http.HandleFunc("/", serveHome)
|
|
|
+ http.HandleFunc("/ws", serveWs)
|
|
|
+ log.Fatal(http.ListenAndServe(*addr, nil))
|
|
|
+}
|