diff options
-rw-r--r-- | lib/pure/asyncio.nim | 43 | ||||
-rw-r--r-- | lib/pure/ftpclient.nim | 18 | ||||
-rwxr-xr-x | lib/pure/sockets.nim | 22 |
3 files changed, 65 insertions, 18 deletions
diff --git a/lib/pure/asyncio.nim b/lib/pure/asyncio.nim index 0f5197792..133d8c24e 100644 --- a/lib/pure/asyncio.nim +++ b/lib/pure/asyncio.nim @@ -126,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 @@ -155,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, @@ -225,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) = @@ -340,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) = @@ -445,6 +460,26 @@ proc recvLine*(s: PAsyncSocket, line: var TaintedString): bool = of RecvFail: 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 diff --git a/lib/pure/ftpclient.nim b/lib/pure/ftpclient.nim index 78c96c3f1..ad3d7a3bb 100644 --- a/lib/pure/ftpclient.nim +++ b/lib/pure/ftpclient.nim @@ -454,11 +454,13 @@ proc doUpload(ftp: PFTPClient, async = false): bool = if ftp.dsockConnected: if ftp.job.toStore.len() > 0: assert(async) - if ftp.asyncDSock.sendAsync(ftp.job.toStore): + let bytesSent = ftp.asyncDSock.sendAsync(ftp.job.toStore) + if bytesSent == ftp.job.toStore.len: ftp.job.toStore = "" - ftp.job.progress.inc(ftp.job.toStore.len) - ftp.job.oneSecond.inc(ftp.job.toStore.len) - + elif bytesSent != ftp.job.toStore.len and bytesSent != 0: + ftp.job.toStore = ftp.job.toStore[bytesSent .. -1] + ftp.job.progress.inc(bytesSent) + ftp.job.oneSecond.inc(bytesSent) else: var s = newStringOfCap(4000) var len = ftp.job.file.readBuffer(addr(s[0]), 4000) @@ -476,8 +478,12 @@ proc doUpload(ftp: PFTPClient, async = false): bool = if not async: getDSock(ftp).send(s) else: - if not ftp.asyncDSock.sendAsync(s): - ftp.job.toStore = s + let bytesSent = ftp.asyncDSock.sendAsync(s) + if bytesSent == 0: + ftp.job.toStore.add(s) + elif bytesSent != s.len: + ftp.job.toStore.add(s[bytesSent .. -1]) + len = bytesSent ftp.job.progress.inc(len) ftp.job.oneSecond.inc(len) diff --git a/lib/pure/sockets.nim b/lib/pure/sockets.nim index 01b97197e..e0eae196f 100755 --- a/lib/pure/sockets.nim +++ b/lib/pure/sockets.nim @@ -1317,13 +1317,18 @@ proc send*(socket: TSocket, data: string) {.tags: [FWriteIO].} = OSError() -proc sendAsync*(socket: TSocket, data: string): bool {.tags: [FWriteIO].} = - ## sends data to a non-blocking socket. Returns whether ``data`` was sent. - result = true - var bytesSent = send(socket, cstring(data), data.len) +proc sendAsync*(socket: TSocket, data: string): int {.tags: [FWriteIO].} = + ## sends data to a non-blocking socket. + ## Returns ``0`` if no data could be sent, if data has been sent + ## returns the amount of bytes of ``data`` that was successfully sent. This + ## number may not always be the length of ``data`` but typically is. + ## + ## An EOS (or ESSL if socket is an SSL socket) exception is raised if an error + ## occurs. + result = send(socket, cstring(data), data.len) when defined(ssl): if socket.isSSL: - if bytesSent <= 0: + if result <= 0: let ret = SSLGetError(socket.sslHandle, bytesSent.cint) case ret of SSL_ERROR_ZERO_RETURN: @@ -1339,17 +1344,18 @@ proc sendAsync*(socket: TSocket, data: string): bool {.tags: [FWriteIO].} = else: SSLError("Unknown Error") else: return - if bytesSent == -1: + if result == -1: when defined(windows): var err = WSAGetLastError() # TODO: Test on windows. if err == WSAEINPROGRESS: - return false + return 0 else: OSError() else: if errno == EAGAIN or errno == EWOULDBLOCK: - return false + return 0 else: OSError() + proc trySend*(socket: TSocket, data: string): bool {.tags: [FWriteIO].} = ## safe alternative to ``send``. Does not raise an EOS when an error occurs, |