From 1c8df9879d2543070445be972519a8ec0858ae6b Mon Sep 17 00:00:00 2001 From: Laura Hausmann Date: Wed, 21 Feb 2024 03:50:38 +0100 Subject: [PATCH] [backend/core] Fix long application exit times by passing IHostApplicationLifetime.ApplicationStopping to long-running awaits --- .../Federation/ActivityPub/UserResolver.cs | 2 +- .../Federation/WebFinger/WebFingerService.cs | 4 ++-- .../Middleware/AuthorizedFetchMiddleware.cs | 20 +++++++++++-------- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/Iceshrimp.Backend/Core/Federation/ActivityPub/UserResolver.cs b/Iceshrimp.Backend/Core/Federation/ActivityPub/UserResolver.cs index e3174e66..14772301 100644 --- a/Iceshrimp.Backend/Core/Federation/ActivityPub/UserResolver.cs +++ b/Iceshrimp.Backend/Core/Federation/ActivityPub/UserResolver.cs @@ -65,7 +65,7 @@ public class UserResolver( } var finalAcct = fingerRes.Subject; - var finalUri = fingerRes.Links.FirstOrDefault(p => p.Rel == "self" && p.Type == "application/activity+json") + var finalUri = fingerRes.Links.FirstOrDefault(p => p is { Rel: "self", Type: "application/activity+json" }) ?.Href ?? throw new GracefulException("Final AP URI was null"); diff --git a/Iceshrimp.Backend/Core/Federation/WebFinger/WebFingerService.cs b/Iceshrimp.Backend/Core/Federation/WebFinger/WebFingerService.cs index 339f7be0..c3ce163b 100644 --- a/Iceshrimp.Backend/Core/Federation/WebFinger/WebFingerService.cs +++ b/Iceshrimp.Backend/Core/Federation/WebFinger/WebFingerService.cs @@ -19,14 +19,14 @@ namespace Iceshrimp.Backend.Core.Federation.WebFinger; //FIXME: handle cursed person/group acct collisions like https://lemmy.ml/.well-known/webfinger?resource=acct:linux@lemmy.ml //FIXME: also check if the query references the local instance in other ways (e.g. @user@{WebDomain}, @user@{AccountDomain}, https://{WebDomain}/..., etc) -public class WebFingerService(HttpClient client, HttpRequestService httpRqSvc) +public class WebFingerService(HttpClient client, HttpRequestService httpRqSvc, IHostApplicationLifetime appLifetime) { public async Task ResolveAsync(string query) { (query, var proto, var domain) = ParseQuery(query); var webFingerUrl = await GetWebFingerUrlAsync(query, proto, domain); - using var cts = new CancellationTokenSource(); + using var cts = CancellationTokenSource.CreateLinkedTokenSource(appLifetime.ApplicationStopping); cts.CancelAfter(TimeSpan.FromSeconds(10)); var req = httpRqSvc.Get(webFingerUrl, ["application/jrd+json", "application/json"]); diff --git a/Iceshrimp.Backend/Core/Middleware/AuthorizedFetchMiddleware.cs b/Iceshrimp.Backend/Core/Middleware/AuthorizedFetchMiddleware.cs index 75011880..e027921f 100644 --- a/Iceshrimp.Backend/Core/Middleware/AuthorizedFetchMiddleware.cs +++ b/Iceshrimp.Backend/Core/Middleware/AuthorizedFetchMiddleware.cs @@ -23,7 +23,8 @@ public class AuthorizedFetchMiddleware( UserService userSvc, SystemUserService systemUserSvc, ActivityPub.FederationControlService fedCtrlSvc, - ILogger logger + ILogger logger, + IHostApplicationLifetime appLifetime ) : IMiddleware { private static readonly JsonSerializerSettings JsonSerializerSettings = @@ -36,6 +37,7 @@ public class AuthorizedFetchMiddleware( if (attribute != null && config.Value.AuthorizedFetch) { var request = ctx.Request; + var ct = appLifetime.ApplicationStopping; //TODO: cache this somewhere var instanceActorUri = $"/users/{(await systemUserSvc.GetInstanceActorAsync()).Id}"; @@ -62,15 +64,17 @@ public class AuthorizedFetchMiddleware( supressLog: true); // First, we check if we already have the key - key = await db.UserPublickeys.Include(p => p.User).FirstOrDefaultAsync(p => p.KeyId == sig.KeyId); + key = await db.UserPublickeys.Include(p => p.User) + .FirstOrDefaultAsync(p => p.KeyId == sig.KeyId, cancellationToken: ct); // If we don't, we need to try to fetch it if (key == null) { try { - var user = await userResolver.ResolveAsync(sig.KeyId); - key = await db.UserPublickeys.Include(p => p.User).FirstOrDefaultAsync(p => p.User == user); + var user = await userResolver.ResolveAsync(sig.KeyId).WaitAsync(ct); + key = await db.UserPublickeys.Include(p => p.User) + .FirstOrDefaultAsync(p => p.User == user, cancellationToken: ct); } catch (Exception e) { @@ -121,7 +125,7 @@ public class AuthorizedFetchMiddleware( if (!ActivityPub.ActivityFetcherService.IsValidActivityContentType(contentType)) throw new Exception("Request body is not an activity"); - var body = await new StreamReader(request.Body).ReadToEndAsync(); + var body = await new StreamReader(request.Body).ReadToEndAsync(ct); request.Body.Seek(0, SeekOrigin.Begin); var deserialized = JsonConvert.DeserializeObject(body); var expanded = LdHelpers.Expand(deserialized); @@ -140,14 +144,14 @@ public class AuthorizedFetchMiddleware( key = null; key = await db.UserPublickeys .Include(p => p.User) - .FirstOrDefaultAsync(p => p.User.Uri == activity.Actor.Id); + .FirstOrDefaultAsync(p => p.User.Uri == activity.Actor.Id, cancellationToken: ct); if (key == null) { - var user = await userResolver.ResolveAsync(activity.Actor.Id); + var user = await userResolver.ResolveAsync(activity.Actor.Id).WaitAsync(ct); key = await db.UserPublickeys .Include(p => p.User) - .FirstOrDefaultAsync(p => p.User == user); + .FirstOrDefaultAsync(p => p.User == user, cancellationToken: ct); if (key == null) throw new Exception($"Failed to fetch public key for user {activity.Actor.Id}");