using System; using System.IO; using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; [assembly: HostingStartup(typeof(Noname.Integration.AzureWebApp.NonameHostingStartup))] // CRITICAL: This allows bypassing the .deps.json dependency graph public class StartupHook { public static void Initialize() { } } namespace Noname.Integration.AzureWebApp { public class NonameHostingStartup : IHostingStartup { public void Configure(IWebHostBuilder builder) { builder.ConfigureServices(services => { services.AddSingleton(); services.AddSingleton(); }); } } public class NonameStartupFilter : IStartupFilter { public Action Configure(Action next) { return app => { app.Use(async (context, nextMiddleware) => { var engineClient = context.RequestServices.GetRequiredService(); await engineClient.CaptureTrafficAsync(context, nextMiddleware); }); next(app); }; } } public class NonameEngineClient { private static readonly HttpClient _httpClient; private static readonly string _engineUrl; private static readonly int _sourceType; private static readonly int _sourceIndex; private static readonly string _sourceKey; private static readonly string _sourceVersion; private const long MaxContentLength = 5242880; static NonameEngineClient() { var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true }; _httpClient = new HttpClient(handler) { Timeout = TimeSpan.FromSeconds(10) }; _engineUrl = Environment.GetEnvironmentVariable("NONAME_ENGINE_URL"); _sourceKey = Environment.GetEnvironmentVariable("NONAME_SOURCE_KEY"); _sourceVersion = Environment.GetEnvironmentVariable("NONAME_SOURCE_VERSION"); int.TryParse(Environment.GetEnvironmentVariable("NONAME_SOURCE_TYPE"), out _sourceType); if (_sourceType == 0) _sourceType = 28; int.TryParse(Environment.GetEnvironmentVariable("NONAME_SOURCE_INDEX"), out _sourceIndex); } public async Task CaptureTrafficAsync(HttpContext context, Func next) { // Guard against null or incomplete context (Azure infrastructure requests) if (context?.Request == null || context.Response == null) { if (next != null) await next(); return; } try { var requestTimestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); // Safely capture request headers with null checks var requestHeaders = new System.Collections.Generic.Dictionary(); try { if (context.Request.Headers != null) { foreach (var header in context.Request.Headers) { if (header.Key != null) { requestHeaders[header.Key] = header.Value.ToString(); } } } } catch { /* Silently skip header enumeration errors */ } // Safely check if we should capture body string requestBody = ""; try { if (ShouldCaptureBody(context.Request.ContentType, context.Request.ContentLength)) { requestBody = await ReadRequestBodyAsync(context.Request); } } catch { /* Silently skip body read errors */ } // Safely wrap response stream var originalResponseBody = context.Response.Body; MemoryStream responseBodyStream = null; try { responseBodyStream = new MemoryStream(); context.Response.Body = responseBodyStream; } catch { // If we can't wrap the response, continue without capturing await next(); return; } await next(); // Safely capture response string responseBody = ""; try { responseBodyStream.Seek(0, SeekOrigin.Begin); if (ShouldCaptureBody(context.Response.ContentType, responseBodyStream.Length)) { responseBody = await new StreamReader(responseBodyStream).ReadToEndAsync(); } } catch { /* Silently skip response body read errors */ } var responseTimestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); // Safely capture response headers var responseHeaders = new System.Collections.Generic.Dictionary(); try { if (context.Response.Headers != null) { foreach (var header in context.Response.Headers) { if (header.Key != null) { responseHeaders[header.Key] = header.Value.ToString(); } } } } catch { /* Silently skip header enumeration errors */ } // Restore original response stream try { responseBodyStream.Seek(0, SeekOrigin.Begin); await responseBodyStream.CopyToAsync(originalResponseBody); context.Response.Body = originalResponseBody; } catch { /* If copy fails, still try to restore */ context.Response.Body = originalResponseBody; } finally { responseBodyStream?.Dispose(); } // PRESERVE DATA BEFORE BACKGROUND THREAD - with null safety string remoteIp = context.Connection?.RemoteIpAddress?.ToString() ?? "0.0.0.0"; string localIp = context.Connection?.LocalIpAddress?.ToString() ?? "0.0.0.0"; int remotePort = context.Connection?.RemotePort ?? 0; int localPort = context.Connection?.LocalPort ?? 0; string protocol = context.Request?.Protocol ?? "HTTP/1.1"; string method = context.Request?.Method ?? "UNKNOWN"; string url = (context.Request?.Path.ToString() ?? "") + (context.Request?.QueryString.ToString() ?? ""); int statusCode = context.Response?.StatusCode ?? 0; _ = Task.Run(() => SendToEngineAsync(requestHeaders, requestBody, requestTimestamp, responseHeaders, responseBody, responseTimestamp, remoteIp, localIp, remotePort, localPort, protocol, method, url, statusCode).GetAwaiter().GetResult()); } catch { // Continue anyway - don't break the request pipeline } } private static bool ShouldCaptureBody(string contentType, long? contentLength) { try { if (string.IsNullOrEmpty(contentType)) return false; return !(contentLength.HasValue && contentLength.Value > MaxContentLength); } catch { return false; } } private static async Task ReadRequestBodyAsync(HttpRequest request) { try { request.EnableBuffering(); request.Body.Position = 0; using var reader = new StreamReader(request.Body, Encoding.UTF8, leaveOpen: true); var body = await reader.ReadToEndAsync(); request.Body.Position = 0; return body; } catch { return ""; } } private async Task SendToEngineAsync(System.Collections.Generic.Dictionary reqH, string reqB, long reqTs, System.Collections.Generic.Dictionary resH, string resB, long resTs, string remIp, string locIp, int remPort, int locPort, string proto, string meth, string url, int stat) { try { if (string.IsNullOrEmpty(_engineUrl)) { return; } var data = new { source = new { type = _sourceType, index = _sourceIndex, key = _sourceKey, version = _sourceVersion }, ip = new { v = 4, src = remIp, dst = locIp }, tcp = new { src = remPort, dst = locPort }, http = new { v = proto, request = new { body = reqB, headers = reqH, ts = reqTs.ToString(), method = meth, url = url }, response = new { body = resB, headers = resH, ts = resTs.ToString(), status = stat } } }; var content = new StringContent(JsonSerializer.Serialize(data), Encoding.UTF8, "application/json"); await _httpClient.PostAsync(_engineUrl, content); } catch { /* Silently handle errors */ } } } }