Feature/cors (#183)

* add cors internally #160

* revendor
This commit is contained in:
Umputun
2018-08-02 19:26:34 -05:00
committed by GitHub
parent 2a7d23c2ca
commit e79b95de0c
5 changed files with 605 additions and 12 deletions
+123 -12
View File
@@ -2,218 +2,287 @@
[[projects]]
digest = "1:180876db3ec295bb9f0babec5ca926fe9f2036b747b7c5bfcd13b333023e7cfd"
name = "cloud.google.com/go"
packages = ["compute/metadata"]
pruneopts = "UT"
revision = "767c40d6a2e058483c25fa193e963a22da17236d"
version = "v0.18.0"
[[projects]]
digest = "1:bff7b2530f02b143623e260c11df5cbf34e0faeaca6aa001a8be31f333518ca9"
name = "github.com/PuerkitoBio/goquery"
packages = ["."]
pruneopts = "UT"
revision = "a86ea073017a6beddef78c8659e7224e8ca634b0"
version = "v1.4.0"
[[projects]]
digest = "1:66b3310cf22cdc96c35ef84ede4f7b9b370971c4025f394c89a2638729653b11"
name = "github.com/andybalholm/cascadia"
packages = ["."]
pruneopts = "UT"
revision = "901648c87902174f774fac311d7f176f8647bdaa"
version = "v1.0.0"
[[projects]]
digest = "1:c28625428387b63dd7154eb857f51e700465cfbf7c06f619e71f2da33cefe47e"
name = "github.com/coreos/bbolt"
packages = ["."]
pruneopts = "UT"
revision = "583e8937c61f1af6513608ccc75c97b6abdf4ff9"
version = "v1.3.0"
[[projects]]
digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39"
name = "github.com/davecgh/go-spew"
packages = ["spew"]
pruneopts = "UT"
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
version = "v1.1.0"
[[projects]]
digest = "1:76dc72490af7174349349838f2fe118996381b31ea83243812a97e5a0fd5ed55"
name = "github.com/dgrijalva/jwt-go"
packages = ["."]
pruneopts = "UT"
revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e"
version = "v3.2.0"
[[projects]]
digest = "1:82c6357bc57f8417f993d490f6c07a9f0b5682ac68b1a64b93a189dece7c5bf5"
name = "github.com/didip/tollbooth"
packages = [
".",
"errors",
"libstring",
"limiter"
"limiter",
]
pruneopts = "UT"
revision = "c95eaa3ddc98f635a91e218b48727fb2e06613ea"
version = "v4.0.0"
[[projects]]
branch = "master"
digest = "1:60fb125752a234a0a43bfc281bfdd9726fd1071a13f66bb35ec4b8e7ed1ef642"
name = "github.com/didip/tollbooth_chi"
packages = ["."]
pruneopts = "UT"
revision = "6ab5f3083f3d925e1944d58cdaebf43bbbff9238"
[[projects]]
branch = "master"
digest = "1:4b08116de0de75c041bb341686f0b139930f26cb84dfdf7641d435548114181d"
name = "github.com/globalsign/mgo"
packages = [
".",
"bson",
"internal/json",
"internal/sasl",
"internal/scram"
"internal/scram",
]
pruneopts = "UT"
revision = "113d3961e7311526535a1ef7042196563d442761"
version = "r2018.06.15"
[[projects]]
digest = "1:4eda9f7bf70f5145b3b9ed3f18ac93e9b1a0e38906eb69e526380c34861e2b07"
name = "github.com/go-chi/chi"
packages = [
".",
"middleware"
"middleware",
]
pruneopts = "UT"
revision = "e83ac2304db3c50cf03d96a2fcd39009d458bc35"
version = "v3.3.2"
[[projects]]
digest = "1:dfa416a1bb8139f30832543340f972f65c0db9932034cb6a1b42c5ac615a3fb8"
name = "github.com/go-chi/cors"
packages = ["."]
pruneopts = "UT"
revision = "dba6525398619dead495962a916728e7ee2ca322"
version = "v1.0.0"
[[projects]]
digest = "1:75f324f9a6b76bca2fdd087ba169de30bc28a95a7139a6cefd5a9ac7582f6dab"
name = "github.com/go-chi/render"
packages = ["."]
pruneopts = "UT"
revision = "9f855fadd4b8cde7773f9ef51f6b2705af239519"
version = "v1.0.0"
[[projects]]
digest = "1:1212e114344a5cdcc834ea69e19d456eef230f9784659080fee67e02ba2cb574"
name = "github.com/go-pkgz/mongo"
packages = ["."]
pruneopts = "UT"
revision = "9a09a574c336c6ae2338a65bbebed2baab2a713c"
version = "v1.0.0"
[[projects]]
digest = "1:0f24c72d7e9bcb682b907be0461ac552973cd4b3f1b60b04b725f6d74a3e59e7"
name = "github.com/go-pkgz/repeater"
packages = [
".",
"strategy"
"strategy",
]
pruneopts = "UT"
revision = "f2a67dcf050cab24d57132a7d8b45553ceab817b"
version = "v1.0.0"
[[projects]]
digest = "1:ffc060c551980d37ee9e428ef528ee2813137249ccebb0bfc412ef83071cac91"
name = "github.com/golang/protobuf"
packages = ["proto"]
pruneopts = "UT"
revision = "925541529c1fa6821df4e44ce2723319eb2be768"
version = "v1.0.0"
[[projects]]
digest = "1:8f8811f9be822914c3a25c6a071e93beb4c805d7b026cbf298bc577bc1cc945b"
name = "github.com/google/uuid"
packages = ["."]
pruneopts = "UT"
revision = "064e2069ce9c359c118179501254f67d7d37ba24"
version = "0.2"
[[projects]]
digest = "1:2b418e5e28a68ccab236a22f344140cebab2d90c3a4a3f5593ecbb82cfe0e5ce"
name = "github.com/gorilla/feeds"
packages = ["."]
pruneopts = "UT"
revision = "6edcbcd2d57fd0bbd7f39947a593ed0c06648388"
version = "v1.1.0"
[[projects]]
branch = "master"
digest = "1:07671f8997086ed115824d1974507d2b147d1e0463675ea5dbf3be89b1c2c563"
name = "github.com/hashicorp/errwrap"
packages = ["."]
pruneopts = "UT"
revision = "7554cd9344cec97297fa6649b055a8c98c2a1e55"
[[projects]]
branch = "master"
digest = "1:e5048c5da80697be2fcdecc944e29d2999e01fd7f48b643168443209779f3463"
name = "github.com/hashicorp/go-multierror"
packages = ["."]
pruneopts = "UT"
revision = "b7773ae218740a7be65057fc60b366a49b538a44"
[[projects]]
branch = "master"
digest = "1:cf296baa185baae04a9a7004efee8511d08e2f5f51d4cbe5375da89722d681db"
name = "github.com/hashicorp/golang-lru"
packages = [
".",
"simplelru"
"simplelru",
]
pruneopts = "UT"
revision = "0fb14efe8c47ae851c0034ed7a448854d3d34cf3"
[[projects]]
branch = "master"
digest = "1:ce3f7860fd68bd2dd4c3735e2aed8c9de7c7d05bd6ad7d97a6bedcf4fe7b84fb"
name = "github.com/hashicorp/logutils"
packages = ["."]
pruneopts = "UT"
revision = "0dc08b1671f34c4250ce212759ebd880f743d883"
[[projects]]
branch = "master"
digest = "1:3217800110ab50cd0e0784307be46b5344c0c103dbd15a16d0994ae4abdc96ab"
name = "github.com/jessevdk/go-flags"
packages = ["."]
pruneopts = "UT"
revision = "1c38ed7ad0cc3d9e66649ac398c30e45f395c4eb"
[[projects]]
branch = "master"
digest = "1:e83a8cf54ecc5c4efdbc88aa914578773d4d6897470b698ae315b5734081e8ed"
name = "github.com/microcosm-cc/bluemonday"
packages = ["."]
pruneopts = "UT"
revision = "542fd4642604d0d0c26112396ce5b1a9d01eee0b"
[[projects]]
branch = "master"
digest = "1:ea014b8bb16b0decc3393baeafc3b19815bcaf92329fe643eef5c0aa89bd3291"
name = "github.com/nullrocks/identicon"
packages = ["."]
pruneopts = "UT"
revision = "7875f45b0022edded6377e40639d8aa620193a62"
[[projects]]
digest = "1:808cdddf087fb64baeae67b8dfaee2069034d9704923a3cb8bd96a995421a625"
name = "github.com/patrickmn/go-cache"
packages = ["."]
pruneopts = "UT"
revision = "a3647f8e31d79543b2d0f0ae2fe5c379d72cedc0"
version = "v2.1.0"
[[projects]]
digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747"
name = "github.com/pkg/errors"
packages = ["."]
pruneopts = "UT"
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
version = "v0.8.0"
[[projects]]
digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe"
name = "github.com/pmezard/go-difflib"
packages = ["difflib"]
pruneopts = "UT"
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
version = "v1.0.0"
[[projects]]
branch = "master"
digest = "1:def689e73e9252f6f7fe66834a76751a41b767e03daab299e607e7226c58a855"
name = "github.com/shurcooL/sanitized_anchor_name"
packages = ["."]
pruneopts = "UT"
revision = "86672fcb3f950f35f2e675df2240550f2a50762f"
[[projects]]
digest = "1:7e8d267900c7fa7f35129a2a37596e38ed0f11ca746d6d9ba727980ee138f9f6"
name = "github.com/stretchr/testify"
packages = [
"assert",
"require"
"require",
]
pruneopts = "UT"
revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71"
version = "v1.2.1"
[[projects]]
branch = "master"
digest = "1:b34062e39d8f3172fdd0c5c22ca1a3badeb2ddde295a997b0b63441e96d916f7"
name = "golang.org/x/image"
packages = [
"draw",
"math/f64"
"math/f64",
]
pruneopts = "UT"
revision = "af66defab954cb421ca110193eed9477c8541e2a"
[[projects]]
branch = "master"
digest = "1:ac7eaa5f1179480f517d32831225215cc20940152d66be29f3d5204ea15d425f"
name = "golang.org/x/net"
packages = [
"context",
"context/ctxhttp",
"html",
"html/atom"
"html/atom",
]
pruneopts = "UT"
revision = "f5dfe339be1d06f81b22525fe34671ee7d2c8904"
[[projects]]
branch = "master"
digest = "1:ccb0526e2eb5d454a25a536634fade769664eaa93ad2e4cd4107967bbc01b4e8"
name = "golang.org/x/oauth2"
packages = [
".",
@@ -223,23 +292,29 @@
"internal",
"jws",
"jwt",
"yandex"
"yandex",
]
pruneopts = "UT"
revision = "543e37812f10c46c622c9575afd7ad22f22a12ba"
[[projects]]
branch = "master"
digest = "1:ba7d5e85e8b4f084fae02a1a9d7462980e889d1eb689c747507b30a30b8bfa67"
name = "golang.org/x/sys"
packages = ["unix"]
pruneopts = "UT"
revision = "37707fdb30a5b38865cfb95e5aab41707daec7fd"
[[projects]]
branch = "master"
digest = "1:51a479a09b7ed06b7be5a854e27fcc328718ae0e5ad159f9ddeef12d0326c2e7"
name = "golang.org/x/time"
packages = ["rate"]
pruneopts = "UT"
revision = "6dc17368e09b0e8634d71cac8168d853e869a0c7"
[[projects]]
digest = "1:a48f97fb737d5d61cf13e81cfef040942d217d086766b823757d39d4f6a4c547"
name = "google.golang.org/appengine"
packages = [
".",
@@ -251,20 +326,56 @@
"internal/modules",
"internal/remote_api",
"internal/urlfetch",
"urlfetch"
"urlfetch",
]
pruneopts = "UT"
revision = "150dc57a1b433e64154302bdc40b6bb8aefa313a"
version = "v1.0.0"
[[projects]]
digest = "1:39c2113f3a89585666e6f973650cff186b2d06deb4aa202c88addb87b0a201db"
name = "gopkg.in/russross/blackfriday.v2"
packages = ["."]
pruneopts = "UT"
revision = "cadec560ec52d93835bf2f15bd794700d3a2473b"
version = "v2.0.0"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "da9242a76135f9ee04287bf437ffa42708fc36ec54f39c74127f41a3473f24ac"
input-imports = [
"github.com/PuerkitoBio/goquery",
"github.com/coreos/bbolt",
"github.com/dgrijalva/jwt-go",
"github.com/didip/tollbooth",
"github.com/didip/tollbooth_chi",
"github.com/globalsign/mgo",
"github.com/globalsign/mgo/bson",
"github.com/go-chi/chi",
"github.com/go-chi/chi/middleware",
"github.com/go-chi/cors",
"github.com/go-chi/render",
"github.com/go-pkgz/mongo",
"github.com/go-pkgz/repeater",
"github.com/google/uuid",
"github.com/gorilla/feeds",
"github.com/hashicorp/go-multierror",
"github.com/hashicorp/golang-lru",
"github.com/hashicorp/logutils",
"github.com/jessevdk/go-flags",
"github.com/microcosm-cc/bluemonday",
"github.com/nullrocks/identicon",
"github.com/patrickmn/go-cache",
"github.com/pkg/errors",
"github.com/stretchr/testify/assert",
"github.com/stretchr/testify/require",
"golang.org/x/image/draw",
"golang.org/x/oauth2",
"golang.org/x/oauth2/facebook",
"golang.org/x/oauth2/github",
"golang.org/x/oauth2/google",
"golang.org/x/oauth2/yandex",
"gopkg.in/russross/blackfriday.v2",
]
solver-name = "gps-cdcl"
solver-version = 1
+11
View File
@@ -17,6 +17,7 @@ import (
"github.com/didip/tollbooth_chi"
"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
"github.com/go-chi/cors"
"github.com/go-chi/render"
"github.com/pkg/errors"
"gopkg.in/russross/blackfriday.v2"
@@ -118,6 +119,16 @@ func (s *Rest) routes() chi.Router {
avatarProxy: s.AvatarProxy,
}
corsMiddleware := cors.New(cors.Options{
AllowedOrigins: []string{"*"},
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-XSRF-Token", "X-JWT"},
ExposedHeaders: []string{"Link"},
AllowCredentials: true,
MaxAge: 300,
})
router.Use(corsMiddleware.Handler)
ipFn := func(ip string) string { return store.HashValue(ip, s.DataService.Secret)[:12] } // logger uses it for anonymization
// auth routes for all providers
+32
View File
@@ -0,0 +1,32 @@
# CORS net/http middleware
(fork of github.com/rs/cors)
## Usage
```go
func main() {
r := chi.NewRouter()
// Basic CORS
// for more ideas, see: https://developer.github.com/v3/#cross-origin-resource-sharing
cors := cors.New(cors.Options{
// AllowedOrigins: []string{"https://foo.com"}, // Use this to allow specific origin hosts
AllowedOrigins: []string{"*"},
// AllowOriginFunc: func(r *http.Request, origin string) bool { return true },
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"},
ExposedHeaders: []string{"Link"},
AllowCredentials: true,
MaxAge: 300, // Maximum value not ignored by any of major browsers
})
r.Use(cors.Handler)
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("welcome"))
})
http.ListenAndServe(":3000", r)
}
```
+369
View File
@@ -0,0 +1,369 @@
// cors package is net/http handler to handle CORS related requests
// 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"},
// AllowCredentials: true,
// })
//
// Then insert the handler in the chain:
//
// handler = c.Handler(handler)
//
// See Options documentation for more options.
//
// The resulting handler is a standard net/http handler.
package cors
import (
"log"
"net/http"
"os"
"strconv"
"strings"
)
// Options is a configuration container to setup the CORS middleware.
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 penality.
// Only one wildcard can be used per origin.
// Default value is ["*"]
AllowedOrigins []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 AllowedOrigins is ignored.
AllowOriginFunc 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)
AllowedMethods []string
// AllowedHeaders is list of non simple headers the client is allowed to use with
// cross-domain requests.
// If the special "*" value is present in the list, all headers will be allowed.
// Default value is [] but "Origin" is always appended to the list.
AllowedHeaders []string
// 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
// OptionsPassthrough instructs preflight to let other potential next handlers to
// process the OPTIONS method. Turn this on if your application handles OPTIONS.
OptionsPassthrough bool
// Debugging flag adds additional output to debug server side CORS issues
Debug bool
}
// Cors http handler
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(r *http.Request, origin string) bool
// Set to true when allowed headers contains a "*"
allowedHeadersAll bool
// Normalized list of allowed headers
allowedHeaders []string
// Normalized list of allowed methods
allowedMethods []string
// Normalized list of exposed headers
exposedHeaders []string
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,
}
if options.Debug {
c.log = log.New(os.Stdout, "[cors] ", log.LstdFlags)
}
// Normalize options
// Note: for origins and methods matching, the spec requires a case-sensitive matching.
// As it may error prone, we chose to ignore the spec here.
// Allowed Origins
if len(options.AllowedOrigins) == 0 {
// Default is all origins
c.allowedOriginsAll = true
} else {
c.allowedOrigins = []string{}
c.allowedWOrigins = []wildcard{}
for _, origin := range options.AllowedOrigins {
// Normalize
origin = strings.ToLower(origin)
if origin == "*" {
// If "*" is present in the list, turn the whole list into a match all
c.allowedOriginsAll = true
c.allowedOrigins = nil
c.allowedWOrigins = nil
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)]}
c.allowedWOrigins = append(c.allowedWOrigins, w)
} else {
c.allowedOrigins = append(c.allowedOrigins, origin)
}
}
}
// Allowed Headers
if len(options.AllowedHeaders) == 0 {
// Use sensible defaults
c.allowedHeaders = []string{"Origin", "Accept", "Content-Type"}
} 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)
for _, h := range options.AllowedHeaders {
if h == "*" {
c.allowedHeadersAll = true
c.allowedHeaders = nil
break
}
}
}
// Allowed Methods
if len(options.AllowedMethods) == 0 {
// Default is spec's "simple" methods
c.allowedMethods = []string{"GET", "POST"}
} else {
c.allowedMethods = convert(options.AllowedMethods, strings.ToUpper)
}
return c
}
// Default creates a new Cors handler with default options
func Default() *Cors {
return New(Options{})
}
// Handler apply the CORS specification on the request, and add relevant CORS headers
// as necessary.
func (c *Cors) Handler(next http.Handler) http.Handler {
return http.HandlerFunc(func(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 {
next.ServeHTTP(w, r)
}
} else {
c.logf("Handler: Actual request")
c.handleActualRequest(w, r)
next.ServeHTTP(w, r)
}
})
}
// handlePreflight handles pre-flight CORS requests
func (c *Cors) handlePreflight(w http.ResponseWriter, r *http.Request) {
headers := w.Header()
origin := r.Header.Get("Origin")
if r.Method != "OPTIONS" {
c.logf("Preflight aborted: %s!=OPTIONS", r.Method)
return
}
// 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")
if origin == "" {
c.logf("Preflight aborted: empty origin")
return
}
if !c.isOriginAllowed(r, origin) {
c.logf("Preflight aborted: origin '%s' not allowed", origin)
return
}
reqMethod := r.Header.Get("Access-Control-Request-Method")
if !c.isMethodAllowed(reqMethod) {
c.logf("Preflight aborted: method '%s' not allowed", reqMethod)
return
}
reqHeaders := parseHeaderList(r.Header.Get("Access-Control-Request-Headers"))
if !c.areHeadersAllowed(reqHeaders) {
c.logf("Preflight aborted: headers '%v' not allowed", reqHeaders)
return
}
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))
if len(reqHeaders) > 0 {
// Spec says: Since the list of headers can be unbounded, simply returning supported headers
// from Access-Control-Request-Headers can be enough
headers.Set("Access-Control-Allow-Headers", strings.Join(reqHeaders, ", "))
}
if c.allowCredentials {
headers.Set("Access-Control-Allow-Credentials", "true")
}
if c.maxAge > 0 {
headers.Set("Access-Control-Max-Age", strconv.Itoa(c.maxAge))
}
c.logf("Preflight response headers: %v", headers)
}
// handleActualRequest handles simple cross-origin requests, actual request or redirects
func (c *Cors) handleActualRequest(w http.ResponseWriter, r *http.Request) {
headers := w.Header()
origin := r.Header.Get("Origin")
if r.Method == "OPTIONS" {
c.logf("Actual request no headers added: method == %s", r.Method)
return
}
// Always set Vary, see https://github.com/rs/cors/issues/10
headers.Add("Vary", "Origin")
if origin == "" {
c.logf("Actual request no headers added: missing origin")
return
}
if !c.isOriginAllowed(r, origin) {
c.logf("Actual request no headers added: origin '%s' not allowed", origin)
return
}
// Note that spec does define a way to specifically disallow a simple method like GET or
// POST. Access-Control-Allow-Methods is only used for pre-flight requests and the
// spec doesn't instruct to check the allowed methods for simple cross-origin requests.
// We think it's a nice feature to be able to have control on those methods though.
if !c.isMethodAllowed(r.Method) {
if c.log != nil {
c.logf("Actual request no headers added: method '%s' not allowed",
r.Method)
}
return
}
headers.Set("Access-Control-Allow-Origin", origin)
if len(c.exposedHeaders) > 0 {
headers.Set("Access-Control-Expose-Headers", strings.Join(c.exposedHeaders, ", "))
}
if c.allowCredentials {
headers.Set("Access-Control-Allow-Credentials", "true")
}
c.logf("Actual response added headers: %v", headers)
}
// convenience method. checks if debugging is turned on before printing
func (c *Cors) logf(format string, a ...interface{}) {
if c.log != nil {
c.log.Printf(format, a...)
}
}
// isOriginAllowed checks if a given origin is allowed to perform cross-domain requests
// on the endpoint
func (c *Cors) isOriginAllowed(r *http.Request, origin string) bool {
if c.allowOriginFunc != nil {
return c.allowOriginFunc(r, origin)
}
if c.allowedOriginsAll {
return true
}
origin = strings.ToLower(origin)
for _, o := range c.allowedOrigins {
if o == origin {
return true
}
}
for _, w := range c.allowedWOrigins {
if w.match(origin) {
return true
}
}
return false
}
// isMethodAllowed checks if a given method can be used as part of a cross-domain request
// on the endpoing
func (c *Cors) isMethodAllowed(method string) bool {
if len(c.allowedMethods) == 0 {
// If no method allowed, always return false, even for preflight request
return false
}
method = strings.ToUpper(method)
if method == "OPTIONS" {
// Always allow preflight requests
return true
}
for _, m := range c.allowedMethods {
if m == method {
return true
}
}
return false
}
// areHeadersAllowed checks if a given list of headers are allowed to used within
// a cross-domain request.
func (c *Cors) areHeadersAllowed(requestedHeaders []string) bool {
if c.allowedHeadersAll || len(requestedHeaders) == 0 {
return true
}
for _, header := range requestedHeaders {
header = http.CanonicalHeaderKey(header)
found := false
for _, h := range c.allowedHeaders {
if h == header {
found = true
}
}
if !found {
return false
}
}
return true
}
+70
View File
@@ -0,0 +1,70 @@
package cors
import "strings"
const toLower = 'a' - 'A'
type converter func(string) string
type wildcard struct {
prefix string
suffix string
}
func (w wildcard) match(s string) bool {
return len(s) >= len(w.prefix+w.suffix) && strings.HasPrefix(s, w.prefix) && strings.HasSuffix(s, w.suffix)
}
// convert converts a list of string using the passed converter function
func convert(s []string, c converter) []string {
out := []string{}
for _, i := range s {
out = append(out, c(i))
}
return out
}
// parseHeaderList tokenize + normalize a string containing a list of headers
func parseHeaderList(headerList string) []string {
l := len(headerList)
h := make([]byte, 0, l)
upper := true
// Estimate the number headers in order to allocate the right splice size
t := 0
for i := 0; i < l; i++ {
if headerList[i] == ',' {
t++
}
}
headers := make([]string, 0, t)
for i := 0; i < l; i++ {
b := headerList[i]
if b >= 'a' && b <= 'z' {
if upper {
h = append(h, b-toLower)
} else {
h = append(h, b)
}
} else if b >= 'A' && b <= 'Z' {
if !upper {
h = append(h, b+toLower)
} else {
h = append(h, b)
}
} else if b == '-' || (b >= '0' && b <= '9') {
h = append(h, b)
}
if b == ' ' || b == ',' || i == l-1 {
if len(h) > 0 {
// Flush the found header
headers = append(headers, string(h))
h = h[:0]
upper = true
}
} else {
upper = b == '-'
}
}
return headers
}