[backend/asp] Refactor middleware stack

This commit splits the request pipeline conditionally instead of invoking every middleware in the stack.

It also simplifies middleware instantiation by using runtime discovery, allowing for Plugins to add Middleware.
This commit is contained in:
Laura Hausmann 2024-11-17 01:09:15 +01:00
parent 70c692e1cb
commit 705e061f74
No known key found for this signature in database
GPG key ID: D044E84C5BE01605
15 changed files with 467 additions and 411 deletions

View file

@ -1,6 +1,8 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Threading.RateLimiting; using System.Threading.RateLimiting;
using System.Xml.Linq; using System.Xml.Linq;
using Iceshrimp.AssemblyUtils;
using Iceshrimp.Backend.Components.PublicPreview.Attributes; using Iceshrimp.Backend.Components.PublicPreview.Attributes;
using Iceshrimp.Backend.Components.PublicPreview.Renderers; using Iceshrimp.Backend.Components.PublicPreview.Renderers;
using Iceshrimp.Backend.Controllers.Federation; using Iceshrimp.Backend.Controllers.Federation;
@ -9,6 +11,7 @@ using Iceshrimp.Backend.Controllers.Web.Renderers;
using Iceshrimp.Backend.Core.Configuration; using Iceshrimp.Backend.Core.Configuration;
using Iceshrimp.Backend.Core.Database; using Iceshrimp.Backend.Core.Database;
using Iceshrimp.Backend.Core.Federation.WebFinger; using Iceshrimp.Backend.Core.Federation.WebFinger;
using Iceshrimp.Backend.Core.Helpers;
using Iceshrimp.Backend.Core.Helpers.LibMfm.Conversion; using Iceshrimp.Backend.Core.Helpers.LibMfm.Conversion;
using Iceshrimp.Backend.Core.Middleware; using Iceshrimp.Backend.Core.Middleware;
using Iceshrimp.Backend.Core.Services; using Iceshrimp.Backend.Core.Services;
@ -30,8 +33,6 @@ using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using AuthenticationMiddleware = Iceshrimp.Backend.Core.Middleware.AuthenticationMiddleware;
using AuthorizationMiddleware = Iceshrimp.Backend.Core.Middleware.AuthorizationMiddleware;
using NoteRenderer = Iceshrimp.Backend.Controllers.Web.Renderers.NoteRenderer; using NoteRenderer = Iceshrimp.Backend.Controllers.Web.Renderers.NoteRenderer;
using NotificationRenderer = Iceshrimp.Backend.Controllers.Web.Renderers.NotificationRenderer; using NotificationRenderer = Iceshrimp.Backend.Controllers.Web.Renderers.NotificationRenderer;
using UserRenderer = Iceshrimp.Backend.Controllers.Web.Renderers.UserRenderer; using UserRenderer = Iceshrimp.Backend.Controllers.Web.Renderers.UserRenderer;
@ -68,10 +69,6 @@ public static class ServiceExtensions
.AddScoped<BiteService>() .AddScoped<BiteService>()
.AddScoped<ImportExportService>() .AddScoped<ImportExportService>()
.AddScoped<UserProfileMentionsResolver>() .AddScoped<UserProfileMentionsResolver>()
.AddScoped<AuthorizedFetchMiddleware>()
.AddScoped<InboxValidationMiddleware>()
.AddScoped<AuthenticationMiddleware>()
.AddScoped<ErrorHandlerMiddleware>()
.AddScoped<Controllers.Mastodon.Renderers.UserRenderer>() .AddScoped<Controllers.Mastodon.Renderers.UserRenderer>()
.AddScoped<Controllers.Mastodon.Renderers.NoteRenderer>() .AddScoped<Controllers.Mastodon.Renderers.NoteRenderer>()
.AddScoped<Controllers.Mastodon.Renderers.NotificationRenderer>() .AddScoped<Controllers.Mastodon.Renderers.NotificationRenderer>()
@ -100,16 +97,10 @@ public static class ServiceExtensions
.AddSingleton<QueueService>() .AddSingleton<QueueService>()
.AddSingleton<ObjectStorageService>() .AddSingleton<ObjectStorageService>()
.AddSingleton<EventService>() .AddSingleton<EventService>()
.AddSingleton<RequestBufferingMiddleware>()
.AddSingleton<AuthorizationMiddleware>()
.AddSingleton<RequestVerificationMiddleware>()
.AddSingleton<RequestDurationMiddleware>()
.AddSingleton<FederationSemaphoreMiddleware>()
.AddSingleton<PushService>() .AddSingleton<PushService>()
.AddSingleton<StreamingService>() .AddSingleton<StreamingService>()
.AddSingleton<ImageProcessor>() .AddSingleton<ImageProcessor>()
.AddSingleton<RazorViewRenderService>() .AddSingleton<RazorViewRenderService>()
.AddSingleton<BlazorSsrHandoffMiddleware>()
.AddSingleton<MfmRenderer>() .AddSingleton<MfmRenderer>()
.AddSingleton<MatcherPolicy, PublicPreviewRouteMatcher>() .AddSingleton<MatcherPolicy, PublicPreviewRouteMatcher>()
.AddSingleton<PolicyService>(); .AddSingleton<PolicyService>();
@ -139,6 +130,21 @@ public static class ServiceExtensions
services.AddHostedService<PushService>(provider => provider.GetRequiredService<PushService>()); services.AddHostedService<PushService>(provider => provider.GetRequiredService<PushService>());
} }
public static void AddMiddleware(this IServiceCollection services)
{
var types = PluginLoader
.Assemblies.Prepend(Assembly.GetExecutingAssembly())
.SelectMany(p => AssemblyLoader.GetImplementationsOfInterface(p, typeof(IMiddlewareService)));
foreach (var type in types)
{
if (type.GetProperty(nameof(IMiddlewareService.Lifetime))?.GetValue(null) is not ServiceLifetime lifetime)
continue;
services.Add(new ServiceDescriptor(type, type, lifetime));
}
}
public static void ConfigureServices(this IServiceCollection services, IConfiguration configuration) public static void ConfigureServices(this IServiceCollection services, IConfiguration configuration)
{ {
// @formatter:off // @formatter:off

View file

@ -1,3 +1,5 @@
using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Iceshrimp.Backend.Core.Configuration; using Iceshrimp.Backend.Core.Configuration;
using Iceshrimp.Backend.Core.Database; using Iceshrimp.Backend.Core.Database;
@ -32,6 +34,10 @@ public static class WebApplicationExtensions
.UseMiddleware<BlazorSsrHandoffMiddleware>(); .UseMiddleware<BlazorSsrHandoffMiddleware>();
} }
// Prevents conditional middleware from being invoked on non-matching requests
private static IApplicationBuilder UseMiddleware<T>(this IApplicationBuilder app) where T : IConditionalMiddleware
=> app.UseWhen(T.Predicate, builder => UseMiddlewareExtensions.UseMiddleware<T>(builder));
public static IApplicationBuilder UseOpenApiWithOptions(this WebApplication app) public static IApplicationBuilder UseOpenApiWithOptions(this WebApplication app)
{ {
app.MapSwagger("/openapi/{documentName}.{extension:regex(^(json|ya?ml)$)}") app.MapSwagger("/openapi/{documentName}.{extension:regex(^(json|ya?ml)$)}")
@ -306,3 +312,31 @@ public static class WebApplicationExtensions
static extern int chmod(string pathname, int mode); static extern int chmod(string pathname, int mode);
} }
} }
public interface IConditionalMiddleware
{
public static abstract bool Predicate(HttpContext ctx);
}
public interface IMiddlewareService : IMiddleware
{
public static abstract ServiceLifetime Lifetime { get; }
}
public class ConditionalMiddleware<T> : IConditionalMiddleware where T : Attribute
{
[SuppressMessage("ReSharper", "StaticMemberInGenericType", Justification = "Intended behavior")]
private static readonly ConcurrentDictionary<Endpoint, bool> Cache = [];
public static bool Predicate(HttpContext ctx)
=> ctx.GetEndpoint() is { } endpoint && Cache.GetOrAdd(endpoint, e => GetAttribute(e) != null);
private static T? GetAttribute(Endpoint? endpoint)
=> endpoint?.Metadata.GetMetadata<T>();
private static T? GetAttribute(HttpContext ctx)
=> GetAttribute(ctx.GetEndpoint());
protected static T GetAttributeOrFail(HttpContext ctx)
=> GetAttribute(ctx) ?? throw new Exception("Failed to get middleware filter attribute");
}

View file

@ -1,6 +1,7 @@
using Iceshrimp.Backend.Controllers.Mastodon.Attributes; using Iceshrimp.Backend.Controllers.Mastodon.Attributes;
using Iceshrimp.Backend.Core.Database; using Iceshrimp.Backend.Core.Database;
using Iceshrimp.Backend.Core.Database.Tables; using Iceshrimp.Backend.Core.Database.Tables;
using Iceshrimp.Backend.Core.Extensions;
using Iceshrimp.Backend.Core.Helpers; using Iceshrimp.Backend.Core.Helpers;
using Iceshrimp.Backend.Core.Helpers.LibMfm.Conversion; using Iceshrimp.Backend.Core.Helpers.LibMfm.Conversion;
using Iceshrimp.Backend.Core.Services; using Iceshrimp.Backend.Core.Services;
@ -9,15 +10,19 @@ using Microsoft.EntityFrameworkCore;
namespace Iceshrimp.Backend.Core.Middleware; namespace Iceshrimp.Backend.Core.Middleware;
public class AuthenticationMiddleware(DatabaseContext db, UserService userSvc, MfmConverter mfmConverter) : IMiddleware public class AuthenticationMiddleware(
DatabaseContext db,
UserService userSvc,
MfmConverter mfmConverter
) : ConditionalMiddleware<AuthenticateAttribute>, IMiddlewareService
{ {
public static ServiceLifetime Lifetime => ServiceLifetime.Scoped;
public async Task InvokeAsync(HttpContext ctx, RequestDelegate next) public async Task InvokeAsync(HttpContext ctx, RequestDelegate next)
{ {
var endpoint = ctx.GetEndpoint(); var endpoint = ctx.GetEndpoint();
var attribute = endpoint?.Metadata.GetMetadata<AuthenticateAttribute>(); var attribute = GetAttributeOrFail(ctx);
if (attribute != null)
{
var isBlazorSsr = endpoint?.Metadata.GetMetadata<RootComponentMetadata>() != null; var isBlazorSsr = endpoint?.Metadata.GetMetadata<RootComponentMetadata>() != null;
if (isBlazorSsr) if (isBlazorSsr)
{ {
@ -104,7 +109,6 @@ public class AuthenticationMiddleware(DatabaseContext db, UserService userSvc, M
userSvc.UpdateSessionMetadata(session); userSvc.UpdateSessionMetadata(session);
ctx.SetSession(session); ctx.SetSession(session);
} }
}
await next(ctx); await next(ctx);
} }

View file

@ -1,17 +1,16 @@
using Iceshrimp.Backend.Controllers.Mastodon.Attributes; using Iceshrimp.Backend.Controllers.Mastodon.Attributes;
using Iceshrimp.Backend.Core.Extensions;
using Iceshrimp.Backend.Core.Helpers; using Iceshrimp.Backend.Core.Helpers;
namespace Iceshrimp.Backend.Core.Middleware; namespace Iceshrimp.Backend.Core.Middleware;
public class AuthorizationMiddleware : IMiddleware public class AuthorizationMiddleware(RequestDelegate next) : ConditionalMiddleware<AuthorizeAttribute>
{ {
public async Task InvokeAsync(HttpContext ctx, RequestDelegate next) public async Task InvokeAsync(HttpContext ctx)
{ {
var endpoint = ctx.GetEndpoint(); var endpoint = ctx.GetEndpoint();
var attribute = endpoint?.Metadata.GetMetadata<AuthorizeAttribute>(); var attribute = GetAttributeOrFail(ctx);
if (attribute != null)
{
ctx.Response.Headers.CacheControl = "private, no-store"; ctx.Response.Headers.CacheControl = "private, no-store";
var isMastodon = endpoint?.Metadata.GetMetadata<MastodonApiControllerAttribute>() != null; var isMastodon = endpoint?.Metadata.GetMetadata<MastodonApiControllerAttribute>() != null;
@ -38,7 +37,6 @@ public class AuthorizationMiddleware : IMiddleware
if (attribute.ModeratorRole && session.User is { IsAdmin: false, IsModerator: false }) if (attribute.ModeratorRole && session.User is { IsAdmin: false, IsModerator: false })
throw GracefulException.Forbidden("This action is outside the authorized scopes"); throw GracefulException.Forbidden("This action is outside the authorized scopes");
} }
}
await next(ctx); await next(ctx);
} }

View file

@ -3,6 +3,7 @@ using System.Net;
using Iceshrimp.Backend.Core.Configuration; using Iceshrimp.Backend.Core.Configuration;
using Iceshrimp.Backend.Core.Database; using Iceshrimp.Backend.Core.Database;
using Iceshrimp.Backend.Core.Database.Tables; using Iceshrimp.Backend.Core.Database.Tables;
using Iceshrimp.Backend.Core.Extensions;
using Iceshrimp.Backend.Core.Federation.Cryptography; using Iceshrimp.Backend.Core.Federation.Cryptography;
using Iceshrimp.Backend.Core.Services; using Iceshrimp.Backend.Core.Services;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@ -21,14 +22,18 @@ public class AuthorizedFetchMiddleware(
ActivityPub.FederationControlService fedCtrlSvc, ActivityPub.FederationControlService fedCtrlSvc,
ILogger<AuthorizedFetchMiddleware> logger, ILogger<AuthorizedFetchMiddleware> logger,
IHostApplicationLifetime appLifetime IHostApplicationLifetime appLifetime
) : IMiddleware ) : ConditionalMiddleware<AuthorizedFetchAttribute>, IMiddlewareService
{ {
public static ServiceLifetime Lifetime => ServiceLifetime.Scoped;
public async Task InvokeAsync(HttpContext ctx, RequestDelegate next) public async Task InvokeAsync(HttpContext ctx, RequestDelegate next)
{ {
var attribute = ctx.GetEndpoint()?.Metadata.GetMetadata<AuthorizedFetchAttribute>(); if (!config.Value.AuthorizedFetch)
if (attribute != null && config.Value.AuthorizedFetch)
{ {
await next(ctx);
return;
}
ctx.Response.Headers.CacheControl = "private, no-store"; ctx.Response.Headers.CacheControl = "private, no-store";
var request = ctx.Request; var request = ctx.Request;
@ -121,7 +126,6 @@ public class AuthorizedFetchMiddleware(
throw new GracefulException(HttpStatusCode.Forbidden, "Request signature validation failed"); throw new GracefulException(HttpStatusCode.Forbidden, "Request signature validation failed");
ctx.SetActor(key.User); ctx.SetActor(key.User);
}
await next(ctx); await next(ctx);
} }

View file

@ -1,15 +1,16 @@
using System.Reflection; using System.Reflection;
using Iceshrimp.Backend.Core.Extensions;
using Microsoft.AspNetCore.Components.Endpoints; using Microsoft.AspNetCore.Components.Endpoints;
namespace Iceshrimp.Backend.Core.Middleware; namespace Iceshrimp.Backend.Core.Middleware;
public class BlazorSsrHandoffMiddleware : IMiddleware public class BlazorSsrHandoffMiddleware(RequestDelegate next) : ConditionalMiddleware<BlazorSsrAttribute>
{ {
public async Task InvokeAsync(HttpContext context, RequestDelegate next) public async Task InvokeAsync(HttpContext context)
{ {
var attribute = context.GetEndpoint() var attribute = context.GetEndpoint()
?.Metadata.GetMetadata<RootComponentMetadata>() ?.Metadata.GetMetadata<RootComponentMetadata>()
?.Type.GetCustomAttributes<RazorSsrAttribute>() ?.Type.GetCustomAttributes<BlazorSsrAttribute>()
.FirstOrDefault(); .FirstOrDefault();
if (attribute != null) if (attribute != null)
@ -34,4 +35,4 @@ public class BlazorSsrHandoffMiddleware : IMiddleware
} }
} }
public class RazorSsrAttribute : Attribute; public class BlazorSsrAttribute : Attribute;

View file

@ -15,11 +15,13 @@ namespace Iceshrimp.Backend.Core.Middleware;
public class ErrorHandlerMiddleware( public class ErrorHandlerMiddleware(
[SuppressMessage("ReSharper", "SuggestBaseTypeForParameterInConstructor")] [SuppressMessage("ReSharper", "SuggestBaseTypeForParameterInConstructor")]
IOptionsSnapshot<Config.SecuritySection> options, IOptionsMonitor<Config.SecuritySection> options,
ILoggerFactory loggerFactory, ILoggerFactory loggerFactory,
RazorViewRenderService razor RazorViewRenderService razor
) : IMiddleware ) : IMiddlewareService
{ {
public static ServiceLifetime Lifetime => ServiceLifetime.Singleton;
private static readonly XmlSerializer XmlSerializer = new(typeof(ErrorResponse)); private static readonly XmlSerializer XmlSerializer = new(typeof(ErrorResponse));
public async Task InvokeAsync(HttpContext ctx, RequestDelegate next) public async Task InvokeAsync(HttpContext ctx, RequestDelegate next)
@ -37,7 +39,7 @@ public class ErrorHandlerMiddleware(
type = type[..(type.IndexOf('>') + 1)]; type = type[..(type.IndexOf('>') + 1)];
var logger = loggerFactory.CreateLogger(type); var logger = loggerFactory.CreateLogger(type);
var verbosity = options.Value.ExceptionVerbosity; var verbosity = options.CurrentValue.ExceptionVerbosity;
if (ctx.Response.HasStarted) if (ctx.Response.HasStarted)
{ {

View file

@ -1,5 +1,6 @@
using System.Net; using System.Net;
using Iceshrimp.Backend.Core.Configuration; using Iceshrimp.Backend.Core.Configuration;
using Iceshrimp.Backend.Core.Extensions;
using Iceshrimp.Backend.Core.Helpers; using Iceshrimp.Backend.Core.Helpers;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@ -8,8 +9,10 @@ namespace Iceshrimp.Backend.Core.Middleware;
public class FederationSemaphoreMiddleware( public class FederationSemaphoreMiddleware(
IOptions<Config.PerformanceSection> config, IOptions<Config.PerformanceSection> config,
IHostApplicationLifetime appLifetime IHostApplicationLifetime appLifetime
) : IMiddleware ) : ConditionalMiddleware<FederationSemaphoreAttribute>, IMiddlewareService
{ {
public static ServiceLifetime Lifetime => ServiceLifetime.Singleton;
private readonly SemaphorePlus _semaphore = new(Math.Max(config.Value.FederationRequestHandlerConcurrency, 1)); private readonly SemaphorePlus _semaphore = new(Math.Max(config.Value.FederationRequestHandlerConcurrency, 1));
public async Task InvokeAsync(HttpContext ctx, RequestDelegate next) public async Task InvokeAsync(HttpContext ctx, RequestDelegate next)
@ -20,13 +23,6 @@ public class FederationSemaphoreMiddleware(
return; return;
} }
var attribute = ctx.GetEndpoint()?.Metadata.GetMetadata<FederationSemaphoreAttribute>();
if (attribute == null)
{
await next(ctx);
return;
}
try try
{ {
var cts = CancellationTokenSource var cts = CancellationTokenSource

View file

@ -4,6 +4,7 @@ using System.Net.Http.Headers;
using Iceshrimp.Backend.Core.Configuration; using Iceshrimp.Backend.Core.Configuration;
using Iceshrimp.Backend.Core.Database; using Iceshrimp.Backend.Core.Database;
using Iceshrimp.Backend.Core.Database.Tables; using Iceshrimp.Backend.Core.Database.Tables;
using Iceshrimp.Backend.Core.Extensions;
using Iceshrimp.Backend.Core.Federation.ActivityStreams; using Iceshrimp.Backend.Core.Federation.ActivityStreams;
using Iceshrimp.Backend.Core.Federation.ActivityStreams.Types; using Iceshrimp.Backend.Core.Federation.ActivityStreams.Types;
using Iceshrimp.Backend.Core.Federation.Cryptography; using Iceshrimp.Backend.Core.Federation.Cryptography;
@ -25,16 +26,14 @@ public class InboxValidationMiddleware(
ActivityPub.FederationControlService fedCtrlSvc, ActivityPub.FederationControlService fedCtrlSvc,
ILogger<InboxValidationMiddleware> logger, ILogger<InboxValidationMiddleware> logger,
IHostApplicationLifetime appLifetime IHostApplicationLifetime appLifetime
) : IMiddleware ) : ConditionalMiddleware<InboxValidationAttribute>, IMiddlewareService
{ {
public static ServiceLifetime Lifetime => ServiceLifetime.Scoped;
private static readonly JsonSerializerSettings JsonSerializerSettings = private static readonly JsonSerializerSettings JsonSerializerSettings =
new() { DateParseHandling = DateParseHandling.None }; new() { DateParseHandling = DateParseHandling.None };
public async Task InvokeAsync(HttpContext ctx, RequestDelegate next) public async Task InvokeAsync(HttpContext ctx, RequestDelegate next)
{
var attribute = ctx.GetEndpoint()?.Metadata.GetMetadata<InboxValidationAttribute>();
if (attribute != null)
{ {
var request = ctx.Request; var request = ctx.Request;
var ct = appLifetime.ApplicationStopping; var ct = appLifetime.ApplicationStopping;
@ -250,7 +249,6 @@ public class InboxValidationMiddleware(
throw new GracefulException(HttpStatusCode.Forbidden, "Request signature validation failed"); throw new GracefulException(HttpStatusCode.Forbidden, "Request signature validation failed");
ctx.SetActor(key.User); ctx.SetActor(key.User);
}
await next(ctx); await next(ctx);
} }

View file

@ -1,16 +1,22 @@
using Iceshrimp.Backend.Core.Extensions;
using JetBrains.Annotations;
namespace Iceshrimp.Backend.Core.Middleware; namespace Iceshrimp.Backend.Core.Middleware;
public class RequestBufferingMiddleware : IMiddleware [UsedImplicitly]
public class RequestBufferingMiddleware(RequestDelegate next) : ConditionalMiddleware<EnableRequestBufferingAttribute>
{ {
public async Task InvokeAsync(HttpContext ctx, RequestDelegate next) [UsedImplicitly]
public async Task InvokeAsync(HttpContext ctx)
{ {
var attribute = ctx.GetEndpoint()?.Metadata.GetMetadata<EnableRequestBufferingAttribute>(); var attr = GetAttributeOrFail(ctx);
if (attribute != null) ctx.Request.EnableBuffering(attribute.MaxLength); ctx.Request.EnableBuffering(attr.MaxLength);
await next(ctx); await next(ctx);
} }
} }
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class EnableRequestBufferingAttribute(long maxLength) : Attribute public class EnableRequestBufferingAttribute(long maxLength) : Attribute
{ {
internal long MaxLength = maxLength; internal readonly long MaxLength = maxLength;
} }

View file

@ -1,11 +1,14 @@
using System.Diagnostics; using System.Diagnostics;
using Iceshrimp.Backend.Core.Extensions; using Iceshrimp.Backend.Core.Extensions;
using JetBrains.Annotations;
namespace Iceshrimp.Backend.Core.Middleware; namespace Iceshrimp.Backend.Core.Middleware;
public class RequestDurationMiddleware : IMiddleware [UsedImplicitly]
public class RequestDurationMiddleware(RequestDelegate next)
{ {
public async Task InvokeAsync(HttpContext ctx, RequestDelegate next) [UsedImplicitly]
public async Task InvokeAsync(HttpContext ctx)
{ {
if (ctx.GetEndpoint()?.Metadata.GetMetadata<HideRequestDuration>() == null) if (ctx.GetEndpoint()?.Metadata.GetMetadata<HideRequestDuration>() == null)
{ {

View file

@ -1,4 +1,5 @@
using Iceshrimp.Backend.Core.Configuration; using Iceshrimp.Backend.Core.Configuration;
using Iceshrimp.Backend.Core.Extensions;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
namespace Iceshrimp.Backend.Core.Middleware; namespace Iceshrimp.Backend.Core.Middleware;
@ -7,8 +8,10 @@ public class RequestVerificationMiddleware(
IOptions<Config.InstanceSection> config, IOptions<Config.InstanceSection> config,
IHostEnvironment environment, IHostEnvironment environment,
ILogger<RequestVerificationMiddleware> logger ILogger<RequestVerificationMiddleware> logger
) : IMiddleware ) : IMiddlewareService
{ {
public static ServiceLifetime Lifetime => ServiceLifetime.Singleton;
private readonly bool _isDevelopment = environment.IsDevelopment(); private readonly bool _isDevelopment = environment.IsDevelopment();
public async Task InvokeAsync(HttpContext ctx, RequestDelegate next) public async Task InvokeAsync(HttpContext ctx, RequestDelegate next)

View file

@ -47,7 +47,7 @@
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" /> <PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
<PackageReference Include="System.Text.Json" Version="9.0.0" /> <PackageReference Include="System.Text.Json" Version="9.0.0" />
<PackageReference Include="Ulid" Version="1.3.4" /> <PackageReference Include="Ulid" Version="1.3.4" />
<PackageReference Include="Iceshrimp.AssemblyUtils" Version="1.0.1" /> <PackageReference Include="Iceshrimp.AssemblyUtils" Version="1.0.2" />
<PackageReference Include="Iceshrimp.MimeTypes" Version="1.0.1" /> <PackageReference Include="Iceshrimp.MimeTypes" Version="1.0.1" />
<PackageReference Include="Iceshrimp.WebPush" Version="2.1.0" /> <PackageReference Include="Iceshrimp.WebPush" Version="2.1.0" />
<PackageReference Include="NetVips" Version="3.0.0" /> <PackageReference Include="NetVips" Version="3.0.0" />

View file

@ -7,7 +7,7 @@
@using Microsoft.AspNetCore.Components.Routing @using Microsoft.AspNetCore.Components.Routing
@inject IOptions<Config.InstanceSection> Instance @inject IOptions<Config.InstanceSection> Instance
@preservewhitespace true @preservewhitespace true
@attribute [RazorSsr] @attribute [BlazorSsr]
@inherits AsyncComponentBase @inherits AsyncComponentBase
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">

View file

@ -37,6 +37,7 @@ builder.Services.AddResponseCompression();
builder.Services.AddRazorPages(); builder.Services.AddRazorPages();
builder.Services.AddRazorComponents(); builder.Services.AddRazorComponents();
builder.Services.AddAntiforgery(o => o.Cookie.Name = "CSRF-Token"); builder.Services.AddAntiforgery(o => o.Cookie.Name = "CSRF-Token");
builder.Services.AddMiddleware();
builder.Services.AddServices(builder.Configuration); builder.Services.AddServices(builder.Configuration);
builder.Services.ConfigureServices(builder.Configuration); builder.Services.ConfigureServices(builder.Configuration);