Skip to content

Commit 806fddc

Browse files
committed
Fix client exit failure with an explicit Close
1 parent 7d1d46a commit 806fddc

File tree

3 files changed

+41
-3
lines changed

3 files changed

+41
-3
lines changed

client.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ type cap struct {
5050
// much simpler.
5151
type Client struct {
5252
*Conn
53+
rwc io.ReadWriteCloser
5354
config ClientConfig
5455

5556
// Internal state
@@ -63,9 +64,10 @@ type Client struct {
6364
}
6465

6566
// NewClient creates a client given an io stream and a client config.
66-
func NewClient(rw io.ReadWriter, config ClientConfig) *Client {
67+
func NewClient(rwc io.ReadWriteCloser, config ClientConfig) *Client {
6768
c := &Client{
68-
Conn: NewConn(rw),
69+
Conn: NewConn(rwc),
70+
rwc: rwc,
6971
config: config,
7072
errChan: make(chan error, 1),
7173
caps: make(map[string]cap),
@@ -312,6 +314,7 @@ func (c *Client) RunContext(ctx context.Context) error {
312314
}
313315

314316
close(exiting)
317+
c.rwc.Close()
315318
wg.Wait()
316319

317320
return err

client_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,4 +408,16 @@ func TestPingLoop(t *testing.T) {
408408
SendLine("001 :hello_world\r\n"),
409409
Delay(25 * time.Millisecond),
410410
})
411+
412+
// See if we can get the client to hang
413+
runClientTest(t, config, errors.New("test error"), nil, []TestAction{
414+
ExpectLine("PASS :test_pass\r\n"),
415+
ExpectLine("NICK :test_nick\r\n"),
416+
ExpectLine("USER test_user 0 * :test_name\r\n"),
417+
// We queue this up a line early because the next write will happen after the delay.
418+
QueueWriteError(errors.New("test error")),
419+
SendLine("001 :hello_world\r\n"),
420+
Delay(2 * time.Second),
421+
AssertClosed(),
422+
})
411423
}

stream_test.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@ func SendLine(output string) TestAction {
1818
return SendLineWithTimeout(output, 1*time.Second)
1919
}
2020

21+
func AssertClosed() TestAction {
22+
return func(t *testing.T, rw *testReadWriter) {
23+
if !rw.closed {
24+
assert.Fail(t, "Expected conn to be closed")
25+
}
26+
}
27+
}
28+
2129
func SendLineWithTimeout(output string, timeout time.Duration) TestAction {
2230
return func(t *testing.T, rw *testReadWriter) {
2331
waitChan := time.After(timeout)
@@ -116,6 +124,7 @@ type testReadWriter struct {
116124
readEmptyChan chan struct{}
117125
exiting chan struct{}
118126
clientDone chan struct{}
127+
closed bool
119128
serverBuffer bytes.Buffer
120129
}
121130

@@ -182,6 +191,20 @@ func (rw *testReadWriter) Write(buf []byte) (int, error) {
182191
}
183192
}
184193

194+
func (rw *testReadWriter) Close() error {
195+
select {
196+
case <-rw.exiting:
197+
return errors.New("Connection closed")
198+
default:
199+
// Ensure no double close
200+
if !rw.closed {
201+
rw.closed = true
202+
close(rw.exiting)
203+
}
204+
return nil
205+
}
206+
}
207+
185208
func newTestReadWriter(actions []TestAction) *testReadWriter {
186209
return &testReadWriter{
187210
actions: actions,
@@ -223,7 +246,7 @@ func runTest(t *testing.T, rw *testReadWriter, actions []TestAction) {
223246
// TODO: Make sure there are no more incoming messages
224247

225248
// Ask everything to shut down
226-
close(rw.exiting)
249+
rw.Close()
227250

228251
// Wait for the client to stop
229252
select {

0 commit comments

Comments
 (0)