parent
e1ed34627f
commit
fd350de580
1 changed files with 145 additions and 125 deletions
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue