Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="TouchSocket.WebApi" Version="4.0.0" />
<PackageReference Include="TouchSocket.Http" Version="4.0.2" />
</ItemGroup>
</Project>

Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,10 @@ internal enum RouteType
internal sealed class MyTcpSessionClientBase : TcpSessionClient
{
#region Paths
public static ReadOnlySpan<byte> Json => "/json"u8;
public static ReadOnlySpan<byte> Plaintext => "/plaintext"u8;
private static ReadOnlySpan<byte> Json => "/json"u8;
private static ReadOnlySpan<byte> Plaintext => "/plaintext"u8;
#endregion


private static ReadOnlySpan<byte> PlainTextBody => "Hello, World!"u8;
private static ReadOnlySpan<byte> JsonBody => "{\"message\":\"Hello, World!\"}"u8;

Expand All @@ -68,14 +67,30 @@ protected override async Task ReceiveLoopAsync(ITransport transport)

while (true)
{
var readResult = await pipeReader.ReadAsync();
ValueTask<ReadResult> readTask = pipeReader.ReadAsync();
ReadResult readResult;

if (readTask.IsCompleted)
{
readResult = readTask.Result;
}
else
{
readResult = await readTask.ConfigureAwait(false);
}
Comment on lines +73 to +80
Copy link

Copilot AI Dec 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both branches of this 'if' statement write to the same variable - consider using '?' to express intent better.

Suggested change
if (readTask.IsCompleted)
{
readResult = readTask.Result;
}
else
{
readResult = await readTask.ConfigureAwait(false);
}
readResult = await readTask.ConfigureAwait(false);

Copilot uses AI. Check for mistakes.

var bufferSequence = readResult.Buffer;

var totalConsumed = ProcessRequests(bufferSequence, pipeWriter, out var responseCount);

if (responseCount > 0)
{
await pipeWriter.FlushAsync();
ValueTask<FlushResult> flushTask = pipeWriter.FlushAsync();

if (!flushTask.IsCompleted)
{
await flushTask.ConfigureAwait(false);
}
}

if (totalConsumed > 0)
Expand All @@ -94,7 +109,7 @@ protected override async Task ReceiveLoopAsync(ITransport transport)
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static long ProcessRequests(ReadOnlySequence<byte> buffer, PipeWriter writer, out int responseCount)
{
var seqReader = new SequenceReader<byte>(buffer);
Expand All @@ -105,23 +120,20 @@ private static long ProcessRequests(ReadOnlySequence<byte> buffer, PipeWriter wr
{
var startConsumed = seqReader.Consumed;

// 请求行
if (!TryReadLine(ref seqReader, out var requestLineLength))
{
// 请求行不完整,不消费任何数据
break;
}

var requestLineConsumed = requestLineLength + 2;

// 读取Headers,直到空行;若不完整,回退并等待更多数据
var headersStartConsumed = seqReader.Consumed;
bool headersComplete = false;

while (!seqReader.End)
{
if (!TryReadLine(ref seqReader, out var headerLength))
{
// 回退到读取Headers前的位置,等待更多数据
var rewind = seqReader.Consumed - headersStartConsumed;
if (rewind > 0)
{
Expand All @@ -134,163 +146,118 @@ private static long ProcessRequests(ReadOnlySequence<byte> buffer, PipeWriter wr
if (headerLength == 0)
{
headersComplete = true;
break; // headers 结束
break;
}
}

if (!headersComplete)
{
// 不完整,等待更多数据
break;
}

// 解析URL - 直接在原始位置解析,避免Slice
var routeType = ParseUrlFast(buffer.Slice(startConsumed), requestLineConsumed);

// 计算本次消费的字节数
var consumed = seqReader.Consumed - startConsumed;
totalConsumed += consumed;

// 写入响应
WriteResponseSync(writer, routeType);
responseCount++;
}

return totalConsumed;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static void WriteResponseSync(PipeWriter writer, RouteType routeType)
{
switch (routeType)
if (routeType == RouteType.Plaintext)
{
case RouteType.Plaintext:
writer.Write(PlaintextPreamble);
writer.Write(DateHeader.HeaderBytes);
writer.Write(PlainTextBody);
break;
case RouteType.Json:
writer.Write(JsonPreamble);
writer.Write(DateHeader.HeaderBytes);
writer.Write(JsonBody);
break;
writer.Write(PlaintextPreamble);
writer.Write(DateHeader.HeaderBytes);
writer.Write(PlainTextBody);
}
else if (routeType == RouteType.Json)
{
writer.Write(JsonPreamble);
writer.Write(DateHeader.HeaderBytes);
writer.Write(JsonBody);
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static bool TryReadLine(ref SequenceReader<byte> reader, out long length)
{
var start = reader.Consumed;

while (!reader.End)
if (reader.TryAdvanceTo((byte)'\r', advancePastDelimiter: false))
{
if (reader.TryRead(out var b))
var start = reader.Consumed;

if (!reader.TryPeek(1, out var next))
{
if (b == '\r')
{
// 查看是否有'\n',若暂不可用则回退并判定不完整
if (!reader.TryPeek(out var next))
{
// 回退已读取的'\r'
reader.Rewind(1);
length = 0;
return false;
}
if (next == '\n')
{
// 消费'\n'并返回本行长度(不含CRLF)
reader.Advance(1);
length = reader.Consumed - start - 2;
return true;
}
}
length = 0;
return false;
}

if (next == '\n')
{
var lineLength = reader.Consumed;
reader.Advance(2);
length = lineLength - start;
Comment on lines +192 to +204
Copy link

Copilot AI Dec 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TryReadLine method has a logic error. On line 192, start is set to reader.Consumed after already advancing to '\r'. Then on line 202, lineLength is set to the same value (reader.Consumed hasn't changed). On line 204, length = lineLength - start will always be 0, which is incorrect.

The correct logic should capture the position before advancing to '\r', or calculate the length differently. The line length should represent the number of bytes in the line excluding CRLF.

Copilot uses AI. Check for mistakes.
return true;
}

reader.Advance(1);
return TryReadLine(ref reader, out length);
Comment on lines +208 to +209
Copy link

Copilot AI Dec 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The recursive call to TryReadLine on line 209 can lead to stack overflow for malformed input. If the input contains many '\r' characters not followed by '\n', this will recursively call itself multiple times. Consider using a loop instead of recursion to handle cases where '\r' is not followed by '\n'.

Copilot uses AI. Check for mistakes.
}

length = 0;
return false;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static RouteType ParseUrlFast(ReadOnlySequence<byte> sequence, long requestLineLength)
{
var reader = new SequenceReader<byte>(sequence);

// 跳过方法
var spaceCount = 0;
var urlStart = 0L;
var urlEnd = 0L;

while (reader.Consumed < requestLineLength && !reader.End)
if (!reader.TryAdvanceTo((byte)' ', advancePastDelimiter: true))
{
if (reader.TryRead(out var b))
{
if (b == ' ')
{
spaceCount++;
if (spaceCount == 1)
{
urlStart = reader.Consumed;
}
else if (spaceCount == 2)
{
urlEnd = reader.Consumed - 1;
break;
}
}
}
return RouteType.Unknown;
}

if (spaceCount < 2)
var urlStart = reader.Consumed;

if (!reader.TryAdvanceTo((byte)' ', advancePastDelimiter: false))
{
return RouteType.Unknown;
}

var urlLength = urlEnd - urlStart;
var urlLength = reader.Consumed - urlStart;

// 截取URL片段
var startPos = sequence.GetPosition(urlStart);
var urlSlice = sequence.Slice(startPos, urlLength);

// "/plaintext"
if (urlLength == Plaintext.Length)
{
var urlSlice = sequence.Slice(urlStart, urlLength);

if (urlSlice.IsSingleSegment)
{
if (urlSlice.FirstSpan.SequenceEqual(Plaintext))
{
return RouteType.Plaintext;
}
}
else
{
Span<byte> tmp = stackalloc byte[(int)urlLength];
urlSlice.CopyTo(tmp);
if (tmp.SequenceEqual(Plaintext))
{
return RouteType.Plaintext;
}
return urlSlice.FirstSpan.SequenceEqual(Plaintext) ? RouteType.Plaintext : RouteType.Unknown;
}

Span<byte> tmp = stackalloc byte[(int)urlLength];
urlSlice.CopyTo(tmp);
return tmp.SequenceEqual(Plaintext) ? RouteType.Plaintext : RouteType.Unknown;
}
// "/json"
else if (urlLength == Json.Length)

if (urlLength == Json.Length)
{
var urlSlice = sequence.Slice(urlStart, urlLength);
Comment on lines +237 to +251
Copy link

Copilot AI Dec 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In ParseUrlFast, the urlSlice variable is created inside each length-check block (lines 237 and 251), but it's created using positions relative to the original sequence. The urlStart value comes from reader.Consumed (line 226), which is the position AFTER advancing past the first space. However, when calling sequence.Slice(urlStart, urlLength), this should work correctly if urlStart represents the absolute position in the sequence. Consider verifying that sequence.Slice() is being called with the correct position, as SequenceReader.Consumed returns the total bytes consumed, not a SequencePosition.

Copilot uses AI. Check for mistakes.

if (urlSlice.IsSingleSegment)
{
if (urlSlice.FirstSpan.SequenceEqual(Json))
{
return RouteType.Json;
}
}
else
{
Span<byte> tmp = stackalloc byte[(int)urlLength];
urlSlice.CopyTo(tmp);
if (tmp.SequenceEqual(Json))
{
return RouteType.Json;
}
return urlSlice.FirstSpan.SequenceEqual(Json) ? RouteType.Json : RouteType.Unknown;
}

Span<byte> tmp = stackalloc byte[(int)urlLength];
urlSlice.CopyTo(tmp);
return tmp.SequenceEqual(Json) ? RouteType.Json : RouteType.Unknown;
}

return RouteType.Unknown;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="TouchSocket" Version="4.0.0" />
<PackageReference Include="TouchSocket" Version="4.0.2" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0" />
<PackageReference Include="TouchSocket.Hosting" Version="4.0.0" />
<PackageReference Include="TouchSocket.WebApi" Version="4.0.0" />
<PackageReference Include="TouchSocket.Hosting" Version="4.0.2" />
<PackageReference Include="TouchSocket.WebApi" Version="4.0.2" />
</ItemGroup>
</Project>
Loading