From 707d057f079c89a3c8d5b714db61b88bd0749946 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 22 Feb 2020 11:46:24 +0000 Subject: [PATCH] Revert broken asynchttpserver FutureStream additions. As discussed in #13394, these changes cannot work. Reverted via ``` git revert --no-commit 5bf571f061d53d35aab727f420afd9f415987723 git revert --no-commit abd660c407d00d0c4f2129ff11bfc69badda8ece git revert --no-commit 955465e5f42b1353f69f3bd884908a7ef91ce13b git commit ``` --- changelog.md | 2 - lib/pure/asynchttpserver.nim | 176 ++++++----------------------------- 2 files changed, 31 insertions(+), 147 deletions(-) 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 &""" -## -## -## -## -##
-## File:
-## -##

-## 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