diff options
| author | Matthieu <20992787+MatthieuCoder@users.noreply.github.com> | 2021-09-26 01:24:34 +0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-09-26 01:24:34 +0400 |
| commit | fe11cf23da7e996613b1d8df503c2a085ac40d31 (patch) | |
| tree | e08ed094a63b14cc79975c1f11492a477970f0d3 /webhook/src/handler/handler.rs | |
| parent | f2e6047c21b3ee814670b17e5901d12ac52a3508 (diff) | |
| parent | 4ad3510c0552aa8b65866590873c7b13bc2d5243 (diff) | |
Merge pull request #6 from discordnova/webhook-receiver
Webhook receiver
Diffstat (limited to 'webhook/src/handler/handler.rs')
| -rw-r--r-- | webhook/src/handler/handler.rs | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/webhook/src/handler/handler.rs b/webhook/src/handler/handler.rs new file mode 100644 index 0000000..b993aaa --- /dev/null +++ b/webhook/src/handler/handler.rs @@ -0,0 +1,126 @@ +use super::{signature::validate_signature, types::Interaction}; +use crate::config::Config; +use hyper::{Body, Method, Request, Response, StatusCode, body::{to_bytes, Bytes}, service::Service}; +use log::{error, info, trace}; +use nats::Connection; +use serde::{Deserialize, Serialize}; +use std::{future::Future, io::{Error, ErrorKind}, pin::Pin, str::from_utf8, sync::Arc, task::{Context, Poll}}; + +/// Hyper service used to handle the discord webhooks +#[derive(Clone)] +pub struct HandlerService { + pub config: Config, + pub nats: Arc<Connection>, +} + +impl HandlerService { + async fn check_request(&self, req: Request<Body>) -> Result<Bytes, Error> { + if req.method() == Method::POST { + let headers = req.headers().clone(); + let signature = headers.get("X-Signature-Ed25519"); + let timestamp = headers.get("X-Signature-Timestamp"); + if let (Some(timestamp), Some(signature)) = (timestamp, signature) { + if let Ok(data) = to_bytes(req.into_body()).await { + let contatenated_data = [timestamp.as_bytes().to_vec(), data.to_vec()].concat(); + if let Ok(signature_str) = &signature.to_str() { + if validate_signature( + &self.config.discord.public_key, + &contatenated_data, + signature_str, + ) { + Ok(data) + } else { + Err(Error::new( + ErrorKind::InvalidData, + "invalid signature specified", + )) + } + } else { + Err(Error::new( + ErrorKind::BrokenPipe, + "failed to read signature", + )) + } + } else { + Err(Error::new(ErrorKind::BrokenPipe, "unable to read body")) + } + } else { + Err(Error::new(ErrorKind::InvalidData, "missing headers")) + } + } else { + Err(Error::new(ErrorKind::InvalidData, "invalid method")) + } + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Ping { + #[serde(rename = "type")] + t: i32 +} + +/// Implementation of the service +impl Service<Request<Body>> for HandlerService { + type Response = Response<Body>; + type Error = hyper::Error; + type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>; + + fn poll_ready(&mut self, _: &mut Context) -> Poll<Result<(), Self::Error>> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, req: Request<Body>) -> Self::Future { + let self_clone = self.clone(); + + Box::pin(async move { + match self_clone.check_request(req).await { + Ok(data) => { + let value: Interaction = serde_json::from_str(from_utf8(&data).unwrap()).unwrap(); + trace!("received value: {:?}", value); + + match value.t { + 1 => { + info!("sending pong"); + // a ping must be responded with another ping + return Ok(Response::builder().header("Content-Type", "application/json").body(serde_json::to_string(&Ping { + t: 1 + }).unwrap().into()).unwrap()); + }, + _ => { + let payload = serde_json::to_string(&common::payloads::CachePayload { + tracing: common::payloads::Tracing { + node_id: "".to_string(), + span: None, + }, + data: value, + }).unwrap(); + + match self_clone.nats.request("nova.cache.dispatch.interaction", payload) { + Ok(response) => { + Ok( + Response::builder() + .header("Content-Type", "application/json") + .body(from_utf8(&response.data).unwrap().to_string().into()) + .unwrap() + ) + }, + Err(error) => { + error!("failed to request nats: {}", error); + Ok( + Response::builder() + .status(500) + .body("an internal server error occured".to_string().into()) + .unwrap() + ) + } + } + }, + } + }, + Err(error) => { + Ok(Response::builder().status(StatusCode::UNAUTHORIZED).body(error.to_string().into()).unwrap()) + } + } + }) + } +} |
