mirror of
https://github.com/ipfs/kubo.git
synced 2026-02-22 19:07:48 +08:00
This fix safelists additional headers allowing JS running on websites to read them when IPFS resource is downloaded via Fetch API. These headers provide metadata necessary for making smart caching decisions when IPFS resources are downloaded via Service Worker or a similar middleware on the edge.
121 lines
3.2 KiB
Go
121 lines
3.2 KiB
Go
package corehttp
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"sort"
|
|
|
|
version "github.com/ipfs/go-ipfs"
|
|
core "github.com/ipfs/go-ipfs/core"
|
|
coreapi "github.com/ipfs/go-ipfs/core/coreapi"
|
|
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
|
|
|
options "github.com/ipfs/interface-go-ipfs-core/options"
|
|
id "github.com/libp2p/go-libp2p/p2p/protocol/identify"
|
|
)
|
|
|
|
type GatewayConfig struct {
|
|
Headers map[string][]string
|
|
Writable bool
|
|
PathPrefixes []string
|
|
FastDirIndexThreshold int
|
|
}
|
|
|
|
// 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()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
api, err := coreapi.NewCoreAPI(n, options.Api.FetchBlocks(!cfg.Gateway.NoFetch))
|
|
if err != nil {
|
|
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{http.MethodGet}
|
|
}
|
|
|
|
headers[ACAHeadersName] = cleanHeaderSet(
|
|
append([]string{
|
|
"Content-Type",
|
|
"User-Agent",
|
|
"Range",
|
|
"X-Requested-With",
|
|
}, headers[ACAHeadersName]...))
|
|
|
|
headers[ACEHeadersName] = cleanHeaderSet(
|
|
append([]string{
|
|
"Content-Length",
|
|
"Content-Range",
|
|
"X-Chunked-Output",
|
|
"X-Stream-Output",
|
|
"X-Ipfs-Path",
|
|
"X-Ipfs-Roots",
|
|
}, headers[ACEHeadersName]...))
|
|
|
|
var gateway http.Handler = newGatewayHandler(GatewayConfig{
|
|
Headers: headers,
|
|
Writable: writable,
|
|
PathPrefixes: cfg.Gateway.PathPrefixes,
|
|
FastDirIndexThreshold: int(cfg.Gateway.FastDirIndexThreshold.WithDefault(100)),
|
|
}, api)
|
|
|
|
gateway = otelhttp.NewHandler(gateway, "Gateway.Request")
|
|
|
|
for _, p := range paths {
|
|
mux.Handle(p+"/", gateway)
|
|
}
|
|
return mux, nil
|
|
}
|
|
}
|
|
|
|
func VersionOption() ServeOption {
|
|
return func(_ *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
|
|
mux.HandleFunc("/version", func(w http.ResponseWriter, r *http.Request) {
|
|
fmt.Fprintf(w, "Commit: %s\n", version.CurrentCommit)
|
|
fmt.Fprintf(w, "Client Version: %s\n", version.GetUserAgentVersion())
|
|
fmt.Fprintf(w, "Protocol Version: %s\n", id.LibP2PVersion)
|
|
})
|
|
return mux, nil
|
|
}
|
|
}
|