p2p: fixes for actual connections

The unit test hooks were turned on 'in production'.
This commit is contained in:
Felix Lange 2015-02-07 00:13:22 +01:00
parent 8564eb9f7e
commit e34d134102
6 changed files with 38 additions and 29 deletions

View File

@ -174,10 +174,10 @@ func (rw *frameRW) ReadMsg() (msg Msg, err error) {
// read magic and payload size // read magic and payload size
start := make([]byte, 8) start := make([]byte, 8)
if _, err = io.ReadFull(rw.bufconn, start); err != nil { if _, err = io.ReadFull(rw.bufconn, start); err != nil {
return msg, newPeerError(errRead, "%v", err) return msg, err
} }
if !bytes.HasPrefix(start, magicToken) { if !bytes.HasPrefix(start, magicToken) {
return msg, newPeerError(errMagicTokenMismatch, "got %x, want %x", start[:4], magicToken) return msg, fmt.Errorf("bad magic token %x", start[:4], magicToken)
} }
size := binary.BigEndian.Uint32(start[4:]) size := binary.BigEndian.Uint32(start[4:])

View File

@ -1,6 +1,7 @@
package p2p package p2p
import ( import (
"errors"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
@ -71,7 +72,8 @@ type Peer struct {
runlock sync.RWMutex // protects running runlock sync.RWMutex // protects running
running map[string]*proto running map[string]*proto
protocolHandshakeEnabled bool // disables protocol handshake, for testing
noHandshake bool
protoWG sync.WaitGroup protoWG sync.WaitGroup
protoErr chan error protoErr chan error
@ -134,11 +136,11 @@ func (p *Peer) Disconnect(reason DiscReason) {
// String implements fmt.Stringer. // String implements fmt.Stringer.
func (p *Peer) String() string { func (p *Peer) String() string {
return fmt.Sprintf("Peer %.8x %v", p.remoteID, p.RemoteAddr()) return fmt.Sprintf("Peer %.8x %v", p.remoteID[:], p.RemoteAddr())
} }
func newPeer(conn net.Conn, protocols []Protocol, ourName string, ourID, remoteID *discover.NodeID) *Peer { func newPeer(conn net.Conn, protocols []Protocol, ourName string, ourID, remoteID *discover.NodeID) *Peer {
logtag := fmt.Sprintf("Peer %.8x %v", remoteID, conn.RemoteAddr()) logtag := fmt.Sprintf("Peer %.8x %v", remoteID[:], conn.RemoteAddr())
return &Peer{ return &Peer{
Logger: logger.NewLogger(logtag), Logger: logger.NewLogger(logtag),
rw: newFrameRW(conn, msgWriteTimeout), rw: newFrameRW(conn, msgWriteTimeout),
@ -164,33 +166,35 @@ func (p *Peer) run() DiscReason {
var readErr = make(chan error, 1) var readErr = make(chan error, 1)
defer p.closeProtocols() defer p.closeProtocols()
defer close(p.closed) defer close(p.closed)
defer p.rw.Close()
// start the read loop
go func() { readErr <- p.readLoop() }() go func() { readErr <- p.readLoop() }()
if p.protocolHandshakeEnabled { if !p.noHandshake {
if err := writeProtocolHandshake(p.rw, p.ourName, *p.ourID, p.protocols); err != nil { if err := writeProtocolHandshake(p.rw, p.ourName, *p.ourID, p.protocols); err != nil {
p.DebugDetailf("Protocol handshake error: %v\n", err) p.DebugDetailf("Protocol handshake error: %v\n", err)
p.rw.Close()
return DiscProtocolError return DiscProtocolError
} }
} }
// wait for an error or disconnect // Wait for an error or disconnect.
var reason DiscReason var reason DiscReason
select { select {
case err := <-readErr: case err := <-readErr:
// We rely on protocols to abort if there is a write error. It // We rely on protocols to abort if there is a write error. It
// might be more robust to handle them here as well. // might be more robust to handle them here as well.
p.DebugDetailf("Read error: %v\n", err) p.DebugDetailf("Read error: %v\n", err)
reason = DiscNetworkError p.rw.Close()
return DiscNetworkError
case err := <-p.protoErr: case err := <-p.protoErr:
reason = discReasonForError(err) reason = discReasonForError(err)
case reason = <-p.disc: case reason = <-p.disc:
} }
if reason != DiscNetworkError {
p.politeDisconnect(reason) p.politeDisconnect(reason)
}
// Wait for readLoop. It will end because conn is now closed.
<-readErr
p.Debugf("Disconnected: %v\n", reason) p.Debugf("Disconnected: %v\n", reason)
return reason return reason
} }
@ -198,9 +202,9 @@ func (p *Peer) run() DiscReason {
func (p *Peer) politeDisconnect(reason DiscReason) { func (p *Peer) politeDisconnect(reason DiscReason) {
done := make(chan struct{}) done := make(chan struct{})
go func() { go func() {
// send reason
EncodeMsg(p.rw, discMsg, uint(reason)) EncodeMsg(p.rw, discMsg, uint(reason))
// discard any data that might arrive // Wait for the other side to close the connection.
// Discard any data that they send until then.
io.Copy(ioutil.Discard, p.rw) io.Copy(ioutil.Discard, p.rw)
close(done) close(done)
}() }()
@ -208,10 +212,11 @@ func (p *Peer) politeDisconnect(reason DiscReason) {
case <-done: case <-done:
case <-time.After(disconnectGracePeriod): case <-time.After(disconnectGracePeriod):
} }
p.rw.Close()
} }
func (p *Peer) readLoop() error { func (p *Peer) readLoop() error {
if p.protocolHandshakeEnabled { if !p.noHandshake {
if err := readProtocolHandshake(p, p.rw); err != nil { if err := readProtocolHandshake(p, p.rw); err != nil {
return err return err
} }
@ -264,7 +269,7 @@ func readProtocolHandshake(p *Peer, rw MsgReadWriter) error {
return newPeerError(errProtocolBreach, "expected handshake, got %x", msg.Code) return newPeerError(errProtocolBreach, "expected handshake, got %x", msg.Code)
} }
if msg.Size > baseProtocolMaxMsgSize { if msg.Size > baseProtocolMaxMsgSize {
return newPeerError(errMisc, "message too big") return newPeerError(errInvalidMsg, "message too big")
} }
var hs handshake var hs handshake
if err := msg.Decode(&hs); err != nil { if err := msg.Decode(&hs); err != nil {
@ -326,7 +331,7 @@ func (p *Peer) startProto(offset uint64, impl Protocol) *proto {
err := impl.Run(p, rw) err := impl.Run(p, rw)
if err == nil { if err == nil {
p.DebugDetailf("Protocol %s/%d returned\n", impl.Name, impl.Version) p.DebugDetailf("Protocol %s/%d returned\n", impl.Name, impl.Version)
err = newPeerError(errMisc, "protocol returned") err = errors.New("protocol returned")
} else { } else {
p.DebugDetailf("Protocol %s/%d error: %v\n", impl.Name, impl.Version, err) p.DebugDetailf("Protocol %s/%d error: %v\n", impl.Name, impl.Version, err)
} }

View File

@ -123,7 +123,7 @@ func discReasonForError(err error) DiscReason {
return DiscProtocolError return DiscProtocolError
case errPingTimeout: case errPingTimeout:
return DiscReadTimeout return DiscReadTimeout
case errRead, errWrite, errMisc: case errRead, errWrite:
return DiscNetworkError return DiscNetworkError
default: default:
return DiscSubprotocolError return DiscSubprotocolError

View File

@ -30,10 +30,10 @@ var discard = Protocol{
}, },
} }
func testPeer(handshake bool, protos []Protocol) (*frameRW, *Peer, <-chan DiscReason) { func testPeer(noHandshake bool, protos []Protocol) (*frameRW, *Peer, <-chan DiscReason) {
conn1, conn2 := net.Pipe() conn1, conn2 := net.Pipe()
peer := newPeer(conn1, protos, "name", &discover.NodeID{}, &discover.NodeID{}) peer := newPeer(conn1, protos, "name", &discover.NodeID{}, &discover.NodeID{})
peer.protocolHandshakeEnabled = handshake peer.noHandshake = noHandshake
errc := make(chan DiscReason, 1) errc := make(chan DiscReason, 1)
go func() { errc <- peer.run() }() go func() { errc <- peer.run() }()
return newFrameRW(conn2, msgWriteTimeout), peer, errc return newFrameRW(conn2, msgWriteTimeout), peer, errc
@ -61,7 +61,7 @@ func TestPeerProtoReadMsg(t *testing.T) {
}, },
} }
rw, peer, errc := testPeer(false, []Protocol{proto}) rw, peer, errc := testPeer(true, []Protocol{proto})
defer rw.Close() defer rw.Close()
peer.startSubprotocols([]Cap{proto.cap()}) peer.startSubprotocols([]Cap{proto.cap()})
@ -100,7 +100,7 @@ func TestPeerProtoReadLargeMsg(t *testing.T) {
}, },
} }
rw, peer, errc := testPeer(false, []Protocol{proto}) rw, peer, errc := testPeer(true, []Protocol{proto})
defer rw.Close() defer rw.Close()
peer.startSubprotocols([]Cap{proto.cap()}) peer.startSubprotocols([]Cap{proto.cap()})
@ -130,7 +130,7 @@ func TestPeerProtoEncodeMsg(t *testing.T) {
return nil return nil
}, },
} }
rw, peer, _ := testPeer(false, []Protocol{proto}) rw, peer, _ := testPeer(true, []Protocol{proto})
defer rw.Close() defer rw.Close()
peer.startSubprotocols([]Cap{proto.cap()}) peer.startSubprotocols([]Cap{proto.cap()})
@ -142,7 +142,7 @@ func TestPeerProtoEncodeMsg(t *testing.T) {
func TestPeerWriteForBroadcast(t *testing.T) { func TestPeerWriteForBroadcast(t *testing.T) {
defer testlog(t).detach() defer testlog(t).detach()
rw, peer, peerErr := testPeer(false, []Protocol{discard}) rw, peer, peerErr := testPeer(true, []Protocol{discard})
defer rw.Close() defer rw.Close()
peer.startSubprotocols([]Cap{discard.cap()}) peer.startSubprotocols([]Cap{discard.cap()})
@ -179,7 +179,7 @@ func TestPeerWriteForBroadcast(t *testing.T) {
func TestPeerPing(t *testing.T) { func TestPeerPing(t *testing.T) {
defer testlog(t).detach() defer testlog(t).detach()
rw, _, _ := testPeer(false, nil) rw, _, _ := testPeer(true, nil)
defer rw.Close() defer rw.Close()
if err := EncodeMsg(rw, pingMsg); err != nil { if err := EncodeMsg(rw, pingMsg); err != nil {
t.Fatal(err) t.Fatal(err)
@ -192,7 +192,7 @@ func TestPeerPing(t *testing.T) {
func TestPeerDisconnect(t *testing.T) { func TestPeerDisconnect(t *testing.T) {
defer testlog(t).detach() defer testlog(t).detach()
rw, _, disc := testPeer(false, nil) rw, _, disc := testPeer(true, nil)
defer rw.Close() defer rw.Close()
if err := EncodeMsg(rw, discMsg, DiscQuitting); err != nil { if err := EncodeMsg(rw, discMsg, DiscQuitting); err != nil {
t.Fatal(err) t.Fatal(err)
@ -233,7 +233,7 @@ func TestPeerHandshake(t *testing.T) {
{Name: "c", Version: 3, Length: 1, Run: run}, {Name: "c", Version: 3, Length: 1, Run: run},
{Name: "d", Version: 4, Length: 1, Run: run}, {Name: "d", Version: 4, Length: 1, Run: run},
} }
rw, p, disc := testPeer(true, protocols) rw, p, disc := testPeer(false, protocols)
p.remoteID = remote.ourID p.remoteID = remote.ourID
defer rw.Close() defer rw.Close()
@ -269,6 +269,7 @@ func TestPeerHandshake(t *testing.T) {
} }
close(stop) close(stop)
expectMsg(rw, discMsg, nil)
t.Logf("disc reason: %v", <-disc) t.Logf("disc reason: %v", <-disc)
} }

View File

@ -408,7 +408,9 @@ func (srv *Server) startPeer(conn net.Conn, dest *discover.Node) {
return return
} }
if srv.newPeerHook != nil {
srv.newPeerHook(p) srv.newPeerHook(p)
}
p.run() p.run()
srv.removePeer(p) srv.removePeer(p)
} }

View File

@ -118,6 +118,7 @@ func TestServerBroadcast(t *testing.T) {
srv := startTestServer(t, func(p *Peer) { srv := startTestServer(t, func(p *Peer) {
p.protocols = []Protocol{discard} p.protocols = []Protocol{discard}
p.startSubprotocols([]Cap{discard.cap()}) p.startSubprotocols([]Cap{discard.cap()})
p.noHandshake = true
connected.Done() connected.Done()
}) })
defer srv.Stop() defer srv.Stop()