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