Skip to content

Commit

Permalink
Outbox error handler
Browse files Browse the repository at this point in the history
  • Loading branch information
dahlia committed Apr 9, 2024
1 parent 8f8058f commit 5540b5c
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 4 deletions.
5 changes: 5 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ To be released.
- `Context.sendActivity()` method now throws `TypeError` instead of silently
failing when the given `Activity` object lacks the actor property.

- Added outbox error handler to `Federation`.

- Added `onOutboxError` option to `new Federation()` constructor.
- Added `OutboxErrorHandler` type.

[Hono]: https://hono.dev/
[#25]: https://github.com/dahlia/fedify/issues/25
[#27]: https://github.com/dahlia/fedify/issues/27
Expand Down
13 changes: 13 additions & 0 deletions federation/callback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,16 @@ export type InboxErrorHandler<TContextData> = (
context: RequestContext<TContextData>,
error: Error,
) => void | Promise<void>;

/**
* A callback that handles errors during outbox processing.
*
* @param error The error that occurred.
* @param activity The activity that caused the error. If it is `null`, the
* error occurred during deserializing the activity.
* @since 0.6.0
*/
export type OutboxErrorHandler = (
error: Error,
activity: Activity | null,
) => void | Promise<void>;
30 changes: 26 additions & 4 deletions federation/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import type {
InboxErrorHandler,
InboxListener,
NodeInfoDispatcher,
OutboxErrorHandler,
} from "./callback.ts";
import type {
Context,
Expand Down Expand Up @@ -82,6 +83,17 @@ export interface FederationParameters {
*/
treatHttps?: boolean;

/**
* A callback that handles errors during outbox processing. Note that this
* callback can be called multiple times for the same error, because failed
* deliveries are retried.
*
* If any errors are thrown in this callback, they are ignored.
*
* @since 0.6.0
*/
onOutboxError?: OutboxErrorHandler;

// TODO: The following option should be removed, and exponential backoff
// should be used instead:
backoffSchedule?: Temporal.Duration[];
Expand Down Expand Up @@ -129,6 +141,7 @@ export class Federation<TContextData> {
#documentLoader: DocumentLoader;
#authenticatedDocumentLoaderFactory: AuthenticatedDocumentLoaderFactory;
#treatHttps: boolean;
#onOutboxError?: OutboxErrorHandler;
#backoffSchedule: Temporal.Duration[];

/**
Expand All @@ -143,6 +156,7 @@ export class Federation<TContextData> {
documentLoader,
authenticatedDocumentLoaderFactory,
treatHttps,
onOutboxError,
backoffSchedule,
}: FederationParameters,
) {
Expand All @@ -167,6 +181,7 @@ export class Federation<TContextData> {
this.#authenticatedDocumentLoaderFactory =
authenticatedDocumentLoaderFactory ??
getAuthenticatedDocumentLoader;
this.#onOutboxError = onOutboxError;
this.#treatHttps = treatHttps ?? false;
this.#backoffSchedule = backoffSchedule ?? [
3_000,
Expand All @@ -180,17 +195,24 @@ export class Federation<TContextData> {
}

async #listenQueue(message: OutboxMessage): Promise<void> {
let activity: Activity | null = null;
try {
activity = await Activity.fromJsonLd(message.activity, {
documentLoader: this.#documentLoader,
});
await sendActivity({
keyId: new URL(message.keyId),
privateKey: await importJwk(message.privateKey, "private"),
activity: await Activity.fromJsonLd(message.activity, {
documentLoader: this.#documentLoader,
}),
activity,
inbox: new URL(message.inbox),
documentLoader: this.#documentLoader,
});
} catch (_) {
} catch (e) {
try {
this.#onOutboxError?.(e, activity);
} catch (_) {
// Ignore errors in the error handler.
}
if (message.trial < this.#backoffSchedule.length) {
this.#queue?.enqueue({
...message,
Expand Down

0 comments on commit 5540b5c

Please sign in to comment.