Fixes and improvements

This commit is contained in:
cheatfate 2021-01-28 05:37:16 +02:00 committed by zah
parent 0e5ea5b737
commit 534df03a0b
2 changed files with 89 additions and 53 deletions

2
.gitignore vendored
View File

@ -1,2 +1,2 @@
nimcache
*.exe

View File

@ -21,24 +21,24 @@ type
HttpServerFlags* = enum
Secure
HttpConnectionStatus* = enum
HttpStatus* = enum
DropConnection, KeepConnection
HttpErrorEnum* = enum
HTTPServerError* {.pure.} = enum
TimeoutError, CatchableError, RecoverableError, CriticalError
HttpProcessError* = object
error*: HttpErrorEnum
exc*: HttpError
error*: HTTPServerError
exc*: ref CatchableError
remote*: TransportAddress
HttpProcessStatus*[T] = Result[T, HttpProcessError]
RequestFence*[T] = Result[T, HttpProcessError]
HttpRequestFlags* {.pure.} = enum
BoundBody, UnboundBody, MultipartForm, UrlencodedForm
HttpProcessCallback* =
proc(request: HttpProcessStatus[HttpRequest]): Future[HttpStatus]
proc(req: RequestFence[HttpRequest]): Future[HttpStatus] {.gcsafe.}
HttpServer* = ref object of RootRef
instance*: StreamServer
@ -74,6 +74,7 @@ type
contentLength: int
connection*: HttpConnection
mainReader*: AsyncStreamReader
mainWriter*: AsyncStreamWriter
HttpResponse* = object
code*: HttpCode
@ -88,11 +89,24 @@ type
transp: StreamTransport
buffer: seq[byte]
proc init(htype: typedesc[HttpProcessError], error: HTTPServerError,
exc: ref CatchableError,
remote: TransportAddress): HttpProcessError =
HttpProcessError(error: error, exc: exc, remote: remote)
proc init*(htype: typedesc[HttpResponse], req: HttpRequest): HttpResponse =
HttpResponse(
version: req.version,
headersTable: HttpTable.init(),
connection: req.connection,
mainWriter: req.mainWriter
)
proc new*(htype: typedesc[HttpServer],
address: TransportAddress,
processCallback: HttpProcessCallback,
flags: set[HttpServerFlags] = {},
serverUri = Uri(),
processCallback: HttpProcessCallback,
maxConnections: int = -1,
bufferSize: int = 4096,
backlogSize: int = 100,
@ -246,9 +260,11 @@ proc prepareRequest(conn: HttpConnection,
request.requestFlags.incl(MultipartForm)
request.mainReader = newAsyncStreamReader(conn.transp)
request.mainWriter = newAsyncStreamWriter(conn.transp)
ok(request)
proc getBodyStream*(request: HttpRequest): HttpResult[AsyncStreamReader] =
## Returns stream's reader instance which can be used to read request's body.
if HttpRequestFlags.BoundBody in request.requestFlags:
ok(newBoundedStreamReader(request.mainReader, request.contentLength))
elif HttpRequestFlags.UnboundBody in request.requestFlags:
@ -345,67 +361,82 @@ proc processLoop(server: HttpServer, transp: StreamTransport) {.async.} =
var breakLoop = false
while true:
var status: HttpProcessStatus
var arg: HttpProcessStatus[HttpRequest]
var
arg: RequestFence[HttpRequest]
resp = HttpStatus.DropConnection
try:
let request = await conn.getRequest().wait(server.headersTimeout)
arg = ok(request)
except AsyncTimeoutError as exc:
discard await conn.sendErrorResponse(HttpVersion11, Http408, false)
breakLoop = true
arg = err(HttpProcessError(exc: exc, remote: transp.remoteAddress()))
arg = RequestFence[HttpRequest].ok(request)
except CancelledError:
breakLoop = true
except HttpRecoverableError:
breakLoop = false
arg = err()
except CatchableError:
breakLoop = true
except AsyncTimeoutError as exc:
let error = HttpProcessError.init(HTTPServerError.TimeoutError, exc,
transp.remoteAddress())
arg = RequestFence[HttpRequest].err(error)
except HttpRecoverableError as exc:
let error = HttpProcessError.init(HTTPServerError.RecoverableError, exc,
transp.remoteAddress())
arg = RequestFence[HttpRequest].err(error)
except HttpCriticalError as exc:
let error = HttpProcessError.init(HTTPServerError.CriticalError, exc,
transp.remoteAddress())
arg = RequestFence[HttpRequest].err(error)
except CatchableError as exc:
let error = HttpProcessError.init(HTTPServerError.CatchableError, exc,
transp.remoteAddress())
arg = RequestFence[HttpRequest].err(error)
if breakLoop:
break
breakLoop = false
let status =
try:
await conn.server.processCallback()
except CancelledError:
breakLoop = true
HttpCriticalError
except CatchableError:
breakLoop = true
HttpCriticalError
echo "== HEADERS TABLE"
echo request.headersTable
echo "== QUERY TABLE"
echo request.queryTable
echo "== TRANSFER ENCODING ", request.transferEncoding
echo "== CONTENT ENCODING ", request.contentEncoding
echo "== REQUEST FLAGS ", request.requestFlags
var stream = await request.getBody()
echo cast[string](stream)
discard await conn.sendErrorResponse(HttpVersion11, Http200, true,
databody = "OK")
except AsyncTimeoutError:
discard await conn.sendErrorResponse(HttpVersion11, Http408, false)
breakLoop = true
var lastError: ref CatchableError
try:
resp = await conn.server.processCallback(arg)
except CancelledError:
breakLoop = true
except HttpRecoverableError:
breakLoop = false
except HttpCriticalError:
breakLoop = true
except CatchableError as exc:
echo "CatchableError received ", exc.name
lastError = exc
if breakLoop:
break
let keepConn =
case resp
of DropConnection:
false
of KeepConnection:
false
if arg.isErr():
case arg.error().error
of HTTPServerError.TimeoutError:
discard await conn.sendErrorResponse(HttpVersion11, Http408, keepConn)
of HTTPServerError.RecoverableError:
discard await conn.sendErrorResponse(HttpVersion11, Http400, keepConn)
of HTTPServerError.CriticalError:
discard await conn.sendErrorResponse(HttpVersion11, Http400, keepConn)
of HTTPServerError.CatchableError:
discard await conn.sendErrorResponse(HttpVersion11, Http400, keepConn)
if not(keepConn):
break
else:
if isNil(lastError):
if arg.get().mainWriter.bytesCount == 0'u64:
# Processor callback finished without an error, but response was not
# sent to client, so we going to send HTTP404 error.
discard await conn.sendErrorResponse(HttpVersion11, Http404, keepConn)
else:
if arg.get().mainWriter.bytesCount == 0'u64:
# Processor callback finished with an error, but response was not
# sent to client, so we going to send HTTP503 error.
discard await conn.sendErrorResponse(HttpVersion11, Http503, keepConn)
if not(keepConn):
break
await transp.closeWait()
server.connections.del(transp.getId())
# if server.maxConnections > 0:
@ -495,7 +526,12 @@ proc join*(server: HttpServer): Future[void] =
retFuture
when isMainModule:
let res = HttpServer.new(initTAddress("127.0.0.1:30080"), maxConnections = 1)
proc processCallback(req: RequestFence[HttpRequest]): Future[HttpStatus] {.
async.} =
echo "process callback"
let res = HttpServer.new(initTAddress("127.0.0.1:30080"), processCallback,
maxConnections = 1)
if res.isOk():
let server = res.get()
server.start()