github.com/zignig/go-ipfs@v0.0.0-20141111235910-c9e5fdf55a52/net/conn/conn.go (about)

     1  package conn
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  	"time"
     7  
     8  	context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
     9  	msgio "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-msgio"
    10  	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
    11  	manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net"
    12  
    13  	peer "github.com/jbenet/go-ipfs/peer"
    14  	u "github.com/jbenet/go-ipfs/util"
    15  	ctxc "github.com/jbenet/go-ipfs/util/ctxcloser"
    16  )
    17  
    18  var log = u.Logger("conn")
    19  
    20  const (
    21  	// ChanBuffer is the size of the buffer in the Conn Chan
    22  	ChanBuffer = 10
    23  
    24  	// MaxMessageSize is the size of the largest single message
    25  	MaxMessageSize = 1 << 20
    26  
    27  	// HandshakeTimeout for when nodes first connect
    28  	HandshakeTimeout = time.Second * 5
    29  )
    30  
    31  // global static buffer pool for byte arrays of size MaxMessageSize
    32  var BufferPool *sync.Pool
    33  
    34  func init() {
    35  	BufferPool = new(sync.Pool)
    36  	BufferPool.New = func() interface{} {
    37  		log.Warning("Pool returning new object")
    38  		return make([]byte, MaxMessageSize)
    39  	}
    40  }
    41  
    42  // ReleaseBuffer puts the given byte array back into the buffer pool,
    43  // first verifying that it is the correct size
    44  func ReleaseBuffer(b []byte) {
    45  	log.Warningf("Releasing buffer! (cap,size = %d, %d)", cap(b), len(b))
    46  	if cap(b) != MaxMessageSize {
    47  		log.Warning("Release buffer failed (cap, size = %d, %d)", cap(b), len(b))
    48  		return
    49  	}
    50  	BufferPool.Put(b[:cap(b)])
    51  }
    52  
    53  // msgioPipe is a pipe using msgio channels.
    54  type msgioPipe struct {
    55  	outgoing *msgio.Chan
    56  	incoming *msgio.Chan
    57  }
    58  
    59  func newMsgioPipe(size int, pool *sync.Pool) *msgioPipe {
    60  	return &msgioPipe{
    61  		outgoing: msgio.NewChan(size),
    62  		incoming: msgio.NewChanWithPool(size, pool),
    63  	}
    64  }
    65  
    66  // singleConn represents a single connection to another Peer (IPFS Node).
    67  type singleConn struct {
    68  	local  peer.Peer
    69  	remote peer.Peer
    70  	maconn manet.Conn
    71  	msgio  *msgioPipe
    72  
    73  	ctxc.ContextCloser
    74  }
    75  
    76  // newConn constructs a new connection
    77  func newSingleConn(ctx context.Context, local, remote peer.Peer,
    78  	maconn manet.Conn) (Conn, error) {
    79  
    80  	conn := &singleConn{
    81  		local:  local,
    82  		remote: remote,
    83  		maconn: maconn,
    84  		msgio:  newMsgioPipe(10, BufferPool),
    85  	}
    86  
    87  	conn.ContextCloser = ctxc.NewContextCloser(ctx, conn.close)
    88  
    89  	log.Info("newSingleConn: %v to %v", local, remote)
    90  
    91  	// setup the various io goroutines
    92  	conn.Children().Add(1)
    93  	go func() {
    94  		conn.msgio.outgoing.WriteTo(maconn)
    95  		conn.Children().Done()
    96  	}()
    97  	conn.Children().Add(1)
    98  	go func() {
    99  		conn.msgio.incoming.ReadFrom(maconn, MaxMessageSize)
   100  		conn.Children().Done()
   101  	}()
   102  
   103  	// version handshake
   104  	ctxT, _ := context.WithTimeout(ctx, HandshakeTimeout)
   105  	if err := Handshake1(ctxT, conn); err != nil {
   106  		conn.Close()
   107  		return nil, fmt.Errorf("Handshake1 failed: %s", err)
   108  	}
   109  
   110  	return conn, nil
   111  }
   112  
   113  // close is the internal close function, called by ContextCloser.Close
   114  func (c *singleConn) close() error {
   115  	log.Debugf("%s closing Conn with %s", c.local, c.remote)
   116  
   117  	// close underlying connection
   118  	err := c.maconn.Close()
   119  	c.msgio.outgoing.Close()
   120  	return err
   121  }
   122  
   123  func (c *singleConn) GetError() error {
   124  	select {
   125  	case err := <-c.msgio.incoming.ErrChan:
   126  		return err
   127  	case err := <-c.msgio.outgoing.ErrChan:
   128  		return err
   129  	default:
   130  		return nil
   131  	}
   132  }
   133  
   134  // ID is an identifier unique to this connection.
   135  func (c *singleConn) ID() string {
   136  	return ID(c)
   137  }
   138  
   139  func (c *singleConn) String() string {
   140  	return String(c, "singleConn")
   141  }
   142  
   143  // LocalMultiaddr is the Multiaddr on this side
   144  func (c *singleConn) LocalMultiaddr() ma.Multiaddr {
   145  	return c.maconn.LocalMultiaddr()
   146  }
   147  
   148  // RemoteMultiaddr is the Multiaddr on the remote side
   149  func (c *singleConn) RemoteMultiaddr() ma.Multiaddr {
   150  	return c.maconn.RemoteMultiaddr()
   151  }
   152  
   153  // LocalPeer is the Peer on this side
   154  func (c *singleConn) LocalPeer() peer.Peer {
   155  	return c.local
   156  }
   157  
   158  // RemotePeer is the Peer on the remote side
   159  func (c *singleConn) RemotePeer() peer.Peer {
   160  	return c.remote
   161  }
   162  
   163  // In returns a readable message channel
   164  func (c *singleConn) In() <-chan []byte {
   165  	return c.msgio.incoming.MsgChan
   166  }
   167  
   168  // Out returns a writable message channel
   169  func (c *singleConn) Out() chan<- []byte {
   170  	return c.msgio.outgoing.MsgChan
   171  }
   172  
   173  // ID returns the ID of a given Conn.
   174  func ID(c Conn) string {
   175  	l := fmt.Sprintf("%s/%s", c.LocalMultiaddr(), c.LocalPeer().ID())
   176  	r := fmt.Sprintf("%s/%s", c.RemoteMultiaddr(), c.RemotePeer().ID())
   177  	lh := u.Hash([]byte(l))
   178  	rh := u.Hash([]byte(r))
   179  	ch := u.XOR(lh, rh)
   180  	return u.Key(ch).Pretty()
   181  }
   182  
   183  // String returns the user-friendly String representation of a conn
   184  func String(c Conn, typ string) string {
   185  	return fmt.Sprintf("%s (%s) <-- %s --> (%s) %s",
   186  		c.LocalPeer(), c.LocalMultiaddr(), typ, c.RemoteMultiaddr(), c.RemotePeer())
   187  }