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) {