Page MenuHomePhabricator

D12935.diff
No OneTemporary

D12935.diff

diff --git a/lib/types/tunnelbroker/notif-types.js b/lib/types/tunnelbroker/notif-types.js
--- a/lib/types/tunnelbroker/notif-types.js
+++ b/lib/types/tunnelbroker/notif-types.js
@@ -23,7 +23,15 @@
+payload: string,
};
+export type TunnelbrokerWNSNotif = {
+ +type: 'WNSNotif',
+ +clientMessageID: string,
+ +deviceID: string,
+ +payload: string,
+};
+
export type TunnelbrokerNotif =
| TunnelbrokerAPNsNotif
| TunnelbrokerFCMNotif
- | TunnelbrokerWebPushNotif;
+ | TunnelbrokerWebPushNotif
+ | TunnelbrokerWNSNotif;
diff --git a/services/tunnelbroker/src/notifs/mod.rs b/services/tunnelbroker/src/notifs/mod.rs
--- a/services/tunnelbroker/src/notifs/mod.rs
+++ b/services/tunnelbroker/src/notifs/mod.rs
@@ -14,7 +14,7 @@
APNs,
FCM,
WebPush,
- WNs,
+ WNS,
}
impl NotifClientType {
@@ -25,7 +25,7 @@
}
NotifClientType::FCM => platform == Platform::Android,
NotifClientType::WebPush => platform == Platform::Web,
- NotifClientType::WNs => platform == Platform::Windows,
+ NotifClientType::WNS => platform == Platform::Windows,
}
}
}
diff --git a/services/tunnelbroker/src/notifs/wns/error.rs b/services/tunnelbroker/src/notifs/wns/error.rs
--- a/services/tunnelbroker/src/notifs/wns/error.rs
+++ b/services/tunnelbroker/src/notifs/wns/error.rs
@@ -1,15 +1,29 @@
use derive_more::{Display, Error, From};
+use super::response::WNSErrorResponse;
+
#[derive(Debug, From, Display, Error)]
pub enum Error {
Reqwest(reqwest::Error),
SerdeJson(serde_json::Error),
- #[display(fmt = "Token not found in response")]
- TokenNotFound,
- #[display(fmt = "Expiry time not found in response")]
- ExpiryNotFound,
+ #[display(fmt = "WNS Token Error: {}", _0)]
+ WNSToken(WNSTokenError),
#[display(fmt = "Failed to acquire read lock")]
ReadLock,
#[display(fmt = "Failed to acquire write lock")]
WriteLock,
+ #[display(fmt = "WNS Notification Error: {}", _0)]
+ WNSNotification(WNSErrorResponse),
+}
+
+#[derive(Debug, From, Display)]
+pub enum WNSTokenError {
+ #[display(fmt = "Token not found in response")]
+ TokenNotFound,
+ #[display(fmt = "Expiry time not found in response")]
+ ExpiryNotFound,
+ #[display(fmt = "Unknown Error: {}", _0)]
+ Unknown(String),
}
+
+impl std::error::Error for WNSTokenError {}
diff --git a/services/tunnelbroker/src/notifs/wns/mod.rs b/services/tunnelbroker/src/notifs/wns/mod.rs
--- a/services/tunnelbroker/src/notifs/wns/mod.rs
+++ b/services/tunnelbroker/src/notifs/wns/mod.rs
@@ -1,4 +1,7 @@
use crate::notifs::wns::config::WNSConfig;
+use error::WNSTokenError;
+use reqwest::StatusCode;
+use response::WNSErrorResponse;
use std::{
sync::{Arc, RwLock},
time::{Duration, SystemTime},
@@ -6,6 +9,7 @@
pub mod config;
mod error;
+mod response;
#[derive(Debug, Clone)]
pub struct WNSAccessToken {
@@ -13,6 +17,12 @@
expires: SystemTime,
}
+#[derive(Debug, Clone)]
+pub struct WNSNotif {
+ pub device_token: String,
+ pub payload: String,
+}
+
#[derive(Clone)]
pub struct WNSClient {
http_client: reqwest::Client,
@@ -30,9 +40,45 @@
})
}
- pub async fn get_wns_token(
- &mut self,
- ) -> Result<Option<String>, error::Error> {
+ pub async fn send(&self, notif: WNSNotif) -> Result<(), error::Error> {
+ let wns_access_token = self.get_wns_token().await?;
+
+ let url = notif.device_token;
+
+ // Send the notification
+ let response = self
+ .http_client
+ .post(&url)
+ .header("Content-Type", "application/octet-stream")
+ .header("X-WNS-Type", "wns/raw")
+ .bearer_auth(wns_access_token)
+ .body(notif.payload)
+ .send()
+ .await?;
+
+ match response.status() {
+ StatusCode::OK => {
+ tracing::debug!("Successfully sent WNS notif to {}", &url);
+ Ok(())
+ }
+ error_status => {
+ let body = response
+ .text()
+ .await
+ .unwrap_or_else(|error| format!("Error occurred: {}", error));
+ tracing::error!(
+ "Failed sending WNS notification to: {}. Status: {}. Body: {}",
+ &url,
+ error_status,
+ body
+ );
+ let wns_error = WNSErrorResponse::from_status(error_status, body);
+ Err(error::Error::WNSNotification(wns_error))
+ }
+ }
+ }
+
+ pub async fn get_wns_token(&self) -> Result<String, error::Error> {
const EXPIRY_WINDOW: Duration = Duration::from_secs(10);
{
@@ -42,7 +88,7 @@
.map_err(|_| error::Error::ReadLock)?;
if let Some(ref token) = *read_guard {
if token.expires >= SystemTime::now() - EXPIRY_WINDOW {
- return Ok(Some(token.token.clone()));
+ return Ok(token.token.clone());
}
}
}
@@ -68,17 +114,17 @@
.await
.unwrap_or_else(|_| String::from("<failed to read body>"));
tracing::error!(status, "Failure when getting the WNS token: {}", body);
- return Ok(None);
+ return Err(error::Error::WNSToken(WNSTokenError::Unknown(status)));
}
let response_json: serde_json::Value = response.json().await?;
let token = response_json["access_token"]
.as_str()
- .ok_or(error::Error::TokenNotFound)?
+ .ok_or(error::Error::WNSToken(WNSTokenError::TokenNotFound))?
.to_string();
let expires_in = response_json["expires_in"]
.as_u64()
- .ok_or(error::Error::ExpiryNotFound)?;
+ .ok_or(error::Error::WNSToken(WNSTokenError::ExpiryNotFound))?;
let expires = SystemTime::now() + Duration::from_secs(expires_in);
@@ -92,6 +138,6 @@
expires,
});
}
- Ok(Some(token))
+ Ok(token)
}
}
diff --git a/services/tunnelbroker/src/notifs/wns/response.rs b/services/tunnelbroker/src/notifs/wns/response.rs
new file mode 100644
--- /dev/null
+++ b/services/tunnelbroker/src/notifs/wns/response.rs
@@ -0,0 +1,77 @@
+use derive_more::{Display, Error};
+use reqwest::StatusCode;
+
+#[derive(PartialEq, Debug, Clone, Display, Error)]
+pub struct InvalidArgumentError {
+ pub details: String,
+}
+
+#[derive(PartialEq, Debug, Display, Error)]
+pub enum WNSErrorResponse {
+ /// No more information is available about this error.
+ UnspecifiedError,
+
+ /// HTTP error code = 400.
+ /// One or more headers were specified incorrectly or conflict with another
+ /// header.
+ BadRequest(InvalidArgumentError),
+
+ /// HTTP error code = 401.
+ /// The cloud service did not present a valid authentication ticket.
+ Unauthorized,
+
+ /// HTTP error code = 403.
+ /// The cloud service is not authorized to send a notification to this URI.
+ Forbidden,
+
+ /// HTTP error code = 404.
+ /// The channel URI is not valid or is not recognized by WNS.
+ NotFound,
+
+ /// HTTP error code = 405.
+ /// Invalid method (GET, CREATE); only POST (Windows or Windows Phone) or
+ /// DELETE (Windows Phone only) is allowed.
+ MethodNotAllowed,
+
+ /// HTTP error code = 406.
+ /// The cloud service exceeded its throttle limit.
+ NotAcceptable,
+
+ /// HTTP error code = 410.
+ /// The channel expired.
+ Gone,
+
+ /// HTTP error code = 413.
+ /// The notification payload exceeds the 5000 byte size limit.
+ RequestEntityTooLarge,
+
+ /// HTTP error code = 500.
+ /// An internal failure caused notification delivery to fail.
+ InternalServerError,
+
+ /// HTTP error code = 503.
+ /// The server is currently unavailable.
+ ServiceUnavailable,
+}
+
+impl WNSErrorResponse {
+ pub fn from_status(status: StatusCode, body: String) -> Self {
+ match status {
+ StatusCode::BAD_REQUEST => {
+ WNSErrorResponse::BadRequest(InvalidArgumentError { details: body })
+ }
+ StatusCode::UNAUTHORIZED => WNSErrorResponse::Unauthorized,
+ StatusCode::FORBIDDEN => WNSErrorResponse::Forbidden,
+ StatusCode::NOT_FOUND => WNSErrorResponse::NotFound,
+ StatusCode::METHOD_NOT_ALLOWED => WNSErrorResponse::MethodNotAllowed,
+ StatusCode::NOT_ACCEPTABLE => WNSErrorResponse::NotAcceptable,
+ StatusCode::GONE => WNSErrorResponse::Gone,
+ StatusCode::PAYLOAD_TOO_LARGE => WNSErrorResponse::RequestEntityTooLarge,
+ StatusCode::INTERNAL_SERVER_ERROR => {
+ WNSErrorResponse::InternalServerError
+ }
+ StatusCode::SERVICE_UNAVAILABLE => WNSErrorResponse::ServiceUnavailable,
+ _ => WNSErrorResponse::UnspecifiedError,
+ }
+ }
+}
diff --git a/services/tunnelbroker/src/websockets/session.rs b/services/tunnelbroker/src/websockets/session.rs
--- a/services/tunnelbroker/src/websockets/session.rs
+++ b/services/tunnelbroker/src/websockets/session.rs
@@ -40,6 +40,7 @@
AndroidConfig, AndroidMessagePriority, FCMMessage,
};
use crate::notifs::web_push::WebPushNotif;
+use crate::notifs::wns::WNSNotif;
use crate::notifs::{apns, NotifClient, NotifClientType};
use crate::{identity, notifs};
@@ -77,6 +78,7 @@
MissingAPNsClient,
MissingFCMClient,
MissingWebPushClient,
+ MissingWNSClient,
MissingDeviceToken,
InvalidDeviceToken,
InvalidNotifProvider,
@@ -581,6 +583,46 @@
self.get_message_to_device_status(&notif.client_message_id, result),
)
}
+ DeviceToTunnelbrokerMessage::WNSNotif(notif) => {
+ if !self.device_info.is_authenticated {
+ debug!(
+ "Unauthenticated device {} tried to send WNS notif. Aborting.",
+ self.device_info.device_id
+ );
+ return Some(MessageSentStatus::Unauthenticated);
+ }
+ debug!("Received WNS notif for {}", notif.device_id);
+
+ let Some(wns_client) = self.notif_client.wns.clone() else {
+ return Some(self.get_message_to_device_status(
+ &notif.client_message_id,
+ Err(SessionError::MissingWNSClient),
+ ));
+ };
+
+ let device_token = match self
+ .get_device_token(notif.device_id, NotifClientType::WNS)
+ .await
+ {
+ Ok(token) => token,
+ Err(e) => {
+ return Some(
+ self
+ .get_message_to_device_status(&notif.client_message_id, Err(e)),
+ )
+ }
+ };
+
+ let wns_notif = WNSNotif {
+ device_token,
+ payload: notif.payload,
+ };
+
+ let result = wns_client.send(wns_notif).await;
+ Some(
+ self.get_message_to_device_status(&notif.client_message_id, result),
+ )
+ }
_ => {
error!("Client sent invalid message type");
Some(MessageSentStatus::InvalidRequest)
diff --git a/shared/tunnelbroker_messages/src/messages/mod.rs b/shared/tunnelbroker_messages/src/messages/mod.rs
--- a/shared/tunnelbroker_messages/src/messages/mod.rs
+++ b/shared/tunnelbroker_messages/src/messages/mod.rs
@@ -46,6 +46,7 @@
APNsNotif(APNsNotif),
FCMNotif(FCMNotif),
WebPushNotif(WebPushNotif),
+ WNSNotif(WNSNotif),
MessageToDeviceRequest(MessageToDeviceRequest),
MessageReceiveConfirmation(MessageReceiveConfirmation),
MessageToTunnelbrokerRequest(MessageToTunnelbrokerRequest),
diff --git a/shared/tunnelbroker_messages/src/messages/notif.rs b/shared/tunnelbroker_messages/src/messages/notif.rs
--- a/shared/tunnelbroker_messages/src/messages/notif.rs
+++ b/shared/tunnelbroker_messages/src/messages/notif.rs
@@ -35,3 +35,14 @@
pub device_id: String,
pub payload: String,
}
+
+/// WNS notif built on client.
+#[derive(Serialize, Deserialize, TagAwareDeserialize, PartialEq, Debug)]
+#[serde(tag = "type", remote = "Self", rename_all = "camelCase")]
+pub struct WNSNotif {
+ #[serde(rename = "clientMessageID")]
+ pub client_message_id: String,
+ #[serde(rename = "deviceID")]
+ pub device_id: String,
+ pub payload: String,
+}

File Metadata

Mime Type
text/plain
Expires
Sun, Dec 22, 6:56 AM (6 h, 7 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2690507
Default Alt Text
D12935.diff (11 KB)

Event Timeline