Skip to content

Commit 289bdec

Browse files
authored
Merge pull request #147 from cloudstruct/feat/handle-muxer-error-during-handshake
feat: properly handle muxer error during handshake
2 parents 41f006b + 5eb3f5f commit 289bdec

File tree

2 files changed

+57
-16
lines changed

2 files changed

+57
-16
lines changed

cmd/go-ouroboros-network/localtxsubmission.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ func testLocalTxSubmission(f *globalFlags) {
4242
go func() {
4343
for {
4444
err := <-errorChan
45-
fmt.Printf("ERROR: %s\n", err)
45+
fmt.Printf("ERROR(async): %s\n", err)
4646
os.Exit(1)
4747
}
4848
}()

ouroboros.go

Lines changed: 56 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,23 @@ import (
1414
"github.com/cloudstruct/go-ouroboros-network/protocol/txsubmission"
1515
"io"
1616
"net"
17+
"sync"
1718
)
1819

1920
type Ouroboros struct {
20-
conn net.Conn
21-
networkMagic uint32
22-
server bool
23-
useNodeToNodeProto bool
24-
muxer *muxer.Muxer
25-
ErrorChan chan error
26-
protoErrorChan chan error
27-
sendKeepAlives bool
28-
delayMuxerStart bool
29-
fullDuplex bool
21+
conn net.Conn
22+
networkMagic uint32
23+
server bool
24+
useNodeToNodeProto bool
25+
muxer *muxer.Muxer
26+
ErrorChan chan error
27+
protoErrorChan chan error
28+
handshakeFinishedChan chan interface{}
29+
doneChan chan interface{}
30+
closeMutex sync.Mutex
31+
sendKeepAlives bool
32+
delayMuxerStart bool
33+
fullDuplex bool
3034
// Mini-protocols
3135
Handshake *handshake.Handshake
3236
ChainSync *chainsync.ChainSync
@@ -45,7 +49,9 @@ type Ouroboros struct {
4549

4650
func New(options ...OuroborosOptionFunc) (*Ouroboros, error) {
4751
o := &Ouroboros{
48-
protoErrorChan: make(chan error, 10),
52+
protoErrorChan: make(chan error, 10),
53+
handshakeFinishedChan: make(chan interface{}),
54+
doneChan: make(chan interface{}),
4955
}
5056
// Apply provided options functions
5157
for _, option := range options {
@@ -81,6 +87,18 @@ func (o *Ouroboros) Dial(proto string, address string) error {
8187
}
8288

8389
func (o *Ouroboros) Close() error {
90+
// We use a mutex to prevent this function from being called multiple times
91+
// concurrently, which would cause a race condition
92+
o.closeMutex.Lock()
93+
defer o.closeMutex.Unlock()
94+
// Immediately return if we're already shutting down
95+
select {
96+
case <-o.doneChan:
97+
return nil
98+
default:
99+
}
100+
// Close doneChan to signify that we're shutting down
101+
close(o.doneChan)
84102
// Gracefully stop the muxer
85103
if o.muxer != nil {
86104
o.muxer.Stop()
@@ -91,6 +109,22 @@ func (o *Ouroboros) Close() error {
91109
return err
92110
}
93111
}
112+
// Close channels
113+
close(o.ErrorChan)
114+
close(o.protoErrorChan)
115+
// We can only close a channel once, so we have to jump through a few hoops
116+
select {
117+
// The channel is either closed or has an item pending
118+
case _, ok := <-o.handshakeFinishedChan:
119+
// We successfully retrieved an item
120+
// This will probably never happen, but it doesn't hurt to cover this case
121+
if ok {
122+
close(o.handshakeFinishedChan)
123+
}
124+
// The channel is open and has no pending items
125+
default:
126+
close(o.handshakeFinishedChan)
127+
}
94128
return nil
95129
}
96130

@@ -131,7 +165,6 @@ func (o *Ouroboros) setupConnection() error {
131165
protoOptions.Role = protocol.ProtocolRoleClient
132166
}
133167
// Perform handshake
134-
handshakeFinishedChan := make(chan interface{})
135168
var handshakeVersion uint16
136169
var handshakeFullDuplex bool
137170
handshakeConfig := &handshake.Config{
@@ -141,7 +174,7 @@ func (o *Ouroboros) setupConnection() error {
141174
FinishedFunc: func(version uint16, fullDuplex bool) error {
142175
handshakeVersion = version
143176
handshakeFullDuplex = fullDuplex
144-
close(handshakeFinishedChan)
177+
close(o.handshakeFinishedChan)
145178
return nil
146179
},
147180
}
@@ -153,9 +186,13 @@ func (o *Ouroboros) setupConnection() error {
153186
}
154187
// Wait for handshake completion or error
155188
select {
189+
case <-o.doneChan:
190+
// Return an error if we're shutting down
191+
return io.EOF
156192
case err := <-o.protoErrorChan:
157193
return err
158-
case <-handshakeFinishedChan:
194+
case <-o.handshakeFinishedChan:
195+
// This is purposely empty, but we need this case to break out when this channel is closed
159196
}
160197
// Provide the negotiated protocol version to the various mini-protocols
161198
protoOptions.Version = handshakeVersion
@@ -165,7 +202,11 @@ func (o *Ouroboros) setupConnection() error {
165202
}
166203
// Start Goroutine to pass along errors from the mini-protocols
167204
go func() {
168-
err := <-o.protoErrorChan
205+
err, ok := <-o.protoErrorChan
206+
// The channel is closed, which means we're already shutting down
207+
if !ok {
208+
return
209+
}
169210
o.ErrorChan <- fmt.Errorf("protocol error: %s", err)
170211
// Close connection on mini-protocol errors
171212
o.Close()

0 commit comments

Comments
 (0)