├── .gitignore ├── .gopack.yml ├── .gr-travis.yml ├── .travis.yml ├── CHANGELOG.md ├── Godeps ├── Godeps.json └── Readme ├── LICENSE ├── README.md ├── docs └── alternative.md ├── hooks ├── README.txt └── tcp-post-connect.sample ├── main.go ├── pxlocal ├── client.go ├── freeport.go ├── freeport_test.go ├── hook.go ├── netpipe.go ├── server.go ├── utils.go └── utils_test.go ├── vendor ├── github.com │ ├── alecthomas │ │ ├── template │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── doc.go │ │ │ ├── exec.go │ │ │ ├── funcs.go │ │ │ ├── helper.go │ │ │ ├── parse │ │ │ │ ├── lex.go │ │ │ │ ├── node.go │ │ │ │ └── parse.go │ │ │ └── template.go │ │ └── units │ │ │ ├── COPYING │ │ │ ├── README.md │ │ │ ├── bytes.go │ │ │ ├── doc.go │ │ │ ├── si.go │ │ │ └── util.go │ ├── gorilla │ │ └── websocket │ │ │ ├── .gitignore │ │ │ ├── .travis.yml │ │ │ ├── AUTHORS │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── client.go │ │ │ ├── compression.go │ │ │ ├── conn.go │ │ │ ├── conn_read.go │ │ │ ├── conn_read_legacy.go │ │ │ ├── doc.go │ │ │ ├── json.go │ │ │ ├── server.go │ │ │ └── util.go │ └── qiniu │ │ └── log │ │ ├── .gitignore │ │ ├── README.md │ │ └── logext.go └── gopkg.in │ └── alecthomas │ └── kingpin.v2 │ ├── .travis.yml │ ├── COPYING │ ├── README.md │ ├── actions.go │ ├── app.go │ ├── args.go │ ├── cmd.go │ ├── completions.go │ ├── doc.go │ ├── envar.go │ ├── flags.go │ ├── global.go │ ├── guesswidth.go │ ├── guesswidth_unix.go │ ├── model.go │ ├── parser.go │ ├── parsers.go │ ├── templates.go │ ├── usage.go │ ├── values.go │ ├── values.json │ └── values_generated.go └── version.go /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.db 3 | *.log 4 | custom/ 5 | data/ 6 | .vendor/ 7 | .idea/ 8 | *.iml 9 | public/img/avatar/ 10 | files/ 11 | 12 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 13 | *.o 14 | *.a 15 | *.so 16 | 17 | # Folders 18 | _obj 19 | _test 20 | 21 | # Architecture specific extensions/prefixes 22 | *.[568vq] 23 | [568vq].out 24 | 25 | *.cgo1.go 26 | *.cgo2.c 27 | _cgo_defun.c 28 | _cgo_gotypes.go 29 | _cgo_export.* 30 | 31 | _testmain.go 32 | 33 | *.exe 34 | *.exe~ 35 | /gogs 36 | profile/ 37 | __pycache__ 38 | *.pem 39 | output* 40 | config.codekit 41 | .brackets.json 42 | -------------------------------------------------------------------------------- /.gopack.yml: -------------------------------------------------------------------------------- 1 | author: codeskyblue 2 | description: proxy local to public 3 | os: darwin linux windows 4 | includes: 5 | - README.md 6 | - LICENSE 7 | excludes: 8 | - \.git 9 | script: 10 | - go build 11 | -------------------------------------------------------------------------------- /.gr-travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.4 4 | env: 5 | - "PATH=/home/travis/gopath/bin:$PATH" 6 | before_install: 7 | - go get github.com/mitchellh/gox 8 | - gox -build-toolchain 9 | - go get github.com/tcnksm/ghr 10 | script: 11 | - go test -v ./... 12 | after_success: 13 | - gox -output "dist/{{.OS}}_{{.Arch}}_{{.Dir}}" 14 | - ghr --username codeskyblue --token $GITHUB_TOKEN --replace --prerelease --debug pre-release dist/ 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.4 4 | before_install: 5 | - go get -v 6 | script: 7 | - go test -v ./... 8 | after_success: 9 | - bash -c "$(curl -fsSL http://bitly.com/gorelease)" gorelease 10 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | - 1.0.1 2 | 3 | kingping to instead of flag 4 | 5 | - 1.0.2 6 | 7 | update godeps (especially grilla/websocket) 8 | 9 | - 1.1.0 10 | 11 | Add --data support, update hooks doc 12 | Client add redial 13 | Clean useless code. 14 | Code speed optimize 15 | 16 | - 1.1.1 17 | 18 | performance enhancement again. 19 | -------------------------------------------------------------------------------- /Godeps/Godeps.json: -------------------------------------------------------------------------------- 1 | { 2 | "ImportPath": "github.com/codeskyblue/proxylocal", 3 | "GoVersion": "go1.6", 4 | "GodepVersion": "v74", 5 | "Deps": [ 6 | { 7 | "ImportPath": "github.com/alecthomas/template", 8 | "Rev": "a0175ee3bccc567396460bf5acd36800cb10c49c" 9 | }, 10 | { 11 | "ImportPath": "github.com/alecthomas/template/parse", 12 | "Rev": "a0175ee3bccc567396460bf5acd36800cb10c49c" 13 | }, 14 | { 15 | "ImportPath": "github.com/alecthomas/units", 16 | "Rev": "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a" 17 | }, 18 | { 19 | "ImportPath": "github.com/gorilla/websocket", 20 | "Comment": "v1.0.0-28-g5df680c", 21 | "Rev": "5df680c89f2a84ad9cb16cf143557a0853cfaabc" 22 | }, 23 | { 24 | "ImportPath": "github.com/qiniu/log", 25 | "Comment": "v1.0.00-1-ga304a74", 26 | "Rev": "a304a74568d6982c5b89de1c68ac8fca3add196a" 27 | }, 28 | { 29 | "ImportPath": "gopkg.in/alecthomas/kingpin.v2", 30 | "Comment": "v2.2.3", 31 | "Rev": "e9044be3ab2a8e11d4e1f418d12f0790d57e8d70" 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /Godeps/Readme: -------------------------------------------------------------------------------- 1 | This directory tree is generated automatically by godep. 2 | 3 | Please do not edit. 4 | 5 | See https://github.com/tools/godep for more information. 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 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 all 13 | 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 THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # proxylocal 2 | [![Build Status](https://travis-ci.org/codeskyblue/proxylocal.svg?branch=master)](https://travis-ci.org/codeskyblue/proxylocal) 3 | [![GoDoc](https://godoc.org/github.com/codeskyblue/proxylocal/pxlocal?status.svg)](https://godoc.org/github.com/codeskyblue/proxylocal/pxlocal) 4 | 5 | Proxy local service to public. 6 | 7 | > I want to expose a local server behide a NAT or firewall to the internet. 8 | 9 | There are some similar service. 10 | 11 | * Write in nodejs. Very good one. 12 | * Write in python. 13 | * Blocked by GFW. 14 | * Need pay price to get service. 15 | * Server seems down. Use ngrok, VPS in china. 16 | 17 | Binary can be download from [gorelease](http://gorelease.herokuapp.com/codeskyblue/proxylocal) 18 | 19 | Very suggest to compile use `go1.4`. I donot know why, but use `go1.5`, the proxylocal got a very very bad performance. 20 | 21 | At the beginning this is just for study how to proxy local server to public network. Now it can be stable use. 22 | 23 | 这个东西目前看来确实是个很不错的东西,可以调试微信,可以把自家路由器的东西放到外网。还可以通过它的tcp转发功能,远程调试家中的树莓派。用途多多 24 | 25 | 不过服务器最好自己搭. 如果希望贡献出来你的server可以发起个Issue. 26 | 27 | ## Installation 28 | ``` 29 | go get -v github.com/codeskyblue/proxylocal 30 | ``` 31 | 32 | ## Usage 33 | Run server in a public network, listen in port 8080 (Assuming your ip is 122.2.2.1) 34 | 35 | proxylocal --listen 8080 36 | 37 | Assume you are running your local tcp-server on port 5037. To make it publicly available run: 38 | 39 | proxylocal --server 122.2.2.1:8080 --proto tcp 5037 40 | 41 | If this is a web server, only need to update `--proto` 42 | 43 | proxylocal --server 122.2.2.1:8080 --proto http 5037 44 | 45 | # expects output 46 | proxy URL: http://localhost:5037 47 | Recv Message: Local server is now publicly available via: 48 | http://wn8yn.t.localhost 49 | 50 | ## Hooks 51 | The functions of hooks are limited. 52 | 53 | The hook system is very familar with git hook. When a new proxy request comes to the server. Server will execute some script. 54 | 55 | Now I put all the hook script in hooks dir. 56 | 57 | There are examples you found in [hooks](hooks) 58 | 59 | ## Use as a library 60 | ```go 61 | package main 62 | 63 | import "github.com/codeskyblue/proxylocal/pxlocal" 64 | 65 | func main(){ 66 | client := pxlocal.NewClient("10.0.1.1:4000") 67 | px, err := client.StartProxy(pxlocal.ProxyOptions{ 68 | Proto: pxlocal.TCP, 69 | LocalAddr: "192.168.0.1:7000", 70 | ListenPort: 40000, // public port 71 | }) 72 | if err != nil { 73 | log.Fatal(err) 74 | } 75 | // px.RemoteAddr() 76 | err = px.Wait() 77 | log.Fatal(err) 78 | } 79 | ``` 80 | ### Environment 81 | Server address default from env-var `PXL_SERVER_ADDR` 82 | 83 | ## LICENSE 84 | [MIT LICENSE](LICENSE) 85 | -------------------------------------------------------------------------------- /docs/alternative.md: -------------------------------------------------------------------------------- 1 | # alternative 2 | 3 | * 商业性质的,不过看起来服务还挺稳定的。宣传网站做的太好看了 4 | * 竟然是python写的,真是没有想到。网站质量一般般,免费1个月,只能说感觉还愁和吧。 5 | * 6 | 7 | 不得不提下,这个NB掉渣天的ngrok,虽然被墙了,但是依然很厉害。毕竟全开源。 8 | 有时候也会抽个风,什么的,哎。有点太复杂了,一开始本来想研究下的,被吓怕了。 9 | 10 | * 11 | 12 | 我还是挺喜欢这个网站的,不知为何,这种简洁的页面真的好喜欢。可惜不维护了,好遗憾。服务器是一种资源,用的多了,估计维护成本也高。 13 | -------------------------------------------------------------------------------- /hooks/README.txt: -------------------------------------------------------------------------------- 1 | README.txt 2 | 3 | :Author: hzsunshx 4 | :Email: hzsunshx@onlinegame-14-51 5 | :Date: 2015-11-13 13:35 6 | 7 | There are example script in this dir. 8 | 9 | Remove `.example` suffix to enable hook script. 10 | -------------------------------------------------------------------------------- /hooks/tcp-post-connect.sample: -------------------------------------------------------------------------------- 1 | #!/bin/bash - 2 | # 3 | # hook tcp post connect 4 | # 5 | # envs: 6 | # - PORT 7 | # server created port 8 | # ex: 12345 9 | # - REMOTE_ADDR 10 | # ex: 10.10.0.1:6730 11 | # - REMOTE_DATA 12 | # data which send from proxylocal client 13 | # ex: hello world 14 | 15 | 16 | #env 17 | echo "PORT=$PORT" 18 | echo "REMOTE_ADDR=$REMOTE_ADDR" 19 | echo "REMOTE_DATA=$REMOTE_DATA" 20 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "net/http" 7 | "net/url" 8 | "regexp" 9 | "strconv" 10 | "time" 11 | 12 | "github.com/codeskyblue/proxylocal/pxlocal" 13 | "github.com/qiniu/log" 14 | "gopkg.in/alecthomas/kingpin.v2" 15 | ) 16 | 17 | type GlobalConfig struct { 18 | Server struct { 19 | Enable bool 20 | Addr string 21 | Domain string 22 | } 23 | 24 | Proto string 25 | Data string 26 | ProxyPort int 27 | SubDomain string 28 | Debug bool 29 | } 30 | 31 | var cfg GlobalConfig 32 | var localAddr string 33 | 34 | func init() { 35 | kingpin.Flag("debug", "Enable debug mode.").BoolVar(&cfg.Debug) 36 | 37 | kingpin.Flag("proto", "Default protocol, http or tcp").Default("http").Short('p').EnumVar(&cfg.Proto, "http", "tcp") // .StringVar(&cfg.Proto) 38 | kingpin.Flag("subdomain", "Proxy subdomain, used for http").StringVar(&cfg.SubDomain) 39 | kingpin.Flag("remote-port", "Proxy server listen port, only used in tcp").IntVar(&cfg.ProxyPort) 40 | kingpin.Flag("data", "Data send to server, can be anything").StringVar(&cfg.Data) 41 | kingpin.Flag("server", "Specify server address").Short('s').OverrideDefaultFromEnvar("PXL_SERVER_ADDR").Default("proxylocal.xyz").StringVar(&cfg.Server.Addr) 42 | 43 | kingpin.Flag("listen", "Run in server mode").Short('l').BoolVar(&cfg.Server.Enable) 44 | kingpin.Flag("domain", "Proxy server mode domain name, optional").StringVar(&cfg.Server.Domain) 45 | 46 | kingpin.Arg("local", "Local address").Required().StringVar(&localAddr) 47 | } 48 | 49 | func parseURL(addr string, defaultProto string) (u *url.URL, err error) { 50 | if !regexp.MustCompile("^(http|https|tcp)://").MatchString(addr) { 51 | if _, err := strconv.Atoi(addr); err == nil { // only contain port 52 | addr = "localhost:" + addr 53 | } else { 54 | _, _, err := net.SplitHostPort(addr) 55 | if err != nil { 56 | return nil, err 57 | } 58 | } 59 | addr = defaultProto + "://" + addr 60 | } 61 | return url.Parse(addr) 62 | } 63 | 64 | func main() { 65 | kingpin.Version(VERSION) 66 | kingpin.CommandLine.VersionFlag.Short('v') 67 | kingpin.CommandLine.HelpFlag.Short('h') 68 | kingpin.Parse() 69 | 70 | if !cfg.Server.Enable && localAddr == "" { 71 | kingpin.Usage() 72 | return 73 | } 74 | if !cfg.Debug { 75 | log.SetOutputLevel(log.Linfo) 76 | } else { 77 | log.SetOutputLevel(log.Ldebug) 78 | } 79 | 80 | pURL, err := pxlocal.ParseURL(localAddr, pxlocal.URLOpts{DefaultScheme: cfg.Proto}) 81 | if err != nil { 82 | log.Fatal(err) 83 | } 84 | 85 | if cfg.Server.Enable { 86 | _, port, _ := net.SplitHostPort(pURL.Host) 87 | if port == "" { 88 | port = "80" 89 | } 90 | addr := net.JoinHostPort("0.0.0.0", port) 91 | if cfg.Server.Domain == "" { 92 | cfg.Server.Domain = "localhost" //cfg.Server.Addr 93 | } 94 | fmt.Printf("proxylocal: server listen on %v, domain is %v\n", addr, cfg.Server.Domain) 95 | ps := pxlocal.NewProxyServer(cfg.Server.Domain) 96 | log.Fatal(http.ListenAndServe(addr, ps)) 97 | } 98 | 99 | client := pxlocal.NewClient(cfg.Server.Addr) 100 | fmt.Println("proxy URL:", pURL) 101 | failCount := 0 102 | for { 103 | px, err := client.RunProxy(pxlocal.ProxyOptions{ 104 | Proto: pxlocal.ProxyProtocol(cfg.Proto), 105 | Subdomain: cfg.SubDomain, 106 | LocalAddr: localAddr, 107 | ListenPort: cfg.ProxyPort, 108 | }) 109 | if err == nil { 110 | err = px.Wait() 111 | } 112 | if err == pxlocal.ErrWebsocketBroken { 113 | failCount = 0 114 | fmt.Println("Reconnect after 5 seconds ...") 115 | time.Sleep(5 * time.Second) 116 | continue 117 | } 118 | if err == pxlocal.ErrDialTCP { 119 | if failCount < 13 { 120 | failCount += 1 121 | } 122 | wait := 7 + failCount 123 | fmt.Printf("Reconnect after %d seconds ...\n", wait) 124 | time.Sleep(time.Duration(wait) * time.Second) 125 | continue 126 | } 127 | log.Fatal(err) 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /pxlocal/client.go: -------------------------------------------------------------------------------- 1 | package pxlocal 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "net" 7 | "net/http" 8 | "net/http/httputil" 9 | "net/url" 10 | "strconv" 11 | "sync" 12 | "time" 13 | 14 | "github.com/gorilla/websocket" 15 | "github.com/qiniu/log" 16 | ) 17 | 18 | var ( 19 | ErrWebsocketBroken = errors.New("Error websocket connection") 20 | ErrDialTCP = errors.New("Error dial tcp connection") 21 | ErrUnknownProtocol = errors.New("Unknown protocol") 22 | ErrPrototolRequired = errors.New("Protocol required") 23 | ) 24 | 25 | type ProxyProtocol string 26 | 27 | const ( 28 | TCP = ProxyProtocol("tcp") 29 | HTTP = ProxyProtocol("http") 30 | ) 31 | 32 | type ProxyOptions struct { 33 | LocalAddr string 34 | Proto ProxyProtocol 35 | Subdomain string 36 | ListenPort int 37 | ExtraData string 38 | } 39 | 40 | type Client struct { 41 | sURL *url.URL 42 | } 43 | 44 | // Proxy Client 45 | func NewClient(serverAddr string) *Client { 46 | u := &url.URL{ 47 | Scheme: "ws", 48 | Host: serverAddr, 49 | Path: "/ws", 50 | } 51 | return &Client{u} 52 | } 53 | 54 | type ProxyConnector struct { 55 | wsConn *websocket.Conn 56 | err error 57 | wg sync.WaitGroup 58 | remoteAddr string 59 | } 60 | 61 | func (p *ProxyConnector) Close() error { 62 | return p.wsConn.Close() 63 | } 64 | 65 | func (p *ProxyConnector) Wait() error { 66 | p.wg.Wait() 67 | return p.err 68 | } 69 | 70 | func (p *ProxyConnector) RemoteAddr() string { 71 | return p.remoteAddr 72 | } 73 | 74 | // This is a immediately return function 75 | func (c *Client) RunProxy(opts ProxyOptions) (pc *ProxyConnector, err error) { 76 | if opts.Proto == "" { 77 | return nil, ErrPrototolRequired 78 | } 79 | q := c.sURL.Query() 80 | q.Add("protocol", string(opts.Proto)) 81 | q.Add("subdomain", opts.Subdomain) 82 | q.Add("data", opts.ExtraData) 83 | if opts.ListenPort != 0 { 84 | q.Add("port", strconv.Itoa(opts.ListenPort)) 85 | } 86 | c.sURL.RawQuery = q.Encode() 87 | 88 | conn, err := net.Dial("tcp", c.sURL.Host) 89 | if err != nil { 90 | return nil, ErrDialTCP 91 | } 92 | wsclient, _, err := websocket.NewClient(conn, c.sURL, nil, 1024, 1024) 93 | if err != nil { 94 | return nil, err 95 | } 96 | pc = &ProxyConnector{wsConn: wsclient} 97 | var msg message 98 | if err := wsclient.ReadJSON(&msg); err != nil { 99 | return nil, err 100 | } 101 | pc.remoteAddr = msg.Body 102 | 103 | pc.wg.Add(1) 104 | go idleWsSend(wsclient) // keep websocket alive to prevent nginx timeout issue 105 | go func() { 106 | defer wsclient.Close() 107 | revListener := newRevNetListener() 108 | defer revListener.Close() 109 | defer pc.wg.Done() 110 | 111 | go serveRevConn(opts.Proto, opts.LocalAddr, revListener) 112 | for { 113 | if err := wsclient.ReadJSON(&msg); err != nil { 114 | pc.err = err 115 | return 116 | } 117 | go handleWsMsg(msg, c.sURL, revListener) // send new conn to rnl 118 | } 119 | }() 120 | return pc, nil 121 | } 122 | 123 | func idleWsSend(wsc *websocket.Conn) { 124 | var msg message 125 | msg.Type = TYPE_IDLE 126 | msg.Name = "idle" 127 | for { 128 | if err := wsc.WriteJSON(&msg); err != nil { 129 | break 130 | } 131 | time.Sleep(5 * time.Second) 132 | } 133 | } 134 | 135 | type reverseNetListener struct { 136 | connCh chan net.Conn 137 | } 138 | 139 | func newRevNetListener() *reverseNetListener { 140 | return &reverseNetListener{ 141 | connCh: make(chan net.Conn, 100), 142 | } 143 | } 144 | 145 | func (r *reverseNetListener) Accept() (net.Conn, error) { 146 | conn, ok := <-r.connCh 147 | if !ok { 148 | return nil, errors.New("RevNet Closed") 149 | } 150 | return conn, nil 151 | } 152 | 153 | func (r *reverseNetListener) Addr() net.Addr { 154 | return nil 155 | } 156 | 157 | func (r *reverseNetListener) Close() error { 158 | close(r.connCh) 159 | return nil 160 | } 161 | 162 | // msg comes from px server by websocket 163 | // 1: connect to px server, use msg.Name to identify self. 164 | // 2: change conn to reverse conn 165 | func handleWsMsg(msg message, sURL *url.URL, rnl *reverseNetListener) { 166 | u := sURL 167 | switch msg.Type { 168 | case TYPE_NEWCONN: 169 | log.Debug("dial remote:", u.Host) 170 | sconn, err := net.Dial("tcp", u.Host) 171 | if err != nil { 172 | log.Println(err) 173 | break 174 | } 175 | log.Infof("proxy for: %s", msg.Name) 176 | _, err = sconn.Write([]byte(fmt.Sprintf( 177 | "GET /proxyhijack HTTP/1.1\r\nHost: proxylocal\r\nX-Proxy-For: %s \r\n\r\n", msg.Name))) 178 | if err != nil { 179 | log.Println(err) 180 | break 181 | } 182 | rnl.connCh <- sconn 183 | case TYPE_MESSAGE: 184 | fmt.Printf("Recv Message: %v\n", msg.Body) 185 | default: 186 | log.Warnf("Type: %v not support", msg.Type) 187 | } 188 | } 189 | 190 | func serveRevConn(proto ProxyProtocol, pAddr string, lis net.Listener) error { 191 | switch proto { 192 | case TCP: 193 | for { 194 | rconn, err := lis.Accept() 195 | if err != nil { 196 | log.Errorf("accept error: %v", err) 197 | return err 198 | } 199 | log.Info("local dial tcp", pAddr) 200 | lconn, err := net.Dial("tcp", pAddr) 201 | if err != nil { 202 | log.Warn(err) 203 | rconn.Close() 204 | return err 205 | } 206 | // start forward local proxy 207 | pc := &proxyConn{ 208 | lconn: lconn, 209 | rconn: rconn, 210 | stats: proxyStats, 211 | } 212 | go pc.start() 213 | } 214 | case HTTP: 215 | rp := &httputil.ReverseProxy{ 216 | Director: func(req *http.Request) { 217 | req.Host = pAddr 218 | req.URL.Scheme = "http" 219 | req.URL.Host = pAddr 220 | }, 221 | } 222 | return http.Serve(lis, rp) 223 | default: 224 | log.Println("Unknown protocol:", proto) 225 | return ErrUnknownProtocol 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /pxlocal/freeport.go: -------------------------------------------------------------------------------- 1 | package pxlocal 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "net" 7 | ) 8 | 9 | type freePort struct { 10 | minPort int // >= 11 | maxPort int // < 12 | next int 13 | count int 14 | } 15 | 16 | func newFreePort(min, max int) *freePort { 17 | if max > 65535 { 18 | max = 65535 19 | } 20 | return &freePort{ 21 | next: min, 22 | minPort: min, 23 | maxPort: max, 24 | count: max - min, 25 | } 26 | } 27 | 28 | func (this *freePort) ListenTCP() (taddr *net.TCPAddr, lis *net.TCPListener, err error) { 29 | next := this.next 30 | for i := 0; i < this.count; i++ { 31 | next = (this.next+i-this.minPort)%this.count + this.minPort 32 | taddr, _ := net.ResolveTCPAddr("tcp", fmt.Sprintf(":%d", next)) 33 | lis, err := net.ListenTCP("tcp", taddr) 34 | if err == nil { 35 | this.next = next + 1 36 | return taddr, lis, nil 37 | } 38 | } 39 | return nil, nil, errors.New("Not free port") 40 | } 41 | -------------------------------------------------------------------------------- /pxlocal/freeport_test.go: -------------------------------------------------------------------------------- 1 | package pxlocal 2 | 3 | import "testing" 4 | 5 | func TestFreePort(t *testing.T) { 6 | fp := newFreePort(42000, 42003) 7 | // fp.ListenTCP() 8 | taddr, lis1, err := fp.ListenTCP() 9 | if err != nil { 10 | t.Fatal(err) 11 | } 12 | if taddr.Port != 42000 { 13 | t.Fatalf("expect taddr 42000, but got %v", taddr) 14 | } 15 | 16 | taddr, lis2, err := fp.ListenTCP() 17 | if err != nil { 18 | t.Fatal(err) 19 | } 20 | if taddr.Port != 42001 { 21 | t.Fatalf("expect taddr 42001, but got %v", taddr) 22 | } 23 | defer lis2.Close() 24 | 25 | lis1.Close() 26 | taddr, lis3, err := fp.ListenTCP() 27 | if err != nil { 28 | t.Fatal(err) 29 | } 30 | if taddr.Port != 42002 { 31 | t.Fatalf("expect taddr 42002, but got %v", taddr) 32 | } 33 | defer lis3.Close() 34 | 35 | taddr, lis4, err := fp.ListenTCP() 36 | if err != nil { 37 | t.Fatal(err) 38 | } 39 | if taddr.Port != 42000 { 40 | t.Fatalf("expect taddr 42000, but got %v", taddr) 41 | } 42 | defer lis4.Close() 43 | } 44 | -------------------------------------------------------------------------------- /pxlocal/hook.go: -------------------------------------------------------------------------------- 1 | package pxlocal 2 | 3 | import ( 4 | "os" 5 | "os/exec" 6 | "path/filepath" 7 | ) 8 | 9 | const ( 10 | HOOK_TCP_POST_CONNECT = "tcp-post-connect" 11 | ) 12 | 13 | func hook(scriptName string, envs []string) error { 14 | scriptPath := filepath.Join("hooks", scriptName) 15 | if _, err := os.Stat(scriptPath); err == nil { 16 | cmd := exec.Command(scriptPath) 17 | cmd.Env = os.Environ() 18 | cmd.Env = append(cmd.Env, envs...) 19 | cmd.Stdout = os.Stdout 20 | cmd.Stderr = os.Stderr 21 | return cmd.Run() 22 | } 23 | return nil 24 | } 25 | -------------------------------------------------------------------------------- /pxlocal/netpipe.go: -------------------------------------------------------------------------------- 1 | package pxlocal 2 | 3 | import ( 4 | "net" 5 | "sync" 6 | 7 | "github.com/qiniu/log" 8 | ) 9 | 10 | //A proxy represents a pair of connections and their state 11 | type ProxyStats struct { 12 | sentBytes uint64 13 | receivedBytes uint64 14 | // laddr, raddr *net.TCPAddr 15 | } 16 | 17 | type proxyConn struct { 18 | sentBytes uint64 19 | receivedBytes uint64 20 | lconn, rconn net.Conn 21 | stats *ProxyStats 22 | } 23 | 24 | func closeRead(c net.Conn) error { 25 | if x, ok := c.(interface { 26 | CloseRead() error 27 | }); ok { 28 | return x.CloseRead() 29 | } else { 30 | log.Debug("force close", c) 31 | return c.Close() 32 | } 33 | } 34 | 35 | func closeWrite(c net.Conn) error { 36 | if x, ok := c.(interface { 37 | CloseWrite() error 38 | }); ok { 39 | return x.CloseWrite() 40 | } else { 41 | log.Debug("force close", c) 42 | return c.Close() 43 | } 44 | } 45 | 46 | func (p *proxyConn) start() { 47 | defer p.lconn.Close() 48 | defer p.rconn.Close() 49 | 50 | // FIXME: may need to set a flag 51 | if tcpconn, ok := p.lconn.(*net.TCPConn); ok { 52 | tcpconn.SetNoDelay(true) 53 | } 54 | if tcpconn, ok := p.rconn.(*net.TCPConn); ok { 55 | tcpconn.SetNoDelay(true) 56 | } 57 | // p.lconn.SetNoDelay(true) 58 | // p.rconn.SetNoDelay(true) 59 | 60 | //display both ends 61 | // log.Printf("Opened %s >>> %s", p.lconn.RemoteAddr().String(), p.rconn.RemoteAddr().String()) 62 | //bidirectional copy 63 | wg := sync.WaitGroup{} 64 | wg.Add(2) 65 | go func() { 66 | ch1 := p.pipe(p.lconn, p.rconn) 67 | <-ch1 68 | closeRead(p.lconn) 69 | closeWrite(p.rconn) 70 | log.Debug("close local -> remote") 71 | wg.Done() 72 | }() 73 | go func() { 74 | ch2 := p.pipe(p.rconn, p.lconn) 75 | <-ch2 76 | closeRead(p.rconn) 77 | closeWrite(p.lconn) 78 | log.Debug("close remote -> local") 79 | wg.Done() 80 | }() 81 | wg.Wait() 82 | //wait for close... 83 | // log.Printf("Closed (%d bytes sent, %d bytes recieved)", p.sentBytes, p.receivedBytes) 84 | } 85 | 86 | func (p *proxyConn) pipe(src, dst net.Conn) chan error { 87 | errch := make(chan error, 1) 88 | islocal := src == p.lconn 89 | 90 | //directional copy (64k buffer) 91 | buff := make([]byte, 0xffff) 92 | go func() { 93 | for { 94 | n, err := src.Read(buff) 95 | if err != nil { 96 | errch <- err 97 | return 98 | } 99 | b := buff[:n] 100 | 101 | //write out result 102 | n, err = dst.Write(b) 103 | if err != nil { 104 | errch <- err 105 | log.Printf("Write failed '%s'\n", err) 106 | return 107 | } 108 | log.Debug("pipe --> local:", islocal, "write:", n) //, string(b[:n])) 109 | if islocal { 110 | p.sentBytes += uint64(n) 111 | p.stats.sentBytes += uint64(n) 112 | } else { 113 | p.receivedBytes += uint64(n) 114 | p.stats.receivedBytes += uint64(n) 115 | } 116 | } 117 | }() 118 | return errch 119 | } 120 | -------------------------------------------------------------------------------- /pxlocal/server.go: -------------------------------------------------------------------------------- 1 | package pxlocal 2 | 3 | import ( 4 | "bufio" 5 | "errors" 6 | "fmt" 7 | "io" 8 | "net" 9 | "net/http" 10 | "net/http/httputil" 11 | "strconv" 12 | "sync" 13 | "time" 14 | 15 | "github.com/gorilla/websocket" 16 | "github.com/qiniu/log" 17 | ) 18 | 19 | // :thinking 20 | // start server HTTP service 21 | // start agent 22 | // - agent connect server with websocket 23 | // - agent convert http request to conn 24 | // need ref: revproxy 25 | 26 | const ( 27 | TCP_MIN_PORT = 40000 28 | TCP_MAX_PORT = 50000 29 | 30 | TYPE_NEWCONN = iota + 1 31 | TYPE_MESSAGE 32 | TYPE_IDLE 33 | ) 34 | 35 | var ( 36 | upgrader = websocket.Upgrader{ 37 | ReadBufferSize: 1024, 38 | WriteBufferSize: 1024, 39 | } 40 | namedConnection = make(map[string]chan net.Conn, 10) 41 | proxyStats = &ProxyStats{} 42 | ) 43 | 44 | type message struct { 45 | Type int 46 | Name string 47 | Body string 48 | } 49 | 50 | type webSocketTunnel struct { 51 | wsconn *websocket.Conn 52 | data string 53 | sync.Mutex 54 | index int64 55 | } 56 | 57 | var freeport = newFreePort(TCP_MIN_PORT, TCP_MAX_PORT) 58 | 59 | func (t *webSocketTunnel) uniqName() string { 60 | t.Lock() 61 | defer t.Unlock() 62 | t.index += 1 63 | return fmt.Sprintf("%d", t.index) 64 | } 65 | 66 | func (t *webSocketTunnel) RequestNewConn(remoteAddr string) (net.Conn, error) { 67 | connC := make(chan net.Conn) 68 | namedConnection[remoteAddr] = connC 69 | defer delete(namedConnection, remoteAddr) 70 | 71 | // request a reverse connection 72 | var msg = message{Type: TYPE_NEWCONN, Name: remoteAddr} 73 | t.wsconn.WriteJSON(msg) 74 | select { 75 | case lconn := <-connC: 76 | if lconn == nil { 77 | return nil, errors.New("maybe hijack not supported, failed") 78 | } 79 | return lconn, nil 80 | case <-time.After(10 * time.Second): 81 | return nil, errors.New("wait reverse connection timeout(10s)") 82 | } 83 | } 84 | 85 | // used for httputil reverse proxy 86 | func (t *webSocketTunnel) generateTransportDial() func(network, addr string) (net.Conn, error) { 87 | return func(network, addr string) (net.Conn, error) { 88 | log.Println("transport", network, addr) 89 | return t.RequestNewConn(t.uniqName()) 90 | } 91 | } 92 | 93 | // Listen and forward connections 94 | func newTcpProxyListener(tunnel *webSocketTunnel, port int) (listener *net.TCPListener, err error) { 95 | var laddr *net.TCPAddr 96 | if port != 0 { 97 | laddr, _ = net.ResolveTCPAddr("tcp", ":"+strconv.Itoa(port)) 98 | listener, err = net.ListenTCP("tcp", laddr) 99 | } else { 100 | laddr, listener, err = freeport.ListenTCP() 101 | } 102 | if err != nil { 103 | return nil, err 104 | } 105 | port = laddr.Port 106 | // hook here 107 | err = hook(HOOK_TCP_POST_CONNECT, []string{ 108 | "PORT=" + strconv.Itoa(port), 109 | "REMOTE_ADDR=" + tunnel.wsconn.RemoteAddr().String(), 110 | "CLIENT_ADDRESS=" + tunnel.wsconn.RemoteAddr().String(), 111 | "REMOTE_DATA=" + tunnel.data, 112 | }) 113 | if err != nil { 114 | listener.Close() 115 | return 116 | } 117 | 118 | go func() { 119 | for { 120 | rconn, err := listener.AcceptTCP() 121 | if err != nil { 122 | log.Warn(err) 123 | break 124 | } 125 | // find proxy to where 126 | log.Debug("Receive new connections from", rconn.RemoteAddr()) 127 | lconn, err := tunnel.RequestNewConn(rconn.RemoteAddr().String()) 128 | if err != nil { 129 | log.Debug("request new conn err:", err) 130 | rconn.Close() 131 | continue 132 | } 133 | 134 | log.Debug("request new conn:", lconn, err) 135 | pc := &proxyConn{ 136 | lconn: lconn, 137 | rconn: rconn, 138 | stats: proxyStats, 139 | } 140 | go pc.start() 141 | } 142 | }() 143 | return listener, nil 144 | } 145 | 146 | type RequestInfo struct { 147 | Protocol string 148 | Subdomain string 149 | Port int 150 | Data string 151 | } 152 | 153 | func parseConnectRequest(r *http.Request) RequestInfo { 154 | protocol := r.FormValue("protocol") 155 | if protocol == "" { 156 | protocol = r.FormValue("protocal") // The last version has type error 157 | } 158 | if protocol == "" { 159 | protocol = "http" 160 | } 161 | 162 | var port int 163 | reqPort := r.FormValue("port") 164 | if reqPort == "" { 165 | port = 0 166 | } else { 167 | fmt.Sscanf(reqPort, "%d", &port) 168 | } 169 | subdomain := r.FormValue("subdomain") 170 | return RequestInfo{ 171 | Protocol: protocol, 172 | Subdomain: subdomain, 173 | Port: port, 174 | Data: r.FormValue("data"), 175 | } 176 | } 177 | 178 | type hijactRW struct { 179 | *net.TCPConn 180 | bufrw *bufio.ReadWriter 181 | } 182 | 183 | func (this *hijactRW) Write(data []byte) (int, error) { 184 | nn, err := this.bufrw.Write(data) 185 | this.bufrw.Flush() 186 | return nn, err 187 | } 188 | 189 | func (this *hijactRW) Read(p []byte) (int, error) { 190 | return this.bufrw.Read(p) 191 | } 192 | 193 | func newHijackReadWriteCloser(conn *net.TCPConn, bufrw *bufio.ReadWriter) net.Conn { 194 | return &hijactRW{ 195 | bufrw: bufrw, 196 | TCPConn: conn, 197 | } 198 | } 199 | 200 | func proxyHandler(w http.ResponseWriter, r *http.Request) { 201 | var proxyFor = r.Header.Get("X-Proxy-For") 202 | log.Infof("[remote %s] X-Proxy-For [%s]", r.RemoteAddr, proxyFor) 203 | 204 | connC, ok := namedConnection[proxyFor] 205 | if !ok { 206 | http.Error(w, "inside error: proxy not ready to receive conn", http.StatusInternalServerError) 207 | return 208 | } 209 | conn, err := hijackHTTPRequest(w) 210 | if err != nil { 211 | log.Warnf("hijeck failed, %v", err) 212 | connC <- nil 213 | return 214 | } 215 | connC <- conn 216 | } 217 | 218 | func hijackHTTPRequest(w http.ResponseWriter) (conn net.Conn, err error) { 219 | hj, ok := w.(http.Hijacker) 220 | if !ok { 221 | err = errors.New("webserver don't support hijacking") 222 | return 223 | } 224 | 225 | hjconn, bufrw, err := hj.Hijack() 226 | if err != nil { 227 | return nil, err 228 | } 229 | conn = newHijackReadWriteCloser(hjconn.(*net.TCPConn), bufrw) 230 | return 231 | } 232 | 233 | type ProxyServer struct { 234 | domain string 235 | *http.ServeMux 236 | revProxies map[string]*httputil.ReverseProxy 237 | sync.RWMutex 238 | } 239 | 240 | func wsSendMessage(conn *websocket.Conn, text string) error { 241 | return conn.WriteJSON(&message{Type: TYPE_MESSAGE, Body: text}) 242 | } 243 | 244 | func (ps *ProxyServer) newHomepageHandler() func(w http.ResponseWriter, r *http.Request) { 245 | return func(w http.ResponseWriter, r *http.Request) { 246 | w.Header().Add("Content-Type", "text/html") 247 | io.WriteString(w, fmt.Sprintf("TCP: recvBytes: %d, sendBytes: %d
", 248 | proxyStats.receivedBytes, proxyStats.sentBytes)) 249 | io.WriteString(w, fmt.Sprintf("HTTP: ...
")) 250 | io.WriteString(w, "
") 251 | for pname, _ := range ps.revProxies { 252 | io.WriteString(w, fmt.Sprintf("http proxy: %s
", pname)) 253 | } 254 | } 255 | } 256 | 257 | func (ps *ProxyServer) newControlHandler() func(w http.ResponseWriter, r *http.Request) { 258 | return func(w http.ResponseWriter, r *http.Request) { 259 | // read listen port from request 260 | //protocol, subdomain, port 261 | reqInfo := parseConnectRequest(r) 262 | log.Debugf("proxy listen proto: %v, subdomain: %v port: %v", 263 | reqInfo.Protocol, reqInfo.Subdomain, reqInfo.Port) 264 | 265 | // create websocket connection 266 | conn, err := upgrader.Upgrade(w, r, nil) 267 | if err != nil { 268 | http.Error(w, err.Error(), 502) 269 | return 270 | } 271 | defer conn.Close() 272 | log.Debug("remote client addr:", conn.RemoteAddr()) 273 | 274 | tunnel := &webSocketTunnel{ 275 | wsconn: conn, 276 | data: reqInfo.Data, 277 | } 278 | // TCP: create new port to listen 279 | log.Infof("New %s proxy for %v", reqInfo.Protocol, conn.RemoteAddr()) 280 | switch reqInfo.Protocol { 281 | case "tcp": 282 | listener, err := newTcpProxyListener(tunnel, reqInfo.Port) 283 | if err != nil { 284 | log.Warnf("new tcp proxy err: %v", err) 285 | http.Error(w, err.Error(), 501) 286 | return 287 | } 288 | defer listener.Close() 289 | _, port, _ := net.SplitHostPort(listener.Addr().String()) 290 | wsSendMessage(conn, fmt.Sprintf("%s:%v", ps.domain, port)) 291 | case "http", "https": 292 | tr := &http.Transport{ 293 | Dial: tunnel.generateTransportDial(), 294 | } 295 | revProxy := &httputil.ReverseProxy{ 296 | Director: func(req *http.Request) { 297 | log.Println("director:", req.RequestURI) 298 | }, 299 | Transport: tr, 300 | } 301 | // should hook here 302 | // hook(HOOK_CREATE_HTTP_SUBDOMAIN, subdomain) 303 | // generate a uniq domain 304 | if reqInfo.Subdomain == "" { 305 | reqInfo.Subdomain = uniqName(5) + ".t" 306 | } 307 | pxDomain := reqInfo.Subdomain + "." + ps.domain 308 | log.Println("http px use domain:", pxDomain) 309 | if _, exists := ps.revProxies[pxDomain]; exists { 310 | wsSendMessage(conn, fmt.Sprintf("subdomain [%s] has already been taken", pxDomain)) 311 | return 312 | } 313 | ps.Lock() 314 | ps.revProxies[pxDomain] = revProxy 315 | ps.Unlock() 316 | wsSendMessage(conn, fmt.Sprintf( 317 | "Local server is now publicly available via:\nhttp://%s\n", pxDomain)) 318 | 319 | defer func() { 320 | ps.Lock() 321 | delete(ps.revProxies, pxDomain) 322 | ps.Unlock() 323 | }() 324 | default: 325 | log.Warn("unknown protocol:", reqInfo.Protocol) 326 | return 327 | } 328 | // HTTP: use httputil.ReverseProxy 329 | for { 330 | var msg message 331 | if err := conn.ReadJSON(&msg); err != nil { 332 | log.Warn(err) 333 | break 334 | } 335 | log.Debug("recv json:", msg) 336 | } 337 | } 338 | } 339 | 340 | func (p *ProxyServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { 341 | // http://stackoverflow.com/questions/6899069/why-are-request-url-host-and-scheme-blank-in-the-development-server 342 | r.URL.Scheme = "http" // ?? 343 | r.URL.Host = r.Host // ?? 344 | log.Debug("URL path:", r.URL.Path) 345 | log.Debugf("proxy lists: %v", p.revProxies) 346 | if rpx, ok := p.revProxies[r.Host]; ok { 347 | log.Debug("server http rev proxy") 348 | rpx.ServeHTTP(w, r) 349 | return 350 | } 351 | 352 | h, _ := p.Handler(r) 353 | h.ServeHTTP(w, r) 354 | } 355 | 356 | // domain, ex shengxiang.me 357 | // dns should set *.shengxiang.me 358 | func NewProxyServer(domain string) *ProxyServer { 359 | if domain == "" { 360 | domain = "localhost" 361 | } 362 | p := &ProxyServer{ 363 | domain: domain, 364 | ServeMux: http.NewServeMux(), 365 | revProxies: make(map[string]*httputil.ReverseProxy), 366 | } 367 | p.HandleFunc("/", p.newHomepageHandler()) 368 | p.HandleFunc("/ws", p.newControlHandler()) 369 | p.HandleFunc("/proxyhijack", proxyHandler) 370 | 371 | return p 372 | } 373 | -------------------------------------------------------------------------------- /pxlocal/utils.go: -------------------------------------------------------------------------------- 1 | package pxlocal 2 | 3 | import ( 4 | "math/rand" 5 | "net" 6 | "net/url" 7 | "regexp" 8 | "strconv" 9 | "time" 10 | ) 11 | 12 | func init() { 13 | rand.Seed(time.Now().UnixNano()) 14 | } 15 | 16 | var uniqMap = make(map[string]bool) 17 | var letterRunes = []rune("abcdefghijklmnopqrstuvwxyz1234567890") //ABCDEFGHIJKLMNOPQRSTUVWXYZ") 18 | 19 | func uniqName(n int) string { 20 | for { 21 | b := make([]rune, n) 22 | for i := range b { 23 | b[i] = letterRunes[rand.Intn(len(letterRunes))] 24 | } 25 | s := string(b) 26 | 27 | if uniqMap[s] { 28 | continue 29 | } 30 | uniqMap[s] = true 31 | return s 32 | } 33 | } 34 | 35 | type URLOpts struct { 36 | DefaultScheme string 37 | DefaultHost string 38 | DefaultPort int 39 | } 40 | 41 | func ParseURL(s string, opts ...URLOpts) (u *url.URL, err error) { 42 | opt := URLOpts{ 43 | DefaultScheme: "http", 44 | DefaultHost: "localhost", 45 | DefaultPort: 80, 46 | } 47 | if len(opts) > 0 { 48 | opt = opts[0] 49 | if opt.DefaultHost == "" { 50 | opt.DefaultHost = "localhost" 51 | } 52 | if opt.DefaultPort == 0 { 53 | opt.DefaultPort = 80 54 | } 55 | if opt.DefaultScheme == "" { 56 | opt.DefaultScheme = "http" 57 | } 58 | } 59 | 60 | if !regexp.MustCompile(`^(\w+)://`).MatchString(s) { 61 | if _, er := strconv.Atoi(s); er == nil { // only contain port 62 | s = opt.DefaultHost + ":" + s 63 | } 64 | if _, _, er := net.SplitHostPort(s); er != nil { 65 | s = s + ":" + strconv.Itoa(opt.DefaultPort) 66 | } 67 | s = opt.DefaultScheme + "://" + s 68 | } 69 | u, err = url.Parse(s) 70 | if err != nil { 71 | return 72 | } 73 | // must contains port 74 | if _, _, er := net.SplitHostPort(u.Host); er != nil { 75 | u.Host += ":" + strconv.Itoa(opt.DefaultPort) 76 | } 77 | return 78 | } 79 | -------------------------------------------------------------------------------- /pxlocal/utils_test.go: -------------------------------------------------------------------------------- 1 | package pxlocal 2 | 3 | import ( 4 | "net" 5 | "testing" 6 | 7 | . "github.com/smartystreets/goconvey/convey" 8 | ) 9 | 10 | func TestParseURL(t *testing.T) { 11 | opt := URLOpts{ 12 | DefaultScheme: "test", 13 | DefaultHost: "local-test", 14 | DefaultPort: 443, 15 | } 16 | validAddrs := []string{ 17 | "80", 18 | "local-test", 19 | "test://local-test", 20 | } 21 | for _, addr := range validAddrs { 22 | Convey("Should parse "+addr, t, func() { 23 | u, err := ParseURL(addr, opt) 24 | So(err, ShouldBeNil) 25 | So(u.Scheme, ShouldEqual, "test") 26 | host, _, err := net.SplitHostPort(u.Host) 27 | So(err, ShouldBeNil) 28 | So(host, ShouldEqual, "local-test") 29 | }) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/template/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/template/README.md: -------------------------------------------------------------------------------- 1 | # Go's `text/template` package with newline elision 2 | 3 | This is a fork of Go 1.4's [text/template](http://golang.org/pkg/text/template/) package with one addition: a backslash immediately after a closing delimiter will delete all subsequent newlines until a non-newline. 4 | 5 | eg. 6 | 7 | ``` 8 | {{if true}}\ 9 | hello 10 | {{end}}\ 11 | ``` 12 | 13 | Will result in: 14 | 15 | ``` 16 | hello\n 17 | ``` 18 | 19 | Rather than: 20 | 21 | ``` 22 | \n 23 | hello\n 24 | \n 25 | ``` 26 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/template/helper.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go 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 | // Helper functions to make constructing templates easier. 6 | 7 | package template 8 | 9 | import ( 10 | "fmt" 11 | "io/ioutil" 12 | "path/filepath" 13 | ) 14 | 15 | // Functions and methods to parse templates. 16 | 17 | // Must is a helper that wraps a call to a function returning (*Template, error) 18 | // and panics if the error is non-nil. It is intended for use in variable 19 | // initializations such as 20 | // var t = template.Must(template.New("name").Parse("text")) 21 | func Must(t *Template, err error) *Template { 22 | if err != nil { 23 | panic(err) 24 | } 25 | return t 26 | } 27 | 28 | // ParseFiles creates a new Template and parses the template definitions from 29 | // the named files. The returned template's name will have the (base) name and 30 | // (parsed) contents of the first file. There must be at least one file. 31 | // If an error occurs, parsing stops and the returned *Template is nil. 32 | func ParseFiles(filenames ...string) (*Template, error) { 33 | return parseFiles(nil, filenames...) 34 | } 35 | 36 | // ParseFiles parses the named files and associates the resulting templates with 37 | // t. If an error occurs, parsing stops and the returned template is nil; 38 | // otherwise it is t. There must be at least one file. 39 | func (t *Template) ParseFiles(filenames ...string) (*Template, error) { 40 | return parseFiles(t, filenames...) 41 | } 42 | 43 | // parseFiles is the helper for the method and function. If the argument 44 | // template is nil, it is created from the first file. 45 | func parseFiles(t *Template, filenames ...string) (*Template, error) { 46 | if len(filenames) == 0 { 47 | // Not really a problem, but be consistent. 48 | return nil, fmt.Errorf("template: no files named in call to ParseFiles") 49 | } 50 | for _, filename := range filenames { 51 | b, err := ioutil.ReadFile(filename) 52 | if err != nil { 53 | return nil, err 54 | } 55 | s := string(b) 56 | name := filepath.Base(filename) 57 | // First template becomes return value if not already defined, 58 | // and we use that one for subsequent New calls to associate 59 | // all the templates together. Also, if this file has the same name 60 | // as t, this file becomes the contents of t, so 61 | // t, err := New(name).Funcs(xxx).ParseFiles(name) 62 | // works. Otherwise we create a new template associated with t. 63 | var tmpl *Template 64 | if t == nil { 65 | t = New(name) 66 | } 67 | if name == t.Name() { 68 | tmpl = t 69 | } else { 70 | tmpl = t.New(name) 71 | } 72 | _, err = tmpl.Parse(s) 73 | if err != nil { 74 | return nil, err 75 | } 76 | } 77 | return t, nil 78 | } 79 | 80 | // ParseGlob creates a new Template and parses the template definitions from the 81 | // files identified by the pattern, which must match at least one file. The 82 | // returned template will have the (base) name and (parsed) contents of the 83 | // first file matched by the pattern. ParseGlob is equivalent to calling 84 | // ParseFiles with the list of files matched by the pattern. 85 | func ParseGlob(pattern string) (*Template, error) { 86 | return parseGlob(nil, pattern) 87 | } 88 | 89 | // ParseGlob parses the template definitions in the files identified by the 90 | // pattern and associates the resulting templates with t. The pattern is 91 | // processed by filepath.Glob and must match at least one file. ParseGlob is 92 | // equivalent to calling t.ParseFiles with the list of files matched by the 93 | // pattern. 94 | func (t *Template) ParseGlob(pattern string) (*Template, error) { 95 | return parseGlob(t, pattern) 96 | } 97 | 98 | // parseGlob is the implementation of the function and method ParseGlob. 99 | func parseGlob(t *Template, pattern string) (*Template, error) { 100 | filenames, err := filepath.Glob(pattern) 101 | if err != nil { 102 | return nil, err 103 | } 104 | if len(filenames) == 0 { 105 | return nil, fmt.Errorf("template: pattern matches no files: %#q", pattern) 106 | } 107 | return parseFiles(t, filenames...) 108 | } 109 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/template/parse/lex.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go 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 parse 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "unicode" 11 | "unicode/utf8" 12 | ) 13 | 14 | // item represents a token or text string returned from the scanner. 15 | type item struct { 16 | typ itemType // The type of this item. 17 | pos Pos // The starting position, in bytes, of this item in the input string. 18 | val string // The value of this item. 19 | } 20 | 21 | func (i item) String() string { 22 | switch { 23 | case i.typ == itemEOF: 24 | return "EOF" 25 | case i.typ == itemError: 26 | return i.val 27 | case i.typ > itemKeyword: 28 | return fmt.Sprintf("<%s>", i.val) 29 | case len(i.val) > 10: 30 | return fmt.Sprintf("%.10q...", i.val) 31 | } 32 | return fmt.Sprintf("%q", i.val) 33 | } 34 | 35 | // itemType identifies the type of lex items. 36 | type itemType int 37 | 38 | const ( 39 | itemError itemType = iota // error occurred; value is text of error 40 | itemBool // boolean constant 41 | itemChar // printable ASCII character; grab bag for comma etc. 42 | itemCharConstant // character constant 43 | itemComplex // complex constant (1+2i); imaginary is just a number 44 | itemColonEquals // colon-equals (':=') introducing a declaration 45 | itemEOF 46 | itemField // alphanumeric identifier starting with '.' 47 | itemIdentifier // alphanumeric identifier not starting with '.' 48 | itemLeftDelim // left action delimiter 49 | itemLeftParen // '(' inside action 50 | itemNumber // simple number, including imaginary 51 | itemPipe // pipe symbol 52 | itemRawString // raw quoted string (includes quotes) 53 | itemRightDelim // right action delimiter 54 | itemElideNewline // elide newline after right delim 55 | itemRightParen // ')' inside action 56 | itemSpace // run of spaces separating arguments 57 | itemString // quoted string (includes quotes) 58 | itemText // plain text 59 | itemVariable // variable starting with '$', such as '$' or '$1' or '$hello' 60 | // Keywords appear after all the rest. 61 | itemKeyword // used only to delimit the keywords 62 | itemDot // the cursor, spelled '.' 63 | itemDefine // define keyword 64 | itemElse // else keyword 65 | itemEnd // end keyword 66 | itemIf // if keyword 67 | itemNil // the untyped nil constant, easiest to treat as a keyword 68 | itemRange // range keyword 69 | itemTemplate // template keyword 70 | itemWith // with keyword 71 | ) 72 | 73 | var key = map[string]itemType{ 74 | ".": itemDot, 75 | "define": itemDefine, 76 | "else": itemElse, 77 | "end": itemEnd, 78 | "if": itemIf, 79 | "range": itemRange, 80 | "nil": itemNil, 81 | "template": itemTemplate, 82 | "with": itemWith, 83 | } 84 | 85 | const eof = -1 86 | 87 | // stateFn represents the state of the scanner as a function that returns the next state. 88 | type stateFn func(*lexer) stateFn 89 | 90 | // lexer holds the state of the scanner. 91 | type lexer struct { 92 | name string // the name of the input; used only for error reports 93 | input string // the string being scanned 94 | leftDelim string // start of action 95 | rightDelim string // end of action 96 | state stateFn // the next lexing function to enter 97 | pos Pos // current position in the input 98 | start Pos // start position of this item 99 | width Pos // width of last rune read from input 100 | lastPos Pos // position of most recent item returned by nextItem 101 | items chan item // channel of scanned items 102 | parenDepth int // nesting depth of ( ) exprs 103 | } 104 | 105 | // next returns the next rune in the input. 106 | func (l *lexer) next() rune { 107 | if int(l.pos) >= len(l.input) { 108 | l.width = 0 109 | return eof 110 | } 111 | r, w := utf8.DecodeRuneInString(l.input[l.pos:]) 112 | l.width = Pos(w) 113 | l.pos += l.width 114 | return r 115 | } 116 | 117 | // peek returns but does not consume the next rune in the input. 118 | func (l *lexer) peek() rune { 119 | r := l.next() 120 | l.backup() 121 | return r 122 | } 123 | 124 | // backup steps back one rune. Can only be called once per call of next. 125 | func (l *lexer) backup() { 126 | l.pos -= l.width 127 | } 128 | 129 | // emit passes an item back to the client. 130 | func (l *lexer) emit(t itemType) { 131 | l.items <- item{t, l.start, l.input[l.start:l.pos]} 132 | l.start = l.pos 133 | } 134 | 135 | // ignore skips over the pending input before this point. 136 | func (l *lexer) ignore() { 137 | l.start = l.pos 138 | } 139 | 140 | // accept consumes the next rune if it's from the valid set. 141 | func (l *lexer) accept(valid string) bool { 142 | if strings.IndexRune(valid, l.next()) >= 0 { 143 | return true 144 | } 145 | l.backup() 146 | return false 147 | } 148 | 149 | // acceptRun consumes a run of runes from the valid set. 150 | func (l *lexer) acceptRun(valid string) { 151 | for strings.IndexRune(valid, l.next()) >= 0 { 152 | } 153 | l.backup() 154 | } 155 | 156 | // lineNumber reports which line we're on, based on the position of 157 | // the previous item returned by nextItem. Doing it this way 158 | // means we don't have to worry about peek double counting. 159 | func (l *lexer) lineNumber() int { 160 | return 1 + strings.Count(l.input[:l.lastPos], "\n") 161 | } 162 | 163 | // errorf returns an error token and terminates the scan by passing 164 | // back a nil pointer that will be the next state, terminating l.nextItem. 165 | func (l *lexer) errorf(format string, args ...interface{}) stateFn { 166 | l.items <- item{itemError, l.start, fmt.Sprintf(format, args...)} 167 | return nil 168 | } 169 | 170 | // nextItem returns the next item from the input. 171 | func (l *lexer) nextItem() item { 172 | item := <-l.items 173 | l.lastPos = item.pos 174 | return item 175 | } 176 | 177 | // lex creates a new scanner for the input string. 178 | func lex(name, input, left, right string) *lexer { 179 | if left == "" { 180 | left = leftDelim 181 | } 182 | if right == "" { 183 | right = rightDelim 184 | } 185 | l := &lexer{ 186 | name: name, 187 | input: input, 188 | leftDelim: left, 189 | rightDelim: right, 190 | items: make(chan item), 191 | } 192 | go l.run() 193 | return l 194 | } 195 | 196 | // run runs the state machine for the lexer. 197 | func (l *lexer) run() { 198 | for l.state = lexText; l.state != nil; { 199 | l.state = l.state(l) 200 | } 201 | } 202 | 203 | // state functions 204 | 205 | const ( 206 | leftDelim = "{{" 207 | rightDelim = "}}" 208 | leftComment = "/*" 209 | rightComment = "*/" 210 | ) 211 | 212 | // lexText scans until an opening action delimiter, "{{". 213 | func lexText(l *lexer) stateFn { 214 | for { 215 | if strings.HasPrefix(l.input[l.pos:], l.leftDelim) { 216 | if l.pos > l.start { 217 | l.emit(itemText) 218 | } 219 | return lexLeftDelim 220 | } 221 | if l.next() == eof { 222 | break 223 | } 224 | } 225 | // Correctly reached EOF. 226 | if l.pos > l.start { 227 | l.emit(itemText) 228 | } 229 | l.emit(itemEOF) 230 | return nil 231 | } 232 | 233 | // lexLeftDelim scans the left delimiter, which is known to be present. 234 | func lexLeftDelim(l *lexer) stateFn { 235 | l.pos += Pos(len(l.leftDelim)) 236 | if strings.HasPrefix(l.input[l.pos:], leftComment) { 237 | return lexComment 238 | } 239 | l.emit(itemLeftDelim) 240 | l.parenDepth = 0 241 | return lexInsideAction 242 | } 243 | 244 | // lexComment scans a comment. The left comment marker is known to be present. 245 | func lexComment(l *lexer) stateFn { 246 | l.pos += Pos(len(leftComment)) 247 | i := strings.Index(l.input[l.pos:], rightComment) 248 | if i < 0 { 249 | return l.errorf("unclosed comment") 250 | } 251 | l.pos += Pos(i + len(rightComment)) 252 | if !strings.HasPrefix(l.input[l.pos:], l.rightDelim) { 253 | return l.errorf("comment ends before closing delimiter") 254 | 255 | } 256 | l.pos += Pos(len(l.rightDelim)) 257 | l.ignore() 258 | return lexText 259 | } 260 | 261 | // lexRightDelim scans the right delimiter, which is known to be present. 262 | func lexRightDelim(l *lexer) stateFn { 263 | l.pos += Pos(len(l.rightDelim)) 264 | l.emit(itemRightDelim) 265 | if l.peek() == '\\' { 266 | l.pos++ 267 | l.emit(itemElideNewline) 268 | } 269 | return lexText 270 | } 271 | 272 | // lexInsideAction scans the elements inside action delimiters. 273 | func lexInsideAction(l *lexer) stateFn { 274 | // Either number, quoted string, or identifier. 275 | // Spaces separate arguments; runs of spaces turn into itemSpace. 276 | // Pipe symbols separate and are emitted. 277 | if strings.HasPrefix(l.input[l.pos:], l.rightDelim+"\\") || strings.HasPrefix(l.input[l.pos:], l.rightDelim) { 278 | if l.parenDepth == 0 { 279 | return lexRightDelim 280 | } 281 | return l.errorf("unclosed left paren") 282 | } 283 | switch r := l.next(); { 284 | case r == eof || isEndOfLine(r): 285 | return l.errorf("unclosed action") 286 | case isSpace(r): 287 | return lexSpace 288 | case r == ':': 289 | if l.next() != '=' { 290 | return l.errorf("expected :=") 291 | } 292 | l.emit(itemColonEquals) 293 | case r == '|': 294 | l.emit(itemPipe) 295 | case r == '"': 296 | return lexQuote 297 | case r == '`': 298 | return lexRawQuote 299 | case r == '$': 300 | return lexVariable 301 | case r == '\'': 302 | return lexChar 303 | case r == '.': 304 | // special look-ahead for ".field" so we don't break l.backup(). 305 | if l.pos < Pos(len(l.input)) { 306 | r := l.input[l.pos] 307 | if r < '0' || '9' < r { 308 | return lexField 309 | } 310 | } 311 | fallthrough // '.' can start a number. 312 | case r == '+' || r == '-' || ('0' <= r && r <= '9'): 313 | l.backup() 314 | return lexNumber 315 | case isAlphaNumeric(r): 316 | l.backup() 317 | return lexIdentifier 318 | case r == '(': 319 | l.emit(itemLeftParen) 320 | l.parenDepth++ 321 | return lexInsideAction 322 | case r == ')': 323 | l.emit(itemRightParen) 324 | l.parenDepth-- 325 | if l.parenDepth < 0 { 326 | return l.errorf("unexpected right paren %#U", r) 327 | } 328 | return lexInsideAction 329 | case r <= unicode.MaxASCII && unicode.IsPrint(r): 330 | l.emit(itemChar) 331 | return lexInsideAction 332 | default: 333 | return l.errorf("unrecognized character in action: %#U", r) 334 | } 335 | return lexInsideAction 336 | } 337 | 338 | // lexSpace scans a run of space characters. 339 | // One space has already been seen. 340 | func lexSpace(l *lexer) stateFn { 341 | for isSpace(l.peek()) { 342 | l.next() 343 | } 344 | l.emit(itemSpace) 345 | return lexInsideAction 346 | } 347 | 348 | // lexIdentifier scans an alphanumeric. 349 | func lexIdentifier(l *lexer) stateFn { 350 | Loop: 351 | for { 352 | switch r := l.next(); { 353 | case isAlphaNumeric(r): 354 | // absorb. 355 | default: 356 | l.backup() 357 | word := l.input[l.start:l.pos] 358 | if !l.atTerminator() { 359 | return l.errorf("bad character %#U", r) 360 | } 361 | switch { 362 | case key[word] > itemKeyword: 363 | l.emit(key[word]) 364 | case word[0] == '.': 365 | l.emit(itemField) 366 | case word == "true", word == "false": 367 | l.emit(itemBool) 368 | default: 369 | l.emit(itemIdentifier) 370 | } 371 | break Loop 372 | } 373 | } 374 | return lexInsideAction 375 | } 376 | 377 | // lexField scans a field: .Alphanumeric. 378 | // The . has been scanned. 379 | func lexField(l *lexer) stateFn { 380 | return lexFieldOrVariable(l, itemField) 381 | } 382 | 383 | // lexVariable scans a Variable: $Alphanumeric. 384 | // The $ has been scanned. 385 | func lexVariable(l *lexer) stateFn { 386 | if l.atTerminator() { // Nothing interesting follows -> "$". 387 | l.emit(itemVariable) 388 | return lexInsideAction 389 | } 390 | return lexFieldOrVariable(l, itemVariable) 391 | } 392 | 393 | // lexVariable scans a field or variable: [.$]Alphanumeric. 394 | // The . or $ has been scanned. 395 | func lexFieldOrVariable(l *lexer, typ itemType) stateFn { 396 | if l.atTerminator() { // Nothing interesting follows -> "." or "$". 397 | if typ == itemVariable { 398 | l.emit(itemVariable) 399 | } else { 400 | l.emit(itemDot) 401 | } 402 | return lexInsideAction 403 | } 404 | var r rune 405 | for { 406 | r = l.next() 407 | if !isAlphaNumeric(r) { 408 | l.backup() 409 | break 410 | } 411 | } 412 | if !l.atTerminator() { 413 | return l.errorf("bad character %#U", r) 414 | } 415 | l.emit(typ) 416 | return lexInsideAction 417 | } 418 | 419 | // atTerminator reports whether the input is at valid termination character to 420 | // appear after an identifier. Breaks .X.Y into two pieces. Also catches cases 421 | // like "$x+2" not being acceptable without a space, in case we decide one 422 | // day to implement arithmetic. 423 | func (l *lexer) atTerminator() bool { 424 | r := l.peek() 425 | if isSpace(r) || isEndOfLine(r) { 426 | return true 427 | } 428 | switch r { 429 | case eof, '.', ',', '|', ':', ')', '(': 430 | return true 431 | } 432 | // Does r start the delimiter? This can be ambiguous (with delim=="//", $x/2 will 433 | // succeed but should fail) but only in extremely rare cases caused by willfully 434 | // bad choice of delimiter. 435 | if rd, _ := utf8.DecodeRuneInString(l.rightDelim); rd == r { 436 | return true 437 | } 438 | return false 439 | } 440 | 441 | // lexChar scans a character constant. The initial quote is already 442 | // scanned. Syntax checking is done by the parser. 443 | func lexChar(l *lexer) stateFn { 444 | Loop: 445 | for { 446 | switch l.next() { 447 | case '\\': 448 | if r := l.next(); r != eof && r != '\n' { 449 | break 450 | } 451 | fallthrough 452 | case eof, '\n': 453 | return l.errorf("unterminated character constant") 454 | case '\'': 455 | break Loop 456 | } 457 | } 458 | l.emit(itemCharConstant) 459 | return lexInsideAction 460 | } 461 | 462 | // lexNumber scans a number: decimal, octal, hex, float, or imaginary. This 463 | // isn't a perfect number scanner - for instance it accepts "." and "0x0.2" 464 | // and "089" - but when it's wrong the input is invalid and the parser (via 465 | // strconv) will notice. 466 | func lexNumber(l *lexer) stateFn { 467 | if !l.scanNumber() { 468 | return l.errorf("bad number syntax: %q", l.input[l.start:l.pos]) 469 | } 470 | if sign := l.peek(); sign == '+' || sign == '-' { 471 | // Complex: 1+2i. No spaces, must end in 'i'. 472 | if !l.scanNumber() || l.input[l.pos-1] != 'i' { 473 | return l.errorf("bad number syntax: %q", l.input[l.start:l.pos]) 474 | } 475 | l.emit(itemComplex) 476 | } else { 477 | l.emit(itemNumber) 478 | } 479 | return lexInsideAction 480 | } 481 | 482 | func (l *lexer) scanNumber() bool { 483 | // Optional leading sign. 484 | l.accept("+-") 485 | // Is it hex? 486 | digits := "0123456789" 487 | if l.accept("0") && l.accept("xX") { 488 | digits = "0123456789abcdefABCDEF" 489 | } 490 | l.acceptRun(digits) 491 | if l.accept(".") { 492 | l.acceptRun(digits) 493 | } 494 | if l.accept("eE") { 495 | l.accept("+-") 496 | l.acceptRun("0123456789") 497 | } 498 | // Is it imaginary? 499 | l.accept("i") 500 | // Next thing mustn't be alphanumeric. 501 | if isAlphaNumeric(l.peek()) { 502 | l.next() 503 | return false 504 | } 505 | return true 506 | } 507 | 508 | // lexQuote scans a quoted string. 509 | func lexQuote(l *lexer) stateFn { 510 | Loop: 511 | for { 512 | switch l.next() { 513 | case '\\': 514 | if r := l.next(); r != eof && r != '\n' { 515 | break 516 | } 517 | fallthrough 518 | case eof, '\n': 519 | return l.errorf("unterminated quoted string") 520 | case '"': 521 | break Loop 522 | } 523 | } 524 | l.emit(itemString) 525 | return lexInsideAction 526 | } 527 | 528 | // lexRawQuote scans a raw quoted string. 529 | func lexRawQuote(l *lexer) stateFn { 530 | Loop: 531 | for { 532 | switch l.next() { 533 | case eof, '\n': 534 | return l.errorf("unterminated raw quoted string") 535 | case '`': 536 | break Loop 537 | } 538 | } 539 | l.emit(itemRawString) 540 | return lexInsideAction 541 | } 542 | 543 | // isSpace reports whether r is a space character. 544 | func isSpace(r rune) bool { 545 | return r == ' ' || r == '\t' 546 | } 547 | 548 | // isEndOfLine reports whether r is an end-of-line character. 549 | func isEndOfLine(r rune) bool { 550 | return r == '\r' || r == '\n' 551 | } 552 | 553 | // isAlphaNumeric reports whether r is an alphabetic, digit, or underscore. 554 | func isAlphaNumeric(r rune) bool { 555 | return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r) 556 | } 557 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/template/template.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go 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 template 6 | 7 | import ( 8 | "fmt" 9 | "reflect" 10 | 11 | "github.com/alecthomas/template/parse" 12 | ) 13 | 14 | // common holds the information shared by related templates. 15 | type common struct { 16 | tmpl map[string]*Template 17 | // We use two maps, one for parsing and one for execution. 18 | // This separation makes the API cleaner since it doesn't 19 | // expose reflection to the client. 20 | parseFuncs FuncMap 21 | execFuncs map[string]reflect.Value 22 | } 23 | 24 | // Template is the representation of a parsed template. The *parse.Tree 25 | // field is exported only for use by html/template and should be treated 26 | // as unexported by all other clients. 27 | type Template struct { 28 | name string 29 | *parse.Tree 30 | *common 31 | leftDelim string 32 | rightDelim string 33 | } 34 | 35 | // New allocates a new template with the given name. 36 | func New(name string) *Template { 37 | return &Template{ 38 | name: name, 39 | } 40 | } 41 | 42 | // Name returns the name of the template. 43 | func (t *Template) Name() string { 44 | return t.name 45 | } 46 | 47 | // New allocates a new template associated with the given one and with the same 48 | // delimiters. The association, which is transitive, allows one template to 49 | // invoke another with a {{template}} action. 50 | func (t *Template) New(name string) *Template { 51 | t.init() 52 | return &Template{ 53 | name: name, 54 | common: t.common, 55 | leftDelim: t.leftDelim, 56 | rightDelim: t.rightDelim, 57 | } 58 | } 59 | 60 | func (t *Template) init() { 61 | if t.common == nil { 62 | t.common = new(common) 63 | t.tmpl = make(map[string]*Template) 64 | t.parseFuncs = make(FuncMap) 65 | t.execFuncs = make(map[string]reflect.Value) 66 | } 67 | } 68 | 69 | // Clone returns a duplicate of the template, including all associated 70 | // templates. The actual representation is not copied, but the name space of 71 | // associated templates is, so further calls to Parse in the copy will add 72 | // templates to the copy but not to the original. Clone can be used to prepare 73 | // common templates and use them with variant definitions for other templates 74 | // by adding the variants after the clone is made. 75 | func (t *Template) Clone() (*Template, error) { 76 | nt := t.copy(nil) 77 | nt.init() 78 | nt.tmpl[t.name] = nt 79 | for k, v := range t.tmpl { 80 | if k == t.name { // Already installed. 81 | continue 82 | } 83 | // The associated templates share nt's common structure. 84 | tmpl := v.copy(nt.common) 85 | nt.tmpl[k] = tmpl 86 | } 87 | for k, v := range t.parseFuncs { 88 | nt.parseFuncs[k] = v 89 | } 90 | for k, v := range t.execFuncs { 91 | nt.execFuncs[k] = v 92 | } 93 | return nt, nil 94 | } 95 | 96 | // copy returns a shallow copy of t, with common set to the argument. 97 | func (t *Template) copy(c *common) *Template { 98 | nt := New(t.name) 99 | nt.Tree = t.Tree 100 | nt.common = c 101 | nt.leftDelim = t.leftDelim 102 | nt.rightDelim = t.rightDelim 103 | return nt 104 | } 105 | 106 | // AddParseTree creates a new template with the name and parse tree 107 | // and associates it with t. 108 | func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error) { 109 | if t.common != nil && t.tmpl[name] != nil { 110 | return nil, fmt.Errorf("template: redefinition of template %q", name) 111 | } 112 | nt := t.New(name) 113 | nt.Tree = tree 114 | t.tmpl[name] = nt 115 | return nt, nil 116 | } 117 | 118 | // Templates returns a slice of the templates associated with t, including t 119 | // itself. 120 | func (t *Template) Templates() []*Template { 121 | if t.common == nil { 122 | return nil 123 | } 124 | // Return a slice so we don't expose the map. 125 | m := make([]*Template, 0, len(t.tmpl)) 126 | for _, v := range t.tmpl { 127 | m = append(m, v) 128 | } 129 | return m 130 | } 131 | 132 | // Delims sets the action delimiters to the specified strings, to be used in 133 | // subsequent calls to Parse, ParseFiles, or ParseGlob. Nested template 134 | // definitions will inherit the settings. An empty delimiter stands for the 135 | // corresponding default: {{ or }}. 136 | // The return value is the template, so calls can be chained. 137 | func (t *Template) Delims(left, right string) *Template { 138 | t.leftDelim = left 139 | t.rightDelim = right 140 | return t 141 | } 142 | 143 | // Funcs adds the elements of the argument map to the template's function map. 144 | // It panics if a value in the map is not a function with appropriate return 145 | // type. However, it is legal to overwrite elements of the map. The return 146 | // value is the template, so calls can be chained. 147 | func (t *Template) Funcs(funcMap FuncMap) *Template { 148 | t.init() 149 | addValueFuncs(t.execFuncs, funcMap) 150 | addFuncs(t.parseFuncs, funcMap) 151 | return t 152 | } 153 | 154 | // Lookup returns the template with the given name that is associated with t, 155 | // or nil if there is no such template. 156 | func (t *Template) Lookup(name string) *Template { 157 | if t.common == nil { 158 | return nil 159 | } 160 | return t.tmpl[name] 161 | } 162 | 163 | // Parse parses a string into a template. Nested template definitions will be 164 | // associated with the top-level template t. Parse may be called multiple times 165 | // to parse definitions of templates to associate with t. It is an error if a 166 | // resulting template is non-empty (contains content other than template 167 | // definitions) and would replace a non-empty template with the same name. 168 | // (In multiple calls to Parse with the same receiver template, only one call 169 | // can contain text other than space, comments, and template definitions.) 170 | func (t *Template) Parse(text string) (*Template, error) { 171 | t.init() 172 | trees, err := parse.Parse(t.name, text, t.leftDelim, t.rightDelim, t.parseFuncs, builtins) 173 | if err != nil { 174 | return nil, err 175 | } 176 | // Add the newly parsed trees, including the one for t, into our common structure. 177 | for name, tree := range trees { 178 | // If the name we parsed is the name of this template, overwrite this template. 179 | // The associate method checks it's not a redefinition. 180 | tmpl := t 181 | if name != t.name { 182 | tmpl = t.New(name) 183 | } 184 | // Even if t == tmpl, we need to install it in the common.tmpl map. 185 | if replace, err := t.associate(tmpl, tree); err != nil { 186 | return nil, err 187 | } else if replace { 188 | tmpl.Tree = tree 189 | } 190 | tmpl.leftDelim = t.leftDelim 191 | tmpl.rightDelim = t.rightDelim 192 | } 193 | return t, nil 194 | } 195 | 196 | // associate installs the new template into the group of templates associated 197 | // with t. It is an error to reuse a name except to overwrite an empty 198 | // template. The two are already known to share the common structure. 199 | // The boolean return value reports wither to store this tree as t.Tree. 200 | func (t *Template) associate(new *Template, tree *parse.Tree) (bool, error) { 201 | if new.common != t.common { 202 | panic("internal error: associate not common") 203 | } 204 | name := new.name 205 | if old := t.tmpl[name]; old != nil { 206 | oldIsEmpty := parse.IsEmptyTree(old.Root) 207 | newIsEmpty := parse.IsEmptyTree(tree.Root) 208 | if newIsEmpty { 209 | // Whether old is empty or not, new is empty; no reason to replace old. 210 | return false, nil 211 | } 212 | if !oldIsEmpty { 213 | return false, fmt.Errorf("template: redefinition of template %q", name) 214 | } 215 | } 216 | t.tmpl[name] = new 217 | return true, nil 218 | } 219 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/units/COPYING: -------------------------------------------------------------------------------- 1 | Copyright (C) 2014 Alec Thomas 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/units/README.md: -------------------------------------------------------------------------------- 1 | # Units - Helpful unit multipliers and functions for Go 2 | 3 | The goal of this package is to have functionality similar to the [time](http://golang.org/pkg/time/) package. 4 | 5 | It allows for code like this: 6 | 7 | ```go 8 | n, err := ParseBase2Bytes("1KB") 9 | // n == 1024 10 | n = units.Mebibyte * 512 11 | ``` 12 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/units/bytes.go: -------------------------------------------------------------------------------- 1 | package units 2 | 3 | // Base2Bytes is the old non-SI power-of-2 byte scale (1024 bytes in a kilobyte, 4 | // etc.). 5 | type Base2Bytes int64 6 | 7 | // Base-2 byte units. 8 | const ( 9 | Kibibyte Base2Bytes = 1024 10 | KiB = Kibibyte 11 | Mebibyte = Kibibyte * 1024 12 | MiB = Mebibyte 13 | Gibibyte = Mebibyte * 1024 14 | GiB = Gibibyte 15 | Tebibyte = Gibibyte * 1024 16 | TiB = Tebibyte 17 | Pebibyte = Tebibyte * 1024 18 | PiB = Pebibyte 19 | Exbibyte = Pebibyte * 1024 20 | EiB = Exbibyte 21 | ) 22 | 23 | var ( 24 | bytesUnitMap = MakeUnitMap("iB", "B", 1024) 25 | oldBytesUnitMap = MakeUnitMap("B", "B", 1024) 26 | ) 27 | 28 | // ParseBase2Bytes supports both iB and B in base-2 multipliers. That is, KB 29 | // and KiB are both 1024. 30 | func ParseBase2Bytes(s string) (Base2Bytes, error) { 31 | n, err := ParseUnit(s, bytesUnitMap) 32 | if err != nil { 33 | n, err = ParseUnit(s, oldBytesUnitMap) 34 | } 35 | return Base2Bytes(n), err 36 | } 37 | 38 | func (b Base2Bytes) String() string { 39 | return ToString(int64(b), 1024, "iB", "B") 40 | } 41 | 42 | var ( 43 | metricBytesUnitMap = MakeUnitMap("B", "B", 1000) 44 | ) 45 | 46 | // MetricBytes are SI byte units (1000 bytes in a kilobyte). 47 | type MetricBytes SI 48 | 49 | // SI base-10 byte units. 50 | const ( 51 | Kilobyte MetricBytes = 1000 52 | KB = Kilobyte 53 | Megabyte = Kilobyte * 1000 54 | MB = Megabyte 55 | Gigabyte = Megabyte * 1000 56 | GB = Gigabyte 57 | Terabyte = Gigabyte * 1000 58 | TB = Terabyte 59 | Petabyte = Terabyte * 1000 60 | PB = Petabyte 61 | Exabyte = Petabyte * 1000 62 | EB = Exabyte 63 | ) 64 | 65 | // ParseMetricBytes parses base-10 metric byte units. That is, KB is 1000 bytes. 66 | func ParseMetricBytes(s string) (MetricBytes, error) { 67 | n, err := ParseUnit(s, metricBytesUnitMap) 68 | return MetricBytes(n), err 69 | } 70 | 71 | func (m MetricBytes) String() string { 72 | return ToString(int64(m), 1000, "B", "B") 73 | } 74 | 75 | // ParseStrictBytes supports both iB and B suffixes for base 2 and metric, 76 | // respectively. That is, KiB represents 1024 and KB represents 1000. 77 | func ParseStrictBytes(s string) (int64, error) { 78 | n, err := ParseUnit(s, bytesUnitMap) 79 | if err != nil { 80 | n, err = ParseUnit(s, metricBytesUnitMap) 81 | } 82 | return int64(n), err 83 | } 84 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/units/doc.go: -------------------------------------------------------------------------------- 1 | // Package units provides helpful unit multipliers and functions for Go. 2 | // 3 | // The goal of this package is to have functionality similar to the time [1] package. 4 | // 5 | // 6 | // [1] http://golang.org/pkg/time/ 7 | // 8 | // It allows for code like this: 9 | // 10 | // n, err := ParseBase2Bytes("1KB") 11 | // // n == 1024 12 | // n = units.Mebibyte * 512 13 | package units 14 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/units/si.go: -------------------------------------------------------------------------------- 1 | package units 2 | 3 | // SI units. 4 | type SI int64 5 | 6 | // SI unit multiples. 7 | const ( 8 | Kilo SI = 1000 9 | Mega = Kilo * 1000 10 | Giga = Mega * 1000 11 | Tera = Giga * 1000 12 | Peta = Tera * 1000 13 | Exa = Peta * 1000 14 | ) 15 | 16 | func MakeUnitMap(suffix, shortSuffix string, scale int64) map[string]float64 { 17 | return map[string]float64{ 18 | shortSuffix: 1, 19 | "K" + suffix: float64(scale), 20 | "M" + suffix: float64(scale * scale), 21 | "G" + suffix: float64(scale * scale * scale), 22 | "T" + suffix: float64(scale * scale * scale * scale), 23 | "P" + suffix: float64(scale * scale * scale * scale * scale), 24 | "E" + suffix: float64(scale * scale * scale * scale * scale * scale), 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/units/util.go: -------------------------------------------------------------------------------- 1 | package units 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "strings" 7 | ) 8 | 9 | var ( 10 | siUnits = []string{"", "K", "M", "G", "T", "P", "E"} 11 | ) 12 | 13 | func ToString(n int64, scale int64, suffix, baseSuffix string) string { 14 | mn := len(siUnits) 15 | out := make([]string, mn) 16 | for i, m := range siUnits { 17 | if n%scale != 0 || i == 0 && n == 0 { 18 | s := suffix 19 | if i == 0 { 20 | s = baseSuffix 21 | } 22 | out[mn-1-i] = fmt.Sprintf("%d%s%s", n%scale, m, s) 23 | } 24 | n /= scale 25 | if n == 0 { 26 | break 27 | } 28 | } 29 | return strings.Join(out, "") 30 | } 31 | 32 | // Below code ripped straight from http://golang.org/src/pkg/time/format.go?s=33392:33438#L1123 33 | var errLeadingInt = errors.New("units: bad [0-9]*") // never printed 34 | 35 | // leadingInt consumes the leading [0-9]* from s. 36 | func leadingInt(s string) (x int64, rem string, err error) { 37 | i := 0 38 | for ; i < len(s); i++ { 39 | c := s[i] 40 | if c < '0' || c > '9' { 41 | break 42 | } 43 | if x >= (1<<63-10)/10 { 44 | // overflow 45 | return 0, "", errLeadingInt 46 | } 47 | x = x*10 + int64(c) - '0' 48 | } 49 | return x, s[i:], nil 50 | } 51 | 52 | func ParseUnit(s string, unitMap map[string]float64) (int64, error) { 53 | // [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+ 54 | orig := s 55 | f := float64(0) 56 | neg := false 57 | 58 | // Consume [-+]? 59 | if s != "" { 60 | c := s[0] 61 | if c == '-' || c == '+' { 62 | neg = c == '-' 63 | s = s[1:] 64 | } 65 | } 66 | // Special case: if all that is left is "0", this is zero. 67 | if s == "0" { 68 | return 0, nil 69 | } 70 | if s == "" { 71 | return 0, errors.New("units: invalid " + orig) 72 | } 73 | for s != "" { 74 | g := float64(0) // this element of the sequence 75 | 76 | var x int64 77 | var err error 78 | 79 | // The next character must be [0-9.] 80 | if !(s[0] == '.' || ('0' <= s[0] && s[0] <= '9')) { 81 | return 0, errors.New("units: invalid " + orig) 82 | } 83 | // Consume [0-9]* 84 | pl := len(s) 85 | x, s, err = leadingInt(s) 86 | if err != nil { 87 | return 0, errors.New("units: invalid " + orig) 88 | } 89 | g = float64(x) 90 | pre := pl != len(s) // whether we consumed anything before a period 91 | 92 | // Consume (\.[0-9]*)? 93 | post := false 94 | if s != "" && s[0] == '.' { 95 | s = s[1:] 96 | pl := len(s) 97 | x, s, err = leadingInt(s) 98 | if err != nil { 99 | return 0, errors.New("units: invalid " + orig) 100 | } 101 | scale := 1.0 102 | for n := pl - len(s); n > 0; n-- { 103 | scale *= 10 104 | } 105 | g += float64(x) / scale 106 | post = pl != len(s) 107 | } 108 | if !pre && !post { 109 | // no digits (e.g. ".s" or "-.s") 110 | return 0, errors.New("units: invalid " + orig) 111 | } 112 | 113 | // Consume unit. 114 | i := 0 115 | for ; i < len(s); i++ { 116 | c := s[i] 117 | if c == '.' || ('0' <= c && c <= '9') { 118 | break 119 | } 120 | } 121 | u := s[:i] 122 | s = s[i:] 123 | unit, ok := unitMap[u] 124 | if !ok { 125 | return 0, errors.New("units: unknown unit " + u + " in " + orig) 126 | } 127 | 128 | f += g * unit 129 | } 130 | 131 | if neg { 132 | f = -f 133 | } 134 | if f < float64(-1<<63) || f > float64(1<<63-1) { 135 | return 0, errors.New("units: overflow parsing unit") 136 | } 137 | return int64(f), nil 138 | } 139 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | 24 | .idea/ 25 | *.iml -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | sudo: false 3 | 4 | matrix: 5 | include: 6 | - go: 1.4 7 | - go: 1.5 8 | - go: 1.6 9 | - go: tip 10 | allow_failures: 11 | - go: tip 12 | 13 | script: 14 | - go get -t -v ./... 15 | - diff -u <(echo -n) <(gofmt -d .) 16 | - go vet $(go list ./... | grep -v /vendor/) 17 | - go test -v -race ./... 18 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of Gorilla WebSocket authors for copyright 2 | # purposes. 3 | # 4 | # Please keep the list sorted. 5 | 6 | Gary Burd 7 | Joachim Bauch 8 | 9 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 9 | Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/README.md: -------------------------------------------------------------------------------- 1 | # Gorilla WebSocket 2 | 3 | Gorilla WebSocket is a [Go](http://golang.org/) implementation of the 4 | [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. 5 | 6 | ### Documentation 7 | 8 | * [API Reference](http://godoc.org/github.com/gorilla/websocket) 9 | * [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat) 10 | * [Command example](https://github.com/gorilla/websocket/tree/master/examples/command) 11 | * [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo) 12 | * [File watch example](https://github.com/gorilla/websocket/tree/master/examples/filewatch) 13 | 14 | ### Status 15 | 16 | The Gorilla WebSocket package provides a complete and tested implementation of 17 | the [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. The 18 | package API is stable. 19 | 20 | ### Installation 21 | 22 | go get github.com/gorilla/websocket 23 | 24 | ### Protocol Compliance 25 | 26 | The Gorilla WebSocket package passes the server tests in the [Autobahn Test 27 | Suite](http://autobahn.ws/testsuite) using the application in the [examples/autobahn 28 | subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn). 29 | 30 | ### Gorilla WebSocket compared with other packages 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
github.com/gorillagolang.org/x/net
RFC 6455 Features
Passes Autobahn Test SuiteYesNo
Receive fragmented messageYesNo, see note 1
Send close messageYesNo
Send pings and receive pongsYesNo
Get the type of a received data messageYesYes, see note 2
Other Features
Read message using io.ReaderYesNo, see note 3
Write message using io.WriteCloserYesNo, see note 3
49 | 50 | Notes: 51 | 52 | 1. Large messages are fragmented in [Chrome's new WebSocket implementation](http://www.ietf.org/mail-archive/web/hybi/current/msg10503.html). 53 | 2. The application can get the type of a received data message by implementing 54 | a [Codec marshal](http://godoc.org/golang.org/x/net/websocket#Codec.Marshal) 55 | function. 56 | 3. The go.net io.Reader and io.Writer operate across WebSocket frame boundaries. 57 | Read returns when the input buffer is full or a frame boundary is 58 | encountered. Each call to Write sends a single frame message. The Gorilla 59 | io.Reader and io.WriteCloser operate on a single WebSocket message. 60 | 61 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/client.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 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 websocket 6 | 7 | import ( 8 | "bufio" 9 | "bytes" 10 | "crypto/tls" 11 | "encoding/base64" 12 | "errors" 13 | "io" 14 | "io/ioutil" 15 | "net" 16 | "net/http" 17 | "net/url" 18 | "strings" 19 | "time" 20 | ) 21 | 22 | // ErrBadHandshake is returned when the server response to opening handshake is 23 | // invalid. 24 | var ErrBadHandshake = errors.New("websocket: bad handshake") 25 | 26 | var errInvalidCompression = errors.New("websocket: invalid compression negotiation") 27 | 28 | // NewClient creates a new client connection using the given net connection. 29 | // The URL u specifies the host and request URI. Use requestHeader to specify 30 | // the origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies 31 | // (Cookie). Use the response.Header to get the selected subprotocol 32 | // (Sec-WebSocket-Protocol) and cookies (Set-Cookie). 33 | // 34 | // If the WebSocket handshake fails, ErrBadHandshake is returned along with a 35 | // non-nil *http.Response so that callers can handle redirects, authentication, 36 | // etc. 37 | // 38 | // Deprecated: Use Dialer instead. 39 | func NewClient(netConn net.Conn, u *url.URL, requestHeader http.Header, readBufSize, writeBufSize int) (c *Conn, response *http.Response, err error) { 40 | d := Dialer{ 41 | ReadBufferSize: readBufSize, 42 | WriteBufferSize: writeBufSize, 43 | NetDial: func(net, addr string) (net.Conn, error) { 44 | return netConn, nil 45 | }, 46 | } 47 | return d.Dial(u.String(), requestHeader) 48 | } 49 | 50 | // A Dialer contains options for connecting to WebSocket server. 51 | type Dialer struct { 52 | // NetDial specifies the dial function for creating TCP connections. If 53 | // NetDial is nil, net.Dial is used. 54 | NetDial func(network, addr string) (net.Conn, error) 55 | 56 | // Proxy specifies a function to return a proxy for a given 57 | // Request. If the function returns a non-nil error, the 58 | // request is aborted with the provided error. 59 | // If Proxy is nil or returns a nil *URL, no proxy is used. 60 | Proxy func(*http.Request) (*url.URL, error) 61 | 62 | // TLSClientConfig specifies the TLS configuration to use with tls.Client. 63 | // If nil, the default configuration is used. 64 | TLSClientConfig *tls.Config 65 | 66 | // HandshakeTimeout specifies the duration for the handshake to complete. 67 | HandshakeTimeout time.Duration 68 | 69 | // Input and output buffer sizes. If the buffer size is zero, then a 70 | // default value of 4096 is used. 71 | ReadBufferSize, WriteBufferSize int 72 | 73 | // Subprotocols specifies the client's requested subprotocols. 74 | Subprotocols []string 75 | 76 | // EnableCompression specifies if the client should attempt to negotiate 77 | // per message compression (RFC 7692). Setting this value to true does not 78 | // guarantee that compression will be supported. Currently only "no context 79 | // takeover" modes are supported. 80 | EnableCompression bool 81 | 82 | // Jar specifies the cookie jar. 83 | // If Jar is nil, cookies are not sent in requests and ignored 84 | // in responses. 85 | Jar http.CookieJar 86 | } 87 | 88 | var errMalformedURL = errors.New("malformed ws or wss URL") 89 | 90 | // parseURL parses the URL. 91 | // 92 | // This function is a replacement for the standard library url.Parse function. 93 | // In Go 1.4 and earlier, url.Parse loses information from the path. 94 | func parseURL(s string) (*url.URL, error) { 95 | // From the RFC: 96 | // 97 | // ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ] 98 | // wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ] 99 | var u url.URL 100 | switch { 101 | case strings.HasPrefix(s, "ws://"): 102 | u.Scheme = "ws" 103 | s = s[len("ws://"):] 104 | case strings.HasPrefix(s, "wss://"): 105 | u.Scheme = "wss" 106 | s = s[len("wss://"):] 107 | default: 108 | return nil, errMalformedURL 109 | } 110 | 111 | if i := strings.Index(s, "?"); i >= 0 { 112 | u.RawQuery = s[i+1:] 113 | s = s[:i] 114 | } 115 | 116 | if i := strings.Index(s, "/"); i >= 0 { 117 | u.Opaque = s[i:] 118 | s = s[:i] 119 | } else { 120 | u.Opaque = "/" 121 | } 122 | 123 | u.Host = s 124 | 125 | if strings.Contains(u.Host, "@") { 126 | // Don't bother parsing user information because user information is 127 | // not allowed in websocket URIs. 128 | return nil, errMalformedURL 129 | } 130 | 131 | return &u, nil 132 | } 133 | 134 | func hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) { 135 | hostPort = u.Host 136 | hostNoPort = u.Host 137 | if i := strings.LastIndex(u.Host, ":"); i > strings.LastIndex(u.Host, "]") { 138 | hostNoPort = hostNoPort[:i] 139 | } else { 140 | switch u.Scheme { 141 | case "wss": 142 | hostPort += ":443" 143 | case "https": 144 | hostPort += ":443" 145 | default: 146 | hostPort += ":80" 147 | } 148 | } 149 | return hostPort, hostNoPort 150 | } 151 | 152 | // DefaultDialer is a dialer with all fields set to the default zero values. 153 | var DefaultDialer = &Dialer{ 154 | Proxy: http.ProxyFromEnvironment, 155 | } 156 | 157 | // Dial creates a new client connection. Use requestHeader to specify the 158 | // origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies (Cookie). 159 | // Use the response.Header to get the selected subprotocol 160 | // (Sec-WebSocket-Protocol) and cookies (Set-Cookie). 161 | // 162 | // If the WebSocket handshake fails, ErrBadHandshake is returned along with a 163 | // non-nil *http.Response so that callers can handle redirects, authentication, 164 | // etcetera. The response body may not contain the entire response and does not 165 | // need to be closed by the application. 166 | func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) { 167 | 168 | if d == nil { 169 | d = &Dialer{ 170 | Proxy: http.ProxyFromEnvironment, 171 | } 172 | } 173 | 174 | challengeKey, err := generateChallengeKey() 175 | if err != nil { 176 | return nil, nil, err 177 | } 178 | 179 | u, err := parseURL(urlStr) 180 | if err != nil { 181 | return nil, nil, err 182 | } 183 | 184 | switch u.Scheme { 185 | case "ws": 186 | u.Scheme = "http" 187 | case "wss": 188 | u.Scheme = "https" 189 | default: 190 | return nil, nil, errMalformedURL 191 | } 192 | 193 | if u.User != nil { 194 | // User name and password are not allowed in websocket URIs. 195 | return nil, nil, errMalformedURL 196 | } 197 | 198 | req := &http.Request{ 199 | Method: "GET", 200 | URL: u, 201 | Proto: "HTTP/1.1", 202 | ProtoMajor: 1, 203 | ProtoMinor: 1, 204 | Header: make(http.Header), 205 | Host: u.Host, 206 | } 207 | 208 | // Set the cookies present in the cookie jar of the dialer 209 | if d.Jar != nil { 210 | for _, cookie := range d.Jar.Cookies(u) { 211 | req.AddCookie(cookie) 212 | } 213 | } 214 | 215 | // Set the request headers using the capitalization for names and values in 216 | // RFC examples. Although the capitalization shouldn't matter, there are 217 | // servers that depend on it. The Header.Set method is not used because the 218 | // method canonicalizes the header names. 219 | req.Header["Upgrade"] = []string{"websocket"} 220 | req.Header["Connection"] = []string{"Upgrade"} 221 | req.Header["Sec-WebSocket-Key"] = []string{challengeKey} 222 | req.Header["Sec-WebSocket-Version"] = []string{"13"} 223 | if len(d.Subprotocols) > 0 { 224 | req.Header["Sec-WebSocket-Protocol"] = []string{strings.Join(d.Subprotocols, ", ")} 225 | } 226 | for k, vs := range requestHeader { 227 | switch { 228 | case k == "Host": 229 | if len(vs) > 0 { 230 | req.Host = vs[0] 231 | } 232 | case k == "Upgrade" || 233 | k == "Connection" || 234 | k == "Sec-Websocket-Key" || 235 | k == "Sec-Websocket-Version" || 236 | k == "Sec-Websocket-Extensions" || 237 | (k == "Sec-Websocket-Protocol" && len(d.Subprotocols) > 0): 238 | return nil, nil, errors.New("websocket: duplicate header not allowed: " + k) 239 | default: 240 | req.Header[k] = vs 241 | } 242 | } 243 | 244 | if d.EnableCompression { 245 | req.Header.Set("Sec-Websocket-Extensions", "permessage-deflate; server_no_context_takeover; client_no_context_takeover") 246 | } 247 | 248 | hostPort, hostNoPort := hostPortNoPort(u) 249 | 250 | var proxyURL *url.URL 251 | // Check wether the proxy method has been configured 252 | if d.Proxy != nil { 253 | proxyURL, err = d.Proxy(req) 254 | } 255 | if err != nil { 256 | return nil, nil, err 257 | } 258 | 259 | var targetHostPort string 260 | if proxyURL != nil { 261 | targetHostPort, _ = hostPortNoPort(proxyURL) 262 | } else { 263 | targetHostPort = hostPort 264 | } 265 | 266 | var deadline time.Time 267 | if d.HandshakeTimeout != 0 { 268 | deadline = time.Now().Add(d.HandshakeTimeout) 269 | } 270 | 271 | netDial := d.NetDial 272 | if netDial == nil { 273 | netDialer := &net.Dialer{Deadline: deadline} 274 | netDial = netDialer.Dial 275 | } 276 | 277 | netConn, err := netDial("tcp", targetHostPort) 278 | if err != nil { 279 | return nil, nil, err 280 | } 281 | 282 | defer func() { 283 | if netConn != nil { 284 | netConn.Close() 285 | } 286 | }() 287 | 288 | if err := netConn.SetDeadline(deadline); err != nil { 289 | return nil, nil, err 290 | } 291 | 292 | if proxyURL != nil { 293 | connectHeader := make(http.Header) 294 | if user := proxyURL.User; user != nil { 295 | proxyUser := user.Username() 296 | if proxyPassword, passwordSet := user.Password(); passwordSet { 297 | credential := base64.StdEncoding.EncodeToString([]byte(proxyUser + ":" + proxyPassword)) 298 | connectHeader.Set("Proxy-Authorization", "Basic "+credential) 299 | } 300 | } 301 | connectReq := &http.Request{ 302 | Method: "CONNECT", 303 | URL: &url.URL{Opaque: hostPort}, 304 | Host: hostPort, 305 | Header: connectHeader, 306 | } 307 | 308 | connectReq.Write(netConn) 309 | 310 | // Read response. 311 | // Okay to use and discard buffered reader here, because 312 | // TLS server will not speak until spoken to. 313 | br := bufio.NewReader(netConn) 314 | resp, err := http.ReadResponse(br, connectReq) 315 | if err != nil { 316 | return nil, nil, err 317 | } 318 | if resp.StatusCode != 200 { 319 | f := strings.SplitN(resp.Status, " ", 2) 320 | return nil, nil, errors.New(f[1]) 321 | } 322 | } 323 | 324 | if u.Scheme == "https" { 325 | cfg := cloneTLSConfig(d.TLSClientConfig) 326 | if cfg.ServerName == "" { 327 | cfg.ServerName = hostNoPort 328 | } 329 | tlsConn := tls.Client(netConn, cfg) 330 | netConn = tlsConn 331 | if err := tlsConn.Handshake(); err != nil { 332 | return nil, nil, err 333 | } 334 | if !cfg.InsecureSkipVerify { 335 | if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil { 336 | return nil, nil, err 337 | } 338 | } 339 | } 340 | 341 | conn := newConn(netConn, false, d.ReadBufferSize, d.WriteBufferSize) 342 | 343 | if err := req.Write(netConn); err != nil { 344 | return nil, nil, err 345 | } 346 | 347 | resp, err := http.ReadResponse(conn.br, req) 348 | if err != nil { 349 | return nil, nil, err 350 | } 351 | 352 | if d.Jar != nil { 353 | if rc := resp.Cookies(); len(rc) > 0 { 354 | d.Jar.SetCookies(u, rc) 355 | } 356 | } 357 | 358 | if resp.StatusCode != 101 || 359 | !strings.EqualFold(resp.Header.Get("Upgrade"), "websocket") || 360 | !strings.EqualFold(resp.Header.Get("Connection"), "upgrade") || 361 | resp.Header.Get("Sec-Websocket-Accept") != computeAcceptKey(challengeKey) { 362 | // Before closing the network connection on return from this 363 | // function, slurp up some of the response to aid application 364 | // debugging. 365 | buf := make([]byte, 1024) 366 | n, _ := io.ReadFull(resp.Body, buf) 367 | resp.Body = ioutil.NopCloser(bytes.NewReader(buf[:n])) 368 | return nil, resp, ErrBadHandshake 369 | } 370 | 371 | for _, ext := range parseExtensions(req.Header) { 372 | if ext[""] != "permessage-deflate" { 373 | continue 374 | } 375 | _, snct := ext["server_no_context_takeover"] 376 | _, cnct := ext["client_no_context_takeover"] 377 | if !snct || !cnct { 378 | return nil, resp, errInvalidCompression 379 | } 380 | conn.newCompressionWriter = compressNoContextTakeover 381 | conn.newDecompressionReader = decompressNoContextTakeover 382 | break 383 | } 384 | 385 | resp.Body = ioutil.NopCloser(bytes.NewReader([]byte{})) 386 | conn.subprotocol = resp.Header.Get("Sec-Websocket-Protocol") 387 | 388 | netConn.SetDeadline(time.Time{}) 389 | netConn = nil // to avoid close in defer. 390 | return conn, resp, nil 391 | } 392 | 393 | // cloneTLSConfig clones all public fields except the fields 394 | // SessionTicketsDisabled and SessionTicketKey. This avoids copying the 395 | // sync.Mutex in the sync.Once and makes it safe to call cloneTLSConfig on a 396 | // config in active use. 397 | func cloneTLSConfig(cfg *tls.Config) *tls.Config { 398 | if cfg == nil { 399 | return &tls.Config{} 400 | } 401 | return &tls.Config{ 402 | Rand: cfg.Rand, 403 | Time: cfg.Time, 404 | Certificates: cfg.Certificates, 405 | NameToCertificate: cfg.NameToCertificate, 406 | GetCertificate: cfg.GetCertificate, 407 | RootCAs: cfg.RootCAs, 408 | NextProtos: cfg.NextProtos, 409 | ServerName: cfg.ServerName, 410 | ClientAuth: cfg.ClientAuth, 411 | ClientCAs: cfg.ClientCAs, 412 | InsecureSkipVerify: cfg.InsecureSkipVerify, 413 | CipherSuites: cfg.CipherSuites, 414 | PreferServerCipherSuites: cfg.PreferServerCipherSuites, 415 | ClientSessionCache: cfg.ClientSessionCache, 416 | MinVersion: cfg.MinVersion, 417 | MaxVersion: cfg.MaxVersion, 418 | CurvePreferences: cfg.CurvePreferences, 419 | } 420 | } 421 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/compression.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 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 websocket 6 | 7 | import ( 8 | "compress/flate" 9 | "errors" 10 | "io" 11 | "strings" 12 | ) 13 | 14 | func decompressNoContextTakeover(r io.Reader) io.Reader { 15 | const tail = 16 | // Add four bytes as specified in RFC 17 | "\x00\x00\xff\xff" + 18 | // Add final block to squelch unexpected EOF error from flate reader. 19 | "\x01\x00\x00\xff\xff" 20 | 21 | return flate.NewReader(io.MultiReader(r, strings.NewReader(tail))) 22 | } 23 | 24 | func compressNoContextTakeover(w io.WriteCloser) (io.WriteCloser, error) { 25 | tw := &truncWriter{w: w} 26 | fw, err := flate.NewWriter(tw, 3) 27 | return &flateWrapper{fw: fw, tw: tw}, err 28 | } 29 | 30 | // truncWriter is an io.Writer that writes all but the last four bytes of the 31 | // stream to another io.Writer. 32 | type truncWriter struct { 33 | w io.WriteCloser 34 | n int 35 | p [4]byte 36 | } 37 | 38 | func (w *truncWriter) Write(p []byte) (int, error) { 39 | n := 0 40 | 41 | // fill buffer first for simplicity. 42 | if w.n < len(w.p) { 43 | n = copy(w.p[w.n:], p) 44 | p = p[n:] 45 | w.n += n 46 | if len(p) == 0 { 47 | return n, nil 48 | } 49 | } 50 | 51 | m := len(p) 52 | if m > len(w.p) { 53 | m = len(w.p) 54 | } 55 | 56 | if nn, err := w.w.Write(w.p[:m]); err != nil { 57 | return n + nn, err 58 | } 59 | 60 | copy(w.p[:], w.p[m:]) 61 | copy(w.p[len(w.p)-m:], p[len(p)-m:]) 62 | nn, err := w.w.Write(p[:len(p)-m]) 63 | return n + nn, err 64 | } 65 | 66 | type flateWrapper struct { 67 | fw *flate.Writer 68 | tw *truncWriter 69 | } 70 | 71 | func (w *flateWrapper) Write(p []byte) (int, error) { 72 | return w.fw.Write(p) 73 | } 74 | 75 | func (w *flateWrapper) Close() error { 76 | err1 := w.fw.Flush() 77 | if w.tw.p != [4]byte{0, 0, 0xff, 0xff} { 78 | return errors.New("websocket: internal error, unexpected bytes at end of flate stream") 79 | } 80 | err2 := w.tw.w.Close() 81 | if err1 != nil { 82 | return err1 83 | } 84 | return err2 85 | } 86 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/conn_read.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 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 | // +build go1.5 6 | 7 | package websocket 8 | 9 | import "io" 10 | 11 | func (c *Conn) read(n int) ([]byte, error) { 12 | p, err := c.br.Peek(n) 13 | if err == io.EOF { 14 | err = errUnexpectedEOF 15 | } 16 | c.br.Discard(len(p)) 17 | return p, err 18 | } 19 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/conn_read_legacy.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 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 | // +build !go1.5 6 | 7 | package websocket 8 | 9 | import "io" 10 | 11 | func (c *Conn) read(n int) ([]byte, error) { 12 | p, err := c.br.Peek(n) 13 | if err == io.EOF { 14 | err = errUnexpectedEOF 15 | } 16 | if len(p) > 0 { 17 | // advance over the bytes just read 18 | io.ReadFull(c.br, p) 19 | } 20 | return p, err 21 | } 22 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 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 websocket implements the WebSocket protocol defined in RFC 6455. 6 | // 7 | // Overview 8 | // 9 | // The Conn type represents a WebSocket connection. A server application uses 10 | // the Upgrade function from an Upgrader object with a HTTP request handler 11 | // to get a pointer to a Conn: 12 | // 13 | // var upgrader = websocket.Upgrader{ 14 | // ReadBufferSize: 1024, 15 | // WriteBufferSize: 1024, 16 | // } 17 | // 18 | // func handler(w http.ResponseWriter, r *http.Request) { 19 | // conn, err := upgrader.Upgrade(w, r, nil) 20 | // if err != nil { 21 | // log.Println(err) 22 | // return 23 | // } 24 | // ... Use conn to send and receive messages. 25 | // } 26 | // 27 | // Call the connection's WriteMessage and ReadMessage methods to send and 28 | // receive messages as a slice of bytes. This snippet of code shows how to echo 29 | // messages using these methods: 30 | // 31 | // for { 32 | // messageType, p, err := conn.ReadMessage() 33 | // if err != nil { 34 | // return 35 | // } 36 | // if err = conn.WriteMessage(messageType, p); err != nil { 37 | // return err 38 | // } 39 | // } 40 | // 41 | // In above snippet of code, p is a []byte and messageType is an int with value 42 | // websocket.BinaryMessage or websocket.TextMessage. 43 | // 44 | // An application can also send and receive messages using the io.WriteCloser 45 | // and io.Reader interfaces. To send a message, call the connection NextWriter 46 | // method to get an io.WriteCloser, write the message to the writer and close 47 | // the writer when done. To receive a message, call the connection NextReader 48 | // method to get an io.Reader and read until io.EOF is returned. This snippet 49 | // shows how to echo messages using the NextWriter and NextReader methods: 50 | // 51 | // for { 52 | // messageType, r, err := conn.NextReader() 53 | // if err != nil { 54 | // return 55 | // } 56 | // w, err := conn.NextWriter(messageType) 57 | // if err != nil { 58 | // return err 59 | // } 60 | // if _, err := io.Copy(w, r); err != nil { 61 | // return err 62 | // } 63 | // if err := w.Close(); err != nil { 64 | // return err 65 | // } 66 | // } 67 | // 68 | // Data Messages 69 | // 70 | // The WebSocket protocol distinguishes between text and binary data messages. 71 | // Text messages are interpreted as UTF-8 encoded text. The interpretation of 72 | // binary messages is left to the application. 73 | // 74 | // This package uses the TextMessage and BinaryMessage integer constants to 75 | // identify the two data message types. The ReadMessage and NextReader methods 76 | // return the type of the received message. The messageType argument to the 77 | // WriteMessage and NextWriter methods specifies the type of a sent message. 78 | // 79 | // It is the application's responsibility to ensure that text messages are 80 | // valid UTF-8 encoded text. 81 | // 82 | // Control Messages 83 | // 84 | // The WebSocket protocol defines three types of control messages: close, ping 85 | // and pong. Call the connection WriteControl, WriteMessage or NextWriter 86 | // methods to send a control message to the peer. 87 | // 88 | // Connections handle received close messages by sending a close message to the 89 | // peer and returning a *CloseError from the the NextReader, ReadMessage or the 90 | // message Read method. 91 | // 92 | // Connections handle received ping and pong messages by invoking callback 93 | // functions set with SetPingHandler and SetPongHandler methods. The callback 94 | // functions are called from the NextReader, ReadMessage and the message Read 95 | // methods. 96 | // 97 | // The default ping handler sends a pong to the peer. The application's reading 98 | // goroutine can block for a short time while the handler writes the pong data 99 | // to the connection. 100 | // 101 | // The application must read the connection to process ping, pong and close 102 | // messages sent from the peer. If the application is not otherwise interested 103 | // in messages from the peer, then the application should start a goroutine to 104 | // read and discard messages from the peer. A simple example is: 105 | // 106 | // func readLoop(c *websocket.Conn) { 107 | // for { 108 | // if _, _, err := c.NextReader(); err != nil { 109 | // c.Close() 110 | // break 111 | // } 112 | // } 113 | // } 114 | // 115 | // Concurrency 116 | // 117 | // Connections support one concurrent reader and one concurrent writer. 118 | // 119 | // Applications are responsible for ensuring that no more than one goroutine 120 | // calls the write methods (NextWriter, SetWriteDeadline, WriteMessage, 121 | // WriteJSON) concurrently and that no more than one goroutine calls the read 122 | // methods (NextReader, SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler, 123 | // SetPingHandler) concurrently. 124 | // 125 | // The Close and WriteControl methods can be called concurrently with all other 126 | // methods. 127 | // 128 | // Origin Considerations 129 | // 130 | // Web browsers allow Javascript applications to open a WebSocket connection to 131 | // any host. It's up to the server to enforce an origin policy using the Origin 132 | // request header sent by the browser. 133 | // 134 | // The Upgrader calls the function specified in the CheckOrigin field to check 135 | // the origin. If the CheckOrigin function returns false, then the Upgrade 136 | // method fails the WebSocket handshake with HTTP status 403. 137 | // 138 | // If the CheckOrigin field is nil, then the Upgrader uses a safe default: fail 139 | // the handshake if the Origin request header is present and not equal to the 140 | // Host request header. 141 | // 142 | // An application can allow connections from any origin by specifying a 143 | // function that always returns true: 144 | // 145 | // var upgrader = websocket.Upgrader{ 146 | // CheckOrigin: func(r *http.Request) bool { return true }, 147 | // } 148 | // 149 | // The deprecated Upgrade function does not enforce an origin policy. It's the 150 | // application's responsibility to check the Origin header before calling 151 | // Upgrade. 152 | // 153 | // Compression [Experimental] 154 | // 155 | // Per message compression extensions (RFC 7692) are experimentally supported 156 | // by this package in a limited capacity. Setting the EnableCompression option 157 | // to true in Dialer or Upgrader will attempt to negotiate per message deflate 158 | // support. If compression was successfully negotiated with the connection's 159 | // peer, any message received in compressed form will be automatically 160 | // decompressed. All Read methods will return uncompressed bytes. 161 | // 162 | // Per message compression of messages written to a connection can be enabled 163 | // or disabled by calling the corresponding Conn method: 164 | // 165 | // conn.EnableWriteCompression(true) 166 | // 167 | // Currently this package does not support compression with "context takeover". 168 | // This means that messages must be compressed and decompressed in isolation, 169 | // without retaining sliding window or dictionary state across messages. For 170 | // more details refer to RFC 7692. 171 | // 172 | // Use of compression is experimental and may result in decreased performance. 173 | package websocket 174 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/json.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 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 websocket 6 | 7 | import ( 8 | "encoding/json" 9 | "io" 10 | ) 11 | 12 | // WriteJSON is deprecated, use c.WriteJSON instead. 13 | func WriteJSON(c *Conn, v interface{}) error { 14 | return c.WriteJSON(v) 15 | } 16 | 17 | // WriteJSON writes the JSON encoding of v to the connection. 18 | // 19 | // See the documentation for encoding/json Marshal for details about the 20 | // conversion of Go values to JSON. 21 | func (c *Conn) WriteJSON(v interface{}) error { 22 | w, err := c.NextWriter(TextMessage) 23 | if err != nil { 24 | return err 25 | } 26 | err1 := json.NewEncoder(w).Encode(v) 27 | err2 := w.Close() 28 | if err1 != nil { 29 | return err1 30 | } 31 | return err2 32 | } 33 | 34 | // ReadJSON is deprecated, use c.ReadJSON instead. 35 | func ReadJSON(c *Conn, v interface{}) error { 36 | return c.ReadJSON(v) 37 | } 38 | 39 | // ReadJSON reads the next JSON-encoded message from the connection and stores 40 | // it in the value pointed to by v. 41 | // 42 | // See the documentation for the encoding/json Unmarshal function for details 43 | // about the conversion of JSON to a Go value. 44 | func (c *Conn) ReadJSON(v interface{}) error { 45 | _, r, err := c.NextReader() 46 | if err != nil { 47 | return err 48 | } 49 | err = json.NewDecoder(r).Decode(v) 50 | if err == io.EOF { 51 | // One value is expected in the message. 52 | err = io.ErrUnexpectedEOF 53 | } 54 | return err 55 | } 56 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/server.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 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 websocket 6 | 7 | import ( 8 | "bufio" 9 | "errors" 10 | "net" 11 | "net/http" 12 | "net/url" 13 | "strings" 14 | "time" 15 | ) 16 | 17 | // HandshakeError describes an error with the handshake from the peer. 18 | type HandshakeError struct { 19 | message string 20 | } 21 | 22 | func (e HandshakeError) Error() string { return e.message } 23 | 24 | // Upgrader specifies parameters for upgrading an HTTP connection to a 25 | // WebSocket connection. 26 | type Upgrader struct { 27 | // HandshakeTimeout specifies the duration for the handshake to complete. 28 | HandshakeTimeout time.Duration 29 | 30 | // ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer 31 | // size is zero, then a default value of 4096 is used. The I/O buffer sizes 32 | // do not limit the size of the messages that can be sent or received. 33 | ReadBufferSize, WriteBufferSize int 34 | 35 | // Subprotocols specifies the server's supported protocols in order of 36 | // preference. If this field is set, then the Upgrade method negotiates a 37 | // subprotocol by selecting the first match in this list with a protocol 38 | // requested by the client. 39 | Subprotocols []string 40 | 41 | // Error specifies the function for generating HTTP error responses. If Error 42 | // is nil, then http.Error is used to generate the HTTP response. 43 | Error func(w http.ResponseWriter, r *http.Request, status int, reason error) 44 | 45 | // CheckOrigin returns true if the request Origin header is acceptable. If 46 | // CheckOrigin is nil, the host in the Origin header must not be set or 47 | // must match the host of the request. 48 | CheckOrigin func(r *http.Request) bool 49 | 50 | // EnableCompression specify if the server should attempt to negotiate per 51 | // message compression (RFC 7692). Setting this value to true does not 52 | // guarantee that compression will be supported. Currently only "no context 53 | // takeover" modes are supported. 54 | EnableCompression bool 55 | } 56 | 57 | func (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status int, reason string) (*Conn, error) { 58 | err := HandshakeError{reason} 59 | if u.Error != nil { 60 | u.Error(w, r, status, err) 61 | } else { 62 | w.Header().Set("Sec-Websocket-Version", "13") 63 | http.Error(w, http.StatusText(status), status) 64 | } 65 | return nil, err 66 | } 67 | 68 | // checkSameOrigin returns true if the origin is not set or is equal to the request host. 69 | func checkSameOrigin(r *http.Request) bool { 70 | origin := r.Header["Origin"] 71 | if len(origin) == 0 { 72 | return true 73 | } 74 | u, err := url.Parse(origin[0]) 75 | if err != nil { 76 | return false 77 | } 78 | return u.Host == r.Host 79 | } 80 | 81 | func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header) string { 82 | if u.Subprotocols != nil { 83 | clientProtocols := Subprotocols(r) 84 | for _, serverProtocol := range u.Subprotocols { 85 | for _, clientProtocol := range clientProtocols { 86 | if clientProtocol == serverProtocol { 87 | return clientProtocol 88 | } 89 | } 90 | } 91 | } else if responseHeader != nil { 92 | return responseHeader.Get("Sec-Websocket-Protocol") 93 | } 94 | return "" 95 | } 96 | 97 | // Upgrade upgrades the HTTP server connection to the WebSocket protocol. 98 | // 99 | // The responseHeader is included in the response to the client's upgrade 100 | // request. Use the responseHeader to specify cookies (Set-Cookie) and the 101 | // application negotiated subprotocol (Sec-Websocket-Protocol). 102 | // 103 | // If the upgrade fails, then Upgrade replies to the client with an HTTP error 104 | // response. 105 | func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) { 106 | if r.Method != "GET" { 107 | return u.returnError(w, r, http.StatusMethodNotAllowed, "websocket: method not GET") 108 | } 109 | 110 | if _, ok := responseHeader["Sec-Websocket-Extensions"]; ok { 111 | return u.returnError(w, r, http.StatusInternalServerError, "websocket: application specific Sec-Websocket-Extensions headers are unsupported") 112 | } 113 | 114 | if !tokenListContainsValue(r.Header, "Sec-Websocket-Version", "13") { 115 | return u.returnError(w, r, http.StatusBadRequest, "websocket: version != 13") 116 | } 117 | 118 | if !tokenListContainsValue(r.Header, "Connection", "upgrade") { 119 | return u.returnError(w, r, http.StatusBadRequest, "websocket: could not find connection header with token 'upgrade'") 120 | } 121 | 122 | if !tokenListContainsValue(r.Header, "Upgrade", "websocket") { 123 | return u.returnError(w, r, http.StatusBadRequest, "websocket: could not find upgrade header with token 'websocket'") 124 | } 125 | 126 | checkOrigin := u.CheckOrigin 127 | if checkOrigin == nil { 128 | checkOrigin = checkSameOrigin 129 | } 130 | if !checkOrigin(r) { 131 | return u.returnError(w, r, http.StatusForbidden, "websocket: origin not allowed") 132 | } 133 | 134 | challengeKey := r.Header.Get("Sec-Websocket-Key") 135 | if challengeKey == "" { 136 | return u.returnError(w, r, http.StatusBadRequest, "websocket: key missing or blank") 137 | } 138 | 139 | subprotocol := u.selectSubprotocol(r, responseHeader) 140 | 141 | // Negotiate PMCE 142 | var compress bool 143 | if u.EnableCompression { 144 | for _, ext := range parseExtensions(r.Header) { 145 | if ext[""] != "permessage-deflate" { 146 | continue 147 | } 148 | compress = true 149 | break 150 | } 151 | } 152 | 153 | var ( 154 | netConn net.Conn 155 | br *bufio.Reader 156 | err error 157 | ) 158 | 159 | h, ok := w.(http.Hijacker) 160 | if !ok { 161 | return u.returnError(w, r, http.StatusInternalServerError, "websocket: response does not implement http.Hijacker") 162 | } 163 | var rw *bufio.ReadWriter 164 | netConn, rw, err = h.Hijack() 165 | if err != nil { 166 | return u.returnError(w, r, http.StatusInternalServerError, err.Error()) 167 | } 168 | br = rw.Reader 169 | 170 | if br.Buffered() > 0 { 171 | netConn.Close() 172 | return nil, errors.New("websocket: client sent data before handshake is complete") 173 | } 174 | 175 | c := newConn(netConn, true, u.ReadBufferSize, u.WriteBufferSize) 176 | c.subprotocol = subprotocol 177 | 178 | if compress { 179 | c.newCompressionWriter = compressNoContextTakeover 180 | c.newDecompressionReader = decompressNoContextTakeover 181 | } 182 | 183 | p := c.writeBuf[:0] 184 | p = append(p, "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "...) 185 | p = append(p, computeAcceptKey(challengeKey)...) 186 | p = append(p, "\r\n"...) 187 | if c.subprotocol != "" { 188 | p = append(p, "Sec-Websocket-Protocol: "...) 189 | p = append(p, c.subprotocol...) 190 | p = append(p, "\r\n"...) 191 | } 192 | if compress { 193 | p = append(p, "Sec-Websocket-Extensions: permessage-deflate; server_no_context_takeover; client_no_context_takeover\r\n"...) 194 | } 195 | for k, vs := range responseHeader { 196 | if k == "Sec-Websocket-Protocol" { 197 | continue 198 | } 199 | for _, v := range vs { 200 | p = append(p, k...) 201 | p = append(p, ": "...) 202 | for i := 0; i < len(v); i++ { 203 | b := v[i] 204 | if b <= 31 { 205 | // prevent response splitting. 206 | b = ' ' 207 | } 208 | p = append(p, b) 209 | } 210 | p = append(p, "\r\n"...) 211 | } 212 | } 213 | p = append(p, "\r\n"...) 214 | 215 | // Clear deadlines set by HTTP server. 216 | netConn.SetDeadline(time.Time{}) 217 | 218 | if u.HandshakeTimeout > 0 { 219 | netConn.SetWriteDeadline(time.Now().Add(u.HandshakeTimeout)) 220 | } 221 | if _, err = netConn.Write(p); err != nil { 222 | netConn.Close() 223 | return nil, err 224 | } 225 | if u.HandshakeTimeout > 0 { 226 | netConn.SetWriteDeadline(time.Time{}) 227 | } 228 | 229 | return c, nil 230 | } 231 | 232 | // Upgrade upgrades the HTTP server connection to the WebSocket protocol. 233 | // 234 | // This function is deprecated, use websocket.Upgrader instead. 235 | // 236 | // The application is responsible for checking the request origin before 237 | // calling Upgrade. An example implementation of the same origin policy is: 238 | // 239 | // if req.Header.Get("Origin") != "http://"+req.Host { 240 | // http.Error(w, "Origin not allowed", 403) 241 | // return 242 | // } 243 | // 244 | // If the endpoint supports subprotocols, then the application is responsible 245 | // for negotiating the protocol used on the connection. Use the Subprotocols() 246 | // function to get the subprotocols requested by the client. Use the 247 | // Sec-Websocket-Protocol response header to specify the subprotocol selected 248 | // by the application. 249 | // 250 | // The responseHeader is included in the response to the client's upgrade 251 | // request. Use the responseHeader to specify cookies (Set-Cookie) and the 252 | // negotiated subprotocol (Sec-Websocket-Protocol). 253 | // 254 | // The connection buffers IO to the underlying network connection. The 255 | // readBufSize and writeBufSize parameters specify the size of the buffers to 256 | // use. Messages can be larger than the buffers. 257 | // 258 | // If the request is not a valid WebSocket handshake, then Upgrade returns an 259 | // error of type HandshakeError. Applications should handle this error by 260 | // replying to the client with an HTTP error response. 261 | func Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header, readBufSize, writeBufSize int) (*Conn, error) { 262 | u := Upgrader{ReadBufferSize: readBufSize, WriteBufferSize: writeBufSize} 263 | u.Error = func(w http.ResponseWriter, r *http.Request, status int, reason error) { 264 | // don't return errors to maintain backwards compatibility 265 | } 266 | u.CheckOrigin = func(r *http.Request) bool { 267 | // allow all connections by default 268 | return true 269 | } 270 | return u.Upgrade(w, r, responseHeader) 271 | } 272 | 273 | // Subprotocols returns the subprotocols requested by the client in the 274 | // Sec-Websocket-Protocol header. 275 | func Subprotocols(r *http.Request) []string { 276 | h := strings.TrimSpace(r.Header.Get("Sec-Websocket-Protocol")) 277 | if h == "" { 278 | return nil 279 | } 280 | protocols := strings.Split(h, ",") 281 | for i := range protocols { 282 | protocols[i] = strings.TrimSpace(protocols[i]) 283 | } 284 | return protocols 285 | } 286 | 287 | // IsWebSocketUpgrade returns true if the client requested upgrade to the 288 | // WebSocket protocol. 289 | func IsWebSocketUpgrade(r *http.Request) bool { 290 | return tokenListContainsValue(r.Header, "Connection", "upgrade") && 291 | tokenListContainsValue(r.Header, "Upgrade", "websocket") 292 | } 293 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/util.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 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 websocket 6 | 7 | import ( 8 | "crypto/rand" 9 | "crypto/sha1" 10 | "encoding/base64" 11 | "io" 12 | "net/http" 13 | "strings" 14 | ) 15 | 16 | var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11") 17 | 18 | func computeAcceptKey(challengeKey string) string { 19 | h := sha1.New() 20 | h.Write([]byte(challengeKey)) 21 | h.Write(keyGUID) 22 | return base64.StdEncoding.EncodeToString(h.Sum(nil)) 23 | } 24 | 25 | func generateChallengeKey() (string, error) { 26 | p := make([]byte, 16) 27 | if _, err := io.ReadFull(rand.Reader, p); err != nil { 28 | return "", err 29 | } 30 | return base64.StdEncoding.EncodeToString(p), nil 31 | } 32 | 33 | // Octet types from RFC 2616. 34 | var octetTypes [256]byte 35 | 36 | const ( 37 | isTokenOctet = 1 << iota 38 | isSpaceOctet 39 | ) 40 | 41 | func init() { 42 | // From RFC 2616 43 | // 44 | // OCTET = 45 | // CHAR = 46 | // CTL = 47 | // CR = 48 | // LF = 49 | // SP = 50 | // HT = 51 | // <"> = 52 | // CRLF = CR LF 53 | // LWS = [CRLF] 1*( SP | HT ) 54 | // TEXT = 55 | // separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <"> 56 | // | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT 57 | // token = 1* 58 | // qdtext = > 59 | 60 | for c := 0; c < 256; c++ { 61 | var t byte 62 | isCtl := c <= 31 || c == 127 63 | isChar := 0 <= c && c <= 127 64 | isSeparator := strings.IndexRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) >= 0 65 | if strings.IndexRune(" \t\r\n", rune(c)) >= 0 { 66 | t |= isSpaceOctet 67 | } 68 | if isChar && !isCtl && !isSeparator { 69 | t |= isTokenOctet 70 | } 71 | octetTypes[c] = t 72 | } 73 | } 74 | 75 | func skipSpace(s string) (rest string) { 76 | i := 0 77 | for ; i < len(s); i++ { 78 | if octetTypes[s[i]]&isSpaceOctet == 0 { 79 | break 80 | } 81 | } 82 | return s[i:] 83 | } 84 | 85 | func nextToken(s string) (token, rest string) { 86 | i := 0 87 | for ; i < len(s); i++ { 88 | if octetTypes[s[i]]&isTokenOctet == 0 { 89 | break 90 | } 91 | } 92 | return s[:i], s[i:] 93 | } 94 | 95 | func nextTokenOrQuoted(s string) (value string, rest string) { 96 | if !strings.HasPrefix(s, "\"") { 97 | return nextToken(s) 98 | } 99 | s = s[1:] 100 | for i := 0; i < len(s); i++ { 101 | switch s[i] { 102 | case '"': 103 | return s[:i], s[i+1:] 104 | case '\\': 105 | p := make([]byte, len(s)-1) 106 | j := copy(p, s[:i]) 107 | escape := true 108 | for i = i + 1; i < len(s); i++ { 109 | b := s[i] 110 | switch { 111 | case escape: 112 | escape = false 113 | p[j] = b 114 | j += 1 115 | case b == '\\': 116 | escape = true 117 | case b == '"': 118 | return string(p[:j]), s[i+1:] 119 | default: 120 | p[j] = b 121 | j += 1 122 | } 123 | } 124 | return "", "" 125 | } 126 | } 127 | return "", "" 128 | } 129 | 130 | // tokenListContainsValue returns true if the 1#token header with the given 131 | // name contains token. 132 | func tokenListContainsValue(header http.Header, name string, value string) bool { 133 | headers: 134 | for _, s := range header[name] { 135 | for { 136 | var t string 137 | t, s = nextToken(skipSpace(s)) 138 | if t == "" { 139 | continue headers 140 | } 141 | s = skipSpace(s) 142 | if s != "" && s[0] != ',' { 143 | continue headers 144 | } 145 | if strings.EqualFold(t, value) { 146 | return true 147 | } 148 | if s == "" { 149 | continue headers 150 | } 151 | s = s[1:] 152 | } 153 | } 154 | return false 155 | } 156 | 157 | // parseExtensiosn parses WebSocket extensions from a header. 158 | func parseExtensions(header http.Header) []map[string]string { 159 | 160 | // From RFC 6455: 161 | // 162 | // Sec-WebSocket-Extensions = extension-list 163 | // extension-list = 1#extension 164 | // extension = extension-token *( ";" extension-param ) 165 | // extension-token = registered-token 166 | // registered-token = token 167 | // extension-param = token [ "=" (token | quoted-string) ] 168 | // ;When using the quoted-string syntax variant, the value 169 | // ;after quoted-string unescaping MUST conform to the 170 | // ;'token' ABNF. 171 | 172 | var result []map[string]string 173 | headers: 174 | for _, s := range header["Sec-Websocket-Extensions"] { 175 | for { 176 | var t string 177 | t, s = nextToken(skipSpace(s)) 178 | if t == "" { 179 | continue headers 180 | } 181 | ext := map[string]string{"": t} 182 | for { 183 | s = skipSpace(s) 184 | if !strings.HasPrefix(s, ";") { 185 | break 186 | } 187 | var k string 188 | k, s = nextToken(skipSpace(s[1:])) 189 | if k == "" { 190 | continue headers 191 | } 192 | s = skipSpace(s) 193 | var v string 194 | if strings.HasPrefix(s, "=") { 195 | v, s = nextTokenOrQuoted(skipSpace(s[1:])) 196 | s = skipSpace(s) 197 | } 198 | if s != "" && s[0] != ',' && s[0] != ';' { 199 | continue headers 200 | } 201 | ext[k] = v 202 | } 203 | if s != "" && s[0] != ',' { 204 | continue headers 205 | } 206 | result = append(result, ext) 207 | if s == "" { 208 | continue headers 209 | } 210 | s = s[1:] 211 | } 212 | } 213 | return result 214 | } 215 | -------------------------------------------------------------------------------- /vendor/github.com/qiniu/log/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | -------------------------------------------------------------------------------- /vendor/github.com/qiniu/log/README.md: -------------------------------------------------------------------------------- 1 | log 2 | === 3 | 4 | Extension module of golang logging -------------------------------------------------------------------------------- /vendor/github.com/qiniu/log/logext.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "os" 8 | "runtime" 9 | "strings" 10 | "sync" 11 | "time" 12 | ) 13 | 14 | // These flags define which text to prefix to each log entry generated by the Logger. 15 | const ( 16 | // Bits or'ed together to control what's printed. There is no control over the 17 | // order they appear (the order listed here) or the format they present (as 18 | // described in the comments). A colon appears after these items: 19 | // 2009/0123 01:23:23.123123 /a/b/c/d.go:23: message 20 | Ldate = 1 << iota // the date: 2009/0123 21 | Ltime // the time: 01:23:23 22 | Lmicroseconds // microsecond resolution: 01:23:23.123123. assumes Ltime. 23 | Llongfile // full file name and line number: /a/b/c/d.go:23 24 | Lshortfile // final file name element and line number: d.go:23. overrides Llongfile 25 | Lmodule // module name 26 | Llevel // level: 0(Debug), 1(Info), 2(Warn), 3(Error), 4(Panic), 5(Fatal) 27 | LstdFlags = Ldate | Ltime // initial values for the standard logger 28 | Ldefault = Lmodule | Llevel | Lshortfile | LstdFlags 29 | ) // [prefix][time][level][module][shortfile|longfile] 30 | 31 | const ( 32 | Ldebug = iota 33 | Linfo 34 | Lwarn 35 | Lerror 36 | Lpanic 37 | Lfatal 38 | ) 39 | 40 | var levels = []string{ 41 | "[DEBUG]", 42 | "[INFO]", 43 | "[WARN]", 44 | "[ERROR]", 45 | "[PANIC]", 46 | "[FATAL]", 47 | } 48 | 49 | // A Logger represents an active logging object that generates lines of 50 | // output to an io.Writer. Each logging operation makes a single call to 51 | // the Writer's Write method. A Logger can be used simultaneously from 52 | // multiple goroutines; it guarantees to serialize access to the Writer. 53 | type Logger struct { 54 | mu sync.Mutex // ensures atomic writes; protects the following fields 55 | prefix string // prefix to write at beginning of each line 56 | flag int // properties 57 | Level int 58 | out io.Writer // destination for output 59 | buf bytes.Buffer // for accumulating text to write 60 | levelStats [6]int64 61 | } 62 | 63 | // New creates a new Logger. The out variable sets the 64 | // destination to which log data will be written. 65 | // The prefix appears at the beginning of each generated log line. 66 | // The flag argument defines the logging properties. 67 | func New(out io.Writer, prefix string, flag int) *Logger { 68 | return &Logger{out: out, prefix: prefix, Level: 1, flag: flag} 69 | } 70 | 71 | var Std = New(os.Stderr, "", Ldefault) 72 | 73 | // Cheap integer to fixed-width decimal ASCII. Give a negative width to avoid zero-padding. 74 | // Knows the buffer has capacity. 75 | func itoa(buf *bytes.Buffer, i int, wid int) { 76 | var u uint = uint(i) 77 | if u == 0 && wid <= 1 { 78 | buf.WriteByte('0') 79 | return 80 | } 81 | 82 | // Assemble decimal in reverse order. 83 | var b [32]byte 84 | bp := len(b) 85 | for ; u > 0 || wid > 0; u /= 10 { 86 | bp-- 87 | wid-- 88 | b[bp] = byte(u%10) + '0' 89 | } 90 | 91 | // avoid slicing b to avoid an allocation. 92 | for bp < len(b) { 93 | buf.WriteByte(b[bp]) 94 | bp++ 95 | } 96 | } 97 | 98 | func moduleOf(file string) string { 99 | pos := strings.LastIndex(file, "/") 100 | if pos != -1 { 101 | pos1 := strings.LastIndex(file[:pos], "/src/") 102 | if pos1 != -1 { 103 | return file[pos1+5 : pos] 104 | } 105 | } 106 | return "UNKNOWN" 107 | } 108 | 109 | func (l *Logger) formatHeader(buf *bytes.Buffer, t time.Time, file string, line int, lvl int, reqId string) { 110 | if l.prefix != "" { 111 | buf.WriteString(l.prefix) 112 | } 113 | if l.flag&(Ldate|Ltime|Lmicroseconds) != 0 { 114 | if l.flag&Ldate != 0 { 115 | year, month, day := t.Date() 116 | itoa(buf, year, 4) 117 | buf.WriteByte('/') 118 | itoa(buf, int(month), 2) 119 | buf.WriteByte('/') 120 | itoa(buf, day, 2) 121 | buf.WriteByte(' ') 122 | } 123 | if l.flag&(Ltime|Lmicroseconds) != 0 { 124 | hour, min, sec := t.Clock() 125 | itoa(buf, hour, 2) 126 | buf.WriteByte(':') 127 | itoa(buf, min, 2) 128 | buf.WriteByte(':') 129 | itoa(buf, sec, 2) 130 | if l.flag&Lmicroseconds != 0 { 131 | buf.WriteByte('.') 132 | itoa(buf, t.Nanosecond()/1e3, 6) 133 | } 134 | buf.WriteByte(' ') 135 | } 136 | } 137 | if reqId != "" { 138 | buf.WriteByte('[') 139 | buf.WriteString(reqId) 140 | buf.WriteByte(']') 141 | } 142 | if l.flag&Llevel != 0 { 143 | buf.WriteString(levels[lvl]) 144 | } 145 | if l.flag&Lmodule != 0 { 146 | buf.WriteByte('[') 147 | buf.WriteString(moduleOf(file)) 148 | buf.WriteByte(']') 149 | buf.WriteByte(' ') 150 | } 151 | if l.flag&(Lshortfile|Llongfile) != 0 { 152 | if l.flag&Lshortfile != 0 { 153 | short := file 154 | for i := len(file) - 1; i > 0; i-- { 155 | if file[i] == '/' { 156 | short = file[i+1:] 157 | break 158 | } 159 | } 160 | file = short 161 | } 162 | buf.WriteString(file) 163 | buf.WriteByte(':') 164 | itoa(buf, line, -1) 165 | buf.WriteString(": ") 166 | } 167 | } 168 | 169 | // Output writes the output for a logging event. The string s contains 170 | // the text to print after the prefix specified by the flags of the 171 | // Logger. A newline is appended if the last character of s is not 172 | // already a newline. Calldepth is used to recover the PC and is 173 | // provided for generality, although at the moment on all pre-defined 174 | // paths it will be 2. 175 | func (l *Logger) Output(reqId string, lvl int, calldepth int, s string) error { 176 | if lvl < l.Level { 177 | return nil 178 | } 179 | now := time.Now() // get this early. 180 | var file string 181 | var line int 182 | l.mu.Lock() 183 | defer l.mu.Unlock() 184 | if l.flag&(Lshortfile|Llongfile|Lmodule) != 0 { 185 | // release lock while getting caller info - it's expensive. 186 | l.mu.Unlock() 187 | var ok bool 188 | _, file, line, ok = runtime.Caller(calldepth) 189 | if !ok { 190 | file = "???" 191 | line = 0 192 | } 193 | l.mu.Lock() 194 | } 195 | l.levelStats[lvl]++ 196 | l.buf.Reset() 197 | l.formatHeader(&l.buf, now, file, line, lvl, reqId) 198 | l.buf.WriteString(s) 199 | if len(s) > 0 && s[len(s)-1] != '\n' { 200 | l.buf.WriteByte('\n') 201 | } 202 | _, err := l.out.Write(l.buf.Bytes()) 203 | return err 204 | } 205 | 206 | // ----------------------------------------- 207 | 208 | // Printf calls l.Output to print to the logger. 209 | // Arguments are handled in the manner of fmt.Printf. 210 | func (l *Logger) Printf(format string, v ...interface{}) { 211 | l.Output("", Linfo, 2, fmt.Sprintf(format, v...)) 212 | } 213 | 214 | // Print calls l.Output to print to the logger. 215 | // Arguments are handled in the manner of fmt.Print. 216 | func (l *Logger) Print(v ...interface{}) { l.Output("", Linfo, 2, fmt.Sprint(v...)) } 217 | 218 | // Println calls l.Output to print to the logger. 219 | // Arguments are handled in the manner of fmt.Println. 220 | func (l *Logger) Println(v ...interface{}) { l.Output("", Linfo, 2, fmt.Sprintln(v...)) } 221 | 222 | // ----------------------------------------- 223 | 224 | func (l *Logger) Debugf(format string, v ...interface{}) { 225 | if Ldebug < l.Level { 226 | return 227 | } 228 | l.Output("", Ldebug, 2, fmt.Sprintf(format, v...)) 229 | } 230 | 231 | func (l *Logger) Debug(v ...interface{}) { 232 | if Ldebug < l.Level { 233 | return 234 | } 235 | l.Output("", Ldebug, 2, fmt.Sprintln(v...)) 236 | } 237 | 238 | // ----------------------------------------- 239 | 240 | func (l *Logger) Infof(format string, v ...interface{}) { 241 | if Linfo < l.Level { 242 | return 243 | } 244 | l.Output("", Linfo, 2, fmt.Sprintf(format, v...)) 245 | } 246 | 247 | func (l *Logger) Info(v ...interface{}) { 248 | if Linfo < l.Level { 249 | return 250 | } 251 | l.Output("", Linfo, 2, fmt.Sprintln(v...)) 252 | } 253 | 254 | // ----------------------------------------- 255 | 256 | func (l *Logger) Warnf(format string, v ...interface{}) { 257 | l.Output("", Lwarn, 2, fmt.Sprintf(format, v...)) 258 | } 259 | 260 | func (l *Logger) Warn(v ...interface{}) { l.Output("", Lwarn, 2, fmt.Sprintln(v...)) } 261 | 262 | // ----------------------------------------- 263 | 264 | func (l *Logger) Errorf(format string, v ...interface{}) { 265 | l.Output("", Lerror, 2, fmt.Sprintf(format, v...)) 266 | } 267 | 268 | func (l *Logger) Error(v ...interface{}) { l.Output("", Lerror, 2, fmt.Sprintln(v...)) } 269 | 270 | // ----------------------------------------- 271 | 272 | func (l *Logger) Fatal(v ...interface{}) { 273 | l.Output("", Lfatal, 2, fmt.Sprint(v...)) 274 | os.Exit(1) 275 | } 276 | 277 | // Fatalf is equivalent to l.Printf() followed by a call to os.Exit(1). 278 | func (l *Logger) Fatalf(format string, v ...interface{}) { 279 | l.Output("", Lfatal, 2, fmt.Sprintf(format, v...)) 280 | os.Exit(1) 281 | } 282 | 283 | // Fatalln is equivalent to l.Println() followed by a call to os.Exit(1). 284 | func (l *Logger) Fatalln(v ...interface{}) { 285 | l.Output("", Lfatal, 2, fmt.Sprintln(v...)) 286 | os.Exit(1) 287 | } 288 | 289 | // ----------------------------------------- 290 | 291 | // Panic is equivalent to l.Print() followed by a call to panic(). 292 | func (l *Logger) Panic(v ...interface{}) { 293 | s := fmt.Sprint(v...) 294 | l.Output("", Lpanic, 2, s) 295 | panic(s) 296 | } 297 | 298 | // Panicf is equivalent to l.Printf() followed by a call to panic(). 299 | func (l *Logger) Panicf(format string, v ...interface{}) { 300 | s := fmt.Sprintf(format, v...) 301 | l.Output("", Lpanic, 2, s) 302 | panic(s) 303 | } 304 | 305 | // Panicln is equivalent to l.Println() followed by a call to panic(). 306 | func (l *Logger) Panicln(v ...interface{}) { 307 | s := fmt.Sprintln(v...) 308 | l.Output("", Lpanic, 2, s) 309 | panic(s) 310 | } 311 | 312 | // ----------------------------------------- 313 | 314 | func (l *Logger) Stack(v ...interface{}) { 315 | s := fmt.Sprint(v...) 316 | s += "\n" 317 | buf := make([]byte, 1024*1024) 318 | n := runtime.Stack(buf, true) 319 | s += string(buf[:n]) 320 | s += "\n" 321 | l.Output("", Lerror, 2, s) 322 | } 323 | 324 | // ----------------------------------------- 325 | 326 | func (l *Logger) Stat() (stats []int64) { 327 | l.mu.Lock() 328 | v := l.levelStats 329 | l.mu.Unlock() 330 | return v[:] 331 | } 332 | 333 | // Flags returns the output flags for the logger. 334 | func (l *Logger) Flags() int { 335 | l.mu.Lock() 336 | defer l.mu.Unlock() 337 | return l.flag 338 | } 339 | 340 | // SetFlags sets the output flags for the logger. 341 | func (l *Logger) SetFlags(flag int) { 342 | l.mu.Lock() 343 | defer l.mu.Unlock() 344 | l.flag = flag 345 | } 346 | 347 | // Prefix returns the output prefix for the logger. 348 | func (l *Logger) Prefix() string { 349 | l.mu.Lock() 350 | defer l.mu.Unlock() 351 | return l.prefix 352 | } 353 | 354 | // SetPrefix sets the output prefix for the logger. 355 | func (l *Logger) SetPrefix(prefix string) { 356 | l.mu.Lock() 357 | defer l.mu.Unlock() 358 | l.prefix = prefix 359 | } 360 | 361 | // SetOutputLevel sets the output level for the logger. 362 | func (l *Logger) SetOutputLevel(lvl int) { 363 | l.mu.Lock() 364 | defer l.mu.Unlock() 365 | l.Level = lvl 366 | } 367 | 368 | // SetOutput sets the output destination for the standard logger. 369 | func SetOutput(w io.Writer) { 370 | Std.mu.Lock() 371 | defer Std.mu.Unlock() 372 | Std.out = w 373 | } 374 | 375 | // Flags returns the output flags for the standard logger. 376 | func Flags() int { 377 | return Std.Flags() 378 | } 379 | 380 | // SetFlags sets the output flags for the standard logger. 381 | func SetFlags(flag int) { 382 | Std.SetFlags(flag) 383 | } 384 | 385 | // Prefix returns the output prefix for the standard logger. 386 | func Prefix() string { 387 | return Std.Prefix() 388 | } 389 | 390 | // SetPrefix sets the output prefix for the standard logger. 391 | func SetPrefix(prefix string) { 392 | Std.SetPrefix(prefix) 393 | } 394 | 395 | func SetOutputLevel(lvl int) { 396 | Std.SetOutputLevel(lvl) 397 | } 398 | 399 | func GetOutputLevel() int { 400 | return Std.Level 401 | } 402 | 403 | // ----------------------------------------- 404 | 405 | // Print calls Output to print to the standard logger. 406 | // Arguments are handled in the manner of fmt.Print. 407 | func Print(v ...interface{}) { 408 | Std.Output("", Linfo, 2, fmt.Sprint(v...)) 409 | } 410 | 411 | // Printf calls Output to print to the standard logger. 412 | // Arguments are handled in the manner of fmt.Printf. 413 | func Printf(format string, v ...interface{}) { 414 | Std.Output("", Linfo, 2, fmt.Sprintf(format, v...)) 415 | } 416 | 417 | // Println calls Output to print to the standard logger. 418 | // Arguments are handled in the manner of fmt.Println. 419 | func Println(v ...interface{}) { 420 | Std.Output("", Linfo, 2, fmt.Sprintln(v...)) 421 | } 422 | 423 | // ----------------------------------------- 424 | 425 | func Debugf(format string, v ...interface{}) { 426 | if Ldebug < Std.Level { 427 | return 428 | } 429 | Std.Output("", Ldebug, 2, fmt.Sprintf(format, v...)) 430 | } 431 | 432 | func Debug(v ...interface{}) { 433 | if Ldebug < Std.Level { 434 | return 435 | } 436 | Std.Output("", Ldebug, 2, fmt.Sprintln(v...)) 437 | } 438 | 439 | // ----------------------------------------- 440 | 441 | func Infof(format string, v ...interface{}) { 442 | if Linfo < Std.Level { 443 | return 444 | } 445 | Std.Output("", Linfo, 2, fmt.Sprintf(format, v...)) 446 | } 447 | 448 | func Info(v ...interface{}) { 449 | if Linfo < Std.Level { 450 | return 451 | } 452 | Std.Output("", Linfo, 2, fmt.Sprintln(v...)) 453 | } 454 | 455 | // ----------------------------------------- 456 | 457 | func Warnf(format string, v ...interface{}) { 458 | Std.Output("", Lwarn, 2, fmt.Sprintf(format, v...)) 459 | } 460 | 461 | func Warn(v ...interface{}) { Std.Output("", Lwarn, 2, fmt.Sprintln(v...)) } 462 | 463 | // ----------------------------------------- 464 | 465 | func Errorf(format string, v ...interface{}) { 466 | Std.Output("", Lerror, 2, fmt.Sprintf(format, v...)) 467 | } 468 | 469 | func Error(v ...interface{}) { Std.Output("", Lerror, 2, fmt.Sprintln(v...)) } 470 | 471 | // ----------------------------------------- 472 | 473 | // Fatal is equivalent to Print() followed by a call to os.Exit(1). 474 | func Fatal(v ...interface{}) { 475 | Std.Output("", Lfatal, 2, fmt.Sprint(v...)) 476 | os.Exit(1) 477 | } 478 | 479 | // Fatalf is equivalent to Printf() followed by a call to os.Exit(1). 480 | func Fatalf(format string, v ...interface{}) { 481 | Std.Output("", Lfatal, 2, fmt.Sprintf(format, v...)) 482 | os.Exit(1) 483 | } 484 | 485 | // Fatalln is equivalent to Println() followed by a call to os.Exit(1). 486 | func Fatalln(v ...interface{}) { 487 | Std.Output("", Lfatal, 2, fmt.Sprintln(v...)) 488 | os.Exit(1) 489 | } 490 | 491 | // ----------------------------------------- 492 | 493 | // Panic is equivalent to Print() followed by a call to panic(). 494 | func Panic(v ...interface{}) { 495 | s := fmt.Sprint(v...) 496 | Std.Output("", Lpanic, 2, s) 497 | panic(s) 498 | } 499 | 500 | // Panicf is equivalent to Printf() followed by a call to panic(). 501 | func Panicf(format string, v ...interface{}) { 502 | s := fmt.Sprintf(format, v...) 503 | Std.Output("", Lpanic, 2, s) 504 | panic(s) 505 | } 506 | 507 | // Panicln is equivalent to Println() followed by a call to panic(). 508 | func Panicln(v ...interface{}) { 509 | s := fmt.Sprintln(v...) 510 | Std.Output("", Lpanic, 2, s) 511 | panic(s) 512 | } 513 | 514 | // ----------------------------------------- 515 | 516 | func Stack(v ...interface{}) { 517 | s := fmt.Sprint(v...) 518 | s += "\n" 519 | buf := make([]byte, 1024*1024) 520 | n := runtime.Stack(buf, true) 521 | s += string(buf[:n]) 522 | s += "\n" 523 | Std.Output("", Lerror, 2, s) 524 | } 525 | 526 | // ----------------------------------------- 527 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: go 3 | install: go get -t -v ./... 4 | go: 1.2 5 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/COPYING: -------------------------------------------------------------------------------- 1 | Copyright (C) 2014 Alec Thomas 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/actions.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | // Action callback executed at various stages after all values are populated. 4 | // The application, commands, arguments and flags all have corresponding 5 | // actions. 6 | type Action func(*ParseContext) error 7 | 8 | type actionMixin struct { 9 | actions []Action 10 | preActions []Action 11 | } 12 | 13 | type actionApplier interface { 14 | applyActions(*ParseContext) error 15 | applyPreActions(*ParseContext) error 16 | } 17 | 18 | func (a *actionMixin) addAction(action Action) { 19 | a.actions = append(a.actions, action) 20 | } 21 | 22 | func (a *actionMixin) addPreAction(action Action) { 23 | a.preActions = append(a.preActions, action) 24 | } 25 | 26 | func (a *actionMixin) applyActions(context *ParseContext) error { 27 | for _, action := range a.actions { 28 | if err := action(context); err != nil { 29 | return err 30 | } 31 | } 32 | return nil 33 | } 34 | 35 | func (a *actionMixin) applyPreActions(context *ParseContext) error { 36 | for _, preAction := range a.preActions { 37 | if err := preAction(context); err != nil { 38 | return err 39 | } 40 | } 41 | return nil 42 | } 43 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/args.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type argGroup struct { 8 | args []*ArgClause 9 | } 10 | 11 | func newArgGroup() *argGroup { 12 | return &argGroup{} 13 | } 14 | 15 | func (a *argGroup) have() bool { 16 | return len(a.args) > 0 17 | } 18 | 19 | // GetArg gets an argument definition. 20 | // 21 | // This allows existing arguments to be modified after definition but before parsing. Useful for 22 | // modular applications. 23 | func (a *argGroup) GetArg(name string) *ArgClause { 24 | for _, arg := range a.args { 25 | if arg.name == name { 26 | return arg 27 | } 28 | } 29 | return nil 30 | } 31 | 32 | func (a *argGroup) Arg(name, help string) *ArgClause { 33 | arg := newArg(name, help) 34 | a.args = append(a.args, arg) 35 | return arg 36 | } 37 | 38 | func (a *argGroup) init() error { 39 | required := 0 40 | seen := map[string]struct{}{} 41 | previousArgMustBeLast := false 42 | for i, arg := range a.args { 43 | if previousArgMustBeLast { 44 | return fmt.Errorf("Args() can't be followed by another argument '%s'", arg.name) 45 | } 46 | if arg.consumesRemainder() { 47 | previousArgMustBeLast = true 48 | } 49 | if _, ok := seen[arg.name]; ok { 50 | return fmt.Errorf("duplicate argument '%s'", arg.name) 51 | } 52 | seen[arg.name] = struct{}{} 53 | if arg.required && required != i { 54 | return fmt.Errorf("required arguments found after non-required") 55 | } 56 | if arg.required { 57 | required++ 58 | } 59 | if err := arg.init(); err != nil { 60 | return err 61 | } 62 | } 63 | return nil 64 | } 65 | 66 | type ArgClause struct { 67 | actionMixin 68 | parserMixin 69 | completionsMixin 70 | envarMixin 71 | name string 72 | help string 73 | defaultValues []string 74 | required bool 75 | } 76 | 77 | func newArg(name, help string) *ArgClause { 78 | a := &ArgClause{ 79 | name: name, 80 | help: help, 81 | } 82 | return a 83 | } 84 | 85 | func (a *ArgClause) setDefault() error { 86 | if a.HasEnvarValue() { 87 | if v, ok := a.value.(remainderArg); !ok || !v.IsCumulative() { 88 | // Use the value as-is 89 | return a.value.Set(a.GetEnvarValue()) 90 | } else { 91 | for _, value := range a.GetSplitEnvarValue() { 92 | if err := a.value.Set(value); err != nil { 93 | return err 94 | } 95 | } 96 | return nil 97 | } 98 | } 99 | 100 | if len(a.defaultValues) > 0 { 101 | for _, defaultValue := range a.defaultValues { 102 | if err := a.value.Set(defaultValue); err != nil { 103 | return err 104 | } 105 | } 106 | return nil 107 | } 108 | 109 | return nil 110 | } 111 | 112 | func (a *ArgClause) needsValue() bool { 113 | haveDefault := len(a.defaultValues) > 0 114 | return a.required && !(haveDefault || a.HasEnvarValue()) 115 | } 116 | 117 | func (a *ArgClause) consumesRemainder() bool { 118 | if r, ok := a.value.(remainderArg); ok { 119 | return r.IsCumulative() 120 | } 121 | return false 122 | } 123 | 124 | // Required arguments must be input by the user. They can not have a Default() value provided. 125 | func (a *ArgClause) Required() *ArgClause { 126 | a.required = true 127 | return a 128 | } 129 | 130 | // Default values for this argument. They *must* be parseable by the value of the argument. 131 | func (a *ArgClause) Default(values ...string) *ArgClause { 132 | a.defaultValues = values 133 | return a 134 | } 135 | 136 | // Envar overrides the default value(s) for a flag from an environment variable, 137 | // if it is set. Several default values can be provided by using new lines to 138 | // separate them. 139 | func (a *ArgClause) Envar(name string) *ArgClause { 140 | a.envar = name 141 | a.noEnvar = false 142 | return a 143 | } 144 | 145 | // NoEnvar forces environment variable defaults to be disabled for this flag. 146 | // Most useful in conjunction with app.DefaultEnvars(). 147 | func (a *ArgClause) NoEnvar() *ArgClause { 148 | a.envar = "" 149 | a.noEnvar = true 150 | return a 151 | } 152 | 153 | func (a *ArgClause) Action(action Action) *ArgClause { 154 | a.addAction(action) 155 | return a 156 | } 157 | 158 | func (a *ArgClause) PreAction(action Action) *ArgClause { 159 | a.addPreAction(action) 160 | return a 161 | } 162 | 163 | // HintAction registers a HintAction (function) for the arg to provide completions 164 | func (a *ArgClause) HintAction(action HintAction) *ArgClause { 165 | a.addHintAction(action) 166 | return a 167 | } 168 | 169 | // HintOptions registers any number of options for the flag to provide completions 170 | func (a *ArgClause) HintOptions(options ...string) *ArgClause { 171 | a.addHintAction(func() []string { 172 | return options 173 | }) 174 | return a 175 | } 176 | 177 | func (a *ArgClause) init() error { 178 | if a.required && len(a.defaultValues) > 0 { 179 | return fmt.Errorf("required argument '%s' with unusable default value", a.name) 180 | } 181 | if a.value == nil { 182 | return fmt.Errorf("no parser defined for arg '%s'", a.name) 183 | } 184 | return nil 185 | } 186 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/cmd.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | type cmdMixin struct { 9 | *flagGroup 10 | *argGroup 11 | *cmdGroup 12 | actionMixin 13 | } 14 | 15 | // CmdCompletion returns completion options for arguments, if that's where 16 | // parsing left off, or commands if there aren't any unsatisfied args. 17 | func (c *cmdMixin) CmdCompletion(context *ParseContext) []string { 18 | var options []string 19 | 20 | // Count args already satisfied - we won't complete those, and add any 21 | // default commands' alternatives, since they weren't listed explicitly 22 | // and the user may want to explicitly list something else. 23 | argsSatisfied := 0 24 | for _, el := range context.Elements { 25 | switch clause := el.Clause.(type) { 26 | case *ArgClause: 27 | if el.Value != nil && *el.Value != "" { 28 | argsSatisfied++ 29 | } 30 | case *CmdClause: 31 | options = append(options, clause.completionAlts...) 32 | default: 33 | } 34 | } 35 | 36 | if argsSatisfied < len(c.argGroup.args) { 37 | // Since not all args have been satisfied, show options for the current one 38 | options = append(options, c.argGroup.args[argsSatisfied].resolveCompletions()...) 39 | } else { 40 | // If all args are satisfied, then go back to completing commands 41 | for _, cmd := range c.cmdGroup.commandOrder { 42 | if !cmd.hidden { 43 | options = append(options, cmd.name) 44 | } 45 | } 46 | } 47 | 48 | return options 49 | } 50 | 51 | func (c *cmdMixin) FlagCompletion(flagName string, flagValue string) (choices []string, flagMatch bool, optionMatch bool) { 52 | // Check if flagName matches a known flag. 53 | // If it does, show the options for the flag 54 | // Otherwise, show all flags 55 | 56 | options := []string{} 57 | 58 | for _, flag := range c.flagGroup.flagOrder { 59 | // Loop through each flag and determine if a match exists 60 | if flag.name == flagName { 61 | // User typed entire flag. Need to look for flag options. 62 | options = flag.resolveCompletions() 63 | if len(options) == 0 { 64 | // No Options to Choose From, Assume Match. 65 | return options, true, true 66 | } 67 | 68 | // Loop options to find if the user specified value matches 69 | isPrefix := false 70 | matched := false 71 | 72 | for _, opt := range options { 73 | if flagValue == opt { 74 | matched = true 75 | } else if strings.HasPrefix(opt, flagValue) { 76 | isPrefix = true 77 | } 78 | } 79 | 80 | // Matched Flag Directly 81 | // Flag Value Not Prefixed, and Matched Directly 82 | return options, true, !isPrefix && matched 83 | } 84 | 85 | if !flag.hidden { 86 | options = append(options, "--"+flag.name) 87 | } 88 | } 89 | // No Flag directly matched. 90 | return options, false, false 91 | 92 | } 93 | 94 | type cmdGroup struct { 95 | app *Application 96 | parent *CmdClause 97 | commands map[string]*CmdClause 98 | commandOrder []*CmdClause 99 | } 100 | 101 | func (c *cmdGroup) defaultSubcommand() *CmdClause { 102 | for _, cmd := range c.commandOrder { 103 | if cmd.isDefault { 104 | return cmd 105 | } 106 | } 107 | return nil 108 | } 109 | 110 | func (c *cmdGroup) cmdNames() []string { 111 | names := make([]string, 0, len(c.commandOrder)) 112 | for _, cmd := range c.commandOrder { 113 | names = append(names, cmd.name) 114 | } 115 | return names 116 | } 117 | 118 | // GetArg gets a command definition. 119 | // 120 | // This allows existing commands to be modified after definition but before parsing. Useful for 121 | // modular applications. 122 | func (c *cmdGroup) GetCommand(name string) *CmdClause { 123 | return c.commands[name] 124 | } 125 | 126 | func newCmdGroup(app *Application) *cmdGroup { 127 | return &cmdGroup{ 128 | app: app, 129 | commands: make(map[string]*CmdClause), 130 | } 131 | } 132 | 133 | func (c *cmdGroup) flattenedCommands() (out []*CmdClause) { 134 | for _, cmd := range c.commandOrder { 135 | if len(cmd.commands) == 0 { 136 | out = append(out, cmd) 137 | } 138 | out = append(out, cmd.flattenedCommands()...) 139 | } 140 | return 141 | } 142 | 143 | func (c *cmdGroup) addCommand(name, help string) *CmdClause { 144 | cmd := newCommand(c.app, name, help) 145 | c.commands[name] = cmd 146 | c.commandOrder = append(c.commandOrder, cmd) 147 | return cmd 148 | } 149 | 150 | func (c *cmdGroup) init() error { 151 | seen := map[string]bool{} 152 | if c.defaultSubcommand() != nil && !c.have() { 153 | return fmt.Errorf("default subcommand %q provided but no subcommands defined", c.defaultSubcommand().name) 154 | } 155 | defaults := []string{} 156 | for _, cmd := range c.commandOrder { 157 | if cmd.isDefault { 158 | defaults = append(defaults, cmd.name) 159 | } 160 | if seen[cmd.name] { 161 | return fmt.Errorf("duplicate command %q", cmd.name) 162 | } 163 | seen[cmd.name] = true 164 | for _, alias := range cmd.aliases { 165 | if seen[alias] { 166 | return fmt.Errorf("alias duplicates existing command %q", alias) 167 | } 168 | c.commands[alias] = cmd 169 | } 170 | if err := cmd.init(); err != nil { 171 | return err 172 | } 173 | } 174 | if len(defaults) > 1 { 175 | return fmt.Errorf("more than one default subcommand exists: %s", strings.Join(defaults, ", ")) 176 | } 177 | return nil 178 | } 179 | 180 | func (c *cmdGroup) have() bool { 181 | return len(c.commands) > 0 182 | } 183 | 184 | type CmdClauseValidator func(*CmdClause) error 185 | 186 | // A CmdClause is a single top-level command. It encapsulates a set of flags 187 | // and either subcommands or positional arguments. 188 | type CmdClause struct { 189 | cmdMixin 190 | app *Application 191 | name string 192 | aliases []string 193 | help string 194 | isDefault bool 195 | validator CmdClauseValidator 196 | hidden bool 197 | completionAlts []string 198 | } 199 | 200 | func newCommand(app *Application, name, help string) *CmdClause { 201 | c := &CmdClause{ 202 | app: app, 203 | name: name, 204 | help: help, 205 | } 206 | c.flagGroup = newFlagGroup() 207 | c.argGroup = newArgGroup() 208 | c.cmdGroup = newCmdGroup(app) 209 | return c 210 | } 211 | 212 | // Add an Alias for this command. 213 | func (c *CmdClause) Alias(name string) *CmdClause { 214 | c.aliases = append(c.aliases, name) 215 | return c 216 | } 217 | 218 | // Validate sets a validation function to run when parsing. 219 | func (c *CmdClause) Validate(validator CmdClauseValidator) *CmdClause { 220 | c.validator = validator 221 | return c 222 | } 223 | 224 | func (c *CmdClause) FullCommand() string { 225 | out := []string{c.name} 226 | for p := c.parent; p != nil; p = p.parent { 227 | out = append([]string{p.name}, out...) 228 | } 229 | return strings.Join(out, " ") 230 | } 231 | 232 | // Command adds a new sub-command. 233 | func (c *CmdClause) Command(name, help string) *CmdClause { 234 | cmd := c.addCommand(name, help) 235 | cmd.parent = c 236 | return cmd 237 | } 238 | 239 | // Default makes this command the default if commands don't match. 240 | func (c *CmdClause) Default() *CmdClause { 241 | c.isDefault = true 242 | return c 243 | } 244 | 245 | func (c *CmdClause) Action(action Action) *CmdClause { 246 | c.addAction(action) 247 | return c 248 | } 249 | 250 | func (c *CmdClause) PreAction(action Action) *CmdClause { 251 | c.addPreAction(action) 252 | return c 253 | } 254 | 255 | func (c *CmdClause) init() error { 256 | if err := c.flagGroup.init(c.app.defaultEnvarPrefix()); err != nil { 257 | return err 258 | } 259 | if c.argGroup.have() && c.cmdGroup.have() { 260 | return fmt.Errorf("can't mix Arg()s with Command()s") 261 | } 262 | if err := c.argGroup.init(); err != nil { 263 | return err 264 | } 265 | if err := c.cmdGroup.init(); err != nil { 266 | return err 267 | } 268 | return nil 269 | } 270 | 271 | func (c *CmdClause) Hidden() *CmdClause { 272 | c.hidden = true 273 | return c 274 | } 275 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/completions.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | // HintAction is a function type who is expected to return a slice of possible 4 | // command line arguments. 5 | type HintAction func() []string 6 | type completionsMixin struct { 7 | hintActions []HintAction 8 | builtinHintActions []HintAction 9 | } 10 | 11 | func (a *completionsMixin) addHintAction(action HintAction) { 12 | a.hintActions = append(a.hintActions, action) 13 | } 14 | 15 | // Allow adding of HintActions which are added internally, ie, EnumVar 16 | func (a *completionsMixin) addHintActionBuiltin(action HintAction) { 17 | a.builtinHintActions = append(a.builtinHintActions, action) 18 | } 19 | 20 | func (a *completionsMixin) resolveCompletions() []string { 21 | var hints []string 22 | 23 | options := a.builtinHintActions 24 | if len(a.hintActions) > 0 { 25 | // User specified their own hintActions. Use those instead. 26 | options = a.hintActions 27 | } 28 | 29 | for _, hintAction := range options { 30 | hints = append(hints, hintAction()...) 31 | } 32 | return hints 33 | } 34 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/doc.go: -------------------------------------------------------------------------------- 1 | // Package kingpin provides command line interfaces like this: 2 | // 3 | // $ chat 4 | // usage: chat [] [] [ ...] 5 | // 6 | // Flags: 7 | // --debug enable debug mode 8 | // --help Show help. 9 | // --server=127.0.0.1 server address 10 | // 11 | // Commands: 12 | // help 13 | // Show help for a command. 14 | // 15 | // post [] 16 | // Post a message to a channel. 17 | // 18 | // register 19 | // Register a new user. 20 | // 21 | // $ chat help post 22 | // usage: chat [] post [] [] 23 | // 24 | // Post a message to a channel. 25 | // 26 | // Flags: 27 | // --image=IMAGE image to post 28 | // 29 | // Args: 30 | // channel to post to 31 | // [] text to post 32 | // $ chat post --image=~/Downloads/owls.jpg pics 33 | // 34 | // From code like this: 35 | // 36 | // package main 37 | // 38 | // import "gopkg.in/alecthomas/kingpin.v1" 39 | // 40 | // var ( 41 | // debug = kingpin.Flag("debug", "enable debug mode").Default("false").Bool() 42 | // serverIP = kingpin.Flag("server", "server address").Default("127.0.0.1").IP() 43 | // 44 | // register = kingpin.Command("register", "Register a new user.") 45 | // registerNick = register.Arg("nick", "nickname for user").Required().String() 46 | // registerName = register.Arg("name", "name of user").Required().String() 47 | // 48 | // post = kingpin.Command("post", "Post a message to a channel.") 49 | // postImage = post.Flag("image", "image to post").ExistingFile() 50 | // postChannel = post.Arg("channel", "channel to post to").Required().String() 51 | // postText = post.Arg("text", "text to post").String() 52 | // ) 53 | // 54 | // func main() { 55 | // switch kingpin.Parse() { 56 | // // Register user 57 | // case "register": 58 | // println(*registerNick) 59 | // 60 | // // Post message 61 | // case "post": 62 | // if *postImage != nil { 63 | // } 64 | // if *postText != "" { 65 | // } 66 | // } 67 | // } 68 | package kingpin 69 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/envar.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "os" 5 | "regexp" 6 | ) 7 | 8 | var ( 9 | envVarValuesSeparator = "\r?\n" 10 | envVarValuesTrimmer = regexp.MustCompile(envVarValuesSeparator + "$") 11 | envVarValuesSplitter = regexp.MustCompile(envVarValuesSeparator) 12 | ) 13 | 14 | type envarMixin struct { 15 | envar string 16 | noEnvar bool 17 | } 18 | 19 | func (e *envarMixin) HasEnvarValue() bool { 20 | return e.GetEnvarValue() != "" 21 | } 22 | 23 | func (e *envarMixin) GetEnvarValue() string { 24 | if e.noEnvar || e.envar == "" { 25 | return "" 26 | } 27 | return os.Getenv(e.envar) 28 | } 29 | 30 | func (e *envarMixin) GetSplitEnvarValue() []string { 31 | values := make([]string, 0) 32 | 33 | envarValue := e.GetEnvarValue() 34 | if envarValue == "" { 35 | return values 36 | } 37 | 38 | // Split by new line to extract multiple values, if any. 39 | trimmed := envVarValuesTrimmer.ReplaceAllString(envarValue, "") 40 | for _, value := range envVarValuesSplitter.Split(trimmed, -1) { 41 | values = append(values, value) 42 | } 43 | 44 | return values 45 | } 46 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/flags.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | type flagGroup struct { 9 | short map[string]*FlagClause 10 | long map[string]*FlagClause 11 | flagOrder []*FlagClause 12 | } 13 | 14 | func newFlagGroup() *flagGroup { 15 | return &flagGroup{ 16 | short: map[string]*FlagClause{}, 17 | long: map[string]*FlagClause{}, 18 | } 19 | } 20 | 21 | // GetFlag gets a flag definition. 22 | // 23 | // This allows existing flags to be modified after definition but before parsing. Useful for 24 | // modular applications. 25 | func (f *flagGroup) GetFlag(name string) *FlagClause { 26 | return f.long[name] 27 | } 28 | 29 | // Flag defines a new flag with the given long name and help. 30 | func (f *flagGroup) Flag(name, help string) *FlagClause { 31 | flag := newFlag(name, help) 32 | f.long[name] = flag 33 | f.flagOrder = append(f.flagOrder, flag) 34 | return flag 35 | } 36 | 37 | func (f *flagGroup) init(defaultEnvarPrefix string) error { 38 | if err := f.checkDuplicates(); err != nil { 39 | return err 40 | } 41 | for _, flag := range f.long { 42 | if defaultEnvarPrefix != "" && !flag.noEnvar && flag.envar == "" { 43 | flag.envar = envarTransform(defaultEnvarPrefix + "_" + flag.name) 44 | } 45 | if err := flag.init(); err != nil { 46 | return err 47 | } 48 | if flag.shorthand != 0 { 49 | f.short[string(flag.shorthand)] = flag 50 | } 51 | } 52 | return nil 53 | } 54 | 55 | func (f *flagGroup) checkDuplicates() error { 56 | seenShort := map[byte]bool{} 57 | seenLong := map[string]bool{} 58 | for _, flag := range f.flagOrder { 59 | if flag.shorthand != 0 { 60 | if _, ok := seenShort[flag.shorthand]; ok { 61 | return fmt.Errorf("duplicate short flag -%c", flag.shorthand) 62 | } 63 | seenShort[flag.shorthand] = true 64 | } 65 | if _, ok := seenLong[flag.name]; ok { 66 | return fmt.Errorf("duplicate long flag --%s", flag.name) 67 | } 68 | seenLong[flag.name] = true 69 | } 70 | return nil 71 | } 72 | 73 | func (f *flagGroup) parse(context *ParseContext) (*FlagClause, error) { 74 | var token *Token 75 | 76 | loop: 77 | for { 78 | token = context.Peek() 79 | switch token.Type { 80 | case TokenEOL: 81 | break loop 82 | 83 | case TokenLong, TokenShort: 84 | flagToken := token 85 | defaultValue := "" 86 | var flag *FlagClause 87 | var ok bool 88 | invert := false 89 | 90 | name := token.Value 91 | if token.Type == TokenLong { 92 | flag, ok = f.long[name] 93 | if !ok { 94 | if strings.HasPrefix(name, "no-") { 95 | name = name[3:] 96 | invert = true 97 | } 98 | flag, ok = f.long[name] 99 | } 100 | if !ok { 101 | return nil, fmt.Errorf("unknown long flag '%s'", flagToken) 102 | } 103 | } else { 104 | flag, ok = f.short[name] 105 | if !ok { 106 | return nil, fmt.Errorf("unknown short flag '%s'", flagToken) 107 | } 108 | } 109 | 110 | context.Next() 111 | 112 | fb, ok := flag.value.(boolFlag) 113 | if ok && fb.IsBoolFlag() { 114 | if invert { 115 | defaultValue = "false" 116 | } else { 117 | defaultValue = "true" 118 | } 119 | } else { 120 | if invert { 121 | context.Push(token) 122 | return nil, fmt.Errorf("unknown long flag '%s'", flagToken) 123 | } 124 | token = context.Peek() 125 | if token.Type != TokenArg { 126 | context.Push(token) 127 | return nil, fmt.Errorf("expected argument for flag '%s'", flagToken) 128 | } 129 | context.Next() 130 | defaultValue = token.Value 131 | } 132 | 133 | context.matchedFlag(flag, defaultValue) 134 | return flag, nil 135 | 136 | default: 137 | break loop 138 | } 139 | } 140 | return nil, nil 141 | } 142 | 143 | // FlagClause is a fluid interface used to build flags. 144 | type FlagClause struct { 145 | parserMixin 146 | actionMixin 147 | completionsMixin 148 | envarMixin 149 | name string 150 | shorthand byte 151 | help string 152 | defaultValues []string 153 | placeholder string 154 | hidden bool 155 | } 156 | 157 | func newFlag(name, help string) *FlagClause { 158 | f := &FlagClause{ 159 | name: name, 160 | help: help, 161 | } 162 | return f 163 | } 164 | 165 | func (f *FlagClause) setDefault() error { 166 | if f.HasEnvarValue() { 167 | if v, ok := f.value.(repeatableFlag); !ok || !v.IsCumulative() { 168 | // Use the value as-is 169 | return f.value.Set(f.GetEnvarValue()) 170 | } else { 171 | for _, value := range f.GetSplitEnvarValue() { 172 | if err := f.value.Set(value); err != nil { 173 | return err 174 | } 175 | } 176 | return nil 177 | } 178 | } 179 | 180 | if len(f.defaultValues) > 0 { 181 | for _, defaultValue := range f.defaultValues { 182 | if err := f.value.Set(defaultValue); err != nil { 183 | return err 184 | } 185 | } 186 | return nil 187 | } 188 | 189 | return nil 190 | } 191 | 192 | func (f *FlagClause) needsValue() bool { 193 | haveDefault := len(f.defaultValues) > 0 194 | return f.required && !(haveDefault || f.HasEnvarValue()) 195 | } 196 | 197 | func (f *FlagClause) init() error { 198 | if f.required && len(f.defaultValues) > 0 { 199 | return fmt.Errorf("required flag '--%s' with default value that will never be used", f.name) 200 | } 201 | if f.value == nil { 202 | return fmt.Errorf("no type defined for --%s (eg. .String())", f.name) 203 | } 204 | if v, ok := f.value.(repeatableFlag); (!ok || !v.IsCumulative()) && len(f.defaultValues) > 1 { 205 | return fmt.Errorf("invalid default for '--%s', expecting single value", f.name) 206 | } 207 | return nil 208 | } 209 | 210 | // Dispatch to the given function after the flag is parsed and validated. 211 | func (f *FlagClause) Action(action Action) *FlagClause { 212 | f.addAction(action) 213 | return f 214 | } 215 | 216 | func (f *FlagClause) PreAction(action Action) *FlagClause { 217 | f.addPreAction(action) 218 | return f 219 | } 220 | 221 | // HintAction registers a HintAction (function) for the flag to provide completions 222 | func (a *FlagClause) HintAction(action HintAction) *FlagClause { 223 | a.addHintAction(action) 224 | return a 225 | } 226 | 227 | // HintOptions registers any number of options for the flag to provide completions 228 | func (a *FlagClause) HintOptions(options ...string) *FlagClause { 229 | a.addHintAction(func() []string { 230 | return options 231 | }) 232 | return a 233 | } 234 | 235 | func (a *FlagClause) EnumVar(target *string, options ...string) { 236 | a.parserMixin.EnumVar(target, options...) 237 | a.addHintActionBuiltin(func() []string { 238 | return options 239 | }) 240 | } 241 | 242 | func (a *FlagClause) Enum(options ...string) (target *string) { 243 | a.addHintActionBuiltin(func() []string { 244 | return options 245 | }) 246 | return a.parserMixin.Enum(options...) 247 | } 248 | 249 | // Default values for this flag. They *must* be parseable by the value of the flag. 250 | func (f *FlagClause) Default(values ...string) *FlagClause { 251 | f.defaultValues = values 252 | return f 253 | } 254 | 255 | // DEPRECATED: Use Envar(name) instead. 256 | func (f *FlagClause) OverrideDefaultFromEnvar(envar string) *FlagClause { 257 | return f.Envar(envar) 258 | } 259 | 260 | // Envar overrides the default value(s) for a flag from an environment variable, 261 | // if it is set. Several default values can be provided by using new lines to 262 | // separate them. 263 | func (f *FlagClause) Envar(name string) *FlagClause { 264 | f.envar = name 265 | f.noEnvar = false 266 | return f 267 | } 268 | 269 | // NoEnvar forces environment variable defaults to be disabled for this flag. 270 | // Most useful in conjunction with app.DefaultEnvars(). 271 | func (f *FlagClause) NoEnvar() *FlagClause { 272 | f.envar = "" 273 | f.noEnvar = true 274 | return f 275 | } 276 | 277 | // PlaceHolder sets the place-holder string used for flag values in the help. The 278 | // default behaviour is to use the value provided by Default() if provided, 279 | // then fall back on the capitalized flag name. 280 | func (f *FlagClause) PlaceHolder(placeholder string) *FlagClause { 281 | f.placeholder = placeholder 282 | return f 283 | } 284 | 285 | // Hidden hides a flag from usage but still allows it to be used. 286 | func (f *FlagClause) Hidden() *FlagClause { 287 | f.hidden = true 288 | return f 289 | } 290 | 291 | // Required makes the flag required. You can not provide a Default() value to a Required() flag. 292 | func (f *FlagClause) Required() *FlagClause { 293 | f.required = true 294 | return f 295 | } 296 | 297 | // Short sets the short flag name. 298 | func (f *FlagClause) Short(name byte) *FlagClause { 299 | f.shorthand = name 300 | return f 301 | } 302 | 303 | // Bool makes this flag a boolean flag. 304 | func (f *FlagClause) Bool() (target *bool) { 305 | target = new(bool) 306 | f.SetValue(newBoolValue(target)) 307 | return 308 | } 309 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/global.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | ) 7 | 8 | var ( 9 | // CommandLine is the default Kingpin parser. 10 | CommandLine = New(filepath.Base(os.Args[0]), "") 11 | // Global help flag. Exposed for user customisation. 12 | HelpFlag = CommandLine.HelpFlag 13 | // Top-level help command. Exposed for user customisation. May be nil. 14 | HelpCommand = CommandLine.HelpCommand 15 | // Global version flag. Exposed for user customisation. May be nil. 16 | VersionFlag = CommandLine.VersionFlag 17 | ) 18 | 19 | // Command adds a new command to the default parser. 20 | func Command(name, help string) *CmdClause { 21 | return CommandLine.Command(name, help) 22 | } 23 | 24 | // Flag adds a new flag to the default parser. 25 | func Flag(name, help string) *FlagClause { 26 | return CommandLine.Flag(name, help) 27 | } 28 | 29 | // Arg adds a new argument to the top-level of the default parser. 30 | func Arg(name, help string) *ArgClause { 31 | return CommandLine.Arg(name, help) 32 | } 33 | 34 | // Parse and return the selected command. Will call the termination handler if 35 | // an error is encountered. 36 | func Parse() string { 37 | selected := MustParse(CommandLine.Parse(os.Args[1:])) 38 | if selected == "" && CommandLine.cmdGroup.have() { 39 | Usage() 40 | CommandLine.terminate(0) 41 | } 42 | return selected 43 | } 44 | 45 | // Errorf prints an error message to stderr. 46 | func Errorf(format string, args ...interface{}) { 47 | CommandLine.Errorf(format, args...) 48 | } 49 | 50 | // Fatalf prints an error message to stderr and exits. 51 | func Fatalf(format string, args ...interface{}) { 52 | CommandLine.Fatalf(format, args...) 53 | } 54 | 55 | // FatalIfError prints an error and exits if err is not nil. The error is printed 56 | // with the given prefix. 57 | func FatalIfError(err error, format string, args ...interface{}) { 58 | CommandLine.FatalIfError(err, format, args...) 59 | } 60 | 61 | // FatalUsage prints an error message followed by usage information, then 62 | // exits with a non-zero status. 63 | func FatalUsage(format string, args ...interface{}) { 64 | CommandLine.FatalUsage(format, args...) 65 | } 66 | 67 | // FatalUsageContext writes a printf formatted error message to stderr, then 68 | // usage information for the given ParseContext, before exiting. 69 | func FatalUsageContext(context *ParseContext, format string, args ...interface{}) { 70 | CommandLine.FatalUsageContext(context, format, args...) 71 | } 72 | 73 | // Usage prints usage to stderr. 74 | func Usage() { 75 | CommandLine.Usage(os.Args[1:]) 76 | } 77 | 78 | // Set global usage template to use (defaults to DefaultUsageTemplate). 79 | func UsageTemplate(template string) *Application { 80 | return CommandLine.UsageTemplate(template) 81 | } 82 | 83 | // MustParse can be used with app.Parse(args) to exit with an error if parsing fails. 84 | func MustParse(command string, err error) string { 85 | if err != nil { 86 | Fatalf("%s, try --help", err) 87 | } 88 | return command 89 | } 90 | 91 | // Version adds a flag for displaying the application version number. 92 | func Version(version string) *Application { 93 | return CommandLine.Version(version) 94 | } 95 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/guesswidth.go: -------------------------------------------------------------------------------- 1 | // +build appengine !linux,!freebsd,!darwin,!dragonfly,!netbsd,!openbsd 2 | 3 | package kingpin 4 | 5 | import "io" 6 | 7 | func guessWidth(w io.Writer) int { 8 | return 80 9 | } 10 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/guesswidth_unix.go: -------------------------------------------------------------------------------- 1 | // +build !appengine,linux freebsd darwin dragonfly netbsd openbsd 2 | 3 | package kingpin 4 | 5 | import ( 6 | "io" 7 | "os" 8 | "strconv" 9 | "syscall" 10 | "unsafe" 11 | ) 12 | 13 | func guessWidth(w io.Writer) int { 14 | // check if COLUMNS env is set to comply with 15 | // http://pubs.opengroup.org/onlinepubs/009604499/basedefs/xbd_chap08.html 16 | colsStr := os.Getenv("COLUMNS") 17 | if colsStr != "" { 18 | if cols, err := strconv.Atoi(colsStr); err == nil { 19 | return cols 20 | } 21 | } 22 | 23 | if t, ok := w.(*os.File); ok { 24 | fd := t.Fd() 25 | var dimensions [4]uint16 26 | 27 | if _, _, err := syscall.Syscall6( 28 | syscall.SYS_IOCTL, 29 | uintptr(fd), 30 | uintptr(syscall.TIOCGWINSZ), 31 | uintptr(unsafe.Pointer(&dimensions)), 32 | 0, 0, 0, 33 | ); err == 0 { 34 | return int(dimensions[1]) 35 | } 36 | } 37 | return 80 38 | } 39 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/model.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "strings" 7 | ) 8 | 9 | // Data model for Kingpin command-line structure. 10 | 11 | type FlagGroupModel struct { 12 | Flags []*FlagModel 13 | } 14 | 15 | func (f *FlagGroupModel) FlagSummary() string { 16 | out := []string{} 17 | count := 0 18 | for _, flag := range f.Flags { 19 | if flag.Name != "help" { 20 | count++ 21 | } 22 | if flag.Required { 23 | if flag.IsBoolFlag() { 24 | out = append(out, fmt.Sprintf("--[no-]%s", flag.Name)) 25 | } else { 26 | out = append(out, fmt.Sprintf("--%s=%s", flag.Name, flag.FormatPlaceHolder())) 27 | } 28 | } 29 | } 30 | if count != len(out) { 31 | out = append(out, "[]") 32 | } 33 | return strings.Join(out, " ") 34 | } 35 | 36 | type FlagModel struct { 37 | Name string 38 | Help string 39 | Short rune 40 | Default []string 41 | Envar string 42 | PlaceHolder string 43 | Required bool 44 | Hidden bool 45 | Value Value 46 | } 47 | 48 | func (f *FlagModel) String() string { 49 | return f.Value.String() 50 | } 51 | 52 | func (f *FlagModel) IsBoolFlag() bool { 53 | if fl, ok := f.Value.(boolFlag); ok { 54 | return fl.IsBoolFlag() 55 | } 56 | return false 57 | } 58 | 59 | func (f *FlagModel) FormatPlaceHolder() string { 60 | if f.PlaceHolder != "" { 61 | return f.PlaceHolder 62 | } 63 | if len(f.Default) > 0 { 64 | ellipsis := "" 65 | if len(f.Default) > 1 { 66 | ellipsis = "..." 67 | } 68 | if _, ok := f.Value.(*stringValue); ok { 69 | return strconv.Quote(f.Default[0]) + ellipsis 70 | } 71 | return f.Default[0] + ellipsis 72 | } 73 | return strings.ToUpper(f.Name) 74 | } 75 | 76 | type ArgGroupModel struct { 77 | Args []*ArgModel 78 | } 79 | 80 | func (a *ArgGroupModel) ArgSummary() string { 81 | depth := 0 82 | out := []string{} 83 | for _, arg := range a.Args { 84 | h := "<" + arg.Name + ">" 85 | if !arg.Required { 86 | h = "[" + h 87 | depth++ 88 | } 89 | out = append(out, h) 90 | } 91 | out[len(out)-1] = out[len(out)-1] + strings.Repeat("]", depth) 92 | return strings.Join(out, " ") 93 | } 94 | 95 | type ArgModel struct { 96 | Name string 97 | Help string 98 | Default []string 99 | Envar string 100 | Required bool 101 | Value Value 102 | } 103 | 104 | func (a *ArgModel) String() string { 105 | return a.Value.String() 106 | } 107 | 108 | type CmdGroupModel struct { 109 | Commands []*CmdModel 110 | } 111 | 112 | func (c *CmdGroupModel) FlattenedCommands() (out []*CmdModel) { 113 | for _, cmd := range c.Commands { 114 | if len(cmd.Commands) == 0 { 115 | out = append(out, cmd) 116 | } 117 | out = append(out, cmd.FlattenedCommands()...) 118 | } 119 | return 120 | } 121 | 122 | type CmdModel struct { 123 | Name string 124 | Aliases []string 125 | Help string 126 | FullCommand string 127 | Depth int 128 | Hidden bool 129 | Default bool 130 | *FlagGroupModel 131 | *ArgGroupModel 132 | *CmdGroupModel 133 | } 134 | 135 | func (c *CmdModel) String() string { 136 | return c.FullCommand 137 | } 138 | 139 | type ApplicationModel struct { 140 | Name string 141 | Help string 142 | Version string 143 | Author string 144 | *ArgGroupModel 145 | *CmdGroupModel 146 | *FlagGroupModel 147 | } 148 | 149 | func (a *Application) Model() *ApplicationModel { 150 | return &ApplicationModel{ 151 | Name: a.Name, 152 | Help: a.Help, 153 | Version: a.version, 154 | Author: a.author, 155 | FlagGroupModel: a.flagGroup.Model(), 156 | ArgGroupModel: a.argGroup.Model(), 157 | CmdGroupModel: a.cmdGroup.Model(), 158 | } 159 | } 160 | 161 | func (a *argGroup) Model() *ArgGroupModel { 162 | m := &ArgGroupModel{} 163 | for _, arg := range a.args { 164 | m.Args = append(m.Args, arg.Model()) 165 | } 166 | return m 167 | } 168 | 169 | func (a *ArgClause) Model() *ArgModel { 170 | return &ArgModel{ 171 | Name: a.name, 172 | Help: a.help, 173 | Default: a.defaultValues, 174 | Envar: a.envar, 175 | Required: a.required, 176 | Value: a.value, 177 | } 178 | } 179 | 180 | func (f *flagGroup) Model() *FlagGroupModel { 181 | m := &FlagGroupModel{} 182 | for _, fl := range f.flagOrder { 183 | m.Flags = append(m.Flags, fl.Model()) 184 | } 185 | return m 186 | } 187 | 188 | func (f *FlagClause) Model() *FlagModel { 189 | return &FlagModel{ 190 | Name: f.name, 191 | Help: f.help, 192 | Short: rune(f.shorthand), 193 | Default: f.defaultValues, 194 | Envar: f.envar, 195 | PlaceHolder: f.placeholder, 196 | Required: f.required, 197 | Hidden: f.hidden, 198 | Value: f.value, 199 | } 200 | } 201 | 202 | func (c *cmdGroup) Model() *CmdGroupModel { 203 | m := &CmdGroupModel{} 204 | for _, cm := range c.commandOrder { 205 | m.Commands = append(m.Commands, cm.Model()) 206 | } 207 | return m 208 | } 209 | 210 | func (c *CmdClause) Model() *CmdModel { 211 | depth := 0 212 | for i := c; i != nil; i = i.parent { 213 | depth++ 214 | } 215 | return &CmdModel{ 216 | Name: c.name, 217 | Aliases: c.aliases, 218 | Help: c.help, 219 | Depth: depth, 220 | Hidden: c.hidden, 221 | Default: c.isDefault, 222 | FullCommand: c.FullCommand(), 223 | FlagGroupModel: c.flagGroup.Model(), 224 | ArgGroupModel: c.argGroup.Model(), 225 | CmdGroupModel: c.cmdGroup.Model(), 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/parser.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | "strings" 8 | ) 9 | 10 | type TokenType int 11 | 12 | // Token types. 13 | const ( 14 | TokenShort TokenType = iota 15 | TokenLong 16 | TokenArg 17 | TokenError 18 | TokenEOL 19 | ) 20 | 21 | func (t TokenType) String() string { 22 | switch t { 23 | case TokenShort: 24 | return "short flag" 25 | case TokenLong: 26 | return "long flag" 27 | case TokenArg: 28 | return "argument" 29 | case TokenError: 30 | return "error" 31 | case TokenEOL: 32 | return "" 33 | } 34 | return "?" 35 | } 36 | 37 | var ( 38 | TokenEOLMarker = Token{-1, TokenEOL, ""} 39 | ) 40 | 41 | type Token struct { 42 | Index int 43 | Type TokenType 44 | Value string 45 | } 46 | 47 | func (t *Token) Equal(o *Token) bool { 48 | return t.Index == o.Index 49 | } 50 | 51 | func (t *Token) IsFlag() bool { 52 | return t.Type == TokenShort || t.Type == TokenLong 53 | } 54 | 55 | func (t *Token) IsEOF() bool { 56 | return t.Type == TokenEOL 57 | } 58 | 59 | func (t *Token) String() string { 60 | switch t.Type { 61 | case TokenShort: 62 | return "-" + t.Value 63 | case TokenLong: 64 | return "--" + t.Value 65 | case TokenArg: 66 | return t.Value 67 | case TokenError: 68 | return "error: " + t.Value 69 | case TokenEOL: 70 | return "" 71 | default: 72 | panic("unhandled type") 73 | } 74 | } 75 | 76 | // A union of possible elements in a parse stack. 77 | type ParseElement struct { 78 | // Clause is either *CmdClause, *ArgClause or *FlagClause. 79 | Clause interface{} 80 | // Value is corresponding value for an ArgClause or FlagClause (if any). 81 | Value *string 82 | } 83 | 84 | // ParseContext holds the current context of the parser. When passed to 85 | // Action() callbacks Elements will be fully populated with *FlagClause, 86 | // *ArgClause and *CmdClause values and their corresponding arguments (if 87 | // any). 88 | type ParseContext struct { 89 | SelectedCommand *CmdClause 90 | ignoreDefault bool 91 | argsOnly bool 92 | peek []*Token 93 | argi int // Index of current command-line arg we're processing. 94 | args []string 95 | rawArgs []string 96 | flags *flagGroup 97 | arguments *argGroup 98 | argumenti int // Cursor into arguments 99 | // Flags, arguments and commands encountered and collected during parse. 100 | Elements []*ParseElement 101 | } 102 | 103 | func (p *ParseContext) nextArg() *ArgClause { 104 | if p.argumenti >= len(p.arguments.args) { 105 | return nil 106 | } 107 | arg := p.arguments.args[p.argumenti] 108 | if !arg.consumesRemainder() { 109 | p.argumenti++ 110 | } 111 | return arg 112 | } 113 | 114 | func (p *ParseContext) next() { 115 | p.argi++ 116 | p.args = p.args[1:] 117 | } 118 | 119 | // HasTrailingArgs returns true if there are unparsed command-line arguments. 120 | // This can occur if the parser can not match remaining arguments. 121 | func (p *ParseContext) HasTrailingArgs() bool { 122 | return len(p.args) > 0 123 | } 124 | 125 | func tokenize(args []string, ignoreDefault bool) *ParseContext { 126 | return &ParseContext{ 127 | ignoreDefault: ignoreDefault, 128 | args: args, 129 | rawArgs: args, 130 | flags: newFlagGroup(), 131 | arguments: newArgGroup(), 132 | } 133 | } 134 | 135 | func (p *ParseContext) mergeFlags(flags *flagGroup) { 136 | for _, flag := range flags.flagOrder { 137 | if flag.shorthand != 0 { 138 | p.flags.short[string(flag.shorthand)] = flag 139 | } 140 | p.flags.long[flag.name] = flag 141 | p.flags.flagOrder = append(p.flags.flagOrder, flag) 142 | } 143 | } 144 | 145 | func (p *ParseContext) mergeArgs(args *argGroup) { 146 | for _, arg := range args.args { 147 | p.arguments.args = append(p.arguments.args, arg) 148 | } 149 | } 150 | 151 | func (p *ParseContext) EOL() bool { 152 | return p.Peek().Type == TokenEOL 153 | } 154 | 155 | // Next token in the parse context. 156 | func (p *ParseContext) Next() *Token { 157 | if len(p.peek) > 0 { 158 | return p.pop() 159 | } 160 | 161 | // End of tokens. 162 | if len(p.args) == 0 { 163 | return &Token{Index: p.argi, Type: TokenEOL} 164 | } 165 | 166 | arg := p.args[0] 167 | p.next() 168 | 169 | if p.argsOnly { 170 | return &Token{p.argi, TokenArg, arg} 171 | } 172 | 173 | // All remaining args are passed directly. 174 | if arg == "--" { 175 | p.argsOnly = true 176 | return p.Next() 177 | } 178 | 179 | if strings.HasPrefix(arg, "--") { 180 | parts := strings.SplitN(arg[2:], "=", 2) 181 | token := &Token{p.argi, TokenLong, parts[0]} 182 | if len(parts) == 2 { 183 | p.Push(&Token{p.argi, TokenArg, parts[1]}) 184 | } 185 | return token 186 | } 187 | 188 | if strings.HasPrefix(arg, "-") { 189 | if len(arg) == 1 { 190 | return &Token{Index: p.argi, Type: TokenShort} 191 | } 192 | short := arg[1:2] 193 | flag, ok := p.flags.short[short] 194 | // Not a known short flag, we'll just return it anyway. 195 | if !ok { 196 | } else if fb, ok := flag.value.(boolFlag); ok && fb.IsBoolFlag() { 197 | // Bool short flag. 198 | } else { 199 | // Short flag with combined argument: -fARG 200 | token := &Token{p.argi, TokenShort, short} 201 | if len(arg) > 2 { 202 | p.Push(&Token{p.argi, TokenArg, arg[2:]}) 203 | } 204 | return token 205 | } 206 | 207 | if len(arg) > 2 { 208 | p.args = append([]string{"-" + arg[2:]}, p.args...) 209 | } 210 | return &Token{p.argi, TokenShort, short} 211 | } else if strings.HasPrefix(arg, "@") { 212 | expanded, err := ExpandArgsFromFile(arg[1:]) 213 | if err != nil { 214 | return &Token{p.argi, TokenError, err.Error()} 215 | } 216 | if p.argi >= len(p.args) { 217 | p.args = append(p.args[:p.argi-1], expanded...) 218 | } else { 219 | p.args = append(p.args[:p.argi-1], append(expanded, p.args[p.argi+1:]...)...) 220 | } 221 | return p.Next() 222 | } 223 | 224 | return &Token{p.argi, TokenArg, arg} 225 | } 226 | 227 | func (p *ParseContext) Peek() *Token { 228 | if len(p.peek) == 0 { 229 | return p.Push(p.Next()) 230 | } 231 | return p.peek[len(p.peek)-1] 232 | } 233 | 234 | func (p *ParseContext) Push(token *Token) *Token { 235 | p.peek = append(p.peek, token) 236 | return token 237 | } 238 | 239 | func (p *ParseContext) pop() *Token { 240 | end := len(p.peek) - 1 241 | token := p.peek[end] 242 | p.peek = p.peek[0:end] 243 | return token 244 | } 245 | 246 | func (p *ParseContext) String() string { 247 | return p.SelectedCommand.FullCommand() 248 | } 249 | 250 | func (p *ParseContext) matchedFlag(flag *FlagClause, value string) { 251 | p.Elements = append(p.Elements, &ParseElement{Clause: flag, Value: &value}) 252 | } 253 | 254 | func (p *ParseContext) matchedArg(arg *ArgClause, value string) { 255 | p.Elements = append(p.Elements, &ParseElement{Clause: arg, Value: &value}) 256 | } 257 | 258 | func (p *ParseContext) matchedCmd(cmd *CmdClause) { 259 | p.Elements = append(p.Elements, &ParseElement{Clause: cmd}) 260 | p.mergeFlags(cmd.flagGroup) 261 | p.mergeArgs(cmd.argGroup) 262 | p.SelectedCommand = cmd 263 | } 264 | 265 | // Expand arguments from a file. Lines starting with # will be treated as comments. 266 | func ExpandArgsFromFile(filename string) (out []string, err error) { 267 | r, err := os.Open(filename) 268 | if err != nil { 269 | return nil, err 270 | } 271 | defer r.Close() 272 | scanner := bufio.NewScanner(r) 273 | for scanner.Scan() { 274 | line := scanner.Text() 275 | if strings.HasPrefix(line, "#") { 276 | continue 277 | } 278 | out = append(out, line) 279 | } 280 | err = scanner.Err() 281 | return 282 | } 283 | 284 | func parse(context *ParseContext, app *Application) (err error) { 285 | context.mergeFlags(app.flagGroup) 286 | context.mergeArgs(app.argGroup) 287 | 288 | cmds := app.cmdGroup 289 | ignoreDefault := context.ignoreDefault 290 | 291 | loop: 292 | for !context.EOL() { 293 | token := context.Peek() 294 | 295 | switch token.Type { 296 | case TokenLong, TokenShort: 297 | if flag, err := context.flags.parse(context); err != nil { 298 | if !ignoreDefault { 299 | if cmd := cmds.defaultSubcommand(); cmd != nil { 300 | cmd.completionAlts = cmds.cmdNames() 301 | context.matchedCmd(cmd) 302 | cmds = cmd.cmdGroup 303 | break 304 | } 305 | } 306 | return err 307 | } else if flag == HelpFlag { 308 | ignoreDefault = true 309 | } 310 | 311 | case TokenArg: 312 | if cmds.have() { 313 | selectedDefault := false 314 | cmd, ok := cmds.commands[token.String()] 315 | if !ok { 316 | if !ignoreDefault { 317 | if cmd = cmds.defaultSubcommand(); cmd != nil { 318 | cmd.completionAlts = cmds.cmdNames() 319 | selectedDefault = true 320 | } 321 | } 322 | if cmd == nil { 323 | return fmt.Errorf("expected command but got %q", token) 324 | } 325 | } 326 | if cmd == HelpCommand { 327 | ignoreDefault = true 328 | } 329 | cmd.completionAlts = nil 330 | context.matchedCmd(cmd) 331 | cmds = cmd.cmdGroup 332 | if !selectedDefault { 333 | context.Next() 334 | } 335 | } else if context.arguments.have() { 336 | if app.noInterspersed { 337 | // no more flags 338 | context.argsOnly = true 339 | } 340 | arg := context.nextArg() 341 | if arg == nil { 342 | break loop 343 | } 344 | context.matchedArg(arg, token.String()) 345 | context.Next() 346 | } else { 347 | break loop 348 | } 349 | 350 | case TokenEOL: 351 | break loop 352 | } 353 | } 354 | 355 | // Move to innermost default command. 356 | for !ignoreDefault { 357 | if cmd := cmds.defaultSubcommand(); cmd != nil { 358 | cmd.completionAlts = cmds.cmdNames() 359 | context.matchedCmd(cmd) 360 | cmds = cmd.cmdGroup 361 | } else { 362 | break 363 | } 364 | } 365 | 366 | if !context.EOL() { 367 | return fmt.Errorf("unexpected %s", context.Peek()) 368 | } 369 | 370 | // Set defaults for all remaining args. 371 | for arg := context.nextArg(); arg != nil && !arg.consumesRemainder(); arg = context.nextArg() { 372 | for _, defaultValue := range arg.defaultValues { 373 | if err := arg.value.Set(defaultValue); err != nil { 374 | return fmt.Errorf("invalid default value '%s' for argument '%s'", defaultValue, arg.name) 375 | } 376 | } 377 | } 378 | 379 | return 380 | } 381 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/parsers.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "net" 5 | "net/url" 6 | "os" 7 | "time" 8 | 9 | "github.com/alecthomas/units" 10 | ) 11 | 12 | type Settings interface { 13 | SetValue(value Value) 14 | } 15 | 16 | type parserMixin struct { 17 | value Value 18 | required bool 19 | } 20 | 21 | func (p *parserMixin) SetValue(value Value) { 22 | p.value = value 23 | } 24 | 25 | // StringMap provides key=value parsing into a map. 26 | func (p *parserMixin) StringMap() (target *map[string]string) { 27 | target = &(map[string]string{}) 28 | p.StringMapVar(target) 29 | return 30 | } 31 | 32 | // Duration sets the parser to a time.Duration parser. 33 | func (p *parserMixin) Duration() (target *time.Duration) { 34 | target = new(time.Duration) 35 | p.DurationVar(target) 36 | return 37 | } 38 | 39 | // Bytes parses numeric byte units. eg. 1.5KB 40 | func (p *parserMixin) Bytes() (target *units.Base2Bytes) { 41 | target = new(units.Base2Bytes) 42 | p.BytesVar(target) 43 | return 44 | } 45 | 46 | // IP sets the parser to a net.IP parser. 47 | func (p *parserMixin) IP() (target *net.IP) { 48 | target = new(net.IP) 49 | p.IPVar(target) 50 | return 51 | } 52 | 53 | // TCP (host:port) address. 54 | func (p *parserMixin) TCP() (target **net.TCPAddr) { 55 | target = new(*net.TCPAddr) 56 | p.TCPVar(target) 57 | return 58 | } 59 | 60 | // TCPVar (host:port) address. 61 | func (p *parserMixin) TCPVar(target **net.TCPAddr) { 62 | p.SetValue(newTCPAddrValue(target)) 63 | } 64 | 65 | // ExistingFile sets the parser to one that requires and returns an existing file. 66 | func (p *parserMixin) ExistingFile() (target *string) { 67 | target = new(string) 68 | p.ExistingFileVar(target) 69 | return 70 | } 71 | 72 | // ExistingDir sets the parser to one that requires and returns an existing directory. 73 | func (p *parserMixin) ExistingDir() (target *string) { 74 | target = new(string) 75 | p.ExistingDirVar(target) 76 | return 77 | } 78 | 79 | // ExistingFileOrDir sets the parser to one that requires and returns an existing file OR directory. 80 | func (p *parserMixin) ExistingFileOrDir() (target *string) { 81 | target = new(string) 82 | p.ExistingFileOrDirVar(target) 83 | return 84 | } 85 | 86 | // File returns an os.File against an existing file. 87 | func (p *parserMixin) File() (target **os.File) { 88 | target = new(*os.File) 89 | p.FileVar(target) 90 | return 91 | } 92 | 93 | // File attempts to open a File with os.OpenFile(flag, perm). 94 | func (p *parserMixin) OpenFile(flag int, perm os.FileMode) (target **os.File) { 95 | target = new(*os.File) 96 | p.OpenFileVar(target, flag, perm) 97 | return 98 | } 99 | 100 | // URL provides a valid, parsed url.URL. 101 | func (p *parserMixin) URL() (target **url.URL) { 102 | target = new(*url.URL) 103 | p.URLVar(target) 104 | return 105 | } 106 | 107 | // StringMap provides key=value parsing into a map. 108 | func (p *parserMixin) StringMapVar(target *map[string]string) { 109 | p.SetValue(newStringMapValue(target)) 110 | } 111 | 112 | // Float sets the parser to a float64 parser. 113 | func (p *parserMixin) Float() (target *float64) { 114 | return p.Float64() 115 | } 116 | 117 | // Float sets the parser to a float64 parser. 118 | func (p *parserMixin) FloatVar(target *float64) { 119 | p.Float64Var(target) 120 | } 121 | 122 | // Duration sets the parser to a time.Duration parser. 123 | func (p *parserMixin) DurationVar(target *time.Duration) { 124 | p.SetValue(newDurationValue(target)) 125 | } 126 | 127 | // BytesVar parses numeric byte units. eg. 1.5KB 128 | func (p *parserMixin) BytesVar(target *units.Base2Bytes) { 129 | p.SetValue(newBytesValue(target)) 130 | } 131 | 132 | // IP sets the parser to a net.IP parser. 133 | func (p *parserMixin) IPVar(target *net.IP) { 134 | p.SetValue(newIPValue(target)) 135 | } 136 | 137 | // ExistingFile sets the parser to one that requires and returns an existing file. 138 | func (p *parserMixin) ExistingFileVar(target *string) { 139 | p.SetValue(newExistingFileValue(target)) 140 | } 141 | 142 | // ExistingDir sets the parser to one that requires and returns an existing directory. 143 | func (p *parserMixin) ExistingDirVar(target *string) { 144 | p.SetValue(newExistingDirValue(target)) 145 | } 146 | 147 | // ExistingDir sets the parser to one that requires and returns an existing directory. 148 | func (p *parserMixin) ExistingFileOrDirVar(target *string) { 149 | p.SetValue(newExistingFileOrDirValue(target)) 150 | } 151 | 152 | // FileVar opens an existing file. 153 | func (p *parserMixin) FileVar(target **os.File) { 154 | p.SetValue(newFileValue(target, os.O_RDONLY, 0)) 155 | } 156 | 157 | // OpenFileVar calls os.OpenFile(flag, perm) 158 | func (p *parserMixin) OpenFileVar(target **os.File, flag int, perm os.FileMode) { 159 | p.SetValue(newFileValue(target, flag, perm)) 160 | } 161 | 162 | // URL provides a valid, parsed url.URL. 163 | func (p *parserMixin) URLVar(target **url.URL) { 164 | p.SetValue(newURLValue(target)) 165 | } 166 | 167 | // URLList provides a parsed list of url.URL values. 168 | func (p *parserMixin) URLList() (target *[]*url.URL) { 169 | target = new([]*url.URL) 170 | p.URLListVar(target) 171 | return 172 | } 173 | 174 | // URLListVar provides a parsed list of url.URL values. 175 | func (p *parserMixin) URLListVar(target *[]*url.URL) { 176 | p.SetValue(newURLListValue(target)) 177 | } 178 | 179 | // Enum allows a value from a set of options. 180 | func (p *parserMixin) Enum(options ...string) (target *string) { 181 | target = new(string) 182 | p.EnumVar(target, options...) 183 | return 184 | } 185 | 186 | // EnumVar allows a value from a set of options. 187 | func (p *parserMixin) EnumVar(target *string, options ...string) { 188 | p.SetValue(newEnumFlag(target, options...)) 189 | } 190 | 191 | // Enums allows a set of values from a set of options. 192 | func (p *parserMixin) Enums(options ...string) (target *[]string) { 193 | target = new([]string) 194 | p.EnumsVar(target, options...) 195 | return 196 | } 197 | 198 | // EnumVar allows a value from a set of options. 199 | func (p *parserMixin) EnumsVar(target *[]string, options ...string) { 200 | p.SetValue(newEnumsFlag(target, options...)) 201 | } 202 | 203 | // A Counter increments a number each time it is encountered. 204 | func (p *parserMixin) Counter() (target *int) { 205 | target = new(int) 206 | p.CounterVar(target) 207 | return 208 | } 209 | 210 | func (p *parserMixin) CounterVar(target *int) { 211 | p.SetValue(newCounterValue(target)) 212 | } 213 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/templates.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | // Default usage template. 4 | var DefaultUsageTemplate = `{{define "FormatCommand"}}\ 5 | {{if .FlagSummary}} {{.FlagSummary}}{{end}}\ 6 | {{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\ 7 | {{end}}\ 8 | 9 | {{define "FormatCommands"}}\ 10 | {{range .FlattenedCommands}}\ 11 | {{if not .Hidden}}\ 12 | {{.FullCommand}}{{if .Default}}*{{end}}{{template "FormatCommand" .}} 13 | {{.Help|Wrap 4}} 14 | {{end}}\ 15 | {{end}}\ 16 | {{end}}\ 17 | 18 | {{define "FormatUsage"}}\ 19 | {{template "FormatCommand" .}}{{if .Commands}} [ ...]{{end}} 20 | {{if .Help}} 21 | {{.Help|Wrap 0}}\ 22 | {{end}}\ 23 | 24 | {{end}}\ 25 | 26 | {{if .Context.SelectedCommand}}\ 27 | usage: {{.App.Name}} {{.Context.SelectedCommand}}{{template "FormatUsage" .Context.SelectedCommand}} 28 | {{else}}\ 29 | usage: {{.App.Name}}{{template "FormatUsage" .App}} 30 | {{end}}\ 31 | {{if .Context.Flags}}\ 32 | Flags: 33 | {{.Context.Flags|FlagsToTwoColumns|FormatTwoColumns}} 34 | {{end}}\ 35 | {{if .Context.Args}}\ 36 | Args: 37 | {{.Context.Args|ArgsToTwoColumns|FormatTwoColumns}} 38 | {{end}}\ 39 | {{if .Context.SelectedCommand}}\ 40 | {{if len .Context.SelectedCommand.Commands}}\ 41 | Subcommands: 42 | {{template "FormatCommands" .Context.SelectedCommand}} 43 | {{end}}\ 44 | {{else if .App.Commands}}\ 45 | Commands: 46 | {{template "FormatCommands" .App}} 47 | {{end}}\ 48 | ` 49 | 50 | // Usage template where command's optional flags are listed separately 51 | var SeparateOptionalFlagsUsageTemplate = `{{define "FormatCommand"}}\ 52 | {{if .FlagSummary}} {{.FlagSummary}}{{end}}\ 53 | {{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\ 54 | {{end}}\ 55 | 56 | {{define "FormatCommands"}}\ 57 | {{range .FlattenedCommands}}\ 58 | {{if not .Hidden}}\ 59 | {{.FullCommand}}{{if .Default}}*{{end}}{{template "FormatCommand" .}} 60 | {{.Help|Wrap 4}} 61 | {{end}}\ 62 | {{end}}\ 63 | {{end}}\ 64 | 65 | {{define "FormatUsage"}}\ 66 | {{template "FormatCommand" .}}{{if .Commands}} [ ...]{{end}} 67 | {{if .Help}} 68 | {{.Help|Wrap 0}}\ 69 | {{end}}\ 70 | 71 | {{end}}\ 72 | {{if .Context.SelectedCommand}}\ 73 | usage: {{.App.Name}} {{.Context.SelectedCommand}}{{template "FormatUsage" .Context.SelectedCommand}} 74 | {{else}}\ 75 | usage: {{.App.Name}}{{template "FormatUsage" .App}} 76 | {{end}}\ 77 | 78 | {{if .Context.Flags|RequiredFlags}}\ 79 | Required flags: 80 | {{.Context.Flags|RequiredFlags|FlagsToTwoColumns|FormatTwoColumns}} 81 | {{end}}\ 82 | {{if .Context.Flags|OptionalFlags}}\ 83 | Optional flags: 84 | {{.Context.Flags|OptionalFlags|FlagsToTwoColumns|FormatTwoColumns}} 85 | {{end}}\ 86 | {{if .Context.Args}}\ 87 | Args: 88 | {{.Context.Args|ArgsToTwoColumns|FormatTwoColumns}} 89 | {{end}}\ 90 | {{if .Context.SelectedCommand}}\ 91 | Subcommands: 92 | {{if .Context.SelectedCommand.Commands}}\ 93 | {{template "FormatCommands" .Context.SelectedCommand}} 94 | {{end}}\ 95 | {{else if .App.Commands}}\ 96 | Commands: 97 | {{template "FormatCommands" .App}} 98 | {{end}}\ 99 | ` 100 | 101 | // Usage template with compactly formatted commands. 102 | var CompactUsageTemplate = `{{define "FormatCommand"}}\ 103 | {{if .FlagSummary}} {{.FlagSummary}}{{end}}\ 104 | {{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\ 105 | {{end}}\ 106 | 107 | {{define "FormatCommandList"}}\ 108 | {{range .}}\ 109 | {{if not .Hidden}}\ 110 | {{.Depth|Indent}}{{.Name}}{{if .Default}}*{{end}}{{template "FormatCommand" .}} 111 | {{end}}\ 112 | {{template "FormatCommandList" .Commands}}\ 113 | {{end}}\ 114 | {{end}}\ 115 | 116 | {{define "FormatUsage"}}\ 117 | {{template "FormatCommand" .}}{{if .Commands}} [ ...]{{end}} 118 | {{if .Help}} 119 | {{.Help|Wrap 0}}\ 120 | {{end}}\ 121 | 122 | {{end}}\ 123 | 124 | {{if .Context.SelectedCommand}}\ 125 | usage: {{.App.Name}} {{.Context.SelectedCommand}}{{template "FormatUsage" .Context.SelectedCommand}} 126 | {{else}}\ 127 | usage: {{.App.Name}}{{template "FormatUsage" .App}} 128 | {{end}}\ 129 | {{if .Context.Flags}}\ 130 | Flags: 131 | {{.Context.Flags|FlagsToTwoColumns|FormatTwoColumns}} 132 | {{end}}\ 133 | {{if .Context.Args}}\ 134 | Args: 135 | {{.Context.Args|ArgsToTwoColumns|FormatTwoColumns}} 136 | {{end}}\ 137 | {{if .Context.SelectedCommand}}\ 138 | {{if .Context.SelectedCommand.Commands}}\ 139 | Commands: 140 | {{.Context.SelectedCommand}} 141 | {{template "FormatCommandList" .Context.SelectedCommand.Commands}} 142 | {{end}}\ 143 | {{else if .App.Commands}}\ 144 | Commands: 145 | {{template "FormatCommandList" .App.Commands}} 146 | {{end}}\ 147 | ` 148 | 149 | var ManPageTemplate = `{{define "FormatFlags"}}\ 150 | {{range .Flags}}\ 151 | {{if not .Hidden}}\ 152 | .TP 153 | \fB{{if .Short}}-{{.Short|Char}}, {{end}}--{{.Name}}{{if not .IsBoolFlag}}={{.FormatPlaceHolder}}{{end}}\\fR 154 | {{.Help}} 155 | {{end}}\ 156 | {{end}}\ 157 | {{end}}\ 158 | 159 | {{define "FormatCommand"}}\ 160 | {{if .FlagSummary}} {{.FlagSummary}}{{end}}\ 161 | {{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}{{if .Default}}*{{end}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\ 162 | {{end}}\ 163 | 164 | {{define "FormatCommands"}}\ 165 | {{range .FlattenedCommands}}\ 166 | {{if not .Hidden}}\ 167 | .SS 168 | \fB{{.FullCommand}}{{template "FormatCommand" .}}\\fR 169 | .PP 170 | {{.Help}} 171 | {{template "FormatFlags" .}}\ 172 | {{end}}\ 173 | {{end}}\ 174 | {{end}}\ 175 | 176 | {{define "FormatUsage"}}\ 177 | {{template "FormatCommand" .}}{{if .Commands}} [ ...]{{end}}\\fR 178 | {{end}}\ 179 | 180 | .TH {{.App.Name}} 1 {{.App.Version}} "{{.App.Author}}" 181 | .SH "NAME" 182 | {{.App.Name}} 183 | .SH "SYNOPSIS" 184 | .TP 185 | \fB{{.App.Name}}{{template "FormatUsage" .App}} 186 | .SH "DESCRIPTION" 187 | {{.App.Help}} 188 | .SH "OPTIONS" 189 | {{template "FormatFlags" .App}}\ 190 | {{if .App.Commands}}\ 191 | .SH "COMMANDS" 192 | {{template "FormatCommands" .App}}\ 193 | {{end}}\ 194 | ` 195 | 196 | // Default usage template. 197 | var LongHelpTemplate = `{{define "FormatCommand"}}\ 198 | {{if .FlagSummary}} {{.FlagSummary}}{{end}}\ 199 | {{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\ 200 | {{end}}\ 201 | 202 | {{define "FormatCommands"}}\ 203 | {{range .FlattenedCommands}}\ 204 | {{if not .Hidden}}\ 205 | {{.FullCommand}}{{template "FormatCommand" .}} 206 | {{.Help|Wrap 4}} 207 | {{with .Flags|FlagsToTwoColumns}}{{FormatTwoColumnsWithIndent . 4 2}}{{end}} 208 | {{end}}\ 209 | {{end}}\ 210 | {{end}}\ 211 | 212 | {{define "FormatUsage"}}\ 213 | {{template "FormatCommand" .}}{{if .Commands}} [ ...]{{end}} 214 | {{if .Help}} 215 | {{.Help|Wrap 0}}\ 216 | {{end}}\ 217 | 218 | {{end}}\ 219 | 220 | usage: {{.App.Name}}{{template "FormatUsage" .App}} 221 | {{if .Context.Flags}}\ 222 | Flags: 223 | {{.Context.Flags|FlagsToTwoColumns|FormatTwoColumns}} 224 | {{end}}\ 225 | {{if .Context.Args}}\ 226 | Args: 227 | {{.Context.Args|ArgsToTwoColumns|FormatTwoColumns}} 228 | {{end}}\ 229 | {{if .App.Commands}}\ 230 | Commands: 231 | {{template "FormatCommands" .App}} 232 | {{end}}\ 233 | ` 234 | 235 | var BashCompletionTemplate = ` 236 | _{{.App.Name}}_bash_autocomplete() { 237 | local cur prev opts base 238 | COMPREPLY=() 239 | cur="${COMP_WORDS[COMP_CWORD]}" 240 | opts=$( ${COMP_WORDS[0]} --completion-bash ${COMP_WORDS[@]:1:$COMP_CWORD} ) 241 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 242 | return 0 243 | } 244 | complete -F _{{.App.Name}}_bash_autocomplete {{.App.Name}} 245 | 246 | ` 247 | 248 | var ZshCompletionTemplate = ` 249 | #compdef {{.App.Name}} 250 | autoload -U compinit && compinit 251 | autoload -U bashcompinit && bashcompinit 252 | 253 | _{{.App.Name}}_bash_autocomplete() { 254 | local cur prev opts base 255 | COMPREPLY=() 256 | cur="${COMP_WORDS[COMP_CWORD]}" 257 | opts=$( ${COMP_WORDS[0]} --completion-bash ${COMP_WORDS[@]:1:$COMP_CWORD} ) 258 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 259 | return 0 260 | } 261 | complete -F _{{.App.Name}}_bash_autocomplete {{.App.Name}} 262 | ` 263 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/usage.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "go/doc" 7 | "io" 8 | "strings" 9 | 10 | "github.com/alecthomas/template" 11 | ) 12 | 13 | var ( 14 | preIndent = " " 15 | ) 16 | 17 | func formatTwoColumns(w io.Writer, indent, padding, width int, rows [][2]string) { 18 | // Find size of first column. 19 | s := 0 20 | for _, row := range rows { 21 | if c := len(row[0]); c > s && c < 30 { 22 | s = c 23 | } 24 | } 25 | 26 | indentStr := strings.Repeat(" ", indent) 27 | offsetStr := strings.Repeat(" ", s+padding) 28 | 29 | for _, row := range rows { 30 | buf := bytes.NewBuffer(nil) 31 | doc.ToText(buf, row[1], "", preIndent, width-s-padding-indent) 32 | lines := strings.Split(strings.TrimRight(buf.String(), "\n"), "\n") 33 | fmt.Fprintf(w, "%s%-*s%*s", indentStr, s, row[0], padding, "") 34 | if len(row[0]) >= 30 { 35 | fmt.Fprintf(w, "\n%s%s", indentStr, offsetStr) 36 | } 37 | fmt.Fprintf(w, "%s\n", lines[0]) 38 | for _, line := range lines[1:] { 39 | fmt.Fprintf(w, "%s%s%s\n", indentStr, offsetStr, line) 40 | } 41 | } 42 | } 43 | 44 | // Usage writes application usage to w. It parses args to determine 45 | // appropriate help context, such as which command to show help for. 46 | func (a *Application) Usage(args []string) { 47 | context, err := a.parseContext(true, args) 48 | a.FatalIfError(err, "") 49 | if err := a.UsageForContextWithTemplate(context, 2, a.usageTemplate); err != nil { 50 | panic(err) 51 | } 52 | } 53 | 54 | func formatAppUsage(app *ApplicationModel) string { 55 | s := []string{app.Name} 56 | if len(app.Flags) > 0 { 57 | s = append(s, app.FlagSummary()) 58 | } 59 | if len(app.Args) > 0 { 60 | s = append(s, app.ArgSummary()) 61 | } 62 | return strings.Join(s, " ") 63 | } 64 | 65 | func formatCmdUsage(app *ApplicationModel, cmd *CmdModel) string { 66 | s := []string{app.Name, cmd.String()} 67 | if len(app.Flags) > 0 { 68 | s = append(s, app.FlagSummary()) 69 | } 70 | if len(app.Args) > 0 { 71 | s = append(s, app.ArgSummary()) 72 | } 73 | return strings.Join(s, " ") 74 | } 75 | 76 | func formatFlag(haveShort bool, flag *FlagModel) string { 77 | flagString := "" 78 | if flag.Short != 0 { 79 | flagString += fmt.Sprintf("-%c, --%s", flag.Short, flag.Name) 80 | } else { 81 | if haveShort { 82 | flagString += fmt.Sprintf(" --%s", flag.Name) 83 | } else { 84 | flagString += fmt.Sprintf("--%s", flag.Name) 85 | } 86 | } 87 | if !flag.IsBoolFlag() { 88 | flagString += fmt.Sprintf("=%s", flag.FormatPlaceHolder()) 89 | } 90 | if v, ok := flag.Value.(repeatableFlag); ok && v.IsCumulative() { 91 | flagString += " ..." 92 | } 93 | return flagString 94 | } 95 | 96 | type templateParseContext struct { 97 | SelectedCommand *CmdModel 98 | *FlagGroupModel 99 | *ArgGroupModel 100 | } 101 | 102 | type templateContext struct { 103 | App *ApplicationModel 104 | Width int 105 | Context *templateParseContext 106 | } 107 | 108 | // UsageForContext displays usage information from a ParseContext (obtained from 109 | // Application.ParseContext() or Action(f) callbacks). 110 | func (a *Application) UsageForContext(context *ParseContext) error { 111 | return a.UsageForContextWithTemplate(context, 2, a.usageTemplate) 112 | } 113 | 114 | // UsageForContextWithTemplate is the base usage function. You generally don't need to use this. 115 | func (a *Application) UsageForContextWithTemplate(context *ParseContext, indent int, tmpl string) error { 116 | width := guessWidth(a.usageWriter) 117 | funcs := template.FuncMap{ 118 | "Indent": func(level int) string { 119 | return strings.Repeat(" ", level*indent) 120 | }, 121 | "Wrap": func(indent int, s string) string { 122 | buf := bytes.NewBuffer(nil) 123 | indentText := strings.Repeat(" ", indent) 124 | doc.ToText(buf, s, indentText, " "+indentText, width-indent) 125 | return buf.String() 126 | }, 127 | "FormatFlag": formatFlag, 128 | "FlagsToTwoColumns": func(f []*FlagModel) [][2]string { 129 | rows := [][2]string{} 130 | haveShort := false 131 | for _, flag := range f { 132 | if flag.Short != 0 { 133 | haveShort = true 134 | break 135 | } 136 | } 137 | for _, flag := range f { 138 | if !flag.Hidden { 139 | rows = append(rows, [2]string{formatFlag(haveShort, flag), flag.Help}) 140 | } 141 | } 142 | return rows 143 | }, 144 | "RequiredFlags": func(f []*FlagModel) []*FlagModel { 145 | requiredFlags := []*FlagModel{} 146 | for _, flag := range f { 147 | if flag.Required { 148 | requiredFlags = append(requiredFlags, flag) 149 | } 150 | } 151 | return requiredFlags 152 | }, 153 | "OptionalFlags": func(f []*FlagModel) []*FlagModel { 154 | optionalFlags := []*FlagModel{} 155 | for _, flag := range f { 156 | if !flag.Required { 157 | optionalFlags = append(optionalFlags, flag) 158 | } 159 | } 160 | return optionalFlags 161 | }, 162 | "ArgsToTwoColumns": func(a []*ArgModel) [][2]string { 163 | rows := [][2]string{} 164 | for _, arg := range a { 165 | s := "<" + arg.Name + ">" 166 | if !arg.Required { 167 | s = "[" + s + "]" 168 | } 169 | rows = append(rows, [2]string{s, arg.Help}) 170 | } 171 | return rows 172 | }, 173 | "FormatTwoColumns": func(rows [][2]string) string { 174 | buf := bytes.NewBuffer(nil) 175 | formatTwoColumns(buf, indent, indent, width, rows) 176 | return buf.String() 177 | }, 178 | "FormatTwoColumnsWithIndent": func(rows [][2]string, indent, padding int) string { 179 | buf := bytes.NewBuffer(nil) 180 | formatTwoColumns(buf, indent, padding, width, rows) 181 | return buf.String() 182 | }, 183 | "FormatAppUsage": formatAppUsage, 184 | "FormatCommandUsage": formatCmdUsage, 185 | "IsCumulative": func(value Value) bool { 186 | r, ok := value.(remainderArg) 187 | return ok && r.IsCumulative() 188 | }, 189 | "Char": func(c rune) string { 190 | return string(c) 191 | }, 192 | } 193 | t, err := template.New("usage").Funcs(funcs).Parse(tmpl) 194 | if err != nil { 195 | return err 196 | } 197 | var selectedCommand *CmdModel 198 | if context.SelectedCommand != nil { 199 | selectedCommand = context.SelectedCommand.Model() 200 | } 201 | ctx := templateContext{ 202 | App: a.Model(), 203 | Width: width, 204 | Context: &templateParseContext{ 205 | SelectedCommand: selectedCommand, 206 | FlagGroupModel: context.flags.Model(), 207 | ArgGroupModel: context.arguments.Model(), 208 | }, 209 | } 210 | return t.Execute(a.usageWriter, ctx) 211 | } 212 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/values.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | //go:generate go run ./cmd/genvalues/main.go 4 | 5 | import ( 6 | "fmt" 7 | "net" 8 | "net/url" 9 | "os" 10 | "reflect" 11 | "regexp" 12 | "strings" 13 | "time" 14 | 15 | "github.com/alecthomas/units" 16 | ) 17 | 18 | // NOTE: Most of the base type values were lifted from: 19 | // http://golang.org/src/pkg/flag/flag.go?s=20146:20222 20 | 21 | // Value is the interface to the dynamic value stored in a flag. 22 | // (The default value is represented as a string.) 23 | // 24 | // If a Value has an IsBoolFlag() bool method returning true, the command-line 25 | // parser makes --name equivalent to -name=true rather than using the next 26 | // command-line argument, and adds a --no-name counterpart for negating the 27 | // flag. 28 | type Value interface { 29 | String() string 30 | Set(string) error 31 | } 32 | 33 | // Getter is an interface that allows the contents of a Value to be retrieved. 34 | // It wraps the Value interface, rather than being part of it, because it 35 | // appeared after Go 1 and its compatibility rules. All Value types provided 36 | // by this package satisfy the Getter interface. 37 | type Getter interface { 38 | Value 39 | Get() interface{} 40 | } 41 | 42 | // Optional interface to indicate boolean flags that don't accept a value, and 43 | // implicitly have a --no- negation counterpart. 44 | type boolFlag interface { 45 | Value 46 | IsBoolFlag() bool 47 | } 48 | 49 | // Optional interface for arguments that cumulatively consume all remaining 50 | // input. 51 | type remainderArg interface { 52 | Value 53 | IsCumulative() bool 54 | } 55 | 56 | // Optional interface for flags that can be repeated. 57 | type repeatableFlag interface { 58 | Value 59 | IsCumulative() bool 60 | } 61 | 62 | type accumulator struct { 63 | element func(value interface{}) Value 64 | typ reflect.Type 65 | slice reflect.Value 66 | } 67 | 68 | // Use reflection to accumulate values into a slice. 69 | // 70 | // target := []string{} 71 | // newAccumulator(&target, func (value interface{}) Value { 72 | // return newStringValue(value.(*string)) 73 | // }) 74 | func newAccumulator(slice interface{}, element func(value interface{}) Value) *accumulator { 75 | typ := reflect.TypeOf(slice) 76 | if typ.Kind() != reflect.Ptr || typ.Elem().Kind() != reflect.Slice { 77 | panic("expected a pointer to a slice") 78 | } 79 | return &accumulator{ 80 | element: element, 81 | typ: typ.Elem().Elem(), 82 | slice: reflect.ValueOf(slice), 83 | } 84 | } 85 | 86 | func (a *accumulator) String() string { 87 | out := []string{} 88 | s := a.slice.Elem() 89 | for i := 0; i < s.Len(); i++ { 90 | out = append(out, a.element(s.Index(i).Addr().Interface()).String()) 91 | } 92 | return strings.Join(out, ",") 93 | } 94 | 95 | func (a *accumulator) Set(value string) error { 96 | e := reflect.New(a.typ) 97 | if err := a.element(e.Interface()).Set(value); err != nil { 98 | return err 99 | } 100 | slice := reflect.Append(a.slice.Elem(), e.Elem()) 101 | a.slice.Elem().Set(slice) 102 | return nil 103 | } 104 | 105 | func (a *accumulator) Get() interface{} { 106 | return a.slice.Interface() 107 | } 108 | 109 | func (a *accumulator) IsCumulative() bool { 110 | return true 111 | } 112 | 113 | func (b *boolValue) IsBoolFlag() bool { return true } 114 | 115 | // -- time.Duration Value 116 | type durationValue time.Duration 117 | 118 | func newDurationValue(p *time.Duration) *durationValue { 119 | return (*durationValue)(p) 120 | } 121 | 122 | func (d *durationValue) Set(s string) error { 123 | v, err := time.ParseDuration(s) 124 | *d = durationValue(v) 125 | return err 126 | } 127 | 128 | func (d *durationValue) Get() interface{} { return time.Duration(*d) } 129 | 130 | func (d *durationValue) String() string { return (*time.Duration)(d).String() } 131 | 132 | // -- map[string]string Value 133 | type stringMapValue map[string]string 134 | 135 | func newStringMapValue(p *map[string]string) *stringMapValue { 136 | return (*stringMapValue)(p) 137 | } 138 | 139 | var stringMapRegex = regexp.MustCompile("[:=]") 140 | 141 | func (s *stringMapValue) Set(value string) error { 142 | parts := stringMapRegex.Split(value, 2) 143 | if len(parts) != 2 { 144 | return fmt.Errorf("expected KEY=VALUE got '%s'", value) 145 | } 146 | (*s)[parts[0]] = parts[1] 147 | return nil 148 | } 149 | 150 | func (s *stringMapValue) Get() interface{} { 151 | return (map[string]string)(*s) 152 | } 153 | 154 | func (s *stringMapValue) String() string { 155 | return fmt.Sprintf("%s", map[string]string(*s)) 156 | } 157 | 158 | func (s *stringMapValue) IsCumulative() bool { 159 | return true 160 | } 161 | 162 | // -- net.IP Value 163 | type ipValue net.IP 164 | 165 | func newIPValue(p *net.IP) *ipValue { 166 | return (*ipValue)(p) 167 | } 168 | 169 | func (i *ipValue) Set(value string) error { 170 | if ip := net.ParseIP(value); ip == nil { 171 | return fmt.Errorf("'%s' is not an IP address", value) 172 | } else { 173 | *i = *(*ipValue)(&ip) 174 | return nil 175 | } 176 | } 177 | 178 | func (i *ipValue) Get() interface{} { 179 | return (net.IP)(*i) 180 | } 181 | 182 | func (i *ipValue) String() string { 183 | return (*net.IP)(i).String() 184 | } 185 | 186 | // -- *net.TCPAddr Value 187 | type tcpAddrValue struct { 188 | addr **net.TCPAddr 189 | } 190 | 191 | func newTCPAddrValue(p **net.TCPAddr) *tcpAddrValue { 192 | return &tcpAddrValue{p} 193 | } 194 | 195 | func (i *tcpAddrValue) Set(value string) error { 196 | if addr, err := net.ResolveTCPAddr("tcp", value); err != nil { 197 | return fmt.Errorf("'%s' is not a valid TCP address: %s", value, err) 198 | } else { 199 | *i.addr = addr 200 | return nil 201 | } 202 | } 203 | 204 | func (t *tcpAddrValue) Get() interface{} { 205 | return (*net.TCPAddr)(*t.addr) 206 | } 207 | 208 | func (i *tcpAddrValue) String() string { 209 | return (*i.addr).String() 210 | } 211 | 212 | // -- existingFile Value 213 | 214 | type fileStatValue struct { 215 | path *string 216 | predicate func(os.FileInfo) error 217 | } 218 | 219 | func newFileStatValue(p *string, predicate func(os.FileInfo) error) *fileStatValue { 220 | return &fileStatValue{ 221 | path: p, 222 | predicate: predicate, 223 | } 224 | } 225 | 226 | func (e *fileStatValue) Set(value string) error { 227 | if s, err := os.Stat(value); os.IsNotExist(err) { 228 | return fmt.Errorf("path '%s' does not exist", value) 229 | } else if err != nil { 230 | return err 231 | } else if err := e.predicate(s); err != nil { 232 | return err 233 | } 234 | *e.path = value 235 | return nil 236 | } 237 | 238 | func (f *fileStatValue) Get() interface{} { 239 | return (string)(*f.path) 240 | } 241 | 242 | func (e *fileStatValue) String() string { 243 | return *e.path 244 | } 245 | 246 | // -- os.File value 247 | 248 | type fileValue struct { 249 | f **os.File 250 | flag int 251 | perm os.FileMode 252 | } 253 | 254 | func newFileValue(p **os.File, flag int, perm os.FileMode) *fileValue { 255 | return &fileValue{p, flag, perm} 256 | } 257 | 258 | func (f *fileValue) Set(value string) error { 259 | if fd, err := os.OpenFile(value, f.flag, f.perm); err != nil { 260 | return err 261 | } else { 262 | *f.f = fd 263 | return nil 264 | } 265 | } 266 | 267 | func (f *fileValue) Get() interface{} { 268 | return (*os.File)(*f.f) 269 | } 270 | 271 | func (f *fileValue) String() string { 272 | if *f.f == nil { 273 | return "" 274 | } 275 | return (*f.f).Name() 276 | } 277 | 278 | // -- url.URL Value 279 | type urlValue struct { 280 | u **url.URL 281 | } 282 | 283 | func newURLValue(p **url.URL) *urlValue { 284 | return &urlValue{p} 285 | } 286 | 287 | func (u *urlValue) Set(value string) error { 288 | if url, err := url.Parse(value); err != nil { 289 | return fmt.Errorf("invalid URL: %s", err) 290 | } else { 291 | *u.u = url 292 | return nil 293 | } 294 | } 295 | 296 | func (u *urlValue) Get() interface{} { 297 | return (*url.URL)(*u.u) 298 | } 299 | 300 | func (u *urlValue) String() string { 301 | if *u.u == nil { 302 | return "" 303 | } 304 | return (*u.u).String() 305 | } 306 | 307 | // -- []*url.URL Value 308 | type urlListValue []*url.URL 309 | 310 | func newURLListValue(p *[]*url.URL) *urlListValue { 311 | return (*urlListValue)(p) 312 | } 313 | 314 | func (u *urlListValue) Set(value string) error { 315 | if url, err := url.Parse(value); err != nil { 316 | return fmt.Errorf("invalid URL: %s", err) 317 | } else { 318 | *u = append(*u, url) 319 | return nil 320 | } 321 | } 322 | 323 | func (u *urlListValue) Get() interface{} { 324 | return ([]*url.URL)(*u) 325 | } 326 | 327 | func (u *urlListValue) String() string { 328 | out := []string{} 329 | for _, url := range *u { 330 | out = append(out, url.String()) 331 | } 332 | return strings.Join(out, ",") 333 | } 334 | 335 | // A flag whose value must be in a set of options. 336 | type enumValue struct { 337 | value *string 338 | options []string 339 | } 340 | 341 | func newEnumFlag(target *string, options ...string) *enumValue { 342 | return &enumValue{ 343 | value: target, 344 | options: options, 345 | } 346 | } 347 | 348 | func (a *enumValue) String() string { 349 | return *a.value 350 | } 351 | 352 | func (a *enumValue) Set(value string) error { 353 | for _, v := range a.options { 354 | if v == value { 355 | *a.value = value 356 | return nil 357 | } 358 | } 359 | return fmt.Errorf("enum value must be one of %s, got '%s'", strings.Join(a.options, ","), value) 360 | } 361 | 362 | func (e *enumValue) Get() interface{} { 363 | return (string)(*e.value) 364 | } 365 | 366 | // -- []string Enum Value 367 | type enumsValue struct { 368 | value *[]string 369 | options []string 370 | } 371 | 372 | func newEnumsFlag(target *[]string, options ...string) *enumsValue { 373 | return &enumsValue{ 374 | value: target, 375 | options: options, 376 | } 377 | } 378 | 379 | func (s *enumsValue) Set(value string) error { 380 | for _, v := range s.options { 381 | if v == value { 382 | *s.value = append(*s.value, value) 383 | return nil 384 | } 385 | } 386 | return fmt.Errorf("enum value must be one of %s, got '%s'", strings.Join(s.options, ","), value) 387 | } 388 | 389 | func (e *enumsValue) Get() interface{} { 390 | return ([]string)(*e.value) 391 | } 392 | 393 | func (s *enumsValue) String() string { 394 | return strings.Join(*s.value, ",") 395 | } 396 | 397 | func (s *enumsValue) IsCumulative() bool { 398 | return true 399 | } 400 | 401 | // -- units.Base2Bytes Value 402 | type bytesValue units.Base2Bytes 403 | 404 | func newBytesValue(p *units.Base2Bytes) *bytesValue { 405 | return (*bytesValue)(p) 406 | } 407 | 408 | func (d *bytesValue) Set(s string) error { 409 | v, err := units.ParseBase2Bytes(s) 410 | *d = bytesValue(v) 411 | return err 412 | } 413 | 414 | func (d *bytesValue) Get() interface{} { return units.Base2Bytes(*d) } 415 | 416 | func (d *bytesValue) String() string { return (*units.Base2Bytes)(d).String() } 417 | 418 | func newExistingFileValue(target *string) *fileStatValue { 419 | return newFileStatValue(target, func(s os.FileInfo) error { 420 | if s.IsDir() { 421 | return fmt.Errorf("'%s' is a directory", s.Name()) 422 | } 423 | return nil 424 | }) 425 | } 426 | 427 | func newExistingDirValue(target *string) *fileStatValue { 428 | return newFileStatValue(target, func(s os.FileInfo) error { 429 | if !s.IsDir() { 430 | return fmt.Errorf("'%s' is a file", s.Name()) 431 | } 432 | return nil 433 | }) 434 | } 435 | 436 | func newExistingFileOrDirValue(target *string) *fileStatValue { 437 | return newFileStatValue(target, func(s os.FileInfo) error { return nil }) 438 | } 439 | 440 | type counterValue int 441 | 442 | func newCounterValue(n *int) *counterValue { 443 | return (*counterValue)(n) 444 | } 445 | 446 | func (c *counterValue) Set(s string) error { 447 | *c++ 448 | return nil 449 | } 450 | 451 | func (c *counterValue) Get() interface{} { return (int)(*c) } 452 | func (c *counterValue) IsBoolFlag() bool { return true } 453 | func (c *counterValue) String() string { return fmt.Sprintf("%d", *c) } 454 | func (c *counterValue) IsCumulative() bool { return true } 455 | 456 | func resolveHost(value string) (net.IP, error) { 457 | if ip := net.ParseIP(value); ip != nil { 458 | return ip, nil 459 | } else { 460 | if addr, err := net.ResolveIPAddr("ip", value); err != nil { 461 | return nil, err 462 | } else { 463 | return addr.IP, nil 464 | } 465 | } 466 | } 467 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/values.json: -------------------------------------------------------------------------------- 1 | [ 2 | {"type": "bool", "parser": "strconv.ParseBool(s)"}, 3 | {"type": "string", "parser": "s, error(nil)", "format": "string(*f.v)", "plural": "Strings"}, 4 | {"type": "uint", "parser": "strconv.ParseUint(s, 0, 64)", "plural": "Uints"}, 5 | {"type": "uint8", "parser": "strconv.ParseUint(s, 0, 8)"}, 6 | {"type": "uint16", "parser": "strconv.ParseUint(s, 0, 16)"}, 7 | {"type": "uint32", "parser": "strconv.ParseUint(s, 0, 32)"}, 8 | {"type": "uint64", "parser": "strconv.ParseUint(s, 0, 64)"}, 9 | {"type": "int", "parser": "strconv.ParseFloat(s, 64)", "plural": "Ints"}, 10 | {"type": "int8", "parser": "strconv.ParseInt(s, 0, 8)"}, 11 | {"type": "int16", "parser": "strconv.ParseInt(s, 0, 16)"}, 12 | {"type": "int32", "parser": "strconv.ParseInt(s, 0, 32)"}, 13 | {"type": "int64", "parser": "strconv.ParseInt(s, 0, 64)"}, 14 | {"type": "float64", "parser": "strconv.ParseFloat(s, 64)"}, 15 | {"type": "float32", "parser": "strconv.ParseFloat(s, 32)"}, 16 | {"name": "Duration", "type": "time.Duration", "no_value_parser": true}, 17 | {"name": "IP", "type": "net.IP", "no_value_parser": true}, 18 | {"name": "TCPAddr", "Type": "*net.TCPAddr", "plural": "TCPList", "no_value_parser": true}, 19 | {"name": "ExistingFile", "Type": "string", "plural": "ExistingFiles", "no_value_parser": true}, 20 | {"name": "ExistingDir", "Type": "string", "plural": "ExistingDirs", "no_value_parser": true}, 21 | {"name": "ExistingFileOrDir", "Type": "string", "plural": "ExistingFilesOrDirs", "no_value_parser": true}, 22 | {"name": "Regexp", "Type": "*regexp.Regexp", "parser": "regexp.Compile(s)"}, 23 | {"name": "ResolvedIP", "Type": "net.IP", "parser": "resolveHost(s)", "help": "Resolve a hostname or IP to an IP."}, 24 | {"name": "HexBytes", "Type": "[]byte", "parser": "hex.DecodeString(s)", "help": "Bytes as a hex string."} 25 | ] 26 | -------------------------------------------------------------------------------- /version.go: -------------------------------------------------------------------------------- 1 | // Update in CHNAGELOG.md 2 | 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "runtime" 8 | ) 9 | 10 | const PXVER = "1.1.2" 11 | 12 | var VERSION = fmt.Sprintf("Proxylocal version %s, %s\nHomepage: %s", 13 | PXVER, runtime.Version(), "https://github.com/codeskyblue/proxylocal") 14 | --------------------------------------------------------------------------------