Source file src/net/tcpconn_keepalive_test.go

     1  // Copyright 2023 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || solaris || windows
     6  
     7  package net
     8  
     9  import (
    10  	"runtime"
    11  	"testing"
    12  )
    13  
    14  func TestTCPConnKeepAliveConfigDialer(t *testing.T) {
    15  	maybeSkipKeepAliveTest(t)
    16  
    17  	t.Cleanup(func() {
    18  		testPreHookSetKeepAlive = func(*netFD) {}
    19  	})
    20  	var (
    21  		errHook error
    22  		oldCfg  KeepAliveConfig
    23  	)
    24  	testPreHookSetKeepAlive = func(nfd *netFD) {
    25  		oldCfg, errHook = getCurrentKeepAliveSettings(fdType(nfd.pfd.Sysfd))
    26  	}
    27  
    28  	handler := func(ls *localServer, ln Listener) {
    29  		for {
    30  			c, err := ln.Accept()
    31  			if err != nil {
    32  				return
    33  			}
    34  			c.Close()
    35  		}
    36  	}
    37  	ln := newLocalListener(t, "tcp", &ListenConfig{
    38  		KeepAlive: -1, // prevent calling hook from accepting
    39  	})
    40  	ls := (&streamListener{Listener: ln}).newLocalServer()
    41  	defer ls.teardown()
    42  	if err := ls.buildup(handler); err != nil {
    43  		t.Fatal(err)
    44  	}
    45  
    46  	for _, cfg := range testConfigs {
    47  		d := Dialer{
    48  			KeepAlive:       defaultTCPKeepAliveIdle, // should be ignored
    49  			KeepAliveConfig: cfg}
    50  		c, err := d.Dial("tcp", ls.Listener.Addr().String())
    51  		if err != nil {
    52  			t.Fatal(err)
    53  		}
    54  		defer c.Close()
    55  
    56  		if errHook != nil {
    57  			t.Fatal(errHook)
    58  		}
    59  
    60  		sc, err := c.(*TCPConn).SyscallConn()
    61  		if err != nil {
    62  			t.Fatal(err)
    63  		}
    64  		if err := sc.Control(func(fd uintptr) {
    65  			verifyKeepAliveSettings(t, fdType(fd), oldCfg, cfg)
    66  		}); err != nil {
    67  			t.Fatal(err)
    68  		}
    69  	}
    70  }
    71  
    72  func TestTCPConnKeepAliveConfigListener(t *testing.T) {
    73  	maybeSkipKeepAliveTest(t)
    74  
    75  	t.Cleanup(func() {
    76  		testPreHookSetKeepAlive = func(*netFD) {}
    77  	})
    78  	var (
    79  		errHook error
    80  		oldCfg  KeepAliveConfig
    81  	)
    82  	testPreHookSetKeepAlive = func(nfd *netFD) {
    83  		oldCfg, errHook = getCurrentKeepAliveSettings(fdType(nfd.pfd.Sysfd))
    84  	}
    85  
    86  	ch := make(chan Conn, 1)
    87  	handler := func(ls *localServer, ln Listener) {
    88  		c, err := ln.Accept()
    89  		if err != nil {
    90  			return
    91  		}
    92  		ch <- c
    93  	}
    94  	for _, cfg := range testConfigs {
    95  		ln := newLocalListener(t, "tcp", &ListenConfig{
    96  			KeepAlive:       defaultTCPKeepAliveIdle, // should be ignored
    97  			KeepAliveConfig: cfg})
    98  		ls := (&streamListener{Listener: ln}).newLocalServer()
    99  		defer ls.teardown()
   100  		if err := ls.buildup(handler); err != nil {
   101  			t.Fatal(err)
   102  		}
   103  		d := Dialer{KeepAlive: -1} // prevent calling hook from dialing
   104  		c, err := d.Dial("tcp", ls.Listener.Addr().String())
   105  		if err != nil {
   106  			t.Fatal(err)
   107  		}
   108  		defer c.Close()
   109  
   110  		cc := <-ch
   111  		defer cc.Close()
   112  		if errHook != nil {
   113  			t.Fatal(errHook)
   114  		}
   115  		sc, err := cc.(*TCPConn).SyscallConn()
   116  		if err != nil {
   117  			t.Fatal(err)
   118  		}
   119  		if err := sc.Control(func(fd uintptr) {
   120  			verifyKeepAliveSettings(t, fdType(fd), oldCfg, cfg)
   121  		}); err != nil {
   122  			t.Fatal(err)
   123  		}
   124  	}
   125  }
   126  
   127  func TestTCPConnKeepAliveConfig(t *testing.T) {
   128  	maybeSkipKeepAliveTest(t)
   129  
   130  	handler := func(ls *localServer, ln Listener) {
   131  		for {
   132  			c, err := ln.Accept()
   133  			if err != nil {
   134  				return
   135  			}
   136  			c.Close()
   137  		}
   138  	}
   139  	ls := newLocalServer(t, "tcp")
   140  	defer ls.teardown()
   141  	if err := ls.buildup(handler); err != nil {
   142  		t.Fatal(err)
   143  	}
   144  	for _, cfg := range testConfigs {
   145  		d := Dialer{KeepAlive: -1} // avoid setting default values before the test
   146  		c, err := d.Dial("tcp", ls.Listener.Addr().String())
   147  		if err != nil {
   148  			t.Fatal(err)
   149  		}
   150  		defer c.Close()
   151  
   152  		sc, err := c.(*TCPConn).SyscallConn()
   153  		if err != nil {
   154  			t.Fatal(err)
   155  		}
   156  
   157  		var (
   158  			errHook error
   159  			oldCfg  KeepAliveConfig
   160  		)
   161  		if err := sc.Control(func(fd uintptr) {
   162  			oldCfg, errHook = getCurrentKeepAliveSettings(fdType(fd))
   163  		}); err != nil {
   164  			t.Fatal(err)
   165  		}
   166  		if errHook != nil {
   167  			t.Fatal(errHook)
   168  		}
   169  
   170  		err = c.(*TCPConn).SetKeepAliveConfig(cfg)
   171  		if err != nil {
   172  			if runtime.GOOS == "solaris" {
   173  				// Solaris prior to 11.4 does not support TCP_KEEPINTVL and TCP_KEEPCNT,
   174  				// so it will return syscall.ENOPROTOOPT when only one of Interval and Count
   175  				// is negative. This is expected, so skip the error check in this case.
   176  				if cfg.Interval >= 0 && cfg.Count >= 0 {
   177  					t.Fatal(err)
   178  				}
   179  			} else {
   180  				t.Fatal(err)
   181  			}
   182  		}
   183  
   184  		if err := sc.Control(func(fd uintptr) {
   185  			verifyKeepAliveSettings(t, fdType(fd), oldCfg, cfg)
   186  		}); err != nil {
   187  			t.Fatal(err)
   188  		}
   189  	}
   190  }
   191  

View as plain text