Skip to content
This repository has been archived by the owner on Oct 5, 2021. It is now read-only.

make multiaddr-net more pluggable #15

Merged
merged 5 commits into from
May 16, 2016
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
192 changes: 105 additions & 87 deletions convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,100 +16,30 @@ func FromNetAddr(a net.Addr) (ma.Multiaddr, error) {
if a == nil {
return nil, fmt.Errorf("nil multiaddr")
}

switch a.Network() {
case "tcp", "tcp4", "tcp6":
ac, ok := a.(*net.TCPAddr)
if !ok {
return nil, errIncorrectNetAddr
}

// Get IP Addr
ipm, err := FromIP(ac.IP)
if err != nil {
return nil, errIncorrectNetAddr
}

// Get TCP Addr
tcpm, err := ma.NewMultiaddr(fmt.Sprintf("/tcp/%d", ac.Port))
if err != nil {
return nil, errIncorrectNetAddr
}

// Encapsulate
return ipm.Encapsulate(tcpm), nil

case "udp", "upd4", "udp6":
ac, ok := a.(*net.UDPAddr)
if !ok {
return nil, errIncorrectNetAddr
}

// Get IP Addr
ipm, err := FromIP(ac.IP)
if err != nil {
return nil, errIncorrectNetAddr
}

// Get UDP Addr
udpm, err := ma.NewMultiaddr(fmt.Sprintf("/udp/%d", ac.Port))
if err != nil {
return nil, errIncorrectNetAddr
}

// Encapsulate
return ipm.Encapsulate(udpm), nil

case "utp", "utp4", "utp6":
acc, ok := a.(*utp.Addr)
if !ok {
return nil, errIncorrectNetAddr
}

// Get UDP Addr
ac, ok := acc.Child().(*net.UDPAddr)
if !ok {
return nil, errIncorrectNetAddr
}

// Get IP Addr
ipm, err := FromIP(ac.IP)
if err != nil {
return nil, errIncorrectNetAddr
}

// Get UDP Addr
utpm, err := ma.NewMultiaddr(fmt.Sprintf("/udp/%d/utp", ac.Port))
if err != nil {
return nil, errIncorrectNetAddr
}

// Encapsulate
return ipm.Encapsulate(utpm), nil

case "ip", "ip4", "ip6":
ac, ok := a.(*net.IPAddr)
if !ok {
return nil, errIncorrectNetAddr
}
return FromIP(ac.IP)

case "ip+net":
ac, ok := a.(*net.IPNet)
if !ok {
return nil, errIncorrectNetAddr
}
return FromIP(ac.IP)

default:
return nil, fmt.Errorf("unknown network %v", a.Network())
p, err := getAddrParser(a.Network())
if err != nil {
return nil, err
}

return p(a)
}

// ToNetAddr converts a Multiaddr to a net.Addr
// Must be ThinWaist. acceptable protocol stacks are:
// /ip{4,6}/{tcp, udp}
func ToNetAddr(maddr ma.Multiaddr) (net.Addr, error) {
protos := maddr.Protocols()
final := protos[len(protos)-1]

p, err := getMaddrParser(final.Name)
if err != nil {
return nil, err
}

return p(maddr)
}

func parseBasicNetMaddr(maddr ma.Multiaddr) (net.Addr, error) {
network, host, err := DialArgs(maddr)
if err != nil {
return nil, err
Expand Down Expand Up @@ -170,3 +100,91 @@ func DialArgs(m ma.Multiaddr) (string, string, error) {
}
return network, host, nil
}

func parseTcpNetAddr(a net.Addr) (ma.Multiaddr, error) {
ac, ok := a.(*net.TCPAddr)
if !ok {
return nil, errIncorrectNetAddr
}

// Get IP Addr
ipm, err := FromIP(ac.IP)
if err != nil {
return nil, errIncorrectNetAddr
}

// Get TCP Addr
tcpm, err := ma.NewMultiaddr(fmt.Sprintf("/tcp/%d", ac.Port))
if err != nil {
return nil, errIncorrectNetAddr
}

// Encapsulate
return ipm.Encapsulate(tcpm), nil
}

func parseUdpNetAddr(a net.Addr) (ma.Multiaddr, error) {
ac, ok := a.(*net.UDPAddr)
if !ok {
return nil, errIncorrectNetAddr
}

// Get IP Addr
ipm, err := FromIP(ac.IP)
if err != nil {
return nil, errIncorrectNetAddr
}

// Get UDP Addr
udpm, err := ma.NewMultiaddr(fmt.Sprintf("/udp/%d", ac.Port))
if err != nil {
return nil, errIncorrectNetAddr
}

// Encapsulate
return ipm.Encapsulate(udpm), nil
}

func parseUtpNetAddr(a net.Addr) (ma.Multiaddr, error) {
acc, ok := a.(*utp.Addr)
if !ok {
return nil, errIncorrectNetAddr
}

// Get UDP Addr
ac, ok := acc.Child().(*net.UDPAddr)
if !ok {
return nil, errIncorrectNetAddr
}

// Get IP Addr
ipm, err := FromIP(ac.IP)
if err != nil {
return nil, errIncorrectNetAddr
}

// Get UDP Addr
utpm, err := ma.NewMultiaddr(fmt.Sprintf("/udp/%d/utp", ac.Port))
if err != nil {
return nil, errIncorrectNetAddr
}

// Encapsulate
return ipm.Encapsulate(utpm), nil
}

func parseIpNetAddr(a net.Addr) (ma.Multiaddr, error) {
ac, ok := a.(*net.IPAddr)
if !ok {
return nil, errIncorrectNetAddr
}
return FromIP(ac.IP)
}

func parseIpPlusNetAddr(a net.Addr) (ma.Multiaddr, error) {
ac, ok := a.(*net.IPNet)
if !ok {
return nil, errIncorrectNetAddr
}
return FromIP(ac.IP)
}
114 changes: 114 additions & 0 deletions registry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package manet

import (
"fmt"
"net"
"sync"

ma "github.com/jbenet/go-multiaddr"
)

type AddrParser func(a net.Addr) (ma.Multiaddr, error)
type MaddrParser func(ma ma.Multiaddr) (net.Addr, error)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of these two functions, which are named a bit confusingly, either:

type NetAddr2MAddr func(a net.Addr) (ma.Multiaddr, error)
type MAddr2NetAddr func(ma ma.Multiaddr) (net.Addr, error)

// or
type FromNetAddr func(a net.Addr) (ma.Multiaddr, error)
type ToNetAddr func(ma ma.Multiaddr) (net.Addr, error)

// or even
type NetCodec struct {
    FromNet func(a net.Addr) (ma.Multiaddr, error)
    ToNet   func(a net.Addr) (ma.Multiaddr, error)
}

var TCPNetCodec = NetCodec{}
TCPNetCodec.FromNet = func ...
TCPNetCodec.ToNet = func ...


var maddrParsers map[string]MaddrParser
var addrParsers map[string]AddrParser
var addrParsersLock sync.Mutex
Copy link
Member

@jbenet jbenet May 10, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

woah please no static stuff like this. I know it's common practice in "i'm doing it live" Go, but this is why we cant have nice things. Users may extend multiaddr with different addrs clashing on a key, and suddenly magically everything could break on them. Static monkey patching like this is pretty bad practice in highly modular code bases. with something like libp2p, we're bound to run into a problem.

this is similar, but actually way more flexible:

type CodecMap struct{
  codecs map[string]NetCodec
  sync.Mutex
}

func (cm *CodecMap) RegisterAddressType(a *AddressSpec) {
  cm.Lock()
  defer cm.Unlock()

  for _, k := range a.NetNames {
    cm.codecs[k] = a.NetCodec
  }
  cm.codecs[a.Key] = a.NetCodec
}

var defaultCodecs CodecMap

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the reason this is better is that this allows "those who want to be careful with modularity" to be perfectly safe, while still making the defaults do the typical go expected behavior for most simple programs.


type AddressSpec struct {
// NetNames is an array of strings that may be returned
// by net.Addr.Network() calls on addresses belonging to this type
NetNames []string
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

call this NetAddrProtocolNames or NetAddrNetworks -- this is a confusing legacy support thing, and we might as well be super clear.


// Key is the string value for Multiaddr address keys
Key string
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i dont understand what this means. is this the default one of the names above?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh i see. it's not called a "Key" nor "address keys". it's called a "Protocol Name". https://github.com/jbenet/multiaddr/blob/master/protocols.csv and https://github.com/jbenet/go-multiaddr/blob/master/protocols.go#L13

call this property ProtocolName or Protocol


// ParseNetAddr parses a net.Addr belonging to this type into a multiaddr
ParseNetAddr AddrParser

// ConvertMultiaddr converts a multiaddr of this type back into a net.Addr
ConvertMultiaddr MaddrParser

// Protocol returns the multiaddr protocol struct for this type
Protocol ma.Protocol
}

func RegisterAddressType(a *AddressSpec) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having a test for this func would be awesome -- then LGTM. 🐴

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its tested indirectly. None of the tests would function if this function didnt work right :P

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i did add a small test for this directly

addrParsersLock.Lock()
defer addrParsersLock.Unlock()
for _, n := range a.NetNames {
addrParsers[n] = a.ParseNetAddr
}

maddrParsers[a.Key] = a.ConvertMultiaddr
}

func init() {
addrParsers = make(map[string]AddrParser)
maddrParsers = make(map[string]MaddrParser)

RegisterAddressType(tcpAddrSpec)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make more sense for the default specs to live in here rather than convert.go?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hrm... you might be right there. code organization is hard

RegisterAddressType(udpAddrSpec)
RegisterAddressType(utpAddrSpec)
RegisterAddressType(ip4AddrSpec)
RegisterAddressType(ip6AddrSpec)

addrParsers["ip+net"] = parseIpPlusNetAddr
}

var tcpAddrSpec = &AddressSpec{
Key: "tcp",
NetNames: []string{"tcp", "tcp4", "tcp6"},
ParseNetAddr: parseTcpNetAddr,
ConvertMultiaddr: parseBasicNetMaddr,
}

var udpAddrSpec = &AddressSpec{
Key: "udp",
NetNames: []string{"udp", "udp4", "udp6"},
ParseNetAddr: parseUdpNetAddr,
ConvertMultiaddr: parseBasicNetMaddr,
}

var utpAddrSpec = &AddressSpec{
Key: "utp",
NetNames: []string{"utp", "utp4", "utp6"},
ParseNetAddr: parseUtpNetAddr,
ConvertMultiaddr: parseBasicNetMaddr,
}

var ip4AddrSpec = &AddressSpec{
Key: "ip4",
NetNames: []string{"ip4"},
ParseNetAddr: parseIpNetAddr,
ConvertMultiaddr: parseBasicNetMaddr,
}

var ip6AddrSpec = &AddressSpec{
Key: "ip6",
NetNames: []string{"ip6"},
ParseNetAddr: parseIpNetAddr,
ConvertMultiaddr: parseBasicNetMaddr,
}

func getAddrParser(net string) (AddrParser, error) {
addrParsersLock.Lock()
defer addrParsersLock.Unlock()

parser, ok := addrParsers[net]
if !ok {
return nil, fmt.Errorf("unknown network %v", net)
}
return parser, nil
}

func getMaddrParser(name string) (MaddrParser, error) {
addrParsersLock.Lock()
defer addrParsersLock.Unlock()
p, ok := maddrParsers[name]
if !ok {
return nil, fmt.Errorf("network not supported: %s", name)
}

return p, nil
}
50 changes: 50 additions & 0 deletions registry_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package manet

import (
"net"
"testing"

ma "github.com/jbenet/go-multiaddr"
)

func TestRegisterSpec(t *testing.T) {
myproto := &AddressSpec{
Key: "test",
NetNames: []string{"test", "iptest", "blahtest"},
ConvertMultiaddr: func(a ma.Multiaddr) (net.Addr, error) { return nil, nil },
ParseNetAddr: func(a net.Addr) (ma.Multiaddr, error) { return nil, nil },
}

RegisterAddressType(myproto)

_, ok := addrParsers["test"]
if !ok {
t.Fatal("myproto not properly registered")
}

_, ok = addrParsers["iptest"]
if !ok {
t.Fatal("myproto not properly registered")
}

_, ok = addrParsers["blahtest"]
if !ok {
t.Fatal("myproto not properly registered")
}

_, ok = maddrParsers["test"]
if !ok {
t.Fatal("myproto not properly registered")
}

_, ok = maddrParsers["iptest"]
if ok {
t.Fatal("myproto not properly registered")
}

_, ok = maddrParsers["blahtest"]
if ok {
t.Fatal("myproto not properly registered")
}

}