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
66c41133
Unverified
Commit
66c41133
authored
Apr 26, 2019
by
honfika
Committed by
GitHub
Apr 26, 2019
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #584 from honfika/master
HTTP/2: allow to read the body
parents
03cd1e18
e6a201e1
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
167 additions
and
62 deletions
+167
-62
MainWindow.xaml.cs
examples/Titanium.Web.Proxy.Examples.Wpf/MainWindow.xaml.cs
+9
-10
SessionEventArgs.cs
src/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs
+42
-12
SessionEventArgsBase.cs
...Titanium.Web.Proxy/EventArguments/SessionEventArgsBase.cs
+1
-19
RequestResponseBase.cs
src/Titanium.Web.Proxy/Http/RequestResponseBase.cs
+7
-0
Http2Helper.cs
src/Titanium.Web.Proxy/Http2/Http2Helper.cs
+75
-20
TcpClientConnection.cs
src/Titanium.Web.Proxy/Network/Tcp/TcpClientConnection.cs
+33
-0
ProxyServer.cs
src/Titanium.Web.Proxy/ProxyServer.cs
+0
-1
No files found.
examples/Titanium.Web.Proxy.Examples.Wpf/MainWindow.xaml.cs
View file @
66c41133
...
@@ -36,6 +36,9 @@ namespace Titanium.Web.Proxy.Examples.Wpf
...
@@ -36,6 +36,9 @@ namespace Titanium.Web.Proxy.Examples.Wpf
public
MainWindow
()
public
MainWindow
()
{
{
proxyServer
=
new
ProxyServer
();
proxyServer
=
new
ProxyServer
();
proxyServer
.
EnableHttp2
=
true
;
//proxyServer.CertificateManager.CertificateEngine = CertificateEngine.DefaultWindows;
//proxyServer.CertificateManager.CertificateEngine = CertificateEngine.DefaultWindows;
////Set a password for the .pfx file
////Set a password for the .pfx file
...
@@ -150,11 +153,9 @@ namespace Titanium.Web.Proxy.Examples.Wpf
...
@@ -150,11 +153,9 @@ namespace Titanium.Web.Proxy.Examples.Wpf
SessionListItem
item
=
null
;
SessionListItem
item
=
null
;
await
Dispatcher
.
InvokeAsync
(()
=>
{
item
=
addSession
(
e
);
});
await
Dispatcher
.
InvokeAsync
(()
=>
{
item
=
addSession
(
e
);
});
if
(
e
.
HttpClient
.
ConnectRequest
?.
TunnelType
==
TunnelType
.
Http2
)
//if (e.HttpClient.ConnectRequest?.TunnelType == TunnelType.Http2)
{
//{
// GetRequestBody for HTTP/2 currently not supported
//}
return
;
}
if
(
e
.
HttpClient
.
Request
.
HasBody
)
if
(
e
.
HttpClient
.
Request
.
HasBody
)
{
{
...
@@ -174,11 +175,9 @@ namespace Titanium.Web.Proxy.Examples.Wpf
...
@@ -174,11 +175,9 @@ namespace Titanium.Web.Proxy.Examples.Wpf
}
}
});
});
if
(
e
.
HttpClient
.
ConnectRequest
?.
TunnelType
==
TunnelType
.
Http2
)
//if (e.HttpClient.ConnectRequest?.TunnelType == TunnelType.Http2)
{
//{
// GetRequestBody for HTTP/2 currently not supported
//}
return
;
}
if
(
item
!=
null
)
if
(
item
!=
null
)
{
{
...
...
src/Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs
View file @
66c41133
...
@@ -89,13 +89,28 @@ namespace Titanium.Web.Proxy.EventArguments
...
@@ -89,13 +89,28 @@ namespace Titanium.Web.Proxy.EventArguments
// If not already read (not cached yet)
// If not already read (not cached yet)
if
(!
request
.
IsBodyRead
)
if
(!
request
.
IsBodyRead
)
{
{
var
body
=
await
readBodyAsync
(
true
,
cancellationToken
);
if
(
request
.
HttpVersion
==
HttpHeader
.
Version20
)
request
.
Body
=
body
;
{
request
.
Http2BodyData
=
new
MemoryStream
();
var
tcs
=
new
TaskCompletionSource
<
bool
>();
request
.
ReadHttp2BodyTaskCompletionSource
=
tcs
;
// signal to HTTP/2 copy frame method to continue
request
.
ReadHttp2BeforeHandlerTaskCompletionSource
.
SetResult
(
true
);
await
tcs
.
Task
;
}
else
{
var
body
=
await
readBodyAsync
(
true
,
cancellationToken
);
request
.
Body
=
body
;
// Now set the flag to true
// Now set the flag to true
// So that next time we can deliver body from cache
// So that next time we can deliver body from cache
request
.
IsBodyRead
=
true
;
request
.
IsBodyRead
=
true
;
OnDataSent
(
body
,
0
,
body
.
Length
);
OnDataSent
(
body
,
0
,
body
.
Length
);
}
}
}
}
}
...
@@ -140,13 +155,28 @@ namespace Titanium.Web.Proxy.EventArguments
...
@@ -140,13 +155,28 @@ namespace Titanium.Web.Proxy.EventArguments
// If not already read (not cached yet)
// If not already read (not cached yet)
if
(!
response
.
IsBodyRead
)
if
(!
response
.
IsBodyRead
)
{
{
var
body
=
await
readBodyAsync
(
false
,
cancellationToken
);
if
(
response
.
HttpVersion
==
HttpHeader
.
Version20
)
response
.
Body
=
body
;
{
response
.
Http2BodyData
=
new
MemoryStream
();
var
tcs
=
new
TaskCompletionSource
<
bool
>();
response
.
ReadHttp2BodyTaskCompletionSource
=
tcs
;
// signal to HTTP/2 copy frame method to continue
response
.
ReadHttp2BeforeHandlerTaskCompletionSource
.
SetResult
(
true
);
await
tcs
.
Task
;
}
else
{
var
body
=
await
readBodyAsync
(
false
,
cancellationToken
);
response
.
Body
=
body
;
// Now set the flag to true
// Now set the flag to true
// So that next time we can deliver body from cache
// So that next time we can deliver body from cache
response
.
IsBodyRead
=
true
;
response
.
IsBodyRead
=
true
;
OnDataReceived
(
body
,
0
,
body
.
Length
);
OnDataReceived
(
body
,
0
,
body
.
Length
);
}
}
}
}
}
...
...
src/Titanium.Web.Proxy/EventArguments/SessionEventArgsBase.cs
View file @
66c41133
...
@@ -55,25 +55,7 @@ namespace Titanium.Web.Proxy.EventArguments
...
@@ -55,25 +55,7 @@ namespace Titanium.Web.Proxy.EventArguments
HttpClient
=
new
HttpWebClient
(
request
);
HttpClient
=
new
HttpWebClient
(
request
);
LocalEndPoint
=
endPoint
;
LocalEndPoint
=
endPoint
;
HttpClient
.
ProcessId
=
new
Lazy
<
int
>(()
=>
HttpClient
.
ProcessId
=
new
Lazy
<
int
>(()
=>
ProxyClient
.
Connection
.
GetProcessId
(
endPoint
));
{
if
(
RunTime
.
IsWindows
)
{
var
remoteEndPoint
=
ClientEndPoint
;
// If client is localhost get the process id
if
(
NetworkHelper
.
IsLocalIpAddress
(
remoteEndPoint
.
Address
))
{
var
ipVersion
=
endPoint
.
IpV6Enabled
?
IpVersion
.
Ipv6
:
IpVersion
.
Ipv4
;
return
TcpHelper
.
GetProcessIdByLocalPort
(
ipVersion
,
remoteEndPoint
.
Port
);
}
// can't access process Id of remote request from remote machine
return
-
1
;
}
throw
new
PlatformNotSupportedException
();
});
}
}
/// <summary>
/// <summary>
...
...
src/Titanium.Web.Proxy/Http/RequestResponseBase.cs
View file @
66c41133
...
@@ -3,6 +3,7 @@ using System.ComponentModel;
...
@@ -3,6 +3,7 @@ using System.ComponentModel;
using
System.Diagnostics.CodeAnalysis
;
using
System.Diagnostics.CodeAnalysis
;
using
System.IO
;
using
System.IO
;
using
System.Text
;
using
System.Text
;
using
System.Threading.Tasks
;
using
Titanium.Web.Proxy.Compression
;
using
Titanium.Web.Proxy.Compression
;
using
Titanium.Web.Proxy.Extensions
;
using
Titanium.Web.Proxy.Extensions
;
using
Titanium.Web.Proxy.Helpers
;
using
Titanium.Web.Proxy.Helpers
;
...
@@ -49,6 +50,12 @@ namespace Titanium.Web.Proxy.Http
...
@@ -49,6 +50,12 @@ namespace Titanium.Web.Proxy.Http
/// </summary>
/// </summary>
internal
string
OriginalContentEncoding
{
get
;
set
;
}
internal
string
OriginalContentEncoding
{
get
;
set
;
}
internal
TaskCompletionSource
<
bool
>
ReadHttp2BeforeHandlerTaskCompletionSource
;
internal
TaskCompletionSource
<
bool
>
ReadHttp2BodyTaskCompletionSource
;
internal
MemoryStream
Http2BodyData
;
/// <summary>
/// <summary>
/// Keeps the body data after the session is finished.
/// Keeps the body data after the session is finished.
/// </summary>
/// </summary>
...
...
src/Titanium.Web.Proxy/Http2/Http2Helper.cs
View file @
66c41133
...
@@ -39,15 +39,14 @@ namespace Titanium.Web.Proxy.Http2
...
@@ -39,15 +39,14 @@ namespace Titanium.Web.Proxy.Http2
CancellationTokenSource
cancellationTokenSource
,
Guid
connectionId
,
CancellationTokenSource
cancellationTokenSource
,
Guid
connectionId
,
ExceptionHandler
exceptionFunc
)
ExceptionHandler
exceptionFunc
)
{
{
var
decoder
=
new
Decoder
(
8192
,
4096
*
16
);
var
sessions
=
new
ConcurrentDictionary
<
int
,
SessionEventArgs
>();
var
sessions
=
new
ConcurrentDictionary
<
int
,
SessionEventArgs
>();
// Now async relay all server=>client & client=>server data
// Now async relay all server=>client & client=>server data
var
sendRelay
=
var
sendRelay
=
copyHttp2FrameAsync
(
clientStream
,
serverStream
,
onDataSend
,
sessionFactory
,
decoder
,
sessions
,
onBeforeRequest
,
copyHttp2FrameAsync
(
clientStream
,
serverStream
,
onDataSend
,
sessionFactory
,
sessions
,
onBeforeRequest
,
bufferSize
,
connectionId
,
true
,
cancellationTokenSource
.
Token
,
exceptionFunc
);
bufferSize
,
connectionId
,
true
,
cancellationTokenSource
.
Token
,
exceptionFunc
);
var
receiveRelay
=
var
receiveRelay
=
copyHttp2FrameAsync
(
serverStream
,
clientStream
,
onDataReceive
,
sessionFactory
,
decoder
,
sessions
,
onBeforeResponse
,
copyHttp2FrameAsync
(
serverStream
,
clientStream
,
onDataReceive
,
sessionFactory
,
sessions
,
onBeforeResponse
,
bufferSize
,
connectionId
,
false
,
cancellationTokenSource
.
Token
,
exceptionFunc
);
bufferSize
,
connectionId
,
false
,
cancellationTokenSource
.
Token
,
exceptionFunc
);
await
Task
.
WhenAny
(
sendRelay
,
receiveRelay
);
await
Task
.
WhenAny
(
sendRelay
,
receiveRelay
);
...
@@ -57,11 +56,13 @@ namespace Titanium.Web.Proxy.Http2
...
@@ -57,11 +56,13 @@ namespace Titanium.Web.Proxy.Http2
}
}
private
static
async
Task
copyHttp2FrameAsync
(
Stream
input
,
Stream
output
,
Action
<
byte
[],
int
,
int
>
onCopy
,
private
static
async
Task
copyHttp2FrameAsync
(
Stream
input
,
Stream
output
,
Action
<
byte
[],
int
,
int
>
onCopy
,
Func
<
SessionEventArgs
>
sessionFactory
,
Decoder
decoder
,
ConcurrentDictionary
<
int
,
SessionEventArgs
>
sessions
,
Func
<
SessionEventArgs
>
sessionFactory
,
ConcurrentDictionary
<
int
,
SessionEventArgs
>
sessions
,
Func
<
SessionEventArgs
,
Task
>
onBeforeRequestResponse
,
Func
<
SessionEventArgs
,
Task
>
onBeforeRequestResponse
,
int
bufferSize
,
Guid
connectionId
,
bool
isClient
,
CancellationToken
cancellationToken
,
int
bufferSize
,
Guid
connectionId
,
bool
isClient
,
CancellationToken
cancellationToken
,
ExceptionHandler
exceptionFunc
)
ExceptionHandler
exceptionFunc
)
{
{
var
decoder
=
new
Decoder
(
8192
,
4096
*
16
);
var
headerBuffer
=
new
byte
[
9
];
var
headerBuffer
=
new
byte
[
9
];
var
buffer
=
new
byte
[
32768
];
var
buffer
=
new
byte
[
32768
];
while
(
true
)
while
(
true
)
...
@@ -88,7 +89,30 @@ namespace Titanium.Web.Proxy.Http2
...
@@ -88,7 +89,30 @@ namespace Titanium.Web.Proxy.Http2
bool
endStream
=
false
;
bool
endStream
=
false
;
//System.Diagnostics.Debug.WriteLine("CLIENT: " + isClient + ", STREAM: " + streamId + ", TYPE: " + type);
SessionEventArgs
args
=
null
;
RequestResponseBase
rr
=
null
;
if
(
type
==
0
||
type
==
1
)
{
if
(!
sessions
.
TryGetValue
(
streamId
,
out
args
))
{
if
(
type
==
0
)
{
throw
new
ProxyHttpException
(
"HTTP Body data received before any header frame."
,
null
,
args
);
}
if
(!
isClient
)
{
throw
new
ProxyHttpException
(
"HTTP Response received before any Request header frame."
,
null
,
args
);
}
args
=
sessionFactory
();
sessions
.
TryAdd
(
streamId
,
args
);
}
rr
=
isClient
?
(
RequestResponseBase
)
args
.
HttpClient
.
Request
:
args
.
HttpClient
.
Response
;
}
//System.Diagnostics.Debug.WriteLine("CONN: " + connectionId + ", CLIENT: " + isClient + ", STREAM: " + streamId + ", TYPE: " + type);
if
(
type
==
0
/* data */
)
if
(
type
==
0
/* data */
)
{
{
bool
endStreamFlag
=
(
flags
&
(
int
)
Http2FrameFlag
.
EndStream
)
!=
0
;
bool
endStreamFlag
=
(
flags
&
(
int
)
Http2FrameFlag
.
EndStream
)
!=
0
;
...
@@ -96,6 +120,30 @@ namespace Titanium.Web.Proxy.Http2
...
@@ -96,6 +120,30 @@ namespace Titanium.Web.Proxy.Http2
{
{
endStream
=
true
;
endStream
=
true
;
}
}
if
(
rr
.
ReadHttp2BodyTaskCompletionSource
!=
null
)
{
// Get body method was called in the "before" event handler
var
data
=
rr
.
Http2BodyData
;
data
.
Write
(
buffer
,
0
,
length
);
if
(
endStream
)
{
rr
.
Body
=
data
.
ToArray
();
rr
.
IsBodyRead
=
true
;
var
tcs
=
rr
.
ReadHttp2BodyTaskCompletionSource
;
rr
.
ReadHttp2BodyTaskCompletionSource
=
null
;
if
(!
tcs
.
Task
.
IsCompleted
)
{
tcs
.
SetResult
(
true
);
}
rr
.
Http2BodyData
=
null
;
}
}
}
}
else
if
(
type
==
1
/*headers*/
)
else
if
(
type
==
1
/*headers*/
)
{
{
...
@@ -125,13 +173,6 @@ namespace Titanium.Web.Proxy.Http2
...
@@ -125,13 +173,6 @@ namespace Titanium.Web.Proxy.Http2
dataLength
-=
buffer
[
0
];
dataLength
-=
buffer
[
0
];
}
}
if
(!
sessions
.
TryGetValue
(
streamId
,
out
var
args
))
{
// todo: remove sessions when finished, otherwise it will be a "memory leak"
args
=
sessionFactory
();
sessions
.
TryAdd
(
streamId
,
args
);
}
var
headerListener
=
new
MyHeaderListener
(
var
headerListener
=
new
MyHeaderListener
(
(
name
,
value
)
=>
(
name
,
value
)
=>
{
{
...
@@ -149,16 +190,18 @@ namespace Titanium.Web.Proxy.Http2
...
@@ -149,16 +190,18 @@ namespace Titanium.Web.Proxy.Http2
if
(
isClient
)
if
(
isClient
)
{
{
args
.
HttpClient
.
Request
.
HttpVersion
=
HttpVersion
.
Version20
;
var
request
=
args
.
HttpClient
.
Request
;
args
.
HttpClient
.
Request
.
Method
=
headerListener
.
Method
;
request
.
HttpVersion
=
HttpVersion
.
Version20
;
args
.
HttpClient
.
Request
.
OriginalUrl
=
headerListener
.
Status
;
request
.
Method
=
headerListener
.
Method
;
args
.
HttpClient
.
Request
.
RequestUri
=
headerListener
.
GetUri
();
request
.
OriginalUrl
=
headerListener
.
Status
;
request
.
RequestUri
=
headerListener
.
GetUri
();
}
}
else
else
{
{
args
.
HttpClient
.
Response
.
HttpVersion
=
HttpVersion
.
Version20
;
var
response
=
args
.
HttpClient
.
Response
;
response
.
HttpVersion
=
HttpVersion
.
Version20
;
int
.
TryParse
(
headerListener
.
Status
,
out
int
statusCode
);
int
.
TryParse
(
headerListener
.
Status
,
out
int
statusCode
);
args
.
HttpClient
.
R
esponse
.
StatusCode
=
statusCode
;
r
esponse
.
StatusCode
=
statusCode
;
}
}
}
}
catch
(
Exception
ex
)
catch
(
Exception
ex
)
...
@@ -168,13 +211,25 @@ namespace Titanium.Web.Proxy.Http2
...
@@ -168,13 +211,25 @@ namespace Titanium.Web.Proxy.Http2
if
(
endHeaders
)
if
(
endHeaders
)
{
{
await
onBeforeRequestResponse
(
args
);
var
tcs
=
new
TaskCompletionSource
<
bool
>();
rr
.
ReadHttp2BeforeHandlerTaskCompletionSource
=
tcs
;
var
handler
=
onBeforeRequestResponse
(
args
);
if
(
handler
==
await
Task
.
WhenAny
(
tcs
.
Task
,
handler
))
{
rr
.
ReadHttp2BeforeHandlerTaskCompletionSource
=
null
;
tcs
.
SetResult
(
true
);
}
rr
.
Locked
=
true
;
}
}
}
}
if
(!
isClient
&&
endStream
)
if
(!
isClient
&&
endStream
)
{
{
sessions
.
TryRemove
(
streamId
,
out
_
);
sessions
.
TryRemove
(
streamId
,
out
_
);
//System.Diagnostics.Debug.WriteLine("REMOVED CONN: " + connectionId + ", CLIENT: " + isClient + ", STREAM: " + streamId + ", TYPE: " + type);
}
}
// do not cancel the write operation
// do not cancel the write operation
...
@@ -186,7 +241,7 @@ namespace Titanium.Web.Proxy.Http2
...
@@ -186,7 +241,7 @@ namespace Titanium.Web.Proxy.Http2
return
;
return
;
}
}
/*using (var fs = new System.IO.FileStream($@"c:\
11
\{connectionId}.{streamId}.dat", FileMode.Append))
/*using (var fs = new System.IO.FileStream($@"c:\
temp
\{connectionId}.{streamId}.dat", FileMode.Append))
{
{
fs.Write(headerBuffer, 0, headerBuffer.Length);
fs.Write(headerBuffer, 0, headerBuffer.Length);
fs.Write(buffer, 0, length);
fs.Write(buffer, 0, length);
...
...
src/Titanium.Web.Proxy/Network/Tcp/TcpClientConnection.cs
View file @
66c41133
...
@@ -7,6 +7,8 @@ using System.Net.Security;
...
@@ -7,6 +7,8 @@ using System.Net.Security;
using
System.Net.Sockets
;
using
System.Net.Sockets
;
using
System.Threading.Tasks
;
using
System.Threading.Tasks
;
using
Titanium.Web.Proxy.Extensions
;
using
Titanium.Web.Proxy.Extensions
;
using
Titanium.Web.Proxy.Helpers
;
using
Titanium.Web.Proxy.Models
;
namespace
Titanium.Web.Proxy.Network.Tcp
namespace
Titanium.Web.Proxy.Network.Tcp
{
{
...
@@ -34,11 +36,42 @@ namespace Titanium.Web.Proxy.Network.Tcp
...
@@ -34,11 +36,42 @@ namespace Titanium.Web.Proxy.Network.Tcp
private
readonly
TcpClient
tcpClient
;
private
readonly
TcpClient
tcpClient
;
private
int
?
processId
;
public
Stream
GetStream
()
public
Stream
GetStream
()
{
{
return
tcpClient
.
GetStream
();
return
tcpClient
.
GetStream
();
}
}
public
int
GetProcessId
(
ProxyEndPoint
endPoint
)
{
if
(
processId
.
HasValue
)
{
return
processId
.
Value
;
}
if
(
RunTime
.
IsWindows
)
{
var
remoteEndPoint
=
(
IPEndPoint
)
RemoteEndPoint
;
// If client is localhost get the process id
if
(
NetworkHelper
.
IsLocalIpAddress
(
remoteEndPoint
.
Address
))
{
var
ipVersion
=
endPoint
.
IpV6Enabled
?
IpVersion
.
Ipv6
:
IpVersion
.
Ipv4
;
processId
=
TcpHelper
.
GetProcessIdByLocalPort
(
ipVersion
,
remoteEndPoint
.
Port
);
}
else
{
// can't access process Id of remote request from remote machine
processId
=
-
1
;
}
return
processId
.
Value
;
}
throw
new
PlatformNotSupportedException
();
}
/// <summary>
/// <summary>
/// Dispose.
/// Dispose.
/// </summary>
/// </summary>
...
...
src/Titanium.Web.Proxy/ProxyServer.cs
View file @
66c41133
...
@@ -149,7 +149,6 @@ namespace Titanium.Web.Proxy
...
@@ -149,7 +149,6 @@ namespace Titanium.Web.Proxy
/// Enable disable HTTP/2 support.
/// Enable disable HTTP/2 support.
/// Warning: HTTP/2 support is very limited
/// Warning: HTTP/2 support is very limited
/// - only enabled when both client and server supports it (no protocol changing in proxy)
/// - only enabled when both client and server supports it (no protocol changing in proxy)
/// - GetRequest/ResponseBody(AsString) methods are not supported
/// - cannot modify the request/response (e.g header modifications in BeforeRequest/Response events are ignored)
/// - cannot modify the request/response (e.g header modifications in BeforeRequest/Response events are ignored)
/// </summary>
/// </summary>
public
bool
EnableHttp2
{
get
;
set
;
}
=
false
;
public
bool
EnableHttp2
{
get
;
set
;
}
=
false
;
...
...
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