diff options
-rw-r--r-- | lib/pure/asyncdispatch.nim | 20 | ||||
-rw-r--r-- | tests/async/tasyncconnect.nim | 33 | ||||
-rw-r--r-- | tests/async/tasynceverror.nim | 65 |
3 files changed, 112 insertions, 6 deletions
diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 2ce31b4e8..7523b29d5 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -972,9 +972,9 @@ else: let data = PData(info.key.data) assert data.fd == info.key.fd.AsyncFD #echo("In poll ", data.fd.cint) - if EvError in info.events: - closeSocket(data.fd) - continue + # There may be EvError here, but we handle them in callbacks, + # so that exceptions can be raised from `send(...)` and + # `recv(...)` routines. if EvRead in info.events: # Callback may add items to ``data.readCBs`` which causes issues if @@ -1013,9 +1013,17 @@ else: var retFuture = newFuture[void]("connect") proc cb(fd: AsyncFD): bool = - # We have connected. - retFuture.complete() - return true + var ret = SocketHandle(fd).getSockOptInt(cint(SOL_SOCKET), cint(SO_ERROR)) + if ret == 0: + # We have connected. + retFuture.complete() + return true + elif ret == EINTR: + # interrupted, keep waiting + return false + else: + retFuture.fail(newException(OSError, osErrorMsg(OSErrorCode(ret)))) + return true assert getSockDomain(socket.SocketHandle) == domain var aiList = getAddrInfo(address, port, domain) diff --git a/tests/async/tasyncconnect.nim b/tests/async/tasyncconnect.nim new file mode 100644 index 000000000..bc63b8e82 --- /dev/null +++ b/tests/async/tasyncconnect.nim @@ -0,0 +1,33 @@ +discard """ + file: "tasyncconnect.nim" + exitcode: 1 + outputsub: "Error: unhandled exception: Connection refused [Exception]" +""" + +import + asyncdispatch, + posix + + +const + testHost = "127.0.0.1" + testPort = Port(17357) + + +when defined(windows) or defined(nimdoc): + discard +else: + proc testAsyncConnect() {.async.} = + var s = newAsyncRawSocket() + + await s.connect(testHost, testPort) + + var peerAddr: SockAddr + var addrSize = Socklen(sizeof(peerAddr)) + var ret = SocketHandle(s).getpeername(addr(peerAddr), addr(addrSize)) + + if ret < 0: + echo("`connect(...)` failed but no exception was raised.") + quit(2) + + waitFor(testAsyncConnect()) diff --git a/tests/async/tasynceverror.nim b/tests/async/tasynceverror.nim new file mode 100644 index 000000000..3b81680cb --- /dev/null +++ b/tests/async/tasynceverror.nim @@ -0,0 +1,65 @@ +discard """ + file: "tasynceverror.nim" + exitcode: 1 + outputsub: "Error: unhandled exception: Connection reset by peer [Exception]" +""" + +import + asyncdispatch, + asyncnet, + rawsockets, + os + + +const + testHost = "127.0.0.1" + testPort = Port(17357) + + +when defined(windows) or defined(nimdoc): + discard +else: + proc createListenSocket(host: string, port: Port): TAsyncFD = + result = newAsyncRawSocket() + + SocketHandle(result).setSockOptInt(SOL_SOCKET, SO_REUSEADDR, 1) + + var aiList = getAddrInfo(host, port, AF_INET) + if SocketHandle(result).bindAddr(aiList.ai_addr, aiList.ai_addrlen.Socklen) < 0'i32: + dealloc(aiList) + raiseOSError(osLastError()) + dealloc(aiList) + + if SocketHandle(result).listen(1) < 0'i32: + raiseOSError(osLastError()) + + + proc testAsyncSend() {.async.} = + var + ls = createListenSocket(testHost, testPort) + s = newAsyncSocket() + + await s.connect(testHost, testPort) + + var ps = await ls.accept() + SocketHandle(ls).close() + + await ps.send("test 1", flags={}) + s.close() + # This send should raise EPIPE + await ps.send("test 2", flags={}) + SocketHandle(ps).close() + + + # The bug was, when the poll function handled EvError for us, + # our callbacks may never get executed, thus making the event + # loop block indefinitely. This is a timer to keep everything + # rolling. 400 ms is an arbitrary value, should be enough though. + proc timer() {.async.} = + await sleepAsync(400) + echo("Timer expired.") + quit(2) + + + asyncCheck(testAsyncSend()) + waitFor(timer()) |