@@ -2112,9 +2112,8 @@ type ServeMux struct {
2112
2112
}
2113
2113
2114
2114
type muxEntry struct {
2115
- explicit bool
2116
- h Handler
2117
- pattern string
2115
+ h Handler
2116
+ pattern string
2118
2117
}
2119
2118
2120
2119
// NewServeMux allocates and returns a new ServeMux.
@@ -2192,6 +2191,31 @@ func (mux *ServeMux) match(path string) (h Handler, pattern string) {
2192
2191
return
2193
2192
}
2194
2193
2194
+ // redirectToPathSlash determines if the given path needs appending "/" to it.
2195
+ // This occurs when a handler for path + "/" was already registered, but
2196
+ // not for path itself. If the path needs appending to, it creates a new
2197
+ // URL, setting the path to u.Path + "/" and returning true to indicate so.
2198
+ func (mux * ServeMux ) redirectToPathSlash (path string , u * url.URL ) (* url.URL , bool ) {
2199
+ if ! mux .shouldRedirect (path ) {
2200
+ return u , false
2201
+ }
2202
+ path = path + "/"
2203
+ u = & url.URL {Path : path , RawQuery : u .RawQuery }
2204
+ return u , true
2205
+ }
2206
+
2207
+ // shouldRedirect reports whether the given path should be redirected to
2208
+ // path+"/". This should happen if a handler is registered for path+"/" but
2209
+ // not path -- see comments at ServeMux.
2210
+ func (mux * ServeMux ) shouldRedirect (path string ) bool {
2211
+ if _ , exist := mux .m [path ]; exist {
2212
+ return false
2213
+ }
2214
+ n := len (path )
2215
+ _ , exist := mux .m [path + "/" ]
2216
+ return n > 0 && path [n - 1 ] != '/' && exist
2217
+ }
2218
+
2195
2219
// Handler returns the handler to use for the given request,
2196
2220
// consulting r.Method, r.Host, and r.URL.Path. It always returns
2197
2221
// a non-nil handler. If the path is not in its canonical form, the
@@ -2211,13 +2235,27 @@ func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
2211
2235
2212
2236
// CONNECT requests are not canonicalized.
2213
2237
if r .Method == "CONNECT" {
2238
+ // If r.URL.Path is /tree and its handler is not registered,
2239
+ // the /tree -> /tree/ redirect applies to CONNECT requests
2240
+ // but the path canonicalization does not.
2241
+ if u , ok := mux .redirectToPathSlash (r .URL .Path , r .URL ); ok {
2242
+ return RedirectHandler (u .String (), StatusMovedPermanently ), u .Path
2243
+ }
2244
+
2214
2245
return mux .handler (r .Host , r .URL .Path )
2215
2246
}
2216
2247
2217
2248
// All other requests have any port stripped and path cleaned
2218
2249
// before passing to mux.handler.
2219
2250
host := stripHostPort (r .Host )
2220
2251
path := cleanPath (r .URL .Path )
2252
+
2253
+ // If the given path is /tree and its handler is not registered,
2254
+ // redirect for /tree/.
2255
+ if u , ok := mux .redirectToPathSlash (path , r .URL ); ok {
2256
+ return RedirectHandler (u .String (), StatusMovedPermanently ), u .Path
2257
+ }
2258
+
2221
2259
if path != r .URL .Path {
2222
2260
_ , pattern = mux .handler (host , path )
2223
2261
url := * r .URL
@@ -2273,35 +2311,18 @@ func (mux *ServeMux) Handle(pattern string, handler Handler) {
2273
2311
if handler == nil {
2274
2312
panic ("http: nil handler" )
2275
2313
}
2276
- if mux .m [pattern ]. explicit {
2314
+ if _ , exist := mux .m [pattern ]; exist {
2277
2315
panic ("http: multiple registrations for " + pattern )
2278
2316
}
2279
2317
2280
2318
if mux .m == nil {
2281
2319
mux .m = make (map [string ]muxEntry )
2282
2320
}
2283
- mux .m [pattern ] = muxEntry {explicit : true , h : handler , pattern : pattern }
2321
+ mux .m [pattern ] = muxEntry {h : handler , pattern : pattern }
2284
2322
2285
2323
if pattern [0 ] != '/' {
2286
2324
mux .hosts = true
2287
2325
}
2288
-
2289
- // Helpful behavior:
2290
- // If pattern is /tree/, insert an implicit permanent redirect for /tree.
2291
- // It can be overridden by an explicit registration.
2292
- n := len (pattern )
2293
- if n > 0 && pattern [n - 1 ] == '/' && ! mux .m [pattern [0 :n - 1 ]].explicit {
2294
- // If pattern contains a host name, strip it and use remaining
2295
- // path for redirect.
2296
- path := pattern
2297
- if pattern [0 ] != '/' {
2298
- // In pattern, at least the last character is a '/', so
2299
- // strings.Index can't be -1.
2300
- path = pattern [strings .Index (pattern , "/" ):]
2301
- }
2302
- url := & url.URL {Path : path }
2303
- mux .m [pattern [0 :n - 1 ]] = muxEntry {h : RedirectHandler (url .String (), StatusMovedPermanently ), pattern : pattern }
2304
- }
2305
2326
}
2306
2327
2307
2328
// HandleFunc registers the handler function for the given pattern.
0 commit comments