168 lines
6.3 KiB
C#
168 lines
6.3 KiB
C#
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||
|
||
using System;
|
||
using System.Text;
|
||
using System.Threading;
|
||
|
||
namespace Cysharp.Threading.Tasks
|
||
{
|
||
// UniTask has no scheduler like TaskScheduler.
|
||
// Only handle unobserved exception.
|
||
|
||
public static class UniTaskScheduler
|
||
{
|
||
/// <summary>
|
||
/// 安全清理异常消息字符串,防止日志注入攻击
|
||
/// 清理控制字符、限制长度、转义特殊字符
|
||
/// </summary>
|
||
/// <param name="message">原始异常消息</param>
|
||
/// <param name="maxLength">最大长度限制,默认1000字符</param>
|
||
/// <returns>清理后的安全消息字符串</returns>
|
||
static string SanitizeExceptionMessage(string message, int maxLength = 1000)
|
||
{
|
||
if (string.IsNullOrEmpty(message))
|
||
{
|
||
return string.Empty;
|
||
}
|
||
|
||
// 创建StringBuilder用于构建清理后的消息
|
||
var sanitized = new StringBuilder(message.Length);
|
||
|
||
// 遍历每个字符进行清理
|
||
for (int i = 0; i < message.Length && sanitized.Length < maxLength; i++)
|
||
{
|
||
char c = message[i];
|
||
|
||
// 保留可打印字符(ASCII 32-126)和常见的Unicode字符
|
||
// 移除控制字符(ASCII 0-31,除了换行符和制表符)
|
||
if (char.IsControl(c) && c != '\n' && c != '\r' && c != '\t')
|
||
{
|
||
// 将控制字符替换为安全的转义表示
|
||
sanitized.Append($"\\u{(int)c:X4}");
|
||
}
|
||
else if (c == '\n')
|
||
{
|
||
// 将换行符替换为安全表示,防止日志注入
|
||
sanitized.Append("\\n");
|
||
}
|
||
else if (c == '\r')
|
||
{
|
||
// 将回车符替换为安全表示
|
||
sanitized.Append("\\r");
|
||
}
|
||
else if (c == '\t')
|
||
{
|
||
// 将制表符替换为空格
|
||
sanitized.Append(" ");
|
||
}
|
||
else
|
||
{
|
||
// 保留正常的可打印字符
|
||
sanitized.Append(c);
|
||
}
|
||
}
|
||
|
||
// 如果消息被截断,添加截断标记
|
||
if (message.Length > maxLength)
|
||
{
|
||
sanitized.Append("...[消息已截断]");
|
||
}
|
||
|
||
return sanitized.ToString();
|
||
}
|
||
public static event Action<Exception> UnobservedTaskException;
|
||
|
||
/// <summary>
|
||
/// Propagate OperationCanceledException to UnobservedTaskException when true. Default is false.
|
||
/// </summary>
|
||
public static bool PropagateOperationCanceledException = false;
|
||
|
||
#if UNITY_2018_3_OR_NEWER
|
||
|
||
/// <summary>
|
||
/// Write log type when catch unobserved exception and not registered UnobservedTaskException. Default is Exception.
|
||
/// </summary>
|
||
public static UnityEngine.LogType UnobservedExceptionWriteLogType = UnityEngine.LogType.Exception;
|
||
|
||
/// <summary>
|
||
/// Dispatch exception event to Unity MainThread. Default is true.
|
||
/// </summary>
|
||
public static bool DispatchUnityMainThread = true;
|
||
|
||
// cache delegate.
|
||
static readonly SendOrPostCallback handleExceptionInvoke = InvokeUnobservedTaskException;
|
||
|
||
static void InvokeUnobservedTaskException(object state)
|
||
{
|
||
UnobservedTaskException((Exception)state);
|
||
}
|
||
#endif
|
||
|
||
internal static void PublishUnobservedTaskException(Exception ex)
|
||
{
|
||
if (ex != null)
|
||
{
|
||
if (!PropagateOperationCanceledException && ex is OperationCanceledException)
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (UnobservedTaskException != null)
|
||
{
|
||
#if UNITY_2018_3_OR_NEWER
|
||
if (!DispatchUnityMainThread || Thread.CurrentThread.ManagedThreadId == PlayerLoopHelper.MainThreadId)
|
||
{
|
||
// allows inlining call.
|
||
UnobservedTaskException.Invoke(ex);
|
||
}
|
||
else
|
||
{
|
||
// Post to MainThread.
|
||
PlayerLoopHelper.UnitySynchronizationContext.Post(handleExceptionInvoke, ex);
|
||
}
|
||
#else
|
||
UnobservedTaskException.Invoke(ex);
|
||
#endif
|
||
}
|
||
else
|
||
{
|
||
#if UNITY_2018_3_OR_NEWER
|
||
string msg = null;
|
||
if (UnobservedExceptionWriteLogType != UnityEngine.LogType.Exception)
|
||
{
|
||
// 使用安全清理方法处理异常消息,防止日志注入攻击
|
||
string safeExceptionMessage = SanitizeExceptionMessage(ex.ToString());
|
||
msg = "UnobservedTaskException: " + safeExceptionMessage;
|
||
}
|
||
switch (UnobservedExceptionWriteLogType)
|
||
{
|
||
case UnityEngine.LogType.Error:
|
||
UnityEngine.Debug.LogError(msg);
|
||
break;
|
||
case UnityEngine.LogType.Assert:
|
||
UnityEngine.Debug.LogAssertion(msg);
|
||
break;
|
||
case UnityEngine.LogType.Warning:
|
||
UnityEngine.Debug.LogWarning(msg);
|
||
break;
|
||
case UnityEngine.LogType.Log:
|
||
UnityEngine.Debug.Log(msg);
|
||
break;
|
||
case UnityEngine.LogType.Exception:
|
||
UnityEngine.Debug.LogException(ex);
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
#else
|
||
// 使用安全清理方法处理异常消息,防止日志注入攻击
|
||
string safeMessage = SanitizeExceptionMessage(ex.ToString());
|
||
Console.WriteLine("UnobservedTaskException: " + safeMessage);
|
||
#endif
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|