diff --git a/changelog.md b/changelog.md
index d929ea0d941a1..ee2c0569aee48 100644
--- a/changelog.md
+++ b/changelog.md
@@ -72,8 +72,6 @@
## Library changes
-- `asynchttpserver` added an iterator that allows the request body to be read in
- chunks of data when new server "stream" option is set to true.
- `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 9aed0255bbf36..186f0da414650 100644
--- a/lib/pure/asynchttpserver.nim
+++ b/lib/pure/asynchttpserver.nim
@@ -29,129 +29,43 @@
## await req.respond(Http200, "Hello World")
##
## waitFor server.serve(Port(8080), cb)
-##
-## Basic Post request handle
-## =========================
-##
-## This example will create an HTTP server on port 8080. The server will
-## respond with a page with the actual and expected body length after
-## submitting a file.
-##
-## .. code-block::nim
-## import asynchttpserver, asyncdispatch
-## import strutils, strformat
-##
-## const stream = true # for test purposes switch from true to false
-##
-## proc htmlpage(contentLength, bodyLength: int): string =
-## return &"""
-##
-##
-##
-##
-##
-## Expected Body Length: {contentLength} bytes
-## Actual Body Length: {bodyLength} bytes
-##
-##
-## """
-##
-## proc cb(req: Request) {.async.} =
-## var
-## contentLength = 0
-## bodyLength = 0
-## if req.reqMethod == HttpPost:
-## contentLength = req.headers["Content-length"].parseInt
-## if stream:
-## # Read 8*1024 bytes at a time
-## # optional chunkSize parameter. The default is 8*1024
-## for length, data in req.bodyStream(8*1024):
-## let content = await data
-## if length == content.len:
-## bodyLength += content.len
-## else:
-## # Handle exception
-## await req.respond(Http400,
-## "Bad Request. Data read has a different length than the expected.")
-## return
-## else:
-## bodyLength += req.body.len
-## await req.respond(Http200, htmlpage(contentLength, bodyLength))
-##
-## let server = newAsyncHttpServer(maxBody = 10485760, stream = stream) # 10 MB
-## waitFor server.serve(Port(8080), cb)
import tables, asyncnet, asyncdispatch, parseutils, uri, strutils
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.
-
-const
- maxLine = 8*1024
-
-when (NimMajor, NimMinor) >= (1, 1):
- 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
- contentLength*: int
-
- type
- 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 (stream = false), the body of the request is read immediately
-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
- reusePort: bool
- maxBody: int ## The maximum content-length that will be read for the body.
-
-when (NimMajor, NimMinor) >= (1, 1):
- proc newAsyncHttpServer*(reuseAddr = true, reusePort = false,
- maxBody = 8388608, stream = false): AsyncHttpServer =
- ## Creates a new ``AsyncHttpServer`` instance.
- new result
- result.reuseAddr = reuseAddr
- result.reusePort = reusePort
- result.maxBody = maxBody
- result.stream = stream
-else:
- proc newAsyncHttpServer*(reuseAddr = true, reusePort = false,
+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
+
+ AsyncHttpServer* = ref object
+ socket: AsyncSocket
+ reuseAddr: bool
+ reusePort: bool
+ maxBody: int ## The maximum content-length that will be read for the body.
+
+proc newAsyncHttpServer*(reuseAddr = true, reusePort = false,
maxBody = 8388608): AsyncHttpServer =
- ## Creates a new ``AsyncHttpServer`` instance.
- new result
- result.reuseAddr = reuseAddr
- result.reusePort = reusePort
- result.maxBody = maxBody
+ ## Creates a new ``AsyncHttpServer`` instance.
+ new result
+ result.reuseAddr = reuseAddr
+ result.reusePort = reusePort
+ result.maxBody = maxBody
proc addHeaders(msg: var string, headers: HttpHeaders) =
for k, v in headers:
@@ -214,30 +128,13 @@ proc parseProtocol(protocol: string): tuple[orig: string, major, minor: int] =
proc sendStatus(client: AsyncSocket, status: string): Future[void] =
client.send("HTTP/1.1 " & status & "\c\L\c\L")
-when (NimMajor, NimMinor) >= (1, 1):
- iterator bodyStream*(
- request: Request,
- chunkSize: int = 8*1024): (int, Future[string]) =
- ## The chunkSize parameter is optional and default value is 8*1024 bytes.
- ##
- ## This iterator return a tuple with the length of the data that was read
- ## and a future.
- var remainder = request.contentLength
- while remainder > 0:
- let readSize = min(remainder, chunkSize)
- let data = request.client.recv(readSize)
- if data.failed:
- raise newException(ValueError, "Error reading POST data from client.")
- yield (readSize, data)
- remainder -= readSize
-
proc processRequest(
server: AsyncHttpServer,
req: FutureVar[Request],
client: AsyncSocket,
address: string,
lineFut: FutureVar[string],
- callback: proc (request: Request): Future[void] {.closure, gcsafe.}
+ callback: proc (request: Request): Future[void] {.closure, gcsafe.},
): Future[bool] {.async.} =
# Alias `request` to `req.mget()` so we don't have to write `mget` everywhere.
@@ -248,12 +145,10 @@ proc processRequest(
# Header: val
# \n
request.headers.clear()
+ request.body = ""
request.hostname.shallowCopy(address)
assert client != nil
request.client = client
- request.body = ""
- when (NimMajor, NimMinor) >= (1, 1):
- request.contentLength = 0
# We should skip at least one empty line before the request
# https://tools.ietf.org/html/rfc7230#section-3.5
@@ -348,19 +243,10 @@ proc processRequest(
if contentLength > server.maxBody:
await request.respondError(Http413)
return false
-
- when (NimMajor, NimMinor) >= (1, 1):
- request.contentLength = contentLength
- if not server.stream:
- 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
- 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
+ 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