mirror of
https://github.com/LBPUnion/ProjectLighthouse.git
synced 2025-05-28 11:42:28 +00:00
Use pipelines to read request bodies instead of stream
This commit is contained in:
parent
30cffef2f2
commit
bff915e304
3 changed files with 70 additions and 25 deletions
|
@ -48,7 +48,7 @@ public class MatchController : ControllerBase
|
|||
|
||||
if (bodyString.Length == 0 || bodyString[0] != '[') return this.BadRequest();
|
||||
|
||||
Logger.Info("Received match data: " + bodyString, LogArea.Match);
|
||||
Logger.Debug("Received match data: " + bodyString, LogArea.Match);
|
||||
|
||||
IMatchCommand? matchData;
|
||||
try
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
#nullable enable
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.IO;
|
||||
using System.IO.Pipelines;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Serialization;
|
||||
|
@ -12,7 +15,7 @@ using Microsoft.AspNetCore.Mvc;
|
|||
|
||||
namespace LBPUnion.ProjectLighthouse.Extensions;
|
||||
|
||||
public static class ControllerExtensions
|
||||
public static partial class ControllerExtensions
|
||||
{
|
||||
|
||||
public static GameToken GetToken(this ControllerBase controller)
|
||||
|
@ -23,25 +26,72 @@ public static class ControllerExtensions
|
|||
return token;
|
||||
}
|
||||
|
||||
private static void AddStringToBuilder(StringBuilder builder, in ReadOnlySequence<byte> readOnlySequence)
|
||||
{
|
||||
// Separate method because Span/ReadOnlySpan cannot be used in async methods
|
||||
ReadOnlySpan<byte> span = readOnlySequence.IsSingleSegment
|
||||
? readOnlySequence.First.Span
|
||||
: readOnlySequence.ToArray().AsSpan();
|
||||
builder.Append(Encoding.UTF8.GetString(span));
|
||||
}
|
||||
|
||||
public static async Task<string> ReadBodyAsync(this ControllerBase controller)
|
||||
{
|
||||
controller.Request.Body.Position = 0;
|
||||
StringBuilder builder = new();
|
||||
|
||||
using StreamReader bodyReader = new(controller.Request.Body);
|
||||
return await bodyReader.ReadToEndAsync();
|
||||
while (true)
|
||||
{
|
||||
ReadResult readResult = await controller.Request.BodyReader.ReadAsync();
|
||||
ReadOnlySequence<byte> buffer = readResult.Buffer;
|
||||
|
||||
SequencePosition? position;
|
||||
|
||||
do
|
||||
{
|
||||
// Look for a EOL in the buffer
|
||||
position = buffer.PositionOf((byte)'\n');
|
||||
if (position == null) continue;
|
||||
|
||||
ReadOnlySequence<byte> readOnlySequence = buffer.Slice(0, position.Value);
|
||||
AddStringToBuilder(builder, in readOnlySequence);
|
||||
|
||||
// Skip the line + the \n character (basically position)
|
||||
buffer = buffer.Slice(buffer.GetPosition(1, position.Value));
|
||||
}
|
||||
while (position != null);
|
||||
|
||||
|
||||
if (readResult.IsCompleted && buffer.Length > 0)
|
||||
{
|
||||
AddStringToBuilder(builder, in buffer);
|
||||
}
|
||||
|
||||
controller.Request.BodyReader.AdvanceTo(buffer.Start, buffer.End);
|
||||
|
||||
// At this point, buffer will be updated to point one byte after the last
|
||||
// \n character.
|
||||
if (readResult.IsCompleted)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
[GeneratedRegex("&(?!(amp|apos|quot|lt|gt);)")]
|
||||
private static partial Regex CharacterEscapeRegex();
|
||||
|
||||
public static async Task<T?> DeserializeBody<T>(this ControllerBase controller, params string[] rootElements)
|
||||
{
|
||||
controller.Request.Body.Position = 0;
|
||||
|
||||
using StreamReader bodyReader = new(controller.Request.Body);
|
||||
string bodyString = await bodyReader.ReadToEndAsync();
|
||||
string bodyString = await controller.ReadBodyAsync();
|
||||
|
||||
try
|
||||
{
|
||||
// Prevent unescaped ampersands from causing deserialization to fail
|
||||
bodyString = Regex.Replace(bodyString, "&(?!(amp|apos|quot|lt|gt);)", "&");
|
||||
bodyString = CharacterEscapeRegex().Replace(bodyString, "&");
|
||||
|
||||
XmlRootAttribute? root = null;
|
||||
if (rootElements.Length > 0)
|
||||
|
|
|
@ -2,14 +2,13 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
using LBPUnion.ProjectLighthouse.Match.MatchCommands;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Helpers;
|
||||
|
||||
public static class MatchHelper
|
||||
public static partial class MatchHelper
|
||||
{
|
||||
public static readonly Dictionary<int, string?> UserLocations = new();
|
||||
public static readonly Dictionary<int, List<int>?> UserRecentlyDivedIn = new();
|
||||
|
@ -38,27 +37,23 @@ public static class MatchHelper
|
|||
|
||||
public static bool ClearUserRecentDiveIns(int userId) => UserRecentlyDivedIn.Remove(userId);
|
||||
|
||||
[GeneratedRegex("^\\[([^,]*),\\[(.*)\\]\\]")]
|
||||
private static partial Regex MatchJsonRegex();
|
||||
|
||||
[GeneratedRegex(@"0x[a-fA-F0-9]{7,8}")]
|
||||
private static partial Regex LocationHexRegex();
|
||||
|
||||
// This is the function used to show people how laughably awful LBP's protocol is. Beware.
|
||||
public static IMatchCommand? Deserialize(string data)
|
||||
{
|
||||
string matchType = "";
|
||||
System.Text.RegularExpressions.Match match = MatchJsonRegex().Match(data);
|
||||
|
||||
int i = 1;
|
||||
while (true)
|
||||
{
|
||||
if (data[i] == ',') break;
|
||||
|
||||
matchType += data[i];
|
||||
i++;
|
||||
}
|
||||
|
||||
string matchData = $"{{{string.Concat(data.Skip(matchType.Length + 3).SkipLast(2))}}}"; // unfuck formatting so we can parse it as json
|
||||
string matchType = match.Groups[1].Value;
|
||||
// Wraps the actual match data in curly braces to parse it as a json object
|
||||
string matchData = $"{{{match.Groups[2].Value}}}";
|
||||
|
||||
// JSON does not like the hex value that location comes in (0x7f000001) so, convert it to int
|
||||
matchData = Regex.Replace(matchData, @"0x[a-fA-F0-9]{8}", m => Convert.ToInt32(m.Value, 16).ToString());
|
||||
// oh, but it gets better than that! LBP also likes to send hex values with an uneven amount of digits (0xa000064, 7 digits). in any case, we handle it here:
|
||||
matchData = Regex.Replace(matchData, @"0x[a-fA-F0-9]{7}", m => Convert.ToInt32(m.Value, 16).ToString());
|
||||
// i'm actually crying about it.
|
||||
matchData = LocationHexRegex().Replace(matchData, m => Convert.ToInt32(m.Value, 16).ToString());
|
||||
|
||||
return Deserialize(matchType, matchData);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue