#![allow(
clippy::expect_used,
clippy::unwrap_used,
clippy::panic,
clippy::new_without_default
)]
mod cache;
mod creds;
mod exec;
mod listen;
mod relay;
mod rng;
mod transport;
pub use cache::XdgModelCache;
pub use creds::XdgCredStore;
pub use listen::{stash_root, TcpBind};
pub use rng::random_token;
pub use transport::HttpTransport;
use std::cell::RefCell;
use std::io::{self, BufRead, BufReader, Write};
use std::net::TcpListener;
use std::process::Command;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use brazen::{BrowserLauncher, Clock, CodeReceiver, Pacer};
pub struct SystemClock;
impl Clock for SystemClock {
fn now(&self) -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0)
}
}
pub struct SystemBrowserLauncher;
impl BrowserLauncher for SystemBrowserLauncher {
fn open(&self, url: &str) -> io::Result<()> {
let mut argv = brazen::browser_argv(url).into_iter();
let prog = argv.next().unwrap_or_default();
Command::new(prog).args(argv).spawn()?;
Ok(())
}
}
pub struct LoopbackReceiver {
listener: RefCell<Option<TcpListener>>,
}
impl LoopbackReceiver {
pub fn new() -> Self {
LoopbackReceiver {
listener: RefCell::new(None),
}
}
}
impl CodeReceiver for LoopbackReceiver {
fn bind(&self, port: Option<u16>) -> io::Result<u16> {
let listener = TcpListener::bind(("127.0.0.1", port.unwrap_or(0)))?;
let actual = listener.local_addr()?.port();
*self.listener.borrow_mut() = Some(listener);
Ok(actual)
}
fn await_query(&self) -> io::Result<String> {
let guard = self.listener.borrow();
let listener = guard
.as_ref()
.ok_or_else(|| io::Error::other("loopback receiver was not bound"))?;
let (stream, _) = listener.accept()?;
let mut reader = BufReader::new(stream.try_clone()?);
let mut line = String::new();
reader.read_line(&mut line)?;
let query = brazen::query_from_request_line(line.trim_end())
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "callback had no query"))?;
let body = "brazen: you may close this tab and return to the terminal.";
let resp = format!(
"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: {}\r\n\r\n{body}",
body.len()
);
let mut stream = stream;
stream.write_all(resp.as_bytes())?;
Ok(query)
}
}
pub struct RealPacer;
impl Pacer for RealPacer {
fn wait(&self, secs: u64) {
std::thread::sleep(Duration::from_secs(secs));
}
}
#[cfg(test)]
mod tests;
#[cfg(test)]
mod cache_tests;