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
80dd44e5
Unverified
Commit
80dd44e5
authored
Jan 02, 2018
by
honfika
Committed by
GitHub
Jan 02, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #353 from justcoding121/develop
beta|develop
parents
7a44e456
5fe25998
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
35 changed files
with
971 additions
and
637 deletions
+971
-637
ProxyTestController.cs
.../Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs
+18
-2
MainWindow.xaml.cs
Examples/Titanium.Web.Proxy.Examples.Wpf/MainWindow.xaml.cs
+9
-5
SessionListItem.cs
Examples/Titanium.Web.Proxy.Examples.Wpf/SessionListItem.cs
+25
-5
Titanium.Web.Proxy.Examples.Wpf.csproj
...Proxy.Examples.Wpf/Titanium.Web.Proxy.Examples.Wpf.csproj
+2
-2
packages.config
Examples/Titanium.Web.Proxy.Examples.Wpf/packages.config
+1
-1
AsyncEventHandler.cs
Titanium.Web.Proxy/EventArguments/AsyncEventHandler.cs
+7
-0
SessionEventArgs.cs
Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs
+196
-71
TunnelConnectEventArgs.cs
Titanium.Web.Proxy/EventArguments/TunnelConnectEventArgs.cs
+4
-3
FuncExtensions.cs
Titanium.Web.Proxy/Extensions/FuncExtensions.cs
+16
-5
SslExtensions.cs
Titanium.Web.Proxy/Extensions/SslExtensions.cs
+22
-0
StreamExtensions.cs
Titanium.Web.Proxy/Extensions/StreamExtensions.cs
+38
-62
TcpExtensions.cs
Titanium.Web.Proxy/Extensions/TcpExtensions.cs
+43
-1
HttpHelper.cs
Titanium.Web.Proxy/Helpers/HttpHelper.cs
+10
-2
HttpRequestWriter.cs
Titanium.Web.Proxy/Helpers/HttpRequestWriter.cs
+1
-2
HttpResponseWriter.cs
Titanium.Web.Proxy/Helpers/HttpResponseWriter.cs
+2
-103
HttpWriter.cs
Titanium.Web.Proxy/Helpers/HttpWriter.cs
+200
-13
Tcp.cs
Titanium.Web.Proxy/Helpers/Tcp.cs
+8
-2
ConnectResponse.cs
Titanium.Web.Proxy/Http/ConnectResponse.cs
+1
-1
HeaderCollection.cs
Titanium.Web.Proxy/Http/HeaderCollection.cs
+19
-6
HttpWebClient.cs
Titanium.Web.Proxy/Http/HttpWebClient.cs
+25
-40
KnownHeaders.cs
Titanium.Web.Proxy/Http/KnownHeaders.cs
+53
-0
Request.cs
Titanium.Web.Proxy/Http/Request.cs
+22
-17
Response.cs
Titanium.Web.Proxy/Http/Response.cs
+29
-17
HttpHeader.cs
Titanium.Web.Proxy/Models/HttpHeader.cs
+6
-10
DebugCustomBufferedStream.cs
Titanium.Web.Proxy/Network/DebugCustomBufferedStream.cs
+50
-0
ProxyClient.cs
Titanium.Web.Proxy/Network/ProxyClient.cs
+2
-2
TcpConnection.cs
Titanium.Web.Proxy/Network/Tcp/TcpConnection.cs
+17
-18
TcpConnectionFactory.cs
Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs
+15
-18
ProxyAuthorizationHandler.cs
Titanium.Web.Proxy/ProxyAuthorizationHandler.cs
+12
-12
ProxyServer.cs
Titanium.Web.Proxy/ProxyServer.cs
+10
-26
RequestHandler.cs
Titanium.Web.Proxy/RequestHandler.cs
+93
-168
ResponseHandler.cs
Titanium.Web.Proxy/ResponseHandler.cs
+3
-6
ProxyConstants.cs
Titanium.Web.Proxy/Shared/ProxyConstants.cs
+2
-8
Titanium.Web.Proxy.csproj
Titanium.Web.Proxy/Titanium.Web.Proxy.csproj
+2
-2
WinAuthHandler.cs
Titanium.Web.Proxy/WinAuthHandler.cs
+8
-7
No files found.
Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs
View file @
80dd44e5
...
...
@@ -143,7 +143,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
}
//intecept & cancel redirect or update requests
p
ublic
async
Task
OnRequest
(
object
sender
,
SessionEventArgs
e
)
p
rivate
async
Task
OnRequest
(
object
sender
,
SessionEventArgs
e
)
{
Console
.
WriteLine
(
"Active Client Connections:"
+
((
ProxyServer
)
sender
).
ClientConnectionCount
);
Console
.
WriteLine
(
e
.
WebSession
.
Request
.
Url
);
...
...
@@ -151,6 +151,12 @@ namespace Titanium.Web.Proxy.Examples.Basic
//read request headers
requestHeaderHistory
[
e
.
Id
]
=
e
.
WebSession
.
Request
.
Headers
;
////This sample shows how to get the multipart form data headers
//if (e.WebSession.Request.Host == "mail.yahoo.com" && e.WebSession.Request.IsMultipartFormData)
//{
// e.MultipartRequestPartSent += MultipartRequestPartSent;
//}
if
(
e
.
WebSession
.
Request
.
HasBody
)
{
//Get/Set request body bytes
...
...
@@ -185,7 +191,17 @@ namespace Titanium.Web.Proxy.Examples.Basic
}
//Modify response
public
async
Task
OnResponse
(
object
sender
,
SessionEventArgs
e
)
private
void
MultipartRequestPartSent
(
object
sender
,
MultipartRequestPartSentEventArgs
e
)
{
var
session
=
(
SessionEventArgs
)
sender
;
Console
.
WriteLine
(
"Multipart form data headers:"
);
foreach
(
var
header
in
e
.
Headers
)
{
Console
.
WriteLine
(
header
);
}
}
private
async
Task
OnResponse
(
object
sender
,
SessionEventArgs
e
)
{
Console
.
WriteLine
(
"Active Server Connections:"
+
((
ProxyServer
)
sender
).
ServerConnectionCount
);
...
...
Examples/Titanium.Web.Proxy.Examples.Wpf/MainWindow.xaml.cs
View file @
80dd44e5
...
...
@@ -11,7 +11,6 @@ using Titanium.Web.Proxy.EventArguments;
using
Titanium.Web.Proxy.Helpers
;
using
Titanium.Web.Proxy.Http
;
using
Titanium.Web.Proxy.Models
;
using
Titanium.Web.Proxy.Network
;
namespace
Titanium.Web.Proxy.Examples.Wpf
{
...
...
@@ -63,13 +62,14 @@ namespace Titanium.Web.Proxy.Examples.Wpf
public
MainWindow
()
{
proxyServer
=
new
ProxyServer
();
//proxyServer.CertificateEngine = CertificateEngine.
BouncyCastle
;
//proxyServer.CertificateEngine = CertificateEngine.
DefaultWindows
;
proxyServer
.
TrustRootCertificate
=
true
;
proxyServer
.
CertificateManager
.
TrustRootCertificateAsAdministrator
();
proxyServer
.
ForwardToUpstreamGateway
=
true
;
var
explicitEndPoint
=
new
ExplicitProxyEndPoint
(
IPAddress
.
Any
,
8000
,
true
)
{
ExcludedHttpsHostNameRegex
=
new
[]
{
"ssllabs.com"
},
//IncludedHttpsHostNameRegex = new string[0],
};
...
...
@@ -134,10 +134,9 @@ namespace Titanium.Web.Proxy.Examples.Wpf
SessionListItem
item
=
null
;
await
Dispatcher
.
InvokeAsync
(()
=>
{
if
(
sessionDictionary
.
TryGetValue
(
e
.
WebSession
,
out
var
item2
))
if
(
sessionDictionary
.
TryGetValue
(
e
.
WebSession
,
out
item
))
{
item2
.
Update
();
item
=
item2
;
item
.
Update
();
}
});
...
...
@@ -147,6 +146,11 @@ namespace Titanium.Web.Proxy.Examples.Wpf
{
e
.
WebSession
.
Response
.
KeepBody
=
true
;
await
e
.
GetResponseBody
();
await
Dispatcher
.
InvokeAsync
(()
=>
{
item
.
Update
();
});
}
}
}
...
...
Examples/Titanium.Web.Proxy.Examples.Wpf/SessionListItem.cs
View file @
80dd44e5
...
...
@@ -13,7 +13,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf
private
string
protocol
;
private
string
host
;
private
string
url
;
private
long
bodySize
;
private
long
?
bodySize
;
private
string
process
;
private
long
receivedDataCount
;
private
long
sentDataCount
;
...
...
@@ -48,7 +48,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf
set
=>
SetField
(
ref
url
,
value
);
}
public
long
BodySize
public
long
?
BodySize
{
get
=>
bodySize
;
set
=>
SetField
(
ref
bodySize
,
value
);
...
...
@@ -76,8 +76,11 @@ namespace Titanium.Web.Proxy.Examples.Wpf
protected
void
SetField
<
T
>(
ref
T
field
,
T
value
,[
CallerMemberName
]
string
propertyName
=
null
)
{
field
=
value
;
OnPropertyChanged
(
propertyName
);
if
(!
Equals
(
field
,
value
))
{
field
=
value
;
OnPropertyChanged
(
propertyName
);
}
}
[
NotifyPropertyChangedInvocator
]
...
...
@@ -105,7 +108,24 @@ namespace Titanium.Web.Proxy.Examples.Wpf
Url
=
request
.
RequestUri
.
AbsolutePath
;
}
BodySize
=
response
?.
ContentLength
??
-
1
;
if
(!
IsTunnelConnect
)
{
long
responseSize
=
-
1
;
if
(
response
!=
null
)
{
if
(
response
.
ContentLength
!=
-
1
)
{
responseSize
=
response
.
ContentLength
;
}
else
if
(
response
.
IsBodyRead
&&
response
.
Body
!=
null
)
{
responseSize
=
response
.
Body
.
Length
;
}
}
BodySize
=
responseSize
;
}
Process
=
GetProcessDescription
(
WebSession
.
ProcessId
.
Value
);
}
...
...
Examples/Titanium.Web.Proxy.Examples.Wpf/Titanium.Web.Proxy.Examples.Wpf.csproj
View file @
80dd44e5
...
...
@@ -51,8 +51,8 @@
<WarningLevel>
4
</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference
Include=
"StreamExtended, Version=1.0.1
10
.0, Culture=neutral, PublicKeyToken=bbfa0f1d54f50043, processorArchitecture=MSIL"
>
<HintPath>
..\..\packages\StreamExtended.1.0.1
10
-beta\lib\net45\StreamExtended.dll
</HintPath>
<Reference
Include=
"StreamExtended, Version=1.0.1
32
.0, Culture=neutral, PublicKeyToken=bbfa0f1d54f50043, processorArchitecture=MSIL"
>
<HintPath>
..\..\packages\StreamExtended.1.0.1
32
-beta\lib\net45\StreamExtended.dll
</HintPath>
</Reference>
<Reference
Include=
"System"
/>
<Reference
Include=
"System.Data"
/>
...
...
Examples/Titanium.Web.Proxy.Examples.Wpf/packages.config
View file @
80dd44e5
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
<
packages
>
<
package
id
=
"StreamExtended"
version
=
"1.0.1
10
-beta"
targetFramework
=
"net45"
/>
<
package
id
=
"StreamExtended"
version
=
"1.0.1
32
-beta"
targetFramework
=
"net45"
/>
</
packages
>
\ No newline at end of file
Titanium.Web.Proxy/EventArguments/AsyncEventHandler.cs
0 → 100644
View file @
80dd44e5
using
System
;
using
System.Threading.Tasks
;
namespace
Titanium.Web.Proxy.EventArguments
{
public
delegate
Task
AsyncEventHandler
<
TEventArgs
>(
object
sender
,
TEventArgs
e
);
}
\ No newline at end of file
Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs
View file @
80dd44e5
This diff is collapsed.
Click to expand it.
Titanium.Web.Proxy/EventArguments/TunnelConnectEventArgs.cs
View file @
80dd44e5
using
Titanium.Web.Proxy.Http
;
using
System
;
using
Titanium.Web.Proxy.Http
;
using
Titanium.Web.Proxy.Models
;
namespace
Titanium.Web.Proxy.EventArguments
...
...
@@ -7,8 +8,8 @@ namespace Titanium.Web.Proxy.EventArguments
{
public
bool
IsHttpsConnect
{
get
;
set
;
}
internal
TunnelConnectSessionEventArgs
(
int
bufferSize
,
ProxyEndPoint
endPoint
,
ConnectRequest
connectRequest
)
:
base
(
bufferSize
,
endPoint
,
null
)
internal
TunnelConnectSessionEventArgs
(
int
bufferSize
,
ProxyEndPoint
endPoint
,
ConnectRequest
connectRequest
,
Action
<
Exception
>
exceptionFunc
)
:
base
(
bufferSize
,
endPoint
,
null
,
exceptionFunc
)
{
WebSession
.
Request
=
connectRequest
;
}
...
...
Titanium.Web.Proxy/Extensions/FuncExtensions.cs
View file @
80dd44e5
using
System
;
using
System.Threading.Tasks
;
using
Titanium.Web.Proxy.EventArguments
;
namespace
Titanium.Web.Proxy.Extensions
{
internal
static
class
FuncExtensions
{
public
static
void
InvokeParallel
<
T
>(
this
Func
<
object
,
T
,
Task
>
callback
,
object
sender
,
T
args
)
public
static
void
InvokeParallel
<
T
>(
this
AsyncEventHandler
<
T
>
callback
,
object
sender
,
T
args
)
{
var
invocationList
=
callback
.
GetInvocationList
();
var
handlerTasks
=
new
Task
[
invocationList
.
Length
];
for
(
int
i
=
0
;
i
<
invocationList
.
Length
;
i
++)
{
handlerTasks
[
i
]
=
((
Func
<
object
,
T
,
Task
>)
invocationList
[
i
])(
sender
,
args
);
handlerTasks
[
i
]
=
((
AsyncEventHandler
<
T
>)
invocationList
[
i
])(
sender
,
args
);
}
Task
.
WhenAll
(
handlerTasks
).
Wait
();
}
public
static
async
Task
InvokeParallelAsync
<
T
>(
this
Func
<
object
,
T
,
Task
>
callback
,
object
sender
,
T
args
,
Action
<
Exception
>
exceptionFunc
)
public
static
async
Task
InvokeParallelAsync
<
T
>(
this
AsyncEventHandler
<
T
>
callback
,
object
sender
,
T
args
,
Action
<
Exception
>
exceptionFunc
)
{
var
invocationList
=
callback
.
GetInvocationList
();
var
handlerTasks
=
new
Task
[
invocationList
.
Length
];
for
(
int
i
=
0
;
i
<
invocationList
.
Length
;
i
++)
{
handlerTasks
[
i
]
=
In
vokeAsync
((
Func
<
object
,
T
,
Task
>)
invocationList
[
i
],
sender
,
args
,
exceptionFunc
);
handlerTasks
[
i
]
=
In
ternalInvokeAsync
((
AsyncEventHandler
<
T
>)
invocationList
[
i
],
sender
,
args
,
exceptionFunc
);
}
await
Task
.
WhenAll
(
handlerTasks
);
}
private
static
async
Task
InvokeAsync
<
T
>(
Func
<
object
,
T
,
Task
>
callback
,
object
sender
,
T
args
,
Action
<
Exception
>
exceptionFunc
)
public
static
async
Task
InvokeAsync
<
T
>(
this
AsyncEventHandler
<
T
>
callback
,
object
sender
,
T
args
,
Action
<
Exception
>
exceptionFunc
)
{
var
invocationList
=
callback
.
GetInvocationList
();
for
(
int
i
=
0
;
i
<
invocationList
.
Length
;
i
++)
{
await
InternalInvokeAsync
((
AsyncEventHandler
<
T
>)
invocationList
[
i
],
sender
,
args
,
exceptionFunc
);
}
}
private
static
async
Task
InternalInvokeAsync
<
T
>(
AsyncEventHandler
<
T
>
callback
,
object
sender
,
T
args
,
Action
<
Exception
>
exceptionFunc
)
{
try
{
...
...
Titanium.Web.Proxy/Extensions/SslExtensions.cs
0 → 100644
View file @
80dd44e5
using
System
;
using
System.Collections.Generic
;
using
System.Linq
;
using
System.Text
;
using
System.Threading.Tasks
;
using
StreamExtended
;
namespace
Titanium.Web.Proxy.Extensions
{
static
class
SslExtensions
{
public
static
string
GetServerName
(
this
ClientHelloInfo
clientHelloInfo
)
{
if
(
clientHelloInfo
.
Extensions
!=
null
&&
clientHelloInfo
.
Extensions
.
TryGetValue
(
"server_name"
,
out
var
serverNameExtension
))
{
return
serverNameExtension
.
Data
;
}
return
null
;
}
}
}
Titanium.Web.Proxy/Extensions/StreamExtensions.cs
View file @
80dd44e5
using
System
;
using
System.Globalization
;
using
System.IO
;
using
System.Threading
;
using
System.Threading.Tasks
;
using
StreamExtended.
Network
;
using
StreamExtended.
Helpers
;
namespace
Titanium.Web.Proxy.Extensions
{
...
...
@@ -18,83 +18,59 @@ namespace Titanium.Web.Proxy.Extensions
/// <param name="output"></param>
/// <param name="onCopy"></param>
/// <param name="bufferSize"></param>
internal
static
async
Task
CopyToAsync
(
this
Stream
input
,
Stream
output
,
Action
<
byte
[],
int
,
int
>
onCopy
,
int
bufferSize
)
internal
static
Task
CopyToAsync
(
this
Stream
input
,
Stream
output
,
Action
<
byte
[],
int
,
int
>
onCopy
,
int
bufferSize
)
{
byte
[]
buffer
=
new
byte
[
bufferSize
];
while
(
true
)
{
int
num
=
await
input
.
ReadAsync
(
buffer
,
0
,
buffer
.
Length
).
ConfigureAwait
(
false
);
int
bytesRead
;
if
((
bytesRead
=
num
)
!=
0
)
{
await
output
.
WriteAsync
(
buffer
,
0
,
bytesRead
).
ConfigureAwait
(
false
);
onCopy
?.
Invoke
(
buffer
,
0
,
bytesRead
);
}
else
{
break
;
}
}
return
CopyToAsync
(
input
,
output
,
onCopy
,
bufferSize
,
CancellationToken
.
None
);
}
/// <summary>
///
copies the specified bytes to the stream from the input stream
///
Copy streams asynchronously
/// </summary>
/// <param name="streamReader"></param>
/// <param name="stream"></param>
/// <param name="totalBytesToRead"></param>
/// <returns></returns>
internal
static
async
Task
CopyBytesToStream
(
this
CustomBinaryReader
streamReader
,
Stream
stream
,
long
totalBytesToRead
)
/// <param name="input"></param>
/// <param name="output"></param>
/// <param name="onCopy"></param>
/// <param name="bufferSize"></param>
/// <param name="cancellationToken"></param>
internal
static
async
Task
CopyToAsync
(
this
Stream
input
,
Stream
output
,
Action
<
byte
[],
int
,
int
>
onCopy
,
int
bufferSize
,
CancellationToken
cancellationToken
)
{
var
buffer
=
streamReader
.
Buffer
;
long
remainingBytes
=
totalBytesToRead
;
while
(
remainingBytes
>
0
)
byte
[]
buffer
=
BufferPool
.
GetBuffer
(
bufferSize
);
try
{
int
bytesToRead
=
buffer
.
Length
;
if
(
remainingBytes
<
bytesToRead
)
{
bytesToRead
=
(
int
)
remainingBytes
;
}
int
bytesRead
=
await
streamReader
.
ReadBytesAsync
(
buffer
,
bytesToRead
);
if
(
bytesRead
==
0
)
while
(!
cancellationToken
.
IsCancellationRequested
)
{
break
;
// cancellation is not working on Socket ReadAsync
// https://github.com/dotnet/corefx/issues/15033
int
num
=
await
input
.
ReadAsync
(
buffer
,
0
,
buffer
.
Length
,
CancellationToken
.
None
).
WithCancellation
(
cancellationToken
);
int
bytesRead
;
if
((
bytesRead
=
num
)
!=
0
&&
!
cancellationToken
.
IsCancellationRequested
)
{
await
output
.
WriteAsync
(
buffer
,
0
,
bytesRead
,
CancellationToken
.
None
);
onCopy
?.
Invoke
(
buffer
,
0
,
bytesRead
);
}
else
{
break
;
}
}
remainingBytes
-=
bytesRead
;
await
stream
.
WriteAsync
(
buffer
,
0
,
bytesRead
);
}
finally
{
BufferPool
.
ReturnBuffer
(
buffer
);
}
}
/// <summary>
/// Copies the stream chunked
/// </summary>
/// <param name="clientStreamReader"></param>
/// <param name="stream"></param>
/// <returns></returns>
internal
static
async
Task
CopyBytesToStreamChunked
(
this
CustomBinaryReader
clientStreamReader
,
Stream
stream
)
private
static
async
Task
<
T
>
WithCancellation
<
T
>(
this
Task
<
T
>
task
,
CancellationToken
cancellationToken
)
{
while
(
true
)
var
tcs
=
new
TaskCompletionSource
<
bool
>();
using
(
cancellationToken
.
Register
(
s
=>
((
TaskCompletionSource
<
bool
>)
s
).
TrySetResult
(
true
),
tcs
))
{
string
chuchkHead
=
await
clientStreamReader
.
ReadLineAsync
();
int
chunkSize
=
int
.
Parse
(
chuchkHead
,
NumberStyles
.
HexNumber
);
if
(
chunkSize
!=
0
)
{
await
CopyBytesToStream
(
clientStreamReader
,
stream
,
chunkSize
);
//chunk trail
await
clientStreamReader
.
ReadLineAsync
();
}
else
if
(
task
!=
await
Task
.
WhenAny
(
task
,
tcs
.
Task
))
{
await
clientStreamReader
.
ReadLineAsync
();
break
;
return
default
(
T
);
}
}
return
await
task
;
}
}
}
Titanium.Web.Proxy/Extensions/TcpExtensions.cs
View file @
80dd44e5
using
Titanium.Web.Proxy.Helpers
;
using
System
;
using
System.Net.Sockets
;
using
System.Reflection
;
using
Titanium.Web.Proxy.Helpers
;
namespace
Titanium.Web.Proxy.Extensions
{
internal
static
class
TcpExtensions
{
private
static
readonly
Func
<
Socket
,
bool
>
socketCleanedUpGetter
;
static
TcpExtensions
()
{
var
property
=
typeof
(
Socket
).
GetProperty
(
"CleanedUp"
,
BindingFlags
.
NonPublic
|
BindingFlags
.
Instance
);
if
(
property
!=
null
)
{
var
method
=
property
.
GetMethod
;
if
(
method
!=
null
&&
method
.
ReturnType
==
typeof
(
bool
))
{
socketCleanedUpGetter
=
(
Func
<
Socket
,
bool
>)
Delegate
.
CreateDelegate
(
typeof
(
Func
<
Socket
,
bool
>),
method
);
}
}
}
/// <summary>
/// Gets the local port from a native TCP row object.
...
...
@@ -24,5 +41,30 @@ namespace Titanium.Web.Proxy.Extensions
{
return
(
tcpRow
.
remotePort1
<<
8
)
+
tcpRow
.
remotePort2
+
(
tcpRow
.
remotePort3
<<
24
)
+
(
tcpRow
.
remotePort4
<<
16
);
}
internal
static
void
CloseSocket
(
this
TcpClient
tcpClient
)
{
if
(
tcpClient
==
null
)
{
return
;
}
try
{
//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
if
(
socketCleanedUpGetter
==
null
||
!
socketCleanedUpGetter
(
tcpClient
.
Client
))
{
tcpClient
.
LingerState
=
new
LingerOption
(
true
,
0
);
}
tcpClient
.
Close
();
}
catch
{
}
}
}
}
Titanium.Web.Proxy/Helpers/HttpHelper.cs
View file @
80dd44e5
using
System
;
using
System.Text
;
using
Titanium.Web.Proxy.Http
;
using
Titanium.Web.Proxy.Shared
;
namespace
Titanium.Web.Proxy.Helpers
...
...
@@ -28,7 +29,7 @@ namespace Titanium.Web.Proxy.Helpers
foreach
(
string
parameter
in
parameters
)
{
var
split
=
parameter
.
Split
(
ProxyConstants
.
EqualSplit
,
2
);
if
(
split
.
Length
==
2
&&
split
[
0
].
Trim
().
Equals
(
"charset"
,
StringComparison
.
CurrentCultureIgnoreCase
))
if
(
split
.
Length
==
2
&&
split
[
0
].
Trim
().
Equals
(
KnownHeaders
.
ContentTypeCharset
,
StringComparison
.
CurrentCultureIgnoreCase
))
{
string
value
=
split
[
1
];
if
(
value
.
Equals
(
"x-user-defined"
,
StringComparison
.
OrdinalIgnoreCase
))
...
...
@@ -65,7 +66,7 @@ namespace Titanium.Web.Proxy.Helpers
foreach
(
string
parameter
in
parameters
)
{
var
split
=
parameter
.
Split
(
ProxyConstants
.
EqualSplit
,
2
);
if
(
split
.
Length
==
2
&&
split
[
0
].
Trim
().
Equals
(
"boundary"
,
StringComparison
.
CurrentCultureIgnoreCase
))
if
(
split
.
Length
==
2
&&
split
[
0
].
Trim
().
Equals
(
KnownHeaders
.
ContentTypeBoundary
,
StringComparison
.
CurrentCultureIgnoreCase
))
{
string
value
=
split
[
1
];
if
(
value
.
Length
>
2
&&
value
[
0
]
==
'"'
&&
value
[
value
.
Length
-
1
]
==
'"'
)
...
...
@@ -97,6 +98,13 @@ namespace Titanium.Web.Proxy.Helpers
if
(
hostname
.
Split
(
ProxyConstants
.
DotSplit
).
Length
>
2
)
{
int
idx
=
hostname
.
IndexOf
(
ProxyConstants
.
DotSplit
);
//issue #352
if
(
hostname
.
Substring
(
0
,
idx
).
Contains
(
"-"
))
{
return
hostname
;
}
string
rootDomain
=
hostname
.
Substring
(
idx
+
1
);
return
"*."
+
rootDomain
;
}
...
...
Titanium.Web.Proxy/Helpers/HttpRequestWriter.cs
View file @
80dd44e5
...
...
@@ -4,8 +4,7 @@ namespace Titanium.Web.Proxy.Helpers
{
sealed
class
HttpRequestWriter
:
HttpWriter
{
public
HttpRequestWriter
(
Stream
stream
,
int
bufferSize
)
:
base
(
stream
,
bufferSize
,
true
)
public
HttpRequestWriter
(
Stream
stream
,
int
bufferSize
)
:
base
(
stream
,
bufferSize
)
{
}
}
...
...
Titanium.Web.Proxy/Helpers/HttpResponseWriter.cs
View file @
80dd44e5
using
System
;
using
System.Globalization
;
using
System.IO
;
using
System.Text
;
using
System.Threading.Tasks
;
using
StreamExtended.Network
;
using
Titanium.Web.Proxy.Extensions
;
using
Titanium.Web.Proxy.Http
;
using
Titanium.Web.Proxy.Shared
;
namespace
Titanium.Web.Proxy.Helpers
{
sealed
class
HttpResponseWriter
:
HttpWriter
{
public
HttpResponseWriter
(
Stream
stream
,
int
bufferSize
)
:
base
(
stream
,
bufferSize
,
true
)
public
HttpResponseWriter
(
Stream
stream
,
int
bufferSize
)
:
base
(
stream
,
bufferSize
)
{
}
...
...
@@ -39,102 +33,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <returns></returns>
public
Task
WriteResponseStatusAsync
(
Version
version
,
int
code
,
string
description
)
{
return
WriteLineAsync
(
$"HTTP/
{
version
.
Major
}
.
{
version
.
Minor
}
{
code
}
{
description
}
"
);
}
/// <summary>
/// Writes the byte array body to the stream; optionally chunked
/// </summary>
/// <param name="data"></param>
/// <param name="isChunked"></param>
/// <returns></returns>
internal
async
Task
WriteResponseBodyAsync
(
byte
[]
data
,
bool
isChunked
)
{
if
(!
isChunked
)
{
await
BaseStream
.
WriteAsync
(
data
,
0
,
data
.
Length
);
}
else
{
await
WriteResponseBodyChunkedAsync
(
data
);
}
}
/// <summary>
/// Copies the specified content length number of bytes to the output stream from the given inputs stream
/// optionally chunked
/// </summary>
/// <param name="bufferSize"></param>
/// <param name="inStreamReader"></param>
/// <param name="isChunked"></param>
/// <param name="contentLength"></param>
/// <returns></returns>
internal
async
Task
WriteResponseBodyAsync
(
int
bufferSize
,
CustomBinaryReader
inStreamReader
,
bool
isChunked
,
long
contentLength
)
{
if
(!
isChunked
)
{
//http 1.0
if
(
contentLength
==
-
1
)
{
contentLength
=
long
.
MaxValue
;
}
await
inStreamReader
.
CopyBytesToStream
(
BaseStream
,
contentLength
);
}
else
{
await
WriteResponseBodyChunkedAsync
(
inStreamReader
);
}
}
/// <summary>
/// Copies the streams chunked
/// </summary>
/// <param name="inStreamReader"></param>
/// <returns></returns>
internal
async
Task
WriteResponseBodyChunkedAsync
(
CustomBinaryReader
inStreamReader
)
{
while
(
true
)
{
string
chunkHead
=
await
inStreamReader
.
ReadLineAsync
();
int
chunkSize
=
int
.
Parse
(
chunkHead
,
NumberStyles
.
HexNumber
);
if
(
chunkSize
!=
0
)
{
var
chunkHeadBytes
=
Encoding
.
ASCII
.
GetBytes
(
chunkSize
.
ToString
(
"x2"
));
await
BaseStream
.
WriteAsync
(
chunkHeadBytes
,
0
,
chunkHeadBytes
.
Length
);
await
BaseStream
.
WriteAsync
(
ProxyConstants
.
NewLineBytes
,
0
,
ProxyConstants
.
NewLineBytes
.
Length
);
await
inStreamReader
.
CopyBytesToStream
(
BaseStream
,
chunkSize
);
await
BaseStream
.
WriteAsync
(
ProxyConstants
.
NewLineBytes
,
0
,
ProxyConstants
.
NewLineBytes
.
Length
);
await
inStreamReader
.
ReadLineAsync
();
}
else
{
await
inStreamReader
.
ReadLineAsync
();
await
BaseStream
.
WriteAsync
(
ProxyConstants
.
ChunkEnd
,
0
,
ProxyConstants
.
ChunkEnd
.
Length
);
break
;
}
}
}
/// <summary>
/// Copies the given input bytes to output stream chunked
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
internal
async
Task
WriteResponseBodyChunkedAsync
(
byte
[]
data
)
{
var
chunkHead
=
Encoding
.
ASCII
.
GetBytes
(
data
.
Length
.
ToString
(
"x2"
));
await
BaseStream
.
WriteAsync
(
chunkHead
,
0
,
chunkHead
.
Length
);
await
BaseStream
.
WriteAsync
(
ProxyConstants
.
NewLineBytes
,
0
,
ProxyConstants
.
NewLineBytes
.
Length
);
await
BaseStream
.
WriteAsync
(
data
,
0
,
data
.
Length
);
await
BaseStream
.
WriteAsync
(
ProxyConstants
.
NewLineBytes
,
0
,
ProxyConstants
.
NewLineBytes
.
Length
);
await
BaseStream
.
WriteAsync
(
ProxyConstants
.
ChunkEnd
,
0
,
ProxyConstants
.
ChunkEnd
.
Length
);
return
WriteLineAsync
(
Response
.
CreateResponseLine
(
version
,
code
,
description
));
}
}
}
Titanium.Web.Proxy/Helpers/HttpWriter.cs
View file @
80dd44e5
using
System.IO
;
using
System
;
using
System.Globalization
;
using
System.IO
;
using
System.Text
;
using
System.Threading.Tasks
;
using
StreamExtended.Helpers
;
using
StreamExtended.Network
;
using
Titanium.Web.Proxy.Http
;
using
Titanium.Web.Proxy.Shared
;
namespace
Titanium.Web.Proxy.Helpers
{
abstract
class
HttpWriter
:
Stream
Writer
internal
class
HttpWriter
:
CustomBinary
Writer
{
protected
HttpWriter
(
Stream
stream
,
int
bufferSize
,
bool
leaveOpen
)
:
base
(
stream
,
Encoding
.
ASCII
,
bufferSize
,
leaveOpen
)
public
int
BufferSize
{
get
;
}
private
readonly
char
[]
charBuffer
;
private
static
readonly
byte
[]
newLine
=
ProxyConstants
.
NewLine
;
private
static
readonly
Encoder
encoder
=
Encoding
.
ASCII
.
GetEncoder
();
internal
HttpWriter
(
Stream
stream
,
int
bufferSize
)
:
base
(
stream
)
{
NewLine
=
ProxyConstants
.
NewLine
;
BufferSize
=
bufferSize
;
// ASCII encoder max byte count is char count + 1
charBuffer
=
new
char
[
BufferSize
-
1
];
}
public
void
WriteHeaders
(
HeaderCollection
headers
,
bool
flush
=
true
)
public
Task
WriteLineAsync
(
)
{
if
(
headers
!=
null
)
return
WriteAsync
(
newLine
);
}
public
async
Task
WriteAsync
(
string
value
)
{
int
charCount
=
value
.
Length
;
value
.
CopyTo
(
0
,
charBuffer
,
0
,
charCount
);
if
(
charCount
<
BufferSize
)
{
foreach
(
var
header
in
headers
)
var
buffer
=
BufferPool
.
GetBuffer
(
BufferSize
);
try
{
header
.
WriteToStream
(
this
);
int
idx
=
encoder
.
GetBytes
(
charBuffer
,
0
,
charCount
,
buffer
,
0
,
true
);
await
WriteAsync
(
buffer
,
0
,
idx
);
}
finally
{
BufferPool
.
ReturnBuffer
(
buffer
);
}
}
WriteLine
();
if
(
flush
)
else
{
Flush
();
var
buffer
=
new
byte
[
charCount
+
1
];
int
idx
=
encoder
.
GetBytes
(
charBuffer
,
0
,
charCount
,
buffer
,
0
,
true
);
await
WriteAsync
(
buffer
,
0
,
idx
);
}
}
public
async
Task
WriteLineAsync
(
string
value
)
{
await
WriteAsync
(
value
);
await
WriteLineAsync
();
}
/// <summary>
/// Write the headers to client
/// </summary>
...
...
@@ -50,8 +84,161 @@ namespace Titanium.Web.Proxy.Helpers
await
WriteLineAsync
();
if
(
flush
)
{
// flush the stream
await
FlushAsync
();
}
}
public
async
Task
WriteAsync
(
byte
[]
data
,
bool
flush
=
false
)
{
await
WriteAsync
(
data
,
0
,
data
.
Length
);
if
(
flush
)
{
// flush the stream
await
FlushAsync
();
}
}
public
async
Task
WriteAsync
(
byte
[]
data
,
int
offset
,
int
count
,
bool
flush
)
{
await
WriteAsync
(
data
,
offset
,
count
);
if
(
flush
)
{
// flush the stream
await
FlushAsync
();
}
}
/// <summary>
/// Writes the byte array body to the stream; optionally chunked
/// </summary>
/// <param name="data"></param>
/// <param name="isChunked"></param>
/// <returns></returns>
internal
Task
WriteBodyAsync
(
byte
[]
data
,
bool
isChunked
)
{
if
(
isChunked
)
{
return
WriteBodyChunkedAsync
(
data
);
}
return
WriteAsync
(
data
);
}
/// <summary>
/// Copies the specified content length number of bytes to the output stream from the given inputs stream
/// optionally chunked
/// </summary>
/// <param name="streamReader"></param>
/// <param name="isChunked"></param>
/// <param name="contentLength"></param>
/// <param name="removeChunkedEncoding"></param>
/// <returns></returns>
internal
Task
CopyBodyAsync
(
CustomBinaryReader
streamReader
,
bool
isChunked
,
long
contentLength
,
bool
removeChunkedEncoding
)
{
//For chunked request we need to read data as they arrive, until we reach a chunk end symbol
if
(
isChunked
)
{
//Need to revist, find any potential bugs
//send the body bytes to server in chunks
return
CopyBodyChunkedAsync
(
streamReader
,
removeChunkedEncoding
);
}
//http 1.0
if
(
contentLength
==
-
1
)
{
contentLength
=
long
.
MaxValue
;
}
//If not chunked then its easy just read the amount of bytes mentioned in content length header
return
CopyBytesFromStream
(
streamReader
,
contentLength
);
}
/// <summary>
/// Copies the given input bytes to output stream chunked
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
private
async
Task
WriteBodyChunkedAsync
(
byte
[]
data
)
{
var
chunkHead
=
Encoding
.
ASCII
.
GetBytes
(
data
.
Length
.
ToString
(
"x2"
));
await
WriteAsync
(
chunkHead
);
await
WriteLineAsync
();
await
WriteAsync
(
data
);
await
WriteLineAsync
();
await
WriteLineAsync
(
"0"
);
await
WriteLineAsync
();
}
/// <summary>
/// Copies the streams chunked
/// </summary>
/// <param name="reader"></param>
/// <param name="removeChunkedEncoding"></param>
/// <returns></returns>
private
async
Task
CopyBodyChunkedAsync
(
CustomBinaryReader
reader
,
bool
removeChunkedEncoding
)
{
while
(
true
)
{
string
chunkHead
=
await
reader
.
ReadLineAsync
();
int
chunkSize
=
int
.
Parse
(
chunkHead
,
NumberStyles
.
HexNumber
);
if
(!
removeChunkedEncoding
)
{
await
WriteLineAsync
(
chunkHead
);
}
if
(
chunkSize
!=
0
)
{
await
CopyBytesFromStream
(
reader
,
chunkSize
);
}
if
(!
removeChunkedEncoding
)
{
await
WriteLineAsync
();
}
//chunk trail
await
reader
.
ReadLineAsync
();
if
(
chunkSize
==
0
)
{
break
;
}
}
}
/// <summary>
/// Copies the specified bytes to the stream from the input stream
/// </summary>
/// <param name="reader"></param>
/// <param name="count"></param>
/// <returns></returns>
private
async
Task
CopyBytesFromStream
(
CustomBinaryReader
reader
,
long
count
)
{
var
buffer
=
reader
.
Buffer
;
long
remainingBytes
=
count
;
while
(
remainingBytes
>
0
)
{
int
bytesToRead
=
buffer
.
Length
;
if
(
remainingBytes
<
bytesToRead
)
{
bytesToRead
=
(
int
)
remainingBytes
;
}
int
bytesRead
=
await
reader
.
ReadBytesAsync
(
buffer
,
bytesToRead
);
if
(
bytesRead
==
0
)
{
break
;
}
remainingBytes
-=
bytesRead
;
await
WriteAsync
(
buffer
,
0
,
bytesRead
);
}
}
}
}
Titanium.Web.Proxy/Helpers/Tcp.cs
View file @
80dd44e5
...
...
@@ -2,6 +2,7 @@ using System;
using
System.Collections.Generic
;
using
System.IO
;
using
System.Runtime.InteropServices
;
using
System.Threading
;
using
System.Threading.Tasks
;
using
Titanium.Web.Proxy.Extensions
;
using
Titanium.Web.Proxy.Network.Tcp
;
...
...
@@ -111,16 +112,21 @@ namespace Titanium.Web.Proxy.Helpers
/// </summary>
/// <param name="clientStream"></param>
/// <param name="serverStream"></param>
/// <param name="bufferSize"></param>
/// <param name="onDataSend"></param>
/// <param name="onDataReceive"></param>
/// <returns></returns>
internal
static
async
Task
SendRaw
(
Stream
clientStream
,
Stream
serverStream
,
int
bufferSize
,
Action
<
byte
[],
int
,
int
>
onDataSend
,
Action
<
byte
[],
int
,
int
>
onDataReceive
)
{
var
cts
=
new
CancellationTokenSource
();
//Now async relay all server=>client & client=>server data
var
sendRelay
=
clientStream
.
CopyToAsync
(
serverStream
,
onDataSend
,
bufferSize
);
var
sendRelay
=
clientStream
.
CopyToAsync
(
serverStream
,
onDataSend
,
bufferSize
,
cts
.
Token
);
var
receiveRelay
=
serverStream
.
CopyToAsync
(
clientStream
,
onDataReceive
,
bufferSize
,
cts
.
Token
);
var
receiveRelay
=
serverStream
.
CopyToAsync
(
clientStream
,
onDataReceive
,
bufferSize
);
await
Task
.
WhenAny
(
sendRelay
,
receiveRelay
);
cts
.
Cancel
();
await
Task
.
WhenAll
(
sendRelay
,
receiveRelay
);
}
...
...
Titanium.Web.Proxy/Http/ConnectResponse.cs
View file @
80dd44e5
...
...
@@ -22,7 +22,7 @@ namespace Titanium.Web.Proxy.Http
StatusDescription
=
"Connection Established"
};
response
.
Headers
.
AddHeader
(
"Timestamp"
,
DateTime
.
Now
.
ToString
());
response
.
Headers
.
AddHeader
(
KnownHeaders
.
Timestamp
,
DateTime
.
Now
.
ToString
());
return
response
;
}
}
...
...
Titanium.Web.Proxy/Http/HeaderCollection.cs
View file @
80dd44e5
...
...
@@ -63,6 +63,21 @@ namespace Titanium.Web.Proxy.Http
return
null
;
}
public
HttpHeader
GetFirstHeader
(
string
name
)
{
if
(
Headers
.
TryGetValue
(
name
,
out
var
header
))
{
return
header
;
}
if
(
NonUniqueHeaders
.
TryGetValue
(
name
,
out
var
headers
))
{
return
headers
.
FirstOrDefault
();
}
return
null
;
}
/// <summary>
/// Returns all headers
/// </summary>
...
...
@@ -235,7 +250,7 @@ namespace Titanium.Web.Proxy.Http
return
null
;
}
internal
string
SetOrAddHeaderValue
(
string
headerName
,
string
value
)
internal
void
SetOrAddHeaderValue
(
string
headerName
,
string
value
)
{
if
(
Headers
.
TryGetValue
(
headerName
,
out
var
header
))
{
...
...
@@ -245,8 +260,6 @@ namespace Titanium.Web.Proxy.Http
{
Headers
.
Add
(
headerName
,
new
HttpHeader
(
headerName
,
value
));
}
return
null
;
}
/// <summary>
...
...
@@ -255,12 +268,12 @@ namespace Titanium.Web.Proxy.Http
internal
void
FixProxyHeaders
()
{
//If proxy-connection close was returned inform to close the connection
string
proxyHeader
=
GetHeaderValueOrNull
(
"proxy-connection"
);
RemoveHeader
(
"proxy-connection"
);
string
proxyHeader
=
GetHeaderValueOrNull
(
KnownHeaders
.
ProxyConnection
);
RemoveHeader
(
KnownHeaders
.
ProxyConnection
);
if
(
proxyHeader
!=
null
)
{
SetOrAddHeaderValue
(
"connection"
,
proxyHeader
);
SetOrAddHeaderValue
(
KnownHeaders
.
Connection
,
proxyHeader
);
}
}
...
...
Titanium.Web.Proxy/Http/HttpWebClient.cs
View file @
80dd44e5
...
...
@@ -2,7 +2,6 @@ using System;
using
System.IO
;
using
System.Net
;
using
System.Threading.Tasks
;
using
Titanium.Web.Proxy.Helpers
;
using
Titanium.Web.Proxy.Models
;
using
Titanium.Web.Proxy.Network.Tcp
;
...
...
@@ -74,61 +73,45 @@ namespace Titanium.Web.Proxy.Http
connection
.
LastAccess
=
DateTime
.
Now
;
ServerConnection
=
connection
;
}
/// <summary>
/// Prepare and send the http(s) request
/// </summary>
/// <returns></returns>
internal
async
Task
SendRequest
(
bool
enable100ContinueBehaviour
)
{
var
stream
=
ServerConnection
.
Stream
;
var
upstreamProxy
=
ServerConnection
.
UpStreamProxy
;
byte
[]
requestBytes
;
using
(
var
ms
=
new
MemoryStream
())
using
(
var
writer
=
new
HttpRequestWriter
(
ms
,
bufferSize
))
{
var
upstreamProxy
=
ServerConnection
.
UpStreamProxy
;
bool
useUpstreamProxy
=
upstreamProxy
!=
null
&&
ServerConnection
.
IsHttps
==
false
;
bool
useUpstreamProxy
=
upstreamProxy
!=
null
&&
ServerConnection
.
IsHttps
==
false
;
var
writer
=
ServerConnection
.
StreamWriter
;
//prepare the request & headers
if
(
useUpstreamProxy
)
{
writer
.
WriteLine
(
$"
{
Request
.
Method
}
{
Request
.
OriginalUrl
}
HTTP/
{
Request
.
HttpVersion
.
Major
}
.
{
Request
.
HttpVersion
.
Minor
}
"
);
}
else
{
writer
.
WriteLine
(
$"
{
Request
.
Method
}
{
Request
.
RequestUri
.
PathAndQuery
}
HTTP/
{
Request
.
HttpVersion
.
Major
}
.
{
Request
.
HttpVersion
.
Minor
}
"
);
}
//prepare the request & headers
await
writer
.
WriteLineAsync
(
Request
.
CreateRequestLine
(
Request
.
Method
,
useUpstreamProxy
?
Request
.
OriginalUrl
:
Request
.
RequestUri
.
PathAndQuery
,
Request
.
HttpVersion
));
//Send Authentication to Upstream proxy if needed
if
(
upstreamProxy
!=
null
&&
ServerConnection
.
IsHttps
==
false
&&
!
string
.
IsNullOrEmpty
(
upstreamProxy
.
UserName
)
&&
upstreamProxy
.
Password
!=
null
)
{
HttpHeader
.
ProxyConnectionKeepAlive
.
WriteToStream
(
writer
);
HttpHeader
.
GetProxyAuthorizationHeader
(
upstreamProxy
.
UserName
,
upstreamProxy
.
Password
).
WriteToStream
(
writer
);
}
//Send Authentication to Upstream proxy if needed
if
(
upstreamProxy
!=
null
&&
ServerConnection
.
IsHttps
==
false
&&
!
string
.
IsNullOrEmpty
(
upstreamProxy
.
UserName
)
&&
upstreamProxy
.
Password
!=
null
)
{
await
HttpHeader
.
ProxyConnectionKeepAlive
.
WriteToStreamAsync
(
writer
);
await
HttpHeader
.
GetProxyAuthorizationHeader
(
upstreamProxy
.
UserName
,
upstreamProxy
.
Password
).
WriteToStreamAsync
(
writer
);
}
//write request headers
foreach
(
var
header
in
Request
.
Headers
)
//write request headers
foreach
(
var
header
in
Request
.
Headers
)
{
if
(
header
.
Name
!=
KnownHeaders
.
ProxyAuthorization
)
{
if
(
header
.
Name
!=
"Proxy-Authorization"
)
{
header
.
WriteToStream
(
writer
);
}
await
header
.
WriteToStreamAsync
(
writer
);
}
writer
.
WriteLine
();
writer
.
Flush
();
requestBytes
=
ms
.
ToArray
();
}
await
stream
.
WriteAsync
(
requestBytes
,
0
,
requestBytes
.
Length
);
await
stream
.
FlushAsync
();
await
writer
.
WriteLineAsync
();
if
(
enable100ContinueBehaviour
)
{
...
...
@@ -191,6 +174,7 @@ namespace Titanium.Web.Proxy.Http
Response
.
Is100Continue
=
true
;
Response
.
StatusCode
=
0
;
await
ServerConnection
.
StreamReader
.
ReadLineAsync
();
//now receive response
await
ReceiveResponse
();
return
;
...
...
@@ -203,6 +187,7 @@ namespace Titanium.Web.Proxy.Http
Response
.
ExpectationFailed
=
true
;
Response
.
StatusCode
=
0
;
await
ServerConnection
.
StreamReader
.
ReadLineAsync
();
//now receive response
await
ReceiveResponse
();
return
;
...
...
Titanium.Web.Proxy/Http/KnownHeaders.cs
0 → 100644
View file @
80dd44e5
using
System
;
using
System.Collections.Generic
;
using
System.Linq
;
using
System.Text
;
using
System.Threading.Tasks
;
namespace
Titanium.Web.Proxy.Http
{
public
static
class
KnownHeaders
{
// Both
public
const
string
Connection
=
"connection"
;
public
const
string
ConnectionClose
=
"close"
;
public
const
string
ConnectionKeepAlive
=
"keep-alive"
;
public
const
string
ContentLength
=
"content-length"
;
public
const
string
ContentType
=
"content-type"
;
public
const
string
ContentTypeCharset
=
"charset"
;
public
const
string
ContentTypeBoundary
=
"boundary"
;
public
const
string
Upgrade
=
"upgrade"
;
public
const
string
UpgradeWebsocket
=
"websocket"
;
// Request headers
public
const
string
AcceptEncoding
=
"accept-encoding"
;
public
const
string
Authorization
=
"Authorization"
;
public
const
string
Expect
=
"expect"
;
public
const
string
Expect100Continue
=
"100-continue"
;
public
const
string
Host
=
"host"
;
public
const
string
ProxyAuthorization
=
"Proxy-Authorization"
;
public
const
string
ProxyConnection
=
"Proxy-Connection"
;
public
const
string
ProxyConnectionClose
=
"close"
;
// Response headers
public
const
string
ContentEncoding
=
"content-encoding"
;
public
const
string
Location
=
"Location"
;
public
const
string
ProxyAuthenticate
=
"Proxy-Authenticate"
;
public
const
string
TransferEncoding
=
"transfer-encoding"
;
public
const
string
TransferEncodingChunked
=
"chunked"
;
// ???
public
const
string
Timestamp
=
"Timestamp"
;
}
}
Titanium.Web.Proxy/Http/Request.cs
View file @
80dd44e5
...
...
@@ -67,14 +67,14 @@ namespace Titanium.Web.Proxy.Http
/// </summary>
public
string
Host
{
get
=>
Headers
.
GetHeaderValueOrNull
(
"host"
);
set
=>
Headers
.
SetOrAddHeaderValue
(
"host"
,
value
);
get
=>
Headers
.
GetHeaderValueOrNull
(
KnownHeaders
.
Host
);
set
=>
Headers
.
SetOrAddHeaderValue
(
KnownHeaders
.
Host
,
value
);
}
/// <summary>
/// Content encoding header value
/// </summary>
public
string
ContentEncoding
=>
Headers
.
GetHeaderValueOrNull
(
"content-encoding"
);
public
string
ContentEncoding
=>
Headers
.
GetHeaderValueOrNull
(
KnownHeaders
.
ContentEncoding
);
/// <summary>
/// Request content-length
...
...
@@ -83,7 +83,7 @@ namespace Titanium.Web.Proxy.Http
{
get
{
string
headerValue
=
Headers
.
GetHeaderValueOrNull
(
"content-length"
);
string
headerValue
=
Headers
.
GetHeaderValueOrNull
(
KnownHeaders
.
ContentLength
);
if
(
headerValue
==
null
)
{
...
...
@@ -102,12 +102,12 @@ namespace Titanium.Web.Proxy.Http
{
if
(
value
>=
0
)
{
Headers
.
SetOrAddHeaderValue
(
"content-length"
,
value
.
ToString
());
Headers
.
SetOrAddHeaderValue
(
KnownHeaders
.
ContentLength
,
value
.
ToString
());
IsChunked
=
false
;
}
else
{
Headers
.
RemoveHeader
(
"content-length"
);
Headers
.
RemoveHeader
(
KnownHeaders
.
ContentLength
);
}
}
}
...
...
@@ -117,8 +117,8 @@ namespace Titanium.Web.Proxy.Http
/// </summary>
public
string
ContentType
{
get
=>
Headers
.
GetHeaderValueOrNull
(
"content-type"
);
set
=>
Headers
.
SetOrAddHeaderValue
(
"content-type"
,
value
);
get
=>
Headers
.
GetHeaderValueOrNull
(
KnownHeaders
.
ContentType
);
set
=>
Headers
.
SetOrAddHeaderValue
(
KnownHeaders
.
ContentType
,
value
);
}
/// <summary>
...
...
@@ -128,19 +128,19 @@ namespace Titanium.Web.Proxy.Http
{
get
{
string
headerValue
=
Headers
.
GetHeaderValueOrNull
(
"transfer-encoding"
);
return
headerValue
!=
null
&&
headerValue
.
ContainsIgnoreCase
(
"chunked"
);
string
headerValue
=
Headers
.
GetHeaderValueOrNull
(
KnownHeaders
.
TransferEncoding
);
return
headerValue
!=
null
&&
headerValue
.
ContainsIgnoreCase
(
KnownHeaders
.
TransferEncodingChunked
);
}
set
{
if
(
value
)
{
Headers
.
SetOrAddHeaderValue
(
"transfer-encoding"
,
"chunked"
);
Headers
.
SetOrAddHeaderValue
(
KnownHeaders
.
TransferEncoding
,
KnownHeaders
.
TransferEncodingChunked
);
ContentLength
=
-
1
;
}
else
{
Headers
.
RemoveHeader
(
"transfer-encoding"
);
Headers
.
RemoveHeader
(
KnownHeaders
.
TransferEncoding
);
}
}
}
...
...
@@ -152,8 +152,8 @@ namespace Titanium.Web.Proxy.Http
{
get
{
string
headerValue
=
Headers
.
GetHeaderValueOrNull
(
"expect"
);
return
headerValue
!=
null
&&
headerValue
.
Equals
(
"100-continue"
);
string
headerValue
=
Headers
.
GetHeaderValueOrNull
(
KnownHeaders
.
Expect
);
return
headerValue
!=
null
&&
headerValue
.
Equals
(
KnownHeaders
.
Expect100Continue
);
}
}
...
...
@@ -241,14 +241,14 @@ namespace Titanium.Web.Proxy.Http
{
get
{
string
headerValue
=
Headers
.
GetHeaderValueOrNull
(
"upgrade"
);
string
headerValue
=
Headers
.
GetHeaderValueOrNull
(
KnownHeaders
.
Upgrade
);
if
(
headerValue
==
null
)
{
return
false
;
}
return
headerValue
.
Equals
(
"websocket"
,
StringComparison
.
CurrentCultureIgnoreCase
);
return
headerValue
.
Equals
(
KnownHeaders
.
UpgradeWebsocket
,
StringComparison
.
CurrentCultureIgnoreCase
);
}
}
...
...
@@ -275,7 +275,7 @@ namespace Titanium.Web.Proxy.Http
get
{
var
sb
=
new
StringBuilder
();
sb
.
AppendLine
(
$"
{
Method
}
{
OriginalUrl
}
HTTP/
{
HttpVersion
.
Major
}
.
{
HttpVersion
.
Minor
}
"
);
sb
.
AppendLine
(
CreateRequestLine
(
Method
,
OriginalUrl
,
HttpVersion
)
);
foreach
(
var
header
in
Headers
)
{
sb
.
AppendLine
(
header
.
ToString
());
...
...
@@ -286,6 +286,11 @@ namespace Titanium.Web.Proxy.Http
}
}
internal
static
string
CreateRequestLine
(
string
httpMethod
,
string
httpUrl
,
Version
version
)
{
return
$"
{
httpMethod
}
{
httpUrl
}
HTTP/
{
version
.
Major
}
.
{
version
.
Minor
}
"
;
}
internal
static
void
ParseRequestLine
(
string
httpCmd
,
out
string
httpMethod
,
out
string
httpUrl
,
out
Version
version
)
{
//break up the line into three components (method, remote URL & Http Version)
...
...
Titanium.Web.Proxy/Http/Response.cs
View file @
80dd44e5
...
...
@@ -42,12 +42,12 @@ namespace Titanium.Web.Proxy.Http
/// <summary>
/// Content encoding for this response
/// </summary>
public
string
ContentEncoding
=>
Headers
.
GetHeaderValueOrNull
(
"content-encoding"
)?.
Trim
();
public
string
ContentEncoding
=>
Headers
.
GetHeaderValueOrNull
(
KnownHeaders
.
ContentEncoding
)?.
Trim
();
/// <summary>
/// Http version
/// </summary>
public
Version
HttpVersion
{
get
;
set
;
}
public
Version
HttpVersion
{
get
;
set
;
}
=
HttpHeader
.
VersionUnknown
;
/// <summary>
/// Keeps the response body data after the session is finished
...
...
@@ -61,16 +61,24 @@ namespace Titanium.Web.Proxy.Http
{
get
{
long
contentLength
=
ContentLength
;
//If content length is set to 0 the response has no body
if
(
contentLength
==
0
)
{
return
false
;
}
//Has body only if response is chunked or content length >0
//If none are true then check if connection:close header exist, if so write response until server or client terminates the connection
if
(
IsChunked
||
C
ontentLength
>
0
||
!
KeepAlive
)
if
(
IsChunked
||
c
ontentLength
>
0
||
!
KeepAlive
)
{
return
true
;
}
//has response if connection:keep-alive header exist and when version is http/1.0
//Because in Http 1.0 server can return a response without content-length (expectation being client would read until end of stream)
if
(
KeepAlive
&&
HttpVersion
.
Minor
==
0
)
if
(
KeepAlive
&&
HttpVersion
==
HttpHeader
.
Version1
0
)
{
return
true
;
}
...
...
@@ -86,11 +94,11 @@ namespace Titanium.Web.Proxy.Http
{
get
{
string
headerValue
=
Headers
.
GetHeaderValueOrNull
(
"connection"
);
string
headerValue
=
Headers
.
GetHeaderValueOrNull
(
KnownHeaders
.
Connection
);
if
(
headerValue
!=
null
)
{
if
(
headerValue
.
ContainsIgnoreCase
(
"close"
))
if
(
headerValue
.
ContainsIgnoreCase
(
KnownHeaders
.
ConnectionClose
))
{
return
false
;
}
...
...
@@ -103,7 +111,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary>
/// Content type of this response
/// </summary>
public
string
ContentType
=>
Headers
.
GetHeaderValueOrNull
(
"content-type"
);
public
string
ContentType
=>
Headers
.
GetHeaderValueOrNull
(
KnownHeaders
.
ContentType
);
/// <summary>
/// Length of response body
...
...
@@ -112,15 +120,14 @@ namespace Titanium.Web.Proxy.Http
{
get
{
string
headerValue
=
Headers
.
GetHeaderValueOrNull
(
"content-length"
);
string
headerValue
=
Headers
.
GetHeaderValueOrNull
(
KnownHeaders
.
ContentLength
);
if
(
headerValue
==
null
)
{
return
-
1
;
}
long
.
TryParse
(
headerValue
,
out
long
contentLen
);
if
(
contentLen
>=
0
)
if
(
long
.
TryParse
(
headerValue
,
out
long
contentLen
))
{
return
contentLen
;
}
...
...
@@ -131,12 +138,12 @@ namespace Titanium.Web.Proxy.Http
{
if
(
value
>=
0
)
{
Headers
.
SetOrAddHeaderValue
(
"content-length"
,
value
.
ToString
());
Headers
.
SetOrAddHeaderValue
(
KnownHeaders
.
ContentLength
,
value
.
ToString
());
IsChunked
=
false
;
}
else
{
Headers
.
RemoveHeader
(
"content-length"
);
Headers
.
RemoveHeader
(
KnownHeaders
.
ContentLength
);
}
}
}
...
...
@@ -148,19 +155,19 @@ namespace Titanium.Web.Proxy.Http
{
get
{
string
headerValue
=
Headers
.
GetHeaderValueOrNull
(
"transfer-encoding"
);
return
headerValue
!=
null
&&
headerValue
.
ContainsIgnoreCase
(
"chunked"
);
string
headerValue
=
Headers
.
GetHeaderValueOrNull
(
KnownHeaders
.
TransferEncoding
);
return
headerValue
!=
null
&&
headerValue
.
ContainsIgnoreCase
(
KnownHeaders
.
TransferEncodingChunked
);
}
set
{
if
(
value
)
{
Headers
.
SetOrAddHeaderValue
(
"transfer-encoding"
,
"chunked"
);
Headers
.
SetOrAddHeaderValue
(
KnownHeaders
.
TransferEncoding
,
KnownHeaders
.
TransferEncodingChunked
);
ContentLength
=
-
1
;
}
else
{
Headers
.
RemoveHeader
(
"transfer-encoding"
);
Headers
.
RemoveHeader
(
KnownHeaders
.
TransferEncoding
);
}
}
}
...
...
@@ -228,7 +235,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary>
/// Gets the resposne status.
/// </summary>
public
string
Status
=>
$"HTTP/
{
HttpVersion
?.
Major
}
.
{
HttpVersion
?.
Minor
}
{
StatusCode
}
{
StatusDescription
}
"
;
public
string
Status
=>
CreateResponseLine
(
HttpVersion
,
StatusCode
,
StatusDescription
)
;
/// <summary>
/// Gets the header text.
...
...
@@ -249,6 +256,11 @@ namespace Titanium.Web.Proxy.Http
}
}
internal
static
string
CreateResponseLine
(
Version
version
,
int
statusCode
,
string
statusDescription
)
{
return
$"HTTP/
{
version
.
Major
}
.
{
version
.
Minor
}
{
statusCode
}
{
statusDescription
}
"
;
}
internal
static
void
ParseResponseLine
(
string
httpStatus
,
out
Version
version
,
out
int
statusCode
,
out
string
statusDescription
)
{
var
httpResult
=
httpStatus
.
Split
(
ProxyConstants
.
SpaceSplit
,
3
);
...
...
Titanium.Web.Proxy/Models/HttpHeader.cs
View file @
80dd44e5
using
System
;
using
System.IO
;
using
System.Text
;
using
System.Threading.Tasks
;
using
Titanium.Web.Proxy.Helpers
;
using
Titanium.Web.Proxy.Http
;
namespace
Titanium.Web.Proxy.Models
{
...
...
@@ -10,6 +11,8 @@ namespace Titanium.Web.Proxy.Models
/// </summary>
public
class
HttpHeader
{
internal
static
readonly
Version
VersionUnknown
=
new
Version
(
0
,
0
);
internal
static
readonly
Version
Version10
=
new
Version
(
1
,
0
);
internal
static
readonly
Version
Version11
=
new
Version
(
1
,
1
);
...
...
@@ -54,19 +57,12 @@ namespace Titanium.Web.Proxy.Models
internal
static
HttpHeader
GetProxyAuthorizationHeader
(
string
userName
,
string
password
)
{
var
result
=
new
HttpHeader
(
"Proxy-Authorization"
,
var
result
=
new
HttpHeader
(
KnownHeaders
.
ProxyAuthorization
,
"Basic "
+
Convert
.
ToBase64String
(
Encoding
.
UTF8
.
GetBytes
(
$"
{
userName
}
:
{
password
}
"
)));
return
result
;
}
internal
void
WriteToStream
(
StreamWriter
writer
)
{
writer
.
Write
(
Name
);
writer
.
Write
(
": "
);
writer
.
WriteLine
(
Value
);
}
internal
async
Task
WriteToStreamAsync
(
StreamWriter
writer
)
internal
async
Task
WriteToStreamAsync
(
HttpWriter
writer
)
{
await
writer
.
WriteAsync
(
Name
);
await
writer
.
WriteAsync
(
": "
);
...
...
Titanium.Web.Proxy/Network/DebugCustomBufferedStream.cs
0 → 100644
View file @
80dd44e5
#
if
DEBUG
using
System
;
using
System.Collections.Generic
;
using
System.IO
;
using
System.Linq
;
using
System.Text
;
using
System.Threading
;
using
System.Threading.Tasks
;
using
StreamExtended.Network
;
namespace
Titanium.Web.Proxy.Network
{
class
DebugCustomBufferedStream
:
CustomBufferedStream
{
private
const
string
basePath
=
@"."
;
private
static
int
counter
;
public
int
Counter
{
get
;
}
private
readonly
FileStream
fileStreamSent
;
private
readonly
FileStream
fileStreamReceived
;
public
DebugCustomBufferedStream
(
Stream
baseStream
,
int
bufferSize
)
:
base
(
baseStream
,
bufferSize
)
{
Counter
=
Interlocked
.
Increment
(
ref
counter
);
fileStreamSent
=
new
FileStream
(
Path
.
Combine
(
basePath
,
$"
{
Counter
}
_sent.dat"
),
FileMode
.
Create
);
fileStreamReceived
=
new
FileStream
(
Path
.
Combine
(
basePath
,
$"
{
Counter
}
_received.dat"
),
FileMode
.
Create
);
}
protected
override
void
OnDataSent
(
byte
[]
buffer
,
int
offset
,
int
count
)
{
fileStreamSent
.
Write
(
buffer
,
offset
,
count
);
}
protected
override
void
OnDataReceived
(
byte
[]
buffer
,
int
offset
,
int
count
)
{
fileStreamReceived
.
Write
(
buffer
,
offset
,
count
);
}
public
override
void
Flush
()
{
fileStreamSent
.
Flush
(
true
);
fileStreamReceived
.
Flush
(
true
);
base
.
Flush
();
}
}
}
#
endif
\ No newline at end of file
Titanium.Web.Proxy/Network/ProxyClient.cs
View file @
80dd44e5
...
...
@@ -15,7 +15,7 @@ namespace Titanium.Web.Proxy.Network
internal
TcpClient
TcpClient
{
get
;
set
;
}
/// <summary>
///
h
olds the stream to client
///
H
olds the stream to client
/// </summary>
internal
CustomBufferedStream
ClientStream
{
get
;
set
;
}
...
...
@@ -25,7 +25,7 @@ namespace Titanium.Web.Proxy.Network
internal
CustomBinaryReader
ClientStreamReader
{
get
;
set
;
}
/// <summary>
///
u
sed to write line by line to client
///
U
sed to write line by line to client
/// </summary>
internal
HttpResponseWriter
ClientStreamWriter
{
get
;
set
;
}
}
...
...
Titanium.Web.Proxy/Network/Tcp/TcpConnection.cs
View file @
80dd44e5
...
...
@@ -2,6 +2,8 @@
using
System
;
using
System.Net
;
using
System.Net.Sockets
;
using
Titanium.Web.Proxy.Extensions
;
using
Titanium.Web.Proxy.Helpers
;
using
Titanium.Web.Proxy.Models
;
namespace
Titanium.Web.Proxy.Network.Tcp
...
...
@@ -11,6 +13,8 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// </summary>
internal
class
TcpConnection
:
IDisposable
{
private
ProxyServer
proxyServer
{
get
;
}
internal
ExternalProxy
UpStreamProxy
{
get
;
set
;
}
internal
string
HostName
{
get
;
set
;
}
...
...
@@ -34,10 +38,15 @@ namespace Titanium.Web.Proxy.Network.Tcp
internal
TcpClient
TcpClient
{
private
get
;
set
;
}
/// <summary>
///
u
sed to read lines from server
///
U
sed to read lines from server
/// </summary>
internal
CustomBinaryReader
StreamReader
{
get
;
set
;
}
/// <summary>
/// Used to write lines to server
/// </summary>
internal
HttpRequestWriter
StreamWriter
{
get
;
set
;
}
/// <summary>
/// Server stream
/// </summary>
...
...
@@ -48,9 +57,11 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// </summary>
internal
DateTime
LastAccess
{
get
;
set
;
}
internal
TcpConnection
()
internal
TcpConnection
(
ProxyServer
proxyServer
)
{
LastAccess
=
DateTime
.
Now
;
this
.
proxyServer
=
proxyServer
;
this
.
proxyServer
.
UpdateServerConnectionCount
(
true
);
}
/// <summary>
...
...
@@ -58,25 +69,13 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// </summary>
public
void
Dispose
()
{
StreamReader
?.
Dispose
();
Stream
?.
Dispose
();
StreamReader
?.
Dispose
();
TcpClient
.
CloseSocket
();
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
.
Close
();
}
}
catch
{
}
proxyServer
.
UpdateServerConnectionCount
(
false
);
}
}
}
Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs
View file @
80dd44e5
...
...
@@ -8,6 +8,7 @@ using System.Text;
using
System.Threading.Tasks
;
using
Titanium.Web.Proxy.Extensions
;
using
Titanium.Web.Proxy.Helpers
;
using
Titanium.Web.Proxy.Http
;
using
Titanium.Web.Proxy.Models
;
namespace
Titanium.Web.Proxy.Network.Tcp
...
...
@@ -68,24 +69,21 @@ namespace Titanium.Web.Proxy.Network.Tcp
if
(
useUpstreamProxy
&&
(
isConnect
||
isHttps
))
{
using
(
var
writer
=
new
HttpRequestWriter
(
stream
,
server
.
BufferSize
))
{
await
writer
.
WriteLineAsync
(
$"CONNECT
{
remoteHostName
}
:
{
remotePort
}
HTTP/
{
httpVersion
}
"
);
await
writer
.
WriteLineAsync
(
$"Host:
{
remoteHostName
}
:
{
remotePort
}
"
);
await
writer
.
WriteLineAsync
(
"Connection: Keep-Alive"
);
if
(!
string
.
IsNullOrEmpty
(
externalProxy
.
UserName
)
&&
externalProxy
.
Password
!=
null
)
{
await
HttpHeader
.
ProxyConnectionKeepAlive
.
WriteToStreamAsync
(
writer
);
await
writer
.
WriteLineAsync
(
"Proxy-Authorization"
+
": Basic "
+
Convert
.
ToBase64String
(
Encoding
.
UTF8
.
GetBytes
(
externalProxy
.
UserName
+
":"
+
externalProxy
.
Password
)));
}
var
writer
=
new
HttpRequestWriter
(
stream
,
server
.
BufferSize
);
await
writer
.
WriteLineAsync
(
$"CONNECT
{
remoteHostName
}
:
{
remotePort
}
HTTP/
{
httpVersion
}
"
);
await
writer
.
WriteLineAsync
(
$"Host:
{
remoteHostName
}
:
{
remotePort
}
"
);
await
writer
.
WriteLineAsync
(
$"
{
KnownHeaders
.
Connection
}
:
{
KnownHeaders
.
ConnectionKeepAlive
}
"
);
await
writer
.
WriteLineAsync
();
await
writer
.
FlushAsync
();
if
(!
string
.
IsNullOrEmpty
(
externalProxy
.
UserName
)
&&
externalProxy
.
Password
!=
null
)
{
await
HttpHeader
.
ProxyConnectionKeepAlive
.
WriteToStreamAsync
(
writer
);
await
writer
.
WriteLineAsync
(
KnownHeaders
.
ProxyAuthorization
+
": Basic "
+
Convert
.
ToBase64String
(
Encoding
.
UTF8
.
GetBytes
(
externalProxy
.
UserName
+
":"
+
externalProxy
.
Password
)));
}
await
writer
.
WriteLineAsync
();
using
(
var
reader
=
new
CustomBinaryReader
(
stream
,
server
.
BufferSize
))
{
string
result
=
await
reader
.
ReadLineAsync
();
...
...
@@ -118,9 +116,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
throw
;
}
server
.
UpdateServerConnectionCount
(
true
);
return
new
TcpConnection
return
new
TcpConnection
(
server
)
{
UpStreamProxy
=
externalProxy
,
UpStreamEndPoint
=
upStreamEndPoint
,
...
...
@@ -130,6 +126,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
UseUpstreamProxy
=
useUpstreamProxy
,
TcpClient
=
client
,
StreamReader
=
new
CustomBinaryReader
(
stream
,
server
.
BufferSize
),
StreamWriter
=
new
HttpRequestWriter
(
stream
,
server
.
BufferSize
),
Stream
=
stream
,
Version
=
httpVersion
};
...
...
Titanium.Web.Proxy/ProxyAuthorizationHandler.cs
View file @
80dd44e5
...
...
@@ -8,6 +8,7 @@ using Titanium.Web.Proxy.Exceptions;
using
Titanium.Web.Proxy.Helpers
;
using
Titanium.Web.Proxy.Http
;
using
Titanium.Web.Proxy.Models
;
using
Titanium.Web.Proxy.Shared
;
namespace
Titanium.Web.Proxy
{
...
...
@@ -20,37 +21,36 @@ namespace Titanium.Web.Proxy
return
true
;
}
var
httpHeaders
=
session
.
WebSession
.
Request
.
Headers
.
ToArray
()
;
var
httpHeaders
=
session
.
WebSession
.
Request
.
Headers
;
try
{
var
header
=
httpHeaders
.
FirstOrDefault
(
t
=>
t
.
Name
==
"Proxy-Authorization"
);
var
header
=
httpHeaders
.
GetFirstHeader
(
KnownHeaders
.
ProxyAuthorization
);
if
(
header
==
null
)
{
session
.
WebSession
.
Response
=
await
SendAuthentication407Response
(
clientStreamWriter
,
"Proxy Authentication Required"
);
return
false
;
}
string
headerValue
=
header
.
Value
.
Trim
(
);
if
(
!
headerValue
.
StartsWith
(
"basic"
,
StringComparison
.
CurrentCultureIgnoreCase
))
var
headerValueParts
=
header
.
Value
.
Split
(
ProxyConstants
.
SpaceSplit
);
if
(
headerValueParts
.
Length
!=
2
||
!
headerValueParts
[
0
].
Equals
(
"basic"
,
StringComparison
.
CurrentCultureIgnoreCase
))
{
//Return not authorized
session
.
WebSession
.
Response
=
await
SendAuthentication407Response
(
clientStreamWriter
,
"Proxy Authentication Invalid"
);
return
false
;
}
headerValue
=
headerValue
.
Substring
(
5
).
Trim
();
string
decoded
=
Encoding
.
UTF8
.
GetString
(
Convert
.
FromBase64String
(
headerValue
));
if
(
decoded
.
Contains
(
":"
)
==
false
)
string
decoded
=
Encoding
.
UTF8
.
GetString
(
Convert
.
FromBase64String
(
headerValueParts
[
1
]));
int
colonIndex
=
decoded
.
IndexOf
(
':'
);
if
(
colonIndex
==
-
1
)
{
//Return not authorized
session
.
WebSession
.
Response
=
await
SendAuthentication407Response
(
clientStreamWriter
,
"Proxy Authentication Invalid"
);
return
false
;
}
string
username
=
decoded
.
Substring
(
0
,
decoded
.
IndexOf
(
':'
)
);
string
password
=
decoded
.
Substring
(
decoded
.
IndexOf
(
':'
)
+
1
);
string
username
=
decoded
.
Substring
(
0
,
colonIndex
);
string
password
=
decoded
.
Substring
(
colonIndex
+
1
);
return
await
AuthenticateUserFunc
(
username
,
password
);
}
catch
(
Exception
e
)
...
...
@@ -72,8 +72,8 @@ namespace Titanium.Web.Proxy
StatusDescription
=
description
};
response
.
Headers
.
AddHeader
(
"Proxy-Authenticate"
,
$"Basic realm=\"
{
ProxyRealm
}
\""
);
response
.
Headers
.
AddHeader
(
"Proxy-Connection"
,
"close"
);
response
.
Headers
.
AddHeader
(
KnownHeaders
.
ProxyAuthenticate
,
$"Basic realm=\"
{
ProxyRealm
}
\""
);
response
.
Headers
.
AddHeader
(
KnownHeaders
.
ProxyConnection
,
KnownHeaders
.
ProxyConnectionClose
);
await
clientStreamWriter
.
WriteResponseAsync
(
response
);
return
response
;
...
...
Titanium.Web.Proxy/ProxyServer.cs
View file @
80dd44e5
...
...
@@ -9,6 +9,7 @@ using System.Threading;
using
System.Threading.Tasks
;
using
StreamExtended.Network
;
using
Titanium.Web.Proxy.EventArguments
;
using
Titanium.Web.Proxy.Extensions
;
using
Titanium.Web.Proxy.Helpers
;
using
Titanium.Web.Proxy.Helpers.WinHttp
;
using
Titanium.Web.Proxy.Models
;
...
...
@@ -164,22 +165,22 @@ namespace Titanium.Web.Proxy
/// <summary>
/// Intercept request to server
/// </summary>
public
event
Func
<
object
,
SessionEventArgs
,
Task
>
BeforeRequest
;
public
event
AsyncEventHandler
<
SessionEventArgs
>
BeforeRequest
;
/// <summary>
/// Intercept response from server
/// </summary>
public
event
Func
<
object
,
SessionEventArgs
,
Task
>
BeforeResponse
;
public
event
AsyncEventHandler
<
SessionEventArgs
>
BeforeResponse
;
/// <summary>
/// Intercept tunnel connect reques
/// </summary>
public
event
Func
<
object
,
TunnelConnectSessionEventArgs
,
Task
>
TunnelConnectRequest
;
public
event
AsyncEventHandler
<
TunnelConnectSessionEventArgs
>
TunnelConnectRequest
;
/// <summary>
/// Intercept tunnel connect response
/// </summary>
public
event
Func
<
object
,
TunnelConnectSessionEventArgs
,
Task
>
TunnelConnectResponse
;
public
event
AsyncEventHandler
<
TunnelConnectSessionEventArgs
>
TunnelConnectResponse
;
/// <summary>
/// Occurs when client connection count changed.
...
...
@@ -229,12 +230,12 @@ namespace Titanium.Web.Proxy
/// <summary>
/// Verifies the remote Secure Sockets Layer (SSL) certificate used for authentication
/// </summary>
public
event
Func
<
object
,
CertificateValidationEventArgs
,
Task
>
ServerCertificateValidationCallback
;
public
event
AsyncEventHandler
<
CertificateValidationEventArgs
>
ServerCertificateValidationCallback
;
/// <summary>
/// Callback tooverride client certificate during SSL mutual authentication
/// </summary>
public
event
Func
<
object
,
CertificateSelectionEventArgs
,
Task
>
ClientCertificateSelectionCallback
;
public
event
AsyncEventHandler
<
CertificateSelectionEventArgs
>
ClientCertificateSelectionCallback
;
/// <summary>
/// Callback for error events in proxy
...
...
@@ -612,16 +613,14 @@ namespace Titanium.Web.Proxy
/// <param name="serverConnection"></param>
private
void
Dispose
(
CustomBufferedStream
clientStream
,
CustomBinaryReader
clientStreamReader
,
HttpResponseWriter
clientStreamWriter
,
TcpConnection
serverConnection
)
{
clientStream
?.
Dispose
();
clientStreamReader
?.
Dispose
();
clientStreamWriter
?.
Dispose
();
clientStream
?.
Dispose
();
if
(
serverConnection
!=
null
)
{
serverConnection
.
Dispose
();
serverConnection
=
null
;
UpdateServerConnectionCount
(
false
);
}
}
...
...
@@ -766,22 +765,7 @@ namespace Titanium.Web.Proxy
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
.
Close
();
}
}
catch
{
}
tcpClient
.
CloseSocket
();
}
}
...
...
Titanium.Web.Proxy/RequestHandler.cs
View file @
80dd44e5
This diff is collapsed.
Click to expand it.
Titanium.Web.Proxy/ResponseHandler.cs
View file @
80dd44e5
...
...
@@ -43,7 +43,7 @@ namespace Titanium.Web.Proxy
//If user requested call back then do it
if
(
BeforeResponse
!=
null
&&
!
response
.
ResponseLocked
)
{
await
BeforeResponse
.
Invoke
Parallel
Async
(
this
,
args
,
ExceptionFunc
);
await
BeforeResponse
.
InvokeAsync
(
this
,
args
,
ExceptionFunc
);
}
//if user requested to send request again
...
...
@@ -96,7 +96,7 @@ namespace Titanium.Web.Proxy
}
await
clientStreamWriter
.
WriteHeadersAsync
(
response
.
Headers
);
await
clientStreamWriter
.
Write
Response
BodyAsync
(
response
.
Body
,
isChunked
);
await
clientStreamWriter
.
WriteBodyAsync
(
response
.
Body
,
isChunked
);
}
else
{
...
...
@@ -105,12 +105,9 @@ namespace Titanium.Web.Proxy
//Write body if exists
if
(
response
.
HasBody
)
{
await
clientStreamWriter
.
WriteResponseBodyAsync
(
BufferSize
,
args
.
WebSession
.
ServerConnection
.
StreamReader
,
response
.
IsChunked
,
response
.
ContentLength
);
await
args
.
CopyResponseBodyAsync
(
clientStreamWriter
,
false
);
}
}
await
clientStreamWriter
.
FlushAsync
();
}
catch
(
Exception
e
)
{
...
...
Titanium.Web.Proxy/Shared/ProxyConstants.cs
View file @
80dd44e5
using
System.Text
;
namespace
Titanium.Web.Proxy.Shared
namespace
Titanium.Web.Proxy.Shared
{
/// <summary>
/// Literals shared by Proxy Server
...
...
@@ -14,10 +12,6 @@ namespace Titanium.Web.Proxy.Shared
internal
static
readonly
char
[]
SemiColonSplit
=
{
';'
};
internal
static
readonly
char
[]
EqualSplit
=
{
'='
};
internal
static
readonly
byte
[]
NewLineBytes
=
Encoding
.
ASCII
.
GetBytes
(
NewLine
);
internal
static
readonly
byte
[]
ChunkEnd
=
Encoding
.
ASCII
.
GetBytes
(
0.
ToString
(
"x2"
)
+
NewLine
+
NewLine
);
internal
const
string
NewLine
=
"\r\n"
;
internal
static
readonly
byte
[]
NewLine
=
{(
byte
)
'\r'
,
(
byte
)
'\n'
};
}
}
Titanium.Web.Proxy/Titanium.Web.Proxy.csproj
View file @
80dd44e5
...
...
@@ -13,7 +13,7 @@
<ItemGroup>
<PackageReference Include="Portable.BouncyCastle" Version="1.8.1.3" />
<PackageReference Include="StreamExtended" Version="1.0.1
10
-beta" />
<PackageReference Include="StreamExtended" Version="1.0.1
32
-beta" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
...
...
@@ -21,7 +21,7 @@
<Version>4.4.0</Version>
</PackageReference>
<PackageReference Include="System.Security.Principal.Windows">
<Version>4.4.
0
</Version>
<Version>4.4.
1
</Version>
</PackageReference>
</ItemGroup>
...
...
Titanium.Web.Proxy/WinAuthHandler.cs
View file @
80dd44e5
...
...
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using
System.Linq
;
using
System.Threading.Tasks
;
using
Titanium.Web.Proxy.EventArguments
;
using
Titanium.Web.Proxy.Http
;
using
Titanium.Web.Proxy.Models
;
using
Titanium.Web.Proxy.Network.WinAuth
;
...
...
@@ -84,9 +85,9 @@ namespace Titanium.Web.Proxy
string
scheme
=
authSchemes
.
FirstOrDefault
(
x
=>
authHeader
.
Value
.
Equals
(
x
,
StringComparison
.
OrdinalIgnoreCase
));
//clear any existing headers to avoid confusing bad servers
if
(
args
.
WebSession
.
Request
.
Headers
.
NonUniqueHeaders
.
ContainsKey
(
"Authorization"
))
if
(
args
.
WebSession
.
Request
.
Headers
.
NonUniqueHeaders
.
ContainsKey
(
KnownHeaders
.
Authorization
))
{
args
.
WebSession
.
Request
.
Headers
.
NonUniqueHeaders
.
Remove
(
"Authorization"
);
args
.
WebSession
.
Request
.
Headers
.
NonUniqueHeaders
.
Remove
(
KnownHeaders
.
Authorization
);
}
//initial value will match exactly any of the schemes
...
...
@@ -94,16 +95,16 @@ namespace Titanium.Web.Proxy
{
string
clientToken
=
WinAuthHandler
.
GetInitialAuthToken
(
args
.
WebSession
.
Request
.
Host
,
scheme
,
args
.
Id
);
var
auth
=
new
HttpHeader
(
"Authorization"
,
string
.
Concat
(
scheme
,
clientToken
));
var
auth
=
new
HttpHeader
(
KnownHeaders
.
Authorization
,
string
.
Concat
(
scheme
,
clientToken
));
//replace existing authorization header if any
if
(
args
.
WebSession
.
Request
.
Headers
.
Headers
.
ContainsKey
(
"Authorization"
))
if
(
args
.
WebSession
.
Request
.
Headers
.
Headers
.
ContainsKey
(
KnownHeaders
.
Authorization
))
{
args
.
WebSession
.
Request
.
Headers
.
Headers
[
"Authorization"
]
=
auth
;
args
.
WebSession
.
Request
.
Headers
.
Headers
[
KnownHeaders
.
Authorization
]
=
auth
;
}
else
{
args
.
WebSession
.
Request
.
Headers
.
Headers
.
Add
(
"Authorization"
,
auth
);
args
.
WebSession
.
Request
.
Headers
.
Headers
.
Add
(
KnownHeaders
.
Authorization
,
auth
);
}
//don't need to send body for Authorization request
...
...
@@ -122,7 +123,7 @@ namespace Titanium.Web.Proxy
string
clientToken
=
WinAuthHandler
.
GetFinalAuthToken
(
args
.
WebSession
.
Request
.
Host
,
serverToken
,
args
.
Id
);
//there will be an existing header from initial client request
args
.
WebSession
.
Request
.
Headers
.
Headers
[
"Authorization"
]
=
new
HttpHeader
(
"Authorization"
,
string
.
Concat
(
scheme
,
clientToken
));
args
.
WebSession
.
Request
.
Headers
.
Headers
[
KnownHeaders
.
Authorization
]
=
new
HttpHeader
(
KnownHeaders
.
Authorization
,
string
.
Concat
(
scheme
,
clientToken
));
//send body for final auth request
if
(
args
.
WebSession
.
Request
.
HasBody
)
...
...
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