Skip to content

Commit 93b6409

Browse files
authored
Merge pull request #287 from blinklabs-io/feat/muxer-multiple-protocol-instances
feat: allow running multiple instances of a protocol in muxer
2 parents d827282 + 1b08ef7 commit 93b6409

File tree

6 files changed

+65
-16
lines changed

6 files changed

+65
-16
lines changed

connection_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ func TestDialFailClose(t *testing.T) {
3838

3939
func TestDoubleClose(t *testing.T) {
4040
mockConn := ouroboros_mock.NewConnection(
41+
ouroboros_mock.ProtocolRoleClient,
4142
[]ouroboros_mock.ConversationEntry{
4243
ouroboros_mock.ConversationEntryHandshakeRequestGeneric,
4344
ouroboros_mock.ConversationEntryHandshakeResponse,

internal/test/ouroboros_mock/connection.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,16 @@ import (
2525
"github.com/blinklabs-io/gouroboros/muxer"
2626
)
2727

28+
// ProtocolRole is an enum of the protocol roles
29+
type ProtocolRole uint
30+
31+
// Protocol roles
32+
const (
33+
ProtocolRoleNone ProtocolRole = 0 // Default (invalid) protocol role
34+
ProtocolRoleClient ProtocolRole = 1 // Client protocol role
35+
ProtocolRoleServer ProtocolRole = 2 // Server protocol role
36+
)
37+
2838
// Connection mocks an Ouroboros connection
2939
type Connection struct {
3040
mockConn net.Conn
@@ -35,15 +45,20 @@ type Connection struct {
3545
}
3646

3747
// NewConnection returns a new Connection with the provided conversation entries
38-
func NewConnection(conversation []ConversationEntry) net.Conn {
48+
func NewConnection(protocolRole ProtocolRole, conversation []ConversationEntry) net.Conn {
3949
c := &Connection{
4050
conversation: conversation,
4151
}
4252
c.conn, c.mockConn = net.Pipe()
4353
// Start a muxer on the mocked side of the connection
4454
c.muxer = muxer.New(c.mockConn)
55+
// The muxer is for the opposite end of the connection, so we flip the protocol role
56+
muxerProtocolRole := muxer.ProtocolRoleResponder
57+
if protocolRole == ProtocolRoleServer {
58+
muxerProtocolRole = muxer.ProtocolRoleInitiator
59+
}
4560
// We use ProtocolUnknown to catch all inbound messages when no other protocols are registered
46-
_, c.muxerRecvChan, _ = c.muxer.RegisterProtocol(muxer.ProtocolUnknown)
61+
_, c.muxerRecvChan, _ = c.muxer.RegisterProtocol(muxer.ProtocolUnknown, muxerProtocolRole)
4762
c.muxer.Start()
4863
// Start async muxer error handler
4964
go func() {

internal/test/ouroboros_mock/mock_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
// Basic test of conversation mock functionality
2424
func TestBasic(t *testing.T) {
2525
mockConn := NewConnection(
26+
ProtocolRoleClient,
2627
[]ConversationEntry{
2728
ConversationEntryHandshakeRequestGeneric,
2829
ConversationEntryHandshakeResponse,

muxer/muxer.go

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,16 @@ const (
4242
DiffusionModeInitiatorAndResponder DiffusionMode = 3 // Initiator and responder (full duplex) mode
4343
)
4444

45+
// ProtocolRole is an enum of the protocol roles
46+
type ProtocolRole uint
47+
48+
// Protocol roles
49+
const (
50+
ProtocolRoleNone ProtocolRole = 0 // Default (invalid) protocol role
51+
ProtocolRoleInitiator ProtocolRole = 1 // Initiator (client) protocol role
52+
ProtocolRoleResponder ProtocolRole = 2 // Responder (server) protocol role
53+
)
54+
4555
// Muxer wraps a connection to allow running multiple mini-protocols over a single connection
4656
type Muxer struct {
4757
errorChan chan error
@@ -50,8 +60,8 @@ type Muxer struct {
5060
startChan chan bool
5161
doneChan chan bool
5262
waitGroup sync.WaitGroup
53-
protocolSenders map[uint16]chan *Segment
54-
protocolReceivers map[uint16]chan *Segment
63+
protocolSenders map[uint16]map[ProtocolRole]chan *Segment
64+
protocolReceivers map[uint16]map[ProtocolRole]chan *Segment
5565
diffusionMode DiffusionMode
5666
onceStart sync.Once
5767
onceStop sync.Once
@@ -64,8 +74,8 @@ func New(conn net.Conn) *Muxer {
6474
startChan: make(chan bool, 1),
6575
doneChan: make(chan bool),
6676
errorChan: make(chan error, 10),
67-
protocolSenders: make(map[uint16]chan *Segment),
68-
protocolReceivers: make(map[uint16]chan *Segment),
77+
protocolSenders: make(map[uint16]map[ProtocolRole]chan *Segment),
78+
protocolReceivers: make(map[uint16]map[ProtocolRole]chan *Segment),
6979
}
7080
m.waitGroup.Add(1)
7181
go m.readLoop()
@@ -95,8 +105,10 @@ func (m *Muxer) Stop() {
95105
m.waitGroup.Wait()
96106
// Close protocol receive channels
97107
// We rely on the individual mini-protocols to close the sender channel
98-
for _, recvChan := range m.protocolReceivers {
99-
close(recvChan)
108+
for _, protocolRoles := range m.protocolReceivers {
109+
for _, recvChan := range protocolRoles {
110+
close(recvChan)
111+
}
100112
}
101113
// Close ErrorChan to signify to consumer that we're shutting down
102114
close(m.errorChan)
@@ -124,13 +136,17 @@ func (m *Muxer) sendError(err error) {
124136

125137
// RegisterProtocol registers the provided protocol ID with the muxer. It returns a channel for sending,
126138
// a channel for receiving, and a channel to know when the muxer is shutting down
127-
func (m *Muxer) RegisterProtocol(protocolId uint16) (chan *Segment, chan *Segment, chan bool) {
139+
func (m *Muxer) RegisterProtocol(protocolId uint16, protocolRole ProtocolRole) (chan *Segment, chan *Segment, chan bool) {
128140
// Generate channels
129141
senderChan := make(chan *Segment, 10)
130142
receiverChan := make(chan *Segment, 10)
131143
// Record channels in protocol sender/receiver maps
132-
m.protocolSenders[protocolId] = senderChan
133-
m.protocolReceivers[protocolId] = receiverChan
144+
if _, ok := m.protocolSenders[protocolId]; !ok {
145+
m.protocolSenders[protocolId] = make(map[ProtocolRole]chan *Segment)
146+
m.protocolReceivers[protocolId] = make(map[ProtocolRole]chan *Segment)
147+
}
148+
m.protocolSenders[protocolId][protocolRole] = senderChan
149+
m.protocolReceivers[protocolId][protocolRole] = receiverChan
134150
// Start Goroutine to handle outbound messages
135151
m.waitGroup.Add(1)
136152
go func() {
@@ -216,15 +232,24 @@ func (m *Muxer) readLoop() {
216232
return
217233
}
218234
// Send message payload to proper receiver
219-
recvChan := m.protocolReceivers[msg.GetProtocolId()]
220-
if recvChan == nil {
235+
protocolRole := ProtocolRoleResponder
236+
if msg.IsResponse() {
237+
protocolRole = ProtocolRoleInitiator
238+
}
239+
protocolRoles, ok := m.protocolReceivers[msg.GetProtocolId()]
240+
if !ok {
221241
// Try the "unknown protocol" receiver if we didn't find an explicit one
222-
recvChan = m.protocolReceivers[ProtocolUnknown]
223-
if recvChan == nil {
242+
protocolRoles, ok = m.protocolReceivers[ProtocolUnknown]
243+
if !ok {
224244
m.sendError(fmt.Errorf("received message for unknown protocol ID %d", msg.GetProtocolId()))
225245
return
226246
}
227247
}
248+
recvChan := protocolRoles[protocolRole]
249+
if recvChan == nil {
250+
m.sendError(fmt.Errorf("received message for unknown protocol ID %d", msg.GetProtocolId()))
251+
return
252+
}
228253
if recvChan != nil {
229254
recvChan <- msg
230255
}

protocol/handshake/client_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424

2525
func TestBasicHandshake(t *testing.T) {
2626
mockConn := ouroboros_mock.NewConnection(
27+
ouroboros_mock.ProtocolRoleClient,
2728
[]ouroboros_mock.ConversationEntry{
2829
ouroboros_mock.ConversationEntryHandshakeRequestGeneric,
2930
ouroboros_mock.ConversationEntryHandshakeResponse,
@@ -53,6 +54,7 @@ func TestBasicHandshake(t *testing.T) {
5354

5455
func TestDoubleStart(t *testing.T) {
5556
mockConn := ouroboros_mock.NewConnection(
57+
ouroboros_mock.ProtocolRoleClient,
5658
[]ouroboros_mock.ConversationEntry{
5759
ouroboros_mock.ConversationEntryHandshakeRequestGeneric,
5860
ouroboros_mock.ConversationEntryHandshakeResponse,

protocol/protocol.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ const (
7575
// ProtocolRole is an enum of the protocol roles
7676
type ProtocolRole uint
7777

78+
// Protocol roles
7879
const (
7980
ProtocolRoleNone ProtocolRole = 0 // Default (invalid) protocol role
8081
ProtocolRoleClient ProtocolRole = 1 // Client protocol role
@@ -110,7 +111,11 @@ func New(config ProtocolConfig) *Protocol {
110111
func (p *Protocol) Start() {
111112
p.onceStart.Do(func() {
112113
// Register protocol with muxer
113-
p.muxerSendChan, p.muxerRecvChan, p.muxerDoneChan = p.config.Muxer.RegisterProtocol(p.config.ProtocolId)
114+
muxerProtocolRole := muxer.ProtocolRoleInitiator
115+
if p.config.Role == ProtocolRoleServer {
116+
muxerProtocolRole = muxer.ProtocolRoleResponder
117+
}
118+
p.muxerSendChan, p.muxerRecvChan, p.muxerDoneChan = p.config.Muxer.RegisterProtocol(p.config.ProtocolId, muxerProtocolRole)
114119
// Create buffers and channels
115120
p.recvBuffer = bytes.NewBuffer(nil)
116121
p.sendQueueChan = make(chan Message, 50)

0 commit comments

Comments
 (0)