use std::sync::mpsc;
use std::thread;
use http_net::{
FieldSpan, HeadFacts, HttpError, Limits, OwnedRequestHead, Parsed, parse_request_head,
};
struct Passport {
bytes: Vec<u8>,
fields: Vec<FieldSpan>,
facts: Option<HeadFacts>,
}
impl Passport {
fn new() -> Self {
Self {
bytes: Vec::with_capacity(4096),
fields: Vec::with_capacity(64),
facts: None,
}
}
}
fn main() -> Result<(), HttpError> {
let input: &[u8] = b"GET /a HTTP/1.1\r\nHost: example.com\r\n\r\n\
POST /b HTTP/1.1\r\nHost: example.com\r\nContent-Length: 0\r\nX-Trace: 7\r\n\r\n\
GET /c HTTP/1.1\r\nHost: exam";
let (to_worker, worker_in) = mpsc::channel::<Passport>();
let (to_pool, pool) = mpsc::channel::<Passport>();
for _ in 0..2 {
to_pool
.send(Passport::new())
.map_err(|_| HttpError::InvalidState("pool"))?;
}
let worker = thread::spawn(move || -> Result<(), HttpError> {
for passport in worker_in {
let facts = passport
.facts
.ok_or(HttpError::InvalidState("empty passport"))?;
let owned = OwnedRequestHead::from_parts(passport.bytes, passport.fields, facts)?;
let head = owned.view();
println!(
"worker: {} {} host={} fields={}",
head.method_str(),
head.target(),
head.host().unwrap_or("-"),
head.raw().fields().len(),
);
let (mut bytes, mut fields, _) = owned.into_parts();
bytes.clear();
fields.clear();
let _ = to_pool.send(Passport {
bytes,
fields,
facts: None,
});
}
Ok(())
});
let limits = Limits::default();
let mut slots = [FieldSpan::default(); 16];
let mut rest = input;
loop {
match parse_request_head(rest, &limits, &mut slots)? {
Parsed::Complete { head, len } => {
let view = head.view();
println!(
"reader: {} {} ({len} bytes)",
view.method_str(),
view.target()
);
let Ok(mut passport) = pool.recv() else { break };
passport.facts = Some(view.copy_into(&mut passport.bytes, &mut passport.fields)?);
if to_worker.send(passport).is_err() {
break;
}
rest = &rest[len..];
}
Parsed::Partial => {
println!("reader: {} bytes wait for more input", rest.len());
break;
}
_ => break,
}
}
drop(to_worker);
worker.join().unwrap_or(Ok(()))
}