mirror of
https://github.com/nim-lang/Nim.git
synced 2026-02-15 07:43:26 +00:00
Move recvInto to asyncnet and don't export it
This commit is contained in:
@@ -542,94 +542,6 @@ when defined(windows) or defined(nimdoc):
|
||||
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
||||
return retFuture
|
||||
|
||||
proc recvInto*(socket: TAsyncFD, buf: cstring, size: int,
|
||||
flags = {SocketFlag.SafeDisconn}): Future[int] =
|
||||
## Reads **up to** ``size`` bytes from ``socket`` into ``buf``, which must
|
||||
## at least be of that size. Returned future will complete once all the
|
||||
## data requested is read, a part of the data has been read, or the socket
|
||||
## has disconnected in which case the future will complete with a value of
|
||||
## ``0``.
|
||||
##
|
||||
## **Warning**: The ``Peek`` socket flag is not supported on Windows.
|
||||
|
||||
|
||||
# Things to note:
|
||||
# * When WSARecv completes immediately then ``bytesReceived`` is very
|
||||
# unreliable.
|
||||
# * Still need to implement message-oriented socket disconnection,
|
||||
# '\0' in the message currently signifies a socket disconnect. Who
|
||||
# knows what will happen when someone sends that to our socket.
|
||||
verifyPresence(socket)
|
||||
assert SocketFlag.Peek notin flags, "Peek not supported on Windows."
|
||||
|
||||
var retFuture = newFuture[int]("recvInto")
|
||||
|
||||
buf[0] = '\0'
|
||||
var dataBuf: TWSABuf
|
||||
dataBuf.buf = buf
|
||||
dataBuf.len = size
|
||||
|
||||
var bytesReceived: Dword
|
||||
var flagsio = flags.toOSFlags().Dword
|
||||
var ol = PCustomOverlapped()
|
||||
GC_ref(ol)
|
||||
ol.data = TCompletionData(fd: socket, cb:
|
||||
proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
|
||||
if not retFuture.finished:
|
||||
if errcode == OSErrorCode(-1):
|
||||
if bytesCount == 0 and dataBuf.buf[0] == '\0':
|
||||
retFuture.complete(0)
|
||||
else:
|
||||
retFuture.complete(bytesCount)
|
||||
else:
|
||||
if flags.isDisconnectionError(errcode):
|
||||
retFuture.complete(0)
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(errcode)))
|
||||
if dataBuf.buf != nil:
|
||||
dataBuf.buf = nil
|
||||
)
|
||||
|
||||
let ret = WSARecv(socket.SocketHandle, addr dataBuf, 1, addr bytesReceived,
|
||||
addr flagsio, cast[POVERLAPPED](ol), nil)
|
||||
if ret == -1:
|
||||
let err = osLastError()
|
||||
if err.int32 != ERROR_IO_PENDING:
|
||||
if dataBuf.buf != nil:
|
||||
dataBuf.buf = nil
|
||||
GC_unref(ol)
|
||||
if flags.isDisconnectionError(err):
|
||||
retFuture.complete(0)
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(err)))
|
||||
elif ret == 0 and bytesReceived == 0 and dataBuf.buf[0] == '\0':
|
||||
# We have to ensure that the buffer is empty because WSARecv will tell
|
||||
# us immediately when it was disconnected, even when there is still
|
||||
# data in the buffer.
|
||||
# We want to give the user as much data as we can. So we only return
|
||||
# the empty string (which signals a disconnection) when there is
|
||||
# nothing left to read.
|
||||
retFuture.complete(0)
|
||||
# TODO: "For message-oriented sockets, where a zero byte message is often
|
||||
# allowable, a failure with an error code of WSAEDISCON is used to
|
||||
# indicate graceful closure."
|
||||
# ~ http://msdn.microsoft.com/en-us/library/ms741688%28v=vs.85%29.aspx
|
||||
else:
|
||||
# Request to read completed immediately.
|
||||
# From my tests bytesReceived isn't reliable.
|
||||
let realSize =
|
||||
if bytesReceived == 0:
|
||||
size
|
||||
else:
|
||||
bytesReceived
|
||||
assert realSize <= size
|
||||
retFuture.complete(realSize)
|
||||
# We don't deallocate ``ol`` here because even though this completed
|
||||
# immediately poll will still be notified about its completion and it will
|
||||
# free ``ol``.
|
||||
return retFuture
|
||||
|
||||
|
||||
proc recv*(socket: TAsyncFD, size: int,
|
||||
flags = {SocketFlag.SafeDisconn}): Future[string] =
|
||||
## Reads **up to** ``size`` bytes from ``socket``. Returned future will
|
||||
@@ -1037,30 +949,6 @@ else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
||||
return retFuture
|
||||
|
||||
proc recvInto*(socket: TAsyncFD, buf: cstring, size: int,
|
||||
flags = {SocketFlag.SafeDisconn}): Future[int] =
|
||||
var retFuture = newFuture[int]("recvInto")
|
||||
|
||||
proc cb(sock: TAsyncFD): bool =
|
||||
result = true
|
||||
let res = recv(sock.SocketHandle, buf, size.cint,
|
||||
flags.toOSFlags())
|
||||
if res < 0:
|
||||
let lastError = osLastError()
|
||||
if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}:
|
||||
if flags.isDisconnectionError(lastError):
|
||||
retFuture.complete(0)
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
||||
else:
|
||||
result = false # We still want this callback to be called.
|
||||
else:
|
||||
retFuture.complete(res)
|
||||
# TODO: The following causes a massive slowdown.
|
||||
#if not cb(socket):
|
||||
addRead(socket, cb)
|
||||
return retFuture
|
||||
|
||||
proc recv*(socket: TAsyncFD, size: int,
|
||||
flags = {SocketFlag.SafeDisconn}): Future[string] =
|
||||
var retFuture = newFuture[string]("recv")
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
##
|
||||
## Chat server
|
||||
## ^^^^^^^^^^^
|
||||
##
|
||||
##
|
||||
## The following example demonstrates a simple chat server.
|
||||
##
|
||||
## .. code-block::nim
|
||||
@@ -182,6 +182,118 @@ proc connect*(socket: AsyncSocket, address: string, port: Port,
|
||||
sslSetConnectState(socket.sslHandle)
|
||||
sslLoop(socket, flags, sslDoHandshake(socket.sslHandle))
|
||||
|
||||
when defined(windows):
|
||||
proc recvInto(socket: TAsyncFD, buf: cstring, size: int,
|
||||
flags = {SocketFlag.SafeDisconn}): Future[int] =
|
||||
## Reads **up to** ``size`` bytes from ``socket`` into ``buf``, which must
|
||||
## at least be of that size. Returned future will complete once all the
|
||||
## data requested is read, a part of the data has been read, or the socket
|
||||
## has disconnected in which case the future will complete with a value of
|
||||
## ``0``.
|
||||
##
|
||||
## **Warning**: The ``Peek`` socket flag is not supported on Windows.
|
||||
|
||||
|
||||
# Things to note:
|
||||
# * When WSARecv completes immediately then ``bytesReceived`` is very
|
||||
# unreliable.
|
||||
# * Still need to implement message-oriented socket disconnection,
|
||||
# '\0' in the message currently signifies a socket disconnect. Who
|
||||
# knows what will happen when someone sends that to our socket.
|
||||
verifyPresence(socket)
|
||||
assert SocketFlag.Peek notin flags, "Peek not supported on Windows."
|
||||
|
||||
var retFuture = newFuture[int]("recvInto")
|
||||
|
||||
buf[0] = '\0'
|
||||
var dataBuf: TWSABuf
|
||||
dataBuf.buf = buf
|
||||
dataBuf.len = size
|
||||
|
||||
var bytesReceived: Dword
|
||||
var flagsio = flags.toOSFlags().Dword
|
||||
var ol = PCustomOverlapped()
|
||||
GC_ref(ol)
|
||||
ol.data = TCompletionData(fd: socket, cb:
|
||||
proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
|
||||
if not retFuture.finished:
|
||||
if errcode == OSErrorCode(-1):
|
||||
if bytesCount == 0 and dataBuf.buf[0] == '\0':
|
||||
retFuture.complete(0)
|
||||
else:
|
||||
retFuture.complete(bytesCount)
|
||||
else:
|
||||
if flags.isDisconnectionError(errcode):
|
||||
retFuture.complete(0)
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(errcode)))
|
||||
if dataBuf.buf != nil:
|
||||
dataBuf.buf = nil
|
||||
)
|
||||
|
||||
let ret = WSARecv(socket.SocketHandle, addr dataBuf, 1, addr bytesReceived,
|
||||
addr flagsio, cast[POVERLAPPED](ol), nil)
|
||||
if ret == -1:
|
||||
let err = osLastError()
|
||||
if err.int32 != ERROR_IO_PENDING:
|
||||
if dataBuf.buf != nil:
|
||||
dataBuf.buf = nil
|
||||
GC_unref(ol)
|
||||
if flags.isDisconnectionError(err):
|
||||
retFuture.complete(0)
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(err)))
|
||||
elif ret == 0 and bytesReceived == 0 and dataBuf.buf[0] == '\0':
|
||||
# We have to ensure that the buffer is empty because WSARecv will tell
|
||||
# us immediately when it was disconnected, even when there is still
|
||||
# data in the buffer.
|
||||
# We want to give the user as much data as we can. So we only return
|
||||
# the empty string (which signals a disconnection) when there is
|
||||
# nothing left to read.
|
||||
retFuture.complete(0)
|
||||
# TODO: "For message-oriented sockets, where a zero byte message is often
|
||||
# allowable, a failure with an error code of WSAEDISCON is used to
|
||||
# indicate graceful closure."
|
||||
# ~ http://msdn.microsoft.com/en-us/library/ms741688%28v=vs.85%29.aspx
|
||||
else:
|
||||
# Request to read completed immediately.
|
||||
# From my tests bytesReceived isn't reliable.
|
||||
let realSize =
|
||||
if bytesReceived == 0:
|
||||
size
|
||||
else:
|
||||
bytesReceived
|
||||
assert realSize <= size
|
||||
retFuture.complete(realSize)
|
||||
# We don't deallocate ``ol`` here because even though this completed
|
||||
# immediately poll will still be notified about its completion and it will
|
||||
# free ``ol``.
|
||||
return retFuture
|
||||
else:
|
||||
proc recvInto(socket: TAsyncFD, buf: cstring, size: int,
|
||||
flags = {SocketFlag.SafeDisconn}): Future[int] =
|
||||
var retFuture = newFuture[int]("recvInto")
|
||||
|
||||
proc cb(sock: TAsyncFD): bool =
|
||||
result = true
|
||||
let res = recv(sock.SocketHandle, buf, size.cint,
|
||||
flags.toOSFlags())
|
||||
if res < 0:
|
||||
let lastError = osLastError()
|
||||
if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}:
|
||||
if flags.isDisconnectionError(lastError):
|
||||
retFuture.complete(0)
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
||||
else:
|
||||
result = false # We still want this callback to be called.
|
||||
else:
|
||||
retFuture.complete(res)
|
||||
# TODO: The following causes a massive slowdown.
|
||||
#if not cb(socket):
|
||||
addRead(socket, cb)
|
||||
return retFuture
|
||||
|
||||
template readInto(buf: cstring, size: int, socket: AsyncSocket,
|
||||
flags: set[SocketFlag]): int =
|
||||
## Reads **up to** ``size`` bytes from ``socket`` into ``buf``. Note that
|
||||
@@ -314,7 +426,7 @@ proc recvLineInto*(socket: AsyncSocket, resString: ptr string,
|
||||
## 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 ``""``.
|
||||
##
|
||||
## If the socket is disconnected in the middle of a line (before ``\r\L``
|
||||
@@ -322,7 +434,7 @@ proc recvLineInto*(socket: AsyncSocket, resString: ptr string,
|
||||
## The partial line **will be lost**.
|
||||
##
|
||||
## **Warning**: The ``Peek`` flag is not yet implemented.
|
||||
##
|
||||
##
|
||||
## **Warning**: ``recvLineInto`` on unbuffered sockets assumes that the
|
||||
## protocol uses ``\r\L`` to delimit a new line.
|
||||
assert SocketFlag.Peek notin flags ## TODO:
|
||||
@@ -388,7 +500,7 @@ proc recvLine*(socket: AsyncSocket,
|
||||
## 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 ``""``.
|
||||
##
|
||||
## If the socket is disconnected in the middle of a line (before ``\r\L``
|
||||
@@ -396,7 +508,7 @@ proc recvLine*(socket: AsyncSocket,
|
||||
## The partial line **will be lost**.
|
||||
##
|
||||
## **Warning**: The ``Peek`` flag is not yet implemented.
|
||||
##
|
||||
##
|
||||
## **Warning**: ``recvLine`` on unbuffered sockets assumes that the protocol
|
||||
## uses ``\r\L`` to delimit a new line.
|
||||
template addNLIfEmpty(): stmt =
|
||||
@@ -535,11 +647,11 @@ when isMainModule:
|
||||
proc (future: Future[void]) =
|
||||
echo("Send")
|
||||
client.close()
|
||||
|
||||
|
||||
var f = accept(sock)
|
||||
f.callback = onAccept
|
||||
|
||||
|
||||
var f = accept(sock)
|
||||
f.callback = onAccept
|
||||
runForever()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user