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!

Reply via email to