1use std::{collections::HashMap, io::ErrorKind};
2
3use crate::utils::request::Request;
4use crate::utils::router::Router;
5use base64::prelude::*;
6use mime_guess::from_path;
7use serde_json::{json, Value};
8use std::fs;
9
10
11#[allow(dead_code)]
12pub struct Response {
13 pub content_type: String,
14 pub content_length: usize,
15 pub status_text: String,
16 pub status: i64,
17 pub body: Value,
18 pub raw: String,
19 pub cookies: HashMap<String, String>,
20 pub is_file: bool,
21}
22
23#[allow(dead_code)]
24impl Response {
25 pub fn new() -> Self {
26 Self {
27 content_length: 0,
28 content_type: "text/plain".to_owned(),
29 status_text: "OK".to_owned(),
30 status: 200,
31 body: json!(format!("")),
32 raw: format!("HTTP/1.1 200 OK\r\nContent-type: text/plain\r\n\r\n"),
33 cookies: HashMap::new(),
34 is_file: false,
35 }
36 }
37
38 pub fn text(&self, body: &str, status: i64) -> Response {
39 Response {
40 content_length: body.len(),
41 content_type: "text/plain".to_owned(),
42 status_text: "OK".to_owned(),
43 status,
44 body: json!(body),
45 raw: format!(
46 "HTTP/1.1 {}\r\nContent-Type: text/plain\r\n\r\n{}",
47 status,
48 body
49 ),
50 cookies: self.cookies.to_owned(),
51 is_file: false,
52 }
53 }
54
55 pub fn json(&self, body: Value, status: i64) -> Response {
56 Response {
57 content_length: body.to_string().len(),
58 content_type: "application/json".to_owned(),
59 status_text: "OK".to_owned(),
60 status,
61 body: body.to_owned(),
62 raw: format!(
63 "HTTP/1.1 {}\r\nContent-Type: application/json\r\n\r\n{}",
64 status,
65 serde_json::to_string(&body.to_owned()).unwrap()
66 ),
67 cookies: self.cookies.to_owned(),
68 is_file: false,
69 }
70 }
71
72 pub fn error(&self, body: &str, status: i64) -> Response {
73 Response {
74 status,
75 body: json!(body),
76 status_text: "Internal Server Error".to_owned(),
77 content_length: body.len(),
78 content_type: "text/plain".to_owned(),
79 raw: format!(
80 "HTTP/1.1 {} Internal Server Error\r\nContent-Type: text/plain\r\n\r\n{}",
81 status,
82 body
83 ),
84 cookies: self.cookies.to_owned(),
85 is_file: false,
86 }
87 }
88
89
90 pub fn send_file(&self, path: &str, status: i64) -> Response {
91 let filename = path.split("/").last().unwrap();
92 let file_type = from_path(path).first_or_octet_stream().to_string();
93 let mut file_content = String::new();
94
95 match fs::read_to_string(path) {
96 Ok(content) => file_content = content,
97 Err(err) => {
98 if err.kind() == ErrorKind::InvalidData {
99 let file_bytes = fs::read(path).expect("Error while reading file bytes");
100 file_content = BASE64_STANDARD.encode(file_bytes)
101 } else {
102 eprint!("Error while reading file! {}", err.to_string())
103 }
104 }
105 }
106
107 Response {
108 content_length: file_content.len(),
109 content_type: file_type.to_owned(),
110 status_text: "OK".to_owned(),
111 status,
112 body: json!(format!("{filename}:{file_content}")),
113 raw: format!(
114 "HTTP/1.1 {}\r\nContent-Type: {}\r\n\r\n{}",
115 status, file_type, self.body
116 ),
117 cookies: self.cookies.to_owned(),
118 is_file: true,
119 }
120 }
121
122 pub fn render(&self, path: &str, status: i64) -> Response {
123 let file_content = fs::read_to_string(path).expect("Error while reading html content");
124 Response {
125 content_length: file_content.len(),
126 content_type: "text/html".to_owned(),
127 status_text: "OK".to_owned(),
128 status,
129 body: json!(file_content),
130 raw: format!(
131 "HTTP/1.1 {} OK\r\nContent-Type: text/html\r\nContent-Length: {}\r\n\r\n{}",
132 status,
133 file_content.len(),
134 file_content
135 ),
136 cookies: self.cookies.to_owned(),
137 is_file: false,
138 }
139 }
140
141}
142
143pub async fn handle_response(
144 stream: &mut tokio::net::TcpStream,
145 request: &Request,
146 router: &Router,
147) {
148 router.handle_request(stream, request).await;
149}