Skip to content

pimalaya/io-http

I/O HTTP Documentation Matrix Mastodon

I/O-free HTTP/1.X client library written in Rust, based on io-socket

Table of contents

RFC coverage

This library implements HTTP as I/O-agnostic coroutines — no sockets, no async runtime, no std required.

RFC What it covers
1945 HTTP/1.0 — request/response coroutine (Http10Send)
6750 OAuth 2.0 Bearer token — Authorization: Bearer <token>
7617 HTTP Basic authentication — Authorization: Basic <base64(user:pass)>
8615 .well-known URI discovery — WellKnown coroutine
9110 HTTP semantics — shared types: HttpRequest, HttpResponse, StatusCode
9112 HTTP/1.1 — request/response coroutine (Http11Send), chunked transfer encoding

Examples

Send an HTTPS/1.1 request via rustls (blocking)

use std::{net::TcpStream, sync::Arc};

use io_http::rfc9110::request::HttpRequest;
use io_http::rfc9112::send::{Http11Send, Http11SendResult};
use io_socket::runtimes::std_stream::handle;
use rustls::{ClientConfig, ClientConnection, StreamOwned};
use rustls_platform_verifier::ConfigVerifierExt;
use url::Url;

let url = Url::parse("https://example.com/").unwrap();
let domain = url.domain().unwrap();

let config = ClientConfig::with_platform_verifier().unwrap();
let server_name = domain.to_string().try_into().unwrap();
let conn = ClientConnection::new(Arc::new(config), server_name).unwrap();
let tcp = TcpStream::connect((domain, 443)).unwrap();
let mut tls = StreamOwned::new(conn, tcp);

let request = HttpRequest::get(url)
    .header("Host", domain)
    .header("Connection", "close");

let mut arg = None;
let mut send = Http11Send::new(request);

let response = loop {
    match send.resume(arg.take()) {
        Http11SendResult::Ok { response, .. } => break response,
        Http11SendResult::Redirect { url, .. } => { /* follow redirect */ break todo!() }
        Http11SendResult::Err { err } => panic!("{err}"),
        Http11SendResult::Io { input } => arg = Some(handle(&mut tls, input).unwrap()),
    }
};

println!("{} {}", response.version, *response.status);

See complete example at ./examples/send.rs.

Discover a .well-known endpoint via Tokio (async)

use std::sync::Arc;

use io_http::rfc8615::well_known::{WellKnown, WellKnownResult};
use io_socket::runtimes::tokio_stream::handle;
use rustls::{ClientConfig, ClientConnection};
use rustls_platform_verifier::ConfigVerifierExt;
use tokio::net::TcpStream;
use tokio_rustls::TlsConnector;

#[tokio::main]
async fn main() {
    let request = WellKnown::prepare_request("https://example.com", "caldav").unwrap();
    let domain = request.url.domain().unwrap().to_owned();

    let config = Arc::new(ClientConfig::with_platform_verifier().unwrap());
    let connector = TlsConnector::from(config);
    let server_name = domain.clone().try_into().unwrap();
    let tcp = TcpStream::connect((domain.as_str(), 443)).await.unwrap();
    let mut tls = connector.connect(server_name, tcp).await.unwrap();

    let mut well_known = WellKnown::new(request);
    let mut arg = None;

    loop {
        match well_known.resume(arg.take()) {
            WellKnownResult::Ok { redirect_url: Some(url), .. } => {
                println!("caldav endpoint: {url}");
                break;
            }
            WellKnownResult::Ok { response, .. } => {
                panic!("expected redirect, got {}", *response.status);
            }
            WellKnownResult::Err { err } => panic!("{err}"),
            WellKnownResult::Io { input } => {
                arg = Some(handle(&mut tls, input).await.unwrap());
            }
        }
    }
}

More examples

Have a look at projects built on the top of this library:

  • io-addressbook: Set of I/O-free coroutines to manage contacts
  • io-oauth: Set of I/O-free Rust coroutines to manage OAuth flows
  • io-starttls: I/O-free Rust coroutine to upgrade any plain stream to a secure one
  • Cardamum: CLI to manage contacts
  • Ortie: CLI to manage OAuth access tokens

License

This project is licensed under either of:

at your option.

Social

Sponsoring

nlnet

Special thanks to the NLnet foundation and the European Commission that have been financially supporting the project for years:

If you appreciate the project, feel free to donate using one of the following providers:

GitHub Ko-fi Buy Me a Coffee Liberapay thanks.dev PayPal