Dmitry eeca435064 Add rendezvous implementation for discovery interface
Update vendor

Integrate rendezvous into status node

Add a test with failover using rendezvous

Use multiple servers in client

Use discovery V5 by default and test that node can be started with rendezvous discovet

Fix linter

Update rendezvous client to one with instrumented stream

Address feedback

Fix test with updated topic limits

Apply several suggestions

Change log to debug for request errors because we continue execution

Remove web3js after rebase

Update rendezvous package
2018-07-25 15:10:57 +03:00

111 lines
3.2 KiB
Go

package tcpreuse
import (
"context"
"fmt"
"math/rand"
"net"
)
type multiDialer struct {
loopback []*net.TCPAddr
unspecified []*net.TCPAddr
global *net.TCPAddr
}
func (d *multiDialer) Dial(network, addr string) (net.Conn, error) {
return d.DialContext(context.Background(), network, addr)
}
func randAddr(addrs []*net.TCPAddr) *net.TCPAddr {
if len(addrs) > 0 {
return addrs[rand.Intn(len(addrs))]
}
return nil
}
func (d *multiDialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
tcpAddr, err := net.ResolveTCPAddr(network, addr)
if err != nil {
return nil, err
}
// We pick the source *port* based on the following algorithm.
//
// 1. If we're dialing loopback, choose a source-port in order of
// preference:
// 1. A port in-use by an explicit loopback listener.
// 2. A port in-use by a listener on an unspecified address (must
// also be listening on localhost).
// 3. A port in-use by a listener on a global address. We don't have
// any other better options (other than picking a random port).
// 2. If we're dialing a global address, choose a source-port in order
// of preference:
// 1. A port in-use by a listener on an unspecified address (the most
// general case).
// 2. A port in-use by a listener on the global address.
// 3. Fail on link-local dials (go-ipfs currently forbids this and I
// figured we could try lifting this restriction later).
//
//
// Note: We *always* dial from the unspecified address (regardless of
// the port we pick). In the future, we could use netlink (on Linux) to
// figure out the right source address but we're going to punt on that.
ip := tcpAddr.IP
source := d.global
switch {
case ip.IsLoopback():
switch {
case len(d.loopback) > 0:
source = randAddr(d.loopback)
case len(d.unspecified) > 0:
source = randAddr(d.unspecified)
}
case ip.IsGlobalUnicast():
switch {
case len(d.unspecified) > 0:
source = randAddr(d.unspecified)
}
default:
return nil, fmt.Errorf("undialable IP: %s", tcpAddr.IP)
}
return reuseDial(ctx, source, network, addr)
}
func newMultiDialer(unspec net.IP, listeners map[*listener]struct{}) dialer {
m := new(multiDialer)
for l := range listeners {
laddr := l.Addr().(*net.TCPAddr)
switch {
case laddr.IP.IsLoopback():
m.loopback = append(m.loopback, laddr)
case laddr.IP.IsGlobalUnicast():
// Different global ports? Crap.
//
// The *proper* way to deal with this is to, e.g., use
// netlink to figure out which source address we would
// normally use to dial a destination address and then
// pick one of the ports we're listening on on that
// source address. However, this is a pain in the ass.
//
// Instead, we're just going to always dial from the
// unspecified address with the first global port we
// find.
//
// TODO: Port priority? Addr priority?
if m.global == nil {
m.global = &net.TCPAddr{
IP: unspec,
Port: laddr.Port,
}
} else {
log.Warning("listening on external interfaces on multiple ports, will dial from %d, not %s", m.global, laddr)
}
case laddr.IP.IsUnspecified():
m.unspecified = append(m.unspecified, laddr)
}
}
return m
}