(w http.ResponseWriter, r *http.Request)
| 101 | var upgrader = websocket.Upgrader{} |
| 102 | |
| 103 | func serveWs(w http.ResponseWriter, r *http.Request) { |
| 104 | ws, err := upgrader.Upgrade(w, r, nil) |
| 105 | if err != nil { |
| 106 | log.Println("upgrade:", err) |
| 107 | return |
| 108 | } |
| 109 | |
| 110 | defer ws.Close() |
| 111 | |
| 112 | outr, outw, err := os.Pipe() |
| 113 | if err != nil { |
| 114 | internalError(ws, "stdout:", err) |
| 115 | return |
| 116 | } |
| 117 | defer outr.Close() |
| 118 | defer outw.Close() |
| 119 | |
| 120 | inr, inw, err := os.Pipe() |
| 121 | if err != nil { |
| 122 | internalError(ws, "stdin:", err) |
| 123 | return |
| 124 | } |
| 125 | defer inr.Close() |
| 126 | defer inw.Close() |
| 127 | |
| 128 | proc, err := os.StartProcess(cmdPath, flag.Args(), &os.ProcAttr{ |
| 129 | Files: []*os.File{inr, outw, outw}, |
| 130 | }) |
| 131 | if err != nil { |
| 132 | internalError(ws, "start:", err) |
| 133 | return |
| 134 | } |
| 135 | |
| 136 | inr.Close() |
| 137 | outw.Close() |
| 138 | |
| 139 | stdoutDone := make(chan struct{}) |
| 140 | go pumpStdout(ws, outr, stdoutDone) |
| 141 | go ping(ws, stdoutDone) |
| 142 | |
| 143 | pumpStdin(ws, inw) |
| 144 | |
| 145 | // Some commands will exit when stdin is closed. |
| 146 | inw.Close() |
| 147 | |
| 148 | // Other commands need a bonk on the head. |
| 149 | if err := proc.Signal(os.Interrupt); err != nil { |
| 150 | log.Println("inter:", err) |
| 151 | } |
| 152 | |
| 153 | select { |
| 154 | case <-stdoutDone: |
| 155 | case <-time.After(time.Second): |
| 156 | // A bigger bonk on the head. |
| 157 | if err := proc.Signal(os.Kill); err != nil { |
| 158 | log.Println("term:", err) |
| 159 | } |
| 160 | <-stdoutDone |
nothing calls this directly
no test coverage detected