Re: AsyncHTTPClient crashes saying invalid http version

2020-04-17 Thread dom96
Stop mixing callbacks and async procs. Your `download` async proc finishes 
before your download operation finishes because you are not awaiting the 
`download` proc. Just do this:


await downloader.downloadFile(node.mediaLink(board, thread), downFileName)


Run


Re: AsyncHTTPClient crashes saying invalid http version

2020-04-17 Thread adnan
> Is this a wrong link? because that doesn't look synchronous.

Ops sorry I had changed the link.

> One important thing to know is that you should never use waitFor in an async 
> procedure, use await.

If I use await, the main thread doesn't stop until all the coroutines I create 
inside the loop finishes


proc download(node: JsonNode; downPath, board, thread: string): 
Future[void] {.async.} =
  var
tim = node["tim"].getInt()
ext = node["ext"].getStr()
downloader = newAsyncHttpClient()
downFileName = fmt"{downPath}/{tim}{ext}"
  echo("Downloading ", node.mediaLink(board, thread), " as ", downFileName, 
"...")
  var downloadFuture = downloader.downloadFile(node.mediaLink(board, 
thread), downFileName)
  downloadFuture.callback=
proc() =
  echo("Downloading ", downFileName, " complete")
  result = downloadFuture

proc main() {.async.} =
  var
boardAndThread = parseBoardAndThread(getRawLink())
downPath = getCurrentDir() & "/" & getRawDownPath()
board = boardAndThread[0]
thread = boardAndThread[1]
  if not downPath.existsDir():
echo("Local path not recognized. Creating directory: ", downPath)
createDir downPath
  var
webClient = newAsyncHttpClient()
link = getAPILink(board, thread)
j: string = await webClient.getContent(link)
  
  var
jsonArray = parseJson(j)["posts"].getElems()
filteredArray = jsonArray.filter(proc(it: JsonNode): bool = it{"tim"} 
!= nil)
downloadSignaledArray = filteredArray.map(proc(it: JsonNode): 
Future[void] = it.download(downPath, board, thread))
  for it in downloadSignaledArray:
await it

waitFor main() # never gets to finish downloading.


Run

For context, this is how the json looks like: 
[https://a.4cdn.org/po/thread/570368.json](https://a.4cdn.org/po/thread/570368.json)

I have to extract `tim` and `ext` from each item in the JArray. Then from the 
program argument I have to parse `po` and build the download link like: 
`https://i.4cdn.org/po/1546293948883.png`. I need to download from any JsonNode 
that has `tim` asynchronously. 


Re: AsyncHTTPClient crashes saying invalid http version

2020-04-17 Thread adnan
> To keep multiple connections open at a time you will need a separate 
> HttpClient for each.

Ah that fixes it. Thanks a bunch.


Re: AsyncHTTPClient crashes saying invalid http version

2020-04-17 Thread dom96
> Writing a synchronous downloader script was a breeze: 
> [https://gitlab.com/snippets/1967018](https://gitlab.com/snippets/1967018)

Is this a wrong link? because that doesn't look synchronous.

You're doing a number of things incorrectly here. One important thing to know 
is that you should never use waitFor in an async procedure, use await.

What I believe is the problem though, is that you are reusing the same 
AsyncHttpClient instance for multiple concurrent requests. You need to create 
one AsyncHttpClient instance for each request that you want to be running 
concurrently. I'm going to create an issue on GitHub to show a better error 
message for this case.

The easiest way to make this work would probably be to split up 
parseJson(j)["posts"] into x many lists, then run x async procs on each list 
which create their own AsyncHttpClient and iterate through the list downloading 
each one-by-one (using await instead of storing each future and awaiting them 
all).


Re: AsyncHTTPClient crashes saying invalid http version

2020-04-17 Thread geotre
To keep multiple connections open at a time you will need a separate HttpClient 
for each. Try moving webClient = newAsyncHttpClient() into the for loop so each 
call to webClient.downloadFile has it's own client


AsyncHTTPClient crashes saying invalid http version

2020-04-17 Thread adnan
Hi, I'm still trying to figure out async/await. I am writing a script that 
parses a json returned by a forum API and tries to download all the files 
described by the json. Writing a synchronous downloader script was a breeze: 
[https://play.nim-lang.org/#ix=2gcb](https://play.nim-lang.org/#ix=2gcb)

However I have been struggling with making the downloads asynchronous. First of 
all I used the `AsyncHTTPClient` called `webClient`. Then I iterate over the 
json nodes to look for parts that I need to construct file download link. Then 
I created a `seq[Future[void]]` caled `futures` to store all the futures 
returned by `downloadFile()` method. Then I start the loop. Each time I make a 
call to `downloadFile()`, I store the future into `futures` seq.

Lastly when the loop finishes I call `waitFor futures.all()` to wait for the 
future returned by the `all()` function 
([https://nim-lang.org/docs/asyncfutures.html#all%2Cvarargs%5BFuture%5BT%5D%5D](https://nim-lang.org/docs/asyncfutures.html#all%2Cvarargs%5BFuture%5BT%5D%5D))
 to yield.

And at the very last I `waitFor` my main function.


import strutils, os, json, strformat, httpclient, asyncdispatch

proc mediaLink(node: JsonNode; board, thread: string): string =
  let
fileID = node["tim"].getInt()
ext = node["ext"].getStr()
  
  result = fmt"https://i.4cdn.org/{board}/{fileID}{ext};

proc parseBoardAndThread(uri: string): (string, string) =
var
slashCount = 0
board = newStringOfCap(2)
thread = newStringOfCap(8)

for c in uri:
if c == '/':
slashCount += 1
elif slashCount == 3:
board &= c
elif slashCount == 5:
if c >= '0' and c <= '9':
thread &= c
else:
break
return (board, thread)

proc getAPILink(board, thread: string): string =
  fmt"https://a.4cdn.org/{board}/thread/{thread}.json;

proc getRawLink(): string =
  if os.paramCount() < 1:
when not defined release: # debugging purposes so I can just call 
./threadgrabber without any link and dir
  result = "https://boards.4channel.org/wsg/thread/3360760;
else:
  quit("Link required. Usage: $threadgrabber  ")
  else:
result = paramStr(1)

proc getRawDownPath(): string =
  if os.paramCount() < 2:
when not defined release:
  result = "out"
else:
  quit("Download destination required. Usage: $threadgrabber  ")
  else:
result = paramStr(2)

proc main() {.async.} =
  var
boardAndThread = parseBoardAndThread(getRawLink())
downPath = getCurrentDir() & "/" & getRawDownPath()
board = boardAndThread[0]
thread = boardAndThread[1]
  if not downPath.existsDir():
echo("Local path not recognized. Creating directory: ", downPath)
createDir downPath
  var
webClient = newAsyncHttpClient()
link = getAPILink(board, thread)
j: string = await webClient.getContent(link)
futures: seq[Future[void]]
  
  for node in parseJson(j)["posts"]:
if node{"tim"} != nil:
  var
tim = node["tim"].getInt()
ext = node["ext"].getStr()
downFileName = fmt"{downPath}/{tim}{ext}"
  echo("Downloading ", node.mediaLink(board, thread), " as ", 
downFileName, "...")
  var downloadFuture = webClient.downloadFile(node.mediaLink(board, 
thread), downFileName)
  downloadFuture.callback=
proc() =
  echo("Downloading ", tim, ext, " Complete")
  futures &= downloadFuture
  waitFor futures.all()

waitFor main()


Run

The program crashes with vague error messages, complaining that the http 
version is not correct: 
[https://pastebin.com/2jXHKbMV](https://pastebin.com/2jXHKbMV)

I'm compiling with `--d:ssl --threads:on` flag. Any pointers?