Skip to main content

gosuto_livekit/room/participant/
rpc.rs

1// Copyright 2025 LiveKit, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::room::participant::ParticipantIdentity;
16use livekit_protocol::RpcError as RpcError_Proto;
17use std::{error::Error, fmt::Display, time::Duration};
18
19/// Parameters for performing an RPC call
20#[derive(Debug, Clone)]
21pub struct PerformRpcData {
22    pub destination_identity: String,
23    pub method: String,
24    pub payload: String,
25    pub response_timeout: Duration,
26}
27
28impl Default for PerformRpcData {
29    fn default() -> Self {
30        Self {
31            destination_identity: Default::default(),
32            method: Default::default(),
33            payload: Default::default(),
34            response_timeout: Duration::from_secs(15),
35        }
36    }
37}
38
39/// Data passed to method handler for incoming RPC invocations
40///
41/// Attributes:
42///     request_id (String): The unique request ID. Will match at both sides of the call, useful for debugging or logging.
43///     caller_identity (ParticipantIdentity): The unique participant identity of the caller.
44///     payload (String): The payload of the request. User-definable format, typically JSON.
45///     response_timeout (Duration): The maximum time the caller will wait for a response.
46#[derive(Debug, Clone)]
47pub struct RpcInvocationData {
48    pub request_id: String,
49    pub caller_identity: ParticipantIdentity,
50    pub payload: String,
51    pub response_timeout: Duration,
52}
53
54/// Specialized error handling for RPC methods.
55///
56/// Instances of this type, when thrown in a method handler, will have their `message`
57/// serialized and sent across the wire. The caller will receive an equivalent error on the other side.
58///
59/// Build-in types are included but developers may use any string, with a max length of 256 bytes.
60#[derive(Debug, Clone)]
61pub struct RpcError {
62    pub code: u32,
63    pub message: String,
64    pub data: Option<String>,
65}
66
67impl RpcError {
68    pub const MAX_MESSAGE_BYTES: usize = 256;
69    pub const MAX_DATA_BYTES: usize = 15360; // 15 KB
70
71    /// Creates an error object with the given code and message, plus an optional data payload.
72    ///
73    /// If thrown in an RPC method handler, the error will be sent back to the caller.
74    ///
75    /// Error codes 1001-1999 are reserved for built-in errors (see RpcErrorCode for their meanings).
76    pub fn new(code: u32, message: String, data: Option<String>) -> Self {
77        Self {
78            code,
79            message: truncate_bytes(&message, Self::MAX_MESSAGE_BYTES),
80            data: data.map(|d| truncate_bytes(&d, Self::MAX_DATA_BYTES)),
81        }
82    }
83
84    pub fn from_proto(proto: RpcError_Proto) -> Self {
85        Self::new(proto.code, proto.message, Some(proto.data))
86    }
87
88    pub fn to_proto(&self) -> RpcError_Proto {
89        RpcError_Proto {
90            code: self.code,
91            message: self.message.clone(),
92            data: self.data.clone().unwrap_or_default(),
93        }
94    }
95}
96
97impl Display for RpcError {
98    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99        write!(f, "RPC Error: {} ({})", self.message, self.code)
100    }
101}
102impl Error for RpcError {}
103
104#[derive(Debug, Clone, Copy)]
105pub enum RpcErrorCode {
106    ApplicationError = 1500,
107    ConnectionTimeout = 1501,
108    ResponseTimeout = 1502,
109    RecipientDisconnected = 1503,
110    ResponsePayloadTooLarge = 1504,
111    SendFailed = 1505,
112
113    UnsupportedMethod = 1400,
114    RecipientNotFound = 1401,
115    RequestPayloadTooLarge = 1402,
116    UnsupportedServer = 1403,
117    UnsupportedVersion = 1404,
118}
119
120impl RpcErrorCode {
121    pub(crate) fn message(&self) -> &'static str {
122        match self {
123            Self::ApplicationError => "Application error in method handler",
124            Self::ConnectionTimeout => "Connection timeout",
125            Self::ResponseTimeout => "Response timeout",
126            Self::RecipientDisconnected => "Recipient disconnected",
127            Self::ResponsePayloadTooLarge => "Response payload too large",
128            Self::SendFailed => "Failed to send",
129
130            Self::UnsupportedMethod => "Method not supported at destination",
131            Self::RecipientNotFound => "Recipient not found",
132            Self::RequestPayloadTooLarge => "Request payload too large",
133            Self::UnsupportedServer => "RPC not supported by server",
134            Self::UnsupportedVersion => "Unsupported RPC version",
135        }
136    }
137}
138
139impl RpcError {
140    /// Creates an error object from the code, with an auto-populated message.
141    pub(crate) fn built_in(code: RpcErrorCode, data: Option<String>) -> Self {
142        Self::new(code as u32, code.message().to_string(), data)
143    }
144}
145
146/// Maximum payload size in bytes
147pub const MAX_PAYLOAD_BYTES: usize = 15360; // 15 KB
148
149/// Calculate the byte length of a string
150pub(crate) fn byte_length(s: &str) -> usize {
151    s.as_bytes().len()
152}
153
154/// Truncate a string to a maximum number of bytes
155pub(crate) fn truncate_bytes(s: &str, max_bytes: usize) -> String {
156    if byte_length(s) <= max_bytes {
157        return s.to_string();
158    }
159
160    let mut result = String::new();
161    for c in s.chars() {
162        if byte_length(&(result.clone() + &c.to_string())) > max_bytes {
163            break;
164        }
165        result.push(c);
166    }
167    result
168}