Skip to content
This repository was archived by the owner on Jul 9, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 22 additions & 15 deletions Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
using System;
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;
Expand All @@ -10,12 +8,19 @@ namespace Titanium.Web.Proxy.Examples.Basic
{
public class ProxyTestController
{
private ProxyServer proxyServer;

public ProxyTestController()
{
proxyServer = new ProxyServer();
}

public void StartProxy()
{
ProxyServer.BeforeRequest += OnRequest;
ProxyServer.BeforeResponse += OnResponse;
ProxyServer.ServerCertificateValidationCallback += OnCertificateValidation;
ProxyServer.ClientCertificateSelectionCallback += OnCertificateSelection;
proxyServer.BeforeRequest += OnRequest;
proxyServer.BeforeResponse += OnResponse;
proxyServer.ServerCertificateValidationCallback += OnCertificateValidation;
proxyServer.ClientCertificateSelectionCallback += OnCertificateSelection;

//Exclude Https addresses you don't want to proxy
//Usefull for clients that use certificate pinning
Expand All @@ -27,8 +32,8 @@ public void StartProxy()

//An explicit endpoint is where the client knows about the existance of a proxy
//So client sends request in a proxy friendly manner
ProxyServer.AddEndPoint(explicitEndPoint);
ProxyServer.Start();
proxyServer.AddEndPoint(explicitEndPoint);
proxyServer.Start();


//Transparent endpoint is usefull for reverse proxying (client is not aware of the existance of proxy)
Expand All @@ -41,26 +46,28 @@ public void StartProxy()
{
GenericCertificateName = "google.com"
};
ProxyServer.AddEndPoint(transparentEndPoint);
proxyServer.AddEndPoint(transparentEndPoint);

//ProxyServer.UpStreamHttpProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 };
//ProxyServer.UpStreamHttpsProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 };

foreach (var endPoint in ProxyServer.ProxyEndPoints)
foreach (var endPoint in proxyServer.ProxyEndPoints)
Console.WriteLine("Listening on '{0}' endpoint at Ip {1} and port: {2} ",
endPoint.GetType().Name, endPoint.IpAddress, endPoint.Port);

//Only explicit proxies can be set as system proxy!
ProxyServer.SetAsSystemHttpProxy(explicitEndPoint);
ProxyServer.SetAsSystemHttpsProxy(explicitEndPoint);
proxyServer.SetAsSystemHttpProxy(explicitEndPoint);
proxyServer.SetAsSystemHttpsProxy(explicitEndPoint);
}

public void Stop()
{
ProxyServer.BeforeRequest -= OnRequest;
ProxyServer.BeforeResponse -= OnResponse;
proxyServer.BeforeRequest -= OnRequest;
proxyServer.BeforeResponse -= OnResponse;
proxyServer.ServerCertificateValidationCallback -= OnCertificateValidation;
proxyServer.ClientCertificateSelectionCallback -= OnCertificateSelection;

ProxyServer.Stop();
proxyServer.Stop();
}

//intecept & cancel, redirect or update requests
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ After installing nuget package mark following files to be copied to app director
Setup HTTP proxy:

```csharp
var ProxyServer = new ProxyServer();

ProxyServer.BeforeRequest += OnRequest;
ProxyServer.BeforeResponse += OnResponse;
ProxyServer.ServerCertificateValidationCallback += OnCertificateValidation;
Expand Down
4 changes: 2 additions & 2 deletions Titanium.Web.Proxy/CertificateHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public partial class ProxyServer
/// <param name="chain"></param>
/// <param name="sslPolicyErrors"></param>
/// <returns></returns>
internal static bool ValidateServerCertificate(
internal bool ValidateServerCertificate(
object sender,
X509Certificate certificate,
X509Chain chain,
Expand Down Expand Up @@ -62,7 +62,7 @@ internal static bool ValidateServerCertificate(
/// <param name="chain"></param>
/// <param name="sslPolicyErrors"></param>
/// <returns></returns>
internal static X509Certificate SelectClientCertificate(
internal X509Certificate SelectClientCertificate(
object sender,
string targetHost,
X509CertificateCollection localCertificates,
Expand Down
2 changes: 1 addition & 1 deletion Titanium.Web.Proxy/Decompression/DefaultDecompression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace Titanium.Web.Proxy.Decompression
/// </summary>
internal class DefaultDecompression : IDecompression
{
public Task<byte[]> Decompress(byte[] compressedArray)
public Task<byte[]> Decompress(byte[] compressedArray, int bufferSize)
{
return Task.FromResult(compressedArray);
}
Expand Down
4 changes: 2 additions & 2 deletions Titanium.Web.Proxy/Decompression/DeflateDecompression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ namespace Titanium.Web.Proxy.Decompression
/// </summary>
internal class DeflateDecompression : IDecompression
{
public async Task<byte[]> Decompress(byte[] compressedArray)
public async Task<byte[]> Decompress(byte[] compressedArray, int bufferSize)
{
var stream = new MemoryStream(compressedArray);

using (var decompressor = new DeflateStream(stream, CompressionMode.Decompress))
{
var buffer = new byte[ProxyConstants.BUFFER_SIZE];
var buffer = new byte[bufferSize];

using (var output = new MemoryStream())
{
Expand Down
4 changes: 2 additions & 2 deletions Titanium.Web.Proxy/Decompression/GZipDecompression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ namespace Titanium.Web.Proxy.Decompression
/// </summary>
internal class GZipDecompression : IDecompression
{
public async Task<byte[]> Decompress(byte[] compressedArray)
public async Task<byte[]> Decompress(byte[] compressedArray, int bufferSize)
{
using (var decompressor = new GZipStream(new MemoryStream(compressedArray), CompressionMode.Decompress))
{
var buffer = new byte[ProxyConstants.BUFFER_SIZE];
var buffer = new byte[bufferSize];
using (var output = new MemoryStream())
{
int read;
Expand Down
2 changes: 1 addition & 1 deletion Titanium.Web.Proxy/Decompression/IDecompression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ namespace Titanium.Web.Proxy.Decompression
/// </summary>
internal interface IDecompression
{
Task<byte[]> Decompress(byte[] compressedArray);
Task<byte[]> Decompress(byte[] compressedArray, int bufferSize);
}
}
4 changes: 2 additions & 2 deletions Titanium.Web.Proxy/Decompression/ZlibDecompression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ namespace Titanium.Web.Proxy.Decompression
/// </summary>
internal class ZlibDecompression : IDecompression
{
public async Task<byte[]> Decompress(byte[] compressedArray)
public async Task<byte[]> Decompress(byte[] compressedArray, int bufferSize)
{
var memoryStream = new MemoryStream(compressedArray);
using (var decompressor = new ZlibStream(memoryStream, CompressionMode.Decompress))
{
var buffer = new byte[ProxyConstants.BUFFER_SIZE];
var buffer = new byte[bufferSize];

using (var output = new MemoryStream())
{
Expand Down
48 changes: 31 additions & 17 deletions Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,16 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary>
public class SessionEventArgs : EventArgs, IDisposable
{

/// <summary>
/// Constructor to initialize the proxy
/// Size of Buffers used by this object
/// </summary>
internal SessionEventArgs()
{
ProxyClient = new ProxyClient();
WebSession = new HttpWebClient();
}
private readonly int bufferSize;

/// <summary>
/// Holds a reference to proxy response handler method
/// </summary>
private readonly Func<SessionEventArgs, Task> httpResponseHandler;

/// <summary>
/// Holds a reference to client
Expand All @@ -50,13 +52,18 @@ internal SessionEventArgs()


/// <summary>
/// implement any cleanup here
/// Constructor to initialize the proxy
/// </summary>
public void Dispose()
internal SessionEventArgs(int bufferSize, Func<SessionEventArgs, Task> httpResponseHandler)
{
this.bufferSize = bufferSize;
this.httpResponseHandler = httpResponseHandler;

ProxyClient = new ProxyClient();
WebSession = new HttpWebClient();
}


/// <summary>
/// Read request body content as bytes[] for current session
/// </summary>
Expand All @@ -80,19 +87,19 @@ private async Task ReadRequestBody()
//For chunked request we need to read data as they arrive, until we reach a chunk end symbol
if (WebSession.Request.IsChunked)
{
await this.ProxyClient.ClientStreamReader.CopyBytesToStreamChunked(requestBodyStream);
await this.ProxyClient.ClientStreamReader.CopyBytesToStreamChunked(bufferSize, requestBodyStream);
}
else
{
//If not chunked then its easy just read the whole body with the content length mentioned in the request header
if (WebSession.Request.ContentLength > 0)
{
//If not chunked then its easy just read the amount of bytes mentioned in content length header of response
await this.ProxyClient.ClientStreamReader.CopyBytesToStream(requestBodyStream, WebSession.Request.ContentLength);
await this.ProxyClient.ClientStreamReader.CopyBytesToStream(bufferSize, requestBodyStream, WebSession.Request.ContentLength);

}
else if(WebSession.Request.HttpVersion.Major == 1 && WebSession.Request.HttpVersion.Minor == 0)
await WebSession.ServerConnection.StreamReader.CopyBytesToStream(requestBodyStream, long.MaxValue);
await WebSession.ServerConnection.StreamReader.CopyBytesToStream(bufferSize, requestBodyStream, long.MaxValue);
}
WebSession.Request.RequestBody = await GetDecompressedResponseBody(WebSession.Request.ContentEncoding, requestBodyStream.ToArray());
}
Expand All @@ -117,18 +124,18 @@ private async Task ReadResponseBody()
//If chuncked the read chunk by chunk until we hit chunk end symbol
if (WebSession.Response.IsChunked)
{
await WebSession.ServerConnection.StreamReader.CopyBytesToStreamChunked(responseBodyStream);
await WebSession.ServerConnection.StreamReader.CopyBytesToStreamChunked(bufferSize, responseBodyStream);
}
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
await WebSession.ServerConnection.StreamReader.CopyBytesToStream(responseBodyStream, WebSession.Response.ContentLength);
await WebSession.ServerConnection.StreamReader.CopyBytesToStream(bufferSize, responseBodyStream, WebSession.Response.ContentLength);

}
else if(WebSession.Response.HttpVersion.Major == 1 && WebSession.Response.HttpVersion.Minor == 0)
await WebSession.ServerConnection.StreamReader.CopyBytesToStream(responseBodyStream, long.MaxValue);
await WebSession.ServerConnection.StreamReader.CopyBytesToStream(bufferSize, responseBodyStream, long.MaxValue);
}

WebSession.Response.ResponseBody = await GetDecompressedResponseBody(WebSession.Response.ContentEncoding, responseBodyStream.ToArray());
Expand Down Expand Up @@ -285,7 +292,7 @@ private async Task<byte[]> GetDecompressedResponseBody(string encodingType, byte
var decompressionFactory = new DecompressionFactory();
var decompressor = decompressionFactory.Create(encodingType);

return await decompressor.Decompress(responseBodyStream);
return await decompressor.Decompress(responseBodyStream, bufferSize);
}


Expand Down Expand Up @@ -338,7 +345,7 @@ public async Task Redirect(string url)

WebSession.Request.CancelRequest = true;
}

/// a generic responder method
public async Task Respond(Response response)
{
Expand All @@ -349,8 +356,15 @@ public async Task Respond(Response response)

WebSession.Response = response;

await ProxyServer.HandleHttpSessionResponse(this);
await httpResponseHandler(this);
}

/// <summary>
/// implement any cleanup here
/// </summary>
public void Dispose()
{

}
}
}
28 changes: 14 additions & 14 deletions Titanium.Web.Proxy/Extensions/StreamExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,22 @@ internal static async Task CopyToAsync(this Stream input, string initialData, St
/// <param name="stream"></param>
/// <param name="totalBytesToRead"></param>
/// <returns></returns>
internal static async Task CopyBytesToStream(this CustomBinaryReader streamReader, Stream stream, long totalBytesToRead)
internal static async Task CopyBytesToStream(this CustomBinaryReader streamReader, int bufferSize, Stream stream, long totalBytesToRead)
{
var totalbytesRead = 0;

long bytesToRead;
if (totalBytesToRead < ProxyConstants.BUFFER_SIZE)
if (totalBytesToRead < bufferSize)
{
bytesToRead = totalBytesToRead;
}
else
bytesToRead = ProxyConstants.BUFFER_SIZE;
bytesToRead = bufferSize;


while (totalbytesRead < totalBytesToRead)
{
var buffer = await streamReader.ReadBytesAsync(bytesToRead);
var buffer = await streamReader.ReadBytesAsync(bufferSize, bytesToRead);

if (buffer.Length == 0)
break;
Expand All @@ -72,7 +72,7 @@ internal static async Task CopyBytesToStream(this CustomBinaryReader streamReade
/// <param name="clientStreamReader"></param>
/// <param name="stream"></param>
/// <returns></returns>
internal static async Task CopyBytesToStreamChunked(this CustomBinaryReader clientStreamReader, Stream stream)
internal static async Task CopyBytesToStreamChunked(this CustomBinaryReader clientStreamReader, int bufferSize, Stream stream)
{
while (true)
{
Expand All @@ -81,7 +81,7 @@ internal static async Task CopyBytesToStreamChunked(this CustomBinaryReader clie

if (chunkSize != 0)
{
var buffer = await clientStreamReader.ReadBytesAsync(chunkSize);
var buffer = await clientStreamReader.ReadBytesAsync(bufferSize, chunkSize);
await stream.WriteAsync(buffer, 0, buffer.Length);
//chunk trail
await clientStreamReader.ReadLineAsync();
Expand Down Expand Up @@ -118,20 +118,20 @@ internal static async Task WriteResponseBody(this Stream clientStream, byte[] da
/// <param name="isChunked"></param>
/// <param name="ContentLength"></param>
/// <returns></returns>
internal static async Task WriteResponseBody(this CustomBinaryReader inStreamReader, Stream outStream, bool isChunked, long ContentLength)
internal static async Task WriteResponseBody(this CustomBinaryReader inStreamReader, int bufferSize, Stream outStream, bool isChunked, long ContentLength)
{
if (!isChunked)
{
//http 1.0
if (ContentLength == -1)
ContentLength = long.MaxValue;

int bytesToRead = ProxyConstants.BUFFER_SIZE;
int bytesToRead = bufferSize;

if (ContentLength < ProxyConstants.BUFFER_SIZE)
if (ContentLength < bufferSize)
bytesToRead = (int)ContentLength;

var buffer = new byte[ProxyConstants.BUFFER_SIZE];
var buffer = new byte[bufferSize];

var bytesRead = 0;
var totalBytesRead = 0;
Expand All @@ -146,11 +146,11 @@ internal static async Task WriteResponseBody(this CustomBinaryReader inStreamRea

bytesRead = 0;
var remainingBytes = (ContentLength - totalBytesRead);
bytesToRead = remainingBytes > (long)ProxyConstants.BUFFER_SIZE ? ProxyConstants.BUFFER_SIZE : (int)remainingBytes;
bytesToRead = remainingBytes > (long)bufferSize ? bufferSize : (int)remainingBytes;
}
}
else
await WriteResponseBodyChunked(inStreamReader, outStream);
await WriteResponseBodyChunked(inStreamReader, bufferSize, outStream);
}

/// <summary>
Expand All @@ -159,7 +159,7 @@ internal static async Task WriteResponseBody(this CustomBinaryReader inStreamRea
/// <param name="inStreamReader"></param>
/// <param name="outStream"></param>
/// <returns></returns>
internal static async Task WriteResponseBodyChunked(this CustomBinaryReader inStreamReader, Stream outStream)
internal static async Task WriteResponseBodyChunked(this CustomBinaryReader inStreamReader, int bufferSize, Stream outStream)
{
while (true)
{
Expand All @@ -168,7 +168,7 @@ internal static async Task WriteResponseBodyChunked(this CustomBinaryReader inSt

if (chunkSize != 0)
{
var buffer = await inStreamReader.ReadBytesAsync(chunkSize);
var buffer = await inStreamReader.ReadBytesAsync(bufferSize, chunkSize);

var chunkHeadBytes = Encoding.ASCII.GetBytes(chunkSize.ToString("x2"));

Expand Down
Loading