Repository: gorilla/mux (go)

Issue:
[bug] setting custom MethodNotAllowedHandler for subrouter returns 404s
**Describe the bug**
> When setting a custom MethodNotAllowedHandler for a Subrouter, a default `404 page not found` error is returned instead of the defined handler. Removing the custom handler makes a `405 method not found` return as normal.

…

**Versions**
> Go version: go1.12 darwin/amd64
> package version: 6137e193cdcba2725f16de84eaf0db2b769d9668

…


**Steps to Reproduce**
> Run a server with a subrouter, whose MethodNotAllowedHandler has been set a custom one. sending a `POST` to `http://localhost:3000/api/test` responses with `404 page not found`

…


**Expected behavior**
> the custom response `custom method not allowed` should be returned when trying to `POST` to `http://localhost:3000/api/test`

…

**Code Snippets**
here's a whole minimal server for your convenience:
```go
package main

import (
	"fmt"
	"net/http"

	"github.com/gorilla/mux"
)

func main() {
	router := mux.NewRouter()
	h := handler{}
	nah := notAllowedHandler{}

	apiRouter := router.PathPrefix("/api").Subrouter()
	apiRouter.MethodNotAllowedHandler = nah
	apiRouter.Handle("/test", h).Methods("GET")

	server := &http.Server{
		Addr:    ":3000",
		Handler: router,
	}

	if err := server.ListenAndServe(); err != http.ErrServerClosed {
		fmt.Println("server shutdown:", err)
	}
}

type handler struct {
}

func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "ok")
}

type notAllowedHandler struct {
}

func (h notAllowedHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusMethodNotAllowed)
	fmt.Fprint(w, "custom method not allowed")
}
```
…


Pull request:
bugfix/subrouter custom methodNotAllowed handler returning 404 (#509)
Fixes #509

**Summary of Changes**

1. check new handler not nil before discarding MethodNotAllowed error
2. add unit test



Patch:
diff --git a/README.md b/README.md
index 28df9a9..35eea9f 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
 [![CircleCI](https://circleci.com/gh/gorilla/mux.svg?style=svg)](https://circleci.com/gh/gorilla/mux)
 [![Sourcegraph](https://sourcegraph.com/github.com/gorilla/mux/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/mux?badge)
 
-![Gorilla Logo](http://www.gorillatoolkit.org/static/images/gorilla-icon-64.png)
+![Gorilla Logo](https://cloud-cdn.questionable.services/gorilla-icon-64.png)
 
 https://www.gorillatoolkit.org/pkg/mux
 
diff --git a/route.go b/route.go
index 8479c68..7343d78 100644
--- a/route.go
+++ b/route.go
@@ -74,7 +74,7 @@ func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
 		return false
 	}
 
-	if match.MatchErr == ErrMethodMismatch {
+	if match.MatchErr == ErrMethodMismatch && r.handler != nil {
 		// We found a route which matches request method, clear MatchErr
 		match.MatchErr = nil
 		// Then override the mis-matched handler


Test patch:
diff --git a/mux_test.go b/mux_test.go
index 34c00dd..e252d39 100644
--- a/mux_test.go
+++ b/mux_test.go
@@ -9,6 +9,7 @@ import (
 	"bytes"
 	"errors"
 	"fmt"
+	"io/ioutil"
 	"net/http"
 	"net/url"
 	"reflect"
@@ -2729,6 +2730,63 @@ func TestMethodNotAllowed(t *testing.T) {
 	}
 }
 
+type customMethodNotAllowedHandler struct {
+	msg string
+}
+
+func (h customMethodNotAllowedHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	w.WriteHeader(http.StatusMethodNotAllowed)
+	fmt.Fprint(w, h.msg)
+}
+
+func TestSubrouterCustomMethodNotAllowed(t *testing.T) {
+	handler := func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) }
+
+	router := NewRouter()
+	router.HandleFunc("/test", handler).Methods(http.MethodGet)
+	router.MethodNotAllowedHandler = customMethodNotAllowedHandler{msg: "custom router handler"}
+
+	subrouter := router.PathPrefix("/sub").Subrouter()
+	subrouter.HandleFunc("/test", handler).Methods(http.MethodGet)
+	subrouter.MethodNotAllowedHandler = customMethodNotAllowedHandler{msg: "custom sub router handler"}
+
+	testCases := map[string]struct {
+		path   string
+		expMsg string
+	}{
+		"router method not allowed": {
+			path:   "/test",
+			expMsg: "custom router handler",
+		},
+		"subrouter method not allowed": {
+			path:   "/sub/test",
+			expMsg: "custom sub router handler",
+		},
+	}
+
+	for name, tc := range testCases {
+		t.Run(name, func(tt *testing.T) {
+			w := NewRecorder()
+			req := newRequest(http.MethodPut, tc.path)
+
+			router.ServeHTTP(w, req)
+
+			if w.Code != http.StatusMethodNotAllowed {
+				tt.Errorf("Expected status code 405 (got %d)", w.Code)
+			}
+
+			b, err := ioutil.ReadAll(w.Body)
+			if err != nil {
+				tt.Errorf("failed to read body: %v", err)
+			}
+
+			if string(b) != tc.expMsg {
+				tt.Errorf("expected msg %q, got %q", tc.expMsg, string(b))
+			}
+		})
+	}
+}
+
 func TestSubrouterNotFound(t *testing.T) {
 	handler := func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) }
 	router := NewRouter()