Initial browse support, server side

This commit is contained in:
John Doty 2022-12-17 08:12:30 -08:00
parent 6f906d80a7
commit d63ceb730f
6 changed files with 297 additions and 5 deletions

View file

@ -54,6 +54,9 @@ pub enum Message {
// List of available ports from server to client.
Ports(Vec<PortDesc>),
// Browse a thing
Browse(String),
}
impl Message {
@ -96,6 +99,10 @@ impl Message {
put_string(result, sliced);
}
}
Browse(url) => {
result.put_u8(0x07);
put_string(result, url);
}
};
}
@ -124,6 +131,7 @@ impl Message {
}
Ok(Ports(ports))
}
0x07 => Ok(Browse(get_string(cursor)?)),
b => Err(Error::Unknown(b).into()),
}
}
@ -274,6 +282,7 @@ mod message_tests {
desc: "metadata-library".to_string(),
},
]));
assert_round_trip(Browse("https://google.com/".to_string()));
}
#[test]

15
src/server/browser.rs Normal file
View file

@ -0,0 +1,15 @@
use crate::message::Message;
use anyhow::Result;
use tokio::sync::mpsc;
#[cfg(target_family = "unix")]
use browser_unix::handle_browser_open_impl;
mod browser_unix;
#[inline]
pub async fn handle_browser_open(
messages: mpsc::Sender<Message>,
) -> Result<()> {
handle_browser_open_impl(messages).await
}

View file

@ -0,0 +1,136 @@
use crate::message::{Message, MessageReader};
use anyhow::{bail, Context, Result};
use log::warn;
use std::os::unix::fs::DirBuilderExt;
use std::path::PathBuf;
use tokio::net::{UnixListener, UnixStream};
use tokio::sync::mpsc;
use users;
use xdg;
pub async fn handle_browser_open_impl(
messages: mpsc::Sender<Message>,
) -> Result<()> {
let path = socket_path().context("Error getting socket path")?;
handle_browser_open_with_path(messages, path).await
}
async fn handle_browser_open_with_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);
}
});
}
}
pub fn socket_path() -> Result<PathBuf> {
let mut socket_path = socket_directory()?;
std::fs::DirBuilder::new()
.recursive(true)
.mode(0o700)
.create(&socket_path)
.context("Error creating socket directory")?;
// TODO: check mode of directory
socket_path.push("browser");
Ok(socket_path)
}
fn socket_directory() -> Result<std::path::PathBuf> {
let base_directories = xdg::BaseDirectories::new()
.context("Error creating BaseDirectories")?;
match base_directories.place_runtime_file("fwd") {
Ok(path) => Ok(path),
Err(_) => {
let mut path = std::env::temp_dir();
path.push(format!("fwd{}", users::get_current_uid()));
Ok(path)
}
}
}
async fn handle_connection(
socket: UnixStream,
sender: mpsc::Sender<Message>,
) -> Result<()> {
let mut reader = MessageReader::new(socket);
let message = reader.read().await.context("Error reading message")?;
match message {
Message::Browse(url) => sender.send(Message::Browse(url)).await?,
_ => bail!("Unsupported message: {:?}", message),
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::message::MessageWriter;
use tempdir::TempDir;
#[test]
fn socket_path_repeats() {
assert_eq!(
socket_path().expect("Could not get socket path a"),
socket_path().expect("Could not get socket path b")
);
}
#[tokio::test]
async fn url_to_message() {
let (sender, mut receiver) = mpsc::channel(64);
let tmp_dir =
TempDir::new("url_to_message").expect("Error getting tmpdir");
let path = tmp_dir.path().join("socket");
let path_override = path.clone();
tokio::spawn(async move {
handle_browser_open_with_path(sender, path_override)
.await
.expect("Error in server!");
});
let mut attempt = 0;
let stream = loop {
match UnixStream::connect(&path).await {
Ok(stream) => break Ok(stream),
Err(e) => {
if attempt == 5 {
break Err(e)
.context("Maximum retries exceeded, last error");
}
attempt += 1;
}
}
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
}
.expect("Error connecting to socket");
let mut writer = MessageWriter::new(stream);
let sent = Message::Browse("https://google.com/".to_string());
writer
.write(sent.clone())
.await
.expect("Error writing browse message");
let received = receiver.recv().await.expect("Error receiving message");
assert_eq!(sent, received);
}
}

View file

@ -4,6 +4,7 @@ use log::{error, warn};
use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt, BufReader, BufWriter};
use tokio::sync::mpsc;
mod browser;
mod refresh;
// We drive writes through an mpsc queue, because we not only handle requests
@ -76,11 +77,13 @@ async fn server_main<
let mut writer = MessageWriter::new(writer);
let mut reader = MessageReader::new(reader);
let (_, result) = tokio::join!(
write_driver(&mut receiver, &mut writer),
server_loop(&mut reader, &mut sender)
);
result
let browse_sender = sender.clone();
tokio::select! {
_ = write_driver(&mut receiver, &mut writer) => Ok(()),
r = server_loop(&mut reader, &mut sender) => r,
r = browser::handle_browser_open(browse_sender) => r,
}
}
pub async fn run_server() {