Skip to content

Commit 955465e

Browse files
authored
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.
1 parent bfe96e0 commit 955465e

File tree

2 files changed

+49
-16
lines changed

2 files changed

+49
-16
lines changed

changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757

5858
## Library changes
5959

60+
- `asynchttpserver` now the request body is a FutureStream.
6061
- `asyncdispatch.drain` now properly takes into account `selector.hasPendingOperations`
6162
and only returns once all pending async operations are guaranteed to have completed.
6263
- `asyncdispatch.drain` now consistently uses the passed timeout value for all

lib/pure/asynchttpserver.nim

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,24 +35,41 @@ import httpcore
3535

3636
export httpcore except parseHeader
3737

38-
const
39-
maxLine = 8*1024
40-
4138
# TODO: If it turns out that the decisions that asynchttpserver makes
4239
# explicitly, about whether to close the client sockets or upgrade them are
4340
# wrong, then add a return value which determines what to do for the callback.
4441
# Also, maybe move `client` out of `Request` object and into the args for
4542
# the proc.
46-
type
47-
Request* = object
48-
client*: AsyncSocket # TODO: Separate this into a Response object?
49-
reqMethod*: HttpMethod
50-
headers*: HttpHeaders
51-
protocol*: tuple[orig: string, major, minor: int]
52-
url*: Uri
53-
hostname*: string ## The hostname of the client that made the request.
54-
body*: string
5543

44+
const
45+
maxLine = 8*1024
46+
47+
when (NimMajor, NimMinor) >= (1, 1):
48+
const
49+
chunkSize = 8*1048 ## This seems perfectly reasonable for default chunkSize.
50+
51+
type
52+
Request* = object
53+
client*: AsyncSocket # TODO: Separate this into a Response object?
54+
reqMethod*: HttpMethod
55+
headers*: HttpHeaders
56+
protocol*: tuple[orig: string, major, minor: int]
57+
url*: Uri
58+
hostname*: string ## The hostname of the client that made the request.
59+
body*: string
60+
bodyStream*: FutureStream[string]
61+
else:
62+
type
63+
Request* = object
64+
client*: AsyncSocket # TODO: Separate this into a Response object?
65+
reqMethod*: HttpMethod
66+
headers*: HttpHeaders
67+
protocol*: tuple[orig: string, major, minor: int]
68+
url*: Uri
69+
hostname*: string ## The hostname of the client that made the request.
70+
body*: string
71+
72+
type
5673
AsyncHttpServer* = ref object
5774
socket: AsyncSocket
5875
reuseAddr: bool
@@ -149,6 +166,8 @@ proc processRequest(
149166
request.hostname.shallowCopy(address)
150167
assert client != nil
151168
request.client = client
169+
when (NimMajor, NimMinor) >= (1, 1):
170+
request.bodyStream = newFutureStream[string]()
152171

153172
# We should skip at least one empty line before the request
154173
# https://tools.ietf.org/html/rfc7230#section-3.5
@@ -243,10 +262,23 @@ proc processRequest(
243262
if contentLength > server.maxBody:
244263
await request.respondError(Http413)
245264
return false
246-
request.body = await client.recv(contentLength)
247-
if request.body.len != contentLength:
248-
await request.respond(Http400, "Bad Request. Content-Length does not match actual.")
249-
return true
265+
266+
when (NimMajor, NimMinor) >= (1, 1):
267+
var remainder = contentLength
268+
while remainder > 0:
269+
let readSize = min(remainder, chunkSize)
270+
let data = await client.recv(read_size)
271+
if data.len != read_size:
272+
await request.respond(Http400, "Bad Request. Content-Length does not match actual.")
273+
return true
274+
await request.bodyStream.write(data)
275+
remainder -= data.len
276+
request.bodyStream.complete()
277+
else:
278+
request.body = await client.recv(contentLength)
279+
if request.body.len != contentLength:
280+
await request.respond(Http400, "Bad Request. Content-Length does not match actual.")
281+
return true
250282
elif request.reqMethod == HttpPost:
251283
await request.respond(Http411, "Content-Length required.")
252284
return true

0 commit comments

Comments
 (0)