From 8b2892f8a4efd7809a6f7408da900e40fabf16a1 Mon Sep 17 00:00:00 2001 From: doganpoyraz Date: Thu, 25 Apr 2024 17:13:39 +0300 Subject: [PATCH 1/2] Fix hanging connection during client construction Set timeout value to the client to supply deadline values to Support calls in the client.New function --- client/client.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/client/client.go b/client/client.go index d93c4cb2..41ee43c0 100644 --- a/client/client.go +++ b/client/client.go @@ -581,6 +581,11 @@ func (c *Client) SetDebug(w io.Writer) { // New creates a new client from an existing connection. func New(conn net.Conn) (*Client, error) { + return NewWithTimeout(conn, 0) +} + +// New creates a new client from an existing connection with a timeout. +func NewWithTimeout(conn net.Conn, timeout time.Duration) (*Client, error) { continues := make(chan bool) w := imap.NewClientWriter(nil, continues) r := imap.NewReader(nil) @@ -591,6 +596,7 @@ func New(conn net.Conn) (*Client, error) { continues: continues, state: imap.ConnectingState, ErrorLog: log.New(os.Stderr, "imap/client: ", log.LstdFlags), + Timeout: timeout, } c.handleContinuationReqs() @@ -632,14 +638,16 @@ func DialWithDialer(dialer Dialer, addr string) (*Client, error) { // there is no way to set the client's Timeout for that action. As a // workaround, if the dialer has a timeout set, use that for the connection's // deadline. + var timeout time.Duration if netDialer, ok := dialer.(*net.Dialer); ok && netDialer.Timeout > 0 { err := conn.SetDeadline(time.Now().Add(netDialer.Timeout)) if err != nil { return nil, err } + timeout = netDialer.Timeout } - c, err := New(conn) + c, err := NewWithTimeout(conn, timeout) if err != nil { return nil, err } @@ -677,14 +685,16 @@ func DialWithDialerTLS(dialer Dialer, addr string, tlsConfig *tls.Config) (*Clie // there is no way to set the client's Timeout for that action. As a // workaround, if the dialer has a timeout set, use that for the connection's // deadline. + var timeout time.Duration if netDialer, ok := dialer.(*net.Dialer); ok && netDialer.Timeout > 0 { err := tlsConn.SetDeadline(time.Now().Add(netDialer.Timeout)) if err != nil { return nil, err } + timeout = netDialer.Timeout } - c, err := New(tlsConn) + c, err := NewWithTimeout(tlsConn, timeout) if err != nil { return nil, err } From 3f117400d35ebd06d4f01c26239851813a86f70a Mon Sep 17 00:00:00 2001 From: doganpoyraz Date: Thu, 25 Apr 2024 17:31:18 +0300 Subject: [PATCH 2/2] Pass explicit timeout for custom dialers --- client/client.go | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/client/client.go b/client/client.go index 41ee43c0..3b9687f0 100644 --- a/client/client.go +++ b/client/client.go @@ -629,6 +629,10 @@ type Dialer interface { // // Among other uses, this allows to apply a dial timeout. func DialWithDialer(dialer Dialer, addr string) (*Client, error) { + return DialWithDialerAndTimeout(dialer, addr, 0) +} + +func DialWithDialerAndTimeout(dialer Dialer, addr string, timeout time.Duration) (*Client, error) { conn, err := dialer.Dial("tcp", addr) if err != nil { return nil, err @@ -638,13 +642,16 @@ func DialWithDialer(dialer Dialer, addr string) (*Client, error) { // there is no way to set the client's Timeout for that action. As a // workaround, if the dialer has a timeout set, use that for the connection's // deadline. - var timeout time.Duration - if netDialer, ok := dialer.(*net.Dialer); ok && netDialer.Timeout > 0 { - err := conn.SetDeadline(time.Now().Add(netDialer.Timeout)) + if timeout == 0 { + if netDialer, ok := dialer.(*net.Dialer); ok && netDialer.Timeout > 0 { + timeout = netDialer.Timeout + } + } + if timeout > 0 { + err = conn.SetDeadline(time.Now().Add(timeout)) if err != nil { return nil, err } - timeout = netDialer.Timeout } c, err := NewWithTimeout(conn, timeout) @@ -666,6 +673,10 @@ func DialTLS(addr string, tlsConfig *tls.Config) (*Client, error) { // // Among other uses, this allows to apply a dial timeout. func DialWithDialerTLS(dialer Dialer, addr string, tlsConfig *tls.Config) (*Client, error) { + return DialWithDialerAndTimeoutTLS(dialer, addr, tlsConfig, 0) +} + +func DialWithDialerAndTimeoutTLS(dialer Dialer, addr string, tlsConfig *tls.Config, timeout time.Duration) (*Client, error) { conn, err := dialer.Dial("tcp", addr) if err != nil { return nil, err @@ -685,13 +696,16 @@ func DialWithDialerTLS(dialer Dialer, addr string, tlsConfig *tls.Config) (*Clie // there is no way to set the client's Timeout for that action. As a // workaround, if the dialer has a timeout set, use that for the connection's // deadline. - var timeout time.Duration - if netDialer, ok := dialer.(*net.Dialer); ok && netDialer.Timeout > 0 { - err := tlsConn.SetDeadline(time.Now().Add(netDialer.Timeout)) + if timeout == 0 { + if netDialer, ok := dialer.(*net.Dialer); ok && netDialer.Timeout > 0 { + timeout = netDialer.Timeout + } + } + if timeout > 0 { + err = conn.SetDeadline(time.Now().Add(timeout)) if err != nil { return nil, err } - timeout = netDialer.Timeout } c, err := NewWithTimeout(tlsConn, timeout)