| 
									
										
										
										
											2014-10-23 16:57:54 +01:00
										 |  |  | package p2p | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2015-02-07 00:13:22 +01:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2014-10-23 16:57:54 +01:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2014-11-21 21:48:49 +01:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2014-10-23 16:57:54 +01:00
										 |  |  | 	"net" | 
					
						
							| 
									
										
										
										
											2014-11-21 21:48:49 +01:00
										 |  |  | 	"sort" | 
					
						
							|  |  |  | 	"sync" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/logger" | 
					
						
							| 
									
										
										
										
											2015-05-06 23:19:14 +02:00
										 |  |  | 	"github.com/ethereum/go-ethereum/logger/glog" | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | 	"github.com/ethereum/go-ethereum/p2p/discover" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/rlp" | 
					
						
							| 
									
										
										
										
											2014-10-23 16:57:54 +01:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | const ( | 
					
						
							| 
									
										
										
										
											2015-04-27 13:42:30 +02:00
										 |  |  | 	baseProtocolVersion    = 4 | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | 	baseProtocolLength     = uint64(16) | 
					
						
							| 
									
										
										
										
											2015-05-18 01:14:35 +02:00
										 |  |  | 	baseProtocolMaxMsgSize = 2 * 1024 | 
					
						
							| 
									
										
										
										
											2015-02-13 14:44:00 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-13 17:34:08 +02:00
										 |  |  | 	pingInterval = 15 * time.Second | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | ) | 
					
						
							| 
									
										
										
										
											2014-11-21 21:48:49 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | const ( | 
					
						
							|  |  |  | 	// devp2p message codes | 
					
						
							|  |  |  | 	handshakeMsg = 0x00 | 
					
						
							|  |  |  | 	discMsg      = 0x01 | 
					
						
							|  |  |  | 	pingMsg      = 0x02 | 
					
						
							|  |  |  | 	pongMsg      = 0x03 | 
					
						
							|  |  |  | 	getPeersMsg  = 0x04 | 
					
						
							|  |  |  | 	peersMsg     = 0x05 | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2014-11-21 21:48:49 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-16 00:38:28 +02:00
										 |  |  | // protoHandshake is the RLP structure of the protocol handshake. | 
					
						
							|  |  |  | type protoHandshake struct { | 
					
						
							|  |  |  | 	Version    uint64 | 
					
						
							|  |  |  | 	Name       string | 
					
						
							|  |  |  | 	Caps       []Cap | 
					
						
							|  |  |  | 	ListenPort uint64 | 
					
						
							|  |  |  | 	ID         discover.NodeID | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | // Peer represents a connected remote node. | 
					
						
							| 
									
										
										
										
											2014-10-23 16:57:54 +01:00
										 |  |  | type Peer struct { | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | 	rw      *conn | 
					
						
							|  |  |  | 	running map[string]*protoRW | 
					
						
							| 
									
										
										
										
											2014-11-21 21:48:49 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-08 17:37:11 +02:00
										 |  |  | 	wg       sync.WaitGroup | 
					
						
							| 
									
										
										
										
											2014-11-21 21:48:49 +01:00
										 |  |  | 	protoErr chan error | 
					
						
							|  |  |  | 	closed   chan struct{} | 
					
						
							|  |  |  | 	disc     chan DiscReason | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewPeer returns a peer for testing purposes. | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | func NewPeer(id discover.NodeID, name string, caps []Cap) *Peer { | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | 	pipe, _ := net.Pipe() | 
					
						
							| 
									
										
										
										
											2015-05-16 00:38:28 +02:00
										 |  |  | 	conn := &conn{fd: pipe, transport: nil, id: id, caps: caps, name: name} | 
					
						
							|  |  |  | 	peer := newPeer(conn, nil) | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | 	close(peer.closed) // ensures Disconnect doesn't block | 
					
						
							| 
									
										
										
										
											2014-10-23 16:57:54 +01:00
										 |  |  | 	return peer | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | // ID returns the node's public key. | 
					
						
							|  |  |  | func (p *Peer) ID() discover.NodeID { | 
					
						
							| 
									
										
										
										
											2015-05-16 00:38:28 +02:00
										 |  |  | 	return p.rw.id | 
					
						
							| 
									
										
										
										
											2014-11-21 21:48:49 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | // Name returns the node name that the remote node advertised. | 
					
						
							|  |  |  | func (p *Peer) Name() string { | 
					
						
							| 
									
										
										
										
											2015-05-16 00:38:28 +02:00
										 |  |  | 	return p.rw.name | 
					
						
							| 
									
										
										
										
											2015-01-18 07:59:54 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-11-21 21:48:49 +01:00
										 |  |  | // Caps returns the capabilities (supported subprotocols) of the remote peer. | 
					
						
							|  |  |  | func (p *Peer) Caps() []Cap { | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | 	// TODO: maybe return copy | 
					
						
							| 
									
										
										
										
											2015-05-16 00:38:28 +02:00
										 |  |  | 	return p.rw.caps | 
					
						
							| 
									
										
										
										
											2014-11-21 21:48:49 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // RemoteAddr returns the remote address of the network connection. | 
					
						
							|  |  |  | func (p *Peer) RemoteAddr() net.Addr { | 
					
						
							| 
									
										
										
										
											2015-05-16 00:38:28 +02:00
										 |  |  | 	return p.rw.fd.RemoteAddr() | 
					
						
							| 
									
										
										
										
											2014-11-21 21:48:49 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // LocalAddr returns the local address of the network connection. | 
					
						
							|  |  |  | func (p *Peer) LocalAddr() net.Addr { | 
					
						
							| 
									
										
										
										
											2015-05-16 00:38:28 +02:00
										 |  |  | 	return p.rw.fd.LocalAddr() | 
					
						
							| 
									
										
										
										
											2014-11-21 21:48:49 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Disconnect terminates the peer connection with the given reason. | 
					
						
							|  |  |  | // It returns immediately and does not wait until the connection is closed. | 
					
						
							|  |  |  | func (p *Peer) Disconnect(reason DiscReason) { | 
					
						
							|  |  |  | 	select { | 
					
						
							|  |  |  | 	case p.disc <- reason: | 
					
						
							|  |  |  | 	case <-p.closed: | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // String implements fmt.Stringer. | 
					
						
							|  |  |  | func (p *Peer) String() string { | 
					
						
							| 
									
										
										
										
											2015-05-16 00:38:28 +02:00
										 |  |  | 	return fmt.Sprintf("Peer %x %v", p.rw.id[:8], p.RemoteAddr()) | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-16 00:38:28 +02:00
										 |  |  | func newPeer(conn *conn, protocols []Protocol) *Peer { | 
					
						
							|  |  |  | 	protomap := matchProtocols(protocols, conn.caps, conn) | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | 	p := &Peer{ | 
					
						
							|  |  |  | 		rw:       conn, | 
					
						
							| 
									
										
										
										
											2015-04-08 17:37:11 +02:00
										 |  |  | 		running:  protomap, | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | 		disc:     make(chan DiscReason), | 
					
						
							| 
									
										
										
										
											2015-04-08 17:37:11 +02:00
										 |  |  | 		protoErr: make(chan error, len(protomap)+1), // protocols + pingLoop | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | 		closed:   make(chan struct{}), | 
					
						
							| 
									
										
										
										
											2014-10-23 16:57:54 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | 	return p | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2014-11-21 21:48:49 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | func (p *Peer) run() DiscReason { | 
					
						
							| 
									
										
										
										
											2015-06-15 13:42:44 +02:00
										 |  |  | 	var ( | 
					
						
							|  |  |  | 		writeStart = make(chan struct{}, 1) | 
					
						
							|  |  |  | 		writeErr   = make(chan error, 1) | 
					
						
							|  |  |  | 		readErr    = make(chan error, 1) | 
					
						
							|  |  |  | 		reason     DiscReason | 
					
						
							|  |  |  | 		requested  bool | 
					
						
							|  |  |  | 	) | 
					
						
							| 
									
										
										
										
											2015-04-08 17:37:11 +02:00
										 |  |  | 	p.wg.Add(2) | 
					
						
							|  |  |  | 	go p.readLoop(readErr) | 
					
						
							|  |  |  | 	go p.pingLoop() | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-15 13:42:44 +02:00
										 |  |  | 	// Start all protocol handlers. | 
					
						
							|  |  |  | 	writeStart <- struct{}{} | 
					
						
							|  |  |  | 	p.startProtocols(writeStart, writeErr) | 
					
						
							| 
									
										
										
										
											2015-02-19 16:53:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-07 00:13:22 +01:00
										 |  |  | 	// Wait for an error or disconnect. | 
					
						
							| 
									
										
										
										
											2015-06-15 13:42:44 +02:00
										 |  |  | loop: | 
					
						
							|  |  |  | 	for { | 
					
						
							|  |  |  | 		select { | 
					
						
							|  |  |  | 		case err := <-writeErr: | 
					
						
							|  |  |  | 			// A write finished. Allow the next write to start if | 
					
						
							|  |  |  | 			// there was no error. | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2015-06-15 14:00:50 +02:00
										 |  |  | 				glog.V(logger.Detail).Infof("%v: write error: %v\n", p, err) | 
					
						
							| 
									
										
										
										
											2015-06-15 13:42:44 +02:00
										 |  |  | 				reason = DiscNetworkError | 
					
						
							|  |  |  | 				break loop | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			writeStart <- struct{}{} | 
					
						
							|  |  |  | 		case err := <-readErr: | 
					
						
							|  |  |  | 			if r, ok := err.(DiscReason); ok { | 
					
						
							| 
									
										
										
										
											2015-06-15 14:00:50 +02:00
										 |  |  | 				glog.V(logger.Debug).Infof("%v: remote requested disconnect: %v\n", p, r) | 
					
						
							|  |  |  | 				requested = true | 
					
						
							| 
									
										
										
										
											2015-06-15 13:42:44 +02:00
										 |  |  | 				reason = r | 
					
						
							|  |  |  | 			} else { | 
					
						
							| 
									
										
										
										
											2015-06-15 14:00:50 +02:00
										 |  |  | 				glog.V(logger.Detail).Infof("%v: read error: %v\n", p, err) | 
					
						
							| 
									
										
										
										
											2015-06-15 13:42:44 +02:00
										 |  |  | 				reason = DiscNetworkError | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			break loop | 
					
						
							|  |  |  | 		case err := <-p.protoErr: | 
					
						
							|  |  |  | 			reason = discReasonForError(err) | 
					
						
							| 
									
										
										
										
											2015-06-15 14:00:50 +02:00
										 |  |  | 			glog.V(logger.Debug).Infof("%v: protocol error: %v (%v)\n", p, err, reason) | 
					
						
							| 
									
										
										
										
											2015-06-15 13:42:44 +02:00
										 |  |  | 			break loop | 
					
						
							|  |  |  | 		case reason = <-p.disc: | 
					
						
							| 
									
										
										
										
											2015-06-15 14:00:50 +02:00
										 |  |  | 			glog.V(logger.Debug).Infof("%v: locally requested disconnect: %v\n", p, reason) | 
					
						
							| 
									
										
										
										
											2015-06-15 13:42:44 +02:00
										 |  |  | 			break loop | 
					
						
							| 
									
										
										
										
											2015-02-19 16:53:52 +01:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2014-11-21 21:48:49 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-06-15 13:42:44 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-08 17:37:11 +02:00
										 |  |  | 	close(p.closed) | 
					
						
							| 
									
										
										
										
											2015-05-16 00:38:28 +02:00
										 |  |  | 	p.rw.close(reason) | 
					
						
							| 
									
										
										
										
											2015-04-08 17:37:11 +02:00
										 |  |  | 	p.wg.Wait() | 
					
						
							| 
									
										
										
										
											2015-05-16 00:38:28 +02:00
										 |  |  | 	if requested { | 
					
						
							|  |  |  | 		reason = DiscRequested | 
					
						
							| 
									
										
										
										
											2014-11-21 21:48:49 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-05-16 00:38:28 +02:00
										 |  |  | 	return reason | 
					
						
							| 
									
										
										
										
											2014-11-21 21:48:49 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-08 17:37:11 +02:00
										 |  |  | func (p *Peer) pingLoop() { | 
					
						
							|  |  |  | 	ping := time.NewTicker(pingInterval) | 
					
						
							|  |  |  | 	defer p.wg.Done() | 
					
						
							|  |  |  | 	defer ping.Stop() | 
					
						
							|  |  |  | 	for { | 
					
						
							|  |  |  | 		select { | 
					
						
							|  |  |  | 		case <-ping.C: | 
					
						
							|  |  |  | 			if err := SendItems(p.rw, pingMsg); err != nil { | 
					
						
							|  |  |  | 				p.protoErr <- err | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		case <-p.closed: | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (p *Peer) readLoop(errc chan<- error) { | 
					
						
							|  |  |  | 	defer p.wg.Done() | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | 	for { | 
					
						
							|  |  |  | 		msg, err := p.rw.ReadMsg() | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2015-04-08 17:37:11 +02:00
										 |  |  | 			errc <- err | 
					
						
							|  |  |  | 			return | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-04-29 22:49:58 +02:00
										 |  |  | 		msg.ReceivedAt = time.Now() | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | 		if err = p.handle(msg); err != nil { | 
					
						
							| 
									
										
										
										
											2015-04-08 17:37:11 +02:00
										 |  |  | 			errc <- err | 
					
						
							|  |  |  | 			return | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-11-21 21:48:49 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | func (p *Peer) handle(msg Msg) error { | 
					
						
							|  |  |  | 	switch { | 
					
						
							|  |  |  | 	case msg.Code == pingMsg: | 
					
						
							|  |  |  | 		msg.Discard() | 
					
						
							| 
									
										
										
										
											2015-03-19 15:11:02 +01:00
										 |  |  | 		go SendItems(p.rw, pongMsg) | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | 	case msg.Code == discMsg: | 
					
						
							| 
									
										
										
										
											2015-03-04 12:03:43 +01:00
										 |  |  | 		var reason [1]DiscReason | 
					
						
							| 
									
										
										
										
											2015-04-08 17:37:11 +02:00
										 |  |  | 		// This is the last message. We don't need to discard or | 
					
						
							|  |  |  | 		// check errors because, the connection will be closed after it. | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | 		rlp.Decode(msg.Payload, &reason) | 
					
						
							| 
									
										
										
										
											2015-05-14 03:04:04 +02:00
										 |  |  | 		return reason[0] | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | 	case msg.Code < baseProtocolLength: | 
					
						
							|  |  |  | 		// ignore other base protocol messages | 
					
						
							|  |  |  | 		return msg.Discard() | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		// it's a subprotocol message | 
					
						
							|  |  |  | 		proto, err := p.getProto(msg.Code) | 
					
						
							| 
									
										
										
										
											2014-11-21 21:48:49 +01:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | 			return fmt.Errorf("msg code out of range: %v", msg.Code) | 
					
						
							| 
									
										
										
										
											2014-11-21 21:48:49 +01:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-04-08 17:37:11 +02:00
										 |  |  | 		select { | 
					
						
							|  |  |  | 		case proto.in <- msg: | 
					
						
							|  |  |  | 			return nil | 
					
						
							|  |  |  | 		case <-p.closed: | 
					
						
							|  |  |  | 			return io.EOF | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2014-11-21 21:48:49 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-02-05 03:07:58 +01:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2014-11-21 21:48:49 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-08 16:09:38 +02:00
										 |  |  | func countMatchingProtocols(protocols []Protocol, caps []Cap) int { | 
					
						
							|  |  |  | 	n := 0 | 
					
						
							|  |  |  | 	for _, cap := range caps { | 
					
						
							|  |  |  | 		for _, proto := range protocols { | 
					
						
							|  |  |  | 			if proto.Name == cap.Name && proto.Version == cap.Version { | 
					
						
							|  |  |  | 				n++ | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return n | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | // matchProtocols creates structures for matching named subprotocols. | 
					
						
							|  |  |  | func matchProtocols(protocols []Protocol, caps []Cap, rw MsgReadWriter) map[string]*protoRW { | 
					
						
							| 
									
										
										
										
											2015-06-26 15:48:50 +03:00
										 |  |  | 	sort.Sort(capsByNameAndVersion(caps)) | 
					
						
							| 
									
										
										
										
											2014-11-21 21:48:49 +01:00
										 |  |  | 	offset := baseProtocolLength | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | 	result := make(map[string]*protoRW) | 
					
						
							| 
									
										
										
										
											2015-06-26 15:48:50 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-11-21 21:48:49 +01:00
										 |  |  | outer: | 
					
						
							|  |  |  | 	for _, cap := range caps { | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | 		for _, proto := range protocols { | 
					
						
							| 
									
										
										
										
											2015-06-26 15:48:50 +03:00
										 |  |  | 			if proto.Name == cap.Name && proto.Version == cap.Version { | 
					
						
							|  |  |  | 				// If an old protocol version matched, revert it | 
					
						
							|  |  |  | 				if old := result[cap.Name]; old != nil { | 
					
						
							|  |  |  | 					offset -= old.Length | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				// Assign the new match | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | 				result[cap.Name] = &protoRW{Protocol: proto, offset: offset, in: make(chan Msg), w: rw} | 
					
						
							| 
									
										
										
										
											2014-11-21 21:48:49 +01:00
										 |  |  | 				offset += proto.Length | 
					
						
							| 
									
										
										
										
											2015-06-26 15:48:50 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-11-21 21:48:49 +01:00
										 |  |  | 				continue outer | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | 	return result | 
					
						
							| 
									
										
										
										
											2014-11-21 21:48:49 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-15 13:42:44 +02:00
										 |  |  | func (p *Peer) startProtocols(writeStart <-chan struct{}, writeErr chan<- error) { | 
					
						
							| 
									
										
										
										
											2015-04-08 17:37:11 +02:00
										 |  |  | 	p.wg.Add(len(p.running)) | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | 	for _, proto := range p.running { | 
					
						
							|  |  |  | 		proto := proto | 
					
						
							| 
									
										
										
										
											2015-04-08 17:37:11 +02:00
										 |  |  | 		proto.closed = p.closed | 
					
						
							| 
									
										
										
										
											2015-06-15 13:42:44 +02:00
										 |  |  | 		proto.wstart = writeStart | 
					
						
							|  |  |  | 		proto.werr = writeErr | 
					
						
							| 
									
										
										
										
											2015-05-06 23:19:14 +02:00
										 |  |  | 		glog.V(logger.Detail).Infof("%v: Starting protocol %s/%d\n", p, proto.Name, proto.Version) | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | 		go func() { | 
					
						
							|  |  |  | 			err := proto.Run(p, proto) | 
					
						
							|  |  |  | 			if err == nil { | 
					
						
							| 
									
										
										
										
											2015-05-06 23:19:14 +02:00
										 |  |  | 				glog.V(logger.Detail).Infof("%v: Protocol %s/%d returned\n", p, proto.Name, proto.Version) | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | 				err = errors.New("protocol returned") | 
					
						
							| 
									
										
										
										
											2015-05-06 23:19:14 +02:00
										 |  |  | 			} else if err != io.EOF { | 
					
						
							| 
									
										
										
										
											2015-05-16 00:38:28 +02:00
										 |  |  | 				glog.V(logger.Detail).Infof("%v: Protocol %s/%d error: %v\n", p, proto.Name, proto.Version, err) | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2015-04-08 17:37:11 +02:00
										 |  |  | 			p.protoErr <- err | 
					
						
							|  |  |  | 			p.wg.Done() | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | 		}() | 
					
						
							| 
									
										
										
										
											2014-11-21 21:48:49 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // getProto finds the protocol responsible for handling | 
					
						
							|  |  |  | // the given message code. | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | func (p *Peer) getProto(code uint64) (*protoRW, error) { | 
					
						
							| 
									
										
										
										
											2014-11-21 21:48:49 +01:00
										 |  |  | 	for _, proto := range p.running { | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | 		if code >= proto.offset && code < proto.offset+proto.Length { | 
					
						
							| 
									
										
										
										
											2014-11-21 21:48:49 +01:00
										 |  |  | 			return proto, nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil, newPeerError(errInvalidMsgCode, "%d", code) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | type protoRW struct { | 
					
						
							|  |  |  | 	Protocol | 
					
						
							| 
									
										
										
										
											2015-06-15 13:42:44 +02:00
										 |  |  | 	in     chan Msg        // receices read messages | 
					
						
							|  |  |  | 	closed <-chan struct{} // receives when peer is shutting down | 
					
						
							|  |  |  | 	wstart <-chan struct{} // receives when write may start | 
					
						
							|  |  |  | 	werr   chan<- error    // for write results | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | 	offset uint64 | 
					
						
							|  |  |  | 	w      MsgWriter | 
					
						
							| 
									
										
										
										
											2014-11-21 21:48:49 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-15 13:42:44 +02:00
										 |  |  | func (rw *protoRW) WriteMsg(msg Msg) (err error) { | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | 	if msg.Code >= rw.Length { | 
					
						
							| 
									
										
										
										
											2014-11-21 21:48:49 +01:00
										 |  |  | 		return newPeerError(errInvalidMsgCode, "not handled") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	msg.Code += rw.offset | 
					
						
							| 
									
										
										
										
											2015-06-15 13:42:44 +02:00
										 |  |  | 	select { | 
					
						
							|  |  |  | 	case <-rw.wstart: | 
					
						
							|  |  |  | 		err = rw.w.WriteMsg(msg) | 
					
						
							|  |  |  | 		// Report write status back to Peer.run. It will initiate | 
					
						
							|  |  |  | 		// shutdown if the error is non-nil and unblock the next write | 
					
						
							|  |  |  | 		// otherwise. The calling protocol code should exit for errors | 
					
						
							|  |  |  | 		// as well but we don't want to rely on that. | 
					
						
							|  |  |  | 		rw.werr <- err | 
					
						
							|  |  |  | 	case <-rw.closed: | 
					
						
							|  |  |  | 		err = fmt.Errorf("shutting down") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return err | 
					
						
							| 
									
										
										
										
											2014-10-23 16:57:54 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-19 01:52:03 +01:00
										 |  |  | func (rw *protoRW) ReadMsg() (Msg, error) { | 
					
						
							| 
									
										
										
										
											2015-04-08 17:37:11 +02:00
										 |  |  | 	select { | 
					
						
							|  |  |  | 	case msg := <-rw.in: | 
					
						
							|  |  |  | 		msg.Code -= rw.offset | 
					
						
							|  |  |  | 		return msg, nil | 
					
						
							|  |  |  | 	case <-rw.closed: | 
					
						
							|  |  |  | 		return Msg{}, io.EOF | 
					
						
							| 
									
										
										
										
											2014-11-21 21:48:49 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-10-23 16:57:54 +01:00
										 |  |  | } |