github.com/gofiber/fiber/v2@v2.47.0/middleware/cors/cors.go (about)

     1  package cors
     2  
     3  import (
     4  	"log"
     5  	"strconv"
     6  	"strings"
     7  
     8  	"github.com/gofiber/fiber/v2"
     9  )
    10  
    11  // Config defines the config for middleware.
    12  type Config struct {
    13  	// Next defines a function to skip this middleware when returned true.
    14  	//
    15  	// Optional. Default: nil
    16  	Next func(c *fiber.Ctx) bool
    17  
    18  	// AllowOriginsFunc defines a function that will set the 'access-control-allow-origin'
    19  	// response header to the 'origin' request header when returned true.
    20  	//
    21  	// Optional. Default: nil
    22  	AllowOriginsFunc func(origin string) bool
    23  
    24  	// AllowOrigin defines a list of origins that may access the resource.
    25  	//
    26  	// Optional. Default value "*"
    27  	AllowOrigins string
    28  
    29  	// AllowMethods defines a list methods allowed when accessing the resource.
    30  	// This is used in response to a preflight request.
    31  	//
    32  	// Optional. Default value "GET,POST,HEAD,PUT,DELETE,PATCH"
    33  	AllowMethods string
    34  
    35  	// AllowHeaders defines a list of request headers that can be used when
    36  	// making the actual request. This is in response to a preflight request.
    37  	//
    38  	// Optional. Default value "".
    39  	AllowHeaders string
    40  
    41  	// AllowCredentials indicates whether or not the response to the request
    42  	// can be exposed when the credentials flag is true. When used as part of
    43  	// a response to a preflight request, this indicates whether or not the
    44  	// actual request can be made using credentials.
    45  	//
    46  	// Optional. Default value false.
    47  	AllowCredentials bool
    48  
    49  	// ExposeHeaders defines a whitelist headers that clients are allowed to
    50  	// access.
    51  	//
    52  	// Optional. Default value "".
    53  	ExposeHeaders string
    54  
    55  	// MaxAge indicates how long (in seconds) the results of a preflight request
    56  	// can be cached.
    57  	//
    58  	// Optional. Default value 0.
    59  	MaxAge int
    60  }
    61  
    62  // ConfigDefault is the default config
    63  var ConfigDefault = Config{
    64  	Next:             nil,
    65  	AllowOriginsFunc: nil,
    66  	AllowOrigins:     "*",
    67  	AllowMethods: strings.Join([]string{
    68  		fiber.MethodGet,
    69  		fiber.MethodPost,
    70  		fiber.MethodHead,
    71  		fiber.MethodPut,
    72  		fiber.MethodDelete,
    73  		fiber.MethodPatch,
    74  	}, ","),
    75  	AllowHeaders:     "",
    76  	AllowCredentials: false,
    77  	ExposeHeaders:    "",
    78  	MaxAge:           0,
    79  }
    80  
    81  // New creates a new middleware handler
    82  func New(config ...Config) fiber.Handler {
    83  	// Set default config
    84  	cfg := ConfigDefault
    85  
    86  	// Override config if provided
    87  	if len(config) > 0 {
    88  		cfg = config[0]
    89  
    90  		// Set default values
    91  		if cfg.AllowMethods == "" {
    92  			cfg.AllowMethods = ConfigDefault.AllowMethods
    93  		}
    94  		if cfg.AllowOrigins == "" {
    95  			cfg.AllowOrigins = ConfigDefault.AllowOrigins
    96  		}
    97  	}
    98  
    99  	// Warning logs if both AllowOrigins and AllowOriginsFunc are set
   100  	if cfg.AllowOrigins != ConfigDefault.AllowOrigins && cfg.AllowOriginsFunc != nil {
   101  		log.Printf("[Warning] - [CORS] Both 'AllowOrigins' and 'AllowOriginsFunc' have been defined.\n")
   102  	}
   103  
   104  	// Convert string to slice
   105  	allowOrigins := strings.Split(strings.ReplaceAll(cfg.AllowOrigins, " ", ""), ",")
   106  
   107  	// Strip white spaces
   108  	allowMethods := strings.ReplaceAll(cfg.AllowMethods, " ", "")
   109  	allowHeaders := strings.ReplaceAll(cfg.AllowHeaders, " ", "")
   110  	exposeHeaders := strings.ReplaceAll(cfg.ExposeHeaders, " ", "")
   111  
   112  	// Convert int to string
   113  	maxAge := strconv.Itoa(cfg.MaxAge)
   114  
   115  	// Return new handler
   116  	return func(c *fiber.Ctx) error {
   117  		// Don't execute middleware if Next returns true
   118  		if cfg.Next != nil && cfg.Next(c) {
   119  			return c.Next()
   120  		}
   121  
   122  		// Get origin header
   123  		origin := c.Get(fiber.HeaderOrigin)
   124  		allowOrigin := ""
   125  
   126  		// Check allowed origins
   127  		for _, o := range allowOrigins {
   128  			if o == "*" {
   129  				allowOrigin = "*"
   130  				break
   131  			}
   132  			if o == origin {
   133  				allowOrigin = o
   134  				break
   135  			}
   136  			if matchSubdomain(origin, o) {
   137  				allowOrigin = origin
   138  				break
   139  			}
   140  		}
   141  
   142  		// Run AllowOriginsFunc if the logic for
   143  		// handling the value in 'AllowOrigins' does
   144  		// not result in allowOrigin being set.
   145  		if (allowOrigin == "" || allowOrigin == ConfigDefault.AllowOrigins) && cfg.AllowOriginsFunc != nil {
   146  			if cfg.AllowOriginsFunc(origin) {
   147  				allowOrigin = origin
   148  			}
   149  		}
   150  
   151  		// Simple request
   152  		if c.Method() != fiber.MethodOptions {
   153  			c.Vary(fiber.HeaderOrigin)
   154  			c.Set(fiber.HeaderAccessControlAllowOrigin, allowOrigin)
   155  
   156  			if cfg.AllowCredentials {
   157  				c.Set(fiber.HeaderAccessControlAllowCredentials, "true")
   158  			}
   159  			if exposeHeaders != "" {
   160  				c.Set(fiber.HeaderAccessControlExposeHeaders, exposeHeaders)
   161  			}
   162  			return c.Next()
   163  		}
   164  
   165  		// Preflight request
   166  		c.Vary(fiber.HeaderOrigin)
   167  		c.Vary(fiber.HeaderAccessControlRequestMethod)
   168  		c.Vary(fiber.HeaderAccessControlRequestHeaders)
   169  		c.Set(fiber.HeaderAccessControlAllowOrigin, allowOrigin)
   170  		c.Set(fiber.HeaderAccessControlAllowMethods, allowMethods)
   171  
   172  		// Set Allow-Credentials if set to true
   173  		if cfg.AllowCredentials {
   174  			c.Set(fiber.HeaderAccessControlAllowCredentials, "true")
   175  		}
   176  
   177  		// Set Allow-Headers if not empty
   178  		if allowHeaders != "" {
   179  			c.Set(fiber.HeaderAccessControlAllowHeaders, allowHeaders)
   180  		} else {
   181  			h := c.Get(fiber.HeaderAccessControlRequestHeaders)
   182  			if h != "" {
   183  				c.Set(fiber.HeaderAccessControlAllowHeaders, h)
   184  			}
   185  		}
   186  
   187  		// Set MaxAge is set
   188  		if cfg.MaxAge > 0 {
   189  			c.Set(fiber.HeaderAccessControlMaxAge, maxAge)
   190  		}
   191  
   192  		// Send 204 No Content
   193  		return c.SendStatus(fiber.StatusNoContent)
   194  	}
   195  }