Skip to content

Commit 8e566a9

Browse files
Merge branch 'v2' into recent-flag
2 parents a711195 + cc7b09a commit 8e566a9

File tree

3 files changed

+77
-0
lines changed

3 files changed

+77
-0
lines changed

imapclient/client.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,15 @@ func (c *Client) Mailbox() *SelectedMailbox {
385385
return c.mailbox
386386
}
387387

388+
// Closed returns a channel that is closed when the connection is closed.
389+
//
390+
// This channel cannot be used to reliably determine whether a connection is healthy. If
391+
// the underlying connection times out, the channel will be closed eventually, but not
392+
// immediately. To check whether the connection is healthy, send a command (such as Noop).
393+
func (c *Client) Closed() <-chan struct{} {
394+
return c.decCh
395+
}
396+
388397
// Close immediately closes the connection.
389398
func (c *Client) Close() error {
390399
c.mutex.Lock()

imapclient/connection_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package imapclient_test
2+
3+
import (
4+
"testing"
5+
"time"
6+
7+
"github.com/emersion/go-imap/v2"
8+
)
9+
10+
// TestClient_Closed tests that the Closed() channel is closed when the
11+
// connection is explicitly closed via Close().
12+
func TestClient_Closed(t *testing.T) {
13+
client, server := newClientServerPair(t, imap.ConnStateAuthenticated)
14+
defer server.Close()
15+
16+
closedCh := client.Closed()
17+
if closedCh == nil {
18+
t.Fatal("Closed() returned nil channel")
19+
}
20+
21+
select {
22+
case <-closedCh:
23+
t.Fatal("Closed() channel closed before calling Close()")
24+
default: // Expected
25+
}
26+
27+
if err := client.Close(); err != nil {
28+
t.Fatalf("Close() = %v", err)
29+
}
30+
31+
select {
32+
case <-closedCh:
33+
t.Log("Closed() channel properly closed after Close()")
34+
case <-time.After(2 * time.Second):
35+
t.Fatal("Closed() channel not closed after Close()")
36+
}
37+
}

imapclient/example_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,3 +378,34 @@ func ExampleClient_Authenticate_oauth() {
378378
log.Fatalf("authentication failed: %v", err)
379379
}
380380
}
381+
382+
func ExampleClient_Closed() {
383+
c, err := imapclient.DialTLS("mail.example.org:993", nil)
384+
if err != nil {
385+
log.Fatalf("failed to dial IMAP server: %v", err)
386+
}
387+
388+
selected := false
389+
390+
go func(c *imapclient.Client) {
391+
if err := c.Login("root", "asdf").Wait(); err != nil {
392+
log.Fatalf("failed to login: %v", err)
393+
}
394+
395+
if _, err := c.Select("INBOX", nil).Wait(); err != nil {
396+
log.Fatalf("failed to select INBOX: %v", err)
397+
}
398+
399+
selected = true
400+
401+
c.Close()
402+
}(c)
403+
404+
// This channel shall be closed when the connection is closed.
405+
<-c.Closed()
406+
log.Println("Connection has been closed")
407+
408+
if !selected {
409+
log.Fatalf("Connection was closed before selecting mailbox")
410+
}
411+
}

0 commit comments

Comments
 (0)