Unverified Commit 33bb3f08 authored by justcoding121's avatar justcoding121 Committed by GitHub

Merge pull request #472 from justcoding121/beta

Stable
parents 35cd23b6 30782eac
...@@ -3,6 +3,7 @@ using System.Collections.Concurrent; ...@@ -3,6 +3,7 @@ using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net; using System.Net;
using System.Net.Security; using System.Net.Security;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Exceptions; using Titanium.Web.Proxy.Exceptions;
...@@ -14,7 +15,7 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -14,7 +15,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
{ {
public class ProxyTestController public class ProxyTestController
{ {
private readonly object lockObj = new object(); private readonly SemaphoreSlim @lock = new SemaphoreSlim(1);
private readonly ProxyServer proxyServer; private readonly ProxyServer proxyServer;
...@@ -23,16 +24,18 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -23,16 +24,18 @@ namespace Titanium.Web.Proxy.Examples.Basic
public ProxyTestController() public ProxyTestController()
{ {
proxyServer = new ProxyServer(); proxyServer = new ProxyServer();
proxyServer.EnableConnectionPool = true;
// generate root certificate without storing it in file system // generate root certificate without storing it in file system
//proxyServer.CertificateManager.CreateRootCertificate(false); //proxyServer.CertificateManager.CreateRootCertificate(false);
//proxyServer.CertificateManager.TrustRootCertificate(); //proxyServer.CertificateManager.TrustRootCertificate();
//proxyServer.CertificateManager.TrustRootCertificateAsAdmin(); //proxyServer.CertificateManager.TrustRootCertificateAsAdmin();
proxyServer.ExceptionFunc = exception => proxyServer.ExceptionFunc = async exception =>
{ {
lock (lockObj) await @lock.WaitAsync();
try
{ {
var color = Console.ForegroundColor; var color = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red; Console.ForegroundColor = ConsoleColor.Red;
...@@ -46,6 +49,11 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -46,6 +49,11 @@ namespace Titanium.Web.Proxy.Examples.Basic
} }
Console.ForegroundColor = color; Console.ForegroundColor = color;
}
finally
{
@lock.Release();
} }
}; };
proxyServer.ForwardToUpstreamGateway = true; proxyServer.ForwardToUpstreamGateway = true;
...@@ -130,7 +138,7 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -130,7 +138,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
private async Task OnBeforeTunnelConnectRequest(object sender, TunnelConnectSessionEventArgs e) private async Task OnBeforeTunnelConnectRequest(object sender, TunnelConnectSessionEventArgs e)
{ {
string hostname = e.WebSession.Request.RequestUri.Host; string hostname = e.WebSession.Request.RequestUri.Host;
WriteToConsole("Tunnel to: " + hostname); await WriteToConsole("Tunnel to: " + hostname);
if (hostname.Contains("dropbox.com")) if (hostname.Contains("dropbox.com"))
{ {
...@@ -141,15 +149,16 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -141,15 +149,16 @@ namespace Titanium.Web.Proxy.Examples.Basic
} }
} }
private async Task OnBeforeTunnelConnectResponse(object sender, TunnelConnectSessionEventArgs e) private Task OnBeforeTunnelConnectResponse(object sender, TunnelConnectSessionEventArgs e)
{ {
return Task.FromResult(false);
} }
// intecept & cancel redirect or update requests // intecept & cancel redirect or update requests
private async Task OnRequest(object sender, SessionEventArgs e) private async Task OnRequest(object sender, SessionEventArgs e)
{ {
WriteToConsole("Active Client Connections:" + ((ProxyServer)sender).ClientConnectionCount); await WriteToConsole("Active Client Connections:" + ((ProxyServer)sender).ClientConnectionCount);
WriteToConsole(e.WebSession.Request.Url); await WriteToConsole(e.WebSession.Request.Url);
// store it in the UserData property // store it in the UserData property
// It can be a simple integer, Guid, or any type // It can be a simple integer, Guid, or any type
...@@ -187,19 +196,19 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -187,19 +196,19 @@ namespace Titanium.Web.Proxy.Examples.Basic
} }
// Modify response // Modify response
private void MultipartRequestPartSent(object sender, MultipartRequestPartSentEventArgs e) private async Task MultipartRequestPartSent(object sender, MultipartRequestPartSentEventArgs e)
{ {
var session = (SessionEventArgs)sender; var session = (SessionEventArgs)sender;
WriteToConsole("Multipart form data headers:"); await WriteToConsole("Multipart form data headers:");
foreach (var header in e.Headers) foreach (var header in e.Headers)
{ {
WriteToConsole(header.ToString()); await WriteToConsole(header.ToString());
} }
} }
private async Task OnResponse(object sender, SessionEventArgs e) private async Task OnResponse(object sender, SessionEventArgs e)
{ {
WriteToConsole("Active Server Connections:" + ((ProxyServer)sender).ServerConnectionCount); await WriteToConsole("Active Server Connections:" + ((ProxyServer)sender).ServerConnectionCount);
string ext = System.IO.Path.GetExtension(e.WebSession.Request.RequestUri.AbsolutePath); string ext = System.IO.Path.GetExtension(e.WebSession.Request.RequestUri.AbsolutePath);
...@@ -271,12 +280,18 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -271,12 +280,18 @@ namespace Titanium.Web.Proxy.Examples.Basic
return Task.FromResult(0); return Task.FromResult(0);
} }
private void WriteToConsole(string message) private async Task WriteToConsole(string message)
{ {
lock (lockObj) await @lock.WaitAsync();
try
{ {
Console.WriteLine(message); Console.WriteLine(message);
} }
finally
{
@lock.Release();
}
} }
///// <summary> ///// <summary>
......
...@@ -51,8 +51,8 @@ ...@@ -51,8 +51,8 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="StreamExtended, Version=1.0.164.0, Culture=neutral, PublicKeyToken=bbfa0f1d54f50043, processorArchitecture=MSIL"> <Reference Include="StreamExtended, Version=1.0.179.0, Culture=neutral, PublicKeyToken=bbfa0f1d54f50043, processorArchitecture=MSIL">
<HintPath>..\..\packages\StreamExtended.1.0.164\lib\net45\StreamExtended.dll</HintPath> <HintPath>..\..\packages\StreamExtended.1.0.179\lib\net45\StreamExtended.dll</HintPath>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
......
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="StreamExtended" version="1.0.164" targetFramework="net45" /> <package id="StreamExtended" version="1.0.179" targetFramework="net45" />
</packages> </packages>
\ No newline at end of file
Doneness: Doneness:
- [ ] Build is okay - I made sure that this change is building successfully. - [ ] Build is okay - I made sure that this change is building successfully.
- [ ] No Bugs - I made sure that this change is working properly as expected. It doesn't have any bugs that you are aware of. - [ ] No Bugs - I made sure that this change is working properly as expected. It doesn't have any bugs that you are aware of.
- [ ] Branching - If this is not a hotfix, I am making this request against develop branch - [ ] Branching - If this is not a hotfix, I am making this request against master branch
...@@ -19,12 +19,9 @@ Kindly report only issues/bugs here . For programming help or questions use [Sta ...@@ -19,12 +19,9 @@ Kindly report only issues/bugs here . For programming help or questions use [Sta
### Features ### Features
* Multithreaded & fully asynchronous proxy * Multithreaded & fully asynchronous proxy employing server connection pooling, certificate cache & buffer pooling
* Supports HTTP(S) and most features of HTTP 1.1 * View/modify/redirect/block requests & responses
* Supports redirect/block/update requests and modifying responses * Supports mutual SSL authentication, proxy authentication & automatic upstream proxy detection
* Safely relays Web Socket requests over HTTP
* Supports mutual SSL authentication
* Supports proxy authentication & automatic proxy detection
* Kerberos/NTLM authentication over HTTP protocols for windows domain * Kerberos/NTLM authentication over HTTP protocols for windows domain
### Usage ### Usage
...@@ -36,7 +33,7 @@ Install by [nuget](https://www.nuget.org/packages/Titanium.Web.Proxy) ...@@ -36,7 +33,7 @@ Install by [nuget](https://www.nuget.org/packages/Titanium.Web.Proxy)
For beta releases on [beta branch](https://github.com/justcoding121/Titanium-Web-Proxy/tree/beta) For beta releases on [beta branch](https://github.com/justcoding121/Titanium-Web-Proxy/tree/beta)
Install-Package Titanium.Web.Proxy -Pre Install-Package Titanium.Web.Proxy
For stable releases on [stable branch](https://github.com/justcoding121/Titanium-Web-Proxy/tree/stable) For stable releases on [stable branch](https://github.com/justcoding121/Titanium-Web-Proxy/tree/stable)
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateInstanceFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateInstanceFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticReadonly/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticReadonly/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=a4ab2e69_002D4d9c_002D4345_002Dbcd1_002D5541dacf5d38/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static, Instance" AccessRightKinds="Private" Description="Method (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="METHOD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=dda2ffa1_002D435c_002D4111_002D88eb_002D1a7c93c382f0/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static, Instance" AccessRightKinds="Private" Description="Property (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="PROPERTY" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=dda2ffa1_002D435c_002D4111_002D88eb_002D1a7c93c382f0/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static, Instance" AccessRightKinds="Private" Description="Property (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="PROPERTY" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpAttributeForSingleLineMethodUpgrade/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpAttributeForSingleLineMethodUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
......
...@@ -2,23 +2,25 @@ ...@@ -2,23 +2,25 @@
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended.Helpers; using StreamExtended;
using StreamExtended.Network; using StreamExtended.Network;
namespace Titanium.Web.Proxy.EventArguments namespace Titanium.Web.Proxy.EventArguments
{ {
internal class LimitedStream : Stream internal class LimitedStream : Stream
{ {
private readonly IBufferPool bufferPool;
private readonly ICustomStreamReader baseStream; private readonly ICustomStreamReader baseStream;
private readonly bool isChunked; private readonly bool isChunked;
private long bytesRemaining; private long bytesRemaining;
private bool readChunkTrail; private bool readChunkTrail;
internal LimitedStream(ICustomStreamReader baseStream, bool isChunked, internal LimitedStream(ICustomStreamReader baseStream, IBufferPool bufferPool, bool isChunked,
long contentLength) long contentLength)
{ {
this.baseStream = baseStream; this.baseStream = baseStream;
this.bufferPool = bufferPool;
this.isChunked = isChunked; this.isChunked = isChunked;
bytesRemaining = isChunked bytesRemaining = isChunked
? 0 ? 0
...@@ -41,7 +43,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -41,7 +43,7 @@ namespace Titanium.Web.Proxy.EventArguments
set => throw new NotSupportedException(); set => throw new NotSupportedException();
} }
private void GetNextChunk() private void getNextChunk()
{ {
if (readChunkTrail) if (readChunkTrail)
{ {
...@@ -96,7 +98,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -96,7 +98,7 @@ namespace Titanium.Web.Proxy.EventArguments
{ {
if (isChunked) if (isChunked)
{ {
GetNextChunk(); getNextChunk();
} }
else else
{ {
...@@ -125,7 +127,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -125,7 +127,7 @@ namespace Titanium.Web.Proxy.EventArguments
{ {
if (bytesRemaining != -1) if (bytesRemaining != -1)
{ {
var buffer = BufferPool.GetBuffer(baseStream.BufferSize); var buffer = bufferPool.GetBuffer(baseStream.BufferSize);
try try
{ {
int res = await ReadAsync(buffer, 0, buffer.Length); int res = await ReadAsync(buffer, 0, buffer.Length);
...@@ -136,7 +138,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -136,7 +138,7 @@ namespace Titanium.Web.Proxy.EventArguments
} }
finally finally
{ {
BufferPool.ReturnBuffer(buffer); bufferPool.ReturnBuffer(buffer);
} }
} }
} }
......
using System; using System;
using System.Net; using System.Net;
using System.Threading; using System.Threading;
using StreamExtended;
using StreamExtended.Network; using StreamExtended.Network;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
...@@ -17,34 +18,32 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -17,34 +18,32 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary> /// </summary>
public abstract class SessionEventArgsBase : EventArgs, IDisposable public abstract class SessionEventArgsBase : EventArgs, IDisposable
{ {
/// <summary>
/// Size of Buffers used by this object
/// </summary>
protected readonly int BufferSize;
internal readonly CancellationTokenSource CancellationTokenSource; internal readonly CancellationTokenSource CancellationTokenSource;
protected readonly ExceptionHandler ExceptionFunc; protected readonly int bufferSize;
protected readonly IBufferPool bufferPool;
protected readonly ExceptionHandler exceptionFunc;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="SessionEventArgsBase" /> class. /// Initializes a new instance of the <see cref="SessionEventArgsBase" /> class.
/// </summary> /// </summary>
internal SessionEventArgsBase(int bufferSize, ProxyEndPoint endPoint, private SessionEventArgsBase(ProxyServer server, ProxyEndPoint endPoint,
CancellationTokenSource cancellationTokenSource, ExceptionHandler exceptionFunc) CancellationTokenSource cancellationTokenSource)
: this(bufferSize, endPoint, cancellationTokenSource, null, exceptionFunc)
{ {
bufferSize = server.BufferSize;
bufferPool = server.BufferPool;
exceptionFunc = server.ExceptionFunc;
} }
protected SessionEventArgsBase(int bufferSize, ProxyEndPoint endPoint, protected SessionEventArgsBase(ProxyServer server, ProxyEndPoint endPoint,
CancellationTokenSource cancellationTokenSource, CancellationTokenSource cancellationTokenSource,
Request request, ExceptionHandler exceptionFunc) Request request) : this(server, endPoint, cancellationTokenSource)
{ {
BufferSize = bufferSize;
ExceptionFunc = exceptionFunc;
CancellationTokenSource = cancellationTokenSource; CancellationTokenSource = cancellationTokenSource;
ProxyClient = new ProxyClient(); ProxyClient = new ProxyClient();
WebSession = new HttpWebClient(bufferSize, request); WebSession = new HttpWebClient(request);
LocalEndPoint = endPoint; LocalEndPoint = endPoint;
WebSession.ProcessId = new Lazy<int>(() => WebSession.ProcessId = new Lazy<int>(() =>
...@@ -151,7 +150,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -151,7 +150,7 @@ namespace Titanium.Web.Proxy.EventArguments
} }
catch (Exception ex) catch (Exception ex)
{ {
ExceptionFunc(new Exception("Exception thrown in user event", ex)); exceptionFunc(new Exception("Exception thrown in user event", ex));
} }
} }
...@@ -163,7 +162,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -163,7 +162,7 @@ namespace Titanium.Web.Proxy.EventArguments
} }
catch (Exception ex) catch (Exception ex)
{ {
ExceptionFunc(new Exception("Exception thrown in user event", ex)); exceptionFunc(new Exception("Exception thrown in user event", ex));
} }
} }
......
...@@ -12,9 +12,9 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -12,9 +12,9 @@ namespace Titanium.Web.Proxy.EventArguments
{ {
private bool? isHttpsConnect; private bool? isHttpsConnect;
internal TunnelConnectSessionEventArgs(int bufferSize, ProxyEndPoint endPoint, ConnectRequest connectRequest, internal TunnelConnectSessionEventArgs(ProxyServer server, ProxyEndPoint endPoint, ConnectRequest connectRequest,
CancellationTokenSource cancellationTokenSource, ExceptionHandler exceptionFunc) CancellationTokenSource cancellationTokenSource)
: base(bufferSize, endPoint, cancellationTokenSource, connectRequest, exceptionFunc) : base(server, endPoint, cancellationTokenSource, connectRequest)
{ {
WebSession.ConnectRequest = connectRequest; WebSession.ConnectRequest = connectRequest;
} }
......
using System;
namespace Titanium.Web.Proxy.Exceptions
{
/// <summary>
/// The server connection was closed upon first read with the new connection from pool.
/// Should retry the request with a new connection.
/// </summary>
public class ServerConnectionException : ProxyException
{
internal ServerConnectionException(string message) : base(message)
{
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="message"></param>
/// <param name="e"></param>
internal ServerConnectionException(string message, Exception e) : base(message, e)
{
}
}
}
This diff is collapsed.
...@@ -13,11 +13,11 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -13,11 +13,11 @@ namespace Titanium.Web.Proxy.Extensions
foreach (var @delegate in invocationList) foreach (var @delegate in invocationList)
{ {
await InternalInvokeAsync((AsyncEventHandler<T>)@delegate, sender, args, exceptionFunc); await internalInvokeAsync((AsyncEventHandler<T>)@delegate, sender, args, exceptionFunc);
} }
} }
private static async Task InternalInvokeAsync<T>(AsyncEventHandler<T> callback, object sender, T args, private static async Task internalInvokeAsync<T>(AsyncEventHandler<T> callback, object sender, T args,
ExceptionHandler exceptionFunc) ExceptionHandler exceptionFunc)
{ {
try try
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended.Helpers; using StreamExtended;
namespace Titanium.Web.Proxy.Extensions namespace Titanium.Web.Proxy.Extensions
{ {
...@@ -19,9 +19,9 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -19,9 +19,9 @@ namespace Titanium.Web.Proxy.Extensions
/// <param name="onCopy"></param> /// <param name="onCopy"></param>
/// <param name="bufferSize"></param> /// <param name="bufferSize"></param>
internal static Task CopyToAsync(this Stream input, Stream output, Action<byte[], int, int> onCopy, internal static Task CopyToAsync(this Stream input, Stream output, Action<byte[], int, int> onCopy,
int bufferSize) IBufferPool bufferPool, int bufferSize)
{ {
return CopyToAsync(input, output, onCopy, bufferSize, CancellationToken.None); return CopyToAsync(input, output, onCopy, bufferPool, bufferSize, CancellationToken.None);
} }
/// <summary> /// <summary>
...@@ -33,9 +33,9 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -33,9 +33,9 @@ namespace Titanium.Web.Proxy.Extensions
/// <param name="bufferSize"></param> /// <param name="bufferSize"></param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
internal static async Task CopyToAsync(this Stream input, Stream output, Action<byte[], int, int> onCopy, internal static async Task CopyToAsync(this Stream input, Stream output, Action<byte[], int, int> onCopy,
int bufferSize, CancellationToken cancellationToken) IBufferPool bufferPool, int bufferSize, CancellationToken cancellationToken)
{ {
var buffer = BufferPool.GetBuffer(bufferSize); var buffer = bufferPool.GetBuffer(bufferSize);
try try
{ {
while (!cancellationToken.IsCancellationRequested) while (!cancellationToken.IsCancellationRequested)
...@@ -43,7 +43,7 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -43,7 +43,7 @@ namespace Titanium.Web.Proxy.Extensions
// cancellation is not working on Socket ReadAsync // cancellation is not working on Socket ReadAsync
// https://github.com/dotnet/corefx/issues/15033 // https://github.com/dotnet/corefx/issues/15033
int num = await input.ReadAsync(buffer, 0, buffer.Length, CancellationToken.None) int num = await input.ReadAsync(buffer, 0, buffer.Length, CancellationToken.None)
.WithCancellation(cancellationToken); .withCancellation(cancellationToken);
int bytesRead; int bytesRead;
if ((bytesRead = num) != 0 && !cancellationToken.IsCancellationRequested) if ((bytesRead = num) != 0 && !cancellationToken.IsCancellationRequested)
{ {
...@@ -58,11 +58,11 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -58,11 +58,11 @@ namespace Titanium.Web.Proxy.Extensions
} }
finally finally
{ {
BufferPool.ReturnBuffer(buffer); bufferPool.ReturnBuffer(buffer);
} }
} }
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)
{ {
var tcs = new TaskCompletionSource<bool>(); var tcs = new TaskCompletionSource<bool>();
using (cancellationToken.Register(s => ((TaskCompletionSource<bool>)s).TrySetResult(true), tcs)) using (cancellationToken.Register(s => ((TaskCompletionSource<bool>)s).TrySetResult(true), tcs))
......
...@@ -122,7 +122,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -122,7 +122,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <returns>1: when CONNECT, 0: when valid HTTP method, -1: otherwise</returns> /// <returns>1: when CONNECT, 0: when valid HTTP method, -1: otherwise</returns>
internal static Task<int> IsConnectMethod(ICustomStreamReader clientStreamReader) internal static Task<int> IsConnectMethod(ICustomStreamReader clientStreamReader)
{ {
return StartsWith(clientStreamReader, "CONNECT"); return startsWith(clientStreamReader, "CONNECT");
} }
/// <summary> /// <summary>
...@@ -132,7 +132,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -132,7 +132,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <returns>1: when PRI, 0: when valid HTTP method, -1: otherwise</returns> /// <returns>1: when PRI, 0: when valid HTTP method, -1: otherwise</returns>
internal static Task<int> IsPriMethod(ICustomStreamReader clientStreamReader) internal static Task<int> IsPriMethod(ICustomStreamReader clientStreamReader)
{ {
return StartsWith(clientStreamReader, "PRI"); return startsWith(clientStreamReader, "PRI");
} }
/// <summary> /// <summary>
...@@ -143,7 +143,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -143,7 +143,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <returns> /// <returns>
/// 1: when starts with the given string, 0: when valid HTTP method, -1: otherwise /// 1: when starts with the given string, 0: when valid HTTP method, -1: otherwise
/// </returns> /// </returns>
private static async Task<int> StartsWith(ICustomStreamReader clientStreamReader, string expectedStart) private static async Task<int> startsWith(ICustomStreamReader clientStreamReader, string expectedStart)
{ {
bool isExpected = true; bool isExpected = true;
int legthToCheck = 10; int legthToCheck = 10;
......
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.Helpers namespace Titanium.Web.Proxy.Helpers
{ {
internal sealed class HttpRequestWriter : HttpWriter internal sealed class HttpRequestWriter : HttpWriter
{ {
internal HttpRequestWriter(Stream stream, int bufferSize) : base(stream, bufferSize) internal HttpRequestWriter(Stream stream, IBufferPool bufferPool, int bufferSize)
: base(stream, bufferPool, bufferSize)
{ {
} }
......
...@@ -2,13 +2,15 @@ ...@@ -2,13 +2,15 @@
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.Helpers namespace Titanium.Web.Proxy.Helpers
{ {
internal sealed class HttpResponseWriter : HttpWriter internal sealed class HttpResponseWriter : HttpWriter
{ {
internal HttpResponseWriter(Stream stream, int bufferSize) : base(stream, bufferSize) internal HttpResponseWriter(Stream stream, IBufferPool bufferPool, int bufferSize)
: base(stream, bufferPool, bufferSize)
{ {
} }
......
...@@ -4,7 +4,7 @@ using System.IO; ...@@ -4,7 +4,7 @@ using System.IO;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended.Helpers; using StreamExtended;
using StreamExtended.Network; using StreamExtended.Network;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Shared; using Titanium.Web.Proxy.Shared;
...@@ -14,6 +14,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -14,6 +14,7 @@ namespace Titanium.Web.Proxy.Helpers
internal class HttpWriter : ICustomStreamWriter internal class HttpWriter : ICustomStreamWriter
{ {
private readonly Stream stream; private readonly Stream stream;
private readonly IBufferPool bufferPool;
private static readonly byte[] newLine = ProxyConstants.NewLine; private static readonly byte[] newLine = ProxyConstants.NewLine;
...@@ -21,10 +22,11 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -21,10 +22,11 @@ namespace Titanium.Web.Proxy.Helpers
private readonly char[] charBuffer; private readonly char[] charBuffer;
internal HttpWriter(Stream stream, int bufferSize) internal HttpWriter(Stream stream, IBufferPool bufferPool, int bufferSize)
{ {
BufferSize = bufferSize; BufferSize = bufferSize;
this.stream = stream; this.stream = stream;
this.bufferPool = bufferPool;
// ASCII encoder max byte count is char count + 1 // ASCII encoder max byte count is char count + 1
charBuffer = new char[BufferSize - 1]; charBuffer = new char[BufferSize - 1];
...@@ -44,10 +46,10 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -44,10 +46,10 @@ namespace Titanium.Web.Proxy.Helpers
internal Task WriteAsync(string value, CancellationToken cancellationToken = default) internal Task WriteAsync(string value, CancellationToken cancellationToken = default)
{ {
return WriteAsyncInternal(value, false, cancellationToken); return writeAsyncInternal(value, false, cancellationToken);
} }
private Task WriteAsyncInternal(string value, bool addNewLine, CancellationToken cancellationToken) private Task writeAsyncInternal(string value, bool addNewLine, CancellationToken cancellationToken)
{ {
int newLineChars = addNewLine ? newLine.Length : 0; int newLineChars = addNewLine ? newLine.Length : 0;
int charCount = value.Length; int charCount = value.Length;
...@@ -55,7 +57,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -55,7 +57,7 @@ namespace Titanium.Web.Proxy.Helpers
{ {
value.CopyTo(0, charBuffer, 0, charCount); value.CopyTo(0, charBuffer, 0, charCount);
var buffer = BufferPool.GetBuffer(BufferSize); var buffer = bufferPool.GetBuffer(BufferSize);
try try
{ {
int idx = encoder.GetBytes(charBuffer, 0, charCount, buffer, 0, true); int idx = encoder.GetBytes(charBuffer, 0, charCount, buffer, 0, true);
...@@ -69,7 +71,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -69,7 +71,7 @@ namespace Titanium.Web.Proxy.Helpers
} }
finally finally
{ {
BufferPool.ReturnBuffer(buffer); bufferPool.ReturnBuffer(buffer);
} }
} }
else else
...@@ -91,7 +93,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -91,7 +93,7 @@ namespace Titanium.Web.Proxy.Helpers
internal Task WriteLineAsync(string value, CancellationToken cancellationToken = default) internal Task WriteLineAsync(string value, CancellationToken cancellationToken = default)
{ {
return WriteAsyncInternal(value, true, cancellationToken); return writeAsyncInternal(value, true, cancellationToken);
} }
/// <summary> /// <summary>
...@@ -104,12 +106,15 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -104,12 +106,15 @@ namespace Titanium.Web.Proxy.Helpers
internal async Task WriteHeadersAsync(HeaderCollection headers, bool flush = true, internal async Task WriteHeadersAsync(HeaderCollection headers, bool flush = true,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
var headerBuilder = new StringBuilder();
foreach (var header in headers) foreach (var header in headers)
{ {
await header.WriteToStreamAsync(this, cancellationToken); headerBuilder.AppendLine(header.ToString());
} }
headerBuilder.AppendLine();
await WriteAsync(headerBuilder.ToString(), cancellationToken);
await WriteLineAsync(cancellationToken);
if (flush) if (flush)
{ {
await stream.FlushAsync(cancellationToken); await stream.FlushAsync(cancellationToken);
...@@ -146,7 +151,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -146,7 +151,7 @@ namespace Titanium.Web.Proxy.Helpers
{ {
if (isChunked) if (isChunked)
{ {
return WriteBodyChunkedAsync(data, cancellationToken); return writeBodyChunkedAsync(data, cancellationToken);
} }
return WriteAsync(data, cancellationToken: cancellationToken); return WriteAsync(data, cancellationToken: cancellationToken);
...@@ -168,7 +173,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -168,7 +173,7 @@ namespace Titanium.Web.Proxy.Helpers
// For chunked request we need to read data as they arrive, until we reach a chunk end symbol // For chunked request we need to read data as they arrive, until we reach a chunk end symbol
if (isChunked) if (isChunked)
{ {
return CopyBodyChunkedAsync(streamReader, onCopy, cancellationToken); return copyBodyChunkedAsync(streamReader, onCopy, cancellationToken);
} }
// http 1.0 or the stream reader limits the stream // http 1.0 or the stream reader limits the stream
...@@ -178,7 +183,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -178,7 +183,7 @@ namespace Titanium.Web.Proxy.Helpers
} }
// If not chunked then its easy just read the amount of bytes mentioned in content length header // If not chunked then its easy just read the amount of bytes mentioned in content length header
return CopyBytesFromStream(streamReader, contentLength, onCopy, cancellationToken); return copyBytesFromStream(streamReader, contentLength, onCopy, cancellationToken);
} }
/// <summary> /// <summary>
...@@ -187,7 +192,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -187,7 +192,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="data"></param> /// <param name="data"></param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
private async Task WriteBodyChunkedAsync(byte[] data, CancellationToken cancellationToken) private async Task writeBodyChunkedAsync(byte[] data, CancellationToken cancellationToken)
{ {
var chunkHead = Encoding.ASCII.GetBytes(data.Length.ToString("x2")); var chunkHead = Encoding.ASCII.GetBytes(data.Length.ToString("x2"));
...@@ -207,7 +212,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -207,7 +212,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="onCopy"></param> /// <param name="onCopy"></param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
private async Task CopyBodyChunkedAsync(ICustomStreamReader reader, Action<byte[], int, int> onCopy, private async Task copyBodyChunkedAsync(ICustomStreamReader reader, Action<byte[], int, int> onCopy,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
while (true) while (true)
...@@ -225,7 +230,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -225,7 +230,7 @@ namespace Titanium.Web.Proxy.Helpers
if (chunkSize != 0) if (chunkSize != 0)
{ {
await CopyBytesFromStream(reader, chunkSize, onCopy, cancellationToken); await copyBytesFromStream(reader, chunkSize, onCopy, cancellationToken);
} }
await WriteLineAsync(cancellationToken); await WriteLineAsync(cancellationToken);
...@@ -248,10 +253,10 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -248,10 +253,10 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="onCopy"></param> /// <param name="onCopy"></param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
private async Task CopyBytesFromStream(ICustomStreamReader reader, long count, Action<byte[], int, int> onCopy, private async Task copyBytesFromStream(ICustomStreamReader reader, long count, Action<byte[], int, int> onCopy,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
var buffer = BufferPool.GetBuffer(BufferSize); var buffer = bufferPool.GetBuffer(BufferSize);
try try
{ {
...@@ -280,7 +285,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -280,7 +285,7 @@ namespace Titanium.Web.Proxy.Helpers
} }
finally finally
{ {
BufferPool.ReturnBuffer(buffer); bufferPool.ReturnBuffer(buffer);
} }
} }
......
...@@ -28,36 +28,44 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -28,36 +28,44 @@ namespace Titanium.Web.Proxy.Helpers
internal static bool IsLocalIpAddress(string hostName) internal static bool IsLocalIpAddress(string hostName)
{ {
bool isLocalhost = false; hostName = hostName.ToLower();
var localhost = Dns.GetHostEntry("127.0.0.1"); if (hostName == "127.0.0.1"
if (hostName == localhost.HostName) || hostName == "localhost")
{ {
var hostEntry = Dns.GetHostEntry(hostName); return true;
isLocalhost = hostEntry.AddressList.Any(IPAddress.IsLoopback);
} }
if (!isLocalhost) var localhostDnsName = Dns.GetHostName().ToLower();
//if hostname matches current machine DNS name
if (hostName == localhostDnsName)
{ {
localhost = Dns.GetHostEntry(Dns.GetHostName()); return true;
}
var isLocalhost = false;
IPHostEntry hostEntry = null;
//check if parsable to an IP Address
if (IPAddress.TryParse(hostName, out var ipAddress)) if (IPAddress.TryParse(hostName, out var ipAddress))
{ {
isLocalhost = localhost.AddressList.Any(x => x.Equals(ipAddress)); hostEntry = Dns.GetHostEntry(localhostDnsName);
isLocalhost = hostEntry.AddressList.Any(x => x.Equals(ipAddress));
} }
if (!isLocalhost) if (!isLocalhost)
{ {
try try
{ {
var hostEntry = Dns.GetHostEntry(hostName); hostEntry = Dns.GetHostEntry(hostName);
isLocalhost = localhost.AddressList.Any(x => hostEntry.AddressList.Any(x.Equals)); isLocalhost = hostEntry.AddressList.Any(x => hostEntry.AddressList.Any(x.Equals));
} }
catch (SocketException) catch (SocketException)
{ {
} }
} }
}
return isLocalhost; return isLocalhost;
} }
......
...@@ -39,7 +39,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -39,7 +39,7 @@ namespace Titanium.Web.Proxy.Helpers
} }
else else
{ {
overrides2.Add(BypassStringEscape(overrideHost)); overrides2.Add(bypassStringEscape(overrideHost));
} }
} }
...@@ -70,7 +70,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -70,7 +70,7 @@ namespace Titanium.Web.Proxy.Helpers
internal string[] BypassList { get; } internal string[] BypassList { get; }
private static string BypassStringEscape(string rawString) private static string bypassStringEscape(string rawString)
{ {
var match = var match =
new Regex("^(?<scheme>.*://)?(?<host>[^:]*)(?<port>:[0-9]{1,5})?$", new Regex("^(?<scheme>.*://)?(?<host>[^:]*)(?<port>:[0-9]{1,5})?$",
...@@ -91,9 +91,9 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -91,9 +91,9 @@ namespace Titanium.Web.Proxy.Helpers
empty2 = string.Empty; empty2 = string.Empty;
} }
string str1 = ConvertRegexReservedChars(empty1); string str1 = convertRegexReservedChars(empty1);
string str2 = ConvertRegexReservedChars(rawString1); string str2 = convertRegexReservedChars(rawString1);
string str3 = ConvertRegexReservedChars(empty2); string str3 = convertRegexReservedChars(empty2);
if (str1 == string.Empty) if (str1 == string.Empty)
{ {
str1 = "(?:.*://)?"; str1 = "(?:.*://)?";
...@@ -107,7 +107,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -107,7 +107,7 @@ namespace Titanium.Web.Proxy.Helpers
return "^" + str1 + str2 + str3 + "$"; return "^" + str1 + str2 + str3 + "$";
} }
private static string ConvertRegexReservedChars(string rawString) private static string convertRegexReservedChars(string rawString)
{ {
if (rawString.Length == 0) if (rawString.Length == 0)
{ {
...@@ -171,11 +171,11 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -171,11 +171,11 @@ namespace Titanium.Web.Proxy.Helpers
if (proxyValues.Length > 0) if (proxyValues.Length > 0)
{ {
result.AddRange(proxyValues.Select(ParseProxyValue).Where(parsedValue => parsedValue != null)); result.AddRange(proxyValues.Select(parseProxyValue).Where(parsedValue => parsedValue != null));
} }
else else
{ {
var parsedValue = ParseProxyValue(proxyServerValues); var parsedValue = parseProxyValue(proxyServerValues);
if (parsedValue != null) if (parsedValue != null)
{ {
result.Add(parsedValue); result.Add(parsedValue);
...@@ -190,7 +190,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -190,7 +190,7 @@ namespace Titanium.Web.Proxy.Helpers
/// </summary> /// </summary>
/// <param name="value"></param> /// <param name="value"></param>
/// <returns></returns> /// <returns></returns>
private static HttpSystemProxyValue ParseProxyValue(string value) private static HttpSystemProxyValue parseProxyValue(string value)
{ {
string tmp = Regex.Replace(value, @"\s+", " ").Trim(); string tmp = Regex.Replace(value, @"\s+", " ").Trim();
......
...@@ -84,8 +84,8 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -84,8 +84,8 @@ namespace Titanium.Web.Proxy.Helpers
if (reg != null) if (reg != null)
{ {
SaveOriginalProxyConfiguration(reg); saveOriginalProxyConfiguration(reg);
PrepareRegistry(reg); prepareRegistry(reg);
string exisitingContent = reg.GetValue(regProxyServer) as string; string exisitingContent = reg.GetValue(regProxyServer) as string;
var existingSystemProxyValues = ProxyInfo.GetSystemProxyValues(exisitingContent); var existingSystemProxyValues = ProxyInfo.GetSystemProxyValues(exisitingContent);
...@@ -115,7 +115,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -115,7 +115,7 @@ namespace Titanium.Web.Proxy.Helpers
reg.SetValue(regProxyServer, reg.SetValue(regProxyServer,
string.Join(";", existingSystemProxyValues.Select(x => x.ToString()).ToArray())); string.Join(";", existingSystemProxyValues.Select(x => x.ToString()).ToArray()));
Refresh(); refresh();
} }
} }
...@@ -129,7 +129,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -129,7 +129,7 @@ namespace Titanium.Web.Proxy.Helpers
{ {
if (saveOriginalConfig) if (saveOriginalConfig)
{ {
SaveOriginalProxyConfiguration(reg); saveOriginalProxyConfiguration(reg);
} }
if (reg.GetValue(regProxyServer) != null) if (reg.GetValue(regProxyServer) != null)
...@@ -152,7 +152,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -152,7 +152,7 @@ namespace Titanium.Web.Proxy.Helpers
} }
} }
Refresh(); refresh();
} }
} }
...@@ -165,12 +165,12 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -165,12 +165,12 @@ namespace Titanium.Web.Proxy.Helpers
if (reg != null) if (reg != null)
{ {
SaveOriginalProxyConfiguration(reg); saveOriginalProxyConfiguration(reg);
reg.SetValue(regProxyEnable, 0); reg.SetValue(regProxyEnable, 0);
reg.SetValue(regProxyServer, string.Empty); reg.SetValue(regProxyServer, string.Empty);
Refresh(); refresh();
} }
} }
...@@ -180,9 +180,9 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -180,9 +180,9 @@ namespace Titanium.Web.Proxy.Helpers
if (reg != null) if (reg != null)
{ {
SaveOriginalProxyConfiguration(reg); saveOriginalProxyConfiguration(reg);
reg.SetValue(regAutoConfigUrl, url); reg.SetValue(regAutoConfigUrl, url);
Refresh(); refresh();
} }
} }
...@@ -192,9 +192,9 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -192,9 +192,9 @@ namespace Titanium.Web.Proxy.Helpers
if (reg != null) if (reg != null)
{ {
SaveOriginalProxyConfiguration(reg); saveOriginalProxyConfiguration(reg);
reg.SetValue(regProxyOverride, proxyOverride); reg.SetValue(regProxyOverride, proxyOverride);
Refresh(); refresh();
} }
} }
...@@ -247,7 +247,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -247,7 +247,7 @@ namespace Titanium.Web.Proxy.Helpers
} }
originalValues = null; originalValues = null;
Refresh(); refresh();
} }
} }
...@@ -257,13 +257,13 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -257,13 +257,13 @@ namespace Titanium.Web.Proxy.Helpers
if (reg != null) if (reg != null)
{ {
return GetProxyInfoFromRegistry(reg); return getProxyInfoFromRegistry(reg);
} }
return null; return null;
} }
private ProxyInfo GetProxyInfoFromRegistry(RegistryKey reg) private ProxyInfo getProxyInfoFromRegistry(RegistryKey reg)
{ {
var pi = new ProxyInfo(null, reg.GetValue(regAutoConfigUrl) as string, reg.GetValue(regProxyEnable) as int?, var pi = new ProxyInfo(null, reg.GetValue(regAutoConfigUrl) as string, reg.GetValue(regProxyEnable) as int?,
reg.GetValue(regProxyServer) as string, reg.GetValue(regProxyServer) as string,
...@@ -272,21 +272,21 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -272,21 +272,21 @@ namespace Titanium.Web.Proxy.Helpers
return pi; return pi;
} }
private void SaveOriginalProxyConfiguration(RegistryKey reg) private void saveOriginalProxyConfiguration(RegistryKey reg)
{ {
if (originalValues != null) if (originalValues != null)
{ {
return; return;
} }
originalValues = GetProxyInfoFromRegistry(reg); originalValues = getProxyInfoFromRegistry(reg);
} }
/// <summary> /// <summary>
/// Prepares the proxy server registry (create empty values if they don't exist) /// Prepares the proxy server registry (create empty values if they don't exist)
/// </summary> /// </summary>
/// <param name="reg"></param> /// <param name="reg"></param>
private static void PrepareRegistry(RegistryKey reg) private static void prepareRegistry(RegistryKey reg)
{ {
if (reg.GetValue(regProxyEnable) == null) if (reg.GetValue(regProxyEnable) == null)
{ {
...@@ -302,7 +302,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -302,7 +302,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <summary> /// <summary>
/// Refresh the settings so that the system know about a change in proxy setting /// Refresh the settings so that the system know about a change in proxy setting
/// </summary> /// </summary>
private void Refresh() private static void refresh()
{ {
NativeMethods.InternetSetOption(IntPtr.Zero, InternetOptionSettingsChanged, IntPtr.Zero, 0); NativeMethods.InternetSetOption(IntPtr.Zero, InternetOptionSettingsChanged, IntPtr.Zero, 0);
NativeMethods.InternetSetOption(IntPtr.Zero, InternetOptionRefresh, IntPtr.Zero, 0); NativeMethods.InternetSetOption(IntPtr.Zero, InternetOptionRefresh, IntPtr.Zero, 0);
......
using System; using System;
using System.IO; using System.IO;
using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended.Helpers; using StreamExtended;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
namespace Titanium.Web.Proxy.Helpers namespace Titanium.Web.Proxy.Helpers
...@@ -39,7 +37,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -39,7 +37,7 @@ namespace Titanium.Web.Proxy.Helpers
0) == 0) 0) == 0)
{ {
int rowCount = *(int*)tcpTable; int rowCount = *(int*)tcpTable;
uint portInNetworkByteOrder = ToNetworkByteOrder((uint)localPort); uint portInNetworkByteOrder = toNetworkByteOrder((uint)localPort);
if (ipVersion == IpVersion.Ipv4) if (ipVersion == IpVersion.Ipv4)
{ {
...@@ -90,7 +88,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -90,7 +88,7 @@ namespace Titanium.Web.Proxy.Helpers
/// </summary> /// </summary>
/// <param name="port"></param> /// <param name="port"></param>
/// <returns></returns> /// <returns></returns>
private static uint ToNetworkByteOrder(uint port) private static uint toNetworkByteOrder(uint port)
{ {
return ((port >> 8) & 0x00FF00FFu) | ((port << 8) & 0xFF00FF00u); return ((port >> 8) & 0x00FF00FFu) | ((port << 8) & 0xFF00FF00u);
} }
...@@ -109,7 +107,8 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -109,7 +107,8 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="cancellationTokenSource"></param> /// <param name="cancellationTokenSource"></param>
/// <param name="exceptionFunc"></param> /// <param name="exceptionFunc"></param>
/// <returns></returns> /// <returns></returns>
internal static async Task SendRawApm(Stream clientStream, Stream serverStream, int bufferSize, internal static async Task SendRawApm(Stream clientStream, Stream serverStream,
IBufferPool bufferPool, int bufferSize,
Action<byte[], int, int> onDataSend, Action<byte[], int, int> onDataReceive, Action<byte[], int, int> onDataSend, Action<byte[], int, int> onDataReceive,
CancellationTokenSource cancellationTokenSource, CancellationTokenSource cancellationTokenSource,
ExceptionHandler exceptionFunc) ExceptionHandler exceptionFunc)
...@@ -118,23 +117,23 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -118,23 +117,23 @@ namespace Titanium.Web.Proxy.Helpers
cancellationTokenSource.Token.Register(() => taskCompletionSource.TrySetResult(true)); cancellationTokenSource.Token.Register(() => taskCompletionSource.TrySetResult(true));
// Now async relay all server=>client & client=>server data // Now async relay all server=>client & client=>server data
var clientBuffer = BufferPool.GetBuffer(bufferSize); var clientBuffer = bufferPool.GetBuffer(bufferSize);
var serverBuffer = BufferPool.GetBuffer(bufferSize); var serverBuffer = bufferPool.GetBuffer(bufferSize);
try try
{ {
BeginRead(clientStream, serverStream, clientBuffer, onDataSend, cancellationTokenSource, exceptionFunc); beginRead(clientStream, serverStream, clientBuffer, onDataSend, cancellationTokenSource, exceptionFunc);
BeginRead(serverStream, clientStream, serverBuffer, onDataReceive, cancellationTokenSource, beginRead(serverStream, clientStream, serverBuffer, onDataReceive, cancellationTokenSource,
exceptionFunc); exceptionFunc);
await taskCompletionSource.Task; await taskCompletionSource.Task;
} }
finally finally
{ {
BufferPool.ReturnBuffer(clientBuffer); bufferPool.ReturnBuffer(clientBuffer);
BufferPool.ReturnBuffer(serverBuffer); bufferPool.ReturnBuffer(serverBuffer);
} }
} }
private static void BeginRead(Stream inputStream, Stream outputStream, byte[] buffer, private static void beginRead(Stream inputStream, Stream outputStream, byte[] buffer,
Action<byte[], int, int> onCopy, CancellationTokenSource cancellationTokenSource, Action<byte[], int, int> onCopy, CancellationTokenSource cancellationTokenSource,
ExceptionHandler exceptionFunc) ExceptionHandler exceptionFunc)
{ {
...@@ -174,7 +173,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -174,7 +173,7 @@ namespace Titanium.Web.Proxy.Helpers
try try
{ {
outputStream.EndWrite(ar2); outputStream.EndWrite(ar2);
BeginRead(inputStream, outputStream, buffer, onCopy, cancellationTokenSource, beginRead(inputStream, outputStream, buffer, onCopy, cancellationTokenSource,
exceptionFunc); exceptionFunc);
} }
catch (IOException ex) catch (IOException ex)
...@@ -214,16 +213,17 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -214,16 +213,17 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="cancellationTokenSource"></param> /// <param name="cancellationTokenSource"></param>
/// <param name="exceptionFunc"></param> /// <param name="exceptionFunc"></param>
/// <returns></returns> /// <returns></returns>
private static async Task SendRawTap(Stream clientStream, Stream serverStream, int bufferSize, private static async Task sendRawTap(Stream clientStream, Stream serverStream,
IBufferPool bufferPool, int bufferSize,
Action<byte[], int, int> onDataSend, Action<byte[], int, int> onDataReceive, Action<byte[], int, int> onDataSend, Action<byte[], int, int> onDataReceive,
CancellationTokenSource cancellationTokenSource, CancellationTokenSource cancellationTokenSource,
ExceptionHandler exceptionFunc) ExceptionHandler exceptionFunc)
{ {
// Now async relay all server=>client & client=>server data // Now async relay all server=>client & client=>server data
var sendRelay = var sendRelay =
clientStream.CopyToAsync(serverStream, onDataSend, bufferSize, cancellationTokenSource.Token); clientStream.CopyToAsync(serverStream, onDataSend, bufferPool, bufferSize, cancellationTokenSource.Token);
var receiveRelay = var receiveRelay =
serverStream.CopyToAsync(clientStream, onDataReceive, bufferSize, cancellationTokenSource.Token); serverStream.CopyToAsync(clientStream, onDataReceive, bufferPool, bufferSize, cancellationTokenSource.Token);
await Task.WhenAny(sendRelay, receiveRelay); await Task.WhenAny(sendRelay, receiveRelay);
cancellationTokenSource.Cancel(); cancellationTokenSource.Cancel();
...@@ -244,13 +244,14 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -244,13 +244,14 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="cancellationTokenSource"></param> /// <param name="cancellationTokenSource"></param>
/// <param name="exceptionFunc"></param> /// <param name="exceptionFunc"></param>
/// <returns></returns> /// <returns></returns>
internal static Task SendRaw(Stream clientStream, Stream serverStream, int bufferSize, internal static Task SendRaw(Stream clientStream, Stream serverStream,
IBufferPool bufferPool, int bufferSize,
Action<byte[], int, int> onDataSend, Action<byte[], int, int> onDataReceive, Action<byte[], int, int> onDataSend, Action<byte[], int, int> onDataReceive,
CancellationTokenSource cancellationTokenSource, CancellationTokenSource cancellationTokenSource,
ExceptionHandler exceptionFunc) ExceptionHandler exceptionFunc)
{ {
// todo: fix APM mode // todo: fix APM mode
return SendRawTap(clientStream, serverStream, bufferSize, onDataSend, onDataReceive, return sendRawTap(clientStream, serverStream, bufferPool, bufferSize, onDataSend, onDataReceive,
cancellationTokenSource, cancellationTokenSource,
exceptionFunc); exceptionFunc);
} }
......
...@@ -19,7 +19,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -19,7 +19,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
session = NativeMethods.WinHttp.WinHttpOpen(null, NativeMethods.WinHttp.AccessType.NoProxy, null, null, 0); session = NativeMethods.WinHttp.WinHttpOpen(null, NativeMethods.WinHttp.AccessType.NoProxy, null, null, 0);
if (session == null || session.IsInvalid) if (session == null || session.IsInvalid)
{ {
int lastWin32Error = GetLastWin32Error(); int lastWin32Error = getLastWin32Error();
} }
else else
{ {
...@@ -30,7 +30,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -30,7 +30,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
return; return;
} }
int lastWin32Error = GetLastWin32Error(); int lastWin32Error = getLastWin32Error();
} }
} }
...@@ -50,7 +50,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -50,7 +50,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
public void Dispose() public void Dispose()
{ {
Dispose(true); dispose(true);
} }
public bool GetAutoProxies(Uri destination, out IList<string> proxyList) public bool GetAutoProxies(Uri destination, out IList<string> proxyList)
...@@ -65,8 +65,8 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -65,8 +65,8 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
var errorCode = NativeMethods.WinHttp.ErrorCodes.AudodetectionFailed; var errorCode = NativeMethods.WinHttp.ErrorCodes.AudodetectionFailed;
if (AutomaticallyDetectSettings && !autoDetectFailed) if (AutomaticallyDetectSettings && !autoDetectFailed)
{ {
errorCode = (NativeMethods.WinHttp.ErrorCodes)GetAutoProxies(destination, null, out proxyListString); errorCode = (NativeMethods.WinHttp.ErrorCodes)getAutoProxies(destination, null, out proxyListString);
autoDetectFailed = IsErrorFatalForAutoDetect(errorCode); autoDetectFailed = isErrorFatalForAutoDetect(errorCode);
if (errorCode == NativeMethods.WinHttp.ErrorCodes.UnrecognizedScheme) if (errorCode == NativeMethods.WinHttp.ErrorCodes.UnrecognizedScheme)
{ {
state = AutoWebProxyState.UnrecognizedScheme; state = AutoWebProxyState.UnrecognizedScheme;
...@@ -74,13 +74,13 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -74,13 +74,13 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
} }
} }
if (AutomaticConfigurationScript != null && IsRecoverableAutoProxyError(errorCode)) if (AutomaticConfigurationScript != null && isRecoverableAutoProxyError(errorCode))
{ {
errorCode = (NativeMethods.WinHttp.ErrorCodes)GetAutoProxies(destination, AutomaticConfigurationScript, errorCode = (NativeMethods.WinHttp.ErrorCodes)getAutoProxies(destination, AutomaticConfigurationScript,
out proxyListString); out proxyListString);
} }
state = GetStateFromErrorCode(errorCode); state = getStateFromErrorCode(errorCode);
if (state != AutoWebProxyState.Completed) if (state != AutoWebProxyState.Completed)
{ {
return false; return false;
...@@ -88,7 +88,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -88,7 +88,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
if (!string.IsNullOrEmpty(proxyListString)) if (!string.IsNullOrEmpty(proxyListString))
{ {
proxyListString = RemoveWhitespaces(proxyListString); proxyListString = removeWhitespaces(proxyListString);
proxyList = proxyListString.Split(';'); proxyList = proxyListString.Split(';');
} }
...@@ -149,7 +149,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -149,7 +149,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
public void LoadFromIE() public void LoadFromIE()
{ {
var pi = GetProxyInfo(); var pi = getProxyInfo();
ProxyInfo = pi; ProxyInfo = pi;
AutomaticallyDetectSettings = pi.AutoDetect == true; AutomaticallyDetectSettings = pi.AutoDetect == true;
AutomaticConfigurationScript = pi.AutoConfigUrl == null ? null : new Uri(pi.AutoConfigUrl); AutomaticConfigurationScript = pi.AutoConfigUrl == null ? null : new Uri(pi.AutoConfigUrl);
...@@ -158,7 +158,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -158,7 +158,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
proxy = new WebProxy(new Uri("http://localhost"), BypassOnLocal, pi.BypassList); proxy = new WebProxy(new Uri("http://localhost"), BypassOnLocal, pi.BypassList);
} }
private ProxyInfo GetProxyInfo() private ProxyInfo getProxyInfo()
{ {
var proxyConfig = new NativeMethods.WinHttp.WINHTTP_CURRENT_USER_IE_PROXY_CONFIG(); var proxyConfig = new NativeMethods.WinHttp.WINHTTP_CURRENT_USER_IE_PROXY_CONFIG();
RuntimeHelpers.PrepareConstrainedRegions(); RuntimeHelpers.PrepareConstrainedRegions();
...@@ -200,7 +200,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -200,7 +200,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
autoDetectFailed = false; autoDetectFailed = false;
} }
private void Dispose(bool disposing) private void dispose(bool disposing)
{ {
if (!disposing || session == null || session.IsInvalid) if (!disposing || session == null || session.IsInvalid)
{ {
...@@ -210,7 +210,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -210,7 +210,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
session.Close(); 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; int num = 0;
var autoProxyOptions = new NativeMethods.WinHttp.WINHTTP_AUTOPROXY_OPTIONS(); var autoProxyOptions = new NativeMethods.WinHttp.WINHTTP_AUTOPROXY_OPTIONS();
...@@ -229,16 +229,16 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -229,16 +229,16 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
autoProxyOptions.AutoDetectFlags = NativeMethods.WinHttp.AutoDetectType.None; autoProxyOptions.AutoDetectFlags = NativeMethods.WinHttp.AutoDetectType.None;
} }
if (!WinHttpGetProxyForUrl(destination.ToString(), ref autoProxyOptions, out proxyListString)) if (!winHttpGetProxyForUrl(destination.ToString(), ref autoProxyOptions, out proxyListString))
{ {
num = GetLastWin32Error(); num = getLastWin32Error();
if (num == (int)NativeMethods.WinHttp.ErrorCodes.LoginFailure && Credentials != null) if (num == (int)NativeMethods.WinHttp.ErrorCodes.LoginFailure && Credentials != null)
{ {
autoProxyOptions.AutoLogonIfChallenged = true; autoProxyOptions.AutoLogonIfChallenged = true;
if (!WinHttpGetProxyForUrl(destination.ToString(), ref autoProxyOptions, out proxyListString)) if (!winHttpGetProxyForUrl(destination.ToString(), ref autoProxyOptions, out proxyListString))
{ {
num = GetLastWin32Error(); num = getLastWin32Error();
} }
} }
} }
...@@ -246,7 +246,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -246,7 +246,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
return num; return num;
} }
private bool WinHttpGetProxyForUrl(string destination, 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; proxyListString = null;
...@@ -271,7 +271,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -271,7 +271,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
return flag; return flag;
} }
private static int GetLastWin32Error() private static int getLastWin32Error()
{ {
int lastWin32Error = Marshal.GetLastWin32Error(); int lastWin32Error = Marshal.GetLastWin32Error();
if (lastWin32Error == 8) if (lastWin32Error == 8)
...@@ -282,7 +282,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -282,7 +282,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
return lastWin32Error; return lastWin32Error;
} }
private static bool IsRecoverableAutoProxyError(NativeMethods.WinHttp.ErrorCodes errorCode) private static bool isRecoverableAutoProxyError(NativeMethods.WinHttp.ErrorCodes errorCode)
{ {
switch (errorCode) switch (errorCode)
{ {
...@@ -300,7 +300,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -300,7 +300,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
} }
} }
private static AutoWebProxyState GetStateFromErrorCode(NativeMethods.WinHttp.ErrorCodes errorCode) private static AutoWebProxyState getStateFromErrorCode(NativeMethods.WinHttp.ErrorCodes errorCode)
{ {
if (errorCode == 0L) if (errorCode == 0L)
{ {
...@@ -324,7 +324,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -324,7 +324,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
} }
} }
private static string RemoveWhitespaces(string value) private static string removeWhitespaces(string value)
{ {
var stringBuilder = new StringBuilder(); var stringBuilder = new StringBuilder();
foreach (char c in value) foreach (char c in value)
...@@ -338,7 +338,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -338,7 +338,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
return stringBuilder.ToString(); return stringBuilder.ToString();
} }
private static bool IsErrorFatalForAutoDetect(NativeMethods.WinHttp.ErrorCodes errorCode) private static bool isErrorFatalForAutoDetect(NativeMethods.WinHttp.ErrorCodes errorCode)
{ {
switch (errorCode) switch (errorCode)
{ {
......
...@@ -24,7 +24,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -24,7 +24,7 @@ namespace Titanium.Web.Proxy.Http
/// </summary> /// </summary>
/// <param name="buffer"></param> /// <param name="buffer"></param>
/// <param name="size"></param> /// <param name="size"></param>
private static void ResizeBuffer(ref byte[] buffer, long size) private static void resizeBuffer(ref byte[] buffer, long size)
{ {
var newBuffer = new byte[size]; var newBuffer = new byte[size];
Buffer.BlockCopy(buffer, 0, newBuffer, 0, buffer.Length); Buffer.BlockCopy(buffer, 0, newBuffer, 0, buffer.Length);
......
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network.Tcp; using Titanium.Web.Proxy.Network.Tcp;
...@@ -15,12 +16,9 @@ namespace Titanium.Web.Proxy.Http ...@@ -15,12 +16,9 @@ namespace Titanium.Web.Proxy.Http
/// </summary> /// </summary>
public class HttpWebClient public class HttpWebClient
{ {
private readonly int bufferSize;
internal HttpWebClient(int bufferSize, Request request = null, Response response = null) internal HttpWebClient(Request request = null, Response response = null)
{ {
this.bufferSize = bufferSize;
Request = request ?? new Request(); Request = request ?? new Request();
Response = response ?? new Response(); Response = response ?? new Response();
} }
...@@ -30,6 +28,11 @@ namespace Titanium.Web.Proxy.Http ...@@ -30,6 +28,11 @@ namespace Titanium.Web.Proxy.Http
/// </summary> /// </summary>
internal TcpServerConnection ServerConnection { get; set; } internal TcpServerConnection ServerConnection { get; set; }
/// <summary>
/// Should we close the server connection at the end of this HTTP request/response session.
/// </summary>
internal bool CloseServerConnection { get; set; }
/// <summary> /// <summary>
/// Stores internal data for the session. /// Stores internal data for the session.
/// </summary> /// </summary>
...@@ -99,15 +102,15 @@ namespace Titanium.Web.Proxy.Http ...@@ -99,15 +102,15 @@ namespace Titanium.Web.Proxy.Http
useUpstreamProxy || isTransparent ? Request.OriginalUrl : Request.RequestUri.PathAndQuery, useUpstreamProxy || isTransparent ? Request.OriginalUrl : Request.RequestUri.PathAndQuery,
Request.HttpVersion), cancellationToken); Request.HttpVersion), cancellationToken);
var headerBuilder = new StringBuilder();
// Send Authentication to Upstream proxy if needed // Send Authentication to Upstream proxy if needed
if (!isTransparent && upstreamProxy != null if (!isTransparent && upstreamProxy != null
&& ServerConnection.IsHttps == false && ServerConnection.IsHttps == false
&& !string.IsNullOrEmpty(upstreamProxy.UserName) && !string.IsNullOrEmpty(upstreamProxy.UserName)
&& upstreamProxy.Password != null) && upstreamProxy.Password != null)
{ {
await HttpHeader.ProxyConnectionKeepAlive.WriteToStreamAsync(writer, cancellationToken); headerBuilder.AppendLine(HttpHeader.ProxyConnectionKeepAlive.ToString());
await HttpHeader.GetProxyAuthorizationHeader(upstreamProxy.UserName, upstreamProxy.Password) headerBuilder.AppendLine(HttpHeader.GetProxyAuthorizationHeader(upstreamProxy.UserName, upstreamProxy.Password).ToString());
.WriteToStreamAsync(writer, cancellationToken);
} }
// write request headers // write request headers
...@@ -115,17 +118,30 @@ namespace Titanium.Web.Proxy.Http ...@@ -115,17 +118,30 @@ namespace Titanium.Web.Proxy.Http
{ {
if (isTransparent || header.Name != KnownHeaders.ProxyAuthorization) if (isTransparent || header.Name != KnownHeaders.ProxyAuthorization)
{ {
await header.WriteToStreamAsync(writer, cancellationToken); headerBuilder.AppendLine(header.ToString());
} }
} }
await writer.WriteLineAsync(cancellationToken); headerBuilder.AppendLine();
await writer.WriteAsync(headerBuilder.ToString(), cancellationToken);
if (enable100ContinueBehaviour) if (enable100ContinueBehaviour)
{ {
if (Request.ExpectContinue) if (Request.ExpectContinue)
{ {
string httpStatus = await ServerConnection.Stream.ReadLineAsync(cancellationToken); string httpStatus;
try
{
httpStatus = await ServerConnection.Stream.ReadLineAsync(cancellationToken);
if (httpStatus == null)
{
throw new ServerConnectionException("Server connection was closed.");
}
}
catch (Exception e) when (!(e is ServerConnectionException))
{
throw new ServerConnectionException("Server connection was closed.");
}
Response.ParseResponseLine(httpStatus, out _, out int responseStatusCode, Response.ParseResponseLine(httpStatus, out _, out int responseStatusCode,
out string responseStatusDescription); out string responseStatusDescription);
...@@ -159,10 +175,18 @@ namespace Titanium.Web.Proxy.Http ...@@ -159,10 +175,18 @@ namespace Titanium.Web.Proxy.Http
return; return;
} }
string httpStatus = await ServerConnection.Stream.ReadLineAsync(cancellationToken); string httpStatus;
try
{
httpStatus = await ServerConnection.Stream.ReadLineAsync(cancellationToken);
if (httpStatus == null) if (httpStatus == null)
{ {
throw new IOException(); throw new ServerConnectionException("Server connection was closed.");
}
}
catch (Exception e) when (!(e is ServerConnectionException))
{
throw new ServerConnectionException("Server connection was closed.");
} }
if (httpStatus == string.Empty) if (httpStatus == string.Empty)
...@@ -217,6 +241,9 @@ namespace Titanium.Web.Proxy.Http ...@@ -217,6 +241,9 @@ namespace Titanium.Web.Proxy.Http
ConnectRequest?.FinishSession(); ConnectRequest?.FinishSession();
Request?.FinishSession(); Request?.FinishSession();
Response?.FinishSession(); Response?.FinishSession();
Data.Clear();
UserData = null;
} }
} }
} }
...@@ -99,7 +99,8 @@ namespace Titanium.Web.Proxy.Http ...@@ -99,7 +99,8 @@ namespace Titanium.Web.Proxy.Http
public string Url => RequestUri.OriginalString; public string Url => RequestUri.OriginalString;
/// <summary> /// <summary>
/// Terminates the underlying Tcp Connection to client after current request. /// Cancels the client HTTP request without sending to server.
/// This should be set when API user responds with custom response.
/// </summary> /// </summary>
internal bool CancelRequest { get; set; } internal bool CancelRequest { get; set; }
...@@ -199,7 +200,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -199,7 +200,7 @@ namespace Titanium.Web.Proxy.Http
// Find the request Verb // Find the request Verb
httpMethod = httpCmdSplit[0]; httpMethod = httpCmdSplit[0];
if (!IsAllUpper(httpMethod)) if (!isAllUpper(httpMethod))
{ {
httpMethod = httpMethod.ToUpper(); httpMethod = httpMethod.ToUpper();
} }
...@@ -219,7 +220,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -219,7 +220,7 @@ namespace Titanium.Web.Proxy.Http
} }
} }
private static bool IsAllUpper(string input) private static bool isAllUpper(string input)
{ {
for (int i = 0; i < input.Length; i++) for (int i = 0; i < input.Length; i++)
{ {
...@@ -232,5 +233,6 @@ namespace Titanium.Web.Proxy.Http ...@@ -232,5 +233,6 @@ namespace Titanium.Web.Proxy.Http
return true; return true;
} }
} }
} }
...@@ -23,10 +23,35 @@ namespace Titanium.Web.Proxy.Http ...@@ -23,10 +23,35 @@ namespace Titanium.Web.Proxy.Http
private string bodyString; private string bodyString;
/// <summary> /// <summary>
/// Store weather the original request/response has body or not, since the user may change the parameters /// Store whether the original request/response has body or not, since the user may change the parameters.
/// We need this detail to syphon out attached tcp connection for reuse.
/// </summary> /// </summary>
internal bool OriginalHasBody { get; set; } internal bool OriginalHasBody { get; set; }
/// <summary>
/// Store original content-length, since the user setting the body may change the parameters.
/// We need this detail to tcp syphon out attached connection for reuse.
/// </summary>
internal long OriginalContentLength { get; set; }
/// <summary>
/// Store whether the original request/response was a chunked body, since the user may change the parameters.
/// We need this detail to syphon out attached tcp connection for reuse.
/// </summary>
internal bool OriginalIsChunked { get; set; }
/// <summary>
/// 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; }
/// <summary>
/// Store whether the original request/response body was read by user.
/// We need this detail to syphon out attached tcp connection for reuse.
/// </summary>
public bool OriginalIsBodyRead { get; internal set; }
/// <summary> /// <summary>
/// Keeps the body data after the session is finished. /// Keeps the body data after the session is finished.
/// </summary> /// </summary>
...@@ -168,6 +193,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -168,6 +193,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary> /// <summary>
/// Is the request/response no more modifyable by user (user callbacks complete?) /// Is the request/response no more modifyable by user (user callbacks complete?)
/// Also if user set this as a custom response then this should be true.
/// </summary> /// </summary>
internal bool Locked { get; set; } internal bool Locked { get; set; }
...@@ -231,6 +257,31 @@ namespace Titanium.Web.Proxy.Http ...@@ -231,6 +257,31 @@ namespace Titanium.Web.Proxy.Http
ContentLength = IsChunked ? -1 : BodyInternal?.Length ?? 0; ContentLength = IsChunked ? -1 : BodyInternal?.Length ?? 0;
} }
/// <summary>
/// Set values for original headers using current headers.
/// </summary>
internal void SetOriginalHeaders()
{
OriginalHasBody = HasBody;
OriginalContentLength = ContentLength;
OriginalIsChunked = IsChunked;
OriginalContentEncoding = ContentEncoding;
OriginalIsBodyRead = IsBodyRead;
}
/// <summary>
/// Copy original header values.
/// </summary>
/// <param name="requestResponseBase"></param>
internal void SetOriginalHeaders(RequestResponseBase requestResponseBase)
{
OriginalHasBody = requestResponseBase.OriginalHasBody;
OriginalContentLength = requestResponseBase.OriginalContentLength;
OriginalIsChunked = requestResponseBase.OriginalIsChunked;
OriginalContentEncoding = requestResponseBase.OriginalContentEncoding;
OriginalIsBodyRead = requestResponseBase.OriginalIsBodyRead;
}
/// <summary> /// <summary>
/// Finish the session /// Finish the session
/// </summary> /// </summary>
......
...@@ -92,8 +92,6 @@ namespace Titanium.Web.Proxy.Http ...@@ -92,8 +92,6 @@ namespace Titanium.Web.Proxy.Http
} }
} }
internal bool TerminateResponse { get; set; }
/// <summary> /// <summary>
/// Is response 100-continue /// Is response 100-continue
/// </summary> /// </summary>
......
This diff is collapsed.
/*
* Copyright 2014 Twitter, Inc
* This file is a derivative work modified by Ringo Leese
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* 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 class DynamicTable
{
// a circular queue of header fields
HttpHeader[] headerFields;
int head;
int tail;
/// <summary>
/// Return the maximum allowable size of the dynamic table.
/// </summary>
/// <value>
/// The capacity.
/// </value>
// ensure setCapacity creates the array
public int Capacity { get; private set; } = -1;
/// <summary>
/// Return the current size of the dynamic table.
/// This is the sum of the size of the entries.
/// </summary>
/// <value>
/// The size.
/// </value>
public int Size { get; private set; }
/// <summary>
/// Creates a new dynamic table with the specified initial capacity.
/// </summary>
/// <param name="initialCapacity">Initial capacity.</param>
public DynamicTable(int initialCapacity)
{
SetCapacity(initialCapacity);
}
/// <summary>
/// Return the number of header fields in the dynamic table.
/// </summary>
public int Length()
{
int length;
if (head < tail)
{
length = headerFields.Length - tail + head;
}
else
{
length = head - tail;
}
return length;
}
/// <summary>
/// Return the header field at the given index.
/// The first and newest entry is always at index 1,
/// and the oldest entry is at the index length().
/// </summary>
/// <returns>The entry.</returns>
/// <param name="index">Index.</param>
public HttpHeader GetEntry(int index)
{
if (index <= 0 || index > Length())
{
throw new IndexOutOfRangeException();
}
int i = head - index;
if (i < 0)
{
return headerFields[i + headerFields.Length];
}
return headerFields[i];
}
/// <summary>
/// Add the header field to the dynamic table.
/// Entries are evicted from the dynamic table until the size of the table
/// and the new header field is less than or equal to the table's capacity.
/// If the size of the new entry is larger than the table's capacity,
/// the dynamic table will be cleared.
/// </summary>
/// <param name="header">Header.</param>
public void Add(HttpHeader header)
{
int headerSize = header.Size;
if (headerSize > Capacity)
{
Clear();
return;
}
while (Size + headerSize > Capacity)
{
Remove();
}
headerFields[head++] = header;
Size += header.Size;
if (head == headerFields.Length)
{
head = 0;
}
}
/// <summary>
/// Remove and return the oldest header field from the dynamic table.
/// </summary>
public HttpHeader Remove()
{
var removed = headerFields[tail];
if (removed == null)
{
return null;
}
Size -= removed.Size;
headerFields[tail++] = null;
if (tail == headerFields.Length)
{
tail = 0;
}
return removed;
}
/// <summary>
/// Remove all entries from the dynamic table.
/// </summary>
public void Clear()
{
while (tail != head)
{
headerFields[tail++] = null;
if (tail == headerFields.Length)
{
tail = 0;
}
}
head = 0;
tail = 0;
Size = 0;
}
/// <summary>
/// Set the maximum size of the dynamic table.
/// Entries are evicted from the dynamic table until the size of the table
/// is less than or equal to the maximum size.
/// </summary>
/// <param name="capacity">Capacity.</param>
public void SetCapacity(int capacity)
{
if (capacity < 0)
{
throw new ArgumentException("Illegal Capacity: " + capacity);
}
// initially capacity will be -1 so init won't return here
if (Capacity == capacity)
{
return;
}
Capacity = capacity;
if (capacity == 0)
{
Clear();
}
else
{
// initially size will be 0 so remove won't be called
while (Size > capacity)
{
Remove();
}
}
int maxEntries = capacity / HttpHeader.HttpHeaderOverhead;
if (capacity % HttpHeader.HttpHeaderOverhead != 0)
{
maxEntries++;
}
// check if capacity change requires us to reallocate the array
if (headerFields != null && headerFields.Length == maxEntries)
{
return;
}
var tmp = new HttpHeader[maxEntries];
// initially length will be 0 so there will be no copy
int len = Length();
int cursor = tail;
for (int i = 0; i < len; i++)
{
var entry = headerFields[cursor++];
tmp[i] = entry;
if (cursor == headerFields.Length)
{
cursor = 0;
}
}
tail = 0;
head = tail + len;
headerFields = tmp;
}
}
}
This diff is collapsed.
/*
* Copyright 2014 Twitter, Inc
* This file is a derivative work modified by Ringo Leese
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
namespace Titanium.Web.Proxy.Http2.Hpack
{
public static class HpackUtil
{
// Section 6.2. Literal Header Field Representation
public enum IndexType
{
Incremental, // Section 6.2.1. Literal Header Field with Incremental Indexing
None, // Section 6.2.2. Literal Header Field without Indexing
Never // Section 6.2.3. Literal Header Field never Indexed
}
// Appendix B: Huffman Codes
// http://tools.ietf.org/html/rfc7541#appendix-B
public static readonly int[] HuffmanCodes = {
0x1ff8,
0x7fffd8,
0xfffffe2,
0xfffffe3,
0xfffffe4,
0xfffffe5,
0xfffffe6,
0xfffffe7,
0xfffffe8,
0xffffea,
0x3ffffffc,
0xfffffe9,
0xfffffea,
0x3ffffffd,
0xfffffeb,
0xfffffec,
0xfffffed,
0xfffffee,
0xfffffef,
0xffffff0,
0xffffff1,
0xffffff2,
0x3ffffffe,
0xffffff3,
0xffffff4,
0xffffff5,
0xffffff6,
0xffffff7,
0xffffff8,
0xffffff9,
0xffffffa,
0xffffffb,
0x14,
0x3f8,
0x3f9,
0xffa,
0x1ff9,
0x15,
0xf8,
0x7fa,
0x3fa,
0x3fb,
0xf9,
0x7fb,
0xfa,
0x16,
0x17,
0x18,
0x0,
0x1,
0x2,
0x19,
0x1a,
0x1b,
0x1c,
0x1d,
0x1e,
0x1f,
0x5c,
0xfb,
0x7ffc,
0x20,
0xffb,
0x3fc,
0x1ffa,
0x21,
0x5d,
0x5e,
0x5f,
0x60,
0x61,
0x62,
0x63,
0x64,
0x65,
0x66,
0x67,
0x68,
0x69,
0x6a,
0x6b,
0x6c,
0x6d,
0x6e,
0x6f,
0x70,
0x71,
0x72,
0xfc,
0x73,
0xfd,
0x1ffb,
0x7fff0,
0x1ffc,
0x3ffc,
0x22,
0x7ffd,
0x3,
0x23,
0x4,
0x24,
0x5,
0x25,
0x26,
0x27,
0x6,
0x74,
0x75,
0x28,
0x29,
0x2a,
0x7,
0x2b,
0x76,
0x2c,
0x8,
0x9,
0x2d,
0x77,
0x78,
0x79,
0x7a,
0x7b,
0x7ffe,
0x7fc,
0x3ffd,
0x1ffd,
0xffffffc,
0xfffe6,
0x3fffd2,
0xfffe7,
0xfffe8,
0x3fffd3,
0x3fffd4,
0x3fffd5,
0x7fffd9,
0x3fffd6,
0x7fffda,
0x7fffdb,
0x7fffdc,
0x7fffdd,
0x7fffde,
0xffffeb,
0x7fffdf,
0xffffec,
0xffffed,
0x3fffd7,
0x7fffe0,
0xffffee,
0x7fffe1,
0x7fffe2,
0x7fffe3,
0x7fffe4,
0x1fffdc,
0x3fffd8,
0x7fffe5,
0x3fffd9,
0x7fffe6,
0x7fffe7,
0xffffef,
0x3fffda,
0x1fffdd,
0xfffe9,
0x3fffdb,
0x3fffdc,
0x7fffe8,
0x7fffe9,
0x1fffde,
0x7fffea,
0x3fffdd,
0x3fffde,
0xfffff0,
0x1fffdf,
0x3fffdf,
0x7fffeb,
0x7fffec,
0x1fffe0,
0x1fffe1,
0x3fffe0,
0x1fffe2,
0x7fffed,
0x3fffe1,
0x7fffee,
0x7fffef,
0xfffea,
0x3fffe2,
0x3fffe3,
0x3fffe4,
0x7ffff0,
0x3fffe5,
0x3fffe6,
0x7ffff1,
0x3ffffe0,
0x3ffffe1,
0xfffeb,
0x7fff1,
0x3fffe7,
0x7ffff2,
0x3fffe8,
0x1ffffec,
0x3ffffe2,
0x3ffffe3,
0x3ffffe4,
0x7ffffde,
0x7ffffdf,
0x3ffffe5,
0xfffff1,
0x1ffffed,
0x7fff2,
0x1fffe3,
0x3ffffe6,
0x7ffffe0,
0x7ffffe1,
0x3ffffe7,
0x7ffffe2,
0xfffff2,
0x1fffe4,
0x1fffe5,
0x3ffffe8,
0x3ffffe9,
0xffffffd,
0x7ffffe3,
0x7ffffe4,
0x7ffffe5,
0xfffec,
0xfffff3,
0xfffed,
0x1fffe6,
0x3fffe9,
0x1fffe7,
0x1fffe8,
0x7ffff3,
0x3fffea,
0x3fffeb,
0x1ffffee,
0x1ffffef,
0xfffff4,
0xfffff5,
0x3ffffea,
0x7ffff4,
0x3ffffeb,
0x7ffffe6,
0x3ffffec,
0x3ffffed,
0x7ffffe7,
0x7ffffe8,
0x7ffffe9,
0x7ffffea,
0x7ffffeb,
0xffffffe,
0x7ffffec,
0x7ffffed,
0x7ffffee,
0x7ffffef,
0x7fffff0,
0x3ffffee,
0x3fffffff // EOS
};
public static readonly byte[] HuffmanCodeLengths = {
13, 23, 28, 28, 28, 28, 28, 28, 28, 24, 30, 28, 28, 30, 28, 28,
28, 28, 28, 28, 28, 28, 30, 28, 28, 28, 28, 28, 28, 28, 28, 28,
6, 10, 10, 12, 13, 6, 8, 11, 10, 10, 8, 11, 8, 6, 6, 6,
5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 8, 15, 6, 12, 10,
13, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 8, 7, 8, 13, 19, 13, 14, 6,
15, 5, 6, 5, 6, 5, 6, 6, 6, 5, 7, 7, 6, 6, 6, 5,
6, 7, 6, 5, 5, 6, 7, 7, 7, 7, 7, 15, 11, 14, 13, 28,
20, 22, 20, 20, 22, 22, 22, 23, 22, 23, 23, 23, 23, 23, 24, 23,
24, 24, 22, 23, 24, 23, 23, 23, 23, 21, 22, 23, 22, 23, 23, 24,
22, 21, 20, 22, 22, 23, 23, 21, 23, 22, 22, 24, 21, 22, 23, 23,
21, 21, 22, 21, 23, 22, 23, 23, 20, 22, 22, 22, 23, 22, 22, 23,
26, 26, 20, 19, 22, 23, 22, 25, 26, 26, 26, 27, 27, 26, 24, 25,
19, 21, 26, 27, 27, 26, 27, 24, 21, 21, 26, 26, 28, 27, 27, 27,
20, 24, 20, 21, 22, 21, 21, 23, 22, 22, 25, 25, 24, 24, 26, 23,
26, 27, 26, 26, 27, 27, 27, 27, 27, 28, 27, 27, 27, 27, 27, 26,
30 // EOS
};
public const int HuffmanEos = 256;
}
}
/*
* Copyright 2014 Twitter, Inc
* This file is a derivative work modified by Ringo Leese
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.IO;
using System.Text;
namespace Titanium.Web.Proxy.Http2.Hpack
{
public class HuffmanDecoder
{
/// <summary>
/// Huffman Decoder
/// </summary>
public static readonly HuffmanDecoder Instance = new HuffmanDecoder();
private readonly Node root;
/// <summary>
/// Creates a new Huffman decoder with the specified Huffman coding.
/// </summary>
private HuffmanDecoder()
{
// the Huffman codes indexed by symbol
var codes = HpackUtil.HuffmanCodes;
// the length of each Huffman code
var lengths = HpackUtil.HuffmanCodeLengths;
if (codes.Length != 257 || codes.Length != lengths.Length)
{
throw new ArgumentException("invalid Huffman coding");
}
root = BuildTree(codes, lengths);
}
/// <summary>
/// Decompresses the given Huffman coded string literal.
/// </summary>
/// <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)
{
var resultBuf = new byte[buf.Length * 2];
int resultSize = 0;
var node = root;
int current = 0;
int bits = 0;
for (int i = 0; i < buf.Length; i++)
{
int b = buf[i];
current = (current << 8) | b;
bits += 8;
while (bits >= 8)
{
int c = (current >> (bits - 8)) & 0xFF;
node = node.Children[c];
bits -= node.Bits;
if (node.IsTerminal)
{
if (node.Symbol == HpackUtil.HuffmanEos)
{
throw new IOException("EOS Decoded");
}
resultBuf[resultSize++] = (byte)node.Symbol;
node = root;
}
}
}
while (bits > 0)
{
int c = (current << (8 - bits)) & 0xFF;
node = node.Children[c];
if (node.IsTerminal && node.Bits <= bits)
{
bits -= node.Bits;
resultBuf[resultSize++] = (byte)node.Symbol;
node = root;
}
else
{
break;
}
}
// Section 5.2. String Literal Representation
// Padding not corresponding to the most significant bits of the code
// for the EOS symbol (0xFF) MUST be treated as a decoding error.
int mask = (1 << bits) - 1;
if ((current & mask) != mask)
{
throw new IOException("Invalid Padding");
}
return Encoding.UTF8.GetString(resultBuf, 0, resultSize);
}
private class Node
{
// terminal nodes have a symbol
public int Symbol { get; }
// number of bits matched by the node
public int Bits { get; }
// internal nodes have children
public Node[] Children { get; }
/// <summary>
/// Initializes a new instance of the <see cref="HuffmanDecoder"/> class.
/// </summary>
public Node()
{
Symbol = 0;
Bits = 8;
Children = new Node[256];
}
/// <summary>
/// Initializes a new instance of the <see cref="HuffmanDecoder"/> class.
/// </summary>
/// <param name="symbol">the symbol the node represents</param>
/// <param name="bits">the number of bits matched by this node</param>
public Node(int symbol, int bits)
{
//assert(bits > 0 && bits <= 8);
Symbol = symbol;
Bits = bits;
Children = null;
}
public bool IsTerminal => Children == null;
}
private static Node BuildTree(int[] codes, byte[] lengths)
{
var root = new Node();
for (int i = 0; i < codes.Length; i++)
{
Insert(root, i, codes[i], lengths[i]);
}
return root;
}
private static void Insert(Node root, int symbol, int code, byte length)
{
// traverse tree using the most significant bytes of code
var current = root;
while (length > 8)
{
if (current.IsTerminal)
{
throw new InvalidDataException("invalid Huffman code: prefix not unique");
}
length -= 8;
int i = (code >> length) & 0xFF;
if (current.Children[i] == null)
{
current.Children[i] = new Node();
}
current = current.Children[i];
}
var terminal = new Node(symbol, length);
int shift = 8 - length;
int start = (code << shift) & 0xFF;
int end = 1 << shift;
for (int i = start; i < start + end; i++)
{
current.Children[i] = terminal;
}
}
}
}
/*
* Copyright 2014 Twitter, Inc
* This file is a derivative work modified by Ringo Leese
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.IO;
namespace Titanium.Web.Proxy.Http2.Hpack
{
public class HuffmanEncoder
{
/// <summary>
/// Huffman Encoder
/// </summary>
public static readonly HuffmanEncoder Instance = new HuffmanEncoder();
/// <summary>
/// the Huffman codes indexed by symbol
/// </summary>
private readonly int[] codes = HpackUtil.HuffmanCodes;
/// <summary>
/// the length of each Huffman code
/// </summary>
private readonly byte[] lengths = HpackUtil.HuffmanCodeLengths;
/// <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>
/// <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)
{
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)
{
return;
}
long current = 0L;
int n = 0;
for (int i = 0; i < len; i++)
{
int b = data[off + i] & 0xFF;
uint code = (uint)codes[b];
int nbits = lengths[b];
current <<= nbits;
current |= code;
n += nbits;
while (n >= 8)
{
n -= 8;
output.Write(((byte)(current >> n)));
}
}
if (n > 0)
{
current <<= (8 - n);
current |= (uint)(0xFF >> n); // this should be EOS symbol
output.Write((byte)current);
}
}
/// <summary>
/// Returns the number of bytes required to Huffman encode the input string literal.
/// </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)
{
if (data == null)
{
throw new ArgumentNullException(nameof(data));
}
long len = 0L;
foreach (byte b in data)
{
len += lengths[b];
}
return (int)((len + 7) >> 3);
}
}
}
/*
* Copyright 2014 Twitter, Inc
* This file is a derivative work modified by Ringo Leese
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Titanium.Web.Proxy.Http2.Hpack
{
public interface IHeaderListener
{
/// <summary>
/// EmitHeader is called by the decoder during header field emission.
/// The name and value byte arrays must not be modified.
/// </summary>
/// <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);
}
}
/*
* Copyright 2014 Twitter, Inc
* This file is a derivative work modified by Ringo Leese
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Text;
using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Http2.Hpack
{
public static class StaticTable
{
/// <summary>
/// Appendix A: Static Table Definition
/// </summary>
/// <see cref="http://tools.ietf.org/html/rfc7541#appendix-A"/>
private static readonly List<HttpHeader> staticTable = new List<HttpHeader>()
{
/* 1 */
new HttpHeader(":authority", string.Empty),
/* 2 */
new HttpHeader(":method", "GET"),
/* 3 */
new HttpHeader(":method", "POST"),
/* 4 */
new HttpHeader(":path", "/"),
/* 5 */
new HttpHeader(":path", "/index.html"),
/* 6 */
new HttpHeader(":scheme", "http"),
/* 7 */
new HttpHeader(":scheme", "https"),
/* 8 */
new HttpHeader(":status", "200"),
/* 9 */
new HttpHeader(":status", "204"),
/* 10 */
new HttpHeader(":status", "206"),
/* 11 */
new HttpHeader(":status", "304"),
/* 12 */
new HttpHeader(":status", "400"),
/* 13 */
new HttpHeader(":status", "404"),
/* 14 */
new HttpHeader(":status", "500"),
/* 15 */
new HttpHeader("accept-charset", string.Empty),
/* 16 */
new HttpHeader("accept-encoding", "gzip, deflate"),
/* 17 */
new HttpHeader("accept-language", string.Empty),
/* 18 */
new HttpHeader("accept-ranges", string.Empty),
/* 19 */
new HttpHeader("accept", string.Empty),
/* 20 */
new HttpHeader("access-control-allow-origin", string.Empty),
/* 21 */
new HttpHeader("age", string.Empty),
/* 22 */
new HttpHeader("allow", string.Empty),
/* 23 */
new HttpHeader("authorization", string.Empty),
/* 24 */
new HttpHeader("cache-control", string.Empty),
/* 25 */
new HttpHeader("content-disposition", string.Empty),
/* 26 */
new HttpHeader("content-encoding", string.Empty),
/* 27 */
new HttpHeader("content-language", string.Empty),
/* 28 */
new HttpHeader("content-length", string.Empty),
/* 29 */
new HttpHeader("content-location", string.Empty),
/* 30 */
new HttpHeader("content-range", string.Empty),
/* 31 */
new HttpHeader("content-type", string.Empty),
/* 32 */
new HttpHeader("cookie", string.Empty),
/* 33 */
new HttpHeader("date", string.Empty),
/* 34 */
new HttpHeader("etag", string.Empty),
/* 35 */
new HttpHeader("expect", string.Empty),
/* 36 */
new HttpHeader("expires", string.Empty),
/* 37 */
new HttpHeader("from", string.Empty),
/* 38 */
new HttpHeader("host", string.Empty),
/* 39 */
new HttpHeader("if-match", string.Empty),
/* 40 */
new HttpHeader("if-modified-since", string.Empty),
/* 41 */
new HttpHeader("if-none-match", string.Empty),
/* 42 */
new HttpHeader("if-range", string.Empty),
/* 43 */
new HttpHeader("if-unmodified-since", string.Empty),
/* 44 */
new HttpHeader("last-modified", string.Empty),
/* 45 */
new HttpHeader("link", string.Empty),
/* 46 */
new HttpHeader("location", string.Empty),
/* 47 */
new HttpHeader("max-forwards", string.Empty),
/* 48 */
new HttpHeader("proxy-authenticate", string.Empty),
/* 49 */
new HttpHeader("proxy-authorization", string.Empty),
/* 50 */
new HttpHeader("range", string.Empty),
/* 51 */
new HttpHeader("referer", string.Empty),
/* 52 */
new HttpHeader("refresh", string.Empty),
/* 53 */
new HttpHeader("retry-after", string.Empty),
/* 54 */
new HttpHeader("server", string.Empty),
/* 55 */
new HttpHeader("set-cookie", string.Empty),
/* 56 */
new HttpHeader("strict-transport-security", string.Empty),
/* 57 */
new HttpHeader("transfer-encoding", string.Empty),
/* 58 */
new HttpHeader("user-agent", string.Empty),
/* 59 */
new HttpHeader("vary", string.Empty),
/* 60 */
new HttpHeader("via", string.Empty),
/* 61 */
new HttpHeader("www-authenticate", string.Empty)
};
private static readonly Dictionary<string, int> staticIndexByName = CreateMap();
/// <summary>
/// The number of header fields in the static table.
/// </summary>
/// <value>The length.</value>
public static int Length => staticTable.Count;
/// <summary>
/// Return the http header field at the given index value.
/// </summary>
/// <returns>The header field.</returns>
/// <param name="index">Index.</param>
public static HttpHeader Get(int index)
{
return staticTable[index - 1];
}
/// <summary>
/// Returns the lowest index value for the given header field name in the static table.
/// Returns -1 if the header field name is not in the static table.
/// </summary>
/// <returns>The index.</returns>
/// <param name="name">Name.</param>
public static int GetIndex(string name)
{
if (!staticIndexByName.ContainsKey(name))
{
return -1;
}
return staticIndexByName[name];
}
/// <summary>
/// Returns the index value for the given header field in the static table.
/// Returns -1 if the header field is not in the static table.
/// </summary>
/// <returns>The index.</returns>
/// <param name="name">Name.</param>
/// <param name="value">Value.</param>
public static int GetIndex(string name, string value)
{
int index = GetIndex(name);
if (index == -1)
{
return -1;
}
// Note this assumes all entries for a given header field are sequential.
while (index <= Length)
{
var entry = Get(index);
if (!HpackUtil.Equals(name, entry.Name))
{
break;
}
if (HpackUtil.Equals(value, entry.Value))
{
return index;
}
index++;
}
return -1;
}
/// <summary>
/// create a map of header name to index value to allow quick lookup
/// </summary>
/// <returns>The map.</returns>
private static Dictionary<string, int> CreateMap()
{
int length = staticTable.Count;
var ret = new Dictionary<string, int>(length);
// Iterate through the static table in reverse order to
// save the smallest index for a given name in the map.
for (int index = length; index > 0; index--)
{
var entry = Get(index);
string name = entry.Name;
ret[name] = index;
}
return ret;
}
}
}
\ No newline at end of file
#if NETCOREAPP2_1
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Http2.Hpack;
namespace Titanium.Web.Proxy.Http2
{
[Flags]
internal enum Http2FrameFlag
{
Ack = 0x01,
EndStream = 0x01,
EndHeaders = 0x04,
Padded = 0x08,
Priority = 0x20,
}
internal class Http2Helper
{
/// <summary>
/// relays the input clientStream to the server at the specified host name and port with the given httpCmd and headers
/// as prefix
/// Usefull for websocket requests
/// Task-based Asynchronous Pattern
/// </summary>
/// <param name="clientStream"></param>
/// <param name="serverStream"></param>
/// <param name="bufferSize"></param>
/// <param name="onDataSend"></param>
/// <param name="onDataReceive"></param>
/// <param name="cancellationTokenSource"></param>
/// <param name="connectionId"></param>
/// <param name="exceptionFunc"></param>
/// <returns></returns>
internal static async Task SendHttp2(Stream clientStream, Stream serverStream, int bufferSize,
Action<byte[], int, int> onDataSend, Action<byte[], int, int> onDataReceive,
CancellationTokenSource cancellationTokenSource, Guid connectionId,
ExceptionHandler exceptionFunc)
{
// Now async relay all server=>client & client=>server data
var sendRelay =
CopyHttp2FrameAsync(clientStream, serverStream, onDataSend, bufferSize, connectionId,
true, cancellationTokenSource.Token);
var receiveRelay =
CopyHttp2FrameAsync(serverStream, clientStream, onDataReceive, bufferSize, connectionId,
false, cancellationTokenSource.Token);
await Task.WhenAny(sendRelay, receiveRelay);
cancellationTokenSource.Cancel();
await Task.WhenAll(sendRelay, receiveRelay);
}
private static async Task CopyHttp2FrameAsync(Stream input, Stream output, Action<byte[], int, int> onCopy,
int bufferSize, Guid connectionId, bool isClient, CancellationToken cancellationToken)
{
var decoder = new Decoder(8192, 4096);
var headerBuffer = new byte[9];
var buffer = new byte[32768];
while (true)
{
int read = await ForceRead(input, headerBuffer, 0, 9, cancellationToken);
if (read != 9)
{
return;
}
int length = (headerBuffer[0] << 16) + (headerBuffer[1] << 8) + headerBuffer[2];
byte type = headerBuffer[3];
byte flags = headerBuffer[4];
int streamId = ((headerBuffer[5] & 0x7f) << 24) + (headerBuffer[6] << 16) + (headerBuffer[7] << 8) +
headerBuffer[8];
read = await ForceRead(input, buffer, 0, length, cancellationToken);
if (read != length)
{
return;
}
if (isClient)
{
if (type == 1 /*headers*/)
{
bool endHeaders = (flags & (int)Http2FrameFlag.EndHeaders) != 0;
bool padded = (flags & (int)Http2FrameFlag.Padded) != 0;
bool priority = (flags & (int)Http2FrameFlag.Priority) != 0;
System.Diagnostics.Debug.WriteLine("HEADER: " + streamId + " end: " + endHeaders);
int offset = 0;
if (padded)
{
offset = 1;
}
if (priority)
{
offset += 5;
}
int dataLength = length - offset;
if (padded)
{
dataLength -= buffer[0];
}
var headerListener = new MyHeaderListener();
try
{
decoder.Decode(new BinaryReader(new MemoryStream(buffer, offset, dataLength)),
headerListener);
decoder.EndHeaderBlock();
}
catch (Exception)
{
}
}
}
await output.WriteAsync(headerBuffer, 0, headerBuffer.Length, cancellationToken);
await output.WriteAsync(buffer, 0, length, cancellationToken);
/*using (var fs = new System.IO.FileStream($@"c:\11\{connectionId}.{streamId}.dat", FileMode.Append))
{
fs.Write(headerBuffer, 0, headerBuffer.Length);
fs.Write(buffer, 0, length);
}*/
}
}
private static async Task<int> ForceRead(Stream input, byte[] buffer, int offset, int bytesToRead,
CancellationToken cancellationToken)
{
int totalRead = 0;
while (bytesToRead > 0)
{
int read = await input.ReadAsync(buffer, offset, bytesToRead, cancellationToken);
if (read == -1)
{
break;
}
totalRead += read;
bytesToRead -= read;
offset += read;
}
return totalRead;
}
class MyHeaderListener : IHeaderListener
{
public void AddHeader(string name, string value, bool sensitive)
{
Console.WriteLine(name + ": " + value + " " + sensitive);
}
}
}
}
#endif
\ No newline at end of file
...@@ -69,6 +69,15 @@ namespace Titanium.Web.Proxy.Models ...@@ -69,6 +69,15 @@ namespace Titanium.Web.Proxy.Models
/// </summary> /// </summary>
public int Port { get; set; } public int Port { get; set; }
/// <summary>
/// Get cache key for Tcp connection cahe.
/// </summary>
/// <returns></returns>
internal string GetCacheKey()
{
return $"{HostName}-{Port}" + (UseDefaultCredentials ? $"-{UserName}-{Password}" : string.Empty);
}
/// <summary> /// <summary>
/// returns data in Hostname:port format. /// returns data in Hostname:port format.
/// </summary> /// </summary>
...@@ -77,5 +86,6 @@ namespace Titanium.Web.Proxy.Models ...@@ -77,5 +86,6 @@ namespace Titanium.Web.Proxy.Models
{ {
return $"{HostName}:{Port}"; return $"{HostName}:{Port}";
} }
} }
} }
using System; using System;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.Models namespace Titanium.Web.Proxy.Models
...@@ -12,6 +9,13 @@ namespace Titanium.Web.Proxy.Models ...@@ -12,6 +9,13 @@ namespace Titanium.Web.Proxy.Models
/// </summary> /// </summary>
public class HttpHeader public class HttpHeader
{ {
/// <summary>
/// HPACK: Header Compression for HTTP/2
/// Section 4.1. Calculating Table Size
/// The additional 32 octets account for an estimated overhead associated with an entry.
/// </summary>
public const int HttpHeaderOverhead = 32;
internal static readonly Version VersionUnknown = new Version(0, 0); internal static readonly Version VersionUnknown = new Version(0, 0);
internal static readonly Version Version10 = new Version(1, 0); internal static readonly Version Version10 = new Version(1, 0);
...@@ -48,6 +52,13 @@ namespace Titanium.Web.Proxy.Models ...@@ -48,6 +52,13 @@ namespace Titanium.Web.Proxy.Models
/// </summary> /// </summary>
public string Value { get; set; } public string Value { get; set; }
public int Size => Name.Length + Value.Length + HttpHeaderOverhead;
public static int SizeOf(string name, string value)
{
return name.Length + value.Length + HttpHeaderOverhead;
}
/// <summary> /// <summary>
/// Returns header as a valid header string. /// Returns header as a valid header string.
/// </summary> /// </summary>
...@@ -63,12 +74,5 @@ namespace Titanium.Web.Proxy.Models ...@@ -63,12 +74,5 @@ namespace Titanium.Web.Proxy.Models
"Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes($"{userName}:{password}"))); "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes($"{userName}:{password}")));
return result; return result;
} }
internal async Task WriteToStreamAsync(HttpWriter writer, CancellationToken cancellationToken)
{
await writer.WriteAsync(Name, cancellationToken);
await writer.WriteAsync(": ", cancellationToken);
await writer.WriteLineAsync(Value, cancellationToken);
}
} }
} }
namespace Titanium.Web.Proxy.Models
{
public enum ProxyAuthenticationResult
{
/// <summary>
/// Indicates the authentication request was successful
/// </summary>
Success,
/// <summary>
/// Indicates the authentication request failed
/// </summary>
Failure,
/// <summary>
/// Indicates that this stage of the authentication request succeeded
/// And a second pass of the handshake needs to occur
/// </summary>
ContinuationNeeded
}
/// <summary>
/// A context container for authentication flows
/// </summary>
public class ProxyAuthenticationContext
{
/// <summary>
/// The result of the current authentication request
/// </summary>
public ProxyAuthenticationResult Result { get; set; }
/// <summary>
/// An optional continuation token to return to the caller if set
/// </summary>
public string Continuation { get; set; }
public static ProxyAuthenticationContext Failed()
{
return new ProxyAuthenticationContext
{
Result = ProxyAuthenticationResult.Failure,
Continuation = null
};
}
public static ProxyAuthenticationContext Succeeded()
{
return new ProxyAuthenticationContext
{
Result = ProxyAuthenticationResult.Success,
Continuation = null
};
}
}
}
...@@ -49,7 +49,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -49,7 +49,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
/// <returns>X509Certificate2 instance.</returns> /// <returns>X509Certificate2 instance.</returns>
public X509Certificate2 MakeCertificate(string sSubjectCn, bool isRoot, X509Certificate2 signingCert = null) public X509Certificate2 MakeCertificate(string sSubjectCn, bool isRoot, X509Certificate2 signingCert = null)
{ {
return MakeCertificateInternal(sSubjectCn, isRoot, true, signingCert); return makeCertificateInternal(sSubjectCn, isRoot, true, signingCert);
} }
/// <summary> /// <summary>
...@@ -65,7 +65,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -65,7 +65,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
/// <param name="hostName">The host name</param> /// <param name="hostName">The host name</param>
/// <returns>X509Certificate2 instance.</returns> /// <returns>X509Certificate2 instance.</returns>
/// <exception cref="PemException">Malformed sequence in RSA private key</exception> /// <exception cref="PemException">Malformed sequence in RSA private key</exception>
private static X509Certificate2 GenerateCertificate(string hostName, private static X509Certificate2 generateCertificate(string hostName,
string subjectName, string subjectName,
string issuerName, DateTime validFrom, string issuerName, DateTime validFrom,
DateTime validTo, int keyStrength = 2048, DateTime validTo, int keyStrength = 2048,
...@@ -145,8 +145,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -145,8 +145,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
var x509Certificate = new X509Certificate2(certificate.GetEncoded()); var x509Certificate = new X509Certificate2(certificate.GetEncoded());
x509Certificate.PrivateKey = DotNetUtilities.ToRSA(rsaparams); x509Certificate.PrivateKey = DotNetUtilities.ToRSA(rsaparams);
#else #else
var x509Certificate = WithPrivateKey(certificate, rsaparams); var x509Certificate = withPrivateKey(certificate, rsaparams);
x509Certificate.FriendlyName = subjectName;
#endif #endif
if (!doNotSetFriendlyName) if (!doNotSetFriendlyName)
...@@ -164,7 +163,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -164,7 +163,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
return x509Certificate; return x509Certificate;
} }
private static X509Certificate2 WithPrivateKey(X509Certificate certificate, AsymmetricKeyParameter privateKey) private static X509Certificate2 withPrivateKey(X509Certificate certificate, AsymmetricKeyParameter privateKey)
{ {
const string password = "password"; const string password = "password";
var store = new Pkcs12Store(); var store = new Pkcs12Store();
...@@ -194,7 +193,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -194,7 +193,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
/// You must specify a Signing Certificate if and only if you are not creating a /// You must specify a Signing Certificate if and only if you are not creating a
/// root. /// root.
/// </exception> /// </exception>
private X509Certificate2 MakeCertificateInternal(bool isRoot, private X509Certificate2 makeCertificateInternal(bool isRoot,
string hostName, string subjectName, string hostName, string subjectName,
DateTime validFrom, DateTime validTo, X509Certificate2 signingCertificate) DateTime validFrom, DateTime validTo, X509Certificate2 signingCertificate)
{ {
...@@ -207,11 +206,11 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -207,11 +206,11 @@ namespace Titanium.Web.Proxy.Network.Certificate
if (isRoot) if (isRoot)
{ {
return GenerateCertificate(null, subjectName, subjectName, validFrom, validTo); return generateCertificate(null, subjectName, subjectName, validFrom, validTo);
} }
var kp = DotNetUtilities.GetKeyPair(signingCertificate.PrivateKey); var kp = DotNetUtilities.GetKeyPair(signingCertificate.PrivateKey);
return GenerateCertificate(hostName, subjectName, signingCertificate.Subject, validFrom, validTo, return generateCertificate(hostName, subjectName, signingCertificate.Subject, validFrom, validTo,
issuerPrivateKey: kp.Private); issuerPrivateKey: kp.Private);
} }
...@@ -224,7 +223,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -224,7 +223,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
/// <param name="signingCert">The signing cert.</param> /// <param name="signingCert">The signing cert.</param>
/// <param name="cancellationToken">Task cancellation token</param> /// <param name="cancellationToken">Task cancellation token</param>
/// <returns>X509Certificate2.</returns> /// <returns>X509Certificate2.</returns>
private X509Certificate2 MakeCertificateInternal(string subject, bool isRoot, private X509Certificate2 makeCertificateInternal(string subject, bool isRoot,
bool switchToMtaIfNeeded, X509Certificate2 signingCert = null, bool switchToMtaIfNeeded, X509Certificate2 signingCert = null,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
...@@ -238,7 +237,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -238,7 +237,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
{ {
try try
{ {
certificate = MakeCertificateInternal(subject, isRoot, false, signingCert); certificate = makeCertificateInternal(subject, isRoot, false, signingCert);
} }
catch (Exception ex) catch (Exception ex)
{ {
...@@ -258,7 +257,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -258,7 +257,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
} }
#endif #endif
return MakeCertificateInternal(isRoot, subject, $"CN={subject}", return makeCertificateInternal(isRoot, subject, $"CN={subject}",
DateTime.UtcNow.AddDays(-certificateGraceDays), DateTime.UtcNow.AddDays(certificateValidDays), DateTime.UtcNow.AddDays(-certificateGraceDays), DateTime.UtcNow.AddDays(certificateValidDays),
isRoot ? null : signingCert); isRoot ? null : signingCert);
} }
......
...@@ -80,10 +80,10 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -80,10 +80,10 @@ namespace Titanium.Web.Proxy.Network.Certificate
/// <returns></returns> /// <returns></returns>
public X509Certificate2 MakeCertificate(string sSubjectCN, bool isRoot, X509Certificate2 signingCert = null) public X509Certificate2 MakeCertificate(string sSubjectCN, bool isRoot, X509Certificate2 signingCert = null)
{ {
return MakeCertificateInternal(sSubjectCN, isRoot, true, signingCert); return makeCertificateInternal(sSubjectCN, isRoot, true, signingCert);
} }
private X509Certificate2 MakeCertificate(bool isRoot, string subject, string fullSubject, private X509Certificate2 makeCertificate(bool isRoot, string subject, string fullSubject,
int privateKeyLength, string hashAlg, DateTime validFrom, DateTime validTo, int privateKeyLength, string hashAlg, DateTime validFrom, DateTime validTo,
X509Certificate2 signingCertificate) X509Certificate2 signingCertificate)
{ {
...@@ -274,13 +274,13 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -274,13 +274,13 @@ namespace Titanium.Web.Proxy.Network.Certificate
return new X509Certificate2(Convert.FromBase64String(empty), string.Empty, X509KeyStorageFlags.Exportable); return new X509Certificate2(Convert.FromBase64String(empty), string.Empty, X509KeyStorageFlags.Exportable);
} }
private X509Certificate2 MakeCertificateInternal(string sSubjectCN, bool isRoot, private X509Certificate2 makeCertificateInternal(string sSubjectCN, bool isRoot,
bool switchToMTAIfNeeded, X509Certificate2 signingCert = null, bool switchToMTAIfNeeded, X509Certificate2 signingCert = null,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
if (switchToMTAIfNeeded && Thread.CurrentThread.GetApartmentState() != ApartmentState.MTA) if (switchToMTAIfNeeded && Thread.CurrentThread.GetApartmentState() != ApartmentState.MTA)
{ {
return Task.Run(() => MakeCertificateInternal(sSubjectCN, isRoot, false, signingCert), return Task.Run(() => makeCertificateInternal(sSubjectCN, isRoot, false, signingCert),
cancellationToken).Result; cancellationToken).Result;
} }
...@@ -301,7 +301,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -301,7 +301,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
var graceTime = DateTime.Now.AddDays(graceDays); var graceTime = DateTime.Now.AddDays(graceDays);
var now = DateTime.Now; var now = DateTime.Now;
var certificate = MakeCertificate(isRoot, sSubjectCN, fullSubject, keyLength, hashAlgo, graceTime, var certificate = makeCertificate(isRoot, sSubjectCN, fullSubject, keyLength, hashAlgo, graceTime,
now.AddDays(validDays), isRoot ? null : signingCert); now.AddDays(validDays), isRoot ? null : signingCert);
return certificate; return certificate;
} }
......
...@@ -3,6 +3,7 @@ using System; ...@@ -3,6 +3,7 @@ using System;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using StreamExtended;
using StreamExtended.Network; using StreamExtended.Network;
namespace Titanium.Web.Proxy.Network namespace Titanium.Web.Proxy.Network
...@@ -17,7 +18,8 @@ namespace Titanium.Web.Proxy.Network ...@@ -17,7 +18,8 @@ namespace Titanium.Web.Proxy.Network
private readonly FileStream fileStreamSent; private readonly FileStream fileStreamSent;
public DebugCustomBufferedStream(Guid connectionId, string type, Stream baseStream, int bufferSize, bool leaveOpen = false) : base(baseStream, bufferSize, leaveOpen) public DebugCustomBufferedStream(Guid connectionId, string type, Stream baseStream, IBufferPool bufferPool, int bufferSize, bool leaveOpen = false)
: base(baseStream, bufferPool, bufferSize, leaveOpen)
{ {
Counter = Interlocked.Increment(ref counter); Counter = Interlocked.Increment(ref counter);
fileStreamSent = new FileStream(Path.Combine(basePath, $"{connectionId}_{type}_{Counter}_sent.dat"), FileMode.Create); fileStreamSent = new FileStream(Path.Combine(basePath, $"{connectionId}_{type}_{Counter}_sent.dat"), FileMode.Create);
......
using System;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Network.Tcp;
namespace Titanium.Web.Proxy.Network
{
internal class RetryPolicy<T> where T : Exception
{
private readonly int retries;
private readonly TcpConnectionFactory tcpConnectionFactory;
private TcpServerConnection currentConnection;
internal RetryPolicy(int retries, TcpConnectionFactory tcpConnectionFactory)
{
this.retries = retries;
this.tcpConnectionFactory = tcpConnectionFactory;
}
/// <summary>
/// Execute and retry the given action until retry number of times.
/// </summary>
/// <param name="action">The action to retry with return value specifying whether caller should continue execution.</param>
/// <param name="generator">The Tcp connection generator to be invoked to get new connection for retry.</param>
/// <param name="initialConnection">Initial Tcp connection to use.</param>
/// <returns>Returns the latest connection used and the latest exception if any.</returns>
internal async Task<RetryResult> ExecuteAsync(Func<TcpServerConnection, Task<bool>> action,
Func<Task<TcpServerConnection>> generator, TcpServerConnection initialConnection)
{
currentConnection = initialConnection;
bool @continue = true;
Exception exception = null;
var attempts = retries;
while (true)
{
try
{
//setup connection
currentConnection = currentConnection as TcpServerConnection ??
await generator();
//try
@continue = await action(currentConnection);
}
catch (Exception ex)
{
exception = ex;
}
attempts--;
if (attempts < 0
|| exception == null
|| !(exception is T))
{
break;
}
exception = null;
await disposeConnection();
}
return new RetryResult(currentConnection, exception, @continue);
}
//before retry clear connection
private async Task disposeConnection()
{
if (currentConnection != null)
{
//close connection on error
await tcpConnectionFactory.Release(currentConnection, true);
currentConnection = null;
}
}
}
internal class RetryResult
{
internal bool IsSuccess => Exception == null;
internal TcpServerConnection LatestConnection { get; }
internal Exception Exception { get; }
internal bool Continue { get; }
internal RetryResult(TcpServerConnection lastConnection, Exception exception, bool @continue)
{
LatestConnection = lastConnection;
Exception = exception;
Continue = @continue;
}
}
}
using System; using System;
using System.IO; using System.IO;
using System.Net; using System.Net;
#if NETCOREAPP2_1
using System.Net.Security; using System.Net.Security;
#endif
using System.Net.Sockets; using System.Net.Sockets;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
...@@ -41,9 +43,8 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -41,9 +43,8 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{ {
tcpClient.CloseSocket();
proxyServer.UpdateClientConnectionCount(false); proxyServer.UpdateClientConnectionCount(false);
tcpClient.CloseSocket();
} }
} }
} }
using System; using System;
using System.Net; using System.Net;
#if NETCOREAPP2_1
using System.Net.Security; using System.Net.Security;
#endif
using System.Net.Sockets; using System.Net.Sockets;
using StreamExtended.Network; using StreamExtended.Network;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
...@@ -48,6 +50,11 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -48,6 +50,11 @@ namespace Titanium.Web.Proxy.Network.Tcp
private readonly TcpClient tcpClient; private readonly TcpClient tcpClient;
/// <summary>
/// The TcpClient.
/// </summary>
internal TcpClient TcpClient => tcpClient;
/// <summary> /// <summary>
/// Used to write lines to server /// Used to write lines to server
/// </summary> /// </summary>
...@@ -63,16 +70,24 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -63,16 +70,24 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// </summary> /// </summary>
internal DateTime LastAccess { get; set; } internal DateTime LastAccess { get; set; }
/// <summary>
/// The cache key used to uniquely identify this connection properties
/// </summary>
internal string CacheKey { get; set; }
/// <summary>
/// Is this connection authenticated via WinAuth
/// </summary>
internal bool IsWinAuthenticated { get; set; }
/// <summary> /// <summary>
/// Dispose. /// Dispose.
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{ {
proxyServer.UpdateServerConnectionCount(false);
Stream?.Dispose(); Stream?.Dispose();
tcpClient.CloseSocket(); tcpClient.CloseSocket();
proxyServer.UpdateServerConnectionCount(false);
} }
} }
} }
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Portable.BouncyCastle" Version="1.8.2" /> <PackageReference Include="Portable.BouncyCastle" Version="1.8.2" />
<PackageReference Include="StreamExtended" Version="1.0.164" /> <PackageReference Include="StreamExtended" Version="1.0.179" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'"> <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
...@@ -34,8 +34,31 @@ ...@@ -34,8 +34,31 @@
</PackageReference> </PackageReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup Condition="'$(TargetFramework)' == 'net45'">
<Reference Include="System.Web" /> <Reference Include="System.Web" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Compile Update="Network\WinAuth\Security\Common.cs">
<ExcludeFromSourceAnalysis>True</ExcludeFromSourceAnalysis>
<ExcludeFromStyleCop>True</ExcludeFromStyleCop>
</Compile>
<Compile Update="Network\WinAuth\Security\LittleEndian.cs">
<ExcludeFromSourceAnalysis>True</ExcludeFromSourceAnalysis>
<ExcludeFromStyleCop>True</ExcludeFromStyleCop>
</Compile>
<Compile Update="Network\WinAuth\Security\Message.cs">
<ExcludeFromSourceAnalysis>True</ExcludeFromSourceAnalysis>
<ExcludeFromStyleCop>True</ExcludeFromStyleCop>
</Compile>
<Compile Update="Network\WinAuth\Security\State.cs">
<ExcludeFromSourceAnalysis>True</ExcludeFromSourceAnalysis>
<ExcludeFromStyleCop>True</ExcludeFromStyleCop>
</Compile>
<Compile Update="Properties\AssemblyInfo.cs">
<ExcludeFromSourceAnalysis>True</ExcludeFromSourceAnalysis>
<ExcludeFromStyleCop>True</ExcludeFromStyleCop>
</Compile>
</ItemGroup>
</Project> </Project>
\ No newline at end of file
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
<copyright>Copyright &#x00A9; Titanium. All rights reserved.</copyright> <copyright>Copyright &#x00A9; Titanium. All rights reserved.</copyright>
<tags></tags> <tags></tags>
<dependencies> <dependencies>
<dependency id="StreamExtended" version="1.0.164" /> <dependency id="StreamExtended" version="1.0.179" />
<dependency id="Portable.BouncyCastle" version="1.8.2" /> <dependency id="Portable.BouncyCastle" version="1.8.2" />
</dependencies> </dependencies>
</metadata> </metadata>
......
This diff is collapsed.
This diff is collapsed.
...@@ -2,5 +2,5 @@ ...@@ -2,5 +2,5 @@
<packages> <packages>
<package id="Portable.BouncyCastle" version="1.8.2" targetFramework="net45" /> <package id="Portable.BouncyCastle" version="1.8.2" targetFramework="net45" />
<package id="StreamExtended" version="1.0.164" targetFramework="net45" /> <package id="StreamExtended" version="1.0.179" targetFramework="net45" />
</packages> </packages>
\ No newline at end of file
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Delegate AsyncEventHandler&lt;TEventArgs&gt; <meta name="title" content="Delegate AsyncEventHandler&lt;TEventArgs&gt;
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.35.4.0"> <meta name="generator" content="docfx 2.37.0.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta name="title" content="Class BeforeSslAuthenticateEventArgs <meta name="title" content="Class BeforeSslAuthenticateEventArgs
| Titanium Web Proxy "> | Titanium Web Proxy ">
<meta name="generator" content="docfx 2.35.4.0"> <meta name="generator" content="docfx 2.37.0.0">
<link rel="shortcut icon" href="../favicon.ico"> <link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css"> <link rel="stylesheet" href="../styles/docfx.vendor.css">
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment