mirror of
https://github.com/rustfs/rustfs.git
synced 2026-07-26 16:28:15 +00:00
fix: 优化net
This commit is contained in:
+39
-130
@@ -1,17 +1,12 @@
|
||||
use super::disks_layout::{DisksLayout, PoolDisksLayout};
|
||||
use super::error::{Error, Result};
|
||||
use super::utils::{
|
||||
net::{is_local_host, split_host_port},
|
||||
net::{is_local_host, is_socket_addr, split_host_port},
|
||||
string::new_string_set,
|
||||
};
|
||||
use path_absolutize::Absolutize;
|
||||
use std::fmt::Display;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
net::{IpAddr, SocketAddr},
|
||||
path::Path,
|
||||
usize,
|
||||
};
|
||||
use std::{collections::HashMap, path::Path, usize};
|
||||
use url::{ParseError, Url};
|
||||
|
||||
pub const DEFAULT_PORT: u16 = 9000;
|
||||
@@ -45,7 +40,7 @@ pub struct Node {
|
||||
// }
|
||||
|
||||
/// any type of endpoint.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Endpoint {
|
||||
pub url: url::Url,
|
||||
pub is_local: bool,
|
||||
@@ -57,10 +52,10 @@ pub struct Endpoint {
|
||||
|
||||
impl Display for Endpoint {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if self.url.has_host() {
|
||||
write!(f, "{}", self.url)
|
||||
} else {
|
||||
if self.url.scheme() == "file" {
|
||||
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 url = match Url::parse(value) {
|
||||
Ok(url) => {
|
||||
Ok(mut url) => {
|
||||
// URL style of endpoint.
|
||||
// Valid URL style endpoint is
|
||||
// - 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"));
|
||||
}
|
||||
|
||||
if is_empty_path(url.path()) {
|
||||
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
|
||||
// supports providing \\host\share support as well.
|
||||
#[cfg(windows)]
|
||||
{}
|
||||
{
|
||||
let path = url.path().to_owned();
|
||||
if Path::new(&path[1..]).has_root() {
|
||||
url.set_path(&path[1..]);
|
||||
}
|
||||
}
|
||||
|
||||
url
|
||||
}
|
||||
@@ -135,7 +136,7 @@ impl TryFrom<&str> for Endpoint {
|
||||
is_local = true;
|
||||
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))),
|
||||
@@ -145,7 +146,9 @@ impl TryFrom<&str> for Endpoint {
|
||||
Ok(Endpoint {
|
||||
url,
|
||||
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))
|
||||
}
|
||||
|
||||
// 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 {
|
||||
pub fn new(arg: &str) -> Result<Self> {
|
||||
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("")
|
||||
// }
|
||||
|
||||
/// returns type of endpoint.
|
||||
pub fn get_type(&self) -> EndpointType {
|
||||
if self.url.has_host() {
|
||||
EndpointType::Url
|
||||
} else {
|
||||
if self.url.scheme() == "file" {
|
||||
EndpointType::Path
|
||||
} else {
|
||||
EndpointType::Url
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn get_scheme(&self) -> String {
|
||||
// self.url.scheme().to_string()
|
||||
// }
|
||||
|
||||
pub fn set_pool_index(&mut self, idx: i32) {
|
||||
self.pool_idx = idx
|
||||
/// sets a specific pool number to this node
|
||||
pub fn set_pool_index(&mut self, idx: usize) {
|
||||
self.pool_idx = Some(idx)
|
||||
}
|
||||
|
||||
pub fn set_set_index(&mut self, idx: i32) {
|
||||
self.set_idx = idx
|
||||
/// sets a specific set number to this node
|
||||
pub fn set_set_index(&mut self, idx: usize) {
|
||||
self.set_idx = Some(idx)
|
||||
}
|
||||
|
||||
pub fn set_disk_index(&mut self, idx: i32) {
|
||||
self.disk_idx = idx
|
||||
/// sets a specific disk number to this node
|
||||
pub fn set_disk_index(&mut self, idx: usize) {
|
||||
self.disk_idx = Some(idx)
|
||||
}
|
||||
|
||||
fn update_islocal(&mut self) -> Result<()> {
|
||||
if self.url.has_host() {
|
||||
/// resolves the host and updates if it is local or not.
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -323,7 +232,7 @@ impl Endpoints {
|
||||
let mut eps = Vec::new();
|
||||
let mut uniq_args = new_string_set();
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
let endpoint = Endpoint::new(arg)?;
|
||||
let endpoint = Endpoint::try_from(arg.as_str())?;
|
||||
if i == 0 {
|
||||
ep_type = endpoint.get_type();
|
||||
scheme = endpoint.url.scheme().to_string();
|
||||
@@ -365,7 +274,7 @@ impl PoolEndpointList {
|
||||
for eps in self.0.iter_mut() {
|
||||
for ep in eps.iter_mut() {
|
||||
// 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())?;
|
||||
// TODO: checkCrossDeviceMounts
|
||||
for (disk_idx, ep) in eps.0.iter_mut().enumerate() {
|
||||
ep.set_pool_index(pool_idx as i32);
|
||||
ep.set_set_index(set_idx as i32);
|
||||
ep.set_disk_index(disk_idx as i32);
|
||||
ep.set_pool_index(pool_idx);
|
||||
ep.set_set_index(set_idx);
|
||||
ep.set_disk_index(disk_idx);
|
||||
|
||||
endpoints.0.push(ep.to_owned());
|
||||
}
|
||||
|
||||
+40
-43
@@ -1,12 +1,17 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
net::{IpAddr, ToSocketAddrs},
|
||||
collections::HashSet,
|
||||
net::{IpAddr, SocketAddr, ToSocketAddrs},
|
||||
};
|
||||
|
||||
use anyhow::{Error, Result};
|
||||
use netif;
|
||||
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)> {
|
||||
let parts: Vec<&str> = s.split(':').collect();
|
||||
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"))
|
||||
}
|
||||
|
||||
// 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 {
|
||||
let local_ips = must_get_local_ips();
|
||||
|
||||
let local_map =
|
||||
local_ips
|
||||
.iter()
|
||||
.map(|ip| ip.to_string())
|
||||
.fold(HashMap::new(), |mut acc, item| {
|
||||
*acc.entry(item).or_insert(true) = true;
|
||||
acc
|
||||
});
|
||||
|
||||
let local_set: HashSet<IpAddr> = local_ips.into_iter().collect();
|
||||
let is_local_host = match host {
|
||||
Host::Domain(domain) => {
|
||||
let ips: Vec<String> = (domain, 0)
|
||||
.to_socket_addrs()
|
||||
.unwrap_or(Vec::new().into_iter())
|
||||
.map(|addr| addr.ip().to_string())
|
||||
.collect();
|
||||
let ips = match (domain, 0).to_socket_addrs().map(|v| v.map(|v| v.ip()).collect::<Vec<_>>()) {
|
||||
Ok(ips) => ips,
|
||||
Err(_) => return false,
|
||||
};
|
||||
|
||||
let mut isok = false;
|
||||
for ip in ips.iter() {
|
||||
if local_map.contains_key(ip) {
|
||||
isok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
isok
|
||||
ips.iter().any(|ip| local_set.contains(ip))
|
||||
}
|
||||
Host::Ipv4(ip) => local_map.contains_key(&ip.to_string()),
|
||||
Host::Ipv6(ip) => local_map.contains_key(&ip.to_string()),
|
||||
Host::Ipv4(ip) => local_set.contains(&IpAddr::V4(ip)),
|
||||
Host::Ipv6(ip) => local_set.contains(&IpAddr::V6(ip)),
|
||||
};
|
||||
|
||||
if port > 0 {
|
||||
@@ -58,37 +48,44 @@ pub fn is_local_host(host: Host<&str>, port: u16, local_port: u16) -> bool {
|
||||
is_local_host
|
||||
}
|
||||
|
||||
/// returns IPs of local interface
|
||||
pub fn must_get_local_ips() -> Vec<IpAddr> {
|
||||
let mut v: Vec<IpAddr> = Vec::new();
|
||||
if let Some(up) = netif::up().ok() {
|
||||
v = up.map(|x| x.address().to_owned()).collect();
|
||||
match netif::up() {
|
||||
Ok(up) => up.map(|x| x.address().to_owned()).collect(),
|
||||
Err(_) => vec![],
|
||||
}
|
||||
|
||||
v
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::net::Ipv4Addr;
|
||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_must_get_local_ips() {
|
||||
let ips = must_get_local_ips();
|
||||
for ip in ips.iter() {
|
||||
println!("{:?}", ip)
|
||||
}
|
||||
let local_ips = must_get_local_ips();
|
||||
let local_set: HashSet<IpAddr> = local_ips.into_iter().collect();
|
||||
|
||||
assert!(local_set.contains(&IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
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::Domain("localhost");
|
||||
let port = 0;
|
||||
let local_port = 9000;
|
||||
let is = is_local_host(host, port, local_port);
|
||||
assert!(is)
|
||||
let is = is_local_host(host, 8000, 9000);
|
||||
assert!(!is);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user