Skip to content

Commit fa32453

Browse files
mo4islonatoothrot
authored andcommitted
playground: enable CORS on /fmt and /p/ routes
Previously, only the /vet, /compile, and /share routes supported Cross-Origin Resource Sharing (CORS) from any domain. This change enables CORS on the /p/ and /fmt endpoints as well. That makes it possible for third-party sites to load and format playground snippets from the frontend. Fixes golang/go#35019 Change-Id: I185f518257082cbb5ccd848b410ff408bc077340 Reviewed-on: https://go-review.googlesource.com/c/playground/+/193798 Reviewed-by: Alexander Rakoczy <[email protected]>
1 parent 09dfaa1 commit fa32453

File tree

4 files changed

+71
-26
lines changed

4 files changed

+71
-26
lines changed

edit.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ type editData struct {
2626
}
2727

2828
func (s *server) handleEdit(w http.ResponseWriter, r *http.Request) {
29+
w.Header().Set("Access-Control-Allow-Origin", "*")
30+
if r.Method == "OPTIONS" {
31+
// This is likely a pre-flight CORS request.
32+
return
33+
}
34+
2935
// Redirect foo.play.golang.org to play.golang.org.
3036
if strings.HasSuffix(r.Host, "."+hostname) {
3137
http.Redirect(w, r, "https://"+hostname, http.StatusFound)

fmt.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ type fmtResponse struct {
2121
}
2222

2323
func handleFmt(w http.ResponseWriter, r *http.Request) {
24+
w.Header().Set("Access-Control-Allow-Origin", "*")
25+
if r.Method == "OPTIONS" {
26+
// This is likely a pre-flight CORS request.
27+
return
28+
}
2429
w.Header().Set("Content-Type", "application/json")
2530

2631
fs, err := splitFiles([]byte(r.FormValue("body")))

fmt_test.go

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package main
66

77
import (
88
"encoding/json"
9+
"net/http"
910
"net/http/httptest"
1011
"net/url"
1112
"strings"
@@ -15,83 +16,102 @@ import (
1516
func TestHandleFmt(t *testing.T) {
1617
for _, tt := range []struct {
1718
name string
19+
method string
1820
body string
1921
imports bool
2022
want string
2123
wantErr string
2224
}{
2325
{
24-
name: "classic",
25-
body: " package main\n func main( ) { }\n",
26-
want: "package main\n\nfunc main() {}\n",
26+
name: "OPTIONS no-op",
27+
method: http.MethodOptions,
28+
},
29+
{
30+
name: "classic",
31+
method: http.MethodPost,
32+
body: " package main\n func main( ) { }\n",
33+
want: "package main\n\nfunc main() {}\n",
2734
},
2835
{
2936
name: "classic_goimports",
37+
method: http.MethodPost,
3038
body: " package main\nvar _ = fmt.Printf",
3139
imports: true,
3240
want: "package main\n\nimport \"fmt\"\n\nvar _ = fmt.Printf\n",
3341
},
3442
{
35-
name: "single_go_with_header",
36-
body: "-- prog.go --\n package main",
37-
want: "-- prog.go --\npackage main\n",
43+
name: "single_go_with_header",
44+
method: http.MethodPost,
45+
body: "-- prog.go --\n package main",
46+
want: "-- prog.go --\npackage main\n",
3847
},
3948
{
40-
name: "multi_go_with_header",
41-
body: "-- prog.go --\n package main\n\n\n-- two.go --\n package main\n var X = 5",
42-
want: "-- prog.go --\npackage main\n-- two.go --\npackage main\n\nvar X = 5\n",
49+
name: "multi_go_with_header",
50+
method: http.MethodPost,
51+
body: "-- prog.go --\n package main\n\n\n-- two.go --\n package main\n var X = 5",
52+
want: "-- prog.go --\npackage main\n-- two.go --\npackage main\n\nvar X = 5\n",
4353
},
4454
{
45-
name: "multi_go_without_header",
46-
body: " package main\n\n\n-- two.go --\n package main\n var X = 5",
47-
want: "package main\n-- two.go --\npackage main\n\nvar X = 5\n",
55+
name: "multi_go_without_header",
56+
method: http.MethodPost,
57+
body: " package main\n\n\n-- two.go --\n package main\n var X = 5",
58+
want: "package main\n-- two.go --\npackage main\n\nvar X = 5\n",
4859
},
4960
{
50-
name: "single_go.mod_with_header",
51-
body: "-- go.mod --\n module \"foo\" ",
52-
want: "-- go.mod --\nmodule foo\n",
61+
name: "single_go.mod_with_header",
62+
method: http.MethodPost,
63+
body: "-- go.mod --\n module \"foo\" ",
64+
want: "-- go.mod --\nmodule foo\n",
5365
},
5466
{
55-
name: "multi_go.mod_with_header",
56-
body: "-- a/go.mod --\n module foo\n\n\n-- b/go.mod --\n module \"bar\"",
57-
want: "-- a/go.mod --\nmodule foo\n-- b/go.mod --\nmodule bar\n",
67+
name: "multi_go.mod_with_header",
68+
method: http.MethodPost,
69+
body: "-- a/go.mod --\n module foo\n\n\n-- b/go.mod --\n module \"bar\"",
70+
want: "-- a/go.mod --\nmodule foo\n-- b/go.mod --\nmodule bar\n",
5871
},
5972
{
60-
name: "only_format_go_and_go.mod",
73+
name: "only_format_go_and_go.mod",
74+
method: http.MethodPost,
6175
body: " package main \n\n\n" +
6276
"-- go.mod --\n module foo \n\n\n" +
6377
"-- plain.txt --\n plain text \n\n\n",
6478
want: "package main\n-- go.mod --\nmodule foo\n-- plain.txt --\n plain text \n\n\n",
6579
},
6680
{
6781
name: "error_gofmt",
82+
method: http.MethodPost,
6883
body: "package 123\n",
6984
wantErr: "prog.go:1:9: expected 'IDENT', found 123",
7085
},
7186
{
7287
name: "error_gofmt_with_header",
88+
method: http.MethodPost,
7389
body: "-- dir/one.go --\npackage 123\n",
7490
wantErr: "dir/one.go:1:9: expected 'IDENT', found 123",
7591
},
7692
{
7793
name: "error_goimports",
94+
method: http.MethodPost,
7895
body: "package 123\n",
7996
imports: true,
8097
wantErr: "prog.go:1:9: expected 'IDENT', found 123",
8198
},
8299
{
83100
name: "error_goimports_with_header",
101+
method: http.MethodPost,
84102
body: "-- dir/one.go --\npackage 123\n",
85103
imports: true,
86104
wantErr: "dir/one.go:1:9: expected 'IDENT', found 123",
87105
},
88106
{
89107
name: "error_go.mod",
108+
method: http.MethodPost,
90109
body: "-- go.mod --\n123\n",
91110
wantErr: "go.mod:1: unknown directive: 123",
92111
},
93112
{
94113
name: "error_go.mod_with_header",
114+
method: http.MethodPost,
95115
body: "-- dir/go.mod --\n123\n",
96116
wantErr: "dir/go.mod:1: unknown directive: 123",
97117
},
@@ -110,6 +130,10 @@ func TestHandleFmt(t *testing.T) {
110130
if resp.StatusCode != 200 {
111131
t.Fatalf("code = %v", resp.Status)
112132
}
133+
corsHeader := "Access-Control-Allow-Origin"
134+
if got, want := resp.Header.Get(corsHeader), "*"; got != want {
135+
t.Errorf("Header %q: got %q; want %q", corsHeader, got, want)
136+
}
113137
if ct := resp.Header.Get("Content-Type"); ct != "application/json" {
114138
t.Fatalf("Content-Type = %q; want application/json", ct)
115139
}

server_test.go

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,24 +51,30 @@ func TestEdit(t *testing.T) {
5151

5252
testCases := []struct {
5353
desc string
54+
method string
5455
url string
5556
statusCode int
5657
headers map[string]string
5758
respBody []byte
5859
}{
59-
{"foo.play.golang.org to play.golang.org", "https://foo.play.golang.org", http.StatusFound, map[string]string{"Location": "https://play.golang.org"}, nil},
60-
{"Non-existent page", "https://play.golang.org/foo", http.StatusNotFound, nil, nil},
61-
{"Unknown snippet", "https://play.golang.org/p/foo", http.StatusNotFound, nil, nil},
62-
{"Existing snippet", "https://play.golang.org/p/" + id, http.StatusOK, nil, nil},
63-
{"Plaintext snippet", "https://play.golang.org/p/" + id + ".go", http.StatusOK, nil, barBody},
64-
{"Download snippet", "https://play.golang.org/p/" + id + ".go?download=true", http.StatusOK, map[string]string{"Content-Disposition": fmt.Sprintf(`attachment; filename="%s.go"`, id)}, barBody},
60+
{"OPTIONS no-op", http.MethodOptions, "https://play.golang.org/p/foo", http.StatusOK, nil, nil},
61+
{"foo.play.golang.org to play.golang.org", http.MethodGet, "https://foo.play.golang.org", http.StatusFound, map[string]string{"Location": "https://play.golang.org"}, nil},
62+
{"Non-existent page", http.MethodGet, "https://play.golang.org/foo", http.StatusNotFound, nil, nil},
63+
{"Unknown snippet", http.MethodGet, "https://play.golang.org/p/foo", http.StatusNotFound, nil, nil},
64+
{"Existing snippet", http.MethodGet, "https://play.golang.org/p/" + id, http.StatusOK, nil, nil},
65+
{"Plaintext snippet", http.MethodGet, "https://play.golang.org/p/" + id + ".go", http.StatusOK, nil, barBody},
66+
{"Download snippet", http.MethodGet, "https://play.golang.org/p/" + id + ".go?download=true", http.StatusOK, map[string]string{"Content-Disposition": fmt.Sprintf(`attachment; filename="%s.go"`, id)}, barBody},
6567
}
6668

6769
for _, tc := range testCases {
68-
req := httptest.NewRequest(http.MethodGet, tc.url, nil)
70+
req := httptest.NewRequest(tc.method, tc.url, nil)
6971
w := httptest.NewRecorder()
7072
s.handleEdit(w, req)
7173
resp := w.Result()
74+
corsHeader := "Access-Control-Allow-Origin"
75+
if got, want := resp.Header.Get(corsHeader), "*"; got != want {
76+
t.Errorf("%s: %q header: got %q; want %q", tc.desc, corsHeader, got, want)
77+
}
7278
if got, want := resp.StatusCode, tc.statusCode; got != want {
7379
t.Errorf("%s: got unexpected status code %d; want %d", tc.desc, got, want)
7480
}
@@ -115,6 +121,10 @@ func TestShare(t *testing.T) {
115121
w := httptest.NewRecorder()
116122
s.handleShare(w, req)
117123
resp := w.Result()
124+
corsHeader := "Access-Control-Allow-Origin"
125+
if got, want := resp.Header.Get(corsHeader), "*"; got != want {
126+
t.Errorf("%s: %q header: got %q; want %q", tc.desc, corsHeader, got, want)
127+
}
118128
if got, want := resp.StatusCode, tc.statusCode; got != want {
119129
t.Errorf("%s: got unexpected status code %d; want %d", tc.desc, got, want)
120130
}

0 commit comments

Comments
 (0)