-
Notifications
You must be signed in to change notification settings - Fork 111
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(dot/network): implement a handshake timeout #1615
Changes from 8 commits
d5f9107
74f4b27
fe8a681
5aac59a
e319ec3
2d25083
00ea2ac
73b991b
8c01bf1
bd23664
8ca0824
943b917
cf93011
6b405f5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,6 +19,7 @@ package network | |
import ( | ||
"errors" | ||
"sync" | ||
"time" | ||
"unsafe" | ||
|
||
libp2pnetwork "github.com/libp2p/go-libp2p-core/network" | ||
|
@@ -29,6 +30,7 @@ import ( | |
var errCannotValidateHandshake = errors.New("failed to validate handshake") | ||
|
||
const maxHandshakeSize = unsafe.Sizeof(BlockAnnounceHandshake{}) //nolint | ||
const handshakeTimeout = time.Second * 10 | ||
|
||
// Handshake is the interface all handshakes for notifications protocols must implement | ||
type Handshake interface { | ||
|
@@ -53,6 +55,11 @@ type ( | |
NotificationsMessageHandler = func(peer peer.ID, msg NotificationsMessage) (propagate bool, err error) | ||
) | ||
|
||
type handshakeReader struct { | ||
hs Handshake | ||
err error | ||
} | ||
|
||
type notificationsProtocol struct { | ||
protocolID protocol.ID | ||
getHandshake HandshakeGetter | ||
|
@@ -63,16 +70,17 @@ type notificationsProtocol struct { | |
} | ||
|
||
func (n *notificationsProtocol) getHandshakeData(pid peer.ID, inbound bool) (handshakeData, bool) { | ||
if inbound { | ||
data, has := n.inboundHandshakeData.Load(pid) | ||
if !has { | ||
return handshakeData{}, false | ||
} | ||
var ( | ||
data interface{} | ||
has bool | ||
) | ||
|
||
return data.(handshakeData), true | ||
if inbound { | ||
data, has = n.inboundHandshakeData.Load(pid) | ||
} else { | ||
data, has = n.outboundHandshakeData.Load(pid) | ||
} | ||
|
||
data, has := n.outboundHandshakeData.Load(pid) | ||
if !has { | ||
return handshakeData{}, false | ||
} | ||
|
@@ -174,7 +182,7 @@ func (s *Service) createNotificationsMessageHandler(info *notificationsProtocol, | |
return nil | ||
} | ||
|
||
logger.Debug("received message on notifications sub-protocol", "protocol", info.protocolID, | ||
logger.Trace("received message on notifications sub-protocol", "protocol", info.protocolID, | ||
"message", msg, | ||
"peer", stream.Conn().RemotePeer(), | ||
) | ||
|
@@ -226,14 +234,32 @@ func (s *Service) sendData(peer peer.ID, hs Handshake, info *notificationsProtoc | |
return | ||
} | ||
|
||
hs, err := s.readHandshake(stream, decodeBlockAnnounceHandshake) | ||
if err != nil { | ||
logger.Trace("failed to read handshake", "protocol", info.protocolID, "peer", peer, "error", err) | ||
hsTicker := time.NewTicker(handshakeTimeout) | ||
var hs Handshake | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can also do
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done! |
||
|
||
select { | ||
case <-hsTicker.C: | ||
hsTicker.Stop() | ||
|
||
logger.Warn("handshake timeout reached", "protocol", info.protocolID, "peer", peer) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. change to trace |
||
_ = stream.Close() | ||
info.outboundHandshakeData.Delete(peer) | ||
return | ||
} | ||
|
||
hsData.received = true | ||
case hsResponse := <-s.readHandshake(stream, decodeBlockAnnounceHandshake): | ||
hsTicker.Stop() | ||
|
||
if hsResponse.err != nil { | ||
logger.Trace("failed to read handshake", "protocol", info.protocolID, "peer", peer, "error", err) | ||
_ = stream.Close() | ||
|
||
info.outboundHandshakeData.Delete(peer) | ||
return | ||
} | ||
|
||
hs = hsResponse.hs | ||
hsData.received = true | ||
} | ||
|
||
err = info.handshakeValidator(peer, hs) | ||
if err != nil { | ||
|
@@ -294,19 +320,30 @@ func (s *Service) broadcastExcluding(info *notificationsProtocol, excluding peer | |
} | ||
} | ||
|
||
func (s *Service) readHandshake(stream libp2pnetwork.Stream, decoder HandshakeDecoder) (Handshake, error) { | ||
msgBytes := s.bufPool.get() | ||
defer s.bufPool.put(&msgBytes) | ||
func (s *Service) readHandshake(stream libp2pnetwork.Stream, decoder HandshakeDecoder) <-chan *handshakeReader { | ||
hsC := make(chan *handshakeReader, 1) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Curious why is this buffered channel? The code will still work if it's an unbuffered channel. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As the |
||
|
||
tot, err := readStream(stream, msgBytes[:]) | ||
if err != nil { | ||
return nil, err | ||
} | ||
go func() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should we close |
||
msgBytes := s.bufPool.get() | ||
defer func() { | ||
s.bufPool.put(&msgBytes) | ||
close(hsC) | ||
}() | ||
|
||
hs, err := decoder(msgBytes[:tot]) | ||
if err != nil { | ||
return nil, err | ||
} | ||
tot, err := readStream(stream, msgBytes[:]) | ||
if err != nil { | ||
hsC <- &handshakeReader{hs: nil, err: err} | ||
return | ||
} | ||
|
||
hs, err := decoder(msgBytes[:tot]) | ||
if err != nil { | ||
hsC <- &handshakeReader{hs: nil, err: err} | ||
return | ||
} | ||
|
||
hsC <- &handshakeReader{hs: hs, err: nil} | ||
}() | ||
|
||
return hs, nil | ||
return hsC | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
User timer instead of ticker.
Timers are for when you want to do something once in the future - tickers are for when you want to do something repeatedly at regular intervals