pax_global_header00006660000000000000000000000064136303471160014516gustar00rootroot0000000000000052 comment=3ce2e80966d59f18f21ca3544f57856072274179 cors-1.3.1/000077500000000000000000000000001363034711600124665ustar00rootroot00000000000000cors-1.3.1/.gitignore000066400000000000000000000002401363034711600144520ustar00rootroot00000000000000*.o *.a *.so _obj _test *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof coverage.out cors-1.3.1/.travis.yml000066400000000000000000000007561363034711600146070ustar00rootroot00000000000000language: go sudo: false go: - 1.11.x - 1.12.x - 1.13.x - 1.14.x - master matrix: fast_finish: true include: - go: 1.11.x env: GO111MODULE=on - go: 1.12.x env: GO111MODULE=on script: - go test -v -covermode=atomic -coverprofile=coverage.out after_success: - bash <(curl -s https://codecov.io/bash) notifications: webhooks: urls: - https://webhooks.gitter.im/e/acc2c57482e94b44f557 on_success: change on_failure: always on_start: false cors-1.3.1/LICENSE000066400000000000000000000020521363034711600134720ustar00rootroot00000000000000MIT License Copyright (c) 2016 Gin-Gonic Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. cors-1.3.1/README.md000066400000000000000000000043621363034711600137520ustar00rootroot00000000000000# CORS gin's middleware [![Build Status](https://travis-ci.org/gin-contrib/cors.svg)](https://travis-ci.org/gin-contrib/cors) [![codecov](https://codecov.io/gh/gin-contrib/cors/branch/master/graph/badge.svg)](https://codecov.io/gh/gin-contrib/cors) [![Go Report Card](https://goreportcard.com/badge/github.com/gin-contrib/cors)](https://goreportcard.com/report/github.com/gin-contrib/cors) [![GoDoc](https://godoc.org/github.com/gin-contrib/cors?status.svg)](https://godoc.org/github.com/gin-contrib/cors) [![Join the chat at https://gitter.im/gin-gonic/gin](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/gin-gonic/gin) Gin middleware/handler to enable CORS support. ## Usage ### Start using it Download and install it: ```sh $ go get github.com/gin-contrib/cors ``` Import it in your code: ```go import "github.com/gin-contrib/cors" ``` ### Canonical example: ```go package main import ( "time" "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" ) func main() { router := gin.Default() // CORS for https://foo.com and https://github.com origins, allowing: // - PUT and PATCH methods // - Origin header // - Credentials share // - Preflight requests cached for 12 hours router.Use(cors.New(cors.Config{ AllowOrigins: []string{"https://foo.com"}, AllowMethods: []string{"PUT", "PATCH"}, AllowHeaders: []string{"Origin"}, ExposeHeaders: []string{"Content-Length"}, AllowCredentials: true, AllowOriginFunc: func(origin string) bool { return origin == "https://github.com" }, MaxAge: 12 * time.Hour, })) router.Run() } ``` ### Using DefaultConfig as start point ```go func main() { router := gin.Default() // - No origin allowed by default // - GET,POST, PUT, HEAD methods // - Credentials share disabled // - Preflight requests cached for 12 hours config := cors.DefaultConfig() config.AllowOrigins = []string{"http://google.com"} // config.AllowOrigins == []string{"http://google.com", "http://facebook.com"} router.Use(cors.New(config)) router.Run() } ``` ### Default() allows all origins ```go func main() { router := gin.Default() // same as // config := cors.DefaultConfig() // config.AllowAllOrigins = true // router.Use(cors.New(config)) router.Use(cors.Default()) router.Run() } ``` cors-1.3.1/config.go000066400000000000000000000054631363034711600142720ustar00rootroot00000000000000package cors import ( "net/http" "strings" "github.com/gin-gonic/gin" ) type cors struct { allowAllOrigins bool allowCredentials bool allowOriginFunc func(string) bool allowOrigins []string exposeHeaders []string normalHeaders http.Header preflightHeaders http.Header wildcardOrigins [][]string } var ( DefaultSchemas = []string{ "http://", "https://", } ExtensionSchemas = []string{ "chrome-extension://", "safari-extension://", "moz-extension://", "ms-browser-extension://", } FileSchemas = []string{ "file://", } WebSocketSchemas = []string{ "ws://", "wss://", } ) func newCors(config Config) *cors { if err := config.Validate(); err != nil { panic(err.Error()) } return &cors{ allowOriginFunc: config.AllowOriginFunc, allowAllOrigins: config.AllowAllOrigins, allowCredentials: config.AllowCredentials, allowOrigins: normalize(config.AllowOrigins), normalHeaders: generateNormalHeaders(config), preflightHeaders: generatePreflightHeaders(config), wildcardOrigins: config.parseWildcardRules(), } } func (cors *cors) applyCors(c *gin.Context) { origin := c.Request.Header.Get("Origin") if len(origin) == 0 { // request is not a CORS request return } host := c.Request.Host if origin == "http://"+host || origin == "https://"+host { // request is not a CORS request but have origin header. // for example, use fetch api return } if !cors.validateOrigin(origin) { c.AbortWithStatus(http.StatusForbidden) return } if c.Request.Method == "OPTIONS" { cors.handlePreflight(c) defer c.AbortWithStatus(http.StatusNoContent) // Using 204 is better than 200 when the request status is OPTIONS } else { cors.handleNormal(c) } if !cors.allowAllOrigins { c.Header("Access-Control-Allow-Origin", origin) } } func (cors *cors) validateWildcardOrigin(origin string) bool { for _, w := range cors.wildcardOrigins { if w[0] == "*" && strings.HasSuffix(origin, w[1]) { return true } if w[1] == "*" && strings.HasPrefix(origin, w[0]) { return true } if strings.HasPrefix(origin, w[0]) && strings.HasSuffix(origin, w[1]) { return true } } return false } func (cors *cors) validateOrigin(origin string) bool { if cors.allowAllOrigins { return true } for _, value := range cors.allowOrigins { if value == origin { return true } } if len(cors.wildcardOrigins) > 0 && cors.validateWildcardOrigin(origin) { return true } if cors.allowOriginFunc != nil { return cors.allowOriginFunc(origin) } return false } func (cors *cors) handlePreflight(c *gin.Context) { header := c.Writer.Header() for key, value := range cors.preflightHeaders { header[key] = value } } func (cors *cors) handleNormal(c *gin.Context) { header := c.Writer.Header() for key, value := range cors.normalHeaders { header[key] = value } } cors-1.3.1/cors.go000066400000000000000000000114761363034711600137740ustar00rootroot00000000000000package cors import ( "errors" "strings" "time" "github.com/gin-gonic/gin" ) // Config represents all available options for the middleware. type Config struct { AllowAllOrigins bool // AllowOrigins 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. // Default value is [] AllowOrigins []string // AllowOriginFunc is a custom function to validate the origin. It take the origin // as argument and returns true if allowed or false otherwise. If this option is // set, the content of AllowOrigins is ignored. AllowOriginFunc func(origin string) bool // AllowMethods is a list of methods the client is allowed to use with // cross-domain requests. Default value is simple methods (GET and POST) AllowMethods []string // AllowHeaders is list of non simple headers the client is allowed to use with // cross-domain requests. AllowHeaders []string // AllowCredentials indicates whether the request can include user credentials like // cookies, HTTP authentication or client side SSL certificates. AllowCredentials bool // ExposedHeaders indicates which headers are safe to expose to the API of a CORS // API specification ExposeHeaders []string // MaxAge indicates how long (in seconds) the results of a preflight request // can be cached MaxAge time.Duration // Allows to add origins like http://some-domain/*, https://api.* or http://some.*.subdomain.com AllowWildcard bool // Allows usage of popular browser extensions schemas AllowBrowserExtensions bool // Allows usage of WebSocket protocol AllowWebSockets bool // Allows usage of file:// schema (dangerous!) use it only when you 100% sure it's needed AllowFiles bool } // AddAllowMethods is allowed to add custom methods func (c *Config) AddAllowMethods(methods ...string) { c.AllowMethods = append(c.AllowMethods, methods...) } // AddAllowHeaders is allowed to add custom headers func (c *Config) AddAllowHeaders(headers ...string) { c.AllowHeaders = append(c.AllowHeaders, headers...) } // AddExposeHeaders is allowed to add custom expose headers func (c *Config) AddExposeHeaders(headers ...string) { c.ExposeHeaders = append(c.ExposeHeaders, headers...) } func (c Config) getAllowedSchemas() []string { allowedSchemas := DefaultSchemas if c.AllowBrowserExtensions { allowedSchemas = append(allowedSchemas, ExtensionSchemas...) } if c.AllowWebSockets { allowedSchemas = append(allowedSchemas, WebSocketSchemas...) } if c.AllowFiles { allowedSchemas = append(allowedSchemas, FileSchemas...) } return allowedSchemas } func (c Config) validateAllowedSchemas(origin string) bool { allowedSchemas := c.getAllowedSchemas() for _, schema := range allowedSchemas { if strings.HasPrefix(origin, schema) { return true } } return false } // Validate is check configuration of user defined. func (c *Config) Validate() error { if c.AllowAllOrigins && (c.AllowOriginFunc != nil || len(c.AllowOrigins) > 0) { return errors.New("conflict settings: all origins are allowed. AllowOriginFunc or AllowOrigins is not needed") } if !c.AllowAllOrigins && c.AllowOriginFunc == nil && len(c.AllowOrigins) == 0 { return errors.New("conflict settings: all origins disabled") } for _, origin := range c.AllowOrigins { if origin == "*" { c.AllowAllOrigins = true return nil } else if !strings.Contains(origin, "*") && !c.validateAllowedSchemas(origin) { return errors.New("bad origin: origins must contain '*' or include " + strings.Join(c.getAllowedSchemas(), ",")) } } return nil } func (c Config) parseWildcardRules() [][]string { var wRules [][]string if !c.AllowWildcard { return wRules } for _, o := range c.AllowOrigins { if !strings.Contains(o, "*") { continue } if c := strings.Count(o, "*"); c > 1 { panic(errors.New("only one * is allowed").Error()) } i := strings.Index(o, "*") if i == 0 { wRules = append(wRules, []string{"*", o[1:]}) continue } if i == (len(o) - 1) { wRules = append(wRules, []string{o[:i-1], "*"}) continue } wRules = append(wRules, []string{o[:i], o[i+1:]}) } return wRules } // DefaultConfig returns a generic default configuration mapped to localhost. func DefaultConfig() Config { return Config{ AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"}, AllowHeaders: []string{"Origin", "Content-Length", "Content-Type"}, AllowCredentials: false, MaxAge: 12 * time.Hour, } } // Default returns the location middleware with default configuration. func Default() gin.HandlerFunc { config := DefaultConfig() config.AllowAllOrigins = true return New(config) } // New returns the location middleware with user-defined custom configuration. func New(config Config) gin.HandlerFunc { cors := newCors(config) return func(c *gin.Context) { cors.applyCors(c) } } cors-1.3.1/cors_test.go000066400000000000000000000344561363034711600150360ustar00rootroot00000000000000package cors import ( "net/http" "net/http/httptest" "strings" "testing" "time" "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" ) func init() { gin.SetMode(gin.TestMode) } func newTestRouter(config Config) *gin.Engine { router := gin.New() router.Use(New(config)) router.GET("/", func(c *gin.Context) { c.String(http.StatusOK, "get") }) router.POST("/", func(c *gin.Context) { c.String(http.StatusOK, "post") }) router.PATCH("/", func(c *gin.Context) { c.String(http.StatusOK, "patch") }) return router } func performRequest(r http.Handler, method, origin string) *httptest.ResponseRecorder { return performRequestWithHeaders(r, method, origin, http.Header{}) } func performRequestWithHeaders(r http.Handler, method, origin string, header http.Header) *httptest.ResponseRecorder { req, _ := http.NewRequest(method, "/", nil) // From go/net/http/request.go: // For incoming requests, the Host header is promoted to the // Request.Host field and removed from the Header map. req.Host = header.Get("Host") header.Del("Host") if len(origin) > 0 { header.Set("Origin", origin) } req.Header = header w := httptest.NewRecorder() r.ServeHTTP(w, req) return w } func TestConfigAddAllow(t *testing.T) { config := Config{} config.AddAllowMethods("POST") config.AddAllowMethods("GET", "PUT") config.AddExposeHeaders() config.AddAllowHeaders("Some", " cool") config.AddAllowHeaders("header") config.AddExposeHeaders() config.AddExposeHeaders() config.AddExposeHeaders("exposed", "header") config.AddExposeHeaders("hey") assert.Equal(t, config.AllowMethods, []string{"POST", "GET", "PUT"}) assert.Equal(t, config.AllowHeaders, []string{"Some", " cool", "header"}) assert.Equal(t, config.ExposeHeaders, []string{"exposed", "header", "hey"}) } func TestBadConfig(t *testing.T) { assert.Panics(t, func() { New(Config{}) }) assert.Panics(t, func() { New(Config{ AllowAllOrigins: true, AllowOrigins: []string{"http://google.com"}, }) }) assert.Panics(t, func() { New(Config{ AllowAllOrigins: true, AllowOriginFunc: func(origin string) bool { return false }, }) }) assert.Panics(t, func() { New(Config{ AllowOrigins: []string{"google.com"}, }) }) } func TestNormalize(t *testing.T) { values := normalize([]string{ "http-Access ", "Post", "POST", " poSt ", "HTTP-Access", "", }) assert.Equal(t, values, []string{"http-access", "post", ""}) values = normalize(nil) assert.Nil(t, values) values = normalize([]string{}) assert.Equal(t, values, []string{}) } func TestConvert(t *testing.T) { methods := []string{"Get", "GET", "get"} headers := []string{"X-CSRF-TOKEN", "X-CSRF-Token", "x-csrf-token"} assert.Equal(t, []string{"GET", "GET", "GET"}, convert(methods, strings.ToUpper)) assert.Equal(t, []string{"X-Csrf-Token", "X-Csrf-Token", "X-Csrf-Token"}, convert(headers, http.CanonicalHeaderKey)) } func TestGenerateNormalHeaders_AllowAllOrigins(t *testing.T) { header := generateNormalHeaders(Config{ AllowAllOrigins: false, }) assert.Equal(t, header.Get("Access-Control-Allow-Origin"), "") assert.Equal(t, header.Get("Vary"), "Origin") assert.Len(t, header, 1) header = generateNormalHeaders(Config{ AllowAllOrigins: true, }) assert.Equal(t, header.Get("Access-Control-Allow-Origin"), "*") assert.Equal(t, header.Get("Vary"), "") assert.Len(t, header, 1) } func TestGenerateNormalHeaders_AllowCredentials(t *testing.T) { header := generateNormalHeaders(Config{ AllowCredentials: true, }) assert.Equal(t, header.Get("Access-Control-Allow-Credentials"), "true") assert.Equal(t, header.Get("Vary"), "Origin") assert.Len(t, header, 2) } func TestGenerateNormalHeaders_ExposedHeaders(t *testing.T) { header := generateNormalHeaders(Config{ ExposeHeaders: []string{"X-user", "xPassword"}, }) assert.Equal(t, header.Get("Access-Control-Expose-Headers"), "X-User,Xpassword") assert.Equal(t, header.Get("Vary"), "Origin") assert.Len(t, header, 2) } func TestGeneratePreflightHeaders(t *testing.T) { header := generatePreflightHeaders(Config{ AllowAllOrigins: false, }) assert.Equal(t, header.Get("Access-Control-Allow-Origin"), "") assert.Equal(t, header.Get("Vary"), "Origin") assert.Len(t, header, 1) header = generateNormalHeaders(Config{ AllowAllOrigins: true, }) assert.Equal(t, header.Get("Access-Control-Allow-Origin"), "*") assert.Equal(t, header.Get("Vary"), "") assert.Len(t, header, 1) } func TestGeneratePreflightHeaders_AllowCredentials(t *testing.T) { header := generatePreflightHeaders(Config{ AllowCredentials: true, }) assert.Equal(t, header.Get("Access-Control-Allow-Credentials"), "true") assert.Equal(t, header.Get("Vary"), "Origin") assert.Len(t, header, 2) } func TestGeneratePreflightHeaders_AllowMethods(t *testing.T) { header := generatePreflightHeaders(Config{ AllowMethods: []string{"GET ", "post", "PUT", " put "}, }) assert.Equal(t, header.Get("Access-Control-Allow-Methods"), "GET,POST,PUT") assert.Equal(t, header.Get("Vary"), "Origin") assert.Len(t, header, 2) } func TestGeneratePreflightHeaders_AllowHeaders(t *testing.T) { header := generatePreflightHeaders(Config{ AllowHeaders: []string{"X-user", "Content-Type"}, }) assert.Equal(t, header.Get("Access-Control-Allow-Headers"), "X-User,Content-Type") assert.Equal(t, header.Get("Vary"), "Origin") assert.Len(t, header, 2) } func TestGeneratePreflightHeaders_MaxAge(t *testing.T) { header := generatePreflightHeaders(Config{ MaxAge: 12 * time.Hour, }) assert.Equal(t, header.Get("Access-Control-Max-Age"), "43200") // 12*60*60 assert.Equal(t, header.Get("Vary"), "Origin") assert.Len(t, header, 2) } func TestValidateOrigin(t *testing.T) { cors := newCors(Config{ AllowAllOrigins: true, }) assert.True(t, cors.validateOrigin("http://google.com")) assert.True(t, cors.validateOrigin("https://google.com")) assert.True(t, cors.validateOrigin("example.com")) assert.True(t, cors.validateOrigin("chrome-extension://random-extension-id")) cors = newCors(Config{ AllowOrigins: []string{"https://google.com", "https://github.com"}, AllowOriginFunc: func(origin string) bool { return (origin == "http://news.ycombinator.com") }, AllowBrowserExtensions: true, }) assert.False(t, cors.validateOrigin("http://google.com")) assert.True(t, cors.validateOrigin("https://google.com")) assert.True(t, cors.validateOrigin("https://github.com")) assert.True(t, cors.validateOrigin("http://news.ycombinator.com")) assert.False(t, cors.validateOrigin("http://example.com")) assert.False(t, cors.validateOrigin("google.com")) assert.False(t, cors.validateOrigin("chrome-extension://random-extension-id")) cors = newCors(Config{ AllowOrigins: []string{"https://google.com", "https://github.com"}, }) assert.False(t, cors.validateOrigin("chrome-extension://random-extension-id")) assert.False(t, cors.validateOrigin("file://some-dangerous-file.js")) assert.False(t, cors.validateOrigin("wss://socket-connection")) cors = newCors(Config{ AllowOrigins: []string{"chrome-extension://*", "safari-extension://my-extension-*-app", "*.some-domain.com"}, AllowBrowserExtensions: true, AllowWildcard: true, }) assert.True(t, cors.validateOrigin("chrome-extension://random-extension-id")) assert.True(t, cors.validateOrigin("chrome-extension://another-one")) assert.True(t, cors.validateOrigin("safari-extension://my-extension-one-app")) assert.True(t, cors.validateOrigin("safari-extension://my-extension-two-app")) assert.False(t, cors.validateOrigin("moz-extension://ext-id-we-not-allow")) assert.True(t, cors.validateOrigin("http://api.some-domain.com")) assert.False(t, cors.validateOrigin("http://api.another-domain.com")) cors = newCors(Config{ AllowOrigins: []string{"file://safe-file.js", "wss://some-session-layer-connection"}, AllowFiles: true, AllowWebSockets: true, }) assert.True(t, cors.validateOrigin("file://safe-file.js")) assert.False(t, cors.validateOrigin("file://some-dangerous-file.js")) assert.True(t, cors.validateOrigin("wss://some-session-layer-connection")) assert.False(t, cors.validateOrigin("ws://not-what-we-expected")) cors = newCors(Config{ AllowOrigins: []string{"*"}, }) assert.True(t, cors.validateOrigin("http://google.com")) assert.True(t, cors.validateOrigin("https://google.com")) assert.True(t, cors.validateOrigin("example.com")) assert.True(t, cors.validateOrigin("chrome-extension://random-extension-id")) } func TestPassesAllowOrigins(t *testing.T) { router := newTestRouter(Config{ AllowOrigins: []string{"http://google.com"}, AllowMethods: []string{" GeT ", "get", "post", "PUT ", "Head", "POST"}, AllowHeaders: []string{"Content-type", "timeStamp "}, ExposeHeaders: []string{"Data", "x-User"}, AllowCredentials: false, MaxAge: 12 * time.Hour, AllowOriginFunc: func(origin string) bool { return origin == "http://github.com" }, }) // no CORS request, origin == "" w := performRequest(router, "GET", "") assert.Equal(t, "get", w.Body.String()) assert.Empty(t, w.Header().Get("Access-Control-Allow-Origin")) assert.Empty(t, w.Header().Get("Access-Control-Allow-Credentials")) assert.Empty(t, w.Header().Get("Access-Control-Expose-Headers")) // no CORS request, origin == host h := http.Header{} h.Set("Host", "facebook.com") w = performRequestWithHeaders(router, "GET", "http://facebook.com", h) assert.Equal(t, "get", w.Body.String()) assert.Empty(t, w.Header().Get("Access-Control-Allow-Origin")) assert.Empty(t, w.Header().Get("Access-Control-Allow-Credentials")) assert.Empty(t, w.Header().Get("Access-Control-Expose-Headers")) // allowed CORS request w = performRequest(router, "GET", "http://google.com") assert.Equal(t, "get", w.Body.String()) assert.Equal(t, "http://google.com", w.Header().Get("Access-Control-Allow-Origin")) assert.Equal(t, "", w.Header().Get("Access-Control-Allow-Credentials")) assert.Equal(t, "Data,X-User", w.Header().Get("Access-Control-Expose-Headers")) w = performRequest(router, "GET", "http://github.com") assert.Equal(t, "get", w.Body.String()) assert.Equal(t, "http://github.com", w.Header().Get("Access-Control-Allow-Origin")) assert.Equal(t, "", w.Header().Get("Access-Control-Allow-Credentials")) assert.Equal(t, "Data,X-User", w.Header().Get("Access-Control-Expose-Headers")) // deny CORS request w = performRequest(router, "GET", "https://google.com") assert.Equal(t, http.StatusForbidden, w.Code) assert.Empty(t, w.Header().Get("Access-Control-Allow-Origin")) assert.Empty(t, w.Header().Get("Access-Control-Allow-Credentials")) assert.Empty(t, w.Header().Get("Access-Control-Expose-Headers")) // allowed CORS prefligh request w = performRequest(router, "OPTIONS", "http://github.com") assert.Equal(t, http.StatusNoContent, w.Code) assert.Equal(t, "http://github.com", w.Header().Get("Access-Control-Allow-Origin")) assert.Equal(t, "", w.Header().Get("Access-Control-Allow-Credentials")) assert.Equal(t, "GET,POST,PUT,HEAD", w.Header().Get("Access-Control-Allow-Methods")) assert.Equal(t, "Content-Type,Timestamp", w.Header().Get("Access-Control-Allow-Headers")) assert.Equal(t, "43200", w.Header().Get("Access-Control-Max-Age")) // deny CORS prefligh request w = performRequest(router, "OPTIONS", "http://example.com") assert.Equal(t, http.StatusForbidden, w.Code) assert.Empty(t, w.Header().Get("Access-Control-Allow-Origin")) assert.Empty(t, w.Header().Get("Access-Control-Allow-Credentials")) assert.Empty(t, w.Header().Get("Access-Control-Allow-Methods")) assert.Empty(t, w.Header().Get("Access-Control-Allow-Headers")) assert.Empty(t, w.Header().Get("Access-Control-Max-Age")) } func TestPassesAllowAllOrigins(t *testing.T) { router := newTestRouter(Config{ AllowAllOrigins: true, AllowMethods: []string{" Patch ", "get", "post", "POST"}, AllowHeaders: []string{"Content-type", " testheader "}, ExposeHeaders: []string{"Data2", "x-User2"}, AllowCredentials: false, MaxAge: 10 * time.Hour, }) // no CORS request, origin == "" w := performRequest(router, "GET", "") assert.Equal(t, "get", w.Body.String()) assert.Empty(t, w.Header().Get("Access-Control-Allow-Origin")) assert.Empty(t, w.Header().Get("Access-Control-Allow-Credentials")) assert.Empty(t, w.Header().Get("Access-Control-Expose-Headers")) assert.Empty(t, w.Header().Get("Access-Control-Allow-Credentials")) // allowed CORS request w = performRequest(router, "POST", "example.com") assert.Equal(t, "post", w.Body.String()) assert.Equal(t, "*", w.Header().Get("Access-Control-Allow-Origin")) assert.Equal(t, "Data2,X-User2", w.Header().Get("Access-Control-Expose-Headers")) assert.Empty(t, w.Header().Get("Access-Control-Allow-Credentials")) assert.Equal(t, "*", w.Header().Get("Access-Control-Allow-Origin")) // allowed CORS prefligh request w = performRequest(router, "OPTIONS", "https://facebook.com") assert.Equal(t, http.StatusNoContent, w.Code) assert.Equal(t, "*", w.Header().Get("Access-Control-Allow-Origin")) assert.Equal(t, "PATCH,GET,POST", w.Header().Get("Access-Control-Allow-Methods")) assert.Equal(t, "Content-Type,Testheader", w.Header().Get("Access-Control-Allow-Headers")) assert.Equal(t, "36000", w.Header().Get("Access-Control-Max-Age")) assert.Empty(t, w.Header().Get("Access-Control-Allow-Credentials")) } func TestWildcard(t *testing.T) { router := newTestRouter(Config{ AllowOrigins: []string{"https://*.github.com", "https://api.*", "http://*", "https://facebook.com", "*.golang.org"}, AllowMethods: []string{"GET"}, AllowWildcard: true, }) w := performRequest(router, "GET", "https://gist.github.com") assert.Equal(t, 200, w.Code) w = performRequest(router, "GET", "https://api.github.com/v1/users") assert.Equal(t, 200, w.Code) w = performRequest(router, "GET", "https://giphy.com/") assert.Equal(t, 403, w.Code) w = performRequest(router, "GET", "http://hard-to-find-http-example.com") assert.Equal(t, 200, w.Code) w = performRequest(router, "GET", "https://facebook.com") assert.Equal(t, 200, w.Code) w = performRequest(router, "GET", "https://something.golang.org") assert.Equal(t, 200, w.Code) w = performRequest(router, "GET", "https://something.go.org") assert.Equal(t, 403, w.Code) router = newTestRouter(Config{ AllowOrigins: []string{"https://github.com", "https://facebook.com"}, AllowMethods: []string{"GET"}, }) w = performRequest(router, "GET", "https://gist.github.com") assert.Equal(t, 403, w.Code) w = performRequest(router, "GET", "https://github.com") assert.Equal(t, 200, w.Code) } cors-1.3.1/examples/000077500000000000000000000000001363034711600143045ustar00rootroot00000000000000cors-1.3.1/examples/example.go000066400000000000000000000012771363034711600162750ustar00rootroot00000000000000package main import ( "time" "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" ) func main() { router := gin.Default() // CORS for https://foo.com and https://github.com origins, allowing: // - PUT and PATCH methods // - Origin header // - Credentials share // - Preflight requests cached for 12 hours router.Use(cors.New(cors.Config{ AllowOrigins: []string{"https://foo.com"}, AllowMethods: []string{"PUT", "PATCH"}, AllowHeaders: []string{"Origin"}, ExposeHeaders: []string{"Content-Length"}, AllowCredentials: true, AllowOriginFunc: func(origin string) bool { return origin == "https://github.com" }, MaxAge: 12 * time.Hour, })) router.Run() } cors-1.3.1/go.mod000066400000000000000000000003511363034711600135730ustar00rootroot00000000000000module github.com/gin-contrib/cors go 1.13 require ( github.com/gin-gonic/gin v1.5.0 github.com/kr/pretty v0.1.0 // indirect github.com/stretchr/testify v1.4.0 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect ) cors-1.3.1/go.sum000066400000000000000000000111571363034711600136260ustar00rootroot00000000000000github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.5.0 h1:fi+bqFAx/oLK54somfCtEZs9HeH1LHVoEPUgARpTqyc= github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do= github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc= github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rmGrCjJ8eAeUP/K/EKx4DM= github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8= github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v9 v9.29.1 h1:SvGtYmN60a5CVKTOzMSyfzWDeZRxRuGvRQyEAKbw1xc= gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= cors-1.3.1/utils.go000066400000000000000000000044041363034711600141570ustar00rootroot00000000000000package cors import ( "net/http" "strconv" "strings" "time" ) type converter func(string) string func generateNormalHeaders(c Config) http.Header { headers := make(http.Header) if c.AllowCredentials { headers.Set("Access-Control-Allow-Credentials", "true") } if len(c.ExposeHeaders) > 0 { exposeHeaders := convert(normalize(c.ExposeHeaders), http.CanonicalHeaderKey) headers.Set("Access-Control-Expose-Headers", strings.Join(exposeHeaders, ",")) } if c.AllowAllOrigins { headers.Set("Access-Control-Allow-Origin", "*") } else { headers.Set("Vary", "Origin") } return headers } func generatePreflightHeaders(c Config) http.Header { headers := make(http.Header) if c.AllowCredentials { headers.Set("Access-Control-Allow-Credentials", "true") } if len(c.AllowMethods) > 0 { allowMethods := convert(normalize(c.AllowMethods), strings.ToUpper) value := strings.Join(allowMethods, ",") headers.Set("Access-Control-Allow-Methods", value) } if len(c.AllowHeaders) > 0 { allowHeaders := convert(normalize(c.AllowHeaders), http.CanonicalHeaderKey) value := strings.Join(allowHeaders, ",") headers.Set("Access-Control-Allow-Headers", value) } if c.MaxAge > time.Duration(0) { value := strconv.FormatInt(int64(c.MaxAge/time.Second), 10) headers.Set("Access-Control-Max-Age", value) } if c.AllowAllOrigins { headers.Set("Access-Control-Allow-Origin", "*") } else { // Always set Vary headers // see https://github.com/rs/cors/issues/10, // https://github.com/rs/cors/commit/dbdca4d95feaa7511a46e6f1efb3b3aa505bc43f#commitcomment-12352001 headers.Add("Vary", "Origin") headers.Add("Vary", "Access-Control-Request-Method") headers.Add("Vary", "Access-Control-Request-Headers") } return headers } func normalize(values []string) []string { if values == nil { return nil } distinctMap := make(map[string]bool, len(values)) normalized := make([]string, 0, len(values)) for _, value := range values { value = strings.TrimSpace(value) value = strings.ToLower(value) if _, seen := distinctMap[value]; !seen { normalized = append(normalized, value) distinctMap[value] = true } } return normalized } func convert(s []string, c converter) []string { var out []string for _, i := range s { out = append(out, c(i)) } return out }