From 03e965791f78fdca8cdbf2c498610e31a9d307d1 Mon Sep 17 00:00:00 2001 From: Michael Zhang Date: Wed, 15 Aug 2018 22:16:13 -0700 Subject: [PATCH] most of git delivery, gotta refactor the whole thing for body tho --- Cargo.lock | 1 + Cargo.toml | 1 + examples/github.rs | 8 ++++-- src/handler.rs | 58 ++++++++++++++++++++++++++++----------- src/lib.rs | 67 +++++++++++++++++++++++++++------------------- 5 files changed, 91 insertions(+), 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a7e6050..3c9ca43 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -187,6 +187,7 @@ name = "dip" version = "0.1.0" dependencies = [ "failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "hmac 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.12.8 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 12b003d..38924da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ sha-1 = "0.7" [dependencies] failure = "0.1" +futures = "0.1" hyper = "0.12" lazy_static = "1.1" notify = "4.0" diff --git a/examples/github.rs b/examples/github.rs index 8880bf1..21c10e9 100644 --- a/examples/github.rs +++ b/examples/github.rs @@ -13,6 +13,7 @@ extern crate structopt; use std::collections::HashMap; use std::io::{self, Read}; use std::iter::FromIterator; +use std::path::PathBuf; use failure::{err_msg, Error}; use generic_array::GenericArray; @@ -31,6 +32,7 @@ struct Opt { #[derive(Serialize, Deserialize)] struct Config { secret: String, + outdir: PathBuf, } #[derive(Serialize, Deserialize)] @@ -45,7 +47,9 @@ fn main() -> Result<(), Error> { let mut payload = String::new(); io::stdin().read_to_string(&mut payload)?; + println!("raw payload: {}", payload); let payload: Payload = serde_json::from_str(&payload)?; + println!("processed payload: {}", payload.body); let secret = GenericArray::from_iter(config.secret.bytes()); let mut mac = Hmac::::new(&secret); @@ -60,13 +64,13 @@ fn main() -> Result<(), Error> { let auth = payload .headers - .get("X-Hub-Signature") + .get("x-hub-signature") .ok_or(err_msg("Missing auth header"))?; let left = SecStr::from(format!("sha1={}", signature)); let right = SecStr::from(auth.bytes().collect::>()); assert!(left == right, "HMAC signature didn't match"); - println!("{}", payload.body); + println!("gonna clone it to {:?}", config.outdir); Ok(()) } diff --git a/src/handler.rs b/src/handler.rs index 3512a46..b01b3b6 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -1,13 +1,16 @@ +use std::io::Write; use std::path::PathBuf; -use std::process::Command; +use std::process::{Command, Stdio}; use failure::{err_msg, Error}; -use serde_json::Value as JsonValue; +use serde::Serialize; +use serde_json::{Serializer as JsonSerializer, Value as JsonValue}; use toml::Value as TomlValue; use PROGRAMS; pub struct Handler { + config: TomlValue, exec: PathBuf, } @@ -25,21 +28,46 @@ impl Handler { .ok_or(err_msg(format!("'{}' is not a valid executable", handler))) .map(|value| value.clone())? }; - Ok(Handler { exec }) + let config = config.clone(); + Ok(Handler { config, exec }) } - pub fn run(&self, _: Result) -> Result { - Command::new(&self.exec) + pub fn run(&self, input: JsonValue) -> Result { + let config = { + let mut buf: Vec = Vec::new(); + { + let mut serializer = JsonSerializer::new(&mut buf); + TomlValue::serialize(&self.config, &mut serializer)?; + } + String::from_utf8(buf).unwrap() + }; + + let mut child = Command::new(&self.exec) .env("DIP_ROOT", "") - .output() - .map_err(|err| err_msg(format!("{}", err))) - .and_then(|output| { - if !output.status.success() { - return Err(err_msg(format!( - "'{:?}' returned with a non-zero status code: {}", - self.exec, output.status - ))); + .arg("--config") + .arg(config) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn()?; + { + match child.stdin { + Some(ref mut stdin) => { + write!(stdin, "{}", input)?; } - Ok(json!({})) - }) + None => bail!("done fucked"), + }; + } + let output = child.wait_with_output()?; + if !output.status.success() { + // TODO: get rid of unwraps + return Err(err_msg(format!( + "'{:?}' returned with a non-zero status code: {}\nstdout:\n{}\nstderr:\n{}", + self.exec, + output.status, + String::from_utf8(output.stdout).unwrap(), + String::from_utf8(output.stderr).unwrap() + ))); + } + Ok(json!({})) } } diff --git a/src/lib.rs b/src/lib.rs index 2d35fa5..20d29bd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,9 @@ //! # Dip +#[macro_use] extern crate failure; extern crate hyper; +extern crate futures; extern crate serde; #[macro_use] extern crate serde_json; @@ -27,7 +29,7 @@ use std::time::Duration; use failure::{err_msg, Error}; use hyper::rt::Future; use hyper::service::service_fn_ok; -use hyper::{Body, Request, Response, Server}; +use hyper::{Body, Request, Response, Server, StatusCode}; use notify::{RecommendedWatcher, RecursiveMode, Watcher}; use regex::Regex; use walkdir::WalkDir; @@ -45,22 +47,12 @@ lazy_static! { static ref HOOKS: Mutex> = Mutex::new(HashMap::new()); } -const NOTFOUND: &str = r#" - - - - -

Looks like you took a wrong turn!

-

There's nothing to see here.

- -"#; +const NOTFOUND: &str = "

Looks like you took a wrong turn!

There's nothing to see here.

"; -fn service_fn(req: &Request) -> Result, Error> { +fn service_fn(req: Request) -> Result, Error> { + let path = req.uri().path().to_owned(); let captures = URIPATTERN - .captures(req.uri().path()) + .captures(path.as_ref()) .ok_or(err_msg("Did not match url pattern"))?; let name = captures .name("name") @@ -70,22 +62,41 @@ fn service_fn(req: &Request) -> Result, Error> { let hook = hooks .get(name) .ok_or(err_msg(format!("Hook '{}' doesn't exist", name)))?; - let req_obj = json!({ - "method": req.method().as_str(), - }); + + let req_obj = { + let headers = req + .headers() + .clone() + .into_iter() + .filter_map(|(k, v)| { + let key = k.unwrap().as_str().to_owned(); + v.to_str().map(|value| (key, value.to_owned())).ok() + }).collect::>(); + let method = req.method().as_str().to_owned(); + // probably not idiomatically the best way to do it + // i was just trying to get something working + let body = "wip".to_owned(); + json!({ + "body": body, + "headers": headers, + "method": method, + }) + }; hook.iter() - .fold(Ok(req_obj), |prev, handler| handler.run(prev)) - .map(|_| Response::new(Body::from("success"))) + .fold(Ok(req_obj), |prev, handler| { + prev.and_then(|val| handler.run(val)) + }).map(|_| Response::new(Body::from("success"))) } fn service_fn_wrapper(req: Request) -> Response { - match service_fn(&req) { - Ok(response) => response, - Err(err) => { - eprintln!("Got error from '{}': {}", req.uri().path(), err); - Response::new(Body::from(NOTFOUND)) - } - } + let uri = req.uri().path().to_owned(); + service_fn(req).unwrap_or_else(|err| { + eprintln!("Error from '{}': {}", uri, err); + Response::builder() + .status(StatusCode::NOT_FOUND) + .body(Body::from(NOTFOUND)) + .unwrap() + }) } fn load_config

(root: P) @@ -96,6 +107,7 @@ where // hold on to the lock while config is being reloaded { let mut programs = PROGRAMS.lock().unwrap(); + // TODO: some kind of smart diff programs.clear(); let programs_dir = { let mut p = root.as_ref().to_path_buf(); @@ -168,6 +180,7 @@ where match rx.recv() { Ok(_) => { // for now, naively reload entire config every time + // TODO: don't do this load_config(root.as_ref()) } Err(e) => println!("watch error: {:?}", e),