Skip to content

Commit 8f160b2

Browse files
committed
Allow arbitrary HTTP method types to be added as routes
1 parent a9879ff commit 8f160b2

File tree

3 files changed

+82
-3
lines changed

3 files changed

+82
-3
lines changed

echo.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -492,8 +492,11 @@ func (e *Echo) RouteNotFound(path string, h HandlerFunc, m ...MiddlewareFunc) *R
492492
return e.Add(RouteNotFound, path, h, m...)
493493
}
494494

495-
// Any registers a new route for all HTTP methods and path with matching handler
495+
// Any registers a new route for all HTTP methods (supported by Echo) and path with matching handler
496496
// in the router with optional route-level middleware.
497+
//
498+
// Note: this method only adds specific set of supported HTTP methods as handler and is not true
499+
// "catch-any-arbitrary-method" way of matching requests.
497500
func (e *Echo) Any(path string, handler HandlerFunc, middleware ...MiddlewareFunc) []*Route {
498501
routes := make([]*Route, len(methods))
499502
for i, m := range methods {

router.go

+17-2
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ type (
5151
put *routeMethod
5252
trace *routeMethod
5353
report *routeMethod
54+
anyOther map[string]*routeMethod
5455
allowHeader string
5556
}
5657
)
@@ -75,7 +76,8 @@ func (m *routeMethods) isHandler() bool {
7576
m.propfind != nil ||
7677
m.put != nil ||
7778
m.trace != nil ||
78-
m.report != nil
79+
m.report != nil ||
80+
len(m.anyOther) != 0
7981
// RouteNotFound/404 is not considered as a handler
8082
}
8183

@@ -121,6 +123,10 @@ func (m *routeMethods) updateAllowHeader() {
121123
if m.report != nil {
122124
buf.WriteString(", REPORT")
123125
}
126+
for method := range m.anyOther { // for simplicity, we use map and therefore order is not deterministic here
127+
buf.WriteString(", ")
128+
buf.WriteString(method)
129+
}
124130
m.allowHeader = buf.String()
125131
}
126132

@@ -408,6 +414,15 @@ func (n *node) addMethod(method string, h *routeMethod) {
408414
case RouteNotFound:
409415
n.notFoundHandler = h
410416
return // RouteNotFound/404 is not considered as a handler so no further logic needs to be executed
417+
default:
418+
if n.methods.anyOther == nil {
419+
n.methods.anyOther = make(map[string]*routeMethod)
420+
}
421+
if h.handler == nil {
422+
delete(n.methods.anyOther, method)
423+
} else {
424+
n.methods.anyOther[method] = h
425+
}
411426
}
412427

413428
n.methods.updateAllowHeader()
@@ -439,7 +454,7 @@ func (n *node) findMethod(method string) *routeMethod {
439454
case REPORT:
440455
return n.methods.report
441456
default: // RouteNotFound/404 is not considered as a handler
442-
return nil
457+
return n.methods.anyOther[method]
443458
}
444459
}
445460

router_test.go

+61
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,67 @@ func TestRouterParam(t *testing.T) {
716716
}
717717
}
718718

719+
func TestRouter_addAndMatchAllSupportedMethods(t *testing.T) {
720+
var testCases = []struct {
721+
name string
722+
givenNoAddRoute bool
723+
whenMethod string
724+
expectPath string
725+
expectError string
726+
}{
727+
{name: "ok, CONNECT", whenMethod: http.MethodConnect},
728+
{name: "ok, DELETE", whenMethod: http.MethodDelete},
729+
{name: "ok, GET", whenMethod: http.MethodGet},
730+
{name: "ok, HEAD", whenMethod: http.MethodHead},
731+
{name: "ok, OPTIONS", whenMethod: http.MethodOptions},
732+
{name: "ok, PATCH", whenMethod: http.MethodPatch},
733+
{name: "ok, POST", whenMethod: http.MethodPost},
734+
{name: "ok, PROPFIND", whenMethod: PROPFIND},
735+
{name: "ok, PUT", whenMethod: http.MethodPut},
736+
{name: "ok, TRACE", whenMethod: http.MethodTrace},
737+
{name: "ok, REPORT", whenMethod: REPORT},
738+
{name: "ok, NON_TRADITIONAL_METHOD", whenMethod: "NON_TRADITIONAL_METHOD"},
739+
{
740+
name: "ok, NOT_EXISTING_METHOD",
741+
whenMethod: "NOT_EXISTING_METHOD",
742+
givenNoAddRoute: true,
743+
expectPath: "/*",
744+
expectError: "code=405, message=Method Not Allowed",
745+
},
746+
}
747+
748+
for _, tc := range testCases {
749+
t.Run(tc.name, func(t *testing.T) {
750+
e := New()
751+
752+
e.GET("/*", handlerFunc)
753+
754+
if !tc.givenNoAddRoute {
755+
e.Add(tc.whenMethod, "/my/*", handlerFunc)
756+
}
757+
758+
req := httptest.NewRequest(tc.whenMethod, "/my/some-url", nil)
759+
rec := httptest.NewRecorder()
760+
c := e.NewContext(req, rec).(*context)
761+
762+
e.router.Find(tc.whenMethod, "/my/some-url", c)
763+
err := c.handler(c)
764+
765+
if tc.expectError != "" {
766+
assert.EqualError(t, err, tc.expectError)
767+
} else {
768+
assert.NoError(t, err)
769+
}
770+
771+
expectPath := "/my/*"
772+
if tc.expectPath != "" {
773+
expectPath = tc.expectPath
774+
}
775+
assert.Equal(t, expectPath, c.Path())
776+
})
777+
}
778+
}
779+
719780
func TestMethodNotAllowedAndNotFound(t *testing.T) {
720781
e := New()
721782
r := e.router

0 commit comments

Comments
 (0)