router not setting ContextKeyHeaderAllow for group, causing cors preflight to not work
Issue Description
Checklist
- [X] Dependencies installed
- [X] No typos
- [X] Searched existing issues and docs
Expected behaviour
curl --location --request OPTIONS 'http://localhost:8092/api/test'
--data ''
should return Allow: OPTIONS, POST Vary: Origin Date: Fri, 05 Jan 2024 12:42:24 GMT
Actual behaviour
returns
Vary: Origin Date: Fri, 05 Jan 2024 12:42:24 GMT
not reaching this code in router
ctx.Set(ContextKeyHeaderAllow, currentNode.methods.allowHeader)
instead it is going into this code above
if matchedRouteMethod != nil {
rPath = matchedRouteMethod.ppath
rPNames = matchedRouteMethod.pnames
Steps to reproduce
Working code to debug
package main
import (
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"net/http"
)
func main() {
e := echo.New()
e.HideBanner = true
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
AllowOrigins: []string{"*"},
AllowHeaders: []string{echo.HeaderOrigin, echo.HeaderContentType, echo.HeaderAccept},
}))
e.Use(middleware.Logger())
e.Use(middleware.Recover())
e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "Hello, World!")
})
api := e.Group("/api", nil)
api.POST("/test", func(c echo.Context) error {
return c.String(http.StatusOK, "Hello, Test!")
})
e.Logger.Fatal(e.Start(":8092"))
}
Version/commit
v4.11.3 and v4.11.4 (only 2 tested)
Hi, sorry for late reply but I think problem is not with router. You example is missing origin header. CORS preflight request must have origin header.
If there would be origin header the response would be:
x@x:~/$ curl --location --request OPTIONS 'http://localhost:8092/api/test' -v -H "Origin: http://test.test"
* processing: http://localhost:8092/api/test
* Trying [::1]:8092...
* Connected to localhost (::1) port 8092
> OPTIONS /api/test HTTP/1.1
> Host: localhost:8092
> User-Agent: curl/8.2.1
> Accept: */*
> Origin: http://test.test
>
< HTTP/1.1 204 No Content
< Access-Control-Allow-Headers: Origin,Content-Type,Accept
< Access-Control-Allow-Methods: GET,HEAD,PUT,PATCH,POST,DELETE
< Access-Control-Allow-Origin: *
< Vary: Origin
< Vary: Access-Control-Request-Method
< Vary: Access-Control-Request-Headers
< Date: Tue, 06 Feb 2024 06:06:27 GMT
<
* Connection #0 to host localhost left intact
but I do admit that Access-Control-Allow-Methods: GET,HEAD,PUT,PATCH,POST,DELETE means that router actually matches 404 route /api/* that is being added with api := e.Group("/api", nil) line. that last nil makes it to add these additional routes.
but nil as middleware makes your other requests panic
For example this panics:
curl --location --request POST 'http://localhost:8092/api/test' -v -H "Origin: http://test.test"
@aakil786 Somewhat related: https://github.com/labstack/echo/issues/2534
Ok yes removing the nil (for middleware) solves the problem, with my test code sample. My actual code however does not pass a nil but uses a keycloak middleware, and I still have a problem. Apologies for my poor sample.
Ok it looks like the issue is when passing a middleware to the group.
package main
import (
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"net/http"
"time"
)
func main() {
e := echo.New()
e.HideBanner = true
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
AllowOrigins: []string{"*"},
AllowHeaders: []string{echo.HeaderOrigin, echo.HeaderContentType, echo.HeaderAccept},
}))
e.Use(middleware.Logger())
e.Use(middleware.Recover())
e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "Hello, World!")
})
api := e.Group("/api", CustomLogger())
api.POST("/test", func(c echo.Context) error {
return c.String(http.StatusOK, "Hello, Test!")
})
e.Logger.Fatal(e.Start(":8092"))
}
// CustomLogger logs the start and end time of each request
func CustomLogger() echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
start := time.Now()
if err := next(c); err != nil {
c.Error(err)
}
end := time.Now()
latency := end.Sub(start)
c.Logger().Printf("---->>>> Latency for %s: %s", c.Request().URL, latency.String())
return nil
}
}
}
curl --location --request OPTIONS 'http://localhost:8092/api/test' -v -H "Origin: http://test.test"
returns the following when a middleware is passed in
< HTTP/1.1 204 No Content < Access-Control-Allow-Headers: Origin,Content-Type,Accept < Access-Control-Allow-Methods: GET,HEAD,PUT,PATCH,POST,DELETE < Access-Control-Allow-Origin: * < Vary: Origin < Vary: Access-Control-Request-Method < Vary: Access-Control-Request-Headers < Date: Thu, 25 Apr 2024 14:34:43 GMT
when the middleware is removed then it returns
< HTTP/1.1 204 No Content < Access-Control-Allow-Headers: Origin,Content-Type,Accept < Access-Control-Allow-Methods: OPTIONS, POST < Access-Control-Allow-Origin: * < Allow: OPTIONS, POST < Vary: Origin < Vary: Access-Control-Request-Method < Vary: Access-Control-Request-Headers < Date: Thu, 25 Apr 2024 14:36:26 GMT
I'm quite sure this caused by hidden routes that group middlewares add. This is very old hack to make sure that group middlewares are applied to 404 cases. Which we can not remove as there are plenty of people "using" that feature.
https://github.com/labstack/echo/blob/88c379ff77278f553a0f3c44d27786b5a450b6e9/group.go#L22-L33