Browse code

Bump gorilla/mux to 1.7.0

This release drops support for Go < 1.7, and removes the gorilla/context
dependency (which was needed for older Go versions).

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>

Sebastiaan van Stijn authored on 2019/02/10 00:23:55
Showing 13 changed files
... ...
@@ -5,8 +5,7 @@ github.com/Microsoft/go-winio v0.4.11
5 5
 github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a
6 6
 github.com/go-check/check 4ed411733c5785b40214c70bce814c3a3a689609 https://github.com/cpuguy83/check.git
7 7
 github.com/golang/gddo 9b12a26f3fbd7397dee4e20939ddca719d840d2a
8
-github.com/gorilla/context v1.1
9
-github.com/gorilla/mux v1.6.2
8
+github.com/gorilla/mux v1.7.0
10 9
 github.com/Microsoft/opengcs v0.3.9
11 10
 github.com/kr/pty 5cf931ef8f
12 11
 github.com/mattn/go-shellwords v1.0.3
13 12
deleted file mode 100644
... ...
@@ -1,27 +0,0 @@
1
-Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
2
-
3
-Redistribution and use in source and binary forms, with or without
4
-modification, are permitted provided that the following conditions are
5
-met:
6
-
7
-	 * Redistributions of source code must retain the above copyright
8
-notice, this list of conditions and the following disclaimer.
9
-	 * Redistributions in binary form must reproduce the above
10
-copyright notice, this list of conditions and the following disclaimer
11
-in the documentation and/or other materials provided with the
12
-distribution.
13
-	 * Neither the name of Google Inc. nor the names of its
14
-contributors may be used to endorse or promote products derived from
15
-this software without specific prior written permission.
16
-
17
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 1
deleted file mode 100644
... ...
@@ -1,7 +0,0 @@
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
8 1
deleted file mode 100644
... ...
@@ -1,143 +0,0 @@
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
-}
144 1
deleted file mode 100644
... ...
@@ -1,82 +0,0 @@
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
... ...
@@ -1,4 +1,4 @@
1
-Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
1
+Copyright (c) 2012-2018 The Gorilla Authors. All rights reserved.
2 2
 
3 3
 Redistribution and use in source and binary forms, with or without
4 4
 modification, are permitted provided that the following conditions are
... ...
@@ -6,7 +6,7 @@
6 6
 
7 7
 ![Gorilla Logo](http://www.gorillatoolkit.org/static/images/gorilla-icon-64.png)
8 8
 
9
-http://www.gorillatoolkit.org/pkg/mux
9
+https://www.gorillatoolkit.org/pkg/mux
10 10
 
11 11
 Package `gorilla/mux` implements a request router and dispatcher for matching incoming requests to
12 12
 their respective handler.
... ...
@@ -88,7 +88,7 @@ r := mux.NewRouter()
88 88
 // Only matches if domain is "www.example.com".
89 89
 r.Host("www.example.com")
90 90
 // Matches a dynamic subdomain.
91
-r.Host("{subdomain:[a-z]+}.domain.com")
91
+r.Host("{subdomain:[a-z]+}.example.com")
92 92
 ```
93 93
 
94 94
 There are several other matchers that can be added. To match path prefixes:
... ...
@@ -238,13 +238,13 @@ This also works for host and query value variables:
238 238
 
239 239
 ```go
240 240
 r := mux.NewRouter()
241
-r.Host("{subdomain}.domain.com").
241
+r.Host("{subdomain}.example.com").
242 242
   Path("/articles/{category}/{id:[0-9]+}").
243 243
   Queries("filter", "{filter}").
244 244
   HandlerFunc(ArticleHandler).
245 245
   Name("article")
246 246
 
247
-// url.String() will be "http://news.domain.com/articles/technology/42?filter=gorilla"
247
+// url.String() will be "http://news.example.com/articles/technology/42?filter=gorilla"
248 248
 url, err := r.Get("article").URL("subdomain", "news",
249 249
                                  "category", "technology",
250 250
                                  "id", "42",
... ...
@@ -264,7 +264,7 @@ r.HeadersRegexp("Content-Type", "application/(text|json)")
264 264
 There's also a way to build only the URL host or path for a route: use the methods `URLHost()` or `URLPath()` instead. For the previous route, we would do:
265 265
 
266 266
 ```go
267
-// "http://news.domain.com/"
267
+// "http://news.example.com/"
268 268
 host, err := r.Get("article").URLHost("subdomain", "news")
269 269
 
270 270
 // "/articles/technology/42"
... ...
@@ -275,12 +275,12 @@ And if you use subrouters, host and path defined separately can be built as well
275 275
 
276 276
 ```go
277 277
 r := mux.NewRouter()
278
-s := r.Host("{subdomain}.domain.com").Subrouter()
278
+s := r.Host("{subdomain}.example.com").Subrouter()
279 279
 s.Path("/articles/{category}/{id:[0-9]+}").
280 280
   HandlerFunc(ArticleHandler).
281 281
   Name("article")
282 282
 
283
-// "http://news.domain.com/articles/technology/42"
283
+// "http://news.example.com/articles/technology/42"
284 284
 url, err := r.Get("article").URL("subdomain", "news",
285 285
                                  "category", "technology",
286 286
                                  "id", "42")
... ...
@@ -503,8 +503,8 @@ package main
503 503
 
504 504
 func HealthCheckHandler(w http.ResponseWriter, r *http.Request) {
505 505
     // A very simple health check.
506
-    w.WriteHeader(http.StatusOK)
507 506
     w.Header().Set("Content-Type", "application/json")
507
+    w.WriteHeader(http.StatusOK)
508 508
 
509 509
     // In the future we could report back on the status of our DB, or our cache
510 510
     // (e.g. Redis) by performing a simple PING, and include them in the response.
511 511
new file mode 100644
... ...
@@ -0,0 +1,18 @@
0
+package mux
1
+
2
+import (
3
+	"context"
4
+	"net/http"
5
+)
6
+
7
+func contextGet(r *http.Request, key interface{}) interface{} {
8
+	return r.Context().Value(key)
9
+}
10
+
11
+func contextSet(r *http.Request, key, val interface{}) *http.Request {
12
+	if val == nil {
13
+		return r
14
+	}
15
+
16
+	return r.WithContext(context.WithValue(r.Context(), key, val))
17
+}
0 18
deleted file mode 100644
... ...
@@ -1,26 +0,0 @@
1
-// +build !go1.7
2
-
3
-package mux
4
-
5
-import (
6
-	"net/http"
7
-
8
-	"github.com/gorilla/context"
9
-)
10
-
11
-func contextGet(r *http.Request, key interface{}) interface{} {
12
-	return context.Get(r, key)
13
-}
14
-
15
-func contextSet(r *http.Request, key, val interface{}) *http.Request {
16
-	if val == nil {
17
-		return r
18
-	}
19
-
20
-	context.Set(r, key, val)
21
-	return r
22
-}
23
-
24
-func contextClear(r *http.Request) {
25
-	context.Clear(r)
26
-}
27 1
deleted file mode 100644
... ...
@@ -1,24 +0,0 @@
1
-// +build go1.7
2
-
3
-package mux
4
-
5
-import (
6
-	"context"
7
-	"net/http"
8
-)
9
-
10
-func contextGet(r *http.Request, key interface{}) interface{} {
11
-	return r.Context().Value(key)
12
-}
13
-
14
-func contextSet(r *http.Request, key, val interface{}) *http.Request {
15
-	if val == nil {
16
-		return r
17
-	}
18
-
19
-	return r.WithContext(context.WithValue(r.Context(), key, val))
20
-}
21
-
22
-func contextClear(r *http.Request) {
23
-	return
24
-}
... ...
@@ -22,7 +22,7 @@ var (
22 22
 
23 23
 // NewRouter returns a new router instance.
24 24
 func NewRouter() *Router {
25
-	return &Router{namedRoutes: make(map[string]*Route), KeepContext: false}
25
+	return &Router{namedRoutes: make(map[string]*Route)}
26 26
 }
27 27
 
28 28
 // Router registers routes to be matched and dispatches a handler.
... ...
@@ -50,24 +50,78 @@ type Router struct {
50 50
 	// Configurable Handler to be used when the request method does not match the route.
51 51
 	MethodNotAllowedHandler http.Handler
52 52
 
53
-	// Parent route, if this is a subrouter.
54
-	parent parentRoute
55 53
 	// Routes to be matched, in order.
56 54
 	routes []*Route
55
+
57 56
 	// Routes by name for URL building.
58 57
 	namedRoutes map[string]*Route
59
-	// See Router.StrictSlash(). This defines the flag for new routes.
60
-	strictSlash bool
61
-	// See Router.SkipClean(). This defines the flag for new routes.
62
-	skipClean bool
58
+
63 59
 	// If true, do not clear the request context after handling the request.
64
-	// This has no effect when go1.7+ is used, since the context is stored
60
+	//
61
+	// Deprecated: No effect when go1.7+ is used, since the context is stored
65 62
 	// on the request itself.
66 63
 	KeepContext bool
67
-	// see Router.UseEncodedPath(). This defines a flag for all routes.
68
-	useEncodedPath bool
64
+
69 65
 	// Slice of middlewares to be called after a match is found
70 66
 	middlewares []middleware
67
+
68
+	// configuration shared with `Route`
69
+	routeConf
70
+}
71
+
72
+// common route configuration shared between `Router` and `Route`
73
+type routeConf struct {
74
+	// If true, "/path/foo%2Fbar/to" will match the path "/path/{var}/to"
75
+	useEncodedPath bool
76
+
77
+	// If true, when the path pattern is "/path/", accessing "/path" will
78
+	// redirect to the former and vice versa.
79
+	strictSlash bool
80
+
81
+	// If true, when the path pattern is "/path//to", accessing "/path//to"
82
+	// will not redirect
83
+	skipClean bool
84
+
85
+	// Manager for the variables from host and path.
86
+	regexp routeRegexpGroup
87
+
88
+	// List of matchers.
89
+	matchers []matcher
90
+
91
+	// The scheme used when building URLs.
92
+	buildScheme string
93
+
94
+	buildVarsFunc BuildVarsFunc
95
+}
96
+
97
+// returns an effective deep copy of `routeConf`
98
+func copyRouteConf(r routeConf) routeConf {
99
+	c := r
100
+
101
+	if r.regexp.path != nil {
102
+		c.regexp.path = copyRouteRegexp(r.regexp.path)
103
+	}
104
+
105
+	if r.regexp.host != nil {
106
+		c.regexp.host = copyRouteRegexp(r.regexp.host)
107
+	}
108
+
109
+	c.regexp.queries = make([]*routeRegexp, 0, len(r.regexp.queries))
110
+	for _, q := range r.regexp.queries {
111
+		c.regexp.queries = append(c.regexp.queries, copyRouteRegexp(q))
112
+	}
113
+
114
+	c.matchers = make([]matcher, 0, len(r.matchers))
115
+	for _, m := range r.matchers {
116
+		c.matchers = append(c.matchers, m)
117
+	}
118
+
119
+	return c
120
+}
121
+
122
+func copyRouteRegexp(r *routeRegexp) *routeRegexp {
123
+	c := *r
124
+	return &c
71 125
 }
72 126
 
73 127
 // Match attempts to match the given request against the router's registered routes.
... ...
@@ -155,22 +209,18 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
155 155
 		handler = http.NotFoundHandler()
156 156
 	}
157 157
 
158
-	if !r.KeepContext {
159
-		defer contextClear(req)
160
-	}
161
-
162 158
 	handler.ServeHTTP(w, req)
163 159
 }
164 160
 
165 161
 // Get returns a route registered with the given name.
166 162
 func (r *Router) Get(name string) *Route {
167
-	return r.getNamedRoutes()[name]
163
+	return r.namedRoutes[name]
168 164
 }
169 165
 
170 166
 // GetRoute returns a route registered with the given name. This method
171 167
 // was renamed to Get() and remains here for backwards compatibility.
172 168
 func (r *Router) GetRoute(name string) *Route {
173
-	return r.getNamedRoutes()[name]
169
+	return r.namedRoutes[name]
174 170
 }
175 171
 
176 172
 // StrictSlash defines the trailing slash behavior for new routes. The initial
... ...
@@ -222,50 +272,13 @@ func (r *Router) UseEncodedPath() *Router {
222 222
 }
223 223
 
224 224
 // ----------------------------------------------------------------------------
225
-// parentRoute
226
-// ----------------------------------------------------------------------------
227
-
228
-func (r *Router) getBuildScheme() string {
229
-	if r.parent != nil {
230
-		return r.parent.getBuildScheme()
231
-	}
232
-	return ""
233
-}
234
-
235
-// getNamedRoutes returns the map where named routes are registered.
236
-func (r *Router) getNamedRoutes() map[string]*Route {
237
-	if r.namedRoutes == nil {
238
-		if r.parent != nil {
239
-			r.namedRoutes = r.parent.getNamedRoutes()
240
-		} else {
241
-			r.namedRoutes = make(map[string]*Route)
242
-		}
243
-	}
244
-	return r.namedRoutes
245
-}
246
-
247
-// getRegexpGroup returns regexp definitions from the parent route, if any.
248
-func (r *Router) getRegexpGroup() *routeRegexpGroup {
249
-	if r.parent != nil {
250
-		return r.parent.getRegexpGroup()
251
-	}
252
-	return nil
253
-}
254
-
255
-func (r *Router) buildVars(m map[string]string) map[string]string {
256
-	if r.parent != nil {
257
-		m = r.parent.buildVars(m)
258
-	}
259
-	return m
260
-}
261
-
262
-// ----------------------------------------------------------------------------
263 225
 // Route factories
264 226
 // ----------------------------------------------------------------------------
265 227
 
266 228
 // NewRoute registers an empty route.
267 229
 func (r *Router) NewRoute() *Route {
268
-	route := &Route{parent: r, strictSlash: r.strictSlash, skipClean: r.skipClean, useEncodedPath: r.useEncodedPath}
230
+	// initialize a route with a copy of the parent router's configuration
231
+	route := &Route{routeConf: copyRouteConf(r.routeConf), namedRoutes: r.namedRoutes}
269 232
 	r.routes = append(r.routes, route)
270 233
 	return route
271 234
 }
... ...
@@ -267,7 +267,7 @@ type routeRegexpGroup struct {
267 267
 }
268 268
 
269 269
 // setMatch extracts the variables from the URL once a route matches.
270
-func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route) {
270
+func (v routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route) {
271 271
 	// Store host variables.
272 272
 	if v.host != nil {
273 273
 		host := getHost(req)
... ...
@@ -296,7 +296,7 @@ func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route)
296 296
 					} else {
297 297
 						u.Path += "/"
298 298
 					}
299
-					m.Handler = http.RedirectHandler(u.String(), 301)
299
+					m.Handler = http.RedirectHandler(u.String(), http.StatusMovedPermanently)
300 300
 				}
301 301
 			}
302 302
 		}
... ...
@@ -312,17 +312,13 @@ func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route)
312 312
 }
313 313
 
314 314
 // getHost tries its best to return the request host.
315
+// According to section 14.23 of RFC 2616 the Host header
316
+// can include the port number if the default value of 80 is not used.
315 317
 func getHost(r *http.Request) string {
316 318
 	if r.URL.IsAbs() {
317 319
 		return r.URL.Host
318 320
 	}
319
-	host := r.Host
320
-	// Slice off any port information.
321
-	if i := strings.Index(host, ":"); i != -1 {
322
-		host = host[:i]
323
-	}
324
-	return host
325
-
321
+	return r.Host
326 322
 }
327 323
 
328 324
 func extractVars(input string, matches []int, names []string, output map[string]string) {
... ...
@@ -15,24 +15,8 @@ import (
15 15
 
16 16
 // Route stores information to match a request and build URLs.
17 17
 type Route struct {
18
-	// Parent where the route was registered (a Router).
19
-	parent parentRoute
20 18
 	// Request handler for the route.
21 19
 	handler http.Handler
22
-	// List of matchers.
23
-	matchers []matcher
24
-	// Manager for the variables from host and path.
25
-	regexp *routeRegexpGroup
26
-	// If true, when the path pattern is "/path/", accessing "/path" will
27
-	// redirect to the former and vice versa.
28
-	strictSlash bool
29
-	// If true, when the path pattern is "/path//to", accessing "/path//to"
30
-	// will not redirect
31
-	skipClean bool
32
-	// If true, "/path/foo%2Fbar/to" will match the path "/path/{var}/to"
33
-	useEncodedPath bool
34
-	// The scheme used when building URLs.
35
-	buildScheme string
36 20
 	// If true, this route never matches: it is only used to build URLs.
37 21
 	buildOnly bool
38 22
 	// The name used to build URLs.
... ...
@@ -40,7 +24,11 @@ type Route struct {
40 40
 	// Error resulted from building a route.
41 41
 	err error
42 42
 
43
-	buildVarsFunc BuildVarsFunc
43
+	// "global" reference to all named routes
44
+	namedRoutes map[string]*Route
45
+
46
+	// config possibly passed in from `Router`
47
+	routeConf
44 48
 }
45 49
 
46 50
 // SkipClean reports whether path cleaning is enabled for this route via
... ...
@@ -64,6 +52,18 @@ func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
64 64
 				matchErr = ErrMethodMismatch
65 65
 				continue
66 66
 			}
67
+
68
+			// Ignore ErrNotFound errors. These errors arise from match call
69
+			// to Subrouters.
70
+			//
71
+			// This prevents subsequent matching subrouters from failing to
72
+			// run middleware. If not ignored, the middleware would see a
73
+			// non-nil MatchErr and be skipped, even when there was a
74
+			// matching route.
75
+			if match.MatchErr == ErrNotFound {
76
+				match.MatchErr = nil
77
+			}
78
+
67 79
 			matchErr = nil
68 80
 			return false
69 81
 		}
... ...
@@ -93,9 +93,7 @@ func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
93 93
 	}
94 94
 
95 95
 	// Set variables.
96
-	if r.regexp != nil {
97
-		r.regexp.setMatch(req, match, r)
98
-	}
96
+	r.regexp.setMatch(req, match, r)
99 97
 	return true
100 98
 }
101 99
 
... ...
@@ -137,7 +135,7 @@ func (r *Route) GetHandler() http.Handler {
137 137
 // Name -----------------------------------------------------------------------
138 138
 
139 139
 // Name sets the name for the route, used to build URLs.
140
-// If the name was registered already it will be overwritten.
140
+// It is an error to call Name more than once on a route.
141 141
 func (r *Route) Name(name string) *Route {
142 142
 	if r.name != "" {
143 143
 		r.err = fmt.Errorf("mux: route already has name %q, can't set %q",
... ...
@@ -145,7 +143,7 @@ func (r *Route) Name(name string) *Route {
145 145
 	}
146 146
 	if r.err == nil {
147 147
 		r.name = name
148
-		r.getNamedRoutes()[name] = r
148
+		r.namedRoutes[name] = r
149 149
 	}
150 150
 	return r
151 151
 }
... ...
@@ -177,7 +175,6 @@ func (r *Route) addRegexpMatcher(tpl string, typ regexpType) error {
177 177
 	if r.err != nil {
178 178
 		return r.err
179 179
 	}
180
-	r.regexp = r.getRegexpGroup()
181 180
 	if typ == regexpTypePath || typ == regexpTypePrefix {
182 181
 		if len(tpl) > 0 && tpl[0] != '/' {
183 182
 			return fmt.Errorf("mux: path must start with a slash, got %q", tpl)
... ...
@@ -424,7 +421,7 @@ func (r *Route) Schemes(schemes ...string) *Route {
424 424
 	for k, v := range schemes {
425 425
 		schemes[k] = strings.ToLower(v)
426 426
 	}
427
-	if r.buildScheme == "" && len(schemes) > 0 {
427
+	if len(schemes) > 0 {
428 428
 		r.buildScheme = schemes[0]
429 429
 	}
430 430
 	return r.addMatcher(schemeMatcher(schemes))
... ...
@@ -439,7 +436,15 @@ type BuildVarsFunc func(map[string]string) map[string]string
439 439
 // BuildVarsFunc adds a custom function to be used to modify build variables
440 440
 // before a route's URL is built.
441 441
 func (r *Route) BuildVarsFunc(f BuildVarsFunc) *Route {
442
-	r.buildVarsFunc = f
442
+	if r.buildVarsFunc != nil {
443
+		// compose the old and new functions
444
+		old := r.buildVarsFunc
445
+		r.buildVarsFunc = func(m map[string]string) map[string]string {
446
+			return f(old(m))
447
+		}
448
+	} else {
449
+		r.buildVarsFunc = f
450
+	}
443 451
 	return r
444 452
 }
445 453
 
... ...
@@ -458,7 +463,8 @@ func (r *Route) BuildVarsFunc(f BuildVarsFunc) *Route {
458 458
 // Here, the routes registered in the subrouter won't be tested if the host
459 459
 // doesn't match.
460 460
 func (r *Route) Subrouter() *Router {
461
-	router := &Router{parent: r, strictSlash: r.strictSlash}
461
+	// initialize a subrouter with a copy of the parent route's configuration
462
+	router := &Router{routeConf: copyRouteConf(r.routeConf), namedRoutes: r.namedRoutes}
462 463
 	r.addMatcher(router)
463 464
 	return router
464 465
 }
... ...
@@ -502,9 +508,6 @@ func (r *Route) URL(pairs ...string) (*url.URL, error) {
502 502
 	if r.err != nil {
503 503
 		return nil, r.err
504 504
 	}
505
-	if r.regexp == nil {
506
-		return nil, errors.New("mux: route doesn't have a host or path")
507
-	}
508 505
 	values, err := r.prepareVars(pairs...)
509 506
 	if err != nil {
510 507
 		return nil, err
... ...
@@ -516,8 +519,8 @@ func (r *Route) URL(pairs ...string) (*url.URL, error) {
516 516
 			return nil, err
517 517
 		}
518 518
 		scheme = "http"
519
-		if s := r.getBuildScheme(); s != "" {
520
-			scheme = s
519
+		if r.buildScheme != "" {
520
+			scheme = r.buildScheme
521 521
 		}
522 522
 	}
523 523
 	if r.regexp.path != nil {
... ...
@@ -547,7 +550,7 @@ func (r *Route) URLHost(pairs ...string) (*url.URL, error) {
547 547
 	if r.err != nil {
548 548
 		return nil, r.err
549 549
 	}
550
-	if r.regexp == nil || r.regexp.host == nil {
550
+	if r.regexp.host == nil {
551 551
 		return nil, errors.New("mux: route doesn't have a host")
552 552
 	}
553 553
 	values, err := r.prepareVars(pairs...)
... ...
@@ -562,8 +565,8 @@ func (r *Route) URLHost(pairs ...string) (*url.URL, error) {
562 562
 		Scheme: "http",
563 563
 		Host:   host,
564 564
 	}
565
-	if s := r.getBuildScheme(); s != "" {
566
-		u.Scheme = s
565
+	if r.buildScheme != "" {
566
+		u.Scheme = r.buildScheme
567 567
 	}
568 568
 	return u, nil
569 569
 }
... ...
@@ -575,7 +578,7 @@ func (r *Route) URLPath(pairs ...string) (*url.URL, error) {
575 575
 	if r.err != nil {
576 576
 		return nil, r.err
577 577
 	}
578
-	if r.regexp == nil || r.regexp.path == nil {
578
+	if r.regexp.path == nil {
579 579
 		return nil, errors.New("mux: route doesn't have a path")
580 580
 	}
581 581
 	values, err := r.prepareVars(pairs...)
... ...
@@ -600,7 +603,7 @@ func (r *Route) GetPathTemplate() (string, error) {
600 600
 	if r.err != nil {
601 601
 		return "", r.err
602 602
 	}
603
-	if r.regexp == nil || r.regexp.path == nil {
603
+	if r.regexp.path == nil {
604 604
 		return "", errors.New("mux: route doesn't have a path")
605 605
 	}
606 606
 	return r.regexp.path.template, nil
... ...
@@ -614,7 +617,7 @@ func (r *Route) GetPathRegexp() (string, error) {
614 614
 	if r.err != nil {
615 615
 		return "", r.err
616 616
 	}
617
-	if r.regexp == nil || r.regexp.path == nil {
617
+	if r.regexp.path == nil {
618 618
 		return "", errors.New("mux: route does not have a path")
619 619
 	}
620 620
 	return r.regexp.path.regexp.String(), nil
... ...
@@ -629,7 +632,7 @@ func (r *Route) GetQueriesRegexp() ([]string, error) {
629 629
 	if r.err != nil {
630 630
 		return nil, r.err
631 631
 	}
632
-	if r.regexp == nil || r.regexp.queries == nil {
632
+	if r.regexp.queries == nil {
633 633
 		return nil, errors.New("mux: route doesn't have queries")
634 634
 	}
635 635
 	var queries []string
... ...
@@ -648,7 +651,7 @@ func (r *Route) GetQueriesTemplates() ([]string, error) {
648 648
 	if r.err != nil {
649 649
 		return nil, r.err
650 650
 	}
651
-	if r.regexp == nil || r.regexp.queries == nil {
651
+	if r.regexp.queries == nil {
652 652
 		return nil, errors.New("mux: route doesn't have queries")
653 653
 	}
654 654
 	var queries []string
... ...
@@ -683,7 +686,7 @@ func (r *Route) GetHostTemplate() (string, error) {
683 683
 	if r.err != nil {
684 684
 		return "", r.err
685 685
 	}
686
-	if r.regexp == nil || r.regexp.host == nil {
686
+	if r.regexp.host == nil {
687 687
 		return "", errors.New("mux: route doesn't have a host")
688 688
 	}
689 689
 	return r.regexp.host.template, nil
... ...
@@ -700,64 +703,8 @@ func (r *Route) prepareVars(pairs ...string) (map[string]string, error) {
700 700
 }
701 701
 
702 702
 func (r *Route) buildVars(m map[string]string) map[string]string {
703
-	if r.parent != nil {
704
-		m = r.parent.buildVars(m)
705
-	}
706 703
 	if r.buildVarsFunc != nil {
707 704
 		m = r.buildVarsFunc(m)
708 705
 	}
709 706
 	return m
710 707
 }
711
-
712
-// ----------------------------------------------------------------------------
713
-// parentRoute
714
-// ----------------------------------------------------------------------------
715
-
716
-// parentRoute allows routes to know about parent host and path definitions.
717
-type parentRoute interface {
718
-	getBuildScheme() string
719
-	getNamedRoutes() map[string]*Route
720
-	getRegexpGroup() *routeRegexpGroup
721
-	buildVars(map[string]string) map[string]string
722
-}
723
-
724
-func (r *Route) getBuildScheme() string {
725
-	if r.buildScheme != "" {
726
-		return r.buildScheme
727
-	}
728
-	if r.parent != nil {
729
-		return r.parent.getBuildScheme()
730
-	}
731
-	return ""
732
-}
733
-
734
-// getNamedRoutes returns the map where named routes are registered.
735
-func (r *Route) getNamedRoutes() map[string]*Route {
736
-	if r.parent == nil {
737
-		// During tests router is not always set.
738
-		r.parent = NewRouter()
739
-	}
740
-	return r.parent.getNamedRoutes()
741
-}
742
-
743
-// getRegexpGroup returns regexp definitions from this route.
744
-func (r *Route) getRegexpGroup() *routeRegexpGroup {
745
-	if r.regexp == nil {
746
-		if r.parent == nil {
747
-			// During tests router is not always set.
748
-			r.parent = NewRouter()
749
-		}
750
-		regexp := r.parent.getRegexpGroup()
751
-		if regexp == nil {
752
-			r.regexp = new(routeRegexpGroup)
753
-		} else {
754
-			// Copy.
755
-			r.regexp = &routeRegexpGroup{
756
-				host:    regexp.host,
757
-				path:    regexp.path,
758
-				queries: regexp.queries,
759
-			}
760
-		}
761
-	}
762
-	return r.regexp
763
-}