fix: 优化net

This commit is contained in:
shiro.lee
2024-07-03 23:00:32 +08:00
parent b33a788c20
commit f9462162a5
2 changed files with 79 additions and 173 deletions
+39 -130
View File
@@ -1,17 +1,12 @@
use super::disks_layout::{DisksLayout, PoolDisksLayout}; use super::disks_layout::{DisksLayout, PoolDisksLayout};
use super::error::{Error, Result}; use super::error::{Error, Result};
use super::utils::{ use super::utils::{
net::{is_local_host, split_host_port}, net::{is_local_host, is_socket_addr, split_host_port},
string::new_string_set, string::new_string_set,
}; };
use path_absolutize::Absolutize; use path_absolutize::Absolutize;
use std::fmt::Display; use std::fmt::Display;
use std::{ use std::{collections::HashMap, path::Path, usize};
collections::HashMap,
net::{IpAddr, SocketAddr},
path::Path,
usize,
};
use url::{ParseError, Url}; use url::{ParseError, Url};
pub const DEFAULT_PORT: u16 = 9000; pub const DEFAULT_PORT: u16 = 9000;
@@ -45,7 +40,7 @@ pub struct Node {
// } // }
/// any type of endpoint. /// any type of endpoint.
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone)]
pub struct Endpoint { pub struct Endpoint {
pub url: url::Url, pub url: url::Url,
pub is_local: bool, pub is_local: bool,
@@ -57,10 +52,10 @@ pub struct Endpoint {
impl Display for Endpoint { impl Display for Endpoint {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.url.has_host() { if self.url.scheme() == "file" {
write!(f, "{}", self.url)
} else {
write!(f, "{}", self.url.path()) write!(f, "{}", self.url.path())
} else {
write!(f, "{}", self.url)
} }
} }
} }
@@ -77,7 +72,7 @@ impl TryFrom<&str> for Endpoint {
let mut is_local = false; let mut is_local = false;
let url = match Url::parse(value) { let url = match Url::parse(value) {
Ok(url) => { Ok(mut url) => {
// URL style of endpoint. // URL style of endpoint.
// Valid URL style endpoint is // Valid URL style endpoint is
// - Scheme field must contain "http" or "https" // - Scheme field must contain "http" or "https"
@@ -89,6 +84,7 @@ impl TryFrom<&str> for Endpoint {
{ {
return Err(Error::from_string("invalid URL endpoint format")); return Err(Error::from_string("invalid URL endpoint format"));
} }
if is_empty_path(url.path()) { if is_empty_path(url.path()) {
return Err(Error::from_string("empty or root endpoint is not supported")); return Err(Error::from_string("empty or root endpoint is not supported"));
} }
@@ -108,7 +104,12 @@ impl TryFrom<&str> for Endpoint {
// Another additional benefit here is that this style also // Another additional benefit here is that this style also
// supports providing \\host\share support as well. // supports providing \\host\share support as well.
#[cfg(windows)] #[cfg(windows)]
{} {
let path = url.path().to_owned();
if Path::new(&path[1..]).has_root() {
url.set_path(&path[1..]);
}
}
url url
} }
@@ -135,7 +136,7 @@ impl TryFrom<&str> for Endpoint {
is_local = true; is_local = true;
url url
} }
Err(err) => return Err(Error::from_string("Convert a file path into an URL failed")), Err(_) => return Err(Error::from_string("Convert a file path into an URL failed")),
} }
} }
_ => return Err(Error::from_string(format!("invalid URL endpoint format: {}", e))), _ => return Err(Error::from_string(format!("invalid URL endpoint format: {}", e))),
@@ -145,7 +146,9 @@ impl TryFrom<&str> for Endpoint {
Ok(Endpoint { Ok(Endpoint {
url, url,
is_local, is_local,
..Default::default() pool_idx: None,
set_idx: None,
disk_idx: None,
}) })
} }
} }
@@ -155,128 +158,34 @@ fn is_empty_path(path: &str) -> bool {
["", "/", "\\"].iter().any(|&v| v.eq(path)) ["", "/", "\\"].iter().any(|&v| v.eq(path))
} }
// helper for validating if the provided arg is an ip address.
fn is_socket_addr(host: &str) -> bool {
host.parse::<SocketAddr>().is_ok() || host.parse::<IpAddr>().is_ok()
}
impl Endpoint { impl Endpoint {
pub fn new(arg: &str) -> Result<Self> { /// returns type of endpoint.
if is_empty_path(arg) {
return Err(Error::from_string("不支持空或根endpoint"));
}
let url = Url::parse(arg).or_else(|e| match e {
ParseError::EmptyHost => Err(Error::from_string("远程地址,域名不能为空")),
ParseError::IdnaError => Err(Error::from_string("域名格式不正确")),
ParseError::InvalidPort => Err(Error::from_string("端口格式不正确")),
ParseError::InvalidIpv4Address => Err(Error::from_string("IP格式不正确")),
ParseError::InvalidIpv6Address => Err(Error::from_string("IP格式不正确")),
ParseError::InvalidDomainCharacter => Err(Error::from_string("域名字符格式不正确")),
// url::ParseError::RelativeUrlWithoutBase => todo!(),
// url::ParseError::RelativeUrlWithCannotBeABaseBase => todo!(),
// url::ParseError::SetHostOnCannotBeABaseUrl => todo!(),
ParseError::Overflow => Err(Error::from_string("长度过长")),
_ => {
if is_host_ip(arg) {
return Err(Error::from_string("无效的URL endpoint格式: 缺少 http 或 https"));
}
let abs_arg = Path::new(arg).canonicalize()?;
let abs = abs_arg.to_str().ok_or(Error::from_string("绝对路径错误"))?;
let url = Url::from_file_path(abs).unwrap();
Ok(url)
}
})?;
if url.scheme() == "file" {
return Ok(Endpoint {
url: url,
is_local: true,
pool_idx: -1,
set_idx: -1,
disk_idx: -1,
});
}
if url.port().is_none() {
return Err(Error::from_string("必须提供端口号"));
}
if !(url.scheme() == "http" || url.scheme() == "https") {
return Err(Error::from_string("URL endpoint格式无效: Scheme字段必须包含'http'或'https'"));
}
// 检查路径
let path = url.path();
if is_empty_path(path) {
return Err(Error::from_string("URL endpoint不支持空或根路径"));
}
// TODO: Windows 系统上的路径处理
#[cfg(windows)]
{
use std::env;
if env::consts::OS == "windows" {
// 处理 Windows 路径的特殊逻辑
}
}
Ok(Endpoint {
url: url,
is_local: false,
pool_idx: -1,
set_idx: -1,
disk_idx: -1,
})
}
// pub fn host_port_str(&self) -> String {
// if self.url.has_host() && self.port() > 0 {
// return format!("{}:{}", self.hostname(), self.port());
// } else if self.url.has_host() && self.port() == 0 {
// return self.hostname().to_string();
// } else if !self.url.has_host() && self.port() > 0 {
// return format!(":{}", self.port());
// } else {
// return String::new();
// }
// }
// pub fn port(&self) -> u16 {
// self.url.port().unwrap_or(0)
// }
// pub fn hostname(&self) -> &str {
// self.url.host_str().unwrap_or("")
// }
pub fn get_type(&self) -> EndpointType { pub fn get_type(&self) -> EndpointType {
if self.url.has_host() { if self.url.scheme() == "file" {
EndpointType::Url
} else {
EndpointType::Path EndpointType::Path
} else {
EndpointType::Url
} }
} }
// pub fn get_scheme(&self) -> String { /// sets a specific pool number to this node
// self.url.scheme().to_string() pub fn set_pool_index(&mut self, idx: usize) {
// } self.pool_idx = Some(idx)
pub fn set_pool_index(&mut self, idx: i32) {
self.pool_idx = idx
} }
pub fn set_set_index(&mut self, idx: i32) { /// sets a specific set number to this node
self.set_idx = idx pub fn set_set_index(&mut self, idx: usize) {
self.set_idx = Some(idx)
} }
pub fn set_disk_index(&mut self, idx: i32) { /// sets a specific disk number to this node
self.disk_idx = idx pub fn set_disk_index(&mut self, idx: usize) {
self.disk_idx = Some(idx)
} }
fn update_islocal(&mut self) -> Result<()> { /// resolves the host and updates if it is local or not.
if self.url.has_host() { fn update_is_local(&mut self) -> Result<()> {
if self.url.scheme() != "file" {
self.is_local = is_local_host(self.url.host().unwrap(), self.url.port().unwrap(), DEFAULT_PORT); self.is_local = is_local_host(self.url.host().unwrap(), self.url.port().unwrap(), DEFAULT_PORT);
} }
@@ -323,7 +232,7 @@ impl Endpoints {
let mut eps = Vec::new(); let mut eps = Vec::new();
let mut uniq_args = new_string_set(); let mut uniq_args = new_string_set();
for (i, arg) in args.iter().enumerate() { for (i, arg) in args.iter().enumerate() {
let endpoint = Endpoint::new(arg)?; let endpoint = Endpoint::try_from(arg.as_str())?;
if i == 0 { if i == 0 {
ep_type = endpoint.get_type(); ep_type = endpoint.get_type();
scheme = endpoint.url.scheme().to_string(); scheme = endpoint.url.scheme().to_string();
@@ -365,7 +274,7 @@ impl PoolEndpointList {
for eps in self.0.iter_mut() { for eps in self.0.iter_mut() {
for ep in eps.iter_mut() { for ep in eps.iter_mut() {
// TODO: // TODO:
ep.update_islocal()? ep.update_is_local()?
} }
} }
@@ -573,9 +482,9 @@ pub fn create_pool_endpoints(server_addr: String, pools: &Vec<PoolDisksLayout>)
let mut eps = Endpoints::from_args(set_layout.to_owned())?; let mut eps = Endpoints::from_args(set_layout.to_owned())?;
// TODO: checkCrossDeviceMounts // TODO: checkCrossDeviceMounts
for (disk_idx, ep) in eps.0.iter_mut().enumerate() { for (disk_idx, ep) in eps.0.iter_mut().enumerate() {
ep.set_pool_index(pool_idx as i32); ep.set_pool_index(pool_idx);
ep.set_set_index(set_idx as i32); ep.set_set_index(set_idx);
ep.set_disk_index(disk_idx as i32); ep.set_disk_index(disk_idx);
endpoints.0.push(ep.to_owned()); endpoints.0.push(ep.to_owned());
} }
+40 -43
View File
@@ -1,12 +1,17 @@
use std::{ use std::{
collections::HashMap, collections::HashSet,
net::{IpAddr, ToSocketAddrs}, net::{IpAddr, SocketAddr, ToSocketAddrs},
}; };
use anyhow::{Error, Result}; use anyhow::{Error, Result};
use netif; use netif;
use url::Host; use url::Host;
// helper for validating if the provided arg is an ip address.
pub fn is_socket_addr(host: &str) -> bool {
host.parse::<SocketAddr>().is_ok() || host.parse::<IpAddr>().is_ok()
}
pub fn split_host_port(s: &str) -> Result<(String, u16)> { pub fn split_host_port(s: &str) -> Result<(String, u16)> {
let parts: Vec<&str> = s.split(':').collect(); let parts: Vec<&str> = s.split(':').collect();
if parts.len() == 2 { if parts.len() == 2 {
@@ -17,38 +22,23 @@ pub fn split_host_port(s: &str) -> Result<(String, u16)> {
Err(Error::msg("Invalid address format or port number")) Err(Error::msg("Invalid address format or port number"))
} }
// is_local_host 判断是否是本地ip /// checks if the given parameter correspond to one of
/// the local IP of the current machine
pub fn is_local_host(host: Host<&str>, port: u16, local_port: u16) -> bool { pub fn is_local_host(host: Host<&str>, port: u16, local_port: u16) -> bool {
let local_ips = must_get_local_ips(); let local_ips = must_get_local_ips();
let local_map = let local_set: HashSet<IpAddr> = local_ips.into_iter().collect();
local_ips
.iter()
.map(|ip| ip.to_string())
.fold(HashMap::new(), |mut acc, item| {
*acc.entry(item).or_insert(true) = true;
acc
});
let is_local_host = match host { let is_local_host = match host {
Host::Domain(domain) => { Host::Domain(domain) => {
let ips: Vec<String> = (domain, 0) let ips = match (domain, 0).to_socket_addrs().map(|v| v.map(|v| v.ip()).collect::<Vec<_>>()) {
.to_socket_addrs() Ok(ips) => ips,
.unwrap_or(Vec::new().into_iter()) Err(_) => return false,
.map(|addr| addr.ip().to_string()) };
.collect();
let mut isok = false; ips.iter().any(|ip| local_set.contains(ip))
for ip in ips.iter() {
if local_map.contains_key(ip) {
isok = true;
break;
}
}
isok
} }
Host::Ipv4(ip) => local_map.contains_key(&ip.to_string()), Host::Ipv4(ip) => local_set.contains(&IpAddr::V4(ip)),
Host::Ipv6(ip) => local_map.contains_key(&ip.to_string()), Host::Ipv6(ip) => local_set.contains(&IpAddr::V6(ip)),
}; };
if port > 0 { if port > 0 {
@@ -58,37 +48,44 @@ pub fn is_local_host(host: Host<&str>, port: u16, local_port: u16) -> bool {
is_local_host is_local_host
} }
/// returns IPs of local interface
pub fn must_get_local_ips() -> Vec<IpAddr> { pub fn must_get_local_ips() -> Vec<IpAddr> {
let mut v: Vec<IpAddr> = Vec::new(); match netif::up() {
if let Some(up) = netif::up().ok() { Ok(up) => up.map(|x| x.address().to_owned()).collect(),
v = up.map(|x| x.address().to_owned()).collect(); Err(_) => vec![],
} }
v
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use std::net::Ipv4Addr; use std::net::{Ipv4Addr, Ipv6Addr};
use super::*; use super::*;
#[test] #[test]
fn test_must_get_local_ips() { fn test_must_get_local_ips() {
let ips = must_get_local_ips(); let local_ips = must_get_local_ips();
for ip in ips.iter() { let local_set: HashSet<IpAddr> = local_ips.into_iter().collect();
println!("{:?}", ip)
} assert!(local_set.contains(&IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))));
} }
#[test] #[test]
fn test_is_local_host() { fn test_is_local_host() {
// let host = Host::Ipv4(Ipv4Addr::new(192, 168, 0, 233)); let host = Host::Domain("localhost");
let is = is_local_host(host, 0, 9000);
assert!(is);
let host = Host::Ipv6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
let is = is_local_host(host, 0, 9000);
assert!(is);
let host = Host::Ipv4(Ipv4Addr::new(8, 8, 8, 8));
let is = is_local_host(host, 0, 9000);
assert!(!is);
let host = Host::Ipv4(Ipv4Addr::new(127, 0, 0, 1)); let host = Host::Ipv4(Ipv4Addr::new(127, 0, 0, 1));
// let host = Host::Domain("localhost"); let is = is_local_host(host, 8000, 9000);
let port = 0; assert!(!is);
let local_port = 9000;
let is = is_local_host(host, port, local_port);
assert!(is)
} }
} }