Commit 2f32d5bd authored by justcoding121's avatar justcoding121 Committed by justcoding121

Merge pull request #218 from honfika/develop

Performance improvements: Buffered stream, avoid string concatenation…
parents eca19fc3 3b8ad6d8
...@@ -14,18 +14,33 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -14,18 +14,33 @@ namespace Titanium.Web.Proxy.Helpers
/// </summary> /// </summary>
internal class CustomBinaryReader : IDisposable internal class CustomBinaryReader : IDisposable
{ {
private readonly Stream stream; private readonly CustomBufferedStream stream;
private readonly int bufferSize;
private readonly Encoding encoding; private readonly Encoding encoding;
private readonly byte[] buffer;
internal CustomBinaryReader(Stream stream, int bufferSize) [ThreadStatic]
private static byte[] staticBuffer;
private byte[] buffer
{
get
{
if (staticBuffer == null || staticBuffer.Length != bufferSize)
{
staticBuffer = new byte[bufferSize];
}
return staticBuffer;
}
}
internal CustomBinaryReader(CustomBufferedStream stream, int bufferSize)
{ {
this.stream = stream; this.stream = stream;
this.bufferSize = bufferSize;
//default to UTF-8 //default to UTF-8
encoding = Encoding.UTF8; encoding = Encoding.UTF8;
buffer = new byte[bufferSize];
} }
internal Stream BaseStream => stream; internal Stream BaseStream => stream;
...@@ -40,12 +55,13 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -40,12 +55,13 @@ namespace Titanium.Web.Proxy.Helpers
int bufferDataLength = 0; int bufferDataLength = 0;
// try to use the instance buffer, usually it is enough // try to use the thread static buffer, usually it is enough
var buffer = this.buffer; var buffer = this.buffer;
while (await stream.ReadAsync(buffer, bufferDataLength, 1) > 0) while (stream.DataAvailable || await stream.FillBufferAsync())
{ {
var newChar = buffer[bufferDataLength]; var newChar = stream.ReadByteFromBuffer();
buffer[bufferDataLength] = newChar;
//if new line //if new line
if (lastChar == '\r' && newChar == '\n') if (lastChar == '\r' && newChar == '\n')
...@@ -111,7 +127,11 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -111,7 +127,11 @@ namespace Titanium.Web.Proxy.Helpers
if (totalBytesToRead < bufferSize) if (totalBytesToRead < bufferSize)
bytesToRead = (int)totalBytesToRead; bytesToRead = (int)totalBytesToRead;
var buffer = bytesToRead > this.buffer.Length ? new byte[bytesToRead] : this.buffer; var buffer = this.buffer;
if (bytesToRead > buffer.Length)
{
buffer = new byte[bytesToRead];
}
int bytesRead; int bytesRead;
var totalBytesRead = 0; var totalBytesRead = 0;
......
This diff is collapsed.
using System; using System;
using System.IO;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Models namespace Titanium.Web.Proxy.Models
{ {
...@@ -28,6 +30,7 @@ namespace Titanium.Web.Proxy.Models ...@@ -28,6 +30,7 @@ namespace Titanium.Web.Proxy.Models
/// Header Name. /// Header Name.
/// </summary> /// </summary>
public string Name { get; set; } public string Name { get; set; }
/// <summary> /// <summary>
/// Header Value. /// Header Value.
/// </summary> /// </summary>
...@@ -41,5 +44,12 @@ namespace Titanium.Web.Proxy.Models ...@@ -41,5 +44,12 @@ namespace Titanium.Web.Proxy.Models
{ {
return $"{Name}: {Value}"; return $"{Name}: {Value}";
} }
internal async Task WriteToStream(StreamWriter writer)
{
await writer.WriteAsync(Name);
await writer.WriteAsync(": ");
await writer.WriteLineAsync(Value);
}
} }
} }
\ No newline at end of file
...@@ -45,7 +45,7 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -45,7 +45,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
Stream clientStream, IPEndPoint upStreamEndPoint) Stream clientStream, IPEndPoint upStreamEndPoint)
{ {
TcpClient client; TcpClient client;
Stream stream; CustomBufferedStream stream;
bool isLocalhost = (externalHttpsProxy == null && externalHttpProxy == null) ? false : NetworkHelper.IsLocalIpAddress(remoteHostName); bool isLocalhost = (externalHttpsProxy == null && externalHttpProxy == null) ? false : NetworkHelper.IsLocalIpAddress(remoteHostName);
...@@ -61,7 +61,7 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -61,7 +61,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
{ {
client = new TcpClient(upStreamEndPoint); client = new TcpClient(upStreamEndPoint);
await client.ConnectAsync(externalHttpsProxy.HostName, externalHttpsProxy.Port); await client.ConnectAsync(externalHttpsProxy.HostName, externalHttpsProxy.Port);
stream = client.GetStream(); stream = new CustomBufferedStream(client.GetStream(), bufferSize);
using (var writer = new StreamWriter(stream, Encoding.ASCII, bufferSize, true) { NewLine = ProxyConstants.NewLine }) using (var writer = new StreamWriter(stream, Encoding.ASCII, bufferSize, true) { NewLine = ProxyConstants.NewLine })
{ {
...@@ -83,7 +83,6 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -83,7 +83,6 @@ namespace Titanium.Web.Proxy.Network.Tcp
{ {
var result = await reader.ReadLineAsync(); var result = await reader.ReadLineAsync();
if (!new[] { "200 OK", "connection established" }.Any(s => result.ContainsIgnoreCase(s))) if (!new[] { "200 OK", "connection established" }.Any(s => result.ContainsIgnoreCase(s)))
{ {
throw new Exception("Upstream proxy failed to create a secure tunnel"); throw new Exception("Upstream proxy failed to create a secure tunnel");
...@@ -96,7 +95,7 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -96,7 +95,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
{ {
client = new TcpClient(upStreamEndPoint); client = new TcpClient(upStreamEndPoint);
await client.ConnectAsync(remoteHostName, remotePort); await client.ConnectAsync(remoteHostName, remotePort);
stream = client.GetStream(); stream = new CustomBufferedStream(client.GetStream(), bufferSize);
} }
try try
...@@ -106,7 +105,7 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -106,7 +105,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
await sslStream.AuthenticateAsClientAsync(remoteHostName, null, supportedSslProtocols, false); await sslStream.AuthenticateAsClientAsync(remoteHostName, null, supportedSslProtocols, false);
stream = sslStream; stream = new CustomBufferedStream(sslStream, bufferSize);
} }
catch catch
{ {
...@@ -121,13 +120,13 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -121,13 +120,13 @@ namespace Titanium.Web.Proxy.Network.Tcp
{ {
client = new TcpClient(upStreamEndPoint); client = new TcpClient(upStreamEndPoint);
await client.ConnectAsync(externalHttpProxy.HostName, externalHttpProxy.Port); await client.ConnectAsync(externalHttpProxy.HostName, externalHttpProxy.Port);
stream = client.GetStream(); stream = new CustomBufferedStream(client.GetStream(), bufferSize);
} }
else else
{ {
client = new TcpClient(upStreamEndPoint); client = new TcpClient(upStreamEndPoint);
await client.ConnectAsync(remoteHostName, remotePort); await client.ConnectAsync(remoteHostName, remotePort);
stream = client.GetStream(); stream = new CustomBufferedStream(client.GetStream(), bufferSize);
} }
} }
......
...@@ -6,7 +6,6 @@ using System.Net; ...@@ -6,7 +6,6 @@ using System.Net;
using System.Net.Security; using System.Net.Security;
using System.Net.Sockets; using System.Net.Sockets;
using System.Security.Authentication; using System.Security.Authentication;
using System.Text.RegularExpressions;
using Titanium.Web.Proxy.Exceptions; using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
...@@ -30,7 +29,7 @@ namespace Titanium.Web.Proxy ...@@ -30,7 +29,7 @@ namespace Titanium.Web.Proxy
private async Task HandleClient(ExplicitProxyEndPoint endPoint, TcpClient tcpClient) private async Task HandleClient(ExplicitProxyEndPoint endPoint, TcpClient tcpClient)
{ {
Stream clientStream = tcpClient.GetStream(); CustomBufferedStream clientStream = new CustomBufferedStream(tcpClient.GetStream(), BufferSize);
clientStream.ReadTimeout = ConnectionTimeOutSeconds * 1000; clientStream.ReadTimeout = ConnectionTimeOutSeconds * 1000;
clientStream.WriteTimeout = ConnectionTimeOutSeconds * 1000; clientStream.WriteTimeout = ConnectionTimeOutSeconds * 1000;
...@@ -112,7 +111,6 @@ namespace Titanium.Web.Proxy ...@@ -112,7 +111,6 @@ namespace Titanium.Web.Proxy
try try
{ {
sslStream = new SslStream(clientStream, true); sslStream = new SslStream(clientStream, true);
var certificate = endPoint.GenericCertificate ?? certificateManager.CreateCertificate(httpRemoteUri.Host, false); var certificate = endPoint.GenericCertificate ?? certificateManager.CreateCertificate(httpRemoteUri.Host, false);
...@@ -121,11 +119,10 @@ namespace Titanium.Web.Proxy ...@@ -121,11 +119,10 @@ namespace Titanium.Web.Proxy
await sslStream.AuthenticateAsServerAsync(certificate, false, await sslStream.AuthenticateAsServerAsync(certificate, false,
SupportedSslProtocols, false); SupportedSslProtocols, false);
//HTTPS server created - we can now decrypt the client's traffic //HTTPS server created - we can now decrypt the client's traffic
clientStream = sslStream; clientStream = new CustomBufferedStream(sslStream, BufferSize);
clientStreamReader = new CustomBinaryReader(sslStream, BufferSize);
clientStreamWriter = new StreamWriter(sslStream) {NewLine = ProxyConstants.NewLine };
clientStreamReader = new CustomBinaryReader(clientStream, BufferSize);
clientStreamWriter = new StreamWriter(clientStream) {NewLine = ProxyConstants.NewLine };
} }
catch catch
{ {
...@@ -171,8 +168,7 @@ namespace Titanium.Web.Proxy ...@@ -171,8 +168,7 @@ namespace Titanium.Web.Proxy
//So for HTTPS requests we would start SSL negotiation right away without expecting a CONNECT request from client //So for HTTPS requests we would start SSL negotiation right away without expecting a CONNECT request from client
private async Task HandleClient(TransparentProxyEndPoint endPoint, TcpClient tcpClient) private async Task HandleClient(TransparentProxyEndPoint endPoint, TcpClient tcpClient)
{ {
CustomBufferedStream clientStream = new CustomBufferedStream(tcpClient.GetStream(), BufferSize);
Stream clientStream = tcpClient.GetStream();
clientStream.ReadTimeout = ConnectionTimeOutSeconds * 1000; clientStream.ReadTimeout = ConnectionTimeOutSeconds * 1000;
clientStream.WriteTimeout = ConnectionTimeOutSeconds * 1000; clientStream.WriteTimeout = ConnectionTimeOutSeconds * 1000;
...@@ -193,8 +189,9 @@ namespace Titanium.Web.Proxy ...@@ -193,8 +189,9 @@ namespace Titanium.Web.Proxy
await sslStream.AuthenticateAsServerAsync(certificate, false, await sslStream.AuthenticateAsServerAsync(certificate, false,
SslProtocols.Tls, false); SslProtocols.Tls, false);
clientStreamReader = new CustomBinaryReader(sslStream, BufferSize); clientStream = new CustomBufferedStream(sslStream, BufferSize);
clientStreamWriter = new StreamWriter(sslStream) { NewLine = ProxyConstants.NewLine }; clientStreamReader = new CustomBinaryReader(clientStream, BufferSize);
clientStreamWriter = new StreamWriter(clientStream) { NewLine = ProxyConstants.NewLine };
//HTTPS server created - we can now decrypt the client's traffic //HTTPS server created - we can now decrypt the client's traffic
} }
...@@ -205,7 +202,6 @@ namespace Titanium.Web.Proxy ...@@ -205,7 +202,6 @@ namespace Titanium.Web.Proxy
Dispose(sslStream, clientStreamReader, clientStreamWriter, null); Dispose(sslStream, clientStreamReader, clientStreamWriter, null);
return; return;
} }
clientStream = sslStream;
} }
else else
{ {
......
...@@ -175,7 +175,7 @@ namespace Titanium.Web.Proxy ...@@ -175,7 +175,7 @@ namespace Titanium.Web.Proxy
foreach (var header in response.ResponseHeaders) foreach (var header in response.ResponseHeaders)
{ {
await responseWriter.WriteLineAsync(header.Value.ToString()); await header.Value.WriteToStream(responseWriter);
} }
//write non unique request headers //write non unique request headers
...@@ -184,11 +184,10 @@ namespace Titanium.Web.Proxy ...@@ -184,11 +184,10 @@ namespace Titanium.Web.Proxy
var headers = headerItem.Value; var headers = headerItem.Value;
foreach (var header in headers) foreach (var header in headers)
{ {
await responseWriter.WriteLineAsync(header.ToString()); await header.WriteToStream(responseWriter);
} }
} }
await responseWriter.WriteLineAsync(); await responseWriter.WriteLineAsync();
await responseWriter.FlushAsync(); await responseWriter.FlushAsync();
} }
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<Prefer32Bit>false</Prefer32Bit> <Prefer32Bit>false</Prefer32Bit>
<DocumentationFile>bin\Debug\Titanium.Web.Proxy.XML</DocumentationFile> <DocumentationFile>bin\Debug\Titanium.Web.Proxy.XML</DocumentationFile>
<LangVersion>6</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
...@@ -73,6 +74,7 @@ ...@@ -73,6 +74,7 @@
<Compile Include="EventArguments\CertificateValidationEventArgs.cs" /> <Compile Include="EventArguments\CertificateValidationEventArgs.cs" />
<Compile Include="Extensions\ByteArrayExtensions.cs" /> <Compile Include="Extensions\ByteArrayExtensions.cs" />
<Compile Include="Extensions\StringExtensions.cs" /> <Compile Include="Extensions\StringExtensions.cs" />
<Compile Include="Helpers\CustomBufferedStream.cs" />
<Compile Include="Helpers\Network.cs" /> <Compile Include="Helpers\Network.cs" />
<Compile Include="Helpers\RunTime.cs" /> <Compile Include="Helpers\RunTime.cs" />
<Compile Include="Http\Responses\GenericResponse.cs" /> <Compile Include="Http\Responses\GenericResponse.cs" />
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment