#![allow(deprecated, dead_code)]
use std::collections::HashMap;
use std::net::TcpStream;
use std::io::Write;
use std::path::Path;
use lazy_static::lazy_static;
use std::hash::{ Hash, Hasher };
use std::collections::hash_map::DefaultHasher;
use termcolor::{ Color, ColorChoice, ColorSpec, StandardStream, WriteColor };
lazy_static! {
static ref STATUS_CODES:&'static [(&'static u16, &'static str); 58] = &[
(&400, "Bad Request"), (&500, "Internal Server Error"),
(&401, "Unauthorized"), (&501, "Not Implemented"),
(&402, "Payment Required"), (&502, "Bad Gateway"),
(&403, "Forbidden"), (&503, "Service Unavailable"),
(&404, "Not Found"), (&504, "Gateway Timeout"), (&405, "Method Not Allowed"), (&505, "HTTP Version Not Supported"), (&406, "Not Acceptable"), (&506, "Variant Also Negotiates"), (&407, "Proxy Authentication Required"), (&507, "Insufficient Storage"),
(&408, "Request Timeout"), (&508, "Loop Detected"),
(&409, "Conflict"), (&510, "Not Extended"),
(&410, "Gone"), (&511, "Network Authentication Required"),
(&411, "Length Required"), (&200, "OK"),
(&412, "Precondition Failed"), (&201, "Created"),
(&413, "Payload Too Large"), (&202, "Accepted"),
(&414, "URI Too Long"), (&204, "No Content"),
(&415, "Unsupported Media Type"), (&205, "Reset Content"),
(&416, "Range Not Satisfiable"), (&206, "Partial Content"),
(&417, "Expectation Failed"), (&207, "Multi-status"),
(&418, "I'm a teapot"), (&208, "Already reported"),
(&421, "Misdirected Request"), (&226, "IM Used"),
(&422, "Unprocessable Entity"), (&300, "Multiple Choices"),
(&423, "Locked"), (&301, "Moved Permanently"),
(&424, "Failed Dependency"), (&302, "Found"),
(&425, "Too Early"), (&303, "See Other"), (&426, "Upgrade Required"), (&304, "Not Modified"), (&428, "Precondition Required"), (&305, "Use Proxy"), (&429, "Too Many Requests"), (&306, "Switch Proxy"),
(&431, "Request Header Fields Too Large"), (&307, "Temporary Redirect"),
(&451, "Unavailable For Legal Reasons"), (&308, "Permanent Redirect"),
];
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub enum HeaderReturn {
Single(String),
Multiple(Vec<String>),
Values(HashMap<String, String>),
All,
None,
}
pub fn parse_headers(request:String, header:HeaderReturn) -> HeaderReturn {
let header_strings:Vec<String> = request.split("\n").map(|e| e.to_string()).collect::<Vec<String>>();
let mut headers:HashMap<String, String> = HashMap::new();
for header in header_strings.iter() {
'charLoop: for (i, c) in header.chars().enumerate() {
if c == ':' {
let key = &header[0..i].to_ascii_lowercase();
let value = &header[i+1..];
headers.insert(key.to_string(), value.to_string());
break 'charLoop;
};
};
};
match header {
HeaderReturn::Single(v) => {
return HeaderReturn::Single(
headers.get(&v).unwrap_or(&"".to_string()).trim().to_string()
);
},
HeaderReturn::Multiple(v) => {
let headers_to_get:Vec<String> = v.into_iter().collect::<Vec<String>>();
return HeaderReturn::Values(
headers_to_get.into_iter()
.map(|v| (
v.to_string(),
headers.get(&v)
.unwrap_or(&"".to_string())
.trim().to_string()
))
.collect::<HashMap<String, String>>()
);
},
HeaderReturn::All => {
return HeaderReturn::Values(
headers.into_iter().map(|(k, v)| {
(k, v.trim().to_string())
}).collect::<HashMap<String, String>>()
);
},
HeaderReturn::Values(_) => panic!("Can't get values - Values is read only"),
HeaderReturn::None => panic!("Can't get None - None is read only"),
};
}
#[derive(Debug)]
pub enum ResponseType {
Text,
Json,
Html,
Image(ResponseTypeImage)
}
#[derive(Debug)]
pub enum ResponseTypeImage {
Jpeg,
Png,
Gif,
Webp,
Svg,
}
pub fn respond(
stream:&mut TcpStream,
status:u16,
respond:Option<(ResponseType, &str)>,
additional_headers:Option<Vec<&str>>
) -> () {
let status_msg = STATUS_CODES.iter().find(|&x| x.0 == &status).unwrap_or(&(&0u16, "Internal error - Missing status code")).1;
let response_type = match &respond {
Some(response_type) => match &response_type.0 {
ResponseType::Text => "text/plain",
ResponseType::Json => "application/json",
ResponseType::Html => "text/html",
ResponseType::Image(c) => {
match c {
ResponseTypeImage::Jpeg => "image/jpeg",
ResponseTypeImage::Png => "image/png",
ResponseTypeImage::Gif => "image/gif",
ResponseTypeImage::Webp => "image/webp",
ResponseTypeImage::Svg => "image/svg+xml",
}
}
},
None => "text/plain",
};
let additional_headers:String = match additional_headers {
Some(v) => {
format!("\r\n{}", v.join("\r\n"))
},
None => "".to_string(),
};
if let Some(c) = respond {
stream.write(
format!("HTTP/1.1 {}\r\nContent-Length: {}\r\nContent-Type: {}{additional_headers}\r\n\r\n{}", status, &c.1.len(), response_type, &c.1).as_bytes()
).unwrap();
}else {
stream.write(
format!("HTTP/1.1 {}{additional_headers}\r\n\r\n{} {}", status, status, status_msg).as_bytes()
).unwrap();
};
stream.flush().unwrap();
}
pub fn expect_headers(
stream:&mut TcpStream,
headers:&HeaderReturn,
required:Vec<String>,
) -> bool {
fn get_missing_response(missing:&str) -> String { format!("Missing headers: {:?}", missing) }
match headers {
HeaderReturn::Values(v) => {
for request in &required {
if !v.contains_key(&request.to_string()) || v.get(&request.to_string()).unwrap().trim().len() == 0 {
respond(stream, 400, Some((ResponseType::Text, &get_missing_response(request))), None);
return false;
};
};
},
_ => panic!("Expected HeaderReturn::Values"),
};
true
}
fn reset_terminal_color(stdout: &mut StandardStream) {
stdout.set_color(
ColorSpec::new()
.set_fg(Some(Color::Rgb(171, 178, 191))))
.unwrap();
}
pub fn log(clr:Color, msg:&str) {
let mut stdout = StandardStream::stdout(ColorChoice::Always);
stdout.set_color(
ColorSpec::new()
.set_fg(Some(clr)))
.unwrap();
writeln!(&mut stdout, "{}", msg).unwrap();
reset_terminal_color(&mut stdout);
}
pub(crate) fn hash<H: Hash>(hashval:&H,u:bool) -> String {
let mut hasher = DefaultHasher::new();
hashval.hash(&mut hasher);
if u { return format!("{:X}", hasher.finish()); }
else { return format!("{:x}", hasher.finish()); }
}
pub fn guess_response_type(path:&str) -> ResponseType {
let path:&Path = Path::new(path);
match path.extension() {
Some(ext) => {
match ext.to_str() {
Some("html") => return ResponseType::Html,
Some("htm") => return ResponseType::Html,
Some("json") => return ResponseType::Json,
Some("yml") => return ResponseType::Json,
Some("yaml") => return ResponseType::Json,
Some("gif") => return ResponseType::Image(ResponseTypeImage::Gif),
Some("png") => return ResponseType::Image(ResponseTypeImage::Png),
Some("jpg") => return ResponseType::Image(ResponseTypeImage::Jpeg),
Some("jpeg") => return ResponseType::Image(ResponseTypeImage::Jpeg),
Some("webp") => return ResponseType::Image(ResponseTypeImage::Webp),
Some("svg") => return ResponseType::Image(ResponseTypeImage::Svg),
Some(_) => return ResponseType::Text,
None => return ResponseType::Text,
};
},
None => return ResponseType::Text,
};
}