mirror of
https://github.com/zeromicro/go-zero.git
synced 2025-02-02 16:28:39 +08:00
feat: support rest.WithCorsHeaders to customize cors headers (#4284)
This commit is contained in:
parent
c2421beb25
commit
2588a36555
@ -26,6 +26,11 @@ const (
|
|||||||
originHeader = "Origin"
|
originHeader = "Origin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// AddAllowHeaders sets the allowed headers.
|
||||||
|
func AddAllowHeaders(header http.Header, headers ...string) {
|
||||||
|
header.Add(allowHeaders, strings.Join(headers, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
// NotAllowedHandler handles cross domain not allowed requests.
|
// NotAllowedHandler handles cross domain not allowed requests.
|
||||||
// At most one origin can be specified, other origins are ignored if given, default to be *.
|
// At most one origin can be specified, other origins are ignored if given, default to be *.
|
||||||
func NotAllowedHandler(fn func(w http.ResponseWriter), origins ...string) http.Handler {
|
func NotAllowedHandler(fn func(w http.ResponseWriter), origins ...string) http.Handler {
|
||||||
|
@ -3,11 +3,80 @@ package cors
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestAddAllowHeaders(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
initial string
|
||||||
|
headers []string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "single header",
|
||||||
|
initial: "",
|
||||||
|
headers: []string{"Content-Type"},
|
||||||
|
expected: "Content-Type",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple headers",
|
||||||
|
initial: "",
|
||||||
|
headers: []string{"Content-Type", "Authorization", "X-Requested-With"},
|
||||||
|
expected: "Content-Type, Authorization, X-Requested-With",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "add to existing headers",
|
||||||
|
initial: "Origin, Accept",
|
||||||
|
headers: []string{"Content-Type"},
|
||||||
|
expected: "Origin, Accept, Content-Type",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no headers",
|
||||||
|
initial: "",
|
||||||
|
headers: []string{},
|
||||||
|
expected: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
header := http.Header{}
|
||||||
|
headers := make(map[string]struct{})
|
||||||
|
if tt.initial != "" {
|
||||||
|
header.Set(allowHeaders, tt.initial)
|
||||||
|
vals := strings.Split(tt.initial, ", ")
|
||||||
|
for _, v := range vals {
|
||||||
|
headers[v] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, h := range tt.headers {
|
||||||
|
headers[h] = struct{}{}
|
||||||
|
}
|
||||||
|
AddAllowHeaders(header, tt.headers...)
|
||||||
|
var actual []string
|
||||||
|
vals := header.Values(allowHeaders)
|
||||||
|
for _, v := range vals {
|
||||||
|
bunch := strings.Split(v, ", ")
|
||||||
|
for _, b := range bunch {
|
||||||
|
if len(b) > 0 {
|
||||||
|
actual = append(actual, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var expect []string
|
||||||
|
for k := range headers {
|
||||||
|
expect = append(expect, k)
|
||||||
|
}
|
||||||
|
assert.ElementsMatch(t, expect, actual)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCorsHandlerWithOrigins(t *testing.T) {
|
func TestCorsHandlerWithOrigins(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -161,6 +161,18 @@ func WithCors(origin ...string) RunOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithCorsHeaders returns a RunOption to enable CORS with given headers.
|
||||||
|
func WithCorsHeaders(headers ...string) RunOption {
|
||||||
|
const allDomains = "*"
|
||||||
|
|
||||||
|
return func(server *Server) {
|
||||||
|
server.router.SetNotAllowedHandler(cors.NotAllowedHandler(nil, allDomains))
|
||||||
|
server.router = newCorsRouter(server.router, func(header http.Header) {
|
||||||
|
cors.AddAllowHeaders(header, headers...)
|
||||||
|
}, allDomains)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WithCustomCors returns a func to enable CORS for given origin, or default to all origins (*),
|
// WithCustomCors returns a func to enable CORS for given origin, or default to all origins (*),
|
||||||
// fn lets caller customizing the response.
|
// fn lets caller customizing the response.
|
||||||
func WithCustomCors(middlewareFn func(header http.Header), notAllowedFn func(http.ResponseWriter),
|
func WithCustomCors(middlewareFn func(header http.Header), notAllowedFn func(http.ResponseWriter),
|
||||||
|
@ -420,6 +420,64 @@ Port: 54321
|
|||||||
opt(svr)
|
opt(svr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWithCorsHeaders(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
headers []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "single header",
|
||||||
|
headers: []string{"UserHeader"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple headers",
|
||||||
|
headers: []string{"UserHeader", "X-Requested-With"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no headers",
|
||||||
|
headers: []string{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
const configYaml = `
|
||||||
|
Name: foo
|
||||||
|
Port: 54321
|
||||||
|
`
|
||||||
|
var cnf RestConf
|
||||||
|
assert.Nil(t, conf.LoadFromYamlBytes([]byte(configYaml), &cnf))
|
||||||
|
rt := router.NewRouter()
|
||||||
|
svr, err := NewServer(cnf, WithRouter(rt))
|
||||||
|
assert.Nil(t, err)
|
||||||
|
defer svr.Stop()
|
||||||
|
option := WithCorsHeaders(tt.headers...)
|
||||||
|
option(svr)
|
||||||
|
|
||||||
|
// Assuming newCorsRouter sets headers correctly,
|
||||||
|
// we would need to verify the behavior here. Since we don't have
|
||||||
|
// direct access to headers, we'll mock newCorsRouter to capture it.
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
svr.ServeHTTP(w, httptest.NewRequest(http.MethodOptions, "/", nil))
|
||||||
|
|
||||||
|
vals := w.Header().Values("Access-Control-Allow-Headers")
|
||||||
|
respHeaders := make(map[string]struct{})
|
||||||
|
for _, header := range vals {
|
||||||
|
headers := strings.Split(header, ", ")
|
||||||
|
for _, h := range headers {
|
||||||
|
if len(h) > 0 {
|
||||||
|
respHeaders[h] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, h := range tt.headers {
|
||||||
|
_, ok := respHeaders[h]
|
||||||
|
assert.Truef(t, ok, "expected header %s not found", h)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestServer_PrintRoutes(t *testing.T) {
|
func TestServer_PrintRoutes(t *testing.T) {
|
||||||
const (
|
const (
|
||||||
configYaml = `
|
configYaml = `
|
||||||
|
Loading…
Reference in New Issue
Block a user