Skip to content

Commit a97f44f

Browse files
committed
routing/http: add more type and WithDynamicProviderInfo
1 parent 7240fcf commit a97f44f

File tree

4 files changed

+119
-13
lines changed

4 files changed

+119
-13
lines changed

routing/http/client/client.go

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"fmt"
99
"mime"
1010
"net/http"
11+
"net/url"
1112
"strings"
1213
"time"
1314

@@ -54,7 +55,7 @@ type client struct {
5455
accepts string
5556

5657
peerID peer.ID
57-
addrs []types.Multiaddr
58+
addrs func() ([]types.Multiaddr, error)
5859
identity crypto.PrivKey
5960

6061
// called immeidately after signing a provide req
@@ -104,10 +105,37 @@ func WithUserAgent(ua string) Option {
104105
}
105106

106107
func WithProviderInfo(peerID peer.ID, addrs []multiaddr.Multiaddr) Option {
108+
taddrs := make([]types.Multiaddr, len(addrs))
109+
for i, v := range addrs {
110+
taddrs[i] = types.Multiaddr{Multiaddr: v}
111+
}
112+
107113
return func(c *client) {
108114
c.peerID = peerID
109-
for _, a := range addrs {
110-
c.addrs = append(c.addrs, types.Multiaddr{Multiaddr: a})
115+
c.addrs = func() ([]types.Multiaddr, error) {
116+
return taddrs, nil
117+
}
118+
}
119+
}
120+
121+
// WithDynamicProviderInfo is like [WithProviderInfo] but the addresses will be queried on each publish operation.
122+
// This is usefull for nodes with changing addresses, like P2P daemons behind NATs.
123+
// Note: due to API limitations can't trivially batch update previous records with new addresses, so you are still relient
124+
// on an consumers using a PeerRouter able to follow your new addresses, for example the IPFS DHT.
125+
func WithDynamicProviderInfo(peerID peer.ID, addrs func() ([]multiaddr.Multiaddr, error)) Option {
126+
return func(c *client) {
127+
c.peerID = peerID
128+
c.addrs = func() ([]types.Multiaddr, error) {
129+
addrs, err := addrs()
130+
if err != nil {
131+
return nil, err
132+
}
133+
134+
taddrs := make([]types.Multiaddr, len(addrs))
135+
for i, v := range addrs {
136+
taddrs[i] = types.Multiaddr{Multiaddr: v}
137+
}
138+
return taddrs, nil
111139
}
112140
}
113141
}
@@ -120,6 +148,7 @@ func WithStreamResultsRequired() Option {
120148

121149
// New creates a content routing API client.
122150
// The Provider and identity parameters are option. If they are nil, the `Provide` method will not function.
151+
// Consider using the more type-safe option [NewURL].
123152
func New(baseURL string, opts ...Option) (*client, error) {
124153
client := &client{
125154
baseURL: baseURL,
@@ -140,6 +169,11 @@ func New(baseURL string, opts ...Option) (*client, error) {
140169
return client, nil
141170
}
142171

172+
// NewURL is a more type-safe version of [New], it takes in an [url.URL].
173+
func NewURL(baseURL url.URL, opts ...Option) (*client, error) {
174+
return New(baseURL.String(), opts...)
175+
}
176+
143177
// measuringIter measures the length of the iter and then publishes metrics about the whole req once the iter is closed.
144178
// Of course, if the caller forgets to close the iter, this won't publish anything.
145179
type measuringIter[T any] struct {
@@ -251,6 +285,11 @@ func (c *client) ProvideBitswap(ctx context.Context, keys []cid.Cid, ttl time.Du
251285

252286
now := c.clock.Now()
253287

288+
addrs, err := c.addrs()
289+
if err != nil {
290+
return 0, fmt.Errorf("failed to query our addresses: %w", err)
291+
}
292+
254293
req := types.WriteBitswapProviderRecord{
255294
Protocol: "transport-bitswap",
256295
Schema: types.SchemaBitswap,
@@ -259,10 +298,10 @@ func (c *client) ProvideBitswap(ctx context.Context, keys []cid.Cid, ttl time.Du
259298
AdvisoryTTL: &types.Duration{Duration: ttl},
260299
Timestamp: &types.Time{Time: now},
261300
ID: &c.peerID,
262-
Addrs: c.addrs,
301+
Addrs: addrs,
263302
},
264303
}
265-
err := req.Sign(c.peerID, c.identity)
304+
err = req.Sign(c.peerID, c.identity)
266305
if err != nil {
267306
return 0, err
268307
}

routing/http/client/client_test.go

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ func drAddrsToAddrs(drmas []types.Multiaddr) (addrs []multiaddr.Multiaddr) {
144144
return
145145
}
146146

147-
func makeBSReadProviderResp() types.ReadBitswapProviderRecord {
147+
func makeBSReadProviderResp(t *testing.T) types.ReadBitswapProviderRecord {
148148
peerID, addrs, _ := makeProviderAndIdentity()
149149
return types.ReadBitswapProviderRecord{
150150
Protocol: "transport-bitswap",
@@ -194,7 +194,7 @@ func (e *osErrContains) errContains(t *testing.T, err error) {
194194
}
195195

196196
func TestClient_FindProviders(t *testing.T) {
197-
bsReadProvResp := makeBSReadProviderResp()
197+
bsReadProvResp := makeBSReadProviderResp(t)
198198
bitswapProvs := []iter.Result[types.ProviderResponse]{
199199
{Val: &bsReadProvResp},
200200
}
@@ -412,11 +412,18 @@ func TestClient_Provide(t *testing.T) {
412412
}
413413
}
414414

415+
var addrs []types.Multiaddr
416+
if f := client.addrs; f != nil {
417+
var err error
418+
addrs, err = client.addrs()
419+
require.NoError(t, err)
420+
}
421+
415422
expectedProvReq := &server.BitswapWriteProvideRequest{
416423
Keys: c.cids,
417424
Timestamp: clock.Now().Truncate(time.Millisecond),
418425
AdvisoryTTL: c.ttl,
419-
Addrs: drAddrsToAddrs(client.addrs),
426+
Addrs: drAddrsToAddrs(addrs),
420427
ID: client.peerID,
421428
}
422429

@@ -442,3 +449,62 @@ func TestClient_Provide(t *testing.T) {
442449
})
443450
}
444451
}
452+
453+
func TestWithDynamicClient(t *testing.T) {
454+
t.Parallel()
455+
456+
const ttl = time.Hour
457+
458+
const testUserAgent = "testUserAgent"
459+
peerID, addrs, identity := makeProviderAndIdentity()
460+
router := &mockContentRouter{}
461+
recordingHandler := &recordingHandler{
462+
Handler: server.Handler(router),
463+
f: []func(*http.Request){
464+
func(r *http.Request) {
465+
assert.Equal(t, testUserAgent, r.Header.Get("User-Agent"))
466+
},
467+
},
468+
}
469+
srv := httptest.NewServer(recordingHandler)
470+
t.Cleanup(srv.Close)
471+
serverAddr := "http://" + srv.Listener.Addr().String()
472+
recordingHTTPClient := &recordingHTTPClient{httpClient: defaultHTTPClient}
473+
var rAddrs []multiaddr.Multiaddr
474+
client, err := New(serverAddr,
475+
WithDynamicProviderInfo(peerID, func() ([]multiaddr.Multiaddr, error) { return rAddrs, nil }),
476+
WithIdentity(identity),
477+
WithUserAgent(testUserAgent),
478+
WithHTTPClient(recordingHTTPClient),
479+
)
480+
require.NoError(t, err)
481+
482+
c := makeCID()
483+
rAddrs = addrs[:1]
484+
485+
clock := clock.NewMock()
486+
clock.Set(time.Now())
487+
client.clock = clock
488+
489+
expectedProvReq := &server.BitswapWriteProvideRequest{
490+
Keys: []cid.Cid{c},
491+
Timestamp: clock.Now().Truncate(time.Millisecond),
492+
AdvisoryTTL: ttl,
493+
Addrs: rAddrs,
494+
ID: peerID,
495+
}
496+
router.On("ProvideBitswap", mock.Anything, expectedProvReq).Return(ttl, nil)
497+
498+
ctx := context.Background()
499+
_, err = client.ProvideBitswap(ctx, []cid.Cid{c}, ttl)
500+
require.NoError(t, err)
501+
502+
c = makeCID()
503+
rAddrs = addrs[1:]
504+
505+
expectedProvReq.Keys[0] = c
506+
expectedProvReq.Addrs = rAddrs
507+
508+
_, err = client.ProvideBitswap(ctx, []cid.Cid{c}, ttl)
509+
require.NoError(t, err)
510+
}

routing/http/contentrouter/contentrouter.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,9 @@ func readProviderResponses(iter iter.ResultIter[types.ProviderResponse], ch chan
124124
continue
125125
}
126126

127-
var addrs []multiaddr.Multiaddr
128-
for _, a := range result.Addrs {
129-
addrs = append(addrs, a.Multiaddr)
127+
addrs := make([]multiaddr.Multiaddr, len(result.Addrs))
128+
for i, a := range result.Addrs {
129+
addrs[i] = a.Multiaddr
130130
}
131131

132132
ch <- peer.AddrInfo{

routing/http/contentrouter/contentrouter_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/ipfs/boxo/routing/http/types/iter"
1111
"github.com/ipfs/go-cid"
1212
"github.com/libp2p/go-libp2p/core/peer"
13+
"github.com/multiformats/go-multiaddr"
1314
"github.com/multiformats/go-multihash"
1415
"github.com/stretchr/testify/assert"
1516
"github.com/stretchr/testify/mock"
@@ -135,8 +136,8 @@ func TestFindProvidersAsync(t *testing.T) {
135136
}
136137

137138
expected := []peer.AddrInfo{
138-
{ID: p1},
139-
{ID: p2},
139+
{ID: p1, Addrs: []multiaddr.Multiaddr{}},
140+
{ID: p2, Addrs: []multiaddr.Multiaddr{}},
140141
}
141142

142143
require.Equal(t, expected, actualAIs)

0 commit comments

Comments
 (0)