Commit 0b714083 authored by Honfika's avatar Honfika

ProcessId fix for tunnel events.

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