diff --git a/source/eventcore/driver.d b/source/eventcore/driver.d index c05181b..8b4f07c 100644 --- a/source/eventcore/driver.d +++ b/source/eventcore/driver.d @@ -128,6 +128,19 @@ interface EventDriverSockets { StreamSocketFD connectStream(scope Address peer_address, scope Address bind_address, ConnectCallback on_connect); /** Aborts asynchronous connect by closing the socket. + + This function may only invoked if the connection state is + `ConnectionState.connecting`. It will cancel the connection attempt and + guarantees that the connection callback will not be invoked in the + future. + + Note that upon completion, the socket handle will be invalid, regardless + of the number of calls to `addRef`, and must not be used for further + operations. + + Params: + sock = Handle of the socket that is currently establishing a + connection */ void cancelConnectStream(StreamSocketFD sock); diff --git a/source/eventcore/drivers/winapi/sockets.d b/source/eventcore/drivers/winapi/sockets.d index 7015df0..4e413d1 100644 --- a/source/eventcore/drivers/winapi/sockets.d +++ b/source/eventcore/drivers/winapi/sockets.d @@ -84,7 +84,15 @@ final class WinAPIEventDriverSockets : EventDriverSockets { final override void cancelConnectStream(StreamSocketFD sock) { - assert(false, "Not implemented"); + assert(sock != StreamSocketFD.invalid, "Invalid socket descriptor"); + + with (m_sockets[sock].streamSocket) { + assert(state == ConnectionState.connecting, + "Must be in 'connecting' state when calling cancelConnection."); + + clearSocketSlot(sock); + () @trusted { closesocket(sock); } (); + } } override StreamSocketFD adoptStream(int socket) diff --git a/tests/0-tcp-cancelconn.d b/tests/0-tcp-cancelconn.d new file mode 100644 index 0000000..6a24452 --- /dev/null +++ b/tests/0-tcp-cancelconn.d @@ -0,0 +1,40 @@ +/++ dub.sdl: + name "test" + dependency "eventcore" path=".." ++/ +module test; + +import eventcore.core; +import eventcore.socket; +import std.socket : parseAddress; +import core.time : Duration, msecs; + +ubyte[256] s_rbuf; +bool s_done; + +void main() +{ + // (hopefully) non-existant host, so that the connection times out + auto baddr = parseAddress("192.0.2.1", 1); + + auto sock = eventDriver.sockets.connectStream(baddr, null, (sock, status) { + assert(false, "Connection callback should not have been called."); + }); + assert(sock != StreamSocketFD.invalid, "Expected connection to be in progress."); + assert(eventDriver.sockets.getConnectionState(sock) == ConnectionState.connecting); + + auto tm = eventDriver.timers.create(); + eventDriver.timers.set(tm, 100.msecs, 0.msecs); + eventDriver.timers.wait(tm, (tm) { + assert(eventDriver.sockets.getConnectionState(sock) == ConnectionState.connecting); + eventDriver.sockets.cancelConnectStream(sock); + s_done = true; + }); + + ExitReason er; + do er = eventDriver.core.processEvents(Duration.max); + while (er == ExitReason.idle); + assert(er == ExitReason.outOfWaiters); + assert(s_done); + s_done = false; +}