├── .gitignore ├── LICENSE ├── README.md ├── main.go ├── release.py └── ws.rb /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, yhat 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 21 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### WS 2 | 3 | A WebSocket command line tool (mostly for testing). 4 | 5 | Type to stdin to send along the WebSocket. WebSocket responses will be printed 6 | to stdout. 7 | 8 | ### Install 9 | 10 | If you have Go installed just run: 11 | 12 | go get github.com/yhat/ws 13 | 14 | If you're on OS X use brew to install (no go required). 15 | 16 | brew install https://raw.githubusercontent.com/yhat/ws/master/ws.rb 17 | 18 | Otherwise direct downloads are available through the [releases page](https://github.com/yhat/ws/releases). 19 | 20 | ### Usage 21 | 22 | Given an example server. 23 | 24 | ```node 25 | var WebSocketServer = require('ws').Server 26 | , wss = new WebSocketServer({port: 5000}); 27 | wss.on('connection', function(ws) { 28 | ws.on('message', function(message) { 29 | ws.send(message) 30 | ws.send("OVER") 31 | }); 32 | }); 33 | ``` 34 | 35 | Let's make sure it's working with `ws`. 36 | 37 | ``` 38 | $ ws ws://127.0.0.1:5000/ 39 | That's a mighty fine websocket cli tool you've got there 40 | That's a mighty fine websocket cli tool you've got there 41 | OVER 42 | ``` 43 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io" 7 | "net/http" 8 | "os" 9 | "strings" 10 | 11 | "golang.org/x/net/websocket" 12 | ) 13 | 14 | var ( 15 | origin string 16 | headers string 17 | version int 18 | ) 19 | 20 | func init() { 21 | help = fmt.Sprintf(help, VERSION) 22 | flag.StringVar(&origin, "o", "http://0.0.0.0/", "websocket origin") 23 | flag.StringVar(&headers, "H", "", "a comma separated list of http headers") 24 | flag.IntVar(&version, "v", websocket.ProtocolVersionHybi13, "websocket version") 25 | flag.Parse() 26 | } 27 | 28 | const VERSION = "0.1" 29 | 30 | var help = `ws - %s 31 | 32 | Usage: 33 | ws [options] 34 | 35 | Use "ws --help" for help. 36 | ` 37 | 38 | func parseHeaders(headers string) http.Header { 39 | h := http.Header{} 40 | for _, header := range strings.Split(headers, ",") { 41 | parts := strings.SplitN(header, ":", 2) 42 | if len(parts) != 2 { 43 | continue 44 | } 45 | h.Add(strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1])) 46 | } 47 | return h 48 | } 49 | 50 | func main() { 51 | fatal := func(format string, a ...interface{}) { 52 | fmt.Fprintf(os.Stderr, format, a...) 53 | os.Exit(2) 54 | } 55 | target := flag.Arg(0) 56 | if target == "" { 57 | fatal(help) 58 | } 59 | config, err := websocket.NewConfig(target, origin) 60 | if err != nil { 61 | fatal("%s\n", err) 62 | } 63 | if headers != "" { 64 | config.Header = parseHeaders(headers) 65 | } 66 | config.Version = version 67 | ws, err := websocket.DialConfig(config) 68 | if err != nil { 69 | fatal("Error dialing %s: %v\n", target, err) 70 | } 71 | errc := make(chan error, 2) 72 | cp := func(dst io.Writer, src io.Reader) { 73 | _, err := io.Copy(dst, src) 74 | errc <- err 75 | } 76 | go cp(os.Stdout, ws) 77 | go cp(ws, os.Stdin) 78 | <-errc 79 | } 80 | -------------------------------------------------------------------------------- /release.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | This is just a script to help automate the release of the Go binary, nothing 5 | to see here! 6 | """ 7 | 8 | import hashlib 9 | import os 10 | import re 11 | import shutil 12 | import subprocess as sp 13 | 14 | from string import Template 15 | from zipfile import ZipFile 16 | 17 | brew_formula_template = Template(""" 18 | # This file was generated by release.py 19 | require 'formula' 20 | class Ws < Formula 21 | homepage 'https://github.com/yhat/ws' 22 | version '${version}' 23 | if Hardware.is_64_bit? 24 | url 'https://github.com/yhat/ws/releases/download/v${version}/ws_darwin_amd64.zip' 25 | sha1 '${darwin_amd64_sha1}' 26 | else 27 | url 'https://github.com/yhat/ws/releases/download/v${version}/ws_darwin_386.zip' 28 | sha1 '${darwin_386_sha1}' 29 | end 30 | def install 31 | bin.install 'ws' 32 | end 33 | end 34 | """.strip()) 35 | 36 | version_re = re.compile(r'\nconst VERSION = \"([0-9\.]+)\"\n') 37 | 38 | if __name__ == "__main__": 39 | with open("main.go", "r") as f: 40 | version = version_re.findall(f.read()) 41 | 42 | assert len(version) == 1, "[-] Failed to find current version %s" % (version,) 43 | version = version[0] 44 | print "[+] Version found:", version 45 | 46 | if os.path.isdir("./dist"): 47 | print "[+] Directory 'dist' exists. Removing it." 48 | shutil.rmtree("./dist") 49 | 50 | print "[+] Cross-compiling ws." 51 | sp.check_call(["gox", "-output", "dist/{{.Dir}}_{{.OS}}_{{.Arch}}"]) 52 | 53 | print "[+] Zipping executables." 54 | sha1sums = {} 55 | for fi in os.listdir("./dist"): 56 | exe = os.path.join("dist", "ws") 57 | zipname = os.path.join("dist", fi) 58 | if fi.endswith(".exe"): 59 | exe += ".exe" 60 | zipname = zipname.rstrip(".exe") 61 | zipname += ".zip" 62 | os.rename(os.path.join("dist", fi), exe) 63 | with ZipFile(zipname, "w") as z: 64 | z.write(exe, os.path.basename(exe)) 65 | os.remove(exe) 66 | h = hashlib.sha1() 67 | with open(zipname, "r") as z: 68 | h.update(z.read()) 69 | sha1sum = h.hexdigest() 70 | print "[+] %s: %s" % (sha1sum, zipname,) 71 | sha1sums[zipname] = sha1sum 72 | 73 | t = brew_formula_template.substitute( 74 | version=version, 75 | darwin_amd64_sha1 = sha1sums["dist/ws_darwin_amd64.zip"], 76 | darwin_386_sha1 = sha1sums["dist/ws_darwin_386.zip"], 77 | ) 78 | print "[+] Updating brew formula." 79 | with open("ws.rb", "w") as f: 80 | f.write(t) 81 | -------------------------------------------------------------------------------- /ws.rb: -------------------------------------------------------------------------------- 1 | # This file was generated by release.py 2 | require 'formula' 3 | class Ws < Formula 4 | homepage 'https://github.com/yhat/ws' 5 | version '0.1' 6 | if Hardware.is_64_bit? 7 | url 'https://github.com/yhat/ws/releases/download/v0.1/ws_darwin_amd64.zip' 8 | sha1 '698ba51bf5b18f4823352fe4093a6a258f941300' 9 | else 10 | url 'https://github.com/yhat/ws/releases/download/v0.1/ws_darwin_386.zip' 11 | sha1 'b086a8577a15d3b124cb8bc3693826016b7eed73' 12 | end 13 | def install 14 | bin.install 'ws' 15 | end 16 | end 17 | --------------------------------------------------------------------------------