|
|
@ -1,9 +1,9 @@ |
|
|
|
|
|
use std::io;
|
|
|
use std::net::SocketAddr;
|
|
|
use std::net::SocketAddr;
|
|
|
|
|
|
|
|
|
use anyhow::Context;
|
|
|
use anyhow::Context;
|
|
|
use futures::stream::{SplitSink, SplitStream};
|
|
|
use futures::stream::{SplitSink, SplitStream};
|
|
|
use futures::{SinkExt, StreamExt};
|
|
|
use futures::{SinkExt, StreamExt};
|
|
|
use solstice_proto::config;
|
|
|
|
|
|
use tokio::net::{TcpListener, TcpStream};
|
|
|
use tokio::net::{TcpListener, TcpStream};
|
|
|
use tokio::sync::mpsc;
|
|
|
use tokio::sync::mpsc;
|
|
|
use tokio_tungstenite::tungstenite::{
|
|
|
use tokio_tungstenite::tungstenite::{
|
|
|
@ -93,25 +93,92 @@ async fn handle( |
|
|
};
|
|
|
};
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
/// Start listening on the socket address stored in configuration, and send
|
|
|
|
|
|
/// control notifications to the client through the given channel.
|
|
|
|
|
|
pub async fn listen(
|
|
|
|
|
|
message_tx: mpsc::UnboundedSender<Message>,
|
|
|
|
|
|
mut response_rx: mpsc::Receiver<Response>,
|
|
|
|
|
|
) -> anyhow::Result<()> {
|
|
|
|
|
|
let address = format!("{}:{}", config::CONTROL_HOST, config::CONTROL_PORT);
|
|
|
|
|
|
let listener = TcpListener::bind(&address).await?;
|
|
|
|
|
|
|
|
|
/// A listener for control connections.
|
|
|
|
|
|
pub struct Listener {
|
|
|
|
|
|
inner: TcpListener,
|
|
|
|
|
|
address: SocketAddr,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
info!("Listening for control connections on {}", address);
|
|
|
|
|
|
|
|
|
impl Listener {
|
|
|
|
|
|
pub async fn bind(address_str: &str) -> io::Result<Self> {
|
|
|
|
|
|
let inner = TcpListener::bind(address_str).await?;
|
|
|
|
|
|
let address = inner.local_addr()?;
|
|
|
|
|
|
|
|
|
while let Ok((raw_stream, remote_address)) = listener.accept().await {
|
|
|
|
|
|
info!("Accepted control connection from {}", remote_address);
|
|
|
|
|
|
|
|
|
info!("Listening for control connections on {}", address);
|
|
|
|
|
|
|
|
|
let ws_stream = tokio_tungstenite::accept_async(raw_stream).await?;
|
|
|
|
|
|
info!("WebSocket connection established from {}", remote_address);
|
|
|
|
|
|
|
|
|
Ok(Self { inner, address })
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
handle(ws_stream, &remote_address, &message_tx, &mut response_rx).await
|
|
|
|
|
|
|
|
|
pub fn address(&self) -> &SocketAddr {
|
|
|
|
|
|
&self.address
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
|
|
|
|
/// Starts accepting control connections, one at a time. For each connection,
|
|
|
|
|
|
/// forwards incoming messages from the socket to `message_tx` and outgoing
|
|
|
|
|
|
/// responses from `response_rx` to the socket.
|
|
|
|
|
|
pub async fn run(
|
|
|
|
|
|
&mut self,
|
|
|
|
|
|
message_tx: mpsc::UnboundedSender<Message>,
|
|
|
|
|
|
mut response_rx: mpsc::Receiver<Response>,
|
|
|
|
|
|
) -> anyhow::Result<()> {
|
|
|
|
|
|
loop {
|
|
|
|
|
|
// TODO: Select from response_rx too, and stop looping when it is closed.
|
|
|
|
|
|
let (raw_stream, remote_address) =
|
|
|
|
|
|
self.inner.accept().await.context("accepting connection")?;
|
|
|
|
|
|
info!("Accepted control connection from {}", remote_address);
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: Continue iterating in case of error.
|
|
|
|
|
|
let ws_stream = tokio_tungstenite::accept_async(raw_stream).await?;
|
|
|
|
|
|
info!("WebSocket connection established from {}", remote_address);
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: Stop gracefully once `response_rx` is closed.
|
|
|
|
|
|
handle(ws_stream, &remote_address, &message_tx, &mut response_rx).await
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
|
mod tests {
|
|
|
|
|
|
use super::Listener;
|
|
|
|
|
|
|
|
|
|
|
|
use std::net::SocketAddr;
|
|
|
|
|
|
|
|
|
|
|
|
use anyhow::Context;
|
|
|
|
|
|
use tokio::sync::mpsc;
|
|
|
|
|
|
use tokio_tungstenite::connect_async;
|
|
|
|
|
|
|
|
|
|
|
|
use crate::control::Response;
|
|
|
|
|
|
use crate::dispatcher::Message;
|
|
|
|
|
|
|
|
|
|
|
|
struct Channels {
|
|
|
|
|
|
message_tx: mpsc::UnboundedSender<Message>,
|
|
|
|
|
|
message_rx: mpsc::UnboundedReceiver<Message>,
|
|
|
|
|
|
response_tx: mpsc::Sender<Response>,
|
|
|
|
|
|
response_rx: mpsc::Receiver<Response>,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl Default for Channels {
|
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
|
let (message_tx, message_rx) = mpsc::unbounded_channel();
|
|
|
|
|
|
let (response_tx, response_rx) = mpsc::channel(100);
|
|
|
|
|
|
Self {
|
|
|
|
|
|
message_tx,
|
|
|
|
|
|
message_rx,
|
|
|
|
|
|
response_tx,
|
|
|
|
|
|
response_rx,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
|
|
async fn binds_to_localhost() -> anyhow::Result<()> {
|
|
|
|
|
|
let listener = Listener::bind("localhost:0")
|
|
|
|
|
|
.await
|
|
|
|
|
|
.context("binding listener")?;
|
|
|
|
|
|
match listener.address() {
|
|
|
|
|
|
SocketAddr::V4(address) => assert!(address.ip().is_loopback()),
|
|
|
|
|
|
SocketAddr::V6(address) => assert!(address.ip().is_loopback()),
|
|
|
|
|
|
};
|
|
|
|
|
|
Ok(())
|
|
|
|
|
|
}
|
|
|
}
|
|
|
}
|