Skip to content
This repository was archived by the owner on Jul 9, 2023. It is now read-only.

Commit 6f77c51

Browse files
StephaneGrazianojustcoding121
authored andcommitted
Mixed implementation for startWith + PeekBytesAsync with the use of bufferpool (to get the best of the two worlds)
1 parent f213048 commit 6f77c51

File tree

6 files changed

+59
-28
lines changed

6 files changed

+59
-28
lines changed

src/StreamExtended/Network/CopyStream.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,9 @@ public byte PeekByteFromBuffer(int index)
5555
return reader.PeekByteAsync(index, cancellationToken);
5656
}
5757

58-
public Task<byte[]> PeekBytesAsync(int index, int size, CancellationToken cancellationToken = default(CancellationToken))
58+
public Task<int> PeekBytesAsync(byte[] buffer, int offset, int index, int size, CancellationToken cancellationToken = default(CancellationToken))
5959
{
60-
return reader.PeekBytesAsync(index, size, cancellationToken);
60+
return reader.PeekBytesAsync(buffer, offset, index, size, cancellationToken);
6161
}
6262

6363
public void Flush()

src/StreamExtended/Network/CustomBufferedPeekStream.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,14 @@ byte ICustomStreamReader.PeekByteFromBuffer(int index)
8888
/// <summary>
8989
/// Peeks bytes asynchronous.
9090
/// </summary>
91+
/// <param name="buffer">The buffer to copy.</param>
92+
/// <param name="offset">The offset where copying.</param>
9193
/// <param name="index">The index.</param>
9294
/// <param name="cancellationToken">The cancellation token.</param>
9395
/// <returns></returns>
94-
Task<byte[]> ICustomStreamReader.PeekBytesAsync(int index, int size, CancellationToken cancellationToken)
96+
Task<int> ICustomStreamReader.PeekBytesAsync(byte[] buffer, int offset, int index, int size, CancellationToken cancellationToken)
9597
{
96-
return baseStream.PeekBytesAsync(index, size, cancellationToken);
98+
return baseStream.PeekBytesAsync(buffer, offset, index, size, cancellationToken);
9799
}
98100

99101
/// <summary>

src/StreamExtended/Network/CustomBufferedStream.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -252,10 +252,12 @@ public override int ReadByte()
252252
/// <summary>
253253
/// Peeks bytes asynchronous.
254254
/// </summary>
255+
/// <param name="buffer">The buffer to copy.</param>
256+
/// <param name="offset">The offset where copying.</param>
255257
/// <param name="index">The index.</param>
256258
/// <param name="cancellationToken">The cancellation token.</param>
257259
/// <returns></returns>
258-
public async Task<byte[]> PeekBytesAsync(int index, int size, CancellationToken cancellationToken = default(CancellationToken))
260+
public async Task<int> PeekBytesAsync(byte[] buffer, int offset, int index, int size, CancellationToken cancellationToken = default(CancellationToken))
259261
{
260262
if (Available <= index)
261263
{
@@ -270,12 +272,12 @@ public override int ReadByte()
270272

271273
if (Available <= (index + size))
272274
{
273-
return null;
275+
return -1;
274276
}
275277

276-
var vRet = new byte[size];
277-
Array.Copy(streamBuffer, index, vRet, 0, size);
278-
return vRet;
278+
Buffer.BlockCopy(streamBuffer, index, buffer, offset, size);
279+
280+
return size;
279281
}
280282

281283
/// <summary>

src/StreamExtended/Network/ICustomStreamReader.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,12 @@ public interface ICustomStreamReader
4040
/// <summary>
4141
/// Peeks bytes asynchronous.
4242
/// </summary>
43+
/// <param name="buffer">The buffer to copy.</param>
44+
/// <param name="offset">The offset where copying.</param>
4345
/// <param name="index">The index.</param>
4446
/// <param name="cancellationToken">The cancellation token.</param>
4547
/// <returns></returns>
46-
Task<byte[]> PeekBytesAsync(int index, int size, CancellationToken cancellationToken = default(CancellationToken));
48+
Task<int> PeekBytesAsync(byte[] buffer, int offset, int index, int size, CancellationToken cancellationToken = default(CancellationToken));
4749

4850
byte ReadByteFromBuffer();
4951

src/Titanium.Web.Proxy/ExplicitClientHandler.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ private async Task handleClient(ExplicitProxyEndPoint endPoint, TcpClientConnect
4949
TunnelConnectSessionEventArgs connectArgs = null;
5050

5151
// Client wants to create a secure tcp tunnel (probably its a HTTPS or Websocket request)
52-
if (await HttpHelper.IsConnectMethod(clientStream, cancellationToken) == 1)
52+
if (await HttpHelper.IsConnectMethod(clientStream, BufferPool, BufferSize, cancellationToken) == 1)
5353
{
5454
// read the first line HTTP command
5555
string httpCmd = await clientStream.ReadLineAsync(cancellationToken);
@@ -220,7 +220,7 @@ await clientStreamWriter.WriteResponseAsync(connectArgs.HttpClient.Response,
220220
$"Couldn't authenticate host '{connectHostname}' with certificate '{certName}'.", e, connectArgs);
221221
}
222222

223-
if (await HttpHelper.IsConnectMethod(clientStream) == -1)
223+
if (await HttpHelper.IsConnectMethod(clientStream, BufferPool, BufferSize, cancellationToken) == -1)
224224
{
225225
decryptSsl = false;
226226
}
@@ -292,7 +292,7 @@ await TcpHelper.SendRaw(clientStream, connection.Stream, BufferPool, BufferSize,
292292
}
293293
}
294294

295-
if (connectArgs != null && await HttpHelper.IsPriMethod(clientStream) == 1)
295+
if (connectArgs != null && await HttpHelper.IsPriMethod(clientStream, BufferPool, BufferSize, cancellationToken) == 1)
296296
{
297297
// todo
298298
string httpCmd = await clientStream.ReadLineAsync(cancellationToken);

src/Titanium.Web.Proxy/Helpers/HttpHelper.cs

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Text.RegularExpressions;
44
using System.Threading;
55
using System.Threading.Tasks;
6+
using StreamExtended;
67
using StreamExtended.Network;
78
using Titanium.Web.Proxy.Extensions;
89
using Titanium.Web.Proxy.Http;
@@ -122,19 +123,19 @@ internal static string GetWildCardDomainName(string hostname)
122123
/// </summary>
123124
/// <param name="clientStreamReader">The client stream reader.</param>
124125
/// <returns>1: when CONNECT, 0: when valid HTTP method, -1: otherwise</returns>
125-
internal static Task<int> IsConnectMethod(ICustomStreamReader clientStreamReader, CancellationToken cancellationToken = default(CancellationToken))
126+
internal static Task<int> IsConnectMethod(ICustomStreamReader clientStreamReader, IBufferPool bufferPool, int bufferSize, CancellationToken cancellationToken = default(CancellationToken))
126127
{
127-
return startsWith(clientStreamReader, "CONNECT", cancellationToken);
128+
return startsWith(clientStreamReader, bufferPool, bufferSize, "CONNECT", cancellationToken);
128129
}
129130

130131
/// <summary>
131132
/// Determines whether is pri method (HTTP/2).
132133
/// </summary>
133134
/// <param name="clientStreamReader">The client stream reader.</param>
134135
/// <returns>1: when PRI, 0: when valid HTTP method, -1: otherwise</returns>
135-
internal static Task<int> IsPriMethod(ICustomStreamReader clientStreamReader, CancellationToken cancellationToken = default(CancellationToken))
136+
internal static Task<int> IsPriMethod(ICustomStreamReader clientStreamReader, IBufferPool bufferPool, int bufferSize, CancellationToken cancellationToken = default(CancellationToken))
136137
{
137-
return startsWith(clientStreamReader, "PRI", cancellationToken);
138+
return startsWith(clientStreamReader, bufferPool, bufferSize, "PRI", cancellationToken);
138139
}
139140

140141
/// <summary>
@@ -145,22 +146,46 @@ internal static string GetWildCardDomainName(string hostname)
145146
/// <returns>
146147
/// 1: when starts with the given string, 0: when valid HTTP method, -1: otherwise
147148
/// </returns>
148-
private static async Task<int> startsWith(ICustomStreamReader clientStreamReader, string expectedStart, CancellationToken cancellationToken = default(CancellationToken))
149+
private static async Task<int> startsWith(ICustomStreamReader clientStreamReader, IBufferPool bufferPool, int bufferSize, string expectedStart, CancellationToken cancellationToken = default(CancellationToken))
149150
{
150151
int iRet = -1;
151-
int lengthToCheck = 10;
152-
153-
var vBuffer = await clientStreamReader.PeekBytesAsync(0, lengthToCheck, cancellationToken);
154-
if (vBuffer != null)
152+
const int lengthToCheck = 10;
153+
byte[] buffer = null;
154+
try
155155
{
156-
var httpMethod = defaultEncoding.GetString(vBuffer);
156+
buffer = bufferPool.GetBuffer(Math.Max(bufferSize, lengthToCheck));
157157

158-
if (httpMethod.StartsWith(expectedStart))
159-
iRet = 1;
160-
else if (Regex.Match(httpMethod, @"^[a-z]{3,} ", RegexOptions.IgnoreCase).Success) //valid HTTP requests start by at least 3 letters + space
161-
iRet = 0;
162-
}
158+
int peeked = await clientStreamReader.PeekBytesAsync(buffer, 0, 0, lengthToCheck, cancellationToken);
159+
160+
if (peeked > 0)
161+
{
162+
bool isExpected = true;
163+
164+
for (int i = 0; i < lengthToCheck; i++)
165+
{
166+
int b = buffer[i];
163167

168+
if (b == ' ' && i > 2)
169+
return isExpected ? 1 : 0;
170+
else
171+
{
172+
char ch = (char)b;
173+
if (!char.IsLetter(ch))
174+
return -1;
175+
else if (i >= expectedStart.Length || ch != expectedStart[i])
176+
isExpected = false;
177+
}
178+
}
179+
180+
// only letters
181+
iRet = isExpected ? 1 : 0;
182+
}
183+
}
184+
finally
185+
{
186+
bufferPool.ReturnBuffer(buffer);
187+
buffer = null;
188+
}
164189
return iRet;
165190
}
166191
}

0 commit comments

Comments
 (0)