diff --git a/source/eventcore/driver.d b/source/eventcore/driver.d index 2c95492..eab444f 100644 --- a/source/eventcore/driver.d +++ b/source/eventcore/driver.d @@ -228,6 +228,9 @@ interface EventDriverSockets { */ void setKeepAliveParams(StreamSocketFD socket, Duration idle, Duration interval, int probeCount = 5); + /// Sets `TCP_USER_TIMEOUT` socket option (linux only). https://tools.ietf.org/html/rfc5482 + void setUserTimeout(StreamSocketFD socket, Duration timeout); + /** Reads data from a stream socket. Note that only a single read operation is allowed at once. The caller diff --git a/source/eventcore/drivers/posix/sockets.d b/source/eventcore/drivers/posix/sockets.d index 1abeb66..3d0c1fd 100644 --- a/source/eventcore/drivers/posix/sockets.d +++ b/source/eventcore/drivers/posix/sockets.d @@ -50,10 +50,12 @@ version (linux) { // Linux-specific TCP options // https://github.com/torvalds/linux/blob/master/include/uapi/linux/tcp.h#L95 + // Some day we should siply import core.sys.linux.netinet.tcp; enum SOL_TCP = 6; enum TCP_KEEPIDLE = 4; enum TCP_KEEPINTVL = 5; enum TCP_KEEPCNT = 6; + enum TCP_USER_TIMEOUT = 18; } version(OSX) { static if (__VERSION__ < 2077) { @@ -303,22 +305,54 @@ final class PosixEventDriverSockets(Loop : PosixEventLoop) : EventDriverSockets () @trusted { setsockopt(cast(sock_t)socket, IPPROTO_TCP, TCP_NODELAY, cast(char*)&opt, opt.sizeof); } (); } - final override void setKeepAlive(StreamSocketFD socket, bool enable) + override void setKeepAlive(StreamSocketFD socket, bool enable) @trusted { - ubyte opt = enable; - () @trusted { setsockopt(cast(sock_t)socket, SOL_SOCKET, SO_KEEPALIVE, cast(char*)&opt, opt.sizeof); } (); + int opt = enable; + int err = setsockopt(cast(sock_t)socket, SOL_SOCKET, SO_KEEPALIVE, &opt, int.sizeof); + if (err != 0) + { + print("sock error %s", getSocketError); + assert(0, "unable to set SO_KEEPALIVE option"); + } } override void setKeepAliveParams(StreamSocketFD socket, Duration idle, Duration interval, int probeCount) @trusted { version (linux) { - ubyte opt = 1; - setsockopt(cast(sock_t)socket, SOL_SOCKET, SO_KEEPALIVE, cast(char*)&opt, opt.sizeof); + setKeepAlive(socket, true); int int_opt = cast(int) idle.total!"seconds"(); - setsockopt(cast(sock_t)socket, SOL_TCP, TCP_KEEPIDLE, &int_opt, int.sizeof); + int err = setsockopt(cast(sock_t)socket, SOL_TCP, TCP_KEEPIDLE, &int_opt, int.sizeof); + if (err != 0) + { + print("sock error %s", getSocketError); + assert(0, "unable to set TCP_KEEPIDLE option"); + } int_opt = cast(int) interval.total!"seconds"(); - setsockopt(cast(sock_t)socket, SOL_TCP, TCP_KEEPINTVL, &int_opt, int.sizeof); - setsockopt(cast(sock_t)socket, SOL_TCP, TCP_KEEPCNT, &probeCount, int.sizeof); + err = setsockopt(cast(sock_t)socket, SOL_TCP, TCP_KEEPINTVL, &int_opt, int.sizeof); + if (err != 0) + { + print("sock error %s", getSocketError); + assert(0, "unable to set TCP_KEEPINTVL option"); + } + err = setsockopt(cast(sock_t)socket, SOL_TCP, TCP_KEEPCNT, &probeCount, int.sizeof); + if (err != 0) + { + print("sock error %s", getSocketError); + assert(0, "unable to set TCP_KEEPCNT option"); + } + } + } + + override void setUserTimeout(StreamSocketFD socket, Duration timeout) @trusted + { + version (linux) { + uint tmsecs = cast(uint) timeout.total!"msecs"; + int err = setsockopt(cast(sock_t)socket, SOL_TCP, TCP_USER_TIMEOUT, &tmsecs, uint.sizeof); + if (err != 0) + { + print("sock error %s", getSocketError); + assert(0, "unable to set TCP_USER_TIMEOUT option"); + } } } diff --git a/source/eventcore/drivers/winapi/sockets.d b/source/eventcore/drivers/winapi/sockets.d index 8ffeb57..4b59e32 100644 --- a/source/eventcore/drivers/winapi/sockets.d +++ b/source/eventcore/drivers/winapi/sockets.d @@ -223,16 +223,18 @@ final class WinAPIEventDriverSockets : EventDriverSockets { override void setKeepAliveParams(StreamSocketFD socket, Duration idle, Duration interval, int probeCount) @trusted { - if (idle < Duration.zero) - assert(0, "negative idle duration"); - if (interval < Duration.zero) - assert(0, "negative interval duration"); tcp_keepalive opts = tcp_keepalive(1, cast(ulong) idle.total!"msecs"(), cast(ulong) interval.total!"msecs"); int result = WSAIoctl(socket, SIO_KEEPALIVE_VALS, &opts, tcp_keepalive.sizeof, null, 0, null, null); - assert(result == 0); + if (result != 0) + { + print("WSAIoctl SIO_KEEPALIVE_VALS returned %d", result); + assert(0, "unable to set TCP keepAlive parameters"); + } } + override void setUserTimeout(StreamSocketFD socket, Duration timeout) {} + override void read(StreamSocketFD socket, ubyte[] buffer, IOMode mode, IOCallback on_read_finish) { auto slot = () @trusted { return &m_sockets[socket].streamSocket(); } ();