diff --git a/core/corehttp/gateway.go b/core/corehttp/gateway.go index 52560750a6e..ae3e035d87e 100644 --- a/core/corehttp/gateway.go +++ b/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" @@ -18,6 +19,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() @@ -30,8 +51,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[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/core/corehttp/gateway_handler.go b/core/corehttp/gateway_handler.go index 553ebdcf8f5..bb6ff51c4c7 100644 --- a/core/corehttp/gateway_handler.go +++ b/core/corehttp/gateway_handler.go @@ -184,19 +184,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). //