Commit 369611a9 authored by justcoding121's avatar justcoding121 Committed by justcoding121

Merge pull request #98 from justcoding121/master

sync with master
parents 17713088 e66f375b
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Titanium.Web.Proxy.Network;
using System.Threading.Tasks;
using System.Collections.Generic;
namespace Titanium.Web.Proxy.UnitTests
{
[TestClass]
public class CertificateManagerTests
{
private readonly static string[] hostNames
= new string[] { "facebook.com", "youtube.com", "google.com",
"bing.com", "yahoo.com"};
private readonly Random random = new Random();
[TestMethod]
public async Task Simple_Create_Certificate_Stress_Test()
{
var tasks = new List<Task>();
var mgr = new CertificateManager("Titanium","Titanium Root Certificate Authority");
mgr.ClearIdleCertificates(1);
for (int i = 0; i < 1000; i++)
{
foreach (var host in hostNames)
{
tasks.Add(Task.Run(async () =>
{
await Task.Delay(random.Next(0, 10) * 1000);
//get the connection
var certificate = await mgr.CreateCertificate(host, false);
Assert.IsNotNull(certificate);
}));
}
}
await Task.WhenAll(tasks.ToArray());
mgr.StopClearIdleCertificates();
}
}
}
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Titanium.Web.Proxy.UnitTests")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Titanium.Web.Proxy.UnitTests")]
[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("b517e3d0-d03b-436f-ab03-34ba0d5321af")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{B517E3D0-D03B-436F-AB03-34BA0D5321AF}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Titanium.Web.Proxy.UnitTests</RootNamespace>
<AssemblyName>Titanium.Web.Proxy.UnitTests</AssemblyName>
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
<IsCodedUITest>False</IsCodedUITest>
<TestProjectType>UnitTest</TestProjectType>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
</ItemGroup>
<Choose>
<When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'">
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
</ItemGroup>
</When>
<Otherwise>
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework" />
</ItemGroup>
</Otherwise>
</Choose>
<ItemGroup>
<Compile Include="CertificateManagerTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Titanium.Web.Proxy\Titanium.Web.Proxy.csproj">
<Project>{8d73a1be-868c-42d2-9ece-f32cc1a02906}</Project>
<Name>Titanium.Web.Proxy</Name>
</ProjectReference>
</ItemGroup>
<Choose>
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.QualityTools.CodedUITestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestTools.UITest.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestTools.UITest.Extension, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestTools.UITesting, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
</ItemGroup>
</When>
</Choose>
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
\ No newline at end of file
...@@ -29,6 +29,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{AC9AE37A ...@@ -29,6 +29,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{AC9AE37A
.build\default.ps1 = .build\default.ps1 .build\default.ps1 = .build\default.ps1
EndProjectSection EndProjectSection
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{BC1E0789-D348-49CF-8B67-5E99D50EDF64}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Titanium.Web.Proxy.UnitTests", "Tests\Titanium.Web.Proxy.UnitTests\Titanium.Web.Proxy.UnitTests.csproj", "{B517E3D0-D03B-436F-AB03-34BA0D5321AF}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
...@@ -43,12 +47,17 @@ Global ...@@ -43,12 +47,17 @@ Global
{F3B7E553-1904-4E80-BDC7-212342B5C952}.Debug|Any CPU.Build.0 = Debug|Any CPU {F3B7E553-1904-4E80-BDC7-212342B5C952}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F3B7E553-1904-4E80-BDC7-212342B5C952}.Release|Any CPU.ActiveCfg = Release|Any CPU {F3B7E553-1904-4E80-BDC7-212342B5C952}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F3B7E553-1904-4E80-BDC7-212342B5C952}.Release|Any CPU.Build.0 = Release|Any CPU {F3B7E553-1904-4E80-BDC7-212342B5C952}.Release|Any CPU.Build.0 = Release|Any CPU
{B517E3D0-D03B-436F-AB03-34BA0D5321AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B517E3D0-D03B-436F-AB03-34BA0D5321AF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B517E3D0-D03B-436F-AB03-34BA0D5321AF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B517E3D0-D03B-436F-AB03-34BA0D5321AF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection
GlobalSection(NestedProjects) = preSolution GlobalSection(NestedProjects) = preSolution
{F3B7E553-1904-4E80-BDC7-212342B5C952} = {B6DBABDC-C985-4872-9C38-B4E5079CBC4B} {F3B7E553-1904-4E80-BDC7-212342B5C952} = {B6DBABDC-C985-4872-9C38-B4E5079CBC4B}
{B517E3D0-D03B-436F-AB03-34BA0D5321AF} = {BC1E0789-D348-49CF-8B67-5E99D50EDF64}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
EnterpriseLibraryConfigurationToolBinariesPath = .1.505.2\lib\NET35 EnterpriseLibraryConfigurationToolBinariesPath = .1.505.2\lib\NET35
......
...@@ -28,7 +28,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -28,7 +28,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="isHttps"></param> /// <param name="isHttps"></param>
/// <returns></returns> /// <returns></returns>
internal static async Task SendRaw(Stream clientStream, string httpCmd, Dictionary<string, HttpHeader> requestHeaders, string hostName, internal static async Task SendRaw(Stream clientStream, string httpCmd, Dictionary<string, HttpHeader> requestHeaders, string hostName,
int tunnelPort, bool isHttps, SslProtocols supportedProtocols) int tunnelPort, bool isHttps, SslProtocols supportedProtocols, int connectionTimeOutSeconds)
{ {
//prepare the prefix content //prepare the prefix content
StringBuilder sb = null; StringBuilder sb = null;
...@@ -83,6 +83,12 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -83,6 +83,12 @@ namespace Titanium.Web.Proxy.Helpers
} }
} }
tunnelClient.SendTimeout = connectionTimeOutSeconds * 1000;
tunnelClient.ReceiveTimeout = connectionTimeOutSeconds * 1000;
tunnelStream.ReadTimeout = connectionTimeOutSeconds * 1000;
tunnelStream.WriteTimeout = connectionTimeOutSeconds * 1000;
Task sendRelay; Task sendRelay;
//Now async relay all server=>client & client=>server data //Now async relay all server=>client & client=>server data
......
...@@ -17,7 +17,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -17,7 +17,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary> /// <summary>
/// Connection to server /// Connection to server
/// </summary> /// </summary>
internal TcpConnectionCache ServerConnection { get; set; } internal TcpConnection ServerConnection { get; set; }
public Request Request { get; set; } public Request Request { get; set; }
public Response Response { get; set; } public Response Response { get; set; }
...@@ -37,7 +37,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -37,7 +37,7 @@ namespace Titanium.Web.Proxy.Http
/// Set the tcp connection to server used by this webclient /// Set the tcp connection to server used by this webclient
/// </summary> /// </summary>
/// <param name="Connection"></param> /// <param name="Connection"></param>
internal void SetConnection(TcpConnectionCache Connection) internal void SetConnection(TcpConnection Connection)
{ {
Connection.LastAccess = DateTime.Now; Connection.LastAccess = DateTime.Now;
ServerConnection = Connection; ServerConnection = Connection;
...@@ -170,6 +170,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -170,6 +170,7 @@ namespace Titanium.Web.Proxy.Http
} }
//Read the Response headers //Read the Response headers
//Read the response headers in to unique and non-unique header collections
string tmpLine; string tmpLine;
while (!string.IsNullOrEmpty(tmpLine = await ServerConnection.StreamReader.ReadLineAsync())) while (!string.IsNullOrEmpty(tmpLine = await ServerConnection.StreamReader.ReadLineAsync()))
{ {
...@@ -177,10 +178,12 @@ namespace Titanium.Web.Proxy.Http ...@@ -177,10 +178,12 @@ namespace Titanium.Web.Proxy.Http
var newHeader = new HttpHeader(header[0], header[1]); var newHeader = new HttpHeader(header[0], header[1]);
//if header exist in non-unique header collection add it there
if (Response.NonUniqueResponseHeaders.ContainsKey(newHeader.Name)) if (Response.NonUniqueResponseHeaders.ContainsKey(newHeader.Name))
{ {
Response.NonUniqueResponseHeaders[newHeader.Name].Add(newHeader); Response.NonUniqueResponseHeaders[newHeader.Name].Add(newHeader);
} }
//if header is alread in unique header collection then move both to non-unique collection
else if (Response.ResponseHeaders.ContainsKey(newHeader.Name)) else if (Response.ResponseHeaders.ContainsKey(newHeader.Name))
{ {
var existing = Response.ResponseHeaders[newHeader.Name]; var existing = Response.ResponseHeaders[newHeader.Name];
...@@ -193,6 +196,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -193,6 +196,7 @@ namespace Titanium.Web.Proxy.Http
Response.NonUniqueResponseHeaders.Add(newHeader.Name, nonUniqueHeaders); Response.NonUniqueResponseHeaders.Add(newHeader.Name, nonUniqueHeaders);
Response.ResponseHeaders.Remove(newHeader.Name); Response.ResponseHeaders.Remove(newHeader.Name);
} }
//add to unique header collection
else else
{ {
Response.ResponseHeaders.Add(newHeader.Name, newHeader); Response.ResponseHeaders.Add(newHeader.Name, newHeader);
......
...@@ -8,7 +8,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -8,7 +8,7 @@ namespace Titanium.Web.Proxy.Network
/// <summary> /// <summary>
/// An object that holds TcpConnection to a particular server & port /// An object that holds TcpConnection to a particular server & port
/// </summary> /// </summary>
public class TcpConnectionCache public class TcpConnection: IDisposable
{ {
internal string HostName { get; set; } internal string HostName { get; set; }
internal int port { get; set; } internal int port { get; set; }
...@@ -37,9 +37,18 @@ namespace Titanium.Web.Proxy.Network ...@@ -37,9 +37,18 @@ namespace Titanium.Web.Proxy.Network
/// </summary> /// </summary>
internal DateTime LastAccess { get; set; } internal DateTime LastAccess { get; set; }
internal TcpConnectionCache() internal TcpConnection()
{ {
LastAccess = DateTime.Now; LastAccess = DateTime.Now;
} }
public void Dispose()
{
Stream.Close();
Stream.Dispose();
TcpClient.Client.Close();
TcpClient.Close();
TcpClient.Client.Dispose();
}
} }
} }
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
...@@ -13,6 +14,7 @@ using System.Runtime.InteropServices; ...@@ -13,6 +14,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyCopyright("Copyright © 2015")] [assembly: AssemblyCopyright("Copyright © 2015")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
[assembly: InternalsVisibleTo("Titanium.Web.Proxy.UnitTests")]
// Setting ComVisible to false makes the types in this assembly not visible // Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from // to COM components. If you need to access a type in this assembly from
......
...@@ -24,9 +24,9 @@ namespace Titanium.Web.Proxy ...@@ -24,9 +24,9 @@ namespace Titanium.Web.Proxy
private CertificateManager certificateCacheManager { get; set; } private CertificateManager certificateCacheManager { get; set; }
/// <summary> /// <summary>
/// A object that manages tcp connection cache to server /// A object that creates tcp connection to server
/// </summary> /// </summary>
private TcpConnectionCacheManager tcpConnectionCacheManager { get; set; } private TcpConnectionFactory tcpConnectionFactory { get; set; }
/// <summary> /// <summary>
/// Manage system proxy settings /// Manage system proxy settings
...@@ -67,14 +67,14 @@ namespace Titanium.Web.Proxy ...@@ -67,14 +67,14 @@ namespace Titanium.Web.Proxy
public bool Enable100ContinueBehaviour { get; set; } public bool Enable100ContinueBehaviour { get; set; }
/// <summary> /// <summary>
/// Minutes TCP connection cache to servers to be kept alive when in idle state /// Minutes certificates should be kept in cache when not used
/// </summary> /// </summary>
public int ConnectionCacheTimeOutMinutes { get; set; } public int CertificateCacheTimeOutMinutes { get; set; }
/// <summary> /// <summary>
/// Minutes certificates should be kept in cache when not used /// Seconds client/server connection are to be kept alive when waiting for read/write to complete
/// </summary> /// </summary>
public int CertificateCacheTimeOutMinutes { get; set; } public int ConnectionTimeOutSeconds { get; set; }
/// <summary> /// <summary>
/// Intercept request to server /// Intercept request to server
...@@ -122,11 +122,11 @@ namespace Titanium.Web.Proxy ...@@ -122,11 +122,11 @@ namespace Titanium.Web.Proxy
public ProxyServer() public ProxyServer()
{ {
//default values //default values
ConnectionCacheTimeOutMinutes = 3; ConnectionTimeOutSeconds = 120;
CertificateCacheTimeOutMinutes = 60; CertificateCacheTimeOutMinutes = 60;
ProxyEndPoints = new List<ProxyEndPoint>(); ProxyEndPoints = new List<ProxyEndPoint>();
tcpConnectionCacheManager = new TcpConnectionCacheManager(); tcpConnectionFactory = new TcpConnectionFactory();
systemProxySettingsManager = new SystemProxyManager(); systemProxySettingsManager = new SystemProxyManager();
firefoxProxySettingsManager = new FireFoxProxySettingsManager(); firefoxProxySettingsManager = new FireFoxProxySettingsManager();
...@@ -273,7 +273,6 @@ namespace Titanium.Web.Proxy ...@@ -273,7 +273,6 @@ namespace Titanium.Web.Proxy
Listen(endPoint); Listen(endPoint);
} }
tcpConnectionCacheManager.ClearIdleConnections(ConnectionCacheTimeOutMinutes);
certificateCacheManager.ClearIdleCertificates(CertificateCacheTimeOutMinutes); certificateCacheManager.ClearIdleCertificates(CertificateCacheTimeOutMinutes);
proxyRunning = true; proxyRunning = true;
...@@ -306,7 +305,6 @@ namespace Titanium.Web.Proxy ...@@ -306,7 +305,6 @@ namespace Titanium.Web.Proxy
ProxyEndPoints.Clear(); ProxyEndPoints.Clear();
tcpConnectionCacheManager.StopClearIdleConnections();
certificateCacheManager.StopClearIdleCertificates(); certificateCacheManager.StopClearIdleCertificates();
proxyRunning = false; proxyRunning = false;
......
...@@ -19,14 +19,19 @@ using Titanium.Web.Proxy.Extensions; ...@@ -19,14 +19,19 @@ using Titanium.Web.Proxy.Extensions;
namespace Titanium.Web.Proxy namespace Titanium.Web.Proxy
{ {
/// <summary> /// <summary>
/// Handle the requesr /// Handle the request
/// </summary> /// </summary>
partial class ProxyServer partial class ProxyServer
{ {
//This is called when client is aware of proxy //This is called when client is aware of proxy
//So for HTTPS requests client would send CONNECT header to negotiate a secure tcp tunnel via proxy
private async void HandleClient(ExplicitProxyEndPoint endPoint, TcpClient client) private async void HandleClient(ExplicitProxyEndPoint endPoint, TcpClient client)
{ {
Stream clientStream = client.GetStream(); Stream clientStream = client.GetStream();
clientStream.ReadTimeout = ConnectionTimeOutSeconds * 1000;
clientStream.WriteTimeout = ConnectionTimeOutSeconds * 1000;
var clientStreamReader = new CustomBinaryReader(clientStream); var clientStreamReader = new CustomBinaryReader(clientStream);
var clientStreamWriter = new StreamWriter(clientStream); var clientStreamWriter = new StreamWriter(clientStream);
...@@ -84,13 +89,6 @@ namespace Titanium.Web.Proxy ...@@ -84,13 +89,6 @@ namespace Titanium.Web.Proxy
try try
{ {
//create the Tcp Connection to server and then release it to connection cache
//Just doing what CONNECT request is asking as to do
var tunnelClient = await tcpConnectionCacheManager.GetClient(httpRemoteUri.Host, httpRemoteUri.Port, true, version,
UpStreamHttpProxy, UpStreamHttpsProxy, BUFFER_SIZE, SupportedSslProtocols, new RemoteCertificateValidationCallback(ValidateServerCertificate),
new LocalCertificateSelectionCallback(SelectClientCertificate));
await tcpConnectionCacheManager.ReleaseClient(tunnelClient);
sslStream = new SslStream(clientStream, true); sslStream = new SslStream(clientStream, true);
var certificate = await certificateCacheManager.CreateCertificate(httpRemoteUri.Host, false); var certificate = await certificateCacheManager.CreateCertificate(httpRemoteUri.Host, false);
...@@ -129,7 +127,7 @@ namespace Titanium.Web.Proxy ...@@ -129,7 +127,7 @@ namespace Titanium.Web.Proxy
//Just relay the request/response without decrypting it //Just relay the request/response without decrypting it
await TcpHelper.SendRaw(clientStream, null, null, httpRemoteUri.Host, httpRemoteUri.Port, await TcpHelper.SendRaw(clientStream, null, null, httpRemoteUri.Host, httpRemoteUri.Port,
false, SupportedSslProtocols); false, SupportedSslProtocols, ConnectionTimeOutSeconds);
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, null); Dispose(client, clientStream, clientStreamReader, clientStreamWriter, null);
return; return;
...@@ -145,11 +143,15 @@ namespace Titanium.Web.Proxy ...@@ -145,11 +143,15 @@ namespace Titanium.Web.Proxy
} }
} }
//This is called when requests are routed through router to this endpoint //This is called when this proxy acts as a reverse proxy (like a real http server)
//For ssl requests //So for HTTPS requests we would start SSL negotiation right away without expecting a CONNECT request from client
private async void HandleClient(TransparentProxyEndPoint endPoint, TcpClient tcpClient) private async void HandleClient(TransparentProxyEndPoint endPoint, TcpClient tcpClient)
{ {
Stream clientStream = tcpClient.GetStream(); Stream clientStream = tcpClient.GetStream();
clientStream.ReadTimeout = ConnectionTimeOutSeconds * 1000;
clientStream.WriteTimeout = ConnectionTimeOutSeconds * 1000;
CustomBinaryReader clientStreamReader = null; CustomBinaryReader clientStreamReader = null;
StreamWriter clientStreamWriter = null; StreamWriter clientStreamWriter = null;
X509Certificate2 certificate = null; X509Certificate2 certificate = null;
...@@ -209,7 +211,7 @@ namespace Titanium.Web.Proxy ...@@ -209,7 +211,7 @@ namespace Titanium.Web.Proxy
private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream, private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream,
CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, string httpsHostName) CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, string httpsHostName)
{ {
TcpConnectionCache connection = null; TcpConnection connection = null;
//Loop through each subsequest request on this particular client connection //Loop through each subsequest request on this particular client connection
//(assuming HTTP connection is kept alive by client) //(assuming HTTP connection is kept alive by client)
...@@ -243,17 +245,7 @@ namespace Titanium.Web.Proxy ...@@ -243,17 +245,7 @@ namespace Titanium.Web.Proxy
} }
} }
#if DEBUG //Read the request headers in to unique and non-unique header collections
//Just ignore local requests while Debugging
//Its annoying
if (httpCmd.Contains("localhost"))
{
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, null);
break;
}
#endif
//Read the request headers
string tmpLine; string tmpLine;
while (!string.IsNullOrEmpty(tmpLine = await clientStreamReader.ReadLineAsync())) while (!string.IsNullOrEmpty(tmpLine = await clientStreamReader.ReadLineAsync()))
{ {
...@@ -261,10 +253,12 @@ namespace Titanium.Web.Proxy ...@@ -261,10 +253,12 @@ namespace Titanium.Web.Proxy
var newHeader = new HttpHeader(header[0], header[1]); var newHeader = new HttpHeader(header[0], header[1]);
//if header exist in non-unique header collection add it there
if (args.WebSession.Request.NonUniqueRequestHeaders.ContainsKey(newHeader.Name)) if (args.WebSession.Request.NonUniqueRequestHeaders.ContainsKey(newHeader.Name))
{ {
args.WebSession.Request.NonUniqueRequestHeaders[newHeader.Name].Add(newHeader); args.WebSession.Request.NonUniqueRequestHeaders[newHeader.Name].Add(newHeader);
} }
//if header is alread in unique header collection then move both to non-unique collection
else if (args.WebSession.Request.RequestHeaders.ContainsKey(newHeader.Name)) else if (args.WebSession.Request.RequestHeaders.ContainsKey(newHeader.Name))
{ {
var existing = args.WebSession.Request.RequestHeaders[newHeader.Name]; var existing = args.WebSession.Request.RequestHeaders[newHeader.Name];
...@@ -277,6 +271,7 @@ namespace Titanium.Web.Proxy ...@@ -277,6 +271,7 @@ namespace Titanium.Web.Proxy
args.WebSession.Request.NonUniqueRequestHeaders.Add(newHeader.Name, nonUniqueHeaders); args.WebSession.Request.NonUniqueRequestHeaders.Add(newHeader.Name, nonUniqueHeaders);
args.WebSession.Request.RequestHeaders.Remove(newHeader.Name); args.WebSession.Request.RequestHeaders.Remove(newHeader.Name);
} }
//add to unique header collection
else else
{ {
args.WebSession.Request.RequestHeaders.Add(newHeader.Name, newHeader); args.WebSession.Request.RequestHeaders.Add(newHeader.Name, newHeader);
...@@ -316,14 +311,15 @@ namespace Titanium.Web.Proxy ...@@ -316,14 +311,15 @@ namespace Titanium.Web.Proxy
if (args.WebSession.Request.UpgradeToWebSocket) if (args.WebSession.Request.UpgradeToWebSocket)
{ {
await TcpHelper.SendRaw(clientStream, httpCmd, args.WebSession.Request.RequestHeaders, await TcpHelper.SendRaw(clientStream, httpCmd, args.WebSession.Request.RequestHeaders,
httpRemoteUri.Host, httpRemoteUri.Port, args.IsHttps, SupportedSslProtocols); httpRemoteUri.Host, httpRemoteUri.Port, args.IsHttps, SupportedSslProtocols, ConnectionTimeOutSeconds);
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args); Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
return; break;
} }
//construct the web request that we are going to issue on behalf of the client. //construct the web request that we are going to issue on behalf of the client.
connection = await tcpConnectionCacheManager.GetClient(args.WebSession.Request.RequestUri.Host, args.WebSession.Request.RequestUri.Port, args.IsHttps, version, connection = connection!=null? connection : await tcpConnectionFactory.GetClient(args.WebSession.Request.RequestUri.Host, args.WebSession.Request.RequestUri.Port, args.IsHttps, version,
UpStreamHttpProxy, UpStreamHttpsProxy, BUFFER_SIZE, SupportedSslProtocols, new RemoteCertificateValidationCallback(ValidateServerCertificate), UpStreamHttpProxy, UpStreamHttpsProxy, BUFFER_SIZE, SupportedSslProtocols, ConnectionTimeOutSeconds, new RemoteCertificateValidationCallback(ValidateServerCertificate),
new LocalCertificateSelectionCallback(SelectClientCertificate)); new LocalCertificateSelectionCallback(SelectClientCertificate));
args.WebSession.Request.RequestLocked = true; args.WebSession.Request.RequestLocked = true;
...@@ -402,12 +398,9 @@ namespace Titanium.Web.Proxy ...@@ -402,12 +398,9 @@ namespace Titanium.Web.Proxy
if (args.WebSession.Response.ResponseKeepAlive == false) if (args.WebSession.Response.ResponseKeepAlive == false)
{ {
Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args); Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
return; break;
} }
//send the tcp connection to server back to connection cache for reuse
await tcpConnectionCacheManager.ReleaseClient(connection);
// read the next request // read the next request
httpCmd = await clientStreamReader.ReadLineAsync(); httpCmd = await clientStreamReader.ReadLineAsync();
...@@ -420,6 +413,12 @@ namespace Titanium.Web.Proxy ...@@ -420,6 +413,12 @@ namespace Titanium.Web.Proxy
} }
if (connection != null)
{
//dispose
connection.Dispose();
}
} }
/// <summary> /// <summary>
......
...@@ -77,8 +77,8 @@ ...@@ -77,8 +77,8 @@
<Compile Include="Http\Request.cs" /> <Compile Include="Http\Request.cs" />
<Compile Include="Http\Response.cs" /> <Compile Include="Http\Response.cs" />
<Compile Include="Models\ExternalProxy.cs" /> <Compile Include="Models\ExternalProxy.cs" />
<Compile Include="Network\TcpConnectionCache.cs" /> <Compile Include="Network\TcpConnection.cs" />
<Compile Include="Network\TcpConnectionCacheManager.cs" /> <Compile Include="Network\TcpConnectionFactory.cs" />
<Compile Include="Models\HttpHeader.cs" /> <Compile Include="Models\HttpHeader.cs" />
<Compile Include="Http\HttpWebClient.cs" /> <Compile Include="Http\HttpWebClient.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.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