Hi everybody,
Just faced with strange thing:
looks like defer statement does not work within async dispath, here is the code:
proc get_data*(): Future[string] {.async.} =
let DOWNLOAD_API_URL = "some HTTP address"
try:
let client = newAsyncHttpClient()
defer: client.close()
client.headers = newHttpHeaders({"Content-Type": "application/json"})
let body = "some body"
let resp = await client.request(DOWNLOAD_API_URL, httpMethod = HttpPost, body = $body)
return await resp.body
except Exception as e:
return "Connection failed: " & e.msg
discard WaitFor get_data()
This code causes connection leak with CLOSE-WAIT status. Surely adding straight close.client() statement at the end of proc solves the problem, but whats wrong with defer? Does it work if you put the client.close() in a finally: section at the end of the try..except?
I've heard Araq say that defer is hard to maintain the past, so wouldn't be surprised if there are bugs with it - maybe it just doesn't work in closure iterators (the underlying mechanism used by async).
With arc/orc you can implement a more robust defer in just a few lines of code, so maybe Nim 2.0 should just ditch the defer keyword and and replace it with a library function like this?
type DeferCode = object
f: proc()
proc `=destroy`(d: var DeferCode) =
d.f()
template myDefer(body: untyped) =
let d {.used.} = DeferCode(f: proc () = body)
myDefer echo "bye 1"
myDefer:
echo "bye 2"
echo "hi"
# output:
# hi
# bye 2
# bye 1
newAsyncHttpClient can't fail though, so I'm fairly certain you could do something like this:
proc get_data*(): Future[string] {.async.} =
let DOWNLOAD_API_URL = "some HTTP address"
let client = newAsyncHttpClient()
client.headers = newHttpHeaders({"Content-Type": "application/json"})
let body = "some body"
try:
let resp = await client.request(DOWNLOAD_API_URL, httpMethod = HttpPost, body = body)
return await resp.body
except Exception as e:
return "Connection failed: " & e.msg
finally:
client.close()
discard waitFor get_data()