diff --git a/cors.go b/cors.go index 8df8163..81424ea 100644 --- a/cors.go +++ b/cors.go @@ -3,15 +3,15 @@ // // You can configure it by passing an option struct to cors.New: // -// c := cors.New(cors.Options{ -// AllowedOrigins: []string{"foo.com"}, -// AllowedMethods: []string{"GET", "POST", "DELETE"}, -// AllowCredentials: true, -// }) +// c := cors.New(cors.Options{ +// AllowedOrigins: []string{"foo.com"}, +// AllowedMethods: []string{"GET", "POST", "DELETE"}, +// AllowCredentials: true, +// }) // // Then insert the handler in the chain: // -// handler = c.Handler(handler) +// handler = c.Handler(handler) // // See Options documentation for more options. // @@ -69,6 +69,17 @@ type Options struct { // Debugging flag adds additional output to debug server side CORS issues Debug bool + + // ErrorHandler is a custom error handler function for handling CORS errors. + // if you want to write success response or call next handler, return true. + // If you want to terminate further processing, return false. + ErrorHandler func(w http.ResponseWriter, r *http.Request, cors Cors, err error) bool +} + +// defaultErrorHandler is the default error handler for the CORS middleware. +func defaultErrorHandler(_ http.ResponseWriter, _ *http.Request, cors Cors, err error) bool { + cors.logf("%v", err) + return true } // Logger generic interface for logger @@ -108,6 +119,8 @@ type Cors struct { allowCredentials bool optionPassthrough bool + + errorHandler func(w http.ResponseWriter, r *http.Request, cors Cors, err error) bool } // New creates a new Cors handler with the provided options. @@ -118,11 +131,16 @@ func New(options Options) *Cors { allowCredentials: options.AllowCredentials, maxAge: options.MaxAge, optionPassthrough: options.OptionsPassthrough, + errorHandler: options.ErrorHandler, } if options.Debug && c.Log == nil { c.Log = log.New(os.Stdout, "[cors] ", log.LstdFlags) } + if c.errorHandler == nil { + c.errorHandler = defaultErrorHandler + } + // Normalize options // Note: for origins and methods matching, the spec requires a case-sensitive matching. // As it may error prone, we chose to ignore the spec here. @@ -212,32 +230,35 @@ func (c *Cors) Handler(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method == http.MethodOptions && r.Header.Get("Access-Control-Request-Method") != "" { c.logf("Handler: Preflight request") - c.handlePreflight(w, r) + ret := c.handlePreflight(w, r) // Preflight requests are standalone and should stop the chain as some other // middleware may not handle OPTIONS requests correctly. One typical example // is authentication middleware ; OPTIONS requests won't carry authentication // headers (see #1) if c.optionPassthrough { - next.ServeHTTP(w, r) + if ret { + next.ServeHTTP(w, r) + } } else { - w.WriteHeader(http.StatusOK) + if ret { + w.WriteHeader(http.StatusOK) + } } } else { - c.logf("Handler: Actual request") - c.handleActualRequest(w, r) - next.ServeHTTP(w, r) + if c.handleActualRequest(w, r) { + next.ServeHTTP(w, r) + } } }) } // handlePreflight handles pre-flight CORS requests -func (c *Cors) handlePreflight(w http.ResponseWriter, r *http.Request) { +func (c *Cors) handlePreflight(w http.ResponseWriter, r *http.Request) bool { headers := w.Header() origin := r.Header.Get("Origin") if r.Method != http.MethodOptions { - c.logf("Preflight aborted: %s!=OPTIONS", r.Method) - return + return c.errorHandler(w, r, *c, &PreflightNotOptionMethodError{Method: r.Method}) } // Always set Vary headers // see https://github.com/rs/cors/issues/10, @@ -247,23 +268,19 @@ func (c *Cors) handlePreflight(w http.ResponseWriter, r *http.Request) { headers.Add("Vary", "Access-Control-Request-Headers") if origin == "" { - c.logf("Preflight aborted: empty origin") - return + return c.errorHandler(w, r, *c, &PreflightEmptyOriginError{}) } if !c.isOriginAllowed(r, origin) { - c.logf("Preflight aborted: origin '%s' not allowed", origin) - return + return c.errorHandler(w, r, *c, &PreflightNotOriginAllowedError{Origin: origin}) } reqMethod := r.Header.Get("Access-Control-Request-Method") if !c.isMethodAllowed(reqMethod) { - c.logf("Preflight aborted: method '%s' not allowed", reqMethod) - return + return c.errorHandler(w, r, *c, &PreflightNotAllowedMethodError{RequestMethod: reqMethod}) } reqHeaders := parseHeaderList(r.Header.Get("Access-Control-Request-Headers")) if !c.areHeadersAllowed(reqHeaders) { - c.logf("Preflight aborted: headers '%v' not allowed", reqHeaders) - return + return c.errorHandler(w, r, *c, &PreflightNotHeadersAllowedError{RequestHeaders: reqHeaders}) } if c.allowedOriginsAll { headers.Set("Access-Control-Allow-Origin", "*") @@ -286,22 +303,22 @@ func (c *Cors) handlePreflight(w http.ResponseWriter, r *http.Request) { headers.Set("Access-Control-Max-Age", strconv.Itoa(c.maxAge)) } c.logf("Preflight response headers: %v", headers) + + return true } // handleActualRequest handles simple cross-origin requests, actual request or redirects -func (c *Cors) handleActualRequest(w http.ResponseWriter, r *http.Request) { +func (c *Cors) handleActualRequest(w http.ResponseWriter, r *http.Request) bool { headers := w.Header() origin := r.Header.Get("Origin") // Always set Vary, see https://github.com/rs/cors/issues/10 headers.Add("Vary", "Origin") if origin == "" { - c.logf("Actual request no headers added: missing origin") - return + return c.errorHandler(w, r, *c, &ActualMissingOriginError{}) } if !c.isOriginAllowed(r, origin) { - c.logf("Actual request no headers added: origin '%s' not allowed", origin) - return + return c.errorHandler(w, r, *c, &ActualOriginNotAllowedError{Origin: origin}) } // Note that spec does define a way to specifically disallow a simple method like GET or @@ -309,9 +326,7 @@ func (c *Cors) handleActualRequest(w http.ResponseWriter, r *http.Request) { // spec doesn't instruct to check the allowed methods for simple cross-origin requests. // We think it's a nice feature to be able to have control on those methods though. if !c.isMethodAllowed(r.Method) { - c.logf("Actual request no headers added: method '%s' not allowed", r.Method) - - return + return c.errorHandler(w, r, *c, &ActualMethodNotAllowedError{RequestMethod: r.Method}) } if c.allowedOriginsAll { headers.Set("Access-Control-Allow-Origin", "*") @@ -325,6 +340,8 @@ func (c *Cors) handleActualRequest(w http.ResponseWriter, r *http.Request) { headers.Set("Access-Control-Allow-Credentials", "true") } c.logf("Actual response added headers: %v", headers) + + return true } // convenience method. checks if a logger is set. diff --git a/cors_test.go b/cors_test.go index e8992a5..d90ea72 100644 --- a/cors_test.go +++ b/cors_test.go @@ -1,6 +1,8 @@ package cors import ( + "encoding/json" + "errors" "net/http" "net/http/httptest" "regexp" @@ -38,6 +40,41 @@ func assertResponse(t *testing.T, res *httptest.ResponseRecorder, responseCode i } } +func customErrorHandler(w http.ResponseWriter, _ *http.Request, cors Cors, err error) bool { + _, ok := err.(Error) + if ok { + cors.logf("%v", err) + res := struct { + Message string `json:"message"` + }{ + Message: "CORS error: " + err.Error(), + } + switch { + case errors.Is(err, &PreflightNotOptionMethodError{}): + fallthrough + case errors.Is(err, &PreflightNotAllowedMethodError{}): + fallthrough + case errors.Is(err, &ActualMethodNotAllowedError{}): + w.WriteHeader(http.StatusMethodNotAllowed) + default: + w.WriteHeader(http.StatusForbidden) + } + if err := json.NewEncoder(w).Encode(res); err != nil { + cors.logf("CORS error encoding failed: %v", err) + } + return false + } + res := struct { + Message string `json:"message"` + }{ + Message: "CORS error: An unexpected error has occurred", + } + if err := json.NewEncoder(w).Encode(res); err != nil { + cors.logf("CORS error encoding failed: %v", err) + } + return false +} + func TestSpec(t *testing.T) { cases := []struct { name string @@ -45,6 +82,7 @@ func TestSpec(t *testing.T) { method string reqHeaders map[string]string resHeaders map[string]string + resCode int }{ { "NoConfig", @@ -56,6 +94,19 @@ func TestSpec(t *testing.T) { map[string]string{ "Vary": "Origin", }, + http.StatusOK, + }, + { + "OnlyCustomErrorHandler", + Options{ + ErrorHandler: customErrorHandler, + }, + "GET", + map[string]string{}, + map[string]string{ + "Vary": "Origin", + }, + http.StatusForbidden, }, { "MatchAllOrigin", @@ -70,6 +121,23 @@ func TestSpec(t *testing.T) { "Vary": "Origin", "Access-Control-Allow-Origin": "*", }, + http.StatusOK, + }, + { + "MatchAllOriginWithCustomErrorHandler", + Options{ + AllowedOrigins: []string{"*"}, + ErrorHandler: customErrorHandler, + }, + "GET", + map[string]string{ + "Origin": "http://foobar.com", + }, + map[string]string{ + "Vary": "Origin", + "Access-Control-Allow-Origin": "*", + }, + http.StatusOK, }, { "MatchAllOriginWithCredentials", @@ -86,6 +154,7 @@ func TestSpec(t *testing.T) { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Credentials": "true", }, + http.StatusOK, }, { "AllowedOrigin", @@ -100,6 +169,7 @@ func TestSpec(t *testing.T) { "Vary": "Origin", "Access-Control-Allow-Origin": "http://foobar.com", }, + http.StatusOK, }, { "WildcardOrigin", @@ -114,6 +184,7 @@ func TestSpec(t *testing.T) { "Vary": "Origin", "Access-Control-Allow-Origin": "http://foo.bar.com", }, + http.StatusOK, }, { "DisallowedOrigin", @@ -127,6 +198,22 @@ func TestSpec(t *testing.T) { map[string]string{ "Vary": "Origin", }, + http.StatusOK, + }, + { + "DisallowedOriginWithCustomErrorHandler", + Options{ + AllowedOrigins: []string{"http://foobar.com"}, + ErrorHandler: customErrorHandler, + }, + "GET", + map[string]string{ + "Origin": "http://barbaz.com", + }, + map[string]string{ + "Vary": "Origin", + }, + http.StatusForbidden, }, { "DisallowedWildcardOrigin", @@ -140,6 +227,7 @@ func TestSpec(t *testing.T) { map[string]string{ "Vary": "Origin", }, + http.StatusOK, }, { "AllowedOriginFuncMatch", @@ -157,6 +245,7 @@ func TestSpec(t *testing.T) { "Vary": "Origin", "Access-Control-Allow-Origin": "http://foobar.com", }, + http.StatusOK, }, { "AllowOriginFuncNotMatch", @@ -173,6 +262,7 @@ func TestSpec(t *testing.T) { map[string]string{ "Vary": "Origin", }, + http.StatusOK, }, { "MaxAge", @@ -192,6 +282,7 @@ func TestSpec(t *testing.T) { "Access-Control-Allow-Methods": "GET", "Access-Control-Max-Age": "10", }, + http.StatusOK, }, { "AllowedMethod", @@ -209,6 +300,7 @@ func TestSpec(t *testing.T) { "Access-Control-Allow-Origin": "http://foobar.com", "Access-Control-Allow-Methods": "PUT", }, + http.StatusOK, }, { "DisallowedMethod", @@ -224,6 +316,24 @@ func TestSpec(t *testing.T) { map[string]string{ "Vary": "Origin, Access-Control-Request-Method, Access-Control-Request-Headers", }, + http.StatusOK, + }, + { + "DisallowedMethodWithCustomErrorHandler", + Options{ + AllowedOrigins: []string{"http://foobar.com"}, + AllowedMethods: []string{"PUT", "DELETE"}, + ErrorHandler: customErrorHandler, + }, + "OPTIONS", + map[string]string{ + "Origin": "http://foobar.com", + "Access-Control-Request-Method": "PATCH", + }, + map[string]string{ + "Vary": "Origin, Access-Control-Request-Method, Access-Control-Request-Headers", + }, + http.StatusMethodNotAllowed, }, { "AllowedHeaders", @@ -243,6 +353,7 @@ func TestSpec(t *testing.T) { "Access-Control-Allow-Methods": "GET", "Access-Control-Allow-Headers": "X-Header-2, X-Header-1", }, + http.StatusOK, }, { "DefaultAllowedHeaders", @@ -262,6 +373,7 @@ func TestSpec(t *testing.T) { "Access-Control-Allow-Methods": "GET", "Access-Control-Allow-Headers": "Content-Type", }, + http.StatusOK, }, { "AllowedWildcardHeader", @@ -281,6 +393,7 @@ func TestSpec(t *testing.T) { "Access-Control-Allow-Methods": "GET", "Access-Control-Allow-Headers": "X-Header-2, X-Header-1", }, + http.StatusOK, }, { "DisallowedHeader", @@ -297,6 +410,7 @@ func TestSpec(t *testing.T) { map[string]string{ "Vary": "Origin, Access-Control-Request-Method, Access-Control-Request-Headers", }, + http.StatusOK, }, { "OriginHeader", @@ -315,6 +429,7 @@ func TestSpec(t *testing.T) { "Access-Control-Allow-Methods": "GET", "Access-Control-Allow-Headers": "Origin", }, + http.StatusOK, }, { "ExposedHeader", @@ -331,6 +446,7 @@ func TestSpec(t *testing.T) { "Access-Control-Allow-Origin": "http://foobar.com", "Access-Control-Expose-Headers": "X-Header-1, X-Header-2", }, + http.StatusOK, }, { "AllowedCredentials", @@ -349,6 +465,7 @@ func TestSpec(t *testing.T) { "Access-Control-Allow-Methods": "GET", "Access-Control-Allow-Credentials": "true", }, + http.StatusOK, }, { "OptionPassthrough", @@ -365,6 +482,7 @@ func TestSpec(t *testing.T) { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET", }, + http.StatusOK, }, { "NonPreflightOptions", @@ -379,6 +497,7 @@ func TestSpec(t *testing.T) { "Vary": "Origin", "Access-Control-Allow-Origin": "http://foobar.com", }, + http.StatusOK, }, } for i := range cases { @@ -395,6 +514,7 @@ func TestSpec(t *testing.T) { res := httptest.NewRecorder() s.Handler(testHandler).ServeHTTP(res, req) assertHeaders(t, res.Header(), tc.resHeaders) + assertResponse(t, res, tc.resCode) }) }) } @@ -424,6 +544,9 @@ func TestDefault(t *testing.T) { if s.allowedMethods == nil { t.Error("c.allowedMethods should be nil when Default") } + if s.errorHandler == nil { + t.Error("c.errorHandler must not be nil when Default") + } } func TestHandlePreflightInvalidOriginAbortion(t *testing.T) { @@ -441,6 +564,23 @@ func TestHandlePreflightInvalidOriginAbortion(t *testing.T) { }) } +func TestHandlePreflightInvalidOriginAbortionWithErrorHandler(t *testing.T) { + s := New(Options{ + AllowedOrigins: []string{"http://foo.com"}, + ErrorHandler: customErrorHandler, + }) + res := httptest.NewRecorder() + req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil) + req.Header.Add("Origin", "http://example.com/") + + s.handlePreflight(res, req) + + assertHeaders(t, res.Header(), map[string]string{ + "Vary": "Origin, Access-Control-Request-Method, Access-Control-Request-Headers", + }) + assertResponse(t, res, http.StatusForbidden) +} + func TestHandlePreflightNoOptionsAbortion(t *testing.T) { s := New(Options{ // Intentionally left blank. @@ -453,6 +593,19 @@ func TestHandlePreflightNoOptionsAbortion(t *testing.T) { assertHeaders(t, res.Header(), map[string]string{}) } +func TestHandlePreflightOnlyErrorHandleOptionAbortion(t *testing.T) { + s := New(Options{ + ErrorHandler: customErrorHandler, + }) + res := httptest.NewRecorder() + req, _ := http.NewRequest("GET", "http://example.com/foo", nil) + + s.handlePreflight(res, req) + + assertHeaders(t, res.Header(), map[string]string{}) + assertResponse(t, res, http.StatusMethodNotAllowed) +} + func TestHandleActualRequestInvalidOriginAbortion(t *testing.T) { s := New(Options{ AllowedOrigins: []string{"http://foo.com"}, @@ -468,6 +621,23 @@ func TestHandleActualRequestInvalidOriginAbortion(t *testing.T) { }) } +func TestHandleActualRequestInvalidOriginAbortionWithErrorHandler(t *testing.T) { + s := New(Options{ + AllowedOrigins: []string{"http://foo.com"}, + ErrorHandler: customErrorHandler, + }) + res := httptest.NewRecorder() + req, _ := http.NewRequest("GET", "http://example.com/foo", nil) + req.Header.Add("Origin", "http://example.com/") + + s.handleActualRequest(res, req) + + assertHeaders(t, res.Header(), map[string]string{ + "Vary": "Origin", + }) + assertResponse(t, res, http.StatusForbidden) +} + func TestHandleActualRequestInvalidMethodAbortion(t *testing.T) { s := New(Options{ AllowedMethods: []string{"POST"}, @@ -484,6 +654,24 @@ func TestHandleActualRequestInvalidMethodAbortion(t *testing.T) { }) } +func TestHandleActualRequestInvalidMethodAbortionWithErrorHandler(t *testing.T) { + s := New(Options{ + AllowedMethods: []string{"POST"}, + AllowCredentials: true, + ErrorHandler: customErrorHandler, + }) + res := httptest.NewRecorder() + req, _ := http.NewRequest("GET", "http://example.com/foo", nil) + req.Header.Add("Origin", "http://example.com/") + + s.handleActualRequest(res, req) + + assertHeaders(t, res.Header(), map[string]string{ + "Vary": "Origin", + }) + assertResponse(t, res, http.StatusMethodNotAllowed) +} + func TestIsMethodAllowedReturnsFalseWithNoMethods(t *testing.T) { s := New(Options{ // Intentionally left blank. diff --git a/error.go b/error.go new file mode 100644 index 0000000..5dc02ff --- /dev/null +++ b/error.go @@ -0,0 +1,262 @@ +package cors + +import "fmt" + +// Error is an interface for CORS errors. +type Error interface { + CorsError() +} + +// PreflightError is an interface for preflight errors. +type PreflightError interface { + PreflightCorsError() +} + +// ActualRequestError is an interface for actual request errors. +type ActualRequestError interface { + ActualRequestCorsError() +} + +// PreflightNotOptionMethodError is returned when the method is not allowed. +type PreflightNotOptionMethodError struct { + Method string +} + +func (e *PreflightNotOptionMethodError) Error() string { + return fmt.Sprintf("Preflight aborted: %s!=OPTIONS", e.Method) +} + +// Is implements the Is method of the error interface. +func (e *PreflightNotOptionMethodError) Is(target error) bool { + _, ok := target.(*PreflightNotOptionMethodError) + return ok +} + +// As implements the As method of the error interface. +func (e *PreflightNotOptionMethodError) As(target interface{}) bool { + switch target.(type) { + case **PreflightNotOptionMethodError: + return true + default: + return false + } +} + +// PreflightCorsError implements the PreflightCorsError interface. +func (e *PreflightNotOptionMethodError) PreflightCorsError() {} + +// CorsError implements the CorsError interface. +func (e *PreflightNotOptionMethodError) CorsError() {} + +// PreflightNotOriginAllowedError is returned when the origin is not allowed. +type PreflightNotOriginAllowedError struct { + Origin string +} + +func (e *PreflightNotOriginAllowedError) Error() string { + return fmt.Sprintf("Preflight aborted: origin '%s' not allowed", e.Origin) +} + +// Is implements the Is method of the error interface. +func (e *PreflightNotOriginAllowedError) Is(target error) bool { + _, ok := target.(*PreflightNotOriginAllowedError) + return ok +} + +// As implements the As method of the error interface. +func (e *PreflightNotOriginAllowedError) As(target interface{}) bool { + switch target.(type) { + case **PreflightNotOriginAllowedError: + return true + default: + return false + } +} + +// PreflightCorsError implements the PreflightCorsError interface. +func (e *PreflightNotOriginAllowedError) PreflightCorsError() {} + +// CorsError implements the CorsError interface. +func (e *PreflightNotOriginAllowedError) CorsError() {} + +// PreflightEmptyOriginError is returned when the origin header is empty. +type PreflightEmptyOriginError struct{} + +func (e *PreflightEmptyOriginError) Error() string { + return "Preflight aborted: empty origin" +} + +// Is implements the Is method of the error interface. +func (e *PreflightEmptyOriginError) Is(target error) bool { + _, ok := target.(*PreflightEmptyOriginError) + return ok +} + +// As implements the As method of the error interface. +func (e *PreflightEmptyOriginError) As(target interface{}) bool { + switch target.(type) { + case **PreflightEmptyOriginError: + return true + default: + return false + } +} + +// PreflightCorsError implements the PreflightCorsError interface. +func (e *PreflightEmptyOriginError) PreflightCorsError() {} + +// CorsError implements the CorsError interface. +func (e *PreflightEmptyOriginError) CorsError() {} + +// PreflightNotAllowedMethodError is returned when the method is not allowed. +type PreflightNotAllowedMethodError struct { + RequestMethod string +} + +func (e *PreflightNotAllowedMethodError) Error() string { + return fmt.Sprintf("Preflight aborted: method '%s' not allowed", e.RequestMethod) +} + +// Is implements the Is method of the error interface. +func (e *PreflightNotAllowedMethodError) Is(target error) bool { + _, ok := target.(*PreflightNotAllowedMethodError) + return ok +} + +// As implements the As method of the error interface. +func (e *PreflightNotAllowedMethodError) As(target interface{}) bool { + switch target.(type) { + case **PreflightNotAllowedMethodError: + return true + default: + return false + } +} + +// PreflightCorsError implements the PreflightCorsError interface. +func (e *PreflightNotAllowedMethodError) PreflightCorsError() {} + +// CorsError implements the CorsError interface. +func (e *PreflightNotAllowedMethodError) CorsError() {} + +// PreflightNotHeadersAllowedError is returned when the headers are not allowed. +type PreflightNotHeadersAllowedError struct { + RequestHeaders []string +} + +func (e *PreflightNotHeadersAllowedError) Error() string { + return fmt.Sprintf("Preflight aborted: headers '%v' not allowed", e.RequestHeaders) +} + +// Is implements the Is method of the error interface. +func (e *PreflightNotHeadersAllowedError) Is(target error) bool { + _, ok := target.(*PreflightNotHeadersAllowedError) + return ok +} + +// As implements the As method of the error interface. +func (e *PreflightNotHeadersAllowedError) As(target interface{}) bool { + switch target.(type) { + case **PreflightNotHeadersAllowedError: + return true + default: + return false + } +} + +// PreflightCorsError implements the PreflightCorsError interface. +func (e *PreflightNotHeadersAllowedError) PreflightCorsError() {} + +// CorsError implements the CorsError interface. +func (e *PreflightNotHeadersAllowedError) CorsError() {} + +// ActualMissingOriginError is returned when the origin header is missing. +type ActualMissingOriginError struct{} + +func (e *ActualMissingOriginError) Error() string { + return "Actual request no headers added: missing origin" +} + +// Is implements the Is method of the error interface. +func (e *ActualMissingOriginError) Is(target error) bool { + _, ok := target.(*ActualMissingOriginError) + return ok +} + +// As implements the As method of the error interface. +func (e *ActualMissingOriginError) As(target interface{}) bool { + switch target.(type) { + case **ActualMissingOriginError: + return true + default: + return false + } +} + +// ActualRequestCorsError implements the ActualRequestCorsError interface. +func (e *ActualMissingOriginError) ActualRequestCorsError() {} + +// CorsError implements the CorsError interface. +func (e *ActualMissingOriginError) CorsError() {} + +// ActualOriginNotAllowedError is returned when the origin is not allowed. +type ActualOriginNotAllowedError struct { + Origin string +} + +func (e *ActualOriginNotAllowedError) Error() string { + return fmt.Sprintf("Actual request no headers added: origin '%s' not allowed", e.Origin) +} + +// Is implements the Is method of the error interface. +func (e *ActualOriginNotAllowedError) Is(target error) bool { + _, ok := target.(*ActualOriginNotAllowedError) + return ok +} + +// As implements the As method of the error interface. +func (e *ActualOriginNotAllowedError) As(target interface{}) bool { + switch target.(type) { + case **ActualOriginNotAllowedError: + return true + default: + return false + } +} + +// ActualRequestCorsError implements the ActualRequestCorsError interface. +func (e *ActualOriginNotAllowedError) ActualRequestCorsError() {} + +// CorsError implements the CorsError interface. +func (e *ActualOriginNotAllowedError) CorsError() {} + +// ActualMethodNotAllowedError is returned when the method is not allowed. +type ActualMethodNotAllowedError struct { + RequestMethod string +} + +func (e *ActualMethodNotAllowedError) Error() string { + return fmt.Sprintf("Actual request no headers added: method '%s' not allowed", e.RequestMethod) +} + +// Is implements the Is method of the error interface. +func (e *ActualMethodNotAllowedError) Is(target error) bool { + _, ok := target.(*ActualMethodNotAllowedError) + return ok +} + +// As implements the As method of the error interface. +func (e *ActualMethodNotAllowedError) As(target interface{}) bool { + switch target.(type) { + case **ActualMethodNotAllowedError: + return true + default: + return false + } +} + +// ActualRequestCorsError implements the ActualRequestCorsError interface. +func (e *ActualMethodNotAllowedError) ActualRequestCorsError() {} + +// CorsError implements the CorsError interface. +func (e *ActualMethodNotAllowedError) CorsError() {}