Skip to content

Commit fb0436c

Browse files
Fix allow_cors: true returning two Access-Control-Allow-Origin headers (#489)
* fix: `allow_cors: true` returning two `Access-Control-Allow-Origin` headers Fixes #93. The `Access-Control-Allow-Origin` was set before on the response before the proxy call, and ClickHouse was returning the response with its own `Access-Control-Allow-Origin` (in my case, "*"). So, having `allow_cors: true` was eventually returning two `Access-Control-Allow-Origin`, one from chproxy and one from ClickHouse. This commit just move the `"Access-Control-Allow-Origin` after the proxy call, overriding the value returned by ClickHouse. If `allow_cors: false`, chproxy does not change the value (so it can be, I believe, any value set by ClickHouse), else, with `allow_cors: true`, it will override to either the value of `Origin` request if any or else `*`. * fix: add tests for allow CORS behavior * fix failing tests * add failing test * fix failing tests * add TODO --------- Co-authored-by: Christophe Kalenzaga <mgachka@aol.com>
1 parent d8674fd commit fb0436c

File tree

3 files changed

+83
-8
lines changed

3 files changed

+83
-8
lines changed

main_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -896,6 +896,64 @@ func TestServe(t *testing.T) {
896896
},
897897
startHTTP,
898898
},
899+
{
900+
"http allow CORS false",
901+
"testdata/http.yml",
902+
func(t *testing.T) {
903+
// TODO: rework this test because it doesn't fails when it should
904+
// cf the discussion in https://github.com/ContentSquare/chproxy/pull/489
905+
q := "cors"
906+
req, err := http.NewRequest("GET", "http://127.0.0.1:9090?query="+url.QueryEscape(q), nil)
907+
checkErr(t, err)
908+
resp, err := http.DefaultClient.Do(req)
909+
checkErr(t, err)
910+
if resp.StatusCode != http.StatusOK {
911+
t.Fatalf("unexpected status code: %d; expected: %d", resp.StatusCode, http.StatusOK)
912+
}
913+
defer resp.Body.Close()
914+
checkHeader(t, resp, "Access-Control-Allow-Origin", "*")
915+
},
916+
startHTTP,
917+
},
918+
{
919+
"http allow CORS true without request Origin header",
920+
"testdata/http.allow.cors.yml",
921+
func(t *testing.T) {
922+
// TODO: rework this test because it doesn't fails when it should
923+
// cf the discussion in https://github.com/ContentSquare/chproxy/pull/489
924+
q := "cors"
925+
req, err := http.NewRequest("GET", "http://127.0.0.1:9090?query="+url.QueryEscape(q), nil)
926+
checkErr(t, err)
927+
resp, err := http.DefaultClient.Do(req)
928+
checkErr(t, err)
929+
if resp.StatusCode != http.StatusOK {
930+
t.Fatalf("unexpected status code: %d; expected: %d", resp.StatusCode, http.StatusOK)
931+
}
932+
defer resp.Body.Close()
933+
checkHeader(t, resp, "Access-Control-Allow-Origin", "*")
934+
},
935+
startHTTP,
936+
},
937+
{
938+
"http allow CORS true with request Origin header",
939+
"testdata/http.allow.cors.yml",
940+
func(t *testing.T) {
941+
// TODO: rework this test because it doesn't fails when it should
942+
// cf the discussion in https://github.com/ContentSquare/chproxy/pull/489
943+
q := "cors"
944+
req, err := http.NewRequest("GET", "http://127.0.0.1:9090?query="+url.QueryEscape(q), nil)
945+
checkErr(t, err)
946+
req.Header.Set("Origin", "http://example.com")
947+
resp, err := http.DefaultClient.Do(req)
948+
checkErr(t, err)
949+
if resp.StatusCode != http.StatusOK {
950+
t.Fatalf("unexpected status code: %d; expected: %d", resp.StatusCode, http.StatusOK)
951+
}
952+
defer resp.Body.Close()
953+
checkHeader(t, resp, "Access-Control-Allow-Origin", "http://example.com")
954+
},
955+
startHTTP,
956+
},
899957
}
900958

901959
// Wait until CHServer starts.
@@ -1108,6 +1166,8 @@ func fakeCHHandler(w http.ResponseWriter, r *http.Request) {
11081166
// execute sleep 1.5 sec
11091167
time.Sleep(1500 * time.Millisecond)
11101168
fmt.Fprint(w, b)
1169+
case q == "cors":
1170+
w.Header().Set("Access-Control-Allow-Origin", "*")
11111171
default:
11121172
if strings.Contains(string(query), killQueryPattern) {
11131173
fakeCHState.kill()

proxy.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -107,14 +107,6 @@ func (rp *reverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
107107
log.Debugf("%s: request start", s)
108108
requestSum.With(s.labels).Inc()
109109

110-
if s.user.allowCORS {
111-
origin := req.Header.Get("Origin")
112-
if len(origin) == 0 {
113-
origin = "*"
114-
}
115-
rw.Header().Set("Access-Control-Allow-Origin", origin)
116-
}
117-
118110
req.Body = &statReadCloser{
119111
ReadCloser: req.Body,
120112
bytesRead: requestBodyBytes.With(s.labels),
@@ -149,6 +141,14 @@ func (rp *reverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
149141
rp.proxyRequest(s, srw, srw, req)
150142
}
151143

144+
if s.user.allowCORS {
145+
origin := req.Header.Get("Origin")
146+
if len(origin) == 0 {
147+
origin = "*"
148+
}
149+
rw.Header().Set("Access-Control-Allow-Origin", origin)
150+
}
151+
152152
// It is safe calling getQuerySnippet here, since the request
153153
// has been already read in proxyRequest or serveFromCache.
154154
query := getQuerySnippet(req)

testdata/http.allow.cors.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
log_debug: true
2+
server:
3+
http:
4+
listen_addr: ":9090"
5+
allowed_networks: ["127.0.0.1/24"]
6+
7+
users:
8+
- name: "default"
9+
to_cluster: "default"
10+
to_user: "default"
11+
allow_cors: true
12+
13+
clusters:
14+
- name: "default"
15+
nodes: ["127.0.0.1:18124"]

0 commit comments

Comments
 (0)