Skip to content

Commit

Permalink
Fix to asynchttpserver form data/body broken with nim-lang#13147
Browse files Browse the repository at this point in the history
This fix add a new server option that 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 and works with the jester and the rosencrantz.

The new server option "stream" when it is true uses the Future Streams otherwise all body is readed at once. The "stream" option by default is set to false.

The idea of this option I took from the python requests module (https://requests.readthedocs.io/en/v1.2.3/api/).
"When stream=True is set on the request, this avoids reading the content at once into memory for large responses. The chunk size is the number of bytes it should read into memory. This is not necessarily the length of each item returned as decoding can take place."

Example:
let server = newAsyncHttpServer(stream = true) # if true use Future Streams
waitFor server.serve(Port(8080), cb)
  • Loading branch information
mrhdias authored Feb 12, 2020
1 parent c446c0f commit 3bacfd1
Showing 1 changed file with 19 additions and 34 deletions.
53 changes: 19 additions & 34 deletions lib/pure/asynchttpserver.nim
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
## bodyLength += data[1].len
## await req.respond(Http200, htmlpage(contentLength, bodyLength))
##
## let server = newAsyncHttpServer(maxBody = 10485760) # 10 MB
## let server = newAsyncHttpServer(maxBody = 10485760, stream = true) # 10 MB
## waitFor server.serve(Port(8080), cb)

import tables, asyncnet, asyncdispatch, parseutils, uri, strutils
Expand All @@ -91,46 +91,34 @@ export httpcore except parseHeader

const
maxLine = 8*1024

when (NimMajor, NimMinor) >= (1, 1):
const
chunkSize = 8*1024 ## 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 # For future removal
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
chunkSize = 8*1024 ## 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]

AsyncHttpServer* = ref object
socket: AsyncSocket
reuseAddr: bool
reusePort: bool
maxBody: int ## The maximum content-length that will be read for the body.
stream: bool ## By default, the body of the response is readed immediately

proc newAsyncHttpServer*(reuseAddr = true, reusePort = false,
maxBody = 8388608): AsyncHttpServer =
maxBody = 8388608, stream = false): AsyncHttpServer =
## Creates a new ``AsyncHttpServer`` instance.
new result
result.reuseAddr = reuseAddr
result.reusePort = reusePort
result.maxBody = maxBody
result.stream = stream

proc addHeaders(msg: var string, headers: HttpHeaders) =
for k, v in headers:
Expand Down Expand Up @@ -213,13 +201,10 @@ proc processRequest(
request.hostname.shallowCopy(address)
assert client != nil
request.client = client
when (NimMajor, NimMinor) >= (1, 1):
if server.stream:
request.bodyStream = newFutureStream[string]()
# To uncomment in the future after compatibility issues
# with third parties are solved
# else:
# request.body = ""
request.body = "" # Temporary fix for future removal
else:
request.body = ""

# We should skip at least one empty line before the request
# https://tools.ietf.org/html/rfc7230#section-3.5
Expand Down Expand Up @@ -315,7 +300,7 @@ proc processRequest(
await request.respondError(Http413)
return false

when (NimMajor, NimMinor) >= (1, 1):
if server.stream:
var remainder = contentLength
while remainder > 0:
let readSize = min(remainder, chunkSize)
Expand Down

0 comments on commit 3bacfd1

Please sign in to comment.