@@ -16,22 +16,20 @@ import (
16
16
ipns "github.com/ipfs/boxo/ipns"
17
17
"github.com/ipfs/boxo/routing/http/contentrouter"
18
18
"github.com/ipfs/boxo/routing/http/internal/drjson"
19
- "github.com/ipfs/boxo/routing/http/server"
20
19
"github.com/ipfs/boxo/routing/http/types"
21
20
"github.com/ipfs/boxo/routing/http/types/iter"
22
21
jsontypes "github.com/ipfs/boxo/routing/http/types/json"
23
22
"github.com/ipfs/boxo/routing/http/types/ndjson"
24
23
"github.com/ipfs/go-cid"
25
24
logging "github.com/ipfs/go-log/v2"
26
- record "github.com/libp2p/go-libp2p-record"
27
25
"github.com/libp2p/go-libp2p/core/crypto"
28
26
"github.com/libp2p/go-libp2p/core/peer"
29
27
"github.com/multiformats/go-multiaddr"
30
28
)
31
29
32
30
var (
33
31
_ contentrouter.Client = & client {}
34
- logger = logging .Logger ("service/delegatedrouting " )
32
+ logger = logging .Logger ("routing/http/client " )
35
33
defaultHTTPClient = & http.Client {
36
34
Transport : & ResponseBodyLimitedTransport {
37
35
RoundTripper : http .DefaultTransport ,
@@ -50,18 +48,17 @@ const (
50
48
type client struct {
51
49
baseURL string
52
50
httpClient httpClient
53
- validator record.Validator
54
51
clock clock.Clock
55
-
56
- accepts string
52
+ accepts string
57
53
58
54
peerID peer.ID
59
55
addrs []types.Multiaddr
60
56
identity crypto.PrivKey
61
57
62
- // called immeidately after signing a provide req
63
- // used for testing, e.g. testing the server with a mangled signature
64
- afterSignCallback func (req * types.WriteBitswapProviderRecord )
58
+ // Called immediately after signing a provide request. It is used
59
+ // for testing, e.g., testing the server with a mangled signature.
60
+ //lint:ignore SA1019 // ignore staticcheck
61
+ afterSignCallback func (req * types.WriteBitswapRecord )
65
62
}
66
63
67
64
// defaultUserAgent is used as a fallback to inform HTTP server which library
@@ -121,12 +118,11 @@ func WithStreamResultsRequired() Option {
121
118
}
122
119
123
120
// New creates a content routing API client.
124
- // The Provider and identity parameters are option. If they are nil, the `Provide` method will not function.
121
+ // The Provider and identity parameters are option. If they are nil, the [client.ProvideBitswap] method will not function.
125
122
func New (baseURL string , opts ... Option ) (* client , error ) {
126
123
client := & client {
127
124
baseURL : baseURL ,
128
125
httpClient : defaultHTTPClient ,
129
- validator : ipns.Validator {},
130
126
clock : clock .New (),
131
127
accepts : strings .Join ([]string {mediaTypeNDJSON , mediaTypeJSON }, "," ),
132
128
}
@@ -164,11 +160,11 @@ func (c *measuringIter[T]) Close() error {
164
160
return c .Iter .Close ()
165
161
}
166
162
167
- func (c * client ) FindProviders (ctx context.Context , key cid.Cid ) (provs iter.ResultIter [types.ProviderResponse ], err error ) {
163
+ func (c * client ) FindProviders (ctx context.Context , key cid.Cid ) (providers iter.ResultIter [types.Record ], err error ) {
168
164
// TODO test measurements
169
165
m := newMeasurement ("FindProviders" )
170
166
171
- url := c .baseURL + server . ProvidePath + key .String ()
167
+ url := c .baseURL + "/routing/v1/providers/" + key .String ()
172
168
req , err := http .NewRequestWithContext (ctx , http .MethodGet , url , nil )
173
169
if err != nil {
174
170
return nil , err
@@ -192,7 +188,7 @@ func (c *client) FindProviders(ctx context.Context, key cid.Cid) (provs iter.Res
192
188
if resp .StatusCode == http .StatusNotFound {
193
189
resp .Body .Close ()
194
190
m .record (ctx )
195
- return iter.FromSlice [iter.Result [types.ProviderResponse ]](nil ), nil
191
+ return iter.FromSlice [iter.Result [types.Record ]](nil ), nil
196
192
}
197
193
198
194
if resp .StatusCode != http .StatusOK {
@@ -220,24 +216,27 @@ func (c *client) FindProviders(ctx context.Context, key cid.Cid) (provs iter.Res
220
216
}
221
217
}()
222
218
223
- var it iter.ResultIter [types.ProviderResponse ]
219
+ var it iter.ResultIter [types.Record ]
224
220
switch mediaType {
225
221
case mediaTypeJSON :
226
- parsedResp := & jsontypes.ReadProvidersResponse {}
222
+ parsedResp := & jsontypes.ProvidersResponse {}
227
223
err = json .NewDecoder (resp .Body ).Decode (parsedResp )
228
- var sliceIt iter.Iter [types.ProviderResponse ] = iter .FromSlice (parsedResp .Providers )
224
+ var sliceIt iter.Iter [types.Record ] = iter .FromSlice (parsedResp .Providers )
229
225
it = iter .ToResultIter (sliceIt )
230
226
case mediaTypeNDJSON :
231
227
skipBodyClose = true
232
- it = ndjson .NewReadProvidersResponseIter (resp .Body )
228
+ it = ndjson .NewRecordsIter (resp .Body )
233
229
default :
234
230
logger .Errorw ("unknown media type" , "MediaType" , mediaType , "ContentType" , respContentType )
235
231
return nil , errors .New ("unknown content type" )
236
232
}
237
233
238
- return & measuringIter [iter.Result [types.ProviderResponse ]]{Iter : it , ctx : ctx , m : m }, nil
234
+ return & measuringIter [iter.Result [types.Record ]]{Iter : it , ctx : ctx , m : m }, nil
239
235
}
240
236
237
+ // Deprecated: protocol-agnostic provide is being worked on in [IPIP-378]:
238
+ //
239
+ // [IPIP-378]: https://github.com/ipfs/specs/pull/378
241
240
func (c * client ) ProvideBitswap (ctx context.Context , keys []cid.Cid , ttl time.Duration ) (time.Duration , error ) {
242
241
if c .identity == nil {
243
242
return 0 , errors .New ("cannot provide Bitswap records without an identity" )
@@ -253,7 +252,7 @@ func (c *client) ProvideBitswap(ctx context.Context, keys []cid.Cid, ttl time.Du
253
252
254
253
now := c .clock .Now ()
255
254
256
- req := types.WriteBitswapProviderRecord {
255
+ req := types.WriteBitswapRecord {
257
256
Protocol : "transport-bitswap" ,
258
257
Schema : types .SchemaBitswap ,
259
258
Payload : types.BitswapPayload {
@@ -282,10 +281,13 @@ func (c *client) ProvideBitswap(ctx context.Context, keys []cid.Cid, ttl time.Du
282
281
}
283
282
284
283
// ProvideAsync makes a provide request to a delegated router
285
- func (c * client ) provideSignedBitswapRecord (ctx context.Context , bswp * types.WriteBitswapProviderRecord ) (time.Duration , error ) {
286
- req := jsontypes.WriteProvidersRequest {Providers : []types.WriteProviderRecord {bswp }}
284
+ //
285
+ //lint:ignore SA1019 // ignore staticcheck
286
+ func (c * client ) provideSignedBitswapRecord (ctx context.Context , bswp * types.WriteBitswapRecord ) (time.Duration , error ) {
287
+ //lint:ignore SA1019 // ignore staticcheck
288
+ req := jsontypes.WriteProvidersRequest {Providers : []types.Record {bswp }}
287
289
288
- url := c .baseURL + server . ProvidePath
290
+ url := c .baseURL + "/routing/v1/providers/"
289
291
290
292
b , err := drjson .MarshalJSONBytes (req )
291
293
if err != nil {
@@ -306,6 +308,8 @@ func (c *client) provideSignedBitswapRecord(ctx context.Context, bswp *types.Wri
306
308
if resp .StatusCode != http .StatusOK {
307
309
return 0 , httpError (resp .StatusCode , resp .Body )
308
310
}
311
+
312
+ //lint:ignore SA1019 // ignore staticcheck
309
313
var provideResult jsontypes.WriteProvidersResponse
310
314
err = json .NewDecoder (resp .Body ).Decode (& provideResult )
311
315
if err != nil {
@@ -315,7 +319,8 @@ func (c *client) provideSignedBitswapRecord(ctx context.Context, bswp *types.Wri
315
319
return 0 , fmt .Errorf ("expected 1 result but got %d" , len (provideResult .ProvideResults ))
316
320
}
317
321
318
- v , ok := provideResult .ProvideResults [0 ].(* types.WriteBitswapProviderRecordResponse )
322
+ //lint:ignore SA1019 // ignore staticcheck
323
+ v , ok := provideResult .ProvideResults [0 ].(* types.WriteBitswapRecordResponse )
319
324
if ! ok {
320
325
return 0 , fmt .Errorf ("expected AdvisoryTTL field" )
321
326
}
@@ -327,7 +332,80 @@ func (c *client) provideSignedBitswapRecord(ctx context.Context, bswp *types.Wri
327
332
return 0 , nil
328
333
}
329
334
330
- func (c * client ) FindIPNSRecord (ctx context.Context , name ipns.Name ) (* ipns.Record , error ) {
335
+ func (c * client ) FindPeers (ctx context.Context , pid peer.ID ) (peers iter.ResultIter [types.Record ], err error ) {
336
+ m := newMeasurement ("FindPeers" )
337
+
338
+ url := c .baseURL + "/routing/v1/peers/" + peer .ToCid (pid ).String ()
339
+ req , err := http .NewRequestWithContext (ctx , http .MethodGet , url , nil )
340
+ if err != nil {
341
+ return nil , err
342
+ }
343
+ req .Header .Set ("Accept" , c .accepts )
344
+
345
+ m .host = req .Host
346
+
347
+ start := c .clock .Now ()
348
+ resp , err := c .httpClient .Do (req )
349
+
350
+ m .err = err
351
+ m .latency = c .clock .Since (start )
352
+
353
+ if err != nil {
354
+ m .record (ctx )
355
+ return nil , err
356
+ }
357
+
358
+ m .statusCode = resp .StatusCode
359
+ if resp .StatusCode == http .StatusNotFound {
360
+ resp .Body .Close ()
361
+ m .record (ctx )
362
+ return iter.FromSlice [iter.Result [types.Record ]](nil ), nil
363
+ }
364
+
365
+ if resp .StatusCode != http .StatusOK {
366
+ err := httpError (resp .StatusCode , resp .Body )
367
+ resp .Body .Close ()
368
+ m .record (ctx )
369
+ return nil , err
370
+ }
371
+
372
+ respContentType := resp .Header .Get ("Content-Type" )
373
+ mediaType , _ , err := mime .ParseMediaType (respContentType )
374
+ if err != nil {
375
+ resp .Body .Close ()
376
+ m .err = err
377
+ m .record (ctx )
378
+ return nil , fmt .Errorf ("parsing Content-Type: %w" , err )
379
+ }
380
+
381
+ m .mediaType = mediaType
382
+
383
+ var skipBodyClose bool
384
+ defer func () {
385
+ if ! skipBodyClose {
386
+ resp .Body .Close ()
387
+ }
388
+ }()
389
+
390
+ var it iter.ResultIter [types.Record ]
391
+ switch mediaType {
392
+ case mediaTypeJSON :
393
+ parsedResp := & jsontypes.PeersResponse {}
394
+ err = json .NewDecoder (resp .Body ).Decode (parsedResp )
395
+ var sliceIt iter.Iter [types.Record ] = iter .FromSlice (parsedResp .Peers )
396
+ it = iter .ToResultIter (sliceIt )
397
+ case mediaTypeNDJSON :
398
+ skipBodyClose = true
399
+ it = ndjson .NewRecordsIter (resp .Body )
400
+ default :
401
+ logger .Errorw ("unknown media type" , "MediaType" , mediaType , "ContentType" , respContentType )
402
+ return nil , errors .New ("unknown content type" )
403
+ }
404
+
405
+ return & measuringIter [iter.Result [types.Record ]]{Iter : it , ctx : ctx , m : m }, nil
406
+ }
407
+
408
+ func (c * client ) GetIPNS (ctx context.Context , name ipns.Name ) (* ipns.Record , error ) {
331
409
url := c .baseURL + "/routing/v1/ipns/" + name .String ()
332
410
333
411
httpReq , err := http .NewRequestWithContext (ctx , http .MethodGet , url , nil )
@@ -365,7 +443,7 @@ func (c *client) FindIPNSRecord(ctx context.Context, name ipns.Name) (*ipns.Reco
365
443
return record , nil
366
444
}
367
445
368
- func (c * client ) ProvideIPNSRecord (ctx context.Context , name ipns.Name , record * ipns.Record ) error {
446
+ func (c * client ) PutIPNS (ctx context.Context , name ipns.Name , record * ipns.Record ) error {
369
447
url := c .baseURL + "/routing/v1/ipns/" + name .String ()
370
448
371
449
rawRecord , err := ipns .MarshalRecord (record )
0 commit comments