goyave.dev/goyave/v5@v5.0.0-rc9.0.20240517145003-d3f977d0b9f3/cors/cors.go (about) 1 package cors 2 3 import ( 4 "net/http" 5 "strconv" 6 "strings" 7 "time" 8 9 "slices" 10 ) 11 12 // Options holds the CORS configuration for a router. 13 type Options struct { 14 // AllowedOrigins is a list of origins a cross-domain request can be executed from. 15 // If the first value in the slice is "*" or if the slice is empty, all origins will be allowed. 16 // Default value is ["*"] 17 AllowedOrigins []string 18 19 // AllowedMethods is a list of methods the client is allowed to use with cross-domain requests. 20 // Default value is ["HEAD", "GET", "POST", "PUT", "PATCH", "DELETE"]. 21 AllowedMethods []string 22 23 // AllowedHeaders is a list of non simple headers the client is allowed to use with 24 // cross-domain requests. 25 // If the first value in the slice is "*", all headers will be allowed. 26 // If the slice is empty, the request's headers will be reflected. 27 // Default value is ["Origin", "Accept", "Content-Type", "X-Requested-With", "Authorization"]. 28 AllowedHeaders []string 29 30 // ExposedHeaders indicates which headers are safe to expose to the API of a CORS 31 // API specification 32 ExposedHeaders []string 33 34 // MaxAge indicates how long the results of a preflight request can be cached. 35 // Default is 12 hours. 36 MaxAge time.Duration 37 38 // AllowCredentials indicates whether the request can include user credentials like 39 // cookies, HTTP authentication or client side SSL certificates. 40 AllowCredentials bool 41 42 // OptionsPassthrough instructs preflight to let other potential next handlers to 43 // process the OPTIONS method. Turn this on if your application handles OPTIONS. 44 OptionsPassthrough bool 45 } 46 47 // Default create new CORS options with default settings. 48 // The returned value can be used as a starting point for 49 // customized options. 50 func Default() *Options { 51 return &Options{ 52 AllowedOrigins: []string{"*"}, 53 AllowedMethods: []string{ 54 http.MethodHead, 55 http.MethodGet, 56 http.MethodPost, 57 http.MethodPut, 58 http.MethodPatch, 59 http.MethodDelete, 60 }, 61 AllowedHeaders: []string{"Origin", "Accept", "Content-Type", "X-Requested-With", "Authorization"}, 62 AllowCredentials: false, 63 MaxAge: time.Hour * 12, 64 } 65 } 66 67 // ConfigureCommon configures common headers between regular and preflight requests: 68 // Origin, Credentials and Exposed Headers. 69 func (o *Options) ConfigureCommon(headers http.Header, requestHeaders http.Header) { 70 o.configureOrigin(headers, requestHeaders) 71 o.configureCredentials(headers) 72 o.configureExposedHeaders(headers) 73 } 74 75 func (o *Options) configureOrigin(headers http.Header, requestHeaders http.Header) { 76 if len(o.AllowedOrigins) == 0 || o.AllowedOrigins[0] == "*" { 77 headers.Set("Access-Control-Allow-Origin", "*") 78 } else { 79 if o.validateOrigin(requestHeaders) { 80 headers.Set("Access-Control-Allow-Origin", requestHeaders.Get("Origin")) 81 } 82 headers.Add("Vary", "Origin") 83 } 84 } 85 86 func (o *Options) configureCredentials(headers http.Header) { 87 if o.AllowCredentials { 88 headers.Set("Access-Control-Allow-Credentials", "true") 89 } 90 } 91 92 func (o *Options) configureExposedHeaders(headers http.Header) { 93 if len(o.ExposedHeaders) > 0 { 94 headers.Set("Access-Control-Expose-Headers", strings.Join(o.ExposedHeaders, ", ")) 95 } 96 } 97 98 func (o *Options) configureAllowedMethods(headers http.Header) { 99 headers.Set("Access-Control-Allow-Methods", strings.Join(o.AllowedMethods, ", ")) 100 } 101 102 func (o *Options) configureAllowedHeaders(headers http.Header, requestHeaders http.Header) { 103 if len(o.AllowedHeaders) == 0 { 104 headers.Add("Vary", "Access-Control-Request-Headers") 105 headers.Set("Access-Control-Allow-Headers", requestHeaders.Get("Access-Control-Request-Headers")) 106 } else { 107 headers.Set("Access-Control-Allow-Headers", strings.Join(o.AllowedHeaders, ", ")) 108 } 109 110 } 111 112 func (o *Options) configureMaxAge(headers http.Header) { 113 headers.Set("Access-Control-Max-Age", strconv.FormatUint(uint64(o.MaxAge.Seconds()), 10)) 114 } 115 116 // HandlePreflight configures headers for preflight requests: 117 // Allowed Methods, Allowed Headers and Max Age. 118 func (o *Options) HandlePreflight(headers http.Header, requestHeaders http.Header) { 119 o.configureAllowedMethods(headers) 120 o.configureAllowedHeaders(headers, requestHeaders) 121 o.configureMaxAge(headers) 122 } 123 124 func (o *Options) validateOrigin(requestHeaders http.Header) bool { 125 return len(o.AllowedOrigins) == 0 || 126 o.AllowedOrigins[0] == "*" || 127 slices.Contains(o.AllowedOrigins, requestHeaders.Get("Origin")) 128 }