Commit 06afb6fd authored by Honfika's avatar Honfika

Allow to read HTTP/2 request/response body

(still not able to modify header or body data)
parent 03cd1e18
......@@ -36,6 +36,9 @@ namespace Titanium.Web.Proxy.Examples.Wpf
public MainWindow()
{
proxyServer = new ProxyServer();
proxyServer.EnableHttp2 = true;
//proxyServer.CertificateManager.CertificateEngine = CertificateEngine.DefaultWindows;
////Set a password for the .pfx file
......@@ -150,11 +153,9 @@ namespace Titanium.Web.Proxy.Examples.Wpf
SessionListItem item = null;
await Dispatcher.InvokeAsync(() => { item = addSession(e); });
if (e.HttpClient.ConnectRequest?.TunnelType == TunnelType.Http2)
{
// GetRequestBody for HTTP/2 currently not supported
return;
}
//if (e.HttpClient.ConnectRequest?.TunnelType == TunnelType.Http2)
//{
//}
if (e.HttpClient.Request.HasBody)
{
......@@ -174,11 +175,9 @@ namespace Titanium.Web.Proxy.Examples.Wpf
}
});
if (e.HttpClient.ConnectRequest?.TunnelType == TunnelType.Http2)
{
// GetRequestBody for HTTP/2 currently not supported
return;
}
//if (e.HttpClient.ConnectRequest?.TunnelType == TunnelType.Http2)
//{
//}
if (item != null)
{
......
......@@ -28,6 +28,8 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary>
private bool reRequest;
internal TaskCompletionSource<bool> ReadHttp2BodyTaskCompletionSource;
/// <summary>
/// Constructor to initialize the proxy
/// </summary>
......@@ -88,6 +90,18 @@ namespace Titanium.Web.Proxy.EventArguments
// If not already read (not cached yet)
if (!request.IsBodyRead)
{
if (request.HttpVersion == HttpVersion.Version20)
{
request.Http2BodyData = new MemoryStream();
request.ReadHttp2BodyTaskCompletionSource = new TaskCompletionSource<bool>();
// signal to HTTP/2 copy frame method to continue
ReadHttp2BodyTaskCompletionSource.SetResult(true);
await request.ReadHttp2BodyTaskCompletionSource.Task;
}
else
{
var body = await readBodyAsync(true, cancellationToken);
request.Body = body;
......@@ -98,6 +112,7 @@ namespace Titanium.Web.Proxy.EventArguments
OnDataSent(body, 0, body.Length);
}
}
}
/// <summary>
/// reinit response object
......@@ -139,6 +154,18 @@ namespace Titanium.Web.Proxy.EventArguments
// If not already read (not cached yet)
if (!response.IsBodyRead)
{
if (response.HttpVersion == HttpVersion.Version20)
{
response.Http2BodyData = new MemoryStream();
response.ReadHttp2BodyTaskCompletionSource = new TaskCompletionSource<bool>();
// signal to HTTP/2 copy frame method to continue
ReadHttp2BodyTaskCompletionSource.SetResult(true);
await response.ReadHttp2BodyTaskCompletionSource.Task;
}
else
{
var body = await readBodyAsync(false, cancellationToken);
response.Body = body;
......@@ -149,6 +176,7 @@ namespace Titanium.Web.Proxy.EventArguments
OnDataReceived(body, 0, body.Length);
}
}
}
private async Task<byte[]> readBodyAsync(bool isRequest, CancellationToken cancellationToken)
{
......
......@@ -3,6 +3,7 @@ using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Compression;
using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers;
......@@ -49,6 +50,10 @@ namespace Titanium.Web.Proxy.Http
/// </summary>
internal string OriginalContentEncoding { get; set; }
internal TaskCompletionSource<bool> ReadHttp2BodyTaskCompletionSource;
internal MemoryStream Http2BodyData;
/// <summary>
/// Keeps the body data after the session is finished.
/// </summary>
......
......@@ -62,6 +62,8 @@ namespace Titanium.Web.Proxy.Http2
int bufferSize, Guid connectionId, bool isClient, CancellationToken cancellationToken,
ExceptionHandler exceptionFunc)
{
decoder = new Decoder(8192, 4096 * 16);
var headerBuffer = new byte[9];
var buffer = new byte[32768];
while (true)
......@@ -96,6 +98,27 @@ namespace Titanium.Web.Proxy.Http2
{
endStream = true;
}
if (!sessions.TryGetValue(streamId, out var args))
{
throw new ProxyHttpException("HTTP Body data received before any header frame.", null, args);
}
var rr = isClient ? (RequestResponseBase)args.HttpClient.Request : args.HttpClient.Response;
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;
rr.ReadHttp2BodyTaskCompletionSource.SetResult(true);
}
}
}
else if (type == 1 /*headers*/)
{
......@@ -127,7 +150,6 @@ namespace Titanium.Web.Proxy.Http2
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);
}
......@@ -149,16 +171,18 @@ namespace Titanium.Web.Proxy.Http2
if (isClient)
{
args.HttpClient.Request.HttpVersion = HttpVersion.Version20;
args.HttpClient.Request.Method = headerListener.Method;
args.HttpClient.Request.OriginalUrl = headerListener.Status;
args.HttpClient.Request.RequestUri = headerListener.GetUri();
var request = args.HttpClient.Request;
request.HttpVersion = HttpVersion.Version20;
request.Method = headerListener.Method;
request.OriginalUrl = headerListener.Status;
request.RequestUri = headerListener.GetUri();
}
else
{
args.HttpClient.Response.HttpVersion = HttpVersion.Version20;
var response = args.HttpClient.Response;
response.HttpVersion = HttpVersion.Version20;
int.TryParse(headerListener.Status, out int statusCode);
args.HttpClient.Response.StatusCode = statusCode;
response.StatusCode = statusCode;
}
}
catch (Exception ex)
......@@ -168,7 +192,18 @@ namespace Titanium.Web.Proxy.Http2
if (endHeaders)
{
await onBeforeRequestResponse(args);
var handler = onBeforeRequestResponse(args);
var tcs = new TaskCompletionSource<bool>();
args.ReadHttp2BodyTaskCompletionSource = tcs;
if (handler == await Task.WhenAny(handler, tcs.Task))
{
tcs.SetResult(true);
}
var rr = isClient ? (RequestResponseBase)args.HttpClient.Request : args.HttpClient.Response;
rr.Locked = true;
}
}
......@@ -186,7 +221,7 @@ namespace Titanium.Web.Proxy.Http2
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(buffer, 0, length);
......
......@@ -149,7 +149,6 @@ namespace Titanium.Web.Proxy
/// Enable disable HTTP/2 support.
/// Warning: HTTP/2 support is very limited
/// - 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)
/// </summary>
public bool EnableHttp2 { get; set; } = false;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment