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  }