#mio #async-http #websocket #async #native-tls #http

mio_httpc

mio_httpc 是一个完全异步和同步的HTTP客户端,仅在 mio 上运行。

75个版本

0.10.5 2024年2月22日
0.10.4 2023年8月10日
0.10.2 2023年1月23日
0.9.5 2021年3月7日
0.2.3 2017年12月18日

#51HTTP客户端


用于 lightql

MIT/Apache

325KB
7.5K SLoC

mio_httpc 是一个仅在 mio 上运行的异步 HTTP 客户端。

为了方便,它还提供了 CallBuilder::exec 用于简单的单行阻塞 HTTP 调用。

除了 CallBuilder::exec 之外,没有调用会阻塞,甚至 DNS 解析也不例外,因为它内部实现为避免阻塞。

为了使 https 能够工作,mio_httpc 需要您使用 features:native、openssl 或 rtls (rustls) 指定 TLS 实现。默认构建将在任何 https URI 上失败。

CallBuilder 还具有 URL 构建函数(host/path_segm/query/set_https/auth/https),这些函数将处理 URL 安全编码。

mio_httpc 做的最小数量的分配,并且通常使用您提供的缓冲区和内部缓冲区池(在新的调用中重用)一起工作。

文档

块传输解码

mio_httpc 使用默认的最大块大小为 128k。如果您正在编写网络爬虫或类似的东西,这可能会让您陷入困境,因为一些网站有相当大的块。我不会增加(或删除)这个默认大小限制,因为我不喜欢第三方的不受检查的内存增长。您可以使用 CallBuilder::chunked_max_chunk 调用来轻松增加它。

TODO/特性列表

  • 基本API
  • 可配置的TLS后端
  • 块编码下载
  • 块编码上传
  • 安全的URL构造
  • 基本认证
  • 摘要认证
  • 自动重定向
  • 保持连接池
  • DNS重试
  • 超时
  • Websockets
  • gzip体解码
  • 在 subjectPublicKeyInfo 上进行 SSL 锚定(OpenSSL 后端与任何 target_os 和 macos/ios 与 native 后端)
  • HTTP2
  • 下载到文件

示例

使用以下方法将 mio_httpc 包含到您的项目中

# System native TLS implementation
mio_httpc = { version = "0.8", features = ["native"] }
# Openssl
# mio_httpc = { version = "0.8", features = ["openssl"] }
# Rustls
# mio_httpc = { version = "0.8", features = ["rtls"] }

同步调用

extern crate mio_httpc;
use mio_httpc::CallBuilder;
 
 // One line blocking call.
 
 let (response_meta, body) = CallBuilder::get().timeout_ms(5000).url("http://www.example.com")?.exec()?;

 // With URL construction.
 // This way of building the URL is highly recommended as it will always result in correct
 // values by percent encoding any URL unsafe characters.
 // This calls: https://www.example.com/a/b?key1=val1
 let (response_meta, body) = CallBuilder::get()
    .timeout_ms(5000)
    .https()
    .host("www.example.com")
    .path_segm("a")
    .path_segm("b")
    .query("key1","val1")
    .exec()?;
 

基本异步获取

cargo run --example get --features "native" -- "https://edition.cnn.com"

// or
cargo run --example get --features "openssl" -- "https://edition.cnn.com"

// or
cargo run --example get --features "rtls" -- "https://edition.cnn.com"
extern crate mio_httpc;
extern crate mio;

use mio_httpc::{CallBuilder,Httpc};
use mio::{Poll,Events};

fn main() {
    let mut poll = Poll::new().unwrap();
    let mut htp = Httpc::new(10,None);
    let args: Vec<String> = ::std::env::args().collect();
    let mut call = CallBuilder::get()
        .url(args[1].as_str()).expect("Can not parse url")
        .timeout_ms(500)
        .call_simple(&mut htp, poll.registry())
        .expect("Call start failed");

    let to = ::std::time::Duration::from_millis(100);
    let mut events = Events::with_capacity(8);
    'outer: loop {
        poll.poll(&mut events, Some(to)).unwrap();
        for cref in htp.timeout().into_iter() {
            if call.is_ref(cref) {
                println!("Request timed out");
                call.abort(&mut htp);
                break 'outer;
            }
        }

        for ev in events.iter() {
            let cref = htp.event(&ev);

            if call.is_call(&cref) {
                if call.perform(&mut htp, poll.registry()).expect("Call failed") {
                    let (resp,body) = call.finish().expect("No response");
                    if let Ok(s) = String::from_utf8(v) {
                        println!("Body: {}",s);
                    }
                    break 'outer;
                }
            }
        }
    }
}

Websockets

cargo run --example ws --features="native" -- "wss://demos.kaazing.com/echo"
extern crate mio_httpc;
extern crate mio;

use mio_httpc::{CallBuilder,Httpc,WebSocket,WSPacket};
use mio::{Poll,Events};
// ws://demos.kaazing.com/echo

fn main() {
    let mut poll = Poll::new().unwrap();
    let mut htp = Httpc::new(10,None);
    let args: Vec<String> = ::std::env::args().collect();

    let mut ws = CallBuilder::get()
        .url(args[1].as_str()).expect("Can not parse url")
        .websocket(&mut htp, poll.registry())
        .expect("Call start failed");

    let to = ::std::time::Duration::from_millis(800);
    'outer: loop {
        let mut events = Events::with_capacity(8);
        poll.poll(&mut events, Some(to)).unwrap();
        for cref in htp.timeout().into_iter() {
            if ws.is_ref(cref) {
                println!("Request timed out");
                break 'outer;
            }
        }

        if events.len() == 0 {
            // ws.ping(None);
            println!("send yo");
            ws.send_text(true, "yo!");
        }

        for ev in events.iter() {
            let cref = htp.event(&ev);

            if ws.is_call(&cref) {
                if ws.is_active() {
                    loop {
                        match ws.recv_packet(&mut htp, poll.registry()).expect("Failed recv") {
                            WSPacket::Pong(_) => {
                                println!("Got pong!");
                            }
                            WSPacket::Ping(_) => {
                                println!("Got ping!");
                                ws.pong(None);
                            }
                            WSPacket::None => {
                                break;
                            }
                            WSPacket::Close(_,_) => {
                                println!("Got close!");
                                ws.close(None, None);
                                break 'outer;
                            }
                            WSPacket::Text(fin,txt) => {
                                println!("Got text={}, fin={}",txt,fin);
                            }
                            WSPacket::Binary(fin,b) => {
                                println!("Got bin={}B, fin={}",b.len(),fin);
                            }
                        }
                    }
                } else {
                    if ws.sendq_len() == 0 {
                        ws.ping(None);
                    }
                }
            }
        }
        // Any ping/pong/close/send_text/send_bin has just been buffered.
        // perform and recv_packet actually send over socket.
        ws.perform(&mut htp, poll.registry()).expect("Call failed");
    }
    ws.perform(&mut htp, poll.registry());
    ws.finish(&mut htp);
}

依赖关系

~4–21MB
~313K SLoC