Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions CodeProject.AI.sln
Original file line number Diff line number Diff line change
@@ -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
Expand Down
26 changes: 20 additions & 6 deletions src/server/Controllers/ProxyController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -65,6 +66,7 @@ public class ProxyController : ControllerBase
private readonly TriggerTaskRunner _commandRunner;
private readonly MeshManager _meshManager;
private readonly ModuleProcessServices _moduleProcessService;
private readonly ILogger<ProxyController> _logger;

private bool _verbose = false;

Expand All @@ -79,13 +81,15 @@ public class ProxyController : ControllerBase
/// <param name="commandRunner">The command runner</param>
/// <param name="meshManager">The mesh manager</param>
/// <param name="moduleProcessService">The module process service</param>
/// <param name="logger">The logger</param>
public ProxyController(CommandDispatcher dispatcher,
BackendRouteMap routeMap,
IOptions<ModuleCollection> ModuleCollectionOptions,
IOptions<TriggersConfig> triggersConfig,
TriggerTaskRunner commandRunner,
MeshManager meshManager,
ModuleProcessServices moduleProcessService)
ModuleProcessServices moduleProcessService,
ILogger<ProxyController> logger)
{
_dispatcher = dispatcher;
_routeMap = routeMap;
Expand All @@ -94,6 +98,7 @@ public ProxyController(CommandDispatcher dispatcher,
_commandRunner = commandRunner;
_meshManager = meshManager;
_moduleProcessService = moduleProcessService;
_logger = logger;
}

/// <summary>
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -521,6 +525,13 @@ public IActionResult ApiSummary()
private async Task<HttpResponseMessage> 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;
Expand All @@ -538,7 +549,10 @@ private async Task<HttpResponseMessage> ForwardAsync(MeshServerRoutingEntry serv
};

foreach (KeyValuePair<string, Microsoft.Extensions.Primitives.StringValues> 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");

Expand Down
2 changes: 1 addition & 1 deletion src/server/Mesh/MeshSummary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
11 changes: 11 additions & 0 deletions src/server/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion src/server/wwwroot/assets/dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -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") &&
Expand Down