http-net 0.9.1

Sans-IO HTTP/1.1 server and client: strict RFC 9112 parsing, smuggling defences, bounded buffers, keep-alive, chunked bodies, upgrades, a blocking std adapter, no third-party dependencies.
Documentation
//! Drivers shared by the integration tests: run a connection over input
//! split into arbitrary pieces and record what it reports, with body runs
//! merged so the record does not depend on where the input was split.

#![allow(dead_code)]

pub mod reference;

use http_net::{
    BodyFraming, ClientConn, ClientEvent, HttpError, Limits, Request, Response, ServerConn,
    ServerEvent, Version,
};

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Ev {
    Head {
        method: String,
        target: String,
        version: Version,
        headers: Vec<(String, Vec<u8>)>,
        framing: BodyFraming,
        keep_alive: bool,
        upgrade: bool,
    },
    Status {
        status: u16,
        headers: Vec<(String, Vec<u8>)>,
        framing: BodyFraming,
    },
    Body(Vec<u8>),
    /// Reported trailer fields (only when the driver asked for them).
    Trailers(Vec<(String, Vec<u8>)>),
    /// A reported interim response (client, only when asked for).
    Informational {
        status: u16,
        headers: Vec<(String, Vec<u8>)>,
    },
    /// `100 Continue` to an `expect_continue` request (client).
    Continue,
    End,
    Upgraded(Vec<u8>),
    Closed,
    Err(HttpError),
}

/// What the drivers ask the connection to report beyond the default events.
#[derive(Clone, Copy, Debug, Default)]
pub struct Opts {
    pub report_trailers: bool,
    pub report_informational: bool,
}

fn fields_of<'a>(it: impl Iterator<Item = (&'a str, &'a [u8])>) -> Vec<(String, Vec<u8>)> {
    it.map(|(n, v)| (n.to_string(), v.to_vec())).collect()
}

fn push_body(events: &mut Vec<Ev>, chunk: &[u8]) {
    if let Some(Ev::Body(b)) = events.last_mut() {
        b.extend_from_slice(chunk);
    } else {
        events.push(Ev::Body(chunk.to_vec()));
    }
}

/// Splits `input` at the given cut points (taken modulo the length).
pub fn split<'a>(input: &'a [u8], cuts: &[usize]) -> Vec<&'a [u8]> {
    if input.is_empty() {
        return vec![input];
    }
    let mut points: Vec<usize> = cuts.iter().map(|c| c % input.len()).collect();
    points.sort_unstable();
    points.dedup();
    let mut pieces = Vec::new();
    let mut last = 0;
    for p in points {
        pieces.push(&input[last..p]);
        last = p;
    }
    pieces.push(&input[last..]);
    pieces
}

/// The first protocol other than `h2c` that a request's `Upgrade` offers.
pub fn first_offered(r: &http_net::RequestHead<'_>) -> Option<String> {
    r.header_all("upgrade")
        .flat_map(|v| v.split(|&b| b == b','))
        .map(|e| e.trim_ascii())
        .find(|e| !e.is_empty() && !e.eq_ignore_ascii_case(b"h2c") && !e.starts_with(b"h2c/"))
        .and_then(|e| std::str::from_utf8(e).ok())
        .map(str::to_string)
}

/// Feeds `pieces` to a server connection, answering every request with an
/// empty 200 (or 101 for upgrades), then signals EOF. Returns the events.
pub fn drive_server(pieces: &[&[u8]], limits: Limits) -> Vec<Ev> {
    drive_server_opts(pieces, limits, Opts::default())
}

/// As [`drive_server`], with the reports `opts` asks for.
pub fn drive_server_opts(pieces: &[&[u8]], limits: Limits, opts: Opts) -> Vec<Ev> {
    let mut conn = ServerConn::with_limits(limits);
    conn.set_report_trailers(opts.report_trailers);
    let mut out = Vec::new();
    let mut events = Vec::new();
    let mut next = 0;
    let mut pending: &[u8] = &[];
    let mut eof = false;
    // The protocol to answer an upgrade request with: the first one the
    // request offered (a 101 must name an offered protocol).
    let mut upgrade_to: Option<String> = None;
    for _ in 0..10_000_000 {
        match conn.poll() {
            Ok(ServerEvent::Request(r)) => {
                upgrade_to = r.is_upgrade().then(|| first_offered(&r)).flatten();
                events.push(Ev::Head {
                    method: r.method_str().to_string(),
                    target: r.target().to_string(),
                    version: r.version(),
                    headers: r
                        .headers()
                        .map(|(n, v)| (n.to_string(), v.to_vec()))
                        .collect(),
                    framing: r.framing(),
                    keep_alive: r.keep_alive(),
                    upgrade: r.is_upgrade(),
                });
            }
            Ok(ServerEvent::Body(b)) => push_body(&mut events, b),
            Ok(ServerEvent::Trailers(t)) => events.push(Ev::Trailers(fields_of(t))),
            Ok(ServerEvent::RequestEnd) => {
                events.push(Ev::End);
                let response = match &upgrade_to {
                    Some(protocol) => Response::switching_protocols(protocol),
                    None => Response::new(200),
                };
                conn.respond(&response, &mut out).expect("respond");
            }
            Ok(ServerEvent::NeedInput) => {
                assert!(!eof, "NeedInput after EOF");
                while pending.is_empty() && next < pieces.len() {
                    pending = pieces[next];
                    next += 1;
                }
                if pending.is_empty() {
                    conn.feed_eof();
                    eof = true;
                } else {
                    let n = conn.feed(pending);
                    assert!(n > 0, "buffer full while the parser waits for input");
                    pending = &pending[n..];
                }
            }
            Ok(ServerEvent::AwaitingResponse) => panic!("driver always responds"),
            Ok(ServerEvent::Upgraded) => {
                let mut rest = conn.remaining_input().to_vec();
                rest.extend_from_slice(pending);
                for p in &pieces[next..] {
                    rest.extend_from_slice(p);
                }
                events.push(Ev::Upgraded(rest));
                return events;
            }
            Ok(ServerEvent::Closed) => {
                events.push(Ev::Closed);
                return events;
            }
            Ok(other) => panic!("unknown event {other:?}"),
            Err(e) => {
                events.push(Ev::Err(e));
                return events;
            }
        }
    }
    panic!("driver did not terminate");
}

/// Sends `request` on a client connection, feeds `pieces` as the response,
/// then EOF. Returns the events.
pub fn drive_client(request: &Request, pieces: &[&[u8]], limits: Limits) -> Vec<Ev> {
    drive_client_opts(request, pieces, limits, Opts::default())
}

/// As [`drive_client`], with the reports `opts` asks for.
pub fn drive_client_opts(
    request: &Request,
    pieces: &[&[u8]],
    limits: Limits,
    opts: Opts,
) -> Vec<Ev> {
    let mut conn = ClientConn::with_limits(limits);
    conn.set_report_trailers(opts.report_trailers);
    conn.set_report_informational(opts.report_informational);
    let mut out = Vec::new();
    conn.send_request(request, &mut out).expect("send");
    let mut events = Vec::new();
    let mut next = 0;
    let mut pending: &[u8] = &[];
    let mut eof = false;
    for _ in 0..10_000_000 {
        match conn.poll() {
            Ok(ClientEvent::Response(r)) => events.push(Ev::Status {
                status: r.status(),
                headers: r
                    .headers()
                    .map(|(n, v)| (n.to_string(), v.to_vec()))
                    .collect(),
                framing: r.framing(),
            }),
            Ok(ClientEvent::Body(b)) => push_body(&mut events, b),
            Ok(ClientEvent::Trailers(t)) => events.push(Ev::Trailers(fields_of(t))),
            Ok(ClientEvent::Informational(r)) => events.push(Ev::Informational {
                status: r.status(),
                headers: fields_of(r.headers()),
            }),
            Ok(ClientEvent::Continue) => events.push(Ev::Continue),
            Ok(ClientEvent::ResponseEnd) => events.push(Ev::End),
            Ok(ClientEvent::NeedInput) => {
                assert!(!eof, "NeedInput after EOF");
                while pending.is_empty() && next < pieces.len() {
                    pending = pieces[next];
                    next += 1;
                }
                if pending.is_empty() {
                    conn.feed_eof();
                    eof = true;
                } else {
                    let n = conn.feed(pending);
                    assert!(n > 0, "buffer full while the parser waits for input");
                    pending = &pending[n..];
                }
            }
            Ok(ClientEvent::Idle) => {
                // Bytes still to come after the response are unsolicited;
                // feed them so the record does not depend on whether they
                // arrived in the same read as the response.
                while pending.is_empty() && next < pieces.len() {
                    pending = pieces[next];
                    next += 1;
                }
                if pending.is_empty() {
                    return events;
                }
                let n = conn.feed(pending);
                assert!(n > 0, "buffer full while idle");
                pending = &pending[n..];
            }
            Ok(ClientEvent::Upgraded) => {
                // Everything after the switch, buffered or not yet fed, so
                // the record does not depend on where the input was split.
                let mut rest = conn.remaining_input().to_vec();
                rest.extend_from_slice(pending);
                for p in &pieces[next..] {
                    rest.extend_from_slice(p);
                }
                events.push(Ev::Upgraded(rest));
                return events;
            }
            Ok(ClientEvent::Closed) => {
                events.push(Ev::Closed);
                return events;
            }
            Ok(other) => panic!("unknown event {other:?}"),
            Err(e) => {
                events.push(Ev::Err(e));
                return events;
            }
        }
    }
    panic!("driver did not terminate");
}

/// The first error a server reports for `input` fed whole, if any.
pub fn server_error(input: &[u8]) -> Option<HttpError> {
    drive_server(&[input], Limits::new())
        .into_iter()
        .find_map(|e| match e {
            Ev::Err(e) => Some(e),
            _ => None,
        })
}