-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Description
Issue Description
Echo's CORS middleware misclassifies all OPTIONS requests as preflight requests, thereby unduly preventing requests from hitting user-registered OPTIONS endpoints.
I've previously discussed the general problem on my personal blog and how Echo suffers from it in issue #2510.
Checklist
- Dependencies installed
- No typos
- Searched existing issues and docs
Expected behaviour
(For this section and the next, please refer to the server code below.)
Consider the OPTIONS requests resulting from the following two curl commands:
curl -v -XOPTIONS \
localhost:8080/hellocurl -v -XOPTIONS \
-H "Origin: https://example.com" \
localhost:8080/helloAccording to the Fetch standard, neither request is a preflight request, because
- the first one lacks both an
Originheader and anAccess-Control-Request-Methodheader, and - the second one lacks an
Access-Control-Request-Methodheader.
Therefore, those requests should get through the CORS middleware, exercise the handler registered on /hello, and get a response of this kind:
HTTP/1.1 204 No Content
Allow: GET, OPTIONS
Date: Mon, 23 Oct 2023 12:30:33 GMTIn contrast, an OPTIONS requests resulting from the following curl command is a bona fide preflight request; as such, it should be (and is) intercepted and handled by the CORS middleware:
curl -v -XOPTIONS \
-H "Origin: https://example.com" \
-H "Access-Control-Request-Method: PUT" \
localhost:8080/helloHTTP/1.1 204 No Content
Access-Control-Allow-Methods: PUT
Access-Control-Allow-Origin: *
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
-snip-Actual behaviour
The first two aforementioned OPTIONS requests get intercepted and handled by the CORS middleware rather than by the handler registered on OPTIONS /hello.
Steps to reproduce
- Run the program to start the server.
- Exercise it via
curl(see commands above).
Working code to debug
package main
import (
"errors"
"net/http"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
)
func main() {
e := echo.New()
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
AllowOrigins: []string{"*"},
AllowMethods: []string{http.MethodPut},
}))
e.GET("/hello", func(c echo.Context) error {
return c.String(http.StatusOK, "Hello, World!")
})
e.OPTIONS("/hello", func(c echo.Context) error {
c.Response().Header().Set("Allow", "GET, OPTIONS")
c.Response().WriteHeader(http.StatusNoContent)
return nil
})
if err := e.Start(":8080"); err != nil && !errors.Is(err, http.ErrServerClosed) {
e.Logger.Fatal(err)
}
}Version/commit
Echo v4.11.2. See my go.mod below:
module whatever
go 1.21.3
require github.com/labstack/echo/v4 v4.11.2
require (
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/labstack/gommon v0.4.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
golang.org/x/crypto v0.14.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/time v0.3.0 // indirect
)