diff options
| author | Andrew <saintruler@gmail.com> | 2021-02-14 16:47:56 +0400 |
|---|---|---|
| committer | Andrew <saintruler@gmail.com> | 2021-02-14 16:47:56 +0400 |
| commit | ba53adb8c8e4b1a5f870ee8cdf97489087f2b0bc (patch) | |
| tree | bd56aec0f295e6ec3c633f670db45d3ca1f1e239 | |
| parent | ad92fb3dc6a3350b7b8952be40c5c3a7aa543892 (diff) | |
Added actual formatting of responses
| -rw-r--r-- | blog/src/main.rs | 4 | ||||
| -rw-r--r-- | spider/src/http_server.rs | 4 | ||||
| -rw-r--r-- | spider/src/http_status.rs | 60 | ||||
| -rw-r--r-- | spider/src/lib.rs | 1 | ||||
| -rw-r--r-- | spider/src/response.rs | 51 |
5 files changed, 103 insertions, 17 deletions
diff --git a/blog/src/main.rs b/blog/src/main.rs index 9693f88..c27bc4e 100644 --- a/blog/src/main.rs +++ b/blog/src/main.rs @@ -6,11 +6,11 @@ struct MyHandler {} impl HttpHandler for MyHandler { fn do_get(&self, _request: Request) -> Response { - return Response::new("hey"); + return Response::html(String::from("hey"), 200); } fn do_post(&self, _request: Request) -> Response { - return Response::new("hey"); + return Response::html(String::from("hey"), 200); } } diff --git a/spider/src/http_server.rs b/spider/src/http_server.rs index a3f4bc2..12c410f 100644 --- a/spider/src/http_server.rs +++ b/spider/src/http_server.rs @@ -44,6 +44,7 @@ impl<T> HttpServer<T> where T: HttpHandler { Ok(s) => { println!("Got connection!"); self.handle_client(&s); + // TODO(andrew): handle possible errors. s.shutdown(Shutdown::Both); println!("Connection hadled"); }, @@ -78,6 +79,7 @@ impl<T> HttpServer<T> where T: HttpHandler { HttpMethod::POST => self.handler.do_post(request) }; let response = response.format(); - stream.write(response); + // TODO(andrew): handle possible errors. + stream.write(&response); } } diff --git a/spider/src/http_status.rs b/spider/src/http_status.rs new file mode 100644 index 0000000..0ea889e --- /dev/null +++ b/spider/src/http_status.rs @@ -0,0 +1,60 @@ +pub fn get_status_text(code: u16) -> Option<String> { + match code { + 100 => Some(String::from("Continue")), + 101 => Some(String::from("Switching Protocols")), + 102 => Some(String::from("Processing")), + 200 => Some(String::from("OK")), + 201 => Some(String::from("Created")), + 202 => Some(String::from("Accepted")), + 203 => Some(String::from("Non Authoritative Information")), + 204 => Some(String::from("No Content")), + 205 => Some(String::from("Reset Content")), + 206 => Some(String::from("Partial Content")), + 207 => Some(String::from("Multi Status")), + 226 => Some(String::from("IM Used")), + 300 => Some(String::from("Multiple Choices")), + 301 => Some(String::from("Moved Permanently")), + 302 => Some(String::from("Found")), + 303 => Some(String::from("See Other")), + 304 => Some(String::from("Not Modified")), + 305 => Some(String::from("Use Proxy")), + 307 => Some(String::from("Temporary Redirect")), + 308 => Some(String::from("Permanent Redirect")), + 400 => Some(String::from("Bad Request")), + 401 => Some(String::from("Unauthorized")), + 402 => Some(String::from("Payment Required")), + 403 => Some(String::from("Forbidden")), + 404 => Some(String::from("Not Found")), + 405 => Some(String::from("Method Not Allowed")), + 406 => Some(String::from("Not Acceptable")), + 407 => Some(String::from("Proxy Authentication Required")), + 408 => Some(String::from("Request Timeout")), + 409 => Some(String::from("Conflict")), + 410 => Some(String::from("Gone")), + 411 => Some(String::from("Length Required")), + 412 => Some(String::from("Precondition Failed")), + 413 => Some(String::from("Request Entity Too Large")), + 414 => Some(String::from("Request URI Too Long")), + 415 => Some(String::from("Unsupported Media Type")), + 416 => Some(String::from("Requested Range Not Satisfiable")), + 417 => Some(String::from("Expectation Failed")), + 418 => Some(String::from("I'm a teapot")), + 421 => Some(String::from("Misdirected Request")), + 422 => Some(String::from("Unprocessable Entity")), + 423 => Some(String::from("Locked")), + 424 => Some(String::from("Failed Dependency")), + 426 => Some(String::from("Upgrade Required")), + 428 => Some(String::from("Precondition Required")), + 429 => Some(String::from("Too Many Requests")), + 431 => Some(String::from("Request Header Fields Too Large")), + 449 => Some(String::from("Retry With")), + 451 => Some(String::from("Unavailable For Legal Reasons")), + 500 => Some(String::from("Internal Server Error")), + 501 => Some(String::from("Not Implemented")), + 502 => Some(String::from("Bad Gateway")), + 503 => Some(String::from("Service Unavailable")), + 504 => Some(String::from("Gateway Timeout")), + 505 => Some(String::from("HTTP Version Not Supported")), + _ => None + } +} diff --git a/spider/src/lib.rs b/spider/src/lib.rs index baa0220..9f4074f 100644 --- a/spider/src/lib.rs +++ b/spider/src/lib.rs @@ -2,3 +2,4 @@ pub mod request; pub mod response; pub mod http_method; pub mod http_server; +mod http_status; diff --git a/spider/src/response.rs b/spider/src/response.rs index 35b2f00..3873315 100644 --- a/spider/src/response.rs +++ b/spider/src/response.rs @@ -1,34 +1,57 @@ use std::collections::HashMap; use std::fmt; -// TODO(andrew): add more flexible status structure. -// (Possibly with enum variants?) +use crate::http_status::get_status_text; + pub struct Response { - status: i32, + code: u16, headers: HashMap<String, String>, - body: String + body: Vec<u8>, } +// TODO(andrew): add more constructors for different content types. impl Response { - pub fn new(body: &str) -> Response { - let headers: HashMap<String, String> = HashMap::new(); + pub fn html(html: String, status_code: u16) -> Response { + let mut headers = HashMap::new(); + headers.insert( String::from("Content-Type") + , String::from("text/html") ); + return Response { - status: 200, + code: status_code, headers: headers, - body: String::from(body) + body: html.as_bytes().to_vec(), }; } - pub fn format<'a>(&self) -> &'a [u8] { - // TODO(andrew): replace placeholder response with actual - // formatted response; - let s = "HTTP/1.1 200 OK\r\nConnection: keep-alive\r\nContent-Type: text/html\r\n\r\n<i>Hello</i>"; - return s.as_bytes(); + pub fn format<'a>(&self) -> Vec<u8> { + let status_text = match get_status_text(self.code) { + Some(text) => text, + None => String::from("UNDEFINED") + }; + + let mut data = Vec::new(); + + let first_line = format!("HTTP/1.1 {} {}", self.code, status_text); + let headers = format_headers(&self.headers); + let head = format!("{}\r\n{}\r\n", first_line, headers); + + data.extend_from_slice(head.as_bytes()); + data.extend_from_slice(&self.body); + return data; } } impl fmt::Display for Response { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - return write!(f, "Response({})", self.status); + return write!(f, "Response({})", self.code); + } +} + +fn format_headers(headers: &HashMap<String, String>) -> String { + let mut result = String::new(); + for (key, value) in headers.iter() { + let line = format!("{}: {}\r\n", key, value); + result.push_str(&line); } + return result; } |