Skip to content

Commit

Permalink
feat(examples): routing v1 client cli
Browse files Browse the repository at this point in the history
  • Loading branch information
hacdias committed Oct 4, 2023
1 parent e2730a5 commit a58f2a2
Show file tree
Hide file tree
Showing 5 changed files with 238 additions and 0 deletions.
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ Let us know if you find any issue or if you want to contribute and add a new tut
- [Fetching a UnixFS file by CID](./unixfs-file-cid)
- [Gateway backed by a CAR file](./gateway/car)
- [Gateway backed by a remote blockstore and IPNS resolver](./gateway/proxy)
- [Delegated Routing V1 Command Line Client](./routing-v1/client/)
1 change: 1 addition & 0 deletions examples/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ require (
github.com/quic-go/quic-go v0.38.0 // indirect
github.com/quic-go/webtransport-go v0.5.3 // indirect
github.com/raulk/go-watchdog v1.3.0 // indirect
github.com/samber/lo v1.36.0 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb // indirect
github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc // indirect
Expand Down
4 changes: 4 additions & 0 deletions examples/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
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/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 @@ -478,6 +479,8 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/samber/lo v1.36.0 h1:4LaOxH1mHnbDGhTVE0i1z8v/lWaQW8AIfOD3HU4mSaw=
github.com/samber/lo v1.36.0/go.mod h1:HLeWcJRRyLKp3+/XBJvOrerCQn9mhdKMHyd7IRlgeQ8=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
Expand Down Expand Up @@ -530,6 +533,7 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M=
github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk=
github.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb h1:Ywfo8sUltxogBpFuMOFRrrSifO788kAFxmvVw31PtQQ=
github.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb/go.mod h1:ikPs9bRWicNw3S7XpJ8sK/smGwU9WcSVU3dy9qahYBM=
Expand Down
68 changes: 68 additions & 0 deletions examples/routing-v1/client/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Delegated Routing V1 Command Line Client

This is an example of how to use the Delegated Routing V1 HTTP client from Boxo.
In this package, we build a small command line tool that allows you to connect to
a Routing V1 endpoint and fetch content providers, peer information, as well as
IPNS records for a certain IPNS name.

## Build

```bash
> go build -o routing-client
```

## Usage

First, you will need a Routing V1 server. For that, you can potentially use [Kubo],
which supports [exposing](https://github.com/ipfs/kubo/blob/master/docs/config.md#gatewayexposeroutingapi)
a Routing V1 endpoint. For the commands below, we assume the endpoint is `http://127.0.0.1:8080`.

### Find CID Providers

To find providers, provide the flag `-c` with the [CID] of the content you're looking for:

```console
$ ./routing-client -e http://127.0.0.1:8080 -c bafkreifjjcie6lypi6ny7amxnfftagclbuxndqonfipmb64f2km2devei4

12D3KooWEfL19QqRGGLraaAYw1XA3dtDdVRYaHt6jymFxcuQo3Zm
Protocols: []
Addresses: [/ip4/163.47.51.218/tcp/28131]
12D3KooWK53GAx2g2UUYfJHHjxDbVLeDgGxNMHXDWeJa5KgMhTD2
Protocols: []
Addresses: [/ip4/195.167.147.43/udp/8888/quic /ip4/195.167.147.43/tcp/8888]
12D3KooWCpr8kACTRLKrPy4LPpSX7LXvKQ7eYqTmY8CBvgK5HZgB
Protocols: []
Addresses: [/ip4/163.47.49.234/tcp/28102]
12D3KooWC9L4RjPGgqpzBUBkcVpKjJYofCkC5i5QdQftg1LdsFb2
Protocols: []
Addresses: [/ip4/198.244.201.187/tcp/4001]
```

### Find Peer Information

To find a peer, provide the flag `-p` with the [Peer ID] of the peer you're looking for:


```console
$ ./routing-client -e http://127.0.0.1:8080 -p 12D3KooWC9L4RjPGgqpzBUBkcVpKjJYofCkC5i5QdQftg1LdsFb2

12D3KooWC9L4RjPGgqpzBUBkcVpKjJYofCkC5i5QdQftg1LdsFb2
Protocols: []
Addresses: [/ip4/198.244.201.187/tcp/4001]
```

### Get an IPNS Record

To find an IPNS record, provide the flag `-n` with the [IPNS Name] you're trying to find a record for:

```console
$ ./routing-client -e http://127.0.0.1:8080 -n /ipns/k51qzi5uqu5diuz0h5tjqama8qbmyxusvqz2hfgn5go5l07l9k2ubqa09m7toe

/ipns/k51qzi5uqu5diuz0h5tjqama8qbmyxusvqz2hfgn5go5l07l9k2ubqa09m7toe
Value: /ipfs/QmUGMoVz62ZARyxkrdEiwmFZanTwVWLLu6EAWvbWHNcwR8
```

[Kubo]: https://github.com/ipfs/kubo
[CID]: https://docs.ipfs.tech/concepts/content-addressing/#what-is-a-cid
[Peer ID]: https://docs.libp2p.io/concepts/fundamentals/peers/#peer-id
[IPNS Name]: https://specs.ipfs.tech/ipns/ipns-record/#ipns-name
164 changes: 164 additions & 0 deletions examples/routing-v1/client/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
package main

import (
"context"
"errors"
"flag"
"fmt"
"log"
"time"

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

func main() {
gatewayUrlPtr := flag.String("e", "", "routing v1 endpoint to use")
timeoutPtr := flag.Int("t", 10, "timeout in seconds for lookup")
cidPtr := flag.String("c", "", "cid to find")
pidPtr := flag.String("p", "", "peer to find")
namePtr := flag.String("n", "", "ipns name to retrieve record for")
flag.Parse()

// Creates a new Delegated Routing V1 client.
client, err := client.New(*gatewayUrlPtr)
if err != nil {
log.Fatal(err)
}

timeout := time.Duration((*timeoutPtr)) * time.Second
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()

if *cidPtr != "" {
err = findProviders(ctx, client, *cidPtr)
} else if *pidPtr != "" {
err = findPeers(ctx, client, *pidPtr)
} else if *namePtr != "" {
err = findIPNS(ctx, client, *namePtr)
} else {
err = errors.New("cid or peer must be provided")
}

if err != nil {
log.Fatal(err)
}
}

func findProviders(ctx context.Context, client *client.Client, cidStr string) error {
// Parses the given CID to lookup the providers for.
contentCid, err := cid.Parse(cidStr)
if err != nil {
return err
}

// Ask for providers providing the given content CID.
provIter, err := client.FindProviders(ctx, contentCid)
if err != nil {
return err
}
defer provIter.Close()

// The response is streamed. Alternatively, you could use [iter.ReadAll]
// to fetch all the results all at once, instead of iterating as they are
// streamed.
for provIter.Next() {
res := provIter.Val()

// Check for error, but do not complain if we exceeded the timeout. We are
// expecting that to happen: we explicitly defined a timeout.
if res.Err != nil {
if !errors.Is(res.Err, context.DeadlineExceeded) {
return res.Err
}

return nil
}

switch res.Val.GetSchema() {
case types.SchemaPeer:
record := res.Val.(*types.PeerRecord)
fmt.Println(record.ID)
fmt.Println("\tProtocols:", record.Protocols)
fmt.Println("\tAddresses:", record.Addrs)
default:
// You may not want to fail here, it's up to you. You can just handle
// the schemas you want, or that you know, but not fail.
log.Printf("unrecognized schema: %s", res.Val.GetSchema())
}
}

return nil
}

func findPeers(ctx context.Context, client *client.Client, pidStr string) error {
// Parses the given Peer ID to lookup the information for.
pid, err := peer.Decode(pidStr)
if err != nil {
return err
}

// Ask for information about the peer with the given peer ID.
provIter, err := client.FindPeers(ctx, pid)
if err != nil {
return err
}
defer provIter.Close()

// The response is streamed. Alternatively, you could use [iter.ReadAll]
// to fetch all the results all at once, instead of iterating as they are
// streamed.
for provIter.Next() {
res := provIter.Val()

// Check for error, but do not complain if we exceeded the timeout. We are
// expecting that to happen: we explicitly defined a timeout.
if res.Err != nil {
if !errors.Is(res.Err, context.DeadlineExceeded) {
return res.Err
}

return nil
}

switch res.Val.GetSchema() {
case types.SchemaPeer:
record := res.Val.(*types.PeerRecord)
fmt.Println(record.ID)
fmt.Println("\tProtocols:", record.Protocols)
fmt.Println("\tAddresses:", record.Addrs)
default:
// You may not want to fail here, it's up to you. You can just handle
// the schemas you want, or that you know, but not fail.
log.Printf("unrecognized schema: %s", res.Val.GetSchema())
}
}

return nil
}

func findIPNS(ctx context.Context, client *client.Client, nameStr string) error {
// Parses the given name string to get a record for.
name, err := ipns.NameFromString(nameStr)
if err != nil {
return err
}

// Ask for a record. [client.GetIPNS] validates the record for us.
record, err := client.GetIPNS(ctx, name)
if err != nil {
return err
}

v, err := record.Value()
if err != nil {
return err
}

fmt.Printf("/ipns/%s\n", name)
fmt.Println("\tValue:", v.String())
return nil
}

0 comments on commit a58f2a2

Please sign in to comment.