Skip to content
This repository was archived by the owner on Jul 9, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments;
Expand Down Expand Up @@ -106,14 +107,14 @@ public void Stop()
//intecept & cancel redirect or update requests
public async Task OnRequest(object sender, SessionEventArgs e)
{
Console.WriteLine("Active Client Connections:" + (sender as ProxyServer).ClientConnectionCount);
Console.WriteLine("Active Client Connections:" + ((ProxyServer) sender).ClientConnectionCount);
Console.WriteLine(e.WebSession.Request.Url);

//read request headers
var requestHeaders = e.WebSession.Request.RequestHeaders;

var method = e.WebSession.Request.Method.ToUpper();
if ((method == "POST" || method == "PUT" || method == "PATCH"))
if (method == "POST" || method == "PUT" || method == "PATCH")
{
//Get/Set request body bytes
byte[] bodyBytes = await e.GetRequestBody();
Expand Down Expand Up @@ -168,7 +169,7 @@ public async Task OnResponse(object sender, SessionEventArgs e)
{
if (e.WebSession.Response.ResponseStatusCode == "200")
{
if (e.WebSession.Response.ContentType!=null && e.WebSession.Response.ContentType.Trim().ToLower().Contains("text/html"))
if (e.WebSession.Response.ContentType != null && e.WebSession.Response.ContentType.Trim().ToLower().Contains("text/html"))
{
byte[] bodyBytes = await e.GetResponseBody();
await e.SetResponseBody(bodyBytes);
Expand Down
3 changes: 2 additions & 1 deletion Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ private async Task ReadRequestBody()
{
//GET request don't have a request body to read
var method = WebSession.Request.Method.ToUpper();
if ((method != "POST" && method != "PUT" && method != "PATCH"))
if (method != "POST" && method != "PUT" && method != "PATCH")
{
throw new BodyNotFoundException("Request don't have a body. " +
"Please verify that this request is a Http POST/PUT/PATCH and request " +
Expand Down Expand Up @@ -411,6 +411,7 @@ public async Task Ok(byte[] result, Dictionary<string, HttpHeader> headers)
{
response.ResponseHeaders = headers;
}

response.HttpVersion = WebSession.Request.HttpVersion;
response.ResponseBody = result;

Expand Down
21 changes: 21 additions & 0 deletions Titanium.Web.Proxy/Extensions/TcpExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Net.Sockets;
using Titanium.Web.Proxy.Helpers;

namespace Titanium.Web.Proxy.Extensions
{
Expand Down Expand Up @@ -32,5 +33,25 @@ internal static bool IsConnected(this Socket client)
client.Blocking = blockingState;
}
}

/// <summary>
/// Gets the local port from a native TCP row object.
/// </summary>
/// <param name="tcpRow">The TCP row.</param>
/// <returns>The local port</returns>
internal static int GetLocalPort(this NativeMethods.TcpRow tcpRow)
{
return (tcpRow.localPort1 << 8) + tcpRow.localPort2 + (tcpRow.localPort3 << 24) + (tcpRow.localPort4 << 16);
}

/// <summary>
/// Gets the remote port from a native TCP row object.
/// </summary>
/// <param name="tcpRow">The TCP row.</param>
/// <returns>The remote port</returns>
internal static int GetRemotePort(this NativeMethods.TcpRow tcpRow)
{
return (tcpRow.remotePort1 << 8) + tcpRow.remotePort2 + (tcpRow.remotePort3 << 24) + (tcpRow.remotePort4 << 16);
}
}
}
24 changes: 7 additions & 17 deletions Titanium.Web.Proxy/Helpers/CustomBinaryReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,14 @@ internal class CustomBinaryReader : IDisposable
{
private readonly CustomBufferedStream stream;
private readonly int bufferSize;
private readonly byte[] staticBuffer;
private readonly Encoding encoding;

[ThreadStatic]
private static byte[] staticBufferField;

private byte[] staticBuffer
{
get
{
if (staticBufferField == null || staticBufferField.Length != bufferSize)
{
staticBufferField = new byte[bufferSize];
}

return staticBufferField;
}
}

internal CustomBinaryReader(CustomBufferedStream stream, int bufferSize)
{
this.stream = stream;
staticBuffer = new byte[bufferSize];

this.bufferSize = bufferSize;

//default to UTF-8
Expand Down Expand Up @@ -122,10 +109,13 @@ internal async Task<byte[]> ReadBytesAsync(long totalBytesToRead)
{
int bytesToRead = bufferSize;

var buffer = staticBuffer;
if (totalBytesToRead < bufferSize)
{
bytesToRead = (int) totalBytesToRead;
buffer = new byte[bytesToRead];
}

var buffer = staticBuffer;
int bytesRead;
var totalBytesRead = 0;

Expand Down
2 changes: 2 additions & 0 deletions Titanium.Web.Proxy/Helpers/CustomBufferedStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ public override async Task CopyToAsync(Stream destination, int bufferSize, Cance
if (bufferLength > 0)
{
await destination.WriteAsync(streamBuffer, bufferPos, bufferLength, cancellationToken);
bufferLength = 0;
}

await baseStream.CopyToAsync(destination, bufferSize, cancellationToken);
Expand Down Expand Up @@ -307,6 +308,7 @@ public byte ReadByteFromBuffer()
/// <returns>
/// A task that represents the asynchronous write operation.
/// </returns>
[DebuggerStepThrough]
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
return baseStream.WriteAsync(buffer, offset, count, cancellationToken);
Expand Down
3 changes: 1 addition & 2 deletions Titanium.Web.Proxy/Helpers/Network.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ internal class NetworkHelper
{
private static int FindProcessIdFromLocalPort(int port, IpVersion ipVersion)
{
var tcpRow = TcpHelper.GetExtendedTcpTable(ipVersion).FirstOrDefault(
row => row.LocalEndPoint.Port == port);
var tcpRow = TcpHelper.GetTcpRowByLocalPort(ipVersion, port);

return tcpRow?.ProcessId ?? 0;
}
Expand Down
46 changes: 46 additions & 0 deletions Titanium.Web.Proxy/Helpers/Tcp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,52 @@ internal static TcpTable GetExtendedTcpTable(IpVersion ipVersion)
return new TcpTable(tcpRows);
}

/// <summary>
/// Gets the TCP row by local port number.
/// </summary>
/// <returns><see cref="TcpRow"/>.</returns>
internal static TcpRow GetTcpRowByLocalPort(IpVersion ipVersion, int localPort)
{
IntPtr tcpTable = IntPtr.Zero;
int tcpTableLength = 0;

var ipVersionValue = ipVersion == IpVersion.Ipv4 ? NativeMethods.AfInet : NativeMethods.AfInet6;

if (NativeMethods.GetExtendedTcpTable(tcpTable, ref tcpTableLength, false, ipVersionValue, (int)NativeMethods.TcpTableType.OwnerPidAll, 0) != 0)
{
try
{
tcpTable = Marshal.AllocHGlobal(tcpTableLength);
if (NativeMethods.GetExtendedTcpTable(tcpTable, ref tcpTableLength, true, ipVersionValue, (int)NativeMethods.TcpTableType.OwnerPidAll, 0) == 0)
{
NativeMethods.TcpTable table = (NativeMethods.TcpTable)Marshal.PtrToStructure(tcpTable, typeof(NativeMethods.TcpTable));

IntPtr rowPtr = (IntPtr)((long)tcpTable + Marshal.SizeOf(table.length));

for (int i = 0; i < table.length; ++i)
{
var tcpRow = (NativeMethods.TcpRow)Marshal.PtrToStructure(rowPtr, typeof(NativeMethods.TcpRow));
if (tcpRow.GetLocalPort() == localPort)
{
return new TcpRow(tcpRow);
}

rowPtr = (IntPtr)((long)rowPtr + Marshal.SizeOf(typeof(NativeMethods.TcpRow)));
}
}
}
finally
{
if (tcpTable != IntPtr.Zero)
{
Marshal.FreeHGlobal(tcpTable);
}
}
}

return null;
}

/// <summary>
/// relays the input clientStream to the server at the specified host name and port with the given httpCmd and headers as prefix
/// Usefull for websocket requests
Expand Down
45 changes: 45 additions & 0 deletions Titanium.Web.Proxy/Http/HeaderParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Shared;

namespace Titanium.Web.Proxy.Http
{
internal static class HeaderParser
{
internal static async Task ReadHeaders(CustomBinaryReader reader,
Dictionary<string, List<HttpHeader>> nonUniqueResponseHeaders,
Dictionary<string, HttpHeader> headers)
{
string tmpLine;
while (!string.IsNullOrEmpty(tmpLine = await reader.ReadLineAsync()))
{
var header = tmpLine.Split(ProxyConstants.ColonSplit, 2);

var newHeader = new HttpHeader(header[0], header[1]);

//if header exist in non-unique header collection add it there
if (nonUniqueResponseHeaders.ContainsKey(newHeader.Name))
{
nonUniqueResponseHeaders[newHeader.Name].Add(newHeader);
}
//if header is alread in unique header collection then move both to non-unique collection
else if (headers.ContainsKey(newHeader.Name))
{
var existing = headers[newHeader.Name];

var nonUniqueHeaders = new List<HttpHeader> { existing, newHeader };

nonUniqueResponseHeaders.Add(newHeader.Name, nonUniqueHeaders);
headers.Remove(newHeader.Name);
}
//add to unique header collection
else
{
headers.Add(newHeader.Name, newHeader);
}
}
}
}
}
41 changes: 7 additions & 34 deletions Titanium.Web.Proxy/Http/HttpWebClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,10 @@ internal async Task ReceiveResponse()

var httpVersion = httpResult[0].Trim().ToLower();

var version = new Version(1, 1);
if (0 == string.CompareOrdinal(httpVersion, "http/1.0"))
var version = HttpHeader.Version11;
if (string.Equals(httpVersion, "HTTP/1.0", StringComparison.OrdinalIgnoreCase))
{
version = new Version(1, 0);
version = HttpHeader.Version10;
}

Response.HttpVersion = version;
Expand All @@ -192,8 +192,9 @@ internal async Task ReceiveResponse()
await ReceiveResponse();
return;
}
else if (Response.ResponseStatusCode.Equals("417")
&& Response.ResponseStatusDescription.Equals("expectation failed", StringComparison.CurrentCultureIgnoreCase))

if (Response.ResponseStatusCode.Equals("417")
&& Response.ResponseStatusDescription.Equals("expectation failed", StringComparison.CurrentCultureIgnoreCase))
{
//read next line after expectation failed response
Response.ExpectationFailed = true;
Expand All @@ -204,36 +205,8 @@ internal async Task ReceiveResponse()
return;
}

//Read the Response headers
//Read the response headers in to unique and non-unique header collections
string tmpLine;
while (!string.IsNullOrEmpty(tmpLine = await ServerConnection.StreamReader.ReadLineAsync()))
{
var header = tmpLine.Split(ProxyConstants.ColonSplit, 2);

var newHeader = new HttpHeader(header[0], header[1]);

//if header exist in non-unique header collection add it there
if (Response.NonUniqueResponseHeaders.ContainsKey(newHeader.Name))
{
Response.NonUniqueResponseHeaders[newHeader.Name].Add(newHeader);
}
//if header is alread in unique header collection then move both to non-unique collection
else if (Response.ResponseHeaders.ContainsKey(newHeader.Name))
{
var existing = Response.ResponseHeaders[newHeader.Name];

var nonUniqueHeaders = new List<HttpHeader> {existing, newHeader};

Response.NonUniqueResponseHeaders.Add(newHeader.Name, nonUniqueHeaders);
Response.ResponseHeaders.Remove(newHeader.Name);
}
//add to unique header collection
else
{
Response.ResponseHeaders.Add(newHeader.Name, newHeader);
}
}
await HeaderParser.ReadHeaders(ServerConnection.StreamReader, Response.NonUniqueResponseHeaders, Response.ResponseHeaders);
}
}
}
4 changes: 2 additions & 2 deletions Titanium.Web.Proxy/Http/Request.cs
Original file line number Diff line number Diff line change
Expand Up @@ -241,9 +241,9 @@ public bool ExpectContinue
/// request body as string
/// </summary>
internal string RequestBodyString { get; set; }



internal bool RequestBodyRead { get; set; }

internal bool RequestLocked { get; set; }

/// <summary>
Expand Down
4 changes: 4 additions & 0 deletions Titanium.Web.Proxy/Models/HttpHeader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ namespace Titanium.Web.Proxy.Models
/// </summary>
public class HttpHeader
{
internal static Version Version10 = new Version(1, 0);

internal static Version Version11 = new Version(1, 1);

/// <summary>
/// Constructor.
/// </summary>
Expand Down
35 changes: 27 additions & 8 deletions Titanium.Web.Proxy/Network/Tcp/TcpRow.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Net;
using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers;

namespace Titanium.Web.Proxy.Network.Tcp
Expand All @@ -17,24 +18,42 @@ internal TcpRow(NativeMethods.TcpRow tcpRow)
{
ProcessId = tcpRow.owningPid;

int localPort = (tcpRow.localPort1 << 8) + (tcpRow.localPort2) + (tcpRow.localPort3 << 24) + (tcpRow.localPort4 << 16);
long localAddress = tcpRow.localAddr;
LocalEndPoint = new IPEndPoint(localAddress, localPort);
LocalPort = tcpRow.GetLocalPort();
LocalAddress = tcpRow.localAddr;

int remotePort = (tcpRow.remotePort1 << 8) + (tcpRow.remotePort2) + (tcpRow.remotePort3 << 24) + (tcpRow.remotePort4 << 16);
long remoteAddress = tcpRow.remoteAddr;
RemoteEndPoint = new IPEndPoint(remoteAddress, remotePort);
RemotePort = tcpRow.GetRemotePort();
RemoteAddress = tcpRow.remoteAddr;
}

/// <summary>
/// Gets the local end point address.
/// </summary>
internal long LocalAddress { get; }

/// <summary>
/// Gets the local end point port.
/// </summary>
internal int LocalPort { get; }

/// <summary>
/// Gets the local end point.
/// </summary>
internal IPEndPoint LocalEndPoint { get; }
internal IPEndPoint LocalEndPoint => new IPEndPoint(LocalAddress, LocalPort);

/// <summary>
/// Gets the remote end point address.
/// </summary>
internal long RemoteAddress { get; }

/// <summary>
/// Gets the remote end point port.
/// </summary>
internal int RemotePort { get; }

/// <summary>
/// Gets the remote end point.
/// </summary>
internal IPEndPoint RemoteEndPoint { get; }
internal IPEndPoint RemoteEndPoint => new IPEndPoint(RemoteAddress, RemotePort);

/// <summary>
/// Gets the process identifier.
Expand Down
Loading