├── .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 | 
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 | [](https://travis-ci.org/gorilla/websocket)
7 | [](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 |
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 |
--------------------------------------------------------------------------------