[backend/core] Fix long application exit times by passing IHostApplicationLifetime.ApplicationStopping to long-running awaits

This commit is contained in:
Laura Hausmann 2024-02-21 03:50:38 +01:00
parent 5da6b39f33
commit 1c8df9879d
No known key found for this signature in database
GPG key ID: D044E84C5BE01605
3 changed files with 15 additions and 11 deletions

View file

@ -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");

View file

@ -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<WebFingerResponse?> 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"]);

View file

@ -23,7 +23,8 @@ public class AuthorizedFetchMiddleware(
UserService userSvc,
SystemUserService systemUserSvc,
ActivityPub.FederationControlService fedCtrlSvc,
ILogger<AuthorizedFetchMiddleware> logger
ILogger<AuthorizedFetchMiddleware> 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<JObject?>(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}");