using System.Net; using System.Net.Sockets; using System.Text; namespace OF_DL.Tests.Services; internal sealed class SimpleHttpServer : IDisposable { private readonly TcpListener _listener; private readonly Task _handlerTask; private readonly byte[] _responseBytes; public SimpleHttpServer(string body, DateTime? lastModifiedUtc = null) { _listener = new TcpListener(IPAddress.Loopback, 0); _listener.Start(); int port = ((IPEndPoint)_listener.LocalEndpoint).Port; Url = new Uri($"http://127.0.0.1:{port}/"); _responseBytes = BuildResponse(body, lastModifiedUtc); _handlerTask = Task.Run(HandleOnceAsync); } public Uri Url { get; } public Task Completion => _handlerTask; public void Dispose() { _listener.Stop(); try { _handlerTask.Wait(TimeSpan.FromSeconds(1)); } catch { // ignored } } private async Task HandleOnceAsync() { using TcpClient client = await _listener.AcceptTcpClientAsync(); await using NetworkStream stream = client.GetStream(); await ReadHeadersAsync(stream); await stream.WriteAsync(_responseBytes); } private static async Task ReadHeadersAsync(NetworkStream stream) { byte[] buffer = new byte[1024]; int read; string data = ""; while ((read = await stream.ReadAsync(buffer)) > 0) { data += Encoding.ASCII.GetString(buffer, 0, read); if (data.Contains("\r\n\r\n", StringComparison.Ordinal)) { break; } } } private static byte[] BuildResponse(string body, DateTime? lastModifiedUtc) { byte[] bodyBytes = Encoding.UTF8.GetBytes(body); StringBuilder header = new(); header.Append("HTTP/1.1 200 OK\r\n"); header.Append("Content-Type: application/xml\r\n"); header.Append($"Content-Length: {bodyBytes.Length}\r\n"); if (lastModifiedUtc.HasValue) { header.Append($"Last-Modified: {lastModifiedUtc.Value.ToUniversalTime():R}\r\n"); } header.Append("Connection: close\r\n\r\n"); byte[] headerBytes = Encoding.ASCII.GetBytes(header.ToString()); byte[] response = new byte[headerBytes.Length + bodyBytes.Length]; Buffer.BlockCopy(headerBytes, 0, response, 0, headerBytes.Length); Buffer.BlockCopy(bodyBytes, 0, response, headerBytes.Length, bodyBytes.Length); return response; } }