using System.Collections.Concurrent;
using System.Net.WebSockets;
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;
using Fleet.Core;
using Fleet.Rpc;
using Microsoft.Extensions.Logging;
namespace Fleet.Server;
///
/// 服务端连接宿主:在线表 + 服务端发起的调用 + 状态推送通知。每个 WebSocket 对应一个 。
///
public sealed class FleetLinkHost : IFleetConnections, IStateNotifier
{
private readonly ConcurrentDictionary _connections = new();
internal FleetLinkHost(
IFleetStore store,
FleetServerOptions options,
TimeProvider time,
ILogger logger,
IEnumerable stateProviders,
IEnumerable inboxes,
IEnumerable sinks,
FleetRouter router,
IServerIdentity? serverIdentity = null,
IBinaryAttestor? binaryAttestor = null)
{
ServerIdentity = serverIdentity;
BinaryAttestor = binaryAttestor;
Store = store;
Options = options;
Time = time;
Logger = logger;
StateProviders = [.. stateProviders];
Inboxes = inboxes.ToDictionary(i => i.Channel);
Sinks = [.. sinks];
Router = router.Inner;
RoleOf = router.RoleOf;
}
internal IFleetStore Store { get; }
/// 服务器的身份密钥;没有时节点连接用旧的明文认证。
internal IServerIdentity? ServerIdentity { get; }
/// 节点二进制的把关;没有时不检查。
internal IBinaryAttestor? BinaryAttestor { get; }
internal FleetServerOptions Options { get; }
internal TimeProvider Time { get; }
internal ILogger Logger { get; }
internal IReadOnlyList StateProviders { get; }
internal IReadOnlyDictionary Inboxes { get; }
internal IReadOnlyList Sinks { get; }
internal RpcRouter Router { get; }
internal Func? RoleOf { get; }
public IReadOnlyCollection OnlineNodes => [.. _connections.Keys];
public bool IsOnline(NodeId node) => _connections.ContainsKey(node);
public NodeSessionInfo? GetSession(NodeId node) =>
_connections.TryGetValue(node, out NodeConnection? c) ? c.Session : null;
/// 处理一条 WebSocket 连接,直到连接结束。
public async Task RunConnectionAsync(WebSocket socket, CancellationToken ct)
{
using var connection = new NodeConnection(this, socket);
await connection.RunAsync(ct);
}
/// 返回被替换掉的旧连接(如果有)。
internal NodeConnection? Register(NodeConnection connection)
{
NodeConnection? replaced = null;
_connections.AddOrUpdate(
connection.NodeId,
connection,
(_, existing) =>
{
replaced = existing;
return connection;
});
return replaced;
}
internal void Unregister(NodeConnection connection) =>
_connections.TryRemove(new KeyValuePair(connection.NodeId, connection));
public async ValueTask> RequestAsync(
NodeId node,
string method,
TParams parameters,
JsonTypeInfo paramsInfo,
JsonTypeInfo resultInfo,
CancellationToken ct)
{
if (!_connections.TryGetValue(node, out NodeConnection? connection))
{
return Result.Fail(FleetErrors.NodeOffline, "node is not connected");
}
Result raw = await connection.RequestAsync(method, JsonSerializer.SerializeToElement(parameters, paramsInfo), ct);
if (!raw.TryGet(out JsonElement value, out FleetError? error))
{
return Result.Fail(error);
}
try
{
TResult? result = value.Deserialize(resultInfo);
return result is null
? Result.Fail(FleetErrors.Internal, "empty result")
: Result.Ok(result);
}
catch (JsonException)
{
return Result.Fail(FleetErrors.Internal, "result could not be parsed");
}
}
public async ValueTask NotifyAsync(NodeId node, string method, TParams parameters, JsonTypeInfo paramsInfo, CancellationToken ct)
{
if (!_connections.TryGetValue(node, out NodeConnection? connection))
{
return false;
}
return await connection.SendAsync(RpcMessage.Notification(method, parameters, paramsInfo), ct);
}
public async ValueTask CloseAllAsync(string reason)
{
foreach (NodeConnection connection in _connections.Values.ToArray())
{
await connection.CloseAsync(reason);
}
}
public async ValueTask DisconnectAsync(NodeId node, string reason, CancellationToken ct)
{
if (_connections.TryGetValue(node, out NodeConnection? connection))
{
await connection.RevokeAsync(reason, ct);
}
}
public void NotifyChanged(NodeId node)
{
if (_connections.TryGetValue(node, out NodeConnection? connection))
{
connection.SignalStateChanged();
}
}
public void NotifyAllChanged()
{
foreach (NodeConnection connection in _connections.Values)
{
connection.SignalStateChanged();
}
}
}
/// 产品注册的 RPC 方法路由。
public sealed class FleetRouter
{
public FleetRouter(RpcRouter inner, Func? roleOf)
{
Inner = inner;
RoleOf = roleOf;
}
internal RpcRouter Inner { get; }
internal Func? RoleOf { get; }
/// 已注册的入站方法名(只读,测试用它核对声明表)。
public IReadOnlyCollection Methods => Inner.Methods;
public MethodTable Table => Inner.Table;
}