Introduce idleConn wrapper to prevent wedged connections Add test for silent server timeout behavior Implement proper TLS handshake and greeting handling Set explicit dial and read timeouts
61 lines
1.5 KiB
Go
61 lines
1.5 KiB
Go
package imapx
|
|
|
|
import (
|
|
"io"
|
|
"net"
|
|
"testing"
|
|
"time"
|
|
|
|
"context"
|
|
)
|
|
|
|
// A server that sends the IMAP greeting, accepts one command, then goes silent
|
|
// forever must NOT wedge the client: the idle read-timeout has to abort the
|
|
// blocked read so the command returns an error instead of hanging. This is the
|
|
// exact production failure (account stuck in "running" with zero progress).
|
|
func TestConnectIdleReadTimeoutUnwedgesSilentServer(t *testing.T) {
|
|
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
|
if err != nil {
|
|
t.Fatalf("listen: %v", err)
|
|
}
|
|
defer ln.Close()
|
|
|
|
go func() {
|
|
c, err := ln.Accept()
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer c.Close()
|
|
_, _ = io.WriteString(c, "* OK IMAP4rev2 ready\r\n")
|
|
// Read the LOGIN command bytes, then never answer.
|
|
buf := make([]byte, 512)
|
|
_, _ = c.Read(buf)
|
|
// Block until the client gives up and closes the connection.
|
|
_, _ = c.Read(buf)
|
|
}()
|
|
|
|
old := idleReadTimeout
|
|
idleReadTimeout = 150 * time.Millisecond
|
|
defer func() { idleReadTimeout = old }()
|
|
|
|
port := ln.Addr().(*net.TCPAddr).Port
|
|
ep := Endpoint{Host: "127.0.0.1", Port: port, TLSMode: "plain"}
|
|
|
|
c, err := Connect(context.Background(), ep)
|
|
if err != nil {
|
|
t.Fatalf("Connect: %v", err)
|
|
}
|
|
|
|
done := make(chan error, 1)
|
|
go func() { done <- c.Login("user", "pass").Wait() }()
|
|
|
|
select {
|
|
case err := <-done:
|
|
if err == nil {
|
|
t.Fatal("expected an error from Login against a silent server, got nil")
|
|
}
|
|
case <-time.After(3 * time.Second):
|
|
t.Fatal("Login did not return: idle read-timeout was not enforced (connection wedged)")
|
|
}
|
|
}
|