diff --git a/.github/workflows/gateway-conformance.yml b/.github/workflows/gateway-conformance.yml index a1b96402a..d49291a43 100644 --- a/.github/workflows/gateway-conformance.yml +++ b/.github/workflows/gateway-conformance.yml @@ -60,6 +60,7 @@ jobs: run: | ./ipfs init --profile=test ./ipfs config --json Gateway.PublicGateways "$GATEWAY_PUBLIC_GATEWAYS" + ./ipfs config --json Experimental.GatewayOverLibp2p true ./ipfs config Addresses.Gateway "/ip4/127.0.0.1/tcp/8080" ./ipfs config Addresses.API "/ip4/127.0.0.1/tcp/5001" working-directory: kubo-gateway/cmd/ipfs diff --git a/cmd/ipfs/daemon.go b/cmd/ipfs/daemon.go index 0763f6eb2..d9207b0f6 100644 --- a/cmd/ipfs/daemon.go +++ b/cmd/ipfs/daemon.go @@ -909,17 +909,27 @@ func serveHTTPGateway(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, e const gatewayProtocolID protocol.ID = "/ipfs/gateway" // FIXME: specify https://github.com/ipfs/specs/issues/433 func serveTrustlessGatewayOverLibp2p(cctx *oldcmds.Context) (<-chan error, error) { + node, err := cctx.ConstructNode() + if err != nil { + return nil, fmt.Errorf("serveHTTPGatewayOverLibp2p: ConstructNode() failed: %s", err) + } + cfg, err := node.Repo.Config() + if err != nil { + return nil, fmt.Errorf("could not read config: %w", err) + } + + if !cfg.Experimental.GatewayOverLibp2p { + errCh := make(chan error) + close(errCh) + return errCh, nil + } + opts := []corehttp.ServeOption{ corehttp.MetricsCollectionOption("libp2p-gateway"), corehttp.Libp2pGatewayOption(), corehttp.VersionOption(), } - node, err := cctx.ConstructNode() - if err != nil { - return nil, fmt.Errorf("serveHTTPGateway: ConstructNode() failed: %s", err) - } - handler, err := corehttp.MakeHandler(node, nil, opts...) if err != nil { return nil, err diff --git a/config/experiments.go b/config/experiments.go index f5ecf4be6..3a63d253d 100644 --- a/config/experiments.go +++ b/config/experiments.go @@ -11,4 +11,5 @@ type Experiments struct { AcceleratedDHTClient experimentalAcceleratedDHTClient `json:",omitempty"` OptimisticProvide bool OptimisticProvideJobsPoolSize int + GatewayOverLibp2p bool `json:",omitempty"` } diff --git a/docs/experimental-features.md b/docs/experimental-features.md index 587d136d5..52bfe703d 100644 --- a/docs/experimental-features.md +++ b/docs/experimental-features.md @@ -27,6 +27,7 @@ the above issue. - [Graphsync](#graphsync) - [Noise](#noise) - [Optimistic Provide](#optimistic-provide) +- [HTTP Gateway over Libp2p](#http-gateway-over-libp2p) --- @@ -617,3 +618,39 @@ ipfs config --json Experimental.OptimisticProvideJobsPoolSize 120 - [ ] Needs more people to use and report on how well it works - [ ] Should prove at least equivalent availability of provider records as the classic approach + +## HTTP Gateway over Libp2p + +### In Version + +0.23.0 + +### State + +Experimental, disabled by default. + +Enables serving the [IPFS HTTP Gateway](https://specs.ipfs.tech/http-gateways/) protocol over libp2p transports and +as described in the [specification](https://github.com/ipfs/specs/pull/434). + +Notes: +- This feature currently is only about serving the gateway requests over libp2p, not about fetching data this way using +[Trustless Gateway Specification](https://specs.ipfs.tech/http-gateways/trustless-gateway/). +- While kubo currently mounts the gateway API at the root (i.e. `/`) of the libp2p `/http/1.1` protocol that is subject to +change. The way to reliably discover where a given HTTP protocol is mounted on a libp2p endpoint is via the `.well-known/libp2p` +resource specified in the [http+libp2p specification](https://github.com/libp2p/specs/pull/508) +- Kubo currently hard codes the gateway-over-libp2p behavior to: + - Only operate on `/ipfs` resources + - Only satisfy the Trustless Gateway API + - Only serve data that is already local to the node (i.e. similar to a `NoFetch` gateway) + +### How to enable + +Modify your ipfs config: + +``` +ipfs config --json Experimental.GatewayOverLibp2p true +``` + +### Road to being a real feature + +- [ ] Needs more people to use and report on how well it works \ No newline at end of file diff --git a/test/cli/http_gateway_over_libp2p_test.go b/test/cli/http_gateway_over_libp2p_test.go index b4b2cf105..ee5717571 100644 --- a/test/cli/http_gateway_over_libp2p_test.go +++ b/test/cli/http_gateway_over_libp2p_test.go @@ -57,6 +57,16 @@ func TestGatewayOverLibp2p(t *testing.T) { p2pProxyNodeHTTPListenAddr, err := manet.ToNetAddr(p2pProxyNodeHTTPListenMA) require.NoError(t, err) + t.Run("DoesNotWorkWithoutExperimentalConfig", func(t *testing.T) { + _, err := http.Get(fmt.Sprintf("http://%s/ipfs/%s?format=raw", p2pProxyNodeHTTPListenAddr, cidDataOnGatewayNode)) + require.Error(t, err) + }) + + // Enable the experimental feature and reconnect the nodes + gwNode.IPFS("config", "--json", "Experimental.GatewayOverLibp2p", "true") + gwNode.StopDaemon().StartDaemon() + nodes.Connect() + // Note: the bare HTTP requests here assume that the gateway is mounted at `/` t.Run("WillNotServeRemoteContent", func(t *testing.T) { resp, err := http.Get(fmt.Sprintf("http://%s/ipfs/%s?format=raw", p2pProxyNodeHTTPListenAddr, cidDataNotOnGatewayNode))