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;
using System.Collections.Generic;
using System.Net;
using System.Net.Security;
using System.Threading;
using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Exceptions;
......@@ -14,7 +15,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
{
public class ProxyTestController
{
private readonly object lockObj = new object();
private readonly SemaphoreSlim @lock = new SemaphoreSlim(1);
private readonly ProxyServer proxyServer;
......@@ -23,34 +24,41 @@ namespace Titanium.Web.Proxy.Examples.Basic
public ProxyTestController()
{
proxyServer = new ProxyServer();
proxyServer.EnableConnectionPool = true;
// generate root certificate without storing it in file system
//proxyServer.CertificateManager.CreateRootCertificate(false);
//proxyServer.CertificateManager.TrustRootCertificate();
//proxyServer.CertificateManager.TrustRootCertificateAsAdmin();
proxyServer.ExceptionFunc = exception =>
proxyServer.ExceptionFunc = async exception =>
{
lock (lockObj)
await @lock.WaitAsync();
try
{
var color = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
if (exception is ProxyHttpException phex)
{
Console.WriteLine(exception.Message + ": " + phex.InnerException?.Message);
}
else
{
Console.WriteLine(exception.Message);
}
Console.ForegroundColor = color;
var color = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
if (exception is ProxyHttpException phex)
{
Console.WriteLine(exception.Message + ": " + phex.InnerException?.Message);
}
else
{
Console.WriteLine(exception.Message);
}
Console.ForegroundColor = color;
}
finally
{
@lock.Release();
}
};
proxyServer.ForwardToUpstreamGateway = true;
proxyServer.CertificateManager.SaveFakeCertificates = true;
// optionally set the Certificate Engine
// Under Mono or Non-Windows runtimes only BouncyCastle will be supported
//proxyServer.CertificateManager.CertificateEngine = Network.CertificateEngine.BouncyCastle;
......@@ -122,7 +130,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
proxyServer.ClientCertificateSelectionCallback -= OnCertificateSelection;
proxyServer.Stop();
// remove the generated certificates
//proxyServer.CertificateManager.RemoveTrustedRootCertificates();
}
......@@ -130,7 +138,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
private async Task OnBeforeTunnelConnectRequest(object sender, TunnelConnectSessionEventArgs e)
{
string hostname = e.WebSession.Request.RequestUri.Host;
WriteToConsole("Tunnel to: " + hostname);
await WriteToConsole("Tunnel to: " + hostname);
if (hostname.Contains("dropbox.com"))
{
......@@ -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
private async Task OnRequest(object sender, SessionEventArgs e)
{
WriteToConsole("Active Client Connections:" + ((ProxyServer)sender).ClientConnectionCount);
WriteToConsole(e.WebSession.Request.Url);
await WriteToConsole("Active Client Connections:" + ((ProxyServer)sender).ClientConnectionCount);
await WriteToConsole(e.WebSession.Request.Url);
// store it in the UserData property
// It can be a simple integer, Guid, or any type
......@@ -187,19 +196,19 @@ namespace Titanium.Web.Proxy.Examples.Basic
}
// Modify response
private void MultipartRequestPartSent(object sender, MultipartRequestPartSentEventArgs e)
private async Task MultipartRequestPartSent(object sender, MultipartRequestPartSentEventArgs e)
{
var session = (SessionEventArgs)sender;
WriteToConsole("Multipart form data headers:");
await WriteToConsole("Multipart form data headers:");
foreach (var header in e.Headers)
{
WriteToConsole(header.ToString());
await WriteToConsole(header.ToString());
}
}
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);
......@@ -271,12 +280,18 @@ namespace Titanium.Web.Proxy.Examples.Basic
return Task.FromResult(0);
}
private void WriteToConsole(string message)
private async Task WriteToConsole(string message)
{
lock (lockObj)
await @lock.WaitAsync();
try
{
Console.WriteLine(message);
}
finally
{
@lock.Release();
}
}
///// <summary>
......
......@@ -51,8 +51,8 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="StreamExtended, Version=1.0.164.0, Culture=neutral, PublicKeyToken=bbfa0f1d54f50043, processorArchitecture=MSIL">
<HintPath>..\..\packages\StreamExtended.1.0.164\lib\net45\StreamExtended.dll</HintPath>
<Reference Include="StreamExtended, Version=1.0.179.0, Culture=neutral, PublicKeyToken=bbfa0f1d54f50043, processorArchitecture=MSIL">
<HintPath>..\..\packages\StreamExtended.1.0.179\lib\net45\StreamExtended.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Data" />
......
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="StreamExtended" version="1.0.164" targetFramework="net45" />
<package id="StreamExtended" version="1.0.179" targetFramework="net45" />
</packages>
\ No newline at end of file
Doneness:
- [ ] Build is okay - I made sure that this change is building successfully.
- [ ] No Bugs - I made sure that this change is working properly as expected. It 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
### Features
* Multithreaded & fully asynchronous proxy
* Supports HTTP(S) and most features of HTTP 1.1
* Supports redirect/block/update requests and modifying responses
* Safely relays Web Socket requests over HTTP
* Supports mutual SSL authentication
* Supports proxy authentication & automatic proxy detection
* Multithreaded & fully asynchronous proxy employing server connection pooling, certificate cache & buffer pooling
* View/modify/redirect/block requests & responses
* Supports mutual SSL authentication, proxy authentication & automatic upstream proxy detection
* Kerberos/NTLM authentication over HTTP protocols for windows domain
### Usage
......@@ -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)
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)
......
......@@ -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/=PrivateStaticFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticReadonly/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=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: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>
......
......@@ -2,23 +2,25 @@
using System.Globalization;
using System.IO;
using System.Threading.Tasks;
using StreamExtended.Helpers;
using StreamExtended;
using StreamExtended.Network;
namespace Titanium.Web.Proxy.EventArguments
{
internal class LimitedStream : Stream
{
private readonly IBufferPool bufferPool;
private readonly ICustomStreamReader baseStream;
private readonly bool isChunked;
private long bytesRemaining;
private bool readChunkTrail;
internal LimitedStream(ICustomStreamReader baseStream, bool isChunked,
internal LimitedStream(ICustomStreamReader baseStream, IBufferPool bufferPool, bool isChunked,
long contentLength)
{
{
this.baseStream = baseStream;
this.bufferPool = bufferPool;
this.isChunked = isChunked;
bytesRemaining = isChunked
? 0
......@@ -41,7 +43,7 @@ namespace Titanium.Web.Proxy.EventArguments
set => throw new NotSupportedException();
}
private void GetNextChunk()
private void getNextChunk()
{
if (readChunkTrail)
{
......@@ -96,7 +98,7 @@ namespace Titanium.Web.Proxy.EventArguments
{
if (isChunked)
{
GetNextChunk();
getNextChunk();
}
else
{
......@@ -125,7 +127,7 @@ namespace Titanium.Web.Proxy.EventArguments
{
if (bytesRemaining != -1)
{
var buffer = BufferPool.GetBuffer(baseStream.BufferSize);
var buffer = bufferPool.GetBuffer(baseStream.BufferSize);
try
{
int res = await ReadAsync(buffer, 0, buffer.Length);
......@@ -136,7 +138,7 @@ namespace Titanium.Web.Proxy.EventArguments
}
finally
{
BufferPool.ReturnBuffer(buffer);
bufferPool.ReturnBuffer(buffer);
}
}
}
......
using System;
using System.Net;
using System.Threading;
using StreamExtended;
using StreamExtended.Network;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http;
......@@ -17,34 +18,32 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary>
public abstract class SessionEventArgsBase : EventArgs, IDisposable
{
/// <summary>
/// Size of Buffers used by this object
/// </summary>
protected readonly int BufferSize;
internal readonly CancellationTokenSource CancellationTokenSource;
protected readonly ExceptionHandler ExceptionFunc;
protected readonly int bufferSize;
protected readonly IBufferPool bufferPool;
protected readonly ExceptionHandler exceptionFunc;
/// <summary>
/// Initializes a new instance of the <see cref="SessionEventArgsBase" /> class.
/// </summary>
internal SessionEventArgsBase(int bufferSize, ProxyEndPoint endPoint,
CancellationTokenSource cancellationTokenSource, ExceptionHandler exceptionFunc)
: this(bufferSize, endPoint, cancellationTokenSource, null, exceptionFunc)
private SessionEventArgsBase(ProxyServer server, ProxyEndPoint endPoint,
CancellationTokenSource cancellationTokenSource)
{
bufferSize = server.BufferSize;
bufferPool = server.BufferPool;
exceptionFunc = server.ExceptionFunc;
}
protected SessionEventArgsBase(int bufferSize, ProxyEndPoint endPoint,
protected SessionEventArgsBase(ProxyServer server, ProxyEndPoint endPoint,
CancellationTokenSource cancellationTokenSource,
Request request, ExceptionHandler exceptionFunc)
Request request) : this(server, endPoint, cancellationTokenSource)
{
BufferSize = bufferSize;
ExceptionFunc = exceptionFunc;
CancellationTokenSource = cancellationTokenSource;
ProxyClient = new ProxyClient();
WebSession = new HttpWebClient(bufferSize, request);
WebSession = new HttpWebClient(request);
LocalEndPoint = endPoint;
WebSession.ProcessId = new Lazy<int>(() =>
......@@ -151,7 +150,7 @@ namespace Titanium.Web.Proxy.EventArguments
}
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
}
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
{
private bool? isHttpsConnect;
internal TunnelConnectSessionEventArgs(int bufferSize, ProxyEndPoint endPoint, ConnectRequest connectRequest,
CancellationTokenSource cancellationTokenSource, ExceptionHandler exceptionFunc)
: base(bufferSize, endPoint, cancellationTokenSource, connectRequest, exceptionFunc)
internal TunnelConnectSessionEventArgs(ProxyServer server, ProxyEndPoint endPoint, ConnectRequest connectRequest,
CancellationTokenSource cancellationTokenSource)
: base(server, endPoint, cancellationTokenSource, 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
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)
{
try
......
......@@ -2,7 +2,7 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using StreamExtended.Helpers;
using StreamExtended;
namespace Titanium.Web.Proxy.Extensions
{
......@@ -19,9 +19,9 @@ namespace Titanium.Web.Proxy.Extensions
/// <param name="onCopy"></param>
/// <param name="bufferSize"></param>
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>
......@@ -33,9 +33,9 @@ namespace Titanium.Web.Proxy.Extensions
/// <param name="bufferSize"></param>
/// <param name="cancellationToken"></param>
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
{
while (!cancellationToken.IsCancellationRequested)
......@@ -43,7 +43,7 @@ namespace Titanium.Web.Proxy.Extensions
// cancellation is not working on Socket ReadAsync
// https://github.com/dotnet/corefx/issues/15033
int num = await input.ReadAsync(buffer, 0, buffer.Length, CancellationToken.None)
.WithCancellation(cancellationToken);
.withCancellation(cancellationToken);
int bytesRead;
if ((bytesRead = num) != 0 && !cancellationToken.IsCancellationRequested)
{
......@@ -58,11 +58,11 @@ namespace Titanium.Web.Proxy.Extensions
}
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>();
using (cancellationToken.Register(s => ((TaskCompletionSource<bool>)s).TrySetResult(true), tcs))
......
......@@ -122,7 +122,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <returns>1: when CONNECT, 0: when valid HTTP method, -1: otherwise</returns>
internal static Task<int> IsConnectMethod(ICustomStreamReader clientStreamReader)
{
return StartsWith(clientStreamReader, "CONNECT");
return startsWith(clientStreamReader, "CONNECT");
}
/// <summary>
......@@ -132,7 +132,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <returns>1: when PRI, 0: when valid HTTP method, -1: otherwise</returns>
internal static Task<int> IsPriMethod(ICustomStreamReader clientStreamReader)
{
return StartsWith(clientStreamReader, "PRI");
return startsWith(clientStreamReader, "PRI");
}
/// <summary>
......@@ -143,7 +143,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <returns>
/// 1: when starts with the given string, 0: when valid HTTP method, -1: otherwise
/// </returns>
private static async Task<int> StartsWith(ICustomStreamReader clientStreamReader, string expectedStart)
private static async Task<int> startsWith(ICustomStreamReader clientStreamReader, string expectedStart)
{
bool isExpected = true;
int legthToCheck = 10;
......
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using StreamExtended;
using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.Helpers
{
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 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using StreamExtended;
using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.Helpers
{
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;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using StreamExtended.Helpers;
using StreamExtended;
using StreamExtended.Network;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Shared;
......@@ -14,6 +14,7 @@ namespace Titanium.Web.Proxy.Helpers
internal class HttpWriter : ICustomStreamWriter
{
private readonly Stream stream;
private readonly IBufferPool bufferPool;
private static readonly byte[] newLine = ProxyConstants.NewLine;
......@@ -21,10 +22,11 @@ namespace Titanium.Web.Proxy.Helpers
private readonly char[] charBuffer;
internal HttpWriter(Stream stream, int bufferSize)
internal HttpWriter(Stream stream, IBufferPool bufferPool, int bufferSize)
{
BufferSize = bufferSize;
this.stream = stream;
this.bufferPool = bufferPool;
// ASCII encoder max byte count is char count + 1
charBuffer = new char[BufferSize - 1];
......@@ -44,10 +46,10 @@ namespace Titanium.Web.Proxy.Helpers
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 charCount = value.Length;
......@@ -55,7 +57,7 @@ namespace Titanium.Web.Proxy.Helpers
{
value.CopyTo(0, charBuffer, 0, charCount);
var buffer = BufferPool.GetBuffer(BufferSize);
var buffer = bufferPool.GetBuffer(BufferSize);
try
{
int idx = encoder.GetBytes(charBuffer, 0, charCount, buffer, 0, true);
......@@ -69,7 +71,7 @@ namespace Titanium.Web.Proxy.Helpers
}
finally
{
BufferPool.ReturnBuffer(buffer);
bufferPool.ReturnBuffer(buffer);
}
}
else
......@@ -91,7 +93,7 @@ namespace Titanium.Web.Proxy.Helpers
internal Task WriteLineAsync(string value, CancellationToken cancellationToken = default)
{
return WriteAsyncInternal(value, true, cancellationToken);
return writeAsyncInternal(value, true, cancellationToken);
}
/// <summary>
......@@ -104,12 +106,15 @@ namespace Titanium.Web.Proxy.Helpers
internal async Task WriteHeadersAsync(HeaderCollection headers, bool flush = true,
CancellationToken cancellationToken = default)
{
var headerBuilder = new StringBuilder();
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)
{
await stream.FlushAsync(cancellationToken);
......@@ -146,7 +151,7 @@ namespace Titanium.Web.Proxy.Helpers
{
if (isChunked)
{
return WriteBodyChunkedAsync(data, cancellationToken);
return writeBodyChunkedAsync(data, cancellationToken);
}
return WriteAsync(data, cancellationToken: cancellationToken);
......@@ -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
if (isChunked)
{
return CopyBodyChunkedAsync(streamReader, onCopy, cancellationToken);
return copyBodyChunkedAsync(streamReader, onCopy, cancellationToken);
}
// http 1.0 or the stream reader limits the stream
......@@ -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
return CopyBytesFromStream(streamReader, contentLength, onCopy, cancellationToken);
return copyBytesFromStream(streamReader, contentLength, onCopy, cancellationToken);
}
/// <summary>
......@@ -187,7 +192,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="data"></param>
/// <param name="cancellationToken"></param>
/// <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"));
......@@ -207,7 +212,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="onCopy"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
private async Task CopyBodyChunkedAsync(ICustomStreamReader reader, Action<byte[], int, int> onCopy,
private async Task copyBodyChunkedAsync(ICustomStreamReader reader, Action<byte[], int, int> onCopy,
CancellationToken cancellationToken)
{
while (true)
......@@ -225,7 +230,7 @@ namespace Titanium.Web.Proxy.Helpers
if (chunkSize != 0)
{
await CopyBytesFromStream(reader, chunkSize, onCopy, cancellationToken);
await copyBytesFromStream(reader, chunkSize, onCopy, cancellationToken);
}
await WriteLineAsync(cancellationToken);
......@@ -248,10 +253,10 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="onCopy"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
private async Task CopyBytesFromStream(ICustomStreamReader reader, long count, Action<byte[], int, int> onCopy,
private async Task copyBytesFromStream(ICustomStreamReader reader, long count, Action<byte[], int, int> onCopy,
CancellationToken cancellationToken)
{
var buffer = BufferPool.GetBuffer(BufferSize);
var buffer = bufferPool.GetBuffer(BufferSize);
try
{
......@@ -280,7 +285,7 @@ namespace Titanium.Web.Proxy.Helpers
}
finally
{
BufferPool.ReturnBuffer(buffer);
bufferPool.ReturnBuffer(buffer);
}
}
......
......@@ -21,44 +21,52 @@ namespace Titanium.Web.Proxy.Helpers
// get local IP addresses
var localIPs = Dns.GetHostAddresses(Dns.GetHostName());
// test if any host IP equals to any local IP or to localhost
return localIPs.Contains(address);
}
internal static bool IsLocalIpAddress(string hostName)
{
bool isLocalhost = false;
hostName = hostName.ToLower();
var localhost = Dns.GetHostEntry("127.0.0.1");
if (hostName == localhost.HostName)
if (hostName == "127.0.0.1"
|| hostName == "localhost")
{
var hostEntry = Dns.GetHostEntry(hostName);
isLocalhost = hostEntry.AddressList.Any(IPAddress.IsLoopback);
return true;
}
if (!isLocalhost)
var localhostDnsName = Dns.GetHostName().ToLower();
//if hostname matches current machine DNS name
if (hostName == localhostDnsName)
{
return true;
}
var isLocalhost = false;
IPHostEntry hostEntry = null;
//check if parsable to an IP Address
if (IPAddress.TryParse(hostName, out var ipAddress))
{
localhost = Dns.GetHostEntry(Dns.GetHostName());
hostEntry = Dns.GetHostEntry(localhostDnsName);
isLocalhost = hostEntry.AddressList.Any(x => x.Equals(ipAddress));
}
if (IPAddress.TryParse(hostName, out var ipAddress))
if (!isLocalhost)
{
try
{
isLocalhost = localhost.AddressList.Any(x => x.Equals(ipAddress));
hostEntry = Dns.GetHostEntry(hostName);
isLocalhost = hostEntry.AddressList.Any(x => hostEntry.AddressList.Any(x.Equals));
}
if (!isLocalhost)
catch (SocketException)
{
try
{
var hostEntry = Dns.GetHostEntry(hostName);
isLocalhost = localhost.AddressList.Any(x => hostEntry.AddressList.Any(x.Equals));
}
catch (SocketException)
{
}
}
}
return isLocalhost;
}
}
......
......@@ -39,7 +39,7 @@ namespace Titanium.Web.Proxy.Helpers
}
else
{
overrides2.Add(BypassStringEscape(overrideHost));
overrides2.Add(bypassStringEscape(overrideHost));
}
}
......@@ -70,7 +70,7 @@ namespace Titanium.Web.Proxy.Helpers
internal string[] BypassList { get; }
private static string BypassStringEscape(string rawString)
private static string bypassStringEscape(string rawString)
{
var match =
new Regex("^(?<scheme>.*://)?(?<host>[^:]*)(?<port>:[0-9]{1,5})?$",
......@@ -91,9 +91,9 @@ namespace Titanium.Web.Proxy.Helpers
empty2 = string.Empty;
}
string str1 = ConvertRegexReservedChars(empty1);
string str2 = ConvertRegexReservedChars(rawString1);
string str3 = ConvertRegexReservedChars(empty2);
string str1 = convertRegexReservedChars(empty1);
string str2 = convertRegexReservedChars(rawString1);
string str3 = convertRegexReservedChars(empty2);
if (str1 == string.Empty)
{
str1 = "(?:.*://)?";
......@@ -107,7 +107,7 @@ namespace Titanium.Web.Proxy.Helpers
return "^" + str1 + str2 + str3 + "$";
}
private static string ConvertRegexReservedChars(string rawString)
private static string convertRegexReservedChars(string rawString)
{
if (rawString.Length == 0)
{
......@@ -171,11 +171,11 @@ namespace Titanium.Web.Proxy.Helpers
if (proxyValues.Length > 0)
{
result.AddRange(proxyValues.Select(ParseProxyValue).Where(parsedValue => parsedValue != null));
result.AddRange(proxyValues.Select(parseProxyValue).Where(parsedValue => parsedValue != null));
}
else
{
var parsedValue = ParseProxyValue(proxyServerValues);
var parsedValue = parseProxyValue(proxyServerValues);
if (parsedValue != null)
{
result.Add(parsedValue);
......@@ -190,7 +190,7 @@ namespace Titanium.Web.Proxy.Helpers
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
private static HttpSystemProxyValue ParseProxyValue(string value)
private static HttpSystemProxyValue parseProxyValue(string value)
{
string tmp = Regex.Replace(value, @"\s+", " ").Trim();
......
......@@ -84,8 +84,8 @@ namespace Titanium.Web.Proxy.Helpers
if (reg != null)
{
SaveOriginalProxyConfiguration(reg);
PrepareRegistry(reg);
saveOriginalProxyConfiguration(reg);
prepareRegistry(reg);
string exisitingContent = reg.GetValue(regProxyServer) as string;
var existingSystemProxyValues = ProxyInfo.GetSystemProxyValues(exisitingContent);
......@@ -115,7 +115,7 @@ namespace Titanium.Web.Proxy.Helpers
reg.SetValue(regProxyServer,
string.Join(";", existingSystemProxyValues.Select(x => x.ToString()).ToArray()));
Refresh();
refresh();
}
}
......@@ -129,7 +129,7 @@ namespace Titanium.Web.Proxy.Helpers
{
if (saveOriginalConfig)
{
SaveOriginalProxyConfiguration(reg);
saveOriginalProxyConfiguration(reg);
}
if (reg.GetValue(regProxyServer) != null)
......@@ -152,7 +152,7 @@ namespace Titanium.Web.Proxy.Helpers
}
}
Refresh();
refresh();
}
}
......@@ -165,12 +165,12 @@ namespace Titanium.Web.Proxy.Helpers
if (reg != null)
{
SaveOriginalProxyConfiguration(reg);
saveOriginalProxyConfiguration(reg);
reg.SetValue(regProxyEnable, 0);
reg.SetValue(regProxyServer, string.Empty);
Refresh();
refresh();
}
}
......@@ -180,9 +180,9 @@ namespace Titanium.Web.Proxy.Helpers
if (reg != null)
{
SaveOriginalProxyConfiguration(reg);
saveOriginalProxyConfiguration(reg);
reg.SetValue(regAutoConfigUrl, url);
Refresh();
refresh();
}
}
......@@ -192,9 +192,9 @@ namespace Titanium.Web.Proxy.Helpers
if (reg != null)
{
SaveOriginalProxyConfiguration(reg);
saveOriginalProxyConfiguration(reg);
reg.SetValue(regProxyOverride, proxyOverride);
Refresh();
refresh();
}
}
......@@ -247,7 +247,7 @@ namespace Titanium.Web.Proxy.Helpers
}
originalValues = null;
Refresh();
refresh();
}
}
......@@ -257,13 +257,13 @@ namespace Titanium.Web.Proxy.Helpers
if (reg != null)
{
return GetProxyInfoFromRegistry(reg);
return getProxyInfoFromRegistry(reg);
}
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?,
reg.GetValue(regProxyServer) as string,
......@@ -272,21 +272,21 @@ namespace Titanium.Web.Proxy.Helpers
return pi;
}
private void SaveOriginalProxyConfiguration(RegistryKey reg)
private void saveOriginalProxyConfiguration(RegistryKey reg)
{
if (originalValues != null)
{
return;
}
originalValues = GetProxyInfoFromRegistry(reg);
originalValues = getProxyInfoFromRegistry(reg);
}
/// <summary>
/// Prepares the proxy server registry (create empty values if they don't exist)
/// </summary>
/// <param name="reg"></param>
private static void PrepareRegistry(RegistryKey reg)
private static void prepareRegistry(RegistryKey reg)
{
if (reg.GetValue(regProxyEnable) == null)
{
......@@ -302,7 +302,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <summary>
/// Refresh the settings so that the system know about a change in proxy setting
/// </summary>
private void Refresh()
private static void refresh()
{
NativeMethods.InternetSetOption(IntPtr.Zero, InternetOptionSettingsChanged, IntPtr.Zero, 0);
NativeMethods.InternetSetOption(IntPtr.Zero, InternetOptionRefresh, IntPtr.Zero, 0);
......
using System;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using StreamExtended.Helpers;
using StreamExtended;
using Titanium.Web.Proxy.Extensions;
namespace Titanium.Web.Proxy.Helpers
......@@ -39,7 +37,7 @@ namespace Titanium.Web.Proxy.Helpers
0) == 0)
{
int rowCount = *(int*)tcpTable;
uint portInNetworkByteOrder = ToNetworkByteOrder((uint)localPort);
uint portInNetworkByteOrder = toNetworkByteOrder((uint)localPort);
if (ipVersion == IpVersion.Ipv4)
{
......@@ -90,7 +88,7 @@ namespace Titanium.Web.Proxy.Helpers
/// </summary>
/// <param name="port"></param>
/// <returns></returns>
private static uint ToNetworkByteOrder(uint port)
private static uint toNetworkByteOrder(uint port)
{
return ((port >> 8) & 0x00FF00FFu) | ((port << 8) & 0xFF00FF00u);
}
......@@ -109,7 +107,8 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="cancellationTokenSource"></param>
/// <param name="exceptionFunc"></param>
/// <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,
CancellationTokenSource cancellationTokenSource,
ExceptionHandler exceptionFunc)
......@@ -118,23 +117,23 @@ namespace Titanium.Web.Proxy.Helpers
cancellationTokenSource.Token.Register(() => taskCompletionSource.TrySetResult(true));
// Now async relay all server=>client & client=>server data
var clientBuffer = BufferPool.GetBuffer(bufferSize);
var serverBuffer = BufferPool.GetBuffer(bufferSize);
var clientBuffer = bufferPool.GetBuffer(bufferSize);
var serverBuffer = bufferPool.GetBuffer(bufferSize);
try
{
BeginRead(clientStream, serverStream, clientBuffer, onDataSend, cancellationTokenSource, exceptionFunc);
BeginRead(serverStream, clientStream, serverBuffer, onDataReceive, cancellationTokenSource,
beginRead(clientStream, serverStream, clientBuffer, onDataSend, cancellationTokenSource, exceptionFunc);
beginRead(serverStream, clientStream, serverBuffer, onDataReceive, cancellationTokenSource,
exceptionFunc);
await taskCompletionSource.Task;
}
finally
{
BufferPool.ReturnBuffer(clientBuffer);
BufferPool.ReturnBuffer(serverBuffer);
bufferPool.ReturnBuffer(clientBuffer);
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,
ExceptionHandler exceptionFunc)
{
......@@ -174,7 +173,7 @@ namespace Titanium.Web.Proxy.Helpers
try
{
outputStream.EndWrite(ar2);
BeginRead(inputStream, outputStream, buffer, onCopy, cancellationTokenSource,
beginRead(inputStream, outputStream, buffer, onCopy, cancellationTokenSource,
exceptionFunc);
}
catch (IOException ex)
......@@ -214,16 +213,17 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="cancellationTokenSource"></param>
/// <param name="exceptionFunc"></param>
/// <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,
CancellationTokenSource cancellationTokenSource,
ExceptionHandler exceptionFunc)
{
// Now async relay all server=>client & client=>server data
var sendRelay =
clientStream.CopyToAsync(serverStream, onDataSend, bufferSize, cancellationTokenSource.Token);
clientStream.CopyToAsync(serverStream, onDataSend, bufferPool, bufferSize, cancellationTokenSource.Token);
var receiveRelay =
serverStream.CopyToAsync(clientStream, onDataReceive, bufferSize, cancellationTokenSource.Token);
serverStream.CopyToAsync(clientStream, onDataReceive, bufferPool, bufferSize, cancellationTokenSource.Token);
await Task.WhenAny(sendRelay, receiveRelay);
cancellationTokenSource.Cancel();
......@@ -244,13 +244,14 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="cancellationTokenSource"></param>
/// <param name="exceptionFunc"></param>
/// <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,
CancellationTokenSource cancellationTokenSource,
ExceptionHandler exceptionFunc)
{
// todo: fix APM mode
return SendRawTap(clientStream, serverStream, bufferSize, onDataSend, onDataReceive,
return sendRawTap(clientStream, serverStream, bufferPool, bufferSize, onDataSend, onDataReceive,
cancellationTokenSource,
exceptionFunc);
}
......
......@@ -19,7 +19,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
session = NativeMethods.WinHttp.WinHttpOpen(null, NativeMethods.WinHttp.AccessType.NoProxy, null, null, 0);
if (session == null || session.IsInvalid)
{
int lastWin32Error = GetLastWin32Error();
int lastWin32Error = getLastWin32Error();
}
else
{
......@@ -30,7 +30,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
return;
}
int lastWin32Error = GetLastWin32Error();
int lastWin32Error = getLastWin32Error();
}
}
......@@ -50,7 +50,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
public void Dispose()
{
Dispose(true);
dispose(true);
}
public bool GetAutoProxies(Uri destination, out IList<string> proxyList)
......@@ -65,8 +65,8 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
var errorCode = NativeMethods.WinHttp.ErrorCodes.AudodetectionFailed;
if (AutomaticallyDetectSettings && !autoDetectFailed)
{
errorCode = (NativeMethods.WinHttp.ErrorCodes)GetAutoProxies(destination, null, out proxyListString);
autoDetectFailed = IsErrorFatalForAutoDetect(errorCode);
errorCode = (NativeMethods.WinHttp.ErrorCodes)getAutoProxies(destination, null, out proxyListString);
autoDetectFailed = isErrorFatalForAutoDetect(errorCode);
if (errorCode == NativeMethods.WinHttp.ErrorCodes.UnrecognizedScheme)
{
state = AutoWebProxyState.UnrecognizedScheme;
......@@ -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);
}
state = GetStateFromErrorCode(errorCode);
state = getStateFromErrorCode(errorCode);
if (state != AutoWebProxyState.Completed)
{
return false;
......@@ -88,7 +88,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
if (!string.IsNullOrEmpty(proxyListString))
{
proxyListString = RemoveWhitespaces(proxyListString);
proxyListString = removeWhitespaces(proxyListString);
proxyList = proxyListString.Split(';');
}
......@@ -149,7 +149,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
public void LoadFromIE()
{
var pi = GetProxyInfo();
var pi = getProxyInfo();
ProxyInfo = pi;
AutomaticallyDetectSettings = pi.AutoDetect == true;
AutomaticConfigurationScript = pi.AutoConfigUrl == null ? null : new Uri(pi.AutoConfigUrl);
......@@ -158,7 +158,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
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();
RuntimeHelpers.PrepareConstrainedRegions();
......@@ -200,7 +200,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
autoDetectFailed = false;
}
private void Dispose(bool disposing)
private void dispose(bool disposing)
{
if (!disposing || session == null || session.IsInvalid)
{
......@@ -210,7 +210,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
session.Close();
}
private int GetAutoProxies(Uri destination, Uri scriptLocation, out string proxyListString)
private int getAutoProxies(Uri destination, Uri scriptLocation, out string proxyListString)
{
int num = 0;
var autoProxyOptions = new NativeMethods.WinHttp.WINHTTP_AUTOPROXY_OPTIONS();
......@@ -229,16 +229,16 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
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)
{
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
return num;
}
private bool WinHttpGetProxyForUrl(string destination,
private bool winHttpGetProxyForUrl(string destination,
ref NativeMethods.WinHttp.WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions, out string proxyListString)
{
proxyListString = null;
......@@ -271,7 +271,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
return flag;
}
private static int GetLastWin32Error()
private static int getLastWin32Error()
{
int lastWin32Error = Marshal.GetLastWin32Error();
if (lastWin32Error == 8)
......@@ -282,7 +282,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
return lastWin32Error;
}
private static bool IsRecoverableAutoProxyError(NativeMethods.WinHttp.ErrorCodes errorCode)
private static bool isRecoverableAutoProxyError(NativeMethods.WinHttp.ErrorCodes errorCode)
{
switch (errorCode)
{
......@@ -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)
{
......@@ -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();
foreach (char c in value)
......@@ -338,7 +338,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
return stringBuilder.ToString();
}
private static bool IsErrorFatalForAutoDetect(NativeMethods.WinHttp.ErrorCodes errorCode)
private static bool isErrorFatalForAutoDetect(NativeMethods.WinHttp.ErrorCodes errorCode)
{
switch (errorCode)
{
......
......@@ -24,7 +24,7 @@ namespace Titanium.Web.Proxy.Http
/// </summary>
/// <param name="buffer"></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];
Buffer.BlockCopy(buffer, 0, newBuffer, 0, buffer.Length);
......
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network.Tcp;
......@@ -15,12 +16,9 @@ namespace Titanium.Web.Proxy.Http
/// </summary>
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();
Response = response ?? new Response();
}
......@@ -30,6 +28,11 @@ namespace Titanium.Web.Proxy.Http
/// </summary>
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>
/// Stores internal data for the session.
/// </summary>
......@@ -98,16 +101,16 @@ namespace Titanium.Web.Proxy.Http
await writer.WriteLineAsync(Request.CreateRequestLine(Request.Method,
useUpstreamProxy || isTransparent ? Request.OriginalUrl : Request.RequestUri.PathAndQuery,
Request.HttpVersion), cancellationToken);
var headerBuilder = new StringBuilder();
// Send Authentication to Upstream proxy if needed
if (!isTransparent && upstreamProxy != null
&& ServerConnection.IsHttps == false
&& !string.IsNullOrEmpty(upstreamProxy.UserName)
&& upstreamProxy.Password != null)
{
await HttpHeader.ProxyConnectionKeepAlive.WriteToStreamAsync(writer, cancellationToken);
await HttpHeader.GetProxyAuthorizationHeader(upstreamProxy.UserName, upstreamProxy.Password)
.WriteToStreamAsync(writer, cancellationToken);
headerBuilder.AppendLine(HttpHeader.ProxyConnectionKeepAlive.ToString());
headerBuilder.AppendLine(HttpHeader.GetProxyAuthorizationHeader(upstreamProxy.UserName, upstreamProxy.Password).ToString());
}
// write request headers
......@@ -115,17 +118,30 @@ namespace Titanium.Web.Proxy.Http
{
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 (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,
out string responseStatusDescription);
......@@ -159,10 +175,18 @@ namespace Titanium.Web.Proxy.Http
return;
}
string httpStatus = await ServerConnection.Stream.ReadLineAsync(cancellationToken);
if (httpStatus == null)
string httpStatus;
try
{
throw new IOException();
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.");
}
if (httpStatus == string.Empty)
......@@ -217,6 +241,9 @@ namespace Titanium.Web.Proxy.Http
ConnectRequest?.FinishSession();
Request?.FinishSession();
Response?.FinishSession();
Data.Clear();
UserData = null;
}
}
}
......@@ -99,7 +99,8 @@ namespace Titanium.Web.Proxy.Http
public string Url => RequestUri.OriginalString;
/// <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>
internal bool CancelRequest { get; set; }
......@@ -199,7 +200,7 @@ namespace Titanium.Web.Proxy.Http
// Find the request Verb
httpMethod = httpCmdSplit[0];
if (!IsAllUpper(httpMethod))
if (!isAllUpper(httpMethod))
{
httpMethod = httpMethod.ToUpper();
}
......@@ -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++)
{
......@@ -232,5 +233,6 @@ namespace Titanium.Web.Proxy.Http
return true;
}
}
}
......@@ -23,10 +23,35 @@ namespace Titanium.Web.Proxy.Http
private string bodyString;
/// <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>
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>
/// Keeps the body data after the session is finished.
/// </summary>
......@@ -168,6 +193,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary>
/// 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>
internal bool Locked { get; set; }
......@@ -231,6 +257,31 @@ namespace Titanium.Web.Proxy.Http
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>
/// Finish the session
/// </summary>
......
......@@ -92,8 +92,6 @@ namespace Titanium.Web.Proxy.Http
}
}
internal bool TerminateResponse { get; set; }
/// <summary>
/// Is response 100-continue
/// </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
/// </summary>
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>
/// returns data in Hostname:port format.
/// </summary>
......@@ -77,5 +86,6 @@ namespace Titanium.Web.Proxy.Models
{
return $"{HostName}:{Port}";
}
}
}
using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.Models
......@@ -12,6 +9,13 @@ namespace Titanium.Web.Proxy.Models
/// </summary>
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 Version10 = new Version(1, 0);
......@@ -48,6 +52,13 @@ namespace Titanium.Web.Proxy.Models
/// </summary>
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>
/// Returns header as a valid header string.
/// </summary>
......@@ -63,12 +74,5 @@ namespace Titanium.Web.Proxy.Models
"Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes($"{userName}:{password}")));
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
/// <returns>X509Certificate2 instance.</returns>
public X509Certificate2 MakeCertificate(string sSubjectCn, bool isRoot, X509Certificate2 signingCert = null)
{
return MakeCertificateInternal(sSubjectCn, isRoot, true, signingCert);
return makeCertificateInternal(sSubjectCn, isRoot, true, signingCert);
}
/// <summary>
......@@ -65,7 +65,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
/// <param name="hostName">The host name</param>
/// <returns>X509Certificate2 instance.</returns>
/// <exception cref="PemException">Malformed sequence in RSA private key</exception>
private static X509Certificate2 GenerateCertificate(string hostName,
private static X509Certificate2 generateCertificate(string hostName,
string subjectName,
string issuerName, DateTime validFrom,
DateTime validTo, int keyStrength = 2048,
......@@ -145,8 +145,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
var x509Certificate = new X509Certificate2(certificate.GetEncoded());
x509Certificate.PrivateKey = DotNetUtilities.ToRSA(rsaparams);
#else
var x509Certificate = WithPrivateKey(certificate, rsaparams);
x509Certificate.FriendlyName = subjectName;
var x509Certificate = withPrivateKey(certificate, rsaparams);
#endif
if (!doNotSetFriendlyName)
......@@ -164,7 +163,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
return x509Certificate;
}
private static X509Certificate2 WithPrivateKey(X509Certificate certificate, AsymmetricKeyParameter privateKey)
private static X509Certificate2 withPrivateKey(X509Certificate certificate, AsymmetricKeyParameter privateKey)
{
const string password = "password";
var store = new Pkcs12Store();
......@@ -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
/// root.
/// </exception>
private X509Certificate2 MakeCertificateInternal(bool isRoot,
private X509Certificate2 makeCertificateInternal(bool isRoot,
string hostName, string subjectName,
DateTime validFrom, DateTime validTo, X509Certificate2 signingCertificate)
{
......@@ -207,11 +206,11 @@ namespace Titanium.Web.Proxy.Network.Certificate
if (isRoot)
{
return GenerateCertificate(null, subjectName, subjectName, validFrom, validTo);
return generateCertificate(null, subjectName, subjectName, validFrom, validTo);
}
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);
}
......@@ -224,7 +223,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
/// <param name="signingCert">The signing cert.</param>
/// <param name="cancellationToken">Task cancellation token</param>
/// <returns>X509Certificate2.</returns>
private X509Certificate2 MakeCertificateInternal(string subject, bool isRoot,
private X509Certificate2 makeCertificateInternal(string subject, bool isRoot,
bool switchToMtaIfNeeded, X509Certificate2 signingCert = null,
CancellationToken cancellationToken = default)
{
......@@ -238,7 +237,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
{
try
{
certificate = MakeCertificateInternal(subject, isRoot, false, signingCert);
certificate = makeCertificateInternal(subject, isRoot, false, signingCert);
}
catch (Exception ex)
{
......@@ -258,7 +257,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
}
#endif
return MakeCertificateInternal(isRoot, subject, $"CN={subject}",
return makeCertificateInternal(isRoot, subject, $"CN={subject}",
DateTime.UtcNow.AddDays(-certificateGraceDays), DateTime.UtcNow.AddDays(certificateValidDays),
isRoot ? null : signingCert);
}
......
......@@ -80,10 +80,10 @@ namespace Titanium.Web.Proxy.Network.Certificate
/// <returns></returns>
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,
X509Certificate2 signingCertificate)
{
......@@ -274,13 +274,13 @@ namespace Titanium.Web.Proxy.Network.Certificate
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,
CancellationToken cancellationToken = default)
{
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;
}
......@@ -301,7 +301,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
var graceTime = DateTime.Now.AddDays(graceDays);
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);
return certificate;
}
......
......@@ -3,6 +3,7 @@ using System;
using System.IO;
using System.Text;
using System.Threading;
using StreamExtended;
using StreamExtended.Network;
namespace Titanium.Web.Proxy.Network
......@@ -17,7 +18,8 @@ namespace Titanium.Web.Proxy.Network
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);
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.IO;
using System.Net;
#if NETCOREAPP2_1
using System.Net.Security;
#endif
using System.Net.Sockets;
using Titanium.Web.Proxy.Extensions;
......@@ -41,9 +43,8 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// </summary>
public void Dispose()
{
tcpClient.CloseSocket();
proxyServer.UpdateClientConnectionCount(false);
tcpClient.CloseSocket();
}
}
}
using System;
using System.Net;
#if NETCOREAPP2_1
using System.Net.Security;
#endif
using System.Net.Sockets;
using StreamExtended.Network;
using Titanium.Web.Proxy.Extensions;
......@@ -48,6 +50,11 @@ namespace Titanium.Web.Proxy.Network.Tcp
private readonly TcpClient tcpClient;
/// <summary>
/// The TcpClient.
/// </summary>
internal TcpClient TcpClient => tcpClient;
/// <summary>
/// Used to write lines to server
/// </summary>
......@@ -63,16 +70,24 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// </summary>
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>
/// Dispose.
/// </summary>
public void Dispose()
{
proxyServer.UpdateServerConnectionCount(false);
Stream?.Dispose();
tcpClient.CloseSocket();
proxyServer.UpdateServerConnectionCount(false);
}
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -14,7 +14,7 @@
<copyright>Copyright &#x00A9; Titanium. All rights reserved.</copyright>
<tags></tags>
<dependencies>
<dependency id="StreamExtended" version="1.0.164" />
<dependency id="StreamExtended" version="1.0.179" />
<dependency id="Portable.BouncyCastle" version="1.8.2" />
</dependencies>
</metadata>
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment