Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
T
Titanium-Web-Proxy
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
Titanium-Web-Proxy
Commits
11632fe1
Commit
11632fe1
authored
Jul 19, 2017
by
justcoding121
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'beta' into stable
parents
14bdb652
99e3b575
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
959 additions
and
944 deletions
+959
-944
SessionEventArgs.cs
Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs
+4
-2
TunnelConnectEventArgs.cs
Titanium.Web.Proxy/EventArguments/TunnelConnectEventArgs.cs
+4
-2
DotNet45Extensions.cs
Titanium.Web.Proxy/Extensions/DotNet45Extensions.cs
+1
-1
StreamExtensions.cs
Titanium.Web.Proxy/Extensions/StreamExtensions.cs
+2
-2
TcpExtensions.cs
Titanium.Web.Proxy/Extensions/TcpExtensions.cs
+0
-28
HttpRequestWriter.cs
Titanium.Web.Proxy/Helpers/HttpRequestWriter.cs
+2
-1
HttpResponseWriter.cs
Titanium.Web.Proxy/Helpers/HttpResponseWriter.cs
+2
-1
HttpWriter.cs
Titanium.Web.Proxy/Helpers/HttpWriter.cs
+2
-1
Tcp.cs
Titanium.Web.Proxy/Helpers/Tcp.cs
+4
-3
HttpWebClient.cs
Titanium.Web.Proxy/Http/HttpWebClient.cs
+11
-2
TcpConnection.cs
Titanium.Web.Proxy/Network/Tcp/TcpConnection.cs
+6
-0
TcpConnectionFactory.cs
Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs
+11
-7
AssemblyInfo.cs
Titanium.Web.Proxy/Properties/AssemblyInfo.cs
+2
-2
ProxyServer.cs
Titanium.Web.Proxy/ProxyServer.cs
+871
-868
RequestHandler.cs
Titanium.Web.Proxy/RequestHandler.cs
+32
-22
Titanium.Web.Proxy.Standard.csproj
Titanium.Web.Proxy/Titanium.Web.Proxy.Standard.csproj
+4
-1
Titanium.Web.Proxy.csproj
Titanium.Web.Proxy/Titanium.Web.Proxy.csproj
+1
-1
No files found.
Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs
View file @
11632fe1
...
...
@@ -98,13 +98,15 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary>
/// Constructor to initialize the proxy
/// </summary>
internal
SessionEventArgs
(
int
bufferSize
,
ProxyEndPoint
endPoint
,
Func
<
SessionEventArgs
,
Task
>
httpResponseHandler
)
internal
SessionEventArgs
(
int
bufferSize
,
ProxyEndPoint
endPoint
,
Func
<
SessionEventArgs
,
Task
>
httpResponseHandler
)
{
this
.
bufferSize
=
bufferSize
;
this
.
httpResponseHandler
=
httpResponseHandler
;
ProxyClient
=
new
ProxyClient
();
WebSession
=
new
HttpWebClient
();
WebSession
=
new
HttpWebClient
(
bufferSize
);
WebSession
.
ProcessId
=
new
Lazy
<
int
>(()
=>
{
...
...
Titanium.Web.Proxy/EventArguments/TunnelConnectEventArgs.cs
View file @
11632fe1
using
Titanium.Web.Proxy.Models
;
using
System.Net
;
using
Titanium.Web.Proxy.Models
;
namespace
Titanium.Web.Proxy.EventArguments
{
...
...
@@ -6,7 +7,8 @@ namespace Titanium.Web.Proxy.EventArguments
{
public
bool
IsHttpsConnect
{
get
;
set
;
}
public
TunnelConnectSessionEventArgs
(
ProxyEndPoint
endPoint
)
:
base
(
0
,
endPoint
,
null
)
public
TunnelConnectSessionEventArgs
(
int
bufferSize
,
ProxyEndPoint
endPoint
)
:
base
(
bufferSize
,
endPoint
,
null
)
{
}
...
...
Titanium.Web.Proxy/Extensions/DotNet
Standard
Extensions.cs
→
Titanium.Web.Proxy/Extensions/DotNet
45
Extensions.cs
View file @
11632fe1
...
...
@@ -3,7 +3,7 @@ using System.Security.Cryptography.X509Certificates;
namespace
Titanium.Web.Proxy.Extensions
{
internal
static
class
DotNet
Standard
Extensions
internal
static
class
DotNet
45
Extensions
{
#if NET45
/// <summary>
...
...
Titanium.Web.Proxy/Extensions/StreamExtensions.cs
View file @
11632fe1
...
...
@@ -17,9 +17,9 @@ namespace Titanium.Web.Proxy.Extensions
/// <param name="input"></param>
/// <param name="output"></param>
/// <param name="onCopy"></param>
internal
static
async
Task
CopyToAsync
(
this
Stream
input
,
Stream
output
,
Action
<
byte
[],
int
,
int
>
onCopy
)
internal
static
async
Task
CopyToAsync
(
this
Stream
input
,
Stream
output
,
Action
<
byte
[],
int
,
int
>
onCopy
,
int
bufferSize
)
{
byte
[]
buffer
=
new
byte
[
81920
];
byte
[]
buffer
=
new
byte
[
bufferSize
];
while
(
true
)
{
int
num
=
await
input
.
ReadAsync
(
buffer
,
0
,
buffer
.
Length
).
ConfigureAwait
(
false
);
...
...
Titanium.Web.Proxy/Extensions/TcpExtensions.cs
View file @
11632fe1
...
...
@@ -5,34 +5,6 @@ namespace Titanium.Web.Proxy.Extensions
{
internal
static
class
TcpExtensions
{
/// <summary>
/// verifies if the underlying socket is connected before using a TcpClient connection
/// </summary>
/// <param name="client"></param>
/// <returns></returns>
internal
static
bool
IsConnected
(
this
Socket
client
)
{
// This is how you can determine whether a socket is still connected.
bool
blockingState
=
client
.
Blocking
;
try
{
var
tmp
=
new
byte
[
1
];
client
.
Blocking
=
false
;
client
.
Send
(
tmp
,
0
,
0
);
return
true
;
}
catch
(
SocketException
e
)
{
// 10035 == WSAEWOULDBLOCK
return
e
.
SocketErrorCode
==
SocketError
.
WouldBlock
;
}
finally
{
client
.
Blocking
=
blockingState
;
}
}
#if NET45
/// <summary>
...
...
Titanium.Web.Proxy/Helpers/HttpRequestWriter.cs
View file @
11632fe1
...
...
@@ -4,7 +4,8 @@ namespace Titanium.Web.Proxy.Helpers
{
sealed
class
HttpRequestWriter
:
HttpWriter
{
public
HttpRequestWriter
(
Stream
stream
)
:
base
(
stream
,
true
)
public
HttpRequestWriter
(
Stream
stream
,
int
bufferSize
)
:
base
(
stream
,
bufferSize
,
true
)
{
}
}
...
...
Titanium.Web.Proxy/Helpers/HttpResponseWriter.cs
View file @
11632fe1
...
...
@@ -12,7 +12,8 @@ namespace Titanium.Web.Proxy.Helpers
{
sealed
class
HttpResponseWriter
:
HttpWriter
{
public
HttpResponseWriter
(
Stream
stream
)
:
base
(
stream
,
true
)
public
HttpResponseWriter
(
Stream
stream
,
int
bufferSize
)
:
base
(
stream
,
bufferSize
,
true
)
{
}
...
...
Titanium.Web.Proxy/Helpers/HttpWriter.cs
View file @
11632fe1
...
...
@@ -8,7 +8,8 @@ namespace Titanium.Web.Proxy.Helpers
{
abstract
class
HttpWriter
:
StreamWriter
{
protected
HttpWriter
(
Stream
stream
,
bool
leaveOpen
)
:
base
(
stream
,
Encoding
.
ASCII
,
1024
,
leaveOpen
)
protected
HttpWriter
(
Stream
stream
,
int
bufferSize
,
bool
leaveOpen
)
:
base
(
stream
,
Encoding
.
ASCII
,
bufferSize
,
leaveOpen
)
{
NewLine
=
ProxyConstants
.
NewLine
;
}
...
...
Titanium.Web.Proxy/Helpers/Tcp.cs
View file @
11632fe1
...
...
@@ -116,12 +116,13 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="onDataSend"></param>
/// <param name="onDataReceive"></param>
/// <returns></returns>
internal
static
async
Task
SendRaw
(
Stream
clientStream
,
Stream
serverStream
,
Action
<
byte
[],
int
,
int
>
onDataSend
,
Action
<
byte
[],
int
,
int
>
onDataReceive
)
internal
static
async
Task
SendRaw
(
Stream
clientStream
,
Stream
serverStream
,
int
bufferSize
,
Action
<
byte
[],
int
,
int
>
onDataSend
,
Action
<
byte
[],
int
,
int
>
onDataReceive
)
{
//Now async relay all server=>client & client=>server data
var
sendRelay
=
clientStream
.
CopyToAsync
(
serverStream
,
onDataSend
);
var
sendRelay
=
clientStream
.
CopyToAsync
(
serverStream
,
onDataSend
,
bufferSize
);
var
receiveRelay
=
serverStream
.
CopyToAsync
(
clientStream
,
onDataReceive
);
var
receiveRelay
=
serverStream
.
CopyToAsync
(
clientStream
,
onDataReceive
,
bufferSize
);
await
Task
.
WhenAll
(
sendRelay
,
receiveRelay
);
}
...
...
Titanium.Web.Proxy/Http/HttpWebClient.cs
View file @
11632fe1
...
...
@@ -13,6 +13,8 @@ namespace Titanium.Web.Proxy.Http
/// </summary>
public
class
HttpWebClient
:
IDisposable
{
private
int
bufferSize
;
/// <summary>
/// Connection to server
/// </summary>
...
...
@@ -23,6 +25,11 @@ namespace Titanium.Web.Proxy.Http
/// </summary>
public
Guid
RequestId
{
get
;
}
/// <summary>
/// Override UpStreamEndPoint for this request; Local NIC via request is made
/// </summary>
public
IPEndPoint
UpStreamEndPoint
{
get
;
set
;
}
/// <summary>
/// Headers passed with Connect.
/// </summary>
...
...
@@ -49,8 +56,10 @@ namespace Titanium.Web.Proxy.Http
/// </summary>
public
bool
IsHttps
=>
Request
.
IsHttps
;
internal
HttpWebClient
()
internal
HttpWebClient
(
int
bufferSize
)
{
this
.
bufferSize
=
bufferSize
;
RequestId
=
Guid
.
NewGuid
();
Request
=
new
Request
();
Response
=
new
Response
();
...
...
@@ -77,7 +86,7 @@ namespace Titanium.Web.Proxy.Http
byte
[]
requestBytes
;
using
(
var
ms
=
new
MemoryStream
())
using
(
var
writer
=
new
HttpRequestWriter
(
ms
))
using
(
var
writer
=
new
HttpRequestWriter
(
ms
,
bufferSize
))
{
var
upstreamProxy
=
ServerConnection
.
UpStreamHttpProxy
;
...
...
Titanium.Web.Proxy/Network/Tcp/TcpConnection.cs
View file @
11632fe1
using
StreamExtended.Network
;
using
System
;
using
System.Net
;
using
System.Net.Sockets
;
using
Titanium.Web.Proxy.Extensions
;
using
Titanium.Web.Proxy.Models
;
...
...
@@ -25,6 +26,11 @@ namespace Titanium.Web.Proxy.Network.Tcp
internal
bool
UseUpstreamProxy
{
get
;
set
;
}
/// <summary>
/// Local NIC via connection is made
/// </summary>
internal
IPEndPoint
UpStreamEndPoint
{
get
;
set
;
}
/// <summary>
/// Http version
/// </summary>
...
...
Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs
View file @
11632fe1
using
StreamExtended.Network
;
using
System
;
using
System.Linq
;
using
System.Net
;
using
System.Net.Security
;
using
System.Net.Sockets
;
using
System.Text
;
...
...
@@ -17,19 +18,21 @@ namespace Titanium.Web.Proxy.Network.Tcp
internal
class
TcpConnectionFactory
{
/// <summary>
/// Creates a TCP connection to server
///
Creates a TCP connection to server
/// </summary>
/// <param name="server"></param>
/// <param name="remoteHostName"></param>
/// <param name="remotePort"></param>
/// <param name="httpVersion"></param>
/// <param name="isConnect"></param>
/// <param name="isHttps"></param>
/// <param name="isConnect"></param>
/// <param name="upStreamEndPoint"></param>
/// <param name="externalHttpProxy"></param>
/// <param name="externalHttpsProxy"></param>
/// <returns></returns>
internal
async
Task
<
TcpConnection
>
CreateClient
(
ProxyServer
server
,
string
remoteHostName
,
int
remotePort
,
Version
httpVersion
,
bool
isHttps
,
bool
isConnect
,
ExternalProxy
externalHttpProxy
,
ExternalProxy
externalHttpsProxy
)
internal
async
Task
<
TcpConnection
>
CreateClient
(
ProxyServer
server
,
string
remoteHostName
,
int
remotePort
,
Version
httpVersion
,
bool
isHttps
,
bool
isConnect
,
IPEndPoint
upStreamEndPoint
,
ExternalProxy
externalHttpProxy
,
ExternalProxy
externalHttpsProxy
)
{
bool
useUpstreamProxy
=
false
;
var
externalProxy
=
isHttps
?
externalHttpsProxy
:
externalHttpProxy
;
...
...
@@ -52,10 +55,10 @@ namespace Titanium.Web.Proxy.Network.Tcp
try
{
#if NET45
client
=
new
TcpClient
(
server
.
U
pStreamEndPoint
);
client
=
new
TcpClient
(
u
pStreamEndPoint
);
#else
client
=
new
TcpClient
();
client
.
Client
.
Bind
(
server
.
U
pStreamEndPoint
);
client
.
Client
.
Bind
(
u
pStreamEndPoint
);
#endif
//If this proxy uses another external proxy then create a tunnel request for HTTP/HTTPS connections
...
...
@@ -72,7 +75,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
if
(
useUpstreamProxy
&&
(
isConnect
||
isHttps
))
{
using
(
var
writer
=
new
HttpRequestWriter
(
stream
))
using
(
var
writer
=
new
HttpRequestWriter
(
stream
,
server
.
BufferSize
))
{
await
writer
.
WriteLineAsync
(
$"CONNECT
{
remoteHostName
}
:
{
remotePort
}
HTTP/
{
httpVersion
}
"
);
await
writer
.
WriteLineAsync
(
$"Host:
{
remoteHostName
}
:
{
remotePort
}
"
);
...
...
@@ -128,6 +131,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
{
UpStreamHttpProxy
=
externalHttpProxy
,
UpStreamHttpsProxy
=
externalHttpsProxy
,
UpStreamEndPoint
=
upStreamEndPoint
,
HostName
=
remoteHostName
,
Port
=
remotePort
,
IsHttps
=
isHttps
,
...
...
Titanium.Web.Proxy/Properties/AssemblyInfo.cs
View file @
11632fe1
...
...
@@ -6,11 +6,11 @@ using System.Runtime.InteropServices;
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Titanium.Web.Proxy
.Properties
")]
[assembly: AssemblyTitle("Titanium.Web.Proxy")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Titanium.Web.Proxy
.Properties
")]
[assembly: AssemblyProduct("Titanium.Web.Proxy")]
[assembly: AssemblyCopyright("Copyright © Titanium 2015-2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
...
...
Titanium.Web.Proxy/ProxyServer.cs
View file @
11632fe1
using
StreamExtended.Network
;
using
System
;
using
System.Collections.Generic
;
using
System.Linq
;
using
System.Net
;
using
System.Net.Sockets
;
using
System.Security.Authentication
;
using
System.Security.Cryptography.X509Certificates
;
using
System.Threading
;
using
System.Threading.Tasks
;
using
Titanium.Web.Proxy.EventArguments
;
using
Titanium.Web.Proxy.Extensions
;
using
Titanium.Web.Proxy.Helpers
;
#if NET45
using
Titanium.Web.Proxy.Helpers.WinHttp
;
#endif
using
Titanium.Web.Proxy.Models
;
using
Titanium.Web.Proxy.Network
;
using
Titanium.Web.Proxy.Network.Tcp
;
#if NET45
using
Titanium.Web.Proxy.Network.WinAuth.Security
;
#endif
namespace
Titanium.Web.Proxy
{
/// <summary>
/// Proxy Server Main class
/// </summary>
public
partial
class
ProxyServer
:
IDisposable
{
#if NET45
internal
static
readonly
string
UriSchemeHttp
=
Uri
.
UriSchemeHttp
;
internal
static
readonly
string
UriSchemeHttps
=
Uri
.
UriSchemeHttps
;
#else
internal
const
string
UriSchemeHttp
=
"http"
;
internal
const
string
UriSchemeHttps
=
"https"
;
#endif
/// <summary>
/// Is the proxy currently running
/// </summary>
private
bool
proxyRunning
{
get
;
set
;
}
/// <summary>
/// An default exception log func
/// </summary>
private
readonly
Lazy
<
Action
<
Exception
>>
defaultExceptionFunc
=
new
Lazy
<
Action
<
Exception
>>(()
=>
(
e
=>
{
}));
/// <summary>
/// backing exception func for exposed public property
/// </summary>
private
Action
<
Exception
>
exceptionFunc
;
/// <summary>
/// Backing field for corresponding public property
/// </summary>
private
bool
trustRootCertificate
;
/// <summary>
/// Backing field for corresponding public property
/// </summary>
private
int
clientConnectionCount
;
/// <summary>
/// Backing field for corresponding public property
/// </summary>
private
int
serverConnectionCount
;
/// <summary>
/// A object that creates tcp connection to server
/// </summary>
private
TcpConnectionFactory
tcpConnectionFactory
{
get
;
}
#if NET45
private
WinHttpWebProxyFinder
systemProxyResolver
;
/// <summary>
/// Manage system proxy settings
/// </summary>
private
SystemProxyManager
systemProxySettingsManager
{
get
;
}
/// <summary>
/// Set firefox to use default system proxy
/// </summary>
private
readonly
FireFoxProxySettingsManager
firefoxProxySettingsManager
=
new
FireFoxProxySettingsManager
();
#endif
/// <summary>
/// Buffer size used throughout this proxy
/// </summary>
public
int
BufferSize
{
get
;
set
;
}
=
8192
;
/// <summary>
/// Manages certificates used by this proxy
/// </summary>
public
CertificateManager
CertificateManager
{
get
;
}
/// <summary>
/// The root certificate
/// </summary>
public
X509Certificate2
RootCertificate
{
get
{
return
CertificateManager
.
RootCertificate
;
}
set
{
CertificateManager
.
RootCertificate
=
value
;
}
}
/// <summary>
/// Name of the root certificate issuer
/// (This is valid only when RootCertificate property is not set)
/// </summary>
public
string
RootCertificateIssuerName
{
get
{
return
CertificateManager
.
Issuer
;
}
set
{
CertificateManager
.
Issuer
=
value
;
}
}
/// <summary>
/// Name of the root certificate
/// (This is valid only when RootCertificate property is not set)
/// If no certificate is provided then a default Root Certificate will be created and used
/// The provided root certificate will be stored in proxy exe directory with the private key
/// Root certificate file will be named as "rootCert.pfx"
/// </summary>
public
string
RootCertificateName
{
get
{
return
CertificateManager
.
RootCertificateName
;
}
set
{
CertificateManager
.
RootCertificateName
=
value
;
}
}
/// <summary>
/// Trust the RootCertificate used by this proxy server
/// Note that this do not make the client trust the certificate!
/// This would import the root certificate to the certificate store of machine that runs this proxy server
/// </summary>
public
bool
TrustRootCertificate
{
get
{
return
trustRootCertificate
;
}
set
{
trustRootCertificate
=
value
;
if
(
value
)
{
EnsureRootCertificate
();
}
}
}
/// <summary>
/// Select Certificate Engine
/// Optionally set to BouncyCastle
/// Mono only support BouncyCastle and it is the default
/// </summary>
public
CertificateEngine
CertificateEngine
{
get
{
return
CertificateManager
.
Engine
;
}
set
{
CertificateManager
.
Engine
=
value
;
}
}
/// <summary>
/// Should we check for certificare revocation during SSL authentication to servers
/// Note: If enabled can reduce performance (Default disabled)
/// </summary>
public
bool
CheckCertificateRevocation
{
get
;
set
;
}
/// <summary>
/// Does this proxy uses the HTTP protocol 100 continue behaviour strictly?
/// Broken 100 contunue implementations on server/client may cause problems if enabled
/// </summary>
public
bool
Enable100ContinueBehaviour
{
get
;
set
;
}
/// <summary>
/// Minutes certificates should be kept in cache when not used
/// </summary>
public
int
CertificateCacheTimeOutMinutes
{
get
;
set
;
}
/// <summary>
/// Seconds client/server connection are to be kept alive when waiting for read/write to complete
/// </summary>
public
int
ConnectionTimeOutSeconds
{
get
;
set
;
}
/// <summary>
/// Intercept request to server
/// </summary>
public
event
Func
<
object
,
SessionEventArgs
,
Task
>
BeforeRequest
;
/// <summary>
/// Intercept response from server
/// </summary>
public
event
Func
<
object
,
SessionEventArgs
,
Task
>
BeforeResponse
;
/// <summary>
/// Intercept tunnel connect reques
/// </summary>
public
event
Func
<
object
,
TunnelConnectSessionEventArgs
,
Task
>
TunnelConnectRequest
;
/// <summary>
/// Intercept tunnel connect response
/// </summary>
public
event
Func
<
object
,
TunnelConnectSessionEventArgs
,
Task
>
TunnelConnectResponse
;
/// <summary>
/// Occurs when client connection count changed.
/// </summary>
public
event
EventHandler
ClientConnectionCountChanged
;
/// <summary>
/// Occurs when server connection count changed.
/// </summary>
public
event
EventHandler
ServerConnectionCountChanged
;
/// <summary>
/// External proxy for Http
/// </summary>
public
ExternalProxy
UpStreamHttpProxy
{
get
;
set
;
}
/// <summary>
/// External proxy for Http
/// </summary>
public
ExternalProxy
UpStreamHttpsProxy
{
get
;
set
;
}
/// <summary>
/// Local adapter/NIC endpoint (where proxy makes request via)
/// default via any IP addresses of this machine
/// </summary>
public
IPEndPoint
UpStreamEndPoint
{
get
;
set
;
}
=
new
IPEndPoint
(
IPAddress
.
Any
,
0
);
/// <summary>
/// Is the proxy currently running
/// </summary>
public
bool
ProxyRunning
=>
proxyRunning
;
/// <summary>
/// Gets or sets a value indicating whether requests will be chained to upstream gateway.
/// </summary>
public
bool
ForwardToUpstreamGateway
{
get
;
set
;
}
/// <summary>
/// Enable disable Windows Authentication (NTLM/Kerberos)
/// Note: NTLM/Kerberos will always send local credentials of current user
/// who is running the proxy process. This is because a man
/// in middle attack is not currently supported
/// (which would require windows delegation enabled for this server process)
/// </summary>
public
bool
EnableWinAuth
{
get
;
set
;
}
/// <summary>
/// Verifies the remote Secure Sockets Layer (SSL) certificate used for authentication
/// </summary>
public
event
Func
<
object
,
CertificateValidationEventArgs
,
Task
>
ServerCertificateValidationCallback
;
/// <summary>
/// Callback tooverride client certificate during SSL mutual authentication
/// </summary>
public
event
Func
<
object
,
CertificateSelectionEventArgs
,
Task
>
ClientCertificateSelectionCallback
;
/// <summary>
/// Callback for error events in proxy
/// </summary>
public
Action
<
Exception
>
ExceptionFunc
{
get
{
return
exceptionFunc
??
defaultExceptionFunc
.
Value
;
}
set
{
exceptionFunc
=
value
;
}
}
/// <summary>
/// A callback to authenticate clients
/// Parameters are username, password provided by client
/// return true for successful authentication
/// </summary>
public
Func
<
string
,
string
,
Task
<
bool
>>
AuthenticateUserFunc
{
get
;
set
;
}
/// <summary>
/// Realm used during Proxy Basic Authentication
/// </summary>
public
string
ProxyRealm
{
get
;
set
;
}
=
"TitaniumProxy"
;
/// <summary>
/// A callback to provide authentication credentials for up stream proxy this proxy is using for HTTP requests
/// return the ExternalProxy object with valid credentials
/// </summary>
public
Func
<
SessionEventArgs
,
Task
<
ExternalProxy
>>
GetCustomUpStreamHttpProxyFunc
{
get
;
set
;
}
/// <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
;
set
;
}
/// <summary>
/// A list of IpAddress and port this proxy is listening to
/// </summary>
using
StreamExtended.Network
;
using
System
;
using
System.Collections.Generic
;
using
System.Linq
;
using
System.Net
;
using
System.Net.Sockets
;
using
System.Security.Authentication
;
using
System.Security.Cryptography.X509Certificates
;
using
System.Threading
;
using
System.Threading.Tasks
;
using
Titanium.Web.Proxy.EventArguments
;
using
Titanium.Web.Proxy.Extensions
;
using
Titanium.Web.Proxy.Helpers
;
#if NET45
using
Titanium.Web.Proxy.Helpers.WinHttp
;
#endif
using
Titanium.Web.Proxy.Models
;
using
Titanium.Web.Proxy.Network
;
using
Titanium.Web.Proxy.Network.Tcp
;
#if NET45
using
Titanium.Web.Proxy.Network.WinAuth.Security
;
#endif
namespace
Titanium.Web.Proxy
{
/// <summary>
/// Proxy Server Main class
/// </summary>
public
partial
class
ProxyServer
:
IDisposable
{
#if NET45
internal
static
readonly
string
UriSchemeHttp
=
Uri
.
UriSchemeHttp
;
internal
static
readonly
string
UriSchemeHttps
=
Uri
.
UriSchemeHttps
;
#else
internal
const
string
UriSchemeHttp
=
"http"
;
internal
const
string
UriSchemeHttps
=
"https"
;
#endif
/// <summary>
/// Is the proxy currently running
/// </summary>
private
bool
proxyRunning
{
get
;
set
;
}
/// <summary>
/// An default exception log func
/// </summary>
private
readonly
Lazy
<
Action
<
Exception
>>
defaultExceptionFunc
=
new
Lazy
<
Action
<
Exception
>>(()
=>
(
e
=>
{
}));
/// <summary>
/// backing exception func for exposed public property
/// </summary>
private
Action
<
Exception
>
exceptionFunc
;
/// <summary>
/// Backing field for corresponding public property
/// </summary>
private
bool
trustRootCertificate
;
/// <summary>
/// Backing field for corresponding public property
/// </summary>
private
int
clientConnectionCount
;
/// <summary>
/// Backing field for corresponding public property
/// </summary>
private
int
serverConnectionCount
;
/// <summary>
/// A object that creates tcp connection to server
/// </summary>
private
TcpConnectionFactory
tcpConnectionFactory
{
get
;
}
#if NET45
private
WinHttpWebProxyFinder
systemProxyResolver
;
/// <summary>
/// Manage system proxy settings
/// </summary>
private
SystemProxyManager
systemProxySettingsManager
{
get
;
}
/// <summary>
/// Set firefox to use default system proxy
/// </summary>
private
readonly
FireFoxProxySettingsManager
firefoxProxySettingsManager
=
new
FireFoxProxySettingsManager
();
#endif
/// <summary>
/// Buffer size used throughout this proxy
/// </summary>
public
int
BufferSize
{
get
;
set
;
}
=
8192
;
/// <summary>
/// Manages certificates used by this proxy
/// </summary>
public
CertificateManager
CertificateManager
{
get
;
}
/// <summary>
/// The root certificate
/// </summary>
public
X509Certificate2
RootCertificate
{
get
{
return
CertificateManager
.
RootCertificate
;
}
set
{
CertificateManager
.
RootCertificate
=
value
;
}
}
/// <summary>
/// Name of the root certificate issuer
/// (This is valid only when RootCertificate property is not set)
/// </summary>
public
string
RootCertificateIssuerName
{
get
{
return
CertificateManager
.
Issuer
;
}
set
{
CertificateManager
.
Issuer
=
value
;
}
}
/// <summary>
/// Name of the root certificate
/// (This is valid only when RootCertificate property is not set)
/// If no certificate is provided then a default Root Certificate will be created and used
/// The provided root certificate will be stored in proxy exe directory with the private key
/// Root certificate file will be named as "rootCert.pfx"
/// </summary>
public
string
RootCertificateName
{
get
{
return
CertificateManager
.
RootCertificateName
;
}
set
{
CertificateManager
.
RootCertificateName
=
value
;
}
}
/// <summary>
/// Trust the RootCertificate used by this proxy server
/// Note that this do not make the client trust the certificate!
/// This would import the root certificate to the certificate store of machine that runs this proxy server
/// </summary>
public
bool
TrustRootCertificate
{
get
{
return
trustRootCertificate
;
}
set
{
trustRootCertificate
=
value
;
if
(
value
)
{
EnsureRootCertificate
();
}
}
}
/// <summary>
/// Select Certificate Engine
/// Optionally set to BouncyCastle
/// Mono only support BouncyCastle and it is the default
/// </summary>
public
CertificateEngine
CertificateEngine
{
get
{
return
CertificateManager
.
Engine
;
}
set
{
CertificateManager
.
Engine
=
value
;
}
}
/// <summary>
/// Should we check for certificare revocation during SSL authentication to servers
/// Note: If enabled can reduce performance (Default disabled)
/// </summary>
public
bool
CheckCertificateRevocation
{
get
;
set
;
}
/// <summary>
/// Does this proxy uses the HTTP protocol 100 continue behaviour strictly?
/// Broken 100 contunue implementations on server/client may cause problems if enabled
/// </summary>
public
bool
Enable100ContinueBehaviour
{
get
;
set
;
}
/// <summary>
/// Minutes certificates should be kept in cache when not used
/// </summary>
public
int
CertificateCacheTimeOutMinutes
{
get
;
set
;
}
/// <summary>
/// Seconds client/server connection are to be kept alive when waiting for read/write to complete
/// </summary>
public
int
ConnectionTimeOutSeconds
{
get
;
set
;
}
/// <summary>
/// Intercept request to server
/// </summary>
public
event
Func
<
object
,
SessionEventArgs
,
Task
>
BeforeRequest
;
/// <summary>
/// Intercept response from server
/// </summary>
public
event
Func
<
object
,
SessionEventArgs
,
Task
>
BeforeResponse
;
/// <summary>
/// Intercept tunnel connect reques
/// </summary>
public
event
Func
<
object
,
TunnelConnectSessionEventArgs
,
Task
>
TunnelConnectRequest
;
/// <summary>
/// Intercept tunnel connect response
/// </summary>
public
event
Func
<
object
,
TunnelConnectSessionEventArgs
,
Task
>
TunnelConnectResponse
;
/// <summary>
/// Occurs when client connection count changed.
/// </summary>
public
event
EventHandler
ClientConnectionCountChanged
;
/// <summary>
/// Occurs when server connection count changed.
/// </summary>
public
event
EventHandler
ServerConnectionCountChanged
;
/// <summary>
/// External proxy for Http
/// </summary>
public
ExternalProxy
UpStreamHttpProxy
{
get
;
set
;
}
/// <summary>
/// External proxy for Http
/// </summary>
public
ExternalProxy
UpStreamHttpsProxy
{
get
;
set
;
}
/// <summary>
/// Local adapter/NIC endpoint (where proxy makes request via)
/// default via any IP addresses of this machine
/// </summary>
public
IPEndPoint
UpStreamEndPoint
{
get
;
set
;
}
=
new
IPEndPoint
(
IPAddress
.
Any
,
0
);
/// <summary>
/// Is the proxy currently running
/// </summary>
public
bool
ProxyRunning
=>
proxyRunning
;
/// <summary>
/// Gets or sets a value indicating whether requests will be chained to upstream gateway.
/// </summary>
public
bool
ForwardToUpstreamGateway
{
get
;
set
;
}
/// <summary>
/// Enable disable Windows Authentication (NTLM/Kerberos)
/// Note: NTLM/Kerberos will always send local credentials of current user
/// who is running the proxy process. This is because a man
/// in middle attack is not currently supported
/// (which would require windows delegation enabled for this server process)
/// </summary>
public
bool
EnableWinAuth
{
get
;
set
;
}
/// <summary>
/// Verifies the remote Secure Sockets Layer (SSL) certificate used for authentication
/// </summary>
public
event
Func
<
object
,
CertificateValidationEventArgs
,
Task
>
ServerCertificateValidationCallback
;
/// <summary>
/// Callback tooverride client certificate during SSL mutual authentication
/// </summary>
public
event
Func
<
object
,
CertificateSelectionEventArgs
,
Task
>
ClientCertificateSelectionCallback
;
/// <summary>
/// Callback for error events in proxy
/// </summary>
public
Action
<
Exception
>
ExceptionFunc
{
get
{
return
exceptionFunc
??
defaultExceptionFunc
.
Value
;
}
set
{
exceptionFunc
=
value
;
}
}
/// <summary>
/// A callback to authenticate clients
/// Parameters are username, password provided by client
/// return true for successful authentication
/// </summary>
public
Func
<
string
,
string
,
Task
<
bool
>>
AuthenticateUserFunc
{
get
;
set
;
}
/// <summary>
/// Realm used during Proxy Basic Authentication
/// </summary>
public
string
ProxyRealm
{
get
;
set
;
}
=
"TitaniumProxy"
;
/// <summary>
/// A callback to provide authentication credentials for up stream proxy this proxy is using for HTTP requests
/// return the ExternalProxy object with valid credentials
/// </summary>
public
Func
<
SessionEventArgs
,
Task
<
ExternalProxy
>>
GetCustomUpStreamHttpProxyFunc
{
get
;
set
;
}
/// <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
;
set
;
}
/// <summary>
/// A list of IpAddress and port this proxy is listening to
/// </summary>
public
List
<
ProxyEndPoint
>
ProxyEndPoints
{
get
;
set
;
}
/// <summary>
/// List of supported Ssl versions
/// </summary>
#if NET45
public
SslProtocols
SupportedSslProtocols
{
get
;
set
;
}
=
SslProtocols
.
Tls
|
SslProtocols
.
Tls11
|
SslProtocols
.
Tls12
|
SslProtocols
.
Ssl3
;
#else
public
SslProtocols
SupportedSslProtocols
{
get
;
set
;
}
=
SslProtocols
.
Tls
|
SslProtocols
.
Tls11
|
SslProtocols
.
Tls12
;
#endif
/// <summary>
/// Total number of active client connections
/// </summary>
public
int
ClientConnectionCount
=>
clientConnectionCount
;
/// <summary>
/// Total number of active server connections
/// </summary>
public
int
ServerConnectionCount
=>
serverConnectionCount
;
/// <summary>
/// Constructor
/// </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
)
{
//default values
ConnectionTimeOutSeconds
=
30
;
CertificateCacheTimeOutMinutes
=
60
;
ProxyEndPoints
=
new
List
<
ProxyEndPoint
>();
tcpConnectionFactory
=
new
TcpConnectionFactory
();
#if NET45
if
(!
RunTime
.
IsRunningOnMono
)
{
systemProxySettingsManager
=
new
SystemProxyManager
();
}
#endif
CertificateManager
=
new
CertificateManager
(
ExceptionFunc
);
if
(
rootCertificateName
!=
null
)
{
RootCertificateName
=
rootCertificateName
;
}
if
(
rootCertificateIssuerName
!=
null
)
{
RootCertificateIssuerName
=
rootCertificateIssuerName
;
}
}
/// <summary>
/// Add a proxy end point
/// </summary>
/// <param name="endPoint"></param>
public
void
AddEndPoint
(
ProxyEndPoint
endPoint
)
{
if
(
ProxyEndPoints
.
Any
(
x
=>
x
.
IpAddress
.
Equals
(
endPoint
.
IpAddress
)
&&
endPoint
.
Port
!=
0
&&
x
.
Port
==
endPoint
.
Port
))
{
throw
new
Exception
(
"Cannot add another endpoint to same port & ip address"
);
}
ProxyEndPoints
.
Add
(
endPoint
);
if
(
proxyRunning
)
{
Listen
(
endPoint
);
}
}
/// <summary>
/// Remove a proxy end point
/// Will throw error if the end point does'nt exist
/// </summary>
/// <param name="endPoint"></param>
public
void
RemoveEndPoint
(
ProxyEndPoint
endPoint
)
{
if
(
ProxyEndPoints
.
Contains
(
endPoint
)
==
false
)
{
throw
new
Exception
(
"Cannot remove endPoints not added to proxy"
);
}
ProxyEndPoints
.
Remove
(
endPoint
);
if
(
proxyRunning
)
{
QuitListen
(
endPoint
);
}
}
#if NET45
/// <summary>
/// Set the given explicit end point as the default proxy server for current machine
/// </summary>
/// <param name="endPoint"></param>
public
void
SetAsSystemHttpProxy
(
ExplicitProxyEndPoint
endPoint
)
{
SetAsSystemProxy
(
endPoint
,
ProxyProtocolType
.
Http
);
}
/// <summary>
/// Set the given explicit end point as the default proxy server for current machine
/// </summary>
/// <param name="endPoint"></param>
public
void
SetAsSystemHttpsProxy
(
ExplicitProxyEndPoint
endPoint
)
{
SetAsSystemProxy
(
endPoint
,
ProxyProtocolType
.
Https
);
}
/// <summary>
/// Set the given explicit end point as the default proxy server for current machine
/// </summary>
/// <param name="endPoint"></param>
/// <param name="protocolType"></param>
public
void
SetAsSystemProxy
(
ExplicitProxyEndPoint
endPoint
,
ProxyProtocolType
protocolType
)
{
if
(
RunTime
.
IsRunningOnMono
)
{
throw
new
Exception
(
"Mono Runtime do not support system proxy settings."
);
}
ValidateEndPointAsSystemProxy
(
endPoint
);
bool
isHttp
=
(
protocolType
&
ProxyProtocolType
.
Http
)
>
0
;
bool
isHttps
=
(
protocolType
&
ProxyProtocolType
.
Https
)
>
0
;
if
(
isHttps
)
{
if
(!
endPoint
.
EnableSsl
)
{
throw
new
Exception
(
"Endpoint do not support Https connections"
);
}
EnsureRootCertificate
();
//If certificate was trusted by the machine
if
(!
CertificateManager
.
CertValidated
)
{
protocolType
=
protocolType
&
~
ProxyProtocolType
.
Https
;
isHttps
=
false
;
}
}
//clear any settings previously added
if
(
isHttp
)
{
ProxyEndPoints
.
OfType
<
ExplicitProxyEndPoint
>().
ToList
().
ForEach
(
x
=>
x
.
IsSystemHttpProxy
=
false
);
}
if
(
isHttps
)
{
ProxyEndPoints
.
OfType
<
ExplicitProxyEndPoint
>().
ToList
().
ForEach
(
x
=>
x
.
IsSystemHttpsProxy
=
false
);
}
systemProxySettingsManager
.
SetProxy
(
Equals
(
endPoint
.
IpAddress
,
IPAddress
.
Any
)
|
Equals
(
endPoint
.
IpAddress
,
IPAddress
.
Loopback
)
?
"127.0.0.1"
:
endPoint
.
IpAddress
.
ToString
(),
endPoint
.
Port
,
protocolType
);
if
(
isHttp
)
{
endPoint
.
IsSystemHttpsProxy
=
true
;
}
if
(
isHttps
)
{
endPoint
.
IsSystemHttpsProxy
=
true
;
}
firefoxProxySettingsManager
.
UseSystemProxy
();
string
proxyType
=
null
;
switch
(
protocolType
)
{
case
ProxyProtocolType
.
Http
:
proxyType
=
"HTTP"
;
break
;
case
ProxyProtocolType
.
Https
:
proxyType
=
"HTTPS"
;
break
;
case
ProxyProtocolType
.
AllHttp
:
proxyType
=
"HTTP and HTTPS"
;
break
;
}
if
(
protocolType
!=
ProxyProtocolType
.
None
)
{
Console
.
WriteLine
(
"Set endpoint at Ip {0} and port: {1} as System {2} Proxy"
,
endPoint
.
IpAddress
,
endPoint
.
Port
,
proxyType
);
}
}
/// <summary>
/// Remove any HTTP proxy setting of current machien
/// </summary>
public
void
DisableSystemHttpProxy
()
{
DisableSystemProxy
(
ProxyProtocolType
.
Http
);
}
/// <summary>
/// Remove any HTTPS proxy setting for current machine
/// </summary>
public
void
DisableSystemHttpsProxy
()
{
DisableSystemProxy
(
ProxyProtocolType
.
Https
);
}
/// <summary>
/// Remove the specified proxy settings for current machine
/// </summary>
public
void
DisableSystemProxy
(
ProxyProtocolType
protocolType
)
{
if
(
RunTime
.
IsRunningOnMono
)
{
throw
new
Exception
(
"Mono Runtime do not support system proxy settings."
);
}
systemProxySettingsManager
.
RemoveProxy
(
protocolType
);
}
/// <summary>
/// Clear all proxy settings for current machine
/// </summary>
public
void
DisableAllSystemProxies
()
{
if
(
RunTime
.
IsRunningOnMono
)
{
throw
new
Exception
(
"Mono Runtime do not support system proxy settings."
);
}
systemProxySettingsManager
.
DisableAllProxy
();
}
#endif
/// <summary>
/// Start this proxy server
/// </summary>
public
void
Start
()
{
if
(
proxyRunning
)
{
throw
new
Exception
(
"Proxy is already running."
);
}
#if NET45
//clear any system proxy settings which is pointing to our own endpoint
//due to non gracious proxy shutdown before
if
(
systemProxySettingsManager
!=
null
)
{
var
proxyInfo
=
systemProxySettingsManager
.
GetProxyInfoFromRegistry
();
if
(
proxyInfo
.
Proxies
!=
null
)
{
var
protocolToRemove
=
ProxyProtocolType
.
None
;
foreach
(
var
proxy
in
proxyInfo
.
Proxies
.
Values
)
{
if
(
proxy
.
HostName
==
"127.0.0.1"
&&
ProxyEndPoints
.
Any
(
x
=>
x
.
Port
==
proxy
.
Port
))
{
protocolToRemove
|=
proxy
.
ProtocolType
;
}
}
if
(
protocolToRemove
!=
ProxyProtocolType
.
None
)
{
//do not restore to any of listening address when we quit
systemProxySettingsManager
.
RemoveProxy
(
protocolToRemove
,
false
);
}
}
}
if
(
ForwardToUpstreamGateway
&&
GetCustomUpStreamHttpProxyFunc
==
null
&&
GetCustomUpStreamHttpsProxyFunc
==
null
&&
systemProxySettingsManager
!=
null
)
{
// Use WinHttp to handle PAC/WAPD scripts.
systemProxyResolver
=
new
WinHttpWebProxyFinder
();
systemProxyResolver
.
LoadFromIE
();
GetCustomUpStreamHttpProxyFunc
=
GetSystemUpStreamProxy
;
GetCustomUpStreamHttpsProxyFunc
=
GetSystemUpStreamProxy
;
}
#endif
foreach
(
var
endPoint
in
ProxyEndPoints
)
{
Listen
(
endPoint
);
}
CertificateManager
.
ClearIdleCertificates
(
CertificateCacheTimeOutMinutes
);
#if NET45
if
(!
RunTime
.
IsRunningOnMono
)
{
//clear orphaned windows auth states every 2 minutes
WinAuthEndPoint
.
ClearIdleStates
(
2
);
}
#endif
proxyRunning
=
true
;
}
/// <summary>
/// Stop this proxy server
/// </summary>
public
void
Stop
()
{
if
(!
proxyRunning
)
{
throw
new
Exception
(
"Proxy is not running."
);
}
#if NET45
if
(!
RunTime
.
IsRunningOnMono
)
{
bool
setAsSystemProxy
=
ProxyEndPoints
.
OfType
<
ExplicitProxyEndPoint
>().
Any
(
x
=>
x
.
IsSystemHttpProxy
||
x
.
IsSystemHttpsProxy
);
if
(
setAsSystemProxy
)
{
systemProxySettingsManager
.
RestoreOriginalSettings
();
}
}
#endif
foreach
(
var
endPoint
in
ProxyEndPoints
)
{
QuitListen
(
endPoint
);
}
ProxyEndPoints
.
Clear
();
CertificateManager
?.
StopClearIdleCertificates
();
proxyRunning
=
false
;
}
/// <summary>
/// Handle dispose of a client/server session
/// </summary>
/// <param name="clientStream"></param>
/// <param name="clientStreamReader"></param>
/// <param name="clientStreamWriter"></param>
/// <param name="serverConnection"></param>
private
void
Dispose
(
CustomBufferedStream
clientStream
,
CustomBinaryReader
clientStreamReader
,
HttpResponseWriter
clientStreamWriter
,
TcpConnection
serverConnection
)
{
clientStream
?.
Dispose
();
clientStreamReader
?.
Dispose
();
clientStreamWriter
?.
Dispose
();
if
(
serverConnection
!=
null
)
{
serverConnection
.
Dispose
();
UpdateServerConnectionCount
(
false
);
}
}
/// <summary>
/// Dispose Proxy.
/// </summary>
public
void
Dispose
()
{
if
(
proxyRunning
)
{
Stop
();
}
CertificateManager
?.
Dispose
();
}
#if NET45
/// <summary>
/// Listen on the given end point on local machine
/// </summary>
/// <param name="endPoint"></param>
private
void
Listen
(
ProxyEndPoint
endPoint
)
{
endPoint
.
Listener
=
new
TcpListener
(
endPoint
.
IpAddress
,
endPoint
.
Port
);
endPoint
.
Listener
.
Start
();
endPoint
.
Port
=
((
IPEndPoint
)
endPoint
.
Listener
.
LocalEndpoint
).
Port
;
// accept clients asynchronously
endPoint
.
Listener
.
BeginAcceptTcpClient
(
OnAcceptConnection
,
endPoint
);
}
#else
private
async
void
Listen
(
ProxyEndPoint
endPoint
)
{
endPoint
.
Listener
=
new
TcpListener
(
endPoint
.
IpAddress
,
endPoint
.
Port
);
endPoint
.
Listener
.
Start
();
endPoint
.
Port
=
((
IPEndPoint
)
endPoint
.
Listener
.
LocalEndpoint
).
Port
;
while
(
true
)
{
TcpClient
tcpClient
=
await
endPoint
.
Listener
.
AcceptTcpClientAsync
();
if
(
tcpClient
!=
null
)
Task
.
Run
(
async
()
=>
HandleClient
(
tcpClient
,
endPoint
));
}
}
#endif
/// <summary>
/// Verifiy if its safe to set this end point as System proxy
/// </summary>
/// <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"
);
}
if
(!
proxyRunning
)
{
throw
new
Exception
(
"Cannot set system proxy settings before proxy has been started."
);
}
}
#if NET45
/// <summary>
/// Gets the system up stream proxy.
/// </summary>
/// <param name="sessionEventArgs">The <see cref="SessionEventArgs"/> instance containing the event data.</param>
/// <returns><see cref="ExternalProxy"/> instance containing valid proxy configuration from PAC/WAPD scripts if any exists.</returns>
private
Task
<
ExternalProxy
>
GetSystemUpStreamProxy
(
SessionEventArgs
sessionEventArgs
)
{
var
proxy
=
systemProxyResolver
.
GetProxy
(
sessionEventArgs
.
WebSession
.
Request
.
RequestUri
);
return
Task
.
FromResult
(
proxy
);
}
#endif
private
void
EnsureRootCertificate
()
{
if
(!
CertificateManager
.
CertValidated
)
{
CertificateManager
.
CreateTrustedRootCertificate
();
if
(
TrustRootCertificate
)
{
CertificateManager
.
TrustRootCertificate
();
}
}
}
#if NET45
/// <summary>
/// When a connection is received from client act
/// </summary>
/// <param name="asyn"></param>
private
void
OnAcceptConnection
(
IAsyncResult
asyn
)
{
var
endPoint
=
(
ProxyEndPoint
)
asyn
.
AsyncState
;
TcpClient
tcpClient
=
null
;
try
{
//based on end point type call appropriate request handlers
tcpClient
=
endPoint
.
Listener
.
EndAcceptTcpClient
(
asyn
);
}
catch
(
ObjectDisposedException
)
{
// The listener was Stop()'d, disposing the underlying socket and
// triggering the completion of the callback. We're already exiting,
// so just return.
return
;
}
catch
{
//Other errors are discarded to keep proxy running
}
if
(
tcpClient
!=
null
)
{
Task
.
Run
(
async
()
=>
{
await
HandleClient
(
tcpClient
,
endPoint
);
});
}
// Get the listener that handles the client request.
endPoint
.
Listener
.
BeginAcceptTcpClient
(
OnAcceptConnection
,
endPoint
);
}
#endif
private
async
Task
HandleClient
(
TcpClient
tcpClient
,
ProxyEndPoint
endPoint
)
{
UpdateClientConnectionCount
(
true
);
tcpClient
.
ReceiveTimeout
=
ConnectionTimeOutSeconds
*
1000
;
tcpClient
.
SendTimeout
=
ConnectionTimeOutSeconds
*
1000
;
try
{
if
(
endPoint
.
GetType
()
==
typeof
(
TransparentProxyEndPoint
))
{
await
HandleClient
(
endPoint
as
TransparentProxyEndPoint
,
tcpClient
);
}
else
{
await
HandleClient
(
endPoint
as
ExplicitProxyEndPoint
,
tcpClient
);
}
}
finally
{
UpdateClientConnectionCount
(
false
);
try
{
if
(
tcpClient
!=
null
)
{
//This line is important!
//contributors please don't remove it without discussion
//It helps to avoid eventual deterioration of performance due to TCP port exhaustion
//due to default TCP CLOSE_WAIT timeout for 4 minutes
tcpClient
.
LingerState
=
new
LingerOption
(
true
,
0
);
tcpClient
.
Dispose
();
}
}
catch
{
}
}
}
/// <summary>
/// Quit listening on the given end point
/// </summary>
/// <param name="endPoint"></param>
private
void
QuitListen
(
ProxyEndPoint
endPoint
)
{
endPoint
.
Listener
.
Stop
();
endPoint
.
Listener
.
Server
.
Dispose
();
}
internal
void
UpdateClientConnectionCount
(
bool
increment
)
{
if
(
increment
)
{
Interlocked
.
Increment
(
ref
clientConnectionCount
);
}
else
{
Interlocked
.
Decrement
(
ref
clientConnectionCount
);
}
ClientConnectionCountChanged
?.
Invoke
(
this
,
EventArgs
.
Empty
);
}
internal
void
UpdateServerConnectionCount
(
bool
increment
)
{
if
(
increment
)
{
Interlocked
.
Increment
(
ref
serverConnectionCount
);
}
else
{
Interlocked
.
Decrement
(
ref
serverConnectionCount
);
}
ServerConnectionCountChanged
?.
Invoke
(
this
,
EventArgs
.
Empty
);
}
}
}
/// <summary>
/// List of supported Ssl versions
/// </summary>
#if NET45
public
SslProtocols
SupportedSslProtocols
{
get
;
set
;
}
=
SslProtocols
.
Tls
|
SslProtocols
.
Tls11
|
SslProtocols
.
Tls12
|
SslProtocols
.
Ssl3
;
#else
public
SslProtocols
SupportedSslProtocols
{
get
;
set
;
}
=
SslProtocols
.
Tls
|
SslProtocols
.
Tls11
|
SslProtocols
.
Tls12
;
#endif
/// <summary>
/// Total number of active client connections
/// </summary>
public
int
ClientConnectionCount
=>
clientConnectionCount
;
/// <summary>
/// Total number of active server connections
/// </summary>
public
int
ServerConnectionCount
=>
serverConnectionCount
;
/// <summary>
/// Constructor
/// </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
)
{
//default values
ConnectionTimeOutSeconds
=
30
;
CertificateCacheTimeOutMinutes
=
60
;
ProxyEndPoints
=
new
List
<
ProxyEndPoint
>();
tcpConnectionFactory
=
new
TcpConnectionFactory
();
#if NET45
if
(!
RunTime
.
IsRunningOnMono
)
{
systemProxySettingsManager
=
new
SystemProxyManager
();
}
#endif
CertificateManager
=
new
CertificateManager
(
ExceptionFunc
);
if
(
rootCertificateName
!=
null
)
{
RootCertificateName
=
rootCertificateName
;
}
if
(
rootCertificateIssuerName
!=
null
)
{
RootCertificateIssuerName
=
rootCertificateIssuerName
;
}
}
/// <summary>
/// Add a proxy end point
/// </summary>
/// <param name="endPoint"></param>
public
void
AddEndPoint
(
ProxyEndPoint
endPoint
)
{
if
(
ProxyEndPoints
.
Any
(
x
=>
x
.
IpAddress
.
Equals
(
endPoint
.
IpAddress
)
&&
endPoint
.
Port
!=
0
&&
x
.
Port
==
endPoint
.
Port
))
{
throw
new
Exception
(
"Cannot add another endpoint to same port & ip address"
);
}
ProxyEndPoints
.
Add
(
endPoint
);
if
(
proxyRunning
)
{
Listen
(
endPoint
);
}
}
/// <summary>
/// Remove a proxy end point
/// Will throw error if the end point does'nt exist
/// </summary>
/// <param name="endPoint"></param>
public
void
RemoveEndPoint
(
ProxyEndPoint
endPoint
)
{
if
(
ProxyEndPoints
.
Contains
(
endPoint
)
==
false
)
{
throw
new
Exception
(
"Cannot remove endPoints not added to proxy"
);
}
ProxyEndPoints
.
Remove
(
endPoint
);
if
(
proxyRunning
)
{
QuitListen
(
endPoint
);
}
}
#if NET45
/// <summary>
/// Set the given explicit end point as the default proxy server for current machine
/// </summary>
/// <param name="endPoint"></param>
public
void
SetAsSystemHttpProxy
(
ExplicitProxyEndPoint
endPoint
)
{
SetAsSystemProxy
(
endPoint
,
ProxyProtocolType
.
Http
);
}
/// <summary>
/// Set the given explicit end point as the default proxy server for current machine
/// </summary>
/// <param name="endPoint"></param>
public
void
SetAsSystemHttpsProxy
(
ExplicitProxyEndPoint
endPoint
)
{
SetAsSystemProxy
(
endPoint
,
ProxyProtocolType
.
Https
);
}
/// <summary>
/// Set the given explicit end point as the default proxy server for current machine
/// </summary>
/// <param name="endPoint"></param>
/// <param name="protocolType"></param>
public
void
SetAsSystemProxy
(
ExplicitProxyEndPoint
endPoint
,
ProxyProtocolType
protocolType
)
{
if
(
RunTime
.
IsRunningOnMono
)
{
throw
new
Exception
(
"Mono Runtime do not support system proxy settings."
);
}
ValidateEndPointAsSystemProxy
(
endPoint
);
bool
isHttp
=
(
protocolType
&
ProxyProtocolType
.
Http
)
>
0
;
bool
isHttps
=
(
protocolType
&
ProxyProtocolType
.
Https
)
>
0
;
if
(
isHttps
)
{
if
(!
endPoint
.
EnableSsl
)
{
throw
new
Exception
(
"Endpoint do not support Https connections"
);
}
EnsureRootCertificate
();
//If certificate was trusted by the machine
if
(!
CertificateManager
.
CertValidated
)
{
protocolType
=
protocolType
&
~
ProxyProtocolType
.
Https
;
isHttps
=
false
;
}
}
//clear any settings previously added
if
(
isHttp
)
{
ProxyEndPoints
.
OfType
<
ExplicitProxyEndPoint
>().
ToList
().
ForEach
(
x
=>
x
.
IsSystemHttpProxy
=
false
);
}
if
(
isHttps
)
{
ProxyEndPoints
.
OfType
<
ExplicitProxyEndPoint
>().
ToList
().
ForEach
(
x
=>
x
.
IsSystemHttpsProxy
=
false
);
}
systemProxySettingsManager
.
SetProxy
(
Equals
(
endPoint
.
IpAddress
,
IPAddress
.
Any
)
|
Equals
(
endPoint
.
IpAddress
,
IPAddress
.
Loopback
)
?
"127.0.0.1"
:
endPoint
.
IpAddress
.
ToString
(),
endPoint
.
Port
,
protocolType
);
if
(
isHttp
)
{
endPoint
.
IsSystemHttpsProxy
=
true
;
}
if
(
isHttps
)
{
endPoint
.
IsSystemHttpsProxy
=
true
;
}
firefoxProxySettingsManager
.
UseSystemProxy
();
string
proxyType
=
null
;
switch
(
protocolType
)
{
case
ProxyProtocolType
.
Http
:
proxyType
=
"HTTP"
;
break
;
case
ProxyProtocolType
.
Https
:
proxyType
=
"HTTPS"
;
break
;
case
ProxyProtocolType
.
AllHttp
:
proxyType
=
"HTTP and HTTPS"
;
break
;
}
if
(
protocolType
!=
ProxyProtocolType
.
None
)
{
Console
.
WriteLine
(
"Set endpoint at Ip {0} and port: {1} as System {2} Proxy"
,
endPoint
.
IpAddress
,
endPoint
.
Port
,
proxyType
);
}
}
/// <summary>
/// Remove any HTTP proxy setting of current machien
/// </summary>
public
void
DisableSystemHttpProxy
()
{
DisableSystemProxy
(
ProxyProtocolType
.
Http
);
}
/// <summary>
/// Remove any HTTPS proxy setting for current machine
/// </summary>
public
void
DisableSystemHttpsProxy
()
{
DisableSystemProxy
(
ProxyProtocolType
.
Https
);
}
/// <summary>
/// Remove the specified proxy settings for current machine
/// </summary>
public
void
DisableSystemProxy
(
ProxyProtocolType
protocolType
)
{
if
(
RunTime
.
IsRunningOnMono
)
{
throw
new
Exception
(
"Mono Runtime do not support system proxy settings."
);
}
systemProxySettingsManager
.
RemoveProxy
(
protocolType
);
}
/// <summary>
/// Clear all proxy settings for current machine
/// </summary>
public
void
DisableAllSystemProxies
()
{
if
(
RunTime
.
IsRunningOnMono
)
{
throw
new
Exception
(
"Mono Runtime do not support system proxy settings."
);
}
systemProxySettingsManager
.
DisableAllProxy
();
}
#endif
/// <summary>
/// Start this proxy server
/// </summary>
public
void
Start
()
{
if
(
proxyRunning
)
{
throw
new
Exception
(
"Proxy is already running."
);
}
#if NET45
//clear any system proxy settings which is pointing to our own endpoint (causing a cycle)
//due to non gracious proxy shutdown before or something else
if
(
systemProxySettingsManager
!=
null
)
{
var
proxyInfo
=
systemProxySettingsManager
.
GetProxyInfoFromRegistry
();
if
(
proxyInfo
.
Proxies
!=
null
)
{
var
protocolToRemove
=
ProxyProtocolType
.
None
;
foreach
(
var
proxy
in
proxyInfo
.
Proxies
.
Values
)
{
if
((
proxy
.
HostName
==
"127.0.0.1"
||
proxy
.
HostName
.
Equals
(
"localhost"
,
StringComparison
.
OrdinalIgnoreCase
))
&&
ProxyEndPoints
.
Any
(
x
=>
x
.
Port
==
proxy
.
Port
))
{
protocolToRemove
|=
proxy
.
ProtocolType
;
}
}
if
(
protocolToRemove
!=
ProxyProtocolType
.
None
)
{
//do not restore to any of listening address when we quit
systemProxySettingsManager
.
RemoveProxy
(
protocolToRemove
,
false
);
}
}
}
if
(
ForwardToUpstreamGateway
&&
GetCustomUpStreamHttpProxyFunc
==
null
&&
GetCustomUpStreamHttpsProxyFunc
==
null
&&
systemProxySettingsManager
!=
null
)
{
// Use WinHttp to handle PAC/WAPD scripts.
systemProxyResolver
=
new
WinHttpWebProxyFinder
();
systemProxyResolver
.
LoadFromIE
();
GetCustomUpStreamHttpProxyFunc
=
GetSystemUpStreamProxy
;
GetCustomUpStreamHttpsProxyFunc
=
GetSystemUpStreamProxy
;
}
#endif
foreach
(
var
endPoint
in
ProxyEndPoints
)
{
Listen
(
endPoint
);
}
CertificateManager
.
ClearIdleCertificates
(
CertificateCacheTimeOutMinutes
);
#if NET45
if
(!
RunTime
.
IsRunningOnMono
)
{
//clear orphaned windows auth states every 2 minutes
WinAuthEndPoint
.
ClearIdleStates
(
2
);
}
#endif
proxyRunning
=
true
;
}
/// <summary>
/// Stop this proxy server
/// </summary>
public
void
Stop
()
{
if
(!
proxyRunning
)
{
throw
new
Exception
(
"Proxy is not running."
);
}
#if NET45
if
(!
RunTime
.
IsRunningOnMono
)
{
bool
setAsSystemProxy
=
ProxyEndPoints
.
OfType
<
ExplicitProxyEndPoint
>().
Any
(
x
=>
x
.
IsSystemHttpProxy
||
x
.
IsSystemHttpsProxy
);
if
(
setAsSystemProxy
)
{
systemProxySettingsManager
.
RestoreOriginalSettings
();
}
}
#endif
foreach
(
var
endPoint
in
ProxyEndPoints
)
{
QuitListen
(
endPoint
);
}
ProxyEndPoints
.
Clear
();
CertificateManager
?.
StopClearIdleCertificates
();
proxyRunning
=
false
;
}
/// <summary>
/// Handle dispose of a client/server session
/// </summary>
/// <param name="clientStream"></param>
/// <param name="clientStreamReader"></param>
/// <param name="clientStreamWriter"></param>
/// <param name="serverConnection"></param>
private
void
Dispose
(
CustomBufferedStream
clientStream
,
CustomBinaryReader
clientStreamReader
,
HttpResponseWriter
clientStreamWriter
,
TcpConnection
serverConnection
)
{
clientStream
?.
Dispose
();
clientStreamReader
?.
Dispose
();
clientStreamWriter
?.
Dispose
();
if
(
serverConnection
!=
null
)
{
serverConnection
.
Dispose
();
serverConnection
=
null
;
UpdateServerConnectionCount
(
false
);
}
}
/// <summary>
/// Dispose Proxy.
/// </summary>
public
void
Dispose
()
{
if
(
proxyRunning
)
{
Stop
();
}
CertificateManager
?.
Dispose
();
}
#if NET45
/// <summary>
/// Listen on the given end point on local machine
/// </summary>
/// <param name="endPoint"></param>
private
void
Listen
(
ProxyEndPoint
endPoint
)
{
endPoint
.
Listener
=
new
TcpListener
(
endPoint
.
IpAddress
,
endPoint
.
Port
);
endPoint
.
Listener
.
Start
();
endPoint
.
Port
=
((
IPEndPoint
)
endPoint
.
Listener
.
LocalEndpoint
).
Port
;
// accept clients asynchronously
endPoint
.
Listener
.
BeginAcceptTcpClient
(
OnAcceptConnection
,
endPoint
);
}
#else
private
async
void
Listen
(
ProxyEndPoint
endPoint
)
{
endPoint
.
Listener
=
new
TcpListener
(
endPoint
.
IpAddress
,
endPoint
.
Port
);
endPoint
.
Listener
.
Start
();
endPoint
.
Port
=
((
IPEndPoint
)
endPoint
.
Listener
.
LocalEndpoint
).
Port
;
while
(
true
)
{
TcpClient
tcpClient
=
await
endPoint
.
Listener
.
AcceptTcpClientAsync
();
if
(
tcpClient
!=
null
)
Task
.
Run
(
async
()
=>
HandleClient
(
tcpClient
,
endPoint
));
}
}
#endif
/// <summary>
/// Verifiy if its safe to set this end point as System proxy
/// </summary>
/// <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"
);
}
if
(!
proxyRunning
)
{
throw
new
Exception
(
"Cannot set system proxy settings before proxy has been started."
);
}
}
#if NET45
/// <summary>
/// Gets the system up stream proxy.
/// </summary>
/// <param name="sessionEventArgs">The <see cref="SessionEventArgs"/> instance containing the event data.</param>
/// <returns><see cref="ExternalProxy"/> instance containing valid proxy configuration from PAC/WAPD scripts if any exists.</returns>
private
Task
<
ExternalProxy
>
GetSystemUpStreamProxy
(
SessionEventArgs
sessionEventArgs
)
{
var
proxy
=
systemProxyResolver
.
GetProxy
(
sessionEventArgs
.
WebSession
.
Request
.
RequestUri
);
return
Task
.
FromResult
(
proxy
);
}
#endif
private
void
EnsureRootCertificate
()
{
if
(!
CertificateManager
.
CertValidated
)
{
CertificateManager
.
CreateTrustedRootCertificate
();
if
(
TrustRootCertificate
)
{
CertificateManager
.
TrustRootCertificate
();
}
}
}
#if NET45
/// <summary>
/// When a connection is received from client act
/// </summary>
/// <param name="asyn"></param>
private
void
OnAcceptConnection
(
IAsyncResult
asyn
)
{
var
endPoint
=
(
ProxyEndPoint
)
asyn
.
AsyncState
;
TcpClient
tcpClient
=
null
;
try
{
//based on end point type call appropriate request handlers
tcpClient
=
endPoint
.
Listener
.
EndAcceptTcpClient
(
asyn
);
}
catch
(
ObjectDisposedException
)
{
// The listener was Stop()'d, disposing the underlying socket and
// triggering the completion of the callback. We're already exiting,
// so just return.
return
;
}
catch
{
//Other errors are discarded to keep proxy running
}
if
(
tcpClient
!=
null
)
{
Task
.
Run
(
async
()
=>
{
await
HandleClient
(
tcpClient
,
endPoint
);
});
}
// Get the listener that handles the client request.
endPoint
.
Listener
.
BeginAcceptTcpClient
(
OnAcceptConnection
,
endPoint
);
}
#endif
private
async
Task
HandleClient
(
TcpClient
tcpClient
,
ProxyEndPoint
endPoint
)
{
UpdateClientConnectionCount
(
true
);
tcpClient
.
ReceiveTimeout
=
ConnectionTimeOutSeconds
*
1000
;
tcpClient
.
SendTimeout
=
ConnectionTimeOutSeconds
*
1000
;
try
{
if
(
endPoint
.
GetType
()
==
typeof
(
TransparentProxyEndPoint
))
{
await
HandleClient
(
endPoint
as
TransparentProxyEndPoint
,
tcpClient
);
}
else
{
await
HandleClient
(
endPoint
as
ExplicitProxyEndPoint
,
tcpClient
);
}
}
finally
{
UpdateClientConnectionCount
(
false
);
try
{
if
(
tcpClient
!=
null
)
{
//This line is important!
//contributors please don't remove it without discussion
//It helps to avoid eventual deterioration of performance due to TCP port exhaustion
//due to default TCP CLOSE_WAIT timeout for 4 minutes
tcpClient
.
LingerState
=
new
LingerOption
(
true
,
0
);
tcpClient
.
Dispose
();
}
}
catch
{
}
}
}
/// <summary>
/// Quit listening on the given end point
/// </summary>
/// <param name="endPoint"></param>
private
void
QuitListen
(
ProxyEndPoint
endPoint
)
{
endPoint
.
Listener
.
Stop
();
endPoint
.
Listener
.
Server
.
Dispose
();
}
internal
void
UpdateClientConnectionCount
(
bool
increment
)
{
if
(
increment
)
{
Interlocked
.
Increment
(
ref
clientConnectionCount
);
}
else
{
Interlocked
.
Decrement
(
ref
clientConnectionCount
);
}
ClientConnectionCountChanged
?.
Invoke
(
this
,
EventArgs
.
Empty
);
}
internal
void
UpdateServerConnectionCount
(
bool
increment
)
{
if
(
increment
)
{
Interlocked
.
Increment
(
ref
serverConnectionCount
);
}
else
{
Interlocked
.
Decrement
(
ref
serverConnectionCount
);
}
ServerConnectionCountChanged
?.
Invoke
(
this
,
EventArgs
.
Empty
);
}
}
}
Titanium.Web.Proxy/RequestHandler.cs
View file @
11632fe1
...
...
@@ -37,7 +37,7 @@ namespace Titanium.Web.Proxy
var
clientStream
=
new
CustomBufferedStream
(
tcpClient
.
GetStream
(),
BufferSize
);
var
clientStreamReader
=
new
CustomBinaryReader
(
clientStream
,
BufferSize
);
var
clientStreamWriter
=
new
HttpResponseWriter
(
clientStream
);
var
clientStreamWriter
=
new
HttpResponseWriter
(
clientStream
,
BufferSize
);
Uri
httpRemoteUri
;
...
...
@@ -86,7 +86,7 @@ namespace Titanium.Web.Proxy
await
HeaderParser
.
ReadHeaders
(
clientStreamReader
,
connectRequest
.
RequestHeaders
);
var
connectArgs
=
new
TunnelConnectSessionEventArgs
(
endPoint
);
var
connectArgs
=
new
TunnelConnectSessionEventArgs
(
BufferSize
,
endPoint
);
connectArgs
.
WebSession
.
Request
=
connectRequest
;
connectArgs
.
ProxyClient
.
TcpClient
=
tcpClient
;
connectArgs
.
ProxyClient
.
ClientStream
=
clientStream
;
...
...
@@ -147,7 +147,7 @@ namespace Titanium.Web.Proxy
clientStreamReader
.
Dispose
();
clientStreamReader
=
new
CustomBinaryReader
(
clientStream
,
BufferSize
);
clientStreamWriter
=
new
HttpResponseWriter
(
clientStream
);
clientStreamWriter
=
new
HttpResponseWriter
(
clientStream
,
BufferSize
);
}
catch
{
...
...
@@ -164,24 +164,30 @@ namespace Titanium.Web.Proxy
//create new connection
using
(
var
connection
=
await
GetServerConnection
(
connectArgs
,
true
))
{
if
(
isClientHello
)
try
{
if
(
clientStream
.
Available
>
0
)
if
(
isClientHello
)
{
//send the buffered data
var
data
=
new
byte
[
clientStream
.
Available
];
await
clientStream
.
ReadAsync
(
data
,
0
,
data
.
Length
);
await
connection
.
Stream
.
WriteAsync
(
data
,
0
,
data
.
Length
);
await
connection
.
Stream
.
FlushAsync
();
if
(
clientStream
.
Available
>
0
)
{
//send the buffered data
var
data
=
new
byte
[
clientStream
.
Available
];
await
clientStream
.
ReadAsync
(
data
,
0
,
data
.
Length
);
await
connection
.
Stream
.
WriteAsync
(
data
,
0
,
data
.
Length
);
await
connection
.
Stream
.
FlushAsync
();
}
var
serverHelloInfo
=
await
SslTools
.
PeekServerHello
(
connection
.
Stream
);
((
ConnectResponse
)
connectArgs
.
WebSession
.
Response
).
ServerHelloInfo
=
serverHelloInfo
;
}
var
serverHelloInfo
=
await
SslTools
.
PeekServerHello
(
connection
.
Stream
);
((
ConnectResponse
)
connectArgs
.
WebSession
.
Response
).
ServerHelloInfo
=
serverHelloInfo
;
await
TcpHelper
.
SendRaw
(
clientStream
,
connection
.
Stream
,
BufferSize
,
(
buffer
,
offset
,
count
)
=>
{
connectArgs
.
OnDataSent
(
buffer
,
offset
,
count
);
},
(
buffer
,
offset
,
count
)
=>
{
connectArgs
.
OnDataReceived
(
buffer
,
offset
,
count
);
});
}
finally
{
UpdateServerConnectionCount
(
false
);
}
await
TcpHelper
.
SendRaw
(
clientStream
,
connection
.
Stream
,
(
buffer
,
offset
,
count
)
=>
{
connectArgs
.
OnDataSent
(
buffer
,
offset
,
count
);
},
(
buffer
,
offset
,
count
)
=>
{
connectArgs
.
OnDataReceived
(
buffer
,
offset
,
count
);
});
UpdateServerConnectionCount
(
false
);
}
return
;
...
...
@@ -243,7 +249,7 @@ namespace Titanium.Web.Proxy
}
clientStreamReader
=
new
CustomBinaryReader
(
clientStream
,
BufferSize
);
clientStreamWriter
=
new
HttpResponseWriter
(
clientStream
);
clientStreamWriter
=
new
HttpResponseWriter
(
clientStream
,
BufferSize
);
//now read the request line
string
httpCmd
=
await
clientStreamReader
.
ReadLineAsync
();
...
...
@@ -354,12 +360,15 @@ namespace Titanium.Web.Proxy
break
;
}
//create a new connection if hostname changes
if
(
connection
!=
null
&&
!
connection
.
HostName
.
Equals
(
args
.
WebSession
.
Request
.
RequestUri
.
Host
,
StringComparison
.
OrdinalIgnoreCase
))
//create a new connection if hostname/upstream end point changes
if
(
connection
!=
null
&&
(!
connection
.
HostName
.
Equals
(
args
.
WebSession
.
Request
.
RequestUri
.
Host
,
StringComparison
.
OrdinalIgnoreCase
)
||
(
args
.
WebSession
.
UpStreamEndPoint
!=
null
&&
!
args
.
WebSession
.
UpStreamEndPoint
.
Equals
(
connection
.
UpStreamEndPoint
))))
{
connection
.
Dispose
();
UpdateServerConnectionCount
(
false
);
connection
=
null
;
UpdateServerConnectionCount
(
false
);
}
if
(
connection
==
null
)
...
...
@@ -374,7 +383,7 @@ namespace Titanium.Web.Proxy
var
requestHeaders
=
args
.
WebSession
.
Request
.
RequestHeaders
;
byte
[]
requestBytes
;
using
(
var
ms
=
new
MemoryStream
())
using
(
var
writer
=
new
HttpRequestWriter
(
ms
))
using
(
var
writer
=
new
HttpRequestWriter
(
ms
,
BufferSize
))
{
writer
.
WriteLine
(
httpCmd
);
writer
.
WriteHeaders
(
requestHeaders
);
...
...
@@ -402,7 +411,7 @@ namespace Titanium.Web.Proxy
await
BeforeResponse
.
InvokeParallelAsync
(
this
,
args
,
ExceptionFunc
);
}
await
TcpHelper
.
SendRaw
(
clientStream
,
connection
.
Stream
,
await
TcpHelper
.
SendRaw
(
clientStream
,
connection
.
Stream
,
BufferSize
,
(
buffer
,
offset
,
count
)
=>
{
args
.
OnDataSent
(
buffer
,
offset
,
count
);
},
(
buffer
,
offset
,
count
)
=>
{
args
.
OnDataReceived
(
buffer
,
offset
,
count
);
});
args
.
Dispose
();
...
...
@@ -599,6 +608,7 @@ namespace Titanium.Web.Proxy
args
.
WebSession
.
Request
.
RequestUri
.
Port
,
args
.
WebSession
.
Request
.
HttpVersion
,
args
.
IsHttps
,
isConnect
,
args
.
WebSession
.
UpStreamEndPoint
??
UpStreamEndPoint
,
customUpStreamHttpProxy
??
UpStreamHttpProxy
,
customUpStreamHttpsProxy
??
UpStreamHttpsProxy
);
}
...
...
Titanium.Web.Proxy/Titanium.Web.Proxy.Standard.csproj
View file @
11632fe1
...
...
@@ -3,6 +3,10 @@
<PropertyGroup>
<TargetFramework>netstandard1.6</TargetFramework>
<RootNamespace>Titanium.Web.Proxy</RootNamespace>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<SignAssembly>True</SignAssembly>
<AssemblyOriginatorKeyFile>StrongNameKey.snk</AssemblyOriginatorKeyFile>
<DelaySign>False</DelaySign>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
...
...
@@ -10,7 +14,6 @@
</PropertyGroup>
<ItemGroup>
<Compile Remove="Properties\AssemblyInfo.cs" />
<Compile Remove="Helpers\WinHttp\NativeMethods.WinHttp.cs" />
<Compile Remove="Helpers\WinHttp\WinHttpHandle.cs" />
<Compile Remove="Helpers\WinHttp\WinHttpWebProxyFinder.cs" />
...
...
Titanium.Web.Proxy/Titanium.Web.Proxy.csproj
View file @
11632fe1
...
...
@@ -84,7 +84,7 @@
<Compile
Include=
"Exceptions\ProxyException.cs"
/>
<Compile
Include=
"Exceptions\ProxyHttpException.cs"
/>
<Compile
Include=
"Extensions\ByteArrayExtensions.cs"
/>
<Compile
Include=
"Extensions\DotNet
Standard
Extensions.cs"
/>
<Compile
Include=
"Extensions\DotNet
45
Extensions.cs"
/>
<Compile
Include=
"Extensions\FuncExtensions.cs"
/>
<Compile
Include=
"Extensions\HttpWebRequestExtensions.cs"
/>
<Compile
Include=
"Extensions\HttpWebResponseExtensions.cs"
/>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment