Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Option to specify preflight response status code #121

Merged
merged 4 commits into from Nov 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -95,6 +95,7 @@ handler = c.Handler(handler)
* **AllowCredentials** `bool`: Indicates whether the request can include user credentials like cookies, HTTP authentication or client side SSL certificates. The default is `false`.
* **MaxAge** `int`: Indicates how long (in seconds) the results of a preflight request can be cached. The default is `0` which stands for no max age.
* **OptionsPassthrough** `bool`: Instructs preflight to let other potential next handlers to process the `OPTIONS` method. Turn this on if your application handles `OPTIONS`.
* **OptionsSuccessStatus** `int`: Provides a status code to use for successful OPTIONS requests. Default value is `http.StatusNoContent` (`204`).
* **Debug** `bool`: Debugging flag adds additional output to debug server side CORS issues.

See [API documentation](http://godoc.org/github.com/rs/cors) for more info.
Expand Down
22 changes: 18 additions & 4 deletions cors.go
Expand Up @@ -65,6 +65,9 @@ type Options struct {
// OptionsPassthrough instructs preflight to let other potential next handlers to
// process the OPTIONS method. Turn this on if your application handles OPTIONS.
OptionsPassthrough bool
// Provides a status code to use for successful OPTIONS requests.
// Default value is http.StatusNoContent (204).
OptionsSuccessStatus int
// Debugging flag adds additional output to debug server side CORS issues
Debug bool
}
Expand Down Expand Up @@ -97,8 +100,10 @@ type Cors struct {
allowedOriginsAll bool
// Set to true when allowed headers contains a "*"
allowedHeadersAll bool
allowCredentials bool
optionPassthrough bool
// Status code to use for successful OPTIONS requests
optionsSuccessStatus int
allowCredentials bool
optionPassthrough bool
}

// New creates a new Cors handler with the provided options.
Expand Down Expand Up @@ -171,6 +176,13 @@ func New(options Options) *Cors {
c.allowedMethods = convert(options.AllowedMethods, strings.ToUpper)
}

// Options Success Status Code
if options.OptionsSuccessStatus == 0 {
c.optionsSuccessStatus = http.StatusNoContent
} else {
c.optionsSuccessStatus = options.OptionsSuccessStatus
}

return c
}

Expand Down Expand Up @@ -211,7 +223,7 @@ func (c *Cors) Handler(h http.Handler) http.Handler {
if c.optionPassthrough {
h.ServeHTTP(w, r)
} else {
w.WriteHeader(http.StatusNoContent)
w.WriteHeader(c.optionsSuccessStatus)
}
} else {
c.logf("Handler: Actual request")
Expand All @@ -226,6 +238,8 @@ func (c *Cors) HandlerFunc(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodOptions && r.Header.Get("Access-Control-Request-Method") != "" {
c.logf("HandlerFunc: Preflight request")
c.handlePreflight(w, r)

w.WriteHeader(c.optionsSuccessStatus)
} else {
c.logf("HandlerFunc: Actual request")
c.handleActualRequest(w, r)
Expand All @@ -244,7 +258,7 @@ func (c *Cors) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.Handl
if c.optionPassthrough {
next(w, r)
} else {
w.WriteHeader(http.StatusNoContent)
w.WriteHeader(c.optionsSuccessStatus)
}
} else {
c.logf("ServeHTTP: Actual request")
Expand Down
50 changes: 50 additions & 0 deletions cors_test.go
Expand Up @@ -558,3 +558,53 @@ func TestIsMethodAllowedReturnsTrueWithOptions(t *testing.T) {
t.Error("IsMethodAllowed should return true when c.allowedMethods is nil.")
}
}

func TestOptionsSuccessStatusCodeDefault(t *testing.T) {
s := New(Options{
// Intentionally left blank.
})

req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil)
req.Header.Add("Access-Control-Request-Method", "GET")

t.Run("Handler", func(t *testing.T) {
res := httptest.NewRecorder()
s.Handler(testHandler).ServeHTTP(res, req)
assertResponse(t, res, http.StatusNoContent)
})
t.Run("HandlerFunc", func(t *testing.T) {
res := httptest.NewRecorder()
s.HandlerFunc(res, req)
assertResponse(t, res, http.StatusNoContent)
})
t.Run("Negroni", func(t *testing.T) {
res := httptest.NewRecorder()
s.ServeHTTP(res, req, testHandler)
assertResponse(t, res, http.StatusNoContent)
})
}

func TestOptionsSuccessStatusCodeOverride(t *testing.T) {
s := New(Options{
OptionsSuccessStatus: http.StatusOK,
})

req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil)
req.Header.Add("Access-Control-Request-Method", "GET")

t.Run("Handler", func(t *testing.T) {
res := httptest.NewRecorder()
s.Handler(testHandler).ServeHTTP(res, req)
assertResponse(t, res, http.StatusOK)
})
t.Run("HandlerFunc", func(t *testing.T) {
res := httptest.NewRecorder()
s.HandlerFunc(res, req)
assertResponse(t, res, http.StatusOK)
})
t.Run("Negroni", func(t *testing.T) {
res := httptest.NewRecorder()
s.ServeHTTP(res, req, testHandler)
assertResponse(t, res, http.StatusOK)
})
}