Replace Probe with Agent across node runtime, Fleet contracts, backend state, API and storage. Report incoming sessions and outgoing link health through each Agent uplink, and retain directed link controls and retry. Update protocol fixtures and end-to-end coverage for the unified model; accept requested cancellation during the Fleet timeout test cleanup.
45 lines
2.2 KiB
C#
45 lines
2.2 KiB
C#
using System.Net.Sockets;
|
|
using System.Security.Authentication;
|
|
using MoShiro.Protocol;
|
|
|
|
namespace MoShiro.Agent;
|
|
|
|
/// <summary>控制连接在已建立之后停止应答:对端不认这个会话了,或者线路中断超过了空闲上限。</summary>
|
|
public sealed class ControlSilentException(string message) : IOException(message);
|
|
|
|
/// <summary>把连接各阶段的异常归类成上报给面板的 <see cref="LinkFault"/>:失败发生在哪个阶段、属于哪一类。</summary>
|
|
public static class LinkFaults
|
|
{
|
|
private const int MaxMessage = 200;
|
|
|
|
public static LinkFault From(Exception ex, string phase)
|
|
{
|
|
Exception inner = ex is AggregateException { InnerExceptions.Count: 1 } aggregate ? aggregate.InnerExceptions[0] : ex;
|
|
string kind = inner switch
|
|
{
|
|
ControlSilentException => "silent",
|
|
OperationCanceledException or TimeoutException => "timeout",
|
|
AuthenticationException => "tls",
|
|
SocketException socket => Kind(socket.SocketErrorCode),
|
|
IOException { InnerException: SocketException socket } => Kind(socket.SocketErrorCode),
|
|
InvalidOperationException invalid when invalid.Message.Contains("authentication", StringComparison.OrdinalIgnoreCase)
|
|
|| invalid.Message.Contains("challenge", StringComparison.OrdinalIgnoreCase) => "auth",
|
|
IOException => "closed",
|
|
_ => "other",
|
|
};
|
|
string message = kind == "timeout" && inner is OperationCanceledException ? $"{phase} did not finish in time" : inner.Message;
|
|
return new LinkFault { Phase = phase, Kind = kind, Message = message.Length > MaxMessage ? message[..MaxMessage] : message };
|
|
}
|
|
|
|
public static LinkFault Closed(string phase, string message) => new() { Phase = phase, Kind = "closed", Message = message };
|
|
|
|
private static string Kind(SocketError error) => error switch
|
|
{
|
|
SocketError.ConnectionRefused => "refused",
|
|
SocketError.ConnectionReset or SocketError.ConnectionAborted => "reset",
|
|
SocketError.HostNotFound or SocketError.NoData or SocketError.TryAgain => "dns",
|
|
SocketError.TimedOut => "timeout",
|
|
_ => "other",
|
|
};
|
|
}
|