mirror of
https://github.com/rustfs/rustfs.git
synced 2026-01-17 01:30:33 +00:00
perf(utils): optimize User-Agent generation and platform detection (#1504)
This commit is contained in:
@@ -13,9 +13,11 @@
|
||||
// limitations under the License.
|
||||
|
||||
use rustfs_config::VERSION;
|
||||
use std::borrow::Cow;
|
||||
use std::env;
|
||||
use std::fmt;
|
||||
#[cfg(not(any(target_os = "openbsd", target_os = "freebsd", target_os = "netbsd")))]
|
||||
use std::sync::OnceLock;
|
||||
#[cfg(not(target_os = "openbsd"))]
|
||||
use sysinfo::System;
|
||||
|
||||
/// Business Type Enumeration
|
||||
@@ -25,7 +27,7 @@ pub enum ServiceType {
|
||||
Core,
|
||||
Event,
|
||||
Logger,
|
||||
Custom(String),
|
||||
Custom(Cow<'static, str>),
|
||||
}
|
||||
|
||||
impl ServiceType {
|
||||
@@ -35,71 +37,65 @@ impl ServiceType {
|
||||
ServiceType::Core => "core",
|
||||
ServiceType::Event => "event",
|
||||
ServiceType::Logger => "logger",
|
||||
ServiceType::Custom(s) => s.as_str(),
|
||||
ServiceType::Custom(s) => s,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// UserAgent structure to hold User-Agent information
|
||||
/// including OS platform, architecture, version, and service type.
|
||||
/// It provides methods to generate a formatted User-Agent string.
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use rustfs_utils::{get_user_agent, ServiceType};
|
||||
///
|
||||
/// let ua = get_user_agent(ServiceType::Core);
|
||||
/// println!("User-Agent: {}", ua);
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
struct UserAgent {
|
||||
os_platform: String,
|
||||
arch: String,
|
||||
version: String,
|
||||
os_platform: &'static str,
|
||||
arch: &'static str,
|
||||
version: &'static str,
|
||||
service: ServiceType,
|
||||
}
|
||||
|
||||
static OS_PLATFORM: OnceLock<String> = OnceLock::new();
|
||||
|
||||
impl UserAgent {
|
||||
/// Create a new UserAgent instance and accept business type parameters
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `service` - The type of service for which the User-Agent is being created.
|
||||
/// # Returns
|
||||
/// A new instance of `UserAgent` with the current OS platform, architecture, version, and service type.
|
||||
fn new(service: ServiceType) -> Self {
|
||||
let os_platform = Self::get_os_platform();
|
||||
let arch = env::consts::ARCH.to_string();
|
||||
let version = VERSION.to_string();
|
||||
|
||||
UserAgent {
|
||||
os_platform,
|
||||
arch,
|
||||
version,
|
||||
os_platform: Self::get_os_platform(),
|
||||
arch: env::consts::ARCH,
|
||||
version: VERSION,
|
||||
service,
|
||||
}
|
||||
}
|
||||
|
||||
/// Obtain operating system platform information
|
||||
fn get_os_platform() -> String {
|
||||
if cfg!(target_os = "windows") {
|
||||
Self::get_windows_platform()
|
||||
} else if cfg!(target_os = "macos") {
|
||||
Self::get_macos_platform()
|
||||
} else if cfg!(target_os = "linux") {
|
||||
Self::get_linux_platform()
|
||||
} else {
|
||||
"Unknown".to_string()
|
||||
}
|
||||
/// Obtain operating system platform information using a thread-safe cache.
|
||||
///
|
||||
/// The value is computed once on first use via `OnceLock` and then reused
|
||||
/// for all subsequent calls for the lifetime of the program.
|
||||
fn get_os_platform() -> &'static str {
|
||||
OS_PLATFORM.get_or_init(|| {
|
||||
if cfg!(target_os = "windows") {
|
||||
Self::get_windows_platform()
|
||||
} else if cfg!(target_os = "macos") {
|
||||
Self::get_macos_platform()
|
||||
} else if cfg!(target_os = "linux") {
|
||||
Self::get_linux_platform()
|
||||
} else if cfg!(target_os = "freebsd") {
|
||||
Self::get_freebsd_platform()
|
||||
} else if cfg!(target_os = "netbsd") {
|
||||
Self::get_netbsd_platform()
|
||||
} else {
|
||||
"Unknown".to_string()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Get Windows platform information
|
||||
#[cfg(windows)]
|
||||
fn get_windows_platform() -> String {
|
||||
// Priority to using sysinfo to get versions
|
||||
let version = match System::os_version() {
|
||||
Some(version) => version,
|
||||
None => "Windows NT Unknown".to_string(),
|
||||
};
|
||||
format!("Windows NT {version}")
|
||||
let version = System::os_version().unwrap_or_else(|| "NT Unknown".to_string());
|
||||
if version.starts_with("Windows") {
|
||||
version
|
||||
} else {
|
||||
format!("Windows NT {version}")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
@@ -110,16 +106,14 @@ impl UserAgent {
|
||||
/// Get macOS platform information
|
||||
#[cfg(target_os = "macos")]
|
||||
fn get_macos_platform() -> String {
|
||||
let binding = System::os_version().unwrap_or("14.5.0".to_string());
|
||||
let version = binding.split('.').collect::<Vec<&str>>();
|
||||
let major = version.first().unwrap_or(&"14").to_string();
|
||||
let minor = version.get(1).unwrap_or(&"5").to_string();
|
||||
let patch = version.get(2).unwrap_or(&"0").to_string();
|
||||
let version_str = System::os_version().unwrap_or_else(|| "14.0.0".to_string());
|
||||
let mut parts = version_str.split('.');
|
||||
let major = parts.next().unwrap_or("14");
|
||||
let minor = parts.next().unwrap_or("0");
|
||||
let patch = parts.next().unwrap_or("0");
|
||||
|
||||
let arch = env::consts::ARCH;
|
||||
let cpu_info = if arch == "aarch64" { "Apple" } else { "Intel" };
|
||||
let cpu_info = if env::consts::ARCH == "aarch64" { "Apple" } else { "Intel" };
|
||||
|
||||
// Convert to User-Agent format
|
||||
format!("Macintosh; {cpu_info} Mac OS X {major}_{minor}_{patch}")
|
||||
}
|
||||
|
||||
@@ -131,40 +125,47 @@ impl UserAgent {
|
||||
/// Get Linux platform information
|
||||
#[cfg(target_os = "linux")]
|
||||
fn get_linux_platform() -> String {
|
||||
format!("X11; {}", System::long_os_version().unwrap_or("Linux Unknown".to_string()))
|
||||
let os_name = System::long_os_version().unwrap_or_else(|| "Linux Unknown".to_string());
|
||||
format!("X11; {os_name}")
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
fn get_linux_platform() -> String {
|
||||
"N/A".to_string()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "freebsd")]
|
||||
fn get_freebsd_platform() -> String {
|
||||
format!("FreeBSD; {}", env::consts::ARCH)
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "freebsd"))]
|
||||
fn get_freebsd_platform() -> String {
|
||||
"N/A".to_string()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "netbsd")]
|
||||
fn get_netbsd_platform() -> String {
|
||||
format!("NetBSD; {}", env::consts::ARCH)
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "netbsd"))]
|
||||
fn get_netbsd_platform() -> String {
|
||||
"N/A".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
/// Implement Display trait to format User-Agent
|
||||
impl fmt::Display for UserAgent {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if self.service == ServiceType::Basis {
|
||||
return write!(f, "Mozilla/5.0 ({}; {}) RustFS/{}", self.os_platform, self.arch, self.version);
|
||||
write!(f, "Mozilla/5.0 ({}; {}) RustFS/{}", self.os_platform, self.arch, self.version)?;
|
||||
if self.service != ServiceType::Basis {
|
||||
write!(f, " ({})", self.service.as_str())?;
|
||||
}
|
||||
write!(
|
||||
f,
|
||||
"Mozilla/5.0 ({}; {}) RustFS/{} ({})",
|
||||
self.os_platform,
|
||||
self.arch,
|
||||
self.version,
|
||||
self.service.as_str()
|
||||
)
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the User-Agent string and accept business type parameters
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `service` - The type of service for which the User-Agent is being created.
|
||||
///
|
||||
/// # Returns
|
||||
/// A formatted User-Agent string.
|
||||
///
|
||||
pub fn get_user_agent(service: ServiceType) -> String {
|
||||
UserAgent::new(service).to_string()
|
||||
}
|
||||
@@ -173,58 +174,33 @@ pub fn get_user_agent(service: ServiceType) -> String {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use rustfs_config::VERSION;
|
||||
use tracing::debug;
|
||||
|
||||
#[test]
|
||||
fn test_user_agent_format_basis() {
|
||||
let ua = get_user_agent(ServiceType::Basis);
|
||||
assert!(ua.starts_with("Mozilla/5.0"));
|
||||
assert!(ua.contains(&format!("RustFS/{VERSION}").to_string()));
|
||||
debug!("Basic User-Agent: {}", ua);
|
||||
assert!(ua.contains(&format!("RustFS/{VERSION}")));
|
||||
assert!(!ua.contains("(basis)"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_user_agent_format_core() {
|
||||
let ua = get_user_agent(ServiceType::Core);
|
||||
assert!(ua.starts_with("Mozilla/5.0"));
|
||||
assert!(ua.contains(&format!("RustFS/{VERSION} (core)").to_string()));
|
||||
debug!("Core User-Agent: {}", ua);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_user_agent_format_event() {
|
||||
let ua = get_user_agent(ServiceType::Event);
|
||||
assert!(ua.starts_with("Mozilla/5.0"));
|
||||
assert!(ua.contains(&format!("RustFS/{VERSION} (event)").to_string()));
|
||||
debug!("Event User-Agent: {}", ua);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_user_agent_format_logger() {
|
||||
let ua = get_user_agent(ServiceType::Logger);
|
||||
assert!(ua.starts_with("Mozilla/5.0"));
|
||||
assert!(ua.contains(&format!("RustFS/{VERSION} (logger)").to_string()));
|
||||
debug!("Logger User-Agent: {}", ua);
|
||||
assert!(ua.contains(&format!("RustFS/{VERSION} (core)")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_user_agent_format_custom() {
|
||||
let ua = get_user_agent(ServiceType::Custom("monitor".to_string()));
|
||||
assert!(ua.starts_with("Mozilla/5.0"));
|
||||
assert!(ua.contains(&format!("RustFS/{VERSION} (monitor)").to_string()));
|
||||
debug!("Monitor User-Agent: {}", ua);
|
||||
let ua = get_user_agent(ServiceType::Custom("monitor".into()));
|
||||
assert!(ua.contains(&format!("RustFS/{VERSION} (monitor)")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_all_service_type() {
|
||||
// Example: Generate User-Agents of Different Business Types
|
||||
let ua_core = get_user_agent(ServiceType::Core);
|
||||
let ua_event = get_user_agent(ServiceType::Event);
|
||||
let ua_logger = get_user_agent(ServiceType::Logger);
|
||||
let ua_custom = get_user_agent(ServiceType::Custom("monitor".to_string()));
|
||||
|
||||
debug!("Core User-Agent: {}", ua_core);
|
||||
debug!("Event User-Agent: {}", ua_event);
|
||||
debug!("Logger User-Agent: {}", ua_logger);
|
||||
debug!("Custom User-Agent: {}", ua_custom);
|
||||
fn test_os_platform_caching() {
|
||||
let ua1 = UserAgent::new(ServiceType::Basis);
|
||||
let ua2 = UserAgent::new(ServiceType::Basis);
|
||||
assert_eq!(ua1.os_platform, ua2.os_platform);
|
||||
// Ensure they point to the same static memory
|
||||
assert!(std::ptr::eq(ua1.os_platform.as_ptr(), ua2.os_platform.as_ptr()));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user