Skip to content

Commit 0b90b5d

Browse files
authored
Handle local inbox delivery via internal REST API (#2379)
1 parent 65d8d74 commit 0b90b5d

File tree

6 files changed

+272
-39
lines changed

6 files changed

+272
-39
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Significance: minor
2+
Type: fixed
3+
4+
Fix local inbox delivery to use internal REST API instead of HTTP, enabling local follows and proper boost counting.

includes/class-dispatcher.php

Lines changed: 63 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,29 @@ class Dispatcher {
2222
/**
2323
* Batch size.
2424
*
25-
* @deprecated unreleased {@see Activitypub\Dispatcher::get_batch_size()}
25+
* @deprecated unreleased Use {@see Dispatcher::get_batch_size()}.
2626
*
2727
* @var int
2828
*/
2929
public static $batch_size = ACTIVITYPUB_OUTBOX_PROCESSING_BATCH_SIZE;
3030

31+
/**
32+
* Initialize the class, registering WordPress hooks.
33+
*/
34+
public static function init() {
35+
\add_action( 'activitypub_process_outbox', array( self::class, 'process_outbox' ) );
36+
37+
\add_action( 'post_activitypub_add_to_outbox', array( self::class, 'send_immediate_accept' ), 10, 2 );
38+
39+
// Default filters to add Inboxes to sent to.
40+
\add_filter( 'activitypub_additional_inboxes', array( self::class, 'add_inboxes_by_mentioned_actors' ), 10, 3 );
41+
\add_filter( 'activitypub_additional_inboxes', array( self::class, 'add_inboxes_of_replied_urls' ), 10, 3 );
42+
\add_filter( 'activitypub_additional_inboxes', array( self::class, 'add_inboxes_of_relays' ), 10, 3 );
43+
44+
Scheduler::register_async_batch_callback( 'activitypub_send_activity', array( self::class, 'send_to_followers' ) );
45+
Scheduler::register_async_batch_callback( 'activitypub_retry_activity', array( self::class, 'retry_send_to_followers' ) );
46+
}
47+
3148
/**
3249
* Get the batch size for processing outbox items.
3350
*
@@ -90,23 +107,6 @@ public static function get_retry_error_codes() {
90107
return apply_filters( 'activitypub_dispatcher_retry_error_codes', array( 408, 429, 500, 502, 503, 504 ) );
91108
}
92109

93-
/**
94-
* Initialize the class, registering WordPress hooks.
95-
*/
96-
public static function init() {
97-
\add_action( 'activitypub_process_outbox', array( self::class, 'process_outbox' ) );
98-
99-
\add_action( 'post_activitypub_add_to_outbox', array( self::class, 'send_immediate_accept' ), 10, 2 );
100-
101-
// Default filters to add Inboxes to sent to.
102-
\add_filter( 'activitypub_additional_inboxes', array( self::class, 'add_inboxes_by_mentioned_actors' ), 10, 3 );
103-
\add_filter( 'activitypub_additional_inboxes', array( self::class, 'add_inboxes_of_replied_urls' ), 10, 3 );
104-
\add_filter( 'activitypub_additional_inboxes', array( self::class, 'add_inboxes_of_relays' ), 10, 3 );
105-
106-
Scheduler::register_async_batch_callback( 'activitypub_send_activity', array( self::class, 'send_to_followers' ) );
107-
Scheduler::register_async_batch_callback( 'activitypub_retry_activity', array( self::class, 'retry_send_to_followers' ) );
108-
}
109-
110110
/**
111111
* Process the outbox.
112112
*
@@ -253,16 +253,21 @@ private static function send_to_inboxes( $inboxes, $outbox_item_id ) {
253253
\do_action( 'activitypub_pre_send_to_inboxes', $json, $inboxes, $outbox_item_id );
254254

255255
foreach ( $inboxes as $inbox ) {
256-
$result = safe_remote_post( $inbox, $json, $outbox_item->post_author );
256+
// Handle local inboxes via internal REST API, remote via HTTP.
257+
if ( is_same_domain( $inbox ) ) {
258+
$result = self::send_to_local_inbox( $inbox, $json );
259+
} else {
260+
$result = safe_remote_post( $inbox, $json, $outbox_item->post_author );
261+
}
257262

258-
if ( is_wp_error( $result ) && in_array( $result->get_error_code(), self::get_retry_error_codes(), true ) ) {
263+
if ( \is_wp_error( $result ) && in_array( $result->get_error_code(), self::get_retry_error_codes(), true ) ) {
259264
$retries[] = $inbox;
260265
}
261266

262267
/**
263268
* Fires after an Activity has been sent to an inbox.
264269
*
265-
* @param array $result The result of the remote post request.
270+
* @param array $result The result of the internal or remote post request.
266271
* @param string $inbox The inbox URL.
267272
* @param string $json The ActivityPub Activity JSON.
268273
* @param int $actor_id The actor ID.
@@ -274,6 +279,41 @@ private static function send_to_inboxes( $inboxes, $outbox_item_id ) {
274279
return $retries;
275280
}
276281

282+
/**
283+
* Send an activity to a local inbox via internal REST API request.
284+
*
285+
* @param string $inbox_url The local inbox URL.
286+
* @param string $json The ActivityPub Activity JSON.
287+
* @return array|\WP_Error The result in the format of a remote post response, or WP_Error on failure.
288+
*/
289+
private static function send_to_local_inbox( $inbox_url, $json ) {
290+
// Parse the inbox URL to extract the REST route.
291+
$path = \wp_parse_url( $inbox_url, PHP_URL_PATH ) ?? '';
292+
$rest_route = \preg_replace( '#^/' . preg_quote( \rest_get_url_prefix(), '#' ) . '#', '', $path );
293+
294+
// Create a REST request.
295+
$request = new \WP_REST_Request( 'POST', $rest_route );
296+
$request->set_header( 'Content-Type', 'application/activity+json' );
297+
$request->set_body( $json );
298+
$request->get_json_params();
299+
300+
\add_filter( 'activitypub_defer_signature_verification', '__return_true' );
301+
$response = \rest_do_request( $request );
302+
\remove_filter( 'activitypub_defer_signature_verification', '__return_true' );
303+
304+
// Return result in format similar to remote post response.
305+
if ( $response->is_error() ) {
306+
return $response->as_error();
307+
}
308+
309+
return array(
310+
'response' => array(
311+
'code' => $response->get_status(),
312+
),
313+
'body' => \wp_json_encode( $response->get_data() ),
314+
);
315+
}
316+
277317
/**
278318
* Schedule a retry.
279319
*
@@ -335,13 +375,8 @@ public static function add_inboxes_by_mentioned_actors( $inboxes, $actor_id, $ac
335375

336376
$audience = array_merge( $cc, $to );
337377

338-
// Remove "public placeholder" and "same domain" from the audience.
339-
$audience = array_filter(
340-
$audience,
341-
function ( $actor ) {
342-
return 'https://www.w3.org/ns/activitystreams#Public' !== $actor && ! is_same_domain( $actor );
343-
}
344-
);
378+
// Remove "public placeholder" from the audience.
379+
$audience = array_diff( $audience, array( 'https://www.w3.org/ns/activitystreams#Public' ) );
345380

346381
if ( $audience ) {
347382
$mentioned_inboxes = Mention::get_inboxes( $audience );

includes/class-scheduler.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,7 @@ public static function schedule_announce_activity( $outbox_activity_id, $activit
535535
$announce->set_type( 'Announce' );
536536
$announce->set_actor( Actors::get_by_id( Actors::BLOG_USER_ID )->get_id() );
537537
$announce->set_object( $activity );
538+
$announce->add_cc( object_to_uri( $activity->get_actor() ) );
538539

539540
$outbox_activity_id = Outbox::add( $announce, Actors::BLOG_USER_ID );
540541

includes/handler/class-announce.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@ public static function maybe_save_announce( $activity, $user_id ) {
101101
return;
102102
}
103103

104+
// If the object is a Create activity, extract the actual object from it.
105+
if ( isset( $activity['object']['type'] ) && 'Create' === $activity['object']['type'] ) {
106+
$activity['object'] = object_to_uri( $activity['object']['object'] );
107+
}
108+
104109
$success = false;
105110
$result = Interactions::add_reaction( $activity );
106111

0 commit comments

Comments
 (0)