aboutsummaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs122
1 files changed, 121 insertions, 1 deletions
diff --git a/src/main.rs b/src/main.rs
index 97a8ae6..0951518 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,4 +1,124 @@
+use std::convert::Infallible;
+use std::net::SocketAddr;
+use std::path::PathBuf;
+use std::rc::Rc;
+use std::sync::Arc;
+
+use hyper::service::{make_service_fn, service_fn};
+use hyper::{header, Body, Request, Response, Server, StatusCode};
+use rhai::{Dynamic, Engine, FnPtr, Map, NativeCallContext, Scope};
+use structopt::StructOpt;
+
+mod utils;
+
+#[derive(Debug, StructOpt)]
+struct Opt {
+ #[structopt(long, parse(from_os_str), default_value = "narchttpd.rhai")]
+ config_script: PathBuf,
+}
+
+fn make_engine() -> Engine {
+ let mut engine = Engine::new();
+ engine.register_type_with_name::<Rc<Request<Body>>>("Request");
+ engine.register_type::<utils::serve_static::Params>();
+ engine.register_fn(
+ "handle_request_serve_static",
+ utils::serve_static::handle_request,
+ );
+ engine.register_fn("serve_static", utils::serve_static::serve_static);
+ engine.register_fn(
+ "handle_request_proxy_child",
+ utils::proxy_child::handle_request,
+ );
+ engine.register_fn("proxy_child", utils::proxy_child::proxy_child);
+ engine
+}
+
+fn get_config_scope(engine: &Engine, opt: &Opt) -> Scope<'static> {
+ let mut ast = engine.compile_file(opt.config_script.clone()).unwrap();
+
+ let mut scope = Scope::new();
+ scope.push("http_ports", [80]);
+ scope.push("https_ports", [443]);
+ scope.push("domains", Map::new());
+ let export_ast = engine
+ .compile("export http_ports, https_ports, domains;")
+ .unwrap();
+ ast.combine(export_ast);
+ let _: () = engine.eval_ast_with_scope(&mut scope, &ast).unwrap();
+ scope
+}
+
#[tokio::main]
async fn main() {
- // swag
+ let opt = Arc::new(Opt::from_args());
+
+ let engine = make_engine();
+ let scope = get_config_scope(&engine, &opt);
+
+ let http_ports: rhai::Array = scope.get_value("http_ports").unwrap();
+ let http_ports: Vec<u16> = http_ports
+ .into_iter()
+ .map(|x| x.as_int().unwrap() as u16)
+ .collect();
+ let https_ports: rhai::Array = scope.get_value("https_ports").unwrap();
+ let https_ports: Vec<u16> = https_ports
+ .into_iter()
+ .map(|x| x.as_int().unwrap() as u16)
+ .collect();
+
+ assert!(https_ports.is_empty(), "HTTPS is complicated oops");
+
+ // TODO learn hyper
+ let do_response = move |ctx: &NativeCallContext, domains: Map, req: Request<Body>| {
+ for (domain, handler) in &domains {
+ let req_domain = req.headers().get(header::HOST).unwrap();
+ if domain.as_str() == req_domain {
+ eprintln!("request {:?} matched domain {}", req, domain);
+ // matched!
+ let handler: FnPtr = handler.clone_cast();
+ let args = [Dynamic::from(Rc::new(req))];
+ let result = handler.call_dynamic(ctx, None, args).unwrap();
+ let result: Rc<Response<Body>> = result.cast();
+ return Rc::try_unwrap(result).unwrap();
+ }
+ }
+ Response::builder()
+ .status(StatusCode::NOT_FOUND)
+ .body(Body::from("Not Found"))
+ .unwrap()
+ };
+ let addr = http_ports.into_iter().flat_map(|port| {
+ ["127.0.0.1", "::1"]
+ .iter()
+ .map(move |ip| SocketAddr::new(ip.parse().unwrap(), port))
+ });
+
+ let make_svc = make_service_fn(move |_conn| async move {
+ Ok::<_, Infallible>(service_fn(move |req| async move {
+ let handle = tokio::runtime::Handle::current();
+ let response = handle
+ .spawn_blocking(move || {
+ let opt = Opt::from_args();
+ let engine = make_engine();
+ let scope = get_config_scope(&engine, &opt);
+ let request_handler_context =
+ NativeCallContext::new(&engine, "handle_request", &[]);
+ let domains: Map = scope.get_value("domains").unwrap();
+ do_response(&request_handler_context, domains, req)
+ })
+ .await
+ .unwrap();
+ Ok::<_, Infallible>(response)
+ }))
+ });
+
+ // TODO uhh
+ let mut addr = addr;
+ let addr = addr.nth(0).unwrap();
+ let server = Server::bind(&addr).serve(make_svc);
+
+ if let Err(e) = server.await {
+ eprintln!("server error: {}", e);
+ }
}