File: tcp_timeouts.go

package info (click to toggle)
incus 6.0.4-2
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 23,864 kB
  • sloc: sh: 16,015; ansic: 3,121; python: 456; makefile: 321; ruby: 51; sql: 50; lisp: 6
file content (70 lines) | stat: -rw-r--r-- 2,404 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
package tcp

import (
	"crypto/tls"
	"fmt"
	"net"
	"reflect"
	"time"
	"unsafe"
)

// ExtractConn tries to extract the underlying net.TCPConn from a tls.Conn or net.Conn.
func ExtractConn(conn net.Conn) (*net.TCPConn, error) {
	var tcpConn *net.TCPConn

	// Go doesn't currently expose the underlying TCP connection of a TLS connection, but we need it in order
	// to set timeout properties on the connection. We use some reflect/unsafe magic to extract the private
	// remote.conn field, which is indeed the underlying TCP connection.
	tlsConn, ok := conn.(*tls.Conn)
	if ok {
		field := reflect.ValueOf(tlsConn).Elem().FieldByName("conn")
		field = reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).Elem()
		c := field.Interface()

		tcpConn, ok = c.(*net.TCPConn)
		if !ok {
			return nil, fmt.Errorf("Underlying tls.Conn connection is not a net.TCPConn")
		}
	} else {
		tcpConn, ok = conn.(*net.TCPConn)
		if !ok {
			return nil, fmt.Errorf("Connection is not a net.TCPConn")
		}
	}

	return tcpConn, nil
}

// SetTimeouts sets TCP_USER_TIMEOUT and TCP keep alive timeouts on a connection.
// If userTimeout is zero, then defaults to 2 minutes.
func SetTimeouts(conn *net.TCPConn, userTimeout time.Duration) error {
	if userTimeout == 0 {
		userTimeout = time.Minute * 2
	}

	// Set TCP_USER_TIMEOUT option to limit the maximum amount of time in ms that transmitted data may remain
	// unacknowledged before TCP will forcefully close the corresponding connection and return ETIMEDOUT to the
	// application. This combined with the TCP keepalive options on the socket will ensure that should the
	// remote side of the connection disappear abruptly, it will be detected and the socket be closed quickly.
	// Decreasing the user timeouts allows applications to "fail fast" if so desired. Otherwise it may take
	// up to 20 minutes with the current system defaults in a normal WAN environment if there are packets in
	// the send queue that will prevent the keepalive timer from working as the retransmission timers kick in.
	// See https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=dca43c75e7e545694a9dd6288553f55c53e2a3a3
	err := SetUserTimeout(conn, userTimeout)
	if err != nil {
		return err
	}

	err = conn.SetKeepAlive(true)
	if err != nil {
		return err
	}

	err = conn.SetKeepAlivePeriod(3 * time.Second)
	if err != nil {
		return err
	}

	return nil
}