Open mstyura opened 1 week ago
cc @mxinden might be of interest.
Many UDP sendmsg failures are transient (e.g. due to wifi dropping out, switching networks, or even spoofed ICMP messages), so we don't generally want to act on them too eagerly, but perhaps this case merits special treatment.
Maybe there is an kind of event that UDP socket rotation is required
While technically feasible, this seems tricky from an ergonomics standpoint. Users should be able to get reasonable behavior by default without having to know about this. Would it make sense for us to try to create a new socket at the same address whenever we get an error like that? How do we square that with user-provided sockets that might have other attached state we don't know about?
What happens to a TCP connection on iOS under the same circumstances?
Maybe additional API from Connection is exposed, like explicit ping method which can return io::Error from underlying UDP socket
I think this is likely to lead to people reimplementing the existing idle timeout mechanism, or inadvertently degrading it. We should try to handle this type of issue automatically.
Idle timer preferably should not restart when sendmsg call failed.
This is an interesting point. Even for transient errors, it's not constructive to sit around waiting for a response that we could've inferred won't happen. We'll need to tweak quinn-proto's API to support this, though. Maybe a Connection::handle_transmit_success
method, or similar?
What happens to a TCP connection on iOS under the same circumstances?
I believe every connection is in a broken state after app was in background and they are recreated. Here is how it looks from logs provided by system frameworks:
Steps to reproduce
quinn
to establishquic
connection to remote server in context ofiOS
application via code like this (only UDP socket configuration provided):iOS
application and lock the phone screen while previously established quic connection was alive;iOS
application and try to use previous quic connection such thatquinn
send some packets.Actual result
quinn
is unable to send any UDP packet over previously constructed UDP socket. It receives "Broken Pipe" error fromsendmsg
; Non of the IP packets leave the device;sendmsg
is called and failed, prolonging automatic detection of socket broken;quinn
connection is closed by local side once idle timeout is reached.Expected result
quinn
connection is not usable anymore.Endpoint::rebind
once problem with socket detected.Connection
is exposed, like explicitping
method which can returnio::Error
from underlying UDP socket and user code can trigger rebind or completeEndpoint/Connection
.sendmsg
call failed.More details
I've tracked down
EPIPE
error fromsendmsg
insideXNU
kernel. The backtrace to likely origin from XNU source code:It look like the socket state contains
SOF_DEFUNCT
orSS_CANTSENDMORE
so it's practically become unusable.According to
FreeBSD
documentationEPIPE
is also indicator of "you can not send more data via provided socket"See: https://man.freebsd.org/cgi/man.cgi?send(2)
UPD: The same "broken" state of socket produces
ENOTCONN
error when called withrecvmsg
, the origin of it seems to be this check in function soreceive.UPD 2: Seems
ENOTCONN
should also have some special treatment on Darwin https://github.com/libevent/libevent/pull/1031/files