diff options
Diffstat (limited to 'src/client.rs')
| -rw-r--r-- | src/client.rs | 88 |
1 files changed, 88 insertions, 0 deletions
diff --git a/src/client.rs b/src/client.rs new file mode 100644 index 0000000..c5c8e18 --- /dev/null +++ b/src/client.rs @@ -0,0 +1,88 @@ +use httparse::EMPTY_HEADER; + +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), +} + +pub struct Client<R, W> { + reader: R, + writer: W, +} + +impl<R, W> Client<R, W> +where + R: AsyncBufRead + Unpin, + W: AsyncWrite + Unpin, +{ + pub fn new(reader: R, writer: W) -> Self { + Self { reader, writer } + } + + pub async fn send_response(&mut self, response: Response) -> io::Result<()> { + response.to_wire(&mut self.writer).await?; + + self.writer.flush().await?; + + Ok(()) + } + + pub async fn read_request(&mut self) -> Result<Option<Request<Vec<u8>>>, Error> { + let mut buf = Vec::new(); + let mut line = Vec::new(); + + 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(); + + Ok(Some(Request::new(builder.body(body)?))) + } +} |
