summary refs log tree commit diff stats
path: root/lib/pure/asyncio.nim
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pure/asyncio.nim')
-rw-r--r--lib/pure/asyncio.nim54
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()