diff --git a/gateway/core/corehttp/commands.go b/gateway/core/corehttp/commands.go index 5ccb31d98..2bfa470f2 100644 --- a/gateway/core/corehttp/commands.go +++ b/gateway/core/corehttp/commands.go @@ -67,12 +67,18 @@ func addHeadersFromConfig(c *cmdsHttp.ServerConfig, nc *config.Config) { } } - c.Headers = make(map[string][]string, len(nc.API.HTTPHeaders)) + c.Headers = make(map[string][]string, len(nc.API.HTTPHeaders)+1) // Copy these because the config is shared and this function is called // in multiple places concurrently. Updating these in-place *is* racy. for h, v := range nc.API.HTTPHeaders { - c.Headers[h] = v + h = http.CanonicalHeaderKey(h) + switch h { + case cmdsHttp.ACAOrigin, cmdsHttp.ACAMethods, cmdsHttp.ACACredentials: + // these are handled by the CORs library. + default: + c.Headers[h] = v + } } c.Headers["Server"] = []string{"go-ipfs/" + version.CurrentVersionNumber} } diff --git a/gateway/core/corehttp/gateway.go b/gateway/core/corehttp/gateway.go index 6aad23786..098df6bd8 100644 --- a/gateway/core/corehttp/gateway.go +++ b/gateway/core/corehttp/gateway.go @@ -4,6 +4,7 @@ import ( "fmt" "net" "net/http" + "sort" version "github.com/ipfs/go-ipfs" core "github.com/ipfs/go-ipfs/core" @@ -19,6 +20,26 @@ type GatewayConfig struct { PathPrefixes []string } +// A helper function to clean up a set of headers: +// 1. Canonicalizes. +// 2. Deduplicates. +// 3. Sorts. +func cleanHeaderSet(headers []string) []string { + // Deduplicate and canonicalize. + m := make(map[string]struct{}, len(headers)) + for _, h := range headers { + m[http.CanonicalHeaderKey(h)] = struct{}{} + } + result := make([]string, 0, len(m)) + for k := range m { + result = append(result, k) + } + + // Sort + sort.Strings(result) + return result +} + func GatewayOption(writable bool, paths ...string) ServeOption { return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) { cfg, err := n.Repo.Config() @@ -31,8 +52,43 @@ func GatewayOption(writable bool, paths ...string) ServeOption { return nil, err } + headers := make(map[string][]string, len(cfg.Gateway.HTTPHeaders)) + for h, v := range cfg.Gateway.HTTPHeaders { + headers[http.CanonicalHeaderKey(h)] = v + } + + // Hard-coded headers. + const ACAHeadersName = "Access-Control-Allow-Headers" + const ACEHeadersName = "Access-Control-Expose-Headers" + const ACAOriginName = "Access-Control-Allow-Origin" + const ACAMethodsName = "Access-Control-Allow-Methods" + + if _, ok := headers[ACAOriginName]; !ok { + // Default to *all* + headers[ACAOriginName] = []string{"*"} + } + if _, ok := headers[ACAMethodsName]; !ok { + // Default to GET + headers[ACAMethodsName] = []string{"GET"} + } + + headers[ACAHeadersName] = cleanHeaderSet( + append([]string{ + "Content-Type", + "User-Agent", + "Range", + "X-Requested-With", + }, headers[ACAHeadersName]...)) + + headers[ACEHeadersName] = cleanHeaderSet( + append([]string{ + "Content-Range", + "X-Chunked-Output", + "X-Stream-Output", + }, headers[ACEHeadersName]...)) + gateway := newGatewayHandler(n, GatewayConfig{ - Headers: cfg.Gateway.HTTPHeaders, + Headers: headers, Writable: writable, PathPrefixes: cfg.Gateway.PathPrefixes, }, api) diff --git a/gateway/core/corehttp/gateway_handler.go b/gateway/core/corehttp/gateway_handler.go index 38313059c..44e823990 100644 --- a/gateway/core/corehttp/gateway_handler.go +++ b/gateway/core/corehttp/gateway_handler.go @@ -182,19 +182,6 @@ func (i *gatewayHandler) getOrHeadHandler(ctx context.Context, w http.ResponseWr w.Header().Set("X-IPFS-Path", urlPath) w.Header().Set("Etag", etag) - // set 'allowed' headers - // & expose those headers - var allowedHeadersArr = []string{ - "Content-Range", - "X-Chunked-Output", - "X-Stream-Output", - } - - var allowedHeaders = strings.Join(allowedHeadersArr, ", ") - - w.Header().Set("Access-Control-Allow-Headers", allowedHeaders) - w.Header().Set("Access-Control-Expose-Headers", allowedHeaders) - // Suborigin header, sandboxes apps from each other in the browser (even // though they are served from the same gateway domain). //