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

Commit

Permalink
Issue #27
Browse files Browse the repository at this point in the history
Improve performance by avoiding new Tasks for each requests
  • Loading branch information
justcoding121 committed Dec 26, 2015
1 parent ca13d73 commit 65be519
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 127 deletions.
18 changes: 9 additions & 9 deletions Titanium.Web.Proxy/Helpers/CustomBinaryReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,35 +14,35 @@ internal CustomBinaryReader(Stream stream, Encoding encoding)

internal string ReadLine()
{
var buf = new char[1];
var readBuffer = new StringBuilder();

try
{
var lastChar = new char();
var lastChar = default(char);

while ((Read(buf, 0, 1)) > 0)
while (true)
{
if (lastChar == '\r' && buf[0] == '\n')
var buf = ReadChar();
if (lastChar == '\r' && buf == '\n')
{
return readBuffer.Remove(readBuffer.Length - 1, 1).ToString();
}
if (buf[0] == '\0')
if (buf == '\0')
{
return readBuffer.ToString();
}
readBuffer.Append(buf[0]);
readBuffer.Append(buf);

lastChar = buf[0];
lastChar = buf;
}
return readBuffer.ToString();

}
catch (IOException)
{
return readBuffer.ToString();
}
}


internal List<string> ReadAllLines()
{
string tmpLine;
Expand Down
2 changes: 1 addition & 1 deletion Titanium.Web.Proxy/ProxyServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public static void Initialize()
{
ServicePointManager.Expect100Continue = false;
WebRequest.DefaultWebProxy = null;
ServicePointManager.DefaultConnectionLimit = 10;
ServicePointManager.DefaultConnectionLimit = int.MaxValue;
ServicePointManager.DnsRefreshTimeout = 3 * 60 * 1000; //3 minutes
ServicePointManager.MaxServicePointIdleTime = 3 * 60 * 1000;

Expand Down
234 changes: 120 additions & 114 deletions Titanium.Web.Proxy/RequestHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,8 @@ private static void HandleClient(TcpClient client)
}

//Now create the request
Task.Factory.StartNew(
() =>
HandleHttpSessionRequest(client, httpCmd, clientStream, clientStreamReader, clientStreamWriter,
httpRemoteUri.Scheme == Uri.UriSchemeHttps ? httpRemoteUri.OriginalString : null));
HandleHttpSessionRequest(client, httpCmd, clientStream, clientStreamReader, clientStreamWriter,
httpRemoteUri.Scheme == Uri.UriSchemeHttps ? httpRemoteUri.OriginalString : null);
}
catch
{
Expand All @@ -112,145 +110,152 @@ private static void HandleClient(TcpClient client)
private static void HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream,
CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, string secureTunnelHostName)
{
if (string.IsNullOrEmpty(httpCmd))
while (true)
{
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, null);
return;
}

var args = new SessionEventArgs(BUFFER_SIZE);
args.Client = client;

if (string.IsNullOrEmpty(httpCmd))
{
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, null);
return;
}

try
{
//break up the line into three components (method, remote URL & Http Version)
var httpCmdSplit = httpCmd.Split(SpaceSplit, 3);
var args = new SessionEventArgs(BUFFER_SIZE);
args.Client = client;

var httpMethod = httpCmdSplit[0];
var httpRemoteUri =
new Uri(secureTunnelHostName == null ? httpCmdSplit[1] : (secureTunnelHostName + httpCmdSplit[1]));
var httpVersion = httpCmdSplit[2];

Version version;
if (httpVersion == "HTTP/1.1")
{
version = new Version(1, 1);
}
else
try
{
version = new Version(1, 0);
}
//break up the line into three components (method, remote URL & Http Version)
var httpCmdSplit = httpCmd.Split(SpaceSplit, 3);

if (httpRemoteUri.Scheme == Uri.UriSchemeHttps)
{
args.IsHttps = true;
}
var httpMethod = httpCmdSplit[0];
var httpRemoteUri =
new Uri(secureTunnelHostName == null ? httpCmdSplit[1] : (secureTunnelHostName + httpCmdSplit[1]));
var httpVersion = httpCmdSplit[2];

args.RequestHeaders = new List<HttpHeader>();
Version version;
if (httpVersion == "HTTP/1.1")
{
version = new Version(1, 1);
}
else
{
version = new Version(1, 0);
}

string tmpLine;
if (httpRemoteUri.Scheme == Uri.UriSchemeHttps)
{
args.IsHttps = true;
}

while (!string.IsNullOrEmpty(tmpLine = clientStreamReader.ReadLine()))
{
var header = tmpLine.Split(ColonSpaceSplit, 2, StringSplitOptions.None);
args.RequestHeaders.Add(new HttpHeader(header[0], header[1]));
}
args.RequestHeaders = new List<HttpHeader>();

for (var i = 0; i < args.RequestHeaders.Count; i++)
{
var rawHeader = args.RequestHeaders[i];
string tmpLine;

while (!string.IsNullOrEmpty(tmpLine = clientStreamReader.ReadLine()))
{
var header = tmpLine.Split(ColonSpaceSplit, 2, StringSplitOptions.None);
args.RequestHeaders.Add(new HttpHeader(header[0], header[1]));
}

//if request was upgrade to web-socket protocol then relay the request without proxying
if ((rawHeader.Name.ToLower() == "upgrade") && (rawHeader.Value.ToLower() == "websocket"))
for (var i = 0; i < args.RequestHeaders.Count; i++)
{
TcpHelper.SendRaw(clientStreamReader.BaseStream, httpCmd, args.RequestHeaders,
httpRemoteUri.Host, httpRemoteUri.Port, httpRemoteUri.Scheme == Uri.UriSchemeHttps);
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
return;
var rawHeader = args.RequestHeaders[i];


//if request was upgrade to web-socket protocol then relay the request without proxying
if ((rawHeader.Name.ToLower() == "upgrade") && (rawHeader.Value.ToLower() == "websocket"))
{
TcpHelper.SendRaw(clientStreamReader.BaseStream, httpCmd, args.RequestHeaders,
httpRemoteUri.Host, httpRemoteUri.Port, httpRemoteUri.Scheme == Uri.UriSchemeHttps);
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
return;
}
}
}

//construct the web request that we are going to issue on behalf of the client.
args.ProxyRequest = (HttpWebRequest)WebRequest.Create(httpRemoteUri);
args.ProxyRequest.Proxy = null;
args.ProxyRequest.UseDefaultCredentials = true;
args.ProxyRequest.Method = httpMethod;
args.ProxyRequest.ProtocolVersion = version;
args.ClientStream = clientStream;
args.ClientStreamReader = clientStreamReader;
args.ClientStreamWriter = clientStreamWriter;
args.ProxyRequest.AllowAutoRedirect = false;
args.ProxyRequest.AutomaticDecompression = DecompressionMethods.None;
args.RequestHostname = args.ProxyRequest.RequestUri.Host;
args.RequestUrl = args.ProxyRequest.RequestUri.OriginalString;
args.ClientPort = ((IPEndPoint)client.Client.RemoteEndPoint).Port;
args.ClientIpAddress = ((IPEndPoint)client.Client.RemoteEndPoint).Address;
args.RequestHttpVersion = version;
args.RequestIsAlive = args.ProxyRequest.KeepAlive;
args.ProxyRequest.AllowWriteStreamBuffering = true;


//If requested interception
if (BeforeRequest != null)
{
args.RequestEncoding = args.ProxyRequest.GetEncoding();
BeforeRequest(null, args);
}

//construct the web request that we are going to issue on behalf of the client.
args.ProxyRequest = (HttpWebRequest) WebRequest.Create(httpRemoteUri);
args.ProxyRequest.Proxy = null;
args.ProxyRequest.UseDefaultCredentials = true;
args.ProxyRequest.Method = httpMethod;
args.ProxyRequest.ProtocolVersion = version;
args.ClientStream = clientStream;
args.ClientStreamReader = clientStreamReader;
args.ClientStreamWriter = clientStreamWriter;
args.ProxyRequest.AllowAutoRedirect = false;
args.ProxyRequest.AutomaticDecompression = DecompressionMethods.None;
args.RequestHostname = args.ProxyRequest.RequestUri.Host;
args.RequestUrl = args.ProxyRequest.RequestUri.OriginalString;
args.ClientPort = ((IPEndPoint) client.Client.RemoteEndPoint).Port;
args.ClientIpAddress = ((IPEndPoint) client.Client.RemoteEndPoint).Address;
args.RequestHttpVersion = version;
args.RequestIsAlive = args.ProxyRequest.KeepAlive;
args.ProxyRequest.ConnectionGroupName = args.RequestHostname;
args.ProxyRequest.AllowWriteStreamBuffering = true;
args.RequestLocked = true;

if (args.CancelRequest)
{
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
return;
}

//If requested interception
if (BeforeRequest != null)
{
args.RequestEncoding = args.ProxyRequest.GetEncoding();
BeforeRequest(null, args);
}
SetRequestHeaders(args.RequestHeaders, args.ProxyRequest);

args.RequestLocked = true;
//If request was modified by user
if (args.RequestBodyRead)
{
args.ProxyRequest.ContentLength = args.RequestBody.Length;
var newStream = args.ProxyRequest.GetRequestStream();
newStream.Write(args.RequestBody, 0, args.RequestBody.Length);
}
else
{
//If its a post/put request, then read the client html body and send it to server
if (httpMethod.ToUpper() == "POST" || httpMethod.ToUpper() == "PUT")
{
SendClientRequestBody(args);
}
}

if (args.CancelRequest)
{
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
return;
}
HandleHttpSessionResponse(args);

SetRequestHeaders(args.RequestHeaders, args.ProxyRequest);
if (args.ResponseHeaders.Any(x => x.Name.ToLower() == "proxy-connection" && x.Value.ToLower() == "close"))
{
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
return;
}
//Now read the next request (if keep-Alive is enabled, otherwise exit this thread)
//If client is pipeling the request, this will be immediately hit before response for previous request was made
httpCmd = clientStreamReader.ReadLine();
//Http request body sent, now wait for next request

//If request was modified by user
if (args.RequestBodyRead)
{
args.ProxyRequest.ContentLength = args.RequestBody.Length;
var newStream = args.ProxyRequest.GetRequestStream();
newStream.Write(args.RequestBody, 0, args.RequestBody.Length);
client = args.Client;
clientStream = args.ClientStream;
clientStreamReader = args.ClientStreamReader;
args.ClientStreamWriter = clientStreamWriter;

args.ProxyRequest.BeginGetResponse(HandleHttpSessionResponse, args);
}
else
catch
{
//If its a post/put request, then read the client html body and send it to server
if (httpMethod.ToUpper() == "POST" || httpMethod.ToUpper() == "PUT")
{
SendClientRequestBody(args);
}
//Http request body sent, now wait asynchronously for response
args.ProxyRequest.BeginGetResponse(HandleHttpSessionResponse, args);
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
throw;
}

//Now read the next request (if keep-Alive is enabled, otherwise exit this thread)
//If client is pipeling the request, this will be immediately hit before response for previous request was made
httpCmd = clientStreamReader.ReadLine();
//Http request body sent, now wait for next request
Task.Factory.StartNew(
() =>
HandleHttpSessionRequest(args.Client, httpCmd, args.ClientStream, args.ClientStreamReader,
args.ClientStreamWriter, secureTunnelHostName));
}
catch
{
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
}
}

private static void WriteConnectResponse(StreamWriter clientStreamWriter, string httpVersion)
{
clientStreamWriter.WriteLine(httpVersion + " 200 Connection established");
clientStreamWriter.WriteLine("Timestamp: {0}", DateTime.Now);
clientStreamWriter.WriteLine("connection:close");
//clientStreamWriter.WriteLine("connection:close");
clientStreamWriter.WriteLine();
clientStreamWriter.Flush();
}
Expand Down Expand Up @@ -302,6 +307,8 @@ private static void SetRequestHeaders(List<HttpHeader> requestHeaders, HttpWebRe
case "proxy-connection":
if (requestHeaders[i].Value.ToLower() == "keep-alive")
webRequest.KeepAlive = true;
else if (requestHeaders[i].Value.ToLower() == "close")
webRequest.KeepAlive = false;
break;
case "range":
var startEnd = requestHeaders[i].Value.Replace(Environment.NewLine, "").Remove(0, 6).Split('-');
Expand Down Expand Up @@ -359,18 +366,18 @@ private static void SendClientRequestBody(SessionEventArgs args)
int bytesToRead;
if (args.ProxyRequest.ContentLength < BUFFER_SIZE)
{
bytesToRead = (int) args.ProxyRequest.ContentLength;
bytesToRead = (int)args.ProxyRequest.ContentLength;
}
else
bytesToRead = BUFFER_SIZE;


while (totalbytesRead < (int) args.ProxyRequest.ContentLength)
while (totalbytesRead < (int)args.ProxyRequest.ContentLength)
{
var buffer = args.ClientStreamReader.ReadBytes(bytesToRead);
totalbytesRead += buffer.Length;

var remainingBytes = (int) args.ProxyRequest.ContentLength - totalbytesRead;
var remainingBytes = (int)args.ProxyRequest.ContentLength - totalbytesRead;
if (remainingBytes < bytesToRead)
{
bytesToRead = remainingBytes;
Expand Down Expand Up @@ -414,7 +421,6 @@ private static void SendClientRequestBody(SessionEventArgs args)
}
}


postStream.Close();
}
catch
Expand Down
Loading

0 comments on commit 65be519

Please sign in to comment.