Skip to content

Rust#

Source code

This is a minimal server application built for Rust with Axum that allows:

It internally uses the LiveKit Rust SDK.

Running this application#

Download the tutorial code:

git clone https://github.com/OpenVidu/openvidu-livekit-tutorials.git

To run this server application, you need Rust installed on your device.

  1. Navigate into the server directory
    cd openvidu-livekit-tutorials/application-server/rust
    
  2. Run the application
    cargo run
    

Info

You can run any Application Client to test against this server right away.

Understanding the code#

The application is a simple Rust app with a single file main.rs that exports two endpoints:

  • /token : generate a token for a given Room name and Participant name.
  • /livekit/webhook : receive LiveKit webhook events.

Let's see the code of the main.rs file:

main.rs
use axum::http::HeaderMap;
use axum::{
    extract::Json, http::header::CONTENT_TYPE, http::Method, http::StatusCode, routing::post,
    Router,
};
use dotenv::dotenv;
use livekit_api::access_token::AccessToken; // (1)!
use livekit_api::access_token::TokenVerifier;
use livekit_api::access_token::VideoGrants;
use livekit_api::webhooks::WebhookReceiver;
use serde_json::{json, Value};
use std::env;
use tokio::net::TcpListener;
use tower_http::cors::{Any, CorsLayer};

#[tokio::main]
async fn main() {
    dotenv().ok(); // (2)!

    let server_port = env::var("SERVER_PORT").unwrap_or("6081".to_string());

    let cors = CorsLayer::new() // (3)!
        .allow_methods([Method::POST])
        .allow_origin(Any)
        .allow_headers([CONTENT_TYPE]);

    let app = Router::new() // (4)!
        .route("/token", post(create_token))
        .route("/livekit/webhook", post(receive_webhook))
        .layer(cors);

    let listener = tokio::net::TcpListener::bind("0.0.0.0:".to_string() + &server_port)
        .await
        .unwrap();
    axum::serve(listener, app).await.unwrap(); // (5)!
}
  1. Import all necessary dependencies from the Rust LiveKit library.
  2. Load environment variables from .env file.
  3. Enable CORS support.
  4. Define /token and /livekit/webhook endpoints.
  5. Start the server listening on the specified port.

The main.rs file imports the required dependencies and loads the necessary environment variables:

  • SERVER_PORT: the port where the application will be listening.
  • LIVEKIT_API_KEY: the API key of LiveKit Server.
  • LIVEKIT_API_SECRET: the API secret of LiveKit Server.

Then CORS support is enabled and the endpoints are defined. Finally the axum application is initialized on the specified port.


Create token endpoint#

The endpoint /token accepts POST requests with a payload of type application/json, containing the following fields:

  • roomName: the name of the Room where the user wants to connect.
  • participantName: the name of the participant that wants to connect to the Room.
main.rs
async fn create_token(payload: Option<Json<Value>>) -> (StatusCode, Json<Value>) {
    if let Some(payload) = payload {
        let livekit_api_key = env::var("LIVEKIT_API_KEY").unwrap_or("devkey".to_string());
        let livekit_api_secret = env::var("LIVEKIT_API_SECRET").unwrap_or("secret".to_string());

        let room_name = match payload.get("roomName") {
            Some(value) => value,
            None => {
                return (
                    StatusCode::BAD_REQUEST,
                    Json(json!({ "errorMessage": "roomName is required" })),
                );
            }
        };
        let participant_name = match payload.get("participantName") {
            Some(value) => value,
            None => {
                return (
                    StatusCode::BAD_REQUEST,
                    Json(json!({ "errorMessage": "participantName is required" })),
                );
            }
        };

        let token = match AccessToken::with_api_key(&livekit_api_key, &livekit_api_secret) // (1)!
            .with_identity(&participant_name.to_string()) // (2)!
            .with_name(&participant_name.to_string())
            .with_grants(VideoGrants { // (3)!
                room_join: true,
                room: room_name.to_string(),
                ..Default::default()
            })
            .to_jwt() // (4)!
        {
            Ok(token) => token,
            Err(_) => {
                return (
                    StatusCode::INTERNAL_SERVER_ERROR,
                    Json(json!({ "errorMessage": "Error creating token" })),
                );
            }
        };

        return (StatusCode::OK, Json(json!({ "token": token }))); // (5)!
    } else {
        return (
            StatusCode::BAD_REQUEST,
            Json(json!({ "errorMessage": "roomName and participantName are required" })),
        );
    }
}
  1. A new AccessToken is created providing the LIVEKIT_API_KEY and LIVEKIT_API_SECRET.
  2. We set participant's name and identity in the AccessToken.
  3. We set the video grants in the AccessToken. room_join allows the user to join a room and room determines the specific room. Check out all Video Grants.
  4. We convert the AccessToken to a JWT token.
  5. Finally, the token is sent back to the client.

The endpoint first obtains the roomName and participantName parameters from the request body. If they are not available, it returns a 400 error.

If required fields are available, a new JWT token is created. For that we use the LiveKit Rust SDK:

  1. A new AccessToken is created providing the LIVEKIT_API_KEY and LIVEKIT_API_SECRET.
  2. We set participant's name and identity in the AccessToken.
  3. We set the video grants in the AccessToken. room_join allows the user to join a room and room determines the specific room. Check out all Video Grants.
  4. We convert the AccessToken to a JWT token.
  5. Finally, the token is sent back to the client.

Receive webhook#

The endpoint /livekit/webhook accepts POST requests with a payload of type application/webhook+json. This is the endpoint where LiveKit Server will send webhook events.

main.rs
async fn receive_webhook(headers: HeaderMap, body: String) -> (StatusCode, String) {
    let livekit_api_key = env::var("LIVEKIT_API_KEY").unwrap_or("devkey".to_string());
    let livekit_api_secret = env::var("LIVEKIT_API_SECRET").unwrap_or("secret".to_string());
    let token_verifier = TokenVerifier::with_api_key(&livekit_api_key, &livekit_api_secret); // (1)!
    let webhook_receiver = WebhookReceiver::new(token_verifier); // (2)!

    let auth_header = match headers.get("Authorization") { // (3)!
        Some(header_value) => match header_value.to_str() {
            Ok(header_str) => header_str,
            Err(_) => {
                return (
                    StatusCode::BAD_REQUEST,
                    "Invalid Authorization header format".to_string(),
                );
            }
        },
        None => {
            return (
                StatusCode::BAD_REQUEST,
                "Authorization header is required".to_string(),
            );
        }
    };

    match webhook_receiver.receive(&body, auth_header) { // (4)!
        Ok(event) => {
            println!("LiveKit WebHook: {:?}", event); // (5)!
            return (StatusCode::OK, "ok".to_string());
        }
        Err(_) => {
            return (
                StatusCode::UNAUTHORIZED,
                "Error validating webhook event".to_string(),
            );
        }
    }
}
  1. Create a TokenVerifier with the LIVEKIT_API_KEY and LIVEKIT_API_SECRET. This will validate the webhook event to confirm it is actually coming from our LiveKit Server.
  2. Create a WebhookReceiver with the TokenVerifier.
  3. Get the Authorization header from the HTTP request.
  4. Obtain the webhook event using the WebhookReceiver#receive method. It expects the raw string body of the request and the Authorization header.
  5. Consume the event as you wish.

We declare as function parameters the map of headers (headers: HeaderMap) and the raw body (body: String) of the HTTP request. We will need both of them to validate and decode the incoming webhook event. We then:

  1. Create a TokenVerifier with the LIVEKIT_API_KEY and LIVEKIT_API_SECRET. This will validate the webhook event to confirm it is actually coming from our LiveKit Server.
  2. Create a WebhookReceiver with the TokenVerifier.
  3. Get the Authorization header from the HTTP request.
  4. Obtain the webhook event using the WebhookReceiver#receive method. It expects the raw string body of the request and the Authorization header.
  5. Consume the event as you wish (in this case, we just log it).

Remember to return a 200 OK response at the end to let LiveKit Server know that the webhook was received correctly.


From production to a local server#

When developing locally pointing to a production deployment and webhooks events are required by your application, you might face issues because OpenVidu cannot access your local server.

To receive webhooks from OpenVidu on your local machine, you need to expose your local server to the internet. This exposure allows OpenVidu to send webhooks directly to your local server.

The following images illustrate the difference between an unreachable local server and a reachable local server:

Unreachable local server

Unreachable local server

Reachable local server

Reachable local server

Exposing your local server to the internet is a common practice when developing applications locally. Tools like Ngrok, LocalTunnel, LocalXpose and Zrok can help you achieve this.

These tools provide you with a public URL that forwards requests to your local server. You can use this URL to receive webhooks from OpenVidu. For information on how to add this URL as the webhook URL in the OpenVidu deployment, refer to the following documentation:

  • Configure webhooks for an OpenVidu Local deployment. Learn more.
  • Configure webhooks for an OpenVidu Single Node deployment. Learn more.
  • Configure webhooks for an OpenVidu Elastic On-Premises deployment. Learn more.

  • Configure webhooks for an OpenVidu Elastic AWS deployment. Learn more.

  • Configure webhooks for an OpenVidu High Availability On-Premises deployment. Learn more.

  • Configure webhooks for an OpenVidu High Availability AWS deployment. Learn more.