@@ -15,14 +15,15 @@ import (
15
15
"regexp"
16
16
"strings"
17
17
18
- "github.com/robfig /pathtree"
18
+ "github.com/revel /pathtree"
19
19
)
20
20
21
21
const (
22
22
httpStatusCode = "404"
23
23
)
24
24
25
25
type Route struct {
26
+ ModuleSource * Module // Module name of route
26
27
Method string // e.g. GET
27
28
Path string // e.g. /app/:id
28
29
Action string // e.g. "Application.ShowApp", "404"
@@ -89,12 +90,13 @@ func treePath(method, path string) string {
89
90
type Router struct {
90
91
Routes []* Route
91
92
Tree * pathtree.Node
93
+ Module string // The module the route is associated with
92
94
path string // path to the routes file
93
95
}
94
96
95
97
var notFound = & RouteMatch {Action : "404" }
96
98
97
- func (router * Router ) Route (req * http.Request ) * RouteMatch {
99
+ func (router * Router ) Route (req * http.Request ) ( routeMatch * RouteMatch ) {
98
100
// Override method if set in header
99
101
if method := req .Header .Get ("X-HTTP-Method-Override" ); method != "" && req .Method == "POST" {
100
102
req .Method = method
@@ -104,7 +106,8 @@ func (router *Router) Route(req *http.Request) *RouteMatch {
104
106
if leaf == nil {
105
107
return nil
106
108
}
107
- route := leaf .Value .(* Route )
109
+ routeList := leaf .Value .([]* Route )
110
+ INFO .Printf ("Found route %s %#v" ,req .URL .Path ,routeList )
108
111
109
112
// Create a map of the route parameters.
110
113
var params url.Values
@@ -114,27 +117,51 @@ func (router *Router) Route(req *http.Request) *RouteMatch {
114
117
params [leaf.Wildcards [i ]] = []string {v }
115
118
}
116
119
}
120
+ var route * Route
121
+ var controllerName string
122
+ for index := range routeList {
123
+ route = routeList [index ]
124
+ // Special handling for explicit 404's.
125
+ if route .Action == httpStatusCode {
126
+ route = nil
127
+ break
128
+ }
117
129
118
- // Special handling for explicit 404's .
119
- if route . Action == httpStatusCode {
120
- return notFound
121
- }
130
+ // If the action is variablized, replace into it with the captured args .
131
+ controllerName = route . ControllerName
132
+ if controllerName [ 0 ] == ':' {
133
+ controllerName = params [ controllerName [ 1 :]][ 0 ]
122
134
123
- // If the action is variablized, replace into it with the captured args.
124
- controllerName , methodName := route .ControllerName , route .MethodName
125
- if controllerName [0 ] == ':' {
126
- controllerName = params [controllerName [1 :]][0 ]
127
- }
128
- if methodName [0 ] == ':' {
129
- methodName = params [methodName [1 :]][0 ]
135
+ // Check that the controller comes from a matching route
136
+ if controller , found := controllers [strings .ToLower (controllerName )]; found {
137
+
138
+ if route .ModuleSource == controller .ModuleSource {
139
+ break
140
+ }
141
+ }
142
+ } else {
143
+ break
144
+ }
145
+ route = nil
130
146
}
131
147
132
- return & RouteMatch {
133
- ControllerName : controllerName ,
134
- MethodName : methodName ,
135
- Params : params ,
136
- FixedParams : route .FixedParams ,
148
+ if route == nil {
149
+ routeMatch = notFound
150
+ } else {
151
+ methodName := route .MethodName
152
+ if methodName [0 ] == ':' {
153
+ methodName = params [methodName [1 :]][0 ]
154
+ }
155
+
156
+ routeMatch = & RouteMatch {
157
+ ControllerName : controllerName ,
158
+ MethodName : methodName ,
159
+ Params : params ,
160
+ FixedParams : route .FixedParams ,
161
+ }
137
162
}
163
+
164
+ return
138
165
}
139
166
140
167
// Refresh re-reads the routes file and re-calculates the routing table.
@@ -150,17 +177,32 @@ func (router *Router) Refresh() (err *Error) {
150
177
151
178
func (router * Router ) updateTree () * Error {
152
179
router .Tree = pathtree .New ()
180
+ pathMap := map [string ][]* Route {}
181
+
182
+ allPathsOrdered := []string {}
183
+ // It is possible for some route paths to overlap
184
+ // based on wildcard matches,
185
+ // TODO when pathtree is fixed (made to be smart enough to not require a predefined intake order) keeping the routes in order is not necessary
153
186
for _ , route := range router .Routes {
154
- err := router .Tree .Add (route .TreePath , route )
187
+ if _ ,found := pathMap [route .TreePath ];! found {
188
+ pathMap [route .TreePath ] = append (pathMap [route .TreePath ], route )
189
+ allPathsOrdered = append (allPathsOrdered , route .TreePath )
190
+ } else {
191
+ pathMap [route .TreePath ] = append (pathMap [route .TreePath ], route )
192
+ }
193
+ }
194
+ for _ ,path := range allPathsOrdered {
195
+ routeList := pathMap [path ]
196
+ err := router .Tree .Add (path , routeList )
155
197
156
198
// Allow GETs to respond to HEAD requests.
157
- if err == nil && route .Method == "GET" {
158
- err = router .Tree .Add (treePath ("HEAD" , route .Path ), route )
199
+ if err == nil && routeList [ 0 ] .Method == "GET" {
200
+ err = router .Tree .Add (treePath ("HEAD" , routeList [ 0 ] .Path ), routeList )
159
201
}
160
202
161
203
// Error adding a route to the pathtree.
162
204
if err != nil {
163
- return routeError (err , route . routesPath , "" , route .line )
205
+ return routeError (err , path , fmt . Sprintf ( "%#v" , routeList ), routeList [ 0 ] .line )
164
206
}
165
207
}
166
208
return nil
@@ -291,15 +333,22 @@ func routeError(err error, routesPath, content string, n int) *Error {
291
333
292
334
// getModuleRoutes loads the routes file for the given module and returns the
293
335
// list of routes.
294
- func getModuleRoutes (moduleName , joinedPath string , validate bool ) ([]* Route , * Error ) {
336
+ func getModuleRoutes (moduleName , joinedPath string , validate bool ) (routes []* Route , err * Error ) {
295
337
// Look up the module. It may be not found due to the common case of e.g. the
296
338
// testrunner module being active only in dev mode.
297
339
module , found := ModuleByName (moduleName )
298
340
if ! found {
299
341
INFO .Println ("Skipping routes for inactive module" , moduleName )
300
342
return nil , nil
301
343
}
302
- return parseRoutesFile (filepath .Join (module .Path , "conf" , "routes" ), joinedPath , validate )
344
+ routes , err = parseRoutesFile (filepath .Join (module .Path , "conf" , "routes" ), joinedPath , validate )
345
+ if err == nil {
346
+ for _ , route := range routes {
347
+ route .ModuleSource = module
348
+ }
349
+ }
350
+
351
+ return routes ,err
303
352
}
304
353
305
354
// Groups:
@@ -368,6 +417,20 @@ func (router *Router) Reverse(action string, argValues map[string]string) *Actio
368
417
argValues [route .MethodName [1 :]] = methodName
369
418
}
370
419
420
+ // Constraint match - wildcard matches can only be made by the same module that created them
421
+ if controllerWildcard || methodWildcard {
422
+ if controller , found := controllers [strings .ToLower (controllerName )]; found {
423
+ // Wildcard match boundary
424
+ if controller .ModuleSource != route .ModuleSource {
425
+ continue
426
+ }
427
+ // See if the path exists in the module based
428
+ } else {
429
+ ERROR .Printf ("Controller %s not found in reverse lookup" , controllerName )
430
+ continue
431
+ }
432
+ }
433
+
371
434
// Build up the URL.
372
435
var (
373
436
queryValues = make (url.Values )
0 commit comments