Unverified Commit 3d443dd7 authored by justcoding121's avatar justcoding121 Committed by GitHub

Merge pull request #435 from justcoding121/master

Connection pool 
parents d3ad2f8b 4485303b
...@@ -23,7 +23,7 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -23,7 +23,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
public ProxyTestController() public ProxyTestController()
{ {
proxyServer = new ProxyServer(); proxyServer = new ProxyServer();
proxyServer.EnableConnectionPool = true;
// generate root certificate without storing it in file system // generate root certificate without storing it in file system
//proxyServer.CertificateManager.CreateRootCertificate(false); //proxyServer.CertificateManager.CreateRootCertificate(false);
......
...@@ -51,8 +51,8 @@ ...@@ -51,8 +51,8 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="StreamExtended, Version=1.0.164.0, Culture=neutral, PublicKeyToken=bbfa0f1d54f50043, processorArchitecture=MSIL"> <Reference Include="StreamExtended, Version=1.0.175.0, Culture=neutral, PublicKeyToken=bbfa0f1d54f50043, processorArchitecture=MSIL">
<HintPath>..\..\packages\StreamExtended.1.0.164\lib\net45\StreamExtended.dll</HintPath> <HintPath>..\..\packages\StreamExtended.1.0.175-beta\lib\net45\StreamExtended.dll</HintPath>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
......
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="StreamExtended" version="1.0.164" targetFramework="net45" /> <package id="StreamExtended" version="1.0.175-beta" targetFramework="net45" />
</packages> </packages>
\ No newline at end of file
...@@ -2,23 +2,25 @@ ...@@ -2,23 +2,25 @@
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended.Helpers; using StreamExtended;
using StreamExtended.Network; using StreamExtended.Network;
namespace Titanium.Web.Proxy.EventArguments namespace Titanium.Web.Proxy.EventArguments
{ {
internal class LimitedStream : Stream internal class LimitedStream : Stream
{ {
private readonly IBufferPool bufferPool;
private readonly ICustomStreamReader baseStream; private readonly ICustomStreamReader baseStream;
private readonly bool isChunked; private readonly bool isChunked;
private long bytesRemaining; private long bytesRemaining;
private bool readChunkTrail; private bool readChunkTrail;
internal LimitedStream(ICustomStreamReader baseStream, bool isChunked, internal LimitedStream(ICustomStreamReader baseStream, IBufferPool bufferPool, bool isChunked,
long contentLength) long contentLength)
{ {
this.baseStream = baseStream; this.baseStream = baseStream;
this.bufferPool = bufferPool;
this.isChunked = isChunked; this.isChunked = isChunked;
bytesRemaining = isChunked bytesRemaining = isChunked
? 0 ? 0
...@@ -41,7 +43,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -41,7 +43,7 @@ namespace Titanium.Web.Proxy.EventArguments
set => throw new NotSupportedException(); set => throw new NotSupportedException();
} }
private void GetNextChunk() private void getNextChunk()
{ {
if (readChunkTrail) if (readChunkTrail)
{ {
...@@ -96,7 +98,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -96,7 +98,7 @@ namespace Titanium.Web.Proxy.EventArguments
{ {
if (isChunked) if (isChunked)
{ {
GetNextChunk(); getNextChunk();
} }
else else
{ {
...@@ -125,7 +127,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -125,7 +127,7 @@ namespace Titanium.Web.Proxy.EventArguments
{ {
if (bytesRemaining != -1) if (bytesRemaining != -1)
{ {
var buffer = BufferPool.GetBuffer(baseStream.BufferSize); var buffer = bufferPool.GetBuffer(baseStream.BufferSize);
try try
{ {
int res = await ReadAsync(buffer, 0, buffer.Length); int res = await ReadAsync(buffer, 0, buffer.Length);
...@@ -136,7 +138,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -136,7 +138,7 @@ namespace Titanium.Web.Proxy.EventArguments
} }
finally finally
{ {
BufferPool.ReturnBuffer(buffer); bufferPool.ReturnBuffer(buffer);
} }
} }
} }
......
...@@ -4,14 +4,12 @@ using System.IO; ...@@ -4,14 +4,12 @@ using System.IO;
using System.Net; using System.Net;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended.Helpers;
using StreamExtended.Network; using StreamExtended.Network;
using Titanium.Web.Proxy.Compression; using Titanium.Web.Proxy.Compression;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Http.Responses; using Titanium.Web.Proxy.Http.Responses;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network;
namespace Titanium.Web.Proxy.EventArguments namespace Titanium.Web.Proxy.EventArguments
{ {
...@@ -33,15 +31,15 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -33,15 +31,15 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary> /// <summary>
/// Constructor to initialize the proxy /// Constructor to initialize the proxy
/// </summary> /// </summary>
internal SessionEventArgs(int bufferSize, ProxyEndPoint endPoint, internal SessionEventArgs(ProxyServer server, ProxyEndPoint endPoint,
CancellationTokenSource cancellationTokenSource, ExceptionHandler exceptionFunc) CancellationTokenSource cancellationTokenSource)
: this(bufferSize, endPoint, null, cancellationTokenSource, exceptionFunc) : this(server, endPoint, null, cancellationTokenSource)
{ {
} }
protected SessionEventArgs(int bufferSize, ProxyEndPoint endPoint, protected SessionEventArgs(ProxyServer server, ProxyEndPoint endPoint,
Request request, CancellationTokenSource cancellationTokenSource, ExceptionHandler exceptionFunc) Request request, CancellationTokenSource cancellationTokenSource)
: base(bufferSize, endPoint, cancellationTokenSource, request, exceptionFunc) : base(server, endPoint, cancellationTokenSource, request)
{ {
} }
...@@ -69,12 +67,12 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -69,12 +67,12 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary> /// </summary>
public event EventHandler<MultipartRequestPartSentEventArgs> MultipartRequestPartSent; public event EventHandler<MultipartRequestPartSentEventArgs> MultipartRequestPartSent;
private ICustomStreamReader GetStreamReader(bool isRequest) private ICustomStreamReader getStreamReader(bool isRequest)
{ {
return isRequest ? ProxyClient.ClientStream : WebSession.ServerConnection.Stream; return isRequest ? ProxyClient.ClientStream : WebSession.ServerConnection.Stream;
} }
private HttpWriter GetStreamWriter(bool isRequest) private HttpWriter getStreamWriter(bool isRequest)
{ {
return isRequest ? (HttpWriter)ProxyClient.ClientStreamWriter : WebSession.ServerConnection.StreamWriter; return isRequest ? (HttpWriter)ProxyClient.ClientStreamWriter : WebSession.ServerConnection.StreamWriter;
} }
...@@ -82,7 +80,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -82,7 +80,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary> /// <summary>
/// Read request body content as bytes[] for current session /// Read request body content as bytes[] for current session
/// </summary> /// </summary>
private async Task ReadRequestBodyAsync(CancellationToken cancellationToken) private async Task readRequestBodyAsync(CancellationToken cancellationToken)
{ {
WebSession.Request.EnsureBodyAvailable(false); WebSession.Request.EnsureBodyAvailable(false);
...@@ -91,7 +89,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -91,7 +89,7 @@ namespace Titanium.Web.Proxy.EventArguments
// If not already read (not cached yet) // If not already read (not cached yet)
if (!request.IsBodyRead) if (!request.IsBodyRead)
{ {
var body = await ReadBodyAsync(true, cancellationToken); var body = await readBodyAsync(true, cancellationToken);
request.Body = body; request.Body = body;
// Now set the flag to true // Now set the flag to true
...@@ -119,14 +117,14 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -119,14 +117,14 @@ namespace Titanium.Web.Proxy.EventArguments
} }
catch (Exception ex) catch (Exception ex)
{ {
ExceptionFunc(new Exception("Exception thrown in user event", ex)); exceptionFunc(new Exception("Exception thrown in user event", ex));
} }
} }
/// <summary> /// <summary>
/// Read response body as byte[] for current response /// Read response body as byte[] for current response
/// </summary> /// </summary>
private async Task ReadResponseBodyAsync(CancellationToken cancellationToken) private async Task readResponseBodyAsync(CancellationToken cancellationToken)
{ {
if (!WebSession.Request.Locked) if (!WebSession.Request.Locked)
{ {
...@@ -142,7 +140,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -142,7 +140,7 @@ namespace Titanium.Web.Proxy.EventArguments
// If not already read (not cached yet) // If not already read (not cached yet)
if (!response.IsBodyRead) if (!response.IsBodyRead)
{ {
var body = await ReadBodyAsync(false, cancellationToken); var body = await readBodyAsync(false, cancellationToken);
response.Body = body; response.Body = body;
// Now set the flag to true // Now set the flag to true
...@@ -152,11 +150,11 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -152,11 +150,11 @@ namespace Titanium.Web.Proxy.EventArguments
} }
} }
private async Task<byte[]> ReadBodyAsync(bool isRequest, CancellationToken cancellationToken) private async Task<byte[]> readBodyAsync(bool isRequest, CancellationToken cancellationToken)
{ {
using (var bodyStream = new MemoryStream()) using (var bodyStream = new MemoryStream())
{ {
var writer = new HttpWriter(bodyStream, BufferSize); var writer = new HttpWriter(bodyStream, bufferPool, bufferSize);
if (isRequest) if (isRequest)
{ {
...@@ -181,8 +179,8 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -181,8 +179,8 @@ namespace Titanium.Web.Proxy.EventArguments
using (var bodyStream = new MemoryStream()) using (var bodyStream = new MemoryStream())
{ {
var writer = new HttpWriter(bodyStream, BufferSize); var writer = new HttpWriter(bodyStream, bufferPool, bufferSize);
await CopyBodyAsync(isRequest, writer, TransformationMode.None, null, cancellationToken); await copyBodyAsync(isRequest, writer, TransformationMode.None, null, cancellationToken);
} }
} }
...@@ -199,14 +197,14 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -199,14 +197,14 @@ namespace Titanium.Web.Proxy.EventArguments
// send the request body bytes to server // send the request body bytes to server
if (contentLength > 0 && hasMulipartEventSubscribers && request.IsMultipartFormData) if (contentLength > 0 && hasMulipartEventSubscribers && request.IsMultipartFormData)
{ {
var reader = GetStreamReader(true); var reader = getStreamReader(true);
string boundary = HttpHelper.GetBoundaryFromContentType(request.ContentType); string boundary = HttpHelper.GetBoundaryFromContentType(request.ContentType);
using (var copyStream = new CopyStream(reader, writer, BufferSize)) using (var copyStream = new CopyStream(reader, writer, bufferPool, bufferSize))
{ {
while (contentLength > copyStream.ReadBytes) while (contentLength > copyStream.ReadBytes)
{ {
long read = await ReadUntilBoundaryAsync(copyStream, contentLength, boundary, cancellationToken); long read = await readUntilBoundaryAsync(copyStream, contentLength, boundary, cancellationToken);
if (read == 0) if (read == 0)
{ {
break; break;
...@@ -225,18 +223,18 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -225,18 +223,18 @@ namespace Titanium.Web.Proxy.EventArguments
} }
else else
{ {
await CopyBodyAsync(true, writer, transformation, OnDataSent, cancellationToken); await copyBodyAsync(true, writer, transformation, OnDataSent, cancellationToken);
} }
} }
internal async Task CopyResponseBodyAsync(HttpWriter writer, TransformationMode transformation, CancellationToken cancellationToken) internal async Task CopyResponseBodyAsync(HttpWriter writer, TransformationMode transformation, CancellationToken cancellationToken)
{ {
await CopyBodyAsync(false, writer, transformation, OnDataReceived, cancellationToken); await copyBodyAsync(false, writer, transformation, OnDataReceived, cancellationToken);
} }
private async Task CopyBodyAsync(bool isRequest, HttpWriter writer, TransformationMode transformation, Action<byte[], int, int> onCopy, CancellationToken cancellationToken) private async Task copyBodyAsync(bool isRequest, HttpWriter writer, TransformationMode transformation, Action<byte[], int, int> onCopy, CancellationToken cancellationToken)
{ {
var stream = GetStreamReader(isRequest); var stream = getStreamReader(isRequest);
var requestResponse = isRequest ? (RequestResponseBase)WebSession.Request : WebSession.Response; var requestResponse = isRequest ? (RequestResponseBase)WebSession.Request : WebSession.Response;
...@@ -253,7 +251,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -253,7 +251,7 @@ namespace Titanium.Web.Proxy.EventArguments
string contentEncoding = requestResponse.ContentEncoding; string contentEncoding = requestResponse.ContentEncoding;
Stream s = limitedStream = new LimitedStream(stream, isChunked, contentLength); Stream s = limitedStream = new LimitedStream(stream, bufferPool, isChunked, contentLength);
if (transformation == TransformationMode.Uncompress && contentEncoding != null) if (transformation == TransformationMode.Uncompress && contentEncoding != null)
{ {
...@@ -262,7 +260,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -262,7 +260,7 @@ namespace Titanium.Web.Proxy.EventArguments
try try
{ {
using (var bufStream = new CustomBufferedStream(s, BufferSize, true)) using (var bufStream = new CustomBufferedStream(s, bufferPool, bufferSize, true))
{ {
await writer.CopyBodyAsync(bufStream, false, -1, onCopy, cancellationToken); await writer.CopyBodyAsync(bufStream, false, -1, onCopy, cancellationToken);
} }
...@@ -280,11 +278,11 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -280,11 +278,11 @@ namespace Titanium.Web.Proxy.EventArguments
/// Read a line from the byte stream /// Read a line from the byte stream
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
private async Task<long> ReadUntilBoundaryAsync(ICustomStreamReader reader, long totalBytesToRead, string boundary, CancellationToken cancellationToken) private async Task<long> readUntilBoundaryAsync(ICustomStreamReader reader, long totalBytesToRead, string boundary, CancellationToken cancellationToken)
{ {
int bufferDataLength = 0; int bufferDataLength = 0;
var buffer = BufferPool.GetBuffer(BufferSize); var buffer = bufferPool.GetBuffer(bufferSize);
try try
{ {
int boundaryLength = boundary.Length + 4; int boundaryLength = boundary.Length + 4;
...@@ -334,7 +332,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -334,7 +332,7 @@ namespace Titanium.Web.Proxy.EventArguments
} }
finally finally
{ {
BufferPool.ReturnBuffer(buffer); bufferPool.ReturnBuffer(buffer);
} }
} }
...@@ -347,7 +345,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -347,7 +345,7 @@ namespace Titanium.Web.Proxy.EventArguments
{ {
if (!WebSession.Request.IsBodyRead) if (!WebSession.Request.IsBodyRead)
{ {
await ReadRequestBodyAsync(cancellationToken); await readRequestBodyAsync(cancellationToken);
} }
return WebSession.Request.Body; return WebSession.Request.Body;
...@@ -362,7 +360,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -362,7 +360,7 @@ namespace Titanium.Web.Proxy.EventArguments
{ {
if (!WebSession.Request.IsBodyRead) if (!WebSession.Request.IsBodyRead)
{ {
await ReadRequestBodyAsync(cancellationToken); await readRequestBodyAsync(cancellationToken);
} }
return WebSession.Request.BodyString; return WebSession.Request.BodyString;
...@@ -407,7 +405,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -407,7 +405,7 @@ namespace Titanium.Web.Proxy.EventArguments
{ {
if (!WebSession.Response.IsBodyRead) if (!WebSession.Response.IsBodyRead)
{ {
await ReadResponseBodyAsync(cancellationToken); await readResponseBodyAsync(cancellationToken);
} }
return WebSession.Response.Body; return WebSession.Response.Body;
...@@ -422,7 +420,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -422,7 +420,7 @@ namespace Titanium.Web.Proxy.EventArguments
{ {
if (!WebSession.Response.IsBodyRead) if (!WebSession.Response.IsBodyRead)
{ {
await ReadResponseBodyAsync(cancellationToken); await readResponseBodyAsync(cancellationToken);
} }
return WebSession.Response.BodyString; return WebSession.Response.BodyString;
......
using System; using System;
using System.Net; using System.Net;
using System.Threading; using System.Threading;
using StreamExtended;
using StreamExtended.Network; using StreamExtended.Network;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
...@@ -17,34 +18,33 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -17,34 +18,33 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary> /// </summary>
public abstract class SessionEventArgsBase : EventArgs, IDisposable public abstract class SessionEventArgsBase : EventArgs, IDisposable
{ {
/// <summary>
/// Size of Buffers used by this object
/// </summary>
protected readonly int BufferSize;
internal readonly CancellationTokenSource CancellationTokenSource; internal readonly CancellationTokenSource CancellationTokenSource;
protected readonly ExceptionHandler ExceptionFunc; protected readonly int bufferSize;
protected readonly IBufferPool bufferPool;
protected readonly ExceptionHandler exceptionFunc;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="SessionEventArgsBase" /> class. /// Initializes a new instance of the <see cref="SessionEventArgsBase" /> class.
/// </summary> /// </summary>
internal SessionEventArgsBase(int bufferSize, ProxyEndPoint endPoint, internal SessionEventArgsBase(ProxyServer server, ProxyEndPoint endPoint,
CancellationTokenSource cancellationTokenSource, ExceptionHandler exceptionFunc) CancellationTokenSource cancellationTokenSource)
: this(bufferSize, endPoint, cancellationTokenSource, null, exceptionFunc) : this(server, endPoint, cancellationTokenSource, null)
{ {
bufferSize = server.BufferSize;
bufferPool = server.BufferPool;
exceptionFunc = server.ExceptionFunc;
} }
protected SessionEventArgsBase(int bufferSize, ProxyEndPoint endPoint, protected SessionEventArgsBase(ProxyServer server, ProxyEndPoint endPoint,
CancellationTokenSource cancellationTokenSource, CancellationTokenSource cancellationTokenSource,
Request request, ExceptionHandler exceptionFunc) Request request)
{ {
BufferSize = bufferSize;
ExceptionFunc = exceptionFunc;
CancellationTokenSource = cancellationTokenSource; CancellationTokenSource = cancellationTokenSource;
ProxyClient = new ProxyClient(); ProxyClient = new ProxyClient();
WebSession = new HttpWebClient(bufferSize, request); WebSession = new HttpWebClient(request);
LocalEndPoint = endPoint; LocalEndPoint = endPoint;
WebSession.ProcessId = new Lazy<int>(() => WebSession.ProcessId = new Lazy<int>(() =>
...@@ -151,7 +151,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -151,7 +151,7 @@ namespace Titanium.Web.Proxy.EventArguments
} }
catch (Exception ex) catch (Exception ex)
{ {
ExceptionFunc(new Exception("Exception thrown in user event", ex)); exceptionFunc(new Exception("Exception thrown in user event", ex));
} }
} }
...@@ -163,7 +163,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -163,7 +163,7 @@ namespace Titanium.Web.Proxy.EventArguments
} }
catch (Exception ex) catch (Exception ex)
{ {
ExceptionFunc(new Exception("Exception thrown in user event", ex)); exceptionFunc(new Exception("Exception thrown in user event", ex));
} }
} }
......
...@@ -12,9 +12,9 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -12,9 +12,9 @@ namespace Titanium.Web.Proxy.EventArguments
{ {
private bool? isHttpsConnect; private bool? isHttpsConnect;
internal TunnelConnectSessionEventArgs(int bufferSize, ProxyEndPoint endPoint, ConnectRequest connectRequest, internal TunnelConnectSessionEventArgs(ProxyServer server, ProxyEndPoint endPoint, ConnectRequest connectRequest,
CancellationTokenSource cancellationTokenSource, ExceptionHandler exceptionFunc) CancellationTokenSource cancellationTokenSource)
: base(bufferSize, endPoint, cancellationTokenSource, connectRequest, exceptionFunc) : base(server, endPoint, cancellationTokenSource, connectRequest)
{ {
WebSession.ConnectRequest = connectRequest; WebSession.ConnectRequest = connectRequest;
} }
......
using System;
namespace Titanium.Web.Proxy.Exceptions
{
/// <summary>
/// The server connection was closed upon first read with the new connection from pool.
/// Should retry the request with a new connection.
/// </summary>
public class ServerConnectionException : ProxyException
{
internal ServerConnectionException(string message) : base(message)
{
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="message"></param>
/// <param name="e"></param>
internal ServerConnectionException(string message, Exception e) : base(message, e)
{
}
}
}
...@@ -7,7 +7,6 @@ using System.Security.Cryptography.X509Certificates; ...@@ -7,7 +7,6 @@ using System.Security.Cryptography.X509Certificates;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended; using StreamExtended;
using StreamExtended.Helpers;
using StreamExtended.Network; using StreamExtended.Network;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Exceptions; using Titanium.Web.Proxy.Exceptions;
...@@ -15,7 +14,6 @@ using Titanium.Web.Proxy.Extensions; ...@@ -15,7 +14,6 @@ using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network;
using Titanium.Web.Proxy.Network.Tcp; using Titanium.Web.Proxy.Network.Tcp;
namespace Titanium.Web.Proxy namespace Titanium.Web.Proxy
...@@ -29,19 +27,23 @@ namespace Titanium.Web.Proxy ...@@ -29,19 +27,23 @@ namespace Titanium.Web.Proxy
/// <param name="endPoint">The explicit endpoint.</param> /// <param name="endPoint">The explicit endpoint.</param>
/// <param name="clientConnection">The client connection.</param> /// <param name="clientConnection">The client connection.</param>
/// <returns>The task.</returns> /// <returns>The task.</returns>
private async Task HandleClient(ExplicitProxyEndPoint endPoint, TcpClientConnection clientConnection) private async Task handleClient(ExplicitProxyEndPoint endPoint, TcpClientConnection clientConnection)
{ {
var cancellationTokenSource = new CancellationTokenSource(); var cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = cancellationTokenSource.Token; var cancellationToken = cancellationTokenSource.Token;
var clientStream = new CustomBufferedStream(clientConnection.GetStream(), BufferSize); var clientStream = new CustomBufferedStream(clientConnection.GetStream(), BufferPool, BufferSize);
var clientStreamWriter = new HttpResponseWriter(clientStream, BufferPool, BufferSize);
var clientStreamWriter = new HttpResponseWriter(clientStream, BufferSize); Task<TcpServerConnection> prefetchConnectionTask = null;
bool closeServerConnection = false;
bool calledRequestHandler = false;
try try
{ {
string connectHostname = null; string connectHostname = null;
TunnelConnectSessionEventArgs connectArgs = null; TunnelConnectSessionEventArgs connectArgs = null;
// Client wants to create a secure tcp tunnel (probably its a HTTPS or Websocket request) // Client wants to create a secure tcp tunnel (probably its a HTTPS or Websocket request)
if (await HttpHelper.IsConnectMethod(clientStream) == 1) if (await HttpHelper.IsConnectMethod(clientStream) == 1)
...@@ -67,8 +69,8 @@ namespace Titanium.Web.Proxy ...@@ -67,8 +69,8 @@ namespace Titanium.Web.Proxy
await HeaderParser.ReadHeaders(clientStream, connectRequest.Headers, cancellationToken); await HeaderParser.ReadHeaders(clientStream, connectRequest.Headers, cancellationToken);
connectArgs = new TunnelConnectSessionEventArgs(BufferSize, endPoint, connectRequest, connectArgs = new TunnelConnectSessionEventArgs(this, endPoint, connectRequest,
cancellationTokenSource, ExceptionFunc); cancellationTokenSource);
connectArgs.ProxyClient.ClientConnection = clientConnection; connectArgs.ProxyClient.ClientConnection = clientConnection;
connectArgs.ProxyClient.ClientStream = clientStream; connectArgs.ProxyClient.ClientStream = clientStream;
...@@ -95,7 +97,7 @@ namespace Titanium.Web.Proxy ...@@ -95,7 +97,7 @@ namespace Titanium.Web.Proxy
return; return;
} }
if (await CheckAuthorization(connectArgs) == false) if (await checkAuthorization(connectArgs) == false)
{ {
await endPoint.InvokeBeforeTunnectConnectResponse(this, connectArgs, ExceptionFunc); await endPoint.InvokeBeforeTunnectConnectResponse(this, connectArgs, ExceptionFunc);
...@@ -115,7 +117,7 @@ namespace Titanium.Web.Proxy ...@@ -115,7 +117,7 @@ namespace Titanium.Web.Proxy
await clientStreamWriter.WriteResponseAsync(response, cancellationToken: cancellationToken); await clientStreamWriter.WriteResponseAsync(response, cancellationToken: cancellationToken);
var clientHelloInfo = await SslTools.PeekClientHello(clientStream, cancellationToken); var clientHelloInfo = await SslTools.PeekClientHello(clientStream, BufferPool, cancellationToken);
bool isClientHello = clientHelloInfo != null; bool isClientHello = clientHelloInfo != null;
if (isClientHello) if (isClientHello)
...@@ -129,21 +131,25 @@ namespace Titanium.Web.Proxy ...@@ -129,21 +131,25 @@ namespace Titanium.Web.Proxy
{ {
connectRequest.RequestUri = new Uri("https://" + httpUrl); connectRequest.RequestUri = new Uri("https://" + httpUrl);
bool http2Supproted = false; bool http2Supported = false;
var alpn = clientHelloInfo.GetAlpn(); var alpn = clientHelloInfo.GetAlpn();
if (alpn != null && alpn.Contains(SslApplicationProtocol.Http2)) if (alpn != null && alpn.Contains(SslApplicationProtocol.Http2))
{ {
// test server HTTP/2 support // test server HTTP/2 support
// todo: this is a hack, because Titanium does not support HTTP protocol changing currently // todo: this is a hack, because Titanium does not support HTTP protocol changing currently
using (var connection = await GetServerConnection(connectArgs, true, SslExtensions.Http2ProtocolAsList, cancellationToken)) var connection = await getServerConnection(connectArgs, true,
{ SslExtensions.Http2ProtocolAsList, cancellationToken);
http2Supproted = connection.NegotiatedApplicationProtocol == SslApplicationProtocol.Http2;
} http2Supported = connection.NegotiatedApplicationProtocol == SslApplicationProtocol.Http2;
//release connection back to pool intead of closing when connection pool is enabled.
await tcpConnectionFactory.Release(connection);
} }
SslStream sslStream = null; SslStream sslStream = null;
prefetchConnectionTask = getServerConnection(connectArgs, true,
null, cancellationToken);
try try
{ {
sslStream = new SslStream(clientStream); sslStream = new SslStream(clientStream);
...@@ -155,7 +161,7 @@ namespace Titanium.Web.Proxy ...@@ -155,7 +161,7 @@ namespace Titanium.Web.Proxy
// Successfully managed to authenticate the client using the fake certificate // Successfully managed to authenticate the client using the fake certificate
var options = new SslServerAuthenticationOptions(); var options = new SslServerAuthenticationOptions();
if (http2Supproted) if (http2Supported)
{ {
options.ApplicationProtocols = clientHelloInfo.GetAlpn(); options.ApplicationProtocols = clientHelloInfo.GetAlpn();
if (options.ApplicationProtocols == null || options.ApplicationProtocols.Count == 0) if (options.ApplicationProtocols == null || options.ApplicationProtocols.Count == 0)
...@@ -173,11 +179,10 @@ namespace Titanium.Web.Proxy ...@@ -173,11 +179,10 @@ namespace Titanium.Web.Proxy
#if NETCOREAPP2_1 #if NETCOREAPP2_1
clientConnection.NegotiatedApplicationProtocol = sslStream.NegotiatedApplicationProtocol; clientConnection.NegotiatedApplicationProtocol = sslStream.NegotiatedApplicationProtocol;
#endif #endif
// HTTPS server created - we can now decrypt the client's traffic
clientStream = new CustomBufferedStream(sslStream, BufferSize);
clientStreamWriter = new HttpResponseWriter(clientStream, BufferSize); // HTTPS server created - we can now decrypt the client's traffic
clientStream = new CustomBufferedStream(sslStream, BufferPool, BufferSize);
clientStreamWriter = new HttpResponseWriter(clientStream, BufferPool, BufferSize);
} }
catch (Exception e) catch (Exception e)
{ {
...@@ -200,41 +205,44 @@ namespace Titanium.Web.Proxy ...@@ -200,41 +205,44 @@ namespace Titanium.Web.Proxy
// Hostname is excluded or it is not an HTTPS connect // Hostname is excluded or it is not an HTTPS connect
if (!decryptSsl || !isClientHello) if (!decryptSsl || !isClientHello)
{ {
// create new connection // create new connection to server.
using (var connection = await GetServerConnection(connectArgs, true, clientConnection.NegotiatedApplicationProtocol, cancellationToken)) // If we detected that client tunnel CONNECTs without SSL by checking for empty client hello then
// this connection should not be HTTPS.
var connection = await getServerConnection(connectArgs, true,
null, cancellationToken);
if (isClientHello)
{ {
if (isClientHello) int available = clientStream.Available;
if (available > 0)
{ {
int available = clientStream.Available; // send the buffered data
if (available > 0) var data = BufferPool.GetBuffer(BufferSize);
try
{ {
// send the buffered data // clientStream.Available sbould be at most BufferSize because it is using the same buffer size
var data = BufferPool.GetBuffer(BufferSize); await clientStream.ReadAsync(data, 0, available, cancellationToken);
await connection.StreamWriter.WriteAsync(data, 0, available, true,
try cancellationToken);
{ }
// clientStream.Available sbould be at most BufferSize because it is using the same buffer size finally
await clientStream.ReadAsync(data, 0, available, cancellationToken); {
await connection.StreamWriter.WriteAsync(data, 0, available, true, BufferPool.ReturnBuffer(data);
cancellationToken);
}
finally
{
BufferPool.ReturnBuffer(data);
}
} }
var serverHelloInfo =
await SslTools.PeekServerHello(connection.Stream, cancellationToken);
((ConnectResponse)connectArgs.WebSession.Response).ServerHelloInfo = serverHelloInfo;
} }
await TcpHelper.SendRaw(clientStream, connection.Stream, BufferSize, var serverHelloInfo =
(buffer, offset, count) => { connectArgs.OnDataSent(buffer, offset, count); }, await SslTools.PeekServerHello(connection.Stream, BufferPool, cancellationToken);
(buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); }, ((ConnectResponse)connectArgs.WebSession.Response).ServerHelloInfo = serverHelloInfo;
connectArgs.CancellationTokenSource, ExceptionFunc);
} }
await TcpHelper.SendRaw(clientStream, connection.Stream, BufferPool, BufferSize,
(buffer, offset, count) => { connectArgs.OnDataSent(buffer, offset, count); },
(buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); },
connectArgs.CancellationTokenSource, ExceptionFunc);
await tcpConnectionFactory.Release(connection, true);
return; return;
} }
} }
...@@ -265,8 +273,9 @@ namespace Titanium.Web.Proxy ...@@ -265,8 +273,9 @@ namespace Titanium.Web.Proxy
} }
// create new connection // create new connection
using (var connection = await GetServerConnection(connectArgs, true, SslExtensions.Http2ProtocolAsList, cancellationToken)) var connection = await getServerConnection(connectArgs, true, SslExtensions.Http2ProtocolAsList,
{ cancellationToken);
await connection.StreamWriter.WriteLineAsync("PRI * HTTP/2.0", cancellationToken); await connection.StreamWriter.WriteLineAsync("PRI * HTTP/2.0", cancellationToken);
await connection.StreamWriter.WriteLineAsync(cancellationToken); await connection.StreamWriter.WriteLineAsync(cancellationToken);
await connection.StreamWriter.WriteLineAsync("SM", cancellationToken); await connection.StreamWriter.WriteLineAsync("SM", cancellationToken);
...@@ -278,33 +287,45 @@ namespace Titanium.Web.Proxy ...@@ -278,33 +287,45 @@ namespace Titanium.Web.Proxy
(buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); }, (buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); },
connectArgs.CancellationTokenSource, clientConnection.Id, ExceptionFunc); connectArgs.CancellationTokenSource, clientConnection.Id, ExceptionFunc);
#endif #endif
} await tcpConnectionFactory.Release(connection, true);
} }
} }
calledRequestHandler = true;
// Now create the request // Now create the request
await HandleHttpSessionRequest(endPoint, clientConnection, clientStream, clientStreamWriter, await handleHttpSessionRequest(endPoint, clientConnection, clientStream, clientStreamWriter,
cancellationTokenSource, connectHostname, connectArgs?.WebSession.ConnectRequest); cancellationTokenSource, connectHostname, connectArgs?.WebSession.ConnectRequest, prefetchConnectionTask);
} }
catch (ProxyException e) catch (ProxyException e)
{ {
OnException(clientStream, e); closeServerConnection = true;
onException(clientStream, e);
} }
catch (IOException e) catch (IOException e)
{ {
OnException(clientStream, new Exception("Connection was aborted", e)); closeServerConnection = true;
onException(clientStream, new Exception("Connection was aborted", e));
} }
catch (SocketException e) catch (SocketException e)
{ {
OnException(clientStream, new Exception("Could not connect", e)); closeServerConnection = true;
onException(clientStream, new Exception("Could not connect", e));
} }
catch (Exception e) catch (Exception e)
{ {
OnException(clientStream, new Exception("Error occured in whilst handling the client", e)); closeServerConnection = true;
onException(clientStream, new Exception("Error occured in whilst handling the client", e));
} }
finally finally
{ {
if (!calledRequestHandler
&& prefetchConnectionTask != null)
{
var connection = await prefetchConnectionTask;
await tcpConnectionFactory.Release(connection, closeServerConnection);
}
clientStream.Dispose(); clientStream.Dispose();
if (!cancellationTokenSource.IsCancellationRequested) if (!cancellationTokenSource.IsCancellationRequested)
{ {
cancellationTokenSource.Cancel(); cancellationTokenSource.Cancel();
......
...@@ -13,11 +13,11 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -13,11 +13,11 @@ namespace Titanium.Web.Proxy.Extensions
foreach (var @delegate in invocationList) foreach (var @delegate in invocationList)
{ {
await InternalInvokeAsync((AsyncEventHandler<T>)@delegate, sender, args, exceptionFunc); await internalInvokeAsync((AsyncEventHandler<T>)@delegate, sender, args, exceptionFunc);
} }
} }
private static async Task InternalInvokeAsync<T>(AsyncEventHandler<T> callback, object sender, T args, private static async Task internalInvokeAsync<T>(AsyncEventHandler<T> callback, object sender, T args,
ExceptionHandler exceptionFunc) ExceptionHandler exceptionFunc)
{ {
try try
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended.Helpers; using StreamExtended;
namespace Titanium.Web.Proxy.Extensions namespace Titanium.Web.Proxy.Extensions
{ {
...@@ -19,9 +19,9 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -19,9 +19,9 @@ namespace Titanium.Web.Proxy.Extensions
/// <param name="onCopy"></param> /// <param name="onCopy"></param>
/// <param name="bufferSize"></param> /// <param name="bufferSize"></param>
internal static Task CopyToAsync(this Stream input, Stream output, Action<byte[], int, int> onCopy, internal static Task CopyToAsync(this Stream input, Stream output, Action<byte[], int, int> onCopy,
int bufferSize) IBufferPool bufferPool, int bufferSize)
{ {
return CopyToAsync(input, output, onCopy, bufferSize, CancellationToken.None); return CopyToAsync(input, output, onCopy, bufferPool, bufferSize, CancellationToken.None);
} }
/// <summary> /// <summary>
...@@ -33,9 +33,9 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -33,9 +33,9 @@ namespace Titanium.Web.Proxy.Extensions
/// <param name="bufferSize"></param> /// <param name="bufferSize"></param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
internal static async Task CopyToAsync(this Stream input, Stream output, Action<byte[], int, int> onCopy, internal static async Task CopyToAsync(this Stream input, Stream output, Action<byte[], int, int> onCopy,
int bufferSize, CancellationToken cancellationToken) IBufferPool bufferPool, int bufferSize, CancellationToken cancellationToken)
{ {
var buffer = BufferPool.GetBuffer(bufferSize); var buffer = bufferPool.GetBuffer(bufferSize);
try try
{ {
while (!cancellationToken.IsCancellationRequested) while (!cancellationToken.IsCancellationRequested)
...@@ -43,7 +43,7 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -43,7 +43,7 @@ namespace Titanium.Web.Proxy.Extensions
// cancellation is not working on Socket ReadAsync // cancellation is not working on Socket ReadAsync
// https://github.com/dotnet/corefx/issues/15033 // https://github.com/dotnet/corefx/issues/15033
int num = await input.ReadAsync(buffer, 0, buffer.Length, CancellationToken.None) int num = await input.ReadAsync(buffer, 0, buffer.Length, CancellationToken.None)
.WithCancellation(cancellationToken); .withCancellation(cancellationToken);
int bytesRead; int bytesRead;
if ((bytesRead = num) != 0 && !cancellationToken.IsCancellationRequested) if ((bytesRead = num) != 0 && !cancellationToken.IsCancellationRequested)
{ {
...@@ -58,11 +58,11 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -58,11 +58,11 @@ namespace Titanium.Web.Proxy.Extensions
} }
finally finally
{ {
BufferPool.ReturnBuffer(buffer); bufferPool.ReturnBuffer(buffer);
} }
} }
private static async Task<T> WithCancellation<T>(this Task<T> task, CancellationToken cancellationToken) private static async Task<T> withCancellation<T>(this Task<T> task, CancellationToken cancellationToken)
{ {
var tcs = new TaskCompletionSource<bool>(); var tcs = new TaskCompletionSource<bool>();
using (cancellationToken.Register(s => ((TaskCompletionSource<bool>)s).TrySetResult(true), tcs)) using (cancellationToken.Register(s => ((TaskCompletionSource<bool>)s).TrySetResult(true), tcs))
......
...@@ -122,7 +122,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -122,7 +122,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <returns>1: when CONNECT, 0: when valid HTTP method, -1: otherwise</returns> /// <returns>1: when CONNECT, 0: when valid HTTP method, -1: otherwise</returns>
internal static Task<int> IsConnectMethod(ICustomStreamReader clientStreamReader) internal static Task<int> IsConnectMethod(ICustomStreamReader clientStreamReader)
{ {
return StartsWith(clientStreamReader, "CONNECT"); return startsWith(clientStreamReader, "CONNECT");
} }
/// <summary> /// <summary>
...@@ -132,7 +132,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -132,7 +132,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <returns>1: when PRI, 0: when valid HTTP method, -1: otherwise</returns> /// <returns>1: when PRI, 0: when valid HTTP method, -1: otherwise</returns>
internal static Task<int> IsPriMethod(ICustomStreamReader clientStreamReader) internal static Task<int> IsPriMethod(ICustomStreamReader clientStreamReader)
{ {
return StartsWith(clientStreamReader, "PRI"); return startsWith(clientStreamReader, "PRI");
} }
/// <summary> /// <summary>
...@@ -143,7 +143,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -143,7 +143,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <returns> /// <returns>
/// 1: when starts with the given string, 0: when valid HTTP method, -1: otherwise /// 1: when starts with the given string, 0: when valid HTTP method, -1: otherwise
/// </returns> /// </returns>
private static async Task<int> StartsWith(ICustomStreamReader clientStreamReader, string expectedStart) private static async Task<int> startsWith(ICustomStreamReader clientStreamReader, string expectedStart)
{ {
bool isExpected = true; bool isExpected = true;
int legthToCheck = 10; int legthToCheck = 10;
......
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.Helpers namespace Titanium.Web.Proxy.Helpers
{ {
internal sealed class HttpRequestWriter : HttpWriter internal sealed class HttpRequestWriter : HttpWriter
{ {
internal HttpRequestWriter(Stream stream, int bufferSize) : base(stream, bufferSize) internal HttpRequestWriter(Stream stream, IBufferPool bufferPool, int bufferSize)
: base(stream, bufferPool, bufferSize)
{ {
} }
......
...@@ -2,13 +2,15 @@ ...@@ -2,13 +2,15 @@
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.Helpers namespace Titanium.Web.Proxy.Helpers
{ {
internal sealed class HttpResponseWriter : HttpWriter internal sealed class HttpResponseWriter : HttpWriter
{ {
internal HttpResponseWriter(Stream stream, int bufferSize) : base(stream, bufferSize) internal HttpResponseWriter(Stream stream, IBufferPool bufferPool, int bufferSize)
: base(stream, bufferPool, bufferSize)
{ {
} }
......
...@@ -4,7 +4,7 @@ using System.IO; ...@@ -4,7 +4,7 @@ using System.IO;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended.Helpers; using StreamExtended;
using StreamExtended.Network; using StreamExtended.Network;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Shared; using Titanium.Web.Proxy.Shared;
...@@ -14,6 +14,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -14,6 +14,7 @@ namespace Titanium.Web.Proxy.Helpers
internal class HttpWriter : ICustomStreamWriter internal class HttpWriter : ICustomStreamWriter
{ {
private readonly Stream stream; private readonly Stream stream;
private readonly IBufferPool bufferPool;
private static readonly byte[] newLine = ProxyConstants.NewLine; private static readonly byte[] newLine = ProxyConstants.NewLine;
...@@ -21,10 +22,11 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -21,10 +22,11 @@ namespace Titanium.Web.Proxy.Helpers
private readonly char[] charBuffer; private readonly char[] charBuffer;
internal HttpWriter(Stream stream, int bufferSize) internal HttpWriter(Stream stream, IBufferPool bufferPool, int bufferSize)
{ {
BufferSize = bufferSize; BufferSize = bufferSize;
this.stream = stream; this.stream = stream;
this.bufferPool = bufferPool;
// ASCII encoder max byte count is char count + 1 // ASCII encoder max byte count is char count + 1
charBuffer = new char[BufferSize - 1]; charBuffer = new char[BufferSize - 1];
...@@ -44,10 +46,10 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -44,10 +46,10 @@ namespace Titanium.Web.Proxy.Helpers
internal Task WriteAsync(string value, CancellationToken cancellationToken = default) internal Task WriteAsync(string value, CancellationToken cancellationToken = default)
{ {
return WriteAsyncInternal(value, false, cancellationToken); return writeAsyncInternal(value, false, cancellationToken);
} }
private Task WriteAsyncInternal(string value, bool addNewLine, CancellationToken cancellationToken) private Task writeAsyncInternal(string value, bool addNewLine, CancellationToken cancellationToken)
{ {
int newLineChars = addNewLine ? newLine.Length : 0; int newLineChars = addNewLine ? newLine.Length : 0;
int charCount = value.Length; int charCount = value.Length;
...@@ -55,7 +57,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -55,7 +57,7 @@ namespace Titanium.Web.Proxy.Helpers
{ {
value.CopyTo(0, charBuffer, 0, charCount); value.CopyTo(0, charBuffer, 0, charCount);
var buffer = BufferPool.GetBuffer(BufferSize); var buffer = bufferPool.GetBuffer(BufferSize);
try try
{ {
int idx = encoder.GetBytes(charBuffer, 0, charCount, buffer, 0, true); int idx = encoder.GetBytes(charBuffer, 0, charCount, buffer, 0, true);
...@@ -69,7 +71,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -69,7 +71,7 @@ namespace Titanium.Web.Proxy.Helpers
} }
finally finally
{ {
BufferPool.ReturnBuffer(buffer); bufferPool.ReturnBuffer(buffer);
} }
} }
else else
...@@ -91,7 +93,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -91,7 +93,7 @@ namespace Titanium.Web.Proxy.Helpers
internal Task WriteLineAsync(string value, CancellationToken cancellationToken = default) internal Task WriteLineAsync(string value, CancellationToken cancellationToken = default)
{ {
return WriteAsyncInternal(value, true, cancellationToken); return writeAsyncInternal(value, true, cancellationToken);
} }
/// <summary> /// <summary>
...@@ -104,12 +106,15 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -104,12 +106,15 @@ namespace Titanium.Web.Proxy.Helpers
internal async Task WriteHeadersAsync(HeaderCollection headers, bool flush = true, internal async Task WriteHeadersAsync(HeaderCollection headers, bool flush = true,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
var headerBuilder = new StringBuilder();
foreach (var header in headers) foreach (var header in headers)
{ {
await header.WriteToStreamAsync(this, cancellationToken); headerBuilder.AppendLine(header.ToString());
} }
headerBuilder.AppendLine();
await WriteAsync(headerBuilder.ToString(), cancellationToken);
await WriteLineAsync(cancellationToken);
if (flush) if (flush)
{ {
await stream.FlushAsync(cancellationToken); await stream.FlushAsync(cancellationToken);
...@@ -146,7 +151,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -146,7 +151,7 @@ namespace Titanium.Web.Proxy.Helpers
{ {
if (isChunked) if (isChunked)
{ {
return WriteBodyChunkedAsync(data, cancellationToken); return writeBodyChunkedAsync(data, cancellationToken);
} }
return WriteAsync(data, cancellationToken: cancellationToken); return WriteAsync(data, cancellationToken: cancellationToken);
...@@ -168,7 +173,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -168,7 +173,7 @@ namespace Titanium.Web.Proxy.Helpers
// For chunked request we need to read data as they arrive, until we reach a chunk end symbol // For chunked request we need to read data as they arrive, until we reach a chunk end symbol
if (isChunked) if (isChunked)
{ {
return CopyBodyChunkedAsync(streamReader, onCopy, cancellationToken); return copyBodyChunkedAsync(streamReader, onCopy, cancellationToken);
} }
// http 1.0 or the stream reader limits the stream // http 1.0 or the stream reader limits the stream
...@@ -178,7 +183,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -178,7 +183,7 @@ namespace Titanium.Web.Proxy.Helpers
} }
// If not chunked then its easy just read the amount of bytes mentioned in content length header // If not chunked then its easy just read the amount of bytes mentioned in content length header
return CopyBytesFromStream(streamReader, contentLength, onCopy, cancellationToken); return copyBytesFromStream(streamReader, contentLength, onCopy, cancellationToken);
} }
/// <summary> /// <summary>
...@@ -187,7 +192,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -187,7 +192,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="data"></param> /// <param name="data"></param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
private async Task WriteBodyChunkedAsync(byte[] data, CancellationToken cancellationToken) private async Task writeBodyChunkedAsync(byte[] data, CancellationToken cancellationToken)
{ {
var chunkHead = Encoding.ASCII.GetBytes(data.Length.ToString("x2")); var chunkHead = Encoding.ASCII.GetBytes(data.Length.ToString("x2"));
...@@ -207,7 +212,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -207,7 +212,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="onCopy"></param> /// <param name="onCopy"></param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
private async Task CopyBodyChunkedAsync(ICustomStreamReader reader, Action<byte[], int, int> onCopy, private async Task copyBodyChunkedAsync(ICustomStreamReader reader, Action<byte[], int, int> onCopy,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
while (true) while (true)
...@@ -225,7 +230,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -225,7 +230,7 @@ namespace Titanium.Web.Proxy.Helpers
if (chunkSize != 0) if (chunkSize != 0)
{ {
await CopyBytesFromStream(reader, chunkSize, onCopy, cancellationToken); await copyBytesFromStream(reader, chunkSize, onCopy, cancellationToken);
} }
await WriteLineAsync(cancellationToken); await WriteLineAsync(cancellationToken);
...@@ -248,10 +253,10 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -248,10 +253,10 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="onCopy"></param> /// <param name="onCopy"></param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
private async Task CopyBytesFromStream(ICustomStreamReader reader, long count, Action<byte[], int, int> onCopy, private async Task copyBytesFromStream(ICustomStreamReader reader, long count, Action<byte[], int, int> onCopy,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
var buffer = BufferPool.GetBuffer(BufferSize); var buffer = bufferPool.GetBuffer(BufferSize);
try try
{ {
...@@ -280,7 +285,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -280,7 +285,7 @@ namespace Titanium.Web.Proxy.Helpers
} }
finally finally
{ {
BufferPool.ReturnBuffer(buffer); bufferPool.ReturnBuffer(buffer);
} }
} }
......
...@@ -39,7 +39,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -39,7 +39,7 @@ namespace Titanium.Web.Proxy.Helpers
} }
else else
{ {
overrides2.Add(BypassStringEscape(overrideHost)); overrides2.Add(bypassStringEscape(overrideHost));
} }
} }
...@@ -70,7 +70,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -70,7 +70,7 @@ namespace Titanium.Web.Proxy.Helpers
internal string[] BypassList { get; } internal string[] BypassList { get; }
private static string BypassStringEscape(string rawString) private static string bypassStringEscape(string rawString)
{ {
var match = var match =
new Regex("^(?<scheme>.*://)?(?<host>[^:]*)(?<port>:[0-9]{1,5})?$", new Regex("^(?<scheme>.*://)?(?<host>[^:]*)(?<port>:[0-9]{1,5})?$",
...@@ -91,9 +91,9 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -91,9 +91,9 @@ namespace Titanium.Web.Proxy.Helpers
empty2 = string.Empty; empty2 = string.Empty;
} }
string str1 = ConvertRegexReservedChars(empty1); string str1 = convertRegexReservedChars(empty1);
string str2 = ConvertRegexReservedChars(rawString1); string str2 = convertRegexReservedChars(rawString1);
string str3 = ConvertRegexReservedChars(empty2); string str3 = convertRegexReservedChars(empty2);
if (str1 == string.Empty) if (str1 == string.Empty)
{ {
str1 = "(?:.*://)?"; str1 = "(?:.*://)?";
...@@ -107,7 +107,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -107,7 +107,7 @@ namespace Titanium.Web.Proxy.Helpers
return "^" + str1 + str2 + str3 + "$"; return "^" + str1 + str2 + str3 + "$";
} }
private static string ConvertRegexReservedChars(string rawString) private static string convertRegexReservedChars(string rawString)
{ {
if (rawString.Length == 0) if (rawString.Length == 0)
{ {
...@@ -171,11 +171,11 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -171,11 +171,11 @@ namespace Titanium.Web.Proxy.Helpers
if (proxyValues.Length > 0) if (proxyValues.Length > 0)
{ {
result.AddRange(proxyValues.Select(ParseProxyValue).Where(parsedValue => parsedValue != null)); result.AddRange(proxyValues.Select(parseProxyValue).Where(parsedValue => parsedValue != null));
} }
else else
{ {
var parsedValue = ParseProxyValue(proxyServerValues); var parsedValue = parseProxyValue(proxyServerValues);
if (parsedValue != null) if (parsedValue != null)
{ {
result.Add(parsedValue); result.Add(parsedValue);
...@@ -190,7 +190,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -190,7 +190,7 @@ namespace Titanium.Web.Proxy.Helpers
/// </summary> /// </summary>
/// <param name="value"></param> /// <param name="value"></param>
/// <returns></returns> /// <returns></returns>
private static HttpSystemProxyValue ParseProxyValue(string value) private static HttpSystemProxyValue parseProxyValue(string value)
{ {
string tmp = Regex.Replace(value, @"\s+", " ").Trim(); string tmp = Regex.Replace(value, @"\s+", " ").Trim();
......
...@@ -84,8 +84,8 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -84,8 +84,8 @@ namespace Titanium.Web.Proxy.Helpers
if (reg != null) if (reg != null)
{ {
SaveOriginalProxyConfiguration(reg); saveOriginalProxyConfiguration(reg);
PrepareRegistry(reg); prepareRegistry(reg);
string exisitingContent = reg.GetValue(regProxyServer) as string; string exisitingContent = reg.GetValue(regProxyServer) as string;
var existingSystemProxyValues = ProxyInfo.GetSystemProxyValues(exisitingContent); var existingSystemProxyValues = ProxyInfo.GetSystemProxyValues(exisitingContent);
...@@ -115,7 +115,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -115,7 +115,7 @@ namespace Titanium.Web.Proxy.Helpers
reg.SetValue(regProxyServer, reg.SetValue(regProxyServer,
string.Join(";", existingSystemProxyValues.Select(x => x.ToString()).ToArray())); string.Join(";", existingSystemProxyValues.Select(x => x.ToString()).ToArray()));
Refresh(); refresh();
} }
} }
...@@ -129,7 +129,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -129,7 +129,7 @@ namespace Titanium.Web.Proxy.Helpers
{ {
if (saveOriginalConfig) if (saveOriginalConfig)
{ {
SaveOriginalProxyConfiguration(reg); saveOriginalProxyConfiguration(reg);
} }
if (reg.GetValue(regProxyServer) != null) if (reg.GetValue(regProxyServer) != null)
...@@ -152,7 +152,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -152,7 +152,7 @@ namespace Titanium.Web.Proxy.Helpers
} }
} }
Refresh(); refresh();
} }
} }
...@@ -165,12 +165,12 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -165,12 +165,12 @@ namespace Titanium.Web.Proxy.Helpers
if (reg != null) if (reg != null)
{ {
SaveOriginalProxyConfiguration(reg); saveOriginalProxyConfiguration(reg);
reg.SetValue(regProxyEnable, 0); reg.SetValue(regProxyEnable, 0);
reg.SetValue(regProxyServer, string.Empty); reg.SetValue(regProxyServer, string.Empty);
Refresh(); refresh();
} }
} }
...@@ -180,9 +180,9 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -180,9 +180,9 @@ namespace Titanium.Web.Proxy.Helpers
if (reg != null) if (reg != null)
{ {
SaveOriginalProxyConfiguration(reg); saveOriginalProxyConfiguration(reg);
reg.SetValue(regAutoConfigUrl, url); reg.SetValue(regAutoConfigUrl, url);
Refresh(); refresh();
} }
} }
...@@ -192,9 +192,9 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -192,9 +192,9 @@ namespace Titanium.Web.Proxy.Helpers
if (reg != null) if (reg != null)
{ {
SaveOriginalProxyConfiguration(reg); saveOriginalProxyConfiguration(reg);
reg.SetValue(regProxyOverride, proxyOverride); reg.SetValue(regProxyOverride, proxyOverride);
Refresh(); refresh();
} }
} }
...@@ -247,7 +247,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -247,7 +247,7 @@ namespace Titanium.Web.Proxy.Helpers
} }
originalValues = null; originalValues = null;
Refresh(); refresh();
} }
} }
...@@ -257,13 +257,13 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -257,13 +257,13 @@ namespace Titanium.Web.Proxy.Helpers
if (reg != null) if (reg != null)
{ {
return GetProxyInfoFromRegistry(reg); return getProxyInfoFromRegistry(reg);
} }
return null; return null;
} }
private ProxyInfo GetProxyInfoFromRegistry(RegistryKey reg) private ProxyInfo getProxyInfoFromRegistry(RegistryKey reg)
{ {
var pi = new ProxyInfo(null, reg.GetValue(regAutoConfigUrl) as string, reg.GetValue(regProxyEnable) as int?, var pi = new ProxyInfo(null, reg.GetValue(regAutoConfigUrl) as string, reg.GetValue(regProxyEnable) as int?,
reg.GetValue(regProxyServer) as string, reg.GetValue(regProxyServer) as string,
...@@ -272,21 +272,21 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -272,21 +272,21 @@ namespace Titanium.Web.Proxy.Helpers
return pi; return pi;
} }
private void SaveOriginalProxyConfiguration(RegistryKey reg) private void saveOriginalProxyConfiguration(RegistryKey reg)
{ {
if (originalValues != null) if (originalValues != null)
{ {
return; return;
} }
originalValues = GetProxyInfoFromRegistry(reg); originalValues = getProxyInfoFromRegistry(reg);
} }
/// <summary> /// <summary>
/// Prepares the proxy server registry (create empty values if they don't exist) /// Prepares the proxy server registry (create empty values if they don't exist)
/// </summary> /// </summary>
/// <param name="reg"></param> /// <param name="reg"></param>
private static void PrepareRegistry(RegistryKey reg) private static void prepareRegistry(RegistryKey reg)
{ {
if (reg.GetValue(regProxyEnable) == null) if (reg.GetValue(regProxyEnable) == null)
{ {
...@@ -302,7 +302,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -302,7 +302,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <summary> /// <summary>
/// Refresh the settings so that the system know about a change in proxy setting /// Refresh the settings so that the system know about a change in proxy setting
/// </summary> /// </summary>
private void Refresh() private static void refresh()
{ {
NativeMethods.InternetSetOption(IntPtr.Zero, InternetOptionSettingsChanged, IntPtr.Zero, 0); NativeMethods.InternetSetOption(IntPtr.Zero, InternetOptionSettingsChanged, IntPtr.Zero, 0);
NativeMethods.InternetSetOption(IntPtr.Zero, InternetOptionRefresh, IntPtr.Zero, 0); NativeMethods.InternetSetOption(IntPtr.Zero, InternetOptionRefresh, IntPtr.Zero, 0);
......
using System; using System;
using System.IO; using System.IO;
using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended.Helpers; using StreamExtended;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
namespace Titanium.Web.Proxy.Helpers namespace Titanium.Web.Proxy.Helpers
...@@ -39,7 +37,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -39,7 +37,7 @@ namespace Titanium.Web.Proxy.Helpers
0) == 0) 0) == 0)
{ {
int rowCount = *(int*)tcpTable; int rowCount = *(int*)tcpTable;
uint portInNetworkByteOrder = ToNetworkByteOrder((uint)localPort); uint portInNetworkByteOrder = toNetworkByteOrder((uint)localPort);
if (ipVersion == IpVersion.Ipv4) if (ipVersion == IpVersion.Ipv4)
{ {
...@@ -90,7 +88,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -90,7 +88,7 @@ namespace Titanium.Web.Proxy.Helpers
/// </summary> /// </summary>
/// <param name="port"></param> /// <param name="port"></param>
/// <returns></returns> /// <returns></returns>
private static uint ToNetworkByteOrder(uint port) private static uint toNetworkByteOrder(uint port)
{ {
return ((port >> 8) & 0x00FF00FFu) | ((port << 8) & 0xFF00FF00u); return ((port >> 8) & 0x00FF00FFu) | ((port << 8) & 0xFF00FF00u);
} }
...@@ -109,7 +107,8 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -109,7 +107,8 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="cancellationTokenSource"></param> /// <param name="cancellationTokenSource"></param>
/// <param name="exceptionFunc"></param> /// <param name="exceptionFunc"></param>
/// <returns></returns> /// <returns></returns>
internal static async Task SendRawApm(Stream clientStream, Stream serverStream, int bufferSize, internal static async Task SendRawApm(Stream clientStream, Stream serverStream,
IBufferPool bufferPool, int bufferSize,
Action<byte[], int, int> onDataSend, Action<byte[], int, int> onDataReceive, Action<byte[], int, int> onDataSend, Action<byte[], int, int> onDataReceive,
CancellationTokenSource cancellationTokenSource, CancellationTokenSource cancellationTokenSource,
ExceptionHandler exceptionFunc) ExceptionHandler exceptionFunc)
...@@ -118,23 +117,23 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -118,23 +117,23 @@ namespace Titanium.Web.Proxy.Helpers
cancellationTokenSource.Token.Register(() => taskCompletionSource.TrySetResult(true)); cancellationTokenSource.Token.Register(() => taskCompletionSource.TrySetResult(true));
// Now async relay all server=>client & client=>server data // Now async relay all server=>client & client=>server data
var clientBuffer = BufferPool.GetBuffer(bufferSize); var clientBuffer = bufferPool.GetBuffer(bufferSize);
var serverBuffer = BufferPool.GetBuffer(bufferSize); var serverBuffer = bufferPool.GetBuffer(bufferSize);
try try
{ {
BeginRead(clientStream, serverStream, clientBuffer, onDataSend, cancellationTokenSource, exceptionFunc); beginRead(clientStream, serverStream, clientBuffer, onDataSend, cancellationTokenSource, exceptionFunc);
BeginRead(serverStream, clientStream, serverBuffer, onDataReceive, cancellationTokenSource, beginRead(serverStream, clientStream, serverBuffer, onDataReceive, cancellationTokenSource,
exceptionFunc); exceptionFunc);
await taskCompletionSource.Task; await taskCompletionSource.Task;
} }
finally finally
{ {
BufferPool.ReturnBuffer(clientBuffer); bufferPool.ReturnBuffer(clientBuffer);
BufferPool.ReturnBuffer(serverBuffer); bufferPool.ReturnBuffer(serverBuffer);
} }
} }
private static void BeginRead(Stream inputStream, Stream outputStream, byte[] buffer, private static void beginRead(Stream inputStream, Stream outputStream, byte[] buffer,
Action<byte[], int, int> onCopy, CancellationTokenSource cancellationTokenSource, Action<byte[], int, int> onCopy, CancellationTokenSource cancellationTokenSource,
ExceptionHandler exceptionFunc) ExceptionHandler exceptionFunc)
{ {
...@@ -174,7 +173,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -174,7 +173,7 @@ namespace Titanium.Web.Proxy.Helpers
try try
{ {
outputStream.EndWrite(ar2); outputStream.EndWrite(ar2);
BeginRead(inputStream, outputStream, buffer, onCopy, cancellationTokenSource, beginRead(inputStream, outputStream, buffer, onCopy, cancellationTokenSource,
exceptionFunc); exceptionFunc);
} }
catch (IOException ex) catch (IOException ex)
...@@ -214,16 +213,17 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -214,16 +213,17 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="cancellationTokenSource"></param> /// <param name="cancellationTokenSource"></param>
/// <param name="exceptionFunc"></param> /// <param name="exceptionFunc"></param>
/// <returns></returns> /// <returns></returns>
private static async Task SendRawTap(Stream clientStream, Stream serverStream, int bufferSize, private static async Task sendRawTap(Stream clientStream, Stream serverStream,
IBufferPool bufferPool, int bufferSize,
Action<byte[], int, int> onDataSend, Action<byte[], int, int> onDataReceive, Action<byte[], int, int> onDataSend, Action<byte[], int, int> onDataReceive,
CancellationTokenSource cancellationTokenSource, CancellationTokenSource cancellationTokenSource,
ExceptionHandler exceptionFunc) ExceptionHandler exceptionFunc)
{ {
// Now async relay all server=>client & client=>server data // Now async relay all server=>client & client=>server data
var sendRelay = var sendRelay =
clientStream.CopyToAsync(serverStream, onDataSend, bufferSize, cancellationTokenSource.Token); clientStream.CopyToAsync(serverStream, onDataSend, bufferPool, bufferSize, cancellationTokenSource.Token);
var receiveRelay = var receiveRelay =
serverStream.CopyToAsync(clientStream, onDataReceive, bufferSize, cancellationTokenSource.Token); serverStream.CopyToAsync(clientStream, onDataReceive, bufferPool, bufferSize, cancellationTokenSource.Token);
await Task.WhenAny(sendRelay, receiveRelay); await Task.WhenAny(sendRelay, receiveRelay);
cancellationTokenSource.Cancel(); cancellationTokenSource.Cancel();
...@@ -244,13 +244,14 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -244,13 +244,14 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="cancellationTokenSource"></param> /// <param name="cancellationTokenSource"></param>
/// <param name="exceptionFunc"></param> /// <param name="exceptionFunc"></param>
/// <returns></returns> /// <returns></returns>
internal static Task SendRaw(Stream clientStream, Stream serverStream, int bufferSize, internal static Task SendRaw(Stream clientStream, Stream serverStream,
IBufferPool bufferPool, int bufferSize,
Action<byte[], int, int> onDataSend, Action<byte[], int, int> onDataReceive, Action<byte[], int, int> onDataSend, Action<byte[], int, int> onDataReceive,
CancellationTokenSource cancellationTokenSource, CancellationTokenSource cancellationTokenSource,
ExceptionHandler exceptionFunc) ExceptionHandler exceptionFunc)
{ {
// todo: fix APM mode // todo: fix APM mode
return SendRawTap(clientStream, serverStream, bufferSize, onDataSend, onDataReceive, return sendRawTap(clientStream, serverStream, bufferPool, bufferSize, onDataSend, onDataReceive,
cancellationTokenSource, cancellationTokenSource,
exceptionFunc); exceptionFunc);
} }
......
...@@ -19,7 +19,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -19,7 +19,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
session = NativeMethods.WinHttp.WinHttpOpen(null, NativeMethods.WinHttp.AccessType.NoProxy, null, null, 0); session = NativeMethods.WinHttp.WinHttpOpen(null, NativeMethods.WinHttp.AccessType.NoProxy, null, null, 0);
if (session == null || session.IsInvalid) if (session == null || session.IsInvalid)
{ {
int lastWin32Error = GetLastWin32Error(); int lastWin32Error = getLastWin32Error();
} }
else else
{ {
...@@ -30,7 +30,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -30,7 +30,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
return; return;
} }
int lastWin32Error = GetLastWin32Error(); int lastWin32Error = getLastWin32Error();
} }
} }
...@@ -50,7 +50,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -50,7 +50,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
public void Dispose() public void Dispose()
{ {
Dispose(true); dispose(true);
} }
public bool GetAutoProxies(Uri destination, out IList<string> proxyList) public bool GetAutoProxies(Uri destination, out IList<string> proxyList)
...@@ -65,8 +65,8 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -65,8 +65,8 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
var errorCode = NativeMethods.WinHttp.ErrorCodes.AudodetectionFailed; var errorCode = NativeMethods.WinHttp.ErrorCodes.AudodetectionFailed;
if (AutomaticallyDetectSettings && !autoDetectFailed) if (AutomaticallyDetectSettings && !autoDetectFailed)
{ {
errorCode = (NativeMethods.WinHttp.ErrorCodes)GetAutoProxies(destination, null, out proxyListString); errorCode = (NativeMethods.WinHttp.ErrorCodes)getAutoProxies(destination, null, out proxyListString);
autoDetectFailed = IsErrorFatalForAutoDetect(errorCode); autoDetectFailed = isErrorFatalForAutoDetect(errorCode);
if (errorCode == NativeMethods.WinHttp.ErrorCodes.UnrecognizedScheme) if (errorCode == NativeMethods.WinHttp.ErrorCodes.UnrecognizedScheme)
{ {
state = AutoWebProxyState.UnrecognizedScheme; state = AutoWebProxyState.UnrecognizedScheme;
...@@ -74,13 +74,13 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -74,13 +74,13 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
} }
} }
if (AutomaticConfigurationScript != null && IsRecoverableAutoProxyError(errorCode)) if (AutomaticConfigurationScript != null && isRecoverableAutoProxyError(errorCode))
{ {
errorCode = (NativeMethods.WinHttp.ErrorCodes)GetAutoProxies(destination, AutomaticConfigurationScript, errorCode = (NativeMethods.WinHttp.ErrorCodes)getAutoProxies(destination, AutomaticConfigurationScript,
out proxyListString); out proxyListString);
} }
state = GetStateFromErrorCode(errorCode); state = getStateFromErrorCode(errorCode);
if (state != AutoWebProxyState.Completed) if (state != AutoWebProxyState.Completed)
{ {
return false; return false;
...@@ -88,7 +88,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -88,7 +88,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
if (!string.IsNullOrEmpty(proxyListString)) if (!string.IsNullOrEmpty(proxyListString))
{ {
proxyListString = RemoveWhitespaces(proxyListString); proxyListString = removeWhitespaces(proxyListString);
proxyList = proxyListString.Split(';'); proxyList = proxyListString.Split(';');
} }
...@@ -149,7 +149,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -149,7 +149,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
public void LoadFromIE() public void LoadFromIE()
{ {
var pi = GetProxyInfo(); var pi = getProxyInfo();
ProxyInfo = pi; ProxyInfo = pi;
AutomaticallyDetectSettings = pi.AutoDetect == true; AutomaticallyDetectSettings = pi.AutoDetect == true;
AutomaticConfigurationScript = pi.AutoConfigUrl == null ? null : new Uri(pi.AutoConfigUrl); AutomaticConfigurationScript = pi.AutoConfigUrl == null ? null : new Uri(pi.AutoConfigUrl);
...@@ -158,7 +158,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -158,7 +158,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
proxy = new WebProxy(new Uri("http://localhost"), BypassOnLocal, pi.BypassList); proxy = new WebProxy(new Uri("http://localhost"), BypassOnLocal, pi.BypassList);
} }
private ProxyInfo GetProxyInfo() private ProxyInfo getProxyInfo()
{ {
var proxyConfig = new NativeMethods.WinHttp.WINHTTP_CURRENT_USER_IE_PROXY_CONFIG(); var proxyConfig = new NativeMethods.WinHttp.WINHTTP_CURRENT_USER_IE_PROXY_CONFIG();
RuntimeHelpers.PrepareConstrainedRegions(); RuntimeHelpers.PrepareConstrainedRegions();
...@@ -200,7 +200,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -200,7 +200,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
autoDetectFailed = false; autoDetectFailed = false;
} }
private void Dispose(bool disposing) private void dispose(bool disposing)
{ {
if (!disposing || session == null || session.IsInvalid) if (!disposing || session == null || session.IsInvalid)
{ {
...@@ -210,7 +210,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -210,7 +210,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
session.Close(); session.Close();
} }
private int GetAutoProxies(Uri destination, Uri scriptLocation, out string proxyListString) private int getAutoProxies(Uri destination, Uri scriptLocation, out string proxyListString)
{ {
int num = 0; int num = 0;
var autoProxyOptions = new NativeMethods.WinHttp.WINHTTP_AUTOPROXY_OPTIONS(); var autoProxyOptions = new NativeMethods.WinHttp.WINHTTP_AUTOPROXY_OPTIONS();
...@@ -229,16 +229,16 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -229,16 +229,16 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
autoProxyOptions.AutoDetectFlags = NativeMethods.WinHttp.AutoDetectType.None; autoProxyOptions.AutoDetectFlags = NativeMethods.WinHttp.AutoDetectType.None;
} }
if (!WinHttpGetProxyForUrl(destination.ToString(), ref autoProxyOptions, out proxyListString)) if (!winHttpGetProxyForUrl(destination.ToString(), ref autoProxyOptions, out proxyListString))
{ {
num = GetLastWin32Error(); num = getLastWin32Error();
if (num == (int)NativeMethods.WinHttp.ErrorCodes.LoginFailure && Credentials != null) if (num == (int)NativeMethods.WinHttp.ErrorCodes.LoginFailure && Credentials != null)
{ {
autoProxyOptions.AutoLogonIfChallenged = true; autoProxyOptions.AutoLogonIfChallenged = true;
if (!WinHttpGetProxyForUrl(destination.ToString(), ref autoProxyOptions, out proxyListString)) if (!winHttpGetProxyForUrl(destination.ToString(), ref autoProxyOptions, out proxyListString))
{ {
num = GetLastWin32Error(); num = getLastWin32Error();
} }
} }
} }
...@@ -246,7 +246,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -246,7 +246,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
return num; return num;
} }
private bool WinHttpGetProxyForUrl(string destination, private bool winHttpGetProxyForUrl(string destination,
ref NativeMethods.WinHttp.WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions, out string proxyListString) ref NativeMethods.WinHttp.WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions, out string proxyListString)
{ {
proxyListString = null; proxyListString = null;
...@@ -271,7 +271,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -271,7 +271,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
return flag; return flag;
} }
private static int GetLastWin32Error() private static int getLastWin32Error()
{ {
int lastWin32Error = Marshal.GetLastWin32Error(); int lastWin32Error = Marshal.GetLastWin32Error();
if (lastWin32Error == 8) if (lastWin32Error == 8)
...@@ -282,7 +282,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -282,7 +282,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
return lastWin32Error; return lastWin32Error;
} }
private static bool IsRecoverableAutoProxyError(NativeMethods.WinHttp.ErrorCodes errorCode) private static bool isRecoverableAutoProxyError(NativeMethods.WinHttp.ErrorCodes errorCode)
{ {
switch (errorCode) switch (errorCode)
{ {
...@@ -300,7 +300,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -300,7 +300,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
} }
} }
private static AutoWebProxyState GetStateFromErrorCode(NativeMethods.WinHttp.ErrorCodes errorCode) private static AutoWebProxyState getStateFromErrorCode(NativeMethods.WinHttp.ErrorCodes errorCode)
{ {
if (errorCode == 0L) if (errorCode == 0L)
{ {
...@@ -324,7 +324,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -324,7 +324,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
} }
} }
private static string RemoveWhitespaces(string value) private static string removeWhitespaces(string value)
{ {
var stringBuilder = new StringBuilder(); var stringBuilder = new StringBuilder();
foreach (char c in value) foreach (char c in value)
...@@ -338,7 +338,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp ...@@ -338,7 +338,7 @@ namespace Titanium.Web.Proxy.Helpers.WinHttp
return stringBuilder.ToString(); return stringBuilder.ToString();
} }
private static bool IsErrorFatalForAutoDetect(NativeMethods.WinHttp.ErrorCodes errorCode) private static bool isErrorFatalForAutoDetect(NativeMethods.WinHttp.ErrorCodes errorCode)
{ {
switch (errorCode) switch (errorCode)
{ {
......
...@@ -24,7 +24,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -24,7 +24,7 @@ namespace Titanium.Web.Proxy.Http
/// </summary> /// </summary>
/// <param name="buffer"></param> /// <param name="buffer"></param>
/// <param name="size"></param> /// <param name="size"></param>
private static void ResizeBuffer(ref byte[] buffer, long size) private static void resizeBuffer(ref byte[] buffer, long size)
{ {
var newBuffer = new byte[size]; var newBuffer = new byte[size];
Buffer.BlockCopy(buffer, 0, newBuffer, 0, buffer.Length); Buffer.BlockCopy(buffer, 0, newBuffer, 0, buffer.Length);
......
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network.Tcp; using Titanium.Web.Proxy.Network.Tcp;
...@@ -15,12 +16,9 @@ namespace Titanium.Web.Proxy.Http ...@@ -15,12 +16,9 @@ namespace Titanium.Web.Proxy.Http
/// </summary> /// </summary>
public class HttpWebClient public class HttpWebClient
{ {
private readonly int bufferSize;
internal HttpWebClient(int bufferSize, Request request = null, Response response = null) internal HttpWebClient(Request request = null, Response response = null)
{ {
this.bufferSize = bufferSize;
Request = request ?? new Request(); Request = request ?? new Request();
Response = response ?? new Response(); Response = response ?? new Response();
} }
...@@ -98,16 +96,16 @@ namespace Titanium.Web.Proxy.Http ...@@ -98,16 +96,16 @@ namespace Titanium.Web.Proxy.Http
await writer.WriteLineAsync(Request.CreateRequestLine(Request.Method, await writer.WriteLineAsync(Request.CreateRequestLine(Request.Method,
useUpstreamProxy || isTransparent ? Request.OriginalUrl : Request.RequestUri.PathAndQuery, useUpstreamProxy || isTransparent ? Request.OriginalUrl : Request.RequestUri.PathAndQuery,
Request.HttpVersion), cancellationToken); Request.HttpVersion), cancellationToken);
var headerBuilder = new StringBuilder();
// Send Authentication to Upstream proxy if needed // Send Authentication to Upstream proxy if needed
if (!isTransparent && upstreamProxy != null if (!isTransparent && upstreamProxy != null
&& ServerConnection.IsHttps == false && ServerConnection.IsHttps == false
&& !string.IsNullOrEmpty(upstreamProxy.UserName) && !string.IsNullOrEmpty(upstreamProxy.UserName)
&& upstreamProxy.Password != null) && upstreamProxy.Password != null)
{ {
await HttpHeader.ProxyConnectionKeepAlive.WriteToStreamAsync(writer, cancellationToken); headerBuilder.AppendLine(HttpHeader.ProxyConnectionKeepAlive.ToString());
await HttpHeader.GetProxyAuthorizationHeader(upstreamProxy.UserName, upstreamProxy.Password) headerBuilder.AppendLine(HttpHeader.GetProxyAuthorizationHeader(upstreamProxy.UserName, upstreamProxy.Password).ToString());
.WriteToStreamAsync(writer, cancellationToken);
} }
// write request headers // write request headers
...@@ -115,17 +113,30 @@ namespace Titanium.Web.Proxy.Http ...@@ -115,17 +113,30 @@ namespace Titanium.Web.Proxy.Http
{ {
if (isTransparent || header.Name != KnownHeaders.ProxyAuthorization) if (isTransparent || header.Name != KnownHeaders.ProxyAuthorization)
{ {
await header.WriteToStreamAsync(writer, cancellationToken); headerBuilder.AppendLine(header.ToString());
} }
} }
await writer.WriteLineAsync(cancellationToken); headerBuilder.AppendLine();
await writer.WriteAsync(headerBuilder.ToString(), cancellationToken);
if (enable100ContinueBehaviour) if (enable100ContinueBehaviour)
{ {
if (Request.ExpectContinue) if (Request.ExpectContinue)
{ {
string httpStatus = await ServerConnection.Stream.ReadLineAsync(cancellationToken); string httpStatus;
try
{
httpStatus = await ServerConnection.Stream.ReadLineAsync(cancellationToken);
if (httpStatus == null)
{
throw new ServerConnectionException("Server connection was closed.");
}
}
catch (Exception e) when (!(e is ServerConnectionException))
{
throw new ServerConnectionException("Server connection was closed.");
}
Response.ParseResponseLine(httpStatus, out _, out int responseStatusCode, Response.ParseResponseLine(httpStatus, out _, out int responseStatusCode,
out string responseStatusDescription); out string responseStatusDescription);
...@@ -159,10 +170,18 @@ namespace Titanium.Web.Proxy.Http ...@@ -159,10 +170,18 @@ namespace Titanium.Web.Proxy.Http
return; return;
} }
string httpStatus = await ServerConnection.Stream.ReadLineAsync(cancellationToken); string httpStatus;
if (httpStatus == null) 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) if (httpStatus == string.Empty)
...@@ -217,6 +236,9 @@ namespace Titanium.Web.Proxy.Http ...@@ -217,6 +236,9 @@ namespace Titanium.Web.Proxy.Http
ConnectRequest?.FinishSession(); ConnectRequest?.FinishSession();
Request?.FinishSession(); Request?.FinishSession();
Response?.FinishSession(); Response?.FinishSession();
Data.Clear();
UserData = null;
} }
} }
} }
...@@ -199,7 +199,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -199,7 +199,7 @@ namespace Titanium.Web.Proxy.Http
// Find the request Verb // Find the request Verb
httpMethod = httpCmdSplit[0]; httpMethod = httpCmdSplit[0];
if (!IsAllUpper(httpMethod)) if (!isAllUpper(httpMethod))
{ {
httpMethod = httpMethod.ToUpper(); httpMethod = httpMethod.ToUpper();
} }
...@@ -219,7 +219,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -219,7 +219,7 @@ namespace Titanium.Web.Proxy.Http
} }
} }
private static bool IsAllUpper(string input) private static bool isAllUpper(string input)
{ {
for (int i = 0; i < input.Length; i++) for (int i = 0; i < input.Length; i++)
{ {
......
...@@ -69,6 +69,15 @@ namespace Titanium.Web.Proxy.Models ...@@ -69,6 +69,15 @@ namespace Titanium.Web.Proxy.Models
/// </summary> /// </summary>
public int Port { get; set; } public int Port { get; set; }
/// <summary>
/// Get cache key for Tcp connection cahe.
/// </summary>
/// <returns></returns>
internal string GetCacheKey()
{
return $"{HostName}-{Port}" + (UseDefaultCredentials ? $"-{UserName}-{Password}" : string.Empty);
}
/// <summary> /// <summary>
/// returns data in Hostname:port format. /// returns data in Hostname:port format.
/// </summary> /// </summary>
...@@ -77,5 +86,6 @@ namespace Titanium.Web.Proxy.Models ...@@ -77,5 +86,6 @@ namespace Titanium.Web.Proxy.Models
{ {
return $"{HostName}:{Port}"; return $"{HostName}:{Port}";
} }
} }
} }
using System; using System;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.Models namespace Titanium.Web.Proxy.Models
...@@ -63,12 +60,5 @@ namespace Titanium.Web.Proxy.Models ...@@ -63,12 +60,5 @@ namespace Titanium.Web.Proxy.Models
"Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes($"{userName}:{password}"))); "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes($"{userName}:{password}")));
return result; return result;
} }
internal async Task WriteToStreamAsync(HttpWriter writer, CancellationToken cancellationToken)
{
await writer.WriteAsync(Name, cancellationToken);
await writer.WriteAsync(": ", cancellationToken);
await writer.WriteLineAsync(Value, cancellationToken);
}
} }
} }
...@@ -49,7 +49,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -49,7 +49,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
/// <returns>X509Certificate2 instance.</returns> /// <returns>X509Certificate2 instance.</returns>
public X509Certificate2 MakeCertificate(string sSubjectCn, bool isRoot, X509Certificate2 signingCert = null) public X509Certificate2 MakeCertificate(string sSubjectCn, bool isRoot, X509Certificate2 signingCert = null)
{ {
return MakeCertificateInternal(sSubjectCn, isRoot, true, signingCert); return makeCertificateInternal(sSubjectCn, isRoot, true, signingCert);
} }
/// <summary> /// <summary>
...@@ -65,7 +65,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -65,7 +65,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
/// <param name="hostName">The host name</param> /// <param name="hostName">The host name</param>
/// <returns>X509Certificate2 instance.</returns> /// <returns>X509Certificate2 instance.</returns>
/// <exception cref="PemException">Malformed sequence in RSA private key</exception> /// <exception cref="PemException">Malformed sequence in RSA private key</exception>
private static X509Certificate2 GenerateCertificate(string hostName, private static X509Certificate2 generateCertificate(string hostName,
string subjectName, string subjectName,
string issuerName, DateTime validFrom, string issuerName, DateTime validFrom,
DateTime validTo, int keyStrength = 2048, DateTime validTo, int keyStrength = 2048,
...@@ -145,7 +145,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -145,7 +145,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
var x509Certificate = new X509Certificate2(certificate.GetEncoded()); var x509Certificate = new X509Certificate2(certificate.GetEncoded());
x509Certificate.PrivateKey = DotNetUtilities.ToRSA(rsaparams); x509Certificate.PrivateKey = DotNetUtilities.ToRSA(rsaparams);
#else #else
var x509Certificate = WithPrivateKey(certificate, rsaparams); var x509Certificate = withPrivateKey(certificate, rsaparams);
x509Certificate.FriendlyName = subjectName; x509Certificate.FriendlyName = subjectName;
#endif #endif
...@@ -164,7 +164,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -164,7 +164,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
return x509Certificate; return x509Certificate;
} }
private static X509Certificate2 WithPrivateKey(X509Certificate certificate, AsymmetricKeyParameter privateKey) private static X509Certificate2 withPrivateKey(X509Certificate certificate, AsymmetricKeyParameter privateKey)
{ {
const string password = "password"; const string password = "password";
var store = new Pkcs12Store(); var store = new Pkcs12Store();
...@@ -194,7 +194,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -194,7 +194,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
/// You must specify a Signing Certificate if and only if you are not creating a /// You must specify a Signing Certificate if and only if you are not creating a
/// root. /// root.
/// </exception> /// </exception>
private X509Certificate2 MakeCertificateInternal(bool isRoot, private X509Certificate2 makeCertificateInternal(bool isRoot,
string hostName, string subjectName, string hostName, string subjectName,
DateTime validFrom, DateTime validTo, X509Certificate2 signingCertificate) DateTime validFrom, DateTime validTo, X509Certificate2 signingCertificate)
{ {
...@@ -207,11 +207,11 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -207,11 +207,11 @@ namespace Titanium.Web.Proxy.Network.Certificate
if (isRoot) if (isRoot)
{ {
return GenerateCertificate(null, subjectName, subjectName, validFrom, validTo); return generateCertificate(null, subjectName, subjectName, validFrom, validTo);
} }
var kp = DotNetUtilities.GetKeyPair(signingCertificate.PrivateKey); var kp = DotNetUtilities.GetKeyPair(signingCertificate.PrivateKey);
return GenerateCertificate(hostName, subjectName, signingCertificate.Subject, validFrom, validTo, return generateCertificate(hostName, subjectName, signingCertificate.Subject, validFrom, validTo,
issuerPrivateKey: kp.Private); issuerPrivateKey: kp.Private);
} }
...@@ -224,7 +224,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -224,7 +224,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
/// <param name="signingCert">The signing cert.</param> /// <param name="signingCert">The signing cert.</param>
/// <param name="cancellationToken">Task cancellation token</param> /// <param name="cancellationToken">Task cancellation token</param>
/// <returns>X509Certificate2.</returns> /// <returns>X509Certificate2.</returns>
private X509Certificate2 MakeCertificateInternal(string subject, bool isRoot, private X509Certificate2 makeCertificateInternal(string subject, bool isRoot,
bool switchToMtaIfNeeded, X509Certificate2 signingCert = null, bool switchToMtaIfNeeded, X509Certificate2 signingCert = null,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
...@@ -238,7 +238,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -238,7 +238,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
{ {
try try
{ {
certificate = MakeCertificateInternal(subject, isRoot, false, signingCert); certificate = makeCertificateInternal(subject, isRoot, false, signingCert);
} }
catch (Exception ex) catch (Exception ex)
{ {
...@@ -258,7 +258,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -258,7 +258,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
} }
#endif #endif
return MakeCertificateInternal(isRoot, subject, $"CN={subject}", return makeCertificateInternal(isRoot, subject, $"CN={subject}",
DateTime.UtcNow.AddDays(-certificateGraceDays), DateTime.UtcNow.AddDays(certificateValidDays), DateTime.UtcNow.AddDays(-certificateGraceDays), DateTime.UtcNow.AddDays(certificateValidDays),
isRoot ? null : signingCert); isRoot ? null : signingCert);
} }
......
...@@ -80,10 +80,10 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -80,10 +80,10 @@ namespace Titanium.Web.Proxy.Network.Certificate
/// <returns></returns> /// <returns></returns>
public X509Certificate2 MakeCertificate(string sSubjectCN, bool isRoot, X509Certificate2 signingCert = null) public X509Certificate2 MakeCertificate(string sSubjectCN, bool isRoot, X509Certificate2 signingCert = null)
{ {
return MakeCertificateInternal(sSubjectCN, isRoot, true, signingCert); return makeCertificateInternal(sSubjectCN, isRoot, true, signingCert);
} }
private X509Certificate2 MakeCertificate(bool isRoot, string subject, string fullSubject, private X509Certificate2 makeCertificate(bool isRoot, string subject, string fullSubject,
int privateKeyLength, string hashAlg, DateTime validFrom, DateTime validTo, int privateKeyLength, string hashAlg, DateTime validFrom, DateTime validTo,
X509Certificate2 signingCertificate) X509Certificate2 signingCertificate)
{ {
...@@ -274,13 +274,13 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -274,13 +274,13 @@ namespace Titanium.Web.Proxy.Network.Certificate
return new X509Certificate2(Convert.FromBase64String(empty), string.Empty, X509KeyStorageFlags.Exportable); return new X509Certificate2(Convert.FromBase64String(empty), string.Empty, X509KeyStorageFlags.Exportable);
} }
private X509Certificate2 MakeCertificateInternal(string sSubjectCN, bool isRoot, private X509Certificate2 makeCertificateInternal(string sSubjectCN, bool isRoot,
bool switchToMTAIfNeeded, X509Certificate2 signingCert = null, bool switchToMTAIfNeeded, X509Certificate2 signingCert = null,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
if (switchToMTAIfNeeded && Thread.CurrentThread.GetApartmentState() != ApartmentState.MTA) if (switchToMTAIfNeeded && Thread.CurrentThread.GetApartmentState() != ApartmentState.MTA)
{ {
return Task.Run(() => MakeCertificateInternal(sSubjectCN, isRoot, false, signingCert), return Task.Run(() => makeCertificateInternal(sSubjectCN, isRoot, false, signingCert),
cancellationToken).Result; cancellationToken).Result;
} }
...@@ -301,7 +301,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -301,7 +301,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
var graceTime = DateTime.Now.AddDays(graceDays); var graceTime = DateTime.Now.AddDays(graceDays);
var now = DateTime.Now; var now = DateTime.Now;
var certificate = MakeCertificate(isRoot, sSubjectCN, fullSubject, keyLength, hashAlgo, graceTime, var certificate = makeCertificate(isRoot, sSubjectCN, fullSubject, keyLength, hashAlgo, graceTime,
now.AddDays(validDays), isRoot ? null : signingCert); now.AddDays(validDays), isRoot ? null : signingCert);
return certificate; return certificate;
} }
......
...@@ -43,7 +43,6 @@ namespace Titanium.Web.Proxy.Network ...@@ -43,7 +43,6 @@ namespace Titanium.Web.Proxy.Network
/// </summary> /// </summary>
private readonly ConcurrentDictionary<string, CachedCertificate> certificateCache; private readonly ConcurrentDictionary<string, CachedCertificate> certificateCache;
private readonly ExceptionHandler exceptionFunc;
private readonly ConcurrentDictionary<string, Task<X509Certificate2>> pendingCertificateCreationTasks; private readonly ConcurrentDictionary<string, Task<X509Certificate2>> pendingCertificateCreationTasks;
private ICertificateMaker certEngine; private ICertificateMaker certEngine;
...@@ -77,7 +76,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -77,7 +76,7 @@ namespace Titanium.Web.Proxy.Network
bool userTrustRootCertificate, bool machineTrustRootCertificate, bool trustRootCertificateAsAdmin, bool userTrustRootCertificate, bool machineTrustRootCertificate, bool trustRootCertificateAsAdmin,
ExceptionHandler exceptionFunc) ExceptionHandler exceptionFunc)
{ {
this.exceptionFunc = exceptionFunc; ExceptionFunc = exceptionFunc;
UserTrustRoot = userTrustRootCertificate || machineTrustRootCertificate; UserTrustRoot = userTrustRootCertificate || machineTrustRootCertificate;
...@@ -94,14 +93,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -94,14 +93,7 @@ namespace Titanium.Web.Proxy.Network
RootCertificateIssuerName = rootCertificateIssuerName; RootCertificateIssuerName = rootCertificateIssuerName;
} }
if (RunTime.IsWindows) CertificateEngine = RunTime.IsWindows ? CertificateEngine.DefaultWindows : CertificateEngine.BouncyCastle;
{
CertificateEngine = CertificateEngine.DefaultWindows;
}
else
{
CertificateEngine = CertificateEngine.BouncyCastle;
}
certificateCache = new ConcurrentDictionary<string, CachedCertificate>(); certificateCache = new ConcurrentDictionary<string, CachedCertificate>();
pendingCertificateCreationTasks = new ConcurrentDictionary<string, Task<X509Certificate2>>(); pendingCertificateCreationTasks = new ConcurrentDictionary<string, Task<X509Certificate2>>();
...@@ -131,6 +123,11 @@ namespace Titanium.Web.Proxy.Network ...@@ -131,6 +123,11 @@ namespace Titanium.Web.Proxy.Network
/// </summary> /// </summary>
internal bool TrustRootAsAdministrator { get; set; } internal bool TrustRootAsAdministrator { get; set; }
/// <summary>
/// Exception handler
/// </summary>
internal ExceptionHandler ExceptionFunc { get; set; }
/// <summary> /// <summary>
/// Select Certificate Engine. /// Select Certificate Engine.
/// Optionally set to BouncyCastle. /// Optionally set to BouncyCastle.
...@@ -156,8 +153,8 @@ namespace Titanium.Web.Proxy.Network ...@@ -156,8 +153,8 @@ namespace Titanium.Web.Proxy.Network
if (certEngine == null) if (certEngine == null)
{ {
certEngine = engine == CertificateEngine.BouncyCastle certEngine = engine == CertificateEngine.BouncyCastle
? (ICertificateMaker)new BCCertificateMaker(exceptionFunc) ? (ICertificateMaker)new BCCertificateMaker(ExceptionFunc)
: new WinCertificateMaker(exceptionFunc); : new WinCertificateMaker(ExceptionFunc);
} }
} }
} }
...@@ -242,7 +239,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -242,7 +239,7 @@ namespace Titanium.Web.Proxy.Network
{ {
} }
private string GetRootCertificateDirectory() private string getRootCertificateDirectory()
{ {
string assemblyLocation = Assembly.GetExecutingAssembly().Location; string assemblyLocation = Assembly.GetExecutingAssembly().Location;
...@@ -261,9 +258,9 @@ namespace Titanium.Web.Proxy.Network ...@@ -261,9 +258,9 @@ namespace Titanium.Web.Proxy.Network
return path; return path;
} }
private string GetCertificatePath() private string getCertificatePath()
{ {
string path = GetRootCertificateDirectory(); string path = getRootCertificateDirectory();
string certPath = Path.Combine(path, "crts"); string certPath = Path.Combine(path, "crts");
if (!Directory.Exists(certPath)) if (!Directory.Exists(certPath))
...@@ -274,9 +271,9 @@ namespace Titanium.Web.Proxy.Network ...@@ -274,9 +271,9 @@ namespace Titanium.Web.Proxy.Network
return certPath; return certPath;
} }
private string GetRootCertificatePath() private string getRootCertificatePath()
{ {
string path = GetRootCertificateDirectory(); string path = getRootCertificateDirectory();
string fileName = PfxFilePath; string fileName = PfxFilePath;
if (fileName == string.Empty) if (fileName == string.Empty)
...@@ -293,15 +290,15 @@ namespace Titanium.Web.Proxy.Network ...@@ -293,15 +290,15 @@ namespace Titanium.Web.Proxy.Network
/// </summary> /// </summary>
/// <param name="storeLocation"></param> /// <param name="storeLocation"></param>
/// <returns></returns> /// <returns></returns>
private bool RootCertificateInstalled(StoreLocation storeLocation) private bool rootCertificateInstalled(StoreLocation storeLocation)
{ {
string value = $"{RootCertificate.Issuer}"; string value = $"{RootCertificate.Issuer}";
return FindCertificates(StoreName.Root, storeLocation, value).Count > 0 return findCertificates(StoreName.Root, storeLocation, value).Count > 0
&& (CertificateEngine != CertificateEngine.DefaultWindows && (CertificateEngine != CertificateEngine.DefaultWindows
|| FindCertificates(StoreName.My, storeLocation, value).Count > 0); || findCertificates(StoreName.My, storeLocation, value).Count > 0);
} }
private X509Certificate2Collection FindCertificates(StoreName storeName, StoreLocation storeLocation, private static X509Certificate2Collection findCertificates(StoreName storeName, StoreLocation storeLocation,
string findValue) string findValue)
{ {
var x509Store = new X509Store(storeName, storeLocation); var x509Store = new X509Store(storeName, storeLocation);
...@@ -321,11 +318,11 @@ namespace Titanium.Web.Proxy.Network ...@@ -321,11 +318,11 @@ namespace Titanium.Web.Proxy.Network
/// </summary> /// </summary>
/// <param name="storeName"></param> /// <param name="storeName"></param>
/// <param name="storeLocation"></param> /// <param name="storeLocation"></param>
private void InstallCertificate(StoreName storeName, StoreLocation storeLocation) private void installCertificate(StoreName storeName, StoreLocation storeLocation)
{ {
if (RootCertificate == null) if (RootCertificate == null)
{ {
exceptionFunc(new Exception("Could not install certificate as it is null or empty.")); ExceptionFunc(new Exception("Could not install certificate as it is null or empty."));
return; return;
} }
...@@ -340,7 +337,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -340,7 +337,7 @@ namespace Titanium.Web.Proxy.Network
} }
catch (Exception e) catch (Exception e)
{ {
exceptionFunc( ExceptionFunc(
new Exception("Failed to make system trust root certificate " new Exception("Failed to make system trust root certificate "
+ $" for {storeName}\\{storeLocation} store location. You may need admin rights.", + $" for {storeName}\\{storeLocation} store location. You may need admin rights.",
e)); e));
...@@ -357,12 +354,12 @@ namespace Titanium.Web.Proxy.Network ...@@ -357,12 +354,12 @@ namespace Titanium.Web.Proxy.Network
/// <param name="storeName"></param> /// <param name="storeName"></param>
/// <param name="storeLocation"></param> /// <param name="storeLocation"></param>
/// <param name="certificate"></param> /// <param name="certificate"></param>
private void UninstallCertificate(StoreName storeName, StoreLocation storeLocation, private void uninstallCertificate(StoreName storeName, StoreLocation storeLocation,
X509Certificate2 certificate) X509Certificate2 certificate)
{ {
if (certificate == null) if (certificate == null)
{ {
exceptionFunc(new Exception("Could not remove certificate as it is null or empty.")); ExceptionFunc(new Exception("Could not remove certificate as it is null or empty."));
return; return;
} }
...@@ -376,7 +373,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -376,7 +373,7 @@ namespace Titanium.Web.Proxy.Network
} }
catch (Exception e) catch (Exception e)
{ {
exceptionFunc( ExceptionFunc(
new Exception("Failed to remove root certificate trust " new Exception("Failed to remove root certificate trust "
+ $" for {storeLocation} store location. You may need admin rights.", e)); + $" for {storeLocation} store location. You may need admin rights.", e));
} }
...@@ -386,7 +383,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -386,7 +383,7 @@ namespace Titanium.Web.Proxy.Network
} }
} }
private X509Certificate2 MakeCertificate(string certificateName, bool isRootCertificate) private X509Certificate2 makeCertificate(string certificateName, bool isRootCertificate)
{ {
if (!isRootCertificate && RootCertificate == null) if (!isRootCertificate && RootCertificate == null)
{ {
...@@ -397,7 +394,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -397,7 +394,7 @@ namespace Titanium.Web.Proxy.Network
if (CertificateEngine == CertificateEngine.DefaultWindows) if (CertificateEngine == CertificateEngine.DefaultWindows)
{ {
Task.Run(() => UninstallCertificate(StoreName.My, StoreLocation.CurrentUser, certificate)); Task.Run(() => uninstallCertificate(StoreName.My, StoreLocation.CurrentUser, certificate));
} }
return certificate; return certificate;
...@@ -416,14 +413,14 @@ namespace Titanium.Web.Proxy.Network ...@@ -416,14 +413,14 @@ namespace Titanium.Web.Proxy.Network
{ {
if (!isRootCertificate && SaveFakeCertificates) if (!isRootCertificate && SaveFakeCertificates)
{ {
string path = GetCertificatePath(); string path = getCertificatePath();
string subjectName = ProxyConstants.CNRemoverRegex.Replace(certificateName, string.Empty); string subjectName = ProxyConstants.CNRemoverRegex.Replace(certificateName, string.Empty);
subjectName = subjectName.Replace("*", "$x$"); subjectName = subjectName.Replace("*", "$x$");
string certificatePath = Path.Combine(path, subjectName + ".pfx"); string certificatePath = Path.Combine(path, subjectName + ".pfx");
if (!File.Exists(certificatePath)) if (!File.Exists(certificatePath))
{ {
certificate = MakeCertificate(certificateName, false); certificate = makeCertificate(certificateName, false);
// store as cache // store as cache
Task.Run(() => Task.Run(() =>
...@@ -434,7 +431,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -434,7 +431,7 @@ namespace Titanium.Web.Proxy.Network
} }
catch (Exception e) catch (Exception e)
{ {
exceptionFunc(new Exception("Failed to save fake certificate.", e)); ExceptionFunc(new Exception("Failed to save fake certificate.", e));
} }
}); });
} }
...@@ -447,18 +444,18 @@ namespace Titanium.Web.Proxy.Network ...@@ -447,18 +444,18 @@ namespace Titanium.Web.Proxy.Network
catch catch
{ {
// if load failed create again // if load failed create again
certificate = MakeCertificate(certificateName, false); certificate = makeCertificate(certificateName, false);
} }
} }
} }
else else
{ {
certificate = MakeCertificate(certificateName, isRootCertificate); certificate = makeCertificate(certificateName, isRootCertificate);
} }
} }
catch (Exception e) catch (Exception e)
{ {
exceptionFunc(e); ExceptionFunc(e);
} }
return certificate; return certificate;
...@@ -568,7 +565,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -568,7 +565,7 @@ namespace Titanium.Web.Proxy.Network
} }
catch (Exception e) catch (Exception e)
{ {
exceptionFunc(e); ExceptionFunc(e);
} }
if (persistToFile && RootCertificate != null) if (persistToFile && RootCertificate != null)
...@@ -577,19 +574,19 @@ namespace Titanium.Web.Proxy.Network ...@@ -577,19 +574,19 @@ namespace Titanium.Web.Proxy.Network
{ {
try try
{ {
Directory.Delete(GetCertificatePath(), true); Directory.Delete(getCertificatePath(), true);
} }
catch catch
{ {
// ignore // ignore
} }
string fileName = GetRootCertificatePath(); string fileName = getRootCertificatePath();
File.WriteAllBytes(fileName, RootCertificate.Export(X509ContentType.Pkcs12, PfxPassword)); File.WriteAllBytes(fileName, RootCertificate.Export(X509ContentType.Pkcs12, PfxPassword));
} }
catch (Exception e) catch (Exception e)
{ {
exceptionFunc(e); ExceptionFunc(e);
} }
} }
...@@ -602,7 +599,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -602,7 +599,7 @@ namespace Titanium.Web.Proxy.Network
/// <returns></returns> /// <returns></returns>
public X509Certificate2 LoadRootCertificate() public X509Certificate2 LoadRootCertificate()
{ {
string fileName = GetRootCertificatePath(); string fileName = getRootCertificatePath();
pfxFileExists = File.Exists(fileName); pfxFileExists = File.Exists(fileName);
if (!pfxFileExists) if (!pfxFileExists)
{ {
...@@ -615,7 +612,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -615,7 +612,7 @@ namespace Titanium.Web.Proxy.Network
} }
catch (Exception e) catch (Exception e)
{ {
exceptionFunc(e); ExceptionFunc(e);
return null; return null;
} }
} }
...@@ -656,20 +653,20 @@ namespace Titanium.Web.Proxy.Network ...@@ -656,20 +653,20 @@ namespace Titanium.Web.Proxy.Network
public void TrustRootCertificate(bool machineTrusted = false) public void TrustRootCertificate(bool machineTrusted = false)
{ {
// currentUser\personal // currentUser\personal
InstallCertificate(StoreName.My, StoreLocation.CurrentUser); installCertificate(StoreName.My, StoreLocation.CurrentUser);
if (!machineTrusted) if (!machineTrusted)
{ {
// currentUser\Root // currentUser\Root
InstallCertificate(StoreName.Root, StoreLocation.CurrentUser); installCertificate(StoreName.Root, StoreLocation.CurrentUser);
} }
else else
{ {
// current system // current system
InstallCertificate(StoreName.My, StoreLocation.LocalMachine); installCertificate(StoreName.My, StoreLocation.LocalMachine);
// this adds to both currentUser\Root & currentMachine\Root // this adds to both currentUser\Root & currentMachine\Root
InstallCertificate(StoreName.Root, StoreLocation.LocalMachine); installCertificate(StoreName.Root, StoreLocation.LocalMachine);
} }
} }
...@@ -686,7 +683,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -686,7 +683,7 @@ namespace Titanium.Web.Proxy.Network
} }
// currentUser\Personal // currentUser\Personal
InstallCertificate(StoreName.My, StoreLocation.CurrentUser); installCertificate(StoreName.My, StoreLocation.CurrentUser);
string pfxFileName = Path.GetTempFileName(); string pfxFileName = Path.GetTempFileName();
File.WriteAllBytes(pfxFileName, RootCertificate.Export(X509ContentType.Pkcs12, PfxPassword)); File.WriteAllBytes(pfxFileName, RootCertificate.Export(X509ContentType.Pkcs12, PfxPassword));
...@@ -724,7 +721,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -724,7 +721,7 @@ namespace Titanium.Web.Proxy.Network
} }
catch (Exception e) catch (Exception e)
{ {
exceptionFunc(e); ExceptionFunc(e);
return false; return false;
} }
...@@ -781,7 +778,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -781,7 +778,7 @@ namespace Titanium.Web.Proxy.Network
/// </summary> /// </summary>
public bool IsRootCertificateUserTrusted() public bool IsRootCertificateUserTrusted()
{ {
return RootCertificateInstalled(StoreLocation.CurrentUser) || IsRootCertificateMachineTrusted(); return rootCertificateInstalled(StoreLocation.CurrentUser) || IsRootCertificateMachineTrusted();
} }
/// <summary> /// <summary>
...@@ -789,7 +786,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -789,7 +786,7 @@ namespace Titanium.Web.Proxy.Network
/// </summary> /// </summary>
public bool IsRootCertificateMachineTrusted() public bool IsRootCertificateMachineTrusted()
{ {
return RootCertificateInstalled(StoreLocation.LocalMachine); return rootCertificateInstalled(StoreLocation.LocalMachine);
} }
/// <summary> /// <summary>
...@@ -800,20 +797,20 @@ namespace Titanium.Web.Proxy.Network ...@@ -800,20 +797,20 @@ namespace Titanium.Web.Proxy.Network
public void RemoveTrustedRootCertificate(bool machineTrusted = false) public void RemoveTrustedRootCertificate(bool machineTrusted = false)
{ {
// currentUser\personal // currentUser\personal
UninstallCertificate(StoreName.My, StoreLocation.CurrentUser, RootCertificate); uninstallCertificate(StoreName.My, StoreLocation.CurrentUser, RootCertificate);
if (!machineTrusted) if (!machineTrusted)
{ {
// currentUser\Root // currentUser\Root
UninstallCertificate(StoreName.Root, StoreLocation.CurrentUser, RootCertificate); uninstallCertificate(StoreName.Root, StoreLocation.CurrentUser, RootCertificate);
} }
else else
{ {
// current system // current system
UninstallCertificate(StoreName.My, StoreLocation.LocalMachine, RootCertificate); uninstallCertificate(StoreName.My, StoreLocation.LocalMachine, RootCertificate);
// this adds to both currentUser\Root & currentMachine\Root // this adds to both currentUser\Root & currentMachine\Root
UninstallCertificate(StoreName.Root, StoreLocation.LocalMachine, RootCertificate); uninstallCertificate(StoreName.Root, StoreLocation.LocalMachine, RootCertificate);
} }
} }
...@@ -829,7 +826,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -829,7 +826,7 @@ namespace Titanium.Web.Proxy.Network
} }
// currentUser\Personal // currentUser\Personal
UninstallCertificate(StoreName.My, StoreLocation.CurrentUser, RootCertificate); uninstallCertificate(StoreName.My, StoreLocation.CurrentUser, RootCertificate);
var infos = new List<ProcessStartInfo>(); var infos = new List<ProcessStartInfo>();
if (!machineTrusted) if (!machineTrusted)
......
...@@ -3,6 +3,7 @@ using System; ...@@ -3,6 +3,7 @@ using System;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using StreamExtended;
using StreamExtended.Network; using StreamExtended.Network;
namespace Titanium.Web.Proxy.Network namespace Titanium.Web.Proxy.Network
...@@ -17,7 +18,8 @@ namespace Titanium.Web.Proxy.Network ...@@ -17,7 +18,8 @@ namespace Titanium.Web.Proxy.Network
private readonly FileStream fileStreamSent; private readonly FileStream fileStreamSent;
public DebugCustomBufferedStream(Guid connectionId, string type, Stream baseStream, int bufferSize, bool leaveOpen = false) : base(baseStream, bufferSize, leaveOpen) public DebugCustomBufferedStream(Guid connectionId, string type, Stream baseStream, IBufferPool bufferPool, int bufferSize, bool leaveOpen = false)
: base(baseStream, bufferPool, bufferSize, leaveOpen)
{ {
Counter = Interlocked.Increment(ref counter); Counter = Interlocked.Increment(ref counter);
fileStreamSent = new FileStream(Path.Combine(basePath, $"{connectionId}_{type}_{Counter}_sent.dat"), FileMode.Create); fileStreamSent = new FileStream(Path.Combine(basePath, $"{connectionId}_{type}_{Counter}_sent.dat"), FileMode.Create);
......
using System; using System;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Net.Security;
using System.Net.Sockets; using System.Net.Sockets;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
...@@ -41,9 +40,8 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -41,9 +40,8 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{ {
tcpClient.CloseSocket();
proxyServer.UpdateClientConnectionCount(false); proxyServer.UpdateClientConnectionCount(false);
tcpClient.CloseSocket();
} }
} }
} }
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Net; using System.Net;
using System.Net.Security; using System.Net.Security;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended.Network; using StreamExtended.Network;
...@@ -14,26 +17,223 @@ using Titanium.Web.Proxy.Models; ...@@ -14,26 +17,223 @@ using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Network.Tcp namespace Titanium.Web.Proxy.Network.Tcp
{ {
/// <summary> /// <summary>
/// A class that manages Tcp Connection to server used by this proxy server /// A class that manages Tcp Connection to server used by this proxy server.
/// </summary> /// </summary>
internal class TcpConnectionFactory internal class TcpConnectionFactory : IDisposable
{ {
//Tcp server connection pool cache
private readonly ConcurrentDictionary<string, ConcurrentQueue<TcpServerConnection>> cache
= new ConcurrentDictionary<string, ConcurrentQueue<TcpServerConnection>>();
//Tcp connections waiting to be disposed by cleanup task
private readonly ConcurrentBag<TcpServerConnection> disposalBag =
new ConcurrentBag<TcpServerConnection>();
//cache object race operations lock
private readonly SemaphoreSlim @lock = new SemaphoreSlim(1);
private volatile bool runCleanUpTask = true;
internal TcpConnectionFactory(ProxyServer server)
{
this.server = server;
Task.Run(async () => await clearOutdatedConnections());
}
internal ProxyServer server { get; set; }
internal string GetConnectionCacheKey(string remoteHostName, int remotePort,
Version httpVersion, bool isHttps, List<SslApplicationProtocol> applicationProtocols, bool isConnect,
ProxyServer proxyServer, IPEndPoint upStreamEndPoint, ExternalProxy externalProxy)
{
var cacheKeyBuilder = new StringBuilder($"{remoteHostName}-{remotePort}" +
$"-{(httpVersion == null ? string.Empty : httpVersion.ToString())}" +
$"-{isHttps}-{isConnect}-");
if (applicationProtocols != null)
{
foreach (var protocol in applicationProtocols)
{
cacheKeyBuilder.Append($"{protocol}-");
}
}
cacheKeyBuilder.Append(upStreamEndPoint != null
? $"{upStreamEndPoint.Address}-{upStreamEndPoint.Port}-"
: string.Empty);
cacheKeyBuilder.Append(externalProxy != null ? $"{externalProxy.GetCacheKey()}-" : string.Empty);
return cacheKeyBuilder.ToString();
}
/// <summary>
/// Gets a TCP connection to server from connection pool.
/// </summary>
/// <param name="remoteHostName">The remote hostname.</param>
/// <param name="remotePort">The remote port.</param>
/// <param name="httpVersion">The http version to use.</param>
/// <param name="isHttps">Is this a HTTPS request.</param>
/// <param name="applicationProtocols">The list of HTTPS application level protocol to negotiate if needed.</param>
/// <param name="isConnect">Is this a CONNECT request.</param>
/// <param name="proxyServer">The current ProxyServer instance.</param>
/// <param name="upStreamEndPoint">The local upstream endpoint to make request via.</param>
/// <param name="externalProxy">The external proxy to make request via.</param>
/// <param name="cancellationToken">The cancellation token for this async task.</param>
/// <returns></returns>
internal async Task<TcpServerConnection> GetClient(string remoteHostName, int remotePort,
Version httpVersion, bool isHttps, List<SslApplicationProtocol> applicationProtocols, bool isConnect,
ProxyServer proxyServer, IPEndPoint upStreamEndPoint, ExternalProxy externalProxy,
CancellationToken cancellationToken)
{
var cacheKey = GetConnectionCacheKey(remoteHostName, remotePort,
httpVersion, isHttps, applicationProtocols, isConnect,
proxyServer, upStreamEndPoint, externalProxy);
if (proxyServer.EnableConnectionPool)
{
if (cache.TryGetValue(cacheKey, out var existingConnections))
{
while (existingConnections.TryDequeue(out var recentConnection))
{
//+3 seconds for potential delay after getting connection
var cutOff = DateTime.Now.AddSeconds(-1 * proxyServer.ConnectionTimeOutSeconds + 3);
if (recentConnection.LastAccess > cutOff
&& isGoodConnection(recentConnection.TcpClient))
{
return recentConnection;
}
disposalBag.Add(recentConnection);
}
}
}
var connection = await createClient(remoteHostName, remotePort, httpVersion, isHttps,
applicationProtocols, isConnect, proxyServer, upStreamEndPoint, externalProxy, cancellationToken);
connection.CacheKey = cacheKey;
return connection;
}
/// <summary>
/// Release connection back to cache.
/// </summary>
/// <param name="connection">The Tcp server connection to return.</param>
/// <param name="close">Should we just close the connection instead of reusing?</param>
internal async Task Release(TcpServerConnection connection, bool close = false)
{
if (connection == null)
{
return;
}
if (close || connection.IsWinAuthenticated || !server.EnableConnectionPool)
{
disposalBag.Add(connection);
return;
}
connection.LastAccess = DateTime.Now;
try
{
await @lock.WaitAsync();
while (true)
{
if (cache.TryGetValue(connection.CacheKey, out var existingConnections))
{
while (existingConnections.Count >= server.MaxCachedConnections)
{
if (existingConnections.TryDequeue(out var staleConnection))
{
disposalBag.Add(staleConnection);
}
}
existingConnections.Enqueue(connection);
break;
}
if (cache.TryAdd(connection.CacheKey,
new ConcurrentQueue<TcpServerConnection>(new[] { connection })))
{
break;
}
}
}
finally
{
@lock.Release();
}
}
private async Task clearOutdatedConnections()
{
while (runCleanUpTask)
{
foreach (var item in cache)
{
var queue = item.Value;
while (queue.TryDequeue(out var connection))
{
var cutOff = DateTime.Now.AddSeconds(-1 * server.ConnectionTimeOutSeconds);
if (!server.EnableConnectionPool
|| connection.LastAccess < cutOff)
{
disposalBag.Add(connection);
continue;
}
queue.Enqueue(connection);
break;
}
}
try
{
await @lock.WaitAsync();
//clear empty queues
var emptyKeys = cache.Where(x => x.Value.Count == 0).Select(x => x.Key).ToList();
foreach (string key in emptyKeys)
{
cache.TryRemove(key, out var _);
}
}
finally
{
@lock.Release();
}
while (disposalBag.TryTake(out var connection))
{
connection?.Dispose();
}
//cleanup every ten seconds by default
await Task.Delay(1000 * 10);
}
}
/// <summary> /// <summary>
/// Creates a TCP connection to server /// Creates a TCP connection to server
/// </summary> /// </summary>
/// <param name="remoteHostName"></param> /// <param name="remoteHostName">The remote hostname.</param>
/// <param name="remotePort"></param> /// <param name="remotePort">The remote port.</param>
/// <param name="httpVersion"></param> /// <param name="httpVersion">The http version to use.</param>
/// <param name="decryptSsl"></param> /// <param name="isHttps">Is this a HTTPS request.</param>
/// <param name="applicationProtocols"></param> /// <param name="applicationProtocols">The list of HTTPS application level protocol to negotiate if needed.</param>
/// <param name="isConnect"></param> /// <param name="isConnect">Is this a CONNECT request.</param>
/// <param name="proxyServer"></param> /// <param name="proxyServer">The current ProxyServer instance.</param>
/// <param name="upStreamEndPoint"></param> /// <param name="upStreamEndPoint">The local upstream endpoint to make request via.</param>
/// <param name="externalProxy"></param> /// <param name="externalProxy">The external proxy to make request via.</param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken">The cancellation token for this async task.</param>
/// <returns></returns> /// <returns></returns>
internal async Task<TcpServerConnection> CreateClient(string remoteHostName, int remotePort, private async Task<TcpServerConnection> createClient(string remoteHostName, int remotePort,
Version httpVersion, bool decryptSsl, List<SslApplicationProtocol> applicationProtocols, bool isConnect, Version httpVersion, bool isHttps, List<SslApplicationProtocol> applicationProtocols, bool isConnect,
ProxyServer proxyServer, IPEndPoint upStreamEndPoint, ExternalProxy externalProxy, ProxyServer proxyServer, IPEndPoint upStreamEndPoint, ExternalProxy externalProxy,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
...@@ -59,7 +259,15 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -59,7 +259,15 @@ namespace Titanium.Web.Proxy.Network.Tcp
try try
{ {
tcpClient = new TcpClient(upStreamEndPoint); tcpClient = new TcpClient(upStreamEndPoint)
{
ReceiveTimeout = proxyServer.ConnectionTimeOutSeconds * 1000,
SendTimeout = proxyServer.ConnectionTimeOutSeconds * 1000,
SendBufferSize = proxyServer.BufferSize,
ReceiveBufferSize = proxyServer.BufferSize
};
await proxyServer.InvokeConnectionCreateEvent(tcpClient, false);
// If this proxy uses another external proxy then create a tunnel request for HTTP/HTTPS connections // If this proxy uses another external proxy then create a tunnel request for HTTP/HTTPS connections
if (useUpstreamProxy) if (useUpstreamProxy)
...@@ -71,11 +279,11 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -71,11 +279,11 @@ namespace Titanium.Web.Proxy.Network.Tcp
await tcpClient.ConnectAsync(remoteHostName, remotePort); await tcpClient.ConnectAsync(remoteHostName, remotePort);
} }
stream = new CustomBufferedStream(tcpClient.GetStream(), proxyServer.BufferSize); stream = new CustomBufferedStream(tcpClient.GetStream(), proxyServer.BufferPool, proxyServer.BufferSize);
if (useUpstreamProxy && (isConnect || decryptSsl)) if (useUpstreamProxy && (isConnect || isHttps))
{ {
var writer = new HttpRequestWriter(stream, proxyServer.BufferSize); var writer = new HttpRequestWriter(stream, proxyServer.BufferPool, proxyServer.BufferSize);
var connectRequest = new ConnectRequest var connectRequest = new ConnectRequest
{ {
OriginalUrl = $"{remoteHostName}:{remotePort}", OriginalUrl = $"{remoteHostName}:{remotePort}",
...@@ -106,26 +314,25 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -106,26 +314,25 @@ namespace Titanium.Web.Proxy.Network.Tcp
await stream.ReadAndIgnoreAllLinesAsync(cancellationToken); await stream.ReadAndIgnoreAllLinesAsync(cancellationToken);
} }
if (decryptSsl) if (isHttps)
{ {
var sslStream = new SslStream(stream, false, proxyServer.ValidateServerCertificate, var sslStream = new SslStream(stream, false, proxyServer.ValidateServerCertificate,
proxyServer.SelectClientCertificate); proxyServer.SelectClientCertificate);
stream = new CustomBufferedStream(sslStream, proxyServer.BufferSize); stream = new CustomBufferedStream(sslStream, proxyServer.BufferPool, proxyServer.BufferSize);
var options = new SslClientAuthenticationOptions(); var options = new SslClientAuthenticationOptions
options.ApplicationProtocols = applicationProtocols; {
options.TargetHost = remoteHostName; ApplicationProtocols = applicationProtocols,
options.ClientCertificates = null; TargetHost = remoteHostName,
options.EnabledSslProtocols = proxyServer.SupportedSslProtocols; ClientCertificates = null,
options.CertificateRevocationCheckMode = proxyServer.CheckCertificateRevocation; EnabledSslProtocols = proxyServer.SupportedSslProtocols,
CertificateRevocationCheckMode = proxyServer.CheckCertificateRevocation
};
await sslStream.AuthenticateAsClientAsync(options, cancellationToken); await sslStream.AuthenticateAsClientAsync(options, cancellationToken);
#if NETCOREAPP2_1 #if NETCOREAPP2_1
negotiatedApplicationProtocol = sslStream.NegotiatedApplicationProtocol; negotiatedApplicationProtocol = sslStream.NegotiatedApplicationProtocol;
#endif #endif
} }
tcpClient.ReceiveTimeout = proxyServer.ConnectionTimeOutSeconds * 1000;
tcpClient.SendTimeout = proxyServer.ConnectionTimeOutSeconds * 1000;
} }
catch (Exception) catch (Exception)
{ {
...@@ -140,13 +347,88 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -140,13 +347,88 @@ namespace Titanium.Web.Proxy.Network.Tcp
UpStreamEndPoint = upStreamEndPoint, UpStreamEndPoint = upStreamEndPoint,
HostName = remoteHostName, HostName = remoteHostName,
Port = remotePort, Port = remotePort,
IsHttps = decryptSsl, IsHttps = isHttps,
NegotiatedApplicationProtocol = negotiatedApplicationProtocol, NegotiatedApplicationProtocol = negotiatedApplicationProtocol,
UseUpstreamProxy = useUpstreamProxy, UseUpstreamProxy = useUpstreamProxy,
StreamWriter = new HttpRequestWriter(stream, proxyServer.BufferSize), StreamWriter = new HttpRequestWriter(stream, proxyServer.BufferPool, proxyServer.BufferSize),
Stream = stream, Stream = stream,
Version = httpVersion Version = httpVersion
}; };
} }
/// <summary>
/// Check if a TcpClient is good to be used.
/// This only checks if send is working so local socket is still connected.
/// Receive can only be verified by doing a valid read from server without exceptions.
/// So in our case we should retry with new connection from pool if first read after getting the connection fails.
/// https://msdn.microsoft.com/en-us/library/system.net.sockets.socket.connected(v=vs.110).aspx
/// </summary>
/// <param name="client"></param>
/// <returns></returns>
private static bool isGoodConnection(TcpClient client)
{
var socket = client.Client;
if (!client.Connected || !socket.Connected)
{
return false;
}
// This is how you can determine whether a socket is still connected.
bool blockingState = socket.Blocking;
try
{
var tmp = new byte[1];
socket.Blocking = false;
socket.Send(tmp, 0, 0);
//Connected.
}
catch
{
//Should we let 10035 == WSAEWOULDBLOCK as valid connection?
return false;
}
finally
{
socket.Blocking = blockingState;
}
return true;
}
public void Dispose()
{
runCleanUpTask = false;
try
{
@lock.Wait();
foreach (var queue in cache.Select(x => x.Value).ToList())
{
while (!queue.IsEmpty)
{
if (queue.TryDequeue(out var connection))
{
disposalBag.Add(connection);
}
}
}
cache.Clear();
}
finally
{
@lock.Release();
}
while (!disposalBag.IsEmpty)
{
if (disposalBag.TryTake(out var connection))
{
connection?.Dispose();
}
}
}
} }
} }
using System; using System;
using System.Net; using System.Net;
using System.Net.Security;
using System.Net.Sockets; using System.Net.Sockets;
using StreamExtended.Network; using StreamExtended.Network;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
...@@ -48,6 +47,11 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -48,6 +47,11 @@ namespace Titanium.Web.Proxy.Network.Tcp
private readonly TcpClient tcpClient; private readonly TcpClient tcpClient;
/// <summary>
/// The TcpClient.
/// </summary>
internal TcpClient TcpClient => tcpClient;
/// <summary> /// <summary>
/// Used to write lines to server /// Used to write lines to server
/// </summary> /// </summary>
...@@ -63,16 +67,24 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -63,16 +67,24 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// </summary> /// </summary>
internal DateTime LastAccess { get; set; } internal DateTime LastAccess { get; set; }
/// <summary>
/// The cache key used to uniquely identify this connection properties
/// </summary>
internal string CacheKey { get; set; }
/// <summary>
/// Is this connection authenticated via WinAuth
/// </summary>
internal bool IsWinAuthenticated { get; set; }
/// <summary> /// <summary>
/// Dispose. /// Dispose.
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{ {
proxyServer.UpdateServerConnectionCount(false);
Stream?.Dispose(); Stream?.Dispose();
tcpClient.CloseSocket(); tcpClient.CloseSocket();
proxyServer.UpdateServerConnectionCount(false);
} }
} }
} }
...@@ -18,7 +18,7 @@ namespace Titanium.Web.Proxy ...@@ -18,7 +18,7 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
/// <param name="session">The session event arguments.</param> /// <param name="session">The session event arguments.</param>
/// <returns>True if authorized.</returns> /// <returns>True if authorized.</returns>
private async Task<bool> CheckAuthorization(SessionEventArgsBase session) private async Task<bool> checkAuthorization(SessionEventArgsBase session)
{ {
// If we are not authorizing clients return true // If we are not authorizing clients return true
if (AuthenticateUserFunc == null) if (AuthenticateUserFunc == null)
...@@ -33,7 +33,7 @@ namespace Titanium.Web.Proxy ...@@ -33,7 +33,7 @@ namespace Titanium.Web.Proxy
var header = httpHeaders.GetFirstHeader(KnownHeaders.ProxyAuthorization); var header = httpHeaders.GetFirstHeader(KnownHeaders.ProxyAuthorization);
if (header == null) if (header == null)
{ {
session.WebSession.Response = CreateAuthentication407Response("Proxy Authentication Required"); session.WebSession.Response = createAuthentication407Response("Proxy Authentication Required");
return false; return false;
} }
...@@ -42,7 +42,7 @@ namespace Titanium.Web.Proxy ...@@ -42,7 +42,7 @@ namespace Titanium.Web.Proxy
!headerValueParts[0].EqualsIgnoreCase(KnownHeaders.ProxyAuthorizationBasic)) !headerValueParts[0].EqualsIgnoreCase(KnownHeaders.ProxyAuthorizationBasic))
{ {
// Return not authorized // Return not authorized
session.WebSession.Response = CreateAuthentication407Response("Proxy Authentication Invalid"); session.WebSession.Response = createAuthentication407Response("Proxy Authentication Invalid");
return false; return false;
} }
...@@ -51,7 +51,7 @@ namespace Titanium.Web.Proxy ...@@ -51,7 +51,7 @@ namespace Titanium.Web.Proxy
if (colonIndex == -1) if (colonIndex == -1)
{ {
// Return not authorized // Return not authorized
session.WebSession.Response = CreateAuthentication407Response("Proxy Authentication Invalid"); session.WebSession.Response = createAuthentication407Response("Proxy Authentication Invalid");
return false; return false;
} }
...@@ -60,7 +60,7 @@ namespace Titanium.Web.Proxy ...@@ -60,7 +60,7 @@ namespace Titanium.Web.Proxy
bool authenticated = await AuthenticateUserFunc(username, password); bool authenticated = await AuthenticateUserFunc(username, password);
if (!authenticated) if (!authenticated)
{ {
session.WebSession.Response = CreateAuthentication407Response("Proxy Authentication Invalid"); session.WebSession.Response = createAuthentication407Response("Proxy Authentication Invalid");
} }
return authenticated; return authenticated;
...@@ -71,7 +71,7 @@ namespace Titanium.Web.Proxy ...@@ -71,7 +71,7 @@ namespace Titanium.Web.Proxy
httpHeaders)); httpHeaders));
// Return not authorized // Return not authorized
session.WebSession.Response = CreateAuthentication407Response("Proxy Authentication Invalid"); session.WebSession.Response = createAuthentication407Response("Proxy Authentication Invalid");
return false; return false;
} }
} }
...@@ -81,7 +81,7 @@ namespace Titanium.Web.Proxy ...@@ -81,7 +81,7 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
/// <param name="description">Response description.</param> /// <param name="description">Response description.</param>
/// <returns></returns> /// <returns></returns>
private Response CreateAuthentication407Response(string description) private Response createAuthentication407Response(string description)
{ {
var response = new Response var response = new Response
{ {
......
...@@ -7,6 +7,7 @@ using System.Security.Authentication; ...@@ -7,6 +7,7 @@ using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended;
using StreamExtended.Network; using StreamExtended.Network;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
...@@ -15,7 +16,6 @@ using Titanium.Web.Proxy.Helpers.WinHttp; ...@@ -15,7 +16,6 @@ using Titanium.Web.Proxy.Helpers.WinHttp;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network; using Titanium.Web.Proxy.Network;
using Titanium.Web.Proxy.Network.Tcp; using Titanium.Web.Proxy.Network.Tcp;
using Titanium.Web.Proxy.Network.WinAuth.Security;
namespace Titanium.Web.Proxy namespace Titanium.Web.Proxy
{ {
...@@ -97,8 +97,13 @@ namespace Titanium.Web.Proxy ...@@ -97,8 +97,13 @@ namespace Titanium.Web.Proxy
// default values // default values
ConnectionTimeOutSeconds = 60; ConnectionTimeOutSeconds = 60;
if (BufferPool == null)
{
BufferPool = new DefaultBufferPool();
}
ProxyEndPoints = new List<ProxyEndPoint>(); ProxyEndPoints = new List<ProxyEndPoint>();
tcpConnectionFactory = new TcpConnectionFactory(); tcpConnectionFactory = new TcpConnectionFactory(this);
if (!RunTime.IsRunningOnMono && RunTime.IsWindows) if (!RunTime.IsRunningOnMono && RunTime.IsWindows)
{ {
systemProxySettingsManager = new SystemProxyManager(); systemProxySettingsManager = new SystemProxyManager();
...@@ -149,6 +154,12 @@ namespace Titanium.Web.Proxy ...@@ -149,6 +154,12 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
public bool Enable100ContinueBehaviour { get; set; } public bool Enable100ContinueBehaviour { get; set; }
/// <summary>
/// Should we enable experimental Tcp server connection pool?
/// Defaults to false.
/// </summary>
public bool EnableConnectionPool { get; set; }
/// <summary> /// <summary>
/// Buffer size used throughout this proxy. /// Buffer size used throughout this proxy.
/// </summary> /// </summary>
...@@ -159,6 +170,14 @@ namespace Titanium.Web.Proxy ...@@ -159,6 +170,14 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
public int ConnectionTimeOutSeconds { get; set; } public int ConnectionTimeOutSeconds { get; set; }
/// <summary>
/// Maximum number of concurrent connections per remote host in cache.
/// Only valid when connection pooling is enabled.
/// Default value is 3.
/// </summary>
public int MaxCachedConnections { get; set; } = 3;
/// <summary> /// <summary>
/// Total number of active client connections. /// Total number of active client connections.
/// </summary> /// </summary>
...@@ -183,6 +202,11 @@ namespace Titanium.Web.Proxy ...@@ -183,6 +202,11 @@ namespace Titanium.Web.Proxy
#endif #endif
SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12; SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12;
/// <summary>
/// The buffer pool used throughout this proxy instance.
/// </summary>
public IBufferPool BufferPool { get; set; }
/// <summary> /// <summary>
/// Manages certificates used by this proxy. /// Manages certificates used by this proxy.
/// </summary> /// </summary>
...@@ -221,7 +245,10 @@ namespace Titanium.Web.Proxy ...@@ -221,7 +245,10 @@ namespace Titanium.Web.Proxy
public ExceptionHandler ExceptionFunc public ExceptionHandler ExceptionFunc
{ {
get => exceptionFunc ?? defaultExceptionFunc; get => exceptionFunc ?? defaultExceptionFunc;
set => exceptionFunc = value; set {
exceptionFunc = value;
CertificateManager.ExceptionFunc = value;
}
} }
/// <summary> /// <summary>
...@@ -240,8 +267,9 @@ namespace Titanium.Web.Proxy ...@@ -240,8 +267,9 @@ namespace Titanium.Web.Proxy
{ {
Stop(); Stop();
} }
CertificateManager?.Dispose(); CertificateManager?.Dispose();
BufferPool?.Dispose();
} }
/// <summary> /// <summary>
...@@ -279,6 +307,16 @@ namespace Titanium.Web.Proxy ...@@ -279,6 +307,16 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
public event AsyncEventHandler<SessionEventArgs> AfterResponse; public event AsyncEventHandler<SessionEventArgs> AfterResponse;
/// <summary>
/// Customize TcpClient used for client connection upon create.
/// </summary>
public event AsyncEventHandler<TcpClient> OnClientConnectionCreate;
/// <summary>
/// Customize TcpClient used for server connection upon create.
/// </summary>
public event AsyncEventHandler<TcpClient> OnServerConnectionCreate;
/// <summary> /// <summary>
/// Add a proxy end point. /// Add a proxy end point.
/// </summary> /// </summary>
...@@ -295,7 +333,7 @@ namespace Titanium.Web.Proxy ...@@ -295,7 +333,7 @@ namespace Titanium.Web.Proxy
if (ProxyRunning) if (ProxyRunning)
{ {
Listen(endPoint); listen(endPoint);
} }
} }
...@@ -315,7 +353,7 @@ namespace Titanium.Web.Proxy ...@@ -315,7 +353,7 @@ namespace Titanium.Web.Proxy
if (ProxyRunning) if (ProxyRunning)
{ {
QuitListen(endPoint); quitListen(endPoint);
} }
} }
...@@ -349,7 +387,7 @@ namespace Titanium.Web.Proxy ...@@ -349,7 +387,7 @@ namespace Titanium.Web.Proxy
throw new Exception("Mono Runtime do not support system proxy settings."); throw new Exception("Mono Runtime do not support system proxy settings.");
} }
ValidateEndPointAsSystemProxy(endPoint); validateEndPointAsSystemProxy(endPoint);
bool isHttp = (protocolType & ProxyProtocolType.Http) > 0; bool isHttp = (protocolType & ProxyProtocolType.Http) > 0;
bool isHttps = (protocolType & ProxyProtocolType.Https) > 0; bool isHttps = (protocolType & ProxyProtocolType.Https) > 0;
...@@ -504,7 +542,7 @@ namespace Titanium.Web.Proxy ...@@ -504,7 +542,7 @@ namespace Titanium.Web.Proxy
systemProxyResolver = new WinHttpWebProxyFinder(); systemProxyResolver = new WinHttpWebProxyFinder();
systemProxyResolver.LoadFromIE(); systemProxyResolver.LoadFromIE();
GetCustomUpStreamProxyFunc = GetSystemUpStreamProxy; GetCustomUpStreamProxyFunc = getSystemUpStreamProxy;
} }
ProxyRunning = true; ProxyRunning = true;
...@@ -513,7 +551,7 @@ namespace Titanium.Web.Proxy ...@@ -513,7 +551,7 @@ namespace Titanium.Web.Proxy
foreach (var endPoint in ProxyEndPoints) foreach (var endPoint in ProxyEndPoints)
{ {
Listen(endPoint); listen(endPoint);
} }
} }
...@@ -540,12 +578,13 @@ namespace Titanium.Web.Proxy ...@@ -540,12 +578,13 @@ namespace Titanium.Web.Proxy
foreach (var endPoint in ProxyEndPoints) foreach (var endPoint in ProxyEndPoints)
{ {
QuitListen(endPoint); quitListen(endPoint);
} }
ProxyEndPoints.Clear(); ProxyEndPoints.Clear();
CertificateManager?.StopClearIdleCertificates(); CertificateManager?.StopClearIdleCertificates();
tcpConnectionFactory.Dispose();
ProxyRunning = false; ProxyRunning = false;
} }
...@@ -554,7 +593,7 @@ namespace Titanium.Web.Proxy ...@@ -554,7 +593,7 @@ namespace Titanium.Web.Proxy
/// Listen on given end point of local machine. /// Listen on given end point of local machine.
/// </summary> /// </summary>
/// <param name="endPoint">The end point to listen.</param> /// <param name="endPoint">The end point to listen.</param>
private void Listen(ProxyEndPoint endPoint) private void listen(ProxyEndPoint endPoint)
{ {
endPoint.Listener = new TcpListener(endPoint.IpAddress, endPoint.Port); endPoint.Listener = new TcpListener(endPoint.IpAddress, endPoint.Port);
try try
...@@ -564,7 +603,7 @@ namespace Titanium.Web.Proxy ...@@ -564,7 +603,7 @@ namespace Titanium.Web.Proxy
endPoint.Port = ((IPEndPoint)endPoint.Listener.LocalEndpoint).Port; endPoint.Port = ((IPEndPoint)endPoint.Listener.LocalEndpoint).Port;
// accept clients asynchronously // accept clients asynchronously
endPoint.Listener.BeginAcceptTcpClient(OnAcceptConnection, endPoint); endPoint.Listener.BeginAcceptTcpClient(onAcceptConnection, endPoint);
} }
catch (SocketException ex) catch (SocketException ex)
{ {
...@@ -580,7 +619,7 @@ namespace Titanium.Web.Proxy ...@@ -580,7 +619,7 @@ namespace Titanium.Web.Proxy
/// Verify if its safe to set this end point as system proxy. /// Verify if its safe to set this end point as system proxy.
/// </summary> /// </summary>
/// <param name="endPoint">The end point to validate.</param> /// <param name="endPoint">The end point to validate.</param>
private void ValidateEndPointAsSystemProxy(ExplicitProxyEndPoint endPoint) private void validateEndPointAsSystemProxy(ExplicitProxyEndPoint endPoint)
{ {
if (endPoint == null) if (endPoint == null)
{ {
...@@ -603,7 +642,7 @@ namespace Titanium.Web.Proxy ...@@ -603,7 +642,7 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
/// <param name="sessionEventArgs">The session.</param> /// <param name="sessionEventArgs">The session.</param>
/// <returns>The external proxy as task result.</returns> /// <returns>The external proxy as task result.</returns>
private Task<ExternalProxy> GetSystemUpStreamProxy(SessionEventArgsBase sessionEventArgs) private Task<ExternalProxy> getSystemUpStreamProxy(SessionEventArgsBase sessionEventArgs)
{ {
var proxy = systemProxyResolver.GetProxy(sessionEventArgs.WebSession.Request.RequestUri); var proxy = systemProxyResolver.GetProxy(sessionEventArgs.WebSession.Request.RequestUri);
return Task.FromResult(proxy); return Task.FromResult(proxy);
...@@ -612,7 +651,7 @@ namespace Titanium.Web.Proxy ...@@ -612,7 +651,7 @@ namespace Titanium.Web.Proxy
/// <summary> /// <summary>
/// Act when a connection is received from client. /// Act when a connection is received from client.
/// </summary> /// </summary>
private void OnAcceptConnection(IAsyncResult asyn) private void onAcceptConnection(IAsyncResult asyn)
{ {
var endPoint = (ProxyEndPoint)asyn.AsyncState; var endPoint = (ProxyEndPoint)asyn.AsyncState;
...@@ -637,11 +676,11 @@ namespace Titanium.Web.Proxy ...@@ -637,11 +676,11 @@ namespace Titanium.Web.Proxy
if (tcpClient != null) if (tcpClient != null)
{ {
Task.Run(async () => { await HandleClient(tcpClient, endPoint); }); Task.Run(async () => { await handleClient(tcpClient, endPoint); });
} }
// Get the listener that handles the client request. // Get the listener that handles the client request.
endPoint.Listener.BeginAcceptTcpClient(OnAcceptConnection, endPoint); endPoint.Listener.BeginAcceptTcpClient(onAcceptConnection, endPoint);
} }
/// <summary> /// <summary>
...@@ -650,20 +689,24 @@ namespace Titanium.Web.Proxy ...@@ -650,20 +689,24 @@ namespace Titanium.Web.Proxy
/// <param name="tcpClient">The client.</param> /// <param name="tcpClient">The client.</param>
/// <param name="endPoint">The proxy endpoint.</param> /// <param name="endPoint">The proxy endpoint.</param>
/// <returns>The task.</returns> /// <returns>The task.</returns>
private async Task HandleClient(TcpClient tcpClient, ProxyEndPoint endPoint) private async Task handleClient(TcpClient tcpClient, ProxyEndPoint endPoint)
{ {
tcpClient.ReceiveTimeout = ConnectionTimeOutSeconds * 1000; tcpClient.ReceiveTimeout = ConnectionTimeOutSeconds * 1000;
tcpClient.SendTimeout = ConnectionTimeOutSeconds * 1000; tcpClient.SendTimeout = ConnectionTimeOutSeconds * 1000;
tcpClient.SendBufferSize = BufferSize;
tcpClient.ReceiveBufferSize = BufferSize;
await InvokeConnectionCreateEvent(tcpClient, true);
using (var clientConnection = new TcpClientConnection(this, tcpClient)) using (var clientConnection = new TcpClientConnection(this, tcpClient))
{ {
if (endPoint is TransparentProxyEndPoint tep) if (endPoint is TransparentProxyEndPoint tep)
{ {
await HandleClient(tep, clientConnection); await handleClient(tep, clientConnection);
} }
else else
{ {
await HandleClient((ExplicitProxyEndPoint)endPoint, clientConnection); await handleClient((ExplicitProxyEndPoint)endPoint, clientConnection);
} }
} }
} }
...@@ -673,7 +716,7 @@ namespace Titanium.Web.Proxy ...@@ -673,7 +716,7 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
/// <param name="clientStream">The client stream.</param> /// <param name="clientStream">The client stream.</param>
/// <param name="exception">The exception.</param> /// <param name="exception">The exception.</param>
private void OnException(CustomBufferedStream clientStream, Exception exception) private void onException(CustomBufferedStream clientStream, Exception exception)
{ {
#if DEBUG #if DEBUG
if (clientStream is DebugCustomBufferedStream debugStream) if (clientStream is DebugCustomBufferedStream debugStream)
...@@ -688,7 +731,7 @@ namespace Titanium.Web.Proxy ...@@ -688,7 +731,7 @@ namespace Titanium.Web.Proxy
/// <summary> /// <summary>
/// Quit listening on the given end point. /// Quit listening on the given end point.
/// </summary> /// </summary>
private void QuitListen(ProxyEndPoint endPoint) private void quitListen(ProxyEndPoint endPoint)
{ {
endPoint.Listener.Stop(); endPoint.Listener.Stop();
endPoint.Listener.Server.Dispose(); endPoint.Listener.Server.Dispose();
...@@ -729,5 +772,26 @@ namespace Titanium.Web.Proxy ...@@ -729,5 +772,26 @@ namespace Titanium.Web.Proxy
ServerConnectionCountChanged?.Invoke(this, EventArgs.Empty); ServerConnectionCountChanged?.Invoke(this, EventArgs.Empty);
} }
/// <summary>
/// Invoke client/server tcp connection events if subscribed by API user.
/// </summary>
/// <param name="client">The TcpClient object.</param>
/// <param name="isClientConnection">Is this a client connection created event? If not then we would assume that its a server connection create event.</param>
/// <returns></returns>
internal async Task InvokeConnectionCreateEvent(TcpClient client, bool isClientConnection)
{
//client connection created
if (isClientConnection && OnClientConnectionCreate != null)
{
await OnClientConnectionCreate.InvokeAsync(this, client, ExceptionFunc);
}
//server connection created
if (!isClientConnection && OnServerConnectionCreate != null)
{
await OnServerConnectionCreate.InvokeAsync(this, client, ExceptionFunc);
}
}
} }
} }
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
...@@ -25,15 +24,16 @@ namespace Titanium.Web.Proxy ...@@ -25,15 +24,16 @@ namespace Titanium.Web.Proxy
private static readonly Regex uriSchemeRegex = private static readonly Regex uriSchemeRegex =
new Regex("^[a-z]*://", RegexOptions.IgnoreCase | RegexOptions.Compiled); new Regex("^[a-z]*://", RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static readonly HashSet<string> proxySupportedCompressions = new HashSet<string>(StringComparer.OrdinalIgnoreCase) private static readonly HashSet<string> proxySupportedCompressions =
{ new HashSet<string>(StringComparer.OrdinalIgnoreCase)
"gzip", {
"deflate" "gzip",
}; "deflate"
};
private bool isWindowsAuthenticationEnabledAndSupported => private bool isWindowsAuthenticationEnabledAndSupported =>
EnableWinAuth && RunTime.IsWindows && !RunTime.IsRunningOnMono; EnableWinAuth && RunTime.IsWindows && !RunTime.IsRunningOnMono;
/// <summary> /// <summary>
/// This is the core request handler method for a particular connection from client. /// This is the core request handler method for a particular connection from client.
/// Will create new session (request/response) sequence until /// Will create new session (request/response) sequence until
...@@ -49,12 +49,17 @@ namespace Titanium.Web.Proxy ...@@ -49,12 +49,17 @@ namespace Titanium.Web.Proxy
/// explicit endpoint. /// explicit endpoint.
/// </param> /// </param>
/// <param name="connectRequest">The Connect request if this is a HTTPS request from explicit endpoint.</param> /// <param name="connectRequest">The Connect request if this is a HTTPS request from explicit endpoint.</param>
private async Task HandleHttpSessionRequest(ProxyEndPoint endPoint, TcpClientConnection clientConnection, /// <param name="prefetchConnectionTask">Prefetched server connection for current client using Connect/SNI headers.</param>
private async Task handleHttpSessionRequest(ProxyEndPoint endPoint, TcpClientConnection clientConnection,
CustomBufferedStream clientStream, HttpResponseWriter clientStreamWriter, CustomBufferedStream clientStream, HttpResponseWriter clientStreamWriter,
CancellationTokenSource cancellationTokenSource, string httpsConnectHostname, ConnectRequest connectRequest) CancellationTokenSource cancellationTokenSource, string httpsConnectHostname, ConnectRequest connectRequest,
Task<TcpServerConnection> prefetchConnectionTask = null)
{ {
var cancellationToken = cancellationTokenSource.Token; var cancellationToken = cancellationTokenSource.Token;
var prefetchTask = prefetchConnectionTask;
TcpServerConnection serverConnection = null; TcpServerConnection serverConnection = null;
bool closeServerConnection = false;
try try
{ {
...@@ -70,7 +75,7 @@ namespace Titanium.Web.Proxy ...@@ -70,7 +75,7 @@ namespace Titanium.Web.Proxy
return; return;
} }
var args = new SessionEventArgs(BufferSize, endPoint, cancellationTokenSource, ExceptionFunc) var args = new SessionEventArgs(this, endPoint, cancellationTokenSource)
{ {
ProxyClient = { ClientConnection = clientConnection }, ProxyClient = { ClientConnection = clientConnection },
WebSession = { ConnectRequest = connectRequest } WebSession = { ConnectRequest = connectRequest }
...@@ -132,9 +137,9 @@ namespace Titanium.Web.Proxy ...@@ -132,9 +137,9 @@ namespace Titanium.Web.Proxy
if (!args.IsTransparent) if (!args.IsTransparent)
{ {
// proxy authorization check // proxy authorization check
if (httpsConnectHostname == null && await CheckAuthorization(args) == false) if (httpsConnectHostname == null && await checkAuthorization(args) == false)
{ {
await InvokeBeforeResponse(args); await invokeBeforeResponse(args);
// send the response // send the response
await clientStreamWriter.WriteResponseAsync(args.WebSession.Response, await clientStreamWriter.WriteResponseAsync(args.WebSession.Response,
...@@ -142,7 +147,7 @@ namespace Titanium.Web.Proxy ...@@ -142,7 +147,7 @@ namespace Titanium.Web.Proxy
return; return;
} }
PrepareRequestHeaders(request.Headers); prepareRequestHeaders(request.Headers);
request.Host = request.RequestUri.Authority; request.Host = request.RequestUri.Authority;
} }
...@@ -157,7 +162,7 @@ namespace Titanium.Web.Proxy ...@@ -157,7 +162,7 @@ namespace Titanium.Web.Proxy
request.OriginalHasBody = request.HasBody; request.OriginalHasBody = request.HasBody;
// If user requested interception do it // If user requested interception do it
await InvokeBeforeRequest(args); await invokeBeforeRequest(args);
var response = args.WebSession.Response; var response = args.WebSession.Response;
...@@ -166,7 +171,7 @@ namespace Titanium.Web.Proxy ...@@ -166,7 +171,7 @@ namespace Titanium.Web.Proxy
// syphon out the request body from client before setting the new body // syphon out the request body from client before setting the new body
await args.SyphonOutBodyAsync(true, cancellationToken); await args.SyphonOutBodyAsync(true, cancellationToken);
await HandleHttpSessionResponse(args); await handleHttpSessionResponse(args);
if (!response.KeepAlive) if (!response.KeepAlive)
{ {
...@@ -176,63 +181,70 @@ namespace Titanium.Web.Proxy ...@@ -176,63 +181,70 @@ namespace Titanium.Web.Proxy
continue; continue;
} }
// create a new connection if hostname/upstream end point changes //If prefetch task is available.
//Delay awaiting prefect task as far as possible.
if (serverConnection == null && prefetchTask != null)
{
serverConnection = await prefetchTask;
prefetchTask = null;
}
// create a new connection if cache key changes
if (serverConnection != null if (serverConnection != null
&& (!serverConnection.HostName.EqualsIgnoreCase(request.RequestUri.Host) && (await getConnectionCacheKey(args, false,
|| args.WebSession.UpStreamEndPoint?.Equals(serverConnection.UpStreamEndPoint) == clientConnection.NegotiatedApplicationProtocol)
false)) != serverConnection.CacheKey))
{ {
serverConnection.Dispose(); await tcpConnectionFactory.Release(serverConnection);
serverConnection = null; serverConnection = null;
} }
//create
if (serverConnection == null) if (serverConnection == null)
{ {
serverConnection = await GetServerConnection(args, false, clientConnection.NegotiatedApplicationProtocol, cancellationToken); serverConnection = await getServerConnection(args, false,
clientConnection.NegotiatedApplicationProtocol, cancellationToken);
} }
// if upgrading to websocket then relay the requet without reading the contents //for connection pool retry fails until cache is exhausted
if (request.UpgradeToWebSocket) int attempt = 0;
while (attempt < MaxCachedConnections + 1)
{ {
// prepare the prefix content try
await serverConnection.StreamWriter.WriteLineAsync(httpCmd, cancellationToken);
await serverConnection.StreamWriter.WriteHeadersAsync(request.Headers,
cancellationToken: cancellationToken);
string httpStatus = await serverConnection.Stream.ReadLineAsync(cancellationToken);
Response.ParseResponseLine(httpStatus, out var responseVersion,
out int responseStatusCode,
out string responseStatusDescription);
response.HttpVersion = responseVersion;
response.StatusCode = responseStatusCode;
response.StatusDescription = responseStatusDescription;
await HeaderParser.ReadHeaders(serverConnection.Stream, response.Headers,
cancellationToken);
if (!args.IsTransparent)
{ {
await clientStreamWriter.WriteResponseAsync(response, // if upgrading to websocket then relay the request without reading the contents
cancellationToken: cancellationToken); if (request.UpgradeToWebSocket)
{
await handleWebSocketUpgrade(httpCmd, args, request,
response, clientStream, clientStreamWriter,
serverConnection, cancellationTokenSource, cancellationToken);
return;
}
// construct the web request that we are going to issue on behalf of the client.
await handleHttpSessionRequestInternal(serverConnection, args);
} }
//connection pool retry
// If user requested call back then do it catch (ServerConnectionException)
if (!args.WebSession.Response.Locked)
{ {
await InvokeBeforeResponse(args); attempt++;
if (!EnableConnectionPool || attempt == MaxCachedConnections + 1)
{
throw;
}
//get new connection from pool
await tcpConnectionFactory.Release(serverConnection, true);
serverConnection = null;
serverConnection = await getServerConnection(args, false,
clientConnection.NegotiatedApplicationProtocol, cancellationToken);
continue;
} }
await TcpHelper.SendRaw(clientStream, serverConnection.Stream, BufferSize, break;
(buffer, offset, count) => { args.OnDataSent(buffer, offset, count); },
(buffer, offset, count) => { args.OnDataReceived(buffer, offset, count); },
cancellationTokenSource, ExceptionFunc);
return;
} }
// construct the web request that we are going to issue on behalf of the client.
await HandleHttpSessionRequestInternal(serverConnection, args);
if (args.WebSession.ServerConnection == null) if (args.WebSession.ServerConnection == null)
{ {
return; return;
...@@ -241,6 +253,7 @@ namespace Titanium.Web.Proxy ...@@ -241,6 +253,7 @@ namespace Titanium.Web.Proxy
// if connection is closing exit // if connection is closing exit
if (!response.KeepAlive) if (!response.KeepAlive)
{ {
closeServerConnection = true;
return; return;
} }
...@@ -248,6 +261,16 @@ namespace Titanium.Web.Proxy ...@@ -248,6 +261,16 @@ namespace Titanium.Web.Proxy
{ {
throw new Exception("Session was terminated by user."); throw new Exception("Session was terminated by user.");
} }
//Get/release server connection for each HTTP session instead of per client connection.
//This will be more efficient especially when client is idly holding server connection
//between sessions without using it.
if (EnableConnectionPool)
{
await tcpConnectionFactory.Release(serverConnection);
serverConnection = null;
}
} }
catch (Exception e) when (!(e is ProxyHttpException)) catch (Exception e) when (!(e is ProxyHttpException))
{ {
...@@ -257,18 +280,26 @@ namespace Titanium.Web.Proxy ...@@ -257,18 +280,26 @@ namespace Titanium.Web.Proxy
catch (Exception e) catch (Exception e)
{ {
args.Exception = e; args.Exception = e;
closeServerConnection = true;
throw; throw;
} }
finally finally
{ {
await InvokeAfterResponse(args); await invokeAfterResponse(args);
args.Dispose(); args.Dispose();
} }
} }
} }
finally finally
{ {
serverConnection?.Dispose(); await tcpConnectionFactory.Release(serverConnection,
closeServerConnection);
if (prefetchTask!=null)
{
await tcpConnectionFactory.Release(await prefetchTask,
closeServerConnection);
}
} }
} }
...@@ -278,85 +309,197 @@ namespace Titanium.Web.Proxy ...@@ -278,85 +309,197 @@ namespace Titanium.Web.Proxy
/// <param name="serverConnection">The tcp connection.</param> /// <param name="serverConnection">The tcp connection.</param>
/// <param name="args">The session event arguments.</param> /// <param name="args">The session event arguments.</param>
/// <returns></returns> /// <returns></returns>
private async Task HandleHttpSessionRequestInternal(TcpServerConnection serverConnection, SessionEventArgs args) private async Task handleHttpSessionRequestInternal(TcpServerConnection serverConnection, SessionEventArgs args)
{ {
try var cancellationToken = args.CancellationTokenSource.Token;
var request = args.WebSession.Request;
request.Locked = true;
var body = request.CompressBodyAndUpdateContentLength();
// if expect continue is enabled then send the headers first
// and see if server would return 100 conitinue
if (request.ExpectContinue)
{ {
var cancellationToken = args.CancellationTokenSource.Token; args.WebSession.SetConnection(serverConnection);
var request = args.WebSession.Request; await args.WebSession.SendRequest(Enable100ContinueBehaviour, args.IsTransparent,
request.Locked = true; cancellationToken);
}
var body = request.CompressBodyAndUpdateContentLength(); // If 100 continue was the response inform that to the client
if (Enable100ContinueBehaviour)
{
var clientStreamWriter = args.ProxyClient.ClientStreamWriter;
// if expect continue is enabled then send the headers first if (request.Is100Continue)
// and see if server would return 100 conitinue
if (request.ExpectContinue)
{ {
args.WebSession.SetConnection(serverConnection); await clientStreamWriter.WriteResponseStatusAsync(args.WebSession.Response.HttpVersion,
await args.WebSession.SendRequest(Enable100ContinueBehaviour, args.IsTransparent, (int)HttpStatusCode.Continue, "Continue", cancellationToken);
cancellationToken); await clientStreamWriter.WriteLineAsync(cancellationToken);
} }
else if (request.ExpectationFailed)
// If 100 continue was the response inform that to the client
if (Enable100ContinueBehaviour)
{ {
var clientStreamWriter = args.ProxyClient.ClientStreamWriter; await clientStreamWriter.WriteResponseStatusAsync(args.WebSession.Response.HttpVersion,
(int)HttpStatusCode.ExpectationFailed, "Expectation Failed", cancellationToken);
if (request.Is100Continue) await clientStreamWriter.WriteLineAsync(cancellationToken);
{
await clientStreamWriter.WriteResponseStatusAsync(args.WebSession.Response.HttpVersion,
(int)HttpStatusCode.Continue, "Continue", cancellationToken);
await clientStreamWriter.WriteLineAsync(cancellationToken);
}
else if (request.ExpectationFailed)
{
await clientStreamWriter.WriteResponseStatusAsync(args.WebSession.Response.HttpVersion,
(int)HttpStatusCode.ExpectationFailed, "Expectation Failed", cancellationToken);
await clientStreamWriter.WriteLineAsync(cancellationToken);
}
} }
}
// If expect continue is not enabled then set the connectio and send request headers
if (!request.ExpectContinue)
{
args.WebSession.SetConnection(serverConnection);
await args.WebSession.SendRequest(Enable100ContinueBehaviour, args.IsTransparent,
cancellationToken);
}
// If expect continue is not enabled then set the connectio and send request headers // check if content-length is > 0
if (!request.ExpectContinue) if (request.ContentLength > 0)
{
if (request.IsBodyRead)
{ {
args.WebSession.SetConnection(serverConnection); var writer = args.WebSession.ServerConnection.StreamWriter;
await args.WebSession.SendRequest(Enable100ContinueBehaviour, args.IsTransparent, await writer.WriteBodyAsync(body, request.IsChunked, cancellationToken);
cancellationToken);
} }
else
// check if content-length is > 0
if (request.ContentLength > 0)
{ {
if (request.IsBodyRead) if (!request.ExpectationFailed)
{ {
var writer = args.WebSession.ServerConnection.StreamWriter; if (request.HasBody)
await writer.WriteBodyAsync(body, request.IsChunked, cancellationToken);
}
else
{
if (!request.ExpectationFailed)
{ {
if (request.HasBody) HttpWriter writer = args.WebSession.ServerConnection.StreamWriter;
{ await args.CopyRequestBodyAsync(writer, TransformationMode.None, cancellationToken);
HttpWriter writer = args.WebSession.ServerConnection.StreamWriter;
await args.CopyRequestBodyAsync(writer, TransformationMode.None, cancellationToken);
}
} }
} }
} }
}
// If not expectation failed response was returned by server then parse response // If not expectation failed response was returned by server then parse response
if (!request.ExpectationFailed) if (!request.ExpectationFailed)
{
await handleHttpSessionResponse(args);
}
}
/// <summary>
/// Handle upgrade to websocket
/// </summary>
private async Task handleWebSocketUpgrade(string httpCmd,
SessionEventArgs args, Request request, Response response,
CustomBufferedStream clientStream, HttpResponseWriter clientStreamWriter,
TcpServerConnection serverConnection,
CancellationTokenSource cancellationTokenSource, CancellationToken cancellationToken)
{
// prepare the prefix content
await serverConnection.StreamWriter.WriteLineAsync(httpCmd, cancellationToken);
await serverConnection.StreamWriter.WriteHeadersAsync(request.Headers,
cancellationToken: cancellationToken);
string httpStatus;
try
{
httpStatus = await serverConnection.Stream.ReadLineAsync(cancellationToken);
if (httpStatus == null)
{ {
await HandleHttpSessionResponse(args); throw new ServerConnectionException("Server connection was closed.");
} }
} }
catch (Exception e) when (!(e is ProxyHttpException)) catch (Exception e) when (!(e is ServerConnectionException))
{
throw new ServerConnectionException("Server connection was closed.", e);
}
Response.ParseResponseLine(httpStatus, out var responseVersion,
out int responseStatusCode,
out string responseStatusDescription);
response.HttpVersion = responseVersion;
response.StatusCode = responseStatusCode;
response.StatusDescription = responseStatusDescription;
await HeaderParser.ReadHeaders(serverConnection.Stream, response.Headers,
cancellationToken);
if (!args.IsTransparent)
{
await clientStreamWriter.WriteResponseAsync(response,
cancellationToken: cancellationToken);
}
// If user requested call back then do it
if (!args.WebSession.Response.Locked)
{
await invokeBeforeResponse(args);
}
await TcpHelper.SendRaw(clientStream, serverConnection.Stream, BufferPool, BufferSize,
(buffer, offset, count) => { args.OnDataSent(buffer, offset, count); },
(buffer, offset, count) => { args.OnDataReceived(buffer, offset, count); },
cancellationTokenSource, ExceptionFunc);
}
/// <summary>
/// Prepare the request headers so that we can avoid encodings not parsable by this proxy
/// </summary>
private void prepareRequestHeaders(HeaderCollection requestHeaders)
{
string acceptEncoding = requestHeaders.GetHeaderValueOrNull(KnownHeaders.AcceptEncoding);
if (acceptEncoding != null)
{
var supportedAcceptEncoding = new List<string>();
//only allow proxy supported compressions
supportedAcceptEncoding.AddRange(acceptEncoding.Split(',')
.Select(x => x.Trim())
.Where(x => proxySupportedCompressions.Contains(x)));
//uncompressed is always supported by proxy
supportedAcceptEncoding.Add("identity");
requestHeaders.SetOrAddHeaderValue(KnownHeaders.AcceptEncoding,
string.Join(",", supportedAcceptEncoding));
}
requestHeaders.FixProxyHeaders();
}
/// <summary>
/// Gets the connection cache key.
/// </summary>
/// <param name="args">The session event arguments.</param>
/// <param name="isConnect">Is this a CONNECT request.</param>
/// <param name="applicationProtocol"></param>
/// <returns></returns>
private async Task<string> getConnectionCacheKey(SessionEventArgsBase args, bool isConnect,
SslApplicationProtocol applicationProtocol)
{
List<SslApplicationProtocol> applicationProtocols = null;
if (applicationProtocol != default)
{ {
throw new ProxyHttpException("Error occured whilst handling session request (internal)", e, args); applicationProtocols = new List<SslApplicationProtocol> { applicationProtocol };
} }
ExternalProxy customUpStreamProxy = null;
bool isHttps = args.IsHttps;
if (GetCustomUpStreamProxyFunc != null)
{
customUpStreamProxy = await GetCustomUpStreamProxyFunc(args);
}
args.CustomUpStreamProxyUsed = customUpStreamProxy;
return tcpConnectionFactory.GetConnectionCacheKey(
args.WebSession.Request.RequestUri.Host,
args.WebSession.Request.RequestUri.Port,
args.WebSession.Request.HttpVersion,
isHttps, applicationProtocols, isConnect,
this, args.WebSession.UpStreamEndPoint ?? UpStreamEndPoint,
customUpStreamProxy ?? (isHttps ? UpStreamHttpsProxy : UpStreamHttpProxy));
} }
/// <summary> /// <summary>
/// Create a server connection. /// Create a server connection.
/// </summary> /// </summary>
...@@ -365,7 +508,7 @@ namespace Titanium.Web.Proxy ...@@ -365,7 +508,7 @@ namespace Titanium.Web.Proxy
/// <param name="applicationProtocol"></param> /// <param name="applicationProtocol"></param>
/// <param name="cancellationToken">The cancellation token for this async task.</param> /// <param name="cancellationToken">The cancellation token for this async task.</param>
/// <returns></returns> /// <returns></returns>
private Task<TcpServerConnection> GetServerConnection(SessionEventArgsBase args, bool isConnect, private Task<TcpServerConnection> getServerConnection(SessionEventArgsBase args, bool isConnect,
SslApplicationProtocol applicationProtocol, CancellationToken cancellationToken) SslApplicationProtocol applicationProtocol, CancellationToken cancellationToken)
{ {
List<SslApplicationProtocol> applicationProtocols = null; List<SslApplicationProtocol> applicationProtocols = null;
...@@ -374,7 +517,7 @@ namespace Titanium.Web.Proxy ...@@ -374,7 +517,7 @@ namespace Titanium.Web.Proxy
applicationProtocols = new List<SslApplicationProtocol> { applicationProtocol }; applicationProtocols = new List<SslApplicationProtocol> { applicationProtocol };
} }
return GetServerConnection(args, isConnect, applicationProtocols, cancellationToken); return getServerConnection(args, isConnect, applicationProtocols, cancellationToken);
} }
/// <summary> /// <summary>
...@@ -385,8 +528,8 @@ namespace Titanium.Web.Proxy ...@@ -385,8 +528,8 @@ namespace Titanium.Web.Proxy
/// <param name="applicationProtocols"></param> /// <param name="applicationProtocols"></param>
/// <param name="cancellationToken">The cancellation token for this async task.</param> /// <param name="cancellationToken">The cancellation token for this async task.</param>
/// <returns></returns> /// <returns></returns>
private async Task<TcpServerConnection> GetServerConnection(SessionEventArgsBase args, bool isConnect, private async Task<TcpServerConnection> getServerConnection(SessionEventArgsBase args, bool isConnect,
List<SslApplicationProtocol> applicationProtocols, CancellationToken cancellationToken) List<SslApplicationProtocol> applicationProtocols, CancellationToken cancellationToken)
{ {
ExternalProxy customUpStreamProxy = null; ExternalProxy customUpStreamProxy = null;
...@@ -398,47 +541,22 @@ namespace Titanium.Web.Proxy ...@@ -398,47 +541,22 @@ namespace Titanium.Web.Proxy
args.CustomUpStreamProxyUsed = customUpStreamProxy; args.CustomUpStreamProxyUsed = customUpStreamProxy;
return await tcpConnectionFactory.CreateClient( return await tcpConnectionFactory.GetClient(
args.WebSession.Request.RequestUri.Host, args.WebSession.Request.RequestUri.Host,
args.WebSession.Request.RequestUri.Port, args.WebSession.Request.RequestUri.Port,
args.WebSession.Request.HttpVersion, args.WebSession.Request.HttpVersion,
isHttps, applicationProtocols, isConnect, isHttps, applicationProtocols, isConnect,
this, args.WebSession.UpStreamEndPoint ?? UpStreamEndPoint, this, args.WebSession.UpStreamEndPoint ?? UpStreamEndPoint,
customUpStreamProxy ?? (isHttps ? UpStreamHttpsProxy : UpStreamHttpProxy), customUpStreamProxy ?? (isHttps ? UpStreamHttpsProxy : UpStreamHttpProxy),
cancellationToken); cancellationToken);
} }
/// <summary>
/// Prepare the request headers so that we can avoid encodings not parsable by this proxy
/// </summary>
private void PrepareRequestHeaders(HeaderCollection requestHeaders)
{
var acceptEncoding = requestHeaders.GetHeaderValueOrNull(KnownHeaders.AcceptEncoding);
if (acceptEncoding != null)
{
var supportedAcceptEncoding = new List<string>();
//only allow proxy supported compressions
supportedAcceptEncoding.AddRange(acceptEncoding.Split(',')
.Select(x => x.Trim())
.Where(x => proxySupportedCompressions.Contains(x)));
//uncompressed is always supported by proxy
supportedAcceptEncoding.Add("identity");
requestHeaders.SetOrAddHeaderValue(KnownHeaders.AcceptEncoding, string.Join(",", supportedAcceptEncoding));
}
requestHeaders.FixProxyHeaders();
}
/// <summary> /// <summary>
/// Invoke before request handler if it is set. /// Invoke before request handler if it is set.
/// </summary> /// </summary>
/// <param name="args">The session event arguments.</param> /// <param name="args">The session event arguments.</param>
/// <returns></returns> /// <returns></returns>
private async Task InvokeBeforeRequest(SessionEventArgs args) private async Task invokeBeforeRequest(SessionEventArgs args)
{ {
if (BeforeRequest != null) if (BeforeRequest != null)
{ {
......
using System; using System.Net;
using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Network.WinAuth.Security; using Titanium.Web.Proxy.Network.WinAuth.Security;
...@@ -18,116 +16,111 @@ namespace Titanium.Web.Proxy ...@@ -18,116 +16,111 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
/// <param name="args">The session event arguments.</param> /// <param name="args">The session event arguments.</param>
/// <returns> The task.</returns> /// <returns> The task.</returns>
private async Task HandleHttpSessionResponse(SessionEventArgs args) private async Task handleHttpSessionResponse(SessionEventArgs args)
{ {
try
{
var cancellationToken = args.CancellationTokenSource.Token;
// read response & headers from server
await args.WebSession.ReceiveResponse(cancellationToken);
var response = args.WebSession.Response; var cancellationToken = args.CancellationTokenSource.Token;
args.ReRequest = false;
// check for windows authentication // read response & headers from server
if (isWindowsAuthenticationEnabledAndSupported) await args.WebSession.ReceiveResponse(cancellationToken);
{
if (response.StatusCode == (int)HttpStatusCode.Unauthorized)
{
await Handle401UnAuthorized(args);
}
else
{
WinAuthEndPoint.AuthenticatedResponse(args.WebSession.Data);
}
}
response.OriginalHasBody = response.HasBody; var response = args.WebSession.Response;
args.ReRequest = false;
// if user requested call back then do it // check for windows authentication
if (!response.Locked) if (isWindowsAuthenticationEnabledAndSupported)
{
if (response.StatusCode == (int)HttpStatusCode.Unauthorized)
{ {
await InvokeBeforeResponse(args); await handle401UnAuthorized(args);
} }
else
// it may changed in the user event
response = args.WebSession.Response;
var clientStreamWriter = args.ProxyClient.ClientStreamWriter;
if (response.TerminateResponse || response.Locked)
{ {
await clientStreamWriter.WriteResponseAsync(response, cancellationToken: cancellationToken); WinAuthEndPoint.AuthenticatedResponse(args.WebSession.Data);
if (!response.TerminateResponse)
{
// syphon out the response body from server before setting the new body
await args.SyphonOutBodyAsync(false, cancellationToken);
}
else
{
args.WebSession.ServerConnection.Dispose();
args.WebSession.ServerConnection = null;
}
return;
} }
}
// if user requested to send request again response.OriginalHasBody = response.HasBody;
// likely after making modifications from User Response Handler
if (args.ReRequest)
{
// clear current response
await args.ClearResponse(cancellationToken);
await HandleHttpSessionRequestInternal(args.WebSession.ServerConnection, args);
return;
}
response.Locked = true; // if user requested call back then do it
if (!response.Locked)
{
await invokeBeforeResponse(args);
}
// Write back to client 100-conitinue response if that's what server returned // it may changed in the user event
if (response.Is100Continue) response = args.WebSession.Response;
{
await clientStreamWriter.WriteResponseStatusAsync(response.HttpVersion,
(int)HttpStatusCode.Continue, "Continue", cancellationToken);
await clientStreamWriter.WriteLineAsync(cancellationToken);
}
else if (response.ExpectationFailed)
{
await clientStreamWriter.WriteResponseStatusAsync(response.HttpVersion,
(int)HttpStatusCode.ExpectationFailed, "Expectation Failed", cancellationToken);
await clientStreamWriter.WriteLineAsync(cancellationToken);
}
if (!args.IsTransparent) var clientStreamWriter = args.ProxyClient.ClientStreamWriter;
{
response.Headers.FixProxyHeaders(); if (response.TerminateResponse || response.Locked)
} {
await clientStreamWriter.WriteResponseAsync(response, cancellationToken: cancellationToken);
if (response.IsBodyRead) if (!response.TerminateResponse)
{ {
await clientStreamWriter.WriteResponseAsync(response, cancellationToken: cancellationToken); // syphon out the response body from server before setting the new body
await args.SyphonOutBodyAsync(false, cancellationToken);
} }
else else
{ {
// Write back response status to client await tcpConnectionFactory.Release(args.WebSession.ServerConnection, true);
await clientStreamWriter.WriteResponseStatusAsync(response.HttpVersion, response.StatusCode, args.WebSession.ServerConnection = null;
response.StatusDescription, cancellationToken);
await clientStreamWriter.WriteHeadersAsync(response.Headers, cancellationToken: cancellationToken);
// Write body if exists
if (response.HasBody)
{
await args.CopyResponseBodyAsync(clientStreamWriter, TransformationMode.None,
cancellationToken);
}
} }
return;
}
// if user requested to send request again
// likely after making modifications from User Response Handler
if (args.ReRequest)
{
// clear current response
await args.ClearResponse(cancellationToken);
await handleHttpSessionRequestInternal(args.WebSession.ServerConnection, args);
return;
}
response.Locked = true;
// Write back to client 100-conitinue response if that's what server returned
if (response.Is100Continue)
{
await clientStreamWriter.WriteResponseStatusAsync(response.HttpVersion,
(int)HttpStatusCode.Continue, "Continue", cancellationToken);
await clientStreamWriter.WriteLineAsync(cancellationToken);
}
else if (response.ExpectationFailed)
{
await clientStreamWriter.WriteResponseStatusAsync(response.HttpVersion,
(int)HttpStatusCode.ExpectationFailed, "Expectation Failed", cancellationToken);
await clientStreamWriter.WriteLineAsync(cancellationToken);
} }
catch (Exception e) when (!(e is ProxyHttpException))
if (!args.IsTransparent)
{
response.Headers.FixProxyHeaders();
}
if (response.IsBodyRead)
{
await clientStreamWriter.WriteResponseAsync(response, cancellationToken: cancellationToken);
}
else
{ {
throw new ProxyHttpException("Error occured whilst handling session response", e, args); // Write back response status to client
await clientStreamWriter.WriteResponseStatusAsync(response.HttpVersion, response.StatusCode,
response.StatusDescription, cancellationToken);
await clientStreamWriter.WriteHeadersAsync(response.Headers, cancellationToken: cancellationToken);
// Write body if exists
if (response.HasBody)
{
await args.CopyResponseBodyAsync(clientStreamWriter, TransformationMode.None,
cancellationToken);
}
} }
} }
/// <summary> /// <summary>
...@@ -135,7 +128,7 @@ namespace Titanium.Web.Proxy ...@@ -135,7 +128,7 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
/// <param name="args"></param> /// <param name="args"></param>
/// <returns></returns> /// <returns></returns>
private async Task InvokeBeforeResponse(SessionEventArgs args) private async Task invokeBeforeResponse(SessionEventArgs args)
{ {
if (BeforeResponse != null) if (BeforeResponse != null)
{ {
...@@ -148,7 +141,7 @@ namespace Titanium.Web.Proxy ...@@ -148,7 +141,7 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
/// <param name="args"></param> /// <param name="args"></param>
/// <returns></returns> /// <returns></returns>
private async Task InvokeAfterResponse(SessionEventArgs args) private async Task invokeAfterResponse(SessionEventArgs args)
{ {
if (AfterResponse != null) if (AfterResponse != null)
{ {
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Portable.BouncyCastle" Version="1.8.2" /> <PackageReference Include="Portable.BouncyCastle" Version="1.8.2" />
<PackageReference Include="StreamExtended" Version="1.0.164" /> <PackageReference Include="StreamExtended" Version="1.0.175-beta" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'"> <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
</PackageReference> </PackageReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup Condition="'$(TargetFramework)' == 'net45'">
<Reference Include="System.Web" /> <Reference Include="System.Web" />
</ItemGroup> </ItemGroup>
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
<copyright>Copyright &#x00A9; Titanium. All rights reserved.</copyright> <copyright>Copyright &#x00A9; Titanium. All rights reserved.</copyright>
<tags></tags> <tags></tags>
<dependencies> <dependencies>
<dependency id="StreamExtended" version="1.0.164" /> <dependency id="StreamExtended" version="1.0.175-beta" />
<dependency id="Portable.BouncyCastle" version="1.8.2" /> <dependency id="Portable.BouncyCastle" version="1.8.2" />
</dependencies> </dependencies>
</metadata> </metadata>
......
...@@ -6,7 +6,6 @@ using System.Security.Authentication; ...@@ -6,7 +6,6 @@ using System.Security.Authentication;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended; using StreamExtended;
using StreamExtended.Helpers;
using StreamExtended.Network; using StreamExtended.Network;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Exceptions; using Titanium.Web.Proxy.Exceptions;
...@@ -26,18 +25,21 @@ namespace Titanium.Web.Proxy ...@@ -26,18 +25,21 @@ namespace Titanium.Web.Proxy
/// <param name="endPoint">The transparent endpoint.</param> /// <param name="endPoint">The transparent endpoint.</param>
/// <param name="clientConnection">The client connection.</param> /// <param name="clientConnection">The client connection.</param>
/// <returns></returns> /// <returns></returns>
private async Task HandleClient(TransparentProxyEndPoint endPoint, TcpClientConnection clientConnection) private async Task handleClient(TransparentProxyEndPoint endPoint, TcpClientConnection clientConnection)
{ {
var cancellationTokenSource = new CancellationTokenSource(); var cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = cancellationTokenSource.Token; var cancellationToken = cancellationTokenSource.Token;
var clientStream = new CustomBufferedStream(clientConnection.GetStream(), BufferSize); var clientStream = new CustomBufferedStream(clientConnection.GetStream(), BufferPool, BufferSize);
var clientStreamWriter = new HttpResponseWriter(clientStream, BufferPool, BufferSize);
var clientStreamWriter = new HttpResponseWriter(clientStream, BufferSize); Task<TcpServerConnection> prefetchConnectionTask = null;
bool closeServerConnection = false;
bool calledRequestHandler = false;
try try
{ {
var clientHelloInfo = await SslTools.PeekClientHello(clientStream, cancellationToken); var clientHelloInfo = await SslTools.PeekClientHello(clientStream, BufferPool, cancellationToken);
bool isHttps = clientHelloInfo != null; bool isHttps = clientHelloInfo != null;
string httpsHostName = null; string httpsHostName = null;
...@@ -60,8 +62,13 @@ namespace Titanium.Web.Proxy ...@@ -60,8 +62,13 @@ namespace Titanium.Web.Proxy
if (endPoint.DecryptSsl && args.DecryptSsl) if (endPoint.DecryptSsl && args.DecryptSsl)
{ {
prefetchConnectionTask = tcpConnectionFactory.GetClient(httpsHostName, endPoint.Port,
null, true, null,
false, this, UpStreamEndPoint, UpStreamHttpsProxy, cancellationToken);
SslStream sslStream = null; SslStream sslStream = null;
//do client authentication using fake certificate
try try
{ {
sslStream = new SslStream(clientStream); sslStream = new SslStream(clientStream);
...@@ -73,9 +80,9 @@ namespace Titanium.Web.Proxy ...@@ -73,9 +80,9 @@ namespace Titanium.Web.Proxy
await sslStream.AuthenticateAsServerAsync(certificate, false, SslProtocols.Tls, false); await sslStream.AuthenticateAsServerAsync(certificate, false, SslProtocols.Tls, false);
// HTTPS server created - we can now decrypt the client's traffic // HTTPS server created - we can now decrypt the client's traffic
clientStream = new CustomBufferedStream(sslStream, BufferSize); clientStream = new CustomBufferedStream(sslStream, BufferPool, BufferSize);
clientStreamWriter = new HttpResponseWriter(clientStream, BufferSize); clientStreamWriter = new HttpResponseWriter(clientStream, BufferPool, BufferSize);
} }
catch (Exception e) catch (Exception e)
{ {
...@@ -87,66 +94,77 @@ namespace Titanium.Web.Proxy ...@@ -87,66 +94,77 @@ namespace Titanium.Web.Proxy
else else
{ {
// create new connection // create new connection
var connection = new TcpClient(UpStreamEndPoint); var connection = await tcpConnectionFactory.GetClient(httpsHostName, endPoint.Port,
await connection.ConnectAsync(httpsHostName, endPoint.Port); null, false, null,
connection.ReceiveTimeout = ConnectionTimeOutSeconds * 1000; true, this, UpStreamEndPoint, UpStreamHttpsProxy, cancellationToken);
connection.SendTimeout = ConnectionTimeOutSeconds * 1000;
using (connection) var serverStream = connection.Stream;
int available = clientStream.Available;
if (available > 0)
{ {
var serverStream = connection.GetStream(); // send the buffered data
var data = BufferPool.GetBuffer(BufferSize);
int available = clientStream.Available; try
if (available > 0)
{ {
// send the buffered data // clientStream.Available sbould be at most BufferSize because it is using the same buffer size
var data = BufferPool.GetBuffer(BufferSize); await clientStream.ReadAsync(data, 0, available, cancellationToken);
await serverStream.WriteAsync(data, 0, available, cancellationToken);
try await serverStream.FlushAsync(cancellationToken);
{
// clientStream.Available sbould be at most BufferSize because it is using the same buffer size
await clientStream.ReadAsync(data, 0, available, cancellationToken);
await serverStream.WriteAsync(data, 0, available, cancellationToken);
await serverStream.FlushAsync(cancellationToken);
}
finally
{
BufferPool.ReturnBuffer(data);
}
} }
finally
{
BufferPool.ReturnBuffer(data);
}
}
////var serverHelloInfo = await SslTools.PeekServerHello(serverStream); await TcpHelper.SendRaw(clientStream, serverStream, BufferPool, BufferSize,
null, null, cancellationTokenSource, ExceptionFunc);
await tcpConnectionFactory.Release(connection, true);
return;
await TcpHelper.SendRaw(clientStream, serverStream, BufferSize,
null, null, cancellationTokenSource, ExceptionFunc);
}
} }
} }
calledRequestHandler = true;
// HTTPS server created - we can now decrypt the client's traffic // HTTPS server created - we can now decrypt the client's traffic
// Now create the request // Now create the request
await HandleHttpSessionRequest(endPoint, clientConnection, clientStream, clientStreamWriter, await handleHttpSessionRequest(endPoint, clientConnection, clientStream, clientStreamWriter,
cancellationTokenSource, isHttps ? httpsHostName : null, null); cancellationTokenSource, isHttps ? httpsHostName : null, null, prefetchConnectionTask);
} }
catch (ProxyException e) catch (ProxyException e)
{ {
OnException(clientStream, e); closeServerConnection = true;
onException(clientStream, e);
} }
catch (IOException e) catch (IOException e)
{ {
OnException(clientStream, new Exception("Connection was aborted", e)); closeServerConnection = true;
onException(clientStream, new Exception("Connection was aborted", e));
} }
catch (SocketException e) catch (SocketException e)
{ {
OnException(clientStream, new Exception("Could not connect", e)); closeServerConnection = true;
onException(clientStream, new Exception("Could not connect", e));
} }
catch (Exception e) catch (Exception e)
{ {
OnException(clientStream, new Exception("Error occured in whilst handling the client", e)); closeServerConnection = true;
onException(clientStream, new Exception("Error occured in whilst handling the client", e));
} }
finally finally
{ {
if (!calledRequestHandler
&& prefetchConnectionTask != null)
{
var connection = await prefetchConnectionTask;
await tcpConnectionFactory.Release(connection, closeServerConnection);
}
clientStream.Dispose(); clientStream.Dispose();
if (!cancellationTokenSource.IsCancellationRequested) if (!cancellationTokenSource.IsCancellationRequested)
{ {
cancellationTokenSource.Cancel(); cancellationTokenSource.Cancel();
......
...@@ -45,7 +45,7 @@ namespace Titanium.Web.Proxy ...@@ -45,7 +45,7 @@ namespace Titanium.Web.Proxy
/// User to server to authenticate requests. /// User to server to authenticate requests.
/// To disable this set ProxyServer.EnableWinAuth to false. /// To disable this set ProxyServer.EnableWinAuth to false.
/// </summary> /// </summary>
internal async Task Handle401UnAuthorized(SessionEventArgs args) private async Task handle401UnAuthorized(SessionEventArgs args)
{ {
string headerName = null; string headerName = null;
HttpHeader authHeader = null; HttpHeader authHeader = null;
...@@ -99,7 +99,7 @@ namespace Titanium.Web.Proxy ...@@ -99,7 +99,7 @@ namespace Titanium.Web.Proxy
if (!WinAuthEndPoint.ValidateWinAuthState(args.WebSession.Data, expectedAuthState)) if (!WinAuthEndPoint.ValidateWinAuthState(args.WebSession.Data, expectedAuthState))
{ {
// Invalid state, create proper error message to client // Invalid state, create proper error message to client
await RewriteUnauthorizedResponse(args); await rewriteUnauthorizedResponse(args);
return; return;
} }
...@@ -145,6 +145,8 @@ namespace Titanium.Web.Proxy ...@@ -145,6 +145,8 @@ namespace Titanium.Web.Proxy
{ {
request.ContentLength = request.Body.Length; request.ContentLength = request.Body.Length;
} }
args.WebSession.ServerConnection.IsWinAuthenticated = true;
} }
// Need to revisit this. // Need to revisit this.
...@@ -161,7 +163,7 @@ namespace Titanium.Web.Proxy ...@@ -161,7 +163,7 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
/// <param name="args"></param> /// <param name="args"></param>
/// <returns></returns> /// <returns></returns>
internal async Task RewriteUnauthorizedResponse(SessionEventArgs args) private async Task rewriteUnauthorizedResponse(SessionEventArgs args)
{ {
var response = args.WebSession.Response; var response = args.WebSession.Response;
......
...@@ -2,5 +2,5 @@ ...@@ -2,5 +2,5 @@
<packages> <packages>
<package id="Portable.BouncyCastle" version="1.8.2" targetFramework="net45" /> <package id="Portable.BouncyCastle" version="1.8.2" targetFramework="net45" />
<package id="StreamExtended" version="1.0.164" targetFramework="net45" /> <package id="StreamExtended" version="1.0.175-beta" targetFramework="net45" />
</packages> </packages>
\ No newline at end of file
...@@ -103,10 +103,13 @@ or when server terminates connection from proxy.</p> ...@@ -103,10 +103,13 @@ or when server terminates connection from proxy.</p>
<div class="inheritedMembers"> <div class="inheritedMembers">
<h5>Inherited Members</h5> <h5>Inherited Members</h5>
<div> <div>
<a class="xref" href="Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_BufferSize">SessionEventArgsBase.BufferSize</a> <a class="xref" href="Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_bufferSize">SessionEventArgsBase.bufferSize</a>
</div> </div>
<div> <div>
<a class="xref" href="Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_ExceptionFunc">SessionEventArgsBase.ExceptionFunc</a> <a class="xref" href="Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_bufferPool">SessionEventArgsBase.bufferPool</a>
</div>
<div>
<a class="xref" href="Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_exceptionFunc">SessionEventArgsBase.exceptionFunc</a>
</div> </div>
<div> <div>
<a class="xref" href="Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_UserData">SessionEventArgsBase.UserData</a> <a class="xref" href="Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_UserData">SessionEventArgsBase.UserData</a>
...@@ -177,12 +180,12 @@ or when server terminates connection from proxy.</p> ...@@ -177,12 +180,12 @@ or when server terminates connection from proxy.</p>
<a id="Titanium_Web_Proxy_EventArguments_SessionEventArgs__ctor_" data-uid="Titanium.Web.Proxy.EventArguments.SessionEventArgs.#ctor*"></a> <a id="Titanium_Web_Proxy_EventArguments_SessionEventArgs__ctor_" data-uid="Titanium.Web.Proxy.EventArguments.SessionEventArgs.#ctor*"></a>
<h4 id="Titanium_Web_Proxy_EventArguments_SessionEventArgs__ctor_System_Int32_Titanium_Web_Proxy_Models_ProxyEndPoint_Titanium_Web_Proxy_Http_Request_System_Threading_CancellationTokenSource_Titanium_Web_Proxy_ExceptionHandler_" data-uid="Titanium.Web.Proxy.EventArguments.SessionEventArgs.#ctor(System.Int32,Titanium.Web.Proxy.Models.ProxyEndPoint,Titanium.Web.Proxy.Http.Request,System.Threading.CancellationTokenSource,Titanium.Web.Proxy.ExceptionHandler)">SessionEventArgs(Int32, ProxyEndPoint, Request, CancellationTokenSource, ExceptionHandler)</h4> <h4 id="Titanium_Web_Proxy_EventArguments_SessionEventArgs__ctor_Titanium_Web_Proxy_ProxyServer_Titanium_Web_Proxy_Models_ProxyEndPoint_Titanium_Web_Proxy_Http_Request_System_Threading_CancellationTokenSource_" data-uid="Titanium.Web.Proxy.EventArguments.SessionEventArgs.#ctor(Titanium.Web.Proxy.ProxyServer,Titanium.Web.Proxy.Models.ProxyEndPoint,Titanium.Web.Proxy.Http.Request,System.Threading.CancellationTokenSource)">SessionEventArgs(ProxyServer, ProxyEndPoint, Request, CancellationTokenSource)</h4>
<div class="markdown level1 summary"></div> <div class="markdown level1 summary"></div>
<div class="markdown level1 conceptual"></div> <div class="markdown level1 conceptual"></div>
<h5 class="decalaration">Declaration</h5> <h5 class="decalaration">Declaration</h5>
<div class="codewrapper"> <div class="codewrapper">
<pre><code class="lang-csharp hljs">protected SessionEventArgs(int bufferSize, ProxyEndPoint endPoint, Request request, CancellationTokenSource cancellationTokenSource, ExceptionHandler exceptionFunc)</code></pre> <pre><code class="lang-csharp hljs">protected SessionEventArgs(ProxyServer server, ProxyEndPoint endPoint, Request request, CancellationTokenSource cancellationTokenSource)</code></pre>
</div> </div>
<h5 class="parameters">Parameters</h5> <h5 class="parameters">Parameters</h5>
<table class="table table-bordered table-striped table-condensed"> <table class="table table-bordered table-striped table-condensed">
...@@ -195,8 +198,8 @@ or when server terminates connection from proxy.</p> ...@@ -195,8 +198,8 @@ or when server terminates connection from proxy.</p>
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.int32">Int32</a></td> <td><a class="xref" href="Titanium.Web.Proxy.ProxyServer.html">ProxyServer</a></td>
<td><span class="parametername">bufferSize</span></td> <td><span class="parametername">server</span></td>
<td></td> <td></td>
</tr> </tr>
<tr> <tr>
...@@ -214,11 +217,6 @@ or when server terminates connection from proxy.</p> ...@@ -214,11 +217,6 @@ or when server terminates connection from proxy.</p>
<td><span class="parametername">cancellationTokenSource</span></td> <td><span class="parametername">cancellationTokenSource</span></td>
<td></td> <td></td>
</tr> </tr>
<tr>
<td><a class="xref" href="Titanium.Web.Proxy.ExceptionHandler.html">ExceptionHandler</a></td>
<td><span class="parametername">exceptionFunc</span></td>
<td></td>
</tr>
</tbody> </tbody>
</table> </table>
<h3 id="properties">Properties <h3 id="properties">Properties
......
...@@ -139,12 +139,12 @@ or when server terminates connection from proxy.</p> ...@@ -139,12 +139,12 @@ or when server terminates connection from proxy.</p>
<a id="Titanium_Web_Proxy_EventArguments_SessionEventArgsBase__ctor_" data-uid="Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.#ctor*"></a> <a id="Titanium_Web_Proxy_EventArguments_SessionEventArgsBase__ctor_" data-uid="Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.#ctor*"></a>
<h4 id="Titanium_Web_Proxy_EventArguments_SessionEventArgsBase__ctor_System_Int32_Titanium_Web_Proxy_Models_ProxyEndPoint_System_Threading_CancellationTokenSource_Titanium_Web_Proxy_Http_Request_Titanium_Web_Proxy_ExceptionHandler_" data-uid="Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.#ctor(System.Int32,Titanium.Web.Proxy.Models.ProxyEndPoint,System.Threading.CancellationTokenSource,Titanium.Web.Proxy.Http.Request,Titanium.Web.Proxy.ExceptionHandler)">SessionEventArgsBase(Int32, ProxyEndPoint, CancellationTokenSource, Request, ExceptionHandler)</h4> <h4 id="Titanium_Web_Proxy_EventArguments_SessionEventArgsBase__ctor_Titanium_Web_Proxy_ProxyServer_Titanium_Web_Proxy_Models_ProxyEndPoint_System_Threading_CancellationTokenSource_Titanium_Web_Proxy_Http_Request_" data-uid="Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.#ctor(Titanium.Web.Proxy.ProxyServer,Titanium.Web.Proxy.Models.ProxyEndPoint,System.Threading.CancellationTokenSource,Titanium.Web.Proxy.Http.Request)">SessionEventArgsBase(ProxyServer, ProxyEndPoint, CancellationTokenSource, Request)</h4>
<div class="markdown level1 summary"></div> <div class="markdown level1 summary"></div>
<div class="markdown level1 conceptual"></div> <div class="markdown level1 conceptual"></div>
<h5 class="decalaration">Declaration</h5> <h5 class="decalaration">Declaration</h5>
<div class="codewrapper"> <div class="codewrapper">
<pre><code class="lang-csharp hljs">protected SessionEventArgsBase(int bufferSize, ProxyEndPoint endPoint, CancellationTokenSource cancellationTokenSource, Request request, ExceptionHandler exceptionFunc)</code></pre> <pre><code class="lang-csharp hljs">protected SessionEventArgsBase(ProxyServer server, ProxyEndPoint endPoint, CancellationTokenSource cancellationTokenSource, Request request)</code></pre>
</div> </div>
<h5 class="parameters">Parameters</h5> <h5 class="parameters">Parameters</h5>
<table class="table table-bordered table-striped table-condensed"> <table class="table table-bordered table-striped table-condensed">
...@@ -157,8 +157,8 @@ or when server terminates connection from proxy.</p> ...@@ -157,8 +157,8 @@ or when server terminates connection from proxy.</p>
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.int32">Int32</a></td> <td><a class="xref" href="Titanium.Web.Proxy.ProxyServer.html">ProxyServer</a></td>
<td><span class="parametername">bufferSize</span></td> <td><span class="parametername">server</span></td>
<td></td> <td></td>
</tr> </tr>
<tr> <tr>
...@@ -176,24 +176,42 @@ or when server terminates connection from proxy.</p> ...@@ -176,24 +176,42 @@ or when server terminates connection from proxy.</p>
<td><span class="parametername">request</span></td> <td><span class="parametername">request</span></td>
<td></td> <td></td>
</tr> </tr>
</tbody>
</table>
<h3 id="fields">Fields
</h3>
<h4 id="Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_bufferPool" data-uid="Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.bufferPool">bufferPool</h4>
<div class="markdown level1 summary"></div>
<div class="markdown level1 conceptual"></div>
<h5 class="decalaration">Declaration</h5>
<div class="codewrapper">
<pre><code class="lang-csharp hljs">protected readonly IBufferPool bufferPool</code></pre>
</div>
<h5 class="fieldValue">Field Value</h5>
<table class="table table-bordered table-striped table-condensed">
<thead>
<tr>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr> <tr>
<td><a class="xref" href="Titanium.Web.Proxy.ExceptionHandler.html">ExceptionHandler</a></td> <td><span class="xref">StreamExtended.IBufferPool</span></td>
<td><span class="parametername">exceptionFunc</span></td>
<td></td> <td></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<h3 id="fields">Fields
</h3>
<h4 id="Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_BufferSize" data-uid="Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.BufferSize">BufferSize</h4> <h4 id="Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_bufferSize" data-uid="Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.bufferSize">bufferSize</h4>
<div class="markdown level1 summary"><p>Size of Buffers used by this object</p> <div class="markdown level1 summary"></div>
</div>
<div class="markdown level1 conceptual"></div> <div class="markdown level1 conceptual"></div>
<h5 class="decalaration">Declaration</h5> <h5 class="decalaration">Declaration</h5>
<div class="codewrapper"> <div class="codewrapper">
<pre><code class="lang-csharp hljs">protected readonly int BufferSize</code></pre> <pre><code class="lang-csharp hljs">protected readonly int bufferSize</code></pre>
</div> </div>
<h5 class="fieldValue">Field Value</h5> <h5 class="fieldValue">Field Value</h5>
<table class="table table-bordered table-striped table-condensed"> <table class="table table-bordered table-striped table-condensed">
...@@ -212,12 +230,12 @@ or when server terminates connection from proxy.</p> ...@@ -212,12 +230,12 @@ or when server terminates connection from proxy.</p>
</table> </table>
<h4 id="Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_ExceptionFunc" data-uid="Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.ExceptionFunc">ExceptionFunc</h4> <h4 id="Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_exceptionFunc" data-uid="Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.exceptionFunc">exceptionFunc</h4>
<div class="markdown level1 summary"></div> <div class="markdown level1 summary"></div>
<div class="markdown level1 conceptual"></div> <div class="markdown level1 conceptual"></div>
<h5 class="decalaration">Declaration</h5> <h5 class="decalaration">Declaration</h5>
<div class="codewrapper"> <div class="codewrapper">
<pre><code class="lang-csharp hljs">protected readonly ExceptionHandler ExceptionFunc</code></pre> <pre><code class="lang-csharp hljs">protected readonly ExceptionHandler exceptionFunc</code></pre>
</div> </div>
<h5 class="fieldValue">Field Value</h5> <h5 class="fieldValue">Field Value</h5>
<table class="table table-bordered table-striped table-condensed"> <table class="table table-bordered table-striped table-condensed">
......
...@@ -100,10 +100,13 @@ ...@@ -100,10 +100,13 @@
<div class="inheritedMembers"> <div class="inheritedMembers">
<h5>Inherited Members</h5> <h5>Inherited Members</h5>
<div> <div>
<a class="xref" href="Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_BufferSize">SessionEventArgsBase.BufferSize</a> <a class="xref" href="Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_bufferSize">SessionEventArgsBase.bufferSize</a>
</div> </div>
<div> <div>
<a class="xref" href="Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_ExceptionFunc">SessionEventArgsBase.ExceptionFunc</a> <a class="xref" href="Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_bufferPool">SessionEventArgsBase.bufferPool</a>
</div>
<div>
<a class="xref" href="Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_exceptionFunc">SessionEventArgsBase.exceptionFunc</a>
</div> </div>
<div> <div>
<a class="xref" href="Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_UserData">SessionEventArgsBase.UserData</a> <a class="xref" href="Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_UserData">SessionEventArgsBase.UserData</a>
......
...@@ -258,6 +258,32 @@ Should return true for successful authentication.</p> ...@@ -258,6 +258,32 @@ Should return true for successful authentication.</p>
</table> </table>
<a id="Titanium_Web_Proxy_ProxyServer_BufferPool_" data-uid="Titanium.Web.Proxy.ProxyServer.BufferPool*"></a>
<h4 id="Titanium_Web_Proxy_ProxyServer_BufferPool" data-uid="Titanium.Web.Proxy.ProxyServer.BufferPool">BufferPool</h4>
<div class="markdown level1 summary"><p>The buffer pool used throughout this proxy instance.</p>
</div>
<div class="markdown level1 conceptual"></div>
<h5 class="decalaration">Declaration</h5>
<div class="codewrapper">
<pre><code class="lang-csharp hljs">public IBufferPool BufferPool { get; set; }</code></pre>
</div>
<h5 class="propertyValue">Property Value</h5>
<table class="table table-bordered table-striped table-condensed">
<thead>
<tr>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><span class="xref">StreamExtended.IBufferPool</span></td>
<td></td>
</tr>
</tbody>
</table>
<a id="Titanium_Web_Proxy_ProxyServer_BufferSize_" data-uid="Titanium.Web.Proxy.ProxyServer.BufferSize*"></a> <a id="Titanium_Web_Proxy_ProxyServer_BufferSize_" data-uid="Titanium.Web.Proxy.ProxyServer.BufferSize*"></a>
<h4 id="Titanium_Web_Proxy_ProxyServer_BufferSize" data-uid="Titanium.Web.Proxy.ProxyServer.BufferSize">BufferSize</h4> <h4 id="Titanium_Web_Proxy_ProxyServer_BufferSize" data-uid="Titanium.Web.Proxy.ProxyServer.BufferSize">BufferSize</h4>
<div class="markdown level1 summary"><p>Buffer size used throughout this proxy.</p> <div class="markdown level1 summary"><p>Buffer size used throughout this proxy.</p>
...@@ -417,6 +443,33 @@ Defaults to false.</p> ...@@ -417,6 +443,33 @@ Defaults to false.</p>
</table> </table>
<a id="Titanium_Web_Proxy_ProxyServer_EnableConnectionPool_" data-uid="Titanium.Web.Proxy.ProxyServer.EnableConnectionPool*"></a>
<h4 id="Titanium_Web_Proxy_ProxyServer_EnableConnectionPool" data-uid="Titanium.Web.Proxy.ProxyServer.EnableConnectionPool">EnableConnectionPool</h4>
<div class="markdown level1 summary"><p>Should we enable experimental Tcp server connection pool?
Defaults to false.</p>
</div>
<div class="markdown level1 conceptual"></div>
<h5 class="decalaration">Declaration</h5>
<div class="codewrapper">
<pre><code class="lang-csharp hljs">public bool EnableConnectionPool { get; set; }</code></pre>
</div>
<h5 class="propertyValue">Property Value</h5>
<table class="table table-bordered table-striped table-condensed">
<thead>
<tr>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.boolean">Boolean</a></td>
<td></td>
</tr>
</tbody>
</table>
<a id="Titanium_Web_Proxy_ProxyServer_EnableWinAuth_" data-uid="Titanium.Web.Proxy.ProxyServer.EnableWinAuth*"></a> <a id="Titanium_Web_Proxy_ProxyServer_EnableWinAuth_" data-uid="Titanium.Web.Proxy.ProxyServer.EnableWinAuth*"></a>
<h4 id="Titanium_Web_Proxy_ProxyServer_EnableWinAuth" data-uid="Titanium.Web.Proxy.ProxyServer.EnableWinAuth">EnableWinAuth</h4> <h4 id="Titanium_Web_Proxy_ProxyServer_EnableWinAuth" data-uid="Titanium.Web.Proxy.ProxyServer.EnableWinAuth">EnableWinAuth</h4>
<div class="markdown level1 summary"><p>Enable disable Windows Authentication (NTLM/Kerberos). <div class="markdown level1 summary"><p>Enable disable Windows Authentication (NTLM/Kerberos).
...@@ -525,6 +578,34 @@ User should return the ExternalProxy object with valid credentials.</p> ...@@ -525,6 +578,34 @@ User should return the ExternalProxy object with valid credentials.</p>
</table> </table>
<a id="Titanium_Web_Proxy_ProxyServer_MaxCachedConnections_" data-uid="Titanium.Web.Proxy.ProxyServer.MaxCachedConnections*"></a>
<h4 id="Titanium_Web_Proxy_ProxyServer_MaxCachedConnections" data-uid="Titanium.Web.Proxy.ProxyServer.MaxCachedConnections">MaxCachedConnections</h4>
<div class="markdown level1 summary"><p>Maximum number of concurrent connections per remote host in cache.
Only valid when connection pooling is enabled.
Default value is 3.</p>
</div>
<div class="markdown level1 conceptual"></div>
<h5 class="decalaration">Declaration</h5>
<div class="codewrapper">
<pre><code class="lang-csharp hljs">public int MaxCachedConnections { get; set; }</code></pre>
</div>
<h5 class="propertyValue">Property Value</h5>
<table class="table table-bordered table-striped table-condensed">
<thead>
<tr>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.int32">Int32</a></td>
<td></td>
</tr>
</tbody>
</table>
<a id="Titanium_Web_Proxy_ProxyServer_ProxyEndPoints_" data-uid="Titanium.Web.Proxy.ProxyServer.ProxyEndPoints*"></a> <a id="Titanium_Web_Proxy_ProxyServer_ProxyEndPoints_" data-uid="Titanium.Web.Proxy.ProxyServer.ProxyEndPoints*"></a>
<h4 id="Titanium_Web_Proxy_ProxyServer_ProxyEndPoints" data-uid="Titanium.Web.Proxy.ProxyServer.ProxyEndPoints">ProxyEndPoints</h4> <h4 id="Titanium_Web_Proxy_ProxyServer_ProxyEndPoints" data-uid="Titanium.Web.Proxy.ProxyServer.ProxyEndPoints">ProxyEndPoints</h4>
<div class="markdown level1 summary"><p>A list of IpAddress and port this proxy is listening to.</p> <div class="markdown level1 summary"><p>A list of IpAddress and port this proxy is listening to.</p>
...@@ -1109,6 +1190,56 @@ Will throw error if the end point does&apos;nt exist.</p> ...@@ -1109,6 +1190,56 @@ Will throw error if the end point does&apos;nt exist.</p>
</table> </table>
<h4 id="Titanium_Web_Proxy_ProxyServer_OnClientConnectionCreate" data-uid="Titanium.Web.Proxy.ProxyServer.OnClientConnectionCreate">OnClientConnectionCreate</h4>
<div class="markdown level1 summary"><p>Customize TcpClient used for client connection upon create.</p>
</div>
<div class="markdown level1 conceptual"></div>
<h5 class="decalaration">Declaration</h5>
<div class="codewrapper">
<pre><code class="lang-csharp hljs">public event AsyncEventHandler&lt;TcpClient&gt; OnClientConnectionCreate</code></pre>
</div>
<h5 class="eventType">Event Type</h5>
<table class="table table-bordered table-striped table-condensed">
<thead>
<tr>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a class="xref" href="Titanium.Web.Proxy.EventArguments.AsyncEventHandler-1.html">AsyncEventHandler</a>&lt;<span class="xref">System.Net.Sockets.TcpClient</span>&gt;</td>
<td></td>
</tr>
</tbody>
</table>
<h4 id="Titanium_Web_Proxy_ProxyServer_OnServerConnectionCreate" data-uid="Titanium.Web.Proxy.ProxyServer.OnServerConnectionCreate">OnServerConnectionCreate</h4>
<div class="markdown level1 summary"><p>Customize TcpClient used for server connection upon create.</p>
</div>
<div class="markdown level1 conceptual"></div>
<h5 class="decalaration">Declaration</h5>
<div class="codewrapper">
<pre><code class="lang-csharp hljs">public event AsyncEventHandler&lt;TcpClient&gt; OnServerConnectionCreate</code></pre>
</div>
<h5 class="eventType">Event Type</h5>
<table class="table table-bordered table-striped table-condensed">
<thead>
<tr>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><a class="xref" href="Titanium.Web.Proxy.EventArguments.AsyncEventHandler-1.html">AsyncEventHandler</a>&lt;<span class="xref">System.Net.Sockets.TcpClient</span>&gt;</td>
<td></td>
</tr>
</tbody>
</table>
<h4 id="Titanium_Web_Proxy_ProxyServer_ServerCertificateValidationCallback" data-uid="Titanium.Web.Proxy.ProxyServer.ServerCertificateValidationCallback">ServerCertificateValidationCallback</h4> <h4 id="Titanium_Web_Proxy_ProxyServer_ServerCertificateValidationCallback" data-uid="Titanium.Web.Proxy.ProxyServer.ServerCertificateValidationCallback">ServerCertificateValidationCallback</h4>
<div class="markdown level1 summary"><p>Event to override the default verification logic of remote SSL certificate received during authentication.</p> <div class="markdown level1 summary"><p>Event to override the default verification logic of remote SSL certificate received during authentication.</p>
</div> </div>
......
...@@ -32,17 +32,17 @@ ...@@ -32,17 +32,17 @@
"api/Titanium.Web.Proxy.EventArguments.SessionEventArgs.html": { "api/Titanium.Web.Proxy.EventArguments.SessionEventArgs.html": {
"href": "api/Titanium.Web.Proxy.EventArguments.SessionEventArgs.html", "href": "api/Titanium.Web.Proxy.EventArguments.SessionEventArgs.html",
"title": "Class SessionEventArgs | Titanium Web Proxy", "title": "Class SessionEventArgs | Titanium Web Proxy",
"keywords": "Class SessionEventArgs Holds info related to a single proxy session (single request/response sequence). A proxy session is bounded to a single connection from client. A proxy session ends when client terminates connection to proxy or when server terminates connection from proxy. Inheritance Object EventArgs SessionEventArgsBase SessionEventArgs Implements IDisposable Inherited Members SessionEventArgsBase.BufferSize SessionEventArgsBase.ExceptionFunc SessionEventArgsBase.UserData SessionEventArgsBase.IsHttps SessionEventArgsBase.ClientEndPoint SessionEventArgsBase.WebSession SessionEventArgsBase.CustomUpStreamProxyUsed SessionEventArgsBase.LocalEndPoint SessionEventArgsBase.IsTransparent SessionEventArgsBase.Exception SessionEventArgsBase.DataSent SessionEventArgsBase.DataReceived SessionEventArgsBase.TerminateSession() EventArgs.Empty Object.ToString() Object.Equals(Object) Object.Equals(Object, Object) Object.ReferenceEquals(Object, Object) Object.GetHashCode() Object.GetType() Object.MemberwiseClone() Namespace : Titanium.Web.Proxy.EventArguments Assembly : Titanium.Web.Proxy.dll Syntax public class SessionEventArgs : SessionEventArgsBase, IDisposable Constructors SessionEventArgs(Int32, ProxyEndPoint, Request, CancellationTokenSource, ExceptionHandler) Declaration protected SessionEventArgs(int bufferSize, ProxyEndPoint endPoint, Request request, CancellationTokenSource cancellationTokenSource, ExceptionHandler exceptionFunc) Parameters Type Name Description Int32 bufferSize ProxyEndPoint endPoint Request request CancellationTokenSource cancellationTokenSource ExceptionHandler exceptionFunc Properties ReRequest Should we send the request again ? Declaration public bool ReRequest { get; set; } Property Value Type Description Boolean Methods Dispose() Implement any cleanup here Declaration public override void Dispose() Overrides SessionEventArgsBase.Dispose() GenericResponse(Byte[], HttpStatusCode, Dictionary<String, HttpHeader>) Before request is made to server respond with the specified byte[], the specified status to client. And then ignore the request. Declaration public void GenericResponse(byte[] result, HttpStatusCode status, Dictionary<string, HttpHeader> headers) Parameters Type Name Description System.Byte [] result The bytes to sent. HttpStatusCode status The HTTP status code. Dictionary < String , HttpHeader > headers The HTTP headers. GenericResponse(String, HttpStatusCode, Dictionary<String, HttpHeader>) Before request is made to server respond with the specified HTML string and the specified status to client. And then ignore the request. Declaration public void GenericResponse(string html, HttpStatusCode status, Dictionary<string, HttpHeader> headers = null) Parameters Type Name Description String html The html content. HttpStatusCode status The HTTP status code. Dictionary < String , HttpHeader > headers The HTTP headers. GetRequestBody(CancellationToken) Gets the request body as bytes. Declaration public Task<byte[]> GetRequestBody(CancellationToken cancellationToken = default(CancellationToken)) Parameters Type Name Description CancellationToken cancellationToken Optional cancellation token for this async task. Returns Type Description Task < System.Byte []> The body as bytes. GetRequestBodyAsString(CancellationToken) Gets the request body as string. Declaration public Task<string> GetRequestBodyAsString(CancellationToken cancellationToken = default(CancellationToken)) Parameters Type Name Description CancellationToken cancellationToken Optional cancellation token for this async task. Returns Type Description Task < String > The body as string. GetResponseBody(CancellationToken) Gets the response body as bytes. Declaration public Task<byte[]> GetResponseBody(CancellationToken cancellationToken = default(CancellationToken)) Parameters Type Name Description CancellationToken cancellationToken Optional cancellation token for this async task. Returns Type Description Task < System.Byte []> The resulting bytes. GetResponseBodyAsString(CancellationToken) Gets the response body as string. Declaration public Task<string> GetResponseBodyAsString(CancellationToken cancellationToken = default(CancellationToken)) Parameters Type Name Description CancellationToken cancellationToken Optional cancellation token for this async task. Returns Type Description Task < String > The string body. Ok(Byte[], Dictionary<String, HttpHeader>) Before request is made to server respond with the specified byte[] to client and ignore the request. Declaration public void Ok(byte[] result, Dictionary<string, HttpHeader> headers = null) Parameters Type Name Description System.Byte [] result The html content bytes. Dictionary < String , HttpHeader > headers The HTTP headers. Ok(String, Dictionary<String, HttpHeader>) Before request is made to server respond with the specified HTML string to client and ignore the request. Declaration public void Ok(string html, Dictionary<string, HttpHeader> headers = null) Parameters Type Name Description String html HTML content to sent. Dictionary < String , HttpHeader > headers HTTP response headers. Redirect(String) Redirect to provided URL. Declaration public void Redirect(string url) Parameters Type Name Description String url The URL to redirect. Respond(Response) Respond with given response object to client. Declaration public void Respond(Response response) Parameters Type Name Description Response response The response object. SetRequestBody(Byte[]) Sets the request body. Declaration public void SetRequestBody(byte[] body) Parameters Type Name Description System.Byte [] body The request body bytes. SetRequestBodyString(String) Sets the body with the specified string. Declaration public void SetRequestBodyString(string body) Parameters Type Name Description String body The request body string to set. SetResponseBody(Byte[]) Set the response body bytes. Declaration public void SetResponseBody(byte[] body) Parameters Type Name Description System.Byte [] body The body bytes to set. SetResponseBodyString(String) Replace the response body with the specified string. Declaration public void SetResponseBodyString(string body) Parameters Type Name Description String body The body string to set. TerminateServerConnection() Terminate the connection to server. Declaration public void TerminateServerConnection() Events MultipartRequestPartSent Occurs when multipart request part sent. Declaration public event EventHandler<MultipartRequestPartSentEventArgs> MultipartRequestPartSent Event Type Type Description EventHandler < MultipartRequestPartSentEventArgs > Implements System.IDisposable" "keywords": "Class SessionEventArgs Holds info related to a single proxy session (single request/response sequence). A proxy session is bounded to a single connection from client. A proxy session ends when client terminates connection to proxy or when server terminates connection from proxy. Inheritance Object EventArgs SessionEventArgsBase SessionEventArgs Implements IDisposable Inherited Members SessionEventArgsBase.bufferSize SessionEventArgsBase.bufferPool SessionEventArgsBase.exceptionFunc SessionEventArgsBase.UserData SessionEventArgsBase.IsHttps SessionEventArgsBase.ClientEndPoint SessionEventArgsBase.WebSession SessionEventArgsBase.CustomUpStreamProxyUsed SessionEventArgsBase.LocalEndPoint SessionEventArgsBase.IsTransparent SessionEventArgsBase.Exception SessionEventArgsBase.DataSent SessionEventArgsBase.DataReceived SessionEventArgsBase.TerminateSession() EventArgs.Empty Object.ToString() Object.Equals(Object) Object.Equals(Object, Object) Object.ReferenceEquals(Object, Object) Object.GetHashCode() Object.GetType() Object.MemberwiseClone() Namespace : Titanium.Web.Proxy.EventArguments Assembly : Titanium.Web.Proxy.dll Syntax public class SessionEventArgs : SessionEventArgsBase, IDisposable Constructors SessionEventArgs(ProxyServer, ProxyEndPoint, Request, CancellationTokenSource) Declaration protected SessionEventArgs(ProxyServer server, ProxyEndPoint endPoint, Request request, CancellationTokenSource cancellationTokenSource) Parameters Type Name Description ProxyServer server ProxyEndPoint endPoint Request request CancellationTokenSource cancellationTokenSource Properties ReRequest Should we send the request again ? Declaration public bool ReRequest { get; set; } Property Value Type Description Boolean Methods Dispose() Implement any cleanup here Declaration public override void Dispose() Overrides SessionEventArgsBase.Dispose() GenericResponse(Byte[], HttpStatusCode, Dictionary<String, HttpHeader>) Before request is made to server respond with the specified byte[], the specified status to client. And then ignore the request. Declaration public void GenericResponse(byte[] result, HttpStatusCode status, Dictionary<string, HttpHeader> headers) Parameters Type Name Description System.Byte [] result The bytes to sent. HttpStatusCode status The HTTP status code. Dictionary < String , HttpHeader > headers The HTTP headers. GenericResponse(String, HttpStatusCode, Dictionary<String, HttpHeader>) Before request is made to server respond with the specified HTML string and the specified status to client. And then ignore the request. Declaration public void GenericResponse(string html, HttpStatusCode status, Dictionary<string, HttpHeader> headers = null) Parameters Type Name Description String html The html content. HttpStatusCode status The HTTP status code. Dictionary < String , HttpHeader > headers The HTTP headers. GetRequestBody(CancellationToken) Gets the request body as bytes. Declaration public Task<byte[]> GetRequestBody(CancellationToken cancellationToken = default(CancellationToken)) Parameters Type Name Description CancellationToken cancellationToken Optional cancellation token for this async task. Returns Type Description Task < System.Byte []> The body as bytes. GetRequestBodyAsString(CancellationToken) Gets the request body as string. Declaration public Task<string> GetRequestBodyAsString(CancellationToken cancellationToken = default(CancellationToken)) Parameters Type Name Description CancellationToken cancellationToken Optional cancellation token for this async task. Returns Type Description Task < String > The body as string. GetResponseBody(CancellationToken) Gets the response body as bytes. Declaration public Task<byte[]> GetResponseBody(CancellationToken cancellationToken = default(CancellationToken)) Parameters Type Name Description CancellationToken cancellationToken Optional cancellation token for this async task. Returns Type Description Task < System.Byte []> The resulting bytes. GetResponseBodyAsString(CancellationToken) Gets the response body as string. Declaration public Task<string> GetResponseBodyAsString(CancellationToken cancellationToken = default(CancellationToken)) Parameters Type Name Description CancellationToken cancellationToken Optional cancellation token for this async task. Returns Type Description Task < String > The string body. Ok(Byte[], Dictionary<String, HttpHeader>) Before request is made to server respond with the specified byte[] to client and ignore the request. Declaration public void Ok(byte[] result, Dictionary<string, HttpHeader> headers = null) Parameters Type Name Description System.Byte [] result The html content bytes. Dictionary < String , HttpHeader > headers The HTTP headers. Ok(String, Dictionary<String, HttpHeader>) Before request is made to server respond with the specified HTML string to client and ignore the request. Declaration public void Ok(string html, Dictionary<string, HttpHeader> headers = null) Parameters Type Name Description String html HTML content to sent. Dictionary < String , HttpHeader > headers HTTP response headers. Redirect(String) Redirect to provided URL. Declaration public void Redirect(string url) Parameters Type Name Description String url The URL to redirect. Respond(Response) Respond with given response object to client. Declaration public void Respond(Response response) Parameters Type Name Description Response response The response object. SetRequestBody(Byte[]) Sets the request body. Declaration public void SetRequestBody(byte[] body) Parameters Type Name Description System.Byte [] body The request body bytes. SetRequestBodyString(String) Sets the body with the specified string. Declaration public void SetRequestBodyString(string body) Parameters Type Name Description String body The request body string to set. SetResponseBody(Byte[]) Set the response body bytes. Declaration public void SetResponseBody(byte[] body) Parameters Type Name Description System.Byte [] body The body bytes to set. SetResponseBodyString(String) Replace the response body with the specified string. Declaration public void SetResponseBodyString(string body) Parameters Type Name Description String body The body string to set. TerminateServerConnection() Terminate the connection to server. Declaration public void TerminateServerConnection() Events MultipartRequestPartSent Occurs when multipart request part sent. Declaration public event EventHandler<MultipartRequestPartSentEventArgs> MultipartRequestPartSent Event Type Type Description EventHandler < MultipartRequestPartSentEventArgs > Implements System.IDisposable"
}, },
"api/Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html": { "api/Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html": {
"href": "api/Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html", "href": "api/Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html",
"title": "Class SessionEventArgsBase | Titanium Web Proxy", "title": "Class SessionEventArgsBase | Titanium Web Proxy",
"keywords": "Class SessionEventArgsBase Holds info related to a single proxy session (single request/response sequence). A proxy session is bounded to a single connection from client. A proxy session ends when client terminates connection to proxy or when server terminates connection from proxy. Inheritance Object EventArgs SessionEventArgsBase SessionEventArgs TunnelConnectSessionEventArgs Implements IDisposable Inherited Members EventArgs.Empty Object.ToString() Object.Equals(Object) Object.Equals(Object, Object) Object.ReferenceEquals(Object, Object) Object.GetHashCode() Object.GetType() Object.MemberwiseClone() Namespace : Titanium.Web.Proxy.EventArguments Assembly : Titanium.Web.Proxy.dll Syntax public abstract class SessionEventArgsBase : EventArgs, IDisposable Constructors SessionEventArgsBase(Int32, ProxyEndPoint, CancellationTokenSource, Request, ExceptionHandler) Declaration protected SessionEventArgsBase(int bufferSize, ProxyEndPoint endPoint, CancellationTokenSource cancellationTokenSource, Request request, ExceptionHandler exceptionFunc) Parameters Type Name Description Int32 bufferSize ProxyEndPoint endPoint CancellationTokenSource cancellationTokenSource Request request ExceptionHandler exceptionFunc Fields BufferSize Size of Buffers used by this object Declaration protected readonly int BufferSize Field Value Type Description Int32 ExceptionFunc Declaration protected readonly ExceptionHandler ExceptionFunc Field Value Type Description ExceptionHandler Properties ClientEndPoint Client End Point. Declaration public IPEndPoint ClientEndPoint { get; } Property Value Type Description IPEndPoint CustomUpStreamProxyUsed Are we using a custom upstream HTTP(S) proxy? Declaration public ExternalProxy CustomUpStreamProxyUsed { get; } Property Value Type Description ExternalProxy Exception The last exception that happened. Declaration public Exception Exception { get; } Property Value Type Description Exception IsHttps Does this session uses SSL? Declaration public bool IsHttps { get; } Property Value Type Description Boolean IsTransparent Is this a transparent endpoint? Declaration public bool IsTransparent { get; } Property Value Type Description Boolean LocalEndPoint Local endpoint via which we make the request. Declaration public ProxyEndPoint LocalEndPoint { get; } Property Value Type Description ProxyEndPoint UserData Returns a user data for this request/response session which is same as the user data of WebSession. Declaration public object UserData { get; set; } Property Value Type Description Object WebSession A web session corresponding to a single request/response sequence within a proxy connection. Declaration public HttpWebClient WebSession { get; } Property Value Type Description HttpWebClient Methods Dispose() Implements cleanup here. Declaration public virtual void Dispose() TerminateSession() Terminates the session abruptly by terminating client/server connections. Declaration public void TerminateSession() Events DataReceived Fired when data is received within this session from client/server. Declaration public event EventHandler<DataEventArgs> DataReceived Event Type Type Description EventHandler < StreamExtended.Network.DataEventArgs > DataSent Fired when data is sent within this session to server/client. Declaration public event EventHandler<DataEventArgs> DataSent Event Type Type Description EventHandler < StreamExtended.Network.DataEventArgs > Implements System.IDisposable" "keywords": "Class SessionEventArgsBase Holds info related to a single proxy session (single request/response sequence). A proxy session is bounded to a single connection from client. A proxy session ends when client terminates connection to proxy or when server terminates connection from proxy. Inheritance Object EventArgs SessionEventArgsBase SessionEventArgs TunnelConnectSessionEventArgs Implements IDisposable Inherited Members EventArgs.Empty Object.ToString() Object.Equals(Object) Object.Equals(Object, Object) Object.ReferenceEquals(Object, Object) Object.GetHashCode() Object.GetType() Object.MemberwiseClone() Namespace : Titanium.Web.Proxy.EventArguments Assembly : Titanium.Web.Proxy.dll Syntax public abstract class SessionEventArgsBase : EventArgs, IDisposable Constructors SessionEventArgsBase(ProxyServer, ProxyEndPoint, CancellationTokenSource, Request) Declaration protected SessionEventArgsBase(ProxyServer server, ProxyEndPoint endPoint, CancellationTokenSource cancellationTokenSource, Request request) Parameters Type Name Description ProxyServer server ProxyEndPoint endPoint CancellationTokenSource cancellationTokenSource Request request Fields bufferPool Declaration protected readonly IBufferPool bufferPool Field Value Type Description StreamExtended.IBufferPool bufferSize Declaration protected readonly int bufferSize Field Value Type Description Int32 exceptionFunc Declaration protected readonly ExceptionHandler exceptionFunc Field Value Type Description ExceptionHandler Properties ClientEndPoint Client End Point. Declaration public IPEndPoint ClientEndPoint { get; } Property Value Type Description IPEndPoint CustomUpStreamProxyUsed Are we using a custom upstream HTTP(S) proxy? Declaration public ExternalProxy CustomUpStreamProxyUsed { get; } Property Value Type Description ExternalProxy Exception The last exception that happened. Declaration public Exception Exception { get; } Property Value Type Description Exception IsHttps Does this session uses SSL? Declaration public bool IsHttps { get; } Property Value Type Description Boolean IsTransparent Is this a transparent endpoint? Declaration public bool IsTransparent { get; } Property Value Type Description Boolean LocalEndPoint Local endpoint via which we make the request. Declaration public ProxyEndPoint LocalEndPoint { get; } Property Value Type Description ProxyEndPoint UserData Returns a user data for this request/response session which is same as the user data of WebSession. Declaration public object UserData { get; set; } Property Value Type Description Object WebSession A web session corresponding to a single request/response sequence within a proxy connection. Declaration public HttpWebClient WebSession { get; } Property Value Type Description HttpWebClient Methods Dispose() Implements cleanup here. Declaration public virtual void Dispose() TerminateSession() Terminates the session abruptly by terminating client/server connections. Declaration public void TerminateSession() Events DataReceived Fired when data is received within this session from client/server. Declaration public event EventHandler<DataEventArgs> DataReceived Event Type Type Description EventHandler < StreamExtended.Network.DataEventArgs > DataSent Fired when data is sent within this session to server/client. Declaration public event EventHandler<DataEventArgs> DataSent Event Type Type Description EventHandler < StreamExtended.Network.DataEventArgs > Implements System.IDisposable"
}, },
"api/Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.html": { "api/Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.html": {
"href": "api/Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.html", "href": "api/Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.html",
"title": "Class TunnelConnectSessionEventArgs | Titanium Web Proxy", "title": "Class TunnelConnectSessionEventArgs | Titanium Web Proxy",
"keywords": "Class TunnelConnectSessionEventArgs A class that wraps the state when a tunnel connect event happen for Explicit endpoints. Inheritance Object EventArgs SessionEventArgsBase TunnelConnectSessionEventArgs Implements IDisposable Inherited Members SessionEventArgsBase.BufferSize SessionEventArgsBase.ExceptionFunc SessionEventArgsBase.UserData SessionEventArgsBase.IsHttps SessionEventArgsBase.ClientEndPoint SessionEventArgsBase.WebSession SessionEventArgsBase.CustomUpStreamProxyUsed SessionEventArgsBase.LocalEndPoint SessionEventArgsBase.IsTransparent SessionEventArgsBase.Exception SessionEventArgsBase.Dispose() SessionEventArgsBase.DataSent SessionEventArgsBase.DataReceived SessionEventArgsBase.TerminateSession() EventArgs.Empty Object.ToString() Object.Equals(Object) Object.Equals(Object, Object) Object.ReferenceEquals(Object, Object) Object.GetHashCode() Object.GetType() Object.MemberwiseClone() Namespace : Titanium.Web.Proxy.EventArguments Assembly : Titanium.Web.Proxy.dll Syntax public class TunnelConnectSessionEventArgs : SessionEventArgsBase, IDisposable Properties DecryptSsl Should we decrypt the Ssl or relay it to server? Default is true. Declaration public bool DecryptSsl { get; set; } Property Value Type Description Boolean DenyConnect When set to true it denies the connect request with a Forbidden status. Declaration public bool DenyConnect { get; set; } Property Value Type Description Boolean IsHttpsConnect Is this a connect request to secure HTTP server? Or is it to someother protocol. Declaration public bool IsHttpsConnect { get; } Property Value Type Description Boolean Implements System.IDisposable" "keywords": "Class TunnelConnectSessionEventArgs A class that wraps the state when a tunnel connect event happen for Explicit endpoints. Inheritance Object EventArgs SessionEventArgsBase TunnelConnectSessionEventArgs Implements IDisposable Inherited Members SessionEventArgsBase.bufferSize SessionEventArgsBase.bufferPool SessionEventArgsBase.exceptionFunc SessionEventArgsBase.UserData SessionEventArgsBase.IsHttps SessionEventArgsBase.ClientEndPoint SessionEventArgsBase.WebSession SessionEventArgsBase.CustomUpStreamProxyUsed SessionEventArgsBase.LocalEndPoint SessionEventArgsBase.IsTransparent SessionEventArgsBase.Exception SessionEventArgsBase.Dispose() SessionEventArgsBase.DataSent SessionEventArgsBase.DataReceived SessionEventArgsBase.TerminateSession() EventArgs.Empty Object.ToString() Object.Equals(Object) Object.Equals(Object, Object) Object.ReferenceEquals(Object, Object) Object.GetHashCode() Object.GetType() Object.MemberwiseClone() Namespace : Titanium.Web.Proxy.EventArguments Assembly : Titanium.Web.Proxy.dll Syntax public class TunnelConnectSessionEventArgs : SessionEventArgsBase, IDisposable Properties DecryptSsl Should we decrypt the Ssl or relay it to server? Default is true. Declaration public bool DecryptSsl { get; set; } Property Value Type Description Boolean DenyConnect When set to true it denies the connect request with a Forbidden status. Declaration public bool DenyConnect { get; set; } Property Value Type Description Boolean IsHttpsConnect Is this a connect request to secure HTTP server? Or is it to someother protocol. Declaration public bool IsHttpsConnect { get; } Property Value Type Description Boolean Implements System.IDisposable"
}, },
"api/Titanium.Web.Proxy.ExceptionHandler.html": { "api/Titanium.Web.Proxy.ExceptionHandler.html": {
"href": "api/Titanium.Web.Proxy.ExceptionHandler.html", "href": "api/Titanium.Web.Proxy.ExceptionHandler.html",
...@@ -192,6 +192,6 @@ ...@@ -192,6 +192,6 @@
"api/Titanium.Web.Proxy.ProxyServer.html": { "api/Titanium.Web.Proxy.ProxyServer.html": {
"href": "api/Titanium.Web.Proxy.ProxyServer.html", "href": "api/Titanium.Web.Proxy.ProxyServer.html",
"title": "Class ProxyServer | Titanium Web Proxy", "title": "Class ProxyServer | Titanium Web Proxy",
"keywords": "Class ProxyServer This class is the backbone of proxy. One can create as many instances as needed. However care should be taken to avoid using the same listening ports across multiple instances. Inheritance Object ProxyServer Implements IDisposable Inherited Members Object.ToString() Object.Equals(Object) Object.Equals(Object, Object) Object.ReferenceEquals(Object, Object) Object.GetHashCode() Object.GetType() Object.MemberwiseClone() Namespace : Titanium.Web.Proxy Assembly : Titanium.Web.Proxy.dll Syntax public class ProxyServer : IDisposable Constructors ProxyServer(Boolean, Boolean, Boolean) Initializes a new instance of ProxyServer class with provided parameters. Declaration public ProxyServer(bool userTrustRootCertificate = true, bool machineTrustRootCertificate = false, bool trustRootCertificateAsAdmin = false) Parameters Type Name Description Boolean userTrustRootCertificate Should fake HTTPS certificate be trusted by this machine's user certificate store? Boolean machineTrustRootCertificate Should fake HTTPS certificate be trusted by this machine's certificate store? Boolean trustRootCertificateAsAdmin Should we attempt to trust certificates with elevated permissions by prompting for UAC if required? ProxyServer(String, String, Boolean, Boolean, Boolean) Initializes a new instance of ProxyServer class with provided parameters. Declaration public ProxyServer(string rootCertificateName, string rootCertificateIssuerName, bool userTrustRootCertificate = true, bool machineTrustRootCertificate = false, bool trustRootCertificateAsAdmin = false) Parameters Type Name Description String rootCertificateName Name of the root certificate. String rootCertificateIssuerName Name of the root certificate issuer. Boolean userTrustRootCertificate Should fake HTTPS certificate be trusted by this machine's user certificate store? Boolean machineTrustRootCertificate Should fake HTTPS certificate be trusted by this machine's certificate store? Boolean trustRootCertificateAsAdmin Should we attempt to trust certificates with elevated permissions by prompting for UAC if required? Properties AuthenticateUserFunc A callback to authenticate clients. Parameters are username and password as provided by client. Should return true for successful authentication. Declaration public Func<string, string, Task<bool>> AuthenticateUserFunc { get; set; } Property Value Type Description Func < String , String , Task < Boolean >> BufferSize Buffer size used throughout this proxy. Declaration public int BufferSize { get; set; } Property Value Type Description Int32 CertificateManager Manages certificates used by this proxy. Declaration public CertificateManager CertificateManager { get; } Property Value Type Description CertificateManager CheckCertificateRevocation Should we check for certificare revocation during SSL authentication to servers Note: If enabled can reduce performance. Defaults to false. Declaration public X509RevocationMode CheckCertificateRevocation { get; set; } Property Value Type Description X509RevocationMode ClientConnectionCount Total number of active client connections. Declaration public int ClientConnectionCount { get; } Property Value Type Description Int32 ConnectionTimeOutSeconds Seconds client/server connection are to be kept alive when waiting for read/write to complete. Declaration public int ConnectionTimeOutSeconds { get; set; } Property Value Type Description Int32 Enable100ContinueBehaviour Does this proxy uses the HTTP protocol 100 continue behaviour strictly? Broken 100 contunue implementations on server/client may cause problems if enabled. Defaults to false. Declaration public bool Enable100ContinueBehaviour { get; set; } Property Value Type Description Boolean EnableWinAuth Enable disable Windows Authentication (NTLM/Kerberos). Note: NTLM/Kerberos will always send local credentials of current user running the proxy process. This is because a man in middle attack with Windows domain authentication is not currently supported. Declaration public bool EnableWinAuth { get; set; } Property Value Type Description Boolean ExceptionFunc Callback for error events in this proxy instance. Declaration public ExceptionHandler ExceptionFunc { get; set; } Property Value Type Description ExceptionHandler ForwardToUpstreamGateway Gets or sets a value indicating whether requests will be chained to upstream gateway. Declaration public bool ForwardToUpstreamGateway { get; set; } Property Value Type Description Boolean GetCustomUpStreamProxyFunc A callback to provide authentication credentials for up stream proxy this proxy is using for HTTP(S) requests. User should return the ExternalProxy object with valid credentials. Declaration public Func<SessionEventArgsBase, Task<ExternalProxy>> GetCustomUpStreamProxyFunc { get; set; } Property Value Type Description Func < SessionEventArgsBase , Task < ExternalProxy >> ProxyEndPoints A list of IpAddress and port this proxy is listening to. Declaration public List<ProxyEndPoint> ProxyEndPoints { get; set; } Property Value Type Description List < ProxyEndPoint > ProxyRealm Realm used during Proxy Basic Authentication. Declaration public string ProxyRealm { get; set; } Property Value Type Description String ProxyRunning Is the proxy currently running? Declaration public bool ProxyRunning { get; } Property Value Type Description Boolean ServerConnectionCount Total number of active server connections. Declaration public int ServerConnectionCount { get; } Property Value Type Description Int32 SupportedSslProtocols List of supported Ssl versions. Declaration public SslProtocols SupportedSslProtocols { get; set; } Property Value Type Description SslProtocols UpStreamEndPoint Local adapter/NIC endpoint where proxy makes request via. Defaults via any IP addresses of this machine. Declaration public IPEndPoint UpStreamEndPoint { get; set; } Property Value Type Description IPEndPoint UpStreamHttpProxy External proxy used for Http requests. Declaration public ExternalProxy UpStreamHttpProxy { get; set; } Property Value Type Description ExternalProxy UpStreamHttpsProxy External proxy used for Https requests. Declaration public ExternalProxy UpStreamHttpsProxy { get; set; } Property Value Type Description ExternalProxy Methods AddEndPoint(ProxyEndPoint) Add a proxy end point. Declaration public void AddEndPoint(ProxyEndPoint endPoint) Parameters Type Name Description ProxyEndPoint endPoint The proxy endpoint. DisableAllSystemProxies() Clear all proxy settings for current machine. Declaration public void DisableAllSystemProxies() DisableSystemHttpProxy() Clear HTTP proxy settings of current machine. Declaration public void DisableSystemHttpProxy() DisableSystemHttpsProxy() Clear HTTPS proxy settings of current machine. Declaration public void DisableSystemHttpsProxy() DisableSystemProxy(ProxyProtocolType) Clear the specified proxy setting for current machine. Declaration public void DisableSystemProxy(ProxyProtocolType protocolType) Parameters Type Name Description ProxyProtocolType protocolType Dispose() Dispose the Proxy instance. Declaration public void Dispose() RemoveEndPoint(ProxyEndPoint) Remove a proxy end point. Will throw error if the end point does'nt exist. Declaration public void RemoveEndPoint(ProxyEndPoint endPoint) Parameters Type Name Description ProxyEndPoint endPoint The existing endpoint to remove. SetAsSystemHttpProxy(ExplicitProxyEndPoint) Set the given explicit end point as the default proxy server for current machine. Declaration public void SetAsSystemHttpProxy(ExplicitProxyEndPoint endPoint) Parameters Type Name Description ExplicitProxyEndPoint endPoint The explicit endpoint. SetAsSystemHttpsProxy(ExplicitProxyEndPoint) Set the given explicit end point as the default proxy server for current machine. Declaration public void SetAsSystemHttpsProxy(ExplicitProxyEndPoint endPoint) Parameters Type Name Description ExplicitProxyEndPoint endPoint The explicit endpoint. SetAsSystemProxy(ExplicitProxyEndPoint, ProxyProtocolType) Set the given explicit end point as the default proxy server for current machine. Declaration public void SetAsSystemProxy(ExplicitProxyEndPoint endPoint, ProxyProtocolType protocolType) Parameters Type Name Description ExplicitProxyEndPoint endPoint The explicit endpoint. ProxyProtocolType protocolType The proxy protocol type. Start() Start this proxy server instance. Declaration public void Start() Stop() Stop this proxy server instance. Declaration public void Stop() Events AfterResponse Intercept after response event from server. Declaration public event AsyncEventHandler<SessionEventArgs> AfterResponse Event Type Type Description AsyncEventHandler < SessionEventArgs > BeforeRequest Intercept request event to server. Declaration public event AsyncEventHandler<SessionEventArgs> BeforeRequest Event Type Type Description AsyncEventHandler < SessionEventArgs > BeforeResponse Intercept response event from server. Declaration public event AsyncEventHandler<SessionEventArgs> BeforeResponse Event Type Type Description AsyncEventHandler < SessionEventArgs > ClientCertificateSelectionCallback Event to override client certificate selection during mutual SSL authentication. Declaration public event AsyncEventHandler<CertificateSelectionEventArgs> ClientCertificateSelectionCallback Event Type Type Description AsyncEventHandler < CertificateSelectionEventArgs > ClientConnectionCountChanged Event occurs when client connection count changed. Declaration public event EventHandler ClientConnectionCountChanged Event Type Type Description EventHandler ServerCertificateValidationCallback Event to override the default verification logic of remote SSL certificate received during authentication. Declaration public event AsyncEventHandler<CertificateValidationEventArgs> ServerCertificateValidationCallback Event Type Type Description AsyncEventHandler < CertificateValidationEventArgs > ServerConnectionCountChanged Event occurs when server connection count changed. Declaration public event EventHandler ServerConnectionCountChanged Event Type Type Description EventHandler Implements System.IDisposable" "keywords": "Class ProxyServer This class is the backbone of proxy. One can create as many instances as needed. However care should be taken to avoid using the same listening ports across multiple instances. Inheritance Object ProxyServer Implements IDisposable Inherited Members Object.ToString() Object.Equals(Object) Object.Equals(Object, Object) Object.ReferenceEquals(Object, Object) Object.GetHashCode() Object.GetType() Object.MemberwiseClone() Namespace : Titanium.Web.Proxy Assembly : Titanium.Web.Proxy.dll Syntax public class ProxyServer : IDisposable Constructors ProxyServer(Boolean, Boolean, Boolean) Initializes a new instance of ProxyServer class with provided parameters. Declaration public ProxyServer(bool userTrustRootCertificate = true, bool machineTrustRootCertificate = false, bool trustRootCertificateAsAdmin = false) Parameters Type Name Description Boolean userTrustRootCertificate Should fake HTTPS certificate be trusted by this machine's user certificate store? Boolean machineTrustRootCertificate Should fake HTTPS certificate be trusted by this machine's certificate store? Boolean trustRootCertificateAsAdmin Should we attempt to trust certificates with elevated permissions by prompting for UAC if required? ProxyServer(String, String, Boolean, Boolean, Boolean) Initializes a new instance of ProxyServer class with provided parameters. Declaration public ProxyServer(string rootCertificateName, string rootCertificateIssuerName, bool userTrustRootCertificate = true, bool machineTrustRootCertificate = false, bool trustRootCertificateAsAdmin = false) Parameters Type Name Description String rootCertificateName Name of the root certificate. String rootCertificateIssuerName Name of the root certificate issuer. Boolean userTrustRootCertificate Should fake HTTPS certificate be trusted by this machine's user certificate store? Boolean machineTrustRootCertificate Should fake HTTPS certificate be trusted by this machine's certificate store? Boolean trustRootCertificateAsAdmin Should we attempt to trust certificates with elevated permissions by prompting for UAC if required? Properties AuthenticateUserFunc A callback to authenticate clients. Parameters are username and password as provided by client. Should return true for successful authentication. Declaration public Func<string, string, Task<bool>> AuthenticateUserFunc { get; set; } Property Value Type Description Func < String , String , Task < Boolean >> BufferPool The buffer pool used throughout this proxy instance. Declaration public IBufferPool BufferPool { get; set; } Property Value Type Description StreamExtended.IBufferPool BufferSize Buffer size used throughout this proxy. Declaration public int BufferSize { get; set; } Property Value Type Description Int32 CertificateManager Manages certificates used by this proxy. Declaration public CertificateManager CertificateManager { get; } Property Value Type Description CertificateManager CheckCertificateRevocation Should we check for certificare revocation during SSL authentication to servers Note: If enabled can reduce performance. Defaults to false. Declaration public X509RevocationMode CheckCertificateRevocation { get; set; } Property Value Type Description X509RevocationMode ClientConnectionCount Total number of active client connections. Declaration public int ClientConnectionCount { get; } Property Value Type Description Int32 ConnectionTimeOutSeconds Seconds client/server connection are to be kept alive when waiting for read/write to complete. Declaration public int ConnectionTimeOutSeconds { get; set; } Property Value Type Description Int32 Enable100ContinueBehaviour Does this proxy uses the HTTP protocol 100 continue behaviour strictly? Broken 100 contunue implementations on server/client may cause problems if enabled. Defaults to false. Declaration public bool Enable100ContinueBehaviour { get; set; } Property Value Type Description Boolean EnableConnectionPool Should we enable experimental Tcp server connection pool? Defaults to false. Declaration public bool EnableConnectionPool { get; set; } Property Value Type Description Boolean EnableWinAuth Enable disable Windows Authentication (NTLM/Kerberos). Note: NTLM/Kerberos will always send local credentials of current user running the proxy process. This is because a man in middle attack with Windows domain authentication is not currently supported. Declaration public bool EnableWinAuth { get; set; } Property Value Type Description Boolean ExceptionFunc Callback for error events in this proxy instance. Declaration public ExceptionHandler ExceptionFunc { get; set; } Property Value Type Description ExceptionHandler ForwardToUpstreamGateway Gets or sets a value indicating whether requests will be chained to upstream gateway. Declaration public bool ForwardToUpstreamGateway { get; set; } Property Value Type Description Boolean GetCustomUpStreamProxyFunc A callback to provide authentication credentials for up stream proxy this proxy is using for HTTP(S) requests. User should return the ExternalProxy object with valid credentials. Declaration public Func<SessionEventArgsBase, Task<ExternalProxy>> GetCustomUpStreamProxyFunc { get; set; } Property Value Type Description Func < SessionEventArgsBase , Task < ExternalProxy >> MaxCachedConnections Maximum number of concurrent connections per remote host in cache. Only valid when connection pooling is enabled. Default value is 3. Declaration public int MaxCachedConnections { get; set; } Property Value Type Description Int32 ProxyEndPoints A list of IpAddress and port this proxy is listening to. Declaration public List<ProxyEndPoint> ProxyEndPoints { get; set; } Property Value Type Description List < ProxyEndPoint > ProxyRealm Realm used during Proxy Basic Authentication. Declaration public string ProxyRealm { get; set; } Property Value Type Description String ProxyRunning Is the proxy currently running? Declaration public bool ProxyRunning { get; } Property Value Type Description Boolean ServerConnectionCount Total number of active server connections. Declaration public int ServerConnectionCount { get; } Property Value Type Description Int32 SupportedSslProtocols List of supported Ssl versions. Declaration public SslProtocols SupportedSslProtocols { get; set; } Property Value Type Description SslProtocols UpStreamEndPoint Local adapter/NIC endpoint where proxy makes request via. Defaults via any IP addresses of this machine. Declaration public IPEndPoint UpStreamEndPoint { get; set; } Property Value Type Description IPEndPoint UpStreamHttpProxy External proxy used for Http requests. Declaration public ExternalProxy UpStreamHttpProxy { get; set; } Property Value Type Description ExternalProxy UpStreamHttpsProxy External proxy used for Https requests. Declaration public ExternalProxy UpStreamHttpsProxy { get; set; } Property Value Type Description ExternalProxy Methods AddEndPoint(ProxyEndPoint) Add a proxy end point. Declaration public void AddEndPoint(ProxyEndPoint endPoint) Parameters Type Name Description ProxyEndPoint endPoint The proxy endpoint. DisableAllSystemProxies() Clear all proxy settings for current machine. Declaration public void DisableAllSystemProxies() DisableSystemHttpProxy() Clear HTTP proxy settings of current machine. Declaration public void DisableSystemHttpProxy() DisableSystemHttpsProxy() Clear HTTPS proxy settings of current machine. Declaration public void DisableSystemHttpsProxy() DisableSystemProxy(ProxyProtocolType) Clear the specified proxy setting for current machine. Declaration public void DisableSystemProxy(ProxyProtocolType protocolType) Parameters Type Name Description ProxyProtocolType protocolType Dispose() Dispose the Proxy instance. Declaration public void Dispose() RemoveEndPoint(ProxyEndPoint) Remove a proxy end point. Will throw error if the end point does'nt exist. Declaration public void RemoveEndPoint(ProxyEndPoint endPoint) Parameters Type Name Description ProxyEndPoint endPoint The existing endpoint to remove. SetAsSystemHttpProxy(ExplicitProxyEndPoint) Set the given explicit end point as the default proxy server for current machine. Declaration public void SetAsSystemHttpProxy(ExplicitProxyEndPoint endPoint) Parameters Type Name Description ExplicitProxyEndPoint endPoint The explicit endpoint. SetAsSystemHttpsProxy(ExplicitProxyEndPoint) Set the given explicit end point as the default proxy server for current machine. Declaration public void SetAsSystemHttpsProxy(ExplicitProxyEndPoint endPoint) Parameters Type Name Description ExplicitProxyEndPoint endPoint The explicit endpoint. SetAsSystemProxy(ExplicitProxyEndPoint, ProxyProtocolType) Set the given explicit end point as the default proxy server for current machine. Declaration public void SetAsSystemProxy(ExplicitProxyEndPoint endPoint, ProxyProtocolType protocolType) Parameters Type Name Description ExplicitProxyEndPoint endPoint The explicit endpoint. ProxyProtocolType protocolType The proxy protocol type. Start() Start this proxy server instance. Declaration public void Start() Stop() Stop this proxy server instance. Declaration public void Stop() Events AfterResponse Intercept after response event from server. Declaration public event AsyncEventHandler<SessionEventArgs> AfterResponse Event Type Type Description AsyncEventHandler < SessionEventArgs > BeforeRequest Intercept request event to server. Declaration public event AsyncEventHandler<SessionEventArgs> BeforeRequest Event Type Type Description AsyncEventHandler < SessionEventArgs > BeforeResponse Intercept response event from server. Declaration public event AsyncEventHandler<SessionEventArgs> BeforeResponse Event Type Type Description AsyncEventHandler < SessionEventArgs > ClientCertificateSelectionCallback Event to override client certificate selection during mutual SSL authentication. Declaration public event AsyncEventHandler<CertificateSelectionEventArgs> ClientCertificateSelectionCallback Event Type Type Description AsyncEventHandler < CertificateSelectionEventArgs > ClientConnectionCountChanged Event occurs when client connection count changed. Declaration public event EventHandler ClientConnectionCountChanged Event Type Type Description EventHandler OnClientConnectionCreate Customize TcpClient used for client connection upon create. Declaration public event AsyncEventHandler<TcpClient> OnClientConnectionCreate Event Type Type Description AsyncEventHandler < System.Net.Sockets.TcpClient > OnServerConnectionCreate Customize TcpClient used for server connection upon create. Declaration public event AsyncEventHandler<TcpClient> OnServerConnectionCreate Event Type Type Description AsyncEventHandler < System.Net.Sockets.TcpClient > ServerCertificateValidationCallback Event to override the default verification logic of remote SSL certificate received during authentication. Declaration public event AsyncEventHandler<CertificateValidationEventArgs> ServerCertificateValidationCallback Event Type Type Description AsyncEventHandler < CertificateValidationEventArgs > ServerConnectionCountChanged Event occurs when server connection count changed. Declaration public event EventHandler ServerConnectionCountChanged Event Type Type Description EventHandler Implements System.IDisposable"
} }
} }
...@@ -247,12 +247,12 @@ references: ...@@ -247,12 +247,12 @@ references:
commentId: T:Titanium.Web.Proxy.EventArguments.SessionEventArgs commentId: T:Titanium.Web.Proxy.EventArguments.SessionEventArgs
fullName: Titanium.Web.Proxy.EventArguments.SessionEventArgs fullName: Titanium.Web.Proxy.EventArguments.SessionEventArgs
nameWithType: SessionEventArgs nameWithType: SessionEventArgs
- uid: Titanium.Web.Proxy.EventArguments.SessionEventArgs.#ctor(System.Int32,Titanium.Web.Proxy.Models.ProxyEndPoint,Titanium.Web.Proxy.Http.Request,System.Threading.CancellationTokenSource,Titanium.Web.Proxy.ExceptionHandler) - uid: Titanium.Web.Proxy.EventArguments.SessionEventArgs.#ctor(Titanium.Web.Proxy.ProxyServer,Titanium.Web.Proxy.Models.ProxyEndPoint,Titanium.Web.Proxy.Http.Request,System.Threading.CancellationTokenSource)
name: SessionEventArgs(Int32, ProxyEndPoint, Request, CancellationTokenSource, ExceptionHandler) name: SessionEventArgs(ProxyServer, ProxyEndPoint, Request, CancellationTokenSource)
href: api/Titanium.Web.Proxy.EventArguments.SessionEventArgs.html#Titanium_Web_Proxy_EventArguments_SessionEventArgs__ctor_System_Int32_Titanium_Web_Proxy_Models_ProxyEndPoint_Titanium_Web_Proxy_Http_Request_System_Threading_CancellationTokenSource_Titanium_Web_Proxy_ExceptionHandler_ href: api/Titanium.Web.Proxy.EventArguments.SessionEventArgs.html#Titanium_Web_Proxy_EventArguments_SessionEventArgs__ctor_Titanium_Web_Proxy_ProxyServer_Titanium_Web_Proxy_Models_ProxyEndPoint_Titanium_Web_Proxy_Http_Request_System_Threading_CancellationTokenSource_
commentId: M:Titanium.Web.Proxy.EventArguments.SessionEventArgs.#ctor(System.Int32,Titanium.Web.Proxy.Models.ProxyEndPoint,Titanium.Web.Proxy.Http.Request,System.Threading.CancellationTokenSource,Titanium.Web.Proxy.ExceptionHandler) commentId: M:Titanium.Web.Proxy.EventArguments.SessionEventArgs.#ctor(Titanium.Web.Proxy.ProxyServer,Titanium.Web.Proxy.Models.ProxyEndPoint,Titanium.Web.Proxy.Http.Request,System.Threading.CancellationTokenSource)
fullName: Titanium.Web.Proxy.EventArguments.SessionEventArgs.SessionEventArgs(System.Int32, Titanium.Web.Proxy.Models.ProxyEndPoint, Titanium.Web.Proxy.Http.Request, System.Threading.CancellationTokenSource, Titanium.Web.Proxy.ExceptionHandler) fullName: Titanium.Web.Proxy.EventArguments.SessionEventArgs.SessionEventArgs(Titanium.Web.Proxy.ProxyServer, Titanium.Web.Proxy.Models.ProxyEndPoint, Titanium.Web.Proxy.Http.Request, System.Threading.CancellationTokenSource)
nameWithType: SessionEventArgs.SessionEventArgs(Int32, ProxyEndPoint, Request, CancellationTokenSource, ExceptionHandler) nameWithType: SessionEventArgs.SessionEventArgs(ProxyServer, ProxyEndPoint, Request, CancellationTokenSource)
- uid: Titanium.Web.Proxy.EventArguments.SessionEventArgs.#ctor* - uid: Titanium.Web.Proxy.EventArguments.SessionEventArgs.#ctor*
name: SessionEventArgs name: SessionEventArgs
href: api/Titanium.Web.Proxy.EventArguments.SessionEventArgs.html#Titanium_Web_Proxy_EventArguments_SessionEventArgs__ctor_ href: api/Titanium.Web.Proxy.EventArguments.SessionEventArgs.html#Titanium_Web_Proxy_EventArguments_SessionEventArgs__ctor_
...@@ -497,12 +497,12 @@ references: ...@@ -497,12 +497,12 @@ references:
commentId: T:Titanium.Web.Proxy.EventArguments.SessionEventArgsBase commentId: T:Titanium.Web.Proxy.EventArguments.SessionEventArgsBase
fullName: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase fullName: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase
nameWithType: SessionEventArgsBase nameWithType: SessionEventArgsBase
- uid: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.#ctor(System.Int32,Titanium.Web.Proxy.Models.ProxyEndPoint,System.Threading.CancellationTokenSource,Titanium.Web.Proxy.Http.Request,Titanium.Web.Proxy.ExceptionHandler) - uid: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.#ctor(Titanium.Web.Proxy.ProxyServer,Titanium.Web.Proxy.Models.ProxyEndPoint,System.Threading.CancellationTokenSource,Titanium.Web.Proxy.Http.Request)
name: SessionEventArgsBase(Int32, ProxyEndPoint, CancellationTokenSource, Request, ExceptionHandler) name: SessionEventArgsBase(ProxyServer, ProxyEndPoint, CancellationTokenSource, Request)
href: api/Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase__ctor_System_Int32_Titanium_Web_Proxy_Models_ProxyEndPoint_System_Threading_CancellationTokenSource_Titanium_Web_Proxy_Http_Request_Titanium_Web_Proxy_ExceptionHandler_ href: api/Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase__ctor_Titanium_Web_Proxy_ProxyServer_Titanium_Web_Proxy_Models_ProxyEndPoint_System_Threading_CancellationTokenSource_Titanium_Web_Proxy_Http_Request_
commentId: M:Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.#ctor(System.Int32,Titanium.Web.Proxy.Models.ProxyEndPoint,System.Threading.CancellationTokenSource,Titanium.Web.Proxy.Http.Request,Titanium.Web.Proxy.ExceptionHandler) commentId: M:Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.#ctor(Titanium.Web.Proxy.ProxyServer,Titanium.Web.Proxy.Models.ProxyEndPoint,System.Threading.CancellationTokenSource,Titanium.Web.Proxy.Http.Request)
fullName: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.SessionEventArgsBase(System.Int32, Titanium.Web.Proxy.Models.ProxyEndPoint, System.Threading.CancellationTokenSource, Titanium.Web.Proxy.Http.Request, Titanium.Web.Proxy.ExceptionHandler) fullName: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.SessionEventArgsBase(Titanium.Web.Proxy.ProxyServer, Titanium.Web.Proxy.Models.ProxyEndPoint, System.Threading.CancellationTokenSource, Titanium.Web.Proxy.Http.Request)
nameWithType: SessionEventArgsBase.SessionEventArgsBase(Int32, ProxyEndPoint, CancellationTokenSource, Request, ExceptionHandler) nameWithType: SessionEventArgsBase.SessionEventArgsBase(ProxyServer, ProxyEndPoint, CancellationTokenSource, Request)
- uid: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.#ctor* - uid: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.#ctor*
name: SessionEventArgsBase name: SessionEventArgsBase
href: api/Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase__ctor_ href: api/Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase__ctor_
...@@ -510,12 +510,18 @@ references: ...@@ -510,12 +510,18 @@ references:
isSpec: "True" isSpec: "True"
fullName: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.SessionEventArgsBase fullName: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.SessionEventArgsBase
nameWithType: SessionEventArgsBase.SessionEventArgsBase nameWithType: SessionEventArgsBase.SessionEventArgsBase
- uid: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.BufferSize - uid: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.bufferPool
name: BufferSize name: bufferPool
href: api/Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_BufferSize href: api/Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_bufferPool
commentId: F:Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.BufferSize commentId: F:Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.bufferPool
fullName: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.BufferSize fullName: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.bufferPool
nameWithType: SessionEventArgsBase.BufferSize nameWithType: SessionEventArgsBase.bufferPool
- uid: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.bufferSize
name: bufferSize
href: api/Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_bufferSize
commentId: F:Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.bufferSize
fullName: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.bufferSize
nameWithType: SessionEventArgsBase.bufferSize
- uid: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.ClientEndPoint - uid: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.ClientEndPoint
name: ClientEndPoint name: ClientEndPoint
href: api/Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_ClientEndPoint href: api/Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_ClientEndPoint
...@@ -580,12 +586,12 @@ references: ...@@ -580,12 +586,12 @@ references:
isSpec: "True" isSpec: "True"
fullName: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.Exception fullName: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.Exception
nameWithType: SessionEventArgsBase.Exception nameWithType: SessionEventArgsBase.Exception
- uid: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.ExceptionFunc - uid: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.exceptionFunc
name: ExceptionFunc name: exceptionFunc
href: api/Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_ExceptionFunc href: api/Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_exceptionFunc
commentId: F:Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.ExceptionFunc commentId: F:Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.exceptionFunc
fullName: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.ExceptionFunc fullName: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.exceptionFunc
nameWithType: SessionEventArgsBase.ExceptionFunc nameWithType: SessionEventArgsBase.exceptionFunc
- uid: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.IsHttps - uid: Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.IsHttps
name: IsHttps name: IsHttps
href: api/Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_IsHttps href: api/Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_IsHttps
...@@ -2596,6 +2602,19 @@ references: ...@@ -2596,6 +2602,19 @@ references:
commentId: E:Titanium.Web.Proxy.ProxyServer.BeforeResponse commentId: E:Titanium.Web.Proxy.ProxyServer.BeforeResponse
fullName: Titanium.Web.Proxy.ProxyServer.BeforeResponse fullName: Titanium.Web.Proxy.ProxyServer.BeforeResponse
nameWithType: ProxyServer.BeforeResponse nameWithType: ProxyServer.BeforeResponse
- uid: Titanium.Web.Proxy.ProxyServer.BufferPool
name: BufferPool
href: api/Titanium.Web.Proxy.ProxyServer.html#Titanium_Web_Proxy_ProxyServer_BufferPool
commentId: P:Titanium.Web.Proxy.ProxyServer.BufferPool
fullName: Titanium.Web.Proxy.ProxyServer.BufferPool
nameWithType: ProxyServer.BufferPool
- uid: Titanium.Web.Proxy.ProxyServer.BufferPool*
name: BufferPool
href: api/Titanium.Web.Proxy.ProxyServer.html#Titanium_Web_Proxy_ProxyServer_BufferPool_
commentId: Overload:Titanium.Web.Proxy.ProxyServer.BufferPool
isSpec: "True"
fullName: Titanium.Web.Proxy.ProxyServer.BufferPool
nameWithType: ProxyServer.BufferPool
- uid: Titanium.Web.Proxy.ProxyServer.BufferSize - uid: Titanium.Web.Proxy.ProxyServer.BufferSize
name: BufferSize name: BufferSize
href: api/Titanium.Web.Proxy.ProxyServer.html#Titanium_Web_Proxy_ProxyServer_BufferSize href: api/Titanium.Web.Proxy.ProxyServer.html#Titanium_Web_Proxy_ProxyServer_BufferSize
...@@ -2751,6 +2770,19 @@ references: ...@@ -2751,6 +2770,19 @@ references:
isSpec: "True" isSpec: "True"
fullName: Titanium.Web.Proxy.ProxyServer.Enable100ContinueBehaviour fullName: Titanium.Web.Proxy.ProxyServer.Enable100ContinueBehaviour
nameWithType: ProxyServer.Enable100ContinueBehaviour nameWithType: ProxyServer.Enable100ContinueBehaviour
- uid: Titanium.Web.Proxy.ProxyServer.EnableConnectionPool
name: EnableConnectionPool
href: api/Titanium.Web.Proxy.ProxyServer.html#Titanium_Web_Proxy_ProxyServer_EnableConnectionPool
commentId: P:Titanium.Web.Proxy.ProxyServer.EnableConnectionPool
fullName: Titanium.Web.Proxy.ProxyServer.EnableConnectionPool
nameWithType: ProxyServer.EnableConnectionPool
- uid: Titanium.Web.Proxy.ProxyServer.EnableConnectionPool*
name: EnableConnectionPool
href: api/Titanium.Web.Proxy.ProxyServer.html#Titanium_Web_Proxy_ProxyServer_EnableConnectionPool_
commentId: Overload:Titanium.Web.Proxy.ProxyServer.EnableConnectionPool
isSpec: "True"
fullName: Titanium.Web.Proxy.ProxyServer.EnableConnectionPool
nameWithType: ProxyServer.EnableConnectionPool
- uid: Titanium.Web.Proxy.ProxyServer.EnableWinAuth - uid: Titanium.Web.Proxy.ProxyServer.EnableWinAuth
name: EnableWinAuth name: EnableWinAuth
href: api/Titanium.Web.Proxy.ProxyServer.html#Titanium_Web_Proxy_ProxyServer_EnableWinAuth href: api/Titanium.Web.Proxy.ProxyServer.html#Titanium_Web_Proxy_ProxyServer_EnableWinAuth
...@@ -2803,6 +2835,31 @@ references: ...@@ -2803,6 +2835,31 @@ references:
isSpec: "True" isSpec: "True"
fullName: Titanium.Web.Proxy.ProxyServer.GetCustomUpStreamProxyFunc fullName: Titanium.Web.Proxy.ProxyServer.GetCustomUpStreamProxyFunc
nameWithType: ProxyServer.GetCustomUpStreamProxyFunc nameWithType: ProxyServer.GetCustomUpStreamProxyFunc
- uid: Titanium.Web.Proxy.ProxyServer.MaxCachedConnections
name: MaxCachedConnections
href: api/Titanium.Web.Proxy.ProxyServer.html#Titanium_Web_Proxy_ProxyServer_MaxCachedConnections
commentId: P:Titanium.Web.Proxy.ProxyServer.MaxCachedConnections
fullName: Titanium.Web.Proxy.ProxyServer.MaxCachedConnections
nameWithType: ProxyServer.MaxCachedConnections
- uid: Titanium.Web.Proxy.ProxyServer.MaxCachedConnections*
name: MaxCachedConnections
href: api/Titanium.Web.Proxy.ProxyServer.html#Titanium_Web_Proxy_ProxyServer_MaxCachedConnections_
commentId: Overload:Titanium.Web.Proxy.ProxyServer.MaxCachedConnections
isSpec: "True"
fullName: Titanium.Web.Proxy.ProxyServer.MaxCachedConnections
nameWithType: ProxyServer.MaxCachedConnections
- uid: Titanium.Web.Proxy.ProxyServer.OnClientConnectionCreate
name: OnClientConnectionCreate
href: api/Titanium.Web.Proxy.ProxyServer.html#Titanium_Web_Proxy_ProxyServer_OnClientConnectionCreate
commentId: E:Titanium.Web.Proxy.ProxyServer.OnClientConnectionCreate
fullName: Titanium.Web.Proxy.ProxyServer.OnClientConnectionCreate
nameWithType: ProxyServer.OnClientConnectionCreate
- uid: Titanium.Web.Proxy.ProxyServer.OnServerConnectionCreate
name: OnServerConnectionCreate
href: api/Titanium.Web.Proxy.ProxyServer.html#Titanium_Web_Proxy_ProxyServer_OnServerConnectionCreate
commentId: E:Titanium.Web.Proxy.ProxyServer.OnServerConnectionCreate
fullName: Titanium.Web.Proxy.ProxyServer.OnServerConnectionCreate
nameWithType: ProxyServer.OnServerConnectionCreate
- uid: Titanium.Web.Proxy.ProxyServer.ProxyEndPoints - uid: Titanium.Web.Proxy.ProxyServer.ProxyEndPoints
name: ProxyEndPoints name: ProxyEndPoints
href: api/Titanium.Web.Proxy.ProxyServer.html#Titanium_Web_Proxy_ProxyServer_ProxyEndPoints href: api/Titanium.Web.Proxy.ProxyServer.html#Titanium_Web_Proxy_ProxyServer_ProxyEndPoints
......
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