Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resize the terminal to match browser's page size. #32

Merged
merged 6 commits into from
Dec 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/cargo-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,6 @@ jobs:
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
# Run build & test separately so that their time can be measured individually
- run: cargo build --verbose
- run: cargo test --verbose
1 change: 1 addition & 0 deletions .github/workflows/clippy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ on:
pull_request:

env:
CARGO_TERM_COLOR: always
RUSTFLAGS: "-Dwarnings"

jobs:
Expand Down
3 changes: 3 additions & 0 deletions agent/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,6 @@ webterm-core = { path = "../core" }
url = "2.5.3"
ring = "0.17.8"
semver = "1.0.23"

[lints.clippy]
new_without_default = "allow"
4 changes: 1 addition & 3 deletions agent/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::args::Args;
use crate::models::relay::Relay;
use std::num::NonZeroU32;
use std::sync::Arc;
use std::time::Duration;
use tracing::error;
Expand Down Expand Up @@ -46,14 +45,13 @@ impl Config {
.split(',')
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
.map(|s| match Relay::new(&s) {
.filter_map(|s| match Relay::new(&s) {
Ok(relay) => Some(Arc::new(relay)),
Err(e) => {
error!("Failed to create relay for {}: {}", s, e);
None
}
})
.filter_map(|relay| relay)
.collect()
})
.unwrap_or_default();
Expand Down
13 changes: 5 additions & 8 deletions agent/src/messaging/process_f2a.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use webterm_core::types::{ActivityId, Bits96, FrontendId, SessionId};
pub async fn process_f2a(
frontend_id: FrontendId,
message: &[u8],
mut send: SendPayload,
send: SendPayload,
config: &Config,
) -> Result<SendPayload, AgentError> {
let root = read_message::<F2aRoot>(message)?;
Expand Down Expand Up @@ -63,19 +63,19 @@ async fn process_plain(
frontend.init_cryptographer(config.secret_key());

let decrypted = frontend.cryptographer()?.decrypt(
&message
message
.challenge_aes256gcm_solution()
.ok_or(AgentError::FBParseError(
"Expected challenge aes256gcm solution for auth present verification, got None"
.to_string(),
))?.bytes().to_vec(),
))?.bytes(),
&Bits96::from(message.challenge_iv().ok_or(AgentError::FBParseError(
"Expected challenge iv for auth present verification, got None".to_string(),
))?),
false,
)?;

if (decrypted == frontend.challenge_nonce()?.0.to_vec()) {
if decrypted == frontend.challenge_nonce()?.0.to_vec() {
success = true;
}

Expand Down Expand Up @@ -113,10 +113,7 @@ async fn process_encrypted(
frontend_id: FrontendId,
mut send: SendPayload,
) -> Result<SendPayload, AgentError> {
let compressed = match root.format() {
F2aMessageFormat::Aes256GcmDeflateRaw => true,
_ => false,
};
let compressed = root.format() == F2aMessageFormat::Aes256GcmDeflateRaw;

let frontend = FrontendRegistry::find(frontend_id).await?;
let frontend = frontend.lock().await;
Expand Down
2 changes: 1 addition & 1 deletion agent/src/messaging/process_r2a.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use webterm_core::types::FrontendId;

pub async fn process_r2a(
message: &[u8],
mut send: SendPayload,
send: SendPayload,
config: &Config,
) -> Result<SendPayload, AgentError> {
let message = read_message::<R2aRoot>(message)?;
Expand Down
28 changes: 13 additions & 15 deletions agent/src/models/activity.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,24 @@
use crate::models::activity_registry::ActivityRegistry;
use crate::models::agent_error::AgentError;
use crate::models::pty_activity::PtyActivity;
use crate::models::session::Session;
use crate::models::session_registry::SessionRegistry;
use crate::models::terminal::Terminal;
use std::fmt::Pointer;
use std::io::Read;
use std::sync::Arc;
use tokio::sync::Mutex;
use webterm_core::generated::flatbuffers_schema::talk_v1::activity::{
TerminalInput, TerminalInputRoot,
};
use tracing::info;
use webterm_core::generated::flatbuffers_schema::talk_v1::activity::{PtyInput, PtyInputRoot};
use webterm_core::serialisers::talk_v1::terminal_output_builder::ActivityInputBlob;
use webterm_core::types::{ActivityId, SessionId};

// in future, manage more activities like a "file browser"
pub enum ActivityType {
Terminal,
Pty,
}

pub struct Activity {
activity_id: ActivityId,
activity_type: ActivityType,
terminal: Option<Terminal>,
terminal: Option<PtyActivity>,
parent_session_id: SessionId,
}

Expand All @@ -32,13 +29,13 @@ impl PartialEq for Activity {
}

impl Activity {
pub async fn create_terminal(session_id: SessionId) -> Result<Arc<Activity>, AgentError> {
pub async fn create_pty(session_id: SessionId) -> Result<Arc<Activity>, AgentError> {
let activity_id = ActivityRegistry::next_activity_id();
let terminal = Terminal::new(activity_id, "/bin/bash").await?;
let terminal = PtyActivity::new(activity_id, "/bin/bash").await?;
let record = Arc::new(Self {
activity_id,
terminal: Some(terminal),
activity_type: ActivityType::Terminal,
activity_type: ActivityType::Pty,
parent_session_id: session_id,
});

Expand All @@ -56,10 +53,10 @@ impl Activity {

pub async fn receive_input(&self, payload: ActivityInputBlob) -> Result<(), AgentError> {
match self.activity_type {
ActivityType::Terminal => {
let input = flatbuffers::root::<TerminalInputRoot>(&payload.0)?;
ActivityType::Pty => {
let input = flatbuffers::root::<PtyInputRoot>(&payload.0)?;
match input.payload_type() {
TerminalInput::UserInput => {
PtyInput::UserInput => {
self.terminal
.as_ref()
.ok_or(AgentError::RuntimeError(
Expand All @@ -80,7 +77,8 @@ impl Activity {
.await?;
Ok(())
}
TerminalInput::Resize => {
PtyInput::Resize => {
info!("Received resize input: {:?}", input.payload_as_resize());
let resize_data = input
.payload_as_resize()
.ok_or(AgentError::FBParseError("Expected resize data".to_string()))?;
Expand Down
10 changes: 5 additions & 5 deletions agent/src/models/frontend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,17 @@ impl Frontend {
encrypted_nonce: &Bits256,
iv: &Bits96,
) -> Result<bool, AgentError> {
if let Some(challenge_nonce) = (self.challenge_nonce) {
if let Some(challenge_nonce) = self.challenge_nonce {
let decrypted_nonce =
self.cryptographer()?
.decrypt(encrypted_nonce.0.as_ref(), iv, false)?;
let result = challenge_nonce.0.to_vec() == decrypted_nonce;
self.challenge_nonce = None;
Ok(result)
} else {
return Err(AgentError::RuntimeError(
Err(AgentError::RuntimeError(
"Challenge nonce is not set".to_string(),
));
))
}
}

Expand All @@ -97,8 +97,8 @@ impl Frontend {
}

pub fn challenge_nonce(&self) -> Result<Bits256, AgentError> {
Ok(self.challenge_nonce.ok_or(AgentError::RuntimeError(
self.challenge_nonce.ok_or(AgentError::RuntimeError(
"Challenge nonce is not set".to_string(),
))?)
))
}
}
1 change: 0 additions & 1 deletion agent/src/models/frontend_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use std::collections::HashMap;
use std::sync::{Arc, OnceLock};
use tokio::sync::{Mutex, RwLock};
use tracing::debug;
use tracing_subscriber::registry;
use webterm_core::types::FrontendId;

// Frontend -> (has one) Session -> (has many) Activities
Expand Down
6 changes: 3 additions & 3 deletions agent/src/models/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ pub mod agent_error;
pub mod frontend;
pub mod frontend_registry;
pub mod panic_error;
pub mod pty_activity;
pub mod pty_activity_reader;
pub mod pty_activity_writer;
pub mod relay;
pub mod relay_connection;
pub mod runner;
Expand All @@ -12,6 +15,3 @@ pub mod session;
pub mod session_registry;
pub mod socket_reader;
pub mod socket_writer;
pub mod terminal;
pub mod terminal_reader;
pub mod terminal_writer;
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
use crate::models::agent_error::AgentError;
use crate::models::terminal_reader::TerminalReader;
use crate::models::pty_activity_reader::PtyActivityReader;
use pty_process::{Command, OwnedWritePty, Pty, Size};
use tokio::io::AsyncWriteExt;
use tokio::sync::Mutex;
use webterm_core::types::ActivityId;

pub struct Terminal {
terminal_reader: TerminalReader,
pub struct PtyActivity {
// required to keep the reader alive
_pty_reader: PtyActivityReader,
pty_writer: Mutex<OwnedWritePty>,
}

impl Terminal {
impl PtyActivity {
pub async fn new(activity_id: ActivityId, command: &str) -> Result<Self, AgentError> {
let pty = Pty::new()?;
let mut command = Command::new(command);
command.env("TERM", "xterm-256color");
command.spawn(&pty.pts().unwrap())?;
let (pty_reader, pty_writer) = pty.into_split();
let pty_writer = Mutex::new(pty_writer);
Ok(Terminal {
terminal_reader: TerminalReader::new(activity_id, pty_reader),
Ok(PtyActivity {
pty_writer,
_pty_reader: PtyActivityReader::new(activity_id, pty_reader),
})
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,51 +1,51 @@
use crate::models::activity::Activity;
use pty_process::OwnedReadPty;
use std::sync::{Arc, OnceLock};
use std::sync::OnceLock;
use tokio::io::AsyncReadExt;
use tokio::sync::{broadcast, mpsc, Mutex};
use tracing::debug;
use tracing::info;
use webterm_core::pty_output_formatter::format_pty_output;
use webterm_core::serialisers::talk_v1::terminal_output_builder::{
ActivityOutputBlob, TerminalOutputBuilder,
};
use webterm_core::types::{ActivityId, SessionId};
use webterm_core::types::ActivityId;

const BUFFER_SIZE: usize = 10240;

pub type TerminalSubscriber = broadcast::Receiver<Vec<u8>>;

type ChannelType = (
mpsc::Sender<TerminalReaderPayload>,
Mutex<mpsc::Receiver<TerminalReaderPayload>>,
mpsc::Sender<PtyActivityReaderPayload>,
Mutex<mpsc::Receiver<PtyActivityReaderPayload>>,
);

pub struct TerminalReaderPayload {
pub struct PtyActivityReaderPayload {
pub(crate) activity_id: ActivityId,
pub(crate) data: Vec<u8>,
}

impl TerminalReaderPayload {
pub fn to_terminal_output(&self) -> ActivityOutputBlob {
impl PtyActivityReaderPayload {
pub fn to_fb_output(&self) -> ActivityOutputBlob {
let builder = TerminalOutputBuilder::new();
builder.build_output(&self.data).to_flatbuffers()
}
}

pub struct TerminalReader {}
pub struct PtyActivityReader {}

impl TerminalReader {
impl PtyActivityReader {
pub fn channel() -> &'static ChannelType {
static CHANNEL: OnceLock<ChannelType> = OnceLock::new();
CHANNEL.get_or_init(|| {
let (tx, rx) = mpsc::channel::<TerminalReaderPayload>(1024);
let (tx, rx) = mpsc::channel::<PtyActivityReaderPayload>(1024);
(tx, Mutex::new(rx))
})
}

pub fn sender() -> mpsc::Sender<TerminalReaderPayload> {
pub fn sender() -> mpsc::Sender<PtyActivityReaderPayload> {
Self::channel().0.clone()
}

pub fn receiver() -> &'static Mutex<mpsc::Receiver<TerminalReaderPayload>> {
pub fn receiver() -> &'static Mutex<mpsc::Receiver<PtyActivityReaderPayload>> {
&Self::channel().1
}

Expand All @@ -54,14 +54,14 @@ impl TerminalReader {
tokio::spawn(async move {
debug!("starting new terminal reader stream");
loop {
let mut buf = [0u8; 1024];
let mut buf = [0u8; BUFFER_SIZE];
if let Ok(length) = reader_stream.read(&mut buf).await {
debug!(
"read from reader stream: {:?}",
format_pty_output(&buf[..length])
);
// debug!(
// "read from reader stream: {:?}",
// format_pty_output(&buf[..length])
// );
sender
.send(TerminalReaderPayload {
.send(PtyActivityReaderPayload {
activity_id,
data: buf[..length].to_vec(),
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ use tracing::info;

pub type TerminalPublisher = mpsc::Sender<Vec<u8>>;

pub struct TerminalWriter {
pub struct PtyActivityWriter {
_tx: TerminalPublisher,
}

impl TerminalWriter {
impl PtyActivityWriter {
pub fn new(writer_stream: Arc<Mutex<OwnedWritePty>>) -> Self {
let (_tx, mut rx) = mpsc::channel::<Vec<u8>>(16);
tokio::spawn(async move {
Expand Down
1 change: 0 additions & 1 deletion agent/src/models/relay.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use crate::config::Config;
use crate::models::agent_error::AgentError;
use tracing::{debug, info};
use url::Url;
Expand Down
3 changes: 1 addition & 2 deletions agent/src/models/relay_connection.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
use crate::config::{Config, RELAY_RECONNECT_INTERVAL};
use crate::models::activity_registry::ActivityRegistry;
use crate::models::agent_error::AgentError;
use crate::models::relay::Relay;
use crate::models::socket_reader::{SocketReader, SocketSubscriber};
use crate::models::socket_writer::{SocketPublisher, SocketWriter};
use futures::StreamExt;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::Notify;
use tokio::sync::RwLock;
use tokio::time::sleep;
Expand All @@ -21,6 +19,7 @@ pub struct RelayConnection {
}

struct State {
#[allow(dead_code)]
relay: Arc<Relay>,
writer: SocketWriter,
reader: SocketReader,
Expand Down
Loading
Loading