diff options
Diffstat (limited to 'lib/pure/asyncio.nim')
-rw-r--r-- | lib/pure/asyncio.nim | 54 |
1 files changed, 49 insertions, 5 deletions
diff --git a/lib/pure/asyncio.nim b/lib/pure/asyncio.nim index 5ecc16de7..be375a1a1 100644 --- a/lib/pure/asyncio.nim +++ b/lib/pure/asyncio.nim @@ -35,6 +35,9 @@ import sockets, os ## that in the future this type's fields will not be exported therefore breaking ## your code. ## +## **Warning:** The API of this module is unstable, and therefore is subject +## to change. +## ## Asynchronous sockets ## ==================== ## @@ -123,6 +126,7 @@ type handleTask*: proc (s: PAsyncSocket) {.closure.} lineBuffer: TaintedString ## Temporary storage for ``recvLine`` + sendBuffer: string ## Temporary storage for ``send`` sslNeedAccept: bool proto: TProtocol deleg: PDelegate @@ -152,6 +156,7 @@ proc newAsyncSocket(): PAsyncSocket = result.handleTask = (proc (s: PAsyncSocket) = nil) result.lineBuffer = "".TaintedString + result.sendBuffer = "" proc AsyncSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM, protocol: TProtocol = IPPROTO_TCP, @@ -222,10 +227,22 @@ proc asyncSockHandleWrite(h: PObject) = else: PAsyncSocket(h).deleg.mode = fmReadWrite else: - if PAsyncSocket(h).handleWrite != nil: - PAsyncSocket(h).handleWrite(PAsyncSocket(h)) + if PAsyncSocket(h).sendBuffer != "": + let sock = PAsyncSocket(h) + let bytesSent = sock.socket.sendAsync(sock.sendBuffer) + assert bytesSent > 0 + if bytesSent != sock.sendBuffer.len: + sock.sendBuffer = sock.sendBuffer[bytesSent .. -1] + elif bytesSent == sock.sendBuffer.len: + sock.sendBuffer = "" + + if PAsyncSocket(h).handleWrite != nil: + PAsyncSocket(h).handleWrite(PAsyncSocket(h)) else: - PAsyncSocket(h).deleg.mode = fmRead + if PAsyncSocket(h).handleWrite != nil: + PAsyncSocket(h).handleWrite(PAsyncSocket(h)) + else: + PAsyncSocket(h).deleg.mode = fmRead when defined(ssl): proc asyncSockDoHandshake(h: PObject) = @@ -337,7 +354,8 @@ proc acceptAddr*(server: PAsyncSocket, client: var PAsyncSocket, # deleg.open is set in ``toDelegate``. client.socket = c - client.lineBuffer = "" + client.lineBuffer = "".TaintedString + client.sendBuffer = "" client.info = SockConnected proc accept*(server: PAsyncSocket, client: var PAsyncSocket) = @@ -422,6 +440,9 @@ proc recvLine*(s: PAsyncSocket, line: var TaintedString): bool = ## sockets properly. This function guarantees that ``line`` is a full line, ## if this function can only retrieve some data; it will save this data and ## add it to the result when a full line is retrieved. + ## + ## Unlike ``sockets.recvLine`` this function will raise an EOS or ESSL + ## exception if an error occurs. setLen(line.string, 0) var dataReceived = "".TaintedString var ret = s.socket.recvLineAsync(dataReceived) @@ -440,8 +461,29 @@ proc recvLine*(s: PAsyncSocket, line: var TaintedString): bool = of RecvDisconnected: result = true of RecvFail: + s.SocketError(async = true) result = false +proc send*(sock: PAsyncSocket, data: string) = + ## Sends ``data`` to socket ``sock``. This is basically a nicer implementation + ## of ``sockets.sendAsync``. + ## + ## If ``data`` cannot be sent immediately it will be buffered and sent + ## when ``sock`` becomes writeable (during the ``handleWrite`` event). + ## It's possible that only a part of ``data`` will be sent immediately, while + ## the rest of it will be buffered and sent later. + if sock.sendBuffer.len != 0: + sock.sendBuffer.add(data) + return + let bytesSent = sock.socket.sendAsync(data) + assert bytesSent >= 0 + if bytesSent == 0: + sock.sendBuffer.add(data) + sock.deleg.mode = fmReadWrite + elif bytesSent != data.len: + sock.sendBuffer.add(data[bytesSent .. -1]) + sock.deleg.mode = fmReadWrite + proc timeValFromMilliseconds(timeout = 500): TTimeVal = if timeout != -1: var seconds = timeout div 1000 @@ -556,7 +598,9 @@ when isMainModule: proc testRead(s: PAsyncSocket, no: int) = echo("Reading! " & $no) - var data = s.getSocket.recv() + var data = "" + if not s.recvLine(data): + OSError() if data == "": echo("Closing connection. " & $no) s.close() |