mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-05-12 14:30:19 -05:00
Merge pull request #9548 from owncloud/dependabot/go_modules/github.com/rs/cors-1.11.0
[full-ci] Get rid of go-chi/cors and bump rs/cors to 1.11.0
This commit is contained in:
@@ -22,7 +22,6 @@ require (
|
||||
github.com/gabriel-vasile/mimetype v1.4.4
|
||||
github.com/ggwhite/go-masker v1.1.0
|
||||
github.com/go-chi/chi/v5 v5.1.0
|
||||
github.com/go-chi/cors v1.2.1
|
||||
github.com/go-chi/render v1.0.3
|
||||
github.com/go-jose/go-jose/v3 v3.0.3
|
||||
github.com/go-ldap/ldap/v3 v3.4.8
|
||||
@@ -79,6 +78,7 @@ require (
|
||||
github.com/r3labs/sse/v2 v2.10.0
|
||||
github.com/riandyrn/otelchi v0.8.0
|
||||
github.com/rogpeppe/go-internal v1.12.0
|
||||
github.com/rs/cors v1.11.0
|
||||
github.com/rs/zerolog v1.33.0
|
||||
github.com/shamaton/msgpack/v2 v2.2.0
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
@@ -302,7 +302,6 @@ require (
|
||||
github.com/prometheus/statsd_exporter v0.22.8 // indirect
|
||||
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect
|
||||
github.com/rivo/uniseg v0.4.2 // indirect
|
||||
github.com/rs/cors v1.10.1 // indirect
|
||||
github.com/rs/xid v1.5.0 // indirect
|
||||
github.com/russellhaering/goxmldsig v1.4.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
|
||||
@@ -1143,8 +1143,6 @@ github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkPro
|
||||
github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
|
||||
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
|
||||
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
|
||||
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
||||
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
|
||||
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
|
||||
github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s=
|
||||
@@ -1928,8 +1926,8 @@ github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6po
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo=
|
||||
github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
||||
github.com/rs/cors v1.11.0 h1:0B9GE/r9Bc2UxRMMtymBkHTenPkHDv0CW4Y98GBY+po=
|
||||
github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
||||
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/cors"
|
||||
|
||||
chicors "github.com/go-chi/cors"
|
||||
rscors "github.com/rs/cors"
|
||||
)
|
||||
|
||||
// NoCache writes required cache headers to all requests.
|
||||
@@ -31,10 +31,11 @@ func Cors(opts ...cors.Option) func(http.Handler) http.Handler {
|
||||
Str("allowed_headers", strings.Join(options.AllowedHeaders, ", ")).
|
||||
Bool("allow_credentials", options.AllowCredentials).
|
||||
Msg("setup cors middleware")
|
||||
return chicors.Handler(chicors.Options{
|
||||
c := rscors.New(rscors.Options{
|
||||
AllowedOrigins: options.AllowedOrigins,
|
||||
AllowedMethods: options.AllowedMethods,
|
||||
AllowedHeaders: options.AllowedHeaders,
|
||||
AllowCredentials: options.AllowCredentials,
|
||||
})
|
||||
return c.Handler
|
||||
}
|
||||
|
||||
@@ -50,17 +50,19 @@ Feature: CORS headers
|
||||
| 2 | /apps/files_sharing/api/v1/shares | 200 | 200 |
|
||||
|
||||
@issue-5194
|
||||
# The Access-Control-Request-Headers need to be in lower-case and alphabetically order to comply with the rs/cors
|
||||
# package see: https://github.com/rs/cors/commit/4c32059b2756926619f6bf70281b91be7b5dddb2#diff-bf80d8fbedf172fab9ba2604da7f7be972e48b2f78a8d0cd21619d5f93665895R367
|
||||
Scenario Outline: CORS headers should be returned when an preflight request is sent
|
||||
Given using OCS API version "<ocs-api-version>"
|
||||
When user "Alice" sends HTTP method "OPTIONS" to OCS API endpoint "<endpoint>" with headers
|
||||
| header | value |
|
||||
| Origin | https://aphno.badal |
|
||||
| Access-Control-Request-Headers | Origin, Accept, Content-Type, Depth, Authorization, Ocs-Apirequest, If-None-Match, If-Match, Destination, Overwrite, X-Request-Id, X-Requested-With, Tus-Resumable, Tus-Checksum-Algorithm, Upload-Concat, Upload-Length, Upload-Metadata, Upload-Defer-Length, Upload-Expires, Upload-Checksum, Upload-Offset, X-Http-Method-Override, Cache-Control |
|
||||
| Access-Control-Request-Headers | accept,authorization,cache-control,content-type,depth,destination,if-match,if-none-match,ocs-apirequest,origin,overwrite,tus-checksum-algorithm,tus-resumable,upload-checksum,upload-concat,upload-defer-length,upload-expires,upload-length,upload-metadata,upload-offset,x-http-method-override,x-request-id,x-requested-with |
|
||||
| Access-Control-Request-Method | <request-method> |
|
||||
And the HTTP status code should be "204"
|
||||
And the following headers should be set
|
||||
| header | value |
|
||||
| Access-Control-Allow-Headers | Origin, Accept, Content-Type, Depth, Authorization, Ocs-Apirequest, If-None-Match, If-Match, Destination, Overwrite, X-Request-Id, X-Requested-With, Tus-Resumable, Tus-Checksum-Algorithm, Upload-Concat, Upload-Length, Upload-Metadata, Upload-Defer-Length, Upload-Expires, Upload-Checksum, Upload-Offset, X-Http-Method-Override, Cache-Control |
|
||||
| Access-Control-Allow-Headers | accept,authorization,cache-control,content-type,depth,destination,if-match,if-none-match,ocs-apirequest,origin,overwrite,tus-checksum-algorithm,tus-resumable,upload-checksum,upload-concat,upload-defer-length,upload-expires,upload-length,upload-metadata,upload-offset,x-http-method-override,x-request-id,x-requested-with |
|
||||
| Access-Control-Allow-Origin | https://aphno.badal |
|
||||
| Access-Control-Allow-Methods | <request-method> |
|
||||
Examples:
|
||||
@@ -134,6 +136,8 @@ Feature: CORS headers
|
||||
Then the HTTP status code should be "403"
|
||||
|
||||
@issue-8380
|
||||
# The Access-Control-Request-Headers need to be in lower-case and alphabetically order to comply with the rs/cors
|
||||
# package see: https://github.com/rs/cors/commit/4c32059b2756926619f6bf70281b91be7b5dddb2#diff-bf80d8fbedf172fab9ba2604da7f7be972e48b2f78a8d0cd21619d5f93665895R367
|
||||
Scenario Outline: CORS headers should be returned when an preflight request is sent to Tus upload
|
||||
Given user "Alice" has created a new TUS resource for the space "Personal" with content "" using the WebDAV API with these headers:
|
||||
| Upload-Length | 5 |
|
||||
@@ -143,12 +147,12 @@ Feature: CORS headers
|
||||
When user "Alice" sends HTTP method "OPTIONS" to URL "<endpoint>" with headers
|
||||
| header | value |
|
||||
| Origin | https://aphno.badal |
|
||||
| Access-Control-Request-Headers | Origin, Accept, Content-Type, Depth, Authorization, Ocs-Apirequest, If-None-Match, If-Match, Destination, Overwrite, X-Request-Id, X-Requested-With, Tus-Resumable, Tus-Checksum-Algorithm, Upload-Concat, Upload-Length, Upload-Metadata, Upload-Defer-Length, Upload-Expires, Upload-Checksum, Upload-Offset, X-Http-Method-Override, Cache-Control |
|
||||
| Access-Control-Request-Headers | accept,authorization,cache-control,content-type,depth,destination,if-match,if-none-match,ocs-apirequest,origin,overwrite,tus-checksum-algorithm,tus-resumable,upload-checksum,upload-concat,upload-defer-length,upload-expires,upload-length,upload-metadata,upload-offset,x-http-method-override,x-request-id,x-requested-with |
|
||||
| Access-Control-Request-Method | <request-method> |
|
||||
And the HTTP status code should be "204"
|
||||
And the following headers should be set
|
||||
| header | value |
|
||||
| Access-Control-Allow-Headers | Origin, Accept, Content-Type, Depth, Authorization, Ocs-Apirequest, If-None-Match, If-Match, Destination, Overwrite, X-Request-Id, X-Requested-With, Tus-Resumable, Tus-Checksum-Algorithm, Upload-Concat, Upload-Length, Upload-Metadata, Upload-Defer-Length, Upload-Expires, Upload-Checksum, Upload-Offset, X-Http-Method-Override, Cache-Control |
|
||||
| Access-Control-Allow-Headers | accept,authorization,cache-control,content-type,depth,destination,if-match,if-none-match,ocs-apirequest,origin,overwrite,tus-checksum-algorithm,tus-resumable,upload-checksum,upload-concat,upload-defer-length,upload-expires,upload-length,upload-metadata,upload-offset,x-http-method-override,x-request-id,x-requested-with |
|
||||
| Access-Control-Allow-Origin | https://aphno.badal |
|
||||
| Access-Control-Allow-Methods | <request-method> |
|
||||
Examples:
|
||||
@@ -156,4 +160,4 @@ Feature: CORS headers
|
||||
| /%tus_upload_location% | PUT |
|
||||
| /%tus_upload_location% | POST |
|
||||
| /%tus_upload_location% | HEAD |
|
||||
| /%tus_upload_location% | PATCH |
|
||||
| /%tus_upload_location% | PATCH |
|
||||
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
Copyright (c) 2014 Olivier Poitrey <rs@dailymotion.com>
|
||||
Copyright (c) 2016-Present https://github.com/go-chi authors
|
||||
|
||||
MIT License
|
||||
|
||||
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.
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
# CORS net/http middleware
|
||||
|
||||
[go-chi/cors](https://github.com/go-chi/cors) is a fork of [github.com/rs/cors](https://github.com/rs/cors) that
|
||||
provides a `net/http` compatible middleware for performing preflight CORS checks on the server side. These headers
|
||||
are required for using the browser native [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API).
|
||||
|
||||
This middleware is designed to be used as a top-level middleware on the [chi](https://github.com/go-chi/chi) router.
|
||||
Applying with within a `r.Group()` or using `With()` will not work without routes matching `OPTIONS` added.
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
func main() {
|
||||
r := chi.NewRouter()
|
||||
|
||||
// Basic CORS
|
||||
// for more ideas, see: https://developer.github.com/v3/#cross-origin-resource-sharing
|
||||
r.Use(cors.Handler(cors.Options{
|
||||
// AllowedOrigins: []string{"https://foo.com"}, // Use this to allow specific origin hosts
|
||||
AllowedOrigins: []string{"https://*", "http://*"},
|
||||
// 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: false,
|
||||
MaxAge: 300, // Maximum value not ignored by any of major browsers
|
||||
}))
|
||||
|
||||
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("welcome"))
|
||||
})
|
||||
|
||||
http.ListenAndServe(":3000", r)
|
||||
}
|
||||
```
|
||||
|
||||
## Credits
|
||||
|
||||
All credit for the original work of this middleware goes out to [github.com/rs](github.com/rs).
|
||||
-400
@@ -1,400 +0,0 @@
|
||||
// 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 penalty.
|
||||
// Only one wildcard can be used per origin.
|
||||
// Default value is ["*"]
|
||||
AllowedOrigins []string
|
||||
|
||||
// AllowOriginFunc is a custom function to validate the origin. It takes 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 (HEAD, 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
|
||||
}
|
||||
|
||||
// Logger generic interface for logger
|
||||
type Logger interface {
|
||||
Printf(string, ...interface{})
|
||||
}
|
||||
|
||||
// Cors http handler
|
||||
type Cors struct {
|
||||
// Debug logger
|
||||
Log Logger
|
||||
|
||||
// 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
|
||||
|
||||
// Normalized list of allowed headers
|
||||
allowedHeaders []string
|
||||
|
||||
// Normalized list of allowed methods
|
||||
allowedMethods []string
|
||||
|
||||
// Normalized list of exposed headers
|
||||
exposedHeaders []string
|
||||
maxAge int
|
||||
|
||||
// Set to true when allowed origins contains a "*"
|
||||
allowedOriginsAll bool
|
||||
|
||||
// Set to true when allowed headers contains a "*"
|
||||
allowedHeadersAll bool
|
||||
|
||||
allowCredentials bool
|
||||
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 == nil {
|
||||
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 {
|
||||
if options.AllowOriginFunc == nil {
|
||||
// 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:]}
|
||||
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{http.MethodGet, http.MethodPost, http.MethodHead}
|
||||
} else {
|
||||
c.allowedMethods = convert(options.AllowedMethods, strings.ToUpper)
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// Handler creates a new Cors handler with passed options.
|
||||
func Handler(options Options) func(next http.Handler) http.Handler {
|
||||
c := New(options)
|
||||
return c.Handler
|
||||
}
|
||||
|
||||
// AllowAll create a new Cors handler with permissive configuration allowing all
|
||||
// origins with all standard methods with any header and credentials.
|
||||
func AllowAll() *Cors {
|
||||
return New(Options{
|
||||
AllowedOrigins: []string{"*"},
|
||||
AllowedMethods: []string{
|
||||
http.MethodHead,
|
||||
http.MethodGet,
|
||||
http.MethodPost,
|
||||
http.MethodPut,
|
||||
http.MethodPatch,
|
||||
http.MethodDelete,
|
||||
},
|
||||
AllowedHeaders: []string{"*"},
|
||||
AllowCredentials: false,
|
||||
})
|
||||
}
|
||||
|
||||
// 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 == http.MethodOptions && r.Header.Get("Access-Control-Request-Method") != "" {
|
||||
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 {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
} 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 != http.MethodOptions {
|
||||
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
|
||||
}
|
||||
if c.allowedOriginsAll {
|
||||
headers.Set("Access-Control-Allow-Origin", "*")
|
||||
} else {
|
||||
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")
|
||||
|
||||
// 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) {
|
||||
c.logf("Actual request no headers added: method '%s' not allowed", r.Method)
|
||||
|
||||
return
|
||||
}
|
||||
if c.allowedOriginsAll {
|
||||
headers.Set("Access-Control-Allow-Origin", "*")
|
||||
} else {
|
||||
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 a logger is set.
|
||||
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 endpoint
|
||||
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 == http.MethodOptions {
|
||||
// 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
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
-70
@@ -1,70 +0,0 @@
|
||||
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 == '_' || 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
|
||||
}
|
||||
+16
-10
@@ -88,11 +88,14 @@ handler = c.Handler(handler)
|
||||
|
||||
* **AllowedOrigins** `[]string`: 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. The default value is `*`.
|
||||
* **AllowOriginFunc** `func (origin string) bool`: A custom function to validate the origin. It takes the origin as an argument and returns true if allowed, or false otherwise. If this option is set, the content of `AllowedOrigins` is ignored.
|
||||
* **AllowOriginRequestFunc** `func (r *http.Request, origin string) bool`: A custom function to validate the origin. It takes the HTTP Request object and the origin as argument and returns true if allowed or false otherwise. If this option is set, the content of `AllowedOrigins` and `AllowOriginFunc` is ignored
|
||||
* **AllowOriginRequestFunc** `func (r *http.Request, origin string) bool`: A custom function to validate the origin. It takes the HTTP Request object and the origin as argument and returns true if allowed or false otherwise. If this option is set, the contents of `AllowedOrigins` and `AllowOriginFunc` are ignored.
|
||||
Deprecated: use `AllowOriginVaryRequestFunc` instead.
|
||||
* **AllowOriginVaryRequestFunc** `func(r *http.Request, origin string) (bool, []string)`: A custom function to validate the origin. It takes the HTTP Request object and the origin as argument and returns true if allowed or false otherwise with a list of headers used to take that decision if any so they can be added to the Vary header. If this option is set, the contents of `AllowedOrigins`, `AllowOriginFunc` and `AllowOriginRequestFunc` are ignored.
|
||||
* **AllowedMethods** `[]string`: A list of methods the client is allowed to use with cross-domain requests. Default value is simple methods (`GET` and `POST`).
|
||||
* **AllowedHeaders** `[]string`: A list of non simple headers the client is allowed to use with cross-domain requests.
|
||||
* **ExposedHeaders** `[]string`: Indicates which headers are safe to expose to the API of a CORS API specification
|
||||
* **ExposedHeaders** `[]string`: Indicates which headers are safe to expose to the API of a CORS API specification.
|
||||
* **AllowCredentials** `bool`: Indicates whether the request can include user credentials like cookies, HTTP authentication or client side SSL certificates. The default is `false`.
|
||||
* **AllowPrivateNetwork** `bool`: Indicates whether to accept cross-origin requests over a private network.
|
||||
* **MaxAge** `int`: Indicates how long (in seconds) the results of a preflight request can be cached. The default is `0` which stands for no max age.
|
||||
* **OptionsPassthrough** `bool`: Instructs preflight to let other potential next handlers to process the `OPTIONS` method. Turn this on if your application handles `OPTIONS`.
|
||||
* **OptionsSuccessStatus** `int`: Provides a status code to use for successful OPTIONS requests. Default value is `http.StatusNoContent` (`204`).
|
||||
@@ -102,18 +105,21 @@ See [API documentation](http://godoc.org/github.com/rs/cors) for more info.
|
||||
|
||||
## Benchmarks
|
||||
|
||||
```
|
||||
goos: darwin
|
||||
goarch: arm64
|
||||
pkg: github.com/rs/cors
|
||||
BenchmarkWithout-10 352671961 3.317 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkDefault-10 18261723 65.63 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkAllowedOrigin-10 13309591 90.21 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkPreflight-10 7247728 166.9 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkPreflightHeader-10 5915437 202.9 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkWildcard/match-10 250336476 4.784 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkWildcard/too_short-10 1000000000 0.6354 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkWithout-10 135325480 8.124 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkDefault-10 24082140 51.40 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkAllowedOrigin-10 16424518 88.25 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkPreflight-10 8010259 147.3 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkPreflightHeader-10 6850962 175.0 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkWildcard/match-10 253275342 4.714 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkWildcard/too_short-10 1000000000 0.6235 ns/op 0 B/op 0 allocs/op
|
||||
PASS
|
||||
ok github.com/rs/cors 9.613s
|
||||
ok github.com/rs/cors 99.131s
|
||||
```
|
||||
|
||||
## Licenses
|
||||
|
||||
All source code is licensed under the [MIT License](https://raw.github.com/rs/cors/master/LICENSE).
|
||||
|
||||
+38
-53
@@ -26,6 +26,8 @@ import (
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/rs/cors/internal"
|
||||
)
|
||||
|
||||
var headerVaryOrigin = []string{"Origin"}
|
||||
@@ -49,13 +51,15 @@ type Options struct {
|
||||
// takes the HTTP Request object and the origin as argument and returns true
|
||||
// if allowed or false otherwise. If headers are used take the decision,
|
||||
// consider using AllowOriginVaryRequestFunc instead. If this option is set,
|
||||
// the content of `AllowedOrigins`, `AllowOriginFunc` are ignored.
|
||||
// the contents of `AllowedOrigins`, `AllowOriginFunc` are ignored.
|
||||
//
|
||||
// Deprecated: use `AllowOriginVaryRequestFunc` instead.
|
||||
AllowOriginRequestFunc func(r *http.Request, origin string) bool
|
||||
// AllowOriginVaryRequestFunc is a custom function to validate the origin.
|
||||
// It takes the HTTP Request object and the origin as argument and returns
|
||||
// true if allowed or false otherwise with a list of headers used to take
|
||||
// that decision if any so they can be added to the Vary header. If this
|
||||
// option is set, the content of `AllowedOrigins`, `AllowOriginFunc` and
|
||||
// option is set, the contents of `AllowedOrigins`, `AllowOriginFunc` and
|
||||
// `AllowOriginRequestFunc` are ignored.
|
||||
AllowOriginVaryRequestFunc func(r *http.Request, origin string) (bool, []string)
|
||||
// AllowedMethods is a list of methods the client is allowed to use with
|
||||
@@ -109,7 +113,11 @@ type Cors struct {
|
||||
// Optional origin validator function
|
||||
allowOriginFunc func(r *http.Request, origin string) (bool, []string)
|
||||
// Normalized list of allowed headers
|
||||
allowedHeaders []string
|
||||
// Note: the Fetch standard guarantees that CORS-unsafe request-header names
|
||||
// (i.e. the values listed in the Access-Control-Request-Headers header)
|
||||
// are unique and sorted;
|
||||
// see https://fetch.spec.whatwg.org/#cors-unsafe-request-header-names.
|
||||
allowedHeaders internal.SortedSet
|
||||
// Normalized list of allowed methods
|
||||
allowedMethods []string
|
||||
// Pre-computed normalized list of exposed headers
|
||||
@@ -140,33 +148,29 @@ func New(options Options) *Cors {
|
||||
c.Log = log.New(os.Stdout, "[cors] ", log.LstdFlags)
|
||||
}
|
||||
|
||||
if options.AllowOriginVaryRequestFunc != nil {
|
||||
// Allowed origins
|
||||
switch {
|
||||
case options.AllowOriginVaryRequestFunc != nil:
|
||||
c.allowOriginFunc = options.AllowOriginVaryRequestFunc
|
||||
} else if options.AllowOriginRequestFunc != nil {
|
||||
case options.AllowOriginRequestFunc != nil:
|
||||
c.allowOriginFunc = func(r *http.Request, origin string) (bool, []string) {
|
||||
return options.AllowOriginRequestFunc(r, origin), nil
|
||||
}
|
||||
} else if options.AllowOriginFunc != nil {
|
||||
case options.AllowOriginFunc != nil:
|
||||
c.allowOriginFunc = func(r *http.Request, origin string) (bool, []string) {
|
||||
return options.AllowOriginFunc(origin), nil
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize options
|
||||
// Note: for origins 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 {
|
||||
case len(options.AllowedOrigins) == 0:
|
||||
if c.allowOriginFunc == nil {
|
||||
// Default is all origins
|
||||
c.allowedOriginsAll = true
|
||||
}
|
||||
} else {
|
||||
default:
|
||||
c.allowedOrigins = []string{}
|
||||
c.allowedWOrigins = []wildcard{}
|
||||
for _, origin := range options.AllowedOrigins {
|
||||
// Normalize
|
||||
// Note: for origins matching, the spec requires a case-sensitive matching.
|
||||
// As it may error prone, we chose to ignore the spec here.
|
||||
origin = strings.ToLower(origin)
|
||||
if origin == "*" {
|
||||
// If "*" is present in the list, turn the whole list into a match all
|
||||
@@ -185,15 +189,19 @@ func New(options Options) *Cors {
|
||||
}
|
||||
|
||||
// Allowed Headers
|
||||
// Note: the Fetch standard guarantees that CORS-unsafe request-header names
|
||||
// (i.e. the values listed in the Access-Control-Request-Headers header)
|
||||
// are lowercase; see https://fetch.spec.whatwg.org/#cors-unsafe-request-header-names.
|
||||
if len(options.AllowedHeaders) == 0 {
|
||||
// Use sensible defaults
|
||||
c.allowedHeaders = []string{"Accept", "Content-Type", "X-Requested-With"}
|
||||
c.allowedHeaders = internal.NewSortedSet("accept", "content-type", "x-requested-with")
|
||||
} else {
|
||||
c.allowedHeaders = convert(options.AllowedHeaders, http.CanonicalHeaderKey)
|
||||
normalized := convert(options.AllowedHeaders, strings.ToLower)
|
||||
c.allowedHeaders = internal.NewSortedSet(normalized...)
|
||||
for _, h := range options.AllowedHeaders {
|
||||
if h == "*" {
|
||||
c.allowedHeadersAll = true
|
||||
c.allowedHeaders = nil
|
||||
c.allowedHeaders = internal.SortedSet{}
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -353,10 +361,12 @@ func (c *Cors) handlePreflight(w http.ResponseWriter, r *http.Request) {
|
||||
c.logf(" Preflight aborted: method '%s' not allowed", reqMethod)
|
||||
return
|
||||
}
|
||||
reqHeadersRaw := r.Header["Access-Control-Request-Headers"]
|
||||
reqHeaders, reqHeadersEdited := convertDidCopy(splitHeaderValues(reqHeadersRaw), http.CanonicalHeaderKey)
|
||||
if !c.areHeadersAllowed(reqHeaders) {
|
||||
c.logf(" Preflight aborted: headers '%v' not allowed", reqHeaders)
|
||||
// Note: the Fetch standard guarantees that at most one
|
||||
// Access-Control-Request-Headers header is present in the preflight request;
|
||||
// see step 5.2 in https://fetch.spec.whatwg.org/#cors-preflight-fetch-0.
|
||||
reqHeaders, found := first(r.Header, "Access-Control-Request-Headers")
|
||||
if found && !c.allowedHeadersAll && !c.allowedHeaders.Subsumes(reqHeaders[0]) {
|
||||
c.logf(" Preflight aborted: headers '%v' not allowed", reqHeaders[0])
|
||||
return
|
||||
}
|
||||
if c.allowedOriginsAll {
|
||||
@@ -367,14 +377,10 @@ func (c *Cors) handlePreflight(w http.ResponseWriter, r *http.Request) {
|
||||
// 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["Access-Control-Allow-Methods"] = r.Header["Access-Control-Request-Method"]
|
||||
if len(reqHeaders) > 0 {
|
||||
if found && len(reqHeaders[0]) > 0 {
|
||||
// Spec says: Since the list of headers can be unbounded, simply returning supported headers
|
||||
// from Access-Control-Request-Headers can be enough
|
||||
if reqHeadersEdited || len(reqHeaders) != len(reqHeadersRaw) {
|
||||
headers.Set("Access-Control-Allow-Headers", strings.Join(reqHeaders, ", "))
|
||||
} else {
|
||||
headers["Access-Control-Allow-Headers"] = reqHeadersRaw
|
||||
}
|
||||
headers["Access-Control-Allow-Headers"] = reqHeaders
|
||||
}
|
||||
if c.allowCredentials {
|
||||
headers["Access-Control-Allow-Credentials"] = headerTrue
|
||||
@@ -398,10 +404,10 @@ func (c *Cors) handleActualRequest(w http.ResponseWriter, r *http.Request) {
|
||||
allowed, additionalVaryHeaders := c.isOriginAllowed(r, origin)
|
||||
|
||||
// Always set Vary, see https://github.com/rs/cors/issues/10
|
||||
if vary, found := headers["Vary"]; found {
|
||||
headers["Vary"] = append(vary, headerVaryOrigin[0])
|
||||
} else {
|
||||
if vary := headers["Vary"]; vary == nil {
|
||||
headers["Vary"] = headerVaryOrigin
|
||||
} else {
|
||||
headers["Vary"] = append(vary, headerVaryOrigin[0])
|
||||
}
|
||||
if len(additionalVaryHeaders) > 0 {
|
||||
headers.Add("Vary", strings.Join(convert(additionalVaryHeaders, http.CanonicalHeaderKey), ", "))
|
||||
@@ -494,24 +500,3 @@ func (c *Cors) isMethodAllowed(method string) bool {
|
||||
}
|
||||
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 {
|
||||
found := false
|
||||
for _, h := range c.allowedHeaders {
|
||||
if h == header {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
// adapted from github.com/jub0bs/cors
|
||||
package internal
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A SortedSet represents a mathematical set of strings sorted in
|
||||
// lexicographical order.
|
||||
// Each element has a unique position ranging from 0 (inclusive)
|
||||
// to the set's cardinality (exclusive).
|
||||
// The zero value represents an empty set.
|
||||
type SortedSet struct {
|
||||
m map[string]int
|
||||
maxLen int
|
||||
}
|
||||
|
||||
// NewSortedSet returns a SortedSet that contains all of elems,
|
||||
// but no other elements.
|
||||
func NewSortedSet(elems ...string) SortedSet {
|
||||
sort.Strings(elems)
|
||||
m := make(map[string]int)
|
||||
var maxLen int
|
||||
i := 0
|
||||
for _, s := range elems {
|
||||
if _, exists := m[s]; exists {
|
||||
continue
|
||||
}
|
||||
m[s] = i
|
||||
i++
|
||||
maxLen = max(maxLen, len(s))
|
||||
}
|
||||
return SortedSet{
|
||||
m: m,
|
||||
maxLen: maxLen,
|
||||
}
|
||||
}
|
||||
|
||||
// Size returns the cardinality of set.
|
||||
func (set SortedSet) Size() int {
|
||||
return len(set.m)
|
||||
}
|
||||
|
||||
// String sorts joins the elements of set (in lexicographical order)
|
||||
// with a comma and returns the resulting string.
|
||||
func (set SortedSet) String() string {
|
||||
elems := make([]string, len(set.m))
|
||||
for elem, i := range set.m {
|
||||
elems[i] = elem // safe indexing, by construction of SortedSet
|
||||
}
|
||||
return strings.Join(elems, ",")
|
||||
}
|
||||
|
||||
// Subsumes reports whether csv is a sequence of comma-separated names that are
|
||||
// - all elements of set,
|
||||
// - sorted in lexicographically order,
|
||||
// - unique.
|
||||
func (set SortedSet) Subsumes(csv string) bool {
|
||||
if csv == "" {
|
||||
return true
|
||||
}
|
||||
posOfLastNameSeen := -1
|
||||
chunkSize := set.maxLen + 1 // (to accommodate for at least one comma)
|
||||
for {
|
||||
// As a defense against maliciously long names in csv,
|
||||
// we only process at most chunkSize bytes per iteration.
|
||||
end := min(len(csv), chunkSize)
|
||||
comma := strings.IndexByte(csv[:end], ',')
|
||||
var name string
|
||||
if comma == -1 {
|
||||
name = csv
|
||||
} else {
|
||||
name = csv[:comma]
|
||||
}
|
||||
pos, found := set.m[name]
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
// The names in csv are expected to be sorted in lexicographical order
|
||||
// and appear at most once in csv.
|
||||
// Therefore, the positions (in set) of the names that
|
||||
// appear in csv should form a strictly increasing sequence.
|
||||
// If that's not actually the case, bail out.
|
||||
if pos <= posOfLastNameSeen {
|
||||
return false
|
||||
}
|
||||
posOfLastNameSeen = pos
|
||||
if comma < 0 { // We've now processed all the names in csv.
|
||||
break
|
||||
}
|
||||
csv = csv[comma+1:]
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// TODO: when updating go directive to 1.21 or later,
|
||||
// use min builtin instead.
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// TODO: when updating go directive to 1.21 or later,
|
||||
// use max builtin instead.
|
||||
func max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
+14
-52
@@ -1,72 +1,34 @@
|
||||
package cors
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
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)+len(w.suffix) && strings.HasPrefix(s, w.prefix) && strings.HasSuffix(s, w.suffix)
|
||||
}
|
||||
|
||||
// split compounded header values ["foo, bar", "baz"] -> ["foo", "bar", "baz"]
|
||||
func splitHeaderValues(values []string) []string {
|
||||
out := values
|
||||
copied := false
|
||||
for i, v := range values {
|
||||
needsSplit := strings.IndexByte(v, ',') != -1
|
||||
if !copied {
|
||||
if needsSplit {
|
||||
split := strings.Split(v, ",")
|
||||
out = make([]string, i, len(values)+len(split)-1)
|
||||
copy(out, values[:i])
|
||||
for _, s := range split {
|
||||
out = append(out, strings.TrimSpace(s))
|
||||
}
|
||||
copied = true
|
||||
}
|
||||
} else {
|
||||
if needsSplit {
|
||||
split := strings.Split(v, ",")
|
||||
for _, s := range split {
|
||||
out = append(out, strings.TrimSpace(s))
|
||||
}
|
||||
} else {
|
||||
out = append(out, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
return len(s) >= len(w.prefix)+len(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, _ := convertDidCopy(s, c)
|
||||
func convert(s []string, f func(string) string) []string {
|
||||
out := make([]string, len(s))
|
||||
for i := range s {
|
||||
out[i] = f(s[i])
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// convertDidCopy is same as convert but returns true if it copied the slice
|
||||
func convertDidCopy(s []string, c converter) ([]string, bool) {
|
||||
out := s
|
||||
copied := false
|
||||
for i, v := range s {
|
||||
if !copied {
|
||||
v2 := c(v)
|
||||
if v2 != v {
|
||||
out = make([]string, len(s))
|
||||
copy(out, s[:i])
|
||||
out[i] = v2
|
||||
copied = true
|
||||
}
|
||||
} else {
|
||||
out[i] = c(v)
|
||||
}
|
||||
func first(hdrs http.Header, k string) ([]string, bool) {
|
||||
v, found := hdrs[k]
|
||||
if !found || len(v) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
return out, copied
|
||||
return v[:1], true
|
||||
}
|
||||
|
||||
Vendored
+2
-4
@@ -825,9 +825,6 @@ github.com/go-asn1-ber/asn1-ber
|
||||
## explicit; go 1.14
|
||||
github.com/go-chi/chi/v5
|
||||
github.com/go-chi/chi/v5/middleware
|
||||
# github.com/go-chi/cors v1.2.1
|
||||
## explicit; go 1.14
|
||||
github.com/go-chi/cors
|
||||
# github.com/go-chi/render v1.0.3
|
||||
## explicit; go 1.16
|
||||
github.com/go-chi/render
|
||||
@@ -1712,9 +1709,10 @@ github.com/rogpeppe/go-internal/internal/syscall/windows/sysdll
|
||||
github.com/rogpeppe/go-internal/lockedfile
|
||||
github.com/rogpeppe/go-internal/lockedfile/internal/filelock
|
||||
github.com/rogpeppe/go-internal/semver
|
||||
# github.com/rs/cors v1.10.1
|
||||
# github.com/rs/cors v1.11.0
|
||||
## explicit; go 1.13
|
||||
github.com/rs/cors
|
||||
github.com/rs/cors/internal
|
||||
# github.com/rs/xid v1.5.0
|
||||
## explicit; go 1.12
|
||||
github.com/rs/xid
|
||||
|
||||
Reference in New Issue
Block a user