Commit 6453375e authored by justcoding121's avatar justcoding121 Committed by GitHub

Merge pull request #95 from justcoding121/perf-improve

Perf improve
parents 2d85486e 7f002099
...@@ -141,7 +141,9 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -141,7 +141,9 @@ namespace Titanium.Web.Proxy.Examples.Basic
{ {
//set IsValid to true/false based on Certificate Errors //set IsValid to true/false based on Certificate Errors
if (e.SslPolicyErrors == System.Net.Security.SslPolicyErrors.None) if (e.SslPolicyErrors == System.Net.Security.SslPolicyErrors.None)
{
e.IsValid = true; e.IsValid = true;
}
return Task.FromResult(0); return Task.FromResult(0);
} }
......
...@@ -47,7 +47,9 @@ namespace Titanium.Web.Proxy ...@@ -47,7 +47,9 @@ namespace Titanium.Web.Proxy
} }
if (sslPolicyErrors == SslPolicyErrors.None) if (sslPolicyErrors == SslPolicyErrors.None)
{
return true; return true;
}
//By default //By default
//do not allow this client to communicate with unauthenticated servers. //do not allow this client to communicate with unauthenticated servers.
...@@ -82,13 +84,17 @@ namespace Titanium.Web.Proxy ...@@ -82,13 +84,17 @@ namespace Titanium.Web.Proxy
{ {
string issuer = certificate.Issuer; string issuer = certificate.Issuer;
if (Array.IndexOf(acceptableIssuers, issuer) != -1) if (Array.IndexOf(acceptableIssuers, issuer) != -1)
{
clientCertificate = certificate; clientCertificate = certificate;
}
} }
} }
if (localCertificates != null && if (localCertificates != null &&
localCertificates.Count > 0) localCertificates.Count > 0)
{
clientCertificate = localCertificates[0]; clientCertificate = localCertificates[0];
}
//If user call back is registered //If user call back is registered
if (ClientCertificateSelectionCallback != null) if (ClientCertificateSelectionCallback != null)
...@@ -115,7 +121,6 @@ namespace Titanium.Web.Proxy ...@@ -115,7 +121,6 @@ namespace Titanium.Web.Proxy
} }
return clientCertificate; return clientCertificate;
} }
} }
} }
...@@ -72,7 +72,8 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -72,7 +72,8 @@ namespace Titanium.Web.Proxy.EventArguments
if ((WebSession.Request.Method.ToUpper() != "POST" && WebSession.Request.Method.ToUpper() != "PUT")) if ((WebSession.Request.Method.ToUpper() != "POST" && WebSession.Request.Method.ToUpper() != "PUT"))
{ {
throw new BodyNotFoundException("Request don't have a body." + throw new BodyNotFoundException("Request don't have a body." +
"Please verify that this request is a Http POST/PUT and request content length is greater than zero before accessing the body."); "Please verify that this request is a Http POST/PUT and request " +
"content length is greater than zero before accessing the body.");
} }
//Caching check //Caching check
...@@ -80,7 +81,6 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -80,7 +81,6 @@ namespace Titanium.Web.Proxy.EventArguments
{ {
//If chunked then its easy just read the whole body with the content length mentioned in the request header //If chunked then its easy just read the whole body with the content length mentioned in the request header
using (var requestBodyStream = new MemoryStream()) using (var requestBodyStream = new MemoryStream())
{ {
//For chunked request we need to read data as they arrive, until we reach a chunk end symbol //For chunked request we need to read data as they arrive, until we reach a chunk end symbol
...@@ -94,13 +94,17 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -94,13 +94,17 @@ namespace Titanium.Web.Proxy.EventArguments
if (WebSession.Request.ContentLength > 0) if (WebSession.Request.ContentLength > 0)
{ {
//If not chunked then its easy just read the amount of bytes mentioned in content length header of response //If not chunked then its easy just read the amount of bytes mentioned in content length header of response
await this.ProxyClient.ClientStreamReader.CopyBytesToStream(bufferSize, requestBodyStream, WebSession.Request.ContentLength); await this.ProxyClient.ClientStreamReader.CopyBytesToStream(bufferSize, requestBodyStream,
WebSession.Request.ContentLength);
} }
else if (WebSession.Request.HttpVersion.Major == 1 && WebSession.Request.HttpVersion.Minor == 0) else if (WebSession.Request.HttpVersion.Major == 1 && WebSession.Request.HttpVersion.Minor == 0)
{
await WebSession.ServerConnection.StreamReader.CopyBytesToStream(bufferSize, requestBodyStream, long.MaxValue); await WebSession.ServerConnection.StreamReader.CopyBytesToStream(bufferSize, requestBodyStream, long.MaxValue);
}
} }
WebSession.Request.RequestBody = await GetDecompressedResponseBody(WebSession.Request.ContentEncoding, requestBodyStream.ToArray()); WebSession.Request.RequestBody = await GetDecompressedResponseBody(WebSession.Request.ContentEncoding,
requestBodyStream.ToArray());
} }
//Now set the flag to true //Now set the flag to true
...@@ -130,14 +134,18 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -130,14 +134,18 @@ namespace Titanium.Web.Proxy.EventArguments
if (WebSession.Response.ContentLength > 0) if (WebSession.Response.ContentLength > 0)
{ {
//If not chunked then its easy just read the amount of bytes mentioned in content length header of response //If not chunked then its easy just read the amount of bytes mentioned in content length header of response
await WebSession.ServerConnection.StreamReader.CopyBytesToStream(bufferSize, responseBodyStream, WebSession.Response.ContentLength); await WebSession.ServerConnection.StreamReader.CopyBytesToStream(bufferSize, responseBodyStream,
WebSession.Response.ContentLength);
} }
else if (WebSession.Response.HttpVersion.Major == 1 && WebSession.Response.HttpVersion.Minor == 0) else if (WebSession.Response.HttpVersion.Major == 1 && WebSession.Response.HttpVersion.Minor == 0)
{
await WebSession.ServerConnection.StreamReader.CopyBytesToStream(bufferSize, responseBodyStream, long.MaxValue); await WebSession.ServerConnection.StreamReader.CopyBytesToStream(bufferSize, responseBodyStream, long.MaxValue);
}
} }
WebSession.Response.ResponseBody = await GetDecompressedResponseBody(WebSession.Response.ContentEncoding, responseBodyStream.ToArray()); WebSession.Response.ResponseBody = await GetDecompressedResponseBody(WebSession.Response.ContentEncoding,
responseBodyStream.ToArray());
} }
//set this to true for caching //set this to true for caching
...@@ -154,7 +162,9 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -154,7 +162,9 @@ namespace Titanium.Web.Proxy.EventArguments
if (!WebSession.Request.RequestBodyRead) if (!WebSession.Request.RequestBodyRead)
{ {
if (WebSession.Request.RequestLocked) if (WebSession.Request.RequestLocked)
{
throw new Exception("You cannot call this function after request is made to server."); throw new Exception("You cannot call this function after request is made to server.");
}
await ReadRequestBody(); await ReadRequestBody();
} }
...@@ -169,7 +179,9 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -169,7 +179,9 @@ namespace Titanium.Web.Proxy.EventArguments
if (!WebSession.Request.RequestBodyRead) if (!WebSession.Request.RequestBodyRead)
{ {
if (WebSession.Request.RequestLocked) if (WebSession.Request.RequestLocked)
{
throw new Exception("You cannot call this function after request is made to server."); throw new Exception("You cannot call this function after request is made to server.");
}
await ReadRequestBody(); await ReadRequestBody();
} }
...@@ -184,7 +196,9 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -184,7 +196,9 @@ namespace Titanium.Web.Proxy.EventArguments
public async Task SetRequestBody(byte[] body) public async Task SetRequestBody(byte[] body)
{ {
if (WebSession.Request.RequestLocked) if (WebSession.Request.RequestLocked)
{
throw new Exception("You cannot call this function after request is made to server."); throw new Exception("You cannot call this function after request is made to server.");
}
//syphon out the request body from client before setting the new body //syphon out the request body from client before setting the new body
if (!WebSession.Request.RequestBodyRead) if (!WebSession.Request.RequestBodyRead)
...@@ -195,9 +209,13 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -195,9 +209,13 @@ namespace Titanium.Web.Proxy.EventArguments
WebSession.Request.RequestBody = body; WebSession.Request.RequestBody = body;
if (WebSession.Request.IsChunked == false) if (WebSession.Request.IsChunked == false)
{
WebSession.Request.ContentLength = body.Length; WebSession.Request.ContentLength = body.Length;
}
else else
{
WebSession.Request.ContentLength = -1; WebSession.Request.ContentLength = -1;
}
} }
/// <summary> /// <summary>
...@@ -207,7 +225,9 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -207,7 +225,9 @@ namespace Titanium.Web.Proxy.EventArguments
public async Task SetRequestBodyString(string body) public async Task SetRequestBodyString(string body)
{ {
if (WebSession.Request.RequestLocked) if (WebSession.Request.RequestLocked)
{
throw new Exception("You cannot call this function after request is made to server."); throw new Exception("You cannot call this function after request is made to server.");
}
//syphon out the request body from client before setting the new body //syphon out the request body from client before setting the new body
if (!WebSession.Request.RequestBodyRead) if (!WebSession.Request.RequestBodyRead)
...@@ -226,7 +246,9 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -226,7 +246,9 @@ namespace Titanium.Web.Proxy.EventArguments
public async Task<byte[]> GetResponseBody() public async Task<byte[]> GetResponseBody()
{ {
if (!WebSession.Request.RequestLocked) if (!WebSession.Request.RequestLocked)
{
throw new Exception("You cannot call this function before request is made to server."); throw new Exception("You cannot call this function before request is made to server.");
}
await ReadResponseBody(); await ReadResponseBody();
return WebSession.Response.ResponseBody; return WebSession.Response.ResponseBody;
...@@ -239,11 +261,14 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -239,11 +261,14 @@ namespace Titanium.Web.Proxy.EventArguments
public async Task<string> GetResponseBodyAsString() public async Task<string> GetResponseBodyAsString()
{ {
if (!WebSession.Request.RequestLocked) if (!WebSession.Request.RequestLocked)
{
throw new Exception("You cannot call this function before request is made to server."); throw new Exception("You cannot call this function before request is made to server.");
}
await GetResponseBody(); await GetResponseBody();
return WebSession.Response.ResponseBodyString ?? (WebSession.Response.ResponseBodyString = WebSession.Response.Encoding.GetString(WebSession.Response.ResponseBody)); return WebSession.Response.ResponseBodyString ??
(WebSession.Response.ResponseBodyString = WebSession.Response.Encoding.GetString(WebSession.Response.ResponseBody));
} }
/// <summary> /// <summary>
...@@ -253,7 +278,9 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -253,7 +278,9 @@ namespace Titanium.Web.Proxy.EventArguments
public async Task SetResponseBody(byte[] body) public async Task SetResponseBody(byte[] body)
{ {
if (!WebSession.Request.RequestLocked) if (!WebSession.Request.RequestLocked)
{
throw new Exception("You cannot call this function before request is made to server."); throw new Exception("You cannot call this function before request is made to server.");
}
//syphon out the response body from server before setting the new body //syphon out the response body from server before setting the new body
if (WebSession.Response.ResponseBody == null) if (WebSession.Response.ResponseBody == null)
...@@ -265,9 +292,13 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -265,9 +292,13 @@ namespace Titanium.Web.Proxy.EventArguments
//If there is a content length header update it //If there is a content length header update it
if (WebSession.Response.IsChunked == false) if (WebSession.Response.IsChunked == false)
{
WebSession.Response.ContentLength = body.Length; WebSession.Response.ContentLength = body.Length;
}
else else
{
WebSession.Response.ContentLength = -1; WebSession.Response.ContentLength = -1;
}
} }
/// <summary> /// <summary>
...@@ -277,7 +308,9 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -277,7 +308,9 @@ namespace Titanium.Web.Proxy.EventArguments
public async Task SetResponseBodyString(string body) public async Task SetResponseBodyString(string body)
{ {
if (!WebSession.Request.RequestLocked) if (!WebSession.Request.RequestLocked)
{
throw new Exception("You cannot call this function before request is made to server."); throw new Exception("You cannot call this function before request is made to server.");
}
//syphon out the response body from server before setting the new body //syphon out the response body from server before setting the new body
if (WebSession.Response.ResponseBody == null) if (WebSession.Response.ResponseBody == null)
...@@ -308,10 +341,14 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -308,10 +341,14 @@ namespace Titanium.Web.Proxy.EventArguments
public async Task Ok(string html) public async Task Ok(string html)
{ {
if (WebSession.Request.RequestLocked) if (WebSession.Request.RequestLocked)
{
throw new Exception("You cannot call this function after request is made to server."); throw new Exception("You cannot call this function after request is made to server.");
}
if (html == null) if (html == null)
{
html = string.Empty; html = string.Empty;
}
var result = Encoding.Default.GetBytes(html); var result = Encoding.Default.GetBytes(html);
...@@ -341,7 +378,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -341,7 +378,7 @@ namespace Titanium.Web.Proxy.EventArguments
var response = new RedirectResponse(); var response = new RedirectResponse();
response.HttpVersion = WebSession.Request.HttpVersion; response.HttpVersion = WebSession.Request.HttpVersion;
response.ResponseHeaders.Add(new Models.HttpHeader("Location", url)); response.ResponseHeaders.Add("Location", new Models.HttpHeader("Location", url));
response.ResponseBody = Encoding.ASCII.GetBytes(string.Empty); response.ResponseBody = Encoding.ASCII.GetBytes(string.Empty);
await Respond(response); await Respond(response);
......
...@@ -20,7 +20,9 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -20,7 +20,9 @@ namespace Titanium.Web.Proxy.Extensions
{ {
//return default if not specified //return default if not specified
if (request.ContentType == null) if (request.ContentType == null)
{
return Encoding.GetEncoding("ISO-8859-1"); return Encoding.GetEncoding("ISO-8859-1");
}
//extract the encoding by finding the charset //extract the encoding by finding the charset
var contentTypes = request.ContentType.Split(ProxyConstants.SemiColonSplit); var contentTypes = request.ContentType.Split(ProxyConstants.SemiColonSplit);
......
...@@ -17,7 +17,9 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -17,7 +17,9 @@ namespace Titanium.Web.Proxy.Extensions
{ {
//return default if not specified //return default if not specified
if (response.ContentType == null) if (response.ContentType == null)
{
return Encoding.GetEncoding("ISO-8859-1"); return Encoding.GetEncoding("ISO-8859-1");
}
//extract the encoding by finding the charset //extract the encoding by finding the charset
var contentTypes = response.ContentType.Split(ProxyConstants.SemiColonSplit); var contentTypes = response.ContentType.Split(ProxyConstants.SemiColonSplit);
......
...@@ -26,6 +26,7 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -26,6 +26,7 @@ namespace Titanium.Web.Proxy.Extensions
var bytes = Encoding.ASCII.GetBytes(initialData); var bytes = Encoding.ASCII.GetBytes(initialData);
await output.WriteAsync(bytes, 0, bytes.Length); await output.WriteAsync(bytes, 0, bytes.Length);
} }
await input.CopyToAsync(output); await input.CopyToAsync(output);
} }
/// <summary> /// <summary>
...@@ -45,7 +46,9 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -45,7 +46,9 @@ namespace Titanium.Web.Proxy.Extensions
bytesToRead = totalBytesToRead; bytesToRead = totalBytesToRead;
} }
else else
{
bytesToRead = bufferSize; bytesToRead = bufferSize;
}
while (totalbytesRead < totalBytesToRead) while (totalbytesRead < totalBytesToRead)
...@@ -53,7 +56,9 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -53,7 +56,9 @@ namespace Titanium.Web.Proxy.Extensions
var buffer = await streamReader.ReadBytesAsync(bufferSize, bytesToRead); var buffer = await streamReader.ReadBytesAsync(bufferSize, bytesToRead);
if (buffer.Length == 0) if (buffer.Length == 0)
{
break; break;
}
totalbytesRead += buffer.Length; totalbytesRead += buffer.Length;
...@@ -62,6 +67,7 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -62,6 +67,7 @@ namespace Titanium.Web.Proxy.Extensions
{ {
bytesToRead = remainingBytes; bytesToRead = remainingBytes;
} }
await stream.WriteAsync(buffer, 0, buffer.Length); await stream.WriteAsync(buffer, 0, buffer.Length);
} }
} }
...@@ -107,7 +113,9 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -107,7 +113,9 @@ namespace Titanium.Web.Proxy.Extensions
await clientStream.WriteAsync(data, 0, data.Length); await clientStream.WriteAsync(data, 0, data.Length);
} }
else else
{
await WriteResponseBodyChunked(data, clientStream); await WriteResponseBodyChunked(data, clientStream);
}
} }
/// <summary> /// <summary>
/// Copies the specified content length number of bytes to the output stream from the given inputs stream /// Copies the specified content length number of bytes to the output stream from the given inputs stream
...@@ -124,12 +132,16 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -124,12 +132,16 @@ namespace Titanium.Web.Proxy.Extensions
{ {
//http 1.0 //http 1.0
if (ContentLength == -1) if (ContentLength == -1)
{
ContentLength = long.MaxValue; ContentLength = long.MaxValue;
}
int bytesToRead = bufferSize; int bytesToRead = bufferSize;
if (ContentLength < bufferSize) if (ContentLength < bufferSize)
{
bytesToRead = (int)ContentLength; bytesToRead = (int)ContentLength;
}
var buffer = new byte[bufferSize]; var buffer = new byte[bufferSize];
...@@ -150,7 +162,9 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -150,7 +162,9 @@ namespace Titanium.Web.Proxy.Extensions
} }
} }
else else
{
await WriteResponseBodyChunked(inStreamReader, bufferSize, outStream); await WriteResponseBodyChunked(inStreamReader, bufferSize, outStream);
}
} }
/// <summary> /// <summary>
......
...@@ -13,6 +13,7 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -13,6 +13,7 @@ namespace Titanium.Web.Proxy.Extensions
{ {
// This is how you can determine whether a socket is still connected. // This is how you can determine whether a socket is still connected.
bool blockingState = client.Blocking; bool blockingState = client.Blocking;
try try
{ {
byte[] tmp = new byte[1]; byte[] tmp = new byte[1];
...@@ -25,7 +26,9 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -25,7 +26,9 @@ namespace Titanium.Web.Proxy.Extensions
{ {
// 10035 == WSAEWOULDBLOCK // 10035 == WSAEWOULDBLOCK
if (e.NativeErrorCode.Equals(10035)) if (e.NativeErrorCode.Equals(10035))
{
return true; return true;
}
else else
{ {
return false; return false;
......
...@@ -27,9 +27,13 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -27,9 +27,13 @@ namespace Titanium.Web.Proxy.Helpers
public override string ToString() public override string ToString()
{ {
if (!IsHttps) if (!IsHttps)
{
return "http=" + HostName + ":" + Port; return "http=" + HostName + ":" + Port;
}
else else
{
return "https=" + HostName + ":" + Port; return "https=" + HostName + ":" + Port;
}
} }
} }
/// <summary> /// <summary>
...@@ -44,6 +48,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -44,6 +48,7 @@ namespace Titanium.Web.Proxy.Helpers
{ {
var reg = Registry.CurrentUser.OpenSubKey( var reg = Registry.CurrentUser.OpenSubKey(
"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", true); "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", true);
if (reg != null) if (reg != null)
{ {
prepareRegistry(reg); prepareRegistry(reg);
......
...@@ -27,7 +27,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -27,7 +27,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="tunnelPort"></param> /// <param name="tunnelPort"></param>
/// <param name="isHttps"></param> /// <param name="isHttps"></param>
/// <returns></returns> /// <returns></returns>
internal static async Task SendRaw(Stream clientStream, string httpCmd, List<HttpHeader> requestHeaders, string hostName, internal static async Task SendRaw(Stream clientStream, string httpCmd, Dictionary<string, HttpHeader> requestHeaders, string hostName,
int tunnelPort, bool isHttps, SslProtocols supportedProtocols) int tunnelPort, bool isHttps, SslProtocols supportedProtocols)
{ {
//prepare the prefix content //prepare the prefix content
...@@ -35,17 +35,22 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -35,17 +35,22 @@ namespace Titanium.Web.Proxy.Helpers
if (httpCmd != null || requestHeaders != null) if (httpCmd != null || requestHeaders != null)
{ {
sb = new StringBuilder(); sb = new StringBuilder();
if (httpCmd != null) if (httpCmd != null)
{ {
sb.Append(httpCmd); sb.Append(httpCmd);
sb.Append(Environment.NewLine); sb.Append(Environment.NewLine);
} }
if (requestHeaders != null) if (requestHeaders != null)
foreach (var header in requestHeaders.Select(t => t.ToString())) {
foreach (var header in requestHeaders.Select(t => t.Value.ToString()))
{ {
sb.Append(header); sb.Append(header);
sb.Append(Environment.NewLine); sb.Append(Environment.NewLine);
} }
}
sb.Append(Environment.NewLine); sb.Append(Environment.NewLine);
} }
...@@ -70,7 +75,9 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -70,7 +75,9 @@ namespace Titanium.Web.Proxy.Helpers
catch catch
{ {
if (sslStream != null) if (sslStream != null)
{
sslStream.Dispose(); sslStream.Dispose();
}
throw; throw;
} }
...@@ -80,9 +87,13 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -80,9 +87,13 @@ namespace Titanium.Web.Proxy.Helpers
//Now async relay all server=>client & client=>server data //Now async relay all server=>client & client=>server data
if (sb != null) if (sb != null)
{
sendRelay = clientStream.CopyToAsync(sb.ToString(), tunnelStream); sendRelay = clientStream.CopyToAsync(sb.ToString(), tunnelStream);
}
else else
{
sendRelay = clientStream.CopyToAsync(string.Empty, tunnelStream); sendRelay = clientStream.CopyToAsync(string.Empty, tunnelStream);
}
var receiveRelay = tunnelStream.CopyToAsync(string.Empty, clientStream); var receiveRelay = tunnelStream.CopyToAsync(string.Empty, clientStream);
...@@ -98,7 +109,9 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -98,7 +109,9 @@ namespace Titanium.Web.Proxy.Helpers
} }
if (tunnelClient != null) if (tunnelClient != null)
{
tunnelClient.Close(); tunnelClient.Close();
}
throw; throw;
} }
......
...@@ -58,7 +58,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -58,7 +58,7 @@ namespace Titanium.Web.Proxy.Http
Stream stream = ServerConnection.Stream; Stream stream = ServerConnection.Stream;
StringBuilder requestLines = new StringBuilder(); StringBuilder requestLines = new StringBuilder();
//prepare the request & headers //prepare the request & headers
requestLines.AppendLine(string.Join(" ", new string[3] requestLines.AppendLine(string.Join(" ", new string[3]
{ {
...@@ -66,10 +66,22 @@ namespace Titanium.Web.Proxy.Http ...@@ -66,10 +66,22 @@ namespace Titanium.Web.Proxy.Http
this.Request.RequestUri.PathAndQuery, this.Request.RequestUri.PathAndQuery,
string.Format("HTTP/{0}.{1}",this.Request.HttpVersion.Major, this.Request.HttpVersion.Minor) string.Format("HTTP/{0}.{1}",this.Request.HttpVersion.Major, this.Request.HttpVersion.Minor)
})); }));
//write request headers //write request headers
foreach (HttpHeader httpHeader in this.Request.RequestHeaders) foreach (var headerItem in this.Request.RequestHeaders)
{ {
requestLines.AppendLine(httpHeader.Name + ':' + httpHeader.Value); var header = headerItem.Value;
requestLines.AppendLine(header.Name + ':' + header.Value);
}
//write non unique request headers
foreach (var headerItem in this.Request.NonUniqueRequestHeaders)
{
var headers = headerItem.Value;
foreach (var header in headers)
{
requestLines.AppendLine(header.Name + ':' + header.Value);
}
} }
requestLines.AppendLine(); requestLines.AppendLine();
...@@ -80,6 +92,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -80,6 +92,7 @@ namespace Titanium.Web.Proxy.Http
await stream.FlushAsync(); await stream.FlushAsync();
if (enable100ContinueBehaviour) if (enable100ContinueBehaviour)
{
if (this.Request.ExpectContinue) if (this.Request.ExpectContinue)
{ {
var httpResult = (await ServerConnection.StreamReader.ReadLineAsync()).Split(ProxyConstants.SpaceSplit, 3); var httpResult = (await ServerConnection.StreamReader.ReadLineAsync()).Split(ProxyConstants.SpaceSplit, 3);
...@@ -100,6 +113,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -100,6 +113,7 @@ namespace Titanium.Web.Proxy.Http
await ServerConnection.StreamReader.ReadLineAsync(); await ServerConnection.StreamReader.ReadLineAsync();
} }
} }
}
} }
/// <summary> /// <summary>
...@@ -121,7 +135,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -121,7 +135,7 @@ namespace Titanium.Web.Proxy.Http
var httpVersion = httpResult[0].Trim().ToLower(); var httpVersion = httpResult[0].Trim().ToLower();
var version = new Version(1,1); var version = new Version(1, 1);
if (httpVersion == "http/1.0") if (httpVersion == "http/1.0")
{ {
version = new Version(1, 0); version = new Version(1, 0);
...@@ -155,13 +169,34 @@ namespace Titanium.Web.Proxy.Http ...@@ -155,13 +169,34 @@ namespace Titanium.Web.Proxy.Http
return; return;
} }
//read response headers //Read the Response headers
List<string> responseLines = await ServerConnection.StreamReader.ReadAllLinesAsync(); string tmpLine;
while (!string.IsNullOrEmpty(tmpLine = await ServerConnection.StreamReader.ReadLineAsync()))
for (int index = 0; index < responseLines.Count; ++index)
{ {
string[] strArray = responseLines[index].Split(ProxyConstants.ColonSplit, 2); var header = tmpLine.Split(ProxyConstants.ColonSplit, 2);
this.Response.ResponseHeaders.Add(new HttpHeader(strArray[0], strArray[1]));
var newHeader = new HttpHeader(header[0], header[1]);
if (Response.NonUniqueResponseHeaders.ContainsKey(newHeader.Name))
{
Response.NonUniqueResponseHeaders[newHeader.Name].Add(newHeader);
}
else if (Response.ResponseHeaders.ContainsKey(newHeader.Name))
{
var existing = Response.ResponseHeaders[newHeader.Name];
var nonUniqueHeaders = new List<HttpHeader>();
nonUniqueHeaders.Add(existing);
nonUniqueHeaders.Add(newHeader);
Response.NonUniqueResponseHeaders.Add(newHeader.Name, nonUniqueHeaders);
Response.ResponseHeaders.Remove(newHeader.Name);
}
else
{
Response.ResponseHeaders.Add(newHeader.Name, newHeader);
}
} }
} }
} }
......
...@@ -34,18 +34,26 @@ namespace Titanium.Web.Proxy.Http ...@@ -34,18 +34,26 @@ namespace Titanium.Web.Proxy.Http
{ {
get get
{ {
var host = RequestHeaders.FirstOrDefault(x => x.Name.ToLower() == "host"); var hasHeader = RequestHeaders.ContainsKey("host");
if (host != null) if (hasHeader)
return host.Value; {
return RequestHeaders["host"].Value;
}
return null; return null;
} }
set set
{ {
var host = RequestHeaders.FirstOrDefault(x => x.Name.ToLower() == "host"); var hasHeader = RequestHeaders.ContainsKey("host");
if (host != null) if (hasHeader)
host.Value = value; {
RequestHeaders["host"].Value = value;
}
else else
RequestHeaders.Add(new HttpHeader("Host", value)); {
RequestHeaders.Add("Host", new HttpHeader("Host", value));
}
} }
} }
...@@ -56,11 +64,11 @@ namespace Titanium.Web.Proxy.Http ...@@ -56,11 +64,11 @@ namespace Titanium.Web.Proxy.Http
{ {
get get
{ {
var header = this.RequestHeaders.FirstOrDefault(x => x.Name.ToLower().Equals("content-encoding")); var hasHeader = RequestHeaders.ContainsKey("content-encoding");
if (header != null) if (hasHeader)
{ {
return header.Value.Trim(); return RequestHeaders["content-encoding"].Value;
} }
return null; return null;
...@@ -74,36 +82,52 @@ namespace Titanium.Web.Proxy.Http ...@@ -74,36 +82,52 @@ namespace Titanium.Web.Proxy.Http
{ {
get get
{ {
var header = RequestHeaders.FirstOrDefault(x => x.Name.ToLower() == "content-length"); var hasHeader = RequestHeaders.ContainsKey("content-length");
if (header == null) if (hasHeader == false)
{
return -1; return -1;
}
var header = RequestHeaders["content-length"];
long contentLen; long contentLen;
long.TryParse(header.Value, out contentLen); long.TryParse(header.Value, out contentLen);
if (contentLen >=0) if (contentLen >= 0)
{
return contentLen; return contentLen;
}
return -1; return -1;
} }
set set
{ {
var header = RequestHeaders.FirstOrDefault(x => x.Name.ToLower() == "content-length"); var hasHeader = RequestHeaders.ContainsKey("content-length");
var header = RequestHeaders["content-length"];
if (value >= 0) if (value >= 0)
{ {
if (header != null) if (hasHeader)
{
header.Value = value.ToString(); header.Value = value.ToString();
}
else else
RequestHeaders.Add(new HttpHeader("content-length", value.ToString())); {
RequestHeaders.Add("content-length", new HttpHeader("content-length", value.ToString()));
}
IsChunked = false; IsChunked = false;
} }
else else
{ {
if (header != null) if (hasHeader)
this.RequestHeaders.Remove(header); {
RequestHeaders.Remove("content-length");
}
} }
} }
} }
...@@ -114,19 +138,29 @@ namespace Titanium.Web.Proxy.Http ...@@ -114,19 +138,29 @@ namespace Titanium.Web.Proxy.Http
{ {
get get
{ {
var header = RequestHeaders.FirstOrDefault(x => x.Name.ToLower() == "content-type"); var hasHeader = RequestHeaders.ContainsKey("content-type");
if (header != null)
if (hasHeader)
{
var header = RequestHeaders["content-type"];
return header.Value; return header.Value;
}
return null; return null;
} }
set set
{ {
var header = RequestHeaders.FirstOrDefault(x => x.Name.ToLower() == "content-type"); var hasHeader = RequestHeaders.ContainsKey("content-type");
if (header != null) if (hasHeader)
{
var header = RequestHeaders["content-type"];
header.Value = value.ToString(); header.Value = value.ToString();
}
else else
RequestHeaders.Add(new HttpHeader("content-type", value.ToString())); {
RequestHeaders.Add("content-type", new HttpHeader("content-type", value.ToString()));
}
} }
} }
...@@ -138,29 +172,41 @@ namespace Titanium.Web.Proxy.Http ...@@ -138,29 +172,41 @@ namespace Titanium.Web.Proxy.Http
{ {
get get
{ {
var header = RequestHeaders.FirstOrDefault(x => x.Name.ToLower() == "transfer-encoding"); var hasHeader = RequestHeaders.ContainsKey("transfer-encoding");
if (header != null) return header.Value.ToLower().Contains("chunked");
if (hasHeader)
{
var header = RequestHeaders["transfer-encoding"];
return header.Value.ToLower().Contains("chunked");
}
return false; return false;
} }
set set
{ {
var header = RequestHeaders.FirstOrDefault(x => x.Name.ToLower() == "transfer-encoding"); var hasHeader = RequestHeaders.ContainsKey("transfer-encoding");
if (value) if (value)
{ {
if (header != null) if (hasHeader)
{ {
var header = RequestHeaders["transfer-encoding"];
header.Value = "chunked"; header.Value = "chunked";
} }
else else
RequestHeaders.Add(new HttpHeader("transfer-encoding", "chunked")); {
RequestHeaders.Add("transfer-encoding", new HttpHeader("transfer-encoding", "chunked"));
}
this.ContentLength = -1; ContentLength = -1;
} }
else else
{ {
if (header != null) if (hasHeader)
RequestHeaders.Remove(header); {
RequestHeaders.Remove("transfer-encoding");
}
} }
} }
} }
...@@ -172,8 +218,15 @@ namespace Titanium.Web.Proxy.Http ...@@ -172,8 +218,15 @@ namespace Titanium.Web.Proxy.Http
{ {
get get
{ {
var header = RequestHeaders.FirstOrDefault(x => x.Name.ToLower() == "expect"); var hasHeader = RequestHeaders.ContainsKey("expect");
if (header != null) return header.Value.Equals("100-continue");
if (hasHeader)
{
var header = RequestHeaders["expect"];
return header.Value.Equals("100-continue");
}
return false; return false;
} }
} }
...@@ -213,12 +266,19 @@ namespace Titanium.Web.Proxy.Http ...@@ -213,12 +266,19 @@ namespace Titanium.Web.Proxy.Http
{ {
get get
{ {
var header = RequestHeaders.FirstOrDefault(x => x.Name.ToLower() == "upgrade"); var hasHeader = RequestHeaders.ContainsKey("upgrade");
if (header == null)
if (hasHeader == false)
{
return false; return false;
}
var header = RequestHeaders["upgrade"];
if (header.Value.ToLower() == "websocket") if (header.Value.ToLower() == "websocket")
{
return true; return true;
}
return false; return false;
...@@ -226,9 +286,14 @@ namespace Titanium.Web.Proxy.Http ...@@ -226,9 +286,14 @@ namespace Titanium.Web.Proxy.Http
} }
/// <summary> /// <summary>
/// Request heade collection /// Unique Request header collection
/// </summary>
public Dictionary<string, HttpHeader> RequestHeaders { get; set; }
/// <summary>
/// Non Unique headers
/// </summary> /// </summary>
public List<HttpHeader> RequestHeaders { get; set; } public Dictionary<string, List<HttpHeader>> NonUniqueRequestHeaders { get; set; }
/// <summary> /// <summary>
/// Does server responsed positively for 100 continue request /// Does server responsed positively for 100 continue request
...@@ -242,7 +307,8 @@ namespace Titanium.Web.Proxy.Http ...@@ -242,7 +307,8 @@ namespace Titanium.Web.Proxy.Http
public Request() public Request()
{ {
this.RequestHeaders = new List<HttpHeader>(); RequestHeaders = new Dictionary<string, HttpHeader>(StringComparer.OrdinalIgnoreCase);
NonUniqueRequestHeaders = new Dictionary<string, List<HttpHeader>>(StringComparer.OrdinalIgnoreCase);
} }
} }
......
...@@ -25,10 +25,12 @@ namespace Titanium.Web.Proxy.Http ...@@ -25,10 +25,12 @@ namespace Titanium.Web.Proxy.Http
{ {
get get
{ {
var header = this.ResponseHeaders.FirstOrDefault(x => x.Name.ToLower().Equals("content-encoding")); var hasHeader = ResponseHeaders.ContainsKey("content-encoding");
if (header != null) if (hasHeader)
{ {
var header = ResponseHeaders["content-encoding"];
return header.Value.Trim(); return header.Value.Trim();
} }
...@@ -45,11 +47,16 @@ namespace Titanium.Web.Proxy.Http ...@@ -45,11 +47,16 @@ namespace Titanium.Web.Proxy.Http
{ {
get get
{ {
var header = this.ResponseHeaders.FirstOrDefault(x => x.Name.ToLower().Equals("connection")); var hasHeader = ResponseHeaders.ContainsKey("connection");
if (header != null && header.Value.ToLower().Contains("close")) if (hasHeader)
{ {
return false; var header = ResponseHeaders["connection"];
if (header.Value.ToLower().Contains("close"))
{
return false;
}
} }
return true; return true;
...@@ -64,10 +71,12 @@ namespace Titanium.Web.Proxy.Http ...@@ -64,10 +71,12 @@ namespace Titanium.Web.Proxy.Http
{ {
get get
{ {
var header = this.ResponseHeaders.FirstOrDefault(x => x.Name.ToLower().Equals("content-type")); var hasHeader = ResponseHeaders.ContainsKey("content-type");
if (header != null) if (hasHeader)
{ {
var header = ResponseHeaders["content-type"];
return header.Value; return header.Value;
} }
...@@ -83,36 +92,49 @@ namespace Titanium.Web.Proxy.Http ...@@ -83,36 +92,49 @@ namespace Titanium.Web.Proxy.Http
{ {
get get
{ {
var header = this.ResponseHeaders.FirstOrDefault(x => x.Name.ToLower().Equals("content-length")); var hasHeader = ResponseHeaders.ContainsKey("content-length");
if (header == null) if (hasHeader == false)
{
return -1; return -1;
}
var header = ResponseHeaders["content-length"];
long contentLen; long contentLen;
long.TryParse(header.Value, out contentLen); long.TryParse(header.Value, out contentLen);
if (contentLen >= 0) if (contentLen >= 0)
{
return contentLen; return contentLen;
}
return -1; return -1;
} }
set set
{ {
var header = this.ResponseHeaders.FirstOrDefault(x => x.Name.ToLower().Equals("content-length")); var hasHeader = ResponseHeaders.ContainsKey("content-length");
if (value >= 0) if (value >= 0)
{ {
if (header != null) if (hasHeader)
{
var header = ResponseHeaders["content-length"];
header.Value = value.ToString(); header.Value = value.ToString();
}
else else
ResponseHeaders.Add(new HttpHeader("content-length", value.ToString())); {
ResponseHeaders.Add("content-length", new HttpHeader("content-length", value.ToString()));
}
IsChunked = false; IsChunked = false;
} }
else else
{ {
if (header != null) if (hasHeader)
this.ResponseHeaders.Remove(header); {
ResponseHeaders.Remove("content-length");
}
} }
} }
} }
...@@ -124,11 +146,16 @@ namespace Titanium.Web.Proxy.Http ...@@ -124,11 +146,16 @@ namespace Titanium.Web.Proxy.Http
{ {
get get
{ {
var header = this.ResponseHeaders.FirstOrDefault(x => x.Name.ToLower().Equals("transfer-encoding")); var hasHeader = ResponseHeaders.ContainsKey("transfer-encoding");
if (header != null && header.Value.ToLower().Contains("chunked")) if (hasHeader)
{ {
return true; var header = ResponseHeaders["transfer-encoding"];
if (header.Value.ToLower().Contains("chunked"))
{
return true;
}
} }
return false; return false;
...@@ -136,23 +163,29 @@ namespace Titanium.Web.Proxy.Http ...@@ -136,23 +163,29 @@ namespace Titanium.Web.Proxy.Http
} }
set set
{ {
var header = this.ResponseHeaders.FirstOrDefault(x => x.Name.ToLower().Equals("transfer-encoding")); var hasHeader = ResponseHeaders.ContainsKey("transfer-encoding");
if (value) if (value)
{ {
if (header != null) if (hasHeader)
{ {
var header = ResponseHeaders["transfer-encoding"];
header.Value = "chunked"; header.Value = "chunked";
} }
else else
ResponseHeaders.Add(new HttpHeader("transfer-encoding", "chunked")); {
ResponseHeaders.Add("transfer-encoding", new HttpHeader("transfer-encoding", "chunked"));
}
this.ContentLength = -1; ContentLength = -1;
} }
else else
{ {
if (header != null) if (hasHeader)
ResponseHeaders.Remove(header); {
ResponseHeaders.Remove("transfer-encoding");
}
} }
} }
...@@ -161,7 +194,13 @@ namespace Titanium.Web.Proxy.Http ...@@ -161,7 +194,13 @@ namespace Titanium.Web.Proxy.Http
/// <summary> /// <summary>
/// Collection of all response headers /// Collection of all response headers
/// </summary> /// </summary>
public List<HttpHeader> ResponseHeaders { get; set; } public Dictionary<string, HttpHeader> ResponseHeaders { get; set; }
/// <summary>
/// Non Unique headers
/// </summary>
public Dictionary<string, List<HttpHeader>> NonUniqueResponseHeaders { get; set; }
/// <summary> /// <summary>
/// Response network stream /// Response network stream
...@@ -193,7 +232,8 @@ namespace Titanium.Web.Proxy.Http ...@@ -193,7 +232,8 @@ namespace Titanium.Web.Proxy.Http
public Response() public Response()
{ {
this.ResponseHeaders = new List<HttpHeader>(); this.ResponseHeaders = new Dictionary<string, HttpHeader>(StringComparer.OrdinalIgnoreCase);
this.NonUniqueResponseHeaders = new Dictionary<string, List<HttpHeader>>(StringComparer.OrdinalIgnoreCase);
} }
} }
......
...@@ -9,7 +9,10 @@ namespace Titanium.Web.Proxy.Models ...@@ -9,7 +9,10 @@ namespace Titanium.Web.Proxy.Models
{ {
public HttpHeader(string name, string value) public HttpHeader(string name, string value)
{ {
if (string.IsNullOrEmpty(name)) throw new Exception("Name cannot be null"); if (string.IsNullOrEmpty(name))
{
throw new Exception("Name cannot be null");
}
Name = name.Trim(); Name = name.Trim();
Value = value.Trim(); Value = value.Trim();
......
...@@ -114,7 +114,9 @@ namespace Titanium.Web.Proxy.Network ...@@ -114,7 +114,9 @@ namespace Titanium.Web.Proxy.Network
certificates = FindCertificates(store, certificateSubject); certificates = FindCertificates(store, certificateSubject);
if (certificates != null) if (certificates != null)
{
certificate = certificates[0]; certificate = certificates[0];
}
} }
if (certificate == null) if (certificate == null)
...@@ -127,16 +129,22 @@ namespace Titanium.Web.Proxy.Network ...@@ -127,16 +129,22 @@ namespace Titanium.Web.Proxy.Network
//remove it from store //remove it from store
if (!isRootCertificate) if (!isRootCertificate)
{
DestroyCertificate(certificateName); DestroyCertificate(certificateName);
}
if (certificates != null) if (certificates != null)
{
certificate = certificates[0]; certificate = certificates[0];
}
} }
store.Close(); store.Close();
if (certificate != null && !certificateCache.ContainsKey(certificateName)) if (certificate != null && !certificateCache.ContainsKey(certificateName))
{
certificateCache.Add(certificateName, new CachedCertificate() { Certificate = certificate }); certificateCache.Add(certificateName, new CachedCertificate() { Certificate = certificate });
}
return certificate; return certificate;
} }
...@@ -162,7 +170,9 @@ namespace Titanium.Web.Proxy.Network ...@@ -162,7 +170,9 @@ namespace Titanium.Web.Proxy.Network
string file = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "makecert.exe"); string file = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "makecert.exe");
if (!File.Exists(file)) if (!File.Exists(file))
{
throw new Exception("Unable to locate 'makecert.exe'."); throw new Exception("Unable to locate 'makecert.exe'.");
}
process.StartInfo.Verb = "runas"; process.StartInfo.Verb = "runas";
process.StartInfo.Arguments = args != null ? args[0] : string.Empty; process.StartInfo.Arguments = args != null ? args[0] : string.Empty;
...@@ -178,6 +188,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -178,6 +188,7 @@ namespace Titanium.Web.Proxy.Network
}; };
bool started = process.Start(); bool started = process.Start();
if (!started) if (!started)
{ {
//you may allow for the process to be re-used (started = false) //you may allow for the process to be re-used (started = false)
...@@ -214,6 +225,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -214,6 +225,7 @@ namespace Titanium.Web.Proxy.Network
string certificateSubject = string.Format("CN={0}, O={1}", certificateName, Issuer); string certificateSubject = string.Format("CN={0}, O={1}", certificateName, Issuer);
certificates = FindCertificates(store, certificateSubject); certificates = FindCertificates(store, certificateSubject);
if (certificates != null) if (certificates != null)
{ {
store.RemoveRange(certificates); store.RemoveRange(certificates);
...@@ -226,6 +238,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -226,6 +238,7 @@ namespace Titanium.Web.Proxy.Network
{ {
certificateCache.Remove(certificateName); certificateCache.Remove(certificateName);
} }
return certificates == null; return certificates == null;
} }
/// <summary> /// <summary>
...@@ -279,7 +292,9 @@ namespace Titanium.Web.Proxy.Network ...@@ -279,7 +292,9 @@ namespace Titanium.Web.Proxy.Network
foreach (var cache in outdated) foreach (var cache in outdated)
certificateCache.Remove(cache.Key); certificateCache.Remove(cache.Key);
} }
finally { semaphoreLock.Release(); } finally {
semaphoreLock.Release();
}
//after a minute come back to check for outdated certificates in cache //after a minute come back to check for outdated certificates in cache
await Task.Delay(1000 * 60); await Task.Delay(1000 * 60);
...@@ -289,10 +304,14 @@ namespace Titanium.Web.Proxy.Network ...@@ -289,10 +304,14 @@ namespace Titanium.Web.Proxy.Network
public void Dispose() public void Dispose()
{ {
if (MyStore != null) if (MyStore != null)
{
MyStore.Close(); MyStore.Close();
}
if (RootStore != null) if (RootStore != null)
{
RootStore.Close(); RootStore.Close();
}
} }
} }
} }
\ No newline at end of file
...@@ -51,6 +51,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -51,6 +51,7 @@ namespace Titanium.Web.Proxy.Network
while (true) while (true)
{ {
await connectionAccessLock.WaitAsync(); await connectionAccessLock.WaitAsync();
try try
{ {
connectionCache.TryGetValue(key, out cachedConnections); connectionCache.TryGetValue(key, out cachedConnections);
...@@ -65,7 +66,9 @@ namespace Titanium.Web.Proxy.Network ...@@ -65,7 +66,9 @@ namespace Titanium.Web.Proxy.Network
cached = null; cached = null;
} }
} }
finally { connectionAccessLock.Release(); } finally {
connectionAccessLock.Release();
}
if (cached != null && !cached.TcpClient.Client.IsConnected()) if (cached != null && !cached.TcpClient.Client.IsConnected())
{ {
...@@ -75,14 +78,18 @@ namespace Titanium.Web.Proxy.Network ...@@ -75,14 +78,18 @@ namespace Titanium.Web.Proxy.Network
} }
if (cached == null) if (cached == null)
{
break; break;
}
} }
if (cached == null) if (cached == null)
{
cached = await CreateClient(hostname, port, isHttps, version, upStreamHttpProxy, upStreamHttpsProxy, bufferSize, supportedSslProtocols, cached = await CreateClient(hostname, port, isHttps, version, upStreamHttpProxy, upStreamHttpsProxy, bufferSize, supportedSslProtocols,
remoteCertificateValidationCallBack, localCertificateSelectionCallback); remoteCertificateValidationCallBack, localCertificateSelectionCallback);
}
if (cachedConnections == null || cachedConnections.Count() <= 2) if (cachedConnections == null || cachedConnections.Count() <= 2)
{ {
...@@ -147,7 +154,9 @@ namespace Titanium.Web.Proxy.Network ...@@ -147,7 +154,9 @@ namespace Titanium.Web.Proxy.Network
var result = await reader.ReadLineAsync(); var result = await reader.ReadLineAsync();
if (!result.ToLower().Contains("200 connection established")) if (!result.ToLower().Contains("200 connection established"))
{
throw new Exception("Upstream proxy failed to create a secure tunnel"); throw new Exception("Upstream proxy failed to create a secure tunnel");
}
await reader.ReadAllLinesAsync(); await reader.ReadAllLinesAsync();
} }
...@@ -162,13 +171,18 @@ namespace Titanium.Web.Proxy.Network ...@@ -162,13 +171,18 @@ namespace Titanium.Web.Proxy.Network
{ {
sslStream = new SslStream(stream, true, remoteCertificateValidationCallBack, sslStream = new SslStream(stream, true, remoteCertificateValidationCallBack,
localCertificateSelectionCallback); localCertificateSelectionCallback);
await sslStream.AuthenticateAsClientAsync(hostname, null, supportedSslProtocols, false); await sslStream.AuthenticateAsClientAsync(hostname, null, supportedSslProtocols, false);
stream = (Stream)sslStream; stream = (Stream)sslStream;
} }
catch catch
{ {
if (sslStream != null) if (sslStream != null)
{
sslStream.Dispose(); sslStream.Dispose();
}
throw; throw;
} }
} }
...@@ -209,19 +223,25 @@ namespace Titanium.Web.Proxy.Network ...@@ -209,19 +223,25 @@ namespace Titanium.Web.Proxy.Network
connection.LastAccess = DateTime.Now; connection.LastAccess = DateTime.Now;
var key = GetConnectionKey(connection.HostName, connection.port, connection.IsHttps, connection.Version); var key = GetConnectionKey(connection.HostName, connection.port, connection.IsHttps, connection.Version);
await connectionAccessLock.WaitAsync(); await connectionAccessLock.WaitAsync();
try try
{ {
List<TcpConnectionCache> cachedConnections; List<TcpConnectionCache> cachedConnections;
connectionCache.TryGetValue(key, out cachedConnections); connectionCache.TryGetValue(key, out cachedConnections);
if (cachedConnections != null) if (cachedConnections != null)
{
cachedConnections.Add(connection); cachedConnections.Add(connection);
}
else else
{
connectionCache.Add(key, new List<TcpConnectionCache>() { connection }); connectionCache.Add(key, new List<TcpConnectionCache>() { connection });
}
} }
finally { connectionAccessLock.Release(); } finally {
connectionAccessLock.Release();
}
} }
private bool clearConenctions { get; set; } private bool clearConenctions { get; set; }
...@@ -240,9 +260,11 @@ namespace Titanium.Web.Proxy.Network ...@@ -240,9 +260,11 @@ namespace Titanium.Web.Proxy.Network
internal async void ClearIdleConnections(int connectionCacheTimeOutMinutes) internal async void ClearIdleConnections(int connectionCacheTimeOutMinutes)
{ {
clearConenctions = true; clearConenctions = true;
while (clearConenctions) while (clearConenctions)
{ {
await connectionAccessLock.WaitAsync(); await connectionAccessLock.WaitAsync();
try try
{ {
var cutOff = DateTime.Now.AddMinutes(-1 * connectionCacheTimeOutMinutes); var cutOff = DateTime.Now.AddMinutes(-1 * connectionCacheTimeOutMinutes);
...@@ -255,7 +277,9 @@ namespace Titanium.Web.Proxy.Network ...@@ -255,7 +277,9 @@ namespace Titanium.Web.Proxy.Network
connectionCache.ToList().ForEach(x => x.Value.RemoveAll(y => y.LastAccess < cutOff)); connectionCache.ToList().ForEach(x => x.Value.RemoveAll(y => y.LastAccess < cutOff));
} }
finally { connectionAccessLock.Release(); } finally {
connectionAccessLock.Release();
}
//every minute run this //every minute run this
await Task.Delay(1000 * 60); await Task.Delay(1000 * 60);
......
...@@ -143,13 +143,17 @@ namespace Titanium.Web.Proxy ...@@ -143,13 +143,17 @@ namespace Titanium.Web.Proxy
/// <param name="endPoint"></param> /// <param name="endPoint"></param>
public void AddEndPoint(ProxyEndPoint endPoint) public void AddEndPoint(ProxyEndPoint endPoint)
{ {
if (ProxyEndPoints.Any(x=>x.IpAddress == endPoint.IpAddress && x.Port == endPoint.Port)) if (ProxyEndPoints.Any(x => x.IpAddress == endPoint.IpAddress && x.Port == endPoint.Port))
{
throw new Exception("Cannot add another endpoint to same port & ip address"); throw new Exception("Cannot add another endpoint to same port & ip address");
}
ProxyEndPoints.Add(endPoint); ProxyEndPoints.Add(endPoint);
if (proxyRunning) if (proxyRunning)
{
Listen(endPoint); Listen(endPoint);
}
} }
/// <summary> /// <summary>
...@@ -160,12 +164,16 @@ namespace Titanium.Web.Proxy ...@@ -160,12 +164,16 @@ namespace Titanium.Web.Proxy
public void RemoveEndPoint(ProxyEndPoint endPoint) public void RemoveEndPoint(ProxyEndPoint endPoint)
{ {
if (ProxyEndPoints.Contains(endPoint) == false) if (ProxyEndPoints.Contains(endPoint) == false)
{
throw new Exception("Cannot remove endPoints not added to proxy"); throw new Exception("Cannot remove endPoints not added to proxy");
}
ProxyEndPoints.Remove(endPoint); ProxyEndPoints.Remove(endPoint);
if (proxyRunning) if (proxyRunning)
{
QuitListen(endPoint); QuitListen(endPoint);
}
} }
/// <summary> /// <summary>
...@@ -254,7 +262,9 @@ namespace Titanium.Web.Proxy ...@@ -254,7 +262,9 @@ namespace Titanium.Web.Proxy
public void Start() public void Start()
{ {
if (proxyRunning) if (proxyRunning)
{
throw new Exception("Proxy is already running."); throw new Exception("Proxy is already running.");
}
certTrusted = certificateCacheManager.CreateTrustedRootCertificate().Result; certTrusted = certificateCacheManager.CreateTrustedRootCertificate().Result;
...@@ -275,7 +285,9 @@ namespace Titanium.Web.Proxy ...@@ -275,7 +285,9 @@ namespace Titanium.Web.Proxy
public void Stop() public void Stop()
{ {
if (!proxyRunning) if (!proxyRunning)
{
throw new Exception("Proxy is not running."); throw new Exception("Proxy is not running.");
}
var setAsSystemProxy = ProxyEndPoints.OfType<ExplicitProxyEndPoint>().Any(x => x.IsSystemHttpProxy || x.IsSystemHttpsProxy); var setAsSystemProxy = ProxyEndPoints.OfType<ExplicitProxyEndPoint>().Any(x => x.IsSystemHttpProxy || x.IsSystemHttpsProxy);
...@@ -332,10 +344,14 @@ namespace Titanium.Web.Proxy ...@@ -332,10 +344,14 @@ namespace Titanium.Web.Proxy
private void ValidateEndPointAsSystemProxy(ExplicitProxyEndPoint endPoint) private void ValidateEndPointAsSystemProxy(ExplicitProxyEndPoint endPoint)
{ {
if (ProxyEndPoints.Contains(endPoint) == false) if (ProxyEndPoints.Contains(endPoint) == false)
{
throw new Exception("Cannot set endPoints not added to proxy as system proxy"); throw new Exception("Cannot set endPoints not added to proxy as system proxy");
}
if (!proxyRunning) if (!proxyRunning)
{
throw new Exception("Cannot set system proxy settings before proxy has been started."); throw new Exception("Cannot set system proxy settings before proxy has been started.");
}
} }
/// <summary> /// <summary>
...@@ -375,7 +391,9 @@ namespace Titanium.Web.Proxy ...@@ -375,7 +391,9 @@ namespace Titanium.Web.Proxy
public void Dispose() public void Dispose()
{ {
if (proxyRunning) if (proxyRunning)
{
Stop(); Stop();
}
certificateCacheManager.Dispose(); certificateCacheManager.Dispose();
} }
......
This diff is collapsed.
...@@ -8,6 +8,7 @@ using Titanium.Web.Proxy.Models; ...@@ -8,6 +8,7 @@ using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Compression; using Titanium.Web.Proxy.Compression;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy namespace Titanium.Web.Proxy
{ {
...@@ -17,7 +18,7 @@ namespace Titanium.Web.Proxy ...@@ -17,7 +18,7 @@ namespace Titanium.Web.Proxy
partial class ProxyServer partial class ProxyServer
{ {
//Called asynchronously when a request was successfully and we received the response //Called asynchronously when a request was successfully and we received the response
public async Task HandleHttpSessionResponse(SessionEventArgs args) public async Task HandleHttpSessionResponse(SessionEventArgs args)
{ {
//read response & headers from server //read response & headers from server
await args.WebSession.ReceiveResponse(); await args.WebSession.ReceiveResponse();
...@@ -25,7 +26,9 @@ namespace Titanium.Web.Proxy ...@@ -25,7 +26,9 @@ namespace Titanium.Web.Proxy
try try
{ {
if (!args.WebSession.Response.ResponseBodyRead) if (!args.WebSession.Response.ResponseBodyRead)
{
args.WebSession.Response.ResponseStream = args.WebSession.ServerConnection.Stream; args.WebSession.Response.ResponseStream = args.WebSession.ServerConnection.Stream;
}
//If user requested call back then do it //If user requested call back then do it
if (BeforeResponse != null && !args.WebSession.Response.ResponseLocked) if (BeforeResponse != null && !args.WebSession.Response.ResponseLocked)
...@@ -71,26 +74,39 @@ namespace Titanium.Web.Proxy ...@@ -71,26 +74,39 @@ namespace Titanium.Web.Proxy
args.WebSession.Response.ResponseBody = await GetCompressedResponseBody(contentEncoding, args.WebSession.Response.ResponseBody); args.WebSession.Response.ResponseBody = await GetCompressedResponseBody(contentEncoding, args.WebSession.Response.ResponseBody);
if (isChunked == false) if (isChunked == false)
{
args.WebSession.Response.ContentLength = args.WebSession.Response.ResponseBody.Length; args.WebSession.Response.ContentLength = args.WebSession.Response.ResponseBody.Length;
}
else else
{
args.WebSession.Response.ContentLength = -1; args.WebSession.Response.ContentLength = -1;
}
} }
await WriteResponseHeaders(args.ProxyClient.ClientStreamWriter, args.WebSession.Response.ResponseHeaders); await WriteResponseHeaders(args.ProxyClient.ClientStreamWriter, args.WebSession.Response);
await args.ProxyClient.ClientStream.WriteResponseBody(args.WebSession.Response.ResponseBody, isChunked); await args.ProxyClient.ClientStream.WriteResponseBody(args.WebSession.Response.ResponseBody, isChunked);
} }
else else
{ {
await WriteResponseHeaders(args.ProxyClient.ClientStreamWriter, args.WebSession.Response.ResponseHeaders); await WriteResponseHeaders(args.ProxyClient.ClientStreamWriter, args.WebSession.Response);
//Write body only if response is chunked or content length >0 //Write body only if response is chunked or content length >0
//Is none are true then check if connection:close header exist, if so write response until server or client terminates the connection //Is none are true then check if connection:close header exist, if so write response until server or client terminates the connection
if (args.WebSession.Response.IsChunked || args.WebSession.Response.ContentLength > 0 || !args.WebSession.Response.ResponseKeepAlive) if (args.WebSession.Response.IsChunked || args.WebSession.Response.ContentLength > 0
await args.WebSession.ServerConnection.StreamReader.WriteResponseBody(BUFFER_SIZE, args.ProxyClient.ClientStream, args.WebSession.Response.IsChunked, args.WebSession.Response.ContentLength); || !args.WebSession.Response.ResponseKeepAlive)
{
await args.WebSession.ServerConnection.StreamReader
.WriteResponseBody(BUFFER_SIZE, args.ProxyClient.ClientStream, args.WebSession.Response.IsChunked,
args.WebSession.Response.ContentLength);
}
//write response if connection:keep-alive header exist and when version is http/1.0 //write response if connection:keep-alive header exist and when version is http/1.0
//Because in Http 1.0 server can return a response without content-length (expectation being client would read until end of stream) //Because in Http 1.0 server can return a response without content-length (expectation being client would read until end of stream)
else if (args.WebSession.Response.ResponseKeepAlive && args.WebSession.Response.HttpVersion.Minor == 0) else if (args.WebSession.Response.ResponseKeepAlive && args.WebSession.Response.HttpVersion.Minor == 0)
await args.WebSession.ServerConnection.StreamReader.WriteResponseBody(BUFFER_SIZE, args.ProxyClient.ClientStream, args.WebSession.Response.IsChunked, args.WebSession.Response.ContentLength); {
await args.WebSession.ServerConnection.StreamReader
.WriteResponseBody(BUFFER_SIZE, args.ProxyClient.ClientStream, args.WebSession.Response.IsChunked,
args.WebSession.Response.ContentLength);
}
} }
await args.ProxyClient.ClientStream.FlushAsync(); await args.ProxyClient.ClientStream.FlushAsync();
...@@ -98,7 +114,8 @@ namespace Titanium.Web.Proxy ...@@ -98,7 +114,8 @@ namespace Titanium.Web.Proxy
} }
catch catch
{ {
Dispose(args.ProxyClient.TcpClient, args.ProxyClient.ClientStream, args.ProxyClient.ClientStreamReader, args.ProxyClient.ClientStreamWriter, args); Dispose(args.ProxyClient.TcpClient, args.ProxyClient.ClientStream, args.ProxyClient.ClientStreamReader,
args.ProxyClient.ClientStreamWriter, args);
} }
finally finally
{ {
...@@ -112,7 +129,7 @@ namespace Titanium.Web.Proxy ...@@ -112,7 +129,7 @@ namespace Titanium.Web.Proxy
/// <param name="encodingType"></param> /// <param name="encodingType"></param>
/// <param name="responseBodyStream"></param> /// <param name="responseBodyStream"></param>
/// <returns></returns> /// <returns></returns>
private async Task<byte[]> GetCompressedResponseBody(string encodingType, byte[] responseBodyStream) private async Task<byte[]> GetCompressedResponseBody(string encodingType, byte[] responseBodyStream)
{ {
var compressionFactory = new CompressionFactory(); var compressionFactory = new CompressionFactory();
var compressor = compressionFactory.Create(encodingType); var compressor = compressionFactory.Create(encodingType);
...@@ -127,7 +144,7 @@ namespace Titanium.Web.Proxy ...@@ -127,7 +144,7 @@ namespace Titanium.Web.Proxy
/// <param name="description"></param> /// <param name="description"></param>
/// <param name="responseWriter"></param> /// <param name="responseWriter"></param>
/// <returns></returns> /// <returns></returns>
private async Task WriteResponseStatus(Version version, string code, string description, private async Task WriteResponseStatus(Version version, string code, string description,
StreamWriter responseWriter) StreamWriter responseWriter)
{ {
await responseWriter.WriteLineAsync(string.Format("HTTP/{0}.{1} {2} {3}", version.Major, version.Minor, code, description)); await responseWriter.WriteLineAsync(string.Format("HTTP/{0}.{1} {2} {3}", version.Major, version.Minor, code, description));
...@@ -139,43 +156,56 @@ namespace Titanium.Web.Proxy ...@@ -139,43 +156,56 @@ namespace Titanium.Web.Proxy
/// <param name="responseWriter"></param> /// <param name="responseWriter"></param>
/// <param name="headers"></param> /// <param name="headers"></param>
/// <returns></returns> /// <returns></returns>
private async Task WriteResponseHeaders(StreamWriter responseWriter, List<HttpHeader> headers) private async Task WriteResponseHeaders(StreamWriter responseWriter, Response response)
{ {
if (headers != null) FixProxyHeaders(response.ResponseHeaders);
foreach (var header in response.ResponseHeaders)
{ {
FixResponseProxyHeaders(headers); await responseWriter.WriteLineAsync(header.Value.ToString());
}
//write non unique request headers
foreach (var headerItem in response.NonUniqueResponseHeaders)
{
var headers = headerItem.Value;
foreach (var header in headers) foreach (var header in headers)
{ {
await responseWriter.WriteLineAsync(header.ToString()); await responseWriter.WriteLineAsync(header.ToString());
} }
} }
await responseWriter.WriteLineAsync(); await responseWriter.WriteLineAsync();
await responseWriter.FlushAsync(); await responseWriter.FlushAsync();
} }
/// <summary> /// <summary>
/// Fix the proxy specific headers before sending response headers to client /// Fix proxy specific headers
/// </summary> /// </summary>
/// <param name="headers"></param> /// <param name="headers"></param>
private void FixResponseProxyHeaders(List<HttpHeader> headers) private void FixProxyHeaders(Dictionary<string, HttpHeader> headers)
{ {
//If proxy-connection close was returned inform to close the connection //If proxy-connection close was returned inform to close the connection
var proxyHeader = headers.FirstOrDefault(x => x.Name.ToLower() == "proxy-connection"); var hasProxyHeader = headers.ContainsKey("proxy-connection");
var connectionHeader = headers.FirstOrDefault(x => x.Name.ToLower() == "connection"); var hasConnectionheader = headers.ContainsKey("connection");
if (proxyHeader != null) if (hasProxyHeader)
if (connectionHeader == null) {
var proxyHeader = headers["proxy-connection"];
if (hasConnectionheader == false)
{ {
headers.Add(new HttpHeader("connection", proxyHeader.Value)); headers.Add("connection", new HttpHeader("connection", proxyHeader.Value));
} }
else else
{ {
var connectionHeader = headers["connection"];
connectionHeader.Value = proxyHeader.Value; connectionHeader.Value = proxyHeader.Value;
} }
headers.RemoveAll(x => x.Name.ToLower() == "proxy-connection"); headers.Remove("proxy-connection");
}
} }
/// <summary> /// <summary>
...@@ -212,7 +242,6 @@ namespace Titanium.Web.Proxy ...@@ -212,7 +242,6 @@ namespace Titanium.Web.Proxy
if (clientStreamWriter != null) if (clientStreamWriter != null)
clientStreamWriter.Dispose(); clientStreamWriter.Dispose();
} }
} }
} }
\ No newline at end of file
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