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(); 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; } }