[fix] 修正解析超大Redis响应包时进行边界处理的缺陷,该问题导致一些超大操作量的指令返回有异常。fix: https://github.com/NewLifeX/NewLife.Redis/issues/157

This commit is contained in:
智能大石头 2025-06-25 15:00:35 +08:00
parent e4fcca6d49
commit 13bb6e444e
5 changed files with 32 additions and 14 deletions

View File

@ -45,6 +45,8 @@ public ref struct BufferedReader
public void Advance(Int32 count)
{
if (count < 0) throw new ArgumentOutOfRangeException(nameof(count));
EnsureSpace(count);
if (_index + count > _span.Length) throw new ArgumentOutOfRangeException(nameof(count));
_index += count;

View File

@ -560,6 +560,10 @@ public class RedisClient : DisposeBase
{
arr[i] = ReadBlocks(ref reader, log);
}
else
{
throw new RedisException($"Invalid [{header}] on blocks[{i}/{len}]");
}
}
return arr;
@ -579,7 +583,8 @@ public class RedisClient : DisposeBase
// 读取数据包,并跳过换行符
var pk = reader.ReadPacket(len);
if (reader.FreeCapacity >= 2) reader.Advance(2);
//if (reader.FreeCapacity >= 2)
reader.Advance(2);
return pk;
}
@ -587,11 +592,13 @@ public class RedisClient : DisposeBase
private static String ReadLine(ref BufferedReader reader)
{
var sb = Pool.StringBuilder.Get();
var count = reader.FreeCapacity;
for (var i = 0; i < count; i++)
// 可能刚好一帧结束字符串至少需要2个字符
//reader.EnsureSpace(2);
//var count = reader.FreeCapacity;
while (true)
{
var b = (Char)reader.ReadByte();
if (b == '\r' && i + 1 < count)
if (b == '\r')
{
var b2 = (Char)reader.ReadByte();
if (b2 == '\n') break;
@ -610,11 +617,15 @@ public class RedisClient : DisposeBase
{
Span<Char> span = stackalloc Char[32];
var k = 0;
var count = reader.FreeCapacity;
for (var i = 0; i < count; i++)
// 缓冲读取器的FreeCapacity不准确它可以读取下一帧数据
// 可能刚好一帧结束字符串至少需要2个字符
//reader.EnsureSpace(2);
//var count = reader.FreeCapacity;
//var count = 16;
while (true)
{
var b = (Char)reader.ReadByte();
if (b == '\r' && i + 1 < count)
if (b == '\r')
{
var b2 = (Char)reader.ReadByte();
if (b2 == '\n') break;

View File

@ -114,6 +114,7 @@ public class HashTest
rh["0"] = new EventInfo { EventId = "1234", EventName = "Stone" };
}
[Fact(DisplayName = "获取所有数据丢失数据bug")]
public void ValuesHashTest()
{
@ -121,8 +122,8 @@ public class HashTest
//大批量数据获取,大概率会数据不完整,具体原有不明
var key = $"NewLife:HashTestInfo:Test";
{
_redis.MaxMessageSize = int.MaxValue;
var hash = _redis.GetDictionary<string>(key) as RedisHash<string, string>;
_redis.MaxMessageSize = Int32.MaxValue;
var hash = _redis.GetDictionary<String>(key) as RedisHash<String, String>;
hash.Clear();
for (var i = 0; i < 10000; i++)
{
@ -134,7 +135,11 @@ public class HashTest
var list = hash.Values.ToList();
for (var i = 0; i < list.Count; i++)
{
try { var item = list[i].ToJsonEntity<EventInfo>(); }
Assert.NotEmpty(list[i]);
try
{
var item = list[i].ToJsonEntity<EventInfo>();
}
catch (Exception ex)
{
//某块连续的数据段可能会不完整

View File

@ -14,7 +14,7 @@ namespace XUnitTest
public class RedisCacheTests
{
public readonly ServiceProvider provider;
private static readonly string prefix = "myPrefix:";
private static readonly String prefix = "myPrefix:";
public RedisCacheTests()
{
var services = new ServiceCollection();

View File

@ -597,13 +597,13 @@ public class RedisTest
var uservalue = rds.Get<User>("user");
var user2value = rds.Get<User>("user2");
var keys = new[] { "user", "user2" };
var keys = new[] { "user3", "user", "user2" };
var values = rds.GetAll<User>(keys);
// 应该返回2个值但是GetALl()这里只返回一个key的值
Assert.True(values.Count >= 2);
Assert.Equal(3, values.Count);
Assert.NotNull(values["user"]);
Assert.Null(values["user2"]);
Assert.Equal(user.Name, values["user"].Name);
Assert.Null(values["user3"]);
}
}