Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions imapclient/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,15 @@ func (c *Client) Mailbox() *SelectedMailbox {
return c.mailbox
}

// Closed returns a channel that is closed when the connection is closed.
//
// This channel cannot be used to reliably determine whether a connection is healthy. If
// the underlying connection times out, the channel will be closed eventually, but not
// immediately. To check whether the connection is healthy, send a command (such as Noop).
func (c *Client) Closed() <-chan struct{} {
return c.decCh
}

// Close immediately closes the connection.
func (c *Client) Close() error {
c.mutex.Lock()
Expand Down
37 changes: 37 additions & 0 deletions imapclient/connection_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package imapclient_test

import (
"testing"
"time"

"github.com/emersion/go-imap/v2"
)

// TestClient_Closed tests that the Closed() channel is closed when the
// connection is explicitly closed via Close().
func TestClient_Closed(t *testing.T) {
client, server := newClientServerPair(t, imap.ConnStateAuthenticated)
defer server.Close()

closedCh := client.Closed()
if closedCh == nil {
t.Fatal("Closed() returned nil channel")
}

select {
case <-closedCh:
t.Fatal("Closed() channel closed before calling Close()")
default: // Expected
}

if err := client.Close(); err != nil {
t.Fatalf("Close() = %v", err)
}

select {
case <-closedCh:
t.Log("Closed() channel properly closed after Close()")
case <-time.After(2 * time.Second):
t.Fatal("Closed() channel not closed after Close()")
}
}
31 changes: 31 additions & 0 deletions imapclient/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -378,3 +378,34 @@ func ExampleClient_Authenticate_oauth() {
log.Fatalf("authentication failed: %v", err)
}
}

func ExampleClient_Closed() {
c, err := imapclient.DialTLS("mail.example.org:993", nil)
if err != nil {
log.Fatalf("failed to dial IMAP server: %v", err)
}

selected := false

go func(c *imapclient.Client) {
if err := c.Login("root", "asdf").Wait(); err != nil {
log.Fatalf("failed to login: %v", err)
}

if _, err := c.Select("INBOX", nil).Wait(); err != nil {
log.Fatalf("failed to select INBOX: %v", err)
}

selected = true

c.Close()
}(c)

// This channel shall be closed when the connection is closed.
<-c.Closed()
log.Println("Connection has been closed")

if !selected {
log.Fatalf("Connection was closed before selecting mailbox")
}
}