Skip to content

Commit

Permalink
cli: Add global pex config
Browse files Browse the repository at this point in the history
  • Loading branch information
madadam committed Aug 14, 2024
1 parent f5c1038 commit 4de4656
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 10 deletions.
20 changes: 20 additions & 0 deletions bridge/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,26 @@ where
}
}

impl<T> ConfigEntry<T>
where
T: Serialize + DeserializeOwned + fmt::Debug + Default,
{
pub async fn modify<F>(&self, f: F) -> Result<(), ConfigError>
where
F: FnOnce(&mut T),
{
let mut value = match self.get().await {
Ok(value) => value,
Err(ConfigError::NotFound) => T::default(),
Err(error) => return Err(error),
};

f(&mut value);

self.set(&value).await
}
}

#[derive(Error, Debug)]
pub enum ConfigError {
#[error("config entry not found")]
Expand Down
45 changes: 45 additions & 0 deletions bridge/src/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ const LAST_USED_UDP_PORT_COMMENT: &str =
This, in turn, is mainly useful for users who can't or don't want to use UPnP and have to\n\
default to manually setting up port forwarding on their routers.";

const PEX_KEY: ConfigKey<PexConfig> = ConfigKey::new("pex", "Peer exchange configuration");

#[derive(Eq, PartialEq, Debug, Serialize, Deserialize)]
pub struct NetworkDefaults {
pub port_forwarding_enabled: bool,
Expand Down Expand Up @@ -72,6 +74,10 @@ pub async fn init(network: &Network, config: &ConfigStore, defaults: NetworkDefa
for peer in peers {
network.add_user_provided_peer(&peer);
}

let PexConfig { send, recv } = config.entry(PEX_KEY).get().await.unwrap_or_default();
network.set_pex_send_enabled(send);
network.set_pex_recv_enabled(recv);
}

/// Binds the network to the specified addresses.
Expand Down Expand Up @@ -162,6 +168,28 @@ pub async fn user_provided_peers(config: &ConfigStore) -> Vec<PeerAddr> {
config.entry(PEERS_KEY).get().await.unwrap_or_default()
}

/// Enables/disables sending contacts over PEX.
pub async fn set_pex_send_enabled(network: &Network, config: &ConfigStore, enabled: bool) {
config
.entry(PEX_KEY)
.modify(|pex_config| pex_config.send = enabled)
.await
.ok();

network.set_pex_send_enabled(enabled);
}

/// Enables/disables receiving contacts over PEX.
pub async fn set_pex_recv_enabled(network: &Network, config: &ConfigStore, enabled: bool) {
config
.entry(PEX_KEY)
.modify(|pex_config| pex_config.recv = enabled)
.await
.ok();

network.set_pex_recv_enabled(enabled);
}

/// Utility to help reuse bind ports across network restarts.
struct LastUsedPorts {
quic_v4: u16,
Expand Down Expand Up @@ -269,6 +297,23 @@ impl LastUsedPorts {
}
}

#[derive(Debug, Serialize, Deserialize)]
struct PexConfig {
// Is sending contacts over PEX enabled?
send: bool,
// Is receiving contacts over PEX enabled?
recv: bool,
}

impl Default for PexConfig {
fn default() -> Self {
Self {
send: true,
recv: true,
}
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
44 changes: 39 additions & 5 deletions cli/src/handler/local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,14 +382,48 @@ impl ouisync_bridge::transport::Handler for LocalHandler {
Ok(holder.registration.is_dht_enabled().into())
}
}
Request::Pex { name, enabled } => {
let holder = self.state.repositories.find(&name)?;
Request::Pex {
name,
enabled,
send,
recv,
} => {
if let Some(name) = name {
let holder = self.state.repositories.find(&name)?;

if let Some(enabled) = enabled {
holder.registration.set_pex_enabled(enabled).await;
Ok(().into())
} else {
Ok(holder.registration.is_pex_enabled().into())
}
} else if send.is_some() || recv.is_some() {
if let Some(send) = send {
ouisync_bridge::network::set_pex_send_enabled(
&self.state.network,
&self.state.config,
send,
)
.await;
}

if let Some(recv) = recv {
ouisync_bridge::network::set_pex_recv_enabled(
&self.state.network,
&self.state.config,
recv,
)
.await;
}

if let Some(enabled) = enabled {
holder.registration.set_pex_enabled(enabled).await;
Ok(().into())
} else {
Ok(holder.registration.is_pex_enabled().into())
Ok(format!(
"send: {} recv: {}",
self.state.network.is_pex_send_enabled(),
self.state.network.is_pex_recv_enabled(),
)
.into())
}
}
Request::Quota {
Expand Down
45 changes: 40 additions & 5 deletions cli/src/protocol.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use chrono::{DateTime, SecondsFormat, Utc};
use clap::{builder::BoolishValueParser, Subcommand, ValueEnum};
use clap::{
builder::{ArgPredicate, BoolishValueParser},
Subcommand, ValueEnum,
};
use ouisync_lib::{AccessMode, PeerAddr, PeerInfo, PeerSource, PeerState, StorageSize};
use serde::{Deserialize, Serialize};
use std::{
Expand Down Expand Up @@ -210,13 +213,39 @@ pub(crate) enum Request {
#[arg(value_parser = BoolishValueParser::new())]
enabled: Option<bool>,
},
/// Enable or disable Peer Exchange (PEX)
/// Configure Peer Exchange (PEX)
Pex {
/// Name of the repository to enable/disable PEX for.
#[arg(short = 'n', long)]
name: String,
name: Option<String>,

/// Whether to enable or disable. If omitted, prints the current state.
#[arg(value_parser = BoolishValueParser::new())]
/// Globally enable/disable sending contacts over PEX. If all of name, send, recv are
/// omitted, prints the current state.
#[arg(
short,
long,
conflicts_with_all = ["name", "enabled"],
value_parser = BoolishValueParser::new(),
value_name = "ENABLED"
)]
send: Option<bool>,

/// Globally enable/disable receiving contacts over PEX. If all of name, send, recv are
/// omitted, prints the current state.
#[arg(
short,
long,
conflicts_with_all = ["name", "enabled"],
value_parser = BoolishValueParser::new(),
value_name = "ENABLED"
)]
recv: Option<bool>,

/// Enable/disable PEX for the specified repository. If omitted, prints the current state.
#[arg(
requires_if(ArgPredicate::IsPresent, "name"),
value_parser = BoolishValueParser::new(),
)]
enabled: Option<bool>,
},
/// Get or set storage quota
Expand Down Expand Up @@ -316,6 +345,12 @@ impl From<String> for Response {
}
}

impl<'a> From<&'a str> for Response {
fn from(value: &'a str) -> Self {
Self::String(value.to_owned())
}
}

impl From<Vec<String>> for Response {
fn from(value: Vec<String>) -> Self {
Self::Strings(value)
Expand Down

0 comments on commit 4de4656

Please sign in to comment.