Commit 988db46f authored by Honfika's avatar Honfika

ProcessId fix for tunnel events.

DataSent/Received events added
parent 09e1d55e
namespace Titanium.Web.Proxy.EventArguments
{
public class DataEventArgs
{
public byte[] Buffer { get; }
public int Offset { get; }
public int Count { get; }
public DataEventArgs(byte[] buffer, int offset, int count)
{
Buffer = buffer;
Offset = offset;
Count = count;
}
}
}
\ No newline at end of file
......@@ -7,6 +7,7 @@ using System.Threading.Tasks;
using Titanium.Web.Proxy.Decompression;
using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Http.Responses;
using Titanium.Web.Proxy.Models;
......@@ -91,16 +92,34 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary>
public ExternalProxy CustomUpStreamHttpsProxyUsed { get; set; }
public event EventHandler<DataEventArgs> DataSent;
public event EventHandler<DataEventArgs> DataReceived;
/// <summary>
/// Constructor to initialize the proxy
/// </summary>
internal SessionEventArgs(int bufferSize, Func<SessionEventArgs, Task> httpResponseHandler)
internal SessionEventArgs(int bufferSize, ProxyEndPoint endPoint, Func<SessionEventArgs, Task> httpResponseHandler)
{
this.bufferSize = bufferSize;
this.httpResponseHandler = httpResponseHandler;
ProxyClient = new ProxyClient();
WebSession = new HttpWebClient();
WebSession.ProcessId = new Lazy<int>(() =>
{
var remoteEndPoint = (IPEndPoint)ProxyClient.TcpClient.Client.RemoteEndPoint;
//If client is localhost get the process id
if (NetworkHelper.IsLocalIpAddress(remoteEndPoint.Address))
{
return NetworkHelper.GetProcessIdFromPort(remoteEndPoint.Port, endPoint.IpV6Enabled);
}
//can't access process Id of remote request from remote machine
return -1;
});
}
/// <summary>
......@@ -139,12 +158,14 @@ namespace Titanium.Web.Proxy.EventArguments
await WebSession.ServerConnection.StreamReader.CopyBytesToStream(requestBodyStream, long.MaxValue);
}
}
WebSession.Request.RequestBody = await GetDecompressedResponseBody(WebSession.Request.ContentEncoding, requestBodyStream.ToArray());
}
//Now set the flag to true
//So that next time we can deliver body from cache
WebSession.Request.RequestBodyRead = true;
OnDataSent(WebSession.Request.RequestBody, 0, WebSession.Request.RequestBody.Length);
}
}
......@@ -159,6 +180,15 @@ namespace Titanium.Web.Proxy.EventArguments
WebSession.Response = new Response();
}
internal void OnDataSent(byte[] buffer, int offset, int count)
{
DataSent?.Invoke(this, new DataEventArgs(buffer, offset, count));
}
internal void OnDataReceived(byte[] buffer, int offset, int count)
{
DataReceived?.Invoke(this, new DataEventArgs(buffer, offset, count));
}
/// <summary>
/// Read response body as byte[] for current response
......@@ -191,8 +221,10 @@ namespace Titanium.Web.Proxy.EventArguments
WebSession.Response.ResponseBody = await GetDecompressedResponseBody(WebSession.Response.ContentEncoding, responseBodyStream.ToArray());
}
//set this to true for caching
WebSession.Response.ResponseBodyRead = true;
OnDataReceived(WebSession.Response.ResponseBody, 0, WebSession.Response.ResponseBody.Length);
}
}
......@@ -211,6 +243,7 @@ namespace Titanium.Web.Proxy.EventArguments
await ReadRequestBody();
}
return WebSession.Request.RequestBody;
}
......@@ -229,6 +262,7 @@ namespace Titanium.Web.Proxy.EventArguments
await ReadRequestBody();
}
//Use the encoding specified in request to decode the byte[] data to string
return WebSession.Request.RequestBodyString ?? (WebSession.Request.RequestBodyString =
WebSession.Request.Encoding.GetString(WebSession.Request.RequestBody));
......
......@@ -4,6 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Models;
namespace Titanium.Web.Proxy.EventArguments
{
......@@ -11,7 +12,7 @@ namespace Titanium.Web.Proxy.EventArguments
{
public bool IsHttps { get; set; }
public TunnelConnectSessionEventArgs() : base(0, null)
public TunnelConnectSessionEventArgs(ProxyEndPoint endPoint) : base(0, endPoint, null)
{
}
......
using System.Globalization;
using System;
using System.Globalization;
using System.IO;
using System.Text;
using System.Threading.Tasks;
......@@ -30,6 +31,25 @@ namespace Titanium.Web.Proxy.Extensions
await input.CopyToAsync(output);
}
internal static async Task CopyToAsync(this Stream input, Stream output, Action<byte[], int, int> onCopy)
{
byte[] buffer = new byte[81920];
while (true)
{
int num = await input.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
int bytesRead;
if ((bytesRead = num) != 0)
{
await output.WriteAsync(buffer, 0, bytesRead).ConfigureAwait(false);
onCopy?.Invoke(buffer, 0, bytesRead);
}
else
{
break;
}
}
}
/// <summary>
/// copies the specified bytes to the stream from the input stream
/// </summary>
......
......@@ -174,39 +174,47 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="requestHeaders"></param>
/// <param name="clientStream"></param>
/// <param name="connection"></param>
/// <param name="onDataSend"></param>
/// <param name="onDataReceive"></param>
/// <returns></returns>
internal static async Task SendRaw(string httpCmd, IEnumerable<HttpHeader> requestHeaders, Stream clientStream, TcpConnection connection)
internal static async Task SendRaw(string httpCmd, IEnumerable<HttpHeader> requestHeaders, Stream clientStream, TcpConnection connection, Action<byte[], int, int> onDataSend, Action<byte[], int, int> onDataReceive)
{
//prepare the prefix content
StringBuilder sb = null;
if (httpCmd != null || requestHeaders != null)
{
sb = new StringBuilder();
if (httpCmd != null)
using (var ms = new MemoryStream())
using (var writer = new StreamWriter(ms, Encoding.ASCII))
{
sb.Append(httpCmd);
sb.Append(ProxyConstants.NewLine);
}
if (httpCmd != null)
{
writer.Write(httpCmd);
writer.Write(ProxyConstants.NewLine);
}
if (requestHeaders != null)
{
foreach (string header in requestHeaders.Select(t => t.ToString()))
if (requestHeaders != null)
{
sb.Append(header);
sb.Append(ProxyConstants.NewLine);
foreach (string header in requestHeaders.Select(t => t.ToString()))
{
writer.Write(header);
writer.Write(ProxyConstants.NewLine);
}
}
}
sb.Append(ProxyConstants.NewLine);
writer.Write(ProxyConstants.NewLine);
writer.Flush();
var data = ms.ToArray();
await ms.WriteAsync(data, 0, data.Length);
onDataSend?.Invoke(data, 0, data.Length);
}
}
var tunnelStream = connection.Stream;
//Now async relay all server=>client & client=>server data
var sendRelay = clientStream.CopyToAsync(sb?.ToString() ?? string.Empty, tunnelStream);
var sendRelay = clientStream.CopyToAsync(tunnelStream, onDataSend);
var receiveRelay = tunnelStream.CopyToAsync(string.Empty, clientStream);
var receiveRelay = tunnelStream.CopyToAsync(clientStream, onDataReceive);
await Task.WhenAll(sendRelay, receiveRelay);
}
......
......@@ -93,11 +93,21 @@ namespace Titanium.Web.Proxy
//Client wants to create a secure tcp tunnel (probably its a HTTPS or Websocket request)
if (httpVerb == "CONNECT")
{
connectRequest = new ConnectRequest();
connectRequest = new ConnectRequest
{
RequestUri = httpRemoteUri,
HttpVersion = version,
Method = httpVerb,
};
await HeaderParser.ReadHeaders(clientStreamReader, connectRequest.RequestHeaders);
var connectArgs = new TunnelConnectSessionEventArgs();
var connectArgs = new TunnelConnectSessionEventArgs(endPoint);
connectArgs.WebSession.Request = connectRequest;
connectArgs.ProxyClient.TcpClient = tcpClient;
connectArgs.ProxyClient.ClientStream = clientStream;
connectArgs.ProxyClient.ClientStreamReader = clientStreamReader;
connectArgs.ProxyClient.ClientStreamWriter = clientStreamWriter;
if (TunnelConnectRequest != null)
{
......@@ -165,26 +175,11 @@ namespace Titanium.Web.Proxy
//Sorry cannot do a HTTPS request decrypt to port 80 at this time
else
{
var args = new SessionEventArgs(BufferSize, HandleHttpSessionResponse);
args.WebSession.Request.RequestUri = httpRemoteUri;
args.WebSession.Request.HttpVersion = version;
args.WebSession.Request.Method = httpVerb;
args.ProxyClient.ClientStream = clientStream;
args.ProxyClient.ClientStreamReader = clientStreamReader;
args.ProxyClient.ClientStreamWriter = clientStreamWriter;
//create new connection
using (var connection = await GetServerConnection(args))
using (var connection = await GetServerConnection(connectArgs))
{
if (connection.UseProxy)
{
await TcpHelper.SendRaw(null, null, clientStream, connection);
}
else
{
await TcpHelper.SendRaw(null, null, clientStream, connection);
}
await TcpHelper.SendRaw(null, null, clientStream, connection,
(buffer, offset, count) => { connectArgs.OnDataSent(buffer, offset, count); }, (buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); });
Interlocked.Decrement(ref serverConnectionCount);
}
......@@ -293,26 +288,12 @@ namespace Titanium.Web.Proxy
break;
}
var args = new SessionEventArgs(BufferSize, HandleHttpSessionResponse)
var args = new SessionEventArgs(BufferSize, endPoint, HandleHttpSessionResponse)
{
ProxyClient = { TcpClient = client },
WebSession = { ConnectRequest = connectRequest }
};
args.WebSession.ProcessId = new Lazy<int>(() =>
{
var remoteEndPoint = (IPEndPoint)args.ProxyClient.TcpClient.Client.RemoteEndPoint;
//If client is localhost get the process id
if (NetworkHelper.IsLocalIpAddress(remoteEndPoint.Address))
{
return NetworkHelper.GetProcessIdFromPort(remoteEndPoint.Port, endPoint.IpV6Enabled);
}
//can't access process Id of remote request from remote machine
return -1;
});
try
{
//break up the line into three components (method, remote URL & Http Version)
......@@ -392,7 +373,8 @@ namespace Titanium.Web.Proxy
//if upgrading to websocket then relay the requet without reading the contents
if (args.WebSession.Request.UpgradeToWebSocket)
{
await TcpHelper.SendRaw(httpCmd, args.WebSession.Request.RequestHeaders, clientStream, connection);
await TcpHelper.SendRaw(httpCmd, args.WebSession.Request.RequestHeaders, clientStream, connection,
(buffer, offset, count) => { args.OnDataSent(buffer, offset, count); }, (buffer, offset, count) => { args.OnDataReceived(buffer, offset, count); });
args.Dispose();
break;
......
......@@ -73,6 +73,7 @@
<Compile Include="Decompression\IDecompression.cs" />
<Compile Include="EventArguments\CertificateSelectionEventArgs.cs" />
<Compile Include="EventArguments\CertificateValidationEventArgs.cs" />
<Compile Include="EventArguments\DataEventArgs.cs" />
<Compile Include="EventArguments\TunnelConnectEventArgs.cs" />
<Compile Include="Extensions\ByteArrayExtensions.cs" />
<Compile Include="Extensions\FuncExtensions.cs" />
......
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