Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 108 additions & 47 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,43 @@ import (
"encoding/json"
"errors"
"fmt"
"io"
"os"
"time"

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

func findProviders(ctx context.Context, key cid.Cid, endpoint string, prettyOutput bool) error {
type askClient struct {
drc *client.Client
out io.Writer
pretty bool
}

func newAskClient(endpoint string, prettyOutput bool, out io.Writer) (*askClient, error) {
drc, err := client.New(endpoint)
if err != nil {
return err
return nil, err
}

recordsIter, err := drc.FindProviders(ctx, key)
if out == nil {
out = os.Stdout
}

return &askClient{
drc: drc,
pretty: prettyOutput,
out: out,
}, nil
}

func (a *askClient) findProviders(ctx context.Context, key cid.Cid) error {
recordsIter, err := a.drc.FindProviders(ctx, key)
if err != nil {
return err
}
Expand All @@ -40,33 +60,24 @@ func findProviders(ctx context.Context, key cid.Cid, endpoint string, prettyOutp
return nil
}

if prettyOutput {
if a.pretty {
switch res.Val.GetSchema() {
case types.SchemaPeer:
record := res.Val.(*types.PeerRecord)
fmt.Fprintln(os.Stdout, record.ID)
fmt.Fprintln(os.Stdout, "\tProtocols:", record.Protocols)
fmt.Fprintln(os.Stdout, "\tAddresses:", record.Addrs)

//lint:ignore SA1019 // ignore staticcheck
case types.SchemaBitswap:
//lint:ignore SA1019 // ignore staticcheck
record := res.Val.(*types.BitswapRecord)
fmt.Fprintln(os.Stdout, record.ID)
fmt.Fprintln(os.Stdout, "\tProtocol:", record.Protocol)
fmt.Fprintln(os.Stdout, "\tAddresses:", record.Addrs)

fmt.Fprintln(a.out, record.ID)
fmt.Fprintln(a.out, "\tProtocols:", record.Protocols)
fmt.Fprintln(a.out, "\tAddresses:", record.Addrs)
default:
// This is an unknown schema. Let's just print it raw.
err := json.NewEncoder(os.Stdout).Encode(res.Val)
err := json.NewEncoder(a.out).Encode(res.Val)
if err != nil {
return err
}
}

fmt.Fprintln(os.Stdout)
fmt.Fprintln(a.out)
} else {
err := json.NewEncoder(os.Stdout).Encode(res.Val)
err := json.NewEncoder(a.out).Encode(res.Val)
if err != nil {
return err
}
Expand All @@ -76,13 +87,56 @@ func findProviders(ctx context.Context, key cid.Cid, endpoint string, prettyOutp
return nil
}

func findPeers(ctx context.Context, pid peer.ID, endpoint string, prettyOutput bool) error {
drc, err := client.New(endpoint)
func (a *askClient) provide(ctx context.Context, records ...*types.AnnouncementRecord) error {
for _, rec := range records {
err := rec.Verify()
if err != nil {
return err
}
}

recordsIter, err := a.drc.ProvideRecords(ctx, records...)
if err != nil {
return err
}
defer recordsIter.Close()
return a.printProvideResult(recordsIter)
}

func (a *askClient) printProvideResult(recordsIter iter.ResultIter[*types.AnnouncementResponseRecord]) error {
for recordsIter.Next() {
res := recordsIter.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
}

if a.pretty {
if res.Val.Error != "" {
fmt.Fprintf(a.out, "Error: %s", res.Val.Error)
} else {
fmt.Fprintf(a.out, "TTL: %s", res.Val.TTL)
}
fmt.Fprintln(a.out)
} else {
err := json.NewEncoder(a.out).Encode(res.Val)
if err != nil {
return err
}
}
}

recordsIter, err := drc.FindPeers(ctx, pid)
return nil
}

func (a *askClient) findPeers(ctx context.Context, pid peer.ID) error {
recordsIter, err := a.drc.FindPeers(ctx, pid)
if err != nil {
return err
}
Expand All @@ -101,13 +155,13 @@ func findPeers(ctx context.Context, pid peer.ID, endpoint string, prettyOutput b
return nil
}

if prettyOutput {
fmt.Fprintln(os.Stdout, res.Val.ID)
fmt.Fprintln(os.Stdout, "\tProtocols:", res.Val.Protocols)
fmt.Fprintln(os.Stdout, "\tAddresses:", res.Val.Addrs)
fmt.Fprintln(os.Stdout)
if a.pretty {
fmt.Fprintln(a.out, res.Val.ID)
fmt.Fprintln(a.out, "\tProtocols:", res.Val.Protocols)
fmt.Fprintln(a.out, "\tAddresses:", res.Val.Addrs)
fmt.Fprintln(a.out)
} else {
err := json.NewEncoder(os.Stdout).Encode(res.Val)
err := json.NewEncoder(a.out).Encode(res.Val)
if err != nil {
return err
}
Expand All @@ -117,18 +171,30 @@ func findPeers(ctx context.Context, pid peer.ID, endpoint string, prettyOutput b
return nil
}

func getIPNS(ctx context.Context, name ipns.Name, endpoint string, prettyOutput bool) error {
drc, err := client.New(endpoint)
func (a *askClient) providePeer(ctx context.Context, records ...*types.AnnouncementRecord) error {
for _, rec := range records {
err := rec.Verify()
if err != nil {
return err
}
}

recordsIter, err := a.drc.ProvidePeerRecords(ctx, records...)
if err != nil {
return err
}
defer recordsIter.Close()

rec, err := drc.GetIPNS(ctx, name)
return a.printProvideResult(recordsIter)
}

func (a *askClient) getIPNS(ctx context.Context, name ipns.Name) error {
rec, err := a.drc.GetIPNS(ctx, name)
if err != nil {
return err
}

if prettyOutput {
if a.pretty {
v, err := rec.Value()
if err != nil {
return err
Expand All @@ -144,19 +210,19 @@ func getIPNS(ctx context.Context, name ipns.Name, endpoint string, prettyOutput
return err
}

fmt.Printf("/ipns/%s\n", name)
fmt.Fprintf(a.out, "/ipns/%s\n", name)

// Since [client.Client.GetIPNS] verifies if the retrieved record is valid, we
// do not need to verify it again. However, if you were not using this specific
// client, but using some other tool, you should always validate the IPNS Record
// using the [ipns.Validate] or [ipns.ValidateWithName] functions.
fmt.Println("\tSignature Validated")
fmt.Println("\tValue:", v.String())
fmt.Println("\tSequence:", seq)
fmt.Println("\tValidityType : EOL/End-of-Life")
fmt.Println("\tValidity:", eol.Format(time.RFC3339))
fmt.Fprintln(a.out, "\tSignature Validated")
fmt.Fprintln(a.out, "\tValue:", v.String())
fmt.Fprintln(a.out, "\tSequence:", seq)
fmt.Fprintln(a.out, "\tValidityType : EOL/End-of-Life")
fmt.Fprintln(a.out, "\tValidity:", eol.Format(time.RFC3339))
if ttl, err := rec.TTL(); err == nil {
fmt.Println("\tTTL:", ttl.String())
fmt.Fprintln(a.out, "\tTTL:", ttl.String())
}

return nil
Expand All @@ -167,20 +233,15 @@ func getIPNS(ctx context.Context, name ipns.Name, endpoint string, prettyOutput
return err
}

_, err = os.Stdout.Write(raw)
_, err = a.out.Write(raw)
return err
}

func putIPNS(ctx context.Context, name ipns.Name, record []byte, endpoint string) error {
drc, err := client.New(endpoint)
if err != nil {
return err
}

func (a *askClient) putIPNS(ctx context.Context, name ipns.Name, record []byte) error {
rec, err := ipns.UnmarshalRecord(record)
if err != nil {
return err
}

return drc.PutIPNS(ctx, name, rec)
return a.drc.PutIPNS(ctx, name, rec)
}
22 changes: 22 additions & 0 deletions docs/environment-variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
- [Configuration](#configuration)
- [`SOMEGUY_LISTEN_ADDRESS`](#someguy_listen_address)
- [`SOMEGUY_ACCELERATED_DHT`](#someguy_accelerated_dht)
- [`SOMEGUY_PUT_ENABLED`](#someguy_put_enabled)
- [`SOMEGUY_DATADIR`](#someguy_datadir)
- [`SOMEGUY_PROVIDER_ENDPOINTS`](#someguy_provider_endpoints)
- [`SOMEGUY_PEER_ENDPOINTS`](#someguy_peer_endpoints)
- [`SOMEGUY_IPNS_ENDPOINTS`](#someguy_ipns_endpoints)
Expand All @@ -28,6 +30,26 @@ Whether or not the Accelerated DHT is enabled or not.

Default: `true`

### `SOMEGUY_PUT_ENABLED`

Whether or not to accept Delegated Routing V1 PUT requests. Affects all PUT requests:
provider records, peer records and IPNS records.

By default, PUT requests are ignored. Therefore, they will neither be stored locally,
nor sent to other remote endpoints.

Default: `false`

### `SOMEGUY_DATADIR`

Used in conjunction with [`SOMEGUY_PUT_ENABLED`](#someguy_put_enabled).

The LevelDB data directory to persist PUT records. When receiving PUT requests,
the records will be stored in this database. The database is queried for GET
requests.

Default: none

### `SOMEGUY_PROVIDER_ENDPOINTS`

Comma-separated list of other Delegated Routing V1 endpoints to proxy provider requests to.
Expand Down
11 changes: 7 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ go 1.21
require (
github.com/CAFxX/httpcompression v0.0.9
github.com/felixge/httpsnoop v1.0.4
github.com/ipfs/boxo v0.19.1-0.20240415103851-7f9506844904
github.com/ipfs/boxo v0.19.1-0.20240507133722-cc217db63f3b
github.com/ipfs/go-cid v0.4.1
github.com/ipfs/go-datastore v0.6.0
github.com/ipfs/go-ds-leveldb v0.5.0
github.com/ipfs/go-log/v2 v2.5.1
github.com/libp2p/go-libp2p v0.33.2
github.com/libp2p/go-libp2p-kad-dht v0.25.2
Expand All @@ -16,14 +18,15 @@ require (
github.com/multiformats/go-multihash v0.2.3
github.com/prometheus/client_golang v1.19.0
github.com/rs/cors v1.10.1
github.com/samber/lo v1.39.0
github.com/slok/go-http-metrics v0.11.0
github.com/stretchr/testify v1.9.0
github.com/urfave/cli/v2 v2.27.1
)

require (
github.com/Jorropo/jsync v1.0.1 // indirect
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/andybalholm/brotli v1.0.6 // indirect
github.com/benbjohnson/clock v1.3.5 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
Expand All @@ -42,6 +45,7 @@ require (
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/gopacket v1.1.19 // indirect
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd // indirect
github.com/google/uuid v1.6.0 // indirect
Expand All @@ -51,7 +55,6 @@ require (
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/golang-lru v1.0.2 // indirect
github.com/huin/goupnp v1.3.0 // indirect
github.com/ipfs/go-datastore v0.6.0 // indirect
github.com/ipfs/go-log v1.0.5 // indirect
github.com/ipld/go-ipld-prime v0.21.0 // indirect
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
Expand Down Expand Up @@ -101,9 +104,9 @@ require (
github.com/quic-go/webtransport-go v0.7.0 // indirect
github.com/raulk/go-watchdog v1.3.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/samber/lo v1.39.0 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/syndtr/goleveldb v1.0.0 // indirect
github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect
go.opencensus.io v0.24.0 // indirect
Expand Down
Loading