Commit bbbb0c57 authored by Jehonathan Thomas's avatar Jehonathan Thomas Committed by GitHub

Merge pull request #244 from justcoding121/beta

Stable
parents 5a683862 a3562f03
using System;
using System.Runtime.InteropServices;
namespace Titanium.Web.Proxy.Examples.Basic.Helpers
{
/// <summary>
/// Adapated from
/// http://stackoverflow.com/questions/13656846/how-to-programmatic-disable-c-sharp-console-applications-quick-edit-mode
/// </summary>
internal static class ConsoleHelper
{
const uint ENABLE_QUICK_EDIT = 0x0040;
// STD_INPUT_HANDLE (DWORD): -10 is the standard input device.
const int STD_INPUT_HANDLE = -10;
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetStdHandle(int nStdHandle);
[DllImport("kernel32.dll")]
static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode);
[DllImport("kernel32.dll")]
static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode);
internal static bool DisableQuickEditMode()
{
IntPtr consoleHandle = GetStdHandle(STD_INPUT_HANDLE);
// get current console mode
uint consoleMode;
if (!GetConsoleMode(consoleHandle, out consoleMode))
{
// ERROR: Unable to get console mode.
return false;
}
// Clear the quick edit bit in the mode flags
consoleMode &= ~ENABLE_QUICK_EDIT;
// set the new mode
if (!SetConsoleMode(consoleHandle, consoleMode))
{
// ERROR: Unable to set console mode
return false;
}
return true;
}
}
}
using System; using System;
using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Titanium.Web.Proxy.Examples.Basic.Helpers;
namespace Titanium.Web.Proxy.Examples.Basic namespace Titanium.Web.Proxy.Examples.Basic
{ {
...@@ -9,11 +11,13 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -9,11 +11,13 @@ namespace Titanium.Web.Proxy.Examples.Basic
public static void Main(string[] args) public static void Main(string[] args)
{ {
//fix console hang due to QuickEdit mode
ConsoleHelper.DisableQuickEditMode();
//On Console exit make sure we also exit the proxy //On Console exit make sure we also exit the proxy
NativeMethods.Handler = ConsoleEventCallback; NativeMethods.Handler = ConsoleEventCallback;
NativeMethods.SetConsoleCtrlHandler(NativeMethods.Handler, true); NativeMethods.SetConsoleCtrlHandler(NativeMethods.Handler, true);
//Start proxy controller //Start proxy controller
controller.StartProxy(); controller.StartProxy();
...@@ -50,5 +54,7 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -50,5 +54,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
// Pinvoke // Pinvoke
internal delegate bool ConsoleEventDelegate(int eventType); internal delegate bool ConsoleEventDelegate(int eventType);
} }
}
\ No newline at end of file }
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Net; using System.Net;
using System.Net.Security;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
...@@ -47,18 +47,18 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -47,18 +47,18 @@ namespace Titanium.Web.Proxy.Examples.Basic
//Exclude Https addresses you don't want to proxy //Exclude Https addresses you don't want to proxy
//Useful for clients that use certificate pinning //Useful for clients that use certificate pinning
//for example google.com and dropbox.com //for example google.com and dropbox.com
ExcludedHttpsHostNameRegex = new List<string>() { "dropbox.com" } ExcludedHttpsHostNameRegex = new List<string> { "dropbox.com" }
//Include Https addresses you want to proxy (others will be excluded) //Include Https addresses you want to proxy (others will be excluded)
//for example github.com //for example github.com
// IncludedHttpsHostNameRegex = new List<string>() { "github.com" } //IncludedHttpsHostNameRegex = new List<string> { "github.com" }
//You can set only one of the ExcludedHttpsHostNameRegex and IncludedHttpsHostNameRegex properties, otherwise ArgumentException will be thrown //You can set only one of the ExcludedHttpsHostNameRegex and IncludedHttpsHostNameRegex properties, otherwise ArgumentException will be thrown
//Use self-issued generic certificate on all https requests //Use self-issued generic certificate on all https requests
//Optimizes performance by not creating a certificate for each https-enabled domain //Optimizes performance by not creating a certificate for each https-enabled domain
//Useful when certificate trust is not required by proxy clients //Useful when certificate trust is not required by proxy clients
// GenericCertificate = new X509Certificate2(Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "genericcert.pfx"), "password") //GenericCertificate = new X509Certificate2(Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "genericcert.pfx"), "password")
}; };
//An explicit endpoint is where the client knows about the existence of a proxy //An explicit endpoint is where the client knows about the existence of a proxy
...@@ -107,7 +107,7 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -107,7 +107,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
//intecept & cancel redirect or update requests //intecept & cancel redirect or update requests
public async Task OnRequest(object sender, SessionEventArgs e) public async Task OnRequest(object sender, SessionEventArgs e)
{ {
Console.WriteLine("Active Client Connections:" + ((ProxyServer) sender).ClientConnectionCount); Console.WriteLine("Active Client Connections:" + ((ProxyServer)sender).ClientConnectionCount);
Console.WriteLine(e.WebSession.Request.Url); Console.WriteLine(e.WebSession.Request.Url);
//read request headers //read request headers
...@@ -150,7 +150,7 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -150,7 +150,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
//Modify response //Modify response
public async Task OnResponse(object sender, SessionEventArgs e) public async Task OnResponse(object sender, SessionEventArgs e)
{ {
Console.WriteLine("Active Server Connections:" + (sender as ProxyServer).ServerConnectionCount); Console.WriteLine("Active Server Connections:" + ((ProxyServer)sender).ServerConnectionCount);
if (requestBodyHistory.ContainsKey(e.Id)) if (requestBodyHistory.ContainsKey(e.Id))
{ {
...@@ -189,7 +189,7 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -189,7 +189,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
public Task OnCertificateValidation(object sender, CertificateValidationEventArgs e) public Task OnCertificateValidation(object sender, CertificateValidationEventArgs e)
{ {
//set IsValid to true/false based on Certificate Errors //set IsValid to true/false based on Certificate Errors
if (e.SslPolicyErrors == System.Net.Security.SslPolicyErrors.None) if (e.SslPolicyErrors == SslPolicyErrors.None)
{ {
e.IsValid = true; e.IsValid = true;
} }
......
...@@ -55,6 +55,7 @@ ...@@ -55,6 +55,7 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Helpers\ConsoleHelper.cs" />
<Compile Include="Program.cs" /> <Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ProxyTestController.cs" /> <Compile Include="ProxyTestController.cs" />
......
...@@ -17,8 +17,7 @@ Features ...@@ -17,8 +17,7 @@ Features
* Safely relays Web Socket requests over HTTP * Safely relays Web Socket requests over HTTP
* Support mutual SSL authentication * Support mutual SSL authentication
* Fully asynchronous proxy * Fully asynchronous proxy
* Supports proxy authentication * Supports proxy authentication & automatic proxy detection
Usage Usage
===== =====
...@@ -204,10 +203,8 @@ public Task OnCertificateSelection(object sender, CertificateSelectionEventArgs ...@@ -204,10 +203,8 @@ public Task OnCertificateSelection(object sender, CertificateSelectionEventArgs
``` ```
Future road map (Pull requests are welcome!) Future road map (Pull requests are welcome!)
============ ============
* Implement Kerberos/NTLM authentication over HTTP protocols for windows domain
* Support Server Name Indication (SNI) for transparent endpoints * Support Server Name Indication (SNI) for transparent endpoints
* Support HTTP 2.0 * Support HTTP 2.0
* Support upstream AutoProxy detection
* Support SOCKS protocol * Support SOCKS protocol
* Implement Kerberos/NTLM authentication over HTTP protocols for windows domain
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following // General Information about an assembly is controlled through the following
......
using System; using System;
using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Diagnostics;
using System.Net; using System.Net;
using System.Net.Http;
using System.Net.Security;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using System.Net.Http;
using System.Diagnostics;
namespace Titanium.Web.Proxy.IntegrationTests namespace Titanium.Web.Proxy.IntegrationTests
{ {
...@@ -13,7 +14,6 @@ namespace Titanium.Web.Proxy.IntegrationTests ...@@ -13,7 +14,6 @@ namespace Titanium.Web.Proxy.IntegrationTests
public class SslTests public class SslTests
{ {
[TestMethod] [TestMethod]
public void TestSsl() public void TestSsl()
{ {
//expand this to stress test to find //expand this to stress test to find
...@@ -103,7 +103,7 @@ namespace Titanium.Web.Proxy.IntegrationTests ...@@ -103,7 +103,7 @@ namespace Titanium.Web.Proxy.IntegrationTests
public Task OnCertificateValidation(object sender, CertificateValidationEventArgs e) public Task OnCertificateValidation(object sender, CertificateValidationEventArgs e)
{ {
//set IsValid to true/false based on Certificate Errors //set IsValid to true/false based on Certificate Errors
if (e.SslPolicyErrors == System.Net.Security.SslPolicyErrors.None) if (e.SslPolicyErrors == SslPolicyErrors.None)
{ {
e.IsValid = true; e.IsValid = true;
} }
......
using System; using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using Titanium.Web.Proxy.Network; using Titanium.Web.Proxy.Network;
using System.Threading.Tasks;
using System.Collections.Generic;
namespace Titanium.Web.Proxy.UnitTests namespace Titanium.Web.Proxy.UnitTests
{ {
...@@ -10,8 +10,7 @@ namespace Titanium.Web.Proxy.UnitTests ...@@ -10,8 +10,7 @@ namespace Titanium.Web.Proxy.UnitTests
public class CertificateManagerTests public class CertificateManagerTests
{ {
private static readonly string[] hostNames private static readonly string[] hostNames
= new string[] { "facebook.com", "youtube.com", "google.com", = { "facebook.com", "youtube.com", "google.com", "bing.com", "yahoo.com" };
"bing.com", "yahoo.com"};
private readonly Random random = new Random(); private readonly Random random = new Random();
...@@ -36,16 +35,13 @@ namespace Titanium.Web.Proxy.UnitTests ...@@ -36,16 +35,13 @@ namespace Titanium.Web.Proxy.UnitTests
var certificate = mgr.CreateCertificate(host, false); var certificate = mgr.CreateCertificate(host, false);
Assert.IsNotNull(certificate); Assert.IsNotNull(certificate);
})); }));
} }
} }
await Task.WhenAll(tasks.ToArray()); await Task.WhenAll(tasks.ToArray());
mgr.StopClearIdleCertificates(); mgr.StopClearIdleCertificates();
} }
} }
} }
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/LINE_FEED_AT_FILE_END/@EntryValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/LINE_FEED_AT_FILE_END/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_AFTER_TYPECAST_PARENTHESES/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHIN_SINGLE_LINE_ARRAY_INITIALIZER_BRACES/@EntryValue">True</s:Boolean>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_LIMIT/@EntryValue">240</s:Int64>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=BC/@EntryIndexedValue">BC</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=BC/@EntryIndexedValue">BC</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=CN/@EntryIndexedValue">CN</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=DN/@EntryIndexedValue">DN</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=EKU/@EntryIndexedValue">EKU</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=KU/@EntryIndexedValue">KU</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MTA/@EntryIndexedValue">MTA</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=OID/@EntryIndexedValue">OID</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=OIDS/@EntryIndexedValue">OIDS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateConstants/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateConstants/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateInstanceFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateInstanceFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticReadonly/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticReadonly/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=dda2ffa1_002D435c_002D4111_002D88eb_002D1a7c93c382f0/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static, Instance" AccessRightKinds="Private" Description="Property (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="PROPERTY" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String></wpf:ResourceDictionary> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=dda2ffa1_002D435c_002D4111_002D88eb_002D1a7c93c382f0/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static, Instance" AccessRightKinds="Private" Description="Property (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="PROPERTY" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
\ No newline at end of file <s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
\ No newline at end of file
using System; using System;
using System.Net.Security; using System.Net.Security;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Extensions;
namespace Titanium.Web.Proxy namespace Titanium.Web.Proxy
{ {
...@@ -32,17 +32,8 @@ namespace Titanium.Web.Proxy ...@@ -32,17 +32,8 @@ namespace Titanium.Web.Proxy
SslPolicyErrors = sslPolicyErrors SslPolicyErrors = sslPolicyErrors
}; };
//why is the sender null?
Delegate[] invocationList = ServerCertificateValidationCallback.GetInvocationList(); ServerCertificateValidationCallback.InvokeParallel(this, args);
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();
return args.IsValid; return args.IsValid;
} }
...@@ -108,17 +99,8 @@ namespace Titanium.Web.Proxy ...@@ -108,17 +99,8 @@ namespace Titanium.Web.Proxy
ClientCertificate = clientCertificate ClientCertificate = clientCertificate
}; };
//why is the sender null?
Delegate[] invocationList = ClientCertificateSelectionCallback.GetInvocationList(); ClientCertificateSelectionCallback.InvokeParallel(this, args);
Task[] handlerTasks = new Task[invocationList.Length];
for (int i = 0; i < invocationList.Length; i++)
{
handlerTasks[i] = ((Func<object, CertificateSelectionEventArgs, Task>) invocationList[i])(null, args);
}
Task.WhenAll(handlerTasks).Wait();
return args.ClientCertificate; return args.ClientCertificate;
} }
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net;
using System.Text; using System.Text;
using Titanium.Web.Proxy.Exceptions; using System.Threading.Tasks;
using Titanium.Web.Proxy.Decompression; using Titanium.Web.Proxy.Decompression;
using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Http.Responses; using Titanium.Web.Proxy.Http.Responses;
using Titanium.Web.Proxy.Extensions;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Network;
using System.Net;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network;
namespace Titanium.Web.Proxy.EventArguments namespace Titanium.Web.Proxy.EventArguments
{ {
...@@ -30,7 +30,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -30,7 +30,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary> /// <summary>
/// Holds a reference to proxy response handler method /// Holds a reference to proxy response handler method
/// </summary> /// </summary>
private readonly Func<SessionEventArgs, Task> httpResponseHandler; private Func<SessionEventArgs, Task> httpResponseHandler;
/// <summary> /// <summary>
/// Holds a reference to client /// Holds a reference to client
...@@ -56,7 +56,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -56,7 +56,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary> /// <summary>
/// Client End Point. /// Client End Point.
/// </summary> /// </summary>
public IPEndPoint ClientEndPoint => (IPEndPoint) ProxyClient.TcpClient.Client.RemoteEndPoint; public IPEndPoint ClientEndPoint => (IPEndPoint)ProxyClient.TcpClient.Client.RemoteEndPoint;
/// <summary> /// <summary>
/// A web session corresponding to a single request/response sequence /// A web session corresponding to a single request/response sequence
...@@ -158,7 +158,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -158,7 +158,7 @@ namespace Titanium.Web.Proxy.EventArguments
await WebSession.ServerConnection.StreamReader.CopyBytesToStream(bufferSize, responseBodyStream, await WebSession.ServerConnection.StreamReader.CopyBytesToStream(bufferSize, responseBodyStream,
WebSession.Response.ContentLength); WebSession.Response.ContentLength);
} }
else if ((WebSession.Response.HttpVersion.Major == 1 && WebSession.Response.HttpVersion.Minor == 0) || WebSession.Response.ContentLength == -1) else if (WebSession.Response.HttpVersion.Major == 1 && WebSession.Response.HttpVersion.Minor == 0 || WebSession.Response.ContentLength == -1)
{ {
await WebSession.ServerConnection.StreamReader.CopyBytesToStream(bufferSize, responseBodyStream, long.MaxValue); await WebSession.ServerConnection.StreamReader.CopyBytesToStream(bufferSize, responseBodyStream, long.MaxValue);
} }
...@@ -522,6 +522,11 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -522,6 +522,11 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{ {
httpResponseHandler = null;
CustomUpStreamHttpProxyUsed = null;
CustomUpStreamHttpsProxyUsed = null;
WebSession.Dispose();
} }
} }
} }
using System;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Extensions
{
internal static class FuncExtensions
{
public static void InvokeParallel<T>(this Func<object, T, Task> callback, object sender, T args)
{
Delegate[] invocationList = callback.GetInvocationList();
Task[] handlerTasks = new Task[invocationList.Length];
for (int i = 0; i < invocationList.Length; i++)
{
handlerTasks[i] = ((Func<object, T, Task>)invocationList[i])(sender, args);
}
Task.WhenAll(handlerTasks).Wait();
}
public static async Task InvokeParallelAsync<T>(this Func<object, T, Task> callback, object sender, T args)
{
Delegate[] invocationList = callback.GetInvocationList();
Task[] handlerTasks = new Task[invocationList.Length];
for (int i = 0; i < invocationList.Length; i++)
{
handlerTasks[i] = ((Func<object, T, Task>)invocationList[i])(sender, args);
}
await Task.WhenAll(handlerTasks);
}
}
}
...@@ -136,7 +136,7 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -136,7 +136,7 @@ namespace Titanium.Web.Proxy.Extensions
if (contentLength < bufferSize) if (contentLength < bufferSize)
{ {
bytesToRead = (int) contentLength; bytesToRead = (int)contentLength;
} }
var buffer = new byte[bufferSize]; var buffer = new byte[bufferSize];
...@@ -153,8 +153,8 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -153,8 +153,8 @@ namespace Titanium.Web.Proxy.Extensions
break; break;
bytesRead = 0; bytesRead = 0;
var remainingBytes = (contentLength - totalBytesRead); var remainingBytes = contentLength - totalBytesRead;
bytesToRead = remainingBytes > (long) bufferSize ? bufferSize : (int) remainingBytes; bytesToRead = remainingBytes > (long)bufferSize ? bufferSize : (int)remainingBytes;
} }
} }
else else
......
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;
...@@ -18,10 +19,18 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -18,10 +19,18 @@ namespace Titanium.Web.Proxy.Helpers
private readonly byte[] staticBuffer; private readonly byte[] staticBuffer;
private readonly Encoding encoding; private readonly Encoding encoding;
private static readonly ConcurrentQueue<byte[]> buffers
= new ConcurrentQueue<byte[]>();
private volatile bool disposed;
internal CustomBinaryReader(CustomBufferedStream stream, int bufferSize) internal CustomBinaryReader(CustomBufferedStream stream, int bufferSize)
{ {
this.stream = stream; this.stream = stream;
staticBuffer = new byte[bufferSize]; if (!buffers.TryDequeue(out staticBuffer) || staticBuffer.Length != bufferSize)
{
staticBuffer = new byte[bufferSize];
}
this.bufferSize = bufferSize; this.bufferSize = bufferSize;
...@@ -112,7 +121,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -112,7 +121,7 @@ namespace Titanium.Web.Proxy.Helpers
var buffer = staticBuffer; var buffer = staticBuffer;
if (totalBytesToRead < bufferSize) if (totalBytesToRead < bufferSize)
{ {
bytesToRead = (int) totalBytesToRead; bytesToRead = (int)totalBytesToRead;
buffer = new byte[bytesToRead]; buffer = new byte[bytesToRead];
} }
...@@ -127,7 +136,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -127,7 +136,7 @@ namespace Titanium.Web.Proxy.Helpers
break; break;
var remainingBytes = totalBytesToRead - totalBytesRead; var remainingBytes = totalBytesToRead - totalBytesRead;
bytesToRead = Math.Min(bufferSize, (int) remainingBytes); bytesToRead = Math.Min(bufferSize, (int)remainingBytes);
if (totalBytesRead + bytesToRead > buffer.Length) if (totalBytesRead + bytesToRead > buffer.Length)
{ {
...@@ -148,8 +157,18 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -148,8 +157,18 @@ namespace Titanium.Web.Proxy.Helpers
public void Dispose() public void Dispose()
{ {
if (!disposed)
{
disposed = true;
buffers.Enqueue(staticBuffer);
}
} }
/// <summary>
/// Increase size of buffer and copy existing content to new buffer
/// </summary>
/// <param name="buffer"></param>
/// <param name="size"></param>
private void ResizeBuffer(ref byte[] buffer, long size) private void ResizeBuffer(ref byte[] buffer, long size)
{ {
var newBuffer = new byte[size]; var newBuffer = new byte[size];
......
...@@ -198,7 +198,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -198,7 +198,7 @@ namespace Titanium.Web.Proxy.Helpers
{ {
if (asyncResult is ReadAsyncResult) if (asyncResult is ReadAsyncResult)
{ {
return ((ReadAsyncResult) asyncResult).ReadBytes; return ((ReadAsyncResult)asyncResult).ReadBytes;
} }
return baseStream.EndRead(asyncResult); return baseStream.EndRead(asyncResult);
......
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
namespace Titanium.Web.Proxy.Helpers namespace Titanium.Web.Proxy.Helpers
{ {
internal class NetworkHelper internal class NetworkHelper
{ {
private static int FindProcessIdFromLocalPort(int port, IpVersion ipVersion) private static int FindProcessIdFromLocalPort(int port, IpVersion ipVersion)
{ {
var tcpRow = TcpHelper.GetTcpRowByLocalPort(ipVersion, port); var tcpRow = TcpHelper.GetTcpRowByLocalPort(ipVersion, port);
return tcpRow?.ProcessId ?? 0; return tcpRow?.ProcessId ?? 0;
} }
internal static int GetProcessIdFromPort(int port, bool ipV6Enabled) internal static int GetProcessIdFromPort(int port, bool ipV6Enabled)
{ {
var processId = FindProcessIdFromLocalPort(port, IpVersion.Ipv4); var processId = FindProcessIdFromLocalPort(port, IpVersion.Ipv4);
if (processId > 0 && !ipV6Enabled) if (processId > 0 && !ipV6Enabled)
{ {
return processId; return processId;
} }
return FindProcessIdFromLocalPort(port, IpVersion.Ipv6); return FindProcessIdFromLocalPort(port, IpVersion.Ipv6);
} }
/// <summary> /// <summary>
/// Adapated from below link /// Adapated from below link
/// http://stackoverflow.com/questions/11834091/how-to-check-if-localhost /// http://stackoverflow.com/questions/11834091/how-to-check-if-localhost
/// </summary> /// </summary>
/// <param name="address"></param> /// <param name="address"></param>
/// <returns></returns> /// <returns></returns>
internal static bool IsLocalIpAddress(IPAddress address) internal static bool IsLocalIpAddress(IPAddress address)
{ {
// get local IP addresses // get local IP addresses
var localIPs = Dns.GetHostAddresses(Dns.GetHostName()); var localIPs = Dns.GetHostAddresses(Dns.GetHostName());
// test if any host IP equals to any local IP or to localhost // test if any host IP equals to any local IP or to localhost
return IPAddress.IsLoopback(address) || localIPs.Contains(address); return IPAddress.IsLoopback(address) || localIPs.Contains(address);
} }
internal static bool IsLocalIpAddress(string hostName) internal static bool IsLocalIpAddress(string hostName)
{ {
bool isLocalhost = false; bool isLocalhost = false;
IPHostEntry localhost = Dns.GetHostEntry("127.0.0.1"); IPHostEntry localhost = Dns.GetHostEntry("127.0.0.1");
if (hostName == localhost.HostName) if (hostName == localhost.HostName)
{ {
IPHostEntry hostEntry = Dns.GetHostEntry(hostName); IPHostEntry hostEntry = Dns.GetHostEntry(hostName);
isLocalhost = hostEntry.AddressList.Any(IPAddress.IsLoopback); isLocalhost = hostEntry.AddressList.Any(IPAddress.IsLoopback);
} }
if (!isLocalhost) if (!isLocalhost)
{ {
localhost = Dns.GetHostEntry(Dns.GetHostName()); localhost = Dns.GetHostEntry(Dns.GetHostName());
IPAddress ipAddress; IPAddress ipAddress;
if (IPAddress.TryParse(hostName, out ipAddress)) if (IPAddress.TryParse(hostName, out ipAddress))
isLocalhost = localhost.AddressList.Any(x => x.Equals(ipAddress)); isLocalhost = localhost.AddressList.Any(x => x.Equals(ipAddress));
if (!isLocalhost) if (!isLocalhost)
{ {
try try
{ {
var hostEntry = Dns.GetHostEntry(hostName); var hostEntry = Dns.GetHostEntry(hostName);
isLocalhost = localhost.AddressList.Any(x => hostEntry.AddressList.Any(x.Equals)); isLocalhost = localhost.AddressList.Any(x => hostEntry.AddressList.Any(x.Equals));
} }
catch (SocketException) catch (SocketException)
{ {
} }
} }
} }
return isLocalhost; return isLocalhost;
} }
} }
} }
using System; using System;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using System.Text.RegularExpressions;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using Microsoft.Win32;
// Helper classes for setting system proxy settings // Helper classes for setting system proxy settings
namespace Titanium.Web.Proxy.Helpers namespace Titanium.Web.Proxy.Helpers
...@@ -84,7 +84,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -84,7 +84,7 @@ namespace Titanium.Web.Proxy.Helpers
var exisitingContent = reg.GetValue("ProxyServer") as string; var exisitingContent = reg.GetValue("ProxyServer") as string;
var existingSystemProxyValues = GetSystemProxyValues(exisitingContent); var existingSystemProxyValues = GetSystemProxyValues(exisitingContent);
existingSystemProxyValues.RemoveAll(x => protocolType == ProxyProtocolType.Https ? x.IsHttps : !x.IsHttps); existingSystemProxyValues.RemoveAll(x => protocolType == ProxyProtocolType.Https ? x.IsHttps : !x.IsHttps);
existingSystemProxyValues.Add(new HttpSystemProxyValue() existingSystemProxyValues.Add(new HttpSystemProxyValue
{ {
HostName = hostname, HostName = hostname,
IsHttps = protocolType == ProxyProtocolType.Https, IsHttps = protocolType == ProxyProtocolType.Https,
......
...@@ -3,10 +3,9 @@ using System.Collections.Generic; ...@@ -3,10 +3,9 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net.NetworkInformation; using System.Net.NetworkInformation;
using System.Net.Security;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Security.Authentication;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
...@@ -15,8 +14,6 @@ using Titanium.Web.Proxy.Shared; ...@@ -15,8 +14,6 @@ using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Helpers namespace Titanium.Web.Proxy.Helpers
{ {
using System.Net;
internal enum IpVersion internal enum IpVersion
{ {
Ipv4 = 1, Ipv4 = 1,
...@@ -232,7 +229,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -232,7 +229,7 @@ namespace Titanium.Web.Proxy.Helpers
finally finally
{ {
tcpConnection.Dispose(); tcpConnection.Dispose();
server.ServerConnectionCount--; Interlocked.Decrement(ref server.serverConnectionCount);
} }
} }
} }
......
...@@ -8,8 +8,8 @@ namespace Titanium.Web.Proxy.Http ...@@ -8,8 +8,8 @@ namespace Titanium.Web.Proxy.Http
{ {
internal static class HeaderParser internal static class HeaderParser
{ {
internal static async Task ReadHeaders(CustomBinaryReader reader, internal static async Task ReadHeaders(CustomBinaryReader reader,
Dictionary<string, List<HttpHeader>> nonUniqueResponseHeaders, Dictionary<string, List<HttpHeader>> nonUniqueResponseHeaders,
Dictionary<string, HttpHeader> headers) Dictionary<string, HttpHeader> headers)
{ {
string tmpLine; string tmpLine;
......
...@@ -11,7 +11,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -11,7 +11,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary> /// <summary>
/// Used to communicate with the server over HTTP(S) /// Used to communicate with the server over HTTP(S)
/// </summary> /// </summary>
public class HttpWebClient public class HttpWebClient : IDisposable
{ {
/// <summary> /// <summary>
/// Connection to server /// Connection to server
...@@ -79,7 +79,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -79,7 +79,7 @@ namespace Titanium.Web.Proxy.Http
var requestLines = new StringBuilder(); var requestLines = new StringBuilder();
//prepare the request & headers //prepare the request & headers
if ((ServerConnection.UpStreamHttpProxy != null && ServerConnection.IsHttps == false) || (ServerConnection.UpStreamHttpsProxy != null && ServerConnection.IsHttps)) if (ServerConnection.UpStreamHttpProxy != null && ServerConnection.IsHttps == false || ServerConnection.UpStreamHttpsProxy != null && ServerConnection.IsHttps)
{ {
requestLines.AppendLine($"{Request.Method} {Request.RequestUri.AbsoluteUri} HTTP/{Request.HttpVersion.Major}.{Request.HttpVersion.Minor}"); requestLines.AppendLine($"{Request.Method} {Request.RequestUri.AbsoluteUri} HTTP/{Request.HttpVersion.Major}.{Request.HttpVersion.Minor}");
} }
...@@ -93,7 +93,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -93,7 +93,7 @@ namespace Titanium.Web.Proxy.Http
{ {
requestLines.AppendLine("Proxy-Connection: keep-alive"); requestLines.AppendLine("Proxy-Connection: keep-alive");
requestLines.AppendLine("Proxy-Authorization" + ": Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes( requestLines.AppendLine("Proxy-Authorization" + ": Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(
$"{ServerConnection.UpStreamHttpProxy.UserName}:{ServerConnection.UpStreamHttpProxy.Password}"))); $"{ServerConnection.UpStreamHttpProxy.UserName}:{ServerConnection.UpStreamHttpProxy.Password}")));
} }
//write request headers //write request headers
foreach (var headerItem in Request.RequestHeaders) foreach (var headerItem in Request.RequestHeaders)
...@@ -208,5 +208,16 @@ namespace Titanium.Web.Proxy.Http ...@@ -208,5 +208,16 @@ namespace Titanium.Web.Proxy.Http
//Read the response headers in to unique and non-unique header collections //Read the response headers in to unique and non-unique header collections
await HeaderParser.ReadHeaders(ServerConnection.StreamReader, Response.NonUniqueResponseHeaders, Response.ResponseHeaders); await HeaderParser.ReadHeaders(ServerConnection.StreamReader, Response.NonUniqueResponseHeaders, Response.ResponseHeaders);
} }
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
ConnectHeaders = null;
Request.Dispose();
Response.Dispose();
}
} }
} }
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Http namespace Titanium.Web.Proxy.Http
{ {
/// <summary> /// <summary>
/// A HTTP(S) request object /// A HTTP(S) request object
/// </summary> /// </summary>
public class Request public class Request : IDisposable
{ {
/// <summary> /// <summary>
/// Request Method /// Request Method
...@@ -241,7 +241,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -241,7 +241,7 @@ namespace Titanium.Web.Proxy.Http
/// request body as string /// request body as string
/// </summary> /// </summary>
internal string RequestBodyString { get; set; } internal string RequestBodyString { get; set; }
internal bool RequestBodyRead { get; set; } internal bool RequestBodyRead { get; set; }
internal bool RequestLocked { get; set; } internal bool RequestLocked { get; set; }
...@@ -294,5 +294,20 @@ namespace Titanium.Web.Proxy.Http ...@@ -294,5 +294,20 @@ namespace Titanium.Web.Proxy.Http
RequestHeaders = new Dictionary<string, HttpHeader>(StringComparer.OrdinalIgnoreCase); RequestHeaders = new Dictionary<string, HttpHeader>(StringComparer.OrdinalIgnoreCase);
NonUniqueRequestHeaders = new Dictionary<string, List<HttpHeader>>(StringComparer.OrdinalIgnoreCase); NonUniqueRequestHeaders = new Dictionary<string, List<HttpHeader>>(StringComparer.OrdinalIgnoreCase);
} }
/// <summary>
/// Dispose off
/// </summary>
public void Dispose()
{
//not really needed since GC will collect it
//but just to be on safe side
RequestHeaders = null;
NonUniqueRequestHeaders = null;
RequestBody = null;
RequestBody = null;
}
} }
} }
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using System; using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Http namespace Titanium.Web.Proxy.Http
{ {
/// <summary> /// <summary>
/// Http(s) response object /// Http(s) response object
/// </summary> /// </summary>
public class Response public class Response : IDisposable
{ {
/// <summary> /// <summary>
/// Response Status Code. /// Response Status Code.
...@@ -234,5 +234,20 @@ namespace Titanium.Web.Proxy.Http ...@@ -234,5 +234,20 @@ namespace Titanium.Web.Proxy.Http
ResponseHeaders = new Dictionary<string, HttpHeader>(StringComparer.OrdinalIgnoreCase); ResponseHeaders = new Dictionary<string, HttpHeader>(StringComparer.OrdinalIgnoreCase);
NonUniqueResponseHeaders = new Dictionary<string, List<HttpHeader>>(StringComparer.OrdinalIgnoreCase); NonUniqueResponseHeaders = new Dictionary<string, List<HttpHeader>>(StringComparer.OrdinalIgnoreCase);
} }
/// <summary>
/// Dispose off
/// </summary>
public void Dispose()
{
//not really needed since GC will collect it
//but just to be on safe side
ResponseHeaders = null;
NonUniqueResponseHeaders = null;
ResponseBody = null;
ResponseBodyString = null;
}
} }
} }
...@@ -13,7 +13,7 @@ namespace Titanium.Web.Proxy.Http.Responses ...@@ -13,7 +13,7 @@ namespace Titanium.Web.Proxy.Http.Responses
/// <param name="status"></param> /// <param name="status"></param>
public GenericResponse(HttpStatusCode status) public GenericResponse(HttpStatusCode status)
{ {
ResponseStatusCode = ((int) status).ToString(); ResponseStatusCode = ((int)status).ToString();
ResponseStatusDescription = status.ToString(); ResponseStatusDescription = status.ToString();
} }
......
...@@ -21,7 +21,7 @@ namespace Titanium.Web.Proxy.Models ...@@ -21,7 +21,7 @@ namespace Titanium.Web.Proxy.Models
/// <summary> /// <summary>
/// Bypass this proxy for connections to localhost? /// Bypass this proxy for connections to localhost?
/// </summary> /// </summary>
public bool BypassForLocalhost { get; set; } public bool BypassLocalhost { get; set; }
/// <summary> /// <summary>
/// Username. /// Username.
......
...@@ -110,7 +110,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -110,7 +110,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
// Corresponding private key // Corresponding private key
var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(subjectKeyPair.Private); var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(subjectKeyPair.Private);
var seq = (Asn1Sequence) Asn1Object.FromByteArray(privateKeyInfo.ParsePrivateKey().GetDerEncoded()); var seq = (Asn1Sequence)Asn1Object.FromByteArray(privateKeyInfo.ParsePrivateKey().GetDerEncoded());
if (seq.Count != 9) if (seq.Count != 9)
{ {
......
...@@ -89,7 +89,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -89,7 +89,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
} }
var x500CertDN = Activator.CreateInstance(typeX500DN); var x500CertDN = Activator.CreateInstance(typeX500DN);
var typeValue = new object[] {fullSubject, 0}; var typeValue = new object[] { fullSubject, 0 };
typeX500DN.InvokeMember("Encode", BindingFlags.InvokeMethod, null, x500CertDN, typeValue); typeX500DN.InvokeMember("Encode", BindingFlags.InvokeMethod, null, x500CertDN, typeValue);
var x500RootCertDN = Activator.CreateInstance(typeX500DN); var x500RootCertDN = Activator.CreateInstance(typeX500DN);
...@@ -110,16 +110,16 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -110,16 +110,16 @@ namespace Titanium.Web.Proxy.Network.Certificate
if (sharedPrivateKey == null) if (sharedPrivateKey == null)
{ {
sharedPrivateKey = Activator.CreateInstance(typeX509PrivateKey); sharedPrivateKey = Activator.CreateInstance(typeX509PrivateKey);
typeValue = new object[] {sProviderName}; typeValue = new object[] { sProviderName };
typeX509PrivateKey.InvokeMember("ProviderName", BindingFlags.PutDispProperty, null, sharedPrivateKey, typeValue); typeX509PrivateKey.InvokeMember("ProviderName", BindingFlags.PutDispProperty, null, sharedPrivateKey, typeValue);
typeValue[0] = 2; typeValue[0] = 2;
typeX509PrivateKey.InvokeMember("ExportPolicy", BindingFlags.PutDispProperty, null, sharedPrivateKey, typeValue); typeX509PrivateKey.InvokeMember("ExportPolicy", BindingFlags.PutDispProperty, null, sharedPrivateKey, typeValue);
typeValue = new object[] {(isRoot ? 2 : 1)}; typeValue = new object[] { isRoot ? 2 : 1 };
typeX509PrivateKey.InvokeMember("KeySpec", BindingFlags.PutDispProperty, null, sharedPrivateKey, typeValue); typeX509PrivateKey.InvokeMember("KeySpec", BindingFlags.PutDispProperty, null, sharedPrivateKey, typeValue);
if (!isRoot) if (!isRoot)
{ {
typeValue = new object[] {176}; typeValue = new object[] { 176 };
typeX509PrivateKey.InvokeMember("KeyUsage", BindingFlags.PutDispProperty, null, sharedPrivateKey, typeValue); typeX509PrivateKey.InvokeMember("KeyUsage", BindingFlags.PutDispProperty, null, sharedPrivateKey, typeValue);
} }
...@@ -149,9 +149,9 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -149,9 +149,9 @@ namespace Titanium.Web.Proxy.Network.Certificate
var requestCert = Activator.CreateInstance(typeRequestCert); var requestCert = Activator.CreateInstance(typeRequestCert);
typeValue = new[] {1, sharedPrivateKey, string.Empty}; typeValue = new[] { 1, sharedPrivateKey, string.Empty };
typeRequestCert.InvokeMember("InitializeFromPrivateKey", BindingFlags.InvokeMethod, null, requestCert, typeValue); typeRequestCert.InvokeMember("InitializeFromPrivateKey", BindingFlags.InvokeMethod, null, requestCert, typeValue);
typeValue = new[] {x500CertDN}; typeValue = new[] { x500CertDN };
typeRequestCert.InvokeMember("Subject", BindingFlags.PutDispProperty, null, requestCert, typeValue); typeRequestCert.InvokeMember("Subject", BindingFlags.PutDispProperty, null, requestCert, typeValue);
typeValue[0] = x500RootCertDN; typeValue[0] = x500RootCertDN;
typeRequestCert.InvokeMember("Issuer", BindingFlags.PutDispProperty, null, requestCert, typeValue); typeRequestCert.InvokeMember("Issuer", BindingFlags.PutDispProperty, null, requestCert, typeValue);
...@@ -186,14 +186,14 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -186,14 +186,14 @@ namespace Titanium.Web.Proxy.Network.Certificate
var extNames = Activator.CreateInstance(typeExtNames); var extNames = Activator.CreateInstance(typeExtNames);
var altDnsNames = Activator.CreateInstance(typeCAlternativeName); var altDnsNames = Activator.CreateInstance(typeCAlternativeName);
typeValue = new object[] {3, subject}; typeValue = new object[] { 3, subject };
typeCAlternativeName.InvokeMember("InitializeFromString", BindingFlags.InvokeMethod, null, altDnsNames, typeValue); typeCAlternativeName.InvokeMember("InitializeFromString", BindingFlags.InvokeMethod, null, altDnsNames, typeValue);
typeValue = new[] {altDnsNames}; typeValue = new[] { altDnsNames };
typeAltNamesCollection.InvokeMember("Add", BindingFlags.InvokeMethod, null, altNameCollection, typeValue); typeAltNamesCollection.InvokeMember("Add", BindingFlags.InvokeMethod, null, altNameCollection, typeValue);
typeValue = new[] {altNameCollection}; typeValue = new[] { altNameCollection };
typeExtNames.InvokeMember("InitializeEncode", BindingFlags.InvokeMethod, null, extNames, typeValue); typeExtNames.InvokeMember("InitializeEncode", BindingFlags.InvokeMethod, null, extNames, typeValue);
typeValue[0] = extNames; typeValue[0] = extNames;
...@@ -204,27 +204,27 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -204,27 +204,27 @@ namespace Titanium.Web.Proxy.Network.Certificate
{ {
var signerCertificate = Activator.CreateInstance(typeSignerCertificate); var signerCertificate = Activator.CreateInstance(typeSignerCertificate);
typeValue = new object[] {0, 0, 12, signingCertificate.Thumbprint}; typeValue = new object[] { 0, 0, 12, signingCertificate.Thumbprint };
typeSignerCertificate.InvokeMember("Initialize", BindingFlags.InvokeMethod, null, signerCertificate, typeValue); typeSignerCertificate.InvokeMember("Initialize", BindingFlags.InvokeMethod, null, signerCertificate, typeValue);
typeValue = new[] {signerCertificate}; typeValue = new[] { signerCertificate };
typeRequestCert.InvokeMember("SignerCertificate", BindingFlags.PutDispProperty, null, requestCert, typeValue); typeRequestCert.InvokeMember("SignerCertificate", BindingFlags.PutDispProperty, null, requestCert, typeValue);
} }
else else
{ {
var basicConstraints = Activator.CreateInstance(typeBasicConstraints); var basicConstraints = Activator.CreateInstance(typeBasicConstraints);
typeValue = new object[] {"true", "0"}; typeValue = new object[] { "true", "0" };
typeBasicConstraints.InvokeMember("InitializeEncode", BindingFlags.InvokeMethod, null, basicConstraints, typeValue); typeBasicConstraints.InvokeMember("InitializeEncode", BindingFlags.InvokeMethod, null, basicConstraints, typeValue);
typeValue = new[] {basicConstraints}; typeValue = new[] { basicConstraints };
typeX509Extensions.InvokeMember("Add", BindingFlags.InvokeMethod, null, certificate, typeValue); typeX509Extensions.InvokeMember("Add", BindingFlags.InvokeMethod, null, certificate, typeValue);
} }
oid = Activator.CreateInstance(typeOID); oid = Activator.CreateInstance(typeOID);
typeValue = new object[] {1, 0, 0, hashAlg}; typeValue = new object[] { 1, 0, 0, hashAlg };
typeOID.InvokeMember("InitializeFromAlgorithmName", BindingFlags.InvokeMethod, null, oid, typeValue); typeOID.InvokeMember("InitializeFromAlgorithmName", BindingFlags.InvokeMethod, null, oid, typeValue);
typeValue = new[] {oid}; typeValue = new[] { oid };
typeRequestCert.InvokeMember("HashAlgorithm", BindingFlags.PutDispProperty, null, requestCert, typeValue); typeRequestCert.InvokeMember("HashAlgorithm", BindingFlags.PutDispProperty, null, requestCert, typeValue);
typeRequestCert.InvokeMember("Encode", BindingFlags.InvokeMethod, null, requestCert, null); typeRequestCert.InvokeMember("Encode", BindingFlags.InvokeMethod, null, requestCert, null);
...@@ -243,15 +243,15 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -243,15 +243,15 @@ namespace Titanium.Web.Proxy.Network.Certificate
typeValue[0] = 0; typeValue[0] = 0;
var createCertRequest = typeX509Enrollment.InvokeMember("CreateRequest", BindingFlags.InvokeMethod, null, x509Enrollment, typeValue); var createCertRequest = typeX509Enrollment.InvokeMember("CreateRequest", BindingFlags.InvokeMethod, null, x509Enrollment, typeValue);
typeValue = new[] {2, createCertRequest, 0, string.Empty}; typeValue = new[] { 2, createCertRequest, 0, string.Empty };
typeX509Enrollment.InvokeMember("InstallResponse", BindingFlags.InvokeMethod, null, x509Enrollment, typeValue); typeX509Enrollment.InvokeMember("InstallResponse", BindingFlags.InvokeMethod, null, x509Enrollment, typeValue);
typeValue = new object[] {null, 0, 1}; typeValue = new object[] { null, 0, 1 };
try try
{ {
var empty = (string) typeX509Enrollment.InvokeMember("CreatePFX", BindingFlags.InvokeMethod, null, x509Enrollment, typeValue); var empty = (string)typeX509Enrollment.InvokeMember("CreatePFX", BindingFlags.InvokeMethod, null, x509Enrollment, typeValue);
return new X509Certificate2(Convert.FromBase64String(empty), string.Empty, X509KeyStorageFlags.Exportable); return new X509Certificate2(Convert.FromBase64String(empty), string.Empty, X509KeyStorageFlags.Exportable);
} }
catch (Exception) catch (Exception)
...@@ -293,8 +293,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -293,8 +293,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
var graceTime = DateTime.Now.AddDays(GraceDays); var graceTime = DateTime.Now.AddDays(GraceDays);
var now = DateTime.Now; var now = DateTime.Now;
rCert = !isRoot ? MakeCertificate(false, sSubjectCN, fullSubject, keyLength, HashAlgo, graceTime, now.AddDays(ValidDays), signingCert) : rCert = MakeCertificate(isRoot, sSubjectCN, fullSubject, keyLength, HashAlgo, graceTime, now.AddDays(ValidDays), isRoot ? null : signingCert);
MakeCertificate(true, sSubjectCN, fullSubject, keyLength, HashAlgo, graceTime, now.AddDays(ValidDays), null);
return rCert; return rCert;
} }
} }
......
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Linq;
using System.Collections.Concurrent;
using System.IO;
using Titanium.Web.Proxy.Network.Certificate;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Network.Certificate;
namespace Titanium.Web.Proxy.Network namespace Titanium.Web.Proxy.Network
{ {
...@@ -29,7 +31,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -29,7 +31,7 @@ namespace Titanium.Web.Proxy.Network
/// <summary> /// <summary>
/// A class to manage SSL certificates used by this proxy server /// A class to manage SSL certificates used by this proxy server
/// </summary> /// </summary>
public class CertificateManager : IDisposable public sealed class CertificateManager : IDisposable
{ {
internal CertificateEngine Engine internal CertificateEngine Engine
{ {
...@@ -51,7 +53,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -51,7 +53,7 @@ namespace Titanium.Web.Proxy.Network
if (certEngine == null) if (certEngine == null)
{ {
certEngine = engine == CertificateEngine.BouncyCastle certEngine = engine == CertificateEngine.BouncyCastle
? (ICertificateMaker) new BCCertificateMaker() ? (ICertificateMaker)new BCCertificateMaker()
: new WinCertificateMaker(); : new WinCertificateMaker();
} }
} }
...@@ -132,12 +134,12 @@ namespace Titanium.Web.Proxy.Network ...@@ -132,12 +134,12 @@ namespace Titanium.Web.Proxy.Network
private string GetRootCertificatePath() private string GetRootCertificatePath()
{ {
var assemblyLocation = System.Reflection.Assembly.GetExecutingAssembly().Location; var assemblyLocation = Assembly.GetExecutingAssembly().Location;
// dynamically loaded assemblies returns string.Empty location // dynamically loaded assemblies returns string.Empty location
if (assemblyLocation == string.Empty) if (assemblyLocation == string.Empty)
{ {
assemblyLocation = System.Reflection.Assembly.GetEntryAssembly().Location; assemblyLocation = Assembly.GetEntryAssembly().Location;
} }
var path = Path.GetDirectoryName(assemblyLocation); var path = Path.GetDirectoryName(assemblyLocation);
...@@ -146,7 +148,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -146,7 +148,7 @@ namespace Titanium.Web.Proxy.Network
return fileName; return fileName;
} }
internal X509Certificate2 LoadRootCertificate() private X509Certificate2 LoadRootCertificate()
{ {
var fileName = GetRootCertificatePath(); var fileName = GetRootCertificatePath();
if (!File.Exists(fileName)) return null; if (!File.Exists(fileName)) return null;
...@@ -217,6 +219,51 @@ namespace Titanium.Web.Proxy.Network ...@@ -217,6 +219,51 @@ namespace Titanium.Web.Proxy.Network
TrustRootCertificate(StoreLocation.LocalMachine); TrustRootCertificate(StoreLocation.LocalMachine);
} }
/// <summary>
/// Puts the certificate to the local machine's certificate store.
/// Needs elevated permission. Works only on Windows.
/// </summary>
/// <returns></returns>
public bool TrustRootCertificateAsAdministrator()
{
if (RunTime.IsRunningOnMono())
{
return false;
}
var fileName = Path.GetTempFileName();
File.WriteAllBytes(fileName, RootCertificate.Export(X509ContentType.Pkcs12));
var info = new ProcessStartInfo
{
FileName = "certutil.exe",
Arguments = "-importPFX -p \"\" -f \"" + fileName + "\"",
CreateNoWindow = true,
UseShellExecute = true,
Verb = "runas",
ErrorDialog = false,
};
try
{
var process = Process.Start(info);
if (process == null)
{
return false;
}
process.WaitForExit();
File.Delete(fileName);
}
catch
{
return false;
}
return true;
}
/// <summary> /// <summary>
/// Removes the trusted certificates. /// Removes the trusted certificates.
/// </summary> /// </summary>
...@@ -229,13 +276,49 @@ namespace Titanium.Web.Proxy.Network ...@@ -229,13 +276,49 @@ namespace Titanium.Web.Proxy.Network
RemoveTrustedRootCertificates(StoreLocation.LocalMachine); RemoveTrustedRootCertificates(StoreLocation.LocalMachine);
} }
/// <summary>
/// Determines whether the root certificate is trusted.
/// </summary>
public bool IsRootCertificateTrusted()
{
return FindRootCertificate(StoreLocation.CurrentUser) || IsRootCertificateMachineTrusted();
}
/// <summary>
/// Determines whether the root certificate is machine trusted.
/// </summary>
public bool IsRootCertificateMachineTrusted()
{
return FindRootCertificate(StoreLocation.LocalMachine);
}
private bool FindRootCertificate(StoreLocation storeLocation)
{
string value = $"{RootCertificate.Issuer}";
return FindCertificates(StoreName.Root, storeLocation, value).Count > 0;
}
private X509Certificate2Collection FindCertificates(StoreName storeName, StoreLocation storeLocation, string findValue)
{
X509Store x509Store = new X509Store(storeName, storeLocation);
try
{
x509Store.Open(OpenFlags.OpenExistingOnly);
return x509Store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, findValue, false);
}
finally
{
x509Store.Close();
}
}
/// <summary> /// <summary>
/// Create an SSL certificate /// Create an SSL certificate
/// </summary> /// </summary>
/// <param name="certificateName"></param> /// <param name="certificateName"></param>
/// <param name="isRootCertificate"></param> /// <param name="isRootCertificate"></param>
/// <returns></returns> /// <returns></returns>
internal virtual X509Certificate2 CreateCertificate(string certificateName, bool isRootCertificate) internal X509Certificate2 CreateCertificate(string certificateName, bool isRootCertificate)
{ {
if (certificateCache.ContainsKey(certificateName)) if (certificateCache.ContainsKey(certificateName))
{ {
...@@ -264,7 +347,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -264,7 +347,7 @@ namespace Titanium.Web.Proxy.Network
} }
if (certificate != null && !certificateCache.ContainsKey(certificateName)) if (certificate != null && !certificateCache.ContainsKey(certificateName))
{ {
certificateCache.Add(certificateName, new CachedCertificate {Certificate = certificate}); certificateCache.Add(certificateName, new CachedCertificate { Certificate = certificate });
} }
} }
else else
...@@ -316,7 +399,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -316,7 +399,7 @@ namespace Titanium.Web.Proxy.Network
/// </summary> /// </summary>
/// <param name="storeLocation"></param> /// <param name="storeLocation"></param>
/// <returns></returns> /// <returns></returns>
internal void TrustRootCertificate(StoreLocation storeLocation) private void TrustRootCertificate(StoreLocation storeLocation)
{ {
if (RootCertificate == null) if (RootCertificate == null)
{ {
...@@ -327,7 +410,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -327,7 +410,7 @@ namespace Titanium.Web.Proxy.Network
return; return;
} }
X509Store x509RootStore = new X509Store(StoreName.Root, storeLocation); var x509RootStore = new X509Store(StoreName.Root, storeLocation);
var x509PersonalStore = new X509Store(StoreName.My, storeLocation); var x509PersonalStore = new X509Store(StoreName.My, storeLocation);
try try
...@@ -356,7 +439,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -356,7 +439,7 @@ namespace Titanium.Web.Proxy.Network
/// </summary> /// </summary>
/// <param name="storeLocation"></param> /// <param name="storeLocation"></param>
/// <returns></returns> /// <returns></returns>
internal void RemoveTrustedRootCertificates(StoreLocation storeLocation) private void RemoveTrustedRootCertificates(StoreLocation storeLocation)
{ {
if (RootCertificate == null) if (RootCertificate == null)
{ {
...@@ -367,7 +450,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -367,7 +450,7 @@ namespace Titanium.Web.Proxy.Network
return; return;
} }
X509Store x509RootStore = new X509Store(StoreName.Root, storeLocation); var x509RootStore = new X509Store(StoreName.Root, storeLocation);
var x509PersonalStore = new X509Store(StoreName.My, storeLocation); var x509PersonalStore = new X509Store(StoreName.My, storeLocation);
try try
...@@ -381,7 +464,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -381,7 +464,7 @@ namespace Titanium.Web.Proxy.Network
catch (Exception e) catch (Exception e)
{ {
exceptionFunc( exceptionFunc(
new Exception("Failed to make system trust root certificate " new Exception("Failed to remove root certificate trust "
+ $" for {storeLocation} store location. You may need admin rights.", e)); + $" for {storeLocation} store location. You may need admin rights.", e));
} }
finally finally
...@@ -391,6 +474,9 @@ namespace Titanium.Web.Proxy.Network ...@@ -391,6 +474,9 @@ namespace Titanium.Web.Proxy.Network
} }
} }
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose() public void Dispose()
{ {
} }
......
...@@ -58,7 +58,21 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -58,7 +58,21 @@ namespace Titanium.Web.Proxy.Network.Tcp
StreamReader?.Dispose(); StreamReader?.Dispose();
TcpClient?.Close(); try
{
if (TcpClient != null)
{
//This line is important!
//contributors please don't remove it without discussion
//It helps to avoid eventual deterioration of performance due to TCP port exhaustion
//due to default TCP CLOSE_WAIT timeout for 4 minutes
TcpClient.LingerState = new LingerOption(true, 0);
TcpClient.Close();
}
}
catch
{
}
} }
} }
} }
...@@ -70,8 +70,8 @@ namespace Titanium.Web.Proxy ...@@ -70,8 +70,8 @@ namespace Titanium.Web.Proxy
{ {
ResponseHeaders = new Dictionary<string, HttpHeader> ResponseHeaders = new Dictionary<string, HttpHeader>
{ {
{"Proxy-Authenticate", new HttpHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\"")}, { "Proxy-Authenticate", new HttpHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\"") },
{"Proxy-Connection", new HttpHeader("Proxy-Connection", "close")} { "Proxy-Connection", new HttpHeader("Proxy-Connection", "close") }
} }
}; };
await WriteResponseHeaders(clientStreamWriter, response); await WriteResponseHeaders(clientStreamWriter, response);
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network; using Titanium.Web.Proxy.Network;
using System.Linq;
using System.Security.Authentication;
using Titanium.Web.Proxy.Network.Tcp; using Titanium.Web.Proxy.Network.Tcp;
using System.Security.Cryptography.X509Certificates;
namespace Titanium.Web.Proxy namespace Titanium.Web.Proxy
{ {
...@@ -34,8 +35,21 @@ namespace Titanium.Web.Proxy ...@@ -34,8 +35,21 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
private Action<Exception> exceptionFunc; private Action<Exception> exceptionFunc;
/// <summary>
/// Backing field for corresponding public property
/// </summary>
private bool trustRootCertificate; private bool trustRootCertificate;
/// <summary>
/// Backing field for corresponding public property
/// </summary>
private int clientConnectionCount;
/// <summary>
/// Backing field for corresponding public property
/// </summary>
internal int serverConnectionCount;
/// <summary> /// <summary>
/// A object that creates tcp connection to server /// A object that creates tcp connection to server
/// </summary> /// </summary>
...@@ -50,8 +64,7 @@ namespace Titanium.Web.Proxy ...@@ -50,8 +64,7 @@ namespace Titanium.Web.Proxy
/// <summary> /// <summary>
/// Set firefox to use default system proxy /// Set firefox to use default system proxy
/// </summary> /// </summary>
private FireFoxProxySettingsManager firefoxProxySettingsManager private FireFoxProxySettingsManager firefoxProxySettingsManager = new FireFoxProxySettingsManager();
= new FireFoxProxySettingsManager();
#endif #endif
/// <summary> /// <summary>
...@@ -125,6 +138,12 @@ namespace Titanium.Web.Proxy ...@@ -125,6 +138,12 @@ namespace Titanium.Web.Proxy
set { CertificateManager.Engine = value; } set { CertificateManager.Engine = value; }
} }
/// <summary>
/// Should we check for certificare revocation during SSL authentication to servers
/// Note: If enabled can reduce performance (Default disabled)
/// </summary>
public bool CheckCertificateRevocation { get; set; }
/// <summary> /// <summary>
/// Does this proxy uses the HTTP protocol 100 continue behaviour strictly? /// Does this proxy uses the HTTP protocol 100 continue behaviour strictly?
/// Broken 100 contunue implementations on server/client may cause problems if enabled /// Broken 100 contunue implementations on server/client may cause problems if enabled
...@@ -224,18 +243,18 @@ namespace Titanium.Web.Proxy ...@@ -224,18 +243,18 @@ namespace Titanium.Web.Proxy
/// List of supported Ssl versions /// List of supported Ssl versions
/// </summary> /// </summary>
public SslProtocols SupportedSslProtocols { get; set; } = SslProtocols.Tls public SslProtocols SupportedSslProtocols { get; set; } = SslProtocols.Tls
| SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Ssl3; | SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Ssl3;
/// <summary> /// <summary>
/// Total number of active client connections /// Total number of active client connections
/// </summary> /// </summary>
public int ClientConnectionCount { get; private set; } public int ClientConnectionCount => clientConnectionCount;
/// <summary> /// <summary>
/// Total number of active server connections /// Total number of active server connections
/// </summary> /// </summary>
public int ServerConnectionCount { get; internal set; } public int ServerConnectionCount => serverConnectionCount;
/// <summary> /// <summary>
/// Constructor /// Constructor
...@@ -381,8 +400,7 @@ namespace Titanium.Web.Proxy ...@@ -381,8 +400,7 @@ namespace Titanium.Web.Proxy
#if !DEBUG #if !DEBUG
firefoxProxySettingsManager.AddFirefox(); firefoxProxySettingsManager.AddFirefox();
#endif #endif
Console.WriteLine("Set endpoint at Ip {0} and port: {1} as System HTTPS Proxy", Console.WriteLine("Set endpoint at Ip {0} and port: {1} as System HTTPS Proxy", endPoint.IpAddress, endPoint.Port);
endPoint.IpAddress, endPoint.Port);
} }
/// <summary> /// <summary>
...@@ -596,13 +614,10 @@ namespace Titanium.Web.Proxy ...@@ -596,13 +614,10 @@ namespace Titanium.Web.Proxy
{ {
Task.Run(async () => Task.Run(async () =>
{ {
ClientConnectionCount++; Interlocked.Increment(ref clientConnectionCount);
//This line is important! tcpClient.ReceiveTimeout = ConnectionTimeOutSeconds * 1000;
//contributors please don't remove it without discussion tcpClient.SendTimeout = ConnectionTimeOutSeconds * 1000;
//It helps to avoid eventual deterioration of performance due to TCP port exhaustion
//due to default TCP CLOSE_WAIT timeout for 4 minutes
tcpClient.LingerState = new LingerOption(true, 0);
try try
{ {
...@@ -617,8 +632,23 @@ namespace Titanium.Web.Proxy ...@@ -617,8 +632,23 @@ namespace Titanium.Web.Proxy
} }
finally finally
{ {
ClientConnectionCount--; Interlocked.Decrement(ref clientConnectionCount);
tcpClient?.Close();
try
{
if (tcpClient != null)
{
//This line is important!
//contributors please don't remove it without discussion
//It helps to avoid eventual deterioration of performance due to TCP port exhaustion
//due to default TCP CLOSE_WAIT timeout for 4 minutes
tcpClient.LingerState = new LingerOption(true, 0);
tcpClient.Close();
}
}
catch
{
}
} }
}); });
} }
...@@ -637,46 +667,5 @@ namespace Titanium.Web.Proxy ...@@ -637,46 +667,5 @@ namespace Titanium.Web.Proxy
endPoint.Listener.Server.Close(); endPoint.Listener.Server.Close();
endPoint.Listener.Server.Dispose(); endPoint.Listener.Server.Dispose();
} }
/// <summary>
/// Invocator for BeforeRequest event.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected virtual void OnBeforeRequest(object sender, SessionEventArgs e)
{
BeforeRequest?.Invoke(sender, e);
}
/// <summary>
/// Invocator for BeforeResponse event.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <returns></returns>
protected virtual void OnBeforeResponse(object sender, SessionEventArgs e)
{
BeforeResponse?.Invoke(sender, e);
}
/// <summary>
/// Invocator for ServerCertificateValidationCallback event.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected virtual void OnServerCertificateValidationCallback(object sender, CertificateValidationEventArgs e)
{
ServerCertificateValidationCallback?.Invoke(sender, e);
}
/// <summary>
/// Invocator for ClientCertifcateSelectionCallback event.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected virtual void OnClientCertificateSelectionCallback(object sender, CertificateSelectionEventArgs e)
{
ClientCertificateSelectionCallback?.Invoke(sender, e);
}
} }
} }
This diff is collapsed.
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using Titanium.Web.Proxy.EventArguments; using System.Threading;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Compression;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.Compression;
using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Exceptions; using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network.Tcp; using Titanium.Web.Proxy.Network.Tcp;
namespace Titanium.Web.Proxy namespace Titanium.Web.Proxy
...@@ -22,7 +23,7 @@ namespace Titanium.Web.Proxy ...@@ -22,7 +23,7 @@ namespace Titanium.Web.Proxy
/// Called asynchronously when a request was successfully and we received the response /// Called asynchronously when a request was successfully and we received the response
/// </summary> /// </summary>
/// <param name="args"></param> /// <param name="args"></param>
/// <returns>true if no errors</returns> /// <returns>true if client/server connection was terminated (and disposed) </returns>
private async Task<bool> HandleHttpSessionResponse(SessionEventArgs args) private async Task<bool> HandleHttpSessionResponse(SessionEventArgs args)
{ {
try try
...@@ -40,28 +41,20 @@ namespace Titanium.Web.Proxy ...@@ -40,28 +41,20 @@ namespace Titanium.Web.Proxy
//If user requested call back then do it //If user requested call back then do it
if (BeforeResponse != null && !args.WebSession.Response.ResponseLocked) if (BeforeResponse != null && !args.WebSession.Response.ResponseLocked)
{ {
Delegate[] invocationList = BeforeResponse.GetInvocationList(); await BeforeResponse.InvokeParallelAsync(this, args);
Task[] handlerTasks = new Task[invocationList.Length];
for (int i = 0; i < invocationList.Length; i++)
{
handlerTasks[i] = ((Func<object, SessionEventArgs, Task>)invocationList[i])(this, args);
}
await Task.WhenAll(handlerTasks);
} }
if (args.ReRequest) if (args.ReRequest)
{ {
if(args.WebSession.ServerConnection != null) if (args.WebSession.ServerConnection != null)
{ {
args.WebSession.ServerConnection.Dispose(); args.WebSession.ServerConnection.Dispose();
ServerConnectionCount--; Interlocked.Decrement(ref serverConnectionCount);
} }
var connection = await GetServerConnection(args); var connection = await GetServerConnection(args);
var result = await HandleHttpSessionRequestInternal(null, args, true); var disposed = await HandleHttpSessionRequestInternal(null, args, true);
return result; return disposed;
} }
args.WebSession.Response.ResponseLocked = true; args.WebSession.Response.ResponseLocked = true;
...@@ -137,12 +130,10 @@ namespace Titanium.Web.Proxy ...@@ -137,12 +130,10 @@ namespace Titanium.Web.Proxy
Dispose(args.ProxyClient.ClientStream, args.ProxyClient.ClientStreamReader, Dispose(args.ProxyClient.ClientStream, args.ProxyClient.ClientStreamReader,
args.ProxyClient.ClientStreamWriter, args.WebSession.ServerConnection); args.ProxyClient.ClientStreamWriter, args.WebSession.ServerConnection);
return false; return true;
} }
args.Dispose(); return false;
return true;
} }
/// <summary> /// <summary>
...@@ -240,15 +231,17 @@ namespace Titanium.Web.Proxy ...@@ -240,15 +231,17 @@ namespace Titanium.Web.Proxy
StreamWriter clientStreamWriter, StreamWriter clientStreamWriter,
TcpConnection serverConnection) TcpConnection serverConnection)
{ {
ServerConnectionCount--;
clientStream?.Close(); clientStream?.Close();
clientStream?.Dispose(); clientStream?.Dispose();
clientStreamReader?.Dispose(); clientStreamReader?.Dispose();
clientStreamWriter?.Dispose(); clientStreamWriter?.Dispose();
serverConnection?.Dispose(); if (serverConnection != null)
{
serverConnection.Dispose();
Interlocked.Decrement(ref serverConnectionCount);
}
} }
} }
} }
...@@ -7,9 +7,9 @@ namespace Titanium.Web.Proxy.Shared ...@@ -7,9 +7,9 @@ namespace Titanium.Web.Proxy.Shared
/// </summary> /// </summary>
internal class ProxyConstants internal class ProxyConstants
{ {
internal static readonly char[] SpaceSplit = {' '}; internal static readonly char[] SpaceSplit = { ' ' };
internal static readonly char[] ColonSplit = {':'}; internal static readonly char[] ColonSplit = { ':' };
internal static readonly char[] SemiColonSplit = {';'}; internal static readonly char[] SemiColonSplit = { ';' };
internal static readonly byte[] NewLineBytes = Encoding.ASCII.GetBytes(NewLine); internal static readonly byte[] NewLineBytes = Encoding.ASCII.GetBytes(NewLine);
......
...@@ -73,6 +73,7 @@ ...@@ -73,6 +73,7 @@
<Compile Include="EventArguments\CertificateSelectionEventArgs.cs" /> <Compile Include="EventArguments\CertificateSelectionEventArgs.cs" />
<Compile Include="EventArguments\CertificateValidationEventArgs.cs" /> <Compile Include="EventArguments\CertificateValidationEventArgs.cs" />
<Compile Include="Extensions\ByteArrayExtensions.cs" /> <Compile Include="Extensions\ByteArrayExtensions.cs" />
<Compile Include="Extensions\FuncExtensions.cs" />
<Compile Include="Extensions\StringExtensions.cs" /> <Compile Include="Extensions\StringExtensions.cs" />
<Compile Include="Helpers\CustomBufferedStream.cs" /> <Compile Include="Helpers\CustomBufferedStream.cs" />
<Compile Include="Helpers\Network.cs" /> <Compile Include="Helpers\Network.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