refactor websocket veilid_config and update scripts

This commit is contained in:
John Smith 2021-12-09 16:00:47 -05:00
parent de36b0d6d6
commit ea8ffea1c9
19 changed files with 797 additions and 145 deletions

View File

@ -15,10 +15,10 @@ if sys.version_info < (3, 0, 0):
sys.exit(1) sys.exit(1)
script_dir = os.path.dirname(os.path.realpath(__file__)) script_dir = os.path.dirname(os.path.realpath(__file__))
veilid_server_exe_debug = os.path.join(script_dir, '..', 'veilid-server', veilid_server_exe_debug = os.path.join(script_dir, '..',
'target', 'debug', 'veilid-server') 'target', 'debug', 'veilid-server')
veilid_server_exe_release = os.path.join( veilid_server_exe_release = os.path.join(
script_dir, '..', 'veilid-server', 'target', 'release', 'veilid-server') script_dir, '..', 'target', 'release', 'veilid-server')
main_process = None main_process = None
subindex_processes = [] subindex_processes = []

View File

@ -860,11 +860,11 @@ impl Network {
pub async fn start_ws_listeners(&self) -> Result<(), String> { pub async fn start_ws_listeners(&self) -> Result<(), String> {
let routing_table = self.routing_table(); let routing_table = self.routing_table();
let (listen_address, public_address, path) = { let (listen_address, url, path) = {
let c = self.config.get(); let c = self.config.get();
( (
c.network.protocol.ws.listen_address.clone(), c.network.protocol.ws.listen_address.clone(),
c.network.protocol.ws.public_address.clone(), c.network.protocol.ws.url.clone(),
c.network.protocol.ws.path.clone(), c.network.protocol.ws.path.clone(),
) )
}; };
@ -888,21 +888,20 @@ impl Network {
); );
// Add static public dialinfo if it's configured // Add static public dialinfo if it's configured
if let Some(public_address) = public_address.as_ref() { if let Some(url) = url.as_ref() {
let (public_fqdn, public_port) = split_port(public_address).map_err(|_| { let split_url = SplitUrl::from_str(url)?;
"invalid WS public address, port not specified correctly".to_owned() if split_url.scheme.to_ascii_lowercase() != "ws" {
})?; return Err("WS URL must use 'ws://' scheme".to_owned());
let public_port = public_port }
.ok_or_else(|| "port must be specified for public WS address".to_owned())?;
routing_table.register_global_dial_info( routing_table.register_global_dial_info(
DialInfo::ws(fqdn, public_port, public_fqdn), DialInfo::ws(
Some(NetworkClass::Server), split_url.host,
DialInfoOrigin::Static, split_url.port.unwrap_or(80),
); split_url
} else { .path
routing_table.register_global_dial_info( .map(|p| p.to_string())
DialInfo::ws(fqdn, port, path.clone()), .unwrap_or_else(|| "/".to_string()),
),
Some(NetworkClass::Server), Some(NetworkClass::Server),
DialInfoOrigin::Static, DialInfoOrigin::Static,
); );
@ -914,11 +913,11 @@ impl Network {
pub async fn start_wss_listeners(&self) -> Result<(), String> { pub async fn start_wss_listeners(&self) -> Result<(), String> {
let routing_table = self.routing_table(); let routing_table = self.routing_table();
let (listen_address, public_address, path) = { let (listen_address, url, path) = {
let c = self.config.get(); let c = self.config.get();
( (
c.network.protocol.wss.listen_address.clone(), c.network.protocol.wss.listen_address.clone(),
c.network.protocol.wss.public_address.clone(), c.network.protocol.wss.url.clone(),
c.network.protocol.wss.path.clone(), c.network.protocol.wss.path.clone(),
) )
}; };
@ -943,24 +942,25 @@ impl Network {
); );
// Add static public dialinfo if it's configured // Add static public dialinfo if it's configured
if let Some(public_address) = public_address.as_ref() { if let Some(url) = url.as_ref() {
let (public_fqdn, public_port) = split_port(public_address).map_err(|_| { let split_url = SplitUrl::from_str(url)?;
"invalid WSS public address, port not specified correctly".to_owned() if split_url.scheme.to_ascii_lowercase() != "wss" {
})?; return Err("WSS URL must use 'wss://' scheme".to_owned());
let public_port = public_port }
.ok_or_else(|| "port must be specified for public WSS address".to_owned())?;
routing_table.register_global_dial_info( routing_table.register_global_dial_info(
DialInfo::wss(fqdn, public_port, public_fqdn), DialInfo::wss(
None, split_url.host,
split_url.port.unwrap_or(443),
split_url
.path
.map(|p| p.to_string())
.unwrap_or_else(|| "/".to_string()),
),
Some(NetworkClass::Server),
DialInfoOrigin::Static, DialInfoOrigin::Static,
); );
} else { } else {
routing_table.register_global_dial_info( return Err("WSS URL must be specified due to TLS requirements".to_owned());
DialInfo::wss(fqdn, port, path.clone()),
None,
DialInfoOrigin::Static,
);
} }
self.inner.lock().wss_listen = true; self.inner.lock().wss_listen = true;

View File

@ -227,10 +227,11 @@ impl NetworkManager {
} }
pub async fn tick(&self) -> Result<(), String> { pub async fn tick(&self) -> Result<(), String> {
let (net, lease_manager, receipt_manager) = { let (routing_table, net, lease_manager, receipt_manager) = {
let inner = self.inner.lock(); let inner = self.inner.lock();
let components = inner.components.as_ref().unwrap(); let components = inner.components.as_ref().unwrap();
( (
inner.routing_table.as_ref().unwrap().clone(),
components.net.clone(), components.net.clone(),
components.lease_manager.clone(), components.lease_manager.clone(),
components.receipt_manager.clone(), components.receipt_manager.clone(),
@ -244,6 +245,9 @@ impl NetworkManager {
net.startup().await?; net.startup().await?;
} }
// Run the routing table tick
routing_table.tick().await?;
// Run the low level network tick // Run the low level network tick
net.tick().await?; net.tick().await?;

View File

@ -205,14 +205,14 @@ impl RoutingTable {
}); });
info!( info!(
"Local Dial Info: {} ({:?})", "Local Dial Info: {}",
NodeDialInfoSingle { NodeDialInfoSingle {
node_id: NodeId::new(inner.node_id), node_id: NodeId::new(inner.node_id),
dial_info dial_info
} }
.to_string(), .to_string(),
origin,
); );
debug!(" Origin: {:?}", origin);
} }
pub fn clear_local_dial_info(&self) { pub fn clear_local_dial_info(&self) {
@ -281,15 +281,15 @@ impl RoutingTable {
}); });
info!( info!(
"Public Dial Info: {} ({:?}#{:?})", "Public Dial Info: {}",
NodeDialInfoSingle { NodeDialInfoSingle {
node_id: NodeId::new(inner.node_id), node_id: NodeId::new(inner.node_id),
dial_info dial_info
} }
.to_string(), .to_string(),
origin,
network_class,
); );
debug!(" Origin: {:?}", origin);
debug!(" Network Class: {:?}", network_class);
} }
pub fn clear_global_dial_info(&self) { pub fn clear_global_dial_info(&self) {
@ -613,6 +613,8 @@ impl RoutingTable {
c.network.bootstrap.clone() c.network.bootstrap.clone()
}; };
trace!("Bootstrap task with: {:?}", bootstrap);
// Map all bootstrap entries to a single key with multiple dialinfo // Map all bootstrap entries to a single key with multiple dialinfo
let mut bsmap: BTreeMap<DHTKey, Vec<DialInfo>> = BTreeMap::new(); let mut bsmap: BTreeMap<DHTKey, Vec<DialInfo>> = BTreeMap::new();
for b in bootstrap { for b in bootstrap {

View File

@ -311,6 +311,131 @@ pub async fn test_sleep() {
} }
} }
macro_rules! assert_split_url {
($url:expr, $scheme:expr, $host:expr) => {
assert_eq!(
SplitUrl::from_str($url),
Ok(SplitUrl::new($scheme, None, $host, None, None))
);
};
($url:expr, $scheme:expr, $host:expr, $port:expr) => {
assert_eq!(
SplitUrl::from_str($url),
Ok(SplitUrl::new($scheme, None, $host, $port, None))
);
};
($url:expr, $scheme:expr, $host:expr, $port:expr, $path:expr) => {
assert_eq!(
SplitUrl::from_str($url),
Ok(SplitUrl::new(
$scheme,
None,
$host,
$port,
Some(SplitUrlPath::new(
$path,
Option::<String>::None,
Option::<String>::None
))
))
);
};
($url:expr, $scheme:expr, $host:expr, $port:expr, $path:expr, $frag:expr, $query:expr) => {
assert_eq!(
SplitUrl::from_str($url),
Ok(SplitUrl::new(
$scheme,
None,
$host,
$port,
Some(SplitUrlPath::new($path, $frag, $query))
))
);
};
}
macro_rules! assert_split_url_parse {
($url:expr) => {
let url = $url;
let su1 = SplitUrl::from_str(url).expect("should parse");
assert_eq!(su1.to_string(), url);
};
}
macro_rules! assert_err {
($ex:expr) => {
if let Ok(v) = $ex {
panic!("assertion failed, expected Err(..), got {:?}", v);
}
};
}
pub async fn test_split_url() {
info!("testing split_url");
assert_split_url!("http://foo", "http", "foo");
assert_split_url!("http://foo:1234", "http", "foo", Some(1234));
assert_split_url!("http://foo:1234/", "http", "foo", Some(1234), "");
assert_split_url!(
"http://foo:1234/asdf/qwer",
"http",
"foo",
Some(1234),
"asdf/qwer"
);
assert_split_url!("http://foo/", "http", "foo", None, "");
assert_split_url!("http://foo/asdf/qwer", "http", "foo", None, "asdf/qwer");
assert_split_url!(
"http://foo/asdf/qwer#3",
"http",
"foo",
None,
"asdf/qwer",
Some("3"),
Option::<String>::None
);
assert_split_url!(
"http://foo/asdf/qwer?xxx",
"http",
"foo",
None,
"asdf/qwer",
Option::<String>::None,
Some("xxx")
);
assert_split_url!(
"http://foo/asdf/qwer#yyy?xxx",
"http",
"foo",
None,
"asdf/qwer",
Some("yyy"),
Some("xxx")
);
assert_err!(SplitUrl::from_str("://asdf"));
assert_err!(SplitUrl::from_str(""));
assert_err!(SplitUrl::from_str("::"));
assert_err!(SplitUrl::from_str("://:"));
assert_err!(SplitUrl::from_str("a://:"));
assert_err!(SplitUrl::from_str("a://:1243"));
assert_err!(SplitUrl::from_str("a://:65536"));
assert_err!(SplitUrl::from_str("a://:-16"));
assert_err!(SplitUrl::from_str("a:///"));
assert_err!(SplitUrl::from_str("a:///qwer:"));
assert_err!(SplitUrl::from_str("a:///qwer://"));
assert_err!(SplitUrl::from_str("a://qwer://"));
assert_split_url_parse!("sch://foo:bar@baz.com:1234/fnord#qux?zuz");
assert_split_url_parse!("sch://foo:bar@baz.com:1234/fnord#qux");
assert_split_url_parse!("sch://foo:bar@baz.com:1234/fnord?zuz");
assert_split_url_parse!("sch://foo:bar@baz.com:1234/fnord/");
assert_split_url_parse!("sch://foo:bar@baz.com:1234//");
assert_split_url_parse!("sch://foo:bar@baz.com:1234");
assert_split_url_parse!("sch://@baz.com:1234");
assert_split_url_parse!("sch://baz.com/asdf/asdf");
assert_split_url_parse!("sch://baz.com/");
assert_split_url_parse!("s://s");
}
pub async fn test_protected_store() { pub async fn test_protected_store() {
info!("testing protected store"); info!("testing protected store");
@ -518,6 +643,7 @@ pub async fn test_all() {
test_log().await; test_log().await;
test_get_timestamp().await; test_get_timestamp().await;
test_tools().await; test_tools().await;
test_split_url().await;
test_get_random_u64().await; test_get_random_u64().await;
test_get_random_u32().await; test_get_random_u32().await;
test_sleep().await; test_sleep().await;

View File

@ -189,13 +189,16 @@ pub fn config_callback(key: String) -> Result<Box<dyn core::any::Any>, String> {
"network.tls.certificate_path" => Ok(Box::new(get_certfile_path())), "network.tls.certificate_path" => Ok(Box::new(get_certfile_path())),
"network.tls.private_key_path" => Ok(Box::new(get_keyfile_path())), "network.tls.private_key_path" => Ok(Box::new(get_keyfile_path())),
"network.tls.connection_initial_timeout" => Ok(Box::new(2_000_000u64)), "network.tls.connection_initial_timeout" => Ok(Box::new(2_000_000u64)),
"network.application.path" => Ok(Box::new(String::from("/app"))), "network.application.https.enabled" => Ok(Box::new(false)),
"network.application.https.enabled" => Ok(Box::new(true)),
"network.application.https.listen_address" => Ok(Box::new(String::from("[::1]:5150"))), "network.application.https.listen_address" => Ok(Box::new(String::from("[::1]:5150"))),
"network.application.http.enabled" => Ok(Box::new(true)), "network.application.https.path" => Ok(Box::new(String::from("app"))),
"network.application.https.url" => Ok(Box::new(Option::<String>::None)),
"network.application.http.enabled" => Ok(Box::new(false)),
"network.application.http.listen_address" => Ok(Box::new(String::from("[::1]:5150"))), "network.application.http.listen_address" => Ok(Box::new(String::from("[::1]:5150"))),
"network.application.http.path" => Ok(Box::new(String::from("app"))),
"network.application.http.url" => Ok(Box::new(Option::<String>::None)),
"network.protocol.udp.enabled" => Ok(Box::new(true)), "network.protocol.udp.enabled" => Ok(Box::new(true)),
"network.protocol.udp.socket_pool_size" => Ok(Box::new(0u32)), "network.protocol.udp.socket_pool_size" => Ok(Box::new(16u32)),
"network.protocol.udp.listen_address" => Ok(Box::new(String::from("[::1]:5150"))), "network.protocol.udp.listen_address" => Ok(Box::new(String::from("[::1]:5150"))),
"network.protocol.udp.public_address" => Ok(Box::new(Option::<String>::None)), "network.protocol.udp.public_address" => Ok(Box::new(Option::<String>::None)),
"network.protocol.tcp.connect" => Ok(Box::new(true)), "network.protocol.tcp.connect" => Ok(Box::new(true)),
@ -203,23 +206,27 @@ pub fn config_callback(key: String) -> Result<Box<dyn core::any::Any>, String> {
"network.protocol.tcp.max_connections" => Ok(Box::new(32u32)), "network.protocol.tcp.max_connections" => Ok(Box::new(32u32)),
"network.protocol.tcp.listen_address" => Ok(Box::new(String::from("[::1]:5150"))), "network.protocol.tcp.listen_address" => Ok(Box::new(String::from("[::1]:5150"))),
"network.protocol.tcp.public_address" => Ok(Box::new(Option::<String>::None)), "network.protocol.tcp.public_address" => Ok(Box::new(Option::<String>::None)),
"network.protocol.ws.connect" => Ok(Box::new(true)), "network.protocol.ws.connect" => Ok(Box::new(false)),
"network.protocol.ws.listen" => Ok(Box::new(true)), "network.protocol.ws.listen" => Ok(Box::new(false)),
"network.protocol.ws.max_connections" => Ok(Box::new(16u32)), "network.protocol.ws.max_connections" => Ok(Box::new(16u32)),
"network.protocol.ws.listen_address" => Ok(Box::new(String::from("[::1]:5150"))), "network.protocol.ws.listen_address" => Ok(Box::new(String::from("[::1]:5150"))),
"network.protocol.ws.path" => Ok(Box::new(String::from("/ws"))), "network.protocol.ws.path" => Ok(Box::new(String::from("ws"))),
"network.protocol.ws.public_address" => Ok(Box::new(Option::<String>::None)), "network.protocol.ws.url" => Ok(Box::new(Option::<String>::None)),
"network.protocol.wss.connect" => Ok(Box::new(true)), "network.protocol.wss.connect" => Ok(Box::new(false)),
"network.protocol.wss.listen" => Ok(Box::new(true)), "network.protocol.wss.listen" => Ok(Box::new(false)),
"network.protocol.wss.max_connections" => Ok(Box::new(16u32)), "network.protocol.wss.max_connections" => Ok(Box::new(16u32)),
"network.protocol.wss.listen_address" => Ok(Box::new(String::from("[::1]:5150"))), "network.protocol.wss.listen_address" => Ok(Box::new(String::from("[::1]:5150"))),
"network.protocol.wss.path" => Ok(Box::new(String::from("/ws"))), "network.protocol.wss.path" => Ok(Box::new(String::from("ws"))),
"network.protocol.wss.public_address" => Ok(Box::new(Option::<String>::None)), "network.protocol.wss.url" => Ok(Box::new(Option::<String>::None)),
"network.leases.max_server_signal_leases" => Ok(Box::new(256u32)), "network.leases.max_server_signal_leases" => Ok(Box::new(256u32)),
"network.leases.max_server_relay_leases" => Ok(Box::new(8u32)), "network.leases.max_server_relay_leases" => Ok(Box::new(8u32)),
"network.leases.max_client_signal_leases" => Ok(Box::new(2u32)), "network.leases.max_client_signal_leases" => Ok(Box::new(2u32)),
"network.leases.max_client_relay_leases" => Ok(Box::new(2u32)), "network.leases.max_client_relay_leases" => Ok(Box::new(2u32)),
_ => Err(format!("config key '{}' doesn't exist", key)), _ => {
let err = format!("config key '{}' doesn't exist", key);
debug!("{}", err);
Err(err)
}
} }
} }
@ -278,13 +285,16 @@ pub async fn test_config() {
assert_eq!(inner.network.tls.private_key_path, get_keyfile_path()); assert_eq!(inner.network.tls.private_key_path, get_keyfile_path());
assert_eq!(inner.network.tls.connection_initial_timeout, 2_000_000u64); assert_eq!(inner.network.tls.connection_initial_timeout, 2_000_000u64);
assert_eq!(inner.network.application.path, "/app"); assert_eq!(inner.network.application.https.enabled, false);
assert_eq!(inner.network.application.https.enabled, true);
assert_eq!(inner.network.application.https.listen_address, "[::1]:5150"); assert_eq!(inner.network.application.https.listen_address, "[::1]:5150");
assert_eq!(inner.network.application.http.enabled, true); assert_eq!(inner.network.application.https.path, "app");
assert_eq!(inner.network.application.https.url, None);
assert_eq!(inner.network.application.http.enabled, false);
assert_eq!(inner.network.application.http.listen_address, "[::1]:5150"); assert_eq!(inner.network.application.http.listen_address, "[::1]:5150");
assert_eq!(inner.network.application.http.path, "app");
assert_eq!(inner.network.application.http.url, None);
assert_eq!(inner.network.protocol.udp.enabled, true); assert_eq!(inner.network.protocol.udp.enabled, true);
assert_eq!(inner.network.protocol.udp.socket_pool_size, 0u32); assert_eq!(inner.network.protocol.udp.socket_pool_size, 16u32);
assert_eq!(inner.network.protocol.udp.listen_address, "[::1]:5150"); assert_eq!(inner.network.protocol.udp.listen_address, "[::1]:5150");
assert_eq!(inner.network.protocol.udp.public_address, None); assert_eq!(inner.network.protocol.udp.public_address, None);
assert_eq!(inner.network.protocol.tcp.connect, true); assert_eq!(inner.network.protocol.tcp.connect, true);
@ -292,18 +302,18 @@ pub async fn test_config() {
assert_eq!(inner.network.protocol.tcp.max_connections, 32u32); assert_eq!(inner.network.protocol.tcp.max_connections, 32u32);
assert_eq!(inner.network.protocol.tcp.listen_address, "[::1]:5150"); assert_eq!(inner.network.protocol.tcp.listen_address, "[::1]:5150");
assert_eq!(inner.network.protocol.tcp.public_address, None); assert_eq!(inner.network.protocol.tcp.public_address, None);
assert_eq!(inner.network.protocol.ws.connect, true); assert_eq!(inner.network.protocol.ws.connect, false);
assert_eq!(inner.network.protocol.ws.listen, true); assert_eq!(inner.network.protocol.ws.listen, false);
assert_eq!(inner.network.protocol.ws.max_connections, 16u32); assert_eq!(inner.network.protocol.ws.max_connections, 16u32);
assert_eq!(inner.network.protocol.ws.listen_address, "[::1]:5150"); assert_eq!(inner.network.protocol.ws.listen_address, "[::1]:5150");
assert_eq!(inner.network.protocol.ws.path, "/ws"); assert_eq!(inner.network.protocol.ws.path, "ws");
assert_eq!(inner.network.protocol.ws.public_address, None); assert_eq!(inner.network.protocol.ws.url, None);
assert_eq!(inner.network.protocol.wss.connect, true); assert_eq!(inner.network.protocol.wss.connect, false);
assert_eq!(inner.network.protocol.wss.listen, true); assert_eq!(inner.network.protocol.wss.listen, false);
assert_eq!(inner.network.protocol.wss.max_connections, 16u32); assert_eq!(inner.network.protocol.wss.max_connections, 16u32);
assert_eq!(inner.network.protocol.wss.listen_address, "[::1]:5150"); assert_eq!(inner.network.protocol.wss.listen_address, "[::1]:5150");
assert_eq!(inner.network.protocol.wss.path, "/ws"); assert_eq!(inner.network.protocol.wss.path, "ws");
assert_eq!(inner.network.protocol.wss.public_address, None); assert_eq!(inner.network.protocol.wss.url, None);
} }
pub async fn test_all() { pub async fn test_all() {

View File

@ -385,7 +385,7 @@ impl DialInfo {
let addr: IpAddr = di let addr: IpAddr = di
.fqdn .fqdn
.parse() .parse()
.map_err(|e| format!("Failed to parse WS fqdn: {}", e))?; .map_err(|e| format!("Failed to parse WSS fqdn: {}", e))?;
Ok(addr) Ok(addr)
} }
} }
@ -896,7 +896,7 @@ impl fmt::Debug for VeilidAPIInner {
impl Drop for VeilidAPIInner { impl Drop for VeilidAPIInner {
fn drop(&mut self) { fn drop(&mut self) {
if let Some(core) = self.core.take() { if let Some(core) = self.core.take() {
intf::spawn_local(core.internal_shutdown()).detach(); intf::spawn_local(core.shutdown()).detach();
} }
} }
} }
@ -953,7 +953,7 @@ impl VeilidAPI {
pub async fn shutdown(self) { pub async fn shutdown(self) {
let core = { self.inner.lock().core.take() }; let core = { self.inner.lock().core.take() };
if let Some(core) = core { if let Some(core) = core {
core.internal_shutdown().await; core.shutdown().await;
} }
} }

View File

@ -14,17 +14,20 @@ cfg_if! {
pub struct VeilidConfigHTTPS { pub struct VeilidConfigHTTPS {
pub enabled: bool, pub enabled: bool,
pub listen_address: String, pub listen_address: String,
pub path: String,
pub url: Option<String>, // Fixed URL is not optional for TLS-based protocols and is dynamically validated
} }
#[derive(Default, Clone)] #[derive(Default, Clone)]
pub struct VeilidConfigHTTP { pub struct VeilidConfigHTTP {
pub enabled: bool, pub enabled: bool,
pub listen_address: String, pub listen_address: String,
pub path: String,
pub url: Option<String>,
} }
#[derive(Default, Clone)] #[derive(Default, Clone)]
pub struct VeilidConfigApplication { pub struct VeilidConfigApplication {
pub path: String,
pub https: VeilidConfigHTTPS, pub https: VeilidConfigHTTPS,
pub http: VeilidConfigHTTP, pub http: VeilidConfigHTTP,
} }
@ -53,7 +56,7 @@ pub struct VeilidConfigWS {
pub max_connections: u32, pub max_connections: u32,
pub listen_address: String, pub listen_address: String,
pub path: String, pub path: String,
pub public_address: Option<String>, pub url: Option<String>,
} }
#[derive(Default, Clone)] #[derive(Default, Clone)]
@ -63,7 +66,7 @@ pub struct VeilidConfigWSS {
pub max_connections: u32, pub max_connections: u32,
pub listen_address: String, pub listen_address: String,
pub path: String, pub path: String,
pub public_address: Option<String>, pub url: Option<String>, // Fixed URL is not optional for TLS-based protocols and is dynamically validated
} }
#[derive(Default, Clone)] #[derive(Default, Clone)]
@ -184,9 +187,11 @@ impl VeilidConfig {
macro_rules! get_config { macro_rules! get_config {
($key:expr) => { ($key:expr) => {
let keyname = &stringify!($key)[6..]; let keyname = &stringify!($key)[6..];
$key = *cb(keyname.to_owned())? $key = *cb(keyname.to_owned())?.downcast().map_err(|_| {
.downcast() let err = format!("incorrect type for key: {}", keyname);
.map_err(|_| format!("incorrect type for key: {}", keyname))?; debug!("{}", err);
err
})?;
}; };
} }
@ -232,11 +237,14 @@ impl VeilidConfig {
get_config!(inner.network.tls.certificate_path); get_config!(inner.network.tls.certificate_path);
get_config!(inner.network.tls.private_key_path); get_config!(inner.network.tls.private_key_path);
get_config!(inner.network.tls.connection_initial_timeout); get_config!(inner.network.tls.connection_initial_timeout);
get_config!(inner.network.application.path);
get_config!(inner.network.application.https.enabled); get_config!(inner.network.application.https.enabled);
get_config!(inner.network.application.https.listen_address); get_config!(inner.network.application.https.listen_address);
get_config!(inner.network.application.https.path);
get_config!(inner.network.application.https.url);
get_config!(inner.network.application.http.enabled); get_config!(inner.network.application.http.enabled);
get_config!(inner.network.application.http.listen_address); get_config!(inner.network.application.http.listen_address);
get_config!(inner.network.application.http.path);
get_config!(inner.network.application.http.url);
get_config!(inner.network.protocol.udp.enabled); get_config!(inner.network.protocol.udp.enabled);
get_config!(inner.network.protocol.udp.socket_pool_size); get_config!(inner.network.protocol.udp.socket_pool_size);
get_config!(inner.network.protocol.udp.listen_address); get_config!(inner.network.protocol.udp.listen_address);
@ -251,13 +259,13 @@ impl VeilidConfig {
get_config!(inner.network.protocol.ws.max_connections); get_config!(inner.network.protocol.ws.max_connections);
get_config!(inner.network.protocol.ws.listen_address); get_config!(inner.network.protocol.ws.listen_address);
get_config!(inner.network.protocol.ws.path); get_config!(inner.network.protocol.ws.path);
get_config!(inner.network.protocol.ws.public_address); get_config!(inner.network.protocol.ws.url);
get_config!(inner.network.protocol.wss.connect); get_config!(inner.network.protocol.wss.connect);
get_config!(inner.network.protocol.wss.listen); get_config!(inner.network.protocol.wss.listen);
get_config!(inner.network.protocol.wss.max_connections); get_config!(inner.network.protocol.wss.max_connections);
get_config!(inner.network.protocol.wss.listen_address); get_config!(inner.network.protocol.wss.listen_address);
get_config!(inner.network.protocol.wss.path); get_config!(inner.network.protocol.wss.path);
get_config!(inner.network.protocol.wss.public_address); get_config!(inner.network.protocol.wss.url);
get_config!(inner.network.leases.max_server_signal_leases); get_config!(inner.network.leases.max_server_signal_leases);
get_config!(inner.network.leases.max_server_relay_leases); get_config!(inner.network.leases.max_server_relay_leases);
get_config!(inner.network.leases.max_client_signal_leases); get_config!(inner.network.leases.max_client_signal_leases);
@ -266,7 +274,12 @@ impl VeilidConfig {
// Initialize node id as early as possible because it is used // Initialize node id as early as possible because it is used
// for encryption purposes all over the program // for encryption purposes all over the program
self.init_node_id().await self.init_node_id().await?;
// Validate settings
self.validate().await?;
Ok(())
} }
pub async fn terminate(&self) { pub async fn terminate(&self) {
@ -277,6 +290,85 @@ impl VeilidConfig {
self.inner.read() self.inner.read()
} }
async fn validate(&self) -> Result<(), String> {
let inner = self.inner.read();
if inner.network.protocol.udp.enabled {
// Validate UDP settings
if inner.network.protocol.udp.socket_pool_size == 0 {
return Err("UDP socket pool size must be > 0 in config key 'network.protocol.udp.socket_pool_size'".to_owned());
}
}
if inner.network.protocol.tcp.listen {
// Validate TCP settings
if inner.network.protocol.tcp.max_connections == 0 {
return Err("TCP max connections must be > 0 in config key 'network.protocol.tcp.max_connections'".to_owned());
}
}
if inner.network.protocol.ws.listen {
// Validate WS settings
if inner.network.protocol.ws.max_connections == 0 {
return Err("WS max connections must be > 0 in config key 'network.protocol.ws.max_connections'".to_owned());
}
if inner.network.application.https.enabled
&& inner.network.application.https.path == inner.network.protocol.ws.path
{
return Err("WS path conflicts with HTTPS application path in config key 'network.protocol.ws.path'".to_owned());
}
if inner.network.application.http.enabled
&& inner.network.application.http.path == inner.network.protocol.ws.path
{
return Err("WS path conflicts with HTTP application path in config key 'network.protocol.ws.path'".to_owned());
}
}
if inner.network.protocol.wss.listen {
// Validate WSS settings
if inner.network.protocol.wss.max_connections == 0 {
return Err("WSS max connections must be > 0 in config key 'network.protocol.wss.max_connections'".to_owned());
}
if inner
.network
.protocol
.wss
.url
.as_ref()
.map(|u| u.is_empty())
.unwrap_or_default()
{
return Err(
"WSS URL must be specified in config key 'network.protocol.wss.url'".to_owned(),
);
}
if inner.network.application.https.enabled
&& inner.network.application.https.path == inner.network.protocol.wss.path
{
return Err("WSS path conflicts with HTTPS application path in config key 'network.protocol.ws.path'".to_owned());
}
if inner.network.application.http.enabled
&& inner.network.application.http.path == inner.network.protocol.wss.path
{
return Err("WSS path conflicts with HTTP application path in config key 'network.protocol.ws.path'".to_owned());
}
}
if inner.network.application.https.enabled {
// Validate HTTPS settings
if inner
.network
.application
.https
.url
.as_ref()
.map(|u| u.is_empty())
.unwrap_or_default()
{
return Err(
"HTTPS URL must be specified in config key 'network.application.https.url'"
.to_owned(),
);
}
}
Ok(())
}
// Get the node id from config if one is specified // Get the node id from config if one is specified
async fn init_node_id(&self) -> Result<(), String> { async fn init_node_id(&self) -> Result<(), String> {
let mut inner = self.inner.write(); let mut inner = self.inner.write();

View File

@ -163,15 +163,13 @@ impl VeilidCore {
match self.internal_startup(&mut *inner, setup).await { match self.internal_startup(&mut *inner, setup).await {
Ok(v) => Ok(v), Ok(v) => Ok(v),
Err(e) => { Err(e) => {
self.clone().internal_shutdown().await; Self::internal_shutdown(&mut *inner).await;
Err(e) Err(e)
} }
} }
} }
// stop the node gracefully because the veilid api was dropped async fn internal_shutdown(inner: &mut VeilidCoreInner) {
pub(crate) async fn internal_shutdown(self) {
let mut inner = self.inner.lock();
trace!("VeilidCore::internal_shutdown starting"); trace!("VeilidCore::internal_shutdown starting");
// Detach the API object // Detach the API object
@ -204,5 +202,11 @@ impl VeilidCore {
trace!("VeilidCore::shutdown complete"); trace!("VeilidCore::shutdown complete");
} }
// stop the node gracefully because the veilid api was dropped
pub(crate) async fn shutdown(self) {
let mut inner = self.inner.lock();
Self::internal_shutdown(&mut *inner);
}
// //
} }

View File

@ -7,12 +7,14 @@ mod ip_addr_port;
mod ip_extra; mod ip_extra;
mod single_future; mod single_future;
mod single_shot_eventual; mod single_shot_eventual;
mod split_url;
mod tick_task; mod tick_task;
mod tools; mod tools;
pub use cfg_if::*; pub use cfg_if::*;
pub use log::*; pub use log::*;
pub use parking_lot::*; pub use parking_lot::*;
pub use split_url::*;
pub use static_assertions::*; pub use static_assertions::*;
pub type PinBox<T> = Pin<Box<T>>; pub type PinBox<T> = Pin<Box<T>>;

View File

@ -0,0 +1,325 @@
// Loose subset interpretation of the URL standard
// Not using full Url crate here for no_std compatibility
//
// Caveats:
// No support for query string parsing
// No support for paths with ';' parameters
// URLs must convert to UTF8
// Only IP address and DNS hostname host fields are supported
use super::IpAddr;
use core::fmt;
use core::str::FromStr;
fn is_alphanum(c: u8) -> bool {
matches!(c,
b'A'..=b'Z'
| b'a'..=b'z'
| b'0'..=b'9'
)
}
fn is_mark(c: u8) -> bool {
matches!(
c,
b'-' | b'_' | b'.' | b'!' | b'~' | b'*' | b'\'' | b'(' | b')'
)
}
fn is_unreserved(c: u8) -> bool {
is_alphanum(c) || is_mark(c)
}
fn must_encode_userinfo(c: u8) -> bool {
!(is_unreserved(c) || matches!(c, b'%' | b':' | b';' | b'&' | b'=' | b'+' | b'$' | b','))
}
fn must_encode_path(c: u8) -> bool {
!(is_unreserved(c)
|| matches!(
c,
b'%' | b'/' | b':' | b'@' | b'&' | b'=' | b'+' | b'$' | b','
))
}
fn is_valid_host<H: AsRef<str>>(host: H) -> bool {
if host.as_ref().is_empty() {
return false;
}
if IpAddr::from_str(host.as_ref()).is_err() {
for ch in host.as_ref().chars() {
if !matches!(ch,
'A'..='Z' | 'a'..='z' | '0'..='9' | '-' | '.' )
{
return false;
}
}
}
true
}
fn is_valid_scheme<H: AsRef<str>>(host: H) -> bool {
let mut chars = host.as_ref().chars();
if let Some(ch) = chars.next() {
if !matches!(ch, 'A'..='Z' | 'a'..='z') {
return false;
}
} else {
return false;
}
for ch in chars {
if !matches!(ch,
'A'..='Z' | 'a'..='z' | '0'..='9' | '-' | '+' | '.' )
{
return false;
}
}
true
}
fn hex_decode(h: u8) -> Result<u8, String> {
match h {
b'0'..=b'9' => Ok(h - b'0'),
b'A'..=b'F' => Ok(h - b'A' + 10),
b'a'..=b'f' => Ok(h - b'a' + 10),
_ => Err("Unexpected character in percent encoding".to_owned()),
}
}
fn hex_encode(c: u8) -> (char, char) {
let c0 = c >> 4;
let c1 = c & 15;
(
if c0 < 10 {
char::from_u32((b'0' + c0) as u32).unwrap()
} else {
char::from_u32((b'A' + c0 - 10) as u32).unwrap()
},
if c1 < 10 {
char::from_u32((b'0' + c1) as u32).unwrap()
} else {
char::from_u32((b'A' + c1 - 10) as u32).unwrap()
},
)
}
fn url_decode<S: AsRef<str>>(s: S) -> Result<String, String> {
let url = s.as_ref().to_owned();
if !url.is_ascii() {
return Err("URL is not in ASCII encoding".to_owned());
}
let url_bytes = url.as_bytes();
let mut dec_bytes: Vec<u8> = Vec::with_capacity(url_bytes.len());
let mut i = 0;
let end = url_bytes.len();
while i < end {
let mut b = url_bytes[i];
i += 1;
if b == b'%' {
if (i + 1) >= end {
return Err("Invalid URL encoding".to_owned());
}
b = hex_decode(url_bytes[i])? << 4 | hex_decode(url_bytes[i + 1])?;
i += 2;
}
dec_bytes.push(b);
}
String::from_utf8(dec_bytes).map_err(|e| format!("Decoded URL is not valid UTF-8: {}", e))
}
fn url_encode<S: AsRef<str>>(s: S, must_encode: impl Fn(u8) -> bool) -> String {
let bytes = s.as_ref().as_bytes();
let mut out = String::new();
for b in bytes {
if must_encode(*b) {
let (c0, c1) = hex_encode(*b);
out.push('%');
out.push(c0);
out.push(c1);
} else {
out.push(char::from_u32(*b as u32).unwrap())
}
}
out
}
fn convert_port<N>(port_str: N) -> Result<u16, String>
where
N: AsRef<str>,
{
port_str
.as_ref()
.parse::<u16>()
.map_err(|e| format!("Invalid port: {}", e))
}
///////////////////////////////////////////////////////////////////////////////
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct SplitUrlPath {
pub path: String,
pub fragment: Option<String>,
pub query: Option<String>,
}
impl SplitUrlPath {
pub fn new<P, F, Q>(path: P, fragment: Option<F>, query: Option<Q>) -> Self
where
P: AsRef<str>,
F: AsRef<str>,
Q: AsRef<str>,
{
Self {
path: path.as_ref().to_owned(),
fragment: fragment.map(|f| f.as_ref().to_owned()),
query: query.map(|f| f.as_ref().to_owned()),
}
}
}
impl FromStr for SplitUrlPath {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(if let Some((p, q)) = s.split_once('?') {
if let Some((p, f)) = p.split_once('#') {
SplitUrlPath::new(url_decode(p)?, Some(url_decode(f)?), Some(q))
} else {
SplitUrlPath::new(url_decode(p)?, Option::<String>::None, Some(q))
}
} else if let Some((p, f)) = s.split_once('#') {
SplitUrlPath::new(url_decode(p)?, Some(url_decode(f)?), Option::<String>::None)
} else {
SplitUrlPath::new(
url_decode(s)?,
Option::<String>::None,
Option::<String>::None,
)
})
}
}
impl fmt::Display for SplitUrlPath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(fragment) = &self.fragment {
if let Some(query) = &self.query {
write!(
f,
"{}#{}?{}",
url_encode(&self.path, must_encode_path),
url_encode(fragment, must_encode_path),
query
)
} else {
write!(f, "{}#{}", self.path, fragment)
}
} else if let Some(query) = &self.query {
write!(f, "{}?{}", url_encode(&self.path, must_encode_path), query)
} else {
write!(f, "{}", url_encode(&self.path, must_encode_path))
}
}
}
///////////////////////////////////////////////////////////////////////////////
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct SplitUrl {
pub scheme: String,
pub userinfo: Option<String>,
pub host: String,
pub port: Option<u16>,
pub path: Option<SplitUrlPath>,
}
impl SplitUrl {
pub fn new<S, H>(
scheme: S,
userinfo: Option<String>,
host: H,
port: Option<u16>,
path: Option<SplitUrlPath>,
) -> Self
where
S: AsRef<str>,
H: AsRef<str>,
{
Self {
scheme: scheme.as_ref().to_owned(),
userinfo,
host: host.as_ref().to_owned(),
port,
path,
}
}
}
impl FromStr for SplitUrl {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Some((scheme, mut rest)) = s.split_once("://") {
if !is_valid_scheme(scheme) {
return Err("Invalid scheme specified".to_owned());
}
let userinfo = {
if let Some((userinfo_str, after)) = rest.split_once("@") {
rest = after;
Some(url_decode(userinfo_str)?)
} else {
None
}
};
if let Some((host, rest)) = rest.rsplit_once(':') {
if !is_valid_host(host) {
return Err("Invalid host specified".to_owned());
}
if let Some((portstr, path)) = rest.split_once('/') {
let port = convert_port(portstr)?;
let path = SplitUrlPath::from_str(path)?;
Ok(SplitUrl::new(
scheme,
userinfo,
host,
Some(port),
Some(path),
))
} else {
let port = convert_port(rest)?;
Ok(SplitUrl::new(scheme, userinfo, host, Some(port), None))
}
} else if let Some((host, path)) = rest.split_once('/') {
if !is_valid_host(host) {
return Err("Invalid host specified".to_owned());
}
let path = SplitUrlPath::from_str(path)?;
Ok(SplitUrl::new(scheme, userinfo, host, None, Some(path)))
} else {
if !is_valid_host(rest) {
return Err("Invalid host specified".to_owned());
}
Ok(SplitUrl::new(scheme, userinfo, rest, None, None))
}
} else {
Err("No scheme specified".to_owned())
}
}
}
impl fmt::Display for SplitUrl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let hostname = {
if let Some(userinfo) = &self.userinfo {
let userinfo = url_encode(userinfo, must_encode_userinfo);
if let Some(port) = self.port {
format!("{}@{}:{}", userinfo, self.host, port)
} else {
format!("{}@{}", userinfo, self.host)
}
} else {
self.host.clone()
}
};
if let Some(path) = &self.path {
write!(f, "{}://{}/{}", self.scheme, hostname, path)
} else {
write!(f, "{}://{}", self.scheme, hostname)
}
}
}

View File

@ -70,13 +70,16 @@ core:
private_key_path: "/etc/veilid/private/server.key" private_key_path: "/etc/veilid/private/server.key"
connection_initial_timeout: 2000000 connection_initial_timeout: 2000000
application: application:
path: "app"
https: https:
enabled: true enabled: false
listen_address: "[::]:5150" listen_address: "[::]:5150"
path: "app"
# url: "https://localhost:5150"
http: http:
enabled: true enabled: false
listen_address: "[::]:5150" listen_address: "[::]:5150"
path: "app"
# url: "http://localhost:5150"
protocol: protocol:
udp: udp:
enabled: true enabled: true
@ -94,15 +97,15 @@ core:
listen: true listen: true
max_connections: 16 max_connections: 16
listen_address: "[::]:5150" listen_address: "[::]:5150"
path: "/ws" path: "ws"
# "public_address": "" # url: "ws://localhost:5150/ws"
wss: wss:
connect: true connect: true
listen: true listen: false
max_connections: 16 max_connections: 16
listen_address: "[::]:5150" listen_address: "[::]:5150"
path: "/ws" path: "ws"
# "public_address": "" # url: ""
leases: leases:
max_server_signal_leases: 256 max_server_signal_leases: 256
max_server_relay_leases: 8 max_server_relay_leases: 8
@ -173,30 +176,47 @@ pub fn convert_loglevel(log_level: LogLevel) -> LevelFilter {
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct ParsedURL { pub struct ParsedUrl {
pub urlstring: String, pub urlstring: String,
pub url: Url, pub url: Url,
} }
impl FromStr for ParsedURL { impl ParsedUrl {
type Err = url::ParseError; pub fn offset_port(&mut self, offset: u16) -> Result<(), ()> {
fn from_str(s: &str) -> Result<ParsedURL, url::ParseError> { // Bump port on url
let url = Url::parse(s)?; self.url.set_port(Some(self.url.port().unwrap() + offset))?;
self.urlstring = self.url.to_string();
Ok(())
}
}
impl FromStr for ParsedUrl {
type Err = url::ParseError;
fn from_str(s: &str) -> Result<ParsedUrl, url::ParseError> {
let mut url = Url::parse(s)?;
if url.scheme().to_lowercase() == "http" && url.port().is_none() {
url.set_port(Some(80))
.map_err(|_| url::ParseError::InvalidPort)?
}
if url.scheme().to_lowercase() == "https" && url.port().is_none() {
url.set_port(Some(443))
.map_err(|_| url::ParseError::InvalidPort)?;
}
let parsed_urlstring = url.to_string();
Ok(Self { Ok(Self {
urlstring: s.to_string(), urlstring: parsed_urlstring,
url, url,
}) })
} }
} }
impl<'de> serde::Deserialize<'de> for ParsedURL { impl<'de> serde::Deserialize<'de> for ParsedUrl {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where where
D: serde::Deserializer<'de>, D: serde::Deserializer<'de>,
{ {
let s = String::deserialize(deserializer)?; let s = String::deserialize(deserializer)?;
ParsedURL::from_str(s.as_str()).map_err(serde::de::Error::custom) ParsedUrl::from_str(s.as_str()).map_err(serde::de::Error::custom)
} }
} }
@ -279,17 +299,20 @@ pub struct Logging {
pub struct Https { pub struct Https {
pub enabled: bool, pub enabled: bool,
pub listen_address: NamedSocketAddrs, pub listen_address: NamedSocketAddrs,
pub path: PathBuf,
pub url: Option<ParsedUrl>,
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub struct Http { pub struct Http {
pub enabled: bool, pub enabled: bool,
pub listen_address: NamedSocketAddrs, pub listen_address: NamedSocketAddrs,
pub path: PathBuf,
pub url: Option<ParsedUrl>,
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub struct Application { pub struct Application {
pub path: PathBuf,
pub https: Https, pub https: Https,
pub http: Http, pub http: Http,
} }
@ -317,8 +340,8 @@ pub struct Ws {
pub listen: bool, pub listen: bool,
pub max_connections: u32, pub max_connections: u32,
pub listen_address: NamedSocketAddrs, pub listen_address: NamedSocketAddrs,
pub path: String, pub path: PathBuf,
pub public_address: Option<NamedSocketAddrs>, pub url: Option<ParsedUrl>,
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@ -327,8 +350,8 @@ pub struct Wss {
pub listen: bool, pub listen: bool,
pub max_connections: u32, pub max_connections: u32,
pub listen_address: NamedSocketAddrs, pub listen_address: NamedSocketAddrs,
pub path: String, pub path: PathBuf,
pub public_address: Option<NamedSocketAddrs>, pub url: Option<ParsedUrl>,
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@ -387,7 +410,7 @@ pub struct Network {
pub connection_initial_timeout: u64, pub connection_initial_timeout: u64,
pub node_id: veilid_core::DHTKey, pub node_id: veilid_core::DHTKey,
pub node_id_secret: veilid_core::DHTKeySecret, pub node_id_secret: veilid_core::DHTKeySecret,
pub bootstrap: Vec<ParsedURL>, pub bootstrap: Vec<ParsedUrl>,
pub rpc: Rpc, pub rpc: Rpc,
pub dht: Dht, pub dht: Dht,
pub upnp: bool, pub upnp: bool,
@ -450,8 +473,12 @@ impl Settings {
load_config(&mut cfg, config_file_path)?; load_config(&mut cfg, config_file_path)?;
} }
// Generate config
let inner: SettingsInner = cfg.try_into()?;
//
Ok(Self { Ok(Self {
inner: Arc::new(RwLock::new(cfg.try_into()?)), inner: Arc::new(RwLock::new(inner)),
}) })
} }
pub fn read(&self) -> RwLockReadGuard<SettingsInner> { pub fn read(&self) -> RwLockReadGuard<SettingsInner> {
@ -493,6 +520,9 @@ impl Settings {
.ws .ws
.listen_address .listen_address
.offset_port(idx)?; .offset_port(idx)?;
if let Some(url) = &mut (*settingsrw).core.network.protocol.ws.url {
url.offset_port(idx)?;
}
(*settingsrw) (*settingsrw)
.core .core
.network .network
@ -500,6 +530,9 @@ impl Settings {
.wss .wss
.listen_address .listen_address
.offset_port(idx)?; .offset_port(idx)?;
if let Some(url) = &mut (*settingsrw).core.network.protocol.wss.url {
url.offset_port(idx)?;
}
// bump application ports // bump application ports
(*settingsrw) (*settingsrw)
.core .core
@ -508,6 +541,9 @@ impl Settings {
.http .http
.listen_address .listen_address
.offset_port(idx)?; .offset_port(idx)?;
if let Some(url) = &mut (*settingsrw).core.network.application.http.url {
url.offset_port(idx)?;
}
(*settingsrw) (*settingsrw)
.core .core
.network .network
@ -515,7 +551,9 @@ impl Settings {
.https .https
.listen_address .listen_address
.offset_port(idx)?; .offset_port(idx)?;
if let Some(url) = &mut (*settingsrw).core.network.application.https.url {
url.offset_port(idx)?;
}
Ok(()) Ok(())
} }
@ -665,15 +703,6 @@ impl Settings {
"network.tls.connection_initial_timeout" => { "network.tls.connection_initial_timeout" => {
Ok(Box::new(inner.core.network.tls.connection_initial_timeout)) Ok(Box::new(inner.core.network.tls.connection_initial_timeout))
} }
"network.application.path" => Ok(Box::new(
inner
.core
.network
.application
.path
.to_string_lossy()
.to_string(),
)),
"network.application.https.enabled" => { "network.application.https.enabled" => {
Ok(Box::new(inner.core.network.application.https.enabled)) Ok(Box::new(inner.core.network.application.https.enabled))
} }
@ -687,6 +716,26 @@ impl Settings {
.name .name
.clone(), .clone(),
)), )),
"network.application.https.path" => Ok(Box::new(
inner
.core
.network
.application
.https
.path
.to_string_lossy()
.to_string(),
)),
"network.application.https.url" => Ok(Box::new(
inner
.core
.network
.application
.https
.url
.as_ref()
.map(|a| a.urlstring.clone()),
)),
"network.application.http.enabled" => { "network.application.http.enabled" => {
Ok(Box::new(inner.core.network.application.http.enabled)) Ok(Box::new(inner.core.network.application.http.enabled))
} }
@ -700,6 +749,26 @@ impl Settings {
.name .name
.clone(), .clone(),
)), )),
"network.application.http.path" => Ok(Box::new(
inner
.core
.network
.application
.http
.path
.to_string_lossy()
.to_string(),
)),
"network.application.http.url" => Ok(Box::new(
inner
.core
.network
.application
.http
.url
.as_ref()
.map(|a| a.urlstring.clone()),
)),
"network.protocol.udp.enabled" => { "network.protocol.udp.enabled" => {
Ok(Box::new(inner.core.network.protocol.udp.enabled)) Ok(Box::new(inner.core.network.protocol.udp.enabled))
} }
@ -751,18 +820,25 @@ impl Settings {
"network.protocol.ws.listen_address" => Ok(Box::new( "network.protocol.ws.listen_address" => Ok(Box::new(
inner.core.network.protocol.ws.listen_address.name.clone(), inner.core.network.protocol.ws.listen_address.name.clone(),
)), )),
"network.protocol.ws.path" => { "network.protocol.ws.path" => Ok(Box::new(
Ok(Box::new(inner.core.network.protocol.ws.path.clone()))
}
"network.protocol.ws.public_address" => Ok(Box::new(
inner inner
.core .core
.network .network
.protocol .protocol
.ws .ws
.public_address .path
.to_string_lossy()
.to_string(),
)),
"network.protocol.ws.url" => Ok(Box::new(
inner
.core
.network
.protocol
.ws
.url
.as_ref() .as_ref()
.map(|a| a.name.clone()), .map(|a| a.urlstring.clone()),
)), )),
"network.protocol.wss.connect" => { "network.protocol.wss.connect" => {
Ok(Box::new(inner.core.network.protocol.wss.connect)) Ok(Box::new(inner.core.network.protocol.wss.connect))
@ -776,19 +852,19 @@ impl Settings {
"network.protocol.wss.listen_address" => Ok(Box::new( "network.protocol.wss.listen_address" => Ok(Box::new(
inner.core.network.protocol.wss.listen_address.name.clone(), inner.core.network.protocol.wss.listen_address.name.clone(),
)), )),
"network.protocol.wss.path" => { "network.protocol.wss.path" => Ok(Box::new(
Ok(Box::new(inner.core.network.protocol.wss.path.clone()))
}
"network.protocol.wss.public_address" => Ok(Box::new(
inner inner
.core .core
.network .network
.protocol .protocol
.wss .wss
.public_address .path
.as_ref() .to_string_lossy()
.map(|a| a.name.clone()), .to_string(),
)), )),
"network.protocol.wss.url" => {
Ok(Box::new(inner.core.network.protocol.wss.url.clone()))
}
"network.leases.max_server_signal_leases" => { "network.leases.max_server_signal_leases" => {
Ok(Box::new(inner.core.network.leases.max_server_signal_leases)) Ok(Box::new(inner.core.network.leases.max_server_signal_leases))
} }
@ -899,11 +975,7 @@ mod tests {
); );
assert_eq!(s.core.network.tls.connection_initial_timeout, 2_000_000u64); assert_eq!(s.core.network.tls.connection_initial_timeout, 2_000_000u64);
// //
assert_eq!( assert_eq!(s.core.network.application.https.enabled, false);
s.core.network.application.path,
std::path::PathBuf::from("app")
);
assert_eq!(s.core.network.application.https.enabled, true);
assert_eq!( assert_eq!(
s.core.network.application.https.listen_address.name, s.core.network.application.https.listen_address.name,
"[::]:5150" "[::]:5150"
@ -915,7 +987,12 @@ mod tests {
.unwrap() .unwrap()
.collect::<Vec<SocketAddr>>() .collect::<Vec<SocketAddr>>()
); );
assert_eq!(s.core.network.application.http.enabled, true); assert_eq!(
s.core.network.application.https.path,
std::path::PathBuf::from("app")
);
assert_eq!(s.core.network.application.https.url, None);
assert_eq!(s.core.network.application.http.enabled, false);
assert_eq!( assert_eq!(
s.core.network.application.http.listen_address.name, s.core.network.application.http.listen_address.name,
"[::]:5150" "[::]:5150"
@ -927,6 +1004,11 @@ mod tests {
.unwrap() .unwrap()
.collect::<Vec<SocketAddr>>() .collect::<Vec<SocketAddr>>()
); );
assert_eq!(
s.core.network.application.http.path,
std::path::PathBuf::from("app")
);
assert_eq!(s.core.network.application.http.url, None);
// //
assert_eq!(s.core.network.protocol.udp.enabled, true); assert_eq!(s.core.network.protocol.udp.enabled, true);
assert_eq!(s.core.network.protocol.udp.socket_pool_size, 0); assert_eq!(s.core.network.protocol.udp.socket_pool_size, 0);
@ -966,11 +1048,14 @@ mod tests {
.unwrap() .unwrap()
.collect::<Vec<SocketAddr>>() .collect::<Vec<SocketAddr>>()
); );
assert_eq!(s.core.network.protocol.ws.path, "/ws"); assert_eq!(
assert_eq!(s.core.network.protocol.ws.public_address, None); s.core.network.protocol.ws.path,
std::path::PathBuf::from("ws")
);
assert_eq!(s.core.network.protocol.ws.url, None);
// //
assert_eq!(s.core.network.protocol.wss.connect, true); assert_eq!(s.core.network.protocol.wss.connect, true);
assert_eq!(s.core.network.protocol.wss.listen, true); assert_eq!(s.core.network.protocol.wss.listen, false);
assert_eq!(s.core.network.protocol.wss.max_connections, 16); assert_eq!(s.core.network.protocol.wss.max_connections, 16);
assert_eq!(s.core.network.protocol.wss.listen_address.name, "[::]:5150"); assert_eq!(s.core.network.protocol.wss.listen_address.name, "[::]:5150");
assert_eq!( assert_eq!(
@ -980,9 +1065,12 @@ mod tests {
.unwrap() .unwrap()
.collect::<Vec<SocketAddr>>() .collect::<Vec<SocketAddr>>()
); );
assert_eq!(s.core.network.protocol.wss.path, "/ws"); assert_eq!(
assert_eq!(s.core.network.protocol.wss.public_address, None); s.core.network.protocol.wss.path,
std::path::PathBuf::from("ws")
);
assert_eq!(s.core.network.protocol.wss.url, None);
//
assert_eq!(s.core.network.leases.max_server_signal_leases, 256); assert_eq!(s.core.network.leases.max_server_signal_leases, 256);
assert_eq!(s.core.network.leases.max_server_relay_leases, 8); assert_eq!(s.core.network.leases.max_server_relay_leases, 8);
assert_eq!(s.core.network.leases.max_client_signal_leases, 2); assert_eq!(s.core.network.leases.max_client_signal_leases, 2);

View File

@ -162,13 +162,12 @@ pub async fn main() -> Result<(), String> {
let bootstrap = match matches.value_of("bootstrap") { let bootstrap = match matches.value_of("bootstrap") {
Some(x) => { Some(x) => {
println!("Overriding bootstrap with: "); println!("Overriding bootstrap with: ");
let mut out: Vec<settings::ParsedURL> = Vec::new(); let mut out: Vec<settings::ParsedUrl> = Vec::new();
for x in x.split(',') { for x in x.split(',') {
println!(" {}", x); println!(" {}", x);
out.push( out.push(settings::ParsedUrl::from_str(x).map_err(|e| {
settings::ParsedURL::from_str(x) format!("unable to parse url in bootstrap list: {} for {}", e, x)
.map_err(|e| format!("unable to parse url in bootstrap list: {}", e))?, })?);
);
} }
out out
} }