Commit 9eb2546f authored by Jehonathan Thomas's avatar Jehonathan Thomas Committed by GitHub

Merge pull request #281 from honfika/develop

Fixes and Demo app improvements
parents c4790ef3 33b3586e
......@@ -29,5 +29,17 @@
</GridView>
</ListView.View>
</ListView>
<TabControl Grid.Column="2">
<TabItem Header="Session">
<Grid Background="Red" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBox x:Name="TextBoxRequest" Grid.Row="0" />
<TextBox x:Name="TextBoxResponse" Grid.Row="1" />
</Grid>
</TabItem>
</TabControl>
</Grid>
</Window>
......@@ -29,11 +29,23 @@ namespace Titanium.Web.Proxy.Examples.Wpf
private int lastSessionNumber;
public ObservableCollection<SessionListItem> Sessions { get; } = new ObservableCollection<SessionListItem>();
public ObservableCollection<SessionListItem> Sessions { get; } = new ObservableCollection<SessionListItem>();
public SessionListItem SelectedSession { get; set; }
public SessionListItem SelectedSession
{
get { return selectedSession; }
set
{
if (value != selectedSession)
{
selectedSession = value;
SelectedSessionChanged();
}
}
}
private readonly Dictionary<SessionEventArgs, SessionListItem> sessionDictionary = new Dictionary<SessionEventArgs, SessionListItem>();
private SessionListItem selectedSession;
public MainWindow()
{
......@@ -76,29 +88,47 @@ namespace Titanium.Web.Proxy.Examples.Wpf
private async Task ProxyServer_BeforeRequest(object sender, SessionEventArgs e)
{
SessionListItem item = null;
Dispatcher.Invoke(() =>
{
AddSession(e);
item = AddSession(e);
});
if (e.WebSession.Request.HasBody)
{
item.RequestBody = await e.GetRequestBody();
}
}
private async Task ProxyServer_BeforeResponse(object sender, SessionEventArgs e)
{
SessionListItem item = null;
Dispatcher.Invoke(() =>
{
SessionListItem item;
if (sessionDictionary.TryGetValue(e, out item))
SessionListItem item2;
if (sessionDictionary.TryGetValue(e, out item2))
{
item.Update();
item2.Response.ResponseStatusCode = e.WebSession.Response.ResponseStatusCode;
item2.Response.ResponseStatusDescription = e.WebSession.Response.ResponseStatusDescription;
item2.Response.HttpVersion = e.WebSession.Response.HttpVersion;
item2.Response.ResponseHeaders.AddHeaders(e.WebSession.Response.ResponseHeaders);
item2.Update();
item = item2;
}
});
if (item != null)
{
item.ResponseBody = await e.GetResponseBody();
}
}
private void AddSession(SessionEventArgs e)
private SessionListItem AddSession(SessionEventArgs e)
{
var item = CreateSessionListItem(e);
Sessions.Add(item);
sessionDictionary.Add(e, item);
return item;
}
private SessionListItem CreateSessionListItem(SessionEventArgs e)
......@@ -108,8 +138,16 @@ namespace Titanium.Web.Proxy.Examples.Wpf
{
Number = lastSessionNumber,
SessionArgs = e,
Request =
{
Method = e.WebSession.Request.Method,
RequestUri = e.WebSession.Request.RequestUri,
HttpVersion = e.WebSession.Request.HttpVersion,
},
};
item.Request.RequestHeaders.AddHeaders(e.WebSession.Request.RequestHeaders);
if (e is TunnelConnectSessionEventArgs)
{
e.DataReceived += (sender, args) =>
......@@ -137,27 +175,38 @@ namespace Titanium.Web.Proxy.Examples.Wpf
return item;
}
private void Session_DataReceived(object sender, EventArgs e)
private void ListViewSessions_OnKeyDown(object sender, KeyEventArgs e)
{
Dispatcher.Invoke(() =>
if (e.Key == Key.Delete)
{
var session = (SessionEventArgs)sender;
SessionListItem item;
if (sessionDictionary.TryGetValue(session, out item))
var selectedItems = ((ListView)sender).SelectedItems;
foreach (var item in selectedItems.Cast<SessionListItem>().ToArray())
{
item.Update();
Sessions.Remove(item);
sessionDictionary.Remove(item.SessionArgs);
}
});
}
}
private void ListViewSessions_OnKeyDown(object sender, KeyEventArgs e)
private void SelectedSessionChanged()
{
var selectedItems = ((ListView)sender).SelectedItems;
foreach (var item in selectedItems.Cast<SessionListItem>().ToArray())
if (SelectedSession == null)
{
Sessions.Remove(item);
sessionDictionary.Remove(item.SessionArgs);
return;
}
var session = SelectedSession;
var data = session.RequestBody ?? new byte[0];
data = data.Take(1024).ToArray();
string dataStr = string.Join(" ", data.Select(x => x.ToString("X2")));
TextBoxRequest.Text = session.Request.HeaderText + dataStr;
data = session.ResponseBody ?? new byte[0];
data = data.Take(1024).ToArray();
dataStr = string.Join(" ", data.Select(x => x.ToString("X2")));
TextBoxResponse.Text = session.Response.HeaderText + dataStr;
}
}
}
......@@ -7,6 +7,7 @@ using System.Text;
using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Examples.Wpf.Annotations;
using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.Examples.Wpf
{
......@@ -73,6 +74,14 @@ namespace Titanium.Web.Proxy.Examples.Wpf
set { SetField(ref sentDataCount, value); }
}
public byte[] RequestBody { get; set; }
public byte[] ResponseBody { get; set; }
public Request Request { get; set; } = new Request();
public Response Response { get; set; } = new Response();
public event PropertyChangedEventHandler PropertyChanged;
protected void SetField<T>(ref T field, T value,[CallerMemberName] string propertyName = null)
......
......@@ -286,15 +286,7 @@ namespace Titanium.Web.Proxy.EventArguments
}
WebSession.Request.RequestBody = body;
if (WebSession.Request.IsChunked == false)
{
WebSession.Request.ContentLength = body.Length;
}
else
{
WebSession.Request.ContentLength = -1;
}
WebSession.Request.ContentLength = WebSession.Request.IsChunked ? -1 : body.Length;
}
/// <summary>
......@@ -409,17 +401,6 @@ namespace Titanium.Web.Proxy.EventArguments
return await decompressor.Decompress(responseBodyStream, bufferSize);
}
/// <summary>
/// Before request is made to server
/// Respond with the specified HTML string to client
/// and ignore the request
/// </summary>
/// <param name="html"></param>
public async Task Ok(string html)
{
await Ok(html, null);
}
/// <summary>
/// Before request is made to server
/// Respond with the specified HTML string to client
......@@ -429,30 +410,14 @@ namespace Titanium.Web.Proxy.EventArguments
/// <param name="headers"></param>
public async Task Ok(string html, Dictionary<string, HttpHeader> headers)
{
if (WebSession.Request.RequestLocked)
{
throw new Exception("You cannot call this function after request is made to server.");
}
if (html == null)
{
html = string.Empty;
}
var result = Encoding.Default.GetBytes(html);
var response = new OkResponse();
response.ResponseHeaders.AddHeaders(headers);
response.HttpVersion = WebSession.Request.HttpVersion;
response.ResponseBody = response.Encoding.GetBytes(html ?? string.Empty);
await Ok(result, headers);
}
await Respond(response);
/// <summary>
/// Before request is made to server
/// Respond with the specified byte[] to client
/// and ignore the request
/// </summary>
/// <param name="result"></param>
public async Task Ok(byte[] result)
{
await Ok(result, null);
WebSession.Request.CancelRequest = true;
}
/// <summary>
......@@ -462,37 +427,14 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary>
/// <param name="result"></param>
/// <param name="headers"></param>
public async Task Ok(byte[] result, Dictionary<string, HttpHeader> headers)
public async Task Ok(byte[] result, Dictionary<string, HttpHeader> headers = null)
{
var response = new OkResponse();
if (headers != null && headers.Count > 0)
{
foreach (var header in headers)
{
response.ResponseHeaders.AddHeader(header.Key, header.Value.Value);
}
}
response.ResponseHeaders.AddHeaders(headers);
response.HttpVersion = WebSession.Request.HttpVersion;
response.ResponseBody = result;
await Respond(response);
WebSession.Request.CancelRequest = true;
}
/// <summary>
/// Before request is made to server 
/// Respond with the specified HTML string to client
/// and ignore the request 
/// </summary>
/// <param name="html"></param>
/// <param name="status"></param>
/// <returns></returns>
public async Task GenericResponse(string html, HttpStatusCode status)
{
await GenericResponse(html, null, status);
}
/// <summary>
......@@ -502,24 +444,17 @@ namespace Titanium.Web.Proxy.EventArguments
/// and ignore the request 
/// </summary>
/// <param name="html"></param>
/// <param name="headers"></param>
/// <param name="status"></param>
/// <param name="headers"></param>
/// <returns></returns>
public async Task GenericResponse(string html, Dictionary<string, HttpHeader> headers, HttpStatusCode status)
public async Task GenericResponse(string html, HttpStatusCode status, Dictionary<string, HttpHeader> headers = null)
{
if (WebSession.Request.RequestLocked)
{
throw new Exception("You cannot call this function after request is made to server.");
}
if (html == null)
{
html = string.Empty;
}
var result = Encoding.Default.GetBytes(html);
var response = new GenericResponse(status);
response.HttpVersion = WebSession.Request.HttpVersion;
response.ResponseHeaders.AddHeaders(headers);
response.ResponseBody = response.Encoding.GetBytes(html ?? string.Empty);
await GenericResponse(result, headers, status);
await Respond(response);
}
/// <summary>
......@@ -529,28 +464,17 @@ namespace Titanium.Web.Proxy.EventArguments
/// and ignore the request
/// </summary>
/// <param name="result"></param>
/// <param name="headers"></param>
/// <param name="status"></param>
/// <param name="headers"></param>
/// <returns></returns>
public async Task GenericResponse(byte[] result, Dictionary<string, HttpHeader> headers, HttpStatusCode status)
public async Task GenericResponse(byte[] result, HttpStatusCode status, Dictionary<string, HttpHeader> headers)
{
var response = new GenericResponse(status);
if (headers != null && headers.Count > 0)
{
foreach (var header in headers)
{
response.ResponseHeaders.AddHeader(header.Key, header.Value.Value);
}
}
response.HttpVersion = WebSession.Request.HttpVersion;
response.ResponseHeaders.AddHeaders(headers);
response.ResponseBody = result;
await Respond(response);
WebSession.Request.CancelRequest = true;
}
/// <summary>
......@@ -561,19 +485,21 @@ namespace Titanium.Web.Proxy.EventArguments
public async Task Redirect(string url)
{
var response = new RedirectResponse();
response.HttpVersion = WebSession.Request.HttpVersion;
response.ResponseHeaders.AddHeader("Location", url);
response.ResponseBody = Encoding.ASCII.GetBytes(string.Empty);
response.ResponseBody = new byte[0];
await Respond(response);
WebSession.Request.CancelRequest = true;
}
/// a generic responder method
public async Task Respond(Response response)
{
if (WebSession.Request.RequestLocked)
{
throw new Exception("You cannot call this function after request is made to server.");
}
WebSession.Request.RequestLocked = true;
response.ResponseLocked = true;
......@@ -582,6 +508,8 @@ namespace Titanium.Web.Proxy.EventArguments
WebSession.Response = response;
await httpResponseHandler(this);
WebSession.Request.CancelRequest = true;
}
/// <summary>
......
......@@ -183,24 +183,22 @@ namespace Titanium.Web.Proxy.Helpers
if (httpCmd != null || requestHeaders != null)
{
using (var ms = new MemoryStream())
using (var writer = new StreamWriter(ms, Encoding.ASCII))
using (var writer = new StreamWriter(ms, Encoding.ASCII) { NewLine = ProxyConstants.NewLine })
{
if (httpCmd != null)
{
writer.Write(httpCmd);
writer.Write(ProxyConstants.NewLine);
writer.WriteLine(httpCmd);
}
if (requestHeaders != null)
{
foreach (string header in requestHeaders.Select(t => t.ToString()))
{
writer.Write(header);
writer.Write(ProxyConstants.NewLine);
writer.WriteLine(header);
}
}
writer.Write(ProxyConstants.NewLine);
writer.WriteLine();
writer.Flush();
var data = ms.ToArray();
......
......@@ -115,6 +115,62 @@ namespace Titanium.Web.Proxy.Http
}
}
/// <summary>
/// Adds the given header objects to Request
/// </summary>
/// <param name="newHeaders"></param>
public void AddHeaders(IEnumerable<HttpHeader> newHeaders)
{
if (newHeaders == null)
{
return;
}
foreach (var header in newHeaders)
{
AddHeader(header);
}
}
/// <summary>
/// Adds the given header objects to Request
/// </summary>
/// <param name="newHeaders"></param>
public void AddHeaders(IEnumerable<KeyValuePair<string, string>> newHeaders)
{
if (newHeaders == null)
{
return;
}
foreach (var header in newHeaders)
{
AddHeader(header.Key, header.Value);
}
}
/// <summary>
/// Adds the given header objects to Request
/// </summary>
/// <param name="newHeaders"></param>
public void AddHeaders(IEnumerable<KeyValuePair<string, HttpHeader>> newHeaders)
{
if (newHeaders == null)
{
return;
}
foreach (var header in newHeaders)
{
if (header.Key != header.Value.Name)
{
throw new Exception("Header name mismatch. Key and the name of the HttpHeader object should be the same.");
}
AddHeader(header.Value);
}
}
/// <summary>
/// removes all headers with given name
/// </summary>
......
......@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.Http
{
......@@ -52,13 +51,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary>
/// Content encoding header value
/// </summary>
public string ContentEncoding
{
get
{
return RequestHeaders.GetHeaderValueOrNull("content-encoding");
}
}
public string ContentEncoding => RequestHeaders.GetHeaderValueOrNull("content-encoding");
/// <summary>
/// Request content-length
......@@ -169,7 +162,7 @@ namespace Titanium.Web.Proxy.Http
internal byte[] RequestBody { get; set; }
/// <summary>
/// request body as string
/// Request body as string
/// </summary>
internal string RequestBodyString { get; set; }
......@@ -204,7 +197,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary>
/// Request header collection
/// </summary>
public HeaderCollection RequestHeaders { get; set; }
public HeaderCollection RequestHeaders { get; private set; } = new HeaderCollection();
/// <summary>
/// Does server responsed positively for 100 continue request
......@@ -216,12 +209,30 @@ namespace Titanium.Web.Proxy.Http
/// </summary>
public bool ExpectationFailed { get; internal set; }
/// <summary>
/// Gets the header text.
/// </summary>
public string HeaderText
{
get
{
var sb = new StringBuilder();
sb.AppendLine($"{Method} {RequestUri.PathAndQuery} HTTP/{HttpVersion.Major}.{HttpVersion.Minor}");
foreach (var header in RequestHeaders)
{
sb.AppendLine(header.ToString());
}
sb.AppendLine();
return sb.ToString();
}
}
/// <summary>
/// Constructor.
/// </summary>
public Request()
{
RequestHeaders = new HeaderCollection();
}
/// <summary>
......@@ -235,7 +246,7 @@ namespace Titanium.Web.Proxy.Http
RequestHeaders = null;
RequestBody = null;
RequestBody = null;
RequestBodyString = null;
}
}
}
......@@ -127,15 +127,15 @@ namespace Titanium.Web.Proxy.Http
/// <summary>
/// Collection of all response headers
/// </summary>
public HeaderCollection ResponseHeaders { get; set; }
public HeaderCollection ResponseHeaders { get; private set; } = new HeaderCollection();
/// <summary>
/// response body content as byte array
/// Response body content as byte array
/// </summary>
internal byte[] ResponseBody { get; set; }
/// <summary>
/// response body as string
/// Response body as string
/// </summary>
internal string ResponseBodyString { get; set; }
......@@ -159,12 +159,35 @@ namespace Titanium.Web.Proxy.Http
/// </summary>
public bool ExpectationFailed { get; internal set; }
/// <summary>
/// Gets the resposne status.
/// </summary>
public string ResponseStatus => $"HTTP/{HttpVersion?.Major}.{HttpVersion?.Minor} {ResponseStatusCode} {ResponseStatusDescription}";
/// <summary>
/// Gets the header text.
/// </summary>
public string HeaderText
{
get
{
var sb = new StringBuilder();
sb.AppendLine(ResponseStatus);
foreach (var header in ResponseHeaders)
{
sb.AppendLine(header.ToString());
}
sb.AppendLine();
return sb.ToString();
}
}
/// <summary>
/// Constructor.
/// </summary>
public Response()
{
ResponseHeaders = new HeaderCollection();
}
/// <summary>
......
......@@ -9,9 +9,9 @@ namespace Titanium.Web.Proxy.Models
/// </summary>
public class HttpHeader
{
internal static Version Version10 = new Version(1, 0);
internal static readonly Version Version10 = new Version(1, 0);
internal static Version Version11 = new Version(1, 1);
internal static readonly Version Version11 = new Version(1, 1);
/// <summary>
/// Constructor.
......
......@@ -335,7 +335,7 @@ namespace Titanium.Web.Proxy
break;
}
PrepareRequestHeaders(args.WebSession.Request.RequestHeaders, args.WebSession);
PrepareRequestHeaders(args.WebSession.Request.RequestHeaders);
args.WebSession.Request.Host = args.WebSession.Request.RequestUri.Authority;
//if win auth is enabled
......@@ -474,6 +474,7 @@ namespace Titanium.Web.Proxy
args.WebSession.Request.RequestBody =
await GetCompressedResponseBody(args.WebSession.Request.ContentEncoding, args.WebSession.Request.RequestBody);
}
//chunked send is not supported as of now
args.WebSession.Request.ContentLength = args.WebSession.Request.RequestBody.Length;
......@@ -595,8 +596,7 @@ namespace Titanium.Web.Proxy
/// prepare the request headers so that we can avoid encodings not parsable by this proxy
/// </summary>
/// <param name="requestHeaders"></param>
/// <param name="webRequest"></param>
private void PrepareRequestHeaders(HeaderCollection requestHeaders, HttpWebClient webRequest)
private void PrepareRequestHeaders(HeaderCollection requestHeaders)
{
foreach (var header in requestHeaders)
{
......@@ -610,7 +610,6 @@ namespace Titanium.Web.Proxy
}
FixProxyHeaders(requestHeaders);
webRequest.Request.RequestHeaders = requestHeaders;
}
/// <summary>
......
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