eabf270d71
Resolve all 622 issues flagged by the enhanced CommentChecker: add missing <returns> tags (incl. the standard phrasing on non-generic Task methods), add missing <summary> tags, and replace misused/redundant <inheritdoc/> on members that override or implement nothing with real documentation. Documentation-only — no behavior change; solution builds clean.
86 lines
3.8 KiB
C#
86 lines
3.8 KiB
C#
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.AspNetCore.Http.Features;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Options;
|
|
|
|
namespace ZB.MOM.WW.ScadaBridge.InboundAPI;
|
|
|
|
/// <summary>
|
|
/// Endpoint filter applied to <c>POST /api/{methodName}</c> that enforces two
|
|
/// cross-cutting guards before the request handler runs:
|
|
///
|
|
/// <list type="bullet">
|
|
/// <item><description>
|
|
/// InboundAPI-008 — active-node gating. The inbound API is central-active-node-only;
|
|
/// a standby node returns HTTP 503 so it never executes method scripts.
|
|
/// </description></item>
|
|
/// <item><description>
|
|
/// InboundAPI-006 — request body size cap. Oversized bodies are rejected with HTTP
|
|
/// 413 before being buffered into a <c>JsonDocument</c>.
|
|
/// </description></item>
|
|
/// </list>
|
|
/// </summary>
|
|
public sealed class InboundApiEndpointFilter : IEndpointFilter
|
|
{
|
|
private readonly ILogger<InboundApiEndpointFilter> _logger;
|
|
private readonly InboundApiOptions _options;
|
|
|
|
/// <summary>Initializes a new <see cref="InboundApiEndpointFilter"/> with the given logger and options.</summary>
|
|
/// <param name="logger">Logger for request rejection diagnostics.</param>
|
|
/// <param name="options">Inbound API options including the maximum request body size.</param>
|
|
public InboundApiEndpointFilter(
|
|
ILogger<InboundApiEndpointFilter> logger,
|
|
IOptions<InboundApiOptions> options)
|
|
{
|
|
_logger = logger;
|
|
_options = options.Value;
|
|
}
|
|
|
|
/// <summary>Applies active-node gating and request body size checks before delegating to the next filter or handler.</summary>
|
|
/// <param name="context">The endpoint filter invocation context containing the HTTP context and arguments.</param>
|
|
/// <param name="next">The next filter or endpoint handler in the pipeline.</param>
|
|
/// <returns>A value task that resolves to the filter pipeline result, or an error result if gating fails.</returns>
|
|
public async ValueTask<object?> InvokeAsync(
|
|
EndpointFilterInvocationContext context,
|
|
EndpointFilterDelegate next)
|
|
{
|
|
var httpContext = context.HttpContext;
|
|
|
|
// InboundAPI-008: refuse to serve the inbound API on a standby central node.
|
|
// The gate is optional — when no IActiveNodeGate is registered (non-clustered
|
|
// host / tests) the API is served, preserving prior behaviour.
|
|
var gate = httpContext.RequestServices.GetService<IActiveNodeGate>();
|
|
if (gate is { IsActiveNode: false })
|
|
{
|
|
_logger.LogWarning(
|
|
"Inbound API request rejected — this node is a standby (not the active central node)");
|
|
return Results.Json(
|
|
new { error = "Inbound API is only available on the active central node" },
|
|
statusCode: StatusCodes.Status503ServiceUnavailable);
|
|
}
|
|
|
|
// InboundAPI-006: cap the request body size. Reject an over-limit body up
|
|
// front via Content-Length; also lower the per-request max body size so a
|
|
// chunked/unknown-length stream is cut off by Kestrel as it is read.
|
|
var maxBytes = _options.MaxRequestBodyBytes;
|
|
if (httpContext.Request.ContentLength is { } declaredLength && declaredLength > maxBytes)
|
|
{
|
|
_logger.LogWarning(
|
|
"Inbound API request rejected — body length {Length} exceeds limit {Limit}",
|
|
declaredLength, maxBytes);
|
|
return Results.Json(
|
|
new { error = "Request body too large" },
|
|
statusCode: StatusCodes.Status413PayloadTooLarge);
|
|
}
|
|
|
|
var sizeFeature = httpContext.Features.Get<IHttpMaxRequestBodySizeFeature>();
|
|
if (sizeFeature is { IsReadOnly: false })
|
|
{
|
|
sizeFeature.MaxRequestBodySize = maxBytes;
|
|
}
|
|
|
|
return await next(context);
|
|
}
|
|
}
|