diff options
| author | Andrew <saintruler@gmail.com> | 2021-02-18 19:48:06 +0400 |
|---|---|---|
| committer | Andrew <saintruler@gmail.com> | 2021-02-18 19:48:06 +0400 |
| commit | 0a1d8409e49343427d26c4ddf0bf56d5b3e7b7d5 (patch) | |
| tree | 9654645e0c74af2cf834ac4f1d7d68ca219a57e6 /spider | |
| parent | 9b69d6460e91244bfd77d8ea77c13d21d9050e4c (diff) | |
Renamed spider package to spider_server
Diffstat (limited to 'spider')
| -rw-r--r-- | spider/Cargo.toml | 7 | ||||
| -rw-r--r-- | spider/src/http_method.rs | 39 | ||||
| -rw-r--r-- | spider/src/http_server.rs | 135 | ||||
| -rw-r--r-- | spider/src/http_status.rs | 60 | ||||
| -rw-r--r-- | spider/src/lib.rs | 5 | ||||
| -rw-r--r-- | spider/src/request.rs | 114 | ||||
| -rw-r--r-- | spider/src/response.rs | 59 |
7 files changed, 0 insertions, 419 deletions
diff --git a/spider/Cargo.toml b/spider/Cargo.toml deleted file mode 100644 index b35cb43..0000000 --- a/spider/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "spider" -version = "0.1.0" -authors = ["Andrew <saintruler@gmail.com>"] -edition = "2018" - -[dependencies] diff --git a/spider/src/http_method.rs b/spider/src/http_method.rs deleted file mode 100644 index 814c93b..0000000 --- a/spider/src/http_method.rs +++ /dev/null @@ -1,39 +0,0 @@ -use std::fmt; - -#[derive(PartialEq)] -pub enum HttpMethod { - GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH -} - -impl fmt::Display for HttpMethod { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - HttpMethod::GET => write!(f, "GET"), - HttpMethod::HEAD => write!(f, "HEAD"), - HttpMethod::POST => write!(f, "POST"), - HttpMethod::PUT => write!(f, "PUT"), - HttpMethod::DELETE => write!(f, "DELETE"), - HttpMethod::CONNECT => write!(f, "CONNECT"), - HttpMethod::OPTIONS => write!(f, "OPTIONS"), - HttpMethod::TRACE => write!(f, "TRACE"), - HttpMethod::PATCH => write!(f, "PATCH"), - } - } -} - -impl HttpMethod { - pub fn parse(s: String) -> Option<HttpMethod> { - match &*s { - "GET" => Some(HttpMethod::GET), - "HEAD" => Some(HttpMethod::HEAD), - "POST" => Some(HttpMethod::POST), - "PUT" => Some(HttpMethod::PUT), - "DELETE" => Some(HttpMethod::DELETE), - "CONNECT" => Some(HttpMethod::CONNECT), - "OPTIONS" => Some(HttpMethod::OPTIONS), - "TRACE" => Some(HttpMethod::TRACE), - "PATCH" => Some(HttpMethod::PATCH), - _ => None - } - } -} diff --git a/spider/src/http_server.rs b/spider/src/http_server.rs deleted file mode 100644 index 479ef3f..0000000 --- a/spider/src/http_server.rs +++ /dev/null @@ -1,135 +0,0 @@ -use std::net::{TcpListener, TcpStream, Shutdown}; -use std::io::Write; -use std::str; -use std::fmt; - -use crate::request::Request; -use crate::response::Response; -use crate::http_method::HttpMethod; - -pub trait HttpHandler { - fn do_get(&self, _request: Request) -> Response { - return self.default_action(_request); - } - - fn do_head(&self, _request: Request) -> Response { - return self.default_action(_request); - } - - fn do_post(&self, _request: Request) -> Response { - return self.default_action(_request); - } - - fn do_put(&self, _request: Request) -> Response { - return self.default_action(_request); - } - - fn do_delete(&self, _request: Request) -> Response { - return self.default_action(_request); - } - - fn do_connect(&self, _request: Request) -> Response { - return self.default_action(_request); - } - - fn do_options(&self, _request: Request) -> Response { - return self.default_action(_request); - } - - fn do_trace(&self, _request: Request) -> Response { - return self.default_action(_request); - } - - fn do_patch(&self, _request: Request) -> Response { - return self.default_action(_request); - } - - fn default_action(&self, _request: Request) -> Response { - let msg = String::from("<h1>Method not allowed</h1>"); - return Response::html(msg, 405); - } -} - -pub struct HttpServer<T: HttpHandler> { - host: String, - port: u16, - socket: TcpListener, - handler: T -} - -impl<T> HttpServer<T> where T: HttpHandler { - // TODO(andrew): add explanations for errors? - pub fn new(host: &str, port: u16, handler: T) -> Result<HttpServer<T>, &str> { - let addr = format!("{}:{}", host, port); - let sock = TcpListener::bind(addr); - match sock { - Ok(s) => { - let server = HttpServer { - host: String::from(host), - port: port, - socket: s, - handler: handler, - }; - return Ok(server); - }, - Err(_) => return Err("Couldn't start server") - }; - } - - pub fn serve_forever(&self) -> Result<(), &str> { - for stream in self.socket.incoming() { - match stream { - Ok(s) => { - // TODO(andrew): replace println with logging. - println!("Got connection!"); - match self.handle_client(&s) { - Ok(_) => (), - Err(msg) => return Err(msg) - }; - match s.shutdown(Shutdown::Both) { - Ok(_) => println!("Closed connection"), - Err(_) => return Err("Couldn't close client socket") - }; - }, - Err(_) => break - }; - } - return Ok(()); - } - - fn handle_client(&self, mut stream: &TcpStream) -> Result<(), &str> { - let mut buf: [u8; 1024] = [0; 1024]; - // TODO(andrew): read all body, not first 1024 bytes. - stream.peek(&mut buf).expect("Couldn't read from socket"); - - let request = Request::from(&buf); - let request = match request { - Some(r) => r, - None => return Err("Request parsed with errors") - }; - - let response = match request.method { - HttpMethod::GET => self.handler.do_get(request), - HttpMethod::HEAD => self.handler.do_head(request), - HttpMethod::POST => self.handler.do_post(request), - HttpMethod::PUT => self.handler.do_put(request), - HttpMethod::DELETE => self.handler.do_delete(request), - HttpMethod::CONNECT => self.handler.do_connect(request), - HttpMethod::OPTIONS => self.handler.do_options(request), - HttpMethod::TRACE => self.handler.do_trace(request), - HttpMethod::PATCH => self.handler.do_patch(request), - }; - let response = response.format(); - match stream.write(&response) { - Ok(_) => return Ok(()), - Err(_) => return Err("Couldn't write to client socket") - }; - } -} - -impl<T> fmt::Display for HttpServer<T> where T: HttpHandler { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - return write!(f, "HttpServer({}:{})", self.host, self.port); - } -} - diff --git a/spider/src/http_status.rs b/spider/src/http_status.rs deleted file mode 100644 index 0ea889e..0000000 --- a/spider/src/http_status.rs +++ /dev/null @@ -1,60 +0,0 @@ -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 deleted file mode 100644 index 9f4074f..0000000 --- a/spider/src/lib.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod request; -pub mod response; -pub mod http_method; -pub mod http_server; -mod http_status; diff --git a/spider/src/request.rs b/spider/src/request.rs deleted file mode 100644 index 3bd6a8f..0000000 --- a/spider/src/request.rs +++ /dev/null @@ -1,114 +0,0 @@ -use std::collections::HashMap; -use std::fmt; - -use crate::http_method::HttpMethod; - -pub struct Request { - pub method: HttpMethod, - pub path: String, - pub http_version: String, - pub headers: HashMap<String, String>, - pub body: Vec<u8>, -} - -impl Request { - pub fn new( method: HttpMethod - , path: String - , http_version: String - , headers: HashMap<String, String> - , body: Vec<u8> ) -> Request { - return Request { - method, - path, - http_version, - headers, - body, - }; - } - - // TODO(andrew): add some error handling. - pub fn from(data: &[u8]) -> Option<Request> { - let mut lines: Vec<String> = Vec::new(); - let mut line = String::new(); - let mut idx = 0; - let mut char_count = 0; - - // Parsing headers until first empty line (char_count == 0). - for c in data { - let c = *c as char; - line.push(c); - idx += 1; - - if c == '\n' { - lines.push(line); - if char_count == 0 { break; } - else { line = String::new(); } - } - if c != '\r' && c != '\n' { - char_count += 1; - } - } - - let mut body = data[idx..].to_vec(); - - let mut headers: HashMap<String, String> = HashMap::new(); - if lines.len() > 0 { - for line in &lines[1..] { - let line = line - .trim_end_matches("\r\n") - .split(": ") - .collect::<Vec<&str>>(); - headers.insert(String::from(line[0]), line[1..].join(" ")); - } - } - - let request_line = lines[0] - .trim_end_matches("\r\n") - .split(" ") - .collect::<Vec<&str>>(); - - let (path, params) = split_path(String::from(request_line[1])); - - let method = String::from(request_line[0]); - match HttpMethod::parse(method) { - Some(method) => { - if method == HttpMethod::GET { - if let Some(p) = params { - body = p.as_bytes().to_vec(); - } - } - return Some(Request::new( - method, - path, - String::from(request_line[2]), - headers, - body )); - } - None => return None - }; - } -} - -fn split_path(resource: String) -> (String, Option<String>) { - let mut idx = None; - for (i, c) in resource.chars().enumerate() { - if c == '?' { - idx = Some(i); - break; - } - } - match idx { - Some(n) => { - let path = &resource[..n]; - let params = &resource[n + 1..]; - return (path.to_string(), Some(params.to_string())); - }, - None => return (resource, None) - }; -} - -impl fmt::Display for Request { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - return write!(f, "Request({}, {})", self.method, self.path); - } -} diff --git a/spider/src/response.rs b/spider/src/response.rs deleted file mode 100644 index fe1df78..0000000 --- a/spider/src/response.rs +++ /dev/null @@ -1,59 +0,0 @@ -use std::collections::HashMap; -use std::fmt; - -use crate::http_status::get_status_text; - -pub struct Response { - code: u16, - headers: HashMap<String, String>, - body: Vec<u8>, -} - -// TODO(andrew): add more constructors for different content types. -impl Response { - 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 { - code: status_code, - headers: headers, - body: html.as_bytes().to_vec(), - }; - } - - pub fn format<'a>(&self) -> Vec<u8> { - // FIXME(andrew): is undefined status code an error that should - // be handled here? - 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.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; -} |