parent
7f67c593c1
commit
4ae520711d
3 changed files with 50 additions and 4 deletions
|
|
@ -21,6 +21,7 @@ type
|
||||||
queue: Deque[T]
|
queue: Deque[T]
|
||||||
finished: bool
|
finished: bool
|
||||||
cb: proc () {.closure, gcsafe.}
|
cb: proc () {.closure, gcsafe.}
|
||||||
|
error*: ref Exception
|
||||||
|
|
||||||
proc newFutureStream*[T](fromProc = "unspecified"): FutureStream[T] =
|
proc newFutureStream*[T](fromProc = "unspecified"): FutureStream[T] =
|
||||||
## Create a new ``FutureStream``. This future's callback is activated when
|
## Create a new ``FutureStream``. This future's callback is activated when
|
||||||
|
|
@ -40,10 +41,19 @@ proc newFutureStream*[T](fromProc = "unspecified"): FutureStream[T] =
|
||||||
|
|
||||||
proc complete*[T](future: FutureStream[T]) =
|
proc complete*[T](future: FutureStream[T]) =
|
||||||
## Completes a ``FutureStream`` signalling the end of data.
|
## Completes a ``FutureStream`` signalling the end of data.
|
||||||
|
assert(future.error == nil, "Trying to complete failed stream")
|
||||||
future.finished = true
|
future.finished = true
|
||||||
if not future.cb.isNil:
|
if not future.cb.isNil:
|
||||||
future.cb()
|
future.cb()
|
||||||
|
|
||||||
|
proc fail*[T](future: FutureStream[T], error: ref Exception) =
|
||||||
|
## Completes ``future`` with ``error``.
|
||||||
|
assert(not future.finished)
|
||||||
|
future.finished = true
|
||||||
|
future.error = error
|
||||||
|
if not future.cb.isNil:
|
||||||
|
future.cb()
|
||||||
|
|
||||||
proc `callback=`*[T](future: FutureStream[T],
|
proc `callback=`*[T](future: FutureStream[T],
|
||||||
cb: proc (future: FutureStream[T]) {.closure, gcsafe.}) =
|
cb: proc (future: FutureStream[T]) {.closure, gcsafe.}) =
|
||||||
## Sets the callback proc to be called when data was placed inside the
|
## Sets the callback proc to be called when data was placed inside the
|
||||||
|
|
@ -65,6 +75,10 @@ proc finished*[T](future: FutureStream[T]): bool =
|
||||||
## no data waiting to be retrieved.
|
## no data waiting to be retrieved.
|
||||||
result = future.finished and future.queue.len == 0
|
result = future.finished and future.queue.len == 0
|
||||||
|
|
||||||
|
proc failed*[T](future: FutureStream[T]): bool =
|
||||||
|
## Determines whether ``future`` completed with an error.
|
||||||
|
return future.error != nil
|
||||||
|
|
||||||
proc write*[T](future: FutureStream[T], value: T): Future[void] =
|
proc write*[T](future: FutureStream[T], value: T): Future[void] =
|
||||||
## Writes the specified value inside the specified future stream.
|
## Writes the specified value inside the specified future stream.
|
||||||
##
|
##
|
||||||
|
|
@ -107,7 +121,10 @@ proc read*[T](future: FutureStream[T]): owned(Future[(bool, T)]) =
|
||||||
res[0] = true
|
res[0] = true
|
||||||
res[1] = fs.queue.popFirst()
|
res[1] = fs.queue.popFirst()
|
||||||
|
|
||||||
resFut.complete(res)
|
if fs.failed:
|
||||||
|
resFut.fail(fs.error)
|
||||||
|
else:
|
||||||
|
resFut.complete(res)
|
||||||
|
|
||||||
# If the saved callback isn't nil then let's call it.
|
# If the saved callback isn't nil then let's call it.
|
||||||
if not savedCb.isNil: savedCb()
|
if not savedCb.isNil: savedCb()
|
||||||
|
|
|
||||||
|
|
@ -841,8 +841,11 @@ proc parseResponse(client: HttpClient | AsyncHttpClient,
|
||||||
client.bodyStream = newFutureStream[string]("parseResponse")
|
client.bodyStream = newFutureStream[string]("parseResponse")
|
||||||
result.bodyStream = client.bodyStream
|
result.bodyStream = client.bodyStream
|
||||||
assert(client.parseBodyFut.isNil or client.parseBodyFut.finished)
|
assert(client.parseBodyFut.isNil or client.parseBodyFut.finished)
|
||||||
|
# do not wait here for the body request to complete
|
||||||
client.parseBodyFut = parseBody(client, result.headers, result.version)
|
client.parseBodyFut = parseBody(client, result.headers, result.version)
|
||||||
# do not wait here for the body request to complete
|
client.parseBodyFut.addCallback do():
|
||||||
|
if client.parseBodyFut.failed:
|
||||||
|
client.bodyStream.fail(client.parseBodyFut.error)
|
||||||
|
|
||||||
proc newConnection(client: HttpClient | AsyncHttpClient,
|
proc newConnection(client: HttpClient | AsyncHttpClient,
|
||||||
url: Uri) {.multisync.} =
|
url: Uri) {.multisync.} =
|
||||||
|
|
@ -1228,13 +1231,16 @@ proc downloadFile*(client: AsyncHttpClient, url: Uri | string,
|
||||||
|
|
||||||
client.bodyStream = newFutureStream[string]("downloadFile")
|
client.bodyStream = newFutureStream[string]("downloadFile")
|
||||||
var file = openAsync(filename, fmWrite)
|
var file = openAsync(filename, fmWrite)
|
||||||
|
defer: file.close()
|
||||||
# Let `parseBody` write response data into client.bodyStream in the
|
# Let `parseBody` write response data into client.bodyStream in the
|
||||||
# background.
|
# background.
|
||||||
asyncCheck parseBody(client, resp.headers, resp.version)
|
let parseBodyFut = parseBody(client, resp.headers, resp.version)
|
||||||
|
parseBodyFut.addCallback do():
|
||||||
|
if parseBodyFut.failed:
|
||||||
|
client.bodyStream.fail(parseBodyFut.error)
|
||||||
# The `writeFromStream` proc will complete once all the data in the
|
# The `writeFromStream` proc will complete once all the data in the
|
||||||
# `bodyStream` has been written to the file.
|
# `bodyStream` has been written to the file.
|
||||||
await file.writeFromStream(client.bodyStream)
|
await file.writeFromStream(client.bodyStream)
|
||||||
file.close()
|
|
||||||
|
|
||||||
if resp.code.is4xx or resp.code.is5xx:
|
if resp.code.is4xx or resp.code.is5xx:
|
||||||
raise newException(HttpRequestError, resp.status)
|
raise newException(HttpRequestError, resp.status)
|
||||||
|
|
|
||||||
23
tests/stdlib/thttpclient_standalone.nim
Normal file
23
tests/stdlib/thttpclient_standalone.nim
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
discard """
|
||||||
|
cmd: "nim c --threads:on $file"
|
||||||
|
"""
|
||||||
|
|
||||||
|
import asynchttpserver, httpclient, asyncdispatch
|
||||||
|
|
||||||
|
block: # bug #16436
|
||||||
|
proc startServer() {.async.} =
|
||||||
|
proc cb(req: Request) {.async.} =
|
||||||
|
let headers = { "Content-length": "15"} # Provide invalid content-length
|
||||||
|
await req.respond(Http200, "Hello World", headers.newHttpHeaders())
|
||||||
|
|
||||||
|
var server = newAsyncHttpServer()
|
||||||
|
await server.serve(Port(5555), cb)
|
||||||
|
|
||||||
|
proc runClient() {.async.} =
|
||||||
|
let c = newAsyncHttpClient(headers = {"Connection": "close"}.newHttpHeaders)
|
||||||
|
let r = await c.getContent("http://127.0.0.1:5555")
|
||||||
|
doAssert false, "should fail earlier"
|
||||||
|
|
||||||
|
asyncCheck startServer()
|
||||||
|
doAssertRaises(ProtocolError):
|
||||||
|
waitFor runClient()
|
||||||
Loading…
Add table
Add a link
Reference in a new issue