Revert broken asynchttpserver FutureStream additions.
As discussed in #13394, these changes cannot work. Reverted via ``` git revert --no-commit5bf571f061git revert --no-commitabd660c407git revert --no-commit955465e5f4git commit ```
This commit is contained in:
parent
b80d293a3f
commit
ec8a17cc86
2 changed files with 29 additions and 145 deletions
|
|
@ -98,8 +98,6 @@ echo f
|
||||||
|
|
||||||
## Library changes
|
## 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`
|
- `asyncdispatch.drain` now properly takes into account `selector.hasPendingOperations`
|
||||||
and only returns once all pending async operations are guaranteed to have completed.
|
and only returns once all pending async operations are guaranteed to have completed.
|
||||||
- `asyncdispatch.drain` now consistently uses the passed timeout value for all
|
- `asyncdispatch.drain` now consistently uses the passed timeout value for all
|
||||||
|
|
|
||||||
|
|
@ -29,129 +29,43 @@
|
||||||
## await req.respond(Http200, "Hello World")
|
## await req.respond(Http200, "Hello World")
|
||||||
##
|
##
|
||||||
## waitFor server.serve(Port(8080), cb)
|
## 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 &"""
|
|
||||||
## <!Doctype html>
|
|
||||||
## <html lang="en">
|
|
||||||
## <head><meta charset="utf-8"/></head>
|
|
||||||
## <body>
|
|
||||||
## <form action="/" method="post" enctype="multipart/form-data">
|
|
||||||
## File: <input type="file" name="testfile" accept="text/*"><br />
|
|
||||||
## <input style="margin:10px 0;" type="submit">
|
|
||||||
## </form><br />
|
|
||||||
## Expected Body Length: {contentLength} bytes<br />
|
|
||||||
## Actual Body Length: {bodyLength} bytes
|
|
||||||
## </body>
|
|
||||||
## </html>
|
|
||||||
## """
|
|
||||||
##
|
|
||||||
## 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 tables, asyncnet, asyncdispatch, parseutils, uri, strutils
|
||||||
import httpcore
|
import httpcore
|
||||||
|
|
||||||
export httpcore except parseHeader
|
export httpcore except parseHeader
|
||||||
|
|
||||||
|
const
|
||||||
|
maxLine = 8*1024
|
||||||
|
|
||||||
# TODO: If it turns out that the decisions that asynchttpserver makes
|
# TODO: If it turns out that the decisions that asynchttpserver makes
|
||||||
# explicitly, about whether to close the client sockets or upgrade them are
|
# 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.
|
# 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
|
# Also, maybe move `client` out of `Request` object and into the args for
|
||||||
# the proc.
|
# 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
|
AsyncHttpServer* = ref object
|
||||||
maxLine = 8*1024
|
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,
|
||||||
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,
|
|
||||||
maxBody = 8388608): AsyncHttpServer =
|
maxBody = 8388608): AsyncHttpServer =
|
||||||
## Creates a new ``AsyncHttpServer`` instance.
|
## Creates a new ``AsyncHttpServer`` instance.
|
||||||
new result
|
new result
|
||||||
result.reuseAddr = reuseAddr
|
result.reuseAddr = reuseAddr
|
||||||
result.reusePort = reusePort
|
result.reusePort = reusePort
|
||||||
result.maxBody = maxBody
|
result.maxBody = maxBody
|
||||||
|
|
||||||
proc addHeaders(msg: var string, headers: HttpHeaders) =
|
proc addHeaders(msg: var string, headers: HttpHeaders) =
|
||||||
for k, v in headers:
|
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] =
|
proc sendStatus(client: AsyncSocket, status: string): Future[void] =
|
||||||
client.send("HTTP/1.1 " & status & "\c\L\c\L")
|
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(
|
proc processRequest(
|
||||||
server: AsyncHttpServer,
|
server: AsyncHttpServer,
|
||||||
req: FutureVar[Request],
|
req: FutureVar[Request],
|
||||||
client: AsyncSocket,
|
client: AsyncSocket,
|
||||||
address: string,
|
address: string,
|
||||||
lineFut: FutureVar[string],
|
lineFut: FutureVar[string],
|
||||||
callback: proc (request: Request): Future[void] {.closure, gcsafe.}
|
callback: proc (request: Request): Future[void] {.closure, gcsafe.},
|
||||||
): Future[bool] {.async.} =
|
): Future[bool] {.async.} =
|
||||||
|
|
||||||
# Alias `request` to `req.mget()` so we don't have to write `mget` everywhere.
|
# Alias `request` to `req.mget()` so we don't have to write `mget` everywhere.
|
||||||
|
|
@ -248,12 +145,10 @@ proc processRequest(
|
||||||
# Header: val
|
# Header: val
|
||||||
# \n
|
# \n
|
||||||
request.headers.clear()
|
request.headers.clear()
|
||||||
|
request.body = ""
|
||||||
request.hostname.shallowCopy(address)
|
request.hostname.shallowCopy(address)
|
||||||
assert client != nil
|
assert client != nil
|
||||||
request.client = client
|
request.client = client
|
||||||
request.body = ""
|
|
||||||
when (NimMajor, NimMinor) >= (1, 1):
|
|
||||||
request.contentLength = 0
|
|
||||||
|
|
||||||
# We should skip at least one empty line before the request
|
# We should skip at least one empty line before the request
|
||||||
# https://tools.ietf.org/html/rfc7230#section-3.5
|
# https://tools.ietf.org/html/rfc7230#section-3.5
|
||||||
|
|
@ -348,19 +243,10 @@ proc processRequest(
|
||||||
if contentLength > server.maxBody:
|
if contentLength > server.maxBody:
|
||||||
await request.respondError(Http413)
|
await request.respondError(Http413)
|
||||||
return false
|
return false
|
||||||
|
request.body = await client.recv(contentLength)
|
||||||
when (NimMajor, NimMinor) >= (1, 1):
|
if request.body.len != contentLength:
|
||||||
request.contentLength = contentLength
|
await request.respond(Http400, "Bad Request. Content-Length does not match actual.")
|
||||||
if not server.stream:
|
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
|
|
||||||
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:
|
elif request.reqMethod == HttpPost:
|
||||||
await request.respond(Http411, "Content-Length required.")
|
await request.respond(Http411, "Content-Length required.")
|
||||||
return true
|
return true
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue