pub struct TcpOpts {
pub nodelay: bool,
pub keepalive: Option<Duration>,
pub keepalive_interval: Option<Duration>,
pub keepalive_retries: Option<u32>,
pub bind_device: Option<String>,
pub user_timeout: Option<Duration>,
pub local_address: Option<IpAddr>,
pub send_buffer_size: Option<usize>,
pub recv_buffer_size: Option<usize>,
pub reuse_address: bool,
}Expand description
Socket options are applied in hclient once, on the socket2::Socket,
and the runtime only adopts the descriptor (TcpAdoptStd). Otherwise
every runtime crate would rewrite this whole rigmarole again.
§default() is all-off, and that was re-decided rather than inherited
Nagle’s algorithm costs the head of a Native TLS exchange 41 ms on
loopback — measured from the server’s side of the wire in
hclient-native’s tests/nagle_cost.rs, and 0.9 ms with nodelay set.
Every field here stays false/None anyway, for two reasons that are
not caution:
- This is a socket seam, and it does not know who is writing. The 41 ms is the write-write-read pattern of a request over TLS meeting a peer’s delayed ACK. A protocol that streams one way is exactly the one Nagle helps, and a default here would impose one caller’s protocol on every other caller of the trait.
- A set option is a refusal, not a preference.
TcpOpts::reject_unsupportedfails the connect on a runtime whoseTcpConnect::APPLIESdoes not cover it, and that default isNONE. Turning a field on here would turn every connect on a backend that forgot to declareAPPLIESinto anUnsupportederror for an option its caller never mentioned — a performance fix aimed straight at the implementors theNONEdefault was written to protect.
So the opinion lives where the protocol is: hclient_native::Native::new
asks for nodelay, and asks only where the runtime declares it applies
it.
Fields§
§nodelay: boolTCP_NODELAY — Nagle’s algorithm off. See the type’s own doc for
why default() leaves it false and who turns it on.
keepalive: Option<Duration>TCP_KEEPIDLE — how long a connection may be idle before the
first probe.
One setting in three parts, with
keepalive_interval and
keepalive_retries. Setting any of
the three turns SO_KEEPALIVE on; each part left None keeps the
operating system’s value for it. That is socket2::TcpKeepalive’s own shape
and it is stated here because the field names do not say it: a
caller who sets only the interval has switched keepalive on, with
the OS’s idle time.
keepalive_interval: Option<Duration>TCP_KEEPINTVL — the gap between probes once they have started.
Worth setting with keepalive rather than
instead of it: the idle time decides when a dead peer starts being
noticed and this decides how fast the noticing then goes, and
Linux’s defaults are 7200 s and 75 s, so an untouched idle time
makes the interval nearly irrelevant.
keepalive_retries: Option<u32>TCP_KEEPCNT — how many unanswered probes end the connection.
bind_device: Option<String>SO_BINDTODEVICE — the interface this socket must use, by name.
Not local_address under another name: an
address binds the source address, and the kernel still routes by
its table, so a request can leave through a different interface
that happens to hold the same address. This binds the interface,
which is what a caller on a multi-homed host or inside a VRF
actually means. Linux, Android and Fuchsia only — see
TcpOptsSupport, which is where a runtime says so per target.
A String rather than a &'static str because an interface name
is configuration a caller reads at run time, and rather than bytes
because every interface name on every platform that has this option
is ASCII.
user_timeout: Option<Duration>TCP_USER_TIMEOUT — how long transmitted data may stay
unacknowledged before the connection is dropped.
The one option here that catches a peer which vanished mid-transfer, where keepalive only catches an idle one: probes are sent when nothing is in flight, so a connection with unsent acknowledgements sits in retransmission for minutes with keepalive never firing. Linux, Android, Fuchsia and Cygwin only.
It overlaps Timeouts::between_bytes and does not replace it: this
is the kernel’s, applies to a socket rather than to an exchange, and
is the only one of the two that a build with no Client above it
can reach.
local_address: Option<IpAddr>§send_buffer_size: Option<usize>§recv_buffer_size: Option<usize>§reuse_address: boolImplementations§
Source§impl TcpOpts
impl TcpOpts
Sourcepub fn reject_unsupported(&self, can: TcpOptsSupport) -> Result<()>
pub fn reject_unsupported(&self, can: TcpOptsSupport) -> Result<()>
Fail when the caller set an option can says this runtime does not
apply — the one sanctioned answer to an option a runtime cannot
honour, since silently ignoring it is not one.
Only fields that are actually set can offend: TcpOpts::default
is all-off, so even a runtime with TcpOptsSupport::NONE still
serves every caller that never asked for anything.
A runtime whose TcpConnect::APPLIES is TcpOptsSupport::ALL
need not call this at all — the call is a no-op by construction,
which reject_unsupported_is_a_no_op_against_all pins.