Skip to content

Commit b335bde

Browse files
committed
feat: bubble TTL out of NameSys
1 parent c0f757a commit b335bde

11 files changed

+78
-75
lines changed

gateway/blocks_backend.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -607,7 +607,7 @@ func (bb *BlocksBackend) GetIPNSRecord(ctx context.Context, c cid.Cid) ([]byte,
607607

608608
func (bb *BlocksBackend) GetDNSLinkRecord(ctx context.Context, hostname string) (ifacepath.Path, error) {
609609
if bb.namesys != nil {
610-
p, err := bb.namesys.Resolve(ctx, "/ipns/"+hostname, namesys.ResolveWithDepth(1))
610+
p, _, err := bb.namesys.Resolve(ctx, "/ipns/"+hostname, namesys.ResolveWithDepth(1))
611611
if err == namesys.ErrResolveRecursion {
612612
err = nil
613613
}

gateway/utilities_test.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"regexp"
1212
"strings"
1313
"testing"
14+
"time"
1415

1516
"github.com/ipfs/boxo/blockservice"
1617
ipath "github.com/ipfs/boxo/coreiface/path"
@@ -53,7 +54,7 @@ func mustDo(t *testing.T, req *http.Request) *http.Response {
5354

5455
type mockNamesys map[string]path.Path
5556

56-
func (m mockNamesys) Resolve(ctx context.Context, name string, opts ...namesys.ResolveOption) (value path.Path, err error) {
57+
func (m mockNamesys) Resolve(ctx context.Context, name string, opts ...namesys.ResolveOption) (value path.Path, ttl time.Duration, err error) {
5758
cfg := namesys.DefaultResolveOptions()
5859
for _, o := range opts {
5960
o(&cfg)
@@ -65,24 +66,24 @@ func (m mockNamesys) Resolve(ctx context.Context, name string, opts ...namesys.R
6566
}
6667
for strings.HasPrefix(name, "/ipns/") {
6768
if depth == 0 {
68-
return value, namesys.ErrResolveRecursion
69+
return value, 0, namesys.ErrResolveRecursion
6970
}
7071
depth--
7172

7273
var ok bool
7374
value, ok = m[name]
7475
if !ok {
75-
return "", namesys.ErrResolveFailed
76+
return "", 0, namesys.ErrResolveFailed
7677
}
7778
name = value.String()
7879
}
79-
return value, nil
80+
return value, 0, nil
8081
}
8182

8283
func (m mockNamesys) ResolveAsync(ctx context.Context, name string, opts ...namesys.ResolveOption) <-chan namesys.ResolveResult {
8384
out := make(chan namesys.ResolveResult, 1)
84-
v, err := m.Resolve(ctx, name, opts...)
85-
out <- namesys.ResolveResult{Path: v, Err: err}
85+
v, ttl, err := m.Resolve(ctx, name, opts...)
86+
out <- namesys.ResolveResult{Path: v, TTL: ttl, Err: err}
8687
close(out)
8788
return out
8889
}
@@ -162,7 +163,7 @@ func (mb *mockBackend) GetIPNSRecord(ctx context.Context, c cid.Cid) ([]byte, er
162163

163164
func (mb *mockBackend) GetDNSLinkRecord(ctx context.Context, hostname string) (ipath.Path, error) {
164165
if mb.namesys != nil {
165-
p, err := mb.namesys.Resolve(ctx, "/ipns/"+hostname, namesys.ResolveWithDepth(1))
166+
p, _, err := mb.namesys.Resolve(ctx, "/ipns/"+hostname, namesys.ResolveWithDepth(1))
166167
if err == namesys.ErrResolveRecursion {
167168
err = nil
168169
}

namesys/dns_resolver.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"net"
88
gpath "path"
99
"strings"
10+
"time"
1011

1112
path "github.com/ipfs/boxo/path"
1213
dns "github.com/miekg/dns"
@@ -31,7 +32,7 @@ func NewDNSResolver(lookup LookupTXTFunc) *DNSResolver {
3132
return &DNSResolver{lookupTXT: lookup}
3233
}
3334

34-
func (r *DNSResolver) Resolve(ctx context.Context, name string, options ...ResolveOption) (path.Path, error) {
35+
func (r *DNSResolver) Resolve(ctx context.Context, name string, options ...ResolveOption) (path.Path, time.Duration, error) {
3536
ctx, span := startSpan(ctx, "DNSResolver.Resolve")
3637
defer span.End()
3738

@@ -45,17 +46,17 @@ func (r *DNSResolver) ResolveAsync(ctx context.Context, name string, options ...
4546
return resolveAsync(ctx, r, name, ProcessResolveOptions(options))
4647
}
4748

48-
func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options ResolveOptions) <-chan onceResult {
49+
func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options ResolveOptions) <-chan ResolveResult {
4950
ctx, span := startSpan(ctx, "DNSResolver.ResolveOnceAsync")
5051
defer span.End()
5152

5253
var fqdn string
53-
out := make(chan onceResult, 1)
54+
out := make(chan ResolveResult, 1)
5455
segments := strings.SplitN(name, "/", 2)
5556
domain := segments[0]
5657

5758
if _, ok := dns.IsDomainName(domain); !ok {
58-
out <- onceResult{err: fmt.Errorf("not a valid domain name: %s", domain)}
59+
out <- ResolveResult{Err: fmt.Errorf("not a valid domain name: %s", domain)}
5960
close(out)
6061
return out
6162
}
@@ -95,7 +96,7 @@ func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options
9596
}
9697
if subRes.Err == nil {
9798
p, err := appendPath(subRes.Path)
98-
emitOnceResult(ctx, out, onceResult{value: p, err: err})
99+
emitOnceResult(ctx, out, ResolveResult{Path: p, Err: err})
99100
// Return without waiting for rootRes, since this result
100101
// (for "_dnslink."+fqdn) takes precedence
101102
return
@@ -108,7 +109,7 @@ func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options
108109
}
109110
if rootRes.Err == nil {
110111
p, err := appendPath(rootRes.Path)
111-
emitOnceResult(ctx, out, onceResult{value: p, err: err})
112+
emitOnceResult(ctx, out, ResolveResult{Path: p, Err: err})
112113
// Do not return here. Wait for subRes so that it is
113114
// output last if good, thereby giving subRes precedence.
114115
} else {
@@ -125,7 +126,7 @@ func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options
125126
if rootResErr == ErrResolveFailed && subResErr == ErrResolveFailed {
126127
// Wrap error so that it can be tested if it is a ErrResolveFailed
127128
err := fmt.Errorf("%w: _dnslink subdomain at %q is missing a TXT record (https://docs.ipfs.tech/concepts/dnslink/)", ErrResolveFailed, gpath.Base(name))
128-
emitOnceResult(ctx, out, onceResult{err: err})
129+
emitOnceResult(ctx, out, ResolveResult{Err: err})
129130
}
130131
return
131132
}
@@ -151,20 +152,20 @@ func workDomain(ctx context.Context, r *DNSResolver, name string, res chan Resol
151152
}
152153
}
153154
// Could not look up any text records for name
154-
res <- ResolveResult{"", err}
155+
res <- ResolveResult{Path: "", Err: err}
155156
return
156157
}
157158

158159
for _, t := range txt {
159160
p, err := parseEntry(t)
160161
if err == nil {
161-
res <- ResolveResult{p, nil}
162+
res <- ResolveResult{Path: p, Err: nil}
162163
return
163164
}
164165
}
165166

166167
// There were no TXT records with a dnslink
167-
res <- ResolveResult{"", ErrResolveFailed}
168+
res <- ResolveResult{Path: "", Err: ErrResolveFailed}
168169
}
169170

170171
func parseEntry(txt string) (path.Path, error) {

namesys/ipns_resolver.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ func NewIPNSResolver(route routing.ValueStore) *IPNSResolver {
3333
}
3434
}
3535

36-
func (r *IPNSResolver) Resolve(ctx context.Context, name string, options ...ResolveOption) (path.Path, error) {
36+
func (r *IPNSResolver) Resolve(ctx context.Context, name string, options ...ResolveOption) (path.Path, time.Duration, error) {
3737
ctx, span := startSpan(ctx, "IpnsResolver.Resolve", trace.WithAttributes(attribute.String("Name", name)))
3838
defer span.End()
3939

@@ -47,11 +47,11 @@ func (r *IPNSResolver) ResolveAsync(ctx context.Context, name string, options ..
4747
return resolveAsync(ctx, r, name, ProcessResolveOptions(options))
4848
}
4949

50-
func (r *IPNSResolver) resolveOnceAsync(ctx context.Context, name string, options ResolveOptions) <-chan onceResult {
50+
func (r *IPNSResolver) resolveOnceAsync(ctx context.Context, name string, options ResolveOptions) <-chan ResolveResult {
5151
ctx, span := startSpan(ctx, "IpnsResolver.ResolveOnceAsync", trace.WithAttributes(attribute.String("Name", name)))
5252
defer span.End()
5353

54-
out := make(chan onceResult, 1)
54+
out := make(chan ResolveResult, 1)
5555
log.Debugf("RoutingResolver resolving %s", name)
5656
cancel := func() {}
5757

@@ -65,7 +65,7 @@ func (r *IPNSResolver) resolveOnceAsync(ctx context.Context, name string, option
6565
pid, err := peer.Decode(name)
6666
if err != nil {
6767
log.Debugf("RoutingResolver: could not convert public key hash %s to peer ID: %s\n", name, err)
68-
out <- onceResult{err: err}
68+
out <- ResolveResult{Err: err}
6969
close(out)
7070
cancel()
7171
return out
@@ -79,7 +79,7 @@ func (r *IPNSResolver) resolveOnceAsync(ctx context.Context, name string, option
7979
vals, err := r.routing.SearchValue(ctx, ipnsKey, dht.Quorum(int(options.DhtRecordCount)))
8080
if err != nil {
8181
log.Debugf("RoutingResolver: dht get for name %s failed: %s", name, err)
82-
out <- onceResult{err: err}
82+
out <- ResolveResult{Err: err}
8383
close(out)
8484
cancel()
8585
return out
@@ -101,13 +101,13 @@ func (r *IPNSResolver) resolveOnceAsync(ctx context.Context, name string, option
101101
rec, err := ipns.UnmarshalRecord(val)
102102
if err != nil {
103103
log.Debugf("RoutingResolver: could not unmarshal value for name %s: %s", name, err)
104-
emitOnceResult(ctx, out, onceResult{err: err})
104+
emitOnceResult(ctx, out, ResolveResult{Err: err})
105105
return
106106
}
107107

108108
p, err := rec.Value()
109109
if err != nil {
110-
emitOnceResult(ctx, out, onceResult{err: err})
110+
emitOnceResult(ctx, out, ResolveResult{Err: err})
111111
return
112112
}
113113

@@ -129,11 +129,11 @@ func (r *IPNSResolver) resolveOnceAsync(ctx context.Context, name string, option
129129
}
130130
default:
131131
log.Errorf("encountered error when parsing EOL: %s", err)
132-
emitOnceResult(ctx, out, onceResult{err: err})
132+
emitOnceResult(ctx, out, ResolveResult{Err: err})
133133
return
134134
}
135135

136-
emitOnceResult(ctx, out, onceResult{value: path.Path(p.String()), ttl: ttl})
136+
emitOnceResult(ctx, out, ResolveResult{Path: path.Path(p.String()), TTL: ttl})
137137
case <-ctx.Done():
138138
return
139139
}

namesys/ipns_resolver_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func TestRoutingResolve(t *testing.T) {
3030
err := publisher.Publish(context.Background(), identity.PrivateKey(), h)
3131
require.NoError(t, err)
3232

33-
res, err := resolver.Resolve(context.Background(), identity.ID().Pretty())
33+
res, _, err := resolver.Resolve(context.Background(), identity.ID().Pretty())
3434
require.NoError(t, err)
3535
require.Equal(t, h, res)
3636
}
@@ -89,7 +89,7 @@ func TestPrexistingRecord(t *testing.T) {
8989
}
9090

9191
func verifyCanResolve(r Resolver, name string, exp path.Path) error {
92-
res, err := r.Resolve(context.Background(), name)
92+
res, _, err := r.Resolve(context.Background(), name)
9393
if err != nil {
9494
return err
9595
}

namesys/mpns.go

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -122,16 +122,18 @@ func NewNameSystem(r routing.ValueStore, opts ...Option) (NameSystem, error) {
122122
}
123123

124124
// Resolve implements Resolver.
125-
func (ns *nameSys) Resolve(ctx context.Context, name string, options ...ResolveOption) (path.Path, error) {
125+
func (ns *nameSys) Resolve(ctx context.Context, name string, options ...ResolveOption) (path.Path, time.Duration, error) {
126126
ctx, span := startSpan(ctx, "MPNS.Resolve", trace.WithAttributes(attribute.String("Name", name)))
127127
defer span.End()
128128

129129
if strings.HasPrefix(name, "/ipfs/") {
130-
return path.ParsePath(name)
130+
p, err := path.ParsePath(name)
131+
return p, 0, err
131132
}
132133

133134
if !strings.HasPrefix(name, "/") {
134-
return path.ParsePath("/ipfs/" + name)
135+
p, err := path.ParsePath("/ipfs/" + name)
136+
return p, 0, err
135137
}
136138

137139
return resolve(ctx, ns, name, ProcessResolveOptions(options))
@@ -144,15 +146,15 @@ func (ns *nameSys) ResolveAsync(ctx context.Context, name string, options ...Res
144146
if strings.HasPrefix(name, "/ipfs/") {
145147
p, err := path.ParsePath(name)
146148
res := make(chan ResolveResult, 1)
147-
res <- ResolveResult{p, err}
149+
res <- ResolveResult{Path: p, Err: err}
148150
close(res)
149151
return res
150152
}
151153

152154
if !strings.HasPrefix(name, "/") {
153155
p, err := path.ParsePath("/ipfs/" + name)
154156
res := make(chan ResolveResult, 1)
155-
res <- ResolveResult{p, err}
157+
res <- ResolveResult{Path: p, Err: err}
156158
close(res)
157159
return res
158160
}
@@ -161,19 +163,19 @@ func (ns *nameSys) ResolveAsync(ctx context.Context, name string, options ...Res
161163
}
162164

163165
// resolveOnce implements resolver.
164-
func (ns *nameSys) resolveOnceAsync(ctx context.Context, name string, options ResolveOptions) <-chan onceResult {
166+
func (ns *nameSys) resolveOnceAsync(ctx context.Context, name string, options ResolveOptions) <-chan ResolveResult {
165167
ctx, span := startSpan(ctx, "MPNS.ResolveOnceAsync")
166168
defer span.End()
167169

168-
out := make(chan onceResult, 1)
170+
out := make(chan ResolveResult, 1)
169171

170172
if !strings.HasPrefix(name, ipns.NamespacePrefix) {
171173
name = ipns.NamespacePrefix + name
172174
}
173175
segments := strings.SplitN(name, "/", 4)
174176
if len(segments) < 3 || segments[0] != "" {
175177
log.Debugf("invalid name syntax for %s", name)
176-
out <- onceResult{err: ErrResolveFailed}
178+
out <- ResolveResult{Err: ErrResolveFailed}
177179
close(out)
178180
return out
179181
}
@@ -194,7 +196,7 @@ func (ns *nameSys) resolveOnceAsync(ctx context.Context, name string, options Re
194196
fixedCid := cid.NewCidV1(cid.Libp2pKey, ipnsCid.Hash()).String()
195197
codecErr := fmt.Errorf("peer ID represented as CIDv1 require libp2p-key multicodec: retry with /ipns/%s", fixedCid)
196198
log.Debugf("RoutingResolver: could not convert public key hash %q to peer ID: %s\n", key, codecErr)
197-
out <- onceResult{err: codecErr}
199+
out <- ResolveResult{Err: codecErr}
198200
close(out)
199201
return out
200202
}
@@ -213,7 +215,7 @@ func (ns *nameSys) resolveOnceAsync(ctx context.Context, name string, options Re
213215
span.SetAttributes(attribute.Bool("CacheHit", true))
214216
span.RecordError(err)
215217

216-
out <- onceResult{value: p, err: err}
218+
out <- ResolveResult{Path: p, Err: err}
217219
close(out)
218220
return out
219221
}
@@ -224,37 +226,37 @@ func (ns *nameSys) resolveOnceAsync(ctx context.Context, name string, options Re
224226
} else if _, ok := dns.IsDomainName(key); ok {
225227
res = ns.dnsResolver
226228
} else {
227-
out <- onceResult{err: fmt.Errorf("invalid IPNS root: %q", key)}
229+
out <- ResolveResult{Err: fmt.Errorf("invalid IPNS root: %q", key)}
228230
close(out)
229231
return out
230232
}
231233

232234
resCh := res.resolveOnceAsync(ctx, key, options)
233-
var best onceResult
235+
var best ResolveResult
234236
go func() {
235237
defer close(out)
236238
for {
237239
select {
238240
case res, ok := <-resCh:
239241
if !ok {
240-
if best != (onceResult{}) {
241-
ns.cacheSet(cacheKey, best.value, best.ttl)
242+
if best != (ResolveResult{}) {
243+
ns.cacheSet(cacheKey, best.Path, best.TTL)
242244
}
243245
return
244246
}
245-
if res.err == nil {
247+
if res.Err == nil {
246248
best = res
247249
}
248-
p := res.value
249-
err := res.err
250-
ttl := res.ttl
250+
p := res.Path
251+
err := res.Err
252+
ttl := res.TTL
251253

252254
// Attach rest of the path
253255
if len(segments) > 3 {
254256
p, err = path.FromSegments("", strings.TrimRight(p.String(), "/"), segments[3])
255257
}
256258

257-
emitOnceResult(ctx, out, onceResult{value: p, ttl: ttl, err: err})
259+
emitOnceResult(ctx, out, ResolveResult{Path: p, TTL: ttl, Err: err})
258260
case <-ctx.Done():
259261
return
260262
}
@@ -264,7 +266,7 @@ func (ns *nameSys) resolveOnceAsync(ctx context.Context, name string, options Re
264266
return out
265267
}
266268

267-
func emitOnceResult(ctx context.Context, outCh chan<- onceResult, r onceResult) {
269+
func emitOnceResult(ctx context.Context, outCh chan<- ResolveResult, r ResolveResult) {
268270
select {
269271
case outCh <- r:
270272
case <-ctx.Done():

namesys/namesys.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ type NameSystem interface {
6969
// ResolveResult is the return type for [Resolver.ResolveAsync].
7070
type ResolveResult struct {
7171
Path path.Path
72+
TTL time.Duration
7273
Err error
7374
}
7475

@@ -94,7 +95,7 @@ type Resolver interface {
9495
//
9596
// There is a default depth-limit to avoid infinite recursion. Most users will be fine with
9697
// this default limit, but if you need to adjust the limit you can specify it as an option.
97-
Resolve(ctx context.Context, name string, options ...ResolveOption) (value path.Path, err error)
98+
Resolve(ctx context.Context, name string, options ...ResolveOption) (value path.Path, ttl time.Duration, err error)
9899

99100
// ResolveAsync performs recursive name lookup, like Resolve, but it returns entries as
100101
// they are discovered in the DHT. Each returned result is guaranteed to be "better"

0 commit comments

Comments
 (0)