Files
libp2p/interface.go
2015-01-20 19:32:37 -08:00

112 lines
3.4 KiB
Go

// Package reuseport provides Listen and Dial functions that set socket options
// in order to be able to reuse ports. You should only use this package if you
// know what SO_REUSEADDR and SO_REUSEPORT are.
//
// For example:
//
// // listen on the same port. oh yeah.
// l1, _ := reuse.Listen("tcp", "127.0.0.1:1234")
// l2, _ := reuse.Listen("tcp", "127.0.0.1:1234")
//
// // dial from the same port. oh yeah.
// l1, _ := reuse.Listen("tcp", "127.0.0.1:1234")
// l2, _ := reuse.Listen("tcp", "127.0.0.1:1235")
// c, _ := reuse.Dial("tcp", "127.0.0.1:1234", "127.0.0.1:1235")
//
// Note: cant dial self because tcp/ip stacks use 4-tuples to identify connections,
// and doing so would clash.
package reuseport
import (
"errors"
"net"
"syscall"
"time"
)
// Available returns whether or not SO_REUSEPORT is available in the OS.
// It does so by attepting to open a tcp listener, setting the option, and
// checking ENOPROTOOPT on error. After checking, the decision is cached
// for the rest of the process run.
func Available() bool {
return available()
}
// ErrUnsuportedProtocol signals that the protocol is not currently
// supported by this package. This package currently only supports TCP.
var ErrUnsupportedProtocol = errors.New("protocol not yet supported")
// ErrReuseFailed is returned if a reuse attempt was unsuccessful.
var ErrReuseFailed = errors.New("reuse failed")
// Listen listens at the given network and address. see net.Listen
// Returns a net.Listener created from a file discriptor for a socket
// with SO_REUSEPORT and SO_REUSEADDR option set.
func Listen(network, address string) (net.Listener, error) {
if !available() {
return nil, syscall.Errno(syscall.ENOPROTOOPT)
}
return listenStream(network, address)
}
// ListenPacket listens at the given network and address. see net.ListenPacket
// Returns a net.Listener created from a file discriptor for a socket
// with SO_REUSEPORT and SO_REUSEADDR option set.
func ListenPacket(network, address string) (net.PacketConn, error) {
if !available() {
return nil, syscall.Errno(syscall.ENOPROTOOPT)
}
return listenPacket(network, address)
}
// Dial dials the given network and address. see net.Dialer.Dial
// Returns a net.Conn created from a file discriptor for a socket
// with SO_REUSEPORT and SO_REUSEADDR option set.
func Dial(network, laddr, raddr string) (net.Conn, error) {
if !available() {
return nil, syscall.Errno(syscall.ENOPROTOOPT)
}
var d Dialer
if laddr != "" {
netladdr, err := ResolveAddr(network, laddr)
if err != nil {
return nil, err
}
d.D.LocalAddr = netladdr
}
return d.Dial(network, raddr)
}
// Dialer is used to specify the Dial options, much like net.Dialer.
// We simply wrap a net.Dialer.
type Dialer struct {
D net.Dialer
}
// Dial dials the given network and address. see net.Dialer.Dial
// Returns a net.Conn created from a file discriptor for a socket
// with SO_REUSEPORT and SO_REUSEADDR option set.
func (d *Dialer) Dial(network, address string) (net.Conn, error) {
c, err := dial(d.D, network, address)
if err != nil {
return nil, err
}
// there's a rare case where dial returns successfully but for some reason the
// RemoteAddr is not yet set. We wait here a while until it is, and if too long
// passes, we fail. This is horrendous.
for start := time.Now(); c.RemoteAddr() == nil; {
if time.Now().Sub(start) > (time.Millisecond * 500) {
c.Close()
return nil, ErrReuseFailed
}
<-time.After(20 * time.Microsecond)
}
return c, nil
}