Commit 4b86cde8 authored by justcoding121's avatar justcoding121

asyncify more

parent ff0dddf4
......@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Models;
......@@ -13,7 +14,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
{
ProxyServer.BeforeRequest += OnRequest;
ProxyServer.BeforeResponse += OnResponse;
ProxyServer.RemoteCertificateValidationCallback += OnCertificateValidation;
ProxyServer.ServerCertificateValidationCallback += OnCertificateValidation;
//Exclude Https addresses you don't want to proxy
//Usefull for clients that use certificate pinning
......@@ -61,9 +62,8 @@ namespace Titanium.Web.Proxy.Examples.Basic
ProxyServer.Stop();
}
//Test On Request, intecept requests
//Read browser URL send back to proxy by the injection script in OnResponse event
public void OnRequest(object sender, SessionEventArgs e)
//intecept & cancel, redirect or update requests
public async Task OnRequest(object sender, SessionEventArgs e)
{
Console.WriteLine(e.WebSession.Request.Url);
......@@ -73,12 +73,12 @@ namespace Titanium.Web.Proxy.Examples.Basic
if ((e.WebSession.Request.Method.ToUpper() == "POST" || e.WebSession.Request.Method.ToUpper() == "PUT"))
{
//Get/Set request body bytes
byte[] bodyBytes = e.GetRequestBody();
e.SetRequestBody(bodyBytes);
byte[] bodyBytes = await e.GetRequestBody();
await e.SetRequestBody(bodyBytes);
//Get/Set request body as string
string bodyString = e.GetRequestBodyAsString();
e.SetRequestBodyString(bodyString);
string bodyString = await e.GetRequestBodyAsString();
await e.SetRequestBodyString(bodyString);
}
......@@ -86,26 +86,24 @@ namespace Titanium.Web.Proxy.Examples.Basic
//Filter URL
if (e.WebSession.Request.RequestUri.AbsoluteUri.Contains("google.com"))
{
e.Ok("<!DOCTYPE html>" +
"<html><body><h1>" +
"Website Blocked" +
"</h1>" +
"<p>Blocked by titanium web proxy.</p>" +
"</body>" +
"</html>");
await e.Ok("<!DOCTYPE html>" +
"<html><body><h1>" +
"Website Blocked" +
"</h1>" +
"<p>Blocked by titanium web proxy.</p>" +
"</body>" +
"</html>");
}
//Redirect example
if (e.WebSession.Request.RequestUri.AbsoluteUri.Contains("wikipedia.org"))
{
e.Redirect("https://www.paypal.com");
await e.Redirect("https://www.paypal.com");
}
}
//Test script injection
//Insert script to read the Browser URL and send it back to proxy
public void OnResponse(object sender, SessionEventArgs e)
//Modify response
public async Task OnResponse(object sender, SessionEventArgs e)
{
//read response headers
var responseHeaders = e.WebSession.Response.ResponseHeaders;
......@@ -116,11 +114,11 @@ namespace Titanium.Web.Proxy.Examples.Basic
{
if (e.WebSession.Response.ContentType.Trim().ToLower().Contains("text/html"))
{
byte[] bodyBytes = e.GetResponseBody();
e.SetResponseBody(bodyBytes);
byte[] bodyBytes = await e.GetResponseBody();
await e.SetResponseBody(bodyBytes);
string body = e.GetResponseBodyAsString();
e.SetResponseBodyString(body);
string body = await e.GetResponseBodyAsString();
await e.SetResponseBodyString(body);
}
}
}
......@@ -131,13 +129,13 @@ namespace Titanium.Web.Proxy.Examples.Basic
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void OnCertificateValidation(object sender, CertificateValidationEventArgs e)
public async Task OnCertificateValidation(object sender, CertificateValidationEventArgs e)
{
//set IsValid to true/false based on Certificate Errors
if (e.SslPolicyErrors == System.Net.Security.SslPolicyErrors.None)
e.IsValid = true;
else
e.Session.Ok("Cannot validate server certificate! Not safe to proceed.");
await e.Session.Ok("Cannot validate server certificate! Not safe to proceed.");
}
}
}
\ No newline at end of file
......@@ -34,7 +34,7 @@ Setup HTTP proxy:
```csharp
ProxyServer.BeforeRequest += OnRequest;
ProxyServer.BeforeResponse += OnResponse;
ProxyServer.RemoteCertificateValidationCallback += OnCertificateValidation;
ProxyServer.ServerCertificateValidationCallback += OnCertificateValidation;
//Exclude Https addresses you don't want to proxy
//Usefull for clients that use certificate pinning
......@@ -61,8 +61,7 @@ Setup HTTP proxy:
GenericCertificateName = "google.com"
};
ProxyServer.AddEndPoint(transparentEndPoint);
//ProxyServer.UpStreamHttpProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 };
//ProxyServer.UpStreamHttpsProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 };
......@@ -87,7 +86,8 @@ Sample request and response event handlers
```csharp
public void OnRequest(object sender, SessionEventArgs e)
//intecept & cancel, redirect or update requests
public async Task OnRequest(object sender, SessionEventArgs e)
{
Console.WriteLine(e.WebSession.Request.Url);
......@@ -97,12 +97,12 @@ Sample request and response event handlers
if ((e.WebSession.Request.Method.ToUpper() == "POST" || e.WebSession.Request.Method.ToUpper() == "PUT"))
{
//Get/Set request body bytes
byte[] bodyBytes = e.GetRequestBody();
e.SetRequestBody(bodyBytes);
byte[] bodyBytes = await e.GetRequestBody();
await e.SetRequestBody(bodyBytes);
//Get/Set request body as string
string bodyString = e.GetRequestBodyAsString();
e.SetRequestBodyString(bodyString);
string bodyString = await e.GetRequestBodyAsString();
await e.SetRequestBodyString(bodyString);
}
......@@ -110,24 +110,24 @@ Sample request and response event handlers
//Filter URL
if (e.WebSession.Request.RequestUri.AbsoluteUri.Contains("google.com"))
{
e.Ok("<!DOCTYPE html>" +
"<html><body><h1>" +
"Website Blocked" +
"</h1>" +
"<p>Blocked by titanium web proxy.</p>" +
"</body>" +
"</html>");
await e.Ok("<!DOCTYPE html>" +
"<html><body><h1>" +
"Website Blocked" +
"</h1>" +
"<p>Blocked by titanium web proxy.</p>" +
"</body>" +
"</html>");
}
//Redirect example
if (e.WebSession.Request.RequestUri.AbsoluteUri.Contains("wikipedia.org"))
{
e.Redirect("https://www.paypal.com");
await e.Redirect("https://www.paypal.com");
}
}
public void OnResponse(object sender, SessionEventArgs e)
{
//Modify response
public async Task OnResponse(object sender, SessionEventArgs e)
{
//read response headers
var responseHeaders = e.WebSession.Response.ResponseHeaders;
......@@ -138,24 +138,28 @@ Sample request and response event handlers
{
if (e.WebSession.Response.ContentType.Trim().ToLower().Contains("text/html"))
{
byte[] bodyBytes = e.GetResponseBody();
e.SetResponseBody(bodyBytes);
byte[] bodyBytes = await e.GetResponseBody();
await e.SetResponseBody(bodyBytes);
string body = e.GetResponseBodyAsString();
e.SetResponseBodyString(body);
string body = await e.GetResponseBodyAsString();
await e.SetResponseBodyString(body);
}
}
}
}
// Allows overriding default certificate validation logic
public void OnCertificateValidation(object sender, CertificateValidationEventArgs e)
/// <summary>
/// Allows overriding default certificate validation logic
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public async Task OnCertificateValidation(object sender, CertificateValidationEventArgs e)
{
//set IsValid to true/false based on Certificate Errors
if (e.SslPolicyErrors == System.Net.Security.SslPolicyErrors.None)
e.IsValid = true;
else
e.Session.Ok("Cannot validate server certificate! Not safe to proceed.");
await e.Session.Ok("Cannot validate server certificate! Not safe to proceed.");
}
```
Future roadmap
......
using System.IO;
using System.IO.Compression;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Compression
{
class DeflateCompression : ICompression
{
public byte[] Compress(byte[] responseBody)
public async Task<byte[]> Compress(byte[] responseBody)
{
using (var ms = new MemoryStream())
{
using (var zip = new DeflateStream(ms, CompressionMode.Compress, true))
{
zip.Write(responseBody, 0, responseBody.Length);
await zip.WriteAsync(responseBody, 0, responseBody.Length).ConfigureAwait(false);
}
return ms.ToArray();
......
using Ionic.Zlib;
using System.IO;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Compression
{
class GZipCompression : ICompression
{
public byte[] Compress(byte[] responseBody)
public async Task<byte[]> Compress(byte[] responseBody)
{
using (var ms = new MemoryStream())
{
using (var zip = new GZipStream(ms, CompressionMode.Compress, true))
{
zip.Write(responseBody, 0, responseBody.Length);
await zip.WriteAsync(responseBody, 0, responseBody.Length).ConfigureAwait(false);
}
return ms.ToArray();
......
namespace Titanium.Web.Proxy.Compression
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Compression
{
interface ICompression
{
byte[] Compress(byte[] responseBody);
Task<byte[]> Compress(byte[] responseBody);
}
}
using Ionic.Zlib;
using System.IO;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Compression
{
class ZlibCompression : ICompression
{
public byte[] Compress(byte[] responseBody)
public async Task<byte[]> Compress(byte[] responseBody)
{
using (var ms = new MemoryStream())
{
using (var zip = new ZlibStream(ms, CompressionMode.Compress, true))
{
zip.Write(responseBody, 0, responseBody.Length);
await zip.WriteAsync(responseBody, 0, responseBody.Length).ConfigureAwait(false);
}
return ms.ToArray();
......
namespace Titanium.Web.Proxy.Decompression
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Decompression
{
class DefaultDecompression : IDecompression
{
public byte[] Decompress(byte[] compressedArray)
public Task<byte[]> Decompress(byte[] compressedArray)
{
return compressedArray;
return Task.FromResult(compressedArray);
}
}
}
using Ionic.Zlib;
using System.IO;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Decompression
{
class DeflateDecompression : IDecompression
{
public byte[] Decompress(byte[] compressedArray)
public async Task<byte[]> Decompress(byte[] compressedArray)
{
var stream = new MemoryStream(compressedArray);
......@@ -17,9 +18,9 @@ namespace Titanium.Web.Proxy.Decompression
using (var output = new MemoryStream())
{
int read;
while ((read = decompressor.Read(buffer, 0, buffer.Length)) > 0)
while ((read = await decompressor.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false)) > 0)
{
output.Write(buffer, 0, read);
await output.WriteAsync(buffer, 0, read).ConfigureAwait(false);
}
return output.ToArray();
......
using System.IO;
using System.IO.Compression;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Decompression
{
class GZipDecompression : IDecompression
{
public byte[] Decompress(byte[] compressedArray)
public async Task<byte[]> Decompress(byte[] compressedArray)
{
using (var decompressor = new GZipStream(new MemoryStream(compressedArray), CompressionMode.Decompress))
{
......@@ -14,9 +15,9 @@ namespace Titanium.Web.Proxy.Decompression
using (var output = new MemoryStream())
{
int read;
while ((read = decompressor.Read(buffer, 0, buffer.Length)) > 0)
while ((read = await decompressor.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false)) > 0)
{
output.Write(buffer, 0, read);
await output.WriteAsync(buffer, 0, read).ConfigureAwait(false);
}
return output.ToArray();
}
......
using System.IO;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Decompression
{
interface IDecompression
{
byte[] Decompress(byte[] compressedArray);
Task<byte[]> Decompress(byte[] compressedArray);
}
}
using Ionic.Zlib;
using System.IO;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Decompression
{
class ZlibDecompression : IDecompression
{
public byte[] Decompress(byte[] compressedArray)
public async Task<byte[]> Decompress(byte[] compressedArray)
{
var memoryStream = new MemoryStream(compressedArray);
using (var decompressor = new ZlibStream(memoryStream, CompressionMode.Decompress))
......@@ -16,9 +17,9 @@ namespace Titanium.Web.Proxy.Decompression
using (var output = new MemoryStream())
{
int read;
while ((read = decompressor.Read(buffer, 0, buffer.Length)) > 0)
while ((read = await decompressor.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false)) > 0)
{
output.Write(buffer, 0, read);
await output.WriteAsync(buffer, 0, read).ConfigureAwait(false);
}
return output.ToArray();
}
......
......@@ -6,6 +6,7 @@ using Titanium.Web.Proxy.Decompression;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Http.Responses;
using Titanium.Web.Proxy.Extensions;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.EventArguments
{
......@@ -54,7 +55,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary>
/// Read request body content as bytes[] for current session
/// </summary>
private void ReadRequestBody()
private async Task ReadRequestBody()
{
//GET request don't have a request body to read
if ((WebSession.Request.Method.ToUpper() != "POST" && WebSession.Request.Method.ToUpper() != "PUT"))
......@@ -74,7 +75,7 @@ namespace Titanium.Web.Proxy.EventArguments
//For chunked request we need to read data as they arrive, until we reach a chunk end symbol
if (WebSession.Request.IsChunked)
{
this.Client.ClientStreamReader.CopyBytesToStreamChunked(requestBodyStream);
await this.Client.ClientStreamReader.CopyBytesToStreamChunked(requestBodyStream).ConfigureAwait(false);
}
else
{
......@@ -82,11 +83,11 @@ namespace Titanium.Web.Proxy.EventArguments
if (WebSession.Request.ContentLength > 0)
{
//If not chunked then its easy just read the amount of bytes mentioned in content length header of response
this.Client.ClientStreamReader.CopyBytesToStream(requestBodyStream, WebSession.Request.ContentLength);
await this.Client.ClientStreamReader.CopyBytesToStream(requestBodyStream, WebSession.Request.ContentLength).ConfigureAwait(false);
}
}
WebSession.Request.RequestBody = GetDecompressedResponseBody(WebSession.Request.ContentEncoding, requestBodyStream.ToArray());
WebSession.Request.RequestBody = await GetDecompressedResponseBody(WebSession.Request.ContentEncoding, requestBodyStream.ToArray()).ConfigureAwait(false);
}
}
......@@ -98,7 +99,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary>
/// Read response body as byte[] for current response
/// </summary>
private void ReadResponseBody()
private async Task ReadResponseBody()
{
//If not already read (not cached yet)
if (WebSession.Response.ResponseBody == null)
......@@ -108,19 +109,19 @@ namespace Titanium.Web.Proxy.EventArguments
//If chuncked the read chunk by chunk until we hit chunk end symbol
if (WebSession.Response.IsChunked)
{
WebSession.ProxyClient.ServerStreamReader.CopyBytesToStreamChunked(responseBodyStream);
await WebSession.ProxyClient.ServerStreamReader.CopyBytesToStreamChunked(responseBodyStream).ConfigureAwait(false);
}
else
{
if (WebSession.Response.ContentLength > 0)
{
//If not chunked then its easy just read the amount of bytes mentioned in content length header of response
WebSession.ProxyClient.ServerStreamReader.CopyBytesToStream(responseBodyStream, WebSession.Response.ContentLength);
await WebSession.ProxyClient.ServerStreamReader.CopyBytesToStream(responseBodyStream, WebSession.Response.ContentLength).ConfigureAwait(false);
}
}
WebSession.Response.ResponseBody = GetDecompressedResponseBody(WebSession.Response.ContentEncoding, responseBodyStream.ToArray());
WebSession.Response.ResponseBody = await GetDecompressedResponseBody(WebSession.Response.ContentEncoding, responseBodyStream.ToArray()).ConfigureAwait(false);
}
//set this to true for caching
......@@ -132,25 +133,25 @@ namespace Titanium.Web.Proxy.EventArguments
/// Gets the request body as bytes
/// </summary>
/// <returns></returns>
public byte[] GetRequestBody()
public async Task<byte[]> GetRequestBody()
{
if (WebSession.Request.RequestLocked)
throw new Exception("You cannot call this function after request is made to server.");
ReadRequestBody();
await ReadRequestBody().ConfigureAwait(false);
return WebSession.Request.RequestBody;
}
/// <summary>
/// Gets the request body as string
/// </summary>
/// <returns></returns>
public string GetRequestBodyAsString()
public async Task<string> GetRequestBodyAsString()
{
if (WebSession.Request.RequestLocked)
throw new Exception("You cannot call this function after request is made to server.");
ReadRequestBody();
await ReadRequestBody().ConfigureAwait(false);
//Use the encoding specified in request to decode the byte[] data to string
return WebSession.Request.RequestBodyString ?? (WebSession.Request.RequestBodyString = WebSession.Request.Encoding.GetString(WebSession.Request.RequestBody));
......@@ -160,7 +161,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// Sets the request body
/// </summary>
/// <param name="body"></param>
public void SetRequestBody(byte[] body)
public async Task SetRequestBody(byte[] body)
{
if (WebSession.Request.RequestLocked)
throw new Exception("You cannot call this function after request is made to server.");
......@@ -168,7 +169,7 @@ namespace Titanium.Web.Proxy.EventArguments
//syphon out the request body from client before setting the new body
if (!WebSession.Request.RequestBodyRead)
{
ReadRequestBody();
await ReadRequestBody().ConfigureAwait(false);
}
WebSession.Request.RequestBody = body;
......@@ -179,7 +180,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// Sets the body with the specified string
/// </summary>
/// <param name="body"></param>
public void SetRequestBodyString(string body)
public async Task SetRequestBodyString(string body)
{
if (WebSession.Request.RequestLocked)
throw new Exception("You cannot call this function after request is made to server.");
......@@ -187,7 +188,7 @@ namespace Titanium.Web.Proxy.EventArguments
//syphon out the request body from client before setting the new body
if (!WebSession.Request.RequestBodyRead)
{
ReadRequestBody();
await ReadRequestBody().ConfigureAwait(false);
}
WebSession.Request.RequestBody = WebSession.Request.Encoding.GetBytes(body);
......@@ -203,12 +204,12 @@ namespace Titanium.Web.Proxy.EventArguments
/// Gets the response body as byte array
/// </summary>
/// <returns></returns>
public byte[] GetResponseBody()
public async Task<byte[]> GetResponseBody()
{
if (!WebSession.Request.RequestLocked)
throw new Exception("You cannot call this function before request is made to server.");
ReadResponseBody();
await ReadResponseBody().ConfigureAwait(false);
return WebSession.Response.ResponseBody;
}
......@@ -216,12 +217,12 @@ namespace Titanium.Web.Proxy.EventArguments
/// Gets the response body as string
/// </summary>
/// <returns></returns>
public string GetResponseBodyAsString()
public async Task<string> GetResponseBodyAsString()
{
if (!WebSession.Request.RequestLocked)
throw new Exception("You cannot call this function before request is made to server.");
GetResponseBody();
await GetResponseBody().ConfigureAwait(false);
return WebSession.Response.ResponseBodyString ?? (WebSession.Response.ResponseBodyString = WebSession.Response.Encoding.GetString(WebSession.Response.ResponseBody));
}
......@@ -230,7 +231,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// Set the response body bytes
/// </summary>
/// <param name="body"></param>
public void SetResponseBody(byte[] body)
public async Task SetResponseBody(byte[] body)
{
if (!WebSession.Request.RequestLocked)
throw new Exception("You cannot call this function before request is made to server.");
......@@ -238,7 +239,7 @@ namespace Titanium.Web.Proxy.EventArguments
//syphon out the response body from server before setting the new body
if (WebSession.Response.ResponseBody == null)
{
GetResponseBody();
await GetResponseBody().ConfigureAwait(false);
}
WebSession.Response.ResponseBody = body;
......@@ -253,7 +254,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// Replace the response body with the specified string
/// </summary>
/// <param name="body"></param>
public void SetResponseBodyString(string body)
public async Task SetResponseBodyString(string body)
{
if (!WebSession.Request.RequestLocked)
throw new Exception("You cannot call this function before request is made to server.");
......@@ -261,19 +262,19 @@ namespace Titanium.Web.Proxy.EventArguments
//syphon out the response body from server before setting the new body
if (WebSession.Response.ResponseBody == null)
{
GetResponseBody();
await GetResponseBody().ConfigureAwait(false);
}
var bodyBytes = WebSession.Response.Encoding.GetBytes(body);
SetResponseBody(bodyBytes);
await SetResponseBody(bodyBytes).ConfigureAwait(false);
}
private byte[] GetDecompressedResponseBody(string encodingType, byte[] responseBodyStream)
private async Task<byte[]> GetDecompressedResponseBody(string encodingType, byte[] responseBodyStream)
{
var decompressionFactory = new DecompressionFactory();
var decompressor = decompressionFactory.Create(encodingType);
return decompressor.Decompress(responseBodyStream);
return await decompressor.Decompress(responseBodyStream).ConfigureAwait(false);
}
......@@ -283,7 +284,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// and ignore the request
/// </summary>
/// <param name="html"></param>
public void Ok(string html)
public async Task Ok(string html)
{
if (WebSession.Request.RequestLocked)
throw new Exception("You cannot call this function after request is made to server.");
......@@ -293,7 +294,7 @@ namespace Titanium.Web.Proxy.EventArguments
var result = Encoding.Default.GetBytes(html);
Ok(result);
await Ok(result).ConfigureAwait(false);
}
/// <summary>
......@@ -302,19 +303,19 @@ namespace Titanium.Web.Proxy.EventArguments
/// and ignore the request
/// </summary>
/// <param name="body"></param>
public void Ok(byte[] result)
public async Task Ok(byte[] result)
{
var response = new OkResponse();
response.HttpVersion = WebSession.Request.HttpVersion;
response.ResponseBody = result;
Respond(response);
await Respond(response).ConfigureAwait(false);
WebSession.Request.CancelRequest = true;
}
public void Redirect(string url)
public async Task Redirect(string url)
{
var response = new RedirectResponse();
......@@ -322,13 +323,13 @@ namespace Titanium.Web.Proxy.EventArguments
response.ResponseHeaders.Add(new Models.HttpHeader("Location", url));
response.ResponseBody = Encoding.ASCII.GetBytes(string.Empty);
Respond(response);
await Respond(response).ConfigureAwait(false);
WebSession.Request.CancelRequest = true;
}
/// a generic responder method
public void Respond(Response response)
public async Task Respond(Response response)
{
WebSession.Request.RequestLocked = true;
......@@ -337,7 +338,7 @@ namespace Titanium.Web.Proxy.EventArguments
WebSession.Response = response;
ProxyServer.HandleHttpSessionResponse(this);
await ProxyServer.HandleHttpSessionResponse(this).ConfigureAwait(false);
}
}
......
......@@ -15,12 +15,12 @@ namespace Titanium.Web.Proxy.Extensions
if (!string.IsNullOrEmpty(initialData))
{
var bytes = Encoding.ASCII.GetBytes(initialData);
output.Write(bytes, 0, bytes.Length);
await output.WriteAsync(bytes, 0, bytes.Length);
}
await input.CopyToAsync(output);
}
internal static void CopyBytesToStream(this CustomBinaryReader clientStreamReader, Stream stream, long totalBytesToRead)
internal static async Task CopyBytesToStream(this CustomBinaryReader clientStreamReader, Stream stream, long totalBytesToRead)
{
var totalbytesRead = 0;
......@@ -35,7 +35,7 @@ namespace Titanium.Web.Proxy.Extensions
while (totalbytesRead < (int)totalBytesToRead)
{
var buffer = clientStreamReader.ReadBytes(bytesToRead);
var buffer = await clientStreamReader.ReadBytesAsync(bytesToRead);
totalbytesRead += buffer.Length;
var remainingBytes = (int)totalBytesToRead - totalbytesRead;
......@@ -43,26 +43,26 @@ namespace Titanium.Web.Proxy.Extensions
{
bytesToRead = remainingBytes;
}
stream.Write(buffer, 0, buffer.Length);
await stream.WriteAsync(buffer, 0, buffer.Length);
}
}
internal static void CopyBytesToStreamChunked(this CustomBinaryReader clientStreamReader, Stream stream)
internal static async Task CopyBytesToStreamChunked(this CustomBinaryReader clientStreamReader, Stream stream)
{
while (true)
{
var chuchkHead = clientStreamReader.ReadLine();
var chuchkHead = await clientStreamReader.ReadLineAsync();
var chunkSize = int.Parse(chuchkHead, NumberStyles.HexNumber);
if (chunkSize != 0)
{
var buffer = clientStreamReader.ReadBytes(chunkSize);
stream.Write(buffer, 0, buffer.Length);
var buffer = await clientStreamReader.ReadBytesAsync(chunkSize);
await stream.WriteAsync(buffer, 0, buffer.Length);
//chunk trail
clientStreamReader.ReadLine();
await clientStreamReader.ReadLineAsync();
}
else
{
clientStreamReader.ReadLine();
await clientStreamReader.ReadLineAsync();
break;
}
}
......
......@@ -4,6 +4,7 @@ using System.IO;
using System.Net.Security;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Network;
using Titanium.Web.Proxy.Shared;
......@@ -31,7 +32,7 @@ namespace Titanium.Web.Proxy.Helpers
/// Read a line from the byte stream
/// </summary>
/// <returns></returns>
internal string ReadLine()
internal async Task<string> ReadLineAsync()
{
var readBuffer = new StringBuilder();
......@@ -40,7 +41,7 @@ namespace Titanium.Web.Proxy.Helpers
var lastChar = default(char);
var buffer = new byte[1];
while (this.stream.Read(buffer, 0, 1) > 0)
while (await this.stream.ReadAsync(buffer, 0, 1).ConfigureAwait(false) > 0)
{
if (lastChar == '\r' && buffer[0] == '\n')
{
......@@ -66,18 +67,18 @@ namespace Titanium.Web.Proxy.Helpers
/// Read until the last new line
/// </summary>
/// <returns></returns>
internal List<string> ReadAllLines()
internal async Task<List<string>> ReadAllLinesAsync()
{
string tmpLine;
var requestLines = new List<string>();
while (!string.IsNullOrEmpty(tmpLine = ReadLine()))
while (!string.IsNullOrEmpty(tmpLine = await ReadLineAsync().ConfigureAwait(false)))
{
requestLines.Add(tmpLine);
}
return requestLines;
}
internal byte[] ReadBytes(long totalBytesToRead)
internal async Task<byte[]> ReadBytesAsync(long totalBytesToRead)
{
int bytesToRead = Constants.BUFFER_SIZE;
......@@ -91,9 +92,9 @@ namespace Titanium.Web.Proxy.Helpers
using (var outStream = new MemoryStream())
{
while ((bytesRead += this.stream.Read(buffer, 0, bytesToRead)) > 0)
while ((bytesRead += await this.stream.ReadAsync(buffer, 0, bytesToRead).ConfigureAwait(false)) > 0)
{
outStream.Write(buffer, 0, bytesRead);
await outStream.WriteAsync(buffer, 0, bytesRead).ConfigureAwait(false);
totalBytesRead += bytesRead;
if (totalBytesRead == totalBytesToRead)
......
......@@ -14,7 +14,7 @@ namespace Titanium.Web.Proxy.Helpers
{
public class TcpHelper
{
public static void SendRaw(Stream clientStream, string httpCmd, List<HttpHeader> requestHeaders, string hostName,
public async static Task SendRaw(Stream clientStream, string httpCmd, List<HttpHeader> requestHeaders, string hostName,
int tunnelPort, bool isHttps)
{
StringBuilder sb = null;
......@@ -50,7 +50,7 @@ namespace Titanium.Web.Proxy.Helpers
try
{
sslStream = new SslStream(tunnelStream);
sslStream.AuthenticateAsClient(hostName, null, Constants.SupportedProtocols, false);
await sslStream.AuthenticateAsClientAsync(hostName, null, Constants.SupportedProtocols, false);
tunnelStream = sslStream;
}
catch
......@@ -62,17 +62,17 @@ namespace Titanium.Web.Proxy.Helpers
}
}
var sendRelay = Task.Factory.StartNew(() =>
{
if (sb != null)
clientStream.CopyToAsync(sb.ToString(), tunnelStream).Wait();
else
clientStream.CopyToAsync(string.Empty, tunnelStream).Wait();
});
Task sendRelay;
if (sb != null)
sendRelay = clientStream.CopyToAsync(sb.ToString(), tunnelStream);
else
sendRelay = clientStream.CopyToAsync(string.Empty, tunnelStream);
var receiveRelay = Task.Factory.StartNew(() =>tunnelStream.CopyToAsync(string.Empty, clientStream).Wait());
var receiveRelay = tunnelStream.CopyToAsync(string.Empty, clientStream);
Task.WaitAll(sendRelay, receiveRelay);
await Task.WhenAll(sendRelay, receiveRelay).ConfigureAwait(false);
}
catch
{
......
......@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network;
using Titanium.Web.Proxy.Shared;
......@@ -35,7 +36,7 @@ namespace Titanium.Web.Proxy.Http
this.Response = new Response();
}
internal void SendRequest()
internal async Task SendRequest()
{
Stream stream = ProxyClient.Stream;
......@@ -57,13 +58,13 @@ namespace Titanium.Web.Proxy.Http
string request = requestLines.ToString();
byte[] requestBytes = Encoding.ASCII.GetBytes(request);
stream.Write(requestBytes, 0, requestBytes.Length);
await stream.WriteAsync(requestBytes, 0, requestBytes.Length);
stream.Flush();
if (ProxyServer.Enable100ContinueBehaviour)
if (this.Request.ExpectContinue)
{
var httpResult = ProxyClient.ServerStreamReader.ReadLine().Split(Constants.SpaceSplit, 3);
var httpResult = (await ProxyClient.ServerStreamReader.ReadLineAsync()).Split(Constants.SpaceSplit, 3);
var responseStatusCode = httpResult[1].Trim();
var responseStatusDescription = httpResult[2].Trim();
......@@ -72,27 +73,27 @@ namespace Titanium.Web.Proxy.Http
&& responseStatusDescription.ToLower().Equals("continue"))
{
this.Request.Is100Continue = true;
ProxyClient.ServerStreamReader.ReadLine();
await ProxyClient.ServerStreamReader.ReadLineAsync();
}
else if (responseStatusCode.Equals("417")
&& responseStatusDescription.ToLower().Equals("expectation failed"))
{
this.Request.ExpectationFailed = true;
ProxyClient.ServerStreamReader.ReadLine();
await ProxyClient.ServerStreamReader.ReadLineAsync();
}
}
}
internal void ReceiveResponse()
internal async Task ReceiveResponse()
{
//return if this is already read
if (this.Response.ResponseStatusCode != null) return;
var httpResult = ProxyClient.ServerStreamReader.ReadLine().Split(Constants.SpaceSplit, 3);
var httpResult = (await ProxyClient.ServerStreamReader.ReadLineAsync()).Split(Constants.SpaceSplit, 3);
if (string.IsNullOrEmpty(httpResult[0]))
{
var s = ProxyClient.ServerStreamReader.ReadLine();
await ProxyClient.ServerStreamReader.ReadLineAsync();
}
this.Response.HttpVersion = httpResult[0].Trim();
......@@ -105,8 +106,8 @@ namespace Titanium.Web.Proxy.Http
{
this.Response.Is100Continue = true;
this.Response.ResponseStatusCode = null;
ProxyClient.ServerStreamReader.ReadLine();
ReceiveResponse();
await ProxyClient.ServerStreamReader.ReadLineAsync();
await ReceiveResponse();
return;
}
else if (this.Response.ResponseStatusCode.Equals("417")
......@@ -114,12 +115,12 @@ namespace Titanium.Web.Proxy.Http
{
this.Response.ExpectationFailed = true;
this.Response.ResponseStatusCode = null;
ProxyClient.ServerStreamReader.ReadLine();
ReceiveResponse();
await ProxyClient.ServerStreamReader.ReadLineAsync();
await ReceiveResponse();
return;
}
List<string> responseLines = ProxyClient.ServerStreamReader.ReadAllLines();
List<string> responseLines = await ProxyClient.ServerStreamReader.ReadAllLinesAsync();
for (int index = 0; index < responseLines.Count; ++index)
{
......
......@@ -61,13 +61,15 @@ namespace Titanium.Web.Proxy.Network
}
if (cached == null)
cached = await CreateClient(sessionArgs,hostname, port, isSecure, version);
cached = await CreateClient(sessionArgs, hostname, port, isSecure, version).ConfigureAwait(false);
//if (ConnectionCache.Where(x => x.HostName == hostname && x.port == port &&
//x.IsSecure == isSecure && x.TcpClient.Connected && x.Version.Equals(version)).Count() < 2)
//{
// Task.Factory.StartNew(() => CreateClient(sessionArgs, hostname, port, isSecure, version));
//}
//just create one more preemptively
if (ConnectionCache.Where(x => x.HostName == hostname && x.port == port &&
x.IsSecure == isSecure && x.TcpClient.Connected && x.Version.Equals(version)).Count() < 2)
{
var task = CreateClient(sessionArgs, hostname, port, isSecure, version)
.ContinueWith(x => ReleaseClient(x.Result));
}
return cached;
}
......@@ -81,26 +83,26 @@ namespace Titanium.Web.Proxy.Network
{
CustomSslStream sslStream = null;
if(ProxyServer.UpStreamHttpsProxy!=null)
if (ProxyServer.UpStreamHttpsProxy != null)
{
client = new TcpClient(ProxyServer.UpStreamHttpsProxy.HostName, ProxyServer.UpStreamHttpsProxy.Port);
stream = (Stream)client.GetStream();
var writer = new StreamWriter(stream,Encoding.ASCII, Constants.BUFFER_SIZE, true);
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));
writer.WriteLine(string.Format("Host: {0}:{1}", sessionArgs.WebSession.Request.RequestUri.Host, sessionArgs.WebSession.Request.RequestUri.Port));
writer.WriteLine("Connection: Keep-Alive");
writer.WriteLine();
writer.Flush();
var reader = new CustomBinaryReader(stream);
var result = reader.ReadLine();
var result = await reader.ReadLineAsync().ConfigureAwait(false);
if (!result.ToLower().Contains("200 connection established"))
throw new Exception("Upstream proxy failed to create a secure tunnel");
reader.ReadAllLines();
await reader.ReadAllLinesAsync().ConfigureAwait(false);
}
else
{
......@@ -110,9 +112,9 @@ namespace Titanium.Web.Proxy.Network
try
{
sslStream = new CustomSslStream(stream, true, ProxyServer.ValidateServerCertificate);
sslStream = new CustomSslStream(stream, true, new RemoteCertificateValidationCallback(ProxyServer.ValidateServerCertificate));
sslStream.Session = sessionArgs;
await sslStream.AuthenticateAsClientAsync(hostname, null, Constants.SupportedProtocols, false);
await sslStream.AuthenticateAsClientAsync(hostname, null, Constants.SupportedProtocols, false).ConfigureAwait(false);
stream = (Stream)sslStream;
}
catch
......@@ -155,7 +157,7 @@ namespace Titanium.Web.Proxy.Network
ConnectionCache.Add(Connection);
}
internal static void ClearIdleConnections()
internal async static void ClearIdleConnections()
{
while (true)
{
......@@ -171,7 +173,7 @@ namespace Titanium.Web.Proxy.Network
ConnectionCache.RemoveAll(x => x.LastAccess < cutOff);
}
Thread.Sleep(1000 * 60 * 3);
await Task.Delay(1000 * 60 * 3).ConfigureAwait(false);
}
}
......
......@@ -34,8 +34,8 @@ namespace Titanium.Web.Proxy
public static string RootCertificateName { get; set; }
public static bool Enable100ContinueBehaviour { get; set; }
public static event EventHandler<SessionEventArgs> BeforeRequest;
public static event EventHandler<SessionEventArgs> BeforeResponse;
public static event Func<object, SessionEventArgs, Task> BeforeRequest;
public static event Func<object, SessionEventArgs, Task> BeforeResponse;
/// <summary>
/// External proxy for Http
......@@ -50,14 +50,14 @@ namespace Titanium.Web.Proxy
/// <summary>
/// Verifies the remote Secure Sockets Layer (SSL) certificate used for authentication
/// </summary>
public static event EventHandler<CertificateValidationEventArgs> RemoteCertificateValidationCallback;
public static event Func<object, CertificateValidationEventArgs, Task> ServerCertificateValidationCallback;
public static List<ProxyEndPoint> ProxyEndPoints { get; set; }
public static void Initialize()
{
Task.Factory.StartNew(() => TcpConnectionManager.ClearIdleConnections());
TcpConnectionManager.ClearIdleConnections();
}
public static void AddEndPoint(ProxyEndPoint endPoint)
......@@ -235,51 +235,6 @@ namespace Titanium.Web.Proxy
}
}
/// <summary>
/// Call back to override server certificate validation
/// </summary>
/// <param name="sender"></param>
/// <param name="certificate"></param>
/// <param name="chain"></param>
/// <param name="sslPolicyErrors"></param>
/// <returns></returns>
internal static bool ValidateServerCertificate(
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
var param = sender as CustomSslStream;
if (RemoteCertificateValidationCallback != null)
{
var args = new CertificateValidationEventArgs();
args.Session = param.Session;
args.Certificate = certificate;
args.Chain = chain;
args.SslPolicyErrors = sslPolicyErrors;
RemoteCertificateValidationCallback.Invoke(null, args);
if(!args.IsValid)
{
param.Session.WebSession.Request.CancelRequest = true;
}
return args.IsValid;
}
if (sslPolicyErrors == SslPolicyErrors.None)
return true;
Console.WriteLine("Certificate error: {0}", sslPolicyErrors);
//By default
//do not allow this client to communicate with unauthenticated servers.
return false;
}
}
}
\ No newline at end of file
......@@ -34,7 +34,7 @@ namespace Titanium.Web.Proxy
{
//read the first line HTTP command
var httpCmd = clientStreamReader.ReadLine();
var httpCmd = await clientStreamReader.ReadLineAsync();
if (string.IsNullOrEmpty(httpCmd))
{
......@@ -63,9 +63,9 @@ namespace Titanium.Web.Proxy
if (httpVerb.ToUpper() == "CONNECT" && !excluded && httpRemoteUri.Port != 80)
{
httpRemoteUri = new Uri("https://" + httpCmdSplit[1]);
clientStreamReader.ReadAllLines();
await clientStreamReader.ReadAllLinesAsync().ConfigureAwait(false);
WriteConnectResponse(clientStreamWriter, httpVersion);
await WriteConnectResponse(clientStreamWriter, httpVersion).ConfigureAwait(false);
var certificate = CertManager.CreateCertificate(httpRemoteUri.Host);
......@@ -77,7 +77,7 @@ namespace Titanium.Web.Proxy
//Successfully managed to authenticate the client using the fake certificate
await sslStream.AuthenticateAsServerAsync(certificate, false,
Constants.SupportedProtocols, false);
Constants.SupportedProtocols, false).ConfigureAwait(false);
clientStreamReader = new CustomBinaryReader(sslStream);
clientStreamWriter = new StreamWriter(sslStream);
......@@ -95,16 +95,16 @@ namespace Titanium.Web.Proxy
}
httpCmd = clientStreamReader.ReadLine();
httpCmd = await clientStreamReader.ReadLineAsync().ConfigureAwait(false);
}
else if (httpVerb.ToUpper() == "CONNECT")
{
clientStreamReader.ReadAllLines();
WriteConnectResponse(clientStreamWriter, httpVersion);
await clientStreamReader.ReadAllLinesAsync().ConfigureAwait(false);
await WriteConnectResponse(clientStreamWriter, httpVersion).ConfigureAwait(false);
TcpHelper.SendRaw(clientStream, null, null, httpRemoteUri.Host, httpRemoteUri.Port,
false);
await TcpHelper.SendRaw(clientStream, null, null, httpRemoteUri.Host, httpRemoteUri.Port,
false).ConfigureAwait(false);
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, null);
return;
......@@ -112,8 +112,8 @@ namespace Titanium.Web.Proxy
//Now create the request
await HandleHttpSessionRequest(client, httpCmd, clientStream, clientStreamReader, clientStreamWriter,
httpRemoteUri.Scheme == Uri.UriSchemeHttps ? true : false);
await HandleHttpSessionRequest(client, httpCmd, clientStream, clientStreamReader, clientStreamWriter,
httpRemoteUri.Scheme == Uri.UriSchemeHttps ? true : false).ConfigureAwait(false);
}
catch
{
......@@ -123,7 +123,7 @@ namespace Titanium.Web.Proxy
//This is called when requests are routed through router to this endpoint
//For ssl requests
private static async Task HandleClient(TransparentProxyEndPoint endPoint, TcpClient tcpClient)
private static async void HandleClient(TransparentProxyEndPoint endPoint, TcpClient tcpClient)
{
Stream clientStream = tcpClient.GetStream();
CustomBinaryReader clientStreamReader = null;
......@@ -144,8 +144,8 @@ namespace Titanium.Web.Proxy
try
{
//Successfully managed to authenticate the client using the fake certificate
sslStream.AuthenticateAsServer(certificate, false,
SslProtocols.Tls, false);
await sslStream.AuthenticateAsServerAsync(certificate, false,
SslProtocols.Tls, false).ConfigureAwait(false);
clientStreamReader = new CustomBinaryReader(sslStream);
clientStreamWriter = new StreamWriter(sslStream);
......@@ -167,11 +167,11 @@ namespace Titanium.Web.Proxy
clientStreamReader = new CustomBinaryReader(clientStream);
}
var httpCmd = clientStreamReader.ReadLine();
var httpCmd = await clientStreamReader.ReadLineAsync().ConfigureAwait(false);
//Now create the request
await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamReader, clientStreamWriter,
true);
await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamReader, clientStreamWriter,
true).ConfigureAwait(false);
}
private static async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream,
......@@ -212,7 +212,7 @@ namespace Titanium.Web.Proxy
args.WebSession.Request.RequestHeaders = new List<HttpHeader>();
string tmpLine;
while (!string.IsNullOrEmpty(tmpLine = clientStreamReader.ReadLine()))
while (!string.IsNullOrEmpty(tmpLine = await clientStreamReader.ReadLineAsync().ConfigureAwait(false)))
{
var header = tmpLine.Split(new char[] { ':' }, 2);
args.WebSession.Request.RequestHeaders.Add(new HttpHeader(header[0], header[1]));
......@@ -231,8 +231,8 @@ namespace Titanium.Web.Proxy
if (args.WebSession.Request.UpgradeToWebSocket)
{
TcpHelper.SendRaw(clientStream, httpCmd, args.WebSession.Request.RequestHeaders,
httpRemoteUri.Host, httpRemoteUri.Port, args.IsHttps);
await TcpHelper.SendRaw(clientStream, httpCmd, args.WebSession.Request.RequestHeaders,
httpRemoteUri.Host, httpRemoteUri.Port, args.IsHttps).ConfigureAwait(false);
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
return;
}
......@@ -241,12 +241,23 @@ namespace Titanium.Web.Proxy
args.WebSession.Request.Host = args.WebSession.Request.RequestUri.Host;
//If requested interception
BeforeRequest?.Invoke(null, args);
if (BeforeRequest != null)
{
Delegate[] invocationList = BeforeRequest.GetInvocationList();
Task[] handlerTasks = new Task[invocationList.Length];
for (int i = 0; i < invocationList.Length; i++)
{
handlerTasks[i] = ((Func<object, SessionEventArgs, Task>)invocationList[i])(null, args);
}
await Task.WhenAll(handlerTasks).ConfigureAwait(false);
}
//construct the web request that we are going to issue on behalf of the client.
connection = connection == null ?
await TcpConnectionManager.GetClient(args, args.WebSession.Request.RequestUri.Host, args.WebSession.Request.RequestUri.Port, args.IsHttps, version)
: lastRequestHostName != args.WebSession.Request.RequestUri.Host ? await TcpConnectionManager.GetClient(args, args.WebSession.Request.RequestUri.Host, args.WebSession.Request.RequestUri.Port, args.IsHttps, version)
await TcpConnectionManager.GetClient(args, args.WebSession.Request.RequestUri.Host, args.WebSession.Request.RequestUri.Port, args.IsHttps, version).ConfigureAwait(false)
: lastRequestHostName != args.WebSession.Request.RequestUri.Host ? await TcpConnectionManager.GetClient(args, args.WebSession.Request.RequestUri.Host, args.WebSession.Request.RequestUri.Port, args.IsHttps, version).ConfigureAwait(false)
: connection;
lastRequestHostName = args.WebSession.Request.RequestUri.Host;
......@@ -262,7 +273,7 @@ namespace Titanium.Web.Proxy
if (args.WebSession.Request.ExpectContinue)
{
args.WebSession.SetConnection(connection);
args.WebSession.SendRequest();
await args.WebSession.SendRequest().ConfigureAwait(false);
}
if (Enable100ContinueBehaviour)
......@@ -282,7 +293,7 @@ namespace Titanium.Web.Proxy
if (!args.WebSession.Request.ExpectContinue)
{
args.WebSession.SetConnection(connection);
args.WebSession.SendRequest();
await args.WebSession.SendRequest().ConfigureAwait(false);
}
//If request was modified by user
......@@ -290,7 +301,7 @@ namespace Titanium.Web.Proxy
{
args.WebSession.Request.ContentLength = args.WebSession.Request.RequestBody.Length;
var newStream = args.WebSession.ProxyClient.Stream;
newStream.Write(args.WebSession.Request.RequestBody, 0, args.WebSession.Request.RequestBody.Length);
await newStream.WriteAsync(args.WebSession.Request.RequestBody, 0, args.WebSession.Request.RequestBody.Length).ConfigureAwait(false);
}
else
{
......@@ -299,14 +310,14 @@ namespace Titanium.Web.Proxy
//If its a post/put request, then read the client html body and send it to server
if (httpMethod.ToUpper() == "POST" || httpMethod.ToUpper() == "PUT")
{
SendClientRequestBody(args);
await SendClientRequestBody(args).ConfigureAwait(false);
}
}
}
if (!args.WebSession.Request.ExpectationFailed)
{
HandleHttpSessionResponse(args);
await HandleHttpSessionResponse(args).ConfigureAwait(false);
}
//if connection is closing exit
......@@ -318,7 +329,7 @@ namespace Titanium.Web.Proxy
}
// read the next request
httpCmd = clientStreamReader.ReadLine();
httpCmd = await clientStreamReader.ReadLineAsync().ConfigureAwait(false);
}
catch
......@@ -333,12 +344,12 @@ namespace Titanium.Web.Proxy
TcpConnectionManager.ReleaseClient(connection);
}
private static void WriteConnectResponse(StreamWriter clientStreamWriter, string httpVersion)
private static async Task WriteConnectResponse(StreamWriter clientStreamWriter, string httpVersion)
{
clientStreamWriter.WriteLine(httpVersion + " 200 Connection established");
clientStreamWriter.WriteLine("Timestamp: {0}", DateTime.Now);
clientStreamWriter.WriteLine();
clientStreamWriter.Flush();
await clientStreamWriter.WriteLineAsync(httpVersion + " 200 Connection established").ConfigureAwait(false);
await clientStreamWriter.WriteLineAsync(string.Format("Timestamp: {0}", DateTime.Now)).ConfigureAwait(false);
await clientStreamWriter.WriteLineAsync().ConfigureAwait(false);
await clientStreamWriter.FlushAsync().ConfigureAwait(false);
}
private static void PrepareRequestHeaders(List<HttpHeader> requestHeaders, HttpWebSession webRequest)
......@@ -378,17 +389,16 @@ namespace Titanium.Web.Proxy
headers.RemoveAll(x => x.Name.ToLower() == "proxy-connection");
}
//This is called when the request is PUT/POST to read the body
private static void SendClientRequestBody(SessionEventArgs args)
private static async Task SendClientRequestBody(SessionEventArgs args)
{
// End the operation
var postStream = args.WebSession.ProxyClient.Stream;
if (args.WebSession.Request.ContentLength > 0)
{
try
{
args.Client.ClientStreamReader.CopyBytesToStream(postStream, args.WebSession.Request.ContentLength);
await args.Client.ClientStreamReader.CopyBytesToStream(postStream, args.WebSession.Request.ContentLength).ConfigureAwait(false);
}
catch
{
......@@ -400,7 +410,7 @@ namespace Titanium.Web.Proxy
{
try
{
args.Client.ClientStreamReader.CopyBytesToStreamChunked(postStream);
await args.Client.ClientStreamReader.CopyBytesToStreamChunked(postStream).ConfigureAwait(false);
}
catch
{
......@@ -409,6 +419,60 @@ namespace Titanium.Web.Proxy
}
}
/// <summary>
/// Call back to override server certificate validation
/// </summary>
/// <param name="sender"></param>
/// <param name="certificate"></param>
/// <param name="chain"></param>
/// <param name="sslPolicyErrors"></param>
/// <returns></returns>
internal static bool ValidateServerCertificate(
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
var param = sender as CustomSslStream;
if (ServerCertificateValidationCallback != null)
{
var args = new CertificateValidationEventArgs();
args.Session = param.Session;
args.Certificate = certificate;
args.Chain = chain;
args.SslPolicyErrors = sslPolicyErrors;
Delegate[] invocationList = ServerCertificateValidationCallback.GetInvocationList();
Task[] handlerTasks = new Task[invocationList.Length];
for (int i = 0; i < invocationList.Length; i++)
{
handlerTasks[i] = ((Func<object, CertificateValidationEventArgs, Task>)invocationList[i])(null, args);
}
Task.WhenAll(handlerTasks).Wait();
if (!args.IsValid)
{
param.Session.WebSession.Request.CancelRequest = true;
}
return args.IsValid;
}
if (sslPolicyErrors == SslPolicyErrors.None)
return true;
Console.WriteLine("Certificate error: {0}", sslPolicyErrors);
//By default
//do not allow this client to communicate with unauthenticated servers.
return false;
}
}
}
\ No newline at end of file
......@@ -10,15 +10,16 @@ using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Compression;
using Titanium.Web.Proxy.Shared;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy
{
partial class ProxyServer
{
//Called asynchronously when a request was successfully and we received the response
public static void HandleHttpSessionResponse(SessionEventArgs args)
public static async Task HandleHttpSessionResponse(SessionEventArgs args)
{
args.WebSession.ReceiveResponse();
await args.WebSession.ReceiveResponse().ConfigureAwait(false);
try
{
......@@ -27,8 +28,16 @@ namespace Titanium.Web.Proxy
if (BeforeResponse != null && !args.WebSession.Response.ResponseLocked)
{
BeforeResponse(null, args);
{
Delegate[] invocationList = BeforeResponse.GetInvocationList();
Task[] handlerTasks = new Task[invocationList.Length];
for (int i = 0; i < invocationList.Length; i++)
{
handlerTasks[i] = ((Func<object, SessionEventArgs, Task>)invocationList[i])(null, args);
}
await Task.WhenAll(handlerTasks).ConfigureAwait(false);
}
args.WebSession.Response.ResponseLocked = true;
......@@ -56,19 +65,19 @@ namespace Titanium.Web.Proxy
if (contentEncoding != null)
{
args.WebSession.Response.ResponseBody = GetCompressedResponseBody(contentEncoding, args.WebSession.Response.ResponseBody);
args.WebSession.Response.ResponseBody = await GetCompressedResponseBody(contentEncoding, args.WebSession.Response.ResponseBody).ConfigureAwait(false);
}
WriteResponseHeaders(args.Client.ClientStreamWriter, args.WebSession.Response.ResponseHeaders, args.WebSession.Response.ResponseBody.Length,
isChunked);
WriteResponseBody(args.Client.ClientStream, args.WebSession.Response.ResponseBody, isChunked);
await WriteResponseHeaders(args.Client.ClientStreamWriter, args.WebSession.Response.ResponseHeaders, args.WebSession.Response.ResponseBody.Length,
isChunked).ConfigureAwait(false);
await WriteResponseBody(args.Client.ClientStream, args.WebSession.Response.ResponseBody, isChunked).ConfigureAwait(false);
}
else
{
WriteResponseHeaders(args.Client.ClientStreamWriter, args.WebSession.Response.ResponseHeaders);
if (args.WebSession.Response.IsChunked || args.WebSession.Response.ContentLength > 0)
WriteResponseBody(args.WebSession.ProxyClient.ServerStreamReader, args.Client.ClientStream, args.WebSession.Response.IsChunked, args.WebSession.Response.ContentLength);
await WriteResponseBody(args.WebSession.ProxyClient.ServerStreamReader, args.Client.ClientStream, args.WebSession.Response.IsChunked, args.WebSession.Response.ContentLength).ConfigureAwait(false);
}
args.Client.ClientStream.Flush();
......@@ -84,11 +93,11 @@ namespace Titanium.Web.Proxy
}
}
private static byte[] GetCompressedResponseBody(string encodingType, byte[] responseBodyStream)
private static async Task<byte[]> GetCompressedResponseBody(string encodingType, byte[] responseBodyStream)
{
var compressionFactory = new CompressionFactory();
var compressor = compressionFactory.Create(encodingType);
return compressor.Compress(responseBodyStream);
return await compressor.Compress(responseBodyStream).ConfigureAwait(false);
}
......@@ -132,7 +141,7 @@ namespace Titanium.Web.Proxy
headers.RemoveAll(x => x.Name.ToLower() == "proxy-connection");
}
private static void WriteResponseHeaders(StreamWriter responseWriter, List<HttpHeader> headers, int length,
private static async Task WriteResponseHeaders(StreamWriter responseWriter, List<HttpHeader> headers, int length,
bool isChunked)
{
FixResponseProxyHeaders(headers);
......@@ -152,25 +161,25 @@ namespace Titanium.Web.Proxy
if (!isChunked && header.Name.ToLower() == "content-length")
header.Value = length.ToString();
responseWriter.WriteLine(header.ToString());
await responseWriter.WriteLineAsync(header.ToString()).ConfigureAwait(false);
}
}
responseWriter.WriteLine();
responseWriter.Flush();
await responseWriter.WriteLineAsync().ConfigureAwait(false);
await responseWriter.FlushAsync().ConfigureAwait(false);
}
private static void WriteResponseBody(Stream clientStream, byte[] data, bool isChunked)
private static async Task WriteResponseBody(Stream clientStream, byte[] data, bool isChunked)
{
if (!isChunked)
{
clientStream.Write(data, 0, data.Length);
await clientStream.WriteAsync(data, 0, data.Length).ConfigureAwait(false);
}
else
WriteResponseBodyChunked(data, clientStream);
await WriteResponseBodyChunked(data, clientStream).ConfigureAwait(false);
}
private static void WriteResponseBody(CustomBinaryReader inStreamReader, Stream outStream, bool isChunked, long ContentLength)
private static async Task WriteResponseBody(CustomBinaryReader inStreamReader, Stream outStream, bool isChunked, long ContentLength)
{
if (!isChunked)
{
......@@ -184,9 +193,9 @@ namespace Titanium.Web.Proxy
var bytesRead = 0;
var totalBytesRead = 0;
while ((bytesRead += inStreamReader.BaseStream.Read(buffer, 0, bytesToRead)) > 0)
while ((bytesRead += await inStreamReader.BaseStream.ReadAsync(buffer, 0, bytesToRead).ConfigureAwait(false)) > 0)
{
outStream.Write(buffer, 0, bytesRead);
await outStream.WriteAsync(buffer, 0, bytesRead).ConfigureAwait(false);
totalBytesRead += bytesRead;
if (totalBytesRead == ContentLength)
......@@ -198,50 +207,50 @@ namespace Titanium.Web.Proxy
}
}
else
WriteResponseBodyChunked(inStreamReader, outStream);
await WriteResponseBodyChunked(inStreamReader, outStream).ConfigureAwait(false);
}
//Send chunked response
private static void WriteResponseBodyChunked(CustomBinaryReader inStreamReader, Stream outStream)
private static async Task WriteResponseBodyChunked(CustomBinaryReader inStreamReader, Stream outStream)
{
while (true)
{
var chuchkHead = inStreamReader.ReadLine();
var chunkSize = int.Parse(chuchkHead, NumberStyles.HexNumber);
var chunkHead = await inStreamReader.ReadLineAsync().ConfigureAwait(false);
var chunkSize = int.Parse(chunkHead, NumberStyles.HexNumber);
if (chunkSize != 0)
{
var buffer = inStreamReader.ReadBytes(chunkSize);
var buffer = await inStreamReader.ReadBytesAsync(chunkSize).ConfigureAwait(false);
var chunkHead = Encoding.ASCII.GetBytes(chunkSize.ToString("x2"));
var chunkHeadBytes = Encoding.ASCII.GetBytes(chunkSize.ToString("x2"));
outStream.Write(chunkHead, 0, chunkHead.Length);
outStream.Write(Constants.NewLineBytes, 0, Constants.NewLineBytes.Length);
await outStream.WriteAsync(chunkHeadBytes, 0, chunkHeadBytes.Length).ConfigureAwait(false);
await outStream.WriteAsync(Constants.NewLineBytes, 0, Constants.NewLineBytes.Length).ConfigureAwait(false);
outStream.Write(buffer, 0, chunkSize);
outStream.Write(Constants.NewLineBytes, 0, Constants.NewLineBytes.Length);
await outStream.WriteAsync(buffer, 0, chunkSize).ConfigureAwait(false);
await outStream.WriteAsync(Constants.NewLineBytes, 0, Constants.NewLineBytes.Length).ConfigureAwait(false);
inStreamReader.ReadLine();
await inStreamReader.ReadLineAsync().ConfigureAwait(false);
}
else
{
inStreamReader.ReadLine();
outStream.Write(Constants.ChunkEnd, 0, Constants.ChunkEnd.Length);
await inStreamReader.ReadLineAsync().ConfigureAwait(false);
await outStream.WriteAsync(Constants.ChunkEnd, 0, Constants.ChunkEnd.Length).ConfigureAwait(false);
break;
}
}
}
private static void WriteResponseBodyChunked(byte[] data, Stream outStream)
private static async Task WriteResponseBodyChunked(byte[] data, Stream outStream)
{
var chunkHead = Encoding.ASCII.GetBytes(data.Length.ToString("x2"));
outStream.Write(chunkHead, 0, chunkHead.Length);
outStream.Write(Constants.NewLineBytes, 0, Constants.NewLineBytes.Length);
outStream.Write(data, 0, data.Length);
outStream.Write(Constants.NewLineBytes, 0, Constants.NewLineBytes.Length);
await outStream.WriteAsync(chunkHead, 0, chunkHead.Length).ConfigureAwait(false);
await outStream.WriteAsync(Constants.NewLineBytes, 0, Constants.NewLineBytes.Length).ConfigureAwait(false);
await outStream.WriteAsync(data, 0, data.Length).ConfigureAwait(false);
await outStream.WriteAsync(Constants.NewLineBytes, 0, Constants.NewLineBytes.Length).ConfigureAwait(false);
outStream.Write(Constants.ChunkEnd, 0, Constants.ChunkEnd.Length);
await outStream.WriteAsync(Constants.ChunkEnd, 0, Constants.ChunkEnd.Length).ConfigureAwait(false);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment