From 4f2c313bcba348de4d5d35211e38e0579f4d8384 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 20 Jan 2015 14:22:17 -0800 Subject: [PATCH] util/ipfsaddr: Multiaddr() + ID() --- util/ipfsaddr/ipfsaddr.go | 80 ++++++++++++++++++++++ util/ipfsaddr/ipfsaddr_test.go | 117 +++++++++++++++++++++++++++++++++ 2 files changed, 197 insertions(+) create mode 100644 util/ipfsaddr/ipfsaddr.go create mode 100644 util/ipfsaddr/ipfsaddr_test.go diff --git a/util/ipfsaddr/ipfsaddr.go b/util/ipfsaddr/ipfsaddr.go new file mode 100644 index 000000000000..4c1f54a1a4e9 --- /dev/null +++ b/util/ipfsaddr/ipfsaddr.go @@ -0,0 +1,80 @@ +package ipfsaddr + +import ( + "errors" + "strings" + + ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" + + peer "github.com/jbenet/go-ipfs/p2p/peer" +) + +// ErrInvalidAddr signals an address is not a valid ipfs address. +var ErrInvalidAddr = errors.New("invalid ipfs address") + +type IPFSAddr interface { + ID() peer.ID + Multiaddr() ma.Multiaddr +} + +type ipfsAddr struct { + ma ma.Multiaddr + id peer.ID +} + +func (a ipfsAddr) ID() peer.ID { + return a.id +} + +func (a ipfsAddr) Multiaddr() ma.Multiaddr { + return a.ma +} + +// ParseString parses a string representation of an address into an IPFSAddr +func ParseString(str string) (a IPFSAddr, err error) { + if str == "" { + return nil, ErrInvalidAddr + } + + m, err := ma.NewMultiaddr(str) + if err != nil { + return nil, err + } + + return ParseMultiaddr(m) +} + +// ParseMultiaddr parses a multiaddr into an IPFSAddr +func ParseMultiaddr(m ma.Multiaddr) (a IPFSAddr, err error) { + // // never panic. + // defer func() { + // if r := recover(); r != nil { + // a = nil + // err = ErrInvalidAddr + // } + // }() + + if m == nil { + return nil, ErrInvalidAddr + } + + // make sure it's an ipfs addr + parts := ma.Split(m) + if len(parts) < 1 { + return nil, ErrInvalidAddr + } + ipfspart := parts[len(parts)-1] // last part + if ipfspart.Protocols()[0].Code != ma.P_IPFS { + return nil, ErrInvalidAddr + } + + // make sure ipfs id parses as a peer.ID + peerIdParts := strings.Split(ipfspart.String(), "/") + peerIdStr := peerIdParts[len(peerIdParts)-1] + id, err := peer.IDB58Decode(peerIdStr) + if err != nil { + return nil, err + } + + return ipfsAddr{ma: m, id: id}, nil +} diff --git a/util/ipfsaddr/ipfsaddr_test.go b/util/ipfsaddr/ipfsaddr_test.go new file mode 100644 index 000000000000..d5e655e9df3b --- /dev/null +++ b/util/ipfsaddr/ipfsaddr_test.go @@ -0,0 +1,117 @@ +package ipfsaddr + +import ( + "strings" + "testing" + + ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" + peer "github.com/jbenet/go-ipfs/p2p/peer" +) + +var good = []string{ + "/ipfs/5dru6bJPUM1B7N69528u49DJiWZnok", + "/ipfs/kTRX47RthhwNzWdi6ggwqjuX", + "/ipfs/QmUCseQWXCSrhf9edzVKTvoj8o8Ts5aXFGNPameZRPJ6uR", + "/ip4/1.2.3.4/tcp/1234/ipfs/5dru6bJPUM1B7N69528u49DJiWZnok", + "/ip4/1.2.3.4/tcp/1234/ipfs/kTRX47RthhwNzWdi6ggwqjuX", + "/ip4/1.2.3.4/tcp/1234/ipfs/QmUCseQWXCSrhf9edzVKTvoj8o8Ts5aXFGNPameZRPJ6uR", +} + +var bad = []string{ + "5dru6bJPUM1B7N69528u49DJiWZnok", // bad ma + "kTRX47RthhwNzWdi6ggwqjuX", // bad ma + "QmUCseQWXCSrhf9edzVKTvoj8o8Ts5aXFGNPameZRPJ6uR", // bad ma + "ipfs/5dru6bJPUM1B7N69528u49DJiWZnok", // bad ma + "ipfs/kTRX47RthhwNzWdi6ggwqjuX", // bad ma + "ipfs/QmUCseQWXCSrhf9edzVKTvoj8o8Ts5aXFGNPameZRPJ6uR", // bad ma + "/ipfs/5dru6bJPUM1B7N69528u49DJiWZno", // bad mh + "/ipfs/kTRX47RthhwNzWdi6ggwqju", // bad mh + "/ipfs/QmUCseQWXCSrhf9edzVKTvj8o8Ts5aXFGNPameZRPJ6uR", // bad mh + "/ipfs/QmUCseQWXCSrhf9edzVKTvoj8o8Ts5aXFGNPameZRPJ6uR/tcp/1234", // ipfs not last + "/ip4/1.2.3.4/tcp/ipfs/5dru6bJPUM1B7N69528u49DJiWZnok", // bad tcp part + "/ip4/tcp/1234/ipfs/kTRX47RthhwNzWdi6ggwqjuX", // bad ip part + "/ip4/1.2.3.4/tcp/1234/ipfs", // no id + "/ip4/1.2.3.4/tcp/1234/ipfs/", // no id +} + +func newMultiaddr(t *testing.T, s string) ma.Multiaddr { + maddr, err := ma.NewMultiaddr(s) + if err != nil { + t.Fatal(err) + } + return maddr +} + +func TestParseStringGood(t *testing.T) { + for _, g := range good { + if _, err := ParseString(g); err != nil { + t.Error("failed to parse", g, err) + } + } +} + +func TestParseStringBad(t *testing.T) { + for _, b := range bad { + if _, err := ParseString(b); err == nil { + t.Error("succeeded in parsing", b) + } + } +} + +func TestParseMultiaddrGood(t *testing.T) { + for _, g := range good { + if _, err := ParseMultiaddr(newMultiaddr(t, g)); err != nil { + t.Error("failed to parse", g, err) + } + } +} + +func TestParseMultiaddrBad(t *testing.T) { + for _, b := range bad { + m, err := ma.NewMultiaddr(b) + if err != nil { + continue // skip these. + } + + if _, err := ParseMultiaddr(m); err == nil { + t.Error("succeeded in parsing", m) + } + } +} + +func TestIDMatches(t *testing.T) { + for _, g := range good { + a, err := ParseString(g) + if err != nil { + t.Error("failed to parse", g, err) + continue + } + + sp := strings.Split(g, "/") + sid := sp[len(sp)-1] + id, err := peer.IDB58Decode(sid) + if err != nil { + t.Error("failed to parse", sid, err) + continue + } + + if a.ID() != id { + t.Error("not equal", a.ID(), id) + } + } +} + +func TestMultiaddrMatches(t *testing.T) { + for _, g := range good { + a, err := ParseString(g) + if err != nil { + t.Error("failed to parse", g, err) + continue + } + + m := newMultiaddr(t, g) + if !a.Multiaddr().Equal(m) { + t.Error("not equal", a.Multiaddr(), m) + } + } +}