Commit cf52b5ab authored by justcoding121's avatar justcoding121 Committed by justcoding121

Issue #27

Improve performance by avoiding new Tasks for each requests
parent 97718aaa
......@@ -14,27 +14,28 @@ namespace Titanium.Web.Proxy.Helpers
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)
{
......@@ -42,7 +43,6 @@ namespace Titanium.Web.Proxy.Helpers
}
}
internal List<string> ReadAllLines()
{
string tmpLine;
......
......@@ -61,7 +61,7 @@ namespace Titanium.Web.Proxy
{
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;
......
......@@ -97,10 +97,8 @@ namespace Titanium.Web.Proxy
}
//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
{
......@@ -112,137 +110,144 @@ namespace Titanium.Web.Proxy
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);
}
}
......@@ -250,7 +255,7 @@ namespace Titanium.Web.Proxy
{
clientStreamWriter.WriteLine(httpVersion + " 200 Connection established");
clientStreamWriter.WriteLine("Timestamp: {0}", DateTime.Now);
clientStreamWriter.WriteLine("connection:close");
//clientStreamWriter.WriteLine("connection:close");
clientStreamWriter.WriteLine();
clientStreamWriter.Flush();
}
......@@ -302,6 +307,8 @@ namespace Titanium.Web.Proxy
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('-');
......@@ -359,18 +366,18 @@ namespace Titanium.Web.Proxy
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;
......@@ -414,7 +421,6 @@ namespace Titanium.Web.Proxy
}
}
postStream.Close();
}
catch
......
......@@ -15,13 +15,12 @@ namespace Titanium.Web.Proxy
partial class ProxyServer
{
//Called asynchronously when a request was successfully and we received the response
private static void HandleHttpSessionResponse(IAsyncResult asynchronousResult)
private static void HandleHttpSessionResponse(SessionEventArgs args)
{
var args = (SessionEventArgs)asynchronousResult.AsyncState;
try
{
args.ServerResponse = (HttpWebResponse)args.ProxyRequest.EndGetResponse(asynchronousResult);
args.ServerResponse = (HttpWebResponse)args.ProxyRequest.GetResponse();
}
catch (WebException webEx)
{
......@@ -132,6 +131,8 @@ namespace Titanium.Web.Proxy
{
if (headers != null)
{
FixProxyHeaders(headers);
foreach (var header in headers)
{
responseWriter.WriteLine(header.ToString());
......@@ -141,10 +142,24 @@ namespace Titanium.Web.Proxy
responseWriter.WriteLine();
responseWriter.Flush();
}
private static void FixProxyHeaders(List<HttpHeader> headers)
{
//If proxy-connection close was returned inform to close the connection
if (headers.Any(x => x.Name.ToLower() == "proxy-connection" && x.Value.ToLower() == "close"))
if (headers.Any(x => x.Name.ToLower() == "connection") == false)
{
headers.Add(new HttpHeader("connection", "close"));
headers.RemoveAll(x => x.Name.ToLower() == "proxy-connection");
}
else
headers.Find(x => x.Name.ToLower() == "connection").Value = "close";
}
private static void WriteResponseHeaders(StreamWriter responseWriter, List<HttpHeader> headers, int length,
bool isChunked)
{
FixProxyHeaders(headers);
if (!isChunked)
{
if (headers.Any(x => x.Name.ToLower() == "content-length") == false)
......
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