blob: 53d0be034fb554fce7532dec5dfd71a517839644 [file] [log] [blame] [edit]
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || solaris || windows
package net
import (
"runtime"
"testing"
)
func TestTCPConnKeepAliveConfigDialer(t *testing.T) {
maybeSkipKeepAliveTest(t)
t.Cleanup(func() {
testPreHookSetKeepAlive = func(*netFD) {}
})
var (
errHook error
oldCfg KeepAliveConfig
)
testPreHookSetKeepAlive = func(nfd *netFD) {
oldCfg, errHook = getCurrentKeepAliveSettings(fdType(nfd.pfd.Sysfd))
}
handler := func(ls *localServer, ln Listener) {
for {
c, err := ln.Accept()
if err != nil {
return
}
c.Close()
}
}
ln := newLocalListener(t, "tcp", &ListenConfig{
KeepAlive: -1, // prevent calling hook from accepting
})
ls := (&streamListener{Listener: ln}).newLocalServer()
defer ls.teardown()
if err := ls.buildup(handler); err != nil {
t.Fatal(err)
}
for _, cfg := range testConfigs {
d := Dialer{
KeepAlive: defaultTCPKeepAliveIdle, // should be ignored
KeepAliveConfig: cfg}
c, err := d.Dial("tcp", ls.Listener.Addr().String())
if err != nil {
t.Fatal(err)
}
defer c.Close()
if errHook != nil {
t.Fatal(errHook)
}
sc, err := c.(*TCPConn).SyscallConn()
if err != nil {
t.Fatal(err)
}
if err := sc.Control(func(fd uintptr) {
verifyKeepAliveSettings(t, fdType(fd), oldCfg, cfg)
}); err != nil {
t.Fatal(err)
}
}
}
func TestTCPConnKeepAliveConfigListener(t *testing.T) {
maybeSkipKeepAliveTest(t)
t.Cleanup(func() {
testPreHookSetKeepAlive = func(*netFD) {}
})
var (
errHook error
oldCfg KeepAliveConfig
)
testPreHookSetKeepAlive = func(nfd *netFD) {
oldCfg, errHook = getCurrentKeepAliveSettings(fdType(nfd.pfd.Sysfd))
}
ch := make(chan Conn, 1)
handler := func(ls *localServer, ln Listener) {
c, err := ln.Accept()
if err != nil {
return
}
ch <- c
}
for _, cfg := range testConfigs {
ln := newLocalListener(t, "tcp", &ListenConfig{
KeepAlive: defaultTCPKeepAliveIdle, // should be ignored
KeepAliveConfig: cfg})
ls := (&streamListener{Listener: ln}).newLocalServer()
defer ls.teardown()
if err := ls.buildup(handler); err != nil {
t.Fatal(err)
}
d := Dialer{KeepAlive: -1} // prevent calling hook from dialing
c, err := d.Dial("tcp", ls.Listener.Addr().String())
if err != nil {
t.Fatal(err)
}
defer c.Close()
cc := <-ch
defer cc.Close()
if errHook != nil {
t.Fatal(errHook)
}
sc, err := cc.(*TCPConn).SyscallConn()
if err != nil {
t.Fatal(err)
}
if err := sc.Control(func(fd uintptr) {
verifyKeepAliveSettings(t, fdType(fd), oldCfg, cfg)
}); err != nil {
t.Fatal(err)
}
}
}
func TestTCPConnKeepAliveConfig(t *testing.T) {
maybeSkipKeepAliveTest(t)
handler := func(ls *localServer, ln Listener) {
for {
c, err := ln.Accept()
if err != nil {
return
}
c.Close()
}
}
ls := newLocalServer(t, "tcp")
defer ls.teardown()
if err := ls.buildup(handler); err != nil {
t.Fatal(err)
}
for _, cfg := range testConfigs {
d := Dialer{KeepAlive: -1} // avoid setting default values before the test
c, err := d.Dial("tcp", ls.Listener.Addr().String())
if err != nil {
t.Fatal(err)
}
defer c.Close()
sc, err := c.(*TCPConn).SyscallConn()
if err != nil {
t.Fatal(err)
}
var (
errHook error
oldCfg KeepAliveConfig
)
if err := sc.Control(func(fd uintptr) {
oldCfg, errHook = getCurrentKeepAliveSettings(fdType(fd))
}); err != nil {
t.Fatal(err)
}
if errHook != nil {
t.Fatal(errHook)
}
err = c.(*TCPConn).SetKeepAliveConfig(cfg)
if err != nil {
if runtime.GOOS == "solaris" {
// Solaris prior to 11.4 does not support TCP_KEEPINTVL and TCP_KEEPCNT,
// so it will return syscall.ENOPROTOOPT when only one of Interval and Count
// is negative. This is expected, so skip the error check in this case.
if cfg.Interval >= 0 && cfg.Count >= 0 {
t.Fatal(err)
}
} else {
t.Fatal(err)
}
}
if err := sc.Control(func(fd uintptr) {
verifyKeepAliveSettings(t, fdType(fd), oldCfg, cfg)
}); err != nil {
t.Fatal(err)
}
}
}