It has been a while since I last posted some new Nim stuff so I thought this would be a good one to share. <https://github.com/guzba/curly>
[Curly](https://github.com/guzba/curly) is a new HTTP client built on top of libcurl. What makes Curly interesting is that it enables running multiple HTTP requests in parallel while controlling how and when you want to block. Some highlights are: * Automatic TCP connection re-use (a big performance benefit for HTTPS connections). * Uses HTTP/2 multiplexing when possible (multiple requests in-flight on one TCP connection). * Any number of threads can start any number of requests and choose their blocking / nonblocking behavior. ##### Getting started import curly let curl = newCurly() # Best to start with a single long-lived instance Run ##### A simple request let response = curl.post("https://...", headers, body) # blocks until complete Run ##### Multiple requests in parallel var batch: RequestBatch batch.post("https://...", headers, body) batch.get("https://...") for (response, error) in curl.makeRequests(batch): # blocks until all are complete if error == "": echo response.code else: # Something prevented a response from being received, maybe a connection # interruption, DNS failure, timeout etc. Error here contains more info. echo error Run ##### A single non-blocking request curl.startRequest("GET", "https://...") # doesn't block # do whatever Run ##### Multiple non-blocking requests var batch: RequestBatch batch.get(url1) batch.get(url2) batch.get(url3) batch.get(url4) curl.startRequests(batch) # doesn't block # do whatever Run ##### Handle responses to non-blocking requests let (response, error) = curl.waitForResponse() # blocks until a request is complete if error == "": echo response.code else: echo error # Or use `let answer = curl.pollForResponse()` and `if answer.isSome:` Run By choosing what blocks and doesn't block, you can manage your program's control flow however makes sense for you. #### My production use-case My Mummy HTTP server mostly makes blocking requests to a handful of endpoints through one Curly instance that is used from many threads. This results in great connection re-use to keep latency as low as possible. I do however have one specific HTTP API call I need to make a lot that does not need to block the Mummy request handler. For this, I created a second Curly instance just for these requests, and use `startRequests` instead. I then have a thread that blocks reading responses and handles any cleanup necessary. #### Sequential vs parallel I have [an example you can run to see the time difference between sequential and parallel HTTP requests here](https://github.com/guzba/curly/blob/master/examples/sequential_vs_parallel.nim). Running requests in parallel is obviously going to be much faster than sequential. Thanks for taking a look!
