mirror of
https://github.com/logos-messaging/go-multiaddr.git
synced 2026-01-02 13:03:11 +00:00
New Multiaddr interface
This commit changes the struct to a new Multiaddr interface:
```Go
type Multiaddr interface {
Equal(Multiaddr) bool
Bytes() []byte
String() string
Protocols() []*Protocol
Encapsulate(Multiaddr) Multiaddr
Decapsulate(Multiaddr) Multiaddr
}
```
This means a few things have changed:
- use Multiaddr interface, struct not exported
- Bytes returns a copy of the internal bytes
- Some methods no longer return errors (catch errors in NewMultiaddr)
- String (panics if malformed)
- Protocols (panics if malformed)
- Decapsulate (no-op if not prefix)
- Moved net-specific functions to package
- Multiaddr.DialArgs() -> DialArgs(Multiaddr)
- Multiaddr.IsThinWaist() -> IsThinWaist(Multiaddr)
cc @whyrusleeping @perfmode
This commit is contained in:
parent
bd30912d08
commit
c90ef4472f
116
index.go
116
index.go
@ -1,116 +0,0 @@
|
||||
package multiaddr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Multiaddr is the data structure representing a multiaddr
|
||||
type Multiaddr struct {
|
||||
Bytes []byte
|
||||
}
|
||||
|
||||
// NewMultiaddr parses and validates an input string, returning a *Multiaddr
|
||||
func NewMultiaddr(s string) (*Multiaddr, error) {
|
||||
b, err := stringToBytes(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Multiaddr{Bytes: b}, nil
|
||||
}
|
||||
|
||||
// Equal tests whether two multiaddrs are equal
|
||||
func (m *Multiaddr) Equal(m2 *Multiaddr) bool {
|
||||
return bytes.Equal(m.Bytes, m2.Bytes)
|
||||
}
|
||||
|
||||
// String returns the string representation of a Multiaddr
|
||||
func (m *Multiaddr) String() string {
|
||||
s, err := bytesToString(m.Bytes)
|
||||
if err != nil {
|
||||
panic("multiaddr failed to convert back to string. corrupted?")
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Protocols returns the list of protocols this Multiaddr has.
|
||||
func (m *Multiaddr) Protocols() (ret []*Protocol, err error) {
|
||||
|
||||
// panic handler, in case we try accessing bytes incorrectly.
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
ret = nil
|
||||
err = e.(error)
|
||||
}
|
||||
}()
|
||||
|
||||
ps := []*Protocol{}
|
||||
b := m.Bytes[:]
|
||||
for len(b) > 0 {
|
||||
p := ProtocolWithCode(int(b[0]))
|
||||
if p == nil {
|
||||
return nil, fmt.Errorf("no protocol with code %d", b[0])
|
||||
}
|
||||
ps = append(ps, p)
|
||||
b = b[1+(p.Size/8):]
|
||||
}
|
||||
return ps, nil
|
||||
}
|
||||
|
||||
// Encapsulate wraps a given Multiaddr, returning the resulting joined Multiaddr
|
||||
func (m *Multiaddr) Encapsulate(o *Multiaddr) *Multiaddr {
|
||||
b := make([]byte, len(m.Bytes)+len(o.Bytes))
|
||||
b = append(m.Bytes, o.Bytes...)
|
||||
return &Multiaddr{Bytes: b}
|
||||
}
|
||||
|
||||
// Decapsulate unwraps Multiaddr up until the given Multiaddr is found.
|
||||
func (m *Multiaddr) Decapsulate(o *Multiaddr) (*Multiaddr, error) {
|
||||
s1 := m.String()
|
||||
s2 := o.String()
|
||||
i := strings.LastIndex(s1, s2)
|
||||
if i < 0 {
|
||||
return nil, fmt.Errorf("%s not contained in %s", s2, s1)
|
||||
}
|
||||
return NewMultiaddr(s1[:i])
|
||||
}
|
||||
|
||||
// DialArgs is a convenience function returning arguments for use in net.Dial
|
||||
func (m *Multiaddr) DialArgs() (string, string, error) {
|
||||
if !m.IsThinWaist() {
|
||||
return "", "", fmt.Errorf("%s is not a 'thin waist' address", m)
|
||||
}
|
||||
|
||||
str := m.String()
|
||||
parts := strings.Split(str, "/")[1:]
|
||||
network := parts[2]
|
||||
|
||||
var host string
|
||||
switch parts[0] {
|
||||
case "ip4":
|
||||
host = strings.Join([]string{parts[1], parts[3]}, ":")
|
||||
case "ip6":
|
||||
host = fmt.Sprintf("[%s]:%s", parts[1], parts[3])
|
||||
}
|
||||
return network, host, nil
|
||||
}
|
||||
|
||||
// IsThinWaist returns whether this multiaddr includes "Thin Waist" Protocols.
|
||||
// This means: /{IP4, IP6}/{TCP, UDP}
|
||||
func (m *Multiaddr) IsThinWaist() bool {
|
||||
p, err := m.Protocols()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if p[0].Code != P_IP4 && p[0].Code != P_IP6 {
|
||||
return false
|
||||
}
|
||||
|
||||
if p[1].Code != P_TCP && p[1].Code != P_UDP {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
42
interface.go
Normal file
42
interface.go
Normal file
@ -0,0 +1,42 @@
|
||||
package multiaddr
|
||||
|
||||
/*
|
||||
Multiaddr is a cross-protocol, cross-platform format for representing
|
||||
internet addresses. It emphasizes explicitness and self-description.
|
||||
Learn more here: https://github.com/jbenet/multiaddr
|
||||
|
||||
Multiaddrs have both a binary and string representation.
|
||||
|
||||
import ma "github.com/jbenet/go-multiaddr"
|
||||
|
||||
addr, err := ma.NewMultiaddr("/ip4/1.2.3.4/tcp/80")
|
||||
// err non-nil when parsing failed.
|
||||
|
||||
*/
|
||||
type Multiaddr interface {
|
||||
// Equal returns whether two Multiaddrs are exactly equal
|
||||
Equal(Multiaddr) bool
|
||||
|
||||
// Bytes returns the []byte representation of this Multiaddr
|
||||
Bytes() []byte
|
||||
|
||||
// String returns the string representation of this Multiaddr
|
||||
// (may panic if internal state is corrupted)
|
||||
String() string
|
||||
|
||||
// Protocols returns the list of Protocols this Multiaddr includes
|
||||
// will panic if protocol code incorrect (and bytes accessed incorrectly)
|
||||
Protocols() []*Protocol
|
||||
|
||||
// Encapsulate wraps this Multiaddr around another. For example:
|
||||
//
|
||||
// /ip4/1.2.3.4 encapsulate /tcp/80 = /ip4/1.2.3.4/tcp/80
|
||||
//
|
||||
Encapsulate(Multiaddr) Multiaddr
|
||||
|
||||
// Decapsultate removes a Multiaddr wrapping. For example:
|
||||
//
|
||||
// /ip4/1.2.3.4/tcp/80 decapsulate /ip4/1.2.3.4 = /tcp/80
|
||||
//
|
||||
Decapsulate(Multiaddr) Multiaddr
|
||||
}
|
||||
110
multiaddr.go
Normal file
110
multiaddr.go
Normal file
@ -0,0 +1,110 @@
|
||||
package multiaddr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// multiaddr is the data structure representing a Multiaddr
|
||||
type multiaddr struct {
|
||||
bytes []byte
|
||||
}
|
||||
|
||||
// NewMultiaddr parses and validates an input string, returning a *Multiaddr
|
||||
func NewMultiaddr(s string) (Multiaddr, error) {
|
||||
b, err := stringToBytes(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &multiaddr{bytes: b}, nil
|
||||
}
|
||||
|
||||
// NewMultiaddrBytes initializes a Multiaddr from a byte representation.
|
||||
// It validates it as an input string.
|
||||
func NewMultiaddrBytes(b []byte) (Multiaddr, error) {
|
||||
s, err := bytesToString(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewMultiaddr(s)
|
||||
}
|
||||
|
||||
// Equal tests whether two multiaddrs are equal
|
||||
func (m *multiaddr) Equal(m2 Multiaddr) bool {
|
||||
return bytes.Equal(m.bytes, m2.Bytes())
|
||||
}
|
||||
|
||||
// Bytes returns the []byte representation of this Multiaddr
|
||||
func (m *multiaddr) Bytes() []byte {
|
||||
// consider returning copy to prevent changing underneath us?
|
||||
cpy := make([]byte, len(m.bytes))
|
||||
copy(cpy, m.bytes)
|
||||
return cpy
|
||||
}
|
||||
|
||||
// String returns the string representation of a Multiaddr
|
||||
func (m *multiaddr) String() string {
|
||||
s, err := bytesToString(m.bytes)
|
||||
if err != nil {
|
||||
panic("multiaddr failed to convert back to string. corrupted?")
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Protocols returns the list of protocols this Multiaddr has.
|
||||
// will panic in case we access bytes incorrectly.
|
||||
func (m *multiaddr) Protocols() []*Protocol {
|
||||
|
||||
// panic handler, in case we try accessing bytes incorrectly.
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err := e.(error)
|
||||
panic("Multiaddr.Protocols error: " + err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
ps := []*Protocol{}
|
||||
b := m.bytes[:]
|
||||
for len(b) > 0 {
|
||||
p := ProtocolWithCode(int(b[0]))
|
||||
if p == nil {
|
||||
// this is a panic (and not returning err) because this should've been
|
||||
// caught on constructing the Multiaddr
|
||||
panic(fmt.Errorf("no protocol with code %d", b[0]))
|
||||
}
|
||||
ps = append(ps, p)
|
||||
b = b[1+(p.Size/8):]
|
||||
}
|
||||
return ps
|
||||
}
|
||||
|
||||
// Encapsulate wraps a given Multiaddr, returning the resulting joined Multiaddr
|
||||
func (m *multiaddr) Encapsulate(o Multiaddr) Multiaddr {
|
||||
mb := m.bytes
|
||||
ob := o.Bytes()
|
||||
|
||||
var b bytes.Buffer
|
||||
b.Write(mb)
|
||||
b.Write(ob)
|
||||
return &multiaddr{bytes: b.Bytes()}
|
||||
}
|
||||
|
||||
// Decapsulate unwraps Multiaddr up until the given Multiaddr is found.
|
||||
func (m *multiaddr) Decapsulate(o Multiaddr) Multiaddr {
|
||||
s1 := m.String()
|
||||
s2 := o.String()
|
||||
i := strings.LastIndex(s1, s2)
|
||||
if i < 0 {
|
||||
// if multiaddr not contained, returns a copy.
|
||||
cpy := make([]byte, len(m.bytes))
|
||||
copy(cpy, m.bytes)
|
||||
return &multiaddr{bytes: cpy}
|
||||
}
|
||||
|
||||
ma, err := NewMultiaddr(s1[:i])
|
||||
if err != nil {
|
||||
panic("Multiaddr.Decapsulate incorrect byte boundaries.")
|
||||
}
|
||||
return ma
|
||||
}
|
||||
@ -6,7 +6,7 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func newMultiaddr(t *testing.T, a string) *Multiaddr {
|
||||
func newMultiaddr(t *testing.T, a string) Multiaddr {
|
||||
m, err := NewMultiaddr(a)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
@ -88,11 +88,7 @@ func TestProtocols(t *testing.T) {
|
||||
t.Error("failed to construct", "/ip4/127.0.0.1/udp/1234")
|
||||
}
|
||||
|
||||
ps, err := m.Protocols()
|
||||
if err != nil {
|
||||
t.Error("failed to get protocols", "/ip4/127.0.0.1/udp/1234")
|
||||
}
|
||||
|
||||
ps := m.Protocols()
|
||||
if ps[0] != ProtocolWithName("ip4") {
|
||||
t.Error(ps[0], ProtocolWithName("ip4"))
|
||||
t.Error("failed to get ip4 protocol")
|
||||
@ -122,42 +118,14 @@ func TestEncapsulate(t *testing.T) {
|
||||
}
|
||||
|
||||
m3, _ := NewMultiaddr("/udp/5678")
|
||||
c, err := b.Decapsulate(m3)
|
||||
if err != nil {
|
||||
t.Error("decapsulate /udp failed.", err)
|
||||
}
|
||||
|
||||
c := b.Decapsulate(m3)
|
||||
if s := c.String(); s != "/ip4/127.0.0.1/udp/1234" {
|
||||
t.Error("decapsulate /udp failed.", "/ip4/127.0.0.1/udp/1234", s)
|
||||
}
|
||||
|
||||
m4, _ := NewMultiaddr("/ip4/127.0.0.1")
|
||||
d, err := c.Decapsulate(m4)
|
||||
if err != nil {
|
||||
t.Error("decapsulate /ip4 failed.", err)
|
||||
}
|
||||
|
||||
d := c.Decapsulate(m4)
|
||||
if s := d.String(); s != "" {
|
||||
t.Error("decapsulate /ip4 failed.", "/", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDialArgs(t *testing.T) {
|
||||
m, err := NewMultiaddr("/ip4/127.0.0.1/udp/1234")
|
||||
if err != nil {
|
||||
t.Fatal("failed to construct", "/ip4/127.0.0.1/udp/1234")
|
||||
}
|
||||
|
||||
nw, host, err := m.DialArgs()
|
||||
if err != nil {
|
||||
t.Fatal("failed to get dial args", "/ip4/127.0.0.1/udp/1234", err)
|
||||
}
|
||||
|
||||
if nw != "udp" {
|
||||
t.Error("failed to get udp network Dial Arg")
|
||||
}
|
||||
|
||||
if host != "127.0.0.1:1234" {
|
||||
t.Error("failed to get host:port Dial Arg")
|
||||
}
|
||||
}
|
||||
|
||||
40
net.go
40
net.go
@ -3,12 +3,13 @@ package multiaddr
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var errIncorrectNetAddr = fmt.Errorf("incorrect network addr conversion")
|
||||
|
||||
// FromNetAddr converts a net.Addr type to a Multiaddr.
|
||||
func FromNetAddr(a net.Addr) (*Multiaddr, error) {
|
||||
func FromNetAddr(a net.Addr) (Multiaddr, error) {
|
||||
switch a.Network() {
|
||||
case "tcp", "tcp4", "tcp6":
|
||||
ac, ok := a.(*net.TCPAddr)
|
||||
@ -65,7 +66,7 @@ func FromNetAddr(a net.Addr) (*Multiaddr, error) {
|
||||
}
|
||||
|
||||
// FromIP converts a net.IP type to a Multiaddr.
|
||||
func FromIP(ip net.IP) (*Multiaddr, error) {
|
||||
func FromIP(ip net.IP) (Multiaddr, error) {
|
||||
switch {
|
||||
case ip.To4() != nil:
|
||||
return NewMultiaddr("/ip4/" + ip.String())
|
||||
@ -75,3 +76,38 @@ func FromIP(ip net.IP) (*Multiaddr, error) {
|
||||
return nil, errIncorrectNetAddr
|
||||
}
|
||||
}
|
||||
|
||||
// DialArgs is a convenience function returning arguments for use in net.Dial
|
||||
func DialArgs(m Multiaddr) (string, string, error) {
|
||||
if !IsThinWaist(m) {
|
||||
return "", "", fmt.Errorf("%s is not a 'thin waist' address", m)
|
||||
}
|
||||
|
||||
str := m.String()
|
||||
parts := strings.Split(str, "/")[1:]
|
||||
network := parts[2]
|
||||
|
||||
var host string
|
||||
switch parts[0] {
|
||||
case "ip4":
|
||||
host = strings.Join([]string{parts[1], parts[3]}, ":")
|
||||
case "ip6":
|
||||
host = fmt.Sprintf("[%s]:%s", parts[1], parts[3])
|
||||
}
|
||||
return network, host, nil
|
||||
}
|
||||
|
||||
// IsThinWaist returns whether a Multiaddr starts with "Thin Waist" Protocols.
|
||||
// This means: /{IP4, IP6}/{TCP, UDP}
|
||||
func IsThinWaist(m Multiaddr) bool {
|
||||
p := m.Protocols()
|
||||
if p[0].Code != P_IP4 && p[0].Code != P_IP6 {
|
||||
return false
|
||||
}
|
||||
|
||||
if p[1].Code != P_TCP && p[1].Code != P_UDP {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
30
net_test.go
30
net_test.go
@ -5,7 +5,7 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
type GenFunc func() (*Multiaddr, error)
|
||||
type GenFunc func() (Multiaddr, error)
|
||||
|
||||
func testConvert(t *testing.T, s string, gen GenFunc) {
|
||||
m, err := gen()
|
||||
@ -19,19 +19,19 @@ func testConvert(t *testing.T, s string, gen GenFunc) {
|
||||
}
|
||||
|
||||
func TestFromIP4(t *testing.T) {
|
||||
testConvert(t, "/ip4/10.20.30.40", func() (*Multiaddr, error) {
|
||||
testConvert(t, "/ip4/10.20.30.40", func() (Multiaddr, error) {
|
||||
return FromIP(net.ParseIP("10.20.30.40"))
|
||||
})
|
||||
}
|
||||
|
||||
func TestFromIP6(t *testing.T) {
|
||||
testConvert(t, "/ip6/2001:4860:0:2001::68", func() (*Multiaddr, error) {
|
||||
testConvert(t, "/ip6/2001:4860:0:2001::68", func() (Multiaddr, error) {
|
||||
return FromIP(net.ParseIP("2001:4860:0:2001::68"))
|
||||
})
|
||||
}
|
||||
|
||||
func TestFromTCP(t *testing.T) {
|
||||
testConvert(t, "/ip4/10.20.30.40/tcp/1234", func() (*Multiaddr, error) {
|
||||
testConvert(t, "/ip4/10.20.30.40/tcp/1234", func() (Multiaddr, error) {
|
||||
return FromNetAddr(&net.TCPAddr{
|
||||
IP: net.ParseIP("10.20.30.40"),
|
||||
Port: 1234,
|
||||
@ -40,10 +40,30 @@ func TestFromTCP(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestFromUDP(t *testing.T) {
|
||||
testConvert(t, "/ip4/10.20.30.40/udp/1234", func() (*Multiaddr, error) {
|
||||
testConvert(t, "/ip4/10.20.30.40/udp/1234", func() (Multiaddr, error) {
|
||||
return FromNetAddr(&net.UDPAddr{
|
||||
IP: net.ParseIP("10.20.30.40"),
|
||||
Port: 1234,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestDialArgs(t *testing.T) {
|
||||
m, err := NewMultiaddr("/ip4/127.0.0.1/udp/1234")
|
||||
if err != nil {
|
||||
t.Fatal("failed to construct", "/ip4/127.0.0.1/udp/1234")
|
||||
}
|
||||
|
||||
nw, host, err := DialArgs(m)
|
||||
if err != nil {
|
||||
t.Fatal("failed to get dial args", "/ip4/127.0.0.1/udp/1234", err)
|
||||
}
|
||||
|
||||
if nw != "udp" {
|
||||
t.Error("failed to get udp network Dial Arg")
|
||||
}
|
||||
|
||||
if host != "127.0.0.1:1234" {
|
||||
t.Error("failed to get host:port Dial Arg")
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user