tuwunel_api_client/rendezvous/msc4388/
put.rs1use std::result::Result as StdResult;
2
3use axum::{
4 body::Body,
5 extract::{Path, State, rejection::PathRejection},
6};
7use bytes::Bytes;
8use ruma::{
9 OwnedTransactionId, TransactionId,
10 api::client::rendezvous::update_rendezvous_session::unstable::Response,
11};
12use serde::Deserialize;
13use serde_json::{Value as JsonValue, from_slice, from_value};
14use tuwunel_core::{Err, Error as CoreError, Result as CoreResult, err, smallstr::SmallString};
15use tuwunel_service::{
16 Services,
17 rendezvous::{Attempt, Meta, Put},
18};
19
20use super::{Error, Result, ensure_available, ensure_data_size, ensure_txn_id};
21use crate::{ClientIp, RumaResponse, router::read_body};
22
23type Id = SmallString<[u8; 32]>;
24type SequenceToken = SmallString<[u8; 43]>;
25type PathParams = (Id, OwnedTransactionId);
26
27#[derive(Deserialize)]
28struct SendBody {
29 sequence_token: SequenceToken,
30 data: String,
31}
32
33#[tracing::instrument(level = "debug", skip_all)]
34pub(crate) async fn put_msc4388_route(
35 State(services): State<crate::State>,
36 ClientIp(client): ClientIp,
37 path: StdResult<Path<PathParams>, PathRejection>,
38 body: Body,
39) -> Result<RumaResponse<Response>> {
40 ensure_available(&services, client)?;
41
42 let Path((id, txn_id)) = path.map_err(CoreError::from)?;
43
44 ensure_txn_id(&txn_id)?;
45
46 let meta = match services.rendezvous.recorded(&id, &txn_id) {
49 | Some(meta) => meta,
50 | None => send(&services, &id, &txn_id, body).await?,
51 };
52
53 let sequence_token = meta.sequence_token().to_owned();
54
55 Ok(RumaResponse(Response { sequence_token }))
56}
57
58async fn send(services: &Services, id: &str, txn_id: &TransactionId, body: Body) -> Result<Meta> {
59 let SendBody { sequence_token, data } = parse_body(services, body).await?;
60
61 ensure_data_size(services, &data)?;
62
63 let attempt = Attempt { txn_id, sequence_token: &sequence_token };
64
65 match services
66 .rendezvous
67 .put_token(id, attempt, Bytes::from(data))
68 {
69 | Put::NotFound =>
70 Err!(Request(NotFound("Rendezvous session not found"))).map_err(Into::into),
71 | Put::PreconditionFailed(_) => Err(Error::ConcurrentWrite),
72 | Put::Accepted(meta) => Ok(meta),
73 }
74}
75
76async fn parse_body(services: &Services, body: Body) -> CoreResult<SendBody> {
77 let body = read_body(services, body).await?;
78
79 let json: JsonValue = from_slice(&body)
82 .map_err(|_| err!(Request(NotJson("Request body is not valid JSON."))))?;
83
84 from_value(json).map_err(|e| err!(Request(BadJson("Request body is malformed: {e}"))))
85}