Fixes long-standing asynchttpserver regression. (#10102)

This commit is contained in:
Dominik Picheta
2018-12-27 21:26:48 +00:00
committed by Andreas Rumpf
parent 513a287c61
commit e3cee541bd

View File

@@ -143,11 +143,14 @@ proc parseUppercaseMethod(name: string): HttpMethod =
of "TRACE": HttpTrace
else: raise newException(ValueError, "Invalid HTTP method " & name)
proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
client: AsyncSocket,
address: string, lineFut: FutureVar[string],
callback: proc (request: Request):
Future[void] {.closure, gcsafe.}) {.async.} =
proc processRequest(
server: AsyncHttpServer,
req: FutureVar[Request],
client: AsyncSocket,
address: string,
lineFut: FutureVar[string],
callback: proc (request: Request): Future[void] {.closure, gcsafe.},
): Future[bool] {.async.} =
# Alias `request` to `req.mget()` so we don't have to write `mget` everywhere.
template request(): Request =
@@ -171,12 +174,12 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
if lineFut.mget == "":
client.close()
return
return false
if lineFut.mget.len > maxLine:
await request.respondError(Http413)
client.close()
return
return false
if lineFut.mget != "\c\L":
break
@@ -189,22 +192,22 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
request.reqMethod = parseUppercaseMethod(linePart)
except ValueError:
asyncCheck request.respondError(Http400)
return
return true # Retry processing of request
of 1:
try:
parseUri(linePart, request.url)
except ValueError:
asyncCheck request.respondError(Http400)
return
return true
of 2:
try:
request.protocol = parseProtocol(linePart)
except ValueError:
asyncCheck request.respondError(Http400)
return
return true
else:
await request.respondError(Http400)
return
return true
inc i
# Headers
@@ -215,10 +218,10 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
await client.recvLineInto(lineFut, maxLength=maxLine)
if lineFut.mget == "":
client.close(); return
client.close(); return false
if lineFut.mget.len > maxLine:
await request.respondError(Http413)
client.close(); return
client.close(); return false
if lineFut.mget == "\c\L": break
let (key, value) = parseHeader(lineFut.mget)
request.headers[key] = value
@@ -226,7 +229,7 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
if request.headers.len > headerLimit:
await client.sendStatus("400 Bad Request")
request.client.close()
return
return false
if request.reqMethod == HttpPost:
# Check for Expect header
@@ -242,24 +245,24 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
var contentLength = 0
if parseSaturatedNatural(request.headers["Content-Length"], contentLength) == 0:
await request.respond(Http400, "Bad Request. Invalid Content-Length.")
return
return true
else:
if contentLength > server.maxBody:
await request.respondError(Http413)
return
return false
request.body = await client.recv(contentLength)
if request.body.len != contentLength:
await request.respond(Http400, "Bad Request. Content-Length does not match actual.")
return
return true
elif request.reqMethod == HttpPost:
await request.respond(Http411, "Content-Length required.")
return
return true
# Call the user's callback.
await callback(request)
if "upgrade" in request.headers.getOrDefault("connection"):
return
return false
# Persistent connections
if (request.protocol == HttpVer11 and
@@ -273,7 +276,7 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
discard
else:
request.client.close()
return
return false
proc processClient(server: AsyncHttpServer, client: AsyncSocket, address: string,
callback: proc (request: Request):
@@ -285,7 +288,10 @@ proc processClient(server: AsyncHttpServer, client: AsyncSocket, address: string
lineFut.mget() = newStringOfCap(80)
while not client.isClosed:
await processRequest(server, request, client, address, lineFut, callback)
let retry = await processRequest(
server, request, client, address, lineFut, callback
)
if not retry: break
proc serve*(server: AsyncHttpServer, port: Port,
callback: proc (request: Request): Future[void] {.closure,gcsafe.},