Commit 46140093 authored by Honfika's avatar Honfika

Websocket fix in GUI demo + handle user exceptions in TWP

parent 91f00ec7
...@@ -118,10 +118,13 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -118,10 +118,13 @@ namespace Titanium.Web.Proxy.Examples.Wpf
}); });
if (item != null) if (item != null)
{
if (e.WebSession.Response.HasBody)
{ {
item.ResponseBody = await e.GetResponseBody(); item.ResponseBody = await e.GetResponseBody();
} }
} }
}
private SessionListItem AddSession(SessionEventArgs e) private SessionListItem AddSession(SessionEventArgs e)
{ {
...@@ -195,18 +198,30 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -195,18 +198,30 @@ namespace Titanium.Web.Proxy.Examples.Wpf
return; return;
} }
const int truncateLimit = 1024;
var session = SelectedSession; var session = SelectedSession;
var data = session.RequestBody ?? new byte[0]; var data = session.RequestBody ?? new byte[0];
data = data.Take(1024).ToArray(); bool truncated = data.Length > truncateLimit;
if (truncated)
{
data = data.Take(truncateLimit).ToArray();
}
string dataStr = string.Join(" ", data.Select(x => x.ToString("X2"))); //string hexStr = string.Join(" ", data.Select(x => x.ToString("X2")));
TextBoxRequest.Text = session.Request.HeaderText + dataStr; TextBoxRequest.Text = session.Request.HeaderText + session.Request.Encoding.GetString(data) +
(truncated ? Environment.NewLine + $"Data is truncated after {truncateLimit} bytes" : null);
data = session.ResponseBody ?? new byte[0]; data = session.ResponseBody ?? new byte[0];
data = data.Take(1024).ToArray(); truncated = data.Length > truncateLimit;
if (truncated)
{
data = data.Take(truncateLimit).ToArray();
}
dataStr = string.Join(" ", data.Select(x => x.ToString("X2"))); //hexStr = string.Join(" ", data.Select(x => x.ToString("X2")));
TextBoxResponse.Text = session.Response.HeaderText + dataStr; TextBoxResponse.Text = session.Response.HeaderText + session.Response.Encoding.GetString(data) +
(truncated ? Environment.NewLine + $"Data is truncated after {truncateLimit} bytes" : null);
} }
} }
} }
...@@ -197,6 +197,8 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -197,6 +197,8 @@ namespace Titanium.Web.Proxy.EventArguments
{ {
//If not already read (not cached yet) //If not already read (not cached yet)
if (WebSession.Response.ResponseBody == null) if (WebSession.Response.ResponseBody == null)
{
if (WebSession.Response.HasBody)
{ {
using (var responseBodyStream = new MemoryStream()) using (var responseBodyStream = new MemoryStream())
{ {
...@@ -221,6 +223,11 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -221,6 +223,11 @@ namespace Titanium.Web.Proxy.EventArguments
WebSession.Response.ResponseBody = await GetDecompressedResponseBody(WebSession.Response.ContentEncoding, responseBodyStream.ToArray()); WebSession.Response.ResponseBody = await GetDecompressedResponseBody(WebSession.Response.ContentEncoding, responseBodyStream.ToArray());
} }
}
else
{
WebSession.Response.ResponseBody = new byte[0];
}
//set this to true for caching //set this to true for caching
WebSession.Response.ResponseBodyRead = true; WebSession.Response.ResponseBodyRead = true;
......
...@@ -18,17 +18,30 @@ namespace Titanium.Web.Proxy.Extensions ...@@ -18,17 +18,30 @@ namespace Titanium.Web.Proxy.Extensions
Task.WhenAll(handlerTasks).Wait(); Task.WhenAll(handlerTasks).Wait();
} }
public static async Task InvokeParallelAsync<T>(this Func<object, T, Task> callback, object sender, T args) public static async Task InvokeParallelAsync<T>(this Func<object, T, Task> callback, object sender, T args, Action<Exception> exceptionFunc)
{ {
var invocationList = callback.GetInvocationList(); var invocationList = callback.GetInvocationList();
var handlerTasks = new Task[invocationList.Length]; var handlerTasks = new Task[invocationList.Length];
for (int i = 0; i < invocationList.Length; i++) for (int i = 0; i < invocationList.Length; i++)
{ {
handlerTasks[i] = ((Func<object, T, Task>)invocationList[i])(sender, args); handlerTasks[i] = InvokeAsync((Func<object, T, Task>)invocationList[i], sender, args, exceptionFunc);
} }
await Task.WhenAll(handlerTasks); await Task.WhenAll(handlerTasks);
} }
private static async Task InvokeAsync<T>(Func<object, T, Task> callback, object sender, T args, Action<Exception> exceptionFunc)
{
try
{
await callback(sender, args);
}
catch (Exception ex)
{
var ex2 = new Exception("Exception thrown in user event", ex);
exceptionFunc(ex2);
}
}
} }
} }
...@@ -37,6 +37,31 @@ namespace Titanium.Web.Proxy.Http ...@@ -37,6 +37,31 @@ namespace Titanium.Web.Proxy.Http
/// </summary> /// </summary>
public Version HttpVersion { get; set; } public Version HttpVersion { get; set; }
/// <summary>
/// Has response body?
/// </summary>
public bool HasBody
{
get
{
//Has body only if response is chunked or content length >0
//If none are true then check if connection:close header exist, if so write response until server or client terminates the connection
if (IsChunked || ContentLength > 0 || !ResponseKeepAlive)
{
return true;
}
//has response if connection:keep-alive header exist and when version is http/1.0
//Because in Http 1.0 server can return a response without content-length (expectation being client would read until end of stream)
if (ResponseKeepAlive && HttpVersion.Minor == 0)
{
return true;
}
return false;
}
}
/// <summary> /// <summary>
/// Keep the connection alive? /// Keep the connection alive?
/// </summary> /// </summary>
......
...@@ -111,14 +111,14 @@ namespace Titanium.Web.Proxy ...@@ -111,14 +111,14 @@ namespace Titanium.Web.Proxy
if (TunnelConnectRequest != null) if (TunnelConnectRequest != null)
{ {
await TunnelConnectRequest.InvokeParallelAsync(this, connectArgs); await TunnelConnectRequest.InvokeParallelAsync(this, connectArgs, ExceptionFunc);
} }
if (!excluded && await CheckAuthorization(clientStreamWriter, connectArgs) == false) if (!excluded && await CheckAuthorization(clientStreamWriter, connectArgs) == false)
{ {
if (TunnelConnectResponse != null) if (TunnelConnectResponse != null)
{ {
await TunnelConnectResponse.InvokeParallelAsync(this, connectArgs); await TunnelConnectResponse.InvokeParallelAsync(this, connectArgs, ExceptionFunc);
} }
return; return;
...@@ -133,7 +133,7 @@ namespace Titanium.Web.Proxy ...@@ -133,7 +133,7 @@ namespace Titanium.Web.Proxy
if (TunnelConnectResponse != null) if (TunnelConnectResponse != null)
{ {
connectArgs.IsHttps = isClientHello; connectArgs.IsHttps = isClientHello;
await TunnelConnectResponse.InvokeParallelAsync(this, connectArgs); await TunnelConnectResponse.InvokeParallelAsync(this, connectArgs, ExceptionFunc);
} }
if (!excluded && isClientHello) if (!excluded && isClientHello)
...@@ -349,7 +349,7 @@ namespace Titanium.Web.Proxy ...@@ -349,7 +349,7 @@ namespace Titanium.Web.Proxy
//If user requested interception do it //If user requested interception do it
if (BeforeRequest != null) if (BeforeRequest != null)
{ {
await BeforeRequest.InvokeParallelAsync(this, args); await BeforeRequest.InvokeParallelAsync(this, args, ExceptionFunc);
} }
if (args.WebSession.Request.CancelRequest) if (args.WebSession.Request.CancelRequest)
......
...@@ -31,10 +31,12 @@ namespace Titanium.Web.Proxy ...@@ -31,10 +31,12 @@ namespace Titanium.Web.Proxy
//read response & headers from server //read response & headers from server
await args.WebSession.ReceiveResponse(); await args.WebSession.ReceiveResponse();
var response = args.WebSession.Response;
//check for windows authentication //check for windows authentication
if (EnableWinAuth if (EnableWinAuth
&& !RunTime.IsRunningOnMono && !RunTime.IsRunningOnMono
&& args.WebSession.Response.ResponseStatusCode == "401") && response.ResponseStatusCode == "401")
{ {
bool disposed = await Handle401UnAuthorized(args); bool disposed = await Handle401UnAuthorized(args);
...@@ -47,9 +49,9 @@ namespace Titanium.Web.Proxy ...@@ -47,9 +49,9 @@ namespace Titanium.Web.Proxy
args.ReRequest = false; args.ReRequest = false;
//If user requested call back then do it //If user requested call back then do it
if (BeforeResponse != null && !args.WebSession.Response.ResponseLocked) if (BeforeResponse != null && !response.ResponseLocked)
{ {
await BeforeResponse.InvokeParallelAsync(this, args); await BeforeResponse.InvokeParallelAsync(this, args, ExceptionFunc);
} }
//if user requested to send request again //if user requested to send request again
...@@ -62,63 +64,55 @@ namespace Titanium.Web.Proxy ...@@ -62,63 +64,55 @@ namespace Titanium.Web.Proxy
return disposed; return disposed;
} }
args.WebSession.Response.ResponseLocked = true; response.ResponseLocked = true;
//Write back to client 100-conitinue response if that's what server returned //Write back to client 100-conitinue response if that's what server returned
if (args.WebSession.Response.Is100Continue) if (response.Is100Continue)
{ {
await WriteResponseStatus(args.WebSession.Response.HttpVersion, "100", "Continue", args.ProxyClient.ClientStreamWriter); await WriteResponseStatus(response.HttpVersion, "100", "Continue", args.ProxyClient.ClientStreamWriter);
await args.ProxyClient.ClientStreamWriter.WriteLineAsync(); await args.ProxyClient.ClientStreamWriter.WriteLineAsync();
} }
else if (args.WebSession.Response.ExpectationFailed) else if (response.ExpectationFailed)
{ {
await WriteResponseStatus(args.WebSession.Response.HttpVersion, "417", "Expectation Failed", args.ProxyClient.ClientStreamWriter); await WriteResponseStatus(response.HttpVersion, "417", "Expectation Failed", args.ProxyClient.ClientStreamWriter);
await args.ProxyClient.ClientStreamWriter.WriteLineAsync(); await args.ProxyClient.ClientStreamWriter.WriteLineAsync();
} }
//Write back response status to client //Write back response status to client
await WriteResponseStatus(args.WebSession.Response.HttpVersion, args.WebSession.Response.ResponseStatusCode, await WriteResponseStatus(response.HttpVersion, response.ResponseStatusCode,
args.WebSession.Response.ResponseStatusDescription, args.ProxyClient.ClientStreamWriter); response.ResponseStatusDescription, args.ProxyClient.ClientStreamWriter);
if (args.WebSession.Response.ResponseBodyRead) if (response.ResponseBodyRead)
{ {
bool isChunked = args.WebSession.Response.IsChunked; bool isChunked = response.IsChunked;
string contentEncoding = args.WebSession.Response.ContentEncoding; string contentEncoding = response.ContentEncoding;
if (contentEncoding != null) if (contentEncoding != null)
{ {
args.WebSession.Response.ResponseBody = await GetCompressedResponseBody(contentEncoding, args.WebSession.Response.ResponseBody); response.ResponseBody = await GetCompressedResponseBody(contentEncoding, response.ResponseBody);
if (isChunked == false) if (isChunked == false)
{ {
args.WebSession.Response.ContentLength = args.WebSession.Response.ResponseBody.Length; response.ContentLength = response.ResponseBody.Length;
} }
else else
{ {
args.WebSession.Response.ContentLength = -1; response.ContentLength = -1;
} }
} }
await WriteResponseHeaders(args.ProxyClient.ClientStreamWriter, args.WebSession.Response); await WriteResponseHeaders(args.ProxyClient.ClientStreamWriter, response);
await args.ProxyClient.ClientStream.WriteResponseBody(args.WebSession.Response.ResponseBody, isChunked); await args.ProxyClient.ClientStream.WriteResponseBody(response.ResponseBody, isChunked);
} }
else else
{ {
await WriteResponseHeaders(args.ProxyClient.ClientStreamWriter, args.WebSession.Response); await WriteResponseHeaders(args.ProxyClient.ClientStreamWriter, response);
//Write body only if response is chunked or content length >0 //Write body if exists
//Is none are true then check if connection:close header exist, if so write response until server or client terminates the connection if (response.HasBody)
if (args.WebSession.Response.IsChunked || args.WebSession.Response.ContentLength > 0 || !args.WebSession.Response.ResponseKeepAlive)
{
await args.WebSession.ServerConnection.StreamReader.WriteResponseBody(BufferSize, args.ProxyClient.ClientStream,
args.WebSession.Response.IsChunked, args.WebSession.Response.ContentLength);
}
//write response if connection:keep-alive header exist and when version is http/1.0
//Because in Http 1.0 server can return a response without content-length (expectation being client would read until end of stream)
else if (args.WebSession.Response.ResponseKeepAlive && args.WebSession.Response.HttpVersion.Minor == 0)
{ {
await args.WebSession.ServerConnection.StreamReader.WriteResponseBody(BufferSize, args.ProxyClient.ClientStream, await args.WebSession.ServerConnection.StreamReader.WriteResponseBody(BufferSize, args.ProxyClient.ClientStream,
args.WebSession.Response.IsChunked, args.WebSession.Response.ContentLength); response.IsChunked, response.ContentLength);
} }
} }
......
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