4
\$\begingroup\$

After my last attempt, I started to learn Rust again. This time I wrote a simple HTTP router. I would appreciate it if you could help me to fix my mistakes.

Here is the link of the repository

main.rs

fn main() {
    let mut router = Router::new();
    router.get("/home", home);
    Server::new(router).run("127.0.0.1:8989");
}

fn home(req: request::Request) -> Response {
    let res = format!("Welcome Home: {}", req.get("name"));
    Response::html(String::from(res), 200)
}

src/router.rs

pub type Handler = fn(Request) -> Response;

pub struct Route {
  pub  pattern: String,
  pub  method: String,
  pub  callback: Handler,
}


pub struct Router {
    pub routes: Vec<Route>,
}

impl Router {
    pub fn new() -> Router {
        Router {
            routes: Vec::new(),
        }
    }

    fn route(&mut self, method: &str, pattern: &str, f: Handler) {
        let r = Route{method: String::from(method), pattern: String::from(pattern), callback: f} ;
        self.routes.push(r);
    }

    pub fn get(&mut self, pattern: &str, f: Handler) {
        self.route(Method::GET, pattern, f)
    }

    pub fn post(&mut self, pattern: &str, f: Handler)  {
        self.route(Method::POST, pattern, f)
    }
}

src/request.rs

pub mod method{
    pub const GET: &str = "GET";
    pub const POST: &str = "POST";
}

pub struct Request {
    path: String,
    method: String,
    params: HashMap<String, String>,
}

impl Request {
    pub fn parse(stream:&mut TcpStream) -> Request {

        let mut lines = String::new();
        let mut reader = BufReader::new(stream);
        let _ = reader.read_line(&mut lines);
        let mut line = lines.split_whitespace();

        let method = match line.nth(0) {
            Some(e) => e,
            None => "GET",
        };

        let path = match line.nth(0) {
            Some(e) => e,
            None => "/",
        };
        let parts = Request::parse_path(path);
        let req_path = parts[0];
        let query_string = match parts.get(1).or(None) {
            Some(q) => {
                let tags: HashMap<String, String> = q.split('&')
                    .map(|kv| kv.split('=').collect::<Vec<&str>>())
                    .map(|vec| {
                        assert_eq!(vec.len(), 2);
                        (vec[0].to_string(), vec[1].to_string())
                    })
                    .collect();
                    tags
            },

            None => HashMap::new(),
        };

        Request {
            method: method.to_string(),
            path: req_path.to_string(),
            params: query_string,
        }

    }

    fn parse_path(line: &str) -> Vec<&str> {
        let parts: Vec<&str> = line.split("?").collect();
        parts
    }

    pub fn get(&self, key: &str) -> &str {
        match self.params.get(key) {
            Some(e) => e,
            None => "",
        }
    }

    pub fn method(&self) -> String {
        self.method.to_string()
    }

    pub fn path(&self) -> String {
        self.path.to_string()
    }
}

src/response.rs

pub struct Server {
    router: Router,
}

impl Server {
    pub fn new(router: Router) -> Server {
        Server{ router: router}
    }

    pub fn run(&self, addr: &str) {
        let listener = TcpListener::bind(addr).unwrap();
        println!("Listening to {}", addr);
        for stream in listener.incoming() {
            self.handle(&mut stream.unwrap());
        }
    }

    fn handle(&self, stream: &mut TcpStream) {
        let req = Request::parse(stream);
        for r in &self.router.routes {
            if r.method == req.method() && r.pattern == req.path() {
                self.dispatch(stream, r.callback, req);
                break;
            }
        }
    }

    fn dispatch(&self, stream:&mut TcpStream, handler: Handler, req: Request)    {
        let response = (handler)(req);    
        response.result(stream);
    }
}
\$\endgroup\$
  • \$\begingroup\$ By mistakes, do you mean the code isn’t working and you want us to fix it or you want advice on how to make it better? If it’s the latter you should change your wording to reflect such. \$\endgroup\$ – Linny May 4 at 15:15
  • \$\begingroup\$ @DavidWhite: It's working fine. I posted here because I want to make it better. The Codereview exists for this purpose, right? \$\endgroup\$ – Saeed M. May 4 at 16:06
  • \$\begingroup\$ Correct, but some could view “mistakes” as code that doesn’t work. Wording means a lot when asking a good question \$\endgroup\$ – Linny May 4 at 20:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.