github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/gorilla/websocket/examples/command/main.go (about) 1 // Copyright 2015 The Gorilla WebSocket Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package main 6 7 import ( 8 "bufio" 9 "flag" 10 "io" 11 "log" 12 "net/http" 13 "os" 14 "os/exec" 15 "text/template" 16 "time" 17 18 "github.com/insionng/yougam/libraries/gorilla/websocket" 19 ) 20 21 var ( 22 addr = flag.String("addr", "127.0.0.1:8080", "http service address") 23 cmdPath string 24 homeTempl = template.Must(template.ParseFiles("home.html")) 25 ) 26 27 const ( 28 // Time allowed to write a message to the peer. 29 writeWait = 10 * time.Second 30 31 // Maximum message size allowed from peer. 32 maxMessageSize = 8192 33 34 // Time allowed to read the next pong message from the peer. 35 pongWait = 60 * time.Second 36 37 // Send pings to peer with this period. Must be less than pongWait. 38 pingPeriod = (pongWait * 9) / 10 39 ) 40 41 func pumpStdin(ws *websocket.Conn, w io.Writer) { 42 defer ws.Close() 43 ws.SetReadLimit(maxMessageSize) 44 ws.SetReadDeadline(time.Now().Add(pongWait)) 45 ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil }) 46 for { 47 _, message, err := ws.ReadMessage() 48 if err != nil { 49 break 50 } 51 message = append(message, '\n') 52 if _, err := w.Write(message); err != nil { 53 break 54 } 55 } 56 } 57 58 func pumpStdout(ws *websocket.Conn, r io.Reader, done chan struct{}) { 59 defer func() { 60 ws.Close() 61 close(done) 62 }() 63 s := bufio.NewScanner(r) 64 for s.Scan() { 65 ws.SetWriteDeadline(time.Now().Add(writeWait)) 66 if err := ws.WriteMessage(websocket.TextMessage, s.Bytes()); err != nil { 67 break 68 } 69 } 70 if s.Err() != nil { 71 log.Println("scan:", s.Err()) 72 } 73 } 74 75 func ping(ws *websocket.Conn, done chan struct{}) { 76 ticker := time.NewTicker(pingPeriod) 77 defer ticker.Stop() 78 for { 79 select { 80 case <-ticker.C: 81 if err := ws.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(writeWait)); err != nil { 82 log.Println("ping:", err) 83 } 84 case <-done: 85 return 86 } 87 } 88 } 89 90 func internalError(ws *websocket.Conn, msg string, err error) { 91 log.Println(msg, err) 92 ws.WriteMessage(websocket.TextMessage, []byte("Internal server error.")) 93 } 94 95 var upgrader = websocket.Upgrader{} 96 97 func serveWs(w http.ResponseWriter, r *http.Request) { 98 ws, err := upgrader.Upgrade(w, r, nil) 99 if err != nil { 100 log.Println("upgrade:", err) 101 return 102 } 103 104 defer ws.Close() 105 106 outr, outw, err := os.Pipe() 107 if err != nil { 108 internalError(ws, "stdout:", err) 109 return 110 } 111 defer outr.Close() 112 defer outw.Close() 113 114 inr, inw, err := os.Pipe() 115 if err != nil { 116 internalError(ws, "stdin:", err) 117 return 118 } 119 defer inr.Close() 120 defer inw.Close() 121 122 proc, err := os.StartProcess(cmdPath, flag.Args(), &os.ProcAttr{ 123 Files: []*os.File{inr, outw, outw}, 124 }) 125 if err != nil { 126 internalError(ws, "start:", err) 127 return 128 } 129 130 inr.Close() 131 outw.Close() 132 133 stdoutDone := make(chan struct{}) 134 go pumpStdout(ws, outr, stdoutDone) 135 go ping(ws, stdoutDone) 136 137 pumpStdin(ws, inw) 138 139 // Some commands will exit when stdin is closed. 140 inw.Close() 141 142 // Other commands need a bonk on the head. 143 if err := proc.Signal(os.Interrupt); err != nil { 144 log.Println("inter:", err) 145 } 146 147 select { 148 case <-stdoutDone: 149 case <-time.After(time.Second): 150 // A bigger bonk on the head. 151 if err := proc.Signal(os.Kill); err != nil { 152 log.Println("term:", err) 153 } 154 <-stdoutDone 155 } 156 157 if _, err := proc.Wait(); err != nil { 158 log.Println("wait:", err) 159 } 160 } 161 162 func serveHome(w http.ResponseWriter, r *http.Request) { 163 if r.URL.Path != "/" { 164 http.Error(w, "Not found", 404) 165 return 166 } 167 if r.Method != "GET" { 168 http.Error(w, "Method not allowed", 405) 169 return 170 } 171 w.Header().Set("Content-Type", "text/html; charset=utf-8") 172 homeTempl.Execute(w, r.Host) 173 } 174 175 func main() { 176 flag.Parse() 177 if len(flag.Args()) < 1 { 178 log.Fatal("must specify at least one argument") 179 } 180 var err error 181 cmdPath, err = exec.LookPath(flag.Args()[0]) 182 if err != nil { 183 log.Fatal(err) 184 } 185 http.HandleFunc("/", serveHome) 186 http.HandleFunc("/ws", serveWs) 187 log.Fatal(http.ListenAndServe(*addr, nil)) 188 }