livekit_api/services/
mod.rs1use std::collections::HashMap;
16use std::fmt::{Debug, Display};
17
18use http::header::{HeaderMap, HeaderValue, AUTHORIZATION};
19use thiserror::Error;
20
21use crate::access_token::{AccessToken, AccessTokenError, SIPGrants, VideoGrants};
22
23pub use livekit_api::LiveKitApi;
24pub use twirp_client::{
25 ServerError,
26 ServerErrorCode,
27 ServerResult,
28 TwirpError,
30 TwirpErrorCode,
31 TwirpResult,
32};
33
34pub mod agent_dispatch;
35pub mod connector;
36pub mod egress;
37pub mod ingress;
38pub mod room;
39pub mod sip;
40
41mod dial_timeout;
42mod failover;
43mod livekit_api;
44mod twirp_client;
45
46#[cfg(all(test, feature = "services-tokio"))]
47mod api_test;
48
49pub const LIVEKIT_PACKAGE: &str = "livekit";
50
51#[derive(Debug, Error)]
52pub enum ServiceError {
53 #[error("invalid environment: {0}")]
54 Env(#[from] std::env::VarError),
55 #[error("invalid access token: {0}")]
56 AccessToken(#[from] AccessTokenError),
57 #[error("server error: {0}")]
58 Twirp(#[from] ServerError),
59}
60
61pub type ServiceResult<T> = Result<T, ServiceError>;
62
63pub(crate) enum ServiceBase {
66 ApiKeySecret { api_key: String, api_secret: String },
68 Token(String),
71}
72
73impl Debug for ServiceBase {
74 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75 match self {
77 Self::ApiKeySecret { api_key, .. } => {
78 f.debug_struct("ServiceBase").field("api_key", api_key).finish_non_exhaustive()
79 }
80 Self::Token(_) => f.debug_struct("ServiceBase").field("token", &"<redacted>").finish(),
81 }
82 }
83}
84
85impl ServiceBase {
86 pub fn with_api_key(api_key: &str, api_secret: &str) -> Self {
87 Self::ApiKeySecret { api_key: api_key.to_owned(), api_secret: api_secret.to_owned() }
88 }
89
90 pub fn with_token(token: &str) -> Self {
91 Self::Token(token.to_owned())
92 }
93
94 pub fn auth_header(
95 &self,
96 grants: VideoGrants,
97 sip: Option<SIPGrants>,
98 ) -> Result<HeaderMap, AccessTokenError> {
99 let token = match self {
100 Self::Token(token) => token.clone(),
101 Self::ApiKeySecret { api_key, api_secret } => {
102 let mut tok = AccessToken::with_api_key(api_key, api_secret).with_grants(grants);
103 if let Some(sip_grants) = sip {
104 tok = tok.with_sip_grants(sip_grants);
105 }
106 tok.to_jwt()?
107 }
108 };
109
110 let mut headers = HeaderMap::new();
111 headers.insert(AUTHORIZATION, HeaderValue::from_str(&format!("Bearer {}", token)).unwrap());
112 Ok(headers)
113 }
114}
115
116#[derive(Debug, Clone)]
120pub struct SipCallError {
121 code: String,
122 sip_status_code: Option<i32>,
123 sip_status: Option<String>,
124 metadata: HashMap<String, String>,
125}
126
127impl SipCallError {
128 pub fn from_error(err: &ServiceError) -> Option<Self> {
131 let ServiceError::Twirp(ServerError::Twirp(code)) = err else {
132 return None;
133 };
134 if !code.meta.contains_key("sip_status_code") && !code.meta.contains_key("sip_status") {
135 return None;
136 }
137 Some(Self {
138 code: code.code.clone(),
139 sip_status_code: code.meta.get("sip_status_code").and_then(|v| v.parse().ok()),
140 sip_status: code.meta.get("sip_status").cloned(),
141 metadata: code.meta.clone(),
142 })
143 }
144
145 pub fn code(&self) -> &str {
147 &self.code
148 }
149
150 pub fn sip_status_code(&self) -> Option<i32> {
152 self.sip_status_code
153 }
154
155 pub fn sip_status(&self) -> Option<&str> {
157 self.sip_status.as_deref()
158 }
159
160 pub fn metadata(&self) -> &HashMap<String, String> {
162 &self.metadata
163 }
164}
165
166impl Display for SipCallError {
167 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
168 write!(f, "SIP call failed: {}", self.sip_status_code.unwrap_or_default())?;
169 if let Some(reason) = &self.sip_status {
170 write!(f, " {}", reason)?;
171 }
172 write!(f, " ({})", self.code)?;
173 let mut extra: Vec<_> = self
174 .metadata
175 .iter()
176 .filter(|(k, _)| {
177 !matches!(k.as_str(), "sip_status_code" | "sip_status" | "error_details")
178 })
179 .map(|(k, v)| format!("{}={}", k, v))
180 .collect();
181 if !extra.is_empty() {
182 extra.sort();
183 write!(f, " [{}]", extra.join(", "))?;
184 }
185 Ok(())
186 }
187}
188
189impl std::error::Error for SipCallError {}