...

Source file src/github.com/gin-contrib/cors/cors.go

Documentation: github.com/gin-contrib/cors

     1  package cors
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/gin-gonic/gin"
    10  )
    11  
    12  // Config represents all available options for the middleware.
    13  type Config struct {
    14  	AllowAllOrigins bool
    15  
    16  	// AllowOrigins is a list of origins a cross-domain request can be executed from.
    17  	// If the special "*" value is present in the list, all origins will be allowed.
    18  	// Default value is []
    19  	AllowOrigins []string
    20  
    21  	// AllowOriginFunc is a custom function to validate the origin. It takes the origin
    22  	// as an argument and returns true if allowed or false otherwise. If this option is
    23  	// set, the content of AllowOrigins is ignored.
    24  	AllowOriginFunc func(origin string) bool
    25  
    26  	// Same as AllowOriginFunc except also receives the full request context.
    27  	// This function should use the context as a read only source and not
    28  	// have any side effects on the request, such as aborting or injecting
    29  	// values on the request.
    30  	AllowOriginWithContextFunc func(c *gin.Context, origin string) bool
    31  
    32  	// AllowMethods is a list of methods the client is allowed to use with
    33  	// cross-domain requests. Default value is simple methods (GET, POST, PUT, PATCH, DELETE, HEAD, and OPTIONS)
    34  	AllowMethods []string
    35  
    36  	// AllowPrivateNetwork indicates whether the response should include allow private network header
    37  	AllowPrivateNetwork bool
    38  
    39  	// AllowHeaders is list of non simple headers the client is allowed to use with
    40  	// cross-domain requests.
    41  	AllowHeaders []string
    42  
    43  	// AllowCredentials indicates whether the request can include user credentials like
    44  	// cookies, HTTP authentication or client side SSL certificates.
    45  	AllowCredentials bool
    46  
    47  	// ExposeHeaders indicates which headers are safe to expose to the API of a CORS
    48  	// API specification
    49  	ExposeHeaders []string
    50  
    51  	// MaxAge indicates how long (with second-precision) the results of a preflight request
    52  	// can be cached
    53  	MaxAge time.Duration
    54  
    55  	// Allows to add origins like http://some-domain/*, https://api.* or http://some.*.subdomain.com
    56  	AllowWildcard bool
    57  
    58  	// Allows usage of popular browser extensions schemas
    59  	AllowBrowserExtensions bool
    60  
    61  	// Allows to add custom schema like tauri://
    62  	CustomSchemas []string
    63  
    64  	// Allows usage of WebSocket protocol
    65  	AllowWebSockets bool
    66  
    67  	// Allows usage of file:// schema (dangerous!) use it only when you 100% sure it's needed
    68  	AllowFiles bool
    69  
    70  	// Allows to pass custom OPTIONS response status code for old browsers / clients
    71  	OptionsResponseStatusCode int
    72  }
    73  
    74  // AddAllowMethods is allowed to add custom methods
    75  func (c *Config) AddAllowMethods(methods ...string) {
    76  	c.AllowMethods = append(c.AllowMethods, methods...)
    77  }
    78  
    79  // AddAllowHeaders is allowed to add custom headers
    80  func (c *Config) AddAllowHeaders(headers ...string) {
    81  	c.AllowHeaders = append(c.AllowHeaders, headers...)
    82  }
    83  
    84  // AddExposeHeaders is allowed to add custom expose headers
    85  func (c *Config) AddExposeHeaders(headers ...string) {
    86  	c.ExposeHeaders = append(c.ExposeHeaders, headers...)
    87  }
    88  
    89  func (c Config) getAllowedSchemas() []string {
    90  	allowedSchemas := DefaultSchemas
    91  	if c.AllowBrowserExtensions {
    92  		allowedSchemas = append(allowedSchemas, ExtensionSchemas...)
    93  	}
    94  	if c.AllowWebSockets {
    95  		allowedSchemas = append(allowedSchemas, WebSocketSchemas...)
    96  	}
    97  	if c.AllowFiles {
    98  		allowedSchemas = append(allowedSchemas, FileSchemas...)
    99  	}
   100  	if c.CustomSchemas != nil {
   101  		allowedSchemas = append(allowedSchemas, c.CustomSchemas...)
   102  	}
   103  	return allowedSchemas
   104  }
   105  
   106  func (c Config) validateAllowedSchemas(origin string) bool {
   107  	allowedSchemas := c.getAllowedSchemas()
   108  	for _, schema := range allowedSchemas {
   109  		if strings.HasPrefix(origin, schema) {
   110  			return true
   111  		}
   112  	}
   113  	return false
   114  }
   115  
   116  // Validate is check configuration of user defined.
   117  func (c Config) Validate() error {
   118  	hasOriginFn := c.AllowOriginFunc != nil
   119  	hasOriginFn = hasOriginFn || c.AllowOriginWithContextFunc != nil
   120  
   121  	if c.AllowAllOrigins && (hasOriginFn || len(c.AllowOrigins) > 0) {
   122  		originFields := strings.Join([]string{
   123  			"AllowOriginFunc",
   124  			"AllowOriginFuncWithContext",
   125  			"AllowOrigins",
   126  		}, " or ")
   127  		return fmt.Errorf(
   128  			"conflict settings: all origins enabled. %s is not needed",
   129  			originFields,
   130  		)
   131  	}
   132  	if !c.AllowAllOrigins && !hasOriginFn && len(c.AllowOrigins) == 0 {
   133  		return errors.New("conflict settings: all origins disabled")
   134  	}
   135  	for _, origin := range c.AllowOrigins {
   136  		if !strings.Contains(origin, "*") && !c.validateAllowedSchemas(origin) {
   137  			return errors.New("bad origin: origins must contain '*' or include " + strings.Join(c.getAllowedSchemas(), ","))
   138  		}
   139  	}
   140  	return nil
   141  }
   142  
   143  func (c Config) parseWildcardRules() [][]string {
   144  	var wRules [][]string
   145  
   146  	if !c.AllowWildcard {
   147  		return wRules
   148  	}
   149  
   150  	for _, o := range c.AllowOrigins {
   151  		if !strings.Contains(o, "*") {
   152  			continue
   153  		}
   154  
   155  		if c := strings.Count(o, "*"); c > 1 {
   156  			panic(errors.New("only one * is allowed").Error())
   157  		}
   158  
   159  		i := strings.Index(o, "*")
   160  		if i == 0 {
   161  			wRules = append(wRules, []string{"*", o[1:]})
   162  			continue
   163  		}
   164  		if i == (len(o) - 1) {
   165  			wRules = append(wRules, []string{o[:i], "*"})
   166  			continue
   167  		}
   168  
   169  		wRules = append(wRules, []string{o[:i], o[i+1:]})
   170  	}
   171  
   172  	return wRules
   173  }
   174  
   175  // DefaultConfig returns a generic default configuration mapped to localhost.
   176  func DefaultConfig() Config {
   177  	return Config{
   178  		AllowMethods:     []string{"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"},
   179  		AllowHeaders:     []string{"Origin", "Content-Length", "Content-Type"},
   180  		AllowCredentials: false,
   181  		MaxAge:           12 * time.Hour,
   182  	}
   183  }
   184  
   185  // Default returns the location middleware with default configuration.
   186  func Default() gin.HandlerFunc {
   187  	config := DefaultConfig()
   188  	config.AllowAllOrigins = true
   189  	return New(config)
   190  }
   191  
   192  // New returns the location middleware with user-defined custom configuration.
   193  func New(config Config) gin.HandlerFunc {
   194  	cors := newCors(config)
   195  	return func(c *gin.Context) {
   196  		cors.applyCors(c)
   197  	}
   198  }
   199  

View as plain text