vendor github.com/gorilla dependencies
verokarhu committed
Sep 18, 2014 at 00:04 UTC
cf0100c97c667d7a024c37a73dd8b88df621d9dc
18 files changed
+3505
-1
Godeps/Godeps.json
+8
@@ -46,6 +46,14 @@
46
"ImportPath": "github.com/gonuts/flag",
47
"Rev": "741a6cbd37a30dedc93f817e7de6aaf0ca38a493"
48
},
49
+ {
50
+ "ImportPath": "github.com/gorilla/context",
51
+ "Rev": "14f550f51af52180c2eefed15e5fd18d63c0a64a"
52
+ },
53
+ {
54
+ "ImportPath": "github.com/gorilla/mux",
55
+ "Rev": "4b8fbc56f3b2400a7c7ea3dba9b3539787c486b6"
56
+ },
57
{
58
"ImportPath": "github.com/jbenet/commander",
59
"Rev": "e0cf317891f0ab6f1ac64dfcb754b4fb5e69f7df"
Godeps/_workspace/src/github.com/gorilla/context/.travis.yml
new
+7
@@ -0,0 +1,7 @@
1
+language: go
2
+
3
+go:
4
+ - 1.0
5
+ - 1.1
6
+ - 1.2
7
+ - tip
Godeps/_workspace/src/github.com/gorilla/context/LICENSE
new
+27
@@ -0,0 +1,27 @@
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.
Godeps/_workspace/src/github.com/gorilla/context/README.md
new
+7
@@ -0,0 +1,7 @@
1
+context
2
+=======
3
+[](https://travis-ci.org/gorilla/context)
4
+
5
+gorilla/context is a general purpose registry for global request variables.
6
+
7
+Read the full documentation here: http://www.gorillatoolkit.org/pkg/context
Godeps/_workspace/src/github.com/gorilla/context/context.go
new
+143
@@ -0,0 +1,143 @@
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.RWMutex
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
+ if data[r] == nil {
23
+ data[r] = make(map[interface{}]interface{})
24
+ datat[r] = time.Now().Unix()
25
+ }
26
+ data[r][key] = val
27
+ mutex.Unlock()
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.RLock()
33
+ if ctx := data[r]; ctx != nil {
34
+ value := ctx[key]
35
+ mutex.RUnlock()
36
+ return value
37
+ }
38
+ mutex.RUnlock()
39
+ return nil
40
+}
41
+
42
+// GetOk returns stored value and presence state like multi-value return of map access.
43
+func GetOk(r *http.Request, key interface{}) (interface{}, bool) {
44
+ mutex.RLock()
45
+ if _, ok := data[r]; ok {
46
+ value, ok := data[r][key]
47
+ mutex.RUnlock()
48
+ return value, ok
49
+ }
50
+ mutex.RUnlock()
51
+ return nil, false
52
+}
53
+
54
+// GetAll returns all stored values for the request as a map. Nil is returned for invalid requests.
55
+func GetAll(r *http.Request) map[interface{}]interface{} {
56
+ mutex.RLock()
57
+ if context, ok := data[r]; ok {
58
+ result := make(map[interface{}]interface{}, len(context))
59
+ for k, v := range context {
60
+ result[k] = v
61
+ }
62
+ mutex.RUnlock()
63
+ return result
64
+ }
65
+ mutex.RUnlock()
66
+ return nil
67
+}
68
+
69
+// GetAllOk returns all stored values for the request as a map and a boolean value that indicates if
70
+// the request was registered.
71
+func GetAllOk(r *http.Request) (map[interface{}]interface{}, bool) {
72
+ mutex.RLock()
73
+ context, ok := data[r]
74
+ result := make(map[interface{}]interface{}, len(context))
75
+ for k, v := range context {
76
+ result[k] = v
77
+ }
78
+ mutex.RUnlock()
79
+ return result, ok
80
+}
81
+
82
+// Delete removes a value stored for a given key in a given request.
83
+func Delete(r *http.Request, key interface{}) {
84
+ mutex.Lock()
85
+ if data[r] != nil {
86
+ delete(data[r], key)
87
+ }
88
+ mutex.Unlock()
89
+}
90
+
91
+// Clear removes all values stored for a given request.
92
+//
93
+// This is usually called by a handler wrapper to clean up request
94
+// variables at the end of a request lifetime. See ClearHandler().
95
+func Clear(r *http.Request) {
96
+ mutex.Lock()
97
+ clear(r)
98
+ mutex.Unlock()
99
+}
100
+
101
+// clear is Clear without the lock.
102
+func clear(r *http.Request) {
103
+ delete(data, r)
104
+ delete(datat, r)
105
+}
106
+
107
+// Purge removes request data stored for longer than maxAge, in seconds.
108
+// It returns the amount of requests removed.
109
+//
110
+// If maxAge <= 0, all request data is removed.
111
+//
112
+// This is only used for sanity check: in case context cleaning was not
113
+// properly set some request data can be kept forever, consuming an increasing
114
+// amount of memory. In case this is detected, Purge() must be called
115
+// periodically until the problem is fixed.
116
+func Purge(maxAge int) int {
117
+ mutex.Lock()
118
+ count := 0
119
+ if maxAge <= 0 {
120
+ count = len(data)
121
+ data = make(map[*http.Request]map[interface{}]interface{})
122
+ datat = make(map[*http.Request]int64)
123
+ } else {
124
+ min := time.Now().Unix() - int64(maxAge)
125
+ for r := range data {
126
+ if datat[r] < min {
127
+ clear(r)
128
+ count++
129
+ }
130
+ }
131
+ }
132
+ mutex.Unlock()
133
+ return count
134
+}
135
+
136
+// ClearHandler wraps an http.Handler and clears request values at the end
137
+// of a request lifetime.
138
+func ClearHandler(h http.Handler) http.Handler {
139
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
140
+ defer Clear(r)
141
+ h.ServeHTTP(w, r)
142
+ })
143
+}
Godeps/_workspace/src/github.com/gorilla/context/context_test.go
new
+161
@@ -0,0 +1,161 @@
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
+ emptyR, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
28
+
29
+ // Get()
30
+ assertEqual(Get(r, key1), nil)
31
+
32
+ // Set()
33
+ Set(r, key1, "1")
34
+ assertEqual(Get(r, key1), "1")
35
+ assertEqual(len(data[r]), 1)
36
+
37
+ Set(r, key2, "2")
38
+ assertEqual(Get(r, key2), "2")
39
+ assertEqual(len(data[r]), 2)
40
+
41
+ //GetOk
42
+ value, ok := GetOk(r, key1)
43
+ assertEqual(value, "1")
44
+ assertEqual(ok, true)
45
+
46
+ value, ok = GetOk(r, "not exists")
47
+ assertEqual(value, nil)
48
+ assertEqual(ok, false)
49
+
50
+ Set(r, "nil value", nil)
51
+ value, ok = GetOk(r, "nil value")
52
+ assertEqual(value, nil)
53
+ assertEqual(ok, true)
54
+
55
+ // GetAll()
56
+ values := GetAll(r)
57
+ assertEqual(len(values), 3)
58
+
59
+ // GetAll() for empty request
60
+ values = GetAll(emptyR)
61
+ if values != nil {
62
+ t.Error("GetAll didn't return nil value for invalid request")
63
+ }
64
+
65
+ // GetAllOk()
66
+ values, ok = GetAllOk(r)
67
+ assertEqual(len(values), 3)
68
+ assertEqual(ok, true)
69
+
70
+ // GetAllOk() for empty request
71
+ values, ok = GetAllOk(emptyR)
72
+ assertEqual(value, nil)
73
+ assertEqual(ok, false)
74
+
75
+ // Delete()
76
+ Delete(r, key1)
77
+ assertEqual(Get(r, key1), nil)
78
+ assertEqual(len(data[r]), 2)
79
+
80
+ Delete(r, key2)
81
+ assertEqual(Get(r, key2), nil)
82
+ assertEqual(len(data[r]), 1)
83
+
84
+ // Clear()
85
+ Clear(r)
86
+ assertEqual(len(data), 0)
87
+}
88
+
89
+func parallelReader(r *http.Request, key string, iterations int, wait, done chan struct{}) {
90
+ <-wait
91
+ for i := 0; i < iterations; i++ {
92
+ Get(r, key)
93
+ }
94
+ done <- struct{}{}
95
+
96
+}
97
+
98
+func parallelWriter(r *http.Request, key, value string, iterations int, wait, done chan struct{}) {
99
+ <-wait
100
+ for i := 0; i < iterations; i++ {
101
+ Get(r, key)
102
+ }
103
+ done <- struct{}{}
104
+
105
+}
106
+
107
+func benchmarkMutex(b *testing.B, numReaders, numWriters, iterations int) {
108
+
109
+ b.StopTimer()
110
+ r, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
111
+ done := make(chan struct{})
112
+ b.StartTimer()
113
+
114
+ for i := 0; i < b.N; i++ {
115
+ wait := make(chan struct{})
116
+
117
+ for i := 0; i < numReaders; i++ {
118
+ go parallelReader(r, "test", iterations, wait, done)
119
+ }
120
+
121
+ for i := 0; i < numWriters; i++ {
122
+ go parallelWriter(r, "test", "123", iterations, wait, done)
123
+ }
124
+
125
+ close(wait)
126
+
127
+ for i := 0; i < numReaders+numWriters; i++ {
128
+ <-done
129
+ }
130
+
131
+ }
132
+
133
+}
134
+
135
+func BenchmarkMutexSameReadWrite1(b *testing.B) {
136
+ benchmarkMutex(b, 1, 1, 32)
137
+}
138
+func BenchmarkMutexSameReadWrite2(b *testing.B) {
139
+ benchmarkMutex(b, 2, 2, 32)
140
+}
141
+func BenchmarkMutexSameReadWrite4(b *testing.B) {
142
+ benchmarkMutex(b, 4, 4, 32)
143
+}
144
+func BenchmarkMutex1(b *testing.B) {
145
+ benchmarkMutex(b, 2, 8, 32)
146
+}
147
+func BenchmarkMutex2(b *testing.B) {
148
+ benchmarkMutex(b, 16, 4, 64)
149
+}
150
+func BenchmarkMutex3(b *testing.B) {
151
+ benchmarkMutex(b, 1, 2, 128)
152
+}
153
+func BenchmarkMutex4(b *testing.B) {
154
+ benchmarkMutex(b, 128, 32, 256)
155
+}
156
+func BenchmarkMutex5(b *testing.B) {
157
+ benchmarkMutex(b, 1024, 2048, 64)
158
+}
159
+func BenchmarkMutex6(b *testing.B) {
160
+ benchmarkMutex(b, 2048, 1024, 512)
161
+}
Godeps/_workspace/src/github.com/gorilla/context/doc.go
new
+82
@@ -0,0 +1,82 @@
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 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
Godeps/_workspace/src/github.com/gorilla/mux/.travis.yml
new
+7
@@ -0,0 +1,7 @@
1
+language: go
2
+
3
+go:
4
+ - 1.0
5
+ - 1.1
6
+ - 1.2
7
+ - tip
Godeps/_workspace/src/github.com/gorilla/mux/LICENSE
new
+27
@@ -0,0 +1,27 @@
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.
Godeps/_workspace/src/github.com/gorilla/mux/README.md
new
+7
@@ -0,0 +1,7 @@
1
+mux
2
+===
3
+[](https://travis-ci.org/gorilla/mux)
4
+
5
+gorilla/mux is a powerful URL router and dispatcher.
6
+
7
+Read the full documentation here: http://www.gorillatoolkit.org/pkg/mux
Godeps/_workspace/src/github.com/gorilla/mux/bench_test.go
new
+21
@@ -0,0 +1,21 @@
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
+ "net/http"
9
+ "testing"
10
+)
11
+
12
+func BenchmarkMux(b *testing.B) {
13
+ router := new(Router)
14
+ handler := func(w http.ResponseWriter, r *http.Request) {}
15
+ router.HandleFunc("/v1/{v1}", handler)
16
+
17
+ request, _ := http.NewRequest("GET", "/v1/anything", nil)
18
+ for i := 0; i < b.N; i++ {
19
+ router.ServeHTTP(nil, request)
20
+ }
21
+}
Godeps/_workspace/src/github.com/gorilla/mux/doc.go
new
+199
@@ -0,0 +1,199 @@
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
Godeps/_workspace/src/github.com/gorilla/mux/mux.go
new
+353
@@ -0,0 +1,353 @@
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/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
+ handler = r.NotFoundHandler
91
+ if handler == nil {
92
+ handler = http.NotFoundHandler()
93
+ }
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 trailing slash behavior for new routes. The initial
113
+// value is false.
114
+//
115
+// When true, if the route path is "/path/", accessing "/path" will redirect
116
+// to the former and vice versa. In other words, your application will always
117
+// see the path as specified in the route.
118
+//
119
+// When false, if the route path is "/path", accessing "/path/" will not match
120
+// this route and vice versa.
121
+//
122
+// Special case: when a route sets a path prefix using the PathPrefix() method,
123
+// strict slash is ignored for that route because the redirect behavior can't
124
+// be determined from a prefix alone. However, any subrouters created from that
125
+// route inherit the original StrictSlash setting.
126
+func (r *Router) StrictSlash(value bool) *Router {
127
+ r.strictSlash = value
128
+ return r
129
+}
130
+
131
+// ----------------------------------------------------------------------------
132
+// parentRoute
133
+// ----------------------------------------------------------------------------
134
+
135
+// getNamedRoutes returns the map where named routes are registered.
136
+func (r *Router) getNamedRoutes() map[string]*Route {
137
+ if r.namedRoutes == nil {
138
+ if r.parent != nil {
139
+ r.namedRoutes = r.parent.getNamedRoutes()
140
+ } else {
141
+ r.namedRoutes = make(map[string]*Route)
142
+ }
143
+ }
144
+ return r.namedRoutes
145
+}
146
+
147
+// getRegexpGroup returns regexp definitions from the parent route, if any.
148
+func (r *Router) getRegexpGroup() *routeRegexpGroup {
149
+ if r.parent != nil {
150
+ return r.parent.getRegexpGroup()
151
+ }
152
+ return nil
153
+}
154
+
155
+// ----------------------------------------------------------------------------
156
+// Route factories
157
+// ----------------------------------------------------------------------------
158
+
159
+// NewRoute registers an empty route.
160
+func (r *Router) NewRoute() *Route {
161
+ route := &Route{parent: r, strictSlash: r.strictSlash}
162
+ r.routes = append(r.routes, route)
163
+ return route
164
+}
165
+
166
+// Handle registers a new route with a matcher for the URL path.
167
+// See Route.Path() and Route.Handler().
168
+func (r *Router) Handle(path string, handler http.Handler) *Route {
169
+ return r.NewRoute().Path(path).Handler(handler)
170
+}
171
+
172
+// HandleFunc registers a new route with a matcher for the URL path.
173
+// See Route.Path() and Route.HandlerFunc().
174
+func (r *Router) HandleFunc(path string, f func(http.ResponseWriter,
175
+ *http.Request)) *Route {
176
+ return r.NewRoute().Path(path).HandlerFunc(f)
177
+}
178
+
179
+// Headers registers a new route with a matcher for request header values.
180
+// See Route.Headers().
181
+func (r *Router) Headers(pairs ...string) *Route {
182
+ return r.NewRoute().Headers(pairs...)
183
+}
184
+
185
+// Host registers a new route with a matcher for the URL host.
186
+// See Route.Host().
187
+func (r *Router) Host(tpl string) *Route {
188
+ return r.NewRoute().Host(tpl)
189
+}
190
+
191
+// MatcherFunc registers a new route with a custom matcher function.
192
+// See Route.MatcherFunc().
193
+func (r *Router) MatcherFunc(f MatcherFunc) *Route {
194
+ return r.NewRoute().MatcherFunc(f)
195
+}
196
+
197
+// Methods registers a new route with a matcher for HTTP methods.
198
+// See Route.Methods().
199
+func (r *Router) Methods(methods ...string) *Route {
200
+ return r.NewRoute().Methods(methods...)
201
+}
202
+
203
+// Path registers a new route with a matcher for the URL path.
204
+// See Route.Path().
205
+func (r *Router) Path(tpl string) *Route {
206
+ return r.NewRoute().Path(tpl)
207
+}
208
+
209
+// PathPrefix registers a new route with a matcher for the URL path prefix.
210
+// See Route.PathPrefix().
211
+func (r *Router) PathPrefix(tpl string) *Route {
212
+ return r.NewRoute().PathPrefix(tpl)
213
+}
214
+
215
+// Queries registers a new route with a matcher for URL query values.
216
+// See Route.Queries().
217
+func (r *Router) Queries(pairs ...string) *Route {
218
+ return r.NewRoute().Queries(pairs...)
219
+}
220
+
221
+// Schemes registers a new route with a matcher for URL schemes.
222
+// See Route.Schemes().
223
+func (r *Router) Schemes(schemes ...string) *Route {
224
+ return r.NewRoute().Schemes(schemes...)
225
+}
226
+
227
+// ----------------------------------------------------------------------------
228
+// Context
229
+// ----------------------------------------------------------------------------
230
+
231
+// RouteMatch stores information about a matched route.
232
+type RouteMatch struct {
233
+ Route *Route
234
+ Handler http.Handler
235
+ Vars map[string]string
236
+}
237
+
238
+type contextKey int
239
+
240
+const (
241
+ varsKey contextKey = iota
242
+ routeKey
243
+)
244
+
245
+// Vars returns the route variables for the current request, if any.
246
+func Vars(r *http.Request) map[string]string {
247
+ if rv := context.Get(r, varsKey); rv != nil {
248
+ return rv.(map[string]string)
249
+ }
250
+ return nil
251
+}
252
+
253
+// CurrentRoute returns the matched route for the current request, if any.
254
+func CurrentRoute(r *http.Request) *Route {
255
+ if rv := context.Get(r, routeKey); rv != nil {
256
+ return rv.(*Route)
257
+ }
258
+ return nil
259
+}
260
+
261
+func setVars(r *http.Request, val interface{}) {
262
+ context.Set(r, varsKey, val)
263
+}
264
+
265
+func setCurrentRoute(r *http.Request, val interface{}) {
266
+ context.Set(r, routeKey, val)
267
+}
268
+
269
+// ----------------------------------------------------------------------------
270
+// Helpers
271
+// ----------------------------------------------------------------------------
272
+
273
+// cleanPath returns the canonical path for p, eliminating . and .. elements.
274
+// Borrowed from the net/http package.
275
+func cleanPath(p string) string {
276
+ if p == "" {
277
+ return "/"
278
+ }
279
+ if p[0] != '/' {
280
+ p = "/" + p
281
+ }
282
+ np := path.Clean(p)
283
+ // path.Clean removes trailing slash except for root;
284
+ // put the trailing slash back if necessary.
285
+ if p[len(p)-1] == '/' && np != "/" {
286
+ np += "/"
287
+ }
288
+ return np
289
+}
290
+
291
+// uniqueVars returns an error if two slices contain duplicated strings.
292
+func uniqueVars(s1, s2 []string) error {
293
+ for _, v1 := range s1 {
294
+ for _, v2 := range s2 {
295
+ if v1 == v2 {
296
+ return fmt.Errorf("mux: duplicated route variable %q", v2)
297
+ }
298
+ }
299
+ }
300
+ return nil
301
+}
302
+
303
+// mapFromPairs converts variadic string parameters to a string map.
304
+func mapFromPairs(pairs ...string) (map[string]string, error) {
305
+ length := len(pairs)
306
+ if length%2 != 0 {
307
+ return nil, fmt.Errorf(
308
+ "mux: number of parameters must be multiple of 2, got %v", pairs)
309
+ }
310
+ m := make(map[string]string, length/2)
311
+ for i := 0; i < length; i += 2 {
312
+ m[pairs[i]] = pairs[i+1]
313
+ }
314
+ return m, nil
315
+}
316
+
317
+// matchInArray returns true if the given string value is in the array.
318
+func matchInArray(arr []string, value string) bool {
319
+ for _, v := range arr {
320
+ if v == value {
321
+ return true
322
+ }
323
+ }
324
+ return false
325
+}
326
+
327
+// matchMap returns true if the given key/value pairs exist in a given map.
328
+func matchMap(toCheck map[string]string, toMatch map[string][]string,
329
+ canonicalKey bool) bool {
330
+ for k, v := range toCheck {
331
+ // Check if key exists.
332
+ if canonicalKey {
333
+ k = http.CanonicalHeaderKey(k)
334
+ }
335
+ if values := toMatch[k]; values == nil {
336
+ return false
337
+ } else if v != "" {
338
+ // If value was defined as an empty string we only check that the
339
+ // key exists. Otherwise we also check for equality.
340
+ valueExists := false
341
+ for _, value := range values {
342
+ if v == value {
343
+ valueExists = true
344
+ break
345
+ }
346
+ }
347
+ if !valueExists {
348
+ return false
349
+ }
350
+ }
351
+ }
352
+ return true
353
+}
Godeps/_workspace/src/github.com/gorilla/mux/mux_test.go
new
+943
@@ -0,0 +1,943 @@
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
+ "testing"
11
+
12
+ "github.com/gorilla/context"
13
+)
14
+
15
+type routeTest struct {
16
+ title string // title of the test
17
+ route *Route // the route being tested
18
+ request *http.Request // a request to test the route
19
+ vars map[string]string // the expected vars of the match
20
+ host string // the expected host of the match
21
+ path string // the expected path of the match
22
+ shouldMatch bool // whether the request is expected to match the route at all
23
+ shouldRedirect bool // whether the request should result in a redirect
24
+}
25
+
26
+func TestHost(t *testing.T) {
27
+ // newRequestHost a new request with a method, url, and host header
28
+ newRequestHost := func(method, url, host string) *http.Request {
29
+ req, err := http.NewRequest(method, url, nil)
30
+ if err != nil {
31
+ panic(err)
32
+ }
33
+ req.Host = host
34
+ return req
35
+ }
36
+
37
+ tests := []routeTest{
38
+ {
39
+ title: "Host route match",
40
+ route: new(Route).Host("aaa.bbb.ccc"),
41
+ request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
42
+ vars: map[string]string{},
43
+ host: "aaa.bbb.ccc",
44
+ path: "",
45
+ shouldMatch: true,
46
+ },
47
+ {
48
+ title: "Host route, wrong host in request URL",
49
+ route: new(Route).Host("aaa.bbb.ccc"),
50
+ request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
51
+ vars: map[string]string{},
52
+ host: "aaa.bbb.ccc",
53
+ path: "",
54
+ shouldMatch: false,
55
+ },
56
+ {
57
+ title: "Host route with port, match",
58
+ route: new(Route).Host("aaa.bbb.ccc:1234"),
59
+ request: newRequest("GET", "http://aaa.bbb.ccc:1234/111/222/333"),
60
+ vars: map[string]string{},
61
+ host: "aaa.bbb.ccc:1234",
62
+ path: "",
63
+ shouldMatch: true,
64
+ },
65
+ {
66
+ title: "Host route with port, wrong port in request URL",
67
+ route: new(Route).Host("aaa.bbb.ccc:1234"),
68
+ request: newRequest("GET", "http://aaa.bbb.ccc:9999/111/222/333"),
69
+ vars: map[string]string{},
70
+ host: "aaa.bbb.ccc:1234",
71
+ path: "",
72
+ shouldMatch: false,
73
+ },
74
+ {
75
+ title: "Host route, match with host in request header",
76
+ route: new(Route).Host("aaa.bbb.ccc"),
77
+ request: newRequestHost("GET", "/111/222/333", "aaa.bbb.ccc"),
78
+ vars: map[string]string{},
79
+ host: "aaa.bbb.ccc",
80
+ path: "",
81
+ shouldMatch: true,
82
+ },
83
+ {
84
+ title: "Host route, wrong host in request header",
85
+ route: new(Route).Host("aaa.bbb.ccc"),
86
+ request: newRequestHost("GET", "/111/222/333", "aaa.222.ccc"),
87
+ vars: map[string]string{},
88
+ host: "aaa.bbb.ccc",
89
+ path: "",
90
+ shouldMatch: false,
91
+ },
92
+ // BUG {new(Route).Host("aaa.bbb.ccc:1234"), newRequestHost("GET", "/111/222/333", "aaa.bbb.ccc:1234"), map[string]string{}, "aaa.bbb.ccc:1234", "", true},
93
+ {
94
+ title: "Host route with port, wrong host in request header",
95
+ route: new(Route).Host("aaa.bbb.ccc:1234"),
96
+ request: newRequestHost("GET", "/111/222/333", "aaa.bbb.ccc:9999"),
97
+ vars: map[string]string{},
98
+ host: "aaa.bbb.ccc:1234",
99
+ path: "",
100
+ shouldMatch: false,
101
+ },
102
+ {
103
+ title: "Host route with pattern, match",
104
+ route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc"),
105
+ request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
106
+ vars: map[string]string{"v1": "bbb"},
107
+ host: "aaa.bbb.ccc",
108
+ path: "",
109
+ shouldMatch: true,
110
+ },
111
+ {
112
+ title: "Host route with pattern, wrong host in request URL",
113
+ route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc"),
114
+ request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
115
+ vars: map[string]string{"v1": "bbb"},
116
+ host: "aaa.bbb.ccc",
117
+ path: "",
118
+ shouldMatch: false,
119
+ },
120
+ {
121
+ title: "Host route with multiple patterns, match",
122
+ route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}"),
123
+ request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
124
+ vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc"},
125
+ host: "aaa.bbb.ccc",
126
+ path: "",
127
+ shouldMatch: true,
128
+ },
129
+ {
130
+ title: "Host route with multiple patterns, wrong host in request URL",
131
+ route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}"),
132
+ request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
133
+ vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc"},
134
+ host: "aaa.bbb.ccc",
135
+ path: "",
136
+ shouldMatch: false,
137
+ },
138
+ }
139
+ for _, test := range tests {
140
+ testRoute(t, test)
141
+ }
142
+}
143
+
144
+func TestPath(t *testing.T) {
145
+ tests := []routeTest{
146
+ {
147
+ title: "Path route, match",
148
+ route: new(Route).Path("/111/222/333"),
149
+ request: newRequest("GET", "http://localhost/111/222/333"),
150
+ vars: map[string]string{},
151
+ host: "",
152
+ path: "/111/222/333",
153
+ shouldMatch: true,
154
+ },
155
+ {
156
+ title: "Path route, match with trailing slash in request and path",
157
+ route: new(Route).Path("/111/"),
158
+ request: newRequest("GET", "http://localhost/111/"),
159
+ vars: map[string]string{},
160
+ host: "",
161
+ path: "/111/",
162
+ shouldMatch: true,
163
+ },
164
+ {
165
+ title: "Path route, do not match with trailing slash in path",
166
+ route: new(Route).Path("/111/"),
167
+ request: newRequest("GET", "http://localhost/111"),
168
+ vars: map[string]string{},
169
+ host: "",
170
+ path: "/111",
171
+ shouldMatch: false,
172
+ },
173
+ {
174
+ title: "Path route, do not match with trailing slash in request",
175
+ route: new(Route).Path("/111"),
176
+ request: newRequest("GET", "http://localhost/111/"),
177
+ vars: map[string]string{},
178
+ host: "",
179
+ path: "/111/",
180
+ shouldMatch: false,
181
+ },
182
+ {
183
+ title: "Path route, wrong path in request in request URL",
184
+ route: new(Route).Path("/111/222/333"),
185
+ request: newRequest("GET", "http://localhost/1/2/3"),
186
+ vars: map[string]string{},
187
+ host: "",
188
+ path: "/111/222/333",
189
+ shouldMatch: false,
190
+ },
191
+ {
192
+ title: "Path route with pattern, match",
193
+ route: new(Route).Path("/111/{v1:[0-9]{3}}/333"),
194
+ request: newRequest("GET", "http://localhost/111/222/333"),
195
+ vars: map[string]string{"v1": "222"},
196
+ host: "",
197
+ path: "/111/222/333",
198
+ shouldMatch: true,
199
+ },
200
+ {
201
+ title: "Path route with pattern, URL in request does not match",
202
+ route: new(Route).Path("/111/{v1:[0-9]{3}}/333"),
203
+ request: newRequest("GET", "http://localhost/111/aaa/333"),
204
+ vars: map[string]string{"v1": "222"},
205
+ host: "",
206
+ path: "/111/222/333",
207
+ shouldMatch: false,
208
+ },
209
+ {
210
+ title: "Path route with multiple patterns, match",
211
+ route: new(Route).Path("/{v1:[0-9]{3}}/{v2:[0-9]{3}}/{v3:[0-9]{3}}"),
212
+ request: newRequest("GET", "http://localhost/111/222/333"),
213
+ vars: map[string]string{"v1": "111", "v2": "222", "v3": "333"},
214
+ host: "",
215
+ path: "/111/222/333",
216
+ shouldMatch: true,
217
+ },
218
+ {
219
+ title: "Path route with multiple patterns, URL in request does not match",
220
+ route: new(Route).Path("/{v1:[0-9]{3}}/{v2:[0-9]{3}}/{v3:[0-9]{3}}"),
221
+ request: newRequest("GET", "http://localhost/111/aaa/333"),
222
+ vars: map[string]string{"v1": "111", "v2": "222", "v3": "333"},
223
+ host: "",
224
+ path: "/111/222/333",
225
+ shouldMatch: false,
226
+ },
227
+ }
228
+
229
+ for _, test := range tests {
230
+ testRoute(t, test)
231
+ }
232
+}
233
+
234
+func TestPathPrefix(t *testing.T) {
235
+ tests := []routeTest{
236
+ {
237
+ title: "PathPrefix route, match",
238
+ route: new(Route).PathPrefix("/111"),
239
+ request: newRequest("GET", "http://localhost/111/222/333"),
240
+ vars: map[string]string{},
241
+ host: "",
242
+ path: "/111",
243
+ shouldMatch: true,
244
+ },
245
+ {
246
+ title: "PathPrefix route, match substring",
247
+ route: new(Route).PathPrefix("/1"),
248
+ request: newRequest("GET", "http://localhost/111/222/333"),
249
+ vars: map[string]string{},
250
+ host: "",
251
+ path: "/1",
252
+ shouldMatch: true,
253
+ },
254
+ {
255
+ title: "PathPrefix route, URL prefix in request does not match",
256
+ route: new(Route).PathPrefix("/111"),
257
+ request: newRequest("GET", "http://localhost/1/2/3"),
258
+ vars: map[string]string{},
259
+ host: "",
260
+ path: "/111",
261
+ shouldMatch: false,
262
+ },
263
+ {
264
+ title: "PathPrefix route with pattern, match",
265
+ route: new(Route).PathPrefix("/111/{v1:[0-9]{3}}"),
266
+ request: newRequest("GET", "http://localhost/111/222/333"),
267
+ vars: map[string]string{"v1": "222"},
268
+ host: "",
269
+ path: "/111/222",
270
+ shouldMatch: true,
271
+ },
272
+ {
273
+ title: "PathPrefix route with pattern, URL prefix in request does not match",
274
+ route: new(Route).PathPrefix("/111/{v1:[0-9]{3}}"),
275
+ request: newRequest("GET", "http://localhost/111/aaa/333"),
276
+ vars: map[string]string{"v1": "222"},
277
+ host: "",
278
+ path: "/111/222",
279
+ shouldMatch: false,
280
+ },
281
+ {
282
+ title: "PathPrefix route with multiple patterns, match",
283
+ route: new(Route).PathPrefix("/{v1:[0-9]{3}}/{v2:[0-9]{3}}"),
284
+ request: newRequest("GET", "http://localhost/111/222/333"),
285
+ vars: map[string]string{"v1": "111", "v2": "222"},
286
+ host: "",
287
+ path: "/111/222",
288
+ shouldMatch: true,
289
+ },
290
+ {
291
+ title: "PathPrefix route with multiple patterns, URL prefix in request does not match",
292
+ route: new(Route).PathPrefix("/{v1:[0-9]{3}}/{v2:[0-9]{3}}"),
293
+ request: newRequest("GET", "http://localhost/111/aaa/333"),
294
+ vars: map[string]string{"v1": "111", "v2": "222"},
295
+ host: "",
296
+ path: "/111/222",
297
+ shouldMatch: false,
298
+ },
299
+ }
300
+
301
+ for _, test := range tests {
302
+ testRoute(t, test)
303
+ }
304
+}
305
+
306
+func TestHostPath(t *testing.T) {
307
+ tests := []routeTest{
308
+ {
309
+ title: "Host and Path route, match",
310
+ route: new(Route).Host("aaa.bbb.ccc").Path("/111/222/333"),
311
+ request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
312
+ vars: map[string]string{},
313
+ host: "",
314
+ path: "",
315
+ shouldMatch: true,
316
+ },
317
+ {
318
+ title: "Host and Path route, wrong host in request URL",
319
+ route: new(Route).Host("aaa.bbb.ccc").Path("/111/222/333"),
320
+ request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
321
+ vars: map[string]string{},
322
+ host: "",
323
+ path: "",
324
+ shouldMatch: false,
325
+ },
326
+ {
327
+ title: "Host and Path route with pattern, match",
328
+ route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc").Path("/111/{v2:[0-9]{3}}/333"),
329
+ request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
330
+ vars: map[string]string{"v1": "bbb", "v2": "222"},
331
+ host: "aaa.bbb.ccc",
332
+ path: "/111/222/333",
333
+ shouldMatch: true,
334
+ },
335
+ {
336
+ title: "Host and Path route with pattern, URL in request does not match",
337
+ route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc").Path("/111/{v2:[0-9]{3}}/333"),
338
+ request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
339
+ vars: map[string]string{"v1": "bbb", "v2": "222"},
340
+ host: "aaa.bbb.ccc",
341
+ path: "/111/222/333",
342
+ shouldMatch: false,
343
+ },
344
+ {
345
+ title: "Host and Path route with multiple patterns, match",
346
+ route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}").Path("/{v4:[0-9]{3}}/{v5:[0-9]{3}}/{v6:[0-9]{3}}"),
347
+ request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
348
+ vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc", "v4": "111", "v5": "222", "v6": "333"},
349
+ host: "aaa.bbb.ccc",
350
+ path: "/111/222/333",
351
+ shouldMatch: true,
352
+ },
353
+ {
354
+ title: "Host and Path route with multiple patterns, URL in request does not match",
355
+ route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}").Path("/{v4:[0-9]{3}}/{v5:[0-9]{3}}/{v6:[0-9]{3}}"),
356
+ request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
357
+ vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc", "v4": "111", "v5": "222", "v6": "333"},
358
+ host: "aaa.bbb.ccc",
359
+ path: "/111/222/333",
360
+ shouldMatch: false,
361
+ },
362
+ }
363
+
364
+ for _, test := range tests {
365
+ testRoute(t, test)
366
+ }
367
+}
368
+
369
+func TestHeaders(t *testing.T) {
370
+ // newRequestHeaders creates a new request with a method, url, and headers
371
+ newRequestHeaders := func(method, url string, headers map[string]string) *http.Request {
372
+ req, err := http.NewRequest(method, url, nil)
373
+ if err != nil {
374
+ panic(err)
375
+ }
376
+ for k, v := range headers {
377
+ req.Header.Add(k, v)
378
+ }
379
+ return req
380
+ }
381
+
382
+ tests := []routeTest{
383
+ {
384
+ title: "Headers route, match",
385
+ route: new(Route).Headers("foo", "bar", "baz", "ding"),
386
+ request: newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "bar", "baz": "ding"}),
387
+ vars: map[string]string{},
388
+ host: "",
389
+ path: "",
390
+ shouldMatch: true,
391
+ },
392
+ {
393
+ title: "Headers route, bad header values",
394
+ route: new(Route).Headers("foo", "bar", "baz", "ding"),
395
+ request: newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "bar", "baz": "dong"}),
396
+ vars: map[string]string{},
397
+ host: "",
398
+ path: "",
399
+ shouldMatch: false,
400
+ },
401
+ }
402
+
403
+ for _, test := range tests {
404
+ testRoute(t, test)
405
+ }
406
+
407
+}
408
+
409
+func TestMethods(t *testing.T) {
410
+ tests := []routeTest{
411
+ {
412
+ title: "Methods route, match GET",
413
+ route: new(Route).Methods("GET", "POST"),
414
+ request: newRequest("GET", "http://localhost"),
415
+ vars: map[string]string{},
416
+ host: "",
417
+ path: "",
418
+ shouldMatch: true,
419
+ },
420
+ {
421
+ title: "Methods route, match POST",
422
+ route: new(Route).Methods("GET", "POST"),
423
+ request: newRequest("POST", "http://localhost"),
424
+ vars: map[string]string{},
425
+ host: "",
426
+ path: "",
427
+ shouldMatch: true,
428
+ },
429
+ {
430
+ title: "Methods route, bad method",
431
+ route: new(Route).Methods("GET", "POST"),
432
+ request: newRequest("PUT", "http://localhost"),
433
+ vars: map[string]string{},
434
+ host: "",
435
+ path: "",
436
+ shouldMatch: false,
437
+ },
438
+ }
439
+
440
+ for _, test := range tests {
441
+ testRoute(t, test)
442
+ }
443
+}
444
+
445
+func TestQueries(t *testing.T) {
446
+ tests := []routeTest{
447
+ {
448
+ title: "Queries route, match",
449
+ route: new(Route).Queries("foo", "bar", "baz", "ding"),
450
+ request: newRequest("GET", "http://localhost?foo=bar&baz=ding"),
451
+ vars: map[string]string{},
452
+ host: "",
453
+ path: "",
454
+ shouldMatch: true,
455
+ },
456
+ {
457
+ title: "Queries route, match with a query string",
458
+ route: new(Route).Host("www.example.com").Path("/api").Queries("foo", "bar", "baz", "ding"),
459
+ request: newRequest("GET", "http://www.example.com/api?foo=bar&baz=ding"),
460
+ vars: map[string]string{},
461
+ host: "",
462
+ path: "",
463
+ shouldMatch: true,
464
+ },
465
+ {
466
+ title: "Queries route, match with a query string out of order",
467
+ route: new(Route).Host("www.example.com").Path("/api").Queries("foo", "bar", "baz", "ding"),
468
+ request: newRequest("GET", "http://www.example.com/api?baz=ding&foo=bar"),
469
+ vars: map[string]string{},
470
+ host: "",
471
+ path: "",
472
+ shouldMatch: true,
473
+ },
474
+ {
475
+ title: "Queries route, bad query",
476
+ route: new(Route).Queries("foo", "bar", "baz", "ding"),
477
+ request: newRequest("GET", "http://localhost?foo=bar&baz=dong"),
478
+ vars: map[string]string{},
479
+ host: "",
480
+ path: "",
481
+ shouldMatch: false,
482
+ },
483
+ {
484
+ title: "Queries route with pattern, match",
485
+ route: new(Route).Queries("foo", "{v1}"),
486
+ request: newRequest("GET", "http://localhost?foo=bar"),
487
+ vars: map[string]string{"v1": "bar"},
488
+ host: "",
489
+ path: "",
490
+ shouldMatch: true,
491
+ },
492
+ {
493
+ title: "Queries route with multiple patterns, match",
494
+ route: new(Route).Queries("foo", "{v1}", "baz", "{v2}"),
495
+ request: newRequest("GET", "http://localhost?foo=bar&baz=ding"),
496
+ vars: map[string]string{"v1": "bar", "v2": "ding"},
497
+ host: "",
498
+ path: "",
499
+ shouldMatch: true,
500
+ },
501
+ {
502
+ title: "Queries route with regexp pattern, match",
503
+ route: new(Route).Queries("foo", "{v1:[0-9]+}"),
504
+ request: newRequest("GET", "http://localhost?foo=10"),
505
+ vars: map[string]string{"v1": "10"},
506
+ host: "",
507
+ path: "",
508
+ shouldMatch: true,
509
+ },
510
+ {
511
+ title: "Queries route with regexp pattern, regexp does not match",
512
+ route: new(Route).Queries("foo", "{v1:[0-9]+}"),
513
+ request: newRequest("GET", "http://localhost?foo=a"),
514
+ vars: map[string]string{},
515
+ host: "",
516
+ path: "",
517
+ shouldMatch: false,
518
+ },
519
+ }
520
+
521
+ for _, test := range tests {
522
+ testRoute(t, test)
523
+ }
524
+}
525
+
526
+func TestSchemes(t *testing.T) {
527
+ tests := []routeTest{
528
+ // Schemes
529
+ {
530
+ title: "Schemes route, match https",
531
+ route: new(Route).Schemes("https", "ftp"),
532
+ request: newRequest("GET", "https://localhost"),
533
+ vars: map[string]string{},
534
+ host: "",
535
+ path: "",
536
+ shouldMatch: true,
537
+ },
538
+ {
539
+ title: "Schemes route, match ftp",
540
+ route: new(Route).Schemes("https", "ftp"),
541
+ request: newRequest("GET", "ftp://localhost"),
542
+ vars: map[string]string{},
543
+ host: "",
544
+ path: "",
545
+ shouldMatch: true,
546
+ },
547
+ {
548
+ title: "Schemes route, bad scheme",
549
+ route: new(Route).Schemes("https", "ftp"),
550
+ request: newRequest("GET", "http://localhost"),
551
+ vars: map[string]string{},
552
+ host: "",
553
+ path: "",
554
+ shouldMatch: false,
555
+ },
556
+ }
557
+ for _, test := range tests {
558
+ testRoute(t, test)
559
+ }
560
+}
561
+
562
+func TestMatcherFunc(t *testing.T) {
563
+ m := func(r *http.Request, m *RouteMatch) bool {
564
+ if r.URL.Host == "aaa.bbb.ccc" {
565
+ return true
566
+ }
567
+ return false
568
+ }
569
+
570
+ tests := []routeTest{
571
+ {
572
+ title: "MatchFunc route, match",
573
+ route: new(Route).MatcherFunc(m),
574
+ request: newRequest("GET", "http://aaa.bbb.ccc"),
575
+ vars: map[string]string{},
576
+ host: "",
577
+ path: "",
578
+ shouldMatch: true,
579
+ },
580
+ {
581
+ title: "MatchFunc route, non-match",
582
+ route: new(Route).MatcherFunc(m),
583
+ request: newRequest("GET", "http://aaa.222.ccc"),
584
+ vars: map[string]string{},
585
+ host: "",
586
+ path: "",
587
+ shouldMatch: false,
588
+ },
589
+ }
590
+
591
+ for _, test := range tests {
592
+ testRoute(t, test)
593
+ }
594
+}
595
+
596
+func TestSubRouter(t *testing.T) {
597
+ subrouter1 := new(Route).Host("{v1:[a-z]+}.google.com").Subrouter()
598
+ subrouter2 := new(Route).PathPrefix("/foo/{v1}").Subrouter()
599
+
600
+ tests := []routeTest{
601
+ {
602
+ route: subrouter1.Path("/{v2:[a-z]+}"),
603
+ request: newRequest("GET", "http://aaa.google.com/bbb"),
604
+ vars: map[string]string{"v1": "aaa", "v2": "bbb"},
605
+ host: "aaa.google.com",
606
+ path: "/bbb",
607
+ shouldMatch: true,
608
+ },
609
+ {
610
+ route: subrouter1.Path("/{v2:[a-z]+}"),
611
+ request: newRequest("GET", "http://111.google.com/111"),
612
+ vars: map[string]string{"v1": "aaa", "v2": "bbb"},
613
+ host: "aaa.google.com",
614
+ path: "/bbb",
615
+ shouldMatch: false,
616
+ },
617
+ {
618
+ route: subrouter2.Path("/baz/{v2}"),
619
+ request: newRequest("GET", "http://localhost/foo/bar/baz/ding"),
620
+ vars: map[string]string{"v1": "bar", "v2": "ding"},
621
+ host: "",
622
+ path: "/foo/bar/baz/ding",
623
+ shouldMatch: true,
624
+ },
625
+ {
626
+ route: subrouter2.Path("/baz/{v2}"),
627
+ request: newRequest("GET", "http://localhost/foo/bar"),
628
+ vars: map[string]string{"v1": "bar", "v2": "ding"},
629
+ host: "",
630
+ path: "/foo/bar/baz/ding",
631
+ shouldMatch: false,
632
+ },
633
+ }
634
+
635
+ for _, test := range tests {
636
+ testRoute(t, test)
637
+ }
638
+}
639
+
640
+func TestNamedRoutes(t *testing.T) {
641
+ r1 := NewRouter()
642
+ r1.NewRoute().Name("a")
643
+ r1.NewRoute().Name("b")
644
+ r1.NewRoute().Name("c")
645
+
646
+ r2 := r1.NewRoute().Subrouter()
647
+ r2.NewRoute().Name("d")
648
+ r2.NewRoute().Name("e")
649
+ r2.NewRoute().Name("f")
650
+
651
+ r3 := r2.NewRoute().Subrouter()
652
+ r3.NewRoute().Name("g")
653
+ r3.NewRoute().Name("h")
654
+ r3.NewRoute().Name("i")
655
+
656
+ if r1.namedRoutes == nil || len(r1.namedRoutes) != 9 {
657
+ t.Errorf("Expected 9 named routes, got %v", r1.namedRoutes)
658
+ } else if r1.Get("i") == nil {
659
+ t.Errorf("Subroute name not registered")
660
+ }
661
+}
662
+
663
+func TestStrictSlash(t *testing.T) {
664
+ r := NewRouter()
665
+ r.StrictSlash(true)
666
+
667
+ tests := []routeTest{
668
+ {
669
+ title: "Redirect path without slash",
670
+ route: r.NewRoute().Path("/111/"),
671
+ request: newRequest("GET", "http://localhost/111"),
672
+ vars: map[string]string{},
673
+ host: "",
674
+ path: "/111/",
675
+ shouldMatch: true,
676
+ shouldRedirect: true,
677
+ },
678
+ {
679
+ title: "Do not redirect path with slash",
680
+ route: r.NewRoute().Path("/111/"),
681
+ request: newRequest("GET", "http://localhost/111/"),
682
+ vars: map[string]string{},
683
+ host: "",
684
+ path: "/111/",
685
+ shouldMatch: true,
686
+ shouldRedirect: false,
687
+ },
688
+ {
689
+ title: "Redirect path with slash",
690
+ route: r.NewRoute().Path("/111"),
691
+ request: newRequest("GET", "http://localhost/111/"),
692
+ vars: map[string]string{},
693
+ host: "",
694
+ path: "/111",
695
+ shouldMatch: true,
696
+ shouldRedirect: true,
697
+ },
698
+ {
699
+ title: "Do not redirect path without slash",
700
+ route: r.NewRoute().Path("/111"),
701
+ request: newRequest("GET", "http://localhost/111"),
702
+ vars: map[string]string{},
703
+ host: "",
704
+ path: "/111",
705
+ shouldMatch: true,
706
+ shouldRedirect: false,
707
+ },
708
+ {
709
+ title: "Propagate StrictSlash to subrouters",
710
+ route: r.NewRoute().PathPrefix("/static/").Subrouter().Path("/images/"),
711
+ request: newRequest("GET", "http://localhost/static/images"),
712
+ vars: map[string]string{},
713
+ host: "",
714
+ path: "/static/images/",
715
+ shouldMatch: true,
716
+ shouldRedirect: true,
717
+ },
718
+ {
719
+ title: "Ignore StrictSlash for path prefix",
720
+ route: r.NewRoute().PathPrefix("/static/"),
721
+ request: newRequest("GET", "http://localhost/static/logo.png"),
722
+ vars: map[string]string{},
723
+ host: "",
724
+ path: "/static/",
725
+ shouldMatch: true,
726
+ shouldRedirect: false,
727
+ },
728
+ }
729
+
730
+ for _, test := range tests {
731
+ testRoute(t, test)
732
+ }
733
+}
734
+
735
+// ----------------------------------------------------------------------------
736
+// Helpers
737
+// ----------------------------------------------------------------------------
738
+
739
+func getRouteTemplate(route *Route) string {
740
+ host, path := "none", "none"
741
+ if route.regexp != nil {
742
+ if route.regexp.host != nil {
743
+ host = route.regexp.host.template
744
+ }
745
+ if route.regexp.path != nil {
746
+ path = route.regexp.path.template
747
+ }
748
+ }
749
+ return fmt.Sprintf("Host: %v, Path: %v", host, path)
750
+}
751
+
752
+func testRoute(t *testing.T, test routeTest) {
753
+ request := test.request
754
+ route := test.route
755
+ vars := test.vars
756
+ shouldMatch := test.shouldMatch
757
+ host := test.host
758
+ path := test.path
759
+ url := test.host + test.path
760
+ shouldRedirect := test.shouldRedirect
761
+
762
+ var match RouteMatch
763
+ ok := route.Match(request, &match)
764
+ if ok != shouldMatch {
765
+ msg := "Should match"
766
+ if !shouldMatch {
767
+ msg = "Should not match"
768
+ }
769
+ t.Errorf("(%v) %v:\nRoute: %#v\nRequest: %#v\nVars: %v\n", test.title, msg, route, request, vars)
770
+ return
771
+ }
772
+ if shouldMatch {
773
+ if test.vars != nil && !stringMapEqual(test.vars, match.Vars) {
774
+ t.Errorf("(%v) Vars not equal: expected %v, got %v", test.title, vars, match.Vars)
775
+ return
776
+ }
777
+ if host != "" {
778
+ u, _ := test.route.URLHost(mapToPairs(match.Vars)...)
779
+ if host != u.Host {
780
+ t.Errorf("(%v) URLHost not equal: expected %v, got %v -- %v", test.title, host, u.Host, getRouteTemplate(route))
781
+ return
782
+ }
783
+ }
784
+ if path != "" {
785
+ u, _ := route.URLPath(mapToPairs(match.Vars)...)
786
+ if path != u.Path {
787
+ t.Errorf("(%v) URLPath not equal: expected %v, got %v -- %v", test.title, path, u.Path, getRouteTemplate(route))
788
+ return
789
+ }
790
+ }
791
+ if url != "" {
792
+ u, _ := route.URL(mapToPairs(match.Vars)...)
793
+ if url != u.Host+u.Path {
794
+ t.Errorf("(%v) URL not equal: expected %v, got %v -- %v", test.title, url, u.Host+u.Path, getRouteTemplate(route))
795
+ return
796
+ }
797
+ }
798
+ if shouldRedirect && match.Handler == nil {
799
+ t.Errorf("(%v) Did not redirect", test.title)
800
+ return
801
+ }
802
+ if !shouldRedirect && match.Handler != nil {
803
+ t.Errorf("(%v) Unexpected redirect", test.title)
804
+ return
805
+ }
806
+ }
807
+}
808
+
809
+// Tests that the context is cleared or not cleared properly depending on
810
+// the configuration of the router
811
+func TestKeepContext(t *testing.T) {
812
+ func1 := func(w http.ResponseWriter, r *http.Request) {}
813
+
814
+ r := NewRouter()
815
+ r.HandleFunc("/", func1).Name("func1")
816
+
817
+ req, _ := http.NewRequest("GET", "http://localhost/", nil)
818
+ context.Set(req, "t", 1)
819
+
820
+ res := new(http.ResponseWriter)
821
+ r.ServeHTTP(*res, req)
822
+
823
+ if _, ok := context.GetOk(req, "t"); ok {
824
+ t.Error("Context should have been cleared at end of request")
825
+ }
826
+
827
+ r.KeepContext = true
828
+
829
+ req, _ = http.NewRequest("GET", "http://localhost/", nil)
830
+ context.Set(req, "t", 1)
831
+
832
+ r.ServeHTTP(*res, req)
833
+ if _, ok := context.GetOk(req, "t"); !ok {
834
+ t.Error("Context should NOT have been cleared at end of request")
835
+ }
836
+
837
+}
838
+
839
+type TestA301ResponseWriter struct {
840
+ hh http.Header
841
+ status int
842
+}
843
+
844
+func (ho TestA301ResponseWriter) Header() http.Header {
845
+ return http.Header(ho.hh)
846
+}
847
+
848
+func (ho TestA301ResponseWriter) Write(b []byte) (int, error) {
849
+ return 0, nil
850
+}
851
+
852
+func (ho TestA301ResponseWriter) WriteHeader(code int) {
853
+ ho.status = code
854
+}
855
+
856
+func Test301Redirect(t *testing.T) {
857
+ m := make(http.Header)
858
+
859
+ func1 := func(w http.ResponseWriter, r *http.Request) {}
860
+ func2 := func(w http.ResponseWriter, r *http.Request) {}
861
+
862
+ r := NewRouter()
863
+ r.HandleFunc("/api/", func2).Name("func2")
864
+ r.HandleFunc("/", func1).Name("func1")
865
+
866
+ req, _ := http.NewRequest("GET", "http://localhost//api/?abc=def", nil)
867
+
868
+ res := TestA301ResponseWriter{
869
+ hh: m,
870
+ status: 0,
871
+ }
872
+ r.ServeHTTP(&res, req)
873
+
874
+ if "http://localhost/api/?abc=def" != res.hh["Location"][0] {
875
+ t.Errorf("Should have complete URL with query string")
876
+ }
877
+}
878
+
879
+// https://plus.google.com/101022900381697718949/posts/eWy6DjFJ6uW
880
+func TestSubrouterHeader(t *testing.T) {
881
+ expected := "func1 response"
882
+ func1 := func(w http.ResponseWriter, r *http.Request) {
883
+ fmt.Fprint(w, expected)
884
+ }
885
+ func2 := func(http.ResponseWriter, *http.Request) {}
886
+
887
+ r := NewRouter()
888
+ s := r.Headers("SomeSpecialHeader", "").Subrouter()
889
+ s.HandleFunc("/", func1).Name("func1")
890
+ r.HandleFunc("/", func2).Name("func2")
891
+
892
+ req, _ := http.NewRequest("GET", "http://localhost/", nil)
893
+ req.Header.Add("SomeSpecialHeader", "foo")
894
+ match := new(RouteMatch)
895
+ matched := r.Match(req, match)
896
+ if !matched {
897
+ t.Errorf("Should match request")
898
+ }
899
+ if match.Route.GetName() != "func1" {
900
+ t.Errorf("Expecting func1 handler, got %s", match.Route.GetName())
901
+ }
902
+ resp := NewRecorder()
903
+ match.Handler.ServeHTTP(resp, req)
904
+ if resp.Body.String() != expected {
905
+ t.Errorf("Expecting %q", expected)
906
+ }
907
+}
908
+
909
+// mapToPairs converts a string map to a slice of string pairs
910
+func mapToPairs(m map[string]string) []string {
911
+ var i int
912
+ p := make([]string, len(m)*2)
913
+ for k, v := range m {
914
+ p[i] = k
915
+ p[i+1] = v
916
+ i += 2
917
+ }
918
+ return p
919
+}
920
+
921
+// stringMapEqual checks the equality of two string maps
922
+func stringMapEqual(m1, m2 map[string]string) bool {
923
+ nil1 := m1 == nil
924
+ nil2 := m2 == nil
925
+ if nil1 != nil2 || len(m1) != len(m2) {
926
+ return false
927
+ }
928
+ for k, v := range m1 {
929
+ if v != m2[k] {
930
+ return false
931
+ }
932
+ }
933
+ return true
934
+}
935
+
936
+// newRequest is a helper function to create a new request with a method and url
937
+func newRequest(method, url string) *http.Request {
938
+ req, err := http.NewRequest(method, url, nil)
939
+ if err != nil {
940
+ panic(err)
941
+ }
942
+ return req
943
+}
Godeps/_workspace/src/github.com/gorilla/mux/old_test.go
new
+714
@@ -0,0 +1,714 @@
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 schemeMatcherTest struct {
333
+ matcher schemeMatcher
334
+ url string
335
+ result bool
336
+}
337
+
338
+var schemeMatcherTests = []schemeMatcherTest{
339
+ {
340
+ matcher: schemeMatcher([]string{"http", "https"}),
341
+ url: "http://localhost:8080/",
342
+ result: true,
343
+ },
344
+ {
345
+ matcher: schemeMatcher([]string{"http", "https"}),
346
+ url: "https://localhost:8080/",
347
+ result: true,
348
+ },
349
+ {
350
+ matcher: schemeMatcher([]string{"https"}),
351
+ url: "http://localhost:8080/",
352
+ result: false,
353
+ },
354
+ {
355
+ matcher: schemeMatcher([]string{"http"}),
356
+ url: "https://localhost:8080/",
357
+ result: false,
358
+ },
359
+}
360
+
361
+type urlBuildingTest struct {
362
+ route *Route
363
+ vars []string
364
+ url string
365
+}
366
+
367
+var urlBuildingTests = []urlBuildingTest{
368
+ {
369
+ route: new(Route).Host("foo.domain.com"),
370
+ vars: []string{},
371
+ url: "http://foo.domain.com",
372
+ },
373
+ {
374
+ route: new(Route).Host("{subdomain}.domain.com"),
375
+ vars: []string{"subdomain", "bar"},
376
+ url: "http://bar.domain.com",
377
+ },
378
+ {
379
+ route: new(Route).Host("foo.domain.com").Path("/articles"),
380
+ vars: []string{},
381
+ url: "http://foo.domain.com/articles",
382
+ },
383
+ {
384
+ route: new(Route).Path("/articles"),
385
+ vars: []string{},
386
+ url: "/articles",
387
+ },
388
+ {
389
+ route: new(Route).Path("/articles/{category}/{id:[0-9]+}"),
390
+ vars: []string{"category", "technology", "id", "42"},
391
+ url: "/articles/technology/42",
392
+ },
393
+ {
394
+ route: new(Route).Host("{subdomain}.domain.com").Path("/articles/{category}/{id:[0-9]+}"),
395
+ vars: []string{"subdomain", "foo", "category", "technology", "id", "42"},
396
+ url: "http://foo.domain.com/articles/technology/42",
397
+ },
398
+}
399
+
400
+func TestHeaderMatcher(t *testing.T) {
401
+ for _, v := range headerMatcherTests {
402
+ request, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
403
+ for key, value := range v.headers {
404
+ request.Header.Add(key, value)
405
+ }
406
+ var routeMatch RouteMatch
407
+ result := v.matcher.Match(request, &routeMatch)
408
+ if result != v.result {
409
+ if v.result {
410
+ t.Errorf("%#v: should match %v.", v.matcher, request.Header)
411
+ } else {
412
+ t.Errorf("%#v: should not match %v.", v.matcher, request.Header)
413
+ }
414
+ }
415
+ }
416
+}
417
+
418
+func TestHostMatcher(t *testing.T) {
419
+ for _, v := range hostMatcherTests {
420
+ request, _ := http.NewRequest("GET", v.url, nil)
421
+ var routeMatch RouteMatch
422
+ result := v.matcher.Match(request, &routeMatch)
423
+ vars := routeMatch.Vars
424
+ if result != v.result {
425
+ if v.result {
426
+ t.Errorf("%#v: should match %v.", v.matcher, v.url)
427
+ } else {
428
+ t.Errorf("%#v: should not match %v.", v.matcher, v.url)
429
+ }
430
+ }
431
+ if result {
432
+ if len(vars) != len(v.vars) {
433
+ t.Errorf("%#v: vars length should be %v, got %v.", v.matcher, len(v.vars), len(vars))
434
+ }
435
+ for name, value := range vars {
436
+ if v.vars[name] != value {
437
+ t.Errorf("%#v: expected value %v for key %v, got %v.", v.matcher, v.vars[name], name, value)
438
+ }
439
+ }
440
+ } else {
441
+ if len(vars) != 0 {
442
+ t.Errorf("%#v: vars length should be 0, got %v.", v.matcher, len(vars))
443
+ }
444
+ }
445
+ }
446
+}
447
+
448
+func TestMethodMatcher(t *testing.T) {
449
+ for _, v := range methodMatcherTests {
450
+ request, _ := http.NewRequest(v.method, "http://localhost:8080/", nil)
451
+ var routeMatch RouteMatch
452
+ result := v.matcher.Match(request, &routeMatch)
453
+ if result != v.result {
454
+ if v.result {
455
+ t.Errorf("%#v: should match %v.", v.matcher, v.method)
456
+ } else {
457
+ t.Errorf("%#v: should not match %v.", v.matcher, v.method)
458
+ }
459
+ }
460
+ }
461
+}
462
+
463
+func TestPathMatcher(t *testing.T) {
464
+ for _, v := range pathMatcherTests {
465
+ request, _ := http.NewRequest("GET", v.url, nil)
466
+ var routeMatch RouteMatch
467
+ result := v.matcher.Match(request, &routeMatch)
468
+ vars := routeMatch.Vars
469
+ if result != v.result {
470
+ if v.result {
471
+ t.Errorf("%#v: should match %v.", v.matcher, v.url)
472
+ } else {
473
+ t.Errorf("%#v: should not match %v.", v.matcher, v.url)
474
+ }
475
+ }
476
+ if result {
477
+ if len(vars) != len(v.vars) {
478
+ t.Errorf("%#v: vars length should be %v, got %v.", v.matcher, len(v.vars), len(vars))
479
+ }
480
+ for name, value := range vars {
481
+ if v.vars[name] != value {
482
+ t.Errorf("%#v: expected value %v for key %v, got %v.", v.matcher, v.vars[name], name, value)
483
+ }
484
+ }
485
+ } else {
486
+ if len(vars) != 0 {
487
+ t.Errorf("%#v: vars length should be 0, got %v.", v.matcher, len(vars))
488
+ }
489
+ }
490
+ }
491
+}
492
+
493
+func TestSchemeMatcher(t *testing.T) {
494
+ for _, v := range schemeMatcherTests {
495
+ request, _ := http.NewRequest("GET", v.url, nil)
496
+ var routeMatch RouteMatch
497
+ result := v.matcher.Match(request, &routeMatch)
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
+ }
506
+}
507
+
508
+func TestUrlBuilding(t *testing.T) {
509
+
510
+ for _, v := range urlBuildingTests {
511
+ u, _ := v.route.URL(v.vars...)
512
+ url := u.String()
513
+ if url != v.url {
514
+ t.Errorf("expected %v, got %v", v.url, url)
515
+ /*
516
+ reversePath := ""
517
+ reverseHost := ""
518
+ if v.route.pathTemplate != nil {
519
+ reversePath = v.route.pathTemplate.Reverse
520
+ }
521
+ if v.route.hostTemplate != nil {
522
+ reverseHost = v.route.hostTemplate.Reverse
523
+ }
524
+
525
+ t.Errorf("%#v:\nexpected: %q\ngot: %q\nreverse path: %q\nreverse host: %q", v.route, v.url, url, reversePath, reverseHost)
526
+ */
527
+ }
528
+ }
529
+
530
+ ArticleHandler := func(w http.ResponseWriter, r *http.Request) {
531
+ }
532
+
533
+ router := NewRouter()
534
+ router.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).Name("article")
535
+
536
+ url, _ := router.Get("article").URL("category", "technology", "id", "42")
537
+ expected := "/articles/technology/42"
538
+ if url.String() != expected {
539
+ t.Errorf("Expected %v, got %v", expected, url.String())
540
+ }
541
+}
542
+
543
+func TestMatchedRouteName(t *testing.T) {
544
+ routeName := "stock"
545
+ router := NewRouter()
546
+ route := router.NewRoute().Path("/products/").Name(routeName)
547
+
548
+ url := "http://www.domain.com/products/"
549
+ request, _ := http.NewRequest("GET", url, nil)
550
+ var rv RouteMatch
551
+ ok := router.Match(request, &rv)
552
+
553
+ if !ok || rv.Route != route {
554
+ t.Errorf("Expected same route, got %+v.", rv.Route)
555
+ }
556
+
557
+ retName := rv.Route.GetName()
558
+ if retName != routeName {
559
+ t.Errorf("Expected %q, got %q.", routeName, retName)
560
+ }
561
+}
562
+
563
+func TestSubRouting(t *testing.T) {
564
+ // Example from docs.
565
+ router := NewRouter()
566
+ subrouter := router.NewRoute().Host("www.domain.com").Subrouter()
567
+ route := subrouter.NewRoute().Path("/products/").Name("products")
568
+
569
+ url := "http://www.domain.com/products/"
570
+ request, _ := http.NewRequest("GET", url, nil)
571
+ var rv RouteMatch
572
+ ok := router.Match(request, &rv)
573
+
574
+ if !ok || rv.Route != route {
575
+ t.Errorf("Expected same route, got %+v.", rv.Route)
576
+ }
577
+
578
+ u, _ := router.Get("products").URL()
579
+ builtUrl := u.String()
580
+ // Yay, subroute aware of the domain when building!
581
+ if builtUrl != url {
582
+ t.Errorf("Expected %q, got %q.", url, builtUrl)
583
+ }
584
+}
585
+
586
+func TestVariableNames(t *testing.T) {
587
+ route := new(Route).Host("{arg1}.domain.com").Path("/{arg1}/{arg2:[0-9]+}")
588
+ if route.err == nil {
589
+ t.Errorf("Expected error for duplicated variable names")
590
+ }
591
+}
592
+
593
+func TestRedirectSlash(t *testing.T) {
594
+ var route *Route
595
+ var routeMatch RouteMatch
596
+ r := NewRouter()
597
+
598
+ r.StrictSlash(false)
599
+ route = r.NewRoute()
600
+ if route.strictSlash != false {
601
+ t.Errorf("Expected false redirectSlash.")
602
+ }
603
+
604
+ r.StrictSlash(true)
605
+ route = r.NewRoute()
606
+ if route.strictSlash != true {
607
+ t.Errorf("Expected true redirectSlash.")
608
+ }
609
+
610
+ route = new(Route)
611
+ route.strictSlash = true
612
+ route.Path("/{arg1}/{arg2:[0-9]+}/")
613
+ request, _ := http.NewRequest("GET", "http://localhost/foo/123", nil)
614
+ routeMatch = RouteMatch{}
615
+ _ = route.Match(request, &routeMatch)
616
+ vars := routeMatch.Vars
617
+ if vars["arg1"] != "foo" {
618
+ t.Errorf("Expected foo.")
619
+ }
620
+ if vars["arg2"] != "123" {
621
+ t.Errorf("Expected 123.")
622
+ }
623
+ rsp := NewRecorder()
624
+ routeMatch.Handler.ServeHTTP(rsp, request)
625
+ if rsp.HeaderMap.Get("Location") != "http://localhost/foo/123/" {
626
+ t.Errorf("Expected redirect header.")
627
+ }
628
+
629
+ route = new(Route)
630
+ route.strictSlash = true
631
+ route.Path("/{arg1}/{arg2:[0-9]+}")
632
+ request, _ = http.NewRequest("GET", "http://localhost/foo/123/", nil)
633
+ routeMatch = RouteMatch{}
634
+ _ = route.Match(request, &routeMatch)
635
+ vars = routeMatch.Vars
636
+ if vars["arg1"] != "foo" {
637
+ t.Errorf("Expected foo.")
638
+ }
639
+ if vars["arg2"] != "123" {
640
+ t.Errorf("Expected 123.")
641
+ }
642
+ rsp = NewRecorder()
643
+ routeMatch.Handler.ServeHTTP(rsp, request)
644
+ if rsp.HeaderMap.Get("Location") != "http://localhost/foo/123" {
645
+ t.Errorf("Expected redirect header.")
646
+ }
647
+}
648
+
649
+// Test for the new regexp library, still not available in stable Go.
650
+func TestNewRegexp(t *testing.T) {
651
+ var p *routeRegexp
652
+ var matches []string
653
+
654
+ tests := map[string]map[string][]string{
655
+ "/{foo:a{2}}": {
656
+ "/a": nil,
657
+ "/aa": {"aa"},
658
+ "/aaa": nil,
659
+ "/aaaa": nil,
660
+ },
661
+ "/{foo:a{2,}}": {
662
+ "/a": nil,
663
+ "/aa": {"aa"},
664
+ "/aaa": {"aaa"},
665
+ "/aaaa": {"aaaa"},
666
+ },
667
+ "/{foo:a{2,3}}": {
668
+ "/a": nil,
669
+ "/aa": {"aa"},
670
+ "/aaa": {"aaa"},
671
+ "/aaaa": nil,
672
+ },
673
+ "/{foo:[a-z]{3}}/{bar:[a-z]{2}}": {
674
+ "/a": nil,
675
+ "/ab": nil,
676
+ "/abc": nil,
677
+ "/abcd": nil,
678
+ "/abc/ab": {"abc", "ab"},
679
+ "/abc/abc": nil,
680
+ "/abcd/ab": nil,
681
+ },
682
+ `/{foo:\w{3,}}/{bar:\d{2,}}`: {
683
+ "/a": nil,
684
+ "/ab": nil,
685
+ "/abc": nil,
686
+ "/abc/1": nil,
687
+ "/abc/12": {"abc", "12"},
688
+ "/abcd/12": {"abcd", "12"},
689
+ "/abcd/123": {"abcd", "123"},
690
+ },
691
+ }
692
+
693
+ for pattern, paths := range tests {
694
+ p, _ = newRouteRegexp(pattern, false, false, false, false)
695
+ for path, result := range paths {
696
+ matches = p.regexp.FindStringSubmatch(path)
697
+ if result == nil {
698
+ if matches != nil {
699
+ t.Errorf("%v should not match %v.", pattern, path)
700
+ }
701
+ } else {
702
+ if len(matches) != len(result)+1 {
703
+ t.Errorf("Expected %v matches, got %v.", len(result)+1, len(matches))
704
+ } else {
705
+ for k, v := range result {
706
+ if matches[k+1] != v {
707
+ t.Errorf("Expected %v, got %v.", v, matches[k+1])
708
+ }
709
+ }
710
+ }
711
+ }
712
+ }
713
+ }
714
+}
Godeps/_workspace/src/github.com/gorilla/mux/regexp.go
new
+274
@@ -0,0 +1,274 @@
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, a path or a query string.
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, matchQuery, 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 matchQuery {
37
+ defaultPattern = "[^?&]+"
38
+ matchPrefix, strictSlash = true, false
39
+ } else if matchHost {
40
+ defaultPattern = "[^.]+"
41
+ matchPrefix, strictSlash = false, false
42
+ }
43
+ if matchPrefix {
44
+ strictSlash = false
45
+ }
46
+ // Set a flag for strictSlash.
47
+ endSlash := false
48
+ if strictSlash && strings.HasSuffix(tpl, "/") {
49
+ tpl = tpl[:len(tpl)-1]
50
+ endSlash = true
51
+ }
52
+ varsN := make([]string, len(idxs)/2)
53
+ varsR := make([]*regexp.Regexp, len(idxs)/2)
54
+ pattern := bytes.NewBufferString("")
55
+ if !matchQuery {
56
+ pattern.WriteByte('^')
57
+ }
58
+ reverse := bytes.NewBufferString("")
59
+ var end int
60
+ var err error
61
+ for i := 0; i < len(idxs); i += 2 {
62
+ // Set all values we are interested in.
63
+ raw := tpl[end:idxs[i]]
64
+ end = idxs[i+1]
65
+ parts := strings.SplitN(tpl[idxs[i]+1:end-1], ":", 2)
66
+ name := parts[0]
67
+ patt := defaultPattern
68
+ if len(parts) == 2 {
69
+ patt = parts[1]
70
+ }
71
+ // Name or pattern can't be empty.
72
+ if name == "" || patt == "" {
73
+ return nil, fmt.Errorf("mux: missing name or pattern in %q",
74
+ tpl[idxs[i]:end])
75
+ }
76
+ // Build the regexp pattern.
77
+ fmt.Fprintf(pattern, "%s(%s)", regexp.QuoteMeta(raw), patt)
78
+ // Build the reverse template.
79
+ fmt.Fprintf(reverse, "%s%%s", raw)
80
+ // Append variable name and compiled pattern.
81
+ varsN[i/2] = name
82
+ varsR[i/2], err = regexp.Compile(fmt.Sprintf("^%s$", patt))
83
+ if err != nil {
84
+ return nil, err
85
+ }
86
+ }
87
+ // Add the remaining.
88
+ raw := tpl[end:]
89
+ pattern.WriteString(regexp.QuoteMeta(raw))
90
+ if strictSlash {
91
+ pattern.WriteString("[/]?")
92
+ }
93
+ if !matchPrefix {
94
+ pattern.WriteByte('$')
95
+ }
96
+ reverse.WriteString(raw)
97
+ if endSlash {
98
+ reverse.WriteByte('/')
99
+ }
100
+ // Compile full regexp.
101
+ reg, errCompile := regexp.Compile(pattern.String())
102
+ if errCompile != nil {
103
+ return nil, errCompile
104
+ }
105
+ // Done!
106
+ return &routeRegexp{
107
+ template: template,
108
+ matchHost: matchHost,
109
+ matchQuery: matchQuery,
110
+ strictSlash: strictSlash,
111
+ regexp: reg,
112
+ reverse: reverse.String(),
113
+ varsN: varsN,
114
+ varsR: varsR,
115
+ }, nil
116
+}
117
+
118
+// routeRegexp stores a regexp to match a host or path and information to
119
+// collect and validate route variables.
120
+type routeRegexp struct {
121
+ // The unmodified template.
122
+ template string
123
+ // True for host match, false for path or query string match.
124
+ matchHost bool
125
+ // True for query string match, false for path and host match.
126
+ matchQuery bool
127
+ // The strictSlash value defined on the route, but disabled if PathPrefix was used.
128
+ strictSlash bool
129
+ // Expanded regexp.
130
+ regexp *regexp.Regexp
131
+ // Reverse template.
132
+ reverse string
133
+ // Variable names.
134
+ varsN []string
135
+ // Variable regexps (validators).
136
+ varsR []*regexp.Regexp
137
+}
138
+
139
+// Match matches the regexp against the URL host or path.
140
+func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool {
141
+ if !r.matchHost {
142
+ if r.matchQuery {
143
+ return r.regexp.MatchString(req.URL.RawQuery)
144
+ } else {
145
+ return r.regexp.MatchString(req.URL.Path)
146
+ }
147
+ }
148
+ return r.regexp.MatchString(getHost(req))
149
+}
150
+
151
+// url builds a URL part using the given values.
152
+func (r *routeRegexp) url(pairs ...string) (string, error) {
153
+ values, err := mapFromPairs(pairs...)
154
+ if err != nil {
155
+ return "", err
156
+ }
157
+ urlValues := make([]interface{}, len(r.varsN))
158
+ for k, v := range r.varsN {
159
+ value, ok := values[v]
160
+ if !ok {
161
+ return "", fmt.Errorf("mux: missing route variable %q", v)
162
+ }
163
+ urlValues[k] = value
164
+ }
165
+ rv := fmt.Sprintf(r.reverse, urlValues...)
166
+ if !r.regexp.MatchString(rv) {
167
+ // The URL is checked against the full regexp, instead of checking
168
+ // individual variables. This is faster but to provide a good error
169
+ // message, we check individual regexps if the URL doesn't match.
170
+ for k, v := range r.varsN {
171
+ if !r.varsR[k].MatchString(values[v]) {
172
+ return "", fmt.Errorf(
173
+ "mux: variable %q doesn't match, expected %q", values[v],
174
+ r.varsR[k].String())
175
+ }
176
+ }
177
+ }
178
+ return rv, nil
179
+}
180
+
181
+// braceIndices returns the first level curly brace indices from a string.
182
+// It returns an error in case of unbalanced braces.
183
+func braceIndices(s string) ([]int, error) {
184
+ var level, idx int
185
+ idxs := make([]int, 0)
186
+ for i := 0; i < len(s); i++ {
187
+ switch s[i] {
188
+ case '{':
189
+ if level++; level == 1 {
190
+ idx = i
191
+ }
192
+ case '}':
193
+ if level--; level == 0 {
194
+ idxs = append(idxs, idx, i+1)
195
+ } else if level < 0 {
196
+ return nil, fmt.Errorf("mux: unbalanced braces in %q", s)
197
+ }
198
+ }
199
+ }
200
+ if level != 0 {
201
+ return nil, fmt.Errorf("mux: unbalanced braces in %q", s)
202
+ }
203
+ return idxs, nil
204
+}
205
+
206
+// ----------------------------------------------------------------------------
207
+// routeRegexpGroup
208
+// ----------------------------------------------------------------------------
209
+
210
+// routeRegexpGroup groups the route matchers that carry variables.
211
+type routeRegexpGroup struct {
212
+ host *routeRegexp
213
+ path *routeRegexp
214
+ queries []*routeRegexp
215
+}
216
+
217
+// setMatch extracts the variables from the URL once a route matches.
218
+func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route) {
219
+ // Store host variables.
220
+ if v.host != nil {
221
+ hostVars := v.host.regexp.FindStringSubmatch(getHost(req))
222
+ if hostVars != nil {
223
+ for k, v := range v.host.varsN {
224
+ m.Vars[v] = hostVars[k+1]
225
+ }
226
+ }
227
+ }
228
+ // Store path variables.
229
+ if v.path != nil {
230
+ pathVars := v.path.regexp.FindStringSubmatch(req.URL.Path)
231
+ if pathVars != nil {
232
+ for k, v := range v.path.varsN {
233
+ m.Vars[v] = pathVars[k+1]
234
+ }
235
+ // Check if we should redirect.
236
+ if v.path.strictSlash {
237
+ p1 := strings.HasSuffix(req.URL.Path, "/")
238
+ p2 := strings.HasSuffix(v.path.template, "/")
239
+ if p1 != p2 {
240
+ u, _ := url.Parse(req.URL.String())
241
+ if p1 {
242
+ u.Path = u.Path[:len(u.Path)-1]
243
+ } else {
244
+ u.Path += "/"
245
+ }
246
+ m.Handler = http.RedirectHandler(u.String(), 301)
247
+ }
248
+ }
249
+ }
250
+ }
251
+ // Store query string variables.
252
+ rawQuery := req.URL.RawQuery
253
+ for _, q := range v.queries {
254
+ queryVars := q.regexp.FindStringSubmatch(rawQuery)
255
+ if queryVars != nil {
256
+ for k, v := range q.varsN {
257
+ m.Vars[v] = queryVars[k+1]
258
+ }
259
+ }
260
+ }
261
+}
262
+
263
+// getHost tries its best to return the request host.
264
+func getHost(r *http.Request) string {
265
+ if !r.URL.IsAbs() {
266
+ host := r.Host
267
+ // Slice off any port information.
268
+ if i := strings.Index(host, ":"); i != -1 {
269
+ host = host[:i]
270
+ }
271
+ return host
272
+ }
273
+ return r.URL.Host
274
+}
Godeps/_workspace/src/github.com/gorilla/mux/route.go
new
+524
@@ -0,0 +1,524 @@
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, matchQuery bool) error {
139
+ if r.err != nil {
140
+ return r.err
141
+ }
142
+ r.regexp = r.getRegexpGroup()
143
+ if !matchHost && !matchQuery {
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, matchQuery, r.strictSlash)
152
+ if err != nil {
153
+ return err
154
+ }
155
+ for _, q := range r.regexp.queries {
156
+ if err = uniqueVars(rr.varsN, q.varsN); err != nil {
157
+ return err
158
+ }
159
+ }
160
+ if matchHost {
161
+ if r.regexp.path != nil {
162
+ if err = uniqueVars(rr.varsN, r.regexp.path.varsN); err != nil {
163
+ return err
164
+ }
165
+ }
166
+ r.regexp.host = rr
167
+ } else {
168
+ if r.regexp.host != nil {
169
+ if err = uniqueVars(rr.varsN, r.regexp.host.varsN); err != nil {
170
+ return err
171
+ }
172
+ }
173
+ if matchQuery {
174
+ r.regexp.queries = append(r.regexp.queries, rr)
175
+ } else {
176
+ r.regexp.path = rr
177
+ }
178
+ }
179
+ r.addMatcher(rr)
180
+ return nil
181
+}
182
+
183
+// Headers --------------------------------------------------------------------
184
+
185
+// headerMatcher matches the request against header values.
186
+type headerMatcher map[string]string
187
+
188
+func (m headerMatcher) Match(r *http.Request, match *RouteMatch) bool {
189
+ return matchMap(m, r.Header, true)
190
+}
191
+
192
+// Headers adds a matcher for request header values.
193
+// It accepts a sequence of key/value pairs to be matched. For example:
194
+//
195
+// r := mux.NewRouter()
196
+// r.Headers("Content-Type", "application/json",
197
+// "X-Requested-With", "XMLHttpRequest")
198
+//
199
+// The above route will only match if both request header values match.
200
+//
201
+// It the value is an empty string, it will match any value if the key is set.
202
+func (r *Route) Headers(pairs ...string) *Route {
203
+ if r.err == nil {
204
+ var headers map[string]string
205
+ headers, r.err = mapFromPairs(pairs...)
206
+ return r.addMatcher(headerMatcher(headers))
207
+ }
208
+ return r
209
+}
210
+
211
+// Host -----------------------------------------------------------------------
212
+
213
+// Host adds a matcher for the URL host.
214
+// It accepts a template with zero or more URL variables enclosed by {}.
215
+// Variables can define an optional regexp pattern to me matched:
216
+//
217
+// - {name} matches anything until the next dot.
218
+//
219
+// - {name:pattern} matches the given regexp pattern.
220
+//
221
+// For example:
222
+//
223
+// r := mux.NewRouter()
224
+// r.Host("www.domain.com")
225
+// r.Host("{subdomain}.domain.com")
226
+// r.Host("{subdomain:[a-z]+}.domain.com")
227
+//
228
+// Variable names must be unique in a given route. They can be retrieved
229
+// calling mux.Vars(request).
230
+func (r *Route) Host(tpl string) *Route {
231
+ r.err = r.addRegexpMatcher(tpl, true, false, false)
232
+ return r
233
+}
234
+
235
+// MatcherFunc ----------------------------------------------------------------
236
+
237
+// MatcherFunc is the function signature used by custom matchers.
238
+type MatcherFunc func(*http.Request, *RouteMatch) bool
239
+
240
+func (m MatcherFunc) Match(r *http.Request, match *RouteMatch) bool {
241
+ return m(r, match)
242
+}
243
+
244
+// MatcherFunc adds a custom function to be used as request matcher.
245
+func (r *Route) MatcherFunc(f MatcherFunc) *Route {
246
+ return r.addMatcher(f)
247
+}
248
+
249
+// Methods --------------------------------------------------------------------
250
+
251
+// methodMatcher matches the request against HTTP methods.
252
+type methodMatcher []string
253
+
254
+func (m methodMatcher) Match(r *http.Request, match *RouteMatch) bool {
255
+ return matchInArray(m, r.Method)
256
+}
257
+
258
+// Methods adds a matcher for HTTP methods.
259
+// It accepts a sequence of one or more methods to be matched, e.g.:
260
+// "GET", "POST", "PUT".
261
+func (r *Route) Methods(methods ...string) *Route {
262
+ for k, v := range methods {
263
+ methods[k] = strings.ToUpper(v)
264
+ }
265
+ return r.addMatcher(methodMatcher(methods))
266
+}
267
+
268
+// Path -----------------------------------------------------------------------
269
+
270
+// Path adds a matcher for the URL path.
271
+// It accepts a template with zero or more URL variables enclosed by {}. The
272
+// template must start with a "/".
273
+// Variables can define an optional regexp pattern to me matched:
274
+//
275
+// - {name} matches anything until the next slash.
276
+//
277
+// - {name:pattern} matches the given regexp pattern.
278
+//
279
+// For example:
280
+//
281
+// r := mux.NewRouter()
282
+// r.Path("/products/").Handler(ProductsHandler)
283
+// r.Path("/products/{key}").Handler(ProductsHandler)
284
+// r.Path("/articles/{category}/{id:[0-9]+}").
285
+// Handler(ArticleHandler)
286
+//
287
+// Variable names must be unique in a given route. They can be retrieved
288
+// calling mux.Vars(request).
289
+func (r *Route) Path(tpl string) *Route {
290
+ r.err = r.addRegexpMatcher(tpl, false, false, false)
291
+ return r
292
+}
293
+
294
+// PathPrefix -----------------------------------------------------------------
295
+
296
+// PathPrefix adds a matcher for the URL path prefix. This matches if the given
297
+// template is a prefix of the full URL path. See Route.Path() for details on
298
+// the tpl argument.
299
+//
300
+// Note that it does not treat slashes specially ("/foobar/" will be matched by
301
+// the prefix "/foo") so you may want to use a trailing slash here.
302
+//
303
+// Also note that the setting of Router.StrictSlash() has no effect on routes
304
+// with a PathPrefix matcher.
305
+func (r *Route) PathPrefix(tpl string) *Route {
306
+ r.err = r.addRegexpMatcher(tpl, false, true, false)
307
+ return r
308
+}
309
+
310
+// Query ----------------------------------------------------------------------
311
+
312
+// Queries adds a matcher for URL query values.
313
+// It accepts a sequence of key/value pairs. Values may define variables.
314
+// For example:
315
+//
316
+// r := mux.NewRouter()
317
+// r.Queries("foo", "bar", "id", "{id:[0-9]+}")
318
+//
319
+// The above route will only match if the URL contains the defined queries
320
+// values, e.g.: ?foo=bar&id=42.
321
+//
322
+// It the value is an empty string, it will match any value if the key is set.
323
+//
324
+// Variables can define an optional regexp pattern to me matched:
325
+//
326
+// - {name} matches anything until the next slash.
327
+//
328
+// - {name:pattern} matches the given regexp pattern.
329
+func (r *Route) Queries(pairs ...string) *Route {
330
+ length := len(pairs)
331
+ if length%2 != 0 {
332
+ r.err = fmt.Errorf(
333
+ "mux: number of parameters must be multiple of 2, got %v", pairs)
334
+ return nil
335
+ }
336
+ for i := 0; i < length; i += 2 {
337
+ if r.err = r.addRegexpMatcher(pairs[i]+"="+pairs[i+1], false, true, true); r.err != nil {
338
+ return r
339
+ }
340
+ }
341
+
342
+ return r
343
+}
344
+
345
+// Schemes --------------------------------------------------------------------
346
+
347
+// schemeMatcher matches the request against URL schemes.
348
+type schemeMatcher []string
349
+
350
+func (m schemeMatcher) Match(r *http.Request, match *RouteMatch) bool {
351
+ return matchInArray(m, r.URL.Scheme)
352
+}
353
+
354
+// Schemes adds a matcher for URL schemes.
355
+// It accepts a sequence of schemes to be matched, e.g.: "http", "https".
356
+func (r *Route) Schemes(schemes ...string) *Route {
357
+ for k, v := range schemes {
358
+ schemes[k] = strings.ToLower(v)
359
+ }
360
+ return r.addMatcher(schemeMatcher(schemes))
361
+}
362
+
363
+// Subrouter ------------------------------------------------------------------
364
+
365
+// Subrouter creates a subrouter for the route.
366
+//
367
+// It will test the inner routes only if the parent route matched. For example:
368
+//
369
+// r := mux.NewRouter()
370
+// s := r.Host("www.domain.com").Subrouter()
371
+// s.HandleFunc("/products/", ProductsHandler)
372
+// s.HandleFunc("/products/{key}", ProductHandler)
373
+// s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
374
+//
375
+// Here, the routes registered in the subrouter won't be tested if the host
376
+// doesn't match.
377
+func (r *Route) Subrouter() *Router {
378
+ router := &Router{parent: r, strictSlash: r.strictSlash}
379
+ r.addMatcher(router)
380
+ return router
381
+}
382
+
383
+// ----------------------------------------------------------------------------
384
+// URL building
385
+// ----------------------------------------------------------------------------
386
+
387
+// URL builds a URL for the route.
388
+//
389
+// It accepts a sequence of key/value pairs for the route variables. For
390
+// example, given this route:
391
+//
392
+// r := mux.NewRouter()
393
+// r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
394
+// Name("article")
395
+//
396
+// ...a URL for it can be built using:
397
+//
398
+// url, err := r.Get("article").URL("category", "technology", "id", "42")
399
+//
400
+// ...which will return an url.URL with the following path:
401
+//
402
+// "/articles/technology/42"
403
+//
404
+// This also works for host variables:
405
+//
406
+// r := mux.NewRouter()
407
+// r.Host("{subdomain}.domain.com").
408
+// HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
409
+// Name("article")
410
+//
411
+// // url.String() will be "http://news.domain.com/articles/technology/42"
412
+// url, err := r.Get("article").URL("subdomain", "news",
413
+// "category", "technology",
414
+// "id", "42")
415
+//
416
+// All variables defined in the route are required, and their values must
417
+// conform to the corresponding patterns.
418
+func (r *Route) URL(pairs ...string) (*url.URL, error) {
419
+ if r.err != nil {
420
+ return nil, r.err
421
+ }
422
+ if r.regexp == nil {
423
+ return nil, errors.New("mux: route doesn't have a host or path")
424
+ }
425
+ var scheme, host, path string
426
+ var err error
427
+ if r.regexp.host != nil {
428
+ // Set a default scheme.
429
+ scheme = "http"
430
+ if host, err = r.regexp.host.url(pairs...); err != nil {
431
+ return nil, err
432
+ }
433
+ }
434
+ if r.regexp.path != nil {
435
+ if path, err = r.regexp.path.url(pairs...); err != nil {
436
+ return nil, err
437
+ }
438
+ }
439
+ return &url.URL{
440
+ Scheme: scheme,
441
+ Host: host,
442
+ Path: path,
443
+ }, nil
444
+}
445
+
446
+// URLHost builds the host part of the URL for a route. See Route.URL().
447
+//
448
+// The route must have a host defined.
449
+func (r *Route) URLHost(pairs ...string) (*url.URL, error) {
450
+ if r.err != nil {
451
+ return nil, r.err
452
+ }
453
+ if r.regexp == nil || r.regexp.host == nil {
454
+ return nil, errors.New("mux: route doesn't have a host")
455
+ }
456
+ host, err := r.regexp.host.url(pairs...)
457
+ if err != nil {
458
+ return nil, err
459
+ }
460
+ return &url.URL{
461
+ Scheme: "http",
462
+ Host: host,
463
+ }, nil
464
+}
465
+
466
+// URLPath builds the path part of the URL for a route. See Route.URL().
467
+//
468
+// The route must have a path defined.
469
+func (r *Route) URLPath(pairs ...string) (*url.URL, error) {
470
+ if r.err != nil {
471
+ return nil, r.err
472
+ }
473
+ if r.regexp == nil || r.regexp.path == nil {
474
+ return nil, errors.New("mux: route doesn't have a path")
475
+ }
476
+ path, err := r.regexp.path.url(pairs...)
477
+ if err != nil {
478
+ return nil, err
479
+ }
480
+ return &url.URL{
481
+ Path: path,
482
+ }, nil
483
+}
484
+
485
+// ----------------------------------------------------------------------------
486
+// parentRoute
487
+// ----------------------------------------------------------------------------
488
+
489
+// parentRoute allows routes to know about parent host and path definitions.
490
+type parentRoute interface {
491
+ getNamedRoutes() map[string]*Route
492
+ getRegexpGroup() *routeRegexpGroup
493
+}
494
+
495
+// getNamedRoutes returns the map where named routes are registered.
496
+func (r *Route) getNamedRoutes() map[string]*Route {
497
+ if r.parent == nil {
498
+ // During tests router is not always set.
499
+ r.parent = NewRouter()
500
+ }
501
+ return r.parent.getNamedRoutes()
502
+}
503
+
504
+// getRegexpGroup returns regexp definitions from this route.
505
+func (r *Route) getRegexpGroup() *routeRegexpGroup {
506
+ if r.regexp == nil {
507
+ if r.parent == nil {
508
+ // During tests router is not always set.
509
+ r.parent = NewRouter()
510
+ }
511
+ regexp := r.parent.getRegexpGroup()
512
+ if regexp == nil {
513
+ r.regexp = new(routeRegexpGroup)
514
+ } else {
515
+ // Copy.
516
+ r.regexp = &routeRegexpGroup{
517
+ host: regexp.host,
518
+ path: regexp.path,
519
+ queries: regexp.queries,
520
+ }
521
+ }
522
+ }
523
+ return r.regexp
524
+}
server/http/http.go
+1
-1
@@ -4,7 +4,7 @@ import (
4
"fmt"
5
"net/http"
6
7
- "github.com/gorilla/mux"
7
+ "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gorilla/mux"
8
mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash"
9
core "github.com/jbenet/go-ipfs/core"
10
)