Unverified Commit 49dd522b authored by justcoding121's avatar justcoding121 Committed by GitHub

Merge pull request #420 from justcoding121/beta

Stable
parents 83377610 347fa30b
param (
[string]$Action="default",
[hashtable]$properties=@{},
[switch]$Help
)
$Here = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)"
Import-Module "$Here\Common"
Install-Chocolatey
Install-Psake
$psakeDirectory = (Resolve-Path $env:ChocolateyInstall\lib\Psake*)
Import-Module (Join-Path $psakeDirectory "tools\Psake\Psake.psm1")
if($Help)
{
try
{
Write-Host "Available build tasks:"
psake -nologo -docs | Out-Host -paging
}
catch {}
return
}
Invoke-Psake -buildFile "$Here\Default.ps1" -parameters $properties -tasklist $Action
\ No newline at end of file
......@@ -5,59 +5,119 @@ $Here = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)"
$SolutionRoot = (Split-Path -parent $Here)
$ProjectName = "Titanium.Web.Proxy"
$GitHubProjectName = "Titanium-Web-Proxy"
$GitHubUserName = "justcoding121"
$SolutionFile = "$SolutionRoot\$ProjectName.sln"
## This comes from the build server iteration
if(!$BuildNumber) { $BuildNumber = $env:APPVEYOR_BUILD_NUMBER }
if(!$BuildNumber) { $BuildNumber = "1"}
if(!$BuildNumber) { $BuildNumber = "0"}
## The build configuration, i.e. Debug/Release
if(!$Configuration) { $Configuration = $env:Configuration }
if(!$Configuration) { $Configuration = "Release" }
if(!$Version) { $Version = $env:APPVEYOR_BUILD_VERSION }
if(!$Version) { $Version = "1.0.$BuildNumber" }
if(!$Version) { $Version = "0.0.$BuildNumber" }
if(!$Branch) { $Branch = $env:APPVEYOR_REPO_BRANCH }
if(!$Branch) { $Branch = "local" }
if($Branch -eq "beta" ) { $Version = "$Version-beta" }
Import-Module "$Here\Common" -DisableNameChecking
$NuGet = Join-Path $SolutionRoot ".nuget\nuget.exe"
$MSBuild = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\msbuild.exe"
$MSBuild -replace ' ', '` '
FormatTaskName (("-"*25) + "[{0}]" + ("-"*25))
Task default -depends Clean, Build, Package
Task Build -depends Restore-Packages{
exec { . $MSBuild $SolutionFile /t:Build /v:normal /p:Configuration=$Configuration /t:restore }
}
Task Package -depends Build {
exec { . $NuGet pack "$SolutionRoot\Titanium.Web.Proxy\Titanium.Web.Proxy.nuspec" -Properties Configuration=$Configuration -OutputDirectory "$SolutionRoot" -Version "$Version" }
}
#default task
Task default -depends Clean, Build, Document, Package
Task Clean -depends Install-BuildTools {
#cleans obj, b
Task Clean {
Get-ChildItem .\ -include bin,obj -Recurse | foreach ($_) { Remove-Item $_.fullname -Force -Recurse }
exec { . $MSBuild $SolutionFile /t:Clean /v:quiet }
}
Task Restore-Packages {
exec { . dotnet restore "$SolutionRoot\Titanium.Web.Proxy.sln" }
}
Task Install-MSBuild {
#install build tools
Task Install-BuildTools -depends Clean {
if(!(Test-Path $MSBuild))
{
cinst microsoft-build-tools -y
}
}
Task Install-BuildTools -depends Install-MSBuild
\ No newline at end of file
#restore nuget packages
Task Restore-Packages -depends Install-BuildTools {
exec { . dotnet restore "$SolutionRoot\$ProjectName.sln" }
}
#build
Task Build -depends Restore-Packages{
exec { . $MSBuild $SolutionFile /t:Build /v:normal /p:Configuration=$Configuration /t:restore }
}
#publish API documentation changes for GitHub pages under master\docs directory
Task Document -depends Build {
if($Branch -eq "master")
{
#use docfx to generate API documentation from source metadata
docfx docfx.json
#patch index.json so that it is always sorted
#otherwise git will think file was changed
$IndexJsonFile = "$SolutionRoot\docs\index.json"
$unsorted = Get-Content $IndexJsonFile | Out-String
[Reflection.Assembly]::LoadFile("$Here\lib\Newtonsoft.Json.dll")
[System.Reflection.Assembly]::LoadWithPartialName("System")
$hashTable = [Newtonsoft.Json.JsonConvert]::DeserializeObject($unsorted, [System.Collections.Generic.SortedDictionary[[string],[object]]])
$obj = [Newtonsoft.Json.JsonConvert]::SerializeObject($hashTable, [Newtonsoft.Json.Formatting]::Indented)
Set-Content -Path $IndexJsonFile -Value $obj
#setup clone directory
$TEMP_REPO_DIR =(Split-Path -parent $SolutionRoot) + "\temp-repo-clone"
If(test-path $TEMP_REPO_DIR)
{
Remove-Item $TEMP_REPO_DIR -Force -Recurse
}
New-Item -ItemType Directory -Force -Path $TEMP_REPO_DIR
#clone
git clone https://github.com/$GitHubUserName/$GitHubProjectName.git --branch master $TEMP_REPO_DIR
If(test-path "$TEMP_REPO_DIR\docs")
{
Remove-Item "$TEMP_REPO_DIR\docs" -Force -Recurse
}
New-Item -ItemType Directory -Force -Path "$TEMP_REPO_DIR\docs"
#cd to docs folder
cd "$TEMP_REPO_DIR\docs"
#copy docs to clone directory\docs
Copy-Item -Path "$SolutionRoot\docs\*" -Destination "$TEMP_REPO_DIR\docs" -Recurse -Force
#push changes to master
git config --global credential.helper store
Add-Content "$HOME\.git-credentials" "https://$($env:github_access_token):x-oauth-basic@github.com`n"
git config --global user.email $env:github_email
git config --global user.name "buildbot121"
git add . -A
git commit -m "API documentation update by build server"
git push origin master
#move cd back to current location
cd $Here
}
}
#package nuget files
Task Package -depends Document {
exec { . $NuGet pack "$SolutionRoot\$ProjectName\$ProjectName.nuspec" -Properties Configuration=$Configuration -OutputDirectory "$SolutionRoot" -Version "$Version" }
}
{
"metadata": [
{
"src": [
{
"files": [ "Titanium.Web.Proxy.Docs.sln"],
"src": "../"
}
],
"dest": "obj/api"
}
],
"build": {
"content": [
{
"files": [ "**/*.yml" ],
"src": "obj/api",
"dest": "api"
},
{
"files": [ "*.md" ]
}
],
"resource": [
{
"files": [ ""]
}
],
"overwrite": "specs/*.md",
"globalMetadata": {
"_appTitle": "Titanium Web Proxy",
"_enableSearch": true
},
"dest": "../docs",
"xrefService": [ "https://xref.docs.microsoft.com/query?uid={uid}" ]
}
}
###
### Common Profile functions for all users
###
$ErrorActionPreference = 'Stop'
Set-StrictMode -Version Latest
$ScriptPath = Split-Path -Parent $MyInvocation.MyCommand.Definition
$SolutionRoot = Split-Path -Parent $ScriptPath
$ToolsPath = Join-Path -Path $SolutionRoot -ChildPath "lib"
Export-ModuleMember -Variable @('ScriptPath', 'SolutionRoot', 'ToolsPath')
param (
[string]$Action="default",
[hashtable]$properties=@{},
[switch]$Help
)
function Install-Chocolatey()
{
......@@ -20,6 +11,7 @@ function Install-Chocolatey()
Write-Output "Chocolatey Not Found, Installing..."
iex ((new-object net.webclient).DownloadString('http://chocolatey.org/install.ps1'))
}
$env:Path += ";${env:ChocolateyInstall}"
}
function Install-Psake()
......@@ -30,4 +22,60 @@ function Install-Psake()
}
}
Export-ModuleMember -Function *-*
\ No newline at end of file
function Install-Git()
{
if(!((Test-Path ${env:ProgramFiles(x86)}\Git*) -Or (Test-Path ${env:ProgramFiles}\Git*)))
{
choco install git.install
}
$env:Path += ";${env:ProgramFiles(x86)}\Git"
$env:Path += ";${env:ProgramFiles}\Git"
}
function Install-DocFx()
{
if(!(Test-Path $env:ChocolateyInstall\lib\docfx\tools*))
{
choco install docfx
}
$env:Path += ";$env:ChocolateyInstall\lib\docfx\tools"
}
#current directory
$Here = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)"
$ErrorActionPreference = 'Stop'
Set-StrictMode -Version Latest
$ScriptPath = Split-Path -Parent $MyInvocation.MyCommand.Definition
$SolutionRoot = Split-Path -Parent $ScriptPath
$ToolsPath = Join-Path -Path $SolutionRoot -ChildPath "lib"
if(-not $env:ChocolateyInstall)
{
$env:ChocolateyInstall = "${env:ALLUSERSPROFILE}\chocolatey";
}
Install-Chocolatey
Install-Psake
Install-Git
Install-DocFx
$psakeDirectory = (Resolve-Path $env:ChocolateyInstall\lib\Psake*)
#appveyor for some reason have different location for psake (it has older psake version?)
if(Test-Path $psakeDirectory\tools\Psake\Psake.psm*)
{
Import-Module (Join-Path $psakeDirectory "tools\Psake\Psake.psm1")
}
else
{
Import-Module (Join-Path $psakeDirectory "tools\Psake.psm1")
}
#invoke the task
Invoke-Psake -buildFile "$Here\build.ps1" -parameters $properties -tasklist $Action
......@@ -203,3 +203,6 @@ FakesAssemblies/
# Visual Studio 6 workspace options file
*.opt
# Docfx
docs/manifest.json
\ No newline at end of file
<?xml version="1.0" encoding="utf-8" ?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
</configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
</configuration>
\ No newline at end of file
......@@ -4,32 +4,31 @@ using System.Runtime.InteropServices;
namespace Titanium.Web.Proxy.Examples.Basic.Helpers
{
/// <summary>
/// Adapated from
/// http://stackoverflow.com/questions/13656846/how-to-programmatic-disable-c-sharp-console-applications-quick-edit-mode
/// Adapated from
/// http://stackoverflow.com/questions/13656846/how-to-programmatic-disable-c-sharp-console-applications-quick-edit-mode
/// </summary>
internal static class ConsoleHelper
{
const uint ENABLE_QUICK_EDIT = 0x0040;
private const uint ENABLE_QUICK_EDIT = 0x0040;
// STD_INPUT_HANDLE (DWORD): -10 is the standard input device.
const int STD_INPUT_HANDLE = -10;
private const int STD_INPUT_HANDLE = -10;
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetStdHandle(int nStdHandle);
private static extern IntPtr GetStdHandle(int nStdHandle);
[DllImport("kernel32.dll")]
static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode);
private static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode);
[DllImport("kernel32.dll")]
static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode);
private static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode);
internal static bool DisableQuickEditMode()
{
var consoleHandle = GetStdHandle(STD_INPUT_HANDLE);
// get current console mode
uint consoleMode;
if (!GetConsoleMode(consoleHandle, out consoleMode))
if (!GetConsoleMode(consoleHandle, out uint consoleMode))
{
// ERROR: Unable to get console mode.
return false;
......
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
......
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/>
</startup>
</configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
</configuration>
\ No newline at end of file
......@@ -4,6 +4,6 @@
xmlns:local="clr-namespace:Titanium.Web.Proxy.Examples.Wpf"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
</Application>
\ No newline at end of file
......@@ -3,7 +3,7 @@
namespace Titanium.Web.Proxy.Examples.Wpf
{
/// <summary>
/// Interaction logic for App.xaml
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
......
......@@ -6,50 +6,51 @@
xmlns:local="clr-namespace:Titanium.Web.Proxy.Examples.Wpf"
mc:Ignorable="d"
Title="MainWindow" Height="500" Width="1000" WindowState="Maximized"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="500" />
<ColumnDefinition Width="3" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<GridSplitter Grid.Column="1" Grid.Row="0" HorizontalAlignment="Stretch" />
<ListView Grid.Column="0" Grid.Row="0" HorizontalAlignment="Stretch" ItemsSource="{Binding Sessions}" SelectedItem="{Binding SelectedSession}"
KeyDown="ListViewSessions_OnKeyDown">
<ListView.View>
<GridView>
<GridViewColumn Header="Result" DisplayMemberBinding="{Binding StatusCode}" />
<GridViewColumn Header="Protocol" DisplayMemberBinding="{Binding Protocol}" />
<GridViewColumn Header="Host" DisplayMemberBinding="{Binding Host}" />
<GridViewColumn Header="Url" DisplayMemberBinding="{Binding Url}" />
<GridViewColumn Header="BodySize" DisplayMemberBinding="{Binding BodySize}" />
<GridViewColumn Header="Process" DisplayMemberBinding="{Binding Process}" />
<GridViewColumn Header="SentBytes" DisplayMemberBinding="{Binding SentDataCount}" />
<GridViewColumn Header="ReceivedBytes" DisplayMemberBinding="{Binding ReceivedDataCount}" />
</GridView>
</ListView.View>
</ListView>
<TabControl Grid.Column="2" Grid.Row="0">
<TabItem Header="Session">
<Grid Background="Red" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBox x:Name="TextBoxRequest" Grid.Row="0" />
<TextBox x:Name="TextBoxResponse" Grid.Row="1" />
</Grid>
</TabItem>
</TabControl>
<StackPanel Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="3" Orientation="Horizontal">
<TextBlock Text="ClientConnectionCount:" />
<TextBlock Text="{Binding ClientConnectionCount}" Margin="10,0,20,0" />
<TextBlock Text="ServerConnectionCount:" />
<TextBlock Text="{Binding ServerConnectionCount}" Margin="10,0,20,0" />
</StackPanel>
<ColumnDefinition Width="500" />
<ColumnDefinition Width="3" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<GridSplitter Grid.Column="1" Grid.Row="0" HorizontalAlignment="Stretch" />
<ListView Grid.Column="0" Grid.Row="0" HorizontalAlignment="Stretch" ItemsSource="{Binding Sessions}"
SelectedItem="{Binding SelectedSession}"
KeyDown="ListViewSessions_OnKeyDown">
<ListView.View>
<GridView>
<GridViewColumn Header="Result" DisplayMemberBinding="{Binding StatusCode}" />
<GridViewColumn Header="Protocol" DisplayMemberBinding="{Binding Protocol}" />
<GridViewColumn Header="Host" DisplayMemberBinding="{Binding Host}" />
<GridViewColumn Header="Url" DisplayMemberBinding="{Binding Url}" />
<GridViewColumn Header="BodySize" DisplayMemberBinding="{Binding BodySize}" />
<GridViewColumn Header="Process" DisplayMemberBinding="{Binding Process}" />
<GridViewColumn Header="SentBytes" DisplayMemberBinding="{Binding SentDataCount}" />
<GridViewColumn Header="ReceivedBytes" DisplayMemberBinding="{Binding ReceivedDataCount}" />
</GridView>
</ListView.View>
</ListView>
<TabControl Grid.Column="2" Grid.Row="0">
<TabItem Header="Session">
<Grid Background="Red" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBox x:Name="TextBoxRequest" Grid.Row="0" />
<TextBox x:Name="TextBoxResponse" Grid.Row="1" />
</Grid>
</TabItem>
</TabControl>
<StackPanel Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="3" Orientation="Horizontal">
<TextBlock Text="ClientConnectionCount:" />
<TextBlock Text="{Binding ClientConnectionCount}" Margin="10,0,20,0" />
<TextBlock Text="ServerConnectionCount:" />
<TextBlock Text="{Binding ServerConnectionCount}" Margin="10,0,20,0" />
</StackPanel>
</Grid>
</Window>
</Window>
\ No newline at end of file
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
......@@ -33,11 +31,11 @@ using System.Windows;
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
......
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
......
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Examples.Wpf.Annotations;
using Titanium.Web.Proxy.Http;
......@@ -9,14 +8,15 @@ namespace Titanium.Web.Proxy.Examples.Wpf
{
public class SessionListItem : INotifyPropertyChanged
{
private string statusCode;
private string protocol;
private long? bodySize;
private Exception exception;
private string host;
private string url;
private long bodySize;
private string process;
private string protocol;
private long receivedDataCount;
private long sentDataCount;
private string statusCode;
private string url;
public int Number { get; set; }
......@@ -26,58 +26,67 @@ namespace Titanium.Web.Proxy.Examples.Wpf
public string StatusCode
{
get { return statusCode; }
set { SetField(ref statusCode, value);}
get => statusCode;
set => SetField(ref statusCode, value);
}
public string Protocol
{
get { return protocol; }
set { SetField(ref protocol, value); }
get => protocol;
set => SetField(ref protocol, value);
}
public string Host
{
get { return host; }
set { SetField(ref host, value); }
get => host;
set => SetField(ref host, value);
}
public string Url
{
get { return url; }
set { SetField(ref url, value); }
get => url;
set => SetField(ref url, value);
}
public long BodySize
public long? BodySize
{
get { return bodySize; }
set { SetField(ref bodySize, value); }
get => bodySize;
set => SetField(ref bodySize, value);
}
public string Process
{
get { return process; }
set { SetField(ref process, value); }
get => process;
set => SetField(ref process, value);
}
public long ReceivedDataCount
{
get { return receivedDataCount; }
set { SetField(ref receivedDataCount, value); }
get => receivedDataCount;
set => SetField(ref receivedDataCount, value);
}
public long SentDataCount
{
get { return sentDataCount; }
set { SetField(ref sentDataCount, value); }
get => sentDataCount;
set => SetField(ref sentDataCount, value);
}
public Exception Exception
{
get => exception;
set => SetField(ref exception, value);
}
public event PropertyChangedEventHandler PropertyChanged;
protected void SetField<T>(ref T field, T value,[CallerMemberName] string propertyName = null)
protected void SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
field = value;
OnPropertyChanged(propertyName);
if (!Equals(field, value))
{
field = value;
OnPropertyChanged(propertyName);
}
}
[NotifyPropertyChangedInvocator]
......@@ -105,7 +114,24 @@ namespace Titanium.Web.Proxy.Examples.Wpf
Url = request.RequestUri.AbsolutePath;
}
BodySize = response?.ContentLength ?? -1;
if (!IsTunnelConnect)
{
long responseSize = -1;
if (response != null)
{
if (response.ContentLength != -1)
{
responseSize = response.ContentLength;
}
else if (response.IsBodyRead && response.Body != null)
{
responseSize = response.Body.Length;
}
}
BodySize = responseSize;
}
Process = GetProcessDescription(WebSession.ProcessId.Value);
}
......
......@@ -51,8 +51,8 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="StreamExtended, Version=1.0.81.0, Culture=neutral, PublicKeyToken=bbfa0f1d54f50043, processorArchitecture=MSIL">
<HintPath>..\..\packages\StreamExtended.1.0.81\lib\net45\StreamExtended.dll</HintPath>
<Reference Include="StreamExtended, Version=1.0.147.0, Culture=neutral, PublicKeyToken=bbfa0f1d54f50043, processorArchitecture=MSIL">
<HintPath>..\..\packages\StreamExtended.1.0.147-beta\lib\net45\StreamExtended.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Data" />
......
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="StreamExtended" version="1.0.81" targetFramework="net45" />
<package id="StreamExtended" version="1.0.147-beta" targetFramework="net45" />
</packages>
\ No newline at end of file
......@@ -2,19 +2,20 @@
A light weight HTTP(S) proxy server written in C#
<a href="https://ci.appveyor.com/project/justcoding121/titanium-web-proxy">![Build Status](https://ci.appveyor.com/api/projects/status/rvlxv8xgj0m7lkr4?svg=true)</a>
<a href="https://ci.appveyor.com/project/justcoding121/titanium-web-proxy">![Build Status](https://ci.appveyor.com/api/projects/status/rvlxv8xgj0m7lkr4?svg=true)</a> [![Join the chat at https://gitter.im/Titanium-Web-Proxy/Lobby](https://badges.gitter.im/Titanium-Web-Proxy/Lobby.svg)](https://gitter.im/Titanium-Web-Proxy/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
Kindly report only issues/bugs here . For programming help or questions use [StackOverflow](http://stackoverflow.com/questions/tagged/titanium-web-proxy) with the tag Titanium-Web-Proxy.
([Wiki & Contribution guidelines](https://github.com/justcoding121/Titanium-Web-Proxy/wiki))
* [API Documentation](https://justcoding121.github.io/Titanium-Web-Proxy/docs/api/Titanium.Web.Proxy.ProxyServer.html)
* [Wiki & Contribution guidelines](https://github.com/justcoding121/Titanium-Web-Proxy/wiki)
**Console example application screenshot**
![alt tag](https://raw.githubusercontent.com/justcoding121/Titanium-Web-Proxy/develop/Examples/Titanium.Web.Proxy.Examples.Basic/Capture.PNG)
![alt tag](https://raw.githubusercontent.com/justcoding121/Titanium-Web-Proxy/master/Examples/Titanium.Web.Proxy.Examples.Basic/Capture.PNG)
**GUI example application screenshot**
![alt tag](https://raw.githubusercontent.com/justcoding121/Titanium-Web-Proxy/develop/Examples/Titanium.Web.Proxy.Examples.Wpf/Capture.PNG)
![alt tag](https://raw.githubusercontent.com/justcoding121/Titanium-Web-Proxy/master/Examples/Titanium.Web.Proxy.Examples.Wpf/Capture.PNG)
### Features
......@@ -52,11 +53,11 @@ Setup HTTP proxy:
var proxyServer = new ProxyServer();
//locally trust root certificate used by this proxy
proxyServer.TrustRootCertificate = true;
proxyServer.CertificateManager.TrustRootCertificate = true;
//optionally set the Certificate Engine
//Under Mono only BouncyCastle will be supported
//proxyServer.CertificateEngine = Network.CertificateEngine.BouncyCastle;
//proxyServer.CertificateManager.CertificateEngine = Network.CertificateEngine.BouncyCastle;
proxyServer.BeforeRequest += OnRequest;
proxyServer.BeforeResponse += OnResponse;
......@@ -66,17 +67,15 @@ proxyServer.ClientCertificateSelectionCallback += OnCertificateSelection;
var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000, true)
{
//Exclude HTTPS addresses you don't want to proxy
//Useful for clients that use certificate pinning
//for example dropbox.com
// ExcludedHttpsHostNameRegex = new List<string>() { "google.com", "dropbox.com" }
//Use self-issued generic certificate on all HTTPS requests
//Optimizes performance by not creating a certificate for each HTTPS-enabled domain
//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")
//GenericCertificate = new X509Certificate2(Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "genericcert.pfx"), "password")
};
//Fired when a CONNECT request is received
explicitEndPoint.BeforeTunnelConnect += OnBeforeTunnelConnect;
//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);
......@@ -109,6 +108,7 @@ proxyServer.SetAsSystemHttpsProxy(explicitEndPoint);
Console.Read();
//Unsubscribe & Quit
explicitEndPoint.BeforeTunnelConnect -= OnBeforeTunnelConnect;
proxyServer.BeforeRequest -= OnRequest;
proxyServer.BeforeResponse -= OnResponse;
proxyServer.ServerCertificateValidationCallback -= OnCertificateValidation;
......@@ -125,6 +125,19 @@ Sample request and response event handlers
private IDictionary<Guid, string> requestBodyHistory
= new ConcurrentDictionary<Guid, string>();
private async Task OnBeforeTunnelConnectRequest(object sender, TunnelConnectSessionEventArgs e)
{
string hostname = e.WebSession.Request.RequestUri.Host;
if (hostname.Contains("dropbox.com"))
{
//Exclude Https addresses you don't want to proxy
//Useful for clients that use certificate pinning
//for example dropbox.com
e.DecryptSsl = false;
}
}
public async Task OnRequest(object sender, SessionEventArgs e)
{
Console.WriteLine(e.WebSession.Request.Url);
......@@ -152,7 +165,7 @@ public async Task OnRequest(object sender, SessionEventArgs e)
//Filter URL
if (e.WebSession.Request.RequestUri.AbsoluteUri.Contains("google.com"))
{
await e.Ok("<!DOCTYPE html>" +
e.Ok("<!DOCTYPE html>" +
"<html><body><h1>" +
"Website Blocked" +
"</h1>" +
......@@ -163,7 +176,7 @@ public async Task OnRequest(object sender, SessionEventArgs e)
//Redirect example
if (e.WebSession.Request.RequestUri.AbsoluteUri.Contains("wikipedia.org"))
{
await e.Redirect("https://www.paypal.com");
e.Redirect("https://www.paypal.com");
}
}
......@@ -218,7 +231,6 @@ public Task OnCertificateSelection(object sender, CertificateSelectionEventArgs
#### Roadmap
* Support HTTP 2.0
* Support SOCKS protocol
#### Collaborators
......
......@@ -35,7 +35,7 @@ namespace Titanium.Web.Proxy.IntegrationTests
var handler = new HttpClientHandler
{
Proxy = new WebProxy($"http://localhost:{localProxyPort}", false),
UseProxy = true,
UseProxy = true
};
var client = new HttpClient(handler);
......@@ -51,7 +51,6 @@ namespace Titanium.Web.Proxy.IntegrationTests
public ProxyTestController()
{
proxyServer = new ProxyServer();
proxyServer.TrustRootCertificate = true;
}
public void StartProxy(int proxyPort)
......@@ -69,8 +68,10 @@ namespace Titanium.Web.Proxy.IntegrationTests
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()
......@@ -97,7 +98,7 @@ namespace Titanium.Web.Proxy.IntegrationTests
}
/// <summary>
/// Allows overriding default certificate validation logic
/// Allows overriding default certificate validation logic
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
......@@ -113,7 +114,7 @@ namespace Titanium.Web.Proxy.IntegrationTests
}
/// <summary>
/// Allows overriding default client certificate selection logic during mutual authentication
/// Allows overriding default client certificate selection logic during mutual authentication
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
......
......@@ -15,25 +15,25 @@ namespace Titanium.Web.Proxy.UnitTests
private readonly Random random = new Random();
[TestMethod]
public async Task Simple_Create_Certificate_Stress_Test()
public async Task Simple_BC_Create_Certificate_Test()
{
var tasks = new List<Task>();
var mgr = new CertificateManager(new Lazy<Action<Exception>>(() => (e => { })).Value);
mgr.ClearIdleCertificates(1);
var mgr = new CertificateManager(null, null, false, false, false, new Lazy<ExceptionHandler>(() => (e =>
{
//Console.WriteLine(e.ToString() + e.InnerException != null ? e.InnerException.ToString() : string.Empty);
})).Value);
for (int i = 0; i < 1000; i++)
mgr.CertificateEngine = CertificateEngine.BouncyCastle;
mgr.ClearIdleCertificates();
for (int i = 0; i < 5; i++)
{
foreach (string host in hostNames)
{
tasks.Add(Task.Run(async () =>
tasks.Add(Task.Run(() =>
{
await Task.Delay(random.Next(0, 10) * 1000);
//get the connection
var certificate = mgr.CreateCertificate(host, false);
Assert.IsNotNull(certificate);
}));
}
......@@ -43,5 +43,39 @@ namespace Titanium.Web.Proxy.UnitTests
mgr.StopClearIdleCertificates();
}
//uncomment this to compare WinCert maker performance with BC (BC takes more time for same test above)
[TestMethod]
public async Task Simple_Create_Win_Certificate_Test()
{
var tasks = new List<Task>();
var mgr = new CertificateManager(null, null, false, false, false, new Lazy<ExceptionHandler>(() => (e =>
{
//Console.WriteLine(e.ToString() + e.InnerException != null ? e.InnerException.ToString() : string.Empty);
})).Value);
mgr.CertificateEngine = CertificateEngine.DefaultWindows;
mgr.CreateRootCertificate(true);
mgr.TrustRootCertificate(true);
mgr.ClearIdleCertificates();
for (int i = 0; i < 5; i++)
{
foreach (string host in hostNames)
{
tasks.Add(Task.Run(() =>
{
//get the connection
var certificate = mgr.CreateCertificate(host, false);
Assert.IsNotNull(certificate);
}));
}
}
await Task.WhenAll(tasks.ToArray());
mgr.RemoveTrustedRootCertificate(true);
mgr.StopClearIdleCertificates();
}
}
}
......@@ -9,7 +9,8 @@ namespace Titanium.Web.Proxy.UnitTests
public class ProxyServerTests
{
[TestMethod]
public void GivenOneEndpointIsAlreadyAddedToAddress_WhenAddingNewEndpointToExistingAddress_ThenExceptionIsThrown()
public void
GivenOneEndpointIsAlreadyAddedToAddress_WhenAddingNewEndpointToExistingAddress_ThenExceptionIsThrown()
{
// Arrange
var proxy = new ProxyServer();
......@@ -34,7 +35,8 @@ namespace Titanium.Web.Proxy.UnitTests
}
[TestMethod]
public void GivenOneEndpointIsAlreadyAddedToAddress_WhenAddingNewEndpointToExistingAddress_ThenTwoEndpointsExists()
public void
GivenOneEndpointIsAlreadyAddedToAddress_WhenAddingNewEndpointToExistingAddress_ThenTwoEndpointsExists()
{
// Arrange
var proxy = new ProxyServer();
......@@ -74,7 +76,8 @@ namespace Titanium.Web.Proxy.UnitTests
}
[TestMethod]
public void GivenOneEndpointIsAlreadyAddedToZeroPort_WhenAddingNewEndpointToExistingPort_ThenTwoEndpointsExists()
public void
GivenOneEndpointIsAlreadyAddedToZeroPort_WhenAddingNewEndpointToExistingPort_ThenTwoEndpointsExists()
{
// Arrange
var proxy = new ProxyServer();
......
......@@ -3,6 +3,7 @@ using System.Net;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Helpers.WinHttp;
using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.UnitTests
{
......@@ -85,7 +86,9 @@ namespace Titanium.Web.Proxy.UnitTests
{
hostName = Dns.GetHostName();
}
catch{}
catch
{
}
if (hostName != null)
{
......
......@@ -8,7 +8,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Titanium.Web.Proxy.UnitTests</RootNamespace>
<AssemblyName>Titanium.Web.Proxy.UnitTests</AssemblyName>
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
<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>
......@@ -16,6 +16,7 @@
<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>
......

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27428.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{6FD3B84B-9283-4E9C-8C43-A234E9AA3EAA}"
ProjectSection(SolutionItems) = preProject
.nuget\NuGet.Config = .nuget\NuGet.Config
.nuget\NuGet.exe = .nuget\NuGet.exe
.nuget\NuGet.targets = .nuget\NuGet.targets
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Titanium.Web.Proxy.Docs", "Titanium.Web.Proxy\Titanium.Web.Proxy.Docs.csproj", "{EBF2EA46-EA00-4350-BE1D-D86AFD699DB3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{EBF2EA46-EA00-4350-BE1D-D86AFD699DB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EBF2EA46-EA00-4350-BE1D-D86AFD699DB3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EBF2EA46-EA00-4350-BE1D-D86AFD699DB3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EBF2EA46-EA00-4350-BE1D-D86AFD699DB3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A250E1E5-3ABA-4FED-9A0E-6C63EB0261E0}
EndGlobalSection
EndGlobal
<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/CodeInspection/CodeAnnotations/NamespacesWithAnnotations/=Titanium_002EWeb_002EProxy_002EExamples_002EWpf_002EAnnotations/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_EXISTING_EMBEDDED_ARRANGEMENT/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/LINE_FEED_AT_FILE_END/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSORHOLDER_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_EMBEDDED_STATEMENT_ON_SAME_LINE/@EntryValue">NEVER</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SIMPLE_CASE_STATEMENT_STYLE/@EntryValue">LINE_BREAK</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SIMPLE_EMBEDDED_STATEMENT_STYLE/@EntryValue">LINE_BREAK</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_AFTER_TYPECAST_PARENTHESES/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHIN_SINGLE_LINE_ARRAY_INITIALIZER_BRACES/@EntryValue">True</s:Boolean>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_LIMIT/@EntryValue">160</s:Int64>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_LIMIT/@EntryValue">120</s:Int64>
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForBuiltInTypes/@EntryValue">UseExplicitType</s:String>
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForSimpleTypes/@EntryValue">UseVar</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=BC/@EntryIndexedValue">BC</s:String>
......@@ -21,6 +24,11 @@
<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>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpAttributeForSingleLineMethodUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpRenamePlacementToArrangementMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
\ No newline at end of file
......@@ -9,14 +9,15 @@ namespace Titanium.Web.Proxy
public partial class ProxyServer
{
/// <summary>
/// Call back to override server certificate validation
/// Call back to override server certificate validation
/// </summary>
/// <param name="sender"></param>
/// <param name="certificate"></param>
/// <param name="chain"></param>
/// <param name="sslPolicyErrors"></param>
/// <returns></returns>
internal bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
/// <param name="sender">The sender object.</param>
/// <param name="certificate">The remote certificate.</param>
/// <param name="chain">The certificate chain.</param>
/// <param name="sslPolicyErrors">Ssl policy errors</param>
/// <returns>Return true if valid certificate.</returns>
internal bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
//if user callback is registered then do it
if (ServerCertificateValidationCallback != null)
......@@ -29,7 +30,7 @@ namespace Titanium.Web.Proxy
};
//why is the sender null?
ServerCertificateValidationCallback.InvokeParallel(this, args);
ServerCertificateValidationCallback.InvokeAsync(this, args, exceptionFunc).Wait();
return args.IsValid;
}
......@@ -44,22 +45,23 @@ namespace Titanium.Web.Proxy
}
/// <summary>
/// Call back to select client certificate used for mutual authentication
/// Call back to select client certificate used for mutual authentication
/// </summary>
/// <param name="sender"></param>
/// <param name="targetHost"></param>
/// <param name="localCertificates"></param>
/// <param name="remoteCertificate"></param>
/// <param name="acceptableIssuers"></param>
/// <param name="sender">The sender.</param>
/// <param name="targetHost">The remote hostname.</param>
/// <param name="localCertificates">Selected local certificates by SslStream.</param>
/// <param name="remoteCertificate">The remote certificate of server.</param>
/// <param name="acceptableIssuers">The acceptable issues for client certificate as listed by server.</param>
/// <returns></returns>
internal X509Certificate SelectClientCertificate(object sender, string targetHost, X509CertificateCollection localCertificates,
internal X509Certificate SelectClientCertificate(object sender, string targetHost,
X509CertificateCollection localCertificates,
X509Certificate remoteCertificate, string[] acceptableIssuers)
{
X509Certificate clientCertificate = null;
if (acceptableIssuers != null && acceptableIssuers.Length > 0 && localCertificates != null && localCertificates.Count > 0)
if (acceptableIssuers != null && acceptableIssuers.Length > 0 && localCertificates != null &&
localCertificates.Count > 0)
{
// Use the first certificate that is from an acceptable issuer.
foreach (var certificate in localCertificates)
{
string issuer = certificate.Issuer;
......@@ -88,7 +90,7 @@ namespace Titanium.Web.Proxy
};
//why is the sender null?
ClientCertificateSelectionCallback.InvokeParallel(this, args);
ClientCertificateSelectionCallback.InvokeAsync(this, args, exceptionFunc).Wait();
return args.ClientCertificate;
}
......
namespace Titanium.Web.Proxy.Compression
using System;
using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.Compression
{
/// <summary>
/// A factory to generate the compression methods based on the type of compression
/// A factory to generate the compression methods based on the type of compression
/// </summary>
internal class CompressionFactory
internal static class CompressionFactory
{
public ICompression Create(string type)
//cache
private static readonly ICompression gzip = new GZipCompression();
private static readonly ICompression deflate = new DeflateCompression();
internal static ICompression GetCompression(string type)
{
switch (type)
{
case "gzip":
return new GZipCompression();
case "deflate":
return new DeflateCompression();
case KnownHeaders.ContentEncodingGzip:
return gzip;
case KnownHeaders.ContentEncodingDeflate:
return deflate;
default:
return null;
throw new Exception($"Unsupported compression mode: {type}");
}
}
}
......
using System.IO;
using System.IO.Compression;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Compression
{
/// <summary>
/// Concrete implementation of deflate compression
/// Concrete implementation of deflate compression
/// </summary>
internal class DeflateCompression : ICompression
{
public async Task<byte[]> Compress(byte[] responseBody)
public Stream GetStream(Stream stream)
{
using (var ms = new MemoryStream())
{
using (var zip = new DeflateStream(ms, CompressionMode.Compress, true))
{
await zip.WriteAsync(responseBody, 0, responseBody.Length);
}
return ms.ToArray();
}
return new DeflateStream(stream, CompressionMode.Compress, true);
}
}
}
using System.IO;
using System.IO.Compression;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Compression
{
/// <summary>
/// concreate implementation of gzip compression
/// concreate implementation of gzip compression
/// </summary>
internal class GZipCompression : ICompression
{
public async Task<byte[]> Compress(byte[] responseBody)
public Stream GetStream(Stream stream)
{
using (var ms = new MemoryStream())
{
using (var zip = new GZipStream(ms, CompressionMode.Compress, true))
{
await zip.WriteAsync(responseBody, 0, responseBody.Length);
}
return ms.ToArray();
}
return new GZipStream(stream, CompressionMode.Compress, true);
}
}
}
using System.Threading.Tasks;
using System.IO;
namespace Titanium.Web.Proxy.Compression
{
/// <summary>
/// An inteface for http compression
/// An inteface for http compression
/// </summary>
interface ICompression
internal interface ICompression
{
Task<byte[]> Compress(byte[] responseBody);
Stream GetStream(Stream stream);
}
}
namespace Titanium.Web.Proxy.Decompression
using System;
using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.Decompression
{
/// <summary>
/// A factory to generate the de-compression methods based on the type of compression
/// A factory to generate the de-compression methods based on the type of compression
/// </summary>
internal class DecompressionFactory
{
internal IDecompression Create(string type)
//cache
private static readonly IDecompression gzip = new GZipDecompression();
private static readonly IDecompression deflate = new DeflateDecompression();
internal static IDecompression Create(string type)
{
switch (type)
{
case "gzip":
return new GZipDecompression();
case "deflate":
return new DeflateDecompression();
case KnownHeaders.ContentEncodingGzip:
return gzip;
case KnownHeaders.ContentEncodingDeflate:
return deflate;
default:
return new DefaultDecompression();
throw new Exception($"Unsupported decompression mode: {type}");
}
}
}
......
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Decompression
{
/// <summary>
/// When no compression is specified just return the byte array
/// </summary>
internal class DefaultDecompression : IDecompression
{
public Task<byte[]> Decompress(byte[] compressedArray, int bufferSize)
{
return Task.FromResult(compressedArray);
}
}
}
using StreamExtended.Helpers;
using System.IO;
using System.IO;
using System.IO.Compression;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Decompression
{
/// <summary>
/// concrete implementation of deflate de-compression
/// concrete implementation of deflate de-compression
/// </summary>
internal class DeflateDecompression : IDecompression
{
public async Task<byte[]> Decompress(byte[] compressedArray, int bufferSize)
public Stream GetStream(Stream stream)
{
using (var stream = new MemoryStream(compressedArray))
using (var decompressor = new DeflateStream(stream, CompressionMode.Decompress))
{
var buffer = BufferPool.GetBuffer(bufferSize);
try
{
using (var output = new MemoryStream())
{
int read;
while ((read = await decompressor.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, read);
}
return output.ToArray();
}
}
finally
{
BufferPool.ReturnBuffer(buffer);
}
}
return new DeflateStream(stream, CompressionMode.Decompress, true);
}
}
}
using StreamExtended.Helpers;
using System.IO;
using System.IO;
using System.IO.Compression;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Decompression
{
/// <summary>
/// concrete implementation of gzip de-compression
/// concrete implementation of gzip de-compression
/// </summary>
internal class GZipDecompression : IDecompression
{
public async Task<byte[]> Decompress(byte[] compressedArray, int bufferSize)
public Stream GetStream(Stream stream)
{
using (var decompressor = new GZipStream(new MemoryStream(compressedArray), CompressionMode.Decompress))
{
var buffer = BufferPool.GetBuffer(bufferSize);
try
{
using (var output = new MemoryStream())
{
int read;
while ((read = await decompressor.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, read);
}
return output.ToArray();
}
}
finally
{
BufferPool.ReturnBuffer(buffer);
}
}
return new GZipStream(stream, CompressionMode.Decompress, true);
}
}
}
using System.Threading.Tasks;
using System.IO;
namespace Titanium.Web.Proxy.Decompression
{
/// <summary>
/// An interface for decompression
/// An interface for decompression
/// </summary>
internal interface IDecompression
{
Task<byte[]> Decompress(byte[] compressedArray, int bufferSize);
Stream GetStream(Stream stream);
}
}
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.EventArguments
{
/// <summary>
/// A generic asynchronous event handler used by the proxy.
/// </summary>
/// <typeparam name="TEventArgs">Event argument type.</typeparam>
/// <param name="sender">The proxy server instance.</param>
/// <param name="e">The event arguments.</param>
/// <returns></returns>
public delegate Task AsyncEventHandler<in TEventArgs>(object sender, TEventArgs e);
}
using System;
using System.Threading;
namespace Titanium.Web.Proxy.EventArguments
{
/// <summary>
/// This is used in transparent endpoint before authenticating client.
/// </summary>
public class BeforeSslAuthenticateEventArgs : EventArgs
{
internal readonly CancellationTokenSource TaskCancellationSource;
internal BeforeSslAuthenticateEventArgs(CancellationTokenSource taskCancellationSource)
{
TaskCancellationSource = taskCancellationSource;
}
/// <summary>
/// The server name indication hostname if available. Otherwise the generic certificate hostname of
/// TransparentEndPoint.
/// </summary>
public string SniHostName { get; internal set; }
/// <summary>
/// Should we decrypt the SSL request?
/// If true we decrypt with fake certificate.
/// If false we relay the connection to the hostname mentioned in SniHostname.
/// </summary>
public bool DecryptSsl { get; set; } = true;
/// <summary>
/// Terminate the request abruptly by closing client/server connections.
/// </summary>
public void TerminateSession()
{
TaskCancellationSource.Cancel();
}
}
}
......@@ -4,37 +4,37 @@ using System.Security.Cryptography.X509Certificates;
namespace Titanium.Web.Proxy.EventArguments
{
/// <summary>
/// An argument passed on to user for client certificate selection during mutual SSL authentication
/// An argument passed on to user for client certificate selection during mutual SSL authentication.
/// </summary>
public class CertificateSelectionEventArgs : EventArgs
{
/// <summary>
/// Sender object.
/// The proxy server instance.
/// </summary>
public object Sender { get; internal set; }
/// <summary>
/// Target host.
/// The remote hostname to which we are authenticating against.
/// </summary>
public string TargetHost { get; internal set; }
/// <summary>
/// Local certificates.
/// Local certificates in store with matching issuers requested by TargetHost website.
/// </summary>
public X509CertificateCollection LocalCertificates { get; internal set; }
/// <summary>
/// Remote certificate.
/// Certificate of the remote server.
/// </summary>
public X509Certificate RemoteCertificate { get; internal set; }
/// <summary>
/// Acceptable issuers.
/// Acceptable issuers as listed by remoted server.
/// </summary>
public string[] AcceptableIssuers { get; internal set; }
/// <summary>
/// Client Certificate.
/// Client Certificate we selected. Set this value to override.
/// </summary>
public X509Certificate ClientCertificate { get; set; }
}
......
......@@ -5,27 +5,28 @@ using System.Security.Cryptography.X509Certificates;
namespace Titanium.Web.Proxy.EventArguments
{
/// <summary>
/// An argument passed on to the user for validating the server certificate during SSL authentication
/// An argument passed on to the user for validating the server certificate
/// during SSL authentication.
/// </summary>
public class CertificateValidationEventArgs : EventArgs
{
/// <summary>
/// Certificate
/// Server certificate.
/// </summary>
public X509Certificate Certificate { get; internal set; }
/// <summary>
/// Certificate chain
/// Certificate chain.
/// </summary>
public X509Chain Chain { get; internal set; }
/// <summary>
/// SSL policy errors.
/// SSL policy errors.
/// </summary>
public SslPolicyErrors SslPolicyErrors { get; internal set; }
/// <summary>
/// is a valid certificate?
/// Is the given server certificate valid?
/// </summary>
public bool IsValid { get; set; }
}
......
......@@ -2,19 +2,31 @@ using System;
namespace Titanium.Web.Proxy.EventArguments
{
/// <summary>
/// Wraps the data sent/received by a proxy server instance.
/// </summary>
public class DataEventArgs : EventArgs
{
public byte[] Buffer { get; }
public int Offset { get; }
public int Count { get; }
public DataEventArgs(byte[] buffer, int offset, int count)
internal DataEventArgs(byte[] buffer, int offset, int count)
{
Buffer = buffer;
Offset = offset;
Count = count;
}
/// <summary>
/// The buffer with data.
/// </summary>
public byte[] Buffer { get; }
/// <summary>
/// Offset in buffer from which valid data begins.
/// </summary>
public int Offset { get; }
/// <summary>
/// Length from offset in buffer with valid data.
/// </summary>
public int Count { get; }
}
}
\ No newline at end of file
}
using System;
using System.Globalization;
using System.IO;
using System.Threading.Tasks;
using StreamExtended.Helpers;
using StreamExtended.Network;
namespace Titanium.Web.Proxy.EventArguments
{
internal class LimitedStream : Stream
{
private readonly CustomBinaryReader baseReader;
private readonly CustomBufferedStream baseStream;
private readonly bool isChunked;
private long bytesRemaining;
private bool readChunkTrail;
internal LimitedStream(CustomBufferedStream baseStream, CustomBinaryReader baseReader, bool isChunked,
long contentLength)
{
this.baseStream = baseStream;
this.baseReader = baseReader;
this.isChunked = isChunked;
bytesRemaining = isChunked
? 0
: contentLength == -1
? long.MaxValue
: contentLength;
}
public override bool CanRead => true;
public override bool CanSeek => false;
public override bool CanWrite => false;
public override long Length => throw new NotSupportedException();
public override long Position
{
get => throw new NotSupportedException();
set => throw new NotSupportedException();
}
private void GetNextChunk()
{
if (readChunkTrail)
{
// read the chunk trail of the previous chunk
string s = baseReader.ReadLineAsync().Result;
}
readChunkTrail = true;
string chunkHead = baseReader.ReadLineAsync().Result;
int idx = chunkHead.IndexOf(";", StringComparison.Ordinal);
if (idx >= 0)
{
chunkHead = chunkHead.Substring(0, idx);
}
int chunkSize = int.Parse(chunkHead, NumberStyles.HexNumber);
bytesRemaining = chunkSize;
if (chunkSize == 0)
{
bytesRemaining = -1;
//chunk trail
baseReader.ReadLineAsync().Wait();
}
}
public override void Flush()
{
throw new NotSupportedException();
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override int Read(byte[] buffer, int offset, int count)
{
if (bytesRemaining == -1)
{
return 0;
}
if (bytesRemaining == 0)
{
if (isChunked)
{
GetNextChunk();
}
else
{
bytesRemaining = -1;
}
}
if (bytesRemaining == -1)
{
return 0;
}
int toRead = (int)Math.Min(count, bytesRemaining);
int res = baseStream.Read(buffer, offset, toRead);
bytesRemaining -= res;
if (res == 0)
{
bytesRemaining = -1;
}
return res;
}
public async Task Finish()
{
if (bytesRemaining != -1)
{
var buffer = BufferPool.GetBuffer(baseReader.Buffer.Length);
try
{
int res = await ReadAsync(buffer, 0, buffer.Length);
if (res != 0)
{
throw new Exception("Data received after stream end");
}
}
finally
{
BufferPool.ReturnBuffer(buffer);
}
}
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
}
}
using System;
using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.EventArguments
{
/// <summary>
/// Class that wraps the multipart sent request arguments.
/// </summary>
public class MultipartRequestPartSentEventArgs : EventArgs
{
internal MultipartRequestPartSentEventArgs(string boundary, HeaderCollection headers)
{
Boundary = boundary;
Headers = headers;
}
/// <summary>
/// Boundary.
/// </summary>
public string Boundary { get; }
/// <summary>
/// The header collection.
/// </summary>
public HeaderCollection Headers { get; }
}
}
using System;
using System.Net;
using System.Threading;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network;
namespace Titanium.Web.Proxy.EventArguments
{
/// <summary>
/// Holds info related to a single proxy session (single request/response sequence).
/// A proxy session is bounded to a single connection from client.
/// A proxy session ends when client terminates connection to proxy
/// or when server terminates connection from proxy.
/// </summary>
public abstract class SessionEventArgsBase : EventArgs, IDisposable
{
/// <summary>
/// Size of Buffers used by this object
/// </summary>
protected readonly int BufferSize;
internal readonly CancellationTokenSource CancellationTokenSource;
protected readonly ExceptionHandler ExceptionFunc;
/// <summary>
/// Constructor to initialize the proxy
/// </summary>
internal SessionEventArgsBase(int bufferSize, ProxyEndPoint endPoint,
CancellationTokenSource cancellationTokenSource, ExceptionHandler exceptionFunc)
: this(bufferSize, endPoint, cancellationTokenSource, null, exceptionFunc)
{
}
protected SessionEventArgsBase(int bufferSize, ProxyEndPoint endPoint,
CancellationTokenSource cancellationTokenSource,
Request request, ExceptionHandler exceptionFunc)
{
BufferSize = bufferSize;
ExceptionFunc = exceptionFunc;
CancellationTokenSource = cancellationTokenSource;
ProxyClient = new ProxyClient();
WebSession = new HttpWebClient(bufferSize, request);
LocalEndPoint = endPoint;
WebSession.ProcessId = new Lazy<int>(() =>
{
if (RunTime.IsWindows)
{
var remoteEndPoint = (IPEndPoint)ProxyClient.TcpClient.Client.RemoteEndPoint;
//If client is localhost get the process id
if (NetworkHelper.IsLocalIpAddress(remoteEndPoint.Address))
{
var ipVersion = endPoint.IpV6Enabled ? IpVersion.Ipv6 : IpVersion.Ipv4;
return TcpHelper.GetProcessIdByLocalPort(ipVersion, remoteEndPoint.Port);
}
//can't access process Id of remote request from remote machine
return -1;
}
throw new PlatformNotSupportedException();
});
}
/// <summary>
/// Holds a reference to client
/// </summary>
internal ProxyClient ProxyClient { get; }
/// <summary>
/// Returns a unique Id for this request/response session which is
/// same as the RequestId of WebSession.
/// </summary>
public Guid Id => WebSession.RequestId;
/// <summary>
/// Does this session uses SSL?
/// </summary>
public bool IsHttps => WebSession.Request.IsHttps;
/// <summary>
/// Client End Point.
/// </summary>
public IPEndPoint ClientEndPoint => (IPEndPoint)ProxyClient.TcpClient.Client.RemoteEndPoint;
/// <summary>
/// A web session corresponding to a single request/response sequence
/// within a proxy connection.
/// </summary>
public HttpWebClient WebSession { get; }
/// <summary>
/// Are we using a custom upstream HTTP(S) proxy?
/// </summary>
public ExternalProxy CustomUpStreamProxyUsed { get; internal set; }
/// <summary>
/// Local endpoint via which we make the request.
/// </summary>
public ProxyEndPoint LocalEndPoint { get; }
/// <summary>
/// Is this a transparent endpoint?
/// </summary>
public bool IsTransparent => LocalEndPoint is TransparentProxyEndPoint;
/// <summary>
/// The last exception that happened.
/// </summary>
public Exception Exception { get; internal set; }
/// <summary>
/// Implements cleanup here.
/// </summary>
public virtual void Dispose()
{
CustomUpStreamProxyUsed = null;
DataSent = null;
DataReceived = null;
Exception = null;
WebSession.FinishSession();
}
/// <summary>
/// Fired when data is sent within this session to server/client.
/// </summary>
public event EventHandler<DataEventArgs> DataSent;
/// <summary>
/// Fired when data is received within this session from client/server.
/// </summary>
public event EventHandler<DataEventArgs> DataReceived;
internal void OnDataSent(byte[] buffer, int offset, int count)
{
try
{
DataSent?.Invoke(this, new DataEventArgs(buffer, offset, count));
}
catch (Exception ex)
{
ExceptionFunc(new Exception("Exception thrown in user event", ex));
}
}
internal void OnDataReceived(byte[] buffer, int offset, int count)
{
try
{
DataReceived?.Invoke(this, new DataEventArgs(buffer, offset, count));
}
catch (Exception ex)
{
ExceptionFunc(new Exception("Exception thrown in user event", ex));
}
}
/// <summary>
/// Terminates the session abruptly by terminating client/server connections.
/// </summary>
public void TerminateSession()
{
CancellationTokenSource.Cancel();
}
}
}
namespace Titanium.Web.Proxy.EventArguments
{
internal enum TransformationMode
{
None,
/// <summary>
/// Removes the chunked encoding
/// </summary>
RemoveChunked,
/// <summary>
/// Uncompress the body (this also removes the chunked encoding if exists)
/// </summary>
Uncompress
}
}
using System.Net;
using System;
using System.Threading;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.EventArguments
{
public class TunnelConnectSessionEventArgs : SessionEventArgs
/// <summary>
/// A class that wraps the state when a tunnel connect event happen for Explicit endpoints.
/// </summary>
public class TunnelConnectSessionEventArgs : SessionEventArgsBase
{
public bool IsHttpsConnect { get; set; }
private bool? isHttpsConnect;
internal TunnelConnectSessionEventArgs(int bufferSize, ProxyEndPoint endPoint, ConnectRequest connectRequest)
: base(bufferSize, endPoint, null)
internal TunnelConnectSessionEventArgs(int bufferSize, ProxyEndPoint endPoint, ConnectRequest connectRequest,
CancellationTokenSource cancellationTokenSource, ExceptionHandler exceptionFunc)
: base(bufferSize, endPoint, cancellationTokenSource, connectRequest, exceptionFunc)
{
WebSession.Request = connectRequest;
WebSession.ConnectRequest = connectRequest;
}
/// <summary>
/// Should we decrypt the Ssl or relay it to server?
/// Default is true.
/// </summary>
public bool DecryptSsl { get; set; } = true;
/// <summary>
/// When set to true it denies the connect request with a Forbidden status.
/// </summary>
public bool DenyConnect { get; set; }
/// <summary>
/// Is this a connect request to secure HTTP server? Or is it to someother protocol.
/// </summary>
public bool IsHttpsConnect
{
get => isHttpsConnect ??
throw new Exception("The value of this property is known in the BeforeTunnectConnectResponse event");
internal set => isHttpsConnect = value;
}
}
}
using System;
namespace Titanium.Web.Proxy
{
/// <summary>
/// A delegate to catch exceptions occuring in proxy.
/// </summary>
/// <param name="exception">The exception occurred in proxy.</param>
public delegate void ExceptionHandler(Exception exception);
}
namespace Titanium.Web.Proxy.Exceptions
{
/// <summary>
/// An expception thrown when body is unexpectedly empty
/// An exception thrown when body is unexpectedly empty.
/// </summary>
public class BodyNotFoundException : ProxyException
{
/// <summary>
/// Constructor.
/// Constructor.
/// </summary>
/// <param name="message"></param>
public BodyNotFoundException(string message) : base(message)
internal BodyNotFoundException(string message) : base(message)
{
}
}
......
using System;
using System.Collections.Generic;
using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Exceptions
{
/// <summary>
/// Proxy authorization exception
/// Proxy authorization exception.
/// </summary>
public class ProxyAuthorizationException : ProxyException
{
/// <summary>
/// Instantiate new instance
/// Instantiate new instance.
/// </summary>
/// <param name="message">Exception message</param>
/// <param name="message">Exception message.</param>
/// <param name="session">The <see cref="SessionEventArgs" /> instance containing the event data.</param>
/// <param name="innerException">Inner exception associated to upstream proxy authorization</param>
/// <param name="headers">Http's headers associated</param>
public ProxyAuthorizationException(string message, Exception innerException, IEnumerable<HttpHeader> headers) : base(message, innerException)
internal ProxyAuthorizationException(string message, SessionEventArgsBase session, Exception innerException,
IEnumerable<HttpHeader> headers) : base(message, innerException)
{
Session = session;
Headers = headers;
}
/// <summary>
/// Headers associated with the authorization exception
/// The current session within which this error happened.
/// </summary>
public SessionEventArgsBase Session { get; }
/// <summary>
/// Headers associated with the authorization exception.
/// </summary>
public IEnumerable<HttpHeader> Headers { get; }
}
......
......@@ -3,12 +3,12 @@
namespace Titanium.Web.Proxy.Exceptions
{
/// <summary>
/// Base class exception associated with this proxy implementation
/// Base class exception associated with this proxy server.
/// </summary>
public abstract class ProxyException : Exception
{
/// <summary>
/// Instantiate a new instance of this exception - must be invoked by derived classes' constructors
/// Instantiate a new instance of this exception - must be invoked by derived classes' constructors
/// </summary>
/// <param name="message">Exception message</param>
protected ProxyException(string message) : base(message)
......@@ -16,7 +16,7 @@ namespace Titanium.Web.Proxy.Exceptions
}
/// <summary>
/// Instantiate this exception - must be invoked by derived classes' constructors
/// Instantiate this exception - must be invoked by derived classes' constructors
/// </summary>
/// <param name="message">Excception message</param>
/// <param name="innerException">Inner exception associated</param>
......
......@@ -4,26 +4,27 @@ using Titanium.Web.Proxy.EventArguments;
namespace Titanium.Web.Proxy.Exceptions
{
/// <summary>
/// Proxy HTTP exception
/// Proxy HTTP exception.
/// </summary>
public class ProxyHttpException : ProxyException
{
/// <summary>
/// Instantiate new instance
/// Instantiate new instance
/// </summary>
/// <param name="message">Message for this exception</param>
/// <param name="innerException">Associated inner exception</param>
/// <param name="sessionEventArgs">Instance of <see cref="EventArguments.SessionEventArgs"/> associated to the exception</param>
public ProxyHttpException(string message, Exception innerException, SessionEventArgs sessionEventArgs) : base(message, innerException)
/// <param name="sessionEventArgs">Instance of <see cref="EventArguments.SessionEventArgs" /> associated to the exception</param>
internal ProxyHttpException(string message, Exception innerException, SessionEventArgs sessionEventArgs) : base(
message, innerException)
{
SessionEventArgs = sessionEventArgs;
}
/// <summary>
/// Gets session info associated to the exception
/// Gets session info associated to the exception.
/// </summary>
/// <remarks>
/// This object should not be edited
/// This object properties should not be edited.
/// </remarks>
public SessionEventArgs SessionEventArgs { get; }
}
......
This diff is collapsed.
using System;
namespace Titanium.Web.Proxy.Extensions
{
/// <summary>
/// Extension methods for Byte Arrays.
/// </summary>
internal static class ByteArrayExtensions
{
/// <summary>
/// Get the sub array from byte of data
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="data"></param>
/// <param name="index"></param>
/// <param name="length"></param>
/// <returns></returns>
internal static T[] SubArray<T>(this T[] data, int index, int length)
{
var result = new T[length];
Array.Copy(data, index, result, 0, length);
return result;
}
}
}
using System;
using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments;
namespace Titanium.Web.Proxy.Extensions
{
internal static class FuncExtensions
{
public static void InvokeParallel<T>(this Func<object, T, Task> callback, object sender, T args)
internal static async Task InvokeAsync<T>(this AsyncEventHandler<T> callback, object sender, T args,
ExceptionHandler exceptionFunc)
{
var invocationList = callback.GetInvocationList();
var handlerTasks = new Task[invocationList.Length];
for (int i = 0; i < invocationList.Length; i++)
foreach (var @delegate in invocationList)
{
handlerTasks[i] = ((Func<object, T, Task>)invocationList[i])(sender, args);
await InternalInvokeAsync((AsyncEventHandler<T>)@delegate, sender, args, exceptionFunc);
}
Task.WhenAll(handlerTasks).Wait();
}
public static async Task InvokeParallelAsync<T>(this Func<object, T, Task> callback, object sender, T args, Action<Exception> exceptionFunc)
{
var invocationList = callback.GetInvocationList();
var handlerTasks = new Task[invocationList.Length];
for (int i = 0; i < invocationList.Length; i++)
{
handlerTasks[i] = InvokeAsync((Func<object, T, Task>)invocationList[i], sender, args, exceptionFunc);
}
await Task.WhenAll(handlerTasks);
}
private static async Task InvokeAsync<T>(Func<object, T, Task> callback, object sender, T args, Action<Exception> exceptionFunc)
private static async Task InternalInvokeAsync<T>(AsyncEventHandler<T> callback, object sender, T args,
ExceptionHandler exceptionFunc)
{
try
{
await callback(sender, args);
}
catch (Exception ex)
catch (Exception e)
{
var ex2 = new Exception("Exception thrown in user event", ex);
exceptionFunc(ex2);
exceptionFunc(new Exception("Exception thrown in user event", e));
}
}
}
......
using System.Text;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.Extensions
{
/// <summary>
/// Extensions on HttpWebSession object
/// </summary>
internal static class HttpWebRequestExtensions
{
/// <summary>
/// parse the character encoding of request from request headers
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
internal static Encoding GetEncoding(this Request request)
{
return HttpHelper.GetEncodingFromContentType(request.ContentType);
}
}
}
using System.Text;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.Extensions
{
internal static class HttpWebResponseExtensions
{
/// <summary>
/// Gets the character encoding of response from response headers
/// </summary>
/// <param name="response"></param>
/// <returns></returns>
internal static Encoding GetResponseCharacterEncoding(this Response response)
{
return HttpHelper.GetEncodingFromContentType(response.ContentType);
}
}
}
This diff is collapsed.
using System.Globalization;
using System;
using System.Globalization;
namespace Titanium.Web.Proxy.Extensions
{
internal static class StringExtensions
{
internal static bool EqualsIgnoreCase(this string str, string value)
{
return str.Equals(value, StringComparison.CurrentCultureIgnoreCase);
}
internal static bool ContainsIgnoreCase(this string str, string value)
{
return CultureInfo.CurrentCulture.CompareInfo.IndexOf(str, value, CompareOptions.IgnoreCase) >= 0;
}
internal static int IndexOfIgnoreCase(this string str, string value)
{
return CultureInfo.CurrentCulture.CompareInfo.IndexOf(str, value, CompareOptions.IgnoreCase);
}
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -9,11 +9,11 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
{
}
public override bool IsInvalid => handle == IntPtr.Zero;
protected override bool ReleaseHandle()
{
return NativeMethods.WinHttp.WinHttpCloseHandle(handle);
}
public override bool IsInvalid => handle == IntPtr.Zero;
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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