|
package p2p
|
|
|
|
import (
|
|
"sync"
|
|
|
|
"github.com/gogo/protobuf/proto"
|
|
)
|
|
|
|
// ChannelID is an arbitrary channel ID.
|
|
type ChannelID uint16
|
|
|
|
// Envelope specifies the message receiver and sender.
|
|
type Envelope struct {
|
|
From PeerID // Message sender, or empty for outbound messages.
|
|
To PeerID // Message receiver, or empty for inbound messages.
|
|
Broadcast bool // Send message to all connected peers, ignoring To.
|
|
Message proto.Message // Payload.
|
|
}
|
|
|
|
// Channel is a bidirectional channel for Protobuf message exchange with peers.
|
|
// A Channel is safe for concurrent use by multiple goroutines.
|
|
type Channel struct {
|
|
closeOnce sync.Once
|
|
|
|
// id defines the unique channel ID.
|
|
id ChannelID
|
|
|
|
// messageType specifies the type of messages exchanged via the channel, and
|
|
// is used e.g. for automatic unmarshaling.
|
|
messageType proto.Message
|
|
|
|
// inCh is a channel for receiving inbound messages. Envelope.From is always
|
|
// set.
|
|
inCh chan Envelope
|
|
|
|
// outCh is a channel for sending outbound messages. Envelope.To or Broadcast
|
|
// must be set, otherwise the message is discarded.
|
|
outCh chan Envelope
|
|
|
|
// errCh is a channel for reporting peer errors to the router, typically used
|
|
// when peers send an invalid or malignant message.
|
|
errCh chan PeerError
|
|
|
|
// doneCh is used to signal that a Channel is closed. A Channel is bi-directional
|
|
// and should be closed by the reactor, where as the router is responsible
|
|
// for explicitly closing the internal In channel.
|
|
doneCh chan struct{}
|
|
}
|
|
|
|
// NewChannel returns a reference to a new p2p Channel. It is the reactor's
|
|
// responsibility to close the Channel. After a channel is closed, the router may
|
|
// safely and explicitly close the internal In channel.
|
|
func NewChannel(id ChannelID, mType proto.Message, in, out chan Envelope, errCh chan PeerError) *Channel {
|
|
return &Channel{
|
|
id: id,
|
|
messageType: mType,
|
|
inCh: in,
|
|
outCh: out,
|
|
errCh: errCh,
|
|
doneCh: make(chan struct{}),
|
|
}
|
|
}
|
|
|
|
// ID returns the Channel's ID.
|
|
func (c *Channel) ID() ChannelID {
|
|
return c.id
|
|
}
|
|
|
|
// In returns a read-only inbound go channel. This go channel should be used by
|
|
// reactors to consume Envelopes sent from peers.
|
|
func (c *Channel) In() <-chan Envelope {
|
|
return c.inCh
|
|
}
|
|
|
|
// Out returns a write-only outbound go channel. This go channel should be used
|
|
// by reactors to route Envelopes to other peers.
|
|
func (c *Channel) Out() chan<- Envelope {
|
|
return c.outCh
|
|
}
|
|
|
|
// Error returns a write-only outbound go channel designated for peer errors only.
|
|
// This go channel should be used by reactors to send peer errors when consuming
|
|
// Envelopes sent from other peers.
|
|
func (c *Channel) Error() chan<- PeerError {
|
|
return c.errCh
|
|
}
|
|
|
|
// Close closes the outbound channel and marks the Channel as done. Internally,
|
|
// the outbound outCh and peer error errCh channels are closed. It is the reactor's
|
|
// responsibility to invoke Close. Any send on the Out or Error channel will
|
|
// panic after the Channel is closed.
|
|
//
|
|
// NOTE: After a Channel is closed, the router may safely assume it can no longer
|
|
// send on the internal inCh, however it should NEVER explicitly close it as
|
|
// that could result in panics by sending on a closed channel.
|
|
func (c *Channel) Close() {
|
|
c.closeOnce.Do(func() {
|
|
close(c.doneCh)
|
|
close(c.outCh)
|
|
close(c.errCh)
|
|
})
|
|
}
|
|
|
|
// Done returns the Channel's internal channel that should be used by a router
|
|
// to signal when it is safe to send on the internal inCh go channel.
|
|
func (c *Channel) Done() <-chan struct{} {
|
|
return c.doneCh
|
|
}
|
|
|
|
// Wrapper is a Protobuf message that can contain a variety of inner messages.
|
|
// If a Channel's message type implements Wrapper, the channel will
|
|
// automatically (un)wrap passed messages using the container type, such that
|
|
// the channel can transparently support multiple message types.
|
|
type Wrapper interface {
|
|
// Wrap will take a message and wrap it in this one.
|
|
Wrap(proto.Message) error
|
|
|
|
// Unwrap will unwrap the inner message contained in this message.
|
|
Unwrap() (proto.Message, error)
|
|
}
|