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),
};
}