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
9bae9513
Unverified
Commit
9bae9513
authored
Nov 30, 2019
by
honfika
Committed by
GitHub
Nov 30, 2019
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #692 from justcoding121/beta
stable
parents
931d59c1
f9a74d7b
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
661 additions
and
129 deletions
+661
-129
ProxyTestController.cs
.../Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs
+49
-5
Titanium.Web.Proxy.sln.DotSettings
src/Titanium.Web.Proxy.sln.DotSettings
+1
-0
SessionEventArgs.cs
src/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs
+4
-0
ExplicitClientHandler.cs
src/Titanium.Web.Proxy/ExplicitClientHandler.cs
+55
-31
HttpHelper.cs
src/Titanium.Web.Proxy/Helpers/HttpHelper.cs
+82
-37
HttpStream.cs
src/Titanium.Web.Proxy/Helpers/HttpStream.cs
+172
-36
KnownMethod.cs
src/Titanium.Web.Proxy/Helpers/KnownMethod.cs
+71
-0
NullWriter.cs
src/Titanium.Web.Proxy/Helpers/NullWriter.cs
+20
-20
Net45Compatibility.cs
src/Titanium.Web.Proxy/Net45Compatibility.cs
+2
-0
TcpConnectionFactory.cs
src/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs
+2
-0
WebSocketDecoder.cs
src/Titanium.Web.Proxy/WebSocketDecoder.cs
+163
-0
WebSocketFrame.cs
src/Titanium.Web.Proxy/WebSocketFrame.cs
+28
-0
WebsocketOpCode.cs
src/Titanium.Web.Proxy/WebsocketOpCode.cs
+12
-0
No files found.
examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs
View file @
9bae9513
using
System
;
using
System.Collections.Concurrent
;
using
System.Collections.Generic
;
using
System.Linq
;
using
System.Net
;
using
System.Net.Security
;
using
System.Text
;
using
System.Threading
;
using
System.Threading.Tasks
;
using
Titanium.Web.Proxy.EventArguments
;
...
...
@@ -10,6 +12,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.StreamExtended.Network
;
namespace
Titanium.Web.Proxy.Examples.Basic
{
...
...
@@ -22,6 +25,9 @@ namespace Titanium.Web.Proxy.Examples.Basic
public
ProxyTestController
()
{
proxyServer
=
new
ProxyServer
();
//proxyServer.EnableHttp2 = true;
// generate root certificate without storing it in file system
//proxyServer.CertificateManager.CreateRootCertificate(false);
...
...
@@ -32,11 +38,11 @@ namespace Titanium.Web.Proxy.Examples.Basic
{
if
(
exception
is
ProxyHttpException
phex
)
{
await
writeToConsole
(
exception
.
Message
+
": "
+
phex
.
InnerException
?.
Message
,
true
);
await
writeToConsole
(
exception
.
Message
+
": "
+
phex
.
InnerException
?.
Message
,
ConsoleColor
.
Red
);
}
else
{
await
writeToConsole
(
exception
.
Message
,
true
);
await
writeToConsole
(
exception
.
Message
,
ConsoleColor
.
Red
);
}
};
proxyServer
.
ForwardToUpstreamGateway
=
true
;
...
...
@@ -146,6 +152,38 @@ namespace Titanium.Web.Proxy.Examples.Basic
}
}
private
void
WebSocket_DataSent
(
object
sender
,
DataEventArgs
e
)
{
var
args
=
(
SessionEventArgs
)
sender
;
WebSocketDataSentReceived
(
args
,
e
,
true
);
}
private
void
WebSocket_DataReceived
(
object
sender
,
DataEventArgs
e
)
{
var
args
=
(
SessionEventArgs
)
sender
;
WebSocketDataSentReceived
(
args
,
e
,
false
);
}
private
void
WebSocketDataSentReceived
(
SessionEventArgs
args
,
DataEventArgs
e
,
bool
sent
)
{
var
color
=
sent
?
ConsoleColor
.
Green
:
ConsoleColor
.
Blue
;
foreach
(
var
frame
in
args
.
WebSocketDecoder
.
Decode
(
e
.
Buffer
,
e
.
Offset
,
e
.
Count
))
{
if
(
frame
.
OpCode
==
WebsocketOpCode
.
Binary
)
{
var
data
=
frame
.
Data
.
ToArray
();
string
str
=
string
.
Join
(
","
,
data
.
ToArray
().
Select
(
x
=>
x
.
ToString
(
"X2"
)));
writeToConsole
(
str
,
color
).
Wait
();
}
if
(
frame
.
OpCode
==
WebsocketOpCode
.
Text
)
{
writeToConsole
(
frame
.
GetText
(),
color
).
Wait
();
}
}
}
private
Task
onBeforeTunnelConnectResponse
(
object
sender
,
TunnelConnectSessionEventArgs
e
)
{
return
Task
.
FromResult
(
false
);
...
...
@@ -205,6 +243,12 @@ namespace Titanium.Web.Proxy.Examples.Basic
private
async
Task
onResponse
(
object
sender
,
SessionEventArgs
e
)
{
if
(
e
.
HttpClient
.
ConnectRequest
?.
TunnelType
==
TunnelType
.
Websocket
)
{
e
.
DataSent
+=
WebSocket_DataSent
;
e
.
DataReceived
+=
WebSocket_DataReceived
;
}
await
writeToConsole
(
"Active Server Connections:"
+
((
ProxyServer
)
sender
).
ServerConnectionCount
);
string
ext
=
System
.
IO
.
Path
.
GetExtension
(
e
.
HttpClient
.
Request
.
RequestUri
.
AbsolutePath
);
...
...
@@ -277,14 +321,14 @@ namespace Titanium.Web.Proxy.Examples.Basic
return
Task
.
FromResult
(
0
);
}
private
async
Task
writeToConsole
(
string
message
,
bool
useRedColor
=
false
)
private
async
Task
writeToConsole
(
string
message
,
ConsoleColor
?
consoleColor
=
null
)
{
await
@lock
.
WaitAsync
();
if
(
useRedColor
)
if
(
consoleColor
.
HasValue
)
{
ConsoleColor
existing
=
Console
.
ForegroundColor
;
Console
.
ForegroundColor
=
ConsoleColor
.
Red
;
Console
.
ForegroundColor
=
consoleColor
.
Value
;
Console
.
WriteLine
(
message
);
Console
.
ForegroundColor
=
existing
;
}
...
...
src/Titanium.Web.Proxy.sln.DotSettings
View file @
9bae9513
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/CodeAnnotations/NamespacesWithAnnotations/=Titanium_002EWeb_002EProxy_002EExamples_002EWpf_002EAnnotations/@EntryIndexedValue">True</s:Boolean>
<s:String x:Key="/Default/CodeInspection/GeneratedCode/GeneratedFileMasks/=docfx_002Ejson/@EntryIndexedValue">docfx.json</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_EXISTING_EMBEDDED_ARRANGEMENT/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/LINE_FEED_AT_FILE_END/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSORHOLDER_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
...
...
src/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs
View file @
9bae9513
...
...
@@ -26,6 +26,8 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary>
private
bool
reRequest
;
private
WebSocketDecoder
webSocketDecoder
;
/// <summary>
/// Is this session a HTTP/2 promise?
/// </summary>
...
...
@@ -58,6 +60,8 @@ namespace Titanium.Web.Proxy.EventArguments
}
}
public
WebSocketDecoder
WebSocketDecoder
=>
webSocketDecoder
??=
new
WebSocketDecoder
(
BufferPool
);
/// <summary>
/// Occurs when multipart request part sent.
/// </summary>
...
...
src/Titanium.Web.Proxy/ExplicitClientHandler.cs
View file @
9bae9513
...
...
@@ -47,9 +47,15 @@ namespace Titanium.Web.Proxy
try
{
TunnelConnectSessionEventArgs
?
connectArgs
=
null
;
var
method
=
await
HttpHelper
.
GetMethod
(
clientStream
,
BufferPool
,
cancellationToken
);
if
(
clientStream
.
IsClosed
)
{
return
;
}
// Client wants to create a secure tcp tunnel (probably its a HTTPS or Websocket request)
if
(
await
HttpHelper
.
IsConnectMethod
(
clientStream
,
BufferPool
,
cancellationToken
)
==
1
)
if
(
method
==
KnownMethod
.
Connect
)
{
// read the first line HTTP command
var
requestLine
=
await
clientStream
.
ReadRequestLine
(
cancellationToken
);
...
...
@@ -75,6 +81,7 @@ namespace Titanium.Web.Proxy
// filter out excluded host names
bool
decryptSsl
=
endPoint
.
DecryptSsl
&&
connectArgs
.
DecryptSsl
;
bool
sendRawData
=
!
decryptSsl
;
if
(
connectArgs
.
DenyConnect
)
{
...
...
@@ -113,6 +120,10 @@ namespace Titanium.Web.Proxy
await
clientStream
.
WriteResponseAsync
(
response
,
cancellationToken
);
var
clientHelloInfo
=
await
SslTools
.
PeekClientHello
(
clientStream
,
BufferPool
,
cancellationToken
);
if
(
clientStream
.
IsClosed
)
{
return
;
}
bool
isClientHello
=
clientHelloInfo
!=
null
;
if
(
clientHelloInfo
!=
null
)
...
...
@@ -130,26 +141,29 @@ namespace Titanium.Web.Proxy
bool
http2Supported
=
false
;
var
alpn
=
clientHelloInfo
.
GetAlpn
();
if
(
alpn
!=
null
&&
alpn
.
Contains
(
SslApplicationProtocol
.
Http2
))
if
(
EnableHttp2
)
{
// test server HTTP/2 support
try
{
// todo: this is a hack, because Titanium does not support HTTP protocol changing currently
var
connection
=
await
tcpConnectionFactory
.
GetServerConnection
(
this
,
connectArgs
,
true
,
SslExtensions
.
Http2ProtocolAsList
,
true
,
cancellationToken
);
http2Supported
=
connection
.
NegotiatedApplicationProtocol
==
SslApplicationProtocol
.
Http2
;
// release connection back to pool instead of closing when connection pool is enabled.
await
tcpConnectionFactory
.
Release
(
connection
,
true
);
}
catch
(
Exception
)
var
alpn
=
clientHelloInfo
.
GetAlpn
();
if
(
alpn
!=
null
&&
alpn
.
Contains
(
SslApplicationProtocol
.
Http2
))
{
// ignore
// test server HTTP/2 support
try
{
// todo: this is a hack, because Titanium does not support HTTP protocol changing currently
var
connection
=
await
tcpConnectionFactory
.
GetServerConnection
(
this
,
connectArgs
,
true
,
SslExtensions
.
Http2ProtocolAsList
,
true
,
cancellationToken
);
http2Supported
=
connection
.
NegotiatedApplicationProtocol
==
SslApplicationProtocol
.
Http2
;
// release connection back to pool instead of closing when connection pool is enabled.
await
tcpConnectionFactory
.
Release
(
connection
,
true
);
}
catch
(
Exception
)
{
// ignore
}
}
}
...
...
@@ -224,36 +238,46 @@ namespace Titanium.Web.Proxy
$"Couldn't authenticate host '
{
connectHostname
}
' with certificate '
{
certName
}
'."
,
e
,
connectArgs
);
}
if
(
await
HttpHelper
.
IsConnectMethod
(
clientStream
,
BufferPool
,
cancellationToken
)
==
-
1
)
method
=
await
HttpHelper
.
GetMethod
(
clientStream
,
BufferPool
,
cancellationToken
);
if
(
clientStream
.
IsClosed
)
{
decryptSsl
=
false
;
return
;
}
if
(
!
decryptSsl
)
if
(
method
==
KnownMethod
.
Invalid
)
{
sendRawData
=
true
;
await
tcpConnectionFactory
.
Release
(
prefetchConnectionTask
,
true
);
prefetchConnectionTask
=
null
;
}
}
else
if
(
clientHelloInfo
==
null
)
{
method
=
await
HttpHelper
.
GetMethod
(
clientStream
,
BufferPool
,
cancellationToken
);
if
(
clientStream
.
IsClosed
)
{
return
;
}
}
if
(
cancellationTokenSource
.
IsCancellationRequested
)
{
throw
new
Exception
(
"Session was terminated by user."
);
}
// Hostname is excluded or it is not an HTTPS connect
if
(!
decryptSsl
||
!
isClientHello
)
if
(
method
==
KnownMethod
.
Invalid
)
{
if
(!
isClientHello
)
{
connectRequest
.
TunnelType
=
TunnelType
.
Websocket
;
}
sendRawData
=
true
;
}
// Hostname is excluded or it is not an HTTPS connect
if
(
sendRawData
)
{
// 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
tcpConnectionFactory
.
GetServerConnection
(
this
,
connectArgs
,
true
,
SslExtensions
.
Http2ProtocolAsList
,
true
,
null
,
true
,
cancellationToken
);
try
...
...
@@ -302,7 +326,7 @@ namespace Titanium.Web.Proxy
}
}
if
(
connectArgs
!=
null
&&
await
HttpHelper
.
IsPriMethod
(
clientStream
,
BufferPool
,
cancellationToken
)
==
1
)
if
(
connectArgs
!=
null
&&
method
==
KnownMethod
.
Pri
)
{
// todo
string
?
httpCmd
=
await
clientStream
.
ReadLineAsync
(
cancellationToken
);
...
...
src/Titanium.Web.Proxy/Helpers/HttpHelper.cs
View file @
9bae9513
...
...
@@ -167,32 +167,11 @@ namespace Titanium.Web.Proxy.Helpers
}
/// <summary>
///
Determines whether is connect method
.
///
Gets the HTTP method from the stream
.
/// </summary>
/// <returns>1: when CONNECT, 0: when valid HTTP method, -1: otherwise</returns>
internal
static
ValueTask
<
int
>
IsConnectMethod
(
IPeekStream
httpReader
,
IBufferPool
bufferPool
,
CancellationToken
cancellationToken
=
default
)
public
static
async
ValueTask
<
KnownMethod
>
GetMethod
(
IPeekStream
httpReader
,
IBufferPool
bufferPool
,
CancellationToken
cancellationToken
=
default
)
{
return
startsWith
(
httpReader
,
bufferPool
,
"CONNECT"
,
cancellationToken
);
}
/// <summary>
/// Determines whether is pri method (HTTP/2).
/// </summary>
/// <returns>1: when PRI, 0: when valid HTTP method, -1: otherwise</returns>
internal
static
ValueTask
<
int
>
IsPriMethod
(
IPeekStream
httpReader
,
IBufferPool
bufferPool
,
CancellationToken
cancellationToken
=
default
)
{
return
startsWith
(
httpReader
,
bufferPool
,
"PRI"
,
cancellationToken
);
}
/// <summary>
/// Determines whether the stream starts with the given string.
/// </summary>
/// <returns>
/// 1: when starts with the given string, 0: when valid HTTP method, -1: otherwise
/// </returns>
private
static
async
ValueTask
<
int
>
startsWith
(
IPeekStream
httpReader
,
IBufferPool
bufferPool
,
string
expectedStart
,
CancellationToken
cancellationToken
=
default
)
{
const
int
lengthToCheck
=
10
;
const
int
lengthToCheck
=
20
;
if
(
bufferPool
.
BufferSize
<
lengthToCheck
)
{
throw
new
Exception
(
$"Buffer is too small. Minimum size is
{
lengthToCheck
}
bytes"
);
...
...
@@ -201,13 +180,12 @@ namespace Titanium.Web.Proxy.Helpers
byte
[]
buffer
=
bufferPool
.
GetBuffer
(
bufferPool
.
BufferSize
);
try
{
bool
isExpected
=
true
;
int
i
=
0
;
while
(
i
<
lengthToCheck
)
{
int
peeked
=
await
httpReader
.
PeekBytesAsync
(
buffer
,
i
,
i
,
lengthToCheck
-
i
,
cancellationToken
);
if
(
peeked
<=
0
)
return
-
1
;
return
KnownMethod
.
Invalid
;
peeked
+=
i
;
...
...
@@ -216,27 +194,94 @@ namespace Titanium.Web.Proxy.Helpers
int
b
=
buffer
[
i
];
if
(
b
==
' '
&&
i
>
2
)
return
isExpected
?
1
:
0
;
else
{
char
ch
=
(
char
)
b
;
if
(
ch
<
'A'
||
ch
>
'z'
||
(
ch
>
'Z'
&&
ch
<
'a'
))
// ASCII letter
return
-
1
;
else
if
(
i
>=
expectedStart
.
Length
||
ch
!=
expectedStart
[
i
])
isExpected
=
false
;
}
return
getKnownMethod
(
buffer
.
AsSpan
(
0
,
i
));
char
ch
=
(
char
)
b
;
if
((
ch
<
'A'
||
ch
>
'z'
||
(
ch
>
'Z'
&&
ch
<
'a'
))
&&
(
ch
!=
'-'
))
// ASCII letter
return
KnownMethod
.
Invalid
;
i
++;
}
}
// only letters
return
0
;
// only letters
, but no space (or shorter than 3 characters)
return
KnownMethod
.
Invalid
;
}
finally
{
bufferPool
.
ReturnBuffer
(
buffer
);
}
}
private
static
KnownMethod
getKnownMethod
(
ReadOnlySpan
<
byte
>
method
)
{
// the following methods are supported:
// Connect
// Delete
// Get
// Head
// Options
// Post
// Put
// Trace
// Pri
// method parameter should have at least 3 bytes
byte
b1
=
method
[
0
];
byte
b2
=
method
[
1
];
byte
b3
=
method
[
2
];
switch
(
method
.
Length
)
{
case
3
:
// Get or Put
if
(
b1
==
'G'
)
return
b2
==
'E'
&&
b3
==
'T'
?
KnownMethod
.
Get
:
KnownMethod
.
Unknown
;
if
(
b1
==
'P'
)
{
if
(
b2
==
'U'
)
return
b3
==
'T'
?
KnownMethod
.
Put
:
KnownMethod
.
Unknown
;
if
(
b2
==
'R'
)
return
b3
==
'I'
?
KnownMethod
.
Pri
:
KnownMethod
.
Unknown
;
}
break
;
case
4
:
// Head or Post
if
(
b1
==
'H'
)
return
b2
==
'E'
&&
b3
==
'A'
&&
method
[
3
]
==
'D'
?
KnownMethod
.
Head
:
KnownMethod
.
Unknown
;
if
(
b1
==
'P'
)
return
b2
==
'O'
&&
b3
==
'S'
&&
method
[
3
]
==
'T'
?
KnownMethod
.
Post
:
KnownMethod
.
Unknown
;
break
;
case
5
:
// Trace
if
(
b1
==
'T'
)
return
b2
==
'R'
&&
b3
==
'A'
&&
method
[
3
]
==
'C'
&&
method
[
4
]
==
'E'
?
KnownMethod
.
Trace
:
KnownMethod
.
Unknown
;
break
;
case
6
:
// Delete
if
(
b1
==
'D'
)
return
b2
==
'E'
&&
b3
==
'L'
&&
method
[
3
]
==
'E'
&&
method
[
4
]
==
'T'
&&
method
[
5
]
==
'E'
?
KnownMethod
.
Delete
:
KnownMethod
.
Unknown
;
break
;
case
7
:
// Connect or Options
if
(
b1
==
'C'
)
return
b2
==
'O'
&&
b3
==
'N'
&&
method
[
3
]
==
'N'
&&
method
[
4
]
==
'E'
&&
method
[
5
]
==
'C'
&&
method
[
6
]
==
'T'
?
KnownMethod
.
Connect
:
KnownMethod
.
Unknown
;
if
(
b1
==
'O'
)
return
b2
==
'P'
&&
b3
==
'T'
&&
method
[
3
]
==
'I'
&&
method
[
4
]
==
'O'
&&
method
[
5
]
==
'N'
&&
method
[
6
]
==
'S'
?
KnownMethod
.
Options
:
KnownMethod
.
Unknown
;
break
;
}
return
KnownMethod
.
Unknown
;
}
}
}
src/Titanium.Web.Proxy/Helpers/HttpStream.cs
View file @
9bae9513
This diff is collapsed.
Click to expand it.
src/Titanium.Web.Proxy/Helpers/KnownMethod.cs
0 → 100644
View file @
9bae9513
namespace
Titanium.Web.Proxy.Helpers
{
internal
enum
KnownMethod
{
Unknown
,
Invalid
,
// RFC 7231: Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content
Connect
,
Delete
,
Get
,
Head
,
Options
,
Post
,
Put
,
Trace
,
// RFC 7540: Hypertext Transfer Protocol Version 2
Pri
,
// RFC 5789: PATCH Method for HTTP
Patch
,
// RFC 3744: Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol
Acl
,
// RFC 3253: Versioning Extensions to WebDAV (Web Distributed Authoring and Versioning)
BaselineControl
,
Checkin
,
Checkout
,
Label
,
Merge
,
Mkactivity
,
Mkworkspace
,
Report
,
Unckeckout
,
Update
,
VersionControl
,
// RFC 3648: Web Distributed Authoring and Versioning (WebDAV) Ordered Collections Protocol
Orderpatch
,
// RFC 4437: Web Distributed Authoring and Versioning (WebDAV): Redirect Reference Resources
Mkredirectref
,
Updateredirectref
,
// RFC 4791: Calendaring Extensions to WebDAV (CalDAV)
Mkcalendar
,
// RFC 4918: HTTP Extensions for Web Distributed Authoring and Versioning (WebDAV)
Copy
,
Lock
,
Mkcol
,
Move
,
Propfind
,
Proppatch
,
Unlock
,
// RFC 5323: Web Distributed Authoring and Versioning (WebDAV) SEARCH
Search
,
// RFC 5842: Binding Extensions to Web Distributed Authoring and Versioning (WebDAV)
Bind
,
Rebind
,
Unbind
,
// Internet Draft snell-link-method: HTTP Link and Unlink Methods
Link
,
Unlink
,
}
}
src/Titanium.Web.Proxy/Helpers/NullWriter.cs
View file @
9bae9513
...
...
@@ -2,33 +2,33 @@
using
System.Threading.Tasks
;
using
Titanium.Web.Proxy.StreamExtended.Network
;
internal
class
NullWriter
:
IHttpStreamWriter
namespace
Titanium.Web.Proxy.Helpers
{
public
static
NullWriter
Instance
{
get
;
}
=
new
NullWriter
();
public
void
Write
(
byte
[]
buffer
,
int
offset
,
int
count
)
internal
class
NullWriter
:
IHttpStreamWriter
{
}
public
static
NullWriter
Instance
{
get
;
}
=
new
NullWriter
();
#if NET45
public
async
Task
WriteAsync
(
byte
[]
buffer
,
int
offset
,
int
count
,
CancellationToken
cancellationToken
)
{
}
public
void
Write
(
byte
[]
buffer
,
int
offset
,
int
count
)
{
}
public
Task
WriteAsync
(
byte
[]
buffer
,
int
offset
,
int
count
,
CancellationToken
cancellationToken
)
{
#if NET45
return
Net45Compatibility
.
CompletedTask
;
#else
public
Task
WriteAsync
(
byte
[]
buffer
,
int
offset
,
int
count
,
CancellationToken
cancellationToken
)
{
return
Task
.
CompletedTask
;
}
return
Task
.
CompletedTask
;
#endif
}
public
ValueTask
WriteLineAsync
(
CancellationToken
cancellationToken
=
default
)
{
throw
new
System
.
NotImplementedException
();
}
public
ValueTask
WriteLineAsync
(
CancellationToken
cancellationToken
=
default
)
{
throw
new
System
.
NotImplementedException
();
}
public
ValueTask
WriteLineAsync
(
string
value
,
CancellationToken
cancellationToken
=
default
)
{
throw
new
System
.
NotImplementedException
();
public
ValueTask
WriteLineAsync
(
string
value
,
CancellationToken
cancellationToken
=
default
)
{
throw
new
System
.
NotImplementedException
();
}
}
}
src/Titanium.Web.Proxy/Net45Compatibility.cs
View file @
9bae9513
...
...
@@ -10,6 +10,8 @@ namespace Titanium.Web.Proxy
class
Net45Compatibility
{
public
static
byte
[]
EmptyArray
=
new
byte
[
0
];
public
static
Task
CompletedTask
=
new
Task
(()
=>
{
});
}
}
#endif
src/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs
View file @
9bae9513
...
...
@@ -60,6 +60,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
cacheKeyBuilder
.
Append
(
"-"
);
cacheKeyBuilder
.
Append
(
remotePort
);
cacheKeyBuilder
.
Append
(
"-"
);
// when creating Tcp client isConnect won't matter
cacheKeyBuilder
.
Append
(
isHttps
);
...
...
@@ -408,6 +409,7 @@ retry:
continue
;
}
break
;
}
catch
(
Exception
e
)
...
...
src/Titanium.Web.Proxy/WebSocketDecoder.cs
0 → 100644
View file @
9bae9513
using
System
;
using
System.Collections.Generic
;
using
System.Linq
;
using
System.Runtime.InteropServices
;
using
Titanium.Web.Proxy.StreamExtended.BufferPool
;
namespace
Titanium.Web.Proxy
{
public
class
WebSocketDecoder
{
private
byte
[]
buffer
;
private
long
bufferLength
;
internal
WebSocketDecoder
(
IBufferPool
bufferPool
)
{
buffer
=
new
byte
[
bufferPool
.
BufferSize
];
}
public
IEnumerable
<
WebSocketFrame
>
Decode
(
byte
[]
data
,
int
offset
,
int
count
)
{
var
buffer
=
data
.
AsMemory
(
offset
,
count
);
bool
copied
=
false
;
if
(
bufferLength
>
0
)
{
// already have remaining data
buffer
=
copyToBuffer
(
buffer
);
copied
=
true
;
}
while
(
true
)
{
var
data1
=
buffer
.
Span
;
if
(!
isDataEnough
(
data1
))
{
break
;
}
var
opCode
=
(
WebsocketOpCode
)(
data1
[
0
]
&
0xf
);
bool
isFinal
=
(
data1
[
0
]
&
0x80
)
!=
0
;
byte
b
=
data1
[
1
];
long
size
=
b
&
0x7f
;
// todo: size > int.Max??
bool
masked
=
(
b
&
0x80
)
!=
0
;
int
idx
=
2
;
if
(
size
>
125
)
{
if
(
size
==
126
)
{
size
=
(
data1
[
2
]
<<
8
)
+
data1
[
3
];
idx
=
4
;
}
else
{
size
=
((
long
)
data1
[
2
]
<<
56
)
+
((
long
)
data1
[
3
]
<<
48
)
+
((
long
)
data1
[
4
]
<<
40
)
+
((
long
)
data1
[
5
]
<<
32
)
+
((
long
)
data1
[
6
]
<<
24
)
+
(
data1
[
7
]
<<
16
)
+
(
data1
[
8
]
<<
8
)
+
data1
[
9
];
idx
=
10
;
}
}
if
(
data1
.
Length
<
idx
+
size
)
{
break
;
}
if
(
masked
)
{
//mask = (uint)(((long)data1[idx++] << 24) + (data1[idx++] << 16) + (data1[idx++] << 8) + data1[idx++]);
//mask = (uint)(data1[idx++] + (data1[idx++] << 8) + (data1[idx++] << 16) + ((long)data1[idx++] << 24));
var
uData
=
MemoryMarshal
.
Cast
<
byte
,
uint
>(
data1
.
Slice
(
idx
,
(
int
)
size
+
4
));
idx
+=
4
;
uint
mask
=
uData
[
0
];
long
size1
=
size
;
if
(
size
>
4
)
{
uData
=
uData
.
Slice
(
1
);
for
(
int
i
=
0
;
i
<
uData
.
Length
;
i
++)
{
uData
[
i
]
=
uData
[
i
]
^
mask
;
}
size1
-=
uData
.
Length
*
4
;
}
if
(
size1
>
0
)
{
int
pos
=
(
int
)(
idx
+
size
-
size1
);
data1
[
pos
]
^=
(
byte
)
mask
;
if
(
size1
>
1
)
{
data1
[
pos
+
1
]
^=
(
byte
)(
mask
>>
8
);
}
if
(
size1
>
2
)
{
data1
[
pos
+
2
]
^=
(
byte
)(
mask
>>
16
);
}
;
}
}
var
frameData
=
buffer
.
Slice
(
idx
,
(
int
)
size
);
var
frame
=
new
WebSocketFrame
{
IsFinal
=
isFinal
,
Data
=
frameData
,
OpCode
=
opCode
};
yield
return
frame
;
buffer
=
buffer
.
Slice
((
int
)(
idx
+
size
));
}
if
(!
copied
&&
buffer
.
Length
>
0
)
{
copyToBuffer
(
buffer
);
}
}
private
Memory
<
byte
>
copyToBuffer
(
ReadOnlyMemory
<
byte
>
data
)
{
long
requiredLength
=
bufferLength
+
data
.
Length
;
if
(
requiredLength
>
buffer
.
Length
)
{
Array
.
Resize
(
ref
buffer
,
(
int
)
Math
.
Min
(
requiredLength
,
buffer
.
Length
*
2
));
}
data
.
CopyTo
(
buffer
.
AsMemory
((
int
)
bufferLength
));
bufferLength
+=
data
.
Length
;
return
buffer
.
AsMemory
(
0
,
(
int
)
bufferLength
);
}
private
static
bool
isDataEnough
(
ReadOnlySpan
<
byte
>
data
)
{
int
length
=
data
.
Length
;
if
(
length
<
2
)
return
false
;
byte
size
=
data
[
1
];
if
((
size
&
0x80
)
!=
0
)
// masked
length
-=
4
;
size
&=
0x7f
;
if
(
size
==
126
)
{
if
(
length
<
2
)
{
return
false
;
}
}
else
if
(
size
==
127
)
{
if
(
length
<
10
)
{
return
false
;
}
}
return
length
>=
size
;
}
}
}
src/Titanium.Web.Proxy/WebSocketFrame.cs
0 → 100644
View file @
9bae9513
using
System
;
using
System.Text
;
namespace
Titanium.Web.Proxy
{
public
class
WebSocketFrame
{
public
bool
IsFinal
{
get
;
internal
set
;
}
public
WebsocketOpCode
OpCode
{
get
;
internal
set
;
}
public
ReadOnlyMemory
<
byte
>
Data
{
get
;
internal
set
;
}
public
string
GetText
()
{
return
GetText
(
Encoding
.
UTF8
);
}
public
string
GetText
(
Encoding
encoding
)
{
#if NETSTANDARD2_1
return
encoding
.
GetString
(
Data
.
Span
);
#else
return
encoding
.
GetString
(
Data
.
ToArray
());
#endif
}
}
}
src/Titanium.Web.Proxy/WebsocketOpCode.cs
0 → 100644
View file @
9bae9513
namespace
Titanium.Web.Proxy
{
public
enum
WebsocketOpCode
:
byte
{
Continuation
,
Text
,
Binary
,
ConnectionClose
=
8
,
Ping
,
Pong
,
}
}
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