parent
d02486aa48
commit
159643824b
4 changed files with 36 additions and 29 deletions
|
|
@ -36,6 +36,9 @@ import httpcore
|
||||||
|
|
||||||
export httpcore except parseHeader
|
export httpcore except parseHeader
|
||||||
|
|
||||||
|
const
|
||||||
|
maxLine = 8*1024
|
||||||
|
|
||||||
# TODO: If it turns out that the decisions that asynchttpserver makes
|
# TODO: If it turns out that the decisions that asynchttpserver makes
|
||||||
# explicitly, about whether to close the client sockets or upgrade them are
|
# explicitly, about whether to close the client sockets or upgrade them are
|
||||||
# wrong, then add a return value which determines what to do for the callback.
|
# wrong, then add a return value which determines what to do for the callback.
|
||||||
|
|
@ -97,6 +100,18 @@ proc respond*(req: Request, code: HttpCode, content: string,
|
||||||
|
|
||||||
if headers != nil:
|
if headers != nil:
|
||||||
msg.addHeaders(headers)
|
msg.addHeaders(headers)
|
||||||
|
msg.add("Content-Length: ")
|
||||||
|
# this particular way saves allocations:
|
||||||
|
msg.add content.len
|
||||||
|
msg.add "\c\L\c\L"
|
||||||
|
msg.add(content)
|
||||||
|
result = req.client.send(msg)
|
||||||
|
|
||||||
|
proc respondError(req: Request, code: HttpCode): Future[void] =
|
||||||
|
## Responds to the request with the specified ``HttpCode``.
|
||||||
|
let content = $code
|
||||||
|
var msg = "HTTP/1.1 " & content & "\c\L"
|
||||||
|
|
||||||
msg.add("Content-Length: " & $content.len & "\c\L\c\L")
|
msg.add("Content-Length: " & $content.len & "\c\L\c\L")
|
||||||
msg.add(content)
|
msg.add(content)
|
||||||
result = req.client.send(msg)
|
result = req.client.send(msg)
|
||||||
|
|
@ -139,12 +154,16 @@ proc processClient(client: AsyncSocket, address: string,
|
||||||
for i in 0..1:
|
for i in 0..1:
|
||||||
lineFut.mget().setLen(0)
|
lineFut.mget().setLen(0)
|
||||||
lineFut.clean()
|
lineFut.clean()
|
||||||
await client.recvLineInto(lineFut) # TODO: Timeouts.
|
await client.recvLineInto(lineFut, maxLength=maxLine) # TODO: Timeouts.
|
||||||
|
|
||||||
if lineFut.mget == "":
|
if lineFut.mget == "":
|
||||||
client.close()
|
client.close()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if lineFut.mget.len > maxLine:
|
||||||
|
await request.respondError(Http413)
|
||||||
|
client.close()
|
||||||
|
return
|
||||||
if lineFut.mget != "\c\L":
|
if lineFut.mget != "\c\L":
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
@ -157,19 +176,17 @@ proc processClient(client: AsyncSocket, address: string,
|
||||||
# TODO: this is likely slow.
|
# TODO: this is likely slow.
|
||||||
request.reqMethod = parseEnum[HttpMethod]("http" & linePart)
|
request.reqMethod = parseEnum[HttpMethod]("http" & linePart)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
asyncCheck request.respond(Http400, "Invalid request method. Got: " &
|
asyncCheck request.respondError(Http400)
|
||||||
linePart)
|
|
||||||
continue
|
continue
|
||||||
of 1: parseUri(linePart, request.url)
|
of 1: parseUri(linePart, request.url)
|
||||||
of 2:
|
of 2:
|
||||||
try:
|
try:
|
||||||
request.protocol = parseProtocol(linePart)
|
request.protocol = parseProtocol(linePart)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
asyncCheck request.respond(Http400,
|
asyncCheck request.respondError(Http400)
|
||||||
"Invalid request protocol. Got: " & linePart)
|
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
await request.respond(Http400, "Invalid request. Got: " & lineFut.mget)
|
await request.respondError(Http400)
|
||||||
continue
|
continue
|
||||||
inc i
|
inc i
|
||||||
|
|
||||||
|
|
@ -178,10 +195,13 @@ proc processClient(client: AsyncSocket, address: string,
|
||||||
i = 0
|
i = 0
|
||||||
lineFut.mget.setLen(0)
|
lineFut.mget.setLen(0)
|
||||||
lineFut.clean()
|
lineFut.clean()
|
||||||
await client.recvLineInto(lineFut)
|
await client.recvLineInto(lineFut, maxLength=maxLine)
|
||||||
|
|
||||||
if lineFut.mget == "":
|
if lineFut.mget == "":
|
||||||
client.close(); return
|
client.close(); return
|
||||||
|
if lineFut.mget.len > maxLine:
|
||||||
|
await request.respondError(Http413)
|
||||||
|
client.close(); return
|
||||||
if lineFut.mget == "\c\L": break
|
if lineFut.mget == "\c\L": break
|
||||||
let (key, value) = parseHeader(lineFut.mget)
|
let (key, value) = parseHeader(lineFut.mget)
|
||||||
request.headers[key] = value
|
request.headers[key] = value
|
||||||
|
|
|
||||||
|
|
@ -464,8 +464,7 @@ proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string],
|
||||||
## The partial line **will be lost**.
|
## The partial line **will be lost**.
|
||||||
##
|
##
|
||||||
## The ``maxLength`` parameter determines the maximum amount of characters
|
## The ``maxLength`` parameter determines the maximum amount of characters
|
||||||
## that can be read before a ``ValueError`` is raised. This prevents Denial
|
## that can be read. ``resString`` will be truncated after that.
|
||||||
## of Service (DOS) attacks.
|
|
||||||
##
|
##
|
||||||
## **Warning**: The ``Peek`` flag is not yet implemented.
|
## **Warning**: The ``Peek`` flag is not yet implemented.
|
||||||
##
|
##
|
||||||
|
|
@ -519,10 +518,7 @@ proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string],
|
||||||
socket.currPos.inc()
|
socket.currPos.inc()
|
||||||
|
|
||||||
# Verify that this isn't a DOS attack: #3847.
|
# Verify that this isn't a DOS attack: #3847.
|
||||||
if resString.mget.len > maxLength:
|
if resString.mget.len > maxLength: break
|
||||||
let msg = "recvLine received more than the specified `maxLength` " &
|
|
||||||
"allowed."
|
|
||||||
raise newException(ValueError, msg)
|
|
||||||
else:
|
else:
|
||||||
var c = ""
|
var c = ""
|
||||||
while true:
|
while true:
|
||||||
|
|
@ -546,10 +542,7 @@ proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string],
|
||||||
resString.mget.add c
|
resString.mget.add c
|
||||||
|
|
||||||
# Verify that this isn't a DOS attack: #3847.
|
# Verify that this isn't a DOS attack: #3847.
|
||||||
if resString.mget.len > maxLength:
|
if resString.mget.len > maxLength: break
|
||||||
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,
|
||||||
|
|
@ -569,8 +562,7 @@ proc recvLine*(socket: AsyncSocket,
|
||||||
## The partial line **will be lost**.
|
## The partial line **will be lost**.
|
||||||
##
|
##
|
||||||
## The ``maxLength`` parameter determines the maximum amount of characters
|
## The ``maxLength`` parameter determines the maximum amount of characters
|
||||||
## that can be read before a ``ValueError`` is raised. This prevents Denial
|
## that can be read. The result is truncated after that.
|
||||||
## of Service (DOS) attacks.
|
|
||||||
##
|
##
|
||||||
## **Warning**: The ``Peek`` flag is not yet implemented.
|
## **Warning**: The ``Peek`` flag is not yet implemented.
|
||||||
##
|
##
|
||||||
|
|
|
||||||
|
|
@ -285,7 +285,7 @@ proc `$`*(code: HttpCode): string =
|
||||||
proc `==`*(a, b: HttpCode): bool {.borrow.}
|
proc `==`*(a, b: HttpCode): bool {.borrow.}
|
||||||
|
|
||||||
proc `==`*(rawCode: string, code: HttpCode): bool =
|
proc `==`*(rawCode: string, code: HttpCode): bool =
|
||||||
return rawCode.toLower() == ($code).toLower()
|
return cmpIgnoreCase(rawCode, $code) == 0
|
||||||
|
|
||||||
proc is2xx*(code: HttpCode): bool =
|
proc is2xx*(code: HttpCode): bool =
|
||||||
## Determines whether ``code`` is a 2xx HTTP status code.
|
## Determines whether ``code`` is a 2xx HTTP status code.
|
||||||
|
|
@ -304,7 +304,7 @@ proc is5xx*(code: HttpCode): bool =
|
||||||
return code.int in {500 .. 599}
|
return code.int in {500 .. 599}
|
||||||
|
|
||||||
proc `$`*(httpMethod: HttpMethod): string =
|
proc `$`*(httpMethod: HttpMethod): string =
|
||||||
return (system.`$`(httpMethod))[4 .. ^1].toUpper()
|
return (system.`$`(httpMethod))[4 .. ^1].toUpperAscii()
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
var test = newHttpHeaders()
|
var test = newHttpHeaders()
|
||||||
|
|
|
||||||
|
|
@ -1010,8 +1010,7 @@ proc readLine*(socket: Socket, line: var TaintedString, timeout = -1,
|
||||||
## 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
|
## The ``maxLength`` parameter determines the maximum amount of characters
|
||||||
## that can be read before a ``ValueError`` is raised. This prevents Denial
|
## that can be read. The result is truncated after that.
|
||||||
## of Service (DOS) attacks.
|
|
||||||
##
|
##
|
||||||
## **Warning**: Only the ``SafeDisconn`` flag is currently supported.
|
## **Warning**: Only the ``SafeDisconn`` flag is currently supported.
|
||||||
|
|
||||||
|
|
@ -1047,10 +1046,7 @@ proc readLine*(socket: Socket, line: var TaintedString, timeout = -1,
|
||||||
add(line.string, c)
|
add(line.string, c)
|
||||||
|
|
||||||
# Verify that this isn't a DOS attack: #3847.
|
# Verify that this isn't a DOS attack: #3847.
|
||||||
if line.string.len > maxLength:
|
if line.string.len > maxLength: break
|
||||||
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},
|
flags = {SocketFlag.SafeDisconn},
|
||||||
|
|
@ -1069,8 +1065,7 @@ proc recvLine*(socket: Socket, timeout = -1,
|
||||||
## 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
|
## The ``maxLength`` parameter determines the maximum amount of characters
|
||||||
## that can be read before a ``ValueError`` is raised. This prevents Denial
|
## that can be read. The result is truncated after that.
|
||||||
## of Service (DOS) attacks.
|
|
||||||
##
|
##
|
||||||
## **Warning**: Only the ``SafeDisconn`` flag is currently supported.
|
## **Warning**: Only the ``SafeDisconn`` flag is currently supported.
|
||||||
result = ""
|
result = ""
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue