diff --git a/services/tunnelbroker/src/notifs/apns/error.rs b/services/tunnelbroker/src/notifs/apns/error.rs --- a/services/tunnelbroker/src/notifs/apns/error.rs +++ b/services/tunnelbroker/src/notifs/apns/error.rs @@ -1,3 +1,4 @@ +use crate::notifs::apns::response::ErrorBody; use derive_more::{Display, Error, From}; #[derive(Debug, From, Display, Error)] @@ -5,6 +6,8 @@ JWTError, ReqwestError(reqwest::Error), InvalidHeaderValue(reqwest::header::InvalidHeaderValue), + SerdeJson(serde_json::Error), + ResponseError(ErrorBody), } impl From for Error { diff --git a/services/tunnelbroker/src/notifs/apns/mod.rs b/services/tunnelbroker/src/notifs/apns/mod.rs --- a/services/tunnelbroker/src/notifs/apns/mod.rs +++ b/services/tunnelbroker/src/notifs/apns/mod.rs @@ -1,8 +1,15 @@ use crate::notifs::apns::config::APNsConfig; +use crate::notifs::apns::error::Error::ResponseError; use crate::notifs::apns::headers::{NotificationHeaders, PushType}; +use crate::notifs::apns::response::ErrorBody; use crate::notifs::apns::token::APNsToken; use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION}; +use reqwest::StatusCode; +use serde::{Deserialize, Serialize}; +use serde_json::Value; use std::time::Duration; +use tracing::debug; + pub mod config; pub mod error; mod headers; @@ -16,6 +23,13 @@ is_prod: bool, } +#[derive(Serialize, Deserialize)] +pub struct APNsNotif { + pub device_token: String, + pub headers: NotificationHeaders, + pub payload: String, +} + impl APNsClient { pub fn new(config: &APNsConfig) -> Result { let token_ttl = Duration::from_secs(60 * 55); @@ -86,4 +100,39 @@ Ok(headers) } + + fn get_endpoint(&self) -> &'static str { + if self.is_prod { + return "api.push.apple.com"; + } + "api.development.push.apple.com" + } + + pub async fn send(&self, notif: APNsNotif) -> Result<(), error::Error> { + debug!("Sending notif to {}", notif.device_token); + + let headers = self.build_headers(notif.headers.clone()).await?; + + let url = format!( + "https://{}/3/device/{}", + self.get_endpoint(), + notif.device_token + ); + + let response = self + .http2_client + .post(url) + .headers(headers.clone()) + .body(notif.payload) + .send() + .await?; + + match response.status() { + StatusCode::OK => Ok(()), + _ => { + let error_body: ErrorBody = response.json().await?; + Err(ResponseError(error_body)) + } + } + } }