diff --git a/ssh/mux.go b/ssh/mux.go index 3bc4afb..5775881 100644 --- a/ssh/mux.go +++ b/ssh/mux.go @@ -155,7 +155,10 @@ func (m *mux) SendRequest(name string, wantReply bool, payload []byte) (bool, [] drain: for { select { - case <-m.globalResponses: + case _, ok := <-m.globalResponses: + if !ok { + break drain + } default: break drain } diff --git a/ssh/mux_test.go b/ssh/mux_test.go index 48a8da6..a7dff34 100644 --- a/ssh/mux_test.go +++ b/ssh/mux_test.go @@ -1405,3 +1405,41 @@ closed: t.Fatal("SendRequest spinloop on closed ch.msg") } } + +func TestMuxSendRequestAfterCloseNoSpinloop(t *testing.T) { + a, b := muxPair() + defer a.Close() + + // Closing the underlying conn causes mux.loop to exit and close + // b.globalResponses. + if err := b.Close(); err != nil { + t.Fatalf("Close: %v", err) + } + // Wait for globalResponses to actually be closed by loop(). + for { + select { + case _, ok := <-b.globalResponses: + if !ok { + goto closed + } + case <-time.After(2 * time.Second): + t.Fatal("globalResponses was not closed") + } + } +closed: + + done := make(chan error, 1) + go func() { + _, _, err := b.SendRequest("ping", true, nil) + done <- err + }() + + select { + case err := <-done: + if err == nil { + t.Fatal("SendRequest after close: got nil error") + } + case <-time.After(2 * time.Second): + t.Fatal("SendRequest spinloop on closed globalResponses") + } +}