| // 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(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(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) | |
| } | |
| } | |
| } |