├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | main 3 | wsd 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - tip 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Alexander Gugel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wsd 2 | 3 | > = **W**eb**S**ocket **D**ebugger 4 | 5 | [![Build Status](https://travis-ci.org/alexanderGugel/wsd.svg?branch=master)](https://travis-ci.org/alexanderGugel/wsd) 6 | 7 | ![Terminal Demo](https://cdn.rawgit.com/alexanderGugel/wsd/demo/demo.gif) 8 | 9 | Simple command line utility for debugging WebSocket servers. 10 | 11 | ## Installation 12 | 13 | Via `go-get`: 14 | 15 | ``` 16 | $ go get github.com/alexanderGugel/wsd 17 | ``` 18 | 19 | ## Usage 20 | 21 | Command-line usage: 22 | 23 | ``` 24 | Usage of ./wsd: 25 | -help 26 | Display help information about wsd 27 | -insecureSkipVerify 28 | Skip TLS certificate verification 29 | -origin string 30 | origin of WebSocket client (default "http://localhost/") 31 | -protocol string 32 | WebSocket subprotocol 33 | -url string 34 | WebSocket server address to connect to (default "ws://localhost:1337/ws") 35 | -version 36 | Display version number``` 37 | 38 | ## Why? 39 | 40 | Debugging WebSocket servers should be as simple as firing up `cURL`. No need 41 | for dozens of flags, just type `wsd -url=ws://localhost:1337/ws` and you're 42 | connected. 43 | 44 | ## License 45 | 46 | MIT 47 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "crypto/tls" 6 | "flag" 7 | "fmt" 8 | "io" 9 | "os" 10 | "sync" 11 | 12 | "github.com/fatih/color" 13 | "golang.org/x/net/websocket" 14 | ) 15 | 16 | // Version is the current version. 17 | const Version = "0.1.0" 18 | 19 | var ( 20 | origin string 21 | url string 22 | protocol string 23 | displayHelp bool 24 | displayVersion bool 25 | insecureSkipVerify bool 26 | red = color.New(color.FgRed).SprintFunc() 27 | magenta = color.New(color.FgMagenta).SprintFunc() 28 | green = color.New(color.FgGreen).SprintFunc() 29 | yellow = color.New(color.FgYellow).SprintFunc() 30 | cyan = color.New(color.FgCyan).SprintFunc() 31 | wg sync.WaitGroup 32 | ) 33 | 34 | func init() { 35 | flag.StringVar(&origin, "origin", "http://localhost/", "origin of WebSocket client") 36 | flag.StringVar(&url, "url", "ws://localhost:1337/ws", "WebSocket server address to connect to") 37 | flag.StringVar(&protocol, "protocol", "", "WebSocket subprotocol") 38 | flag.BoolVar(&insecureSkipVerify, "insecureSkipVerify", false, "Skip TLS certificate verification") 39 | flag.BoolVar(&displayHelp, "help", false, "Display help information about wsd") 40 | flag.BoolVar(&displayVersion, "version", false, "Display version number") 41 | } 42 | 43 | func inLoop(ws *websocket.Conn, errors chan<- error, in chan<- []byte) { 44 | var msg = make([]byte, 512) 45 | 46 | for { 47 | var n int 48 | var err error 49 | 50 | n, err = ws.Read(msg) 51 | 52 | if err != nil { 53 | errors <- err 54 | continue 55 | } 56 | 57 | in <- msg[:n] 58 | } 59 | } 60 | 61 | func printErrors(errors <-chan error) { 62 | for err := range errors { 63 | if err == io.EOF { 64 | fmt.Printf("\r✝ %v - connection closed by remote\n", magenta(err)) 65 | os.Exit(0) 66 | } else { 67 | fmt.Printf("\rerr %v\n> ", red(err)) 68 | } 69 | } 70 | } 71 | 72 | func printReceivedMessages(in <-chan []byte) { 73 | for msg := range in { 74 | fmt.Printf("\r< %s\n> ", cyan(string(msg))) 75 | } 76 | } 77 | 78 | func outLoop(ws *websocket.Conn, out <-chan []byte, errors chan<- error) { 79 | for msg := range out { 80 | _, err := ws.Write(msg) 81 | if err != nil { 82 | errors <- err 83 | } 84 | } 85 | } 86 | 87 | func dial(url, protocol, origin string) (ws *websocket.Conn, err error) { 88 | config, err := websocket.NewConfig(url, origin) 89 | if err != nil { 90 | return nil, err 91 | } 92 | if protocol != "" { 93 | config.Protocol = []string{protocol} 94 | } 95 | config.TlsConfig = &tls.Config{ 96 | InsecureSkipVerify: insecureSkipVerify, 97 | } 98 | return websocket.DialConfig(config) 99 | } 100 | 101 | func main() { 102 | flag.Parse() 103 | 104 | if displayVersion { 105 | fmt.Fprintf(os.Stdout, "%s version %s\n", os.Args[0], Version) 106 | os.Exit(0) 107 | } 108 | 109 | if displayHelp { 110 | fmt.Fprintf(os.Stdout, "Usage of %s:\n", os.Args[0]) 111 | flag.PrintDefaults() 112 | os.Exit(0) 113 | } 114 | 115 | ws, err := dial(url, protocol, origin) 116 | 117 | if protocol != "" { 118 | fmt.Printf("connecting to %s via %s from %s...\n", yellow(url), yellow(protocol), yellow(origin)) 119 | } else { 120 | fmt.Printf("connecting to %s from %s...\n", yellow(url), yellow(origin)) 121 | } 122 | 123 | defer ws.Close() 124 | 125 | if err != nil { 126 | panic(err) 127 | } 128 | 129 | fmt.Printf("successfully connected to %s\n\n", green(url)) 130 | 131 | wg.Add(3) 132 | 133 | errors := make(chan error) 134 | in := make(chan []byte) 135 | out := make(chan []byte) 136 | 137 | defer close(errors) 138 | defer close(out) 139 | defer close(in) 140 | 141 | go inLoop(ws, errors, in) 142 | go printReceivedMessages(in) 143 | go printErrors(errors) 144 | go outLoop(ws, out, errors) 145 | 146 | scanner := bufio.NewScanner(os.Stdin) 147 | 148 | fmt.Print("> ") 149 | for scanner.Scan() { 150 | out <- []byte(scanner.Text()) 151 | fmt.Print("> ") 152 | } 153 | 154 | wg.Wait() 155 | } 156 | --------------------------------------------------------------------------------