httplz 0.0.0

A sans-io, no-std HTTP implementation
Documentation
//! A simple echo server that echoes the body of a POST request, and returns a
//! 405 for any other method
//!
//! Usage:
//! cargo run --example echo -- <host> <port>

use std::{error::Error, io::Write, net::TcpListener};

use httplz::Lift;

fn main() -> Result<(), Box<dyn Error>> {
	let mut args = std::env::args().skip(1);
	let (host, port) = args
		.next()
		.and_then(|h| Some((h, args.next()?)))
		.ok_or_else(|| Box::<dyn Error>::from(String::from("Missing required argument")))?;

	let listener = TcpListener::bind((host, port.parse()?))?;
	loop {
		let (mut stream, _) = listener.accept()?;
		let mut conn = httplz::Connection::new(httplz::Role::Server);

		let mut body: Vec<u8> = Vec::new();
		let mut buf = vec![0; 1024].into_boxed_slice();
		let mut buf = httplz_ext::Buf::new(&mut buf);

		let mut method_not_allowed = false;

		loop {
			let data = buf.filled();
			let (remaining, r) = conn.handle_recv(data).lift();
			match r.map_err(|e| e.kind) {
				Err(httplz::ErrorKind::NeedMoreData) => {
					buf.read_from(&mut stream)?;
					continue;
				},
				Err(e) => panic!("{e:?}"),
				Ok(event) => {
					match event {
						httplz::Event::RequestLine(r) => {
							if !r.method.eq_ignore_ascii_case("post") {
								method_not_allowed = true;
							}
						},
						httplz::Event::RecvDone => break,
						httplz::Event::BodyChunk(b) => body.extend_from_slice(b),
						_ => (),
					};
				},
			};

			let len = data.len() - remaining.len();
			buf.pop_front(len);
		}

		let parts: &[httplz::Event] = if method_not_allowed {
			&[
				httplz::Event::StatusLine(httplz::StatusLine {
					version: httplz::Version::HTTP1_1,
					status_code: 405,
					status_text: "Method not allowed",
				}),
				httplz::Event::HeadersDone,
				httplz::Event::SendDone,
			]
		} else {
			&[
				httplz::Event::StatusLine(httplz::StatusLine {
					version: httplz::Version::HTTP1_1,
					status_code: 200,
					status_text: "OK",
				}),
				httplz::Event::Header(httplz::Header::Special(
					httplz::HeaderSpecial::ContentLength(body.len()),
				)),
				httplz::Event::HeadersDone,
				httplz::Event::BodyChunk(body.as_slice()),
				httplz::Event::SendDone,
			]
		};

		let buf = vec![0; 1024];
		let mut cursor = httplz::WriteCursor::new(buf);
		for p in parts {
			if let Err(e) = conn.handle_send(p, &mut cursor) {
				panic!("{e:?}")
			};
			stream.write_all(cursor.written())?;
			cursor.reset();
		}
	}
}