Propagate keep alive settings from the config

This commit is contained in:
9seconds
2026-04-07 13:41:44 +02:00
parent 83b43aecc1
commit 102f8a6cce
12 changed files with 76 additions and 24 deletions
+11 -1
View File
@@ -204,13 +204,23 @@ proxies = [
# define a global timeout on establishing of network connections. idle # define a global timeout on establishing of network connections. idle
# means a timeout on pumping data between sockset when nothing is # means a timeout on pumping data between sockset when nothing is
# happening. # happening.
#
[network.timeout] [network.timeout]
tcp = "5s" tcp = "5s"
http = "10s" http = "10s"
idle = "5m" idle = "5m"
handshake = "10s" handshake = "10s"
# this defines a configuration for TCP keep alives. Default values are taken
# from Golang default behavior.
[network.keep-alive]
disabled = false
# idle means a time period after which we start sending TCP Keep Alive probes
idle = "15s"
# interval is a period between 2 consecutive probes
interval = "15s"
# if we miss that many probes, a connection will be considered as a dead one.
count = 9
# mtg has to mimic real websites. It does not mean domain fronting, it also # mtg has to mimic real websites. It does not mean domain fronting, it also
# means that traffic characteristics should be similar to real world traffic. # means that traffic characteristics should be similar to real world traffic.
# websites and applications behave differently, their traffic patterns are also # websites and applications behave differently, their traffic patterns are also
+6
View File
@@ -97,6 +97,12 @@ func (d *Doctor) Run(cli *CLI, version string) error {
conf.Network.Timeout.TCP.Get(10*time.Second), conf.Network.Timeout.TCP.Get(10*time.Second),
conf.Network.Timeout.HTTP.Get(0), conf.Network.Timeout.HTTP.Get(0),
conf.Network.Timeout.Idle.Get(0), conf.Network.Timeout.Idle.Get(0),
net.KeepAliveConfig{
Enable: !conf.Network.KeepAlive.Disabled.Get(false),
Idle: conf.Network.KeepAlive.Idle.Get(0),
Interval: conf.Network.KeepAlive.Interval.Get(0),
Count: int(conf.Network.KeepAlive.Count.Get(0)),
},
) )
fmt.Println("Validate native network connectivity") fmt.Println("Validate native network connectivity")
+6
View File
@@ -50,6 +50,12 @@ func makeNetwork(conf *config.Config, version string) (mtglib.Network, error) {
conf.Network.Timeout.TCP.Get(0), conf.Network.Timeout.TCP.Get(0),
conf.Network.Timeout.HTTP.Get(0), conf.Network.Timeout.HTTP.Get(0),
conf.Network.Timeout.Idle.Get(0), conf.Network.Timeout.Idle.Get(0),
net.KeepAliveConfig{
Enable: !conf.Network.KeepAlive.Disabled.Get(false),
Idle: conf.Network.KeepAlive.Idle.Get(0),
Interval: conf.Network.KeepAlive.Interval.Get(0),
Count: int(conf.Network.KeepAlive.Count.Get(0)),
},
) )
proxyDialers := make([]mtglib.Network, len(conf.Network.Proxies)) proxyDialers := make([]mtglib.Network, len(conf.Network.Proxies))
+6
View File
@@ -65,6 +65,12 @@ type Config struct {
Idle TypeDuration `json:"idle"` Idle TypeDuration `json:"idle"`
Handshake TypeDuration `json:"handshake"` Handshake TypeDuration `json:"handshake"`
} `json:"timeout"` } `json:"timeout"`
KeepAlive struct {
Disabled TypeBool `json:"disabled"`
Idle TypeDuration `json:"idle"`
Interval TypeDuration `json:"interval"`
Count TypeConcurrency `json:"count"`
} `json:"keepAlive"`
DOHIP TypeIP `json:"dohIp"` DOHIP TypeIP `json:"dohIp"`
DNS TypeDNSURI `json:"dns"` DNS TypeDNSURI `json:"dns"`
Proxies []TypeProxyURL `json:"proxies"` Proxies []TypeProxyURL `json:"proxies"`
+6
View File
@@ -60,6 +60,12 @@ type tomlConfig struct {
Idle string `toml:"idle" json:"idle,omitempty"` Idle string `toml:"idle" json:"idle,omitempty"`
Handshake string `toml:"handshake" json:"handshake,omitempty"` Handshake string `toml:"handshake" json:"handshake,omitempty"`
} `toml:"timeout" json:"timeout,omitempty"` } `toml:"timeout" json:"timeout,omitempty"`
KeepAlive struct {
Disabled bool `toml:"disabled" json:"disabled,omitempty"`
Idle string `toml:"idle" json:"idle,omitempty"`
Interval string `toml:"interval" json:"interval,omitempty"`
Count uint `toml:"count" json:"count,omitempty"`
} `toml:"keep-alive" json:"keepAlive,omitempty"`
DOHIP string `toml:"doh-ip" json:"dohIp,omitempty"` DOHIP string `toml:"doh-ip" json:"dohIp,omitempty"`
DNS string `toml:"dns" json:"dns,omitempty"` DNS string `toml:"dns" json:"dns,omitempty"`
Proxies []string `toml:"proxies" json:"proxies,omitempty"` Proxies []string `toml:"proxies" json:"proxies,omitempty"`
+1 -1
View File
@@ -25,7 +25,7 @@ func (suite *BaseHTTPTestSuite) SetupSuite() {
} }
func (suite *BaseHTTPTestSuite) SetupTest() { func (suite *BaseHTTPTestSuite) SetupTest() {
suite.client = network.New(nil, "mtg/1", 0, 0, 0).MakeHTTPClient(nil) suite.client = network.New(nil, "mtg/1", 0, 0, 0, network.DefaultKeepAliveConfig).MakeHTTPClient(nil)
} }
func (suite *BaseHTTPTestSuite) TestGet() { func (suite *BaseHTTPTestSuite) TestGet() {
+1 -1
View File
@@ -19,7 +19,7 @@ type BaseNetworkTestSuite struct {
func (suite *BaseNetworkTestSuite) SetupSuite() { func (suite *BaseNetworkTestSuite) SetupSuite() {
suite.EchoServerTestSuite.SetupSuite() suite.EchoServerTestSuite.SetupSuite()
suite.net = network.New(nil, "agent", 0, 0, 0) suite.net = network.New(nil, "agent", 0, 0, 0, network.DefaultKeepAliveConfig)
} }
func (suite *BaseNetworkTestSuite) TestDialUnknownNetwork() { func (suite *BaseNetworkTestSuite) TestDialUnknownNetwork() {
+22 -2
View File
@@ -11,6 +11,7 @@ package network
import ( import (
"errors" "errors"
"net"
"time" "time"
) )
@@ -27,19 +28,25 @@ const (
// DefaultTCPKeepAlivePeriod defines a time period between 2 consecuitive // DefaultTCPKeepAlivePeriod defines a time period between 2 consecuitive
// probes. // probes.
// //
// Deprecated: use DefaultKeepAliveIdle and DefaultKeepAliveInterval instead. // Deprecated: use DefaultKeepAliveConfig
DefaultTCPKeepAlivePeriod = 10 * time.Second DefaultTCPKeepAlivePeriod = 10 * time.Second
// DefaultKeepAliveIdle is the time a connection must be idle before // DefaultKeepAliveIdle is the time a connection must be idle before
// the first keepalive probe is sent. // the first keepalive probe is sent.
//
// Deprecated: use DefaultKeepAliveConfig
DefaultKeepAliveIdle = 30 * time.Second DefaultKeepAliveIdle = 30 * time.Second
// DefaultKeepAliveInterval is the time between consecutive keepalive // DefaultKeepAliveInterval is the time between consecutive keepalive
// probes. // probes.
//
// Deprecated: use DefaultKeepAliveConfig
DefaultKeepAliveInterval = 10 * time.Second DefaultKeepAliveInterval = 10 * time.Second
// DefaultKeepAliveCount is the number of unacknowledged probes before // DefaultKeepAliveCount is the number of unacknowledged probes before
// the connection is considered dead. // the connection is considered dead.
//
// Deprecated: use DefaultKeepAliveConfig
DefaultKeepAliveCount = 3 DefaultKeepAliveCount = 3
// User Agent to use in HTTP client. // User Agent to use in HTTP client.
@@ -50,4 +57,17 @@ const (
tcpLingerTimeout = 1 tcpLingerTimeout = 1
) )
var ErrCannotDial = errors.New("cannot dial to any address") var (
ErrCannotDial = errors.New("cannot dial to any address")
// DefaultKeepAliveConfig defines a default configuration for
// keep alive settings. As per official documentation, if keep alive
// is enabled, then:
//
// Idle = 15 * time.Second
// Interval = 15 * time.Second
// Count = 9
DefaultKeepAliveConfig = net.KeepAliveConfig{
Enable: true,
}
)
+4 -1
View File
@@ -14,6 +14,7 @@ import (
type network struct { type network struct {
net.Dialer net.Dialer
keepAliveConfig net.KeepAliveConfig
httpTimeout time.Duration httpTimeout time.Duration
idleTimeout time.Duration idleTimeout time.Duration
userAgent string userAgent string
@@ -37,7 +38,7 @@ func (n *network) DialContext(ctx context.Context, network, address string) (ess
tcpConn := conn.(*net.TCPConn) tcpConn := conn.(*net.TCPConn)
return tcpConn, setCommonSocketOptions(tcpConn) return tcpConn, setCommonSocketOptions(tcpConn, n.keepAliveConfig)
} }
func (n *network) MakeHTTPClient( func (n *network) MakeHTTPClient(
@@ -71,6 +72,7 @@ func New(
tcpTimeout, tcpTimeout,
httpTimeout, httpTimeout,
idleTimeout time.Duration, idleTimeout time.Duration,
keepAliveConfig net.KeepAliveConfig,
) mtglib.Network { ) mtglib.Network {
if dnsResolver == nil { if dnsResolver == nil {
dnsResolver = net.DefaultResolver dnsResolver = net.DefaultResolver
@@ -89,5 +91,6 @@ func New(
userAgent: userAgent, userAgent: userAgent,
idleTimeout: idleTimeout, idleTimeout: idleTimeout,
httpTimeout: httpTimeout, httpTimeout: httpTimeout,
keepAliveConfig: keepAliveConfig,
} }
} }
+2 -7
View File
@@ -5,13 +5,8 @@ import (
"net" "net"
) )
func setCommonSocketOptions(conn *net.TCPConn) error { func setCommonSocketOptions(conn *net.TCPConn, keepAliveConfig net.KeepAliveConfig) error {
if err := conn.SetKeepAliveConfig(net.KeepAliveConfig{ if err := conn.SetKeepAliveConfig(keepAliveConfig); err != nil {
Enable: true,
Idle: DefaultKeepAliveIdle,
Interval: DefaultKeepAliveInterval,
Count: DefaultKeepAliveCount,
}); err != nil {
return fmt.Errorf("cannot configure TCP keepalive: %w", err) return fmt.Errorf("cannot configure TCP keepalive: %w", err)
} }
+4 -4
View File
@@ -65,7 +65,7 @@ func TestSetCommonSocketOptionsKeepAlive(t *testing.T) {
tcpConn := accepted.(*net.TCPConn) tcpConn := accepted.(*net.TCPConn)
err = setCommonSocketOptions(tcpConn) err = setCommonSocketOptions(tcpConn, DefaultKeepAliveConfig)
require.NoError(t, err) require.NoError(t, err)
rawConn, err := tcpConn.SyscallConn() rawConn, err := tcpConn.SyscallConn()
@@ -78,15 +78,15 @@ func TestSetCommonSocketOptionsKeepAlive(t *testing.T) {
idle, err := unix.GetsockoptInt(int(fd), syscall.IPPROTO_TCP, tcpKeepIdleOption()) idle, err := unix.GetsockoptInt(int(fd), syscall.IPPROTO_TCP, tcpKeepIdleOption())
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, int(DefaultKeepAliveIdle.Seconds()), idle, "keepalive idle should match DefaultKeepAliveIdle") require.Equal(t, 15, idle, "keepalive idle should match DefaultKeepAliveIdle")
interval, err := unix.GetsockoptInt(int(fd), syscall.IPPROTO_TCP, unix.TCP_KEEPINTVL) interval, err := unix.GetsockoptInt(int(fd), syscall.IPPROTO_TCP, unix.TCP_KEEPINTVL)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, int(DefaultKeepAliveInterval.Seconds()), interval, "keepalive interval should match DefaultKeepAliveInterval") require.Equal(t, 15, interval, "keepalive interval should match DefaultKeepAliveInterval")
count, err := unix.GetsockoptInt(int(fd), syscall.IPPROTO_TCP, unix.TCP_KEEPCNT) count, err := unix.GetsockoptInt(int(fd), syscall.IPPROTO_TCP, unix.TCP_KEEPCNT)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, DefaultKeepAliveCount, count, "keepalive count should match DefaultKeepAliveCount") require.Equal(t, 9, count, "keepalive count should match DefaultKeepAliveCount")
}) })
require.NoError(t, err) require.NoError(t, err)
} }
+1 -1
View File
@@ -66,7 +66,7 @@ func (suite *SocksProxyTestSuite) SetupSuite() {
require.NoError(suite.T(), err) require.NoError(suite.T(), err)
suite.authURL = parsed suite.authURL = parsed
suite.baseNetwork = network.New(nil, "mtg", 0, 0, 0) suite.baseNetwork = network.New(nil, "mtg", 0, 0, 0, network.DefaultKeepAliveConfig)
} }
func (suite *SocksProxyTestSuite) TestIncorrectSchema() { func (suite *SocksProxyTestSuite) TestIncorrectSchema() {