diff --git a/gateway/blocks_backend.go b/gateway/blocks_backend.go index edad729322..010fe44d2f 100644 --- a/gateway/blocks_backend.go +++ b/gateway/blocks_backend.go @@ -596,7 +596,11 @@ func (bb *BlocksBackend) GetIPNSRecord(ctx context.Context, c cid.Cid) ([]byte, func (bb *BlocksBackend) GetDNSLinkRecord(ctx context.Context, hostname string) (path.Path, error) { if bb.namesys != nil { - p, _, err := bb.namesys.Resolve(ctx, "/ipns/"+hostname, namesys.ResolveWithDepth(1)) + p, err := path.NewPath("/ipns/" + hostname) + if err != nil { + return nil, err + } + p, _, err = bb.namesys.Resolve(ctx, p, namesys.ResolveWithDepth(1)) if err == namesys.ErrResolveRecursion { err = nil } diff --git a/gateway/utilities_test.go b/gateway/utilities_test.go index 7bb10b8a6f..3ab850732b 100644 --- a/gateway/utilities_test.go +++ b/gateway/utilities_test.go @@ -62,7 +62,7 @@ func newMockNamesysItem(p path.Path, ttl time.Duration) *mockNamesysItem { type mockNamesys map[string]*mockNamesysItem -func (m mockNamesys) Resolve(ctx context.Context, name string, opts ...namesys.ResolveOption) (value path.Path, ttl time.Duration, err error) { +func (m mockNamesys) Resolve(ctx context.Context, p path.Path, opts ...namesys.ResolveOption) (value path.Path, ttl time.Duration, err error) { cfg := namesys.DefaultResolveOptions() for _, o := range opts { o(&cfg) @@ -72,11 +72,7 @@ func (m mockNamesys) Resolve(ctx context.Context, name string, opts ...namesys.R // max uint depth = ^uint(0) } - p, err := path.NewPath(name) - if err != nil { - return nil, 0, err - } - name = path.SegmentsToString(p.Segments()[:2]...) + name := path.SegmentsToString(p.Segments()[:2]...) for strings.HasPrefix(name, "/ipns/") { if depth == 0 { return value, 0, namesys.ErrResolveRecursion @@ -96,9 +92,9 @@ func (m mockNamesys) Resolve(ctx context.Context, name string, opts ...namesys.R return value, ttl, err } -func (m mockNamesys) ResolveAsync(ctx context.Context, name string, opts ...namesys.ResolveOption) <-chan namesys.ResolveResult { +func (m mockNamesys) ResolveAsync(ctx context.Context, p path.Path, opts ...namesys.ResolveOption) <-chan namesys.ResolveResult { out := make(chan namesys.ResolveResult, 1) - v, ttl, err := m.Resolve(ctx, name, opts...) + v, ttl, err := m.Resolve(ctx, p, opts...) out <- namesys.ResolveResult{Path: v, TTL: ttl, Err: err} close(out) return out @@ -179,7 +175,11 @@ func (mb *mockBackend) GetIPNSRecord(ctx context.Context, c cid.Cid) ([]byte, er func (mb *mockBackend) GetDNSLinkRecord(ctx context.Context, hostname string) (path.Path, error) { if mb.namesys != nil { - p, _, err := mb.namesys.Resolve(ctx, "/ipns/"+hostname, namesys.ResolveWithDepth(1)) + p, err := path.NewPath("/ipns/" + hostname) + if err != nil { + return nil, err + } + p, _, err = mb.namesys.Resolve(ctx, p, namesys.ResolveWithDepth(1)) if err == namesys.ErrResolveRecursion { err = nil } diff --git a/namesys/dns_resolver.go b/namesys/dns_resolver.go index 63289684f8..55c86d8d08 100644 --- a/namesys/dns_resolver.go +++ b/namesys/dns_resolver.go @@ -9,7 +9,6 @@ import ( "strings" "time" - "github.com/ipfs/boxo/ipns" path "github.com/ipfs/boxo/path" "github.com/ipfs/go-cid" dns "github.com/miekg/dns" @@ -32,41 +31,43 @@ func NewDNSResolver(lookup LookupTXTFunc) *DNSResolver { return &DNSResolver{lookupTXT: lookup} } -func (r *DNSResolver) Resolve(ctx context.Context, name string, options ...ResolveOption) (path.Path, time.Duration, error) { - ctx, span := startSpan(ctx, "DNSResolver.Resolve") +func (r *DNSResolver) Resolve(ctx context.Context, p path.Path, options ...ResolveOption) (path.Path, time.Duration, error) { + ctx, span := startSpan(ctx, "DNSResolver.Resolve", trace.WithAttributes(attribute.Stringer("Path", p))) defer span.End() - return resolve(ctx, r, name, ProcessResolveOptions(options)) + return resolve(ctx, r, p, ProcessResolveOptions(options)) } -func (r *DNSResolver) ResolveAsync(ctx context.Context, name string, options ...ResolveOption) <-chan ResolveResult { - ctx, span := startSpan(ctx, "DNSResolver.ResolveAsync") +func (r *DNSResolver) ResolveAsync(ctx context.Context, p path.Path, options ...ResolveOption) <-chan ResolveResult { + ctx, span := startSpan(ctx, "DNSResolver.ResolveAsync", trace.WithAttributes(attribute.Stringer("Path", p))) defer span.End() - return resolveAsync(ctx, r, name, ProcessResolveOptions(options)) + return resolveAsync(ctx, r, p, ProcessResolveOptions(options)) } -func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options ResolveOptions) <-chan ResolveResult { - ctx, span := startSpan(ctx, "DNSResolver.ResolveOnceAsync") +func (r *DNSResolver) resolveOnceAsync(ctx context.Context, p path.Path, options ResolveOptions) <-chan ResolveResult { + ctx, span := startSpan(ctx, "DNSResolver.ResolveOnceAsync", trace.WithAttributes(attribute.Stringer("Path", p))) defer span.End() - var fqdn string out := make(chan ResolveResult, 1) - name = strings.TrimPrefix(name, ipns.NamespacePrefix) - segments := strings.SplitN(name, "/", 2) - domain := segments[0] + if p.Namespace() != path.IPNSNamespace { + out <- ResolveResult{Err: fmt.Errorf("unsupported namespace: %s", p.Namespace().String())} + close(out) + return out + } - if _, ok := dns.IsDomainName(domain); !ok { - out <- ResolveResult{Err: fmt.Errorf("not a valid domain name: %q", domain)} + segments := p.Segments() + fqdn := segments[1] + if _, ok := dns.IsDomainName(fqdn); !ok { + out <- ResolveResult{Err: fmt.Errorf("not a valid domain name: %q", fqdn)} close(out) return out } - log.Debugf("DNSResolver resolving %s", domain) - if strings.HasSuffix(domain, ".") { - fqdn = domain - } else { - fqdn = domain + "." + log.Debugf("DNSResolver resolving %s", fqdn) + + if !strings.HasSuffix(fqdn, ".") { + fqdn += "." } rootChan := make(chan ResolveResult, 1) @@ -75,13 +76,6 @@ func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options subChan := make(chan ResolveResult, 1) go workDomain(ctx, r, "_dnslink."+fqdn, subChan) - appendPath := func(p path.Path) (path.Path, error) { - if len(segments) > 1 { - return path.Join(p, segments[1]) - } - return p, nil - } - go func() { defer close(out) ctx, span := startSpan(ctx, "DNSResolver.ResolveOnceAsync.Worker") @@ -96,7 +90,7 @@ func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options break } if subRes.Err == nil { - p, err := appendPath(subRes.Path) + p, err := path.Join(subRes.Path, segments[2:]...) emitOnceResult(ctx, out, ResolveResult{Path: p, Err: err}) // Return without waiting for rootRes, since this result // (for "_dnslink."+fqdn) takes precedence @@ -109,7 +103,7 @@ func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options break } if rootRes.Err == nil { - p, err := appendPath(rootRes.Path) + p, err := path.Join(rootRes.Path, segments[2:]...) emitOnceResult(ctx, out, ResolveResult{Path: p, Err: err}) // Do not return here. Wait for subRes so that it is // output last if good, thereby giving subRes precedence. @@ -126,7 +120,7 @@ func (r *DNSResolver) resolveOnceAsync(ctx context.Context, name string, options // dnslink, then output a more specific error message if rootResErr == ErrResolveFailed && subResErr == ErrResolveFailed { // Wrap error so that it can be tested if it is a ErrResolveFailed - err := fmt.Errorf("%w: _dnslink subdomain at %q is missing a TXT record (https://docs.ipfs.tech/concepts/dnslink/)", ErrResolveFailed, gopath.Base(name)) + err := fmt.Errorf("%w: _dnslink subdomain at %q is missing a TXT record (https://docs.ipfs.tech/concepts/dnslink/)", ErrResolveFailed, gopath.Base(fqdn)) emitOnceResult(ctx, out, ResolveResult{Err: err}) } return diff --git a/namesys/dns_resolver_test.go b/namesys/dns_resolver_test.go index 5deb9c2e04..f45020a5cc 100644 --- a/namesys/dns_resolver_test.go +++ b/namesys/dns_resolver_test.go @@ -151,46 +151,47 @@ func TestDNSResolution(t *testing.T) { expectedPath string expectedError error }{ - {"multihash.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil}, {"/ipns/multihash.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil}, - {"ipfs.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil}, - {"dipfs.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil}, - {"dns1.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil}, - {"dns1.example.com", 1, "/ipns/ipfs.example.com", ErrResolveRecursion}, - {"dns2.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil}, - {"dns2.example.com", 1, "/ipns/dns1.example.com", ErrResolveRecursion}, - {"dns2.example.com", 2, "/ipns/ipfs.example.com", ErrResolveRecursion}, - {"multi.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil}, - {"multi.example.com", 1, "/ipns/dns1.example.com", ErrResolveRecursion}, - {"multi.example.com", 2, "/ipns/ipfs.example.com", ErrResolveRecursion}, - {"equals.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/=equals", nil}, - {"loop1.example.com", 1, "/ipns/loop2.example.com", ErrResolveRecursion}, - {"loop1.example.com", 2, "/ipns/loop1.example.com", ErrResolveRecursion}, - {"loop1.example.com", 3, "/ipns/loop2.example.com", ErrResolveRecursion}, - {"loop1.example.com", DefaultDepthLimit, "/ipns/loop1.example.com", ErrResolveRecursion}, - {"dloop1.example.com", 1, "/ipns/loop2.example.com", ErrResolveRecursion}, - {"dloop1.example.com", 2, "/ipns/loop1.example.com", ErrResolveRecursion}, - {"dloop1.example.com", 3, "/ipns/loop2.example.com", ErrResolveRecursion}, - {"dloop1.example.com", DefaultDepthLimit, "/ipns/loop1.example.com", ErrResolveRecursion}, - {"bad.example.com", DefaultDepthLimit, "", ErrResolveFailed}, + {"/ipns/ipfs.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil}, + {"/ipns/dipfs.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil}, + {"/ipns/dns1.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil}, + {"/ipns/dns1.example.com", 1, "/ipns/ipfs.example.com", ErrResolveRecursion}, + {"/ipns/dns2.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil}, + {"/ipns/dns2.example.com", 1, "/ipns/dns1.example.com", ErrResolveRecursion}, + {"/ipns/dns2.example.com", 2, "/ipns/ipfs.example.com", ErrResolveRecursion}, + {"/ipns/multi.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil}, + {"/ipns/multi.example.com", 1, "/ipns/dns1.example.com", ErrResolveRecursion}, + {"/ipns/multi.example.com", 2, "/ipns/ipfs.example.com", ErrResolveRecursion}, + {"/ipns/equals.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/=equals", nil}, + {"/ipns/loop1.example.com", 1, "/ipns/loop2.example.com", ErrResolveRecursion}, + {"/ipns/loop1.example.com", 2, "/ipns/loop1.example.com", ErrResolveRecursion}, + {"/ipns/loop1.example.com", 3, "/ipns/loop2.example.com", ErrResolveRecursion}, + {"/ipns/loop1.example.com", DefaultDepthLimit, "/ipns/loop1.example.com", ErrResolveRecursion}, + {"/ipns/dloop1.example.com", 1, "/ipns/loop2.example.com", ErrResolveRecursion}, + {"/ipns/dloop1.example.com", 2, "/ipns/loop1.example.com", ErrResolveRecursion}, + {"/ipns/dloop1.example.com", 3, "/ipns/loop2.example.com", ErrResolveRecursion}, + {"/ipns/dloop1.example.com", DefaultDepthLimit, "/ipns/loop1.example.com", ErrResolveRecursion}, {"/ipns/bad.example.com", DefaultDepthLimit, "", ErrResolveFailed}, - {"withsegment.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment", nil}, - {"withrecsegment.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment/subsub", nil}, - {"withsegment.example.com/test1", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment/test1", nil}, - {"withrecsegment.example.com/test2", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment/subsub/test2", nil}, - {"withrecsegment.example.com/test3/", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment/subsub/test3/", nil}, - {"withtrailingrec.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment/", nil}, - {"double.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil}, - {"conflict.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjE", nil}, - {"fqdn.example.com.", DefaultDepthLimit, "/ipfs/QmYvMB9yrsSf7RKBghkfwmHJkzJhW2ZgVwq3LxBXXPasFr", nil}, - {"en.wikipedia-on-ipfs.org", 2, "/ipfs/bafybeiaysi4s6lnjev27ln5icwm6tueaw2vdykrtjkwiphwekaywqhcjze", nil}, - {"custom.non-icann.tldextravaganza.", 2, "/ipfs/bafybeieto6mcuvqlechv4iadoqvnffondeiwxc2bcfcewhvpsd2odvbmvm", nil}, - {"singlednslabelshouldbeok", 2, "/ipfs/bafybeih4a6ylafdki6ailjrdvmr7o4fbbeceeeuty4v3qyyouiz5koqlpi", nil}, - {"www.wealdtech.eth", 1, "/ipns/ipfs.example.com", ErrResolveRecursion}, - {"www.wealdtech.eth", 2, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil}, + {"/ipns/bad.example.com", DefaultDepthLimit, "", ErrResolveFailed}, + {"/ipns/withsegment.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment", nil}, + {"/ipns/withrecsegment.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment/subsub", nil}, + {"/ipns/withsegment.example.com/test1", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment/test1", nil}, + {"/ipns/withrecsegment.example.com/test2", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment/subsub/test2", nil}, + {"/ipns/withrecsegment.example.com/test3/", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment/subsub/test3/", nil}, + {"/ipns/withtrailingrec.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD/sub/segment/", nil}, + {"/ipns/double.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil}, + {"/ipns/conflict.example.com", DefaultDepthLimit, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjE", nil}, + {"/ipns/fqdn.example.com.", DefaultDepthLimit, "/ipfs/QmYvMB9yrsSf7RKBghkfwmHJkzJhW2ZgVwq3LxBXXPasFr", nil}, + {"/ipns/en.wikipedia-on-ipfs.org", 2, "/ipfs/bafybeiaysi4s6lnjev27ln5icwm6tueaw2vdykrtjkwiphwekaywqhcjze", nil}, + {"/ipns/custom.non-icann.tldextravaganza.", 2, "/ipfs/bafybeieto6mcuvqlechv4iadoqvnffondeiwxc2bcfcewhvpsd2odvbmvm", nil}, + {"/ipns/singlednslabelshouldbeok", 2, "/ipfs/bafybeih4a6ylafdki6ailjrdvmr7o4fbbeceeeuty4v3qyyouiz5koqlpi", nil}, + {"/ipns/www.wealdtech.eth", 1, "/ipns/ipfs.example.com", ErrResolveRecursion}, + {"/ipns/www.wealdtech.eth", 2, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil}, + {"/ipns/www.wealdtech.eth", 2, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil}, {"/ipns/www.wealdtech.eth", 2, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil}, - {"www.wealdtech.eth", 2, "/ipfs/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", nil}, } { - testResolution(t, r, testCase.name, (testCase.depth), testCase.expectedPath, 0, testCase.expectedError) + t.Run(testCase.name, func(t *testing.T) { + testResolution(t, r, testCase.name, (testCase.depth), testCase.expectedPath, 0, testCase.expectedError) + }) } } diff --git a/namesys/interface.go b/namesys/interface.go index c5d7da1ad9..2615b6ab5e 100644 --- a/namesys/interface.go +++ b/namesys/interface.go @@ -88,12 +88,12 @@ type Resolver interface { // // There is a default depth-limit to avoid infinite recursion. Most users will be fine with // this default limit, but if you need to adjust the limit you can specify it as an option. - Resolve(ctx context.Context, name string, options ...ResolveOption) (value path.Path, ttl time.Duration, err error) + Resolve(context.Context, path.Path, ...ResolveOption) (path.Path, time.Duration, error) // ResolveAsync performs recursive name lookup, like Resolve, but it returns entries as // they are discovered in the DHT. Each returned result is guaranteed to be "better" // (which usually means newer) than the previous one. - ResolveAsync(ctx context.Context, name string, options ...ResolveOption) <-chan ResolveResult + ResolveAsync(context.Context, path.Path, ...ResolveOption) <-chan ResolveResult } // ResolveOptions specifies options for resolving an IPNS Path. diff --git a/namesys/ipns_resolver.go b/namesys/ipns_resolver.go index 9cd85fc135..83a32afb8d 100644 --- a/namesys/ipns_resolver.go +++ b/namesys/ipns_resolver.go @@ -2,6 +2,7 @@ package namesys import ( "context" + "fmt" "time" "github.com/ipfs/boxo/ipns" @@ -36,36 +37,40 @@ func NewIPNSResolver(route routing.ValueStore) *IPNSResolver { } } -func (r *IPNSResolver) Resolve(ctx context.Context, name string, options ...ResolveOption) (path.Path, time.Duration, error) { - ctx, span := startSpan(ctx, "IPNSResolver.Resolve", trace.WithAttributes(attribute.String("Name", name))) +func (r *IPNSResolver) Resolve(ctx context.Context, p path.Path, options ...ResolveOption) (path.Path, time.Duration, error) { + ctx, span := startSpan(ctx, "IPNSResolver.Resolve", trace.WithAttributes(attribute.Stringer("Path", p))) defer span.End() - return resolve(ctx, r, name, ProcessResolveOptions(options)) + return resolve(ctx, r, p, ProcessResolveOptions(options)) } -func (r *IPNSResolver) ResolveAsync(ctx context.Context, name string, options ...ResolveOption) <-chan ResolveResult { - ctx, span := startSpan(ctx, "IPNSResolver.ResolveAsync", trace.WithAttributes(attribute.String("Name", name))) +func (r *IPNSResolver) ResolveAsync(ctx context.Context, p path.Path, options ...ResolveOption) <-chan ResolveResult { + ctx, span := startSpan(ctx, "IPNSResolver.ResolveAsync", trace.WithAttributes(attribute.Stringer("Path", p))) defer span.End() - return resolveAsync(ctx, r, name, ProcessResolveOptions(options)) + return resolveAsync(ctx, r, p, ProcessResolveOptions(options)) } -func (r *IPNSResolver) resolveOnceAsync(ctx context.Context, nameStr string, options ResolveOptions) <-chan ResolveResult { - ctx, span := startSpan(ctx, "IPNSResolver.ResolveOnceAsync", trace.WithAttributes(attribute.String("Name", nameStr))) +func (r *IPNSResolver) resolveOnceAsync(ctx context.Context, p path.Path, options ResolveOptions) <-chan ResolveResult { + ctx, span := startSpan(ctx, "IPNSResolver.ResolveOnceAsync", trace.WithAttributes(attribute.Stringer("Path", p))) defer span.End() out := make(chan ResolveResult, 1) - log.Debugf("RoutingResolver resolving %s", nameStr) - cancel := func() {} + if p.Namespace() != path.IPNSNamespace { + out <- ResolveResult{Err: fmt.Errorf("unsupported namespace: %s", p.Namespace().String())} + close(out) + return out + } + cancel := func() {} if options.DhtTimeout != 0 { // Resolution must complete within the timeout ctx, cancel = context.WithTimeout(ctx, options.DhtTimeout) } - name, err := ipns.NameFromString(nameStr) + segments := p.Segments() + name, err := ipns.NameFromString(segments[1]) if err != nil { - log.Debugf("RoutingResolver: could not convert key %q to IPNS name: %s\n", nameStr, err) out <- ResolveResult{Err: err} close(out) cancel() @@ -74,7 +79,6 @@ func (r *IPNSResolver) resolveOnceAsync(ctx context.Context, nameStr string, opt vals, err := r.routing.SearchValue(ctx, string(name.RoutingKey()), dht.Quorum(int(options.DhtRecordCount))) if err != nil { - log.Debugf("RoutingResolver: dht get for name %s failed: %s", nameStr, err) out <- ResolveResult{Err: err} close(out) cancel() @@ -96,7 +100,6 @@ func (r *IPNSResolver) resolveOnceAsync(ctx context.Context, nameStr string, opt rec, err := ipns.UnmarshalRecord(val) if err != nil { - log.Debugf("RoutingResolver: could not unmarshal value for name %s: %s", nameStr, err) emitOnceResult(ctx, out, ResolveResult{Err: err}) return } @@ -107,6 +110,12 @@ func (r *IPNSResolver) resolveOnceAsync(ctx context.Context, nameStr string, opt return } + p, err = path.Join(p, segments[2:]...) + if err != nil { + emitOnceResult(ctx, out, ResolveResult{Err: err}) + return + } + ttl, err := calculateBestTTL(rec) if err != nil { emitOnceResult(ctx, out, ResolveResult{Err: err}) diff --git a/namesys/ipns_resolver_test.go b/namesys/ipns_resolver_test.go index 19ff3b2f7e..7b70c35ce3 100644 --- a/namesys/ipns_resolver_test.go +++ b/namesys/ipns_resolver_test.go @@ -54,7 +54,7 @@ func TestResolver(t *testing.T) { err := publisher.Publish(context.Background(), id.PrivateKey(), pathCat) require.NoError(t, err) - res, _, err := resolver.Resolve(context.Background(), name.String()) + res, _, err := resolver.Resolve(context.Background(), name.AsPath()) require.NoError(t, err) require.Equal(t, pathCat, res) }) @@ -77,7 +77,7 @@ func TestResolver(t *testing.T) { require.NoError(t, err) // Expect to not be able to resolve. - _, _, err = resolver.Resolve(context.Background(), name.String()) + _, _, err = resolver.Resolve(context.Background(), name.AsPath()) require.ErrorIs(t, err, ErrResolveFailed) }) @@ -103,7 +103,7 @@ func TestResolver(t *testing.T) { require.NoError(t, err) // Expect new. - res, _, err := resolver.Resolve(context.Background(), name.String()) + res, _, err := resolver.Resolve(context.Background(), name.AsPath()) require.NoError(t, err) require.Equal(t, pathDog, res) }) @@ -124,7 +124,7 @@ func TestResolver(t *testing.T) { require.NoError(t, err) // Should receive newer! - res, _, err := resolver.Resolve(context.Background(), name.String()) + res, _, err := resolver.Resolve(context.Background(), name.AsPath()) require.NoError(t, err) require.Equal(t, pathDog, res) }) diff --git a/namesys/namesys.go b/namesys/namesys.go index afac2bdc4c..00282ac6be 100644 --- a/namesys/namesys.go +++ b/namesys/namesys.go @@ -136,61 +136,48 @@ func NewNameSystem(r routing.ValueStore, opts ...Option) (NameSystem, error) { } // Resolve implements Resolver. -func (ns *namesys) Resolve(ctx context.Context, name string, options ...ResolveOption) (path.Path, time.Duration, error) { - ctx, span := startSpan(ctx, "MPNS.Resolve", trace.WithAttributes(attribute.String("Name", name))) +func (ns *namesys) Resolve(ctx context.Context, p path.Path, options ...ResolveOption) (path.Path, time.Duration, error) { + ctx, span := startSpan(ctx, "namesys.Resolve", trace.WithAttributes(attribute.Stringer("Path", p))) defer span.End() - return resolve(ctx, ns, name, ProcessResolveOptions(options)) + return resolve(ctx, ns, p, ProcessResolveOptions(options)) } -func (ns *namesys) ResolveAsync(ctx context.Context, name string, options ...ResolveOption) <-chan ResolveResult { - ctx, span := startSpan(ctx, "MPNS.ResolveAsync", trace.WithAttributes(attribute.String("Name", name))) +func (ns *namesys) ResolveAsync(ctx context.Context, p path.Path, options ...ResolveOption) <-chan ResolveResult { + ctx, span := startSpan(ctx, "namesys.ResolveAsync", trace.WithAttributes(attribute.Stringer("Path", p))) defer span.End() - return resolveAsync(ctx, ns, name, ProcessResolveOptions(options)) + return resolveAsync(ctx, ns, p, ProcessResolveOptions(options)) } // resolveOnce implements resolver. -func (ns *namesys) resolveOnceAsync(ctx context.Context, name string, options ResolveOptions) <-chan ResolveResult { - ctx, span := startSpan(ctx, "MPNS.ResolveOnceAsync") +func (ns *namesys) resolveOnceAsync(ctx context.Context, p path.Path, options ResolveOptions) <-chan ResolveResult { + ctx, span := startSpan(ctx, "namesys.ResolveOnceAsync", trace.WithAttributes(attribute.Stringer("Path", p))) defer span.End() out := make(chan ResolveResult, 1) - - p, err := path.NewPath(name) - if err != nil { - if p, err = path.NewPath("/ipfs/" + name); err == nil { - out <- ResolveResult{Path: p} - } else { - log.Debugf("invalid name syntax for %q", name) - out <- ResolveResult{Err: ErrResolveFailed} - } - - close(out) - return out - } else if !p.Namespace().Mutable() { - out <- ResolveResult{Path: p, Err: err} + if !p.Namespace().Mutable() { + out <- ResolveResult{Path: p} close(out) return out } - segments := p.Segments() - key := segments[1] - ipnsName, err := ipns.NameFromString(key) + // TODO - cacheKey := key - if err == nil { - cacheKey = ipnsName.String() + segments := p.Segments() + resolvablePath, err := path.NewPathFromSegments(segments[0], segments[1]) + if err != nil { + out <- ResolveResult{Err: err} + close(out) + return out } - if p, ttl, ok := ns.cacheGet(cacheKey); ok { - var err error + if p, ttl, ok := ns.cacheGet(resolvablePath.String()); ok { if len(segments) > 2 { p, err = path.Join(p, segments[2:]...) } span.SetAttributes(attribute.Bool("CacheHit", true)) span.RecordError(err) - out <- ResolveResult{Path: p, TTL: ttl, Err: err} close(out) return out @@ -203,17 +190,17 @@ func (ns *namesys) resolveOnceAsync(ctx context.Context, name string, options Re // 2. if it is a domain name, resolve through DNSLink. var res resolver - if err == nil { + if _, err := ipns.NameFromString(segments[1]); err == nil { res = ns.ipnsResolver - } else if _, ok := dns.IsDomainName(key); ok { + } else if _, ok := dns.IsDomainName(segments[1]); ok { res = ns.dnsResolver } else { - out <- ResolveResult{Err: fmt.Errorf("invalid IPNS root: %q", key)} + out <- ResolveResult{Err: fmt.Errorf("cannot resolve: %q", resolvablePath.String())} close(out) return out } - resCh := res.resolveOnceAsync(ctx, key, options) + resCh := res.resolveOnceAsync(ctx, resolvablePath, options) var best ResolveResult go func() { defer close(out) @@ -222,7 +209,7 @@ func (ns *namesys) resolveOnceAsync(ctx context.Context, name string, options Re case res, ok := <-resCh: if !ok { if best != (ResolveResult{}) { - ns.cacheSet(cacheKey, best.Path, best.TTL) + ns.cacheSet(resolvablePath.String(), best.Path, best.TTL) } return } @@ -299,12 +286,12 @@ func (ns *namesys) Publish(ctx context.Context, name ci.PrivKey, value path.Path // returns the result of [NameSystem.Resolve] for the given path. If the given namesys // is nil, [ErrNoNamesys] is returned. func Resolve(ctx context.Context, ns NameSystem, p path.Path) (path.Path, time.Duration, error) { - ctx, span := startSpan(ctx, "Resolve", trace.WithAttributes(attribute.String("Path", p.String()))) + ctx, span := startSpan(ctx, "Resolve", trace.WithAttributes(attribute.Stringer("Path", p))) defer span.End() if ns == nil { return nil, 0, ErrNoNamesys } - return ns.Resolve(ctx, p.String()) + return ns.Resolve(ctx, p) } diff --git a/namesys/namesys_test.go b/namesys/namesys_test.go index 2a01681bb6..ffcf01dc72 100644 --- a/namesys/namesys_test.go +++ b/namesys/namesys_test.go @@ -23,7 +23,10 @@ type mockResolver struct { func testResolution(t *testing.T, resolver Resolver, name string, depth uint, expected string, expectedTTL time.Duration, expectedError error) { t.Helper() - p, ttl, err := resolver.Resolve(context.Background(), name, ResolveWithDepth(depth)) + ptr, err := path.NewPath(name) + require.NoError(t, err) + + p, ttl, err := resolver.Resolve(context.Background(), ptr, ResolveWithDepth(depth)) require.ErrorIs(t, err, expectedError) require.Equal(t, expectedTTL, ttl) if expected == "" { @@ -33,8 +36,8 @@ func testResolution(t *testing.T, resolver Resolver, name string, depth uint, ex } } -func (r *mockResolver) resolveOnceAsync(ctx context.Context, name string, options ResolveOptions) <-chan ResolveResult { - p, err := path.NewPath(r.entries[name]) +func (r *mockResolver) resolveOnceAsync(ctx context.Context, p path.Path, options ResolveOptions) <-chan ResolveResult { + p, err := path.NewPath(r.entries[p.String()]) out := make(chan ResolveResult, 1) out <- ResolveResult{Path: p, Err: err} close(out) @@ -44,12 +47,12 @@ func (r *mockResolver) resolveOnceAsync(ctx context.Context, name string, option func mockResolverOne() *mockResolver { return &mockResolver{ entries: map[string]string{ - "QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy": "/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", - "QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n": "/ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy", - "QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD": "/ipns/ipfs.io", - "QmQ4QZh8nrsczdUEwTyfBope4THUhqxqc1fx6qYhhzZQei": "/ipfs/QmP3ouCnU8NNLsW6261pAx2pNLV2E4dQoisB1sgda12Act", - "12D3KooWFB51PRY9BxcXSH6khFXw1BZeszeLDy7C8GciskqCTZn5": "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", // ed25519+identity multihash - "bafzbeickencdqw37dpz3ha36ewrh4undfjt2do52chtcky4rxkj447qhdm": "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", // cidv1 in base32 with libp2p-key multicodec + "/ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy": "/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", + "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n": "/ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy", + "/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD": "/ipns/ipfs.io", + "/ipns/QmQ4QZh8nrsczdUEwTyfBope4THUhqxqc1fx6qYhhzZQei": "/ipfs/QmP3ouCnU8NNLsW6261pAx2pNLV2E4dQoisB1sgda12Act", + "/ipns/12D3KooWFB51PRY9BxcXSH6khFXw1BZeszeLDy7C8GciskqCTZn5": "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", // ed25519+identity multihash + "/ipns/bafzbeickencdqw37dpz3ha36ewrh4undfjt2do52chtcky4rxkj447qhdm": "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", // cidv1 in base32 with libp2p-key multicodec }, } } @@ -57,7 +60,7 @@ func mockResolverOne() *mockResolver { func mockResolverTwo() *mockResolver { return &mockResolver{ entries: map[string]string{ - "ipfs.io": "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", + "/ipns/ipfs.io": "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", }, } } @@ -68,19 +71,31 @@ func TestNamesysResolution(t *testing.T) { dnsResolver: mockResolverTwo(), } - testResolution(t, r, "Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", DefaultDepthLimit, "/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", 0, nil) - testResolution(t, r, "/ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy", DefaultDepthLimit, "/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", 0, nil) - testResolution(t, r, "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", DefaultDepthLimit, "/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", 0, nil) - testResolution(t, r, "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", 1, "/ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy", 0, ErrResolveRecursion) - testResolution(t, r, "/ipns/ipfs.io", DefaultDepthLimit, "/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", 0, nil) - testResolution(t, r, "/ipns/ipfs.io", 1, "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", 0, ErrResolveRecursion) - testResolution(t, r, "/ipns/ipfs.io", 2, "/ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy", 0, ErrResolveRecursion) - testResolution(t, r, "/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", DefaultDepthLimit, "/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", 0, nil) - testResolution(t, r, "/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", 1, "/ipns/ipfs.io", 0, ErrResolveRecursion) - testResolution(t, r, "/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", 2, "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", 0, ErrResolveRecursion) - testResolution(t, r, "/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", 3, "/ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy", 0, ErrResolveRecursion) - testResolution(t, r, "/ipns/12D3KooWFB51PRY9BxcXSH6khFXw1BZeszeLDy7C8GciskqCTZn5", 1, "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", 0, ErrResolveRecursion) - testResolution(t, r, "/ipns/bafzbeickencdqw37dpz3ha36ewrh4undfjt2do52chtcky4rxkj447qhdm", 1, "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", 0, ErrResolveRecursion) + for _, testCase := range []struct { + name string + depth uint + expectedPath string + expectedTTL time.Duration + expectedError error + }{ + {"/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", DefaultDepthLimit, "/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", 0, nil}, + {"/ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy", DefaultDepthLimit, "/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", 0, nil}, + {"/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", DefaultDepthLimit, "/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", 0, nil}, + {"/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", 1, "/ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy", 0, ErrResolveRecursion}, + {"/ipns/ipfs.io", DefaultDepthLimit, "/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", 0, nil}, + {"/ipns/ipfs.io", 1, "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", 0, ErrResolveRecursion}, + {"/ipns/ipfs.io", 2, "/ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy", 0, ErrResolveRecursion}, + {"/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", DefaultDepthLimit, "/ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj", 0, nil}, + {"/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", 1, "/ipns/ipfs.io", 0, ErrResolveRecursion}, + {"/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", 2, "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", 0, ErrResolveRecursion}, + {"/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", 3, "/ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy", 0, ErrResolveRecursion}, + {"/ipns/12D3KooWFB51PRY9BxcXSH6khFXw1BZeszeLDy7C8GciskqCTZn5", 1, "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", 0, ErrResolveRecursion}, + {"/ipns/bafzbeickencdqw37dpz3ha36ewrh4undfjt2do52chtcky4rxkj447qhdm", 1, "/ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", 0, ErrResolveRecursion}, + } { + t.Run(testCase.name, func(t *testing.T) { + testResolution(t, r, testCase.name, (testCase.depth), testCase.expectedPath, 0, testCase.expectedError) + }) + } } func TestResolveIPNS(t *testing.T) { diff --git a/namesys/republisher/repub_test.go b/namesys/republisher/repub_test.go index ff5e506346..2462ddfd8b 100644 --- a/namesys/republisher/repub_test.go +++ b/namesys/republisher/repub_test.go @@ -90,7 +90,7 @@ func TestRepublish(t *testing.T) { require.NoError(t, err) rp := namesys.NewIPNSPublisher(publisher.dht, publisher.store) - name := ipns.NameFromPeer(publisher.id).AsPath().String() + name := ipns.NameFromPeer(publisher.id).AsPath() // Retry in case the record expires before we can fetch it. This can // happen when running the test on a slow machine. @@ -166,7 +166,7 @@ func TestLongEOLRepublish(t *testing.T) { require.NoError(t, err) rp := namesys.NewIPNSPublisher(publisher.dht, publisher.store) - name := ipns.NameFromPeer(publisher.id).AsPath().String() + name := ipns.NameFromPeer(publisher.id).AsPath() expiration := time.Now().Add(time.Hour) err = rp.Publish(ctx, publisher.privKey, p, namesys.PublishWithEOL(expiration)) @@ -209,7 +209,7 @@ func getLastIPNSRecord(ctx context.Context, dstore ds.Datastore, id peer.ID) (*i return ipns.UnmarshalRecord(val) } -func verifyResolution(nsystems []namesys.NameSystem, key string, exp path.Path) error { +func verifyResolution(nsystems []namesys.NameSystem, key path.Path, exp path.Path) error { ctx, cancel := context.WithCancel(context.Background()) defer cancel() for _, n := range nsystems { @@ -225,7 +225,7 @@ func verifyResolution(nsystems []namesys.NameSystem, key string, exp path.Path) return nil } -func verifyResolutionFails(nsystems []namesys.NameSystem, key string) error { +func verifyResolutionFails(nsystems []namesys.NameSystem, key path.Path) error { ctx, cancel := context.WithCancel(context.Background()) defer cancel() for _, n := range nsystems { diff --git a/namesys/utilities.go b/namesys/utilities.go index a2dc8860d6..1893029634 100644 --- a/namesys/utilities.go +++ b/namesys/utilities.go @@ -11,32 +11,32 @@ import ( ) type resolver interface { - resolveOnceAsync(ctx context.Context, name string, options ResolveOptions) <-chan ResolveResult + resolveOnceAsync(context.Context, path.Path, ResolveOptions) <-chan ResolveResult } // resolve is a helper for implementing Resolver.ResolveN using resolveOnce. -func resolve(ctx context.Context, r resolver, name string, options ResolveOptions) (p path.Path, ttl time.Duration, err error) { +func resolve(ctx context.Context, r resolver, p path.Path, options ResolveOptions) (value path.Path, ttl time.Duration, err error) { ctx, cancel := context.WithCancel(ctx) defer cancel() err = ErrResolveFailed - resCh := resolveAsync(ctx, r, name, options) + resCh := resolveAsync(ctx, r, p, options) for res := range resCh { - p, ttl, err = res.Path, res.TTL, res.Err + value, ttl, err = res.Path, res.TTL, res.Err if err != nil { break } } - return p, ttl, err + return value, ttl, err } -func resolveAsync(ctx context.Context, r resolver, name string, options ResolveOptions) <-chan ResolveResult { +func resolveAsync(ctx context.Context, r resolver, p path.Path, options ResolveOptions) <-chan ResolveResult { ctx, span := startSpan(ctx, "ResolveAsync") defer span.End() - resCh := r.resolveOnceAsync(ctx, name, options) + resCh := r.resolveOnceAsync(ctx, p, options) depth := options.Depth outCh := make(chan ResolveResult, 1) @@ -66,7 +66,7 @@ func resolveAsync(ctx context.Context, r resolver, name string, options ResolveO return } - log.Debugf("resolved %s to %s", name, res.Path.String()) + log.Debugf("resolved %s to %s", p.String(), res.Path.String()) if !res.Path.Namespace().Mutable() { emitResult(ctx, outCh, res) @@ -93,7 +93,7 @@ func resolveAsync(ctx context.Context, r resolver, name string, options ResolveO subCtx, cancelSub = context.WithCancel(ctx) _ = cancelSub - subCh = resolveAsync(subCtx, r, res.Path.String(), subOpts) + subCh = resolveAsync(subCtx, r, res.Path, subOpts) case res, ok := <-subCh: if !ok { subCh = nil