Commit 5a683862 authored by justcoding121's avatar justcoding121

Merge with Beta

Since Chrome HTTPS is broken in stable due to #199, lets merge with beta
asap
parent 45d3a905
......@@ -5,7 +5,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
{
public class Program
{
private static readonly ProxyTestController Controller = new ProxyTestController();
private static readonly ProxyTestController controller = new ProxyTestController();
public static void Main(string[] args)
{
......@@ -15,13 +15,13 @@ namespace Titanium.Web.Proxy.Examples.Basic
//Start proxy controller
Controller.StartProxy();
controller.StartProxy();
Console.WriteLine("Hit any key to exit..");
Console.WriteLine();
Console.Read();
Controller.Stop();
controller.Stop();
}
......@@ -30,7 +30,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
if (eventType != 2) return false;
try
{
Controller.Stop();
controller.Stop();
}
catch
{
......
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments;
......@@ -9,16 +10,29 @@ namespace Titanium.Web.Proxy.Examples.Basic
{
public class ProxyTestController
{
private ProxyServer proxyServer;
private readonly ProxyServer proxyServer;
//share requestBody outside handlers
private Dictionary<Guid, string> requestBodyHistory;
private readonly Dictionary<Guid, string> requestBodyHistory = new Dictionary<Guid, string>();
public ProxyTestController()
{
proxyServer = new ProxyServer();
//generate root certificate without storing it in file system
//proxyServer.CertificateEngine = Network.CertificateEngine.BouncyCastle;
//proxyServer.CertificateManager.CreateTrustedRootCertificate(false);
//proxyServer.CertificateManager.TrustRootCertificate();
proxyServer.ExceptionFunc = exception => Console.WriteLine(exception.Message);
proxyServer.TrustRootCertificate = true;
requestBodyHistory = new Dictionary<Guid, string>();
//optionally set the Certificate Engine
//Under Mono only BouncyCastle will be supported
//proxyServer.CertificateEngine = Network.CertificateEngine.BouncyCastle;
//optionally set the Root Certificate
//proxyServer.RootCertificate = new X509Certificate2("myCert.pfx", string.Empty, X509KeyStorageFlags.Exportable);
}
public void StartProxy()
......@@ -28,21 +42,32 @@ namespace Titanium.Web.Proxy.Examples.Basic
proxyServer.ServerCertificateValidationCallback += OnCertificateValidation;
proxyServer.ClientCertificateSelectionCallback += OnCertificateSelection;
//Exclude Https addresses you don't want to proxy
//Usefull for clients that use certificate pinning
//for example dropbox.com
var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000, true)
{
// ExcludedHttpsHostNameRegex = new List<string>() { "google.com", "dropbox.com" }
//Exclude Https addresses you don't want to proxy
//Useful for clients that use certificate pinning
//for example google.com and dropbox.com
ExcludedHttpsHostNameRegex = new List<string>() { "dropbox.com" }
//Include Https addresses you want to proxy (others will be excluded)
//for example github.com
// IncludedHttpsHostNameRegex = new List<string>() { "github.com" }
//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
//Optimizes performance by not creating a certificate for each https-enabled domain
//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")
};
//An explicit endpoint is where the client knows about the existance of a proxy
//An explicit endpoint is where the client knows about the existence of a proxy
//So client sends request in a proxy friendly manner
proxyServer.AddEndPoint(explicitEndPoint);
proxyServer.Start();
//Transparent endpoint is usefull for reverse proxying (client is not aware of the existance of proxy)
//Transparent endpoint is useful for reverse proxying (client is not aware of the existence of proxy)
//A transparent endpoint usually requires a network router port forwarding HTTP(S) packets to this endpoint
//Currently do not support Server Name Indication (It is not currently supported by SslStream class)
//That means that the transparent endpoint will always provide the same Generic Certificate to all HTTPS requests
......@@ -74,18 +99,22 @@ namespace Titanium.Web.Proxy.Examples.Basic
proxyServer.ClientCertificateSelectionCallback -= OnCertificateSelection;
proxyServer.Stop();
//remove the generated certificates
//proxyServer.CertificateManager.RemoveTrustedRootCertificates();
}
//intecept & cancel, redirect or update requests
//intecept & cancel redirect or update requests
public async Task OnRequest(object sender, SessionEventArgs e)
{
Console.WriteLine("Active Client Connections:" + ((ProxyServer) sender).ClientConnectionCount);
Console.WriteLine(e.WebSession.Request.Url);
////read request headers
//read request headers
var requestHeaders = e.WebSession.Request.RequestHeaders;
var method = e.WebSession.Request.Method.ToUpper();
if ((method == "POST" || method == "PUT" || method == "PATCH"))
if (method == "POST" || method == "PUT" || method == "PATCH")
{
//Get/Set request body bytes
byte[] bodyBytes = await e.GetRequestBody();
......@@ -98,45 +127,49 @@ namespace Titanium.Web.Proxy.Examples.Basic
requestBodyHistory[e.Id] = bodyString;
}
//To cancel a request with a custom HTML content
//Filter URL
if (e.WebSession.Request.RequestUri.AbsoluteUri.Contains("google.com"))
{
await e.Ok("<!DOCTYPE html>" +
"<html><body><h1>" +
"Website Blocked" +
"</h1>" +
"<p>Blocked by titanium web proxy.</p>" +
"</body>" +
"</html>");
}
//Redirect example
if (e.WebSession.Request.RequestUri.AbsoluteUri.Contains("wikipedia.org"))
{
await e.Redirect("https://www.paypal.com");
}
////To cancel a request with a custom HTML content
////Filter URL
//if (e.WebSession.Request.RequestUri.AbsoluteUri.Contains("google.com"))
//{
// await e.Ok("<!DOCTYPE html>" +
// "<html><body><h1>" +
// "Website Blocked" +
// "</h1>" +
// "<p>Blocked by titanium web proxy.</p>" +
// "</body>" +
// "</html>");
//}
////Redirect example
//if (e.WebSession.Request.RequestUri.AbsoluteUri.Contains("wikipedia.org"))
//{
// await e.Redirect("https://www.paypal.com");
//}
}
//Modify response
public async Task OnResponse(object sender, SessionEventArgs e)
{
if(requestBodyHistory.ContainsKey(e.Id))
Console.WriteLine("Active Server Connections:" + (sender as ProxyServer).ServerConnectionCount);
if (requestBodyHistory.ContainsKey(e.Id))
{
//access request body by looking up the shared dictionary using requestId
var requestBody = requestBodyHistory[e.Id];
}
//read response headers
var responseHeaders = e.WebSession.Response.ResponseHeaders;
// print out process id of current session
Console.WriteLine($"PID: {e.WebSession.ProcessId.Value}");
//Console.WriteLine($"PID: {e.WebSession.ProcessId.Value}");
//if (!e.ProxySession.Request.Host.Equals("medeczane.sgk.gov.tr")) return;
if (e.WebSession.Request.Method == "GET" || e.WebSession.Request.Method == "POST")
{
if (e.WebSession.Response.ResponseStatusCode == "200")
{
if (e.WebSession.Response.ContentType!=null && e.WebSession.Response.ContentType.Trim().ToLower().Contains("text/html"))
if (e.WebSession.Response.ContentType != null && e.WebSession.Response.ContentType.Trim().ToLower().Contains("text/html"))
{
byte[] bodyBytes = await e.GetResponseBody();
await e.SetResponseBody(bodyBytes);
......@@ -176,4 +209,4 @@ namespace Titanium.Web.Proxy.Examples.Basic
return Task.FromResult(0);
}
}
}
\ No newline at end of file
}
......@@ -25,7 +25,7 @@
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE;NET45</DefineConstants>
......@@ -33,6 +33,7 @@
<WarningLevel>4</WarningLevel>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<Prefer32Bit>false</Prefer32Bit>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
......
Doneness:
- [ ] Build is okay - I made sure that this change is building successfully.
- [ ] No Bugs - I made sure that this change is working properly as expected. It does'nt have any bugs that you are aware of.
- [ ] No Bugs - I made sure that this change is working properly as expected. It doesn't have any bugs that you are aware of.
- [ ] Branching - If this is not a hotfix, I am making this request against develop branch
This diff is collapsed.
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.IntegrationTests")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Titanium.Web.Proxy.IntegrationTests")]
[assembly: AssemblyCopyright("Copyright © 2017")]
[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("32231301-b0fb-4f9e-98df-b3e8a88f4c16")]
// 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")]
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Net;
using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Models;
using System.Net.Http;
using System.Diagnostics;
namespace Titanium.Web.Proxy.IntegrationTests
{
[TestClass]
public class SslTests
{
[TestMethod]
public void TestSsl()
{
//expand this to stress test to find
//why in long run proxy becomes unresponsive as per issue #184
var testUrl = "https://google.com";
int proxyPort = 8086;
var proxy = new ProxyTestController();
proxy.StartProxy(proxyPort);
using (var client = CreateHttpClient(testUrl, proxyPort))
{
var response = client.GetAsync(new Uri(testUrl)).Result;
}
}
private HttpClient CreateHttpClient(string url, int localProxyPort)
{
var handler = new HttpClientHandler
{
Proxy = new WebProxy($"http://localhost:{localProxyPort}", false),
UseProxy = true,
};
var client = new HttpClient(handler);
return client;
}
}
public class ProxyTestController
{
private readonly ProxyServer proxyServer;
public ProxyTestController()
{
proxyServer = new ProxyServer();
proxyServer.TrustRootCertificate = true;
}
public void StartProxy(int proxyPort)
{
proxyServer.BeforeRequest += OnRequest;
proxyServer.BeforeResponse += OnResponse;
proxyServer.ServerCertificateValidationCallback += OnCertificateValidation;
proxyServer.ClientCertificateSelectionCallback += OnCertificateSelection;
var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, proxyPort, true);
//An explicit endpoint is where the client knows about the existance of a proxy
//So client sends request in a proxy friendly manner
proxyServer.AddEndPoint(explicitEndPoint);
proxyServer.Start();
foreach (var endPoint in proxyServer.ProxyEndPoints)
Console.WriteLine("Listening on '{0}' endpoint at Ip {1} and port: {2} ",
endPoint.GetType().Name, endPoint.IpAddress, endPoint.Port);
}
public void Stop()
{
proxyServer.BeforeRequest -= OnRequest;
proxyServer.BeforeResponse -= OnResponse;
proxyServer.ServerCertificateValidationCallback -= OnCertificateValidation;
proxyServer.ClientCertificateSelectionCallback -= OnCertificateSelection;
proxyServer.Stop();
}
//intecept & cancel, redirect or update requests
public async Task OnRequest(object sender, SessionEventArgs e)
{
Debug.WriteLine(e.WebSession.Request.Url);
await Task.FromResult(0);
}
//Modify response
public async Task OnResponse(object sender, SessionEventArgs e)
{
await Task.FromResult(0);
}
/// <summary>
/// Allows overriding default certificate validation logic
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public Task OnCertificateValidation(object sender, CertificateValidationEventArgs e)
{
//set IsValid to true/false based on Certificate Errors
if (e.SslPolicyErrors == System.Net.Security.SslPolicyErrors.None)
{
e.IsValid = true;
}
return Task.FromResult(0);
}
/// <summary>
/// Allows overriding default client certificate selection logic during mutual authentication
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public Task OnCertificateSelection(object sender, CertificateSelectionEventArgs e)
{
//set e.clientCertificate to override
return Task.FromResult(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>{32231301-B0FB-4F9E-98DF-B3E8A88F4C16}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Titanium.Web.Proxy.IntegrationTests</RootNamespace>
<AssemblyName>Titanium.Web.Proxy.IntegrationTests</AssemblyName>
<TargetFrameworkVersion>v4.5.2</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>
<TargetFrameworkProfile />
</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" />
<Reference Include="System.Net.Http" />
</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="SslTests.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
......@@ -9,7 +9,7 @@ namespace Titanium.Web.Proxy.UnitTests
[TestClass]
public class CertificateManagerTests
{
private readonly static string[] hostNames
private static readonly string[] hostNames
= new string[] { "facebook.com", "youtube.com", "google.com",
"bing.com", "yahoo.com"};
......@@ -20,8 +20,7 @@ namespace Titanium.Web.Proxy.UnitTests
{
var tasks = new List<Task>();
var mgr = new CertificateManager("Titanium", "Titanium Root Certificate Authority",
new Lazy<Action<Exception>>(() => (e => { })).Value);
var mgr = new CertificateManager(new Lazy<Action<Exception>>(() => (e => { })).Value);
mgr.ClearIdleCertificates(1);
......
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
......
......@@ -34,6 +34,12 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<SignAssembly>true</SignAssembly>
</PropertyGroup>
<PropertyGroup>
<AssemblyOriginatorKeyFile>StrongNameKey.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
</ItemGroup>
......@@ -60,6 +66,9 @@
<Name>Titanium.Web.Proxy</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="StrongNameKey.snk" />
</ItemGroup>
<Choose>
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
<ItemGroup>
......

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25123.0
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{B6DBABDC-C985-4872-9C38-B4E5079CBC4B}"
EndProject
......@@ -33,6 +33,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{BC1E0789
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
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Titanium.Web.Proxy.IntegrationTests", "Tests\Titanium.Web.Proxy.IntegrationTests\Titanium.Web.Proxy.IntegrationTests.csproj", "{32231301-B0FB-4F9E-98DF-B3E8A88F4C16}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
......@@ -51,6 +53,10 @@ Global
{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
{32231301-B0FB-4F9E-98DF-B3E8A88F4C16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{32231301-B0FB-4F9E-98DF-B3E8A88F4C16}.Debug|Any CPU.Build.0 = Debug|Any CPU
{32231301-B0FB-4F9E-98DF-B3E8A88F4C16}.Release|Any CPU.ActiveCfg = Release|Any CPU
{32231301-B0FB-4F9E-98DF-B3E8A88F4C16}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
......@@ -58,6 +64,7 @@ Global
GlobalSection(NestedProjects) = preSolution
{F3B7E553-1904-4E80-BDC7-212342B5C952} = {B6DBABDC-C985-4872-9C38-B4E5079CBC4B}
{B517E3D0-D03B-436F-AB03-34BA0D5321AF} = {BC1E0789-D348-49CF-8B67-5E99D50EDF64}
{32231301-B0FB-4F9E-98DF-B3E8A88F4C16} = {BC1E0789-D348-49CF-8B67-5E99D50EDF64}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
EnterpriseLibraryConfigurationToolBinariesPath = .1.505.2\lib\NET35
......
<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:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=BC/@EntryIndexedValue">BC</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/=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/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>
\ No newline at end of file
using System;
using System.Linq;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
......@@ -18,19 +17,20 @@ namespace Titanium.Web.Proxy
/// <param name="sslPolicyErrors"></param>
/// <returns></returns>
internal bool ValidateServerCertificate(
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
//if user callback is registered then do it
if (ServerCertificateValidationCallback != null)
{
var args = new CertificateValidationEventArgs();
args.Certificate = certificate;
args.Chain = chain;
args.SslPolicyErrors = sslPolicyErrors;
var args = new CertificateValidationEventArgs
{
Certificate = certificate,
Chain = chain,
SslPolicyErrors = sslPolicyErrors
};
Delegate[] invocationList = ServerCertificateValidationCallback.GetInvocationList();
......@@ -38,7 +38,7 @@ namespace Titanium.Web.Proxy
for (int i = 0; i < invocationList.Length; i++)
{
handlerTasks[i] = ((Func<object, CertificateValidationEventArgs, Task>)invocationList[i])(null, args);
handlerTasks[i] = ((Func<object, CertificateValidationEventArgs, Task>) invocationList[i])(null, args);
}
Task.WhenAll(handlerTasks).Wait();
......@@ -73,7 +73,6 @@ namespace Titanium.Web.Proxy
string[] acceptableIssuers)
{
X509Certificate clientCertificate = null;
var customSslStream = sender as SslStream;
if (acceptableIssuers != null &&
acceptableIssuers.Length > 0 &&
......@@ -100,20 +99,22 @@ namespace Titanium.Web.Proxy
//If user call back is registered
if (ClientCertificateSelectionCallback != null)
{
var args = new CertificateSelectionEventArgs();
var args = new CertificateSelectionEventArgs
{
TargetHost = targetHost,
LocalCertificates = localCertificates,
RemoteCertificate = remoteCertificate,
AcceptableIssuers = acceptableIssuers,
ClientCertificate = clientCertificate
};
args.TargetHost = targetHost;
args.LocalCertificates = localCertificates;
args.RemoteCertificate = remoteCertificate;
args.AcceptableIssuers = acceptableIssuers;
args.ClientCertificate = clientCertificate;
Delegate[] invocationList = ClientCertificateSelectionCallback.GetInvocationList();
Task[] handlerTasks = new Task[invocationList.Length];
for (int i = 0; i < invocationList.Length; i++)
{
handlerTasks[i] = ((Func<object, CertificateSelectionEventArgs, Task>)invocationList[i])(null, args);
handlerTasks[i] = ((Func<object, CertificateSelectionEventArgs, Task>) invocationList[i])(null, args);
}
Task.WhenAll(handlerTasks).Wait();
......
......@@ -13,8 +13,6 @@
return new GZipCompression();
case "deflate":
return new DeflateCompression();
case "zlib":
return new ZlibCompression();
default:
return null;
}
......
......@@ -15,7 +15,7 @@ namespace Titanium.Web.Proxy.Compression
{
using (var zip = new DeflateStream(ms, CompressionMode.Compress, true))
{
await zip.WriteAsync(responseBody, 0, responseBody.Length);
await zip.WriteAsync(responseBody, 0, responseBody.Length);
}
return ms.ToArray();
......
using Ionic.Zlib;
using System.IO;
using System.IO;
using System.IO.Compression;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Compression
......@@ -15,7 +15,7 @@ namespace Titanium.Web.Proxy.Compression
{
using (var zip = new GZipStream(ms, CompressionMode.Compress, true))
{
await zip.WriteAsync(responseBody, 0, responseBody.Length);
await zip.WriteAsync(responseBody, 0, responseBody.Length);
}
return ms.ToArray();
......
using Ionic.Zlib;
using System.IO;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Compression
{
/// <summary>
/// concrete implementation of zlib compression
/// </summary>
internal class ZlibCompression : ICompression
{
public async Task<byte[]> Compress(byte[] responseBody)
{
using (var ms = new MemoryStream())
{
using (var zip = new ZlibStream(ms, CompressionMode.Compress, true))
{
await zip.WriteAsync(responseBody, 0, responseBody.Length);
}
return ms.ToArray();
}
}
}
}
......@@ -7,14 +7,12 @@
{
internal IDecompression Create(string type)
{
switch(type)
switch (type)
{
case "gzip":
return new GZipDecompression();
case "deflate":
return new DeflateDecompression();
case "zlib":
return new ZlibDecompression();
default:
return new DefaultDecompression();
}
......
......@@ -2,7 +2,6 @@
namespace Titanium.Web.Proxy.Decompression
{
/// <summary>
/// When no compression is specified just return the byte array
/// </summary>
......
using Ionic.Zlib;
using System.IO;
using System.IO;
using System.IO.Compression;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Decompression
{
......@@ -12,8 +11,7 @@ namespace Titanium.Web.Proxy.Decompression
{
public async Task<byte[]> Decompress(byte[] compressedArray, int bufferSize)
{
var stream = new MemoryStream(compressedArray);
using (var stream = new MemoryStream(compressedArray))
using (var decompressor = new DeflateStream(stream, CompressionMode.Decompress))
{
var buffer = new byte[bufferSize];
......@@ -23,7 +21,7 @@ namespace Titanium.Web.Proxy.Decompression
int read;
while ((read = await decompressor.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
await output.WriteAsync(buffer, 0, read);
output.Write(buffer, 0, read);
}
return output.ToArray();
......
using System.IO;
using System.IO.Compression;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Decompression
{
......@@ -20,8 +19,9 @@ namespace Titanium.Web.Proxy.Decompression
int read;
while ((read = await decompressor.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
await output.WriteAsync(buffer, 0, read);
output.Write(buffer, 0, read);
}
return output.ToArray();
}
}
......
......@@ -7,6 +7,6 @@ namespace Titanium.Web.Proxy.Decompression
/// </summary>
internal interface IDecompression
{
Task<byte[]> Decompress(byte[] compressedArray, int bufferSize);
Task<byte[]> Decompress(byte[] compressedArray, int bufferSize);
}
}
using Ionic.Zlib;
using System.IO;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Decompression
{
/// <summary>
/// concrete implemetation of zlib de-compression
/// </summary>
internal class ZlibDecompression : IDecompression
{
public async Task<byte[]> Decompress(byte[] compressedArray, int bufferSize)
{
var memoryStream = new MemoryStream(compressedArray);
using (var decompressor = new ZlibStream(memoryStream, CompressionMode.Decompress))
{
var buffer = new byte[bufferSize];
using (var output = new MemoryStream())
{
int read;
while ((read = await decompressor.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
await output.WriteAsync(buffer, 0, read);
}
return output.ToArray();
}
}
}
}
}
......@@ -8,13 +8,34 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary>
public class CertificateSelectionEventArgs : EventArgs
{
/// <summary>
/// Sender object.
/// </summary>
public object Sender { get; internal set; }
/// <summary>
/// Target host.
/// </summary>
public string TargetHost { get; internal set; }
/// <summary>
/// Local certificates.
/// </summary>
public X509CertificateCollection LocalCertificates { get; internal set; }
/// <summary>
/// Remote certificate.
/// </summary>
public X509Certificate RemoteCertificate { get; internal set; }
/// <summary>
/// Acceptable issuers.
/// </summary>
public string[] AcceptableIssuers { get; internal set; }
/// <summary>
/// Client Certificate.
/// </summary>
public X509Certificate ClientCertificate { get; set; }
}
}
......@@ -7,17 +7,26 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary>
/// An argument passed on to the user for validating the server certificate during SSL authentication
/// </summary>
public class CertificateValidationEventArgs : EventArgs, IDisposable
public class CertificateValidationEventArgs : EventArgs
{
/// <summary>
/// Certificate
/// </summary>
public X509Certificate Certificate { get; internal set; }
/// <summary>
/// Certificate chain
/// </summary>
public X509Chain Chain { get; internal set; }
/// <summary>
/// SSL policy errors.
/// </summary>
public SslPolicyErrors SslPolicyErrors { get; internal set; }
/// <summary>
/// is a valid certificate?
/// </summary>
public bool IsValid { get; set; }
public void Dispose()
{
}
}
}
using System;
namespace Titanium.Web.Proxy.Exceptions
namespace Titanium.Web.Proxy.Exceptions
{
/// <summary>
/// An expception thrown when body is unexpectedly empty
/// </summary>
public class BodyNotFoundException : ProxyException
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="message"></param>
public BodyNotFoundException(string message)
: base(message)
{
}
}
}
\ No newline at end of file
}
......@@ -25,4 +25,4 @@ namespace Titanium.Web.Proxy.Exceptions
/// </summary>
public IEnumerable<HttpHeader> Headers { get; }
}
}
\ No newline at end of file
}
......@@ -24,4 +24,4 @@ namespace Titanium.Web.Proxy.Exceptions
{
}
}
}
\ No newline at end of file
}
......@@ -27,4 +27,4 @@ namespace Titanium.Web.Proxy.Exceptions
/// </remarks>
public SessionEventArgs SessionEventArgs { get; }
}
}
\ No newline at end of file
}
......@@ -2,7 +2,10 @@
namespace Titanium.Web.Proxy.Extensions
{
public static class ByteArrayExtensions
/// <summary>
/// Extension methods for Byte Arrays.
/// </summary>
internal static class ByteArrayExtensions
{
/// <summary>
/// Get the sub array from byte of data
......@@ -12,12 +15,11 @@ namespace Titanium.Web.Proxy.Extensions
/// <param name="index"></param>
/// <param name="length"></param>
/// <returns></returns>
public static T[] SubArray<T>(this T[] data, int index, int length)
internal static T[] SubArray<T>(this T[] data, int index, int length)
{
T[] result = new T[length];
var result = new T[length];
Array.Copy(data, index, result, 0, length);
return result;
}
}
}
using System.Text;
using System;
using System.Text;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Shared;
......@@ -29,7 +30,7 @@ namespace Titanium.Web.Proxy.Extensions
foreach (var contentType in contentTypes)
{
var encodingSplit = contentType.Split('=');
if (encodingSplit.Length == 2 && encodingSplit[0].ToLower().Trim() == "charset")
if (encodingSplit.Length == 2 && encodingSplit[0].Trim().Equals("charset", StringComparison.CurrentCultureIgnoreCase))
{
return Encoding.GetEncoding(encodingSplit[1]);
}
......@@ -45,4 +46,4 @@ namespace Titanium.Web.Proxy.Extensions
return Encoding.GetEncoding("ISO-8859-1");
}
}
}
\ No newline at end of file
}
using System.Text;
using System;
using System.Text;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Shared;
......@@ -26,7 +27,7 @@ namespace Titanium.Web.Proxy.Extensions
foreach (var contentType in contentTypes)
{
var encodingSplit = contentType.Split('=');
if (encodingSplit.Length == 2 && encodingSplit[0].ToLower().Trim() == "charset")
if (encodingSplit.Length == 2 && encodingSplit[0].Trim().Equals("charset", StringComparison.CurrentCultureIgnoreCase))
{
return Encoding.GetEncoding(encodingSplit[1]);
}
......@@ -42,4 +43,4 @@ namespace Titanium.Web.Proxy.Extensions
return Encoding.GetEncoding("ISO-8859-1");
}
}
}
\ No newline at end of file
}
......@@ -30,15 +30,15 @@ namespace Titanium.Web.Proxy.Extensions
await input.CopyToAsync(output);
}
/// <summary>
/// copies the specified bytes to the stream from the input stream
/// </summary>
/// <param name="streamReader"></param>
/// <param name="bufferSize"></param>
/// <param name="stream"></param>
/// <param name="totalBytesToRead"></param>
/// <returns></returns>
internal static async Task CopyBytesToStream(this CustomBinaryReader streamReader, int bufferSize, Stream stream, long totalBytesToRead)
/// <summary>
/// copies the specified bytes to the stream from the input stream
/// </summary>
/// <param name="streamReader"></param>
/// <param name="bufferSize"></param>
/// <param name="stream"></param>
/// <param name="totalBytesToRead"></param>
/// <returns></returns>
internal static async Task CopyBytesToStream(this CustomBinaryReader streamReader, int bufferSize, Stream stream, long totalBytesToRead)
{
var totalbytesRead = 0;
......@@ -46,7 +46,7 @@ namespace Titanium.Web.Proxy.Extensions
while (totalbytesRead < totalBytesToRead)
{
var buffer = await streamReader.ReadBytesAsync(bufferSize, bytesToRead);
var buffer = await streamReader.ReadBytesAsync(bytesToRead);
if (buffer.Length == 0)
{
......@@ -65,14 +65,13 @@ namespace Titanium.Web.Proxy.Extensions
}
}
/// <summary>
/// Copies the stream chunked
/// </summary>
/// <param name="clientStreamReader"></param>
/// <param name="bufferSize"></param>
/// <param name="stream"></param>
/// <returns></returns>
internal static async Task CopyBytesToStreamChunked(this CustomBinaryReader clientStreamReader, int bufferSize, Stream stream)
/// <summary>
/// Copies the stream chunked
/// </summary>
/// <param name="clientStreamReader"></param>
/// <param name="stream"></param>
/// <returns></returns>
internal static async Task CopyBytesToStreamChunked(this CustomBinaryReader clientStreamReader, Stream stream)
{
while (true)
{
......@@ -81,7 +80,7 @@ namespace Titanium.Web.Proxy.Extensions
if (chunkSize != 0)
{
var buffer = await clientStreamReader.ReadBytesAsync(bufferSize, chunkSize);
var buffer = await clientStreamReader.ReadBytesAsync(chunkSize);
await stream.WriteAsync(buffer, 0, buffer.Length);
//chunk trail
await clientStreamReader.ReadLineAsync();
......@@ -93,6 +92,7 @@ namespace Titanium.Web.Proxy.Extensions
}
}
}
/// <summary>
/// Writes the byte array body to the given stream; optionally chunked
/// </summary>
......@@ -112,17 +112,17 @@ namespace Titanium.Web.Proxy.Extensions
}
}
/// <summary>
/// Copies the specified content length number of bytes to the output stream from the given inputs stream
/// optionally chunked
/// </summary>
/// <param name="inStreamReader"></param>
/// <param name="bufferSize"></param>
/// <param name="outStream"></param>
/// <param name="isChunked"></param>
/// <param name="contentLength"></param>
/// <returns></returns>
internal static async Task WriteResponseBody(this CustomBinaryReader inStreamReader, int bufferSize, Stream outStream, bool isChunked, long contentLength)
/// <summary>
/// Copies the specified content length number of bytes to the output stream from the given inputs stream
/// optionally chunked
/// </summary>
/// <param name="inStreamReader"></param>
/// <param name="bufferSize"></param>
/// <param name="outStream"></param>
/// <param name="isChunked"></param>
/// <param name="contentLength"></param>
/// <returns></returns>
internal static async Task WriteResponseBody(this CustomBinaryReader inStreamReader, int bufferSize, Stream outStream, bool isChunked, long contentLength)
{
if (!isChunked)
{
......@@ -136,7 +136,7 @@ namespace Titanium.Web.Proxy.Extensions
if (contentLength < bufferSize)
{
bytesToRead = (int)contentLength;
bytesToRead = (int) contentLength;
}
var buffer = new byte[bufferSize];
......@@ -154,23 +154,22 @@ namespace Titanium.Web.Proxy.Extensions
bytesRead = 0;
var remainingBytes = (contentLength - totalBytesRead);
bytesToRead = remainingBytes > (long)bufferSize ? bufferSize : (int)remainingBytes;
bytesToRead = remainingBytes > (long) bufferSize ? bufferSize : (int) remainingBytes;
}
}
else
{
await WriteResponseBodyChunked(inStreamReader, bufferSize, outStream);
await WriteResponseBodyChunked(inStreamReader, outStream);
}
}
/// <summary>
/// Copies the streams chunked
/// </summary>
/// <param name="inStreamReader"></param>
/// <param name="bufferSize"></param>
/// <param name="outStream"></param>
/// <returns></returns>
internal static async Task WriteResponseBodyChunked(this CustomBinaryReader inStreamReader, int bufferSize, Stream outStream)
/// <summary>
/// Copies the streams chunked
/// </summary>
/// <param name="inStreamReader"></param>
/// <param name="outStream"></param>
/// <returns></returns>
internal static async Task WriteResponseBodyChunked(this CustomBinaryReader inStreamReader, Stream outStream)
{
while (true)
{
......@@ -179,7 +178,7 @@ namespace Titanium.Web.Proxy.Extensions
if (chunkSize != 0)
{
var buffer = await inStreamReader.ReadBytesAsync(bufferSize, chunkSize);
var buffer = await inStreamReader.ReadBytesAsync(chunkSize);
var chunkHeadBytes = Encoding.ASCII.GetBytes(chunkSize.ToString("x2"));
......@@ -199,6 +198,7 @@ namespace Titanium.Web.Proxy.Extensions
}
}
}
/// <summary>
/// Copies the given input bytes to output stream chunked
/// </summary>
......@@ -216,6 +216,5 @@ namespace Titanium.Web.Proxy.Extensions
await outStream.WriteAsync(ProxyConstants.ChunkEnd, 0, ProxyConstants.ChunkEnd.Length);
}
}
}
\ No newline at end of file
}
using System.Globalization;
namespace Titanium.Web.Proxy.Extensions
{
internal static class StringExtensions
{
internal static bool ContainsIgnoreCase(this string str, string value)
{
return CultureInfo.CurrentCulture.CompareInfo.IndexOf(str, value, CompareOptions.IgnoreCase) >= 0;
}
}
}
using System.Net.Sockets;
using Titanium.Web.Proxy.Helpers;
namespace Titanium.Web.Proxy.Extensions
{
......@@ -12,11 +13,11 @@ namespace Titanium.Web.Proxy.Extensions
internal static bool IsConnected(this Socket client)
{
// This is how you can determine whether a socket is still connected.
bool blockingState = client.Blocking;
var blockingState = client.Blocking;
try
{
byte[] tmp = new byte[1];
var tmp = new byte[1];
client.Blocking = false;
client.Send(tmp, 0, 0);
......@@ -25,20 +26,32 @@ namespace Titanium.Web.Proxy.Extensions
catch (SocketException e)
{
// 10035 == WSAEWOULDBLOCK
if (e.NativeErrorCode.Equals(10035))
{
return true;
}
else
{
return false;
}
return e.NativeErrorCode.Equals(10035);
}
finally
{
client.Blocking = blockingState;
}
}
}
/// <summary>
/// Gets the local port from a native TCP row object.
/// </summary>
/// <param name="tcpRow">The TCP row.</param>
/// <returns>The local port</returns>
internal static int GetLocalPort(this NativeMethods.TcpRow tcpRow)
{
return (tcpRow.localPort1 << 8) + tcpRow.localPort2 + (tcpRow.localPort3 << 24) + (tcpRow.localPort4 << 16);
}
/// <summary>
/// Gets the remote port from a native TCP row object.
/// </summary>
/// <param name="tcpRow">The TCP row.</param>
/// <returns>The remote port</returns>
internal static int GetRemotePort(this NativeMethods.TcpRow tcpRow)
{
return (tcpRow.remotePort1 << 8) + tcpRow.remotePort2 + (tcpRow.remotePort3 << 24) + (tcpRow.remotePort4 << 16);
}
}
}
......@@ -3,12 +3,9 @@ using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Helpers
{
/// <summary>
/// A custom binary reader that would allo us to read string line by line
/// using the specified encoding
......@@ -16,15 +13,20 @@ namespace Titanium.Web.Proxy.Helpers
/// </summary>
internal class CustomBinaryReader : IDisposable
{
private Stream stream;
private Encoding encoding;
private readonly CustomBufferedStream stream;
private readonly int bufferSize;
private readonly byte[] staticBuffer;
private readonly Encoding encoding;
internal CustomBinaryReader(Stream stream)
internal CustomBinaryReader(CustomBufferedStream stream, int bufferSize)
{
this.stream = stream;
staticBuffer = new byte[bufferSize];
this.bufferSize = bufferSize;
//default to UTF-8
this.encoding = Encoding.UTF8;
encoding = Encoding.UTF8;
}
internal Stream BaseStream => stream;
......@@ -35,33 +37,41 @@ namespace Titanium.Web.Proxy.Helpers
/// <returns></returns>
internal async Task<string> ReadLineAsync()
{
using (var readBuffer = new MemoryStream())
var lastChar = default(byte);
int bufferDataLength = 0;
// try to use the thread static buffer, usually it is enough
var buffer = staticBuffer;
while (stream.DataAvailable || await stream.FillBufferAsync())
{
var lastChar = default(char);
var buffer = new byte[1];
while ((await this.stream.ReadAsync(buffer, 0, 1)) > 0)
{
//if new line
if (lastChar == '\r' && buffer[0] == '\n')
{
var result = readBuffer.ToArray();
return encoding.GetString(result.SubArray(0, result.Length - 1));
}
//end of stream
if (buffer[0] == '\0')
{
return encoding.GetString(readBuffer.ToArray());
}
await readBuffer.WriteAsync(buffer,0,1);
//store last char for new line comparison
lastChar = (char)buffer[0];
}
return encoding.GetString(readBuffer.ToArray());
var newChar = stream.ReadByteFromBuffer();
buffer[bufferDataLength] = newChar;
//if new line
if (lastChar == '\r' && newChar == '\n')
{
return encoding.GetString(buffer, 0, bufferDataLength - 1);
}
//end of stream
if (newChar == '\0')
{
return encoding.GetString(buffer, 0, bufferDataLength);
}
bufferDataLength++;
//store last char for new line comparison
lastChar = newChar;
if (bufferDataLength == buffer.Length)
{
ResizeBuffer(ref buffer, bufferDataLength * 2);
}
}
return encoding.GetString(buffer, 0, bufferDataLength);
}
/// <summary>
......@@ -79,46 +89,72 @@ namespace Titanium.Web.Proxy.Helpers
return requestLines;
}
/// <summary>
/// Read until the last new line, ignores the result
/// </summary>
/// <returns></returns>
internal async Task ReadAndIgnoreAllLinesAsync()
{
while (!string.IsNullOrEmpty(await ReadLineAsync()))
{
}
}
/// <summary>
/// Read the specified number of raw bytes from the base stream
/// </summary>
/// <param name="totalBytesToRead"></param>
/// <returns></returns>
internal async Task<byte[]> ReadBytesAsync(int bufferSize, long totalBytesToRead)
internal async Task<byte[]> ReadBytesAsync(long totalBytesToRead)
{
int bytesToRead = bufferSize;
var buffer = staticBuffer;
if (totalBytesToRead < bufferSize)
bytesToRead = (int)totalBytesToRead;
var buffer = new byte[bufferSize];
{
bytesToRead = (int) totalBytesToRead;
buffer = new byte[bytesToRead];
}
var bytesRead = 0;
int bytesRead;
var totalBytesRead = 0;
using (var outStream = new MemoryStream())
while ((bytesRead = await stream.ReadAsync(buffer, totalBytesRead, bytesToRead)) > 0)
{
while ((bytesRead += await this.stream.ReadAsync(buffer, 0, bytesToRead)) > 0)
{
await outStream.WriteAsync(buffer, 0, bytesRead);
totalBytesRead += bytesRead;
totalBytesRead += bytesRead;
if (totalBytesRead == totalBytesToRead)
break;
if (totalBytesRead == totalBytesToRead)
break;
bytesRead = 0;
var remainingBytes = (totalBytesToRead - totalBytesRead);
bytesToRead = remainingBytes > (long)bufferSize ? bufferSize : (int)remainingBytes;
var remainingBytes = totalBytesToRead - totalBytesRead;
bytesToRead = Math.Min(bufferSize, (int) remainingBytes);
if (totalBytesRead + bytesToRead > buffer.Length)
{
ResizeBuffer(ref buffer, Math.Min(totalBytesToRead, buffer.Length * 2));
}
}
return outStream.ToArray();
if (totalBytesRead != buffer.Length)
{
//Normally this should not happen. Resize the buffer anyway
var newBuffer = new byte[totalBytesRead];
Buffer.BlockCopy(buffer, 0, newBuffer, 0, totalBytesRead);
buffer = newBuffer;
}
return buffer;
}
public void Dispose()
{
}
private void ResizeBuffer(ref byte[] buffer, long size)
{
var newBuffer = new byte[size];
Buffer.BlockCopy(buffer, 0, newBuffer, 0, buffer.Length);
buffer = newBuffer;
}
}
}
\ No newline at end of file
}
This diff is collapsed.
......@@ -6,9 +6,12 @@ namespace Titanium.Web.Proxy.Helpers
/// <summary>
/// A helper class to set proxy settings for firefox
/// </summary>
public class FireFoxProxySettingsManager
internal class FireFoxProxySettingsManager
{
public void AddFirefox()
/// <summary>
/// Add Firefox settings.
/// </summary>
internal void AddFirefox()
{
try
{
......@@ -16,21 +19,17 @@ namespace Titanium.Web.Proxy.Helpers
new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) +
"\\Mozilla\\Firefox\\Profiles\\").GetDirectories("*.default");
var myFfPrefFile = myProfileDirectory[0].FullName + "\\prefs.js";
if (File.Exists(myFfPrefFile))
{
// We have a pref file so let''s make sure it has the proxy setting
var myReader = new StreamReader(myFfPrefFile);
var myPrefContents = myReader.ReadToEnd();
myReader.Close();
if (myPrefContents.Contains("user_pref(\"network.proxy.type\", 0);"))
{
// Add the proxy enable line and write it back to the file
myPrefContents = myPrefContents.Replace("user_pref(\"network.proxy.type\", 0);", "");
if (!File.Exists(myFfPrefFile)) return;
// We have a pref file so let''s make sure it has the proxy setting
var myReader = new StreamReader(myFfPrefFile);
var myPrefContents = myReader.ReadToEnd();
myReader.Close();
if (!myPrefContents.Contains("user_pref(\"network.proxy.type\", 0);")) return;
// Add the proxy enable line and write it back to the file
myPrefContents = myPrefContents.Replace("user_pref(\"network.proxy.type\", 0);", "");
File.Delete(myFfPrefFile);
File.WriteAllText(myFfPrefFile, myPrefContents);
}
}
File.Delete(myFfPrefFile);
File.WriteAllText(myFfPrefFile, myPrefContents);
}
catch (Exception)
{
......@@ -38,7 +37,10 @@ namespace Titanium.Web.Proxy.Helpers
}
}
public void RemoveFirefox()
/// <summary>
/// Remove firefox settings.
/// </summary>
internal void RemoveFirefox()
{
try
{
......@@ -46,20 +48,18 @@ namespace Titanium.Web.Proxy.Helpers
new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) +
"\\Mozilla\\Firefox\\Profiles\\").GetDirectories("*.default");
var myFfPrefFile = myProfileDirectory[0].FullName + "\\prefs.js";
if (File.Exists(myFfPrefFile))
if (!File.Exists(myFfPrefFile)) return;
// We have a pref file so let''s make sure it has the proxy setting
var myReader = new StreamReader(myFfPrefFile);
var myPrefContents = myReader.ReadToEnd();
myReader.Close();
if (!myPrefContents.Contains("user_pref(\"network.proxy.type\", 0);"))
{
// We have a pref file so let''s make sure it has the proxy setting
var myReader = new StreamReader(myFfPrefFile);
var myPrefContents = myReader.ReadToEnd();
myReader.Close();
if (!myPrefContents.Contains("user_pref(\"network.proxy.type\", 0);"))
{
// Add the proxy enable line and write it back to the file
myPrefContents = myPrefContents + "\n\r" + "user_pref(\"network.proxy.type\", 0);";
// Add the proxy enable line and write it back to the file
myPrefContents = myPrefContents + "\n\r" + "user_pref(\"network.proxy.type\", 0);";
File.Delete(myFfPrefFile);
File.WriteAllText(myFfPrefFile, myPrefContents);
}
File.Delete(myFfPrefFile);
File.WriteAllText(myFfPrefFile, myPrefContents);
}
}
catch (Exception)
......@@ -68,4 +68,4 @@ namespace Titanium.Web.Proxy.Helpers
}
}
}
}
\ No newline at end of file
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
namespace Titanium.Web.Proxy.Helpers
{
......@@ -11,8 +8,7 @@ namespace Titanium.Web.Proxy.Helpers
{
private static int FindProcessIdFromLocalPort(int port, IpVersion ipVersion)
{
var tcpRow = TcpHelper.GetExtendedTcpTable(ipVersion).FirstOrDefault(
row => row.LocalEndPoint.Port == port);
var tcpRow = TcpHelper.GetTcpRowByLocalPort(ipVersion, port);
return tcpRow?.ProcessId ?? 0;
}
......@@ -33,28 +29,50 @@ namespace Titanium.Web.Proxy.Helpers
/// Adapated from below link
/// http://stackoverflow.com/questions/11834091/how-to-check-if-localhost
/// </summary>
/// <param name="address></param>
/// <param name="address"></param>
/// <returns></returns>
internal static bool IsLocalIpAddress(IPAddress address)
{
try
// get local IP addresses
var localIPs = Dns.GetHostAddresses(Dns.GetHostName());
// test if any host IP equals to any local IP or to localhost
return IPAddress.IsLoopback(address) || localIPs.Contains(address);
}
internal static bool IsLocalIpAddress(string hostName)
{
bool isLocalhost = false;
IPHostEntry localhost = Dns.GetHostEntry("127.0.0.1");
if (hostName == localhost.HostName)
{
IPHostEntry hostEntry = Dns.GetHostEntry(hostName);
isLocalhost = hostEntry.AddressList.Any(IPAddress.IsLoopback);
}
if (!isLocalhost)
{
// get local IP addresses
IPAddress[] localIPs = Dns.GetHostAddresses(Dns.GetHostName());
localhost = Dns.GetHostEntry(Dns.GetHostName());
IPAddress ipAddress;
// test if any host IP equals to any local IP or to localhost
if (IPAddress.TryParse(hostName, out ipAddress))
isLocalhost = localhost.AddressList.Any(x => x.Equals(ipAddress));
// is localhost
if (IPAddress.IsLoopback(address)) return true;
// is local address
foreach (IPAddress localIP in localIPs)
if (!isLocalhost)
{
if (address.Equals(localIP)) return true;
try
{
var hostEntry = Dns.GetHostEntry(hostName);
isLocalhost = localhost.AddressList.Any(x => hostEntry.AddressList.Any(x.Equals));
}
catch (SocketException)
{
}
}
}
catch { }
return false;
return isLocalhost;
}
}
}
using System;
namespace Titanium.Web.Proxy.Helpers
{
/// <summary>
/// Run time helpers
/// </summary>
internal class RunTime
{
/// <summary>
/// Checks if current run time is Mono
/// </summary>
/// <returns></returns>
internal static bool IsRunningOnMono()
{
return Type.GetType("Mono.Runtime") != null;
}
}
}
......@@ -4,11 +4,8 @@ using Microsoft.Win32;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
/// <summary>
/// Helper classes for setting system proxy settings
/// </summary>
// Helper classes for setting system proxy settings
namespace Titanium.Web.Proxy.Helpers
{
internal enum ProxyProtocolType
......@@ -39,7 +36,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <summary>
/// Manage system proxy settings
/// </summary>
internal class SystemProxyManager
internal class SystemProxyManager
{
internal const int InternetOptionSettingsChanged = 39;
internal const int InternetOptionRefresh = 37;
......@@ -128,7 +125,7 @@ namespace Titanium.Web.Proxy.Helpers
}
/// <summary>
/// Removes all types of proxy settings (both http & https)
/// Removes all types of proxy settings (both http and https)
/// </summary>
internal void DisableAllProxy()
{
......@@ -184,17 +181,17 @@ namespace Titanium.Web.Proxy.Helpers
if (tmp.StartsWith("http=") || tmp.StartsWith("https="))
{
var endPoint = tmp.Substring(5);
return new HttpSystemProxyValue()
return new HttpSystemProxyValue
{
HostName = endPoint.Split(':')[0],
Port = int.Parse(endPoint.Split(':')[1]),
IsHttps = tmp.StartsWith("https=")
};
};
}
return null;
}
/// <summary>
/// Prepares the proxy server registry (create empty values if they don't exist)
/// </summary>
......@@ -210,7 +207,6 @@ namespace Titanium.Web.Proxy.Helpers
{
reg.SetValue("ProxyServer", string.Empty);
}
}
/// <summary>
......@@ -222,4 +218,4 @@ namespace Titanium.Web.Proxy.Helpers
NativeMethods.InternetSetOption(IntPtr.Zero, InternetOptionRefresh, IntPtr.Zero, 0);
}
}
}
\ No newline at end of file
}
......@@ -42,7 +42,7 @@ namespace Titanium.Web.Proxy.Helpers
}
/// <summary>
/// <see cref="http://msdn2.microsoft.com/en-us/library/aa366921.aspx"/>
/// <see href="http://msdn2.microsoft.com/en-us/library/aa366921.aspx"/>
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct TcpTable
......@@ -52,7 +52,7 @@ namespace Titanium.Web.Proxy.Helpers
}
/// <summary>
/// <see cref="http://msdn2.microsoft.com/en-us/library/aa366913.aspx"/>
/// <see href="http://msdn2.microsoft.com/en-us/library/aa366913.aspx"/>
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct TcpRow
......@@ -72,7 +72,7 @@ namespace Titanium.Web.Proxy.Helpers
}
/// <summary>
/// <see cref="http://msdn2.microsoft.com/en-us/library/aa365928.aspx"/>
/// <see href="http://msdn2.microsoft.com/en-us/library/aa365928.aspx"/>
/// </summary>
[DllImport("iphlpapi.dll", SetLastError = true)]
internal static extern uint GetExtendedTcpTable(IntPtr tcpTable, ref int size, bool sort, int ipVersion, int tableClass, int reserved);
......@@ -124,28 +124,70 @@ namespace Titanium.Web.Proxy.Helpers
}
/// <summary>
/// relays the input clientStream to the server at the specified host name & port with the given httpCmd & headers as prefix
/// Gets the TCP row by local port number.
/// </summary>
/// <returns><see cref="TcpRow"/>.</returns>
internal static TcpRow GetTcpRowByLocalPort(IpVersion ipVersion, int localPort)
{
IntPtr tcpTable = IntPtr.Zero;
int tcpTableLength = 0;
var ipVersionValue = ipVersion == IpVersion.Ipv4 ? NativeMethods.AfInet : NativeMethods.AfInet6;
if (NativeMethods.GetExtendedTcpTable(tcpTable, ref tcpTableLength, false, ipVersionValue, (int)NativeMethods.TcpTableType.OwnerPidAll, 0) != 0)
{
try
{
tcpTable = Marshal.AllocHGlobal(tcpTableLength);
if (NativeMethods.GetExtendedTcpTable(tcpTable, ref tcpTableLength, true, ipVersionValue, (int)NativeMethods.TcpTableType.OwnerPidAll, 0) == 0)
{
NativeMethods.TcpTable table = (NativeMethods.TcpTable)Marshal.PtrToStructure(tcpTable, typeof(NativeMethods.TcpTable));
IntPtr rowPtr = (IntPtr)((long)tcpTable + Marshal.SizeOf(table.length));
for (int i = 0; i < table.length; ++i)
{
var tcpRow = (NativeMethods.TcpRow)Marshal.PtrToStructure(rowPtr, typeof(NativeMethods.TcpRow));
if (tcpRow.GetLocalPort() == localPort)
{
return new TcpRow(tcpRow);
}
rowPtr = (IntPtr)((long)rowPtr + Marshal.SizeOf(typeof(NativeMethods.TcpRow)));
}
}
}
finally
{
if (tcpTable != IntPtr.Zero)
{
Marshal.FreeHGlobal(tcpTable);
}
}
}
return null;
}
/// <summary>
/// relays the input clientStream to the server at the specified host name and port with the given httpCmd and headers as prefix
/// Usefull for websocket requests
/// </summary>
/// <param name="bufferSize"></param>
/// <param name="connectionTimeOutSeconds"></param>
/// <param name="server"></param>
/// <param name="remoteHostName"></param>
/// <param name="remotePort"></param>
/// <param name="httpCmd"></param>
/// <param name="httpVersion"></param>
/// <param name="requestHeaders"></param>
/// <param name="isHttps"></param>
/// <param name="remotePort"></param>
/// <param name="supportedProtocols"></param>
/// <param name="remoteCertificateValidationCallback"></param>
/// <param name="localCertificateSelectionCallback"></param>
/// <param name="clientStream"></param>
/// <param name="tcpConnectionFactory"></param>
/// <returns></returns>
internal static async Task SendRaw(int bufferSize, int connectionTimeOutSeconds,
string remoteHostName, int remotePort, string httpCmd, Version httpVersion, Dictionary<string, HttpHeader> requestHeaders,
bool isHttps, SslProtocols supportedProtocols,
RemoteCertificateValidationCallback remoteCertificateValidationCallback, LocalCertificateSelectionCallback localCertificateSelectionCallback,
Stream clientStream, TcpConnectionFactory tcpConnectionFactory, IPEndPoint upStreamEndPoint)
internal static async Task SendRaw(ProxyServer server,
string remoteHostName, int remotePort,
string httpCmd, Version httpVersion, Dictionary<string, HttpHeader> requestHeaders,
bool isHttps,
Stream clientStream, TcpConnectionFactory tcpConnectionFactory)
{
//prepare the prefix content
StringBuilder sb = null;
......@@ -171,12 +213,11 @@ namespace Titanium.Web.Proxy.Helpers
sb.Append(ProxyConstants.NewLine);
}
var tcpConnection = await tcpConnectionFactory.CreateClient(bufferSize, connectionTimeOutSeconds,
remoteHostName, remotePort,
httpVersion, isHttps,
supportedProtocols, remoteCertificateValidationCallback, localCertificateSelectionCallback,
null, null, clientStream, upStreamEndPoint);
var tcpConnection = await tcpConnectionFactory.CreateClient(server,
remoteHostName, remotePort,
httpVersion, isHttps,
null, null, clientStream);
try
{
Stream tunnelStream = tcpConnection.Stream;
......@@ -191,7 +232,8 @@ namespace Titanium.Web.Proxy.Helpers
finally
{
tcpConnection.Dispose();
server.ServerConnectionCount--;
}
}
}
}
\ No newline at end of file
}
using System.Collections.Generic;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Http
{
internal static class HeaderParser
{
internal static async Task ReadHeaders(CustomBinaryReader reader,
Dictionary<string, List<HttpHeader>> nonUniqueResponseHeaders,
Dictionary<string, HttpHeader> headers)
{
string tmpLine;
while (!string.IsNullOrEmpty(tmpLine = await reader.ReadLineAsync()))
{
var header = tmpLine.Split(ProxyConstants.ColonSplit, 2);
var newHeader = new HttpHeader(header[0], header[1]);
//if header exist in non-unique header collection add it there
if (nonUniqueResponseHeaders.ContainsKey(newHeader.Name))
{
nonUniqueResponseHeaders[newHeader.Name].Add(newHeader);
}
//if header is alread in unique header collection then move both to non-unique collection
else if (headers.ContainsKey(newHeader.Name))
{
var existing = headers[newHeader.Name];
var nonUniqueHeaders = new List<HttpHeader> { existing, newHeader };
nonUniqueResponseHeaders.Add(newHeader.Name, nonUniqueHeaders);
headers.Remove(newHeader.Name);
}
//add to unique header collection
else
{
headers.Add(newHeader.Name, newHeader);
}
}
}
}
}
This diff is collapsed.
......@@ -34,12 +34,7 @@ namespace Titanium.Web.Proxy.Http
get
{
var hasHeader = RequestHeaders.ContainsKey("host");
if (hasHeader)
{
return RequestHeaders["host"].Value;
}
return null;
return hasHeader ? RequestHeaders["host"].Value : null;
}
set
{
......@@ -52,7 +47,6 @@ namespace Titanium.Web.Proxy.Http
{
RequestHeaders.Add("Host", new HttpHeader("Host", value));
}
}
}
......@@ -124,9 +118,7 @@ namespace Titanium.Web.Proxy.Http
{
RequestHeaders.Remove("content-length");
}
}
}
}
......@@ -154,14 +146,13 @@ namespace Titanium.Web.Proxy.Http
if (hasHeader)
{
var header = RequestHeaders["content-type"];
header.Value = value.ToString();
header.Value = value;
}
else
{
RequestHeaders.Add("content-type", new HttpHeader("content-type", value.ToString()));
RequestHeaders.Add("content-type", new HttpHeader("content-type", value));
}
}
}
/// <summary>
......@@ -177,7 +168,7 @@ namespace Titanium.Web.Proxy.Http
{
var header = RequestHeaders["transfer-encoding"];
return header.Value.ToLower().Contains("chunked");
return header.Value.ContainsIgnoreCase("chunked");
}
return false;
......@@ -219,14 +210,10 @@ namespace Titanium.Web.Proxy.Http
{
var hasHeader = RequestHeaders.ContainsKey("expect");
if (hasHeader)
{
var header = RequestHeaders["expect"];
if (!hasHeader) return false;
var header = RequestHeaders["expect"];
return header.Value.Equals("100-continue");
}
return false;
return header.Value.Equals("100-continue");
}
}
......@@ -235,12 +222,12 @@ namespace Titanium.Web.Proxy.Http
/// </summary>
public string Url => RequestUri.OriginalString;
/// <summary>
/// <summary>
/// Encoding for this request
/// </summary>
internal Encoding Encoding => this.GetEncoding();
/// <summary>
/// <summary>
/// Terminates the underlying Tcp Connection to client after current request
/// </summary>
internal bool CancelRequest { get; set; }
......@@ -254,9 +241,9 @@ namespace Titanium.Web.Proxy.Http
/// request body as string
/// </summary>
internal string RequestBodyString { get; set; }
internal bool RequestBodyRead { get; set; }
internal bool RequestLocked { get; set; }
/// <summary>
......@@ -275,13 +262,7 @@ namespace Titanium.Web.Proxy.Http
var header = RequestHeaders["upgrade"];
if (header.Value.ToLower() == "websocket")
{
return true;
}
return false;
return header.Value.Equals("websocket", StringComparison.CurrentCultureIgnoreCase);
}
}
......@@ -305,11 +286,13 @@ namespace Titanium.Web.Proxy.Http
/// </summary>
public bool ExpectationFailed { get; internal set; }
/// <summary>
/// Constructor.
/// </summary>
public Request()
{
RequestHeaders = new Dictionary<string, HttpHeader>(StringComparer.OrdinalIgnoreCase);
NonUniqueRequestHeaders = new Dictionary<string, List<HttpHeader>>(StringComparer.OrdinalIgnoreCase);
}
}
}
......@@ -12,12 +12,19 @@ namespace Titanium.Web.Proxy.Http
/// </summary>
public class Response
{
/// <summary>
/// Response Status Code.
/// </summary>
public string ResponseStatusCode { get; set; }
/// <summary>
/// Response Status description.
/// </summary>
public string ResponseStatusDescription { get; set; }
internal Encoding Encoding => this.GetResponseCharacterEncoding();
/// <summary>
/// <summary>
/// Content encoding for this response
/// </summary>
internal string ContentEncoding
......@@ -26,14 +33,10 @@ namespace Titanium.Web.Proxy.Http
{
var hasHeader = ResponseHeaders.ContainsKey("content-encoding");
if (hasHeader)
{
var header = ResponseHeaders["content-encoding"];
return header.Value.Trim();
}
if (!hasHeader) return null;
var header = ResponseHeaders["content-encoding"];
return null;
return header.Value.Trim();
}
}
......@@ -52,14 +55,13 @@ namespace Titanium.Web.Proxy.Http
{
var header = ResponseHeaders["connection"];
if (header.Value.ToLower().Contains("close"))
if (header.Value.ContainsIgnoreCase("close"))
{
return false;
}
}
return true;
}
}
......@@ -80,7 +82,6 @@ namespace Titanium.Web.Proxy.Http
}
return null;
}
}
......@@ -108,7 +109,6 @@ namespace Titanium.Web.Proxy.Http
}
return -1;
}
set
{
......@@ -151,14 +151,13 @@ namespace Titanium.Web.Proxy.Http
{
var header = ResponseHeaders["transfer-encoding"];
if (header.Value.ToLower().Contains("chunked"))
if (header.Value.ContainsIgnoreCase("chunked"))
{
return true;
}
}
}
return false;
}
set
{
......@@ -184,9 +183,7 @@ namespace Titanium.Web.Proxy.Http
{
ResponseHeaders.Remove("transfer-encoding");
}
}
}
}
......@@ -229,11 +226,13 @@ namespace Titanium.Web.Proxy.Http
/// </summary>
public bool ExpectationFailed { get; internal set; }
/// <summary>
/// Constructor.
/// </summary>
public Response()
{
this.ResponseHeaders = new Dictionary<string, HttpHeader>(StringComparer.OrdinalIgnoreCase);
this.NonUniqueResponseHeaders = new Dictionary<string, List<HttpHeader>>(StringComparer.OrdinalIgnoreCase);
ResponseHeaders = new Dictionary<string, HttpHeader>(StringComparer.OrdinalIgnoreCase);
NonUniqueResponseHeaders = new Dictionary<string, List<HttpHeader>>(StringComparer.OrdinalIgnoreCase);
}
}
}
using System.Net;
namespace Titanium.Web.Proxy.Http.Responses
{
/// <summary>
/// Anything other than a 200 or 302 response
/// </summary>
public class GenericResponse : Response
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="status"></param>
public GenericResponse(HttpStatusCode status)
{
ResponseStatusCode = ((int) status).ToString();
ResponseStatusDescription = status.ToString();
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="statusCode"></param>
/// <param name="statusDescription"></param>
public GenericResponse(string statusCode, string statusDescription)
{
ResponseStatusCode = statusCode;
ResponseStatusDescription = statusDescription;
}
}
}
......@@ -5,6 +5,9 @@
/// </summary>
public sealed class OkResponse : Response
{
/// <summary>
/// Constructor.
/// </summary>
public OkResponse()
{
ResponseStatusCode = "200";
......
......@@ -5,6 +5,9 @@
/// </summary>
public sealed class RedirectResponse : Response
{
/// <summary>
/// Constructor.
/// </summary>
public RedirectResponse()
{
ResponseStatusCode = "302";
......
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
using System.Text.RegularExpressions;
namespace Titanium.Web.Proxy.Models
{
......@@ -9,22 +13,42 @@ namespace Titanium.Web.Proxy.Models
/// </summary>
public abstract class ProxyEndPoint
{
public ProxyEndPoint(IPAddress IpAddress, int Port, bool EnableSsl)
/// <summary>
/// Constructor.
/// </summary>
/// <param name="ipAddress"></param>
/// <param name="port"></param>
/// <param name="enableSsl"></param>
protected ProxyEndPoint(IPAddress ipAddress, int port, bool enableSsl)
{
this.IpAddress = IpAddress;
this.Port = Port;
this.EnableSsl = EnableSsl;
IpAddress = ipAddress;
Port = port;
EnableSsl = enableSsl;
}
/// <summary>
/// Ip Address.
/// </summary>
public IPAddress IpAddress { get; internal set; }
/// <summary>
/// Port.
/// </summary>
public int Port { get; internal set; }
/// <summary>
/// Enable SSL?
/// </summary>
public bool EnableSsl { get; internal set; }
public bool IpV6Enabled => IpAddress == IPAddress.IPv6Any
|| IpAddress == IPAddress.IPv6Loopback
|| IpAddress == IPAddress.IPv6None;
/// <summary>
/// Is IPv6 enabled?
/// </summary>
public bool IpV6Enabled => Equals(IpAddress, IPAddress.IPv6Any)
|| Equals(IpAddress, IPAddress.IPv6Loopback)
|| Equals(IpAddress, IPAddress.IPv6None);
internal TcpListener listener { get; set; }
internal TcpListener Listener { get; set; }
}
/// <summary>
......@@ -33,15 +57,61 @@ namespace Titanium.Web.Proxy.Models
/// </summary>
public class ExplicitProxyEndPoint : ProxyEndPoint
{
internal List<Regex> ExcludedHttpsHostNameRegexList;
internal List<Regex> IncludedHttpsHostNameRegexList;
internal bool IsSystemHttpProxy { get; set; }
internal bool IsSystemHttpsProxy { get; set; }
public List<string> ExcludedHttpsHostNameRegex { get; set; }
/// <summary>
/// List of host names to exclude using Regular Expressions.
/// </summary>
public IEnumerable<string> ExcludedHttpsHostNameRegex
{
get { return ExcludedHttpsHostNameRegexList?.Select(x => x.ToString()).ToList(); }
set
{
if (IncludedHttpsHostNameRegex != null)
{
throw new ArgumentException("Cannot set excluded when included is set");
}
ExcludedHttpsHostNameRegexList = value?.Select(x => new Regex(x, RegexOptions.Compiled)).ToList();
}
}
/// <summary>
/// List of host names to exclude using Regular Expressions.
/// </summary>
public IEnumerable<string> IncludedHttpsHostNameRegex
{
get { return IncludedHttpsHostNameRegexList?.Select(x => x.ToString()).ToList(); }
set
{
if (ExcludedHttpsHostNameRegex != null)
{
throw new ArgumentException("Cannot set included when excluded is set");
}
IncludedHttpsHostNameRegexList = value?.Select(x => new Regex(x, RegexOptions.Compiled)).ToList();
}
}
public ExplicitProxyEndPoint(IPAddress IpAddress, int Port, bool EnableSsl)
: base(IpAddress, Port, EnableSsl)
/// <summary>
/// Generic certificate to use for SSL decryption.
/// </summary>
public X509Certificate2 GenericCertificate { get; set; }
/// <summary>
/// Constructor.
/// </summary>
/// <param name="ipAddress"></param>
/// <param name="port"></param>
/// <param name="enableSsl"></param>
public ExplicitProxyEndPoint(IPAddress ipAddress, int port, bool enableSsl)
: base(ipAddress, port, enableSsl)
{
}
}
......@@ -51,18 +121,22 @@ namespace Titanium.Web.Proxy.Models
/// </summary>
public class TransparentProxyEndPoint : ProxyEndPoint
{
//Name of the Certificate need to be sent (same as the hostname we want to proxy)
//This is valid only when UseServerNameIndication is set to false
/// <summary>
/// Name of the Certificate need to be sent (same as the hostname we want to proxy)
/// This is valid only when UseServerNameIndication is set to false
/// </summary>
public string GenericCertificateName { get; set; }
// public bool UseServerNameIndication { get; set; }
public TransparentProxyEndPoint(IPAddress IpAddress, int Port, bool EnableSsl)
: base(IpAddress, Port, EnableSsl)
/// <summary>
/// Constructor.
/// </summary>
/// <param name="ipAddress"></param>
/// <param name="port"></param>
/// <param name="enableSsl"></param>
public TransparentProxyEndPoint(IPAddress ipAddress, int port, bool enableSsl)
: base(ipAddress, port, enableSsl)
{
this.GenericCertificateName = "localhost";
GenericCertificateName = "localhost";
}
}
}
......@@ -8,41 +8,63 @@ namespace Titanium.Web.Proxy.Models
/// </summary>
public class ExternalProxy
{
private static readonly Lazy<NetworkCredential> DefaultCredentials = new Lazy<NetworkCredential>(() => CredentialCache.DefaultNetworkCredentials);
private static readonly Lazy<NetworkCredential> defaultCredentials = new Lazy<NetworkCredential>(() => CredentialCache.DefaultNetworkCredentials);
private string userName;
private string password;
/// <summary>
/// Use default windows credentials?
/// </summary>
public bool UseDefaultCredentials { get; set; }
public string UserName {
get { return UseDefaultCredentials ? DefaultCredentials.Value.UserName : userName; }
/// <summary>
/// Bypass this proxy for connections to localhost?
/// </summary>
public bool BypassForLocalhost { get; set; }
/// <summary>
/// Username.
/// </summary>
public string UserName
{
get { return UseDefaultCredentials ? defaultCredentials.Value.UserName : userName; }
set
{
userName = value;
if (DefaultCredentials.Value.UserName != userName)
if (defaultCredentials.Value.UserName != userName)
{
UseDefaultCredentials = false;
}
}
}
/// <summary>
/// Password.
/// </summary>
public string Password
{
get { return UseDefaultCredentials ? DefaultCredentials.Value.Password : password; }
get { return UseDefaultCredentials ? defaultCredentials.Value.Password : password; }
set
{
password = value;
if (DefaultCredentials.Value.Password != password)
if (defaultCredentials.Value.Password != password)
{
UseDefaultCredentials = false;
}
}
}
/// <summary>
/// Host name.
/// </summary>
public string HostName { get; set; }
/// <summary>
/// Port.
/// </summary>
public int Port { get; set; }
}
}
using System;
using System.IO;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Models
{
......@@ -7,6 +9,16 @@ namespace Titanium.Web.Proxy.Models
/// </summary>
public class HttpHeader
{
internal static Version Version10 = new Version(1, 0);
internal static Version Version11 = new Version(1, 1);
/// <summary>
/// Constructor.
/// </summary>
/// <param name="name"></param>
/// <param name="value"></param>
/// <exception cref="Exception"></exception>
public HttpHeader(string name, string value)
{
if (string.IsNullOrEmpty(name))
......@@ -18,7 +30,14 @@ namespace Titanium.Web.Proxy.Models
Value = value.Trim();
}
/// <summary>
/// Header Name.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Header Value.
/// </summary>
public string Value { get; set; }
/// <summary>
......@@ -27,7 +46,14 @@ namespace Titanium.Web.Proxy.Models
/// <returns></returns>
public override string ToString()
{
return string.Format("{0}: {1}", Name, Value);
return $"{Name}: {Value}";
}
internal async Task WriteToStream(StreamWriter writer)
{
await writer.WriteAsync(Name);
await writer.WriteAsync(": ");
await writer.WriteLineAsync(Value);
}
}
}
\ No newline at end of file
}
......@@ -20,6 +20,5 @@ namespace Titanium.Web.Proxy.Network
{
LastAccess = DateTime.Now;
}
}
}
using System;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Operators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Prng;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.X509;
namespace Titanium.Web.Proxy.Network.Certificate
{
/// <summary>
/// Implements certificate generation operations.
/// </summary>
internal class BCCertificateMaker : ICertificateMaker
{
private const int certificateValidDays = 1825;
private const int certificateGraceDays = 366;
/// <summary>
/// Makes the certificate.
/// </summary>
/// <param name="sSubjectCn">The s subject cn.</param>
/// <param name="isRoot">if set to <c>true</c> [is root].</param>
/// <param name="signingCert">The signing cert.</param>
/// <returns>X509Certificate2 instance.</returns>
public X509Certificate2 MakeCertificate(string sSubjectCn, bool isRoot, X509Certificate2 signingCert = null)
{
return MakeCertificateInternal(sSubjectCn, isRoot, true, signingCert);
}
/// <summary>
/// Generates the certificate.
/// </summary>
/// <param name="subjectName">Name of the subject.</param>
/// <param name="issuerName">Name of the issuer.</param>
/// <param name="validFrom">The valid from.</param>
/// <param name="validTo">The valid to.</param>
/// <param name="keyStrength">The key strength.</param>
/// <param name="signatureAlgorithm">The signature algorithm.</param>
/// <param name="issuerPrivateKey">The issuer private key.</param>
/// <param name="hostName">The host name</param>
/// <returns>X509Certificate2 instance.</returns>
/// <exception cref="PemException">Malformed sequence in RSA private key</exception>
private static X509Certificate2 GenerateCertificate(string hostName,
string subjectName,
string issuerName, DateTime validFrom,
DateTime validTo, int keyStrength = 2048,
string signatureAlgorithm = "SHA256WithRSA",
AsymmetricKeyParameter issuerPrivateKey = null)
{
// Generating Random Numbers
var randomGenerator = new CryptoApiRandomGenerator();
var secureRandom = new SecureRandom(randomGenerator);
// The Certificate Generator
var certificateGenerator = new X509V3CertificateGenerator();
// Serial Number
var serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(long.MaxValue), secureRandom);
certificateGenerator.SetSerialNumber(serialNumber);
// Issuer and Subject Name
var subjectDn = new X509Name(subjectName);
var issuerDn = new X509Name(issuerName);
certificateGenerator.SetIssuerDN(issuerDn);
certificateGenerator.SetSubjectDN(subjectDn);
certificateGenerator.SetNotBefore(validFrom);
certificateGenerator.SetNotAfter(validTo);
if (hostName != null)
{
//add subject alternative names
var subjectAlternativeNames = new Asn1Encodable[]
{
new GeneralName(GeneralName.DnsName, hostName),
};
var subjectAlternativeNamesExtension = new DerSequence(subjectAlternativeNames);
certificateGenerator.AddExtension(
X509Extensions.SubjectAlternativeName.Id, false, subjectAlternativeNamesExtension);
}
// Subject Public Key
var keyGenerationParameters = new KeyGenerationParameters(secureRandom, keyStrength);
var keyPairGenerator = new RsaKeyPairGenerator();
keyPairGenerator.Init(keyGenerationParameters);
var subjectKeyPair = keyPairGenerator.GenerateKeyPair();
certificateGenerator.SetPublicKey(subjectKeyPair.Public);
// Set certificate intended purposes to only Server Authentication
certificateGenerator.AddExtension(X509Extensions.ExtendedKeyUsage.Id, false, new ExtendedKeyUsage(KeyPurposeID.IdKPServerAuth));
var signatureFactory = new Asn1SignatureFactory(signatureAlgorithm, issuerPrivateKey ?? subjectKeyPair.Private, secureRandom);
// Self-sign the certificate
var certificate = certificateGenerator.Generate(signatureFactory);
var x509Certificate = new X509Certificate2(certificate.GetEncoded());
// Corresponding private key
var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(subjectKeyPair.Private);
var seq = (Asn1Sequence) Asn1Object.FromByteArray(privateKeyInfo.ParsePrivateKey().GetDerEncoded());
if (seq.Count != 9)
{
throw new PemException("Malformed sequence in RSA private key");
}
var rsa = RsaPrivateKeyStructure.GetInstance(seq);
var rsaparams = new RsaPrivateCrtKeyParameters(rsa.Modulus, rsa.PublicExponent, rsa.PrivateExponent, rsa.Prime1, rsa.Prime2, rsa.Exponent1, rsa.Exponent2, rsa.Coefficient);
// Set private key onto certificate instance
x509Certificate.PrivateKey = DotNetUtilities.ToRSA(rsaparams);
x509Certificate.FriendlyName = subjectName;
return x509Certificate;
}
/// <summary>
/// Makes the certificate internal.
/// </summary>
/// <param name="isRoot">if set to <c>true</c> [is root].</param>
/// <param name="hostName">hostname for certificate</param>
/// <param name="subjectName">The full subject.</param>
/// <param name="validFrom">The valid from.</param>
/// <param name="validTo">The valid to.</param>
/// <param name="signingCertificate">The signing certificate.</param>
/// <returns>X509Certificate2 instance.</returns>
/// <exception cref="System.ArgumentException">You must specify a Signing Certificate if and only if you are not creating a root.</exception>
private X509Certificate2 MakeCertificateInternal(bool isRoot,
string hostName, string subjectName,
DateTime validFrom, DateTime validTo, X509Certificate2 signingCertificate)
{
if (isRoot != (null == signingCertificate))
{
throw new ArgumentException("You must specify a Signing Certificate if and only if you are not creating a root.", nameof(signingCertificate));
}
return isRoot
? GenerateCertificate(null, subjectName, subjectName, validFrom, validTo)
: GenerateCertificate(hostName, subjectName, signingCertificate.Subject, validFrom, validTo, issuerPrivateKey: DotNetUtilities.GetKeyPair(signingCertificate.PrivateKey).Private);
}
/// <summary>
/// Makes the certificate internal.
/// </summary>
/// <param name="subject">The s subject cn.</param>
/// <param name="isRoot">if set to <c>true</c> [is root].</param>
/// <param name="switchToMtaIfNeeded">if set to <c>true</c> [switch to MTA if needed].</param>
/// <param name="signingCert">The signing cert.</param>
/// <param name="cancellationToken">Task cancellation token</param>
/// <returns>X509Certificate2.</returns>
private X509Certificate2 MakeCertificateInternal(string subject, bool isRoot,
bool switchToMtaIfNeeded, X509Certificate2 signingCert = null,
CancellationToken cancellationToken = default(CancellationToken))
{
X509Certificate2 certificate = null;
if (switchToMtaIfNeeded && Thread.CurrentThread.GetApartmentState() != ApartmentState.MTA)
{
using (var manualResetEvent = new ManualResetEventSlim(false))
{
ThreadPool.QueueUserWorkItem(o =>
{
certificate = MakeCertificateInternal(subject, isRoot, false, signingCert);
if (!cancellationToken.IsCancellationRequested)
{
manualResetEvent?.Set();
}
});
manualResetEvent.Wait(TimeSpan.FromMinutes(1), cancellationToken);
}
return certificate;
}
return MakeCertificateInternal(isRoot, subject, $"CN={subject}", DateTime.UtcNow.AddDays(-certificateGraceDays), DateTime.UtcNow.AddDays(certificateValidDays), isRoot ? null : signingCert);
}
}
}
using System.Security.Cryptography.X509Certificates;
namespace Titanium.Web.Proxy.Network.Certificate
{
/// <summary>
/// Abstract interface for different Certificate Maker Engines
/// </summary>
internal interface ICertificateMaker
{
X509Certificate2 MakeCertificate(string sSubjectCn, bool isRoot, X509Certificate2 signingCert);
}
}
This diff is collapsed.
This diff is collapsed.
......@@ -7,7 +7,7 @@ namespace Titanium.Web.Proxy.Network
/// <summary>
/// This class wraps Tcp connection to client
/// </summary>
public class ProxyClient
internal class ProxyClient
{
/// <summary>
/// TcpClient used to communicate with client
......@@ -28,6 +28,5 @@ namespace Titanium.Web.Proxy.Network
/// used to write line by line to client
/// </summary>
internal StreamWriter ClientStreamWriter { get; set; }
}
}
......@@ -7,15 +7,16 @@ using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Network.Tcp
{
/// <summary>
/// An object that holds TcpConnection to a particular server & port
/// An object that holds TcpConnection to a particular server and port
/// </summary>
public class TcpConnection : IDisposable
internal class TcpConnection : IDisposable
{
internal ExternalProxy UpStreamHttpProxy { get; set; }
internal ExternalProxy UpStreamHttpsProxy { get; set; }
internal string HostName { get; set; }
internal int Port { get; set; }
internal bool IsHttps { get; set; }
......@@ -47,18 +48,17 @@ namespace Titanium.Web.Proxy.Network.Tcp
LastAccess = DateTime.Now;
}
/// <summary>
/// Dispose.
/// </summary>
public void Dispose()
{
Stream.Close();
Stream.Dispose();
Stream?.Close();
Stream?.Dispose();
TcpClient.LingerState = new LingerOption(true, 0);
TcpClient.Client.Shutdown(SocketShutdown.Both);
TcpClient.Client.Close();
TcpClient.Client.Dispose();
StreamReader?.Dispose();
TcpClient.Close();
TcpClient?.Close();
}
}
}
This diff is collapsed.
......@@ -6,7 +6,9 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// <summary>
/// Represents collection of TcpRows
/// </summary>
/// <seealso cref="System.Collections.Generic.IEnumerable{Proxy.Tcp.TcpRow}" />
/// <seealso>
/// <cref>System.Collections.Generic.IEnumerable{Proxy.Tcp.TcpRow}</cref>
/// </seealso>
internal class TcpTable : IEnumerable<TcpRow>
{
private readonly IEnumerable<TcpRow> tcpRows;
......@@ -15,7 +17,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// Initializes a new instance of the <see cref="TcpTable"/> class.
/// </summary>
/// <param name="tcpRows">TcpRow collection to initialize with.</param>
public TcpTable(IEnumerable<TcpRow> tcpRows)
internal TcpTable(IEnumerable<TcpRow> tcpRows)
{
this.tcpRows = tcpRows;
}
......@@ -23,7 +25,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// <summary>
/// Gets the TCP rows.
/// </summary>
public IEnumerable<TcpRow> TcpRows => tcpRows;
internal IEnumerable<TcpRow> TcpRows => tcpRows;
/// <summary>
/// Returns an enumerator that iterates through the collection.
......@@ -43,4 +45,4 @@ namespace Titanium.Web.Proxy.Network.Tcp
return GetEnumerator();
}
}
}
\ No newline at end of file
}
......@@ -14,7 +14,12 @@ using System.Runtime.InteropServices;
[assembly: AssemblyCopyright("Copyright © 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: InternalsVisibleTo("Titanium.Web.Proxy.UnitTests")]
[assembly: InternalsVisibleTo("Titanium.Web.Proxy.UnitTests, PublicKey=" +
"0024000004800000940000000602000000240000525341310004000001000100e7368e0ccc717e" +
"eb4d57d35ad6a8305cbbed14faa222e13869405e92c83856266d400887d857005f1393ffca2b92" +
"de7f3ba0bdad35ec2d6057ee1846091b34be2abc3f97dc7e72c16fd4958c15126b12923df76964" +
"7d84922c3f4f3b80ee0ae8e4cb40bc1973b782afb90bb00519fd16adf960f217e23696e7c31654" +
"01d0acd6")]
// 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
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -14,7 +14,7 @@
<copyright>Copyright &#x00A9; Titanium. All rights reserved.</copyright>
<tags></tags>
<dependencies>
<dependency id="DotNetZip" version="1.9.8" />
<dependency id="BouncyCastle" version="1.8.1" />
</dependencies>
</metadata>
<files>
......
This diff is collapsed.
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="DotNetZip" version="1.9.8" targetFramework="net45" />
<package id="BouncyCastle" version="1.8.1" targetFramework="net45" />
</packages>
\ No newline at end of file
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