Unverified Commit d8df61ba authored by honfika's avatar honfika Committed by GitHub

Merge pull request #659 from justcoding121/beta

Beta
parents 4a7ccfac 6923f8aa
......@@ -29,7 +29,7 @@ if($Branch -eq "beta" ) { $Version = "$Version-beta" }
$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\2019\Community\MSBuild\Current\Bin\msbuild.exe"
$MSBuild -replace ' ', '` '
FormatTaskName (("-"*25) + "[{0}]" + ("-"*25))
......
......@@ -8,7 +8,7 @@
# version format
version: 3.1.{build}
image: Visual Studio 2017
image: Visual Studio 2019
shallow_clone: false
......
......@@ -99,9 +99,6 @@
</div>
<div class="inheritedMembers">
<h5>Inherited Members</h5>
<div>
<a class="xref" href="Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_BufferSize">SessionEventArgsBase.BufferSize</a>
</div>
<div>
<a class="xref" href="Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_BufferPool">SessionEventArgsBase.BufferPool</a>
</div>
......@@ -188,7 +185,7 @@
<a href="https://github.com/justcoding121/Titanium-Web-Proxy/new/master/apiSpec/new?filename=Titanium_Web_Proxy_EventArguments_TunnelConnectSessionEventArgs_DecryptSsl.md&amp;value=---%0Auid%3A%20Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.DecryptSsl%0Asummary%3A%20'*You%20can%20override%20summary%20for%20the%20API%20here%20using%20*MARKDOWN*%20syntax'%0A---%0A%0A*Please%20type%20below%20more%20information%20about%20this%20API%3A*%0A%0A">Improve this Doc</a>
</span>
<span class="small pull-right mobile-hide">
<a href="https://github.com/justcoding121/Titanium-Web-Proxy/blob/master/src/Titanium.Web.Proxy/EventArguments/TunnelConnectEventArgs.cs/#L26">View Source</a>
<a href="https://github.com/justcoding121/Titanium-Web-Proxy/blob/master/src/Titanium.Web.Proxy/EventArguments/TunnelConnectEventArgs.cs/#L27">View Source</a>
</span>
<a id="Titanium_Web_Proxy_EventArguments_TunnelConnectSessionEventArgs_DecryptSsl_" data-uid="Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.DecryptSsl*"></a>
<h4 id="Titanium_Web_Proxy_EventArguments_TunnelConnectSessionEventArgs_DecryptSsl" data-uid="Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.DecryptSsl">DecryptSsl</h4>
......@@ -220,7 +217,7 @@ Default is true.</p>
<a href="https://github.com/justcoding121/Titanium-Web-Proxy/new/master/apiSpec/new?filename=Titanium_Web_Proxy_EventArguments_TunnelConnectSessionEventArgs_DenyConnect.md&amp;value=---%0Auid%3A%20Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.DenyConnect%0Asummary%3A%20'*You%20can%20override%20summary%20for%20the%20API%20here%20using%20*MARKDOWN*%20syntax'%0A---%0A%0A*Please%20type%20below%20more%20information%20about%20this%20API%3A*%0A%0A">Improve this Doc</a>
</span>
<span class="small pull-right mobile-hide">
<a href="https://github.com/justcoding121/Titanium-Web-Proxy/blob/master/src/Titanium.Web.Proxy/EventArguments/TunnelConnectEventArgs.cs/#L31">View Source</a>
<a href="https://github.com/justcoding121/Titanium-Web-Proxy/blob/master/src/Titanium.Web.Proxy/EventArguments/TunnelConnectEventArgs.cs/#L32">View Source</a>
</span>
<a id="Titanium_Web_Proxy_EventArguments_TunnelConnectSessionEventArgs_DenyConnect_" data-uid="Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.DenyConnect*"></a>
<h4 id="Titanium_Web_Proxy_EventArguments_TunnelConnectSessionEventArgs_DenyConnect" data-uid="Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.DenyConnect">DenyConnect</h4>
......@@ -251,7 +248,7 @@ Default is true.</p>
<a href="https://github.com/justcoding121/Titanium-Web-Proxy/new/master/apiSpec/new?filename=Titanium_Web_Proxy_EventArguments_TunnelConnectSessionEventArgs_IsHttpsConnect.md&amp;value=---%0Auid%3A%20Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.IsHttpsConnect%0Asummary%3A%20'*You%20can%20override%20summary%20for%20the%20API%20here%20using%20*MARKDOWN*%20syntax'%0A---%0A%0A*Please%20type%20below%20more%20information%20about%20this%20API%3A*%0A%0A">Improve this Doc</a>
</span>
<span class="small pull-right mobile-hide">
<a href="https://github.com/justcoding121/Titanium-Web-Proxy/blob/master/src/Titanium.Web.Proxy/EventArguments/TunnelConnectEventArgs.cs/#L36">View Source</a>
<a href="https://github.com/justcoding121/Titanium-Web-Proxy/blob/master/src/Titanium.Web.Proxy/EventArguments/TunnelConnectEventArgs.cs/#L37">View Source</a>
</span>
<a id="Titanium_Web_Proxy_EventArguments_TunnelConnectSessionEventArgs_IsHttpsConnect_" data-uid="Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.IsHttpsConnect*"></a>
<h4 id="Titanium_Web_Proxy_EventArguments_TunnelConnectSessionEventArgs_IsHttpsConnect" data-uid="Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.IsHttpsConnect">IsHttpsConnect</h4>
......@@ -277,6 +274,68 @@ Default is true.</p>
</tr>
</tbody>
</table>
<h3 id="events">Events
</h3>
<span class="small pull-right mobile-hide">
<span class="divider">|</span>
<a href="https://github.com/justcoding121/Titanium-Web-Proxy/new/master/apiSpec/new?filename=Titanium_Web_Proxy_EventArguments_TunnelConnectSessionEventArgs_DecryptedDataReceived.md&amp;value=---%0Auid%3A%20Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.DecryptedDataReceived%0Asummary%3A%20'*You%20can%20override%20summary%20for%20the%20API%20here%20using%20*MARKDOWN*%20syntax'%0A---%0A%0A*Please%20type%20below%20more%20information%20about%20this%20API%3A*%0A%0A">Improve this Doc</a>
</span>
<span class="small pull-right mobile-hide">
<a href="https://github.com/justcoding121/Titanium-Web-Proxy/blob/master/src/Titanium.Web.Proxy/EventArguments/TunnelConnectEventArgs.cs/#L53">View Source</a>
</span>
<h4 id="Titanium_Web_Proxy_EventArguments_TunnelConnectSessionEventArgs_DecryptedDataReceived" data-uid="Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.DecryptedDataReceived">DecryptedDataReceived</h4>
<div class="markdown level1 summary"><p>Fired when decrypted data is received within this session from client/server.</p>
</div>
<div class="markdown level1 conceptual"></div>
<h5 class="decalaration">Declaration</h5>
<div class="codewrapper">
<pre><code class="lang-csharp hljs">public event EventHandler&lt;DataEventArgs&gt; DecryptedDataReceived</code></pre>
</div>
<h5 class="eventType">Event Type</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.eventhandler-1">EventHandler</a>&lt;<span class="xref">DataEventArgs</span>&gt;</td>
<td></td>
</tr>
</tbody>
</table>
<span class="small pull-right mobile-hide">
<span class="divider">|</span>
<a href="https://github.com/justcoding121/Titanium-Web-Proxy/new/master/apiSpec/new?filename=Titanium_Web_Proxy_EventArguments_TunnelConnectSessionEventArgs_DecryptedDataSent.md&amp;value=---%0Auid%3A%20Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.DecryptedDataSent%0Asummary%3A%20'*You%20can%20override%20summary%20for%20the%20API%20here%20using%20*MARKDOWN*%20syntax'%0A---%0A%0A*Please%20type%20below%20more%20information%20about%20this%20API%3A*%0A%0A">Improve this Doc</a>
</span>
<span class="small pull-right mobile-hide">
<a href="https://github.com/justcoding121/Titanium-Web-Proxy/blob/master/src/Titanium.Web.Proxy/EventArguments/TunnelConnectEventArgs.cs/#L48">View Source</a>
</span>
<h4 id="Titanium_Web_Proxy_EventArguments_TunnelConnectSessionEventArgs_DecryptedDataSent" data-uid="Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.DecryptedDataSent">DecryptedDataSent</h4>
<div class="markdown level1 summary"><p>Fired when decrypted data is sent within this session to server/client.</p>
</div>
<div class="markdown level1 conceptual"></div>
<h5 class="decalaration">Declaration</h5>
<div class="codewrapper">
<pre><code class="lang-csharp hljs">public event EventHandler&lt;DataEventArgs&gt; DecryptedDataSent</code></pre>
</div>
<h5 class="eventType">Event Type</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.eventhandler-1">EventHandler</a>&lt;<span class="xref">DataEventArgs</span>&gt;</td>
<td></td>
</tr>
</tbody>
</table>
<h3 id="implements">Implements</h3>
<div>
<a class="xref" href="https://docs.microsoft.com/dotnet/api/system.idisposable">System.IDisposable</a>
......@@ -292,7 +351,7 @@ Default is true.</p>
<a href="https://github.com/justcoding121/Titanium-Web-Proxy/new/master/apiSpec/new?filename=Titanium_Web_Proxy_EventArguments_TunnelConnectSessionEventArgs.md&amp;value=---%0Auid%3A%20Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs%0Asummary%3A%20'*You%20can%20override%20summary%20for%20the%20API%20here%20using%20*MARKDOWN*%20syntax'%0A---%0A%0A*Please%20type%20below%20more%20information%20about%20this%20API%3A*%0A%0A" class="contribution-link">Improve this Doc</a>
</li>
<li>
<a href="https://github.com/justcoding121/Titanium-Web-Proxy/blob/master/src/Titanium.Web.Proxy/EventArguments/TunnelConnectEventArgs.cs/#L11" class="contribution-link">View Source</a>
<a href="https://github.com/justcoding121/Titanium-Web-Proxy/blob/master/src/Titanium.Web.Proxy/EventArguments/TunnelConnectEventArgs.cs/#L12" class="contribution-link">View Source</a>
</li>
</ul>
</div>
......
......@@ -104,9 +104,6 @@ So client application know that it is communicating with a proxy server.</p>
<div>
<a class="xref" href="Titanium.Web.Proxy.Models.ProxyEndPoint.html#Titanium_Web_Proxy_Models_ProxyEndPoint_DecryptSsl">ProxyEndPoint.DecryptSsl</a>
</div>
<div>
<a class="xref" href="Titanium.Web.Proxy.Models.ProxyEndPoint.html#Titanium_Web_Proxy_Models_ProxyEndPoint_IpV6Enabled">ProxyEndPoint.IpV6Enabled</a>
</div>
<div>
<a class="xref" href="Titanium.Web.Proxy.Models.ProxyEndPoint.html#Titanium_Web_Proxy_Models_ProxyEndPoint_GenericCertificate">ProxyEndPoint.GenericCertificate</a>
</div>
......
......@@ -206,7 +206,7 @@
<a href="https://github.com/justcoding121/Titanium-Web-Proxy/new/master/apiSpec/new?filename=Titanium_Web_Proxy_Models_ProxyEndPoint_GenericCertificate.md&amp;value=---%0Auid%3A%20Titanium.Web.Proxy.Models.ProxyEndPoint.GenericCertificate%0Asummary%3A%20'*You%20can%20override%20summary%20for%20the%20API%20here%20using%20*MARKDOWN*%20syntax'%0A---%0A%0A*Please%20type%20below%20more%20information%20about%20this%20API%3A*%0A%0A">Improve this Doc</a>
</span>
<span class="small pull-right mobile-hide">
<a href="https://github.com/justcoding121/Titanium-Web-Proxy/blob/master/src/Titanium.Web.Proxy/Models/ProxyEndPoint.cs/#L56">View Source</a>
<a href="https://github.com/justcoding121/Titanium-Web-Proxy/blob/master/src/Titanium.Web.Proxy/Models/ProxyEndPoint.cs/#L48">View Source</a>
</span>
<a id="Titanium_Web_Proxy_Models_ProxyEndPoint_GenericCertificate_" data-uid="Titanium.Web.Proxy.Models.ProxyEndPoint.GenericCertificate*"></a>
<h4 id="Titanium_Web_Proxy_Models_ProxyEndPoint_GenericCertificate" data-uid="Titanium.Web.Proxy.Models.ProxyEndPoint.GenericCertificate">GenericCertificate</h4>
......@@ -263,37 +263,6 @@
</tr>
</tbody>
</table>
<span class="small pull-right mobile-hide">
<span class="divider">|</span>
<a href="https://github.com/justcoding121/Titanium-Web-Proxy/new/master/apiSpec/new?filename=Titanium_Web_Proxy_Models_ProxyEndPoint_IpV6Enabled.md&amp;value=---%0Auid%3A%20Titanium.Web.Proxy.Models.ProxyEndPoint.IpV6Enabled%0Asummary%3A%20'*You%20can%20override%20summary%20for%20the%20API%20here%20using%20*MARKDOWN*%20syntax'%0A---%0A%0A*Please%20type%20below%20more%20information%20about%20this%20API%3A*%0A%0A">Improve this Doc</a>
</span>
<span class="small pull-right mobile-hide">
<a href="https://github.com/justcoding121/Titanium-Web-Proxy/blob/master/src/Titanium.Web.Proxy/Models/ProxyEndPoint.cs/#L48">View Source</a>
</span>
<a id="Titanium_Web_Proxy_Models_ProxyEndPoint_IpV6Enabled_" data-uid="Titanium.Web.Proxy.Models.ProxyEndPoint.IpV6Enabled*"></a>
<h4 id="Titanium_Web_Proxy_Models_ProxyEndPoint_IpV6Enabled" data-uid="Titanium.Web.Proxy.Models.ProxyEndPoint.IpV6Enabled">IpV6Enabled</h4>
<div class="markdown level1 summary"><p>Is IPv6 enabled?</p>
</div>
<div class="markdown level1 conceptual"></div>
<h5 class="decalaration">Declaration</h5>
<div class="codewrapper">
<pre><code class="lang-csharp hljs">public bool IpV6Enabled { 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>
<span class="small pull-right mobile-hide">
<span class="divider">|</span>
<a href="https://github.com/justcoding121/Titanium-Web-Proxy/new/master/apiSpec/new?filename=Titanium_Web_Proxy_Models_ProxyEndPoint_Port.md&amp;value=---%0Auid%3A%20Titanium.Web.Proxy.Models.ProxyEndPoint.Port%0Asummary%3A%20'*You%20can%20override%20summary%20for%20the%20API%20here%20using%20*MARKDOWN*%20syntax'%0A---%0A%0A*Please%20type%20below%20more%20information%20about%20this%20API%3A*%0A%0A">Improve this Doc</a>
......
......@@ -104,9 +104,6 @@ Useful when requests are redirected to this proxy end point through port forward
<div>
<a class="xref" href="Titanium.Web.Proxy.Models.ProxyEndPoint.html#Titanium_Web_Proxy_Models_ProxyEndPoint_DecryptSsl">ProxyEndPoint.DecryptSsl</a>
</div>
<div>
<a class="xref" href="Titanium.Web.Proxy.Models.ProxyEndPoint.html#Titanium_Web_Proxy_Models_ProxyEndPoint_IpV6Enabled">ProxyEndPoint.IpV6Enabled</a>
</div>
<div>
<a class="xref" href="Titanium.Web.Proxy.Models.ProxyEndPoint.html#Titanium_Web_Proxy_Models_ProxyEndPoint_GenericCertificate">ProxyEndPoint.GenericCertificate</a>
</div>
......
This diff is collapsed.
......@@ -529,12 +529,6 @@ references:
commentId: F:Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.BufferPool
fullName: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.BufferPool
nameWithType: SessionEventArgsBase.BufferPool
- uid: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.BufferSize
name: BufferSize
href: api/Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_BufferSize
commentId: F:Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.BufferSize
fullName: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.BufferSize
nameWithType: SessionEventArgsBase.BufferSize
- uid: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.ClientEndPoint
name: ClientEndPoint
href: api/Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_ClientEndPoint
......@@ -715,6 +709,18 @@ references:
commentId: T:Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs
fullName: Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs
nameWithType: TunnelConnectSessionEventArgs
- uid: Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.DecryptedDataReceived
name: DecryptedDataReceived
href: api/Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.html#Titanium_Web_Proxy_EventArguments_TunnelConnectSessionEventArgs_DecryptedDataReceived
commentId: E:Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.DecryptedDataReceived
fullName: Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.DecryptedDataReceived
nameWithType: TunnelConnectSessionEventArgs.DecryptedDataReceived
- uid: Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.DecryptedDataSent
name: DecryptedDataSent
href: api/Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.html#Titanium_Web_Proxy_EventArguments_TunnelConnectSessionEventArgs_DecryptedDataSent
commentId: E:Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.DecryptedDataSent
fullName: Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.DecryptedDataSent
nameWithType: TunnelConnectSessionEventArgs.DecryptedDataSent
- uid: Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.DecryptSsl
name: DecryptSsl
href: api/Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.html#Titanium_Web_Proxy_EventArguments_TunnelConnectSessionEventArgs_DecryptSsl
......@@ -2865,19 +2871,6 @@ references:
isSpec: "True"
fullName: Titanium.Web.Proxy.Models.ProxyEndPoint.IpAddress
nameWithType: ProxyEndPoint.IpAddress
- uid: Titanium.Web.Proxy.Models.ProxyEndPoint.IpV6Enabled
name: IpV6Enabled
href: api/Titanium.Web.Proxy.Models.ProxyEndPoint.html#Titanium_Web_Proxy_Models_ProxyEndPoint_IpV6Enabled
commentId: P:Titanium.Web.Proxy.Models.ProxyEndPoint.IpV6Enabled
fullName: Titanium.Web.Proxy.Models.ProxyEndPoint.IpV6Enabled
nameWithType: ProxyEndPoint.IpV6Enabled
- uid: Titanium.Web.Proxy.Models.ProxyEndPoint.IpV6Enabled*
name: IpV6Enabled
href: api/Titanium.Web.Proxy.Models.ProxyEndPoint.html#Titanium_Web_Proxy_Models_ProxyEndPoint_IpV6Enabled_
commentId: Overload:Titanium.Web.Proxy.Models.ProxyEndPoint.IpV6Enabled
isSpec: "True"
fullName: Titanium.Web.Proxy.Models.ProxyEndPoint.IpV6Enabled
nameWithType: ProxyEndPoint.IpV6Enabled
- uid: Titanium.Web.Proxy.Models.ProxyEndPoint.Port
name: Port
href: api/Titanium.Web.Proxy.Models.ProxyEndPoint.html#Titanium_Web_Proxy_Models_ProxyEndPoint_Port
......@@ -3369,19 +3362,6 @@ references:
isSpec: "True"
fullName: Titanium.Web.Proxy.ProxyServer.BufferPool
nameWithType: ProxyServer.BufferPool
- uid: Titanium.Web.Proxy.ProxyServer.BufferSize
name: BufferSize
href: api/Titanium.Web.Proxy.ProxyServer.html#Titanium_Web_Proxy_ProxyServer_BufferSize
commentId: P:Titanium.Web.Proxy.ProxyServer.BufferSize
fullName: Titanium.Web.Proxy.ProxyServer.BufferSize
nameWithType: ProxyServer.BufferSize
- uid: Titanium.Web.Proxy.ProxyServer.BufferSize*
name: BufferSize
href: api/Titanium.Web.Proxy.ProxyServer.html#Titanium_Web_Proxy_ProxyServer_BufferSize_
commentId: Overload:Titanium.Web.Proxy.ProxyServer.BufferSize
isSpec: "True"
fullName: Titanium.Web.Proxy.ProxyServer.BufferSize
nameWithType: ProxyServer.BufferSize
- uid: Titanium.Web.Proxy.ProxyServer.CertificateManager
name: CertificateManager
href: api/Titanium.Web.Proxy.ProxyServer.html#Titanium_Web_Proxy_ProxyServer_CertificateManager
......
......@@ -278,7 +278,6 @@ namespace Titanium.Web.Proxy.Examples.Basic
}
@lock.Release();
}
///// <summary>
......
......@@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net461;netcoreapp2.1</TargetFrameworks>
<TargetFrameworks>net461;netcoreapp3.0</TargetFrameworks>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<LangVersion>7.1</LangVersion>
</PropertyGroup>
......
......@@ -31,6 +31,7 @@
<GridViewColumn Header="Process" DisplayMemberBinding="{Binding Process}" />
<GridViewColumn Header="SentBytes" DisplayMemberBinding="{Binding SentDataCount}" />
<GridViewColumn Header="ReceivedBytes" DisplayMemberBinding="{Binding ReceivedDataCount}" />
<GridViewColumn Header="ClientEndPoint" DisplayMemberBinding="{Binding ClientEndPoint}" />
</GridView>
</ListView.View>
</ListView>
......
......@@ -226,39 +226,60 @@ namespace Titanium.Web.Proxy.Examples.Wpf
{
Number = lastSessionNumber,
HttpClient = e.HttpClient,
ClientEndPoint = e.ClientEndPoint,
IsTunnelConnect = isTunnelConnect
};
if (isTunnelConnect || e.HttpClient.Request.UpgradeToWebSocket)
//if (isTunnelConnect || e.HttpClient.Request.UpgradeToWebSocket)
e.DataReceived += (sender, args) =>
{
e.DataReceived += (sender, args) =>
var session = (SessionEventArgsBase)sender;
if (sessionDictionary.TryGetValue(session.HttpClient, out var li))
{
var session = (SessionEventArgsBase)sender;
if (sessionDictionary.TryGetValue(session.HttpClient, out var li))
var tunnelType = session.HttpClient.ConnectRequest?.TunnelType ?? TunnelType.Unknown;
if (tunnelType != TunnelType.Unknown)
{
var tunnelType = session.HttpClient.ConnectRequest?.TunnelType ?? TunnelType.Unknown;
if (tunnelType != TunnelType.Unknown)
{
li.Protocol = TunnelTypeToString(tunnelType);
}
li.Protocol = TunnelTypeToString(tunnelType);
}
li.ReceivedDataCount += args.Count;
AppendTransferLog(session.GetHashCode() + (isTunnelConnect ? "_tunnel" : "") + "_received",
args.Buffer, args.Offset, args.Count);
}
};
li.ReceivedDataCount += args.Count;
e.DataSent += (sender, args) =>
{
var session = (SessionEventArgsBase)sender;
if (sessionDictionary.TryGetValue(session.HttpClient, out var li))
{
var tunnelType = session.HttpClient.ConnectRequest?.TunnelType ?? TunnelType.Unknown;
if (tunnelType != TunnelType.Unknown)
{
li.Protocol = TunnelTypeToString(tunnelType);
}
};
e.DataSent += (sender, args) =>
li.SentDataCount += args.Count;
AppendTransferLog(session.GetHashCode() + (isTunnelConnect ? "_tunnel" : "") + "_sent",
args.Buffer, args.Offset, args.Count);
}
};
if (e is TunnelConnectSessionEventArgs te)
{
te.DecryptedDataReceived += (sender, args) =>
{
var session = (SessionEventArgsBase)sender;
if (sessionDictionary.TryGetValue(session.HttpClient, out var li))
{
var tunnelType = session.HttpClient.ConnectRequest?.TunnelType ?? TunnelType.Unknown;
if (tunnelType != TunnelType.Unknown)
{
li.Protocol = TunnelTypeToString(tunnelType);
}
AppendTransferLog(session.GetHashCode() + "_decrypted_received", args.Buffer, args.Offset,
args.Count);
};
li.SentDataCount += args.Count;
}
te.DecryptedDataSent += (sender, args) =>
{
var session = (SessionEventArgsBase)sender;
AppendTransferLog(session.GetHashCode() + "_decrypted_sent", args.Buffer, args.Offset, args.Count);
};
}
......@@ -266,6 +287,15 @@ namespace Titanium.Web.Proxy.Examples.Wpf
return item;
}
private void AppendTransferLog(string fileName, byte[] buffer, int offset, int count)
{
//string basePath = @"c:\!titanium\";
//using (var fs = new FileStream(basePath + fileName, FileMode.Append, FileAccess.Write, FileShare.Read))
//{
// fs.Write(buffer, offset, count);
//}
}
private string TunnelTypeToString(TunnelType tunnelType)
{
switch (tunnelType)
......
using System;
using System.ComponentModel;
using System.Net;
using System.Runtime.CompilerServices;
using Titanium.Web.Proxy.Examples.Wpf.Annotations;
using Titanium.Web.Proxy.Http;
......@@ -22,6 +23,8 @@ namespace Titanium.Web.Proxy.Examples.Wpf
public HttpWebClient HttpClient { get; set; }
public IPEndPoint ClientEndPoint { get; set; }
public bool IsTunnelConnect { get; set; }
public string StatusCode
......
......@@ -6,11 +6,11 @@
<UseWPF>true</UseWPF>
</PropertyGroup>
<!--<ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net461'">
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="WindowsBase" />
</ItemGroup>-->
</ItemGroup>
<ItemGroup>
<Compile Remove="Properties\AssemblyInfo.cs" />
......@@ -20,23 +20,6 @@
<ProjectReference Include="..\..\src\Titanium.Web.Proxy\Titanium.Web.Proxy.csproj" />
</ItemGroup>
<!--<ItemGroup>
<ApplicationDefinition Include="App.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Page Include="MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Compile Update="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
</Compile>
<Compile Update="MainWindow.xaml.cs">
<DependentUpon>MainWindow.xaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
......@@ -52,16 +35,6 @@
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<None Update="App.xaml">
<Generator>MSBuild:Compile</Generator>
</None>
<None Update="MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>
</None>
<None Update="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
</ItemGroup>-->
</ItemGroup>
</Project>
\ No newline at end of file
......@@ -68,6 +68,7 @@ Global
{4406CE17-9A39-4F28-8363-6169A4F799C1}.Debug|x64.ActiveCfg = Debug|Any CPU
{4406CE17-9A39-4F28-8363-6169A4F799C1}.Debug|x64.Build.0 = Debug|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|x64.ActiveCfg = Release|Any CPU
{4406CE17-9A39-4F28-8363-6169A4F799C1}.Release|x64.Build.0 = Release|Any CPU
{1D053D72-DCB4-4517-ACDD-D35ADC186950}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
......@@ -89,7 +90,7 @@ Global
{1D053D72-DCB4-4517-ACDD-D35ADC186950} = {BC1E0789-D348-49CF-8B67-5E99D50EDF64}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {625C1EB5-44CF-47DE-A85A-B4C8C40ED90A}
EnterpriseLibraryConfigurationToolBinariesPath = .1.505.2\lib\NET35
SolutionGuid = {625C1EB5-44CF-47DE-A85A-B4C8C40ED90A}
EndGlobalSection
EndGlobal
......@@ -22,15 +22,10 @@ namespace Titanium.Web.Proxy
// if user callback is registered then do it
if (ServerCertificateValidationCallback != null)
{
var args = new CertificateValidationEventArgs
{
Certificate = certificate,
Chain = chain,
SslPolicyErrors = sslPolicyErrors
};
var args = new CertificateValidationEventArgs(certificate, chain, sslPolicyErrors);
// why is the sender null?
ServerCertificateValidationCallback.InvokeAsync(this, args, exceptionFunc).Wait();
ServerCertificateValidationCallback.InvokeAsync(this, args, ExceptionFunc).Wait();
return args.IsValid;
}
......@@ -53,11 +48,11 @@ namespace Titanium.Web.Proxy
/// <param name="remoteCertificate">The remote certificate of server.</param>
/// <param name="acceptableIssuers">The acceptable issues for client certificate as listed by server.</param>
/// <returns></returns>
internal X509Certificate SelectClientCertificate(object sender, string targetHost,
internal X509Certificate? SelectClientCertificate(object sender, string targetHost,
X509CertificateCollection localCertificates,
X509Certificate remoteCertificate, string[] acceptableIssuers)
{
X509Certificate clientCertificate = null;
X509Certificate? clientCertificate = null;
if (acceptableIssuers != null && acceptableIssuers.Length > 0 && localCertificates != null &&
localCertificates.Count > 0)
......@@ -90,7 +85,7 @@ namespace Titanium.Web.Proxy
};
// why is the sender null?
ClientCertificateSelectionCallback.InvokeAsync(this, args, exceptionFunc).Wait();
ClientCertificateSelectionCallback.InvokeAsync(this, args, ExceptionFunc).Wait();
return args.ClientCertificate;
}
......
using System;
using System.IO;
using System.IO.Compression;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.Compression
......@@ -11,15 +10,15 @@ namespace Titanium.Web.Proxy.Compression
/// </summary>
internal static class CompressionFactory
{
internal static Stream Create(string type, Stream stream, bool leaveOpen = true)
internal static Stream Create(HttpCompression type, Stream stream, bool leaveOpen = true)
{
switch (type)
{
case KnownHeaders.ContentEncodingGzip:
case HttpCompression.Gzip:
return new GZipStream(stream, CompressionMode.Compress, leaveOpen);
case KnownHeaders.ContentEncodingDeflate:
case HttpCompression.Deflate:
return new DeflateStream(stream, CompressionMode.Compress, leaveOpen);
case KnownHeaders.ContentEncodingBrotli:
case HttpCompression.Brotli:
return new BrotliSharpLib.BrotliStream(stream, CompressionMode.Compress, leaveOpen);
default:
throw new Exception($"Unsupported compression mode: {type}");
......
using System;
using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.Compression
{
internal static class CompressionUtil
{
public static HttpCompression CompressionNameToEnum(string name)
{
if (KnownHeaders.ContentEncodingGzip.Equals(name))
return HttpCompression.Gzip;
if (KnownHeaders.ContentEncodingDeflate.Equals(name))
return HttpCompression.Deflate;
if (KnownHeaders.ContentEncodingBrotli.Equals(name))
return HttpCompression.Brotli;
return HttpCompression.Unsupported;
}
}
}
using System;
using System.IO;
using System.IO.Compression;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.Compression
......@@ -11,15 +10,15 @@ namespace Titanium.Web.Proxy.Compression
/// </summary>
internal class DecompressionFactory
{
internal static Stream Create(string type, Stream stream, bool leaveOpen = true)
internal static Stream Create(HttpCompression type, Stream stream, bool leaveOpen = true)
{
switch (type)
{
case KnownHeaders.ContentEncodingGzip:
case HttpCompression.Gzip:
return new GZipStream(stream, CompressionMode.Decompress, leaveOpen);
case KnownHeaders.ContentEncodingDeflate:
case HttpCompression.Deflate:
return new DeflateStream(stream, CompressionMode.Decompress, leaveOpen);
case KnownHeaders.ContentEncodingBrotli:
case HttpCompression.Brotli:
return new BrotliSharpLib.BrotliStream(stream, CompressionMode.Decompress, leaveOpen);
default:
throw new Exception($"Unsupported decompression mode: {type}");
......
using System.IO.Compression;
namespace Titanium.Web.Proxy.Compression
{
internal enum HttpCompression
{
Unsupported,
Gzip,
Deflate,
Brotli,
}
}
......@@ -10,16 +10,17 @@ namespace Titanium.Web.Proxy.EventArguments
{
internal readonly CancellationTokenSource TaskCancellationSource;
internal BeforeSslAuthenticateEventArgs(CancellationTokenSource taskCancellationSource)
internal BeforeSslAuthenticateEventArgs(CancellationTokenSource taskCancellationSource, string sniHostName)
{
TaskCancellationSource = taskCancellationSource;
SniHostName = sniHostName;
}
/// <summary>
/// The server name indication hostname if available. Otherwise the generic certificate hostname of
/// TransparentEndPoint.
/// </summary>
public string SniHostName { get; internal set; }
public string SniHostName { get; }
/// <summary>
/// Should we decrypt the SSL request?
......
......@@ -11,31 +11,31 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary>
/// The proxy server instance.
/// </summary>
public object Sender { get; internal set; }
public object? Sender { get; internal set; }
/// <summary>
/// The remote hostname to which we are authenticating against.
/// </summary>
public string TargetHost { get; internal set; }
public string? TargetHost { get; internal set; }
/// <summary>
/// Local certificates in store with matching issuers requested by TargetHost website.
/// </summary>
public X509CertificateCollection LocalCertificates { get; internal set; }
public X509CertificateCollection? LocalCertificates { get; internal set; }
/// <summary>
/// Certificate of the remote server.
/// </summary>
public X509Certificate RemoteCertificate { get; internal set; }
public X509Certificate? RemoteCertificate { get; internal set; }
/// <summary>
/// Acceptable issuers as listed by remote server.
/// </summary>
public string[] AcceptableIssuers { get; internal set; }
public string[]? AcceptableIssuers { get; internal set; }
/// <summary>
/// Client Certificate we selected. Set this value to override.
/// </summary>
public X509Certificate ClientCertificate { get; set; }
public X509Certificate? ClientCertificate { get; set; }
}
}
......@@ -10,20 +10,27 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary>
public class CertificateValidationEventArgs : EventArgs
{
public CertificateValidationEventArgs(X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
Certificate = certificate;
Chain = chain;
SslPolicyErrors = sslPolicyErrors;
}
/// <summary>
/// Server certificate.
/// </summary>
public X509Certificate Certificate { get; internal set; }
public X509Certificate Certificate { get; }
/// <summary>
/// Certificate chain.
/// </summary>
public X509Chain Chain { get; internal set; }
public X509Chain Chain { get; }
/// <summary>
/// SSL policy errors.
/// </summary>
public SslPolicyErrors SslPolicyErrors { get; internal set; }
public SslPolicyErrors SslPolicyErrors { get; }
/// <summary>
/// Is the given server certificate valid?
......
......@@ -11,13 +11,13 @@ namespace Titanium.Web.Proxy.EventArguments
internal class LimitedStream : Stream
{
private readonly IBufferPool bufferPool;
private readonly ICustomStreamReader baseStream;
private readonly CustomBufferedStream baseStream;
private readonly bool isChunked;
private long bytesRemaining;
private bool readChunkTrail;
internal LimitedStream(ICustomStreamReader baseStream, IBufferPool bufferPool, bool isChunked,
internal LimitedStream(CustomBufferedStream baseStream, IBufferPool bufferPool, bool isChunked,
long contentLength)
{
this.baseStream = baseStream;
......@@ -49,12 +49,12 @@ namespace Titanium.Web.Proxy.EventArguments
if (readChunkTrail)
{
// read the chunk trail of the previous chunk
string s = baseStream.ReadLineAsync().Result;
string? s = baseStream.ReadLineAsync().Result;
}
readChunkTrail = true;
string chunkHead = baseStream.ReadLineAsync().Result;
string? chunkHead = baseStream.ReadLineAsync().Result!;
int idx = chunkHead.IndexOf(";", StringComparison.Ordinal);
if (idx >= 0)
{
......@@ -73,7 +73,9 @@ namespace Titanium.Web.Proxy.EventArguments
bytesRemaining = -1;
// chunk trail
baseStream.ReadLineAsync().Wait();
var task = baseStream.ReadLineAsync();
if (!task.IsCompleted)
task.AsTask().Wait();
}
}
......@@ -132,7 +134,7 @@ namespace Titanium.Web.Proxy.EventArguments
{
if (bytesRemaining != -1)
{
var buffer = bufferPool.GetBuffer(baseStream.BufferSize);
var buffer = bufferPool.GetBuffer();
try
{
int res = await ReadAsync(buffer, 0, buffer.Length);
......
......@@ -20,13 +20,17 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary>
public abstract class SessionEventArgsBase : EventArgs, IDisposable
{
private static bool isWindowsAuthenticationSupported => RunTime.IsWindows;
internal readonly CancellationTokenSource CancellationTokenSource;
internal TcpServerConnection ServerConnection => HttpClient.Connection;
internal TcpClientConnection ClientConnection => ProxyClient.Connection;
protected readonly int BufferSize;
protected readonly IBufferPool BufferPool;
protected readonly ExceptionHandler ExceptionFunc;
private bool enableWinAuth;
/// <summary>
/// Relative milliseconds for various events.
......@@ -36,26 +40,19 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary>
/// Initializes a new instance of the <see cref="SessionEventArgsBase" /> class.
/// </summary>
private SessionEventArgsBase(ProxyServer server, ProxyEndPoint endPoint,
CancellationTokenSource cancellationTokenSource)
private protected SessionEventArgsBase(ProxyServer server, ProxyEndPoint endPoint,
ProxyClient proxyClient, ConnectRequest? connectRequest, Request request, CancellationTokenSource cancellationTokenSource)
{
BufferSize = server.BufferSize;
BufferPool = server.BufferPool;
ExceptionFunc = server.ExceptionFunc;
TimeLine["Session Created"] = DateTime.Now;
}
protected SessionEventArgsBase(ProxyServer server, ProxyEndPoint endPoint,
CancellationTokenSource cancellationTokenSource,
Request request) : this(server, endPoint, cancellationTokenSource)
{
CancellationTokenSource = cancellationTokenSource;
ProxyClient = new ProxyClient();
HttpClient = new HttpWebClient(request);
ProxyClient = proxyClient;
HttpClient = new HttpWebClient(connectRequest, request, new Lazy<int>(() => ProxyClient.Connection.GetProcessId(endPoint)));
LocalEndPoint = endPoint;
HttpClient.ProcessId = new Lazy<int>(() => ProxyClient.Connection.GetProcessId(endPoint));
EnableWinAuth = server.EnableWinAuth && isWindowsAuthenticationSupported;
}
/// <summary>
......@@ -67,12 +64,27 @@ namespace Titanium.Web.Proxy.EventArguments
/// Returns a user data for this request/response session which is
/// same as the user data of HttpClient.
/// </summary>
public object UserData
public object? UserData
{
get => HttpClient.UserData;
set => HttpClient.UserData = value;
}
/// <summary>
/// Enable/disable Windows Authentication (NTLM/Kerberos) for the current session.
/// </summary>
public bool EnableWinAuth
{
get => enableWinAuth;
set
{
if (value && !isWindowsAuthenticationSupported)
throw new Exception("Windows Authentication is not supported");
enableWinAuth = value;
}
}
/// <summary>
/// Does this session uses SSL?
/// </summary>
......@@ -94,7 +106,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary>
/// Are we using a custom upstream HTTP(S) proxy?
/// </summary>
public ExternalProxy CustomUpStreamProxyUsed { get; internal set; }
public ExternalProxy? CustomUpStreamProxyUsed { get; internal set; }
/// <summary>
/// Local endpoint via which we make the request.
......@@ -109,7 +121,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary>
/// The last exception that happened.
/// </summary>
public Exception Exception { get; internal set; }
public Exception? Exception { get; internal set; }
/// <summary>
/// Implements cleanup here.
......@@ -128,12 +140,12 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary>
/// Fired when data is sent within this session to server/client.
/// </summary>
public event EventHandler<DataEventArgs> DataSent;
public event EventHandler<DataEventArgs>? DataSent;
/// <summary>
/// Fired when data is received within this session from client/server.
/// </summary>
public event EventHandler<DataEventArgs> DataReceived;
public event EventHandler<DataEventArgs>? DataReceived;
internal void OnDataSent(byte[] buffer, int offset, int count)
{
......
......@@ -2,6 +2,8 @@
using System.Threading;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network;
using Titanium.Web.Proxy.StreamExtended.Network;
namespace Titanium.Web.Proxy.EventArguments
{
......@@ -13,10 +15,9 @@ namespace Titanium.Web.Proxy.EventArguments
private bool? isHttpsConnect;
internal TunnelConnectSessionEventArgs(ProxyServer server, ProxyEndPoint endPoint, ConnectRequest connectRequest,
CancellationTokenSource cancellationTokenSource)
: base(server, endPoint, cancellationTokenSource, connectRequest)
ProxyClient proxyClient, CancellationTokenSource cancellationTokenSource)
: base(server, endPoint, proxyClient, connectRequest, connectRequest, cancellationTokenSource)
{
HttpClient.ConnectRequest = connectRequest;
}
/// <summary>
......@@ -40,5 +41,39 @@ namespace Titanium.Web.Proxy.EventArguments
internal set => isHttpsConnect = value;
}
/// <summary>
/// Fired when decrypted data is sent within this session to server/client.
/// </summary>
public event EventHandler<DataEventArgs>? DecryptedDataSent;
/// <summary>
/// Fired when decrypted data is received within this session from client/server.
/// </summary>
public event EventHandler<DataEventArgs>? DecryptedDataReceived;
internal void OnDecryptedDataSent(byte[] buffer, int offset, int count)
{
try
{
DecryptedDataSent?.Invoke(this, new DataEventArgs(buffer, offset, count));
}
catch (Exception ex)
{
ExceptionFunc(new Exception("Exception thrown in user event", ex));
}
}
internal void OnDecryptedDataReceived(byte[] buffer, int offset, int count)
{
try
{
DecryptedDataReceived?.Invoke(this, new DataEventArgs(buffer, offset, count));
}
catch (Exception ex)
{
ExceptionFunc(new Exception("Exception thrown in user event", ex));
}
}
}
}
using System;
using System;
namespace Titanium.Web.Proxy
{
......
......@@ -22,7 +22,7 @@ namespace Titanium.Web.Proxy.Exceptions
/// </summary>
/// <param name="message">Exception message</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)
{
}
}
......
......@@ -14,7 +14,7 @@ namespace Titanium.Web.Proxy.Exceptions
/// <param name="message">Message for this exception</param>
/// <param name="innerException">Associated inner exception</param>
/// <param name="session">Instance of <see cref="EventArguments.SessionEventArgs" /> associated to the exception</param>
internal ProxyHttpException(string message, Exception innerException, SessionEventArgs session) : base(
internal ProxyHttpException(string message, Exception? innerException, SessionEventArgs? session) : base(
message, innerException)
{
Session = session;
......@@ -26,6 +26,6 @@ namespace Titanium.Web.Proxy.Exceptions
/// <remarks>
/// This object properties should not be edited.
/// </remarks>
public SessionEventArgs Session { get; }
public SessionEventArgs? Session { get; }
}
}
using System;
using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Extensions
{
internal static class HttpHeaderExtensions
{
internal static string GetString(this ByteString str)
{
return GetString(str.Span);
}
internal static string GetString(this ReadOnlySpan<byte> bytes)
{
#if NETSTANDARD2_1
return HttpHeader.Encoding.GetString(bytes);
#else
return HttpHeader.Encoding.GetString(bytes.ToArray());
#endif
}
internal static ByteString GetByteString(this string str)
{
return HttpHeader.Encoding.GetBytes(str);
}
}
}
......@@ -17,7 +17,7 @@ namespace Titanium.Web.Proxy.Extensions
internal static readonly List<SslApplicationProtocol> Http2ProtocolAsList =
new List<SslApplicationProtocol> { SslApplicationProtocol.Http2 };
internal static string GetServerName(this ClientHelloInfo clientHelloInfo)
internal static string? GetServerName(this ClientHelloInfo clientHelloInfo)
{
if (clientHelloInfo.Extensions != null &&
clientHelloInfo.Extensions.TryGetValue("server_name", out var serverNameExtension))
......@@ -28,8 +28,8 @@ namespace Titanium.Web.Proxy.Extensions
return null;
}
#if NETCOREAPP2_1
internal static List<SslApplicationProtocol> GetAlpn(this ClientHelloInfo clientHelloInfo)
#if NETSTANDARD2_1
internal static List<SslApplicationProtocol>? GetAlpn(this ClientHelloInfo clientHelloInfo)
{
if (clientHelloInfo.Extensions != null && clientHelloInfo.Extensions.TryGetValue("ALPN", out var alpnExtension))
{
......@@ -77,32 +77,36 @@ namespace Titanium.Web.Proxy.Extensions
}
#endif
}
}
#if !NETCOREAPP2_1
#if !NETSTANDARD2_1
namespace System.Net.Security
{
internal enum SslApplicationProtocol
{
Http11,
Http2
}
[SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleType", Justification = "Reviewed.")]
[SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleType", Justification =
"Reviewed.")]
internal class SslClientAuthenticationOptions
{
internal bool AllowRenegotiation { get; set; }
internal string TargetHost { get; set; }
internal string? TargetHost { get; set; }
internal X509CertificateCollection ClientCertificates { get; set; }
internal X509CertificateCollection? ClientCertificates { get; set; }
internal LocalCertificateSelectionCallback LocalCertificateSelectionCallback { get; set; }
internal LocalCertificateSelectionCallback? LocalCertificateSelectionCallback { get; set; }
internal SslProtocols EnabledSslProtocols { get; set; }
internal X509RevocationMode CertificateRevocationCheckMode { get; set; }
internal List<SslApplicationProtocol> ApplicationProtocols { get; set; }
internal List<SslApplicationProtocol>? ApplicationProtocols { get; set; }
internal RemoteCertificateValidationCallback RemoteCertificateValidationCallback { get; set; }
internal RemoteCertificateValidationCallback? RemoteCertificateValidationCallback { get; set; }
internal EncryptionPolicy EncryptionPolicy { get; set; }
}
......@@ -111,7 +115,7 @@ namespace Titanium.Web.Proxy.Extensions
{
internal bool AllowRenegotiation { get; set; }
internal X509Certificate ServerCertificate { get; set; }
internal X509Certificate? ServerCertificate { get; set; }
internal bool ClientCertificateRequired { get; set; }
......@@ -119,11 +123,11 @@ namespace Titanium.Web.Proxy.Extensions
internal X509RevocationMode CertificateRevocationCheckMode { get; set; }
internal List<SslApplicationProtocol> ApplicationProtocols { get; set; }
internal List<SslApplicationProtocol>? ApplicationProtocols { get; set; }
internal RemoteCertificateValidationCallback RemoteCertificateValidationCallback { get; set; }
internal RemoteCertificateValidationCallback? RemoteCertificateValidationCallback { get; set; }
internal EncryptionPolicy EncryptionPolicy { get; set; }
}
#endif
}
#endif
......@@ -18,11 +18,10 @@ namespace Titanium.Web.Proxy.Extensions
/// <param name="output"></param>
/// <param name="onCopy"></param>
/// <param name="bufferPool"></param>
/// <param name="bufferSize"></param>
internal static Task CopyToAsync(this Stream input, Stream output, Action<byte[], int, int> onCopy,
IBufferPool bufferPool, int bufferSize)
IBufferPool bufferPool)
{
return CopyToAsync(input, output, onCopy, bufferPool, bufferSize, CancellationToken.None);
return CopyToAsync(input, output, onCopy, bufferPool, CancellationToken.None);
}
/// <summary>
......@@ -32,12 +31,11 @@ namespace Titanium.Web.Proxy.Extensions
/// <param name="output"></param>
/// <param name="onCopy"></param>
/// <param name="bufferPool"></param>
/// <param name="bufferSize"></param>
/// <param name="cancellationToken"></param>
internal static async Task CopyToAsync(this Stream input, Stream output, Action<byte[], int, int> onCopy,
IBufferPool bufferPool, int bufferSize, CancellationToken cancellationToken)
internal static async Task CopyToAsync(this Stream input, Stream output, Action<byte[], int, int>? onCopy,
IBufferPool bufferPool, CancellationToken cancellationToken)
{
var buffer = bufferPool.GetBuffer(bufferSize);
var buffer = bufferPool.GetBuffer();
try
{
while (!cancellationToken.IsCancellationRequested)
......@@ -64,7 +62,7 @@ namespace Titanium.Web.Proxy.Extensions
}
}
private static async Task<T> withCancellation<T>(this Task<T> task, CancellationToken cancellationToken)
private static async Task<T> withCancellation<T>(this Task<T> task, CancellationToken cancellationToken) where T : struct
{
var tcs = new TaskCompletionSource<bool>();
using (cancellationToken.Register(s => ((TaskCompletionSource<bool>)s).TrySetResult(true), tcs))
......
using System;
using System.Globalization;
using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Extensions
{
......@@ -10,6 +11,11 @@ namespace Titanium.Web.Proxy.Extensions
return str.Equals(value, StringComparison.CurrentCultureIgnoreCase);
}
internal static bool EqualsIgnoreCase(this ReadOnlySpan<char> str, ReadOnlySpan<char> value)
{
return str.Equals(value, StringComparison.CurrentCultureIgnoreCase);
}
internal static bool ContainsIgnoreCase(this string str, string value)
{
return CultureInfo.CurrentCulture.CompareInfo.IndexOf(str, value, CompareOptions.IgnoreCase) >= 0;
......
using System;
using System.Net.Sockets;
using System.Reflection;
namespace Titanium.Web.Proxy.Extensions
{
......
using System;
using System.Text;
namespace Titanium.Web.Proxy.Extensions
{
......
......@@ -8,8 +8,8 @@ namespace Titanium.Web.Proxy.Helpers
{
internal sealed class HttpRequestWriter : HttpWriter
{
internal HttpRequestWriter(Stream stream, IBufferPool bufferPool, int bufferSize)
: base(stream, bufferPool, bufferSize)
internal HttpRequestWriter(Stream stream, IBufferPool bufferPool)
: base(stream, bufferPool)
{
}
......@@ -17,15 +17,13 @@ namespace Titanium.Web.Proxy.Helpers
/// Writes the request.
/// </summary>
/// <param name="request">The request object.</param>
/// <param name="flush">Should we flush after write?</param>
/// <param name="cancellationToken">Optional cancellation token for this async task.</param>
/// <returns></returns>
internal async Task WriteRequestAsync(Request request, bool flush = true,
CancellationToken cancellationToken = default)
internal async Task WriteRequestAsync(Request request, CancellationToken cancellationToken = default)
{
await WriteLineAsync(Request.CreateRequestLine(request.Method, request.RequestUriString, request.HttpVersion),
cancellationToken);
await WriteAsync(request, flush, cancellationToken);
var headerBuilder = new HeaderBuilder();
headerBuilder.WriteRequestLine(request.Method, request.Url, request.HttpVersion);
await WriteAsync(request, headerBuilder, cancellationToken);
}
}
}
......@@ -9,8 +9,8 @@ namespace Titanium.Web.Proxy.Helpers
{
internal sealed class HttpResponseWriter : HttpWriter
{
internal HttpResponseWriter(Stream stream, IBufferPool bufferPool, int bufferSize)
: base(stream, bufferPool, bufferSize)
internal HttpResponseWriter(Stream stream, IBufferPool bufferPool)
: base(stream, bufferPool)
{
}
......@@ -18,29 +18,13 @@ namespace Titanium.Web.Proxy.Helpers
/// Writes the response.
/// </summary>
/// <param name="response">The response object.</param>
/// <param name="flush">Should we flush after write?</param>
/// <param name="cancellationToken">Optional cancellation token for this async task.</param>
/// <returns>The Task.</returns>
internal async Task WriteResponseAsync(Response response, bool flush = true,
CancellationToken cancellationToken = default)
internal async Task WriteResponseAsync(Response response, CancellationToken cancellationToken = default)
{
await WriteResponseStatusAsync(response.HttpVersion, response.StatusCode, response.StatusDescription,
cancellationToken);
await WriteAsync(response, flush, cancellationToken);
}
/// <summary>
/// Write response status
/// </summary>
/// <param name="version">The Http version.</param>
/// <param name="code">The HTTP status code.</param>
/// <param name="description">The HTTP status description.</param>
/// <param name="cancellationToken">Optional cancellation token for this async task.</param>
/// <returns>The Task.</returns>
internal Task WriteResponseStatusAsync(Version version, int code, string description,
CancellationToken cancellationToken)
{
return WriteLineAsync(Response.CreateResponseLine(version, code, description), cancellationToken);
var headerBuilder = new HeaderBuilder();
headerBuilder.WriteResponseLine(response.HttpVersion, response.StatusCode, response.StatusDescription);
await WriteAsync(response, headerBuilder, cancellationToken);
}
}
}
using System;
using System.Buffers;
using System.Globalization;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Shared;
using Titanium.Web.Proxy.StreamExtended.BufferPool;
using Titanium.Web.Proxy.StreamExtended.Network;
......@@ -18,17 +20,14 @@ namespace Titanium.Web.Proxy.Helpers
private static readonly byte[] newLine = ProxyConstants.NewLineBytes;
private static readonly Encoding encoder = Encoding.ASCII;
private static Encoding encoding => HttpHeader.Encoding;
internal HttpWriter(Stream stream, IBufferPool bufferPool, int bufferSize)
internal HttpWriter(Stream stream, IBufferPool bufferPool)
{
BufferSize = bufferSize;
this.stream = stream;
this.bufferPool = bufferPool;
}
internal int BufferSize { get; }
/// <summary>
/// Writes a line async
/// </summary>
......@@ -48,12 +47,12 @@ namespace Titanium.Web.Proxy.Helpers
{
int newLineChars = addNewLine ? newLine.Length : 0;
int charCount = value.Length;
if (charCount < BufferSize - newLineChars)
if (charCount < bufferPool.BufferSize - newLineChars)
{
var buffer = bufferPool.GetBuffer(BufferSize);
var buffer = bufferPool.GetBuffer();
try
{
int idx = encoder.GetBytes(value, 0, charCount, buffer, 0);
int idx = encoding.GetBytes(value, 0, charCount, buffer, 0);
if (newLineChars > 0)
{
Buffer.BlockCopy(newLine, 0, buffer, idx, newLineChars);
......@@ -70,7 +69,7 @@ namespace Titanium.Web.Proxy.Helpers
else
{
var buffer = new byte[charCount + newLineChars + 1];
int idx = encoder.GetBytes(value, 0, charCount, buffer, 0);
int idx = encoding.GetBytes(value, 0, charCount, buffer, 0);
if (newLineChars > 0)
{
Buffer.BlockCopy(newLine, 0, buffer, idx, newLineChars);
......@@ -89,28 +88,21 @@ namespace Titanium.Web.Proxy.Helpers
/// <summary>
/// Write the headers to client
/// </summary>
/// <param name="headers"></param>
/// <param name="flush"></param>
/// <param name="headerBuilder"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
internal async Task WriteHeadersAsync(HeaderCollection headers, bool flush = true,
CancellationToken cancellationToken = default)
internal async Task WriteHeadersAsync(HeaderBuilder headerBuilder, CancellationToken cancellationToken = default)
{
var headerBuilder = new StringBuilder();
foreach (var header in headers)
{
headerBuilder.Append($"{header.ToString()}{ProxyConstants.NewLine}");
}
headerBuilder.Append(ProxyConstants.NewLine);
await WriteAsync(headerBuilder.ToString(), cancellationToken);
if (flush)
{
await stream.FlushAsync(cancellationToken);
}
var buffer = headerBuilder.GetBuffer();
await WriteAsync(buffer.Array, buffer.Offset, buffer.Count, true, cancellationToken);
}
/// <summary>
/// Writes the data to the stream.
/// </summary>
/// <param name="data">The data.</param>
/// <param name="flush">Should we flush after write?</param>
/// <param name="cancellationToken">The cancellation token.</param>
internal async Task WriteAsync(byte[] data, bool flush = false, CancellationToken cancellationToken = default)
{
await stream.WriteAsync(data, 0, data.Length, cancellationToken);
......@@ -157,8 +149,8 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="onCopy"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
internal Task CopyBodyAsync(ICustomStreamReader streamReader, bool isChunked, long contentLength,
Action<byte[], int, int> onCopy, CancellationToken cancellationToken)
internal Task CopyBodyAsync(CustomBufferedStream streamReader, bool isChunked, long contentLength,
Action<byte[], int, int>? onCopy, CancellationToken cancellationToken)
{
// For chunked request we need to read data as they arrive, until we reach a chunk end symbol
if (isChunked)
......@@ -202,12 +194,11 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="onCopy"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
private async Task copyBodyChunkedAsync(ICustomStreamReader reader, Action<byte[], int, int> onCopy,
CancellationToken cancellationToken)
private async Task copyBodyChunkedAsync(CustomBufferedStream reader, Action<byte[], int, int>? onCopy, CancellationToken cancellationToken)
{
while (true)
{
string chunkHead = await reader.ReadLineAsync(cancellationToken);
string chunkHead = (await reader.ReadLineAsync(cancellationToken))!;
int idx = chunkHead.IndexOf(";");
if (idx >= 0)
{
......@@ -243,10 +234,10 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="onCopy"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
private async Task copyBytesFromStream(ICustomStreamReader reader, long count, Action<byte[], int, int> onCopy,
private async Task copyBytesFromStream(CustomBufferedStream reader, long count, Action<byte[], int, int>? onCopy,
CancellationToken cancellationToken)
{
var buffer = bufferPool.GetBuffer(BufferSize);
var buffer = bufferPool.GetBuffer();
try
{
......@@ -283,14 +274,14 @@ namespace Titanium.Web.Proxy.Helpers
/// Writes the request/response headers and body.
/// </summary>
/// <param name="requestResponse"></param>
/// <param name="flush"></param>
/// <param name="headerBuilder"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
protected async Task WriteAsync(RequestResponseBase requestResponse, bool flush = true,
CancellationToken cancellationToken = default)
protected async Task WriteAsync(RequestResponseBase requestResponse, HeaderBuilder headerBuilder, CancellationToken cancellationToken = default)
{
var body = requestResponse.CompressBodyAndUpdateContentLength();
await WriteHeadersAsync(requestResponse.Headers, flush, cancellationToken);
headerBuilder.WriteHeaders(requestResponse.Headers);
await WriteHeadersAsync(headerBuilder, cancellationToken);
if (body != null)
{
......@@ -325,5 +316,31 @@ namespace Titanium.Web.Proxy.Helpers
{
return stream.WriteAsync(buffer, offset, count, cancellationToken);
}
#if NETSTANDARD2_1
/// <summary>
/// Asynchronously writes a sequence of bytes to the current stream, advances the current position within this stream by the number of bytes written, and monitors cancellation requests.
/// </summary>
/// <param name="buffer">The buffer to write data from.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <see cref="P:System.Threading.CancellationToken.None" />.</param>
/// <returns>A task that represents the asynchronous write operation.</returns>
public ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken)
{
return stream.WriteAsync(buffer, cancellationToken);
}
#else
/// <summary>
/// Asynchronously writes a sequence of bytes to the current stream, advances the current position within this stream by the number of bytes written, and monitors cancellation requests.
/// </summary>
/// <param name="buffer">The buffer to write data from.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <see cref="P:System.Threading.CancellationToken.None" />.</param>
/// <returns>A task that represents the asynchronous write operation.</returns>
public Task WriteAsync2(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken)
{
var buf = ArrayPool<byte>.Shared.Rent(buffer.Length);
buffer.CopyTo(buf);
return stream.WriteAsync(buf, 0, buf.Length, cancellationToken);
}
#endif
}
}
using System;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
namespace Titanium.Web.Proxy.Helpers
......
......@@ -9,8 +9,8 @@ namespace Titanium.Web.Proxy.Helpers
{
internal class ProxyInfo
{
internal ProxyInfo(bool? autoDetect, string autoConfigUrl, int? proxyEnable, string proxyServer,
string proxyOverride)
internal ProxyInfo(bool? autoDetect, string? autoConfigUrl, int? proxyEnable, string? proxyServer,
string? proxyOverride)
{
AutoDetect = autoDetect;
AutoConfigUrl = autoConfigUrl;
......@@ -54,21 +54,21 @@ namespace Titanium.Web.Proxy.Helpers
internal bool? AutoDetect { get; }
internal string AutoConfigUrl { get; }
internal string? AutoConfigUrl { get; }
internal int? ProxyEnable { get; }
internal string ProxyServer { get; }
internal string? ProxyServer { get; }
internal string ProxyOverride { get; }
internal string? ProxyOverride { get; }
internal bool BypassLoopback { get; }
internal bool BypassOnLocal { get; }
internal Dictionary<ProxyProtocolType, HttpSystemProxyValue> Proxies { get; }
internal Dictionary<ProxyProtocolType, HttpSystemProxyValue>? Proxies { get; }
internal string[] BypassList { get; }
internal string[]? BypassList { get; }
private static string bypassStringEscape(string rawString)
{
......@@ -158,7 +158,7 @@ namespace Titanium.Web.Proxy.Helpers
/// </summary>
/// <param name="proxyServerValues"></param>
/// <returns></returns>
internal static List<HttpSystemProxyValue> GetSystemProxyValues(string proxyServerValues)
internal static List<HttpSystemProxyValue> GetSystemProxyValues(string? proxyServerValues)
{
var result = new List<HttpSystemProxyValue>();
......@@ -167,11 +167,19 @@ namespace Titanium.Web.Proxy.Helpers
return result;
}
var proxyValues = proxyServerValues.Split(';');
var proxyValues = proxyServerValues!.Split(';');
if (proxyValues.Length > 0)
{
result.AddRange(proxyValues.Select(parseProxyValue).Where(parsedValue => parsedValue != null));
foreach (string str in proxyValues)
{
var proxyValue = parseProxyValue(str);
if (proxyValue != null)
{
result.Add(proxyValue);
}
}
}
else
{
......@@ -190,7 +198,7 @@ namespace Titanium.Web.Proxy.Helpers
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
private static HttpSystemProxyValue parseProxyValue(string value)
private static HttpSystemProxyValue? parseProxyValue(string value)
{
string tmp = Regex.Replace(value, @"\s+", " ").Trim();
......
using System;
using System;
using System.Reflection;
using System.Text;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
namespace Titanium.Web.Proxy.Helpers
{
......@@ -41,6 +43,62 @@ namespace Titanium.Web.Proxy.Helpers
public static bool IsUwpOnWindows => IsWindows && UwpHelper.IsRunningAsUwp();
public static bool IsMac => isRunningOnMac;
/// <summary>
/// Is socket reuse available to use?
/// </summary>
public static bool IsSocketReuseAvailable => isSocketReuseAvailable();
private static bool? _isSocketReuseAvailable;
private static bool isSocketReuseAvailable()
{
// use the cached value if we have one
if (_isSocketReuseAvailable != null)
return _isSocketReuseAvailable.Value;
try
{
if (IsWindows)
{
// since we are on windows just return true
// store the result in our static object so we don't have to be bothered going through all this more than once
_isSocketReuseAvailable = true;
return true;
}
// get the currently running framework name and version (EX: .NETFramework,Version=v4.5.1) (Ex: .NETCoreApp,Version=v2.0)
string ver = Assembly.GetEntryAssembly()?.GetCustomAttribute<TargetFrameworkAttribute>()?.FrameworkName;
if (ver == null)
return false; // play it safe if we can not figure out what the framework is
// make sure we are on .NETCoreApp
ver = ver.ToLower(); // make everything lowercase to simplify comparison
if (ver.Contains(".netcoreapp"))
{
var versionString = ver.Replace(".netcoreapp,version=v", "");
var versionArr = versionString.Split('.');
var majorVersion = Convert.ToInt32(versionArr[0]);
var result = majorVersion >= 3; // version 3 and up supports socket reuse
// store the result in our static object so we don't have to be bothered going through all this more than once
_isSocketReuseAvailable = result;
return result;
}
// store the result in our static object so we don't have to be bothered going through all this more than once
_isSocketReuseAvailable = false;
return false;
}
catch
{
// store the result in our static object so we don't have to be bothered going through all this more than once
_isSocketReuseAvailable = false;
return false;
}
}
// https://github.com/qmatteoq/DesktopBridgeHelpers/blob/master/DesktopBridge.Helpers/Helpers.cs
private class UwpHelper
......
......@@ -49,7 +49,7 @@ namespace Titanium.Web.Proxy.Helpers
internal const int InternetOptionSettingsChanged = 39;
internal const int InternetOptionRefresh = 37;
private ProxyInfo originalValues;
private ProxyInfo? originalValues;
public SystemProxyManager()
{
......@@ -90,8 +90,8 @@ namespace Titanium.Web.Proxy.Helpers
saveOriginalProxyConfiguration(reg);
prepareRegistry(reg);
string exisitingContent = reg.GetValue(regProxyServer) as string;
var existingSystemProxyValues = ProxyInfo.GetSystemProxyValues(exisitingContent);
string? existingContent = reg.GetValue(regProxyServer) as string;
var existingSystemProxyValues = ProxyInfo.GetSystemProxyValues(existingContent);
existingSystemProxyValues.RemoveAll(x => (protocolType & x.ProtocolType) != 0);
if ((protocolType & ProxyProtocolType.Http) != 0)
{
......@@ -141,9 +141,9 @@ namespace Titanium.Web.Proxy.Helpers
if (reg.GetValue(regProxyServer) != null)
{
string exisitingContent = reg.GetValue(regProxyServer) as string;
string? existingContent = reg.GetValue(regProxyServer) as string;
var existingSystemProxyValues = ProxyInfo.GetSystemProxyValues(exisitingContent);
var existingSystemProxyValues = ProxyInfo.GetSystemProxyValues(existingContent);
existingSystemProxyValues.RemoveAll(x => (protocolType & x.ProtocolType) != 0);
if (existingSystemProxyValues.Count != 0)
......@@ -284,7 +284,7 @@ namespace Titanium.Web.Proxy.Helpers
}
}
internal ProxyInfo GetProxyInfoFromRegistry()
internal ProxyInfo? GetProxyInfoFromRegistry()
{
using (var reg = openInternetSettingsKey())
{
......
using System;
using System.IO;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
......@@ -8,38 +9,32 @@ using Titanium.Web.Proxy.StreamExtended.BufferPool;
namespace Titanium.Web.Proxy.Helpers
{
internal enum IpVersion
{
Ipv4 = 1,
Ipv6 = 2
}
internal class TcpHelper
{
/// <summary>
/// Gets the process id by local port number.
/// </summary>
/// <returns>Process id.</returns>
internal static unsafe int GetProcessIdByLocalPort(IpVersion ipVersion, int localPort)
internal static unsafe int GetProcessIdByLocalPort(AddressFamily addressFamily, int localPort)
{
var tcpTable = IntPtr.Zero;
int tcpTableLength = 0;
int ipVersionValue = ipVersion == IpVersion.Ipv4 ? NativeMethods.AfInet : NativeMethods.AfInet6;
int addressFamilyValue = addressFamily == AddressFamily.InterNetwork ? NativeMethods.AfInet : NativeMethods.AfInet6;
const int allPid = (int)NativeMethods.TcpTableType.OwnerPidAll;
if (NativeMethods.GetExtendedTcpTable(tcpTable, ref tcpTableLength, false, ipVersionValue, allPid, 0) != 0)
if (NativeMethods.GetExtendedTcpTable(tcpTable, ref tcpTableLength, false, addressFamilyValue, allPid, 0) != 0)
{
try
{
tcpTable = Marshal.AllocHGlobal(tcpTableLength);
if (NativeMethods.GetExtendedTcpTable(tcpTable, ref tcpTableLength, true, ipVersionValue, allPid,
if (NativeMethods.GetExtendedTcpTable(tcpTable, ref tcpTableLength, true, addressFamilyValue, allPid,
0) == 0)
{
int rowCount = *(int*)tcpTable;
uint portInNetworkByteOrder = toNetworkByteOrder((uint)localPort);
if (ipVersion == IpVersion.Ipv4)
if (addressFamily == AddressFamily.InterNetwork)
{
var rowPtr = (NativeMethods.TcpRow*)(tcpTable + 4);
......@@ -102,23 +97,21 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="clientStream"></param>
/// <param name="serverStream"></param>
/// <param name="bufferPool"></param>
/// <param name="bufferSize"></param>
/// <param name="onDataSend"></param>
/// <param name="onDataReceive"></param>
/// <param name="cancellationTokenSource"></param>
/// <param name="exceptionFunc"></param>
/// <returns></returns>
private static async Task sendRawTap(Stream clientStream, Stream serverStream,
IBufferPool bufferPool, int bufferSize,
Action<byte[], int, int> onDataSend, Action<byte[], int, int> onDataReceive,
private static async Task sendRawTap(Stream clientStream, Stream serverStream, IBufferPool bufferPool,
Action<byte[], int, int>? onDataSend, Action<byte[], int, int>? onDataReceive,
CancellationTokenSource cancellationTokenSource,
ExceptionHandler exceptionFunc)
{
// Now async relay all server=>client & client=>server data
var sendRelay =
clientStream.CopyToAsync(serverStream, onDataSend, bufferPool, bufferSize, cancellationTokenSource.Token);
clientStream.CopyToAsync(serverStream, onDataSend, bufferPool, cancellationTokenSource.Token);
var receiveRelay =
serverStream.CopyToAsync(clientStream, onDataReceive, bufferPool, bufferSize, cancellationTokenSource.Token);
serverStream.CopyToAsync(clientStream, onDataReceive, bufferPool, cancellationTokenSource.Token);
await Task.WhenAny(sendRelay, receiveRelay);
cancellationTokenSource.Cancel();
......@@ -134,20 +127,18 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="clientStream"></param>
/// <param name="serverStream"></param>
/// <param name="bufferPool"></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 Task SendRaw(Stream clientStream, Stream serverStream,
IBufferPool bufferPool, int bufferSize,
Action<byte[], int, int> onDataSend, Action<byte[], int, int> onDataReceive,
internal static Task SendRaw(Stream clientStream, Stream serverStream, IBufferPool bufferPool,
Action<byte[], int, int>? onDataSend, Action<byte[], int, int>? onDataReceive,
CancellationTokenSource cancellationTokenSource,
ExceptionHandler exceptionFunc)
{
// todo: fix APM mode
return sendRawTap(clientStream, serverStream, bufferPool, bufferSize, onDataSend, onDataReceive,
return sendRawTap(clientStream, serverStream, bufferPool, onDataSend, onDataReceive,
cancellationTokenSource,
exceptionFunc);
}
......
......@@ -13,8 +13,8 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
ref WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxyConfig);
[DllImport("winhttp.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern WinHttpHandle WinHttpOpen(string userAgent, AccessType accessType, string proxyName,
string proxyBypass, int dwFlags);
internal static extern WinHttpHandle WinHttpOpen(string? userAgent, AccessType accessType, string? proxyName,
string? proxyBypass, int dwFlags);
[DllImport("winhttp.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern bool WinHttpSetTimeouts(WinHttpHandle session, int resolveTimeout,
......@@ -66,7 +66,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
{
public AutoProxyFlags Flags;
public AutoDetectType AutoDetectFlags;
[MarshalAs(UnmanagedType.LPWStr)] public string AutoConfigUrl;
[MarshalAs(UnmanagedType.LPWStr)] public string? AutoConfigUrl;
private readonly IntPtr lpvReserved;
private readonly int dwReserved;
public bool AutoLogonIfChallenged;
......
using System;
using System;
using System.Collections.Generic;
using System.Net;
using System.Runtime.CompilerServices;
......@@ -34,26 +34,26 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
}
}
public ICredentials Credentials { get; set; }
public ICredentials? Credentials { get; set; }
public ProxyInfo ProxyInfo { get; internal set; }
public ProxyInfo? ProxyInfo { get; internal set; }
public bool BypassLoopback { get; internal set; }
public bool BypassOnLocal { get; internal set; }
public Uri AutomaticConfigurationScript { get; internal set; }
public Uri? AutomaticConfigurationScript { get; internal set; }
public bool AutomaticallyDetectSettings { get; internal set; }
private WebProxy proxy { get; set; }
private WebProxy? proxy { get; set; }
public void Dispose()
{
dispose(true);
}
public bool GetAutoProxies(Uri destination, out IList<string> proxyList)
public bool GetAutoProxies(Uri destination, out IList<string>? proxyList)
{
proxyList = null;
if (session == null || session.IsInvalid || state == AutoWebProxyState.UnrecognizedScheme)
......@@ -61,7 +61,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
return false;
}
string proxyListString = null;
string? proxyListString = null;
var errorCode = NativeMethods.WinHttp.ErrorCodes.AudodetectionFailed;
if (AutomaticallyDetectSettings && !autoDetectFailed)
{
......@@ -88,14 +88,14 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
if (!string.IsNullOrEmpty(proxyListString))
{
proxyListString = removeWhitespaces(proxyListString);
proxyListString = removeWhitespaces(proxyListString!);
proxyList = proxyListString.Split(';');
}
return true;
}
public ExternalProxy GetProxy(Uri destination)
public ExternalProxy? GetProxy(Uri destination)
{
if (GetAutoProxies(destination, out var proxies))
{
......@@ -131,12 +131,12 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
var protocolType = ProxyInfo.ParseProtocolType(destination.Scheme);
if (protocolType.HasValue)
{
HttpSystemProxyValue value = null;
HttpSystemProxyValue? value = null;
if (ProxyInfo?.Proxies?.TryGetValue(protocolType.Value, out value) == true)
{
var systemProxy = new ExternalProxy
{
HostName = value.HostName,
HostName = value!.HostName,
Port = value.Port
};
......@@ -210,7 +210,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
session.Close();
}
private int getAutoProxies(Uri destination, Uri scriptLocation, out string proxyListString)
private int getAutoProxies(Uri destination, Uri? scriptLocation, out string? proxyListString)
{
int num = 0;
var autoProxyOptions = new NativeMethods.WinHttp.WINHTTP_AUTOPROXY_OPTIONS();
......@@ -247,7 +247,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
}
private bool winHttpGetProxyForUrl(string destination,
ref NativeMethods.WinHttp.WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions, out string proxyListString)
ref NativeMethods.WinHttp.WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions, out string? proxyListString)
{
proxyListString = null;
bool flag;
......
using Titanium.Web.Proxy.StreamExtended;
using System;
using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.StreamExtended;
namespace Titanium.Web.Proxy.Http
{
......@@ -7,13 +9,14 @@ namespace Titanium.Web.Proxy.Http
/// </summary>
public class ConnectRequest : Request
{
public ConnectRequest()
public ConnectRequest(string authority)
{
Method = "CONNECT";
Authority = authority;
}
public TunnelType TunnelType { get; internal set; }
public ClientHelloInfo ClientHelloInfo { get; set; }
public ClientHelloInfo? ClientHelloInfo { get; set; }
}
}
......@@ -9,7 +9,7 @@ namespace Titanium.Web.Proxy.Http
/// </summary>
public class ConnectResponse : Response
{
public ServerHelloInfo ServerHelloInfo { get; set; }
public ServerHelloInfo? ServerHelloInfo { get; set; }
/// <summary>
/// Creates a successful CONNECT response
......
using System;
using System.Buffers;
using System.IO;
using System.Text;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Http
{
internal class HeaderBuilder
{
private readonly MemoryStream stream = new MemoryStream();
public void WriteRequestLine(string httpMethod, string httpUrl, Version version)
{
// "{httpMethod} {httpUrl} HTTP/{version.Major}.{version.Minor}";
Write(httpMethod);
Write(" ");
Write(httpUrl);
Write(" HTTP/");
Write(version.Major.ToString());
Write(".");
Write(version.Minor.ToString());
WriteLine();
}
public void WriteResponseLine(Version version, int statusCode, string statusDescription)
{
// "HTTP/{version.Major}.{version.Minor} {statusCode} {statusDescription}";
Write("HTTP/");
Write(version.Major.ToString());
Write(".");
Write(version.Minor.ToString());
Write(" ");
Write(statusCode.ToString());
Write(" ");
Write(statusDescription);
WriteLine();
}
public void WriteHeaders(HeaderCollection headers)
{
foreach (var header in headers)
{
WriteHeader(header);
}
WriteLine();
}
public void WriteHeader(HttpHeader header)
{
Write(header.Name);
Write(": ");
Write(header.Value);
WriteLine();
}
public void WriteLine()
{
var data = ProxyConstants.NewLineBytes;
stream.Write(data, 0, data.Length);
}
public void Write(string str)
{
var encoding = HttpHeader.Encoding;
#if NETSTANDARD2_1
var buf = ArrayPool<byte>.Shared.Rent(encoding.GetMaxByteCount(str.Length));
var span = new Span<byte>(buf);
int bytes = encoding.GetBytes(str.AsSpan(), span);
stream.Write(span.Slice(0, bytes));
ArrayPool<byte>.Shared.Return(buf);
#else
var data = encoding.GetBytes(str);
stream.Write(data, 0, data.Length);
#endif
}
public ArraySegment<byte> GetBuffer()
{
stream.TryGetBuffer(out var buffer);
return buffer;
}
public string GetString(Encoding encoding)
{
stream.TryGetBuffer(out var buffer);
return encoding.GetString(buffer.Array, buffer.Offset, buffer.Count);
}
}
}
......@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Http
......@@ -71,7 +72,7 @@ namespace Titanium.Web.Proxy.Http
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public List<HttpHeader> GetHeaders(string name)
public List<HttpHeader>? GetHeaders(string name)
{
if (headers.ContainsKey(name))
{
......@@ -89,7 +90,7 @@ namespace Titanium.Web.Proxy.Http
return null;
}
public HttpHeader GetFirstHeader(string name)
public HttpHeader? GetFirstHeader(string name)
{
if (headers.TryGetValue(name, out var header))
{
......@@ -104,6 +105,21 @@ namespace Titanium.Web.Proxy.Http
return null;
}
internal HttpHeader? GetFirstHeader(KnownHeader name)
{
if (headers.TryGetValue(name.String, out var header))
{
return header;
}
if (nonUniqueHeaders.TryGetValue(name.String, out var h))
{
return h.FirstOrDefault();
}
return null;
}
/// <summary>
/// Returns all headers
/// </summary>
......@@ -128,6 +144,16 @@ namespace Titanium.Web.Proxy.Http
AddHeader(new HttpHeader(name, value));
}
internal void AddHeader(KnownHeader name, string value)
{
AddHeader(new HttpHeader(name, value));
}
internal void AddHeader(KnownHeader name, KnownHeader value)
{
AddHeader(new HttpHeader(name, value));
}
/// <summary>
/// Adds the given header object to Request
/// </summary>
......@@ -198,7 +224,7 @@ namespace Titanium.Web.Proxy.Http
/// Adds the given header objects to Request
/// </summary>
/// <param name="newHeaders"></param>
public void AddHeaders(IEnumerable<KeyValuePair<string, HttpHeader>> newHeaders)
public void AddHeaders(IEnumerable<KeyValuePair<string, HttpHeader>>? newHeaders)
{
if (newHeaders == null)
{
......@@ -238,6 +264,27 @@ namespace Titanium.Web.Proxy.Http
return result;
}
/// <summary>
/// removes all headers with given name
/// </summary>
/// <param name="headerName"></param>
/// <returns>
/// True if header was removed
/// False if no header exists with given name
/// </returns>
public bool RemoveHeader(KnownHeader headerName)
{
bool result = headers.Remove(headerName.String);
// do not convert to '||' expression to avoid lazy evaluation
if (nonUniqueHeaders.Remove(headerName.String))
{
result = true;
}
return result;
}
/// <summary>
/// Removes given header object if it exist
/// </summary>
......@@ -272,9 +319,9 @@ namespace Titanium.Web.Proxy.Http
nonUniqueHeaders.Clear();
}
internal string GetHeaderValueOrNull(string headerName)
internal string? GetHeaderValueOrNull(KnownHeader headerName)
{
if (headers.TryGetValue(headerName, out var header))
if (headers.TryGetValue(headerName.String, out var header))
{
return header.Value;
}
......@@ -282,15 +329,33 @@ namespace Titanium.Web.Proxy.Http
return null;
}
internal void SetOrAddHeaderValue(string headerName, string value)
internal void SetOrAddHeaderValue(KnownHeader headerName, string? value)
{
if (value == null)
{
RemoveHeader(headerName);
return;
}
if (headers.TryGetValue(headerName.String, out var header))
{
header.SetValue(value);
}
else
{
headers.Add(headerName.String, new HttpHeader(headerName, value));
}
}
internal void SetOrAddHeaderValue(KnownHeader headerName, KnownHeader value)
{
if (headers.TryGetValue(headerName, out var header))
if (headers.TryGetValue(headerName.String, out var header))
{
header.Value = value;
header.SetValue(value);
}
else
{
headers.Add(headerName, new HttpHeader(headerName, value));
headers.Add(headerName.String, new HttpHeader(headerName, value));
}
}
......@@ -300,7 +365,7 @@ namespace Titanium.Web.Proxy.Http
internal void FixProxyHeaders()
{
// If proxy-connection close was returned inform to close the connection
string proxyHeader = GetHeaderValueOrNull(KnownHeaders.ProxyConnection);
string? proxyHeader = GetHeaderValueOrNull(KnownHeaders.ProxyConnection);
RemoveHeader(KnownHeaders.ProxyConnection);
if (proxyHeader != null)
......
using System;
using System.Threading;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Shared;
using Titanium.Web.Proxy.StreamExtended.Network;
namespace Titanium.Web.Proxy.Http
{
internal static class HeaderParser
{
internal static async Task ReadHeaders(ICustomStreamReader reader, HeaderCollection headerCollection,
internal static async Task ReadHeaders(ILineStream reader, HeaderCollection headerCollection,
CancellationToken cancellationToken)
{
string tmpLine;
string? tmpLine;
while (!string.IsNullOrEmpty(tmpLine = await reader.ReadLineAsync(cancellationToken)))
{
var header = tmpLine.Split(ProxyConstants.ColonSplit, 2);
headerCollection.AddHeader(header[0], header[1]);
int colonIndex = tmpLine!.IndexOf(':');
if (colonIndex == -1)
{
throw new Exception("Header line should contain a colon character.");
}
string headerName = tmpLine.AsSpan(0, colonIndex).ToString();
string headerValue = tmpLine.AsSpan(colonIndex + 1).TrimStart().ToString();
headerCollection.AddHeader(headerName, headerValue);
}
}
......
......@@ -8,7 +8,6 @@ using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network.Tcp;
using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Http
{
......@@ -17,16 +16,33 @@ namespace Titanium.Web.Proxy.Http
/// </summary>
public class HttpWebClient
{
internal HttpWebClient(Request request)
private TcpServerConnection? connection;
internal HttpWebClient(ConnectRequest? connectRequest, Request request, Lazy<int> processIdFunc)
{
Request = request ?? new Request();
ConnectRequest = connectRequest;
Request = request;
Response = new Response();
ProcessId = processIdFunc;
}
/// <summary>
/// Connection to server
/// </summary>
internal TcpServerConnection Connection { get; set; }
internal TcpServerConnection Connection
{
get
{
if (connection == null)
{
throw new Exception("Connection is null");
}
return connection;
}
}
internal bool HasConnection => connection != null;
/// <summary>
/// Should we close the server connection at the end of this HTTP request/response session.
......@@ -41,17 +57,17 @@ namespace Titanium.Web.Proxy.Http
/// <summary>
/// Gets or sets the user data.
/// </summary>
public object UserData { get; set; }
public object? UserData { get; set; }
/// <summary>
/// Override UpStreamEndPoint for this request; Local NIC via request is made
/// </summary>
public IPEndPoint UpStreamEndPoint { get; set; }
public IPEndPoint? UpStreamEndPoint { get; set; }
/// <summary>
/// Headers passed with Connect.
/// </summary>
public ConnectRequest ConnectRequest { get; internal set; }
public ConnectRequest? ConnectRequest { get; }
/// <summary>
/// Web Request.
......@@ -81,7 +97,7 @@ namespace Titanium.Web.Proxy.Http
internal void SetConnection(TcpServerConnection serverConnection)
{
serverConnection.LastAccess = DateTime.Now;
Connection = serverConnection;
connection = serverConnection;
}
/// <summary>
......@@ -100,40 +116,44 @@ namespace Titanium.Web.Proxy.Http
string url;
if (useUpstreamProxy || isTransparent)
{
url = Request.RequestUriString;
url = Request.Url;
}
else
{
url = Request.RequestUri.GetOriginalPathAndQuery();
if (url == string.Empty)
{
url = "/";
}
}
var headerBuilder = new HeaderBuilder();
// prepare the request & headers
await writer.WriteLineAsync(Request.CreateRequestLine(Request.Method, url, Request.HttpVersion), cancellationToken);
headerBuilder.WriteRequestLine(Request.Method, url, Request.HttpVersion);
var headerBuilder = new StringBuilder();
// Send Authentication to Upstream proxy if needed
if (!isTransparent && upstreamProxy != null
&& Connection.IsHttps == false
&& !string.IsNullOrEmpty(upstreamProxy.UserName)
&& upstreamProxy.Password != null)
{
headerBuilder.Append($"{HttpHeader.ProxyConnectionKeepAlive}{ProxyConstants.NewLine}");
headerBuilder.Append($"{HttpHeader.GetProxyAuthorizationHeader(upstreamProxy.UserName, upstreamProxy.Password)}{ProxyConstants.NewLine}");
headerBuilder.WriteHeader(HttpHeader.ProxyConnectionKeepAlive);
headerBuilder.WriteHeader(HttpHeader.GetProxyAuthorizationHeader(upstreamProxy.UserName, upstreamProxy.Password));
}
// write request headers
foreach (var header in Request.Headers)
{
if (isTransparent || header.Name != KnownHeaders.ProxyAuthorization)
if (isTransparent || header.Name != KnownHeaders.ProxyAuthorization.String)
{
headerBuilder.Append($"{header}{ProxyConstants.NewLine}");
headerBuilder.WriteHeader(header);
}
}
headerBuilder.Append(ProxyConstants.NewLine);
await writer.WriteAsync(headerBuilder.ToString(), cancellationToken);
headerBuilder.WriteLine();
await writer.WriteHeadersAsync(headerBuilder, cancellationToken);
if (enable100ContinueBehaviour && Request.ExpectContinue)
{
......@@ -166,11 +186,8 @@ namespace Titanium.Web.Proxy.Http
string httpStatus;
try
{
httpStatus = await Connection.Stream.ReadLineAsync(cancellationToken);
if (httpStatus == null)
{
throw new ServerConnectionException("Server connection was closed.");
}
httpStatus = await Connection.Stream.ReadLineAsync(cancellationToken) ??
throw new ServerConnectionException("Server connection was closed.");
}
catch (Exception e) when (!(e is ServerConnectionException))
{
......@@ -179,7 +196,8 @@ namespace Titanium.Web.Proxy.Http
if (httpStatus == string.Empty)
{
httpStatus = await Connection.Stream.ReadLineAsync(cancellationToken);
httpStatus = await Connection.Stream.ReadLineAsync(cancellationToken) ??
throw new ServerConnectionException("Server connection was closed.");
}
Response.ParseResponseLine(httpStatus, out var version, out int statusCode, out string statusDescription);
......@@ -197,7 +215,7 @@ namespace Titanium.Web.Proxy.Http
/// </summary>
internal void FinishSession()
{
Connection = null;
connection = null;
ConnectRequest?.FinishSession();
Request?.FinishSession();
......
using System.Collections.Generic;
using System.Collections.Generic;
namespace Titanium.Web.Proxy.Http
{
......@@ -13,7 +13,8 @@ namespace Titanium.Web.Proxy.Http
}
else
{
value = default;
// hack: https://stackoverflow.com/questions/54593923/nullable-reference-types-with-generic-return-type
value = default!;
}
return result;
......@@ -24,4 +25,4 @@ namespace Titanium.Web.Proxy.Http
return (T)this[key];
}
}
}
\ No newline at end of file
}
using System;
using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Http
{
public class KnownHeader
{
internal ByteString String8;
public string String;
private KnownHeader(string str)
{
String8 = (ByteString)str;
String = str;
}
public override string ToString()
{
return String;
}
internal bool Equals(ReadOnlySpan<char> value)
{
return String.AsSpan().EqualsIgnoreCase(value);
}
public static implicit operator KnownHeader(string str) => new KnownHeader(str);
}
}
......@@ -6,46 +6,46 @@
public static class KnownHeaders
{
// Both
public const string Connection = "Connection";
public const string ConnectionClose = "close";
public const string ConnectionKeepAlive = "keep-alive";
public static KnownHeader Connection = "Connection";
public static KnownHeader ConnectionClose = "close";
public static KnownHeader ConnectionKeepAlive = "keep-alive";
public const string ContentLength = "Content-Length";
public static KnownHeader ContentLength = "Content-Length";
public const string ContentType = "Content-Type";
public const string ContentTypeCharset = "charset";
public const string ContentTypeBoundary = "boundary";
public static KnownHeader ContentType = "Content-Type";
public static KnownHeader ContentTypeCharset = "charset";
public static KnownHeader ContentTypeBoundary = "boundary";
public const string Upgrade = "Upgrade";
public const string UpgradeWebsocket = "websocket";
public static KnownHeader Upgrade = "Upgrade";
public static KnownHeader UpgradeWebsocket = "websocket";
// Request headers
public const string AcceptEncoding = "Accept-Encoding";
public static KnownHeader AcceptEncoding = "Accept-Encoding";
public const string Authorization = "Authorization";
public static KnownHeader Authorization = "Authorization";
public const string Expect = "Expect";
public const string Expect100Continue = "100-continue";
public static KnownHeader Expect = "Expect";
public static KnownHeader Expect100Continue = "100-continue";
public const string Host = "Host";
public static KnownHeader Host = "Host";
public const string ProxyAuthorization = "Proxy-Authorization";
public const string ProxyAuthorizationBasic = "basic";
public static KnownHeader ProxyAuthorization = "Proxy-Authorization";
public static KnownHeader ProxyAuthorizationBasic = "basic";
public const string ProxyConnection = "Proxy-Connection";
public const string ProxyConnectionClose = "close";
public static KnownHeader ProxyConnection = "Proxy-Connection";
public static KnownHeader ProxyConnectionClose = "close";
// Response headers
public const string ContentEncoding = "Content-Encoding";
public const string ContentEncodingDeflate = "deflate";
public const string ContentEncodingGzip = "gzip";
public const string ContentEncodingBrotli = "br";
public static KnownHeader ContentEncoding = "Content-Encoding";
public static KnownHeader ContentEncodingDeflate = "deflate";
public static KnownHeader ContentEncodingGzip = "gzip";
public static KnownHeader ContentEncodingBrotli = "br";
public const string Location = "Location";
public static KnownHeader Location = "Location";
public const string ProxyAuthenticate = "Proxy-Authenticate";
public static KnownHeader ProxyAuthenticate = "Proxy-Authenticate";
public const string TransferEncoding = "Transfer-Encoding";
public const string TransferEncodingChunked = "chunked";
public static KnownHeader TransferEncoding = "Transfer-Encoding";
public static KnownHeader TransferEncodingChunked = "chunked";
}
}
}
\ No newline at end of file
......@@ -3,8 +3,8 @@ using System.ComponentModel;
using System.Text;
using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Http
{
......@@ -14,40 +14,109 @@ namespace Titanium.Web.Proxy.Http
[TypeConverter(typeof(ExpandableObjectConverter))]
public class Request : RequestResponseBase
{
private string originalUrl;
/// <summary>
/// Request Method.
/// </summary>
public string Method { get; set; }
/// <summary>
/// Request HTTP Uri.
/// Is Https?
/// </summary>
public Uri RequestUri { get; set; }
public bool IsHttps { get; internal set; }
private ByteString originalUrlData;
private ByteString urlData;
internal ByteString OriginalUrlData
{
get => originalUrlData;
set
{
originalUrlData = value;
UrlData = value;
}
}
private protected ByteString UrlData
{
get => urlData;
set
{
urlData = value;
var scheme = getUriScheme(UrlData);
if (scheme.Length > 0)
{
IsHttps = scheme.Equals(ProxyServer.UriSchemeHttps8);
}
}
}
internal string? Authority { get; set; }
/// <summary>
/// Is Https?
/// The original request Url.
/// </summary>
public bool IsHttps => RequestUri.Scheme == ProxyServer.UriSchemeHttps;
public string OriginalUrl => originalUrlData.GetString();
/// <summary>
/// The original request Url.
/// Request HTTP Uri.
/// </summary>
public string OriginalUrl
public Uri RequestUri
{
get => originalUrl;
internal set
get
{
originalUrl = value;
RequestUriString = value;
string url = UrlData.GetString();
if (getUriScheme(UrlData).Length == 0)
{
string? hostAndPath = Host ?? Authority;
if (url.StartsWith("/"))
{
hostAndPath += url;
}
else
{
//throw new Exception($"Invalid URL: '{url}'");
}
url = string.Concat(IsHttps ? "https://" : "http://", hostAndPath);
}
try
{
return new Uri(url);
}
catch (Exception ex)
{
throw new Exception($"Invalid URI: '{url}'", ex);
}
}
}
/// <summary>
/// The request uri as it is in the HTTP header
/// The request url as it is in the HTTP header
/// </summary>
public string RequestUriString { get; set; }
public string Url
{
get => UrlData.GetString();
set
{
UrlData = value.GetByteString();
if (Host != null)
{
var uri = new Uri(value);
Host = uri.Authority;
}
}
}
[Obsolete("This property is obsolete. Use Url property instead")]
public string RequestUriString
{
get => Url;
set => Url = value;
}
/// <summary>
/// Has request body?
......@@ -85,7 +154,7 @@ namespace Titanium.Web.Proxy.Http
/// Note: Changing this does NOT change host in RequestUri.
/// Users can set new RequestUri separately.
/// </summary>
public string Host
public string? Host
{
get => Headers.GetHeaderValueOrNull(KnownHeaders.Host);
set => Headers.SetOrAddHeaderValue(KnownHeaders.Host, value);
......@@ -98,8 +167,8 @@ namespace Titanium.Web.Proxy.Http
{
get
{
string headerValue = Headers.GetHeaderValueOrNull(KnownHeaders.Expect);
return headerValue != null && headerValue.Equals(KnownHeaders.Expect100Continue);
string? headerValue = Headers.GetHeaderValueOrNull(KnownHeaders.Expect);
return KnownHeaders.Expect100Continue.Equals(headerValue);
}
}
......@@ -108,11 +177,6 @@ namespace Titanium.Web.Proxy.Http
/// </summary>
public bool IsMultipartFormData => ContentType?.StartsWith("multipart/form-data") == true;
/// <summary>
/// Request Url.
/// </summary>
public string Url => RequestUri.OriginalString;
/// <summary>
/// Cancels the client HTTP request without sending to server.
/// This should be set when API user responds with custom response.
......@@ -126,14 +190,14 @@ namespace Titanium.Web.Proxy.Http
{
get
{
string headerValue = Headers.GetHeaderValueOrNull(KnownHeaders.Upgrade);
string? headerValue = Headers.GetHeaderValueOrNull(KnownHeaders.Upgrade);
if (headerValue == null)
{
return false;
}
return headerValue.EqualsIgnoreCase(KnownHeaders.UpgradeWebsocket);
return headerValue.EqualsIgnoreCase(KnownHeaders.UpgradeWebsocket.String);
}
}
......@@ -154,15 +218,10 @@ namespace Titanium.Web.Proxy.Http
{
get
{
var sb = new StringBuilder();
sb.Append($"{CreateRequestLine(Method, RequestUriString, HttpVersion)}{ProxyConstants.NewLine}");
foreach (var header in Headers)
{
sb.Append($"{header.ToString()}{ProxyConstants.NewLine}");
}
sb.Append(ProxyConstants.NewLine);
return sb.ToString();
var headerBuilder = new HeaderBuilder();
headerBuilder.WriteRequestLine(Method, Url, HttpVersion);
headerBuilder.WriteHeaders(Headers);
return headerBuilder.GetString(HttpHeader.Encoding);
}
}
......@@ -197,38 +256,41 @@ namespace Titanium.Web.Proxy.Http
}
}
internal static string CreateRequestLine(string httpMethod, string httpUrl, Version version)
{
return $"{httpMethod} {httpUrl} HTTP/{version.Major}.{version.Minor}";
}
internal static void ParseRequestLine(string httpCmd, out string httpMethod, out string httpUrl,
out Version version)
{
// break up the line into three components (method, remote URL & Http Version)
var httpCmdSplit = httpCmd.Split(ProxyConstants.SpaceSplit, 3);
if (httpCmdSplit.Length < 2)
int firstSpace = httpCmd.IndexOf(' ');
if (firstSpace == -1)
{
// does not contain at least 2 parts
throw new Exception("Invalid HTTP request line: " + httpCmd);
}
int lastSpace = httpCmd.LastIndexOf(' ');
// break up the line into three components (method, remote URL & Http Version)
// Find the request Verb
httpMethod = httpCmdSplit[0];
httpMethod = httpCmd.Substring(0, firstSpace);
if (!isAllUpper(httpMethod))
{
httpMethod = httpMethod.ToUpper();
}
httpUrl = httpCmdSplit[1];
// parse the HTTP version
version = HttpHeader.Version11;
if (httpCmdSplit.Length == 3)
if (firstSpace == lastSpace)
{
string httpVersion = httpCmdSplit[2].Trim();
httpUrl = httpCmd.AsSpan(firstSpace + 1).ToString();
}
else
{
httpUrl = httpCmd.AsSpan(firstSpace + 1, lastSpace - firstSpace - 1).ToString();
// parse the HTTP version
var httpVersion = httpCmd.AsSpan(lastSpace + 1);
if (httpVersion.EqualsIgnoreCase("HTTP/1.0"))
if (httpVersion.EqualsIgnoreCase("HTTP/1.0".AsSpan(0)))
{
version = HttpHeader.Version10;
}
......@@ -249,5 +311,46 @@ namespace Titanium.Web.Proxy.Http
return true;
}
private ByteString getUriScheme(ByteString str)
{
if (str.Length < 3)
{
return ByteString.Empty;
}
// regex: "^[a-z]*://"
int i;
for (i = 0; i < str.Length - 3; i++)
{
byte ch = str[i];
if (ch == ':')
{
break;
}
if (ch < 'A' || ch > 'z' || (ch > 'Z' && ch < 'a')) // ASCII letter
{
return ByteString.Empty;
}
}
if (str[i++] != ':')
{
return ByteString.Empty;
}
if (str[i++] != '/')
{
return ByteString.Empty;
}
if (str[i] != '/')
{
return ByteString.Empty;
}
return new ByteString(str.Data.Slice(0, i - 2));
}
}
}
using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Text;
using System.Threading.Tasks;
......@@ -19,12 +18,12 @@ namespace Titanium.Web.Proxy.Http
/// <summary>
/// Cached body content as byte array.
/// </summary>
protected byte[] BodyInternal { get; private set; }
protected byte[]? BodyInternal { get; private set; }
/// <summary>
/// Cached body as string.
/// </summary>
private string bodyString;
private string? bodyString;
/// <summary>
/// Store whether the original request/response has body or not, since the user may change the parameters.
......@@ -48,17 +47,17 @@ namespace Titanium.Web.Proxy.Http
/// Store whether the original request/response content-encoding, since the user may change the parameters.
/// We need this detail to syphon out attached tcp connection for reuse.
/// </summary>
internal string OriginalContentEncoding { get; set; }
internal string? OriginalContentEncoding { get; set; }
internal TaskCompletionSource<bool> ReadHttp2BeforeHandlerTaskCompletionSource;
internal TaskCompletionSource<bool>? ReadHttp2BeforeHandlerTaskCompletionSource;
internal TaskCompletionSource<bool> ReadHttp2BodyTaskCompletionSource;
internal TaskCompletionSource<bool>? ReadHttp2BodyTaskCompletionSource;
internal MemoryStream Http2BodyData;
internal MemoryStream? Http2BodyData;
internal bool Http2IgnoreBodyFrames;
internal Task Http2BeforeHandlerTask;
internal Task? Http2BeforeHandlerTask;
/// <summary>
/// Priority used only in HTTP/2
......@@ -87,7 +86,7 @@ namespace Titanium.Web.Proxy.Http
{
get
{
string headerValue = Headers.GetHeaderValueOrNull(KnownHeaders.ContentLength);
string? headerValue = Headers.GetHeaderValueOrNull(KnownHeaders.ContentLength);
if (headerValue == null)
{
......@@ -119,7 +118,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary>
/// Content encoding for this request/response.
/// </summary>
public string ContentEncoding => Headers.GetHeaderValueOrNull(KnownHeaders.ContentEncoding)?.Trim();
public string? ContentEncoding => Headers.GetHeaderValueOrNull(KnownHeaders.ContentEncoding)?.Trim();
/// <summary>
/// Encoding for this request/response.
......@@ -129,7 +128,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary>
/// Content-type of the request/response.
/// </summary>
public string ContentType
public string? ContentType
{
get => Headers.GetHeaderValueOrNull(KnownHeaders.ContentType);
set => Headers.SetOrAddHeaderValue(KnownHeaders.ContentType, value);
......@@ -142,8 +141,8 @@ namespace Titanium.Web.Proxy.Http
{
get
{
string headerValue = Headers.GetHeaderValueOrNull(KnownHeaders.TransferEncoding);
return headerValue != null && headerValue.ContainsIgnoreCase(KnownHeaders.TransferEncodingChunked);
string? headerValue = Headers.GetHeaderValueOrNull(KnownHeaders.TransferEncoding);
return headerValue != null && headerValue.ContainsIgnoreCase(KnownHeaders.TransferEncodingChunked.String);
}
set
......@@ -174,7 +173,7 @@ namespace Titanium.Web.Proxy.Http
get
{
EnsureBodyAvailable();
return BodyInternal;
return BodyInternal!;
}
internal set
......@@ -220,7 +219,7 @@ namespace Titanium.Web.Proxy.Http
/// <param name="encodingType"></param>
/// <param name="body"></param>
/// <returns></returns>
internal byte[] GetCompressedBody(string encodingType, byte[] body)
internal byte[] GetCompressedBody(HttpCompression encodingType, byte[] body)
{
using (var ms = new MemoryStream())
{
......@@ -233,7 +232,7 @@ namespace Titanium.Web.Proxy.Http
}
}
internal byte[] CompressBodyAndUpdateContentLength()
internal byte[]? CompressBodyAndUpdateContentLength()
{
if (!IsBodyRead && BodyInternal == null)
{
......@@ -241,14 +240,14 @@ namespace Titanium.Web.Proxy.Http
}
bool isChunked = IsChunked;
string contentEncoding = ContentEncoding;
string? contentEncoding = ContentEncoding;
if (HasBody)
{
var body = Body;
if (contentEncoding != null && body != null)
{
body = GetCompressedBody(contentEncoding, body);
body = GetCompressedBody(CompressionUtil.CompressionNameToEnum(contentEncoding), body);
if (isChunked == false)
{
......
......@@ -2,8 +2,8 @@
using System.ComponentModel;
using System.Text;
using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Http
{
......@@ -36,7 +36,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary>
/// Response Status description.
/// </summary>
public string StatusDescription { get; set; }
public string StatusDescription { get; set; } = string.Empty;
/// <summary>
/// Has response body?
......@@ -78,11 +78,11 @@ namespace Titanium.Web.Proxy.Http
{
get
{
string headerValue = Headers.GetHeaderValueOrNull(KnownHeaders.Connection);
string? headerValue = Headers.GetHeaderValueOrNull(KnownHeaders.Connection);
if (headerValue != null)
{
if (headerValue.EqualsIgnoreCase(KnownHeaders.ConnectionClose))
if (headerValue.EqualsIgnoreCase(KnownHeaders.ConnectionClose.String))
{
return false;
}
......@@ -99,15 +99,10 @@ namespace Titanium.Web.Proxy.Http
{
get
{
var sb = new StringBuilder();
sb.Append($"{CreateResponseLine(HttpVersion, StatusCode, StatusDescription)}{ProxyConstants.NewLine}");
foreach (var header in Headers)
{
sb.Append($"{header.ToString()}{ProxyConstants.NewLine}");
}
sb.Append(ProxyConstants.NewLine);
return sb.ToString();
var headerBuilder = new HeaderBuilder();
headerBuilder.WriteResponseLine(HttpVersion, StatusCode, StatusDescription);
headerBuilder.WriteHeaders(Headers);
return headerBuilder.GetString(HttpHeader.Encoding);
}
}
......@@ -126,30 +121,41 @@ namespace Titanium.Web.Proxy.Http
}
}
internal static string CreateResponseLine(Version version, int statusCode, string statusDescription)
{
return $"HTTP/{version.Major}.{version.Minor} {statusCode} {statusDescription}";
}
internal static void ParseResponseLine(string httpStatus, out Version version, out int statusCode,
out string statusDescription)
internal static void ParseResponseLine(string httpStatus, out Version version, out int statusCode, out string statusDescription)
{
var httpResult = httpStatus.Split(ProxyConstants.SpaceSplit, 3);
if (httpResult.Length <= 1)
int firstSpace = httpStatus.IndexOf(' ');
if (firstSpace == -1)
{
throw new Exception("Invalid HTTP status line: " + httpStatus);
}
string httpVersion = httpResult[0];
var httpVersion = httpStatus.AsSpan(0, firstSpace);
version = HttpHeader.Version11;
if (httpVersion.EqualsIgnoreCase("HTTP/1.0"))
if (httpVersion.EqualsIgnoreCase("HTTP/1.0".AsSpan()))
{
version = HttpHeader.Version10;
}
statusCode = int.Parse(httpResult[1]);
statusDescription = httpResult.Length > 2 ? httpResult[2] : string.Empty;
int secondSpace = httpStatus.IndexOf(' ', firstSpace + 1);
if (secondSpace != -1)
{
#if NETSTANDARD2_1
statusCode = int.Parse(httpStatus.AsSpan(firstSpace + 1, secondSpace - firstSpace - 1));
#else
statusCode = int.Parse(httpStatus.AsSpan(firstSpace + 1, secondSpace - firstSpace - 1).ToString());
#endif
statusDescription = httpStatus.AsSpan(secondSpace + 1).ToString();
}
else
{
#if NETSTANDARD2_1
statusCode = int.Parse(httpStatus.AsSpan(firstSpace + 1));
#else
statusCode = int.Parse(httpStatus.AsSpan(firstSpace + 1).ToString());
#endif
statusDescription = string.Empty;
}
}
}
}
using System.Net;
using System.Web;
namespace Titanium.Web.Proxy.Http.Responses
{
......@@ -15,7 +14,7 @@ namespace Titanium.Web.Proxy.Http.Responses
public GenericResponse(HttpStatusCode status)
{
StatusCode = (int)status;
StatusDescription = Get(StatusCode);
StatusDescription = Get(StatusCode) ?? string.Empty;
}
/// <summary>
......@@ -29,7 +28,7 @@ namespace Titanium.Web.Proxy.Http.Responses
StatusDescription = statusDescription;
}
internal static string Get(int code)
internal static string? Get(int code)
{
switch (code)
{
......@@ -98,6 +97,7 @@ namespace Titanium.Web.Proxy.Http.Responses
case 510: return "Not Extended";
case 511: return "Network Authentication Required";
}
return null;
}
}
......
......@@ -22,7 +22,7 @@ using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Http2.Hpack
{
public class Decoder
internal class Decoder
{
private readonly DynamicTable dynamicTable;
......@@ -39,7 +39,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
private int skipLength;
private int nameLength;
private int valueLength;
private string name;
private ByteString name;
private enum State
{
......@@ -248,7 +248,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
if (indexType == HpackUtil.IndexType.None)
{
// Name is unused so skip bytes
name = string.Empty;
name = ByteString.Empty;
skipLength = nameLength;
state = State.SkipLiteralHeaderName;
break;
......@@ -258,7 +258,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
if (nameLength + HttpHeader.HttpHeaderOverhead > dynamicTable.Capacity)
{
dynamicTable.Clear();
name = string.Empty;
name = Array.Empty<byte>();
skipLength = nameLength;
state = State.SkipLiteralHeaderName;
break;
......@@ -292,7 +292,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
if (indexType == HpackUtil.IndexType.None)
{
// Name is unused so skip bytes
name = string.Empty;
name = ByteString.Empty;
skipLength = nameLength;
state = State.SkipLiteralHeaderName;
break;
......@@ -302,7 +302,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
if (nameLength + HttpHeader.HttpHeaderOverhead > dynamicTable.Capacity)
{
dynamicTable.Clear();
name = string.Empty;
name = ByteString.Empty;
skipLength = nameLength;
state = State.SkipLiteralHeaderName;
break;
......@@ -375,7 +375,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
if (valueLength == 0)
{
InsertHeader(headerListener, name, string.Empty, indexType);
InsertHeader(headerListener, name, Array.Empty<byte>(), indexType);
state = State.ReadHeaderRepresentation;
}
else
......@@ -519,29 +519,28 @@ namespace Titanium.Web.Proxy.Http2.Hpack
var headerField = StaticTable.Get(index);
return headerField;
}
else if (index - StaticTable.Length <= dynamicTable.Length())
if (index - StaticTable.Length <= dynamicTable.Length())
{
var headerField = dynamicTable.GetEntry(index - StaticTable.Length);
return headerField;
}
else
{
throw new IOException("illegal index value (" + index + ")");
}
throw new IOException("illegal index value (" + index + ")");
}
private void ReadName(int index)
{
name = GetHeaderField(index).Name;
name = GetHeaderField(index).NameData;
}
private void IndexHeader(int index, IHeaderListener headerListener)
{
var headerField = GetHeaderField(index);
AddHeader(headerListener, headerField.Name, headerField.Value, false);
AddHeader(headerListener, headerField.NameData, headerField.ValueData, false);
}
private void InsertHeader(IHeaderListener headerListener, string name, string value, HpackUtil.IndexType indexType)
private void InsertHeader(IHeaderListener headerListener, ByteString name, ByteString value, HpackUtil.IndexType indexType)
{
AddHeader(headerListener, name, value, indexType == HpackUtil.IndexType.Never);
......@@ -560,7 +559,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
}
}
private void AddHeader(IHeaderListener headerListener, string name, string value, bool sensitive)
private void AddHeader(IHeaderListener headerListener, ByteString name, ByteString value, bool sensitive)
{
if (name.Length == 0)
{
......@@ -593,7 +592,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
return true;
}
private string ReadStringLiteral(BinaryReader input, int length)
private ByteString ReadStringLiteral(BinaryReader input, int length)
{
var buf = new byte[length];
int lengthToRead = length;
......@@ -608,7 +607,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
throw new IOException("decompression failure");
}
return huffmanEncoded ? HuffmanDecoder.Instance.Decode(buf) : Encoding.UTF8.GetString(buf);
return new ByteString(huffmanEncoded ? HuffmanDecoder.Instance.Decode(buf) : buf);
}
// Unsigned Little Endian Base 128 Variable-Length Integer Encoding
......
......@@ -23,7 +23,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
public class DynamicTable
{
// a circular queue of header fields
HttpHeader[] headerFields;
HttpHeader[] headerFields = Array.Empty<HttpHeader>();
int head;
int tail;
......@@ -89,10 +89,10 @@ namespace Titanium.Web.Proxy.Http2.Hpack
int i = head - index;
if (i < 0)
{
return headerFields[i + headerFields.Length];
return headerFields[i + headerFields.Length]!;
}
return headerFields[i];
return headerFields[i]!;
}
/// <summary>
......@@ -128,7 +128,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
/// <summary>
/// Remove and return the oldest header field from the dynamic table.
/// </summary>
public HttpHeader Remove()
public HttpHeader? Remove()
{
var removed = headerFields[tail];
if (removed == null)
......@@ -218,8 +218,8 @@ namespace Titanium.Web.Proxy.Http2.Hpack
int cursor = tail;
for (int i = 0; i < len; i++)
{
var entry = headerFields[cursor++];
tmp[i] = entry;
var entry = headerFields![cursor++];
tmp[i] = entry!;
if (cursor == headerFields.Length)
{
cursor = 0;
......
/*

using Titanium.Web.Proxy.Extensions;
#if NETSTANDARD2_1
/*
* Copyright 2014 Twitter, Inc
* This file is a derivative work modified by Ringo Leese
*
......@@ -14,7 +18,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.IO;
using System.Text;
......@@ -22,13 +25,13 @@ using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Http2.Hpack
{
public class Encoder
internal class Encoder
{
private const int bucketSize = 17;
// a linked hash map of header fields
private readonly HeaderEntry[] headerFields = new HeaderEntry[bucketSize];
private readonly HeaderEntry head = new HeaderEntry(-1, string.Empty, string.Empty, int.MaxValue, null);
private readonly HeaderEntry head = new HeaderEntry(-1, ByteString.Empty, ByteString.Empty, int.MaxValue, null);
private int size;
/// <summary>
......@@ -63,7 +66,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
/// <param name="sensitive">If set to <c>true</c> sensitive.</param>
/// <param name="indexType">Index type.</param>
/// <param name="useStaticName">Use static name.</param>
public void EncodeHeader(BinaryWriter output, string name, string value, bool sensitive = false, HpackUtil.IndexType indexType = HpackUtil.IndexType.Incremental, bool useStaticName = true)
public void EncodeHeader(BinaryWriter output, ByteString name, ByteString value, bool sensitive = false, HpackUtil.IndexType indexType = HpackUtil.IndexType.Incremental, bool useStaticName = true)
{
// If the header value is sensitive then it must never be indexed
if (sensitive)
......@@ -190,12 +193,11 @@ namespace Titanium.Web.Proxy.Http2.Hpack
/// Encode string literal according to Section 5.2.
/// </summary>
/// <param name="output">Output.</param>
/// <param name="stringLiteral">String literal.</param>
private void encodeStringLiteral(BinaryWriter output, string stringLiteral)
/// <param name="stringData">String data.</param>
private void encodeStringLiteral(BinaryWriter output, ByteString stringData)
{
var stringData = Encoding.UTF8.GetBytes(stringLiteral);
int huffmanLength = HuffmanEncoder.Instance.GetEncodedLength(stringData);
if (huffmanLength < stringLiteral.Length)
if (huffmanLength < stringData.Length)
{
encodeInteger(output, 0x80, 7, huffmanLength);
HuffmanEncoder.Instance.Encode(output, stringData);
......@@ -203,7 +205,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
else
{
encodeInteger(output, 0x00, 7, stringData.Length);
output.Write(stringData, 0, stringData.Length);
output.Write(stringData.Span);
}
}
......@@ -215,7 +217,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
/// <param name="value">Value.</param>
/// <param name="indexType">Index type.</param>
/// <param name="nameIndex">Name index.</param>
private void encodeLiteral(BinaryWriter output, string name, string value, HpackUtil.IndexType indexType,
private void encodeLiteral(BinaryWriter output, ByteString name, ByteString value, HpackUtil.IndexType indexType,
int nameIndex)
{
int mask;
......@@ -250,7 +252,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
encodeStringLiteral(output, value);
}
private int getNameIndex(string name)
private int getNameIndex(ByteString name)
{
int index = StaticTable.GetIndex(name);
if (index == -1)
......@@ -299,9 +301,9 @@ namespace Titanium.Web.Proxy.Http2.Hpack
/// <returns>The entry.</returns>
/// <param name="name">Name.</param>
/// <param name="value">Value.</param>
private HeaderEntry getEntry(string name, string value)
private HeaderEntry? getEntry(ByteString name, ByteString value)
{
if (length() == 0 || name == null || value == null)
if (length() == 0 || name.Length == 0 || value.Length == 0)
{
return null;
}
......@@ -310,7 +312,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
int i = index(h);
for (var e = headerFields[i]; e != null; e = e.Next)
{
if (e.Hash == h && name.Equals(e.Name, StringComparison.OrdinalIgnoreCase) && Equals(value, e.Value))
if (e.Hash == h && name.Equals(e.NameData) && Equals(value, e.ValueData))
{
return e;
}
......@@ -325,9 +327,9 @@ namespace Titanium.Web.Proxy.Http2.Hpack
/// </summary>
/// <returns>The index.</returns>
/// <param name="name">Name.</param>
private int getIndex(string name)
private int getIndex(ByteString name)
{
if (length() == 0 || name == null)
if (length() == 0 || name.Length == 0)
{
return -1;
}
......@@ -335,9 +337,9 @@ namespace Titanium.Web.Proxy.Http2.Hpack
int h = hash(name);
int i = Encoder.index(h);
int index = -1;
for (var e = headerFields[i]; e != null; e = e.Next)
for (HeaderEntry? e = headerFields[i]; e != null; e = e.Next)
{
if (e.Hash == h && name.Equals(e.Name, StringComparison.OrdinalIgnoreCase))
if (e.Hash == h && name.Equals(e.NameData))
{
index = e.Index;
break;
......@@ -371,7 +373,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
/// </summary>
/// <param name="name">Name.</param>
/// <param name="value">Value.</param>
private void add(string name, string value)
private void add(ByteString name, ByteString value)
{
int headerSize = HttpHeader.SizeOf(name, value);
......@@ -400,7 +402,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
/// <summary>
/// Remove and return the oldest header field from the dynamic table.
/// </summary>
private HttpHeader remove()
private HttpHeader? remove()
{
if (size == 0)
{
......@@ -423,7 +425,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
}
else
{
prev.Next = next;
prev!.Next = next;
}
eldest.Remove();
......@@ -457,12 +459,12 @@ namespace Titanium.Web.Proxy.Http2.Hpack
/// </summary>
/// <returns><c>true</c> if hash name; otherwise, <c>false</c>.</returns>
/// <param name="name">Name.</param>
private static int hash(string name)
private static int hash(ByteString name)
{
int h = 0;
for (int i = 0; i < name.Length; i++)
{
h = 31 * h + name[i];
h = 31 * h + name.Span[i];
}
if (h > 0)
......@@ -500,7 +502,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
public HeaderEntry After { get; set; }
// These fields comprise the chained list for header fields with the same hash.
public HeaderEntry Next { get; set; }
public HeaderEntry? Next { get; set; }
public int Hash { get; }
......@@ -514,11 +516,13 @@ namespace Titanium.Web.Proxy.Http2.Hpack
/// <param name="value">Value.</param>
/// <param name="index">Index.</param>
/// <param name="next">Next.</param>
public HeaderEntry(int hash, string name, string value, int index, HeaderEntry next) : base(name, value, true)
public HeaderEntry(int hash, ByteString name, ByteString value, int index, HeaderEntry? next) : base(name, value, true)
{
Index = index;
Hash = hash;
Next = next;
Before = this;
After = this;
}
/// <summary>
......@@ -544,3 +548,4 @@ namespace Titanium.Web.Proxy.Http2.Hpack
}
}
}
#endif
......@@ -54,7 +54,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
/// <param name="buf">the string literal to be decoded</param>
/// <returns>the output stream for the compressed data</returns>
/// <exception cref="IOException">throws IOException if an I/O error occurs. In particular, an <code>IOException</code> may be thrown if the output stream has been closed.</exception>
public string Decode(byte[] buf)
public ReadOnlyMemory<byte> Decode(byte[] buf)
{
var resultBuf = new byte[buf.Length * 2];
int resultSize = 0;
......@@ -69,7 +69,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
while (bits >= 8)
{
int c = (current >> (bits - 8)) & 0xFF;
node = node.Children[c];
node = node.Children![c];
bits -= node.Bits;
if (node.IsTerminal)
{
......@@ -87,7 +87,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
while (bits > 0)
{
int c = (current << (8 - bits)) & 0xFF;
node = node.Children[c];
node = node.Children![c];
if (node.IsTerminal && node.Bits <= bits)
{
bits -= node.Bits;
......@@ -109,7 +109,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
throw new IOException("Invalid Padding");
}
return Encoding.UTF8.GetString(resultBuf, 0, resultSize);
return resultBuf.AsMemory(0, resultSize);
}
private class Node
......@@ -121,7 +121,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
public int Bits { get; }
// internal nodes have children
public Node[] Children { get; }
public Node[]? Children { get; }
/// <summary>
/// Initializes a new instance of the <see cref="HuffmanDecoder"/> class.
......@@ -173,7 +173,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
length -= 8;
int i = (code >> length) & 0xFF;
if (current.Children[i] == null)
if (current.Children![i] == null)
{
current.Children[i] = new Node();
}
......@@ -187,7 +187,7 @@ namespace Titanium.Web.Proxy.Http2.Hpack
int end = 1 << shift;
for (int i = start; i < start + end; i++)
{
current.Children[i] = terminal;
current.Children![i] = terminal;
}
}
}
......
......@@ -17,10 +17,11 @@
using System;
using System.IO;
using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Http2.Hpack
{
public class HuffmanEncoder
internal class HuffmanEncoder
{
/// <summary>
/// Huffman Encoder
......@@ -42,39 +43,15 @@ namespace Titanium.Web.Proxy.Http2.Hpack
/// </summary>
/// <param name="output">the output stream for the compressed data</param>
/// <param name="data">the string literal to be Huffman encoded</param>
/// <exception cref="IOException">if an I/O error occurs.</exception>
/// <see cref="Encode(BinaryWriter,byte[],int,int)"/>
public void Encode(BinaryWriter output, byte[] data)
{
Encode(output, data, 0, data.Length);
}
/// <summary>
/// Compresses the input string literal using the Huffman coding.
/// </summary>
/// <param name="output">the output stream for the compressed data</param>
/// <param name="data">the string literal to be Huffman encoded</param>
/// <param name="off">the start offset in the data</param>
/// <param name="len">the number of bytes to encode</param>
/// <exception cref="IOException">if an I/O error occurs. In particular, an <code>IOException</code> may be thrown if the output stream has been closed.</exception>
public void Encode(BinaryWriter output, byte[] data, int off, int len)
public void Encode(BinaryWriter output, ByteString data)
{
if (output == null)
{
throw new ArgumentNullException(nameof(output));
}
if (data == null)
{
throw new ArgumentNullException(nameof(data));
}
if (off < 0 || len < 0 || (off + len) < 0 || off > data.Length || (off + len) > data.Length)
{
throw new IndexOutOfRangeException();
}
if (len == 0)
if (data.Length == 0)
{
return;
}
......@@ -82,9 +59,9 @@ namespace Titanium.Web.Proxy.Http2.Hpack
long current = 0L;
int n = 0;
for (int i = 0; i < len; i++)
for (int i = 0; i < data.Length; i++)
{
int b = data[off + i] & 0xFF;
int b = data.Span[i] & 0xFF;
uint code = (uint)codes[b];
int nbits = lengths[b];
......@@ -112,15 +89,10 @@ namespace Titanium.Web.Proxy.Http2.Hpack
/// </summary>
/// <returns>the number of bytes required to Huffman encode <code>data</code></returns>
/// <param name="data">the string literal to be Huffman encoded</param>
public int GetEncodedLength(byte[] data)
public int GetEncodedLength(ByteString data)
{
if (data == null)
{
throw new ArgumentNullException(nameof(data));
}
long len = 0L;
foreach (byte b in data)
foreach (byte b in data.Span)
{
len += lengths[b];
}
......
......@@ -14,9 +14,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Http2.Hpack
{
public interface IHeaderListener
internal interface IHeaderListener
{
/// <summary>
/// EmitHeader is called by the decoder during header field emission.
......@@ -25,6 +29,6 @@ namespace Titanium.Web.Proxy.Http2.Hpack
/// <param name="name">Name.</param>
/// <param name="value">Value.</param>
/// <param name="sensitive">If set to <c>true</c> sensitive.</param>
void AddHeader(string name, string value, bool sensitive);
void AddHeader(ByteString name, ByteString value, bool sensitive);
}
}
......@@ -10,12 +10,9 @@
public int StreamId;
public byte[] Buffer;
public byte[] CopyToBuffer()
public void CopyToBuffer(byte[] buf)
{
int length = Length;
var buf = /*new byte[9];*/Buffer;
buf[0] = (byte)((length >> 16) & 0xff);
buf[1] = (byte)((length >> 8) & 0xff);
buf[2] = (byte)(length & 0xff);
......@@ -26,7 +23,6 @@
//buf[6] = (byte)((streamId >> 16) & 0xff);
//buf[7] = (byte)((streamId >> 8) & 0xff);
//buf[8] = (byte)(streamId & 0xff);
return buf;
}
}
}
This diff is collapsed.
using System;
using System.Text;
namespace Titanium.Web.Proxy.Models
{
internal struct ByteString : IEquatable<ByteString>
{
public static ByteString Empty = new ByteString(ReadOnlyMemory<byte>.Empty);
public ReadOnlyMemory<byte> Data { get; }
public ReadOnlySpan<byte> Span => Data.Span;
public int Length => Data.Length;
public ByteString(ReadOnlyMemory<byte> data)
{
Data = data;
}
public override bool Equals(object? obj)
{
return obj is ByteString other && Equals(other);
}
public bool Equals(ByteString other)
{
return Data.Span.SequenceEqual(other.Data.Span);
}
public override int GetHashCode()
{
return Data.GetHashCode();
}
public static explicit operator ByteString(string str) => new ByteString(Encoding.ASCII.GetBytes(str));
public static implicit operator ByteString(byte[] data) => new ByteString(data);
public static implicit operator ByteString(ReadOnlyMemory<byte> data) => new ByteString(data);
public byte this[int i]
{
get => Span[i];
}
}
}
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Diagnostics;
using System.Net;
using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Extensions;
......@@ -10,6 +10,7 @@ namespace Titanium.Web.Proxy.Models
/// A proxy endpoint that the client is aware of.
/// So client application know that it is communicating with a proxy server.
/// </summary>
[DebuggerDisplay("Explicit: {IpAddress}:{Port}")]
public class ExplicitProxyEndPoint : ProxyEndPoint
{
/// <summary>
......@@ -33,13 +34,13 @@ namespace Titanium.Web.Proxy.Models
/// Set the <see cref="TunnelConnectSessionEventArgs.DecryptSsl" /> property to false if this HTTP connect request
/// shouldn't be decrypted and instead be relayed.
/// </summary>
public event AsyncEventHandler<TunnelConnectSessionEventArgs> BeforeTunnelConnectRequest;
public event AsyncEventHandler<TunnelConnectSessionEventArgs>? BeforeTunnelConnectRequest;
/// <summary>
/// Intercept tunnel connect response.
/// Valid only for explicit endpoints.
/// </summary>
public event AsyncEventHandler<TunnelConnectSessionEventArgs> BeforeTunnelConnectResponse;
public event AsyncEventHandler<TunnelConnectSessionEventArgs>? BeforeTunnelConnectResponse;
internal async Task InvokeBeforeTunnelConnectRequest(ProxyServer proxyServer,
TunnelConnectSessionEventArgs connectArgs, ExceptionHandler exceptionFunc)
......
......@@ -11,9 +11,9 @@ namespace Titanium.Web.Proxy.Models
private static readonly Lazy<NetworkCredential> defaultCredentials =
new Lazy<NetworkCredential>(() => CredentialCache.DefaultNetworkCredentials);
private string password;
private string? password;
private string userName;
private string? userName;
/// <summary>
/// Use default windows credentials?
......@@ -28,7 +28,7 @@ namespace Titanium.Web.Proxy.Models
/// <summary>
/// Username.
/// </summary>
public string UserName
public string? UserName
{
get => UseDefaultCredentials ? defaultCredentials.Value.UserName : userName;
set
......@@ -45,7 +45,7 @@ namespace Titanium.Web.Proxy.Models
/// <summary>
/// Password.
/// </summary>
public string Password
public string? Password
{
get => UseDefaultCredentials ? defaultCredentials.Value.Password : password;
set
......@@ -62,22 +62,13 @@ namespace Titanium.Web.Proxy.Models
/// <summary>
/// Host name.
/// </summary>
public string HostName { get; set; }
public string HostName { get; set; } = string.Empty;
/// <summary>
/// Port.
/// </summary>
public int Port { get; set; }
/// <summary>
/// Get cache key for Tcp connection cache.
/// </summary>
/// <returns></returns>
internal string GetCacheKey()
{
return $"{HostName}-{Port}" + (UseDefaultCredentials ? $"-{UserName}-{Password}" : string.Empty);
}
/// <summary>
/// returns data in Hostname:port format.
/// </summary>
......
This diff is collapsed.
......@@ -30,7 +30,7 @@
/// <summary>
/// An optional continuation token to return to the caller if set
/// </summary>
public string Continuation { get; set; }
public string? Continuation { get; set; }
public static ProxyAuthenticationContext Failed()
{
......
......@@ -25,7 +25,7 @@ namespace Titanium.Web.Proxy.Models
/// <summary>
/// underlying TCP Listener object
/// </summary>
internal TcpListener Listener { get; set; }
internal TcpListener? Listener { get; set; }
/// <summary>
/// Ip Address we are listening.
......@@ -42,17 +42,9 @@ namespace Titanium.Web.Proxy.Models
/// </summary>
public bool DecryptSsl { get; }
/// <summary>
/// Is IPv6 enabled?
/// </summary>
public bool IpV6Enabled => Equals(IpAddress, IPAddress.IPv6Any)
|| Equals(IpAddress, IPAddress.IPv6Loopback)
|| Equals(IpAddress, IPAddress.IPv6None);
/// <summary>
/// Generic certificate to use for SSL decryption.
/// </summary>
public X509Certificate2 GenericCertificate { get; set; }
public X509Certificate2? GenericCertificate { get; set; }
}
}
......@@ -7,6 +7,6 @@ namespace Titanium.Web.Proxy.Network.Certificate
/// </summary>
internal interface ICertificateMaker
{
X509Certificate2 MakeCertificate(string sSubjectCn, bool isRoot, X509Certificate2 signingCert);
X509Certificate2 MakeCertificate(string sSubjectCn, X509Certificate2? signingCert);
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment