webterm-agent 0.2.5

Lightweight agent component for Webterm that enables secure browser-based terminal access
Documentation
use crate::agent_version::agent_version_to_flatbuffers;
use crate::config::Config;
use crate::messaging::process_r2a_handshake::process_r2a_handshake;
use crate::models::agent_error::AgentError;
use crate::models::relay::Relay;
use reqwest::Client;
use std::sync::Arc;
use tracing::info;
use webterm_core::flatbuffers_helpers::read_message;
use webterm_core::generated::flatbuffers_schema::handshake_v1::R2aHandshakeRoot;
use webterm_core::serialisers::handshake_v1::a2r_handshake_builder::A2rHandshakeBuilder;

pub struct Handshaker {
    pub result_relay: Arc<Relay>,
    pub result_nonce: String,
}

impl Handshaker {
    pub async fn run(config: Arc<Config>) -> Result<Self, AgentError> {
        let relay = config.random_relay();
        let mut subname = config.original_device_subname().to_string();
        loop {
            let result = initiate(config.clone(), relay.clone(), subname).await;

            match result {
                Ok((relay, nonce)) => {
                    return Ok(Self {
                        result_nonce: nonce,
                        result_relay: relay,
                    });
                }
                Err(AgentError::RelayErrorAgentAlreadyExists) => {
                    subname = config.try_new_device_subname()
                }
                Err(e) => {
                    return Err(e);
                }
            }
        }
    }
}

async fn initiate(
    config: Arc<Config>,
    relay: Arc<Relay>,
    subname: String,
) -> Result<(Arc<Relay>, String), AgentError> {
    info!(
        "Starting handshake: {} with device {}/{}",
        relay.host(),
        config.device_name(),
        subname
    );
    let payload = A2rHandshakeBuilder::new()
        .root_request_connection(config.device_name(), &subname)
        .to_flatbuffers(agent_version_to_flatbuffers());
    let url = relay.handshake_url();

    let client = Client::new();
    let response = client.post(url).body(payload.0).send().await?;

    let body = response.bytes().await?;
    let message = read_message::<R2aHandshakeRoot>(&body)?;
    let output = process_r2a_handshake(message).await?;

    Ok((relay, output.auth_nonce))
}