Commit e27a4f92 authored by justcoding121's avatar justcoding121 Committed by GitHub

Merge pull request #96 from justcoding121/release

Peformance: Use header dictionary instead of List
parents 216b8c90 6453375e
...@@ -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,10 +209,14 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -195,10 +209,14 @@ 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>
/// Sets the body with the specified string /// Sets the body with the specified string
...@@ -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,10 +292,14 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -265,10 +292,14 @@ 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>
/// Replace the response body with the specified string /// Replace the response body with the specified string
...@@ -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,8 +113,10 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -107,8 +113,10 @@ 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
/// optionally chunked /// optionally chunked
...@@ -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,8 +162,10 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -150,8 +162,10 @@ namespace Titanium.Web.Proxy.Extensions
} }
} }
else else
{
await WriteResponseBodyChunked(inStreamReader, bufferSize, outStream); await WriteResponseBodyChunked(inStreamReader, bufferSize, outStream);
} }
}
/// <summary> /// <summary>
/// Copies the streams chunked /// Copies the streams chunked
......
...@@ -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,11 +27,15 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -27,11 +27,15 @@ 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>
/// Manage system proxy settings /// Manage system proxy settings
/// </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;
} }
......
...@@ -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);
...@@ -101,6 +114,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -101,6 +114,7 @@ namespace Titanium.Web.Proxy.Http
} }
} }
} }
}
/// <summary> /// <summary>
/// Receive & parse the http response from server /// Receive & parse the http response from server
...@@ -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()))
{
var header = tmpLine.Split(ProxyConstants.ColonSplit, 2);
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);
for (int index = 0; index < responseLines.Count; ++index) Response.NonUniqueResponseHeaders.Add(newHeader.Name, nonUniqueHeaders);
Response.ResponseHeaders.Remove(newHeader.Name);
}
else
{ {
string[] strArray = responseLines[index].Split(ProxyConstants.ColonSplit, 2); Response.ResponseHeaders.Add(newHeader.Name, newHeader);
this.Response.ResponseHeaders.Add(new HttpHeader(strArray[0], strArray[1])); }
} }
} }
} }
......
...@@ -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,12 +47,17 @@ namespace Titanium.Web.Proxy.Http ...@@ -45,12 +47,17 @@ namespace Titanium.Web.Proxy.Http
{ {
get get
{ {
var header = this.ResponseHeaders.FirstOrDefault(x => x.Name.ToLower().Equals("connection")); var hasHeader = ResponseHeaders.ContainsKey("connection");
if (hasHeader)
{
var header = ResponseHeaders["connection"];
if (header != null && header.Value.ToLower().Contains("close")) if (header.Value.ToLower().Contains("close"))
{ {
return false; 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,35 +146,46 @@ namespace Titanium.Web.Proxy.Http ...@@ -124,35 +146,46 @@ 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 (hasHeader)
{
var header = ResponseHeaders["transfer-encoding"];
if (header != null && header.Value.ToLower().Contains("chunked")) if (header.Value.ToLower().Contains("chunked"))
{ {
return true; return true;
} }
}
return false; return false;
} }
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,8 +114,10 @@ namespace Titanium.Web.Proxy.Network ...@@ -114,8 +114,10 @@ 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,14 +143,18 @@ namespace Titanium.Web.Proxy ...@@ -143,14 +143,18 @@ 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>
/// Remove a proxy end point /// Remove a proxy end point
...@@ -160,13 +164,17 @@ namespace Titanium.Web.Proxy ...@@ -160,13 +164,17 @@ 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>
/// Set the given explicit end point as the default proxy server for current machine /// Set the given explicit end point as the default proxy server for current machine
...@@ -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,11 +344,15 @@ namespace Titanium.Web.Proxy ...@@ -332,11 +344,15 @@ 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>
/// When a connection is received from client act /// When a connection is received from client act
...@@ -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();
} }
......
...@@ -49,9 +49,13 @@ namespace Titanium.Web.Proxy ...@@ -49,9 +49,13 @@ namespace Titanium.Web.Proxy
var httpVerb = httpCmdSplit[0]; var httpVerb = httpCmdSplit[0];
if (httpVerb.ToUpper() == "CONNECT") if (httpVerb.ToUpper() == "CONNECT")
{
httpRemoteUri = new Uri("http://" + httpCmdSplit[1]); httpRemoteUri = new Uri("http://" + httpCmdSplit[1]);
}
else else
{
httpRemoteUri = new Uri(httpCmdSplit[1]); httpRemoteUri = new Uri(httpCmdSplit[1]);
}
//parse the HTTP version //parse the HTTP version
Version version = new Version(1, 1); Version version = new Version(1, 1);
...@@ -103,7 +107,9 @@ namespace Titanium.Web.Proxy ...@@ -103,7 +107,9 @@ namespace Titanium.Web.Proxy
catch catch
{ {
if (sslStream != null) if (sslStream != null)
{
sslStream.Dispose(); sslStream.Dispose();
}
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, null); Dispose(client, clientStream, clientStreamReader, clientStreamWriter, null);
return; return;
...@@ -131,7 +137,7 @@ namespace Titanium.Web.Proxy ...@@ -131,7 +137,7 @@ namespace Titanium.Web.Proxy
//Now create the request //Now create the request
await HandleHttpSessionRequest(client, httpCmd, clientStream, clientStreamReader, clientStreamWriter, await HandleHttpSessionRequest(client, httpCmd, clientStream, clientStreamReader, clientStreamWriter,
httpRemoteUri.Scheme == Uri.UriSchemeHttps ? true : false); httpRemoteUri.Scheme == Uri.UriSchemeHttps ? httpRemoteUri.Host : null);
} }
catch catch
{ {
...@@ -169,7 +175,9 @@ namespace Titanium.Web.Proxy ...@@ -169,7 +175,9 @@ namespace Titanium.Web.Proxy
catch (Exception) catch (Exception)
{ {
if (sslStream != null) if (sslStream != null)
{
sslStream.Dispose(); sslStream.Dispose();
}
Dispose(tcpClient, sslStream, clientStreamReader, clientStreamWriter, null); Dispose(tcpClient, sslStream, clientStreamReader, clientStreamWriter, null);
return; return;
...@@ -186,7 +194,7 @@ namespace Titanium.Web.Proxy ...@@ -186,7 +194,7 @@ namespace Titanium.Web.Proxy
//Now create the request //Now create the request
await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamReader, clientStreamWriter, await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamReader, clientStreamWriter,
true); endPoint.EnableSsl ? endPoint.GenericCertificateName : null);
} }
/// <summary> /// <summary>
/// This is the core request handler method for a particular connection from client /// This is the core request handler method for a particular connection from client
...@@ -196,10 +204,10 @@ namespace Titanium.Web.Proxy ...@@ -196,10 +204,10 @@ namespace Titanium.Web.Proxy
/// <param name="clientStream"></param> /// <param name="clientStream"></param>
/// <param name="clientStreamReader"></param> /// <param name="clientStreamReader"></param>
/// <param name="clientStreamWriter"></param> /// <param name="clientStreamWriter"></param>
/// <param name="isHttps"></param> /// <param name="httpsHostName"></param>
/// <returns></returns> /// <returns></returns>
private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream, private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream,
CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, bool isHttps) CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, string httpsHostName)
{ {
TcpConnectionCache connection = null; TcpConnectionCache connection = null;
...@@ -235,26 +243,50 @@ namespace Titanium.Web.Proxy ...@@ -235,26 +243,50 @@ namespace Titanium.Web.Proxy
} }
} }
args.WebSession.Request.RequestHeaders = new List<HttpHeader>(); #if DEBUG
//Just ignore local requests while Debugging
//Its annoying
if (httpCmd.Contains("localhost"))
{
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, null);
break;
}
#endif
//Read the request headers //Read the request headers
string tmpLine; string tmpLine;
while (!string.IsNullOrEmpty(tmpLine = await clientStreamReader.ReadLineAsync())) while (!string.IsNullOrEmpty(tmpLine = await clientStreamReader.ReadLineAsync()))
{ {
var header = tmpLine.Split(ProxyConstants.ColonSplit, 2); var header = tmpLine.Split(ProxyConstants.ColonSplit, 2);
args.WebSession.Request.RequestHeaders.Add(new HttpHeader(header[0], header[1]));
var newHeader = new HttpHeader(header[0], header[1]);
if (args.WebSession.Request.NonUniqueRequestHeaders.ContainsKey(newHeader.Name))
{
args.WebSession.Request.NonUniqueRequestHeaders[newHeader.Name].Add(newHeader);
} }
else if (args.WebSession.Request.RequestHeaders.ContainsKey(newHeader.Name))
{
var existing = args.WebSession.Request.RequestHeaders[newHeader.Name];
var httpRemoteUri = new Uri(!isHttps ? httpCmdSplit[1] : (string.Concat("https://", args.WebSession.Request.Host, httpCmdSplit[1]))); var nonUniqueHeaders = new List<HttpHeader>();
#if DEBUG
//Just ignore local requests while Debugging nonUniqueHeaders.Add(existing);
//Its annoying nonUniqueHeaders.Add(newHeader);
if (httpRemoteUri.Host.Contains("localhost"))
args.WebSession.Request.NonUniqueRequestHeaders.Add(newHeader.Name, nonUniqueHeaders);
args.WebSession.Request.RequestHeaders.Remove(newHeader.Name);
}
else
{ {
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, null); args.WebSession.Request.RequestHeaders.Add(newHeader.Name, newHeader);
break;
} }
#endif }
var httpRemoteUri = new Uri(httpsHostName == null ? httpCmdSplit[1]
: (string.Concat("https://", args.WebSession.Request.Host == null ?
httpsHostName : args.WebSession.Request.Host, httpCmdSplit[1])));
args.WebSession.Request.RequestUri = httpRemoteUri; args.WebSession.Request.RequestUri = httpRemoteUri;
args.WebSession.Request.Method = httpMethod; args.WebSession.Request.Method = httpMethod;
...@@ -313,6 +345,7 @@ namespace Titanium.Web.Proxy ...@@ -313,6 +345,7 @@ namespace Titanium.Web.Proxy
//If 100 continue was the response inform that to the client //If 100 continue was the response inform that to the client
if (Enable100ContinueBehaviour) if (Enable100ContinueBehaviour)
{
if (args.WebSession.Request.Is100Continue) if (args.WebSession.Request.Is100Continue)
{ {
await WriteResponseStatus(args.WebSession.Response.HttpVersion, "100", await WriteResponseStatus(args.WebSession.Response.HttpVersion, "100",
...@@ -325,6 +358,7 @@ namespace Titanium.Web.Proxy ...@@ -325,6 +358,7 @@ namespace Titanium.Web.Proxy
"Expectation Failed", args.ProxyClient.ClientStreamWriter); "Expectation Failed", args.ProxyClient.ClientStreamWriter);
await args.ProxyClient.ClientStreamWriter.WriteLineAsync(); await args.ProxyClient.ClientStreamWriter.WriteLineAsync();
} }
}
//If expect continue is not enabled then set the connectio and send request headers //If expect continue is not enabled then set the connectio and send request headers
if (!args.WebSession.Request.ExpectContinue) if (!args.WebSession.Request.ExpectContinue)
...@@ -407,15 +441,17 @@ namespace Titanium.Web.Proxy ...@@ -407,15 +441,17 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
/// <param name="requestHeaders"></param> /// <param name="requestHeaders"></param>
/// <param name="webRequest"></param> /// <param name="webRequest"></param>
private void PrepareRequestHeaders(List<HttpHeader> requestHeaders, HttpWebClient webRequest) private void PrepareRequestHeaders(Dictionary<string, HttpHeader> requestHeaders, HttpWebClient webRequest)
{ {
for (var i = 0; i < requestHeaders.Count; i++) foreach (var headerItem in requestHeaders)
{ {
switch (requestHeaders[i].Name.ToLower()) var header = headerItem.Value;
switch (header.Name.ToLower())
{ {
//these are the only encoding this proxy can read //these are the only encoding this proxy can read
case "accept-encoding": case "accept-encoding":
requestHeaders[i].Value = "gzip,deflate,zlib"; header.Value = "gzip,deflate,zlib";
break; break;
default: default:
...@@ -423,32 +459,10 @@ namespace Titanium.Web.Proxy ...@@ -423,32 +459,10 @@ namespace Titanium.Web.Proxy
} }
} }
FixRequestProxyHeaders(requestHeaders); FixProxyHeaders(requestHeaders);
webRequest.Request.RequestHeaders = requestHeaders; webRequest.Request.RequestHeaders = requestHeaders;
} }
/// <summary>
/// Fix proxy specific headers
/// </summary>
/// <param name="headers"></param>
private void FixRequestProxyHeaders(List<HttpHeader> headers)
{
//If proxy-connection close was returned inform to close the connection
var proxyHeader = headers.FirstOrDefault(x => x.Name.ToLower() == "proxy-connection");
var connectionheader = headers.FirstOrDefault(x => x.Name.ToLower() == "connection");
if (proxyHeader != null)
if (connectionheader == null)
{
headers.Add(new HttpHeader("connection", proxyHeader.Value));
}
else
{
connectionheader.Value = proxyHeader.Value;
}
headers.RemoveAll(x => x.Name.ToLower() == "proxy-connection");
}
/// <summary> /// <summary>
/// This is called when the request is PUT/POST to read the body /// This is called when the request is PUT/POST to read the body
......
...@@ -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
{ {
...@@ -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
{ {
...@@ -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)
{ {
headers.Add(new HttpHeader("connection", proxyHeader.Value)); var proxyHeader = headers["proxy-connection"];
if (hasConnectionheader == false)
{
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