/ src / http / http_1_0_parser.rs
http_1_0_parser.rs
  1  use std::fs;
  2  
  3  use chrono::prelude::*;
  4  
  5  use crate::content_fetcher::{file_retriever, path_retriever};
  6  
  7  use crate::http::http_init::{HttpVersion, RequestLine, RequestMethod, StatusLine};
  8  use crate::needed::Polar;
  9  
 10  // reference: https://datatracker.ietf.org/doc/html/rfc1945#section-6
 11  #[allow(non_camel_case_types)]
 12  pub(crate) struct http_1_0_response {
 13      pub(crate) status_line: StatusLine,
 14      pub(crate) general_headers: Vec<GeneralHeader>,
 15      pub(crate) response_headers: Vec<ResponseHeader>,
 16      pub(crate) entity_headers: Vec<EntityHeader>,
 17      pub(crate) body: Vec<u8>,
 18  }
 19  
 20  
 21  // reference: https://datatracker.ietf.org/doc/html/rfc1945#section-5
 22  #[allow(non_camel_case_types)]
 23  struct http_1_0_request {
 24      request_line: RequestLine,
 25      general_headers: Vec<GeneralHeader>,
 26      request_headers: Vec<RequestHeader>,
 27      entity_headers: Vec<EntityHeader>,
 28      body: Option<Vec<u8>>,
 29  }
 30  
 31  pub(crate) enum GeneralHeader {
 32      Date(String),       // http-date of when the message was sent
 33      Pragma(String),     // this can specify that it shouldn't get cached material
 34  }
 35  
 36  enum RequestHeader {
 37      Authorization(String),      // authorization for the url
 38      From(String),               // from what email the request was sent from... why is this a thing?
 39      IfModifiedSince(String),    // http-date
 40      Referer(String),            // the uri from where the client was before
 41      UserAgent(String),          // the user agent of the browser, silly
 42  }
 43  
 44  pub(crate) enum ResponseHeader {
 45      Location(String),           // the new location if a file is moved somewhere
 46      Server(String),             // the name of the server and any info
 47      WWWAuthenticate(String),    // tells the client how it should authenticate (for 401)
 48  }
 49  
 50  
 51  // only exists when there is a body :3
 52  pub(crate) enum EntityHeader {
 53      Allow(Vec<RequestMethod>),          // what request methods is supported on the page, should be ignored with POST
 54      ContentEncoding(String),            // encodings (e.g. gzip) that the client supports
 55      ContentLength(u128),                // length of "content" (body) in bytes
 56      ContentType(String),                // the media-type of the content
 57      Expires(String),                    // for how long it should be cached, in http-date
 58      LastModified(String),               // when the client things it was last modified, in http-date
 59      ExtensionHeader(String, String),    // custom headers that aren't in the http 1.0 spec
 60  }
 61  
 62  
 63  fn parser(request: Vec<String>, request_line: RequestLine) -> http_1_0_request {
 64      let mut general_headers = Vec::new();
 65      let mut request_headers = Vec::new();
 66      let mut entity_headers = Vec::new();
 67  
 68      for line in request {
 69          if let Some((header_name, header_value)) = line.split_once(": ") {
 70              let header_value = header_value.to_string(); // overshadow for ease of use lmao
 71  
 72              match header_name {
 73                  // General Headers
 74                  "Date" => general_headers.push(GeneralHeader::Date(header_value)),
 75                  "Pragma" => general_headers.push(GeneralHeader::Pragma(header_value)),
 76  
 77                  // Request Headers
 78                  "Authorization" => request_headers.push(RequestHeader::Authorization(header_value)),
 79                  "From" => request_headers.push(RequestHeader::From(header_value)),
 80                  "If-Modified-Since" => request_headers.push(RequestHeader::IfModifiedSince(header_value)),
 81                  "Referer" => request_headers.push(RequestHeader::Referer(header_value)),
 82                  "User-Agent" => request_headers.push(RequestHeader::UserAgent(header_value)),
 83  
 84                  // Entity Headers
 85                  "Allow" => {
 86                      let methods: Vec<RequestMethod> = header_value
 87                          .split(", ")
 88                          .map(|value| match value {
 89                              "GET" => RequestMethod::GET,
 90                              "HEAD" => RequestMethod::HEAD,
 91                              "POST" => RequestMethod::POST,
 92                              _ => RequestMethod::NotImplemented,
 93                          }
 94                          ).collect();
 95  
 96                      entity_headers.push(EntityHeader::Allow(methods))
 97                  }
 98  
 99                  "Content-Encoding" => entity_headers.push(EntityHeader::ContentEncoding(header_value)),
100                  "Content-Length" =>
101                      if let Ok(length) = header_value.parse::<u128>() {
102                          entity_headers.push(EntityHeader::ContentLength(length));
103                      }
104                  
105                  "Content-Type" => entity_headers.push(EntityHeader::ContentType(header_value)),
106                  "Expires" => entity_headers.push(EntityHeader::Expires(header_value)),
107                  "Last-Modified" => entity_headers.push(EntityHeader::LastModified(header_value)),
108  
109                  // all the non-spec headers will be seen as entity headers :3
110                  _ => entity_headers.push(EntityHeader::ExtensionHeader(header_name.to_string(), header_value)), // any unknown headers :D!
111              }
112          }
113      }
114  
115      if request_line.method == RequestMethod::POST {
116          todo!() // hehe~
117      }
118  
119      http_1_0_request {
120          request_line,
121          general_headers,
122          request_headers,
123          entity_headers,
124          body: None,
125      }
126  }
127  
128  
129  pub(crate) fn return_response(request: Vec<String>, request_line: RequestLine) -> Polar<http_1_0_response> {
130      let request = parser(request, request_line);
131  
132      let path_buf = match path_retriever(request.request_line.uri) {
133          Polar::Some(path_buf) => path_buf,
134          Polar::Silly(code) => return Polar::Silly(code),
135      };
136  
137      let metadata = match fs::metadata(&path_buf) {
138          Ok(data) => data,
139          Err(_) => return Polar::Silly(500),
140      };
141  
142      let file = match file_retriever(&path_buf) {
143          Polar::Some(file) => file,
144          Polar::Silly(code) => return Polar::Silly(code),
145      };
146  
147  
148      let general_headers: Vec<GeneralHeader> = vec![
149          GeneralHeader::Date({
150              let now: DateTime<Utc> = Utc::now();    // Get the current UTC time
151              now.format("%a, %d %b %Y %H:%M:%S GMT").to_string() // Format da time as HTTP-date
152          }),
153          GeneralHeader::Pragma("no-cache".to_string()),
154      ];
155  
156      let response_headers: Vec<ResponseHeader> = vec![
157          //ResponseHeader::Location(),
158          ResponseHeader::Server("PolarBear".to_string()),
159          //ResponseHeader::WWWAuthenticate(),
160      ];
161  
162  
163      let entity_headers: Vec<EntityHeader> = vec![
164          EntityHeader::Allow(vec![RequestMethod::GET]),
165          //EntityHeader::ContentEncoding()
166          EntityHeader::ContentLength(file.len() as u128),
167          EntityHeader::ContentType(
168              //todo! change this to metadata? maybe?
169              match path_buf.extension().and_then(|ext| ext.to_str()) {
170                  None => { "text/plain; charset=utf-8".to_string() }
171                  Some(extension) => match extension {
172                      "html" => "text/html; charset=utf-8".to_string(),
173                      "css" => "text/css; charset=utf-8".to_string(),
174                      "js" => "text/javascript; charset=utf-8".to_string(),
175                      "txt" => "text/plain; charset=utf-8".to_string(),
176  
177                      "png" => "image/png".to_string(),
178                      "apng" => "image/apng".to_string(),
179                      "avif" => "image/avif".to_string(),
180                      "gif" => "image/gif".to_string(),
181                      "jpeg" => "image/jpeg".to_string(),
182                      "jpg" => "image/jpeg".to_string(),
183                      "svg" => "image/svg+xml".to_string(),
184                      "webp" => "image/webp".to_string(),
185  
186                      "mp4" => "video/mp4".to_string(),
187                      "av1" => "video/AV1".to_string(),
188  
189                      _ => "text/plain; charset=utf-8".to_string(),
190                  }
191              }),
192          //EntityHeader::Expires()
193          EntityHeader::LastModified(metadata
194              .modified()
195              .map(|time| {
196                  let datetime: DateTime<Utc> = time.into(); // Convert `SystemTime` to the `DateTime<Utc>`
197                  datetime.format("%a, %d %b %Y %H:%M:%S GMT").to_string() // Format the time in HTTP-date format
198              })
199              .unwrap_or({ // Fallback to a default date
200                  let now: DateTime<Utc> = Utc::now();    // Get the current UTC time
201                  now.format("%a, %d %b %Y %H:%M:%S GMT").to_string() // Format da time as HTTP-date
202              })
203          ),
204      ];
205  
206  
207      Polar::Some(http_1_0_response {
208          status_line: StatusLine {
209              version: HttpVersion::HTTP_1_0,
210              status_code: 200,
211              reason_phrase: "OK".to_string(),
212          },
213          general_headers,
214          response_headers,
215          entity_headers,
216          body: file,
217      })
218  }
219  
220  
221  impl http_1_0_response {
222      pub(crate) fn into_response(mut self) -> Vec<u8> {
223          let mut response: Vec<u8> = vec![];
224          // status line
225          response.append(&mut format!("HTTP/1.0 {} {} \r\n", self.status_line.status_code, self.status_line.reason_phrase).into_bytes());
226  
227          // da headers
228          for general_header in self.general_headers {
229              match general_header {
230                  GeneralHeader::Date(data) => response.append(&mut format!("Date: {}\r\n", data).into_bytes()),
231                  GeneralHeader::Pragma(data) => response.append(&mut format!("Pragma: {}\r\n", data).into_bytes()),
232              }
233          }
234  
235          for response_header in self.response_headers {
236              match response_header {
237                  ResponseHeader::Location(data) => response.append(&mut format!("Location: {}\r\n", data).into_bytes()),
238                  ResponseHeader::Server(data) => response.append(&mut format!("Server: {}\r\n", data).into_bytes()),
239                  ResponseHeader::WWWAuthenticate(data) => response.append(&mut format!("WWW-Authenticate: {}\r\n", data).into_bytes()),
240              }
241          }
242  
243          for entity_header in self.entity_headers {
244              match entity_header {
245                  EntityHeader::Allow(data) => {
246                      let data: String = data
247                          .iter()
248                          .map(|value| match value {
249                              RequestMethod::GET => "GET".to_string(),
250                              RequestMethod::HEAD => "HEAD".to_string(),
251                              RequestMethod::POST => "POST".to_string(),
252                              RequestMethod::NotImplemented => "".to_string(),
253                          })
254                          .collect::<Vec<String>>()
255                          .join(", ");
256  
257                      response.append(&mut format!("Allow: {}\r\n", data).into_bytes())
258                  }
259                  EntityHeader::ContentEncoding(data) => response.append(&mut format!("Content-Encoding: {}\r\n", data).into_bytes()), //todo: make modular since multiple can be defined
260                  EntityHeader::ContentLength(data) => response.append(&mut format!("Content-Length: {}\r\n", data).into_bytes()),
261                  EntityHeader::ContentType(data) => response.append(&mut format!("Content-Type: {}\r\n", data).into_bytes()),
262                  EntityHeader::Expires(data) => response.append(&mut format!("Expires: {}\r\n", data).into_bytes()),
263                  EntityHeader::LastModified(data) => response.append(&mut format!("Last-Modified: {}\r\n", data).into_bytes()),
264                  EntityHeader::ExtensionHeader(_, _) => {} // todo: will not be processed, should prob change this but whatever
265              }
266          }
267          response.append(&mut "\r\n".to_string().into_bytes());
268          // body
269          response.append(&mut self.body);
270  
271          // return response :3
272          response
273      }
274  }
275  
276  
277