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
commit e3cee541bd

View file

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