use std::sync::Arc;
use futures::{SinkExt, StreamExt, lock::Mutex, stream};
use tokio_util::codec::{Framed, LinesCodec};
use crate::{memsocket::MemorySocket, multiaddr::Multiaddr, test_utils::transport::build_connected_sockets};
pub async fn spawn() -> (Multiaddr, State, MemorySocket) {
let (addr, socket_out, socket_in) = build_connected_sockets().await;
let server = TorControlPortTestServer::new(socket_in);
let state = server.get_shared_state();
tokio::spawn(server.run());
(addr, state, socket_out)
}
#[derive(Clone)]
pub struct State {
request_lines: Arc<Mutex<Vec<String>>>,
canned_response: Arc<Mutex<Vec<String>>>,
}
impl State {
pub fn new() -> Self {
Self {
request_lines: Arc::new(Mutex::new(Vec::new())),
canned_response: Arc::new(Mutex::new(all_to_owned(canned_responses::OK))),
}
}
pub async fn set_canned_response<'a, T: AsRef<[&'a str]>>(&self, lines: T) {
*self.canned_response.lock().await = all_to_owned(lines);
}
pub async fn take_requests(&self) -> Vec<String> {
self.request_lines.lock().await.drain(..).collect()
}
}
pub struct TorControlPortTestServer {
socket: MemorySocket,
state: State,
}
impl TorControlPortTestServer {
pub fn new(socket: MemorySocket) -> Self {
Self {
socket,
state: State::new(),
}
}
pub fn get_shared_state(&self) -> State {
self.state.clone()
}
pub async fn run(self) {
let mut framed = Framed::new(self.socket, LinesCodec::new());
let state = self.state;
while let Some(msg) = framed.next().await {
state.request_lines.lock().await.push(msg.unwrap());
let mut responses = stream::iter(state.canned_response.lock().await.clone()).map(Ok);
framed.send_all(&mut responses).await.unwrap();
}
}
}
fn all_to_owned<'a, T: AsRef<[&'a str]>>(strings: T) -> Vec<String> {
strings.as_ref().iter().map(|s| (*s).to_owned()).collect()
}
pub mod canned_responses {
pub const OK: &[&str] = &["250 OK"];
pub const GET_CONF_HIDDEN_SERVICE_PORT_OK: &[&str] = &[
"250-HiddenServicePort=8080",
"250-HiddenServicePort=8081 127.0.0.1:9000",
"250 HiddenServicePort=8082 127.0.0.1:9001",
];
pub const GET_INFO_NET_LISTENERS_OK: &[&str] = &[
"250-net/listeners/socks=\"127.0.0.1:9050\" \"unix:/run/tor/socks\"",
"250 OK",
];
pub const GET_INFO_ONIONS_DETACHED_OK: &[&str] = &[
"250+onions/detached=",
"mochz2xppfziim5olr5f6q27poc4vfob2xxxxxxxxxxxxxxxxxxxxxxx",
"nhqdqym6j35rk7tdou4cdj4gjjqagimutxxxxxxxxxxxxxxxxxxxxxxx",
".",
"250 OK",
];
pub const ADD_ONION_OK: &[&str] = &[
"250-ServiceID=qigbgbs4ue3ghbupsotgh73cmmkjrin2aprlyxsrnrvpmcmzy3g4wbid",
"250-PrivateKey=ED25519-V3:\
Pg3GEyssauPRW3jP6mHwKOxvl_fMsF0QsZC3DvQ8jZ9AxmfRvSP35m9l0vOYyOxkOqWM6ufjdYuM8Ae6cR2UdreG6",
"250 OK",
];
pub const ADD_ONION_RSA1024_OK: &[&str] = &["250-ServiceID=62q4tswkxp74dtn7", "250 OK"];
pub const ADD_ONION_DISCARDPK_OK: &[&str] = &[
"250-ServiceID=qigbgbs4ue3ghbupsotgh73cmmkjrin2aprlyxsrnrvpmcmzy3g4wbid",
"250 OK",
];
pub const ERR_552: &[&str] = &["552 Unrecognised configuration key \"dummy\""];
pub const PROTOCOL_INFO_NO_AUTH_OK: &[&str] = &[
"250-PROTOCOLINFO 1",
"250-AUTH METHODS=NULL",
"250-VERSION Tor=\"0.4.6.10\"",
"250 OK",
];
pub const PROTOCOL_INFO_COOKIE_AUTH_OK: &[&str] = &[
"250-PROTOCOLINFO 1",
"250-AUTH METHODS=COOKIE,SAFECOOKIE COOKIEFILE=\"/home/user/.tor/control_auth_cookie\"",
"250-VERSION Tor=\"0.4.6.10\"",
"250 OK",
];
}