├── .gitignore ├── README.md └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | wsc 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wsc 2 | 3 | A simplistic tool for sending and receiving websocket messages from a command line. 4 | Mainly useful to test websocket servers. 5 | 6 | Getting started: 7 | ``` 8 | $ go get github.com/raphael/wsc 9 | $ wsc -o http://websocket.org -H "Sample-Header-1: foo" -H "Sample-Header-2: bar" -u ws://echo.websocket.org 10 | 2016/03/08 22:51:51 connecting to ws://echo.websocket.org... 11 | 2016/03/08 22:51:52 ready, exit with CTRL+C. 12 | foo 13 | >> foo 14 | << foo 15 | ^C 16 | exiting 17 | ``` 18 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "log" 8 | "net/http" 9 | "net/url" 10 | "os" 11 | "os/signal" 12 | "strings" 13 | 14 | "golang.org/x/net/websocket" 15 | ) 16 | 17 | type headers []string 18 | 19 | func (h *headers) String() string { 20 | return strings.Join(*h, ", ") 21 | } 22 | 23 | func (h *headers) Set(value string) error { 24 | *h = append(*h, value) 25 | return nil 26 | } 27 | 28 | func main() { 29 | var ( 30 | target = flag.String("u", "", "The URL to connect to") 31 | origin = flag.String("o", "", "The origin to use in the WS request") 32 | h headers 33 | origURL *url.URL 34 | ) 35 | flag.Var(&h, "H", `Headers to use in the WS request, can be used to multiple times to specify multiple headers.`+ 36 | ` Example: -H "Sample-Header-1: foo" -H "Sample-Header-2: bar"`) 37 | flag.Parse() 38 | 39 | if *target == "" { 40 | fmt.Fprintf(os.Stderr, "missing url\n") 41 | os.Exit(1) 42 | } 43 | 44 | if *origin != "" { 45 | var err error 46 | origURL, err = url.Parse(*origin) 47 | if err != nil { 48 | fmt.Fprintf(os.Stderr, "failed to parse origin URL: %s", err.Error()) 49 | os.Exit(1) 50 | } 51 | } 52 | ws := connect(*target, makeHeader(h), origURL) 53 | trapCtrlC(ws) 54 | go write(ws) 55 | read(ws) 56 | } 57 | 58 | func makeHeader(h headers) http.Header { 59 | httpH := make(http.Header) 60 | for _, hv := range h { 61 | splits := strings.SplitN(hv, ":", 2) 62 | httpH.Add(strings.TrimSpace(splits[0]), strings.TrimSpace(splits[1])) 63 | } 64 | return httpH 65 | } 66 | 67 | func connect(addr string, h http.Header, origin *url.URL) *websocket.Conn { 68 | log.Printf("connecting to %s...", addr) 69 | conf, err := websocket.NewConfig(addr, addr) 70 | if err != nil { 71 | log.Fatal(err) 72 | } 73 | conf.Header = h 74 | conf.Origin = origin 75 | ws, err := websocket.DialConfig(conf) 76 | if err != nil { 77 | log.Fatal(err) 78 | } 79 | log.Print("ready, exit with CTRL+C.") 80 | return ws 81 | } 82 | 83 | // Graceful shutdown 84 | func trapCtrlC(c *websocket.Conn) { 85 | ch := make(chan os.Signal, 1) 86 | signal.Notify(ch, os.Interrupt) 87 | go func() { 88 | for range ch { 89 | fmt.Println("\nexiting") 90 | c.Close() 91 | os.Exit(0) 92 | } 93 | }() 94 | } 95 | 96 | // Send STDIN lines to websocket server. 97 | func write(ws *websocket.Conn) { 98 | scanner := bufio.NewScanner(os.Stdin) 99 | for scanner.Scan() { 100 | t := scanner.Text() 101 | ws.Write([]byte(t)) 102 | fmt.Printf(">> %s\n", t) 103 | } 104 | } 105 | 106 | // Read from websocket and print messages to STDOUT 107 | func read(ws *websocket.Conn) { 108 | msg := make([]byte, 16384) 109 | for { 110 | n, err := ws.Read(msg) 111 | if err != nil { 112 | log.Fatal(err) 113 | } 114 | fmt.Printf("<< %s\n", msg[:n]) 115 | } 116 | } 117 | --------------------------------------------------------------------------------