imap: add tcp connection options
Allow fine tuning tcp connection options. Signed-off-by: Robin Jarry <robin@jarry.cc>
This commit is contained in:
parent
33aaf94663
commit
5dfeff75f3
2 changed files with 131 additions and 0 deletions
|
@ -61,6 +61,35 @@ available:
|
||||||
|
|
||||||
pass hostname/username
|
pass hostname/username
|
||||||
|
|
||||||
|
*connection-timeout*
|
||||||
|
Maximum delay to establish a connection to the IMAP server. See
|
||||||
|
https://pkg.go.dev/time#ParseDuration.
|
||||||
|
|
||||||
|
Default: 30s
|
||||||
|
|
||||||
|
*keepalive-period*
|
||||||
|
The interval between the last data packet sent (simple ACKs are not
|
||||||
|
considered data) and the first keepalive probe. After the connection is
|
||||||
|
marked to need keepalive, this counter is not used any further. See
|
||||||
|
https://pkg.go.dev/time#ParseDuration.
|
||||||
|
|
||||||
|
By default, the system tcp socket settings are used.
|
||||||
|
|
||||||
|
*keepalive-probes*
|
||||||
|
The number of unacknowledged probes to send before considering the
|
||||||
|
connection dead and notifying the application layer.
|
||||||
|
|
||||||
|
By default, the system tcp socket settings are used.
|
||||||
|
If keepalive-period is specified, this option defaults to 3 probes.
|
||||||
|
|
||||||
|
*keepalive-interval*
|
||||||
|
The interval between subsequential keepalive probes, regardless of what
|
||||||
|
the connection has exchanged in the meantime. Fractional seconds are
|
||||||
|
truncated.
|
||||||
|
|
||||||
|
By default, the system tcp socket settings are used.
|
||||||
|
If keepalive-period is specified, this option defaults to 3s.
|
||||||
|
|
||||||
# SEE ALSO
|
# SEE ALSO
|
||||||
|
|
||||||
*aerc*(1) *aerc-config*(5)
|
*aerc*(1) *aerc-config*(5)
|
||||||
|
|
|
@ -5,7 +5,10 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/emersion/go-imap"
|
"github.com/emersion/go-imap"
|
||||||
sortthread "github.com/emersion/go-imap-sortthread"
|
sortthread "github.com/emersion/go-imap-sortthread"
|
||||||
|
@ -39,6 +42,11 @@ type IMAPWorker struct {
|
||||||
user *url.Userinfo
|
user *url.Userinfo
|
||||||
folders []string
|
folders []string
|
||||||
oauthBearer lib.OAuthBearer
|
oauthBearer lib.OAuthBearer
|
||||||
|
// tcp connection parameters
|
||||||
|
connection_timeout time.Duration
|
||||||
|
keepalive_period time.Duration
|
||||||
|
keepalive_probes int
|
||||||
|
keepalive_interval int
|
||||||
}
|
}
|
||||||
|
|
||||||
client *imapClient
|
client *imapClient
|
||||||
|
@ -107,6 +115,46 @@ func (w *IMAPWorker) handleMessage(msg types.WorkerMessage) error {
|
||||||
|
|
||||||
w.config.user = u.User
|
w.config.user = u.User
|
||||||
w.config.folders = msg.Config.Folders
|
w.config.folders = msg.Config.Folders
|
||||||
|
w.config.connection_timeout = 30 * time.Second
|
||||||
|
w.config.keepalive_period = 0 * time.Second
|
||||||
|
w.config.keepalive_probes = 3
|
||||||
|
w.config.keepalive_interval = 3
|
||||||
|
for key, value := range msg.Config.Params {
|
||||||
|
switch key {
|
||||||
|
case "connection-timeout":
|
||||||
|
val, err := time.ParseDuration(value)
|
||||||
|
if err != nil || val < 0 {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"invalid connection-timeout value %v: %v",
|
||||||
|
value, err)
|
||||||
|
}
|
||||||
|
w.config.connection_timeout = val
|
||||||
|
case "keepalive-period":
|
||||||
|
val, err := time.ParseDuration(value)
|
||||||
|
if err != nil || val < 0 {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"invalid keepalive-period value %v: %v",
|
||||||
|
value, err)
|
||||||
|
}
|
||||||
|
w.config.keepalive_period = val
|
||||||
|
case "keepalive-probes":
|
||||||
|
val, err := strconv.Atoi(value)
|
||||||
|
if err != nil || val < 0 {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"invalid keepalive-probes value %v: %v",
|
||||||
|
value, err)
|
||||||
|
}
|
||||||
|
w.config.keepalive_probes = val
|
||||||
|
case "keepalive-interval":
|
||||||
|
val, err := time.ParseDuration(value)
|
||||||
|
if err != nil || val < 0 {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"invalid keepalive-interval value %v: %v",
|
||||||
|
value, err)
|
||||||
|
}
|
||||||
|
w.config.keepalive_interval = int(val.Seconds())
|
||||||
|
}
|
||||||
|
}
|
||||||
case *types.Connect:
|
case *types.Connect:
|
||||||
if w.client != nil && w.client.State() == imap.SelectedState {
|
if w.client != nil && w.client.State() == imap.SelectedState {
|
||||||
return fmt.Errorf("Already connected")
|
return fmt.Errorf("Already connected")
|
||||||
|
@ -229,6 +277,20 @@ func (w *IMAPWorker) connect() (*client.Client, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if w.config.connection_timeout > 0 {
|
||||||
|
end := time.Now().Add(w.config.connection_timeout)
|
||||||
|
err = conn.SetDeadline(end)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if w.config.keepalive_period > 0 {
|
||||||
|
err = w.setKeepaliveParameters(conn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
serverName, _, _ := net.SplitHostPort(w.config.addr)
|
serverName, _, _ := net.SplitHostPort(w.config.addr)
|
||||||
tlsConfig := &tls.Config{ServerName: serverName}
|
tlsConfig := &tls.Config{ServerName: serverName}
|
||||||
|
|
||||||
|
@ -281,6 +343,46 @@ func (w *IMAPWorker) connect() (*client.Client, error) {
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set additional keepalive parameters.
|
||||||
|
// Uses new interfaces introduced in Go1.11, which let us get connection's file
|
||||||
|
// descriptor, without blocking, and therefore without uncontrolled spawning of
|
||||||
|
// threads (not goroutines, actual threads).
|
||||||
|
func (w *IMAPWorker) setKeepaliveParameters(conn *net.TCPConn) error {
|
||||||
|
err := conn.SetKeepAlive(true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Idle time before sending a keepalive probe
|
||||||
|
err = conn.SetKeepAlivePeriod(w.config.keepalive_period)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rawConn, e := conn.SyscallConn()
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
err = rawConn.Control(func(fdPtr uintptr) {
|
||||||
|
fd := int(fdPtr)
|
||||||
|
// Max number of probes before failure
|
||||||
|
err := syscall.SetsockoptInt(
|
||||||
|
fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPCNT,
|
||||||
|
w.config.keepalive_probes)
|
||||||
|
if err != nil {
|
||||||
|
w.worker.Logger.Printf(
|
||||||
|
"cannot set tcp keepalive probes: %v\n", err)
|
||||||
|
}
|
||||||
|
// Wait time after an unsuccessful probe
|
||||||
|
err = syscall.SetsockoptInt(
|
||||||
|
fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL,
|
||||||
|
w.config.keepalive_interval)
|
||||||
|
if err != nil {
|
||||||
|
w.worker.Logger.Printf(
|
||||||
|
"cannot set tcp keepalive interval: %v\n", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (w *IMAPWorker) Run() {
|
func (w *IMAPWorker) Run() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
|
Loading…
Reference in a new issue