Fixes #3847.
This commit is contained in:
parent
6e7d50310e
commit
8a6343b5b6
4 changed files with 52 additions and 10 deletions
|
|
@ -1646,7 +1646,7 @@ proc send*(socket: AsyncFD, data: string,
|
||||||
# -- Await Macro
|
# -- Await Macro
|
||||||
include asyncmacro
|
include asyncmacro
|
||||||
|
|
||||||
proc recvLine*(socket: AsyncFD): Future[string] {.async.} =
|
proc recvLine*(socket: AsyncFD): Future[string] {.async, deprecated.} =
|
||||||
## Reads a line of data from ``socket``. Returned future will complete once
|
## Reads a line of data from ``socket``. Returned future will complete once
|
||||||
## a full line is read or an error occurs.
|
## a full line is read or an error occurs.
|
||||||
##
|
##
|
||||||
|
|
@ -1664,6 +1664,8 @@ proc recvLine*(socket: AsyncFD): Future[string] {.async.} =
|
||||||
##
|
##
|
||||||
## **Note**: This procedure is mostly used for testing. You likely want to
|
## **Note**: This procedure is mostly used for testing. You likely want to
|
||||||
## use ``asyncnet.recvLine`` instead.
|
## use ``asyncnet.recvLine`` instead.
|
||||||
|
##
|
||||||
|
## **Deprecated since version 0.15.0**: Use ``asyncnet.recvLine()`` instead.
|
||||||
|
|
||||||
template addNLIfEmpty(): stmt =
|
template addNLIfEmpty(): stmt =
|
||||||
if result.len == 0:
|
if result.len == 0:
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,12 @@
|
||||||
|
|
||||||
## This module implements a high performance asynchronous HTTP server.
|
## This module implements a high performance asynchronous HTTP server.
|
||||||
##
|
##
|
||||||
|
## This HTTP server has not been designed to be used in production, but
|
||||||
|
## for testing applications locally. Because of this, when deploying your
|
||||||
|
## application you should use a reverse proxy (for example nginx) instead of
|
||||||
|
## allowing users to connect directly to this server.
|
||||||
|
##
|
||||||
|
##
|
||||||
## Examples
|
## Examples
|
||||||
## --------
|
## --------
|
||||||
##
|
##
|
||||||
|
|
|
||||||
|
|
@ -388,7 +388,7 @@ proc accept*(socket: AsyncSocket,
|
||||||
return retFut
|
return retFut
|
||||||
|
|
||||||
proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string],
|
proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string],
|
||||||
flags = {SocketFlag.SafeDisconn}) {.async.} =
|
flags = {SocketFlag.SafeDisconn}, maxLength = MaxLineLength) {.async.} =
|
||||||
## Reads a line of data from ``socket`` into ``resString``.
|
## Reads a line of data from ``socket`` into ``resString``.
|
||||||
##
|
##
|
||||||
## If a full line is read ``\r\L`` is not
|
## If a full line is read ``\r\L`` is not
|
||||||
|
|
@ -401,13 +401,14 @@ proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string],
|
||||||
## is read) then line will be set to ``""``.
|
## is read) then line will be set to ``""``.
|
||||||
## The partial line **will be lost**.
|
## The partial line **will be lost**.
|
||||||
##
|
##
|
||||||
|
## The ``maxLength`` parameter determines the maximum amount of characters
|
||||||
|
## that can be read before a ``ValueError`` is raised. This prevents Denial
|
||||||
|
## of Service (DOS) attacks.
|
||||||
|
##
|
||||||
## **Warning**: The ``Peek`` flag is not yet implemented.
|
## **Warning**: The ``Peek`` flag is not yet implemented.
|
||||||
##
|
##
|
||||||
## **Warning**: ``recvLineInto`` on unbuffered sockets assumes that the
|
## **Warning**: ``recvLineInto`` on unbuffered sockets assumes that the
|
||||||
## protocol uses ``\r\L`` to delimit a new line.
|
## protocol uses ``\r\L`` to delimit a new line.
|
||||||
##
|
|
||||||
## **Warning**: ``recvLineInto`` currently uses a raw pointer to a string for
|
|
||||||
## performance reasons. This will likely change soon to use FutureVars.
|
|
||||||
assert SocketFlag.Peek notin flags ## TODO:
|
assert SocketFlag.Peek notin flags ## TODO:
|
||||||
assert(not resString.mget.isNil(),
|
assert(not resString.mget.isNil(),
|
||||||
"String inside resString future needs to be initialised")
|
"String inside resString future needs to be initialised")
|
||||||
|
|
@ -454,6 +455,12 @@ proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string],
|
||||||
else:
|
else:
|
||||||
resString.mget.add socket.buffer[socket.currPos]
|
resString.mget.add socket.buffer[socket.currPos]
|
||||||
socket.currPos.inc()
|
socket.currPos.inc()
|
||||||
|
|
||||||
|
# Verify that this isn't a DOS attack: #3847.
|
||||||
|
if resString.mget.len > maxLength:
|
||||||
|
let msg = "recvLine received more than the specified `maxLength` " &
|
||||||
|
"allowed."
|
||||||
|
raise newException(ValueError, msg)
|
||||||
else:
|
else:
|
||||||
var c = ""
|
var c = ""
|
||||||
while true:
|
while true:
|
||||||
|
|
@ -475,10 +482,17 @@ proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string],
|
||||||
resString.complete()
|
resString.complete()
|
||||||
return
|
return
|
||||||
resString.mget.add c
|
resString.mget.add c
|
||||||
|
|
||||||
|
# Verify that this isn't a DOS attack: #3847.
|
||||||
|
if resString.mget.len > maxLength:
|
||||||
|
let msg = "recvLine received more than the specified `maxLength` " &
|
||||||
|
"allowed."
|
||||||
|
raise newException(ValueError, msg)
|
||||||
resString.complete()
|
resString.complete()
|
||||||
|
|
||||||
proc recvLine*(socket: AsyncSocket,
|
proc recvLine*(socket: AsyncSocket,
|
||||||
flags = {SocketFlag.SafeDisconn}): Future[string] {.async.} =
|
flags = {SocketFlag.SafeDisconn},
|
||||||
|
maxLength = MaxLineLength): Future[string] {.async.} =
|
||||||
## Reads a line of data from ``socket``. Returned future will complete once
|
## Reads a line of data from ``socket``. Returned future will complete once
|
||||||
## a full line is read or an error occurs.
|
## a full line is read or an error occurs.
|
||||||
##
|
##
|
||||||
|
|
@ -492,6 +506,10 @@ proc recvLine*(socket: AsyncSocket,
|
||||||
## is read) then line will be set to ``""``.
|
## is read) then line will be set to ``""``.
|
||||||
## The partial line **will be lost**.
|
## The partial line **will be lost**.
|
||||||
##
|
##
|
||||||
|
## The ``maxLength`` parameter determines the maximum amount of characters
|
||||||
|
## that can be read before a ``ValueError`` is raised. This prevents Denial
|
||||||
|
## of Service (DOS) attacks.
|
||||||
|
##
|
||||||
## **Warning**: The ``Peek`` flag is not yet implemented.
|
## **Warning**: The ``Peek`` flag is not yet implemented.
|
||||||
##
|
##
|
||||||
## **Warning**: ``recvLine`` on unbuffered sockets assumes that the protocol
|
## **Warning**: ``recvLine`` on unbuffered sockets assumes that the protocol
|
||||||
|
|
@ -501,7 +519,7 @@ proc recvLine*(socket: AsyncSocket,
|
||||||
# TODO: Optimise this
|
# TODO: Optimise this
|
||||||
var resString = newFutureVar[string]("asyncnet.recvLine")
|
var resString = newFutureVar[string]("asyncnet.recvLine")
|
||||||
resString.mget() = ""
|
resString.mget() = ""
|
||||||
await socket.recvLineInto(resString, flags)
|
await socket.recvLineInto(resString, flags, maxLength)
|
||||||
result = resString.mget()
|
result = resString.mget()
|
||||||
|
|
||||||
proc listen*(socket: AsyncSocket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} =
|
proc listen*(socket: AsyncSocket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} =
|
||||||
|
|
|
||||||
|
|
@ -112,6 +112,7 @@ else:
|
||||||
|
|
||||||
const
|
const
|
||||||
BufferSize*: int = 4000 ## size of a buffered socket's buffer
|
BufferSize*: int = 4000 ## size of a buffered socket's buffer
|
||||||
|
MaxLineLength* = 1_000_000
|
||||||
|
|
||||||
type
|
type
|
||||||
SocketImpl* = object ## socket type
|
SocketImpl* = object ## socket type
|
||||||
|
|
@ -1006,7 +1007,7 @@ proc peekChar(socket: Socket, c: var char): int {.tags: [ReadIOEffect].} =
|
||||||
result = recv(socket.fd, addr(c), 1, MSG_PEEK)
|
result = recv(socket.fd, addr(c), 1, MSG_PEEK)
|
||||||
|
|
||||||
proc readLine*(socket: Socket, line: var TaintedString, timeout = -1,
|
proc readLine*(socket: Socket, line: var TaintedString, timeout = -1,
|
||||||
flags = {SocketFlag.SafeDisconn}) {.
|
flags = {SocketFlag.SafeDisconn}, maxLength = MaxLineLength) {.
|
||||||
tags: [ReadIOEffect, TimeEffect].} =
|
tags: [ReadIOEffect, TimeEffect].} =
|
||||||
## Reads a line of data from ``socket``.
|
## Reads a line of data from ``socket``.
|
||||||
##
|
##
|
||||||
|
|
@ -1021,6 +1022,10 @@ proc readLine*(socket: Socket, line: var TaintedString, timeout = -1,
|
||||||
## A timeout can be specified in milliseconds, if data is not received within
|
## A timeout can be specified in milliseconds, if data is not received within
|
||||||
## the specified time an ETimeout exception will be raised.
|
## the specified time an ETimeout exception will be raised.
|
||||||
##
|
##
|
||||||
|
## The ``maxLength`` parameter determines the maximum amount of characters
|
||||||
|
## that can be read before a ``ValueError`` is raised. This prevents Denial
|
||||||
|
## of Service (DOS) attacks.
|
||||||
|
##
|
||||||
## **Warning**: Only the ``SafeDisconn`` flag is currently supported.
|
## **Warning**: Only the ``SafeDisconn`` flag is currently supported.
|
||||||
|
|
||||||
template addNLIfEmpty() =
|
template addNLIfEmpty() =
|
||||||
|
|
@ -1054,8 +1059,15 @@ proc readLine*(socket: Socket, line: var TaintedString, timeout = -1,
|
||||||
return
|
return
|
||||||
add(line.string, c)
|
add(line.string, c)
|
||||||
|
|
||||||
|
# Verify that this isn't a DOS attack: #3847.
|
||||||
|
if line.string.len > maxLength:
|
||||||
|
let msg = "recvLine received more than the specified `maxLength` " &
|
||||||
|
"allowed."
|
||||||
|
raise newException(ValueError, msg)
|
||||||
|
|
||||||
proc recvLine*(socket: Socket, timeout = -1,
|
proc recvLine*(socket: Socket, timeout = -1,
|
||||||
flags = {SocketFlag.SafeDisconn}): TaintedString =
|
flags = {SocketFlag.SafeDisconn},
|
||||||
|
maxLength = MaxLineLength): TaintedString =
|
||||||
## Reads a line of data from ``socket``.
|
## Reads a line of data from ``socket``.
|
||||||
##
|
##
|
||||||
## If a full line is read ``\r\L`` is not
|
## If a full line is read ``\r\L`` is not
|
||||||
|
|
@ -1069,9 +1081,13 @@ proc recvLine*(socket: Socket, timeout = -1,
|
||||||
## A timeout can be specified in milliseconds, if data is not received within
|
## A timeout can be specified in milliseconds, if data is not received within
|
||||||
## the specified time an ETimeout exception will be raised.
|
## the specified time an ETimeout exception will be raised.
|
||||||
##
|
##
|
||||||
|
## The ``maxLength`` parameter determines the maximum amount of characters
|
||||||
|
## that can be read before a ``ValueError`` is raised. This prevents Denial
|
||||||
|
## of Service (DOS) attacks.
|
||||||
|
##
|
||||||
## **Warning**: Only the ``SafeDisconn`` flag is currently supported.
|
## **Warning**: Only the ``SafeDisconn`` flag is currently supported.
|
||||||
result = ""
|
result = ""
|
||||||
readLine(socket, result, timeout, flags)
|
readLine(socket, result, timeout, flags, maxLength)
|
||||||
|
|
||||||
proc recvFrom*(socket: Socket, data: var string, length: int,
|
proc recvFrom*(socket: Socket, data: var string, length: int,
|
||||||
address: var string, port: var Port, flags = 0'i32): int {.
|
address: var string, port: var Port, flags = 0'i32): int {.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue