Http responses contain body

This commit is contained in:
2021-05-16 12:23:34 +02:00
parent a8c6962b20
commit 782ad13e48
7 changed files with 175 additions and 30 deletions

View File

@ -95,16 +95,18 @@ impl StatusClass {
pub struct Status {
code: u16,
message: String,
desc: &'static str,
class: StatusClass,
}
impl Status {
pub fn from_code(status_code: u16) -> Status {
for (code, class, msg, _desc) in &consts::HTTP_STATUSES {
for (code, class, msg, desc) in &consts::HTTP_STATUSES {
if *code == status_code {
return Status {
code: status_code,
message: msg.to_string(),
desc,
class: class.clone(),
};
}
@ -116,10 +118,12 @@ impl Status {
if status_code < 100 || status_code > 599 {
panic!("invalid status code");
}
let status = Status::from_code(status_code);
Status {
code: status_code,
message: message.to_string(),
class: StatusClass::from_code(status_code),
desc: status.desc,
class: status.class,
}
}
}
@ -137,8 +141,8 @@ impl std::fmt::Display for HeaderField {
pub struct Request {
version: String,
method: Method,
uri: String,
pub method: Method,
pub uri: String,
header_fields: Vec<HeaderField>,
}
@ -149,7 +153,7 @@ pub struct Response {
}
impl Response {
fn new() -> Response {
pub fn new() -> Response {
Response {
version: "1.1".to_string(),
status: Status::from_code(200),
@ -157,18 +161,73 @@ impl Response {
}
}
fn status(&mut self, status_code: u16) {
pub fn status(&mut self, status_code: u16) {
self.status = Status::from_code(status_code)
}
fn add_header(&mut self, name: &str, value: &str) {
pub fn add_header(&mut self, name: &str, value: &str) {
self.header_fields.push(HeaderField {
name: String::from(name),
value: String::from(value),
});
}
fn send(&self, stream: &mut Stream) -> Result<(), std::io::Error> {
pub fn find_header(&self, header_name: &str) -> Option<String> {
for field in &self.header_fields {
if field
.name
.to_lowercase()
.eq(header_name.to_ascii_lowercase().as_str())
{
return Some(field.value.clone());
}
}
return None;
}
pub fn send(&mut self, stream: &mut Stream) -> Result<(), std::io::Error> {
self.add_header("Server", "Locutus");
self.add_header(
"Date",
chrono::Utc::now()
.format("%a, %d %b %Y %H:%M:%S GMT")
.to_string()
.as_str(),
);
let mut buf = None;
if let None = self.find_header("Content-Length") {
let (doc, color_name, color) = match self.status.class {
StatusClass::Informational => (consts::INFO_DOCUMENT, "info", "#606060"),
StatusClass::Success => (consts::SUCCESS_DOCUMENT, "success", "#008000"),
StatusClass::Redirection => (consts::WARNING_DOCUMENT, "warning", "#E0C000"),
StatusClass::ClientError => (consts::ERROR_DOCUMENT, "error", "#C00000"),
StatusClass::ServerError => (consts::ERROR_DOCUMENT, "error", "#C00000"),
};
let new_buf = consts::DEFAULT_DOCUMENT
.replace("{status_code}", self.status.code.to_string().as_str())
.replace("{status_message}", self.status.message.as_str())
.replace("{hostname}", "localhost") // TODO hostname
.replace("{theme_color}", color)
.replace("{color_name}", color_name)
.replace("{server_str}", "Locutus server") // TODO server string
.replace(
"{doc}",
doc.replace("{code}", self.status.code.to_string().as_str())
.replace("{message}", self.status.message.as_str())
.replace("{desc}", self.status.desc)
.replace("{info}", "") // TODO info string
.as_str(),
)
.replace("{{", "{")
.replace("}}", "}");
self.add_header("Content-Length", new_buf.len().to_string().as_str());
self.add_header("Content-Type", "text/html; charset=utf-8");
buf = Some(new_buf);
}
let mut header = format!(
"HTTP/{} {:03} {}\r\n",
self.version, self.status.code, self.status.message
@ -177,7 +236,12 @@ impl Response {
header.push_str(format!("{}: {}\r\n", header_field.name, header_field.value).as_str());
}
header.push_str("\r\n");
stream.write_all(header.as_bytes())
stream.write_all(header.as_bytes())?;
if let Some(buf) = buf {
stream.write_all(buf.as_bytes());
}
Ok(())
}
}