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
b584af90
Unverified
Commit
b584af90
authored
Nov 24, 2019
by
honfika
Committed by
GitHub
Nov 24, 2019
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #679 from justcoding121/master
beta
parents
8fa9ebd3
fdfaf871
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
32 changed files
with
408 additions
and
305 deletions
+408
-305
ProxyTestController.cs
.../Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs
+16
-0
MainWindow.xaml
examples/Titanium.Web.Proxy.Examples.Wpf/MainWindow.xaml
+2
-0
MainWindow.xaml.cs
examples/Titanium.Web.Proxy.Examples.Wpf/MainWindow.xaml.cs
+32
-9
SessionListItem.cs
examples/Titanium.Web.Proxy.Examples.Wpf/SessionListItem.cs
+18
-1
SessionEventArgs.cs
src/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs
+10
-57
SessionEventArgsBase.cs
...Titanium.Web.Proxy/EventArguments/SessionEventArgsBase.cs
+4
-0
ExplicitClientHandler.cs
src/Titanium.Web.Proxy/ExplicitClientHandler.cs
+7
-9
HttpClientStream.cs
src/Titanium.Web.Proxy/Helpers/HttpClientStream.cs
+4
-1
HttpHelper.cs
src/Titanium.Web.Proxy/Helpers/HttpHelper.cs
+3
-3
HttpServerStream.cs
src/Titanium.Web.Proxy/Helpers/HttpServerStream.cs
+29
-4
HttpStream.cs
src/Titanium.Web.Proxy/Helpers/HttpStream.cs
+63
-27
NullWriter.cs
src/Titanium.Web.Proxy/Helpers/NullWriter.cs
+34
-0
ResponseStatusInfo.cs
src/Titanium.Web.Proxy/Helpers/ResponseStatusInfo.cs
+13
-0
HeaderBuilder.cs
src/Titanium.Web.Proxy/Http/HeaderBuilder.cs
+12
-2
HeaderParser.cs
src/Titanium.Web.Proxy/Http/HeaderParser.cs
+1
-1
HttpWebClient.cs
src/Titanium.Web.Proxy/Http/HttpWebClient.cs
+18
-48
Request.cs
src/Titanium.Web.Proxy/Http/Request.cs
+44
-46
RequestResponseBase.cs
src/Titanium.Web.Proxy/Http/RequestResponseBase.cs
+2
-0
Http2Helper.cs
src/Titanium.Web.Proxy/Http2/Http2Helper.cs
+1
-1
RetryPolicy.cs
src/Titanium.Web.Proxy/Network/RetryPolicy.cs
+2
-2
TcpConnectionFactory.cs
src/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs
+30
-21
TcpServerConnection.cs
src/Titanium.Web.Proxy/Network/Tcp/TcpServerConnection.cs
+3
-4
ProxyServer.cs
src/Titanium.Web.Proxy/ProxyServer.cs
+6
-0
RequestHandler.cs
src/Titanium.Web.Proxy/RequestHandler.cs
+10
-10
ResponseHandler.cs
src/Titanium.Web.Proxy/ResponseHandler.cs
+16
-18
IHttpStreamReader.cs
...ium.Web.Proxy/StreamExtended/Network/IHttpStreamReader.cs
+5
-1
IHttpStreamWriter.cs
...ium.Web.Proxy/StreamExtended/Network/IHttpStreamWriter.cs
+6
-5
WebSocketHandler.cs
src/Titanium.Web.Proxy/WebSocketHandler.cs
+9
-26
HttpContinueClient.cs
....Web.Proxy.IntegrationTests/Helpers/HttpContinueClient.cs
+1
-1
HttpMessageParsing.cs
....Web.Proxy.IntegrationTests/Helpers/HttpMessageParsing.cs
+1
-1
InterceptionTests.cs
.../Titanium.Web.Proxy.IntegrationTests/InterceptionTests.cs
+5
-6
Titanium.Web.Proxy.IntegrationTests.csproj
...tegrationTests/Titanium.Web.Proxy.IntegrationTests.csproj
+1
-1
No files found.
examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs
View file @
b584af90
...
...
@@ -42,6 +42,10 @@ namespace Titanium.Web.Proxy.Examples.Basic
proxyServer
.
ForwardToUpstreamGateway
=
true
;
proxyServer
.
CertificateManager
.
SaveFakeCertificates
=
true
;
// this is just to show the functionality, provided implementations use junk value
//proxyServer.GetCustomUpStreamProxyFunc = onGetCustomUpStreamProxyFunc;
//proxyServer.CustomUpStreamProxyFailureFunc = onCustomUpStreamProxyFailureFunc;
// optionally set the Certificate Engine
// Under Mono or Non-Windows runtimes only BouncyCastle will be supported
//proxyServer.CertificateManager.CertificateEngine = Network.CertificateEngine.BouncyCastle;
...
...
@@ -116,6 +120,18 @@ namespace Titanium.Web.Proxy.Examples.Basic
//proxyServer.CertificateManager.RemoveTrustedRootCertificates();
}
private
async
Task
<
IExternalProxy
>
onGetCustomUpStreamProxyFunc
(
SessionEventArgsBase
arg
)
{
// this is just to show the functionality, provided values are junk
return
new
ExternalProxy
()
{
BypassLocalhost
=
false
,
HostName
=
"127.0.0.9"
,
Port
=
9090
,
Password
=
"fake"
,
UserName
=
"fake"
,
UseDefaultCredentials
=
false
};
}
private
async
Task
<
IExternalProxy
>
onCustomUpStreamProxyFailureFunc
(
SessionEventArgsBase
arg
)
{
// this is just to show the functionality, provided values are junk
return
new
ExternalProxy
()
{
BypassLocalhost
=
false
,
HostName
=
"127.0.0.10"
,
Port
=
9191
,
Password
=
"fake2"
,
UserName
=
"fake2"
,
UseDefaultCredentials
=
false
};
}
private
async
Task
onBeforeTunnelConnectRequest
(
object
sender
,
TunnelConnectSessionEventArgs
e
)
{
string
hostname
=
e
.
HttpClient
.
Request
.
RequestUri
.
Host
;
...
...
examples/Titanium.Web.Proxy.Examples.Wpf/MainWindow.xaml
View file @
b584af90
...
...
@@ -32,6 +32,8 @@
<GridViewColumn Header="SentBytes" DisplayMemberBinding="{Binding SentDataCount}" />
<GridViewColumn Header="ReceivedBytes" DisplayMemberBinding="{Binding ReceivedDataCount}" />
<GridViewColumn Header="ClientEndPoint" DisplayMemberBinding="{Binding ClientEndPoint}" />
<GridViewColumn Header="ClientConnectionId" DisplayMemberBinding="{Binding ClientConnectionId}" />
<GridViewColumn Header="ServerConnectionId" DisplayMemberBinding="{Binding ServerConnectionId}" />
</GridView>
</ListView.View>
</ListView>
...
...
examples/Titanium.Web.Proxy.Examples.Wpf/MainWindow.xaml.cs
View file @
b584af90
...
...
@@ -146,13 +146,15 @@ namespace Titanium.Web.Proxy.Examples.Wpf
{
if
(
sessionDictionary
.
TryGetValue
(
e
.
HttpClient
,
out
var
item
))
{
item
.
Update
();
item
.
Update
(
e
);
}
});
}
private
async
Task
ProxyServer_BeforeRequest
(
object
sender
,
SessionEventArgs
e
)
{
//if (e.HttpClient.Request.HttpVersion.Major != 2) return;
SessionListItem
item
=
null
;
await
Dispatcher
.
InvokeAsync
(()
=>
{
item
=
addSession
(
e
);
});
...
...
@@ -175,7 +177,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf
{
if
(
sessionDictionary
.
TryGetValue
(
e
.
HttpClient
,
out
item
))
{
item
.
Update
();
item
.
Update
(
e
);
}
});
...
...
@@ -190,7 +192,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf
e
.
HttpClient
.
Response
.
KeepBody
=
true
;
await
e
.
GetResponseBody
();
await
Dispatcher
.
InvokeAsync
(()
=>
{
item
.
Update
();
});
await
Dispatcher
.
InvokeAsync
(()
=>
{
item
.
Update
(
e
);
});
if
(
item
==
SelectedSession
)
{
await
Dispatcher
.
InvokeAsync
(
selectedSessionChanged
);
...
...
@@ -225,6 +227,8 @@ namespace Titanium.Web.Proxy.Examples.Wpf
var
item
=
new
SessionListItem
{
Number
=
lastSessionNumber
,
ClientConnectionId
=
e
.
ClientConnectionId
,
ServerConnectionId
=
e
.
ServerConnectionId
,
HttpClient
=
e
.
HttpClient
,
ClientEndPoint
=
e
.
ClientEndPoint
,
IsTunnelConnect
=
isTunnelConnect
...
...
@@ -236,7 +240,8 @@ namespace Titanium.Web.Proxy.Examples.Wpf
var
session
=
(
SessionEventArgsBase
)
sender
;
if
(
sessionDictionary
.
TryGetValue
(
session
.
HttpClient
,
out
var
li
))
{
var
tunnelType
=
session
.
HttpClient
.
ConnectRequest
?.
TunnelType
??
TunnelType
.
Unknown
;
var
connectRequest
=
session
.
HttpClient
.
ConnectRequest
;
var
tunnelType
=
connectRequest
?.
TunnelType
??
TunnelType
.
Unknown
;
if
(
tunnelType
!=
TunnelType
.
Unknown
)
{
li
.
Protocol
=
TunnelTypeToString
(
tunnelType
);
...
...
@@ -244,6 +249,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf
li
.
ReceivedDataCount
+=
args
.
Count
;
//if (tunnelType == TunnelType.Http2)
AppendTransferLog
(
session
.
GetHashCode
()
+
(
isTunnelConnect
?
"_tunnel"
:
""
)
+
"_received"
,
args
.
Buffer
,
args
.
Offset
,
args
.
Count
);
}
...
...
@@ -254,7 +260,8 @@ namespace Titanium.Web.Proxy.Examples.Wpf
var
session
=
(
SessionEventArgsBase
)
sender
;
if
(
sessionDictionary
.
TryGetValue
(
session
.
HttpClient
,
out
var
li
))
{
var
tunnelType
=
session
.
HttpClient
.
ConnectRequest
?.
TunnelType
??
TunnelType
.
Unknown
;
var
connectRequest
=
session
.
HttpClient
.
ConnectRequest
;
var
tunnelType
=
connectRequest
?.
TunnelType
??
TunnelType
.
Unknown
;
if
(
tunnelType
!=
TunnelType
.
Unknown
)
{
li
.
Protocol
=
TunnelTypeToString
(
tunnelType
);
...
...
@@ -262,7 +269,8 @@ namespace Titanium.Web.Proxy.Examples.Wpf
li
.
SentDataCount
+=
args
.
Count
;
AppendTransferLog
(
session
.
GetHashCode
()
+
(
isTunnelConnect
?
"_tunnel"
:
""
)
+
"_sent"
,
//if (tunnelType == TunnelType.Http2)
AppendTransferLog
(
session
.
GetHashCode
()
+
(
isTunnelConnect
?
"_tunnel"
:
""
)
+
"_sent"
,
args
.
Buffer
,
args
.
Offset
,
args
.
Count
);
}
};
...
...
@@ -272,6 +280,8 @@ namespace Titanium.Web.Proxy.Examples.Wpf
te
.
DecryptedDataReceived
+=
(
sender
,
args
)
=>
{
var
session
=
(
SessionEventArgsBase
)
sender
;
//var tunnelType = session.HttpClient.ConnectRequest?.TunnelType ?? TunnelType.Unknown;
//if (tunnelType == TunnelType.Http2)
AppendTransferLog
(
session
.
GetHashCode
()
+
"_decrypted_received"
,
args
.
Buffer
,
args
.
Offset
,
args
.
Count
);
};
...
...
@@ -279,11 +289,13 @@ namespace Titanium.Web.Proxy.Examples.Wpf
te
.
DecryptedDataSent
+=
(
sender
,
args
)
=>
{
var
session
=
(
SessionEventArgsBase
)
sender
;
//var tunnelType = session.HttpClient.ConnectRequest?.TunnelType ?? TunnelType.Unknown;
//if (tunnelType == TunnelType.Http2)
AppendTransferLog
(
session
.
GetHashCode
()
+
"_decrypted_sent"
,
args
.
Buffer
,
args
.
Offset
,
args
.
Count
);
};
}
item
.
Update
();
item
.
Update
(
e
);
return
item
;
}
...
...
@@ -362,9 +374,15 @@ namespace Titanium.Web.Proxy.Examples.Wpf
//string hexStr = string.Join(" ", data.Select(x => x.ToString("X2")));
var
sb
=
new
StringBuilder
();
sb
.
AppendLine
(
"URI: "
+
request
.
RequestUri
);
sb
.
Append
(
request
.
HeaderText
);
sb
.
Append
(
request
.
Encoding
.
GetString
(
data
));
sb
.
Append
(
truncated
?
Environment
.
NewLine
+
$"Data is truncated after
{
truncateLimit
}
bytes"
:
null
);
if
(
truncated
)
{
sb
.
AppendLine
();
sb
.
Append
(
$"Data is truncated after
{
truncateLimit
}
bytes"
);
}
sb
.
Append
((
request
as
ConnectRequest
)?.
ClientHelloInfo
);
TextBoxRequest
.
Text
=
sb
.
ToString
();
...
...
@@ -381,7 +399,12 @@ namespace Titanium.Web.Proxy.Examples.Wpf
sb
=
new
StringBuilder
();
sb
.
Append
(
response
.
HeaderText
);
sb
.
Append
(
response
.
Encoding
.
GetString
(
data
));
sb
.
Append
(
truncated
?
Environment
.
NewLine
+
$"Data is truncated after
{
truncateLimit
}
bytes"
:
null
);
if
(
truncated
)
{
sb
.
AppendLine
();
sb
.
Append
(
$"Data is truncated after
{
truncateLimit
}
bytes"
);
}
sb
.
Append
((
response
as
ConnectResponse
)?.
ServerHelloInfo
);
if
(
SelectedSession
.
Exception
!=
null
)
{
...
...
examples/Titanium.Web.Proxy.Examples.Wpf/SessionListItem.cs
View file @
b584af90
...
...
@@ -2,6 +2,7 @@
using
System.ComponentModel
;
using
System.Net
;
using
System.Runtime.CompilerServices
;
using
Titanium.Web.Proxy.EventArguments
;
using
Titanium.Web.Proxy.Examples.Wpf.Annotations
;
using
Titanium.Web.Proxy.Http
;
...
...
@@ -18,9 +19,23 @@ namespace Titanium.Web.Proxy.Examples.Wpf
private
long
sentDataCount
;
private
string
statusCode
;
private
string
url
;
private
Guid
clientConnectionId
;
private
Guid
serverConnectionId
;
public
int
Number
{
get
;
set
;
}
public
Guid
ClientConnectionId
{
get
=>
clientConnectionId
;
set
=>
SetField
(
ref
clientConnectionId
,
value
);
}
public
Guid
ServerConnectionId
{
get
=>
serverConnectionId
;
set
=>
SetField
(
ref
serverConnectionId
,
value
);
}
public
HttpWebClient
HttpClient
{
get
;
set
;
}
public
IPEndPoint
ClientEndPoint
{
get
;
set
;
}
...
...
@@ -123,13 +138,15 @@ namespace Titanium.Web.Proxy.Examples.Wpf
PropertyChanged
?.
Invoke
(
this
,
new
PropertyChangedEventArgs
(
propertyName
));
}
public
void
Update
()
public
void
Update
(
SessionEventArgsBase
args
)
{
var
request
=
HttpClient
.
Request
;
var
response
=
HttpClient
.
Response
;
int
statusCode
=
response
?.
StatusCode
??
0
;
StatusCode
=
statusCode
==
0
?
"-"
:
statusCode
.
ToString
();
Protocol
=
request
.
RequestUri
.
Scheme
;
ClientConnectionId
=
args
.
ClientConnectionId
;
ServerConnectionId
=
args
.
ServerConnectionId
;
if
(
IsTunnelConnect
)
{
...
...
src/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs
View file @
b584af90
...
...
@@ -65,11 +65,6 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary>
public
event
EventHandler
<
MultipartRequestPartSentEventArgs
>?
MultipartRequestPartSent
;
private
HttpStream
getStream
(
bool
isRequest
)
{
return
isRequest
?
(
HttpStream
)
ClientStream
:
HttpClient
.
Connection
.
Stream
;
}
/// <summary>
/// Read request body content as bytes[] for current session
/// </summary>
...
...
@@ -194,15 +189,15 @@ namespace Titanium.Web.Proxy.EventArguments
private
async
Task
<
byte
[
]>
readBodyAsync
(
bool
isRequest
,
CancellationToken
cancellationToken
)
{
using
var
bodyStream
=
new
MemoryStream
();
using
var
http
=
new
HttpStream
(
bodyStream
,
BufferPool
);
using
var
writer
=
new
HttpStream
(
bodyStream
,
BufferPool
);
if
(
isRequest
)
{
await
CopyRequestBodyAsync
(
http
,
TransformationMode
.
Uncompress
,
cancellationToken
);
await
CopyRequestBodyAsync
(
writer
,
TransformationMode
.
Uncompress
,
cancellationToken
);
}
else
{
await
CopyResponseBodyAsync
(
http
,
TransformationMode
.
Uncompress
,
cancellationToken
);
await
copyResponseBodyAsync
(
writer
,
TransformationMode
.
Uncompress
,
cancellationToken
);
}
return
bodyStream
.
ToArray
();
...
...
@@ -223,9 +218,9 @@ namespace Titanium.Web.Proxy.EventArguments
return
;
}
using
var
bodyStream
=
new
MemoryStream
()
;
using
var
http
=
new
HttpStream
(
bodyStream
,
BufferPool
);
await
copyBodyAsync
(
isRequest
,
true
,
http
,
TransformationMode
.
None
,
null
,
cancellationToken
);
var
reader
=
isRequest
?
(
HttpStream
)
ClientStream
:
HttpClient
.
Connection
.
Stream
;
await
reader
.
CopyBodyAsync
(
requestResponse
,
true
,
new
NullWriter
()
,
TransformationMode
.
None
,
null
,
cancellationToken
);
}
/// <summary>
...
...
@@ -235,13 +230,13 @@ namespace Titanium.Web.Proxy.EventArguments
internal
async
Task
CopyRequestBodyAsync
(
IHttpStreamWriter
writer
,
TransformationMode
transformation
,
CancellationToken
cancellationToken
)
{
var
request
=
HttpClient
.
Request
;
var
reader
=
ClientStream
;
long
contentLength
=
request
.
ContentLength
;
// send the request body bytes to server
if
(
contentLength
>
0
&&
hasMulipartEventSubscribers
&&
request
.
IsMultipartFormData
)
{
var
reader
=
getStream
(
true
);
var
boundary
=
HttpHelper
.
GetBoundaryFromContentType
(
request
.
ContentType
);
using
(
var
copyStream
=
new
CopyStream
(
reader
,
writer
,
BufferPool
))
...
...
@@ -267,54 +262,13 @@ namespace Titanium.Web.Proxy.EventArguments
}
else
{
await
copyBodyAsync
(
true
,
false
,
writer
,
transformation
,
OnDataSent
,
cancellationToken
);
await
reader
.
CopyBodyAsync
(
request
,
false
,
writer
,
transformation
,
OnDataSent
,
cancellationToken
);
}
}
internal
async
Task
CopyResponseBodyAsync
(
IHttpStreamWriter
writer
,
TransformationMode
transformation
,
CancellationToken
cancellationToken
)
{
await
copyBodyAsync
(
false
,
false
,
writer
,
transformation
,
OnDataReceived
,
cancellationToken
);
}
private
async
Task
copyBodyAsync
(
bool
isRequest
,
bool
useOriginalHeaderValues
,
IHttpStreamWriter
writer
,
TransformationMode
transformation
,
Action
<
byte
[],
int
,
int
>?
onCopy
,
CancellationToken
cancellationToken
)
private
async
Task
copyResponseBodyAsync
(
IHttpStreamWriter
writer
,
TransformationMode
transformation
,
CancellationToken
cancellationToken
)
{
var
stream
=
getStream
(
isRequest
);
var
requestResponse
=
isRequest
?
(
RequestResponseBase
)
HttpClient
.
Request
:
HttpClient
.
Response
;
bool
isChunked
=
useOriginalHeaderValues
?
requestResponse
.
OriginalIsChunked
:
requestResponse
.
IsChunked
;
long
contentLength
=
useOriginalHeaderValues
?
requestResponse
.
OriginalContentLength
:
requestResponse
.
ContentLength
;
if
(
transformation
==
TransformationMode
.
None
)
{
await
writer
.
CopyBodyAsync
(
stream
,
isChunked
,
contentLength
,
onCopy
,
cancellationToken
);
return
;
}
LimitedStream
limitedStream
;
Stream
?
decompressStream
=
null
;
string
?
contentEncoding
=
useOriginalHeaderValues
?
requestResponse
.
OriginalContentEncoding
:
requestResponse
.
ContentEncoding
;
Stream
s
=
limitedStream
=
new
LimitedStream
(
stream
,
BufferPool
,
isChunked
,
contentLength
);
if
(
transformation
==
TransformationMode
.
Uncompress
&&
contentEncoding
!=
null
)
{
s
=
decompressStream
=
DecompressionFactory
.
Create
(
CompressionUtil
.
CompressionNameToEnum
(
contentEncoding
),
s
);
}
try
{
var
http
=
new
HttpStream
(
s
,
BufferPool
,
true
);
await
writer
.
CopyBodyAsync
(
http
,
false
,
-
1
,
onCopy
,
cancellationToken
);
}
finally
{
decompressStream
?.
Dispose
();
await
limitedStream
.
Finish
();
limitedStream
.
Dispose
();
}
await
HttpClient
.
Connection
.
Stream
.
CopyBodyAsync
(
HttpClient
.
Response
,
false
,
writer
,
transformation
,
OnDataReceived
,
cancellationToken
);
}
/// <summary>
...
...
@@ -638,7 +592,6 @@ namespace Titanium.Web.Proxy.EventArguments
HttpClient
.
Response
=
response
;
HttpClient
.
Response
.
Locked
=
true
;
}
}
/// <summary>
...
...
src/Titanium.Web.Proxy/EventArguments/SessionEventArgsBase.cs
View file @
b584af90
...
...
@@ -33,6 +33,10 @@ namespace Titanium.Web.Proxy.EventArguments
internal
HttpClientStream
ClientStream
{
get
;
}
public
Guid
ClientConnectionId
=>
ClientConnection
.
Id
;
public
Guid
ServerConnectionId
=>
HttpClient
.
HasConnection
?
ServerConnection
.
Id
:
Guid
.
Empty
;
protected
readonly
IBufferPool
BufferPool
;
protected
readonly
ExceptionHandler
ExceptionFunc
;
private
bool
enableWinAuth
;
...
...
src/Titanium.Web.Proxy/ExplicitClientHandler.cs
View file @
b584af90
...
...
@@ -58,11 +58,11 @@ namespace Titanium.Web.Proxy
return
;
}
Request
.
ParseRequestLine
(
httpCmd
!,
out
string
_
,
out
string
httpUrl
,
out
var
version
);
Request
.
ParseRequestLine
(
httpCmd
!,
out
string
_
,
out
var
httpUrl
,
out
var
version
);
var
connectRequest
=
new
ConnectRequest
(
httpUrl
)
var
connectRequest
=
new
ConnectRequest
(
httpUrl
.
GetString
()
)
{
OriginalUrlData
=
HttpHeader
.
Encoding
.
GetBytes
(
httpUrl
)
,
RequestUriString8
=
httpUrl
,
HttpVersion
=
version
};
...
...
@@ -91,8 +91,7 @@ namespace Titanium.Web.Proxy
}
// send the response
await
clientStream
.
WriteResponseAsync
(
connectArgs
.
HttpClient
.
Response
,
cancellationToken
:
cancellationToken
);
await
clientStream
.
WriteResponseAsync
(
connectArgs
.
HttpClient
.
Response
,
cancellationToken
);
return
;
}
...
...
@@ -101,8 +100,7 @@ namespace Titanium.Web.Proxy
await
endPoint
.
InvokeBeforeTunnelConnectResponse
(
this
,
connectArgs
,
ExceptionFunc
);
// send the response
await
clientStream
.
WriteResponseAsync
(
connectArgs
.
HttpClient
.
Response
,
cancellationToken
:
cancellationToken
);
await
clientStream
.
WriteResponseAsync
(
connectArgs
.
HttpClient
.
Response
,
cancellationToken
);
return
;
}
...
...
@@ -114,7 +112,7 @@ namespace Titanium.Web.Proxy
response
.
Headers
.
FixProxyHeaders
();
connectArgs
.
HttpClient
.
Response
=
response
;
await
clientStream
.
WriteResponseAsync
(
response
,
cancellationToken
:
cancellationToken
);
await
clientStream
.
WriteResponseAsync
(
response
,
cancellationToken
);
var
clientHelloInfo
=
await
SslTools
.
PeekClientHello
(
clientStream
,
BufferPool
,
cancellationToken
);
...
...
@@ -177,7 +175,7 @@ namespace Titanium.Web.Proxy
}
}
string
connectHostname
=
httpUrl
;
string
connectHostname
=
httpUrl
.
GetString
()
;
int
idx
=
connectHostname
.
IndexOf
(
":"
);
if
(
idx
>=
0
)
{
...
...
src/Titanium.Web.Proxy/Helpers/HttpClientStream.cs
View file @
b584af90
...
...
@@ -20,10 +20,13 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="response">The response object.</param>
/// <param name="cancellationToken">Optional cancellation token for this async task.</param>
/// <returns>The Task.</returns>
internal
async
Task
WriteResponseAsync
(
Response
response
,
CancellationToken
cancellationToken
=
default
)
internal
async
Value
Task
WriteResponseAsync
(
Response
response
,
CancellationToken
cancellationToken
=
default
)
{
var
headerBuilder
=
new
HeaderBuilder
();
// Write back response status to client
headerBuilder
.
WriteResponseLine
(
response
.
HttpVersion
,
response
.
StatusCode
,
response
.
StatusDescription
);
await
WriteAsync
(
response
,
headerBuilder
,
cancellationToken
);
}
}
...
...
src/Titanium.Web.Proxy/Helpers/HttpHelper.cs
View file @
b584af90
...
...
@@ -170,7 +170,7 @@ namespace Titanium.Web.Proxy.Helpers
/// Determines whether is connect method.
/// </summary>
/// <returns>1: when CONNECT, 0: when valid HTTP method, -1: otherwise</returns>
internal
static
Task
<
int
>
IsConnectMethod
(
IPeekStream
httpReader
,
IBufferPool
bufferPool
,
CancellationToken
cancellationToken
=
default
)
internal
static
Value
Task
<
int
>
IsConnectMethod
(
IPeekStream
httpReader
,
IBufferPool
bufferPool
,
CancellationToken
cancellationToken
=
default
)
{
return
startsWith
(
httpReader
,
bufferPool
,
"CONNECT"
,
cancellationToken
);
}
...
...
@@ -179,7 +179,7 @@ namespace Titanium.Web.Proxy.Helpers
/// Determines whether is pri method (HTTP/2).
/// </summary>
/// <returns>1: when PRI, 0: when valid HTTP method, -1: otherwise</returns>
internal
static
Task
<
int
>
IsPriMethod
(
IPeekStream
httpReader
,
IBufferPool
bufferPool
,
CancellationToken
cancellationToken
=
default
)
internal
static
Value
Task
<
int
>
IsPriMethod
(
IPeekStream
httpReader
,
IBufferPool
bufferPool
,
CancellationToken
cancellationToken
=
default
)
{
return
startsWith
(
httpReader
,
bufferPool
,
"PRI"
,
cancellationToken
);
}
...
...
@@ -190,7 +190,7 @@ namespace Titanium.Web.Proxy.Helpers
/// <returns>
/// 1: when starts with the given string, 0: when valid HTTP method, -1: otherwise
/// </returns>
private
static
async
Task
<
int
>
startsWith
(
IPeekStream
httpReader
,
IBufferPool
bufferPool
,
string
expectedStart
,
CancellationToken
cancellationToken
=
default
)
private
static
async
Value
Task
<
int
>
startsWith
(
IPeekStream
httpReader
,
IBufferPool
bufferPool
,
string
expectedStart
,
CancellationToken
cancellationToken
=
default
)
{
const
int
lengthToCheck
=
10
;
if
(
bufferPool
.
BufferSize
<
lengthToCheck
)
...
...
src/Titanium.Web.Proxy/Helpers/HttpServerStream.cs
View file @
b584af90
using
System.IO
;
using
System
;
using
System.IO
;
using
System.Threading
;
using
System.Threading.Tasks
;
using
Titanium.Web.Proxy.Exceptions
;
using
Titanium.Web.Proxy.Http
;
using
Titanium.Web.Proxy.StreamExtended.BufferPool
;
...
...
@@ -8,7 +10,7 @@ namespace Titanium.Web.Proxy.Helpers
{
internal
sealed
class
HttpServerStream
:
HttpStream
{
internal
HttpServerStream
(
Stream
stream
,
IBufferPool
bufferPool
)
internal
HttpServerStream
(
Stream
stream
,
IBufferPool
bufferPool
)
:
base
(
stream
,
bufferPool
)
{
}
...
...
@@ -19,11 +21,34 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="request">The request object.</param>
/// <param name="cancellationToken">Optional cancellation token for this async task.</param>
/// <returns></returns>
internal
async
Task
WriteRequestAsync
(
Request
request
,
CancellationToken
cancellationToken
=
default
)
internal
async
Value
Task
WriteRequestAsync
(
Request
request
,
CancellationToken
cancellationToken
=
default
)
{
var
headerBuilder
=
new
HeaderBuilder
();
headerBuilder
.
WriteRequestLine
(
request
.
Method
,
request
.
Url
,
request
.
HttpVersion
);
headerBuilder
.
WriteRequestLine
(
request
.
Method
,
request
.
RequestUriString
,
request
.
HttpVersion
);
await
WriteAsync
(
request
,
headerBuilder
,
cancellationToken
);
}
internal
async
ValueTask
<
ResponseStatusInfo
>
ReadResponseStatus
(
CancellationToken
cancellationToken
=
default
)
{
try
{
string
httpStatus
=
await
ReadLineAsync
(
cancellationToken
)
??
throw
new
ServerConnectionException
(
"Server connection was closed."
);
if
(
httpStatus
==
string
.
Empty
)
{
// is this really possible?
httpStatus
=
await
ReadLineAsync
(
cancellationToken
)
??
throw
new
ServerConnectionException
(
"Server connection was closed."
);
}
Response
.
ParseResponseLine
(
httpStatus
,
out
var
version
,
out
int
statusCode
,
out
string
description
);
return
new
ResponseStatusInfo
{
Version
=
version
,
StatusCode
=
statusCode
,
Description
=
description
};
}
catch
(
Exception
e
)
when
(!(
e
is
ServerConnectionException
))
{
throw
new
ServerConnectionException
(
"Server connection was closed."
);
}
}
}
}
src/Titanium.Web.Proxy/Helpers/HttpStream.cs
View file @
b584af90
This diff is collapsed.
Click to expand it.
src/Titanium.Web.Proxy/Helpers/NullWriter.cs
0 → 100644
View file @
b584af90
using
System.Threading
;
using
System.Threading.Tasks
;
using
Titanium.Web.Proxy.StreamExtended.Network
;
internal
class
NullWriter
:
IHttpStreamWriter
{
public
static
NullWriter
Instance
{
get
;
}
=
new
NullWriter
();
public
void
Write
(
byte
[]
buffer
,
int
offset
,
int
count
)
{
}
#if NET45
public
async
Task
WriteAsync
(
byte
[]
buffer
,
int
offset
,
int
count
,
CancellationToken
cancellationToken
)
{
}
#else
public
Task
WriteAsync
(
byte
[]
buffer
,
int
offset
,
int
count
,
CancellationToken
cancellationToken
)
{
return
Task
.
CompletedTask
;
}
#endif
public
ValueTask
WriteLineAsync
(
CancellationToken
cancellationToken
=
default
)
{
throw
new
System
.
NotImplementedException
();
}
public
ValueTask
WriteLineAsync
(
string
value
,
CancellationToken
cancellationToken
=
default
)
{
throw
new
System
.
NotImplementedException
();
}
}
src/Titanium.Web.Proxy/Helpers/ResponseStatusInfo.cs
0 → 100644
View file @
b584af90
using
System
;
namespace
Titanium.Web.Proxy.Helpers
{
struct
ResponseStatusInfo
{
public
Version
Version
{
get
;
set
;
}
public
int
StatusCode
{
get
;
set
;
}
public
string
Description
{
get
;
set
;
}
}
}
src/Titanium.Web.Proxy/Http/HeaderBuilder.cs
View file @
b584af90
...
...
@@ -41,11 +41,21 @@ namespace Titanium.Web.Proxy.Http
WriteLine
();
}
public
void
WriteHeaders
(
HeaderCollection
headers
)
public
void
WriteHeaders
(
HeaderCollection
headers
,
bool
sendProxyAuthorization
=
true
,
string
?
upstreamProxyUserName
=
null
,
string
?
upstreamProxyPassword
=
null
)
{
if
(
upstreamProxyUserName
!=
null
&&
upstreamProxyPassword
!=
null
)
{
WriteHeader
(
HttpHeader
.
ProxyConnectionKeepAlive
);
WriteHeader
(
HttpHeader
.
GetProxyAuthorizationHeader
(
upstreamProxyUserName
,
upstreamProxyPassword
));
}
foreach
(
var
header
in
headers
)
{
WriteHeader
(
header
);
if
(
sendProxyAuthorization
||
!
KnownHeaders
.
ProxyAuthorization
.
Equals
(
header
.
Name
))
{
WriteHeader
(
header
);
}
}
WriteLine
();
...
...
src/Titanium.Web.Proxy/Http/HeaderParser.cs
View file @
b584af90
...
...
@@ -7,7 +7,7 @@ namespace Titanium.Web.Proxy.Http
{
internal
static
class
HeaderParser
{
internal
static
async
Task
ReadHeaders
(
ILineStream
reader
,
HeaderCollection
headerCollection
,
internal
static
async
Value
Task
ReadHeaders
(
ILineStream
reader
,
HeaderCollection
headerCollection
,
CancellationToken
cancellationToken
)
{
string
?
tmpLine
;
...
...
src/Titanium.Web.Proxy/Http/HttpWebClient.cs
View file @
b584af90
...
...
@@ -103,9 +103,8 @@ namespace Titanium.Web.Proxy.Http
/// <summary>
/// Prepare and send the http(s) request
/// </summary>
/// <returns></returns>
internal
async
Task
SendRequest
(
bool
enable100ContinueBehaviour
,
bool
isTransparent
,
CancellationToken
cancellationToken
)
/// <returns></returns>
internal
async
Task
SendRequest
(
bool
enable100ContinueBehaviour
,
bool
isTransparent
,
CancellationToken
cancellationToken
)
{
var
upstreamProxy
=
Connection
.
UpStreamProxy
;
...
...
@@ -114,23 +113,17 @@ namespace Titanium.Web.Proxy.Http
var
serverStream
=
Connection
.
Stream
;
string
url
;
if
(
useUpstreamProxy
||
isTransparent
)
if
(
!
useUpstreamProxy
||
isTransparent
)
{
url
=
Request
.
Url
;
url
=
Request
.
RequestUriString
;
}
else
{
url
=
Request
.
RequestUri
.
GetOriginalPathAndQuery
();
if
(
url
==
string
.
Empty
)
{
url
=
"/"
;
}
url
=
Request
.
RequestUri
.
ToString
();
}
var
headerBuilder
=
new
HeaderBuilder
();
// prepare the request & headers
headerBuilder
.
WriteRequestLine
(
Request
.
Method
,
url
,
Request
.
HttpVersion
);
string
?
upstreamProxyUserName
=
null
;
string
?
upstreamProxyPassword
=
null
;
// Send Authentication to Upstream proxy if needed
if
(!
isTransparent
&&
upstreamProxy
!=
null
...
...
@@ -138,21 +131,16 @@ namespace Titanium.Web.Proxy.Http
&&
!
string
.
IsNullOrEmpty
(
upstreamProxy
.
UserName
)
&&
upstreamProxy
.
Password
!=
null
)
{
headerBuilder
.
WriteHeader
(
HttpHeader
.
ProxyConnectionKeepAlive
);
headerBuilder
.
WriteHeader
(
HttpHeader
.
GetProxyAuthorizationHeader
(
upstreamProxy
.
UserName
,
upstreamProxy
.
Password
));
}
// write request headers
foreach
(
var
header
in
Request
.
Headers
)
{
if
(
isTransparent
||
header
.
Name
!=
KnownHeaders
.
ProxyAuthorization
.
String
)
{
headerBuilder
.
WriteHeader
(
header
);
}
upstreamProxyUserName
=
upstreamProxy
.
UserName
;
upstreamProxyPassword
=
upstreamProxy
.
Password
;
}
headerBuilder
.
WriteLine
();
// prepare the request & headers
var
headerBuilder
=
new
HeaderBuilder
();
headerBuilder
.
WriteRequestLine
(
Request
.
Method
,
url
,
Request
.
HttpVersion
);
headerBuilder
.
WriteHeaders
(
Request
.
Headers
,
!
isTransparent
,
upstreamProxyUserName
,
upstreamProxyPassword
);
// write request headers
await
serverStream
.
WriteHeadersAsync
(
headerBuilder
,
cancellationToken
);
if
(
enable100ContinueBehaviour
&&
Request
.
ExpectContinue
)
...
...
@@ -183,28 +171,10 @@ namespace Titanium.Web.Proxy.Http
return
;
}
string
httpStatus
;
try
{
httpStatus
=
await
Connection
.
Stream
.
ReadLineAsync
(
cancellationToken
)
??
throw
new
ServerConnectionException
(
"Server connection was closed."
);
}
catch
(
Exception
e
)
when
(!(
e
is
ServerConnectionException
))
{
throw
new
ServerConnectionException
(
"Server connection was closed."
);
}
if
(
httpStatus
==
string
.
Empty
)
{
httpStatus
=
await
Connection
.
Stream
.
ReadLineAsync
(
cancellationToken
)
??
throw
new
ServerConnectionException
(
"Server connection was closed."
);
}
Response
.
ParseResponseLine
(
httpStatus
,
out
var
version
,
out
int
statusCode
,
out
string
statusDescription
);
Response
.
HttpVersion
=
version
;
Response
.
StatusCode
=
statusCode
;
Response
.
StatusDescription
=
statusDescription
;
var
httpStatus
=
await
Connection
.
Stream
.
ReadResponseStatus
(
cancellationToken
);
Response
.
HttpVersion
=
httpStatus
.
Version
;
Response
.
StatusCode
=
httpStatus
.
StatusCode
;
Response
.
StatusDescription
=
httpStatus
.
Description
;
// Read the response headers in to unique and non-unique header collections
await
HeaderParser
.
ReadHeaders
(
Connection
.
Stream
,
Response
.
Headers
,
cancellationToken
);
...
...
src/Titanium.Web.Proxy/Http/Request.cs
View file @
b584af90
...
...
@@ -24,26 +24,15 @@ namespace Titanium.Web.Proxy.Http
/// </summary>
public
bool
IsHttps
{
get
;
internal
set
;
}
private
ByteString
originalUrlData
;
private
ByteString
urlData
;
private
ByteString
requestUriString8
;
internal
ByteString
OriginalUrlData
internal
ByteString
RequestUriString8
{
get
=>
originalUrlData
;
get
=>
requestUriString8
;
set
{
originalUrlData
=
value
;
UrlData
=
value
;
}
}
private
protected
ByteString
UrlData
{
get
=>
urlData
;
set
{
urlData
=
value
;
var
scheme
=
getUriScheme
(
UrlData
);
requestUriString8
=
value
;
var
scheme
=
getUriScheme
(
value
);
if
(
scheme
.
Length
>
0
)
{
IsHttps
=
scheme
.
Equals
(
ProxyServer
.
UriSchemeHttps8
);
...
...
@@ -54,19 +43,37 @@ namespace Titanium.Web.Proxy.Http
internal
string
?
Authority
{
get
;
set
;
}
/// <summary>
///
The original request Url
.
///
Request HTTP Uri
.
/// </summary>
public
string
OriginalUrl
=>
originalUrlData
.
GetString
();
public
Uri
RequestUri
{
get
{
string
url
=
Url
;
try
{
return
new
Uri
(
url
);
}
catch
(
Exception
ex
)
{
throw
new
Exception
(
$"Invalid URI: '
{
url
}
'"
,
ex
);
}
}
set
{
Url
=
value
.
ToString
();
}
}
/// <summary>
///
Request HTTP Uri.
///
The request url as it is in the HTTP header
/// </summary>
public
Uri
RequestUri
public
string
Url
{
get
{
string
url
=
UrlData
.
GetString
();
if
(
getUriScheme
(
UrlData
).
Length
==
0
)
string
url
=
RequestUriString8
.
GetString
();
if
(
getUriScheme
(
RequestUriString8
).
Length
==
0
)
{
string
?
hostAndPath
=
Host
??
Authority
;
...
...
@@ -82,28 +89,26 @@ namespace Titanium.Web.Proxy.Http
url
=
string
.
Concat
(
IsHttps
?
"https://"
:
"http://"
,
hostAndPath
);
}
try
{
return
new
Uri
(
url
);
}
catch
(
Exception
ex
)
{
throw
new
Exception
(
$"Invalid URI: '
{
url
}
'"
,
ex
);
}
return
url
;
}
set
{
RequestUriString
=
value
;
}
}
/// <summary>
/// The request ur
l
as it is in the HTTP header
/// The request ur
i
as it is in the HTTP header
/// </summary>
public
string
Url
public
string
RequestUriString
{
get
=>
UrlData
.
GetString
();
get
=>
RequestUriString8
.
GetString
();
set
{
UrlData
=
value
.
GetByteString
()
;
RequestUriString8
=
(
ByteString
)
value
;
if
(
Host
!=
null
)
var
scheme
=
getUriScheme
(
RequestUriString8
);
if
(
scheme
.
Length
>
0
&&
Host
!=
null
)
{
var
uri
=
new
Uri
(
value
);
Host
=
uri
.
Authority
;
...
...
@@ -111,13 +116,6 @@ namespace Titanium.Web.Proxy.Http
}
}
[
Obsolete
(
"This property is obsolete. Use Url property instead"
)]
public
string
RequestUriString
{
get
=>
Url
;
set
=>
Url
=
value
;
}
/// <summary>
/// Has request body?
/// </summary>
...
...
@@ -219,7 +217,7 @@ namespace Titanium.Web.Proxy.Http
get
{
var
headerBuilder
=
new
HeaderBuilder
();
headerBuilder
.
WriteRequestLine
(
Method
,
Url
,
HttpVersion
);
headerBuilder
.
WriteRequestLine
(
Method
,
RequestUriString
,
HttpVersion
);
headerBuilder
.
WriteHeaders
(
Headers
);
return
headerBuilder
.
GetString
(
HttpHeader
.
Encoding
);
}
...
...
@@ -256,7 +254,7 @@ namespace Titanium.Web.Proxy.Http
}
}
internal
static
void
ParseRequestLine
(
string
httpCmd
,
out
string
httpMethod
,
out
s
tring
httpUrl
,
internal
static
void
ParseRequestLine
(
string
httpCmd
,
out
string
httpMethod
,
out
ByteS
tring
httpUrl
,
out
Version
version
)
{
int
firstSpace
=
httpCmd
.
IndexOf
(
' '
);
...
...
@@ -281,11 +279,11 @@ namespace Titanium.Web.Proxy.Http
if
(
firstSpace
==
lastSpace
)
{
httpUrl
=
httpCmd
.
AsSpan
(
firstSpace
+
1
).
ToString
();
httpUrl
=
(
ByteString
)
httpCmd
.
AsSpan
(
firstSpace
+
1
).
ToString
();
}
else
{
httpUrl
=
httpCmd
.
AsSpan
(
firstSpace
+
1
,
lastSpace
-
firstSpace
-
1
).
ToString
();
httpUrl
=
(
ByteString
)
httpCmd
.
AsSpan
(
firstSpace
+
1
,
lastSpace
-
firstSpace
-
1
).
ToString
();
// parse the HTTP version
var
httpVersion
=
httpCmd
.
AsSpan
(
lastSpace
+
1
);
...
...
src/Titanium.Web.Proxy/Http/RequestResponseBase.cs
View file @
b584af90
...
...
@@ -210,6 +210,8 @@ namespace Titanium.Web.Proxy.Http
internal
bool
Locked
{
get
;
set
;
}
internal
bool
BodyAvailable
=>
BodyInternal
!=
null
;
internal
bool
IsBodySent
{
get
;
set
;
}
internal
abstract
void
EnsureBodyAvailable
(
bool
throwWhenNotReadYet
=
true
);
...
...
src/Titanium.Web.Proxy/Http2/Http2Helper.cs
View file @
b584af90
...
...
@@ -265,7 +265,7 @@ namespace Titanium.Web.Proxy.Http2
request
.
Method
=
method
.
GetString
();
request
.
IsHttps
=
headerListener
.
Scheme
==
ProxyServer
.
UriSchemeHttps
;
request
.
Authority
=
headerListener
.
Authority
.
GetString
();
request
.
OriginalUrlData
=
path
;
request
.
RequestUriString8
=
path
;
//request.RequestUri = headerListener.GetUri();
}
...
...
src/Titanium.Web.Proxy/Network/RetryPolicy.cs
View file @
b584af90
...
...
@@ -38,8 +38,8 @@ namespace Titanium.Web.Proxy.Network
try
{
// setup connection
currentConnection
=
currentConnection
as
TcpServerConnection
??
await
generator
();
currentConnection
??=
await
generator
();
// try
@continue
=
await
action
(
currentConnection
);
...
...
src/Titanium.Web.Proxy/Network/Tcp/TcpConnectionFactory.cs
View file @
b584af90
...
...
@@ -281,21 +281,25 @@ namespace Titanium.Web.Proxy.Network.Tcp
}
}
bool
useUpstreamProxy
=
false
;
bool
useUpstreamProxy
1
=
false
;
// check if external proxy is set for HTTP/HTTPS
if
(
externalProxy
!=
null
&&
!(
externalProxy
.
HostName
==
remoteHostName
&&
externalProxy
.
Port
==
remotePort
))
if
(
externalProxy
!=
null
&&
!(
externalProxy
.
HostName
==
remoteHostName
&&
externalProxy
.
Port
==
remotePort
))
{
useUpstreamProxy
=
true
;
useUpstreamProxy
1
=
true
;
// check if we need to ByPass
if
(
externalProxy
.
BypassLocalhost
&&
NetworkHelper
.
IsLocalIpAddress
(
remoteHostName
))
{
useUpstreamProxy
=
false
;
useUpstreamProxy
1
=
false
;
}
}
if
(!
useUpstreamProxy1
)
{
externalProxy
=
null
;
}
TcpClient
?
tcpClient
=
null
;
HttpServerStream
?
stream
=
null
;
...
...
@@ -307,8 +311,8 @@ namespace Titanium.Web.Proxy.Network.Tcp
retry
:
try
{
string
hostname
=
useUpstreamProxy
?
externalProxy
!
.
HostName
:
remoteHostName
;
int
port
=
useUpstreamProxy
?
externalProxy
!.
Port
:
remotePort
;
string
hostname
=
externalProxy
!=
null
?
externalProxy
.
HostName
:
remoteHostName
;
int
port
=
externalProxy
?.
Port
??
remotePort
;
var
ipAddresses
=
await
Dns
.
GetHostAddressesAsync
(
hostname
);
if
(
ipAddresses
==
null
||
ipAddresses
.
Length
==
0
)
...
...
@@ -358,7 +362,6 @@ retry:
try
{
connectTask
.
Dispose
();
}
catch
{
...
...
@@ -397,6 +400,17 @@ retry:
if
(
tcpClient
==
null
)
{
if
(
session
!=
null
&&
proxyServer
.
CustomUpStreamProxyFailureFunc
!=
null
)
{
var
newUpstreamProxy
=
await
proxyServer
.
CustomUpStreamProxyFailureFunc
(
session
);
if
(
newUpstreamProxy
!=
null
)
{
session
.
CustomUpStreamProxyUsed
=
newUpstreamProxy
;
session
.
TimeLine
[
"Retrying Upstream Proxy Connection"
]
=
DateTime
.
Now
;
return
await
createServerConnection
(
remoteHostName
,
remotePort
,
httpVersion
,
isHttps
,
sslProtocol
,
applicationProtocols
,
isConnect
,
proxyServer
,
session
,
upStreamEndPoint
,
externalProxy
,
cacheKey
,
cancellationToken
);
}
}
throw
new
Exception
(
$"Could not establish connection to
{
hostname
}
"
,
lastException
);
}
...
...
@@ -409,34 +423,30 @@ retry:
stream
=
new
HttpServerStream
(
tcpClient
.
GetStream
(),
proxyServer
.
BufferPool
);
if
(
useUpstreamProxy
&&
(
isConnect
||
isHttps
))
if
(
externalProxy
!=
null
&&
(
isConnect
||
isHttps
))
{
string
authority
=
$"
{
remoteHostName
}
:
{
remotePort
}
"
;
var
connectRequest
=
new
ConnectRequest
(
authority
)
{
IsHttps
=
isHttps
,
OriginalUrlData
=
HttpHeader
.
Encoding
.
GetBytes
(
authority
),
RequestUriString8
=
HttpHeader
.
Encoding
.
GetBytes
(
authority
),
HttpVersion
=
httpVersion
};
connectRequest
.
Headers
.
AddHeader
(
KnownHeaders
.
Connection
,
KnownHeaders
.
ConnectionKeepAlive
);
if
(!
string
.
IsNullOrEmpty
(
externalProxy
!
.
UserName
)
&&
externalProxy
.
Password
!=
null
)
if
(!
string
.
IsNullOrEmpty
(
externalProxy
.
UserName
)
&&
externalProxy
.
Password
!=
null
)
{
connectRequest
.
Headers
.
AddHeader
(
HttpHeader
.
ProxyConnectionKeepAlive
);
connectRequest
.
Headers
.
AddHeader
(
HttpHeader
.
GetProxyAuthorizationHeader
(
externalProxy
.
UserName
,
externalProxy
.
Password
));
connectRequest
.
Headers
.
AddHeader
(
HttpHeader
.
GetProxyAuthorizationHeader
(
externalProxy
.
UserName
,
externalProxy
.
Password
));
}
await
stream
.
WriteRequestAsync
(
connectRequest
,
cancellationToken
:
cancellationToken
);
string
httpStatus
=
await
stream
.
ReadLineAsync
(
cancellationToken
)
??
throw
new
ServerConnectionException
(
"Server connection was closed."
);
var
httpStatus
=
await
stream
.
ReadResponseStatus
(
cancellationToken
);
Response
.
ParseResponseLine
(
httpStatus
,
out
_
,
out
int
statusCode
,
out
string
statusDescription
);
if
(
statusCode
!=
200
&&
!
statusDescription
.
EqualsIgnoreCase
(
"OK"
)
&&
!
statusDescription
.
EqualsIgnoreCase
(
"Connection Established"
))
if
(
httpStatus
.
StatusCode
!=
200
&&
!
httpStatus
.
Description
.
EqualsIgnoreCase
(
"OK"
)
&&
!
httpStatus
.
Description
.
EqualsIgnoreCase
(
"Connection Established"
))
{
throw
new
Exception
(
"Upstream proxy failed to create a secure tunnel"
);
}
...
...
@@ -486,7 +496,7 @@ retry:
}
return
new
TcpServerConnection
(
proxyServer
,
tcpClient
,
stream
,
remoteHostName
,
remotePort
,
isHttps
,
negotiatedApplicationProtocol
,
httpVersion
,
useUpstreamProxy
,
externalProxy
,
upStreamEndPoint
,
cacheKey
);
negotiatedApplicationProtocol
,
httpVersion
,
externalProxy
,
upStreamEndPoint
,
cacheKey
);
}
...
...
@@ -659,4 +669,3 @@ retry:
}
}
}
src/Titanium.Web.Proxy/Network/Tcp/TcpServerConnection.cs
View file @
b584af90
...
...
@@ -15,9 +15,11 @@ namespace Titanium.Web.Proxy.Network.Tcp
/// </summary>
internal
class
TcpServerConnection
:
IDisposable
{
public
Guid
Id
{
get
;
}
=
Guid
.
NewGuid
();
internal
TcpServerConnection
(
ProxyServer
proxyServer
,
TcpClient
tcpClient
,
HttpServerStream
stream
,
string
hostName
,
int
port
,
bool
isHttps
,
SslApplicationProtocol
negotiatedApplicationProtocol
,
Version
version
,
bool
useUpstreamProxy
,
IExternalProxy
?
upStreamProxy
,
IPEndPoint
?
upStreamEndPoint
,
string
cacheKey
)
Version
version
,
IExternalProxy
?
upStreamProxy
,
IPEndPoint
?
upStreamEndPoint
,
string
cacheKey
)
{
TcpClient
=
tcpClient
;
LastAccess
=
DateTime
.
Now
;
...
...
@@ -29,7 +31,6 @@ namespace Titanium.Web.Proxy.Network.Tcp
IsHttps
=
isHttps
;
NegotiatedApplicationProtocol
=
negotiatedApplicationProtocol
;
Version
=
version
;
UseUpstreamProxy
=
useUpstreamProxy
;
UpStreamProxy
=
upStreamProxy
;
UpStreamEndPoint
=
upStreamEndPoint
;
...
...
@@ -50,8 +51,6 @@ namespace Titanium.Web.Proxy.Network.Tcp
internal
SslApplicationProtocol
NegotiatedApplicationProtocol
{
get
;
set
;
}
internal
bool
UseUpstreamProxy
{
get
;
set
;
}
/// <summary>
/// Local NIC via connection is made
/// </summary>
...
...
src/Titanium.Web.Proxy/ProxyServer.cs
View file @
b584af90
...
...
@@ -283,6 +283,12 @@ namespace Titanium.Web.Proxy
/// </summary>
public
Func
<
SessionEventArgsBase
,
Task
<
IExternalProxy
?>>?
GetCustomUpStreamProxyFunc
{
get
;
set
;
}
/// <summary>
/// A callback to provide a chance for an upstream proxy failure to be handled by a new upstream proxy.
/// User should return the ExternalProxy object with valid credentials or null.
/// </summary>
public
Func
<
SessionEventArgsBase
,
Task
<
IExternalProxy
?>>?
CustomUpStreamProxyFailureFunc
{
get
;
set
;
}
/// <summary>
/// Callback for error events in this proxy instance.
/// </summary>
...
...
src/Titanium.Web.Proxy/RequestHandler.cs
View file @
b584af90
...
...
@@ -73,7 +73,7 @@ namespace Titanium.Web.Proxy
{
try
{
Request
.
ParseRequestLine
(
httpCmd
!,
out
string
httpMethod
,
out
s
tring
httpUrl
,
out
var
version
);
Request
.
ParseRequestLine
(
httpCmd
!,
out
string
httpMethod
,
out
ByteS
tring
httpUrl
,
out
var
version
);
// Read the request headers in to unique and non-unique header collections
await
HeaderParser
.
ReadHeaders
(
clientStream
,
args
.
HttpClient
.
Request
.
Headers
,
...
...
@@ -86,7 +86,7 @@ namespace Titanium.Web.Proxy
request
.
Authority
=
connectRequest
.
Authority
;
}
request
.
OriginalUrlData
=
HttpHeader
.
Encoding
.
GetBytes
(
httpUrl
)
;
request
.
RequestUriString8
=
httpUrl
;
request
.
Method
=
httpMethod
;
request
.
HttpVersion
=
version
;
...
...
@@ -99,8 +99,7 @@ namespace Titanium.Web.Proxy
await
onBeforeResponse
(
args
);
// send the response
await
clientStream
.
WriteResponseAsync
(
args
.
HttpClient
.
Response
,
cancellationToken
:
cancellationToken
);
await
clientStream
.
WriteResponseAsync
(
args
.
HttpClient
.
Response
,
cancellationToken
);
return
;
}
...
...
@@ -173,7 +172,7 @@ namespace Titanium.Web.Proxy
connection
=
null
;
}
var
result
=
await
handleHttpSessionRequest
(
httpMethod
,
httpUrl
,
version
,
args
,
connection
,
var
result
=
await
handleHttpSessionRequest
(
args
,
connection
,
clientConnection
.
NegotiatedApplicationProtocol
,
cancellationToken
,
cancellationTokenSource
);
...
...
@@ -253,17 +252,20 @@ namespace Titanium.Web.Proxy
}
}
private
async
Task
<
RetryResult
>
handleHttpSessionRequest
(
string
requestHttpMethod
,
string
requestHttpUrl
,
Version
requestVersion
,
SessionEventArgs
args
,
private
async
Task
<
RetryResult
>
handleHttpSessionRequest
(
SessionEventArgs
args
,
TcpServerConnection
?
serverConnection
,
SslApplicationProtocol
sslApplicationProtocol
,
CancellationToken
cancellationToken
,
CancellationTokenSource
cancellationTokenSource
)
{
args
.
HttpClient
.
Request
.
Locked
=
true
;
// do not cache server connections for WebSockets
bool
noCache
=
args
.
HttpClient
.
Request
.
UpgradeToWebSocket
;
// a connection generator task with captured parameters via closure.
Func
<
Task
<
TcpServerConnection
>>
generator
=
()
=>
tcpConnectionFactory
.
GetServerConnection
(
this
,
args
,
isConnect
:
false
,
applicationProtocol
:
sslApplicationProtocol
,
noCache
:
fals
e
,
cancellationToken
:
cancellationToken
);
noCache
:
noCach
e
,
cancellationToken
:
cancellationToken
);
// for connection pool, retry fails until cache is exhausted.
return
await
retryPolicy
<
ServerConnectionException
>().
ExecuteAsync
(
async
(
connection
)
=>
...
...
@@ -278,9 +280,7 @@ namespace Titanium.Web.Proxy
args
.
HttpClient
.
ConnectRequest
!.
TunnelType
=
TunnelType
.
Websocket
;
// if upgrading to websocket then relay the request without reading the contents
await
handleWebSocketUpgrade
(
requestHttpMethod
,
requestHttpUrl
,
requestVersion
,
args
,
args
.
HttpClient
.
Request
,
args
.
HttpClient
.
Response
,
args
.
ClientStream
,
connection
,
cancellationTokenSource
,
cancellationToken
);
await
handleWebSocketUpgrade
(
args
,
args
.
ClientStream
,
connection
,
cancellationTokenSource
,
cancellationToken
);
return
false
;
}
...
...
src/Titanium.Web.Proxy/ResponseHandler.cs
View file @
b584af90
...
...
@@ -70,7 +70,7 @@ namespace Titanium.Web.Proxy
if
(
response
.
Locked
)
{
// write custom user response with body and return.
await
clientStream
.
WriteResponseAsync
(
response
,
cancellationToken
:
cancellationToken
);
await
clientStream
.
WriteResponseAsync
(
response
,
cancellationToken
);
if
(
args
.
HttpClient
.
HasConnection
&&
!
args
.
HttpClient
.
CloseServerConnection
)
{
...
...
@@ -93,8 +93,7 @@ namespace Titanium.Web.Proxy
// clear current response
await
args
.
ClearResponse
(
cancellationToken
);
await
handleHttpSessionRequest
(
args
.
HttpClient
.
Request
.
Method
,
args
.
HttpClient
.
Request
.
Url
,
args
.
HttpClient
.
Request
.
HttpVersion
,
args
,
null
,
args
.
ClientConnection
.
NegotiatedApplicationProtocol
,
await
handleHttpSessionRequest
(
args
,
null
,
args
.
ClientConnection
.
NegotiatedApplicationProtocol
,
cancellationToken
,
args
.
CancellationTokenSource
);
return
;
}
...
...
@@ -106,26 +105,25 @@ namespace Titanium.Web.Proxy
response
.
Headers
.
FixProxyHeaders
();
}
if
(
response
.
IsBodyRead
)
{
await
clientStream
.
WriteResponseAsync
(
response
,
cancellationToken
:
cancellationToken
);
}
else
await
clientStream
.
WriteResponseAsync
(
response
,
cancellationToken
);
if
(
response
.
OriginalHasBody
)
{
// Write back response status to client
var
headerBuilder
=
new
HeaderBuilder
();
headerBuilder
.
WriteResponseLine
(
response
.
HttpVersion
,
response
.
StatusCode
,
response
.
StatusDescription
);
headerBuilder
.
WriteHeaders
(
response
.
Headers
);
await
clientStream
.
WriteHeadersAsync
(
headerBuilder
,
cancellationToken
);
// Write body if exists
if
(
response
.
HasBody
)
if
(
response
.
IsBodySent
)
{
// syphon out body
await
args
.
SyphonOutBodyAsync
(
false
,
cancellationToken
);
}
else
{
await
args
.
CopyResponseBodyAsync
(
clientStream
,
TransformationMode
.
None
,
cancellationToken
);
// Copy body if exists
var
serverStream
=
args
.
HttpClient
.
Connection
.
Stream
;
await
serverStream
.
CopyBodyAsync
(
response
,
false
,
clientStream
,
TransformationMode
.
None
,
args
.
OnDataReceived
,
cancellationToken
);
}
}
args
.
TimeLine
[
"Response Sent"
]
=
DateTime
.
Now
;
}
...
...
src/Titanium.Web.Proxy/StreamExtended/Network/IHttpStreamReader.cs
View file @
b584af90
using
System.Threading
;
using
System
;
using
System.Threading
;
using
System.Threading.Tasks
;
namespace
Titanium.Web.Proxy.StreamExtended.Network
...
...
@@ -8,5 +9,8 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
int
Read
(
byte
[]
buffer
,
int
offset
,
int
count
);
Task
<
int
>
ReadAsync
(
byte
[]
buffer
,
int
offset
,
int
count
,
CancellationToken
cancellationToken
);
Task
CopyBodyAsync
(
IHttpStreamWriter
writer
,
bool
isChunked
,
long
contentLength
,
Action
<
byte
[],
int
,
int
>?
onCopy
,
CancellationToken
cancellationToken
);
}
}
src/Titanium.Web.Proxy/StreamExtended/Network/IHttpStreamWriter.cs
View file @
b584af90
...
...
@@ -7,13 +7,14 @@ namespace Titanium.Web.Proxy.StreamExtended.Network
/// <summary>
/// A concrete implementation of this interface is required when calling CopyStream.
/// </summary>
internal
interface
IHttpStreamWriter
public
interface
IHttpStreamWriter
{
void
Write
(
byte
[]
buffer
,
int
i
,
int
bufferLength
);
void
Write
(
byte
[]
buffer
,
int
offset
,
int
count
);
Task
WriteAsync
(
byte
[]
buffer
,
int
i
,
int
bufferLength
,
CancellationToken
cancellationToken
);
Task
WriteAsync
(
byte
[]
buffer
,
int
offset
,
int
count
,
CancellationToken
cancellationToken
);
Task
CopyBodyAsync
(
IHttpStreamReader
streamReader
,
bool
isChunked
,
long
contentLength
,
Action
<
byte
[],
int
,
int
>?
onCopy
,
CancellationToken
cancellationToken
);
ValueTask
WriteLineAsync
(
CancellationToken
cancellationToken
=
default
);
ValueTask
WriteLineAsync
(
string
value
,
CancellationToken
cancellationToken
=
default
);
}
}
src/Titanium.Web.Proxy/WebSocketHandler.cs
View file @
b584af90
...
...
@@ -16,42 +16,25 @@ namespace Titanium.Web.Proxy
/// <summary>
/// Handle upgrade to websocket
/// </summary>
private
async
Task
handleWebSocketUpgrade
(
string
requestHttpMethod
,
string
requestHttpUrl
,
Version
requestVersion
,
SessionEventArgs
args
,
Request
request
,
Response
response
,
private
async
Task
handleWebSocketUpgrade
(
SessionEventArgs
args
,
HttpClientStream
clientStream
,
TcpServerConnection
serverConnection
,
CancellationTokenSource
cancellationTokenSource
,
CancellationToken
cancellationToken
)
{
// prepare the prefix content
var
headerBuilder
=
new
HeaderBuilder
();
headerBuilder
.
WriteRequestLine
(
requestHttpMethod
,
requestHttpUrl
,
requestVersion
);
headerBuilder
.
WriteHeaders
(
request
.
Headers
);
await
serverConnection
.
Stream
.
WriteHeadersAsync
(
headerBuilder
,
cancellationToken
);
await
serverConnection
.
Stream
.
WriteRequestAsync
(
args
.
HttpClient
.
Request
,
cancellationToken
);
string
httpStatus
;
try
{
httpStatus
=
await
serverConnection
.
Stream
.
ReadLineAsync
(
cancellationToken
)
??
throw
new
ServerConnectionException
(
"Server connection was closed."
);
}
catch
(
Exception
e
)
when
(!(
e
is
ServerConnectionException
))
{
throw
new
ServerConnectionException
(
"Server connection was closed."
,
e
);
}
Response
.
ParseResponseLine
(
httpStatus
,
out
var
responseVersion
,
out
int
responseStatusCode
,
out
string
responseStatusDescription
);
response
.
HttpVersion
=
responseVersion
;
response
.
StatusCode
=
responseStatusCode
;
response
.
StatusDescription
=
responseStatusDescription
;
var
httpStatus
=
await
serverConnection
.
Stream
.
ReadResponseStatus
(
cancellationToken
);
var
response
=
args
.
HttpClient
.
Response
;
response
.
HttpVersion
=
httpStatus
.
Version
;
response
.
StatusCode
=
httpStatus
.
StatusCode
;
response
.
StatusDescription
=
httpStatus
.
Description
;
await
HeaderParser
.
ReadHeaders
(
serverConnection
.
Stream
,
response
.
Headers
,
cancellationToken
);
if
(!
args
.
IsTransparent
)
{
await
clientStream
.
WriteResponseAsync
(
response
,
cancellationToken
:
cancellationToken
);
await
clientStream
.
WriteResponseAsync
(
response
,
cancellationToken
);
}
// If user requested call back then do it
...
...
tests/Titanium.Web.Proxy.IntegrationTests/Helpers/HttpContinueClient.cs
View file @
b584af90
...
...
@@ -22,7 +22,7 @@ namespace Titanium.Web.Proxy.IntegrationTests.Helpers
var
request
=
new
Request
{
Method
=
"POST"
,
Url
=
"/"
,
RequestUriString
=
"/"
,
HttpVersion
=
new
Version
(
1
,
1
)
};
request
.
Headers
.
AddHeader
(
KnownHeaders
.
Host
,
server
);
...
...
tests/Titanium.Web.Proxy.IntegrationTests/Helpers/HttpMessageParsing.cs
View file @
b584af90
...
...
@@ -28,7 +28,7 @@ namespace Titanium.Web.Proxy.IntegrationTests.Helpers
Request
.
ParseRequestLine
(
line
,
out
var
method
,
out
var
url
,
out
var
version
);
var
request
=
new
Request
{
Method
=
method
,
Url
=
url
,
HttpVersion
=
version
Method
=
method
,
RequestUriString8
=
url
,
HttpVersion
=
version
};
while
(!
string
.
IsNullOrEmpty
(
line
=
reader
.
ReadLine
()))
{
...
...
tests/Titanium.Web.Proxy.IntegrationTests/InterceptionTests.cs
View file @
b584af90
...
...
@@ -15,9 +15,12 @@ namespace Titanium.Web.Proxy.IntegrationTests
{
var
testSuite
=
new
TestSuite
();
bool
serverCalled
=
false
;
var
server
=
testSuite
.
GetServer
();
server
.
HandleRequest
((
context
)
=>
{
serverCalled
=
true
;
return
context
.
Response
.
WriteAsync
(
"I am server. I received your greetings."
);
});
...
...
@@ -37,11 +40,11 @@ namespace Titanium.Web.Proxy.IntegrationTests
var
response
=
await
client
.
GetAsync
(
new
Uri
(
server
.
ListeningHttpUrl
));
Assert
.
IsFalse
(
serverCalled
,
"Server should not be called."
);
Assert
.
AreEqual
(
HttpStatusCode
.
OK
,
response
.
StatusCode
);
var
body
=
await
response
.
Content
.
ReadAsStringAsync
();
Assert
.
IsTrue
(
body
.
Contains
(
"TitaniumWebProxy-Stopped!!"
));
}
[
TestMethod
]
...
...
@@ -76,7 +79,6 @@ namespace Titanium.Web.Proxy.IntegrationTests
var
body
=
await
response
.
Content
.
ReadAsStringAsync
();
Assert
.
IsTrue
(
body
.
Contains
(
"TitaniumWebProxy-Stopped!!"
));
}
[
TestMethod
]
...
...
@@ -111,7 +113,6 @@ namespace Titanium.Web.Proxy.IntegrationTests
var
body
=
await
response
.
Content
.
ReadAsStringAsync
();
Assert
.
IsTrue
(
body
.
Contains
(
"TitaniumWebProxy-Stopped!!"
));
}
...
...
@@ -147,7 +148,6 @@ namespace Titanium.Web.Proxy.IntegrationTests
var
body
=
await
response
.
Content
.
ReadAsStringAsync
();
Assert
.
IsTrue
(
body
.
Contains
(
"TitaniumWebProxy-Stopped!!"
));
}
[
TestMethod
]
...
...
@@ -181,7 +181,6 @@ namespace Titanium.Web.Proxy.IntegrationTests
var
body
=
await
response
.
Content
.
ReadAsStringAsync
();
Assert
.
IsTrue
(
body
.
Contains
(
"TitaniumWebProxy-Stopped!!"
));
}
}
}
\ No newline at end of file
}
tests/Titanium.Web.Proxy.IntegrationTests/Titanium.Web.Proxy.IntegrationTests.csproj
View file @
b584af90
...
...
@@ -16,7 +16,7 @@
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Https" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.0.
0
" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.0.
1
" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
<PackageReference Include="MSTest.TestAdapter" Version="2.0.0" />
<PackageReference Include="MSTest.TestFramework" Version="2.0.0" />
...
...
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