This commit is contained in:
Dominik Picheta 2017-11-19 01:55:03 +00:00 • committed by Andreas Rumpf
commit fd350de580

View file

@ -58,15 +58,18 @@ type
socket: AsyncSocket socket: AsyncSocket
reuseAddr: bool reuseAddr: bool
reusePort: bool reusePort: bool
maxBody: int ## The maximum content-length that will be read for the body.
{.deprecated: [TRequest: Request, PAsyncHttpServer: AsyncHttpServer, {.deprecated: [TRequest: Request, PAsyncHttpServer: AsyncHttpServer,
THttpCode: HttpCode, THttpVersion: HttpVersion].} THttpCode: HttpCode, THttpVersion: HttpVersion].}
proc newAsyncHttpServer*(reuseAddr = true, reusePort = false): AsyncHttpServer = proc newAsyncHttpServer*(reuseAddr = true, reusePort = false,
maxBody = 8388608): AsyncHttpServer =
## Creates a new ``AsyncHttpServer`` instance. ## Creates a new ``AsyncHttpServer`` instance.
new result new result
result.reuseAddr = reuseAddr result.reuseAddr = reuseAddr
result.reusePort = reusePort result.reusePort = reusePort
result.maxBody = maxBody
proc addHeaders(msg: var string, headers: HttpHeaders) = proc addHeaders(msg: var string, headers: HttpHeaders) =
for k, v in headers: for k, v in headers:
@ -129,17 +132,16 @@ proc parseProtocol(protocol: string): tuple[orig: string, major, minor: int] =
proc sendStatus(client: AsyncSocket, status: string): Future[void] = proc sendStatus(client: AsyncSocket, status: string): Future[void] =
client.send("HTTP/1.1 " & status & "\c\L\c\L") client.send("HTTP/1.1 " & status & "\c\L\c\L")
proc processClient(client: AsyncSocket, address: string, proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
client: AsyncSocket,
address: string, lineFut: FutureVar[string],
callback: proc (request: Request): callback: proc (request: Request):
Future[void] {.closure, gcsafe.}) {.async.} = Future[void] {.closure, gcsafe.}) {.async.} =
var request: Request
request.url = initUri()
request.headers = newHttpHeaders()
var lineFut = newFutureVar[string]("asynchttpserver.processClient")
lineFut.mget() = newStringOfCap(80)
var key, value = ""
while not client.isClosed: # Alias `request` to `req.mget()` so we don't have to write `mget` everywhere.
template request(): Request =
req.mget()
# GET /path HTTP/1.1 # GET /path HTTP/1.1
# Header: val # Header: val
# \n # \n
@ -177,22 +179,22 @@ proc processClient(client: AsyncSocket, address: string,
request.reqMethod = parseEnum[HttpMethod]("http" & linePart) request.reqMethod = parseEnum[HttpMethod]("http" & linePart)
except ValueError: except ValueError:
asyncCheck request.respondError(Http400) asyncCheck request.respondError(Http400)
continue return
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)
continue return
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)
continue return
else: else:
await request.respondError(Http400) await request.respondError(Http400)
continue return
inc i inc i
# Headers # Headers
@ -231,15 +233,18 @@ proc processClient(client: AsyncSocket, address: string,
if parseInt(request.headers["Content-Length"], if parseInt(request.headers["Content-Length"],
contentLength) == 0: contentLength) == 0:
await request.respond(Http400, "Bad Request. Invalid Content-Length.") await request.respond(Http400, "Bad Request. Invalid Content-Length.")
continue return
else: else:
if contentLength > server.maxBody:
await request.respondError(Http413)
return
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.")
continue return
elif request.reqMethod == HttpPost: elif request.reqMethod == HttpPost:
await request.respond(Http411, "Content-Length required.") await request.respond(Http411, "Content-Length required.")
continue return
# Call the user's callback. # Call the user's callback.
await callback(request) await callback(request)
@ -259,7 +264,22 @@ proc processClient(client: AsyncSocket, address: string,
discard discard
else: else:
request.client.close() request.client.close()
break return
proc processClient(server: AsyncHttpServer, client: AsyncSocket, address: string,
callback: proc (request: Request):
Future[void] {.closure, gcsafe.}) {.async.} =
var request = newFutureVar[Request]("asynchttpserver.processClient")
request.mget().url = initUri()
request.mget().headers = newHttpHeaders()
var lineFut = newFutureVar[string]("asynchttpserver.processClient")
lineFut.mget() = newStringOfCap(80)
while not client.isClosed:
try:
await processRequest(server, request, client, address, lineFut, callback)
except:
asyncCheck request.mget().respondError(Http500)
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.},
@ -280,7 +300,7 @@ proc serve*(server: AsyncHttpServer, port: Port,
# TODO: Causes compiler crash. # TODO: Causes compiler crash.
#var (address, client) = await server.socket.acceptAddr() #var (address, client) = await server.socket.acceptAddr()
var fut = await server.socket.acceptAddr() var fut = await server.socket.acceptAddr()
asyncCheck processClient(fut.client, fut.address, callback) asyncCheck processClient(server, fut.client, fut.address, callback)
#echo(f.isNil) #echo(f.isNil)
#echo(f.repr) #echo(f.repr)