czh_http_server/
response.rs1use std::{
2 cell::RefCell,
3 collections::HashMap,
4 fmt::format,
5 io::{BufReader, BufWriter, Read, Write},
6 net::TcpStream,
7 ops::{Deref, DerefMut},
8 rc::Rc,
9};
10
11struct StatusLine {
12 version: String,
13 status_code: u16,
14 reason: String,
15}
16impl StatusLine {
17 fn to_string(&self) -> String {
18 format!("{} {} {}\r\n", self.version, self.status_code, self.reason)
19 }
20}
21pub struct HttpResponse {
22 stream: Option<Rc<RefCell<TcpStream>>>,
23 headers: HashMap<String, String>,
24 status_line: StatusLine,
25}
26impl HttpResponse {
27 pub fn init(stream: Rc<RefCell<TcpStream>>, version: &str) -> Self {
28 let headers = init_headers();
29 HttpResponse {
30 stream: Some(stream),
31 headers,
32 status_line: StatusLine {
33 version: String::from(version),
34 status_code: 200,
35 reason: "OK".to_string(),
36 },
37 }
38 }
39 fn set_content_length(&mut self, len: u64) {
40 self.headers
41 .insert("Content-Length".to_string(), len.to_string());
42 }
43 fn set_content_type(&mut self, content_type: ContentType) {
44 self.headers
45 .insert("Content-Type".to_string(), content_type.into());
46 }
47
48 pub fn json<T>(mut self, data: T)
49 where
50 T: serde::Serialize,
51 {
52 self.headers
53 .insert(String::from("Content-Type"), "application/json".to_string());
54 let json = serde_json::to_string(&data).unwrap();
55 let data = json.as_bytes();
56 self.set_content_length(data.len() as u64);
57 let stream = self.stream.take().unwrap();
58 let mut stream = stream.borrow_mut();
59 let mut writer: BufWriter<&mut TcpStream> = BufWriter::new(stream.deref_mut());
60 self.write_status_headers(&mut writer);
61 writer.write_all(data).unwrap();
62 }
63 fn write_status_headers(&mut self, writer: &mut BufWriter<&mut TcpStream>) {
64 let status = self.status_line.to_string();
65 writer.write_all(status.as_bytes()).unwrap();
66 for (key, value) in self.headers.iter() {
67 let header = format!("{}:{}\r\n", key, value);
68 writer.write_all(header.as_bytes()).unwrap();
69 }
70 writer.write_all(b"\r\n").unwrap();
71 }
72 pub(crate) fn file(mut self, file: std::fs::File, content_type: ContentType) {
73 self.set_content_length(file.metadata().unwrap().len());
74 self.set_content_type(content_type);
75
76 let stream = self.stream.take().unwrap();
77 let mut stream = stream.borrow_mut();
78 let mut writer: BufWriter<&mut TcpStream> = BufWriter::new(stream.deref_mut());
79 self.write_status_headers(&mut writer);
80 let mut reader = BufReader::new(file);
81 let mut buffer = [0; 1024];
82 loop {
83 let size = reader.read(&mut buffer).unwrap();
84 if size == 0 {
85 break;
86 }
87 writer.write_all(&buffer[0..size]).unwrap();
88 }
89 }
90 pub fn set_cookie(&mut self, name: &str, value: &str) {
110 self.headers.insert("Set-Cookie".to_string(), format!("{}={};", name, value));
111 }
112}
113
114fn init_headers() -> HashMap<String, String> {
115 let mut headers = HashMap::new();
116 headers
118}
119pub enum ContentType {
120 JSON,
121 HTML,
122 CSS,
123 JS,
124 PNG,
125 JPG,
126 SVG,
127 TXT,
128 OTHER,
129}
130impl From<&str> for ContentType {
131 fn from(suffix: &str) -> Self {
132 match suffix {
133 "json" => ContentType::JSON,
134 "html" => ContentType::HTML,
135 "css" => ContentType::CSS,
136 "js" => ContentType::JS,
137 "png" => ContentType::PNG,
138 "jpg" => ContentType::JPG,
139 "jpeg" => ContentType::JPG,
140 "svg" => ContentType::SVG,
141 "txt" => ContentType::TXT,
142 _ => ContentType::OTHER,
143 }
144 }
145}
146impl Into<String> for ContentType {
147 fn into(self) -> String {
148 match self {
149 ContentType::JSON => "application/json".to_string(),
150 ContentType::HTML => "text/html".to_string(),
151 ContentType::CSS => "text/css".to_string(),
152 ContentType::JS => "text/javascript".to_string(),
153 ContentType::PNG => "image/png".to_string(),
154 ContentType::JPG => "image/jpeg".to_string(),
155 ContentType::SVG => "image/svg+xml".to_string(),
156 ContentType::TXT => "text/plain".to_string(),
157 ContentType::OTHER => "application/octet-stream".to_string(),
158 }
159 }
160}