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
e170b5ae
Commit
e170b5ae
authored
Oct 12, 2016
by
Hakan Arıcı
Committed by
GitHub
Oct 12, 2016
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #3 from justcoding121/release
Sync
parents
5026d40a
5ecc3c81
Show whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
338 additions
and
172 deletions
+338
-172
ProxyTestController.cs
.../Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs
+1
-0
README.md
README.md
+10
-7
CertificateManagerTests.cs
...s/Titanium.Web.Proxy.UnitTests/CertificateManagerTests.cs
+2
-1
ProxyServerTests.cs
Tests/Titanium.Web.Proxy.UnitTests/ProxyServerTests.cs
+91
-0
Titanium.Web.Proxy.UnitTests.csproj
...m.Web.Proxy.UnitTests/Titanium.Web.Proxy.UnitTests.csproj
+1
-0
SessionEventArgs.cs
Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs
+0
-2
HttpWebClient.cs
Titanium.Web.Proxy/Http/HttpWebClient.cs
+2
-1
CertificateMaker.cs
Titanium.Web.Proxy/Network/CertificateMaker.cs
+9
-13
CertificateManager.cs
Titanium.Web.Proxy/Network/CertificateManager.cs
+27
-21
ProxyAuthorizationHandler.cs
Titanium.Web.Proxy/ProxyAuthorizationHandler.cs
+94
-0
ProxyServer.cs
Titanium.Web.Proxy/ProxyServer.cs
+90
-44
RequestHandler.cs
Titanium.Web.Proxy/RequestHandler.cs
+4
-80
ResponseHandler.cs
Titanium.Web.Proxy/ResponseHandler.cs
+5
-1
Titanium.Web.Proxy.csproj
Titanium.Web.Proxy/Titanium.Web.Proxy.csproj
+2
-1
Titanium.Web.Proxy.nuspec
Titanium.Web.Proxy/Titanium.Web.Proxy.nuspec
+0
-1
No files found.
Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs
View file @
e170b5ae
...
...
@@ -13,6 +13,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
public
ProxyTestController
()
{
proxyServer
=
new
ProxyServer
();
proxyServer
.
TrustRootCertificate
=
true
;
}
public
void
StartProxy
()
...
...
README.md
View file @
e170b5ae
...
...
@@ -17,6 +17,8 @@ Features
*
Safely relays WebSocket requests over Http
*
Support mutual SSL authentication
*
Fully asynchronous proxy
*
Supports proxy authentication
Usage
=====
...
...
@@ -25,11 +27,7 @@ Refer the HTTP Proxy Server library in your project, look up Test project to lea
Install by nuget:
Install-Package Titanium.Web.Proxy
After installing nuget package mark following files to be copied to app directory
*
makecert.exe
Install-Package Titanium.Web.Proxy -Pre
Setup HTTP proxy:
...
...
@@ -37,6 +35,9 @@ Setup HTTP proxy:
```
csharp
var
proxyServer
=
new
ProxyServer
();
//locally trust root certificate used by this proxy
proxyServer
.
TrustRootCertificate
=
true
;
proxyServer
.
BeforeRequest
+=
OnRequest
;
proxyServer
.
BeforeResponse
+=
OnResponse
;
proxyServer
.
ServerCertificateValidationCallback
+=
OnCertificateValidation
;
...
...
@@ -176,7 +177,9 @@ Sample request and response event handlers
```
Future roadmap
============
*
Implement Kerberos/NTLM authentication over HTTP protocols for windows domain
*
Support Server Name Indication (SNI) for transparent endpoints
*
Support HTTP 2.0
*
Support updstream AutoProxy detection
*
Implement Kerberos/NTLM authentication over HTTP protocols for windows domain
Tests/Titanium.Web.Proxy.UnitTests/CertificateManagerTests.cs
View file @
e170b5ae
...
...
@@ -20,7 +20,8 @@ namespace Titanium.Web.Proxy.UnitTests
{
var
tasks
=
new
List
<
Task
>();
var
mgr
=
new
CertificateManager
(
"Titanium"
,
"Titanium Root Certificate Authority"
);
var
mgr
=
new
CertificateManager
(
"Titanium"
,
"Titanium Root Certificate Authority"
,
new
Lazy
<
Action
<
Exception
>>(()
=>
(
e
=>
{
})).
Value
);
mgr
.
ClearIdleCertificates
(
1
);
...
...
Tests/Titanium.Web.Proxy.UnitTests/ProxyServerTests.cs
0 → 100644
View file @
e170b5ae
using
System
;
using
System.Net
;
using
Microsoft.VisualStudio.TestTools.UnitTesting
;
using
Titanium.Web.Proxy.Models
;
namespace
Titanium.Web.Proxy.UnitTests
{
[
TestClass
]
public
class
ProxyServerTests
{
[
TestMethod
]
public
void
GivenOneEndpointIsAlreadyAddedToAddress_WhenAddingNewEndpointToExistingAddress_ThenExceptionIsThrown
()
{
// Arrange
var
proxy
=
new
ProxyServer
();
const
int
port
=
9999
;
var
firstIpAddress
=
IPAddress
.
Parse
(
"127.0.0.1"
);
var
secondIpAddress
=
IPAddress
.
Parse
(
"127.0.0.1"
);
proxy
.
AddEndPoint
(
new
ExplicitProxyEndPoint
(
firstIpAddress
,
port
,
false
));
// Act
try
{
proxy
.
AddEndPoint
(
new
ExplicitProxyEndPoint
(
secondIpAddress
,
port
,
false
));
}
catch
(
Exception
exc
)
{
// Assert
StringAssert
.
Contains
(
exc
.
Message
,
"Cannot add another endpoint to same port"
);
return
;
}
Assert
.
Fail
(
"An exception should be thrown by now"
);
}
[
TestMethod
]
public
void
GivenOneEndpointIsAlreadyAddedToAddress_WhenAddingNewEndpointToExistingAddress_ThenTwoEndpointsExists
()
{
// Arrange
var
proxy
=
new
ProxyServer
();
const
int
port
=
9999
;
var
firstIpAddress
=
IPAddress
.
Parse
(
"127.0.0.1"
);
var
secondIpAddress
=
IPAddress
.
Parse
(
"192.168.1.1"
);
proxy
.
AddEndPoint
(
new
ExplicitProxyEndPoint
(
firstIpAddress
,
port
,
false
));
// Act
proxy
.
AddEndPoint
(
new
ExplicitProxyEndPoint
(
secondIpAddress
,
port
,
false
));
// Assert
Assert
.
AreEqual
(
2
,
proxy
.
ProxyEndPoints
.
Count
);
}
[
TestMethod
]
public
void
GivenOneEndpointIsAlreadyAddedToPort_WhenAddingNewEndpointToExistingPort_ThenExceptionIsThrown
()
{
// Arrange
var
proxy
=
new
ProxyServer
();
const
int
port
=
9999
;
proxy
.
AddEndPoint
(
new
ExplicitProxyEndPoint
(
IPAddress
.
Loopback
,
port
,
false
));
// Act
try
{
proxy
.
AddEndPoint
(
new
ExplicitProxyEndPoint
(
IPAddress
.
Loopback
,
port
,
false
));
}
catch
(
Exception
exc
)
{
// Assert
StringAssert
.
Contains
(
exc
.
Message
,
"Cannot add another endpoint to same port"
);
return
;
}
Assert
.
Fail
(
"An exception should be thrown by now"
);
}
[
TestMethod
]
public
void
GivenOneEndpointIsAlreadyAddedToZeroPort_WhenAddingNewEndpointToExistingPort_ThenTwoEndpointsExists
()
{
// Arrange
var
proxy
=
new
ProxyServer
();
const
int
port
=
0
;
proxy
.
AddEndPoint
(
new
ExplicitProxyEndPoint
(
IPAddress
.
Loopback
,
port
,
false
));
// Act
proxy
.
AddEndPoint
(
new
ExplicitProxyEndPoint
(
IPAddress
.
Loopback
,
port
,
false
));
// Assert
Assert
.
AreEqual
(
2
,
proxy
.
ProxyEndPoints
.
Count
);
}
}
}
Tests/Titanium.Web.Proxy.UnitTests/Titanium.Web.Proxy.UnitTests.csproj
View file @
e170b5ae
...
...
@@ -52,6 +52,7 @@
<ItemGroup>
<Compile
Include=
"CertificateManagerTests.cs"
/>
<Compile
Include=
"Properties\AssemblyInfo.cs"
/>
<Compile
Include=
"ProxyServerTests.cs"
/>
</ItemGroup>
<ItemGroup>
<ProjectReference
Include=
"..\..\Titanium.Web.Proxy\Titanium.Web.Proxy.csproj"
>
...
...
Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs
View file @
e170b5ae
...
...
@@ -64,8 +64,6 @@ namespace Titanium.Web.Proxy.EventArguments
public
ExternalProxy
CustomUpStreamHttpsProxyUsed
{
get
;
set
;
}
/// <summary>
/// Constructor to initialize the proxy
/// </summary>
...
...
Titanium.Web.Proxy/Http/HttpWebClient.cs
View file @
e170b5ae
...
...
@@ -38,7 +38,8 @@ namespace Titanium.Web.Proxy.Http
{
if
(
processId
==
0
)
{
TcpRow
tcpRow
=
TcpHelper
.
GetExtendedTcpTable
().
TcpRows
.
FirstOrDefault
(
row
=>
row
.
LocalEndPoint
.
Port
==
ServerConnection
.
port
);
TcpRow
tcpRow
=
TcpHelper
.
GetExtendedTcpTable
().
TcpRows
.
FirstOrDefault
(
row
=>
row
.
LocalEndPoint
.
Port
==
ServerConnection
.
port
);
processId
=
tcpRow
?.
ProcessId
??
-
1
;
}
...
...
Titanium.Web.Proxy/Network/Cert
EnrollEngine
.cs
→
Titanium.Web.Proxy/Network/Cert
ificateMaker
.cs
View file @
e170b5ae
using
System
;
using
System.Collections.Generic
;
using
System.Linq
;
using
System.Text
;
using
System.Threading.Tasks
;
using
System.Reflection
;
using
System.Security.Cryptography.X509Certificates
;
using
System.Threading
;
...
...
@@ -10,7 +6,7 @@ using System.Threading;
namespace
Titanium.Web.Proxy.Network
{
public
class
Cert
EnrollEngine
public
class
Cert
ificateMaker
{
private
Type
typeX500DN
;
...
...
@@ -44,7 +40,7 @@ namespace Titanium.Web.Proxy.Network
private
object
_SharedPrivateKey
;
public
Cert
EnrollEngine
()
public
Cert
ificateMaker
()
{
this
.
typeX500DN
=
Type
.
GetTypeFromProgID
(
"X509Enrollment.CX500DistinguishedName"
,
true
);
this
.
typeX509PrivateKey
=
Type
.
GetTypeFromProgID
(
"X509Enrollment.CX509PrivateKey"
,
true
);
...
...
@@ -62,12 +58,12 @@ namespace Titanium.Web.Proxy.Network
this
.
typeAlternativeNamesExt
=
Type
.
GetTypeFromProgID
(
"X509Enrollment.CX509ExtensionAlternativeNames"
);
}
public
X509Certificate2
CreateCert
(
string
sSubjectCN
,
bool
isRoot
,
X509Certificate2
signingCert
=
null
)
public
X509Certificate2
MakeCertificate
(
string
sSubjectCN
,
bool
isRoot
,
X509Certificate2
signingCert
=
null
)
{
return
this
.
InternalCreateCert
(
sSubjectCN
,
isRoot
,
true
,
signingCert
);
return
this
.
MakeCertificateInternal
(
sSubjectCN
,
isRoot
,
true
,
signingCert
);
}
private
X509Certificate2
Generat
eCertificate
(
bool
IsRoot
,
string
SubjectCN
,
string
FullSubject
,
int
PrivateKeyLength
,
string
HashAlg
,
DateTime
ValidFrom
,
DateTime
ValidTo
,
X509Certificate2
SigningCertificate
)
private
X509Certificate2
Mak
eCertificate
(
bool
IsRoot
,
string
SubjectCN
,
string
FullSubject
,
int
PrivateKeyLength
,
string
HashAlg
,
DateTime
ValidFrom
,
DateTime
ValidTo
,
X509Certificate2
SigningCertificate
)
{
X509Certificate2
cert
;
if
(
IsRoot
!=
(
null
==
SigningCertificate
))
...
...
@@ -193,7 +189,7 @@ namespace Titanium.Web.Proxy.Network
return
cert
;
}
private
X509Certificate2
InternalCreateCert
(
string
sSubjectCN
,
bool
isRoot
,
bool
switchToMTAIfNeeded
,
X509Certificate2
signingCert
=
null
)
private
X509Certificate2
MakeCertificateInternal
(
string
sSubjectCN
,
bool
isRoot
,
bool
switchToMTAIfNeeded
,
X509Certificate2
signingCert
=
null
)
{
X509Certificate2
rCert
=
null
;
if
(
switchToMTAIfNeeded
&&
Thread
.
CurrentThread
.
GetApartmentState
()
!=
ApartmentState
.
MTA
)
...
...
@@ -201,7 +197,7 @@ namespace Titanium.Web.Proxy.Network
ManualResetEvent
manualResetEvent
=
new
ManualResetEvent
(
false
);
ThreadPool
.
QueueUserWorkItem
((
object
o
)
=>
{
rCert
=
this
.
InternalCreateCert
(
sSubjectCN
,
isRoot
,
false
,
signingCert
);
rCert
=
this
.
MakeCertificateInternal
(
sSubjectCN
,
isRoot
,
false
,
signingCert
);
manualResetEvent
.
Set
();
});
manualResetEvent
.
WaitOne
();
...
...
@@ -220,11 +216,11 @@ namespace Titanium.Web.Proxy.Network
{
if
(!
isRoot
)
{
rCert
=
this
.
Generat
eCertificate
(
false
,
sSubjectCN
,
fullSubject
,
keyLength
,
HashAlgo
,
graceTime
,
now
.
AddDays
((
double
)
ValidDays
),
signingCert
);
rCert
=
this
.
Mak
eCertificate
(
false
,
sSubjectCN
,
fullSubject
,
keyLength
,
HashAlgo
,
graceTime
,
now
.
AddDays
((
double
)
ValidDays
),
signingCert
);
}
else
{
rCert
=
this
.
Generat
eCertificate
(
true
,
sSubjectCN
,
fullSubject
,
keyLength
,
HashAlgo
,
graceTime
,
now
.
AddDays
((
double
)
ValidDays
),
null
);
rCert
=
this
.
Mak
eCertificate
(
true
,
sSubjectCN
,
fullSubject
,
keyLength
,
HashAlgo
,
graceTime
,
now
.
AddDays
((
double
)
ValidDays
),
null
);
}
}
catch
(
Exception
e
)
...
...
Titanium.Web.Proxy/Network/CertificateManager.cs
View file @
e170b5ae
...
...
@@ -13,23 +13,26 @@ namespace Titanium.Web.Proxy.Network
/// </summary>
internal
class
CertificateManager
:
IDisposable
{
private
CertEnrollEngine
certEngine
=
null
;
private
CertificateMaker
certEngine
=
null
;
private
bool
clearCertificates
{
get
;
set
;
}
/// <summary>
/// Cache dictionary
/// </summary>
private
readonly
IDictionary
<
string
,
CachedCertificate
>
certificateCache
;
private
Action
<
Exception
>
exceptionFunc
;
internal
string
Issuer
{
get
;
private
set
;
}
internal
string
RootCertificateName
{
get
;
private
set
;
}
internal
X509Certificate2
rootCertificate
{
get
;
set
;
}
internal
CertificateManager
(
string
issuer
,
string
rootCertificateName
)
internal
CertificateManager
(
string
issuer
,
string
rootCertificateName
,
Action
<
Exception
>
exceptionFunc
)
{
certEngine
=
new
CertEnrollEngine
();
this
.
exceptionFunc
=
exceptionFunc
;
certEngine
=
new
CertificateMaker
();
Issuer
=
issuer
;
RootCertificateName
=
rootCertificateName
;
...
...
@@ -37,7 +40,7 @@ namespace Titanium.Web.Proxy.Network
certificateCache
=
new
ConcurrentDictionary
<
string
,
CachedCertificate
>();
}
X509Certificate2
GetRootCertificate
()
internal
X509Certificate2
GetRootCertificate
()
{
var
fileName
=
Path
.
Combine
(
System
.
IO
.
Path
.
GetDirectoryName
(
System
.
Reflection
.
Assembly
.
GetEntryAssembly
().
Location
),
"rootCert.pfx"
);
...
...
@@ -50,7 +53,7 @@ namespace Titanium.Web.Proxy.Network
}
catch
(
Exception
e
)
{
ProxyServer
.
E
xceptionFunc
(
e
);
e
xceptionFunc
(
e
);
return
null
;
}
}
...
...
@@ -74,7 +77,7 @@ namespace Titanium.Web.Proxy.Network
}
catch
(
Exception
e
)
{
ProxyServer
.
E
xceptionFunc
(
e
);
e
xceptionFunc
(
e
);
}
if
(
rootCertificate
!=
null
)
{
...
...
@@ -85,7 +88,7 @@ namespace Titanium.Web.Proxy.Network
}
catch
(
Exception
e
)
{
ProxyServer
.
E
xceptionFunc
(
e
);
e
xceptionFunc
(
e
);
}
}
return
rootCertificate
!=
null
;
...
...
@@ -97,7 +100,7 @@ namespace Titanium.Web.Proxy.Network
/// <param name="certificateName"></param>
/// <param name="isRootCertificate"></param>
/// <returns></returns>
public
virtual
X509Certificate2
CreateCertificate
(
string
certificateName
,
bool
isRootCertificate
)
internal
virtual
X509Certificate2
CreateCertificate
(
string
certificateName
,
bool
isRootCertificate
)
{
try
{
...
...
@@ -119,11 +122,11 @@ namespace Titanium.Web.Proxy.Network
{
try
{
certificate
=
certEngine
.
CreateCert
(
certificateName
,
isRootCertificate
,
rootCertificate
);
certificate
=
certEngine
.
MakeCertificate
(
certificateName
,
isRootCertificate
,
rootCertificate
);
}
catch
(
Exception
e
)
{
ProxyServer
.
E
xceptionFunc
(
e
);
e
xceptionFunc
(
e
);
}
if
(
certificate
!=
null
&&
!
certificateCache
.
ContainsKey
(
certificateName
))
{
...
...
@@ -147,9 +150,6 @@ namespace Titanium.Web.Proxy.Network
}
private
bool
clearCertificates
{
get
;
set
;
}
/// <summary>
/// Stops the certificate cache clear process
/// </summary>
...
...
@@ -187,7 +187,7 @@ namespace Titanium.Web.Proxy.Network
}
}
public
bool
TrustRootCertificate
()
internal
bool
TrustRootCertificate
()
{
if
(
rootCertificate
==
null
)
{
...
...
@@ -195,19 +195,25 @@ namespace Titanium.Web.Proxy.Network
}
try
{
X509Store
x509Store
=
new
X509Store
(
StoreName
.
Root
,
StoreLocation
.
CurrentUser
);
x509Store
.
Open
(
OpenFlags
.
ReadWrite
);
X509Store
x509RootStore
=
new
X509Store
(
StoreName
.
Root
,
StoreLocation
.
CurrentUser
);
var
x509PersonalStore
=
new
X509Store
(
StoreName
.
My
,
StoreLocation
.
CurrentUser
);
x509RootStore
.
Open
(
OpenFlags
.
ReadWrite
);
x509PersonalStore
.
Open
(
OpenFlags
.
ReadWrite
);
try
{
x509Store
.
Add
(
rootCertificate
);
x509RootStore
.
Add
(
rootCertificate
);
x509PersonalStore
.
Add
(
rootCertificate
);
}
finally
{
x509Store
.
Close
();
x509RootStore
.
Close
();
x509PersonalStore
.
Close
();
}
return
true
;
}
catch
(
Exception
exception
)
catch
{
return
false
;
}
...
...
Titanium.Web.Proxy/ProxyAuthorizationHandler.cs
0 → 100644
View file @
e170b5ae
using
System
;
using
System.Collections.Generic
;
using
System.IO
;
using
System.Linq
;
using
System.Text
;
using
System.Threading.Tasks
;
using
Titanium.Web.Proxy.Http
;
using
Titanium.Web.Proxy.Models
;
namespace
Titanium.Web.Proxy
{
public
partial
class
ProxyServer
{
private
async
Task
<
bool
>
CheckAuthorization
(
StreamWriter
clientStreamWriter
,
IEnumerable
<
HttpHeader
>
Headers
)
{
if
(
AuthenticateUserFunc
==
null
)
{
return
true
;
}
try
{
if
(!
Headers
.
Where
(
t
=>
t
.
Name
==
"Proxy-Authorization"
).
Any
())
{
await
WriteResponseStatus
(
new
Version
(
1
,
1
),
"407"
,
"Proxy Authentication Required"
,
clientStreamWriter
);
var
response
=
new
Response
();
response
.
ResponseHeaders
=
new
Dictionary
<
string
,
HttpHeader
>();
response
.
ResponseHeaders
.
Add
(
"Proxy-Authenticate"
,
new
HttpHeader
(
"Proxy-Authenticate"
,
"Basic realm=\"TitaniumProxy\""
));
response
.
ResponseHeaders
.
Add
(
"Proxy-Connection"
,
new
HttpHeader
(
"Proxy-Connection"
,
"close"
));
await
WriteResponseHeaders
(
clientStreamWriter
,
response
);
await
clientStreamWriter
.
WriteLineAsync
();
return
false
;
}
else
{
var
headerValue
=
Headers
.
Where
(
t
=>
t
.
Name
==
"Proxy-Authorization"
).
FirstOrDefault
().
Value
.
Trim
();
if
(!
headerValue
.
ToLower
().
StartsWith
(
"basic"
))
{
//Return not authorized
await
WriteResponseStatus
(
new
Version
(
1
,
1
),
"407"
,
"Proxy Authentication Invalid"
,
clientStreamWriter
);
var
response
=
new
Response
();
response
.
ResponseHeaders
=
new
Dictionary
<
string
,
HttpHeader
>();
response
.
ResponseHeaders
.
Add
(
"Proxy-Authenticate"
,
new
HttpHeader
(
"Proxy-Authenticate"
,
"Basic realm=\"TitaniumProxy\""
));
response
.
ResponseHeaders
.
Add
(
"Proxy-Connection"
,
new
HttpHeader
(
"Proxy-Connection"
,
"close"
));
await
WriteResponseHeaders
(
clientStreamWriter
,
response
);
await
clientStreamWriter
.
WriteLineAsync
();
return
false
;
}
headerValue
=
headerValue
.
Substring
(
5
).
Trim
();
var
decoded
=
Encoding
.
UTF8
.
GetString
(
Convert
.
FromBase64String
(
headerValue
));
if
(
decoded
.
Contains
(
":"
)
==
false
)
{
//Return not authorized
await
WriteResponseStatus
(
new
Version
(
1
,
1
),
"407"
,
"Proxy Authentication Invalid"
,
clientStreamWriter
);
var
response
=
new
Response
();
response
.
ResponseHeaders
=
new
Dictionary
<
string
,
HttpHeader
>();
response
.
ResponseHeaders
.
Add
(
"Proxy-Authenticate"
,
new
HttpHeader
(
"Proxy-Authenticate"
,
"Basic realm=\"TitaniumProxy\""
));
response
.
ResponseHeaders
.
Add
(
"Proxy-Connection"
,
new
HttpHeader
(
"Proxy-Connection"
,
"close"
));
await
WriteResponseHeaders
(
clientStreamWriter
,
response
);
await
clientStreamWriter
.
WriteLineAsync
();
return
false
;
}
var
username
=
decoded
.
Substring
(
0
,
decoded
.
IndexOf
(
':'
));
var
password
=
decoded
.
Substring
(
decoded
.
IndexOf
(
':'
)
+
1
);
return
await
AuthenticateUserFunc
(
username
,
password
).
ConfigureAwait
(
false
);
}
}
catch
(
Exception
e
)
{
ExceptionFunc
(
e
);
//Return not authorized
await
WriteResponseStatus
(
new
Version
(
1
,
1
),
"407"
,
"Proxy Authentication Invalid"
,
clientStreamWriter
);
var
response
=
new
Response
();
response
.
ResponseHeaders
=
new
Dictionary
<
string
,
HttpHeader
>();
response
.
ResponseHeaders
.
Add
(
"Proxy-Authenticate"
,
new
HttpHeader
(
"Proxy-Authenticate"
,
"Basic realm=\"TitaniumProxy\""
));
response
.
ResponseHeaders
.
Add
(
"Proxy-Connection"
,
new
HttpHeader
(
"Proxy-Connection"
,
"close"
));
await
WriteResponseHeaders
(
clientStreamWriter
,
response
);
await
clientStreamWriter
.
WriteLineAsync
();
return
false
;
}
}
}
}
Titanium.Web.Proxy/ProxyServer.cs
View file @
e170b5ae
...
...
@@ -17,37 +17,16 @@ namespace Titanium.Web.Proxy
/// </summary>
public
partial
class
ProxyServer
:
IDisposable
{
private
static
readonly
Lazy
<
Action
<
Exception
>>
_defaultExceptionFunc
=
new
Lazy
<
Action
<
Exception
>>(()
=>
(
e
=>
{
}));
private
static
Action
<
Exception
>
_exceptionFunc
;
public
static
Action
<
Exception
>
ExceptionFunc
{
get
{
return
_exceptionFunc
??
_defaultExceptionFunc
.
Value
;
}
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 root certificate used by this proxy is valid?
/// </summary>
private
bool
certValidated
{
get
;
set
;
}
/// <summary>
/// Is the proxy currently running
/// </summary>
private
bool
proxyRunning
{
get
;
set
;
}
/// <summary>
/// Manages certificates used by this proxy
...
...
@@ -55,26 +34,26 @@ namespace Titanium.Web.Proxy
private
CertificateManager
certificateCacheManager
{
get
;
set
;
}
/// <summary>
/// A
object that creates tcp connection to server
/// A
n default exception log func
/// </summary>
private
TcpConnectionFactory
tcpConnectionFactory
{
get
;
set
;
}
private
readonly
Lazy
<
Action
<
Exception
>>
defaultExceptionFunc
=
new
Lazy
<
Action
<
Exception
>>(()
=>
(
e
=>
{
}));
/// <summary>
///
Manage system proxy settings
///
backing exception func for exposed public property
/// </summary>
private
SystemProxyManager
systemProxySettingsManager
{
get
;
set
;
}
private
FireFoxProxySettingsManager
firefoxProxySettingsManager
{
get
;
set
;
}
private
Action
<
Exception
>
exceptionFunc
;
/// <summary>
///
Does the root certificate used by this proxy is trusted by the machine?
///
A object that creates tcp connection to server
/// </summary>
private
bool
certTrusted
{
get
;
set
;
}
private
TcpConnectionFactory
tcpConnectionFactory
{
get
;
set
;
}
/// <summary>
///
Is the proxy currently running
///
Manage system proxy settings
/// </summary>
private
bool
proxyRunning
{
get
;
set
;
}
private
SystemProxyManager
systemProxySettingsManager
{
get
;
set
;
}
private
FireFoxProxySettingsManager
firefoxProxySettingsManager
{
get
;
set
;
}
/// <summary>
/// Buffer size used throughout this proxy
...
...
@@ -88,9 +67,19 @@ namespace Titanium.Web.Proxy
/// <summary>
/// Name of the root certificate
/// If no certificate is provided then a default Root Certificate will be created and used
/// The provided root certificate has to be in the proxy exe directory with the private key
/// The root certificate file should be named as "rootCert.pfx"
/// </summary>
public
string
RootCertificateName
{
get
;
set
;
}
/// <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
;
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
...
...
@@ -137,6 +126,51 @@ namespace Titanium.Web.Proxy
/// </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>
/// 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
public
Func
<
SessionEventArgs
,
Task
<
ExternalProxy
>>
GetCustomUpStreamHttpsProxyFunc
{
get
;
set
;
}
/// <summary>
/// A list of IpAddress & port this proxy is listening to
/// </summary>
...
...
@@ -180,7 +214,7 @@ namespace Titanium.Web.Proxy
/// <param name="endPoint"></param>
public
void
AddEndPoint
(
ProxyEndPoint
endPoint
)
{
if
(
ProxyEndPoints
.
Any
(
x
=>
x
.
IpAddress
==
endPoint
.
IpAddress
&&
x
.
Port
==
endPoint
.
Port
))
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"
);
}
...
...
@@ -254,13 +288,14 @@ namespace Titanium.Web.Proxy
//If certificate was trusted by the machine
if
(
cert
Trus
ted
)
if
(
cert
Valida
ted
)
{
systemProxySettingsManager
.
SetHttpsProxy
(
Equals
(
endPoint
.
IpAddress
,
IPAddress
.
Any
)
|
Equals
(
endPoint
.
IpAddress
,
IPAddress
.
Loopback
)
?
"127.0.0.1"
:
endPoint
.
IpAddress
.
ToString
(),
endPoint
.
Port
);
}
endPoint
.
IsSystemHttpsProxy
=
true
;
#if !DEBUG
...
...
@@ -304,9 +339,14 @@ namespace Titanium.Web.Proxy
}
certificateCacheManager
=
new
CertificateManager
(
RootCertificateIssuerName
,
RootCertificateName
);
RootCertificateName
,
ExceptionFunc
);
certTrusted
=
certificateCacheManager
.
CreateTrustedRootCertificate
();
certValidated
=
certificateCacheManager
.
CreateTrustedRootCertificate
();
if
(
TrustRootCertificate
)
{
certificateCacheManager
.
TrustRootCertificate
();
}
foreach
(
var
endPoint
in
ProxyEndPoints
)
{
...
...
@@ -442,6 +482,12 @@ namespace Titanium.Web.Proxy
{
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
.
Client
.
Shutdown
(
SocketShutdown
.
Both
);
tcpClient
.
Client
.
Close
();
tcpClient
.
Client
.
Dispose
();
...
...
Titanium.Web.Proxy/RequestHandler.cs
View file @
e170b5ae
...
...
@@ -25,83 +25,6 @@ namespace Titanium.Web.Proxy
partial
class
ProxyServer
{
private
async
Task
<
bool
>
CheckAuthorization
(
StreamWriter
clientStreamWriter
,
IEnumerable
<
HttpHeader
>
Headers
)
{
if
(
AuthenticateUserFunc
==
null
)
{
return
true
;
}
try
{
if
(!
Headers
.
Where
(
t
=>
t
.
Name
==
"Proxy-Authorization"
).
Any
())
{
await
WriteResponseStatus
(
new
Version
(
1
,
1
),
"407"
,
"Proxy Authentication Required"
,
clientStreamWriter
);
var
response
=
new
Response
();
response
.
ResponseHeaders
=
new
Dictionary
<
string
,
HttpHeader
>();
response
.
ResponseHeaders
.
Add
(
"Proxy-Authenticate"
,
new
HttpHeader
(
"Proxy-Authenticate"
,
"Basic realm=\"TitaniumProxy\""
));
response
.
ResponseHeaders
.
Add
(
"Proxy-Connection"
,
new
HttpHeader
(
"Proxy-Connection"
,
"close"
));
await
WriteResponseHeaders
(
clientStreamWriter
,
response
);
await
clientStreamWriter
.
WriteLineAsync
();
return
false
;
}
else
{
var
headerValue
=
Headers
.
Where
(
t
=>
t
.
Name
==
"Proxy-Authorization"
).
FirstOrDefault
().
Value
.
Trim
();
if
(!
headerValue
.
ToLower
().
StartsWith
(
"basic"
))
{
//Return not authorized
await
WriteResponseStatus
(
new
Version
(
1
,
1
),
"407"
,
"Proxy Authentication Invalid"
,
clientStreamWriter
);
var
response
=
new
Response
();
response
.
ResponseHeaders
=
new
Dictionary
<
string
,
HttpHeader
>();
response
.
ResponseHeaders
.
Add
(
"Proxy-Authenticate"
,
new
HttpHeader
(
"Proxy-Authenticate"
,
"Basic realm=\"TitaniumProxy\""
));
response
.
ResponseHeaders
.
Add
(
"Proxy-Connection"
,
new
HttpHeader
(
"Proxy-Connection"
,
"close"
));
await
WriteResponseHeaders
(
clientStreamWriter
,
response
);
await
clientStreamWriter
.
WriteLineAsync
();
return
false
;
}
headerValue
=
headerValue
.
Substring
(
5
).
Trim
();
var
decoded
=
Encoding
.
UTF8
.
GetString
(
Convert
.
FromBase64String
(
headerValue
));
if
(
decoded
.
Contains
(
":"
)
==
false
)
{
//Return not authorized
await
WriteResponseStatus
(
new
Version
(
1
,
1
),
"407"
,
"Proxy Authentication Invalid"
,
clientStreamWriter
);
var
response
=
new
Response
();
response
.
ResponseHeaders
=
new
Dictionary
<
string
,
HttpHeader
>();
response
.
ResponseHeaders
.
Add
(
"Proxy-Authenticate"
,
new
HttpHeader
(
"Proxy-Authenticate"
,
"Basic realm=\"TitaniumProxy\""
));
response
.
ResponseHeaders
.
Add
(
"Proxy-Connection"
,
new
HttpHeader
(
"Proxy-Connection"
,
"close"
));
await
WriteResponseHeaders
(
clientStreamWriter
,
response
);
await
clientStreamWriter
.
WriteLineAsync
();
return
false
;
}
var
username
=
decoded
.
Substring
(
0
,
decoded
.
IndexOf
(
':'
));
var
password
=
decoded
.
Substring
(
decoded
.
IndexOf
(
':'
)
+
1
);
return
await
AuthenticateUserFunc
(
username
,
password
).
ConfigureAwait
(
false
);
}
}
catch
(
Exception
e
)
{
//Return not authorized
await
WriteResponseStatus
(
new
Version
(
1
,
1
),
"407"
,
"Proxy Authentication Invalid"
,
clientStreamWriter
);
var
response
=
new
Response
();
response
.
ResponseHeaders
=
new
Dictionary
<
string
,
HttpHeader
>();
response
.
ResponseHeaders
.
Add
(
"Proxy-Authenticate"
,
new
HttpHeader
(
"Proxy-Authenticate"
,
"Basic realm=\"TitaniumProxy\""
));
response
.
ResponseHeaders
.
Add
(
"Proxy-Connection"
,
new
HttpHeader
(
"Proxy-Connection"
,
"close"
));
await
WriteResponseHeaders
(
clientStreamWriter
,
response
);
await
clientStreamWriter
.
WriteLineAsync
();
return
false
;
}
}
//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
private
async
Task
HandleClient
(
ExplicitProxyEndPoint
endPoint
,
TcpClient
client
)
...
...
@@ -172,6 +95,7 @@ namespace Titanium.Web.Proxy
var
newHeader
=
new
HttpHeader
(
header
[
0
],
header
[
1
]);
connectRequestHeaders
.
Add
(
newHeader
);
}
if
(
await
CheckAuthorization
(
clientStreamWriter
,
connectRequestHeaders
)
==
false
)
{
Dispose
(
clientStream
,
clientStreamReader
,
clientStreamWriter
,
null
);
...
...
@@ -287,6 +211,7 @@ namespace Titanium.Web.Proxy
else
{
clientStreamReader
=
new
CustomBinaryReader
(
clientStream
);
clientStreamWriter
=
new
StreamWriter
(
clientStream
);
}
//now read the request line
...
...
@@ -413,7 +338,7 @@ namespace Titanium.Web.Proxy
}
catch
(
Exception
e
)
{
ProxyServer
.
ExceptionFunc
(
e
);
ExceptionFunc
(
e
);
Dispose
(
args
.
ProxyClient
.
ClientStream
,
args
.
ProxyClient
.
ClientStreamReader
,
args
.
ProxyClient
.
ClientStreamWriter
,
args
);
return
;
}
...
...
@@ -525,7 +450,6 @@ namespace Titanium.Web.Proxy
}
PrepareRequestHeaders
(
args
.
WebSession
.
Request
.
RequestHeaders
,
args
.
WebSession
);
args
.
WebSession
.
Request
.
Host
=
args
.
WebSession
.
Request
.
RequestUri
.
Authority
;
...
...
@@ -577,7 +501,7 @@ namespace Titanium.Web.Proxy
}
catch
(
Exception
e
)
{
ProxyServer
.
ExceptionFunc
(
e
);
ExceptionFunc
(
e
);
Dispose
(
clientStream
,
clientStreamReader
,
clientStreamWriter
,
args
);
break
;
}
...
...
Titanium.Web.Proxy/ResponseHandler.cs
View file @
e170b5ae
...
...
@@ -30,6 +30,7 @@ namespace Titanium.Web.Proxy
}
args
.
ReRequest
=
false
;
//If user requested call back then do it
if
(
BeforeResponse
!=
null
&&
!
args
.
WebSession
.
Response
.
ResponseLocked
)
{
...
...
@@ -43,11 +44,13 @@ namespace Titanium.Web.Proxy
await
Task
.
WhenAll
(
handlerTasks
);
}
if
(
args
.
ReRequest
)
{
await
HandleHttpSessionRequestInternal
(
null
,
args
,
null
,
null
,
true
).
ConfigureAwait
(
false
);
return
;
}
args
.
WebSession
.
Response
.
ResponseLocked
=
true
;
//Write back to client 100-conitinue response if that's what server returned
...
...
@@ -116,8 +119,9 @@ namespace Titanium.Web.Proxy
await
args
.
ProxyClient
.
ClientStream
.
FlushAsync
();
}
catch
catch
(
Exception
e
)
{
ExceptionFunc
(
e
);
Dispose
(
args
.
ProxyClient
.
ClientStream
,
args
.
ProxyClient
.
ClientStreamReader
,
args
.
ProxyClient
.
ClientStreamWriter
,
args
);
}
...
...
Titanium.Web.Proxy/Titanium.Web.Proxy.csproj
View file @
e170b5ae
...
...
@@ -65,7 +65,7 @@
<Compile
Include=
"EventArguments\CertificateValidationEventArgs.cs"
/>
<Compile
Include=
"Extensions\ByteArrayExtensions.cs"
/>
<Compile
Include=
"Network\CachedCertificate.cs"
/>
<Compile
Include=
"Network\Cert
EnrollEngine
.cs"
/>
<Compile
Include=
"Network\Cert
ificateMaker
.cs"
/>
<Compile
Include=
"Network\ProxyClient.cs"
/>
<Compile
Include=
"Exceptions\BodyNotFoundException.cs"
/>
<Compile
Include=
"Extensions\HttpWebResponseExtensions.cs"
/>
...
...
@@ -83,6 +83,7 @@
<Compile
Include=
"Models\HttpHeader.cs"
/>
<Compile
Include=
"Http\HttpWebClient.cs"
/>
<Compile
Include=
"Properties\AssemblyInfo.cs"
/>
<Compile
Include=
"ProxyAuthorizationHandler.cs"
/>
<Compile
Include=
"RequestHandler.cs"
/>
<Compile
Include=
"ResponseHandler.cs"
/>
<Compile
Include=
"Helpers\CustomBinaryReader.cs"
/>
...
...
Titanium.Web.Proxy/Titanium.Web.Proxy.nuspec
View file @
e170b5ae
...
...
@@ -19,6 +19,5 @@
</metadata>
<files>
<file
src=
"bin\$configuration$\Titanium.Web.Proxy.dll"
target=
"lib\net45"
/>
<file
src=
"bin\$configuration$\makecert.exe"
target=
"content"
/>
</files>
</package>
\ No newline at end of file
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