Unverified Commit 5759c936 authored by Anton Ryzhov's avatar Anton Ryzhov Committed by GitHub

Merge pull request #1 from justcoding121/master

Upstream merge
parents 042a3a06 c037b265
...@@ -2,7 +2,8 @@ $PSake.use_exit_on_error = $true ...@@ -2,7 +2,8 @@ $PSake.use_exit_on_error = $true
$Here = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)" $Here = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)"
$SolutionRoot = (Split-Path -parent $Here) $RepoRoot = $(Split-Path -parent $Here)
$SolutionRoot = "$RepoRoot\src"
$ProjectName = "Titanium.Web.Proxy" $ProjectName = "Titanium.Web.Proxy"
$GitHubProjectName = "Titanium-Web-Proxy" $GitHubProjectName = "Titanium-Web-Proxy"
...@@ -26,7 +27,7 @@ if(!$Branch) { $Branch = "local" } ...@@ -26,7 +27,7 @@ if(!$Branch) { $Branch = "local" }
if($Branch -eq "beta" ) { $Version = "$Version-beta" } if($Branch -eq "beta" ) { $Version = "$Version-beta" }
$NuGet = Join-Path $SolutionRoot ".nuget\nuget.exe" $NuGet = Join-Path $RepoRoot ".nuget\nuget.exe"
$MSBuild = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\msbuild.exe" $MSBuild = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\msbuild.exe"
$MSBuild -replace ' ', '` ' $MSBuild -replace ' ', '` '
...@@ -57,7 +58,7 @@ Task Restore-Packages -depends Install-BuildTools { ...@@ -57,7 +58,7 @@ Task Restore-Packages -depends Install-BuildTools {
#build #build
Task Build -depends Restore-Packages{ Task Build -depends Restore-Packages{
exec { . $MSBuild $SolutionFile /t:Build /v:normal /p:Configuration=$Configuration /t:restore } exec { . $MSBuild $SolutionFile /t:Build /v:normal /p:Configuration=$Configuration /p:Platform="Any CPU" /t:restore }
} }
#publish API documentation changes for GitHub pages under master\docs directory #publish API documentation changes for GitHub pages under master\docs directory
...@@ -65,12 +66,13 @@ Task Document -depends Build { ...@@ -65,12 +66,13 @@ Task Document -depends Build {
if($Branch -eq "master") if($Branch -eq "master")
{ {
#use docfx to generate API documentation from source metadata #use docfx to generate API documentation from source metadata
docfx docfx.json docfx docfx.json
#patch index.json so that it is always sorted #patch index.json so that it is always sorted
#otherwise git will think file was changed #otherwise git will think file was changed
$IndexJsonFile = "$SolutionRoot\docs\index.json" $IndexJsonFile = "$RepoRoot\docs\index.json"
$unsorted = Get-Content $IndexJsonFile | Out-String $unsorted = Get-Content $IndexJsonFile | Out-String
[Reflection.Assembly]::LoadFile("$Here\lib\Newtonsoft.Json.dll") [Reflection.Assembly]::LoadFile("$Here\lib\Newtonsoft.Json.dll")
[System.Reflection.Assembly]::LoadWithPartialName("System") [System.Reflection.Assembly]::LoadWithPartialName("System")
...@@ -79,7 +81,7 @@ Task Document -depends Build { ...@@ -79,7 +81,7 @@ Task Document -depends Build {
Set-Content -Path $IndexJsonFile -Value $obj Set-Content -Path $IndexJsonFile -Value $obj
#setup clone directory #setup clone directory
$TEMP_REPO_DIR =(Split-Path -parent $SolutionRoot) + "\temp-repo-clone" $TEMP_REPO_DIR =(Split-Path -parent $RepoRoot) + "\temp-repo-clone"
If(test-path $TEMP_REPO_DIR) If(test-path $TEMP_REPO_DIR)
{ {
...@@ -101,7 +103,7 @@ Task Document -depends Build { ...@@ -101,7 +103,7 @@ Task Document -depends Build {
cd "$TEMP_REPO_DIR\docs" cd "$TEMP_REPO_DIR\docs"
#copy docs to clone directory\docs #copy docs to clone directory\docs
Copy-Item -Path "$SolutionRoot\docs\*" -Destination "$TEMP_REPO_DIR\docs" -Recurse -Force Copy-Item -Path "$RepoRoot\docs\*" -Destination "$TEMP_REPO_DIR\docs" -Recurse -Force
#push changes to master #push changes to master
git config --global credential.helper store git config --global credential.helper store
...@@ -119,5 +121,5 @@ Task Document -depends Build { ...@@ -119,5 +121,5 @@ Task Document -depends Build {
#package nuget files #package nuget files
Task Package -depends Document { Task Package -depends Document {
exec { . $NuGet pack "$SolutionRoot\$ProjectName\$ProjectName.nuspec" -Properties Configuration=$Configuration -OutputDirectory "$SolutionRoot" -Version "$Version" } exec { . $NuGet pack "$SolutionRoot\$ProjectName\$ProjectName.nuspec" -Properties Configuration=$Configuration -OutputDirectory "$RepoRoot" -Version "$Version" }
} }
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
"src": [ "src": [
{ {
"files": [ "Titanium.Web.Proxy.Docs.sln"], "files": [ "Titanium.Web.Proxy.Docs.sln"],
"src": "../" "src": "../src/"
} }
], ],
"dest": "obj/api" "dest": "obj/api"
......
...@@ -6,7 +6,7 @@ param ( ...@@ -6,7 +6,7 @@ param (
function Install-Chocolatey() function Install-Chocolatey()
{ {
if(-not $env:ChocolateyInstall -or -not (Test-Path "$env:ChocolateyInstall")) if(-not $env:ChocolateyInstall -or -not (Test-Path "$env:ChocolateyInstall\*"))
{ {
Write-Output "Chocolatey Not Found, Installing..." Write-Output "Chocolatey Not Found, Installing..."
iex ((new-object net.webclient).DownloadString('http://chocolatey.org/install.ps1')) iex ((new-object net.webclient).DownloadString('http://chocolatey.org/install.ps1'))
......
{
"version": "0.2.0",
"configurations": [
{
"name": "NetCore|Debug|Basic Example",
"type": "coreclr",
"request": "launch",
"program": "${workspaceRoot}/examples/Titanium.Web.Proxy.Examples.Basic/bin/Debug/netcoreapp2.0/Titanium.Web.Proxy.Examples.Basic.NetCore.dll",
"args": [],
"cwd": "${workspaceRoot}",
"stopAtEntry": false,
"console": "integratedTerminal",
"preLaunchTask": "build-basic-example-netcore-debug"
},
{
"name": "NetCore|Release|Basic Example",
"type": "coreclr",
"request": "launch",
"program": "${workspaceRoot}/examples/Titanium.Web.Proxy.Examples.Basic/bin/Release/netcoreapp2.0/Titanium.Web.Proxy.Examples.Basic.NetCore.dll",
"args": [],
"cwd": "${workspaceRoot}",
"stopAtEntry": false,
"console": "integratedTerminal",
"preLaunchTask": "build-basic-example-netcore-release"
}
]
}
\ No newline at end of file
{
// The following will hide the js and map files in the editor
"files.exclude": {
"**/.build": true,
"**/.nuget": true,
"**/.vs": true,
"**/docs": true,
"**/packages": true,
"**/bin": true,
"**/obj": true,
"**/*.DotSettings": true,
"**/*.sln": true,
"**/tests/" : true,
"**/Titanium.Web.Proxy.Examples.Wpf/" : true,
"**/*.Basic.csproj/": true,
"**/*.Docs.csproj/": true,
"**/*.Proxy.csproj/": true,
"**/*.Mono.csproj" : true
},
"search.exclude": {
"**/.build": true,
"**/.nuget": true,
"**/.vs": true,
"**/docs": true,
"**/packages": true,
"**/bin": true,
"**/obj": true,
"**/*.DotSettings": true,
"**/*.sln": true,
"**/tests/" : true,
"**/Titanium.Web.Proxy.Examples.Wpf/" : true,
"**/*.Basic.csproj/": true,
"**/*.Docs.csproj/": true,
"**/*.Proxy.csproj/": true,
"**/*.Mono.csproj" : true
}
}
\ No newline at end of file
{
"version": "2.0.0",
"tasks": [
{
"label": "build-basic-example-netcore-debug",
"type": "process",
"command": "dotnet",
"args": ["build","${workspaceFolder}/examples/Titanium.Web.Proxy.Examples.Basic/Titanium.Web.Proxy.Examples.Basic.NetCore.csproj"],
"problemMatcher": "$msCompile",
"group": {
"kind": "build",
"isDefault": true
}
},
{
"label": "build-basic-example-netcore-release",
"type": "process",
"command": "dotnet",
"args": ["build","${workspaceFolder}/examples/Titanium.Web.Proxy.Examples.Basic/Titanium.Web.Proxy.Examples.Basic.NetCore.csproj", "-c", "Release"],
"problemMatcher": "$msCompile"
}
]
}
\ No newline at end of file
...@@ -9,14 +9,6 @@ Kindly report only issues/bugs here . For programming help or questions use [Sta ...@@ -9,14 +9,6 @@ Kindly report only issues/bugs here . For programming help or questions use [Sta
* [API Documentation](https://justcoding121.github.io/Titanium-Web-Proxy/docs/api/Titanium.Web.Proxy.ProxyServer.html) * [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) * [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/master/Examples/Titanium.Web.Proxy.Examples.Basic/Capture.PNG)
**GUI example application screenshot**
![alt tag](https://raw.githubusercontent.com/justcoding121/Titanium-Web-Proxy/master/Examples/Titanium.Web.Proxy.Examples.Wpf/Capture.PNG)
### Features ### Features
* Multithreaded & fully asynchronous proxy employing server connection pooling, certificate cache & buffer pooling * Multithreaded & fully asynchronous proxy employing server connection pooling, certificate cache & buffer pooling
...@@ -24,16 +16,12 @@ Kindly report only issues/bugs here . For programming help or questions use [Sta ...@@ -24,16 +16,12 @@ Kindly report only issues/bugs here . For programming help or questions use [Sta
* Supports mutual SSL authentication, proxy authentication & automatic upstream proxy detection * Supports mutual SSL authentication, proxy authentication & automatic upstream proxy detection
* Kerberos/NTLM authentication over HTTP protocols for windows domain * Kerberos/NTLM authentication over HTTP protocols for windows domain
### Usage ### Installation
Refer the HTTP Proxy Server library in your project, look up Test project to learn usage.
Install by [nuget](https://www.nuget.org/packages/Titanium.Web.Proxy) Install by [nuget](https://www.nuget.org/packages/Titanium.Web.Proxy)
For beta releases on [beta branch](https://github.com/justcoding121/Titanium-Web-Proxy/tree/beta) For beta releases on [beta branch](https://github.com/justcoding121/Titanium-Web-Proxy/tree/beta)
Install-Package Titanium.Web.Proxy Install-Package Titanium.Web.Proxy -Pre
For stable releases on [stable branch](https://github.com/justcoding121/Titanium-Web-Proxy/tree/stable) For stable releases on [stable branch](https://github.com/justcoding121/Titanium-Web-Proxy/tree/stable)
...@@ -44,6 +32,24 @@ Supports ...@@ -44,6 +32,24 @@ Supports
* .Net Standard 2.0 or above * .Net Standard 2.0 or above
* .Net Framework 4.5 or above * .Net Framework 4.5 or above
### Development environment
#### Windows
* Visual Studio Code as IDE for .NET core
* Visual Studio 2017 as IDE for .NET framework/.NET core
#### Mac OS
* Visual Studio Code as IDE for .NET core
* Visual Studio 2017 as IDE for Mono
#### Linux
* Visual Studio Code as IDE for .NET core
* Mono develop as IDE for Mono
### Usage
Refer the HTTP Proxy Server library in your project, look up Test project to learn usage.
Setup HTTP proxy: Setup HTTP proxy:
```csharp ```csharp
...@@ -229,3 +235,12 @@ public Task OnCertificateSelection(object sender, CertificateSelectionEventArgs ...@@ -229,3 +235,12 @@ public Task OnCertificateSelection(object sender, CertificateSelectionEventArgs
#### Collaborators #### Collaborators
* [honfika](https://github.com/honfika) * [honfika](https://github.com/honfika)
**Console example application screenshot**
![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/master/examples/Titanium.Web.Proxy.Examples.Wpf/Capture.PNG)
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Portable.BouncyCastle" version="1.8.2" targetFramework="net45" />
<package id="StreamExtended" version="1.0.179" targetFramework="net45" />
</packages>
\ No newline at end of file
...@@ -41,7 +41,12 @@ skip_tags: true ...@@ -41,7 +41,12 @@ skip_tags: true
skip_commits: skip_commits:
author: buildbot121 author: buildbot121
files:
- docs/*
- .vscode/*
- README.md
- LICENSE
#---------------------------------# #---------------------------------#
# artifacts configuration # # artifacts configuration #
#---------------------------------# #---------------------------------#
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Delegate AsyncEventHandler&lt;TEventArgs&gt; <meta name="title" content="Delegate AsyncEventHandler&lt;TEventArgs&gt;
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Class BeforeSslAuthenticateEventArgs <meta name="title" content="Class BeforeSslAuthenticateEventArgs
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Class CertificateSelectionEventArgs <meta name="title" content="Class CertificateSelectionEventArgs
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Class CertificateValidationEventArgs <meta name="title" content="Class CertificateValidationEventArgs
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Class MultipartRequestPartSentEventArgs <meta name="title" content="Class MultipartRequestPartSentEventArgs
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Class SessionEventArgs <meta name="title" content="Class SessionEventArgs
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Class SessionEventArgsBase <meta name="title" content="Class SessionEventArgsBase
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Class TunnelConnectSessionEventArgs <meta name="title" content="Class TunnelConnectSessionEventArgs
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Namespace Titanium.Web.Proxy.EventArguments <meta name="title" content="Namespace Titanium.Web.Proxy.EventArguments
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Delegate ExceptionHandler <meta name="title" content="Delegate ExceptionHandler
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Class BodyNotFoundException <meta name="title" content="Class BodyNotFoundException
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Class ProxyAuthorizationException <meta name="title" content="Class ProxyAuthorizationException
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Class ProxyException <meta name="title" content="Class ProxyException
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Class ProxyHttpException <meta name="title" content="Class ProxyHttpException
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Namespace Titanium.Web.Proxy.Exceptions <meta name="title" content="Namespace Titanium.Web.Proxy.Exceptions
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
<!DOCTYPE html>
<!--[if IE]><![endif]-->
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Class RunTime
| Titanium Web Proxy </title>
<meta name="viewport" content="width=device-width">
<meta name="title" content="Class RunTime
| Titanium Web Proxy ">
<meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css">
<link rel="stylesheet" href="../styles/docfx.css">
<link rel="stylesheet" href="../styles/main.css">
<meta property="docfx:navrel" content="">
<meta property="docfx:tocrel" content="toc.html">
<meta property="docfx:rel" content="../">
</head>
<body data-spy="scroll" data-target="#affix" data-offset="120">
<div id="wrapper">
<header>
<nav id="autocollapse" class="navbar navbar-inverse ng-scope" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="../index.html">
<img id="logo" class="svg" src="../logo.svg" alt="">
</a>
</div>
<div class="collapse navbar-collapse" id="navbar">
<form class="navbar-form navbar-right" role="search" id="search">
<div class="form-group">
<input type="text" class="form-control" id="search-query" placeholder="Search" autocomplete="off">
</div>
</form>
</div>
</div>
</nav>
<div class="subnav navbar navbar-default">
<div class="container hide-when-search" id="breadcrumb">
<ul class="breadcrumb">
<li></li>
</ul>
</div>
</div>
</header>
<div class="container body-content">
<div id="search-results">
<div class="search-list"></div>
<div class="sr-items">
<p><i class="glyphicon glyphicon-refresh index-loading"></i></p>
</div>
<ul id="pagination"></ul>
</div>
</div>
<div role="main" class="container body-content hide-when-search">
<div class="sidenav hide-when-search">
<a class="btn toc-toggle collapse" data-toggle="collapse" href="#sidetoggle" aria-expanded="false" aria-controls="sidetoggle">Show / Hide Table of Contents</a>
<div class="sidetoggle collapse" id="sidetoggle">
<div id="sidetoc"></div>
</div>
</div>
<div class="article row grid-right">
<div class="col-md-10">
<article class="content wrap" id="_content" data-uid="Titanium.Web.Proxy.Helpers.RunTime">
<h1 id="Titanium_Web_Proxy_Helpers_RunTime" data-uid="Titanium.Web.Proxy.Helpers.RunTime" class="text-break">Class RunTime
</h1>
<div class="markdown level0 summary"><p>Run time helpers</p>
</div>
<div class="markdown level0 conceptual"></div>
<div class="inheritance">
<h5>Inheritance</h5>
<div class="level0"><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.object">Object</a></div>
<div class="level1"><span class="xref">RunTime</span></div>
</div>
<div class="inheritedMembers">
<h5>Inherited Members</h5>
<div>
<a class="xref" href="https://docs.microsoft.com/dotnet/api/system.object.tostring#System_Object_ToString">Object.ToString()</a>
</div>
<div>
<a class="xref" href="https://docs.microsoft.com/dotnet/api/system.object.equals#System_Object_Equals_System_Object_">Object.Equals(Object)</a>
</div>
<div>
<a class="xref" href="https://docs.microsoft.com/dotnet/api/system.object.equals#System_Object_Equals_System_Object_System_Object_">Object.Equals(Object, Object)</a>
</div>
<div>
<a class="xref" href="https://docs.microsoft.com/dotnet/api/system.object.referenceequals#System_Object_ReferenceEquals_System_Object_System_Object_">Object.ReferenceEquals(Object, Object)</a>
</div>
<div>
<a class="xref" href="https://docs.microsoft.com/dotnet/api/system.object.gethashcode#System_Object_GetHashCode">Object.GetHashCode()</a>
</div>
<div>
<a class="xref" href="https://docs.microsoft.com/dotnet/api/system.object.gettype#System_Object_GetType">Object.GetType()</a>
</div>
<div>
<a class="xref" href="https://docs.microsoft.com/dotnet/api/system.object.memberwiseclone#System_Object_MemberwiseClone">Object.MemberwiseClone()</a>
</div>
</div>
<h6><strong>Namespace</strong>: <a class="xref" href="Titanium.Web.Proxy.Helpers.html">Titanium.Web.Proxy.Helpers</a></h6>
<h6><strong>Assembly</strong>: Titanium.Web.Proxy.dll</h6>
<h5 id="Titanium_Web_Proxy_Helpers_RunTime_syntax">Syntax</h5>
<div class="codewrapper">
<pre><code class="lang-csharp hljs">public static class RunTime</code></pre>
</div>
<h3 id="properties">Properties
</h3>
<a id="Titanium_Web_Proxy_Helpers_RunTime_IsLinux_" data-uid="Titanium.Web.Proxy.Helpers.RunTime.IsLinux*"></a>
<h4 id="Titanium_Web_Proxy_Helpers_RunTime_IsLinux" data-uid="Titanium.Web.Proxy.Helpers.RunTime.IsLinux">IsLinux</h4>
<div class="markdown level1 summary"></div>
<div class="markdown level1 conceptual"></div>
<h5 class="decalaration">Declaration</h5>
<div class="codewrapper">
<pre><code class="lang-csharp hljs">public static bool IsLinux { get; }</code></pre>
</div>
<h5 class="propertyValue">Property Value</h5>
<table class="table table-bordered table-striped table-condensed">
<thead>
<tr>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.boolean">Boolean</a></td>
<td></td>
</tr>
</tbody>
</table>
<a id="Titanium_Web_Proxy_Helpers_RunTime_IsMac_" data-uid="Titanium.Web.Proxy.Helpers.RunTime.IsMac*"></a>
<h4 id="Titanium_Web_Proxy_Helpers_RunTime_IsMac" data-uid="Titanium.Web.Proxy.Helpers.RunTime.IsMac">IsMac</h4>
<div class="markdown level1 summary"></div>
<div class="markdown level1 conceptual"></div>
<h5 class="decalaration">Declaration</h5>
<div class="codewrapper">
<pre><code class="lang-csharp hljs">public static bool IsMac { get; }</code></pre>
</div>
<h5 class="propertyValue">Property Value</h5>
<table class="table table-bordered table-striped table-condensed">
<thead>
<tr>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.boolean">Boolean</a></td>
<td></td>
</tr>
</tbody>
</table>
<a id="Titanium_Web_Proxy_Helpers_RunTime_IsWindows_" data-uid="Titanium.Web.Proxy.Helpers.RunTime.IsWindows*"></a>
<h4 id="Titanium_Web_Proxy_Helpers_RunTime_IsWindows" data-uid="Titanium.Web.Proxy.Helpers.RunTime.IsWindows">IsWindows</h4>
<div class="markdown level1 summary"></div>
<div class="markdown level1 conceptual"></div>
<h5 class="decalaration">Declaration</h5>
<div class="codewrapper">
<pre><code class="lang-csharp hljs">public static bool IsWindows { get; }</code></pre>
</div>
<h5 class="propertyValue">Property Value</h5>
<table class="table table-bordered table-striped table-condensed">
<thead>
<tr>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.boolean">Boolean</a></td>
<td></td>
</tr>
</tbody>
</table>
</article>
</div>
<div class="hidden-sm col-md-2" role="complementary">
<div class="sideaffix">
<div class="contribution">
<ul class="nav">
</ul>
</div>
<nav class="bs-docs-sidebar hidden-print hidden-xs hidden-sm affix" id="affix">
<!-- <p><a class="back-to-top" href="#top">Back to top</a><p> -->
</nav>
</div>
</div>
</div>
</div>
<footer>
<div class="grad-bottom"></div>
<div class="footer">
<div class="container">
<span class="pull-right">
<a href="#top">Back to top</a>
</span>
<span>Generated by <strong>DocFX</strong></span>
</div>
</div>
</footer>
</div>
<script type="text/javascript" src="../styles/docfx.vendor.js"></script>
<script type="text/javascript" src="../styles/docfx.js"></script>
<script type="text/javascript" src="../styles/main.js"></script>
</body>
</html>
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Namespace Titanium.Web.Proxy.Helpers <meta name="title" content="Namespace Titanium.Web.Proxy.Helpers
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.35.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
...@@ -85,10 +85,11 @@ ...@@ -85,10 +85,11 @@
<div class="markdown level0 summary"></div> <div class="markdown level0 summary"></div>
<div class="markdown level0 conceptual"></div> <div class="markdown level0 conceptual"></div>
<div class="markdown level0 remarks"></div> <div class="markdown level0 remarks"></div>
<h3 id="enums">Enums <h3 id="classes">Classes
</h3> </h3>
<h4><a class="xref" href="Titanium.Web.Proxy.Helpers.ProxyProtocolType.html">ProxyProtocolType</a></h4> <h4><a class="xref" href="Titanium.Web.Proxy.Helpers.RunTime.html">RunTime</a></h4>
<section></section> <section><p>Run time helpers</p>
</section>
</article> </article>
</div> </div>
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Class ConnectRequest <meta name="title" content="Class ConnectRequest
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Class ConnectResponse <meta name="title" content="Class ConnectResponse
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Class HeaderCollection <meta name="title" content="Class HeaderCollection
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Class HttpWebClient <meta name="title" content="Class HttpWebClient
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Class KnownHeaders <meta name="title" content="Class KnownHeaders
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
...@@ -269,6 +269,30 @@ ...@@ -269,6 +269,30 @@
</table> </table>
<h4 id="Titanium_Web_Proxy_Http_KnownHeaders_ContentEncodingBrotli" data-uid="Titanium.Web.Proxy.Http.KnownHeaders.ContentEncodingBrotli">ContentEncodingBrotli</h4>
<div class="markdown level1 summary"></div>
<div class="markdown level1 conceptual"></div>
<h5 class="decalaration">Declaration</h5>
<div class="codewrapper">
<pre><code class="lang-csharp hljs">public const string ContentEncodingBrotli = &quot;br&quot;</code></pre>
</div>
<h5 class="fieldValue">Field Value</h5>
<table class="table table-bordered table-striped table-condensed">
<thead>
<tr>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.string">String</a></td>
<td></td>
</tr>
</tbody>
</table>
<h4 id="Titanium_Web_Proxy_Http_KnownHeaders_ContentEncodingDeflate" data-uid="Titanium.Web.Proxy.Http.KnownHeaders.ContentEncodingDeflate">ContentEncodingDeflate</h4> <h4 id="Titanium_Web_Proxy_Http_KnownHeaders_ContentEncodingDeflate" data-uid="Titanium.Web.Proxy.Http.KnownHeaders.ContentEncodingDeflate">ContentEncodingDeflate</h4>
<div class="markdown level1 summary"></div> <div class="markdown level1 summary"></div>
<div class="markdown level1 conceptual"></div> <div class="markdown level1 conceptual"></div>
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Class Request <meta name="title" content="Class Request
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Class RequestResponseBase <meta name="title" content="Class RequestResponseBase
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Class Response <meta name="title" content="Class Response
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Class GenericResponse <meta name="title" content="Class GenericResponse
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Class OkResponse <meta name="title" content="Class OkResponse
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Class RedirectResponse <meta name="title" content="Class RedirectResponse
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Namespace Titanium.Web.Proxy.Http.Responses <meta name="title" content="Namespace Titanium.Web.Proxy.Http.Responses
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Namespace Titanium.Web.Proxy.Http <meta name="title" content="Namespace Titanium.Web.Proxy.Http
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Class ExplicitProxyEndPoint <meta name="title" content="Class ExplicitProxyEndPoint
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Class ExternalProxy <meta name="title" content="Class ExternalProxy
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Class HttpHeader <meta name="title" content="Class HttpHeader
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Class ProxyEndPoint <meta name="title" content="Class ProxyEndPoint
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Class TransparentProxyEndPoint <meta name="title" content="Class TransparentProxyEndPoint
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Namespace Titanium.Web.Proxy.Models <meta name="title" content="Namespace Titanium.Web.Proxy.Models
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Enum CertificateEngine <meta name="title" content="Enum CertificateEngine
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
...@@ -110,7 +110,8 @@ Default.</p> ...@@ -110,7 +110,8 @@ Default.</p>
</tr> </tr>
<tr> <tr>
<td id="Titanium_Web_Proxy_Network_CertificateEngine_DefaultWindows">DefaultWindows</td> <td id="Titanium_Web_Proxy_Network_CertificateEngine_DefaultWindows">DefaultWindows</td>
<td><p>Uses Windows Certification Generation API. <td><p>Uses Windows Certification Generation API and only valid in Windows OS.
Observed to be faster than BouncyCastle.
Bug #468 Reported.</p> Bug #468 Reported.</p>
</td> </td>
</tr> </tr>
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Class CertificateManager <meta name="title" content="Class CertificateManager
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Namespace Titanium.Web.Proxy.Network <meta name="title" content="Namespace Titanium.Web.Proxy.Network
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Class ProxyServer <meta name="title" content="Class ProxyServer
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
...@@ -423,7 +423,7 @@ Defaults to false.</p> ...@@ -423,7 +423,7 @@ Defaults to false.</p>
<a id="Titanium_Web_Proxy_ProxyServer_EnableConnectionPool_" data-uid="Titanium.Web.Proxy.ProxyServer.EnableConnectionPool*"></a> <a id="Titanium_Web_Proxy_ProxyServer_EnableConnectionPool_" data-uid="Titanium.Web.Proxy.ProxyServer.EnableConnectionPool*"></a>
<h4 id="Titanium_Web_Proxy_ProxyServer_EnableConnectionPool" data-uid="Titanium.Web.Proxy.ProxyServer.EnableConnectionPool">EnableConnectionPool</h4> <h4 id="Titanium_Web_Proxy_ProxyServer_EnableConnectionPool" data-uid="Titanium.Web.Proxy.ProxyServer.EnableConnectionPool">EnableConnectionPool</h4>
<div class="markdown level1 summary"><p>Should we enable experimental server connection pool? <div class="markdown level1 summary"><p>Should we enable experimental server connection pool?
Defaults to disable.</p> Defaults to true.</p>
</div> </div>
<div class="markdown level1 conceptual"></div> <div class="markdown level1 conceptual"></div>
<h5 class="decalaration">Declaration</h5> <h5 class="decalaration">Declaration</h5>
...@@ -447,6 +447,37 @@ Defaults to disable.</p> ...@@ -447,6 +447,37 @@ Defaults to disable.</p>
</table> </table>
<a id="Titanium_Web_Proxy_ProxyServer_EnableTcpServerConnectionPrefetch_" data-uid="Titanium.Web.Proxy.ProxyServer.EnableTcpServerConnectionPrefetch*"></a>
<h4 id="Titanium_Web_Proxy_ProxyServer_EnableTcpServerConnectionPrefetch" data-uid="Titanium.Web.Proxy.ProxyServer.EnableTcpServerConnectionPrefetch">EnableTcpServerConnectionPrefetch</h4>
<div class="markdown level1 summary"><p>Should we enable tcp server connection prefetching?
When enabled, as soon as we receive a client connection we concurrently initiate
corresponding server connection process using CONNECT hostname or SNI hostname on a separate task so that after parsing client request
we will have the server connection immediately ready or in the process of getting ready.
If a server connection is available in cache then this prefetch task will immediatly return with the available connection from cache.
Defaults to true.</p>
</div>
<div class="markdown level1 conceptual"></div>
<h5 class="decalaration">Declaration</h5>
<div class="codewrapper">
<pre><code class="lang-csharp hljs">public bool EnableTcpServerConnectionPrefetch { get; set; }</code></pre>
</div>
<h5 class="propertyValue">Property Value</h5>
<table class="table table-bordered table-striped table-condensed">
<thead>
<tr>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.boolean">Boolean</a></td>
<td></td>
</tr>
</tbody>
</table>
<a id="Titanium_Web_Proxy_ProxyServer_EnableWinAuth_" data-uid="Titanium.Web.Proxy.ProxyServer.EnableWinAuth*"></a> <a id="Titanium_Web_Proxy_ProxyServer_EnableWinAuth_" data-uid="Titanium.Web.Proxy.ProxyServer.EnableWinAuth*"></a>
<h4 id="Titanium_Web_Proxy_ProxyServer_EnableWinAuth" data-uid="Titanium.Web.Proxy.ProxyServer.EnableWinAuth">EnableWinAuth</h4> <h4 id="Titanium_Web_Proxy_ProxyServer_EnableWinAuth" data-uid="Titanium.Web.Proxy.ProxyServer.EnableWinAuth">EnableWinAuth</h4>
<div class="markdown level1 summary"><p>Enable disable Windows Authentication (NTLM/Kerberos). <div class="markdown level1 summary"><p>Enable disable Windows Authentication (NTLM/Kerberos).
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Namespace Titanium.Web.Proxy <meta name="title" content="Namespace Titanium.Web.Proxy
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.37.2.0"> <meta name="generator" content="docfx 2.39.2.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -11,145 +11,163 @@ ...@@ -11,145 +11,163 @@
<div class="toc" id="toc"> <div class="toc" id="toc">
<ul class="nav level1"> <ul class="nav level1">
<li> <li>
<span class="expand-stub"></span> <span class="expand-stub"></span>
<a href="Titanium.Web.Proxy.html" name="" title="Titanium.Web.Proxy">Titanium.Web.Proxy</a> <a href="Titanium.Web.Proxy.html" name="" title="Titanium.Web.Proxy">Titanium.Web.Proxy</a>
<ul class="nav level2"> <ul class="nav level2">
<li> <li>
<a href="Titanium.Web.Proxy.ExceptionHandler.html" name="" title="ExceptionHandler">ExceptionHandler</a> <a href="Titanium.Web.Proxy.ExceptionHandler.html" name="" title="ExceptionHandler">ExceptionHandler</a>
</li> </li>
<li> <li>
<a href="Titanium.Web.Proxy.ProxyServer.html" name="" title="ProxyServer">ProxyServer</a> <a href="Titanium.Web.Proxy.ProxyServer.html" name="" title="ProxyServer">ProxyServer</a>
</li> </li>
</ul> </li> </ul>
<li> </li>
<span class="expand-stub"></span> <li>
<a href="Titanium.Web.Proxy.EventArguments.html" name="" title="Titanium.Web.Proxy.EventArguments">Titanium.Web.Proxy.EventArguments</a> <span class="expand-stub"></span>
<a href="Titanium.Web.Proxy.EventArguments.html" name="" title="Titanium.Web.Proxy.EventArguments">Titanium.Web.Proxy.EventArguments</a>
<ul class="nav level2">
<li> <ul class="nav level2">
<a href="Titanium.Web.Proxy.EventArguments.AsyncEventHandler-1.html" name="" title="AsyncEventHandler&lt;TEventArgs&gt;">AsyncEventHandler&lt;TEventArgs&gt;</a> <li>
</li> <a href="Titanium.Web.Proxy.EventArguments.AsyncEventHandler-1.html" name="" title="AsyncEventHandler&lt;TEventArgs&gt;">AsyncEventHandler&lt;TEventArgs&gt;</a>
<li> </li>
<a href="Titanium.Web.Proxy.EventArguments.BeforeSslAuthenticateEventArgs.html" name="" title="BeforeSslAuthenticateEventArgs">BeforeSslAuthenticateEventArgs</a> <li>
</li> <a href="Titanium.Web.Proxy.EventArguments.BeforeSslAuthenticateEventArgs.html" name="" title="BeforeSslAuthenticateEventArgs">BeforeSslAuthenticateEventArgs</a>
<li> </li>
<a href="Titanium.Web.Proxy.EventArguments.CertificateSelectionEventArgs.html" name="" title="CertificateSelectionEventArgs">CertificateSelectionEventArgs</a> <li>
</li> <a href="Titanium.Web.Proxy.EventArguments.CertificateSelectionEventArgs.html" name="" title="CertificateSelectionEventArgs">CertificateSelectionEventArgs</a>
<li> </li>
<a href="Titanium.Web.Proxy.EventArguments.CertificateValidationEventArgs.html" name="" title="CertificateValidationEventArgs">CertificateValidationEventArgs</a> <li>
</li> <a href="Titanium.Web.Proxy.EventArguments.CertificateValidationEventArgs.html" name="" title="CertificateValidationEventArgs">CertificateValidationEventArgs</a>
<li> </li>
<a href="Titanium.Web.Proxy.EventArguments.MultipartRequestPartSentEventArgs.html" name="" title="MultipartRequestPartSentEventArgs">MultipartRequestPartSentEventArgs</a> <li>
</li> <a href="Titanium.Web.Proxy.EventArguments.MultipartRequestPartSentEventArgs.html" name="" title="MultipartRequestPartSentEventArgs">MultipartRequestPartSentEventArgs</a>
<li> </li>
<a href="Titanium.Web.Proxy.EventArguments.SessionEventArgs.html" name="" title="SessionEventArgs">SessionEventArgs</a> <li>
</li> <a href="Titanium.Web.Proxy.EventArguments.SessionEventArgs.html" name="" title="SessionEventArgs">SessionEventArgs</a>
<li> </li>
<a href="Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html" name="" title="SessionEventArgsBase">SessionEventArgsBase</a> <li>
</li> <a href="Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html" name="" title="SessionEventArgsBase">SessionEventArgsBase</a>
<li> </li>
<a href="Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.html" name="" title="TunnelConnectSessionEventArgs">TunnelConnectSessionEventArgs</a> <li>
</li> <a href="Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.html" name="" title="TunnelConnectSessionEventArgs">TunnelConnectSessionEventArgs</a>
</ul> </li> </li>
<li> </ul>
<span class="expand-stub"></span> </li>
<a href="Titanium.Web.Proxy.Exceptions.html" name="" title="Titanium.Web.Proxy.Exceptions">Titanium.Web.Proxy.Exceptions</a> <li>
<span class="expand-stub"></span>
<ul class="nav level2"> <a href="Titanium.Web.Proxy.Exceptions.html" name="" title="Titanium.Web.Proxy.Exceptions">Titanium.Web.Proxy.Exceptions</a>
<li>
<a href="Titanium.Web.Proxy.Exceptions.BodyNotFoundException.html" name="" title="BodyNotFoundException">BodyNotFoundException</a> <ul class="nav level2">
</li> <li>
<li> <a href="Titanium.Web.Proxy.Exceptions.BodyNotFoundException.html" name="" title="BodyNotFoundException">BodyNotFoundException</a>
<a href="Titanium.Web.Proxy.Exceptions.ProxyAuthorizationException.html" name="" title="ProxyAuthorizationException">ProxyAuthorizationException</a> </li>
</li> <li>
<li> <a href="Titanium.Web.Proxy.Exceptions.ProxyAuthorizationException.html" name="" title="ProxyAuthorizationException">ProxyAuthorizationException</a>
<a href="Titanium.Web.Proxy.Exceptions.ProxyException.html" name="" title="ProxyException">ProxyException</a> </li>
</li> <li>
<li> <a href="Titanium.Web.Proxy.Exceptions.ProxyException.html" name="" title="ProxyException">ProxyException</a>
<a href="Titanium.Web.Proxy.Exceptions.ProxyHttpException.html" name="" title="ProxyHttpException">ProxyHttpException</a> </li>
</li> <li>
</ul> </li> <a href="Titanium.Web.Proxy.Exceptions.ProxyHttpException.html" name="" title="ProxyHttpException">ProxyHttpException</a>
<li> </li>
<span class="expand-stub"></span> </ul>
<a href="Titanium.Web.Proxy.Http.html" name="" title="Titanium.Web.Proxy.Http">Titanium.Web.Proxy.Http</a> </li>
<li>
<ul class="nav level2"> <span class="expand-stub"></span>
<li> <a href="Titanium.Web.Proxy.Helpers.html" name="" title="Titanium.Web.Proxy.Helpers">Titanium.Web.Proxy.Helpers</a>
<a href="Titanium.Web.Proxy.Http.ConnectRequest.html" name="" title="ConnectRequest">ConnectRequest</a>
</li> <ul class="nav level2">
<li> <li>
<a href="Titanium.Web.Proxy.Http.ConnectResponse.html" name="" title="ConnectResponse">ConnectResponse</a> <a href="Titanium.Web.Proxy.Helpers.RunTime.html" name="" title="RunTime">RunTime</a>
</li> </li>
<li> </ul>
<a href="Titanium.Web.Proxy.Http.HeaderCollection.html" name="" title="HeaderCollection">HeaderCollection</a> </li>
</li> <li>
<li> <span class="expand-stub"></span>
<a href="Titanium.Web.Proxy.Http.HttpWebClient.html" name="" title="HttpWebClient">HttpWebClient</a> <a href="Titanium.Web.Proxy.Http.html" name="" title="Titanium.Web.Proxy.Http">Titanium.Web.Proxy.Http</a>
</li>
<li> <ul class="nav level2">
<a href="Titanium.Web.Proxy.Http.KnownHeaders.html" name="" title="KnownHeaders">KnownHeaders</a> <li>
</li> <a href="Titanium.Web.Proxy.Http.ConnectRequest.html" name="" title="ConnectRequest">ConnectRequest</a>
<li> </li>
<a href="Titanium.Web.Proxy.Http.Request.html" name="" title="Request">Request</a> <li>
</li> <a href="Titanium.Web.Proxy.Http.ConnectResponse.html" name="" title="ConnectResponse">ConnectResponse</a>
<li> </li>
<a href="Titanium.Web.Proxy.Http.RequestResponseBase.html" name="" title="RequestResponseBase">RequestResponseBase</a> <li>
</li> <a href="Titanium.Web.Proxy.Http.HeaderCollection.html" name="" title="HeaderCollection">HeaderCollection</a>
<li> </li>
<a href="Titanium.Web.Proxy.Http.Response.html" name="" title="Response">Response</a> <li>
</li> <a href="Titanium.Web.Proxy.Http.HttpWebClient.html" name="" title="HttpWebClient">HttpWebClient</a>
</ul> </li> </li>
<li> <li>
<span class="expand-stub"></span> <a href="Titanium.Web.Proxy.Http.KnownHeaders.html" name="" title="KnownHeaders">KnownHeaders</a>
<a href="Titanium.Web.Proxy.Http.Responses.html" name="" title="Titanium.Web.Proxy.Http.Responses">Titanium.Web.Proxy.Http.Responses</a> </li>
<li>
<ul class="nav level2"> <a href="Titanium.Web.Proxy.Http.Request.html" name="" title="Request">Request</a>
<li> </li>
<a href="Titanium.Web.Proxy.Http.Responses.GenericResponse.html" name="" title="GenericResponse">GenericResponse</a> <li>
</li> <a href="Titanium.Web.Proxy.Http.RequestResponseBase.html" name="" title="RequestResponseBase">RequestResponseBase</a>
<li> </li>
<a href="Titanium.Web.Proxy.Http.Responses.OkResponse.html" name="" title="OkResponse">OkResponse</a> <li>
</li> <a href="Titanium.Web.Proxy.Http.Response.html" name="" title="Response">Response</a>
<li> </li>
<a href="Titanium.Web.Proxy.Http.Responses.RedirectResponse.html" name="" title="RedirectResponse">RedirectResponse</a> </ul>
</li> </li>
</ul> </li> <li>
<li> <span class="expand-stub"></span>
<span class="expand-stub"></span> <a href="Titanium.Web.Proxy.Http.Responses.html" name="" title="Titanium.Web.Proxy.Http.Responses">Titanium.Web.Proxy.Http.Responses</a>
<a href="Titanium.Web.Proxy.Models.html" name="" title="Titanium.Web.Proxy.Models">Titanium.Web.Proxy.Models</a>
<ul class="nav level2">
<ul class="nav level2"> <li>
<li> <a href="Titanium.Web.Proxy.Http.Responses.GenericResponse.html" name="" title="GenericResponse">GenericResponse</a>
<a href="Titanium.Web.Proxy.Models.ExplicitProxyEndPoint.html" name="" title="ExplicitProxyEndPoint">ExplicitProxyEndPoint</a> </li>
</li> <li>
<li> <a href="Titanium.Web.Proxy.Http.Responses.OkResponse.html" name="" title="OkResponse">OkResponse</a>
<a href="Titanium.Web.Proxy.Models.ExternalProxy.html" name="" title="ExternalProxy">ExternalProxy</a> </li>
</li> <li>
<li> <a href="Titanium.Web.Proxy.Http.Responses.RedirectResponse.html" name="" title="RedirectResponse">RedirectResponse</a>
<a href="Titanium.Web.Proxy.Models.HttpHeader.html" name="" title="HttpHeader">HttpHeader</a> </li>
</li> </ul>
<li> </li>
<a href="Titanium.Web.Proxy.Models.ProxyEndPoint.html" name="" title="ProxyEndPoint">ProxyEndPoint</a> <li>
</li> <span class="expand-stub"></span>
<li> <a href="Titanium.Web.Proxy.Models.html" name="" title="Titanium.Web.Proxy.Models">Titanium.Web.Proxy.Models</a>
<a href="Titanium.Web.Proxy.Models.TransparentProxyEndPoint.html" name="" title="TransparentProxyEndPoint">TransparentProxyEndPoint</a>
</li> <ul class="nav level2">
</ul> </li> <li>
<li> <a href="Titanium.Web.Proxy.Models.ExplicitProxyEndPoint.html" name="" title="ExplicitProxyEndPoint">ExplicitProxyEndPoint</a>
<span class="expand-stub"></span> </li>
<a href="Titanium.Web.Proxy.Network.html" name="" title="Titanium.Web.Proxy.Network">Titanium.Web.Proxy.Network</a> <li>
<a href="Titanium.Web.Proxy.Models.ExternalProxy.html" name="" title="ExternalProxy">ExternalProxy</a>
<ul class="nav level2"> </li>
<li> <li>
<a href="Titanium.Web.Proxy.Network.CertificateEngine.html" name="" title="CertificateEngine">CertificateEngine</a> <a href="Titanium.Web.Proxy.Models.HttpHeader.html" name="" title="HttpHeader">HttpHeader</a>
</li> </li>
<li> <li>
<a href="Titanium.Web.Proxy.Network.CertificateManager.html" name="" title="CertificateManager">CertificateManager</a> <a href="Titanium.Web.Proxy.Models.ProxyEndPoint.html" name="" title="ProxyEndPoint">ProxyEndPoint</a>
</li> </li>
</ul> </li> <li>
</ul> </div> <a href="Titanium.Web.Proxy.Models.TransparentProxyEndPoint.html" name="" title="TransparentProxyEndPoint">TransparentProxyEndPoint</a>
</li>
</ul>
</li>
<li>
<span class="expand-stub"></span>
<a href="Titanium.Web.Proxy.Network.html" name="" title="Titanium.Web.Proxy.Network">Titanium.Web.Proxy.Network</a>
<ul class="nav level2">
<li>
<a href="Titanium.Web.Proxy.Network.CertificateEngine.html" name="" title="CertificateEngine">CertificateEngine</a>
</li>
<li>
<a href="Titanium.Web.Proxy.Network.CertificateManager.html" name="" title="CertificateManager">CertificateManager</a>
</li>
</ul>
</li>
</ul>
</div>
</div> </div>
</div> </div>
</div> </div>
\ No newline at end of file
...@@ -74,6 +74,16 @@ ...@@ -74,6 +74,16 @@
"title": "Class ProxyHttpException | Titanium Web Proxy", "title": "Class ProxyHttpException | Titanium Web Proxy",
"keywords": "Class ProxyHttpException Proxy HTTP exception. Inheritance Object Exception ProxyException ProxyHttpException Implements ISerializable _Exception Inherited Members Exception.GetBaseException() Exception.ToString() Exception.GetObjectData(SerializationInfo, StreamingContext) Exception.GetType() Exception.Message Exception.Data Exception.InnerException Exception.TargetSite Exception.StackTrace Exception.HelpLink Exception.Source Exception.HResult Exception.SerializeObjectState Object.Equals(Object) Object.Equals(Object, Object) Object.ReferenceEquals(Object, Object) Object.GetHashCode() Object.MemberwiseClone() Namespace : Titanium.Web.Proxy.Exceptions Assembly : Titanium.Web.Proxy.dll Syntax public class ProxyHttpException : ProxyException, ISerializable, _Exception Properties SessionEventArgs Gets session info associated to the exception. Declaration public SessionEventArgs SessionEventArgs { get; } Property Value Type Description SessionEventArgs Remarks This object properties should not be edited. Implements System.Runtime.Serialization.ISerializable System.Runtime.InteropServices._Exception" "keywords": "Class ProxyHttpException Proxy HTTP exception. Inheritance Object Exception ProxyException ProxyHttpException Implements ISerializable _Exception Inherited Members Exception.GetBaseException() Exception.ToString() Exception.GetObjectData(SerializationInfo, StreamingContext) Exception.GetType() Exception.Message Exception.Data Exception.InnerException Exception.TargetSite Exception.StackTrace Exception.HelpLink Exception.Source Exception.HResult Exception.SerializeObjectState Object.Equals(Object) Object.Equals(Object, Object) Object.ReferenceEquals(Object, Object) Object.GetHashCode() Object.MemberwiseClone() Namespace : Titanium.Web.Proxy.Exceptions Assembly : Titanium.Web.Proxy.dll Syntax public class ProxyHttpException : ProxyException, ISerializable, _Exception Properties SessionEventArgs Gets session info associated to the exception. Declaration public SessionEventArgs SessionEventArgs { get; } Property Value Type Description SessionEventArgs Remarks This object properties should not be edited. Implements System.Runtime.Serialization.ISerializable System.Runtime.InteropServices._Exception"
}, },
"api/Titanium.Web.Proxy.Helpers.html": {
"href": "api/Titanium.Web.Proxy.Helpers.html",
"title": "Namespace Titanium.Web.Proxy.Helpers | Titanium Web Proxy",
"keywords": "Namespace Titanium.Web.Proxy.Helpers Classes RunTime Run time helpers"
},
"api/Titanium.Web.Proxy.Helpers.RunTime.html": {
"href": "api/Titanium.Web.Proxy.Helpers.RunTime.html",
"title": "Class RunTime | Titanium Web Proxy",
"keywords": "Class RunTime Run time helpers Inheritance Object RunTime Inherited Members Object.ToString() Object.Equals(Object) Object.Equals(Object, Object) Object.ReferenceEquals(Object, Object) Object.GetHashCode() Object.GetType() Object.MemberwiseClone() Namespace : Titanium.Web.Proxy.Helpers Assembly : Titanium.Web.Proxy.dll Syntax public static class RunTime Properties IsLinux Declaration public static bool IsLinux { get; } Property Value Type Description Boolean IsMac Declaration public static bool IsMac { get; } Property Value Type Description Boolean IsWindows Declaration public static bool IsWindows { get; } Property Value Type Description Boolean"
},
"api/Titanium.Web.Proxy.html": { "api/Titanium.Web.Proxy.html": {
"href": "api/Titanium.Web.Proxy.html", "href": "api/Titanium.Web.Proxy.html",
"title": "Namespace Titanium.Web.Proxy | Titanium Web Proxy", "title": "Namespace Titanium.Web.Proxy | Titanium Web Proxy",
...@@ -107,7 +117,7 @@ ...@@ -107,7 +117,7 @@
"api/Titanium.Web.Proxy.Http.KnownHeaders.html": { "api/Titanium.Web.Proxy.Http.KnownHeaders.html": {
"href": "api/Titanium.Web.Proxy.Http.KnownHeaders.html", "href": "api/Titanium.Web.Proxy.Http.KnownHeaders.html",
"title": "Class KnownHeaders | Titanium Web Proxy", "title": "Class KnownHeaders | Titanium Web Proxy",
"keywords": "Class KnownHeaders Well known http headers. Inheritance Object KnownHeaders Inherited Members Object.ToString() Object.Equals(Object) Object.Equals(Object, Object) Object.ReferenceEquals(Object, Object) Object.GetHashCode() Object.GetType() Object.MemberwiseClone() Namespace : Titanium.Web.Proxy.Http Assembly : Titanium.Web.Proxy.dll Syntax public static class KnownHeaders Fields AcceptEncoding Declaration public const string AcceptEncoding = \"accept-encoding\" Field Value Type Description String Authorization Declaration public const string Authorization = \"Authorization\" Field Value Type Description String Connection Declaration public const string Connection = \"connection\" Field Value Type Description String ConnectionClose Declaration public const string ConnectionClose = \"close\" Field Value Type Description String ConnectionKeepAlive Declaration public const string ConnectionKeepAlive = \"keep-alive\" Field Value Type Description String ContentEncoding Declaration public const string ContentEncoding = \"content-encoding\" Field Value Type Description String ContentEncodingDeflate Declaration public const string ContentEncodingDeflate = \"deflate\" Field Value Type Description String ContentEncodingGzip Declaration public const string ContentEncodingGzip = \"gzip\" Field Value Type Description String ContentLength Declaration public const string ContentLength = \"content-length\" Field Value Type Description String ContentType Declaration public const string ContentType = \"content-type\" Field Value Type Description String ContentTypeBoundary Declaration public const string ContentTypeBoundary = \"boundary\" Field Value Type Description String ContentTypeCharset Declaration public const string ContentTypeCharset = \"charset\" Field Value Type Description String Expect Declaration public const string Expect = \"expect\" Field Value Type Description String Expect100Continue Declaration public const string Expect100Continue = \"100-continue\" Field Value Type Description String Host Declaration public const string Host = \"host\" Field Value Type Description String Location Declaration public const string Location = \"Location\" Field Value Type Description String ProxyAuthenticate Declaration public const string ProxyAuthenticate = \"Proxy-Authenticate\" Field Value Type Description String ProxyAuthorization Declaration public const string ProxyAuthorization = \"Proxy-Authorization\" Field Value Type Description String ProxyAuthorizationBasic Declaration public const string ProxyAuthorizationBasic = \"basic\" Field Value Type Description String ProxyConnection Declaration public const string ProxyConnection = \"Proxy-Connection\" Field Value Type Description String ProxyConnectionClose Declaration public const string ProxyConnectionClose = \"close\" Field Value Type Description String TransferEncoding Declaration public const string TransferEncoding = \"transfer-encoding\" Field Value Type Description String TransferEncodingChunked Declaration public const string TransferEncodingChunked = \"chunked\" Field Value Type Description String Upgrade Declaration public const string Upgrade = \"upgrade\" Field Value Type Description String UpgradeWebsocket Declaration public const string UpgradeWebsocket = \"websocket\" Field Value Type Description String" "keywords": "Class KnownHeaders Well known http headers. Inheritance Object KnownHeaders Inherited Members Object.ToString() Object.Equals(Object) Object.Equals(Object, Object) Object.ReferenceEquals(Object, Object) Object.GetHashCode() Object.GetType() Object.MemberwiseClone() Namespace : Titanium.Web.Proxy.Http Assembly : Titanium.Web.Proxy.dll Syntax public static class KnownHeaders Fields AcceptEncoding Declaration public const string AcceptEncoding = \"accept-encoding\" Field Value Type Description String Authorization Declaration public const string Authorization = \"Authorization\" Field Value Type Description String Connection Declaration public const string Connection = \"connection\" Field Value Type Description String ConnectionClose Declaration public const string ConnectionClose = \"close\" Field Value Type Description String ConnectionKeepAlive Declaration public const string ConnectionKeepAlive = \"keep-alive\" Field Value Type Description String ContentEncoding Declaration public const string ContentEncoding = \"content-encoding\" Field Value Type Description String ContentEncodingBrotli Declaration public const string ContentEncodingBrotli = \"br\" Field Value Type Description String ContentEncodingDeflate Declaration public const string ContentEncodingDeflate = \"deflate\" Field Value Type Description String ContentEncodingGzip Declaration public const string ContentEncodingGzip = \"gzip\" Field Value Type Description String ContentLength Declaration public const string ContentLength = \"content-length\" Field Value Type Description String ContentType Declaration public const string ContentType = \"content-type\" Field Value Type Description String ContentTypeBoundary Declaration public const string ContentTypeBoundary = \"boundary\" Field Value Type Description String ContentTypeCharset Declaration public const string ContentTypeCharset = \"charset\" Field Value Type Description String Expect Declaration public const string Expect = \"expect\" Field Value Type Description String Expect100Continue Declaration public const string Expect100Continue = \"100-continue\" Field Value Type Description String Host Declaration public const string Host = \"host\" Field Value Type Description String Location Declaration public const string Location = \"Location\" Field Value Type Description String ProxyAuthenticate Declaration public const string ProxyAuthenticate = \"Proxy-Authenticate\" Field Value Type Description String ProxyAuthorization Declaration public const string ProxyAuthorization = \"Proxy-Authorization\" Field Value Type Description String ProxyAuthorizationBasic Declaration public const string ProxyAuthorizationBasic = \"basic\" Field Value Type Description String ProxyConnection Declaration public const string ProxyConnection = \"Proxy-Connection\" Field Value Type Description String ProxyConnectionClose Declaration public const string ProxyConnectionClose = \"close\" Field Value Type Description String TransferEncoding Declaration public const string TransferEncoding = \"transfer-encoding\" Field Value Type Description String TransferEncodingChunked Declaration public const string TransferEncodingChunked = \"chunked\" Field Value Type Description String Upgrade Declaration public const string Upgrade = \"upgrade\" Field Value Type Description String UpgradeWebsocket Declaration public const string UpgradeWebsocket = \"websocket\" Field Value Type Description String"
}, },
"api/Titanium.Web.Proxy.Http.Request.html": { "api/Titanium.Web.Proxy.Http.Request.html": {
"href": "api/Titanium.Web.Proxy.Http.Request.html", "href": "api/Titanium.Web.Proxy.Http.Request.html",
...@@ -177,7 +187,7 @@ ...@@ -177,7 +187,7 @@
"api/Titanium.Web.Proxy.Network.CertificateEngine.html": { "api/Titanium.Web.Proxy.Network.CertificateEngine.html": {
"href": "api/Titanium.Web.Proxy.Network.CertificateEngine.html", "href": "api/Titanium.Web.Proxy.Network.CertificateEngine.html",
"title": "Enum CertificateEngine | Titanium Web Proxy", "title": "Enum CertificateEngine | Titanium Web Proxy",
"keywords": "Enum CertificateEngine Certificate Engine option. Namespace : Titanium.Web.Proxy.Network Assembly : Titanium.Web.Proxy.dll Syntax public enum CertificateEngine Fields Name Description BouncyCastle Uses BouncyCastle 3rd party library. Default. DefaultWindows Uses Windows Certification Generation API. Bug #468 Reported." "keywords": "Enum CertificateEngine Certificate Engine option. Namespace : Titanium.Web.Proxy.Network Assembly : Titanium.Web.Proxy.dll Syntax public enum CertificateEngine Fields Name Description BouncyCastle Uses BouncyCastle 3rd party library. Default. DefaultWindows Uses Windows Certification Generation API and only valid in Windows OS. Observed to be faster than BouncyCastle. Bug #468 Reported."
}, },
"api/Titanium.Web.Proxy.Network.CertificateManager.html": { "api/Titanium.Web.Proxy.Network.CertificateManager.html": {
"href": "api/Titanium.Web.Proxy.Network.CertificateManager.html", "href": "api/Titanium.Web.Proxy.Network.CertificateManager.html",
...@@ -192,6 +202,6 @@ ...@@ -192,6 +202,6 @@
"api/Titanium.Web.Proxy.ProxyServer.html": { "api/Titanium.Web.Proxy.ProxyServer.html": {
"href": "api/Titanium.Web.Proxy.ProxyServer.html", "href": "api/Titanium.Web.Proxy.ProxyServer.html",
"title": "Class ProxyServer | Titanium Web Proxy", "title": "Class ProxyServer | Titanium Web Proxy",
"keywords": "Class ProxyServer This class is the backbone of proxy. One can create as many instances as needed. However care should be taken to avoid using the same listening ports across multiple instances. Inheritance Object ProxyServer Implements IDisposable Inherited Members Object.ToString() Object.Equals(Object) Object.Equals(Object, Object) Object.ReferenceEquals(Object, Object) Object.GetHashCode() Object.GetType() Object.MemberwiseClone() Namespace : Titanium.Web.Proxy Assembly : Titanium.Web.Proxy.dll Syntax public class ProxyServer : IDisposable Constructors ProxyServer(Boolean, Boolean, Boolean) Initializes a new instance of ProxyServer class with provided parameters. Declaration public ProxyServer(bool userTrustRootCertificate = true, bool machineTrustRootCertificate = false, bool trustRootCertificateAsAdmin = false) Parameters Type Name Description Boolean userTrustRootCertificate Should fake HTTPS certificate be trusted by this machine's user certificate store? Boolean machineTrustRootCertificate Should fake HTTPS certificate be trusted by this machine's certificate store? Boolean trustRootCertificateAsAdmin Should we attempt to trust certificates with elevated permissions by prompting for UAC if required? ProxyServer(String, String, Boolean, Boolean, Boolean) Initializes a new instance of ProxyServer class with provided parameters. Declaration public ProxyServer(string rootCertificateName, string rootCertificateIssuerName, bool userTrustRootCertificate = true, bool machineTrustRootCertificate = false, bool trustRootCertificateAsAdmin = false) Parameters Type Name Description String rootCertificateName Name of the root certificate. String rootCertificateIssuerName Name of the root certificate issuer. Boolean userTrustRootCertificate Should fake HTTPS certificate be trusted by this machine's user certificate store? Boolean machineTrustRootCertificate Should fake HTTPS certificate be trusted by this machine's certificate store? Boolean trustRootCertificateAsAdmin Should we attempt to trust certificates with elevated permissions by prompting for UAC if required? Properties BufferPool The buffer pool used throughout this proxy instance. Set custom implementations by implementing this interface. By default this uses DefaultBufferPool implementation available in StreamExtended library package. Declaration public IBufferPool BufferPool { get; set; } Property Value Type Description StreamExtended.IBufferPool BufferSize Buffer size in bytes used throughout this proxy. Default value is 8192 bytes. Declaration public int BufferSize { get; set; } Property Value Type Description Int32 CertificateManager Manages certificates used by this proxy. Declaration public CertificateManager CertificateManager { get; } Property Value Type Description CertificateManager CheckCertificateRevocation Should we check for certificare revocation during SSL authentication to servers Note: If enabled can reduce performance. Defaults to false. Declaration public X509RevocationMode CheckCertificateRevocation { get; set; } Property Value Type Description X509RevocationMode ClientConnectionCount Total number of active client connections. Declaration public int ClientConnectionCount { get; } Property Value Type Description Int32 ConnectionTimeOutSeconds Seconds client/server connection are to be kept alive when waiting for read/write to complete. This will also determine the pool eviction time when connection pool is enabled. Default value is 60 seconds. Declaration public int ConnectionTimeOutSeconds { get; set; } Property Value Type Description Int32 Enable100ContinueBehaviour Does this proxy uses the HTTP protocol 100 continue behaviour strictly? Broken 100 contunue implementations on server/client may cause problems if enabled. Defaults to false. Declaration public bool Enable100ContinueBehaviour { get; set; } Property Value Type Description Boolean EnableConnectionPool Should we enable experimental server connection pool? Defaults to disable. Declaration public bool EnableConnectionPool { get; set; } Property Value Type Description Boolean EnableWinAuth Enable disable Windows Authentication (NTLM/Kerberos). Note: NTLM/Kerberos will always send local credentials of current user running the proxy process. This is because a man in middle attack with Windows domain authentication is not currently supported. Defaults to false. Declaration public bool EnableWinAuth { get; set; } Property Value Type Description Boolean ExceptionFunc Callback for error events in this proxy instance. Declaration public ExceptionHandler ExceptionFunc { get; set; } Property Value Type Description ExceptionHandler ForwardToUpstreamGateway Gets or sets a value indicating whether requests will be chained to upstream gateway. Defaults to false. Declaration public bool ForwardToUpstreamGateway { get; set; } Property Value Type Description Boolean GetCustomUpStreamProxyFunc A callback to provide authentication credentials for up stream proxy this proxy is using for HTTP(S) requests. User should return the ExternalProxy object with valid credentials. Declaration public Func<SessionEventArgsBase, Task<ExternalProxy>> GetCustomUpStreamProxyFunc { get; set; } Property Value Type Description Func < SessionEventArgsBase , Task < ExternalProxy >> MaxCachedConnections Maximum number of concurrent connections per remote host in cache. Only valid when connection pooling is enabled. Default value is 2. Declaration public int MaxCachedConnections { get; set; } Property Value Type Description Int32 ProxyAuthenticationRealm Realm used during Proxy Basic Authentication. Declaration public string ProxyAuthenticationRealm { get; set; } Property Value Type Description String ProxyAuthenticationSchemes A collection of scheme types, e.g. basic, NTLM, Kerberos, Negotiate, to return if scheme authentication is required. Works in relation with ProxySchemeAuthenticateFunc. Declaration public IEnumerable<string> ProxyAuthenticationSchemes { get; set; } Property Value Type Description IEnumerable < String > ProxyBasicAuthenticateFunc A callback to authenticate proxy clients via basic authentication. Parameters are username and password as provided by client. Should return true for successful authentication. Declaration public Func<SessionEventArgsBase, string, string, Task<bool>> ProxyBasicAuthenticateFunc { get; set; } Property Value Type Description Func < SessionEventArgsBase , String , String , Task < Boolean >> ProxyEndPoints A list of IpAddress and port this proxy is listening to. Declaration public List<ProxyEndPoint> ProxyEndPoints { get; set; } Property Value Type Description List < ProxyEndPoint > ProxyRunning Is the proxy currently running? Declaration public bool ProxyRunning { get; } Property Value Type Description Boolean ProxySchemeAuthenticateFunc A pluggable callback to authenticate clients by scheme instead of requiring basic authentication through ProxyBasicAuthenticateFunc. Parameters are current working session, schemeType, and token as provided by a calling client. Should return success for successful authentication, continuation if the package requests, or failure. Declaration public Func<SessionEventArgsBase, string, string, Task<ProxyAuthenticationContext>> ProxySchemeAuthenticateFunc { get; set; } Property Value Type Description Func < SessionEventArgsBase , String , String , Task < ProxyAuthenticationContext >> ReuseSocket Should we reuse client/server tcp sockets. Default is true (disabled for linux/macOS due to bug in .Net core). Declaration public bool ReuseSocket { get; set; } Property Value Type Description Boolean ServerConnectionCount Total number of active server connections. Declaration public int ServerConnectionCount { get; } Property Value Type Description Int32 SupportedSslProtocols List of supported Ssl versions. Declaration public SslProtocols SupportedSslProtocols { get; set; } Property Value Type Description SslProtocols TcpTimeWaitSeconds Number of seconds to linger when Tcp connection is in TIME_WAIT state. Default value is 30. Declaration public int TcpTimeWaitSeconds { get; set; } Property Value Type Description Int32 UpStreamEndPoint Local adapter/NIC endpoint where proxy makes request via. Defaults via any IP addresses of this machine. Declaration public IPEndPoint UpStreamEndPoint { get; set; } Property Value Type Description IPEndPoint UpStreamHttpProxy External proxy used for Http requests. Declaration public ExternalProxy UpStreamHttpProxy { get; set; } Property Value Type Description ExternalProxy UpStreamHttpsProxy External proxy used for Https requests. Declaration public ExternalProxy UpStreamHttpsProxy { get; set; } Property Value Type Description ExternalProxy Methods AddEndPoint(ProxyEndPoint) Add a proxy end point. Declaration public void AddEndPoint(ProxyEndPoint endPoint) Parameters Type Name Description ProxyEndPoint endPoint The proxy endpoint. DisableAllSystemProxies() Clear all proxy settings for current machine. Declaration public void DisableAllSystemProxies() DisableSystemHttpProxy() Clear HTTP proxy settings of current machine. Declaration public void DisableSystemHttpProxy() DisableSystemHttpsProxy() Clear HTTPS proxy settings of current machine. Declaration public void DisableSystemHttpsProxy() DisableSystemProxy(ProxyProtocolType) Clear the specified proxy setting for current machine. Declaration public void DisableSystemProxy(ProxyProtocolType protocolType) Parameters Type Name Description ProxyProtocolType protocolType Dispose() Dispose the Proxy instance. Declaration public void Dispose() RemoveEndPoint(ProxyEndPoint) Remove a proxy end point. Will throw error if the end point does'nt exist. Declaration public void RemoveEndPoint(ProxyEndPoint endPoint) Parameters Type Name Description ProxyEndPoint endPoint The existing endpoint to remove. SetAsSystemHttpProxy(ExplicitProxyEndPoint) Set the given explicit end point as the default proxy server for current machine. Declaration public void SetAsSystemHttpProxy(ExplicitProxyEndPoint endPoint) Parameters Type Name Description ExplicitProxyEndPoint endPoint The explicit endpoint. SetAsSystemHttpsProxy(ExplicitProxyEndPoint) Set the given explicit end point as the default proxy server for current machine. Declaration public void SetAsSystemHttpsProxy(ExplicitProxyEndPoint endPoint) Parameters Type Name Description ExplicitProxyEndPoint endPoint The explicit endpoint. SetAsSystemProxy(ExplicitProxyEndPoint, ProxyProtocolType) Set the given explicit end point as the default proxy server for current machine. Declaration public void SetAsSystemProxy(ExplicitProxyEndPoint endPoint, ProxyProtocolType protocolType) Parameters Type Name Description ExplicitProxyEndPoint endPoint The explicit endpoint. ProxyProtocolType protocolType The proxy protocol type. Start() Start this proxy server instance. Declaration public void Start() Stop() Stop this proxy server instance. Declaration public void Stop() Events AfterResponse Intercept after response event from server. Declaration public event AsyncEventHandler<SessionEventArgs> AfterResponse Event Type Type Description AsyncEventHandler < SessionEventArgs > BeforeRequest Intercept request event to server. Declaration public event AsyncEventHandler<SessionEventArgs> BeforeRequest Event Type Type Description AsyncEventHandler < SessionEventArgs > BeforeResponse Intercept response event from server. Declaration public event AsyncEventHandler<SessionEventArgs> BeforeResponse Event Type Type Description AsyncEventHandler < SessionEventArgs > ClientCertificateSelectionCallback Event to override client certificate selection during mutual SSL authentication. Declaration public event AsyncEventHandler<CertificateSelectionEventArgs> ClientCertificateSelectionCallback Event Type Type Description AsyncEventHandler < CertificateSelectionEventArgs > ClientConnectionCountChanged Event occurs when client connection count changed. Declaration public event EventHandler ClientConnectionCountChanged Event Type Type Description EventHandler OnClientConnectionCreate Customize TcpClient used for client connection upon create. Declaration public event AsyncEventHandler<TcpClient> OnClientConnectionCreate Event Type Type Description AsyncEventHandler < TcpClient > OnServerConnectionCreate Customize TcpClient used for server connection upon create. Declaration public event AsyncEventHandler<TcpClient> OnServerConnectionCreate Event Type Type Description AsyncEventHandler < TcpClient > ServerCertificateValidationCallback Event to override the default verification logic of remote SSL certificate received during authentication. Declaration public event AsyncEventHandler<CertificateValidationEventArgs> ServerCertificateValidationCallback Event Type Type Description AsyncEventHandler < CertificateValidationEventArgs > ServerConnectionCountChanged Event occurs when server connection count changed. Declaration public event EventHandler ServerConnectionCountChanged Event Type Type Description EventHandler Implements System.IDisposable" "keywords": "Class ProxyServer This class is the backbone of proxy. One can create as many instances as needed. However care should be taken to avoid using the same listening ports across multiple instances. Inheritance Object ProxyServer Implements IDisposable Inherited Members Object.ToString() Object.Equals(Object) Object.Equals(Object, Object) Object.ReferenceEquals(Object, Object) Object.GetHashCode() Object.GetType() Object.MemberwiseClone() Namespace : Titanium.Web.Proxy Assembly : Titanium.Web.Proxy.dll Syntax public class ProxyServer : IDisposable Constructors ProxyServer(Boolean, Boolean, Boolean) Initializes a new instance of ProxyServer class with provided parameters. Declaration public ProxyServer(bool userTrustRootCertificate = true, bool machineTrustRootCertificate = false, bool trustRootCertificateAsAdmin = false) Parameters Type Name Description Boolean userTrustRootCertificate Should fake HTTPS certificate be trusted by this machine's user certificate store? Boolean machineTrustRootCertificate Should fake HTTPS certificate be trusted by this machine's certificate store? Boolean trustRootCertificateAsAdmin Should we attempt to trust certificates with elevated permissions by prompting for UAC if required? ProxyServer(String, String, Boolean, Boolean, Boolean) Initializes a new instance of ProxyServer class with provided parameters. Declaration public ProxyServer(string rootCertificateName, string rootCertificateIssuerName, bool userTrustRootCertificate = true, bool machineTrustRootCertificate = false, bool trustRootCertificateAsAdmin = false) Parameters Type Name Description String rootCertificateName Name of the root certificate. String rootCertificateIssuerName Name of the root certificate issuer. Boolean userTrustRootCertificate Should fake HTTPS certificate be trusted by this machine's user certificate store? Boolean machineTrustRootCertificate Should fake HTTPS certificate be trusted by this machine's certificate store? Boolean trustRootCertificateAsAdmin Should we attempt to trust certificates with elevated permissions by prompting for UAC if required? Properties BufferPool The buffer pool used throughout this proxy instance. Set custom implementations by implementing this interface. By default this uses DefaultBufferPool implementation available in StreamExtended library package. Declaration public IBufferPool BufferPool { get; set; } Property Value Type Description StreamExtended.IBufferPool BufferSize Buffer size in bytes used throughout this proxy. Default value is 8192 bytes. Declaration public int BufferSize { get; set; } Property Value Type Description Int32 CertificateManager Manages certificates used by this proxy. Declaration public CertificateManager CertificateManager { get; } Property Value Type Description CertificateManager CheckCertificateRevocation Should we check for certificare revocation during SSL authentication to servers Note: If enabled can reduce performance. Defaults to false. Declaration public X509RevocationMode CheckCertificateRevocation { get; set; } Property Value Type Description X509RevocationMode ClientConnectionCount Total number of active client connections. Declaration public int ClientConnectionCount { get; } Property Value Type Description Int32 ConnectionTimeOutSeconds Seconds client/server connection are to be kept alive when waiting for read/write to complete. This will also determine the pool eviction time when connection pool is enabled. Default value is 60 seconds. Declaration public int ConnectionTimeOutSeconds { get; set; } Property Value Type Description Int32 Enable100ContinueBehaviour Does this proxy uses the HTTP protocol 100 continue behaviour strictly? Broken 100 contunue implementations on server/client may cause problems if enabled. Defaults to false. Declaration public bool Enable100ContinueBehaviour { get; set; } Property Value Type Description Boolean EnableConnectionPool Should we enable experimental server connection pool? Defaults to true. Declaration public bool EnableConnectionPool { get; set; } Property Value Type Description Boolean EnableTcpServerConnectionPrefetch Should we enable tcp server connection prefetching? When enabled, as soon as we receive a client connection we concurrently initiate corresponding server connection process using CONNECT hostname or SNI hostname on a separate task so that after parsing client request we will have the server connection immediately ready or in the process of getting ready. If a server connection is available in cache then this prefetch task will immediatly return with the available connection from cache. Defaults to true. Declaration public bool EnableTcpServerConnectionPrefetch { get; set; } Property Value Type Description Boolean EnableWinAuth Enable disable Windows Authentication (NTLM/Kerberos). Note: NTLM/Kerberos will always send local credentials of current user running the proxy process. This is because a man in middle attack with Windows domain authentication is not currently supported. Defaults to false. Declaration public bool EnableWinAuth { get; set; } Property Value Type Description Boolean ExceptionFunc Callback for error events in this proxy instance. Declaration public ExceptionHandler ExceptionFunc { get; set; } Property Value Type Description ExceptionHandler ForwardToUpstreamGateway Gets or sets a value indicating whether requests will be chained to upstream gateway. Defaults to false. Declaration public bool ForwardToUpstreamGateway { get; set; } Property Value Type Description Boolean GetCustomUpStreamProxyFunc A callback to provide authentication credentials for up stream proxy this proxy is using for HTTP(S) requests. User should return the ExternalProxy object with valid credentials. Declaration public Func<SessionEventArgsBase, Task<ExternalProxy>> GetCustomUpStreamProxyFunc { get; set; } Property Value Type Description Func < SessionEventArgsBase , Task < ExternalProxy >> MaxCachedConnections Maximum number of concurrent connections per remote host in cache. Only valid when connection pooling is enabled. Default value is 2. Declaration public int MaxCachedConnections { get; set; } Property Value Type Description Int32 ProxyAuthenticationRealm Realm used during Proxy Basic Authentication. Declaration public string ProxyAuthenticationRealm { get; set; } Property Value Type Description String ProxyAuthenticationSchemes A collection of scheme types, e.g. basic, NTLM, Kerberos, Negotiate, to return if scheme authentication is required. Works in relation with ProxySchemeAuthenticateFunc. Declaration public IEnumerable<string> ProxyAuthenticationSchemes { get; set; } Property Value Type Description IEnumerable < String > ProxyBasicAuthenticateFunc A callback to authenticate proxy clients via basic authentication. Parameters are username and password as provided by client. Should return true for successful authentication. Declaration public Func<SessionEventArgsBase, string, string, Task<bool>> ProxyBasicAuthenticateFunc { get; set; } Property Value Type Description Func < SessionEventArgsBase , String , String , Task < Boolean >> ProxyEndPoints A list of IpAddress and port this proxy is listening to. Declaration public List<ProxyEndPoint> ProxyEndPoints { get; set; } Property Value Type Description List < ProxyEndPoint > ProxyRunning Is the proxy currently running? Declaration public bool ProxyRunning { get; } Property Value Type Description Boolean ProxySchemeAuthenticateFunc A pluggable callback to authenticate clients by scheme instead of requiring basic authentication through ProxyBasicAuthenticateFunc. Parameters are current working session, schemeType, and token as provided by a calling client. Should return success for successful authentication, continuation if the package requests, or failure. Declaration public Func<SessionEventArgsBase, string, string, Task<ProxyAuthenticationContext>> ProxySchemeAuthenticateFunc { get; set; } Property Value Type Description Func < SessionEventArgsBase , String , String , Task < ProxyAuthenticationContext >> ReuseSocket Should we reuse client/server tcp sockets. Default is true (disabled for linux/macOS due to bug in .Net core). Declaration public bool ReuseSocket { get; set; } Property Value Type Description Boolean ServerConnectionCount Total number of active server connections. Declaration public int ServerConnectionCount { get; } Property Value Type Description Int32 SupportedSslProtocols List of supported Ssl versions. Declaration public SslProtocols SupportedSslProtocols { get; set; } Property Value Type Description SslProtocols TcpTimeWaitSeconds Number of seconds to linger when Tcp connection is in TIME_WAIT state. Default value is 30. Declaration public int TcpTimeWaitSeconds { get; set; } Property Value Type Description Int32 UpStreamEndPoint Local adapter/NIC endpoint where proxy makes request via. Defaults via any IP addresses of this machine. Declaration public IPEndPoint UpStreamEndPoint { get; set; } Property Value Type Description IPEndPoint UpStreamHttpProxy External proxy used for Http requests. Declaration public ExternalProxy UpStreamHttpProxy { get; set; } Property Value Type Description ExternalProxy UpStreamHttpsProxy External proxy used for Https requests. Declaration public ExternalProxy UpStreamHttpsProxy { get; set; } Property Value Type Description ExternalProxy Methods AddEndPoint(ProxyEndPoint) Add a proxy end point. Declaration public void AddEndPoint(ProxyEndPoint endPoint) Parameters Type Name Description ProxyEndPoint endPoint The proxy endpoint. DisableAllSystemProxies() Clear all proxy settings for current machine. Declaration public void DisableAllSystemProxies() DisableSystemHttpProxy() Clear HTTP proxy settings of current machine. Declaration public void DisableSystemHttpProxy() DisableSystemHttpsProxy() Clear HTTPS proxy settings of current machine. Declaration public void DisableSystemHttpsProxy() DisableSystemProxy(ProxyProtocolType) Clear the specified proxy setting for current machine. Declaration public void DisableSystemProxy(ProxyProtocolType protocolType) Parameters Type Name Description ProxyProtocolType protocolType Dispose() Dispose the Proxy instance. Declaration public void Dispose() RemoveEndPoint(ProxyEndPoint) Remove a proxy end point. Will throw error if the end point does'nt exist. Declaration public void RemoveEndPoint(ProxyEndPoint endPoint) Parameters Type Name Description ProxyEndPoint endPoint The existing endpoint to remove. SetAsSystemHttpProxy(ExplicitProxyEndPoint) Set the given explicit end point as the default proxy server for current machine. Declaration public void SetAsSystemHttpProxy(ExplicitProxyEndPoint endPoint) Parameters Type Name Description ExplicitProxyEndPoint endPoint The explicit endpoint. SetAsSystemHttpsProxy(ExplicitProxyEndPoint) Set the given explicit end point as the default proxy server for current machine. Declaration public void SetAsSystemHttpsProxy(ExplicitProxyEndPoint endPoint) Parameters Type Name Description ExplicitProxyEndPoint endPoint The explicit endpoint. SetAsSystemProxy(ExplicitProxyEndPoint, ProxyProtocolType) Set the given explicit end point as the default proxy server for current machine. Declaration public void SetAsSystemProxy(ExplicitProxyEndPoint endPoint, ProxyProtocolType protocolType) Parameters Type Name Description ExplicitProxyEndPoint endPoint The explicit endpoint. ProxyProtocolType protocolType The proxy protocol type. Start() Start this proxy server instance. Declaration public void Start() Stop() Stop this proxy server instance. Declaration public void Stop() Events AfterResponse Intercept after response event from server. Declaration public event AsyncEventHandler<SessionEventArgs> AfterResponse Event Type Type Description AsyncEventHandler < SessionEventArgs > BeforeRequest Intercept request event to server. Declaration public event AsyncEventHandler<SessionEventArgs> BeforeRequest Event Type Type Description AsyncEventHandler < SessionEventArgs > BeforeResponse Intercept response event from server. Declaration public event AsyncEventHandler<SessionEventArgs> BeforeResponse Event Type Type Description AsyncEventHandler < SessionEventArgs > ClientCertificateSelectionCallback Event to override client certificate selection during mutual SSL authentication. Declaration public event AsyncEventHandler<CertificateSelectionEventArgs> ClientCertificateSelectionCallback Event Type Type Description AsyncEventHandler < CertificateSelectionEventArgs > ClientConnectionCountChanged Event occurs when client connection count changed. Declaration public event EventHandler ClientConnectionCountChanged Event Type Type Description EventHandler OnClientConnectionCreate Customize TcpClient used for client connection upon create. Declaration public event AsyncEventHandler<TcpClient> OnClientConnectionCreate Event Type Type Description AsyncEventHandler < TcpClient > OnServerConnectionCreate Customize TcpClient used for server connection upon create. Declaration public event AsyncEventHandler<TcpClient> OnServerConnectionCreate Event Type Type Description AsyncEventHandler < TcpClient > ServerCertificateValidationCallback Event to override the default verification logic of remote SSL certificate received during authentication. Declaration public event AsyncEventHandler<CertificateValidationEventArgs> ServerCertificateValidationCallback Event Type Type Description AsyncEventHandler < CertificateValidationEventArgs > ServerConnectionCountChanged Event occurs when server connection count changed. Declaration public event EventHandler ServerConnectionCountChanged Event Type Type Description EventHandler Implements System.IDisposable"
} }
} }
...@@ -387,7 +387,10 @@ $(function () { ...@@ -387,7 +387,10 @@ $(function () {
} }
} else { } else {
if (util.getAbsolutePath(href) === currentAbsPath) { if (util.getAbsolutePath(href) === currentAbsPath) {
isActive = true; var dropdown = $(e).attr('data-toggle') == "dropdown"
if (!dropdown) {
isActive = true;
}
} }
} }
if (isActive) { if (isActive) {
......
...@@ -27,14 +27,15 @@ return{aliases:["styl"],cI:!1,k:"if else for in",i:"("+l.join("|")+")",c:[e.QSM, ...@@ -27,14 +27,15 @@ return{aliases:["styl"],cI:!1,k:"if else for in",i:"("+l.join("|")+")",c:[e.QSM,
built_in:"ip eip rip al ah bl bh cl ch dl dh sil dil bpl spl r8b r9b r10b r11b r12b r13b r14b r15b ax bx cx dx si di bp sp r8w r9w r10w r11w r12w r13w r14w r15w eax ebx ecx edx esi edi ebp esp eip r8d r9d r10d r11d r12d r13d r14d r15d rax rbx rcx rdx rsi rdi rbp rsp r8 r9 r10 r11 r12 r13 r14 r15 cs ds es fs gs ss st st0 st1 st2 st3 st4 st5 st6 st7 mm0 mm1 mm2 mm3 mm4 mm5 mm6 mm7 xmm0 xmm1 xmm2 xmm3 xmm4 xmm5 xmm6 xmm7 xmm8 xmm9 xmm10 xmm11 xmm12 xmm13 xmm14 xmm15 xmm16 xmm17 xmm18 xmm19 xmm20 xmm21 xmm22 xmm23 xmm24 xmm25 xmm26 xmm27 xmm28 xmm29 xmm30 xmm31 ymm0 ymm1 ymm2 ymm3 ymm4 ymm5 ymm6 ymm7 ymm8 ymm9 ymm10 ymm11 ymm12 ymm13 ymm14 ymm15 ymm16 ymm17 ymm18 ymm19 ymm20 ymm21 ymm22 ymm23 ymm24 ymm25 ymm26 ymm27 ymm28 ymm29 ymm30 ymm31 zmm0 zmm1 zmm2 zmm3 zmm4 zmm5 zmm6 zmm7 zmm8 zmm9 zmm10 zmm11 zmm12 zmm13 zmm14 zmm15 zmm16 zmm17 zmm18 zmm19 zmm20 zmm21 zmm22 zmm23 zmm24 zmm25 zmm26 zmm27 zmm28 zmm29 zmm30 zmm31 k0 k1 k2 k3 k4 k5 k6 k7 bnd0 bnd1 bnd2 bnd3 cr0 cr1 cr2 cr3 cr4 cr8 dr0 dr1 dr2 dr3 dr8 tr3 tr4 tr5 tr6 tr7 r0 r1 r2 r3 r4 r5 r6 r7 r0b r1b r2b r3b r4b r5b r6b r7b r0w r1w r2w r3w r4w r5w r6w r7w r0d r1d r2d r3d r4d r5d r6d r7d r0h r1h r2h r3h r0l r1l r2l r3l r4l r5l r6l r7l r8l r9l r10l r11l r12l r13l r14l r15l db dw dd dq dt ddq do dy dz resb resw resd resq rest resdq reso resy resz incbin equ times byte word dword qword nosplit rel abs seg wrt strict near far a32 ptr",meta:"%define %xdefine %+ %undef %defstr %deftok %assign %strcat %strlen %substr %rotate %elif %else %endif %if %ifmacro %ifctx %ifidn %ifidni %ifid %ifnum %ifstr %iftoken %ifempty %ifenv %error %warning %fatal %rep %endrep %include %push %pop %repl %pathsearch %depend %use %arg %stacksize %local %line %comment %endcomment .nolist __FILE__ __LINE__ __SECT__ __BITS__ __OUTPUT_FORMAT__ __DATE__ __TIME__ __DATE_NUM__ __TIME_NUM__ __UTC_DATE__ __UTC_TIME__ __UTC_DATE_NUM__ __UTC_TIME_NUM__ __PASS__ struc endstruc istruc at iend align alignb sectalign daz nodaz up down zero default option assume public bits use16 use32 use64 default section segment absolute extern global common cpu float __utf16__ __utf16le__ __utf16be__ __utf32__ __utf32le__ __utf32be__ __float8__ __float16__ __float32__ __float64__ __float80m__ __float80e__ __float128l__ __float128h__ __Infinity__ __QNaN__ __SNaN__ Inf NaN QNaN SNaN float8 float16 float32 float64 float80m float80e float128l float128h __FLOAT_DAZ__ __FLOAT_ROUND__ __FLOAT__"},c:[e.C(";","$",{r:0}),{cN:"number",v:[{b:"\\b(?:([0-9][0-9_]*)?\\.[0-9_]*(?:[eE][+-]?[0-9_]+)?|(0[Xx])?[0-9][0-9_]*\\.?[0-9_]*(?:[pP](?:[+-]?[0-9_]+)?)?)\\b",r:0},{b:"\\$[0-9][0-9A-Fa-f]*",r:0},{b:"\\b(?:[0-9A-Fa-f][0-9A-Fa-f_]*[Hh]|[0-9][0-9_]*[DdTt]?|[0-7][0-7_]*[QqOo]|[0-1][0-1_]*[BbYy])\\b"},{b:"\\b(?:0[Xx][0-9A-Fa-f_]+|0[DdTt][0-9_]+|0[QqOo][0-7_]+|0[BbYy][0-1_]+)\\b"}]},e.QSM,{cN:"string",v:[{b:"'",e:"[^\\\\]'"},{b:"`",e:"[^\\\\]`"}],r:0},{cN:"symbol",v:[{b:"^\\s*[A-Za-z._?][A-Za-z0-9_$#@~.?]*(:|\\s+label)"},{b:"^\\s*%%[A-Za-z0-9_$#@~.?]*:"}],r:0},{cN:"subst",b:"%[0-9]+",r:0},{cN:"subst",b:"%!S+",r:0},{cN:"meta",b:/^\s*\.[\w_-]+/}]}}),e.registerLanguage("xl",function(e){var t="ObjectLoader Animate MovieCredits Slides Filters Shading Materials LensFlare Mapping VLCAudioVideo StereoDecoder PointCloud NetworkAccess RemoteControl RegExp ChromaKey Snowfall NodeJS Speech Charts",r={keyword:"if then else do while until for loop import with is as where when by data constant integer real text name boolean symbol infix prefix postfix block tree",literal:"true false nil",built_in:"in mod rem and or xor not abs sign floor ceil sqrt sin cos tan asin acos atan exp expm1 log log2 log10 log1p pi at text_length text_range text_find text_replace contains page slide basic_slide title_slide title subtitle fade_in fade_out fade_at clear_color color line_color line_width texture_wrap texture_transform texture scale_?x scale_?y scale_?z? translate_?x translate_?y translate_?z? rotate_?x rotate_?y rotate_?z? rectangle circle ellipse sphere path line_to move_to quad_to curve_to theme background contents locally time mouse_?x mouse_?y mouse_buttons "+t},a={cN:"string",b:'"',e:'"',i:"\\n"},i={cN:"string",b:"'",e:"'",i:"\\n"},n={cN:"string",b:"<<",e:">>"},o={cN:"number",b:"[0-9]+#[0-9A-Z_]+(\\.[0-9-A-Z_]+)?#?([Ee][+-]?[0-9]+)?"},s={bK:"import",e:"$",k:r,c:[a]},l={cN:"function",b:/[a-z][^\n]*->/,rB:!0,e:/->/,c:[e.inherit(e.TM,{starts:{eW:!0,k:r}})]};return{aliases:["tao"],l:/[a-zA-Z][a-zA-Z0-9_?]*/,k:r,c:[e.CLCM,e.CBCM,a,i,n,l,s,o,e.NM]}}),e.registerLanguage("xquery",function(e){var t="for let if while then else return where group by xquery encoding versionmodule namespace boundary-space preserve strip default collation base-uri orderingcopy-namespaces order declare import schema namespace function option in allowing emptyat tumbling window sliding window start when only end when previous next stable ascendingdescending empty greatest least some every satisfies switch case typeswitch try catch andor to union intersect instance of treat as castable cast map array delete insert intoreplace value rename copy modify update",r="false true xs:string xs:integer element item xs:date xs:datetime xs:float xs:double xs:decimal QName xs:anyURI xs:long xs:int xs:short xs:byte attribute",a={b:/\$[a-zA-Z0-9\-]+/},i={cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},n={cN:"string",v:[{b:/"/,e:/"/,c:[{b:/""/,r:0}]},{b:/'/,e:/'/,c:[{b:/''/,r:0}]}]},o={cN:"meta",b:"%\\w+"},s={cN:"comment",b:"\\(:",e:":\\)",r:10,c:[{cN:"doctag",b:"@\\w+"}]},l={b:"{",e:"}"},c=[a,n,i,s,o,l];return l.c=c,{aliases:["xpath","xq"],cI:!1,l:/[a-zA-Z\$][a-zA-Z0-9_:\-]*/,i:/(proc)|(abstract)|(extends)|(until)|(#)/,k:{keyword:t,literal:r},c:c}}),e.registerLanguage("zephir",function(e){var t={cN:"string",c:[e.BE],v:[{b:'b"',e:'"'},{b:"b'",e:"'"},e.inherit(e.ASM,{i:null}),e.inherit(e.QSM,{i:null})]},r={v:[e.BNM,e.CNM]};return{aliases:["zep"],cI:!0,k:"and include_once list abstract global private echo interface as static endswitch array null if endwhile or const for endforeach self var let while isset public protected exit foreach throw elseif include __FILE__ empty require_once do xor return parent clone use __CLASS__ __LINE__ else break print eval new catch __METHOD__ case exception default die require __FUNCTION__ enddeclare final try switch continue endfor endif declare unset true false trait goto instanceof insteadof __DIR__ __NAMESPACE__ yield finally int uint long ulong char uchar double float bool boolean stringlikely unlikely",c:[e.CLCM,e.HCM,e.C("/\\*","\\*/",{c:[{cN:"doctag",b:"@[A-Za-z]+"}]}),e.C("__halt_compiler.+?;",!1,{eW:!0,k:"__halt_compiler",l:e.UIR}),{cN:"string",b:"<<<['\"]?\\w+['\"]?$",e:"^\\w+;",c:[e.BE]},{b:/(::|->)+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/},{cN:"function",bK:"function",e:/[;{]/,eE:!0,i:"\\$|\\[|%",c:[e.UTM,{cN:"params",b:"\\(",e:"\\)",c:["self",e.CBCM,t,r]}]},{cN:"class",bK:"class interface",e:"{",eE:!0,i:/[:\(\$"]/,c:[{bK:"extends implements"},e.UTM]},{bK:"namespace",e:";",i:/[\.']/,c:[e.UTM]},{bK:"use",e:";",c:[e.UTM]},{b:"=>"},t,r]}}),e}); built_in:"ip eip rip al ah bl bh cl ch dl dh sil dil bpl spl r8b r9b r10b r11b r12b r13b r14b r15b ax bx cx dx si di bp sp r8w r9w r10w r11w r12w r13w r14w r15w eax ebx ecx edx esi edi ebp esp eip r8d r9d r10d r11d r12d r13d r14d r15d rax rbx rcx rdx rsi rdi rbp rsp r8 r9 r10 r11 r12 r13 r14 r15 cs ds es fs gs ss st st0 st1 st2 st3 st4 st5 st6 st7 mm0 mm1 mm2 mm3 mm4 mm5 mm6 mm7 xmm0 xmm1 xmm2 xmm3 xmm4 xmm5 xmm6 xmm7 xmm8 xmm9 xmm10 xmm11 xmm12 xmm13 xmm14 xmm15 xmm16 xmm17 xmm18 xmm19 xmm20 xmm21 xmm22 xmm23 xmm24 xmm25 xmm26 xmm27 xmm28 xmm29 xmm30 xmm31 ymm0 ymm1 ymm2 ymm3 ymm4 ymm5 ymm6 ymm7 ymm8 ymm9 ymm10 ymm11 ymm12 ymm13 ymm14 ymm15 ymm16 ymm17 ymm18 ymm19 ymm20 ymm21 ymm22 ymm23 ymm24 ymm25 ymm26 ymm27 ymm28 ymm29 ymm30 ymm31 zmm0 zmm1 zmm2 zmm3 zmm4 zmm5 zmm6 zmm7 zmm8 zmm9 zmm10 zmm11 zmm12 zmm13 zmm14 zmm15 zmm16 zmm17 zmm18 zmm19 zmm20 zmm21 zmm22 zmm23 zmm24 zmm25 zmm26 zmm27 zmm28 zmm29 zmm30 zmm31 k0 k1 k2 k3 k4 k5 k6 k7 bnd0 bnd1 bnd2 bnd3 cr0 cr1 cr2 cr3 cr4 cr8 dr0 dr1 dr2 dr3 dr8 tr3 tr4 tr5 tr6 tr7 r0 r1 r2 r3 r4 r5 r6 r7 r0b r1b r2b r3b r4b r5b r6b r7b r0w r1w r2w r3w r4w r5w r6w r7w r0d r1d r2d r3d r4d r5d r6d r7d r0h r1h r2h r3h r0l r1l r2l r3l r4l r5l r6l r7l r8l r9l r10l r11l r12l r13l r14l r15l db dw dd dq dt ddq do dy dz resb resw resd resq rest resdq reso resy resz incbin equ times byte word dword qword nosplit rel abs seg wrt strict near far a32 ptr",meta:"%define %xdefine %+ %undef %defstr %deftok %assign %strcat %strlen %substr %rotate %elif %else %endif %if %ifmacro %ifctx %ifidn %ifidni %ifid %ifnum %ifstr %iftoken %ifempty %ifenv %error %warning %fatal %rep %endrep %include %push %pop %repl %pathsearch %depend %use %arg %stacksize %local %line %comment %endcomment .nolist __FILE__ __LINE__ __SECT__ __BITS__ __OUTPUT_FORMAT__ __DATE__ __TIME__ __DATE_NUM__ __TIME_NUM__ __UTC_DATE__ __UTC_TIME__ __UTC_DATE_NUM__ __UTC_TIME_NUM__ __PASS__ struc endstruc istruc at iend align alignb sectalign daz nodaz up down zero default option assume public bits use16 use32 use64 default section segment absolute extern global common cpu float __utf16__ __utf16le__ __utf16be__ __utf32__ __utf32le__ __utf32be__ __float8__ __float16__ __float32__ __float64__ __float80m__ __float80e__ __float128l__ __float128h__ __Infinity__ __QNaN__ __SNaN__ Inf NaN QNaN SNaN float8 float16 float32 float64 float80m float80e float128l float128h __FLOAT_DAZ__ __FLOAT_ROUND__ __FLOAT__"},c:[e.C(";","$",{r:0}),{cN:"number",v:[{b:"\\b(?:([0-9][0-9_]*)?\\.[0-9_]*(?:[eE][+-]?[0-9_]+)?|(0[Xx])?[0-9][0-9_]*\\.?[0-9_]*(?:[pP](?:[+-]?[0-9_]+)?)?)\\b",r:0},{b:"\\$[0-9][0-9A-Fa-f]*",r:0},{b:"\\b(?:[0-9A-Fa-f][0-9A-Fa-f_]*[Hh]|[0-9][0-9_]*[DdTt]?|[0-7][0-7_]*[QqOo]|[0-1][0-1_]*[BbYy])\\b"},{b:"\\b(?:0[Xx][0-9A-Fa-f_]+|0[DdTt][0-9_]+|0[QqOo][0-7_]+|0[BbYy][0-1_]+)\\b"}]},e.QSM,{cN:"string",v:[{b:"'",e:"[^\\\\]'"},{b:"`",e:"[^\\\\]`"}],r:0},{cN:"symbol",v:[{b:"^\\s*[A-Za-z._?][A-Za-z0-9_$#@~.?]*(:|\\s+label)"},{b:"^\\s*%%[A-Za-z0-9_$#@~.?]*:"}],r:0},{cN:"subst",b:"%[0-9]+",r:0},{cN:"subst",b:"%!S+",r:0},{cN:"meta",b:/^\s*\.[\w_-]+/}]}}),e.registerLanguage("xl",function(e){var t="ObjectLoader Animate MovieCredits Slides Filters Shading Materials LensFlare Mapping VLCAudioVideo StereoDecoder PointCloud NetworkAccess RemoteControl RegExp ChromaKey Snowfall NodeJS Speech Charts",r={keyword:"if then else do while until for loop import with is as where when by data constant integer real text name boolean symbol infix prefix postfix block tree",literal:"true false nil",built_in:"in mod rem and or xor not abs sign floor ceil sqrt sin cos tan asin acos atan exp expm1 log log2 log10 log1p pi at text_length text_range text_find text_replace contains page slide basic_slide title_slide title subtitle fade_in fade_out fade_at clear_color color line_color line_width texture_wrap texture_transform texture scale_?x scale_?y scale_?z? translate_?x translate_?y translate_?z? rotate_?x rotate_?y rotate_?z? rectangle circle ellipse sphere path line_to move_to quad_to curve_to theme background contents locally time mouse_?x mouse_?y mouse_buttons "+t},a={cN:"string",b:'"',e:'"',i:"\\n"},i={cN:"string",b:"'",e:"'",i:"\\n"},n={cN:"string",b:"<<",e:">>"},o={cN:"number",b:"[0-9]+#[0-9A-Z_]+(\\.[0-9-A-Z_]+)?#?([Ee][+-]?[0-9]+)?"},s={bK:"import",e:"$",k:r,c:[a]},l={cN:"function",b:/[a-z][^\n]*->/,rB:!0,e:/->/,c:[e.inherit(e.TM,{starts:{eW:!0,k:r}})]};return{aliases:["tao"],l:/[a-zA-Z][a-zA-Z0-9_?]*/,k:r,c:[e.CLCM,e.CBCM,a,i,n,l,s,o,e.NM]}}),e.registerLanguage("xquery",function(e){var t="for let if while then else return where group by xquery encoding versionmodule namespace boundary-space preserve strip default collation base-uri orderingcopy-namespaces order declare import schema namespace function option in allowing emptyat tumbling window sliding window start when only end when previous next stable ascendingdescending empty greatest least some every satisfies switch case typeswitch try catch andor to union intersect instance of treat as castable cast map array delete insert intoreplace value rename copy modify update",r="false true xs:string xs:integer element item xs:date xs:datetime xs:float xs:double xs:decimal QName xs:anyURI xs:long xs:int xs:short xs:byte attribute",a={b:/\$[a-zA-Z0-9\-]+/},i={cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},n={cN:"string",v:[{b:/"/,e:/"/,c:[{b:/""/,r:0}]},{b:/'/,e:/'/,c:[{b:/''/,r:0}]}]},o={cN:"meta",b:"%\\w+"},s={cN:"comment",b:"\\(:",e:":\\)",r:10,c:[{cN:"doctag",b:"@\\w+"}]},l={b:"{",e:"}"},c=[a,n,i,s,o,l];return l.c=c,{aliases:["xpath","xq"],cI:!1,l:/[a-zA-Z\$][a-zA-Z0-9_:\-]*/,i:/(proc)|(abstract)|(extends)|(until)|(#)/,k:{keyword:t,literal:r},c:c}}),e.registerLanguage("zephir",function(e){var t={cN:"string",c:[e.BE],v:[{b:'b"',e:'"'},{b:"b'",e:"'"},e.inherit(e.ASM,{i:null}),e.inherit(e.QSM,{i:null})]},r={v:[e.BNM,e.CNM]};return{aliases:["zep"],cI:!0,k:"and include_once list abstract global private echo interface as static endswitch array null if endwhile or const for endforeach self var let while isset public protected exit foreach throw elseif include __FILE__ empty require_once do xor return parent clone use __CLASS__ __LINE__ else break print eval new catch __METHOD__ case exception default die require __FUNCTION__ enddeclare final try switch continue endfor endif declare unset true false trait goto instanceof insteadof __DIR__ __NAMESPACE__ yield finally int uint long ulong char uchar double float bool boolean stringlikely unlikely",c:[e.CLCM,e.HCM,e.C("/\\*","\\*/",{c:[{cN:"doctag",b:"@[A-Za-z]+"}]}),e.C("__halt_compiler.+?;",!1,{eW:!0,k:"__halt_compiler",l:e.UIR}),{cN:"string",b:"<<<['\"]?\\w+['\"]?$",e:"^\\w+;",c:[e.BE]},{b:/(::|->)+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/},{cN:"function",bK:"function",e:/[;{]/,eE:!0,i:"\\$|\\[|%",c:[e.UTM,{cN:"params",b:"\\(",e:"\\)",c:["self",e.CBCM,t,r]}]},{cN:"class",bK:"class interface",e:"{",eE:!0,i:/[:\(\$"]/,c:[{bK:"extends implements"},e.UTM]},{bK:"namespace",e:";",i:/[\.']/,c:[e.UTM]},{bK:"use",e:";",c:[e.UTM]},{b:"=>"},t,r]}}),e});
/*! url - v1.8.6 - 2013-11-22 */window.url=function(){function a(a){return!isNaN(parseFloat(a))&&isFinite(a)}return function(b,c){var d=c||window.location.toString();if(!b)return d;b=b.toString(),"//"===d.substring(0,2)?d="http:"+d:1===d.split("://").length&&(d="http://"+d),c=d.split("/");var e={auth:""},f=c[2].split("@");1===f.length?f=f[0].split(":"):(e.auth=f[0],f=f[1].split(":")),e.protocol=c[0],e.hostname=f[0],e.port=f[1]||("https"===e.protocol.split(":")[0].toLowerCase()?"443":"80"),e.pathname=(c.length>3?"/":"")+c.slice(3,c.length).join("/").split("?")[0].split("#")[0];var g=e.pathname;"/"===g.charAt(g.length-1)&&(g=g.substring(0,g.length-1));var h=e.hostname,i=h.split("."),j=g.split("/");if("hostname"===b)return h;if("domain"===b)return/^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/.test(h)?h:i.slice(-2).join(".");if("sub"===b)return i.slice(0,i.length-2).join(".");if("port"===b)return e.port;if("protocol"===b)return e.protocol.split(":")[0];if("auth"===b)return e.auth;if("user"===b)return e.auth.split(":")[0];if("pass"===b)return e.auth.split(":")[1]||"";if("path"===b)return e.pathname;if("."===b.charAt(0)){if(b=b.substring(1),a(b))return b=parseInt(b,10),i[0>b?i.length+b:b-1]||""}else{if(a(b))return b=parseInt(b,10),j[0>b?j.length+b:b]||"";if("file"===b)return j.slice(-1)[0];if("filename"===b)return j.slice(-1)[0].split(".")[0];if("fileext"===b)return j.slice(-1)[0].split(".")[1]||"";if("?"===b.charAt(0)||"#"===b.charAt(0)){var k=d,l=null;if("?"===b.charAt(0)?k=(k.split("?")[1]||"").split("#")[0]:"#"===b.charAt(0)&&(k=k.split("#")[1]||""),!b.charAt(1))return k;b=b.substring(1),k=k.split("&");for(var m=0,n=k.length;n>m;m++)if(l=k[m].split("="),l[0]===b)return l[1]||"";return null}}return""}}(),"undefined"!=typeof jQuery&&jQuery.extend({url:function(a,b){return window.url(a,b)}}); /*! url - v1.8.6 - 2013-11-22 */window.url=function(){function a(a){return!isNaN(parseFloat(a))&&isFinite(a)}return function(b,c){var d=c||window.location.toString();if(!b)return d;b=b.toString(),"//"===d.substring(0,2)?d="http:"+d:1===d.split("://").length&&(d="http://"+d),c=d.split("/");var e={auth:""},f=c[2].split("@");1===f.length?f=f[0].split(":"):(e.auth=f[0],f=f[1].split(":")),e.protocol=c[0],e.hostname=f[0],e.port=f[1]||("https"===e.protocol.split(":")[0].toLowerCase()?"443":"80"),e.pathname=(c.length>3?"/":"")+c.slice(3,c.length).join("/").split("?")[0].split("#")[0];var g=e.pathname;"/"===g.charAt(g.length-1)&&(g=g.substring(0,g.length-1));var h=e.hostname,i=h.split("."),j=g.split("/");if("hostname"===b)return h;if("domain"===b)return/^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/.test(h)?h:i.slice(-2).join(".");if("sub"===b)return i.slice(0,i.length-2).join(".");if("port"===b)return e.port;if("protocol"===b)return e.protocol.split(":")[0];if("auth"===b)return e.auth;if("user"===b)return e.auth.split(":")[0];if("pass"===b)return e.auth.split(":")[1]||"";if("path"===b)return e.pathname;if("."===b.charAt(0)){if(b=b.substring(1),a(b))return b=parseInt(b,10),i[0>b?i.length+b:b-1]||""}else{if(a(b))return b=parseInt(b,10),j[0>b?j.length+b:b]||"";if("file"===b)return j.slice(-1)[0];if("filename"===b)return j.slice(-1)[0].split(".")[0];if("fileext"===b)return j.slice(-1)[0].split(".")[1]||"";if("?"===b.charAt(0)||"#"===b.charAt(0)){var k=d,l=null;if("?"===b.charAt(0)?k=(k.split("?")[1]||"").split("#")[0]:"#"===b.charAt(0)&&(k=k.split("#")[1]||""),!b.charAt(1))return k;b=b.substring(1),k=k.split("&");for(var m=0,n=k.length;n>m;m++)if(l=k[m].split("="),l[0]===b)return l[1]||"";return null}}return""}}(),"undefined"!=typeof jQuery&&jQuery.extend({url:function(a,b){return window.url(a,b)}});
/* /*
* jQuery pagination plugin v1.4.1 * jQuery Bootstrap Pagination v1.4.2
* http://esimakin.github.io/twbs-pagination/ * https://github.com/josecebe/twbs-pagination
* *
* Copyright 2014-2016, Eugene Simakin * Copyright 2014-2018, Eugene Simakin <john-24@list.ru>
* Released under Apache 2.0 license * Released under Apache-2.0 license
* http://apache.org/licenses/LICENSE-2.0.html * http://apache.org/licenses/LICENSE-2.0.html
*/ */
(function(e,d,a,f){var b=e.fn.twbsPagination;var c=function(i,g){this.$element=e(i);this.options=e.extend({},e.fn.twbsPagination.defaults,g);if(this.options.startPage<1||this.options.startPage>this.options.totalPages){throw new Error("Start page option is incorrect")}this.options.totalPages=parseInt(this.options.totalPages);if(isNaN(this.options.totalPages)){throw new Error("Total pages option is not correct!")}this.options.visiblePages=parseInt(this.options.visiblePages);if(isNaN(this.options.visiblePages)){throw new Error("Visible pages option is not correct!")}if(this.options.onPageClick instanceof Function){this.$element.first().on("page",this.options.onPageClick)}if(this.options.hideOnlyOnePage&&this.options.totalPages==1){this.$element.trigger("page",1);return this}if(this.options.totalPages<this.options.visiblePages){this.options.visiblePages=this.options.totalPages}if(this.options.href){this.options.startPage=this.getPageFromQueryString();if(!this.options.startPage){this.options.startPage=1}}var h=(typeof this.$element.prop==="function")?this.$element.prop("tagName"):this.$element.attr("tagName");if(h==="UL"){this.$listContainer=this.$element}else{this.$listContainer=e("<ul></ul>")}this.$listContainer.addClass(this.options.paginationClass);if(h!=="UL"){this.$element.append(this.$listContainer)}if(this.options.initiateStartPageClick){this.show(this.options.startPage)}else{this.render(this.getPages(this.options.startPage));this.setupEvents()}return this};c.prototype={constructor:c,destroy:function(){this.$element.empty();this.$element.removeData("twbs-pagination");this.$element.off("page");return this},show:function(g){if(g<1||g>this.options.totalPages){throw new Error("Page is incorrect.")}this.currentPage=g;this.render(this.getPages(g));this.setupEvents();this.$element.trigger("page",g);return this},buildListItems:function(g){var l=[];if(this.options.first){l.push(this.buildItem("first",1))}if(this.options.prev){var k=g.currentPage>1?g.currentPage-1:this.options.loop?this.options.totalPages:1;l.push(this.buildItem("prev",k))}for(var h=0;h<g.numeric.length;h++){l.push(this.buildItem("page",g.numeric[h]))}if(this.options.next){var j=g.currentPage<this.options.totalPages?g.currentPage+1:this.options.loop?1:this.options.totalPages;l.push(this.buildItem("next",j))}if(this.options.last){l.push(this.buildItem("last",this.options.totalPages))}return l},buildItem:function(i,j){var k=e("<li></li>"),h=e("<a></a>"),g=this.options[i]?this.makeText(this.options[i],j):j;k.addClass(this.options[i+"Class"]);k.data("page",j);k.data("page-type",i);k.append(h.attr("href",this.makeHref(j)).addClass(this.options.anchorClass).html(g));return k},getPages:function(j){var g=[];var k=Math.floor(this.options.visiblePages/2);var l=j-k+1-this.options.visiblePages%2;var h=j+k;if(l<=0){l=1;h=this.options.visiblePages}if(h>this.options.totalPages){l=this.options.totalPages-this.options.visiblePages+1;h=this.options.totalPages}var i=l;while(i<=h){g.push(i);i++}return{currentPage:j,numeric:g}},render:function(g){var i=this;this.$listContainer.children().remove();var h=this.buildListItems(g);jQuery.each(h,function(j,k){i.$listContainer.append(k)});this.$listContainer.children().each(function(){var k=e(this),j=k.data("page-type");switch(j){case"page":if(k.data("page")===g.currentPage){k.addClass(i.options.activeClass)}break;case"first":k.toggleClass(i.options.disabledClass,g.currentPage===1);break;case"last":k.toggleClass(i.options.disabledClass,g.currentPage===i.options.totalPages);break;case"prev":k.toggleClass(i.options.disabledClass,!i.options.loop&&g.currentPage===1);break;case"next":k.toggleClass(i.options.disabledClass,!i.options.loop&&g.currentPage===i.options.totalPages);break;default:break}})},setupEvents:function(){var g=this;this.$listContainer.off("click").on("click","li",function(h){var i=e(this);if(i.hasClass(g.options.disabledClass)||i.hasClass(g.options.activeClass)){return false}!g.options.href&&h.preventDefault();g.show(parseInt(i.data("page")))})},makeHref:function(g){return this.options.href?this.generateQueryString(g):"#"},makeText:function(h,g){return h.replace(this.options.pageVariable,g).replace(this.options.totalPagesVariable,this.options.totalPages)},getPageFromQueryString:function(g){var h=this.getSearchString(g),i=new RegExp(this.options.pageVariable+"(=([^&#]*)|&|#|$)"),j=i.exec(h);if(!j||!j[2]){return null}j=decodeURIComponent(j[2]);j=parseInt(j);if(isNaN(j)){return null}return j},generateQueryString:function(g,h){var i=this.getSearchString(h),j=new RegExp(this.options.pageVariable+"=*[^&#]*");if(!i){return""}return"?"+i.replace(j,this.options.pageVariable+"="+g)},getSearchString:function(g){var h=g||d.location.search;if(h===""){return null}if(h.indexOf("?")===0){h=h.substr(1)}return h}};e.fn.twbsPagination=function(i){var h=Array.prototype.slice.call(arguments,1);var k;var l=e(this);var j=l.data("twbs-pagination");var g=typeof i==="object"?i:{};if(!j){l.data("twbs-pagination",(j=new c(this,g)))}if(typeof i==="string"){k=j[i].apply(j,h)}return(k===f)?l:k};e.fn.twbsPagination.defaults={totalPages:1,startPage:1,visiblePages:5,initiateStartPageClick:true,hideOnlyOnePage:false,href:false,pageVariable:"{{page}}",totalPagesVariable:"{{total_pages}}",page:null,first:"First",prev:"Previous",next:"Next",last:"Last",loop:false,onPageClick:null,paginationClass:"pagination",nextClass:"page-item next",prevClass:"page-item prev",lastClass:"page-item last",firstClass:"page-item first",pageClass:"page-item",activeClass:"active",disabledClass:"disabled",anchorClass:"page-link"};e.fn.twbsPagination.Constructor=c;e.fn.twbsPagination.noConflict=function(){e.fn.twbsPagination=b;return this};e.fn.twbsPagination.version="1.4.1"})(window.jQuery,window,document);
!function(o,e,t,s){"use strict";var i=o.fn.twbsPagination,r=function(t,s){if(this.$element=o(t),this.options=o.extend({},o.fn.twbsPagination.defaults,s),this.options.startPage<1||this.options.startPage>this.options.totalPages)throw new Error("Start page option is incorrect");if(this.options.totalPages=parseInt(this.options.totalPages),isNaN(this.options.totalPages))throw new Error("Total pages option is not correct!");if(this.options.visiblePages=parseInt(this.options.visiblePages),isNaN(this.options.visiblePages))throw new Error("Visible pages option is not correct!");if(this.options.beforePageClick instanceof Function&&this.$element.first().on("beforePage",this.options.beforePageClick),this.options.onPageClick instanceof Function&&this.$element.first().on("page",this.options.onPageClick),this.options.hideOnlyOnePage&&1==this.options.totalPages)return this.options.initiateStartPageClick&&this.$element.trigger("page",1),this;if(this.options.href&&(this.options.startPage=this.getPageFromQueryString(),this.options.startPage||(this.options.startPage=1)),"UL"===("function"==typeof this.$element.prop?this.$element.prop("tagName"):this.$element.attr("tagName")))this.$listContainer=this.$element;else{var e=this.$element,i=o([]);e.each(function(t){var s=o("<ul></ul>");o(this).append(s),i.push(s[0])}),this.$listContainer=i,this.$element=i}return this.$listContainer.addClass(this.options.paginationClass),this.options.initiateStartPageClick?this.show(this.options.startPage):(this.currentPage=this.options.startPage,this.render(this.getPages(this.options.startPage)),this.setupEvents()),this};r.prototype={constructor:r,destroy:function(){return this.$element.empty(),this.$element.removeData("twbs-pagination"),this.$element.off("page"),this},show:function(t){if(t<1||t>this.options.totalPages)throw new Error("Page is incorrect.");this.currentPage=t,this.$element.trigger("beforePage",t);var s=this.getPages(t);return this.render(s),this.setupEvents(),this.$element.trigger("page",t),s},enable:function(){this.show(this.currentPage)},disable:function(){var t=this;this.$listContainer.off("click").on("click","li",function(t){t.preventDefault()}),this.$listContainer.children().each(function(){o(this).hasClass(t.options.activeClass)||o(this).addClass(t.options.disabledClass)})},buildListItems:function(t){var s=[];if(this.options.first&&s.push(this.buildItem("first",1)),this.options.prev){var e=1<t.currentPage?t.currentPage-1:this.options.loop?this.options.totalPages:1;s.push(this.buildItem("prev",e))}for(var i=0;i<t.numeric.length;i++)s.push(this.buildItem("page",t.numeric[i]));if(this.options.next){var a=t.currentPage<this.options.totalPages?t.currentPage+1:this.options.loop?1:this.options.totalPages;s.push(this.buildItem("next",a))}return this.options.last&&s.push(this.buildItem("last",this.options.totalPages)),s},buildItem:function(t,s){var e=o("<li></li>"),i=o("<a></a>"),a=this.options[t]?this.makeText(this.options[t],s):s;return e.addClass(this.options[t+"Class"]),e.data("page",s),e.data("page-type",t),e.append(i.attr("href",this.makeHref(s)).addClass(this.options.anchorClass).html(a)),e},getPages:function(t){var s=[],e=Math.floor(this.options.visiblePages/2),i=t-e+1-this.options.visiblePages%2,a=t+e,n=this.options.visiblePages;n>this.options.totalPages&&(n=this.options.totalPages),i<=0&&(i=1,a=n),a>this.options.totalPages&&(i=this.options.totalPages-n+1,a=this.options.totalPages);for(var o=i;o<=a;)s.push(o),o++;return{currentPage:t,numeric:s}},render:function(s){var e=this;this.$listContainer.children().remove();var t=this.buildListItems(s);o.each(t,function(t,s){e.$listContainer.append(s)}),this.$listContainer.children().each(function(){var t=o(this);switch(t.data("page-type")){case"page":t.data("page")===s.currentPage&&t.addClass(e.options.activeClass);break;case"first":t.toggleClass(e.options.disabledClass,1===s.currentPage);break;case"last":t.toggleClass(e.options.disabledClass,s.currentPage===e.options.totalPages);break;case"prev":t.toggleClass(e.options.disabledClass,!e.options.loop&&1===s.currentPage);break;case"next":t.toggleClass(e.options.disabledClass,!e.options.loop&&s.currentPage===e.options.totalPages)}})},setupEvents:function(){var e=this;this.$listContainer.off("click").on("click","li",function(t){var s=o(this);if(s.hasClass(e.options.disabledClass)||s.hasClass(e.options.activeClass))return!1;!e.options.href&&t.preventDefault(),e.show(parseInt(s.data("page")))})},changeTotalPages:function(t,s){return this.options.totalPages=t,this.show(s)},makeHref:function(t){return this.options.href?this.generateQueryString(t):"#"},makeText:function(t,s){return t.replace(this.options.pageVariable,s).replace(this.options.totalPagesVariable,this.options.totalPages)},getPageFromQueryString:function(t){var s=this.getSearchString(t),e=new RegExp(this.options.pageVariable+"(=([^&#]*)|&|#|$)").exec(s);return e&&e[2]?(e=decodeURIComponent(e[2]),e=parseInt(e),isNaN(e)?null:e):null},generateQueryString:function(t,s){var e=this.getSearchString(s),i=new RegExp(this.options.pageVariable+"=*[^&#]*");return e?"?"+e.replace(i,this.options.pageVariable+"="+t):""},getSearchString:function(t){var s=t||e.location.search;return""===s?null:(0===s.indexOf("?")&&(s=s.substr(1)),s)},getCurrentPage:function(){return this.currentPage},getTotalPages:function(){return this.options.totalPages}},o.fn.twbsPagination=function(t){var s,e=Array.prototype.slice.call(arguments,1),i=o(this),a=i.data("twbs-pagination"),n="object"==typeof t?t:{};return a||i.data("twbs-pagination",a=new r(this,n)),"string"==typeof t&&(s=a[t].apply(a,e)),void 0===s?i:s},o.fn.twbsPagination.defaults={totalPages:1,startPage:1,visiblePages:5,initiateStartPageClick:!0,hideOnlyOnePage:!1,href:!1,pageVariable:"{{page}}",totalPagesVariable:"{{total_pages}}",page:null,first:"First",prev:"Previous",next:"Next",last:"Last",loop:!1,beforePageClick:null,onPageClick:null,paginationClass:"pagination",nextClass:"page-item next",prevClass:"page-item prev",lastClass:"page-item last",firstClass:"page-item first",pageClass:"page-item",activeClass:"active",disabledClass:"disabled",anchorClass:"page-link"},o.fn.twbsPagination.Constructor=r,o.fn.twbsPagination.noConflict=function(){return o.fn.twbsPagination=i,this},o.fn.twbsPagination.version="1.4.2"}(window.jQuery,window,document);
/*!*************************************************** /*!***************************************************
* mark.js v8.11.1 * mark.js v8.11.1
* https://markjs.io/ * https://markjs.io/
......
...@@ -822,6 +822,57 @@ references: ...@@ -822,6 +822,57 @@ references:
isSpec: "True" isSpec: "True"
fullName: Titanium.Web.Proxy.Exceptions.ProxyHttpException.SessionEventArgs fullName: Titanium.Web.Proxy.Exceptions.ProxyHttpException.SessionEventArgs
nameWithType: ProxyHttpException.SessionEventArgs nameWithType: ProxyHttpException.SessionEventArgs
- uid: Titanium.Web.Proxy.Helpers
name: Titanium.Web.Proxy.Helpers
href: api/Titanium.Web.Proxy.Helpers.html
commentId: N:Titanium.Web.Proxy.Helpers
fullName: Titanium.Web.Proxy.Helpers
nameWithType: Titanium.Web.Proxy.Helpers
- uid: Titanium.Web.Proxy.Helpers.RunTime
name: RunTime
href: api/Titanium.Web.Proxy.Helpers.RunTime.html
commentId: T:Titanium.Web.Proxy.Helpers.RunTime
fullName: Titanium.Web.Proxy.Helpers.RunTime
nameWithType: RunTime
- uid: Titanium.Web.Proxy.Helpers.RunTime.IsLinux
name: IsLinux
href: api/Titanium.Web.Proxy.Helpers.RunTime.html#Titanium_Web_Proxy_Helpers_RunTime_IsLinux
commentId: P:Titanium.Web.Proxy.Helpers.RunTime.IsLinux
fullName: Titanium.Web.Proxy.Helpers.RunTime.IsLinux
nameWithType: RunTime.IsLinux
- uid: Titanium.Web.Proxy.Helpers.RunTime.IsLinux*
name: IsLinux
href: api/Titanium.Web.Proxy.Helpers.RunTime.html#Titanium_Web_Proxy_Helpers_RunTime_IsLinux_
commentId: Overload:Titanium.Web.Proxy.Helpers.RunTime.IsLinux
isSpec: "True"
fullName: Titanium.Web.Proxy.Helpers.RunTime.IsLinux
nameWithType: RunTime.IsLinux
- uid: Titanium.Web.Proxy.Helpers.RunTime.IsMac
name: IsMac
href: api/Titanium.Web.Proxy.Helpers.RunTime.html#Titanium_Web_Proxy_Helpers_RunTime_IsMac
commentId: P:Titanium.Web.Proxy.Helpers.RunTime.IsMac
fullName: Titanium.Web.Proxy.Helpers.RunTime.IsMac
nameWithType: RunTime.IsMac
- uid: Titanium.Web.Proxy.Helpers.RunTime.IsMac*
name: IsMac
href: api/Titanium.Web.Proxy.Helpers.RunTime.html#Titanium_Web_Proxy_Helpers_RunTime_IsMac_
commentId: Overload:Titanium.Web.Proxy.Helpers.RunTime.IsMac
isSpec: "True"
fullName: Titanium.Web.Proxy.Helpers.RunTime.IsMac
nameWithType: RunTime.IsMac
- uid: Titanium.Web.Proxy.Helpers.RunTime.IsWindows
name: IsWindows
href: api/Titanium.Web.Proxy.Helpers.RunTime.html#Titanium_Web_Proxy_Helpers_RunTime_IsWindows
commentId: P:Titanium.Web.Proxy.Helpers.RunTime.IsWindows
fullName: Titanium.Web.Proxy.Helpers.RunTime.IsWindows
nameWithType: RunTime.IsWindows
- uid: Titanium.Web.Proxy.Helpers.RunTime.IsWindows*
name: IsWindows
href: api/Titanium.Web.Proxy.Helpers.RunTime.html#Titanium_Web_Proxy_Helpers_RunTime_IsWindows_
commentId: Overload:Titanium.Web.Proxy.Helpers.RunTime.IsWindows
isSpec: "True"
fullName: Titanium.Web.Proxy.Helpers.RunTime.IsWindows
nameWithType: RunTime.IsWindows
- uid: Titanium.Web.Proxy.Http - uid: Titanium.Web.Proxy.Http
name: Titanium.Web.Proxy.Http name: Titanium.Web.Proxy.Http
href: api/Titanium.Web.Proxy.Http.html href: api/Titanium.Web.Proxy.Http.html
...@@ -1230,6 +1281,12 @@ references: ...@@ -1230,6 +1281,12 @@ references:
commentId: F:Titanium.Web.Proxy.Http.KnownHeaders.ContentEncoding commentId: F:Titanium.Web.Proxy.Http.KnownHeaders.ContentEncoding
fullName: Titanium.Web.Proxy.Http.KnownHeaders.ContentEncoding fullName: Titanium.Web.Proxy.Http.KnownHeaders.ContentEncoding
nameWithType: KnownHeaders.ContentEncoding nameWithType: KnownHeaders.ContentEncoding
- uid: Titanium.Web.Proxy.Http.KnownHeaders.ContentEncodingBrotli
name: ContentEncodingBrotli
href: api/Titanium.Web.Proxy.Http.KnownHeaders.html#Titanium_Web_Proxy_Http_KnownHeaders_ContentEncodingBrotli
commentId: F:Titanium.Web.Proxy.Http.KnownHeaders.ContentEncodingBrotli
fullName: Titanium.Web.Proxy.Http.KnownHeaders.ContentEncodingBrotli
nameWithType: KnownHeaders.ContentEncodingBrotli
- uid: Titanium.Web.Proxy.Http.KnownHeaders.ContentEncodingDeflate - uid: Titanium.Web.Proxy.Http.KnownHeaders.ContentEncodingDeflate
name: ContentEncodingDeflate name: ContentEncodingDeflate
href: api/Titanium.Web.Proxy.Http.KnownHeaders.html#Titanium_Web_Proxy_Http_KnownHeaders_ContentEncodingDeflate href: api/Titanium.Web.Proxy.Http.KnownHeaders.html#Titanium_Web_Proxy_Http_KnownHeaders_ContentEncodingDeflate
...@@ -2828,6 +2885,19 @@ references: ...@@ -2828,6 +2885,19 @@ references:
isSpec: "True" isSpec: "True"
fullName: Titanium.Web.Proxy.ProxyServer.EnableConnectionPool fullName: Titanium.Web.Proxy.ProxyServer.EnableConnectionPool
nameWithType: ProxyServer.EnableConnectionPool nameWithType: ProxyServer.EnableConnectionPool
- uid: Titanium.Web.Proxy.ProxyServer.EnableTcpServerConnectionPrefetch
name: EnableTcpServerConnectionPrefetch
href: api/Titanium.Web.Proxy.ProxyServer.html#Titanium_Web_Proxy_ProxyServer_EnableTcpServerConnectionPrefetch
commentId: P:Titanium.Web.Proxy.ProxyServer.EnableTcpServerConnectionPrefetch
fullName: Titanium.Web.Proxy.ProxyServer.EnableTcpServerConnectionPrefetch
nameWithType: ProxyServer.EnableTcpServerConnectionPrefetch
- uid: Titanium.Web.Proxy.ProxyServer.EnableTcpServerConnectionPrefetch*
name: EnableTcpServerConnectionPrefetch
href: api/Titanium.Web.Proxy.ProxyServer.html#Titanium_Web_Proxy_ProxyServer_EnableTcpServerConnectionPrefetch_
commentId: Overload:Titanium.Web.Proxy.ProxyServer.EnableTcpServerConnectionPrefetch
isSpec: "True"
fullName: Titanium.Web.Proxy.ProxyServer.EnableTcpServerConnectionPrefetch
nameWithType: ProxyServer.EnableTcpServerConnectionPrefetch
- uid: Titanium.Web.Proxy.ProxyServer.EnableWinAuth - uid: Titanium.Web.Proxy.ProxyServer.EnableWinAuth
name: EnableWinAuth name: EnableWinAuth
href: api/Titanium.Web.Proxy.ProxyServer.html#Titanium_Web_Proxy_ProxyServer_EnableWinAuth href: api/Titanium.Web.Proxy.ProxyServer.html#Titanium_Web_Proxy_ProxyServer_EnableWinAuth
......
using System; using System;
using Titanium.Web.Proxy.Examples.Basic.Helpers; using Titanium.Web.Proxy.Examples.Basic.Helpers;
using Titanium.Web.Proxy.Helpers;
namespace Titanium.Web.Proxy.Examples.Basic namespace Titanium.Web.Proxy.Examples.Basic
{ {
...@@ -9,8 +10,12 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -9,8 +10,12 @@ namespace Titanium.Web.Proxy.Examples.Basic
public static void Main(string[] args) public static void Main(string[] args)
{ {
// fix console hang due to QuickEdit mode
ConsoleHelper.DisableQuickEditMode(); if (RunTime.IsWindows)
{
// fix console hang due to QuickEdit mode
ConsoleHelper.DisableQuickEditMode();
}
// Start proxy controller // Start proxy controller
controller.StartProxy(); controller.StartProxy();
......
...@@ -16,15 +16,12 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -16,15 +16,12 @@ namespace Titanium.Web.Proxy.Examples.Basic
public class ProxyTestController public class ProxyTestController
{ {
private readonly SemaphoreSlim @lock = new SemaphoreSlim(1); private readonly SemaphoreSlim @lock = new SemaphoreSlim(1);
private readonly ProxyServer proxyServer; private readonly ProxyServer proxyServer;
private ExplicitProxyEndPoint explicitEndPoint; private ExplicitProxyEndPoint explicitEndPoint;
public ProxyTestController() public ProxyTestController()
{ {
proxyServer = new ProxyServer(); proxyServer = new ProxyServer();
proxyServer.EnableConnectionPool = true;
// generate root certificate without storing it in file system // generate root certificate without storing it in file system
//proxyServer.CertificateManager.CreateRootCertificate(false); //proxyServer.CertificateManager.CreateRootCertificate(false);
...@@ -33,27 +30,13 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -33,27 +30,13 @@ namespace Titanium.Web.Proxy.Examples.Basic
proxyServer.ExceptionFunc = async exception => proxyServer.ExceptionFunc = async exception =>
{ {
await @lock.WaitAsync(); if (exception is ProxyHttpException phex)
try
{ {
var color = Console.ForegroundColor; await WriteToConsole(exception.Message + ": " + phex.InnerException?.Message, true);
Console.ForegroundColor = ConsoleColor.Red;
if (exception is ProxyHttpException phex)
{
Console.WriteLine(exception.Message + ": " + phex.InnerException?.Message);
}
else
{
Console.WriteLine(exception.Message);
}
Console.ForegroundColor = color;
} }
finally else
{ {
@lock.Release(); await WriteToConsole(exception.Message, true);
} }
}; };
proxyServer.ForwardToUpstreamGateway = true; proxyServer.ForwardToUpstreamGateway = true;
...@@ -108,13 +91,11 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -108,13 +91,11 @@ namespace Titanium.Web.Proxy.Examples.Basic
endPoint.IpAddress, endPoint.Port); endPoint.IpAddress, endPoint.Port);
} }
#if NETSTANDARD2_0 // Only explicit proxies can be set as system proxy!
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) //proxyServer.SetAsSystemHttpProxy(explicitEndPoint);
#endif //proxyServer.SetAsSystemHttpsProxy(explicitEndPoint);
if (RunTime.IsWindows)
{ {
// Only explicit proxies can be set as system proxy!
//proxyServer.SetAsSystemHttpProxy(explicitEndPoint);
//proxyServer.SetAsSystemHttpsProxy(explicitEndPoint);
proxyServer.SetAsSystemProxy(explicitEndPoint, ProxyProtocolType.AllHttp); proxyServer.SetAsSystemProxy(explicitEndPoint, ProxyProtocolType.AllHttp);
} }
} }
...@@ -280,18 +261,24 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -280,18 +261,24 @@ namespace Titanium.Web.Proxy.Examples.Basic
return Task.FromResult(0); return Task.FromResult(0);
} }
private async Task WriteToConsole(string message) private async Task WriteToConsole(string message, bool useRedColor = false)
{ {
await @lock.WaitAsync(); await @lock.WaitAsync();
try if (useRedColor)
{ {
ConsoleColor existing = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(message); Console.WriteLine(message);
Console.ForegroundColor = existing;
} }
finally else
{ {
@lock.Release(); Console.WriteLine(message);
} }
@lock.Release();
} }
///// <summary> ///// <summary>
......
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net45</TargetFrameworks>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<LangVersion>7.1</LangVersion>
<Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Titanium.Web.Proxy\Titanium.Web.Proxy.Mono.csproj" />
</ItemGroup>
</Project>
\ No newline at end of file
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFrameworks>net45;netcoreapp2.0</TargetFrameworks> <TargetFrameworks>netcoreapp2.0</TargetFrameworks>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<LangVersion>7.1</LangVersion> <LangVersion>7.1</LangVersion>
</PropertyGroup> </PropertyGroup>
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\Titanium.Web.Proxy\Titanium.Web.Proxy.csproj" /> <ProjectReference Include="..\..\src\Titanium.Web.Proxy\Titanium.Web.Proxy.NetCore.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>
\ No newline at end of file
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net45;netcoreapp2.1</TargetFrameworks>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<LangVersion>7.1</LangVersion>
<Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Titanium.Web.Proxy\Titanium.Web.Proxy.csproj" />
</ItemGroup>
</Project>
\ No newline at end of file
...@@ -50,9 +50,30 @@ ...@@ -50,9 +50,30 @@
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<LangVersion>7.1</LangVersion>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="StreamExtended, Version=1.0.179.0, Culture=neutral, PublicKeyToken=bbfa0f1d54f50043, processorArchitecture=MSIL"> <Reference Include="StreamExtended, Version=1.0.188.0, Culture=neutral, PublicKeyToken=bbfa0f1d54f50043, processorArchitecture=MSIL">
<HintPath>..\..\packages\StreamExtended.1.0.179\lib\net45\StreamExtended.dll</HintPath> <HintPath>..\..\src\packages\StreamExtended.1.0.188-beta\lib\net45\StreamExtended.dll</HintPath>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
...@@ -116,12 +137,6 @@ ...@@ -116,12 +137,6 @@
<ItemGroup> <ItemGroup>
<None Include="App.config" /> <None Include="App.config" />
</ItemGroup> </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>
<ItemGroup> <ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1"> <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible> <Visible>False</Visible>
...@@ -129,5 +144,11 @@ ...@@ -129,5 +144,11 @@
<Install>false</Install> <Install>false</Install>
</BootstrapperPackage> </BootstrapperPackage>
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Titanium.Web.Proxy\Titanium.Web.Proxy.csproj">
<Project>{91018b6d-a7a9-45be-9cb3-79cbb8b169a6}</Project>
<Name>Titanium.Web.Proxy</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project> </Project>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="StreamExtended" version="1.0.179" targetFramework="net45" /> <package id="StreamExtended" version="1.0.188-beta" targetFramework="net45" />
</packages> </packages>
\ No newline at end of file
{
"fileOptions": {
"excludeSearchPatterns": [
"**/*.sln",
"**/*.Docs.csproj",
"**/tests/",
"**/Titanium.Web.Proxy.Examples.Wpf/",
"**/*.Basic.csproj",
"**/*.Proxy.csproj",
"**/*.Mono.csproj"
]
}
}
\ No newline at end of file
...@@ -3,13 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 ...@@ -3,13 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15 # Visual Studio 15
VisualStudioVersion = 15.0.27428.1 VisualStudioVersion = 15.0.27428.1
MinimumVisualStudioVersion = 10.0.40219.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}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Titanium.Web.Proxy.Docs", "Titanium.Web.Proxy\Titanium.Web.Proxy.Docs.csproj", "{EBF2EA46-EA00-4350-BE1D-D86AFD699DB3}"
EndProject EndProject
Global Global
......

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26906.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{B6DBABDC-C985-4872-9C38-B4E5079CBC4B}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Documentation", "Documentation", "{38EA62D0-D2CB-465D-AF4F-407C5B4D4A1E}"
ProjectSection(SolutionItems) = preProject
..\LICENSE = ..\LICENSE
..\PULL_REQUEST_TEMPLATE.md = ..\PULL_REQUEST_TEMPLATE.md
..\README.md = ..\README.md
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{AC9AE37A-3059-4FDB-9A5C-363AD86F2EEF}"
ProjectSection(SolutionItems) = preProject
..\.build\build.ps1 = ..\.build\build.ps1
..\.build\docfx.json = ..\.build\docfx.json
..\.build\setup.ps1 = ..\.build\setup.ps1
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Titanium.Web.Proxy.Mono", "Titanium.Web.Proxy\Titanium.Web.Proxy.Mono.csproj", "{5985EBC2-75E8-4555-B715-B2302D879F9B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Titanium.Web.Proxy.Examples.Basic.Mono", "..\examples\Titanium.Web.Proxy.Examples.Basic\Titanium.Web.Proxy.Examples.Basic.Mono.csproj", "{9B5FA6A0-8D7C-46AD-B4F5-3AF6E2720C09}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{5985EBC2-75E8-4555-B715-B2302D879F9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5985EBC2-75E8-4555-B715-B2302D879F9B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5985EBC2-75E8-4555-B715-B2302D879F9B}.Debug|x64.ActiveCfg = Debug|Any CPU
{5985EBC2-75E8-4555-B715-B2302D879F9B}.Debug|x64.Build.0 = Debug|Any CPU
{5985EBC2-75E8-4555-B715-B2302D879F9B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5985EBC2-75E8-4555-B715-B2302D879F9B}.Release|Any CPU.Build.0 = Release|Any CPU
{5985EBC2-75E8-4555-B715-B2302D879F9B}.Release|x64.ActiveCfg = Release|Any CPU
{5985EBC2-75E8-4555-B715-B2302D879F9B}.Release|x64.Build.0 = Release|Any CPU
{9B5FA6A0-8D7C-46AD-B4F5-3AF6E2720C09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9B5FA6A0-8D7C-46AD-B4F5-3AF6E2720C09}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9B5FA6A0-8D7C-46AD-B4F5-3AF6E2720C09}.Debug|x64.ActiveCfg = Debug|Any CPU
{9B5FA6A0-8D7C-46AD-B4F5-3AF6E2720C09}.Debug|x64.Build.0 = Debug|Any CPU
{9B5FA6A0-8D7C-46AD-B4F5-3AF6E2720C09}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9B5FA6A0-8D7C-46AD-B4F5-3AF6E2720C09}.Release|Any CPU.Build.0 = Release|Any CPU
{9B5FA6A0-8D7C-46AD-B4F5-3AF6E2720C09}.Release|x64.ActiveCfg = Release|Any CPU
{9B5FA6A0-8D7C-46AD-B4F5-3AF6E2720C09}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{9B5FA6A0-8D7C-46AD-B4F5-3AF6E2720C09} = {B6DBABDC-C985-4872-9C38-B4E5079CBC4B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
EnterpriseLibraryConfigurationToolBinariesPath = .1.505.2\lib\NET35
SolutionGuid = {625C1EB5-44CF-47DE-A85A-B4C8C40ED90A}
EndGlobalSection
EndGlobal
...@@ -5,64 +5,80 @@ VisualStudioVersion = 15.0.26906.1 ...@@ -5,64 +5,80 @@ VisualStudioVersion = 15.0.26906.1
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{B6DBABDC-C985-4872-9C38-B4E5079CBC4B}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{B6DBABDC-C985-4872-9C38-B4E5079CBC4B}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Titanium.Web.Proxy", "Titanium.Web.Proxy\Titanium.Web.Proxy.csproj", "{8D73A1BE-868C-42D2-9ECE-F32CC1A02906}"
EndProject
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("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Documentation", "Documentation", "{38EA62D0-D2CB-465D-AF4F-407C5B4D4A1E}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Documentation", "Documentation", "{38EA62D0-D2CB-465D-AF4F-407C5B4D4A1E}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
LICENSE = LICENSE ..\LICENSE = ..\LICENSE
README.md = README.md ..\PULL_REQUEST_TEMPLATE.md = ..\PULL_REQUEST_TEMPLATE.md
..\README.md = ..\README.md
EndProjectSection EndProjectSection
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{AC9AE37A-3059-4FDB-9A5C-363AD86F2EEF}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{AC9AE37A-3059-4FDB-9A5C-363AD86F2EEF}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
.build\Bootstrap.ps1 = .build\Bootstrap.ps1 ..\.build\build.ps1 = ..\.build\build.ps1
.build\Common.psm1 = .build\Common.psm1 ..\.build\docfx.json = ..\.build\docfx.json
.build\default.ps1 = .build\default.ps1 ..\.build\setup.ps1 = ..\.build\setup.ps1
EndProjectSection EndProjectSection
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{BC1E0789-D348-49CF-8B67-5E99D50EDF64}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{BC1E0789-D348-49CF-8B67-5E99D50EDF64}"
EndProject 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}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Titanium.Web.Proxy", "Titanium.Web.Proxy\Titanium.Web.Proxy.csproj", "{91018B6D-A7A9-45BE-9CB3-79CBB8B169A6}"
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 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}" 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 EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Titanium.Web.Proxy.Examples.Wpf", "Examples\Titanium.Web.Proxy.Examples.Wpf\Titanium.Web.Proxy.Examples.Wpf.csproj", "{4406CE17-9A39-4F28-8363-6169A4F799C1}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Titanium.Web.Proxy.Examples.Basic", "..\examples\Titanium.Web.Proxy.Examples.Basic\Titanium.Web.Proxy.Examples.Basic.csproj", "{1FAC4205-4445-4F2B-BB8F-618E8A0C15FD}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Titanium.Web.Proxy.Examples.Basic", "Examples\Titanium.Web.Proxy.Examples.Basic\Titanium.Web.Proxy.Examples.Basic.csproj", "{9A2C6980-90D1-4082-AD60-B2428F3D6197}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Titanium.Web.Proxy.Examples.Wpf", "..\examples\Titanium.Web.Proxy.Examples.Wpf\Titanium.Web.Proxy.Examples.Wpf.csproj", "{4406CE17-9A39-4F28-8363-6169A4F799C1}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Release|Any CPU = Release|Any CPU Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8D73A1BE-868C-42D2-9ECE-F32CC1A02906}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {91018B6D-A7A9-45BE-9CB3-79CBB8B169A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8D73A1BE-868C-42D2-9ECE-F32CC1A02906}.Debug|Any CPU.Build.0 = Debug|Any CPU {91018B6D-A7A9-45BE-9CB3-79CBB8B169A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8D73A1BE-868C-42D2-9ECE-F32CC1A02906}.Release|Any CPU.ActiveCfg = Release|Any CPU {91018B6D-A7A9-45BE-9CB3-79CBB8B169A6}.Debug|x64.ActiveCfg = Debug|x64
{8D73A1BE-868C-42D2-9ECE-F32CC1A02906}.Release|Any CPU.Build.0 = Release|Any CPU {91018B6D-A7A9-45BE-9CB3-79CBB8B169A6}.Debug|x64.Build.0 = Debug|x64
{91018B6D-A7A9-45BE-9CB3-79CBB8B169A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{91018B6D-A7A9-45BE-9CB3-79CBB8B169A6}.Release|Any CPU.Build.0 = Release|Any CPU
{91018B6D-A7A9-45BE-9CB3-79CBB8B169A6}.Release|x64.ActiveCfg = Release|x64
{91018B6D-A7A9-45BE-9CB3-79CBB8B169A6}.Release|x64.Build.0 = Release|x64
{B517E3D0-D03B-436F-AB03-34BA0D5321AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B517E3D0-D03B-436F-AB03-34BA0D5321AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B517E3D0-D03B-436F-AB03-34BA0D5321AF}.Debug|Any CPU.Build.0 = Debug|Any CPU {B517E3D0-D03B-436F-AB03-34BA0D5321AF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B517E3D0-D03B-436F-AB03-34BA0D5321AF}.Debug|x64.ActiveCfg = Debug|x64
{B517E3D0-D03B-436F-AB03-34BA0D5321AF}.Debug|x64.Build.0 = Debug|x64
{B517E3D0-D03B-436F-AB03-34BA0D5321AF}.Release|Any CPU.ActiveCfg = Release|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 {B517E3D0-D03B-436F-AB03-34BA0D5321AF}.Release|Any CPU.Build.0 = Release|Any CPU
{B517E3D0-D03B-436F-AB03-34BA0D5321AF}.Release|x64.ActiveCfg = Release|x64
{B517E3D0-D03B-436F-AB03-34BA0D5321AF}.Release|x64.Build.0 = Release|x64
{32231301-B0FB-4F9E-98DF-B3E8A88F4C16}.Debug|Any CPU.ActiveCfg = Debug|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}.Debug|Any CPU.Build.0 = Debug|Any CPU
{32231301-B0FB-4F9E-98DF-B3E8A88F4C16}.Debug|x64.ActiveCfg = Debug|x64
{32231301-B0FB-4F9E-98DF-B3E8A88F4C16}.Debug|x64.Build.0 = Debug|x64
{32231301-B0FB-4F9E-98DF-B3E8A88F4C16}.Release|Any CPU.ActiveCfg = Release|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 {32231301-B0FB-4F9E-98DF-B3E8A88F4C16}.Release|Any CPU.Build.0 = Release|Any CPU
{32231301-B0FB-4F9E-98DF-B3E8A88F4C16}.Release|x64.ActiveCfg = Release|x64
{32231301-B0FB-4F9E-98DF-B3E8A88F4C16}.Release|x64.Build.0 = Release|x64
{1FAC4205-4445-4F2B-BB8F-618E8A0C15FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1FAC4205-4445-4F2B-BB8F-618E8A0C15FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1FAC4205-4445-4F2B-BB8F-618E8A0C15FD}.Debug|x64.ActiveCfg = Debug|x64
{1FAC4205-4445-4F2B-BB8F-618E8A0C15FD}.Debug|x64.Build.0 = Debug|x64
{1FAC4205-4445-4F2B-BB8F-618E8A0C15FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1FAC4205-4445-4F2B-BB8F-618E8A0C15FD}.Release|Any CPU.Build.0 = Release|Any CPU
{1FAC4205-4445-4F2B-BB8F-618E8A0C15FD}.Release|x64.ActiveCfg = Release|x64
{1FAC4205-4445-4F2B-BB8F-618E8A0C15FD}.Release|x64.Build.0 = Release|x64
{4406CE17-9A39-4F28-8363-6169A4F799C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4406CE17-9A39-4F28-8363-6169A4F799C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4406CE17-9A39-4F28-8363-6169A4F799C1}.Debug|Any CPU.Build.0 = Debug|Any CPU {4406CE17-9A39-4F28-8363-6169A4F799C1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4406CE17-9A39-4F28-8363-6169A4F799C1}.Debug|x64.ActiveCfg = Debug|x64
{4406CE17-9A39-4F28-8363-6169A4F799C1}.Debug|x64.Build.0 = Debug|x64
{4406CE17-9A39-4F28-8363-6169A4F799C1}.Release|Any CPU.ActiveCfg = Release|Any CPU {4406CE17-9A39-4F28-8363-6169A4F799C1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4406CE17-9A39-4F28-8363-6169A4F799C1}.Release|Any CPU.Build.0 = Release|Any CPU {4406CE17-9A39-4F28-8363-6169A4F799C1}.Release|Any CPU.Build.0 = Release|Any CPU
{9A2C6980-90D1-4082-AD60-B2428F3D6197}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4406CE17-9A39-4F28-8363-6169A4F799C1}.Release|x64.ActiveCfg = Release|x64
{9A2C6980-90D1-4082-AD60-B2428F3D6197}.Debug|Any CPU.Build.0 = Debug|Any CPU {4406CE17-9A39-4F28-8363-6169A4F799C1}.Release|x64.Build.0 = Release|x64
{9A2C6980-90D1-4082-AD60-B2428F3D6197}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9A2C6980-90D1-4082-AD60-B2428F3D6197}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
...@@ -70,8 +86,8 @@ Global ...@@ -70,8 +86,8 @@ Global
GlobalSection(NestedProjects) = preSolution GlobalSection(NestedProjects) = preSolution
{B517E3D0-D03B-436F-AB03-34BA0D5321AF} = {BC1E0789-D348-49CF-8B67-5E99D50EDF64} {B517E3D0-D03B-436F-AB03-34BA0D5321AF} = {BC1E0789-D348-49CF-8B67-5E99D50EDF64}
{32231301-B0FB-4F9E-98DF-B3E8A88F4C16} = {BC1E0789-D348-49CF-8B67-5E99D50EDF64} {32231301-B0FB-4F9E-98DF-B3E8A88F4C16} = {BC1E0789-D348-49CF-8B67-5E99D50EDF64}
{1FAC4205-4445-4F2B-BB8F-618E8A0C15FD} = {B6DBABDC-C985-4872-9C38-B4E5079CBC4B}
{4406CE17-9A39-4F28-8363-6169A4F799C1} = {B6DBABDC-C985-4872-9C38-B4E5079CBC4B} {4406CE17-9A39-4F28-8363-6169A4F799C1} = {B6DBABDC-C985-4872-9C38-B4E5079CBC4B}
{9A2C6980-90D1-4082-AD60-B2428F3D6197} = {B6DBABDC-C985-4872-9C38-B4E5079CBC4B}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
EnterpriseLibraryConfigurationToolBinariesPath = .1.505.2\lib\NET35 EnterpriseLibraryConfigurationToolBinariesPath = .1.505.2\lib\NET35
......
using System; using System;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.Compression namespace Titanium.Web.Proxy.Compression
...@@ -18,6 +19,8 @@ namespace Titanium.Web.Proxy.Compression ...@@ -18,6 +19,8 @@ namespace Titanium.Web.Proxy.Compression
return new GZipStream(stream, CompressionMode.Compress, leaveOpen); return new GZipStream(stream, CompressionMode.Compress, leaveOpen);
case KnownHeaders.ContentEncodingDeflate: case KnownHeaders.ContentEncodingDeflate:
return new DeflateStream(stream, CompressionMode.Compress, leaveOpen); return new DeflateStream(stream, CompressionMode.Compress, leaveOpen);
case KnownHeaders.ContentEncodingBrotli:
return new BrotliSharpLib.BrotliStream(stream, CompressionMode.Compress, leaveOpen);
default: default:
throw new Exception($"Unsupported compression mode: {type}"); throw new Exception($"Unsupported compression mode: {type}");
} }
......
using System; using System;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.Compression namespace Titanium.Web.Proxy.Compression
...@@ -18,6 +19,8 @@ namespace Titanium.Web.Proxy.Compression ...@@ -18,6 +19,8 @@ namespace Titanium.Web.Proxy.Compression
return new GZipStream(stream, CompressionMode.Decompress, leaveOpen); return new GZipStream(stream, CompressionMode.Decompress, leaveOpen);
case KnownHeaders.ContentEncodingDeflate: case KnownHeaders.ContentEncodingDeflate:
return new DeflateStream(stream, CompressionMode.Decompress, leaveOpen); return new DeflateStream(stream, CompressionMode.Decompress, leaveOpen);
case KnownHeaders.ContentEncodingBrotli:
return new BrotliSharpLib.BrotliStream(stream, CompressionMode.Decompress, leaveOpen);
default: default:
throw new Exception($"Unsupported decompression mode: {type}"); throw new Exception($"Unsupported decompression mode: {type}");
} }
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended.Network; using StreamExtended.Network;
using Titanium.Web.Proxy.Compression; using Titanium.Web.Proxy.Compression;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Http.Responses; using Titanium.Web.Proxy.Http.Responses;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.EventArguments namespace Titanium.Web.Proxy.EventArguments
{ {
/// <summary> /// <summary>
/// Holds info related to a single proxy session (single request/response sequence). /// 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 is bounded to a single connection from client.
/// A proxy session ends when client terminates connection to proxy /// A proxy session ends when client terminates connection to proxy
/// or when server terminates connection from proxy. /// or when server terminates connection from proxy.
/// </summary> /// </summary>
public class SessionEventArgs : SessionEventArgsBase public class SessionEventArgs : SessionEventArgsBase
{ {
private static readonly byte[] emptyData = new byte[0]; private static readonly byte[] emptyData = new byte[0];
/// <summary> /// <summary>
/// Backing field for corresponding public property /// Backing field for corresponding public property
/// </summary> /// </summary>
private bool reRequest; private bool reRequest;
/// <summary> /// <summary>
/// Constructor to initialize the proxy /// Constructor to initialize the proxy
/// </summary> /// </summary>
internal SessionEventArgs(ProxyServer server, ProxyEndPoint endPoint, internal SessionEventArgs(ProxyServer server, ProxyEndPoint endPoint,
CancellationTokenSource cancellationTokenSource) CancellationTokenSource cancellationTokenSource)
: this(server, endPoint, null, cancellationTokenSource) : this(server, endPoint, null, cancellationTokenSource)
{ {
} }
protected SessionEventArgs(ProxyServer server, ProxyEndPoint endPoint, protected SessionEventArgs(ProxyServer server, ProxyEndPoint endPoint,
Request request, CancellationTokenSource cancellationTokenSource) Request request, CancellationTokenSource cancellationTokenSource)
: base(server, endPoint, cancellationTokenSource, request) : base(server, endPoint, cancellationTokenSource, request)
{ {
} }
private bool hasMulipartEventSubscribers => MultipartRequestPartSent != null; private bool hasMulipartEventSubscribers => MultipartRequestPartSent != null;
/// <summary> /// <summary>
/// Should we send the request again ? /// Should we send the request again ?
/// </summary> /// </summary>
public bool ReRequest public bool ReRequest
{ {
get => reRequest; get => reRequest;
set set
{ {
if (WebSession.Response.StatusCode == 0) if (WebSession.Response.StatusCode == 0)
{ {
throw new Exception("Response status code is empty. Cannot request again a request " + "which was never send to server."); throw new Exception("Response status code is empty. Cannot request again a request " + "which was never send to server.");
} }
reRequest = value; reRequest = value;
} }
} }
/// <summary> /// <summary>
/// Occurs when multipart request part sent. /// Occurs when multipart request part sent.
/// </summary> /// </summary>
public event EventHandler<MultipartRequestPartSentEventArgs> MultipartRequestPartSent; public event EventHandler<MultipartRequestPartSentEventArgs> MultipartRequestPartSent;
private ICustomStreamReader getStreamReader(bool isRequest) private ICustomStreamReader getStreamReader(bool isRequest)
{ {
return isRequest ? ProxyClient.ClientStream : WebSession.ServerConnection.Stream; return isRequest ? ProxyClient.ClientStream : WebSession.ServerConnection.Stream;
} }
private HttpWriter getStreamWriter(bool isRequest) private HttpWriter getStreamWriter(bool isRequest)
{ {
return isRequest ? (HttpWriter)ProxyClient.ClientStreamWriter : WebSession.ServerConnection.StreamWriter; return isRequest ? (HttpWriter)ProxyClient.ClientStreamWriter : WebSession.ServerConnection.StreamWriter;
} }
/// <summary> /// <summary>
/// Read request body content as bytes[] for current session /// Read request body content as bytes[] for current session
/// </summary> /// </summary>
private async Task readRequestBodyAsync(CancellationToken cancellationToken) private async Task readRequestBodyAsync(CancellationToken cancellationToken)
{ {
WebSession.Request.EnsureBodyAvailable(false); WebSession.Request.EnsureBodyAvailable(false);
var request = WebSession.Request; var request = WebSession.Request;
// If not already read (not cached yet) // If not already read (not cached yet)
if (!request.IsBodyRead) if (!request.IsBodyRead)
{ {
var body = await readBodyAsync(true, cancellationToken); var body = await readBodyAsync(true, cancellationToken);
request.Body = body; request.Body = body;
// Now set the flag to true // Now set the flag to true
// So that next time we can deliver body from cache // So that next time we can deliver body from cache
request.IsBodyRead = true; request.IsBodyRead = true;
OnDataSent(body, 0, body.Length); OnDataSent(body, 0, body.Length);
} }
} }
/// <summary> /// <summary>
/// reinit response object /// reinit response object
/// </summary> /// </summary>
internal async Task ClearResponse(CancellationToken cancellationToken) internal async Task ClearResponse(CancellationToken cancellationToken)
{ {
// syphon out the response body from server // syphon out the response body from server
await SyphonOutBodyAsync(false, cancellationToken); await SyphonOutBodyAsync(false, cancellationToken);
WebSession.Response = new Response(); WebSession.Response = new Response();
} }
internal void OnMultipartRequestPartSent(string boundary, HeaderCollection headers) internal void OnMultipartRequestPartSent(string boundary, HeaderCollection headers)
{ {
try try
{ {
MultipartRequestPartSent?.Invoke(this, new MultipartRequestPartSentEventArgs(boundary, headers)); MultipartRequestPartSent?.Invoke(this, new MultipartRequestPartSentEventArgs(boundary, headers));
} }
catch (Exception ex) catch (Exception ex)
{ {
exceptionFunc(new Exception("Exception thrown in user event", ex)); exceptionFunc(new Exception("Exception thrown in user event", ex));
} }
} }
/// <summary> /// <summary>
/// Read response body as byte[] for current response /// Read response body as byte[] for current response
/// </summary> /// </summary>
private async Task readResponseBodyAsync(CancellationToken cancellationToken) private async Task readResponseBodyAsync(CancellationToken cancellationToken)
{ {
if (!WebSession.Request.Locked) if (!WebSession.Request.Locked)
{ {
throw new Exception("You cannot read the response body before request is made to server."); throw new Exception("You cannot read the response body before request is made to server.");
} }
var response = WebSession.Response; var response = WebSession.Response;
if (!response.HasBody) if (!response.HasBody)
{ {
return; return;
} }
// If not already read (not cached yet) // If not already read (not cached yet)
if (!response.IsBodyRead) if (!response.IsBodyRead)
{ {
var body = await readBodyAsync(false, cancellationToken); var body = await readBodyAsync(false, cancellationToken);
response.Body = body; response.Body = body;
// Now set the flag to true // Now set the flag to true
// So that next time we can deliver body from cache // So that next time we can deliver body from cache
response.IsBodyRead = true; response.IsBodyRead = true;
OnDataReceived(body, 0, body.Length); OnDataReceived(body, 0, body.Length);
} }
} }
private async Task<byte[]> readBodyAsync(bool isRequest, CancellationToken cancellationToken) private async Task<byte[]> readBodyAsync(bool isRequest, CancellationToken cancellationToken)
{ {
using (var bodyStream = new MemoryStream()) using (var bodyStream = new MemoryStream())
{ {
var writer = new HttpWriter(bodyStream, bufferPool, bufferSize); var writer = new HttpWriter(bodyStream, bufferPool, bufferSize);
if (isRequest) if (isRequest)
{ {
await CopyRequestBodyAsync(writer, TransformationMode.Uncompress, cancellationToken); await CopyRequestBodyAsync(writer, TransformationMode.Uncompress, cancellationToken);
} }
else else
{ {
await CopyResponseBodyAsync(writer, TransformationMode.Uncompress, cancellationToken); await CopyResponseBodyAsync(writer, TransformationMode.Uncompress, cancellationToken);
} }
return bodyStream.ToArray(); return bodyStream.ToArray();
} }
} }
/// <summary> /// <summary>
/// Syphon out any left over data in given request/response from backing tcp connection. /// Syphon out any left over data in given request/response from backing tcp connection.
/// When user modifies the response/request we need to do this to reuse tcp connections. /// When user modifies the response/request we need to do this to reuse tcp connections.
/// </summary> /// </summary>
/// <param name="isRequest"></param> /// <param name="isRequest"></param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
internal async Task SyphonOutBodyAsync(bool isRequest, CancellationToken cancellationToken) internal async Task SyphonOutBodyAsync(bool isRequest, CancellationToken cancellationToken)
{ {
var requestResponse = isRequest ? (RequestResponseBase)WebSession.Request : WebSession.Response; var requestResponse = isRequest ? (RequestResponseBase)WebSession.Request : WebSession.Response;
if (requestResponse.OriginalIsBodyRead || !requestResponse.OriginalHasBody) if (requestResponse.OriginalIsBodyRead || !requestResponse.OriginalHasBody)
{ {
return; return;
} }
using (var bodyStream = new MemoryStream()) using (var bodyStream = new MemoryStream())
{ {
var writer = new HttpWriter(bodyStream, bufferPool, bufferSize); var writer = new HttpWriter(bodyStream, bufferPool, bufferSize);
await copyBodyAsync(isRequest, true, writer, TransformationMode.None, null, cancellationToken); await copyBodyAsync(isRequest, true, writer, TransformationMode.None, null, cancellationToken);
} }
} }
/// <summary> /// <summary>
/// This is called when the request is PUT/POST/PATCH to read the body /// This is called when the request is PUT/POST/PATCH to read the body
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
internal async Task CopyRequestBodyAsync(HttpWriter writer, TransformationMode transformation, CancellationToken cancellationToken) internal async Task CopyRequestBodyAsync(HttpWriter writer, TransformationMode transformation, CancellationToken cancellationToken)
{ {
var request = WebSession.Request; var request = WebSession.Request;
long contentLength = request.ContentLength; long contentLength = request.ContentLength;
// send the request body bytes to server // send the request body bytes to server
if (contentLength > 0 && hasMulipartEventSubscribers && request.IsMultipartFormData) if (contentLength > 0 && hasMulipartEventSubscribers && request.IsMultipartFormData)
{ {
var reader = getStreamReader(true); var reader = getStreamReader(true);
string boundary = HttpHelper.GetBoundaryFromContentType(request.ContentType); string boundary = HttpHelper.GetBoundaryFromContentType(request.ContentType);
using (var copyStream = new CopyStream(reader, writer, bufferPool, bufferSize)) using (var copyStream = new CopyStream(reader, writer, bufferPool, bufferSize))
{ {
while (contentLength > copyStream.ReadBytes) while (contentLength > copyStream.ReadBytes)
{ {
long read = await readUntilBoundaryAsync(copyStream, contentLength, boundary, cancellationToken); long read = await readUntilBoundaryAsync(copyStream, contentLength, boundary, cancellationToken);
if (read == 0) if (read == 0)
{ {
break; break;
} }
if (contentLength > copyStream.ReadBytes) if (contentLength > copyStream.ReadBytes)
{ {
var headers = new HeaderCollection(); var headers = new HeaderCollection();
await HeaderParser.ReadHeaders(copyStream, headers, cancellationToken); await HeaderParser.ReadHeaders(copyStream, headers, cancellationToken);
OnMultipartRequestPartSent(boundary, headers); OnMultipartRequestPartSent(boundary, headers);
} }
} }
await copyStream.FlushAsync(cancellationToken); await copyStream.FlushAsync(cancellationToken);
} }
} }
else else
{ {
await copyBodyAsync(true, false, writer, transformation, OnDataSent, cancellationToken); await copyBodyAsync(true, false, writer, transformation, OnDataSent, cancellationToken);
} }
} }
internal async Task CopyResponseBodyAsync(HttpWriter writer, TransformationMode transformation, CancellationToken cancellationToken) internal async Task CopyResponseBodyAsync(HttpWriter writer, TransformationMode transformation, CancellationToken cancellationToken)
{ {
await copyBodyAsync(false, false, writer, transformation, OnDataReceived, cancellationToken); await copyBodyAsync(false, false, writer, transformation, OnDataReceived, cancellationToken);
} }
private async Task copyBodyAsync(bool isRequest, bool useOriginalHeaderValues, HttpWriter writer, TransformationMode transformation, Action<byte[], int, int> onCopy, CancellationToken cancellationToken) private async Task copyBodyAsync(bool isRequest, bool useOriginalHeaderValues, HttpWriter writer, TransformationMode transformation, Action<byte[], int, int> onCopy, CancellationToken cancellationToken)
{ {
var stream = getStreamReader(isRequest); var stream = getStreamReader(isRequest);
var requestResponse = isRequest ? (RequestResponseBase)WebSession.Request : WebSession.Response; var requestResponse = isRequest ? (RequestResponseBase)WebSession.Request : WebSession.Response;
bool isChunked = useOriginalHeaderValues? requestResponse.OriginalIsChunked : requestResponse.IsChunked; bool isChunked = useOriginalHeaderValues? requestResponse.OriginalIsChunked : requestResponse.IsChunked;
long contentLength = useOriginalHeaderValues ? requestResponse.OriginalContentLength : requestResponse.ContentLength; long contentLength = useOriginalHeaderValues ? requestResponse.OriginalContentLength : requestResponse.ContentLength;
if (transformation == TransformationMode.None) if (transformation == TransformationMode.None)
{ {
await writer.CopyBodyAsync(stream, isChunked, contentLength, onCopy, cancellationToken); await writer.CopyBodyAsync(stream, isChunked, contentLength, onCopy, cancellationToken);
return; return;
} }
LimitedStream limitedStream; LimitedStream limitedStream;
Stream decompressStream = null; Stream decompressStream = null;
string contentEncoding = useOriginalHeaderValues ? requestResponse.OriginalContentEncoding : requestResponse.ContentEncoding; string contentEncoding = useOriginalHeaderValues ? requestResponse.OriginalContentEncoding : requestResponse.ContentEncoding;
Stream s = limitedStream = new LimitedStream(stream, bufferPool, isChunked, contentLength); Stream s = limitedStream = new LimitedStream(stream, bufferPool, isChunked, contentLength);
if (transformation == TransformationMode.Uncompress && contentEncoding != null) if (transformation == TransformationMode.Uncompress && contentEncoding != null)
{ {
s = decompressStream = DecompressionFactory.Create(contentEncoding, s); s = decompressStream = DecompressionFactory.Create(contentEncoding, s);
} }
try try
{ {
using (var bufStream = new CustomBufferedStream(s, bufferPool, bufferSize, true)) using (var bufStream = new CustomBufferedStream(s, bufferPool, bufferSize, true))
{ {
await writer.CopyBodyAsync(bufStream, false, -1, onCopy, cancellationToken); await writer.CopyBodyAsync(bufStream, false, -1, onCopy, cancellationToken);
} }
} }
finally finally
{ {
decompressStream?.Dispose(); decompressStream?.Dispose();
await limitedStream.Finish(); await limitedStream.Finish();
limitedStream.Dispose(); limitedStream.Dispose();
} }
} }
/// <summary> /// <summary>
/// Read a line from the byte stream /// Read a line from the byte stream
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
private async Task<long> readUntilBoundaryAsync(ICustomStreamReader reader, long totalBytesToRead, string boundary, CancellationToken cancellationToken) private async Task<long> readUntilBoundaryAsync(ICustomStreamReader reader, long totalBytesToRead, string boundary, CancellationToken cancellationToken)
{ {
int bufferDataLength = 0; int bufferDataLength = 0;
var buffer = bufferPool.GetBuffer(bufferSize); var buffer = bufferPool.GetBuffer(bufferSize);
try try
{ {
int boundaryLength = boundary.Length + 4; int boundaryLength = boundary.Length + 4;
long bytesRead = 0; long bytesRead = 0;
while (bytesRead < totalBytesToRead && (reader.DataAvailable || await reader.FillBufferAsync(cancellationToken))) while (bytesRead < totalBytesToRead && (reader.DataAvailable || await reader.FillBufferAsync(cancellationToken)))
{ {
byte newChar = reader.ReadByteFromBuffer(); byte newChar = reader.ReadByteFromBuffer();
buffer[bufferDataLength] = newChar; buffer[bufferDataLength] = newChar;
bufferDataLength++; bufferDataLength++;
bytesRead++; bytesRead++;
if (bufferDataLength >= boundaryLength) if (bufferDataLength >= boundaryLength)
{ {
int startIdx = bufferDataLength - boundaryLength; int startIdx = bufferDataLength - boundaryLength;
if (buffer[startIdx] == '-' && buffer[startIdx + 1] == '-') if (buffer[startIdx] == '-' && buffer[startIdx + 1] == '-')
{ {
startIdx += 2; startIdx += 2;
bool ok = true; bool ok = true;
for (int i = 0; i < boundary.Length; i++) for (int i = 0; i < boundary.Length; i++)
{ {
if (buffer[startIdx + i] != boundary[i]) if (buffer[startIdx + i] != boundary[i])
{ {
ok = false; ok = false;
break; break;
} }
} }
if (ok) if (ok)
{ {
break; break;
} }
} }
} }
if (bufferDataLength == buffer.Length) if (bufferDataLength == buffer.Length)
{ {
// boundary is not longer than 70 bytes according to the specification, so keeping the last 100 (minimum 74) bytes is enough // boundary is not longer than 70 bytes according to the specification, so keeping the last 100 (minimum 74) bytes is enough
const int bytesToKeep = 100; const int bytesToKeep = 100;
Buffer.BlockCopy(buffer, buffer.Length - bytesToKeep, buffer, 0, bytesToKeep); Buffer.BlockCopy(buffer, buffer.Length - bytesToKeep, buffer, 0, bytesToKeep);
bufferDataLength = bytesToKeep; bufferDataLength = bytesToKeep;
} }
} }
return bytesRead; return bytesRead;
} }
finally finally
{ {
bufferPool.ReturnBuffer(buffer); bufferPool.ReturnBuffer(buffer);
} }
} }
/// <summary> /// <summary>
/// Gets the request body as bytes. /// Gets the request body as bytes.
/// </summary> /// </summary>
/// <param name="cancellationToken">Optional cancellation token for this async task.</param> /// <param name="cancellationToken">Optional cancellation token for this async task.</param>
/// <returns>The body as bytes.</returns> /// <returns>The body as bytes.</returns>
public async Task<byte[]> GetRequestBody(CancellationToken cancellationToken = default) public async Task<byte[]> GetRequestBody(CancellationToken cancellationToken = default)
{ {
if (!WebSession.Request.IsBodyRead) if (!WebSession.Request.IsBodyRead)
{ {
await readRequestBodyAsync(cancellationToken); await readRequestBodyAsync(cancellationToken);
} }
return WebSession.Request.Body; return WebSession.Request.Body;
} }
/// <summary> /// <summary>
/// Gets the request body as string. /// Gets the request body as string.
/// </summary> /// </summary>
/// <param name="cancellationToken">Optional cancellation token for this async task.</param> /// <param name="cancellationToken">Optional cancellation token for this async task.</param>
/// <returns>The body as string.</returns> /// <returns>The body as string.</returns>
public async Task<string> GetRequestBodyAsString(CancellationToken cancellationToken = default) public async Task<string> GetRequestBodyAsString(CancellationToken cancellationToken = default)
{ {
if (!WebSession.Request.IsBodyRead) if (!WebSession.Request.IsBodyRead)
{ {
await readRequestBodyAsync(cancellationToken); await readRequestBodyAsync(cancellationToken);
} }
return WebSession.Request.BodyString; return WebSession.Request.BodyString;
} }
/// <summary> /// <summary>
/// Sets the request body. /// Sets the request body.
/// </summary> /// </summary>
/// <param name="body">The request body bytes.</param> /// <param name="body">The request body bytes.</param>
public void SetRequestBody(byte[] body) public void SetRequestBody(byte[] body)
{ {
var request = WebSession.Request; var request = WebSession.Request;
if (request.Locked) if (request.Locked)
{ {
throw new Exception("You cannot call this function after request is made to server."); throw new Exception("You cannot call this function after request is made to server.");
} }
request.Body = body; request.Body = body;
} }
/// <summary> /// <summary>
/// Sets the body with the specified string. /// Sets the body with the specified string.
/// </summary> /// </summary>
/// <param name="body">The request body string to set.</param> /// <param name="body">The request body string to set.</param>
public void SetRequestBodyString(string body) public void SetRequestBodyString(string body)
{ {
if (WebSession.Request.Locked) if (WebSession.Request.Locked)
{ {
throw new Exception("You cannot call this function after request is made to server."); throw new Exception("You cannot call this function after request is made to server.");
} }
SetRequestBody(WebSession.Request.Encoding.GetBytes(body)); SetRequestBody(WebSession.Request.Encoding.GetBytes(body));
} }
/// <summary> /// <summary>
/// Gets the response body as bytes. /// Gets the response body as bytes.
/// </summary> /// </summary>
/// <param name="cancellationToken">Optional cancellation token for this async task.</param> /// <param name="cancellationToken">Optional cancellation token for this async task.</param>
/// <returns>The resulting bytes.</returns> /// <returns>The resulting bytes.</returns>
public async Task<byte[]> GetResponseBody(CancellationToken cancellationToken = default) public async Task<byte[]> GetResponseBody(CancellationToken cancellationToken = default)
{ {
if (!WebSession.Response.IsBodyRead) if (!WebSession.Response.IsBodyRead)
{ {
await readResponseBodyAsync(cancellationToken); await readResponseBodyAsync(cancellationToken);
} }
return WebSession.Response.Body; return WebSession.Response.Body;
} }
/// <summary> /// <summary>
/// Gets the response body as string. /// Gets the response body as string.
/// </summary> /// </summary>
/// <param name="cancellationToken">Optional cancellation token for this async task.</param> /// <param name="cancellationToken">Optional cancellation token for this async task.</param>
/// <returns>The string body.</returns> /// <returns>The string body.</returns>
public async Task<string> GetResponseBodyAsString(CancellationToken cancellationToken = default) public async Task<string> GetResponseBodyAsString(CancellationToken cancellationToken = default)
{ {
if (!WebSession.Response.IsBodyRead) if (!WebSession.Response.IsBodyRead)
{ {
await readResponseBodyAsync(cancellationToken); await readResponseBodyAsync(cancellationToken);
} }
return WebSession.Response.BodyString; return WebSession.Response.BodyString;
} }
/// <summary> /// <summary>
/// Set the response body bytes. /// Set the response body bytes.
/// </summary> /// </summary>
/// <param name="body">The body bytes to set.</param> /// <param name="body">The body bytes to set.</param>
public void SetResponseBody(byte[] body) public void SetResponseBody(byte[] body)
{ {
if (!WebSession.Request.Locked) if (!WebSession.Request.Locked)
{ {
throw new Exception("You cannot call this function before request is made to server."); throw new Exception("You cannot call this function before request is made to server.");
} }
var response = WebSession.Response; var response = WebSession.Response;
response.Body = body; response.Body = body;
} }
/// <summary> /// <summary>
/// Replace the response body with the specified string. /// Replace the response body with the specified string.
/// </summary> /// </summary>
/// <param name="body">The body string to set.</param> /// <param name="body">The body string to set.</param>
public void SetResponseBodyString(string body) public void SetResponseBodyString(string body)
{ {
if (!WebSession.Request.Locked) if (!WebSession.Request.Locked)
{ {
throw new Exception("You cannot call this function before request is made to server."); throw new Exception("You cannot call this function before request is made to server.");
} }
var bodyBytes = WebSession.Response.Encoding.GetBytes(body); var bodyBytes = WebSession.Response.Encoding.GetBytes(body);
SetResponseBody(bodyBytes); SetResponseBody(bodyBytes);
} }
/// <summary> /// <summary>
/// Before request is made to server respond with the specified HTML string to client /// Before request is made to server respond with the specified HTML string to client
/// and ignore the request. /// and ignore the request.
/// </summary> /// </summary>
/// <param name="html">HTML content to sent.</param> /// <param name="html">HTML content to sent.</param>
/// <param name="headers">HTTP response headers.</param> /// <param name="headers">HTTP response headers.</param>
/// <param name="closeServerConnection">Close the server connection used by request if any?</param> /// <param name="closeServerConnection">Close the server connection used by request if any?</param>
public void Ok(string html, Dictionary<string, HttpHeader> headers = null, public void Ok(string html, Dictionary<string, HttpHeader> headers = null,
bool closeServerConnection = false) bool closeServerConnection = false)
{ {
var response = new OkResponse(); var response = new OkResponse();
if (headers != null) if (headers != null)
{ {
response.Headers.AddHeaders(headers); response.Headers.AddHeaders(headers);
} }
response.HttpVersion = WebSession.Request.HttpVersion; response.HttpVersion = WebSession.Request.HttpVersion;
response.Body = response.Encoding.GetBytes(html ?? string.Empty); response.Body = response.Encoding.GetBytes(html ?? string.Empty);
Respond(response, closeServerConnection); Respond(response, closeServerConnection);
} }
/// <summary> /// <summary>
/// Before request is made to server respond with the specified byte[] to client /// Before request is made to server respond with the specified byte[] to client
/// and ignore the request. /// and ignore the request.
/// </summary> /// </summary>
/// <param name="result">The html content bytes.</param> /// <param name="result">The html content bytes.</param>
/// <param name="headers">The HTTP headers.</param> /// <param name="headers">The HTTP headers.</param>
/// <param name="closeServerConnection">Close the server connection used by request if any?</param> /// <param name="closeServerConnection">Close the server connection used by request if any?</param>
public void Ok(byte[] result, Dictionary<string, HttpHeader> headers = null, public void Ok(byte[] result, Dictionary<string, HttpHeader> headers = null,
bool closeServerConnection = false) bool closeServerConnection = false)
{ {
var response = new OkResponse(); var response = new OkResponse();
response.Headers.AddHeaders(headers); response.Headers.AddHeaders(headers);
response.HttpVersion = WebSession.Request.HttpVersion; response.HttpVersion = WebSession.Request.HttpVersion;
response.Body = result; response.Body = result;
Respond(response, closeServerConnection); Respond(response, closeServerConnection);
} }
/// <summary> /// <summary>
/// Before request is made to server  /// Before request is made to server 
/// respond with the specified HTML string and the specified status to client. /// respond with the specified HTML string and the specified status to client.
/// And then ignore the request.  /// And then ignore the request. 
/// </summary> /// </summary>
/// <param name="html">The html content.</param> /// <param name="html">The html content.</param>
/// <param name="status">The HTTP status code.</param> /// <param name="status">The HTTP status code.</param>
/// <param name="headers">The HTTP headers.</param> /// <param name="headers">The HTTP headers.</param>
/// <param name="closeServerConnection">Close the server connection used by request if any?</param> /// <param name="closeServerConnection">Close the server connection used by request if any?</param>
public void GenericResponse(string html, HttpStatusCode status, public void GenericResponse(string html, HttpStatusCode status,
Dictionary<string, HttpHeader> headers = null, bool closeServerConnection = false) Dictionary<string, HttpHeader> headers = null, bool closeServerConnection = false)
{ {
var response = new GenericResponse(status); var response = new GenericResponse(status);
response.HttpVersion = WebSession.Request.HttpVersion; response.HttpVersion = WebSession.Request.HttpVersion;
response.Headers.AddHeaders(headers); response.Headers.AddHeaders(headers);
response.Body = response.Encoding.GetBytes(html ?? string.Empty); response.Body = response.Encoding.GetBytes(html ?? string.Empty);
Respond(response, closeServerConnection); Respond(response, closeServerConnection);
} }
/// <summary> /// <summary>
/// Before request is made to server respond with the specified byte[], /// Before request is made to server respond with the specified byte[],
/// the specified status to client. And then ignore the request. /// the specified status to client. And then ignore the request.
/// </summary> /// </summary>
/// <param name="result">The bytes to sent.</param> /// <param name="result">The bytes to sent.</param>
/// <param name="status">The HTTP status code.</param> /// <param name="status">The HTTP status code.</param>
/// <param name="headers">The HTTP headers.</param> /// <param name="headers">The HTTP headers.</param>
/// <param name="closeServerConnection">Close the server connection used by request if any?</param> /// <param name="closeServerConnection">Close the server connection used by request if any?</param>
public void GenericResponse(byte[] result, HttpStatusCode status, public void GenericResponse(byte[] result, HttpStatusCode status,
Dictionary<string, HttpHeader> headers, bool closeServerConnection = false) Dictionary<string, HttpHeader> headers, bool closeServerConnection = false)
{ {
var response = new GenericResponse(status); var response = new GenericResponse(status);
response.HttpVersion = WebSession.Request.HttpVersion; response.HttpVersion = WebSession.Request.HttpVersion;
response.Headers.AddHeaders(headers); response.Headers.AddHeaders(headers);
response.Body = result; response.Body = result;
Respond(response, closeServerConnection); Respond(response, closeServerConnection);
} }
/// <summary> /// <summary>
/// Redirect to provided URL. /// Redirect to provided URL.
/// </summary> /// </summary>
/// <param name="url">The URL to redirect.</param> /// <param name="url">The URL to redirect.</param>
/// <param name="closeServerConnection">Close the server connection used by request if any?</param> /// <param name="closeServerConnection">Close the server connection used by request if any?</param>
public void Redirect(string url, bool closeServerConnection = false) public void Redirect(string url, bool closeServerConnection = false)
{ {
var response = new RedirectResponse(); var response = new RedirectResponse();
response.HttpVersion = WebSession.Request.HttpVersion; response.HttpVersion = WebSession.Request.HttpVersion;
response.Headers.AddHeader(KnownHeaders.Location, url); response.Headers.AddHeader(KnownHeaders.Location, url);
response.Body = emptyData; response.Body = emptyData;
Respond(response, closeServerConnection); Respond(response, closeServerConnection);
} }
/// <summary> /// <summary>
/// Respond with given response object to client. /// Respond with given response object to client.
/// </summary> /// </summary>
/// <param name="response">The response object.</param> /// <param name="response">The response object.</param>
/// <param name="closeServerConnection">Close the server connection used by request if any?</param> /// <param name="closeServerConnection">Close the server connection used by request if any?</param>
public void Respond(Response response, bool closeServerConnection = false) public void Respond(Response response, bool closeServerConnection = false)
{ {
//request already send/ready to be sent. //request already send/ready to be sent.
if (WebSession.Request.Locked) if (WebSession.Request.Locked)
{ {
//response already received from server and ready to be sent to client. //response already received from server and ready to be sent to client.
if (WebSession.Response.Locked) if (WebSession.Response.Locked)
{ {
throw new Exception("You cannot call this function after response is sent to the client."); throw new Exception("You cannot call this function after response is sent to the client.");
} }
//cleanup original response. //cleanup original response.
if (closeServerConnection) if (closeServerConnection)
{ {
//no need to cleanup original connection. //no need to cleanup original connection.
//it will be closed any way. //it will be closed any way.
TerminateServerConnection(); TerminateServerConnection();
} }
response.SetOriginalHeaders(WebSession.Response); response.SetOriginalHeaders(WebSession.Response);
//response already received from server but not yet ready to sent to client. //response already received from server but not yet ready to sent to client.
WebSession.Response = response; WebSession.Response = response;
WebSession.Response.Locked = true; WebSession.Response.Locked = true;
} }
//request not yet sent/not yet ready to be sent. //request not yet sent/not yet ready to be sent.
else else
{ {
WebSession.Request.Locked = true; WebSession.Request.Locked = true;
WebSession.Request.CancelRequest = true; WebSession.Request.CancelRequest = true;
//set new response. //set new response.
WebSession.Response = response; WebSession.Response = response;
WebSession.Response.Locked = true; WebSession.Response.Locked = true;
} }
} }
/// <summary> /// <summary>
/// Terminate the connection to server at the end of this HTTP request/response session. /// Terminate the connection to server at the end of this HTTP request/response session.
/// </summary> /// </summary>
public void TerminateServerConnection() public void TerminateServerConnection()
{ {
WebSession.CloseServerConnection = true; WebSession.CloseServerConnection = true;
} }
/// <summary> /// <summary>
/// Implement any cleanup here /// Implement any cleanup here
/// </summary> /// </summary>
public override void Dispose() public override void Dispose()
{ {
MultipartRequestPartSent = null; MultipartRequestPartSent = null;
base.Dispose(); base.Dispose();
} }
} }
} }
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Exceptions namespace Titanium.Web.Proxy.Exceptions
{ {
/// <summary> /// <summary>
/// Proxy authorization exception. /// Proxy authorization exception.
/// </summary> /// </summary>
public class ProxyAuthorizationException : ProxyException public class ProxyAuthorizationException : ProxyException
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ProxyAuthorizationException" /> class. /// Initializes a new instance of the <see cref="ProxyAuthorizationException" /> class.
/// </summary> /// </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="session">The <see cref="SessionEventArgs" /> instance containing the event data.</param>
/// <param name="innerException">Inner exception associated to upstream proxy authorization</param> /// <param name="innerException">Inner exception associated to upstream proxy authorization</param>
/// <param name="headers">Http's headers associated</param> /// <param name="headers">Http's headers associated</param>
internal ProxyAuthorizationException(string message, SessionEventArgsBase session, Exception innerException, internal ProxyAuthorizationException(string message, SessionEventArgsBase session, Exception innerException,
IEnumerable<HttpHeader> headers) : base(message, innerException) IEnumerable<HttpHeader> headers) : base(message, innerException)
{ {
Session = session; Session = session;
Headers = headers; Headers = headers;
} }
/// <summary> /// <summary>
/// The current session within which this error happened. /// The current session within which this error happened.
/// </summary> /// </summary>
public SessionEventArgsBase Session { get; } public SessionEventArgsBase Session { get; }
/// <summary> /// <summary>
/// Headers associated with the authorization exception. /// Headers associated with the authorization exception.
/// </summary> /// </summary>
public IEnumerable<HttpHeader> Headers { get; } public IEnumerable<HttpHeader> Headers { get; }
} }
} }
using System; using System;
namespace Titanium.Web.Proxy.Exceptions namespace Titanium.Web.Proxy.Exceptions
{ {
/// <summary> /// <summary>
/// Base class exception associated with this proxy server. /// Base class exception associated with this proxy server.
/// </summary> /// </summary>
public abstract class ProxyException : Exception public abstract class ProxyException : Exception
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ProxyException" /> class. /// Initializes a new instance of the <see cref="ProxyException" /> class.
/// - must be invoked by derived classes' constructors /// - must be invoked by derived classes' constructors
/// </summary> /// </summary>
/// <param name="message">Exception message</param> /// <param name="message">Exception message</param>
protected ProxyException(string message) : base(message) protected ProxyException(string message) : base(message)
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ProxyException" /> class. /// Initializes a new instance of the <see cref="ProxyException" /> class.
/// - must be invoked by derived classes' constructors /// - must be invoked by derived classes' constructors
/// </summary> /// </summary>
/// <param name="message">Excception message</param> /// <param name="message">Excception message</param>
/// <param name="innerException">Inner exception associated</param> /// <param name="innerException">Inner exception associated</param>
protected ProxyException(string message, Exception innerException) : base(message, innerException) protected ProxyException(string message, Exception innerException) : base(message, innerException)
{ {
} }
} }
} }
using System; using System;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
namespace Titanium.Web.Proxy.Exceptions namespace Titanium.Web.Proxy.Exceptions
{ {
/// <summary> /// <summary>
/// Proxy HTTP exception. /// Proxy HTTP exception.
/// </summary> /// </summary>
public class ProxyHttpException : ProxyException public class ProxyHttpException : ProxyException
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ProxyHttpException" /> class. /// Initializes a new instance of the <see cref="ProxyHttpException" /> class.
/// </summary> /// </summary>
/// <param name="message">Message for this exception</param> /// <param name="message">Message for this exception</param>
/// <param name="innerException">Associated inner exception</param> /// <param name="innerException">Associated inner exception</param>
/// <param name="sessionEventArgs">Instance of <see cref="EventArguments.SessionEventArgs" /> associated to the exception</param> /// <param name="sessionEventArgs">Instance of <see cref="EventArguments.SessionEventArgs" /> associated to the exception</param>
internal ProxyHttpException(string message, Exception innerException, SessionEventArgs sessionEventArgs) : base( internal ProxyHttpException(string message, Exception innerException, SessionEventArgs sessionEventArgs) : base(
message, innerException) message, innerException)
{ {
SessionEventArgs = sessionEventArgs; SessionEventArgs = sessionEventArgs;
} }
/// <summary> /// <summary>
/// Gets session info associated to the exception. /// Gets session info associated to the exception.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// This object properties should not be edited. /// This object properties should not be edited.
/// </remarks> /// </remarks>
public SessionEventArgs SessionEventArgs { get; } public SessionEventArgs SessionEventArgs { get; }
} }
} }
...@@ -152,11 +152,14 @@ namespace Titanium.Web.Proxy ...@@ -152,11 +152,14 @@ namespace Titanium.Web.Proxy
SslStream sslStream = null; SslStream sslStream = null;
//don't pass cancellation token here if (EnableTcpServerConnectionPrefetch)
//it could cause floating server connections when client exits {
prefetchConnectionTask = tcpConnectionFactory.GetServerConnection(this, connectArgs, //don't pass cancellation token here
isConnect: true, applicationProtocols: null, noCache: false, //it could cause floating server connections when client exits
cancellationToken: CancellationToken.None); prefetchConnectionTask = tcpConnectionFactory.GetServerConnection(this, connectArgs,
isConnect: true, applicationProtocols: null, noCache: false,
cancellationToken: CancellationToken.None);
}
try try
{ {
...@@ -204,10 +207,10 @@ namespace Titanium.Web.Proxy ...@@ -204,10 +207,10 @@ namespace Titanium.Web.Proxy
decryptSsl = false; decryptSsl = false;
} }
if(!decryptSsl) if (!decryptSsl)
{ {
await tcpConnectionFactory.Release(prefetchConnectionTask, true); await tcpConnectionFactory.Release(prefetchConnectionTask, true);
prefetchConnectionTask = null; prefetchConnectionTask = null;
} }
} }
......
...@@ -16,7 +16,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -16,7 +16,7 @@ namespace Titanium.Web.Proxy.Helpers
private readonly Stream stream; private readonly Stream stream;
private readonly IBufferPool bufferPool; private readonly IBufferPool bufferPool;
private static readonly byte[] newLine = ProxyConstants.NewLine; private static readonly byte[] newLine = ProxyConstants.NewLineBytes;
private static readonly Encoder encoder = Encoding.ASCII.GetEncoder(); private static readonly Encoder encoder = Encoding.ASCII.GetEncoder();
...@@ -109,9 +109,9 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -109,9 +109,9 @@ namespace Titanium.Web.Proxy.Helpers
var headerBuilder = new StringBuilder(); var headerBuilder = new StringBuilder();
foreach (var header in headers) foreach (var header in headers)
{ {
headerBuilder.AppendLine(header.ToString()); headerBuilder.Append($"{header.ToString()}{ProxyConstants.NewLine}");
} }
headerBuilder.AppendLine(); headerBuilder.Append(ProxyConstants.NewLine);
await WriteAsync(headerBuilder.ToString(), cancellationToken); await WriteAsync(headerBuilder.ToString(), cancellationToken);
......
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
namespace Titanium.Web.Proxy.Helpers namespace Titanium.Web.Proxy.Helpers
{ {
internal class NetworkHelper internal class NetworkHelper
{ {
/// <summary> /// <summary>
/// Adapated from below link /// Adapated from below link
/// http://stackoverflow.com/questions/11834091/how-to-check-if-localhost /// http://stackoverflow.com/questions/11834091/how-to-check-if-localhost
/// </summary> /// </summary>
/// <param name="address"></param> /// <param name="address"></param>
/// <returns></returns> /// <returns></returns>
internal static bool IsLocalIpAddress(IPAddress address) internal static bool IsLocalIpAddress(IPAddress address)
{ {
if (IPAddress.IsLoopback(address)) if (IPAddress.IsLoopback(address))
{ {
return true; return true;
} }
// get local IP addresses // get local IP addresses
var localIPs = Dns.GetHostAddresses(Dns.GetHostName()); var localIPs = Dns.GetHostAddresses(Dns.GetHostName());
// test if any host IP equals to any local IP or to localhost // test if any host IP equals to any local IP or to localhost
return localIPs.Contains(address); return localIPs.Contains(address);
} }
internal static bool IsLocalIpAddress(string hostName) internal static bool IsLocalIpAddress(string hostName)
{ {
hostName = hostName.ToLower(); hostName = hostName.ToLower();
if (hostName == "127.0.0.1" if (hostName == "127.0.0.1"
|| hostName == "localhost") || hostName == "localhost")
{ {
return true; return true;
} }
var localhostDnsName = Dns.GetHostName().ToLower(); var localhostDnsName = Dns.GetHostName().ToLower();
//if hostname matches current machine DNS name //if hostname matches current machine DNS name
if (hostName == localhostDnsName) if (hostName == localhostDnsName)
{ {
return true; return true;
} }
var isLocalhost = false; var isLocalhost = false;
IPHostEntry hostEntry = null; IPHostEntry hostEntry = null;
//check if parsable to an IP Address //check if parsable to an IP Address
if (IPAddress.TryParse(hostName, out var ipAddress)) if (IPAddress.TryParse(hostName, out var ipAddress))
{ {
hostEntry = Dns.GetHostEntry(localhostDnsName); hostEntry = Dns.GetHostEntry(localhostDnsName);
isLocalhost = hostEntry.AddressList.Any(x => x.Equals(ipAddress)); isLocalhost = hostEntry.AddressList.Any(x => x.Equals(ipAddress));
} }
if (!isLocalhost) if (!isLocalhost)
{ {
try try
{ {
hostEntry = Dns.GetHostEntry(hostName); hostEntry = Dns.GetHostEntry(hostName);
isLocalhost = hostEntry.AddressList.Any(x => hostEntry.AddressList.Any(x.Equals)); isLocalhost = hostEntry.AddressList.Any(x => hostEntry.AddressList.Any(x.Equals));
} }
catch (SocketException) catch (SocketException)
{ {
} }
} }
return isLocalhost; return isLocalhost;
} }
} }
} }
using System; using System;
#if NETSTANDARD2_0 #if NETSTANDARD2_0
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
#endif #endif
namespace Titanium.Web.Proxy.Helpers namespace Titanium.Web.Proxy.Helpers
{ {
/// <summary> /// <summary>
/// Run time helpers /// Run time helpers
/// </summary> /// </summary>
internal class RunTime public static class RunTime
{ {
/// <summary> /// <summary>
/// cache for mono runtime check /// cache for mono runtime check
...@@ -16,33 +16,49 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -16,33 +16,49 @@ namespace Titanium.Web.Proxy.Helpers
/// <returns></returns> /// <returns></returns>
private static readonly Lazy<bool> isRunningOnMono = new Lazy<bool>(() => Type.GetType("Mono.Runtime") != null); private static readonly Lazy<bool> isRunningOnMono = new Lazy<bool>(() => Type.GetType("Mono.Runtime") != null);
/// <summary>
/// cache for mono runtime check
/// </summary>
/// <returns></returns>
private static readonly Lazy<bool> isRunningOnMonoLinux = new Lazy<bool>(() => IsRunningOnMono && (int)Environment.OSVersion.Platform == 4);
/// <summary>
/// cache for mono runtime check
/// </summary>
/// <returns></returns>
private static readonly Lazy<bool> isRunningOnMonoMac = new Lazy<bool>(() => IsRunningOnMono && (int)Environment.OSVersion.Platform == 6);
#if NETSTANDARD2_0 #if NETSTANDARD2_0
/// <summary> /// <summary>
/// cache for Windows platform check /// cache for Windows platform check
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
private static readonly Lazy<bool> isRunningOnWindows private static bool isRunningOnWindows => RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
= new Lazy<bool>(() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); private static bool isRunningOnLinux => RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
private static bool isRunningOnMac => RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
private static readonly Lazy<bool> isRunningOnLinux
= new Lazy<bool>(() => RuntimeInformation.IsOSPlatform(OSPlatform.Linux));
#endif #endif
/// <summary> /// <summary>
/// Is running on Mono? /// Is running on Mono?
/// </summary> /// </summary>
internal static bool IsRunningOnMono => isRunningOnMono.Value; internal static bool IsRunningOnMono => isRunningOnMono.Value;
#if NETSTANDARD2_0 #if NETSTANDARD2_0
internal static bool IsLinux => isRunningOnLinux.Value; public static bool IsLinux => isRunningOnLinux;
#else #else
internal static bool IsLinux => !IsWindows; public static bool IsLinux => isRunningOnMonoLinux.Value;
#endif #endif
#if NETSTANDARD2_0 #if NETSTANDARD2_0
internal static bool IsWindows => isRunningOnWindows.Value; public static bool IsWindows => isRunningOnWindows;
#else #else
internal static bool IsWindows => true; public static bool IsWindows => !IsLinux && !IsMac;
#endif #endif
#if NETSTANDARD2_0
public static bool IsMac => isRunningOnMac;
#else
public static bool IsMac => isRunningOnMonoMac.Value;
#endif
} }
} }
...@@ -93,112 +93,6 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -93,112 +93,6 @@ namespace Titanium.Web.Proxy.Helpers
return ((port >> 8) & 0x00FF00FFu) | ((port << 8) & 0xFF00FF00u); return ((port >> 8) & 0x00FF00FFu) | ((port << 8) & 0xFF00FF00u);
} }
/// <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
/// Asynchronous Programming Model, which does not throw exceptions when the socket is closed
/// </summary>
/// <param name="clientStream"></param>
/// <param name="serverStream"></param>
/// <param name="bufferSize"></param>
/// <param name="onDataSend"></param>
/// <param name="onDataReceive"></param>
/// <param name="cancellationTokenSource"></param>
/// <param name="exceptionFunc"></param>
/// <returns></returns>
internal static async Task SendRawApm(Stream clientStream, Stream serverStream,
IBufferPool bufferPool, int bufferSize,
Action<byte[], int, int> onDataSend, Action<byte[], int, int> onDataReceive,
CancellationTokenSource cancellationTokenSource,
ExceptionHandler exceptionFunc)
{
var taskCompletionSource = new TaskCompletionSource<bool>();
cancellationTokenSource.Token.Register(() => taskCompletionSource.TrySetResult(true));
// Now async relay all server=>client & client=>server data
var clientBuffer = bufferPool.GetBuffer(bufferSize);
var serverBuffer = bufferPool.GetBuffer(bufferSize);
try
{
beginRead(clientStream, serverStream, clientBuffer, onDataSend, cancellationTokenSource, exceptionFunc);
beginRead(serverStream, clientStream, serverBuffer, onDataReceive, cancellationTokenSource,
exceptionFunc);
await taskCompletionSource.Task;
}
finally
{
bufferPool.ReturnBuffer(clientBuffer);
bufferPool.ReturnBuffer(serverBuffer);
}
}
private static void beginRead(Stream inputStream, Stream outputStream, byte[] buffer,
Action<byte[], int, int> onCopy, CancellationTokenSource cancellationTokenSource,
ExceptionHandler exceptionFunc)
{
if (cancellationTokenSource.IsCancellationRequested)
{
return;
}
bool readFlag = false;
var readCallback = (AsyncCallback)(ar =>
{
if (cancellationTokenSource.IsCancellationRequested || readFlag)
{
return;
}
readFlag = true;
try
{
int read = inputStream.EndRead(ar);
if (read <= 0)
{
cancellationTokenSource.Cancel();
return;
}
onCopy?.Invoke(buffer, 0, read);
var writeCallback = (AsyncCallback)(ar2 =>
{
if (cancellationTokenSource.IsCancellationRequested)
{
return;
}
try
{
outputStream.EndWrite(ar2);
beginRead(inputStream, outputStream, buffer, onCopy, cancellationTokenSource,
exceptionFunc);
}
catch (IOException ex)
{
cancellationTokenSource.Cancel();
exceptionFunc(ex);
}
});
outputStream.BeginWrite(buffer, 0, read, writeCallback, null);
}
catch (IOException ex)
{
cancellationTokenSource.Cancel();
exceptionFunc(ex);
}
});
var readResult = inputStream.BeginRead(buffer, 0, buffer.Length, readCallback, null);
if (readResult.CompletedSynchronously)
{
readCallback(readResult);
}
}
/// <summary> /// <summary>
/// relays the input clientStream to the server at the specified host name and port with the given httpCmd and headers /// relays the input clientStream to the server at the specified host name and port with the given httpCmd and headers
/// as prefix /// as prefix
......
using System; using System;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.Exceptions; using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network.Tcp; using Titanium.Web.Proxy.Network.Tcp;
using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Http
{ namespace Titanium.Web.Proxy.Http
/// <summary> {
/// Used to communicate with the server over HTTP(S) /// <summary>
/// </summary> /// Used to communicate with the server over HTTP(S)
public class HttpWebClient /// </summary>
{ public class HttpWebClient
{
internal HttpWebClient(Request request = null, Response response = null) internal HttpWebClient(Request request = null, Response response = null)
{ {
Request = request ?? new Request(); Request = request ?? new Request();
Response = response ?? new Response(); Response = response ?? new Response();
} }
/// <summary> /// <summary>
/// Connection to server /// Connection to server
/// </summary> /// </summary>
internal TcpServerConnection ServerConnection { get; set; } internal TcpServerConnection ServerConnection { get; set; }
/// <summary> /// <summary>
/// Should we close the server connection at the end of this HTTP request/response session. /// Should we close the server connection at the end of this HTTP request/response session.
/// </summary> /// </summary>
internal bool CloseServerConnection { get; set; } internal bool CloseServerConnection { get; set; }
/// <summary> /// <summary>
/// Stores internal data for the session. /// Stores internal data for the session.
/// </summary> /// </summary>
internal InternalDataStore Data { get; } = new InternalDataStore(); internal InternalDataStore Data { get; } = new InternalDataStore();
/// <summary> /// <summary>
/// Gets or sets the user data. /// Gets or sets the user data.
/// </summary> /// </summary>
public object UserData { get; set; } public object UserData { get; set; }
/// <summary> /// <summary>
/// Override UpStreamEndPoint for this request; Local NIC via request is made /// Override UpStreamEndPoint for this request; Local NIC via request is made
/// </summary> /// </summary>
public IPEndPoint UpStreamEndPoint { get; set; } public IPEndPoint UpStreamEndPoint { get; set; }
/// <summary> /// <summary>
/// Headers passed with Connect. /// Headers passed with Connect.
/// </summary> /// </summary>
public ConnectRequest ConnectRequest { get; internal set; } public ConnectRequest ConnectRequest { get; internal set; }
/// <summary> /// <summary>
/// Web Request. /// Web Request.
/// </summary> /// </summary>
public Request Request { get; } public Request Request { get; }
/// <summary> /// <summary>
/// Web Response. /// Web Response.
/// </summary> /// </summary>
public Response Response { get; internal set; } public Response Response { get; internal set; }
/// <summary> /// <summary>
/// PID of the process that is created the current session when client is running in this machine /// PID of the process that is created the current session when client is running in this machine
/// If client is remote then this will return /// If client is remote then this will return
/// </summary> /// </summary>
public Lazy<int> ProcessId { get; internal set; } public Lazy<int> ProcessId { get; internal set; }
/// <summary> /// <summary>
/// Is Https? /// Is Https?
/// </summary> /// </summary>
public bool IsHttps => Request.IsHttps; public bool IsHttps => Request.IsHttps;
/// <summary> /// <summary>
/// Set the tcp connection to server used by this webclient /// Set the tcp connection to server used by this webclient
/// </summary> /// </summary>
/// <param name="serverConnection">Instance of <see cref="TcpServerConnection" /></param> /// <param name="serverConnection">Instance of <see cref="TcpServerConnection" /></param>
internal void SetConnection(TcpServerConnection serverConnection) internal void SetConnection(TcpServerConnection serverConnection)
{ {
serverConnection.LastAccess = DateTime.Now; serverConnection.LastAccess = DateTime.Now;
ServerConnection = serverConnection; ServerConnection = serverConnection;
} }
/// <summary> /// <summary>
/// Prepare and send the http(s) request /// Prepare and send the http(s) request
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
internal async Task SendRequest(bool enable100ContinueBehaviour, bool isTransparent, internal async Task SendRequest(bool enable100ContinueBehaviour, bool isTransparent,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
var upstreamProxy = ServerConnection.UpStreamProxy; var upstreamProxy = ServerConnection.UpStreamProxy;
bool useUpstreamProxy = upstreamProxy != null && ServerConnection.IsHttps == false; bool useUpstreamProxy = upstreamProxy != null && ServerConnection.IsHttps == false;
var writer = ServerConnection.StreamWriter; var writer = ServerConnection.StreamWriter;
// prepare the request & headers // prepare the request & headers
await writer.WriteLineAsync(Request.CreateRequestLine(Request.Method, await writer.WriteLineAsync(Request.CreateRequestLine(Request.Method,
useUpstreamProxy || isTransparent ? Request.OriginalUrl : Request.RequestUri.PathAndQuery, useUpstreamProxy || isTransparent ? Request.OriginalUrl : Request.RequestUri.PathAndQuery,
Request.HttpVersion), cancellationToken); Request.HttpVersion), cancellationToken);
var headerBuilder = new StringBuilder(); var headerBuilder = new StringBuilder();
// Send Authentication to Upstream proxy if needed
if (!isTransparent && upstreamProxy != null // Send Authentication to Upstream proxy if needed
&& ServerConnection.IsHttps == false if (!isTransparent && upstreamProxy != null
&& !string.IsNullOrEmpty(upstreamProxy.UserName) && ServerConnection.IsHttps == false
&& upstreamProxy.Password != null) && !string.IsNullOrEmpty(upstreamProxy.UserName)
{ && upstreamProxy.Password != null)
headerBuilder.AppendLine(HttpHeader.ProxyConnectionKeepAlive.ToString()); {
headerBuilder.AppendLine(HttpHeader.GetProxyAuthorizationHeader(upstreamProxy.UserName, upstreamProxy.Password).ToString()); headerBuilder.Append($"{HttpHeader.ProxyConnectionKeepAlive}{ProxyConstants.NewLine}");
} headerBuilder.Append($"{HttpHeader.GetProxyAuthorizationHeader(upstreamProxy.UserName, upstreamProxy.Password)}{ProxyConstants.NewLine}");
}
// write request headers
foreach (var header in Request.Headers) // write request headers
{ foreach (var header in Request.Headers)
if (isTransparent || header.Name != KnownHeaders.ProxyAuthorization) {
{ if (isTransparent || header.Name != KnownHeaders.ProxyAuthorization)
headerBuilder.AppendLine(header.ToString()); {
} headerBuilder.Append($"{header}{ProxyConstants.NewLine}");
} }
}
headerBuilder.AppendLine();
await writer.WriteAsync(headerBuilder.ToString(), cancellationToken); headerBuilder.Append(ProxyConstants.NewLine);
if (enable100ContinueBehaviour) await writer.WriteAsync(headerBuilder.ToString(), cancellationToken);
{
if (Request.ExpectContinue) if (enable100ContinueBehaviour)
{ {
string httpStatus; if (Request.ExpectContinue)
try {
{ string httpStatus;
httpStatus = await ServerConnection.Stream.ReadLineAsync(cancellationToken); try
if (httpStatus == null) {
{ httpStatus = await ServerConnection.Stream.ReadLineAsync(cancellationToken);
throw new ServerConnectionException("Server connection was closed."); if (httpStatus == null)
} {
} throw new ServerConnectionException("Server connection was closed.");
catch (Exception e) when (!(e is ServerConnectionException)) }
{ }
throw new ServerConnectionException("Server connection was closed."); catch (Exception e) when (!(e is ServerConnectionException))
} {
throw new ServerConnectionException("Server connection was closed.");
Response.ParseResponseLine(httpStatus, out _, out int responseStatusCode, }
out string responseStatusDescription);
Response.ParseResponseLine(httpStatus, out _, out int responseStatusCode,
// find if server is willing for expect continue out string responseStatusDescription);
if (responseStatusCode == (int)HttpStatusCode.Continue
&& responseStatusDescription.EqualsIgnoreCase("continue")) // find if server is willing for expect continue
{ if (responseStatusCode == (int)HttpStatusCode.Continue
Request.Is100Continue = true; && responseStatusDescription.EqualsIgnoreCase("continue"))
await ServerConnection.Stream.ReadLineAsync(cancellationToken); {
} Request.Is100Continue = true;
else if (responseStatusCode == (int)HttpStatusCode.ExpectationFailed await ServerConnection.Stream.ReadLineAsync(cancellationToken);
&& responseStatusDescription.EqualsIgnoreCase("expectation failed")) }
{ else if (responseStatusCode == (int)HttpStatusCode.ExpectationFailed
Request.ExpectationFailed = true; && responseStatusDescription.EqualsIgnoreCase("expectation failed"))
await ServerConnection.Stream.ReadLineAsync(cancellationToken); {
} Request.ExpectationFailed = true;
} await ServerConnection.Stream.ReadLineAsync(cancellationToken);
} }
} }
}
/// <summary> }
/// Receive and parse the http response from server
/// </summary> /// <summary>
/// <returns></returns> /// Receive and parse the http response from server
internal async Task ReceiveResponse(CancellationToken cancellationToken) /// </summary>
{ /// <returns></returns>
// return if this is already read internal async Task ReceiveResponse(CancellationToken cancellationToken)
if (Response.StatusCode != 0) {
{ // return if this is already read
return; if (Response.StatusCode != 0)
} {
return;
string httpStatus; }
try
{ string httpStatus;
httpStatus = await ServerConnection.Stream.ReadLineAsync(cancellationToken); try
if (httpStatus == null) {
{ httpStatus = await ServerConnection.Stream.ReadLineAsync(cancellationToken);
throw new ServerConnectionException("Server connection was closed."); if (httpStatus == null)
} {
} throw new ServerConnectionException("Server connection was closed.");
catch (Exception e) when (!(e is ServerConnectionException)) }
{ }
throw new ServerConnectionException("Server connection was closed."); catch (Exception e) when (!(e is ServerConnectionException))
} {
throw new ServerConnectionException("Server connection was closed.");
if (httpStatus == string.Empty) }
{
httpStatus = await ServerConnection.Stream.ReadLineAsync(cancellationToken); if (httpStatus == string.Empty)
} {
httpStatus = await ServerConnection.Stream.ReadLineAsync(cancellationToken);
Response.ParseResponseLine(httpStatus, out var version, out int statusCode, out string statusDescription); }
Response.HttpVersion = version; Response.ParseResponseLine(httpStatus, out var version, out int statusCode, out string statusDescription);
Response.StatusCode = statusCode;
Response.StatusDescription = statusDescription; Response.HttpVersion = version;
Response.StatusCode = statusCode;
// For HTTP 1.1 comptibility server may send expect-continue even if not asked for it in request Response.StatusDescription = statusDescription;
if (Response.StatusCode == (int)HttpStatusCode.Continue
&& Response.StatusDescription.EqualsIgnoreCase("continue")) // For HTTP 1.1 comptibility server may send expect-continue even if not asked for it in request
{ if (Response.StatusCode == (int)HttpStatusCode.Continue
// Read the next line after 100-continue && Response.StatusDescription.EqualsIgnoreCase("continue"))
Response.Is100Continue = true; {
Response.StatusCode = 0; // Read the next line after 100-continue
await ServerConnection.Stream.ReadLineAsync(cancellationToken); Response.Is100Continue = true;
Response.StatusCode = 0;
// now receive response await ServerConnection.Stream.ReadLineAsync(cancellationToken);
await ReceiveResponse(cancellationToken);
return; // now receive response
} await ReceiveResponse(cancellationToken);
return;
if (Response.StatusCode == (int)HttpStatusCode.ExpectationFailed }
&& Response.StatusDescription.EqualsIgnoreCase("expectation failed"))
{ if (Response.StatusCode == (int)HttpStatusCode.ExpectationFailed
// read next line after expectation failed response && Response.StatusDescription.EqualsIgnoreCase("expectation failed"))
Response.ExpectationFailed = true; {
Response.StatusCode = 0; // read next line after expectation failed response
await ServerConnection.Stream.ReadLineAsync(cancellationToken); Response.ExpectationFailed = true;
Response.StatusCode = 0;
// now receive response await ServerConnection.Stream.ReadLineAsync(cancellationToken);
await ReceiveResponse(cancellationToken);
return; // now receive response
} await ReceiveResponse(cancellationToken);
return;
// Read the response headers in to unique and non-unique header collections }
await HeaderParser.ReadHeaders(ServerConnection.Stream, Response.Headers, cancellationToken);
} // Read the response headers in to unique and non-unique header collections
await HeaderParser.ReadHeaders(ServerConnection.Stream, Response.Headers, cancellationToken);
/// <summary> }
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary> /// <summary>
internal void FinishSession() /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
{ /// </summary>
ServerConnection = null; internal void FinishSession()
{
ConnectRequest?.FinishSession(); ServerConnection = null;
Request?.FinishSession();
Response?.FinishSession(); ConnectRequest?.FinishSession();
Request?.FinishSession();
Data.Clear(); Response?.FinishSession();
UserData = null;
} Data.Clear();
} UserData = null;
} }
}
}
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
public const string ContentEncoding = "content-encoding"; public const string ContentEncoding = "content-encoding";
public const string ContentEncodingDeflate = "deflate"; public const string ContentEncodingDeflate = "deflate";
public const string ContentEncodingGzip = "gzip"; public const string ContentEncodingGzip = "gzip";
public const string ContentEncodingBrotli = "br";
public const string Location = "Location"; public const string Location = "Location";
......
...@@ -140,13 +140,13 @@ namespace Titanium.Web.Proxy.Http ...@@ -140,13 +140,13 @@ namespace Titanium.Web.Proxy.Http
get get
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
sb.AppendLine(CreateRequestLine(Method, OriginalUrl, HttpVersion)); sb.Append($"{CreateRequestLine(Method, OriginalUrl, HttpVersion)}{ProxyConstants.NewLine}");
foreach (var header in Headers) foreach (var header in Headers)
{ {
sb.AppendLine(header.ToString()); sb.Append($"{header.ToString()}{ProxyConstants.NewLine}");
} }
sb.AppendLine(); sb.Append(ProxyConstants.NewLine);
return sb.ToString(); return sb.ToString();
} }
} }
......
...@@ -110,13 +110,13 @@ namespace Titanium.Web.Proxy.Http ...@@ -110,13 +110,13 @@ namespace Titanium.Web.Proxy.Http
get get
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
sb.AppendLine(CreateResponseLine(HttpVersion, StatusCode, StatusDescription)); sb.Append($"{CreateResponseLine(HttpVersion, StatusCode, StatusDescription)}{ProxyConstants.NewLine}");
foreach (var header in Headers) foreach (var header in Headers)
{ {
sb.AppendLine(header.ToString()); sb.Append($"{header.ToString()}{ProxyConstants.NewLine}");
} }
sb.AppendLine(); sb.Append(ProxyConstants.NewLine);
return sb.ToString(); return sb.ToString();
} }
} }
...@@ -145,7 +145,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -145,7 +145,7 @@ namespace Titanium.Web.Proxy.Http
out string statusDescription) out string statusDescription)
{ {
var httpResult = httpStatus.Split(ProxyConstants.SpaceSplit, 3); var httpResult = httpStatus.Split(ProxyConstants.SpaceSplit, 3);
if (httpResult.Length != 3) if (httpResult.Length <= 1)
{ {
throw new Exception("Invalid HTTP status line: " + httpStatus); throw new Exception("Invalid HTTP status line: " + httpStatus);
} }
...@@ -159,7 +159,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -159,7 +159,7 @@ namespace Titanium.Web.Proxy.Http
} }
statusCode = int.Parse(httpResult[1]); statusCode = int.Parse(httpResult[1]);
statusDescription = httpResult[2]; statusDescription = httpResult.Length > 2 ? httpResult[2] : string.Empty;
} }
} }
} }
using System; using System;
using System.Net; using System.Net;
namespace Titanium.Web.Proxy.Models namespace Titanium.Web.Proxy.Models
{ {
/// <summary> /// <summary>
/// An upstream proxy this proxy uses if any. /// An upstream proxy this proxy uses if any.
/// </summary> /// </summary>
public class ExternalProxy public class ExternalProxy
{ {
private static readonly Lazy<NetworkCredential> defaultCredentials = private static readonly Lazy<NetworkCredential> defaultCredentials =
new Lazy<NetworkCredential>(() => CredentialCache.DefaultNetworkCredentials); new Lazy<NetworkCredential>(() => CredentialCache.DefaultNetworkCredentials);
private string password; private string password;
private string userName; private string userName;
/// <summary> /// <summary>
/// Use default windows credentials? /// Use default windows credentials?
/// </summary> /// </summary>
public bool UseDefaultCredentials { get; set; } public bool UseDefaultCredentials { get; set; }
/// <summary> /// <summary>
/// Bypass this proxy for connections to localhost? /// Bypass this proxy for connections to localhost?
/// </summary> /// </summary>
public bool BypassLocalhost { get; set; } public bool BypassLocalhost { get; set; }
/// <summary> /// <summary>
/// Username. /// Username.
/// </summary> /// </summary>
public string UserName public string UserName
{ {
get => UseDefaultCredentials ? defaultCredentials.Value.UserName : userName; get => UseDefaultCredentials ? defaultCredentials.Value.UserName : userName;
set set
{ {
userName = value; userName = value;
if (defaultCredentials.Value.UserName != userName) if (defaultCredentials.Value.UserName != userName)
{ {
UseDefaultCredentials = false; UseDefaultCredentials = false;
} }
} }
} }
/// <summary> /// <summary>
/// Password. /// Password.
/// </summary> /// </summary>
public string Password public string Password
{ {
get => UseDefaultCredentials ? defaultCredentials.Value.Password : password; get => UseDefaultCredentials ? defaultCredentials.Value.Password : password;
set set
{ {
password = value; password = value;
if (defaultCredentials.Value.Password != password) if (defaultCredentials.Value.Password != password)
{ {
UseDefaultCredentials = false; UseDefaultCredentials = false;
} }
} }
} }
/// <summary> /// <summary>
/// Host name. /// Host name.
/// </summary> /// </summary>
public string HostName { get; set; } public string HostName { get; set; }
/// <summary> /// <summary>
/// Port. /// Port.
/// </summary> /// </summary>
public int Port { get; set; } public int Port { get; set; }
/// <summary> /// <summary>
/// Get cache key for Tcp connection cahe. /// Get cache key for Tcp connection cahe.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
internal string GetCacheKey() internal string GetCacheKey()
{ {
return $"{HostName}-{Port}" + (UseDefaultCredentials ? $"-{UserName}-{Password}" : string.Empty); return $"{HostName}-{Port}" + (UseDefaultCredentials ? $"-{UserName}-{Password}" : string.Empty);
} }
/// <summary> /// <summary>
/// returns data in Hostname:port format. /// returns data in Hostname:port format.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public override string ToString() public override string ToString()
{ {
return $"{HostName}:{Port}"; return $"{HostName}:{Port}";
} }
} }
} }
...@@ -52,6 +52,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -52,6 +52,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
internal WinCertificateMaker(ExceptionHandler exceptionFunc) internal WinCertificateMaker(ExceptionHandler exceptionFunc)
{ {
this.exceptionFunc = exceptionFunc; this.exceptionFunc = exceptionFunc;
typeX500DN = Type.GetTypeFromProgID("X509Enrollment.CX500DistinguishedName", true); typeX500DN = Type.GetTypeFromProgID("X509Enrollment.CX500DistinguishedName", true);
typeX509PrivateKey = Type.GetTypeFromProgID("X509Enrollment.CX509PrivateKey", true); typeX509PrivateKey = Type.GetTypeFromProgID("X509Enrollment.CX509PrivateKey", true);
typeOID = Type.GetTypeFromProgID("X509Enrollment.CObjectId", true); typeOID = Type.GetTypeFromProgID("X509Enrollment.CObjectId", true);
...@@ -74,13 +75,41 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -74,13 +75,41 @@ namespace Titanium.Web.Proxy.Network.Certificate
/// <summary> /// <summary>
/// Make certificate. /// Make certificate.
/// </summary> /// </summary>
/// <param name="sSubjectCN"></param>
/// <param name="isRoot"></param>
/// <param name="signingCert"></param>
/// <returns></returns>
public X509Certificate2 MakeCertificate(string sSubjectCN, bool isRoot, X509Certificate2 signingCert = null) public X509Certificate2 MakeCertificate(string sSubjectCN, bool isRoot, X509Certificate2 signingCert = null)
{ {
return makeCertificateInternal(sSubjectCN, isRoot, true, signingCert); return makeCertificate(sSubjectCN, isRoot, true, signingCert);
}
private X509Certificate2 makeCertificate(string sSubjectCN, bool isRoot,
bool switchToMTAIfNeeded, X509Certificate2 signingCert = null,
CancellationToken cancellationToken = default)
{
if (switchToMTAIfNeeded && Thread.CurrentThread.GetApartmentState() != ApartmentState.MTA)
{
return Task.Run(() => makeCertificate(sSubjectCN, isRoot, false, signingCert),
cancellationToken).Result;
}
// Subject
string fullSubject = $"CN={sSubjectCN}";
// Sig Algo
const string hashAlgo = "SHA256";
// Grace Days
const int graceDays = -366;
// ValiDays
const int validDays = 1825;
// KeyLength
const int keyLength = 2048;
var graceTime = DateTime.Now.AddDays(graceDays);
var now = DateTime.Now;
var certificate = makeCertificate(isRoot, sSubjectCN, fullSubject, keyLength, hashAlgo, graceTime,
now.AddDays(validDays), isRoot ? null : signingCert);
return certificate;
} }
private X509Certificate2 makeCertificate(bool isRoot, string subject, string fullSubject, private X509Certificate2 makeCertificate(bool isRoot, string subject, string fullSubject,
...@@ -271,39 +300,9 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -271,39 +300,9 @@ namespace Titanium.Web.Proxy.Network.Certificate
string empty = (string)typeX509Enrollment.InvokeMember("CreatePFX", BindingFlags.InvokeMethod, null, string empty = (string)typeX509Enrollment.InvokeMember("CreatePFX", BindingFlags.InvokeMethod, null,
x509Enrollment, typeValue); x509Enrollment, typeValue);
return new X509Certificate2(Convert.FromBase64String(empty), string.Empty, X509KeyStorageFlags.Exportable); return new X509Certificate2(Convert.FromBase64String(empty), string.Empty, X509KeyStorageFlags.Exportable);
} }
private X509Certificate2 makeCertificateInternal(string sSubjectCN, bool isRoot,
bool switchToMTAIfNeeded, X509Certificate2 signingCert = null,
CancellationToken cancellationToken = default)
{
if (switchToMTAIfNeeded && Thread.CurrentThread.GetApartmentState() != ApartmentState.MTA)
{
return Task.Run(() => makeCertificateInternal(sSubjectCN, isRoot, false, signingCert),
cancellationToken).Result;
}
// Subject
string fullSubject = $"CN={sSubjectCN}";
// Sig Algo
const string hashAlgo = "SHA256";
// Grace Days
const int graceDays = -366;
// ValiDays
const int validDays = 1825;
// KeyLength
const int keyLength = 2048;
var graceTime = DateTime.Now.AddDays(graceDays);
var now = DateTime.Now;
var certificate = makeCertificate(isRoot, sSubjectCN, fullSubject, keyLength, hashAlgo, graceTime,
now.AddDays(validDays), isRoot ? null : signingCert);
return certificate;
}
} }
} }
...@@ -25,7 +25,8 @@ namespace Titanium.Web.Proxy.Network ...@@ -25,7 +25,8 @@ namespace Titanium.Web.Proxy.Network
BouncyCastle = 0, BouncyCastle = 0,
/// <summary> /// <summary>
/// Uses Windows Certification Generation API. /// Uses Windows Certification Generation API and only valid in Windows OS.
/// Observed to be faster than BouncyCastle.
/// Bug #468 Reported. /// Bug #468 Reported.
/// </summary> /// </summary>
DefaultWindows = 1 DefaultWindows = 1
...@@ -59,7 +60,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -59,7 +60,7 @@ namespace Titanium.Web.Proxy.Network
private X509Certificate2 rootCertificate; private X509Certificate2 rootCertificate;
private string rootCertificateName; private string rootCertificateName;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="CertificateManager"/> class. /// Initializes a new instance of the <see cref="CertificateManager"/> class.
/// </summary> /// </summary>
...@@ -241,7 +242,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -241,7 +242,7 @@ namespace Titanium.Web.Proxy.Network
public void Dispose() public void Dispose()
{ {
} }
private string getRootCertificateDirectory() private string getRootCertificateDirectory()
{ {
string assemblyLocation = Assembly.GetExecutingAssembly().Location; string assemblyLocation = Assembly.GetExecutingAssembly().Location;
...@@ -426,17 +427,16 @@ namespace Titanium.Web.Proxy.Network ...@@ -426,17 +427,16 @@ namespace Titanium.Web.Proxy.Network
certificate = makeCertificate(certificateName, false); certificate = makeCertificate(certificateName, false);
// store as cache // store as cache
Task.Run(() => try
{
var exported = certificate.Export(X509ContentType.Pkcs12);
File.WriteAllBytes(certificatePath, exported);
}
catch (Exception e)
{ {
try ExceptionFunc(new Exception("Failed to save fake certificate.", e));
{ }
File.WriteAllBytes(certificatePath, certificate.Export(X509ContentType.Pkcs12));
}
catch (Exception e)
{
ExceptionFunc(new Exception("Failed to save fake certificate.", e));
}
});
} }
else else
{ {
...@@ -529,7 +529,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -529,7 +529,7 @@ namespace Titanium.Web.Proxy.Network
await Task.Delay(1000 * 60); await Task.Delay(1000 * 60);
} }
} }
/// <summary> /// <summary>
/// Stops the certificate cache clear process /// Stops the certificate cache clear process
/// </summary> /// </summary>
...@@ -775,7 +775,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -775,7 +775,7 @@ namespace Titanium.Web.Proxy.Network
EnsureRootCertificate(); EnsureRootCertificate();
} }
/// <summary> /// <summary>
/// Determines whether the root certificate is trusted. /// Determines whether the root certificate is trusted.
/// </summary> /// </summary>
......
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Security; using System.Net.Security;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended.Network; using StreamExtended.Network;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Network.Tcp namespace Titanium.Web.Proxy.Network.Tcp
{ {
/// <summary> /// <summary>
/// A class that manages Tcp Connection to server used by this proxy server. /// A class that manages Tcp Connection to server used by this proxy server.
/// </summary> /// </summary>
internal class TcpConnectionFactory : IDisposable internal class TcpConnectionFactory : IDisposable
{ {
//Tcp server connection pool cache
private readonly ConcurrentDictionary<string, ConcurrentQueue<TcpServerConnection>> cache //Tcp server connection pool cache
= new ConcurrentDictionary<string, ConcurrentQueue<TcpServerConnection>>(); private readonly ConcurrentDictionary<string, ConcurrentQueue<TcpServerConnection>> cache
= new ConcurrentDictionary<string, ConcurrentQueue<TcpServerConnection>>();
//Tcp connections waiting to be disposed by cleanup task
private readonly ConcurrentBag<TcpServerConnection> disposalBag = //Tcp connections waiting to be disposed by cleanup task
new ConcurrentBag<TcpServerConnection>(); private readonly ConcurrentBag<TcpServerConnection> disposalBag =
new ConcurrentBag<TcpServerConnection>();
//cache object race operations lock
private readonly SemaphoreSlim @lock = new SemaphoreSlim(1); //cache object race operations lock
private readonly SemaphoreSlim @lock = new SemaphoreSlim(1);
private volatile bool runCleanUpTask = true;
private volatile bool runCleanUpTask = true;
internal TcpConnectionFactory(ProxyServer server)
{ internal TcpConnectionFactory(ProxyServer server)
this.server = server; {
Task.Run(async () => await clearOutdatedConnections()); this.server = server;
} Task.Run(async () => await clearOutdatedConnections());
}
internal ProxyServer server { get; set; }
internal ProxyServer server { get; set; }
internal string GetConnectionCacheKey(string remoteHostName, int remotePort,
bool isHttps, List<SslApplicationProtocol> applicationProtocols, internal string GetConnectionCacheKey(string remoteHostName, int remotePort,
ProxyServer proxyServer, IPEndPoint upStreamEndPoint, ExternalProxy externalProxy) bool isHttps, List<SslApplicationProtocol> applicationProtocols,
{ ProxyServer proxyServer, IPEndPoint upStreamEndPoint, ExternalProxy externalProxy)
//http version is ignored since its an application level decision b/w HTTP 1.0/1.1 {
//also when doing connect request MS Edge browser sends http 1.0 but uses 1.1 after server sends 1.1 its response. //http version is ignored since its an application level decision b/w HTTP 1.0/1.1
//That can create cache miss for same server connection unneccessarily expecially when prefetcing with Connect. //also when doing connect request MS Edge browser sends http 1.0 but uses 1.1 after server sends 1.1 its response.
//http version 2 is separated using applicationProtocols below. //That can create cache miss for same server connection unneccessarily expecially when prefetcing with Connect.
var cacheKeyBuilder = new StringBuilder($"{remoteHostName}-{remotePort}-" + //http version 2 is separated using applicationProtocols below.
//when creating Tcp client isConnect won't matter var cacheKeyBuilder = new StringBuilder($"{remoteHostName}-{remotePort}-" +
$"{isHttps}-"); //when creating Tcp client isConnect won't matter
if (applicationProtocols != null) $"{isHttps}-");
{ if (applicationProtocols != null)
foreach (var protocol in applicationProtocols.OrderBy(x => x)) {
{ foreach (var protocol in applicationProtocols.OrderBy(x => x))
cacheKeyBuilder.Append($"{protocol}-"); {
} cacheKeyBuilder.Append($"{protocol}-");
} }
}
cacheKeyBuilder.Append(upStreamEndPoint != null
? $"{upStreamEndPoint.Address}-{upStreamEndPoint.Port}-" cacheKeyBuilder.Append(upStreamEndPoint != null
: string.Empty); ? $"{upStreamEndPoint.Address}-{upStreamEndPoint.Port}-"
cacheKeyBuilder.Append(externalProxy != null ? $"{externalProxy.GetCacheKey()}-" : string.Empty); : string.Empty);
cacheKeyBuilder.Append(externalProxy != null ? $"{externalProxy.GetCacheKey()}-" : string.Empty);
return cacheKeyBuilder.ToString();
return cacheKeyBuilder.ToString();
}
}
/// <summary>
/// Gets the connection cache key. /// <summary>
/// </summary> /// Gets the connection cache key.
/// <param name="args">The session event arguments.</param> /// </summary>
/// <param name="applicationProtocol"></param> /// <param name="session">The session event arguments.</param>
/// <returns></returns> /// <param name="applicationProtocol"></param>
internal async Task<string> GetConnectionCacheKey(ProxyServer server, SessionEventArgsBase args, /// <returns></returns>
SslApplicationProtocol applicationProtocol) internal async Task<string> GetConnectionCacheKey(ProxyServer server, SessionEventArgsBase session,
{ SslApplicationProtocol applicationProtocol)
List<SslApplicationProtocol> applicationProtocols = null; {
if (applicationProtocol != default) List<SslApplicationProtocol> applicationProtocols = null;
{ if (applicationProtocol != default)
applicationProtocols = new List<SslApplicationProtocol> { applicationProtocol }; {
} applicationProtocols = new List<SslApplicationProtocol> { applicationProtocol };
}
ExternalProxy customUpStreamProxy = null;
ExternalProxy customUpStreamProxy = null;
bool isHttps = args.IsHttps;
if (server.GetCustomUpStreamProxyFunc != null) bool isHttps = session.IsHttps;
{ if (server.GetCustomUpStreamProxyFunc != null)
customUpStreamProxy = await server.GetCustomUpStreamProxyFunc(args); {
} customUpStreamProxy = await server.GetCustomUpStreamProxyFunc(session);
}
args.CustomUpStreamProxyUsed = customUpStreamProxy;
session.CustomUpStreamProxyUsed = customUpStreamProxy;
return GetConnectionCacheKey(
args.WebSession.Request.RequestUri.Host, return GetConnectionCacheKey(
args.WebSession.Request.RequestUri.Port, session.WebSession.Request.RequestUri.Host,
isHttps, applicationProtocols, session.WebSession.Request.RequestUri.Port,
server, args.WebSession.UpStreamEndPoint ?? server.UpStreamEndPoint, isHttps, applicationProtocols,
customUpStreamProxy ?? (isHttps ? server.UpStreamHttpsProxy : server.UpStreamHttpProxy)); server, session.WebSession.UpStreamEndPoint ?? server.UpStreamEndPoint,
} customUpStreamProxy ?? (isHttps ? server.UpStreamHttpsProxy : server.UpStreamHttpProxy));
}
/// <summary>
/// Create a server connection. /// <summary>
/// </summary> /// Create a server connection.
/// <param name="args">The session event arguments.</param> /// </summary>
/// <param name="isConnect">Is this a CONNECT request.</param> /// <param name="session">The session event arguments.</param>
/// <param name="applicationProtocol"></param> /// <param name="isConnect">Is this a CONNECT request.</param>
/// <param name="cancellationToken">The cancellation token for this async task.</param> /// <param name="applicationProtocol"></param>
/// <returns></returns> /// <param name="cancellationToken">The cancellation token for this async task.</param>
internal Task<TcpServerConnection> GetServerConnection(ProxyServer server, SessionEventArgsBase args, bool isConnect, /// <returns></returns>
SslApplicationProtocol applicationProtocol, bool noCache, CancellationToken cancellationToken) internal Task<TcpServerConnection> GetServerConnection(ProxyServer server, SessionEventArgsBase session, bool isConnect,
{ SslApplicationProtocol applicationProtocol, bool noCache, CancellationToken cancellationToken)
List<SslApplicationProtocol> applicationProtocols = null; {
if (applicationProtocol != default) List<SslApplicationProtocol> applicationProtocols = null;
{ if (applicationProtocol != default)
applicationProtocols = new List<SslApplicationProtocol> { applicationProtocol }; {
} applicationProtocols = new List<SslApplicationProtocol> { applicationProtocol };
}
return GetServerConnection(server, args, isConnect, applicationProtocols, noCache, cancellationToken);
} return GetServerConnection(server, session, isConnect, applicationProtocols, noCache, cancellationToken);
}
/// <summary>
/// Create a server connection. /// <summary>
/// </summary> /// Create a server connection.
/// <param name="args">The session event arguments.</param> /// </summary>
/// <param name="isConnect">Is this a CONNECT request.</param> /// <param name="session">The session event arguments.</param>
/// <param name="applicationProtocols"></param> /// <param name="isConnect">Is this a CONNECT request.</param>
/// <param name="cancellationToken">The cancellation token for this async task.</param> /// <param name="applicationProtocols"></param>
/// <returns></returns> /// <param name="cancellationToken">The cancellation token for this async task.</param>
internal async Task<TcpServerConnection> GetServerConnection(ProxyServer server, SessionEventArgsBase args, bool isConnect, /// <returns></returns>
List<SslApplicationProtocol> applicationProtocols, bool noCache, CancellationToken cancellationToken) internal async Task<TcpServerConnection> GetServerConnection(ProxyServer server, SessionEventArgsBase session, bool isConnect,
{ List<SslApplicationProtocol> applicationProtocols, bool noCache, CancellationToken cancellationToken)
ExternalProxy customUpStreamProxy = null; {
ExternalProxy customUpStreamProxy = null;
bool isHttps = args.IsHttps;
if (server.GetCustomUpStreamProxyFunc != null) bool isHttps = session.IsHttps;
{ if (server.GetCustomUpStreamProxyFunc != null)
customUpStreamProxy = await server.GetCustomUpStreamProxyFunc(args); {
} customUpStreamProxy = await server.GetCustomUpStreamProxyFunc(session);
}
args.CustomUpStreamProxyUsed = customUpStreamProxy;
session.CustomUpStreamProxyUsed = customUpStreamProxy;
return await GetServerConnection(
args.WebSession.Request.RequestUri.Host, return await GetServerConnection(
args.WebSession.Request.RequestUri.Port, session.WebSession.Request.RequestUri.Host,
args.WebSession.Request.HttpVersion, session.WebSession.Request.RequestUri.Port,
isHttps, applicationProtocols, isConnect, session.WebSession.Request.HttpVersion,
server, args.WebSession.UpStreamEndPoint ?? server.UpStreamEndPoint, isHttps, applicationProtocols, isConnect,
customUpStreamProxy ?? (isHttps ? server.UpStreamHttpsProxy : server.UpStreamHttpProxy), server, session, session.WebSession.UpStreamEndPoint ?? server.UpStreamEndPoint,
noCache, cancellationToken); customUpStreamProxy ?? (isHttps ? server.UpStreamHttpsProxy : server.UpStreamHttpProxy),
} noCache, cancellationToken);
/// <summary> }
/// Gets a TCP connection to server from connection pool. /// <summary>
/// </summary> /// Gets a TCP connection to server from connection pool.
/// <param name="remoteHostName">The remote hostname.</param> /// </summary>
/// <param name="remotePort">The remote port.</param> /// <param name="remoteHostName">The remote hostname.</param>
/// <param name="httpVersion">The http version to use.</param> /// <param name="remotePort">The remote port.</param>
/// <param name="isHttps">Is this a HTTPS request.</param> /// <param name="httpVersion">The http version to use.</param>
/// <param name="applicationProtocols">The list of HTTPS application level protocol to negotiate if needed.</param> /// <param name="isHttps">Is this a HTTPS request.</param>
/// <param name="isConnect">Is this a CONNECT request.</param> /// <param name="applicationProtocols">The list of HTTPS application level protocol to negotiate if needed.</param>
/// <param name="proxyServer">The current ProxyServer instance.</param> /// <param name="isConnect">Is this a CONNECT request.</param>
/// <param name="upStreamEndPoint">The local upstream endpoint to make request via.</param> /// <param name="proxyServer">The current ProxyServer instance.</param>
/// <param name="externalProxy">The external proxy to make request via.</param> /// <param name="upStreamEndPoint">The local upstream endpoint to make request via.</param>
/// <param name="noCache">Not from cache/create new connection.</param> /// <param name="externalProxy">The external proxy to make request via.</param>
/// <param name="cancellationToken">The cancellation token for this async task.</param> /// <param name="noCache">Not from cache/create new connection.</param>
/// <returns></returns> /// <param name="cancellationToken">The cancellation token for this async task.</param>
internal async Task<TcpServerConnection> GetServerConnection(string remoteHostName, int remotePort, /// <returns></returns>
Version httpVersion, bool isHttps, List<SslApplicationProtocol> applicationProtocols, bool isConnect, internal async Task<TcpServerConnection> GetServerConnection(string remoteHostName, int remotePort,
ProxyServer proxyServer, IPEndPoint upStreamEndPoint, ExternalProxy externalProxy, Version httpVersion, bool isHttps, List<SslApplicationProtocol> applicationProtocols, bool isConnect,
bool noCache, CancellationToken cancellationToken) ProxyServer proxyServer, SessionEventArgsBase session, IPEndPoint upStreamEndPoint, ExternalProxy externalProxy,
{ bool noCache, CancellationToken cancellationToken)
var cacheKey = GetConnectionCacheKey(remoteHostName, remotePort, {
isHttps, applicationProtocols, var cacheKey = GetConnectionCacheKey(remoteHostName, remotePort,
proxyServer, upStreamEndPoint, externalProxy); isHttps, applicationProtocols,
proxyServer, upStreamEndPoint, externalProxy);
if (proxyServer.EnableConnectionPool && !noCache)
{ if (proxyServer.EnableConnectionPool && !noCache)
if (cache.TryGetValue(cacheKey, out var existingConnections)) {
{ if (cache.TryGetValue(cacheKey, out var existingConnections))
while (existingConnections.Count > 0) {
{ while (existingConnections.Count > 0)
if (existingConnections.TryDequeue(out var recentConnection)) {
{ if (existingConnections.TryDequeue(out var recentConnection))
//+3 seconds for potential delay after getting connection {
var cutOff = DateTime.Now.AddSeconds(-1 * proxyServer.ConnectionTimeOutSeconds + 3); //+3 seconds for potential delay after getting connection
var cutOff = DateTime.Now.AddSeconds(-1 * proxyServer.ConnectionTimeOutSeconds + 3);
if (recentConnection.LastAccess > cutOff
&& recentConnection.TcpClient.IsGoodConnection()) if (recentConnection.LastAccess > cutOff
{ && recentConnection.TcpClient.IsGoodConnection())
return recentConnection; {
} return recentConnection;
}
disposalBag.Add(recentConnection);
} disposalBag.Add(recentConnection);
} }
} }
} }
}
var connection = await createServerConnection(remoteHostName, remotePort, httpVersion, isHttps,
applicationProtocols, isConnect, proxyServer, upStreamEndPoint, externalProxy, cancellationToken); var connection = await createServerConnection(remoteHostName, remotePort, httpVersion, isHttps,
applicationProtocols, isConnect, proxyServer, session, upStreamEndPoint, externalProxy, cancellationToken);
connection.CacheKey = cacheKey;
connection.CacheKey = cacheKey;
return connection;
} return connection;
}
/// <summary>
/// Creates a TCP connection to server /// <summary>
/// </summary> /// Creates a TCP connection to server
/// <param name="remoteHostName">The remote hostname.</param> /// </summary>
/// <param name="remotePort">The remote port.</param> /// <param name="remoteHostName">The remote hostname.</param>
/// <param name="httpVersion">The http version to use.</param> /// <param name="remotePort">The remote port.</param>
/// <param name="isHttps">Is this a HTTPS request.</param> /// <param name="httpVersion">The http version to use.</param>
/// <param name="applicationProtocols">The list of HTTPS application level protocol to negotiate if needed.</param> /// <param name="isHttps">Is this a HTTPS request.</param>
/// <param name="isConnect">Is this a CONNECT request.</param> /// <param name="applicationProtocols">The list of HTTPS application level protocol to negotiate if needed.</param>
/// <param name="proxyServer">The current ProxyServer instance.</param> /// <param name="isConnect">Is this a CONNECT request.</param>
/// <param name="upStreamEndPoint">The local upstream endpoint to make request via.</param> /// <param name="proxyServer">The current ProxyServer instance.</param>
/// <param name="externalProxy">The external proxy to make request via.</param> /// <param name="session">The http session.</param>
/// <param name="cancellationToken">The cancellation token for this async task.</param> /// <param name="upStreamEndPoint">The local upstream endpoint to make request via.</param>
/// <returns></returns> /// <param name="externalProxy">The external proxy to make request via.</param>
private async Task<TcpServerConnection> createServerConnection(string remoteHostName, int remotePort, /// <param name="cancellationToken">The cancellation token for this async task.</param>
Version httpVersion, bool isHttps, List<SslApplicationProtocol> applicationProtocols, bool isConnect, /// <returns></returns>
ProxyServer proxyServer, IPEndPoint upStreamEndPoint, ExternalProxy externalProxy, private async Task<TcpServerConnection> createServerConnection(string remoteHostName, int remotePort,
CancellationToken cancellationToken) Version httpVersion, bool isHttps, List<SslApplicationProtocol> applicationProtocols, bool isConnect,
{ ProxyServer proxyServer, SessionEventArgsBase session, IPEndPoint upStreamEndPoint, ExternalProxy externalProxy,
//deny connection to proxy end points to avoid infinite connection loop. CancellationToken cancellationToken)
if (server.ProxyEndPoints.Any(x => x.Port == remotePort) {
&& NetworkHelper.IsLocalIpAddress(remoteHostName)) //deny connection to proxy end points to avoid infinite connection loop.
{ if (server.ProxyEndPoints.Any(x => x.Port == remotePort)
throw new Exception($"A client is making HTTP request to one of the listening ports of this proxy {remoteHostName}:{remotePort}"); && NetworkHelper.IsLocalIpAddress(remoteHostName))
} {
throw new Exception($"A client is making HTTP request to one of the listening ports of this proxy {remoteHostName}:{remotePort}");
if (externalProxy != null) }
{
if (server.ProxyEndPoints.Any(x => x.Port == externalProxy.Port) if (externalProxy != null)
&& NetworkHelper.IsLocalIpAddress(externalProxy.HostName)) {
{ if (server.ProxyEndPoints.Any(x => x.Port == externalProxy.Port)
throw new Exception($"A client is making HTTP request via external proxy to one of the listening ports of this proxy {remoteHostName}:{remotePort}"); && NetworkHelper.IsLocalIpAddress(externalProxy.HostName))
} {
} throw new Exception($"A client is making HTTP request via external proxy to one of the listening ports of this proxy {remoteHostName}:{remotePort}");
}
bool useUpstreamProxy = false; }
// check if external proxy is set for HTTP/HTTPS bool useUpstreamProxy = false;
if (externalProxy != null &&
!(externalProxy.HostName == remoteHostName && externalProxy.Port == remotePort)) // check if external proxy is set for HTTP/HTTPS
{ if (externalProxy != null &&
useUpstreamProxy = true; !(externalProxy.HostName == remoteHostName && externalProxy.Port == remotePort))
{
// check if we need to ByPass useUpstreamProxy = true;
if (externalProxy.BypassLocalhost && NetworkHelper.IsLocalIpAddress(remoteHostName))
{ // check if we need to ByPass
useUpstreamProxy = false; if (externalProxy.BypassLocalhost && NetworkHelper.IsLocalIpAddress(remoteHostName))
} {
} useUpstreamProxy = false;
}
TcpClient tcpClient = null; }
CustomBufferedStream stream = null;
TcpClient tcpClient = null;
SslApplicationProtocol negotiatedApplicationProtocol = default; CustomBufferedStream stream = null;
try SslApplicationProtocol negotiatedApplicationProtocol = default;
{
tcpClient = new TcpClient(upStreamEndPoint) try
{ {
ReceiveTimeout = proxyServer.ConnectionTimeOutSeconds * 1000, tcpClient = new TcpClient(upStreamEndPoint)
SendTimeout = proxyServer.ConnectionTimeOutSeconds * 1000, {
SendBufferSize = proxyServer.BufferSize, ReceiveTimeout = proxyServer.ConnectionTimeOutSeconds * 1000,
ReceiveBufferSize = proxyServer.BufferSize, SendTimeout = proxyServer.ConnectionTimeOutSeconds * 1000,
LingerState = new LingerOption(true, proxyServer.TcpTimeWaitSeconds) SendBufferSize = proxyServer.BufferSize,
}; ReceiveBufferSize = proxyServer.BufferSize,
LingerState = new LingerOption(true, proxyServer.TcpTimeWaitSeconds)
//linux has a bug with socket reuse in .net core. };
if (proxyServer.ReuseSocket && RunTime.IsWindows || RunTime.IsRunningOnMono)
{ //linux has a bug with socket reuse in .net core.
tcpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); if (proxyServer.ReuseSocket && RunTime.IsWindows || RunTime.IsRunningOnMono)
} {
tcpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
// If this proxy uses another external proxy then create a tunnel request for HTTP/HTTPS connections }
if (useUpstreamProxy)
{ var hostname = useUpstreamProxy ? externalProxy.HostName : remoteHostName;
await tcpClient.ConnectAsync(externalProxy.HostName, externalProxy.Port); var port = useUpstreamProxy ? externalProxy.Port : remotePort;
}
else var ipAddresses = await Dns.GetHostAddressesAsync(hostname);
{ if (ipAddresses == null || ipAddresses.Length == 0)
await tcpClient.ConnectAsync(remoteHostName, remotePort); {
} throw new Exception($"Could not resolve the hostname {hostname}");
}
await proxyServer.InvokeConnectionCreateEvent(tcpClient, false);
if (session != null)
stream = new CustomBufferedStream(tcpClient.GetStream(), proxyServer.BufferPool, proxyServer.BufferSize); {
session.TimeLine["Dns Resolved"] = DateTime.Now;
if (useUpstreamProxy && (isConnect || isHttps)) }
{
var writer = new HttpRequestWriter(stream, proxyServer.BufferPool, proxyServer.BufferSize); for (int i = 0; i < ipAddresses.Length; i++)
var connectRequest = new ConnectRequest {
{ try
OriginalUrl = $"{remoteHostName}:{remotePort}", {
HttpVersion = httpVersion await tcpClient.ConnectAsync(ipAddresses[i], port);
}; break;
}
connectRequest.Headers.AddHeader(KnownHeaders.Connection, KnownHeaders.ConnectionKeepAlive); catch (Exception e)
{
if (!string.IsNullOrEmpty(externalProxy.UserName) && externalProxy.Password != null) if (i == ipAddresses.Length - 1)
{ {
connectRequest.Headers.AddHeader(HttpHeader.ProxyConnectionKeepAlive); throw new Exception($"Could not establish connection to {hostname}", e);
connectRequest.Headers.AddHeader( }
HttpHeader.GetProxyAuthorizationHeader(externalProxy.UserName, externalProxy.Password)); }
} }
await writer.WriteRequestAsync(connectRequest, cancellationToken: cancellationToken); if (session != null)
{
string httpStatus = await stream.ReadLineAsync(cancellationToken); session.TimeLine["Connection Established"] = DateTime.Now;
}
Response.ParseResponseLine(httpStatus, out _, out int statusCode, out string statusDescription);
await proxyServer.InvokeConnectionCreateEvent(tcpClient, false);
if (statusCode != 200 && !statusDescription.EqualsIgnoreCase("OK")
&& !statusDescription.EqualsIgnoreCase("Connection Established")) stream = new CustomBufferedStream(tcpClient.GetStream(), proxyServer.BufferPool, proxyServer.BufferSize);
{
throw new Exception("Upstream proxy failed to create a secure tunnel"); if (useUpstreamProxy && (isConnect || isHttps))
} {
var writer = new HttpRequestWriter(stream, proxyServer.BufferPool, proxyServer.BufferSize);
await stream.ReadAndIgnoreAllLinesAsync(cancellationToken); var connectRequest = new ConnectRequest
} {
OriginalUrl = $"{remoteHostName}:{remotePort}",
if (isHttps) HttpVersion = httpVersion
{ };
var sslStream = new SslStream(stream, false, proxyServer.ValidateServerCertificate,
proxyServer.SelectClientCertificate); connectRequest.Headers.AddHeader(KnownHeaders.Connection, KnownHeaders.ConnectionKeepAlive);
stream = new CustomBufferedStream(sslStream, proxyServer.BufferPool, proxyServer.BufferSize);
if (!string.IsNullOrEmpty(externalProxy.UserName) && externalProxy.Password != null)
var options = new SslClientAuthenticationOptions {
{ connectRequest.Headers.AddHeader(HttpHeader.ProxyConnectionKeepAlive);
ApplicationProtocols = applicationProtocols, connectRequest.Headers.AddHeader(
TargetHost = remoteHostName, HttpHeader.GetProxyAuthorizationHeader(externalProxy.UserName, externalProxy.Password));
ClientCertificates = null, }
EnabledSslProtocols = proxyServer.SupportedSslProtocols,
CertificateRevocationCheckMode = proxyServer.CheckCertificateRevocation await writer.WriteRequestAsync(connectRequest, cancellationToken: cancellationToken);
};
await sslStream.AuthenticateAsClientAsync(options, cancellationToken); string httpStatus = await stream.ReadLineAsync(cancellationToken);
#if NETCOREAPP2_1
negotiatedApplicationProtocol = sslStream.NegotiatedApplicationProtocol; Response.ParseResponseLine(httpStatus, out _, out int statusCode, out string statusDescription);
#endif
} if (statusCode != 200 && !statusDescription.EqualsIgnoreCase("OK")
} && !statusDescription.EqualsIgnoreCase("Connection Established"))
catch (Exception) {
{ throw new Exception("Upstream proxy failed to create a secure tunnel");
stream?.Dispose(); }
tcpClient?.Close();
throw; await stream.ReadAndIgnoreAllLinesAsync(cancellationToken);
} }
return new TcpServerConnection(proxyServer, tcpClient) if (isHttps)
{ {
UpStreamProxy = externalProxy, var sslStream = new SslStream(stream, false, proxyServer.ValidateServerCertificate,
UpStreamEndPoint = upStreamEndPoint, proxyServer.SelectClientCertificate);
HostName = remoteHostName, stream = new CustomBufferedStream(sslStream, proxyServer.BufferPool, proxyServer.BufferSize);
Port = remotePort,
IsHttps = isHttps, var options = new SslClientAuthenticationOptions
NegotiatedApplicationProtocol = negotiatedApplicationProtocol, {
UseUpstreamProxy = useUpstreamProxy, ApplicationProtocols = applicationProtocols,
StreamWriter = new HttpRequestWriter(stream, proxyServer.BufferPool, proxyServer.BufferSize), TargetHost = remoteHostName,
Stream = stream, ClientCertificates = null,
Version = httpVersion EnabledSslProtocols = proxyServer.SupportedSslProtocols,
}; CertificateRevocationCheckMode = proxyServer.CheckCertificateRevocation
} };
await sslStream.AuthenticateAsClientAsync(options, cancellationToken);
#if NETCOREAPP2_1
/// <summary> negotiatedApplicationProtocol = sslStream.NegotiatedApplicationProtocol;
/// Release connection back to cache. #endif
/// </summary>
/// <param name="connection">The Tcp server connection to return.</param> if (session != null)
/// <param name="close">Should we just close the connection instead of reusing?</param> {
internal async Task Release(TcpServerConnection connection, bool close = false) session.TimeLine["HTTPS Established"] = DateTime.Now;
{ }
if (connection == null)
{ }
return; }
} catch (Exception)
{
if (close || connection.IsWinAuthenticated || !server.EnableConnectionPool) stream?.Dispose();
{ tcpClient?.Close();
disposalBag.Add(connection); throw;
return; }
}
return new TcpServerConnection(proxyServer, tcpClient)
connection.LastAccess = DateTime.Now; {
UpStreamProxy = externalProxy,
try UpStreamEndPoint = upStreamEndPoint,
{ HostName = remoteHostName,
await @lock.WaitAsync(); Port = remotePort,
IsHttps = isHttps,
while (true) NegotiatedApplicationProtocol = negotiatedApplicationProtocol,
{ UseUpstreamProxy = useUpstreamProxy,
if (cache.TryGetValue(connection.CacheKey, out var existingConnections)) StreamWriter = new HttpRequestWriter(stream, proxyServer.BufferPool, proxyServer.BufferSize),
{ Stream = stream,
while (existingConnections.Count >= server.MaxCachedConnections) Version = httpVersion
{ };
if (existingConnections.TryDequeue(out var staleConnection)) }
{
disposalBag.Add(staleConnection);
} /// <summary>
} /// Release connection back to cache.
/// </summary>
existingConnections.Enqueue(connection); /// <param name="connection">The Tcp server connection to return.</param>
break; /// <param name="close">Should we just close the connection instead of reusing?</param>
} internal async Task Release(TcpServerConnection connection, bool close = false)
{
if (cache.TryAdd(connection.CacheKey, if (connection == null)
new ConcurrentQueue<TcpServerConnection>(new[] { connection }))) {
{ return;
break; }
}
} if (close || connection.IsWinAuthenticated || !server.EnableConnectionPool)
{
} disposalBag.Add(connection);
finally return;
{ }
@lock.Release();
} connection.LastAccess = DateTime.Now;
}
try
internal async Task Release(Task<TcpServerConnection> connectionCreateTask, bool closeServerConnection) {
{ await @lock.WaitAsync();
if (connectionCreateTask != null)
{ while (true)
TcpServerConnection connection = null; {
try if (cache.TryGetValue(connection.CacheKey, out var existingConnections))
{ {
connection = await connectionCreateTask; while (existingConnections.Count >= server.MaxCachedConnections)
} {
catch { } if (existingConnections.TryDequeue(out var staleConnection))
finally {
{ disposalBag.Add(staleConnection);
await Release(connection, closeServerConnection); }
} }
}
} existingConnections.Enqueue(connection);
break;
private async Task clearOutdatedConnections() }
{
while (runCleanUpTask) if (cache.TryAdd(connection.CacheKey,
{ new ConcurrentQueue<TcpServerConnection>(new[] { connection })))
try {
{ break;
foreach (var item in cache) }
{ }
var queue = item.Value;
}
while (queue.Count > 0) finally
{ {
if (queue.TryDequeue(out var connection)) @lock.Release();
{ }
var cutOff = DateTime.Now.AddSeconds(-1 * server.ConnectionTimeOutSeconds); }
if (!server.EnableConnectionPool
|| connection.LastAccess < cutOff) internal async Task Release(Task<TcpServerConnection> connectionCreateTask, bool closeServerConnection)
{ {
disposalBag.Add(connection); if (connectionCreateTask != null)
continue; {
} TcpServerConnection connection = null;
try
queue.Enqueue(connection); {
break; connection = await connectionCreateTask;
} }
} catch { }
} finally
{
try await Release(connection, closeServerConnection);
{ }
await @lock.WaitAsync(); }
}
//clear empty queues
var emptyKeys = cache.Where(x => x.Value.Count == 0).Select(x => x.Key).ToList(); private async Task clearOutdatedConnections()
foreach (string key in emptyKeys) {
{ while (runCleanUpTask)
cache.TryRemove(key, out var _); {
} try
} {
finally foreach (var item in cache)
{ {
@lock.Release(); var queue = item.Value;
}
while (queue.Count > 0)
while (!disposalBag.IsEmpty) {
{ if (queue.TryDequeue(out var connection))
if (disposalBag.TryTake(out var connection)) {
{ var cutOff = DateTime.Now.AddSeconds(-1 * server.ConnectionTimeOutSeconds);
connection?.Dispose(); if (!server.EnableConnectionPool
} || connection.LastAccess < cutOff)
} {
} disposalBag.Add(connection);
catch (Exception e) continue;
{ }
server.ExceptionFunc(new Exception("An error occurred when disposing server connections.", e));
} queue.Enqueue(connection);
finally break;
{ }
//cleanup every 3 seconds by default }
await Task.Delay(1000 * 3); }
}
try
} {
} await @lock.WaitAsync();
public void Dispose() //clear empty queues
{ var emptyKeys = cache.Where(x => x.Value.Count == 0).Select(x => x.Key).ToList();
runCleanUpTask = false; foreach (string key in emptyKeys)
{
try cache.TryRemove(key, out var _);
{ }
@lock.Wait(); }
finally
foreach (var queue in cache.Select(x => x.Value).ToList()) {
{ @lock.Release();
while (!queue.IsEmpty) }
{
if (queue.TryDequeue(out var connection)) while (!disposalBag.IsEmpty)
{ {
disposalBag.Add(connection); if (disposalBag.TryTake(out var connection))
} {
} connection?.Dispose();
} }
cache.Clear(); }
} }
finally catch (Exception e)
{ {
@lock.Release(); server.ExceptionFunc(new Exception("An error occurred when disposing server connections.", e));
} }
finally
while (!disposalBag.IsEmpty) {
{ //cleanup every 3 seconds by default
if (disposalBag.TryTake(out var connection)) await Task.Delay(1000 * 3);
{ }
connection?.Dispose();
} }
} }
}
} public void Dispose()
} {
runCleanUpTask = false;
try
{
@lock.Wait();
foreach (var queue in cache.Select(x => x.Value).ToList())
{
while (!queue.IsEmpty)
{
if (queue.TryDequeue(out var connection))
{
disposalBag.Add(connection);
}
}
}
cache.Clear();
}
finally
{
@lock.Release();
}
while (!disposalBag.IsEmpty)
{
if (disposalBag.TryTake(out var connection))
{
connection?.Dispose();
}
}
}
}
}
using System; using System;
using System.Net; using System.Net;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Exceptions; using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Shared; using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy namespace Titanium.Web.Proxy
{ {
public partial class ProxyServer public partial class ProxyServer
{ {
/// <summary> /// <summary>
/// Callback to authorize clients of this proxy instance. /// Callback to authorize clients of this proxy instance.
/// </summary> /// </summary>
/// <param name="session">The session event arguments.</param> /// <param name="session">The session event arguments.</param>
/// <returns>True if authorized.</returns> /// <returns>True if authorized.</returns>
private async Task<bool> checkAuthorization(SessionEventArgsBase session) private async Task<bool> checkAuthorization(SessionEventArgsBase session)
{ {
// If we are not authorizing clients return true // If we are not authorizing clients return true
if (ProxyBasicAuthenticateFunc == null && ProxySchemeAuthenticateFunc == null) if (ProxyBasicAuthenticateFunc == null && ProxySchemeAuthenticateFunc == null)
{ {
return true; return true;
} }
var httpHeaders = session.WebSession.Request.Headers; var httpHeaders = session.WebSession.Request.Headers;
try try
{ {
var header = httpHeaders.GetFirstHeader(KnownHeaders.ProxyAuthorization); var header = httpHeaders.GetFirstHeader(KnownHeaders.ProxyAuthorization);
if (header == null) if (header == null)
{ {
session.WebSession.Response = createAuthentication407Response("Proxy Authentication Required"); session.WebSession.Response = createAuthentication407Response("Proxy Authentication Required");
return false; return false;
} }
var headerValueParts = header.Value.Split(ProxyConstants.SpaceSplit); var headerValueParts = header.Value.Split(ProxyConstants.SpaceSplit);
if (headerValueParts.Length != 2) if (headerValueParts.Length != 2)
{ {
// Return not authorized // Return not authorized
session.WebSession.Response = createAuthentication407Response("Proxy Authentication Invalid"); session.WebSession.Response = createAuthentication407Response("Proxy Authentication Invalid");
return false; return false;
} }
if (ProxyBasicAuthenticateFunc != null) if (ProxyBasicAuthenticateFunc != null)
{ {
return await authenticateUserBasic(session, headerValueParts); return await authenticateUserBasic(session, headerValueParts);
} }
if (ProxySchemeAuthenticateFunc != null) if (ProxySchemeAuthenticateFunc != null)
{ {
var result = await ProxySchemeAuthenticateFunc(session, headerValueParts[0], headerValueParts[1]); var result = await ProxySchemeAuthenticateFunc(session, headerValueParts[0], headerValueParts[1]);
if (result.Result == ProxyAuthenticationResult.ContinuationNeeded) if (result.Result == ProxyAuthenticationResult.ContinuationNeeded)
{ {
session.WebSession.Response = createAuthentication407Response("Proxy Authentication Invalid", result.Continuation); session.WebSession.Response = createAuthentication407Response("Proxy Authentication Invalid", result.Continuation);
return false; return false;
} }
return result.Result == ProxyAuthenticationResult.Success; return result.Result == ProxyAuthenticationResult.Success;
} }
return false; return false;
} }
catch (Exception e) catch (Exception e)
{ {
ExceptionFunc(new ProxyAuthorizationException("Error whilst authorizing request", session, e, ExceptionFunc(new ProxyAuthorizationException("Error whilst authorizing request", session, e,
httpHeaders)); httpHeaders));
// Return not authorized // Return not authorized
session.WebSession.Response = createAuthentication407Response("Proxy Authentication Invalid"); session.WebSession.Response = createAuthentication407Response("Proxy Authentication Invalid");
return false; return false;
} }
} }
private async Task<bool> authenticateUserBasic(SessionEventArgsBase session, string[] headerValueParts) private async Task<bool> authenticateUserBasic(SessionEventArgsBase session, string[] headerValueParts)
{ {
if (!headerValueParts[0].EqualsIgnoreCase(KnownHeaders.ProxyAuthorizationBasic)) if (!headerValueParts[0].EqualsIgnoreCase(KnownHeaders.ProxyAuthorizationBasic))
{ {
// Return not authorized // Return not authorized
session.WebSession.Response = createAuthentication407Response("Proxy Authentication Invalid"); session.WebSession.Response = createAuthentication407Response("Proxy Authentication Invalid");
return false; return false;
} }
string decoded = Encoding.UTF8.GetString(Convert.FromBase64String(headerValueParts[1])); string decoded = Encoding.UTF8.GetString(Convert.FromBase64String(headerValueParts[1]));
int colonIndex = decoded.IndexOf(':'); int colonIndex = decoded.IndexOf(':');
if (colonIndex == -1) if (colonIndex == -1)
{ {
// Return not authorized // Return not authorized
session.WebSession.Response = createAuthentication407Response("Proxy Authentication Invalid"); session.WebSession.Response = createAuthentication407Response("Proxy Authentication Invalid");
return false; return false;
} }
string username = decoded.Substring(0, colonIndex); string username = decoded.Substring(0, colonIndex);
string password = decoded.Substring(colonIndex + 1); string password = decoded.Substring(colonIndex + 1);
bool authenticated = await ProxyBasicAuthenticateFunc(session, username, password); bool authenticated = await ProxyBasicAuthenticateFunc(session, username, password);
if (!authenticated) if (!authenticated)
{ {
session.WebSession.Response = createAuthentication407Response("Proxy Authentication Invalid"); session.WebSession.Response = createAuthentication407Response("Proxy Authentication Invalid");
} }
return authenticated; return authenticated;
} }
/// <summary> /// <summary>
/// Create an authentication required response. /// Create an authentication required response.
/// </summary> /// </summary>
/// <param name="description">Response description.</param> /// <param name="description">Response description.</param>
/// <returns></returns> /// <returns></returns>
private Response createAuthentication407Response(string description, string continuation = null) private Response createAuthentication407Response(string description, string continuation = null)
{ {
var response = new Response var response = new Response
{ {
HttpVersion = HttpHeader.Version11, HttpVersion = HttpHeader.Version11,
StatusCode = (int)HttpStatusCode.ProxyAuthenticationRequired, StatusCode = (int)HttpStatusCode.ProxyAuthenticationRequired,
StatusDescription = description StatusDescription = description
}; };
if (!string.IsNullOrWhiteSpace(continuation)) if (!string.IsNullOrWhiteSpace(continuation))
{ {
return createContinuationResponse(response, continuation); return createContinuationResponse(response, continuation);
} }
if (ProxyBasicAuthenticateFunc != null) if (ProxyBasicAuthenticateFunc != null)
{ {
response.Headers.AddHeader(KnownHeaders.ProxyAuthenticate, $"Basic realm=\"{ProxyAuthenticationRealm}\""); response.Headers.AddHeader(KnownHeaders.ProxyAuthenticate, $"Basic realm=\"{ProxyAuthenticationRealm}\"");
} }
if (ProxySchemeAuthenticateFunc != null) if (ProxySchemeAuthenticateFunc != null)
{ {
foreach (var scheme in ProxyAuthenticationSchemes) foreach (var scheme in ProxyAuthenticationSchemes)
{ {
response.Headers.AddHeader(KnownHeaders.ProxyAuthenticate, scheme); response.Headers.AddHeader(KnownHeaders.ProxyAuthenticate, scheme);
} }
} }
response.Headers.AddHeader(KnownHeaders.ProxyConnection, KnownHeaders.ProxyConnectionClose); response.Headers.AddHeader(KnownHeaders.ProxyConnection, KnownHeaders.ProxyConnectionClose);
response.Headers.FixProxyHeaders(); response.Headers.FixProxyHeaders();
return response; return response;
} }
private Response createContinuationResponse(Response response, string continuation) private Response createContinuationResponse(Response response, string continuation)
{ {
response.Headers.AddHeader(KnownHeaders.ProxyAuthenticate, continuation); response.Headers.AddHeader(KnownHeaders.ProxyAuthenticate, continuation);
response.Headers.AddHeader(KnownHeaders.ProxyConnection, KnownHeaders.ConnectionKeepAlive); response.Headers.AddHeader(KnownHeaders.ProxyConnection, KnownHeaders.ConnectionKeepAlive);
response.Headers.FixProxyHeaders(); response.Headers.FixProxyHeaders();
return response; return response;
} }
} }
} }
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Security.Authentication; using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended; using StreamExtended;
using StreamExtended.Network; using StreamExtended.Network;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Helpers.WinHttp; using Titanium.Web.Proxy.Helpers.WinHttp;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network; using Titanium.Web.Proxy.Network;
using Titanium.Web.Proxy.Network.Tcp; using Titanium.Web.Proxy.Network.Tcp;
namespace Titanium.Web.Proxy namespace Titanium.Web.Proxy
{ {
/// <inheritdoc /> /// <inheritdoc />
/// <summary> /// <summary>
/// This class is the backbone of proxy. One can create as many instances as needed. /// This class is the backbone of proxy. One can create as many instances as needed.
/// However care should be taken to avoid using the same listening ports across multiple instances. /// However care should be taken to avoid using the same listening ports across multiple instances.
/// </summary> /// </summary>
public partial class ProxyServer : IDisposable public partial class ProxyServer : IDisposable
{ {
/// <summary> /// <summary>
/// HTTP &amp; HTTPS scheme shorthands. /// HTTP &amp; HTTPS scheme shorthands.
/// </summary> /// </summary>
internal static readonly string UriSchemeHttp = Uri.UriSchemeHttp; internal static readonly string UriSchemeHttp = Uri.UriSchemeHttp;
internal static readonly string UriSchemeHttps = Uri.UriSchemeHttps; internal static readonly string UriSchemeHttps = Uri.UriSchemeHttps;
/// <summary> /// <summary>
/// A default exception log func. /// A default exception log func.
/// </summary> /// </summary>
private readonly ExceptionHandler defaultExceptionFunc = e => { }; private readonly ExceptionHandler defaultExceptionFunc = e => { };
/// <summary> /// <summary>
/// Backing field for exposed public property. /// Backing field for exposed public property.
/// </summary> /// </summary>
private int clientConnectionCount; private int clientConnectionCount;
/// <summary> /// <summary>
/// Backing field for exposed public property. /// Backing field for exposed public property.
/// </summary> /// </summary>
private ExceptionHandler exceptionFunc; private ExceptionHandler exceptionFunc;
/// <summary> /// <summary>
/// Backing field for exposed public property. /// Backing field for exposed public property.
/// </summary> /// </summary>
private int serverConnectionCount; private int serverConnectionCount;
/// <summary> /// <summary>
/// Upstream proxy manager. /// Upstream proxy manager.
/// </summary> /// </summary>
private WinHttpWebProxyFinder systemProxyResolver; private WinHttpWebProxyFinder systemProxyResolver;
/// <inheritdoc /> /// <inheritdoc />
/// <summary> /// <summary>
/// Initializes a new instance of ProxyServer class with provided parameters. /// Initializes a new instance of ProxyServer class with provided parameters.
/// </summary> /// </summary>
/// <param name="userTrustRootCertificate"> /// <param name="userTrustRootCertificate">
/// Should fake HTTPS certificate be trusted by this machine's user certificate /// Should fake HTTPS certificate be trusted by this machine's user certificate
/// store? /// store?
/// </param> /// </param>
/// <param name="machineTrustRootCertificate">Should fake HTTPS certificate be trusted by this machine's certificate store?</param> /// <param name="machineTrustRootCertificate">Should fake HTTPS certificate be trusted by this machine's certificate store?</param>
/// <param name="trustRootCertificateAsAdmin"> /// <param name="trustRootCertificateAsAdmin">
/// Should we attempt to trust certificates with elevated permissions by /// Should we attempt to trust certificates with elevated permissions by
/// prompting for UAC if required? /// prompting for UAC if required?
/// </param> /// </param>
public ProxyServer(bool userTrustRootCertificate = true, bool machineTrustRootCertificate = false, public ProxyServer(bool userTrustRootCertificate = true, bool machineTrustRootCertificate = false,
bool trustRootCertificateAsAdmin = false) : this(null, null, userTrustRootCertificate, bool trustRootCertificateAsAdmin = false) : this(null, null, userTrustRootCertificate,
machineTrustRootCertificate, trustRootCertificateAsAdmin) machineTrustRootCertificate, trustRootCertificateAsAdmin)
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of ProxyServer class with provided parameters. /// Initializes a new instance of ProxyServer class with provided parameters.
/// </summary> /// </summary>
/// <param name="rootCertificateName">Name of the root certificate.</param> /// <param name="rootCertificateName">Name of the root certificate.</param>
/// <param name="rootCertificateIssuerName">Name of the root certificate issuer.</param> /// <param name="rootCertificateIssuerName">Name of the root certificate issuer.</param>
/// <param name="userTrustRootCertificate"> /// <param name="userTrustRootCertificate">
/// Should fake HTTPS certificate be trusted by this machine's user certificate /// Should fake HTTPS certificate be trusted by this machine's user certificate
/// store? /// store?
/// </param> /// </param>
/// <param name="machineTrustRootCertificate">Should fake HTTPS certificate be trusted by this machine's certificate store?</param> /// <param name="machineTrustRootCertificate">Should fake HTTPS certificate be trusted by this machine's certificate store?</param>
/// <param name="trustRootCertificateAsAdmin"> /// <param name="trustRootCertificateAsAdmin">
/// Should we attempt to trust certificates with elevated permissions by /// Should we attempt to trust certificates with elevated permissions by
/// prompting for UAC if required? /// prompting for UAC if required?
/// </param> /// </param>
public ProxyServer(string rootCertificateName, string rootCertificateIssuerName, public ProxyServer(string rootCertificateName, string rootCertificateIssuerName,
bool userTrustRootCertificate = true, bool machineTrustRootCertificate = false, bool userTrustRootCertificate = true, bool machineTrustRootCertificate = false,
bool trustRootCertificateAsAdmin = false) bool trustRootCertificateAsAdmin = false)
{ {
// default values
ConnectionTimeOutSeconds = 60; if (BufferPool == null)
{
if (BufferPool == null) BufferPool = new DefaultBufferPool();
{ }
BufferPool = new DefaultBufferPool();
} ProxyEndPoints = new List<ProxyEndPoint>();
tcpConnectionFactory = new TcpConnectionFactory(this);
ProxyEndPoints = new List<ProxyEndPoint>(); if (!RunTime.IsRunningOnMono && RunTime.IsWindows)
tcpConnectionFactory = new TcpConnectionFactory(this); {
if (!RunTime.IsRunningOnMono && RunTime.IsWindows) systemProxySettingsManager = new SystemProxyManager();
{ }
systemProxySettingsManager = new SystemProxyManager();
} CertificateManager = new CertificateManager(rootCertificateName, rootCertificateIssuerName,
userTrustRootCertificate, machineTrustRootCertificate, trustRootCertificateAsAdmin, ExceptionFunc);
CertificateManager = new CertificateManager(rootCertificateName, rootCertificateIssuerName, }
userTrustRootCertificate, machineTrustRootCertificate, trustRootCertificateAsAdmin, ExceptionFunc);
} /// <summary>
/// An factory that creates tcp connection to server.
/// <summary> /// </summary>
/// An factory that creates tcp connection to server. private TcpConnectionFactory tcpConnectionFactory { get; }
/// </summary>
private TcpConnectionFactory tcpConnectionFactory { get; } /// <summary>
/// Manage system proxy settings.
/// <summary> /// </summary>
/// Manage system proxy settings. private SystemProxyManager systemProxySettingsManager { get; }
/// </summary>
private SystemProxyManager systemProxySettingsManager { get; } //Number of exception retries when connection pool is enabled.
private int retries => EnableConnectionPool ? MaxCachedConnections : 0;
//Number of exception retries when connection pool is enabled.
private int retries => EnableConnectionPool ? MaxCachedConnections : 0; /// <summary>
/// Is the proxy currently running?
/// <summary> /// </summary>
/// Is the proxy currently running? public bool ProxyRunning { get; private set; }
/// </summary>
public bool ProxyRunning { get; private set; } /// <summary>
/// Gets or sets a value indicating whether requests will be chained to upstream gateway.
/// <summary> /// Defaults to false.
/// Gets or sets a value indicating whether requests will be chained to upstream gateway. /// </summary>
/// Defaults to false. public bool ForwardToUpstreamGateway { get; set; }
/// </summary>
public bool ForwardToUpstreamGateway { get; set; } /// <summary>
/// Enable disable Windows Authentication (NTLM/Kerberos).
/// <summary> /// Note: NTLM/Kerberos will always send local credentials of current user
/// Enable disable Windows Authentication (NTLM/Kerberos). /// running the proxy process. This is because a man
/// Note: NTLM/Kerberos will always send local credentials of current user /// in middle attack with Windows domain authentication is not currently supported.
/// running the proxy process. This is because a man /// Defaults to false.
/// in middle attack with Windows domain authentication is not currently supported. /// </summary>
/// Defaults to false. public bool EnableWinAuth { get; set; }
/// </summary>
public bool EnableWinAuth { get; set; } /// <summary>
/// Should we check for certificare revocation during SSL authentication to servers
/// <summary> /// Note: If enabled can reduce performance. Defaults to false.
/// Should we check for certificare revocation during SSL authentication to servers /// </summary>
/// Note: If enabled can reduce performance. Defaults to false. public X509RevocationMode CheckCertificateRevocation { get; set; }
/// </summary>
public X509RevocationMode CheckCertificateRevocation { get; set; } /// <summary>
/// Does this proxy uses the HTTP protocol 100 continue behaviour strictly?
/// <summary> /// Broken 100 contunue implementations on server/client may cause problems if enabled.
/// Does this proxy uses the HTTP protocol 100 continue behaviour strictly? /// Defaults to false.
/// Broken 100 contunue implementations on server/client may cause problems if enabled. /// </summary>
/// Defaults to false. public bool Enable100ContinueBehaviour { get; set; }
/// </summary>
public bool Enable100ContinueBehaviour { get; set; } /// <summary>
/// Should we enable experimental server connection pool?
/// <summary> /// Defaults to true.
/// Should we enable experimental server connection pool? /// </summary>
/// Defaults to disable. public bool EnableConnectionPool { get; set; } = true;
/// </summary>
public bool EnableConnectionPool { get; set; } /// <summary>
/// Should we enable tcp server connection prefetching?
/// <summary> /// When enabled, as soon as we receive a client connection we concurrently initiate
/// Buffer size in bytes used throughout this proxy. /// corresponding server connection process using CONNECT hostname or SNI hostname on a separate task so that after parsing client request
/// Default value is 8192 bytes. /// we will have the server connection immediately ready or in the process of getting ready.
/// </summary> /// If a server connection is available in cache then this prefetch task will immediatly return with the available connection from cache.
public int BufferSize { get; set; } = 8192; /// Defaults to true.
/// </summary>
/// <summary> public bool EnableTcpServerConnectionPrefetch { get; set; } = true;
/// Seconds client/server connection are to be kept alive when waiting for read/write to complete.
/// This will also determine the pool eviction time when connection pool is enabled. /// <summary>
/// Default value is 60 seconds. /// Buffer size in bytes used throughout this proxy.
/// </summary> /// Default value is 8192 bytes.
public int ConnectionTimeOutSeconds { get; set; } /// </summary>
public int BufferSize { get; set; } = 8192;
/// <summary>
/// Maximum number of concurrent connections per remote host in cache. /// <summary>
/// Only valid when connection pooling is enabled. /// Seconds client/server connection are to be kept alive when waiting for read/write to complete.
/// Default value is 2. /// This will also determine the pool eviction time when connection pool is enabled.
/// </summary> /// Default value is 60 seconds.
public int MaxCachedConnections { get; set; } = 2; /// </summary>
public int ConnectionTimeOutSeconds { get; set; } = 60;
/// <summary>
/// Number of seconds to linger when Tcp connection is in TIME_WAIT state. /// <summary>
/// Default value is 30. /// Maximum number of concurrent connections per remote host in cache.
/// </summary> /// Only valid when connection pooling is enabled.
public int TcpTimeWaitSeconds { get; set; } = 30; /// Default value is 2.
/// </summary>
/// <summary> public int MaxCachedConnections { get; set; } = 2;
/// Should we reuse client/server tcp sockets.
/// Default is true (disabled for linux/macOS due to bug in .Net core). /// <summary>
/// </summary> /// Number of seconds to linger when Tcp connection is in TIME_WAIT state.
public bool ReuseSocket { get; set; } = true; /// Default value is 30.
/// </summary>
/// <summary> public int TcpTimeWaitSeconds { get; set; } = 30;
/// Total number of active client connections.
/// </summary> /// <summary>
public int ClientConnectionCount => clientConnectionCount; /// Should we reuse client/server tcp sockets.
/// Default is true (disabled for linux/macOS due to bug in .Net core).
/// <summary> /// </summary>
/// Total number of active server connections. public bool ReuseSocket { get; set; } = true;
/// </summary>
public int ServerConnectionCount => serverConnectionCount; /// <summary>
/// Total number of active client connections.
/// <summary> /// </summary>
/// Realm used during Proxy Basic Authentication. public int ClientConnectionCount => clientConnectionCount;
/// </summary>
public string ProxyAuthenticationRealm { get; set; } = "TitaniumProxy"; /// <summary>
/// Total number of active server connections.
/// <summary> /// </summary>
/// List of supported Ssl versions. public int ServerConnectionCount => serverConnectionCount;
/// </summary>
public SslProtocols SupportedSslProtocols { get; set; } = /// <summary>
#if NET45 /// Realm used during Proxy Basic Authentication.
SslProtocols.Ssl3 | /// </summary>
#endif public string ProxyAuthenticationRealm { get; set; } = "TitaniumProxy";
SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12;
/// <summary>
/// <summary> /// List of supported Ssl versions.
/// The buffer pool used throughout this proxy instance. /// </summary>
/// Set custom implementations by implementing this interface. public SslProtocols SupportedSslProtocols { get; set; } =
/// By default this uses DefaultBufferPool implementation available in StreamExtended library package. #if NET45
/// </summary> SslProtocols.Ssl3 |
public IBufferPool BufferPool { get; set; } #endif
SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12;
/// <summary>
/// Manages certificates used by this proxy. /// <summary>
/// </summary> /// The buffer pool used throughout this proxy instance.
public CertificateManager CertificateManager { get; } /// Set custom implementations by implementing this interface.
/// By default this uses DefaultBufferPool implementation available in StreamExtended library package.
/// <summary> /// </summary>
/// External proxy used for Http requests. public IBufferPool BufferPool { get; set; }
/// </summary>
public ExternalProxy UpStreamHttpProxy { get; set; } /// <summary>
/// Manages certificates used by this proxy.
/// <summary> /// </summary>
/// External proxy used for Https requests. public CertificateManager CertificateManager { get; }
/// </summary>
public ExternalProxy UpStreamHttpsProxy { get; set; } /// <summary>
/// External proxy used for Http requests.
/// <summary> /// </summary>
/// Local adapter/NIC endpoint where proxy makes request via. public ExternalProxy UpStreamHttpProxy { get; set; }
/// Defaults via any IP addresses of this machine.
/// </summary> /// <summary>
public IPEndPoint UpStreamEndPoint { get; set; } = new IPEndPoint(IPAddress.Any, 0); /// External proxy used for Https requests.
/// </summary>
/// <summary> public ExternalProxy UpStreamHttpsProxy { get; set; }
/// A list of IpAddress and port this proxy is listening to.
/// </summary> /// <summary>
public List<ProxyEndPoint> ProxyEndPoints { get; set; } /// Local adapter/NIC endpoint where proxy makes request via.
/// Defaults via any IP addresses of this machine.
/// <summary> /// </summary>
/// A callback to provide authentication credentials for up stream proxy this proxy is using for HTTP(S) requests. public IPEndPoint UpStreamEndPoint { get; set; } = new IPEndPoint(IPAddress.Any, 0);
/// User should return the ExternalProxy object with valid credentials.
/// </summary> /// <summary>
public Func<SessionEventArgsBase, Task<ExternalProxy>> GetCustomUpStreamProxyFunc { get; set; } /// A list of IpAddress and port this proxy is listening to.
/// </summary>
/// <summary> public List<ProxyEndPoint> ProxyEndPoints { get; set; }
/// Callback for error events in this proxy instance.
/// </summary> /// <summary>
public ExceptionHandler ExceptionFunc /// A callback to provide authentication credentials for up stream proxy this proxy is using for HTTP(S) requests.
{ /// User should return the ExternalProxy object with valid credentials.
get => exceptionFunc ?? defaultExceptionFunc; /// </summary>
set public Func<SessionEventArgsBase, Task<ExternalProxy>> GetCustomUpStreamProxyFunc { get; set; }
{
exceptionFunc = value; /// <summary>
CertificateManager.ExceptionFunc = value; /// Callback for error events in this proxy instance.
} /// </summary>
} public ExceptionHandler ExceptionFunc
{
/// <summary> get => exceptionFunc ?? defaultExceptionFunc;
/// A callback to authenticate proxy clients via basic authentication. set
/// Parameters are username and password as provided by client. {
/// Should return true for successful authentication. exceptionFunc = value;
/// </summary> CertificateManager.ExceptionFunc = value;
public Func<SessionEventArgsBase, string, string, Task<bool>> ProxyBasicAuthenticateFunc { get; set; } }
}
/// <summary>
/// A pluggable callback to authenticate clients by scheme instead of requiring basic authentication through ProxyBasicAuthenticateFunc. /// <summary>
/// Parameters are current working session, schemeType, and token as provided by a calling client. /// A callback to authenticate proxy clients via basic authentication.
/// Should return success for successful authentication, continuation if the package requests, or failure. /// Parameters are username and password as provided by client.
/// </summary> /// Should return true for successful authentication.
public Func<SessionEventArgsBase, string, string, Task<ProxyAuthenticationContext>> ProxySchemeAuthenticateFunc { get; set; } /// </summary>
public Func<SessionEventArgsBase, string, string, Task<bool>> ProxyBasicAuthenticateFunc { get; set; }
/// <summary>
/// A collection of scheme types, e.g. basic, NTLM, Kerberos, Negotiate, to return if scheme authentication is required. /// <summary>
/// Works in relation with ProxySchemeAuthenticateFunc. /// A pluggable callback to authenticate clients by scheme instead of requiring basic authentication through ProxyBasicAuthenticateFunc.
/// </summary> /// Parameters are current working session, schemeType, and token as provided by a calling client.
public IEnumerable<string> ProxyAuthenticationSchemes { get; set; } = new string[0]; /// Should return success for successful authentication, continuation if the package requests, or failure.
/// </summary>
/// <summary> public Func<SessionEventArgsBase, string, string, Task<ProxyAuthenticationContext>> ProxySchemeAuthenticateFunc { get; set; }
/// Event occurs when client connection count changed.
/// </summary> /// <summary>
public event EventHandler ClientConnectionCountChanged; /// A collection of scheme types, e.g. basic, NTLM, Kerberos, Negotiate, to return if scheme authentication is required.
/// Works in relation with ProxySchemeAuthenticateFunc.
/// <summary> /// </summary>
/// Event occurs when server connection count changed. public IEnumerable<string> ProxyAuthenticationSchemes { get; set; } = new string[0];
/// </summary>
public event EventHandler ServerConnectionCountChanged; /// <summary>
/// Event occurs when client connection count changed.
/// <summary> /// </summary>
/// Event to override the default verification logic of remote SSL certificate received during authentication. public event EventHandler ClientConnectionCountChanged;
/// </summary>
public event AsyncEventHandler<CertificateValidationEventArgs> ServerCertificateValidationCallback; /// <summary>
/// Event occurs when server connection count changed.
/// <summary> /// </summary>
/// Event to override client certificate selection during mutual SSL authentication. public event EventHandler ServerConnectionCountChanged;
/// </summary>
public event AsyncEventHandler<CertificateSelectionEventArgs> ClientCertificateSelectionCallback; /// <summary>
/// Event to override the default verification logic of remote SSL certificate received during authentication.
/// <summary> /// </summary>
/// Intercept request event to server. public event AsyncEventHandler<CertificateValidationEventArgs> ServerCertificateValidationCallback;
/// </summary>
public event AsyncEventHandler<SessionEventArgs> BeforeRequest; /// <summary>
/// Event to override client certificate selection during mutual SSL authentication.
/// <summary> /// </summary>
/// Intercept response event from server. public event AsyncEventHandler<CertificateSelectionEventArgs> ClientCertificateSelectionCallback;
/// </summary>
public event AsyncEventHandler<SessionEventArgs> BeforeResponse; /// <summary>
/// Intercept request event to server.
/// <summary> /// </summary>
/// Intercept after response event from server. public event AsyncEventHandler<SessionEventArgs> BeforeRequest;
/// </summary>
public event AsyncEventHandler<SessionEventArgs> AfterResponse; /// <summary>
/// Intercept response event from server.
/// <summary> /// </summary>
/// Customize TcpClient used for client connection upon create. public event AsyncEventHandler<SessionEventArgs> BeforeResponse;
/// </summary>
public event AsyncEventHandler<TcpClient> OnClientConnectionCreate; /// <summary>
/// Intercept after response event from server.
/// <summary> /// </summary>
/// Customize TcpClient used for server connection upon create. public event AsyncEventHandler<SessionEventArgs> AfterResponse;
/// </summary>
public event AsyncEventHandler<TcpClient> OnServerConnectionCreate; /// <summary>
/// Customize TcpClient used for client connection upon create.
/// <summary> /// </summary>
/// Add a proxy end point. public event AsyncEventHandler<TcpClient> OnClientConnectionCreate;
/// </summary>
/// <param name="endPoint">The proxy endpoint.</param> /// <summary>
public void AddEndPoint(ProxyEndPoint endPoint) /// Customize TcpClient used for server connection upon create.
{ /// </summary>
if (ProxyEndPoints.Any(x => public event AsyncEventHandler<TcpClient> OnServerConnectionCreate;
x.IpAddress.Equals(endPoint.IpAddress) && endPoint.Port != 0 && x.Port == endPoint.Port))
{ /// <summary>
throw new Exception("Cannot add another endpoint to same port & ip address"); /// Add a proxy end point.
} /// </summary>
/// <param name="endPoint">The proxy endpoint.</param>
ProxyEndPoints.Add(endPoint); public void AddEndPoint(ProxyEndPoint endPoint)
{
if (ProxyRunning) if (ProxyEndPoints.Any(x =>
{ x.IpAddress.Equals(endPoint.IpAddress) && endPoint.Port != 0 && x.Port == endPoint.Port))
listen(endPoint); {
} throw new Exception("Cannot add another endpoint to same port & ip address");
} }
/// <summary> ProxyEndPoints.Add(endPoint);
/// Remove a proxy end point.
/// Will throw error if the end point does'nt exist. if (ProxyRunning)
/// </summary> {
/// <param name="endPoint">The existing endpoint to remove.</param> listen(endPoint);
public void RemoveEndPoint(ProxyEndPoint endPoint) }
{ }
if (ProxyEndPoints.Contains(endPoint) == false)
{ /// <summary>
throw new Exception("Cannot remove endPoints not added to proxy"); /// Remove a proxy end point.
} /// Will throw error if the end point does'nt exist.
/// </summary>
ProxyEndPoints.Remove(endPoint); /// <param name="endPoint">The existing endpoint to remove.</param>
public void RemoveEndPoint(ProxyEndPoint endPoint)
if (ProxyRunning) {
{ if (ProxyEndPoints.Contains(endPoint) == false)
quitListen(endPoint); {
} throw new Exception("Cannot remove endPoints not added to proxy");
} }
/// <summary> ProxyEndPoints.Remove(endPoint);
/// Set the given explicit end point as the default proxy server for current machine.
/// </summary> if (ProxyRunning)
/// <param name="endPoint">The explicit endpoint.</param> {
public void SetAsSystemHttpProxy(ExplicitProxyEndPoint endPoint) quitListen(endPoint);
{ }
SetAsSystemProxy(endPoint, ProxyProtocolType.Http); }
}
/// <summary>
/// <summary> /// Set the given explicit end point as the default proxy server for current machine.
/// Set the given explicit end point as the default proxy server for current machine. /// </summary>
/// </summary> /// <param name="endPoint">The explicit endpoint.</param>
/// <param name="endPoint">The explicit endpoint.</param> public void SetAsSystemHttpProxy(ExplicitProxyEndPoint endPoint)
public void SetAsSystemHttpsProxy(ExplicitProxyEndPoint endPoint) {
{ SetAsSystemProxy(endPoint, ProxyProtocolType.Http);
SetAsSystemProxy(endPoint, ProxyProtocolType.Https); }
}
/// <summary>
/// <summary> /// Set the given explicit end point as the default proxy server for current machine.
/// Set the given explicit end point as the default proxy server for current machine. /// </summary>
/// </summary> /// <param name="endPoint">The explicit endpoint.</param>
/// <param name="endPoint">The explicit endpoint.</param> public void SetAsSystemHttpsProxy(ExplicitProxyEndPoint endPoint)
/// <param name="protocolType">The proxy protocol type.</param> {
public void SetAsSystemProxy(ExplicitProxyEndPoint endPoint, ProxyProtocolType protocolType) SetAsSystemProxy(endPoint, ProxyProtocolType.Https);
{ }
if (RunTime.IsRunningOnMono)
{ /// <summary>
throw new Exception("Mono Runtime do not support system proxy settings."); /// Set the given explicit end point as the default proxy server for current machine.
} /// </summary>
/// <param name="endPoint">The explicit endpoint.</param>
validateEndPointAsSystemProxy(endPoint); /// <param name="protocolType">The proxy protocol type.</param>
public void SetAsSystemProxy(ExplicitProxyEndPoint endPoint, ProxyProtocolType protocolType)
bool isHttp = (protocolType & ProxyProtocolType.Http) > 0; {
bool isHttps = (protocolType & ProxyProtocolType.Https) > 0; if (RunTime.IsRunningOnMono)
{
if (isHttps) throw new Exception("Mono Runtime do not support system proxy settings.");
{ }
CertificateManager.EnsureRootCertificate();
validateEndPointAsSystemProxy(endPoint);
// If certificate was trusted by the machine
if (!CertificateManager.CertValidated) bool isHttp = (protocolType & ProxyProtocolType.Http) > 0;
{ bool isHttps = (protocolType & ProxyProtocolType.Https) > 0;
protocolType = protocolType & ~ProxyProtocolType.Https;
isHttps = false; if (isHttps)
} {
} CertificateManager.EnsureRootCertificate();
// clear any settings previously added // If certificate was trusted by the machine
if (isHttp) if (!CertificateManager.CertValidated)
{ {
ProxyEndPoints.OfType<ExplicitProxyEndPoint>().ToList().ForEach(x => x.IsSystemHttpProxy = false); protocolType = protocolType & ~ProxyProtocolType.Https;
} isHttps = false;
}
if (isHttps) }
{
ProxyEndPoints.OfType<ExplicitProxyEndPoint>().ToList().ForEach(x => x.IsSystemHttpsProxy = false); // clear any settings previously added
} if (isHttp)
{
systemProxySettingsManager.SetProxy( ProxyEndPoints.OfType<ExplicitProxyEndPoint>().ToList().ForEach(x => x.IsSystemHttpProxy = false);
Equals(endPoint.IpAddress, IPAddress.Any) | }
Equals(endPoint.IpAddress, IPAddress.Loopback)
? "localhost" if (isHttps)
: endPoint.IpAddress.ToString(), {
endPoint.Port, ProxyEndPoints.OfType<ExplicitProxyEndPoint>().ToList().ForEach(x => x.IsSystemHttpsProxy = false);
protocolType); }
if (isHttp) systemProxySettingsManager.SetProxy(
{ Equals(endPoint.IpAddress, IPAddress.Any) |
endPoint.IsSystemHttpProxy = true; Equals(endPoint.IpAddress, IPAddress.Loopback)
} ? "localhost"
: endPoint.IpAddress.ToString(),
if (isHttps) endPoint.Port,
{ protocolType);
endPoint.IsSystemHttpsProxy = true;
} if (isHttp)
{
string proxyType = null; endPoint.IsSystemHttpProxy = true;
switch (protocolType) }
{
case ProxyProtocolType.Http: if (isHttps)
proxyType = "HTTP"; {
break; endPoint.IsSystemHttpsProxy = true;
case ProxyProtocolType.Https: }
proxyType = "HTTPS";
break; string proxyType = null;
case ProxyProtocolType.AllHttp: switch (protocolType)
proxyType = "HTTP and HTTPS"; {
break; case ProxyProtocolType.Http:
} proxyType = "HTTP";
break;
if (protocolType != ProxyProtocolType.None) case ProxyProtocolType.Https:
{ proxyType = "HTTPS";
Console.WriteLine("Set endpoint at Ip {0} and port: {1} as System {2} Proxy", endPoint.IpAddress, break;
endPoint.Port, proxyType); case ProxyProtocolType.AllHttp:
} proxyType = "HTTP and HTTPS";
} break;
}
/// <summary>
/// Clear HTTP proxy settings of current machine. if (protocolType != ProxyProtocolType.None)
/// </summary> {
public void DisableSystemHttpProxy() Console.WriteLine("Set endpoint at Ip {0} and port: {1} as System {2} Proxy", endPoint.IpAddress,
{ endPoint.Port, proxyType);
DisableSystemProxy(ProxyProtocolType.Http); }
} }
/// <summary> /// <summary>
/// Clear HTTPS proxy settings of current machine. /// Clear HTTP proxy settings of current machine.
/// </summary> /// </summary>
public void DisableSystemHttpsProxy() public void DisableSystemHttpProxy()
{ {
DisableSystemProxy(ProxyProtocolType.Https); DisableSystemProxy(ProxyProtocolType.Http);
} }
/// <summary> /// <summary>
/// Clear the specified proxy setting for current machine. /// Clear HTTPS proxy settings of current machine.
/// </summary> /// </summary>
public void DisableSystemProxy(ProxyProtocolType protocolType) public void DisableSystemHttpsProxy()
{ {
if (RunTime.IsRunningOnMono) DisableSystemProxy(ProxyProtocolType.Https);
{ }
throw new Exception("Mono Runtime do not support system proxy settings.");
} /// <summary>
/// Clear the specified proxy setting for current machine.
systemProxySettingsManager.RemoveProxy(protocolType); /// </summary>
} public void DisableSystemProxy(ProxyProtocolType protocolType)
{
/// <summary> if (RunTime.IsRunningOnMono)
/// Clear all proxy settings for current machine. {
/// </summary> throw new Exception("Mono Runtime do not support system proxy settings.");
public void DisableAllSystemProxies() }
{
if (RunTime.IsRunningOnMono) systemProxySettingsManager.RemoveProxy(protocolType);
{ }
throw new Exception("Mono Runtime do not support system proxy settings.");
} /// <summary>
/// Clear all proxy settings for current machine.
systemProxySettingsManager.DisableAllProxy(); /// </summary>
} public void DisableAllSystemProxies()
{
/// <summary> if (RunTime.IsRunningOnMono)
/// Start this proxy server instance. {
/// </summary> throw new Exception("Mono Runtime do not support system proxy settings.");
public void Start() }
{
if (ProxyRunning) systemProxySettingsManager.DisableAllProxy();
{ }
throw new Exception("Proxy is already running.");
} /// <summary>
/// Start this proxy server instance.
if (ProxyEndPoints.OfType<ExplicitProxyEndPoint>().Any(x => x.GenericCertificate == null)) /// </summary>
{ public void Start()
CertificateManager.EnsureRootCertificate(); {
} if (ProxyRunning)
{
// clear any system proxy settings which is pointing to our own endpoint (causing a cycle) throw new Exception("Proxy is already running.");
// due to ungracious proxy shutdown before or something else }
if (systemProxySettingsManager != null && RunTime.IsWindows)
{ if (ProxyEndPoints.OfType<ExplicitProxyEndPoint>().Any(x => x.GenericCertificate == null))
var proxyInfo = systemProxySettingsManager.GetProxyInfoFromRegistry(); {
if (proxyInfo.Proxies != null) CertificateManager.EnsureRootCertificate();
{ }
var protocolToRemove = ProxyProtocolType.None;
foreach (var proxy in proxyInfo.Proxies.Values) // clear any system proxy settings which is pointing to our own endpoint (causing a cycle)
{ // due to ungracious proxy shutdown before or something else
if (NetworkHelper.IsLocalIpAddress(proxy.HostName) if (systemProxySettingsManager != null && RunTime.IsWindows)
&& ProxyEndPoints.Any(x => x.Port == proxy.Port)) {
{ var proxyInfo = systemProxySettingsManager.GetProxyInfoFromRegistry();
protocolToRemove |= proxy.ProtocolType; if (proxyInfo.Proxies != null)
} {
} var protocolToRemove = ProxyProtocolType.None;
foreach (var proxy in proxyInfo.Proxies.Values)
if (protocolToRemove != ProxyProtocolType.None) {
{ if (NetworkHelper.IsLocalIpAddress(proxy.HostName)
systemProxySettingsManager.RemoveProxy(protocolToRemove, false); && ProxyEndPoints.Any(x => x.Port == proxy.Port))
} {
} protocolToRemove |= proxy.ProtocolType;
} }
}
if (ForwardToUpstreamGateway && GetCustomUpStreamProxyFunc == null && systemProxySettingsManager != null)
{ if (protocolToRemove != ProxyProtocolType.None)
// Use WinHttp to handle PAC/WAPD scripts. {
systemProxyResolver = new WinHttpWebProxyFinder(); systemProxySettingsManager.RemoveProxy(protocolToRemove, false);
systemProxyResolver.LoadFromIE(); }
}
GetCustomUpStreamProxyFunc = getSystemUpStreamProxy; }
}
if (ForwardToUpstreamGateway && GetCustomUpStreamProxyFunc == null && systemProxySettingsManager != null)
ProxyRunning = true; {
// Use WinHttp to handle PAC/WAPD scripts.
CertificateManager.ClearIdleCertificates(); systemProxyResolver = new WinHttpWebProxyFinder();
systemProxyResolver.LoadFromIE();
foreach (var endPoint in ProxyEndPoints)
{ GetCustomUpStreamProxyFunc = getSystemUpStreamProxy;
listen(endPoint); }
}
} ProxyRunning = true;
/// <summary> CertificateManager.ClearIdleCertificates();
/// Stop this proxy server instance.
/// </summary> foreach (var endPoint in ProxyEndPoints)
public void Stop() {
{ listen(endPoint);
if (!ProxyRunning) }
{ }
throw new Exception("Proxy is not running.");
} /// <summary>
/// Stop this proxy server instance.
if (!RunTime.IsRunningOnMono && RunTime.IsWindows) /// </summary>
{ public void Stop()
bool setAsSystemProxy = ProxyEndPoints.OfType<ExplicitProxyEndPoint>() {
.Any(x => x.IsSystemHttpProxy || x.IsSystemHttpsProxy); if (!ProxyRunning)
{
if (setAsSystemProxy) throw new Exception("Proxy is not running.");
{ }
systemProxySettingsManager.RestoreOriginalSettings();
} if (!RunTime.IsRunningOnMono && RunTime.IsWindows)
} {
bool setAsSystemProxy = ProxyEndPoints.OfType<ExplicitProxyEndPoint>()
foreach (var endPoint in ProxyEndPoints) .Any(x => x.IsSystemHttpProxy || x.IsSystemHttpsProxy);
{
quitListen(endPoint); if (setAsSystemProxy)
} {
systemProxySettingsManager.RestoreOriginalSettings();
ProxyEndPoints.Clear(); }
}
CertificateManager?.StopClearIdleCertificates();
tcpConnectionFactory.Dispose(); foreach (var endPoint in ProxyEndPoints)
{
ProxyRunning = false; quitListen(endPoint);
} }
/// <summary> ProxyEndPoints.Clear();
/// Listen on given end point of local machine.
/// </summary> CertificateManager?.StopClearIdleCertificates();
/// <param name="endPoint">The end point to listen.</param> tcpConnectionFactory.Dispose();
private void listen(ProxyEndPoint endPoint)
{ ProxyRunning = false;
endPoint.Listener = new TcpListener(endPoint.IpAddress, endPoint.Port); }
//linux/macOS has a bug with socket reuse in .net core. /// <summary>
if (ReuseSocket && (RunTime.IsWindows || RunTime.IsRunningOnMono)) /// Listen on given end point of local machine.
{ /// </summary>
endPoint.Listener.Server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); /// <param name="endPoint">The end point to listen.</param>
} private void listen(ProxyEndPoint endPoint)
{
try endPoint.Listener = new TcpListener(endPoint.IpAddress, endPoint.Port);
{
endPoint.Listener.Start(); //linux/macOS has a bug with socket reuse in .net core.
if (ReuseSocket && (RunTime.IsWindows || RunTime.IsRunningOnMono))
endPoint.Port = ((IPEndPoint)endPoint.Listener.LocalEndpoint).Port; {
endPoint.Listener.Server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
// accept clients asynchronously }
endPoint.Listener.BeginAcceptTcpClient(onAcceptConnection, endPoint);
} try
catch (SocketException ex) {
{ endPoint.Listener.Start();
var pex = new Exception(
$"Endpoint {endPoint} failed to start. Check inner exception and exception data for details.", ex); endPoint.Port = ((IPEndPoint)endPoint.Listener.LocalEndpoint).Port;
pex.Data.Add("ipAddress", endPoint.IpAddress);
pex.Data.Add("port", endPoint.Port); // accept clients asynchronously
throw pex; endPoint.Listener.BeginAcceptTcpClient(onAcceptConnection, endPoint);
} }
} catch (SocketException ex)
{
/// <summary> var pex = new Exception(
/// Verify if its safe to set this end point as system proxy. $"Endpoint {endPoint} failed to start. Check inner exception and exception data for details.", ex);
/// </summary> pex.Data.Add("ipAddress", endPoint.IpAddress);
/// <param name="endPoint">The end point to validate.</param> pex.Data.Add("port", endPoint.Port);
private void validateEndPointAsSystemProxy(ExplicitProxyEndPoint endPoint) throw pex;
{ }
if (endPoint == null) }
{
throw new ArgumentNullException(nameof(endPoint)); /// <summary>
} /// Verify if its safe to set this end point as system proxy.
/// </summary>
if (ProxyEndPoints.Contains(endPoint) == false) /// <param name="endPoint">The end point to validate.</param>
{ private void validateEndPointAsSystemProxy(ExplicitProxyEndPoint endPoint)
throw new Exception("Cannot set endPoints not added to proxy as system proxy"); {
} if (endPoint == null)
{
if (!ProxyRunning) throw new ArgumentNullException(nameof(endPoint));
{ }
throw new Exception("Cannot set system proxy settings before proxy has been started.");
} if (ProxyEndPoints.Contains(endPoint) == false)
} {
throw new Exception("Cannot set endPoints not added to proxy as system proxy");
/// <summary> }
/// Gets the system up stream proxy.
/// </summary> if (!ProxyRunning)
/// <param name="sessionEventArgs">The session.</param> {
/// <returns>The external proxy as task result.</returns> throw new Exception("Cannot set system proxy settings before proxy has been started.");
private Task<ExternalProxy> getSystemUpStreamProxy(SessionEventArgsBase sessionEventArgs) }
{ }
var proxy = systemProxyResolver.GetProxy(sessionEventArgs.WebSession.Request.RequestUri);
return Task.FromResult(proxy); /// <summary>
} /// Gets the system up stream proxy.
/// </summary>
/// <summary> /// <param name="sessionEventArgs">The session.</param>
/// Act when a connection is received from client. /// <returns>The external proxy as task result.</returns>
/// </summary> private Task<ExternalProxy> getSystemUpStreamProxy(SessionEventArgsBase sessionEventArgs)
private void onAcceptConnection(IAsyncResult asyn) {
{ var proxy = systemProxyResolver.GetProxy(sessionEventArgs.WebSession.Request.RequestUri);
var endPoint = (ProxyEndPoint)asyn.AsyncState; return Task.FromResult(proxy);
}
TcpClient tcpClient = null;
/// <summary>
try /// Act when a connection is received from client.
{ /// </summary>
// based on end point type call appropriate request handlers private void onAcceptConnection(IAsyncResult asyn)
tcpClient = endPoint.Listener.EndAcceptTcpClient(asyn); {
} var endPoint = (ProxyEndPoint)asyn.AsyncState;
catch (ObjectDisposedException)
{ TcpClient tcpClient = null;
// The listener was Stop()'d, disposing the underlying socket and
// triggering the completion of the callback. We're already exiting, try
// so just return. {
return; // based on end point type call appropriate request handlers
} tcpClient = endPoint.Listener.EndAcceptTcpClient(asyn);
catch }
{ catch (ObjectDisposedException)
// Other errors are discarded to keep proxy running {
} // The listener was Stop()'d, disposing the underlying socket and
// triggering the completion of the callback. We're already exiting,
if (tcpClient != null) // so just return.
{ return;
Task.Run(async () => { await handleClient(tcpClient, endPoint); }); }
} catch
{
// Get the listener that handles the client request. // Other errors are discarded to keep proxy running
endPoint.Listener.BeginAcceptTcpClient(onAcceptConnection, endPoint); }
}
if (tcpClient != null)
/// <summary> {
/// Handle the client. Task.Run(async () => { await handleClient(tcpClient, endPoint); });
/// </summary> }
/// <param name="tcpClient">The client.</param>
/// <param name="endPoint">The proxy endpoint.</param> // Get the listener that handles the client request.
/// <returns>The task.</returns> endPoint.Listener.BeginAcceptTcpClient(onAcceptConnection, endPoint);
private async Task handleClient(TcpClient tcpClient, ProxyEndPoint endPoint) }
{
tcpClient.ReceiveTimeout = ConnectionTimeOutSeconds * 1000; /// <summary>
tcpClient.SendTimeout = ConnectionTimeOutSeconds * 1000; /// Handle the client.
tcpClient.SendBufferSize = BufferSize; /// </summary>
tcpClient.ReceiveBufferSize = BufferSize; /// <param name="tcpClient">The client.</param>
tcpClient.LingerState = new LingerOption(true, TcpTimeWaitSeconds); /// <param name="endPoint">The proxy endpoint.</param>
/// <returns>The task.</returns>
await InvokeConnectionCreateEvent(tcpClient, true); private async Task handleClient(TcpClient tcpClient, ProxyEndPoint endPoint)
{
using (var clientConnection = new TcpClientConnection(this, tcpClient)) tcpClient.ReceiveTimeout = ConnectionTimeOutSeconds * 1000;
{ tcpClient.SendTimeout = ConnectionTimeOutSeconds * 1000;
if (endPoint is TransparentProxyEndPoint tep) tcpClient.SendBufferSize = BufferSize;
{ tcpClient.ReceiveBufferSize = BufferSize;
await handleClient(tep, clientConnection); tcpClient.LingerState = new LingerOption(true, TcpTimeWaitSeconds);
}
else await InvokeConnectionCreateEvent(tcpClient, true);
{
await handleClient((ExplicitProxyEndPoint)endPoint, clientConnection); using (var clientConnection = new TcpClientConnection(this, tcpClient))
} {
} if (endPoint is TransparentProxyEndPoint tep)
} {
await handleClient(tep, clientConnection);
/// <summary> }
/// Handle exception. else
/// </summary> {
/// <param name="clientStream">The client stream.</param> await handleClient((ExplicitProxyEndPoint)endPoint, clientConnection);
/// <param name="exception">The exception.</param> }
private void onException(CustomBufferedStream clientStream, Exception exception) }
{ }
#if DEBUG
if (clientStream is DebugCustomBufferedStream debugStream) /// <summary>
{ /// Handle exception.
debugStream.LogException(exception); /// </summary>
} /// <param name="clientStream">The client stream.</param>
#endif /// <param name="exception">The exception.</param>
private void onException(CustomBufferedStream clientStream, Exception exception)
ExceptionFunc(exception); {
} #if DEBUG
if (clientStream is DebugCustomBufferedStream debugStream)
/// <summary> {
/// Quit listening on the given end point. debugStream.LogException(exception);
/// </summary> }
private void quitListen(ProxyEndPoint endPoint) #endif
{
endPoint.Listener.Stop(); ExceptionFunc(exception);
endPoint.Listener.Server.Dispose(); }
}
/// <summary>
/// <summary> /// Quit listening on the given end point.
/// Update client connection count. /// </summary>
/// </summary> private void quitListen(ProxyEndPoint endPoint)
/// <param name="increment">Should we increment/decrement?</param> {
internal void UpdateClientConnectionCount(bool increment) endPoint.Listener.Stop();
{ endPoint.Listener.Server.Dispose();
if (increment) }
{
Interlocked.Increment(ref clientConnectionCount); /// <summary>
} /// Update client connection count.
else /// </summary>
{ /// <param name="increment">Should we increment/decrement?</param>
Interlocked.Decrement(ref clientConnectionCount); internal void UpdateClientConnectionCount(bool increment)
} {
if (increment)
ClientConnectionCountChanged?.Invoke(this, EventArgs.Empty); {
} Interlocked.Increment(ref clientConnectionCount);
}
/// <summary> else
/// Update server connection count. {
/// </summary> Interlocked.Decrement(ref clientConnectionCount);
/// <param name="increment">Should we increment/decrement?</param> }
internal void UpdateServerConnectionCount(bool increment)
{ ClientConnectionCountChanged?.Invoke(this, EventArgs.Empty);
if (increment) }
{
Interlocked.Increment(ref serverConnectionCount); /// <summary>
} /// Update server connection count.
else /// </summary>
{ /// <param name="increment">Should we increment/decrement?</param>
Interlocked.Decrement(ref serverConnectionCount); internal void UpdateServerConnectionCount(bool increment)
} {
if (increment)
ServerConnectionCountChanged?.Invoke(this, EventArgs.Empty); {
} Interlocked.Increment(ref serverConnectionCount);
}
/// <summary> else
/// Invoke client/server tcp connection events if subscribed by API user. {
/// </summary> Interlocked.Decrement(ref serverConnectionCount);
/// <param name="client">The TcpClient object.</param> }
/// <param name="isClientConnection">Is this a client connection created event? If not then we would assume that its a server connection create event.</param>
/// <returns></returns> ServerConnectionCountChanged?.Invoke(this, EventArgs.Empty);
internal async Task InvokeConnectionCreateEvent(TcpClient client, bool isClientConnection) }
{
//client connection created /// <summary>
if (isClientConnection && OnClientConnectionCreate != null) /// Invoke client/server tcp connection events if subscribed by API user.
{ /// </summary>
await OnClientConnectionCreate.InvokeAsync(this, client, ExceptionFunc); /// <param name="client">The TcpClient object.</param>
} /// <param name="isClientConnection">Is this a client connection created event? If not then we would assume that its a server connection create event.</param>
/// <returns></returns>
//server connection created internal async Task InvokeConnectionCreateEvent(TcpClient client, bool isClientConnection)
if (!isClientConnection && OnServerConnectionCreate != null) {
{ //client connection created
await OnServerConnectionCreate.InvokeAsync(this, client, ExceptionFunc); if (isClientConnection && OnClientConnectionCreate != null)
} {
} await OnClientConnectionCreate.InvokeAsync(this, client, ExceptionFunc);
}
/// <summary>
/// Connection retry policy when using connection pool. //server connection created
/// </summary> if (!isClientConnection && OnServerConnectionCreate != null)
private RetryPolicy<T> retryPolicy<T>() where T : Exception {
{ await OnServerConnectionCreate.InvokeAsync(this, client, ExceptionFunc);
return new RetryPolicy<T>(retries, tcpConnectionFactory); }
} }
/// <summary> /// <summary>
/// Dispose the Proxy instance. /// Connection retry policy when using connection pool.
/// </summary> /// </summary>
public void Dispose() private RetryPolicy<T> retryPolicy<T>() where T : Exception
{ {
if (ProxyRunning) return new RetryPolicy<T>(retries, tcpConnectionFactory);
{ }
Stop();
} /// <summary>
/// Dispose the Proxy instance.
CertificateManager?.Dispose(); /// </summary>
BufferPool?.Dispose(); public void Dispose()
} {
} if (ProxyRunning)
} {
Stop();
}
CertificateManager?.Dispose();
BufferPool?.Dispose();
}
}
}
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
#if NETCOREAPP2_1 #if NETCOREAPP2_1
using System.Net.Security; using System.Net.Security;
#endif #endif
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended.Network; using StreamExtended.Network;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Exceptions; using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network.Tcp; using Titanium.Web.Proxy.Network.Tcp;
using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy
{ namespace Titanium.Web.Proxy
/// <summary> {
/// Handle the request /// <summary>
/// </summary> /// Handle the request
public partial class ProxyServer /// </summary>
{ public partial class ProxyServer
private static readonly Regex uriSchemeRegex = {
new Regex("^[a-z]*://", RegexOptions.IgnoreCase | RegexOptions.Compiled);
private bool isWindowsAuthenticationEnabledAndSupported =>
private static readonly HashSet<string> proxySupportedCompressions = EnableWinAuth && RunTime.IsWindows && !RunTime.IsRunningOnMono;
new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{ /// <summary>
"gzip", /// This is the core request handler method for a particular connection from client.
"deflate" /// Will create new session (request/response) sequence until
}; /// client/server abruptly terminates connection or by normal HTTP termination.
/// </summary>
private bool isWindowsAuthenticationEnabledAndSupported => /// <param name="endPoint">The proxy endpoint.</param>
EnableWinAuth && RunTime.IsWindows && !RunTime.IsRunningOnMono; /// <param name="clientConnection">The client connection.</param>
/// <param name="clientStream">The client stream.</param>
/// <summary> /// <param name="clientStreamWriter">The client stream writer.</param>
/// This is the core request handler method for a particular connection from client. /// <param name="cancellationTokenSource">The cancellation token source for this async task.</param>
/// Will create new session (request/response) sequence until /// <param name="httpsConnectHostname">
/// client/server abruptly terminates connection or by normal HTTP termination. /// The https hostname as appeared in CONNECT request if this is a HTTPS request from
/// </summary> /// explicit endpoint.
/// <param name="endPoint">The proxy endpoint.</param> /// </param>
/// <param name="clientConnection">The client connection.</param> /// <param name="connectRequest">The Connect request if this is a HTTPS request from explicit endpoint.</param>
/// <param name="clientStream">The client stream.</param> /// <param name="prefetchConnectionTask">Prefetched server connection for current client using Connect/SNI headers.</param>
/// <param name="clientStreamWriter">The client stream writer.</param> private async Task handleHttpSessionRequest(ProxyEndPoint endPoint, TcpClientConnection clientConnection,
/// <param name="cancellationTokenSource">The cancellation token source for this async task.</param> CustomBufferedStream clientStream, HttpResponseWriter clientStreamWriter,
/// <param name="httpsConnectHostname"> CancellationTokenSource cancellationTokenSource, string httpsConnectHostname, ConnectRequest connectRequest,
/// The https hostname as appeared in CONNECT request if this is a HTTPS request from Task<TcpServerConnection> prefetchConnectionTask = null)
/// explicit endpoint. {
/// </param> var prefetchTask = prefetchConnectionTask;
/// <param name="connectRequest">The Connect request if this is a HTTPS request from explicit endpoint.</param> TcpServerConnection connection = null;
/// <param name="prefetchConnectionTask">Prefetched server connection for current client using Connect/SNI headers.</param> bool closeServerConnection = false;
private async Task handleHttpSessionRequest(ProxyEndPoint endPoint, TcpClientConnection clientConnection,
CustomBufferedStream clientStream, HttpResponseWriter clientStreamWriter, try
CancellationTokenSource cancellationTokenSource, string httpsConnectHostname, ConnectRequest connectRequest, {
Task<TcpServerConnection> prefetchConnectionTask = null) var cancellationToken = cancellationTokenSource.Token;
{
var prefetchTask = prefetchConnectionTask; // Loop through each subsequest request on this particular client connection
TcpServerConnection connection = null; // (assuming HTTP connection is kept alive by client)
bool closeServerConnection = false; while (true)
{
try // read the request line
{ string httpCmd = await clientStream.ReadLineAsync(cancellationToken);
var cancellationToken = cancellationTokenSource.Token;
if (string.IsNullOrEmpty(httpCmd))
// Loop through each subsequest request on this particular client connection {
// (assuming HTTP connection is kept alive by client) return;
while (true) }
{
// read the request line var args = new SessionEventArgs(this, endPoint, cancellationTokenSource)
string httpCmd = await clientStream.ReadLineAsync(cancellationToken); {
ProxyClient = { ClientConnection = clientConnection },
if (string.IsNullOrEmpty(httpCmd)) WebSession = { ConnectRequest = connectRequest }
{ };
return;
} try
{
var args = new SessionEventArgs(this, endPoint, cancellationTokenSource) try
{ {
ProxyClient = { ClientConnection = clientConnection }, Request.ParseRequestLine(httpCmd, out string httpMethod, out string httpUrl,
WebSession = { ConnectRequest = connectRequest } out var version);
};
// Read the request headers in to unique and non-unique header collections
try await HeaderParser.ReadHeaders(clientStream, args.WebSession.Request.Headers,
{ cancellationToken);
try
{ Uri httpRemoteUri;
Request.ParseRequestLine(httpCmd, out string httpMethod, out string httpUrl, if (ProxyConstants.UriSchemeRegex.IsMatch(httpUrl))
out var version); {
try
// Read the request headers in to unique and non-unique header collections {
await HeaderParser.ReadHeaders(clientStream, args.WebSession.Request.Headers, httpRemoteUri = new Uri(httpUrl);
cancellationToken); }
catch (Exception ex)
Uri httpRemoteUri; {
if (uriSchemeRegex.IsMatch(httpUrl)) throw new Exception($"Invalid URI: '{httpUrl}'", ex);
{ }
try }
{ else
httpRemoteUri = new Uri(httpUrl); {
} string host = args.WebSession.Request.Host ?? httpsConnectHostname;
catch (Exception ex) string hostAndPath = host;
{ if (httpUrl.StartsWith("/"))
throw new Exception($"Invalid URI: '{httpUrl}'", ex); {
} hostAndPath += httpUrl;
} }
else
{ string url = string.Concat(httpsConnectHostname == null ? "http://" : "https://",
string host = args.WebSession.Request.Host ?? httpsConnectHostname; hostAndPath);
string hostAndPath = host; try
if (httpUrl.StartsWith("/")) {
{ httpRemoteUri = new Uri(url);
hostAndPath += httpUrl; }
} catch (Exception ex)
{
string url = string.Concat(httpsConnectHostname == null ? "http://" : "https://", throw new Exception($"Invalid URI: '{url}'", ex);
hostAndPath); }
try }
{
httpRemoteUri = new Uri(url); var request = args.WebSession.Request;
} request.RequestUri = httpRemoteUri;
catch (Exception ex) request.OriginalUrl = httpUrl;
{
throw new Exception($"Invalid URI: '{url}'", ex); request.Method = httpMethod;
} request.HttpVersion = version;
} args.ProxyClient.ClientStream = clientStream;
args.ProxyClient.ClientStreamWriter = clientStreamWriter;
var request = args.WebSession.Request;
request.RequestUri = httpRemoteUri; if (!args.IsTransparent)
request.OriginalUrl = httpUrl; {
// proxy authorization check
request.Method = httpMethod; if (httpsConnectHostname == null && await checkAuthorization(args) == false)
request.HttpVersion = version; {
args.ProxyClient.ClientStream = clientStream; await invokeBeforeResponse(args);
args.ProxyClient.ClientStreamWriter = clientStreamWriter;
// send the response
if (!args.IsTransparent) await clientStreamWriter.WriteResponseAsync(args.WebSession.Response,
{ cancellationToken: cancellationToken);
// proxy authorization check return;
if (httpsConnectHostname == null && await checkAuthorization(args) == false) }
{
await invokeBeforeResponse(args); prepareRequestHeaders(request.Headers);
request.Host = request.RequestUri.Authority;
// send the response }
await clientStreamWriter.WriteResponseAsync(args.WebSession.Response,
cancellationToken: cancellationToken); // if win auth is enabled
return; // we need a cache of request body
} // so that we can send it after authentication in WinAuthHandler.cs
if (isWindowsAuthenticationEnabledAndSupported && request.HasBody)
prepareRequestHeaders(request.Headers); {
request.Host = request.RequestUri.Authority; await args.GetRequestBody(cancellationToken);
} }
// if win auth is enabled //we need this to syphon out data from connection if API user changes them.
// we need a cache of request body request.SetOriginalHeaders();
// so that we can send it after authentication in WinAuthHandler.cs
if (isWindowsAuthenticationEnabledAndSupported && request.HasBody) args.TimeLine["Request Received"] = DateTime.Now;
{
await args.GetRequestBody(cancellationToken); // If user requested interception do it
} await invokeBeforeRequest(args);
//we need this to syphon out data from connection if API user changes them. var response = args.WebSession.Response;
request.SetOriginalHeaders();
if (request.CancelRequest)
args.TimeLine["Request Received"] = DateTime.Now; {
// syphon out the request body from client before setting the new body
// If user requested interception do it await args.SyphonOutBodyAsync(true, cancellationToken);
await invokeBeforeRequest(args);
await handleHttpSessionResponse(args);
var response = args.WebSession.Response;
if (!response.KeepAlive)
if (request.CancelRequest) {
{ return;
// syphon out the request body from client before setting the new body }
await args.SyphonOutBodyAsync(true, cancellationToken);
continue;
await handleHttpSessionResponse(args); }
if (!response.KeepAlive) //If prefetch task is available.
{ if (connection == null && prefetchTask != null)
return; {
} connection = await prefetchTask;
prefetchTask = null;
continue; }
}
// create a new connection if cache key changes.
//If prefetch task is available. // only gets hit when connection pool is disabled.
if (connection == null && prefetchTask != null) // or when prefetch task has a unexpectedly different connection.
{ if (connection != null
connection = await prefetchTask; && (await tcpConnectionFactory.GetConnectionCacheKey(this, args,
prefetchTask = null; clientConnection.NegotiatedApplicationProtocol)
} != connection.CacheKey))
{
// create a new connection if cache key changes. await tcpConnectionFactory.Release(connection);
// only gets hit when connection pool is disabled. connection = null;
// or when prefetch task has a unexpectedly different connection. }
if (connection != null
&& (await tcpConnectionFactory.GetConnectionCacheKey(this, args, //a connection generator task with captured parameters via closure.
clientConnection.NegotiatedApplicationProtocol) Func<Task<TcpServerConnection>> generator = () =>
!= connection.CacheKey)) tcpConnectionFactory.GetServerConnection(this, args, isConnect: false,
{ applicationProtocol:clientConnection.NegotiatedApplicationProtocol,
await tcpConnectionFactory.Release(connection); noCache: false, cancellationToken: cancellationToken);
connection = null;
} //for connection pool, retry fails until cache is exhausted.
var result = await retryPolicy<ServerConnectionException>().ExecuteAsync(async (serverConnection) =>
//a connection generator task with captured parameters via closure. {
Func<Task<TcpServerConnection>> generator = () => args.TimeLine["Connection Ready"] = DateTime.Now;
tcpConnectionFactory.GetServerConnection(this, args, isConnect: false,
applicationProtocol:clientConnection.NegotiatedApplicationProtocol, // if upgrading to websocket then relay the request without reading the contents
noCache: false, cancellationToken: cancellationToken); if (request.UpgradeToWebSocket)
{
//for connection pool, retry fails until cache is exhausted. await handleWebSocketUpgrade(httpCmd, args, request,
var result = await retryPolicy<ServerConnectionException>().ExecuteAsync(async (serverConnection) => response, clientStream, clientStreamWriter,
{ serverConnection, cancellationTokenSource, cancellationToken);
args.TimeLine["Server Connection Created"] = DateTime.Now; closeServerConnection = true;
return false;
// if upgrading to websocket then relay the request without reading the contents }
if (request.UpgradeToWebSocket)
{ // construct the web request that we are going to issue on behalf of the client.
await handleWebSocketUpgrade(httpCmd, args, request, await handleHttpSessionRequest(serverConnection, args);
response, clientStream, clientStreamWriter, return true;
serverConnection, cancellationTokenSource, cancellationToken);
closeServerConnection = true; }, generator, connection);
return false;
} //update connection to latest used
connection = result.LatestConnection;
// construct the web request that we are going to issue on behalf of the client.
await handleHttpSessionRequestInternal(serverConnection, args); //throw if exception happened
return true; if(!result.IsSuccess)
{
}, generator, connection); throw result.Exception;
}
//update connection to latest used
connection = result.LatestConnection; if(!result.Continue)
{
//throw if exception happened return;
if(!result.IsSuccess) }
{
throw result.Exception; //user requested
} if (args.WebSession.CloseServerConnection)
{
if(!result.Continue) closeServerConnection = true;
{ return;
return; }
}
// if connection is closing exit
//user requested if (!response.KeepAlive)
if (args.WebSession.CloseServerConnection) {
{ closeServerConnection = true;
closeServerConnection = true; return;
return; }
}
if (cancellationTokenSource.IsCancellationRequested)
// if connection is closing exit {
if (!response.KeepAlive) throw new Exception("Session was terminated by user.");
{ }
closeServerConnection = true;
return; //Get/release server connection for each HTTP session instead of per client connection.
} //This will be more efficient especially when client is idly holding server connection
//between sessions without using it.
if (cancellationTokenSource.IsCancellationRequested) //Do not release authenticated connections for performance reasons.
{ //Otherwise it will keep authenticating per session.
throw new Exception("Session was terminated by user."); if (EnableConnectionPool && connection!=null
} && !connection.IsWinAuthenticated)
{
//Get/release server connection for each HTTP session instead of per client connection. await tcpConnectionFactory.Release(connection);
//This will be more efficient especially when client is idly holding server connection connection = null;
//between sessions without using it. }
//Do not release authenticated connections for performance reasons.
//Otherwise it will keep authenticating per session. }
if (EnableConnectionPool && connection!=null catch (Exception e) when (!(e is ProxyHttpException))
&& !connection.IsWinAuthenticated) {
{ throw new ProxyHttpException("Error occured whilst handling session request", e, args);
await tcpConnectionFactory.Release(connection); }
connection = null; }
} catch (Exception e)
{
} args.Exception = e;
catch (Exception e) when (!(e is ProxyHttpException)) closeServerConnection = true;
{ throw;
throw new ProxyHttpException("Error occured whilst handling session request", e, args); }
} finally
} {
catch (Exception e) await invokeAfterResponse(args);
{ args.Dispose();
args.Exception = e; }
closeServerConnection = true; }
throw; }
} finally
finally {
{ await tcpConnectionFactory.Release(connection,
await invokeAfterResponse(args); closeServerConnection);
args.Dispose();
} await tcpConnectionFactory.Release(prefetchTask, closeServerConnection);
} }
} }
finally
{ /// <summary>
await tcpConnectionFactory.Release(connection, /// Handle a specific session (request/response sequence)
closeServerConnection); /// </summary>
/// <param name="serverConnection">The tcp connection.</param>
await tcpConnectionFactory.Release(prefetchTask, closeServerConnection); /// <param name="args">The session event arguments.</param>
} /// <returns></returns>
} private async Task handleHttpSessionRequest(TcpServerConnection serverConnection, SessionEventArgs args)
{
/// <summary> var cancellationToken = args.CancellationTokenSource.Token;
/// Handle a specific session (request/response sequence) var request = args.WebSession.Request;
/// </summary> request.Locked = true;
/// <param name="serverConnection">The tcp connection.</param>
/// <param name="args">The session event arguments.</param> var body = request.CompressBodyAndUpdateContentLength();
/// <returns></returns>
private async Task handleHttpSessionRequestInternal(TcpServerConnection serverConnection, SessionEventArgs args) // if expect continue is enabled then send the headers first
{ // and see if server would return 100 conitinue
var cancellationToken = args.CancellationTokenSource.Token; if (request.ExpectContinue)
var request = args.WebSession.Request; {
request.Locked = true; args.WebSession.SetConnection(serverConnection);
await args.WebSession.SendRequest(Enable100ContinueBehaviour, args.IsTransparent,
var body = request.CompressBodyAndUpdateContentLength(); cancellationToken);
}
// if expect continue is enabled then send the headers first
// and see if server would return 100 conitinue // If 100 continue was the response inform that to the client
if (request.ExpectContinue) if (Enable100ContinueBehaviour)
{ {
args.WebSession.SetConnection(serverConnection); var clientStreamWriter = args.ProxyClient.ClientStreamWriter;
await args.WebSession.SendRequest(Enable100ContinueBehaviour, args.IsTransparent,
cancellationToken); if (request.Is100Continue)
} {
await clientStreamWriter.WriteResponseStatusAsync(args.WebSession.Response.HttpVersion,
// If 100 continue was the response inform that to the client (int)HttpStatusCode.Continue, "Continue", cancellationToken);
if (Enable100ContinueBehaviour) await clientStreamWriter.WriteLineAsync(cancellationToken);
{ }
var clientStreamWriter = args.ProxyClient.ClientStreamWriter; else if (request.ExpectationFailed)
{
if (request.Is100Continue) await clientStreamWriter.WriteResponseStatusAsync(args.WebSession.Response.HttpVersion,
{ (int)HttpStatusCode.ExpectationFailed, "Expectation Failed", cancellationToken);
await clientStreamWriter.WriteResponseStatusAsync(args.WebSession.Response.HttpVersion, await clientStreamWriter.WriteLineAsync(cancellationToken);
(int)HttpStatusCode.Continue, "Continue", cancellationToken); }
await clientStreamWriter.WriteLineAsync(cancellationToken); }
}
else if (request.ExpectationFailed) // If expect continue is not enabled then set the connectio and send request headers
{ if (!request.ExpectContinue)
await clientStreamWriter.WriteResponseStatusAsync(args.WebSession.Response.HttpVersion, {
(int)HttpStatusCode.ExpectationFailed, "Expectation Failed", cancellationToken); args.WebSession.SetConnection(serverConnection);
await clientStreamWriter.WriteLineAsync(cancellationToken); await args.WebSession.SendRequest(Enable100ContinueBehaviour, args.IsTransparent,
} cancellationToken);
} }
// If expect continue is not enabled then set the connectio and send request headers // check if content-length is > 0
if (!request.ExpectContinue) if (request.ContentLength > 0)
{ {
args.WebSession.SetConnection(serverConnection); if (request.IsBodyRead)
await args.WebSession.SendRequest(Enable100ContinueBehaviour, args.IsTransparent, {
cancellationToken); var writer = args.WebSession.ServerConnection.StreamWriter;
} await writer.WriteBodyAsync(body, request.IsChunked, cancellationToken);
}
// check if content-length is > 0 else
if (request.ContentLength > 0) {
{ if (!request.ExpectationFailed)
if (request.IsBodyRead) {
{ if (request.HasBody)
var writer = args.WebSession.ServerConnection.StreamWriter; {
await writer.WriteBodyAsync(body, request.IsChunked, cancellationToken); HttpWriter writer = args.WebSession.ServerConnection.StreamWriter;
} await args.CopyRequestBodyAsync(writer, TransformationMode.None, cancellationToken);
else }
{ }
if (!request.ExpectationFailed) }
{ }
if (request.HasBody)
{ args.TimeLine["Request Sent"] = DateTime.Now;
HttpWriter writer = args.WebSession.ServerConnection.StreamWriter;
await args.CopyRequestBodyAsync(writer, TransformationMode.None, cancellationToken); // If not expectation failed response was returned by server then parse response
} if (!request.ExpectationFailed)
} {
} await handleHttpSessionResponse(args);
} }
// If not expectation failed response was returned by server then parse response args.TimeLine["Response Sent"] = DateTime.Now;
if (!request.ExpectationFailed) }
{
await handleHttpSessionResponse(args); /// <summary>
} /// Prepare the request headers so that we can avoid encodings not parsable by this proxy
/// </summary>
args.TimeLine["Response Sent"] = DateTime.Now; private void prepareRequestHeaders(HeaderCollection requestHeaders)
} {
string acceptEncoding = requestHeaders.GetHeaderValueOrNull(KnownHeaders.AcceptEncoding);
/// <summary>
/// Prepare the request headers so that we can avoid encodings not parsable by this proxy if (acceptEncoding != null)
/// </summary> {
private void prepareRequestHeaders(HeaderCollection requestHeaders) var supportedAcceptEncoding = new List<string>();
{
string acceptEncoding = requestHeaders.GetHeaderValueOrNull(KnownHeaders.AcceptEncoding); //only allow proxy supported compressions
supportedAcceptEncoding.AddRange(acceptEncoding.Split(',')
if (acceptEncoding != null) .Select(x => x.Trim())
{ .Where(x => ProxyConstants.ProxySupportedCompressions.Contains(x)));
var supportedAcceptEncoding = new List<string>();
//uncompressed is always supported by proxy
//only allow proxy supported compressions supportedAcceptEncoding.Add("identity");
supportedAcceptEncoding.AddRange(acceptEncoding.Split(',')
.Select(x => x.Trim()) requestHeaders.SetOrAddHeaderValue(KnownHeaders.AcceptEncoding,
.Where(x => proxySupportedCompressions.Contains(x))); string.Join(",", supportedAcceptEncoding));
}
//uncompressed is always supported by proxy
supportedAcceptEncoding.Add("identity"); requestHeaders.FixProxyHeaders();
}
requestHeaders.SetOrAddHeaderValue(KnownHeaders.AcceptEncoding,
string.Join(",", supportedAcceptEncoding)); /// <summary>
} /// Invoke before request handler if it is set.
/// </summary>
requestHeaders.FixProxyHeaders(); /// <param name="args">The session event arguments.</param>
} /// <returns></returns>
private async Task invokeBeforeRequest(SessionEventArgs args)
/// <summary> {
/// Invoke before request handler if it is set. if (BeforeRequest != null)
/// </summary> {
/// <param name="args">The session event arguments.</param> await BeforeRequest.InvokeAsync(this, args, ExceptionFunc);
/// <returns></returns> }
private async Task invokeBeforeRequest(SessionEventArgs args) }
{ }
if (BeforeRequest != null) }
{
await BeforeRequest.InvokeAsync(this, args, ExceptionFunc);
}
}
}
}
using System; using System;
using System.Net; using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Network.WinAuth.Security; using Titanium.Web.Proxy.Network.WinAuth.Security;
namespace Titanium.Web.Proxy namespace Titanium.Web.Proxy
{ {
/// <summary> /// <summary>
/// Handle the response from server. /// Handle the response from server.
/// </summary> /// </summary>
public partial class ProxyServer public partial class ProxyServer
{ {
/// <summary> /// <summary>
/// Called asynchronously when a request was successfull and we received the response. /// Called asynchronously when a request was successfull and we received the response.
/// </summary> /// </summary>
/// <param name="args">The session event arguments.</param> /// <param name="args">The session event arguments.</param>
/// <returns> The task.</returns> /// <returns> The task.</returns>
private async Task handleHttpSessionResponse(SessionEventArgs args) private async Task handleHttpSessionResponse(SessionEventArgs args)
{ {
var cancellationToken = args.CancellationTokenSource.Token; var cancellationToken = args.CancellationTokenSource.Token;
// read response & headers from server // read response & headers from server
await args.WebSession.ReceiveResponse(cancellationToken); await args.WebSession.ReceiveResponse(cancellationToken);
args.TimeLine["Response Received"] = DateTime.Now; args.TimeLine["Response Received"] = DateTime.Now;
var response = args.WebSession.Response; var response = args.WebSession.Response;
args.ReRequest = false; args.ReRequest = false;
// check for windows authentication // check for windows authentication
if (isWindowsAuthenticationEnabledAndSupported) if (isWindowsAuthenticationEnabledAndSupported)
{ {
if (response.StatusCode == (int)HttpStatusCode.Unauthorized) if (response.StatusCode == (int)HttpStatusCode.Unauthorized)
{ {
await handle401UnAuthorized(args); await handle401UnAuthorized(args);
} }
else else
{ {
WinAuthEndPoint.AuthenticatedResponse(args.WebSession.Data); WinAuthEndPoint.AuthenticatedResponse(args.WebSession.Data);
} }
} }
//save original values so that if user changes them //save original values so that if user changes them
//we can still use original values when syphoning out data from attached tcp connection. //we can still use original values when syphoning out data from attached tcp connection.
response.SetOriginalHeaders(); response.SetOriginalHeaders();
// if user requested call back then do it // if user requested call back then do it
if (!response.Locked) if (!response.Locked)
{ {
await invokeBeforeResponse(args); await invokeBeforeResponse(args);
} }
// it may changed in the user event // it may changed in the user event
response = args.WebSession.Response; response = args.WebSession.Response;
var clientStreamWriter = args.ProxyClient.ClientStreamWriter; var clientStreamWriter = args.ProxyClient.ClientStreamWriter;
//user set custom response by ignoring original response from server. //user set custom response by ignoring original response from server.
if (response.Locked) if (response.Locked)
{ {
//write custom user response with body and return. //write custom user response with body and return.
await clientStreamWriter.WriteResponseAsync(response, cancellationToken: cancellationToken); await clientStreamWriter.WriteResponseAsync(response, cancellationToken: cancellationToken);
if(args.WebSession.ServerConnection != null if(args.WebSession.ServerConnection != null
&& !args.WebSession.CloseServerConnection) && !args.WebSession.CloseServerConnection)
{ {
// syphon out the original response body from server connection // syphon out the original response body from server connection
// so that connection will be good to be reused. // so that connection will be good to be reused.
await args.SyphonOutBodyAsync(false, cancellationToken); await args.SyphonOutBodyAsync(false, cancellationToken);
} }
return; return;
} }
// if user requested to send request again // if user requested to send request again
// likely after making modifications from User Response Handler // likely after making modifications from User Response Handler
if (args.ReRequest) if (args.ReRequest)
{ {
// clear current response // clear current response
await args.ClearResponse(cancellationToken); await args.ClearResponse(cancellationToken);
await handleHttpSessionRequestInternal(args.WebSession.ServerConnection, args); await handleHttpSessionRequest(args.WebSession.ServerConnection, args);
return; return;
} }
response.Locked = true; response.Locked = true;
// Write back to client 100-conitinue response if that's what server returned // Write back to client 100-conitinue response if that's what server returned
if (response.Is100Continue) if (response.Is100Continue)
{ {
await clientStreamWriter.WriteResponseStatusAsync(response.HttpVersion, await clientStreamWriter.WriteResponseStatusAsync(response.HttpVersion,
(int)HttpStatusCode.Continue, "Continue", cancellationToken); (int)HttpStatusCode.Continue, "Continue", cancellationToken);
await clientStreamWriter.WriteLineAsync(cancellationToken); await clientStreamWriter.WriteLineAsync(cancellationToken);
} }
else if (response.ExpectationFailed) else if (response.ExpectationFailed)
{ {
await clientStreamWriter.WriteResponseStatusAsync(response.HttpVersion, await clientStreamWriter.WriteResponseStatusAsync(response.HttpVersion,
(int)HttpStatusCode.ExpectationFailed, "Expectation Failed", cancellationToken); (int)HttpStatusCode.ExpectationFailed, "Expectation Failed", cancellationToken);
await clientStreamWriter.WriteLineAsync(cancellationToken); await clientStreamWriter.WriteLineAsync(cancellationToken);
} }
if (!args.IsTransparent) if (!args.IsTransparent)
{ {
response.Headers.FixProxyHeaders(); response.Headers.FixProxyHeaders();
} }
if (response.IsBodyRead) if (response.IsBodyRead)
{ {
await clientStreamWriter.WriteResponseAsync(response, cancellationToken: cancellationToken); await clientStreamWriter.WriteResponseAsync(response, cancellationToken: cancellationToken);
} }
else else
{ {
// Write back response status to client // Write back response status to client
await clientStreamWriter.WriteResponseStatusAsync(response.HttpVersion, response.StatusCode, await clientStreamWriter.WriteResponseStatusAsync(response.HttpVersion, response.StatusCode,
response.StatusDescription, cancellationToken); response.StatusDescription, cancellationToken);
await clientStreamWriter.WriteHeadersAsync(response.Headers, cancellationToken: cancellationToken); await clientStreamWriter.WriteHeadersAsync(response.Headers, cancellationToken: cancellationToken);
// Write body if exists // Write body if exists
if (response.HasBody) if (response.HasBody)
{ {
await args.CopyResponseBodyAsync(clientStreamWriter, TransformationMode.None, await args.CopyResponseBodyAsync(clientStreamWriter, TransformationMode.None,
cancellationToken); cancellationToken);
} }
} }
} }
/// <summary> /// <summary>
/// Invoke before response if it is set. /// Invoke before response if it is set.
/// </summary> /// </summary>
/// <param name="args"></param> /// <param name="args"></param>
/// <returns></returns> /// <returns></returns>
private async Task invokeBeforeResponse(SessionEventArgs args) private async Task invokeBeforeResponse(SessionEventArgs args)
{ {
if (BeforeResponse != null) if (BeforeResponse != null)
{ {
await BeforeResponse.InvokeAsync(this, args, ExceptionFunc); await BeforeResponse.InvokeAsync(this, args, ExceptionFunc);
} }
} }
/// <summary> /// <summary>
/// Invoke after response if it is set. /// Invoke after response if it is set.
/// </summary> /// </summary>
/// <param name="args"></param> /// <param name="args"></param>
/// <returns></returns> /// <returns></returns>
private async Task invokeAfterResponse(SessionEventArgs args) private async Task invokeAfterResponse(SessionEventArgs args)
{ {
if (AfterResponse != null) if (AfterResponse != null)
{ {
await AfterResponse.InvokeAsync(this, args, ExceptionFunc); await AfterResponse.InvokeAsync(this, args, ExceptionFunc);
} }
} }
} }
} }
using System.Text.RegularExpressions; using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.Shared namespace Titanium.Web.Proxy.Shared
{ {
...@@ -14,9 +17,21 @@ namespace Titanium.Web.Proxy.Shared ...@@ -14,9 +17,21 @@ namespace Titanium.Web.Proxy.Shared
internal static readonly char[] SemiColonSplit = { ';' }; internal static readonly char[] SemiColonSplit = { ';' };
internal static readonly char[] EqualSplit = { '=' }; internal static readonly char[] EqualSplit = { '=' };
internal static readonly byte[] NewLine = { (byte)'\r', (byte)'\n' }; internal static readonly string NewLine = "\r\n";
internal static readonly byte[] NewLineBytes = { (byte)'\r', (byte)'\n' };
public static readonly Regex CNRemoverRegex = internal static readonly Regex UriSchemeRegex =
new Regex("^[a-z]*://", RegexOptions.IgnoreCase | RegexOptions.Compiled);
internal static readonly HashSet<string> ProxySupportedCompressions =
new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
KnownHeaders.ContentEncodingGzip,
KnownHeaders.ContentEncodingDeflate,
KnownHeaders.ContentEncodingBrotli
};
internal static readonly Regex CNRemoverRegex =
new Regex(@"^CN\s*=\s*", RegexOptions.IgnoreCase | RegexOptions.Compiled); new Regex(@"^CN\s*=\s*", RegexOptions.IgnoreCase | RegexOptions.Compiled);
} }
} }
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net45</TargetFrameworks>
<RootNamespace>Titanium.Web.Proxy</RootNamespace>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<SignAssembly>True</SignAssembly>
<AssemblyOriginatorKeyFile>StrongNameKey.snk</AssemblyOriginatorKeyFile>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<LangVersion>7.1</LangVersion>
<Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BrotliSharpLib" Version="0.3.1" />
<PackageReference Include="Portable.BouncyCastle" Version="1.8.3" />
<PackageReference Include="StreamExtended" Version="1.0.188-beta" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net45'">
<Reference Include="System.Web" />
</ItemGroup>
<ItemGroup>
<Compile Update="Network\WinAuth\Security\Common.cs">
<ExcludeFromSourceAnalysis>True</ExcludeFromSourceAnalysis>
<ExcludeFromStyleCop>True</ExcludeFromStyleCop>
</Compile>
<Compile Update="Network\WinAuth\Security\LittleEndian.cs">
<ExcludeFromSourceAnalysis>True</ExcludeFromSourceAnalysis>
<ExcludeFromStyleCop>True</ExcludeFromStyleCop>
</Compile>
<Compile Update="Network\WinAuth\Security\Message.cs">
<ExcludeFromSourceAnalysis>True</ExcludeFromSourceAnalysis>
<ExcludeFromStyleCop>True</ExcludeFromStyleCop>
</Compile>
<Compile Update="Network\WinAuth\Security\State.cs">
<ExcludeFromSourceAnalysis>True</ExcludeFromSourceAnalysis>
<ExcludeFromStyleCop>True</ExcludeFromStyleCop>
</Compile>
<Compile Update="Properties\AssemblyInfo.cs">
<ExcludeFromSourceAnalysis>True</ExcludeFromSourceAnalysis>
<ExcludeFromStyleCop>True</ExcludeFromStyleCop>
</Compile>
</ItemGroup>
</Project>
\ No newline at end of file
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<RootNamespace>Titanium.Web.Proxy</RootNamespace>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<SignAssembly>True</SignAssembly>
<AssemblyOriginatorKeyFile>StrongNameKey.snk</AssemblyOriginatorKeyFile>
<DelaySign>False</DelaySign>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<LangVersion>7.1</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BrotliSharpLib" Version="0.3.1" />
<PackageReference Include="Portable.BouncyCastle" Version="1.8.3" />
<PackageReference Include="StreamExtended" Version="1.0.188-beta" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="Microsoft.Win32.Registry">
<Version>4.4.0</Version>
</PackageReference>
<PackageReference Include="System.Security.Principal.Windows">
<Version>4.4.1</Version>
</PackageReference>
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp2.1'">
<PackageReference Include="Microsoft.Win32.Registry">
<Version>4.4.0</Version>
</PackageReference>
<PackageReference Include="System.Security.Principal.Windows">
<Version>4.4.1</Version>
</PackageReference>
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net45'">
<Reference Include="System.Web" />
</ItemGroup>
<ItemGroup>
<Compile Update="Network\WinAuth\Security\Common.cs">
<ExcludeFromSourceAnalysis>True</ExcludeFromSourceAnalysis>
<ExcludeFromStyleCop>True</ExcludeFromStyleCop>
</Compile>
<Compile Update="Network\WinAuth\Security\LittleEndian.cs">
<ExcludeFromSourceAnalysis>True</ExcludeFromSourceAnalysis>
<ExcludeFromStyleCop>True</ExcludeFromStyleCop>
</Compile>
<Compile Update="Network\WinAuth\Security\Message.cs">
<ExcludeFromSourceAnalysis>True</ExcludeFromSourceAnalysis>
<ExcludeFromStyleCop>True</ExcludeFromStyleCop>
</Compile>
<Compile Update="Network\WinAuth\Security\State.cs">
<ExcludeFromSourceAnalysis>True</ExcludeFromSourceAnalysis>
<ExcludeFromStyleCop>True</ExcludeFromStyleCop>
</Compile>
<Compile Update="Properties\AssemblyInfo.cs">
<ExcludeFromSourceAnalysis>True</ExcludeFromSourceAnalysis>
<ExcludeFromStyleCop>True</ExcludeFromStyleCop>
</Compile>
</ItemGroup>
</Project>
\ No newline at end of file
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net45;netstandard2.0</TargetFrameworks> <TargetFrameworks>net45;netstandard2.0</TargetFrameworks>
<RootNamespace>Titanium.Web.Proxy</RootNamespace> <RootNamespace>Titanium.Web.Proxy</RootNamespace>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<SignAssembly>True</SignAssembly> <SignAssembly>True</SignAssembly>
<AssemblyOriginatorKeyFile>StrongNameKey.snk</AssemblyOriginatorKeyFile> <AssemblyOriginatorKeyFile>StrongNameKey.snk</AssemblyOriginatorKeyFile>
<DelaySign>False</DelaySign> <DelaySign>False</DelaySign>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks> <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<LangVersion>7.1</LangVersion> <LangVersion>7.1</LangVersion>
</PropertyGroup> <Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Portable.BouncyCastle" Version="1.8.2" /> <ItemGroup>
<PackageReference Include="StreamExtended" Version="1.0.179" /> <PackageReference Include="BrotliSharpLib" Version="0.3.1" />
</ItemGroup> <PackageReference Include="Portable.BouncyCastle" Version="1.8.3" />
<PackageReference Include="StreamExtended" Version="1.0.188-beta" />
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'"> </ItemGroup>
<PackageReference Include="Microsoft.Win32.Registry">
<Version>4.4.0</Version> <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
</PackageReference> <PackageReference Include="Microsoft.Win32.Registry">
<PackageReference Include="System.Security.Principal.Windows"> <Version>4.4.0</Version>
<Version>4.4.1</Version> </PackageReference>
</PackageReference> <PackageReference Include="System.Security.Principal.Windows">
</ItemGroup> <Version>4.4.1</Version>
</PackageReference>
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp2.1'"> </ItemGroup>
<PackageReference Include="Microsoft.Win32.Registry">
<Version>4.4.0</Version> <ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp2.1'">
</PackageReference> <PackageReference Include="Microsoft.Win32.Registry">
<PackageReference Include="System.Security.Principal.Windows"> <Version>4.4.0</Version>
<Version>4.4.1</Version> </PackageReference>
</PackageReference> <PackageReference Include="System.Security.Principal.Windows">
</ItemGroup> <Version>4.4.1</Version>
</PackageReference>
<ItemGroup Condition="'$(TargetFramework)' == 'net45'"> </ItemGroup>
<Reference Include="System.Web" />
</ItemGroup> <ItemGroup Condition="'$(TargetFramework)' == 'net45'">
<Reference Include="System.Web" />
<ItemGroup> </ItemGroup>
<Compile Update="Network\WinAuth\Security\Common.cs">
<ExcludeFromSourceAnalysis>True</ExcludeFromSourceAnalysis> <ItemGroup>
<ExcludeFromStyleCop>True</ExcludeFromStyleCop> <Compile Update="Network\WinAuth\Security\Common.cs">
</Compile> <ExcludeFromSourceAnalysis>True</ExcludeFromSourceAnalysis>
<Compile Update="Network\WinAuth\Security\LittleEndian.cs"> <ExcludeFromStyleCop>True</ExcludeFromStyleCop>
<ExcludeFromSourceAnalysis>True</ExcludeFromSourceAnalysis> </Compile>
<ExcludeFromStyleCop>True</ExcludeFromStyleCop> <Compile Update="Network\WinAuth\Security\LittleEndian.cs">
</Compile> <ExcludeFromSourceAnalysis>True</ExcludeFromSourceAnalysis>
<Compile Update="Network\WinAuth\Security\Message.cs"> <ExcludeFromStyleCop>True</ExcludeFromStyleCop>
<ExcludeFromSourceAnalysis>True</ExcludeFromSourceAnalysis> </Compile>
<ExcludeFromStyleCop>True</ExcludeFromStyleCop> <Compile Update="Network\WinAuth\Security\Message.cs">
</Compile> <ExcludeFromSourceAnalysis>True</ExcludeFromSourceAnalysis>
<Compile Update="Network\WinAuth\Security\State.cs"> <ExcludeFromStyleCop>True</ExcludeFromStyleCop>
<ExcludeFromSourceAnalysis>True</ExcludeFromSourceAnalysis> </Compile>
<ExcludeFromStyleCop>True</ExcludeFromStyleCop> <Compile Update="Network\WinAuth\Security\State.cs">
</Compile> <ExcludeFromSourceAnalysis>True</ExcludeFromSourceAnalysis>
<Compile Update="Properties\AssemblyInfo.cs"> <ExcludeFromStyleCop>True</ExcludeFromStyleCop>
<ExcludeFromSourceAnalysis>True</ExcludeFromSourceAnalysis> </Compile>
<ExcludeFromStyleCop>True</ExcludeFromStyleCop> <Compile Update="Properties\AssemblyInfo.cs">
</Compile> <ExcludeFromSourceAnalysis>True</ExcludeFromSourceAnalysis>
</ItemGroup> <ExcludeFromStyleCop>True</ExcludeFromStyleCop>
</Compile>
</ItemGroup>
</Project> </Project>
\ No newline at end of file
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
<dependencies> <dependencies>
<dependency id="StreamExtended" version="1.0.179" /> <dependency id="StreamExtended" version="1.0.179" />
<dependency id="Portable.BouncyCastle" version="1.8.2" /> <dependency id="Portable.BouncyCastle" version="1.8.2" />
<dependency id="BrotliSharpLib" version="0.3.1" />
</dependencies> </dependencies>
</metadata> </metadata>
<files> <files>
......
...@@ -63,13 +63,16 @@ namespace Titanium.Web.Proxy ...@@ -63,13 +63,16 @@ namespace Titanium.Web.Proxy
if (endPoint.DecryptSsl && args.DecryptSsl) if (endPoint.DecryptSsl && args.DecryptSsl)
{ {
//don't pass cancellation token here if(EnableTcpServerConnectionPrefetch)
//it could cause floating server connections when client exits {
prefetchConnectionTask = tcpConnectionFactory.GetServerConnection(httpsHostName, endPoint.Port, //don't pass cancellation token here
httpVersion: null, isHttps: true, applicationProtocols: null, isConnect: false, //it could cause floating server connections when client exits
proxyServer: this, upStreamEndPoint: UpStreamEndPoint, externalProxy: UpStreamHttpsProxy, prefetchConnectionTask = tcpConnectionFactory.GetServerConnection(httpsHostName, endPoint.Port,
noCache: false, cancellationToken: CancellationToken.None); httpVersion: null, isHttps: true, applicationProtocols: null, isConnect: false,
proxyServer: this, session: null, upStreamEndPoint: UpStreamEndPoint, externalProxy: UpStreamHttpsProxy,
noCache: false, cancellationToken: CancellationToken.None);
}
SslStream sslStream = null; SslStream sslStream = null;
//do client authentication using fake certificate //do client authentication using fake certificate
...@@ -99,7 +102,7 @@ namespace Titanium.Web.Proxy ...@@ -99,7 +102,7 @@ namespace Titanium.Web.Proxy
{ {
var connection = await tcpConnectionFactory.GetServerConnection(httpsHostName, endPoint.Port, var connection = await tcpConnectionFactory.GetServerConnection(httpsHostName, endPoint.Port,
httpVersion: null, isHttps: false, applicationProtocols: null, httpVersion: null, isHttps: false, applicationProtocols: null,
isConnect: true, proxyServer: this, upStreamEndPoint: UpStreamEndPoint, isConnect: true, proxyServer: this, session:null, upStreamEndPoint: UpStreamEndPoint,
externalProxy: UpStreamHttpsProxy, noCache: true, cancellationToken: cancellationToken); externalProxy: UpStreamHttpsProxy, noCache: true, cancellationToken: cancellationToken);
try try
......
...@@ -35,6 +35,24 @@ ...@@ -35,6 +35,24 @@
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Net.Http" /> <Reference Include="System.Net.Http" />
...@@ -56,8 +74,8 @@ ...@@ -56,8 +74,8 @@
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\Titanium.Web.Proxy\Titanium.Web.Proxy.csproj"> <ProjectReference Include="..\..\src\Titanium.Web.Proxy\Titanium.Web.Proxy.csproj">
<Project>{8d73a1be-868c-42d2-9ece-f32cc1a02906}</Project> <Project>{91018b6d-a7a9-45be-9cb3-79cbb8b169a6}</Project>
<Name>Titanium.Web.Proxy</Name> <Name>Titanium.Web.Proxy</Name>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
......
...@@ -42,6 +42,25 @@ ...@@ -42,6 +42,25 @@
<PropertyGroup> <PropertyGroup>
<AssemblyOriginatorKeyFile>StrongNameKey.snk</AssemblyOriginatorKeyFile> <AssemblyOriginatorKeyFile>StrongNameKey.snk</AssemblyOriginatorKeyFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="System" /> <Reference Include="System" />
</ItemGroup> </ItemGroup>
...@@ -65,13 +84,13 @@ ...@@ -65,13 +84,13 @@
<Compile Include="WinAuthTests.cs" /> <Compile Include="WinAuthTests.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\Titanium.Web.Proxy\Titanium.Web.Proxy.csproj"> <None Include="StrongNameKey.snk" />
<Project>{8d73a1be-868c-42d2-9ece-f32cc1a02906}</Project>
<Name>Titanium.Web.Proxy</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="StrongNameKey.snk" /> <ProjectReference Include="..\..\src\Titanium.Web.Proxy\Titanium.Web.Proxy.csproj">
<Project>{91018b6d-a7a9-45be-9cb3-79cbb8b169a6}</Project>
<Name>Titanium.Web.Proxy</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<Choose> <Choose>
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'"> <When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
......
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