gdritter repos httputils / master src / main.rs
master

Tree @master (Download .tar.gz)

main.rs @master

81a9f6b
 
e79eabc
 
 
 
81a9f6b
a35954d
81a9f6b
 
a35954d
e79eabc
a35954d
 
 
e79eabc
b9afbad
1df44df
a35954d
81a9f6b
a35954d
 
 
81a9f6b
 
 
a35954d
 
 
81a9f6b
 
a35954d
 
b9afbad
a35954d
 
 
 
 
 
 
 
 
 
 
1df44df
a35954d
 
 
 
 
 
 
 
 
1df44df
a35954d
 
 
 
 
 
 
 
 
 
 
e79eabc
a35954d
1df44df
 
 
a35954d
 
b9afbad
1df44df
b9afbad
 
 
 
 
1df44df
b9afbad
a35954d
 
 
 
81a9f6b
 
a35954d
 
 
 
81a9f6b
 
e79eabc
 
1df44df
b9afbad
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5095e2e
b9afbad
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e79eabc
 
 
a35954d
 
 
5095e2e
4c37c7d
 
1df44df
 
a35954d
 
 
 
81a9f6b
 
 
a35954d
 
 
b9afbad
a35954d
 
 
 
 
b9afbad
a35954d
 
 
 
 
 
 
 
 
 
 
extern crate hyper;

mod error;
mod helper;
mod matching;

use hyper::Server;
use hyper::server::{Handler,Request,Response};
use hyper::net::Fresh;

use std::env::{args,current_dir,set_current_dir};
use std::fs::{read_dir};
use std::iter::Iterator;
use std::path::Path;

use error::{AloysError,aloys_error};
use matching::do_match;
use helper::{path_for,domain_for,with_file};

#[derive(Debug, Clone)]
enum Delegate {
    Forward(HTTPForward),
    Defer(Routes),
}

#[derive(Debug, Clone)]
struct HTTPForward {
    domn: String,
    port: u32,
}

#[derive(Debug, Clone)]
struct RouteSpec {
    conf_at:     String,
    for_path:    Option<String>,
    for_domain:  Option<String>,
    delegate_to: Delegate,
}

#[derive(Debug, Clone)]
struct Routes {
    routes: Vec<RouteSpec>,
}

impl Routes {
    /// Take a path and load the route specifications contained there.
    fn load_paths(dir: &Path) -> Result<Routes,AloysError> {
        let mut routes = vec![];
        for entry in try!(read_dir(dir)) {
            let entry_path = try!(entry).path();
            routes.push(try!(RouteSpec::from_path(entry_path.as_path())));
        }
        Ok(Routes { routes: routes })
    }

    /// Given a request route it appropriately, or raise an error.
    fn dispatch(&self, req: &Request, res: Response<Fresh>) {
        match self.find_delegate(&req) {
            Some(delegate) => delegate.run(req, res),
            None => {
                let _ = res.send(b"[no matching handler]");
            },
        }
    }

    fn find_delegate(&self, req: &Request) -> Option<&Delegate> {
        for d in self.routes.iter() {
            let mut matches = true;
            if let Some(ref d) = d.for_domain {
                if let Some(domain) = domain_for(req) {
                    matches = matches && do_match(d, domain);
                }
            }
            if let Some(ref p) = d.for_path {
                if let Some(req_path) = path_for(req) {
                    matches = matches && do_match(p, &req_path);
                } else {
                    matches = false;
                }
            }
            if matches {
                println!("using {:?}", d.conf_at);
                return Some(&d.delegate_to);
            }
        }
        return None;
    }
}

impl Handler for Routes {
    fn handle(&self, req: Request, res: Response<Fresh>) {
        self.dispatch(&req, res);
    }
}

impl RouteSpec {
    fn from_path(dir: &Path) -> Result<RouteSpec,AloysError> {
        println!("at dir {:?}", dir);
        let path   = with_file(&dir.join("path"),
                               None,
                               |s| Some(s));
        let domain = with_file(&dir.join("domain"),
                               None,
                               |s| Some(s));
        let mode   = with_file(&dir.join("mode"),
                               "http".to_string(),
                               |s| s);
        let delegate =
            if mode == "http" {
                let domn = with_file(&dir.join("host"),
                                     "localhost".to_string(),
                                     |s| s);
                let port = with_file(&dir.join("port"),
                                     Ok(80),
                                     |s| s.parse::<u32>() );
                let port_num = match port {
                    Ok(n)  => n,
                    Err(e) => return aloys_error(format!("Bad port number in `{:?}: {:?}`", dir, e)),
                };
                Delegate::Forward(HTTPForward { domn: domn,
                                                port: port_num })
            } else if mode == "aloys" {
                let fwd = with_file(&dir.join("conf"),
                                    "/dev/null".to_string(),
                                    |s| s);
                let rts = try!(Routes::load_paths(Path::new(&fwd)));
                Delegate::Defer(rts)
            } else {
                return aloys_error(format!("Unknown mode `{:?}` in `{:?}", mode, dir))
            };
        Ok(RouteSpec {
            conf_at: dir.to_string_lossy().to_string(),
            for_path: path,
            for_domain: domain,
            delegate_to: delegate
        })
    }
}

impl Delegate {
    fn run(&self, req: &Request, res: Response<Fresh>) {
        match self {
            &Delegate::Forward(ref fwd) => {
                let _ = res.send(format!("[forward to {:?}:{:?}]",
                                         fwd.domn,
                                         fwd.port,
                                         ).as_bytes());
            },
            &Delegate::Defer(ref rts) => {
                rts.dispatch(req, res);
            },
        }
    }
}


fn run_server() -> Result<(), AloysError> {
    if let Some(target_dir) = args().skip(1).next() {
        println!("switching to directory {:}", target_dir);
        try!(set_current_dir(Path::new(&target_dir)));
    }
    let cwd = try!(current_dir());
    let paths = try!(Routes::load_paths(cwd.as_path()));
    println!("Read routes: {:?}", paths);
    let srv = try!(Server::http("127.0.0.1:8080"));
    let _ = srv.handle(paths);
    Ok(())
}

fn main() {
    match run_server() {
        Ok(_) => (),
        Err(e) => println!("Got error: {:?}", e),
    };
}