Commit 34a68075 authored by justcoding121's avatar justcoding121

Asyncify fixes

parent 03205075
...@@ -12,9 +12,9 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -12,9 +12,9 @@ namespace Titanium.Web.Proxy.Examples.Basic
{ {
public void StartProxy() public void StartProxy()
{ {
ProxyServer.BeforeRequest += OnRequest; // ProxyServer.BeforeRequest += OnRequest;
ProxyServer.BeforeResponse += OnResponse; // ProxyServer.BeforeResponse += OnResponse;
ProxyServer.ServerCertificateValidationCallback += OnCertificateValidation; // ProxyServer.ServerCertificateValidationCallback += OnCertificateValidation;
//Exclude Https addresses you don't want to proxy //Exclude Https addresses you don't want to proxy
//Usefull for clients that use certificate pinning //Usefull for clients that use certificate pinning
...@@ -112,7 +112,7 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -112,7 +112,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
{ {
if (e.WebSession.Response.ResponseStatusCode == "200") if (e.WebSession.Response.ResponseStatusCode == "200")
{ {
if (e.WebSession.Response.ContentType.Trim().ToLower().Contains("text/html")) if (e.WebSession.Response.ContentType!=null && e.WebSession.Response.ContentType.Trim().ToLower().Contains("text/html"))
{ {
byte[] bodyBytes = await e.GetResponseBody(); byte[] bodyBytes = await e.GetResponseBody();
await e.SetResponseBody(bodyBytes); await e.SetResponseBody(bodyBytes);
......
...@@ -136,7 +136,7 @@ Sample request and response event handlers ...@@ -136,7 +136,7 @@ Sample request and response event handlers
{ {
if (e.WebSession.Response.ResponseStatusCode == "200") if (e.WebSession.Response.ResponseStatusCode == "200")
{ {
if (e.WebSession.Response.ContentType.Trim().ToLower().Contains("text/html")) if (e.WebSession.Response.ContentType!=null && e.WebSession.Response.ContentType.Trim().ToLower().Contains("text/html"))
{ {
byte[] bodyBytes = await e.GetResponseBody(); byte[] bodyBytes = await e.GetResponseBody();
await e.SetResponseBody(bodyBytes); await e.SetResponseBody(bodyBytes);
...@@ -148,11 +148,8 @@ Sample request and response event handlers ...@@ -148,11 +148,8 @@ Sample request and response event handlers
} }
} }
/// <summary>
/// Allows overriding default certificate validation logic /// Allows overriding default certificate validation logic
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public async Task OnCertificateValidation(object sender, CertificateValidationEventArgs e) public async Task OnCertificateValidation(object sender, CertificateValidationEventArgs e)
{ {
//set IsValid to true/false based on Certificate Errors //set IsValid to true/false based on Certificate Errors
......
...@@ -7,6 +7,7 @@ using Titanium.Web.Proxy.Http; ...@@ -7,6 +7,7 @@ using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Http.Responses; using Titanium.Web.Proxy.Http.Responses;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.Network;
namespace Titanium.Web.Proxy.EventArguments namespace Titanium.Web.Proxy.EventArguments
{ {
...@@ -86,14 +87,17 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -86,14 +87,17 @@ namespace Titanium.Web.Proxy.EventArguments
await this.Client.ClientStreamReader.CopyBytesToStream(requestBodyStream, WebSession.Request.ContentLength).ConfigureAwait(false); await this.Client.ClientStreamReader.CopyBytesToStream(requestBodyStream, WebSession.Request.ContentLength).ConfigureAwait(false);
} }
else if (WebSession.Request.HttpVersion.ToLower().Trim().Equals("http/1.0"))
await WebSession.ServerConnection.StreamReader.CopyBytesToStream(requestBodyStream, long.MaxValue).ConfigureAwait(false);
} }
WebSession.Request.RequestBody = await GetDecompressedResponseBody(WebSession.Request.ContentEncoding, requestBodyStream.ToArray()).ConfigureAwait(false); WebSession.Request.RequestBody = await GetDecompressedResponseBody(WebSession.Request.ContentEncoding, requestBodyStream.ToArray()).ConfigureAwait(false);
} }
}
//Now set the flag to true //Now set the flag to true
//So that next time we can deliver body from cache //So that next time we can deliver body from cache
WebSession.Request.RequestBodyRead = true; WebSession.Request.RequestBodyRead = true;
}
} }
/// <summary> /// <summary>
...@@ -109,16 +113,18 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -109,16 +113,18 @@ namespace Titanium.Web.Proxy.EventArguments
//If chuncked the read chunk by chunk until we hit chunk end symbol //If chuncked the read chunk by chunk until we hit chunk end symbol
if (WebSession.Response.IsChunked) if (WebSession.Response.IsChunked)
{ {
await WebSession.ProxyClient.ServerStreamReader.CopyBytesToStreamChunked(responseBodyStream).ConfigureAwait(false); await WebSession.ServerConnection.StreamReader.CopyBytesToStreamChunked(responseBodyStream).ConfigureAwait(false);
} }
else else
{ {
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.ProxyClient.ServerStreamReader.CopyBytesToStream(responseBodyStream, WebSession.Response.ContentLength).ConfigureAwait(false); await WebSession.ServerConnection.StreamReader.CopyBytesToStream(responseBodyStream, WebSession.Response.ContentLength).ConfigureAwait(false);
} }
else if(WebSession.Response.HttpVersion.ToLower().Trim().Equals("http/1.0"))
await WebSession.ServerConnection.StreamReader.CopyBytesToStream(responseBodyStream, long.MaxValue).ConfigureAwait(false);
} }
WebSession.Response.ResponseBody = await GetDecompressedResponseBody(WebSession.Response.ContentEncoding, responseBodyStream.ToArray()).ConfigureAwait(false); WebSession.Response.ResponseBody = await GetDecompressedResponseBody(WebSession.Response.ContentEncoding, responseBodyStream.ToArray()).ConfigureAwait(false);
...@@ -173,7 +179,11 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -173,7 +179,11 @@ namespace Titanium.Web.Proxy.EventArguments
} }
WebSession.Request.RequestBody = body; WebSession.Request.RequestBody = body;
WebSession.Request.RequestBodyRead = true;
if (WebSession.Request.IsChunked == false)
WebSession.Request.ContentLength = body.Length;
else
WebSession.Request.ContentLength = -1;
} }
/// <summary> /// <summary>
...@@ -191,13 +201,8 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -191,13 +201,8 @@ namespace Titanium.Web.Proxy.EventArguments
await ReadRequestBody().ConfigureAwait(false); await ReadRequestBody().ConfigureAwait(false);
} }
WebSession.Request.RequestBody = WebSession.Request.Encoding.GetBytes(body); await SetRequestBody(WebSession.Request.Encoding.GetBytes(body));
//If there is a content length header update it
if (!WebSession.Request.IsChunked)
WebSession.Request.ContentLength = body.Length;
WebSession.Request.RequestBodyRead = true;
} }
/// <summary> /// <summary>
...@@ -245,9 +250,10 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -245,9 +250,10 @@ namespace Titanium.Web.Proxy.EventArguments
WebSession.Response.ResponseBody = body; WebSession.Response.ResponseBody = body;
//If there is a content length header update it //If there is a content length header update it
if (!WebSession.Response.IsChunked) if (WebSession.Response.IsChunked == false)
WebSession.Response.ContentLength = body.Length; WebSession.Response.ContentLength = body.Length;
else
WebSession.Response.ContentLength = -1;
} }
/// <summary> /// <summary>
...@@ -266,8 +272,9 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -266,8 +272,9 @@ namespace Titanium.Web.Proxy.EventArguments
} }
var bodyBytes = WebSession.Response.Encoding.GetBytes(body); var bodyBytes = WebSession.Response.Encoding.GetBytes(body);
await SetResponseBody(bodyBytes).ConfigureAwait(false); await SetResponseBody(bodyBytes).ConfigureAwait(false);
} }
private async Task<byte[]> GetDecompressedResponseBody(string encodingType, byte[] responseBodyStream) private async Task<byte[]> GetDecompressedResponseBody(string encodingType, byte[] responseBodyStream)
{ {
......
...@@ -15,7 +15,8 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -15,7 +15,8 @@ namespace Titanium.Web.Proxy.Extensions
try try
{ {
//return default if not specified //return default if not specified
if (request.ContentType == null) return Encoding.GetEncoding("ISO-8859-1"); if (request.ContentType == null)
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(Constants.SemiColonSplit); var contentTypes = request.ContentType.Split(Constants.SemiColonSplit);
......
using System.Text; using System.Text;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Extensions namespace Titanium.Web.Proxy.Extensions
{ {
public static class HttpWebResponseExtensions public static class HttpWebResponseExtensions
{ {
public static Encoding GetResponseEncoding(this Response response) public static Encoding GetResponseCharacterEncoding(this Response response)
{ {
if (string.IsNullOrEmpty(response.CharacterSet))
return Encoding.GetEncoding("ISO-8859-1");
try try
{ {
return Encoding.GetEncoding(response.CharacterSet.Replace(@"""", string.Empty)); //return default if not specified
if (response.ContentType == null)
return Encoding.GetEncoding("ISO-8859-1");
//extract the encoding by finding the charset
var contentTypes = response.ContentType.Split(Constants.SemiColonSplit);
foreach (var contentType in contentTypes)
{
var encodingSplit = contentType.Split('=');
if (encodingSplit.Length == 2 && encodingSplit[0].ToLower().Trim() == "charset")
{
return Encoding.GetEncoding(encodingSplit[1]);
}
}
} }
catch { return Encoding.GetEncoding("ISO-8859-1"); } catch
{
//parsing errors
// ignored
}
//return default if not specified
return Encoding.GetEncoding("ISO-8859-1");
} }
} }
} }
\ No newline at end of file
...@@ -20,25 +20,29 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -20,25 +20,29 @@ namespace Titanium.Web.Proxy.Extensions
await input.CopyToAsync(output); await input.CopyToAsync(output);
} }
internal static async Task CopyBytesToStream(this CustomBinaryReader clientStreamReader, Stream stream, long totalBytesToRead) internal static async Task CopyBytesToStream(this CustomBinaryReader streamReader, Stream stream, long totalBytesToRead)
{ {
var totalbytesRead = 0; var totalbytesRead = 0;
int bytesToRead; long bytesToRead;
if (totalBytesToRead < Constants.BUFFER_SIZE) if (totalBytesToRead < Constants.BUFFER_SIZE)
{ {
bytesToRead = (int)totalBytesToRead; bytesToRead = totalBytesToRead;
} }
else else
bytesToRead = Constants.BUFFER_SIZE; bytesToRead = Constants.BUFFER_SIZE;
while (totalbytesRead < (int)totalBytesToRead) while (totalbytesRead < totalBytesToRead)
{ {
var buffer = await clientStreamReader.ReadBytesAsync(bytesToRead); var buffer = await streamReader.ReadBytesAsync(bytesToRead);
if (buffer.Length == 0)
break;
totalbytesRead += buffer.Length; totalbytesRead += buffer.Length;
var remainingBytes = (int)totalBytesToRead - totalbytesRead; var remainingBytes = totalBytesToRead - totalbytesRead;
if (remainingBytes < bytesToRead) if (remainingBytes < bytesToRead)
{ {
bytesToRead = remainingBytes; bytesToRead = remainingBytes;
......
...@@ -4,15 +4,18 @@ using System.Diagnostics; ...@@ -4,15 +4,18 @@ using System.Diagnostics;
using System.Collections.Generic; using System.Collections.Generic;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks;
using System.Threading;
namespace Titanium.Web.Proxy.Helpers namespace Titanium.Web.Proxy.Helpers
{ {
public class CertificateManager : IDisposable public class CertificateManager : IDisposable
{ {
private const string CertCreateFormat = private const string CertCreateFormat =
"-ss {0} -n \"CN={1}, O={2}\" -sky {3} -cy {4} -m 120 -a sha256 -eku 1.3.6.1.5.5.7.3.1 {5}"; "-ss {0} -n \"CN={1}, O={2}\" -sky {3} -cy {4} -m 120 -a sha256 -eku 1.3.6.1.5.5.7.3.1 {5}";
private readonly IDictionary<string, X509Certificate2> _certificateCache; private readonly IDictionary<string, X509Certificate2> _certificateCache;
private static SemaphoreSlim semaphoreLock = new SemaphoreSlim(1);
public string Issuer { get; private set; } public string Issuer { get; private set; }
public string RootCertificateName { get; private set; } public string RootCertificateName { get; private set; }
...@@ -35,10 +38,10 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -35,10 +38,10 @@ namespace Titanium.Web.Proxy.Helpers
/// Attempts to move a self-signed certificate to the root store. /// Attempts to move a self-signed certificate to the root store.
/// </summary> /// </summary>
/// <returns>true if succeeded, else false</returns> /// <returns>true if succeeded, else false</returns>
public bool CreateTrustedRootCertificate() public async Task<bool> CreateTrustedRootCertificate()
{ {
X509Certificate2 rootCertificate = X509Certificate2 rootCertificate =
CreateCertificate(RootStore, RootCertificateName); await CreateCertificate(RootStore, RootCertificateName);
return rootCertificate != null; return rootCertificate != null;
} }
...@@ -46,9 +49,9 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -46,9 +49,9 @@ namespace Titanium.Web.Proxy.Helpers
/// Attempts to remove the self-signed certificate from the root store. /// Attempts to remove the self-signed certificate from the root store.
/// </summary> /// </summary>
/// <returns>true if succeeded, else false</returns> /// <returns>true if succeeded, else false</returns>
public bool DestroyTrustedRootCertificate() public async Task<bool> DestroyTrustedRootCertificate()
{ {
return DestroyCertificate(RootStore, RootCertificateName); return await DestroyCertificate(RootStore, RootCertificateName);
} }
public X509Certificate2Collection FindCertificates(string certificateSubject) public X509Certificate2Collection FindCertificates(string certificateSubject)
...@@ -64,103 +67,126 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -64,103 +67,126 @@ namespace Titanium.Web.Proxy.Helpers
discoveredCertificates : null; discoveredCertificates : null;
} }
public X509Certificate2 CreateCertificate(string certificateName) public async Task<X509Certificate2> CreateCertificate(string certificateName)
{ {
return CreateCertificate(MyStore, certificateName); return await CreateCertificate(MyStore, certificateName);
} }
protected virtual X509Certificate2 CreateCertificate(X509Store store, string certificateName) protected async virtual Task<X509Certificate2> CreateCertificate(X509Store store, string certificateName)
{ {
if (_certificateCache.ContainsKey(certificateName)) if (_certificateCache.ContainsKey(certificateName))
return _certificateCache[certificateName]; return _certificateCache[certificateName];
lock (store) await semaphoreLock.WaitAsync();
X509Certificate2 certificate = null;
try
{ {
X509Certificate2 certificate = null; store.Open(OpenFlags.ReadWrite);
try string certificateSubject = string.Format("CN={0}, O={1}", certificateName, Issuer);
{
store.Open(OpenFlags.ReadWrite);
string certificateSubject = string.Format("CN={0}, O={1}", certificateName, Issuer);
var certificates = var certificates =
FindCertificates(store, certificateSubject); FindCertificates(store, certificateSubject);
if (certificates != null) if (certificates != null)
certificate = certificates[0]; certificate = certificates[0];
if (certificate == null) if (certificate == null)
{ {
string[] args = new[] { string[] args = new[] {
GetCertificateCreateArgs(store, certificateName) }; GetCertificateCreateArgs(store, certificateName) };
CreateCertificate(args); await CreateCertificate(args);
certificates = FindCertificates(store, certificateSubject); certificates = FindCertificates(store, certificateSubject);
return certificates != null ? return certificates != null ?
certificates[0] : null; certificates[0] : null;
}
return certificate;
}
finally
{
store.Close();
if (certificate != null && !_certificateCache.ContainsKey(certificateName))
_certificateCache.Add(certificateName, certificate);
} }
store.Close();
if (certificate != null && !_certificateCache.ContainsKey(certificateName))
_certificateCache.Add(certificateName, certificate);
return certificate;
}
finally
{
semaphoreLock.Release();
} }
} }
protected virtual void CreateCertificate(string[] args) protected virtual Task<int> CreateCertificate(string[] args)
{ {
using (var process = new Process())
{
string file = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "makecert.exe");
if (!File.Exists(file)) // there is no non-generic TaskCompletionSource
throw new Exception("Unable to locate 'makecert.exe'."); var tcs = new TaskCompletionSource<int>();
var process = new Process();
process.StartInfo.Verb = "runas"; string file = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "makecert.exe");
process.StartInfo.Arguments = args != null ? args[0] : string.Empty;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = file;
process.Start(); if (!File.Exists(file))
process.WaitForExit(); throw new Exception("Unable to locate 'makecert.exe'.");
process.StartInfo.Verb = "runas";
process.StartInfo.Arguments = args != null ? args[0] : string.Empty;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = file;
process.EnableRaisingEvents = true;
process.Exited += (sender, processArgs) =>
{
tcs.SetResult(process.ExitCode);
process.Dispose();
};
bool started = process.Start();
if (!started)
{
//you may allow for the process to be re-used (started = false)
//but I'm not sure about the guarantees of the Exited event in such a case
throw new InvalidOperationException("Could not start process: " + process);
} }
return tcs.Task;
} }
public bool DestroyCertificate(string certificateName) public async Task<bool> DestroyCertificate(string certificateName)
{ {
return DestroyCertificate(MyStore, certificateName); return await DestroyCertificate(MyStore, certificateName);
} }
protected virtual bool DestroyCertificate(X509Store store, string certificateName) protected virtual async Task<bool> DestroyCertificate(X509Store store, string certificateName)
{ {
lock (store) await semaphoreLock.WaitAsync();
X509Certificate2Collection certificates = null;
try
{ {
X509Certificate2Collection certificates = null; store.Open(OpenFlags.ReadWrite);
try string certificateSubject = string.Format("CN={0}, O={1}", certificateName, Issuer);
{
store.Open(OpenFlags.ReadWrite);
string certificateSubject = string.Format("CN={0}, O={1}", certificateName, Issuer);
certificates = FindCertificates(store, certificateSubject);
if (certificates != null)
{
store.RemoveRange(certificates);
certificates = FindCertificates(store, certificateSubject); certificates = FindCertificates(store, certificateSubject);
if (certificates != null)
{
store.RemoveRange(certificates);
certificates = FindCertificates(store, certificateSubject);
}
return certificates == null;
} }
finally
store.Close();
if (certificates == null &&
_certificateCache.ContainsKey(certificateName))
{ {
store.Close(); _certificateCache.Remove(certificateName);
if (certificates == null &&
_certificateCache.ContainsKey(certificateName))
{
_certificateCache.Remove(certificateName);
}
} }
return certificates == null;
} }
finally
{
semaphoreLock.Release();
}
} }
protected virtual string GetCertificateCreateArgs(X509Store store, string certificateName) protected virtual string GetCertificateCreateArgs(X509Store store, string certificateName)
......
...@@ -11,7 +11,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -11,7 +11,7 @@ namespace Titanium.Web.Proxy.Http
{ {
public class HttpWebSession public class HttpWebSession
{ {
internal TcpConnection ProxyClient { get; set; } internal TcpConnection ServerConnection { get; set; }
public Request Request { get; set; } public Request Request { get; set; }
public Response Response { get; set; } public Response Response { get; set; }
...@@ -27,7 +27,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -27,7 +27,7 @@ namespace Titanium.Web.Proxy.Http
internal void SetConnection(TcpConnection Connection) internal void SetConnection(TcpConnection Connection)
{ {
Connection.LastAccess = DateTime.Now; Connection.LastAccess = DateTime.Now;
ProxyClient = Connection; ServerConnection = Connection;
} }
internal HttpWebSession() internal HttpWebSession()
...@@ -38,7 +38,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -38,7 +38,7 @@ namespace Titanium.Web.Proxy.Http
internal async Task SendRequest() internal async Task SendRequest()
{ {
Stream stream = ProxyClient.Stream; Stream stream = ServerConnection.Stream;
StringBuilder requestLines = new StringBuilder(); StringBuilder requestLines = new StringBuilder();
...@@ -59,12 +59,12 @@ namespace Titanium.Web.Proxy.Http ...@@ -59,12 +59,12 @@ namespace Titanium.Web.Proxy.Http
string request = requestLines.ToString(); string request = requestLines.ToString();
byte[] requestBytes = Encoding.ASCII.GetBytes(request); byte[] requestBytes = Encoding.ASCII.GetBytes(request);
await stream.WriteAsync(requestBytes, 0, requestBytes.Length); await stream.WriteAsync(requestBytes, 0, requestBytes.Length);
stream.Flush(); await stream.FlushAsync();
if (ProxyServer.Enable100ContinueBehaviour) if (ProxyServer.Enable100ContinueBehaviour)
if (this.Request.ExpectContinue) if (this.Request.ExpectContinue)
{ {
var httpResult = (await ProxyClient.ServerStreamReader.ReadLineAsync()).Split(Constants.SpaceSplit, 3); var httpResult = (await ServerConnection.StreamReader.ReadLineAsync()).Split(Constants.SpaceSplit, 3);
var responseStatusCode = httpResult[1].Trim(); var responseStatusCode = httpResult[1].Trim();
var responseStatusDescription = httpResult[2].Trim(); var responseStatusDescription = httpResult[2].Trim();
...@@ -73,13 +73,13 @@ namespace Titanium.Web.Proxy.Http ...@@ -73,13 +73,13 @@ namespace Titanium.Web.Proxy.Http
&& responseStatusDescription.ToLower().Equals("continue")) && responseStatusDescription.ToLower().Equals("continue"))
{ {
this.Request.Is100Continue = true; this.Request.Is100Continue = true;
await ProxyClient.ServerStreamReader.ReadLineAsync().ConfigureAwait(false); await ServerConnection.StreamReader.ReadLineAsync().ConfigureAwait(false);
} }
else if (responseStatusCode.Equals("417") else if (responseStatusCode.Equals("417")
&& responseStatusDescription.ToLower().Equals("expectation failed")) && responseStatusDescription.ToLower().Equals("expectation failed"))
{ {
this.Request.ExpectationFailed = true; this.Request.ExpectationFailed = true;
await ProxyClient.ServerStreamReader.ReadLineAsync().ConfigureAwait(false); await ServerConnection.StreamReader.ReadLineAsync().ConfigureAwait(false);
} }
} }
} }
...@@ -89,11 +89,11 @@ namespace Titanium.Web.Proxy.Http ...@@ -89,11 +89,11 @@ namespace Titanium.Web.Proxy.Http
//return if this is already read //return if this is already read
if (this.Response.ResponseStatusCode != null) return; if (this.Response.ResponseStatusCode != null) return;
var httpResult = (await ProxyClient.ServerStreamReader.ReadLineAsync()).Split(Constants.SpaceSplit, 3); var httpResult = (await ServerConnection.StreamReader.ReadLineAsync()).Split(Constants.SpaceSplit, 3);
if (string.IsNullOrEmpty(httpResult[0])) if (string.IsNullOrEmpty(httpResult[0]))
{ {
await ProxyClient.ServerStreamReader.ReadLineAsync().ConfigureAwait(false); await ServerConnection.StreamReader.ReadLineAsync().ConfigureAwait(false);
} }
this.Response.HttpVersion = httpResult[0].Trim(); this.Response.HttpVersion = httpResult[0].Trim();
...@@ -106,7 +106,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -106,7 +106,7 @@ namespace Titanium.Web.Proxy.Http
{ {
this.Response.Is100Continue = true; this.Response.Is100Continue = true;
this.Response.ResponseStatusCode = null; this.Response.ResponseStatusCode = null;
await ProxyClient.ServerStreamReader.ReadLineAsync().ConfigureAwait(false); await ServerConnection.StreamReader.ReadLineAsync().ConfigureAwait(false);
await ReceiveResponse(); await ReceiveResponse();
return; return;
} }
...@@ -115,12 +115,12 @@ namespace Titanium.Web.Proxy.Http ...@@ -115,12 +115,12 @@ namespace Titanium.Web.Proxy.Http
{ {
this.Response.ExpectationFailed = true; this.Response.ExpectationFailed = true;
this.Response.ResponseStatusCode = null; this.Response.ResponseStatusCode = null;
await ProxyClient.ServerStreamReader.ReadLineAsync().ConfigureAwait(false); await ServerConnection.StreamReader.ReadLineAsync().ConfigureAwait(false);
await ReceiveResponse(); await ReceiveResponse();
return; return;
} }
List<string> responseLines = await ProxyClient.ServerStreamReader.ReadAllLinesAsync().ConfigureAwait(false); List<string> responseLines = await ServerConnection.StreamReader.ReadAllLinesAsync().ConfigureAwait(false);
for (int index = 0; index < responseLines.Count; ++index) for (int index = 0; index < responseLines.Count; ++index)
{ {
......
...@@ -13,22 +13,9 @@ namespace Titanium.Web.Proxy.Http ...@@ -13,22 +13,9 @@ namespace Titanium.Web.Proxy.Http
public string ResponseStatusCode { get; set; } public string ResponseStatusCode { get; set; }
public string ResponseStatusDescription { get; set; } public string ResponseStatusDescription { get; set; }
internal Encoding Encoding { get { return this.GetResponseEncoding(); } } internal Encoding Encoding { get { return this.GetResponseCharacterEncoding(); } }
internal string CharacterSet
{
get
{
if (this.ContentType.Contains(";"))
{
return this.ContentType.Split(';')[1].Substring(9).Trim();
}
return null;
}
}
internal string ContentEncoding internal string ContentEncoding
{ {
get get
...@@ -69,13 +56,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -69,13 +56,7 @@ namespace Titanium.Web.Proxy.Http
if (header != null) if (header != null)
{ {
if (header.Value.Contains(";")) return header.Value;
{
return header.Value.Split(';')[0].Trim();
}
else
return header.Value.ToLower().Trim();
} }
return null; return null;
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
using System.Net.Sockets; using System.Net.Sockets;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
namespace Titanium.Web.Proxy.EventArguments namespace Titanium.Web.Proxy.Network
{ {
/// <summary> /// <summary>
/// This class wraps Tcp connection to Server /// This class wraps Tcp connection to Server
......
...@@ -23,7 +23,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -23,7 +23,7 @@ namespace Titanium.Web.Proxy.Network
internal Version Version { get; set; } internal Version Version { get; set; }
internal TcpClient TcpClient { get; set; } internal TcpClient TcpClient { get; set; }
internal CustomBinaryReader ServerStreamReader { get; set; } internal CustomBinaryReader StreamReader { get; set; }
internal Stream Stream { get; set; } internal Stream Stream { get; set; }
internal DateTime LastAccess { get; set; } internal DateTime LastAccess { get; set; }
...@@ -88,21 +88,25 @@ namespace Titanium.Web.Proxy.Network ...@@ -88,21 +88,25 @@ namespace Titanium.Web.Proxy.Network
client = new TcpClient(ProxyServer.UpStreamHttpsProxy.HostName, ProxyServer.UpStreamHttpsProxy.Port); client = new TcpClient(ProxyServer.UpStreamHttpsProxy.HostName, ProxyServer.UpStreamHttpsProxy.Port);
stream = (Stream)client.GetStream(); stream = (Stream)client.GetStream();
var writer = new StreamWriter(stream, Encoding.ASCII, Constants.BUFFER_SIZE, true); using (var writer = new StreamWriter(stream, Encoding.ASCII, Constants.BUFFER_SIZE, true))
{
writer.WriteLine(string.Format("CONNECT {0}:{1} {2}", sessionArgs.WebSession.Request.RequestUri.Host, sessionArgs.WebSession.Request.RequestUri.Port, sessionArgs.WebSession.Request.HttpVersion)); await writer.WriteLineAsync(string.Format("CONNECT {0}:{1} {2}", sessionArgs.WebSession.Request.RequestUri.Host, sessionArgs.WebSession.Request.RequestUri.Port, sessionArgs.WebSession.Request.HttpVersion));
writer.WriteLine(string.Format("Host: {0}:{1}", sessionArgs.WebSession.Request.RequestUri.Host, sessionArgs.WebSession.Request.RequestUri.Port)); await writer.WriteLineAsync(string.Format("Host: {0}:{1}", sessionArgs.WebSession.Request.RequestUri.Host, sessionArgs.WebSession.Request.RequestUri.Port));
writer.WriteLine("Connection: Keep-Alive"); await writer.WriteLineAsync("Connection: Keep-Alive");
writer.WriteLine(); await writer.WriteLineAsync();
writer.Flush(); await writer.FlushAsync();
writer.Close();
var reader = new CustomBinaryReader(stream); }
var result = await reader.ReadLineAsync().ConfigureAwait(false);
using (var reader = new CustomBinaryReader(stream))
if (!result.ToLower().Contains("200 connection established")) {
throw new Exception("Upstream proxy failed to create a secure tunnel"); var result = await reader.ReadLineAsync().ConfigureAwait(false);
await reader.ReadAllLinesAsync().ConfigureAwait(false); if (!result.ToLower().Contains("200 connection established"))
throw new Exception("Upstream proxy failed to create a secure tunnel");
await reader.ReadAllLinesAsync().ConfigureAwait(false);
}
} }
else else
{ {
...@@ -144,7 +148,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -144,7 +148,7 @@ namespace Titanium.Web.Proxy.Network
port = port, port = port,
IsSecure = isSecure, IsSecure = isSecure,
TcpClient = client, TcpClient = client,
ServerStreamReader = new CustomBinaryReader(stream), StreamReader = new CustomBinaryReader(stream),
Stream = stream, Stream = stream,
Version = version Version = version
}; };
...@@ -178,6 +182,5 @@ namespace Titanium.Web.Proxy.Network ...@@ -178,6 +182,5 @@ namespace Titanium.Web.Proxy.Network
} }
} }
} }
...@@ -154,7 +154,7 @@ namespace Titanium.Web.Proxy ...@@ -154,7 +154,7 @@ namespace Titanium.Web.Proxy
CertManager = new CertificateManager(RootCertificateIssuerName, CertManager = new CertificateManager(RootCertificateIssuerName,
RootCertificateName); RootCertificateName);
certTrusted = CertManager.CreateTrustedRootCertificate(); certTrusted = CertManager.CreateTrustedRootCertificate().Result;
foreach (var endPoint in ProxyEndPoints) foreach (var endPoint in ProxyEndPoints)
{ {
...@@ -222,18 +222,21 @@ namespace Titanium.Web.Proxy ...@@ -222,18 +222,21 @@ namespace Titanium.Web.Proxy
{ {
var client = endPoint.listener.EndAcceptTcpClient(asyn); var client = endPoint.listener.EndAcceptTcpClient(asyn);
if (endPoint.GetType() == typeof(TransparentProxyEndPoint)) if (endPoint.GetType() == typeof(TransparentProxyEndPoint))
Task.Factory.StartNew(() => HandleClient(endPoint as TransparentProxyEndPoint, client)); HandleClient(endPoint as TransparentProxyEndPoint, client);
else else
Task.Factory.StartNew(() => HandleClient(endPoint as ExplicitProxyEndPoint, client)); HandleClient(endPoint as ExplicitProxyEndPoint, client);
// Get the listener that handles the client request. // Get the listener that handles the client request.
endPoint.listener.BeginAcceptTcpClient(OnAcceptConnection, endPoint); endPoint.listener.BeginAcceptTcpClient(OnAcceptConnection, endPoint);
} }
catch catch (ObjectDisposedException)
{ {
// ignored // The listener was Stop()'d, disposing the underlying socket and
// triggering the completion of the callback. We're already exiting,
// so just return.
return;
} }
} }
} }
......
...@@ -67,7 +67,7 @@ namespace Titanium.Web.Proxy ...@@ -67,7 +67,7 @@ namespace Titanium.Web.Proxy
await WriteConnectResponse(clientStreamWriter, httpVersion).ConfigureAwait(false); await WriteConnectResponse(clientStreamWriter, httpVersion).ConfigureAwait(false);
var certificate = CertManager.CreateCertificate(httpRemoteUri.Host); var certificate = await CertManager.CreateCertificate(httpRemoteUri.Host);
SslStream sslStream = null; SslStream sslStream = null;
...@@ -139,7 +139,7 @@ namespace Titanium.Web.Proxy ...@@ -139,7 +139,7 @@ namespace Titanium.Web.Proxy
// certificate = CertManager.CreateCertificate(hostName); // certificate = CertManager.CreateCertificate(hostName);
//} //}
//else //else
certificate = CertManager.CreateCertificate(endPoint.GenericCertificateName); certificate = await CertManager.CreateCertificate(endPoint.GenericCertificateName);
try try
{ {
...@@ -281,13 +281,13 @@ namespace Titanium.Web.Proxy ...@@ -281,13 +281,13 @@ namespace Titanium.Web.Proxy
{ {
WriteResponseStatus(args.WebSession.Response.HttpVersion, "100", WriteResponseStatus(args.WebSession.Response.HttpVersion, "100",
"Continue", args.Client.ClientStreamWriter); "Continue", args.Client.ClientStreamWriter);
args.Client.ClientStreamWriter.WriteLine(); await args.Client.ClientStreamWriter.WriteLineAsync();
} }
else if (args.WebSession.Request.ExpectationFailed) else if (args.WebSession.Request.ExpectationFailed)
{ {
WriteResponseStatus(args.WebSession.Response.HttpVersion, "417", WriteResponseStatus(args.WebSession.Response.HttpVersion, "417",
"Expectation Failed", args.Client.ClientStreamWriter); "Expectation Failed", args.Client.ClientStreamWriter);
args.Client.ClientStreamWriter.WriteLine(); await args.Client.ClientStreamWriter.WriteLineAsync();
} }
if (!args.WebSession.Request.ExpectContinue) if (!args.WebSession.Request.ExpectContinue)
...@@ -299,8 +299,14 @@ namespace Titanium.Web.Proxy ...@@ -299,8 +299,14 @@ namespace Titanium.Web.Proxy
//If request was modified by user //If request was modified by user
if (args.WebSession.Request.RequestBodyRead) if (args.WebSession.Request.RequestBodyRead)
{ {
if(args.WebSession.Request.ContentEncoding!=null)
{
args.WebSession.Request.RequestBody = await GetCompressedResponseBody(args.WebSession.Request.ContentEncoding, args.WebSession.Request.RequestBody);
}
args.WebSession.Request.ContentLength = args.WebSession.Request.RequestBody.Length; args.WebSession.Request.ContentLength = args.WebSession.Request.RequestBody.Length;
var newStream = args.WebSession.ProxyClient.Stream;
var newStream = args.WebSession.ServerConnection.Stream;
await newStream.WriteAsync(args.WebSession.Request.RequestBody, 0, args.WebSession.Request.RequestBody.Length).ConfigureAwait(false); await newStream.WriteAsync(args.WebSession.Request.RequestBody, 0, args.WebSession.Request.RequestBody.Length).ConfigureAwait(false);
} }
else else
...@@ -392,7 +398,7 @@ namespace Titanium.Web.Proxy ...@@ -392,7 +398,7 @@ namespace Titanium.Web.Proxy
private static async Task SendClientRequestBody(SessionEventArgs args) private static async Task SendClientRequestBody(SessionEventArgs args)
{ {
// End the operation // End the operation
var postStream = args.WebSession.ProxyClient.Stream; var postStream = args.WebSession.ServerConnection.Stream;
if (args.WebSession.Request.ContentLength > 0) if (args.WebSession.Request.ContentLength > 0)
{ {
...@@ -466,8 +472,6 @@ namespace Titanium.Web.Proxy ...@@ -466,8 +472,6 @@ namespace Titanium.Web.Proxy
if (sslPolicyErrors == SslPolicyErrors.None) if (sslPolicyErrors == SslPolicyErrors.None)
return true; return true;
Console.WriteLine("Certificate error: {0}", sslPolicyErrors);
//By default //By default
//do not allow this client to communicate with unauthenticated servers. //do not allow this client to communicate with unauthenticated servers.
return false; return false;
......
...@@ -24,7 +24,7 @@ namespace Titanium.Web.Proxy ...@@ -24,7 +24,7 @@ namespace Titanium.Web.Proxy
try try
{ {
if (!args.WebSession.Response.ResponseBodyRead) if (!args.WebSession.Response.ResponseBodyRead)
args.WebSession.Response.ResponseStream = args.WebSession.ProxyClient.Stream; args.WebSession.Response.ResponseStream = args.WebSession.ServerConnection.Stream;
if (BeforeResponse != null && !args.WebSession.Response.ResponseLocked) if (BeforeResponse != null && !args.WebSession.Response.ResponseLocked)
...@@ -46,13 +46,13 @@ namespace Titanium.Web.Proxy ...@@ -46,13 +46,13 @@ namespace Titanium.Web.Proxy
{ {
WriteResponseStatus(args.WebSession.Response.HttpVersion, "100", WriteResponseStatus(args.WebSession.Response.HttpVersion, "100",
"Continue", args.Client.ClientStreamWriter); "Continue", args.Client.ClientStreamWriter);
args.Client.ClientStreamWriter.WriteLine(); await args.Client.ClientStreamWriter.WriteLineAsync();
} }
else if (args.WebSession.Response.ExpectationFailed) else if (args.WebSession.Response.ExpectationFailed)
{ {
WriteResponseStatus(args.WebSession.Response.HttpVersion, "417", WriteResponseStatus(args.WebSession.Response.HttpVersion, "417",
"Expectation Failed", args.Client.ClientStreamWriter); "Expectation Failed", args.Client.ClientStreamWriter);
args.Client.ClientStreamWriter.WriteLine(); await args.Client.ClientStreamWriter.WriteLineAsync();
} }
WriteResponseStatus(args.WebSession.Response.HttpVersion, args.WebSession.Response.ResponseStatusCode, WriteResponseStatus(args.WebSession.Response.HttpVersion, args.WebSession.Response.ResponseStatusCode,
...@@ -68,19 +68,20 @@ namespace Titanium.Web.Proxy ...@@ -68,19 +68,20 @@ namespace Titanium.Web.Proxy
args.WebSession.Response.ResponseBody = await GetCompressedResponseBody(contentEncoding, args.WebSession.Response.ResponseBody).ConfigureAwait(false); args.WebSession.Response.ResponseBody = await GetCompressedResponseBody(contentEncoding, args.WebSession.Response.ResponseBody).ConfigureAwait(false);
} }
await WriteResponseHeaders(args.Client.ClientStreamWriter, args.WebSession.Response.ResponseHeaders, args.WebSession.Response.ResponseBody.Length, args.WebSession.Response.ContentLength = args.WebSession.Response.ResponseBody.Length;
isChunked).ConfigureAwait(false);
await WriteResponseBody(args.Client.ClientStream, args.WebSession.Response.ResponseBody, isChunked).ConfigureAwait(false); await WriteResponseHeaders(args.Client.ClientStreamWriter, args.WebSession.Response.ResponseHeaders).ConfigureAwait(false);
await WriteResponseBody(args.Client.ClientStream, args.WebSession.Response.ResponseBody, isChunked).ConfigureAwait(false);
} }
else else
{ {
WriteResponseHeaders(args.Client.ClientStreamWriter, args.WebSession.Response.ResponseHeaders); await WriteResponseHeaders(args.Client.ClientStreamWriter, args.WebSession.Response.ResponseHeaders);
if (args.WebSession.Response.IsChunked || args.WebSession.Response.ContentLength > 0) if (args.WebSession.Response.IsChunked || args.WebSession.Response.ContentLength > 0 || args.WebSession.Response.HttpVersion.ToLower().Trim() == "http/1.0")
await WriteResponseBody(args.WebSession.ProxyClient.ServerStreamReader, args.Client.ClientStream, args.WebSession.Response.IsChunked, args.WebSession.Response.ContentLength).ConfigureAwait(false); await WriteResponseBody(args.WebSession.ServerConnection.StreamReader, args.Client.ClientStream, args.WebSession.Response.IsChunked, args.WebSession.Response.ContentLength).ConfigureAwait(false);
} }
args.Client.ClientStream.Flush(); await args.Client.ClientStream.FlushAsync();
} }
catch catch
...@@ -104,10 +105,10 @@ namespace Titanium.Web.Proxy ...@@ -104,10 +105,10 @@ namespace Titanium.Web.Proxy
private static void WriteResponseStatus(string version, string code, string description, private static void WriteResponseStatus(string version, string code, string description,
StreamWriter responseWriter) StreamWriter responseWriter)
{ {
responseWriter.WriteLine(string.Format("{0} {1} {2}", version, code, description)); responseWriter.WriteLineAsync(string.Format("{0} {1} {2}", version, code, description));
} }
private static void WriteResponseHeaders(StreamWriter responseWriter, List<HttpHeader> headers) private static async Task WriteResponseHeaders(StreamWriter responseWriter, List<HttpHeader> headers)
{ {
if (headers != null) if (headers != null)
{ {
...@@ -115,12 +116,12 @@ namespace Titanium.Web.Proxy ...@@ -115,12 +116,12 @@ namespace Titanium.Web.Proxy
foreach (var header in headers) foreach (var header in headers)
{ {
responseWriter.WriteLine(header.ToString()); await responseWriter.WriteLineAsync(header.ToString());
} }
} }
responseWriter.WriteLine(); await responseWriter.WriteLineAsync();
responseWriter.Flush(); await responseWriter.FlushAsync();
} }
private static void FixResponseProxyHeaders(List<HttpHeader> headers) private static void FixResponseProxyHeaders(List<HttpHeader> headers)
{ {
...@@ -141,34 +142,6 @@ namespace Titanium.Web.Proxy ...@@ -141,34 +142,6 @@ namespace Titanium.Web.Proxy
headers.RemoveAll(x => x.Name.ToLower() == "proxy-connection"); headers.RemoveAll(x => x.Name.ToLower() == "proxy-connection");
} }
private static async Task WriteResponseHeaders(StreamWriter responseWriter, List<HttpHeader> headers, int length,
bool isChunked)
{
FixResponseProxyHeaders(headers);
if (!isChunked)
{
if (headers.Any(x => x.Name.ToLower() == "content-length") == false)
{
headers.Add(new HttpHeader("Content-Length", length.ToString()));
}
}
if (headers != null)
{
foreach (var header in headers)
{
if (!isChunked && header.Name.ToLower() == "content-length")
header.Value = length.ToString();
await responseWriter.WriteLineAsync(header.ToString()).ConfigureAwait(false);
}
}
await responseWriter.WriteLineAsync().ConfigureAwait(false);
await responseWriter.FlushAsync().ConfigureAwait(false);
}
private static async Task WriteResponseBody(Stream clientStream, byte[] data, bool isChunked) private static async Task WriteResponseBody(Stream clientStream, byte[] data, bool isChunked)
{ {
if (!isChunked) if (!isChunked)
...@@ -183,6 +156,10 @@ namespace Titanium.Web.Proxy ...@@ -183,6 +156,10 @@ namespace Titanium.Web.Proxy
{ {
if (!isChunked) if (!isChunked)
{ {
//http 1.0
if (ContentLength == -1)
ContentLength = long.MaxValue;
int bytesToRead = Constants.BUFFER_SIZE; int bytesToRead = Constants.BUFFER_SIZE;
if (ContentLength < Constants.BUFFER_SIZE) if (ContentLength < Constants.BUFFER_SIZE)
......
...@@ -61,7 +61,7 @@ ...@@ -61,7 +61,7 @@
<Compile Include="Decompression\IDecompression.cs" /> <Compile Include="Decompression\IDecompression.cs" />
<Compile Include="Decompression\ZlibDecompression.cs" /> <Compile Include="Decompression\ZlibDecompression.cs" />
<Compile Include="EventArguments\CertificateValidationEventArgs.cs" /> <Compile Include="EventArguments\CertificateValidationEventArgs.cs" />
<Compile Include="EventArguments\ProxyClient.cs" /> <Compile Include="Network\ProxyClient.cs" />
<Compile Include="Exceptions\BodyNotFoundException.cs" /> <Compile Include="Exceptions\BodyNotFoundException.cs" />
<Compile Include="Extensions\HttpWebResponseExtensions.cs" /> <Compile Include="Extensions\HttpWebResponseExtensions.cs" />
<Compile Include="Extensions\HttpWebRequestExtensions.cs" /> <Compile Include="Extensions\HttpWebRequestExtensions.cs" />
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment