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
d9331b49
Commit
d9331b49
authored
May 14, 2018
by
justcoding121
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
use polly for retries
parent
c9c47fab
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
106 additions
and
87 deletions
+106
-87
ProxyTestController.cs
.../Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs
+1
-1
ExplicitClientHandler.cs
Titanium.Web.Proxy/ExplicitClientHandler.cs
+24
-21
TcpConnectionFactory.cs
Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs
+3
-2
ProxyServer.cs
Titanium.Web.Proxy/ProxyServer.cs
+29
-3
RequestHandler.cs
Titanium.Web.Proxy/RequestHandler.cs
+38
-52
Titanium.Web.Proxy.csproj
Titanium.Web.Proxy/Titanium.Web.Proxy.csproj
+1
-0
Titanium.Web.Proxy.nuspec
Titanium.Web.Proxy/Titanium.Web.Proxy.nuspec
+1
-0
TransparentClientHandler.cs
Titanium.Web.Proxy/TransparentClientHandler.cs
+8
-8
packages.config
Titanium.Web.Proxy/packages.config
+1
-0
No files found.
Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs
View file @
d9331b49
...
...
@@ -23,7 +23,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
public
ProxyTestController
()
{
proxyServer
=
new
ProxyServer
();
//proxyServer.EnableConnectionPool = fals
e;
proxyServer
.
EnableConnectionPool
=
tru
e
;
// generate root certificate without storing it in file system
//proxyServer.CertificateManager.CreateRootCertificate(false);
...
...
Titanium.Web.Proxy/ExplicitClientHandler.cs
View file @
d9331b49
using
System
;
using
System.Collections.Generic
;
using
System.IO
;
using
System.Net
;
using
System.Net.Security
;
...
...
@@ -140,17 +141,17 @@ namespace Titanium.Web.Proxy
// test server HTTP/2 support
// todo: this is a hack, because Titanium does not support HTTP protocol changing currently
var
connection
=
await
getServerConnection
(
connectArgs
,
true
,
SslExtensions
.
Http2ProtocolAsList
,
cancellationToken
);
SslExtensions
.
Http2ProtocolAsList
,
true
,
cancellationToken
);
http2Supported
=
connection
.
NegotiatedApplicationProtocol
==
SslApplicationProtocol
.
Http2
;
//release connection back to pool intead of closing when connection pool is enabled.
await
tcpConnectionFactory
.
Release
(
connection
);
await
tcpConnectionFactory
.
Release
(
connection
,
true
);
}
SslStream
sslStream
=
null
;
prefetchConnectionTask
=
getServerConnection
(
connectArgs
,
true
,
null
,
cancellationToken
);
null
,
false
,
cancellationToken
);
try
{
sslStream
=
new
SslStream
(
clientStream
);
...
...
@@ -209,8 +210,8 @@ namespace Titanium.Web.Proxy
// create new connection to server.
// If we detected that client tunnel CONNECTs without SSL by checking for empty client hello then
// this connection should not be HTTPS.
var
connection
=
await
getServerConnection
(
connectArgs
,
true
,
null
,
cancellationToken
);
var
connection
=
await
getServerConnection
(
connectArgs
,
true
,
SslExtensions
.
Http2ProtocolAsList
,
true
,
cancellationToken
);
try
{
...
...
@@ -224,10 +225,9 @@ namespace Titanium.Web.Proxy
try
{
// clientStream.Available sbould be at most BufferSize because it is using the same buffer size
await
clientStream
.
ReadAsync
(
data
,
0
,
available
,
cancellationToken
);
await
connection
.
StreamWriter
.
WriteAsync
(
data
,
0
,
available
,
true
,
cancellationToken
);
// clientStream.Available sbould be at most BufferSize because it is using the same buffer size
await
connection
.
StreamWriter
.
WriteAsync
(
data
,
0
,
available
,
true
,
cancellationToken
);
}
finally
{
...
...
@@ -235,8 +235,7 @@ namespace Titanium.Web.Proxy
}
}
var
serverHelloInfo
=
await
SslTools
.
PeekServerHello
(
connection
.
Stream
,
BufferPool
,
cancellationToken
);
var
serverHelloInfo
=
await
SslTools
.
PeekServerHello
(
connection
.
Stream
,
BufferPool
,
cancellationToken
);
((
ConnectResponse
)
connectArgs
.
WebSession
.
Response
).
ServerHelloInfo
=
serverHelloInfo
;
}
...
...
@@ -244,11 +243,14 @@ namespace Titanium.Web.Proxy
(
buffer
,
offset
,
count
)
=>
{
connectArgs
.
OnDataSent
(
buffer
,
offset
,
count
);
},
(
buffer
,
offset
,
count
)
=>
{
connectArgs
.
OnDataReceived
(
buffer
,
offset
,
count
);
},
connectArgs
.
CancellationTokenSource
,
ExceptionFunc
);
}
finally
{
await
tcpConnectionFactory
.
Release
(
connection
,
true
);
}
calledRequestHandler
=
true
;
return
;
}
}
...
...
@@ -278,22 +280,22 @@ namespace Titanium.Web.Proxy
throw
new
Exception
(
$"HTTP/2 Protocol violation. Empty string expected, '
{
line
}
' received"
);
}
// create new connection
var
connection
=
await
getServerConnection
(
connectArgs
,
true
,
SslExtensions
.
Http2ProtocolAsList
,
cancellationToken
);
var
connection
=
await
getServerConnection
(
connectArgs
,
true
,
SslExtensions
.
Http2ProtocolAsList
,
true
,
cancellationToken
);
try
{
await
connection
.
StreamWriter
.
WriteLineAsync
(
"PRI * HTTP/2.0"
,
cancellationToken
);
await
connection
.
StreamWriter
.
WriteLineAsync
(
cancellationToken
);
await
connection
.
StreamWriter
.
WriteLineAsync
(
"SM"
,
cancellationToken
);
await
connection
.
StreamWriter
.
WriteLineAsync
(
cancellationToken
);
#if NETCOREAPP2_1
await
Http2Helper
.
SendHttp2
(
clientStream
,
connection
.
Stream
,
BufferSize
,
(
buffer
,
offset
,
count
)
=>
{
connectArgs
.
OnDataSent
(
buffer
,
offset
,
count
);
},
(
buffer
,
offset
,
count
)
=>
{
connectArgs
.
OnDataReceived
(
buffer
,
offset
,
count
);
},
connectArgs
.
CancellationTokenSource
,
clientConnection
.
Id
,
ExceptionFunc
);
#endif
}
finally
{
...
...
@@ -301,6 +303,7 @@ namespace Titanium.Web.Proxy
}
}
}
calledRequestHandler
=
true
;
// Now create the request
await
handleHttpSessionRequest
(
endPoint
,
clientConnection
,
clientStream
,
clientStreamWriter
,
...
...
@@ -328,19 +331,19 @@ namespace Titanium.Web.Proxy
}
finally
{
if
(!
calledRequestHandler
&&
prefetchConnectionTask
!=
null
)
{
var
connection
=
await
prefetchConnectionTask
;
await
tcpConnectionFactory
.
Release
(
connection
,
closeServerConnection
);
}
clientStream
.
Dispose
();
if
(!
cancellationTokenSource
.
IsCancellationRequested
)
{
cancellationTokenSource
.
Cancel
();
}
if
(!
calledRequestHandler
&&
prefetchConnectionTask
!=
null
)
{
var
connection
=
await
prefetchConnectionTask
;
await
tcpConnectionFactory
.
Release
(
connection
,
closeServerConnection
);
}
}
}
}
...
...
Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs
View file @
d9331b49
...
...
@@ -81,18 +81,19 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// <param name="proxyServer">The current ProxyServer instance.</param>
/// <param name="upStreamEndPoint">The local upstream endpoint to make request via.</param>
/// <param name="externalProxy">The external proxy to make request via.</param>
/// <param name="noCache">Not from cache/create new connection.</param>
/// <param name="cancellationToken">The cancellation token for this async task.</param>
/// <returns></returns>
internal
async
Task
<
TcpServerConnection
>
GetClient
(
string
remoteHostName
,
int
remotePort
,
Version
httpVersion
,
bool
isHttps
,
List
<
SslApplicationProtocol
>
applicationProtocols
,
bool
isConnect
,
ProxyServer
proxyServer
,
IPEndPoint
upStreamEndPoint
,
ExternalProxy
externalProxy
,
CancellationToken
cancellationToken
)
bool
noCache
,
CancellationToken
cancellationToken
)
{
var
cacheKey
=
GetConnectionCacheKey
(
remoteHostName
,
remotePort
,
isHttps
,
applicationProtocols
,
proxyServer
,
upStreamEndPoint
,
externalProxy
);
if
(
proxyServer
.
EnableConnectionPool
)
if
(
proxyServer
.
EnableConnectionPool
&&
!
noCache
)
{
if
(
cache
.
TryGetValue
(
cacheKey
,
out
var
existingConnections
))
{
...
...
Titanium.Web.Proxy/ProxyServer.cs
View file @
d9331b49
...
...
@@ -7,6 +7,7 @@ using System.Security.Authentication;
using
System.Security.Cryptography.X509Certificates
;
using
System.Threading
;
using
System.Threading.Tasks
;
using
Polly
;
using
StreamExtended
;
using
StreamExtended.Network
;
using
Titanium.Web.Proxy.EventArguments
;
...
...
@@ -123,6 +124,9 @@ namespace Titanium.Web.Proxy
/// </summary>
private
SystemProxyManager
systemProxySettingsManager
{
get
;
}
//Number of exception retries when connection pool is enabled.
private
int
retries
=>
EnableConnectionPool
?
MaxCachedConnections
+
1
:
0
;
/// <summary>
/// Is the proxy currently running?
/// </summary>
...
...
@@ -197,7 +201,7 @@ namespace Titanium.Web.Proxy
/// Realm used during Proxy Basic Authentication.
/// </summary>
public
string
ProxyRealm
{
get
;
set
;
}
=
"TitaniumProxy"
;
/// <summary>
/// List of supported Ssl versions.
/// </summary>
...
...
@@ -252,7 +256,8 @@ namespace Titanium.Web.Proxy
public
ExceptionHandler
ExceptionFunc
{
get
=>
exceptionFunc
??
defaultExceptionFunc
;
set
{
set
{
exceptionFunc
=
value
;
CertificateManager
.
ExceptionFunc
=
value
;
}
...
...
@@ -274,7 +279,7 @@ namespace Titanium.Web.Proxy
{
Stop
();
}
CertificateManager
?.
Dispose
();
BufferPool
?.
Dispose
();
}
...
...
@@ -800,5 +805,26 @@ namespace Titanium.Web.Proxy
await
OnServerConnectionCreate
.
InvokeAsync
(
this
,
client
,
ExceptionFunc
);
}
}
/// <summary>
/// Connection retry policy when using connection pool.
/// </summary>
private
Policy
retryPolicy
<
T
>()
where
T
:
Exception
{
return
Policy
.
Handle
<
T
>()
.
RetryAsync
(
retries
,
onRetryAsync
:
async
(
ex
,
i
,
context
)
=>
{
if
(
context
.
ContainsKey
(
"connection"
))
{
//close connection on error
var
connection
=
(
TcpServerConnection
)
context
[
"connection"
];
await
tcpConnectionFactory
.
Release
(
connection
,
true
);
context
.
Remove
(
"connection"
);
}
});
}
}
}
Titanium.Web.Proxy/RequestHandler.cs
View file @
d9331b49
...
...
@@ -61,7 +61,7 @@ namespace Titanium.Web.Proxy
var
cancellationToken
=
cancellationTokenSource
.
Token
;
var
prefetchTask
=
prefetchConnectionTask
;
TcpServerConnection
serverC
onnection
=
null
;
TcpServerConnection
c
onnection
=
null
;
bool
closeServerConnection
=
false
;
try
...
...
@@ -186,70 +186,56 @@ namespace Titanium.Web.Proxy
}
//If prefetch task is available.
if
(
serverC
onnection
==
null
&&
prefetchTask
!=
null
)
if
(
c
onnection
==
null
&&
prefetchTask
!=
null
)
{
serverC
onnection
=
await
prefetchTask
;
c
onnection
=
await
prefetchTask
;
prefetchTask
=
null
;
}
// create a new connection if cache key changes.
// only gets hit when connection pool is disabled.
// or when prefetch task has a unexpectedly different connection.
if
(
serverC
onnection
!=
null
if
(
c
onnection
!=
null
&&
(
await
getConnectionCacheKey
(
args
,
clientConnection
.
NegotiatedApplicationProtocol
)
!=
serverC
onnection
.
CacheKey
))
!=
c
onnection
.
CacheKey
))
{
await
tcpConnectionFactory
.
Release
(
serverC
onnection
);
serverC
onnection
=
null
;
await
tcpConnectionFactory
.
Release
(
c
onnection
);
c
onnection
=
null
;
}
//create
if
(
serverConnection
=
=
null
)
var
contextData
=
new
Dictionary
<
string
,
object
>();
if
(
connection
!
=
null
)
{
serverConnection
=
await
getServerConnection
(
args
,
false
,
clientConnection
.
NegotiatedApplicationProtocol
,
cancellationToken
);
contextData
.
Add
(
"connection"
,
connection
);
}
//for connection pool retry fails until cache is exhausted
int
attempt
=
0
;
while
(
attempt
<
MaxCachedConnections
+
1
)
//for connection pool retry fails until cache is exhausted
await
retryPolicy
<
ServerConnectionException
>().
ExecuteAsync
(
async
(
context
)
=>
{
try
if
(
connection
==
null
)
{
// if upgrading to websocket then relay the request without reading the contents
if
(
request
.
UpgradeToWebSocket
)
{
await
handleWebSocketUpgrade
(
httpCmd
,
args
,
request
,
response
,
clientStream
,
clientStreamWriter
,
serverConnection
,
cancellationTokenSource
,
cancellationToken
);
closeServerConnection
=
true
;
return
;
}
// construct the web request that we are going to issue on behalf of the client.
await
handleHttpSessionRequestInternal
(
serverConnection
,
args
);
connection
=
await
getServerConnection
(
args
,
false
,
clientConnection
.
NegotiatedApplicationProtocol
,
false
,
cancellationToken
);
context
[
"connection"
]
=
connection
;
}
//connection pool retry
catch
(
ServerConnectionException
)
// if upgrading to websocket then relay the request without reading the contents
if
(
request
.
UpgradeToWebSocket
)
{
attempt
++;
if
(!
EnableConnectionPool
||
attempt
==
MaxCachedConnections
+
1
)
{
throw
;
}
//get new connection from pool
await
tcpConnectionFactory
.
Release
(
serverConnection
,
true
);
serverConnection
=
null
;
serverConnection
=
await
getServerConnection
(
args
,
false
,
clientConnection
.
NegotiatedApplicationProtocol
,
cancellationToken
);
continue
;
await
handleWebSocketUpgrade
(
httpCmd
,
args
,
request
,
response
,
clientStream
,
clientStreamWriter
,
connection
,
cancellationTokenSource
,
cancellationToken
);
closeServerConnection
=
true
;
return
;
}
break
;
}
// construct the web request that we are going to issue on behalf of the client.
await
handleHttpSessionRequestInternal
(
connection
,
args
);
},
contextData
);
//user requested
if
(
args
.
WebSession
.
CloseServerConnection
)
...
...
@@ -275,8 +261,8 @@ namespace Titanium.Web.Proxy
//between sessions without using it.
if
(
EnableConnectionPool
)
{
await
tcpConnectionFactory
.
Release
(
serverC
onnection
);
serverConnection
=
null
;
await
tcpConnectionFactory
.
Release
(
c
onnection
);
connection
=
null
;
}
}
...
...
@@ -300,10 +286,10 @@ namespace Titanium.Web.Proxy
}
finally
{
await
tcpConnectionFactory
.
Release
(
serverC
onnection
,
await
tcpConnectionFactory
.
Release
(
c
onnection
,
closeServerConnection
);
if
(
prefetchTask
!=
null
)
if
(
prefetchTask
!=
null
)
{
await
tcpConnectionFactory
.
Release
(
await
prefetchTask
,
closeServerConnection
);
...
...
@@ -515,7 +501,7 @@ namespace Titanium.Web.Proxy
/// <param name="cancellationToken">The cancellation token for this async task.</param>
/// <returns></returns>
private
Task
<
TcpServerConnection
>
getServerConnection
(
SessionEventArgsBase
args
,
bool
isConnect
,
SslApplicationProtocol
applicationProtocol
,
CancellationToken
cancellationToken
)
SslApplicationProtocol
applicationProtocol
,
bool
noCache
,
CancellationToken
cancellationToken
)
{
List
<
SslApplicationProtocol
>
applicationProtocols
=
null
;
if
(
applicationProtocol
!=
default
)
...
...
@@ -523,7 +509,7 @@ namespace Titanium.Web.Proxy
applicationProtocols
=
new
List
<
SslApplicationProtocol
>
{
applicationProtocol
};
}
return
getServerConnection
(
args
,
isConnect
,
applicationProtocols
,
cancellationToken
);
return
getServerConnection
(
args
,
isConnect
,
applicationProtocols
,
noCache
,
cancellationToken
);
}
/// <summary>
...
...
@@ -535,7 +521,7 @@ namespace Titanium.Web.Proxy
/// <param name="cancellationToken">The cancellation token for this async task.</param>
/// <returns></returns>
private
async
Task
<
TcpServerConnection
>
getServerConnection
(
SessionEventArgsBase
args
,
bool
isConnect
,
List
<
SslApplicationProtocol
>
applicationProtocols
,
CancellationToken
cancellationToken
)
List
<
SslApplicationProtocol
>
applicationProtocols
,
bool
noCache
,
CancellationToken
cancellationToken
)
{
ExternalProxy
customUpStreamProxy
=
null
;
...
...
@@ -554,7 +540,7 @@ namespace Titanium.Web.Proxy
isHttps
,
applicationProtocols
,
isConnect
,
this
,
args
.
WebSession
.
UpStreamEndPoint
??
UpStreamEndPoint
,
customUpStreamProxy
??
(
isHttps
?
UpStreamHttpsProxy
:
UpStreamHttpProxy
),
cancellationToken
);
noCache
,
cancellationToken
);
}
/// <summary>
...
...
Titanium.Web.Proxy/Titanium.Web.Proxy.csproj
View file @
d9331b49
...
...
@@ -12,6 +12,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Polly" Version="6.0.1" />
<PackageReference Include="Portable.BouncyCastle" Version="1.8.2" />
<PackageReference Include="StreamExtended" Version="1.0.175-beta" />
</ItemGroup>
...
...
Titanium.Web.Proxy/Titanium.Web.Proxy.nuspec
View file @
d9331b49
...
...
@@ -16,6 +16,7 @@
<dependencies>
<dependency
id=
"StreamExtended"
version=
"1.0.175-beta"
/>
<dependency
id=
"Portable.BouncyCastle"
version=
"1.8.2"
/>
<dependency
id=
"Polly"
version=
"6.0.1"
/>
</dependencies>
</metadata>
<files>
...
...
Titanium.Web.Proxy/TransparentClientHandler.cs
View file @
d9331b49
using
System
;
using
System.Collections.Generic
;
using
System.IO
;
using
System.Net.Security
;
using
System.Net.Sockets
;
...
...
@@ -63,8 +64,8 @@ namespace Titanium.Web.Proxy
if
(
endPoint
.
DecryptSsl
&&
args
.
DecryptSsl
)
{
prefetchConnectionTask
=
tcpConnectionFactory
.
GetClient
(
httpsHostName
,
endPoint
.
Port
,
null
,
true
,
null
,
false
,
this
,
UpStreamEndPoint
,
UpStreamHttpsProxy
,
cancellationToken
);
null
,
true
,
null
,
false
,
this
,
UpStreamEndPoint
,
UpStreamHttpsProxy
,
false
,
cancellationToken
);
SslStream
sslStream
=
null
;
...
...
@@ -93,25 +94,24 @@ namespace Titanium.Web.Proxy
}
else
{
// create new connection
var
connection
=
await
tcpConnectionFactory
.
GetClient
(
httpsHostName
,
endPoint
.
Port
,
null
,
false
,
null
,
true
,
this
,
UpStreamEndPoint
,
UpStreamHttpsProxy
,
cancellationToken
);
null
,
false
,
null
,
true
,
this
,
UpStreamEndPoint
,
UpStreamHttpsProxy
,
true
,
cancellationToken
);
try
{
var
serverStream
=
connection
.
Stream
;
CustomBufferedStream
serverStream
=
null
;
int
available
=
clientStream
.
Available
;
if
(
available
>
0
)
{
// send the buffered data
var
data
=
BufferPool
.
GetBuffer
(
BufferSize
);
try
{
// clientStream.Available sbould be at most BufferSize because it is using the same buffer size
await
clientStream
.
ReadAsync
(
data
,
0
,
available
,
cancellationToken
);
serverStream
=
connection
.
Stream
;
await
serverStream
.
WriteAsync
(
data
,
0
,
available
,
cancellationToken
);
await
serverStream
.
FlushAsync
(
cancellationToken
);
}
...
...
Titanium.Web.Proxy/packages.config
View file @
d9331b49
...
...
@@ -3,4 +3,5 @@
<
packages
>
<
package
id
=
"Portable.BouncyCastle"
version
=
"1.8.2"
targetFramework
=
"net45"
/>
<
package
id
=
"StreamExtended"
version
=
"1.0.175-beta"
targetFramework
=
"net45"
/>
<
package
id
=
"Polly"
version
=
"6.0.1"
targetFramework
=
"net45"
/>
</
packages
>
\ 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