clientapi-pve 2026.5.24

Generated from apidoc.js. NOT an official Proxmox specification. See https://pve.proxmox.com/pve-docs/api-viewer/ for the upstream documentation.
Documentation
// Auto-generated by scripts/sdk-facades.ts. Do not edit.
//
// Auto-reconnect decorator (Decorator pattern). Wraps the underlying
// session with retry-and-reconnect on non-user-initiated close.
//
// Feature-gated under `extras` (same as the rest of the WebSocket
// helpers).

use std::time::Duration;

use crate::apis::configuration::Configuration;
use crate::websocket::{
    ConsoleConnector, TerminalSession, TerminalTarget, VncSession, VncTarget, WsError,
};

#[derive(Debug, Clone)]
pub struct RetryOptions {
    pub max_retries: u32,
    pub initial_delay: Duration,
    pub max_delay: Duration,
    pub backoff_multiplier: f64,
}

impl Default for RetryOptions {
    fn default() -> Self {
        Self {
            max_retries: 10,
            initial_delay: Duration::from_millis(500),
            max_delay: Duration::from_secs(30),
            backoff_multiplier: 2.0,
        }
    }
}

fn delay_for(attempt: u32, opts: &RetryOptions) -> Duration {
    let mut d = opts.initial_delay.as_secs_f64();
    for _ in 0..attempt {
        d *= opts.backoff_multiplier;
    }
    let max = opts.max_delay.as_secs_f64();
    Duration::from_secs_f64(d.min(max))
}

/// Wraps a TerminalSession with reconnect logic. Each user-facing
/// `recv` automatically attempts to re-issue + re-upgrade if the
/// underlying stream closed unexpectedly.
pub struct ResilientTerminalSession {
    cfg: Configuration,
    target: TerminalTarget,
    opts: RetryOptions,
    session: TerminalSession,
    closed: bool,
}

impl ResilientTerminalSession {
    pub async fn open(
        cfg: Configuration,
        target: TerminalTarget,
        opts: RetryOptions,
    ) -> Result<Self, WsError> {
        let session = ConsoleConnector::default()
            .open_terminal(&cfg, target.clone())
            .await?;
        Ok(Self {
            cfg,
            target,
            opts,
            session,
            closed: false,
        })
    }

    pub async fn send(&mut self, text: &str) -> Result<(), WsError> {
        self.session.send(text).await
    }

    pub async fn resize(&mut self, cols: u32, rows: u32) -> Result<(), WsError> {
        self.session.resize(cols, rows).await
    }

    pub async fn recv(&mut self) -> Result<Option<String>, WsError> {
        loop {
            match self.session.recv().await {
                Ok(Some(msg)) => return Ok(Some(msg)),
                Ok(None) | Err(_) if !self.closed => {
                    if !self.try_reconnect().await {
                        return Ok(None);
                    }
                }
                Ok(None) => return Ok(None),
                Err(e) => return Err(e),
            }
        }
    }

    pub async fn close(self) -> Result<(), WsError> {
        self.session.close().await
    }

    async fn try_reconnect(&mut self) -> bool {
        for i in 0..self.opts.max_retries {
            tokio::time::sleep(delay_for(i, &self.opts)).await;
            if let Ok(s) = ConsoleConnector::default()
                .open_terminal(&self.cfg, self.target.clone())
                .await
            {
                self.session = s;
                return true;
            }
        }
        false
    }
}

pub struct ResilientVncSession {
    cfg: Configuration,
    target: VncTarget,
    opts: RetryOptions,
    session: VncSession,
    closed: bool,
}

impl ResilientVncSession {
    pub async fn open(
        cfg: Configuration,
        target: VncTarget,
        opts: RetryOptions,
    ) -> Result<Self, WsError> {
        let session = ConsoleConnector::default()
            .open_vnc(&cfg, target.clone())
            .await?;
        Ok(Self {
            cfg,
            target,
            opts,
            session,
            closed: false,
        })
    }

    pub async fn send(&mut self, data: Vec<u8>) -> Result<(), WsError> {
        self.session.send(data).await
    }

    pub async fn recv(&mut self) -> Result<Option<Vec<u8>>, WsError> {
        loop {
            match self.session.recv().await {
                Ok(Some(b)) => return Ok(Some(b)),
                Ok(None) | Err(_) if !self.closed => {
                    if !self.try_reconnect().await {
                        return Ok(None);
                    }
                }
                Ok(None) => return Ok(None),
                Err(e) => return Err(e),
            }
        }
    }

    pub async fn close(self) -> Result<(), WsError> {
        self.session.close().await
    }

    async fn try_reconnect(&mut self) -> bool {
        for i in 0..self.opts.max_retries {
            tokio::time::sleep(delay_for(i, &self.opts)).await;
            if let Ok(s) = ConsoleConnector::default()
                .open_vnc(&self.cfg, self.target.clone())
                .await
            {
                self.session = s;
                return true;
            }
        }
        false
    }
}

pub async fn connect_terminal_resilient(
    cfg: Configuration,
    target: TerminalTarget,
    opts: RetryOptions,
) -> Result<ResilientTerminalSession, WsError> {
    ResilientTerminalSession::open(cfg, target, opts).await
}

pub async fn connect_vnc_resilient(
    cfg: Configuration,
    target: VncTarget,
    opts: RetryOptions,
) -> Result<ResilientVncSession, WsError> {
    ResilientVncSession::open(cfg, target, opts).await
}