@ -5,8 +5,8 @@ as defined by http://www.w3.org/TR/cors/
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" } ,
AllowedOrigins : [ ] string { "foo.com" } ,
AllowedMethods : [ ] string { http . MethodGet , http . MethodPost , http . MethodDelete } ,
AllowCredentials : true ,
} )
@ -26,9 +26,6 @@ import (
"os"
"strconv"
"strings"
"github.com/rs/xhandler"
"golang.org/x/net/context"
)
// Options is a configuration container to setup the CORS middleware.
@ -36,7 +33,7 @@ type Options struct {
// AllowedOrigins is a list of origins a cross-domain request can be executed from.
// If the special "*" value is present in the list, all origins will be allowed.
// An origin may contain a wildcard (*) to replace 0 or more characters
// (i.e.: http://*.domain.com). Usage of wildcards implies a small performance penali ty.
// (i.e.: http://*.domain.com). Usage of wildcards implies a small performance penalty.
// Only one wildcard can be used per origin.
// Default value is ["*"]
AllowedOrigins [ ] string
@ -44,8 +41,12 @@ type Options struct {
// as argument and returns true if allowed or false otherwise. If this option is
// set, the content of AllowedOrigins is ignored.
AllowOriginFunc func ( origin string ) bool
// AllowOriginFunc is a custom function to validate the origin. It takes the HTTP Request object and the origin as
// argument and returns true if allowed or false otherwise. If this option is set, the content of `AllowedOrigins`
// and `AllowOriginFunc` is ignored.
AllowOriginRequestFunc func ( r * http . Request , origin string ) bool
// AllowedMethods is a list of methods the client is allowed to use with
// cross-domain requests. Default value is simple methods (GET and POST)
// cross-domain requests. Default value is simple methods (HEAD, GET and POST).
AllowedMethods [ ] string
// AllowedHeaders is list of non simple headers the client is allowed to use with
// cross-domain requests.
@ -55,12 +56,12 @@ type Options struct {
// ExposedHeaders indicates which headers are safe to expose to the API of a CORS
// API specification
ExposedHeaders [ ] string
// AllowCredentials indicates whether the request can include user credentials like
// cookies, HTTP authentication or client side SSL certificates.
AllowCredentials bool
// MaxAge indicates how long (in seconds) the results of a preflight request
// can be cached
MaxAge int
// AllowCredentials indicates whether the request can include user credentials like
// cookies, HTTP authentication or client side SSL certificates.
AllowCredentials bool
// OptionsPassthrough instructs preflight to let other potential next handlers to
// process the OPTIONS method. Turn this on if your application handles OPTIONS.
OptionsPassthrough bool
@ -72,35 +73,38 @@ type Options struct {
type Cors struct {
// Debug logger
Log * log . Logger
// Set to true when allowed origins contains a "*"
allowedOriginsAll bool
// Normalized list of plain allowed origins
allowedOrigins [ ] string
// List of allowed origins containing wildcards
allowedWOrigins [ ] wildcard
// Optional origin validator function
allowOriginFunc func ( origin string ) bool
// Set to true when allowed headers contains a "*"
allowedHeadersAll bool
// Optional origin validator (with request) function
allowOriginRequestFunc func ( r * http . Request , origin string ) bool
// Normalized list of allowed headers
allowedHeaders [ ] string
// Normalized list of allowed methods
allowedMethods [ ] string
// Normalized list of exposed headers
exposedHeaders [ ] string
exposedHeaders [ ] string
maxAge int
// Set to true when allowed origins contains a "*"
allowedOriginsAll bool
// Set to true when allowed headers contains a "*"
allowedHeadersAll bool
allowCredentials bool
maxAge int
optionPassthrough bool
}
// New creates a new Cors handler with the provided options.
func New ( options Options ) * Cors {
c := & Cors {
exposedHeaders : convert ( options . ExposedHeaders , http . CanonicalHeaderKey ) ,
allowOriginFunc : options . AllowOriginFunc ,
allowCredentials : options . AllowCredentials ,
maxAge : options . MaxAge ,
optionPassthrough : options . OptionsPassthrough ,
exposedHeaders : convert ( options . ExposedHeaders , http . CanonicalHeaderKey ) ,
allowOriginFunc : options . AllowOriginFunc ,
allowOriginRequestFunc : options . AllowOriginRequestFunc ,
allowCredentials : options . AllowCredentials ,
maxAge : options . MaxAge ,
optionPassthrough : options . OptionsPassthrough ,
}
if options . Debug {
c . Log = log . New ( os . Stdout , "[cors] " , log . LstdFlags )
@ -112,8 +116,10 @@ func New(options Options) *Cors {
// Allowed Origins
if len ( options . AllowedOrigins ) == 0 {
// Default is all origins
c . allowedOriginsAll = true
if options . AllowOriginFunc == nil && options . AllowOriginRequestFunc == nil {
// Default is all origins
c . allowedOriginsAll = true
}
} else {
c . allowedOrigins = [ ] string { }
c . allowedWOrigins = [ ] wildcard { }
@ -128,7 +134,7 @@ func New(options Options) *Cors {
break
} else if i := strings . IndexByte ( origin , '*' ) ; i >= 0 {
// Split the origin in two: start and end string without the *
w := wildcard { origin [ 0 : i ] , origin [ i + 1 : len ( origin ) ] }
w := wildcard { origin [ 0 : i ] , origin [ i + 1 : ] }
c . allowedWOrigins = append ( c . allowedWOrigins , w )
} else {
c . allowedOrigins = append ( c . allowedOrigins , origin )
@ -139,7 +145,7 @@ func New(options Options) *Cors {
// Allowed Headers
if len ( options . AllowedHeaders ) == 0 {
// Use sensible defaults
c . allowedHeaders = [ ] string { "Origin" , "Accept" , "Content-Type" }
c . allowedHeaders = [ ] string { "Origin" , "Accept" , "Content-Type" , "X-Requested-With" }
} else {
// Origin is always appended as some browsers will always request for this header at preflight
c . allowedHeaders = convert ( append ( options . AllowedHeaders , "Origin" ) , http . CanonicalHeaderKey )
@ -155,7 +161,7 @@ func New(options Options) *Cors {
// Allowed Methods
if len ( options . AllowedMethods ) == 0 {
// Default is spec's "simple" methods
c . allowedMethods = [ ] string { "GET" , "POST" }
c . allowedMethods = [ ] string { http . MethodGet , http . MethodPost , http . MethodHead }
} else {
c . allowedMethods = convert ( options . AllowedMethods , strings . ToUpper )
}
@ -163,16 +169,34 @@ func New(options Options) *Cors {
return c
}
// Default creates a new Cors handler with default options
// Default creates a new Cors handler with default options.
func Default ( ) * Cors {
return New ( Options { } )
}
// AllowAll create a new Cors handler with permissive configuration allowing all
// origins with all standard methods with any header and credentials.
func AllowAll ( ) * Cors {
return New ( Options {
AllowedOrigins : [ ] string { "*" } ,
AllowedMethods : [ ] string {
http . MethodHead ,
http . MethodGet ,
http . MethodPost ,
http . MethodPut ,
http . MethodPatch ,
http . MethodDelete ,
} ,
AllowedHeaders : [ ] string { "*" } ,
AllowCredentials : false ,
} )
}
// Handler apply the CORS specification on the request, and add relevant CORS headers
// as necessary.
func ( c * Cors ) Handler ( h http . Handler ) http . Handler {
return http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
if r . Method == "OPTIONS" {
if r . Method == http . MethodOptions && r . Header . Get ( "Access-Control-Request-Method" ) != " " {
c . logf ( "Handler: Preflight request" )
c . handlePreflight ( w , r )
// Preflight requests are standalone and should stop the chain as some other
@ -192,32 +216,9 @@ func (c *Cors) Handler(h http.Handler) http.Handler {
} )
}
// HandlerC is net/context aware handler
func ( c * Cors ) HandlerC ( h xhandler . HandlerC ) xhandler . HandlerC {
return xhandler . HandlerFuncC ( func ( ctx context . Context , w http . ResponseWriter , r * http . Request ) {
if r . Method == "OPTIONS" {
c . logf ( "Handler: Preflight request" )
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 {
h . ServeHTTPC ( ctx , w , r )
} else {
w . WriteHeader ( http . StatusOK )
}
} else {
c . logf ( "Handler: Actual request" )
c . handleActualRequest ( w , r )
h . ServeHTTPC ( ctx , w , r )
}
} )
}
// HandlerFunc provides Martini compatible handler
func ( c * Cors ) HandlerFunc ( w http . ResponseWriter , r * http . Request ) {
if r . Method == "OPTIONS " {
if r . Method == http . MethodOptions && r . Header . Get ( "Access-Control-Request-Method" ) != "" {
c . logf ( "HandlerFunc: Preflight request" )
c . handlePreflight ( w , r )
} else {
@ -228,7 +229,7 @@ func (c *Cors) HandlerFunc(w http.ResponseWriter, r *http.Request) {
// Negroni compatible interface
func ( c * Cors ) ServeHTTP ( w http . ResponseWriter , r * http . Request , next http . HandlerFunc ) {
if r . Method == "OPTIONS " {
if r . Method == http . MethodOptions && r . Header . Get ( "Access-Control-Request-Method" ) != " " {
c . logf ( "ServeHTTP: Preflight request" )
c . handlePreflight ( w , r )
// Preflight requests are standalone and should stop the chain as some other
@ -252,7 +253,7 @@ func (c *Cors) handlePreflight(w http.ResponseWriter, r *http.Request) {
headers := w . Header ( )
origin := r . Header . Get ( "Origin" )
if r . Method != "OPTIONS" {
if r . Method != http . MethodOptions {
c . logf ( " Preflight aborted: %s!=OPTIONS" , r . Method )
return
}
@ -267,7 +268,7 @@ func (c *Cors) handlePreflight(w http.ResponseWriter, r *http.Request) {
c . logf ( " Preflight aborted: empty origin" )
return
}
if ! c . isOriginAllowed ( origin ) {
if ! c . isOriginAllowed ( r , origin ) {
c . logf ( " Preflight aborted: origin '%s' not allowed" , origin )
return
}
@ -282,7 +283,11 @@ func (c *Cors) handlePreflight(w http.ResponseWriter, r *http.Request) {
c . logf ( " Preflight aborted: headers '%v' not allowed" , reqHeaders )
return
}
headers . Set ( "Access-Control-Allow-Origin" , origin )
if c . allowedOriginsAll {
headers . Set ( "Access-Control-Allow-Origin" , "*" )
} else {
headers . Set ( "Access-Control-Allow-Origin" , origin )
}
// Spec says: Since the list of methods can be unbounded, simply returning the method indicated
// by Access-Control-Request-Method (if supported) can be enough
headers . Set ( "Access-Control-Allow-Methods" , strings . ToUpper ( reqMethod ) )
@ -306,7 +311,7 @@ func (c *Cors) handleActualRequest(w http.ResponseWriter, r *http.Request) {
headers := w . Header ( )
origin := r . Header . Get ( "Origin" )
if r . Method == "OPTIONS" {
if r . Method == http . MethodOptions {
c . logf ( " Actual request no headers added: method == %s" , r . Method )
return
}
@ -316,7 +321,7 @@ func (c *Cors) handleActualRequest(w http.ResponseWriter, r *http.Request) {
c . logf ( " Actual request no headers added: missing origin" )
return
}
if ! c . isOriginAllowed ( origin ) {
if ! c . isOriginAllowed ( r , origin ) {
c . logf ( " Actual request no headers added: origin '%s' not allowed" , origin )
return
}
@ -330,7 +335,11 @@ func (c *Cors) handleActualRequest(w http.ResponseWriter, r *http.Request) {
return
}
headers . Set ( "Access-Control-Allow-Origin" , origin )
if c . allowedOriginsAll {
headers . Set ( "Access-Control-Allow-Origin" , "*" )
} else {
headers . Set ( "Access-Control-Allow-Origin" , origin )
}
if len ( c . exposedHeaders ) > 0 {
headers . Set ( "Access-Control-Expose-Headers" , strings . Join ( c . exposedHeaders , ", " ) )
}
@ -349,7 +358,10 @@ func (c *Cors) logf(format string, a ...interface{}) {
// isOriginAllowed checks if a given origin is allowed to perform cross-domain requests
// on the endpoint
func ( c * Cors ) isOriginAllowed ( origin string ) bool {
func ( c * Cors ) isOriginAllowed ( r * http . Request , origin string ) bool {
if c . allowOriginRequestFunc != nil {
return c . allowOriginRequestFunc ( r , origin )
}
if c . allowOriginFunc != nil {
return c . allowOriginFunc ( origin )
}
@ -378,7 +390,7 @@ func (c *Cors) isMethodAllowed(method string) bool {
return false
}
method = strings . ToUpper ( method )
if method == "OPTIONS" {
if method == http . MethodOptions {
// Always allow preflight requests
return true
}