[backend/core] Fix long application exit times by passing IHostApplicationLifetime.ApplicationStopping to long-running awaits
This commit is contained in:
parent
5da6b39f33
commit
1c8df9879d
3 changed files with 15 additions and 11 deletions
|
@ -65,7 +65,7 @@ public class UserResolver(
|
||||||
}
|
}
|
||||||
|
|
||||||
var finalAcct = fingerRes.Subject;
|
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 ??
|
?.Href ??
|
||||||
throw new GracefulException("Final AP URI was null");
|
throw new GracefulException("Final AP URI was null");
|
||||||
|
|
||||||
|
|
|
@ -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: 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)
|
//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)
|
public async Task<WebFingerResponse?> ResolveAsync(string query)
|
||||||
{
|
{
|
||||||
(query, var proto, var domain) = ParseQuery(query);
|
(query, var proto, var domain) = ParseQuery(query);
|
||||||
var webFingerUrl = await GetWebFingerUrlAsync(query, proto, domain);
|
var webFingerUrl = await GetWebFingerUrlAsync(query, proto, domain);
|
||||||
|
|
||||||
using var cts = new CancellationTokenSource();
|
using var cts = CancellationTokenSource.CreateLinkedTokenSource(appLifetime.ApplicationStopping);
|
||||||
cts.CancelAfter(TimeSpan.FromSeconds(10));
|
cts.CancelAfter(TimeSpan.FromSeconds(10));
|
||||||
|
|
||||||
var req = httpRqSvc.Get(webFingerUrl, ["application/jrd+json", "application/json"]);
|
var req = httpRqSvc.Get(webFingerUrl, ["application/jrd+json", "application/json"]);
|
||||||
|
|
|
@ -23,7 +23,8 @@ public class AuthorizedFetchMiddleware(
|
||||||
UserService userSvc,
|
UserService userSvc,
|
||||||
SystemUserService systemUserSvc,
|
SystemUserService systemUserSvc,
|
||||||
ActivityPub.FederationControlService fedCtrlSvc,
|
ActivityPub.FederationControlService fedCtrlSvc,
|
||||||
ILogger<AuthorizedFetchMiddleware> logger
|
ILogger<AuthorizedFetchMiddleware> logger,
|
||||||
|
IHostApplicationLifetime appLifetime
|
||||||
) : IMiddleware
|
) : IMiddleware
|
||||||
{
|
{
|
||||||
private static readonly JsonSerializerSettings JsonSerializerSettings =
|
private static readonly JsonSerializerSettings JsonSerializerSettings =
|
||||||
|
@ -36,6 +37,7 @@ public class AuthorizedFetchMiddleware(
|
||||||
if (attribute != null && config.Value.AuthorizedFetch)
|
if (attribute != null && config.Value.AuthorizedFetch)
|
||||||
{
|
{
|
||||||
var request = ctx.Request;
|
var request = ctx.Request;
|
||||||
|
var ct = appLifetime.ApplicationStopping;
|
||||||
|
|
||||||
//TODO: cache this somewhere
|
//TODO: cache this somewhere
|
||||||
var instanceActorUri = $"/users/{(await systemUserSvc.GetInstanceActorAsync()).Id}";
|
var instanceActorUri = $"/users/{(await systemUserSvc.GetInstanceActorAsync()).Id}";
|
||||||
|
@ -62,15 +64,17 @@ public class AuthorizedFetchMiddleware(
|
||||||
supressLog: true);
|
supressLog: true);
|
||||||
|
|
||||||
// First, we check if we already have the key
|
// 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 we don't, we need to try to fetch it
|
||||||
if (key == null)
|
if (key == null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var user = await userResolver.ResolveAsync(sig.KeyId);
|
var user = await userResolver.ResolveAsync(sig.KeyId).WaitAsync(ct);
|
||||||
key = await db.UserPublickeys.Include(p => p.User).FirstOrDefaultAsync(p => p.User == user);
|
key = await db.UserPublickeys.Include(p => p.User)
|
||||||
|
.FirstOrDefaultAsync(p => p.User == user, cancellationToken: ct);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
@ -121,7 +125,7 @@ public class AuthorizedFetchMiddleware(
|
||||||
if (!ActivityPub.ActivityFetcherService.IsValidActivityContentType(contentType))
|
if (!ActivityPub.ActivityFetcherService.IsValidActivityContentType(contentType))
|
||||||
throw new Exception("Request body is not an activity");
|
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);
|
request.Body.Seek(0, SeekOrigin.Begin);
|
||||||
var deserialized = JsonConvert.DeserializeObject<JObject?>(body);
|
var deserialized = JsonConvert.DeserializeObject<JObject?>(body);
|
||||||
var expanded = LdHelpers.Expand(deserialized);
|
var expanded = LdHelpers.Expand(deserialized);
|
||||||
|
@ -140,14 +144,14 @@ public class AuthorizedFetchMiddleware(
|
||||||
key = null;
|
key = null;
|
||||||
key = await db.UserPublickeys
|
key = await db.UserPublickeys
|
||||||
.Include(p => p.User)
|
.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)
|
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
|
key = await db.UserPublickeys
|
||||||
.Include(p => p.User)
|
.Include(p => p.User)
|
||||||
.FirstOrDefaultAsync(p => p.User == user);
|
.FirstOrDefaultAsync(p => p.User == user, cancellationToken: ct);
|
||||||
|
|
||||||
if (key == null)
|
if (key == null)
|
||||||
throw new Exception($"Failed to fetch public key for user {activity.Actor.Id}");
|
throw new Exception($"Failed to fetch public key for user {activity.Actor.Id}");
|
||||||
|
|
Loading…
Add table
Reference in a new issue