AsyncHttpClient: return from requests before body completion

Store the body completion future at the client and wait for it to
complete before issuing additional requests. This allows the body
FutureStream reader to drain the stream and read buffers to be freed
asynchronously.

Fix #8109
This commit is contained in:
Emery Hemingway 2018-08-07 17:36:56 +02:00
commit 817e4bb2fe

View file

@ -807,6 +807,7 @@ type
lastProgressReport: float lastProgressReport: float
when SocketType is AsyncSocket: when SocketType is AsyncSocket:
bodyStream: FutureStream[string] bodyStream: FutureStream[string]
parseBodyFut: Future[void]
else: else:
bodyStream: Stream bodyStream: Stream
getBody: bool ## When `false`, the body is never read in requestAux. getBody: bool ## When `false`, the body is never read in requestAux.
@ -1066,10 +1067,14 @@ proc parseResponse(client: HttpClient | AsyncHttpClient,
if getBody: if getBody:
when client is HttpClient: when client is HttpClient:
client.bodyStream = newStringStream() client.bodyStream = newStringStream()
result.bodyStream = client.bodyStream
parseBody(client, result.headers, result.version)
else: else:
client.bodyStream = newFutureStream[string]("parseResponse") client.bodyStream = newFutureStream[string]("parseResponse")
await parseBody(client, result.headers, result.version)
result.bodyStream = client.bodyStream result.bodyStream = client.bodyStream
assert(client.parseBodyFut.isNil or client.parseBodyFut.finished)
client.parseBodyFut = parseBody(client, result.headers, result.version)
# do not wait here for the body request to complete
proc newConnection(client: HttpClient | AsyncHttpClient, proc newConnection(client: HttpClient | AsyncHttpClient,
url: Uri) {.multisync.} = url: Uri) {.multisync.} =
@ -1159,6 +1164,12 @@ proc requestAux(client: HttpClient | AsyncHttpClient, url: string,
# Helper that actually makes the request. Does not handle redirects. # Helper that actually makes the request. Does not handle redirects.
let requestUrl = parseUri(url) let requestUrl = parseUri(url)
when client is AsyncHttpClient:
if not client.parseBodyFut.isNil:
# let the current operation finish before making another request
await client.parseBodyFut
client.parseBodyFut = nil
await newConnection(client, requestUrl) await newConnection(client, requestUrl)
let effectiveHeaders = client.headers.override(headers) let effectiveHeaders = client.headers.override(headers)