Refactor in prep for clip

This commit is contained in:
John Doty 2024-06-22 07:32:11 -07:00
parent 3f7afc5b78
commit 3eba65f6e6
8 changed files with 101 additions and 94 deletions

View file

@ -22,7 +22,7 @@ indoc = "1"
log = { version = "0.4", features = ["std"] } log = { version = "0.4", features = ["std"] }
open = "3" open = "3"
thiserror = "1.0" thiserror = "1.0"
tokio = { version = "1", features = ["io-std", "io-util", "macros", "net", "process", "rt", "rt-multi-thread"] } tokio = { version = "1", features = ["io-std", "io-util", "macros", "net", "process", "rt", "rt-multi-thread", "fs"] }
tokio-stream = "0.1" tokio-stream = "0.1"
toml = "0.5" toml = "0.5"
tui = "0.19" tui = "0.19"

View file

@ -9,5 +9,10 @@ async fn main() {
std::process::exit(1); std::process::exit(1);
} }
fwd::browse_url(&args[1]).await; let url = &args[1];
if let Err(e) = fwd::browse_url(url).await {
eprintln!("Unable to open {url}");
eprintln!("{}", e);
std::process::exit(1);
}
} }

View file

@ -1,40 +0,0 @@
use crate::message::Message;
use anyhow::Result;
use tokio::sync::mpsc;
#[cfg(target_family = "unix")]
mod browse_unix;
#[cfg(target_family = "unix")]
use browse_unix::{browse_url_impl, handle_browser_open_impl};
#[inline]
pub async fn browse_url(url: &String) {
if let Err(e) = browse_url_impl(url).await {
eprintln!("Unable to open {url}");
eprintln!("{}", e);
std::process::exit(1);
}
}
#[cfg(not(target_family = "unix"))]
pub async fn browse_url_impl(_url: &String) -> Result<()> {
use anyhow::anyhow;
Err(anyhow!(
"Opening a browser is not supported on this platform"
))
}
#[inline]
pub async fn handle_browser_open(
messages: mpsc::Sender<Message>,
) -> Result<()> {
handle_browser_open_impl(messages).await
}
#[cfg(not(target_family = "unix"))]
async fn handle_browser_open_impl(
_messages: mpsc::Sender<Message>,
) -> Result<()> {
std::future::pending().await
}

View file

@ -1,8 +1,8 @@
mod browse;
mod client; mod client;
mod message; mod message;
mod reverse;
mod server; mod server;
pub use browse::browse_url;
pub use client::run_client; pub use client::run_client;
pub use reverse::browse_url;
pub use server::run_server; pub use server::run_server;

View file

@ -5,7 +5,7 @@ const VERSION: &str = env!("CARGO_PKG_VERSION");
fn usage() { fn usage() {
println!(indoc! {" println!(indoc! {"
usage: fwd [options] (<server> | browse <url>) usage: fwd [options] (<server> | browse <url> | clip [<file>])
To connect a client to a server that has an `fwd` installed in its path, run To connect a client to a server that has an `fwd` installed in its path, run
`fwd <server>` on the client, where <server> is the name of the server to `fwd <server>` on the client, where <server> is the name of the server to
@ -60,9 +60,13 @@ fn parse_args(args: Vec<String>) -> Args {
} else { } else {
Args::Error Args::Error
} }
} else if rest.len() > 1 && rest[0] == "browse" { } else if rest.len() > 1 {
if rest.len() == 2 { if rest[0] == "browse" {
Args::Browse(rest[1].to_string()) if rest.len() == 2 {
Args::Browse(rest[1].to_string())
} else {
Args::Error
}
} else { } else {
Args::Error Args::Error
} }
@ -73,6 +77,14 @@ fn parse_args(args: Vec<String>) -> Args {
} }
} }
async fn browse_url(url: &str) {
if let Err(e) = fwd::browse_url(&url).await {
eprintln!("Unable to open {url}");
eprintln!("{}", e);
std::process::exit(1);
}
}
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
match parse_args(std::env::args().collect()) { match parse_args(std::env::args().collect()) {
@ -86,7 +98,7 @@ async fn main() {
fwd::run_server().await; fwd::run_server().await;
} }
Args::Browse(url) => { Args::Browse(url) => {
fwd::browse_url(&url).await; browse_url(&url).await;
} }
Args::Client(server, sudo) => { Args::Client(server, sudo) => {
fwd::run_client(&server, sudo).await; fwd::run_client(&server, sudo).await;

29
src/reverse.rs Normal file
View file

@ -0,0 +1,29 @@
use anyhow::Result;
#[cfg(target_family = "unix")]
mod unix;
#[cfg(target_family = "unix")]
pub use unix::{handle_reverse_connections, send_reverse_message};
use crate::message::Message;
#[cfg(not(target_family = "unix"))]
pub async fn send_reverse_message(_message: Message) -> Result<()> {
use anyhow::anyhow;
Err(anyhow!(
"Server-side operations are not supported on this platform"
))
}
#[cfg(not(target_family = "unix"))]
pub async fn handle_reverse_connections(
_messages: mpsc::Sender<Message>,
) -> Result<()> {
std::future::pending().await
}
#[inline]
pub async fn browse_url(url: &str) -> Result<()> {
send_reverse_message(Message::Browse(url.to_string())).await
}

View file

@ -1,14 +1,15 @@
use crate::message::{Message, MessageReader, MessageWriter}; // The reverse client connects to the server via a local connection to send
// commands back to the client.
use anyhow::{bail, Context, Result}; use anyhow::{bail, Context, Result};
use log::warn; use log::warn;
use std::os::unix::fs::DirBuilderExt; use std::os::unix::fs::DirBuilderExt;
use std::path::PathBuf; use std::path::PathBuf;
use tokio::net::{UnixListener, UnixStream}; use tokio::net::{UnixListener, UnixStream};
use tokio::sync::mpsc; use tokio::sync::mpsc;
use users;
use xdg;
pub async fn browse_url_impl(url: &String) -> Result<()> { use crate::message::{Message, MessageReader, MessageWriter};
pub async fn send_reverse_message(message: Message) -> Result<()> {
let path = socket_path().context("Error getting socket path")?; let path = socket_path().context("Error getting socket path")?;
let stream = match UnixStream::connect(&path).await { let stream = match UnixStream::connect(&path).await {
Ok(s) => s, Ok(s) => s,
@ -18,38 +19,22 @@ pub async fn browse_url_impl(url: &String) -> Result<()> {
}; };
let mut writer = MessageWriter::new(stream); let mut writer = MessageWriter::new(stream);
writer writer
.write(Message::Browse(url.clone())) .write(message)
.await .await
.context("Error sending browse message")?; .context("Error sending browse message")?;
Ok(()) Ok(())
} }
pub async fn handle_browser_open_impl( fn socket_directory() -> Result<std::path::PathBuf> {
messages: mpsc::Sender<Message>, let base_directories = xdg::BaseDirectories::new()
) -> Result<()> { .context("Error creating BaseDirectories")?;
let path = socket_path().context("Error getting socket path")?; match base_directories.place_runtime_file("fwd") {
handle_browser_open_with_path(messages, path).await Ok(path) => Ok(path),
} Err(_) => {
let mut path = std::env::temp_dir();
async fn handle_browser_open_with_path( path.push(format!("fwd{}", users::get_current_uid()));
messages: mpsc::Sender<Message>, Ok(path)
path: PathBuf, }
) -> Result<()> {
let _ = std::fs::remove_file(&path);
let listener = UnixListener::bind(&path)
.with_context(|| format!("Failed to bind to {}", path.display()))?;
loop {
let (socket, _addr) = listener
.accept()
.await
.context("Error accepting connection")?;
let sender = messages.clone();
tokio::spawn(async move {
if let Err(e) = handle_connection(socket, sender).await {
warn!("Error handling socket connection: {:?}", e);
}
});
} }
} }
@ -68,16 +53,32 @@ pub fn socket_path() -> Result<PathBuf> {
Ok(socket_path) Ok(socket_path)
} }
fn socket_directory() -> Result<std::path::PathBuf> { pub async fn handle_reverse_connections(
let base_directories = xdg::BaseDirectories::new() messages: mpsc::Sender<Message>,
.context("Error creating BaseDirectories")?; ) -> Result<()> {
match base_directories.place_runtime_file("fwd") { let path = socket_path().context("Error getting socket path")?;
Ok(path) => Ok(path), handle_reverse_connections_with_path(messages, path).await
Err(_) => { }
let mut path = std::env::temp_dir();
path.push(format!("fwd{}", users::get_current_uid())); async fn handle_reverse_connections_with_path(
Ok(path) messages: mpsc::Sender<Message>,
} path: PathBuf,
) -> Result<()> {
let _ = std::fs::remove_file(&path);
let listener = UnixListener::bind(&path)
.with_context(|| format!("Failed to bind to {}", path.display()))?;
loop {
let (socket, _addr) = listener
.accept()
.await
.context("Error accepting connection")?;
let sender = messages.clone();
tokio::spawn(async move {
if let Err(e) = handle_connection(socket, sender).await {
warn!("Error handling socket connection: {:?}", e);
}
});
} }
} }
@ -119,7 +120,7 @@ mod tests {
let path_override = path.clone(); let path_override = path.clone();
tokio::spawn(async move { tokio::spawn(async move {
handle_browser_open_with_path(sender, path_override) handle_reverse_connections_with_path(sender, path_override)
.await .await
.expect("Error in server!"); .expect("Error in server!");
}); });

View file

@ -1,5 +1,5 @@
use crate::browse::handle_browser_open;
use crate::message::{Message, MessageReader, MessageWriter}; use crate::message::{Message, MessageReader, MessageWriter};
use crate::reverse::handle_reverse_connections;
use anyhow::Result; use anyhow::Result;
use log::{error, warn}; use log::{error, warn};
use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt, BufReader, BufWriter}; use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt, BufReader, BufWriter};
@ -13,7 +13,7 @@ mod refresh;
async fn write_driver<Writer: AsyncWrite + Unpin>( async fn write_driver<Writer: AsyncWrite + Unpin>(
messages: &mut mpsc::Receiver<Message>, messages: &mut mpsc::Receiver<Message>,
writer: &mut MessageWriter<Writer>, writer: &mut MessageWriter<Writer>,
) -> () { ) {
while let Some(m) = messages.recv().await { while let Some(m) = messages.recv().await {
writer.write(m).await.expect("Failed to write the message") writer.write(m).await.expect("Failed to write the message")
} }
@ -77,7 +77,7 @@ async fn server_main<
tokio::select! { tokio::select! {
_ = write_driver(&mut receiver, &mut writer) => Ok(()), _ = write_driver(&mut receiver, &mut writer) => Ok(()),
r = server_loop(&mut reader, &mut sender) => r, r = server_loop(&mut reader, &mut sender) => r,
r = handle_browser_open(browse_sender) => r, r = handle_reverse_connections(browse_sender) => r,
} }
} }