livekit_token_source/
token_source.rs1use std::collections::HashMap;
16
17use crate::caching::TokenSourceCached;
18use crate::error::TokenSourceError;
19use crate::request::{TokenSourceFetchOptions, TokenSourceRequest};
20use crate::response::{TokenSourceResponse, TokenSourceResult};
21use async_trait::async_trait;
22use livekit_net::{Header, HttpClientExt};
23
24const DEVELOPMENT_TOKEN_SERVER_ENDPOINT_URL: &str =
25 "https://cloud-api.livekit.io/api/v2/sandbox/connection-details";
26const DEVELOPMENT_TOKEN_SERVER_ID_HEADER: &str = "X-Sandbox-ID";
27
28#[async_trait]
31pub trait TokenSourceFixed {
32 async fn fetch(&self) -> TokenSourceResult<TokenSourceResponse>;
33}
34
35pub struct TokenSourceLiteral {
37 response: TokenSourceResponse,
38}
39
40#[async_trait]
41impl TokenSourceFixed for TokenSourceLiteral {
42 async fn fetch(&self) -> TokenSourceResult<TokenSourceResponse> {
43 Ok(self.response.clone())
44 }
45}
46
47pub fn literal(
50 server_url: impl Into<String>,
51 participant_token: impl Into<String>,
52) -> TokenSourceLiteral {
53 TokenSourceLiteral {
54 response: TokenSourceResponse {
55 server_url: server_url.into(),
56 participant_token: participant_token.into(),
57 },
58 }
59}
60
61#[async_trait]
68pub trait TokenSourceConfigurable {
69 async fn fetch(
70 &self,
71 options: &TokenSourceFetchOptions,
72 ) -> TokenSourceResult<TokenSourceResponse>;
73
74 fn cached(self) -> TokenSourceCached<Self>
78 where
79 Self: Sized + Send + Sync,
80 {
81 TokenSourceCached::new(self)
82 }
83}
84
85pub struct TokenSourceEndpoint {
87 endpoint_url: String,
88 headers: HashMap<String, String>,
89}
90
91#[async_trait]
92impl TokenSourceConfigurable for TokenSourceEndpoint {
93 async fn fetch(
94 &self,
95 options: &TokenSourceFetchOptions,
96 ) -> TokenSourceResult<TokenSourceResponse> {
97 let request = TokenSourceRequest::from(options);
98
99 let http_client =
100 livekit_net::http_client().ok_or(TokenSourceError::TransportNotConfigured)?;
101
102 let body = serde_json::to_vec(&request)?;
103 let mut headers =
104 vec![Header { name: "Content-Type".into(), value: "application/json".into() }];
105 headers.extend(
106 self.headers
107 .iter()
108 .map(|(name, value)| Header { name: name.clone(), value: value.clone() }),
109 );
110
111 let response = http_client.post(self.endpoint_url.clone(), headers, body).await?;
112
113 if !(200..300).contains(&response.status) {
114 return Err(TokenSourceError::Server {
115 status: response.status,
116 body: String::from_utf8_lossy(&response.body).into_owned(),
117 });
118 }
119
120 let connection_details = serde_json::from_slice::<TokenSourceResponse>(&response.body)?;
121 Ok(connection_details)
122 }
123}
124
125pub fn endpoint(endpoint_url: impl Into<String>) -> TokenSourceEndpoint {
134 TokenSourceEndpoint { endpoint_url: endpoint_url.into(), headers: HashMap::new() }
135}
136
137impl TokenSourceEndpoint {
138 pub fn with_header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
140 self.headers.insert(key.into(), value.into());
141 self
142 }
143
144 pub fn with_headers(
147 mut self,
148 value: impl IntoIterator<Item = (impl Into<String>, impl Into<String>)>,
149 ) -> Self {
150 self.headers.extend(value.into_iter().map(|(k, v)| (k.into(), v.into())));
151 self
152 }
153}
154
155pub struct TokenSourceDevelopmentTokenServer {
157 token_source_endpoint: TokenSourceEndpoint,
158}
159
160#[async_trait]
161impl TokenSourceConfigurable for TokenSourceDevelopmentTokenServer {
162 async fn fetch(
163 &self,
164 options: &TokenSourceFetchOptions,
165 ) -> TokenSourceResult<TokenSourceResponse> {
166 self.token_source_endpoint.fetch(options).await
167 }
168}
169
170pub fn development_token_server(
178 token_server_id: impl Into<String>,
179) -> TokenSourceDevelopmentTokenServer {
180 TokenSourceDevelopmentTokenServer {
181 token_source_endpoint: endpoint(DEVELOPMENT_TOKEN_SERVER_ENDPOINT_URL)
182 .with_header(DEVELOPMENT_TOKEN_SERVER_ID_HEADER, token_server_id),
183 }
184}