Skip to content

Commit

Permalink
Split out PumpBuffer and separated HTTP header parser.
Browse files Browse the repository at this point in the history
  • Loading branch information
NTDLS committed Jun 12, 2024
1 parent f9da8d6 commit cefc14f
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 153 deletions.
30 changes: 30 additions & 0 deletions NetProxy.Library/PumpBuffer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
namespace NetProxy.Library
{
public class PumpBuffer
{
public byte[] Bytes { get; set; }
public int Length { get; set; }

public PumpBuffer(int initialSize)
{
Bytes = new byte[initialSize];
}

public void AutoResize(int maxBufferSize)
{
if (Length == Bytes.Length && Bytes.Length < maxBufferSize)
{
//If we read as much data as we could fit in the buffer, resize it a bit up to the maximum.
int newBufferSize = (int)(Bytes.Length + (Bytes.Length * 0.20));
if (newBufferSize > maxBufferSize)
{
newBufferSize = maxBufferSize;
}

Bytes = new byte[newBufferSize];
}
}
}
}


16 changes: 12 additions & 4 deletions NetProxy.Service/NpServiceManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public class NpServiceManager
public NpConfiguration? Configuration { get; set; }
private readonly RmServer _messageServer;
private readonly NpProxyCollection _proxies = new();
public HashSet<Guid> AuthenticatedConnections { get; set; } = new();
private readonly HashSet<Guid> _authenticatedConnections = new();

public NpServiceManager()
{
Expand All @@ -28,10 +28,18 @@ public NpServiceManager()
_messageServer.OnDisconnected += _messageServer_OnDisconnected;
}

public void RemoveAuthenticated(Guid connectionId)
=> _authenticatedConnections.Remove(connectionId);

public void AddAuthenticated(Guid connectionId)
=> _authenticatedConnections.Add(connectionId);

public bool IsAuthenticated(Guid connectionId)
=> _authenticatedConnections.Contains(connectionId);

public void Notify(Guid connectionId, IRmNotification notification)
=> _messageServer.Notify(connectionId, notification);


public NpProxy? GetProxyById(Guid proxyId)
=> _proxies.Where(o => o.Configuration.Id == proxyId).SingleOrDefault();

Expand All @@ -48,9 +56,9 @@ private void _messageServer_OnDisconnected(RmContext context)
{
lock (Configuration.EnsureNotNull())
{
AuthenticatedConnections.Remove(context.ConnectionId);
_authenticatedConnections.Remove(context.ConnectionId);
}
Console.WriteLine($"Deregistered connection: {context.ConnectionId} (Logged in users {AuthenticatedConnections.Count}).");
Console.WriteLine($"Deregistered connection: {context.ConnectionId} (Logged in users {_authenticatedConnections.Count}).");
}

public void SaveConfiguration()
Expand Down
2 changes: 1 addition & 1 deletion NetProxy.Service/NpServiceManagerMessageHandlerBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ internal class NpServiceManagerMessageHandlerBase
public NpServiceManager EnforceLoginAndGetServiceManager(RmContext context)
{
var serviceManager = (context.Endpoint.Parameter as NpServiceManager).EnsureNotNull();
if (serviceManager.AuthenticatedConnections.Contains(context.ConnectionId) == false)
if (serviceManager.IsAuthenticated(context.ConnectionId) == false)
{
throw new Exception("Login has not been completed.");
}
Expand Down
31 changes: 0 additions & 31 deletions NetProxy.Service/NpServiceManagerNotificationHandlers.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using NetProxy.Library.Payloads.ReliableMessages.Notifications;
using NetProxy.Library.Utilities;
using NetProxy.Service.Proxy;
using NTDLS.NullExtensions;
using NTDLS.ReliableMessaging;
Expand All @@ -8,35 +7,6 @@ namespace NetProxy.Service
{
internal class NpServiceManagerNotificationHandlers : NpServiceManagerMessageHandlerBase, IRmMessageHandler
{
public void OnNotificationRegisterLogin(RmContext context, NotificationRegisterLogin notification)
{
var serviceManager = (context.Endpoint.Parameter as NpServiceManager).EnsureNotNull();

try
{
lock (serviceManager.Configuration.EnsureNotNull())
{
if (serviceManager.Configuration.Users.Collection.Where(o =>
o.UserName.ToLower() == notification.UserName.ToLower() && o.PasswordHash.ToLower() == notification.PasswordHash.ToLower()).Any())
{
serviceManager.AuthenticatedConnections.Add(context.ConnectionId);

Singletons.Logging.Write(NpLogging.Severity.Verbose,
$"Registered connection: {context.ConnectionId}, User: {notification.UserName} (Logged in users {serviceManager.AuthenticatedConnections.Count}).");
}
else
{
Singletons.Logging.Write(NpLogging.Severity.Verbose,
$"Failed to register connection: {context.ConnectionId}, User: {notification.UserName} (Logged in users {serviceManager.AuthenticatedConnections.Count}).");
}
}
}
catch (Exception ex)
{
Singletons.Logging.Write("An error occurred while logging in.", ex);
}
}

public void OnNotificationPersistUserList(RmContext context, NotificationPersistUserList notification)
{
var serviceManager = EnforceLoginAndGetServiceManager(context);
Expand Down Expand Up @@ -67,7 +37,6 @@ public void OnNotificationUpsertProxy(RmContext context, NotificationUpsertProxy

try
{

lock (serviceManager.Configuration.EnsureNotNull())
{
var existingProxy = serviceManager.GetProxyById(notification.ProxyConfiguration.Id);
Expand Down
6 changes: 3 additions & 3 deletions NetProxy.Service/NpServiceManagerQueryHandlers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ public QueryLoginReply OnQueryLogin(RmContext context, QueryLogin query)
o.UserName.Equals(query.UserName, StringComparison.CurrentCultureIgnoreCase)
&& o.PasswordHash.Equals(query.PasswordHash, StringComparison.CurrentCultureIgnoreCase)).Any())
{
serviceManager.AuthenticatedConnections.Add(context.ConnectionId);
serviceManager.AddAuthenticated(context.ConnectionId);
Singletons.Logging.Write(NpLogging.Severity.Verbose,
$"Logged in connection: {context.ConnectionId}, User: {query.UserName} (Logged in users {serviceManager.AuthenticatedConnections.Count}).");
$"Logged in connection: {context.ConnectionId}, User: {query.UserName}.");
}
else
{
Singletons.Logging.Write(NpLogging.Severity.Verbose,
$"Failed login connection: {context.ConnectionId}, User: {query.UserName} (Logged in users {serviceManager.AuthenticatedConnections.Count}).");
$"Failed login connection: {context.ConnectionId}, User: {query.UserName}.");
}

reply.Result = true;
Expand Down
93 changes: 93 additions & 0 deletions NetProxy.Service/Proxy/HttpHeaderAugmentation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
using NetProxy.Library;
using NetProxy.Library.Payloads.Routing;
using System.Text;

namespace NetProxy.Service.Proxy
{
internal static class HttpHeaderAugmentation
{
public enum HTTPHeaderResult
{
WaitOnData,
Present,
NotPresent
}

public static HTTPHeaderResult Process(ref StringBuilder httpHeaderBuilder, NpProxyConfiguration proxyConfig, PumpBuffer buffer)
{
try
{
if (httpHeaderBuilder.Length > 0) //We are reconstructing a fragmented HTTP request header.
{
var stringContent = Encoding.UTF8.GetString(buffer.Bytes, 0, buffer.Length);
httpHeaderBuilder.Append(stringContent);
}
else if (HttpUtility.StartsWithHTTPVerb(buffer.Bytes))
{
var stringContent = Encoding.UTF8.GetString(buffer.Bytes, 0, buffer.Length);
httpHeaderBuilder.Append(stringContent);
}

string headerDelimiter = string.Empty;

if (httpHeaderBuilder.Length > 0)
{
var headerType = HttpUtility.IsHttpHeader(httpHeaderBuilder.ToString(), out string? requestVerb);

if (headerType != HttpHeaderType.None && requestVerb != null)
{
var endOfHeaderIndex = HttpUtility.GetHttpHeaderEnd(httpHeaderBuilder.ToString(), out headerDelimiter);
if (endOfHeaderIndex < 0)
{
return HTTPHeaderResult.WaitOnData; //We have a HTTP header but its a fragment. Wait on the remaining header.
}
else
{
if (buffer.Length > headerDelimiter.Length * 2) // "\r\n" or "\r\n\r\n"
{
//If we received more bytes than just the delimiter then we
// need to determine how many non-header bytes need to be sent to the peer.

int endOfHeaderInBufferIndex = HttpUtility.FindDelimiterIndexInByteArray(buffer.Bytes, buffer.Length, $"{headerDelimiter}{headerDelimiter}");
if (endOfHeaderInBufferIndex < 0)
{
throw new Exception("Could not locate HTTP header in receive buffer.");
}

int bufferEndOfHeaderOffset = endOfHeaderInBufferIndex + (headerDelimiter.Length * 2);

if (bufferEndOfHeaderOffset > buffer.Length)
{
//We received extra non-header bytes. We need to remove the header bytes from the buffer
// and then send them after we modify and send the header.
int newBufferLength = buffer.Length - bufferEndOfHeaderOffset;
Array.Copy(buffer.Bytes, bufferEndOfHeaderOffset, buffer.Bytes, 0, newBufferLength);
}

buffer.Length -= bufferEndOfHeaderOffset;
}
else
{
buffer.Length = 0;
}
}

//Apply the header rules, replacing the original http header builder.
httpHeaderBuilder = new StringBuilder(
HttpUtility.ApplyHttpHeaderRules(proxyConfig,
httpHeaderBuilder.ToString(), headerType, requestVerb, headerDelimiter));

return HTTPHeaderResult.Present;
}
}
}
catch (Exception ex)
{
httpHeaderBuilder.Clear();
Singletons.Logging.Write("An error occurred while parsing the HTTP request header.", ex);
}

return HTTPHeaderResult.NotPresent;
}
}
}
Loading

0 comments on commit cefc14f

Please sign in to comment.