Commit 31edf6dd authored by Rob Garrett's avatar Rob Garrett

Resharper cleanup

parent 2b3f9ffe
using System;
using System.Linq;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
......@@ -26,11 +25,13 @@ namespace Titanium.Web.Proxy
//if user callback is registered then do it
if (ServerCertificateValidationCallback != null)
{
var args = new CertificateValidationEventArgs();
var args = new CertificateValidationEventArgs
{
Certificate = certificate,
Chain = chain,
SslPolicyErrors = sslPolicyErrors
};
args.Certificate = certificate;
args.Chain = chain;
args.SslPolicyErrors = sslPolicyErrors;
Delegate[] invocationList = ServerCertificateValidationCallback.GetInvocationList();
......@@ -73,7 +74,6 @@ namespace Titanium.Web.Proxy
string[] acceptableIssuers)
{
X509Certificate clientCertificate = null;
var customSslStream = sender as SslStream;
if (acceptableIssuers != null &&
acceptableIssuers.Length > 0 &&
......@@ -100,13 +100,15 @@ namespace Titanium.Web.Proxy
//If user call back is registered
if (ClientCertificateSelectionCallback != null)
{
var args = new CertificateSelectionEventArgs();
var args = new CertificateSelectionEventArgs
{
TargetHost = targetHost,
LocalCertificates = localCertificates,
RemoteCertificate = remoteCertificate,
AcceptableIssuers = acceptableIssuers,
ClientCertificate = clientCertificate
};
args.TargetHost = targetHost;
args.LocalCertificates = localCertificates;
args.RemoteCertificate = remoteCertificate;
args.AcceptableIssuers = acceptableIssuers;
args.ClientCertificate = clientCertificate;
Delegate[] invocationList = ClientCertificateSelectionCallback.GetInvocationList();
Task[] handlerTasks = new Task[invocationList.Length];
......
using System.IO;
using System.IO.Compression;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Decompression
{
......
......@@ -8,12 +8,29 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary>
public class CertificateSelectionEventArgs : EventArgs
{
/// <summary>
/// Sender object.
/// </summary>
public object Sender { get; internal set; }
/// <summary>
/// Target host.
/// </summary>
public string TargetHost { get; internal set; }
/// <summary>
/// Local certificates.
/// </summary>
public X509CertificateCollection LocalCertificates { get; internal set; }
/// <summary>
/// Remote certificate.
/// </summary>
public X509Certificate RemoteCertificate { get; internal set; }
/// <summary>
/// Acceptable issuers.
/// </summary>
public string[] AcceptableIssuers { get; internal set; }
/// <summary>
/// Client Certificate.
/// </summary>
public X509Certificate ClientCertificate { get; set; }
}
......
......@@ -7,17 +7,24 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary>
/// An argument passed on to the user for validating the server certificate during SSL authentication
/// </summary>
public class CertificateValidationEventArgs : EventArgs, IDisposable
public class CertificateValidationEventArgs : EventArgs
{
/// <summary>
/// Certificate
/// </summary>
public X509Certificate Certificate { get; internal set; }
/// <summary>
/// Certificate chain
/// </summary>
public X509Chain Chain { get; internal set; }
/// <summary>
/// SSL policy errors.
/// </summary>
public SslPolicyErrors SslPolicyErrors { get; internal set; }
/// <summary>
/// is a valid certificate?
/// </summary>
public bool IsValid { get; set; }
public void Dispose()
{
}
}
}
......@@ -44,7 +44,9 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary>
public Guid Id => WebSession.RequestId;
//Should we send a rerequest
/// <summary>
/// Should we send a rerequest
/// </summary>
public bool ReRequest
{
get;
......@@ -56,7 +58,9 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary>
public bool IsHttps => WebSession.Request.RequestUri.Scheme == Uri.UriSchemeHttps;
/// <summary>
/// Client End Point.
/// </summary>
public IPEndPoint ClientEndPoint => (IPEndPoint)ProxyClient.TcpClient.Client.RemoteEndPoint;
/// <summary>
......@@ -65,8 +69,14 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary>
public HttpWebClient WebSession { get; set; }
/// <summary>
/// Are we using a custom upstream HTTP proxy?
/// </summary>
public ExternalProxy CustomUpStreamHttpProxyUsed { get; set; }
/// <summary>
/// Are we using a custom upstream HTTPS proxy?
/// </summary>
public ExternalProxy CustomUpStreamHttpsProxyUsed { get; set; }
/// <summary>
......@@ -105,7 +115,7 @@ namespace Titanium.Web.Proxy.EventArguments
//For chunked request we need to read data as they arrive, until we reach a chunk end symbol
if (WebSession.Request.IsChunked)
{
await this.ProxyClient.ClientStreamReader.CopyBytesToStreamChunked(bufferSize, requestBodyStream);
await ProxyClient.ClientStreamReader.CopyBytesToStreamChunked(bufferSize, requestBodyStream);
}
else
{
......@@ -113,7 +123,7 @@ namespace Titanium.Web.Proxy.EventArguments
if (WebSession.Request.ContentLength > 0)
{
//If not chunked then its easy just read the amount of bytes mentioned in content length header of response
await this.ProxyClient.ClientStreamReader.CopyBytesToStream(bufferSize, requestBodyStream,
await ProxyClient.ClientStreamReader.CopyBytesToStream(bufferSize, requestBodyStream,
WebSession.Request.ContentLength);
}
......@@ -431,7 +441,7 @@ namespace Titanium.Web.Proxy.EventArguments
            await GenericResponse(html, null, status);
}
/// <summary>
/// <summary>
/// Before request is made to server 
/// Respond with the specified HTML string to client
/// and the specified status
......@@ -485,12 +495,17 @@ namespace Titanium.Web.Proxy.EventArguments
WebSession.Request.CancelRequest = true;
}
/// <summary>
/// Redirect to URL.
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public async Task Redirect(string url)
{
var response = new RedirectResponse();
response.HttpVersion = WebSession.Request.HttpVersion;
response.ResponseHeaders.Add("Location", new Models.HttpHeader("Location", url));
response.ResponseHeaders.Add("Location", new HttpHeader("Location", url));
response.ResponseBody = Encoding.ASCII.GetBytes(string.Empty);
await Respond(response);
......
using System;

namespace Titanium.Web.Proxy.Exceptions
{
/// <summary>
......@@ -7,6 +6,10 @@ namespace Titanium.Web.Proxy.Exceptions
/// </summary>
public class BodyNotFoundException : ProxyException
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="message"></param>
public BodyNotFoundException(string message)
: base(message)
{
......
......@@ -2,6 +2,9 @@
namespace Titanium.Web.Proxy.Extensions
{
/// <summary>
/// Extension methods for Byte Arrays.
/// </summary>
public static class ByteArrayExtensions
{
/// <summary>
......@@ -14,7 +17,7 @@ namespace Titanium.Web.Proxy.Extensions
/// <returns></returns>
public static T[] SubArray<T>(this T[] data, int index, int length)
{
T[] result = new T[length];
var result = new T[length];
Array.Copy(data, index, result, 0, length);
return result;
}
......
......@@ -12,11 +12,11 @@ namespace Titanium.Web.Proxy.Extensions
internal static bool IsConnected(this Socket client)
{
// This is how you can determine whether a socket is still connected.
bool blockingState = client.Blocking;
var blockingState = client.Blocking;
try
{
byte[] tmp = new byte[1];
var tmp = new byte[1];
client.Blocking = false;
client.Send(tmp, 0, 0);
......@@ -25,14 +25,7 @@ namespace Titanium.Web.Proxy.Extensions
catch (SocketException e)
{
// 10035 == WSAEWOULDBLOCK
if (e.NativeErrorCode.Equals(10035))
{
return true;
}
else
{
return false;
}
return e.NativeErrorCode.Equals(10035);
}
finally
{
......
......@@ -4,7 +4,6 @@ using System.IO;
using System.Text;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Helpers
{
......@@ -16,15 +15,15 @@ namespace Titanium.Web.Proxy.Helpers
/// </summary>
internal class CustomBinaryReader : IDisposable
{
private Stream stream;
private Encoding encoding;
private readonly Stream stream;
private readonly Encoding encoding;
internal CustomBinaryReader(Stream stream)
{
this.stream = stream;
//default to UTF-8
this.encoding = Encoding.UTF8;
encoding = Encoding.UTF8;
}
internal Stream BaseStream => stream;
......@@ -40,7 +39,7 @@ namespace Titanium.Web.Proxy.Helpers
var lastChar = default(char);
var buffer = new byte[1];
while ((await this.stream.ReadAsync(buffer, 0, 1)) > 0)
while ((await stream.ReadAsync(buffer, 0, 1)) > 0)
{
//if new line
if (lastChar == '\r' && buffer[0] == '\n')
......@@ -82,6 +81,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <summary>
/// Read the specified number of raw bytes from the base stream
/// </summary>
/// <param name="bufferSize"></param>
/// <param name="totalBytesToRead"></param>
/// <returns></returns>
internal async Task<byte[]> ReadBytesAsync(int bufferSize, long totalBytesToRead)
......@@ -98,7 +98,7 @@ namespace Titanium.Web.Proxy.Helpers
using (var outStream = new MemoryStream())
{
while ((bytesRead += await this.stream.ReadAsync(buffer, 0, bytesToRead)) > 0)
while ((bytesRead += await stream.ReadAsync(buffer, 0, bytesToRead)) > 0)
{
await outStream.WriteAsync(buffer, 0, bytesRead);
totalBytesRead += bytesRead;
......
......@@ -8,6 +8,9 @@ namespace Titanium.Web.Proxy.Helpers
/// </summary>
public class FireFoxProxySettingsManager
{
/// <summary>
/// Add Firefox settings.
/// </summary>
public void AddFirefox()
{
try
......@@ -16,28 +19,27 @@ namespace Titanium.Web.Proxy.Helpers
new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) +
"\\Mozilla\\Firefox\\Profiles\\").GetDirectories("*.default");
var myFfPrefFile = myProfileDirectory[0].FullName + "\\prefs.js";
if (File.Exists(myFfPrefFile))
{
if (!File.Exists(myFfPrefFile)) return;
// We have a pref file so let''s make sure it has the proxy setting
var myReader = new StreamReader(myFfPrefFile);
var myPrefContents = myReader.ReadToEnd();
myReader.Close();
if (myPrefContents.Contains("user_pref(\"network.proxy.type\", 0);"))
{
if (!myPrefContents.Contains("user_pref(\"network.proxy.type\", 0);")) return;
// Add the proxy enable line and write it back to the file
myPrefContents = myPrefContents.Replace("user_pref(\"network.proxy.type\", 0);", "");
File.Delete(myFfPrefFile);
File.WriteAllText(myFfPrefFile, myPrefContents);
}
}
}
catch (Exception)
{
// Only exception should be a read/write error because the user opened up FireFox so they can be ignored.
}
}
/// <summary>
/// Remove firefox settings.
/// </summary>
public void RemoveFirefox()
{
try
......@@ -46,8 +48,7 @@ namespace Titanium.Web.Proxy.Helpers
new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) +
"\\Mozilla\\Firefox\\Profiles\\").GetDirectories("*.default");
var myFfPrefFile = myProfileDirectory[0].FullName + "\\prefs.js";
if (File.Exists(myFfPrefFile))
{
if (!File.Exists(myFfPrefFile)) return;
// We have a pref file so let''s make sure it has the proxy setting
var myReader = new StreamReader(myFfPrefFile);
var myPrefContents = myReader.ReadToEnd();
......@@ -61,7 +62,6 @@ namespace Titanium.Web.Proxy.Helpers
File.WriteAllText(myFfPrefFile, myPrefContents);
}
}
}
catch (Exception)
{
// Only exception should be a read/write error because the user opened up FireFox so they can be ignored.
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Helpers
{
......@@ -33,28 +29,14 @@ namespace Titanium.Web.Proxy.Helpers
/// Adapated from below link
/// http://stackoverflow.com/questions/11834091/how-to-check-if-localhost
/// </summary>
/// <param name="address></param>
/// <param name="address"></param>
/// <returns></returns>
internal static bool IsLocalIpAddress(IPAddress address)
{
try
{
// get local IP addresses
IPAddress[] localIPs = Dns.GetHostAddresses(Dns.GetHostName());
var localIPs = Dns.GetHostAddresses(Dns.GetHostName());
// test if any host IP equals to any local IP or to localhost
// is localhost
if (IPAddress.IsLoopback(address)) return true;
// is local address
foreach (IPAddress localIP in localIPs)
{
if (address.Equals(localIP)) return true;
}
}
catch { }
return false;
return IPAddress.IsLoopback(address) || localIPs.Contains(address);
}
}
}
......@@ -4,11 +4,8 @@ using Microsoft.Win32;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
/// <summary>
/// Helper classes for setting system proxy settings
/// </summary>
// Helper classes for setting system proxy settings
namespace Titanium.Web.Proxy.Helpers
{
internal enum ProxyProtocolType
......
......@@ -140,6 +140,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="localCertificateSelectionCallback"></param>
/// <param name="clientStream"></param>
/// <param name="tcpConnectionFactory"></param>
/// <param name="upStreamEndPoint"></param>
/// <returns></returns>
internal static async Task SendRaw(int bufferSize, int connectionTimeOutSeconds,
string remoteHostName, int remotePort, string httpCmd, Version httpVersion, Dictionary<string, HttpHeader> requestHeaders,
......
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network;
using Titanium.Web.Proxy.Network.Tcp;
using Titanium.Web.Proxy.Shared;
......@@ -15,17 +13,26 @@ namespace Titanium.Web.Proxy.Http
/// </summary>
public class HttpWebClient
{
/// <summary>
/// Connection to server
/// </summary>
internal TcpConnection ServerConnection { get; set; }
public Guid RequestId { get; private set; }
/// <summary>
/// Request ID.
/// </summary>
public Guid RequestId { get; }
/// <summary>
/// Headers passed with Connect.
/// </summary>
public List<HttpHeader> ConnectHeaders { get; set; }
/// <summary>
/// Web Request.
/// </summary>
public Request Request { get; set; }
/// <summary>
/// Web Response.
/// </summary>
public Response Response { get; set; }
/// <summary>
......@@ -37,15 +44,14 @@ namespace Titanium.Web.Proxy.Http
/// <summary>
/// Is Https?
/// </summary>
public bool IsHttps => this.Request.RequestUri.Scheme == Uri.UriSchemeHttps;
public bool IsHttps => Request.RequestUri.Scheme == Uri.UriSchemeHttps;
internal HttpWebClient()
{
this.RequestId = Guid.NewGuid();
this.Request = new Request();
this.Response = new Response();
RequestId = Guid.NewGuid();
Request = new Request();
Response = new Response();
}
/// <summary>
......@@ -65,18 +71,18 @@ namespace Titanium.Web.Proxy.Http
/// <returns></returns>
internal async Task SendRequest(bool enable100ContinueBehaviour)
{
Stream stream = ServerConnection.Stream;
var stream = ServerConnection.Stream;
StringBuilder requestLines = new StringBuilder();
var requestLines = new StringBuilder();
//prepare the request & headers
if ((ServerConnection.UpStreamHttpProxy != null && ServerConnection.IsHttps == false) || (ServerConnection.UpStreamHttpsProxy != null && ServerConnection.IsHttps == true))
if ((ServerConnection.UpStreamHttpProxy != null && ServerConnection.IsHttps == false) || (ServerConnection.UpStreamHttpsProxy != null && ServerConnection.IsHttps))
{
requestLines.AppendLine(string.Join(" ", this.Request.Method, this.Request.RequestUri.AbsoluteUri, $"HTTP/{this.Request.HttpVersion.Major}.{this.Request.HttpVersion.Minor}"));
requestLines.AppendLine(string.Join(" ", Request.Method, Request.RequestUri.AbsoluteUri, $"HTTP/{Request.HttpVersion.Major}.{Request.HttpVersion.Minor}"));
}
else
{
requestLines.AppendLine(string.Join(" ", this.Request.Method, this.Request.RequestUri.PathAndQuery, $"HTTP/{this.Request.HttpVersion.Major}.{this.Request.HttpVersion.Minor}"));
requestLines.AppendLine(string.Join(" ", Request.Method, Request.RequestUri.PathAndQuery, $"HTTP/{Request.HttpVersion.Major}.{Request.HttpVersion.Minor}"));
}
//Send Authentication to Upstream proxy if needed
......@@ -86,7 +92,7 @@ namespace Titanium.Web.Proxy.Http
requestLines.AppendLine("Proxy-Authorization" + ": Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(ServerConnection.UpStreamHttpProxy.UserName + ":" + ServerConnection.UpStreamHttpProxy.Password)));
}
//write request headers
foreach (var headerItem in this.Request.RequestHeaders)
foreach (var headerItem in Request.RequestHeaders)
{
var header = headerItem.Value;
if (headerItem.Key != "Proxy-Authorization")
......@@ -96,7 +102,7 @@ namespace Titanium.Web.Proxy.Http
}
//write non unique request headers
foreach (var headerItem in this.Request.NonUniqueRequestHeaders)
foreach (var headerItem in Request.NonUniqueRequestHeaders)
{
var headers = headerItem.Value;
foreach (var header in headers)
......@@ -110,15 +116,15 @@ namespace Titanium.Web.Proxy.Http
requestLines.AppendLine();
string request = requestLines.ToString();
byte[] requestBytes = Encoding.ASCII.GetBytes(request);
var request = requestLines.ToString();
var requestBytes = Encoding.ASCII.GetBytes(request);
await stream.WriteAsync(requestBytes, 0, requestBytes.Length);
await stream.FlushAsync();
if (enable100ContinueBehaviour)
{
if (this.Request.ExpectContinue)
if (Request.ExpectContinue)
{
var httpResult = (await ServerConnection.StreamReader.ReadLineAsync()).Split(ProxyConstants.SpaceSplit, 3);
var responseStatusCode = httpResult[1].Trim();
......@@ -128,13 +134,13 @@ namespace Titanium.Web.Proxy.Http
if (responseStatusCode.Equals("100")
&& responseStatusDescription.ToLower().Equals("continue"))
{
this.Request.Is100Continue = true;
Request.Is100Continue = true;
await ServerConnection.StreamReader.ReadLineAsync();
}
else if (responseStatusCode.Equals("417")
&& responseStatusDescription.ToLower().Equals("expectation failed"))
{
this.Request.ExpectationFailed = true;
Request.ExpectationFailed = true;
await ServerConnection.StreamReader.ReadLineAsync();
}
}
......@@ -148,7 +154,7 @@ namespace Titanium.Web.Proxy.Http
internal async Task ReceiveResponse()
{
//return if this is already read
if (this.Response.ResponseStatusCode != null) return;
if (Response.ResponseStatusCode != null) return;
var httpResult = (await ServerConnection.StreamReader.ReadLineAsync()).Split(ProxyConstants.SpaceSplit, 3);
......@@ -161,33 +167,33 @@ namespace Titanium.Web.Proxy.Http
var httpVersion = httpResult[0].Trim().ToLower();
var version = new Version(1, 1);
if (httpVersion == "http/1.0")
if (0 == string.CompareOrdinal(httpVersion, "http/1.0"))
{
version = new Version(1, 0);
}
this.Response.HttpVersion = version;
this.Response.ResponseStatusCode = httpResult[1].Trim();
this.Response.ResponseStatusDescription = httpResult[2].Trim();
Response.HttpVersion = version;
Response.ResponseStatusCode = httpResult[1].Trim();
Response.ResponseStatusDescription = httpResult[2].Trim();
//For HTTP 1.1 comptibility server may send expect-continue even if not asked for it in request
if (this.Response.ResponseStatusCode.Equals("100")
&& this.Response.ResponseStatusDescription.ToLower().Equals("continue"))
if (Response.ResponseStatusCode.Equals("100")
&& Response.ResponseStatusDescription.ToLower().Equals("continue"))
{
//Read the next line after 100-continue
this.Response.Is100Continue = true;
this.Response.ResponseStatusCode = null;
Response.Is100Continue = true;
Response.ResponseStatusCode = null;
await ServerConnection.StreamReader.ReadLineAsync();
//now receive response
await ReceiveResponse();
return;
}
else if (this.Response.ResponseStatusCode.Equals("417")
&& this.Response.ResponseStatusDescription.ToLower().Equals("expectation failed"))
else if (Response.ResponseStatusCode.Equals("417")
&& Response.ResponseStatusDescription.ToLower().Equals("expectation failed"))
{
//read next line after expectation failed response
this.Response.ExpectationFailed = true;
this.Response.ResponseStatusCode = null;
Response.ExpectationFailed = true;
Response.ResponseStatusCode = null;
await ServerConnection.StreamReader.ReadLineAsync();
//now receive response
await ReceiveResponse();
......
......@@ -34,12 +34,7 @@ namespace Titanium.Web.Proxy.Http
get
{
var hasHeader = RequestHeaders.ContainsKey("host");
if (hasHeader)
{
return RequestHeaders["host"].Value;
}
return null;
return hasHeader ? RequestHeaders["host"].Value : null;
}
set
{
......@@ -154,11 +149,11 @@ namespace Titanium.Web.Proxy.Http
if (hasHeader)
{
var header = RequestHeaders["content-type"];
header.Value = value.ToString();
header.Value = value;
}
else
{
RequestHeaders.Add("content-type", new HttpHeader("content-type", value.ToString()));
RequestHeaders.Add("content-type", new HttpHeader("content-type", value));
}
}
......@@ -219,15 +214,11 @@ namespace Titanium.Web.Proxy.Http
{
var hasHeader = RequestHeaders.ContainsKey("expect");
if (hasHeader)
{
if (!hasHeader) return false;
var header = RequestHeaders["expect"];
return header.Value.Equals("100-continue");
}
return false;
}
}
/// <summary>
......@@ -275,13 +266,7 @@ namespace Titanium.Web.Proxy.Http
var header = RequestHeaders["upgrade"];
if (header.Value.ToLower() == "websocket")
{
return true;
}
return false;
return header.Value.ToLower() == "websocket";
}
}
......@@ -305,6 +290,9 @@ namespace Titanium.Web.Proxy.Http
/// </summary>
public bool ExpectationFailed { get; internal set; }
/// <summary>
/// Constructor.
/// </summary>
public Request()
{
RequestHeaders = new Dictionary<string, HttpHeader>(StringComparer.OrdinalIgnoreCase);
......
......@@ -12,7 +12,13 @@ namespace Titanium.Web.Proxy.Http
/// </summary>
public class Response
{
/// <summary>
/// Response Status Code.
/// </summary>
public string ResponseStatusCode { get; set; }
/// <summary>
/// Response Status description.
/// </summary>
public string ResponseStatusDescription { get; set; }
internal Encoding Encoding => this.GetResponseCharacterEncoding();
......@@ -26,15 +32,11 @@ namespace Titanium.Web.Proxy.Http
{
var hasHeader = ResponseHeaders.ContainsKey("content-encoding");
if (hasHeader)
{
if (!hasHeader) return null;
var header = ResponseHeaders["content-encoding"];
return header.Value.Trim();
}
return null;
}
}
internal Version HttpVersion { get; set; }
......@@ -229,10 +231,13 @@ namespace Titanium.Web.Proxy.Http
/// </summary>
public bool ExpectationFailed { get; internal set; }
/// <summary>
/// Constructor.
/// </summary>
public Response()
{
this.ResponseHeaders = new Dictionary<string, HttpHeader>(StringComparer.OrdinalIgnoreCase);
this.NonUniqueResponseHeaders = new Dictionary<string, List<HttpHeader>>(StringComparer.OrdinalIgnoreCase);
ResponseHeaders = new Dictionary<string, HttpHeader>(StringComparer.OrdinalIgnoreCase);
NonUniqueResponseHeaders = new Dictionary<string, List<HttpHeader>>(StringComparer.OrdinalIgnoreCase);
}
}
......
......@@ -7,16 +7,25 @@ namespace Titanium.Web.Proxy.Http.Responses
    /// </summary>
    public class GenericResponse : Response
    {
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="status"></param>
        public GenericResponse(HttpStatusCode status)
        {
            ResponseStatusCode = ((int)status).ToString();
            ResponseStatusDescription = status.ToString(); 
        }
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="statusCode"></param>
        /// <param name="statusDescription"></param>
        public GenericResponse(string statusCode, string statusDescription)
        {
            ResponseStatusCode = statusCode;
            ResponseStatusDescription = statusCode;
            ResponseStatusDescription = statusDescription;
        }
    }
}
......@@ -5,6 +5,9 @@
/// </summary>
public sealed class OkResponse : Response
{
/// <summary>
/// Constructor.
/// </summary>
public OkResponse()
{
ResponseStatusCode = "200";
......
......@@ -5,6 +5,9 @@
/// </summary>
public sealed class RedirectResponse : Response
{
/// <summary>
/// Constructor.
/// </summary>
public RedirectResponse()
{
ResponseStatusCode = "302";
......
......@@ -10,20 +10,38 @@ namespace Titanium.Web.Proxy.Models
/// </summary>
public abstract class ProxyEndPoint
{
public ProxyEndPoint(IPAddress IpAddress, int Port, bool EnableSsl)
/// <summary>
/// Constructor.
/// </summary>
/// <param name="IpAddress"></param>
/// <param name="Port"></param>
/// <param name="EnableSsl"></param>
protected ProxyEndPoint(IPAddress IpAddress, int Port, bool EnableSsl)
{
this.IpAddress = IpAddress;
this.Port = Port;
this.EnableSsl = EnableSsl;
}
/// <summary>
/// Ip Address.
/// </summary>
public IPAddress IpAddress { get; internal set; }
/// <summary>
/// Port.
/// </summary>
public int Port { get; internal set; }
/// <summary>
/// Enable SSL?
/// </summary>
public bool EnableSsl { get; internal set; }
public bool IpV6Enabled => IpAddress == IPAddress.IPv6Any
|| IpAddress == IPAddress.IPv6Loopback
|| IpAddress == IPAddress.IPv6None;
/// <summary>
/// Is IPv6 enabled?
/// </summary>
public bool IpV6Enabled => Equals(IpAddress, IPAddress.IPv6Any)
|| Equals(IpAddress, IPAddress.IPv6Loopback)
|| Equals(IpAddress, IPAddress.IPv6None);
internal TcpListener listener { get; set; }
}
......@@ -37,10 +55,22 @@ namespace Titanium.Web.Proxy.Models
internal bool IsSystemHttpProxy { get; set; }
internal bool IsSystemHttpsProxy { get; set; }
/// <summary>
/// List of host names to exclude using Regular Expressions.
/// </summary>
public List<string> ExcludedHttpsHostNameRegex { get; set; }
/// <summary>
/// Generic certificate to use for SSL decryption.
/// </summary>
public X509Certificate2 GenericCertificate { get; set; }
/// <summary>
/// Constructor.
/// </summary>
/// <param name="IpAddress"></param>
/// <param name="Port"></param>
/// <param name="EnableSsl"></param>
public ExplicitProxyEndPoint(IPAddress IpAddress, int Port, bool EnableSsl)
: base(IpAddress, Port, EnableSsl)
{
......@@ -54,13 +84,19 @@ namespace Titanium.Web.Proxy.Models
/// </summary>
public class TransparentProxyEndPoint : ProxyEndPoint
{
//Name of the Certificate need to be sent (same as the hostname we want to proxy)
//This is valid only when UseServerNameIndication is set to false
public string GenericCertificateName { get; set; }
// public bool UseServerNameIndication { get; set; }
/// <summary>
/// Name of the Certificate need to be sent (same as the hostname we want to proxy)
/// This is valid only when UseServerNameIndication is set to false
/// </summary>
public string GenericCertificateName { get; set; }
/// <summary>
/// Constructor.
/// </summary>
/// <param name="IpAddress"></param>
/// <param name="Port"></param>
/// <param name="EnableSsl"></param>
public TransparentProxyEndPoint(IPAddress IpAddress, int Port, bool EnableSsl)
: base(IpAddress, Port, EnableSsl)
{
......
......@@ -13,10 +13,16 @@ namespace Titanium.Web.Proxy.Models
private string userName;
private string password;
/// <summary>
/// Use default windows credentials?
/// </summary>
public bool UseDefaultCredentials { get; set; }
/// <summary>
/// Username.
/// </summary>
public string UserName {
get { return UseDefaultCredentials ? DefaultCredentials.Value.UserName : userName; }
get => UseDefaultCredentials ? DefaultCredentials.Value.UserName : userName;
set
{
userName = value;
......@@ -28,9 +34,12 @@ namespace Titanium.Web.Proxy.Models
}
}
/// <summary>
/// Password.
/// </summary>
public string Password
{
get { return UseDefaultCredentials ? DefaultCredentials.Value.Password : password; }
get => UseDefaultCredentials ? DefaultCredentials.Value.Password : password;
set
{
password = value;
......@@ -42,7 +51,13 @@ namespace Titanium.Web.Proxy.Models
}
}
/// <summary>
/// Host name.
/// </summary>
public string HostName { get; set; }
/// <summary>
/// Port.
/// </summary>
public int Port { get; set; }
}
}
......@@ -7,6 +7,12 @@ namespace Titanium.Web.Proxy.Models
/// </summary>
public class HttpHeader
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="name"></param>
/// <param name="value"></param>
/// <exception cref="Exception"></exception>
public HttpHeader(string name, string value)
{
if (string.IsNullOrEmpty(name))
......@@ -18,7 +24,13 @@ namespace Titanium.Web.Proxy.Models
Value = value.Trim();
}
/// <summary>
/// Header Name.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Header Value.
/// </summary>
public string Value { get; set; }
/// <summary>
......@@ -27,7 +39,7 @@ namespace Titanium.Web.Proxy.Models
/// <returns></returns>
public override string ToString()
{
return string.Format("{0}: {1}", Name, Value);
return $"{Name}: {Value}";
}
}
}
\ No newline at end of file
......@@ -6,187 +6,197 @@ using System.Threading;
namespace Titanium.Web.Proxy.Network
{
/// <summary>
/// Certificate Maker - uses MakeCert
/// </summary>
public class CertificateMaker
{
private Type typeX500DN;
private readonly Type typeX500DN;
private Type typeX509PrivateKey;
private readonly Type typeX509PrivateKey;
private Type typeOID;
private readonly Type typeOID;
private Type typeOIDS;
private readonly Type typeOIDS;
private Type typeKUExt;
private readonly Type typeKUExt;
private Type typeEKUExt;
private readonly Type typeEKUExt;
private Type typeRequestCert;
private readonly Type typeRequestCert;
private Type typeX509Extensions;
private readonly Type typeX509Extensions;
private Type typeBasicConstraints;
private readonly Type typeBasicConstraints;
private Type typeSignerCertificate;
private readonly Type typeSignerCertificate;
private Type typeX509Enrollment;
private readonly Type typeX509Enrollment;
private Type typeAlternativeName;
//private Type typeAlternativeName;
private Type typeAlternativeNames;
//private Type typeAlternativeNames;
private Type typeAlternativeNamesExt;
//private Type typeAlternativeNamesExt;
private string sProviderName = "Microsoft Enhanced Cryptographic Provider v1.0";
private readonly string sProviderName = "Microsoft Enhanced Cryptographic Provider v1.0";
private object _SharedPrivateKey;
/// <summary>
/// Constructor.
/// </summary>
public CertificateMaker()
{
this.typeX500DN = Type.GetTypeFromProgID("X509Enrollment.CX500DistinguishedName", true);
this.typeX509PrivateKey = Type.GetTypeFromProgID("X509Enrollment.CX509PrivateKey", true);
this.typeOID = Type.GetTypeFromProgID("X509Enrollment.CObjectId", true);
this.typeOIDS = Type.GetTypeFromProgID("X509Enrollment.CObjectIds.1", true);
this.typeEKUExt = Type.GetTypeFromProgID("X509Enrollment.CX509ExtensionEnhancedKeyUsage");
this.typeKUExt = Type.GetTypeFromProgID("X509Enrollment.CX509ExtensionKeyUsage");
this.typeRequestCert = Type.GetTypeFromProgID("X509Enrollment.CX509CertificateRequestCertificate");
this.typeX509Extensions = Type.GetTypeFromProgID("X509Enrollment.CX509Extensions");
this.typeBasicConstraints = Type.GetTypeFromProgID("X509Enrollment.CX509ExtensionBasicConstraints");
this.typeSignerCertificate = Type.GetTypeFromProgID("X509Enrollment.CSignerCertificate");
this.typeX509Enrollment = Type.GetTypeFromProgID("X509Enrollment.CX509Enrollment");
this.typeAlternativeName = Type.GetTypeFromProgID("X509Enrollment.CAlternativeName");
this.typeAlternativeNames = Type.GetTypeFromProgID("X509Enrollment.CAlternativeNames");
this.typeAlternativeNamesExt = Type.GetTypeFromProgID("X509Enrollment.CX509ExtensionAlternativeNames");
}
typeX500DN = Type.GetTypeFromProgID("X509Enrollment.CX500DistinguishedName", true);
typeX509PrivateKey = Type.GetTypeFromProgID("X509Enrollment.CX509PrivateKey", true);
typeOID = Type.GetTypeFromProgID("X509Enrollment.CObjectId", true);
typeOIDS = Type.GetTypeFromProgID("X509Enrollment.CObjectIds.1", true);
typeEKUExt = Type.GetTypeFromProgID("X509Enrollment.CX509ExtensionEnhancedKeyUsage");
typeKUExt = Type.GetTypeFromProgID("X509Enrollment.CX509ExtensionKeyUsage");
typeRequestCert = Type.GetTypeFromProgID("X509Enrollment.CX509CertificateRequestCertificate");
typeX509Extensions = Type.GetTypeFromProgID("X509Enrollment.CX509Extensions");
typeBasicConstraints = Type.GetTypeFromProgID("X509Enrollment.CX509ExtensionBasicConstraints");
typeSignerCertificate = Type.GetTypeFromProgID("X509Enrollment.CSignerCertificate");
typeX509Enrollment = Type.GetTypeFromProgID("X509Enrollment.CX509Enrollment");
//this.typeAlternativeName = Type.GetTypeFromProgID("X509Enrollment.CAlternativeName");
//this.typeAlternativeNames = Type.GetTypeFromProgID("X509Enrollment.CAlternativeNames");
//this.typeAlternativeNamesExt = Type.GetTypeFromProgID("X509Enrollment.CX509ExtensionAlternativeNames");
}
/// <summary>
/// Make certificate.
/// </summary>
/// <param name="sSubjectCN"></param>
/// <param name="isRoot"></param>
/// <param name="signingCert"></param>
/// <returns></returns>
public X509Certificate2 MakeCertificate(string sSubjectCN, bool isRoot,X509Certificate2 signingCert=null)
{
return this.MakeCertificateInternal(sSubjectCN, isRoot, true, signingCert);
return MakeCertificateInternal(sSubjectCN, isRoot, true, signingCert);
}
private X509Certificate2 MakeCertificate(bool IsRoot, string SubjectCN, string FullSubject, int PrivateKeyLength, string HashAlg, DateTime ValidFrom, DateTime ValidTo, X509Certificate2 SigningCertificate)
private X509Certificate2 MakeCertificate(bool IsRoot, string FullSubject, int PrivateKeyLength, string HashAlg, DateTime ValidFrom, DateTime ValidTo, X509Certificate2 SigningCertificate)
{
X509Certificate2 cert;
if (IsRoot != (null == SigningCertificate))
{
throw new ArgumentException("You must specify a Signing Certificate if and only if you are not creating a root.", "oSigningCertificate");
throw new ArgumentException("You must specify a Signing Certificate if and only if you are not creating a root.", nameof(IsRoot));
}
object x500DN = Activator.CreateInstance(this.typeX500DN);
object[] subject = new object[] { FullSubject, 0 };
this.typeX500DN.InvokeMember("Encode", BindingFlags.InvokeMethod, null, x500DN, subject);
object x500DN2 = Activator.CreateInstance(this.typeX500DN);
var x500DN = Activator.CreateInstance(typeX500DN);
var subject = new object[] { FullSubject, 0 };
typeX500DN.InvokeMember("Encode", BindingFlags.InvokeMethod, null, x500DN, subject);
var x500DN2 = Activator.CreateInstance(typeX500DN);
if (!IsRoot)
{
subject[0] = SigningCertificate.Subject;
}
this.typeX500DN.InvokeMember("Encode", BindingFlags.InvokeMethod, null, x500DN2, subject);
typeX500DN.InvokeMember("Encode", BindingFlags.InvokeMethod, null, x500DN2, subject);
object sharedPrivateKey = null;
if (!IsRoot)
{
sharedPrivateKey = this._SharedPrivateKey;
sharedPrivateKey = _SharedPrivateKey;
}
if (sharedPrivateKey == null)
{
sharedPrivateKey = Activator.CreateInstance(this.typeX509PrivateKey);
subject = new object[] { this.sProviderName };
this.typeX509PrivateKey.InvokeMember("ProviderName", BindingFlags.PutDispProperty, null, sharedPrivateKey, subject);
sharedPrivateKey = Activator.CreateInstance(typeX509PrivateKey);
subject = new object[] { sProviderName };
typeX509PrivateKey.InvokeMember("ProviderName", BindingFlags.PutDispProperty, null, sharedPrivateKey, subject);
subject[0] = 2;
this.typeX509PrivateKey.InvokeMember("ExportPolicy", BindingFlags.PutDispProperty, null, sharedPrivateKey, subject);
typeX509PrivateKey.InvokeMember("ExportPolicy", BindingFlags.PutDispProperty, null, sharedPrivateKey, subject);
subject = new object[] { (IsRoot ? 2 : 1) };
this.typeX509PrivateKey.InvokeMember("KeySpec", BindingFlags.PutDispProperty, null, sharedPrivateKey, subject);
typeX509PrivateKey.InvokeMember("KeySpec", BindingFlags.PutDispProperty, null, sharedPrivateKey, subject);
if (!IsRoot)
{
subject = new object[] { 176 };
this.typeX509PrivateKey.InvokeMember("KeyUsage", BindingFlags.PutDispProperty, null, sharedPrivateKey, subject);
typeX509PrivateKey.InvokeMember("KeyUsage", BindingFlags.PutDispProperty, null, sharedPrivateKey, subject);
}
subject[0] = PrivateKeyLength;
this.typeX509PrivateKey.InvokeMember("Length", BindingFlags.PutDispProperty, null, sharedPrivateKey, subject);
this.typeX509PrivateKey.InvokeMember("Create", BindingFlags.InvokeMethod, null, sharedPrivateKey, null);
typeX509PrivateKey.InvokeMember("Length", BindingFlags.PutDispProperty, null, sharedPrivateKey, subject);
typeX509PrivateKey.InvokeMember("Create", BindingFlags.InvokeMethod, null, sharedPrivateKey, null);
if (!IsRoot)
{
this._SharedPrivateKey = sharedPrivateKey;
_SharedPrivateKey = sharedPrivateKey;
}
}
subject = new object[1];
object obj3 = Activator.CreateInstance(this.typeOID);
var obj3 = Activator.CreateInstance(typeOID);
subject[0] = "1.3.6.1.5.5.7.3.1";
this.typeOID.InvokeMember("InitializeFromValue", BindingFlags.InvokeMethod, null, obj3, subject);
object obj4 = Activator.CreateInstance(this.typeOIDS);
typeOID.InvokeMember("InitializeFromValue", BindingFlags.InvokeMethod, null, obj3, subject);
var obj4 = Activator.CreateInstance(typeOIDS);
subject[0] = obj3;
this.typeOIDS.InvokeMember("Add", BindingFlags.InvokeMethod, null, obj4, subject);
object obj5 = Activator.CreateInstance(this.typeEKUExt);
typeOIDS.InvokeMember("Add", BindingFlags.InvokeMethod, null, obj4, subject);
var obj5 = Activator.CreateInstance(typeEKUExt);
subject[0] = obj4;
this.typeEKUExt.InvokeMember("InitializeEncode", BindingFlags.InvokeMethod, null, obj5, subject);
object obj6 = Activator.CreateInstance(this.typeRequestCert);
subject = new object[] { 1, sharedPrivateKey, string.Empty };
this.typeRequestCert.InvokeMember("InitializeFromPrivateKey", BindingFlags.InvokeMethod, null, obj6, subject);
subject = new object[] { x500DN };
this.typeRequestCert.InvokeMember("Subject", BindingFlags.PutDispProperty, null, obj6, subject);
typeEKUExt.InvokeMember("InitializeEncode", BindingFlags.InvokeMethod, null, obj5, subject);
var obj6 = Activator.CreateInstance(typeRequestCert);
subject = new[] { 1, sharedPrivateKey, string.Empty };
typeRequestCert.InvokeMember("InitializeFromPrivateKey", BindingFlags.InvokeMethod, null, obj6, subject);
subject = new[] { x500DN };
typeRequestCert.InvokeMember("Subject", BindingFlags.PutDispProperty, null, obj6, subject);
subject[0] = x500DN;
this.typeRequestCert.InvokeMember("Issuer", BindingFlags.PutDispProperty, null, obj6, subject);
typeRequestCert.InvokeMember("Issuer", BindingFlags.PutDispProperty, null, obj6, subject);
subject[0] = ValidFrom;
this.typeRequestCert.InvokeMember("NotBefore", BindingFlags.PutDispProperty, null, obj6, subject);
typeRequestCert.InvokeMember("NotBefore", BindingFlags.PutDispProperty, null, obj6, subject);
subject[0] = ValidTo;
this.typeRequestCert.InvokeMember("NotAfter", BindingFlags.PutDispProperty, null, obj6, subject);
object obj7 = Activator.CreateInstance(this.typeKUExt);
typeRequestCert.InvokeMember("NotAfter", BindingFlags.PutDispProperty, null, obj6, subject);
var obj7 = Activator.CreateInstance(typeKUExt);
subject[0] = 176;
this.typeKUExt.InvokeMember("InitializeEncode", BindingFlags.InvokeMethod, null, obj7, subject);
object obj8 = this.typeRequestCert.InvokeMember("X509Extensions", BindingFlags.GetProperty, null, obj6, null);
typeKUExt.InvokeMember("InitializeEncode", BindingFlags.InvokeMethod, null, obj7, subject);
var obj8 = typeRequestCert.InvokeMember("X509Extensions", BindingFlags.GetProperty, null, obj6, null);
subject = new object[1];
if (!IsRoot)
{
subject[0] = obj7;
this.typeX509Extensions.InvokeMember("Add", BindingFlags.InvokeMethod, null, obj8, subject);
typeX509Extensions.InvokeMember("Add", BindingFlags.InvokeMethod, null, obj8, subject);
}
subject[0] = obj5;
this.typeX509Extensions.InvokeMember("Add", BindingFlags.InvokeMethod, null, obj8, subject);
typeX509Extensions.InvokeMember("Add", BindingFlags.InvokeMethod, null, obj8, subject);
if (!IsRoot)
{
object obj12 = Activator.CreateInstance(this.typeSignerCertificate);
var obj12 = Activator.CreateInstance(typeSignerCertificate);
subject = new object[] { 0, 0, 12, SigningCertificate.Thumbprint };
this.typeSignerCertificate.InvokeMember("Initialize", BindingFlags.InvokeMethod, null, obj12, subject);
subject = new object[] { obj12 };
this.typeRequestCert.InvokeMember("SignerCertificate", BindingFlags.PutDispProperty, null, obj6, subject);
typeSignerCertificate.InvokeMember("Initialize", BindingFlags.InvokeMethod, null, obj12, subject);
subject = new[] { obj12 };
typeRequestCert.InvokeMember("SignerCertificate", BindingFlags.PutDispProperty, null, obj6, subject);
}
else
{
object obj13 = Activator.CreateInstance(this.typeBasicConstraints);
var obj13 = Activator.CreateInstance(typeBasicConstraints);
subject = new object[] { "true", "0" };
this.typeBasicConstraints.InvokeMember("InitializeEncode", BindingFlags.InvokeMethod, null, obj13, subject);
subject = new object[] { obj13 };
this.typeX509Extensions.InvokeMember("Add", BindingFlags.InvokeMethod, null, obj8, subject);
typeBasicConstraints.InvokeMember("InitializeEncode", BindingFlags.InvokeMethod, null, obj13, subject);
subject = new[] { obj13 };
typeX509Extensions.InvokeMember("Add", BindingFlags.InvokeMethod, null, obj8, subject);
}
object obj14 = Activator.CreateInstance(this.typeOID);
var obj14 = Activator.CreateInstance(typeOID);
subject = new object[] { 1, 0, 0, HashAlg };
this.typeOID.InvokeMember("InitializeFromAlgorithmName", BindingFlags.InvokeMethod, null, obj14, subject);
subject = new object[] { obj14 };
this.typeRequestCert.InvokeMember("HashAlgorithm", BindingFlags.PutDispProperty, null, obj6, subject);
this.typeRequestCert.InvokeMember("Encode", BindingFlags.InvokeMethod, null, obj6, null);
object obj15 = Activator.CreateInstance(this.typeX509Enrollment);
typeOID.InvokeMember("InitializeFromAlgorithmName", BindingFlags.InvokeMethod, null, obj14, subject);
subject = new[] { obj14 };
typeRequestCert.InvokeMember("HashAlgorithm", BindingFlags.PutDispProperty, null, obj6, subject);
typeRequestCert.InvokeMember("Encode", BindingFlags.InvokeMethod, null, obj6, null);
var obj15 = Activator.CreateInstance(typeX509Enrollment);
subject[0] = obj6;
this.typeX509Enrollment.InvokeMember("InitializeFromRequest", BindingFlags.InvokeMethod, null, obj15, subject);
typeX509Enrollment.InvokeMember("InitializeFromRequest", BindingFlags.InvokeMethod, null, obj15, subject);
if (IsRoot)
{
subject[0] = "DO_NOT_TRUST_TitaniumProxy-CE";
this.typeX509Enrollment.InvokeMember("CertificateFriendlyName", BindingFlags.PutDispProperty, null, obj15, subject);
typeX509Enrollment.InvokeMember("CertificateFriendlyName", BindingFlags.PutDispProperty, null, obj15, subject);
}
subject[0] = 0;
object obj16 = this.typeX509Enrollment.InvokeMember("CreateRequest", BindingFlags.InvokeMethod, null, obj15, subject);
subject = new object[] { 2, obj16, 0, string.Empty };
this.typeX509Enrollment.InvokeMember("InstallResponse", BindingFlags.InvokeMethod, null, obj15, subject);
var obj16 = typeX509Enrollment.InvokeMember("CreateRequest", BindingFlags.InvokeMethod, null, obj15, subject);
subject = new[] { 2, obj16, 0, string.Empty };
typeX509Enrollment.InvokeMember("InstallResponse", BindingFlags.InvokeMethod, null, obj15, subject);
subject = new object[] { null, 0, 1 };
string empty = string.Empty;
try
{
empty = (string)this.typeX509Enrollment.InvokeMember("CreatePFX", BindingFlags.InvokeMethod, null, obj15, subject);
var empty = (string)typeX509Enrollment.InvokeMember("CreatePFX", BindingFlags.InvokeMethod, null, obj15, subject);
return new X509Certificate2(Convert.FromBase64String(empty), string.Empty, X509KeyStorageFlags.Exportable);
}
catch (Exception exception1)
catch (Exception)
{
Exception exception = exception1;
cert = null;
// ignored
}
return cert;
return null;
}
private X509Certificate2 MakeCertificateInternal(string sSubjectCN, bool isRoot, bool switchToMTAIfNeeded,X509Certificate2 signingCert=null)
......@@ -194,39 +204,26 @@ namespace Titanium.Web.Proxy.Network
X509Certificate2 rCert=null;
if (switchToMTAIfNeeded && Thread.CurrentThread.GetApartmentState() != ApartmentState.MTA)
{
ManualResetEvent manualResetEvent = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem((object o) =>
var manualResetEvent = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem(o =>
{
rCert = this.MakeCertificateInternal(sSubjectCN, isRoot, false,signingCert);
rCert = MakeCertificateInternal(sSubjectCN, isRoot, false,signingCert);
manualResetEvent.Set();
});
manualResetEvent.WaitOne();
manualResetEvent.Close();
return rCert;
}
string fullSubject = string.Format("CN={0}{1}", sSubjectCN, "");//Subject
string HashAlgo = "SHA256"; //Sig Algo
int GraceDays = -366; //Grace Days
int ValidDays = 1825; //ValiDays
int keyLength = 2048; //KeyLength
var fullSubject = $"CN={sSubjectCN}";//Subject
var HashAlgo = "SHA256"; //Sig Algo
var GraceDays = -366; //Grace Days
var ValidDays = 1825; //ValiDays
var keyLength = 2048; //KeyLength
DateTime graceTime = DateTime.Now.AddDays((double)GraceDays);
DateTime now = DateTime.Now;
try
{
if (!isRoot)
{
rCert = this.MakeCertificate(false, sSubjectCN, fullSubject, keyLength, HashAlgo, graceTime, now.AddDays((double)ValidDays), signingCert);
}
else
{
rCert = this.MakeCertificate(true, sSubjectCN, fullSubject, keyLength, HashAlgo, graceTime, now.AddDays((double)ValidDays), null);
}
}
catch (Exception e)
{
throw e;
}
var graceTime = DateTime.Now.AddDays(GraceDays);
var now = DateTime.Now;
rCert = !isRoot ? MakeCertificate(false, fullSubject, keyLength, HashAlgo, graceTime, now.AddDays(ValidDays), signingCert) :
MakeCertificate(true, fullSubject, keyLength, HashAlgo, graceTime, now.AddDays(ValidDays), null);
return rCert;
}
}
......
......@@ -13,7 +13,7 @@ namespace Titanium.Web.Proxy.Network
/// </summary>
internal class CertificateManager : IDisposable
{
private CertificateMaker certEngine = null;
private readonly CertificateMaker certEngine;
private bool clearCertificates { get; set; }
/// <summary>
......@@ -21,10 +21,10 @@ namespace Titanium.Web.Proxy.Network
/// </summary>
private readonly IDictionary<string, CachedCertificate> certificateCache;
private Action<Exception> exceptionFunc;
private readonly Action<Exception> exceptionFunc;
internal string Issuer { get; private set; }
internal string RootCertificateName { get; private set; }
internal string Issuer { get; }
internal string RootCertificateName { get; }
internal X509Certificate2 rootCertificate { get; set; }
......@@ -42,10 +42,11 @@ namespace Titanium.Web.Proxy.Network
internal X509Certificate2 GetRootCertificate()
{
var fileName = Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "rootCert.pfx");
var path = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
if (null == path) throw new NullReferenceException();
var fileName = Path.Combine(path, "rootCert.pfx");
if (File.Exists(fileName))
{
if (!File.Exists(fileName)) return null;
try
{
return new X509Certificate2(fileName, string.Empty, X509KeyStorageFlags.Exportable);
......@@ -57,8 +58,6 @@ namespace Titanium.Web.Proxy.Network
return null;
}
}
return null;
}
/// <summary>
/// Attempts to create a RootCertificate
/// </summary>
......@@ -75,7 +74,7 @@ namespace Titanium.Web.Proxy.Network
{
rootCertificate = CreateCertificate(RootCertificateName, true);
}
catch(Exception e)
catch (Exception e)
{
exceptionFunc(e);
}
......@@ -83,38 +82,36 @@ namespace Titanium.Web.Proxy.Network
{
try
{
var fileName = Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "rootCert.pfx");
var path = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
if (null == path) throw new NullReferenceException();
var fileName = Path.Combine(path, "rootCert.pfx");
File.WriteAllBytes(fileName, rootCertificate.Export(X509ContentType.Pkcs12));
}
catch(Exception e)
catch (Exception e)
{
exceptionFunc(e);
}
}
return rootCertificate != null;
}
/// <summary>
/// Create an SSL certificate
/// </summary>
/// <param name="store"></param>
/// <param name="certificateName"></param>
/// <param name="isRootCertificate"></param>
/// <returns></returns>
internal virtual X509Certificate2 CreateCertificate(string certificateName, bool isRootCertificate)
{
try
{
if (certificateCache.ContainsKey(certificateName))
{
var cached = certificateCache[certificateName];
cached.LastAccess = DateTime.Now;
return cached.Certificate;
}
}
catch
{
}
X509Certificate2 certificate = null;
lock (string.Intern(certificateName))
{
......@@ -124,7 +121,7 @@ namespace Titanium.Web.Proxy.Network
{
certificate = certEngine.MakeCertificate(certificateName, isRootCertificate, rootCertificate);
}
catch(Exception e)
catch (Exception e)
{
exceptionFunc(e);
}
......@@ -165,9 +162,6 @@ namespace Titanium.Web.Proxy.Network
{
clearCertificates = true;
while (clearCertificates)
{
try
{
var cutOff = DateTime.Now.AddMinutes(-1 * certificateCacheTimeOutMinutes);
......@@ -177,10 +171,6 @@ namespace Titanium.Web.Proxy.Network
foreach (var cache in outdated)
certificateCache.Remove(cache.Key);
}
finally
{
}
//after a minute come back to check for outdated certificates in cache
await Task.Delay(1000 * 60);
......
......@@ -47,6 +47,9 @@ namespace Titanium.Web.Proxy.Network.Tcp
LastAccess = DateTime.Now;
}
/// <summary>
/// Dispose.
/// </summary>
public void Dispose()
{
Stream.Close();
......
......@@ -19,14 +19,12 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// </summary>
internal class TcpConnectionFactory
{
/// <summary>
/// Creates a TCP connection to server
/// </summary>
/// <param name="bufferSize"></param>
/// <param name="connectionTimeOutSeconds"></param>
/// <param name="remoteHostName"></param>
/// <param name="httpCmd"></param>
/// <param name="httpVersion"></param>
/// <param name="isHttps"></param>
/// <param name="remotePort"></param>
......@@ -69,7 +67,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
if (!string.IsNullOrEmpty(externalHttpsProxy.UserName) && externalHttpsProxy.Password != null)
{
await writer.WriteLineAsync("Proxy-Connection: keep-alive");
await writer.WriteLineAsync("Proxy-Authorization" + ": Basic " + Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(externalHttpsProxy.UserName + ":" + externalHttpsProxy.Password)));
await writer.WriteLineAsync("Proxy-Authorization" + ": Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(externalHttpsProxy.UserName + ":" + externalHttpsProxy.Password)));
}
await writer.WriteLineAsync();
await writer.FlushAsync();
......@@ -81,7 +79,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
var result = await reader.ReadLineAsync();
if (!new string[] { "200 OK", "connection established" }.Any(s => result.ToLower().Contains(s.ToLower())))
if (!new[] { "200 OK", "connection established" }.Any(s => result.ToLower().Contains(s.ToLower())))
{
throw new Exception("Upstream proxy failed to create a secure tunnel");
}
......
......@@ -29,16 +29,16 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// <summary>
/// Gets the local end point.
/// </summary>
public IPEndPoint LocalEndPoint { get; private set; }
public IPEndPoint LocalEndPoint { get; }
/// <summary>
/// Gets the remote end point.
/// </summary>
public IPEndPoint RemoteEndPoint { get; private set; }
public IPEndPoint RemoteEndPoint { get; }
/// <summary>
/// Gets the process identifier.
/// </summary>
public int ProcessId { get; private set; }
public int ProcessId { get; }
}
}
\ No newline at end of file
......@@ -6,7 +6,9 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// <summary>
/// Represents collection of TcpRows
/// </summary>
/// <seealso cref="System.Collections.Generic.IEnumerable{Proxy.Tcp.TcpRow}" />
/// <seealso>
/// <cref>System.Collections.Generic.IEnumerable{Proxy.Tcp.TcpRow}</cref>
/// </seealso>
internal class TcpTable : IEnumerable<TcpRow>
{
private readonly IEnumerable<TcpRow> tcpRows;
......
......@@ -24,32 +24,46 @@ namespace Titanium.Web.Proxy
try
{
if (!httpHeaders.Where(t => t.Name == "Proxy-Authorization").Any())
if (httpHeaders.All(t => t.Name != "Proxy-Authorization"))
{
await WriteResponseStatus(new Version(1, 1), "407",
"Proxy Authentication Required", clientStreamWriter);
var response = new Response();
response.ResponseHeaders = new Dictionary<string, HttpHeader>();
response.ResponseHeaders.Add("Proxy-Authenticate", new HttpHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\""));
response.ResponseHeaders.Add("Proxy-Connection", new HttpHeader("Proxy-Connection", "close"));
var response = new Response
{
ResponseHeaders = new Dictionary<string, HttpHeader>
{
{
"Proxy-Authenticate",
new HttpHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\"")
},
{"Proxy-Connection", new HttpHeader("Proxy-Connection", "close")}
}
};
await WriteResponseHeaders(clientStreamWriter, response);
await clientStreamWriter.WriteLineAsync();
return false;
}
else
{
var headerValue = httpHeaders.Where(t => t.Name == "Proxy-Authorization").FirstOrDefault().Value.Trim();
var header = httpHeaders.FirstOrDefault(t => t.Name == "Proxy-Authorization");
if (null == header) throw new NullReferenceException();
var headerValue = header.Value.Trim();
if (!headerValue.ToLower().StartsWith("basic"))
{
//Return not authorized
await WriteResponseStatus(new Version(1, 1), "407",
"Proxy Authentication Invalid", clientStreamWriter);
var response = new Response();
response.ResponseHeaders = new Dictionary<string, HttpHeader>();
response.ResponseHeaders.Add("Proxy-Authenticate", new HttpHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\""));
response.ResponseHeaders.Add("Proxy-Connection", new HttpHeader("Proxy-Connection", "close"));
var response = new Response
{
ResponseHeaders = new Dictionary<string, HttpHeader>
{
{
"Proxy-Authenticate",
new HttpHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\"")
},
{"Proxy-Connection", new HttpHeader("Proxy-Connection", "close")}
}
};
await WriteResponseHeaders(clientStreamWriter, response);
await clientStreamWriter.WriteLineAsync();
......@@ -63,10 +77,17 @@ namespace Titanium.Web.Proxy
//Return not authorized
await WriteResponseStatus(new Version(1, 1), "407",
"Proxy Authentication Invalid", clientStreamWriter);
var response = new Response();
response.ResponseHeaders = new Dictionary<string, HttpHeader>();
response.ResponseHeaders.Add("Proxy-Authenticate", new HttpHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\""));
response.ResponseHeaders.Add("Proxy-Connection", new HttpHeader("Proxy-Connection", "close"));
var response = new Response
{
ResponseHeaders = new Dictionary<string, HttpHeader>
{
{
"Proxy-Authenticate",
new HttpHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\"")
},
{"Proxy-Connection", new HttpHeader("Proxy-Connection", "close")}
}
};
await WriteResponseHeaders(clientStreamWriter, response);
await clientStreamWriter.WriteLineAsync();
......@@ -76,17 +97,20 @@ namespace Titanium.Web.Proxy
var password = decoded.Substring(decoded.IndexOf(':') + 1);
return await AuthenticateUserFunc(username, password).ConfigureAwait(false);
}
}
catch (Exception e)
{
ExceptionFunc(new ProxyAuthorizationException("Error whilst authorizing request", e, httpHeaders));
//Return not authorized
await WriteResponseStatus(new Version(1, 1), "407",
"Proxy Authentication Invalid", clientStreamWriter);
var response = new Response();
response.ResponseHeaders = new Dictionary<string, HttpHeader>();
response.ResponseHeaders.Add("Proxy-Authenticate", new HttpHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\""));
response.ResponseHeaders.Add("Proxy-Connection", new HttpHeader("Proxy-Connection", "close"));
var response = new Response
{
ResponseHeaders = new Dictionary<string, HttpHeader>
{
{"Proxy-Authenticate", new HttpHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\"")},
{"Proxy-Connection", new HttpHeader("Proxy-Connection", "close")}
}
};
await WriteResponseHeaders(clientStreamWriter, response);
await clientStreamWriter.WriteLineAsync();
......
......@@ -47,14 +47,16 @@ namespace Titanium.Web.Proxy
/// <summary>
/// A object that creates tcp connection to server
/// </summary>
private TcpConnectionFactory tcpConnectionFactory { get; set; }
private TcpConnectionFactory tcpConnectionFactory { get; }
/// <summary>
/// Manage system proxy settings
/// </summary>
private SystemProxyManager systemProxySettingsManager { get; set; }
private SystemProxyManager systemProxySettingsManager { get; }
#if !DEBUG
private FireFoxProxySettingsManager firefoxProxySettingsManager { get; set; }
#endif
/// <summary>
/// Buffer size used throughout this proxy
......@@ -138,14 +140,8 @@ namespace Titanium.Web.Proxy
/// </summary>
public Action<Exception> ExceptionFunc
{
get
{
return exceptionFunc ?? defaultExceptionFunc.Value;
}
set
{
exceptionFunc = value;
}
get => exceptionFunc ?? defaultExceptionFunc.Value;
set => exceptionFunc = value;
}
/// <summary>
......@@ -172,6 +168,7 @@ namespace Titanium.Web.Proxy
/// <summary>
/// A callback to provide authentication credentials for up stream proxy this proxy is using for HTTPS requests
/// return the ExternalProxy object with valid credentials
/// </summary>
public Func<SessionEventArgs, Task<ExternalProxy>> GetCustomUpStreamHttpsProxyFunc
{
get;
......@@ -203,6 +200,11 @@ namespace Titanium.Web.Proxy
/// </summary>
public ProxyServer() : this(null, null) { }
/// <summary>
/// Constructor.
/// </summary>
/// <param name="rootCertificateName">Name of root certificate.</param>
/// <param name="rootCertificateIssuerName">Name of root certificate issuer.</param>
public ProxyServer(string rootCertificateName, string rootCertificateIssuerName)
{
RootCertificateName = rootCertificateName;
......@@ -214,8 +216,9 @@ namespace Titanium.Web.Proxy
ProxyEndPoints = new List<ProxyEndPoint>();
tcpConnectionFactory = new TcpConnectionFactory();
systemProxySettingsManager = new SystemProxyManager();
firefoxProxySettingsManager = new FireFoxProxySettingsManager();
#if !DEBUG
new FireFoxProxySettingsManager();
#endif
RootCertificateName = RootCertificateName ?? "Titanium Root Certificate Authority";
RootCertificateIssuerName = RootCertificateIssuerName ?? "Titanium";
}
......@@ -277,7 +280,7 @@ namespace Titanium.Web.Proxy
#if !DEBUG
firefoxProxySettingsManager.AddFirefox();
#endif
Console.WriteLine("Set endpoint at Ip {1} and port: {2} as System HTTP Proxy", endPoint.GetType().Name, endPoint.IpAddress, endPoint.Port);
Console.WriteLine("Set endpoint at Ip {0} and port: {1} as System HTTP Proxy", endPoint.IpAddress, endPoint.Port);
}
......@@ -313,7 +316,7 @@ namespace Titanium.Web.Proxy
#if !DEBUG
firefoxProxySettingsManager.AddFirefox();
#endif
Console.WriteLine("Set endpoint at Ip {1} and port: {2} as System HTTPS Proxy", endPoint.GetType().Name, endPoint.IpAddress, endPoint.Port);
Console.WriteLine("Set endpoint at Ip {0} and port: {1} as System HTTPS Proxy", endPoint.IpAddress, endPoint.Port);
}
/// <summary>
......@@ -461,6 +464,7 @@ namespace Titanium.Web.Proxy
/// <param name="endPoint"></param>
private void ValidateEndPointAsSystemProxy(ExplicitProxyEndPoint endPoint)
{
if (endPoint == null) throw new ArgumentNullException(nameof(endPoint));
if (ProxyEndPoints.Contains(endPoint) == false)
{
throw new Exception("Cannot set endPoints not added to proxy as system proxy");
......@@ -542,6 +546,9 @@ namespace Titanium.Web.Proxy
endPoint.listener.BeginAcceptTcpClient(OnAcceptConnection, endPoint);
}
/// <summary>
/// Dispose Proxy.
/// </summary>
public void Dispose()
{
if (proxyRunning)
......@@ -551,5 +558,45 @@ namespace Titanium.Web.Proxy
certificateCacheManager?.Dispose();
}
/// <summary>
/// Invocator for BeforeRequest event.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected virtual void OnBeforeRequest(object sender, SessionEventArgs e)
{
BeforeRequest?.Invoke(sender, e);
}
/// <summary>
/// Invocator for BeforeResponse event.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <returns></returns>
protected virtual void OnBeforeResponse(object sender, SessionEventArgs e)
{
BeforeResponse?.Invoke(sender, e);
}
/// <summary>
/// Invocator for ServerCertificateValidationCallback event.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected virtual void OnServerCertificateValidationCallback(object sender, CertificateValidationEventArgs e)
{
ServerCertificateValidationCallback?.Invoke(sender, e);
}
/// <summary>
/// Invocator for ClientCertifcateSelectionCallback event.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected virtual void OnClientCertificateSelectionCallback(object sender, CertificateSelectionEventArgs e)
{
ClientCertificateSelectionCallback?.Invoke(sender, e);
}
}
}
\ No newline at end of file
......@@ -11,7 +11,6 @@ using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models;
using System.Security.Cryptography.X509Certificates;
using Titanium.Web.Proxy.Shared;
using Titanium.Web.Proxy.Http;
using System.Threading.Tasks;
......@@ -58,29 +57,21 @@ namespace Titanium.Web.Proxy
//Find the request Verb
var httpVerb = httpCmdSplit[0];
if (httpVerb.ToUpper() == "CONNECT")
{
httpRemoteUri = new Uri("http://" + httpCmdSplit[1]);
}
else
{
httpRemoteUri = new Uri(httpCmdSplit[1]);
}
httpRemoteUri = httpVerb.ToUpper() == "CONNECT" ? new Uri("http://" + httpCmdSplit[1]) : new Uri(httpCmdSplit[1]);
//parse the HTTP version
Version version = new Version(1, 1);
var version = new Version(1, 1);
if (httpCmdSplit.Length == 3)
{
string httpVersion = httpCmdSplit[2].Trim();
var httpVersion = httpCmdSplit[2].Trim();
if (httpVersion == "http/1.0")
if (0 == string.CompareOrdinal(httpVersion, "http/1.0"))
{
version = new Version(1, 0);
}
}
//filter out excluded host names
var excluded = endPoint.ExcludedHttpsHostNameRegex != null ?
endPoint.ExcludedHttpsHostNameRegex.Any(x => Regex.IsMatch(httpRemoteUri.Host, x)) : false;
var excluded = endPoint.ExcludedHttpsHostNameRegex != null && endPoint.ExcludedHttpsHostNameRegex.Any(x => Regex.IsMatch(httpRemoteUri.Host, x));
List<HttpHeader> connectRequestHeaders = null;
......@@ -89,7 +80,7 @@ namespace Titanium.Web.Proxy
if (httpVerb.ToUpper() == "CONNECT" && !excluded && httpRemoteUri.Port != 80)
{
httpRemoteUri = new Uri("https://" + httpCmdSplit[1]);
string tmpLine = null;
string tmpLine;
connectRequestHeaders = new List<HttpHeader>();
while (!string.IsNullOrEmpty(tmpLine = await clientStreamReader.ReadLineAsync()))
{
......@@ -114,15 +105,7 @@ namespace Titanium.Web.Proxy
sslStream = new SslStream(clientStream, true);
X509Certificate2 certificate;
if (endPoint.GenericCertificate != null)
{
certificate = endPoint.GenericCertificate;
}
else
{
certificate = certificateCacheManager.CreateCertificate(httpRemoteUri.Host, false);
}
var certificate = endPoint.GenericCertificate ?? certificateCacheManager.CreateCertificate(httpRemoteUri.Host, false);
//Successfully managed to authenticate the client using the fake certificate
await sslStream.AuthenticateAsServerAsync(certificate, false,
......@@ -136,10 +119,7 @@ namespace Titanium.Web.Proxy
}
catch
{
if (sslStream != null)
{
sslStream.Dispose();
}
sslStream?.Dispose();
Dispose(clientStream, clientStreamReader, clientStreamWriter, null);
return;
......@@ -160,8 +140,8 @@ namespace Titanium.Web.Proxy
await TcpHelper.SendRaw(BUFFER_SIZE, ConnectionTimeOutSeconds, httpRemoteUri.Host, httpRemoteUri.Port,
httpCmd, version, null,
false, SupportedSslProtocols,
new RemoteCertificateValidationCallback(ValidateServerCertificate),
new LocalCertificateSelectionCallback(SelectClientCertificate),
ValidateServerCertificate,
SelectClientCertificate,
clientStream, tcpConnectionFactory, UpStreamEndPoint);
Dispose(clientStream, clientStreamReader, clientStreamWriter, null);
......@@ -169,7 +149,7 @@ namespace Titanium.Web.Proxy
}
//Now create the request
await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamReader, clientStreamWriter,
httpRemoteUri.Scheme == Uri.UriSchemeHttps ? httpRemoteUri.Host : null, endPoint, connectRequestHeaders, null, null);
httpRemoteUri.Scheme == Uri.UriSchemeHttps ? httpRemoteUri.Host : null, endPoint, connectRequestHeaders);
}
catch (Exception)
{
......@@ -189,14 +169,13 @@ namespace Titanium.Web.Proxy
CustomBinaryReader clientStreamReader = null;
StreamWriter clientStreamWriter = null;
X509Certificate2 certificate = null;
if (endPoint.EnableSsl)
{
var sslStream = new SslStream(clientStream, true);
//implement in future once SNI supported by SSL stream, for now use the same certificate
certificate = certificateCacheManager.CreateCertificate(endPoint.GenericCertificateName, false);
var certificate = certificateCacheManager.CreateCertificate(endPoint.GenericCertificateName, false);
try
{
......@@ -259,8 +238,8 @@ namespace Titanium.Web.Proxy
connection = await tcpConnectionFactory.CreateClient(BUFFER_SIZE, ConnectionTimeOutSeconds,
args.WebSession.Request.RequestUri.Host, args.WebSession.Request.RequestUri.Port, args.WebSession.Request.HttpVersion,
args.IsHttps, SupportedSslProtocols,
new RemoteCertificateValidationCallback(ValidateServerCertificate),
new LocalCertificateSelectionCallback(SelectClientCertificate),
ValidateServerCertificate,
SelectClientCertificate,
customUpStreamHttpProxy ?? UpStreamHttpProxy, customUpStreamHttpsProxy ?? UpStreamHttpsProxy, args.ProxyClient.ClientStream, UpStreamEndPoint);
}
......@@ -367,9 +346,14 @@ namespace Titanium.Web.Proxy
/// <param name="clientStreamReader"></param>
/// <param name="clientStreamWriter"></param>
/// <param name="httpsHostName"></param>
/// <param name="endPoint"></param>
/// <param name="connectHeaders"></param>
/// <param name="customUpStreamHttpProxy"></param>
/// <param name="customUpStreamHttpsProxy"></param>
/// <returns></returns>
private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream,
CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, string httpsHostName, ProxyEndPoint endPoint, List<HttpHeader> connectHeaders, ExternalProxy customUpStreamHttpProxy = null, ExternalProxy customUpStreamHttpsProxy = null)
CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, string httpsHostName,
ProxyEndPoint endPoint, List<HttpHeader> connectHeaders, ExternalProxy customUpStreamHttpProxy = null, ExternalProxy customUpStreamHttpsProxy = null)
{
TcpConnection connection = null;
......@@ -383,9 +367,12 @@ namespace Titanium.Web.Proxy
break;
}
var args = new SessionEventArgs(BUFFER_SIZE, HandleHttpSessionResponse);
args.ProxyClient.TcpClient = client;
args.WebSession.ConnectHeaders = connectHeaders;
var args =
new SessionEventArgs(BUFFER_SIZE, HandleHttpSessionResponse)
{
ProxyClient = {TcpClient = client},
WebSession = {ConnectHeaders = connectHeaders}
};
args.WebSession.ProcessId = new Lazy<int>(() =>
{
......@@ -409,12 +396,12 @@ namespace Titanium.Web.Proxy
var httpMethod = httpCmdSplit[0];
//find the request HTTP version
Version httpVersion = new Version(1, 1);
var httpVersion = new Version(1, 1);
if (httpCmdSplit.Length == 3)
{
var httpVersionString = httpCmdSplit[2].ToLower().Trim();
if (httpVersionString == "http/1.0")
if (0 == string.CompareOrdinal(httpVersionString, "http/1.0"))
{
httpVersion = new Version(1, 0);
}
......@@ -439,10 +426,8 @@ namespace Titanium.Web.Proxy
{
var existing = args.WebSession.Request.RequestHeaders[newHeader.Name];
var nonUniqueHeaders = new List<HttpHeader>();
var nonUniqueHeaders = new List<HttpHeader> {existing, newHeader};
nonUniqueHeaders.Add(existing);
nonUniqueHeaders.Add(newHeader);
args.WebSession.Request.NonUniqueRequestHeaders.Add(newHeader.Name, nonUniqueHeaders);
args.WebSession.Request.RequestHeaders.Remove(newHeader.Name);
......@@ -455,8 +440,7 @@ namespace Titanium.Web.Proxy
}
var httpRemoteUri = new Uri(httpsHostName == null ? httpCmdSplit[1]
: (string.Concat("https://", args.WebSession.Request.Host == null ?
httpsHostName : args.WebSession.Request.Host, httpCmdSplit[1])));
: (string.Concat("https://", args.WebSession.Request.Host ?? httpsHostName, httpCmdSplit[1])));
args.WebSession.Request.RequestUri = httpRemoteUri;
......@@ -479,10 +463,10 @@ namespace Titanium.Web.Proxy
//If user requested interception do it
if (BeforeRequest != null)
{
Delegate[] invocationList = BeforeRequest.GetInvocationList();
Task[] handlerTasks = new Task[invocationList.Length];
var invocationList = BeforeRequest.GetInvocationList();
var handlerTasks = new Task[invocationList.Length];
for (int i = 0; i < invocationList.Length; i++)
for (var i = 0; i < invocationList.Length; i++)
{
handlerTasks[i] = ((Func<object, SessionEventArgs, Task>)invocationList[i])(null, args);
}
......@@ -495,8 +479,8 @@ namespace Titanium.Web.Proxy
{
await TcpHelper.SendRaw(BUFFER_SIZE, ConnectionTimeOutSeconds, httpRemoteUri.Host, httpRemoteUri.Port,
httpCmd, httpVersion, args.WebSession.Request.RequestHeaders, args.IsHttps,
SupportedSslProtocols, new RemoteCertificateValidationCallback(ValidateServerCertificate),
new LocalCertificateSelectionCallback(SelectClientCertificate),
SupportedSslProtocols, ValidateServerCertificate,
SelectClientCertificate,
clientStream, tcpConnectionFactory, UpStreamEndPoint);
Dispose(clientStream, clientStreamReader, clientStreamWriter, args);
......@@ -504,7 +488,7 @@ namespace Titanium.Web.Proxy
}
//construct the web request that we are going to issue on behalf of the client.
await HandleHttpSessionRequestInternal(connection, args, customUpStreamHttpProxy, customUpStreamHttpsProxy, false).ConfigureAwait(false);
await HandleHttpSessionRequestInternal(null, args, customUpStreamHttpProxy, customUpStreamHttpsProxy, false).ConfigureAwait(false);
if (args.WebSession.Request.CancelRequest)
......@@ -531,12 +515,8 @@ namespace Titanium.Web.Proxy
}
if (connection != null)
{
//dispose
connection.Dispose();
}
connection?.Dispose();
}
/// <summary>
......@@ -547,8 +527,9 @@ namespace Titanium.Web.Proxy
/// <returns></returns>
private async Task WriteConnectResponse(StreamWriter clientStreamWriter, Version httpVersion)
{
await clientStreamWriter.WriteLineAsync(string.Format("HTTP/{0}.{1} {2}", httpVersion.Major, httpVersion.Minor, "200 Connection established"));
await clientStreamWriter.WriteLineAsync(string.Format("Timestamp: {0}", DateTime.Now));
await clientStreamWriter.WriteLineAsync(
$"HTTP/{httpVersion.Major}.{httpVersion.Minor} 200 Connection established");
await clientStreamWriter.WriteLineAsync($"Timestamp: {DateTime.Now}");
await clientStreamWriter.WriteLineAsync();
await clientStreamWriter.FlushAsync();
}
......@@ -570,9 +551,6 @@ namespace Titanium.Web.Proxy
case "accept-encoding":
header.Value = "gzip,deflate";
break;
default:
break;
}
}
......
......@@ -17,7 +17,11 @@ namespace Titanium.Web.Proxy
/// </summary>
partial class ProxyServer
{
//Called asynchronously when a request was successfully and we received the response
/// <summary>
/// Called asynchronously when a request was successfully and we received the response
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
public async Task HandleHttpSessionResponse(SessionEventArgs args)
{
//read response & headers from server
......@@ -156,14 +160,14 @@ namespace Titanium.Web.Proxy
private async Task WriteResponseStatus(Version version, string code, string description,
StreamWriter responseWriter)
{
await responseWriter.WriteLineAsync(string.Format("HTTP/{0}.{1} {2} {3}", version.Major, version.Minor, code, description));
await responseWriter.WriteLineAsync($"HTTP/{version.Major}.{version.Minor} {code} {description}");
}
/// <summary>
/// Write response headers to client
/// </summary>
/// <param name="responseWriter"></param>
/// <param name="headers"></param>
/// <param name="response"></param>
/// <returns></returns>
private async Task WriteResponseHeaders(StreamWriter responseWriter, Response response)
{
......@@ -220,7 +224,6 @@ namespace Titanium.Web.Proxy
/// <summary>
/// Handle dispose of a client/server session
/// </summary>
/// <param name="tcpClient"></param>
/// <param name="clientStream"></param>
/// <param name="clientStreamReader"></param>
/// <param name="clientStreamWriter"></param>
......@@ -235,21 +238,13 @@ namespace Titanium.Web.Proxy
clientStream.Dispose();
}
if (args != null)
{
args.Dispose();
}
args?.Dispose();
if (clientStreamReader != null)
{
clientStreamReader.Dispose();
}
clientStreamReader?.Dispose();
if (clientStreamWriter != null)
{
if (clientStreamWriter == null) return;
clientStreamWriter.Close();
clientStreamWriter.Dispose();
}
}
}
}
\ No newline at end of file
using System;
using System.Text;
using System.Text;
namespace Titanium.Web.Proxy.Shared
{
......
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