123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531 |
- package cors
- import (
- "net/http"
- "net/http/httptest"
- "regexp"
- "strings"
- "testing"
- )
- var testHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- w.Write([]byte("bar"))
- })
- var allHeaders = []string{
- "Vary",
- "Access-Control-Allow-Origin",
- "Access-Control-Allow-Methods",
- "Access-Control-Allow-Headers",
- "Access-Control-Allow-Credentials",
- "Access-Control-Max-Age",
- "Access-Control-Expose-Headers",
- }
- func assertHeaders(t *testing.T, resHeaders http.Header, expHeaders map[string]string) {
- for _, name := range allHeaders {
- got := strings.Join(resHeaders[name], ", ")
- want := expHeaders[name]
- if got != want {
- t.Errorf("Response header %q = %q, want %q", name, got, want)
- }
- }
- }
- func assertResponse(t *testing.T, res *httptest.ResponseRecorder, responseCode int) {
- if responseCode != res.Code {
- t.Errorf("assertResponse: expected response code to be %d but got %d. ", responseCode, res.Code)
- }
- }
- func TestSpec(t *testing.T) {
- cases := []struct {
- name string
- options Options
- method string
- reqHeaders map[string]string
- resHeaders map[string]string
- }{
- {
- "NoConfig",
- Options{
- // Intentionally left blank.
- },
- "GET",
- map[string]string{},
- map[string]string{
- "Vary": "Origin",
- },
- },
- {
- "MatchAllOrigin",
- Options{
- AllowedOrigins: []string{"*"},
- },
- "GET",
- map[string]string{
- "Origin": "http://foobar.com",
- },
- map[string]string{
- "Vary": "Origin",
- "Access-Control-Allow-Origin": "*",
- },
- },
- {
- "MatchAllOriginWithCredentials",
- Options{
- AllowedOrigins: []string{"*"},
- AllowCredentials: true,
- },
- "GET",
- map[string]string{
- "Origin": "http://foobar.com",
- },
- map[string]string{
- "Vary": "Origin",
- "Access-Control-Allow-Origin": "*",
- "Access-Control-Allow-Credentials": "true",
- },
- },
- {
- "AllowedOrigin",
- Options{
- AllowedOrigins: []string{"http://foobar.com"},
- },
- "GET",
- map[string]string{
- "Origin": "http://foobar.com",
- },
- map[string]string{
- "Vary": "Origin",
- "Access-Control-Allow-Origin": "http://foobar.com",
- },
- },
- {
- "WildcardOrigin",
- Options{
- AllowedOrigins: []string{"http://*.bar.com"},
- },
- "GET",
- map[string]string{
- "Origin": "http://foo.bar.com",
- },
- map[string]string{
- "Vary": "Origin",
- "Access-Control-Allow-Origin": "http://foo.bar.com",
- },
- },
- {
- "DisallowedOrigin",
- Options{
- AllowedOrigins: []string{"http://foobar.com"},
- },
- "GET",
- map[string]string{
- "Origin": "http://barbaz.com",
- },
- map[string]string{
- "Vary": "Origin",
- },
- },
- {
- "DisallowedWildcardOrigin",
- Options{
- AllowedOrigins: []string{"http://*.bar.com"},
- },
- "GET",
- map[string]string{
- "Origin": "http://foo.baz.com",
- },
- map[string]string{
- "Vary": "Origin",
- },
- },
- {
- "AllowedOriginFuncMatch",
- Options{
- AllowOriginFunc: func(o string) bool {
- return regexp.MustCompile("^http://foo").MatchString(o)
- },
- },
- "GET",
- map[string]string{
- "Origin": "http://foobar.com",
- },
- map[string]string{
- "Vary": "Origin",
- "Access-Control-Allow-Origin": "http://foobar.com",
- },
- },
- {
- "AllowOriginRequestFuncMatch",
- Options{
- AllowOriginRequestFunc: func(r *http.Request, o string) bool {
- return regexp.MustCompile("^http://foo").MatchString(o) && r.Header.Get("Authorization") == "secret"
- },
- },
- "GET",
- map[string]string{
- "Origin": "http://foobar.com",
- "Authorization": "secret",
- },
- map[string]string{
- "Vary": "Origin",
- "Access-Control-Allow-Origin": "http://foobar.com",
- },
- },
- {
- "AllowOriginRequestFuncNotMatch",
- Options{
- AllowOriginRequestFunc: func(r *http.Request, o string) bool {
- return regexp.MustCompile("^http://foo").MatchString(o) && r.Header.Get("Authorization") == "secret"
- },
- },
- "GET",
- map[string]string{
- "Origin": "http://foobar.com",
- "Authorization": "not-secret",
- },
- map[string]string{
- "Vary": "Origin",
- },
- },
- {
- "MaxAge",
- Options{
- AllowedOrigins: []string{"http://example.com/"},
- AllowedMethods: []string{"GET"},
- MaxAge: 10,
- },
- "OPTIONS",
- map[string]string{
- "Origin": "http://example.com/",
- "Access-Control-Request-Method": "GET",
- },
- map[string]string{
- "Vary": "Origin, Access-Control-Request-Method, Access-Control-Request-Headers",
- "Access-Control-Allow-Origin": "http://example.com/",
- "Access-Control-Allow-Methods": "GET",
- "Access-Control-Max-Age": "10",
- },
- },
- {
- "AllowedMethod",
- Options{
- AllowedOrigins: []string{"http://foobar.com"},
- AllowedMethods: []string{"PUT", "DELETE"},
- },
- "OPTIONS",
- map[string]string{
- "Origin": "http://foobar.com",
- "Access-Control-Request-Method": "PUT",
- },
- map[string]string{
- "Vary": "Origin, Access-Control-Request-Method, Access-Control-Request-Headers",
- "Access-Control-Allow-Origin": "http://foobar.com",
- "Access-Control-Allow-Methods": "PUT",
- },
- },
- {
- "DisallowedMethod",
- Options{
- AllowedOrigins: []string{"http://foobar.com"},
- AllowedMethods: []string{"PUT", "DELETE"},
- },
- "OPTIONS",
- map[string]string{
- "Origin": "http://foobar.com",
- "Access-Control-Request-Method": "PATCH",
- },
- map[string]string{
- "Vary": "Origin, Access-Control-Request-Method, Access-Control-Request-Headers",
- },
- },
- {
- "AllowedHeaders",
- Options{
- AllowedOrigins: []string{"http://foobar.com"},
- AllowedHeaders: []string{"X-Header-1", "x-header-2"},
- },
- "OPTIONS",
- map[string]string{
- "Origin": "http://foobar.com",
- "Access-Control-Request-Method": "GET",
- "Access-Control-Request-Headers": "X-Header-2, X-HEADER-1",
- },
- map[string]string{
- "Vary": "Origin, Access-Control-Request-Method, Access-Control-Request-Headers",
- "Access-Control-Allow-Origin": "http://foobar.com",
- "Access-Control-Allow-Methods": "GET",
- "Access-Control-Allow-Headers": "X-Header-2, X-Header-1",
- },
- },
- {
- "DefaultAllowedHeaders",
- Options{
- AllowedOrigins: []string{"http://foobar.com"},
- AllowedHeaders: []string{},
- },
- "OPTIONS",
- map[string]string{
- "Origin": "http://foobar.com",
- "Access-Control-Request-Method": "GET",
- "Access-Control-Request-Headers": "X-Requested-With",
- },
- map[string]string{
- "Vary": "Origin, Access-Control-Request-Method, Access-Control-Request-Headers",
- "Access-Control-Allow-Origin": "http://foobar.com",
- "Access-Control-Allow-Methods": "GET",
- "Access-Control-Allow-Headers": "X-Requested-With",
- },
- },
- {
- "AllowedWildcardHeader",
- Options{
- AllowedOrigins: []string{"http://foobar.com"},
- AllowedHeaders: []string{"*"},
- },
- "OPTIONS",
- map[string]string{
- "Origin": "http://foobar.com",
- "Access-Control-Request-Method": "GET",
- "Access-Control-Request-Headers": "X-Header-2, X-HEADER-1",
- },
- map[string]string{
- "Vary": "Origin, Access-Control-Request-Method, Access-Control-Request-Headers",
- "Access-Control-Allow-Origin": "http://foobar.com",
- "Access-Control-Allow-Methods": "GET",
- "Access-Control-Allow-Headers": "X-Header-2, X-Header-1",
- },
- },
- {
- "DisallowedHeader",
- Options{
- AllowedOrigins: []string{"http://foobar.com"},
- AllowedHeaders: []string{"X-Header-1", "x-header-2"},
- },
- "OPTIONS",
- map[string]string{
- "Origin": "http://foobar.com",
- "Access-Control-Request-Method": "GET",
- "Access-Control-Request-Headers": "X-Header-3, X-Header-1",
- },
- map[string]string{
- "Vary": "Origin, Access-Control-Request-Method, Access-Control-Request-Headers",
- },
- },
- {
- "OriginHeader",
- Options{
- AllowedOrigins: []string{"http://foobar.com"},
- },
- "OPTIONS",
- map[string]string{
- "Origin": "http://foobar.com",
- "Access-Control-Request-Method": "GET",
- "Access-Control-Request-Headers": "origin",
- },
- map[string]string{
- "Vary": "Origin, Access-Control-Request-Method, Access-Control-Request-Headers",
- "Access-Control-Allow-Origin": "http://foobar.com",
- "Access-Control-Allow-Methods": "GET",
- "Access-Control-Allow-Headers": "Origin",
- },
- },
- {
- "ExposedHeader",
- Options{
- AllowedOrigins: []string{"http://foobar.com"},
- ExposedHeaders: []string{"X-Header-1", "x-header-2"},
- },
- "GET",
- map[string]string{
- "Origin": "http://foobar.com",
- },
- map[string]string{
- "Vary": "Origin",
- "Access-Control-Allow-Origin": "http://foobar.com",
- "Access-Control-Expose-Headers": "X-Header-1, X-Header-2",
- },
- },
- {
- "AllowedCredentials",
- Options{
- AllowedOrigins: []string{"http://foobar.com"},
- AllowCredentials: true,
- },
- "OPTIONS",
- map[string]string{
- "Origin": "http://foobar.com",
- "Access-Control-Request-Method": "GET",
- },
- map[string]string{
- "Vary": "Origin, Access-Control-Request-Method, Access-Control-Request-Headers",
- "Access-Control-Allow-Origin": "http://foobar.com",
- "Access-Control-Allow-Methods": "GET",
- "Access-Control-Allow-Credentials": "true",
- },
- },
- {
- "OptionPassthrough",
- Options{
- OptionsPassthrough: true,
- },
- "OPTIONS",
- map[string]string{
- "Origin": "http://foobar.com",
- "Access-Control-Request-Method": "GET",
- },
- map[string]string{
- "Vary": "Origin, Access-Control-Request-Method, Access-Control-Request-Headers",
- "Access-Control-Allow-Origin": "*",
- "Access-Control-Allow-Methods": "GET",
- },
- },
- {
- "NonPreflightOptions",
- Options{
- AllowedOrigins: []string{"http://foobar.com"},
- },
- "OPTIONS",
- map[string]string{
- "Origin": "http://foobar.com",
- },
- map[string]string{
- "Vary": "Origin",
- "Access-Control-Allow-Origin": "http://foobar.com",
- },
- },
- }
- for i := range cases {
- tc := cases[i]
- t.Run(tc.name, func(t *testing.T) {
- s := New(tc.options)
- req, _ := http.NewRequest(tc.method, "http://example.com/foo", nil)
- for name, value := range tc.reqHeaders {
- req.Header.Add(name, value)
- }
- t.Run("Handler", func(t *testing.T) {
- res := httptest.NewRecorder()
- s.Handler(testHandler).ServeHTTP(res, req)
- assertHeaders(t, res.Header(), tc.resHeaders)
- })
- t.Run("HandlerFunc", func(t *testing.T) {
- res := httptest.NewRecorder()
- s.HandlerFunc(res, req)
- assertHeaders(t, res.Header(), tc.resHeaders)
- })
- t.Run("Negroni", func(t *testing.T) {
- res := httptest.NewRecorder()
- s.ServeHTTP(res, req, testHandler)
- assertHeaders(t, res.Header(), tc.resHeaders)
- })
- })
- }
- }
- func TestDebug(t *testing.T) {
- s := New(Options{
- Debug: true,
- })
- if s.Log == nil {
- t.Error("Logger not created when debug=true")
- }
- }
- func TestDefault(t *testing.T) {
- s := Default()
- if s.Log != nil {
- t.Error("c.log should be nil when Default")
- }
- if !s.allowedOriginsAll {
- t.Error("c.allowedOriginsAll should be true when Default")
- }
- if s.allowedHeaders == nil {
- t.Error("c.allowedHeaders should be nil when Default")
- }
- if s.allowedMethods == nil {
- t.Error("c.allowedMethods should be nil when Default")
- }
- }
- func TestHandlePreflightInvalidOriginAbortion(t *testing.T) {
- s := New(Options{
- AllowedOrigins: []string{"http://foo.com"},
- })
- res := httptest.NewRecorder()
- req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil)
- req.Header.Add("Origin", "http://example.com/")
- s.handlePreflight(res, req)
- assertHeaders(t, res.Header(), map[string]string{
- "Vary": "Origin, Access-Control-Request-Method, Access-Control-Request-Headers",
- })
- }
- func TestHandlePreflightNoOptionsAbortion(t *testing.T) {
- s := New(Options{
- // Intentionally left blank.
- })
- res := httptest.NewRecorder()
- req, _ := http.NewRequest("GET", "http://example.com/foo", nil)
- s.handlePreflight(res, req)
- assertHeaders(t, res.Header(), map[string]string{})
- }
- func TestHandleActualRequestInvalidOriginAbortion(t *testing.T) {
- s := New(Options{
- AllowedOrigins: []string{"http://foo.com"},
- })
- res := httptest.NewRecorder()
- req, _ := http.NewRequest("GET", "http://example.com/foo", nil)
- req.Header.Add("Origin", "http://example.com/")
- s.handleActualRequest(res, req)
- assertHeaders(t, res.Header(), map[string]string{
- "Vary": "Origin",
- })
- }
- func TestHandleActualRequestInvalidMethodAbortion(t *testing.T) {
- s := New(Options{
- AllowedMethods: []string{"POST"},
- AllowCredentials: true,
- })
- res := httptest.NewRecorder()
- req, _ := http.NewRequest("GET", "http://example.com/foo", nil)
- req.Header.Add("Origin", "http://example.com/")
- s.handleActualRequest(res, req)
- assertHeaders(t, res.Header(), map[string]string{
- "Vary": "Origin",
- })
- }
- func TestIsMethodAllowedReturnsFalseWithNoMethods(t *testing.T) {
- s := New(Options{
- // Intentionally left blank.
- })
- s.allowedMethods = []string{}
- if s.isMethodAllowed("") {
- t.Error("IsMethodAllowed should return false when c.allowedMethods is nil.")
- }
- }
- func TestIsMethodAllowedReturnsTrueWithOptions(t *testing.T) {
- s := New(Options{
- // Intentionally left blank.
- })
- if !s.isMethodAllowed("OPTIONS") {
- t.Error("IsMethodAllowed should return true when c.allowedMethods is nil.")
- }
- }
|