gateway: fix CORs headers
fixes #5138 -- always add user-agent to access-control-allow-headers. fixes #5888 -- same with content-type. fixes #5892 -- extend user-provided headers instead of overriding them. License: MIT Signed-off-by: Steven Allen <steven@stebalien.com>
Steven Allen committed
Jan 4, 2019 at 12:08 UTC
82629c00ec12e376e26b28d3551395d5e2dfb85c
2 files changed
+57
-14
core/corehttp/gateway.go
+57
-1
@@ -4,6 +4,7 @@ import (
4
"fmt"
5
"net"
6
"net/http"
7
+ "sort"
8
9
version "github.com/ipfs/go-ipfs"
10
core "github.com/ipfs/go-ipfs/core"
@@ -18,6 +19,26 @@ type GatewayConfig struct {
19
PathPrefixes []string
20
}
21
22
+// A helper function to clean up a set of headers:
23
+// 1. Canonicalizes.
24
+// 2. Deduplicates.
25
+// 3. Sorts.
26
+func cleanHeaderSet(headers []string) []string {
27
+ // Deduplicate and canonicalize.
28
+ m := make(map[string]struct{}, len(headers))
29
+ for _, h := range headers {
30
+ m[http.CanonicalHeaderKey(h)] = struct{}{}
31
+ }
32
+ result := make([]string, 0, len(m))
33
+ for k := range m {
34
+ result = append(result, k)
35
+ }
36
+
37
+ // Sort
38
+ sort.Strings(result)
39
+ return result
40
+}
41
+
42
func GatewayOption(writable bool, paths ...string) ServeOption {
43
return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
44
cfg, err := n.Repo.Config()
@@ -30,8 +51,43 @@ func GatewayOption(writable bool, paths ...string) ServeOption {
51
return nil, err
52
}
53
54
+ headers := make(map[string][]string, len(cfg.Gateway.HTTPHeaders))
55
+ for h, v := range cfg.Gateway.HTTPHeaders {
56
+ headers[h] = v
57
+ }
58
+
59
+ // Hard-coded headers.
60
+ const ACAHeadersName = "Access-Control-Allow-Headers"
61
+ const ACEHeadersName = "Access-Control-Expose-Headers"
62
+ const ACAOriginName = "Access-Control-Allow-Origin"
63
+ const ACAMethodsName = "Access-Control-Allow-Methods"
64
+
65
+ if _, ok := headers[ACAOriginName]; !ok {
66
+ // Default to *all*
67
+ headers[ACAOriginName] = []string{"*"}
68
+ }
69
+ if _, ok := headers[ACAMethodsName]; !ok {
70
+ // Default to GET
71
+ headers[ACAMethodsName] = []string{"GET"}
72
+ }
73
+
74
+ headers[ACAHeadersName] = cleanHeaderSet(
75
+ append([]string{
76
+ "Content-Type",
77
+ "User-Agent",
78
+ "Range",
79
+ "X-Requested-With",
80
+ }, headers[ACAHeadersName]...))
81
+
82
+ headers[ACEHeadersName] = cleanHeaderSet(
83
+ append([]string{
84
+ "Content-Range",
85
+ "X-Chunked-Output",
86
+ "X-Stream-Output",
87
+ }, headers[ACEHeadersName]...))
88
+
89
gateway := newGatewayHandler(n, GatewayConfig{
34
- Headers: cfg.Gateway.HTTPHeaders,
90
+ Headers: headers,
91
Writable: writable,
92
PathPrefixes: cfg.Gateway.PathPrefixes,
93
}, api)
core/corehttp/gateway_handler.go
-13
@@ -184,19 +184,6 @@ func (i *gatewayHandler) getOrHeadHandler(ctx context.Context, w http.ResponseWr
184
w.Header().Set("X-IPFS-Path", urlPath)
185
w.Header().Set("Etag", etag)
186
187
- // set 'allowed' headers
188
- // & expose those headers
189
- var allowedHeadersArr = []string{
190
- "Content-Range",
191
- "X-Chunked-Output",
192
- "X-Stream-Output",
193
- }
194
-
195
- var allowedHeaders = strings.Join(allowedHeadersArr, ", ")
196
-
197
- w.Header().Set("Access-Control-Allow-Headers", allowedHeaders)
198
- w.Header().Set("Access-Control-Expose-Headers", allowedHeaders)
199
-
187
// Suborigin header, sandboxes apps from each other in the browser (even
188
// though they are served from the same gateway domain).
189
//