Send Allow header when 405 Method Not Allowed Required by RFC 2616
diff --git a/router.go b/router.go index 8b5ff34..1f7bbe9 100644 --- a/router.go +++ b/router.go
@@ -145,6 +145,8 @@ // Configurable http.Handler which is called when a request // cannot be routed and HandleMethodNotAllowed is true. // If it is not set, http.Error with http.StatusMethodNotAllowed is used. + // The "Allow" header with allowed request methods is set before the handler + // is called. MethodNotAllowed http.Handler // Function to handle panics recovered from http handlers. @@ -333,6 +335,7 @@ // Handle 405 if r.HandleMethodNotAllowed { + var allow string for method := range r.trees { // Skip the requested method - we already tried this one if method == req.Method { @@ -341,17 +344,27 @@ handle, _, _ := r.trees[method].getValue(req.URL.Path) if handle != nil { - if r.MethodNotAllowed != nil { - r.MethodNotAllowed.ServeHTTP(w, req) + // add request method to list of allowed methods + if len(allow) == 0 { + allow = method } else { - http.Error(w, - http.StatusText(http.StatusMethodNotAllowed), - http.StatusMethodNotAllowed, - ) + allow += ", " + method } - return } } + + if len(allow) > 0 { + w.Header().Set("Allow", allow) + if r.MethodNotAllowed != nil { + r.MethodNotAllowed.ServeHTTP(w, req) + } else { + http.Error(w, + http.StatusText(http.StatusMethodNotAllowed), + http.StatusMethodNotAllowed, + ) + } + return + } } // Handle 404
diff --git a/router_test.go b/router_test.go index e3141bd..0924d48 100644 --- a/router_test.go +++ b/router_test.go
@@ -222,14 +222,30 @@ router := New() router.POST("/path", handlerFunc) - // Test not allowed + // test not allowed r, _ := http.NewRequest("GET", "/path", nil) w := httptest.NewRecorder() router.ServeHTTP(w, r) if !(w.Code == http.StatusMethodNotAllowed) { t.Errorf("NotAllowed handling failed: Code=%d, Header=%v", w.Code, w.Header()) + } else if allow := w.Header().Get("Allow"); allow != "POST" { + t.Error("unexpected Allow header value: " + allow) } + // add another method + router.DELETE("/path", handlerFunc) + + // test again + r, _ = http.NewRequest("GET", "/path", nil) + w = httptest.NewRecorder() + router.ServeHTTP(w, r) + if !(w.Code == http.StatusMethodNotAllowed) { + t.Errorf("NotAllowed handling failed: Code=%d, Header=%v", w.Code, w.Header()) + } else if allow := w.Header().Get("Allow"); allow != "POST, DELETE" && allow != "DELETE, POST" { + t.Error("unexpected Allow header value: " + allow) + } + + // test custom handler w = httptest.NewRecorder() responseText := "custom method" router.MethodNotAllowed = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { @@ -243,6 +259,9 @@ if w.Code != http.StatusTeapot { t.Errorf("unexpected response code %d want %d", w.Code, http.StatusTeapot) } + if allow := w.Header().Get("Allow"); allow != "POST, DELETE" && allow != "DELETE, POST" { + t.Error("unexpected Allow header value: " + allow) + } } func TestRouterNotFound(t *testing.T) {