diff options
| author | John Turner <jturner.usa@gmail.com> | 2026-03-04 20:53:24 -0500 |
|---|---|---|
| committer | John Turner <jturner.usa@gmail.com> | 2026-03-04 20:53:32 -0500 |
| commit | 4a16841789604614bc495c36972236749e5f35b0 (patch) | |
| tree | ff7383c17f5d265967a1db083884fec78062e53f /src/client.rs | |
| parent | 3c4208abd325d317c7524ba0dc3b701edfa9ebf8 (diff) | |
| download | httpd-4a16841789604614bc495c36972236749e5f35b0.tar.gz | |
roll our own http types
Diffstat (limited to 'src/client.rs')
| -rw-r--r-- | src/client.rs | 95 |
1 files changed, 23 insertions, 72 deletions
diff --git a/src/client.rs b/src/client.rs index c5c8e18..f5ed2f9 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,88 +1,39 @@ -use httparse::EMPTY_HEADER; +use tokio::io::{self, AsyncBufRead, AsyncWrite, AsyncWriteExt}; -use tokio::io::{self, AsyncBufRead, AsyncBufReadExt, AsyncWrite, AsyncWriteExt}; - -use crate::{request::Request, response::Response}; - -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error("unsupported version")] - Version, - - #[error("io error: {0}")] - Io(#[from] io::Error), - - #[error("invalid method: {0}")] - Method(#[from] http::method::InvalidMethod), - - #[error("http error: {0}")] - Http(#[from] http::Error), - - #[error("http parse error: {0}")] - Parse(#[from] httparse::Error), -} +use crate::{ + request::{self, Request}, + response::Response, +}; +#[derive(Debug)] pub struct Client<R, W> { reader: R, writer: W, + buf: Vec<u8>, + line: Vec<u8>, } -impl<R, W> Client<R, W> -where - R: AsyncBufRead + Unpin, - W: AsyncWrite + Unpin, -{ +impl<R, W> Client<R, W> { pub fn new(reader: R, writer: W) -> Self { - Self { reader, writer } + Self { + reader, + writer, + buf: Vec::new(), + line: Vec::new(), + } } +} - pub async fn send_response(&mut self, response: Response) -> io::Result<()> { - response.to_wire(&mut self.writer).await?; - - self.writer.flush().await?; - - Ok(()) +impl<R: AsyncBufRead + Unpin, W: AsyncWrite + Unpin> Client<R, W> { + pub async fn read_request(&mut self) -> Result<Option<Request>, request::Error> { + Request::parse(&mut self.reader, &mut self.buf, &mut self.line).await } - pub async fn read_request(&mut self) -> Result<Option<Request<Vec<u8>>>, Error> { - let mut buf = Vec::new(); - let mut line = Vec::new(); + pub async fn send_response(&mut self, response: Response) -> Result<(), io::Error> { + response.serialize(&mut self.writer).await?; - loop { - line.clear(); - - if self.reader.read_until(b'\n', &mut line).await? == 0 { - return Ok(None); - } - - if line == b"\r\n" || line.is_empty() { - break; - } - - buf.extend_from_slice(&line); - buf.extend_from_slice(b"\r\n"); - } - - let mut headers = [EMPTY_HEADER; 64]; - let mut parsed = httparse::Request::new(&mut headers); - - parsed.parse(&buf)?; - - let mut builder = http::Request::builder(); - - builder = builder.method(http::Method::from_bytes(parsed.method.unwrap().as_bytes())?); - builder = builder.uri(parsed.path.unwrap()); - builder = builder.version(match parsed.version.unwrap() { - 1 => http::Version::HTTP_11, - _ => return Err(Error::Version), - }); - - for header in parsed.headers { - builder = builder.header(header.name, header.value); - } - - let body: Vec<u8> = Vec::new(); + self.writer.flush().await?; - Ok(Some(Request::new(builder.body(body)?))) + Ok(()) } } |
