matrixclient-testsuite

A TestSuite for Matrix Clients like sytest for Servers (Mainly written for Daydream)
git clone git://archive.git.mtrnord.blog/MTRNord/matrixclient-testsuite.git
Log | Files | Refs

ruma_wrapper.rs (6106B)


      1 /// From https://git.koesters.xyz/timo/conduit/src/commit/88d091fca1c1db9f238c7a42467f63a619201f8c/src/ruma_wrapper.rs
      2 use crate::utils;
      3 use log::warn;
      4 use rocket::{
      5     data::{Data, FromData, FromDataFuture, Transform, TransformFuture, Transformed},
      6     http::Status,
      7     response::{self, Responder},
      8     Outcome::*,
      9     Request, State,
     10 };
     11 use ruma::{api::Endpoint, identifiers::UserId};
     12 use std::{convert::TryInto, io::Cursor, ops::Deref};
     13 use tokio::io::AsyncReadExt;
     14 
     15 const MESSAGE_LIMIT: u64 = 20 * 1024 * 1024; // 20 MB
     16 
     17 /// This struct converts rocket requests into ruma structs by converting them into http requests
     18 /// first.
     19 pub struct Ruma<T> {
     20     pub body: T,
     21     pub user_id: Option<UserId>,
     22     pub device_id: Option<String>,
     23     pub json_body: Option<Box<serde_json::value::RawValue>>, // This is None when body is not a valid string
     24 }
     25 
     26 impl<'a, T: Endpoint> FromData<'a> for Ruma<T> {
     27     type Error = (); // TODO: Better error handling
     28     type Owned = Data;
     29     type Borrowed = Self::Owned;
     30 
     31     fn transform<'r>(
     32         _req: &'r Request<'_>,
     33         data: Data,
     34     ) -> TransformFuture<'r, Self::Owned, Self::Error> {
     35         Box::pin(async move { Transform::Owned(Success(data)) })
     36     }
     37 
     38     fn from_data(
     39         request: &'a Request<'_>,
     40         outcome: Transformed<'a, Self>,
     41     ) -> FromDataFuture<'a, Self, Self::Error> {
     42         Box::pin(async move {
     43             let data = rocket::try_outcome!(outcome.owned());
     44 
     45             let (user_id, device_id) = if T::METADATA.requires_authentication {
     46                 let db = request
     47                     .guard::<State<'_, crate::data::Data>>()
     48                     .await
     49                     .unwrap();
     50 
     51                 // Get token from header or query value
     52                 let token = match request
     53                     .headers()
     54                     .get_one("Authorization")
     55                     .map(|s| s[7..].to_owned()) // Split off "Bearer "
     56                     .or_else(|| request.get_query_value("access_token").and_then(|r| r.ok()))
     57                 {
     58                     // TODO: M_MISSING_TOKEN
     59                     None => return Failure((Status::Unauthorized, ())),
     60                     Some(token) => token,
     61                 };
     62 
     63                 // Check if token is valid
     64                 match db.users.find_from_token(&token).unwrap() {
     65                     // TODO: M_UNKNOWN_TOKEN
     66                     None => return Failure((Status::Unauthorized, ())),
     67                     Some((user_id, device_id)) => (Some(user_id), Some(device_id)),
     68                 }
     69             } else {
     70                 (None, None)
     71             };
     72 
     73             let mut http_request = http::Request::builder()
     74                 .uri(request.uri().to_string())
     75                 .method(&*request.method().to_string());
     76             for header in request.headers().iter() {
     77                 http_request = http_request.header(header.name.as_str(), &*header.value);
     78             }
     79 
     80             let mut handle = data.open().take(MESSAGE_LIMIT);
     81             let mut body = Vec::new();
     82             handle.read_to_end(&mut body).await.unwrap();
     83 
     84             let http_request = http_request.body(body.clone()).unwrap();
     85             log::info!("{:?}", http_request);
     86 
     87             match T::try_from(http_request) {
     88                 Ok(t) => Success(Ruma {
     89                     body: t,
     90                     user_id,
     91                     device_id,
     92                     // TODO: Can we avoid parsing it again? (We only need this for append_pdu)
     93                     json_body: utils::string_from_bytes(&body)
     94                         .ok()
     95                         .and_then(|s| serde_json::value::RawValue::from_string(s).ok()),
     96                 }),
     97                 Err(e) => {
     98                     warn!("{:?}", e);
     99                     Failure((Status::BadRequest, ()))
    100                 }
    101             }
    102         })
    103     }
    104 }
    105 
    106 impl<T> Deref for Ruma<T> {
    107     type Target = T;
    108 
    109     fn deref(&self) -> &Self::Target {
    110         &self.body
    111     }
    112 }
    113 
    114 /// This struct converts ruma responses into rocket http responses.
    115 pub struct MatrixResult<T, E = ruma::api::client::Error>(pub std::result::Result<T, E>);
    116 
    117 impl<T, E> TryInto<http::Response<Vec<u8>>> for MatrixResult<T, E>
    118 where
    119     T: TryInto<http::Response<Vec<u8>>>,
    120     E: Into<http::Response<Vec<u8>>>,
    121 {
    122     type Error = T::Error;
    123 
    124     fn try_into(self) -> Result<http::Response<Vec<u8>>, T::Error> {
    125         match self.0 {
    126             Ok(t) => t.try_into(),
    127             Err(e) => Ok(e.into()),
    128         }
    129     }
    130 }
    131 
    132 #[rocket::async_trait]
    133 impl<'r, T, E> Responder<'r> for MatrixResult<T, E>
    134 where
    135     T: Send + TryInto<http::Response<Vec<u8>>>,
    136     T::Error: Send,
    137     E: Into<http::Response<Vec<u8>>> + Send,
    138 {
    139     async fn respond_to(self, _: &'r Request<'_>) -> response::Result<'r> {
    140         let http_response: Result<http::Response<_>, _> = self.try_into();
    141         match http_response {
    142             Ok(http_response) => {
    143                 let mut response = rocket::response::Response::build();
    144 
    145                 let status = http_response.status();
    146                 response.raw_status(status.into(), "");
    147 
    148                 for header in http_response.headers() {
    149                     response
    150                         .raw_header(header.0.to_string(), header.1.to_str().unwrap().to_owned());
    151                 }
    152 
    153                 response
    154                     .sized_body(Cursor::new(http_response.into_body()))
    155                     .await;
    156 
    157                 response.raw_header("Access-Control-Allow-Origin", "*");
    158                 response.raw_header(
    159                     "Access-Control-Allow-Methods",
    160                     "GET, POST, PUT, DELETE, OPTIONS",
    161                 );
    162                 response.raw_header(
    163                     "Access-Control-Allow-Headers",
    164                     "Origin, X-Requested-With, Content-Type, Accept, Authorization",
    165                 );
    166                 response.ok()
    167             }
    168             Err(_) => Err(Status::InternalServerError),
    169         }
    170     }
    171 }
    172 
    173 impl<T, E> Deref for MatrixResult<T, E> {
    174     type Target = Result<T, E>;
    175 
    176     fn deref(&self) -> &Self::Target {
    177         &self.0
    178     }
    179 }