Unverified Commit 35cd23b6 authored by justcoding121's avatar justcoding121 Committed by GitHub

Merge pull request #430 from justcoding121/beta

Stable 
parents 741d1d8e d3ad2f8b
......@@ -205,4 +205,4 @@ FakesAssemblies/
*.opt
# Docfx
docs/manifest.json
\ No newline at end of file
docs/manifest.json
......@@ -9,10 +9,10 @@ namespace Titanium.Web.Proxy.Examples.Basic
public static void Main(string[] args)
{
//fix console hang due to QuickEdit mode
// fix console hang due to QuickEdit mode
ConsoleHelper.DisableQuickEditMode();
//Start proxy controller
// Start proxy controller
controller.StartProxy();
Console.WriteLine("Hit any key to exit..");
......
......@@ -9,7 +9,6 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Models;
......
......@@ -51,8 +51,8 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="StreamExtended, Version=1.0.147.0, Culture=neutral, PublicKeyToken=bbfa0f1d54f50043, processorArchitecture=MSIL">
<HintPath>..\..\packages\StreamExtended.1.0.147-beta\lib\net45\StreamExtended.dll</HintPath>
<Reference Include="StreamExtended, Version=1.0.164.0, Culture=neutral, PublicKeyToken=bbfa0f1d54f50043, processorArchitecture=MSIL">
<HintPath>..\..\packages\StreamExtended.1.0.164\lib\net45\StreamExtended.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Data" />
......
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="StreamExtended" version="1.0.147-beta" targetFramework="net45" />
<package id="StreamExtended" version="1.0.164" targetFramework="net45" />
</packages>
\ No newline at end of file
......@@ -121,10 +121,6 @@ Sample request and response event handlers
```csharp
//To access requestBody from OnResponse handler
private IDictionary<Guid, string> requestBodyHistory
= new ConcurrentDictionary<Guid, string>();
private async Task OnBeforeTunnelConnectRequest(object sender, TunnelConnectSessionEventArgs e)
{
string hostname = e.WebSession.Request.RequestUri.Host;
......@@ -156,9 +152,9 @@ public async Task OnRequest(object sender, SessionEventArgs e)
string bodyString = await e.GetRequestBodyAsString();
await e.SetRequestBodyString(bodyString);
//store request Body/request headers etc with request Id as key
//so that you can find it from response handler using request Id
requestBodyHistory[e.Id] = bodyString;
//store request
//so that you can find it from response handler
e.UserData = e.WebSession.Request;
}
//To cancel a request with a custom HTML content
......@@ -202,11 +198,12 @@ public async Task OnResponse(object sender, SessionEventArgs e)
}
}
//access request body/request headers etc by looking up using requestId
if(requestBodyHistory.ContainsKey(e.Id))
if(e.UserData!=null)
{
var requestBody = requestBodyHistory[e.Id];
//access request from UserData property where we stored it in RequestHandler
var request = (Request)e.UserData;
}
}
/// Allows overriding default certificate validation logic
......
......@@ -17,8 +17,8 @@ namespace Titanium.Web.Proxy.IntegrationTests
//disable this test until CI is prepared to handle
public void TestSsl()
{
//expand this to stress test to find
//why in long run proxy becomes unresponsive as per issue #184
// expand this to stress test to find
// why in long run proxy becomes unresponsive as per issue #184
string testUrl = "https://google.com";
int proxyPort = 8086;
var proxy = new ProxyTestController();
......@@ -62,8 +62,8 @@ namespace Titanium.Web.Proxy.IntegrationTests
var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, proxyPort, true);
//An explicit endpoint is where the client knows about the existance of a proxy
//So client sends request in a proxy friendly manner
// An explicit endpoint is where the client knows about the existance of a proxy
// So client sends request in a proxy friendly manner
proxyServer.AddEndPoint(explicitEndPoint);
proxyServer.Start();
......@@ -84,14 +84,14 @@ namespace Titanium.Web.Proxy.IntegrationTests
proxyServer.Stop();
}
//intecept & cancel, redirect or update requests
// intecept & cancel, redirect or update requests
public async Task OnRequest(object sender, SessionEventArgs e)
{
Debug.WriteLine(e.WebSession.Request.Url);
await Task.FromResult(0);
}
//Modify response
// Modify response
public async Task OnResponse(object sender, SessionEventArgs e)
{
await Task.FromResult(0);
......@@ -104,7 +104,7 @@ namespace Titanium.Web.Proxy.IntegrationTests
/// <param name="e"></param>
public Task OnCertificateValidation(object sender, CertificateValidationEventArgs e)
{
//set IsValid to true/false based on Certificate Errors
// set IsValid to true/false based on Certificate Errors
if (e.SslPolicyErrors == SslPolicyErrors.None)
{
e.IsValid = true;
......@@ -120,7 +120,7 @@ namespace Titanium.Web.Proxy.IntegrationTests
/// <param name="e"></param>
public Task OnCertificateSelection(object sender, CertificateSelectionEventArgs e)
{
//set e.clientCertificate to override
// set e.clientCertificate to override
return Task.FromResult(0);
}
......
......@@ -33,7 +33,7 @@ namespace Titanium.Web.Proxy.UnitTests
{
tasks.AddRange(hostNames.Select(host => Task.Run(() =>
{
//get the connection
// get the connection
var certificate = mgr.CreateCertificate(host, false);
Assert.IsNotNull(certificate);
})));
......@@ -44,7 +44,7 @@ namespace Titanium.Web.Proxy.UnitTests
mgr.StopClearIdleCertificates();
}
//uncomment this to compare WinCert maker performance with BC (BC takes more time for same test above)
// uncomment this to compare WinCert maker performance with BC (BC takes more time for same test above)
[TestMethod]
public async Task Simple_Create_Win_Certificate_Test()
{
......@@ -66,7 +66,7 @@ namespace Titanium.Web.Proxy.UnitTests
{
tasks.AddRange(hostNames.Select(host => Task.Run(() =>
{
//get the connection
// get the connection
var certificate = mgr.CreateCertificate(host, false);
Assert.IsNotNull(certificate);
})));
......
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Network.WinAuth;
namespace Titanium.Web.Proxy.UnitTests
......@@ -10,7 +11,7 @@ namespace Titanium.Web.Proxy.UnitTests
[TestMethod]
public void Test_Acquire_Client_Token()
{
string token = WinAuthHandler.GetInitialAuthToken("mylocalserver.com", "NTLM", Guid.NewGuid());
string token = WinAuthHandler.GetInitialAuthToken("mylocalserver.com", "NTLM", new InternalDataStore());
Assert.IsTrue(token.Length > 1);
}
}
......
......@@ -19,7 +19,7 @@ namespace Titanium.Web.Proxy
internal bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
//if user callback is registered then do it
// if user callback is registered then do it
if (ServerCertificateValidationCallback != null)
{
var args = new CertificateValidationEventArgs
......@@ -29,7 +29,7 @@ namespace Titanium.Web.Proxy
SslPolicyErrors = sslPolicyErrors
};
//why is the sender null?
// why is the sender null?
ServerCertificateValidationCallback.InvokeAsync(this, args, exceptionFunc).Wait();
return args.IsValid;
}
......@@ -39,8 +39,8 @@ namespace Titanium.Web.Proxy
return true;
}
//By default
//do not allow this client to communicate with unauthenticated servers.
// By default
// do not allow this client to communicate with unauthenticated servers.
return false;
}
......@@ -77,7 +77,7 @@ namespace Titanium.Web.Proxy
clientCertificate = localCertificates[0];
}
//If user call back is registered
// If user call back is registered
if (ClientCertificateSelectionCallback != null)
{
var args = new CertificateSelectionEventArgs
......@@ -89,7 +89,7 @@ namespace Titanium.Web.Proxy
ClientCertificate = clientCertificate
};
//why is the sender null?
// why is the sender null?
ClientCertificateSelectionCallback.InvokeAsync(this, args, exceptionFunc).Wait();
return args.ClientCertificate;
}
......
using System;
using System.IO;
using System.IO.Compression;
using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.Compression
......@@ -8,18 +10,14 @@ namespace Titanium.Web.Proxy.Compression
/// </summary>
internal static class CompressionFactory
{
//cache
private static readonly ICompression gzip = new GZipCompression();
private static readonly ICompression deflate = new DeflateCompression();
internal static ICompression GetCompression(string type)
internal static Stream Create(string type, Stream stream, bool leaveOpen = true)
{
switch (type)
{
case KnownHeaders.ContentEncodingGzip:
return gzip;
return new GZipStream(stream, CompressionMode.Compress, leaveOpen);
case KnownHeaders.ContentEncodingDeflate:
return deflate;
return new DeflateStream(stream, CompressionMode.Compress, leaveOpen);
default:
throw new Exception($"Unsupported compression mode: {type}");
}
......
using System;
using System.IO;
using System.IO.Compression;
using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.Decompression
namespace Titanium.Web.Proxy.Compression
{
/// <summary>
/// A factory to generate the de-compression methods based on the type of compression
/// </summary>
internal class DecompressionFactory
{
//cache
private static readonly IDecompression gzip = new GZipDecompression();
private static readonly IDecompression deflate = new DeflateDecompression();
internal static IDecompression Create(string type)
internal static Stream Create(string type, Stream stream, bool leaveOpen = true)
{
switch (type)
{
case KnownHeaders.ContentEncodingGzip:
return gzip;
return new GZipStream(stream, CompressionMode.Decompress, leaveOpen);
case KnownHeaders.ContentEncodingDeflate:
return deflate;
return new DeflateStream(stream, CompressionMode.Decompress, leaveOpen);
default:
throw new Exception($"Unsupported decompression mode: {type}");
}
......
using System.IO;
using System.IO.Compression;
namespace Titanium.Web.Proxy.Compression
{
/// <summary>
/// Concrete implementation of deflate compression
/// </summary>
internal class DeflateCompression : ICompression
{
public Stream GetStream(Stream stream)
{
return new DeflateStream(stream, CompressionMode.Compress, true);
}
}
}
using System.IO;
using System.IO.Compression;
namespace Titanium.Web.Proxy.Compression
{
/// <summary>
/// concreate implementation of gzip compression
/// </summary>
internal class GZipCompression : ICompression
{
public Stream GetStream(Stream stream)
{
return new GZipStream(stream, CompressionMode.Compress, true);
}
}
}
using System.IO;
namespace Titanium.Web.Proxy.Compression
{
/// <summary>
/// An inteface for http compression
/// </summary>
internal interface ICompression
{
Stream GetStream(Stream stream);
}
}
using System.IO;
using System.IO.Compression;
namespace Titanium.Web.Proxy.Decompression
{
/// <summary>
/// concrete implementation of deflate de-compression
/// </summary>
internal class DeflateDecompression : IDecompression
{
public Stream GetStream(Stream stream)
{
return new DeflateStream(stream, CompressionMode.Decompress, true);
}
}
}
using System.IO;
using System.IO.Compression;
namespace Titanium.Web.Proxy.Decompression
{
/// <summary>
/// concrete implementation of gzip de-compression
/// </summary>
internal class GZipDecompression : IDecompression
{
public Stream GetStream(Stream stream)
{
return new GZipStream(stream, CompressionMode.Decompress, true);
}
}
}
using System.IO;
namespace Titanium.Web.Proxy.Decompression
{
/// <summary>
/// An interface for decompression
/// </summary>
internal interface IDecompression
{
Stream GetStream(Stream stream);
}
}
using System;
namespace Titanium.Web.Proxy.EventArguments
{
/// <summary>
/// Wraps the data sent/received by a proxy server instance.
/// </summary>
public class DataEventArgs : EventArgs
{
internal DataEventArgs(byte[] buffer, int offset, int count)
{
Buffer = buffer;
Offset = offset;
Count = count;
}
/// <summary>
/// The buffer with data.
/// </summary>
public byte[] Buffer { get; }
/// <summary>
/// Offset in buffer from which valid data begins.
/// </summary>
public int Offset { get; }
/// <summary>
/// Length from offset in buffer with valid data.
/// </summary>
public int Count { get; }
}
}
......@@ -9,18 +9,16 @@ namespace Titanium.Web.Proxy.EventArguments
{
internal class LimitedStream : Stream
{
private readonly CustomBinaryReader baseReader;
private readonly CustomBufferedStream baseStream;
private readonly ICustomStreamReader baseStream;
private readonly bool isChunked;
private long bytesRemaining;
private bool readChunkTrail;
internal LimitedStream(CustomBufferedStream baseStream, CustomBinaryReader baseReader, bool isChunked,
internal LimitedStream(ICustomStreamReader baseStream, bool isChunked,
long contentLength)
{
this.baseStream = baseStream;
this.baseReader = baseReader;
this.isChunked = isChunked;
bytesRemaining = isChunked
? 0
......@@ -48,12 +46,12 @@ namespace Titanium.Web.Proxy.EventArguments
if (readChunkTrail)
{
// read the chunk trail of the previous chunk
string s = baseReader.ReadLineAsync().Result;
string s = baseStream.ReadLineAsync().Result;
}
readChunkTrail = true;
string chunkHead = baseReader.ReadLineAsync().Result;
string chunkHead = baseStream.ReadLineAsync().Result;
int idx = chunkHead.IndexOf(";", StringComparison.Ordinal);
if (idx >= 0)
{
......@@ -67,8 +65,8 @@ namespace Titanium.Web.Proxy.EventArguments
{
bytesRemaining = -1;
//chunk trail
baseReader.ReadLineAsync().Wait();
// chunk trail
baseStream.ReadLineAsync().Wait();
}
}
......@@ -127,7 +125,7 @@ namespace Titanium.Web.Proxy.EventArguments
{
if (bytesRemaining != -1)
{
var buffer = BufferPool.GetBuffer(baseReader.Buffer.Length);
var buffer = BufferPool.GetBuffer(baseStream.BufferSize);
try
{
int res = await ReadAsync(buffer, 0, buffer.Length);
......
......@@ -6,11 +6,12 @@ using System.Threading;
using System.Threading.Tasks;
using StreamExtended.Helpers;
using StreamExtended.Network;
using Titanium.Web.Proxy.Decompression;
using Titanium.Web.Proxy.Compression;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Http.Responses;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network;
namespace Titanium.Web.Proxy.EventArguments
{
......@@ -68,16 +69,11 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary>
public event EventHandler<MultipartRequestPartSentEventArgs> MultipartRequestPartSent;
private CustomBufferedStream GetStream(bool isRequest)
private ICustomStreamReader GetStreamReader(bool isRequest)
{
return isRequest ? ProxyClient.ClientStream : WebSession.ServerConnection.Stream;
}
private CustomBinaryReader GetStreamReader(bool isRequest)
{
return isRequest ? ProxyClient.ClientStreamReader : WebSession.ServerConnection.StreamReader;
}
private HttpWriter GetStreamWriter(bool isRequest)
{
return isRequest ? (HttpWriter)ProxyClient.ClientStreamWriter : WebSession.ServerConnection.StreamWriter;
......@@ -92,14 +88,14 @@ namespace Titanium.Web.Proxy.EventArguments
var request = WebSession.Request;
//If not already read (not cached yet)
// If not already read (not cached yet)
if (!request.IsBodyRead)
{
var body = await ReadBodyAsync(true, cancellationToken);
request.Body = body;
//Now set the flag to true
//So that next time we can deliver body from cache
// Now set the flag to true
// So that next time we can deliver body from cache
request.IsBodyRead = true;
OnDataSent(body, 0, body.Length);
}
......@@ -110,7 +106,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary>
internal async Task ClearResponse(CancellationToken cancellationToken)
{
//syphon out the response body from server
// syphon out the response body from server
await SyphonOutBodyAsync(false, cancellationToken);
WebSession.Response = new Response();
}
......@@ -143,14 +139,14 @@ namespace Titanium.Web.Proxy.EventArguments
return;
}
//If not already read (not cached yet)
// If not already read (not cached yet)
if (!response.IsBodyRead)
{
var body = await ReadBodyAsync(false, cancellationToken);
response.Body = body;
//Now set the flag to true
//So that next time we can deliver body from cache
// Now set the flag to true
// So that next time we can deliver body from cache
response.IsBodyRead = true;
OnDataReceived(body, 0, body.Length);
}
......@@ -200,18 +196,17 @@ namespace Titanium.Web.Proxy.EventArguments
long contentLength = request.ContentLength;
//send the request body bytes to server
// send the request body bytes to server
if (contentLength > 0 && hasMulipartEventSubscribers && request.IsMultipartFormData)
{
var reader = GetStreamReader(true);
string boundary = HttpHelper.GetBoundaryFromContentType(request.ContentType);
using (var copyStream = new CopyStream(reader, writer, BufferSize))
using (var copyStreamReader = new CustomBinaryReader(copyStream, BufferSize))
{
while (contentLength > copyStream.ReadBytes)
{
long read = await ReadUntilBoundaryAsync(copyStreamReader, contentLength, boundary, cancellationToken);
long read = await ReadUntilBoundaryAsync(copyStream, contentLength, boundary, cancellationToken);
if (read == 0)
{
break;
......@@ -220,7 +215,7 @@ namespace Titanium.Web.Proxy.EventArguments
if (contentLength > copyStream.ReadBytes)
{
var headers = new HeaderCollection();
await HeaderParser.ReadHeaders(copyStreamReader, headers, cancellationToken);
await HeaderParser.ReadHeaders(copyStream, headers, cancellationToken);
OnMultipartRequestPartSent(boundary, headers);
}
}
......@@ -241,8 +236,7 @@ namespace Titanium.Web.Proxy.EventArguments
private async Task CopyBodyAsync(bool isRequest, HttpWriter writer, TransformationMode transformation, Action<byte[], int, int> onCopy, CancellationToken cancellationToken)
{
var stream = GetStream(isRequest);
var reader = GetStreamReader(isRequest);
var stream = GetStreamReader(isRequest);
var requestResponse = isRequest ? (RequestResponseBase)WebSession.Request : WebSession.Response;
......@@ -250,7 +244,7 @@ namespace Titanium.Web.Proxy.EventArguments
long contentLength = requestResponse.ContentLength;
if (transformation == TransformationMode.None)
{
await writer.CopyBodyAsync(reader, isChunked, contentLength, onCopy, cancellationToken);
await writer.CopyBodyAsync(stream, isChunked, contentLength, onCopy, cancellationToken);
return;
}
......@@ -259,23 +253,22 @@ namespace Titanium.Web.Proxy.EventArguments
string contentEncoding = requestResponse.ContentEncoding;
Stream s = limitedStream = new LimitedStream(stream, reader, isChunked, contentLength);
Stream s = limitedStream = new LimitedStream(stream, isChunked, contentLength);
if (transformation == TransformationMode.Uncompress && contentEncoding != null)
{
s = decompressStream = DecompressionFactory.Create(contentEncoding).GetStream(s);
s = decompressStream = DecompressionFactory.Create(contentEncoding, s);
}
try
{
var bufStream = new CustomBufferedStream(s, BufferSize, true);
reader = new CustomBinaryReader(bufStream, BufferSize);
await writer.CopyBodyAsync(reader, false, -1, onCopy, cancellationToken);
using (var bufStream = new CustomBufferedStream(s, BufferSize, true))
{
await writer.CopyBodyAsync(bufStream, false, -1, onCopy, cancellationToken);
}
}
finally
{
reader?.Dispose();
decompressStream?.Dispose();
await limitedStream.Finish();
......@@ -287,7 +280,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// Read a line from the byte stream
/// </summary>
/// <returns></returns>
private async Task<long> ReadUntilBoundaryAsync(CustomBinaryReader reader, long totalBytesToRead, string boundary, CancellationToken cancellationToken)
private async Task<long> ReadUntilBoundaryAsync(ICustomStreamReader reader, long totalBytesToRead, string boundary, CancellationToken cancellationToken)
{
int bufferDataLength = 0;
......@@ -330,7 +323,7 @@ namespace Titanium.Web.Proxy.EventArguments
if (bufferDataLength == buffer.Length)
{
//boundary is not longer than 70 bytes according to the specification, so keeping the last 100 (minimum 74) bytes is enough
// boundary is not longer than 70 bytes according to the specification, so keeping the last 100 (minimum 74) bytes is enough
const int bytesToKeep = 100;
Buffer.BlockCopy(buffer, buffer.Length - bytesToKeep, buffer, 0, bytesToKeep);
bufferDataLength = bytesToKeep;
......
using System;
using System.Net;
using System.Threading;
using StreamExtended.Network;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Models;
......@@ -26,7 +27,7 @@ namespace Titanium.Web.Proxy.EventArguments
protected readonly ExceptionHandler ExceptionFunc;
/// <summary>
/// Constructor to initialize the proxy
/// Initializes a new instance of the <see cref="SessionEventArgsBase" /> class.
/// </summary>
internal SessionEventArgsBase(int bufferSize, ProxyEndPoint endPoint,
CancellationTokenSource cancellationTokenSource, ExceptionHandler exceptionFunc)
......@@ -50,16 +51,16 @@ namespace Titanium.Web.Proxy.EventArguments
{
if (RunTime.IsWindows)
{
var remoteEndPoint = (IPEndPoint)ProxyClient.TcpClient.Client.RemoteEndPoint;
var remoteEndPoint = ClientEndPoint;
//If client is localhost get the process id
// If client is localhost get the process id
if (NetworkHelper.IsLocalIpAddress(remoteEndPoint.Address))
{
var ipVersion = endPoint.IpV6Enabled ? IpVersion.Ipv6 : IpVersion.Ipv4;
return TcpHelper.GetProcessIdByLocalPort(ipVersion, remoteEndPoint.Port);
}
//can't access process Id of remote request from remote machine
// can't access process Id of remote request from remote machine
return -1;
}
......@@ -73,10 +74,14 @@ namespace Titanium.Web.Proxy.EventArguments
internal ProxyClient ProxyClient { get; }
/// <summary>
/// Returns a unique Id for this request/response session which is
/// same as the RequestId of WebSession.
/// Returns a user data for this request/response session which is
/// same as the user data of WebSession.
/// </summary>
public Guid Id => WebSession.RequestId;
public object UserData
{
get => WebSession.UserData;
set => WebSession.UserData = value;
}
/// <summary>
/// Does this session uses SSL?
......@@ -86,7 +91,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary>
/// Client End Point.
/// </summary>
public IPEndPoint ClientEndPoint => (IPEndPoint)ProxyClient.TcpClient.Client.RemoteEndPoint;
public IPEndPoint ClientEndPoint => (IPEndPoint)ProxyClient.ClientConnection.RemoteEndPoint;
/// <summary>
/// A web session corresponding to a single request/response sequence
......
......@@ -37,6 +37,7 @@ namespace Titanium.Web.Proxy.EventArguments
{
get => isHttpsConnect ??
throw new Exception("The value of this property is known in the BeforeTunnectConnectResponse event");
internal set => isHttpsConnect = value;
}
}
......
......@@ -11,7 +11,7 @@ namespace Titanium.Web.Proxy.Exceptions
public class ProxyAuthorizationException : ProxyException
{
/// <summary>
/// Instantiate new instance.
/// Initializes a new instance of the <see cref="ProxyAuthorizationException" /> class.
/// </summary>
/// <param name="message">Exception message.</param>
/// <param name="session">The <see cref="SessionEventArgs" /> instance containing the event data.</param>
......
using System;
using Titanium.Web.Proxy.EventArguments;
namespace Titanium.Web.Proxy.Exceptions
{
/// <summary>
/// Proxy Connection exception.
/// </summary>
public class ProxyConnectException : ProxyException
{
/// <summary>
/// Initializes a new instance of the <see cref="ProxyConnectException" /> class.
/// </summary>
/// <param name="message">Message for this exception</param>
/// <param name="innerException">Associated inner exception</param>
/// <param name="connectEventArgs">Instance of <see cref="EventArguments.TunnelConnectSessionEventArgs" /> associated to the exception</param>
internal ProxyConnectException(string message, Exception innerException, TunnelConnectSessionEventArgs connectEventArgs) : base(
message, innerException)
{
ConnectEventArgs = connectEventArgs;
}
/// <summary>
/// Gets session info associated to the exception.
/// </summary>
/// <remarks>
/// This object properties should not be edited.
/// </remarks>
public TunnelConnectSessionEventArgs ConnectEventArgs { get; }
}
}
......@@ -8,7 +8,8 @@ namespace Titanium.Web.Proxy.Exceptions
public abstract class ProxyException : Exception
{
/// <summary>
/// Instantiate a new instance of this exception - must be invoked by derived classes' constructors
/// Initializes a new instance of the <see cref="ProxyException" /> class.
/// - must be invoked by derived classes' constructors
/// </summary>
/// <param name="message">Exception message</param>
protected ProxyException(string message) : base(message)
......@@ -16,7 +17,8 @@ namespace Titanium.Web.Proxy.Exceptions
}
/// <summary>
/// Instantiate this exception - must be invoked by derived classes' constructors
/// Initializes a new instance of the <see cref="ProxyException" /> class.
/// - must be invoked by derived classes' constructors
/// </summary>
/// <param name="message">Excception message</param>
/// <param name="innerException">Inner exception associated</param>
......
......@@ -9,7 +9,7 @@ namespace Titanium.Web.Proxy.Exceptions
public class ProxyHttpException : ProxyException
{
/// <summary>
/// Instantiate new instance
/// Initializes a new instance of the <see cref="ProxyHttpException" /> class.
/// </summary>
/// <param name="message">Message for this exception</param>
/// <param name="innerException">Associated inner exception</param>
......
This diff is collapsed.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
......@@ -13,6 +14,9 @@ namespace Titanium.Web.Proxy.Extensions
internal static readonly List<SslApplicationProtocol> Http11ProtocolAsList =
new List<SslApplicationProtocol> { SslApplicationProtocol.Http11 };
internal static readonly List<SslApplicationProtocol> Http2ProtocolAsList =
new List<SslApplicationProtocol> { SslApplicationProtocol.Http2 };
internal static string GetServerName(this ClientHelloInfo clientHelloInfo)
{
if (clientHelloInfo.Extensions != null &&
......@@ -81,6 +85,7 @@ namespace Titanium.Web.Proxy.Extensions
Http2
}
[SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleType", Justification = "Reviewed.")]
internal class SslClientAuthenticationOptions
{
internal bool AllowRenegotiation { get; set; }
......
......@@ -31,10 +31,10 @@ namespace Titanium.Web.Proxy.Extensions
try
{
//This line is important!
//contributors please don't remove it without discussion
//It helps to avoid eventual deterioration of performance due to TCP port exhaustion
//due to default TCP CLOSE_WAIT timeout for 4 minutes
// This line is important!
// contributors please don't remove it without discussion
// It helps to avoid eventual deterioration of performance due to TCP port exhaustion
// due to default TCP CLOSE_WAIT timeout for 4 minutes
if (socketCleanedUpGetter == null || !socketCleanedUpGetter(tcpClient.Client))
{
tcpClient.LingerState = new LingerOption(true, 0);
......
......@@ -21,13 +21,13 @@ namespace Titanium.Web.Proxy.Helpers
{
try
{
//return default if not specified
// return default if not specified
if (contentType == null)
{
return defaultEncoding;
}
//extract the encoding by finding the charset
// extract the encoding by finding the charset
var parameters = contentType.Split(ProxyConstants.SemiColonSplit);
foreach (string parameter in parameters)
{
......@@ -35,7 +35,7 @@ namespace Titanium.Web.Proxy.Helpers
if (split.Length == 2 && split[0].Trim().EqualsIgnoreCase(KnownHeaders.ContentTypeCharset))
{
string value = split[1];
if (value.Equals("x-user-defined", StringComparison.OrdinalIgnoreCase))
if (value.EqualsIgnoreCase("x-user-defined"))
{
continue;
}
......@@ -51,11 +51,11 @@ namespace Titanium.Web.Proxy.Helpers
}
catch
{
//parsing errors
// parsing errors
// ignored
}
//return default if not specified
// return default if not specified
return defaultEncoding;
}
......@@ -63,7 +63,7 @@ namespace Titanium.Web.Proxy.Helpers
{
if (contentType != null)
{
//extract the boundary
// extract the boundary
var parameters = contentType.Split(ProxyConstants.SemiColonSplit);
foreach (string parameter in parameters)
{
......@@ -81,7 +81,7 @@ namespace Titanium.Web.Proxy.Helpers
}
}
//return null if not specified
// return null if not specified
return null;
}
......@@ -94,14 +94,14 @@ namespace Titanium.Web.Proxy.Helpers
/// <returns></returns>
internal static string GetWildCardDomainName(string hostname)
{
//only for subdomains we need wild card
//example www.google.com or gstatic.google.com
//but NOT for google.com
// only for subdomains we need wild card
// example www.google.com or gstatic.google.com
// but NOT for google.com
if (hostname.Split(ProxyConstants.DotSplit).Length > 2)
{
int idx = hostname.IndexOf(ProxyConstants.DotSplit);
//issue #352
// issue #352
if (hostname.Substring(0, idx).Contains("-"))
{
return hostname;
......@@ -111,45 +111,45 @@ namespace Titanium.Web.Proxy.Helpers
return "*." + rootDomain;
}
//return as it is
// return as it is
return hostname;
}
/// <summary>
/// Determines whether is connect method.
/// </summary>
/// <param name="clientStream">The client stream.</param>
/// <param name="clientStreamReader">The client stream reader.</param>
/// <returns>1: when CONNECT, 0: when valid HTTP method, -1: otherwise</returns>
internal static Task<int> IsConnectMethod(CustomBufferedStream clientStream)
internal static Task<int> IsConnectMethod(ICustomStreamReader clientStreamReader)
{
return StartsWith(clientStream, "CONNECT");
return StartsWith(clientStreamReader, "CONNECT");
}
/// <summary>
/// Determines whether is pri method (HTTP/2).
/// </summary>
/// <param name="clientStream">The client stream.</param>
/// <param name="clientStreamReader">The client stream reader.</param>
/// <returns>1: when PRI, 0: when valid HTTP method, -1: otherwise</returns>
internal static Task<int> IsPriMethod(CustomBufferedStream clientStream)
internal static Task<int> IsPriMethod(ICustomStreamReader clientStreamReader)
{
return StartsWith(clientStream, "PRI");
return StartsWith(clientStreamReader, "PRI");
}
/// <summary>
/// Determines whether the stream starts with the given string.
/// </summary>
/// <param name="clientStream">The client stream.</param>
/// <param name="clientStreamReader">The client stream reader.</param>
/// <param name="expectedStart">The expected start.</param>
/// <returns>
/// 1: when starts with the given string, 0: when valid HTTP method, -1: otherwise
/// </returns>
private static async Task<int> StartsWith(CustomBufferedStream clientStream, string expectedStart)
private static async Task<int> StartsWith(ICustomStreamReader clientStreamReader, string expectedStart)
{
bool isExpected = true;
int legthToCheck = 10;
for (int i = 0; i < legthToCheck; i++)
{
int b = await clientStream.PeekByteAsync(i);
int b = await clientStreamReader.PeekByteAsync(i);
if (b == -1)
{
return -1;
......
......@@ -11,17 +11,20 @@ using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Helpers
{
internal class HttpWriter : CustomBinaryWriter
internal class HttpWriter : ICustomStreamWriter
{
private readonly Stream stream;
private static readonly byte[] newLine = ProxyConstants.NewLine;
private static readonly Encoder encoder = Encoding.ASCII.GetEncoder();
private readonly char[] charBuffer;
internal HttpWriter(Stream stream, int bufferSize) : base(stream)
internal HttpWriter(Stream stream, int bufferSize)
{
BufferSize = bufferSize;
this.stream = stream;
// ASCII encoder max byte count is char count + 1
charBuffer = new char[BufferSize - 1];
......@@ -62,7 +65,7 @@ namespace Titanium.Web.Proxy.Helpers
idx += newLineChars;
}
return WriteAsync(buffer, 0, idx, cancellationToken);
return stream.WriteAsync(buffer, 0, idx, cancellationToken);
}
finally
{
......@@ -82,7 +85,7 @@ namespace Titanium.Web.Proxy.Helpers
idx += newLineChars;
}
return WriteAsync(buffer, 0, idx, cancellationToken);
return stream.WriteAsync(buffer, 0, idx, cancellationToken);
}
}
......@@ -109,26 +112,26 @@ namespace Titanium.Web.Proxy.Helpers
await WriteLineAsync(cancellationToken);
if (flush)
{
await FlushAsync(cancellationToken);
await stream.FlushAsync(cancellationToken);
}
}
internal async Task WriteAsync(byte[] data, bool flush = false, CancellationToken cancellationToken = default)
{
await WriteAsync(data, 0, data.Length, cancellationToken);
await stream.WriteAsync(data, 0, data.Length, cancellationToken);
if (flush)
{
await FlushAsync(cancellationToken);
await stream.FlushAsync(cancellationToken);
}
}
internal async Task WriteAsync(byte[] data, int offset, int count, bool flush,
CancellationToken cancellationToken = default)
{
await WriteAsync(data, offset, count, cancellationToken);
await stream.WriteAsync(data, offset, count, cancellationToken);
if (flush)
{
await FlushAsync(cancellationToken);
await stream.FlushAsync(cancellationToken);
}
}
......@@ -159,22 +162,22 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="onCopy"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
internal Task CopyBodyAsync(CustomBinaryReader streamReader, bool isChunked, long contentLength,
internal Task CopyBodyAsync(ICustomStreamReader streamReader, bool isChunked, long contentLength,
Action<byte[], int, int> onCopy, CancellationToken cancellationToken)
{
//For chunked request we need to read data as they arrive, until we reach a chunk end symbol
// For chunked request we need to read data as they arrive, until we reach a chunk end symbol
if (isChunked)
{
return CopyBodyChunkedAsync(streamReader, onCopy, cancellationToken);
}
//http 1.0 or the stream reader limits the stream
// http 1.0 or the stream reader limits the stream
if (contentLength == -1)
{
contentLength = long.MaxValue;
}
//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);
}
......@@ -204,7 +207,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="onCopy"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
private async Task CopyBodyChunkedAsync(CustomBinaryReader reader, Action<byte[], int, int> onCopy,
private async Task CopyBodyChunkedAsync(ICustomStreamReader reader, Action<byte[], int, int> onCopy,
CancellationToken cancellationToken)
{
while (true)
......@@ -227,7 +230,7 @@ namespace Titanium.Web.Proxy.Helpers
await WriteLineAsync(cancellationToken);
//chunk trail
// chunk trail
await reader.ReadLineAsync(cancellationToken);
if (chunkSize == 0)
......@@ -245,31 +248,39 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="onCopy"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
private async Task CopyBytesFromStream(CustomBinaryReader reader, long count, Action<byte[], int, int> onCopy,
private async Task CopyBytesFromStream(ICustomStreamReader reader, long count, Action<byte[], int, int> onCopy,
CancellationToken cancellationToken)
{
var buffer = reader.Buffer;
long remainingBytes = count;
var buffer = BufferPool.GetBuffer(BufferSize);
while (remainingBytes > 0)
try
{
int bytesToRead = buffer.Length;
if (remainingBytes < bytesToRead)
{
bytesToRead = (int)remainingBytes;
}
long remainingBytes = count;
int bytesRead = await reader.ReadBytesAsync(buffer, bytesToRead, cancellationToken);
if (bytesRead == 0)
while (remainingBytes > 0)
{
break;
}
int bytesToRead = buffer.Length;
if (remainingBytes < bytesToRead)
{
bytesToRead = (int)remainingBytes;
}
remainingBytes -= bytesRead;
int bytesRead = await reader.ReadAsync(buffer, 0, bytesToRead, cancellationToken);
if (bytesRead == 0)
{
break;
}
await WriteAsync(buffer, 0, bytesRead, cancellationToken);
remainingBytes -= bytesRead;
onCopy?.Invoke(buffer, 0, bytesRead);
await stream.WriteAsync(buffer, 0, bytesRead, cancellationToken);
onCopy?.Invoke(buffer, 0, bytesRead);
}
}
finally
{
BufferPool.ReturnBuffer(buffer);
}
}
......@@ -291,5 +302,33 @@ namespace Titanium.Web.Proxy.Helpers
await WriteBodyAsync(body, requestResponse.IsChunked, cancellationToken);
}
}
/// <summary>When overridden in a derived class, writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written.</summary>
/// <param name="buffer">An array of bytes. This method copies count bytes from buffer to the current stream.</param>
/// <param name="offset">The zero-based byte offset in buffer at which to begin copying bytes to the current stream.</param>
/// <param name="count">The number of bytes to be written to the current stream.</param>
/// <exception cref="T:System.ArgumentException">The sum of offset and count is greater than the buffer length.</exception>
/// <exception cref="T:System.ArgumentNullException">buffer is null.</exception>
/// <exception cref="T:System.ArgumentOutOfRangeException">offset or count is negative.</exception>
/// <exception cref="T:System.IO.IOException">An I/O error occured, such as the specified file cannot be found.</exception>
/// <exception cref="T:System.NotSupportedException">The stream does not support writing.</exception>
/// <exception cref="T:System.ObjectDisposedException"><see cref="M:System.IO.Stream.Write(System.Byte[],System.Int32,System.Int32)"></see> was called after the stream was closed.</exception>
public void Write(byte[] buffer, int offset, int count)
{
stream.Write(buffer, offset, count);
}
/// <summary>
/// Asynchronously writes a sequence of bytes to the current stream, advances the current position within this stream by the number of bytes written, and monitors cancellation requests.
/// </summary>
/// <param name="buffer">The buffer to write data from.</param>
/// <param name="offset">The zero-based byte offset in <paramref name="buffer" /> from which to begin copying bytes to the stream.</param>
/// <param name="count">The maximum number of bytes to write.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <see cref="P:System.Threading.CancellationToken.None" />.</param>
/// <returns>A task that represents the asynchronous write operation.</returns>
public Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
return stream.WriteAsync(buffer, offset, count, cancellationToken);
}
}
}
......@@ -21,6 +21,7 @@ namespace Titanium.Web.Proxy.Helpers
// get local IP addresses
var localIPs = Dns.GetHostAddresses(Dns.GetHostName());
// test if any host IP equals to any local IP or to localhost
return localIPs.Contains(address);
}
......
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.Win32;
using Titanium.Web.Proxy.Models;
......@@ -6,7 +7,6 @@ using Titanium.Web.Proxy.Models;
// Helper classes for setting system proxy settings
namespace Titanium.Web.Proxy.Helpers
{
internal class HttpSystemProxyValue
{
internal string HostName { get; set; }
......@@ -37,6 +37,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <summary>
/// Manage system proxy settings
/// </summary>
[SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleType", Justification = "Reviewed.")]
internal class SystemProxyManager
{
private const string regKeyInternetSettings = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings";
......@@ -66,7 +67,7 @@ namespace Titanium.Web.Proxy.Helpers
return false;
};
//On Console exit make sure we also exit the proxy
// On Console exit make sure we also exit the proxy
NativeMethods.SetConsoleCtrlHandler(NativeMethods.Handler, true);
}
}
......
......@@ -296,7 +296,7 @@ namespace Titanium.Web.Proxy.Http
/// </summary>
internal void FixProxyHeaders()
{
//If proxy-connection close was returned inform to close the connection
// If proxy-connection close was returned inform to close the connection
string proxyHeader = GetHeaderValueOrNull(KnownHeaders.ProxyConnection);
RemoveHeader(KnownHeaders.ProxyConnection);
......
......@@ -8,7 +8,7 @@ namespace Titanium.Web.Proxy.Http
{
internal static class HeaderParser
{
internal static async Task ReadHeaders(CustomBinaryReader reader, HeaderCollection headerCollection,
internal static async Task ReadHeaders(ICustomStreamReader reader, HeaderCollection headerCollection,
CancellationToken cancellationToken)
{
string tmpLine;
......
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Threading;
......@@ -20,7 +21,6 @@ namespace Titanium.Web.Proxy.Http
{
this.bufferSize = bufferSize;
RequestId = Guid.NewGuid();
Request = request ?? new Request();
Response = response ?? new Response();
}
......@@ -28,12 +28,17 @@ namespace Titanium.Web.Proxy.Http
/// <summary>
/// Connection to server
/// </summary>
internal TcpConnection ServerConnection { get; set; }
internal TcpServerConnection ServerConnection { get; set; }
/// <summary>
/// Request ID.
/// Stores internal data for the session.
/// </summary>
public Guid RequestId { get; }
internal InternalDataStore Data { get; } = new InternalDataStore();
/// <summary>
/// Gets or sets the user data.
/// </summary>
public object UserData { get; set; }
/// <summary>
/// Override UpStreamEndPoint for this request; Local NIC via request is made
......@@ -69,11 +74,11 @@ namespace Titanium.Web.Proxy.Http
/// <summary>
/// Set the tcp connection to server used by this webclient
/// </summary>
/// <param name="connection">Instance of <see cref="TcpConnection" /></param>
internal void SetConnection(TcpConnection connection)
/// <param name="serverConnection">Instance of <see cref="TcpServerConnection" /></param>
internal void SetConnection(TcpServerConnection serverConnection)
{
connection.LastAccess = DateTime.Now;
ServerConnection = connection;
serverConnection.LastAccess = DateTime.Now;
ServerConnection = serverConnection;
}
/// <summary>
......@@ -89,13 +94,12 @@ namespace Titanium.Web.Proxy.Http
var writer = ServerConnection.StreamWriter;
//prepare the request & headers
// prepare the request & headers
await writer.WriteLineAsync(Request.CreateRequestLine(Request.Method,
useUpstreamProxy || isTransparent ? Request.OriginalUrl : Request.RequestUri.PathAndQuery,
Request.HttpVersion), cancellationToken);
//Send Authentication to Upstream proxy if needed
// Send Authentication to Upstream proxy if needed
if (!isTransparent && upstreamProxy != null
&& ServerConnection.IsHttps == false
&& !string.IsNullOrEmpty(upstreamProxy.UserName)
......@@ -106,7 +110,7 @@ namespace Titanium.Web.Proxy.Http
.WriteToStreamAsync(writer, cancellationToken);
}
//write request headers
// write request headers
foreach (var header in Request.Headers)
{
if (isTransparent || header.Name != KnownHeaders.ProxyAuthorization)
......@@ -121,23 +125,23 @@ namespace Titanium.Web.Proxy.Http
{
if (Request.ExpectContinue)
{
string httpStatus = await ServerConnection.StreamReader.ReadLineAsync(cancellationToken);
string httpStatus = await ServerConnection.Stream.ReadLineAsync(cancellationToken);
Response.ParseResponseLine(httpStatus, out _, out int responseStatusCode,
out string responseStatusDescription);
//find if server is willing for expect continue
// find if server is willing for expect continue
if (responseStatusCode == (int)HttpStatusCode.Continue
&& responseStatusDescription.EqualsIgnoreCase("continue"))
{
Request.Is100Continue = true;
await ServerConnection.StreamReader.ReadLineAsync(cancellationToken);
await ServerConnection.Stream.ReadLineAsync(cancellationToken);
}
else if (responseStatusCode == (int)HttpStatusCode.ExpectationFailed
&& responseStatusDescription.EqualsIgnoreCase("expectation failed"))
{
Request.ExpectationFailed = true;
await ServerConnection.StreamReader.ReadLineAsync(cancellationToken);
await ServerConnection.Stream.ReadLineAsync(cancellationToken);
}
}
}
......@@ -149,13 +153,13 @@ namespace Titanium.Web.Proxy.Http
/// <returns></returns>
internal async Task ReceiveResponse(CancellationToken cancellationToken)
{
//return if this is already read
// return if this is already read
if (Response.StatusCode != 0)
{
return;
}
string httpStatus = await ServerConnection.StreamReader.ReadLineAsync(cancellationToken);
string httpStatus = await ServerConnection.Stream.ReadLineAsync(cancellationToken);
if (httpStatus == null)
{
throw new IOException();
......@@ -163,7 +167,7 @@ namespace Titanium.Web.Proxy.Http
if (httpStatus == string.Empty)
{
httpStatus = await ServerConnection.StreamReader.ReadLineAsync(cancellationToken);
httpStatus = await ServerConnection.Stream.ReadLineAsync(cancellationToken);
}
Response.ParseResponseLine(httpStatus, out var version, out int statusCode, out string statusDescription);
......@@ -172,16 +176,16 @@ namespace Titanium.Web.Proxy.Http
Response.StatusCode = statusCode;
Response.StatusDescription = statusDescription;
//For HTTP 1.1 comptibility server may send expect-continue even if not asked for it in request
// For HTTP 1.1 comptibility server may send expect-continue even if not asked for it in request
if (Response.StatusCode == (int)HttpStatusCode.Continue
&& Response.StatusDescription.EqualsIgnoreCase("continue"))
{
//Read the next line after 100-continue
// Read the next line after 100-continue
Response.Is100Continue = true;
Response.StatusCode = 0;
await ServerConnection.StreamReader.ReadLineAsync(cancellationToken);
await ServerConnection.Stream.ReadLineAsync(cancellationToken);
//now receive response
// now receive response
await ReceiveResponse(cancellationToken);
return;
}
......@@ -189,18 +193,18 @@ namespace Titanium.Web.Proxy.Http
if (Response.StatusCode == (int)HttpStatusCode.ExpectationFailed
&& Response.StatusDescription.EqualsIgnoreCase("expectation failed"))
{
//read next line after expectation failed response
// read next line after expectation failed response
Response.ExpectationFailed = true;
Response.StatusCode = 0;
await ServerConnection.StreamReader.ReadLineAsync(cancellationToken);
await ServerConnection.Stream.ReadLineAsync(cancellationToken);
//now receive response
// now receive response
await ReceiveResponse(cancellationToken);
return;
}
//Read the response headers in to unique and non-unique header collections
await HeaderParser.ReadHeaders(ServerConnection.StreamReader, Response.Headers, cancellationToken);
// Read the response headers in to unique and non-unique header collections
await HeaderParser.ReadHeaders(ServerConnection.Stream, Response.Headers, cancellationToken);
}
/// <summary>
......
using System.Collections.Generic;
namespace Titanium.Web.Proxy.Http
{
class InternalDataStore : Dictionary<string, object>
{
public bool TryGetValueAs<T>(string key, out T value)
{
bool result = TryGetValue(key, out var value1);
if (result)
{
value = (T)value1;
}
else
{
value = default;
}
return result;
}
public T GetAs<T>(string key)
{
return (T)this[key];
}
}
}
\ No newline at end of file
......@@ -43,19 +43,19 @@ namespace Titanium.Web.Proxy.Http
{
long contentLength = ContentLength;
//If content length is set to 0 the request has no body
// If content length is set to 0 the request has no body
if (contentLength == 0)
{
return false;
}
//Has body only if request is chunked or content length >0
// Has body only if request is chunked or content length >0
if (IsChunked || contentLength > 0)
{
return true;
}
//has body if POST and when version is http/1.0
// has body if POST and when version is http/1.0
if (Method == "POST" && HttpVersion == HttpHeader.Version10)
{
return true;
......@@ -157,7 +157,7 @@ namespace Titanium.Web.Proxy.Http
return;
}
//GET request don't have a request body to read
// GET request don't have a request body to read
if (!HasBody)
{
throw new BodyNotFoundException("Request don't have a body. " +
......@@ -189,7 +189,7 @@ namespace Titanium.Web.Proxy.Http
internal static void ParseRequestLine(string httpCmd, out string httpMethod, out string httpUrl,
out Version version)
{
//break up the line into three components (method, remote URL & Http Version)
// break up the line into three components (method, remote URL & Http Version)
var httpCmdSplit = httpCmd.Split(ProxyConstants.SpaceSplit, 3);
if (httpCmdSplit.Length < 2)
......@@ -197,7 +197,7 @@ namespace Titanium.Web.Proxy.Http
throw new Exception("Invalid HTTP request line: " + httpCmd);
}
//Find the request Verb
// Find the request Verb
httpMethod = httpCmdSplit[0];
if (!IsAllUpper(httpMethod))
{
......@@ -206,13 +206,13 @@ namespace Titanium.Web.Proxy.Http
httpUrl = httpCmdSplit[1];
//parse the HTTP version
// parse the HTTP version
version = HttpHeader.Version11;
if (httpCmdSplit.Length == 3)
{
string httpVersion = httpCmdSplit[2].Trim();
if (string.Equals(httpVersion, "HTTP/1.0", StringComparison.OrdinalIgnoreCase))
if (httpVersion.EqualsIgnoreCase("HTTP/1.0"))
{
version = HttpHeader.Version10;
}
......
using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Text;
using Titanium.Web.Proxy.Compression;
......@@ -14,7 +15,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary>
/// Cached body content as byte array.
/// </summary>
protected byte[] BodyInternal;
protected byte[] BodyInternal { get; private set; }
/// <summary>
/// Cached body as string.
......@@ -24,7 +25,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary>
/// Store weather the original request/response has body or not, since the user may change the parameters
/// </summary>
internal bool OriginalHasBody;
internal bool OriginalHasBody { get; set; }
/// <summary>
/// Keeps the body data after the session is finished.
......@@ -62,6 +63,7 @@ namespace Titanium.Web.Proxy.Http
return -1;
}
set
{
if (value >= 0)
......@@ -105,6 +107,7 @@ namespace Titanium.Web.Proxy.Http
string headerValue = Headers.GetHeaderValueOrNull(KnownHeaders.TransferEncoding);
return headerValue != null && headerValue.ContainsIgnoreCase(KnownHeaders.TransferEncodingChunked);
}
set
{
if (value)
......@@ -135,12 +138,13 @@ namespace Titanium.Web.Proxy.Http
EnsureBodyAvailable();
return BodyInternal;
}
internal set
{
BodyInternal = value;
bodyString = null;
//If there is a content length header update it
// If there is a content length header update it
UpdateContentLength();
}
}
......@@ -177,10 +181,9 @@ namespace Titanium.Web.Proxy.Http
/// <returns></returns>
internal byte[] GetCompressedBody(string encodingType, byte[] body)
{
var compressor = CompressionFactory.GetCompression(encodingType);
using (var ms = new MemoryStream())
{
using (var zip = compressor.GetStream(ms))
using (var zip = CompressionFactory.Create(encodingType, ms))
{
zip.Write(body, 0, body.Length);
}
......
......@@ -47,21 +47,21 @@ namespace Titanium.Web.Proxy.Http
{
long contentLength = ContentLength;
//If content length is set to 0 the response has no body
// If content length is set to 0 the response has no body
if (contentLength == 0)
{
return false;
}
//Has body only if response is chunked or content length >0
//If none are true then check if connection:close header exist, if so write response until server or client terminates the connection
// Has body only if response is chunked or content length >0
// If none are true then check if connection:close header exist, if so write response until server or client terminates the connection
if (IsChunked || contentLength > 0 || !KeepAlive)
{
return true;
}
//has response if connection:keep-alive header exist and when version is http/1.0
//Because in Http 1.0 server can return a response without content-length (expectation being client would read until end of stream)
// has response if connection:keep-alive header exist and when version is http/1.0
// Because in Http 1.0 server can return a response without content-length (expectation being client would read until end of stream)
if (KeepAlive && HttpVersion == HttpHeader.Version10)
{
return true;
......@@ -155,7 +155,7 @@ namespace Titanium.Web.Proxy.Http
string httpVersion = httpResult[0];
version = HttpHeader.Version11;
if (string.Equals(httpVersion, "HTTP/1.0", StringComparison.OrdinalIgnoreCase))
if (httpVersion.EqualsIgnoreCase("HTTP/1.0"))
{
version = HttpHeader.Version10;
}
......
......@@ -3,9 +3,9 @@ using System.Web;
namespace Titanium.Web.Proxy.Http.Responses
{
/// <summary>
/// Anything other than a 200 or 302 response
/// </summary>
/// <summary>
/// Anything other than a 200 or 302 response
/// </summary>
public class GenericResponse : Response
{
/// <summary>
......@@ -19,8 +19,8 @@ namespace Titanium.Web.Proxy.Http.Responses
#if NET45
StatusDescription = HttpWorkerRequest.GetStatusDescription(StatusCode);
#else
//todo: this is not really correct, status description should contain spaces, too
//see: https://tools.ietf.org/html/rfc7231#section-6.1
// todo: this is not really correct, status description should contain spaces, too
// see: https://tools.ietf.org/html/rfc7231#section-6.1
StatusDescription = status.ToString();
#endif
}
......
......@@ -8,7 +8,7 @@ namespace Titanium.Web.Proxy.Http.Responses
public sealed class RedirectResponse : Response
{
/// <summary>
/// Constructor.
/// Initializes a new instance of the <see cref="RedirectResponse" /> class.
/// </summary>
public RedirectResponse()
{
......
......@@ -95,7 +95,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
if (hostName != null)
{
//add subject alternative names
// add subject alternative names
var subjectAlternativeNames = new Asn1Encodable[] { new GeneralName(GeneralName.DnsName, hostName) };
var subjectAlternativeNamesExtension = new DerSequence(subjectAlternativeNames);
......
......@@ -64,7 +64,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
typeSignerCertificate = Type.GetTypeFromProgID("X509Enrollment.CSignerCertificate");
typeX509Enrollment = Type.GetTypeFromProgID("X509Enrollment.CX509Enrollment");
//for alternative names
// for alternative names
typeAltNamesCollection = Type.GetTypeFromProgID("X509Enrollment.CAlternativeNames");
typeExtNames = Type.GetTypeFromProgID("X509Enrollment.CX509ExtensionAlternativeNames");
typeCAlternativeName = Type.GetTypeFromProgID("X509Enrollment.CAlternativeName");
......@@ -192,7 +192,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
if (!isRoot)
{
//add alternative names
// add alternative names
// https://forums.iis.net/t/1180823.aspx
var altNameCollection = Activator.CreateInstance(typeAltNamesCollection);
......@@ -284,15 +284,19 @@ namespace Titanium.Web.Proxy.Network.Certificate
cancellationToken).Result;
}
//Subject
// Subject
string fullSubject = $"CN={sSubjectCN}";
//Sig Algo
// Sig Algo
const string hashAlgo = "SHA256";
//Grace Days
// Grace Days
const int graceDays = -366;
//ValiDays
// ValiDays
const int validDays = 1825;
//KeyLength
// KeyLength
const int keyLength = 2048;
var graceTime = DateTime.Now.AddDays(graceDays);
......
#if DEBUG
using System;
using System.IO;
using System.Text;
using System.Threading;
using StreamExtended.Network;
......@@ -15,30 +17,54 @@ namespace Titanium.Web.Proxy.Network
private readonly FileStream fileStreamSent;
public DebugCustomBufferedStream(Stream baseStream, int bufferSize) : base(baseStream, bufferSize)
public DebugCustomBufferedStream(Guid connectionId, string type, Stream baseStream, int bufferSize, bool leaveOpen = false) : base(baseStream, bufferSize, leaveOpen)
{
Counter = Interlocked.Increment(ref counter);
fileStreamSent = new FileStream(Path.Combine(basePath, $"{Counter}_sent.dat"), FileMode.Create);
fileStreamReceived = new FileStream(Path.Combine(basePath, $"{Counter}_received.dat"), FileMode.Create);
fileStreamSent = new FileStream(Path.Combine(basePath, $"{connectionId}_{type}_{Counter}_sent.dat"), FileMode.Create);
fileStreamReceived = new FileStream(Path.Combine(basePath, $"{connectionId}_{type}_{Counter}_received.dat"), FileMode.Create);
}
public int Counter { get; }
protected override void OnDataSent(byte[] buffer, int offset, int count)
protected override void OnDataWrite(byte[] buffer, int offset, int count)
{
fileStreamSent.Write(buffer, offset, count);
Flush();
}
protected override void OnDataReceived(byte[] buffer, int offset, int count)
protected override void OnDataRead(byte[] buffer, int offset, int count)
{
fileStreamReceived.Write(buffer, offset, count);
Flush();
}
public void LogException(Exception ex)
{
var data = Encoding.UTF8.GetBytes("EXCEPTION: " + ex);
fileStreamReceived.Write(data, 0, data.Length);
fileStreamReceived.Flush();
}
public override void Flush()
{
fileStreamSent.Flush(true);
fileStreamReceived.Flush(true);
base.Flush();
if (CanWrite)
{
base.Flush();
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
Flush();
fileStreamSent.Dispose();
fileStreamReceived.Dispose();
}
base.Dispose(disposing);
}
}
}
......
using System.Net.Sockets;
using StreamExtended.Network;
using StreamExtended.Network;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Network.Tcp;
namespace Titanium.Web.Proxy.Network
{
......@@ -10,20 +10,15 @@ namespace Titanium.Web.Proxy.Network
internal class ProxyClient
{
/// <summary>
/// TcpClient used to communicate with client
/// TcpClient connection used to communicate with client
/// </summary>
internal TcpClient TcpClient { get; set; }
internal TcpClientConnection ClientConnection { get; set; }
/// <summary>
/// Holds the stream to client
/// </summary>
internal CustomBufferedStream ClientStream { get; set; }
/// <summary>
/// Used to read line by line from client
/// </summary>
internal CustomBinaryReader ClientStreamReader { get; set; }
/// <summary>
/// Used to write line by line to client
/// </summary>
......
using System;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using Titanium.Web.Proxy.Extensions;
namespace Titanium.Web.Proxy.Network.Tcp
{
/// <summary>
/// An object that holds TcpConnection to a particular server and port
/// </summary>
internal class TcpClientConnection : IDisposable
{
internal TcpClientConnection(ProxyServer proxyServer, TcpClient tcpClient)
{
this.tcpClient = tcpClient;
this.proxyServer = proxyServer;
this.proxyServer.UpdateClientConnectionCount(true);
}
private ProxyServer proxyServer { get; }
public Guid Id { get; } = Guid.NewGuid();
public EndPoint LocalEndPoint => tcpClient.Client.LocalEndPoint;
public EndPoint RemoteEndPoint => tcpClient.Client.RemoteEndPoint;
internal SslApplicationProtocol NegotiatedApplicationProtocol { get; set; }
private readonly TcpClient tcpClient;
public Stream GetStream()
{
return tcpClient.GetStream();
}
/// <summary>
/// Dispose.
/// </summary>
public void Dispose()
{
tcpClient.CloseSocket();
proxyServer.UpdateClientConnectionCount(false);
}
}
}
......@@ -23,55 +23,55 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// </summary>
/// <param name="remoteHostName"></param>
/// <param name="remotePort"></param>
/// <param name="applicationProtocols"></param>
/// <param name="httpVersion"></param>
/// <param name="decryptSsl"></param>
/// <param name="applicationProtocols"></param>
/// <param name="isConnect"></param>
/// <param name="proxyServer"></param>
/// <param name="upStreamEndPoint"></param>
/// <param name="externalProxy"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
internal async Task<TcpConnection> CreateClient(string remoteHostName, int remotePort,
List<SslApplicationProtocol> applicationProtocols, Version httpVersion, bool decryptSsl, bool isConnect,
internal async Task<TcpServerConnection> CreateClient(string remoteHostName, int remotePort,
Version httpVersion, bool decryptSsl, List<SslApplicationProtocol> applicationProtocols, bool isConnect,
ProxyServer proxyServer, IPEndPoint upStreamEndPoint, ExternalProxy externalProxy,
CancellationToken cancellationToken)
{
bool useUpstreamProxy = false;
//check if external proxy is set for HTTP/HTTPS
// check if external proxy is set for HTTP/HTTPS
if (externalProxy != null &&
!(externalProxy.HostName == remoteHostName && externalProxy.Port == remotePort))
{
useUpstreamProxy = true;
//check if we need to ByPass
// check if we need to ByPass
if (externalProxy.BypassLocalhost && NetworkHelper.IsLocalIpAddress(remoteHostName))
{
useUpstreamProxy = false;
}
}
TcpClient client = null;
TcpClient tcpClient = null;
CustomBufferedStream stream = null;
bool http2Supported = false;
SslApplicationProtocol negotiatedApplicationProtocol = default;
try
{
client = new TcpClient(upStreamEndPoint);
tcpClient = new TcpClient(upStreamEndPoint);
//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)
{
await client.ConnectAsync(externalProxy.HostName, externalProxy.Port);
await tcpClient.ConnectAsync(externalProxy.HostName, externalProxy.Port);
}
else
{
await client.ConnectAsync(remoteHostName, remotePort);
await tcpClient.ConnectAsync(remoteHostName, remotePort);
}
stream = new CustomBufferedStream(client.GetStream(), proxyServer.BufferSize);
stream = new CustomBufferedStream(tcpClient.GetStream(), proxyServer.BufferSize);
if (useUpstreamProxy && (isConnect || decryptSsl))
{
......@@ -93,20 +93,17 @@ namespace Titanium.Web.Proxy.Network.Tcp
await writer.WriteRequestAsync(connectRequest, cancellationToken: cancellationToken);
using (var reader = new CustomBinaryReader(stream, proxyServer.BufferSize))
{
string httpStatus = await reader.ReadLineAsync(cancellationToken);
string httpStatus = await stream.ReadLineAsync(cancellationToken);
Response.ParseResponseLine(httpStatus, out _, out int statusCode, out string statusDescription);
Response.ParseResponseLine(httpStatus, out _, out int statusCode, out string statusDescription);
if (statusCode != 200 && !statusDescription.EqualsIgnoreCase("OK")
&& !statusDescription.EqualsIgnoreCase("Connection Established"))
{
throw new Exception("Upstream proxy failed to create a secure tunnel");
}
await reader.ReadAndIgnoreAllLinesAsync(cancellationToken);
if (statusCode != 200 && !statusDescription.EqualsIgnoreCase("OK")
&& !statusDescription.EqualsIgnoreCase("Connection Established"))
{
throw new Exception("Upstream proxy failed to create a secure tunnel");
}
await stream.ReadAndIgnoreAllLinesAsync(cancellationToken);
}
if (decryptSsl)
......@@ -117,42 +114,35 @@ namespace Titanium.Web.Proxy.Network.Tcp
var options = new SslClientAuthenticationOptions();
options.ApplicationProtocols = applicationProtocols;
if (options.ApplicationProtocols == null || options.ApplicationProtocols.Count == 0)
{
options.ApplicationProtocols = SslExtensions.Http11ProtocolAsList;
}
options.TargetHost = remoteHostName;
options.ClientCertificates = null;
options.EnabledSslProtocols = proxyServer.SupportedSslProtocols;
options.CertificateRevocationCheckMode = proxyServer.CheckCertificateRevocation;
await sslStream.AuthenticateAsClientAsync(options, cancellationToken);
#if NETCOREAPP2_1
http2Supported = sslStream.NegotiatedApplicationProtocol == SslApplicationProtocol.Http2;
negotiatedApplicationProtocol = sslStream.NegotiatedApplicationProtocol;
#endif
}
client.ReceiveTimeout = proxyServer.ConnectionTimeOutSeconds * 1000;
client.SendTimeout = proxyServer.ConnectionTimeOutSeconds * 1000;
tcpClient.ReceiveTimeout = proxyServer.ConnectionTimeOutSeconds * 1000;
tcpClient.SendTimeout = proxyServer.ConnectionTimeOutSeconds * 1000;
}
catch (Exception)
{
stream?.Dispose();
client?.Close();
tcpClient?.Close();
throw;
}
return new TcpConnection(proxyServer)
return new TcpServerConnection(proxyServer, tcpClient)
{
UpStreamProxy = externalProxy,
UpStreamEndPoint = upStreamEndPoint,
HostName = remoteHostName,
Port = remotePort,
IsHttps = decryptSsl,
IsHttp2Supported = http2Supported,
NegotiatedApplicationProtocol = negotiatedApplicationProtocol,
UseUpstreamProxy = useUpstreamProxy,
TcpClient = client,
StreamReader = new CustomBinaryReader(stream, proxyServer.BufferSize),
StreamWriter = new HttpRequestWriter(stream, proxyServer.BufferSize),
Stream = stream,
Version = httpVersion
......
using System;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using StreamExtended.Network;
using Titanium.Web.Proxy.Extensions;
......@@ -11,10 +12,11 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// <summary>
/// An object that holds TcpConnection to a particular server and port
/// </summary>
internal class TcpConnection : IDisposable
internal class TcpServerConnection : IDisposable
{
internal TcpConnection(ProxyServer proxyServer)
internal TcpServerConnection(ProxyServer proxyServer, TcpClient tcpClient)
{
this.tcpClient = tcpClient;
LastAccess = DateTime.Now;
this.proxyServer = proxyServer;
this.proxyServer.UpdateServerConnectionCount(true);
......@@ -30,7 +32,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
internal bool IsHttps { get; set; }
internal bool IsHttp2Supported { get; set; }
internal SslApplicationProtocol NegotiatedApplicationProtocol { get; set; }
internal bool UseUpstreamProxy { get; set; }
......@@ -44,12 +46,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// </summary>
internal Version Version { get; set; }
internal TcpClient TcpClient { private get; set; }
/// <summary>
/// Used to read lines from server
/// </summary>
internal CustomBinaryReader StreamReader { get; set; }
private readonly TcpClient tcpClient;
/// <summary>
/// Used to write lines to server
......@@ -71,11 +68,9 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// </summary>
public void Dispose()
{
StreamReader?.Dispose();
Stream?.Dispose();
TcpClient.CloseSocket();
tcpClient.CloseSocket();
proxyServer.UpdateServerConnectionCount(false);
}
......
......@@ -173,7 +173,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
{
internal int ulVersion;
internal int cBuffers;
internal IntPtr pBuffers; //Point to SecBuffer
internal IntPtr pBuffers; // Point to SecBuffer
internal SecurityBufferDesciption(int bufferSize)
{
......@@ -206,12 +206,12 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
{
for (int index = 0; index < cBuffers; index++)
{
//The bits were written out the following order:
//int cbBuffer;
//int BufferType;
//pvBuffer;
// The bits were written out the following order:
// int cbBuffer;
// int BufferType;
// pvBuffer;
//What we need to do here is to grab a hold of the pvBuffer allocate by the individual
//SecBuffer and release it...
// SecBuffer and release it...
int currentOffset = index * Marshal.SizeOf(typeof(Buffer));
var secBufferpvBuffer = Marshal.ReadIntPtr(pBuffers,
currentOffset + Marshal.SizeOf(typeof(int)) + Marshal.SizeOf(typeof(int)));
......@@ -249,11 +249,11 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
for (int index = 0; index < cBuffers; index++)
{
//The bits were written out the following order:
//int cbBuffer;
//int BufferType;
//pvBuffer;
//What we need to do here calculate the total number of bytes we need to copy...
// The bits were written out the following order:
// int cbBuffer;
// int BufferType;
// pvBuffer;
// What we need to do here calculate the total number of bytes we need to copy...
int currentOffset = index * Marshal.SizeOf(typeof(Buffer));
bytesToAllocate += Marshal.ReadInt32(pBuffers, currentOffset);
}
......@@ -262,12 +262,12 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
for (int index = 0, bufferIndex = 0; index < cBuffers; index++)
{
//The bits were written out the following order:
//int cbBuffer;
//int BufferType;
//pvBuffer;
//Now iterate over the individual buffers and put them together into a
//byte array...
// The bits were written out the following order:
// int cbBuffer;
// int BufferType;
// pvBuffer;
// Now iterate over the individual buffers and put them together into a
// byte array...
int currentOffset = index * Marshal.SizeOf(typeof(Buffer));
int bytesToCopy = Marshal.ReadInt32(pBuffers, currentOffset);
var secBufferpvBuffer = Marshal.ReadIntPtr(pBuffers,
......
......@@ -65,29 +65,27 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
// methods
private void Decode(byte[] message)
{
//base.Decode (message);
if (message == null)
{
throw new ArgumentNullException("message");
throw new ArgumentNullException(nameof(message));
}
if (message.Length < 12)
{
string msg = "Minimum Type3 message length is 12 bytes.";
throw new ArgumentOutOfRangeException("message", message.Length, msg);
throw new ArgumentOutOfRangeException(nameof(message), message.Length, msg);
}
if (!CheckHeader(message))
{
string msg = "Invalid Type3 message header.";
throw new ArgumentException(msg, "message");
throw new ArgumentException(msg, nameof(message));
}
if (LittleEndian.ToUInt16(message, 56) != message.Length)
{
string msg = "Invalid Type3 message length.";
throw new ArgumentException(msg, "message");
throw new ArgumentException(msg, nameof(message));
}
if (message.Length >= 64)
......
using System;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Network.WinAuth.Security;
namespace Titanium.Web.Proxy.Network.WinAuth
......@@ -16,27 +17,26 @@ namespace Titanium.Web.Proxy.Network.WinAuth
/// </summary>
/// <param name="serverHostname"></param>
/// <param name="authScheme"></param>
/// <param name="requestId"></param>
/// <param name="data"></param>
/// <returns></returns>
internal static string GetInitialAuthToken(string serverHostname, string authScheme, Guid requestId)
internal static string GetInitialAuthToken(string serverHostname, string authScheme, InternalDataStore data)
{
var tokenBytes = WinAuthEndPoint.AcquireInitialSecurityToken(serverHostname, authScheme, requestId);
var tokenBytes = WinAuthEndPoint.AcquireInitialSecurityToken(serverHostname, authScheme, data);
return string.Concat(" ", Convert.ToBase64String(tokenBytes));
}
/// <summary>
/// Get the final token given the server challenge token
/// </summary>
/// <param name="serverHostname"></param>
/// <param name="serverToken"></param>
/// <param name="requestId"></param>
/// <param name="data"></param>
/// <returns></returns>
internal static string GetFinalAuthToken(string serverHostname, string serverToken, Guid requestId)
internal static string GetFinalAuthToken(string serverHostname, string serverToken, InternalDataStore data)
{
var tokenBytes =
WinAuthEndPoint.AcquireFinalSecurityToken(serverHostname, Convert.FromBase64String(serverToken),
requestId);
data);
return string.Concat(" ", Convert.ToBase64String(tokenBytes));
}
......
......@@ -20,7 +20,7 @@ namespace Titanium.Web.Proxy
/// <returns>True if authorized.</returns>
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)
{
return true;
......@@ -41,7 +41,7 @@ namespace Titanium.Web.Proxy
if (headerValueParts.Length != 2 ||
!headerValueParts[0].EqualsIgnoreCase(KnownHeaders.ProxyAuthorizationBasic))
{
//Return not authorized
// Return not authorized
session.WebSession.Response = CreateAuthentication407Response("Proxy Authentication Invalid");
return false;
}
......@@ -50,7 +50,7 @@ namespace Titanium.Web.Proxy
int colonIndex = decoded.IndexOf(':');
if (colonIndex == -1)
{
//Return not authorized
// Return not authorized
session.WebSession.Response = CreateAuthentication407Response("Proxy Authentication Invalid");
return false;
}
......@@ -70,7 +70,7 @@ namespace Titanium.Web.Proxy
ExceptionFunc(new ProxyAuthorizationException("Error whilst authorizing request", session, e,
httpHeaders));
//Return not authorized
// Return not authorized
session.WebSession.Response = CreateAuthentication407Response("Proxy Authentication Invalid");
return false;
}
......
This diff is collapsed.
This diff is collapsed.
......@@ -11,7 +11,7 @@ namespace Titanium.Web.Proxy
/// <summary>
/// Handle the response from server.
/// </summary>
partial class ProxyServer
public partial class ProxyServer
{
/// <summary>
/// Called asynchronously when a request was successfully and we received the response.
......@@ -23,13 +23,14 @@ namespace Titanium.Web.Proxy
try
{
var cancellationToken = args.CancellationTokenSource.Token;
//read response & headers from server
// read response & headers from server
await args.WebSession.ReceiveResponse(cancellationToken);
var response = args.WebSession.Response;
args.ReRequest = false;
//check for windows authentication
// check for windows authentication
if (isWindowsAuthenticationEnabledAndSupported)
{
if (response.StatusCode == (int)HttpStatusCode.Unauthorized)
......@@ -38,13 +39,13 @@ namespace Titanium.Web.Proxy
}
else
{
WinAuthEndPoint.AuthenticatedResponse(args.WebSession.RequestId);
WinAuthEndPoint.AuthenticatedResponse(args.WebSession.Data);
}
}
response.OriginalHasBody = response.HasBody;
//if user requested call back then do it
// if user requested call back then do it
if (!response.Locked)
{
await InvokeBeforeResponse(args);
......@@ -61,7 +62,7 @@ namespace Titanium.Web.Proxy
if (!response.TerminateResponse)
{
//syphon out the response body from server before setting the new body
// syphon out the response body from server before setting the new body
await args.SyphonOutBodyAsync(false, cancellationToken);
}
else
......@@ -73,11 +74,11 @@ namespace Titanium.Web.Proxy
return;
}
//if user requested to send request again
//likely after making modifications from User Response Handler
// if user requested to send request again
// likely after making modifications from User Response Handler
if (args.ReRequest)
{
//clear current response
// clear current response
await args.ClearResponse(cancellationToken);
await HandleHttpSessionRequestInternal(args.WebSession.ServerConnection, args);
return;
......@@ -85,7 +86,7 @@ namespace Titanium.Web.Proxy
response.Locked = true;
//Write back to client 100-conitinue response if that's what server returned
// Write back to client 100-conitinue response if that's what server returned
if (response.Is100Continue)
{
await clientStreamWriter.WriteResponseStatusAsync(response.HttpVersion,
......@@ -110,12 +111,12 @@ namespace Titanium.Web.Proxy
}
else
{
//Write back response status to client
// 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
// Write body if exists
if (response.HasBody)
{
await args.CopyResponseBodyAsync(clientStreamWriter, TransformationMode.None,
......
<StyleCopSettings Version="105">
<Analyzers>
<Analyzer AnalyzerId="StyleCop.CSharp.DocumentationRules">
<Rules>
<Rule Name="FileMustHaveHeader">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementParameterDocumentationMustHaveText">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementReturnValueMustBeDocumented">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementReturnValueDocumentationMustHaveText">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="PropertySummaryDocumentationMustMatchAccessors">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ConstructorSummaryDocumentationMustBeginWithStandardText">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementsMustBeDocumented">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="EnumerationItemsMustBeDocumented">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="DocumentationTextMustContainWhitespace">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="FileHeaderMustShowCopyright">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementParametersMustBeDocumented">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="PartialElementsMustBeDocumented">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
</Rules>
<AnalyzerSettings />
</Analyzer>
<Analyzer AnalyzerId="StyleCop.CSharp.ReadabilityRules">
<Rules>
<Rule Name="PrefixLocalCallsWithThis">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ParameterMustFollowComma">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="SplitParametersMustStartOnLineAfterDeclaration">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ParametersMustBeOnSameLineOrSeparateLines">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="PrefixCallsCorrectly">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
</Rules>
<AnalyzerSettings />
</Analyzer>
<Analyzer AnalyzerId="StyleCop.CSharp.NamingRules">
<Rules>
<Rule Name="ElementMustBeginWithUpperCaseLetter">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="StaticReadonlyFieldsMustBeginWithUpperCaseLetter">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ConstFieldNamesMustBeginWithUpperCaseLetter">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="FieldNamesMustNotUseHungarianNotation">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="FieldNamesMustBeginWithLowerCaseLetter">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
</Rules>
<AnalyzerSettings />
</Analyzer>
<Analyzer AnalyzerId="StyleCop.CSharp.OrderingRules">
<Rules>
<Rule Name="UsingDirectivesMustBePlacedWithinNamespace">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementsMustBeOrderedByAccess">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="StaticElementsMustAppearBeforeInstanceElements">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementsMustAppearInTheCorrectOrder">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ConstantsMustAppearBeforeFields">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
</Rules>
<AnalyzerSettings />
</Analyzer>
</Analyzers>
</StyleCopSettings>
\ No newline at end of file
......@@ -37,8 +37,8 @@
<Reference Include="BouncyCastle.Crypto, Version=1.8.2.0, Culture=neutral, PublicKeyToken=0e99375e54769942, processorArchitecture=MSIL">
<HintPath>..\packages\Portable.BouncyCastle.1.8.2\lib\net40\BouncyCastle.Crypto.dll</HintPath>
</Reference>
<Reference Include="StreamExtended, Version=1.0.147.0, Culture=neutral, PublicKeyToken=bbfa0f1d54f50043, processorArchitecture=MSIL">
<HintPath>..\packages\StreamExtended.1.0.147-beta\lib\net45\StreamExtended.dll</HintPath>
<Reference Include="StreamExtended, Version=1.0.160.0, Culture=neutral, PublicKeyToken=bbfa0f1d54f50043, processorArchitecture=MSIL">
<HintPath>..\packages\StreamExtended.1.0.160-beta\lib\net45\StreamExtended.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
......
......@@ -13,7 +13,7 @@
<ItemGroup>
<PackageReference Include="Portable.BouncyCastle" Version="1.8.2" />
<PackageReference Include="StreamExtended" Version="1.0.147-beta" />
<PackageReference Include="StreamExtended" Version="1.0.164" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
......
......@@ -14,7 +14,7 @@
<copyright>Copyright &#x00A9; Titanium. All rights reserved.</copyright>
<tags></tags>
<dependencies>
<dependency id="StreamExtended" version="1.0.147-beta" />
<dependency id="StreamExtended" version="1.0.164" />
<dependency id="Portable.BouncyCastle" version="1.8.2" />
</dependencies>
</metadata>
......
using System;
using System.IO;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
......@@ -8,29 +9,30 @@ using StreamExtended;
using StreamExtended.Helpers;
using StreamExtended.Network;
using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network.Tcp;
namespace Titanium.Web.Proxy
{
partial class ProxyServer
public partial class ProxyServer
{
/// <summary>
/// This is called when this proxy acts as a reverse proxy (like a real http server).
/// So for HTTPS requests we would start SSL negotiation right away without expecting a CONNECT request from client
/// </summary>
/// <param name="endPoint">The transparent endpoint.</param>
/// <param name="tcpClient">The client.</param>
/// <param name="clientConnection">The client connection.</param>
/// <returns></returns>
private async Task HandleClient(TransparentProxyEndPoint endPoint, TcpClient tcpClient)
private async Task HandleClient(TransparentProxyEndPoint endPoint, TcpClientConnection clientConnection)
{
var cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = cancellationTokenSource.Token;
var clientStream = new CustomBufferedStream(tcpClient.GetStream(), BufferSize);
var clientStream = new CustomBufferedStream(clientConnection.GetStream(), BufferSize);
var clientStreamReader = new CustomBinaryReader(clientStream, BufferSize);
var clientStreamWriter = new HttpResponseWriter(clientStream, BufferSize);
try
......@@ -67,27 +69,24 @@ namespace Titanium.Web.Proxy
string certName = HttpHelper.GetWildCardDomainName(httpsHostName);
var certificate = await CertificateManager.CreateCertificateAsync(certName);
//Successfully managed to authenticate the client using the fake certificate
// Successfully managed to authenticate the client using the fake certificate
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);
clientStreamReader.Dispose();
clientStreamReader = new CustomBinaryReader(clientStream, BufferSize);
clientStreamWriter = new HttpResponseWriter(clientStream, BufferSize);
}
catch (Exception e)
{
ExceptionFunc(new Exception(
$"Could'nt authenticate client '{httpsHostName}' with fake certificate.", e));
sslStream?.Dispose();
return;
throw new ProxyConnectException(
$"Could'nt authenticate client '{httpsHostName}' with fake certificate.", e, null);
}
}
else
{
//create new connection
// create new connection
var connection = new TcpClient(UpStreamEndPoint);
await connection.ConnectAsync(httpsHostName, endPoint.Port);
connection.ReceiveTimeout = ConnectionTimeOutSeconds * 1000;
......@@ -100,7 +99,7 @@ namespace Titanium.Web.Proxy
int available = clientStream.Available;
if (available > 0)
{
//send the buffered data
// send the buffered data
var data = BufferPool.GetBuffer(BufferSize);
try
......@@ -116,7 +115,7 @@ namespace Titanium.Web.Proxy
}
}
//var serverHelloInfo = await SslTools.PeekServerHello(serverStream);
////var serverHelloInfo = await SslTools.PeekServerHello(serverStream);
await TcpHelper.SendRaw(clientStream, serverStream, BufferSize,
null, null, cancellationTokenSource, ExceptionFunc);
......@@ -124,14 +123,29 @@ namespace Titanium.Web.Proxy
}
}
//HTTPS server created - we can now decrypt the client's traffic
//Now create the request
await HandleHttpSessionRequest(endPoint, tcpClient, clientStream, clientStreamReader,
clientStreamWriter, cancellationTokenSource, isHttps ? httpsHostName : null, null, true);
// HTTPS server created - we can now decrypt the client's traffic
// Now create the request
await HandleHttpSessionRequest(endPoint, clientConnection, clientStream, clientStreamWriter,
cancellationTokenSource, isHttps ? httpsHostName : null, null);
}
catch (ProxyException e)
{
OnException(clientStream, e);
}
catch (IOException e)
{
OnException(clientStream, new Exception("Connection was aborted", e));
}
catch (SocketException e)
{
OnException(clientStream, new Exception("Could not connect", e));
}
catch (Exception e)
{
OnException(clientStream, new Exception("Error occured in whilst handling the client", e));
}
finally
{
clientStreamReader.Dispose();
clientStream.Dispose();
if (!cancellationTokenSource.IsCancellationRequested)
{
......
......@@ -19,7 +19,8 @@ namespace Titanium.Web.Proxy
private static readonly HashSet<string> authHeaderNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"WWW-Authenticate",
//IIS 6.0 messed up names below
// IIS 6.0 messed up names below
"WWWAuthenticate",
"NTLMAuthorization",
"NegotiateAuthorization",
......@@ -51,7 +52,7 @@ namespace Titanium.Web.Proxy
var response = args.WebSession.Response;
//check in non-unique headers first
// check in non-unique headers first
var header = response.Headers.NonUniqueHeaders.FirstOrDefault(x => authHeaderNames.Contains(x.Key));
if (!header.Equals(new KeyValuePair<string, List<HttpHeader>>()))
......@@ -66,12 +67,12 @@ namespace Titanium.Web.Proxy
x => authSchemes.Any(y => x.Value.StartsWith(y, StringComparison.OrdinalIgnoreCase)));
}
//check in unique headers
// check in unique headers
if (authHeader == null)
{
headerName = null;
//check in non-unique headers first
// check in non-unique headers first
var uHeader = response.Headers.Headers.FirstOrDefault(x => authHeaderNames.Contains(x.Key));
if (!uHeader.Equals(new KeyValuePair<string, HttpHeader>()))
......@@ -95,7 +96,7 @@ namespace Titanium.Web.Proxy
var expectedAuthState =
scheme == null ? State.WinAuthState.INITIAL_TOKEN : State.WinAuthState.UNAUTHORIZED;
if (!WinAuthEndPoint.ValidateWinAuthState(args.WebSession.RequestId, expectedAuthState))
if (!WinAuthEndPoint.ValidateWinAuthState(args.WebSession.Data, expectedAuthState))
{
// Invalid state, create proper error message to client
await RewriteUnauthorizedResponse(args);
......@@ -104,50 +105,51 @@ namespace Titanium.Web.Proxy
var request = args.WebSession.Request;
//clear any existing headers to avoid confusing bad servers
// clear any existing headers to avoid confusing bad servers
request.Headers.RemoveHeader(KnownHeaders.Authorization);
//initial value will match exactly any of the schemes
// initial value will match exactly any of the schemes
if (scheme != null)
{
string clientToken = WinAuthHandler.GetInitialAuthToken(request.Host, scheme, args.Id);
string clientToken = WinAuthHandler.GetInitialAuthToken(request.Host, scheme, args.WebSession.Data);
string auth = string.Concat(scheme, clientToken);
//replace existing authorization header if any
// replace existing authorization header if any
request.Headers.SetOrAddHeaderValue(KnownHeaders.Authorization, auth);
//don't need to send body for Authorization request
// don't need to send body for Authorization request
if (request.HasBody)
{
request.ContentLength = 0;
}
}
//challenge value will start with any of the scheme selected
else
{
// challenge value will start with any of the scheme selected
scheme = authSchemes.First(x =>
authHeader.Value.StartsWith(x, StringComparison.OrdinalIgnoreCase) &&
authHeader.Value.Length > x.Length + 1);
string serverToken = authHeader.Value.Substring(scheme.Length + 1);
string clientToken = WinAuthHandler.GetFinalAuthToken(request.Host, serverToken, args.Id);
string clientToken = WinAuthHandler.GetFinalAuthToken(request.Host, serverToken, args.WebSession.Data);
string auth = string.Concat(scheme, clientToken);
//there will be an existing header from initial client request
// there will be an existing header from initial client request
request.Headers.SetOrAddHeaderValue(KnownHeaders.Authorization, auth);
//send body for final auth request
// send body for final auth request
if (request.OriginalHasBody)
{
request.ContentLength = request.Body.Length;
}
}
//Need to revisit this.
//Should we cache all Set-Cokiee headers from server during auth process
//and send it to client after auth?
// Need to revisit this.
// Should we cache all Set-Cokiee headers from server during auth process
// and send it to client after auth?
// Let ResponseHandler send the updated request
args.ReRequest = true;
......@@ -172,7 +174,7 @@ namespace Titanium.Web.Proxy
// Add custom div to body to clarify that the proxy (not the client browser) failed authentication
string authErrorMessage =
"<div class=\"inserted-by-proxy\"><h2>NTLM authentication through Titanium.Web.Proxy (" +
args.ProxyClient.TcpClient.Client.LocalEndPoint +
args.ProxyClient.ClientConnection.LocalEndPoint +
") failed. Please check credentials.</h2></div>";
string originalErrorMessage =
"<div class=\"inserted-by-proxy\"><h3>Response from remote web server below.</h3></div><br/>";
......
......@@ -2,5 +2,5 @@
<packages>
<package id="Portable.BouncyCastle" version="1.8.2" targetFramework="net45" />
<package id="StreamExtended" version="1.0.147-beta" targetFramework="net45" />
<package id="StreamExtended" version="1.0.164" targetFramework="net45" />
</packages>
\ No newline at end of file
......@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width">
<meta name="title" content="Delegate AsyncEventHandler&lt;TEventArgs&gt;
| Titanium Web Proxy ">
<meta name="generator" content="docfx 2.35.2.0">
<meta name="generator" content="docfx 2.35.4.0">
<link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css">
......
......@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width">
<meta name="title" content="Class BeforeSslAuthenticateEventArgs
| Titanium Web Proxy ">
<meta name="generator" content="docfx 2.35.2.0">
<meta name="generator" content="docfx 2.35.4.0">
<link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css">
......
......@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width">
<meta name="title" content="Class CertificateSelectionEventArgs
| Titanium Web Proxy ">
<meta name="generator" content="docfx 2.35.2.0">
<meta name="generator" content="docfx 2.35.4.0">
<link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css">
......
......@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width">
<meta name="title" content="Class CertificateValidationEventArgs
| Titanium Web Proxy ">
<meta name="generator" content="docfx 2.35.2.0">
<meta name="generator" content="docfx 2.35.4.0">
<link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css">
......
......@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width">
<meta name="title" content="Class MultipartRequestPartSentEventArgs
| Titanium Web Proxy ">
<meta name="generator" content="docfx 2.35.2.0">
<meta name="generator" content="docfx 2.35.4.0">
<link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css">
......
......@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width">
<meta name="title" content="Class SessionEventArgs
| Titanium Web Proxy ">
<meta name="generator" content="docfx 2.35.2.0">
<meta name="generator" content="docfx 2.35.4.0">
<link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css">
......@@ -109,7 +109,7 @@ or when server terminates connection from proxy.</p>
<a class="xref" href="Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_ExceptionFunc">SessionEventArgsBase.ExceptionFunc</a>
</div>
<div>
<a class="xref" href="Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_Id">SessionEventArgsBase.Id</a>
<a class="xref" href="Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_UserData">SessionEventArgsBase.UserData</a>
</div>
<div>
<a class="xref" href="Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_IsHttps">SessionEventArgsBase.IsHttps</a>
......
......@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width">
<meta name="title" content="Class TunnelConnectSessionEventArgs
| Titanium Web Proxy ">
<meta name="generator" content="docfx 2.35.2.0">
<meta name="generator" content="docfx 2.35.4.0">
<link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css">
......@@ -106,7 +106,7 @@
<a class="xref" href="Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_ExceptionFunc">SessionEventArgsBase.ExceptionFunc</a>
</div>
<div>
<a class="xref" href="Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_Id">SessionEventArgsBase.Id</a>
<a class="xref" href="Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_UserData">SessionEventArgsBase.UserData</a>
</div>
<div>
<a class="xref" href="Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html#Titanium_Web_Proxy_EventArguments_SessionEventArgsBase_IsHttps">SessionEventArgsBase.IsHttps</a>
......
......@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width">
<meta name="title" content="Namespace Titanium.Web.Proxy.EventArguments
| Titanium Web Proxy ">
<meta name="generator" content="docfx 2.35.2.0">
<meta name="generator" content="docfx 2.35.4.0">
<link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css">
......@@ -96,9 +96,6 @@
<h4><a class="xref" href="Titanium.Web.Proxy.EventArguments.CertificateValidationEventArgs.html">CertificateValidationEventArgs</a></h4>
<section><p>An argument passed on to the user for validating the server certificate
during SSL authentication.</p>
</section>
<h4><a class="xref" href="Titanium.Web.Proxy.EventArguments.DataEventArgs.html">DataEventArgs</a></h4>
<section><p>Wraps the data sent/received by a proxy server instance.</p>
</section>
<h4><a class="xref" href="Titanium.Web.Proxy.EventArguments.MultipartRequestPartSentEventArgs.html">MultipartRequestPartSentEventArgs</a></h4>
<section><p>Class that wraps the multipart sent request arguments.</p>
......
......@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width">
<meta name="title" content="Delegate ExceptionHandler
| Titanium Web Proxy ">
<meta name="generator" content="docfx 2.35.2.0">
<meta name="generator" content="docfx 2.35.4.0">
<link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css">
......
......@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width">
<meta name="title" content="Class BodyNotFoundException
| Titanium Web Proxy ">
<meta name="generator" content="docfx 2.35.2.0">
<meta name="generator" content="docfx 2.35.4.0">
<link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css">
......
......@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width">
<meta name="title" content="Class ProxyAuthorizationException
| Titanium Web Proxy ">
<meta name="generator" content="docfx 2.35.2.0">
<meta name="generator" content="docfx 2.35.4.0">
<link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css">
......
......@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width">
<meta name="title" content="Class ProxyException
| Titanium Web Proxy ">
<meta name="generator" content="docfx 2.35.2.0">
<meta name="generator" content="docfx 2.35.4.0">
<link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css">
......@@ -169,7 +169,10 @@
<a id="Titanium_Web_Proxy_Exceptions_ProxyException__ctor_" data-uid="Titanium.Web.Proxy.Exceptions.ProxyException.#ctor*"></a>
<h4 id="Titanium_Web_Proxy_Exceptions_ProxyException__ctor_System_String_" data-uid="Titanium.Web.Proxy.Exceptions.ProxyException.#ctor(System.String)">ProxyException(String)</h4>
<div class="markdown level1 summary"><p>Instantiate a new instance of this exception - must be invoked by derived classes&apos; constructors</p>
<div class="markdown level1 summary"><p>Initializes a new instance of the <a class="xref" href="Titanium.Web.Proxy.Exceptions.ProxyException.html">ProxyException</a> class.</p>
<ul>
<li>must be invoked by derived classes&apos; constructors</li>
</ul>
</div>
<div class="markdown level1 conceptual"></div>
<h5 class="decalaration">Declaration</h5>
......@@ -198,7 +201,10 @@
<a id="Titanium_Web_Proxy_Exceptions_ProxyException__ctor_" data-uid="Titanium.Web.Proxy.Exceptions.ProxyException.#ctor*"></a>
<h4 id="Titanium_Web_Proxy_Exceptions_ProxyException__ctor_System_String_System_Exception_" data-uid="Titanium.Web.Proxy.Exceptions.ProxyException.#ctor(System.String,System.Exception)">ProxyException(String, Exception)</h4>
<div class="markdown level1 summary"><p>Instantiate this exception - must be invoked by derived classes&apos; constructors</p>
<div class="markdown level1 summary"><p>Initializes a new instance of the <a class="xref" href="Titanium.Web.Proxy.Exceptions.ProxyException.html">ProxyException</a> class.</p>
<ul>
<li>must be invoked by derived classes&apos; constructors</li>
</ul>
</div>
<div class="markdown level1 conceptual"></div>
<h5 class="decalaration">Declaration</h5>
......
......@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width">
<meta name="title" content="Class ProxyHttpException
| Titanium Web Proxy ">
<meta name="generator" content="docfx 2.35.2.0">
<meta name="generator" content="docfx 2.35.4.0">
<link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css">
......
......@@ -10,7 +10,7 @@
<meta name="viewport" content="width=device-width">
<meta name="title" content="Namespace Titanium.Web.Proxy.Exceptions
| Titanium Web Proxy ">
<meta name="generator" content="docfx 2.35.2.0">
<meta name="generator" content="docfx 2.35.4.0">
<link rel="shortcut icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles/docfx.vendor.css">
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment