diff options
-rw-r--r-- | lib/pure/asyncfile.nim | 12 | ||||
-rw-r--r-- | lib/pure/httpclient.nim | 60 | ||||
-rw-r--r-- | tests/stdlib/thttpclient.nim | 6 |
3 files changed, 62 insertions, 16 deletions
diff --git a/lib/pure/asyncfile.nim b/lib/pure/asyncfile.nim index 0241e4796..5a23f3ba2 100644 --- a/lib/pure/asyncfile.nim +++ b/lib/pure/asyncfile.nim @@ -476,3 +476,15 @@ proc close*(f: AsyncFile) = if close(f.fd.cint) == -1: raiseOSError(osLastError()) +proc writeFromStream(f: AsyncFile, fut: FutureStream[string]) {.async.} = + while true: + let (hasValue, value) = await fut.take() + if hasValue: + await f.write(value) + else: + break + +proc getWriteStream*(f: AsyncFile): FutureStream[string] = + ## Returns a new stream that can be used for writing to the file. + result = newFutureStream[string]() + asyncCheck writeFromStream(f, result) diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 024643384..4f26c078a 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -118,7 +118,7 @@ import net, strutils, uri, parseutils, strtabs, base64, os, mimetypes, math, random, httpcore, times, tables, streams -import asyncnet, asyncdispatch +import asyncnet, asyncdispatch, asyncfile import nativesockets export httpcore except parseHeader # TODO: The ``except`` doesn't work @@ -129,7 +129,7 @@ type status*: string headers*: HttpHeaders body: string # TODO: here for compatibility with old httpclient procs. - bodyStream*: StringStream + bodyStream*: Stream AsyncResponse* = ref object version*: string @@ -696,10 +696,13 @@ proc postContent*(url: string, extraHeaders = "", body = "", proc downloadFile*(url: string, outputFilename: string, sslContext: SSLContext = defaultSSLContext, timeout = -1, userAgent = defUserAgent, - proxy: Proxy = nil) = + proxy: Proxy = nil) {.deprecated.} = ## | Downloads ``url`` and saves it to ``outputFilename`` ## | An optional timeout can be specified in milliseconds, if reading from the ## server takes longer than specified an ETimeout exception will be raised. + ## + ## **Deprecated since version 0.16.2**: use ``HttpClient.downloadFile`` + ## instead. var f: File if open(f, outputFilename, fmWrite): f.write(getContent(url, sslContext = sslContext, timeout = timeout, @@ -781,7 +784,8 @@ type when SocketType is AsyncSocket: bodyStream: FutureStream[string] else: - bodyStream: StringStream + bodyStream: Stream + getBody: bool ## When `false`, the body is never read in requestAux. type HttpClient* = HttpClientBase[Socket] @@ -812,6 +816,7 @@ proc newHttpClient*(userAgent = defUserAgent, result.timeout = timeout result.onProgressChanged = nil result.bodyStream = newStringStream() + result.getBody = true when defined(ssl): result.sslContext = sslContext @@ -843,6 +848,7 @@ proc newAsyncHttpClient*(userAgent = defUserAgent, result.timeout = -1 # TODO result.onProgressChanged = nil result.bodyStream = newFutureStream[string]("newAsyncHttpClient") + result.getBody = true when defined(ssl): result.sslContext = sslContext @@ -933,10 +939,8 @@ proc parseBody(client: HttpClient | AsyncHttpClient, client.oneSecondProgress = 0 client.lastProgressReport = 0 - when client is HttpClient: - client.bodyStream = newStringStream() - else: - client.bodyStream = newFutureStream[string]("parseResponse") + when client is AsyncHttpClient: + assert(not client.bodyStream.finished) if headers.getOrDefault"Transfer-Encoding" == "chunked": await parseChunks(client) @@ -1020,7 +1024,12 @@ proc parseResponse(client: HttpClient | AsyncHttpClient, if not fullyRead: httpError("Connection was closed before full request has been made") + if getBody: + when client is HttpClient: + client.bodyStream = newStringStream() + else: + client.bodyStream = newFutureStream[string]("parseResponse") await parseBody(client, result.headers, result.version) result.bodyStream = client.bodyStream @@ -1112,8 +1121,9 @@ proc requestAux(client: HttpClient | AsyncHttpClient, url: string, if body != "": await client.socket.send(body) - result = await parseResponse(client, - httpMethod.toLower() notin ["head", "connect"]) + let getBody = httpMethod.toLowerAscii() notin ["head", "connect"] and + client.getBody + result = await parseResponse(client, getBody) # Restore the clients proxy in case it was overwritten. client.proxy = savedProxy @@ -1200,16 +1210,14 @@ proc post*(client: HttpClient | AsyncHttpClient, url: string, body = "", headers["Content-Type"] = mpHeader.split(": ")[1] headers["Content-Length"] = $len(xb) - result = await client.requestAux(url, $HttpPOST, xb, - headers = headers) + result = await client.requestAux(url, $HttpPOST, xb, headers) # Handle redirects. var lastURL = url for i in 1..client.maxRedirects: if result.status.redirection(): let redirectTo = getNewLocation(lastURL, result.headers) var meth = if result.status != "307": HttpGet else: HttpPost - result = await client.requestAux(redirectTo, $meth, xb, - headers = headers) + result = await client.requestAux(redirectTo, $meth, xb, headers) lastURL = redirectTo proc postContent*(client: HttpClient | AsyncHttpClient, url: string, @@ -1228,3 +1236,27 @@ proc postContent*(client: HttpClient | AsyncHttpClient, url: string, raise newException(HttpRequestError, resp.status) else: return await resp.bodyStream.readAll() + +proc downloadFile*(client: HttpClient | AsyncHttpClient, + url: string, filename: string): Future[void] {.multisync.} = + ## Downloads ``url`` and saves it to ``filename``. + client.getBody = false + let resp = await client.get(url) + + when client is HttpClient: + client.bodyStream = newFileStream(filename, fmWrite) + if client.bodyStream.isNil: + fileError("Unable to open file") + else: + var f = openAsync(filename, fmWrite) + client.bodyStream = f.getWriteStream() + + await parseBody(client, resp.headers, resp.version) + + when client is HttpClient: + client.bodyStream.close() + else: + f.close() + + if resp.code.is4xx or resp.code.is5xx: + raise newException(HttpRequestError, resp.status) \ No newline at end of file diff --git a/tests/stdlib/thttpclient.nim b/tests/stdlib/thttpclient.nim index c5739f0e1..62c1ebee7 100644 --- a/tests/stdlib/thttpclient.nim +++ b/tests/stdlib/thttpclient.nim @@ -49,7 +49,8 @@ proc asyncTest() {.async.} = echo("Downloaded ", progress, " of ", total) echo("Current rate: ", speed div 1000, "kb/s") client.onProgressChanged = onProgressChanged - discard await client.getContent("http://speedtest-ams2.digitalocean.com/100mb.test") + await client.downloadFile("http://speedtest-ams2.digitalocean.com/100mb.test", + "100mb.test") client.close() @@ -96,7 +97,8 @@ proc syncTest() = echo("Downloaded ", progress, " of ", total) echo("Current rate: ", speed div 1000, "kb/s") client.onProgressChanged = onProgressChanged - discard client.getContent("http://speedtest-ams2.digitalocean.com/100mb.test") + client.downloadFile("http://speedtest-ams2.digitalocean.com/100mb.test", + "100mb.test") client.close() |