fundamentum_edge_pfwd/
errors.rs

1//! App Error module
2
3use std::{io, num::TryFromIntError};
4
5use displaydoc::Display;
6use fundamentum_portforwarding_proto_rust::ErrorCode;
7use thiserror::Error;
8use tokio::sync::mpsc::error::SendError;
9use tracing::error;
10use uuid::Uuid;
11
12use crate::protocol_sdk::ProtocolSdk;
13
14/// Define each possible Error
15#[derive(Debug, Error)]
16pub enum Error {
17    /// Header parsing error.
18    #[error(transparent)]
19    HeaderParse(#[from] HeaderParseError),
20
21    /// Internal error that is not sendable to the cloud.
22    #[error(transparent)]
23    InternalError(#[from] InternalError),
24
25    /// Error related to an operation sent from cloud.
26    #[error(transparent)]
27    CloudOperationError(#[from] CloudOperationError),
28}
29
30/// Define internal errors that are not sendable to the cloud.
31#[derive(Debug, Error, Display)]
32pub enum InternalError {
33    /// Cannot send a message into publisher channel : `{0}`
34    SendMessage(String),
35
36    /// Failed to receive message from channel : `{0}`.
37    ReceiveMessage(String),
38
39    /// Message is not a port forwarding message.
40    MessageIsNotPortForwarding,
41}
42
43/// Define errors that can be sent to the cloud.
44#[derive(Debug, Error, Display)]
45pub enum CloudOperationError {
46    /// Instance of port forwarding not found.
47    InstanceNotFound,
48
49    /// Cannot send data into instance : `{0}`
50    SendDataToInstance(SendError<Vec<u8>>, Uuid),
51
52    /// Cannot spawn a new TTY : `{0}`
53    #[cfg(unix)]
54    SpawnTty(pty_process::Error),
55
56    /// Cannot spawn a new TTY command : `{0}`
57    #[cfg(unix)]
58    FailedToSpawnTtyCommand(pty_process::Error),
59
60    /// Cannot resize TTY : `{0}`
61    #[cfg(unix)]
62    FailedToResizeTty(pty_process::Error),
63
64    /// Cannot spawn a new TCP connection : `{0}`
65    SpawnTcp(io::Error),
66
67    /// Cannot convert rows and/or cols to u16. Value should be between 1 and 65535.
68    InvalidRowsColsValueForTty(TryFromIntError),
69
70    /// Given port is invalid. Should be between 1 and 65535.
71    InvalidPort(TryFromIntError),
72
73    /// Cannot write data to socket/tty, server returned 0 byte.
74    WriteDataToServer(io::Error),
75
76    /// PTY connections are not supported on this platform. You need to be on a Unix-like system.
77    UnsupportedPlatform,
78
79    /// Unexpected operation received from the cloud. It could be an error or a status message which is not expected in the current context.
80    UnexpectedOperation,
81}
82
83/// Define message headers error when parsing
84#[derive(Debug, Error, Display)]
85pub enum HeaderParseError {
86    /// Version not found in headers.
87    VersionNotFound,
88
89    /// Version found but is invalid : `{0}`
90    InvalidVersion(String),
91
92    /// Usid not found in headers.
93    UsidNotFound,
94
95    /// Usid found but is invalid (cannot be deserialized) : `{0}`
96    InvalidUsid(String),
97
98    /// Operation not found in headers.
99    OperationNotFound,
100
101    /// Operation found but does not match the operation code : `{0}`.
102    InvalidOperation(String),
103
104    /// Operation code is invalid : `{0}`.
105    InvalidOperationCode(String),
106}
107
108/// Sends an error using the provided `ProtocolSdk` sender and/or logs it, depending on the error type.
109///
110/// # Errors
111///
112/// Returns an [`Error`] if sending the error through the `ProtocolSdk` fails.
113pub async fn send_or_log_error<P: ProtocolSdk>(sender: P, err: Error, instance_id: Uuid) {
114    match err {
115        Error::CloudOperationError(sendable_err) => {
116            let err_string = sendable_err.to_string();
117            sender
118                .send_error(instance_id, sendable_err.into(), &err_string)
119                .await
120                .unwrap_or_else(|e| error!(error = %e, "failed to send error to the cloud"));
121        }
122        Error::InternalError(InternalError::MessageIsNotPortForwarding) => {}
123        _ => error!(error = %err, "unexpected error occured"),
124    };
125}
126
127impl From<CloudOperationError> for ErrorCode {
128    fn from(val: CloudOperationError) -> Self {
129        match val {
130            CloudOperationError::InstanceNotFound => Self::InstanceNotFound,
131            CloudOperationError::SendDataToInstance(_, _) => Self::InstanceCorrupted,
132            CloudOperationError::SpawnTcp(_) => Self::SpawnTcp,
133            CloudOperationError::InvalidRowsColsValueForTty(_) => Self::InvalidRowsColsValueForTty,
134            CloudOperationError::InvalidPort(_) => Self::InvalidPort,
135            CloudOperationError::WriteDataToServer(_) => Self::WriteDataToServer,
136            CloudOperationError::UnsupportedPlatform => Self::UnsupportedPlatform,
137            CloudOperationError::UnexpectedOperation => Self::UnexpectedOperation,
138            #[cfg(unix)]
139            CloudOperationError::SpawnTty(_) => Self::SpawnTty,
140            #[cfg(unix)]
141            CloudOperationError::FailedToSpawnTtyCommand(_) => Self::SpawnTtyCommand,
142            #[cfg(unix)]
143            CloudOperationError::FailedToResizeTty(_) => Self::ResizeTty,
144        }
145    }
146}