gnostr_web/response/
mod.rs

1use crate::headers::Headers;
2use crate::request::Request;
3use crate::status::{Status, StatusCode, StatusMethods};
4use std::collections::HashMap;
5use std::io::{BufWriter, Write};
6use std::net::Shutdown;
7
8pub struct Response {
9    pub request: Request,
10    // Response headers
11    pub headers: Option<Headers>,
12    pub status: Option<usize>,
13    pub fixed_content: Option<String>,
14}
15
16impl Response {
17    pub fn new(request: Request) -> Self {
18        return Self {
19            request,
20            headers: None,
21            status: None,
22            fixed_content: None,
23        };
24    }
25
26    fn init_headers(&mut self) {
27        if !self.headers.is_some() {
28            self.headers = Some(HashMap::new());
29        }
30    }
31
32    pub fn set_content_type(&mut self, text: &str) {
33        self.init_headers();
34
35        // Unwrap header hashmap
36        if let Some(ref mut headers) = self.headers {
37            if headers.contains_key("Content-Type") {
38                let content_types = headers.get_mut("Content-Type").unwrap();
39
40                // Content type can only have one instance
41                content_types[0] = text.to_string();
42            } else {
43                let content_types = vec![text.to_string()];
44                let _ = headers.insert("Content-Type".to_string(), content_types);
45            }
46        }
47    }
48
49    /// Headers will be keep appending to the list if already exists
50    pub fn add_header(&mut self, name: &str, value: &str) -> &mut Self {
51        self.init_headers();
52
53        // Unwrap header hashmap
54        if let Some(ref mut headers) = self.headers {
55            if headers.contains_key(name) {
56                let values = headers.get_mut(name).unwrap();
57                values.push(value.to_string());
58            } else {
59                let values = vec![value.to_string()];
60                headers.insert(name.to_string(), values);
61            }
62        }
63
64        return self;
65    }
66
67    pub fn html<T: StatusCode>(&mut self, status: T, text: String) -> &mut Self {
68        self.set_content(status.to_usize(), text);
69        self.set_content_type("text/html");
70        return self;
71    }
72
73    pub fn json<T: StatusCode>(&mut self, status: T, text: String) -> &mut Self {
74        self.set_content(status.to_usize(), text);
75        self.set_content_type("application/json");
76        return self;
77    }
78
79    pub fn set_content(&mut self, status: usize, text: String) -> &mut Self {
80        self.status = Some(status);
81        self.fixed_content = Some(text);
82        return self;
83    }
84
85    pub fn send(&mut self) {
86        if self.status.is_some() {
87            let request = &self.request;
88            let access_from: String;
89
90            match request.stream.peer_addr() {
91                Ok(addr) => {
92                    access_from = addr.to_string();
93                }
94
95                Err(_) => access_from = "UnKnown".to_string(),
96            }
97
98            println!(
99                "{} - \"{} {} {}\"",
100                access_from,
101                request.method,
102                request.pathname,
103                self.status.unwrap()
104            );
105            self.write_http();
106        }
107    }
108
109    fn write_http(&mut self) {
110        let should_close = self.request.should_close_connection();
111
112        let headers = self.headers.as_mut().expect("Response headers missing.");
113        let content_length = format!(
114            "{}",
115            self.fixed_content
116                .as_ref()
117                .expect("Fixed content is missing.")
118                .len()
119        );
120        headers.insert("Content-Length".to_string(), vec![content_length]);
121
122        if !should_close {
123            headers.insert("Connection".to_string(), vec!["keep-alive".to_string()]);
124        }
125
126        // Write repose headers
127        let headers = self.prepare_raw_headers();
128
129        let cloned_stream = self.request.stream.try_clone();
130        if !cloned_stream.is_ok() {
131            println!("Connection closed");
132            self.request.context.dont_wait();
133            return;
134        }
135
136        let mut buf_writer = BufWriter::new(cloned_stream.unwrap());
137        match buf_writer.write_all(headers.as_bytes()) {
138            Ok(_) => {}
139            Err(_) => {
140                println!("Connection closed");
141                self.request.context.dont_wait();
142                return;
143            }
144        }
145
146        // Write response body
147        if self.request.method != "HEAD" {
148            if let Some(content) = &self.fixed_content {
149                buf_writer.write_all(content.as_bytes()).unwrap();
150            }
151        }
152
153        // Flush the buffer
154        if !buf_writer.flush().is_ok() {
155            print!("Connection closed");
156            self.request.context.dont_wait();
157        };
158
159        if should_close {
160            let _ = self.request.stream.shutdown(Shutdown::Both);
161            self.request.context.dont_wait();
162        }
163    }
164
165    fn prepare_raw_headers(&mut self) -> String {
166        let status_code = self.status.expect("Status code not set.");
167
168        let mut status_text = Status::status_text(status_code);
169        if !status_text.is_some() {
170            status_text = Some("Custom Status".to_string());
171        }
172
173        // Header start
174        let mut raw_headers = format!(
175            "HTTP/1.1 {} {}\r\n",
176            self.status.unwrap(),
177            status_text.unwrap()
178        );
179
180        if let Some(headers) = &self.headers {
181            for header_name in headers.keys() {
182                let values = headers.get(header_name).unwrap();
183
184                for value in values {
185                    let header_line = format!("{}: {}\r\n", header_name, value);
186                    raw_headers.push_str(&header_line);
187                }
188            }
189        }
190
191        // Header end
192        raw_headers.push_str("\r\n");
193        return raw_headers;
194    }
195}