@cryptotaxi247 / kubo / commits / cf0100c97

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 +[![Build Status](https://travis-ci.org/gorilla/context.png?branch=master)](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 +[![Build Status](https://travis-ci.org/gorilla/mux.png?branch=master)](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 )