|
|
@ -28,11 +28,11 @@ type identifySnapshot struct { |
|
|
|
} |
|
|
|
|
|
|
|
type peerHandler struct { |
|
|
|
ids *IDService |
|
|
|
ids *IDService |
|
|
|
started bool |
|
|
|
|
|
|
|
ctx context.Context |
|
|
|
cancel context.CancelFunc |
|
|
|
wg sync.WaitGroup |
|
|
|
|
|
|
|
pid peer.ID |
|
|
|
|
|
|
@ -57,49 +57,62 @@ func newPeerHandler(pid peer.ID, ids *IDService) *peerHandler { |
|
|
|
return ph |
|
|
|
} |
|
|
|
|
|
|
|
func (ph *peerHandler) start() { |
|
|
|
ctx, cancel := context.WithCancel(context.Background()) |
|
|
|
ph.ctx = ctx |
|
|
|
// start starts a handler. This may only be called on a stopped handler, and must
|
|
|
|
// not be called concurrently with start/stop.
|
|
|
|
//
|
|
|
|
// This may _not_ be called on a _canceled_ handler. I.e., a handler where the
|
|
|
|
// passed in context expired.
|
|
|
|
func (ph *peerHandler) start(ctx context.Context, onExit func()) { |
|
|
|
if ph.cancel != nil { |
|
|
|
// If this happens, we have a bug. It means we tried to start
|
|
|
|
// before we stopped.
|
|
|
|
panic("peer handler already running") |
|
|
|
} |
|
|
|
|
|
|
|
ctx, cancel := context.WithCancel(ctx) |
|
|
|
ph.cancel = cancel |
|
|
|
|
|
|
|
ph.wg.Add(1) |
|
|
|
go ph.loop() |
|
|
|
go ph.loop(ctx, onExit) |
|
|
|
} |
|
|
|
|
|
|
|
func (ph *peerHandler) close() error { |
|
|
|
ph.cancel() |
|
|
|
ph.wg.Wait() |
|
|
|
// stop stops a handler. This may not be called concurrently with any
|
|
|
|
// other calls to stop/start.
|
|
|
|
func (ph *peerHandler) stop() error { |
|
|
|
if ph.cancel != nil { |
|
|
|
ph.cancel() |
|
|
|
ph.cancel = nil |
|
|
|
} |
|
|
|
return nil |
|
|
|
} |
|
|
|
|
|
|
|
// per peer loop for pushing updates
|
|
|
|
func (ph *peerHandler) loop() { |
|
|
|
defer ph.wg.Done() |
|
|
|
func (ph *peerHandler) loop(ctx context.Context, onExit func()) { |
|
|
|
defer onExit() |
|
|
|
|
|
|
|
for { |
|
|
|
select { |
|
|
|
// our listen addresses have changed, send an IDPush.
|
|
|
|
case <-ph.pushCh: |
|
|
|
if err := ph.sendPush(); err != nil { |
|
|
|
if err := ph.sendPush(ctx); err != nil { |
|
|
|
log.Warnw("failed to send Identify Push", "peer", ph.pid, "error", err) |
|
|
|
} |
|
|
|
|
|
|
|
case <-ph.deltaCh: |
|
|
|
if err := ph.sendDelta(); err != nil { |
|
|
|
if err := ph.sendDelta(ctx); err != nil { |
|
|
|
log.Warnw("failed to send Identify Delta", "peer", ph.pid, "error", err) |
|
|
|
} |
|
|
|
|
|
|
|
case <-ph.ctx.Done(): |
|
|
|
case <-ctx.Done(): |
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
func (ph *peerHandler) sendDelta() error { |
|
|
|
func (ph *peerHandler) sendDelta(ctx context.Context) error { |
|
|
|
// send a push if the peer does not support the Delta protocol.
|
|
|
|
if !ph.peerSupportsProtos([]string{IDDelta}) { |
|
|
|
if !ph.peerSupportsProtos(ctx, []string{IDDelta}) { |
|
|
|
log.Debugw("will send push as peer does not support delta", "peer", ph.pid) |
|
|
|
if err := ph.sendPush(); err != nil { |
|
|
|
if err := ph.sendPush(ctx); err != nil { |
|
|
|
return fmt.Errorf("failed to send push on delta message: %w", err) |
|
|
|
} |
|
|
|
return nil |
|
|
@ -111,7 +124,7 @@ func (ph *peerHandler) sendDelta() error { |
|
|
|
return nil |
|
|
|
} |
|
|
|
|
|
|
|
ds, err := ph.openStream([]string{IDDelta}) |
|
|
|
ds, err := ph.openStream(ctx, []string{IDDelta}) |
|
|
|
if err != nil { |
|
|
|
return fmt.Errorf("failed to open delta stream: %w", err) |
|
|
|
} |
|
|
@ -128,8 +141,8 @@ func (ph *peerHandler) sendDelta() error { |
|
|
|
return nil |
|
|
|
} |
|
|
|
|
|
|
|
func (ph *peerHandler) sendPush() error { |
|
|
|
dp, err := ph.openStream([]string{IDPush}) |
|
|
|
func (ph *peerHandler) sendPush(ctx context.Context) error { |
|
|
|
dp, err := ph.openStream(ctx, []string{IDPush}) |
|
|
|
if err == errProtocolNotSupported { |
|
|
|
log.Debugw("not sending push as peer does not support protocol", "peer", ph.pid) |
|
|
|
return nil |
|
|
@ -150,7 +163,7 @@ func (ph *peerHandler) sendPush() error { |
|
|
|
return nil |
|
|
|
} |
|
|
|
|
|
|
|
func (ph *peerHandler) openStream(protos []string) (network.Stream, error) { |
|
|
|
func (ph *peerHandler) openStream(ctx context.Context, protos []string) (network.Stream, error) { |
|
|
|
// wait for the other peer to send us an Identify response on "all" connections we have with it
|
|
|
|
// so we can look at it's supported protocols and avoid a multistream-select roundtrip to negotiate the protocol
|
|
|
|
// if we know for a fact that it dosen't support the protocol.
|
|
|
@ -158,17 +171,17 @@ func (ph *peerHandler) openStream(protos []string) (network.Stream, error) { |
|
|
|
for _, c := range conns { |
|
|
|
select { |
|
|
|
case <-ph.ids.IdentifyWait(c): |
|
|
|
case <-ph.ctx.Done(): |
|
|
|
return nil, ph.ctx.Err() |
|
|
|
case <-ctx.Done(): |
|
|
|
return nil, ctx.Err() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if !ph.peerSupportsProtos(protos) { |
|
|
|
if !ph.peerSupportsProtos(ctx, protos) { |
|
|
|
return nil, errProtocolNotSupported |
|
|
|
} |
|
|
|
|
|
|
|
// negotiate a stream without opening a new connection as we "should" already have a connection.
|
|
|
|
ctx, cancel := context.WithTimeout(ph.ctx, 30*time.Second) |
|
|
|
ctx, cancel := context.WithTimeout(ctx, 30*time.Second) |
|
|
|
defer cancel() |
|
|
|
ctx = network.WithNoDial(ctx, "should already have connection") |
|
|
|
|
|
|
@ -183,12 +196,12 @@ func (ph *peerHandler) openStream(protos []string) (network.Stream, error) { |
|
|
|
} |
|
|
|
|
|
|
|
// returns true if the peer supports atleast one of the given protocols
|
|
|
|
func (ph *peerHandler) peerSupportsProtos(protos []string) bool { |
|
|
|
func (ph *peerHandler) peerSupportsProtos(ctx context.Context, protos []string) bool { |
|
|
|
conns := ph.ids.Host.Network().ConnsToPeer(ph.pid) |
|
|
|
for _, c := range conns { |
|
|
|
select { |
|
|
|
case <-ph.ids.IdentifyWait(c): |
|
|
|
case <-ph.ctx.Done(): |
|
|
|
case <-ctx.Done(): |
|
|
|
return false |
|
|
|
} |
|
|
|
} |
|
|
|