126 lines
3.7 KiB
C#
126 lines
3.7 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Text;
|
|
|
|
namespace TrajectoryPlanningVisualization;
|
|
|
|
internal sealed class LoopbackHttpRequest
|
|
{
|
|
public LoopbackHttpRequest(string method, string path, string token)
|
|
{
|
|
Method = method;
|
|
Path = path;
|
|
Token = token;
|
|
}
|
|
|
|
public string Method { get; }
|
|
public string Path { get; }
|
|
public string Token { get; }
|
|
}
|
|
|
|
internal static class LoopbackHttpRequestReader
|
|
{
|
|
internal const int MaximumHeaderBytes = 16 * 1024;
|
|
private const int RequestReadTimeoutMilliseconds = 2000;
|
|
|
|
public static bool TryRead(Stream stream, out LoopbackHttpRequest request)
|
|
{
|
|
request = null;
|
|
var bytes = new List<byte>();
|
|
bool tooLarge = false;
|
|
int terminatorBytes = 0;
|
|
var stopwatch = Stopwatch.StartNew();
|
|
while (stopwatch.ElapsedMilliseconds < RequestReadTimeoutMilliseconds)
|
|
{
|
|
int value;
|
|
try
|
|
{
|
|
int remainingMilliseconds = Math.Max(1, RequestReadTimeoutMilliseconds - (int)stopwatch.ElapsedMilliseconds);
|
|
stream.ReadTimeout = remainingMilliseconds;
|
|
value = stream.ReadByte();
|
|
}
|
|
catch (IOException)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (value < 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
byte next = (byte)value;
|
|
if (!tooLarge)
|
|
{
|
|
bytes.Add(next);
|
|
if (bytes.Count >= MaximumHeaderBytes)
|
|
{
|
|
tooLarge = true;
|
|
}
|
|
}
|
|
|
|
terminatorBytes = next == (terminatorBytes == 0 || terminatorBytes == 2 ? (byte)'\r' : (byte)'\n')
|
|
? terminatorBytes + 1
|
|
: next == '\r' ? 1 : 0;
|
|
if (terminatorBytes == 4)
|
|
{
|
|
return !tooLarge && TryParse(Encoding.ASCII.GetString(bytes.ToArray()), out request);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private static bool TryParse(string headerText, out LoopbackHttpRequest request)
|
|
{
|
|
request = null;
|
|
string[] lines = headerText.Split(new[] { "\r\n" }, StringSplitOptions.None);
|
|
if (lines.Length < 2 || string.IsNullOrEmpty(lines[0]))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
string[] parts = lines[0].Split(' ');
|
|
if (parts.Length != 3 || parts[0].Length == 0 || parts[1].Length == 0 || parts[2] != "HTTP/1.1")
|
|
{
|
|
return false;
|
|
}
|
|
|
|
string target = parts[1];
|
|
if (!target.StartsWith("/", StringComparison.Ordinal) || target.IndexOf('#') >= 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int queryStart = target.IndexOf('?');
|
|
string path = queryStart < 0 ? target : target.Substring(0, queryStart);
|
|
string token = null;
|
|
if (queryStart >= 0)
|
|
{
|
|
string query = target.Substring(queryStart + 1);
|
|
string[] pairs = query.Split('&');
|
|
foreach (string pair in pairs)
|
|
{
|
|
int equals = pair.IndexOf('=');
|
|
string name = equals < 0 ? pair : pair.Substring(0, equals);
|
|
if (name == "token")
|
|
{
|
|
try
|
|
{
|
|
token = Uri.UnescapeDataString(equals < 0 ? string.Empty : pair.Substring(equals + 1));
|
|
}
|
|
catch (UriFormatException)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
request = new LoopbackHttpRequest(parts[0], path, token);
|
|
return true;
|
|
}
|
|
}
|