From abce8ac3904b2186c54a49175c2878b1ae14c4af Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Thu, 16 Oct 2025 15:57:22 +0200 Subject: [PATCH] imapclient: implement Client.Closed Implement a Client.Closed() to be used by clients to monitor when a connection is closed. --- imapclient/client.go | 5 +++++ imapclient/connection_test.go | 37 +++++++++++++++++++++++++++++++++++ imapclient/example_test.go | 22 +++++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 imapclient/connection_test.go diff --git a/imapclient/client.go b/imapclient/client.go index 620bce36..fc96b024 100644 --- a/imapclient/client.go +++ b/imapclient/client.go @@ -385,6 +385,11 @@ func (c *Client) Mailbox() *SelectedMailbox { return c.mailbox } +// Closed returns a channel that is closed when the connection is closed. +func (c *Client) Closed() <-chan struct{} { + return c.decCh +} + // Close immediately closes the connection. func (c *Client) Close() error { c.mutex.Lock() diff --git a/imapclient/connection_test.go b/imapclient/connection_test.go new file mode 100644 index 00000000..a15e338e --- /dev/null +++ b/imapclient/connection_test.go @@ -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()") + } +} diff --git a/imapclient/example_test.go b/imapclient/example_test.go index 86435e7e..f5003b28 100644 --- a/imapclient/example_test.go +++ b/imapclient/example_test.go @@ -378,3 +378,25 @@ 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) + } + defer c.Close() + + if err := c.Login("root", "asdf").Wait(); err != nil { + log.Fatalf("failed to login: %v", err) + } + + // Monitor the connection in a separate goroutine. + go func() { + <-c.Closed() + log.Println("Connection has been closed") + }() + + if _, err := c.Select("INBOX", nil).Wait(); err != nil { + log.Fatalf("failed to select INBOX: %v", err) + } +}