Skip to content

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
danieltrick committed Feb 7, 2023
0 parents commit ce350a7
Show file tree
Hide file tree
Showing 20 changed files with 969 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .cargo/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[target.x86_64-pc-windows-msvc]
rustflags = ["-Ctarget-feature=+crt-static"]

[target.i686-pc-windows-msvc]
rustflags = ["-Ctarget-feature=+crt-static"]

[target.i586-pc-windows-msvc]
rustflags = ["-Ctarget-feature=+crt-static"]
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/target
*.lock
18 changes: 18 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
edition = "2021"
name = "rusty_httpd"
version = "0.1.0"

[dependencies]
case_insensitive_hashmap = "1.0.0"
const_format = "0.2.30"
crossbeam-channel = "0.5.6"
ctrlc = "3.2.4"
lazy_static = "1.4.0"
log = "0.4.17"
env_logger = "0.10.0"
mtcp-rs = "0.1.8"
num_cpus = "1.15.0"
rand = "0.8.5"
regex = "1.7.1"
urlencoding = "2.1.2"
3 changes: 3 additions & 0 deletions dump.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash
find . -name '*.rs' -printf "%d %P\n" | sort -n | cut -c3- | xargs -i bash -c \
'printf "\n\n/* -------------------------------- */\n/* %-32s */\n/* -------------------------------- */\n\n" "{}"; cat "{}"'
14 changes: 14 additions & 0 deletions public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8">
<title>Cool Title</title>
<link rel="stylesheet" href="style.css">
</head>

<body>
<h1>Welcome from index.html</h1>
</body>

</html>
3 changes: 3 additions & 0 deletions public/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
h1 {
color: red
}
94 changes: 94 additions & 0 deletions src/http/content_type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
use std::{fmt::Display, path::Path};
use case_insensitive_hashmap::CaseInsensitiveHashMap;

use lazy_static::lazy_static;

#[derive(Copy, Clone)]
pub enum ContentType {
Binary,
BZip2,
CSS,
GIF,
GZip,
HTML,
JavaScript,
JPEG,
PDF,
PNG,
Tar,
Text,
ZIP,
XZ,
}

lazy_static! {
static ref EXTENSIONS: CaseInsensitiveHashMap<ContentType> = {
let mut builder = CaseInsensitiveHashMap::new();
builder.insert("bin", ContentType::Binary);
builder.insert("bz2", ContentType::BZip2);
builder.insert("css", ContentType::CSS);
builder.insert("dat", ContentType::Binary);
builder.insert("exe", ContentType::Binary);
builder.insert("gif", ContentType::GIF);
builder.insert("gz", ContentType::GZip);
builder.insert("htm", ContentType::HTML);
builder.insert("html", ContentType::HTML);
builder.insert("jpe", ContentType::JPEG);
builder.insert("jpeg", ContentType::JPEG);
builder.insert("jpg", ContentType::JPEG);
builder.insert("js", ContentType::JavaScript);
builder.insert("pdf", ContentType::PDF);
builder.insert("png", ContentType::PNG);
builder.insert("tar", ContentType::Tar);
builder.insert("tbz2", ContentType::BZip2);
builder.insert("tgz", ContentType::GZip);
builder.insert("txt", ContentType::Text);
builder.insert("txz", ContentType::XZ);
builder.insert("xz", ContentType::XZ);
builder.insert("zip", ContentType::ZIP);
builder
};
}

impl ContentType {
pub fn from_path(path: &Path) -> Option<ContentType> {
match path.extension() {
Some(ext) => match ext.to_str() {
Some(str) => EXTENSIONS.get(str).copied(),
None => None,
},
None => None,
}
}

pub fn as_str(&self) -> &'static str {
match *self {
Self::Binary => "application/octet-stream",
Self::CSS => "text/css",
Self::GIF => "image/gif",
Self::GZip => "application/gzip",
Self::BZip2 => "application/x-bzip2",
Self::HTML => "text/html",
Self::JavaScript => "text/javascript",
Self::JPEG => "image/jpg",
Self::PDF => "application/pdf",
Self::PNG => "image/png",
Self::Tar => "application/x-tar",
Self::Text => "text/plain",
Self::ZIP => "application/zip",
Self::XZ => "application/x-xz",
}
}
}

impl AsRef<str> for ContentType {
fn as_ref(&self) -> &str {
self.as_str()
}
}

impl Display for ContentType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_ref())
}
}
43 changes: 43 additions & 0 deletions src/http/headers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use core::str::Lines;
use std::convert::TryFrom;
use std::io::{Error as IoError, ErrorKind};
use crate::utils::ValueMap;

#[derive(Debug)]
pub struct Headers<'buf> {
map: ValueMap<'buf>,
}

impl<'buf> Headers<'buf> {
pub fn names(&'buf self) -> impl Iterator<Item = &'buf str> {
self.map.keys()
}

pub fn values(&'buf self, name: &str) -> Option<impl Iterator<Item = &str>> {
self.map.values(name)
}
}

impl<'buf> TryFrom<Lines<'buf>> for Headers<'buf> {
type Error = IoError;

fn try_from(lines: Lines<'buf>) -> Result<Self, IoError> {
let mut map = ValueMap::new();

for line in lines.map(str::trim).take_while(|&str| !str.is_empty()) {
let mut parts = line.splitn(2, ':');
let key = parts.next().map(str::trim).unwrap_or_default();
let val = parts.next().map(str::trim).unwrap_or_default();

if !key.is_empty() {
map.put(key.into(), val.into());
}
}

if map.is_empty() {
return Err(IoError::new(ErrorKind::NotFound, "No headers found!"));
}

Ok (Self { map })
}
}
43 changes: 43 additions & 0 deletions src/http/method.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use core::fmt;
use std::{str::FromStr, fmt::Display};

#[derive(Debug)]
pub enum Method {
GET,
DELETE,
POST,
PUT,
HEAD,
CONNECT,
OPTIONS,
TRACE,
PATCH,
}

impl FromStr for Method {
type Err = MethodError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let uppr = s.trim().to_ascii_uppercase();
match uppr.as_str() {
"GET" => Ok(Self::GET),
"DELETE" => Ok(Self::DELETE),
"POST" => Ok(Self::POST),
"PUT" => Ok(Self::PUT),
"HEAD" => Ok(Self::HEAD),
"CONNECT" => Ok(Self::CONNECT),
"OPTIONS" => Ok(Self::OPTIONS),
"TRACE" => Ok(Self::TRACE),
"PATCH" => Ok(Self::PATCH),
_ => Err(MethodError),
}
}
}

impl Display for Method {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fmt::Debug::fmt(self, f)
}
}

pub struct MethodError;
15 changes: 15 additions & 0 deletions src/http/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
pub use method::Method;
pub use query_string::QueryString;
pub use headers::Headers;
pub use request::ParseError;
pub use request::Request;
pub use response::Response;
pub use status_code::StatusCode;

pub mod method;
pub mod query_string;
pub mod headers;
pub mod request;
pub mod response;
pub mod status_code;
pub mod content_type;
51 changes: 51 additions & 0 deletions src/http/query_string.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use std::borrow::Cow;
use std::convert::TryFrom;
use std::io::{Error as IoError, ErrorKind};
use crate::utils::ValueMap;
use urlencoding::decode as url_decode;

#[derive(Debug)]
pub struct QueryString<'buf> {
map: ValueMap<'buf>,
}

impl<'buf> QueryString<'buf>{
pub fn keys(&self) -> impl Iterator<Item = &str> {
self.map.keys()
}

pub fn values(&self, key: &str) -> Option<impl Iterator<Item = &str>> {
self.map.values(key)
}
}

impl<'buf> TryFrom<&'buf str> for QueryString<'buf> {
type Error = IoError;

fn try_from(query_string: &'buf str) -> Result<Self, IoError> {
let mut map = ValueMap::new();

for sub_str in query_string.split('&').map(str::trim).filter(|&str| !str.is_empty()) {
let mut parts = sub_str.splitn(2, '=');
let key = decode(parts.next().map(str::trim).unwrap_or_default());
let val = decode(parts.next().map(str::trim).unwrap_or_default());

if !key.is_empty() {
map.put(key, val);
}
}

if map.is_empty() {
return Err(IoError::new(ErrorKind::NotFound, "No query parameters found!"));
}

Ok(Self { map })
}
}

fn decode(str: &str) -> Cow<str> {
match url_decode(str) {
Ok(decoded) => decoded,
Err(_) => str.into()
}
}
Loading

0 comments on commit ce350a7

Please sign in to comment.