From dc5abd6f3b56c5dbc71aa8bb5dcfc667eb9abf0d Mon Sep 17 00:00:00 2001 From: Matthias Ahouansou Date: Sat, 8 Mar 2025 11:26:17 +0000 Subject: [PATCH] feat(appservice): pinging --- src/api/client_server/appservice.rs | 80 +++++++++++++++++++++++++++++ src/api/client_server/mod.rs | 2 + src/main.rs | 1 + src/utils/error.rs | 2 + 4 files changed, 85 insertions(+) create mode 100644 src/api/client_server/appservice.rs diff --git a/src/api/client_server/appservice.rs b/src/api/client_server/appservice.rs new file mode 100644 index 00000000..1b525f7c --- /dev/null +++ b/src/api/client_server/appservice.rs @@ -0,0 +1,80 @@ +use std::time::Instant; + +use ruma::api::{ + appservice::ping::send_ping, + client::{appservice::request_ping, error::ErrorKind}, +}; + +use crate::{api::appservice_server, Error, Result, Ruma}; + +/// # `POST /_matrix/client/v1/appservice/{appserviceId}/ping` +/// +/// Allows an appservice to check whether the server and +/// appservice can connect, and how fast their connection is +pub async fn ping_appservice_route( + body: Ruma, +) -> Result { + let Ruma:: { + appservice_info, + body, + .. + } = body; + + let registration = appservice_info + .expect("Only appservices can call this endpoint") + .registration; + + if registration.id != body.appservice_id { + return Err(Error::BadRequest( + ErrorKind::forbidden(), + "Appservice ID specified in path does not match the requesting access token", + )); + } + + if registration.url.is_some() { + let start = Instant::now(); + let response = appservice_server::send_request( + registration, + send_ping::v1::Request { + transaction_id: body.transaction_id, + }, + ) + .await; + let elapsed = start.elapsed(); + + if let Err(error) = response { + Err(match error { + Error::ReqwestError { source } => { + if source.is_timeout() { + Error::BadRequest( + ErrorKind::ConnectionTimeout, + "Connection to appservice timed-out", + ) + } else if let Some(status_code) = source.status() { + Error::BadRequest( + ErrorKind::BadStatus { + status: Some(status_code), + body: Some(source.to_string()), + }, + "Ping returned error status", + ) + } else { + Error::BadRequest(ErrorKind::ConnectionFailed, "Failed to ping appservice") + } + } + Error::BadServerResponse(_) => Error::BadRequest( + ErrorKind::ConnectionFailed, + "Recieved invalid response from appservice", + ), + e => e, + }) + } else { + Ok(request_ping::v1::Response::new(elapsed)) + } + } else { + Err(Error::BadRequest( + ErrorKind::UrlNotSet, + "Appservice doesn't have a URL configured", + )) + } +} diff --git a/src/api/client_server/mod.rs b/src/api/client_server/mod.rs index a35d7a98..e5d0a5d5 100644 --- a/src/api/client_server/mod.rs +++ b/src/api/client_server/mod.rs @@ -1,5 +1,6 @@ mod account; mod alias; +mod appservice; mod backup; mod capabilities; mod config; @@ -37,6 +38,7 @@ mod well_known; pub use account::*; pub use alias::*; +pub use appservice::*; pub use backup::*; pub use capabilities::*; pub use config::*; diff --git a/src/main.rs b/src/main.rs index 5669cc0a..48d082cc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -279,6 +279,7 @@ async fn unrecognized_method( fn routes(config: &Config) -> Router { let router = Router::new() + .ruma_route(client_server::ping_appservice_route) .ruma_route(client_server::get_supported_versions_route) .ruma_route(client_server::get_register_available_route) .ruma_route(client_server::register_route) diff --git a/src/utils/error.rs b/src/utils/error.rs index 50afd6d0..406db205 100644 --- a/src/utils/error.rs +++ b/src/utils/error.rs @@ -122,6 +122,8 @@ impl Error { LimitExceeded { .. } => StatusCode::TOO_MANY_REQUESTS, UserDeactivated => StatusCode::FORBIDDEN, TooLarge => StatusCode::PAYLOAD_TOO_LARGE, + ConnectionTimeout => StatusCode::GATEWAY_TIMEOUT, + BadStatus { .. } | ConnectionFailed => StatusCode::BAD_GATEWAY, _ => StatusCode::BAD_REQUEST, }, ),