├── .gitignore ├── Godeps ├── Godeps.json └── Readme ├── README.md ├── client ├── client.go ├── config.go ├── connection.go └── pool.go ├── common ├── request.go ├── response.go ├── rules.go └── utils.go ├── server ├── config.go ├── connection.go ├── pool.go └── server.go ├── test_api └── test_api.go ├── vendor ├── github.com │ ├── 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 │ │ │ ├── mask.go │ │ │ ├── server.go │ │ │ └── util.go │ ├── nu7hatch │ │ └── gouuid │ │ │ ├── .gitignore │ │ │ ├── COPYING │ │ │ ├── README.md │ │ │ └── uuid.go │ └── root-gg │ │ └── utils │ │ ├── .gitignore │ │ ├── README.md │ │ ├── bytes.go │ │ ├── caller.go │ │ ├── dumper.go │ │ ├── json.go │ │ ├── md5sum.go │ │ ├── net.go │ │ ├── reflect.go │ │ ├── strings.go │ │ ├── time.go │ │ └── timer.go └── gopkg.in │ └── yaml.v2 │ ├── .travis.yml │ ├── LICENSE │ ├── LICENSE.libyaml │ ├── README.md │ ├── apic.go │ ├── decode.go │ ├── emitterc.go │ ├── encode.go │ ├── parserc.go │ ├── readerc.go │ ├── resolve.go │ ├── scannerc.go │ ├── sorter.go │ ├── writerc.go │ ├── yaml.go │ ├── yamlh.go │ └── yamlprivateh.go ├── wsp_client ├── wsp_client.cfg └── wsp_client.go └── wsp_server ├── wsp_server.cfg └── wsp_server.go /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | 3 | ssl/ 4 | *.key 5 | *.crt 6 | 7 | wsp_server/wsp_server 8 | wsp_client/wsp_client 9 | test_api/test_api 10 | 11 | # IntelliJ IDEA 12 | .idea 13 | -------------------------------------------------------------------------------- /Godeps/Godeps.json: -------------------------------------------------------------------------------- 1 | { 2 | "ImportPath": "github.com/root-gg/wsp", 3 | "GoVersion": "go1.6", 4 | "GodepVersion": "v75", 5 | "Deps": [ 6 | { 7 | "ImportPath": "github.com/gorilla/websocket", 8 | "Comment": "v1.0.0-39-ge8f0f8a", 9 | "Rev": "e8f0f8aaa98dfb6586cbdf2978d511e3199a960a" 10 | }, 11 | { 12 | "ImportPath": "github.com/nu7hatch/gouuid", 13 | "Rev": "179d4d0c4d8d407a32af483c2354df1d2c91e6c3" 14 | }, 15 | { 16 | "ImportPath": "github.com/root-gg/utils", 17 | "Rev": "38f45ede2ce220d9c08734edd8a13107022cc20d" 18 | }, 19 | { 20 | "ImportPath": "gopkg.in/yaml.v2", 21 | "Rev": "a5b47d31c556af34a302ce5d659e6fea44d90de0" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | WS PROXY 2 | ======== 3 | 4 | This is a reverse HTTP proxy over websockets. 5 | The aim is to securely make call to internal APIs from outside. 6 | 7 | How does it works 8 | ----------------- 9 | 10 | a WSP client runs in the internal network ( alongside the APIs ) 11 | and connects to a remote WSP server with HTTP websockets. 12 | 13 | One issue HTTP requests to the WSP server with an extra 14 | HTTP header 'X-PROXY-DESTINATION: "http://api.internal/resource"' 15 | to the /request endpoint. 16 | 17 | The WSP Server then forward the request to the WSP Client over the 18 | one of the offered websockets. The WSP Client receive and execute 19 | locally an HTTP request to the URL provided in X-PROXY-DESTINATION 20 | and forwards the HTTP response back to the WSP server which in turn 21 | forwards the response back to the client. Please note that no 22 | buffering of any sort occurs. 23 | 24 | If several WSP clients connect to a WSP server, requests will be spread 25 | in a random way to all the WSP clients. 26 | 27 | ![wsp schema](https://cloud.githubusercontent.com/assets/6413246/24397653/3f2e4b30-13a7-11e7-820b-cde6e784382f.png) 28 | 29 | 30 | Get code 31 | -------- 32 | ``` 33 | go get github.com/root-gg/wsp 34 | ``` 35 | 36 | 37 | WSP server configuration 38 | ------------------------ 39 | 40 | ``` 41 | # wsp_server.cfg 42 | --- 43 | host : 127.0.0.1 # Address to bind the HTTP server 44 | port : 8080 # Port to bind the HTTP server 45 | timeout : 1000 # Time to wait before acquiring a WS connection to forward the request (milliseconds) 46 | idletimeout : 60000 # Time to wait before closing idle connection when there is enough idle connections (milliseconds) 47 | #blacklist : # Forbidden destination ( deny nothing if empty ) 48 | # - method : ".*" # Applied in order before whitelist 49 | # url : "^http(s)?://google.*" # None must match 50 | # headers : # Optinal header check 51 | # X-CUSTOM-HEADER : "^value$" # 52 | #whitelist : # Allowed destinations ( allow all if empty ) 53 | # - method : "^GET$" # Applied in order after blacklist 54 | # url : "^http(s)?://.*$" # One must match 55 | # headers : # Optinal header check 56 | # X-CUSTOM-HEADER : "^value$" # 57 | # secretkey : ThisIsASecret # secret key that must be set in clients configuration 58 | ``` 59 | 60 | ``` 61 | $ cd wsp_server && go build 62 | $ ./wsp_server -config wsp_server.cfg 63 | { 64 | "Host": "127.0.0.1", 65 | "Port": 8080 66 | } 67 | 2016/11/22 15:31:39 Registering new connection from 7e2d8782-f893-4ff3-7e9d-299b4c0a518a 68 | 2016/11/22 15:31:40 Registering new connection from 7e2d8782-f893-4ff3-7e9d-299b4c0a518a 69 | 2016/11/22 15:31:40 Registering new connection from 7e2d8782-f893-4ff3-7e9d-299b4c0a518a 70 | 2016/11/22 15:31:40 Registering new connection from 7e2d8782-f893-4ff3-7e9d-299b4c0a518a 71 | 2016/11/22 15:31:40 Registering new connection from 7e2d8782-f893-4ff3-7e9d-299b4c0a518a 72 | 2016/11/22 15:31:40 Registering new connection from 7e2d8782-f893-4ff3-7e9d-299b4c0a518a 73 | 2016/11/22 15:31:40 Registering new connection from 7e2d8782-f893-4ff3-7e9d-299b4c0a518a 74 | 2016/11/22 15:31:40 Registering new connection from 7e2d8782-f893-4ff3-7e9d-299b4c0a518a 75 | 2016/11/22 15:31:40 Registering new connection from 7e2d8782-f893-4ff3-7e9d-299b4c0a518a 76 | 2016/11/22 15:31:40 Registering new connection from 7e2d8782-f893-4ff3-7e9d-299b4c0a518a 77 | 2016/11/22 15:33:34 GET map[User-Agent:[curl/7.26.0] Accept:[*/*] X-Proxy-Destination:[https://google.fr]] 78 | 2016/11/22 15:33:34 proxy request to 7e2d8782-f893-4ff3-7e9d-299b4c0a518a 79 | ``` 80 | 81 | For now TLS setup should be implemented using an HTTP reverse proxy 82 | like NGinx or Apache... 83 | 84 | WSP proxy configuration 85 | ----------------------- 86 | 87 | ``` 88 | # wsp_client.cfg 89 | --- 90 | targets : # Endpoints to connect to 91 | - ws://127.0.0.1:8080/register # 92 | poolidlesize : 10 # Default number of concurrent open (TCP) connections to keep idle per WSP server 93 | poolmaxsize : 100 # Maximum number of concurrent open (TCP) connections per WSP server 94 | #blacklist : # Forbidden destination ( deny nothing if empty ) 95 | # - method : ".*" # Applied in order before whitelist 96 | # url : ".*forbidden.*" # None must match 97 | # headers : # Optinal header check 98 | # X-CUSTOM-HEADER : "^value$" # 99 | #whitelist : # Allowed destinations ( allow all if empty ) 100 | # - method : "^GET$" # Applied in order after blacklist 101 | # url : "http(s)?://.*$" # One must match 102 | # headers : # Optinal header check 103 | # X-CUSTOM-HEADER : "^value$" # 104 | # secretkey : ThisIsASecret # secret key that must match the value set in servers configuration 105 | ``` 106 | 107 | - poolMinSize is the default number of opened TCP/HTTP/WS connections 108 | to open per WSP server. If there is a burst of simpultaneous requests 109 | the number of open connection will rise and then decrease back to this 110 | number. 111 | - poolMinIdleSize is the number of connection to keep idle, meaning 112 | that if there is more than this number of simultaneous requests the 113 | WSP client will try to open more connections to keep idle connection. 114 | - poolMaxSize is the maximum number of simultaneous connection that 115 | the proxy will ever initiate per WSP server. 116 | 117 | ``` 118 | $ cd wsp_client && go build 119 | $ ./wsp_client -config wsp_client.cfg 120 | { 121 | "ID": "7e2d8782-f893-4ff3-7e9d-299b4c0a518a", 122 | "Targets": [ 123 | "ws://127.0.0.1:8080/register" 124 | ], 125 | "PoolMinSize": 10, 126 | "PoolMinIdleSize": 5, 127 | "PoolMaxSize": 100 128 | } 129 | 2016/11/22 15:31:39 Connecting to ws://127.0.0.1:8080/register 130 | 2016/11/22 15:31:40 Connecting to ws://127.0.0.1:8080/register 131 | 2016/11/22 15:31:40 Connecting to ws://127.0.0.1:8080/register 132 | 2016/11/22 15:31:40 Connecting to ws://127.0.0.1:8080/register 133 | 2016/11/22 15:31:40 Connecting to ws://127.0.0.1:8080/register 134 | 2016/11/22 15:31:40 Connecting to ws://127.0.0.1:8080/register 135 | 2016/11/22 15:31:40 Connecting to ws://127.0.0.1:8080/register 136 | 2016/11/22 15:31:40 Connecting to ws://127.0.0.1:8080/register 137 | 2016/11/22 15:31:40 Connecting to ws://127.0.0.1:8080/register 138 | 2016/11/22 15:31:40 Connecting to ws://127.0.0.1:8080/register 139 | 2016/11/22 15:33:34 got request : {"Method":"GET","URL":"https://google.fr","Header":{"Accept":["*/*"],"User-Agent":["curl/7.26.0"],"X-Proxy-Destination":["https://google.fr"]},"ContentLength":0} 140 | ``` 141 | 142 | Client 143 | ------ 144 | 145 | ``` 146 | $ curl -H 'X-PROXY-DESTINATION: https://google.fr' http://127.0.0.1:8080/request 147 | 0 { 120 | for _, rule := range connection.pool.client.Config.Blacklist { 121 | if rule.Match(req) { 122 | // Discard request body 123 | err = connection.discard() 124 | if err != nil { 125 | break 126 | } 127 | err = connection.error("Destination is forbidden") 128 | if err != nil { 129 | break 130 | } 131 | continue 132 | } 133 | } 134 | } 135 | 136 | // Apply whitelist 137 | if len(connection.pool.client.Config.Whitelist) > 0 { 138 | allowed := false 139 | for _, rule := range connection.pool.client.Config.Whitelist { 140 | if rule.Match(req) { 141 | allowed = true 142 | break 143 | } 144 | } 145 | if !allowed { 146 | // Discard request body 147 | err = connection.discard() 148 | if err != nil { 149 | break 150 | } 151 | err = connection.error("Destination is not allowed\n") 152 | if err != nil { 153 | break 154 | } 155 | continue 156 | } 157 | } 158 | 159 | // Pipe request body 160 | _, bodyReader, err := connection.ws.NextReader() 161 | if err != nil { 162 | log.Printf("Unable to get response body reader : %v", err) 163 | break 164 | } 165 | req.Body = ioutil.NopCloser(bodyReader) 166 | 167 | // Execute request 168 | resp, err := connection.pool.client.client.Do(req) 169 | if err != nil { 170 | err = connection.error(fmt.Sprintf("Unable to execute request : %v\n", err)) 171 | if err != nil { 172 | break 173 | } 174 | continue 175 | } 176 | 177 | // Serialize response 178 | jsonResponse, err := json.Marshal(common.SerializeHTTPResponse(resp)) 179 | if err != nil { 180 | err = connection.error(fmt.Sprintf("Unable to serialize response : %v\n", err)) 181 | if err != nil { 182 | break 183 | } 184 | continue 185 | } 186 | 187 | // Write response 188 | err = connection.ws.WriteMessage(websocket.TextMessage, jsonResponse) 189 | if err != nil { 190 | log.Printf("Unable to write response : %v", err) 191 | break 192 | } 193 | 194 | // Pipe response body 195 | bodyWriter, err := connection.ws.NextWriter(websocket.BinaryMessage) 196 | if err != nil { 197 | log.Printf("Unable to get response body writer : %v", err) 198 | break 199 | } 200 | _, err = io.Copy(bodyWriter, resp.Body) 201 | if err != nil { 202 | log.Printf("Unable to get pipe response body : %v", err) 203 | break 204 | } 205 | bodyWriter.Close() 206 | } 207 | } 208 | 209 | func (connection *Connection) error(msg string) (err error) { 210 | resp := common.NewHTTPResponse() 211 | resp.StatusCode = 527 212 | 213 | log.Println(msg) 214 | 215 | resp.ContentLength = int64(len(msg)) 216 | 217 | // Serialize response 218 | jsonResponse, err := json.Marshal(resp) 219 | if err != nil { 220 | log.Printf("Unable to serialize response : %v", err) 221 | return 222 | } 223 | 224 | // Write response 225 | err = connection.ws.WriteMessage(websocket.TextMessage, jsonResponse) 226 | if err != nil { 227 | log.Printf("Unable to write response : %v", err) 228 | return 229 | } 230 | 231 | // Write response body 232 | err = connection.ws.WriteMessage(websocket.BinaryMessage, []byte(msg)) 233 | if err != nil { 234 | log.Printf("Unable to write response body : %v", err) 235 | return 236 | } 237 | 238 | return 239 | } 240 | 241 | // Discard request body 242 | func (connection *Connection) discard() (err error) { 243 | mt, _, err := connection.ws.NextReader() 244 | if err != nil { 245 | return nil 246 | } 247 | if mt != websocket.BinaryMessage { 248 | return errors.New("Invalid body message type") 249 | } 250 | return 251 | } 252 | 253 | // Close close the ws/tcp connection and remove it from the pool 254 | func (connection *Connection) Close() { 255 | connection.pool.lock.Lock() 256 | defer connection.pool.lock.Unlock() 257 | 258 | connection.pool.remove(connection) 259 | connection.ws.Close() 260 | } 261 | -------------------------------------------------------------------------------- /client/pool.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | // Pool manage a pool of connection to a remote Server 11 | type Pool struct { 12 | client *Client 13 | target string 14 | secretKey string 15 | 16 | connections []*Connection 17 | lock sync.RWMutex 18 | 19 | done chan struct{} 20 | } 21 | 22 | // NewPool creates a new Pool 23 | func NewPool(client *Client, target string, secretKey string) (pool *Pool) { 24 | pool = new(Pool) 25 | pool.client = client 26 | pool.target = target 27 | pool.connections = make([]*Connection, 0) 28 | pool.secretKey = secretKey 29 | pool.done = make(chan struct{}) 30 | return 31 | } 32 | 33 | // Start connect to the remote Server 34 | func (pool *Pool) Start() { 35 | pool.connector() 36 | go func() { 37 | ticker := time.Tick(time.Second) 38 | for { 39 | select { 40 | case <-pool.done: 41 | break 42 | case <-ticker: 43 | pool.connector() 44 | } 45 | } 46 | }() 47 | } 48 | 49 | // The garbage collector 50 | func (pool *Pool) connector() { 51 | pool.lock.Lock() 52 | defer pool.lock.Unlock() 53 | 54 | poolSize := pool.Size() 55 | 56 | //log.Printf("%s pool size : %v", pool.target, poolSize) 57 | 58 | // Create enough connection to fill the pool 59 | toCreate := pool.client.Config.PoolIdleSize - poolSize.idle 60 | 61 | // Create only one connection if the pool is empty 62 | if poolSize.total == 0 { 63 | toCreate = 1 64 | } 65 | 66 | // Ensure to open at most PoolMaxSize connections 67 | if poolSize.total+toCreate > pool.client.Config.PoolMaxSize { 68 | toCreate = pool.client.Config.PoolMaxSize - poolSize.total 69 | } 70 | 71 | //log.Printf("%v",toCreate) 72 | 73 | // Try to reach ideal pool size 74 | for i := 0; i < toCreate; i++ { 75 | conn := NewConnection(pool) 76 | pool.connections = append(pool.connections, conn) 77 | 78 | go func() { 79 | err := conn.Connect() 80 | if err != nil { 81 | log.Printf("Unable to connect to %s : %s", pool.target, err) 82 | 83 | pool.lock.Lock() 84 | defer pool.lock.Unlock() 85 | pool.remove(conn) 86 | } 87 | }() 88 | } 89 | } 90 | 91 | // Add a connection to the pool 92 | func (pool *Pool) add(conn *Connection) { 93 | pool.connections = append(pool.connections, conn) 94 | } 95 | 96 | // Remove a connection from the pool 97 | func (pool *Pool) remove(conn *Connection) { 98 | // This trick uses the fact that a slice shares the same backing array and capacity as the original, 99 | // so the storage is reused for the filtered slice. Of course, the original contents are modified. 100 | 101 | var filtered []*Connection // == nil 102 | for _, c := range pool.connections { 103 | if conn != c { 104 | filtered = append(filtered, c) 105 | } 106 | } 107 | pool.connections = filtered 108 | } 109 | 110 | // Shutdown close all connection in the pool 111 | func (pool *Pool) Shutdown() { 112 | close(pool.done) 113 | for _, conn := range pool.connections { 114 | conn.Close() 115 | } 116 | } 117 | 118 | // PoolSize represent the number of open connections per status 119 | type PoolSize struct { 120 | connecting int 121 | idle int 122 | running int 123 | total int 124 | } 125 | 126 | func (poolSize *PoolSize) String() string { 127 | return fmt.Sprintf("Connecting %d, idle %d, running %d, total %d", poolSize.connecting, poolSize.idle, poolSize.running, poolSize.total) 128 | } 129 | 130 | // Size return the current state of the pool 131 | func (pool *Pool) Size() (poolSize *PoolSize) { 132 | poolSize = new(PoolSize) 133 | poolSize.total = len(pool.connections) 134 | for _, connection := range pool.connections { 135 | switch connection.status { 136 | case CONNECTING: 137 | poolSize.connecting++ 138 | case IDLE: 139 | poolSize.idle++ 140 | case RUNNING: 141 | poolSize.running++ 142 | } 143 | } 144 | 145 | return 146 | } 147 | -------------------------------------------------------------------------------- /common/request.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "net/http" 5 | "net/url" 6 | ) 7 | 8 | // HTTPRequest is a serializable version of http.Request ( with only usefull fields ) 9 | type HTTPRequest struct { 10 | Method string 11 | URL string 12 | Header map[string][]string 13 | ContentLength int64 14 | } 15 | 16 | // SerializeHTTPRequest create a new HTTPRequest from a http.Request 17 | func SerializeHTTPRequest(req *http.Request) (r *HTTPRequest) { 18 | r = new(HTTPRequest) 19 | r.URL = req.URL.String() 20 | r.Method = req.Method 21 | r.Header = req.Header 22 | r.ContentLength = req.ContentLength 23 | return 24 | } 25 | 26 | // UnserializeHTTPRequest create a new http.Request from a HTTPRequest 27 | func UnserializeHTTPRequest(req *HTTPRequest) (r *http.Request, err error) { 28 | r = new(http.Request) 29 | r.Method = req.Method 30 | r.URL, err = url.Parse(req.URL) 31 | if err != nil { 32 | return 33 | } 34 | r.Header = req.Header 35 | r.ContentLength = req.ContentLength 36 | return 37 | } 38 | -------------------------------------------------------------------------------- /common/response.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "net/http" 5 | ) 6 | 7 | // HTTPResponse is a serializable version of http.Response ( with only useful fields ) 8 | type HTTPResponse struct { 9 | StatusCode int 10 | Header http.Header 11 | ContentLength int64 12 | } 13 | 14 | // SerializeHTTPResponse create a new HTTPResponse from a http.Response 15 | func SerializeHTTPResponse(resp *http.Response) *HTTPResponse { 16 | r := new(HTTPResponse) 17 | r.StatusCode = resp.StatusCode 18 | r.Header = resp.Header 19 | r.ContentLength = resp.ContentLength 20 | return r 21 | } 22 | 23 | // NewHTTPResponse creates a new HTTPResponse 24 | func NewHTTPResponse() (r *HTTPResponse) { 25 | r = new(HTTPResponse) 26 | r.Header = make(http.Header) 27 | return 28 | } 29 | -------------------------------------------------------------------------------- /common/rules.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "regexp" 7 | ) 8 | 9 | // Rule match HTTP requests to allow / deny access 10 | type Rule struct { 11 | Method string 12 | URL string 13 | Headers map[string]string 14 | 15 | methodRegex *regexp.Regexp 16 | urlRegex *regexp.Regexp 17 | headersRegex map[string]*regexp.Regexp 18 | } 19 | 20 | // NewRule creates a new Rule 21 | func NewRule(method string, url string, headers map[string]string) (rule *Rule, err error) { 22 | rule = new(Rule) 23 | rule.Method = method 24 | rule.URL = url 25 | if headers != nil { 26 | rule.Headers = headers 27 | } else { 28 | rule.Headers = make(map[string]string) 29 | } 30 | err = rule.Compile() 31 | return 32 | } 33 | 34 | // Compile the regular expressions 35 | func (rule *Rule) Compile() (err error) { 36 | if rule.Method != "" { 37 | rule.methodRegex, err = regexp.Compile(rule.Method) 38 | if err != nil { 39 | return 40 | } 41 | } 42 | if rule.URL != "" { 43 | rule.urlRegex, err = regexp.Compile(rule.URL) 44 | if err != nil { 45 | return 46 | } 47 | } 48 | rule.headersRegex = make(map[string]*regexp.Regexp) 49 | for header, regexStr := range rule.Headers { 50 | var regex *regexp.Regexp 51 | regex, err = regexp.Compile(regexStr) 52 | if err != nil { 53 | return 54 | } 55 | rule.headersRegex[header] = regex 56 | } 57 | return 58 | } 59 | 60 | // Match returns true if the http.Request matches the Rule 61 | func (rule *Rule) Match(req *http.Request) bool { 62 | if rule.methodRegex != nil && !rule.methodRegex.MatchString(req.Method) { 63 | return false 64 | } 65 | if rule.urlRegex != nil && !rule.urlRegex.MatchString(req.URL.String()) { 66 | return false 67 | } 68 | 69 | for headerName, regex := range rule.headersRegex { 70 | if !regex.MatchString(req.Header.Get(headerName)) { 71 | return false 72 | } 73 | } 74 | 75 | return true 76 | } 77 | 78 | func (rule *Rule) String() string { 79 | return fmt.Sprintf("%s %s %v", rule.Method, rule.URL, rule.Headers) 80 | } 81 | -------------------------------------------------------------------------------- /common/utils.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | ) 8 | 9 | // ProxyError log error and return a HTTP 526 error with the message 10 | func ProxyError(w http.ResponseWriter, err error) { 11 | log.Println(err) 12 | http.Error(w, err.Error(), 526) 13 | } 14 | 15 | // ProxyErrorf log error and return a HTTP 526 error with the message 16 | func ProxyErrorf(w http.ResponseWriter, format string, args ...interface{}) { 17 | ProxyError(w, fmt.Errorf(format, args...)) 18 | } 19 | -------------------------------------------------------------------------------- /server/config.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "io/ioutil" 5 | 6 | "gopkg.in/yaml.v2" 7 | 8 | "github.com/root-gg/wsp/common" 9 | ) 10 | 11 | // Config configures an Server 12 | type Config struct { 13 | Host string 14 | Port int 15 | Timeout int 16 | IdleTimeout int 17 | Whitelist []*common.Rule 18 | Blacklist []*common.Rule 19 | SecretKey string 20 | } 21 | 22 | // NewConfig creates a new ProxyConfig 23 | func NewConfig() (config *Config) { 24 | config = new(Config) 25 | config.Host = "127.0.0.1" 26 | config.Port = 8080 27 | config.Timeout = 1000 28 | config.IdleTimeout = 60000 29 | config.Whitelist = make([]*common.Rule, 0) 30 | config.Blacklist = make([]*common.Rule, 0) 31 | return 32 | } 33 | 34 | // LoadConfiguration loads configuration from a YAML file 35 | func LoadConfiguration(path string) (config *Config, err error) { 36 | config = NewConfig() 37 | 38 | bytes, err := ioutil.ReadFile(path) 39 | if err != nil { 40 | return 41 | } 42 | 43 | err = yaml.Unmarshal(bytes, config) 44 | if err != nil { 45 | return 46 | } 47 | 48 | // Compile the rules 49 | 50 | for _, rule := range config.Whitelist { 51 | if err = rule.Compile(); err != nil { 52 | return 53 | } 54 | } 55 | 56 | for _, rule := range config.Blacklist { 57 | if err = rule.Compile(); err != nil { 58 | return 59 | } 60 | } 61 | 62 | return 63 | } 64 | -------------------------------------------------------------------------------- /server/connection.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | "io/ioutil" 8 | "log" 9 | "net/http" 10 | "sync" 11 | "time" 12 | 13 | "github.com/gorilla/websocket" 14 | 15 | "github.com/root-gg/wsp/common" 16 | ) 17 | 18 | // Status of a Connection 19 | const ( 20 | IDLE = iota 21 | BUSY 22 | CLOSED 23 | ) 24 | 25 | // Connection manage a single websocket connection from 26 | type Connection struct { 27 | pool *Pool 28 | ws *websocket.Conn 29 | status int 30 | idleSince time.Time 31 | lock sync.Mutex 32 | nextResponse chan chan io.Reader 33 | } 34 | 35 | // NewConnection return a new Connection 36 | func NewConnection(pool *Pool, ws *websocket.Conn) (connection *Connection) { 37 | connection = new(Connection) 38 | connection.pool = pool 39 | connection.ws = ws 40 | connection.nextResponse = make(chan chan io.Reader) 41 | 42 | connection.Release() 43 | 44 | go connection.read() 45 | 46 | return 47 | } 48 | 49 | // read the incoming message of the connection 50 | func (connection *Connection) read() { 51 | defer func() { 52 | if r := recover(); r != nil { 53 | log.Printf("Websocket crash recovered : %s", r) 54 | } 55 | connection.Close() 56 | }() 57 | 58 | for { 59 | if connection.status == CLOSED { 60 | break 61 | } 62 | 63 | // https://godoc.org/github.com/gorilla/websocket#hdr-Control_Messages 64 | // 65 | // We need to ensure : 66 | // - no concurrent calls to ws.NextReader() / ws.ReadMessage() 67 | // - only one reader exists at a time 68 | // - wait for reader to be consumed before requesting the next one 69 | // - always be reading on the socket to be able to process control messages ( ping / pong / close ) 70 | 71 | // We will block here until a message is received or the ws is closed 72 | _, reader, err := connection.ws.NextReader() 73 | if err != nil { 74 | break 75 | } 76 | 77 | if connection.status != BUSY { 78 | // We received a wild unexpected message 79 | break 80 | } 81 | 82 | // We received a message from the proxy 83 | // It is expected to be either a HttpResponse or a HttpResponseBody 84 | // We wait for proxyRequest to send a channel to get the message 85 | c := <-connection.nextResponse 86 | if c == nil { 87 | // We have been unlocked by Close() 88 | break 89 | } 90 | 91 | // Send the reader back to proxyRequest 92 | c <- reader 93 | 94 | // Wait for proxyRequest to close the channel 95 | // this notify that it is done with the reader 96 | <-c 97 | } 98 | } 99 | 100 | // Proxy a HTTP request through the Proxy over the websocket connection 101 | func (connection *Connection) proxyRequest(w http.ResponseWriter, r *http.Request) (err error) { 102 | log.Printf("proxy request to %s", connection.pool.id) 103 | 104 | // Serialize HTTP request 105 | jsonReq, err := json.Marshal(common.SerializeHTTPRequest(r)) 106 | if err != nil { 107 | return fmt.Errorf("Unable to serialize request : %s", err) 108 | } 109 | 110 | // Send the serialized HTTP request to the remote Proxy 111 | err = connection.ws.WriteMessage(websocket.TextMessage, jsonReq) 112 | if err != nil { 113 | return fmt.Errorf("Unable to write request : %s", err) 114 | } 115 | 116 | // Pipe the HTTP request body to the remote Proxy 117 | bodyWriter, err := connection.ws.NextWriter(websocket.BinaryMessage) 118 | if err != nil { 119 | return fmt.Errorf("Unable to get request body writer : %s", err) 120 | } 121 | _, err = io.Copy(bodyWriter, r.Body) 122 | if err != nil { 123 | return fmt.Errorf("Unable to pipe request body : %s", err) 124 | } 125 | err = bodyWriter.Close() 126 | if err != nil { 127 | return fmt.Errorf("Unable to pipe request body (close) : %s", err) 128 | } 129 | 130 | // Get the serialized HTTP Response from the remote Proxy 131 | // To do so send a new channel to the read() goroutine 132 | // to get the next message reader 133 | responseChannel := make(chan (io.Reader)) 134 | connection.nextResponse <- responseChannel 135 | responseReader, more := <-responseChannel 136 | if responseReader == nil { 137 | if more { 138 | // If more is false the channel is already closed 139 | close(responseChannel) 140 | } 141 | return fmt.Errorf("Unable to get http response reader : %s", err) 142 | } 143 | 144 | // Read the HTTP Response 145 | jsonResponse, err := ioutil.ReadAll(responseReader) 146 | if err != nil { 147 | close(responseChannel) 148 | return fmt.Errorf("Unable to read http response : %s", err) 149 | } 150 | 151 | // Notify the read() goroutine that we are done reading the response 152 | close(responseChannel) 153 | 154 | // Deserialize the HTTP Response 155 | httpResponse := new(common.HTTPResponse) 156 | err = json.Unmarshal(jsonResponse, httpResponse) 157 | if err != nil { 158 | return fmt.Errorf("Unable to unserialize http response : %s", err) 159 | } 160 | 161 | // Write response headers back to the client 162 | for header, values := range httpResponse.Header { 163 | for _, value := range values { 164 | w.Header().Add(header, value) 165 | } 166 | } 167 | w.WriteHeader(httpResponse.StatusCode) 168 | 169 | // Get the HTTP Response body from the remote Proxy 170 | // To do so send a new channel to the read() goroutine 171 | // to get the next message reader 172 | responseBodyChannel := make(chan (io.Reader)) 173 | connection.nextResponse <- responseBodyChannel 174 | responseBodyReader, more := <-responseBodyChannel 175 | if responseBodyReader == nil { 176 | if more { 177 | // If more is false the channel is already closed 178 | close(responseChannel) 179 | } 180 | return fmt.Errorf("Unable to get http response body reader : %s", err) 181 | } 182 | 183 | // Pipe the HTTP response body right from the remote Proxy to the client 184 | _, err = io.Copy(w, responseBodyReader) 185 | if err != nil { 186 | close(responseBodyChannel) 187 | return fmt.Errorf("Unable to pipe response body : %s", err) 188 | } 189 | 190 | // Notify read() that we are done reading the response body 191 | close(responseBodyChannel) 192 | 193 | connection.Release() 194 | 195 | return 196 | } 197 | 198 | // Take notifies that this connection is going to be used 199 | func (connection *Connection) Take() bool { 200 | connection.lock.Lock() 201 | defer connection.lock.Unlock() 202 | 203 | if connection.status == CLOSED { 204 | return false 205 | } 206 | 207 | if connection.status == BUSY { 208 | return false 209 | } 210 | 211 | connection.status = BUSY 212 | return true 213 | } 214 | 215 | // Release notifies that this connection is ready to use again 216 | func (connection *Connection) Release() { 217 | connection.lock.Lock() 218 | defer connection.lock.Unlock() 219 | 220 | if connection.status == CLOSED { 221 | return 222 | } 223 | 224 | connection.idleSince = time.Now() 225 | connection.status = IDLE 226 | 227 | go connection.pool.Offer(connection) 228 | } 229 | 230 | // Close the connection 231 | func (connection *Connection) Close() { 232 | connection.lock.Lock() 233 | defer connection.lock.Unlock() 234 | 235 | connection.close() 236 | } 237 | 238 | // Close the connection ( without lock ) 239 | func (connection *Connection) close() { 240 | if connection.status == CLOSED { 241 | return 242 | } 243 | 244 | log.Printf("Closing connection from %s", connection.pool.id) 245 | 246 | // This one will be executed *before* lock.Unlock() 247 | defer func() { connection.status = CLOSED }() 248 | 249 | // Unlock a possible read() wild message 250 | close(connection.nextResponse) 251 | 252 | // Close the underlying TCP connection 253 | connection.ws.Close() 254 | } 255 | -------------------------------------------------------------------------------- /server/pool.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "log" 5 | "sync" 6 | "time" 7 | 8 | "github.com/gorilla/websocket" 9 | ) 10 | 11 | // Pool handle all connections from a remote Proxy 12 | type Pool struct { 13 | server *Server 14 | id string 15 | 16 | size int 17 | 18 | connections []*Connection 19 | idle chan *Connection 20 | 21 | done bool 22 | lock sync.RWMutex 23 | } 24 | 25 | // NewPool creates a new Pool 26 | func NewPool(server *Server, id string) (pool *Pool) { 27 | pool = new(Pool) 28 | pool.server = server 29 | pool.id = id 30 | pool.idle = make(chan *Connection) 31 | return 32 | } 33 | 34 | // Register creates a new Connection and adds it to the pool 35 | func (pool *Pool) Register(ws *websocket.Conn) { 36 | pool.lock.Lock() 37 | defer pool.lock.Unlock() 38 | 39 | // Ensure we never add a connection to a pool we have garbage collected 40 | if pool.done { 41 | return 42 | } 43 | 44 | log.Printf("Registering new connection from %s", pool.id) 45 | connection := NewConnection(pool, ws) 46 | pool.connections = append(pool.connections, connection) 47 | 48 | return 49 | } 50 | 51 | // Offer an idle connection to the server 52 | func (pool *Pool) Offer(connection *Connection) { 53 | go func() { pool.idle <- connection }() 54 | } 55 | 56 | // Clean removes dead connection from the pool 57 | // Look for dead connection in the pool 58 | // This MUST be surrounded by pool.lock.Lock() 59 | func (pool *Pool) Clean() { 60 | idle := 0 61 | var connections []*Connection 62 | 63 | for _, connection := range pool.connections { 64 | // We need to be sur we'll never close a BUSY or soon to be BUSY connection 65 | connection.lock.Lock() 66 | if connection.status == IDLE { 67 | idle++ 68 | if idle > pool.size { 69 | // We have enough idle connections in the pool. 70 | // Terminate the connection if it is idle since more that IdleTimeout 71 | if int(time.Now().Sub(connection.idleSince).Seconds())*1000 > pool.server.Config.IdleTimeout { 72 | connection.close() 73 | } 74 | } 75 | } 76 | connection.lock.Unlock() 77 | if connection.status == CLOSED { 78 | continue 79 | } 80 | connections = append(connections, connection) 81 | } 82 | pool.connections = connections 83 | } 84 | 85 | // IsEmpty clean the pool and return true if the pool is empty 86 | func (pool *Pool) IsEmpty() bool { 87 | pool.lock.Lock() 88 | defer pool.lock.Unlock() 89 | 90 | pool.Clean() 91 | return len(pool.connections) == 0 92 | } 93 | 94 | // Shutdown closes every connections in the pool and cleans it 95 | func (pool *Pool) Shutdown() { 96 | pool.lock.Lock() 97 | defer pool.lock.Unlock() 98 | 99 | pool.done = true 100 | 101 | for _, connection := range pool.connections { 102 | connection.Close() 103 | } 104 | pool.Clean() 105 | } 106 | 107 | // PoolSize is the number of connection in each state in the pool 108 | type PoolSize struct { 109 | Idle int 110 | Busy int 111 | Closed int 112 | } 113 | 114 | // Size return the number of connection in each state in the pool 115 | func (pool *Pool) Size() (ps *PoolSize) { 116 | pool.lock.Lock() 117 | defer pool.lock.Unlock() 118 | 119 | ps = new(PoolSize) 120 | for _, connection := range pool.connections { 121 | if connection.status == IDLE { 122 | ps.Idle++ 123 | } else if connection.status == BUSY { 124 | ps.Busy++ 125 | } else if connection.status == CLOSED { 126 | ps.Closed++ 127 | } 128 | } 129 | 130 | return 131 | } 132 | -------------------------------------------------------------------------------- /server/server.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "log" 5 | "math/rand" 6 | "net/http" 7 | "net/url" 8 | "reflect" 9 | "strconv" 10 | "strings" 11 | "sync" 12 | "time" 13 | 14 | "github.com/gorilla/websocket" 15 | "github.com/root-gg/wsp/common" 16 | ) 17 | 18 | // Server is a Reverse HTTP Proxy over WebSocket 19 | // This is the Server part, Clients will offer websocket connections, 20 | // those will be pooled to transfer HTTP Request and response 21 | type Server struct { 22 | Config *Config 23 | 24 | upgrader websocket.Upgrader 25 | 26 | pools []*Pool 27 | lock sync.RWMutex 28 | done chan struct{} 29 | 30 | dispatcher chan *ConnectionRequest 31 | 32 | server *http.Server 33 | } 34 | 35 | // ConnectionRequest is used to request a proxy connection from the dispatcher 36 | type ConnectionRequest struct { 37 | connection chan *Connection 38 | timeout <-chan time.Time 39 | } 40 | 41 | // NewConnectionRequest creates a new connection request 42 | func NewConnectionRequest(timeout time.Duration) (cr *ConnectionRequest) { 43 | cr = new(ConnectionRequest) 44 | cr.connection = make(chan *Connection) 45 | if timeout > 0 { 46 | cr.timeout = time.After(timeout) 47 | } 48 | return 49 | } 50 | 51 | // NewServer return a new Server instance 52 | func NewServer(config *Config) (server *Server) { 53 | rand.Seed(time.Now().Unix()) 54 | 55 | server = new(Server) 56 | server.Config = config 57 | server.upgrader = websocket.Upgrader{} 58 | 59 | server.done = make(chan struct{}) 60 | server.dispatcher = make(chan *ConnectionRequest) 61 | return 62 | } 63 | 64 | // Start Server HTTP server 65 | func (server *Server) Start() { 66 | go func() { 67 | for { 68 | select { 69 | case <-server.done: 70 | break 71 | case <-time.After(5 * time.Second): 72 | server.clean() 73 | } 74 | } 75 | }() 76 | 77 | r := http.NewServeMux() 78 | r.HandleFunc("/request", server.request) 79 | r.HandleFunc("/register", server.register) 80 | r.HandleFunc("/status", server.status) 81 | 82 | go server.dispatchConnections() 83 | 84 | server.server = &http.Server{Addr: server.Config.Host + ":" + strconv.Itoa(server.Config.Port), Handler: r} 85 | go func() { log.Fatal(server.server.ListenAndServe()) }() 86 | } 87 | 88 | // clean remove empty Pools 89 | func (server *Server) clean() { 90 | server.lock.Lock() 91 | defer server.lock.Unlock() 92 | 93 | if len(server.pools) == 0 { 94 | return 95 | } 96 | 97 | idle := 0 98 | busy := 0 99 | 100 | var pools []*Pool 101 | for _, pool := range server.pools { 102 | if pool.IsEmpty() { 103 | log.Printf("Removing empty connection pool : %s", pool.id) 104 | pool.Shutdown() 105 | } else { 106 | pools = append(pools, pool) 107 | } 108 | 109 | ps := pool.Size() 110 | idle += ps.Idle 111 | busy += ps.Busy 112 | } 113 | 114 | log.Printf("%d pools, %d idle, %d busy", len(pools), idle, busy) 115 | 116 | server.pools = pools 117 | } 118 | 119 | // Dispatch connection from available pools to clients requests 120 | func (server *Server) dispatchConnections() { 121 | for { 122 | // A client requests a connection 123 | request, ok := <-server.dispatcher 124 | if !ok { 125 | // Shutdown 126 | break 127 | } 128 | 129 | for { 130 | server.lock.RLock() 131 | 132 | if len(server.pools) == 0 { 133 | // No connection pool available 134 | server.lock.RUnlock() 135 | break 136 | } 137 | 138 | // Build a select statement dynamically 139 | cases := make([]reflect.SelectCase, len(server.pools)+1) 140 | 141 | // Add all pools idle connection channel 142 | for i, ch := range server.pools { 143 | cases[i] = reflect.SelectCase{ 144 | Dir: reflect.SelectRecv, 145 | Chan: reflect.ValueOf(ch.idle)} 146 | } 147 | 148 | // Add timeout channel 149 | if request.timeout != nil { 150 | cases[len(cases)-1] = reflect.SelectCase{ 151 | Dir: reflect.SelectRecv, 152 | Chan: reflect.ValueOf(request.timeout)} 153 | } else { 154 | cases[len(cases)-1] = reflect.SelectCase{ 155 | Dir: reflect.SelectDefault} 156 | } 157 | 158 | server.lock.RUnlock() 159 | 160 | // This call blocks 161 | chosen, value, ok := reflect.Select(cases) 162 | 163 | if chosen == len(cases)-1 { 164 | // a timeout occured 165 | break 166 | } 167 | if !ok { 168 | // a proxy pool has been removed, try again 169 | continue 170 | } 171 | connection, _ := value.Interface().(*Connection) 172 | 173 | // Verify that we can use this connection 174 | if connection.Take() { 175 | request.connection <- connection 176 | break 177 | } 178 | } 179 | 180 | close(request.connection) 181 | } 182 | } 183 | 184 | // This is the way for clients to execute HTTP requests through an Proxy 185 | func (server *Server) request(w http.ResponseWriter, r *http.Request) { 186 | // Parse destination URL 187 | dstURL := r.Header.Get("X-PROXY-DESTINATION") 188 | if dstURL == "" { 189 | common.ProxyErrorf(w, "Missing X-PROXY-DESTINATION header") 190 | return 191 | } 192 | URL, err := url.Parse(dstURL) 193 | if err != nil { 194 | common.ProxyErrorf(w, "Unable to parse X-PROXY-DESTINATION header") 195 | return 196 | } 197 | r.URL = URL 198 | 199 | log.Printf("[%s] %s", r.Method, r.URL.String()) 200 | 201 | // Apply blacklist 202 | if len(server.Config.Blacklist) > 0 { 203 | for _, rule := range server.Config.Blacklist { 204 | if rule.Match(r) { 205 | common.ProxyErrorf(w, "Destination is forbidden") 206 | return 207 | } 208 | } 209 | } 210 | 211 | // Apply whitelist 212 | if len(server.Config.Whitelist) > 0 { 213 | allowed := false 214 | for _, rule := range server.Config.Whitelist { 215 | if rule.Match(r) { 216 | allowed = true 217 | break 218 | } 219 | } 220 | if !allowed { 221 | common.ProxyErrorf(w, "Destination is not allowed") 222 | return 223 | } 224 | } 225 | 226 | if len(server.pools) == 0 { 227 | common.ProxyErrorf(w, "No proxy available") 228 | return 229 | } 230 | 231 | // Get a proxy connection 232 | request := NewConnectionRequest(time.Duration(server.Config.Timeout) * time.Millisecond) 233 | server.dispatcher <- request 234 | connection := <-request.connection 235 | if connection == nil { 236 | common.ProxyErrorf(w, "Unable to get a proxy connection") 237 | return 238 | } 239 | 240 | // Send the request to the proxy 241 | err = connection.proxyRequest(w, r) 242 | if err != nil { 243 | // An error occurred throw the connection away 244 | log.Println(err) 245 | connection.Close() 246 | 247 | // Try to return an error to the client 248 | // This might fail if response headers have already been sent 249 | common.ProxyError(w, err) 250 | } 251 | } 252 | 253 | // This is the way for wsp clients to offer websocket connections 254 | func (server *Server) register(w http.ResponseWriter, r *http.Request) { 255 | secretKey := r.Header.Get("X-SECRET-KEY") 256 | if secretKey != server.Config.SecretKey { 257 | common.ProxyErrorf(w, "Invalid X-SECRET-KEY") 258 | return 259 | } 260 | 261 | ws, err := server.upgrader.Upgrade(w, r, nil) 262 | if err != nil { 263 | common.ProxyErrorf(w, "HTTP upgrade error : %v", err) 264 | return 265 | } 266 | 267 | // The first message should contains the remote Proxy name and size 268 | _, greeting, err := ws.ReadMessage() 269 | if err != nil { 270 | common.ProxyErrorf(w, "Unable to read greeting message : %s", err) 271 | ws.Close() 272 | return 273 | } 274 | 275 | // Parse the greeting message 276 | split := strings.Split(string(greeting), "_") 277 | id := split[0] 278 | size, err := strconv.Atoi(split[1]) 279 | if err != nil { 280 | common.ProxyErrorf(w, "Unable to parse greeting message : %s", err) 281 | ws.Close() 282 | return 283 | } 284 | 285 | server.lock.Lock() 286 | defer server.lock.Unlock() 287 | 288 | // Get that client's Pool 289 | var pool *Pool 290 | for _, p := range server.pools { 291 | if p.id == id { 292 | pool = p 293 | break 294 | } 295 | } 296 | if pool == nil { 297 | pool = NewPool(server, id) 298 | server.pools = append(server.pools, pool) 299 | } 300 | 301 | // update pool size 302 | pool.size = size 303 | 304 | // Add the ws to the pool 305 | pool.Register(ws) 306 | } 307 | 308 | func (server *Server) status(w http.ResponseWriter, r *http.Request) { 309 | w.Write([]byte("ok")) 310 | } 311 | 312 | // Shutdown stop the Server 313 | func (server *Server) Shutdown() { 314 | close(server.done) 315 | close(server.dispatcher) 316 | for _, pool := range server.pools { 317 | pool.Shutdown() 318 | } 319 | server.clean() 320 | } 321 | -------------------------------------------------------------------------------- /test_api/test_api.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "io/ioutil" 6 | "log" 7 | "net/http" 8 | "time" 9 | ) 10 | 11 | // For testing purpose 12 | 13 | var addr = flag.String("addr", "localhost:8081", "http service address") 14 | 15 | func hello(w http.ResponseWriter, r *http.Request) { 16 | log.Println("hello") 17 | w.Write([]byte("hello world\n")) 18 | } 19 | 20 | func header(w http.ResponseWriter, r *http.Request) { 21 | log.Println("header") 22 | w.Header().Add("hello", "world") 23 | w.Write([]byte("hello world in header\n")) 24 | } 25 | 26 | func post(w http.ResponseWriter, r *http.Request) { 27 | body, err := ioutil.ReadAll(r.Body) 28 | if err != nil { 29 | log.Println(err) 30 | } 31 | r.Body.Close() 32 | 33 | log.Println("post") 34 | log.Println(string(body)) 35 | 36 | w.Write(body) 37 | } 38 | 39 | func fail(w http.ResponseWriter, r *http.Request) { 40 | http.Error(w, "GO FUNK YOURSELF", 666) 41 | } 42 | 43 | func sleep(w http.ResponseWriter, r *http.Request) { 44 | time.Sleep(10 * time.Second) 45 | w.Write([]byte("ok")) 46 | } 47 | 48 | func main() { 49 | flag.Parse() 50 | log.SetFlags(0) 51 | http.HandleFunc("/hello", hello) 52 | http.HandleFunc("/header", header) 53 | http.HandleFunc("/fail", fail) 54 | http.HandleFunc("/post", post) 55 | http.HandleFunc("/sleep", sleep) 56 | log.Fatal(http.ListenAndServe(*addr, nil)) 57 | } 58 | -------------------------------------------------------------------------------- /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: 1.7 10 | - go: tip 11 | allow_failures: 12 | - go: tip 13 | 14 | script: 15 | - go get -t -v ./... 16 | - diff -u <(echo -n) <(gofmt -d .) 17 | - go vet $(go list ./... | grep -v /vendor/) 18 | - go test -v -race ./... 19 | -------------------------------------------------------------------------------- /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 | [![Build Status](https://travis-ci.org/gorilla/websocket.svg?branch=master)](https://travis-ci.org/gorilla/websocket) 7 | [![GoDoc](https://godoc.org/github.com/gorilla/websocket?status.svg)](https://godoc.org/github.com/gorilla/websocket) 8 | 9 | ### Documentation 10 | 11 | * [API Reference](http://godoc.org/github.com/gorilla/websocket) 12 | * [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat) 13 | * [Command example](https://github.com/gorilla/websocket/tree/master/examples/command) 14 | * [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo) 15 | * [File watch example](https://github.com/gorilla/websocket/tree/master/examples/filewatch) 16 | 17 | ### Status 18 | 19 | The Gorilla WebSocket package provides a complete and tested implementation of 20 | the [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. The 21 | package API is stable. 22 | 23 | ### Installation 24 | 25 | go get github.com/gorilla/websocket 26 | 27 | ### Protocol Compliance 28 | 29 | The Gorilla WebSocket package passes the server tests in the [Autobahn Test 30 | Suite](http://autobahn.ws/testsuite) using the application in the [examples/autobahn 31 | subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn). 32 | 33 | ### Gorilla WebSocket compared with other packages 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 |
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
Compression ExtensionsExperimentalNo
Read message using io.ReaderYesNo, see note 3
Write message using io.WriteCloserYesNo, see note 3
53 | 54 | Notes: 55 | 56 | 1. Large messages are fragmented in [Chrome's new WebSocket implementation](http://www.ietf.org/mail-archive/web/hybi/current/msg10503.html). 57 | 2. The application can get the type of a received data message by implementing 58 | a [Codec marshal](http://godoc.org/golang.org/x/net/websocket#Codec.Marshal) 59 | function. 60 | 3. The go.net io.Reader and io.Writer operate across WebSocket frame boundaries. 61 | Read returns when the input buffer is full or a frame boundary is 62 | encountered. Each call to Write sends a single frame message. The Gorilla 63 | io.Reader and io.WriteCloser operate on a single WebSocket message. 64 | 65 | -------------------------------------------------------------------------------- /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/mask.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of 2 | // this source code is governed by a BSD-style license that can be found in the 3 | // LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "math/rand" 9 | "unsafe" 10 | ) 11 | 12 | const wordSize = int(unsafe.Sizeof(uintptr(0))) 13 | 14 | func newMaskKey() [4]byte { 15 | n := rand.Uint32() 16 | return [4]byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)} 17 | } 18 | 19 | func maskBytes(key [4]byte, pos int, b []byte) int { 20 | 21 | // Mask one byte at a time for small buffers. 22 | if len(b) < 2*wordSize { 23 | for i := range b { 24 | b[i] ^= key[pos&3] 25 | pos++ 26 | } 27 | return pos & 3 28 | } 29 | 30 | // Mask one byte at a time to word boundary. 31 | if n := int(uintptr(unsafe.Pointer(&b[0]))) % wordSize; n != 0 { 32 | n = wordSize - n 33 | for i := range b[:n] { 34 | b[i] ^= key[pos&3] 35 | pos++ 36 | } 37 | b = b[n:] 38 | } 39 | 40 | // Create aligned word size key. 41 | var k [wordSize]byte 42 | for i := range k { 43 | k[i] = key[(pos+i)&3] 44 | } 45 | kw := *(*uintptr)(unsafe.Pointer(&k)) 46 | 47 | // Mask one word at a time. 48 | n := (len(b) / wordSize) * wordSize 49 | for i := 0; i < n; i += wordSize { 50 | *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i))) ^= kw 51 | } 52 | 53 | // Mask one byte at a time for remaining bytes. 54 | b = b[n:] 55 | for i := range b { 56 | b[i] ^= key[pos&3] 57 | pos++ 58 | } 59 | 60 | return pos & 3 61 | } 62 | -------------------------------------------------------------------------------- /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/nu7hatch/gouuid/.gitignore: -------------------------------------------------------------------------------- 1 | _obj 2 | _test 3 | *.6 4 | *.out 5 | _testmain.go 6 | \#* 7 | .\#* 8 | *.log 9 | _cgo* 10 | *.o 11 | *.a 12 | -------------------------------------------------------------------------------- /vendor/github.com/nu7hatch/gouuid/COPYING: -------------------------------------------------------------------------------- 1 | Copyright (C) 2011 by Krzysztof Kowalik 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. -------------------------------------------------------------------------------- /vendor/github.com/nu7hatch/gouuid/README.md: -------------------------------------------------------------------------------- 1 | # Pure Go UUID implementation 2 | 3 | This package provides immutable UUID structs and the functions 4 | NewV3, NewV4, NewV5 and Parse() for generating versions 3, 4 5 | and 5 UUIDs as specified in [RFC 4122](http://www.ietf.org/rfc/rfc4122.txt). 6 | 7 | ## Installation 8 | 9 | Use the `go` tool: 10 | 11 | $ go get github.com/nu7hatch/gouuid 12 | 13 | ## Usage 14 | 15 | See [documentation and examples](http://godoc.org/github.com/nu7hatch/gouuid) 16 | for more information. 17 | 18 | ## Copyright 19 | 20 | Copyright (C) 2011 by Krzysztof Kowalik . See [COPYING](https://github.com/nu7hatch/gouuid/tree/master/COPYING) 21 | file for details. 22 | -------------------------------------------------------------------------------- /vendor/github.com/nu7hatch/gouuid/uuid.go: -------------------------------------------------------------------------------- 1 | // This package provides immutable UUID structs and the functions 2 | // NewV3, NewV4, NewV5 and Parse() for generating versions 3, 4 3 | // and 5 UUIDs as specified in RFC 4122. 4 | // 5 | // Copyright (C) 2011 by Krzysztof Kowalik 6 | package uuid 7 | 8 | import ( 9 | "crypto/md5" 10 | "crypto/rand" 11 | "crypto/sha1" 12 | "encoding/hex" 13 | "errors" 14 | "fmt" 15 | "hash" 16 | "regexp" 17 | ) 18 | 19 | // The UUID reserved variants. 20 | const ( 21 | ReservedNCS byte = 0x80 22 | ReservedRFC4122 byte = 0x40 23 | ReservedMicrosoft byte = 0x20 24 | ReservedFuture byte = 0x00 25 | ) 26 | 27 | // The following standard UUIDs are for use with NewV3() or NewV5(). 28 | var ( 29 | NamespaceDNS, _ = ParseHex("6ba7b810-9dad-11d1-80b4-00c04fd430c8") 30 | NamespaceURL, _ = ParseHex("6ba7b811-9dad-11d1-80b4-00c04fd430c8") 31 | NamespaceOID, _ = ParseHex("6ba7b812-9dad-11d1-80b4-00c04fd430c8") 32 | NamespaceX500, _ = ParseHex("6ba7b814-9dad-11d1-80b4-00c04fd430c8") 33 | ) 34 | 35 | // Pattern used to parse hex string representation of the UUID. 36 | // FIXME: do something to consider both brackets at one time, 37 | // current one allows to parse string with only one opening 38 | // or closing bracket. 39 | const hexPattern = "^(urn\\:uuid\\:)?\\{?([a-z0-9]{8})-([a-z0-9]{4})-" + 40 | "([1-5][a-z0-9]{3})-([a-z0-9]{4})-([a-z0-9]{12})\\}?$" 41 | 42 | var re = regexp.MustCompile(hexPattern) 43 | 44 | // A UUID representation compliant with specification in 45 | // RFC 4122 document. 46 | type UUID [16]byte 47 | 48 | // ParseHex creates a UUID object from given hex string 49 | // representation. Function accepts UUID string in following 50 | // formats: 51 | // 52 | // uuid.ParseHex("6ba7b814-9dad-11d1-80b4-00c04fd430c8") 53 | // uuid.ParseHex("{6ba7b814-9dad-11d1-80b4-00c04fd430c8}") 54 | // uuid.ParseHex("urn:uuid:6ba7b814-9dad-11d1-80b4-00c04fd430c8") 55 | // 56 | func ParseHex(s string) (u *UUID, err error) { 57 | md := re.FindStringSubmatch(s) 58 | if md == nil { 59 | err = errors.New("Invalid UUID string") 60 | return 61 | } 62 | hash := md[2] + md[3] + md[4] + md[5] + md[6] 63 | b, err := hex.DecodeString(hash) 64 | if err != nil { 65 | return 66 | } 67 | u = new(UUID) 68 | copy(u[:], b) 69 | return 70 | } 71 | 72 | // Parse creates a UUID object from given bytes slice. 73 | func Parse(b []byte) (u *UUID, err error) { 74 | if len(b) != 16 { 75 | err = errors.New("Given slice is not valid UUID sequence") 76 | return 77 | } 78 | u = new(UUID) 79 | copy(u[:], b) 80 | return 81 | } 82 | 83 | // Generate a UUID based on the MD5 hash of a namespace identifier 84 | // and a name. 85 | func NewV3(ns *UUID, name []byte) (u *UUID, err error) { 86 | if ns == nil { 87 | err = errors.New("Invalid namespace UUID") 88 | return 89 | } 90 | u = new(UUID) 91 | // Set all bits to MD5 hash generated from namespace and name. 92 | u.setBytesFromHash(md5.New(), ns[:], name) 93 | u.setVariant(ReservedRFC4122) 94 | u.setVersion(3) 95 | return 96 | } 97 | 98 | // Generate a random UUID. 99 | func NewV4() (u *UUID, err error) { 100 | u = new(UUID) 101 | // Set all bits to randomly (or pseudo-randomly) chosen values. 102 | _, err = rand.Read(u[:]) 103 | if err != nil { 104 | return 105 | } 106 | u.setVariant(ReservedRFC4122) 107 | u.setVersion(4) 108 | return 109 | } 110 | 111 | // Generate a UUID based on the SHA-1 hash of a namespace identifier 112 | // and a name. 113 | func NewV5(ns *UUID, name []byte) (u *UUID, err error) { 114 | u = new(UUID) 115 | // Set all bits to truncated SHA1 hash generated from namespace 116 | // and name. 117 | u.setBytesFromHash(sha1.New(), ns[:], name) 118 | u.setVariant(ReservedRFC4122) 119 | u.setVersion(5) 120 | return 121 | } 122 | 123 | // Generate a MD5 hash of a namespace and a name, and copy it to the 124 | // UUID slice. 125 | func (u *UUID) setBytesFromHash(hash hash.Hash, ns, name []byte) { 126 | hash.Write(ns[:]) 127 | hash.Write(name) 128 | copy(u[:], hash.Sum([]byte{})[:16]) 129 | } 130 | 131 | // Set the two most significant bits (bits 6 and 7) of the 132 | // clock_seq_hi_and_reserved to zero and one, respectively. 133 | func (u *UUID) setVariant(v byte) { 134 | switch v { 135 | case ReservedNCS: 136 | u[8] = (u[8] | ReservedNCS) & 0xBF 137 | case ReservedRFC4122: 138 | u[8] = (u[8] | ReservedRFC4122) & 0x7F 139 | case ReservedMicrosoft: 140 | u[8] = (u[8] | ReservedMicrosoft) & 0x3F 141 | } 142 | } 143 | 144 | // Variant returns the UUID Variant, which determines the internal 145 | // layout of the UUID. This will be one of the constants: RESERVED_NCS, 146 | // RFC_4122, RESERVED_MICROSOFT, RESERVED_FUTURE. 147 | func (u *UUID) Variant() byte { 148 | if u[8]&ReservedNCS == ReservedNCS { 149 | return ReservedNCS 150 | } else if u[8]&ReservedRFC4122 == ReservedRFC4122 { 151 | return ReservedRFC4122 152 | } else if u[8]&ReservedMicrosoft == ReservedMicrosoft { 153 | return ReservedMicrosoft 154 | } 155 | return ReservedFuture 156 | } 157 | 158 | // Set the four most significant bits (bits 12 through 15) of the 159 | // time_hi_and_version field to the 4-bit version number. 160 | func (u *UUID) setVersion(v byte) { 161 | u[6] = (u[6] & 0xF) | (v << 4) 162 | } 163 | 164 | // Version returns a version number of the algorithm used to 165 | // generate the UUID sequence. 166 | func (u *UUID) Version() uint { 167 | return uint(u[6] >> 4) 168 | } 169 | 170 | // Returns unparsed version of the generated UUID sequence. 171 | func (u *UUID) String() string { 172 | return fmt.Sprintf("%x-%x-%x-%x-%x", u[0:4], u[4:6], u[6:8], u[8:10], u[10:]) 173 | } 174 | -------------------------------------------------------------------------------- /vendor/github.com/root-gg/utils/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /vendor/github.com/root-gg/utils/README.md: -------------------------------------------------------------------------------- 1 | Miscellaneous golang utils 2 | ========================== 3 | -------------------------------------------------------------------------------- /vendor/github.com/root-gg/utils/bytes.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | func BytesToString(size int) (result string) { 9 | switch { 10 | case size > (1024 * 1024 * 1024): 11 | result = fmt.Sprintf("%#.02f GB", float64(size)/1024/1024/1024) 12 | case size > (1024 * 1024): 13 | result = fmt.Sprintf("%#.02f MB", float64(size)/1024/1024) 14 | case size > 1024: 15 | result = fmt.Sprintf("%#.02f KB", float64(size)/1024) 16 | default: 17 | result = fmt.Sprintf("%d B", size) 18 | } 19 | result = strings.Trim(result, " ") 20 | return 21 | } 22 | -------------------------------------------------------------------------------- /vendor/github.com/root-gg/utils/caller.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "runtime" 5 | "strings" 6 | ) 7 | 8 | func GetCaller(depth int) (file string, line int, function string) { 9 | pc, file, line, ok := runtime.Caller(depth) 10 | if ok { 11 | function = runtime.FuncForPC(pc).Name() 12 | } 13 | return 14 | } 15 | 16 | func ParseFunction(fct string) (pkg string, function string) { 17 | i := strings.LastIndex(fct, ".") 18 | if i > 0 { 19 | pkg = fct[:i] 20 | function = fct[i+1:] 21 | } 22 | return 23 | } 24 | -------------------------------------------------------------------------------- /vendor/github.com/root-gg/utils/dumper.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | func Dump(data interface{}) { 9 | fmt.Println(Sdump(data)) 10 | } 11 | 12 | func Sdump(data interface{}) string { 13 | if json, err := json.MarshalIndent(data, "", " "); err == nil { 14 | return string(json) 15 | } else { 16 | return fmt.Sprintf("Unable to json data %v : %s", data, err) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /vendor/github.com/root-gg/utils/json.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "encoding/json" 4 | 5 | func ToJson(data interface{}) ([]byte, error) { 6 | return json.Marshal(data) 7 | } 8 | 9 | func ToJsonString(data interface{}) (string, error) { 10 | json, err := ToJson(data) 11 | return string(json), err 12 | } 13 | -------------------------------------------------------------------------------- /vendor/github.com/root-gg/utils/md5sum.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "crypto/md5" 5 | "fmt" 6 | "io" 7 | "os" 8 | ) 9 | 10 | func Md5sum(str string) (md5sum string, err error) { 11 | h := md5.New() 12 | _, err = io.WriteString(h, str) 13 | if err != nil { 14 | return 15 | } 16 | md5sum = fmt.Sprintf("%x", h.Sum(nil)) 17 | return 18 | } 19 | 20 | func FileMd5sum(filePath string) (md5sum string, err error) { 21 | file, err := os.Open(filePath) 22 | if err != nil { 23 | return 24 | } 25 | defer file.Close() 26 | 27 | h := md5.New() 28 | if _, err = io.Copy(h, file); err != nil { 29 | return 30 | } 31 | 32 | md5sum = fmt.Sprintf("%x", h.Sum(nil)) 33 | return 34 | } 35 | -------------------------------------------------------------------------------- /vendor/github.com/root-gg/utils/net.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | func NtoI(ip net.IP) (ipInt uint32) { 8 | ip = ip.To4() 9 | ipInt |= uint32(ip[0]) << 24 10 | ipInt |= uint32(ip[1]) << 16 11 | ipInt |= uint32(ip[2]) << 8 12 | ipInt |= uint32(ip[3]) 13 | return 14 | } 15 | 16 | func ItoN(ipInt uint32) net.IP { 17 | bytes := make([]byte, 4) 18 | bytes[0] = byte(ipInt >> 24 & 0xFF) 19 | bytes[1] = byte(ipInt >> 16 & 0xFF) 20 | bytes[2] = byte(ipInt >> 8 & 0xFF) 21 | bytes[3] = byte(ipInt & 0xFF) 22 | return net.IP(bytes) 23 | } 24 | -------------------------------------------------------------------------------- /vendor/github.com/root-gg/utils/reflect.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | /* 9 | * Assign a map[string]interface{} to a struct mapping the map pairs to 10 | * the structure members by name using reflexion. 11 | */ 12 | func Assign(config interface{}, values map[string]interface{}) { 13 | s := reflect.ValueOf(config).Elem() 14 | t := reflect.TypeOf(config) 15 | if t.Kind() == reflect.Ptr { 16 | t = t.Elem() 17 | } 18 | for key, val := range values { 19 | if typ, ok := t.FieldByName(key); ok { 20 | s.FieldByName(key).Set(reflect.ValueOf(val).Convert(typ.Type)) 21 | } 22 | } 23 | } 24 | 25 | /* 26 | * Transform []T to []interface{} 27 | */ 28 | func ToInterfaceArray(v interface{}) (res []interface{}) { 29 | switch reflect.TypeOf(v).Kind() { 30 | case reflect.Slice: 31 | s := reflect.ValueOf(v) 32 | res = make([]interface{}, s.Len()) 33 | for i := 0; i < s.Len(); i++ { 34 | res[i] = s.Index(i).Interface() 35 | } 36 | default: 37 | panic(fmt.Sprintf("unexpected type %T", v)) 38 | } 39 | return res 40 | } 41 | -------------------------------------------------------------------------------- /vendor/github.com/root-gg/utils/strings.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | func Chomp(str string) string { 4 | if str[len(str)-1] == '\n' { 5 | str = str[:len(str)-1] 6 | } 7 | return str 8 | } 9 | -------------------------------------------------------------------------------- /vendor/github.com/root-gg/utils/time.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "time" 4 | 5 | func TruncateDuration(d time.Duration, precision time.Duration) time.Duration { 6 | if d == 0 { 7 | return time.Duration(0) 8 | } 9 | p := float64(precision) 10 | n := float64(int(float64(d)/p)) * p 11 | return time.Duration(n) 12 | } 13 | -------------------------------------------------------------------------------- /vendor/github.com/root-gg/utils/timer.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "time" 7 | ) 8 | 9 | var Running = errors.New("running") 10 | var Stopped = errors.New("stopped") 11 | var Uninitalized = errors.New("uninitalized") 12 | 13 | type SplitTime struct { 14 | name string 15 | start *time.Time 16 | split *time.Time 17 | stop *time.Time 18 | } 19 | 20 | func NewSplitTime(name string) (split *SplitTime) { 21 | split = new(SplitTime) 22 | split.name = name 23 | return 24 | } 25 | 26 | func (split *SplitTime) Name() string { 27 | return split.name 28 | } 29 | 30 | func (split *SplitTime) Start() { 31 | if split.start == nil { 32 | now := time.Now() 33 | split.start = &now 34 | } 35 | } 36 | 37 | func (split *SplitTime) StartDate() *time.Time { 38 | return split.start 39 | } 40 | 41 | func (split *SplitTime) Split() (elapsed time.Duration) { 42 | if split.start != nil { 43 | if split.stop == nil { 44 | now := time.Now() 45 | if split.split == nil { 46 | elapsed = now.Sub(*split.start) 47 | } else { 48 | elapsed = now.Sub(*split.split) 49 | } 50 | split.split = &now 51 | return 52 | } 53 | } 54 | return 55 | } 56 | 57 | func (split *SplitTime) Stop() { 58 | if split.stop == nil { 59 | now := time.Now() 60 | split.stop = &now 61 | if split.start == nil { 62 | split.start = split.stop 63 | } 64 | } 65 | } 66 | 67 | func (split *SplitTime) StopDate() *time.Time { 68 | return split.stop 69 | } 70 | 71 | func (split *SplitTime) Elapsed() (elapsed time.Duration) { 72 | if split.start != nil { 73 | if split.stop == nil { 74 | elapsed = time.Since(*split.start) 75 | } else { 76 | elapsed = split.stop.Sub(*split.start) 77 | } 78 | } 79 | return 80 | } 81 | 82 | func (split *SplitTime) Status() error { 83 | if split.start == nil { 84 | return Uninitalized 85 | } else if split.stop == nil { 86 | return Running 87 | } else { 88 | return Stopped 89 | } 90 | } 91 | 92 | func (split *SplitTime) String() string { 93 | if split.start == nil { 94 | return fmt.Sprintf("%s : %s", split.name, split.Status()) 95 | } else { 96 | return fmt.Sprintf("%s : %s : %s", split.name, split.Status(), split.Elapsed()) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.4 5 | - 1.5 6 | - 1.6 7 | - tip 8 | 9 | go_import_path: gopkg.in/yaml.v2 10 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2011-2016 Canonical Ltd. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/LICENSE.libyaml: -------------------------------------------------------------------------------- 1 | The following files were ported to Go from C files of libyaml, and thus 2 | are still covered by their original copyright and license: 3 | 4 | apic.go 5 | emitterc.go 6 | parserc.go 7 | readerc.go 8 | scannerc.go 9 | writerc.go 10 | yamlh.go 11 | yamlprivateh.go 12 | 13 | Copyright (c) 2006 Kirill Simonov 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy of 16 | this software and associated documentation files (the "Software"), to deal in 17 | the Software without restriction, including without limitation the rights to 18 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 19 | of the Software, and to permit persons to whom the Software is furnished to do 20 | so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in all 23 | copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | SOFTWARE. 32 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/README.md: -------------------------------------------------------------------------------- 1 | # YAML support for the Go language 2 | 3 | Introduction 4 | ------------ 5 | 6 | The yaml package enables Go programs to comfortably encode and decode YAML 7 | values. It was developed within [Canonical](https://www.canonical.com) as 8 | part of the [juju](https://juju.ubuntu.com) project, and is based on a 9 | pure Go port of the well-known [libyaml](http://pyyaml.org/wiki/LibYAML) 10 | C library to parse and generate YAML data quickly and reliably. 11 | 12 | Compatibility 13 | ------------- 14 | 15 | The yaml package supports most of YAML 1.1 and 1.2, including support for 16 | anchors, tags, map merging, etc. Multi-document unmarshalling is not yet 17 | implemented, and base-60 floats from YAML 1.1 are purposefully not 18 | supported since they're a poor design and are gone in YAML 1.2. 19 | 20 | Installation and usage 21 | ---------------------- 22 | 23 | The import path for the package is *gopkg.in/yaml.v2*. 24 | 25 | To install it, run: 26 | 27 | go get gopkg.in/yaml.v2 28 | 29 | API documentation 30 | ----------------- 31 | 32 | If opened in a browser, the import path itself leads to the API documentation: 33 | 34 | * [https://gopkg.in/yaml.v2](https://gopkg.in/yaml.v2) 35 | 36 | API stability 37 | ------------- 38 | 39 | The package API for yaml v2 will remain stable as described in [gopkg.in](https://gopkg.in). 40 | 41 | 42 | License 43 | ------- 44 | 45 | The yaml package is licensed under the Apache License 2.0. Please see the LICENSE file for details. 46 | 47 | 48 | Example 49 | ------- 50 | 51 | ```Go 52 | package main 53 | 54 | import ( 55 | "fmt" 56 | "log" 57 | 58 | "gopkg.in/yaml.v2" 59 | ) 60 | 61 | var data = ` 62 | a: Easy! 63 | b: 64 | c: 2 65 | d: [3, 4] 66 | ` 67 | 68 | type T struct { 69 | A string 70 | B struct { 71 | RenamedC int `yaml:"c"` 72 | D []int `yaml:",flow"` 73 | } 74 | } 75 | 76 | func main() { 77 | t := T{} 78 | 79 | err := yaml.Unmarshal([]byte(data), &t) 80 | if err != nil { 81 | log.Fatalf("error: %v", err) 82 | } 83 | fmt.Printf("--- t:\n%v\n\n", t) 84 | 85 | d, err := yaml.Marshal(&t) 86 | if err != nil { 87 | log.Fatalf("error: %v", err) 88 | } 89 | fmt.Printf("--- t dump:\n%s\n\n", string(d)) 90 | 91 | m := make(map[interface{}]interface{}) 92 | 93 | err = yaml.Unmarshal([]byte(data), &m) 94 | if err != nil { 95 | log.Fatalf("error: %v", err) 96 | } 97 | fmt.Printf("--- m:\n%v\n\n", m) 98 | 99 | d, err = yaml.Marshal(&m) 100 | if err != nil { 101 | log.Fatalf("error: %v", err) 102 | } 103 | fmt.Printf("--- m dump:\n%s\n\n", string(d)) 104 | } 105 | ``` 106 | 107 | This example will generate the following output: 108 | 109 | ``` 110 | --- t: 111 | {Easy! {2 [3 4]}} 112 | 113 | --- t dump: 114 | a: Easy! 115 | b: 116 | c: 2 117 | d: [3, 4] 118 | 119 | 120 | --- m: 121 | map[a:Easy! b:map[c:2 d:[3 4]]] 122 | 123 | --- m dump: 124 | a: Easy! 125 | b: 126 | c: 2 127 | d: 128 | - 3 129 | - 4 130 | ``` 131 | 132 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/apic.go: -------------------------------------------------------------------------------- 1 | package yaml 2 | 3 | import ( 4 | "io" 5 | "os" 6 | ) 7 | 8 | func yaml_insert_token(parser *yaml_parser_t, pos int, token *yaml_token_t) { 9 | //fmt.Println("yaml_insert_token", "pos:", pos, "typ:", token.typ, "head:", parser.tokens_head, "len:", len(parser.tokens)) 10 | 11 | // Check if we can move the queue at the beginning of the buffer. 12 | if parser.tokens_head > 0 && len(parser.tokens) == cap(parser.tokens) { 13 | if parser.tokens_head != len(parser.tokens) { 14 | copy(parser.tokens, parser.tokens[parser.tokens_head:]) 15 | } 16 | parser.tokens = parser.tokens[:len(parser.tokens)-parser.tokens_head] 17 | parser.tokens_head = 0 18 | } 19 | parser.tokens = append(parser.tokens, *token) 20 | if pos < 0 { 21 | return 22 | } 23 | copy(parser.tokens[parser.tokens_head+pos+1:], parser.tokens[parser.tokens_head+pos:]) 24 | parser.tokens[parser.tokens_head+pos] = *token 25 | } 26 | 27 | // Create a new parser object. 28 | func yaml_parser_initialize(parser *yaml_parser_t) bool { 29 | *parser = yaml_parser_t{ 30 | raw_buffer: make([]byte, 0, input_raw_buffer_size), 31 | buffer: make([]byte, 0, input_buffer_size), 32 | } 33 | return true 34 | } 35 | 36 | // Destroy a parser object. 37 | func yaml_parser_delete(parser *yaml_parser_t) { 38 | *parser = yaml_parser_t{} 39 | } 40 | 41 | // String read handler. 42 | func yaml_string_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) { 43 | if parser.input_pos == len(parser.input) { 44 | return 0, io.EOF 45 | } 46 | n = copy(buffer, parser.input[parser.input_pos:]) 47 | parser.input_pos += n 48 | return n, nil 49 | } 50 | 51 | // File read handler. 52 | func yaml_file_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) { 53 | return parser.input_file.Read(buffer) 54 | } 55 | 56 | // Set a string input. 57 | func yaml_parser_set_input_string(parser *yaml_parser_t, input []byte) { 58 | if parser.read_handler != nil { 59 | panic("must set the input source only once") 60 | } 61 | parser.read_handler = yaml_string_read_handler 62 | parser.input = input 63 | parser.input_pos = 0 64 | } 65 | 66 | // Set a file input. 67 | func yaml_parser_set_input_file(parser *yaml_parser_t, file *os.File) { 68 | if parser.read_handler != nil { 69 | panic("must set the input source only once") 70 | } 71 | parser.read_handler = yaml_file_read_handler 72 | parser.input_file = file 73 | } 74 | 75 | // Set the source encoding. 76 | func yaml_parser_set_encoding(parser *yaml_parser_t, encoding yaml_encoding_t) { 77 | if parser.encoding != yaml_ANY_ENCODING { 78 | panic("must set the encoding only once") 79 | } 80 | parser.encoding = encoding 81 | } 82 | 83 | // Create a new emitter object. 84 | func yaml_emitter_initialize(emitter *yaml_emitter_t) bool { 85 | *emitter = yaml_emitter_t{ 86 | buffer: make([]byte, output_buffer_size), 87 | raw_buffer: make([]byte, 0, output_raw_buffer_size), 88 | states: make([]yaml_emitter_state_t, 0, initial_stack_size), 89 | events: make([]yaml_event_t, 0, initial_queue_size), 90 | } 91 | return true 92 | } 93 | 94 | // Destroy an emitter object. 95 | func yaml_emitter_delete(emitter *yaml_emitter_t) { 96 | *emitter = yaml_emitter_t{} 97 | } 98 | 99 | // String write handler. 100 | func yaml_string_write_handler(emitter *yaml_emitter_t, buffer []byte) error { 101 | *emitter.output_buffer = append(*emitter.output_buffer, buffer...) 102 | return nil 103 | } 104 | 105 | // File write handler. 106 | func yaml_file_write_handler(emitter *yaml_emitter_t, buffer []byte) error { 107 | _, err := emitter.output_file.Write(buffer) 108 | return err 109 | } 110 | 111 | // Set a string output. 112 | func yaml_emitter_set_output_string(emitter *yaml_emitter_t, output_buffer *[]byte) { 113 | if emitter.write_handler != nil { 114 | panic("must set the output target only once") 115 | } 116 | emitter.write_handler = yaml_string_write_handler 117 | emitter.output_buffer = output_buffer 118 | } 119 | 120 | // Set a file output. 121 | func yaml_emitter_set_output_file(emitter *yaml_emitter_t, file io.Writer) { 122 | if emitter.write_handler != nil { 123 | panic("must set the output target only once") 124 | } 125 | emitter.write_handler = yaml_file_write_handler 126 | emitter.output_file = file 127 | } 128 | 129 | // Set the output encoding. 130 | func yaml_emitter_set_encoding(emitter *yaml_emitter_t, encoding yaml_encoding_t) { 131 | if emitter.encoding != yaml_ANY_ENCODING { 132 | panic("must set the output encoding only once") 133 | } 134 | emitter.encoding = encoding 135 | } 136 | 137 | // Set the canonical output style. 138 | func yaml_emitter_set_canonical(emitter *yaml_emitter_t, canonical bool) { 139 | emitter.canonical = canonical 140 | } 141 | 142 | //// Set the indentation increment. 143 | func yaml_emitter_set_indent(emitter *yaml_emitter_t, indent int) { 144 | if indent < 2 || indent > 9 { 145 | indent = 2 146 | } 147 | emitter.best_indent = indent 148 | } 149 | 150 | // Set the preferred line width. 151 | func yaml_emitter_set_width(emitter *yaml_emitter_t, width int) { 152 | if width < 0 { 153 | width = -1 154 | } 155 | emitter.best_width = width 156 | } 157 | 158 | // Set if unescaped non-ASCII characters are allowed. 159 | func yaml_emitter_set_unicode(emitter *yaml_emitter_t, unicode bool) { 160 | emitter.unicode = unicode 161 | } 162 | 163 | // Set the preferred line break character. 164 | func yaml_emitter_set_break(emitter *yaml_emitter_t, line_break yaml_break_t) { 165 | emitter.line_break = line_break 166 | } 167 | 168 | ///* 169 | // * Destroy a token object. 170 | // */ 171 | // 172 | //YAML_DECLARE(void) 173 | //yaml_token_delete(yaml_token_t *token) 174 | //{ 175 | // assert(token); // Non-NULL token object expected. 176 | // 177 | // switch (token.type) 178 | // { 179 | // case YAML_TAG_DIRECTIVE_TOKEN: 180 | // yaml_free(token.data.tag_directive.handle); 181 | // yaml_free(token.data.tag_directive.prefix); 182 | // break; 183 | // 184 | // case YAML_ALIAS_TOKEN: 185 | // yaml_free(token.data.alias.value); 186 | // break; 187 | // 188 | // case YAML_ANCHOR_TOKEN: 189 | // yaml_free(token.data.anchor.value); 190 | // break; 191 | // 192 | // case YAML_TAG_TOKEN: 193 | // yaml_free(token.data.tag.handle); 194 | // yaml_free(token.data.tag.suffix); 195 | // break; 196 | // 197 | // case YAML_SCALAR_TOKEN: 198 | // yaml_free(token.data.scalar.value); 199 | // break; 200 | // 201 | // default: 202 | // break; 203 | // } 204 | // 205 | // memset(token, 0, sizeof(yaml_token_t)); 206 | //} 207 | // 208 | ///* 209 | // * Check if a string is a valid UTF-8 sequence. 210 | // * 211 | // * Check 'reader.c' for more details on UTF-8 encoding. 212 | // */ 213 | // 214 | //static int 215 | //yaml_check_utf8(yaml_char_t *start, size_t length) 216 | //{ 217 | // yaml_char_t *end = start+length; 218 | // yaml_char_t *pointer = start; 219 | // 220 | // while (pointer < end) { 221 | // unsigned char octet; 222 | // unsigned int width; 223 | // unsigned int value; 224 | // size_t k; 225 | // 226 | // octet = pointer[0]; 227 | // width = (octet & 0x80) == 0x00 ? 1 : 228 | // (octet & 0xE0) == 0xC0 ? 2 : 229 | // (octet & 0xF0) == 0xE0 ? 3 : 230 | // (octet & 0xF8) == 0xF0 ? 4 : 0; 231 | // value = (octet & 0x80) == 0x00 ? octet & 0x7F : 232 | // (octet & 0xE0) == 0xC0 ? octet & 0x1F : 233 | // (octet & 0xF0) == 0xE0 ? octet & 0x0F : 234 | // (octet & 0xF8) == 0xF0 ? octet & 0x07 : 0; 235 | // if (!width) return 0; 236 | // if (pointer+width > end) return 0; 237 | // for (k = 1; k < width; k ++) { 238 | // octet = pointer[k]; 239 | // if ((octet & 0xC0) != 0x80) return 0; 240 | // value = (value << 6) + (octet & 0x3F); 241 | // } 242 | // if (!((width == 1) || 243 | // (width == 2 && value >= 0x80) || 244 | // (width == 3 && value >= 0x800) || 245 | // (width == 4 && value >= 0x10000))) return 0; 246 | // 247 | // pointer += width; 248 | // } 249 | // 250 | // return 1; 251 | //} 252 | // 253 | 254 | // Create STREAM-START. 255 | func yaml_stream_start_event_initialize(event *yaml_event_t, encoding yaml_encoding_t) bool { 256 | *event = yaml_event_t{ 257 | typ: yaml_STREAM_START_EVENT, 258 | encoding: encoding, 259 | } 260 | return true 261 | } 262 | 263 | // Create STREAM-END. 264 | func yaml_stream_end_event_initialize(event *yaml_event_t) bool { 265 | *event = yaml_event_t{ 266 | typ: yaml_STREAM_END_EVENT, 267 | } 268 | return true 269 | } 270 | 271 | // Create DOCUMENT-START. 272 | func yaml_document_start_event_initialize(event *yaml_event_t, version_directive *yaml_version_directive_t, 273 | tag_directives []yaml_tag_directive_t, implicit bool) bool { 274 | *event = yaml_event_t{ 275 | typ: yaml_DOCUMENT_START_EVENT, 276 | version_directive: version_directive, 277 | tag_directives: tag_directives, 278 | implicit: implicit, 279 | } 280 | return true 281 | } 282 | 283 | // Create DOCUMENT-END. 284 | func yaml_document_end_event_initialize(event *yaml_event_t, implicit bool) bool { 285 | *event = yaml_event_t{ 286 | typ: yaml_DOCUMENT_END_EVENT, 287 | implicit: implicit, 288 | } 289 | return true 290 | } 291 | 292 | ///* 293 | // * Create ALIAS. 294 | // */ 295 | // 296 | //YAML_DECLARE(int) 297 | //yaml_alias_event_initialize(event *yaml_event_t, anchor *yaml_char_t) 298 | //{ 299 | // mark yaml_mark_t = { 0, 0, 0 } 300 | // anchor_copy *yaml_char_t = NULL 301 | // 302 | // assert(event) // Non-NULL event object is expected. 303 | // assert(anchor) // Non-NULL anchor is expected. 304 | // 305 | // if (!yaml_check_utf8(anchor, strlen((char *)anchor))) return 0 306 | // 307 | // anchor_copy = yaml_strdup(anchor) 308 | // if (!anchor_copy) 309 | // return 0 310 | // 311 | // ALIAS_EVENT_INIT(*event, anchor_copy, mark, mark) 312 | // 313 | // return 1 314 | //} 315 | 316 | // Create SCALAR. 317 | func yaml_scalar_event_initialize(event *yaml_event_t, anchor, tag, value []byte, plain_implicit, quoted_implicit bool, style yaml_scalar_style_t) bool { 318 | *event = yaml_event_t{ 319 | typ: yaml_SCALAR_EVENT, 320 | anchor: anchor, 321 | tag: tag, 322 | value: value, 323 | implicit: plain_implicit, 324 | quoted_implicit: quoted_implicit, 325 | style: yaml_style_t(style), 326 | } 327 | return true 328 | } 329 | 330 | // Create SEQUENCE-START. 331 | func yaml_sequence_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_sequence_style_t) bool { 332 | *event = yaml_event_t{ 333 | typ: yaml_SEQUENCE_START_EVENT, 334 | anchor: anchor, 335 | tag: tag, 336 | implicit: implicit, 337 | style: yaml_style_t(style), 338 | } 339 | return true 340 | } 341 | 342 | // Create SEQUENCE-END. 343 | func yaml_sequence_end_event_initialize(event *yaml_event_t) bool { 344 | *event = yaml_event_t{ 345 | typ: yaml_SEQUENCE_END_EVENT, 346 | } 347 | return true 348 | } 349 | 350 | // Create MAPPING-START. 351 | func yaml_mapping_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_mapping_style_t) bool { 352 | *event = yaml_event_t{ 353 | typ: yaml_MAPPING_START_EVENT, 354 | anchor: anchor, 355 | tag: tag, 356 | implicit: implicit, 357 | style: yaml_style_t(style), 358 | } 359 | return true 360 | } 361 | 362 | // Create MAPPING-END. 363 | func yaml_mapping_end_event_initialize(event *yaml_event_t) bool { 364 | *event = yaml_event_t{ 365 | typ: yaml_MAPPING_END_EVENT, 366 | } 367 | return true 368 | } 369 | 370 | // Destroy an event object. 371 | func yaml_event_delete(event *yaml_event_t) { 372 | *event = yaml_event_t{} 373 | } 374 | 375 | ///* 376 | // * Create a document object. 377 | // */ 378 | // 379 | //YAML_DECLARE(int) 380 | //yaml_document_initialize(document *yaml_document_t, 381 | // version_directive *yaml_version_directive_t, 382 | // tag_directives_start *yaml_tag_directive_t, 383 | // tag_directives_end *yaml_tag_directive_t, 384 | // start_implicit int, end_implicit int) 385 | //{ 386 | // struct { 387 | // error yaml_error_type_t 388 | // } context 389 | // struct { 390 | // start *yaml_node_t 391 | // end *yaml_node_t 392 | // top *yaml_node_t 393 | // } nodes = { NULL, NULL, NULL } 394 | // version_directive_copy *yaml_version_directive_t = NULL 395 | // struct { 396 | // start *yaml_tag_directive_t 397 | // end *yaml_tag_directive_t 398 | // top *yaml_tag_directive_t 399 | // } tag_directives_copy = { NULL, NULL, NULL } 400 | // value yaml_tag_directive_t = { NULL, NULL } 401 | // mark yaml_mark_t = { 0, 0, 0 } 402 | // 403 | // assert(document) // Non-NULL document object is expected. 404 | // assert((tag_directives_start && tag_directives_end) || 405 | // (tag_directives_start == tag_directives_end)) 406 | // // Valid tag directives are expected. 407 | // 408 | // if (!STACK_INIT(&context, nodes, INITIAL_STACK_SIZE)) goto error 409 | // 410 | // if (version_directive) { 411 | // version_directive_copy = yaml_malloc(sizeof(yaml_version_directive_t)) 412 | // if (!version_directive_copy) goto error 413 | // version_directive_copy.major = version_directive.major 414 | // version_directive_copy.minor = version_directive.minor 415 | // } 416 | // 417 | // if (tag_directives_start != tag_directives_end) { 418 | // tag_directive *yaml_tag_directive_t 419 | // if (!STACK_INIT(&context, tag_directives_copy, INITIAL_STACK_SIZE)) 420 | // goto error 421 | // for (tag_directive = tag_directives_start 422 | // tag_directive != tag_directives_end; tag_directive ++) { 423 | // assert(tag_directive.handle) 424 | // assert(tag_directive.prefix) 425 | // if (!yaml_check_utf8(tag_directive.handle, 426 | // strlen((char *)tag_directive.handle))) 427 | // goto error 428 | // if (!yaml_check_utf8(tag_directive.prefix, 429 | // strlen((char *)tag_directive.prefix))) 430 | // goto error 431 | // value.handle = yaml_strdup(tag_directive.handle) 432 | // value.prefix = yaml_strdup(tag_directive.prefix) 433 | // if (!value.handle || !value.prefix) goto error 434 | // if (!PUSH(&context, tag_directives_copy, value)) 435 | // goto error 436 | // value.handle = NULL 437 | // value.prefix = NULL 438 | // } 439 | // } 440 | // 441 | // DOCUMENT_INIT(*document, nodes.start, nodes.end, version_directive_copy, 442 | // tag_directives_copy.start, tag_directives_copy.top, 443 | // start_implicit, end_implicit, mark, mark) 444 | // 445 | // return 1 446 | // 447 | //error: 448 | // STACK_DEL(&context, nodes) 449 | // yaml_free(version_directive_copy) 450 | // while (!STACK_EMPTY(&context, tag_directives_copy)) { 451 | // value yaml_tag_directive_t = POP(&context, tag_directives_copy) 452 | // yaml_free(value.handle) 453 | // yaml_free(value.prefix) 454 | // } 455 | // STACK_DEL(&context, tag_directives_copy) 456 | // yaml_free(value.handle) 457 | // yaml_free(value.prefix) 458 | // 459 | // return 0 460 | //} 461 | // 462 | ///* 463 | // * Destroy a document object. 464 | // */ 465 | // 466 | //YAML_DECLARE(void) 467 | //yaml_document_delete(document *yaml_document_t) 468 | //{ 469 | // struct { 470 | // error yaml_error_type_t 471 | // } context 472 | // tag_directive *yaml_tag_directive_t 473 | // 474 | // context.error = YAML_NO_ERROR // Eliminate a compliler warning. 475 | // 476 | // assert(document) // Non-NULL document object is expected. 477 | // 478 | // while (!STACK_EMPTY(&context, document.nodes)) { 479 | // node yaml_node_t = POP(&context, document.nodes) 480 | // yaml_free(node.tag) 481 | // switch (node.type) { 482 | // case YAML_SCALAR_NODE: 483 | // yaml_free(node.data.scalar.value) 484 | // break 485 | // case YAML_SEQUENCE_NODE: 486 | // STACK_DEL(&context, node.data.sequence.items) 487 | // break 488 | // case YAML_MAPPING_NODE: 489 | // STACK_DEL(&context, node.data.mapping.pairs) 490 | // break 491 | // default: 492 | // assert(0) // Should not happen. 493 | // } 494 | // } 495 | // STACK_DEL(&context, document.nodes) 496 | // 497 | // yaml_free(document.version_directive) 498 | // for (tag_directive = document.tag_directives.start 499 | // tag_directive != document.tag_directives.end 500 | // tag_directive++) { 501 | // yaml_free(tag_directive.handle) 502 | // yaml_free(tag_directive.prefix) 503 | // } 504 | // yaml_free(document.tag_directives.start) 505 | // 506 | // memset(document, 0, sizeof(yaml_document_t)) 507 | //} 508 | // 509 | ///** 510 | // * Get a document node. 511 | // */ 512 | // 513 | //YAML_DECLARE(yaml_node_t *) 514 | //yaml_document_get_node(document *yaml_document_t, index int) 515 | //{ 516 | // assert(document) // Non-NULL document object is expected. 517 | // 518 | // if (index > 0 && document.nodes.start + index <= document.nodes.top) { 519 | // return document.nodes.start + index - 1 520 | // } 521 | // return NULL 522 | //} 523 | // 524 | ///** 525 | // * Get the root object. 526 | // */ 527 | // 528 | //YAML_DECLARE(yaml_node_t *) 529 | //yaml_document_get_root_node(document *yaml_document_t) 530 | //{ 531 | // assert(document) // Non-NULL document object is expected. 532 | // 533 | // if (document.nodes.top != document.nodes.start) { 534 | // return document.nodes.start 535 | // } 536 | // return NULL 537 | //} 538 | // 539 | ///* 540 | // * Add a scalar node to a document. 541 | // */ 542 | // 543 | //YAML_DECLARE(int) 544 | //yaml_document_add_scalar(document *yaml_document_t, 545 | // tag *yaml_char_t, value *yaml_char_t, length int, 546 | // style yaml_scalar_style_t) 547 | //{ 548 | // struct { 549 | // error yaml_error_type_t 550 | // } context 551 | // mark yaml_mark_t = { 0, 0, 0 } 552 | // tag_copy *yaml_char_t = NULL 553 | // value_copy *yaml_char_t = NULL 554 | // node yaml_node_t 555 | // 556 | // assert(document) // Non-NULL document object is expected. 557 | // assert(value) // Non-NULL value is expected. 558 | // 559 | // if (!tag) { 560 | // tag = (yaml_char_t *)YAML_DEFAULT_SCALAR_TAG 561 | // } 562 | // 563 | // if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error 564 | // tag_copy = yaml_strdup(tag) 565 | // if (!tag_copy) goto error 566 | // 567 | // if (length < 0) { 568 | // length = strlen((char *)value) 569 | // } 570 | // 571 | // if (!yaml_check_utf8(value, length)) goto error 572 | // value_copy = yaml_malloc(length+1) 573 | // if (!value_copy) goto error 574 | // memcpy(value_copy, value, length) 575 | // value_copy[length] = '\0' 576 | // 577 | // SCALAR_NODE_INIT(node, tag_copy, value_copy, length, style, mark, mark) 578 | // if (!PUSH(&context, document.nodes, node)) goto error 579 | // 580 | // return document.nodes.top - document.nodes.start 581 | // 582 | //error: 583 | // yaml_free(tag_copy) 584 | // yaml_free(value_copy) 585 | // 586 | // return 0 587 | //} 588 | // 589 | ///* 590 | // * Add a sequence node to a document. 591 | // */ 592 | // 593 | //YAML_DECLARE(int) 594 | //yaml_document_add_sequence(document *yaml_document_t, 595 | // tag *yaml_char_t, style yaml_sequence_style_t) 596 | //{ 597 | // struct { 598 | // error yaml_error_type_t 599 | // } context 600 | // mark yaml_mark_t = { 0, 0, 0 } 601 | // tag_copy *yaml_char_t = NULL 602 | // struct { 603 | // start *yaml_node_item_t 604 | // end *yaml_node_item_t 605 | // top *yaml_node_item_t 606 | // } items = { NULL, NULL, NULL } 607 | // node yaml_node_t 608 | // 609 | // assert(document) // Non-NULL document object is expected. 610 | // 611 | // if (!tag) { 612 | // tag = (yaml_char_t *)YAML_DEFAULT_SEQUENCE_TAG 613 | // } 614 | // 615 | // if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error 616 | // tag_copy = yaml_strdup(tag) 617 | // if (!tag_copy) goto error 618 | // 619 | // if (!STACK_INIT(&context, items, INITIAL_STACK_SIZE)) goto error 620 | // 621 | // SEQUENCE_NODE_INIT(node, tag_copy, items.start, items.end, 622 | // style, mark, mark) 623 | // if (!PUSH(&context, document.nodes, node)) goto error 624 | // 625 | // return document.nodes.top - document.nodes.start 626 | // 627 | //error: 628 | // STACK_DEL(&context, items) 629 | // yaml_free(tag_copy) 630 | // 631 | // return 0 632 | //} 633 | // 634 | ///* 635 | // * Add a mapping node to a document. 636 | // */ 637 | // 638 | //YAML_DECLARE(int) 639 | //yaml_document_add_mapping(document *yaml_document_t, 640 | // tag *yaml_char_t, style yaml_mapping_style_t) 641 | //{ 642 | // struct { 643 | // error yaml_error_type_t 644 | // } context 645 | // mark yaml_mark_t = { 0, 0, 0 } 646 | // tag_copy *yaml_char_t = NULL 647 | // struct { 648 | // start *yaml_node_pair_t 649 | // end *yaml_node_pair_t 650 | // top *yaml_node_pair_t 651 | // } pairs = { NULL, NULL, NULL } 652 | // node yaml_node_t 653 | // 654 | // assert(document) // Non-NULL document object is expected. 655 | // 656 | // if (!tag) { 657 | // tag = (yaml_char_t *)YAML_DEFAULT_MAPPING_TAG 658 | // } 659 | // 660 | // if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error 661 | // tag_copy = yaml_strdup(tag) 662 | // if (!tag_copy) goto error 663 | // 664 | // if (!STACK_INIT(&context, pairs, INITIAL_STACK_SIZE)) goto error 665 | // 666 | // MAPPING_NODE_INIT(node, tag_copy, pairs.start, pairs.end, 667 | // style, mark, mark) 668 | // if (!PUSH(&context, document.nodes, node)) goto error 669 | // 670 | // return document.nodes.top - document.nodes.start 671 | // 672 | //error: 673 | // STACK_DEL(&context, pairs) 674 | // yaml_free(tag_copy) 675 | // 676 | // return 0 677 | //} 678 | // 679 | ///* 680 | // * Append an item to a sequence node. 681 | // */ 682 | // 683 | //YAML_DECLARE(int) 684 | //yaml_document_append_sequence_item(document *yaml_document_t, 685 | // sequence int, item int) 686 | //{ 687 | // struct { 688 | // error yaml_error_type_t 689 | // } context 690 | // 691 | // assert(document) // Non-NULL document is required. 692 | // assert(sequence > 0 693 | // && document.nodes.start + sequence <= document.nodes.top) 694 | // // Valid sequence id is required. 695 | // assert(document.nodes.start[sequence-1].type == YAML_SEQUENCE_NODE) 696 | // // A sequence node is required. 697 | // assert(item > 0 && document.nodes.start + item <= document.nodes.top) 698 | // // Valid item id is required. 699 | // 700 | // if (!PUSH(&context, 701 | // document.nodes.start[sequence-1].data.sequence.items, item)) 702 | // return 0 703 | // 704 | // return 1 705 | //} 706 | // 707 | ///* 708 | // * Append a pair of a key and a value to a mapping node. 709 | // */ 710 | // 711 | //YAML_DECLARE(int) 712 | //yaml_document_append_mapping_pair(document *yaml_document_t, 713 | // mapping int, key int, value int) 714 | //{ 715 | // struct { 716 | // error yaml_error_type_t 717 | // } context 718 | // 719 | // pair yaml_node_pair_t 720 | // 721 | // assert(document) // Non-NULL document is required. 722 | // assert(mapping > 0 723 | // && document.nodes.start + mapping <= document.nodes.top) 724 | // // Valid mapping id is required. 725 | // assert(document.nodes.start[mapping-1].type == YAML_MAPPING_NODE) 726 | // // A mapping node is required. 727 | // assert(key > 0 && document.nodes.start + key <= document.nodes.top) 728 | // // Valid key id is required. 729 | // assert(value > 0 && document.nodes.start + value <= document.nodes.top) 730 | // // Valid value id is required. 731 | // 732 | // pair.key = key 733 | // pair.value = value 734 | // 735 | // if (!PUSH(&context, 736 | // document.nodes.start[mapping-1].data.mapping.pairs, pair)) 737 | // return 0 738 | // 739 | // return 1 740 | //} 741 | // 742 | // 743 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/decode.go: -------------------------------------------------------------------------------- 1 | package yaml 2 | 3 | import ( 4 | "encoding" 5 | "encoding/base64" 6 | "fmt" 7 | "math" 8 | "reflect" 9 | "strconv" 10 | "time" 11 | ) 12 | 13 | const ( 14 | documentNode = 1 << iota 15 | mappingNode 16 | sequenceNode 17 | scalarNode 18 | aliasNode 19 | ) 20 | 21 | type node struct { 22 | kind int 23 | line, column int 24 | tag string 25 | value string 26 | implicit bool 27 | children []*node 28 | anchors map[string]*node 29 | } 30 | 31 | // ---------------------------------------------------------------------------- 32 | // Parser, produces a node tree out of a libyaml event stream. 33 | 34 | type parser struct { 35 | parser yaml_parser_t 36 | event yaml_event_t 37 | doc *node 38 | } 39 | 40 | func newParser(b []byte) *parser { 41 | p := parser{} 42 | if !yaml_parser_initialize(&p.parser) { 43 | panic("failed to initialize YAML emitter") 44 | } 45 | 46 | if len(b) == 0 { 47 | b = []byte{'\n'} 48 | } 49 | 50 | yaml_parser_set_input_string(&p.parser, b) 51 | 52 | p.skip() 53 | if p.event.typ != yaml_STREAM_START_EVENT { 54 | panic("expected stream start event, got " + strconv.Itoa(int(p.event.typ))) 55 | } 56 | p.skip() 57 | return &p 58 | } 59 | 60 | func (p *parser) destroy() { 61 | if p.event.typ != yaml_NO_EVENT { 62 | yaml_event_delete(&p.event) 63 | } 64 | yaml_parser_delete(&p.parser) 65 | } 66 | 67 | func (p *parser) skip() { 68 | if p.event.typ != yaml_NO_EVENT { 69 | if p.event.typ == yaml_STREAM_END_EVENT { 70 | failf("attempted to go past the end of stream; corrupted value?") 71 | } 72 | yaml_event_delete(&p.event) 73 | } 74 | if !yaml_parser_parse(&p.parser, &p.event) { 75 | p.fail() 76 | } 77 | } 78 | 79 | func (p *parser) fail() { 80 | var where string 81 | var line int 82 | if p.parser.problem_mark.line != 0 { 83 | line = p.parser.problem_mark.line 84 | } else if p.parser.context_mark.line != 0 { 85 | line = p.parser.context_mark.line 86 | } 87 | if line != 0 { 88 | where = "line " + strconv.Itoa(line) + ": " 89 | } 90 | var msg string 91 | if len(p.parser.problem) > 0 { 92 | msg = p.parser.problem 93 | } else { 94 | msg = "unknown problem parsing YAML content" 95 | } 96 | failf("%s%s", where, msg) 97 | } 98 | 99 | func (p *parser) anchor(n *node, anchor []byte) { 100 | if anchor != nil { 101 | p.doc.anchors[string(anchor)] = n 102 | } 103 | } 104 | 105 | func (p *parser) parse() *node { 106 | switch p.event.typ { 107 | case yaml_SCALAR_EVENT: 108 | return p.scalar() 109 | case yaml_ALIAS_EVENT: 110 | return p.alias() 111 | case yaml_MAPPING_START_EVENT: 112 | return p.mapping() 113 | case yaml_SEQUENCE_START_EVENT: 114 | return p.sequence() 115 | case yaml_DOCUMENT_START_EVENT: 116 | return p.document() 117 | case yaml_STREAM_END_EVENT: 118 | // Happens when attempting to decode an empty buffer. 119 | return nil 120 | default: 121 | panic("attempted to parse unknown event: " + strconv.Itoa(int(p.event.typ))) 122 | } 123 | panic("unreachable") 124 | } 125 | 126 | func (p *parser) node(kind int) *node { 127 | return &node{ 128 | kind: kind, 129 | line: p.event.start_mark.line, 130 | column: p.event.start_mark.column, 131 | } 132 | } 133 | 134 | func (p *parser) document() *node { 135 | n := p.node(documentNode) 136 | n.anchors = make(map[string]*node) 137 | p.doc = n 138 | p.skip() 139 | n.children = append(n.children, p.parse()) 140 | if p.event.typ != yaml_DOCUMENT_END_EVENT { 141 | panic("expected end of document event but got " + strconv.Itoa(int(p.event.typ))) 142 | } 143 | p.skip() 144 | return n 145 | } 146 | 147 | func (p *parser) alias() *node { 148 | n := p.node(aliasNode) 149 | n.value = string(p.event.anchor) 150 | p.skip() 151 | return n 152 | } 153 | 154 | func (p *parser) scalar() *node { 155 | n := p.node(scalarNode) 156 | n.value = string(p.event.value) 157 | n.tag = string(p.event.tag) 158 | n.implicit = p.event.implicit 159 | p.anchor(n, p.event.anchor) 160 | p.skip() 161 | return n 162 | } 163 | 164 | func (p *parser) sequence() *node { 165 | n := p.node(sequenceNode) 166 | p.anchor(n, p.event.anchor) 167 | p.skip() 168 | for p.event.typ != yaml_SEQUENCE_END_EVENT { 169 | n.children = append(n.children, p.parse()) 170 | } 171 | p.skip() 172 | return n 173 | } 174 | 175 | func (p *parser) mapping() *node { 176 | n := p.node(mappingNode) 177 | p.anchor(n, p.event.anchor) 178 | p.skip() 179 | for p.event.typ != yaml_MAPPING_END_EVENT { 180 | n.children = append(n.children, p.parse(), p.parse()) 181 | } 182 | p.skip() 183 | return n 184 | } 185 | 186 | // ---------------------------------------------------------------------------- 187 | // Decoder, unmarshals a node into a provided value. 188 | 189 | type decoder struct { 190 | doc *node 191 | aliases map[string]bool 192 | mapType reflect.Type 193 | terrors []string 194 | } 195 | 196 | var ( 197 | mapItemType = reflect.TypeOf(MapItem{}) 198 | durationType = reflect.TypeOf(time.Duration(0)) 199 | defaultMapType = reflect.TypeOf(map[interface{}]interface{}{}) 200 | ifaceType = defaultMapType.Elem() 201 | ) 202 | 203 | func newDecoder() *decoder { 204 | d := &decoder{mapType: defaultMapType} 205 | d.aliases = make(map[string]bool) 206 | return d 207 | } 208 | 209 | func (d *decoder) terror(n *node, tag string, out reflect.Value) { 210 | if n.tag != "" { 211 | tag = n.tag 212 | } 213 | value := n.value 214 | if tag != yaml_SEQ_TAG && tag != yaml_MAP_TAG { 215 | if len(value) > 10 { 216 | value = " `" + value[:7] + "...`" 217 | } else { 218 | value = " `" + value + "`" 219 | } 220 | } 221 | d.terrors = append(d.terrors, fmt.Sprintf("line %d: cannot unmarshal %s%s into %s", n.line+1, shortTag(tag), value, out.Type())) 222 | } 223 | 224 | func (d *decoder) callUnmarshaler(n *node, u Unmarshaler) (good bool) { 225 | terrlen := len(d.terrors) 226 | err := u.UnmarshalYAML(func(v interface{}) (err error) { 227 | defer handleErr(&err) 228 | d.unmarshal(n, reflect.ValueOf(v)) 229 | if len(d.terrors) > terrlen { 230 | issues := d.terrors[terrlen:] 231 | d.terrors = d.terrors[:terrlen] 232 | return &TypeError{issues} 233 | } 234 | return nil 235 | }) 236 | if e, ok := err.(*TypeError); ok { 237 | d.terrors = append(d.terrors, e.Errors...) 238 | return false 239 | } 240 | if err != nil { 241 | fail(err) 242 | } 243 | return true 244 | } 245 | 246 | // d.prepare initializes and dereferences pointers and calls UnmarshalYAML 247 | // if a value is found to implement it. 248 | // It returns the initialized and dereferenced out value, whether 249 | // unmarshalling was already done by UnmarshalYAML, and if so whether 250 | // its types unmarshalled appropriately. 251 | // 252 | // If n holds a null value, prepare returns before doing anything. 253 | func (d *decoder) prepare(n *node, out reflect.Value) (newout reflect.Value, unmarshaled, good bool) { 254 | if n.tag == yaml_NULL_TAG || n.kind == scalarNode && n.tag == "" && (n.value == "null" || n.value == "" && n.implicit) { 255 | return out, false, false 256 | } 257 | again := true 258 | for again { 259 | again = false 260 | if out.Kind() == reflect.Ptr { 261 | if out.IsNil() { 262 | out.Set(reflect.New(out.Type().Elem())) 263 | } 264 | out = out.Elem() 265 | again = true 266 | } 267 | if out.CanAddr() { 268 | if u, ok := out.Addr().Interface().(Unmarshaler); ok { 269 | good = d.callUnmarshaler(n, u) 270 | return out, true, good 271 | } 272 | } 273 | } 274 | return out, false, false 275 | } 276 | 277 | func (d *decoder) unmarshal(n *node, out reflect.Value) (good bool) { 278 | switch n.kind { 279 | case documentNode: 280 | return d.document(n, out) 281 | case aliasNode: 282 | return d.alias(n, out) 283 | } 284 | out, unmarshaled, good := d.prepare(n, out) 285 | if unmarshaled { 286 | return good 287 | } 288 | switch n.kind { 289 | case scalarNode: 290 | good = d.scalar(n, out) 291 | case mappingNode: 292 | good = d.mapping(n, out) 293 | case sequenceNode: 294 | good = d.sequence(n, out) 295 | default: 296 | panic("internal error: unknown node kind: " + strconv.Itoa(n.kind)) 297 | } 298 | return good 299 | } 300 | 301 | func (d *decoder) document(n *node, out reflect.Value) (good bool) { 302 | if len(n.children) == 1 { 303 | d.doc = n 304 | d.unmarshal(n.children[0], out) 305 | return true 306 | } 307 | return false 308 | } 309 | 310 | func (d *decoder) alias(n *node, out reflect.Value) (good bool) { 311 | an, ok := d.doc.anchors[n.value] 312 | if !ok { 313 | failf("unknown anchor '%s' referenced", n.value) 314 | } 315 | if d.aliases[n.value] { 316 | failf("anchor '%s' value contains itself", n.value) 317 | } 318 | d.aliases[n.value] = true 319 | good = d.unmarshal(an, out) 320 | delete(d.aliases, n.value) 321 | return good 322 | } 323 | 324 | var zeroValue reflect.Value 325 | 326 | func resetMap(out reflect.Value) { 327 | for _, k := range out.MapKeys() { 328 | out.SetMapIndex(k, zeroValue) 329 | } 330 | } 331 | 332 | func (d *decoder) scalar(n *node, out reflect.Value) (good bool) { 333 | var tag string 334 | var resolved interface{} 335 | if n.tag == "" && !n.implicit { 336 | tag = yaml_STR_TAG 337 | resolved = n.value 338 | } else { 339 | tag, resolved = resolve(n.tag, n.value) 340 | if tag == yaml_BINARY_TAG { 341 | data, err := base64.StdEncoding.DecodeString(resolved.(string)) 342 | if err != nil { 343 | failf("!!binary value contains invalid base64 data") 344 | } 345 | resolved = string(data) 346 | } 347 | } 348 | if resolved == nil { 349 | if out.Kind() == reflect.Map && !out.CanAddr() { 350 | resetMap(out) 351 | } else { 352 | out.Set(reflect.Zero(out.Type())) 353 | } 354 | return true 355 | } 356 | if s, ok := resolved.(string); ok && out.CanAddr() { 357 | if u, ok := out.Addr().Interface().(encoding.TextUnmarshaler); ok { 358 | err := u.UnmarshalText([]byte(s)) 359 | if err != nil { 360 | fail(err) 361 | } 362 | return true 363 | } 364 | } 365 | switch out.Kind() { 366 | case reflect.String: 367 | if tag == yaml_BINARY_TAG { 368 | out.SetString(resolved.(string)) 369 | good = true 370 | } else if resolved != nil { 371 | out.SetString(n.value) 372 | good = true 373 | } 374 | case reflect.Interface: 375 | if resolved == nil { 376 | out.Set(reflect.Zero(out.Type())) 377 | } else { 378 | out.Set(reflect.ValueOf(resolved)) 379 | } 380 | good = true 381 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 382 | switch resolved := resolved.(type) { 383 | case int: 384 | if !out.OverflowInt(int64(resolved)) { 385 | out.SetInt(int64(resolved)) 386 | good = true 387 | } 388 | case int64: 389 | if !out.OverflowInt(resolved) { 390 | out.SetInt(resolved) 391 | good = true 392 | } 393 | case uint64: 394 | if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { 395 | out.SetInt(int64(resolved)) 396 | good = true 397 | } 398 | case float64: 399 | if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { 400 | out.SetInt(int64(resolved)) 401 | good = true 402 | } 403 | case string: 404 | if out.Type() == durationType { 405 | d, err := time.ParseDuration(resolved) 406 | if err == nil { 407 | out.SetInt(int64(d)) 408 | good = true 409 | } 410 | } 411 | } 412 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 413 | switch resolved := resolved.(type) { 414 | case int: 415 | if resolved >= 0 && !out.OverflowUint(uint64(resolved)) { 416 | out.SetUint(uint64(resolved)) 417 | good = true 418 | } 419 | case int64: 420 | if resolved >= 0 && !out.OverflowUint(uint64(resolved)) { 421 | out.SetUint(uint64(resolved)) 422 | good = true 423 | } 424 | case uint64: 425 | if !out.OverflowUint(uint64(resolved)) { 426 | out.SetUint(uint64(resolved)) 427 | good = true 428 | } 429 | case float64: 430 | if resolved <= math.MaxUint64 && !out.OverflowUint(uint64(resolved)) { 431 | out.SetUint(uint64(resolved)) 432 | good = true 433 | } 434 | } 435 | case reflect.Bool: 436 | switch resolved := resolved.(type) { 437 | case bool: 438 | out.SetBool(resolved) 439 | good = true 440 | } 441 | case reflect.Float32, reflect.Float64: 442 | switch resolved := resolved.(type) { 443 | case int: 444 | out.SetFloat(float64(resolved)) 445 | good = true 446 | case int64: 447 | out.SetFloat(float64(resolved)) 448 | good = true 449 | case uint64: 450 | out.SetFloat(float64(resolved)) 451 | good = true 452 | case float64: 453 | out.SetFloat(resolved) 454 | good = true 455 | } 456 | case reflect.Ptr: 457 | if out.Type().Elem() == reflect.TypeOf(resolved) { 458 | // TODO DOes this make sense? When is out a Ptr except when decoding a nil value? 459 | elem := reflect.New(out.Type().Elem()) 460 | elem.Elem().Set(reflect.ValueOf(resolved)) 461 | out.Set(elem) 462 | good = true 463 | } 464 | } 465 | if !good { 466 | d.terror(n, tag, out) 467 | } 468 | return good 469 | } 470 | 471 | func settableValueOf(i interface{}) reflect.Value { 472 | v := reflect.ValueOf(i) 473 | sv := reflect.New(v.Type()).Elem() 474 | sv.Set(v) 475 | return sv 476 | } 477 | 478 | func (d *decoder) sequence(n *node, out reflect.Value) (good bool) { 479 | l := len(n.children) 480 | 481 | var iface reflect.Value 482 | switch out.Kind() { 483 | case reflect.Slice: 484 | out.Set(reflect.MakeSlice(out.Type(), l, l)) 485 | case reflect.Interface: 486 | // No type hints. Will have to use a generic sequence. 487 | iface = out 488 | out = settableValueOf(make([]interface{}, l)) 489 | default: 490 | d.terror(n, yaml_SEQ_TAG, out) 491 | return false 492 | } 493 | et := out.Type().Elem() 494 | 495 | j := 0 496 | for i := 0; i < l; i++ { 497 | e := reflect.New(et).Elem() 498 | if ok := d.unmarshal(n.children[i], e); ok { 499 | out.Index(j).Set(e) 500 | j++ 501 | } 502 | } 503 | out.Set(out.Slice(0, j)) 504 | if iface.IsValid() { 505 | iface.Set(out) 506 | } 507 | return true 508 | } 509 | 510 | func (d *decoder) mapping(n *node, out reflect.Value) (good bool) { 511 | switch out.Kind() { 512 | case reflect.Struct: 513 | return d.mappingStruct(n, out) 514 | case reflect.Slice: 515 | return d.mappingSlice(n, out) 516 | case reflect.Map: 517 | // okay 518 | case reflect.Interface: 519 | if d.mapType.Kind() == reflect.Map { 520 | iface := out 521 | out = reflect.MakeMap(d.mapType) 522 | iface.Set(out) 523 | } else { 524 | slicev := reflect.New(d.mapType).Elem() 525 | if !d.mappingSlice(n, slicev) { 526 | return false 527 | } 528 | out.Set(slicev) 529 | return true 530 | } 531 | default: 532 | d.terror(n, yaml_MAP_TAG, out) 533 | return false 534 | } 535 | outt := out.Type() 536 | kt := outt.Key() 537 | et := outt.Elem() 538 | 539 | mapType := d.mapType 540 | if outt.Key() == ifaceType && outt.Elem() == ifaceType { 541 | d.mapType = outt 542 | } 543 | 544 | if out.IsNil() { 545 | out.Set(reflect.MakeMap(outt)) 546 | } 547 | l := len(n.children) 548 | for i := 0; i < l; i += 2 { 549 | if isMerge(n.children[i]) { 550 | d.merge(n.children[i+1], out) 551 | continue 552 | } 553 | k := reflect.New(kt).Elem() 554 | if d.unmarshal(n.children[i], k) { 555 | kkind := k.Kind() 556 | if kkind == reflect.Interface { 557 | kkind = k.Elem().Kind() 558 | } 559 | if kkind == reflect.Map || kkind == reflect.Slice { 560 | failf("invalid map key: %#v", k.Interface()) 561 | } 562 | e := reflect.New(et).Elem() 563 | if d.unmarshal(n.children[i+1], e) { 564 | out.SetMapIndex(k, e) 565 | } 566 | } 567 | } 568 | d.mapType = mapType 569 | return true 570 | } 571 | 572 | func (d *decoder) mappingSlice(n *node, out reflect.Value) (good bool) { 573 | outt := out.Type() 574 | if outt.Elem() != mapItemType { 575 | d.terror(n, yaml_MAP_TAG, out) 576 | return false 577 | } 578 | 579 | mapType := d.mapType 580 | d.mapType = outt 581 | 582 | var slice []MapItem 583 | var l = len(n.children) 584 | for i := 0; i < l; i += 2 { 585 | if isMerge(n.children[i]) { 586 | d.merge(n.children[i+1], out) 587 | continue 588 | } 589 | item := MapItem{} 590 | k := reflect.ValueOf(&item.Key).Elem() 591 | if d.unmarshal(n.children[i], k) { 592 | v := reflect.ValueOf(&item.Value).Elem() 593 | if d.unmarshal(n.children[i+1], v) { 594 | slice = append(slice, item) 595 | } 596 | } 597 | } 598 | out.Set(reflect.ValueOf(slice)) 599 | d.mapType = mapType 600 | return true 601 | } 602 | 603 | func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) { 604 | sinfo, err := getStructInfo(out.Type()) 605 | if err != nil { 606 | panic(err) 607 | } 608 | name := settableValueOf("") 609 | l := len(n.children) 610 | 611 | var inlineMap reflect.Value 612 | var elemType reflect.Type 613 | if sinfo.InlineMap != -1 { 614 | inlineMap = out.Field(sinfo.InlineMap) 615 | inlineMap.Set(reflect.New(inlineMap.Type()).Elem()) 616 | elemType = inlineMap.Type().Elem() 617 | } 618 | 619 | for i := 0; i < l; i += 2 { 620 | ni := n.children[i] 621 | if isMerge(ni) { 622 | d.merge(n.children[i+1], out) 623 | continue 624 | } 625 | if !d.unmarshal(ni, name) { 626 | continue 627 | } 628 | if info, ok := sinfo.FieldsMap[name.String()]; ok { 629 | var field reflect.Value 630 | if info.Inline == nil { 631 | field = out.Field(info.Num) 632 | } else { 633 | field = out.FieldByIndex(info.Inline) 634 | } 635 | d.unmarshal(n.children[i+1], field) 636 | } else if sinfo.InlineMap != -1 { 637 | if inlineMap.IsNil() { 638 | inlineMap.Set(reflect.MakeMap(inlineMap.Type())) 639 | } 640 | value := reflect.New(elemType).Elem() 641 | d.unmarshal(n.children[i+1], value) 642 | inlineMap.SetMapIndex(name, value) 643 | } 644 | } 645 | return true 646 | } 647 | 648 | func failWantMap() { 649 | failf("map merge requires map or sequence of maps as the value") 650 | } 651 | 652 | func (d *decoder) merge(n *node, out reflect.Value) { 653 | switch n.kind { 654 | case mappingNode: 655 | d.unmarshal(n, out) 656 | case aliasNode: 657 | an, ok := d.doc.anchors[n.value] 658 | if ok && an.kind != mappingNode { 659 | failWantMap() 660 | } 661 | d.unmarshal(n, out) 662 | case sequenceNode: 663 | // Step backwards as earlier nodes take precedence. 664 | for i := len(n.children) - 1; i >= 0; i-- { 665 | ni := n.children[i] 666 | if ni.kind == aliasNode { 667 | an, ok := d.doc.anchors[ni.value] 668 | if ok && an.kind != mappingNode { 669 | failWantMap() 670 | } 671 | } else if ni.kind != mappingNode { 672 | failWantMap() 673 | } 674 | d.unmarshal(ni, out) 675 | } 676 | default: 677 | failWantMap() 678 | } 679 | } 680 | 681 | func isMerge(n *node) bool { 682 | return n.kind == scalarNode && n.value == "<<" && (n.implicit == true || n.tag == yaml_MERGE_TAG) 683 | } 684 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/encode.go: -------------------------------------------------------------------------------- 1 | package yaml 2 | 3 | import ( 4 | "encoding" 5 | "fmt" 6 | "reflect" 7 | "regexp" 8 | "sort" 9 | "strconv" 10 | "strings" 11 | "time" 12 | ) 13 | 14 | type encoder struct { 15 | emitter yaml_emitter_t 16 | event yaml_event_t 17 | out []byte 18 | flow bool 19 | } 20 | 21 | func newEncoder() (e *encoder) { 22 | e = &encoder{} 23 | e.must(yaml_emitter_initialize(&e.emitter)) 24 | yaml_emitter_set_output_string(&e.emitter, &e.out) 25 | yaml_emitter_set_unicode(&e.emitter, true) 26 | e.must(yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING)) 27 | e.emit() 28 | e.must(yaml_document_start_event_initialize(&e.event, nil, nil, true)) 29 | e.emit() 30 | return e 31 | } 32 | 33 | func (e *encoder) finish() { 34 | e.must(yaml_document_end_event_initialize(&e.event, true)) 35 | e.emit() 36 | e.emitter.open_ended = false 37 | e.must(yaml_stream_end_event_initialize(&e.event)) 38 | e.emit() 39 | } 40 | 41 | func (e *encoder) destroy() { 42 | yaml_emitter_delete(&e.emitter) 43 | } 44 | 45 | func (e *encoder) emit() { 46 | // This will internally delete the e.event value. 47 | if !yaml_emitter_emit(&e.emitter, &e.event) && e.event.typ != yaml_DOCUMENT_END_EVENT && e.event.typ != yaml_STREAM_END_EVENT { 48 | e.must(false) 49 | } 50 | } 51 | 52 | func (e *encoder) must(ok bool) { 53 | if !ok { 54 | msg := e.emitter.problem 55 | if msg == "" { 56 | msg = "unknown problem generating YAML content" 57 | } 58 | failf("%s", msg) 59 | } 60 | } 61 | 62 | func (e *encoder) marshal(tag string, in reflect.Value) { 63 | if !in.IsValid() { 64 | e.nilv() 65 | return 66 | } 67 | iface := in.Interface() 68 | if m, ok := iface.(Marshaler); ok { 69 | v, err := m.MarshalYAML() 70 | if err != nil { 71 | fail(err) 72 | } 73 | if v == nil { 74 | e.nilv() 75 | return 76 | } 77 | in = reflect.ValueOf(v) 78 | } else if m, ok := iface.(encoding.TextMarshaler); ok { 79 | text, err := m.MarshalText() 80 | if err != nil { 81 | fail(err) 82 | } 83 | in = reflect.ValueOf(string(text)) 84 | } 85 | switch in.Kind() { 86 | case reflect.Interface: 87 | if in.IsNil() { 88 | e.nilv() 89 | } else { 90 | e.marshal(tag, in.Elem()) 91 | } 92 | case reflect.Map: 93 | e.mapv(tag, in) 94 | case reflect.Ptr: 95 | if in.IsNil() { 96 | e.nilv() 97 | } else { 98 | e.marshal(tag, in.Elem()) 99 | } 100 | case reflect.Struct: 101 | e.structv(tag, in) 102 | case reflect.Slice: 103 | if in.Type().Elem() == mapItemType { 104 | e.itemsv(tag, in) 105 | } else { 106 | e.slicev(tag, in) 107 | } 108 | case reflect.String: 109 | e.stringv(tag, in) 110 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 111 | if in.Type() == durationType { 112 | e.stringv(tag, reflect.ValueOf(iface.(time.Duration).String())) 113 | } else { 114 | e.intv(tag, in) 115 | } 116 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 117 | e.uintv(tag, in) 118 | case reflect.Float32, reflect.Float64: 119 | e.floatv(tag, in) 120 | case reflect.Bool: 121 | e.boolv(tag, in) 122 | default: 123 | panic("cannot marshal type: " + in.Type().String()) 124 | } 125 | } 126 | 127 | func (e *encoder) mapv(tag string, in reflect.Value) { 128 | e.mappingv(tag, func() { 129 | keys := keyList(in.MapKeys()) 130 | sort.Sort(keys) 131 | for _, k := range keys { 132 | e.marshal("", k) 133 | e.marshal("", in.MapIndex(k)) 134 | } 135 | }) 136 | } 137 | 138 | func (e *encoder) itemsv(tag string, in reflect.Value) { 139 | e.mappingv(tag, func() { 140 | slice := in.Convert(reflect.TypeOf([]MapItem{})).Interface().([]MapItem) 141 | for _, item := range slice { 142 | e.marshal("", reflect.ValueOf(item.Key)) 143 | e.marshal("", reflect.ValueOf(item.Value)) 144 | } 145 | }) 146 | } 147 | 148 | func (e *encoder) structv(tag string, in reflect.Value) { 149 | sinfo, err := getStructInfo(in.Type()) 150 | if err != nil { 151 | panic(err) 152 | } 153 | e.mappingv(tag, func() { 154 | for _, info := range sinfo.FieldsList { 155 | var value reflect.Value 156 | if info.Inline == nil { 157 | value = in.Field(info.Num) 158 | } else { 159 | value = in.FieldByIndex(info.Inline) 160 | } 161 | if info.OmitEmpty && isZero(value) { 162 | continue 163 | } 164 | e.marshal("", reflect.ValueOf(info.Key)) 165 | e.flow = info.Flow 166 | e.marshal("", value) 167 | } 168 | if sinfo.InlineMap >= 0 { 169 | m := in.Field(sinfo.InlineMap) 170 | if m.Len() > 0 { 171 | e.flow = false 172 | keys := keyList(m.MapKeys()) 173 | sort.Sort(keys) 174 | for _, k := range keys { 175 | if _, found := sinfo.FieldsMap[k.String()]; found { 176 | panic(fmt.Sprintf("Can't have key %q in inlined map; conflicts with struct field", k.String())) 177 | } 178 | e.marshal("", k) 179 | e.flow = false 180 | e.marshal("", m.MapIndex(k)) 181 | } 182 | } 183 | } 184 | }) 185 | } 186 | 187 | func (e *encoder) mappingv(tag string, f func()) { 188 | implicit := tag == "" 189 | style := yaml_BLOCK_MAPPING_STYLE 190 | if e.flow { 191 | e.flow = false 192 | style = yaml_FLOW_MAPPING_STYLE 193 | } 194 | e.must(yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)) 195 | e.emit() 196 | f() 197 | e.must(yaml_mapping_end_event_initialize(&e.event)) 198 | e.emit() 199 | } 200 | 201 | func (e *encoder) slicev(tag string, in reflect.Value) { 202 | implicit := tag == "" 203 | style := yaml_BLOCK_SEQUENCE_STYLE 204 | if e.flow { 205 | e.flow = false 206 | style = yaml_FLOW_SEQUENCE_STYLE 207 | } 208 | e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)) 209 | e.emit() 210 | n := in.Len() 211 | for i := 0; i < n; i++ { 212 | e.marshal("", in.Index(i)) 213 | } 214 | e.must(yaml_sequence_end_event_initialize(&e.event)) 215 | e.emit() 216 | } 217 | 218 | // isBase60 returns whether s is in base 60 notation as defined in YAML 1.1. 219 | // 220 | // The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported 221 | // in YAML 1.2 and by this package, but these should be marshalled quoted for 222 | // the time being for compatibility with other parsers. 223 | func isBase60Float(s string) (result bool) { 224 | // Fast path. 225 | if s == "" { 226 | return false 227 | } 228 | c := s[0] 229 | if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 { 230 | return false 231 | } 232 | // Do the full match. 233 | return base60float.MatchString(s) 234 | } 235 | 236 | // From http://yaml.org/type/float.html, except the regular expression there 237 | // is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix. 238 | var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`) 239 | 240 | func (e *encoder) stringv(tag string, in reflect.Value) { 241 | var style yaml_scalar_style_t 242 | s := in.String() 243 | rtag, rs := resolve("", s) 244 | if rtag == yaml_BINARY_TAG { 245 | if tag == "" || tag == yaml_STR_TAG { 246 | tag = rtag 247 | s = rs.(string) 248 | } else if tag == yaml_BINARY_TAG { 249 | failf("explicitly tagged !!binary data must be base64-encoded") 250 | } else { 251 | failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag)) 252 | } 253 | } 254 | if tag == "" && (rtag != yaml_STR_TAG || isBase60Float(s)) { 255 | style = yaml_DOUBLE_QUOTED_SCALAR_STYLE 256 | } else if strings.Contains(s, "\n") { 257 | style = yaml_LITERAL_SCALAR_STYLE 258 | } else { 259 | style = yaml_PLAIN_SCALAR_STYLE 260 | } 261 | e.emitScalar(s, "", tag, style) 262 | } 263 | 264 | func (e *encoder) boolv(tag string, in reflect.Value) { 265 | var s string 266 | if in.Bool() { 267 | s = "true" 268 | } else { 269 | s = "false" 270 | } 271 | e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) 272 | } 273 | 274 | func (e *encoder) intv(tag string, in reflect.Value) { 275 | s := strconv.FormatInt(in.Int(), 10) 276 | e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) 277 | } 278 | 279 | func (e *encoder) uintv(tag string, in reflect.Value) { 280 | s := strconv.FormatUint(in.Uint(), 10) 281 | e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) 282 | } 283 | 284 | func (e *encoder) floatv(tag string, in reflect.Value) { 285 | // FIXME: Handle 64 bits here. 286 | s := strconv.FormatFloat(float64(in.Float()), 'g', -1, 32) 287 | switch s { 288 | case "+Inf": 289 | s = ".inf" 290 | case "-Inf": 291 | s = "-.inf" 292 | case "NaN": 293 | s = ".nan" 294 | } 295 | e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) 296 | } 297 | 298 | func (e *encoder) nilv() { 299 | e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE) 300 | } 301 | 302 | func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t) { 303 | implicit := tag == "" 304 | e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style)) 305 | e.emit() 306 | } 307 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/readerc.go: -------------------------------------------------------------------------------- 1 | package yaml 2 | 3 | import ( 4 | "io" 5 | ) 6 | 7 | // Set the reader error and return 0. 8 | func yaml_parser_set_reader_error(parser *yaml_parser_t, problem string, offset int, value int) bool { 9 | parser.error = yaml_READER_ERROR 10 | parser.problem = problem 11 | parser.problem_offset = offset 12 | parser.problem_value = value 13 | return false 14 | } 15 | 16 | // Byte order marks. 17 | const ( 18 | bom_UTF8 = "\xef\xbb\xbf" 19 | bom_UTF16LE = "\xff\xfe" 20 | bom_UTF16BE = "\xfe\xff" 21 | ) 22 | 23 | // Determine the input stream encoding by checking the BOM symbol. If no BOM is 24 | // found, the UTF-8 encoding is assumed. Return 1 on success, 0 on failure. 25 | func yaml_parser_determine_encoding(parser *yaml_parser_t) bool { 26 | // Ensure that we had enough bytes in the raw buffer. 27 | for !parser.eof && len(parser.raw_buffer)-parser.raw_buffer_pos < 3 { 28 | if !yaml_parser_update_raw_buffer(parser) { 29 | return false 30 | } 31 | } 32 | 33 | // Determine the encoding. 34 | buf := parser.raw_buffer 35 | pos := parser.raw_buffer_pos 36 | avail := len(buf) - pos 37 | if avail >= 2 && buf[pos] == bom_UTF16LE[0] && buf[pos+1] == bom_UTF16LE[1] { 38 | parser.encoding = yaml_UTF16LE_ENCODING 39 | parser.raw_buffer_pos += 2 40 | parser.offset += 2 41 | } else if avail >= 2 && buf[pos] == bom_UTF16BE[0] && buf[pos+1] == bom_UTF16BE[1] { 42 | parser.encoding = yaml_UTF16BE_ENCODING 43 | parser.raw_buffer_pos += 2 44 | parser.offset += 2 45 | } else if avail >= 3 && buf[pos] == bom_UTF8[0] && buf[pos+1] == bom_UTF8[1] && buf[pos+2] == bom_UTF8[2] { 46 | parser.encoding = yaml_UTF8_ENCODING 47 | parser.raw_buffer_pos += 3 48 | parser.offset += 3 49 | } else { 50 | parser.encoding = yaml_UTF8_ENCODING 51 | } 52 | return true 53 | } 54 | 55 | // Update the raw buffer. 56 | func yaml_parser_update_raw_buffer(parser *yaml_parser_t) bool { 57 | size_read := 0 58 | 59 | // Return if the raw buffer is full. 60 | if parser.raw_buffer_pos == 0 && len(parser.raw_buffer) == cap(parser.raw_buffer) { 61 | return true 62 | } 63 | 64 | // Return on EOF. 65 | if parser.eof { 66 | return true 67 | } 68 | 69 | // Move the remaining bytes in the raw buffer to the beginning. 70 | if parser.raw_buffer_pos > 0 && parser.raw_buffer_pos < len(parser.raw_buffer) { 71 | copy(parser.raw_buffer, parser.raw_buffer[parser.raw_buffer_pos:]) 72 | } 73 | parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)-parser.raw_buffer_pos] 74 | parser.raw_buffer_pos = 0 75 | 76 | // Call the read handler to fill the buffer. 77 | size_read, err := parser.read_handler(parser, parser.raw_buffer[len(parser.raw_buffer):cap(parser.raw_buffer)]) 78 | parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)+size_read] 79 | if err == io.EOF { 80 | parser.eof = true 81 | } else if err != nil { 82 | return yaml_parser_set_reader_error(parser, "input error: "+err.Error(), parser.offset, -1) 83 | } 84 | return true 85 | } 86 | 87 | // Ensure that the buffer contains at least `length` characters. 88 | // Return true on success, false on failure. 89 | // 90 | // The length is supposed to be significantly less that the buffer size. 91 | func yaml_parser_update_buffer(parser *yaml_parser_t, length int) bool { 92 | if parser.read_handler == nil { 93 | panic("read handler must be set") 94 | } 95 | 96 | // If the EOF flag is set and the raw buffer is empty, do nothing. 97 | if parser.eof && parser.raw_buffer_pos == len(parser.raw_buffer) { 98 | return true 99 | } 100 | 101 | // Return if the buffer contains enough characters. 102 | if parser.unread >= length { 103 | return true 104 | } 105 | 106 | // Determine the input encoding if it is not known yet. 107 | if parser.encoding == yaml_ANY_ENCODING { 108 | if !yaml_parser_determine_encoding(parser) { 109 | return false 110 | } 111 | } 112 | 113 | // Move the unread characters to the beginning of the buffer. 114 | buffer_len := len(parser.buffer) 115 | if parser.buffer_pos > 0 && parser.buffer_pos < buffer_len { 116 | copy(parser.buffer, parser.buffer[parser.buffer_pos:]) 117 | buffer_len -= parser.buffer_pos 118 | parser.buffer_pos = 0 119 | } else if parser.buffer_pos == buffer_len { 120 | buffer_len = 0 121 | parser.buffer_pos = 0 122 | } 123 | 124 | // Open the whole buffer for writing, and cut it before returning. 125 | parser.buffer = parser.buffer[:cap(parser.buffer)] 126 | 127 | // Fill the buffer until it has enough characters. 128 | first := true 129 | for parser.unread < length { 130 | 131 | // Fill the raw buffer if necessary. 132 | if !first || parser.raw_buffer_pos == len(parser.raw_buffer) { 133 | if !yaml_parser_update_raw_buffer(parser) { 134 | parser.buffer = parser.buffer[:buffer_len] 135 | return false 136 | } 137 | } 138 | first = false 139 | 140 | // Decode the raw buffer. 141 | inner: 142 | for parser.raw_buffer_pos != len(parser.raw_buffer) { 143 | var value rune 144 | var width int 145 | 146 | raw_unread := len(parser.raw_buffer) - parser.raw_buffer_pos 147 | 148 | // Decode the next character. 149 | switch parser.encoding { 150 | case yaml_UTF8_ENCODING: 151 | // Decode a UTF-8 character. Check RFC 3629 152 | // (http://www.ietf.org/rfc/rfc3629.txt) for more details. 153 | // 154 | // The following table (taken from the RFC) is used for 155 | // decoding. 156 | // 157 | // Char. number range | UTF-8 octet sequence 158 | // (hexadecimal) | (binary) 159 | // --------------------+------------------------------------ 160 | // 0000 0000-0000 007F | 0xxxxxxx 161 | // 0000 0080-0000 07FF | 110xxxxx 10xxxxxx 162 | // 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx 163 | // 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 164 | // 165 | // Additionally, the characters in the range 0xD800-0xDFFF 166 | // are prohibited as they are reserved for use with UTF-16 167 | // surrogate pairs. 168 | 169 | // Determine the length of the UTF-8 sequence. 170 | octet := parser.raw_buffer[parser.raw_buffer_pos] 171 | switch { 172 | case octet&0x80 == 0x00: 173 | width = 1 174 | case octet&0xE0 == 0xC0: 175 | width = 2 176 | case octet&0xF0 == 0xE0: 177 | width = 3 178 | case octet&0xF8 == 0xF0: 179 | width = 4 180 | default: 181 | // The leading octet is invalid. 182 | return yaml_parser_set_reader_error(parser, 183 | "invalid leading UTF-8 octet", 184 | parser.offset, int(octet)) 185 | } 186 | 187 | // Check if the raw buffer contains an incomplete character. 188 | if width > raw_unread { 189 | if parser.eof { 190 | return yaml_parser_set_reader_error(parser, 191 | "incomplete UTF-8 octet sequence", 192 | parser.offset, -1) 193 | } 194 | break inner 195 | } 196 | 197 | // Decode the leading octet. 198 | switch { 199 | case octet&0x80 == 0x00: 200 | value = rune(octet & 0x7F) 201 | case octet&0xE0 == 0xC0: 202 | value = rune(octet & 0x1F) 203 | case octet&0xF0 == 0xE0: 204 | value = rune(octet & 0x0F) 205 | case octet&0xF8 == 0xF0: 206 | value = rune(octet & 0x07) 207 | default: 208 | value = 0 209 | } 210 | 211 | // Check and decode the trailing octets. 212 | for k := 1; k < width; k++ { 213 | octet = parser.raw_buffer[parser.raw_buffer_pos+k] 214 | 215 | // Check if the octet is valid. 216 | if (octet & 0xC0) != 0x80 { 217 | return yaml_parser_set_reader_error(parser, 218 | "invalid trailing UTF-8 octet", 219 | parser.offset+k, int(octet)) 220 | } 221 | 222 | // Decode the octet. 223 | value = (value << 6) + rune(octet&0x3F) 224 | } 225 | 226 | // Check the length of the sequence against the value. 227 | switch { 228 | case width == 1: 229 | case width == 2 && value >= 0x80: 230 | case width == 3 && value >= 0x800: 231 | case width == 4 && value >= 0x10000: 232 | default: 233 | return yaml_parser_set_reader_error(parser, 234 | "invalid length of a UTF-8 sequence", 235 | parser.offset, -1) 236 | } 237 | 238 | // Check the range of the value. 239 | if value >= 0xD800 && value <= 0xDFFF || value > 0x10FFFF { 240 | return yaml_parser_set_reader_error(parser, 241 | "invalid Unicode character", 242 | parser.offset, int(value)) 243 | } 244 | 245 | case yaml_UTF16LE_ENCODING, yaml_UTF16BE_ENCODING: 246 | var low, high int 247 | if parser.encoding == yaml_UTF16LE_ENCODING { 248 | low, high = 0, 1 249 | } else { 250 | low, high = 1, 0 251 | } 252 | 253 | // The UTF-16 encoding is not as simple as one might 254 | // naively think. Check RFC 2781 255 | // (http://www.ietf.org/rfc/rfc2781.txt). 256 | // 257 | // Normally, two subsequent bytes describe a Unicode 258 | // character. However a special technique (called a 259 | // surrogate pair) is used for specifying character 260 | // values larger than 0xFFFF. 261 | // 262 | // A surrogate pair consists of two pseudo-characters: 263 | // high surrogate area (0xD800-0xDBFF) 264 | // low surrogate area (0xDC00-0xDFFF) 265 | // 266 | // The following formulas are used for decoding 267 | // and encoding characters using surrogate pairs: 268 | // 269 | // U = U' + 0x10000 (0x01 00 00 <= U <= 0x10 FF FF) 270 | // U' = yyyyyyyyyyxxxxxxxxxx (0 <= U' <= 0x0F FF FF) 271 | // W1 = 110110yyyyyyyyyy 272 | // W2 = 110111xxxxxxxxxx 273 | // 274 | // where U is the character value, W1 is the high surrogate 275 | // area, W2 is the low surrogate area. 276 | 277 | // Check for incomplete UTF-16 character. 278 | if raw_unread < 2 { 279 | if parser.eof { 280 | return yaml_parser_set_reader_error(parser, 281 | "incomplete UTF-16 character", 282 | parser.offset, -1) 283 | } 284 | break inner 285 | } 286 | 287 | // Get the character. 288 | value = rune(parser.raw_buffer[parser.raw_buffer_pos+low]) + 289 | (rune(parser.raw_buffer[parser.raw_buffer_pos+high]) << 8) 290 | 291 | // Check for unexpected low surrogate area. 292 | if value&0xFC00 == 0xDC00 { 293 | return yaml_parser_set_reader_error(parser, 294 | "unexpected low surrogate area", 295 | parser.offset, int(value)) 296 | } 297 | 298 | // Check for a high surrogate area. 299 | if value&0xFC00 == 0xD800 { 300 | width = 4 301 | 302 | // Check for incomplete surrogate pair. 303 | if raw_unread < 4 { 304 | if parser.eof { 305 | return yaml_parser_set_reader_error(parser, 306 | "incomplete UTF-16 surrogate pair", 307 | parser.offset, -1) 308 | } 309 | break inner 310 | } 311 | 312 | // Get the next character. 313 | value2 := rune(parser.raw_buffer[parser.raw_buffer_pos+low+2]) + 314 | (rune(parser.raw_buffer[parser.raw_buffer_pos+high+2]) << 8) 315 | 316 | // Check for a low surrogate area. 317 | if value2&0xFC00 != 0xDC00 { 318 | return yaml_parser_set_reader_error(parser, 319 | "expected low surrogate area", 320 | parser.offset+2, int(value2)) 321 | } 322 | 323 | // Generate the value of the surrogate pair. 324 | value = 0x10000 + ((value & 0x3FF) << 10) + (value2 & 0x3FF) 325 | } else { 326 | width = 2 327 | } 328 | 329 | default: 330 | panic("impossible") 331 | } 332 | 333 | // Check if the character is in the allowed range: 334 | // #x9 | #xA | #xD | [#x20-#x7E] (8 bit) 335 | // | #x85 | [#xA0-#xD7FF] | [#xE000-#xFFFD] (16 bit) 336 | // | [#x10000-#x10FFFF] (32 bit) 337 | switch { 338 | case value == 0x09: 339 | case value == 0x0A: 340 | case value == 0x0D: 341 | case value >= 0x20 && value <= 0x7E: 342 | case value == 0x85: 343 | case value >= 0xA0 && value <= 0xD7FF: 344 | case value >= 0xE000 && value <= 0xFFFD: 345 | case value >= 0x10000 && value <= 0x10FFFF: 346 | default: 347 | return yaml_parser_set_reader_error(parser, 348 | "control characters are not allowed", 349 | parser.offset, int(value)) 350 | } 351 | 352 | // Move the raw pointers. 353 | parser.raw_buffer_pos += width 354 | parser.offset += width 355 | 356 | // Finally put the character into the buffer. 357 | if value <= 0x7F { 358 | // 0000 0000-0000 007F . 0xxxxxxx 359 | parser.buffer[buffer_len+0] = byte(value) 360 | buffer_len += 1 361 | } else if value <= 0x7FF { 362 | // 0000 0080-0000 07FF . 110xxxxx 10xxxxxx 363 | parser.buffer[buffer_len+0] = byte(0xC0 + (value >> 6)) 364 | parser.buffer[buffer_len+1] = byte(0x80 + (value & 0x3F)) 365 | buffer_len += 2 366 | } else if value <= 0xFFFF { 367 | // 0000 0800-0000 FFFF . 1110xxxx 10xxxxxx 10xxxxxx 368 | parser.buffer[buffer_len+0] = byte(0xE0 + (value >> 12)) 369 | parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 6) & 0x3F)) 370 | parser.buffer[buffer_len+2] = byte(0x80 + (value & 0x3F)) 371 | buffer_len += 3 372 | } else { 373 | // 0001 0000-0010 FFFF . 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 374 | parser.buffer[buffer_len+0] = byte(0xF0 + (value >> 18)) 375 | parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 12) & 0x3F)) 376 | parser.buffer[buffer_len+2] = byte(0x80 + ((value >> 6) & 0x3F)) 377 | parser.buffer[buffer_len+3] = byte(0x80 + (value & 0x3F)) 378 | buffer_len += 4 379 | } 380 | 381 | parser.unread++ 382 | } 383 | 384 | // On EOF, put NUL into the buffer and return. 385 | if parser.eof { 386 | parser.buffer[buffer_len] = 0 387 | buffer_len++ 388 | parser.unread++ 389 | break 390 | } 391 | } 392 | parser.buffer = parser.buffer[:buffer_len] 393 | return true 394 | } 395 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/resolve.go: -------------------------------------------------------------------------------- 1 | package yaml 2 | 3 | import ( 4 | "encoding/base64" 5 | "math" 6 | "strconv" 7 | "strings" 8 | "unicode/utf8" 9 | ) 10 | 11 | type resolveMapItem struct { 12 | value interface{} 13 | tag string 14 | } 15 | 16 | var resolveTable = make([]byte, 256) 17 | var resolveMap = make(map[string]resolveMapItem) 18 | 19 | func init() { 20 | t := resolveTable 21 | t[int('+')] = 'S' // Sign 22 | t[int('-')] = 'S' 23 | for _, c := range "0123456789" { 24 | t[int(c)] = 'D' // Digit 25 | } 26 | for _, c := range "yYnNtTfFoO~" { 27 | t[int(c)] = 'M' // In map 28 | } 29 | t[int('.')] = '.' // Float (potentially in map) 30 | 31 | var resolveMapList = []struct { 32 | v interface{} 33 | tag string 34 | l []string 35 | }{ 36 | {true, yaml_BOOL_TAG, []string{"y", "Y", "yes", "Yes", "YES"}}, 37 | {true, yaml_BOOL_TAG, []string{"true", "True", "TRUE"}}, 38 | {true, yaml_BOOL_TAG, []string{"on", "On", "ON"}}, 39 | {false, yaml_BOOL_TAG, []string{"n", "N", "no", "No", "NO"}}, 40 | {false, yaml_BOOL_TAG, []string{"false", "False", "FALSE"}}, 41 | {false, yaml_BOOL_TAG, []string{"off", "Off", "OFF"}}, 42 | {nil, yaml_NULL_TAG, []string{"", "~", "null", "Null", "NULL"}}, 43 | {math.NaN(), yaml_FLOAT_TAG, []string{".nan", ".NaN", ".NAN"}}, 44 | {math.Inf(+1), yaml_FLOAT_TAG, []string{".inf", ".Inf", ".INF"}}, 45 | {math.Inf(+1), yaml_FLOAT_TAG, []string{"+.inf", "+.Inf", "+.INF"}}, 46 | {math.Inf(-1), yaml_FLOAT_TAG, []string{"-.inf", "-.Inf", "-.INF"}}, 47 | {"<<", yaml_MERGE_TAG, []string{"<<"}}, 48 | } 49 | 50 | m := resolveMap 51 | for _, item := range resolveMapList { 52 | for _, s := range item.l { 53 | m[s] = resolveMapItem{item.v, item.tag} 54 | } 55 | } 56 | } 57 | 58 | const longTagPrefix = "tag:yaml.org,2002:" 59 | 60 | func shortTag(tag string) string { 61 | // TODO This can easily be made faster and produce less garbage. 62 | if strings.HasPrefix(tag, longTagPrefix) { 63 | return "!!" + tag[len(longTagPrefix):] 64 | } 65 | return tag 66 | } 67 | 68 | func longTag(tag string) string { 69 | if strings.HasPrefix(tag, "!!") { 70 | return longTagPrefix + tag[2:] 71 | } 72 | return tag 73 | } 74 | 75 | func resolvableTag(tag string) bool { 76 | switch tag { 77 | case "", yaml_STR_TAG, yaml_BOOL_TAG, yaml_INT_TAG, yaml_FLOAT_TAG, yaml_NULL_TAG: 78 | return true 79 | } 80 | return false 81 | } 82 | 83 | func resolve(tag string, in string) (rtag string, out interface{}) { 84 | if !resolvableTag(tag) { 85 | return tag, in 86 | } 87 | 88 | defer func() { 89 | switch tag { 90 | case "", rtag, yaml_STR_TAG, yaml_BINARY_TAG: 91 | return 92 | } 93 | failf("cannot decode %s `%s` as a %s", shortTag(rtag), in, shortTag(tag)) 94 | }() 95 | 96 | // Any data is accepted as a !!str or !!binary. 97 | // Otherwise, the prefix is enough of a hint about what it might be. 98 | hint := byte('N') 99 | if in != "" { 100 | hint = resolveTable[in[0]] 101 | } 102 | if hint != 0 && tag != yaml_STR_TAG && tag != yaml_BINARY_TAG { 103 | // Handle things we can lookup in a map. 104 | if item, ok := resolveMap[in]; ok { 105 | return item.tag, item.value 106 | } 107 | 108 | // Base 60 floats are a bad idea, were dropped in YAML 1.2, and 109 | // are purposefully unsupported here. They're still quoted on 110 | // the way out for compatibility with other parser, though. 111 | 112 | switch hint { 113 | case 'M': 114 | // We've already checked the map above. 115 | 116 | case '.': 117 | // Not in the map, so maybe a normal float. 118 | floatv, err := strconv.ParseFloat(in, 64) 119 | if err == nil { 120 | return yaml_FLOAT_TAG, floatv 121 | } 122 | 123 | case 'D', 'S': 124 | // Int, float, or timestamp. 125 | plain := strings.Replace(in, "_", "", -1) 126 | intv, err := strconv.ParseInt(plain, 0, 64) 127 | if err == nil { 128 | if intv == int64(int(intv)) { 129 | return yaml_INT_TAG, int(intv) 130 | } else { 131 | return yaml_INT_TAG, intv 132 | } 133 | } 134 | uintv, err := strconv.ParseUint(plain, 0, 64) 135 | if err == nil { 136 | return yaml_INT_TAG, uintv 137 | } 138 | floatv, err := strconv.ParseFloat(plain, 64) 139 | if err == nil { 140 | return yaml_FLOAT_TAG, floatv 141 | } 142 | if strings.HasPrefix(plain, "0b") { 143 | intv, err := strconv.ParseInt(plain[2:], 2, 64) 144 | if err == nil { 145 | if intv == int64(int(intv)) { 146 | return yaml_INT_TAG, int(intv) 147 | } else { 148 | return yaml_INT_TAG, intv 149 | } 150 | } 151 | uintv, err := strconv.ParseUint(plain[2:], 2, 64) 152 | if err == nil { 153 | return yaml_INT_TAG, uintv 154 | } 155 | } else if strings.HasPrefix(plain, "-0b") { 156 | intv, err := strconv.ParseInt(plain[3:], 2, 64) 157 | if err == nil { 158 | if intv == int64(int(intv)) { 159 | return yaml_INT_TAG, -int(intv) 160 | } else { 161 | return yaml_INT_TAG, -intv 162 | } 163 | } 164 | } 165 | // XXX Handle timestamps here. 166 | 167 | default: 168 | panic("resolveTable item not yet handled: " + string(rune(hint)) + " (with " + in + ")") 169 | } 170 | } 171 | if tag == yaml_BINARY_TAG { 172 | return yaml_BINARY_TAG, in 173 | } 174 | if utf8.ValidString(in) { 175 | return yaml_STR_TAG, in 176 | } 177 | return yaml_BINARY_TAG, encodeBase64(in) 178 | } 179 | 180 | // encodeBase64 encodes s as base64 that is broken up into multiple lines 181 | // as appropriate for the resulting length. 182 | func encodeBase64(s string) string { 183 | const lineLen = 70 184 | encLen := base64.StdEncoding.EncodedLen(len(s)) 185 | lines := encLen/lineLen + 1 186 | buf := make([]byte, encLen*2+lines) 187 | in := buf[0:encLen] 188 | out := buf[encLen:] 189 | base64.StdEncoding.Encode(in, []byte(s)) 190 | k := 0 191 | for i := 0; i < len(in); i += lineLen { 192 | j := i + lineLen 193 | if j > len(in) { 194 | j = len(in) 195 | } 196 | k += copy(out[k:], in[i:j]) 197 | if lines > 1 { 198 | out[k] = '\n' 199 | k++ 200 | } 201 | } 202 | return string(out[:k]) 203 | } 204 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/sorter.go: -------------------------------------------------------------------------------- 1 | package yaml 2 | 3 | import ( 4 | "reflect" 5 | "unicode" 6 | ) 7 | 8 | type keyList []reflect.Value 9 | 10 | func (l keyList) Len() int { return len(l) } 11 | func (l keyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } 12 | func (l keyList) Less(i, j int) bool { 13 | a := l[i] 14 | b := l[j] 15 | ak := a.Kind() 16 | bk := b.Kind() 17 | for (ak == reflect.Interface || ak == reflect.Ptr) && !a.IsNil() { 18 | a = a.Elem() 19 | ak = a.Kind() 20 | } 21 | for (bk == reflect.Interface || bk == reflect.Ptr) && !b.IsNil() { 22 | b = b.Elem() 23 | bk = b.Kind() 24 | } 25 | af, aok := keyFloat(a) 26 | bf, bok := keyFloat(b) 27 | if aok && bok { 28 | if af != bf { 29 | return af < bf 30 | } 31 | if ak != bk { 32 | return ak < bk 33 | } 34 | return numLess(a, b) 35 | } 36 | if ak != reflect.String || bk != reflect.String { 37 | return ak < bk 38 | } 39 | ar, br := []rune(a.String()), []rune(b.String()) 40 | for i := 0; i < len(ar) && i < len(br); i++ { 41 | if ar[i] == br[i] { 42 | continue 43 | } 44 | al := unicode.IsLetter(ar[i]) 45 | bl := unicode.IsLetter(br[i]) 46 | if al && bl { 47 | return ar[i] < br[i] 48 | } 49 | if al || bl { 50 | return bl 51 | } 52 | var ai, bi int 53 | var an, bn int64 54 | for ai = i; ai < len(ar) && unicode.IsDigit(ar[ai]); ai++ { 55 | an = an*10 + int64(ar[ai]-'0') 56 | } 57 | for bi = i; bi < len(br) && unicode.IsDigit(br[bi]); bi++ { 58 | bn = bn*10 + int64(br[bi]-'0') 59 | } 60 | if an != bn { 61 | return an < bn 62 | } 63 | if ai != bi { 64 | return ai < bi 65 | } 66 | return ar[i] < br[i] 67 | } 68 | return len(ar) < len(br) 69 | } 70 | 71 | // keyFloat returns a float value for v if it is a number/bool 72 | // and whether it is a number/bool or not. 73 | func keyFloat(v reflect.Value) (f float64, ok bool) { 74 | switch v.Kind() { 75 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 76 | return float64(v.Int()), true 77 | case reflect.Float32, reflect.Float64: 78 | return v.Float(), true 79 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 80 | return float64(v.Uint()), true 81 | case reflect.Bool: 82 | if v.Bool() { 83 | return 1, true 84 | } 85 | return 0, true 86 | } 87 | return 0, false 88 | } 89 | 90 | // numLess returns whether a < b. 91 | // a and b must necessarily have the same kind. 92 | func numLess(a, b reflect.Value) bool { 93 | switch a.Kind() { 94 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 95 | return a.Int() < b.Int() 96 | case reflect.Float32, reflect.Float64: 97 | return a.Float() < b.Float() 98 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 99 | return a.Uint() < b.Uint() 100 | case reflect.Bool: 101 | return !a.Bool() && b.Bool() 102 | } 103 | panic("not a number") 104 | } 105 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/writerc.go: -------------------------------------------------------------------------------- 1 | package yaml 2 | 3 | // Set the writer error and return false. 4 | func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool { 5 | emitter.error = yaml_WRITER_ERROR 6 | emitter.problem = problem 7 | return false 8 | } 9 | 10 | // Flush the output buffer. 11 | func yaml_emitter_flush(emitter *yaml_emitter_t) bool { 12 | if emitter.write_handler == nil { 13 | panic("write handler not set") 14 | } 15 | 16 | // Check if the buffer is empty. 17 | if emitter.buffer_pos == 0 { 18 | return true 19 | } 20 | 21 | // If the output encoding is UTF-8, we don't need to recode the buffer. 22 | if emitter.encoding == yaml_UTF8_ENCODING { 23 | if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil { 24 | return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error()) 25 | } 26 | emitter.buffer_pos = 0 27 | return true 28 | } 29 | 30 | // Recode the buffer into the raw buffer. 31 | var low, high int 32 | if emitter.encoding == yaml_UTF16LE_ENCODING { 33 | low, high = 0, 1 34 | } else { 35 | high, low = 1, 0 36 | } 37 | 38 | pos := 0 39 | for pos < emitter.buffer_pos { 40 | // See the "reader.c" code for more details on UTF-8 encoding. Note 41 | // that we assume that the buffer contains a valid UTF-8 sequence. 42 | 43 | // Read the next UTF-8 character. 44 | octet := emitter.buffer[pos] 45 | 46 | var w int 47 | var value rune 48 | switch { 49 | case octet&0x80 == 0x00: 50 | w, value = 1, rune(octet&0x7F) 51 | case octet&0xE0 == 0xC0: 52 | w, value = 2, rune(octet&0x1F) 53 | case octet&0xF0 == 0xE0: 54 | w, value = 3, rune(octet&0x0F) 55 | case octet&0xF8 == 0xF0: 56 | w, value = 4, rune(octet&0x07) 57 | } 58 | for k := 1; k < w; k++ { 59 | octet = emitter.buffer[pos+k] 60 | value = (value << 6) + (rune(octet) & 0x3F) 61 | } 62 | pos += w 63 | 64 | // Write the character. 65 | if value < 0x10000 { 66 | var b [2]byte 67 | b[high] = byte(value >> 8) 68 | b[low] = byte(value & 0xFF) 69 | emitter.raw_buffer = append(emitter.raw_buffer, b[0], b[1]) 70 | } else { 71 | // Write the character using a surrogate pair (check "reader.c"). 72 | var b [4]byte 73 | value -= 0x10000 74 | b[high] = byte(0xD8 + (value >> 18)) 75 | b[low] = byte((value >> 10) & 0xFF) 76 | b[high+2] = byte(0xDC + ((value >> 8) & 0xFF)) 77 | b[low+2] = byte(value & 0xFF) 78 | emitter.raw_buffer = append(emitter.raw_buffer, b[0], b[1], b[2], b[3]) 79 | } 80 | } 81 | 82 | // Write the raw buffer. 83 | if err := emitter.write_handler(emitter, emitter.raw_buffer); err != nil { 84 | return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error()) 85 | } 86 | emitter.buffer_pos = 0 87 | emitter.raw_buffer = emitter.raw_buffer[:0] 88 | return true 89 | } 90 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/yaml.go: -------------------------------------------------------------------------------- 1 | // Package yaml implements YAML support for the Go language. 2 | // 3 | // Source code and other details for the project are available at GitHub: 4 | // 5 | // https://github.com/go-yaml/yaml 6 | // 7 | package yaml 8 | 9 | import ( 10 | "errors" 11 | "fmt" 12 | "reflect" 13 | "strings" 14 | "sync" 15 | ) 16 | 17 | // MapSlice encodes and decodes as a YAML map. 18 | // The order of keys is preserved when encoding and decoding. 19 | type MapSlice []MapItem 20 | 21 | // MapItem is an item in a MapSlice. 22 | type MapItem struct { 23 | Key, Value interface{} 24 | } 25 | 26 | // The Unmarshaler interface may be implemented by types to customize their 27 | // behavior when being unmarshaled from a YAML document. The UnmarshalYAML 28 | // method receives a function that may be called to unmarshal the original 29 | // YAML value into a field or variable. It is safe to call the unmarshal 30 | // function parameter more than once if necessary. 31 | type Unmarshaler interface { 32 | UnmarshalYAML(unmarshal func(interface{}) error) error 33 | } 34 | 35 | // The Marshaler interface may be implemented by types to customize their 36 | // behavior when being marshaled into a YAML document. The returned value 37 | // is marshaled in place of the original value implementing Marshaler. 38 | // 39 | // If an error is returned by MarshalYAML, the marshaling procedure stops 40 | // and returns with the provided error. 41 | type Marshaler interface { 42 | MarshalYAML() (interface{}, error) 43 | } 44 | 45 | // Unmarshal decodes the first document found within the in byte slice 46 | // and assigns decoded values into the out value. 47 | // 48 | // Maps and pointers (to a struct, string, int, etc) are accepted as out 49 | // values. If an internal pointer within a struct is not initialized, 50 | // the yaml package will initialize it if necessary for unmarshalling 51 | // the provided data. The out parameter must not be nil. 52 | // 53 | // The type of the decoded values should be compatible with the respective 54 | // values in out. If one or more values cannot be decoded due to a type 55 | // mismatches, decoding continues partially until the end of the YAML 56 | // content, and a *yaml.TypeError is returned with details for all 57 | // missed values. 58 | // 59 | // Struct fields are only unmarshalled if they are exported (have an 60 | // upper case first letter), and are unmarshalled using the field name 61 | // lowercased as the default key. Custom keys may be defined via the 62 | // "yaml" name in the field tag: the content preceding the first comma 63 | // is used as the key, and the following comma-separated options are 64 | // used to tweak the marshalling process (see Marshal). 65 | // Conflicting names result in a runtime error. 66 | // 67 | // For example: 68 | // 69 | // type T struct { 70 | // F int `yaml:"a,omitempty"` 71 | // B int 72 | // } 73 | // var t T 74 | // yaml.Unmarshal([]byte("a: 1\nb: 2"), &t) 75 | // 76 | // See the documentation of Marshal for the format of tags and a list of 77 | // supported tag options. 78 | // 79 | func Unmarshal(in []byte, out interface{}) (err error) { 80 | defer handleErr(&err) 81 | d := newDecoder() 82 | p := newParser(in) 83 | defer p.destroy() 84 | node := p.parse() 85 | if node != nil { 86 | v := reflect.ValueOf(out) 87 | if v.Kind() == reflect.Ptr && !v.IsNil() { 88 | v = v.Elem() 89 | } 90 | d.unmarshal(node, v) 91 | } 92 | if len(d.terrors) > 0 { 93 | return &TypeError{d.terrors} 94 | } 95 | return nil 96 | } 97 | 98 | // Marshal serializes the value provided into a YAML document. The structure 99 | // of the generated document will reflect the structure of the value itself. 100 | // Maps and pointers (to struct, string, int, etc) are accepted as the in value. 101 | // 102 | // Struct fields are only unmarshalled if they are exported (have an upper case 103 | // first letter), and are unmarshalled using the field name lowercased as the 104 | // default key. Custom keys may be defined via the "yaml" name in the field 105 | // tag: the content preceding the first comma is used as the key, and the 106 | // following comma-separated options are used to tweak the marshalling process. 107 | // Conflicting names result in a runtime error. 108 | // 109 | // The field tag format accepted is: 110 | // 111 | // `(...) yaml:"[][,[,]]" (...)` 112 | // 113 | // The following flags are currently supported: 114 | // 115 | // omitempty Only include the field if it's not set to the zero 116 | // value for the type or to empty slices or maps. 117 | // Does not apply to zero valued structs. 118 | // 119 | // flow Marshal using a flow style (useful for structs, 120 | // sequences and maps). 121 | // 122 | // inline Inline the field, which must be a struct or a map, 123 | // causing all of its fields or keys to be processed as if 124 | // they were part of the outer struct. For maps, keys must 125 | // not conflict with the yaml keys of other struct fields. 126 | // 127 | // In addition, if the key is "-", the field is ignored. 128 | // 129 | // For example: 130 | // 131 | // type T struct { 132 | // F int "a,omitempty" 133 | // B int 134 | // } 135 | // yaml.Marshal(&T{B: 2}) // Returns "b: 2\n" 136 | // yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n" 137 | // 138 | func Marshal(in interface{}) (out []byte, err error) { 139 | defer handleErr(&err) 140 | e := newEncoder() 141 | defer e.destroy() 142 | e.marshal("", reflect.ValueOf(in)) 143 | e.finish() 144 | out = e.out 145 | return 146 | } 147 | 148 | func handleErr(err *error) { 149 | if v := recover(); v != nil { 150 | if e, ok := v.(yamlError); ok { 151 | *err = e.err 152 | } else { 153 | panic(v) 154 | } 155 | } 156 | } 157 | 158 | type yamlError struct { 159 | err error 160 | } 161 | 162 | func fail(err error) { 163 | panic(yamlError{err}) 164 | } 165 | 166 | func failf(format string, args ...interface{}) { 167 | panic(yamlError{fmt.Errorf("yaml: "+format, args...)}) 168 | } 169 | 170 | // A TypeError is returned by Unmarshal when one or more fields in 171 | // the YAML document cannot be properly decoded into the requested 172 | // types. When this error is returned, the value is still 173 | // unmarshaled partially. 174 | type TypeError struct { 175 | Errors []string 176 | } 177 | 178 | func (e *TypeError) Error() string { 179 | return fmt.Sprintf("yaml: unmarshal errors:\n %s", strings.Join(e.Errors, "\n ")) 180 | } 181 | 182 | // -------------------------------------------------------------------------- 183 | // Maintain a mapping of keys to structure field indexes 184 | 185 | // The code in this section was copied from mgo/bson. 186 | 187 | // structInfo holds details for the serialization of fields of 188 | // a given struct. 189 | type structInfo struct { 190 | FieldsMap map[string]fieldInfo 191 | FieldsList []fieldInfo 192 | 193 | // InlineMap is the number of the field in the struct that 194 | // contains an ,inline map, or -1 if there's none. 195 | InlineMap int 196 | } 197 | 198 | type fieldInfo struct { 199 | Key string 200 | Num int 201 | OmitEmpty bool 202 | Flow bool 203 | 204 | // Inline holds the field index if the field is part of an inlined struct. 205 | Inline []int 206 | } 207 | 208 | var structMap = make(map[reflect.Type]*structInfo) 209 | var fieldMapMutex sync.RWMutex 210 | 211 | func getStructInfo(st reflect.Type) (*structInfo, error) { 212 | fieldMapMutex.RLock() 213 | sinfo, found := structMap[st] 214 | fieldMapMutex.RUnlock() 215 | if found { 216 | return sinfo, nil 217 | } 218 | 219 | n := st.NumField() 220 | fieldsMap := make(map[string]fieldInfo) 221 | fieldsList := make([]fieldInfo, 0, n) 222 | inlineMap := -1 223 | for i := 0; i != n; i++ { 224 | field := st.Field(i) 225 | if field.PkgPath != "" && !field.Anonymous { 226 | continue // Private field 227 | } 228 | 229 | info := fieldInfo{Num: i} 230 | 231 | tag := field.Tag.Get("yaml") 232 | if tag == "" && strings.Index(string(field.Tag), ":") < 0 { 233 | tag = string(field.Tag) 234 | } 235 | if tag == "-" { 236 | continue 237 | } 238 | 239 | inline := false 240 | fields := strings.Split(tag, ",") 241 | if len(fields) > 1 { 242 | for _, flag := range fields[1:] { 243 | switch flag { 244 | case "omitempty": 245 | info.OmitEmpty = true 246 | case "flow": 247 | info.Flow = true 248 | case "inline": 249 | inline = true 250 | default: 251 | return nil, errors.New(fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st)) 252 | } 253 | } 254 | tag = fields[0] 255 | } 256 | 257 | if inline { 258 | switch field.Type.Kind() { 259 | case reflect.Map: 260 | if inlineMap >= 0 { 261 | return nil, errors.New("Multiple ,inline maps in struct " + st.String()) 262 | } 263 | if field.Type.Key() != reflect.TypeOf("") { 264 | return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String()) 265 | } 266 | inlineMap = info.Num 267 | case reflect.Struct: 268 | sinfo, err := getStructInfo(field.Type) 269 | if err != nil { 270 | return nil, err 271 | } 272 | for _, finfo := range sinfo.FieldsList { 273 | if _, found := fieldsMap[finfo.Key]; found { 274 | msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String() 275 | return nil, errors.New(msg) 276 | } 277 | if finfo.Inline == nil { 278 | finfo.Inline = []int{i, finfo.Num} 279 | } else { 280 | finfo.Inline = append([]int{i}, finfo.Inline...) 281 | } 282 | fieldsMap[finfo.Key] = finfo 283 | fieldsList = append(fieldsList, finfo) 284 | } 285 | default: 286 | //return nil, errors.New("Option ,inline needs a struct value or map field") 287 | return nil, errors.New("Option ,inline needs a struct value field") 288 | } 289 | continue 290 | } 291 | 292 | if tag != "" { 293 | info.Key = tag 294 | } else { 295 | info.Key = strings.ToLower(field.Name) 296 | } 297 | 298 | if _, found = fieldsMap[info.Key]; found { 299 | msg := "Duplicated key '" + info.Key + "' in struct " + st.String() 300 | return nil, errors.New(msg) 301 | } 302 | 303 | fieldsList = append(fieldsList, info) 304 | fieldsMap[info.Key] = info 305 | } 306 | 307 | sinfo = &structInfo{fieldsMap, fieldsList, inlineMap} 308 | 309 | fieldMapMutex.Lock() 310 | structMap[st] = sinfo 311 | fieldMapMutex.Unlock() 312 | return sinfo, nil 313 | } 314 | 315 | func isZero(v reflect.Value) bool { 316 | switch v.Kind() { 317 | case reflect.String: 318 | return len(v.String()) == 0 319 | case reflect.Interface, reflect.Ptr: 320 | return v.IsNil() 321 | case reflect.Slice: 322 | return v.Len() == 0 323 | case reflect.Map: 324 | return v.Len() == 0 325 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 326 | return v.Int() == 0 327 | case reflect.Float32, reflect.Float64: 328 | return v.Float() == 0 329 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 330 | return v.Uint() == 0 331 | case reflect.Bool: 332 | return !v.Bool() 333 | case reflect.Struct: 334 | vt := v.Type() 335 | for i := v.NumField() - 1; i >= 0; i-- { 336 | if vt.Field(i).PkgPath != "" { 337 | continue // Private field 338 | } 339 | if !isZero(v.Field(i)) { 340 | return false 341 | } 342 | } 343 | return true 344 | } 345 | return false 346 | } 347 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/yamlprivateh.go: -------------------------------------------------------------------------------- 1 | package yaml 2 | 3 | const ( 4 | // The size of the input raw buffer. 5 | input_raw_buffer_size = 512 6 | 7 | // The size of the input buffer. 8 | // It should be possible to decode the whole raw buffer. 9 | input_buffer_size = input_raw_buffer_size * 3 10 | 11 | // The size of the output buffer. 12 | output_buffer_size = 128 13 | 14 | // The size of the output raw buffer. 15 | // It should be possible to encode the whole output buffer. 16 | output_raw_buffer_size = (output_buffer_size*2 + 2) 17 | 18 | // The size of other stacks and queues. 19 | initial_stack_size = 16 20 | initial_queue_size = 16 21 | initial_string_size = 16 22 | ) 23 | 24 | // Check if the character at the specified position is an alphabetical 25 | // character, a digit, '_', or '-'. 26 | func is_alpha(b []byte, i int) bool { 27 | return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'Z' || b[i] >= 'a' && b[i] <= 'z' || b[i] == '_' || b[i] == '-' 28 | } 29 | 30 | // Check if the character at the specified position is a digit. 31 | func is_digit(b []byte, i int) bool { 32 | return b[i] >= '0' && b[i] <= '9' 33 | } 34 | 35 | // Get the value of a digit. 36 | func as_digit(b []byte, i int) int { 37 | return int(b[i]) - '0' 38 | } 39 | 40 | // Check if the character at the specified position is a hex-digit. 41 | func is_hex(b []byte, i int) bool { 42 | return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'F' || b[i] >= 'a' && b[i] <= 'f' 43 | } 44 | 45 | // Get the value of a hex-digit. 46 | func as_hex(b []byte, i int) int { 47 | bi := b[i] 48 | if bi >= 'A' && bi <= 'F' { 49 | return int(bi) - 'A' + 10 50 | } 51 | if bi >= 'a' && bi <= 'f' { 52 | return int(bi) - 'a' + 10 53 | } 54 | return int(bi) - '0' 55 | } 56 | 57 | // Check if the character is ASCII. 58 | func is_ascii(b []byte, i int) bool { 59 | return b[i] <= 0x7F 60 | } 61 | 62 | // Check if the character at the start of the buffer can be printed unescaped. 63 | func is_printable(b []byte, i int) bool { 64 | return ((b[i] == 0x0A) || // . == #x0A 65 | (b[i] >= 0x20 && b[i] <= 0x7E) || // #x20 <= . <= #x7E 66 | (b[i] == 0xC2 && b[i+1] >= 0xA0) || // #0xA0 <= . <= #xD7FF 67 | (b[i] > 0xC2 && b[i] < 0xED) || 68 | (b[i] == 0xED && b[i+1] < 0xA0) || 69 | (b[i] == 0xEE) || 70 | (b[i] == 0xEF && // #xE000 <= . <= #xFFFD 71 | !(b[i+1] == 0xBB && b[i+2] == 0xBF) && // && . != #xFEFF 72 | !(b[i+1] == 0xBF && (b[i+2] == 0xBE || b[i+2] == 0xBF)))) 73 | } 74 | 75 | // Check if the character at the specified position is NUL. 76 | func is_z(b []byte, i int) bool { 77 | return b[i] == 0x00 78 | } 79 | 80 | // Check if the beginning of the buffer is a BOM. 81 | func is_bom(b []byte, i int) bool { 82 | return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF 83 | } 84 | 85 | // Check if the character at the specified position is space. 86 | func is_space(b []byte, i int) bool { 87 | return b[i] == ' ' 88 | } 89 | 90 | // Check if the character at the specified position is tab. 91 | func is_tab(b []byte, i int) bool { 92 | return b[i] == '\t' 93 | } 94 | 95 | // Check if the character at the specified position is blank (space or tab). 96 | func is_blank(b []byte, i int) bool { 97 | //return is_space(b, i) || is_tab(b, i) 98 | return b[i] == ' ' || b[i] == '\t' 99 | } 100 | 101 | // Check if the character at the specified position is a line break. 102 | func is_break(b []byte, i int) bool { 103 | return (b[i] == '\r' || // CR (#xD) 104 | b[i] == '\n' || // LF (#xA) 105 | b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) 106 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) 107 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9) // PS (#x2029) 108 | } 109 | 110 | func is_crlf(b []byte, i int) bool { 111 | return b[i] == '\r' && b[i+1] == '\n' 112 | } 113 | 114 | // Check if the character is a line break or NUL. 115 | func is_breakz(b []byte, i int) bool { 116 | //return is_break(b, i) || is_z(b, i) 117 | return ( // is_break: 118 | b[i] == '\r' || // CR (#xD) 119 | b[i] == '\n' || // LF (#xA) 120 | b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) 121 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) 122 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) 123 | // is_z: 124 | b[i] == 0) 125 | } 126 | 127 | // Check if the character is a line break, space, or NUL. 128 | func is_spacez(b []byte, i int) bool { 129 | //return is_space(b, i) || is_breakz(b, i) 130 | return ( // is_space: 131 | b[i] == ' ' || 132 | // is_breakz: 133 | b[i] == '\r' || // CR (#xD) 134 | b[i] == '\n' || // LF (#xA) 135 | b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) 136 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) 137 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) 138 | b[i] == 0) 139 | } 140 | 141 | // Check if the character is a line break, space, tab, or NUL. 142 | func is_blankz(b []byte, i int) bool { 143 | //return is_blank(b, i) || is_breakz(b, i) 144 | return ( // is_blank: 145 | b[i] == ' ' || b[i] == '\t' || 146 | // is_breakz: 147 | b[i] == '\r' || // CR (#xD) 148 | b[i] == '\n' || // LF (#xA) 149 | b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) 150 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) 151 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) 152 | b[i] == 0) 153 | } 154 | 155 | // Determine the width of the character. 156 | func width(b byte) int { 157 | // Don't replace these by a switch without first 158 | // confirming that it is being inlined. 159 | if b&0x80 == 0x00 { 160 | return 1 161 | } 162 | if b&0xE0 == 0xC0 { 163 | return 2 164 | } 165 | if b&0xF0 == 0xE0 { 166 | return 3 167 | } 168 | if b&0xF8 == 0xF0 { 169 | return 4 170 | } 171 | return 0 172 | 173 | } 174 | -------------------------------------------------------------------------------- /wsp_client/wsp_client.cfg: -------------------------------------------------------------------------------- 1 | --- 2 | targets : # Endpoints to connect to 3 | - ws://127.0.0.1:8080/register # 4 | poolidlesize : 10 # Default number of concurrent open (TCP) connections to keep idle per WSP server 5 | poolmaxsize : 100 # Maximum number of concurrent open (TCP) connections per WSP server 6 | #blacklist : # Forbidden destination ( deny nothing if empty ) 7 | # - method : ".*" # Applied in order before whitelist 8 | # url : ".*forbidden.*" # None must match 9 | # headers : # Optinal header check 10 | # X-CUSTOM-HEADER : "^value$" # 11 | #whitelist : # Allowed destinations ( allow all if empty ) 12 | # - method : "^GET$" # Applied in order after blacklist 13 | # url : "http(s)?://.*$" # One must match 14 | # headers : # Optinal header check 15 | # X-CUSTOM-HEADER : "^value$" # 16 | # secretkey : ThisIsASecret # secret key that must match the value set in servers configuration 17 | -------------------------------------------------------------------------------- /wsp_client/wsp_client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | "os" 7 | "os/signal" 8 | 9 | "github.com/root-gg/utils" 10 | 11 | "github.com/root-gg/wsp/client" 12 | ) 13 | 14 | func main() { 15 | configFile := flag.String("config", "wsp_client.cfg", "config file path") 16 | flag.Parse() 17 | 18 | // Load configuration 19 | config, err := client.LoadConfiguration(*configFile) 20 | if err != nil { 21 | log.Fatalf("Unable to load configuration : %s", err) 22 | } 23 | utils.Dump(config) 24 | 25 | proxy := client.NewClient(config) 26 | 27 | // Handle SIGINT 28 | c := make(chan os.Signal, 1) 29 | signal.Notify(c, os.Interrupt) 30 | go func() { 31 | for { 32 | <-c 33 | log.Println("SIGINT Detected") 34 | proxy.Shutdown() 35 | os.Exit(0) 36 | } 37 | }() 38 | 39 | proxy.Start() 40 | 41 | select {} 42 | } 43 | -------------------------------------------------------------------------------- /wsp_server/wsp_server.cfg: -------------------------------------------------------------------------------- 1 | --- 2 | host : 127.0.0.1 # Address to bind the HTTP server 3 | port : 8080 # Port to bind the HTTP server 4 | timeout : 1000 # Time to wait before acquiring a WS connection to forward the request (milliseconds) 5 | idletimeout : 60000 # Time to wait before closing idle connection when there is enough idle connections (milliseconds) 6 | #blacklist : # Forbidden destination ( deny nothing if empty ) 7 | # - method : ".*" # Applied in order before whitelist 8 | # url : "^http(s)?://google.*" # None must match 9 | # headers : # Optinal header check 10 | # X-CUSTOM-HEADER : "^value$" # 11 | #whitelist : # Allowed destinations ( allow all if empty ) 12 | # - method : "^GET$" # Applied in order after blacklist 13 | # url : "^http(s)?://.*$" # One must match 14 | # headers : # Optinal header check 15 | # X-CUSTOM-HEADER : "^value$" # 16 | # secretkey : ThisIsASecret # secret key that must be set in clients configuration 17 | -------------------------------------------------------------------------------- /wsp_server/wsp_server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | "os" 7 | "os/signal" 8 | 9 | "github.com/root-gg/utils" 10 | 11 | "github.com/root-gg/wsp/server" 12 | ) 13 | 14 | func main() { 15 | configFile := flag.String("config", "wsp_server.cfg", "config file path") 16 | flag.Parse() 17 | 18 | // Load configuration 19 | config, err := server.LoadConfiguration(*configFile) 20 | if err != nil { 21 | log.Fatalf("Unable to load configuration : %s", err) 22 | } 23 | utils.Dump(config) 24 | 25 | server := server.NewServer(config) 26 | 27 | // Handle SIGINT 28 | c := make(chan os.Signal, 1) 29 | signal.Notify(c, os.Interrupt) 30 | go func() { 31 | for { 32 | <-c 33 | log.Println("SIGINT Detected") 34 | server.Shutdown() 35 | os.Exit(0) 36 | } 37 | }() 38 | 39 | server.Start() 40 | 41 | select {} 42 | } 43 | --------------------------------------------------------------------------------