@@ -63,7 +63,7 @@ pub struct RemoteClientConfig {
6363 pub idle_connections : usize ,
6464 // Determines client connection timeouts. Used plainly for HTTP/2 connections, and with a
6565 // `TCP_KEEPALIVE_FACTOR` for TCP connections.
66- #[ validate( custom( function = "validate_tcp_exceeds_http_keepalive " ) ) ]
66+ #[ validate( custom( function = "validate_keepalive_timeout_ms " ) ) ]
6767 pub keepalive_timeout_ms : u64 ,
6868 pub attempts_per_log : usize ,
6969 pub initial_retry_delay_ms : u64 ,
@@ -91,30 +91,51 @@ impl Default for RemoteClientConfig {
9191 }
9292}
9393
94- /// Validates that the TCP keepalive duration (at second granularity, as the OS stores
95- /// `TCP_KEEPIDLE` in whole seconds) is greater than or equal to the HTTP keepalive duration
96- /// (millisecond granularity). If the configured `keepalive_timeout_ms * TCP_KEEPALIVE_FACTOR` is
97- /// less than 1 second, truncation to whole seconds yields 0 s, making the TCP keepalive shorter
98- /// than the HTTP keepalive.
99- fn validate_tcp_exceeds_http_keepalive ( keepalive_timeout_ms : u64 ) -> Result < ( ) , ValidationError > {
94+ /// Validates that `keepalive_timeout_ms` is positive, and that the derived TCP keepalive duration
95+ /// (at second granularity, as the OS stores `TCP_KEEPIDLE` in whole seconds) is greater than or
96+ /// equal to the HTTP keepalive duration (millisecond granularity). If the configured
97+ /// `keepalive_timeout_ms * TCP_KEEPALIVE_FACTOR` rounds to 0 s, the kernel rejects the socket
98+ /// option with `EINVAL`. If it is less than `keepalive_timeout_ms`, the TCP keepalive fires before
99+ /// the HTTP-level timeout.
100+ pub ( crate ) fn validate_keepalive_timeout_ms (
101+ keepalive_timeout_ms : u64 ,
102+ ) -> Result < ( ) , ValidationError > {
103+ if keepalive_timeout_ms == 0 {
104+ return Err ( create_validation_error (
105+ "keepalive_timeout_ms must be greater than 0" . to_string ( ) ,
106+ "keepalive_timeout_ms_zero" ,
107+ "keepalive_timeout_ms must be > 0." ,
108+ ) ) ;
109+ }
100110 let http_keepalive = Duration :: from_millis ( keepalive_timeout_ms) ;
101111 let tcp_keepalive_raw = http_keepalive. mul_f64 ( TCP_KEEPALIVE_FACTOR ) ;
102112 // TCP_KEEPIDLE is stored in whole seconds; fractional seconds are truncated by the OS.
103- let tcp_keepalive = Duration :: from_secs ( tcp_keepalive_raw. as_secs ( ) ) ;
104- if tcp_keepalive >= http_keepalive {
105- Ok ( ( ) )
106- } else {
107- Err ( create_validation_error (
113+ let tcp_keepalive_secs = tcp_keepalive_raw. as_secs ( ) ;
114+ if tcp_keepalive_secs == 0 {
115+ return Err ( create_validation_error (
116+ format ! (
117+ "TCP keepalive rounds to 0 s (keepalive_timeout_ms={keepalive_timeout_ms}, \
118+ factor={TCP_KEEPALIVE_FACTOR}): increase keepalive_timeout_ms so that \
119+ keepalive_timeout_ms * {TCP_KEEPALIVE_FACTOR} is at least 1 s",
120+ ) ,
121+ "tcp_keepalive_zero" ,
122+ "TCP keepalive (second granularity) must be > 0 s." ,
123+ ) ) ;
124+ }
125+ let tcp_keepalive = Duration :: from_secs ( tcp_keepalive_secs) ;
126+ if tcp_keepalive < http_keepalive {
127+ return Err ( create_validation_error (
108128 format ! (
109- "TCP keepalive ({} s) is shorter than HTTP keepalive ({keepalive_timeout_ms} ms): \
110- increase keepalive_timeout_ms so that keepalive_timeout_ms * \
111- {TCP_KEEPALIVE_FACTOR} rounds to at least {keepalive_timeout_ms} ms" ,
112- tcp_keepalive . as_secs ( ) ,
129+ "TCP keepalive ({tcp_keepalive_secs } s) is shorter than HTTP keepalive \
130+ ({keepalive_timeout_ms} ms): increase keepalive_timeout_ms so that \
131+ keepalive_timeout_ms * {TCP_KEEPALIVE_FACTOR} rounds to at least \
132+ {keepalive_timeout_ms} ms" ,
113133 ) ,
114134 "tcp_keepalive_shorter_than_http_keepalive" ,
115135 "TCP keepalive (second granularity) must be >= HTTP keepalive (ms granularity)." ,
116- ) )
136+ ) ) ;
117137 }
138+ Ok ( ( ) )
118139}
119140
120141impl SerializeConfig for RemoteClientConfig {
0 commit comments