mirror of
https://github.com/ipfs/kubo.git
synced 2026-03-01 06:17:56 +08:00
namesys: Add recursive resolution
This allows direct access to the earlier protocol-specific Resolve implementations. The guts of each protocol-specific resolver are in the internal resolveOnce method, and we've added a new: ResolveN(ctx, name, depth) method to the public interface. There's also: Resolve(ctx, name) which wraps ResolveN using DefaultDepthLimit. The extra API endpoint is intended to reduce the likelyhood of clients accidentally calling the more dangerous ResolveN with a nonsensically high or infinite depth. On IRC on 2015-05-17, Juan said: 15:34 <jbenet> If 90% of uses is the reduced API with no chance to screw it up, that's a huge win. 15:34 <wking> Why would those 90% not just set depth=0 or depth=1, depending on which they need? 15:34 <jbenet> Because people will start writing `r.Resolve(ctx, name, d)` where d is a variable. 15:35 <wking> And then accidentally set that variable to some huge number? 15:35 <jbenet> Grom experience, i've seen this happen _dozens_ of times. people screw trivial things up. 15:35 <wking> Why won't those same people be using ResolveN? 15:36 <jbenet> Because almost every example they see will tell them to use Resolve(), and they will mostly stay away from ResolveN. The per-prodocol versions also resolve recursively within their protocol. For example: DNSResolver.Resolve(ctx, "ipfs.io", 0) will recursively resolve DNS links until the referenced value is no longer a DNS link. I also renamed the multi-protocol ipfs NameSystem (defined in namesys/namesys.go) to 'mpns' (for Multi-Protocol Name System), because I wasn't clear on whether IPNS applied to the whole system or just to to the DHT-based system. The new name is unambiguously multi-protocol, which is good. It would be nice to have a distinct name for the DHT-based link system. Now that resolver output is always prefixed with a namespace and unprefixed mpns resolver input is interpreted as /ipfs/, core/corehttp/ipns_hostname.go can dispense with it's old manual /ipfs/ injection. Now that the Resolver interface handles recursion, we don't need the resolveRecurse helper in core/pathresolver.go. The pathresolver cleanup also called for an adjustment to FromSegments to more easily get slash-prefixed paths. Now that recursive resolution with the namesys/namesys.go composite resolver always gets you to an /ipfs/... path, there's no need for the /ipns/ special case in fuse/ipns/ipns_unix.go. Now that DNS links can be things other than /ipfs/ or DHT-link references (e.g. they could be /ipns/<domain-name> references) I've also loosened the ParsePath logic to only attempt multihash validation on IPFS paths. It checks to ensure that other paths have a known-protocol prefix, but otherwise leaves them alone. I also changed some key-stringification from .Pretty() to .String() following the potential deprecation mentioned in util/key.go.
This commit is contained in:
parent
04a969835e
commit
3ead2443e5
@ -75,7 +75,7 @@ Resolve te value of another name:
|
||||
name = req.Arguments()[0]
|
||||
}
|
||||
|
||||
output, err := n.Namesys.Resolve(n.Context(), name)
|
||||
output, err := n.Namesys.Resolve(n.Context(), "/ipns/"+name)
|
||||
if err != nil {
|
||||
res.SetError(err, cmds.ErrNormal)
|
||||
return
|
||||
|
||||
@ -22,6 +22,10 @@ import (
|
||||
type mockNamesys map[string]path.Path
|
||||
|
||||
func (m mockNamesys) Resolve(ctx context.Context, name string) (value path.Path, err error) {
|
||||
return m.ResolveN(ctx, name, namesys.DefaultDepthLimit)
|
||||
}
|
||||
|
||||
func (m mockNamesys) ResolveN(ctx context.Context, name string, depth int) (value path.Path, err error) {
|
||||
p, ok := m[name]
|
||||
if !ok {
|
||||
return "", namesys.ErrResolveFailed
|
||||
@ -29,11 +33,6 @@ func (m mockNamesys) Resolve(ctx context.Context, name string) (value path.Path,
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (m mockNamesys) CanResolve(name string) bool {
|
||||
_, ok := m[name]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (m mockNamesys) Publish(ctx context.Context, name ci.PrivKey, value path.Path) error {
|
||||
return errors.New("not implemented for mockNamesys")
|
||||
}
|
||||
|
||||
@ -20,7 +20,7 @@ func IPNSHostnameOption() ServeOption {
|
||||
|
||||
host := strings.SplitN(r.Host, ":", 2)[0]
|
||||
if p, err := n.Namesys.Resolve(ctx, host); err == nil {
|
||||
r.URL.Path = "/ipfs/" + p.String() + r.URL.Path
|
||||
r.URL.Path = p.String() + r.URL.Path
|
||||
}
|
||||
childMux.ServeHTTP(w, r)
|
||||
})
|
||||
|
||||
@ -2,7 +2,6 @@ package core
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
|
||||
@ -11,64 +10,42 @@ import (
|
||||
path "github.com/ipfs/go-ipfs/path"
|
||||
)
|
||||
|
||||
const maxLinks = 32
|
||||
// ErrNoNamesys is an explicit error for when an IPFS node doesn't
|
||||
// (yet) have a name system
|
||||
var ErrNoNamesys = errors.New(
|
||||
"core/resolve: no Namesys on IpfsNode - can't resolve ipns entry")
|
||||
|
||||
// errors returned by Resolve function
|
||||
var (
|
||||
ErrTooManyLinks = errors.New("core/resolve: exceeded maximum number of links in ipns entry")
|
||||
ErrNoNamesys = errors.New("core/resolve: no Namesys on IpfsNode - can't resolve ipns entry")
|
||||
)
|
||||
|
||||
// Resolve resolves the given path by parsing out /ipns/ entries and then going
|
||||
// through the /ipfs/ entries and returning the final merkledage node.
|
||||
// Effectively enables /ipns/ in CLI commands.
|
||||
// Resolve resolves the given path by parsing out protocol-specific
|
||||
// entries (e.g. /ipns/<node-key>) and then going through the /ipfs/
|
||||
// entries and returning the final merkledage node. Effectively
|
||||
// enables /ipns/, /dns/, etc. in commands.
|
||||
func Resolve(ctx context.Context, n *IpfsNode, p path.Path) (*merkledag.Node, error) {
|
||||
r := resolver{ctx, n, p}
|
||||
return r.resolveRecurse(0)
|
||||
}
|
||||
|
||||
type resolver struct {
|
||||
ctx context.Context
|
||||
n *IpfsNode
|
||||
p path.Path
|
||||
}
|
||||
|
||||
func (r *resolver) resolveRecurse(depth int) (*merkledag.Node, error) {
|
||||
if depth >= maxLinks {
|
||||
return nil, ErrTooManyLinks
|
||||
}
|
||||
// for now, we only try to resolve ipns paths if
|
||||
// they begin with "/ipns/". Otherwise, ambiguity
|
||||
// emerges when resolving just a <hash>. Is it meant
|
||||
// to be an ipfs or an ipns resolution?
|
||||
|
||||
if strings.HasPrefix(r.p.String(), "/ipns/") {
|
||||
if strings.HasPrefix(p.String(), "/") {
|
||||
// namespaced path (/ipfs/..., /ipns/..., etc.)
|
||||
// TODO(cryptix): we sould be able to query the local cache for the path
|
||||
if r.n.Namesys == nil {
|
||||
if n.Namesys == nil {
|
||||
return nil, ErrNoNamesys
|
||||
}
|
||||
// if it's an ipns path, try to resolve it.
|
||||
// if we can't, we can give that error back to the user.
|
||||
seg := r.p.Segments()
|
||||
if len(seg) < 2 || seg[1] == "" { // just "/ipns/"
|
||||
return nil, fmt.Errorf("invalid path: %s", string(r.p))
|
||||
|
||||
seg := p.Segments()
|
||||
extensions := seg[2:]
|
||||
resolvable, err := path.FromSegments("/", seg[0], seg[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ipnsPath := seg[1]
|
||||
extensions := seg[2:]
|
||||
respath, err := r.n.Namesys.Resolve(r.ctx, ipnsPath)
|
||||
respath, err := n.Namesys.Resolve(ctx, resolvable.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
segments := append(respath.Segments(), extensions...)
|
||||
r.p, err = path.FromSegments(segments...)
|
||||
p, err = path.FromSegments("/", segments...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r.resolveRecurse(depth + 1)
|
||||
}
|
||||
|
||||
// ok, we have an ipfs path now (or what we'll treat as one)
|
||||
return r.n.Resolver.ResolvePath(r.ctx, r.p)
|
||||
return n.Resolver.ResolvePath(ctx, p)
|
||||
}
|
||||
|
||||
@ -462,7 +462,7 @@ func TestFastRepublish(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pubkeyHash := u.Key(h).Pretty()
|
||||
pubkeyPath := "/ipns/" + u.Key(h).String()
|
||||
|
||||
// set them back
|
||||
defer func() {
|
||||
@ -482,9 +482,9 @@ func TestFastRepublish(t *testing.T) {
|
||||
writeFileData(t, dataA, fname) // random
|
||||
<-time.After(shortRepublishTimeout * 2)
|
||||
log.Debug("resolving first hash")
|
||||
resolvedHash, err := node.Namesys.Resolve(context.Background(), pubkeyHash)
|
||||
resolvedHash, err := node.Namesys.Resolve(context.Background(), pubkeyPath)
|
||||
if err != nil {
|
||||
t.Fatal("resolve err:", pubkeyHash, err)
|
||||
t.Fatal("resolve err:", pubkeyPath, err)
|
||||
}
|
||||
|
||||
// constantly keep writing to the file
|
||||
@ -501,7 +501,7 @@ func TestFastRepublish(t *testing.T) {
|
||||
}(shortRepublishTimeout)
|
||||
|
||||
hasPublished := func() bool {
|
||||
res, err := node.Namesys.Resolve(context.Background(), pubkeyHash)
|
||||
res, err := node.Namesys.Resolve(context.Background(), pubkeyPath)
|
||||
if err != nil {
|
||||
t.Fatalf("resolve err: %v", err)
|
||||
}
|
||||
|
||||
@ -150,9 +150,6 @@ func (s *Root) Lookup(ctx context.Context, name string) (fs.Node, error) {
|
||||
if segments[0] == "ipfs" {
|
||||
p := strings.Join(resolved.Segments()[1:], "/")
|
||||
return &Link{s.IpfsRoot + "/" + p}, nil
|
||||
} else if segments[0] == "ipns" {
|
||||
p := strings.Join(resolved.Segments()[1:], "/")
|
||||
return &Link{s.IpnsRoot + "/" + p}, nil
|
||||
} else {
|
||||
log.Error("Invalid path.Path: ", resolved)
|
||||
return nil, errors.New("invalid path from ipns record")
|
||||
|
||||
@ -141,7 +141,7 @@ func (fs *Filesystem) newKeyRoot(parent context.Context, k ci.PrivKey) (*KeyRoot
|
||||
return nil, err
|
||||
}
|
||||
|
||||
name := u.Key(hash).Pretty()
|
||||
name := "/ipns/" + u.Key(hash).String()
|
||||
|
||||
root := new(KeyRoot)
|
||||
root.key = k
|
||||
|
||||
54
namesys/base.go
Normal file
54
namesys/base.go
Normal file
@ -0,0 +1,54 @@
|
||||
package namesys
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
|
||||
|
||||
path "github.com/ipfs/go-ipfs/path"
|
||||
)
|
||||
|
||||
type resolver interface {
|
||||
// resolveOnce looks up a name once (without recursion).
|
||||
resolveOnce(ctx context.Context, name string) (value path.Path, err error)
|
||||
}
|
||||
|
||||
// resolve is a helper for implementing Resolver.ResolveN using resolveOnce.
|
||||
func resolve(ctx context.Context, r resolver, name string, depth int, prefixes ...string) (path.Path, error) {
|
||||
for {
|
||||
p, err := r.resolveOnce(ctx, name)
|
||||
if err != nil {
|
||||
log.Warningf("Could not resolve %s", name)
|
||||
return "", err
|
||||
}
|
||||
log.Debugf("Resolved %s to %s", name, p.String())
|
||||
|
||||
if strings.HasPrefix(p.String(), "/ipfs/") {
|
||||
// we've bottomed out with an IPFS path
|
||||
return p, nil
|
||||
}
|
||||
|
||||
if depth == 1 {
|
||||
return p, ErrResolveRecursion
|
||||
}
|
||||
|
||||
matched := false
|
||||
for _, prefix := range prefixes {
|
||||
if strings.HasPrefix(p.String(), prefix) {
|
||||
matched = true
|
||||
if len(prefixes) == 1 {
|
||||
name = strings.TrimPrefix(p.String(), prefix)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !matched {
|
||||
return p, nil
|
||||
}
|
||||
|
||||
if depth > 1 {
|
||||
depth--
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -17,16 +17,25 @@ type DNSResolver struct {
|
||||
// cache would need a timeout
|
||||
}
|
||||
|
||||
// CanResolve implements Resolver
|
||||
func (r *DNSResolver) CanResolve(name string) bool {
|
||||
return isd.IsDomain(name)
|
||||
// Resolve implements Resolver.
|
||||
func (r *DNSResolver) Resolve(ctx context.Context, name string) (path.Path, error) {
|
||||
return r.ResolveN(ctx, name, DefaultDepthLimit)
|
||||
}
|
||||
|
||||
// Resolve implements Resolver
|
||||
// ResolveN implements Resolver.
|
||||
func (r *DNSResolver) ResolveN(ctx context.Context, name string, depth int) (path.Path, error) {
|
||||
return resolve(ctx, r, name, depth, "/ipns/")
|
||||
}
|
||||
|
||||
// resolveOnce implements resolver.
|
||||
// TXT records for a given domain name should contain a b58
|
||||
// encoded multihash.
|
||||
func (r *DNSResolver) Resolve(ctx context.Context, name string) (path.Path, error) {
|
||||
log.Info("DNSResolver resolving %v", name)
|
||||
func (r *DNSResolver) resolveOnce(ctx context.Context, name string) (path.Path, error) {
|
||||
if !isd.IsDomain(name) {
|
||||
return "", errors.New("not a valid domain name")
|
||||
}
|
||||
|
||||
log.Infof("DNSResolver resolving %s", name)
|
||||
txt, err := net.LookupTXT(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@ -43,7 +52,7 @@ func (r *DNSResolver) Resolve(ctx context.Context, name string) (path.Path, erro
|
||||
}
|
||||
|
||||
func parseEntry(txt string) (path.Path, error) {
|
||||
p, err := path.ParseKeyToPath(txt)
|
||||
p, err := path.ParseKeyToPath(txt) // bare IPFS multihashes
|
||||
if err == nil {
|
||||
return p, nil
|
||||
}
|
||||
|
||||
@ -37,9 +37,24 @@ import (
|
||||
path "github.com/ipfs/go-ipfs/path"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultDepthLimit is the default depth limit used by Resolve.
|
||||
DefaultDepthLimit = 32
|
||||
|
||||
// UnlimitedDepth allows infinite recursion in ResolveN. You
|
||||
// probably don't want to use this, but it's here if you absolutely
|
||||
// trust resolution to eventually complete and can't put an upper
|
||||
// limit on how many steps it will take.
|
||||
UnlimitedDepth = 0
|
||||
)
|
||||
|
||||
// ErrResolveFailed signals an error when attempting to resolve.
|
||||
var ErrResolveFailed = errors.New("could not resolve name.")
|
||||
|
||||
// ErrResolveRecursion signals a recursion-depth limit.
|
||||
var ErrResolveRecursion = errors.New(
|
||||
"could not resolve name (recursion limit exceeded).")
|
||||
|
||||
// ErrPublishFailed signals an error when attempting to publish.
|
||||
var ErrPublishFailed = errors.New("could not publish name.")
|
||||
|
||||
@ -58,11 +73,30 @@ type NameSystem interface {
|
||||
// Resolver is an object capable of resolving names.
|
||||
type Resolver interface {
|
||||
|
||||
// Resolve looks up a name, and returns the value previously published.
|
||||
// Resolve performs a recursive lookup, returning the dereferenced
|
||||
// path. For example, if ipfs.io has a DNS TXT record pointing to
|
||||
// /ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy
|
||||
// and there is a DHT IPNS entry for
|
||||
// QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy
|
||||
// -> /ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj
|
||||
// then
|
||||
// Resolve(ctx, "/ipns/ipfs.io")
|
||||
// will resolve both names, returning
|
||||
// /ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj
|
||||
//
|
||||
// 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 use ResolveN.
|
||||
Resolve(ctx context.Context, name string) (value path.Path, err error)
|
||||
|
||||
// CanResolve checks whether this Resolver can resolve a name
|
||||
CanResolve(name string) bool
|
||||
// ResolveN performs a recursive lookup, returning the dereferenced
|
||||
// path. The only difference from Resolve is that the depth limit
|
||||
// is configurable. You can use DefaultDepthLimit, UnlimitedDepth,
|
||||
// or a depth limit of your own choosing.
|
||||
//
|
||||
// Most users should use Resolve, since the default limit works well
|
||||
// in most real-world situations.
|
||||
ResolveN(ctx context.Context, name string, depth int) (value path.Path, err error)
|
||||
}
|
||||
|
||||
// Publisher is an object capable of publishing particular names.
|
||||
|
||||
@ -1,59 +1,83 @@
|
||||
package namesys
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
|
||||
ci "github.com/ipfs/go-ipfs/p2p/crypto"
|
||||
path "github.com/ipfs/go-ipfs/path"
|
||||
routing "github.com/ipfs/go-ipfs/routing"
|
||||
)
|
||||
|
||||
// ipnsNameSystem implements IPNS naming.
|
||||
// mpns (a multi-protocol NameSystem) implements generic IPFS naming.
|
||||
//
|
||||
// Uses three Resolvers:
|
||||
// Uses several Resolvers:
|
||||
// (a) ipfs routing naming: SFS-like PKI names.
|
||||
// (b) dns domains: resolves using links in DNS TXT records
|
||||
// (c) proquints: interprets string as the raw byte data.
|
||||
//
|
||||
// It can only publish to: (a) ipfs routing naming.
|
||||
//
|
||||
type ipns struct {
|
||||
resolvers []Resolver
|
||||
publisher Publisher
|
||||
type mpns struct {
|
||||
resolvers map[string]resolver
|
||||
publishers map[string]Publisher
|
||||
}
|
||||
|
||||
// NewNameSystem will construct the IPFS naming system based on Routing
|
||||
func NewNameSystem(r routing.IpfsRouting) NameSystem {
|
||||
return &ipns{
|
||||
resolvers: []Resolver{
|
||||
new(DNSResolver),
|
||||
new(ProquintResolver),
|
||||
NewRoutingResolver(r),
|
||||
return &mpns{
|
||||
resolvers: map[string]resolver{
|
||||
"dns": new(DNSResolver),
|
||||
"proquint": new(ProquintResolver),
|
||||
"dht": newRoutingResolver(r),
|
||||
},
|
||||
publishers: map[string]Publisher{
|
||||
"/ipns/": NewRoutingPublisher(r),
|
||||
},
|
||||
publisher: NewRoutingPublisher(r),
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve implements Resolver
|
||||
func (ns *ipns) Resolve(ctx context.Context, name string) (path.Path, error) {
|
||||
for _, r := range ns.resolvers {
|
||||
if r.CanResolve(name) {
|
||||
return r.Resolve(ctx, name)
|
||||
// Resolve implements Resolver.
|
||||
func (ns *mpns) Resolve(ctx context.Context, name string) (path.Path, error) {
|
||||
return ns.ResolveN(ctx, name, DefaultDepthLimit)
|
||||
}
|
||||
|
||||
// ResolveN implements Resolver.
|
||||
func (ns *mpns) ResolveN(ctx context.Context, name string, depth int) (path.Path, error) {
|
||||
if strings.HasPrefix(name, "/ipfs/") {
|
||||
return path.ParsePath(name)
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(name, "/") {
|
||||
return path.ParsePath("/ipfs/" + name)
|
||||
}
|
||||
|
||||
return resolve(ctx, ns, name, depth, "/ipns/")
|
||||
}
|
||||
|
||||
// resolveOnce implements resolver.
|
||||
func (ns *mpns) resolveOnce(ctx context.Context, name string) (path.Path, error) {
|
||||
if !strings.HasPrefix(name, "/ipns/") {
|
||||
name = "/ipns/" + name
|
||||
}
|
||||
segments := strings.SplitN(name, "/", 3)
|
||||
if len(segments) < 3 || segments[0] != "" {
|
||||
log.Warningf("Invalid name syntax for %s", name)
|
||||
return "", ErrResolveFailed
|
||||
}
|
||||
|
||||
for protocol, resolver := range ns.resolvers {
|
||||
log.Debugf("Attempting to resolve %s with %s", name, protocol)
|
||||
p, err := resolver.resolveOnce(ctx, segments[2])
|
||||
if err == nil {
|
||||
return p, err
|
||||
}
|
||||
}
|
||||
log.Warningf("No resolver found for %s", name)
|
||||
return "", ErrResolveFailed
|
||||
}
|
||||
|
||||
// CanResolve implements Resolver
|
||||
func (ns *ipns) CanResolve(name string) bool {
|
||||
for _, r := range ns.resolvers {
|
||||
if r.CanResolve(name) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Publish implements Publisher
|
||||
func (ns *ipns) Publish(ctx context.Context, name ci.PrivKey, value path.Path) error {
|
||||
return ns.publisher.Publish(ctx, name, value)
|
||||
func (ns *mpns) Publish(ctx context.Context, name ci.PrivKey, value path.Path) error {
|
||||
return ns.publishers["/ipns/"].Publish(ctx, name, value)
|
||||
}
|
||||
|
||||
@ -10,16 +10,20 @@ import (
|
||||
|
||||
type ProquintResolver struct{}
|
||||
|
||||
// CanResolve implements Resolver. Checks whether the name is a proquint string.
|
||||
func (r *ProquintResolver) CanResolve(name string) bool {
|
||||
ok, err := proquint.IsProquint(name)
|
||||
return err == nil && ok
|
||||
// Resolve implements Resolver.
|
||||
func (r *ProquintResolver) Resolve(ctx context.Context, name string) (path.Path, error) {
|
||||
return r.ResolveN(ctx, name, DefaultDepthLimit)
|
||||
}
|
||||
|
||||
// Resolve implements Resolver. Decodes the proquint string.
|
||||
func (r *ProquintResolver) Resolve(ctx context.Context, name string) (path.Path, error) {
|
||||
ok := r.CanResolve(name)
|
||||
if !ok {
|
||||
// ResolveN implements Resolver.
|
||||
func (r *ProquintResolver) ResolveN(ctx context.Context, name string, depth int) (path.Path, error) {
|
||||
return resolve(ctx, r, name, depth, "/ipns/")
|
||||
}
|
||||
|
||||
// resolveOnce implements resolver. Decodes the proquint string.
|
||||
func (r *ProquintResolver) resolveOnce(ctx context.Context, name string) (path.Path, error) {
|
||||
ok, err := proquint.IsProquint(name)
|
||||
if err != nil || !ok {
|
||||
return "", errors.New("not a valid proquint string")
|
||||
}
|
||||
return path.FromString(string(proquint.Decode(name))), nil
|
||||
|
||||
@ -30,15 +30,28 @@ func NewRoutingResolver(route routing.IpfsRouting) Resolver {
|
||||
return &routingResolver{routing: route}
|
||||
}
|
||||
|
||||
// CanResolve implements Resolver. Checks whether name is a b58 encoded string.
|
||||
func (r *routingResolver) CanResolve(name string) bool {
|
||||
_, err := mh.FromB58String(name)
|
||||
return err == nil
|
||||
// newRoutingResolver returns a resolver instead of a Resolver.
|
||||
func newRoutingResolver(route routing.IpfsRouting) resolver {
|
||||
if route == nil {
|
||||
panic("attempt to create resolver with nil routing system")
|
||||
}
|
||||
|
||||
return &routingResolver{routing: route}
|
||||
}
|
||||
|
||||
// Resolve implements Resolver. Uses the IPFS routing system to resolve SFS-like
|
||||
// names.
|
||||
// Resolve implements Resolver.
|
||||
func (r *routingResolver) Resolve(ctx context.Context, name string) (path.Path, error) {
|
||||
return r.ResolveN(ctx, name, DefaultDepthLimit)
|
||||
}
|
||||
|
||||
// ResolveN implements Resolver.
|
||||
func (r *routingResolver) ResolveN(ctx context.Context, name string, depth int) (path.Path, error) {
|
||||
return resolve(ctx, r, name, depth, "/ipns/")
|
||||
}
|
||||
|
||||
// resolveOnce implements resolver. Uses the IPFS routing system to
|
||||
// resolve SFS-like names.
|
||||
func (r *routingResolver) resolveOnce(ctx context.Context, name string) (path.Path, error) {
|
||||
log.Debugf("RoutingResolve: '%s'", name)
|
||||
hash, err := mh.FromB58String(name)
|
||||
if err != nil {
|
||||
|
||||
20
path/path.go
20
path/path.go
@ -44,12 +44,8 @@ func (p Path) String() string {
|
||||
return string(p)
|
||||
}
|
||||
|
||||
func FromSegments(seg ...string) (Path, error) {
|
||||
var pref string
|
||||
if seg[0] == "ipfs" || seg[0] == "ipns" {
|
||||
pref = "/"
|
||||
}
|
||||
return ParsePath(pref + strings.Join(seg, "/"))
|
||||
func FromSegments(prefix string, seg ...string) (Path, error) {
|
||||
return ParsePath(prefix + strings.Join(seg, "/"))
|
||||
}
|
||||
|
||||
func ParsePath(txt string) (Path, error) {
|
||||
@ -68,15 +64,15 @@ func ParsePath(txt string) (Path, error) {
|
||||
return "", ErrBadPath
|
||||
}
|
||||
|
||||
if parts[1] != "ipfs" && parts[1] != "ipns" {
|
||||
if parts[1] == "ipfs" {
|
||||
_, err := ParseKeyToPath(parts[2])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
} else if parts[1] != "ipns" {
|
||||
return "", ErrBadPath
|
||||
}
|
||||
|
||||
_, err := ParseKeyToPath(parts[2])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return Path(txt), nil
|
||||
}
|
||||
|
||||
|
||||
@ -59,8 +59,8 @@ func TestRecurivePathResolution(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
segments := []string{"", "ipfs", aKey.String(), "child", "grandchild"}
|
||||
p, err := path.FromSegments(segments...)
|
||||
segments := []string{aKey.String(), "child", "grandchild"}
|
||||
p, err := path.FromSegments("/ipfs/", segments...)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user