From 52679849857b9a289ac5c75c420e6bce8680b3c7 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 23 Mar 2013 20:13:11 +0000 Subject: [PATCH] Deprecated recvLine and added an improved version named readLine to the sockets module. --- lib/pure/asyncio.nim | 40 ++++++++++++++++- lib/pure/sockets.nim | 90 ++++++++++++++++++++++++++++++++++++++- tests/run/tasynciossl.nim | 2 +- 3 files changed, 127 insertions(+), 5 deletions(-) diff --git a/lib/pure/asyncio.nim b/lib/pure/asyncio.nim index be375a1a10..c5c1f56963 100644 --- a/lib/pure/asyncio.nim +++ b/lib/pure/asyncio.nim @@ -8,7 +8,8 @@ import sockets, os -## This module implements an asynchronous event loop for sockets. +## This module implements an asynchronous event loop together with asynchronous sockets +## which use this event loop. ## It is akin to Python's asyncore module. Many modules that use sockets ## have an implementation for this module, those modules should all have a ## ``register`` function which you should use to add the desired objects to a @@ -435,7 +436,8 @@ proc delHandleWrite*(s: PAsyncSocket) = ## Removes the ``handleWrite`` event handler on ``s``. s.handleWrite = nil -proc recvLine*(s: PAsyncSocket, line: var TaintedString): bool = +{.push warning[deprecated]: off.} +proc recvLine*(s: PAsyncSocket, line: var TaintedString): bool {.deprecated.} = ## Behaves similar to ``sockets.recvLine``, however it handles non-blocking ## 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 @@ -443,6 +445,9 @@ proc recvLine*(s: PAsyncSocket, line: var TaintedString): bool = ## ## Unlike ``sockets.recvLine`` this function will raise an EOS or ESSL ## exception if an error occurs. + ## + ## **Deprecated since version 0.9.2**: This function has been deprecated in + ## favour of readLine. setLen(line.string, 0) var dataReceived = "".TaintedString var ret = s.socket.recvLineAsync(dataReceived) @@ -463,6 +468,37 @@ proc recvLine*(s: PAsyncSocket, line: var TaintedString): bool = of RecvFail: s.SocketError(async = true) result = false +{.pop.} + +proc readLine*(s: PAsyncSocket, line: var TaintedString): bool = + ## Behaves similar to ``sockets.readLine``, however it handles non-blocking + ## 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, when this happens + ## False will be returned. True will only be returned if a full line has been + ## retrieved or the socket has been disconnected in which case ``line`` will + ## be set to "". + ## + ## This function will raise an EOS exception when a socket error occurs. + setLen(line.string, 0) + var dataReceived = "".TaintedString + var ret = s.socket.readLineAsync(dataReceived) + case ret + of ReadFullLine: + if s.lineBuffer.len > 0: + string(line).add(s.lineBuffer.string) + setLen(s.lineBuffer.string, 0) + string(line).add(dataReceived.string) + if string(line) == "": + line = "\c\L".TaintedString + result = true + of ReadPartialLine: + string(s.lineBuffer).add(dataReceived.string) + result = false + of ReadNone: + result = false + of ReadDisconnected: + result = true proc send*(sock: PAsyncSocket, data: string) = ## Sends ``data`` to socket ``sock``. This is basically a nicer implementation diff --git a/lib/pure/sockets.nim b/lib/pure/sockets.nim index e70fbd09aa..2d602c86e6 100644 --- a/lib/pure/sockets.nim +++ b/lib/pure/sockets.nim @@ -121,6 +121,9 @@ type TRecvLineResult* = enum ## result for recvLineAsync RecvFullLine, RecvPartialLine, RecvDisconnected, RecvFail + TReadLineResult* = enum ## result for readLineAsync + ReadFullLine, ReadPartialLine, ReadDisconnected, ReadNone + ETimeout* = object of ESynch proc newTSocket(fd: int32, isBuff: bool): TSocket = @@ -1180,7 +1183,7 @@ proc peekChar(socket: TSocket, c: var char): int {.tags: [FReadIO].} = result = recv(socket.fd, addr(c), 1, MSG_PEEK) proc recvLine*(socket: TSocket, line: var TaintedString, timeout = -1): bool {. - tags: [FReadIO, FTime].} = + tags: [FReadIO, FTime], deprecated.} = ## Receive a line of data from ``socket``. ## ## If a full line is received ``\r\L`` is not @@ -1196,6 +1199,10 @@ proc recvLine*(socket: TSocket, line: var TaintedString, timeout = -1): bool {. ## ## A timeout can be specified in miliseconds, if data is not received within ## the specified time an ETimeout exception will be raised. + ## + ## **Deprecated since version 0.9.2**: This function has been deprecated in + ## favour of readLine. + template addNLIfEmpty(): stmt = if line.len == 0: line.add("\c\L") @@ -1222,8 +1229,49 @@ proc recvLine*(socket: TSocket, line: var TaintedString, timeout = -1): bool {. return true add(line.string, c) +proc readLine*(socket: TSocket, line: var TaintedString, timeout = -1) {. + tags: [FReadIO, FTime].} = + ## Reads a line of data from ``socket``. + ## + ## If a full line is read ``\r\L`` is not + ## added to ``line``, however if solely ``\r\L`` is read then ``line`` + ## will be set to it. + ## + ## If the socket is disconnected, ``line`` will be set to ``""``. + ## + ## An EOS exception will be raised in the case of a socket error. + ## + ## A timeout can be specified in miliseconds, if data is not received within + ## the specified time an ETimeout exception will be raised. + + template addNLIfEmpty(): stmt = + if line.len == 0: + line.add("\c\L") + + var waited = 0.0 + + setLen(line.string, 0) + while true: + var c: char + discard waitFor(socket, waited, timeout, 1, "readLine") + var n = recv(socket, addr(c), 1) + if n < 0: OSError() + elif n == 0: return + if c == '\r': + discard waitFor(socket, waited, timeout, 1, "readLine") + n = peekChar(socket, c) + if n > 0 and c == '\L': + discard recv(socket, addr(c), 1) + elif n <= 0: OSError() + addNlIfEmpty() + return + elif c == '\L': + addNlIfEmpty() + return + add(line.string, c) + proc recvLineAsync*(socket: TSocket, - line: var TaintedString): TRecvLineResult {.tags: [FReadIO].} = + line: var TaintedString): TRecvLineResult {.tags: [FReadIO], deprecated.} = ## Similar to ``recvLine`` but designed for non-blocking sockets. ## ## The values of the returned enum should be pretty self explanatory: @@ -1232,6 +1280,10 @@ proc recvLineAsync*(socket: TSocket, ## * If some data has been retrieved; ``RecvPartialLine`` is returned. ## * If the socket has been disconnected; ``RecvDisconnected`` is returned. ## * If call to ``recv`` failed; ``RecvFail`` is returned. + ## + ## **Deprecated since version 0.9.2**: This function has been deprecated in + ## favour of readLineAsync. + setLen(line.string, 0) while true: var c: char @@ -1250,6 +1302,40 @@ proc recvLineAsync*(socket: TSocket, elif c == '\L': return RecvFullLine add(line.string, c) +proc readLineAsync*(socket: TSocket, + line: var TaintedString): TReadLineResult {.tags: [FReadIO].} = + ## Similar to ``recvLine`` but designed for non-blocking sockets. + ## + ## The values of the returned enum should be pretty self explanatory: + ## + ## * If a full line has been retrieved; ``ReadFullLine`` is returned. + ## * If some data has been retrieved; ``ReadPartialLine`` is returned. + ## * If the socket has been disconnected; ``ReadDisconnected`` is returned. + ## * If no data could be retrieved; ``ReadNone`` is returned. + ## * If call to ``recv`` failed; **an EOS exception is raised.** + setLen(line.string, 0) + + template errorOrNone = + socket.SocketError(async = true) + return ReadNone + + while true: + var c: char + var n = recv(socket, addr(c), 1) + if n < 0: + if line.len == 0: errorOrNone else: return ReadPartialLine + elif n == 0: + return (if line.len == 0: ReadDisconnected else: ReadPartialLine) + if c == '\r': + n = peekChar(socket, c) + if n > 0 and c == '\L': + discard recv(socket, addr(c), 1) + elif n <= 0: + if line.len == 0: errorOrNone else: return ReadPartialLine + return ReadFullLine + elif c == '\L': return ReadFullLine + add(line.string, c) + proc recv*(socket: TSocket): TaintedString {.tags: [FReadIO], deprecated.} = ## receives all the available data from the socket. ## Socket errors will result in an ``EOS`` error. diff --git a/tests/run/tasynciossl.nim b/tests/run/tasynciossl.nim index e5fb9610ca..fbed46efb0 100644 --- a/tests/run/tasynciossl.nim +++ b/tests/run/tasynciossl.nim @@ -26,7 +26,7 @@ proc swarmConnect(s: PAsyncSocket) = proc serverRead(s: PAsyncSocket) = var line = "" - assert s.recvLine(line) + assert s.readLine(line) if line != "": #echo(line) if line.startsWith("Message "):