use futures::stream::Stream;
use futures::{future, Future};
use hyper::service::{NewService, Service};
use hyper::{Body, Error, Request, Response, StatusCode};
use super::Constructor;
use super::ContentType;
use super::Delivery;
use super::DeliveryType;
use super::Handler;
macro_rules! hyper_get_header_value {
($headers:expr, $key:expr) => {
if let Some(value) = $headers.get($key) {
if let Ok(inner) = value.to_str() {
Some(String::from(inner.clone()))
} else {
None
}
} else {
None
}
};
}
impl NewService for Constructor {
type ReqBody = Body;
type ResBody = Body;
type Error = Error;
type Service = Handler;
type Future = Box<Future<Item = Self::Service, Error = Self::InitError> + Send>;
type InitError = Error;
fn new_service(&self) -> Self::Future {
debug!("Creating new service");
Box::new(future::ok(Handler::from(self)))
}
}
impl Service for Handler {
type ReqBody = Body;
type ResBody = Body;
type Error = Error;
type Future = Box<Future<Item = Response<Body>, Error = Error> + Send + 'static>;
fn call(&mut self, req: Request<Self::ReqBody>) -> Self::Future {
fn response(status_code: StatusCode, body: &'static str) -> Response<Body> {
Response::builder()
.status(status_code)
.body(body.into())
.unwrap()
}
let headers = req.headers();
let (mut event, delivery_type) =
if let Some(event_string) = hyper_get_header_value!(&headers, "X-Github-Event") {
(event_string.clone(), DeliveryType::GitHub)
} else if let Some(event_string) = hyper_get_header_value!(&headers, "X-Gitlab-Event") {
(event_string.clone(), DeliveryType::GitLab)
} else {
return Box::new(future::ok(response(
StatusCode::ACCEPTED,
"Invalid payload",
)));
};
event.make_ascii_lowercase();
event = event.replace(" ", "_");
let executor = self.get_hooks(event.as_str());
if executor.is_empty() {
return Box::new(future::ok(response(
StatusCode::ACCEPTED,
"No matched hook configured",
)));
}
let id = hyper_get_header_value!(&headers, "X-Github-Delivery");
let signature = match delivery_type {
DeliveryType::GitHub => hyper_get_header_value!(&headers, "X-Hub-Signature"),
DeliveryType::GitLab => hyper_get_header_value!(&headers, "X-Gitlab-Token"),
};
let content_type = hyper_get_header_value!(&headers, "content-type");
if content_type.is_none() {
return Box::new(future::ok(response(
StatusCode::ACCEPTED,
"Invalid payload",
)));
}
Box::new(
req.into_body()
.concat2()
.map(move |chunk| String::from_utf8(chunk.to_vec()).ok())
.and_then(move |request_body| {
let content_type: ContentType = match content_type.unwrap().as_str() {
"application/x-www-form-urlencoded" => ContentType::URLENCODED,
_ => ContentType::JSON, };
if request_body.is_some() {
let delivery = Delivery::new(
delivery_type,
id,
Some(event),
signature,
content_type,
request_body,
);
debug!("Received delivery: {:#?}", &delivery);
executor.run(delivery);
future::ok(response(StatusCode::OK, "OK"))
} else {
future::ok(response(StatusCode::ACCEPTED, "Invalid payload"))
}
}),
)
}
}