diff --git a/CodeProject.AI.sln b/CodeProject.AI.sln index 35ac323..55a419a 100644 --- a/CodeProject.AI.sln +++ b/CodeProject.AI.sln @@ -1,5 +1,4 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 +Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.2.32616.157 MinimumVisualStudioVersion = 10.0.40219.1 diff --git a/src/server/Controllers/ProxyController.cs b/src/server/Controllers/ProxyController.cs index 06a5071..e42f16c 100644 --- a/src/server/Controllers/ProxyController.cs +++ b/src/server/Controllers/ProxyController.cs @@ -15,6 +15,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using CodeProject.AI.SDK.API; @@ -65,6 +66,7 @@ public class ProxyController : ControllerBase private readonly TriggerTaskRunner _commandRunner; private readonly MeshManager _meshManager; private readonly ModuleProcessServices _moduleProcessService; + private readonly ILogger _logger; private bool _verbose = false; @@ -79,13 +81,15 @@ public class ProxyController : ControllerBase /// The command runner /// The mesh manager /// The module process service + /// The logger public ProxyController(CommandDispatcher dispatcher, BackendRouteMap routeMap, IOptions ModuleCollectionOptions, IOptions triggersConfig, TriggerTaskRunner commandRunner, MeshManager meshManager, - ModuleProcessServices moduleProcessService) + ModuleProcessServices moduleProcessService, + ILogger logger) { _dispatcher = dispatcher; _routeMap = routeMap; @@ -94,6 +98,7 @@ public ProxyController(CommandDispatcher dispatcher, _commandRunner = commandRunner; _meshManager = meshManager; _moduleProcessService = moduleProcessService; + _logger = logger; } /// @@ -386,11 +391,10 @@ public IActionResult ApiSummary() } catch (Exception ex) { - Debug.WriteLine($"Error in DispatchRemoteRequest ({server.Status.Hostname}): {ex}"); + _logger.LogError(ex, "Error in DispatchRemoteRequest ({Hostname})", server.Status.Hostname); - // Bump the response to 30s to push this server out of contention. Maybe also add - // exception info to the message - error = $"Exception when forwarding request to {server.Status.Hostname}"; + // Bump the response to 30s to push this server out of contention. + error = $"Exception when forwarding request to {server.Status.Hostname}: {ex.Message}"; elapsedMs = 30_000; } finally @@ -521,6 +525,13 @@ public IActionResult ApiSummary() private async Task ForwardAsync(MeshServerRoutingEntry server) { HttpRequest originalRequest = Request; + + // The body may already have been read once by ASP.NET Core's own model-binding + // pipeline before this point. EnableBuffering() (see Startup.cs) makes the body + // seekable so we can safely rewind it here and forward the full original content. + if (originalRequest.Body.CanSeek) + originalRequest.Body.Position = 0; + int? port = originalRequest.Host.Port; string portString = port.HasValue ? $":{port}" : string.Empty; var queryString = originalRequest.QueryString; @@ -538,7 +549,10 @@ private async Task ForwardAsync(MeshServerRoutingEntry serv }; foreach (KeyValuePair header in originalRequest.Headers) - newRequest.Headers.TryAddWithoutValidation(header.Key, header.Value.AsEnumerable()); + { + if (!newRequest.Headers.TryAddWithoutValidation(header.Key, header.Value.AsEnumerable())) + newRequest.Content!.Headers.TryAddWithoutValidation(header.Key, header.Value.AsEnumerable()); + } newRequest.Headers.Add(CPAI_Forwarded_Header, "true"); diff --git a/src/server/Mesh/MeshSummary.cs b/src/server/Mesh/MeshSummary.cs index 30a6701..df9b2cc 100644 --- a/src/server/Mesh/MeshSummary.cs +++ b/src/server/Mesh/MeshSummary.cs @@ -36,7 +36,7 @@ public string Summary } summary.AppendLine($"{indent}Active: {IsActive}"); summary.AppendLine($"{indent}Forwarding Requests: {Status.AllowRequestForwarding}"); - summary.AppendLine($"{indent}Accepting Requests: {Status.AllowRequestForwarding}"); + summary.AppendLine($"{indent}Accepting Requests: {Status.AcceptForwardedRequests}"); if (Status.KnownHostnames is not null) { diff --git a/src/server/Startup.cs b/src/server/Startup.cs index d2864c1..3db3bc5 100644 --- a/src/server/Startup.cs +++ b/src/server/Startup.cs @@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -137,6 +138,16 @@ public void Configure(IApplicationBuilder app, _versionConfig = versionConfig.Value; _logger = logger; + // Enable request body buffering globally, as early as possible, so any code + // downstream (in particular ProxyController's mesh-forwarding logic) can safely + // re-read/replay the request body even after ASP.NET Core's own model-binding + // infrastructure has already read from it once. + app.Use(async (context, next) => + { + context.Request.EnableBuffering(); + await next(); + }); + if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); diff --git a/src/server/wwwroot/assets/dashboard.js b/src/server/wwwroot/assets/dashboard.js index a8902db..84b9605 100755 --- a/src/server/wwwroot/assets/dashboard.js +++ b/src/server/wwwroot/assets/dashboard.js @@ -546,7 +546,7 @@ function meshServerSummary(server, isLocal) { summary += `${indent}All Addresses: ${server.allIPAddresses.join(", ")}\n`; summary += `${indent}Active: ${activeStr(server.isActive)}\n`; summary += `${indent}Forwarding Requests: ${activeStr(server.status.allowRequestForwarding)}\n`; - summary += `${indent}Accepting Requests: ${activeStr(server.status.allowRequestForwarding)}\n`; + summary += `${indent}Accepting Requests: ${activeStr(server.status.acceptForwardedRequests)}\n`; // Routes (may be an empty list) let hideRoutes = document.getElementById("showMeshRoutes") &&