Add async post() procedure.
This commit is contained in:
parent
53aec163eb
commit
74b0f85fc7
|
@ -13,6 +13,7 @@ const
|
|||
useChroniclesLogging* {.booldefine.} = false
|
||||
|
||||
HeadersMark* = @[byte(0x0D), byte(0x0A), byte(0x0D), byte(0x0A)]
|
||||
PostMethods* = {MethodPost, MethodPatch, MethodPut, MethodDelete}
|
||||
|
||||
type
|
||||
HttpResult*[T] = Result[T, string]
|
||||
|
@ -109,20 +110,3 @@ func getContentType*(ch: openarray[string]): HttpResult[string] =
|
|||
else:
|
||||
let mparts = ch[0].split(";")
|
||||
ok(strip(mparts[0]).toLowerAscii())
|
||||
|
||||
func getMultipartBoundary*(contentType: string): HttpResult[string] =
|
||||
## Process ``multipart/form-data`` ``Content-Type`` header and return
|
||||
## multipart boundary.
|
||||
let mparts = contentType.split(";")
|
||||
if strip(mparts[0]).toLowerAscii() != "multipart/form-data":
|
||||
return err("Content-Type is not multipart")
|
||||
if len(mparts) < 2:
|
||||
return err("Content-Type missing boundary value")
|
||||
let stripped = strip(mparts[1])
|
||||
if not(stripped.toLowerAscii().startsWith("boundary")):
|
||||
return err("Incorrect Content-Type boundary format")
|
||||
let bparts = stripped.split("=")
|
||||
if len(bparts) < 2:
|
||||
err("Missing Content-Type boundary")
|
||||
else:
|
||||
ok(strip(bparts[1]))
|
||||
|
|
|
@ -10,8 +10,8 @@ import std/[tables, options, uri, strutils]
|
|||
import stew/results, httputils
|
||||
import ../../asyncloop, ../../asyncsync
|
||||
import ../../streams/[asyncstream, boundstream, chunkstream]
|
||||
import httptable, httpcommon
|
||||
export httpcommon
|
||||
import httptable, httpcommon, multipart
|
||||
export httpcommon, multipart
|
||||
|
||||
when defined(useChroniclesLogging):
|
||||
echo "Importing chronicles"
|
||||
|
@ -58,10 +58,10 @@ type
|
|||
HttpServerState* = enum
|
||||
ServerRunning, ServerStopped, ServerClosed
|
||||
|
||||
HttpRequest* = object
|
||||
HttpRequest* = ref object of RootRef
|
||||
headersTable: HttpTable
|
||||
queryTable: HttpTable
|
||||
postTable: HttpTable
|
||||
postTable: Option[HttpTable]
|
||||
rawPath*: string
|
||||
rawQuery*: string
|
||||
uri*: Uri
|
||||
|
@ -398,6 +398,8 @@ proc processLoop(server: HttpServer, transp: StreamTransport) {.async.} =
|
|||
except CancelledError:
|
||||
breakLoop = true
|
||||
except CatchableError as exc:
|
||||
resp = DropConnection
|
||||
echo "Exception received from processor callback ", exc.name
|
||||
lastError = exc
|
||||
|
||||
if breakLoop:
|
||||
|
@ -424,15 +426,20 @@ proc processLoop(server: HttpServer, transp: StreamTransport) {.async.} =
|
|||
break
|
||||
else:
|
||||
if isNil(lastError):
|
||||
echo "lastError = nil"
|
||||
echo "mainWriter.bytesCount = ", arg.get().mainWriter.bytesCount
|
||||
|
||||
if arg.get().mainWriter.bytesCount == 0'u64:
|
||||
echo "Sending 404 keepConn = ", keepConn
|
||||
# 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)
|
||||
echo "bytesCount = ", arg.get().mainWriter.bytesCount
|
||||
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)
|
||||
discard await conn.sendErrorResponse(HttpVersion11, Http503, true)
|
||||
|
||||
if not(keepConn):
|
||||
break
|
||||
|
@ -525,9 +532,69 @@ proc join*(server: HttpServer): Future[void] =
|
|||
|
||||
retFuture
|
||||
|
||||
proc post*(req: HttpRequest): Future[HttpTable] {.async.} =
|
||||
## Return POST parameters
|
||||
if req.postTable.isSome():
|
||||
return req.postTable.get()
|
||||
else:
|
||||
if req.meth notin PostMethods:
|
||||
return HttpTable.init()
|
||||
|
||||
if UrlencodedForm in req.requestFlags:
|
||||
var table = HttpTable.init()
|
||||
var body = await req.getBody()
|
||||
## TODO (cheatfate) double copy here.
|
||||
var strbody = newString(len(body))
|
||||
if len(body) > 0:
|
||||
copyMem(addr strbody[0], addr body[0], len(body))
|
||||
for key, value in queryParams(strbody):
|
||||
table.add(key, value)
|
||||
req.postTable = some(table)
|
||||
return table
|
||||
elif MultipartForm in req.requestFlags:
|
||||
let cres = getContentType(req.headersTable.getList("content-type"))
|
||||
if cres.isErr():
|
||||
raise newHttpCriticalError(cres.error)
|
||||
let bres = getMultipartBoundary(req.headersTable.getList("content-type"))
|
||||
if bres.isErr():
|
||||
raise newHttpCriticalError(bres.error)
|
||||
var reader = req.getBodyStream()
|
||||
if reader.isErr():
|
||||
raise newHttpCriticalError(reader.error)
|
||||
var mpreader = MultiPartReaderRef.new(reader.get(), bres.get())
|
||||
var table = HttpTable.init()
|
||||
var runLoop = true
|
||||
while runLoop:
|
||||
try:
|
||||
let res = await mpreader.readPart()
|
||||
var value = await res.getBody()
|
||||
## TODO (cheatfate) double copy here.
|
||||
var strvalue = newString(len(value))
|
||||
if len(value) > 0:
|
||||
copyMem(addr strvalue[0], addr value[0], len(value))
|
||||
table.add(res.name, strvalue)
|
||||
except MultiPartEoM:
|
||||
runLoop = false
|
||||
req.postTable = some(table)
|
||||
return table
|
||||
else:
|
||||
if BoundBody in req.requestFlags:
|
||||
if req.contentLength != 0:
|
||||
raise newHttpCriticalError("Unsupported request body")
|
||||
return HttpTable.init()
|
||||
elif UnboundBody in req.requestFlags:
|
||||
raise newHttpCriticalError("Unsupported request body")
|
||||
|
||||
when isMainModule:
|
||||
proc processCallback(req: RequestFence[HttpRequest]): Future[HttpStatus] {.
|
||||
async.} =
|
||||
if req.isOk():
|
||||
let request = req.get()
|
||||
echo "Got ", request.meth, " request"
|
||||
let post = await request.post()
|
||||
echo "post = ", post
|
||||
else:
|
||||
echo "Got FAILURE", req.error()
|
||||
echo "process callback"
|
||||
|
||||
let res = HttpServer.new(initTAddress("127.0.0.1:30080"), processCallback,
|
||||
|
|
Loading…
Reference in New Issue