diff options
| -rw-r--r-- | blog/src/main.rs | 33 | ||||
| -rw-r--r-- | spider/src/http_method.rs | 28 | ||||
| -rw-r--r-- | spider/src/http_server.rs | 75 | ||||
| -rw-r--r-- | spider/src/lib.rs | 142 | ||||
| -rw-r--r-- | spider/src/request.rs | 56 | ||||
| -rw-r--r-- | spider/src/response.rs | 35 |
6 files changed, 223 insertions, 146 deletions
diff --git a/blog/src/main.rs b/blog/src/main.rs index 0dba17a..c779191 100644 --- a/blog/src/main.rs +++ b/blog/src/main.rs @@ -1,14 +1,31 @@ -use std::net::TcpListener; +use spider::http_server::{HttpHandler, HttpServer}; +use spider::request::Request; +use spider::response::Response; -use spider::handle_client; +struct MyHandler {} -fn main() -> std::io::Result<()> { - let listener = TcpListener::bind("localhost:3000")?; +impl HttpHandler for MyHandler { + fn do_get(&self, _request: Request) -> Response { + return Response::new("hey"); + } - // accept connections and process them serially - for stream in listener.incoming() { - handle_client(stream?); + fn do_post(&self, _request: Request) -> Response { + return Response::new("hey"); } +} - Ok(()) +impl MyHandler { + pub fn new() -> MyHandler { + return MyHandler {}; + } +} + + +fn main() { + let handler = MyHandler::new(); + let server = HttpServer::new("localhost", 3000, handler); + match server { + Some(serv) => serv.serve_forever(), + None => println!("Couldn't start server.") + } } diff --git a/spider/src/http_method.rs b/spider/src/http_method.rs new file mode 100644 index 0000000..b1cc884 --- /dev/null +++ b/spider/src/http_method.rs @@ -0,0 +1,28 @@ +use std::fmt; + +pub enum HttpMethod { + GET, POST +} + +impl fmt::Display for HttpMethod { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + HttpMethod::GET => write!(f, "GET"), + HttpMethod::POST => write!(f, "POST") + } + } +} + +impl HttpMethod { + pub fn parse(s: String) -> Option<HttpMethod> { + if s == "GET" { + return Some(HttpMethod::GET); + } + else if s == "POST" { + return Some(HttpMethod::POST); + } + else { + return None; + } + } +} diff --git a/spider/src/http_server.rs b/spider/src/http_server.rs new file mode 100644 index 0000000..d3fc140 --- /dev/null +++ b/spider/src/http_server.rs @@ -0,0 +1,75 @@ +use std::net::{TcpListener, TcpStream}; +use std::io::Write; +use std::str; + +use crate::request::Request; +use crate::response::Response; +use crate::http_method::HttpMethod; + +pub trait HttpHandler { + fn do_get(&self, request: Request) -> Response; + fn do_post(&self, request: Request) -> Response; +} + +pub struct HttpServer<T: HttpHandler> { + host: String, + port: u16, + socket: TcpListener, + handler: T +} + +impl<T> HttpServer<T> where T: HttpHandler { + // TODO(andrew): Add more verbose error handling. + pub fn new(host: &str, port: u16, handler: T) -> Option<HttpServer<T>> { + 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 Some(server); + }, + Err(_) => return None + } + } + + pub fn serve_forever(&self) { + for stream in self.socket.incoming() { + match stream { + Ok(s) => { + self.handle_client(s); + }, + Err(_) => break + }; + } + } + + fn handle_client(&self, mut stream: TcpStream) { + let mut buf: [u8; 1024] = [0; 1024]; + stream.peek(&mut buf).expect("Couldn't read from socket"); + + // TODO(andrew): remove panic. + let s = match str::from_utf8(&buf) { + Ok(v) => v, + Err(_) => panic!("Couldn't convert u8 to character") + }; + + let request = Request::from(&s); + // TODO(andrew): remove panic. + let request = match request { + Some(r) => r, + None => panic!("Request parsed with errors") + }; + + let response = match request.method { + HttpMethod::GET => self.handler.do_get(request), + HttpMethod::POST => self.handler.do_post(request) + }; + let response = response.format(); + stream.write(response); + } +} diff --git a/spider/src/lib.rs b/spider/src/lib.rs index 66947cf..baa0220 100644 --- a/spider/src/lib.rs +++ b/spider/src/lib.rs @@ -1,138 +1,4 @@ -use std::net::{TcpListener, TcpStream}; -use std::str; -use std::collections::HashMap; -use std::fmt; - -enum HttpMethod { - GET, POST -} - -impl fmt::Display for HttpMethod { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - HttpMethod::GET => write!(f, "GET"), - HttpMethod::POST => write!(f, "POST") - } - } -} - -impl HttpMethod { - pub fn parse(s: String) -> Option<HttpMethod> { - if s == "GET" { - return Some(HttpMethod::GET); - } - else if s == "POST" { - return Some(HttpMethod::POST); - } - else { - return None; - } - } -} - -struct Request { - method: HttpMethod, - path: String, - http_version: String, - headers: HashMap<String, String>, - // body: &'a [u8] -} - -impl Request { - pub fn new( method: HttpMethod - , path: String - , http_version: String - , headers: HashMap<String, String>) -> Request { - return Request { - method, - path, - http_version, - headers - }; - } - - pub fn print(&self) { - println!("Request({}, {})", self.method, self.path); - } -} - -struct Response { - status: i32, - headers: HashMap<String, String>, - body: String -} - -impl Response { - pub fn new(body: &str) -> Response { - let headers: HashMap<String, String> = HashMap::new(); - return Response { - status: 200, - headers: headers, - body: String::from(body) - }; - } -} - -fn do_get(request: Request) -> Response { - return Response::new("hey"); -} - -fn do_post(request: Request) -> Response { - return Response::new("hey"); -} - -// TODO(andrew): Add some error handling. -fn parse_request(data: &str) -> Option<Request> { - let lines = data.split("\r\n").collect::<Vec<&str>>(); - - let mut headers: HashMap<String, String> = HashMap::new(); - for line in &lines[1..] { - let line = line.split(": ").collect::<Vec<&str>>(); - headers.insert(String::from(line[0]), line[1..].join(" ")); - } - - let first_line = lines[0].split(" ").collect::<Vec<&str>>(); - - let method = String::from(first_line[0]); - match HttpMethod::parse(method) { - Some(method) => - return Some(Request::new( - method, - String::from(first_line[1]), - String::from(first_line[2]), - headers )), - None => return None - }; -} - -fn format_response<'a>(response: Response) -> &'a [u8] { - let buf: &[u8]; - buf = &[0; 1024]; - - - return buf; -} - -pub fn handle_client(stream: TcpStream) { - let mut buf: [u8; 1024] = [0; 1024]; - stream.peek(&mut buf).expect("Couldn't read from socket"); - - let s = match str::from_utf8(&buf) { - Ok(v) => v, - Err(_e) => panic!("Couldn't convert u8 to character") - }; - - let request = parse_request(&s); - // TODO(andrew): remove panic!. - let request = match request { - Some(r) => r, - None => panic!("Request parsed with errors") - }; - - let response = match request.method { - HttpMethod::GET => do_get(request), - HttpMethod::POST => do_post(request) - }; - let response = format_response(response); -} - +pub mod request; +pub mod response; +pub mod http_method; +pub mod http_server; diff --git a/spider/src/request.rs b/spider/src/request.rs new file mode 100644 index 0000000..4fae0c5 --- /dev/null +++ b/spider/src/request.rs @@ -0,0 +1,56 @@ +use std::collections::HashMap; +use std::fmt; + +use crate::http_method::HttpMethod; + +pub struct Request { + pub method: HttpMethod, + path: String, + http_version: String, + headers: HashMap<String, String>, + // body: &'a [u8] +} + +impl Request { + pub fn new( method: HttpMethod + , path: String + , http_version: String + , headers: HashMap<String, String>) -> Request { + return Request { + method, + path, + http_version, + headers + }; + } + + // TODO(andrew): Add some error handling. + pub fn from(data: &str) -> Option<Request> { + let lines = data.split("\r\n").collect::<Vec<&str>>(); + + let mut headers: HashMap<String, String> = HashMap::new(); + for line in &lines[1..] { + let line = line.split(": ").collect::<Vec<&str>>(); + headers.insert(String::from(line[0]), line[1..].join(" ")); + } + + let first_line = lines[0].split(" ").collect::<Vec<&str>>(); + + let method = String::from(first_line[0]); + match HttpMethod::parse(method) { + Some(method) => + return Some(Request::new( + method, + String::from(first_line[1]), + String::from(first_line[2]), + headers )), + None => return 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 new file mode 100644 index 0000000..e192b22 --- /dev/null +++ b/spider/src/response.rs @@ -0,0 +1,35 @@ +use std::collections::HashMap; +use std::fmt; + +pub struct Response { + status: i32, + headers: HashMap<String, String>, + body: String +} + +impl Response { + pub fn new(body: &str) -> Response { + let headers: HashMap<String, String> = HashMap::new(); + return Response { + status: 200, + headers: headers, + body: String::from(body) + }; + } + + pub fn format<'a>(&self) -> &'a [u8] { + 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(); + + // let buf: &[u8]; + // buf = &[0; 1024]; +// + // return buf; + } +} + +impl fmt::Display for Response { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + return write!(f, "Response({})", self.status); + } +} |