1 use crate::{check_is_apub_id_valid, APUB_JSON_CONTENT_TYPE};
3 use lemmy_utils::{request::retry, LemmyError};
5 use reqwest::{Client, StatusCode};
6 use serde::Deserialize;
7 use std::time::Duration;
11 /// Maximum number of HTTP requests allowed to handle a single incoming activity (or a single object
12 /// fetch through the search).
14 /// A community fetch will load the outbox with up to 20 items, and fetch the creator for each item.
15 /// So we are looking at a maximum of 22 requests (rounded up just to be safe).
16 static MAX_REQUEST_NUMBER: i32 = 25;
18 #[derive(Debug, Error)]
19 pub(in crate::fetcher) struct FetchError {
20 pub inner: anyhow::Error,
21 pub status_code: Option<StatusCode>,
24 impl From<LemmyError> for FetchError {
25 fn from(t: LemmyError) -> Self {
33 impl From<reqwest::Error> for FetchError {
34 fn from(t: reqwest::Error) -> Self {
35 let status = t.status();
43 impl std::fmt::Display for FetchError {
44 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
45 std::fmt::Display::fmt(&self, f)
49 /// Fetch any type of ActivityPub object, handling things like HTTP headers, deserialisation,
51 pub(in crate::fetcher) async fn fetch_remote_object<Response>(
54 recursion_counter: &mut i32,
55 ) -> Result<Response, FetchError>
57 Response: for<'de> Deserialize<'de> + std::fmt::Debug,
59 *recursion_counter += 1;
60 if *recursion_counter > MAX_REQUEST_NUMBER {
61 return Err(LemmyError::from(anyhow!("Maximum recursion depth reached")).into());
63 check_is_apub_id_valid(&url, false)?;
65 let timeout = Duration::from_secs(60);
70 .header("Accept", APUB_JSON_CONTENT_TYPE)
76 if res.status() == StatusCode::GONE {
77 info!("Fetched remote object {} which was deleted", url);
78 return Err(FetchError {
79 inner: anyhow!("Fetched remote object {} which was deleted", url),
80 status_code: Some(res.status()),
84 let object = res.json().await?;
85 info!("Fetched remote object {}", url);