Skip to content

Commit

Permalink
Add option to set bind address for websocket
Browse files Browse the repository at this point in the history
  • Loading branch information
HEnquist committed Sep 8, 2020
1 parent 7af0bb8 commit 50718c3
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 49 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ New features:
- Added capture and playback devices Stdin & Stdout.
- Improved error messages.
- Improved validation of mixer config
- Added option to set which IP address to bind websocket server to

Bugfixes:
- Fix websocket `exit` command.
Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ This starts the processing defined in the specified config file. The config is f
Starting with the --help flag prints a short help message:
```
> camilladsp --help
CamillaDSP 0.1.0
CamillaDSP 0.3.2
Henrik Enquist <[email protected]>
A flexible tool for processing audio
Expand All @@ -247,7 +247,8 @@ FLAGS:
-w, --wait Wait for config from websocket
OPTIONS:
-p, --port <port> Port for websocket server
-a, --address <address> IP address to bind websocket server to
-p, --port <port> Port for websocket server
ARGS:
<configfile> The configuration file to use
Expand All @@ -256,6 +257,8 @@ If the "check" flag is given, the program will exit after checking the configura

To enable the websocket server, provide a port number with the `-p` option. Leave it out, or give 0 to disable.

By default the websocket server binds to the address 127.0.0.1 which means it's only accessible locally. If it should be also available to remote machines, give the IP address of the interface where it should be available with the `-a` option. Giving 0.0.0.0 will bind to all interfaces.

If the "wait" flag, `-w` is given, CamillaDSP will start the websocket server and wait for a configuration to be uploaded. Then the config file argument must be left out.

The default logging setting prints messages of levels "error", "warn" and "info". By passing the verbosity flag once, `-v` it also prints "debug". If and if's given twice, `-vv`, it also prints "trace" messages.
Expand All @@ -267,8 +270,6 @@ The configuration can be reloaded without restarting by sending a SIGHUP to the
## Controlling via websocket
See the [separate readme for the websocket server](./websocket.md)

If the websocket server is enabled with the -p option, CamillaDSP will listen to incoming websocket connections on the specified port.


# Capturing audio

Expand Down
38 changes: 29 additions & 9 deletions src/bin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ use camillalib::config;
use camillalib::processing;
#[cfg(feature = "socketserver")]
use camillalib::socketserver;
#[cfg(feature = "socketserver")]
use std::net::IpAddr;

use camillalib::StatusMessage;

Expand Down Expand Up @@ -336,6 +338,20 @@ fn main() {
Err(String::from("Must be an integer between 0 and 65535"))
}),
)
.arg(
Arg::with_name("address")
.help("IP address to bind websocket server to")
.short("a")
.long("address")
.takes_value(true)
.requires("port")
.validator(|val: String| -> Result<(), String> {
if val.parse::<IpAddr>().is_ok() {
return Ok(());
}
Err(String::from("Must be a valid IP address"))
}),
)
.arg(
Arg::with_name("wait")
.short("w")
Expand Down Expand Up @@ -410,16 +426,20 @@ fn main() {
#[cfg(feature = "socketserver")]
{
if let Some(port_str) = matches.value_of("port") {
let serveraddress = match matches.value_of("address") {
Some(addr) => addr,
None => "127.0.0.1",
};
let serverport = port_str.parse::<usize>().unwrap();
socketserver::start_server(
serverport,
signal_reload.clone(),
signal_exit.clone(),
active_config.clone(),
active_config_path.clone(),
new_config.clone(),
capture_status.clone(),
);
let shared_data = socketserver::SharedData {
signal_reload: signal_reload.clone(),
signal_exit: signal_exit.clone(),
active_config: active_config.clone(),
active_config_path: active_config_path.clone(),
new_config: new_config.clone(),
capture_status: capture_status.clone(),
};
socketserver::start_server(serveraddress, serverport, shared_data);
}
}

Expand Down
83 changes: 47 additions & 36 deletions src/socketserver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ use std::thread;
use crate::CaptureStatus;
use config;

#[derive(Debug, Clone)]
pub struct SharedData {
pub signal_reload: Arc<AtomicBool>,
pub signal_exit: Arc<AtomicUsize>,
pub active_config: Arc<Mutex<Option<config::Configuration>>>,
pub active_config_path: Arc<Mutex<Option<String>>>,
pub new_config: Arc<Mutex<Option<config::Configuration>>>,
pub capture_status: Arc<RwLock<CaptureStatus>>,
}

#[derive(Debug, PartialEq)]
enum WSCommand {
SetConfigName(String),
Expand Down Expand Up @@ -110,77 +120,70 @@ fn parse_command(cmd: &ws::Message) -> WSCommand {
}
}

pub fn start_server(
port: usize,
signal_reload: Arc<AtomicBool>,
signal_exit: Arc<AtomicUsize>,
active_config_shared: Arc<Mutex<Option<config::Configuration>>>,
active_config_path: Arc<Mutex<Option<String>>>,
new_config_shared: Arc<Mutex<Option<config::Configuration>>>,
capture_status: Arc<RwLock<CaptureStatus>>,
) {
pub fn start_server(bind_address: &str, port: usize, shared_data: SharedData) {
let address = bind_address.to_owned();
debug!("Start websocket server on port {}", port);
thread::spawn(move || {
ws::listen(format!("127.0.0.1:{}", port), |socket| {
let signal_reload_inst = signal_reload.clone();
let signal_exit_inst = signal_exit.clone();
let active_config_inst = active_config_shared.clone();
let new_config_inst = new_config_shared.clone();
let active_config_path_inst = active_config_path.clone();
let capture_status_inst = capture_status.clone();
let ws_result = ws::listen(format!("{}:{}", address, port), |socket| {
let shared_data_inst = shared_data.clone();
move |msg: ws::Message| {
let command = parse_command(&msg);
debug!("parsed command: {:?}", command);
match command {
WSCommand::Reload => {
signal_reload_inst.store(true, Ordering::Relaxed);
shared_data_inst
.signal_reload
.store(true, Ordering::Relaxed);
socket.send("OK:RELOAD")
}
WSCommand::GetCaptureRate => {
let capstat = capture_status_inst.read().unwrap();
let capstat = shared_data_inst.capture_status.read().unwrap();
socket.send(format!("OK:GETCAPTURERATE:{}", capstat.measured_samplerate))
}
WSCommand::GetSignalRange => {
let capstat = capture_status_inst.read().unwrap();
let capstat = shared_data_inst.capture_status.read().unwrap();
socket.send(format!("OK:GETSIGNALRANGE:{}", capstat.signal_range))
}
WSCommand::GetVersion => {
socket.send(format!("OK:GETVERSION:{}", crate_version!()))
}
WSCommand::GetState => {
let capstat = capture_status_inst.read().unwrap();
let capstat = shared_data_inst.capture_status.read().unwrap();
socket.send(format!("OK:GETSTATE:{}", &capstat.state.to_string()))
}
WSCommand::GetRateAdjust => {
let capstat = capture_status_inst.read().unwrap();
let capstat = shared_data_inst.capture_status.read().unwrap();
socket.send(format!("OK:GETRATEADJUST:{}", capstat.rate_adjust))
}
WSCommand::GetUpdateInterval => {
let capstat = capture_status_inst.read().unwrap();
let capstat = shared_data_inst.capture_status.read().unwrap();
socket.send(format!("OK:GETUPDATEINTERVAL:{}", capstat.update_interval))
}
WSCommand::SetUpdateInterval(nbr) => {
let mut capstat = capture_status_inst.write().unwrap();
let mut capstat = shared_data_inst.capture_status.write().unwrap();
capstat.update_interval = nbr;
socket.send("OK:SETUPDATEINTERVAL".to_string())
}
WSCommand::GetConfig => {
//let conf_yaml = serde_yaml::to_string(&*active_config_inst.lock().unwrap()).unwrap();
socket.send(format!(
"OK:GETCONFIG:{}",
serde_yaml::to_string(&*active_config_inst.lock().unwrap()).unwrap(),
serde_yaml::to_string(&*shared_data_inst.active_config.lock().unwrap())
.unwrap(),
))
}
WSCommand::GetConfigJson => {
//let conf_yaml = serde_yaml::to_string(&*active_config_inst.lock().unwrap()).unwrap();
socket.send(format!(
"OK:GETCONFIGJSON:{}",
serde_json::to_string(&*active_config_inst.lock().unwrap()).unwrap(),
serde_json::to_string(&*shared_data_inst.active_config.lock().unwrap())
.unwrap(),
))
}
WSCommand::GetConfigName => socket.send(format!(
"OK:GETCONFIGNAME:{}",
active_config_path_inst
shared_data_inst
.active_config_path
.lock()
.unwrap()
.as_ref()
Expand All @@ -189,7 +192,8 @@ pub fn start_server(
)),
WSCommand::SetConfigName(path) => match config::load_validate_config(&path) {
Ok(_) => {
*active_config_path_inst.lock().unwrap() = Some(path.clone());
*shared_data_inst.active_config_path.lock().unwrap() =
Some(path.clone());
socket.send(format!("OK:SETCONFIGNAME:{}", path))
}
_ => socket.send("ERROR:SETCONFIGNAME"),
Expand All @@ -199,8 +203,10 @@ pub fn start_server(
Ok(conf) => match config::validate_config(conf.clone()) {
Ok(()) => {
//*active_config_path_inst.lock().unwrap() = String::from("none");
*new_config_inst.lock().unwrap() = Some(conf);
signal_reload_inst.store(true, Ordering::Relaxed);
*shared_data_inst.new_config.lock().unwrap() = Some(conf);
shared_data_inst
.signal_reload
.store(true, Ordering::Relaxed);
socket.send("OK:SETCONFIG")
}
_ => socket.send("ERROR:SETCONFIG"),
Expand All @@ -216,8 +222,10 @@ pub fn start_server(
Ok(conf) => match config::validate_config(conf.clone()) {
Ok(()) => {
//*active_config_path_inst.lock().unwrap() = String::from("none");
*new_config_inst.lock().unwrap() = Some(conf);
signal_reload_inst.store(true, Ordering::Relaxed);
*shared_data_inst.new_config.lock().unwrap() = Some(conf);
shared_data_inst
.signal_reload
.store(true, Ordering::Relaxed);
socket.send("OK:SETCONFIGJSON")
}
_ => socket.send("ERROR:SETCONFIGJSON"),
Expand Down Expand Up @@ -262,12 +270,12 @@ pub fn start_server(
}
}
WSCommand::Stop => {
*new_config_inst.lock().unwrap() = None;
signal_exit_inst.store(2, Ordering::Relaxed);
*shared_data_inst.new_config.lock().unwrap() = None;
shared_data_inst.signal_exit.store(2, Ordering::Relaxed);
socket.send("OK:STOP")
}
WSCommand::Exit => {
signal_exit_inst.store(1, Ordering::Relaxed);
shared_data_inst.signal_exit.store(1, Ordering::Relaxed);
socket.send("OK:EXIT")
}
WSCommand::Invalid => {
Expand All @@ -276,8 +284,11 @@ pub fn start_server(
}
}
}
})
.unwrap();
});
match ws_result {
Ok(_) => {}
Err(err) => error!("Failed to start websocket server: {}", err),
}
});
}

Expand Down
2 changes: 2 additions & 0 deletions websocket.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ If the websocket server is enabled with the `-p` option, CamillaDSP will listen

If additionally the "wait" flag is given, it will wait for a config to be uploaded via the websocket server before starting the processing.

By default the websocket server binds to the address 127.0.0.1, which means it's only accessible locally (on the same machine). If it should be also available to remote machines, give the IP address of the interface where it should be available with the `-a` option. Giving 0.0.0.0 will bind to all interfaces.

The available commands are:

### General
Expand Down

0 comments on commit 50718c3

Please sign in to comment.