#async-io #http-request #actor-model #async-http-client #curl #non-blocking

curl-http-client

This is a wrapper for Easy2 from curl-rust crate for ergonomic use and can perform synchronously and asynchronously using async-curl crate that uses an actor model (Message passing) to achieve a non-blocking I/O

19 releases (11 stable)

2.3.3 Apr 8, 2024
2.3.0 Mar 17, 2024
1.0.1 Feb 17, 2024
0.4.3 Feb 12, 2024
0.2.2 Oct 29, 2023

#81 in HTTP client

37 downloads per month

MIT license

7.5MB
2K SLoC

curl-http-client

This is a wrapper for Easy2 from curl-rust crate for ergonomic use and is able to perform synchronously and asynchronously using async-curl crate that uses an actor model (Message passing) to achieve a non-blocking I/O.

Latest Version License Documentation Build Status Downloads

Asynchronous Examples

Get Request

use async_curl::CurlActor;
use curl_http_client::*;
use http::{Method, Request};
use url::Url;

#[tokio::main(flavor = "current_thread")]
async fn main() {
    let actor = CurlActor::new();
    let collector = Collector::Ram(Vec::new());

    let request = Request::builder()
        .uri("<SOURCE URL>")
        .method(Method::GET)
        .body(None)
        .unwrap();

    let response = HttpClient::new(collector)
        .request(request).unwrap()
        .nonblocking(actor)
        .perform()
        .await.unwrap();
    println!("Response: {:?}", response);
}

Post Request

use async_curl::CurlActor;
use curl_http_client::*;
use http::{Method, Request};
use url::Url;

#[tokio::main(flavor = "current_thread")]
async fn main() {
    let actor = CurlActor::new();
    let collector = Collector::Ram(Vec::new());

    let request = Request::builder()
        .uri("<TARGET URL>")
        .method(Method::POST)
        .body(Some("test body".as_bytes().to_vec()))
        .unwrap();

    let response = HttpClient::new(collector)
        .request(request).unwrap()
        .nonblocking(actor)
        .perform()
        .await.unwrap();

    println!("Response: {:?}", response);
}

Downloading a File

use std::path::PathBuf;

use async_curl::CurlActor;
use curl_http_client::*;
use http::{Method, Request};
use url::Url;

#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let actor = CurlActor::new();

    let collector = Collector::File(FileInfo::path(PathBuf::from("<FILE PATH TO SAVE>")));

    let request = Request::builder()
        .uri("<SOURCE URL>")
        .method(Method::GET)
        .body(None)
        .unwrap();

    let response = HttpClient::new(collector)
        .request(request)
        .unwrap()
        .nonblocking(actor)
        .perform()
        .await.unwrap();

    println!("Response: {:?}", response);
    Ok(())
}

Uploading a File

use std::{fs, path::PathBuf};

use async_curl::CurlActor;
use curl_http_client::*;
use http::{HeaderMap, Method, Request};
use url::Url;

#[tokio::main(flavor = "current_thread")]
async fn main() {
    let file_to_be_uploaded = PathBuf::from("<FILE PATH TO BE UPLOADED>");
    let file_size = fs::metadata(file_to_be_uploaded.as_path()).unwrap().len() as usize;

    let actor = CurlActor::new();
    let collector = Collector::File(FileInfo::path(file_to_be_uploaded));

    let request = Request::builder()
        .uri("<TARGET URL>")
        .method(Method::PUT)
        .body(None)
        .unwrap();

    let response = HttpClient::new(collector)
        .upload_file_size(FileSize::from(file_size)).unwrap()
        .request(request).unwrap()
        .nonblocking(actor)
        .perform()
        .await.unwrap();

    println!("Response: {:?}", response);
}

Concurrency

use async_curl::CurlActor;
use curl_http_client::*;
use futures::future;
use http::{HeaderMap, Method, Request};
use url::Url;

#[tokio::main(flavor = "current_thread")]
async fn main() {
    const NUM_CONCURRENT: usize = 5;

    let actor = CurlActor::new();
    let mut handles = Vec::new();

    for _n in 0..NUM_CONCURRENT {
        let actor = actor.clone();

        let handle = tokio::spawn(async move {
            let collector = Collector::Ram(Vec::new());
            let request = Request::builder()
                .uri("https://www.rust-lang.org/")
                .method(Method::GET)
                .body(None)
                .unwrap();

            let response = HttpClient::new(collector)
                .request(request)
                .unwrap()
                .nonblocking(actor)
                .perform()
                .await
                .unwrap();
            println!("Response: {:?}", response);
        });
        handles.push(handle);
    }

    let results: Vec<Result<_, _>> = future::join_all(handles).await;

    for (_i, result) in results.into_iter().enumerate() {
        result.unwrap();
    }
}

Resume Downloading a File

use std::fs;
use std::path::PathBuf;

use async_curl::CurlActor;
use curl_http_client::*;
use http::{HeaderMap, Method, Request};
use url::Url;

#[tokio::main(flavor = "current_thread")]
async fn main() {
    let actor = CurlActor::new();
    let save_to = PathBuf::from("<FILE PATH TO SAVE>");
    let collector = Collector::File(FileInfo::path(save_to.clone()));

    let partial_download_file_size = fs::metadata(save_to.as_path()).unwrap().len() as usize;
    let request = Request::builder()
        .uri("<SOURCE URL>")
        .method(Method::GET)
        .body(None)
        .unwrap();

    let response = HttpClient::new(collector)
        .resume_from(BytesOffset::from(partial_download_file_size)).unwrap()
        .request(request).unwrap()
        .nonblocking(actor)
        .perform()
        .await.unwrap();

    println!("Response: {:?}", response);
}

Downloading a File with download speed information sent to different task

use std::path::PathBuf;

use async_curl::CurlActor;
use curl_http_client::*;
use http::{HeaderMap, Method, Request};
use tokio::sync::mpsc::channel;
use url::Url;

#[tokio::main(flavor = "current_thread")]
async fn main() {
    let (tx, mut rx) = channel(1);

    let actor = CurlActor::new();
    let file_info = FileInfo::path(PathBuf::from("<FILE PATH TO SAVE>")).with_transfer_speed_sender(tx);
    let collector = Collector::File(file_info);

    let handle = tokio::spawn(async move {
        while let Some(speed) = rx.recv().await {
            println!("Download Speed: {} kB/s", speed.as_bytes_per_sec());
        }
    });

    let request = Request::builder()
        .uri("<SOURCE URL>")
        .method(Method::GET)
        .body(None)
        .unwrap();

    let response = HttpClient::new(collector)
        .request(request).unwrap()
        .nonblocking(actor)
        .perform()
        .await.unwrap();

    println!("Response: {:?}", response);

    handle.abort();
}

Uploading a File with upload speed information sent to different task

use std::{fs, path::PathBuf};

use async_curl::CurlActor;
use curl_http_client::*;
use http::{HeaderMap, Method, Request};
use tokio::sync::mpsc::channel;
use url::Url;

#[tokio::main(flavor = "current_thread")]
async fn main() {
    let (tx, mut rx) = channel(1);

    let file_to_be_uploaded = PathBuf::from("<FILE PATH TO BE UPLOADED>");
    let file_size = fs::metadata(file_to_be_uploaded.as_path()).unwrap().len() as usize;

    let actor = CurlActor::new();
    let file_info = FileInfo::path(file_to_be_uploaded).with_transfer_speed_sender(tx);
    let collector = Collector::File(file_info);

    let handle = tokio::spawn(async move {
        while let Some(speed) = rx.recv().await {
            println!("Upload Speed: {} kB/s", speed.as_bytes_per_sec());
        }
    });

    let request = Request::builder()
        .uri("<TARGET URL>")
        .method(Method::PUT)
        .body(None)
        .unwrap();

    let response = HttpClient::new(collector)
        .upload_file_size(FileSize::from(file_size)).unwrap()
        .request(request).unwrap()
        .nonblocking(actor)
        .perform()
        .await.unwrap();

    println!("Response: {:?}", response);
    handle.abort();
}

Synchronous Examples

Get Request

use curl_http_client::*;
use http::{HeaderMap, Method, Request};
use url::Url;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let collector = Collector::Ram(Vec::new());

let request = Request::builder()
    .uri("<SOURCE URL>")
    .method(Method::GET)
    .body(None)
    .unwrap();

    let response = HttpClient::new(collector)
        .request(request)?
        .blocking()
        .perform()?;

    println!("Response: {:?}", response);
    Ok(())
}

Post Request

use curl_http_client::*;
use http::{HeaderMap, Method, Request};
use url::Url;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let collector = Collector::Ram(Vec::new());

let request = Request::builder()
    .uri("<TARGET URL>")
    .method(Method::POST)
    .body(Some("test body".as_bytes().to_vec()))
    .unwrap();

    let response = HttpClient::new(collector)
        .request(request)?
        .blocking()
        .perform()?;

    println!("Response: {:?}", response);
    Ok(())
}

Downloading a File

use std::path::PathBuf;

use curl_http_client::*;
use http::{HeaderMap, Method, Request};
use url::Url;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let collector = Collector::File(FileInfo::path(PathBuf::from("<FILE PATH TO SAVE>")));

let request = Request::builder()
    .uri("<SOURCE URL>")
    .method(Method::GET)
    .body(None)
    .unwrap();

    let response = HttpClient::new(collector)
        .request(request)?
        .blocking()
        .perform()?;

    println!("Response: {:?}", response);
    Ok(())
}

Uploading a File

use std::{fs, path::PathBuf};

use curl_http_client::*;
use http::{HeaderMap, Method, Request};
use url::Url;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let file_to_be_uploaded = PathBuf::from("<FILE PATH TO BE UPLOADED>");
    let file_size = fs::metadata(file_to_be_uploaded.as_path()).unwrap().len() as usize;

    let collector = Collector::File(FileInfo::path(file_to_be_uploaded));

let request = Request::builder()
    .uri("<TARGET URL>")
    .method(Method::PUT)
    .body(None)
    .unwrap();

    let response = HttpClient::new(collector)
        .upload_file_size(FileSize::from(file_size))?
        .request(request)?
        .blocking()
        .perform()?;

    println!("Response: {:?}", response);
    Ok(())
}

Dependencies

~18–28MB
~430K SLoC