Commit 46140093 authored by Honfika's avatar Honfika

Websocket fix in GUI demo + handle user exceptions in TWP

parent 91f00ec7
......@@ -119,7 +119,10 @@ namespace Titanium.Web.Proxy.Examples.Wpf
if (item != null)
{
item.ResponseBody = await e.GetResponseBody();
if (e.WebSession.Response.HasBody)
{
item.ResponseBody = await e.GetResponseBody();
}
}
}
......@@ -195,18 +198,30 @@ namespace Titanium.Web.Proxy.Examples.Wpf
return;
}
const int truncateLimit = 1024;
var session = SelectedSession;
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")));
TextBoxRequest.Text = session.Request.HeaderText + dataStr;
//string hexStr = string.Join(" ", data.Select(x => x.ToString("X2")));
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 = data.Take(1024).ToArray();
truncated = data.Length > truncateLimit;
if (truncated)
{
data = data.Take(truncateLimit).ToArray();
}
dataStr = string.Join(" ", data.Select(x => x.ToString("X2")));
TextBoxResponse.Text = session.Response.HeaderText + dataStr;
//hexStr = string.Join(" ", data.Select(x => x.ToString("X2")));
TextBoxResponse.Text = session.Response.HeaderText + session.Response.Encoding.GetString(data) +
(truncated ? Environment.NewLine + $"Data is truncated after {truncateLimit} bytes" : null);
}
}
}
......@@ -198,28 +198,35 @@ namespace Titanium.Web.Proxy.EventArguments
//If not already read (not cached yet)
if (WebSession.Response.ResponseBody == null)
{
using (var responseBodyStream = new MemoryStream())
if (WebSession.Response.HasBody)
{
//If chuncked the read chunk by chunk until we hit chunk end symbol
if (WebSession.Response.IsChunked)
using (var responseBodyStream = new MemoryStream())
{
await WebSession.ServerConnection.StreamReader.CopyBytesToStreamChunked(responseBodyStream);
}
else
{
if (WebSession.Response.ContentLength > 0)
//If chuncked the read chunk by chunk until we hit chunk end symbol
if (WebSession.Response.IsChunked)
{
//If not chunked then its easy just read the amount of bytes mentioned in content length header of response
await WebSession.ServerConnection.StreamReader.CopyBytesToStream(responseBodyStream, WebSession.Response.ContentLength);
await WebSession.ServerConnection.StreamReader.CopyBytesToStreamChunked(responseBodyStream);
}
else if (WebSession.Response.HttpVersion.Major == 1 && WebSession.Response.HttpVersion.Minor == 0 ||
WebSession.Response.ContentLength == -1)
else
{
await WebSession.ServerConnection.StreamReader.CopyBytesToStream(responseBodyStream, long.MaxValue);
if (WebSession.Response.ContentLength > 0)
{
//If not chunked then its easy just read the amount of bytes mentioned in content length header of response
await WebSession.ServerConnection.StreamReader.CopyBytesToStream(responseBodyStream, WebSession.Response.ContentLength);
}
else if (WebSession.Response.HttpVersion.Major == 1 && WebSession.Response.HttpVersion.Minor == 0 ||
WebSession.Response.ContentLength == -1)
{
await WebSession.ServerConnection.StreamReader.CopyBytesToStream(responseBodyStream, long.MaxValue);
}
}
}
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
......
......@@ -18,17 +18,30 @@ namespace Titanium.Web.Proxy.Extensions
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 handlerTasks = new Task[invocationList.Length];
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);
}
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
/// </summary>
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>
/// Keep the connection alive?
/// </summary>
......
......@@ -111,14 +111,14 @@ namespace Titanium.Web.Proxy
if (TunnelConnectRequest != null)
{
await TunnelConnectRequest.InvokeParallelAsync(this, connectArgs);
await TunnelConnectRequest.InvokeParallelAsync(this, connectArgs, ExceptionFunc);
}
if (!excluded && await CheckAuthorization(clientStreamWriter, connectArgs) == false)
{
if (TunnelConnectResponse != null)
{
await TunnelConnectResponse.InvokeParallelAsync(this, connectArgs);
await TunnelConnectResponse.InvokeParallelAsync(this, connectArgs, ExceptionFunc);
}
return;
......@@ -133,7 +133,7 @@ namespace Titanium.Web.Proxy
if (TunnelConnectResponse != null)
{
connectArgs.IsHttps = isClientHello;
await TunnelConnectResponse.InvokeParallelAsync(this, connectArgs);
await TunnelConnectResponse.InvokeParallelAsync(this, connectArgs, ExceptionFunc);
}
if (!excluded && isClientHello)
......@@ -349,7 +349,7 @@ namespace Titanium.Web.Proxy
//If user requested interception do it
if (BeforeRequest != null)
{
await BeforeRequest.InvokeParallelAsync(this, args);
await BeforeRequest.InvokeParallelAsync(this, args, ExceptionFunc);
}
if (args.WebSession.Request.CancelRequest)
......
......@@ -31,10 +31,12 @@ namespace Titanium.Web.Proxy
//read response & headers from server
await args.WebSession.ReceiveResponse();
var response = args.WebSession.Response;
//check for windows authentication
if (EnableWinAuth
&& !RunTime.IsRunningOnMono
&& args.WebSession.Response.ResponseStatusCode == "401")
&& response.ResponseStatusCode == "401")
{
bool disposed = await Handle401UnAuthorized(args);
......@@ -47,9 +49,9 @@ namespace Titanium.Web.Proxy
args.ReRequest = false;
//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
......@@ -62,63 +64,55 @@ namespace Titanium.Web.Proxy
return disposed;
}
args.WebSession.Response.ResponseLocked = true;
response.ResponseLocked = true;
//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();
}
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();
}
//Write back response status to client
await WriteResponseStatus(args.WebSession.Response.HttpVersion, args.WebSession.Response.ResponseStatusCode,
args.WebSession.Response.ResponseStatusDescription, args.ProxyClient.ClientStreamWriter);
await WriteResponseStatus(response.HttpVersion, response.ResponseStatusCode,
response.ResponseStatusDescription, args.ProxyClient.ClientStreamWriter);
if (args.WebSession.Response.ResponseBodyRead)
if (response.ResponseBodyRead)
{
bool isChunked = args.WebSession.Response.IsChunked;
string contentEncoding = args.WebSession.Response.ContentEncoding;
bool isChunked = response.IsChunked;
string contentEncoding = response.ContentEncoding;
if (contentEncoding != null)
{
args.WebSession.Response.ResponseBody = await GetCompressedResponseBody(contentEncoding, args.WebSession.Response.ResponseBody);
response.ResponseBody = await GetCompressedResponseBody(contentEncoding, response.ResponseBody);
if (isChunked == false)
{
args.WebSession.Response.ContentLength = args.WebSession.Response.ResponseBody.Length;
response.ContentLength = response.ResponseBody.Length;
}
else
{
args.WebSession.Response.ContentLength = -1;
response.ContentLength = -1;
}
}
await WriteResponseHeaders(args.ProxyClient.ClientStreamWriter, args.WebSession.Response);
await args.ProxyClient.ClientStream.WriteResponseBody(args.WebSession.Response.ResponseBody, isChunked);
await WriteResponseHeaders(args.ProxyClient.ClientStreamWriter, response);
await args.ProxyClient.ClientStream.WriteResponseBody(response.ResponseBody, isChunked);
}
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
//Is none are true then check if connection:close header exist, if so write response until server or client terminates the connection
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)
//Write body if exists
if (response.HasBody)
{
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