2018-03-09 15:55:25 +00:00
|
|
|
package network
|
|
|
|
|
|
|
|
import (
|
|
|
|
"net"
|
2018-03-10 12:04:06 +00:00
|
|
|
"sync"
|
2018-03-09 15:55:25 +00:00
|
|
|
|
|
|
|
"github.com/CityOfZion/neo-go/pkg/network/payload"
|
|
|
|
"github.com/CityOfZion/neo-go/pkg/util"
|
|
|
|
)
|
|
|
|
|
|
|
|
// TCPPeer represents a connected remote node in the
|
|
|
|
// network over TCP.
|
|
|
|
type TCPPeer struct {
|
2018-03-14 09:36:59 +00:00
|
|
|
// underlying TCP connection.
|
|
|
|
conn net.Conn
|
2018-03-09 15:55:25 +00:00
|
|
|
endpoint util.Endpoint
|
|
|
|
|
2018-03-14 09:36:59 +00:00
|
|
|
// The version of the peer.
|
2018-03-09 15:55:25 +00:00
|
|
|
version *payload.Version
|
|
|
|
|
2018-03-14 09:36:59 +00:00
|
|
|
done chan error
|
|
|
|
closed chan struct{}
|
|
|
|
disc chan error
|
2018-03-09 15:55:25 +00:00
|
|
|
|
2018-03-14 09:36:59 +00:00
|
|
|
wg sync.WaitGroup
|
2018-03-09 15:55:25 +00:00
|
|
|
}
|
|
|
|
|
2018-03-14 09:36:59 +00:00
|
|
|
func NewTCPPeer(conn net.Conn, proto chan protoTuple) *TCPPeer {
|
2018-03-09 15:55:25 +00:00
|
|
|
return &TCPPeer{
|
2018-03-14 09:36:59 +00:00
|
|
|
conn: conn,
|
|
|
|
done: make(chan error),
|
|
|
|
closed: make(chan struct{}),
|
|
|
|
disc: make(chan error),
|
|
|
|
endpoint: util.NewEndpoint(conn.RemoteAddr().String()),
|
2018-03-09 15:55:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-14 09:36:59 +00:00
|
|
|
// Send implements the Peer interface. This will encode the message
|
|
|
|
// to the underlying connection.
|
|
|
|
func (p *TCPPeer) Send(msg *Message) {
|
2018-04-09 16:58:09 +00:00
|
|
|
if err := msg.Encode(p.conn); err != nil {
|
2018-03-14 09:36:59 +00:00
|
|
|
select {
|
|
|
|
case p.disc <- err:
|
|
|
|
case <-p.closed:
|
|
|
|
}
|
|
|
|
}
|
2018-03-09 15:55:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Endpoint implements the Peer interface.
|
|
|
|
func (p *TCPPeer) Endpoint() util.Endpoint {
|
|
|
|
return p.endpoint
|
|
|
|
}
|
|
|
|
|
2018-03-14 09:36:59 +00:00
|
|
|
// Done implements the Peer interface and notifies
|
|
|
|
// all other resources operating on it that this peer
|
|
|
|
// is no longer running.
|
|
|
|
func (p *TCPPeer) Done() chan error {
|
2018-03-09 15:55:25 +00:00
|
|
|
return p.done
|
|
|
|
}
|
|
|
|
|
2018-03-14 09:36:59 +00:00
|
|
|
// Version implements the Peer interface.
|
|
|
|
func (p *TCPPeer) Version() *payload.Version {
|
|
|
|
return p.version
|
2018-03-10 12:04:06 +00:00
|
|
|
}
|
2018-03-09 15:55:25 +00:00
|
|
|
|
2018-03-14 09:36:59 +00:00
|
|
|
func (p *TCPPeer) readLoop(proto chan protoTuple, readErr chan error) {
|
2018-03-10 12:04:06 +00:00
|
|
|
defer p.wg.Done()
|
2018-03-09 15:55:25 +00:00
|
|
|
for {
|
2018-03-10 12:04:06 +00:00
|
|
|
select {
|
|
|
|
case <-p.closed:
|
|
|
|
return
|
|
|
|
default:
|
|
|
|
msg := &Message{}
|
2018-04-09 16:58:09 +00:00
|
|
|
if err := msg.Decode(p.conn); err != nil {
|
2018-03-14 09:36:59 +00:00
|
|
|
readErr <- err
|
2018-03-10 12:04:06 +00:00
|
|
|
return
|
|
|
|
}
|
2018-03-14 09:36:59 +00:00
|
|
|
p.handleMessage(msg, proto)
|
2018-03-09 15:55:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-14 09:36:59 +00:00
|
|
|
func (p *TCPPeer) handleMessage(msg *Message, proto chan protoTuple) {
|
|
|
|
switch payload := msg.Payload.(type) {
|
|
|
|
case *payload.Version:
|
|
|
|
p.version = payload
|
|
|
|
}
|
|
|
|
proto <- protoTuple{
|
|
|
|
msg: msg,
|
|
|
|
peer: p,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *TCPPeer) run(proto chan protoTuple) {
|
|
|
|
var (
|
|
|
|
readErr = make(chan error, 1)
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
p.wg.Add(1)
|
|
|
|
go p.readLoop(proto, readErr)
|
|
|
|
|
|
|
|
run:
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case err = <-p.disc:
|
|
|
|
break run
|
|
|
|
case err = <-readErr:
|
|
|
|
break run
|
2018-03-10 12:04:06 +00:00
|
|
|
}
|
2018-03-09 15:55:25 +00:00
|
|
|
}
|
2018-03-14 09:36:59 +00:00
|
|
|
|
|
|
|
// If the peer has not started the protocol with the server
|
|
|
|
// there will be noone reading from this channel.
|
|
|
|
select {
|
|
|
|
case p.done <- err:
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
|
|
|
|
close(p.closed)
|
|
|
|
p.conn.Close()
|
|
|
|
p.wg.Wait()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Disconnect implements the Peer interface.
|
|
|
|
func (p *TCPPeer) Disconnect(reason error) {
|
|
|
|
p.disc <- reason
|
2018-03-09 15:55:25 +00:00
|
|
|
}
|