Repository: gin-gonic/gin (language: go)

Issue:
405 Method Not Allowed is missing the Allow header.
## Description

https://tools.ietf.org/html/rfc7231#section-6.5.5 says

>The origin server MUST generate an Allow header field in a 405 response containing a list of the target resource's currently supported methods.

gin does not do this with `HandleMethodNotAllowed` set to `true`. Also it is arguable that even with this setting enabled, all HTTP/1.0 requests should still be given a 404 error as HTTP 1.0 does not define 405.

## How to reproduce

Server:
```
package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

func main() {
	router := gin.Default()
	router.GET("hi", func(c *gin.Context) {
		name := c.Param("name")
		c.String(http.StatusOK, "Hello %s", name)
	})
	router.HandleMethodNotAllowed = true
	router.Run()
}
```

Test:
```
$ curl -D- localhost:8080/hi -XPOST
```

## Expectations

<!-- Your expectation result of 'curl' command, like -->
```
HTTP/1.0 405 Method Not Allowed
Allow: GET
```

## Actual result

<!-- Actual result showing the problem -->
```
HTTP/1.0 405 Method Not Allowed
Content-Type: text/plain
Date: Mon, 25 May 2020 21:53:14 GMT
Content-Length: 22

405 method not allowed
```

## Environment

- go version: go version go1.14.3 darwin/amd64
- gin version (or commit ref): v1.6.3
- operating system: Mac



Discussion / hints:

Fix patch:
diff --git a/gin.go b/gin.go
index ed8b6dad75..03e7d97839 100644
--- a/gin.go
+++ b/gin.go
@@ -634,17 +634,25 @@ func (engine *Engine) handleHTTPRequest(c *Context) {
 	}
 
 	if engine.HandleMethodNotAllowed {
+		// According to RFC 7231 section 6.5.5, MUST generate an Allow header field in response
+		// containing a list of the target resource's currently supported methods.
+		allowed := make([]string, 0, len(t)-1)
 		for _, tree := range engine.trees {
 			if tree.method == httpMethod {
 				continue
 			}
 			if value := tree.root.getValue(rPath, nil, c.skippedNodes, unescape); value.handlers != nil {
-				c.handlers = engine.allNoMethod
-				serveError(c, http.StatusMethodNotAllowed, default405Body)
-				return
+				allowed = append(allowed, tree.method)
 			}
 		}
+		if len(allowed) > 0 {
+			c.handlers = engine.allNoMethod
+			c.writermem.Header().Set("Allow", strings.Join(allowed, ", "))
+			serveError(c, http.StatusMethodNotAllowed, default405Body)
+			return
+		}
 	}
+
 	c.handlers = engine.allNoRoute
 	serveError(c, http.StatusNotFound, default404Body)
 }


Test patch:
diff --git a/routes_test.go b/routes_test.go
index 633c0aba14..15c0ae759e 100644
--- a/routes_test.go
+++ b/routes_test.go
@@ -475,6 +475,18 @@ func TestRouteNotAllowedEnabled2(t *testing.T) {
 	assert.Equal(t, http.StatusMethodNotAllowed, w.Code)
 }
 
+func TestRouteNotAllowedEnabled3(t *testing.T) {
+	router := New()
+	router.HandleMethodNotAllowed = true
+	router.GET("/path", func(c *Context) {})
+	router.POST("/path", func(c *Context) {})
+	w := PerformRequest(router, http.MethodPut, "/path")
+	assert.Equal(t, http.StatusMethodNotAllowed, w.Code)
+	allowed := w.Header().Get("Allow")
+	assert.Contains(t, allowed, "GET")
+	assert.Contains(t, allowed, "POST")
+}
+
 func TestRouteNotAllowedDisabled(t *testing.T) {
 	router := New()
 	router.HandleMethodNotAllowed = false