diff --git a/src/Services.Core/Virtualization/BinaryMiddleware.cs b/src/Services.Core/Virtualization/BinaryMiddleware.cs index efc4ceb7a..dddb8783c 100644 --- a/src/Services.Core/Virtualization/BinaryMiddleware.cs +++ b/src/Services.Core/Virtualization/BinaryMiddleware.cs @@ -1,34 +1,133 @@ -using Microsoft.AspNetCore.Http; -using System.Threading.Tasks; -using SenseNet.Services.Core.Diagnostics; - -namespace SenseNet.Services.Core.Virtualization -{ - /// - /// ASP.NET Core middleware to process binary requests. - /// - public class BinaryMiddleware - { - private readonly RequestDelegate _next; - - public BinaryMiddleware(RequestDelegate next) - { - _next = next; - } - - public async Task InvokeAsync(HttpContext httpContext, WebTransferRegistrator statistics) - { - var statData = statistics?.RegisterWebRequest(httpContext); - - var bh = new BinaryHandler(httpContext); - - await bh.ProcessRequestCore().ConfigureAwait(false); - - statistics?.RegisterWebResponse(statData, httpContext); - - // Call next middleware in the chain if exists - if (_next != null) - await _next(httpContext).ConfigureAwait(false); - } - } -} +using Microsoft.AspNetCore.Cors.Infrastructure; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using System.Linq; +using System.Threading.Tasks; +using SenseNet.Services.Core.Diagnostics; + +namespace SenseNet.Services.Core.Virtualization +{ + /// + /// ASP.NET Core middleware to process binary requests. + /// + public class BinaryMiddleware + { + private readonly RequestDelegate _next; + private readonly ICorsPolicyProvider _corsPolicyProvider; + private readonly ILogger _logger; + + public BinaryMiddleware(RequestDelegate next, ICorsPolicyProvider corsPolicyProvider = null, ILogger logger = null) + { + _next = next; + _corsPolicyProvider = corsPolicyProvider; + _logger = logger; + } + + public async Task InvokeAsync(HttpContext httpContext, WebTransferRegistrator statistics) + { + _logger?.LogInformation("=== BinaryMiddleware START ==="); + _logger?.LogInformation("Request Path: {Path}", httpContext.Request.Path); + _logger?.LogInformation("Request Method: {Method}", httpContext.Request.Method); + + // Apply CORS policy manually since MapMiddlewareWhen creates a separate branch + // that bypasses the UseSenseNetCors middleware + if (_corsPolicyProvider != null) + { + _logger?.LogInformation("CORS Policy Provider is available"); + + var corsPolicy = await _corsPolicyProvider.GetPolicyAsync(httpContext, "sensenet"); + if (corsPolicy != null) + { + _logger?.LogInformation("CORS Policy retrieved successfully"); + _logger?.LogInformation("CORS Policy - AllowAnyOrigin: {AllowAnyOrigin}", corsPolicy.AllowAnyOrigin); + _logger?.LogInformation("CORS Policy - SupportsCredentials: {SupportsCredentials}", corsPolicy.SupportsCredentials); + _logger?.LogInformation("CORS Policy - Origins: {Origins}", string.Join(", ", corsPolicy.Origins)); + + // Apply CORS headers based on the policy + var origin = httpContext.Request.Headers["Origin"].FirstOrDefault(); + _logger?.LogInformation("Request Origin header: '{Origin}'", origin ?? "NULL"); + + if (!string.IsNullOrEmpty(origin)) + { + // Check if the origin is allowed (either matches exactly or policy allows any origin) + var isAllowed = corsPolicy.Origins.Contains(origin) || + corsPolicy.Origins.Contains("*") || + corsPolicy.AllowAnyOrigin; + + _logger?.LogInformation("Origin is allowed: {IsAllowed}", isAllowed); + + if (isAllowed) + { + // For any origin (*), we can only return * if credentials are NOT supported + if (corsPolicy.AllowAnyOrigin || corsPolicy.Origins.Contains("*")) + { + if (!corsPolicy.SupportsCredentials) + { + httpContext.Response.Headers.Append("Access-Control-Allow-Origin", "*"); + _logger?.LogInformation("Added CORS header: Access-Control-Allow-Origin = *"); + } + else + { + httpContext.Response.Headers.Append("Access-Control-Allow-Origin", origin); + _logger?.LogInformation("Added CORS header: Access-Control-Allow-Origin = {Origin}", origin); + } + } + else + { + httpContext.Response.Headers.Append("Access-Control-Allow-Origin", origin); + _logger?.LogInformation("Added CORS header: Access-Control-Allow-Origin = {Origin}", origin); + } + + if (corsPolicy.SupportsCredentials) + { + httpContext.Response.Headers.Append("Access-Control-Allow-Credentials", "true"); + _logger?.LogInformation("Added CORS header: Access-Control-Allow-Credentials = true"); + } + + if (corsPolicy.ExposedHeaders.Count > 0) + { + var exposedHeaders = string.Join(", ", corsPolicy.ExposedHeaders); + httpContext.Response.Headers.Append("Access-Control-Expose-Headers", exposedHeaders); + _logger?.LogInformation("Added CORS header: Access-Control-Expose-Headers = {Headers}", exposedHeaders); + } + } + else + { + _logger?.LogWarning("Origin '{Origin}' is NOT allowed by CORS policy", origin); + } + } + else + { + _logger?.LogInformation("No Origin header in request"); + } + } + else + { + _logger?.LogWarning("CORS Policy is NULL - could not retrieve policy"); + } + } + else + { + _logger?.LogWarning("CORS Policy Provider is NULL - CORS headers will NOT be added"); + } + + var statData = statistics?.RegisterWebRequest(httpContext); + + var bh = new BinaryHandler(httpContext); + + await bh.ProcessRequestCore().ConfigureAwait(false); + + _logger?.LogInformation("Response Status Code: {StatusCode}", httpContext.Response.StatusCode); + _logger?.LogInformation("Response Headers: {Headers}", + string.Join("; ", httpContext.Response.Headers.Select(h => $"{h.Key}={h.Value}"))); + + statistics?.RegisterWebResponse(statData, httpContext); + + _logger?.LogInformation("=== BinaryMiddleware END ==="); + + // Call next middleware in the chain if exists + if (_next != null) + await _next(httpContext).ConfigureAwait(false); + } + } +}