Skip to content

Commit

Permalink
feat(gateway): expose /routing/v1 server (opt-in) (#9877)
Browse files Browse the repository at this point in the history
  • Loading branch information
hacdias authored Aug 25, 2023
1 parent 86bde28 commit 4606586
Show file tree
Hide file tree
Showing 18 changed files with 505 additions and 56 deletions.
10 changes: 10 additions & 0 deletions cmd/ipfs/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,12 @@ func serveHTTPGateway(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, e
fmt.Printf("Gateway server listening on %s\n", listener.Multiaddr())
}

if cfg.Gateway.ExposeRoutingAPI.WithDefault(config.DefaultExposeRoutingAPI) {
for _, listener := range listeners {
fmt.Printf("Routing V1 API exposed at http://%s/routing/v1\n", listener.Addr())
}
}

cmdctx := *cctx
cmdctx.Gateway = true

Expand All @@ -848,6 +854,10 @@ func serveHTTPGateway(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, e
opts = append(opts, corehttp.P2PProxyOption())
}

if cfg.Gateway.ExposeRoutingAPI.WithDefault(config.DefaultExposeRoutingAPI) {
opts = append(opts, corehttp.RoutingOption())
}

if len(cfg.Gateway.RootRedirect) > 0 {
opts = append(opts, corehttp.RedirectOption("", cfg.Gateway.RootRedirect))
}
Expand Down
5 changes: 5 additions & 0 deletions config/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package config
const (
DefaultInlineDNSLink = false
DefaultDeserializedResponses = true
DefaultExposeRoutingAPI = false
)

type GatewaySpec struct {
Expand Down Expand Up @@ -72,4 +73,8 @@ type Gateway struct {
// PublicGateways configures behavior of known public gateways.
// Each key is a fully qualified domain name (FQDN).
PublicGateways map[string]*GatewaySpec

// ExposeRoutingAPI configures the gateway port to expose
// routing system as HTTP API at /routing/v1 (https://specs.ipfs.tech/routing/http-routing-v1/).
ExposeRoutingAPI Flag
}
129 changes: 129 additions & 0 deletions core/corehttp/routing.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package corehttp

import (
"context"
"net"
"net/http"
"time"

"github.com/ipfs/boxo/ipns"
"github.com/ipfs/boxo/routing/http/server"
"github.com/ipfs/boxo/routing/http/types"
"github.com/ipfs/boxo/routing/http/types/iter"
cid "github.com/ipfs/go-cid"
core "github.com/ipfs/kubo/core"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/routing"
)

func RoutingOption() ServeOption {
return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
handler := server.Handler(&contentRouter{n})
mux.Handle("/routing/v1/", handler)
return mux, nil
}
}

type contentRouter struct {
n *core.IpfsNode
}

func (r *contentRouter) FindProviders(ctx context.Context, key cid.Cid, limit int) (iter.ResultIter[types.Record], error) {
ctx, cancel := context.WithCancel(ctx)
ch := r.n.Routing.FindProvidersAsync(ctx, key, limit)
return iter.ToResultIter[types.Record](&peerChanIter{
ch: ch,
cancel: cancel,
}), nil
}

// nolint deprecated
func (r *contentRouter) ProvideBitswap(ctx context.Context, req *server.BitswapWriteProvideRequest) (time.Duration, error) {
return 0, routing.ErrNotSupported
}

func (r *contentRouter) FindPeers(ctx context.Context, pid peer.ID, limit int) (iter.ResultIter[types.Record], error) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()

addr, err := r.n.Routing.FindPeer(ctx, pid)
if err != nil {
return nil, err
}

rec := &types.PeerRecord{
Schema: types.SchemaPeer,
ID: &addr.ID,
}

for _, addr := range addr.Addrs {
rec.Addrs = append(rec.Addrs, types.Multiaddr{Multiaddr: addr})
}

return iter.ToResultIter[types.Record](iter.FromSlice[types.Record]([]types.Record{rec})), nil
}

func (r *contentRouter) GetIPNS(ctx context.Context, name ipns.Name) (*ipns.Record, error) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()

raw, err := r.n.Routing.GetValue(ctx, string(name.RoutingKey()))
if err != nil {
return nil, err
}

return ipns.UnmarshalRecord(raw)
}

func (r *contentRouter) PutIPNS(ctx context.Context, name ipns.Name, record *ipns.Record) error {
ctx, cancel := context.WithCancel(ctx)
defer cancel()

raw, err := ipns.MarshalRecord(record)
if err != nil {
return err
}

// The caller guarantees that name matches the record. This is double checked
// by the internals of PutValue.
return r.n.Routing.PutValue(ctx, string(name.RoutingKey()), raw)
}

type peerChanIter struct {
ch <-chan peer.AddrInfo
cancel context.CancelFunc
next *peer.AddrInfo
}

func (it *peerChanIter) Next() bool {
addr, ok := <-it.ch
if ok {
it.next = &addr
return true
} else {
it.next = nil
return false
}
}

func (it *peerChanIter) Val() types.Record {
if it.next == nil {
return nil
}

rec := &types.PeerRecord{
Schema: types.SchemaPeer,
ID: &it.next.ID,
}

for _, addr := range it.next.Addrs {
rec.Addrs = append(rec.Addrs, types.Multiaddr{Multiaddr: addr})
}

return rec
}

func (it *peerChanIter) Close() error {
it.cancel()
return nil
}
3 changes: 2 additions & 1 deletion core/node/libp2p/routingopt.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,8 @@ func ConstructDelegatedRouting(routers config.Routers, methods config.Methods, p
PeerID: peerID,
Addrs: httpAddrsFromConfig(addrs),
PrivKeyB64: privKey,
})
},
)
}
}

Expand Down
8 changes: 8 additions & 0 deletions docs/changelogs/v0.23.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- [Mplex deprecation](#mplex-deprecation)
- [Gateway: meaningful CAR responses on Not Found errors](#gateway-meaningful-car-responses-on-not-found-errors)
- [Binary characters in file names: no longer works with old clients and new Kubo servers](#binary-characters-in-file-names-no-longer-works-with-old-clients-and-new-kubo-servers)
- [Self-hosting `/routing/v1` endpoint for delegated routing needs](#self-hosting-routingv1-endpoint-for-delegated-routing-needs)
- [📝 Changelog](#-changelog)
- [👨‍👩‍👧‍👦 Contributors](#-contributors)

Expand Down Expand Up @@ -59,6 +60,13 @@ the compatibility table:

*Old clients can only send Unicode file paths to the server.

#### Self-hosting `/routing/v1` endpoint for delegated routing needs

The `Routing` system configured in Kubo can be now exposed on the gateway port as a standard
HTTP [Routing V1](https://specs.ipfs.tech/routing/http-routing-v1/) API endpoint. This allows
self-hosting and experimentation with custom delegated routers. This is disabled by default,
but can be enabled by setting [`Gateway.ExposeRoutingAPI`](https://github.com/ipfs/kubo/blob/master/docs/config.md#gatewayexposeroutingapi) to `true` .

### 📝 Changelog

### 👨‍👩‍👧‍👦 Contributors
10 changes: 10 additions & 0 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,16 @@ Default: `true`

Type: `flag`

#### `Gateway.ExposeRoutingAPI`

An optional flag to expose Kubo `Routing` system on the gateway port as a [Routing
V1](https://specs.ipfs.tech/routing/routing-v1/) endpoint. This only affects your
local gateway, at `127.0.0.1`.

Default: `false`

Type: `flag`

### `Gateway.HTTPHeaders`

Headers to set on gateway responses.
Expand Down
3 changes: 1 addition & 2 deletions docs/examples/kubo-as-a-library/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ go 1.20
replace github.com/ipfs/kubo => ./../../..

require (
github.com/ipfs/boxo v0.12.1-0.20230822135301-303595bcdba7
github.com/ipfs/boxo v0.12.1-0.20230825151903-13569468babd
github.com/ipfs/kubo v0.0.0-00010101000000-000000000000
github.com/libp2p/go-libp2p v0.30.0
github.com/multiformats/go-multiaddr v0.11.0
Expand Down Expand Up @@ -52,7 +52,6 @@ require (
github.com/google/gopacket v1.1.19 // indirect
github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect
github.com/hannahhoward/go-pubsub v0.0.0-20200423002714-8d62886cc36e // indirect
Expand Down
5 changes: 2 additions & 3 deletions docs/examples/kubo-as-a-library/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,6 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
Expand Down Expand Up @@ -301,8 +300,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs=
github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0=
github.com/ipfs/boxo v0.12.1-0.20230822135301-303595bcdba7 h1:f7n4M8UIf+4BY6Q0kcZ5FbpkxKaIqq/BW3evqI87DNo=
github.com/ipfs/boxo v0.12.1-0.20230822135301-303595bcdba7/go.mod h1:btrtHy0lmO1ODMECbbEY1pxNtrLilvKSYLoGQt1yYCk=
github.com/ipfs/boxo v0.12.1-0.20230825151903-13569468babd h1:uAp9W7FRQ7W16FENlURZqBh7/3PnakG0DjHpKPirKVY=
github.com/ipfs/boxo v0.12.1-0.20230825151903-13569468babd/go.mod h1:btrtHy0lmO1ODMECbbEY1pxNtrLilvKSYLoGQt1yYCk=
github.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA=
github.com/ipfs/go-bitfield v1.1.0/go.mod h1:paqf1wjq/D2BBmzfTVFlJQ9IlFOZpg422HL0HqsGWHU=
github.com/ipfs/go-block-format v0.0.2/go.mod h1:AWR46JfpcObNfg3ok2JHDUfdiHRgWhJgCQF+KIgOPJY=
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ require (
github.com/fsnotify/fsnotify v1.6.0
github.com/google/uuid v1.3.0
github.com/hashicorp/go-multierror v1.1.1
github.com/ipfs/boxo v0.12.1-0.20230822135301-303595bcdba7
github.com/ipfs/boxo v0.12.1-0.20230825151903-13569468babd
github.com/ipfs/go-block-format v0.1.2
github.com/ipfs/go-cid v0.4.1
github.com/ipfs/go-cidutil v0.1.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -335,8 +335,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs=
github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0=
github.com/ipfs/boxo v0.12.1-0.20230822135301-303595bcdba7 h1:f7n4M8UIf+4BY6Q0kcZ5FbpkxKaIqq/BW3evqI87DNo=
github.com/ipfs/boxo v0.12.1-0.20230822135301-303595bcdba7/go.mod h1:btrtHy0lmO1ODMECbbEY1pxNtrLilvKSYLoGQt1yYCk=
github.com/ipfs/boxo v0.12.1-0.20230825151903-13569468babd h1:uAp9W7FRQ7W16FENlURZqBh7/3PnakG0DjHpKPirKVY=
github.com/ipfs/boxo v0.12.1-0.20230825151903-13569468babd/go.mod h1:btrtHy0lmO1ODMECbbEY1pxNtrLilvKSYLoGQt1yYCk=
github.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA=
github.com/ipfs/go-bitfield v1.1.0/go.mod h1:paqf1wjq/D2BBmzfTVFlJQ9IlFOZpg422HL0HqsGWHU=
github.com/ipfs/go-bitswap v0.11.0 h1:j1WVvhDX1yhG32NTC9xfxnqycqYIlhzEzLXG/cU1HyQ=
Expand Down
2 changes: 2 additions & 0 deletions routing/delegated.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,8 @@ func httpRoutingFromConfig(conf config.Router, extraHTTP *ExtraHTTPParams) (rout

return &httpRoutingWrapper{
ContentRouting: cr,
PeerRouting: cr,
ValueStore: cr,
ProvideManyRouter: cr,
}, nil
}
Expand Down
21 changes: 2 additions & 19 deletions routing/wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"

routinghelpers "github.com/libp2p/go-libp2p-routing-helpers"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/routing"
)

Expand All @@ -22,27 +21,11 @@ var (
// http delegated routing.
type httpRoutingWrapper struct {
routing.ContentRouting
routing.PeerRouting
routing.ValueStore
routinghelpers.ProvideManyRouter
}

func (c *httpRoutingWrapper) Bootstrap(ctx context.Context) error {
return nil
}

func (c *httpRoutingWrapper) FindPeer(ctx context.Context, id peer.ID) (peer.AddrInfo, error) {
return peer.AddrInfo{}, routing.ErrNotSupported
}

func (c *httpRoutingWrapper) PutValue(context.Context, string, []byte, ...routing.Option) error {
return routing.ErrNotSupported
}

func (c *httpRoutingWrapper) GetValue(context.Context, string, ...routing.Option) ([]byte, error) {
return nil, routing.ErrNotSupported
}

func (c *httpRoutingWrapper) SearchValue(context.Context, string, ...routing.Option) (<-chan []byte, error) {
out := make(chan []byte)
close(out)
return out, routing.ErrNotSupported
}
25 changes: 14 additions & 11 deletions test/cli/content_routing_http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,42 +16,45 @@ import (
"github.com/ipfs/go-cid"
"github.com/ipfs/kubo/test/cli/harness"
"github.com/ipfs/kubo/test/cli/testutils"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/routing"
"github.com/stretchr/testify/assert"
)

type fakeHTTPContentRouter struct {
m sync.Mutex
findProvidersCalls int
provideCalls int
m sync.Mutex
provideBitswapCalls int
findProvidersCalls int
findPeersCalls int
}

func (r *fakeHTTPContentRouter) FindProviders(ctx context.Context, key cid.Cid, limit int) (iter.ResultIter[types.ProviderResponse], error) {
func (r *fakeHTTPContentRouter) FindProviders(ctx context.Context, key cid.Cid, limit int) (iter.ResultIter[types.Record], error) {
r.m.Lock()
defer r.m.Unlock()
r.findProvidersCalls++
return iter.FromSlice([]iter.Result[types.ProviderResponse]{}), nil
return iter.FromSlice([]iter.Result[types.Record]{}), nil
}

// nolint deprecated
func (r *fakeHTTPContentRouter) ProvideBitswap(ctx context.Context, req *server.BitswapWriteProvideRequest) (time.Duration, error) {
r.m.Lock()
defer r.m.Unlock()
r.provideCalls++
r.provideBitswapCalls++
return 0, nil
}

func (r *fakeHTTPContentRouter) Provide(ctx context.Context, req *server.WriteProvideRequest) (types.ProviderResponse, error) {
func (r *fakeHTTPContentRouter) FindPeers(ctx context.Context, pid peer.ID, limit int) (iter.ResultIter[types.Record], error) {
r.m.Lock()
defer r.m.Unlock()
r.provideCalls++
return nil, nil
r.findPeersCalls++
return iter.FromSlice([]iter.Result[types.Record]{}), nil
}

func (r *fakeHTTPContentRouter) FindIPNSRecord(ctx context.Context, name ipns.Name) (*ipns.Record, error) {
func (r *fakeHTTPContentRouter) GetIPNS(ctx context.Context, name ipns.Name) (*ipns.Record, error) {
return nil, routing.ErrNotSupported
}

func (r *fakeHTTPContentRouter) ProvideIPNSRecord(ctx context.Context, name ipns.Name, rec *ipns.Record) error {
func (r *fakeHTTPContentRouter) PutIPNS(ctx context.Context, name ipns.Name, rec *ipns.Record) error {
return routing.ErrNotSupported
}

Expand Down
Loading

0 comments on commit 4606586

Please sign in to comment.