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

curl-http-client

这是curl-rust crate中的Easy2的包装器,用于方便使用,并可以使用async-curl crate以异步方式执行,该crate使用actor模型(消息传递)来实现非阻塞I/O。

19个版本 (11个稳定版本)

2.3.3 2024年4月8日
2.3.0 2024年3月17日
1.0.1 2024年2月17日
0.4.3 2024年2月12日
0.2.2 2023年10月29日

#75 in HTTP客户端

Download history 176/week @ 2024-04-09 4/week @ 2024-04-16 1/week @ 2024-04-23 4/week @ 2024-04-30 1/week @ 2024-05-14 7/week @ 2024-05-21 3/week @ 2024-05-28 11/week @ 2024-06-04 1/week @ 2024-06-18 92/week @ 2024-07-02 13/week @ 2024-07-09 2/week @ 2024-07-16 13/week @ 2024-07-23

每月120次下载

MIT许可证

7.5MB
2K SLoC

curl-http-client

这是curl-rust crate中的Easy2的包装器,用于方便使用,并可以使用async-curl crate以异步方式执行,该crate使用actor模型(消息传递)来实现非阻塞I/O。

Latest Version License Documentation Build Status Downloads

异步示例

获取请求

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请求

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);
}

下载文件

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(())
}

上传文件

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);
}

并发

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.net.cn/")
                .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();
    }
}

恢复下载文件

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);
}

向不同任务发送下载速度信息的文件下载

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();
}

向不同任务发送上传速度信息的文件上传

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();
}

同步示例

获取请求

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请求

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(())
}

下载文件

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(())
}

上传文件

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(())
}

依赖关系

~15–25MB
~397K SLoC