Option to allow the request body to be processed outside the asynchttpserver library. (#13147)

Allow the request body to be processed outside the asynchttpserver library to break big files into chunks of data. This change does not break anything.
This commit is contained in:
Henrique Dias
2020-02-04 20:13:25 +00:00
committed by GitHub
parent bfe96e069b
commit 955465e5f4
2 changed files with 49 additions and 16 deletions

View File

@@ -57,6 +57,7 @@
## Library changes
- `asynchttpserver` now the request body is a FutureStream.
- `asyncdispatch.drain` now properly takes into account `selector.hasPendingOperations`
and only returns once all pending async operations are guaranteed to have completed.
- `asyncdispatch.drain` now consistently uses the passed timeout value for all

View File

@@ -35,24 +35,41 @@ import httpcore
export httpcore except parseHeader
const
maxLine = 8*1024
# TODO: If it turns out that the decisions that asynchttpserver makes
# explicitly, about whether to close the client sockets or upgrade them are
# wrong, then add a return value which determines what to do for the callback.
# Also, maybe move `client` out of `Request` object and into the args for
# the proc.
type
Request* = object
client*: AsyncSocket # TODO: Separate this into a Response object?
reqMethod*: HttpMethod
headers*: HttpHeaders
protocol*: tuple[orig: string, major, minor: int]
url*: Uri
hostname*: string ## The hostname of the client that made the request.
body*: string
const
maxLine = 8*1024
when (NimMajor, NimMinor) >= (1, 1):
const
chunkSize = 8*1048 ## This seems perfectly reasonable for default chunkSize.
type
Request* = object
client*: AsyncSocket # TODO: Separate this into a Response object?
reqMethod*: HttpMethod
headers*: HttpHeaders
protocol*: tuple[orig: string, major, minor: int]
url*: Uri
hostname*: string ## The hostname of the client that made the request.
body*: string
bodyStream*: FutureStream[string]
else:
type
Request* = object
client*: AsyncSocket # TODO: Separate this into a Response object?
reqMethod*: HttpMethod
headers*: HttpHeaders
protocol*: tuple[orig: string, major, minor: int]
url*: Uri
hostname*: string ## The hostname of the client that made the request.
body*: string
type
AsyncHttpServer* = ref object
socket: AsyncSocket
reuseAddr: bool
@@ -149,6 +166,8 @@ proc processRequest(
request.hostname.shallowCopy(address)
assert client != nil
request.client = client
when (NimMajor, NimMinor) >= (1, 1):
request.bodyStream = newFutureStream[string]()
# We should skip at least one empty line before the request
# https://tools.ietf.org/html/rfc7230#section-3.5
@@ -243,10 +262,23 @@ proc processRequest(
if contentLength > server.maxBody:
await request.respondError(Http413)
return false
request.body = await client.recv(contentLength)
if request.body.len != contentLength:
await request.respond(Http400, "Bad Request. Content-Length does not match actual.")
return true
when (NimMajor, NimMinor) >= (1, 1):
var remainder = contentLength
while remainder > 0:
let readSize = min(remainder, chunkSize)
let data = await client.recv(read_size)
if data.len != read_size:
await request.respond(Http400, "Bad Request. Content-Length does not match actual.")
return true
await request.bodyStream.write(data)
remainder -= data.len
request.bodyStream.complete()
else:
request.body = await client.recv(contentLength)
if request.body.len != contentLength:
await request.respond(Http400, "Bad Request. Content-Length does not match actual.")
return true
elif request.reqMethod == HttpPost:
await request.respond(Http411, "Content-Length required.")
return true