@@ -246,6 +246,10 @@ func TestCORS(t *testing.T) {
246246 for k , v := range tc .whenHeaders {
247247 req .Header .Set (k , v )
248248 }
249+ if method == http .MethodOptions && req .Header .Get (echo .HeaderOrigin ) != "" &&
250+ req .Header .Get (echo .HeaderAccessControlRequestMethod ) == "" {
251+ req .Header .Set (echo .HeaderAccessControlRequestMethod , http .MethodGet )
252+ }
249253
250254 err := h (c )
251255
@@ -265,6 +269,70 @@ func TestCORS(t *testing.T) {
265269 }
266270}
267271
272+ func TestCORS_NonPreflightOPTIONSPassThrough (t * testing.T ) {
273+ e := echo .New ()
274+ cors := CORSWithConfig (CORSConfig {
275+ AllowOrigins : []string {"*" },
276+ AllowMethods : []string {http .MethodPut },
277+ })
278+
279+ for _ , tc := range []struct {
280+ name string
281+ headers map [string ]string
282+ }{
283+ {
284+ name : "OPTIONS without Origin" ,
285+ },
286+ {
287+ name : "OPTIONS with Origin but without Access-Control-Request-Method" ,
288+ headers : map [string ]string {
289+ echo .HeaderOrigin : "https://example.com" ,
290+ },
291+ },
292+ } {
293+ t .Run (tc .name , func (t * testing.T ) {
294+ req := httptest .NewRequest (http .MethodOptions , "/hello" , nil )
295+ for k , v := range tc .headers {
296+ req .Header .Set (k , v )
297+ }
298+ rec := httptest .NewRecorder ()
299+ c := e .NewContext (req , rec )
300+ called := false
301+
302+ h := cors (func (c echo.Context ) error {
303+ called = true
304+ c .Response ().Header ().Set (echo .HeaderAllow , "GET, OPTIONS" )
305+ return c .NoContent (http .StatusNoContent )
306+ })
307+
308+ assert .NoError (t , h (c ))
309+ assert .True (t , called )
310+ assert .Equal (t , "GET, OPTIONS" , rec .Header ().Get (echo .HeaderAllow ))
311+ assert .Empty (t , rec .Header ().Get (echo .HeaderAccessControlAllowMethods ))
312+ })
313+ }
314+
315+ t .Run ("true preflight short-circuits next" , func (t * testing.T ) {
316+ req := httptest .NewRequest (http .MethodOptions , "/hello" , nil )
317+ req .Header .Set (echo .HeaderOrigin , "https://example.com" )
318+ req .Header .Set (echo .HeaderAccessControlRequestMethod , http .MethodPut )
319+ rec := httptest .NewRecorder ()
320+ c := e .NewContext (req , rec )
321+ called := false
322+
323+ h := cors (func (c echo.Context ) error {
324+ called = true
325+ return c .NoContent (http .StatusOK )
326+ })
327+
328+ assert .NoError (t , h (c ))
329+ assert .False (t , called )
330+ assert .Equal (t , http .StatusNoContent , rec .Code )
331+ assert .Equal (t , "*" , rec .Header ().Get (echo .HeaderAccessControlAllowOrigin ))
332+ assert .Equal (t , "PUT" , rec .Header ().Get (echo .HeaderAccessControlAllowMethods ))
333+ })
334+ }
335+
268336func Test_allowOriginScheme (t * testing.T ) {
269337 tests := []struct {
270338 domain , pattern string
@@ -298,6 +366,7 @@ func Test_allowOriginScheme(t *testing.T) {
298366 rec := httptest .NewRecorder ()
299367 c := e .NewContext (req , rec )
300368 req .Header .Set (echo .HeaderOrigin , tt .domain )
369+ req .Header .Set (echo .HeaderAccessControlRequestMethod , http .MethodGet )
301370 cors := CORSWithConfig (CORSConfig {
302371 AllowOrigins : []string {tt .pattern },
303372 })
@@ -389,6 +458,7 @@ func Test_allowOriginSubdomain(t *testing.T) {
389458 rec := httptest .NewRecorder ()
390459 c := e .NewContext (req , rec )
391460 req .Header .Set (echo .HeaderOrigin , tt .domain )
461+ req .Header .Set (echo .HeaderAccessControlRequestMethod , http .MethodGet )
392462 cors := CORSWithConfig (CORSConfig {
393463 AllowOrigins : []string {tt .pattern },
394464 })
@@ -472,6 +542,9 @@ func TestCORSWithConfig_AllowMethods(t *testing.T) {
472542 c := e .NewContext (req , rec )
473543
474544 req .Header .Set (echo .HeaderOrigin , tc .whenOrigin )
545+ if tc .whenOrigin != "" {
546+ req .Header .Set (echo .HeaderAccessControlRequestMethod , http .MethodGet )
547+ }
475548 if tc .allowContextKey != "" {
476549 c .Set (echo .ContextKeyHeaderAllow , tc .allowContextKey )
477550 }
@@ -605,6 +678,9 @@ func TestCorsHeaders(t *testing.T) {
605678 if tc .originDomain != "" {
606679 req .Header .Set (echo .HeaderOrigin , tc .originDomain )
607680 }
681+ if tc .method == http .MethodOptions && tc .originDomain != "" {
682+ req .Header .Set (echo .HeaderAccessControlRequestMethod , http .MethodGet )
683+ }
608684
609685 // we run through whole Echo handler chain to see how CORS works with Router OPTIONS handler
610686 e .ServeHTTP (rec , req )
@@ -663,6 +739,7 @@ func Test_allowOriginFunc(t *testing.T) {
663739 rec := httptest .NewRecorder ()
664740 c := e .NewContext (req , rec )
665741 req .Header .Set (echo .HeaderOrigin , origin )
742+ req .Header .Set (echo .HeaderAccessControlRequestMethod , http .MethodGet )
666743 cors := CORSWithConfig (CORSConfig {
667744 AllowOriginFunc : allowOriginFunc ,
668745 })
0 commit comments