Skip to content

Commit 9b85d47

Browse files
committed
1 parent 88697e9 commit 9b85d47

File tree

2 files changed

+163
-3
lines changed

2 files changed

+163
-3
lines changed

mux_test.go

+157
Original file line numberDiff line numberDiff line change
@@ -3137,6 +3137,163 @@ func BenchmarkPopulateContext(b *testing.B) {
31373137
}
31383138
}
31393139

3140+
// testOptionsMiddleWare returns 200 on an OPTIONS request
3141+
func testOptionsMiddleWare(inner http.Handler) http.Handler {
3142+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
3143+
if r.Method == http.MethodOptions {
3144+
w.WriteHeader(http.StatusOK)
3145+
return
3146+
}
3147+
inner.ServeHTTP(w, r)
3148+
})
3149+
}
3150+
3151+
// TestRouterOrder Should Pass whichever order route is defined
3152+
func TestRouterOrder(t *testing.T) {
3153+
type requestCase struct {
3154+
request *http.Request
3155+
expCode int
3156+
}
3157+
3158+
tests := []struct {
3159+
name string
3160+
routes []*Route
3161+
customMiddleware MiddlewareFunc
3162+
requests []requestCase
3163+
}{
3164+
{
3165+
name: "Routes added with same method and intersecting path regex",
3166+
routes: []*Route{
3167+
new(Route).Path("/a/b").Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
3168+
w.WriteHeader(http.StatusNotFound)
3169+
})).Methods(http.MethodGet),
3170+
new(Route).Path("/a/{a}").Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
3171+
w.WriteHeader(http.StatusOK)
3172+
})).Methods(http.MethodGet),
3173+
},
3174+
requests: []requestCase{
3175+
{
3176+
request: newRequest(http.MethodGet, "/a/b"),
3177+
expCode: http.StatusNotFound,
3178+
},
3179+
{
3180+
request: newRequest(http.MethodGet, "/a/a"),
3181+
expCode: http.StatusOK,
3182+
},
3183+
},
3184+
},
3185+
{
3186+
name: "Routes added with same method and intersecting path regex, path with pathVariable first",
3187+
routes: []*Route{
3188+
new(Route).Path("/a/{a}").Handler(http.HandlerFunc(
3189+
func(w http.ResponseWriter, r *http.Request) {
3190+
w.WriteHeader(http.StatusOK)
3191+
})).Methods(http.MethodGet),
3192+
new(Route).Path("/a/b").Handler(http.HandlerFunc(
3193+
func(w http.ResponseWriter, r *http.Request) {
3194+
w.WriteHeader(http.StatusNotFound)
3195+
})).Methods(http.MethodGet),
3196+
},
3197+
requests: []requestCase{
3198+
{
3199+
request: newRequest(http.MethodGet, "/a/b"),
3200+
expCode: http.StatusOK,
3201+
},
3202+
{
3203+
request: newRequest(http.MethodGet, "/a/a"),
3204+
expCode: http.StatusOK,
3205+
},
3206+
},
3207+
},
3208+
{
3209+
name: "Routes added same path - different methods, no path variables",
3210+
routes: []*Route{
3211+
new(Route).Path("/a/b").Handler(http.HandlerFunc(
3212+
func(w http.ResponseWriter, r *http.Request) {
3213+
w.WriteHeader(http.StatusOK)
3214+
})).Methods(http.MethodGet),
3215+
new(Route).Path("/a/b").Handler(http.HandlerFunc(
3216+
func(w http.ResponseWriter, r *http.Request) {
3217+
w.WriteHeader(http.StatusNotFound)
3218+
})).Methods(http.MethodOptions),
3219+
},
3220+
requests: []requestCase{
3221+
{
3222+
request: newRequest(http.MethodGet, "/a/b"),
3223+
expCode: http.StatusOK,
3224+
},
3225+
{
3226+
request: newRequest(http.MethodOptions, "/a/b"),
3227+
expCode: http.StatusNotFound,
3228+
},
3229+
},
3230+
},
3231+
{
3232+
name: "Routes added same path - different methods, with path variables and middleware",
3233+
routes: []*Route{
3234+
new(Route).Path("/a/{a}").Handler(http.HandlerFunc(
3235+
func(w http.ResponseWriter, r *http.Request) {
3236+
w.WriteHeader(http.StatusNotFound)
3237+
})).Methods(http.MethodGet),
3238+
new(Route).Path("/a/b").Handler(nil).Methods(http.MethodOptions),
3239+
},
3240+
customMiddleware: testOptionsMiddleWare,
3241+
requests: []requestCase{
3242+
{
3243+
request: newRequest(http.MethodGet, "/a/b"),
3244+
expCode: http.StatusNotFound,
3245+
},
3246+
{
3247+
request: newRequest(http.MethodOptions, "/a/b"),
3248+
expCode: http.StatusOK,
3249+
},
3250+
},
3251+
},
3252+
{
3253+
name: "Routes added same path - different methods, with path variables and middleware order reversed",
3254+
routes: []*Route{
3255+
new(Route).Path("/a/b").Handler(nil).Methods(http.MethodOptions),
3256+
new(Route).Path("/a/{a}").Handler(http.HandlerFunc(
3257+
func(w http.ResponseWriter, r *http.Request) {
3258+
w.WriteHeader(http.StatusNotFound)
3259+
})).Methods(http.MethodGet),
3260+
},
3261+
customMiddleware: testOptionsMiddleWare,
3262+
requests: []requestCase{
3263+
{
3264+
request: newRequest(http.MethodGet, "/a/b"),
3265+
expCode: http.StatusNotFound,
3266+
},
3267+
{
3268+
request: newRequest(http.MethodOptions, "/a/b"),
3269+
expCode: http.StatusOK,
3270+
},
3271+
},
3272+
},
3273+
}
3274+
3275+
for _, test := range tests {
3276+
t.Run(test.name, func(t *testing.T) {
3277+
router := NewRouter()
3278+
3279+
if test.customMiddleware != nil {
3280+
router.Use(test.customMiddleware)
3281+
}
3282+
3283+
router.routes = test.routes
3284+
w := NewRecorder()
3285+
3286+
for _, requestCase := range test.requests {
3287+
router.ServeHTTP(w, requestCase.request)
3288+
3289+
if w.Code != requestCase.expCode {
3290+
t.Fatalf("Expected status code %d (got %d)", requestCase.expCode, w.Code)
3291+
}
3292+
}
3293+
})
3294+
}
3295+
}
3296+
31403297
// mapToPairs converts a string map to a slice of string pairs
31413298
func mapToPairs(m map[string]string) []string {
31423299
var i int

route.go

+6-3
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,14 @@ func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
103103
return false
104104
}
105105

106-
if match.MatchErr != nil && r.handler != nil {
106+
// If a route matches, but the HTTP method does not, we do one of two (2) things:
107+
// 1. Reset the match error if we find a matching method later.
108+
// 2. Else, we override the matched handler in the event we have a possible fallback handler for that route.
109+
//
110+
// This prevents propagation of ErrMethodMismatch once a suitable match is found for a Method-Path combination
111+
if match.MatchErr == ErrMethodMismatch {
107112
// We found a route which matches request method, clear MatchErr
108113
match.MatchErr = nil
109-
// Then override the mis-matched handler
110-
match.Handler = r.handler
111114
}
112115

113116
// Yay, we have a match. Let's collect some info about it.

0 commit comments

Comments
 (0)