mio_httpc 0.2.0

mio_httpc is a fully async http client running on top of mio only.
Documentation

mio_httpc is an async http client that runs on top of mio only.

No call will block, not even for DNS resolution as it is implemented internally to avoid blocking.

It uses http crate for Request/Response types.

mio_httpc requires you specify one of the TLS implementations using features: rustls, native, openssl. Not picking any feature will NOT work, as all calls will be no-op.

WARNING

openssl and native backends work well.

rustls is unreliable at the moment. I'm not sure if the issue is rustls or tls-api-rustls.

TODO

  • Basic API
  • Configurable TLS backend
  • Chunked encoding download
  • Chunked encoding upload
  • Basic Auth
  • Keep-alive connection pool
  • DNS retries
  • Timeouts
  • Websockets
  • HTTP2

EXAMPLES

Basic get

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

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

fn main() {
    let poll = Poll::new().unwrap();
    let mut htp = Httpc::new(10);
    let mut req = Request::builder();
    let args: Vec<String> = ::std::env::args().collect();
    let req = req.uri(args[1].as_str()).body(Vec::new()).expect("can not build request");

    let call = CallBuilder::new(req).timeout_ms(500).call(&mut htp, &poll).expect("Call start failed");
    let mut call = SimpleCall::from(call);

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

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

            if call.is_call(&cref) {
                if call.perform(&mut htp, &poll).expect("Call failed") {
                    let mut resp = call.close().expect("No response");
                    let v = mio_httpc::extract_body(&mut resp);
                    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::{Request,CallBuilder,Httpc,WebSocket,WSPacket};
use mio::{Poll,Events};
// ws://demos.kaazing.com/echo

fn main() {
    let poll = Poll::new().unwrap();
    let mut htp = Httpc::new(10);
    let mut req = Request::builder();
    let args: Vec<String> = ::std::env::args().collect();
    let req = req.uri(args[1].as_str()).body(Vec::new()).expect("can not build request");

    let mut ws = CallBuilder::new(req).websocket(&mut htp, &poll).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() {
                    match ws.recv_packet(&mut htp, &poll).expect("Failed recv") {
                        WSPacket::Pong(_) => {
                            println!("Got pong!");
                        }
                        WSPacket::Ping(_) => {
                            println!("Got ping!");
                            ws.pong(None);
                        }
                        WSPacket::None => {}
                        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).expect("Call failed");
    }
    ws.perform(&mut htp, &poll);
    ws.finish(&mut htp);
}