diff options
author | lamp | 2023-03-05 21:37:45 +0000 |
---|---|---|
committer | lamp | 2023-03-05 21:37:45 +0000 |
commit | d4c3b70d6290c52f903e674e9957b979abd4ce07 (patch) | |
tree | ba415f0f85809ffe07dd17d83ed46e5c09f2c14d /src |
initmain
Diffstat (limited to 'src')
-rw-r--r-- | src/chunked_bufreader.rs | 48 | ||||
-rw-r--r-- | src/http/flatten.rs | 37 | ||||
-rw-r--r-- | src/http/mod.rs | 6 | ||||
-rw-r--r-- | src/http/parser.rs | 523 | ||||
-rw-r--r-- | src/http/rule.rs | 123 | ||||
-rw-r--r-- | src/main.rs | 159 | ||||
-rw-r--r-- | src/peekable_bufreader.rs | 47 | ||||
-rw-r--r-- | src/response.rs | 55 |
8 files changed, 998 insertions, 0 deletions
diff --git a/src/chunked_bufreader.rs b/src/chunked_bufreader.rs new file mode 100644 index 0000000..02c2bb8 --- /dev/null +++ b/src/chunked_bufreader.rs @@ -0,0 +1,48 @@ +use std::pin::Pin; + +use async_std::prelude::*; +use async_std::io::BufReader; +use async_std::io::Read; +use futures::task::{Context, Poll}; +use pin_project::pin_project; + +const CHUNK_SIZE: usize = 4096; + +#[pin_project] +pub struct ChunkedBufReader<T> + where T: Read + Unpin { + #[pin] + reader: BufReader<T>, +} + +impl<T> ChunkedBufReader<T> + where T: Read + Unpin { + pub fn new(reader: BufReader<T>) -> Self { + Self { + reader, + } + } +} + +impl<T> Stream for ChunkedBufReader<T> + where T: Read + Unpin { + type Item = Vec<u8>; + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { + let this = self.project(); + // This is quite wasteful, but perfomance is fine and the only real optimization (other + // than some zero-copy shenanigans) would be to not initialize this vec, with unsafe{} + let mut chunk = vec![0; CHUNK_SIZE]; + match this.reader.poll_read(cx, &mut chunk[0..(CHUNK_SIZE - 1)]) { + Poll::Ready(Ok(size)) => { + if size == 0 { + Poll::Ready(None) + } else { + chunk.truncate(size); + Poll::Ready(Some(chunk)) + } + }, + Poll::Ready(Err(_)) => Poll::Ready(None), + Poll::Pending => Poll::Pending, + } + } +} diff --git a/src/http/flatten.rs b/src/http/flatten.rs new file mode 100644 index 0000000..51ead0c --- /dev/null +++ b/src/http/flatten.rs @@ -0,0 +1,37 @@ +use std::collections::HashMap; + +use super::rule::{HTTPMessage, Method}; + +#[derive(Debug)] +pub enum Version { + Http0_9, + Http1_0, + Http1_1, + // HTTP/2 won't parse, anyway. +} + +#[derive(Debug)] +pub struct HTTPRequest { + pub method: Method, + pub version: Version, + pub requested_path: Vec<String>, + pub headers: HashMap<String, Vec<u8>>, +} + +pub fn flatten(message: HTTPMessage) -> Option<HTTPRequest> { + let method = message.request_line.method; + let version = match (message.request_line.http_version.major, message.request_line.http_version.minor) { + (0, 9) => Version::Http0_9, + (1, 0) => Version::Http1_0, + (1, 1) => Version::Http1_1, + _ => return None, + }; + let requested_path = message.request_line.request_target.absolute_path.segments.into_iter().map(|segment| segment.lexeme).collect(); + let headers = message.header_fields.into_iter().map(|field| (field.name.lexeme, field.value.content)).collect(); + Some(HTTPRequest{ + method, + version, + requested_path, + headers, + }) +} diff --git a/src/http/mod.rs b/src/http/mod.rs new file mode 100644 index 0000000..d2fd5ac --- /dev/null +++ b/src/http/mod.rs @@ -0,0 +1,6 @@ +mod parser; +mod rule; +mod flatten; + +pub use parser::Parser; +pub use rule::Method; diff --git a/src/http/parser.rs b/src/http/parser.rs new file mode 100644 index 0000000..259ddf2 --- /dev/null +++ b/src/http/parser.rs @@ -0,0 +1,523 @@ +use async_std::io::Read; + +use super::rule::{HTTPMessage, HeaderField, FieldName, FieldValue, FieldContent, RequestLine, Method, OriginForm, HTTPVersion, AbsolutePath, Query, Segment}; +use super::flatten::{flatten, HTTPRequest}; +use crate::peekable_bufreader::PeekableBufReader; + +const HTAB: u8 = 0x09; +const SPACE: u8 = 0x20; +const PERCENT: u8 = 0x25; +const SLASH: u8 = 0x2F; +const DOT: u8 = 0x2E; +const COLON: u8 = 0x3A; +const QUESTION_MARK: u8 = 0x3F; +const ATSIGN: u8 = 0x40; + +enum ErrorType { + Missing, + Malformed, +} + +pub struct Parser<T> + where T: Read + Unpin { + source: PeekableBufReader<T>, +} + +impl<T> Parser<T> + where T: Read + Unpin { + pub fn new(source: PeekableBufReader<T>) -> Self { + Self { + source, + } + } + + pub async fn parse(self) -> Option<HTTPRequest> { + flatten(self.http_message().await?) + } + + /* + * RFC 7230, Page 19 + */ + async fn http_message(mut self) -> Option<HTTPMessage> { + let start_line = self.start_line().await?; + let mut header_fields = Vec::new(); + loop { + if let Some(_) = self.consume_carriage_return().await { + break; + } + header_fields.push(self.header_field().await?); + self.consume_carriage_return().await?; + } + // GET Requests don't have a message body, and we only really deal with GET requests. + // There's no need to examine the headers and attempt to read a message body. + Some(HTTPMessage { + request_line: start_line, + header_fields, + }) + } + + /* + * RFC 7230, Page 23 + */ + async fn header_field(&mut self) -> Option<HeaderField> { + let name = self.field_name().await?; + self.consume_char(&COLON).await?; + self.consume_optional_whitespace().await; + let value = self.field_value().await?; + self.consume_optional_whitespace().await; + Some(HeaderField { + name, + value, + }) + } + + /* + * RFC 7230, Page 23 + */ + async fn field_name(&mut self) -> Option<FieldName> { + Some(FieldName { + lexeme: self.logical_token().await?, + }) + } + + /* + * RFC 7230, Page 23 + * obs-fold is deprecated except within message/http media. This isn't going to come up for us, + * so we deviate from the grammar slightly. + */ + async fn field_value(&mut self) -> Option<FieldValue> { + let mut content = Vec::new(); + loop { + // Look, no obs-fold! + if let Some(value) = self.field_content().await { + // Let's do some pre-emptive flattening here. + content.push(value.first_char); + if let Some(second_char) = value.second_char { + content.push(SPACE); + content.push(second_char); + } + } else { + break; + } + } + Some(FieldValue { + content, + }) + } + + /* + * RFC 7230, Page 23 + */ + async fn field_content(&mut self) -> Option<FieldContent> { + let first_char = self.field_vchar().await?; + let second_char = if let Some(_) = self.consume_required_whitespace().await { + Some(self.field_vchar().await?) + } else { + None + }; + Some(FieldContent{ + first_char, + second_char, + }) + } + + async fn field_vchar(&mut self) -> Option<u8> { + let next_char = self.source.peek().await?; + if Self::is_visible_char(next_char) || Self::is_obs_text_char(next_char) { + return self.source.next().await; + } + None + } + + /* + * RFC 7230, Page 21 + * This is a server, so the start-line is exclusively a request-line. + */ + async fn start_line(&mut self) -> Option<RequestLine> { + self.request_line().await + } + + /* + * RFC 7230, Page 21 + */ + async fn request_line(&mut self) -> Option<RequestLine> { + let method = self.method().await?; + self.consume_char(&SPACE).await?; + let request_target = self.request_target().await?; + self.consume_char(&SPACE).await?; + let http_version = self.http_version().await?; + self.consume_carriage_return().await?; + Some(RequestLine { + method, + request_target, + http_version, + }) + } + + /* + * RFC 7230, Page 41 + * We only serve some static content; therefore we only need support origin-form. + */ + async fn request_target(&mut self) -> Option<OriginForm> { + self.origin_form().await + } + + /* + * RFC 7230, Page 42 + */ + async fn origin_form(&mut self) -> Option<OriginForm> { + let absolute_path = self.absolute_path().await?; + let query = if let Some(_) = self.consume_char(&QUESTION_MARK).await { + Some(self.query().await?) + } else { + None + }; + Some(OriginForm { + absolute_path, + query, + }) + } + + /* + * RFC 7230, Page 16 + */ + async fn absolute_path(&mut self) -> Option<AbsolutePath> { + let mut segments = Vec::new(); + self.consume_char(&SLASH).await?; + segments.push(self.segment().await?); + loop { + if let None = self.consume_char(&SLASH).await { + break; + } + if let Some(segment) = self.segment().await { + segments.push(segment); + } else { + return None; + } + } + Some(AbsolutePath { + segments, + }) + } + + /* + * RFC 3986, Page 23 + */ + async fn segment(&mut self) -> Option<Segment> { + let mut segment = Vec::new(); + while self.source.peek().await.is_some() { + match self.consume_path_character().await { + Ok(character) => segment.push(character as char), + Err(ErrorType::Missing) => break, + Err(ErrorType::Malformed) => return None, + } + } + Some(Segment{ + lexeme: segment.into_iter().collect(), + }) + } + + /* + * RFC 3986, Page 50 + */ + async fn query(&mut self) -> Option<Query> { + let mut query = Vec::new(); + while self.source.peek().await.is_some() { + match self.consume_query_character().await { + Ok(character) => query.push(character as char), + Err(ErrorType::Missing) => break, + Err(ErrorType::Malformed) => return None, + } + } + Some(Query{ + lexeme: query.into_iter().collect(), + }) + } + + /* + * RFC 7230, Page 14 + */ + async fn http_version(&mut self) -> Option<HTTPVersion> { + self.consume_logical_token("HTTP").await?; + self.consume_char(&SLASH).await?; + let major = Self::ascii_digit_to_value(&self.consume_digit().await?); + self.consume_char(&DOT).await?; + let minor = Self::ascii_digit_to_value(&self.consume_digit().await?); + Some(HTTPVersion{ + major, + minor, + }) + } + + /* + * RFC 7230, Page 21 + */ + async fn method(&mut self) -> Option<Method> { + Method::from_string(&self.logical_token().await?) + } + + /* + * RFC 7230, Page 27 + */ + async fn logical_token(&mut self) -> Option<String> { + let mut logical_token = Vec::new(); + if !Self::is_logical_token_char(self.source.peek().await?) { + return None; + } + while self.source.peek().await.is_some() && Self::is_logical_token_char(self.source.peek().await.unwrap()) { + logical_token.push(self.source.next().await.unwrap() as char); + } + Some(logical_token.into_iter().collect()) + } + + async fn consume_char(&mut self, character: &u8) -> Option<u8> { + let next_char = self.source.peek().await?; + if *next_char == *character { + return self.source.next().await; + } + None + } + + async fn consume_logical_token(&mut self, value: &str) -> Option<String> { + let logical_token = self.logical_token().await?; + if logical_token == value { + return Some(logical_token); + } + None + } + + /* + * RFC 3986, Page 23 + */ + async fn consume_path_character(&mut self) -> Result<u8, ErrorType> { + match self.consume_unreserved_character().await { + Some(character) => return Ok(character), + _ => {} + } + match self.consume_sub_delim_character().await { + Some(character) => return Ok(character), + _ => {} + } + match self.consume_char(&COLON).await { + Some(character) => return Ok(character), + _ => {} + } + match self.consume_char(&ATSIGN).await { + Some(character) => return Ok(character), + _ => {} + } + self.consume_percent_encoded().await + } + + /* + * RFC 3986, Page 50 + */ + async fn consume_query_character(&mut self) -> Result<u8, ErrorType> { + match self.consume_unreserved_character().await { + Some(character) => return Ok(character), + _ => {} + } + match self.consume_sub_delim_character().await { + Some(character) => return Ok(character), + _ => {} + } + match self.consume_char(&COLON).await { + Some(character) => return Ok(character), + _ => {} + } + match self.consume_char(&ATSIGN).await { + Some(character) => return Ok(character), + _ => {} + } + match self.consume_char(&SLASH).await { + Some(character) => return Ok(character), + _ => {} + } + match self.consume_char(&QUESTION_MARK).await { + Some(character) => return Ok(character), + _ => {} + } + self.consume_percent_encoded().await + } + + /* + * RFC 5234, Page 5 + */ + async fn consume_carriage_return(&mut self) -> Option<()> { + self.consume_char(&0x0D).await?; + self.consume_char(&0x0A).await?; + Some(()) + } + + async fn consume_optional_whitespace(&mut self) { + loop { + if let None = self.consume_char(&SPACE).await { + if let None = self.consume_char(&HTAB).await { + break; + } + } + } + } + + async fn consume_required_whitespace(&mut self) -> Option<()> { + if let None = self.consume_char(&SPACE).await { + if let None = self.consume_char(&HTAB).await { + return None; + } + } + loop { + if let None = self.consume_char(&SPACE).await { + if let None = self.consume_char(&HTAB).await { + break; + } + } + } + Some(()) + } + + /* + * RFC 5234, Page 14 + */ + async fn consume_digit(&mut self) -> Option<u8> { + let next_char = self.source.peek().await?; + if Self::is_digit_char(next_char) { + return self.source.next().await + } + None + } + + async fn consume_unreserved_character(&mut self) -> Option<u8> { + let next_char = self.source.peek().await?; + if Self::is_unreserved_char(next_char) { + return self.source.next().await + } + None + } + + async fn consume_sub_delim_character(&mut self) -> Option<u8> { + let next_char = self.source.peek().await?; + if Self::is_sub_delim_char(next_char) { + return self.source.next().await + } + None + } + + async fn consume_percent_encoded(&mut self) -> Result<u8, ErrorType> { + self.consume_char(&PERCENT).await.ok_or(ErrorType::Missing)?; + let high_word = self.consume_hex_digit().await.ok_or(ErrorType::Malformed)?; + let low_word = self.consume_hex_digit().await.ok_or(ErrorType::Malformed)?; + Self::hex_digits_to_byte(high_word, low_word).ok_or(ErrorType::Malformed) + } + + async fn consume_hex_digit(&mut self) -> Option<u8> { + let next_char = self.source.peek().await?; + if Self::is_hex_digit_char(next_char) { + return self.source.next().await + } + None + } + + fn ascii_digit_to_value(character: &u8) -> u32 { + *character as u32 - 0x30 + } + + fn hex_digits_to_byte(high_word: u8, low_word: u8) -> Option<u8> { + u8::from_str_radix(&((high_word as char).to_string() + &(low_word as char).to_string()), 16).ok() + } + + /* + * RFC 7230, Page 27 + */ + fn is_logical_token_char(character: &u8) -> bool { + *character == 0x21 || // ! + *character == 0x23 || // # + *character == 0x24 || // $ + *character == 0x25 || // % + *character == 0x26 || // & + *character == 0x27 || // ' + *character == 0x2A || // * + *character == 0x2B || // + + *character == 0x2D || // - + *character == 0x2E || // . + *character == 0x5E || // ^ + *character == 0x5F || // _ + *character == 0x60 || // ` + *character == 0x7C || // | + *character == 0x7E || // ~ + Self::is_digit_char(character) || + Self::is_alpha_char(character) + } + + /* + * RFC 3986, Page 13 + */ + fn is_unreserved_char(character: &u8) -> bool { + Self::is_alpha_char(character) || + Self::is_digit_char(character) || + *character == 0x2D || // - + *character == 0x2E || // . + *character == 0x5F || // _ + *character == 0x7E // ~ + } + + /* + * RFC 3986, Page 13 + */ + fn is_sub_delim_char(character: &u8) -> bool { + *character == 0x21 || // ! + *character == 0x24 || // $ + *character == 0x26 || // & + *character == 0x27 || // ' + *character == 0x28 || // ( + *character == 0x29 || // ) + *character == 0x2A || // * + *character == 0x2B || // + + *character == 0x2C || // , + *character == 0x3B || // ; + *character == 0x3D // = + } + + /* + * RFC 5234, Page 13 + */ + fn is_alpha_char(character: &u8) -> bool { + (*character >= 0x41 && *character <= 0x5A) || (*character >= 0x61 && *character <= 0x7A) + } + + /* + * RFC 5234, Page 14 + */ + fn is_digit_char(character: &u8) -> bool { + *character >= 0x30 && *character <= 0x39 + } + + /* + * RFC 5234, Page 14 + */ + fn is_hex_digit_char(character: &u8) -> bool { + Self::is_digit_char(character) || + *character == 0x41 || // A + *character == 0x42 || // B + *character == 0x43 || // C + *character == 0x44 || // D + *character == 0x45 || // E + *character == 0x46 || // F + *character == 0x61 || // a + *character == 0x62 || // b + *character == 0x63 || // c + *character == 0x64 || // d + *character == 0x65 || // e + *character == 0x66 // f + } + + /* + * RFC 5234, Page 14 + */ + fn is_visible_char(character: &u8) -> bool { + *character >= 0x21 && *character <= 0x7E + } + + fn is_obs_text_char(character: &u8) -> bool { + *character >= 0x80 + } +} diff --git a/src/http/rule.rs b/src/http/rule.rs new file mode 100644 index 0000000..1672d99 --- /dev/null +++ b/src/http/rule.rs @@ -0,0 +1,123 @@ +use std::collections::HashMap; + +lazy_static! { + static ref METHODS: HashMap<&'static str, Method> = { + let mut m = HashMap::new(); + m.insert("GET", Method::GET); + m.insert("HEAD", Method::HEAD); + m.insert("POST", Method::POST); + m.insert("PUT", Method::PUT); + m.insert("DELETE", Method::DELETE); + m.insert("CONNECT", Method::CONNECT); + m.insert("OPTIONS", Method::OPTIONS); + m.insert("TRACE", Method::TRACE); + m + }; +} + +/* +* RFC 7230, Page 19 +*/ +#[derive(Debug)] +pub struct HTTPMessage { + pub request_line: RequestLine, + pub header_fields: Vec<HeaderField>, +} + +/* +* RFC 7230, Page 23 +*/ +#[derive(Debug)] +pub struct HeaderField { + pub name: FieldName, + pub value: FieldValue, +} + +/* +* RFC 7230, Page 23 +*/ +#[derive(Debug)] +pub struct FieldName { + pub lexeme: String, +} + +/* +* RFC 7230, Page 23 +*/ +#[derive(Debug)] +pub struct FieldValue { + pub content: Vec<u8>, +} + +/* +* RFC 7230, Page 23 +*/ +#[derive(Debug)] +pub struct FieldContent { + pub first_char: u8, + pub second_char: Option<u8>, +} + +/* +* RFC 7230, Page 21 +*/ +#[derive(Debug)] +pub struct RequestLine { + pub method: Method, + pub request_target: OriginForm, + pub http_version: HTTPVersion, +} + +/* +* RFC 7231, Page 22 +*/ +#[derive(Debug, Clone)] +pub enum Method { + GET, + HEAD, + POST, + PUT, + DELETE, + CONNECT, + OPTIONS, + TRACE, +} + +impl Method { + pub fn from_string(string: &str) -> Option<Method> { + METHODS.get(string).cloned() + } +} + +/* +* RFC 7230, Page 41 +*/ +#[derive(Debug)] +pub struct OriginForm { + pub absolute_path: AbsolutePath, + pub query: Option<Query>, +} + +/* +* RFC 7230, Page 14 +*/ +#[derive(Debug)] +pub struct HTTPVersion { + pub major: u32, + pub minor: u32, +} + +#[derive(Debug)] +pub struct AbsolutePath { + pub segments: Vec<Segment>, +} + +#[derive(Debug)] +pub struct Query { + pub lexeme: String, +} + +#[derive(Debug)] +pub struct Segment { + pub lexeme: String, +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..36699af --- /dev/null +++ b/src/main.rs @@ -0,0 +1,159 @@ +#[macro_use] +extern crate lazy_static; + +mod http; +mod peekable_bufreader; +mod response; +mod chunked_bufreader; + +use std::env; +use std::path::PathBuf; +use std::path::Path; + +use futures::stream; +use futures::stream::StreamExt; + +use async_std::prelude::*; +use async_std::task; +use async_std::net::{TcpStream, TcpListener}; +use async_std::io::{BufReader, BufWriter}; +use async_std::fs::File; +use async_std::fs; + +use chrono::offset::Local; +use chrono::DateTime; + +use http::{Parser, Method}; +use peekable_bufreader::PeekableBufReader; +use chunked_bufreader::ChunkedBufReader; +use response::{Response, BadRequest, NotFound, NotImplemented, InternalServerError}; + +#[async_std::main] +async fn main() { + let port = env::args().skip(1).next().unwrap_or("8000".to_owned()).parse::<u16>().unwrap_or(8000); + let listener = TcpListener::bind(format!("0.0.0.0:{}", port)).await; + match listener { + Err(e) => { + eprintln!("Failed to bind TCP Listener: {}", e); + return; + }, + _ => {}, + } + let listener = listener.unwrap(); + listener + .incoming() + .for_each_concurrent(None, |stream| async move { + if let Ok(valid) = stream { + task::spawn(handle_connection(valid)); + } + }) + .await; +} + +async fn handle_connection(mut stream: TcpStream) { + let response = generate_response(&stream).await.response_bytes(); + let writer = BufWriter::new(&mut stream); + let mut writer = response.fold(writer, |mut writer, bytes| async move { + writer.write(&bytes).await.unwrap(); + writer + }).await; + writer.flush().await.unwrap(); + stream.flush().await.unwrap(); +} + +async fn generate_response(stream: &TcpStream) -> Box<dyn Response> { + let reader = PeekableBufReader::new(BufReader::new(stream)); + let request = match Parser::new(reader).parse().await { + Some(success) => success, + None => return Box::new(BadRequest{}), + }; + match request.method { + Method::GET => { + if request.requested_path.iter().filter(|segment| segment.contains("/")).count() != 0 { + return Box::new(BadRequest{}); + } else { + let path = match PathBuf::from("./".to_owned() + &request.requested_path.join("/")).canonicalize() { + Ok(canonical_path) => canonical_path, + Err(_) => return Box::new(NotFound{}), + }; + let current_dir = match env::current_dir() { + Ok(dir) => match dir.canonicalize() { + Ok(canonical_path) => canonical_path, + Err(_) => return Box::new(InternalServerError{}), + }, + Err(_) => return Box::new(InternalServerError{}), + }; + if !is_path_ancestor_of(¤t_dir, &path) { + return Box::new(NotFound{}); + } + let metadata = match fs::metadata(&path).await { + Ok(data) => data, + Err(_) => return Box::new(NotFound{}), + }; + if metadata.is_dir() { + let friendly_name = match path.strip_prefix(¤t_dir) { + Ok(result) => { + match result.to_str() { + Some(result) => result, + None => return Box::new(InternalServerError{}), + } + }, + Err(_) => return Box::new(InternalServerError{}), + }; + let mut listings = Vec::new(); + if path != current_dir { + listings.push(format!(include_str!("../res/listing_entry.html"), path.parent().unwrap().strip_prefix(¤t_dir).unwrap().to_str().unwrap(), "..", "-", "-")); + } + for file in path.read_dir().unwrap() { + let file_path = file.unwrap().path(); + let file_metadata = fs::metadata(&file_path).await.unwrap(); + let created_time = match file_metadata.created() { + Ok(birth_time) => { + let formatted_time: DateTime<Local> = birth_time.into(); + formatted_time.format("%d-%b-%Y %H:%M").to_string() + }, + Err(_) => "-".to_owned(), + }; + let file_size = if file_metadata.is_dir() { + "-".to_owned() + } else { + file_metadata.len().to_string() + }; + listings.push(format!(include_str!("../res/listing_entry.html"), + file_path.strip_prefix(¤t_dir).unwrap().to_str().unwrap(), + file_path.file_name().unwrap().to_str().unwrap(), + created_time, + file_size, + )) + } + return Box::new(response::Ok { + file_stream: Box::new(stream::iter(vec![Vec::from( + format!( + include_str!("../res/listing.html"), + friendly_name, + friendly_name, + listings.join("\n"), + ).as_bytes() + )].into_iter().map(|entry| entry.to_owned()))) + }); + } else { + return Box::new(response::Ok{ file_stream: Box::new(ChunkedBufReader::new(BufReader::new(File::open(&path).await.unwrap()))) }) + } + } + }, + _ => return Box::new(NotImplemented{}), + } +} + +fn is_path_ancestor_of(ancestor: &Path, child: &Path) -> bool { + let mut ancestors = child.ancestors(); + loop { + if let Some(parent) = ancestors.next() { + if parent == ancestor { + return true; + } + } else { + return false; + } + } +} diff --git a/src/peekable_bufreader.rs b/src/peekable_bufreader.rs new file mode 100644 index 0000000..ae02155 --- /dev/null +++ b/src/peekable_bufreader.rs @@ -0,0 +1,47 @@ +use async_std::prelude::*; +use async_std::io::BufReader; +use async_std::io::Read; + +pub struct PeekableBufReader<T> + where T: Read + Unpin { + reader: BufReader<T>, + buffer: [u8; 1], + peeked_last: bool, +} + +impl<T> PeekableBufReader<T> + where T: Read + Unpin { + pub fn new(reader: BufReader<T>) -> Self { + Self { + reader, + buffer: [0], + peeked_last: false, + } + } + + pub async fn next(&mut self) -> Option<u8> { + if self.peeked_last { + self.peeked_last = false; + Some(self.buffer[0]) + } else { + match self.reader.read(&mut self.buffer).await.ok()? { + 1 => Some(self.buffer[0]), + _ => None, + } + } + } + + pub async fn peek(&mut self) -> Option<&u8> { + if self.peeked_last { + Some(&self.buffer[0]) + } else { + match self.reader.read(&mut self.buffer).await.ok()? { + 1 => { + self.peeked_last = true; + Some(&self.buffer[0]) + }, + _ => None, + } + } + } +} diff --git a/src/response.rs b/src/response.rs new file mode 100644 index 0000000..8817148 --- /dev/null +++ b/src/response.rs @@ -0,0 +1,55 @@ +use futures::prelude::*; + +pub trait Response: Send + Sync { + fn response_bytes(self: Box<Self>) -> Box<dyn Stream<Item = Vec<u8>> + Unpin + Send>; +} + +pub struct Ok { + pub file_stream: Box<dyn Stream<Item = Vec<u8>> + Unpin + Send + Sync>, +} + +impl Response for Ok { + fn response_bytes(self: Box<Self>) -> Box<dyn Stream<Item = Vec<u8>> + Unpin + Send> { + Box::new(stream::iter(vec![Vec::from(b"HTTP/1.1 200 OK\r\n\r\n" as &[u8])].into_iter().map(|entry| entry.to_owned())).chain(self.file_stream)) + } +} + +pub struct BadRequest { + +} + +impl Response for BadRequest { + fn response_bytes(self: Box<Self>) -> Box<dyn Stream<Item = Vec<u8>> + Unpin + Send> { + Box::new(stream::iter(vec![Vec::from(b"HTTP/1.1 400 Bad Request\r\n\r\n" as &[u8]), include_bytes!("../res/400.html").to_vec()].into_iter().map(|entry| entry.to_owned()))) + } +} + +pub struct NotFound { + +} + +impl Response for NotFound { + fn response_bytes(self: Box<Self>) -> Box<dyn Stream<Item = Vec<u8>> + Unpin + Send> { + Box::new(stream::iter(vec![Vec::from(b"HTTP/1.1 404 Not Found\r\n\r\n" as &[u8]), include_bytes!("../res/404.html").to_vec()].into_iter().map(|entry| entry.to_owned()))) + } +} + +pub struct NotImplemented { + +} + +impl Response for NotImplemented { + fn response_bytes(self: Box<Self>) -> Box<dyn Stream<Item = Vec<u8>> + Unpin + Send> { + Box::new(stream::iter(vec![Vec::from(b"HTTP/1.1 501 Not Implemented\r\n\r\n" as &[u8]), include_bytes!("../res/501.html").to_vec()].into_iter().map(|entry| entry.to_owned()))) + } +} + +pub struct InternalServerError { + +} + +impl Response for InternalServerError { + fn response_bytes(self: Box<Self>) -> Box<dyn Stream<Item = Vec<u8>> + Unpin + Send> { + Box::new(stream::iter(vec![Vec::from(b"HTTP/1.1 500 InternalServerError\r\n\r\n" as &[u8]), include_bytes!("../res/500.html").to_vec()].into_iter().map(|entry| entry.to_owned()))) + } +} |