Reduce stack memory usage in apub code

- use our own, smaller Endpoints struct
- wrap ObjectId.url in Box
- adjust usage of Box in different places
This commit is contained in:
Felix Ableitner 2021-11-06 14:25:34 +01:00
parent c725514841
commit 8ea21c39b7
20 changed files with 65 additions and 64 deletions

View file

@ -73,7 +73,7 @@ impl Perform for CreatePostLike {
.await??; .await??;
let community_id = post.community_id; let community_id = post.community_id;
let object = PostOrComment::Post(Box::new(post)); let object = PostOrComment::Post(post);
// Only add the like if the score isnt 0 // Only add the like if the score isnt 0
let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1); let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);

View file

@ -146,7 +146,7 @@ impl PerformCrud for CreatePost {
context, context,
) )
.await?; .await?;
let object = PostOrComment::Post(Box::new(apub_post)); let object = PostOrComment::Post(apub_post);
Vote::send( Vote::send(
&object, &object,
&local_user_view.person.clone().into(), &local_user_view.person.clone().into(),

View file

@ -9,7 +9,7 @@ use crate::{
}, },
activity_lists::AnnouncableActivities, activity_lists::AnnouncableActivities,
objects::{community::ApubCommunity, person::ApubPerson}, objects::{community::ApubCommunity, person::ApubPerson},
protocol::{activities::community::update::UpdateCommunity, objects::group::Group}, protocol::activities::community::update::UpdateCommunity,
}; };
use activitystreams::{activity::kind::UpdateType, public}; use activitystreams::{activity::kind::UpdateType, public};
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
@ -38,14 +38,14 @@ impl UpdateCommunity {
let update = UpdateCommunity { let update = UpdateCommunity {
actor: ObjectId::new(actor.actor_id()), actor: ObjectId::new(actor.actor_id()),
to: vec![public()], to: vec![public()],
object: community.clone().into_apub(context).await?, object: Box::new(community.clone().into_apub(context).await?),
cc: vec![community.actor_id()], cc: vec![community.actor_id()],
kind: UpdateType::Update, kind: UpdateType::Update,
id: id.clone(), id: id.clone(),
unparsed: Default::default(), unparsed: Default::default(),
}; };
let activity = AnnouncableActivities::UpdateCommunity(Box::new(update)); let activity = AnnouncableActivities::UpdateCommunity(update);
send_to_community(activity, &id, actor, &community, vec![], context).await send_to_community(activity, &id, actor, &community, vec![], context).await
} }
} }
@ -73,12 +73,10 @@ impl ActivityHandler for UpdateCommunity {
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let community = self.get_community(context, request_counter).await?; let community = self.get_community(context, request_counter).await?;
let updated_community = Group::into_form( let updated_community = self
self.object, .object
&community.actor_id.clone().into(), .into_form(&community.actor_id.clone().into(), &context.settings())
&context.settings(), .await?;
)
.await?;
let cf = CommunityForm { let cf = CommunityForm {
name: updated_community.name, name: updated_community.name,
title: updated_community.title, title: updated_community.title,

View file

@ -53,9 +53,9 @@ pub async fn send_apub_remove(
} }
pub enum DeletableObjects { pub enum DeletableObjects {
Community(Box<ApubCommunity>), Community(ApubCommunity),
Comment(Box<ApubComment>), Comment(ApubComment),
Post(Box<ApubPost>), Post(ApubPost),
} }
impl DeletableObjects { impl DeletableObjects {
@ -64,13 +64,13 @@ impl DeletableObjects {
context: &LemmyContext, context: &LemmyContext,
) -> Result<DeletableObjects, LemmyError> { ) -> Result<DeletableObjects, LemmyError> {
if let Some(c) = ApubCommunity::read_from_apub_id(ap_id.clone(), context).await? { if let Some(c) = ApubCommunity::read_from_apub_id(ap_id.clone(), context).await? {
return Ok(DeletableObjects::Community(Box::new(c))); return Ok(DeletableObjects::Community(c));
} }
if let Some(p) = ApubPost::read_from_apub_id(ap_id.clone(), context).await? { if let Some(p) = ApubPost::read_from_apub_id(ap_id.clone(), context).await? {
return Ok(DeletableObjects::Post(Box::new(p))); return Ok(DeletableObjects::Post(p));
} }
if let Some(c) = ApubComment::read_from_apub_id(ap_id.clone(), context).await? { if let Some(c) = ApubComment::read_from_apub_id(ap_id.clone(), context).await? {
return Ok(DeletableObjects::Comment(Box::new(c))); return Ok(DeletableObjects::Comment(c));
} }
Err(diesel::NotFound.into()) Err(diesel::NotFound.into())
} }

View file

@ -61,7 +61,7 @@ impl CreateOrUpdatePost {
.into(); .into();
let create_or_update = CreateOrUpdatePost::new(post, actor, &community, kind, context).await?; let create_or_update = CreateOrUpdatePost::new(post, actor, &community, kind, context).await?;
let id = create_or_update.id.clone(); let id = create_or_update.id.clone();
let activity = AnnouncableActivities::CreateOrUpdatePost(Box::new(create_or_update)); let activity = AnnouncableActivities::CreateOrUpdatePost(create_or_update);
send_to_community(activity, &id, actor, &community, vec![], context).await send_to_community(activity, &id, actor, &community, vec![], context).await
} }
} }

View file

@ -26,7 +26,6 @@ use lemmy_apub_lib::{
use lemmy_db_schema::{newtypes::CommunityId, source::community::Community, traits::Crud}; use lemmy_db_schema::{newtypes::CommunityId, source::community::Community, traits::Crud};
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use std::ops::Deref;
impl UndoVote { impl UndoVote {
pub async fn send( pub async fn send(
@ -90,7 +89,7 @@ impl ActivityHandler for UndoVote {
.dereference(context, request_counter) .dereference(context, request_counter)
.await?; .await?;
match object { match object {
PostOrComment::Post(p) => undo_vote_post(actor, p.deref(), context).await, PostOrComment::Post(p) => undo_vote_post(actor, &p, context).await,
PostOrComment::Comment(c) => undo_vote_comment(actor, &c, context).await, PostOrComment::Comment(c) => undo_vote_comment(actor, &c, context).await,
} }
} }

View file

@ -26,7 +26,6 @@ use lemmy_db_schema::{
}; };
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use std::ops::Deref;
impl Vote { impl Vote {
pub(in crate::activities::voting) fn new( pub(in crate::activities::voting) fn new(
@ -90,7 +89,7 @@ impl ActivityHandler for Vote {
let actor = self.actor.dereference(context, request_counter).await?; let actor = self.actor.dereference(context, request_counter).await?;
let object = self.object.dereference(context, request_counter).await?; let object = self.object.dereference(context, request_counter).await?;
match object { match object {
PostOrComment::Post(p) => vote_post(&self.kind, actor, p.deref(), context).await, PostOrComment::Post(p) => vote_post(&self.kind, actor, &p, context).await,
PostOrComment::Comment(c) => vote_comment(&self.kind, actor, &c, context).await, PostOrComment::Comment(c) => vote_comment(&self.kind, actor, &c, context).await,
} }
} }

View file

@ -38,7 +38,7 @@ pub enum SharedInboxActivities {
GroupInboxActivities(GroupInboxActivities), GroupInboxActivities(GroupInboxActivities),
// Note, pm activities need to be at the end, otherwise comments will end up here. We can probably // Note, pm activities need to be at the end, otherwise comments will end up here. We can probably
// avoid this problem by replacing createpm.object with our own struct, instead of NoteExt. // avoid this problem by replacing createpm.object with our own struct, instead of NoteExt.
PersonInboxActivities(PersonInboxActivities), PersonInboxActivities(Box<PersonInboxActivities>),
} }
#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)]
@ -47,7 +47,7 @@ pub enum SharedInboxActivities {
pub enum GroupInboxActivities { pub enum GroupInboxActivities {
FollowCommunity(FollowCommunity), FollowCommunity(FollowCommunity),
UndoFollowCommunity(UndoFollowCommunity), UndoFollowCommunity(UndoFollowCommunity),
AnnouncableActivities(AnnouncableActivities), AnnouncableActivities(Box<AnnouncableActivities>),
Report(Report), Report(Report),
} }
@ -61,7 +61,7 @@ pub enum PersonInboxActivities {
CreateOrUpdatePrivateMessage(CreateOrUpdatePrivateMessage), CreateOrUpdatePrivateMessage(CreateOrUpdatePrivateMessage),
DeletePrivateMessage(DeletePrivateMessage), DeletePrivateMessage(DeletePrivateMessage),
UndoDeletePrivateMessage(UndoDeletePrivateMessage), UndoDeletePrivateMessage(UndoDeletePrivateMessage),
AnnounceActivity(Box<AnnounceActivity>), AnnounceActivity(AnnounceActivity),
} }
#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)]
@ -69,12 +69,12 @@ pub enum PersonInboxActivities {
#[activity_handler(LemmyContext)] #[activity_handler(LemmyContext)]
pub enum AnnouncableActivities { pub enum AnnouncableActivities {
CreateOrUpdateComment(CreateOrUpdateComment), CreateOrUpdateComment(CreateOrUpdateComment),
CreateOrUpdatePost(Box<CreateOrUpdatePost>), CreateOrUpdatePost(CreateOrUpdatePost),
Vote(Vote), Vote(Vote),
UndoVote(UndoVote), UndoVote(UndoVote),
Delete(Delete), Delete(Delete),
UndoDelete(UndoDelete), UndoDelete(UndoDelete),
UpdateCommunity(Box<UpdateCommunity>), UpdateCommunity(UpdateCommunity),
BlockUserFromCommunity(BlockUserFromCommunity), BlockUserFromCommunity(BlockUserFromCommunity),
UndoBlockUserFromCommunity(UndoBlockUserFromCommunity), UndoBlockUserFromCommunity(UndoBlockUserFromCommunity),
AddMod(AddMod), AddMod(AddMod),

View file

@ -11,15 +11,15 @@ use url::Url;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum PostOrComment { pub enum PostOrComment {
Post(Box<ApubPost>), Post(ApubPost),
Comment(ApubComment), Comment(ApubComment),
} }
#[derive(Deserialize)] #[derive(Deserialize)]
#[serde(untagged)] #[serde(untagged)]
pub enum PageOrNote { pub enum PageOrNote {
Page(Box<Page>), Page(Page),
Note(Box<Note>), Note(Note),
} }
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
@ -39,7 +39,7 @@ impl ApubObject for PostOrComment {
) -> Result<Option<Self>, LemmyError> { ) -> Result<Option<Self>, LemmyError> {
let post = ApubPost::read_from_apub_id(object_id.clone(), data).await?; let post = ApubPost::read_from_apub_id(object_id.clone(), data).await?;
Ok(match post { Ok(match post {
Some(o) => Some(PostOrComment::Post(Box::new(o))), Some(o) => Some(PostOrComment::Post(o)),
None => ApubComment::read_from_apub_id(object_id, data) None => ApubComment::read_from_apub_id(object_id, data)
.await? .await?
.map(PostOrComment::Comment), .map(PostOrComment::Comment),
@ -68,11 +68,11 @@ impl ApubObject for PostOrComment {
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<Self, LemmyError> { ) -> Result<Self, LemmyError> {
Ok(match apub { Ok(match apub {
PageOrNote::Page(p) => PostOrComment::Post(Box::new( PageOrNote::Page(p) => PostOrComment::Post(
ApubPost::from_apub(*p, context, expected_domain, request_counter).await?, ApubPost::from_apub(p, context, expected_domain, request_counter).await?,
)), ),
PageOrNote::Note(n) => PostOrComment::Comment( PageOrNote::Note(n) => PostOrComment::Comment(
ApubComment::from_apub(*n, context, expected_domain, request_counter).await?, ApubComment::from_apub(n, context, expected_domain, request_counter).await?,
), ),
}) })
} }

View file

@ -85,7 +85,7 @@ pub(in crate::http) async fn receive_group_inbox(
let community = announcable.get_community(context, &mut 0).await?; let community = announcable.get_community(context, &mut 0).await?;
verify_person_in_community(&actor_id, &community, context, &mut 0).await?; verify_person_in_community(&actor_id, &community, context, &mut 0).await?;
if community.local { if community.local {
AnnounceActivity::send(announcable, &community, vec![], context).await?; AnnounceActivity::send(*announcable, &community, vec![], context).await?;
} }
} }

View file

@ -52,7 +52,7 @@ pub async fn shared_inbox(
receive_group_inbox(g, activity_data, request, &context).await receive_group_inbox(g, activity_data, request, &context).await
} }
SharedInboxActivities::PersonInboxActivities(p) => { SharedInboxActivities::PersonInboxActivities(p) => {
receive_person_inbox(p, activity_data, request, &context).await receive_person_inbox(*p, activity_data, request, &context).await
} }
} }
} }

View file

@ -4,15 +4,12 @@ use crate::{
generate_moderators_url, generate_moderators_url,
generate_outbox_url, generate_outbox_url,
protocol::{ protocol::{
objects::{group::Group, tombstone::Tombstone}, objects::{group::Group, tombstone::Tombstone, Endpoints},
ImageObject, ImageObject,
Source, Source,
}, },
}; };
use activitystreams::{ use activitystreams::{actor::kind::GroupType, object::kind::ImageType};
actor::{kind::GroupType, Endpoints},
object::kind::ImageType,
};
use chrono::NaiveDateTime; use chrono::NaiveDateTime;
use itertools::Itertools; use itertools::Itertools;
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
@ -111,7 +108,6 @@ impl ApubObject for ApubCommunity {
followers: self.followers_url.clone().into(), followers: self.followers_url.clone().into(),
endpoints: Endpoints { endpoints: Endpoints {
shared_inbox: self.shared_inbox_url.clone().map(|s| s.into()), shared_inbox: self.shared_inbox_url.clone().map(|s| s.into()),
..Default::default()
}, },
public_key: self.get_public_key()?, public_key: self.get_public_key()?,
published: Some(convert_datetime(self.published)), published: Some(convert_datetime(self.published)),

View file

@ -3,12 +3,15 @@ use crate::{
generate_outbox_url, generate_outbox_url,
objects::get_summary_from_string_or_source, objects::get_summary_from_string_or_source,
protocol::{ protocol::{
objects::person::{Person, UserTypes}, objects::{
person::{Person, UserTypes},
Endpoints,
},
ImageObject, ImageObject,
Source, Source,
}, },
}; };
use activitystreams::{actor::Endpoints, object::kind::ImageType}; use activitystreams::object::kind::ImageType;
use chrono::NaiveDateTime; use chrono::NaiveDateTime;
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
use lemmy_apub_lib::{ use lemmy_apub_lib::{
@ -109,7 +112,6 @@ impl ApubObject for ApubPerson {
outbox: generate_outbox_url(&self.actor_id)?.into(), outbox: generate_outbox_url(&self.actor_id)?.into(),
endpoints: Endpoints { endpoints: Endpoints {
shared_inbox: self.shared_inbox_url.clone().map(|s| s.into()), shared_inbox: self.shared_inbox_url.clone().map(|s| s.into()),
..Default::default()
}, },
public_key: self.get_public_key()?, public_key: self.get_public_key()?,
updated: self.updated.map(convert_datetime), updated: self.updated.map(convert_datetime),

View file

@ -12,7 +12,7 @@ pub struct UpdateCommunity {
pub(crate) actor: ObjectId<ApubPerson>, pub(crate) actor: ObjectId<ApubPerson>,
pub(crate) to: Vec<Url>, pub(crate) to: Vec<Url>,
// TODO: would be nice to use a separate struct here, which only contains the fields updated here // TODO: would be nice to use a separate struct here, which only contains the fields updated here
pub(crate) object: Group, pub(crate) object: Box<Group>,
pub(crate) cc: Vec<Url>, pub(crate) cc: Vec<Url>,
#[serde(rename = "type")] #[serde(rename = "type")]
pub(crate) kind: UpdateType, pub(crate) kind: UpdateType,

View file

@ -5,12 +5,9 @@ use crate::{
community_outbox::ApubCommunityOutbox, community_outbox::ApubCommunityOutbox,
}, },
objects::{community::ApubCommunity, get_summary_from_string_or_source}, objects::{community::ApubCommunity, get_summary_from_string_or_source},
protocol::{ImageObject, Source}, protocol::{objects::Endpoints, ImageObject, Source},
};
use activitystreams::{
actor::{kind::GroupType, Endpoints},
unparsed::Unparsed,
}; };
use activitystreams::{actor::kind::GroupType, unparsed::Unparsed};
use chrono::{DateTime, FixedOffset}; use chrono::{DateTime, FixedOffset};
use lemmy_apub_lib::{object_id::ObjectId, signatures::PublicKey, verify::verify_domains_match}; use lemmy_apub_lib::{object_id::ObjectId, signatures::PublicKey, verify::verify_domains_match};
use lemmy_db_schema::{naive_now, source::community::CommunityForm}; use lemmy_db_schema::{naive_now, source::community::CommunityForm};
@ -46,7 +43,7 @@ pub struct Group {
pub(crate) inbox: Url, pub(crate) inbox: Url,
pub(crate) outbox: ObjectId<ApubCommunityOutbox>, pub(crate) outbox: ObjectId<ApubCommunityOutbox>,
pub(crate) followers: Url, pub(crate) followers: Url,
pub(crate) endpoints: Endpoints<Url>, pub(crate) endpoints: Endpoints,
pub(crate) public_key: PublicKey, pub(crate) public_key: PublicKey,
pub(crate) published: Option<DateTime<FixedOffset>>, pub(crate) published: Option<DateTime<FixedOffset>>,
pub(crate) updated: Option<DateTime<FixedOffset>>, pub(crate) updated: Option<DateTime<FixedOffset>>,
@ -72,7 +69,6 @@ impl Group {
check_slurs(&title, slur_regex)?; check_slurs(&title, slur_regex)?;
check_slurs_opt(&description, slur_regex)?; check_slurs_opt(&description, slur_regex)?;
// TODO: test_parse_lemmy_community_moderators() keeps failing here with stack overflow
Ok(CommunityForm { Ok(CommunityForm {
name, name,
title, title,

View file

@ -1,3 +1,6 @@
use serde::{Deserialize, Serialize};
use url::Url;
pub(crate) mod chat_message; pub(crate) mod chat_message;
pub(crate) mod group; pub(crate) mod group;
pub(crate) mod note; pub(crate) mod note;
@ -5,6 +8,13 @@ pub(crate) mod page;
pub(crate) mod person; pub(crate) mod person;
pub(crate) mod tombstone; pub(crate) mod tombstone;
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Endpoints {
#[serde(skip_serializing_if = "Option::is_none")]
pub shared_inbox: Option<Url>,
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::protocol::{ use crate::protocol::{

View file

@ -1,8 +1,8 @@
use crate::{ use crate::{
objects::person::ApubPerson, objects::person::ApubPerson,
protocol::{ImageObject, Source}, protocol::{objects::Endpoints, ImageObject, Source},
}; };
use activitystreams::{actor::Endpoints, unparsed::Unparsed, url::Url}; use activitystreams::{unparsed::Unparsed, url::Url};
use chrono::{DateTime, FixedOffset}; use chrono::{DateTime, FixedOffset};
use lemmy_apub_lib::{object_id::ObjectId, signatures::PublicKey}; use lemmy_apub_lib::{object_id::ObjectId, signatures::PublicKey};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -35,7 +35,7 @@ pub struct Person {
pub(crate) inbox: Url, pub(crate) inbox: Url,
/// mandatory field in activitypub, currently empty in lemmy /// mandatory field in activitypub, currently empty in lemmy
pub(crate) outbox: Url, pub(crate) outbox: Url,
pub(crate) endpoints: Endpoints<Url>, pub(crate) endpoints: Endpoints,
pub(crate) public_key: PublicKey, pub(crate) public_key: PublicKey,
pub(crate) published: Option<DateTime<FixedOffset>>, pub(crate) published: Option<DateTime<FixedOffset>>,
pub(crate) updated: Option<DateTime<FixedOffset>>, pub(crate) updated: Option<DateTime<FixedOffset>>,

View file

@ -28,9 +28,10 @@ lazy_static! {
.unwrap(); .unwrap();
} }
/// We store Url on the heap because it is quite large (88 bytes).
#[derive(Clone, PartialEq, Serialize, Deserialize, Debug)] #[derive(Clone, PartialEq, Serialize, Deserialize, Debug)]
#[serde(transparent)] #[serde(transparent)]
pub struct ObjectId<Kind>(Url, #[serde(skip)] PhantomData<Kind>) pub struct ObjectId<Kind>(Box<Url>, #[serde(skip)] PhantomData<Kind>)
where where
Kind: ApubObject + Send + 'static, Kind: ApubObject + Send + 'static,
for<'de2> <Kind as ApubObject>::ApubType: serde::Deserialize<'de2>; for<'de2> <Kind as ApubObject>::ApubType: serde::Deserialize<'de2>;
@ -44,7 +45,7 @@ where
where where
T: Into<Url>, T: Into<Url>,
{ {
ObjectId(url.into(), PhantomData::<Kind>) ObjectId(Box::new(url.into()), PhantomData::<Kind>)
} }
pub fn inner(&self) -> &Url { pub fn inner(&self) -> &Url {
@ -103,7 +104,7 @@ where
data: &<Kind as ApubObject>::DataType, data: &<Kind as ApubObject>::DataType,
) -> Result<Option<Kind>, LemmyError> { ) -> Result<Option<Kind>, LemmyError> {
let id = self.0.clone(); let id = self.0.clone();
ApubObject::read_from_apub_id(id, data).await ApubObject::read_from_apub_id(*id, data).await
} }
async fn dereference_from_http( async fn dereference_from_http(
@ -181,7 +182,7 @@ where
for<'de2> <Kind as ApubObject>::ApubType: serde::Deserialize<'de2>, for<'de2> <Kind as ApubObject>::ApubType: serde::Deserialize<'de2>,
{ {
fn from(id: ObjectId<Kind>) -> Self { fn from(id: ObjectId<Kind>) -> Self {
id.0 *id.0
} }
} }

View file

@ -91,7 +91,7 @@ pub fn verify_signature(request: &HttpRequest, public_key: &str) -> Result<(), L
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct PublicKey { pub struct PublicKey {
pub id: String, pub(crate) id: String,
pub owner: Url, pub(crate) owner: Box<Url>,
pub public_key_pem: String, pub public_key_pem: String,
} }

View file

@ -82,7 +82,7 @@ pub trait ActorType {
fn get_public_key(&self) -> Result<PublicKey, LemmyError> { fn get_public_key(&self) -> Result<PublicKey, LemmyError> {
Ok(PublicKey { Ok(PublicKey {
id: format!("{}#main-key", self.actor_id()), id: format!("{}#main-key", self.actor_id()),
owner: self.actor_id(), owner: Box::new(self.actor_id()),
public_key_pem: self.public_key().context(location_info!())?, public_key_pem: self.public_key().context(location_info!())?,
}) })
} }