-
Notifications
You must be signed in to change notification settings - Fork 460
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Copy cors from etcd, since they eliminated the package (#1320)
A small hack I did caught up to me--etcd had a reasonable cors middleware implementation, which I added to m3query. They later removed the package (not surprisingly--it was a random thing to expose). This causes glide up to fail. To fix this, I copied in the package to x/net/http. We could instead add a different dependency (e.g. https://github.com/rs/cors). I mostly went with the copy route because the package is quite small and it didn't seem worth the added dep.
- Loading branch information
1 parent
84b1341
commit bd05bcd
Showing
4 changed files
with
260 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
M3DB includes derived work from etcd (https://github.com/etcd-io/etcd) under the Apache License 2.0: | ||
|
||
Copyright 2015 The etcd Authors | ||
|
||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
|
||
http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
|
||
The derived work can be found in the files: | ||
- src/x/net/http/cors/cors.go | ||
- src/x/net/http/cors/cors_test.go |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
// Copyright (c) 2018 Uber Technologies, Inc. | ||
// | ||
// 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. | ||
|
||
// Derived from https://github.com/etcd-io/etcd/tree/v3.2.10/pkg/cors under | ||
// http://www.apache.org/licenses/LICENSE-2.0#redistribution . | ||
// See https://github.com/m3db/m3/blob/master/NOTICES.txt for the original copyright. | ||
|
||
// Package cors handles cross-origin HTTP requests (CORS). | ||
package cors | ||
|
||
import ( | ||
"fmt" | ||
"net/http" | ||
"net/url" | ||
"sort" | ||
"strings" | ||
) | ||
|
||
// Info represents a set of allowed origins. | ||
type Info map[string]bool | ||
|
||
// Set implements the flag.Value interface to allow users to define a list of CORS origins | ||
func (ci *Info) Set(s string) error { | ||
m := make(map[string]bool) | ||
for _, v := range strings.Split(s, ",") { | ||
v = strings.TrimSpace(v) | ||
if v == "" { | ||
continue | ||
} | ||
if v != "*" { | ||
if _, err := url.Parse(v); err != nil { | ||
return fmt.Errorf("Invalid CORS origin: %s", err) | ||
} | ||
} | ||
m[v] = true | ||
|
||
} | ||
*ci = Info(m) | ||
return nil | ||
} | ||
|
||
func (ci *Info) String() string { | ||
o := make([]string, 0) | ||
for k := range *ci { | ||
o = append(o, k) | ||
} | ||
sort.StringSlice(o).Sort() | ||
return strings.Join(o, ",") | ||
} | ||
|
||
// OriginAllowed determines whether the server will allow a given CORS origin. | ||
func (ci Info) OriginAllowed(origin string) bool { | ||
return ci["*"] || ci[origin] | ||
} | ||
|
||
// Handler wraps an http.Handler instance to provide configurable CORS support. CORS headers will be added to all | ||
// responses. | ||
type Handler struct { | ||
Handler http.Handler | ||
Info *Info | ||
} | ||
|
||
// addHeader adds the correct cors headers given an origin | ||
func (h *Handler) addHeader(w http.ResponseWriter, origin string) { | ||
w.Header().Add("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE") | ||
w.Header().Add("Access-Control-Allow-Origin", origin) | ||
w.Header().Add("Access-Control-Allow-Headers", "accept, content-type, authorization") | ||
} | ||
|
||
// ServeHTTP adds the correct CORS headers based on the origin and returns immediately | ||
// with a 200 OK if the method is OPTIONS. | ||
func (h *Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { | ||
// Write CORS header. | ||
if h.Info.OriginAllowed("*") { | ||
h.addHeader(w, "*") | ||
} else if origin := req.Header.Get("Origin"); h.Info.OriginAllowed(origin) { | ||
h.addHeader(w, origin) | ||
} | ||
|
||
if req.Method == "OPTIONS" { | ||
w.WriteHeader(http.StatusOK) | ||
return | ||
} | ||
|
||
h.Handler.ServeHTTP(w, req) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
// Copyright (c) 2018 Uber Technologies, Inc. | ||
// | ||
// 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. | ||
// | ||
// Derived from https://github.com/etcd-io/etcd/tree/v3.2.10/pkg/cors under | ||
// http://www.apache.org/licenses/LICENSE-2.0#redistribution . | ||
// See https://github.com/m3db/m3/blob/master/NOTICES.txt for the original copyright. | ||
|
||
package cors | ||
|
||
import ( | ||
"net/http" | ||
"net/http/httptest" | ||
"reflect" | ||
"testing" | ||
) | ||
|
||
func TestCORSInfo(t *testing.T) { | ||
tests := []struct { | ||
s string | ||
winfo Info | ||
ws string | ||
}{ | ||
{"", Info{}, ""}, | ||
{"http://127.0.0.1", Info{"http://127.0.0.1": true}, "http://127.0.0.1"}, | ||
{"*", Info{"*": true}, "*"}, | ||
// with space around | ||
{" http://127.0.0.1 ", Info{"http://127.0.0.1": true}, "http://127.0.0.1"}, | ||
// multiple addrs | ||
{ | ||
"http://127.0.0.1,http://127.0.0.2", | ||
Info{"http://127.0.0.1": true, "http://127.0.0.2": true}, | ||
"http://127.0.0.1,http://127.0.0.2", | ||
}, | ||
} | ||
for i, tt := range tests { | ||
info := Info{} | ||
if err := info.Set(tt.s); err != nil { | ||
t.Errorf("#%d: set error = %v, want nil", i, err) | ||
} | ||
if !reflect.DeepEqual(info, tt.winfo) { | ||
t.Errorf("#%d: info = %v, want %v", i, info, tt.winfo) | ||
} | ||
if g := info.String(); g != tt.ws { | ||
t.Errorf("#%d: info string = %s, want %s", i, g, tt.ws) | ||
} | ||
} | ||
} | ||
|
||
func TestCORSInfoOriginAllowed(t *testing.T) { | ||
tests := []struct { | ||
set string | ||
origin string | ||
wallowed bool | ||
}{ | ||
{"http://127.0.0.1,http://127.0.0.2", "http://127.0.0.1", true}, | ||
{"http://127.0.0.1,http://127.0.0.2", "http://127.0.0.2", true}, | ||
{"http://127.0.0.1,http://127.0.0.2", "*", false}, | ||
{"http://127.0.0.1,http://127.0.0.2", "http://127.0.0.3", false}, | ||
{"*", "*", true}, | ||
{"*", "http://127.0.0.1", true}, | ||
} | ||
for i, tt := range tests { | ||
info := Info{} | ||
if err := info.Set(tt.set); err != nil { | ||
t.Errorf("#%d: set error = %v, want nil", i, err) | ||
} | ||
if g := info.OriginAllowed(tt.origin); g != tt.wallowed { | ||
t.Errorf("#%d: allowed = %v, want %v", i, g, tt.wallowed) | ||
} | ||
} | ||
} | ||
|
||
func TestCORSHandler(t *testing.T) { | ||
info := &Info{} | ||
if err := info.Set("http://127.0.0.1,http://127.0.0.2"); err != nil { | ||
t.Fatalf("unexpected set error: %v", err) | ||
} | ||
h := &Handler{ | ||
Handler: http.NotFoundHandler(), | ||
Info: info, | ||
} | ||
|
||
header := func(origin string) http.Header { | ||
return http.Header{ | ||
"Access-Control-Allow-Methods": []string{"POST, GET, OPTIONS, PUT, DELETE"}, | ||
"Access-Control-Allow-Origin": []string{origin}, | ||
"Access-Control-Allow-Headers": []string{"accept, content-type, authorization"}, | ||
} | ||
} | ||
tests := []struct { | ||
method string | ||
origin string | ||
wcode int | ||
wheader http.Header | ||
}{ | ||
{"GET", "http://127.0.0.1", http.StatusNotFound, header("http://127.0.0.1")}, | ||
{"GET", "http://127.0.0.2", http.StatusNotFound, header("http://127.0.0.2")}, | ||
{"GET", "http://127.0.0.3", http.StatusNotFound, http.Header{}}, | ||
{"OPTIONS", "http://127.0.0.1", http.StatusOK, header("http://127.0.0.1")}, | ||
} | ||
for i, tt := range tests { | ||
rr := httptest.NewRecorder() | ||
req := &http.Request{ | ||
Method: tt.method, | ||
Header: http.Header{"Origin": []string{tt.origin}}, | ||
} | ||
h.ServeHTTP(rr, req) | ||
if rr.Code != tt.wcode { | ||
t.Errorf("#%d: code = %v, want %v", i, rr.Code, tt.wcode) | ||
} | ||
// it is set by http package, and there is no need to test it | ||
rr.HeaderMap.Del("Content-Type") | ||
rr.HeaderMap.Del("X-Content-Type-Options") | ||
if !reflect.DeepEqual(rr.HeaderMap, tt.wheader) { | ||
t.Errorf("#%d: header = %+v, want %+v", i, rr.HeaderMap, tt.wheader) | ||
} | ||
} | ||
} |