22 |
23 |
24 |
25 |
26 |
27 | Documentation
28 | IRC #coreos
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/third_party/github.com/gorilla/context/context_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 The Gorilla 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 context
6 |
7 | import (
8 | "net/http"
9 | "testing"
10 | )
11 |
12 | type keyType int
13 |
14 | const (
15 | key1 keyType = iota
16 | key2
17 | )
18 |
19 | func TestContext(t *testing.T) {
20 | assertEqual := func(val interface{}, exp interface{}) {
21 | if val != exp {
22 | t.Errorf("Expected %v, got %v.", exp, val)
23 | }
24 | }
25 |
26 | r, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
27 |
28 | // Get()
29 | assertEqual(Get(r, key1), nil)
30 |
31 | // Set()
32 | Set(r, key1, "1")
33 | assertEqual(Get(r, key1), "1")
34 | assertEqual(len(data[r]), 1)
35 |
36 | Set(r, key2, "2")
37 | assertEqual(Get(r, key2), "2")
38 | assertEqual(len(data[r]), 2)
39 |
40 | //GetOk
41 | value, ok := GetOk(r, key1)
42 | assertEqual(value, "1")
43 | assertEqual(ok, true)
44 |
45 | value, ok = GetOk(r, "not exists")
46 | assertEqual(value, nil)
47 | assertEqual(ok, false)
48 |
49 | Set(r, "nil value", nil)
50 | value, ok = GetOk(r, "nil value")
51 | assertEqual(value, nil)
52 | assertEqual(ok, true)
53 |
54 | // Delete()
55 | Delete(r, key1)
56 | assertEqual(Get(r, key1), nil)
57 | assertEqual(len(data[r]), 2)
58 |
59 | Delete(r, key2)
60 | assertEqual(Get(r, key2), nil)
61 | assertEqual(len(data[r]), 1)
62 |
63 | // Clear()
64 | Clear(r)
65 | assertEqual(len(data), 0)
66 | }
67 |
--------------------------------------------------------------------------------
/third_party/github.com/gorilla/context/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
2 |
3 | Redistribution and use in source and binary forms, with or without
4 | modification, are permitted provided that the following conditions are
5 | met:
6 |
7 | * Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 | * Redistributions in binary form must reproduce the above
10 | copyright notice, this list of conditions and the following disclaimer
11 | in the documentation and/or other materials provided with the
12 | distribution.
13 | * Neither the name of Google Inc. nor the names of its
14 | contributors may be used to endorse or promote products derived from
15 | this software without specific prior written permission.
16 |
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
--------------------------------------------------------------------------------
/third_party/github.com/gorilla/mux/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
2 |
3 | Redistribution and use in source and binary forms, with or without
4 | modification, are permitted provided that the following conditions are
5 | met:
6 |
7 | * Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 | * Redistributions in binary form must reproduce the above
10 | copyright notice, this list of conditions and the following disclaimer
11 | in the documentation and/or other materials provided with the
12 | distribution.
13 | * Neither the name of Google Inc. nor the names of its
14 | contributors may be used to endorse or promote products derived from
15 | this software without specific prior written permission.
16 |
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
--------------------------------------------------------------------------------
/dashboard/dist/coreos-web/img/globe-only.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
21 |
--------------------------------------------------------------------------------
/third_party/github.com/gorilla/context/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 The Gorilla 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 | /*
6 | Package gorilla/context stores values shared during a request lifetime.
7 |
8 | For example, a router can set variables extracted from the URL and later
9 | application handlers can access those values, or it can be used to store
10 | sessions values to be saved at the end of a request. There are several
11 | others common uses.
12 |
13 | The idea was posted by Brad Fitzpatrick to the go-nuts mailing list:
14 |
15 | http://groups.google.com/group/golang-nuts/msg/e2d679d303aa5d53
16 |
17 | Here's the basic usage: first define the keys that you will need. The key
18 | type is interface{} so a key can be of any type that supports equality.
19 | Here we define a key using a custom int type to avoid name collisions:
20 |
21 | package foo
22 |
23 | import (
24 | "github.com/gorilla/context"
25 | )
26 |
27 | type key int
28 |
29 | const MyKey key = 0
30 |
31 | Then set a variable. Variables are bound to an http.Request object, so you
32 | need a request instance to set a value:
33 |
34 | context.Set(r, MyKey, "bar")
35 |
36 | The application can later access the variable using the same key you provided:
37 |
38 | func MyHandler(w http.ResponseWriter, r *http.Request) {
39 | // val is "bar".
40 | val := context.Get(r, foo.MyKey)
41 |
42 | // returns ("bar", true)
43 | val, ok := context.GetOk(r, foo.MyKey)
44 | // ...
45 | }
46 |
47 | And that's all about the basic usage. We discuss some other ideas below.
48 |
49 | Any type can be stored in the context. To enforce a given type, make the key
50 | private and wrap Get() and Set() to accept and return values of a specific
51 | type:
52 |
53 | type key int
54 |
55 | const mykey key = 0
56 |
57 | // GetMyKey returns a value for this package from the request values.
58 | func GetMyKey(r *http.Request) SomeType {
59 | if rv := context.Get(r, mykey); rv != nil {
60 | return rv.(SomeType)
61 | }
62 | return nil
63 | }
64 |
65 | // SetMyKey sets a value for this package in the request values.
66 | func SetMyKey(r *http.Request, val SomeType) {
67 | context.Set(r, mykey, val)
68 | }
69 |
70 | Variables must be cleared at the end of a request, to remove all values
71 | that were stored. This can be done in an http.Handler, after a request was
72 | served. Just call Clear() passing the request:
73 |
74 | context.Clear(r)
75 |
76 | ...or use ClearHandler(), which conveniently wraps an http.Handler to clear
77 | variables at the end of a request lifetime.
78 |
79 | The Routers from the packages gorilla/mux and gorilla/pat call Clear()
80 | so if you are using either of them you don't need to clear the context manually.
81 | */
82 | package context
83 |
--------------------------------------------------------------------------------
/third_party/github.com/gorilla/context/context.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 The Gorilla 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 context
6 |
7 | import (
8 | "net/http"
9 | "sync"
10 | "time"
11 | )
12 |
13 | var (
14 | mutex sync.Mutex
15 | data = make(map[*http.Request]map[interface{}]interface{})
16 | datat = make(map[*http.Request]int64)
17 | )
18 |
19 | // Set stores a value for a given key in a given request.
20 | func Set(r *http.Request, key, val interface{}) {
21 | mutex.Lock()
22 | defer mutex.Unlock()
23 | if data[r] == nil {
24 | data[r] = make(map[interface{}]interface{})
25 | datat[r] = time.Now().Unix()
26 | }
27 | data[r][key] = val
28 | }
29 |
30 | // Get returns a value stored for a given key in a given request.
31 | func Get(r *http.Request, key interface{}) interface{} {
32 | mutex.Lock()
33 | defer mutex.Unlock()
34 | if data[r] != nil {
35 | return data[r][key]
36 | }
37 | return nil
38 | }
39 |
40 | // GetOk returns stored value and presence state like multi-value return of map access.
41 | func GetOk(r *http.Request, key interface{}) (interface{}, bool) {
42 | mutex.Lock()
43 | defer mutex.Unlock()
44 | if _, ok := data[r]; ok {
45 | value, ok := data[r][key]
46 | return value, ok
47 | }
48 | return nil, false
49 | }
50 |
51 | // Delete removes a value stored for a given key in a given request.
52 | func Delete(r *http.Request, key interface{}) {
53 | mutex.Lock()
54 | defer mutex.Unlock()
55 | if data[r] != nil {
56 | delete(data[r], key)
57 | }
58 | }
59 |
60 | // Clear removes all values stored for a given request.
61 | //
62 | // This is usually called by a handler wrapper to clean up request
63 | // variables at the end of a request lifetime. See ClearHandler().
64 | func Clear(r *http.Request) {
65 | mutex.Lock()
66 | defer mutex.Unlock()
67 | clear(r)
68 | }
69 |
70 | // clear is Clear without the lock.
71 | func clear(r *http.Request) {
72 | delete(data, r)
73 | delete(datat, r)
74 | }
75 |
76 | // Purge removes request data stored for longer than maxAge, in seconds.
77 | // It returns the amount of requests removed.
78 | //
79 | // If maxAge <= 0, all request data is removed.
80 | //
81 | // This is only used for sanity check: in case context cleaning was not
82 | // properly set some request data can be kept forever, consuming an increasing
83 | // amount of memory. In case this is detected, Purge() must be called
84 | // periodically until the problem is fixed.
85 | func Purge(maxAge int) int {
86 | mutex.Lock()
87 | defer mutex.Unlock()
88 | count := 0
89 | if maxAge <= 0 {
90 | count = len(data)
91 | data = make(map[*http.Request]map[interface{}]interface{})
92 | datat = make(map[*http.Request]int64)
93 | } else {
94 | min := time.Now().Unix() - int64(maxAge)
95 | for r := range data {
96 | if datat[r] < min {
97 | clear(r)
98 | count++
99 | }
100 | }
101 | }
102 | return count
103 | }
104 |
105 | // ClearHandler wraps an http.Handler and clears request values at the end
106 | // of a request lifetime.
107 | func ClearHandler(h http.Handler) http.Handler {
108 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
109 | defer Clear(r)
110 | h.ServeHTTP(w, r)
111 | })
112 | }
113 |
--------------------------------------------------------------------------------
/dashboard/dist/coreos-web/img/logo.svg:
--------------------------------------------------------------------------------
1 |
47 |
--------------------------------------------------------------------------------
/third_party/github.com/gorilla/mux/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 The Gorilla 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 | /*
6 | Package gorilla/mux implements a request router and dispatcher.
7 |
8 | The name mux stands for "HTTP request multiplexer". Like the standard
9 | http.ServeMux, mux.Router matches incoming requests against a list of
10 | registered routes and calls a handler for the route that matches the URL
11 | or other conditions. The main features are:
12 |
13 | * Requests can be matched based on URL host, path, path prefix, schemes,
14 | header and query values, HTTP methods or using custom matchers.
15 | * URL hosts and paths can have variables with an optional regular
16 | expression.
17 | * Registered URLs can be built, or "reversed", which helps maintaining
18 | references to resources.
19 | * Routes can be used as subrouters: nested routes are only tested if the
20 | parent route matches. This is useful to define groups of routes that
21 | share common conditions like a host, a path prefix or other repeated
22 | attributes. As a bonus, this optimizes request matching.
23 | * It implements the http.Handler interface so it is compatible with the
24 | standard http.ServeMux.
25 |
26 | Let's start registering a couple of URL paths and handlers:
27 |
28 | func main() {
29 | r := mux.NewRouter()
30 | r.HandleFunc("/", HomeHandler)
31 | r.HandleFunc("/products", ProductsHandler)
32 | r.HandleFunc("/articles", ArticlesHandler)
33 | http.Handle("/", r)
34 | }
35 |
36 | Here we register three routes mapping URL paths to handlers. This is
37 | equivalent to how http.HandleFunc() works: if an incoming request URL matches
38 | one of the paths, the corresponding handler is called passing
39 | (http.ResponseWriter, *http.Request) as parameters.
40 |
41 | Paths can have variables. They are defined using the format {name} or
42 | {name:pattern}. If a regular expression pattern is not defined, the matched
43 | variable will be anything until the next slash. For example:
44 |
45 | r := mux.NewRouter()
46 | r.HandleFunc("/products/{key}", ProductHandler)
47 | r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler)
48 | r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler)
49 |
50 | The names are used to create a map of route variables which can be retrieved
51 | calling mux.Vars():
52 |
53 | vars := mux.Vars(request)
54 | category := vars["category"]
55 |
56 | And this is all you need to know about the basic usage. More advanced options
57 | are explained below.
58 |
59 | Routes can also be restricted to a domain or subdomain. Just define a host
60 | pattern to be matched. They can also have variables:
61 |
62 | r := mux.NewRouter()
63 | // Only matches if domain is "www.domain.com".
64 | r.Host("www.domain.com")
65 | // Matches a dynamic subdomain.
66 | r.Host("{subdomain:[a-z]+}.domain.com")
67 |
68 | There are several other matchers that can be added. To match path prefixes:
69 |
70 | r.PathPrefix("/products/")
71 |
72 | ...or HTTP methods:
73 |
74 | r.Methods("GET", "POST")
75 |
76 | ...or URL schemes:
77 |
78 | r.Schemes("https")
79 |
80 | ...or header values:
81 |
82 | r.Headers("X-Requested-With", "XMLHttpRequest")
83 |
84 | ...or query values:
85 |
86 | r.Queries("key", "value")
87 |
88 | ...or to use a custom matcher function:
89 |
90 | r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool {
91 | return r.ProtoMajor == 0
92 | })
93 |
94 | ...and finally, it is possible to combine several matchers in a single route:
95 |
96 | r.HandleFunc("/products", ProductsHandler).
97 | Host("www.domain.com").
98 | Methods("GET").
99 | Schemes("http")
100 |
101 | Setting the same matching conditions again and again can be boring, so we have
102 | a way to group several routes that share the same requirements.
103 | We call it "subrouting".
104 |
105 | For example, let's say we have several URLs that should only match when the
106 | host is "www.domain.com". Create a route for that host and get a "subrouter"
107 | from it:
108 |
109 | r := mux.NewRouter()
110 | s := r.Host("www.domain.com").Subrouter()
111 |
112 | Then register routes in the subrouter:
113 |
114 | s.HandleFunc("/products/", ProductsHandler)
115 | s.HandleFunc("/products/{key}", ProductHandler)
116 | s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
117 |
118 | The three URL paths we registered above will only be tested if the domain is
119 | "www.domain.com", because the subrouter is tested first. This is not
120 | only convenient, but also optimizes request matching. You can create
121 | subrouters combining any attribute matchers accepted by a route.
122 |
123 | Subrouters can be used to create domain or path "namespaces": you define
124 | subrouters in a central place and then parts of the app can register its
125 | paths relatively to a given subrouter.
126 |
127 | There's one more thing about subroutes. When a subrouter has a path prefix,
128 | the inner routes use it as base for their paths:
129 |
130 | r := mux.NewRouter()
131 | s := r.PathPrefix("/products").Subrouter()
132 | // "/products/"
133 | s.HandleFunc("/", ProductsHandler)
134 | // "/products/{key}/"
135 | s.HandleFunc("/{key}/", ProductHandler)
136 | // "/products/{key}/details"
137 | s.HandleFunc("/{key}/details", ProductDetailsHandler)
138 |
139 | Now let's see how to build registered URLs.
140 |
141 | Routes can be named. All routes that define a name can have their URLs built,
142 | or "reversed". We define a name calling Name() on a route. For example:
143 |
144 | r := mux.NewRouter()
145 | r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
146 | Name("article")
147 |
148 | To build a URL, get the route and call the URL() method, passing a sequence of
149 | key/value pairs for the route variables. For the previous route, we would do:
150 |
151 | url, err := r.Get("article").URL("category", "technology", "id", "42")
152 |
153 | ...and the result will be a url.URL with the following path:
154 |
155 | "/articles/technology/42"
156 |
157 | This also works for host variables:
158 |
159 | r := mux.NewRouter()
160 | r.Host("{subdomain}.domain.com").
161 | Path("/articles/{category}/{id:[0-9]+}").
162 | HandlerFunc(ArticleHandler).
163 | Name("article")
164 |
165 | // url.String() will be "http://news.domain.com/articles/technology/42"
166 | url, err := r.Get("article").URL("subdomain", "news",
167 | "category", "technology",
168 | "id", "42")
169 |
170 | All variables defined in the route are required, and their values must
171 | conform to the corresponding patterns. These requirements guarantee that a
172 | generated URL will always match a registered route -- the only exception is
173 | for explicitly defined "build-only" routes which never match.
174 |
175 | There's also a way to build only the URL host or path for a route:
176 | use the methods URLHost() or URLPath() instead. For the previous route,
177 | we would do:
178 |
179 | // "http://news.domain.com/"
180 | host, err := r.Get("article").URLHost("subdomain", "news")
181 |
182 | // "/articles/technology/42"
183 | path, err := r.Get("article").URLPath("category", "technology", "id", "42")
184 |
185 | And if you use subrouters, host and path defined separately can be built
186 | as well:
187 |
188 | r := mux.NewRouter()
189 | s := r.Host("{subdomain}.domain.com").Subrouter()
190 | s.Path("/articles/{category}/{id:[0-9]+}").
191 | HandlerFunc(ArticleHandler).
192 | Name("article")
193 |
194 | // "http://news.domain.com/articles/technology/42"
195 | url, err := r.Get("article").URL("subdomain", "news",
196 | "category", "technology",
197 | "id", "42")
198 | */
199 | package mux
200 |
--------------------------------------------------------------------------------
/third_party/github.com/gorilla/mux/regexp.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 The Gorilla 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 mux
6 |
7 | import (
8 | "bytes"
9 | "fmt"
10 | "net/http"
11 | "net/url"
12 | "regexp"
13 | "strings"
14 | )
15 |
16 | // newRouteRegexp parses a route template and returns a routeRegexp,
17 | // used to match a host or path.
18 | //
19 | // It will extract named variables, assemble a regexp to be matched, create
20 | // a "reverse" template to build URLs and compile regexps to validate variable
21 | // values used in URL building.
22 | //
23 | // Previously we accepted only Python-like identifiers for variable
24 | // names ([a-zA-Z_][a-zA-Z0-9_]*), but currently the only restriction is that
25 | // name and pattern can't be empty, and names can't contain a colon.
26 | func newRouteRegexp(tpl string, matchHost, matchPrefix, strictSlash bool) (*routeRegexp, error) {
27 | // Check if it is well-formed.
28 | idxs, errBraces := braceIndices(tpl)
29 | if errBraces != nil {
30 | return nil, errBraces
31 | }
32 | // Backup the original.
33 | template := tpl
34 | // Now let's parse it.
35 | defaultPattern := "[^/]+"
36 | if matchHost {
37 | defaultPattern = "[^.]+"
38 | matchPrefix, strictSlash = false, false
39 | }
40 | if matchPrefix {
41 | strictSlash = false
42 | }
43 | // Set a flag for strictSlash.
44 | endSlash := false
45 | if strictSlash && strings.HasSuffix(tpl, "/") {
46 | tpl = tpl[:len(tpl)-1]
47 | endSlash = true
48 | }
49 | varsN := make([]string, len(idxs)/2)
50 | varsR := make([]*regexp.Regexp, len(idxs)/2)
51 | pattern := bytes.NewBufferString("^")
52 | reverse := bytes.NewBufferString("")
53 | var end int
54 | var err error
55 | for i := 0; i < len(idxs); i += 2 {
56 | // Set all values we are interested in.
57 | raw := tpl[end:idxs[i]]
58 | end = idxs[i+1]
59 | parts := strings.SplitN(tpl[idxs[i]+1:end-1], ":", 2)
60 | name := parts[0]
61 | patt := defaultPattern
62 | if len(parts) == 2 {
63 | patt = parts[1]
64 | }
65 | // Name or pattern can't be empty.
66 | if name == "" || patt == "" {
67 | return nil, fmt.Errorf("mux: missing name or pattern in %q",
68 | tpl[idxs[i]:end])
69 | }
70 | // Build the regexp pattern.
71 | fmt.Fprintf(pattern, "%s(%s)", regexp.QuoteMeta(raw), patt)
72 | // Build the reverse template.
73 | fmt.Fprintf(reverse, "%s%%s", raw)
74 | // Append variable name and compiled pattern.
75 | varsN[i/2] = name
76 | varsR[i/2], err = regexp.Compile(fmt.Sprintf("^%s$", patt))
77 | if err != nil {
78 | return nil, err
79 | }
80 | }
81 | // Add the remaining.
82 | raw := tpl[end:]
83 | pattern.WriteString(regexp.QuoteMeta(raw))
84 | if strictSlash {
85 | pattern.WriteString("[/]?")
86 | }
87 | if !matchPrefix {
88 | pattern.WriteByte('$')
89 | }
90 | reverse.WriteString(raw)
91 | if endSlash {
92 | reverse.WriteByte('/')
93 | }
94 | // Compile full regexp.
95 | reg, errCompile := regexp.Compile(pattern.String())
96 | if errCompile != nil {
97 | return nil, errCompile
98 | }
99 | // Done!
100 | return &routeRegexp{
101 | template: template,
102 | matchHost: matchHost,
103 | regexp: reg,
104 | reverse: reverse.String(),
105 | varsN: varsN,
106 | varsR: varsR,
107 | }, nil
108 | }
109 |
110 | // routeRegexp stores a regexp to match a host or path and information to
111 | // collect and validate route variables.
112 | type routeRegexp struct {
113 | // The unmodified template.
114 | template string
115 | // True for host match, false for path match.
116 | matchHost bool
117 | // Expanded regexp.
118 | regexp *regexp.Regexp
119 | // Reverse template.
120 | reverse string
121 | // Variable names.
122 | varsN []string
123 | // Variable regexps (validators).
124 | varsR []*regexp.Regexp
125 | }
126 |
127 | // Match matches the regexp against the URL host or path.
128 | func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool {
129 | if !r.matchHost {
130 | return r.regexp.MatchString(req.URL.Path)
131 | }
132 | return r.regexp.MatchString(getHost(req))
133 | }
134 |
135 | // url builds a URL part using the given values.
136 | func (r *routeRegexp) url(pairs ...string) (string, error) {
137 | values, err := mapFromPairs(pairs...)
138 | if err != nil {
139 | return "", err
140 | }
141 | urlValues := make([]interface{}, len(r.varsN))
142 | for k, v := range r.varsN {
143 | value, ok := values[v]
144 | if !ok {
145 | return "", fmt.Errorf("mux: missing route variable %q", v)
146 | }
147 | urlValues[k] = value
148 | }
149 | rv := fmt.Sprintf(r.reverse, urlValues...)
150 | if !r.regexp.MatchString(rv) {
151 | // The URL is checked against the full regexp, instead of checking
152 | // individual variables. This is faster but to provide a good error
153 | // message, we check individual regexps if the URL doesn't match.
154 | for k, v := range r.varsN {
155 | if !r.varsR[k].MatchString(values[v]) {
156 | return "", fmt.Errorf(
157 | "mux: variable %q doesn't match, expected %q", values[v],
158 | r.varsR[k].String())
159 | }
160 | }
161 | }
162 | return rv, nil
163 | }
164 |
165 | // braceIndices returns the first level curly brace indices from a string.
166 | // It returns an error in case of unbalanced braces.
167 | func braceIndices(s string) ([]int, error) {
168 | var level, idx int
169 | idxs := make([]int, 0)
170 | for i := 0; i < len(s); i++ {
171 | switch s[i] {
172 | case '{':
173 | if level++; level == 1 {
174 | idx = i
175 | }
176 | case '}':
177 | if level--; level == 0 {
178 | idxs = append(idxs, idx, i+1)
179 | } else if level < 0 {
180 | return nil, fmt.Errorf("mux: unbalanced braces in %q", s)
181 | }
182 | }
183 | }
184 | if level != 0 {
185 | return nil, fmt.Errorf("mux: unbalanced braces in %q", s)
186 | }
187 | return idxs, nil
188 | }
189 |
190 | // ----------------------------------------------------------------------------
191 | // routeRegexpGroup
192 | // ----------------------------------------------------------------------------
193 |
194 | // routeRegexpGroup groups the route matchers that carry variables.
195 | type routeRegexpGroup struct {
196 | host *routeRegexp
197 | path *routeRegexp
198 | }
199 |
200 | // setMatch extracts the variables from the URL once a route matches.
201 | func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route) {
202 | // Store host variables.
203 | if v.host != nil {
204 | hostVars := v.host.regexp.FindStringSubmatch(getHost(req))
205 | if hostVars != nil {
206 | for k, v := range v.host.varsN {
207 | m.Vars[v] = hostVars[k+1]
208 | }
209 | }
210 | }
211 | // Store path variables.
212 | if v.path != nil {
213 | pathVars := v.path.regexp.FindStringSubmatch(req.URL.Path)
214 | if pathVars != nil {
215 | for k, v := range v.path.varsN {
216 | m.Vars[v] = pathVars[k+1]
217 | }
218 | // Check if we should redirect.
219 | if r.strictSlash {
220 | p1 := strings.HasSuffix(req.URL.Path, "/")
221 | p2 := strings.HasSuffix(v.path.template, "/")
222 | if p1 != p2 {
223 | u, _ := url.Parse(req.URL.String())
224 | if p1 {
225 | u.Path = u.Path[:len(u.Path)-1]
226 | } else {
227 | u.Path += "/"
228 | }
229 | m.Handler = http.RedirectHandler(u.String(), 301)
230 | }
231 | }
232 | }
233 | }
234 | }
235 |
236 | // getHost tries its best to return the request host.
237 | func getHost(r *http.Request) string {
238 | if !r.URL.IsAbs() {
239 | host := r.Host
240 | // Slice off any port information.
241 | if i := strings.Index(host, ":"); i != -1 {
242 | host = host[:i]
243 | }
244 | return host
245 | }
246 | return r.URL.Host
247 | }
248 |
--------------------------------------------------------------------------------
/third_party/github.com/gorilla/mux/mux.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 The Gorilla 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 mux
6 |
7 | import (
8 | "fmt"
9 | "net/http"
10 | "path"
11 |
12 | "github.com/AdoHe/etcd-dashboard/third_party/github.com/gorilla/context"
13 | )
14 |
15 | // NewRouter returns a new router instance.
16 | func NewRouter() *Router {
17 | return &Router{namedRoutes: make(map[string]*Route), KeepContext: false}
18 | }
19 |
20 | // Router registers routes to be matched and dispatches a handler.
21 | //
22 | // It implements the http.Handler interface, so it can be registered to serve
23 | // requests:
24 | //
25 | // var router = mux.NewRouter()
26 | //
27 | // func main() {
28 | // http.Handle("/", router)
29 | // }
30 | //
31 | // Or, for Google App Engine, register it in a init() function:
32 | //
33 | // func init() {
34 | // http.Handle("/", router)
35 | // }
36 | //
37 | // This will send all incoming requests to the router.
38 | type Router struct {
39 | // Configurable Handler to be used when no route matches.
40 | NotFoundHandler http.Handler
41 | // Parent route, if this is a subrouter.
42 | parent parentRoute
43 | // Routes to be matched, in order.
44 | routes []*Route
45 | // Routes by name for URL building.
46 | namedRoutes map[string]*Route
47 | // See Router.StrictSlash(). This defines the flag for new routes.
48 | strictSlash bool
49 | // If true, do not clear the request context after handling the request
50 | KeepContext bool
51 | }
52 |
53 | // Match matches registered routes against the request.
54 | func (r *Router) Match(req *http.Request, match *RouteMatch) bool {
55 | for _, route := range r.routes {
56 | if route.Match(req, match) {
57 | return true
58 | }
59 | }
60 | return false
61 | }
62 |
63 | // ServeHTTP dispatches the handler registered in the matched route.
64 | //
65 | // When there is a match, the route variables can be retrieved calling
66 | // mux.Vars(request).
67 | func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
68 | // Clean path to canonical form and redirect.
69 | if p := cleanPath(req.URL.Path); p != req.URL.Path {
70 |
71 | // Added 3 lines (Philip Schlump) - It was droping the query string and #whatever from query.
72 | // This matches with fix in go 1.2 r.c. 4 for same problem. Go Issue:
73 | // http://code.google.com/p/go/issues/detail?id=5252
74 | url := *req.URL
75 | url.Path = p
76 | p = url.String()
77 |
78 | w.Header().Set("Location", p)
79 | w.WriteHeader(http.StatusMovedPermanently)
80 | return
81 | }
82 | var match RouteMatch
83 | var handler http.Handler
84 | if r.Match(req, &match) {
85 | handler = match.Handler
86 | setVars(req, match.Vars)
87 | setCurrentRoute(req, match.Route)
88 | }
89 | if handler == nil {
90 | if r.NotFoundHandler == nil {
91 | r.NotFoundHandler = http.NotFoundHandler()
92 | }
93 | handler = r.NotFoundHandler
94 | }
95 | if !r.KeepContext {
96 | defer context.Clear(req)
97 | }
98 | handler.ServeHTTP(w, req)
99 | }
100 |
101 | // Get returns a route registered with the given name.
102 | func (r *Router) Get(name string) *Route {
103 | return r.getNamedRoutes()[name]
104 | }
105 |
106 | // GetRoute returns a route registered with the given name. This method
107 | // was renamed to Get() and remains here for backwards compatibility.
108 | func (r *Router) GetRoute(name string) *Route {
109 | return r.getNamedRoutes()[name]
110 | }
111 |
112 | // StrictSlash defines the slash behavior for new routes.
113 | //
114 | // When true, if the route path is "/path/", accessing "/path" will redirect
115 | // to the former and vice versa.
116 | //
117 | // Special case: when a route sets a path prefix, strict slash is
118 | // automatically set to false for that route because the redirect behavior
119 | // can't be determined for prefixes.
120 | func (r *Router) StrictSlash(value bool) *Router {
121 | r.strictSlash = value
122 | return r
123 | }
124 |
125 | // ----------------------------------------------------------------------------
126 | // parentRoute
127 | // ----------------------------------------------------------------------------
128 |
129 | // getNamedRoutes returns the map where named routes are registered.
130 | func (r *Router) getNamedRoutes() map[string]*Route {
131 | if r.namedRoutes == nil {
132 | if r.parent != nil {
133 | r.namedRoutes = r.parent.getNamedRoutes()
134 | } else {
135 | r.namedRoutes = make(map[string]*Route)
136 | }
137 | }
138 | return r.namedRoutes
139 | }
140 |
141 | // getRegexpGroup returns regexp definitions from the parent route, if any.
142 | func (r *Router) getRegexpGroup() *routeRegexpGroup {
143 | if r.parent != nil {
144 | return r.parent.getRegexpGroup()
145 | }
146 | return nil
147 | }
148 |
149 | // ----------------------------------------------------------------------------
150 | // Route factories
151 | // ----------------------------------------------------------------------------
152 |
153 | // NewRoute registers an empty route.
154 | func (r *Router) NewRoute() *Route {
155 | route := &Route{parent: r, strictSlash: r.strictSlash}
156 | r.routes = append(r.routes, route)
157 | return route
158 | }
159 |
160 | // Handle registers a new route with a matcher for the URL path.
161 | // See Route.Path() and Route.Handler().
162 | func (r *Router) Handle(path string, handler http.Handler) *Route {
163 | return r.NewRoute().Path(path).Handler(handler)
164 | }
165 |
166 | // HandleFunc registers a new route with a matcher for the URL path.
167 | // See Route.Path() and Route.HandlerFunc().
168 | func (r *Router) HandleFunc(path string, f func(http.ResponseWriter,
169 | *http.Request)) *Route {
170 | return r.NewRoute().Path(path).HandlerFunc(f)
171 | }
172 |
173 | // Headers registers a new route with a matcher for request header values.
174 | // See Route.Headers().
175 | func (r *Router) Headers(pairs ...string) *Route {
176 | return r.NewRoute().Headers(pairs...)
177 | }
178 |
179 | // Host registers a new route with a matcher for the URL host.
180 | // See Route.Host().
181 | func (r *Router) Host(tpl string) *Route {
182 | return r.NewRoute().Host(tpl)
183 | }
184 |
185 | // MatcherFunc registers a new route with a custom matcher function.
186 | // See Route.MatcherFunc().
187 | func (r *Router) MatcherFunc(f MatcherFunc) *Route {
188 | return r.NewRoute().MatcherFunc(f)
189 | }
190 |
191 | // Methods registers a new route with a matcher for HTTP methods.
192 | // See Route.Methods().
193 | func (r *Router) Methods(methods ...string) *Route {
194 | return r.NewRoute().Methods(methods...)
195 | }
196 |
197 | // Path registers a new route with a matcher for the URL path.
198 | // See Route.Path().
199 | func (r *Router) Path(tpl string) *Route {
200 | return r.NewRoute().Path(tpl)
201 | }
202 |
203 | // PathPrefix registers a new route with a matcher for the URL path prefix.
204 | // See Route.PathPrefix().
205 | func (r *Router) PathPrefix(tpl string) *Route {
206 | return r.NewRoute().PathPrefix(tpl)
207 | }
208 |
209 | // Queries registers a new route with a matcher for URL query values.
210 | // See Route.Queries().
211 | func (r *Router) Queries(pairs ...string) *Route {
212 | return r.NewRoute().Queries(pairs...)
213 | }
214 |
215 | // Schemes registers a new route with a matcher for URL schemes.
216 | // See Route.Schemes().
217 | func (r *Router) Schemes(schemes ...string) *Route {
218 | return r.NewRoute().Schemes(schemes...)
219 | }
220 |
221 | // ----------------------------------------------------------------------------
222 | // Context
223 | // ----------------------------------------------------------------------------
224 |
225 | // RouteMatch stores information about a matched route.
226 | type RouteMatch struct {
227 | Route *Route
228 | Handler http.Handler
229 | Vars map[string]string
230 | }
231 |
232 | type contextKey int
233 |
234 | const (
235 | varsKey contextKey = iota
236 | routeKey
237 | )
238 |
239 | // Vars returns the route variables for the current request, if any.
240 | func Vars(r *http.Request) map[string]string {
241 | if rv := context.Get(r, varsKey); rv != nil {
242 | return rv.(map[string]string)
243 | }
244 | return nil
245 | }
246 |
247 | // CurrentRoute returns the matched route for the current request, if any.
248 | func CurrentRoute(r *http.Request) *Route {
249 | if rv := context.Get(r, routeKey); rv != nil {
250 | return rv.(*Route)
251 | }
252 | return nil
253 | }
254 |
255 | func setVars(r *http.Request, val interface{}) {
256 | context.Set(r, varsKey, val)
257 | }
258 |
259 | func setCurrentRoute(r *http.Request, val interface{}) {
260 | context.Set(r, routeKey, val)
261 | }
262 |
263 | // ----------------------------------------------------------------------------
264 | // Helpers
265 | // ----------------------------------------------------------------------------
266 |
267 | // cleanPath returns the canonical path for p, eliminating . and .. elements.
268 | // Borrowed from the net/http package.
269 | func cleanPath(p string) string {
270 | if p == "" {
271 | return "/"
272 | }
273 | if p[0] != '/' {
274 | p = "/" + p
275 | }
276 | np := path.Clean(p)
277 | // path.Clean removes trailing slash except for root;
278 | // put the trailing slash back if necessary.
279 | if p[len(p)-1] == '/' && np != "/" {
280 | np += "/"
281 | }
282 | return np
283 | }
284 |
285 | // uniqueVars returns an error if two slices contain duplicated strings.
286 | func uniqueVars(s1, s2 []string) error {
287 | for _, v1 := range s1 {
288 | for _, v2 := range s2 {
289 | if v1 == v2 {
290 | return fmt.Errorf("mux: duplicated route variable %q", v2)
291 | }
292 | }
293 | }
294 | return nil
295 | }
296 |
297 | // mapFromPairs converts variadic string parameters to a string map.
298 | func mapFromPairs(pairs ...string) (map[string]string, error) {
299 | length := len(pairs)
300 | if length%2 != 0 {
301 | return nil, fmt.Errorf(
302 | "mux: number of parameters must be multiple of 2, got %v", pairs)
303 | }
304 | m := make(map[string]string, length/2)
305 | for i := 0; i < length; i += 2 {
306 | m[pairs[i]] = pairs[i+1]
307 | }
308 | return m, nil
309 | }
310 |
311 | // matchInArray returns true if the given string value is in the array.
312 | func matchInArray(arr []string, value string) bool {
313 | for _, v := range arr {
314 | if v == value {
315 | return true
316 | }
317 | }
318 | return false
319 | }
320 |
321 | // matchMap returns true if the given key/value pairs exist in a given map.
322 | func matchMap(toCheck map[string]string, toMatch map[string][]string,
323 | canonicalKey bool) bool {
324 | for k, v := range toCheck {
325 | // Check if key exists.
326 | if canonicalKey {
327 | k = http.CanonicalHeaderKey(k)
328 | }
329 | if values := toMatch[k]; values == nil {
330 | return false
331 | } else if v != "" {
332 | // If value was defined as an empty string we only check that the
333 | // key exists. Otherwise we also check for equality.
334 | valueExists := false
335 | for _, value := range values {
336 | if v == value {
337 | valueExists = true
338 | break
339 | }
340 | }
341 | if !valueExists {
342 | return false
343 | }
344 | }
345 | }
346 | return true
347 | }
348 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
203 |
--------------------------------------------------------------------------------
/third_party/github.com/gorilla/mux/route.go:
--------------------------------------------------------------------------------
1 | // Copyright 2012 The Gorilla 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 mux
6 |
7 | import (
8 | "errors"
9 | "fmt"
10 | "net/http"
11 | "net/url"
12 | "strings"
13 | )
14 |
15 | // Route stores information to match a request and build URLs.
16 | type Route struct {
17 | // Parent where the route was registered (a Router).
18 | parent parentRoute
19 | // Request handler for the route.
20 | handler http.Handler
21 | // List of matchers.
22 | matchers []matcher
23 | // Manager for the variables from host and path.
24 | regexp *routeRegexpGroup
25 | // If true, when the path pattern is "/path/", accessing "/path" will
26 | // redirect to the former and vice versa.
27 | strictSlash bool
28 | // If true, this route never matches: it is only used to build URLs.
29 | buildOnly bool
30 | // The name used to build URLs.
31 | name string
32 | // Error resulted from building a route.
33 | err error
34 | }
35 |
36 | // Match matches the route against the request.
37 | func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
38 | if r.buildOnly || r.err != nil {
39 | return false
40 | }
41 | // Match everything.
42 | for _, m := range r.matchers {
43 | if matched := m.Match(req, match); !matched {
44 | return false
45 | }
46 | }
47 | // Yay, we have a match. Let's collect some info about it.
48 | if match.Route == nil {
49 | match.Route = r
50 | }
51 | if match.Handler == nil {
52 | match.Handler = r.handler
53 | }
54 | if match.Vars == nil {
55 | match.Vars = make(map[string]string)
56 | }
57 | // Set variables.
58 | if r.regexp != nil {
59 | r.regexp.setMatch(req, match, r)
60 | }
61 | return true
62 | }
63 |
64 | // ----------------------------------------------------------------------------
65 | // Route attributes
66 | // ----------------------------------------------------------------------------
67 |
68 | // GetError returns an error resulted from building the route, if any.
69 | func (r *Route) GetError() error {
70 | return r.err
71 | }
72 |
73 | // BuildOnly sets the route to never match: it is only used to build URLs.
74 | func (r *Route) BuildOnly() *Route {
75 | r.buildOnly = true
76 | return r
77 | }
78 |
79 | // Handler --------------------------------------------------------------------
80 |
81 | // Handler sets a handler for the route.
82 | func (r *Route) Handler(handler http.Handler) *Route {
83 | if r.err == nil {
84 | r.handler = handler
85 | }
86 | return r
87 | }
88 |
89 | // HandlerFunc sets a handler function for the route.
90 | func (r *Route) HandlerFunc(f func(http.ResponseWriter, *http.Request)) *Route {
91 | return r.Handler(http.HandlerFunc(f))
92 | }
93 |
94 | // GetHandler returns the handler for the route, if any.
95 | func (r *Route) GetHandler() http.Handler {
96 | return r.handler
97 | }
98 |
99 | // Name -----------------------------------------------------------------------
100 |
101 | // Name sets the name for the route, used to build URLs.
102 | // If the name was registered already it will be overwritten.
103 | func (r *Route) Name(name string) *Route {
104 | if r.name != "" {
105 | r.err = fmt.Errorf("mux: route already has name %q, can't set %q",
106 | r.name, name)
107 | }
108 | if r.err == nil {
109 | r.name = name
110 | r.getNamedRoutes()[name] = r
111 | }
112 | return r
113 | }
114 |
115 | // GetName returns the name for the route, if any.
116 | func (r *Route) GetName() string {
117 | return r.name
118 | }
119 |
120 | // ----------------------------------------------------------------------------
121 | // Matchers
122 | // ----------------------------------------------------------------------------
123 |
124 | // matcher types try to match a request.
125 | type matcher interface {
126 | Match(*http.Request, *RouteMatch) bool
127 | }
128 |
129 | // addMatcher adds a matcher to the route.
130 | func (r *Route) addMatcher(m matcher) *Route {
131 | if r.err == nil {
132 | r.matchers = append(r.matchers, m)
133 | }
134 | return r
135 | }
136 |
137 | // addRegexpMatcher adds a host or path matcher and builder to a route.
138 | func (r *Route) addRegexpMatcher(tpl string, matchHost, matchPrefix bool) error {
139 | if r.err != nil {
140 | return r.err
141 | }
142 | r.regexp = r.getRegexpGroup()
143 | if !matchHost {
144 | if len(tpl) == 0 || tpl[0] != '/' {
145 | return fmt.Errorf("mux: path must start with a slash, got %q", tpl)
146 | }
147 | if r.regexp.path != nil {
148 | tpl = strings.TrimRight(r.regexp.path.template, "/") + tpl
149 | }
150 | }
151 | rr, err := newRouteRegexp(tpl, matchHost, matchPrefix, r.strictSlash)
152 | if err != nil {
153 | return err
154 | }
155 | if matchHost {
156 | if r.regexp.path != nil {
157 | if err = uniqueVars(rr.varsN, r.regexp.path.varsN); err != nil {
158 | return err
159 | }
160 | }
161 | r.regexp.host = rr
162 | } else {
163 | if r.regexp.host != nil {
164 | if err = uniqueVars(rr.varsN, r.regexp.host.varsN); err != nil {
165 | return err
166 | }
167 | }
168 | r.regexp.path = rr
169 | }
170 | r.addMatcher(rr)
171 | return nil
172 | }
173 |
174 | // Headers --------------------------------------------------------------------
175 |
176 | // headerMatcher matches the request against header values.
177 | type headerMatcher map[string]string
178 |
179 | func (m headerMatcher) Match(r *http.Request, match *RouteMatch) bool {
180 | return matchMap(m, r.Header, true)
181 | }
182 |
183 | // Headers adds a matcher for request header values.
184 | // It accepts a sequence of key/value pairs to be matched. For example:
185 | //
186 | // r := mux.NewRouter()
187 | // r.Headers("Content-Type", "application/json",
188 | // "X-Requested-With", "XMLHttpRequest")
189 | //
190 | // The above route will only match if both request header values match.
191 | //
192 | // It the value is an empty string, it will match any value if the key is set.
193 | func (r *Route) Headers(pairs ...string) *Route {
194 | if r.err == nil {
195 | var headers map[string]string
196 | headers, r.err = mapFromPairs(pairs...)
197 | return r.addMatcher(headerMatcher(headers))
198 | }
199 | return r
200 | }
201 |
202 | // Host -----------------------------------------------------------------------
203 |
204 | // Host adds a matcher for the URL host.
205 | // It accepts a template with zero or more URL variables enclosed by {}.
206 | // Variables can define an optional regexp pattern to me matched:
207 | //
208 | // - {name} matches anything until the next dot.
209 | //
210 | // - {name:pattern} matches the given regexp pattern.
211 | //
212 | // For example:
213 | //
214 | // r := mux.NewRouter()
215 | // r.Host("www.domain.com")
216 | // r.Host("{subdomain}.domain.com")
217 | // r.Host("{subdomain:[a-z]+}.domain.com")
218 | //
219 | // Variable names must be unique in a given route. They can be retrieved
220 | // calling mux.Vars(request).
221 | func (r *Route) Host(tpl string) *Route {
222 | r.err = r.addRegexpMatcher(tpl, true, false)
223 | return r
224 | }
225 |
226 | // MatcherFunc ----------------------------------------------------------------
227 |
228 | // MatcherFunc is the function signature used by custom matchers.
229 | type MatcherFunc func(*http.Request, *RouteMatch) bool
230 |
231 | func (m MatcherFunc) Match(r *http.Request, match *RouteMatch) bool {
232 | return m(r, match)
233 | }
234 |
235 | // MatcherFunc adds a custom function to be used as request matcher.
236 | func (r *Route) MatcherFunc(f MatcherFunc) *Route {
237 | return r.addMatcher(f)
238 | }
239 |
240 | // Methods --------------------------------------------------------------------
241 |
242 | // methodMatcher matches the request against HTTP methods.
243 | type methodMatcher []string
244 |
245 | func (m methodMatcher) Match(r *http.Request, match *RouteMatch) bool {
246 | return matchInArray(m, r.Method)
247 | }
248 |
249 | // Methods adds a matcher for HTTP methods.
250 | // It accepts a sequence of one or more methods to be matched, e.g.:
251 | // "GET", "POST", "PUT".
252 | func (r *Route) Methods(methods ...string) *Route {
253 | for k, v := range methods {
254 | methods[k] = strings.ToUpper(v)
255 | }
256 | return r.addMatcher(methodMatcher(methods))
257 | }
258 |
259 | // Path -----------------------------------------------------------------------
260 |
261 | // Path adds a matcher for the URL path.
262 | // It accepts a template with zero or more URL variables enclosed by {}.
263 | // Variables can define an optional regexp pattern to me matched:
264 | //
265 | // - {name} matches anything until the next slash.
266 | //
267 | // - {name:pattern} matches the given regexp pattern.
268 | //
269 | // For example:
270 | //
271 | // r := mux.NewRouter()
272 | // r.Path("/products/").Handler(ProductsHandler)
273 | // r.Path("/products/{key}").Handler(ProductsHandler)
274 | // r.Path("/articles/{category}/{id:[0-9]+}").
275 | // Handler(ArticleHandler)
276 | //
277 | // Variable names must be unique in a given route. They can be retrieved
278 | // calling mux.Vars(request).
279 | func (r *Route) Path(tpl string) *Route {
280 | r.err = r.addRegexpMatcher(tpl, false, false)
281 | return r
282 | }
283 |
284 | // PathPrefix -----------------------------------------------------------------
285 |
286 | // PathPrefix adds a matcher for the URL path prefix.
287 | func (r *Route) PathPrefix(tpl string) *Route {
288 | r.strictSlash = false
289 | r.err = r.addRegexpMatcher(tpl, false, true)
290 | return r
291 | }
292 |
293 | // Query ----------------------------------------------------------------------
294 |
295 | // queryMatcher matches the request against URL queries.
296 | type queryMatcher map[string]string
297 |
298 | func (m queryMatcher) Match(r *http.Request, match *RouteMatch) bool {
299 | return matchMap(m, r.URL.Query(), false)
300 | }
301 |
302 | // Queries adds a matcher for URL query values.
303 | // It accepts a sequence of key/value pairs. For example:
304 | //
305 | // r := mux.NewRouter()
306 | // r.Queries("foo", "bar", "baz", "ding")
307 | //
308 | // The above route will only match if the URL contains the defined queries
309 | // values, e.g.: ?foo=bar&baz=ding.
310 | //
311 | // It the value is an empty string, it will match any value if the key is set.
312 | func (r *Route) Queries(pairs ...string) *Route {
313 | if r.err == nil {
314 | var queries map[string]string
315 | queries, r.err = mapFromPairs(pairs...)
316 | return r.addMatcher(queryMatcher(queries))
317 | }
318 | return r
319 | }
320 |
321 | // Schemes --------------------------------------------------------------------
322 |
323 | // schemeMatcher matches the request against URL schemes.
324 | type schemeMatcher []string
325 |
326 | func (m schemeMatcher) Match(r *http.Request, match *RouteMatch) bool {
327 | return matchInArray(m, r.URL.Scheme)
328 | }
329 |
330 | // Schemes adds a matcher for URL schemes.
331 | // It accepts a sequence of schemes to be matched, e.g.: "http", "https".
332 | func (r *Route) Schemes(schemes ...string) *Route {
333 | for k, v := range schemes {
334 | schemes[k] = strings.ToLower(v)
335 | }
336 | return r.addMatcher(schemeMatcher(schemes))
337 | }
338 |
339 | // Subrouter ------------------------------------------------------------------
340 |
341 | // Subrouter creates a subrouter for the route.
342 | //
343 | // It will test the inner routes only if the parent route matched. For example:
344 | //
345 | // r := mux.NewRouter()
346 | // s := r.Host("www.domain.com").Subrouter()
347 | // s.HandleFunc("/products/", ProductsHandler)
348 | // s.HandleFunc("/products/{key}", ProductHandler)
349 | // s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
350 | //
351 | // Here, the routes registered in the subrouter won't be tested if the host
352 | // doesn't match.
353 | func (r *Route) Subrouter() *Router {
354 | router := &Router{parent: r, strictSlash: r.strictSlash}
355 | r.addMatcher(router)
356 | return router
357 | }
358 |
359 | // ----------------------------------------------------------------------------
360 | // URL building
361 | // ----------------------------------------------------------------------------
362 |
363 | // URL builds a URL for the route.
364 | //
365 | // It accepts a sequence of key/value pairs for the route variables. For
366 | // example, given this route:
367 | //
368 | // r := mux.NewRouter()
369 | // r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
370 | // Name("article")
371 | //
372 | // ...a URL for it can be built using:
373 | //
374 | // url, err := r.Get("article").URL("category", "technology", "id", "42")
375 | //
376 | // ...which will return an url.URL with the following path:
377 | //
378 | // "/articles/technology/42"
379 | //
380 | // This also works for host variables:
381 | //
382 | // r := mux.NewRouter()
383 | // r.Host("{subdomain}.domain.com").
384 | // HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
385 | // Name("article")
386 | //
387 | // // url.String() will be "http://news.domain.com/articles/technology/42"
388 | // url, err := r.Get("article").URL("subdomain", "news",
389 | // "category", "technology",
390 | // "id", "42")
391 | //
392 | // All variables defined in the route are required, and their values must
393 | // conform to the corresponding patterns.
394 | func (r *Route) URL(pairs ...string) (*url.URL, error) {
395 | if r.err != nil {
396 | return nil, r.err
397 | }
398 | if r.regexp == nil {
399 | return nil, errors.New("mux: route doesn't have a host or path")
400 | }
401 | var scheme, host, path string
402 | var err error
403 | if r.regexp.host != nil {
404 | // Set a default scheme.
405 | scheme = "http"
406 | if host, err = r.regexp.host.url(pairs...); err != nil {
407 | return nil, err
408 | }
409 | }
410 | if r.regexp.path != nil {
411 | if path, err = r.regexp.path.url(pairs...); err != nil {
412 | return nil, err
413 | }
414 | }
415 | return &url.URL{
416 | Scheme: scheme,
417 | Host: host,
418 | Path: path,
419 | }, nil
420 | }
421 |
422 | // URLHost builds the host part of the URL for a route. See Route.URL().
423 | //
424 | // The route must have a host defined.
425 | func (r *Route) URLHost(pairs ...string) (*url.URL, error) {
426 | if r.err != nil {
427 | return nil, r.err
428 | }
429 | if r.regexp == nil || r.regexp.host == nil {
430 | return nil, errors.New("mux: route doesn't have a host")
431 | }
432 | host, err := r.regexp.host.url(pairs...)
433 | if err != nil {
434 | return nil, err
435 | }
436 | return &url.URL{
437 | Scheme: "http",
438 | Host: host,
439 | }, nil
440 | }
441 |
442 | // URLPath builds the path part of the URL for a route. See Route.URL().
443 | //
444 | // The route must have a path defined.
445 | func (r *Route) URLPath(pairs ...string) (*url.URL, error) {
446 | if r.err != nil {
447 | return nil, r.err
448 | }
449 | if r.regexp == nil || r.regexp.path == nil {
450 | return nil, errors.New("mux: route doesn't have a path")
451 | }
452 | path, err := r.regexp.path.url(pairs...)
453 | if err != nil {
454 | return nil, err
455 | }
456 | return &url.URL{
457 | Path: path,
458 | }, nil
459 | }
460 |
461 | // ----------------------------------------------------------------------------
462 | // parentRoute
463 | // ----------------------------------------------------------------------------
464 |
465 | // parentRoute allows routes to know about parent host and path definitions.
466 | type parentRoute interface {
467 | getNamedRoutes() map[string]*Route
468 | getRegexpGroup() *routeRegexpGroup
469 | }
470 |
471 | // getNamedRoutes returns the map where named routes are registered.
472 | func (r *Route) getNamedRoutes() map[string]*Route {
473 | if r.parent == nil {
474 | // During tests router is not always set.
475 | r.parent = NewRouter()
476 | }
477 | return r.parent.getNamedRoutes()
478 | }
479 |
480 | // getRegexpGroup returns regexp definitions from this route.
481 | func (r *Route) getRegexpGroup() *routeRegexpGroup {
482 | if r.regexp == nil {
483 | if r.parent == nil {
484 | // During tests router is not always set.
485 | r.parent = NewRouter()
486 | }
487 | regexp := r.parent.getRegexpGroup()
488 | if regexp == nil {
489 | r.regexp = new(routeRegexpGroup)
490 | } else {
491 | // Copy.
492 | r.regexp = &routeRegexpGroup{
493 | host: regexp.host,
494 | path: regexp.path,
495 | }
496 | }
497 | }
498 | return r.regexp
499 | }
500 |
--------------------------------------------------------------------------------
/third_party/github.com/gorilla/mux/old_test.go:
--------------------------------------------------------------------------------
1 | // Old tests ported to Go1. This is a mess. Want to drop it one day.
2 |
3 | // Copyright 2011 Gorilla Authors. All rights reserved.
4 | // Use of this source code is governed by a BSD-style
5 | // license that can be found in the LICENSE file.
6 |
7 | package mux
8 |
9 | import (
10 | "bytes"
11 | "net/http"
12 | "testing"
13 | )
14 |
15 | // ----------------------------------------------------------------------------
16 | // ResponseRecorder
17 | // ----------------------------------------------------------------------------
18 | // Copyright 2009 The Go Authors. All rights reserved.
19 | // Use of this source code is governed by a BSD-style
20 | // license that can be found in the LICENSE file.
21 |
22 | // ResponseRecorder is an implementation of http.ResponseWriter that
23 | // records its mutations for later inspection in tests.
24 | type ResponseRecorder struct {
25 | Code int // the HTTP response code from WriteHeader
26 | HeaderMap http.Header // the HTTP response headers
27 | Body *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to
28 | Flushed bool
29 | }
30 |
31 | // NewRecorder returns an initialized ResponseRecorder.
32 | func NewRecorder() *ResponseRecorder {
33 | return &ResponseRecorder{
34 | HeaderMap: make(http.Header),
35 | Body: new(bytes.Buffer),
36 | }
37 | }
38 |
39 | // DefaultRemoteAddr is the default remote address to return in RemoteAddr if
40 | // an explicit DefaultRemoteAddr isn't set on ResponseRecorder.
41 | const DefaultRemoteAddr = "1.2.3.4"
42 |
43 | // Header returns the response headers.
44 | func (rw *ResponseRecorder) Header() http.Header {
45 | return rw.HeaderMap
46 | }
47 |
48 | // Write always succeeds and writes to rw.Body, if not nil.
49 | func (rw *ResponseRecorder) Write(buf []byte) (int, error) {
50 | if rw.Body != nil {
51 | rw.Body.Write(buf)
52 | }
53 | if rw.Code == 0 {
54 | rw.Code = http.StatusOK
55 | }
56 | return len(buf), nil
57 | }
58 |
59 | // WriteHeader sets rw.Code.
60 | func (rw *ResponseRecorder) WriteHeader(code int) {
61 | rw.Code = code
62 | }
63 |
64 | // Flush sets rw.Flushed to true.
65 | func (rw *ResponseRecorder) Flush() {
66 | rw.Flushed = true
67 | }
68 |
69 | // ----------------------------------------------------------------------------
70 |
71 | func TestRouteMatchers(t *testing.T) {
72 | var scheme, host, path, query, method string
73 | var headers map[string]string
74 | var resultVars map[bool]map[string]string
75 |
76 | router := NewRouter()
77 | router.NewRoute().Host("{var1}.google.com").
78 | Path("/{var2:[a-z]+}/{var3:[0-9]+}").
79 | Queries("foo", "bar").
80 | Methods("GET").
81 | Schemes("https").
82 | Headers("x-requested-with", "XMLHttpRequest")
83 | router.NewRoute().Host("www.{var4}.com").
84 | PathPrefix("/foo/{var5:[a-z]+}/{var6:[0-9]+}").
85 | Queries("baz", "ding").
86 | Methods("POST").
87 | Schemes("http").
88 | Headers("Content-Type", "application/json")
89 |
90 | reset := func() {
91 | // Everything match.
92 | scheme = "https"
93 | host = "www.google.com"
94 | path = "/product/42"
95 | query = "?foo=bar"
96 | method = "GET"
97 | headers = map[string]string{"X-Requested-With": "XMLHttpRequest"}
98 | resultVars = map[bool]map[string]string{
99 | true: {"var1": "www", "var2": "product", "var3": "42"},
100 | false: {},
101 | }
102 | }
103 |
104 | reset2 := func() {
105 | // Everything match.
106 | scheme = "http"
107 | host = "www.google.com"
108 | path = "/foo/product/42/path/that/is/ignored"
109 | query = "?baz=ding"
110 | method = "POST"
111 | headers = map[string]string{"Content-Type": "application/json"}
112 | resultVars = map[bool]map[string]string{
113 | true: {"var4": "google", "var5": "product", "var6": "42"},
114 | false: {},
115 | }
116 | }
117 |
118 | match := func(shouldMatch bool) {
119 | url := scheme + "://" + host + path + query
120 | request, _ := http.NewRequest(method, url, nil)
121 | for key, value := range headers {
122 | request.Header.Add(key, value)
123 | }
124 |
125 | var routeMatch RouteMatch
126 | matched := router.Match(request, &routeMatch)
127 | if matched != shouldMatch {
128 | // Need better messages. :)
129 | if matched {
130 | t.Errorf("Should match.")
131 | } else {
132 | t.Errorf("Should not match.")
133 | }
134 | }
135 |
136 | if matched {
137 | currentRoute := routeMatch.Route
138 | if currentRoute == nil {
139 | t.Errorf("Expected a current route.")
140 | }
141 | vars := routeMatch.Vars
142 | expectedVars := resultVars[shouldMatch]
143 | if len(vars) != len(expectedVars) {
144 | t.Errorf("Expected vars: %v Got: %v.", expectedVars, vars)
145 | }
146 | for name, value := range vars {
147 | if expectedVars[name] != value {
148 | t.Errorf("Expected vars: %v Got: %v.", expectedVars, vars)
149 | }
150 | }
151 | }
152 | }
153 |
154 | // 1st route --------------------------------------------------------------
155 |
156 | // Everything match.
157 | reset()
158 | match(true)
159 |
160 | // Scheme doesn't match.
161 | reset()
162 | scheme = "http"
163 | match(false)
164 |
165 | // Host doesn't match.
166 | reset()
167 | host = "www.mygoogle.com"
168 | match(false)
169 |
170 | // Path doesn't match.
171 | reset()
172 | path = "/product/notdigits"
173 | match(false)
174 |
175 | // Query doesn't match.
176 | reset()
177 | query = "?foo=baz"
178 | match(false)
179 |
180 | // Method doesn't match.
181 | reset()
182 | method = "POST"
183 | match(false)
184 |
185 | // Header doesn't match.
186 | reset()
187 | headers = map[string]string{}
188 | match(false)
189 |
190 | // Everything match, again.
191 | reset()
192 | match(true)
193 |
194 | // 2nd route --------------------------------------------------------------
195 |
196 | // Everything match.
197 | reset2()
198 | match(true)
199 |
200 | // Scheme doesn't match.
201 | reset2()
202 | scheme = "https"
203 | match(false)
204 |
205 | // Host doesn't match.
206 | reset2()
207 | host = "sub.google.com"
208 | match(false)
209 |
210 | // Path doesn't match.
211 | reset2()
212 | path = "/bar/product/42"
213 | match(false)
214 |
215 | // Query doesn't match.
216 | reset2()
217 | query = "?foo=baz"
218 | match(false)
219 |
220 | // Method doesn't match.
221 | reset2()
222 | method = "GET"
223 | match(false)
224 |
225 | // Header doesn't match.
226 | reset2()
227 | headers = map[string]string{}
228 | match(false)
229 |
230 | // Everything match, again.
231 | reset2()
232 | match(true)
233 | }
234 |
235 | type headerMatcherTest struct {
236 | matcher headerMatcher
237 | headers map[string]string
238 | result bool
239 | }
240 |
241 | var headerMatcherTests = []headerMatcherTest{
242 | {
243 | matcher: headerMatcher(map[string]string{"x-requested-with": "XMLHttpRequest"}),
244 | headers: map[string]string{"X-Requested-With": "XMLHttpRequest"},
245 | result: true,
246 | },
247 | {
248 | matcher: headerMatcher(map[string]string{"x-requested-with": ""}),
249 | headers: map[string]string{"X-Requested-With": "anything"},
250 | result: true,
251 | },
252 | {
253 | matcher: headerMatcher(map[string]string{"x-requested-with": "XMLHttpRequest"}),
254 | headers: map[string]string{},
255 | result: false,
256 | },
257 | }
258 |
259 | type hostMatcherTest struct {
260 | matcher *Route
261 | url string
262 | vars map[string]string
263 | result bool
264 | }
265 |
266 | var hostMatcherTests = []hostMatcherTest{
267 | {
268 | matcher: NewRouter().NewRoute().Host("{foo:[a-z][a-z][a-z]}.{bar:[a-z][a-z][a-z]}.{baz:[a-z][a-z][a-z]}"),
269 | url: "http://abc.def.ghi/",
270 | vars: map[string]string{"foo": "abc", "bar": "def", "baz": "ghi"},
271 | result: true,
272 | },
273 | {
274 | matcher: NewRouter().NewRoute().Host("{foo:[a-z][a-z][a-z]}.{bar:[a-z][a-z][a-z]}.{baz:[a-z][a-z][a-z]}"),
275 | url: "http://a.b.c/",
276 | vars: map[string]string{"foo": "abc", "bar": "def", "baz": "ghi"},
277 | result: false,
278 | },
279 | }
280 |
281 | type methodMatcherTest struct {
282 | matcher methodMatcher
283 | method string
284 | result bool
285 | }
286 |
287 | var methodMatcherTests = []methodMatcherTest{
288 | {
289 | matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
290 | method: "GET",
291 | result: true,
292 | },
293 | {
294 | matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
295 | method: "POST",
296 | result: true,
297 | },
298 | {
299 | matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
300 | method: "PUT",
301 | result: true,
302 | },
303 | {
304 | matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
305 | method: "DELETE",
306 | result: false,
307 | },
308 | }
309 |
310 | type pathMatcherTest struct {
311 | matcher *Route
312 | url string
313 | vars map[string]string
314 | result bool
315 | }
316 |
317 | var pathMatcherTests = []pathMatcherTest{
318 | {
319 | matcher: NewRouter().NewRoute().Path("/{foo:[0-9][0-9][0-9]}/{bar:[0-9][0-9][0-9]}/{baz:[0-9][0-9][0-9]}"),
320 | url: "http://localhost:8080/123/456/789",
321 | vars: map[string]string{"foo": "123", "bar": "456", "baz": "789"},
322 | result: true,
323 | },
324 | {
325 | matcher: NewRouter().NewRoute().Path("/{foo:[0-9][0-9][0-9]}/{bar:[0-9][0-9][0-9]}/{baz:[0-9][0-9][0-9]}"),
326 | url: "http://localhost:8080/1/2/3",
327 | vars: map[string]string{"foo": "123", "bar": "456", "baz": "789"},
328 | result: false,
329 | },
330 | }
331 |
332 | type queryMatcherTest struct {
333 | matcher queryMatcher
334 | url string
335 | result bool
336 | }
337 |
338 | var queryMatcherTests = []queryMatcherTest{
339 | {
340 | matcher: queryMatcher(map[string]string{"foo": "bar", "baz": "ding"}),
341 | url: "http://localhost:8080/?foo=bar&baz=ding",
342 | result: true,
343 | },
344 | {
345 | matcher: queryMatcher(map[string]string{"foo": "", "baz": ""}),
346 | url: "http://localhost:8080/?foo=anything&baz=anything",
347 | result: true,
348 | },
349 | {
350 | matcher: queryMatcher(map[string]string{"foo": "ding", "baz": "bar"}),
351 | url: "http://localhost:8080/?foo=bar&baz=ding",
352 | result: false,
353 | },
354 | {
355 | matcher: queryMatcher(map[string]string{"bar": "foo", "ding": "baz"}),
356 | url: "http://localhost:8080/?foo=bar&baz=ding",
357 | result: false,
358 | },
359 | }
360 |
361 | type schemeMatcherTest struct {
362 | matcher schemeMatcher
363 | url string
364 | result bool
365 | }
366 |
367 | var schemeMatcherTests = []schemeMatcherTest{
368 | {
369 | matcher: schemeMatcher([]string{"http", "https"}),
370 | url: "http://localhost:8080/",
371 | result: true,
372 | },
373 | {
374 | matcher: schemeMatcher([]string{"http", "https"}),
375 | url: "https://localhost:8080/",
376 | result: true,
377 | },
378 | {
379 | matcher: schemeMatcher([]string{"https"}),
380 | url: "http://localhost:8080/",
381 | result: false,
382 | },
383 | {
384 | matcher: schemeMatcher([]string{"http"}),
385 | url: "https://localhost:8080/",
386 | result: false,
387 | },
388 | }
389 |
390 | type urlBuildingTest struct {
391 | route *Route
392 | vars []string
393 | url string
394 | }
395 |
396 | var urlBuildingTests = []urlBuildingTest{
397 | {
398 | route: new(Route).Host("foo.domain.com"),
399 | vars: []string{},
400 | url: "http://foo.domain.com",
401 | },
402 | {
403 | route: new(Route).Host("{subdomain}.domain.com"),
404 | vars: []string{"subdomain", "bar"},
405 | url: "http://bar.domain.com",
406 | },
407 | {
408 | route: new(Route).Host("foo.domain.com").Path("/articles"),
409 | vars: []string{},
410 | url: "http://foo.domain.com/articles",
411 | },
412 | {
413 | route: new(Route).Path("/articles"),
414 | vars: []string{},
415 | url: "/articles",
416 | },
417 | {
418 | route: new(Route).Path("/articles/{category}/{id:[0-9]+}"),
419 | vars: []string{"category", "technology", "id", "42"},
420 | url: "/articles/technology/42",
421 | },
422 | {
423 | route: new(Route).Host("{subdomain}.domain.com").Path("/articles/{category}/{id:[0-9]+}"),
424 | vars: []string{"subdomain", "foo", "category", "technology", "id", "42"},
425 | url: "http://foo.domain.com/articles/technology/42",
426 | },
427 | }
428 |
429 | func TestHeaderMatcher(t *testing.T) {
430 | for _, v := range headerMatcherTests {
431 | request, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
432 | for key, value := range v.headers {
433 | request.Header.Add(key, value)
434 | }
435 | var routeMatch RouteMatch
436 | result := v.matcher.Match(request, &routeMatch)
437 | if result != v.result {
438 | if v.result {
439 | t.Errorf("%#v: should match %v.", v.matcher, request.Header)
440 | } else {
441 | t.Errorf("%#v: should not match %v.", v.matcher, request.Header)
442 | }
443 | }
444 | }
445 | }
446 |
447 | func TestHostMatcher(t *testing.T) {
448 | for _, v := range hostMatcherTests {
449 | request, _ := http.NewRequest("GET", v.url, nil)
450 | var routeMatch RouteMatch
451 | result := v.matcher.Match(request, &routeMatch)
452 | vars := routeMatch.Vars
453 | if result != v.result {
454 | if v.result {
455 | t.Errorf("%#v: should match %v.", v.matcher, v.url)
456 | } else {
457 | t.Errorf("%#v: should not match %v.", v.matcher, v.url)
458 | }
459 | }
460 | if result {
461 | if len(vars) != len(v.vars) {
462 | t.Errorf("%#v: vars length should be %v, got %v.", v.matcher, len(v.vars), len(vars))
463 | }
464 | for name, value := range vars {
465 | if v.vars[name] != value {
466 | t.Errorf("%#v: expected value %v for key %v, got %v.", v.matcher, v.vars[name], name, value)
467 | }
468 | }
469 | } else {
470 | if len(vars) != 0 {
471 | t.Errorf("%#v: vars length should be 0, got %v.", v.matcher, len(vars))
472 | }
473 | }
474 | }
475 | }
476 |
477 | func TestMethodMatcher(t *testing.T) {
478 | for _, v := range methodMatcherTests {
479 | request, _ := http.NewRequest(v.method, "http://localhost:8080/", nil)
480 | var routeMatch RouteMatch
481 | result := v.matcher.Match(request, &routeMatch)
482 | if result != v.result {
483 | if v.result {
484 | t.Errorf("%#v: should match %v.", v.matcher, v.method)
485 | } else {
486 | t.Errorf("%#v: should not match %v.", v.matcher, v.method)
487 | }
488 | }
489 | }
490 | }
491 |
492 | func TestPathMatcher(t *testing.T) {
493 | for _, v := range pathMatcherTests {
494 | request, _ := http.NewRequest("GET", v.url, nil)
495 | var routeMatch RouteMatch
496 | result := v.matcher.Match(request, &routeMatch)
497 | vars := routeMatch.Vars
498 | if result != v.result {
499 | if v.result {
500 | t.Errorf("%#v: should match %v.", v.matcher, v.url)
501 | } else {
502 | t.Errorf("%#v: should not match %v.", v.matcher, v.url)
503 | }
504 | }
505 | if result {
506 | if len(vars) != len(v.vars) {
507 | t.Errorf("%#v: vars length should be %v, got %v.", v.matcher, len(v.vars), len(vars))
508 | }
509 | for name, value := range vars {
510 | if v.vars[name] != value {
511 | t.Errorf("%#v: expected value %v for key %v, got %v.", v.matcher, v.vars[name], name, value)
512 | }
513 | }
514 | } else {
515 | if len(vars) != 0 {
516 | t.Errorf("%#v: vars length should be 0, got %v.", v.matcher, len(vars))
517 | }
518 | }
519 | }
520 | }
521 |
522 | func TestQueryMatcher(t *testing.T) {
523 | for _, v := range queryMatcherTests {
524 | request, _ := http.NewRequest("GET", v.url, nil)
525 | var routeMatch RouteMatch
526 | result := v.matcher.Match(request, &routeMatch)
527 | if result != v.result {
528 | if v.result {
529 | t.Errorf("%#v: should match %v.", v.matcher, v.url)
530 | } else {
531 | t.Errorf("%#v: should not match %v.", v.matcher, v.url)
532 | }
533 | }
534 | }
535 | }
536 |
537 | func TestSchemeMatcher(t *testing.T) {
538 | for _, v := range queryMatcherTests {
539 | request, _ := http.NewRequest("GET", v.url, nil)
540 | var routeMatch RouteMatch
541 | result := v.matcher.Match(request, &routeMatch)
542 | if result != v.result {
543 | if v.result {
544 | t.Errorf("%#v: should match %v.", v.matcher, v.url)
545 | } else {
546 | t.Errorf("%#v: should not match %v.", v.matcher, v.url)
547 | }
548 | }
549 | }
550 | }
551 |
552 | func TestUrlBuilding(t *testing.T) {
553 |
554 | for _, v := range urlBuildingTests {
555 | u, _ := v.route.URL(v.vars...)
556 | url := u.String()
557 | if url != v.url {
558 | t.Errorf("expected %v, got %v", v.url, url)
559 | /*
560 | reversePath := ""
561 | reverseHost := ""
562 | if v.route.pathTemplate != nil {
563 | reversePath = v.route.pathTemplate.Reverse
564 | }
565 | if v.route.hostTemplate != nil {
566 | reverseHost = v.route.hostTemplate.Reverse
567 | }
568 |
569 | t.Errorf("%#v:\nexpected: %q\ngot: %q\nreverse path: %q\nreverse host: %q", v.route, v.url, url, reversePath, reverseHost)
570 | */
571 | }
572 | }
573 |
574 | ArticleHandler := func(w http.ResponseWriter, r *http.Request) {
575 | }
576 |
577 | router := NewRouter()
578 | router.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).Name("article")
579 |
580 | url, _ := router.Get("article").URL("category", "technology", "id", "42")
581 | expected := "/articles/technology/42"
582 | if url.String() != expected {
583 | t.Errorf("Expected %v, got %v", expected, url.String())
584 | }
585 | }
586 |
587 | func TestMatchedRouteName(t *testing.T) {
588 | routeName := "stock"
589 | router := NewRouter()
590 | route := router.NewRoute().Path("/products/").Name(routeName)
591 |
592 | url := "http://www.domain.com/products/"
593 | request, _ := http.NewRequest("GET", url, nil)
594 | var rv RouteMatch
595 | ok := router.Match(request, &rv)
596 |
597 | if !ok || rv.Route != route {
598 | t.Errorf("Expected same route, got %+v.", rv.Route)
599 | }
600 |
601 | retName := rv.Route.GetName()
602 | if retName != routeName {
603 | t.Errorf("Expected %q, got %q.", routeName, retName)
604 | }
605 | }
606 |
607 | func TestSubRouting(t *testing.T) {
608 | // Example from docs.
609 | router := NewRouter()
610 | subrouter := router.NewRoute().Host("www.domain.com").Subrouter()
611 | route := subrouter.NewRoute().Path("/products/").Name("products")
612 |
613 | url := "http://www.domain.com/products/"
614 | request, _ := http.NewRequest("GET", url, nil)
615 | var rv RouteMatch
616 | ok := router.Match(request, &rv)
617 |
618 | if !ok || rv.Route != route {
619 | t.Errorf("Expected same route, got %+v.", rv.Route)
620 | }
621 |
622 | u, _ := router.Get("products").URL()
623 | builtUrl := u.String()
624 | // Yay, subroute aware of the domain when building!
625 | if builtUrl != url {
626 | t.Errorf("Expected %q, got %q.", url, builtUrl)
627 | }
628 | }
629 |
630 | func TestVariableNames(t *testing.T) {
631 | route := new(Route).Host("{arg1}.domain.com").Path("/{arg1}/{arg2:[0-9]+}")
632 | if route.err == nil {
633 | t.Errorf("Expected error for duplicated variable names")
634 | }
635 | }
636 |
637 | func TestRedirectSlash(t *testing.T) {
638 | var route *Route
639 | var routeMatch RouteMatch
640 | r := NewRouter()
641 |
642 | r.StrictSlash(false)
643 | route = r.NewRoute()
644 | if route.strictSlash != false {
645 | t.Errorf("Expected false redirectSlash.")
646 | }
647 |
648 | r.StrictSlash(true)
649 | route = r.NewRoute()
650 | if route.strictSlash != true {
651 | t.Errorf("Expected true redirectSlash.")
652 | }
653 |
654 | route = new(Route)
655 | route.strictSlash = true
656 | route.Path("/{arg1}/{arg2:[0-9]+}/")
657 | request, _ := http.NewRequest("GET", "http://localhost/foo/123", nil)
658 | routeMatch = RouteMatch{}
659 | _ = route.Match(request, &routeMatch)
660 | vars := routeMatch.Vars
661 | if vars["arg1"] != "foo" {
662 | t.Errorf("Expected foo.")
663 | }
664 | if vars["arg2"] != "123" {
665 | t.Errorf("Expected 123.")
666 | }
667 | rsp := NewRecorder()
668 | routeMatch.Handler.ServeHTTP(rsp, request)
669 | if rsp.HeaderMap.Get("Location") != "http://localhost/foo/123/" {
670 | t.Errorf("Expected redirect header.")
671 | }
672 |
673 | route = new(Route)
674 | route.strictSlash = true
675 | route.Path("/{arg1}/{arg2:[0-9]+}")
676 | request, _ = http.NewRequest("GET", "http://localhost/foo/123/", nil)
677 | routeMatch = RouteMatch{}
678 | _ = route.Match(request, &routeMatch)
679 | vars = routeMatch.Vars
680 | if vars["arg1"] != "foo" {
681 | t.Errorf("Expected foo.")
682 | }
683 | if vars["arg2"] != "123" {
684 | t.Errorf("Expected 123.")
685 | }
686 | rsp = NewRecorder()
687 | routeMatch.Handler.ServeHTTP(rsp, request)
688 | if rsp.HeaderMap.Get("Location") != "http://localhost/foo/123" {
689 | t.Errorf("Expected redirect header.")
690 | }
691 | }
692 |
693 | // Test for the new regexp library, still not available in stable Go.
694 | func TestNewRegexp(t *testing.T) {
695 | var p *routeRegexp
696 | var matches []string
697 |
698 | tests := map[string]map[string][]string{
699 | "/{foo:a{2}}": {
700 | "/a": nil,
701 | "/aa": {"aa"},
702 | "/aaa": nil,
703 | "/aaaa": nil,
704 | },
705 | "/{foo:a{2,}}": {
706 | "/a": nil,
707 | "/aa": {"aa"},
708 | "/aaa": {"aaa"},
709 | "/aaaa": {"aaaa"},
710 | },
711 | "/{foo:a{2,3}}": {
712 | "/a": nil,
713 | "/aa": {"aa"},
714 | "/aaa": {"aaa"},
715 | "/aaaa": nil,
716 | },
717 | "/{foo:[a-z]{3}}/{bar:[a-z]{2}}": {
718 | "/a": nil,
719 | "/ab": nil,
720 | "/abc": nil,
721 | "/abcd": nil,
722 | "/abc/ab": {"abc", "ab"},
723 | "/abc/abc": nil,
724 | "/abcd/ab": nil,
725 | },
726 | `/{foo:\w{3,}}/{bar:\d{2,}}`: {
727 | "/a": nil,
728 | "/ab": nil,
729 | "/abc": nil,
730 | "/abc/1": nil,
731 | "/abc/12": {"abc", "12"},
732 | "/abcd/12": {"abcd", "12"},
733 | "/abcd/123": {"abcd", "123"},
734 | },
735 | }
736 |
737 | for pattern, paths := range tests {
738 | p, _ = newRouteRegexp(pattern, false, false, false)
739 | for path, result := range paths {
740 | matches = p.regexp.FindStringSubmatch(path)
741 | if result == nil {
742 | if matches != nil {
743 | t.Errorf("%v should not match %v.", pattern, path)
744 | }
745 | } else {
746 | if len(matches) != len(result)+1 {
747 | t.Errorf("Expected %v matches, got %v.", len(result)+1, len(matches))
748 | } else {
749 | for k, v := range result {
750 | if matches[k+1] != v {
751 | t.Errorf("Expected %v, got %v.", v, matches[k+1])
752 | }
753 | }
754 | }
755 | }
756 | }
757 | }
758 | }
759 |
--------------------------------------------------------------------------------
/dashboard/dist/app.js:
--------------------------------------------------------------------------------
1 | angular.module("templates-views",["/page/browser/browser.html","/page/browser/create-node.html","/page/browser/edit-node.html","/page/browser/edit-ttl.html","/page/browser/node-info.html","/page/stats/stats-detail.html","/page/stats/stats.html","/ui/breadcrumb.html","/ui/latency-graph.html","/ui/node-cog.html"]),angular.module("/page/browser/browser.html",[]).run(["$templateCache",function(a){a.put("/page/browser/browser.html",'