@@ -1064,6 +1064,33 @@ def data_directory_is_empty(data_dir: str) -> bool:
10641064 return all (os .name != 'nt' and (n .startswith ('.' ) or n == 'lost+found' ) for n in os .listdir (data_dir ))
10651065
10661066
1067+ def apply_keepalive_limit (option : str , value : int ) -> int :
1068+ """
1069+ Ensures provided *value* for keepalive *option* does not exceed the maximum allowed value for the current platform.
1070+
1071+ :param option: The TCP keepalive option name. Possible values are:
1072+
1073+ * ``TCP_USER_TIMEOUT``;
1074+ * ``TCP_KEEPIDLE``;
1075+ * ``TCP_KEEPINTVL``;
1076+ * ``TCP_KEEPCNT``.
1077+
1078+ :param value: The desired value for the keepalive option.
1079+
1080+ :returns: maybe adjusted value.
1081+ """
1082+ max_of_options = {
1083+ 'linux' : {'TCP_USER_TIMEOUT' : 2147483647 , 'TCP_KEEPIDLE' : 32767 , 'TCP_KEEPINTVL' : 32767 , 'TCP_KEEPCNT' : 127 },
1084+ 'darwin' : {'TCP_KEEPIDLE' : 4294967 , 'TCP_KEEPINTVL' : 4294967 , 'TCP_KEEPCNT' : 2147483647 },
1085+ }
1086+ platform = 'linux' if sys .platform .startswith ('linux' ) else sys .platform
1087+ max_possible_value = max_of_options .get (platform , {}).get (option )
1088+ if max_possible_value is not None and value > max_possible_value :
1089+ logger .debug ('%s changed from %d to %d.' , option , value , max_possible_value )
1090+ value = max_possible_value
1091+ return value
1092+
1093+
10671094def keepalive_intvl (timeout : int , idle : int , cnt : int = 3 ) -> int :
10681095 """Calculate the value to be used as ``TCP_KEEPINTVL`` based on *timeout*, *idle*, and *cnt*.
10691096
@@ -1073,7 +1100,8 @@ def keepalive_intvl(timeout: int, idle: int, cnt: int = 3) -> int:
10731100
10741101 :returns: the value to be used as ``TCP_KEEPINTVL``.
10751102 """
1076- return max (1 , int (float (timeout - idle ) / cnt ))
1103+ intvl = max (1 , int (float (timeout - idle ) / cnt ))
1104+ return apply_keepalive_limit ('TCP_KEEPINTVL' , intvl )
10771105
10781106
10791107def keepalive_socket_options (timeout : int , idle : int , cnt : int = 3 ) -> Iterator [Tuple [int , int , int ]]:
@@ -1105,20 +1133,22 @@ def keepalive_socket_options(timeout: int, idle: int, cnt: int = 3) -> Iterator[
11051133 if not (sys .platform .startswith ('linux' ) or sys .platform .startswith ('darwin' )):
11061134 return
11071135
1108- if sys . platform . startswith ( 'linux' ):
1109- yield ( socket . SOL_TCP , 18 , int ( timeout * 1000 )) # TCP_USER_TIMEOUT
1110-
1136+ TCP_USER_TIMEOUT = getattr ( socket , 'TCP_USER_TIMEOUT' , None )
1137+ if TCP_USER_TIMEOUT is not None :
1138+ yield ( socket . SOL_TCP , TCP_USER_TIMEOUT , apply_keepalive_limit ( 'TCP_USER_TIMEOUT' , int ( timeout * 1000 )))
11111139 # The socket constants from MacOS netinet/tcp.h are not exported by python's
11121140 # socket module, therefore we are using 0x10, 0x101, 0x102 constants.
11131141 TCP_KEEPIDLE = getattr (socket , 'TCP_KEEPIDLE' , 0x10 if sys .platform .startswith ('darwin' ) else None )
11141142 if TCP_KEEPIDLE is not None :
1143+ idle = apply_keepalive_limit ('TCP_KEEPIDLE' , idle )
11151144 yield (socket .IPPROTO_TCP , TCP_KEEPIDLE , idle )
11161145 TCP_KEEPINTVL = getattr (socket , 'TCP_KEEPINTVL' , 0x101 if sys .platform .startswith ('darwin' ) else None )
11171146 if TCP_KEEPINTVL is not None :
11181147 intvl = keepalive_intvl (timeout , idle , cnt )
11191148 yield (socket .IPPROTO_TCP , TCP_KEEPINTVL , intvl )
11201149 TCP_KEEPCNT = getattr (socket , 'TCP_KEEPCNT' , 0x102 if sys .platform .startswith ('darwin' ) else None )
11211150 if TCP_KEEPCNT is not None :
1151+ cnt = apply_keepalive_limit ('TCP_KEEPCNT' , cnt )
11221152 yield (socket .IPPROTO_TCP , TCP_KEEPCNT , cnt )
11231153
11241154
0 commit comments