[backend/federation] Add short-lived cache for non-specific federation requests

This commit is contained in:
Laura Hausmann 2024-11-14 18:58:29 +01:00
parent 762278426f
commit ff59b07035
No known key found for this signature in database
GPG key ID: D044E84C5BE01605
4 changed files with 27 additions and 0 deletions

View file

@ -12,6 +12,7 @@ using Iceshrimp.Backend.Core.Middleware;
using Iceshrimp.Backend.Core.Queues; using Iceshrimp.Backend.Core.Queues;
using Iceshrimp.Backend.Core.Services; using Iceshrimp.Backend.Core.Services;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.OutputCaching;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
@ -108,6 +109,7 @@ public class ActivityPubController(
[HttpGet("/users/{id}")] [HttpGet("/users/{id}")]
[AuthorizedFetch] [AuthorizedFetch]
[OutputCache(PolicyName = "federation")]
[MediaTypeRouteFilter("application/activity+json", "application/ld+json")] [MediaTypeRouteFilter("application/activity+json", "application/ld+json")]
[OverrideResultType<ASActor>] [OverrideResultType<ASActor>]
[ProducesResults(HttpStatusCode.OK, HttpStatusCode.MovedPermanently)] [ProducesResults(HttpStatusCode.OK, HttpStatusCode.MovedPermanently)]
@ -129,6 +131,7 @@ public class ActivityPubController(
[HttpGet("/users/{id}/collections/featured")] [HttpGet("/users/{id}/collections/featured")]
[AuthorizedFetch] [AuthorizedFetch]
[OutputCache(PolicyName = "federation")]
[OverrideResultType<ASOrderedCollection>] [OverrideResultType<ASOrderedCollection>]
[ProducesResults(HttpStatusCode.OK)] [ProducesResults(HttpStatusCode.OK)]
[ProducesErrors(HttpStatusCode.NotFound)] [ProducesErrors(HttpStatusCode.NotFound)]
@ -159,6 +162,7 @@ public class ActivityPubController(
[HttpGet("/@{acct}")] [HttpGet("/@{acct}")]
[AuthorizedFetch] [AuthorizedFetch]
[OutputCache(PolicyName = "federation")]
[MediaTypeRouteFilter("application/activity+json", "application/ld+json")] [MediaTypeRouteFilter("application/activity+json", "application/ld+json")]
[OverrideResultType<ASActor>] [OverrideResultType<ASActor>]
[ProducesResults(HttpStatusCode.OK, HttpStatusCode.MovedPermanently)] [ProducesResults(HttpStatusCode.OK, HttpStatusCode.MovedPermanently)]
@ -212,6 +216,7 @@ public class ActivityPubController(
[HttpGet("/emoji/{name}")] [HttpGet("/emoji/{name}")]
[AuthorizedFetch] [AuthorizedFetch]
[OutputCache(PolicyName = "federation")]
[MediaTypeRouteFilter("application/activity+json", "application/ld+json")] [MediaTypeRouteFilter("application/activity+json", "application/ld+json")]
[OverrideResultType<ASEmoji>] [OverrideResultType<ASEmoji>]
[ProducesResults(HttpStatusCode.OK)] [ProducesResults(HttpStatusCode.OK)]

View file

@ -9,6 +9,7 @@ using Iceshrimp.Backend.Core.Federation.WebFinger;
using Iceshrimp.Backend.Core.Middleware; using Iceshrimp.Backend.Core.Middleware;
using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.OutputCaching;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@ -17,6 +18,7 @@ namespace Iceshrimp.Backend.Controllers.Federation;
[FederationApiController] [FederationApiController]
[Route("/.well-known")] [Route("/.well-known")]
[EnableCors("well-known")] [EnableCors("well-known")]
[OutputCache(PolicyName = "federation")]
public class WellKnownController(IOptions<Config.InstanceSection> config, DatabaseContext db) : ControllerBase public class WellKnownController(IOptions<Config.InstanceSection> config, DatabaseContext db) : ControllerBase
{ {
[HttpGet("webfinger")] [HttpGet("webfinger")]

View file

@ -374,10 +374,22 @@ public static class ServiceExtensions
options.AddScheme<IAuthenticationHandler>("StubAuthenticationHandler", null); options.AddScheme<IAuthenticationHandler>("StubAuthenticationHandler", null);
}); });
} }
public static void AddOutputCacheWithOptions(this IServiceCollection services)
{
services.AddOutputCache(options =>
{
options.AddPolicy("conditional", o => o.With(ctx => ctx.HttpContext.ShouldCacheOutput()));
options.AddPolicy("federation", o => o.SetVaryByHeader("Accept").Expire(TimeSpan.FromSeconds(60)));
options.DefaultExpirationTimeSpan = TimeSpan.FromDays(365);
});
}
} }
public static partial class HttpContextExtensions public static partial class HttpContextExtensions
{ {
private const string CacheKey = "shouldCache";
public static string GetRateLimitPartition(this HttpContext ctx, bool includeRoute) => public static string GetRateLimitPartition(this HttpContext ctx, bool includeRoute) =>
(includeRoute ? ctx.Request.Path.ToString() + "#" : "") + (GetRateLimitPartitionInternal(ctx) ?? ""); (includeRoute ? ctx.Request.Path.ToString() + "#" : "") + (GetRateLimitPartitionInternal(ctx) ?? "");
@ -385,6 +397,11 @@ public static partial class HttpContextExtensions
ctx.GetUser()?.Id ?? ctx.GetUser()?.Id ??
ctx.Request.Headers["X-Forwarded-For"].FirstOrDefault() ?? ctx.Request.Headers["X-Forwarded-For"].FirstOrDefault() ??
ctx.Connection.RemoteIpAddress?.ToString(); ctx.Connection.RemoteIpAddress?.ToString();
public static void CacheOutput(this HttpContext ctx) => ctx.Items[CacheKey] = true;
public static bool ShouldCacheOutput(this HttpContext ctx) =>
ctx.Items.TryGetValue(CacheKey, out var s) && s is true;
} }
#region AsyncDataProtection handlers #region AsyncDataProtection handlers

View file

@ -24,6 +24,7 @@ builder.Services
.AddPlugins(PluginLoader.Assemblies); .AddPlugins(PluginLoader.Assemblies);
builder.Services.AddSwaggerGenWithOptions(); builder.Services.AddSwaggerGenWithOptions();
builder.Services.AddOutputCacheWithOptions();
builder.Services.AddLogging(logging => logging.AddCustomConsoleFormatter()); builder.Services.AddLogging(logging => logging.AddCustomConsoleFormatter());
builder.Services.AddDatabaseContext(builder.Configuration); builder.Services.AddDatabaseContext(builder.Configuration);
builder.Services.AddSlidingWindowRateLimiter(); builder.Services.AddSlidingWindowRateLimiter();
@ -38,6 +39,7 @@ builder.Services.AddAntiforgery(o => o.Cookie.Name = "CSRF-Token");
builder.Services.AddServices(builder.Configuration); builder.Services.AddServices(builder.Configuration);
builder.Services.ConfigureServices(builder.Configuration); builder.Services.ConfigureServices(builder.Configuration);
builder.WebHost.ConfigureKestrel(builder.Configuration); builder.WebHost.ConfigureKestrel(builder.Configuration);
builder.WebHost.UseStaticWebAssets(); builder.WebHost.UseStaticWebAssets();
@ -64,6 +66,7 @@ app.UseAuthorization();
app.UseWebSockets(new WebSocketOptions { KeepAliveInterval = TimeSpan.FromSeconds(30) }); app.UseWebSockets(new WebSocketOptions { KeepAliveInterval = TimeSpan.FromSeconds(30) });
app.UseCustomMiddleware(); app.UseCustomMiddleware();
app.UseAntiforgery(); app.UseAntiforgery();
app.UseOutputCache();
app.MapStaticAssetsWithTransparentDecompression(); app.MapStaticAssetsWithTransparentDecompression();
app.MapControllers(); app.MapControllers();