use crate::common::MessageParser;
use crate::common::compression::CompressionAlgorithm;
use crate::common::encryption::EncryptionAlgorithm;
use crate::common::error::Result;
use crate::common::message::pipeline::{
ArcMessageMiddleware, ArcMessageProcessor, MessagePipeline,
};
use crate::common::protocol::{Frame, Reliability, SerializationFormat, frame_with_system_command};
use crate::server::auth::Authenticator;
use crate::server::config::ServerConfig;
use crate::server::connection::{
ConnectionManager, ConnectionManagerTrait, device_handler, negotiation,
};
use crate::server::device::DeviceManager;
use crate::server::events::factory::ServerMessageObserverFactory;
use crate::server::events::handler::ServerEventHandler;
use crate::server::handle::ServerHandle;
use crate::server::heartbeat::HeartbeatDetector;
use async_trait::async_trait;
use std::collections::HashMap;
use std::sync::Arc;
use std::time::Duration;
use tracing::{debug, error, info, warn};
pub struct ServerCore {
pub connection_manager: Arc<ConnectionManager>,
pub parser: MessageParser,
heartbeat_detector: Arc<tokio::sync::Mutex<Option<HeartbeatDetector>>>,
device_manager: Option<Arc<DeviceManager>>,
event_handler: Option<Arc<dyn ServerEventHandler>>,
authenticator: Option<Arc<dyn Authenticator>>,
auth_enabled: bool,
auth_timeout: Duration,
default_serialization_format: SerializationFormat,
default_compression: CompressionAlgorithm,
default_encryption: EncryptionAlgorithm,
observer_factory: Arc<dyn ServerMessageObserverFactory>,
shared_middlewares: Vec<ArcMessageMiddleware>,
shared_processors: Vec<ArcMessageProcessor>,
pipeline_cache: Arc<tokio::sync::Mutex<HashMap<PipelineCacheKey, Arc<MessagePipeline>>>>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct PipelineCacheKey {
format: SerializationFormat,
compression: CompressionAlgorithm,
encryption: EncryptionAlgorithm,
}
struct NegotiatedConnectionUpdate {
format: SerializationFormat,
compression: CompressionAlgorithm,
encryption: EncryptionAlgorithm,
user_id: Option<String>,
metadata: Option<HashMap<String, String>>,
}
impl ServerCore {
pub fn default_serialization_format(&self) -> SerializationFormat {
self.default_serialization_format
}
pub fn default_compression(&self) -> CompressionAlgorithm {
self.default_compression.clone()
}
#[must_use]
pub fn with_default_format(mut self, format: SerializationFormat) -> Self {
self.default_serialization_format = format;
self
}
#[must_use]
pub fn with_default_compression(mut self, compression: CompressionAlgorithm) -> Self {
self.default_compression = compression;
self
}
pub fn new(config: &ServerConfig, connection_manager: Option<Arc<ConnectionManager>>) -> Self {
Self::with_observer_factory(
config,
connection_manager,
Arc::new(crate::server::events::DefaultServerMessageObserverFactory::new()),
)
}
pub fn with_observer_factory(
config: &ServerConfig,
connection_manager: Option<Arc<ConnectionManager>>,
observer_factory: Arc<dyn ServerMessageObserverFactory>,
) -> Self {
let connection_manager = connection_manager.unwrap_or_else(|| {
Arc::new(ConnectionManager::with_limits(
config.write_timeout,
config.fanout_concurrency,
))
});
let parser = MessageParser::new(
config.default_serialization_format,
config.default_compression.clone(),
EncryptionAlgorithm::None, );
Self {
connection_manager,
parser,
heartbeat_detector: Arc::new(tokio::sync::Mutex::new(None)),
device_manager: None,
event_handler: None,
authenticator: None,
auth_enabled: config.auth_enabled,
auth_timeout: config.auth_timeout,
default_serialization_format: config.default_serialization_format,
default_compression: config.default_compression.clone(),
default_encryption: config.default_encryption.clone(),
observer_factory,
shared_middlewares: Vec::new(), shared_processors: Vec::new(), pipeline_cache: Arc::new(tokio::sync::Mutex::new(HashMap::new())),
}
}
#[must_use]
pub fn with_device_manager(mut self, device_manager: Option<Arc<DeviceManager>>) -> Self {
self.device_manager = device_manager;
self
}
pub fn device_manager(&self) -> Option<Arc<DeviceManager>> {
self.device_manager.clone()
}
#[must_use]
pub fn with_event_handler(
mut self,
event_handler: Option<Arc<dyn ServerEventHandler>>,
) -> Self {
self.event_handler = event_handler;
self
}
pub fn set_observer_factory(&mut self, factory: Arc<dyn ServerMessageObserverFactory>) {
self.observer_factory = factory;
}
pub fn observer_factory(&self) -> &Arc<dyn ServerMessageObserverFactory> {
&self.observer_factory
}
pub fn create_observer_with_core(
&self,
connection_id: String,
core_arc: Arc<ServerCore>,
) -> Arc<dyn crate::transport::events::ConnectionObserver> {
let core_ref = Arc::new(crate::server::events::factory::ServerCoreRef {
device_manager: self.device_manager.clone(),
event_handler: self.event_handler.clone(),
});
let event_handler = self
.event_handler
.clone()
.expect("ServerEventHandler is required for creating observer");
self.observer_factory.create_observer(
Arc::clone(&self.connection_manager),
self.parser.clone(),
event_handler,
connection_id,
core_ref,
core_arc,
)
}
pub fn event_handler(&self) -> Option<Arc<dyn ServerEventHandler>> {
self.event_handler.clone()
}
pub fn set_event_handler(&mut self, event_handler: Option<Arc<dyn ServerEventHandler>>) {
self.event_handler = event_handler;
}
pub fn set_device_manager(&mut self, device_manager: Option<Arc<DeviceManager>>) {
self.device_manager = device_manager;
}
#[must_use]
pub fn with_authenticator(mut self, authenticator: Option<Arc<dyn Authenticator>>) -> Self {
self.authenticator = authenticator;
self
}
pub fn authenticator(&self) -> Option<Arc<dyn Authenticator>> {
self.authenticator.clone()
}
pub fn set_authenticator(&mut self, authenticator: Option<Arc<dyn Authenticator>>) {
self.authenticator = authenticator;
}
pub fn auth_enabled(&self) -> bool {
self.auth_enabled && self.authenticator.is_some()
}
pub fn auth_timeout(&self) -> Duration {
self.auth_timeout
}
pub async fn add_middleware(&mut self, middleware: ArcMessageMiddleware) {
self.shared_middlewares.push(middleware);
self.pipeline_cache.lock().await.clear();
}
pub async fn add_processor(&mut self, processor: ArcMessageProcessor) {
self.shared_processors.push(processor);
self.pipeline_cache.lock().await.clear();
}
pub fn start_heartbeat(&self, config: &ServerConfig) {
let manager_trait = Arc::clone(&self.connection_manager) as Arc<dyn ConnectionManagerTrait>;
let timeout = config.connection_timeout;
let check_interval =
Duration::from_secs(timeout.as_secs() / 3).max(Duration::from_secs(10));
let mut detector = HeartbeatDetector::new(manager_trait, timeout, check_interval);
detector.start();
let detector_arc = Arc::clone(&self.heartbeat_detector);
tokio::spawn(async move {
let mut guard = detector_arc.lock().await;
*guard = Some(detector);
});
}
pub fn stop_heartbeat(&self) {
let detector_arc = Arc::clone(&self.heartbeat_detector);
tokio::spawn(async move {
let mut guard = detector_arc.lock().await;
if let Some(ref mut detector) = *guard {
detector.stop();
}
});
}
pub fn connection_manager_trait(&self) -> Arc<dyn ConnectionManagerTrait> {
Arc::clone(&self.connection_manager) as Arc<dyn ConnectionManagerTrait>
}
pub async fn send_to(&self, connection_id: &str, frame: &Frame) -> Result<()> {
let manager_trait = self.connection_manager_trait();
manager_trait
.send_frame_to(connection_id, frame, None)
.await
}
pub async fn send_to_user(&self, user_id: &str, frame: &Frame) -> Result<()> {
let manager_trait = self.connection_manager_trait();
manager_trait.send_frame_to_user(user_id, frame, None).await
}
pub async fn broadcast(&self, frame: &Frame) -> Result<()> {
let manager_trait = self.connection_manager_trait();
manager_trait.broadcast_frame(frame, None).await
}
pub async fn broadcast_except(&self, frame: &Frame, exclude_connection_id: &str) -> Result<()> {
let manager_trait = self.connection_manager_trait();
manager_trait
.broadcast_frame_except(frame, exclude_connection_id, None)
.await
}
pub fn connection_count(&self) -> usize {
self.connection_manager.connection_count()
}
pub fn user_count(&self) -> usize {
self.connection_manager.stats().total_users
}
pub async fn disconnect(&self, connection_id: &str) -> Result<()> {
let manager_trait = self.connection_manager_trait();
manager_trait.remove_connection(connection_id).await
}
pub async fn list_connections(&self) -> Vec<String> {
let manager_trait = self.connection_manager_trait();
manager_trait.list_connections().await
}
pub async fn handle_connect_message(
&self,
frame: &Frame,
connection_id: &str,
) -> Result<(Frame, MessageParser)> {
let negotiation = negotiation::parse_connect_message(frame)?;
let (final_format, final_compression, final_encryption) =
self.determine_negotiation_result(&negotiation);
self.log_negotiation_details(
connection_id,
&negotiation,
final_format,
final_compression.clone(),
final_encryption.clone(),
);
let (auth_user_id, meta_data) = self
.authenticate_connection(frame, connection_id, &negotiation)
.await?;
let user_id = auth_user_id.clone().or_else(|| negotiation.user_id.clone());
self.update_connection_info(
connection_id,
&negotiation,
NegotiatedConnectionUpdate {
format: final_format,
compression: final_compression.clone(),
encryption: final_encryption.clone(),
user_id: user_id.clone(),
metadata: meta_data,
},
)
.await;
self.mark_connection_authenticated(connection_id, &user_id)
.await;
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
let conflict_connections = self
.handle_device_conflict(connection_id, &negotiation)
.await;
let ack_frame = self.create_connect_ack(
final_format,
final_compression.clone(),
final_encryption.clone(),
&conflict_connections,
);
let parser = MessageParser::new(final_format, final_compression, final_encryption);
Ok((ack_frame, parser))
}
pub async fn handle_connect_complete(
&self,
frame: &Frame,
connection_id: &str,
connection: Arc<tokio::sync::Mutex<Box<dyn crate::transport::connection::Connection>>>,
) -> Result<()> {
let (ack_frame, negotiation_parser) =
self.handle_connect_message(frame, connection_id).await?;
let final_format = negotiation_parser.default_format();
let final_compression = negotiation_parser.default_compression();
let final_encryption = negotiation_parser.default_encryption();
debug!(
"[ServerCore] ✅ 协商完成: connection_id={}, 最终序列化方式={:?}, 最终压缩方式={:?},最终加密方式={:?}",
connection_id, final_format, final_compression, final_encryption
);
use crate::common::message::parser::PRE_NEGOTIATION_PARSER;
let ack_data = PRE_NEGOTIATION_PARSER.serialize(&ack_frame)?;
{
let mut conn = connection.lock().await;
conn.send(&ack_data).await?;
}
debug!(
"[ServerCore] CONNECT_ACK 已发送: connection_id={}",
connection_id
);
self.finalize_negotiation_after_ack(
connection_id,
final_format,
final_compression,
final_encryption,
)
.await;
let event_handler = self
.event_handler
.as_ref()
.expect("ServerEventHandler is required");
if let Err(e) = event_handler.on_connect(connection_id).await {
error!(
"[ServerCore] ServerEventHandler.on_connect 失败: connection_id={}, error={}",
connection_id, e
);
return Err(e);
}
Ok(())
}
async fn finalize_negotiation_after_ack(
&self,
connection_id: &str,
final_format: SerializationFormat,
final_compression: CompressionAlgorithm,
final_encryption: EncryptionAlgorithm,
) {
let manager = Arc::clone(&self.connection_manager);
let info = match manager.get_connection(connection_id) {
Some((_, conn_info)) => conn_info,
None => {
error!(
"[ServerCore] 连接不存在,无法完成协商: connection_id={}",
connection_id
);
return;
}
};
let compression_clone = final_compression.clone();
let encryption_clone = final_encryption.clone();
let parser = crate::common::MessageParser::new(
final_format,
compression_clone.clone(),
encryption_clone.clone(),
);
let pipeline = self
.pipeline_for_negotiation_profile(
&parser,
PipelineCacheKey {
format: final_format,
compression: final_compression.clone(),
encryption: final_encryption.clone(),
},
)
.await;
if let Err(e) = manager.update_connection_negotiation_with_pipeline(
connection_id,
info.device_info.clone(),
final_format,
final_compression,
final_encryption,
info.user_id.clone(),
parser,
pipeline,
) {
error!("[ServerCore] 最终完成协商失败: {}", e);
return;
}
debug!(
"[ServerCore] ✅ 协商最终完成(CONNECT_ACK 已发送): connection_id={}",
connection_id
);
}
async fn pipeline_for_negotiation_profile(
&self,
parser: &MessageParser,
key: PipelineCacheKey,
) -> Option<Arc<MessagePipeline>> {
if self.shared_middlewares.is_empty() && self.shared_processors.is_empty() {
return None;
}
{
let cache = self.pipeline_cache.lock().await;
if let Some(pipeline) = cache.get(&key) {
return Some(Arc::clone(pipeline));
}
}
let pipeline = MessagePipeline::new(parser.clone());
for middleware in &self.shared_middlewares {
pipeline.add_middleware(Arc::clone(middleware)).await;
}
for processor in &self.shared_processors {
pipeline.add_processor(Arc::clone(processor)).await;
}
let pipeline = Arc::new(pipeline);
let mut cache = self.pipeline_cache.lock().await;
let cached = cache.entry(key).or_insert_with(|| Arc::clone(&pipeline));
Some(Arc::clone(cached))
}
fn determine_negotiation_result(
&self,
negotiation: &negotiation::NegotiationResult,
) -> (
SerializationFormat,
CompressionAlgorithm,
EncryptionAlgorithm,
) {
let final_format = if negotiation.is_forced || negotiation.serialization_format_specified {
negotiation.serialization_format
} else {
self.default_serialization_format
};
let final_compression =
if negotiation.is_forced || negotiation.compression != CompressionAlgorithm::None {
negotiation.compression.clone()
} else {
self.default_compression.clone()
};
let final_encryption = if negotiation.encryption != EncryptionAlgorithm::None {
negotiation.encryption.clone()
} else {
self.default_encryption.clone()
};
(final_format, final_compression, final_encryption)
}
fn log_negotiation_details(
&self,
connection_id: &str,
negotiation: &negotiation::NegotiationResult,
final_format: SerializationFormat,
final_compression: CompressionAlgorithm,
final_encryption: EncryptionAlgorithm,
) {
debug!(
"[ServerCore] 📥 收到 CONNECT 消息: connection_id={}",
connection_id
);
debug!(
"[ServerCore] 📋 协商详情: 客户端请求={:?}, 客户端是否指定格式={}, 客户端压缩={:?}, 强制模式={}, 服务端默认={:?}, 服务端默认压缩={:?}, 最终格式={:?}, 最终压缩={:?},最终加密={:?} device={:?}, user_id={:?}",
negotiation.serialization_format,
negotiation.serialization_format_specified,
negotiation.compression,
negotiation.is_forced,
self.default_serialization_format,
self.default_compression,
final_format,
final_compression,
final_encryption,
negotiation.device_info.as_ref().map(|d| &d.platform),
negotiation.user_id
);
}
async fn handle_device_conflict(
&self,
connection_id: &str,
negotiation: &negotiation::NegotiationResult,
) -> Vec<String> {
let mut conflict_connections = Vec::new();
debug!(
"[ServerCore] 设备冲突检测条件: device_manager={}, device_info={}, user_id={}",
self.device_manager.is_some(),
negotiation.device_info.is_some(),
negotiation.user_id.is_some()
);
if let (Some(device_mgr), Some(device_info)) =
(&self.device_manager, &negotiation.device_info)
{
if let Some(user_id) = &negotiation.user_id {
info!(
"[ServerCore] 🔍 开始设备冲突检测: user_id={}, connection_id={}, platform={:?}",
user_id, connection_id, device_info.platform
);
let manager_trait = self.connection_manager_trait();
let platform = device_info.platform.clone();
match device_handler::handle_device_conflict(
Some(Arc::clone(device_mgr)),
user_id,
connection_id,
&platform,
device_info,
manager_trait,
)
.await
{
Ok(conflict_result) => {
conflict_connections = conflict_result.conflict_connections;
conflict_connections.retain(|conn_id| {
if conn_id == connection_id {
error!(
"[ServerCore] ❌ 错误:冲突连接列表包含新连接ID,已过滤: connection_id={}",
connection_id
);
false
} else {
true
}
});
if !conflict_connections.is_empty() {
info!(
"[ServerCore] ⚠️ 检测到设备冲突: user_id={}, 新连接={}, 将踢掉 {} 个旧连接: {:?}",
user_id,
connection_id,
conflict_connections.len(),
conflict_connections
);
} else {
debug!(
"[ServerCore] ✅ 无设备冲突: user_id={}, platform={:?}, 新连接={}",
user_id, platform, connection_id
);
}
}
Err(e) => {
error!("[ServerCore] 设备冲突处理失败: {}", e);
}
}
} else {
debug!("[ServerCore] 跳过设备冲突检测: user_id 为空");
}
} else {
debug!(
"[ServerCore] 跳过设备冲突检测: device_manager={}, device_info={}",
self.device_manager.is_some(),
negotiation.device_info.is_some()
);
}
conflict_connections
}
async fn authenticate_connection(
&self,
frame: &Frame,
connection_id: &str,
negotiation: &negotiation::NegotiationResult,
) -> Result<(Option<String>, Option<HashMap<String, String>>)> {
let auth_user_id = negotiation.user_id.clone();
let auth_enabled = self.auth_enabled();
if !auth_enabled {
debug!("[ServerCore] 跳过 token 验证: 认证未启用");
return Ok((auth_user_id, Some(HashMap::new())));
}
let Some(authenticator) = &self.authenticator else {
return Ok((auth_user_id, Some(HashMap::new())));
};
let token = Self::extract_token_from_frame(frame);
let Some(token) = token else {
error!(
"[ServerCore] ❌ 未提供 token: connection_id={}",
connection_id
);
return Err(crate::common::error::FlareError::authentication_failed(
"未提供 token".to_string(),
));
};
debug!(
"[ServerCore] 🔐 开始验证 token: connection_id={}",
connection_id
);
let metadata = Self::extract_system_command_metadata(frame);
match authenticator
.authenticate(
&token,
connection_id,
negotiation.device_info.as_ref(),
metadata,
)
.await
{
Ok(auth_result) => {
if auth_result.authenticated {
debug!(
"[ServerCore] ✅ Token 验证成功: connection_id={}, user_id={:?}",
connection_id, auth_result.user_id
);
Ok((auth_result.user_id, auth_result.user_metadata))
} else {
let error_msg = auth_result
.error_message
.unwrap_or_else(|| "Token 验证失败".to_string());
error!(
"[ServerCore] ❌ Token 验证失败: connection_id={}, error={}",
connection_id, error_msg
);
Err(crate::common::error::FlareError::authentication_failed(
error_msg,
))
}
}
Err(e) => {
error!(
"[ServerCore] ❌ Token 验证过程出错: connection_id={}, error={}",
connection_id, e
);
Err(crate::common::error::FlareError::authentication_failed(
format!("验证过程出错: {}", e),
))
}
}
}
fn extract_token_from_frame(frame: &Frame) -> Option<String> {
frame.command.as_ref().and_then(|cmd| {
if let Some(crate::common::protocol::flare::core::commands::command::Type::System(
sys_cmd,
)) = &cmd.r#type
{
sys_cmd
.metadata
.get("token")
.and_then(|bytes| String::from_utf8(bytes.clone()).ok())
} else {
None
}
})
}
fn extract_system_command_metadata(
frame: &Frame,
) -> Option<&std::collections::HashMap<String, Vec<u8>>> {
frame.command.as_ref().and_then(|cmd| {
if let Some(crate::common::protocol::flare::core::commands::command::Type::System(
sys_cmd,
)) = &cmd.r#type
{
Some(&sys_cmd.metadata)
} else {
None
}
})
}
async fn update_connection_info(
&self,
connection_id: &str,
negotiation: &negotiation::NegotiationResult,
update: NegotiatedConnectionUpdate,
) {
let manager = Arc::clone(&self.connection_manager);
let auth_user_id = update.user_id.clone();
let user_id = auth_user_id.clone().or_else(|| negotiation.user_id.clone());
if let Err(e) = manager.update_connection_negotiation(
connection_id,
negotiation.device_info.clone(),
update.format,
update.compression,
update.encryption,
user_id.clone(),
update.metadata,
) {
error!("[ServerCore] 更新连接协商信息失败: {}", e);
return;
}
if let Some(user_id) = &user_id {
debug!(
"[ServerCore] 已更新连接协商信息: connection_id={}, user_id={}",
connection_id, user_id
);
if let Some((_, conn_info)) = manager.get_connection(connection_id) {
match conn_info.user_id {
Some(ref updated_user_id) => {
debug!(
"[ServerCore] ✅ 验证成功: 连接信息中的 user_id={}",
updated_user_id
);
}
None => {
error!("[ServerCore] ❌ 验证失败: 连接信息中的 user_id 仍为 None");
}
}
}
} else {
warn!(
"[ServerCore] ⚠️ 连接信息中没有 user_id: connection_id={}, negotiation.user_id={:?}, auth_user_id={:?}",
connection_id, negotiation.user_id, auth_user_id
);
}
}
async fn mark_connection_authenticated(
&self,
connection_id: &str,
auth_user_id: &Option<String>,
) {
let manager = Arc::clone(&self.connection_manager);
let manager_trait = manager as Arc<dyn ConnectionManagerTrait>;
let auth_enabled = self.auth_enabled();
if let Err(e) = manager_trait
.set_connection_authenticated(connection_id, auth_user_id.clone())
.await
{
error!("[ServerCore] 标记连接为已验证失败: {}", e);
return;
}
if auth_enabled {
debug!(
"[ServerCore] ✅ 连接已标记为已验证(认证通过): connection_id={}, user_id={:?}",
connection_id, auth_user_id
);
} else {
debug!(
"[ServerCore] ✅ 连接已标记为已验证(无需认证): connection_id={}, user_id={:?}",
connection_id, auth_user_id
);
}
}
fn create_connect_ack(
&self,
final_format: SerializationFormat,
final_compression: CompressionAlgorithm,
final_encryption: EncryptionAlgorithm,
conflict_connections: &[String],
) -> Frame {
let mut ack_metadata = std::collections::HashMap::new();
if !conflict_connections.is_empty() {
let conflicts_json =
serde_json::to_string(conflict_connections).unwrap_or_else(|_| "[]".to_string());
ack_metadata.insert(
"conflict_connections".to_string(),
conflicts_json.into_bytes(),
);
}
let connect_ack_cmd = negotiation::create_connect_ack(
final_format,
final_compression,
final_encryption, Some(ack_metadata),
);
frame_with_system_command(connect_ack_cmd, Reliability::AtLeastOnce)
}
}
#[async_trait]
impl ServerHandle for ServerCore {
async fn send_to(&self, connection_id: &str, frame: &Frame) -> Result<()> {
self.send_to(connection_id, frame).await
}
async fn send_to_user(&self, user_id: &str, frame: &Frame) -> Result<()> {
self.send_to_user(user_id, frame).await
}
async fn broadcast(&self, frame: &Frame) -> Result<()> {
self.broadcast(frame).await
}
async fn broadcast_except(&self, frame: &Frame, exclude_connection_id: &str) -> Result<()> {
self.broadcast_except(frame, exclude_connection_id).await
}
async fn disconnect(&self, connection_id: &str) -> Result<()> {
self.disconnect(connection_id).await
}
fn connection_count(&self) -> usize {
self.connection_count()
}
fn user_count(&self) -> usize {
self.user_count()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::common::message::ValidationMiddleware;
use crate::common::platform::{MonotonicInstant, monotonic_now};
use crate::transport::connection::Connection;
use crate::transport::events::ArcObserver;
struct TestConnection {
last_active: MonotonicInstant,
}
#[async_trait]
impl Connection for TestConnection {
fn add_observer(&mut self, _observer: ArcObserver) {}
fn remove_observer(&mut self, _observer: ArcObserver) {}
async fn send(&mut self, _data: &[u8]) -> Result<()> {
self.last_active = monotonic_now();
Ok(())
}
async fn close(&mut self) -> Result<()> {
Ok(())
}
fn last_active_time(&self) -> MonotonicInstant {
self.last_active
}
fn update_active_time(&mut self) {
self.last_active = monotonic_now();
}
}
#[test]
fn negotiation_uses_server_default_when_client_format_unspecified() {
let config = ServerConfig::default().with_format(SerializationFormat::Protobuf);
let core = ServerCore::new(&config, None);
let negotiation = negotiation::NegotiationResult::default();
let (format, compression, encryption) = core.determine_negotiation_result(&negotiation);
assert_eq!(format, SerializationFormat::Protobuf);
assert_eq!(compression, config.default_compression);
assert_eq!(encryption, config.default_encryption);
}
#[test]
fn negotiation_honors_explicit_client_format_metadata() {
let config = ServerConfig::default().with_format(SerializationFormat::Protobuf);
let core = ServerCore::new(&config, None);
let negotiation = negotiation::NegotiationResult {
serialization_format: SerializationFormat::Json,
serialization_format_specified: true,
..Default::default()
};
let (format, _, _) = core.determine_negotiation_result(&negotiation);
assert_eq!(format, SerializationFormat::Json);
}
#[tokio::test]
async fn shared_pipeline_is_reused_for_same_negotiation_profile() {
let config = ServerConfig::default().with_format(SerializationFormat::Protobuf);
let mut core = ServerCore::new(&config, None);
core.add_middleware(Arc::new(ValidationMiddleware::new("noop", |_| Ok(()))))
.await;
for connection_id in ["conn1", "conn2"] {
core.connection_manager
.add_connection(
connection_id.to_string(),
Box::new(TestConnection {
last_active: monotonic_now(),
}),
Some("user1".to_string()),
false,
)
.unwrap();
core.finalize_negotiation_after_ack(
connection_id,
SerializationFormat::Protobuf,
CompressionAlgorithm::None,
EncryptionAlgorithm::None,
)
.await;
}
let first_pipeline = core
.connection_manager
.get_connection("conn1")
.and_then(|(_, info)| info.cached_pipeline)
.expect("first connection should have cached pipeline");
let second_pipeline = core
.connection_manager
.get_connection("conn2")
.and_then(|(_, info)| info.cached_pipeline)
.expect("second connection should have cached pipeline");
assert!(
Arc::ptr_eq(&first_pipeline, &second_pipeline),
"connections with the same negotiated parser profile should share one pipeline"
);
}
}