summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorHenrique Dias <mrhdias@gmail.com>2020-02-04 20:13:25 +0000
committerGitHub <noreply@github.com>2020-02-04 21:13:25 +0100
commit955465e5f42b1353f69f3bd884908a7ef91ce13b (patch)
tree9d06255bcfda193677c13afcf4a08cf518c4d85f
parentbfe96e069bbdfbbb7ee8979648abeaba233dfe19 (diff)
downloadNim-955465e5f42b1353f69f3bd884908a7ef91ce13b.tar.gz
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.
-rw-r--r--changelog.md1
-rw-r--r--lib/pure/asynchttpserver.nim64
2 files changed, 49 insertions, 16 deletions
diff --git a/changelog.md b/changelog.md
index 3e498e7f8..36b8ef3d4 100644
--- a/changelog.md
+++ b/changelog.md
@@ -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
diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim
index 186f0da41..2754b128b 100644
--- a/lib/pure/asynchttpserver.nim
+++ b/lib/pure/asynchttpserver.nim
@@ -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