Commit 6a66e7b1 authored by Jehonathan Thomas's avatar Jehonathan Thomas Committed by GitHub

Merge pull request #240 from justcoding121/develop

Beta
parents 9afb81c6 37b14243
...@@ -30,7 +30,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -30,7 +30,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary> /// <summary>
/// Holds a reference to proxy response handler method /// Holds a reference to proxy response handler method
/// </summary> /// </summary>
private readonly Func<SessionEventArgs, Task> httpResponseHandler; private Func<SessionEventArgs, Task> httpResponseHandler;
/// <summary> /// <summary>
/// Holds a reference to client /// Holds a reference to client
...@@ -522,6 +522,10 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -522,6 +522,10 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{ {
httpResponseHandler = null;
CustomUpStreamHttpProxyUsed = null;
CustomUpStreamHttpsProxyUsed = null;
WebSession.Dispose(); WebSession.Dispose();
} }
} }
......
...@@ -214,14 +214,10 @@ namespace Titanium.Web.Proxy.Http ...@@ -214,14 +214,10 @@ namespace Titanium.Web.Proxy.Http
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{ {
//not really needed since GC will collect it ConnectHeaders = null;
//but just to be on safe side
Request.RequestBody = null;
Response.ResponseBody = null;
Request.RequestBodyString = null;
Response.ResponseBodyString = null;
Request.Dispose();
Response.Dispose();
} }
} }
} }
...@@ -9,7 +9,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -9,7 +9,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary> /// <summary>
/// A HTTP(S) request object /// A HTTP(S) request object
/// </summary> /// </summary>
public class Request public class Request : IDisposable
{ {
/// <summary> /// <summary>
/// Request Method /// Request Method
...@@ -294,5 +294,20 @@ namespace Titanium.Web.Proxy.Http ...@@ -294,5 +294,20 @@ namespace Titanium.Web.Proxy.Http
RequestHeaders = new Dictionary<string, HttpHeader>(StringComparer.OrdinalIgnoreCase); RequestHeaders = new Dictionary<string, HttpHeader>(StringComparer.OrdinalIgnoreCase);
NonUniqueRequestHeaders = new Dictionary<string, List<HttpHeader>>(StringComparer.OrdinalIgnoreCase); NonUniqueRequestHeaders = new Dictionary<string, List<HttpHeader>>(StringComparer.OrdinalIgnoreCase);
} }
/// <summary>
/// Dispose off
/// </summary>
public void Dispose()
{
//not really needed since GC will collect it
//but just to be on safe side
RequestHeaders = null;
NonUniqueRequestHeaders = null;
RequestBody = null;
RequestBody = null;
}
} }
} }
...@@ -10,7 +10,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -10,7 +10,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary> /// <summary>
/// Http(s) response object /// Http(s) response object
/// </summary> /// </summary>
public class Response public class Response : IDisposable
{ {
/// <summary> /// <summary>
/// Response Status Code. /// Response Status Code.
...@@ -234,5 +234,20 @@ namespace Titanium.Web.Proxy.Http ...@@ -234,5 +234,20 @@ namespace Titanium.Web.Proxy.Http
ResponseHeaders = new Dictionary<string, HttpHeader>(StringComparer.OrdinalIgnoreCase); ResponseHeaders = new Dictionary<string, HttpHeader>(StringComparer.OrdinalIgnoreCase);
NonUniqueResponseHeaders = new Dictionary<string, List<HttpHeader>>(StringComparer.OrdinalIgnoreCase); NonUniqueResponseHeaders = new Dictionary<string, List<HttpHeader>>(StringComparer.OrdinalIgnoreCase);
} }
/// <summary>
/// Dispose off
/// </summary>
public void Dispose()
{
//not really needed since GC will collect it
//but just to be on safe side
ResponseHeaders = null;
NonUniqueResponseHeaders = null;
ResponseBody = null;
ResponseBodyString = null;
}
} }
} }
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
...@@ -30,7 +31,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -30,7 +31,7 @@ namespace Titanium.Web.Proxy.Network
/// <summary> /// <summary>
/// A class to manage SSL certificates used by this proxy server /// A class to manage SSL certificates used by this proxy server
/// </summary> /// </summary>
public class CertificateManager : IDisposable public sealed class CertificateManager : IDisposable
{ {
internal CertificateEngine Engine internal CertificateEngine Engine
{ {
...@@ -147,7 +148,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -147,7 +148,7 @@ namespace Titanium.Web.Proxy.Network
return fileName; return fileName;
} }
internal X509Certificate2 LoadRootCertificate() private X509Certificate2 LoadRootCertificate()
{ {
var fileName = GetRootCertificatePath(); var fileName = GetRootCertificatePath();
if (!File.Exists(fileName)) return null; if (!File.Exists(fileName)) return null;
...@@ -218,6 +219,51 @@ namespace Titanium.Web.Proxy.Network ...@@ -218,6 +219,51 @@ namespace Titanium.Web.Proxy.Network
TrustRootCertificate(StoreLocation.LocalMachine); TrustRootCertificate(StoreLocation.LocalMachine);
} }
/// <summary>
/// Puts the certificate to the local machine's certificate store.
/// Needs elevated permission. Works only on Windows.
/// </summary>
/// <returns></returns>
public bool TrustRootCertificateAsAdministrator()
{
if (RunTime.IsRunningOnMono())
{
return false;
}
var fileName = Path.GetTempFileName();
File.WriteAllBytes(fileName, RootCertificate.Export(X509ContentType.Pkcs12));
var info = new ProcessStartInfo
{
FileName = "certutil.exe",
Arguments = "-importPFX -p \"\" -f \"" + fileName + "\"",
CreateNoWindow = true,
UseShellExecute = true,
Verb = "runas",
ErrorDialog = false,
};
try
{
var process = Process.Start(info);
if (process == null)
{
return false;
}
process.WaitForExit();
File.Delete(fileName);
}
catch
{
return false;
}
return true;
}
/// <summary> /// <summary>
/// Removes the trusted certificates. /// Removes the trusted certificates.
/// </summary> /// </summary>
...@@ -230,13 +276,49 @@ namespace Titanium.Web.Proxy.Network ...@@ -230,13 +276,49 @@ namespace Titanium.Web.Proxy.Network
RemoveTrustedRootCertificates(StoreLocation.LocalMachine); RemoveTrustedRootCertificates(StoreLocation.LocalMachine);
} }
/// <summary>
/// Determines whether the root certificate is trusted.
/// </summary>
public bool IsRootCertificateTrusted()
{
return FindRootCertificate(StoreLocation.CurrentUser) || IsRootCertificateMachineTrusted();
}
/// <summary>
/// Determines whether the root certificate is machine trusted.
/// </summary>
public bool IsRootCertificateMachineTrusted()
{
return FindRootCertificate(StoreLocation.LocalMachine);
}
private bool FindRootCertificate(StoreLocation storeLocation)
{
string value = $"{RootCertificate.Issuer}";
return FindCertificates(StoreName.Root, storeLocation, value).Count > 0;
}
private X509Certificate2Collection FindCertificates(StoreName storeName, StoreLocation storeLocation, string findValue)
{
X509Store x509Store = new X509Store(storeName, storeLocation);
try
{
x509Store.Open(OpenFlags.OpenExistingOnly);
return x509Store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, findValue, false);
}
finally
{
x509Store.Close();
}
}
/// <summary> /// <summary>
/// Create an SSL certificate /// Create an SSL certificate
/// </summary> /// </summary>
/// <param name="certificateName"></param> /// <param name="certificateName"></param>
/// <param name="isRootCertificate"></param> /// <param name="isRootCertificate"></param>
/// <returns></returns> /// <returns></returns>
internal virtual X509Certificate2 CreateCertificate(string certificateName, bool isRootCertificate) internal X509Certificate2 CreateCertificate(string certificateName, bool isRootCertificate)
{ {
if (certificateCache.ContainsKey(certificateName)) if (certificateCache.ContainsKey(certificateName))
{ {
...@@ -317,7 +399,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -317,7 +399,7 @@ namespace Titanium.Web.Proxy.Network
/// </summary> /// </summary>
/// <param name="storeLocation"></param> /// <param name="storeLocation"></param>
/// <returns></returns> /// <returns></returns>
internal void TrustRootCertificate(StoreLocation storeLocation) private void TrustRootCertificate(StoreLocation storeLocation)
{ {
if (RootCertificate == null) if (RootCertificate == null)
{ {
...@@ -328,7 +410,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -328,7 +410,7 @@ namespace Titanium.Web.Proxy.Network
return; return;
} }
X509Store x509RootStore = new X509Store(StoreName.Root, storeLocation); var x509RootStore = new X509Store(StoreName.Root, storeLocation);
var x509PersonalStore = new X509Store(StoreName.My, storeLocation); var x509PersonalStore = new X509Store(StoreName.My, storeLocation);
try try
...@@ -357,7 +439,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -357,7 +439,7 @@ namespace Titanium.Web.Proxy.Network
/// </summary> /// </summary>
/// <param name="storeLocation"></param> /// <param name="storeLocation"></param>
/// <returns></returns> /// <returns></returns>
internal void RemoveTrustedRootCertificates(StoreLocation storeLocation) private void RemoveTrustedRootCertificates(StoreLocation storeLocation)
{ {
if (RootCertificate == null) if (RootCertificate == null)
{ {
...@@ -368,7 +450,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -368,7 +450,7 @@ namespace Titanium.Web.Proxy.Network
return; return;
} }
X509Store x509RootStore = new X509Store(StoreName.Root, storeLocation); var x509RootStore = new X509Store(StoreName.Root, storeLocation);
var x509PersonalStore = new X509Store(StoreName.My, storeLocation); var x509PersonalStore = new X509Store(StoreName.My, storeLocation);
try try
...@@ -382,7 +464,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -382,7 +464,7 @@ namespace Titanium.Web.Proxy.Network
catch (Exception e) catch (Exception e)
{ {
exceptionFunc( exceptionFunc(
new Exception("Failed to make system trust root certificate " new Exception("Failed to remove root certificate trust "
+ $" for {storeLocation} store location. You may need admin rights.", e)); + $" for {storeLocation} store location. You may need admin rights.", e));
} }
finally finally
...@@ -392,6 +474,9 @@ namespace Titanium.Web.Proxy.Network ...@@ -392,6 +474,9 @@ namespace Titanium.Web.Proxy.Network
} }
} }
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose() public void Dispose()
{ {
} }
......
...@@ -647,46 +647,5 @@ namespace Titanium.Web.Proxy ...@@ -647,46 +647,5 @@ namespace Titanium.Web.Proxy
endPoint.Listener.Server.Close(); endPoint.Listener.Server.Close();
endPoint.Listener.Server.Dispose(); endPoint.Listener.Server.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);
}
} }
} }
...@@ -23,8 +23,13 @@ namespace Titanium.Web.Proxy ...@@ -23,8 +23,13 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
partial class ProxyServer partial class ProxyServer
{ {
//This is called when client is aware of proxy /// <summary>
//So for HTTPS requests client would send CONNECT header to negotiate a secure tcp tunnel via proxy /// This is called when client is aware of proxy
/// So for HTTPS requests client would send CONNECT header to negotiate a secure tcp tunnel via proxy
/// </summary>
/// <param name="endPoint"></param>
/// <param name="tcpClient"></param>
/// <returns></returns>
private async Task HandleClient(ExplicitProxyEndPoint endPoint, TcpClient tcpClient) private async Task HandleClient(ExplicitProxyEndPoint endPoint, TcpClient tcpClient)
{ {
var disposed = false; var disposed = false;
...@@ -139,7 +144,7 @@ namespace Titanium.Web.Proxy ...@@ -139,7 +144,7 @@ namespace Titanium.Web.Proxy
{ {
//Siphon out CONNECT request headers //Siphon out CONNECT request headers
await clientStreamReader.ReadAndIgnoreAllLinesAsync(); await clientStreamReader.ReadAndIgnoreAllLinesAsync();
//write back successfull CONNECT response //write back successfull CONNECT response
await WriteConnectResponse(clientStreamWriter, version); await WriteConnectResponse(clientStreamWriter, version);
...@@ -170,8 +175,13 @@ namespace Titanium.Web.Proxy ...@@ -170,8 +175,13 @@ namespace Titanium.Web.Proxy
} }
} }
//This is called when this proxy acts as a reverse proxy (like a real http server) /// <summary>
//So for HTTPS requests we would start SSL negotiation right away without expecting a CONNECT request from client /// This is called when this proxy acts as a reverse proxy (like a real http server)
/// So for HTTPS requests we would start SSL negotiation right away without expecting a CONNECT request from client
/// </summary>
/// <param name="endPoint"></param>
/// <param name="tcpClient"></param>
/// <returns></returns>
private async Task HandleClient(TransparentProxyEndPoint endPoint, TcpClient tcpClient) private async Task HandleClient(TransparentProxyEndPoint endPoint, TcpClient tcpClient)
{ {
bool disposed = false; bool disposed = false;
...@@ -219,162 +229,10 @@ namespace Titanium.Web.Proxy ...@@ -219,162 +229,10 @@ namespace Titanium.Web.Proxy
} }
} }
/// <summary>
/// Create a Server Connection
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
private async Task<TcpConnection> GetServerConnection(
SessionEventArgs args)
{
ExternalProxy customUpStreamHttpProxy = null;
ExternalProxy customUpStreamHttpsProxy = null;
if (args.WebSession.Request.RequestUri.Scheme == "http")
{
if (GetCustomUpStreamHttpProxyFunc != null)
{
customUpStreamHttpProxy = await GetCustomUpStreamHttpProxyFunc(args);
}
}
else
{
if (GetCustomUpStreamHttpsProxyFunc != null)
{
customUpStreamHttpsProxy = await GetCustomUpStreamHttpsProxyFunc(args);
}
}
args.CustomUpStreamHttpProxyUsed = customUpStreamHttpProxy;
args.CustomUpStreamHttpsProxyUsed = customUpStreamHttpsProxy;
return await tcpConnectionFactory.CreateClient(this,
args.WebSession.Request.RequestUri.Host,
args.WebSession.Request.RequestUri.Port,
args.WebSession.Request.HttpVersion,
args.IsHttps,
customUpStreamHttpProxy ?? UpStreamHttpProxy,
customUpStreamHttpsProxy ?? UpStreamHttpsProxy,
args.ProxyClient.ClientStream);
}
private async Task<bool> HandleHttpSessionRequestInternal(TcpConnection connection, SessionEventArgs args, bool closeConnection)
{
bool disposed = false;
bool keepAlive = false;
try
{
args.WebSession.Request.RequestLocked = true;
//If request was cancelled by user then dispose the client
if (args.WebSession.Request.CancelRequest)
{
return true;
}
//if expect continue is enabled then send the headers first
//and see if server would return 100 conitinue
if (args.WebSession.Request.ExpectContinue)
{
args.WebSession.SetConnection(connection);
await args.WebSession.SendRequest(Enable100ContinueBehaviour);
}
//If 100 continue was the response inform that to the client
if (Enable100ContinueBehaviour)
{
if (args.WebSession.Request.Is100Continue)
{
await WriteResponseStatus(args.WebSession.Response.HttpVersion, "100",
"Continue", args.ProxyClient.ClientStreamWriter);
await args.ProxyClient.ClientStreamWriter.WriteLineAsync();
}
else if (args.WebSession.Request.ExpectationFailed)
{
await WriteResponseStatus(args.WebSession.Response.HttpVersion, "417",
"Expectation Failed", args.ProxyClient.ClientStreamWriter);
await args.ProxyClient.ClientStreamWriter.WriteLineAsync();
}
}
//If expect continue is not enabled then set the connectio and send request headers
if (!args.WebSession.Request.ExpectContinue)
{
args.WebSession.SetConnection(connection);
await args.WebSession.SendRequest(Enable100ContinueBehaviour);
}
//If request was modified by user
if (args.WebSession.Request.RequestBodyRead)
{
if (args.WebSession.Request.ContentEncoding != null)
{
args.WebSession.Request.RequestBody = await GetCompressedResponseBody(args.WebSession.Request.ContentEncoding, args.WebSession.Request.RequestBody);
}
//chunked send is not supported as of now
args.WebSession.Request.ContentLength = args.WebSession.Request.RequestBody.Length;
var newStream = args.WebSession.ServerConnection.Stream;
await newStream.WriteAsync(args.WebSession.Request.RequestBody, 0, args.WebSession.Request.RequestBody.Length);
}
else
{
if (!args.WebSession.Request.ExpectationFailed)
{
//If its a post/put/patch request, then read the client html body and send it to server
var method = args.WebSession.Request.Method.ToUpper();
if (method == "POST" || method == "PUT" || method == "PATCH")
{
await SendClientRequestBody(args);
}
}
}
//If not expectation failed response was returned by server then parse response
if (!args.WebSession.Request.ExpectationFailed)
{
disposed = await HandleHttpSessionResponse(args);
//already disposed inside above method
if (disposed)
{
return true;
}
}
//if connection is closing exit
if (args.WebSession.Response.ResponseKeepAlive == false)
{
return true;
}
if (!closeConnection)
{
keepAlive = true;
return false;
}
}
catch (Exception e)
{
ExceptionFunc(new ProxyHttpException("Error occured whilst handling session request (internal)", e, args));
return true;
}
finally
{
if (!disposed && !keepAlive)
{
//dispose
Dispose(args.ProxyClient.ClientStream, args.ProxyClient.ClientStreamReader, args.ProxyClient.ClientStreamWriter, args.WebSession.ServerConnection);
}
}
return true;
}
/// <summary> /// <summary>
/// This is the core request handler method for a particular connection from client /// This is the core request handler method for a particular connection from client
/// Will create new session (request/response) sequence until
/// client/server abruptly terminates connection or by normal HTTP termination
/// </summary> /// </summary>
/// <param name="client"></param> /// <param name="client"></param>
/// <param name="httpCmd"></param> /// <param name="httpCmd"></param>
...@@ -533,6 +391,168 @@ namespace Titanium.Web.Proxy ...@@ -533,6 +391,168 @@ namespace Titanium.Web.Proxy
return true; return true;
} }
/// <summary>
/// Handle a specific session (request/response sequence)
/// </summary>
/// <param name="connection"></param>
/// <param name="args"></param>
/// <param name="closeConnection"></param>
/// <returns></returns>
private async Task<bool> HandleHttpSessionRequestInternal(TcpConnection connection,
SessionEventArgs args, bool closeConnection)
{
bool disposed = false;
bool keepAlive = false;
try
{
args.WebSession.Request.RequestLocked = true;
//If request was cancelled by user then dispose the client
if (args.WebSession.Request.CancelRequest)
{
return true;
}
//if expect continue is enabled then send the headers first
//and see if server would return 100 conitinue
if (args.WebSession.Request.ExpectContinue)
{
args.WebSession.SetConnection(connection);
await args.WebSession.SendRequest(Enable100ContinueBehaviour);
}
//If 100 continue was the response inform that to the client
if (Enable100ContinueBehaviour)
{
if (args.WebSession.Request.Is100Continue)
{
await WriteResponseStatus(args.WebSession.Response.HttpVersion, "100",
"Continue", args.ProxyClient.ClientStreamWriter);
await args.ProxyClient.ClientStreamWriter.WriteLineAsync();
}
else if (args.WebSession.Request.ExpectationFailed)
{
await WriteResponseStatus(args.WebSession.Response.HttpVersion, "417",
"Expectation Failed", args.ProxyClient.ClientStreamWriter);
await args.ProxyClient.ClientStreamWriter.WriteLineAsync();
}
}
//If expect continue is not enabled then set the connectio and send request headers
if (!args.WebSession.Request.ExpectContinue)
{
args.WebSession.SetConnection(connection);
await args.WebSession.SendRequest(Enable100ContinueBehaviour);
}
//If request was modified by user
if (args.WebSession.Request.RequestBodyRead)
{
if (args.WebSession.Request.ContentEncoding != null)
{
args.WebSession.Request.RequestBody = await GetCompressedResponseBody(args.WebSession.Request.ContentEncoding, args.WebSession.Request.RequestBody);
}
//chunked send is not supported as of now
args.WebSession.Request.ContentLength = args.WebSession.Request.RequestBody.Length;
var newStream = args.WebSession.ServerConnection.Stream;
await newStream.WriteAsync(args.WebSession.Request.RequestBody, 0, args.WebSession.Request.RequestBody.Length);
}
else
{
if (!args.WebSession.Request.ExpectationFailed)
{
//If its a post/put/patch request, then read the client html body and send it to server
var method = args.WebSession.Request.Method.ToUpper();
if (method == "POST" || method == "PUT" || method == "PATCH")
{
await SendClientRequestBody(args);
}
}
}
//If not expectation failed response was returned by server then parse response
if (!args.WebSession.Request.ExpectationFailed)
{
disposed = await HandleHttpSessionResponse(args);
//already disposed inside above method
if (disposed)
{
return true;
}
}
//if connection is closing exit
if (args.WebSession.Response.ResponseKeepAlive == false)
{
return true;
}
if (!closeConnection)
{
keepAlive = true;
return false;
}
}
catch (Exception e)
{
ExceptionFunc(new ProxyHttpException("Error occured whilst handling session request (internal)", e, args));
return true;
}
finally
{
if (!disposed && !keepAlive)
{
//dispose
Dispose(args.ProxyClient.ClientStream, args.ProxyClient.ClientStreamReader, args.ProxyClient.ClientStreamWriter, args.WebSession.ServerConnection);
}
}
return true;
}
/// <summary>
/// Create a Server Connection
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
private async Task<TcpConnection> GetServerConnection(
SessionEventArgs args)
{
ExternalProxy customUpStreamHttpProxy = null;
ExternalProxy customUpStreamHttpsProxy = null;
if (args.WebSession.Request.RequestUri.Scheme == "http")
{
if (GetCustomUpStreamHttpProxyFunc != null)
{
customUpStreamHttpProxy = await GetCustomUpStreamHttpProxyFunc(args);
}
}
else
{
if (GetCustomUpStreamHttpsProxyFunc != null)
{
customUpStreamHttpsProxy = await GetCustomUpStreamHttpsProxyFunc(args);
}
}
args.CustomUpStreamHttpProxyUsed = customUpStreamHttpProxy;
args.CustomUpStreamHttpsProxyUsed = customUpStreamHttpsProxy;
return await tcpConnectionFactory.CreateClient(this,
args.WebSession.Request.RequestUri.Host,
args.WebSession.Request.RequestUri.Port,
args.WebSession.Request.HttpVersion,
args.IsHttps,
customUpStreamHttpProxy ?? UpStreamHttpProxy,
customUpStreamHttpsProxy ?? UpStreamHttpsProxy,
args.ProxyClient.ClientStream);
}
/// <summary> /// <summary>
/// Write successfull CONNECT response to client /// Write successfull CONNECT response to client
/// </summary> /// </summary>
......
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