Commit 8be11ef4 authored by Jehonathan's avatar Jehonathan Committed by GitHub

Merge pull request #123 from ilushka85/master

Switch to CertEnroll and remove semaphore locking - Faster SSL Generation 
parents 43acfe89 f9091d97
...@@ -69,8 +69,8 @@ Setup HTTP proxy: ...@@ -69,8 +69,8 @@ Setup HTTP proxy:
}; };
proxyServer.AddEndPoint(transparentEndPoint); proxyServer.AddEndPoint(transparentEndPoint);
//proxyServer.ExternalHttpProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 }; //proxyServer.UpStreamHttpProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 };
//proxyServer.ExternalHttpsProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 }; //proxyServer.UpStreamHttpsProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 };
foreach (var endPoint in proxyServer.ProxyEndPoints) foreach (var endPoint in proxyServer.ProxyEndPoints)
Console.WriteLine("Listening on '{0}' endpoint at Ip {1} and port: {2} ", Console.WriteLine("Listening on '{0}' endpoint at Ip {1} and port: {2} ",
......
...@@ -38,6 +38,13 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -38,6 +38,13 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary> /// </summary>
internal ProxyClient ProxyClient { get; set; } internal ProxyClient ProxyClient { get; set; }
//Should we send a rerequest
public bool ReRequest
{
get;
set;
}
/// <summary> /// <summary>
/// Does this session uses SSL /// Does this session uses SSL
/// </summary> /// </summary>
...@@ -52,6 +59,12 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -52,6 +59,12 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary> /// </summary>
public HttpWebClient WebSession { get; set; } public HttpWebClient WebSession { get; set; }
public ExternalProxy CustomUpStreamHttpProxyUsed { get; set; }
public ExternalProxy CustomUpStreamHttpsProxyUsed { get; set; }
/// <summary> /// <summary>
/// Constructor to initialize the proxy /// Constructor to initialize the proxy
...@@ -140,7 +153,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -140,7 +153,7 @@ namespace Titanium.Web.Proxy.EventArguments
WebSession.Response.ContentLength); WebSession.Response.ContentLength);
} }
else if (WebSession.Response.HttpVersion.Major == 1 && WebSession.Response.HttpVersion.Minor == 0) else if ((WebSession.Response.HttpVersion.Major == 1 && WebSession.Response.HttpVersion.Minor == 0) || WebSession.Response.ContentLength == -1)
{ {
await WebSession.ServerConnection.StreamReader.CopyBytesToStream(bufferSize, responseBodyStream, long.MaxValue); await WebSession.ServerConnection.StreamReader.CopyBytesToStream(bufferSize, responseBodyStream, long.MaxValue);
} }
......
...@@ -19,6 +19,8 @@ namespace Titanium.Web.Proxy.Http ...@@ -19,6 +19,8 @@ namespace Titanium.Web.Proxy.Http
/// </summary> /// </summary>
internal TcpConnection ServerConnection { get; set; } internal TcpConnection ServerConnection { get; set; }
public List<HttpHeader> ConnectHeaders { get; set; }
public Request Request { get; set; } public Request Request { get; set; }
public Response Response { get; set; } public Response Response { get; set; }
...@@ -39,6 +41,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -39,6 +41,7 @@ namespace Titanium.Web.Proxy.Http
/// <param name="Connection"></param> /// <param name="Connection"></param>
internal void SetConnection(TcpConnection Connection) internal void SetConnection(TcpConnection Connection)
{ {
Connection.LastAccess = DateTime.Now;
ServerConnection = Connection; ServerConnection = Connection;
} }
...@@ -59,34 +62,61 @@ namespace Titanium.Web.Proxy.Http ...@@ -59,34 +62,61 @@ namespace Titanium.Web.Proxy.Http
StringBuilder requestLines = new StringBuilder(); StringBuilder requestLines = new StringBuilder();
//prepare the request & headers //prepare the request & headers
if ((ServerConnection.UpStreamHttpProxy != null && ServerConnection.IsHttps == false) || (ServerConnection.UpStreamHttpsProxy != null && ServerConnection.IsHttps == true))
{
requestLines.AppendLine(string.Join(" ", new string[3]
{
this.Request.Method,
this.Request.RequestUri.AbsoluteUri,
string.Format("HTTP/{0}.{1}",this.Request.HttpVersion.Major, this.Request.HttpVersion.Minor)
}));
}
else
{
requestLines.AppendLine(string.Join(" ", new string[3] requestLines.AppendLine(string.Join(" ", new string[3]
{ {
this.Request.Method, this.Request.Method,
this.Request.RequestUri.PathAndQuery, this.Request.RequestUri.PathAndQuery,
string.Format("HTTP/{0}.{1}",this.Request.HttpVersion.Major, this.Request.HttpVersion.Minor) string.Format("HTTP/{0}.{1}",this.Request.HttpVersion.Major, this.Request.HttpVersion.Minor)
})); }));
}
//Send Authentication to Upstream proxy if needed
if (ServerConnection.UpStreamHttpProxy != null && ServerConnection.IsHttps == false && ServerConnection.UpStreamHttpProxy.UserName != null && ServerConnection.UpStreamHttpProxy.UserName != "" && ServerConnection.UpStreamHttpProxy.Password != null)
{
requestLines.AppendLine("Proxy-Connection: keep-alive");
requestLines.AppendLine("Proxy-Authorization" + ": Basic " + Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(ServerConnection.UpStreamHttpProxy.UserName + ":" + ServerConnection.UpStreamHttpProxy.Password)));
}
//write request headers //write request headers
foreach (var headerItem in this.Request.RequestHeaders) foreach (var headerItem in this.Request.RequestHeaders)
{ {
var header = headerItem.Value; var header = headerItem.Value;
if (headerItem.Key != "Proxy-Authorization")
{
requestLines.AppendLine(header.Name + ':' + header.Value); requestLines.AppendLine(header.Name + ':' + header.Value);
} }
}
//write non unique request headers //write non unique request headers
foreach (var headerItem in this.Request.NonUniqueRequestHeaders) foreach (var headerItem in this.Request.NonUniqueRequestHeaders)
{ {
var headers = headerItem.Value; var headers = headerItem.Value;
foreach (var header in headers) foreach (var header in headers)
{
if (headerItem.Key != "Proxy-Authorization")
{ {
requestLines.AppendLine(header.Name + ':' + header.Value); requestLines.AppendLine(header.Name + ':' + header.Value);
} }
} }
}
requestLines.AppendLine(); requestLines.AppendLine();
string request = requestLines.ToString(); string request = requestLines.ToString();
byte[] requestBytes = Encoding.ASCII.GetBytes(request); byte[] requestBytes = Encoding.ASCII.GetBytes(request);
var test = Encoding.UTF8.GetString(requestBytes);
await stream.WriteAsync(requestBytes, 0, requestBytes.Length); await stream.WriteAsync(requestBytes, 0, requestBytes.Length);
await stream.FlushAsync(); await stream.FlushAsync();
......
...@@ -205,7 +205,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -205,7 +205,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary> /// <summary>
/// Response network stream /// Response network stream
/// </summary> /// </summary>
internal Stream ResponseStream { get; set; } public Stream ResponseStream { get; set; }
/// <summary> /// <summary>
/// response body contenst as byte array /// response body contenst as byte array
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
/// </summary> /// </summary>
public class ExternalProxy public class ExternalProxy
{ {
public string UserName { get; set; }
public string Password { get; set; }
public string HostName { get; set; } public string HostName { get; set; }
public int Port { get; set; } public int Port { get; set; }
} }
......
This diff is collapsed.
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
using System.IO; using System.IO;
using System.Net.Sockets; using System.Net.Sockets;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Network namespace Titanium.Web.Proxy.Network
{ {
...@@ -10,11 +11,20 @@ namespace Titanium.Web.Proxy.Network ...@@ -10,11 +11,20 @@ namespace Titanium.Web.Proxy.Network
/// </summary> /// </summary>
public class TcpConnection : IDisposable public class TcpConnection : IDisposable
{ {
internal ExternalProxy UpStreamHttpProxy { get; set; }
internal ExternalProxy UpStreamHttpsProxy { get; set; }
internal string HostName { get; set; } internal string HostName { get; set; }
internal int port { get; set; } internal int port { get; set; }
internal bool IsHttps { get; set; } internal bool IsHttps { get; set; }
/// <summary>
/// Http version
/// </summary>
internal Version Version { get; set; }
internal TcpClient TcpClient { get; set; } internal TcpClient TcpClient { get; set; }
/// <summary> /// <summary>
...@@ -27,6 +37,16 @@ namespace Titanium.Web.Proxy.Network ...@@ -27,6 +37,16 @@ namespace Titanium.Web.Proxy.Network
/// </summary> /// </summary>
internal Stream Stream { get; set; } internal Stream Stream { get; set; }
/// <summary>
/// Last time this connection was used
/// </summary>
internal DateTime LastAccess { get; set; }
internal TcpConnection()
{
LastAccess = DateTime.Now;
}
public void Dispose() public void Dispose()
{ {
Stream.Close(); Stream.Close();
......
...@@ -7,6 +7,7 @@ using System.Net.Security; ...@@ -7,6 +7,7 @@ using System.Net.Security;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using System.Security.Authentication; using System.Security.Authentication;
using System.Linq;
namespace Titanium.Web.Proxy.Network namespace Titanium.Web.Proxy.Network
{ {
...@@ -55,9 +56,15 @@ namespace Titanium.Web.Proxy.Network ...@@ -55,9 +56,15 @@ namespace Titanium.Web.Proxy.Network
using (var writer = new StreamWriter(stream, Encoding.ASCII, bufferSize, true)) using (var writer = new StreamWriter(stream, Encoding.ASCII, bufferSize, true))
{ {
await writer.WriteLineAsync(string.Format("CONNECT {0}:{1} {2}", remoteHostName, remotePort, httpVersion)); await writer.WriteLineAsync(string.Format("CONNECT {0}:{1} HTTP/{2}", remoteHostName, remotePort,httpVersion));
await writer.WriteLineAsync(string.Format("Host: {0}:{1}", remoteHostName, remotePort)); await writer.WriteLineAsync(string.Format("Host: {0}:{1}", remoteHostName, remotePort));
await writer.WriteLineAsync("Connection: Keep-Alive"); await writer.WriteLineAsync("Connection: Keep-Alive");
if (externalHttpsProxy.UserName != null && 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(); await writer.WriteLineAsync();
await writer.FlushAsync(); await writer.FlushAsync();
writer.Close(); writer.Close();
...@@ -67,7 +74,8 @@ namespace Titanium.Web.Proxy.Network ...@@ -67,7 +74,8 @@ namespace Titanium.Web.Proxy.Network
{ {
var result = await reader.ReadLineAsync(); var result = await reader.ReadLineAsync();
if (!result.ToLower().Contains("200 connection established"))
if (!new string[] { "200 OK", "connection established" }.Any(s=> result.ToLower().Contains(s.ToLower())))
{ {
throw new Exception("Upstream proxy failed to create a secure tunnel"); throw new Exception("Upstream proxy failed to create a secure tunnel");
} }
...@@ -122,12 +130,15 @@ namespace Titanium.Web.Proxy.Network ...@@ -122,12 +130,15 @@ namespace Titanium.Web.Proxy.Network
return new TcpConnection() return new TcpConnection()
{ {
UpStreamHttpProxy = externalHttpProxy,
UpStreamHttpsProxy = externalHttpsProxy,
HostName = remoteHostName, HostName = remoteHostName,
port = remotePort, port = remotePort,
IsHttps = isHttps, IsHttps = isHttps,
TcpClient = client, TcpClient = client,
StreamReader = new CustomBinaryReader(stream), StreamReader = new CustomBinaryReader(stream),
Stream = stream Stream = stream,
Version = httpVersion
}; };
} }
} }
......
...@@ -9,6 +9,7 @@ using Titanium.Web.Proxy.Models; ...@@ -9,6 +9,7 @@ using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network; using Titanium.Web.Proxy.Network;
using System.Linq; using System.Linq;
using System.Security.Authentication; using System.Security.Authentication;
using System.Collections.Concurrent;
namespace Titanium.Web.Proxy namespace Titanium.Web.Proxy
{ {
...@@ -17,15 +18,46 @@ namespace Titanium.Web.Proxy ...@@ -17,15 +18,46 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
public partial class ProxyServer : IDisposable public partial class ProxyServer : IDisposable
{ {
/// <summary> private static Action<Exception> _exceptionFunc=null;
/// Does the root certificate used by this proxy is trusted by the machine? public static Action<Exception> ExceptionFunc
/// </summary> {
private bool certTrusted { get; set; } get
{
if(_exceptionFunc!=null)
{
return _exceptionFunc;
}
else
{
return (e)=>
{
};
}
}
set
{
_exceptionFunc = value;
}
}
public Func<string, string, Task<bool>> AuthenticateUserFunc
{
get;
set;
}
//parameter is list of headers
public Func<SessionEventArgs, Task<ExternalProxy>> GetCustomUpStreamHttpProxyFunc
{
get;
set;
}
//parameter is list of headers
public Func<SessionEventArgs, Task<ExternalProxy>> GetCustomUpStreamHttpsProxyFunc
{
get;
set;
}
/// <summary>
/// Is the proxy currently running
/// </summary>
private bool proxyRunning { get; set; }
/// <summary> /// <summary>
/// Manages certificates used by this proxy /// Manages certificates used by this proxy
...@@ -44,6 +76,16 @@ namespace Titanium.Web.Proxy ...@@ -44,6 +76,16 @@ namespace Titanium.Web.Proxy
private FireFoxProxySettingsManager firefoxProxySettingsManager { get; set; } private FireFoxProxySettingsManager firefoxProxySettingsManager { get; set; }
/// <summary>
/// Does the root certificate used by this proxy is trusted by the machine?
/// </summary>
private bool certTrusted { get; set; }
/// <summary>
/// Is the proxy currently running
/// </summary>
private bool proxyRunning { get; set; }
/// <summary> /// <summary>
/// Buffer size used throughout this proxy /// Buffer size used throughout this proxy
/// </summary> /// </summary>
...@@ -88,12 +130,12 @@ namespace Titanium.Web.Proxy ...@@ -88,12 +130,12 @@ namespace Titanium.Web.Proxy
/// <summary> /// <summary>
/// External proxy for Http /// External proxy for Http
/// </summary> /// </summary>
public ExternalProxy ExternalHttpProxy { get; set; } public ExternalProxy UpStreamHttpProxy { get; set; }
/// <summary> /// <summary>
/// External proxy for Https /// External proxy for Http
/// </summary> /// </summary>
public ExternalProxy ExternalHttpsProxy { get; set; } public ExternalProxy UpStreamHttpsProxy { get; set; }
/// <summary> /// <summary>
/// Verifies the remote Secure Sockets Layer (SSL) certificate used for authentication /// Verifies the remote Secure Sockets Layer (SSL) certificate used for authentication
...@@ -269,7 +311,7 @@ namespace Titanium.Web.Proxy ...@@ -269,7 +311,7 @@ namespace Titanium.Web.Proxy
certificateCacheManager = new CertificateManager(RootCertificateIssuerName, certificateCacheManager = new CertificateManager(RootCertificateIssuerName,
RootCertificateName); RootCertificateName);
certTrusted = certificateCacheManager.CreateTrustedRootCertificate().Result; certTrusted = certificateCacheManager.CreateTrustedRootCertificate();
foreach (var endPoint in ProxyEndPoints) foreach (var endPoint in ProxyEndPoints)
{ {
...@@ -382,6 +424,7 @@ namespace Titanium.Web.Proxy ...@@ -382,6 +424,7 @@ namespace Titanium.Web.Proxy
//Other errors are discarded to keep proxy running //Other errors are discarded to keep proxy running
} }
if (tcpClient != null) if (tcpClient != null)
{ {
Task.Run(async () => Task.Run(async () =>
...@@ -394,6 +437,8 @@ namespace Titanium.Web.Proxy ...@@ -394,6 +437,8 @@ namespace Titanium.Web.Proxy
} }
else else
{ {
await HandleClient(endPoint as ExplicitProxyEndPoint, tcpClient); await HandleClient(endPoint as ExplicitProxyEndPoint, tcpClient);
} }
...@@ -402,12 +447,11 @@ namespace Titanium.Web.Proxy ...@@ -402,12 +447,11 @@ namespace Titanium.Web.Proxy
{ {
if (tcpClient != null) if (tcpClient != null)
{ {
tcpClient.LingerState = new LingerOption(true, 0);
tcpClient.Client.Shutdown(SocketShutdown.Both); tcpClient.Client.Shutdown(SocketShutdown.Both);
tcpClient.Client.Close(); tcpClient.Client.Close();
tcpClient.Client.Dispose(); tcpClient.Client.Dispose();
tcpClient.Close(); tcpClient.Close();
} }
} }
}); });
......
This diff is collapsed.
...@@ -29,6 +29,7 @@ namespace Titanium.Web.Proxy ...@@ -29,6 +29,7 @@ namespace Titanium.Web.Proxy
args.WebSession.Response.ResponseStream = args.WebSession.ServerConnection.Stream; args.WebSession.Response.ResponseStream = args.WebSession.ServerConnection.Stream;
} }
args.ReRequest = false;
//If user requested call back then do it //If user requested call back then do it
if (BeforeResponse != null && !args.WebSession.Response.ResponseLocked) if (BeforeResponse != null && !args.WebSession.Response.ResponseLocked)
{ {
...@@ -37,12 +38,16 @@ namespace Titanium.Web.Proxy ...@@ -37,12 +38,16 @@ namespace Titanium.Web.Proxy
for (int i = 0; i < invocationList.Length; i++) for (int i = 0; i < invocationList.Length; i++)
{ {
handlerTasks[i] = ((Func<object, SessionEventArgs, Task>)invocationList[i])(null, args); handlerTasks[i] = ((Func<object, SessionEventArgs, Task>)invocationList[i])(this, args);
} }
await Task.WhenAll(handlerTasks); await Task.WhenAll(handlerTasks);
} }
if(args.ReRequest)
{
await HandleHttpSessionRequestInternal(null, args, null, null, true).ConfigureAwait(false);
return;
}
args.WebSession.Response.ResponseLocked = true; args.WebSession.Response.ResponseLocked = 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
......
...@@ -65,6 +65,7 @@ ...@@ -65,6 +65,7 @@
<Compile Include="EventArguments\CertificateValidationEventArgs.cs" /> <Compile Include="EventArguments\CertificateValidationEventArgs.cs" />
<Compile Include="Extensions\ByteArrayExtensions.cs" /> <Compile Include="Extensions\ByteArrayExtensions.cs" />
<Compile Include="Network\CachedCertificate.cs" /> <Compile Include="Network\CachedCertificate.cs" />
<Compile Include="Network\CertEnrollEngine.cs" />
<Compile Include="Network\ProxyClient.cs" /> <Compile Include="Network\ProxyClient.cs" />
<Compile Include="Exceptions\BodyNotFoundException.cs" /> <Compile Include="Exceptions\BodyNotFoundException.cs" />
<Compile Include="Extensions\HttpWebResponseExtensions.cs" /> <Compile Include="Extensions\HttpWebResponseExtensions.cs" />
...@@ -93,15 +94,20 @@ ...@@ -93,15 +94,20 @@
<Compile Include="Http\Responses\RedirectResponse.cs" /> <Compile Include="Http\Responses\RedirectResponse.cs" />
<Compile Include="Shared\ProxyConstants.cs" /> <Compile Include="Shared\ProxyConstants.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup />
<ItemGroup> <ItemGroup>
<None Include="app.config" /> <COMReference Include="CERTENROLLLib">
<None Include="packages.config" /> <Guid>{728AB348-217D-11DA-B2A4-000E7BBB2B09}</Guid>
<VersionMajor>1</VersionMajor>
<VersionMinor>0</VersionMinor>
<Lcid>0</Lcid>
<WrapperTool>tlbimp</WrapperTool>
<Isolated>False</Isolated>
<EmbedInteropTypes>True</EmbedInteropTypes>
</COMReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="makecert.exe"> <None Include="app.config" />
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <None Include="packages.config" />
</None>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" /> <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
......
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